diff options
author | Erick Ochoa <erick.ochoa@theobroma-systems.com> | 2020-07-22 15:11:24 +0200 |
---|---|---|
committer | Erick Ochoa <erick.ochoa@theobroma-systems.com> | 2020-10-30 13:09:51 +0100 |
commit | f38765e21c117c53d9c0429b6ccef8fc4c470a10 (patch) | |
tree | a5c3908ade2bfee3baeca375267937ddf89a3d12 | |
parent | 24cdb7c1eb662442b26f409f404bdd92ba6b9010 (diff) |
Dead-field warning in structs at LTO-time
This commit includes the following components:
Type-based escape analysis to determine structs that can be modified at
link-time.
Field access analysis to determine which fields are never read.
The type-based escape analysis provides a list of types, that are not
visible outside of the current linking unit (e.g. parameter types of external
functions).
The field access analyses non-escaping structs for fields that
are not used in the linking unit and thus can be removed.
84 files changed, 6507 insertions, 301 deletions
diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 5fda9aacdcd..0b3eb79d73c 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1408,30 +1408,7 @@ OBJS = \ incpath.o \ init-regs.o \ internal-fn.o \ - ipa-prototype.o \ ipa-type-escape-analysis.o \ - type-walker.o \ - type-reconstructor.o \ - expr-walker.o \ - gimple-walker.o \ - gimple-accesser.o \ - expr-accessor.o \ - type-accessor.o \ - type-collector.o \ - expr-collector.o \ - gimple-collector.o \ - gimple-caster.o \ - gimple-rewriter.o \ - expr-rewriter.o \ - type-escaper.o \ - type-structural-equality.o \ - type-structural-main-variant.o \ - type-canonical-equality.o \ - type-incomplete-equality.o \ - expr-escaper.o \ - gimple-escaper.o \ - type-stringifier.o \ - collect-types.o \ ipa-cp.o \ ipa-sra.o \ ipa-devirt.o \ diff --git a/gcc/common.opt b/gcc/common.opt index 9b85cf5c0c3..fe16c2105d4 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -3464,31 +3464,21 @@ fipa-ra Common Report Var(flag_ipa_ra) Optimization Use caller save register across calls if possible. -fipa-typelist-struct= -Common Joined Report Var(flag_ipa_typelist_struct) Init(0) --fipa-typelist-struct=<string> Name of struct of interest - -fipa-typelist-field= -Common Joined Report Var(flag_ipa_typelist_field) Init(0) --fipa-typelist-field=<string> Name of struct of interest - -ftp-types-compared= -Common Joined Report Var(flag_tp_types_compared) Init(0) - -ftp-comparison-functions= -Common Joined Report Var(flag_tp_comparison_functions) Init(0) - -fipa-prototype -Common Report Var(flag_ipa_prototype) Optimization -TBD - fipa-type-escape-analysis Common Report Var(flag_ipa_type_escape_analysis) Optimization This flag is only used for debugging the type escape analysis +Wdfa +Common Var(warn_dfa) Init(1) Warning +Warn about dead fields at link time. + +fipa-type-escape-analysis-keep-casts +Common Report Var(flag_ipa_type_escape_analysis_keep_casts) Init(0) +This flag is used to ignore casting in the legality analysis of dead field elimination. + fprint-escape-analysis Common Report Var(flag_print_escape_analysis) Optimization -This flag is used to print the escape analysis proper. It does not include type casting. +This flag is used to print the escape analysis proper. It does not include type casting. fprint-cast-analysis Common Report Var(flag_print_cast_analysis) Optimization @@ -3498,8 +3488,4 @@ fprint-access-analysis Common Report Var(flag_print_access_analysis) Optimization This flag is used to print the access analysis (if field is read or written to). -fipa-dlo -Common Report Var(flag_ipa_dlo) Optimization -Perform data-layout optimizations - ; This comment is to ensure we retain the blank line above. diff --git a/gcc/ipa-type-escape-analysis.c b/gcc/ipa-type-escape-analysis.c index 45875f74443..ac5697f9321 100644 --- a/gcc/ipa-type-escape-analysis.c +++ b/gcc/ipa-type-escape-analysis.c @@ -1,3 +1,105 @@ +/* IPA Type Escape Analysis and Dead Field Elimination + Copyright (C) 2019-2020 Free Software Foundation, Inc. + + Contributed by Erick Ochoa <erick.ochoa@theobroma-systems.com> + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* Interprocedural dead field analysis (IPA-DFA) + + The goal of this analysis is to + + 1) discover RECORD_TYPEs which do not escape the current linking unit. + + 2) discover fields in RECORD_TYPEs that are never read. + + 3) merge the results from 1 and 2 to determine which fields are not needed. + + The algorithm basically consists of the following stages: + + 1) Partition all TYPE_P trees into two sets: those trees which reach a + tree of RECORD_TYPE. + + 2.a) Analyze callsites to determine if arguments and return types are + escaping. + 2.b) Analyze casts to determine if it would be safe to mark a field as dead. + 2.c) Analyze for constructors and static initialization and mark this as + TYPE_P trees as unable to be modified + 2.d) Analyze if FIELD_DECL are accessed via pointer arithmetic and mark + FIELD_DECLs before as unable to be modified. + 2.e) Analyze if an address of a FIELD_DECL is taken and mark the whole + RECORD_TYPE as unable to be modified. + 2.f) Propagate this information to nested TYPE_P trees. + 2.g) Propagate this information across different TYPE_P trees that represent + equivalent TYPE_P types. + + 3.a) Analyze FIELD_DECL to determine whether they are read, + written or neither. + 3.b) Unify this information across different RECORD_TYPE trees that + represent equivalent types + 3.c) Determine which FIELD_DECL can be deleted. + + 4) Calculate the intersection of non-escaping RECORD_TYPEs with RECORD_TYPEs + that have a field that can be deleted. + + First stage - Determining if a TYPE_P points to a RECORD_TYPE + ============================================================= + + This stage is computed through the *Collector classes. Those are + TypeCollector, ExprCollector and GimpleTypeCollector which walk up and down + types, expressions, and gimple respectively and propagate information about + TYPE_P trees and mantain information on the type partitions. + + Second stage - Determining if a TYPE_P escapes + ============================================== + + This stage is computed through the *Escaper classes. Those are + TypeEscaper, ExprEscaper, GimpleEscaper, GimpleCaster classes. These + classes walk up and down types, expressions and gimple respectively and + propagate reasons why a TYPE_P tree might be escaping. + Reasons are always ORed and equivalent TYPE_P trees might hold different + results up to when equivalence is computed for all TYPE_P trees and reasons + are propagated until a fixedpoint is achieved. + + Third stage - Calculating FIELD_DECL accesses + ============================================= + + This stage is computed through the *Accessor classes. Those are + TypeAccessor, ExprAccessor, and GimpleAccessor. These classes walk up and + down TYPE_P, expressions, and gimple respectively and propagate access + information. If an expression occurs in the LHS, it is marked as written + and if it occurs on the RHS, it is marked as read. Special cases where + addresses of a FIELD_DECLs are taken mark all FIELD_DECL in a RECORD_TYPE + as read. + + Fourth stage - Intersection of accesses and non_escaping + ======================================================== + + This stage happens in the function obtain_unescaped_and_unaccessed_fields. + First all FIELD_DECLs are translated to their respective field offset. + Then all field offsets for FIELD_DECLs which are READ are stored + in a set. We then compute the complement of this set and these are the + offsets of FIELD_DECLs which are never read. + + Offsets are needed if we are to find dead fields for anonymous fields. +*/ + + + #include "config.h" #include "system.h" #include "coretypes.h" @@ -21,47 +123,106 @@ #include "tree-ssa-ccp.h" #include "stringpool.h" #include "attribs.h" - -#include "gimple-collector.hpp" -#include "gimple-escaper.hpp" -#include "gimple-caster.hpp" -#include "gimple-accesser.hpp" -#include "type-stringifier.hpp" -#include "type-incomplete-equality.hpp" -#include "type-reconstructor.hpp" -#include "gimple-rewriter.hpp" +#include "basic-block.h" //needed for gimple.h +#include "function.h" //needed for gimple.h +#include "gimple.h" +#include "stor-layout.h" +#include "cfg.h" // needed for gimple-iterator.h +#include "gimple-iterator.h" +#include "gimplify.h" //unshare_expr +#include "value-range.h" // make_ssa_name dependency +#include "tree-ssanames.h" // make_ssa_name +#include "ssa.h" +#include "tree-into-ssa.h" +#include "gimple-ssa.h" // update_stmt +#include "tree.h" +#include "gimple-expr.h" +#include "predict.h" +#include "alloc-pool.h" +#include "tree-pass.h" +#include "cgraph.h" +#include "diagnostic.h" +#include "fold-const.h" +#include "gimple-fold.h" +#include "symbol-summary.h" +#include "tree-vrp.h" +#include "ipa-prop.h" +#include "tree-pretty-print.h" +#include "tree-inline.h" +#include "ipa-fnsummary.h" +#include "ipa-utils.h" +#include "tree-ssa-ccp.h" +#include "stringpool.h" +#include "attribs.h" +#include "tree-ssa-alias.h" +#include "tree-ssanames.h" +#include "gimple.h" +#include "cfg.h" +#include "gimple-iterator.h" +#include "gimple-ssa.h" +#include "gimple-pretty-print.h" #include <vector> +#include <set> +#include <map> +#include <stack> + +#include "ipa-type-escape-analysis.h" +// Main function that drives dfe. +static unsigned int +lto_dfe_execute (); -static unsigned int iphw_execute(); +// Partition types into reching record or non reaching record sets. +static tpartitions_t +partition_types_into_record_reaching_or_non_record_reaching (); -namespace { -/* ==What is type-escape-analysis?== - * type-escape-analysis is a SIMPLE_IPA_PASS that performs no transformations. - * type-escape-analysis only performs analysis and outputs to a WPA dump file. - * - * ==Why should we run type-escape-analysis?== - * type-escape-analysis is useful to run unit tests in gcc. - * By having the type-escape-analysis execute during WPA we are able to use - * the dejagnu framework to see if an expected output matches the observed - * output in the wpa dump file. - * - * ==How do we use type-escape-analysis?== - * Compile with - * -flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis - * - * To use type-escape-analysis in tests use the following lines - * { dg-do link } - * { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } - * C code to test... - * { dg-final { scan-wpa-ipa-dump "regex" "type-escape-analysis" } } - * - * ==TODO== - * At the moment, the tests are not set up to work with the current framework, - * so I will need to update them in the following day. +// Partition types into escaping or non escaping sets. +static tpartitions_t +partition_types_into_escaping_nonescaping (); + +// Perform dead field elimination. +static void +lto_dead_field_elimination (); + +// Fixed point calculating to determine escaping types. +static void +fix_escaping_types_in_set (tpartitions_t &types); + +// Find which fields are accessed. +static record_field_map_t +find_fields_accessed (); + +// Obtain intersection of unaccessed and non escaping types. +static record_field_offset_map_t +obtain_nonescaping_unaccessed_fields (tpartitions_t casting, + record_field_map_t record_field_map); + +// TODO: +// This was copy pasted from tree-ssa-structalias.c +// Maybe delete this and make the function visible? +static HOST_WIDE_INT +bitpos_of_field (const tree fdecl) +{ + if (!tree_fits_shwi_p (DECL_FIELD_OFFSET (fdecl)) + || !tree_fits_shwi_p (DECL_FIELD_BIT_OFFSET (fdecl))) + return -1; + + return (tree_to_shwi (DECL_FIELD_OFFSET (fdecl)) * BITS_PER_UNIT + + tree_to_shwi (DECL_FIELD_BIT_OFFSET (fdecl))); +} + +/* There are some cases where I need to change a const_tree to a tree. + * Some of these are part of the way the API is written. To avoid + * warnings, always use this function for casting away const-ness. */ -const pass_data pass_data_ipa_type_escape_analysis = +inline static tree +const_tree_to_tree (const_tree t) { + return (tree) t; +} + +namespace { +const pass_data pass_data_ipa_type_escape_analysis = { SIMPLE_IPA_PASS, "type-escape-analysis", OPTGROUP_NONE, @@ -77,293 +238,3175 @@ class pass_ipa_type_escape_analysis : public simple_ipa_opt_pass { public: pass_ipa_type_escape_analysis (gcc::context *ctx) - : simple_ipa_opt_pass(pass_data_ipa_type_escape_analysis, ctx) + : simple_ipa_opt_pass (pass_data_ipa_type_escape_analysis, ctx) {} - virtual bool gate(function*) { return in_lto_p && flag_ipa_type_escape_analysis && flag_profile_use; } - virtual unsigned execute (function*) { return iphw_execute(); } + virtual bool gate (function *) + { + return in_lto_p && flag_ipa_type_escape_analysis; + } + virtual unsigned execute (function *) + { + return lto_dfe_execute (); + } }; -} // anon namespace +} // namespace -simple_ipa_opt_pass* -make_pass_ipa_type_escape_analysis (gcc::context *ctx) +/* Top level function. */ +static unsigned int +lto_dfe_execute () { - return new pass_ipa_type_escape_analysis (ctx); + lto_dead_field_elimination (); + log ("finished!\n"); + return 0; } -static void collect_types(); +/* + * Perform dead field elimination at link-time. + * This transformation is composed of two main stages: + * * Finding out which fields are non escaping and unaccessed. + * * Creating new types and substituting their mention throughout the + * program. + */ +static void +lto_dead_field_elimination () +{ + // Analysis. + tpartitions_t escaping_nonescaping_sets + = partition_types_into_escaping_nonescaping (); + record_field_map_t record_field_map = find_fields_accessed (); + record_field_offset_map_t record_field_offset_map + = obtain_nonescaping_unaccessed_fields (escaping_nonescaping_sets, + record_field_map); + if (record_field_offset_map.empty ()) + return; -static unsigned int -iphw_execute() +} + +/* Iterate all gimple bodies and collect trees + * which are themselves RECORD_TYPE or which + * somehow reach a RECORD_TYPE tree (e.g., via a + * pointer, array, reference, union, field, etc...). + * Let's call these trees record_reaching_trees. + */ +static tpartitions_t +partition_types_into_record_reaching_or_non_record_reaching () { - collect_types(); - return 0; + GimpleTypeCollector collector; + collector.walk (); + tpartitions_t partitions = collector.get_record_reaching_trees (); + return partitions; +} + +/* Iterate over all gimple bodies and find out + * which types are escaping AND are being casted. + */ +static tpartitions_t +partition_types_into_escaping_nonescaping () +{ + tpartitions_t partitions + = partition_types_into_record_reaching_or_non_record_reaching (); + GimpleCaster caster (partitions); + caster.walk (); + + // Dump for debugging. + if (flag_print_cast_analysis) + caster.print_reasons (); + + partitions = caster.get_sets (); + // Unify results from different trees representing the same type + // until a fixed point is reached. + fix_escaping_types_in_set (partitions); + return partitions; +} + +/* Iterate over all gimple bodies and find out + * which fields are accessed for all RECORD_TYPE + * types. + */ +static record_field_map_t +find_fields_accessed () +{ + GimpleAccesser accesser; + accesser.walk (); + + // Dump for debugging. + if (flag_print_access_analysis) + accesser.print_accesses (); + + // This record_field_map holds + // RECORD_TYPE -> (FIELD_DECL -> how field is accessed) + record_field_map_t record_field_map = accesser.get_map (); + return record_field_map; +} + +/* Find equivalent RECORD_TYPE trees to const_tree r_i. + * This equivalence will be used for merging the results of field accesses + * across all equivalent RECORD_TYPE trees. + + * r_i should exists in the points_to_record set + * and it is a tree for which this method is going to find the rest of + * equivalent trees found in record_field_map. + */ +static std::vector<const_tree> +find_equivalent_trees (const_tree r_i, record_field_map_t record_field_map, + tpartitions_t casting) +{ + TypeIncompleteEquality equality; + std::vector<const_tree> equivalence; + bool is_rin_record = casting.in_points_to_record (r_i); + if (!is_rin_record) + return equivalence; + + for (std::map<const_tree, field_access_map_t>::const_iterator j + = record_field_map.begin (), + f = record_field_map.end (); + j != f; j++) + { + const_tree r_j = j->first; + const bool pointer_equal = r_i == r_j; + if (pointer_equal) + continue; + + bool is_p_record = casting.in_points_to_record (r_i) + && casting.in_points_to_record (r_j); + if (!is_p_record) + continue; + + const bool are_equal = equality.equal (r_i, r_j); + if (!are_equal) + continue; + + equivalence.push_back (r_j); + } + return equivalence; } +/* + * FIELD is a FIELD_DECL tree, ACCESSED is a a bitflag that marks whether the + * field is read, written or neither. FIELD_OFFSET will hold the following map: + * tree (RECORD_TYPE) -> unsigned (bitpos_of_field for read fields). + */ static void -fix_escaping_types_in_set(ptrset_t &types) +add_offset_only_if_read (const_tree field, unsigned access, + field_offsets_t &field_offset) { - bool fixed_point_reached = false; - TypeIncompleteEquality structuralEquality; - TypeStringifier stringifier; - do { - std::vector<const_tree> fixes; - fixed_point_reached = true; - for (auto i = types.escaping.cbegin(), e = types.escaping.cend(); i != e; ++i) + assert_is_type (field, FIELD_DECL); + const bool is_read = access & Read; + if (!is_read) + return; + + tree _field = const_tree_to_tree (field); + unsigned f_offset = bitpos_of_field (_field); + field_offset.insert (f_offset); +} + +/* + * FIELD_MAP holds the following map: + * tree (FIELD_DECL) -> access type + * FIELD_OFFSET is being built here. + * It should hold + * tree (RECORD_TYPE) -> bitpos_of_field for read fields). + */ +static void +keep_only_read_fields_from_field_map (field_access_map_t &field_map, + field_offsets_t &field_offset) +{ + for (std::map<const_tree, unsigned>::iterator j = field_map.begin (), + f = field_map.end (); + j != f; ++j) { - for (auto j = types.non_escaping.cbegin(), f = types.non_escaping.cend(); j != f; ++j) - { - const_tree type_esc = *i; - gcc_assert(type_esc); - const_tree type_non = *j; - gcc_assert(type_non); - // There can be cases where incomplete types are marked as non-escaping - // and complete types counter parts are marked as escaping. - //const bool interesting_case = eq_type_compare(type_esc, type_non); - //TODO: We are going to need a different type comparison because this one - //fails to take into account the recursion... - std::string type_esc_name = TypeStringifier::get_type_identifier(type_esc); - std::string type_non_name = TypeStringifier::get_type_identifier(type_non); - - type_esc_name = stringifier.stringify(type_esc); - type_non_name = stringifier.stringify(type_non); - - const bool equal = structuralEquality.equal(type_esc, type_non); - if (!equal) continue; - - log("recalulating %s == %s\n", type_esc_name.c_str(), type_non_name.c_str()); - fixed_point_reached = false; - // Add incomplete to escaping - // delete incomplete from non_escaping - // We shouldn't do that inside our iteration loop. - fixes.push_back(type_non); - } + add_offset_only_if_read (j->first, j->second, field_offset); } +} - for (auto i = fixes.cbegin(), e = fixes.cend(); i != e; ++i) +/* + * EQUIVALENT holds equivalent trees of RECORD_TYPE + * Update FIELD_OFFSET as the union of all READ FIELDS for the equivalent trees. + */ +static void +keep_only_read_fields_from_equivalent_field_maps ( + std::vector<const_tree> equivalent, record_field_map_t &record_field_map, + field_offsets_t &field_offset) +{ + for (std::vector<const_tree>::iterator j = equivalent.begin (), + f = equivalent.end (); + j != f; j++) { - const_tree escaping_type = *i; - types.escaping.insert(escaping_type); - types.non_escaping.erase(escaping_type); + const_tree r_j = *j; + field_access_map_t equivalent_field_map = record_field_map[r_j]; + keep_only_read_fields_from_field_map (equivalent_field_map, field_offset); } - } while (!fixed_point_reached); } +/* + * Whether RECORDS are escaping or can't be modified, + * delete them from the set of candidate RECORDS to be modified. + */ static void -collect_types() +erase_if_no_fields_can_be_deleted ( + record_field_offset_map_t &record_field_offset_map, + std::set<const_tree> &to_keep, std::set<const_tree> &to_erase) +{ + for (std::map<const_tree, field_offsets_t>::iterator i + = record_field_offset_map.begin (), + e = record_field_offset_map.end (); + i != e; ++i) + { + const_tree record = i->first; + const bool keep = to_keep.find (record) != to_keep.end (); + if (keep) + continue; + + to_erase.insert (record); + } + + for (std::set<const_tree>::iterator i = to_erase.begin (), + e = to_erase.end (); + i != e; ++i) + { + const_tree record = *i; + record_field_offset_map.erase (record); + } +} + +/* + * Mark escaping RECORD_TYPEs as ready to be deleted from the + * set of candidates to be modified. + */ +static void +mark_escaping_types_to_be_deleted ( + record_field_offset_map_t &record_field_offset_map, + std::set<const_tree> &to_erase, tpartitions_t casting) +{ + const tset_t &non_escaping = casting.non_escaping; + for (std::map<const_tree, field_offsets_t>::iterator i + = record_field_offset_map.begin (), + e = record_field_offset_map.end (); + i != e; ++i) + { + const_tree record = i->first; + const bool in_set = non_escaping.find (record) != non_escaping.end (); + if (in_set) + continue; + + to_erase.insert (record); + } +} + +// Obtain nonescaping unaccessed fields +static record_field_offset_map_t +obtain_nonescaping_unaccessed_fields (tpartitions_t casting, + record_field_map_t record_field_map) { - GimpleTypeCollector collector; - collector.walk(); - collector.print_collected(); - ptrset_t types = collector.get_pointer_set(); - GimpleCaster caster(types); - caster.walk(); - if (flag_print_cast_analysis) caster.print_reasons(); - ptrset_t casting = caster.get_sets(); - fix_escaping_types_in_set(casting); - GimpleAccesser accesser; - accesser.walk(); - if (flag_print_access_analysis) accesser.print_accesses(); - record_field_map_t record_field_map = accesser.get_map(); - TypeIncompleteEquality equality; bool has_fields_that_can_be_deleted = false; - typedef std::set<unsigned> field_offsets_t; - typedef std::map<const_tree, field_offsets_t> record_field_offset_map_t; record_field_offset_map_t record_field_offset_map; - //TODO: We need to optimize this, compiling GCC is taking too long - for (auto i = record_field_map.begin(), e = record_field_map.end(); i != e; ++i) - { - const_tree r_i = i->first; - std::vector<const_tree> equivalence; - for (auto j = record_field_map.cbegin(), f = record_field_map.cend(); j != f; j++) + for (std::map<const_tree, field_access_map_t>::iterator i + = record_field_map.begin (), + e = record_field_map.end (); + i != e; ++i) + { + const_tree r_i = i->first; + std::vector<const_tree> equivalence + = find_equivalent_trees (r_i, record_field_map, casting); + field_offsets_t field_offset; + field_access_map_t original_field_map = record_field_map[r_i]; + keep_only_read_fields_from_field_map (original_field_map, field_offset); + keep_only_read_fields_from_equivalent_field_maps (equivalence, + record_field_map, + field_offset); + // These map holds the following: + // RECORD_TYPE -> unsigned (bit_pos_offset which has been read) + record_field_offset_map[r_i] = field_offset; + } + + // So now that we only have the FIELDS which are read, + // we need to compute the complement... + + // Improve: This is tightly coupled, I need to decouple it... + std::set<const_tree> to_erase; + std::set<const_tree> to_keep; + mark_escaping_types_to_be_deleted (record_field_offset_map, to_erase, + casting); + for (std::map<const_tree, field_offsets_t>::iterator i + = record_field_offset_map.begin (), + e = record_field_offset_map.end (); + i != e; ++i) { - const_tree r_j = j->first; - const bool pointer_equal = r_i == r_j; - if (pointer_equal) continue; + const_tree record = i->first; + const bool will_be_erased = to_erase.find (record) != to_erase.end (); + // No need to compute which fields can be deleted if type is escaping + if (will_be_erased) + continue; - bool is_p_record = casting.in_points_to_record(r_i) && casting.in_points_to_record(r_j); - if (!is_p_record) continue; + field_offsets_t field_offset = i->second; + for (tree field = TYPE_FIELDS (record); field; field = DECL_CHAIN (field)) + { + unsigned f_offset = bitpos_of_field (field); + bool in_set2 = field_offset.find (f_offset) != field_offset.end (); + if (in_set2) + { + field_offset.erase (f_offset); + continue; + } + to_keep.insert (record); + field_offset.insert (f_offset); + has_fields_that_can_be_deleted = true; + // NOTE: With anonymous fields this might be weird to print. + log ("%s.%s may be deleted\n", + TypeStringifier::get_type_identifier (record).c_str (), + TypeStringifier::get_field_identifier (field).c_str ()); - const bool are_equal = equality.equal(r_i, r_j); - if (!are_equal) continue; + if (OPT_Wdfa == 0) continue; + // Anonymous fields? (Which the record can be!). + warning (OPT_Wdfa, "RECORD_TYPE %qE has dead field %qE in LTO.\n", + record, field); + } + record_field_offset_map[record] = field_offset; + } - equivalence.push_back(r_j); + // Improve: Make this more elegant. + if (!has_fields_that_can_be_deleted) + { + record_field_offset_map_t empty; + return empty; } - field_offsets_t field_offset; - field_access_map_t original_field_map = record_field_map[r_i]; - for (auto j = original_field_map.begin(), f = original_field_map.end(); j != f; ++j) + erase_if_no_fields_can_be_deleted (record_field_offset_map, to_keep, + to_erase); + + return record_field_offset_map; +} + +// Main interface to TypeWalker +// Start recursive walk +void +TypeWalker::walk (const_tree t) +{ + gcc_assert (t); + this->tset.clear (); + this->_walk (t); +} + +void +TypeWalker::_walk (const_tree type) +{ + // Improve, verify that having a type is an invariant. + // I think there was a specific example which didn't + // allow for it + if (!type) + return; + + gcc_assert (type); + + // This is an optimization. + const bool _is_memoized = is_memoized (type); + if (_is_memoized) + return; + + // This is for correctness + // Some types are represented as a graph + // of trees and therefore we need a way to + // avoid loops in this graph. + // Imrpove: Outline finding if it is recursive? + const bool is_recursing = tset.find (type) != tset.end (); + if (is_recursing) + return; + + tset.insert (type); + const enum tree_code code = TREE_CODE (type); + switch (code) { - const_tree f_k = j->first; - unsigned access = j->second; - const bool is_read = access & Read; - unsigned f_offset = tree_to_uhwi(DECL_FIELD_OFFSET(f_k)); - unsigned f_offset_2 = tree_to_uhwi(DECL_FIELD_BIT_OFFSET(f_k)); - //log("%s offset %u %u is_read %s\n", TypeStringifier::get_field_identifier(f_k).c_str(), f_offset, f_offset_2, is_read ? "t" :"f"); - if (!is_read) continue; - field_offset.insert(f_offset * 8 + f_offset_2); + case VOID_TYPE: + this->walk_VOID_TYPE (type); + break; + case INTEGER_TYPE: + this->walk_INTEGER_TYPE (type); + break; + case REAL_TYPE: + this->walk_REAL_TYPE (type); + break; + case FIXED_POINT_TYPE: + this->walk_FIXED_POINT_TYPE (type); + break; + case COMPLEX_TYPE: + this->walk_COMPLEX_TYPE (type); + break; + case ENUMERAL_TYPE: + this->walk_ENUMERAL_TYPE (type); + break; + case BOOLEAN_TYPE: + this->walk_BOOLEAN_TYPE (type); + break; + case OFFSET_TYPE: + this->walk_OFFSET_TYPE (type); + break; + case RECORD_TYPE: + this->walk_RECORD_TYPE (type); + break; + case POINTER_TYPE: + this->walk_POINTER_TYPE (type); + break; + case REFERENCE_TYPE: + this->walk_REFERENCE_TYPE (type); + break; + case ARRAY_TYPE: + this->walk_ARRAY_TYPE (type); + break; + case UNION_TYPE: + this->walk_UNION_TYPE (type); + break; + case FUNCTION_TYPE: + this->walk_FUNCTION_TYPE (type); + break; + case METHOD_TYPE: + this->walk_METHOD_TYPE (type); + break; + // Since we are dealing only with C at the moment, + // we don't care about QUAL_UNION_TYPE nor LANG_TYPEs + // So fail early. + case QUAL_UNION_TYPE: + case LANG_TYPE: + default: + { + log ("missing %s\n", get_tree_code_name (code)); + gcc_unreachable (); + } + break; } - for (auto j = equivalence.begin(), f = equivalence.end(); j != f; j++) + tset.erase (type); +} + +// This is used to walk over subtrees. +// But before walking subtrees, we need to +// call the pre-order callback +// and after we need to +// call the post-order callback. +#define TypeWalkerFuncDef(code) \ + void TypeWalker::walk_##code (const_tree t) \ + { \ + assert_is_type (t, code); \ + _walk_##code##_pre (t); \ + _walk_##code (t); \ + _walk_##code##_post (t); \ + } + +#define TypeWalkerFuncDefInternal(code) \ + void TypeWalker::_walk_##code (__attribute__ ((unused)) const_tree t) \ + {} + +TypeWalkerFuncDef (VOID_TYPE) +TypeWalkerFuncDefInternal (VOID_TYPE) +TypeWalkerFuncDef (INTEGER_TYPE) +TypeWalkerFuncDefInternal (INTEGER_TYPE) +TypeWalkerFuncDef (REAL_TYPE) +TypeWalkerFuncDefInternal (REAL_TYPE) +TypeWalkerFuncDef (BOOLEAN_TYPE) +TypeWalkerFuncDefInternal (BOOLEAN_TYPE) +TypeWalkerFuncDef (OFFSET_TYPE) +TypeWalkerFuncDefInternal (OFFSET_TYPE) +TypeWalkerFuncDef (FIXED_POINT_TYPE) +TypeWalkerFuncDefInternal (FIXED_POINT_TYPE) +TypeWalkerFuncDef (COMPLEX_TYPE) +TypeWalkerFuncDefInternal (COMPLEX_TYPE) +TypeWalkerFuncDef (ENUMERAL_TYPE) +TypeWalkerFuncDefInternal (ENUMERAL_TYPE) + +/* walk wrapper is used for unwrapping + * REFERENCE_TYPE, POINTER_TYPE, ARRAY_TYPE. + */ +void TypeWalker::_walk_wrapper (const_tree t) +{ + const_tree inner_type = TREE_TYPE (t); + // I think I encountered this code: + // FIXME: Do we really need this? + if (!inner_type) + return; + + gcc_assert (inner_type); + _walk (inner_type); +} + +#define TypeWalkerFuncDefWrapper(code) \ + void TypeWalker::_walk_##code (const_tree t) \ + { _walk_wrapper (t); } + +TypeWalkerFuncDef (POINTER_TYPE) +TypeWalkerFuncDefWrapper (POINTER_TYPE) +TypeWalkerFuncDefWrapper (REFERENCE_TYPE) +TypeWalkerFuncDef (REFERENCE_TYPE) +TypeWalkerFuncDef (ARRAY_TYPE) +TypeWalkerFuncDefWrapper (ARRAY_TYPE) +TypeWalkerFuncDef (RECORD_TYPE) + +void +TypeWalker::_walk_RECORD_TYPE (const_tree t) +{ + _walk_record_or_union (t); +} + +TypeWalkerFuncDef (UNION_TYPE) + +void +TypeWalker::_walk_UNION_TYPE (const_tree t) +{ + _walk_record_or_union (t); +} + +void +TypeWalker::_walk_record_or_union (const_tree t) +{ + for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field)) { - const_tree r_j = *j; - field_access_map_t equivalent_field_map = record_field_map[r_j]; + gcc_assert (field); + walk_field (field); + } +} + +void +TypeWalker::walk_field (const_tree t) +{ + _walk_field_pre (t); + _walk_field (t); + _walk_field_post (t); +} + +void +TypeWalker::_walk_field (const_tree t) +{ + const_tree inner_type = TREE_TYPE (t); + gcc_assert (inner_type); + _walk (inner_type); +} + +TypeWalkerFuncDef (FUNCTION_TYPE) + + void TypeWalker::_walk_FUNCTION_TYPE (const_tree t) +{ + _walk_function_or_method (t); +} + +TypeWalkerFuncDef (METHOD_TYPE) + + void TypeWalker::_walk_METHOD_TYPE (const_tree t) +{ + _walk_function_or_method (t); +} + +void +TypeWalker::_walk_function_or_method (const_tree t) +{ + const_tree ret_type = TREE_TYPE (t); + walk_return (ret_type); + walk_args (t); +} + +void +TypeWalker::walk_return (const_tree t) +{ + _walk_return_pre (t); + _walk_return (t); + _walk_return_post (t); +} + +void +TypeWalker::_walk_return (const_tree t) +{ + _walk (t); +} + +void +TypeWalker::walk_args (const_tree t) +{ + _walk_args_pre (t); + _walk_args (t); + _walk_args_post (t); +} + +void +TypeWalker::_walk_args (const_tree t) +{ + for (tree arg_node = TYPE_ARG_TYPES (t); NULL_TREE != arg_node; + arg_node = TREE_CHAIN (arg_node)) + { + const_tree arg_node_type = TREE_VALUE (arg_node); + gcc_assert (arg_node_type); + walk_arg (arg_node_type); + } +} - for (auto k = equivalent_field_map.begin(), g = equivalent_field_map.end(); k != g; ++k) +void +TypeWalker::walk_arg (const_tree t) +{ + _walk_arg_pre (t); + _walk_arg (t); + _walk_arg_post (t); +} + +void +TypeWalker::_walk_arg (const_tree t) +{ + _walk (t); +} + +/* Main interface for the ExprWalker... */ +void +ExprWalker::walk (const_tree e) +{ + _walk_pre (e); + _walk (e); + _walk_post (e); +} + +void +ExprWalker::_walk (const_tree e) +{ + gcc_assert (e); + const enum tree_code code = TREE_CODE (e); + switch (code) + { + case INTEGER_CST: + walk_INTEGER_CST (e); + break; + case REAL_CST: + walk_REAL_CST (e); + break; + case STRING_CST: + walk_STRING_CST (e); + break; + case BIT_FIELD_REF: + walk_BIT_FIELD_REF (e); + break; + case ARRAY_REF: + walk_ARRAY_REF (e); + break; + case MEM_REF: + walk_MEM_REF (e); + break; + case COMPONENT_REF: + walk_COMPONENT_REF (e); + break; + case SSA_NAME: + walk_SSA_NAME (e); + break; + case ADDR_EXPR: + walk_ADDR_EXPR (e); + break; + case VIEW_CONVERT_EXPR: + walk_VIEW_CONVERT_EXPR (e); + break; + case IMAGPART_EXPR: + walk_IMAGPART_EXPR (e); + break; + case VAR_DECL: + walk_VAR_DECL (e); + break; + case FIELD_DECL: + walk_FIELD_DECL (e); + break; + case RESULT_DECL: + walk_RESULT_DECL (e); + break; + case PARM_DECL: + walk_PARM_DECL (e); + break; + case FUNCTION_DECL: + walk_FUNCTION_DECL (e); + break; + case CONSTRUCTOR: + walk_CONSTRUCTOR (e); + break; + case LE_EXPR: + walk_LE_EXPR (e); + break; + case LT_EXPR: + walk_LT_EXPR (e); + break; + case EQ_EXPR: + walk_EQ_EXPR (e); + break; + case GT_EXPR: + walk_GT_EXPR (e); + break; + case GE_EXPR: + walk_GE_EXPR (e); + break; + case NE_EXPR: + walk_NE_EXPR (e); + break; + default: { - const_tree f_k = k->first; - unsigned access = k->second; - const bool is_read = access & Read; - unsigned f_offset = tree_to_uhwi(DECL_FIELD_OFFSET(f_k)); - unsigned f_offset_2 = tree_to_uhwi(DECL_FIELD_BIT_OFFSET(f_k)); - //log("%s offset %u %u is_read %s\n", TypeStringifier::get_field_identifier(f_k).c_str(), f_offset, f_offset_2, is_read ? "t" :"f"); - if (!is_read) continue; - field_offset.insert(f_offset * 8 + f_offset_2); + log ("missing %s\n", get_tree_code_name (code)); + gcc_unreachable (); } + break; } - record_field_offset_map[r_i] = field_offset; +} + +/* call pre-order callback for everything + * call pre-order callback for specific code + * walk subtrees + * call post-order callback for specific code + * call post-order callback for everything. + */ +#define ExprWalkerFuncDef(code) \ + void ExprWalker::walk_##code (const_tree e) \ + { \ + assert_is_type (e, code); \ + _walk_pre (e); \ + _walk_##code##_pre (e); \ + _walk_##code (e); \ + _walk_##code##_post (e); \ + _walk_post (e); \ } +ExprWalkerFuncDef (CONSTRUCTOR) +ExprWalkerFuncDef (INTEGER_CST) +ExprWalkerFuncDef (REAL_CST) +ExprWalkerFuncDef (STRING_CST) +ExprWalkerFuncDef (BIT_FIELD_REF) +ExprWalkerFuncDef (ARRAY_REF) +ExprWalkerFuncDef (MEM_REF) +ExprWalkerFuncDef (COMPONENT_REF) +ExprWalkerFuncDef (SSA_NAME) +ExprWalkerFuncDef (ADDR_EXPR) +ExprWalkerFuncDef (VIEW_CONVERT_EXPR) +ExprWalkerFuncDef (IMAGPART_EXPR) +ExprWalkerFuncDef (FIELD_DECL) +ExprWalkerFuncDef (VAR_DECL) +ExprWalkerFuncDef (RESULT_DECL) +ExprWalkerFuncDef (PARM_DECL) +ExprWalkerFuncDef (FUNCTION_DECL) +ExprWalkerFuncDef (LE_EXPR) +ExprWalkerFuncDef (LT_EXPR) +ExprWalkerFuncDef (EQ_EXPR) +ExprWalkerFuncDef (GT_EXPR) +ExprWalkerFuncDef (GE_EXPR) +ExprWalkerFuncDef (NE_EXPR) - const typeset &non_escaping = casting.non_escaping; - - std::vector<const_tree> to_erase; - std::set<const_tree> to_keep; - for (auto i = record_field_offset_map.begin(), e = record_field_offset_map.end(); i != e; ++i) +void ExprWalker::_walk_leaf (const_tree e, const enum tree_code c) +{ + assert_is_type (e, c); +} + +void +ExprWalker::_walk_op_n (const_tree e, unsigned n) +{ + gcc_assert (e); + const_tree op_n = TREE_OPERAND (e, n); + gcc_assert (op_n); + walk (op_n); +} + +void +ExprWalker::_walk_op_0 (const_tree e, const enum tree_code c) +{ + assert_is_type (e, c); + _walk_op_n (e, 0); +} + +void +ExprWalker::_walk_op_1 (const_tree e, const enum tree_code c) +{ + assert_is_type (e, c); + _walk_op_n (e, 0); + _walk_op_n (e, 1); +} + +void +ExprWalker::_walk_CONSTRUCTOR (__attribute__ ((unused)) const_tree e) +{ + // Future-work: If we want to support rewriting CONSTRUCTORs + // we will have to walk them +} + +void +ExprWalker::_walk_LE_EXPR (const_tree e) +{ + _walk_op_1 (e, LE_EXPR); +} + +void +ExprWalker::_walk_LT_EXPR (const_tree e) +{ + _walk_op_1 (e, LT_EXPR); +} + +void +ExprWalker::_walk_EQ_EXPR (const_tree e) +{ + _walk_op_1 (e, EQ_EXPR); +} + +void +ExprWalker::_walk_GT_EXPR (const_tree e) +{ + _walk_op_1 (e, GT_EXPR); +} + +void +ExprWalker::_walk_GE_EXPR (const_tree e) +{ + _walk_op_1 (e, GE_EXPR); +} + +void +ExprWalker::_walk_NE_EXPR (const_tree e) +{ + _walk_op_1 (e, NE_EXPR); +} + +void +ExprWalker::_walk_INTEGER_CST (const_tree e) +{ + _walk_leaf (e, INTEGER_CST); +} + +void +ExprWalker::_walk_REAL_CST (const_tree e) +{ + _walk_leaf (e, REAL_CST); +} + +void +ExprWalker::_walk_STRING_CST (const_tree e) +{ + _walk_leaf (e, STRING_CST); +} + +void +ExprWalker::_walk_BIT_FIELD_REF (__attribute__ ((unused)) const_tree e) +{ + // TODO: + // We currently don't support bit_field_ref + // but maybe we need to do something here? +} + +void +ExprWalker::_walk_ARRAY_REF (const_tree e) +{ + _walk_op_1 (e, ARRAY_REF); +} + +void +ExprWalker::_walk_MEM_REF (const_tree e) +{ + _walk_op_1 (e, MEM_REF); +} + +void +ExprWalker::_walk_COMPONENT_REF (const_tree e) +{ + _walk_op_1 (e, COMPONENT_REF); +} + +void +ExprWalker::_walk_SSA_NAME (const_tree e) +{ + _walk_leaf (e, SSA_NAME); +} + +void +ExprWalker::_walk_ADDR_EXPR (const_tree e) +{ + _walk_op_0 (e, ADDR_EXPR); +} + +void +ExprWalker::_walk_VIEW_CONVERT_EXPR (__attribute__ ((unused)) const_tree e) +{ + // TODO: I don't think we need to do anything here +} + +void +ExprWalker::_walk_IMAGPART_EXPR (__attribute__ ((unused)) const_tree e) +{ + // TODO: I don't think we need to do anything here +} + +void +ExprWalker::_walk_FIELD_DECL (const_tree e) +{ + _walk_leaf (e, FIELD_DECL); +} + +void +ExprWalker::_walk_VAR_DECL (const_tree e) +{ + _walk_leaf (e, VAR_DECL); +} + +void +ExprWalker::_walk_RESULT_DECL (const_tree e) +{ + _walk_leaf (e, RESULT_DECL); +} + +void +ExprWalker::_walk_PARM_DECL (const_tree e) +{ + _walk_leaf (e, PARM_DECL); +} + +void +ExprWalker::_walk_FUNCTION_DECL (const_tree e) +{ + _walk_leaf (e, FUNCTION_DECL); + for (tree parm = DECL_ARGUMENTS (e); parm; parm = DECL_CHAIN (parm)) + { + walk (parm); + } +} + +/* Main interface for GimpleWalker: + * iterate over global variables and then for all functions + * with gimple body. + */ +void +GimpleWalker::walk () +{ + _walk_globals (); + std::set<tree> fndecls; + cgraph_node *node = NULL; + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) + { + node->get_untransformed_body (); + tree decl = node->decl; + gcc_assert (decl); + const bool already_in_set = fndecls.find (decl) != fndecls.end (); + // I think it is possible for different nodes to point to the same + // declaration. + if (already_in_set) + continue; + + _walk_cnode (node); + fndecls.insert (decl); + } +} + +/* For each global variable. */ +void +GimpleWalker::_walk_globals () +{ + varpool_node *vnode = NULL; + FOR_EACH_VARIABLE (vnode) + { + _walk_global (vnode); + } +} + +/* Walk over global variable VNODE. */ +void +GimpleWalker::_walk_global (varpool_node *vnode) +{ + gcc_assert (vnode); + struct ipa_ref *ref = NULL; + for (unsigned i = 0; vnode->iterate_referring (i, ref); i++) + { + tree var_decl = vnode->decl; + walk_tree2 (var_decl); + } +} + +/* Walk over SSA_NAMEs in CNODE. */ +void +GimpleWalker::_walk_ssa_names (cgraph_node *cnode) +{ + const_tree decl = cnode->decl; + gcc_assert (decl); + function *func = DECL_STRUCT_FUNCTION (decl); + gcc_assert (func); + size_t i = 0; + tree ssa_name = NULL; + push_cfun (func); + FOR_EACH_SSA_NAME (i, ssa_name, cfun) { - const_tree record = i->first; - const bool in_set = non_escaping.find(record) != non_escaping.end(); - if (!in_set) { - to_erase.push_back(record); + gcc_assert (ssa_name); + walk_tree2 (ssa_name); + tree ssa_name_var = SSA_NAME_VAR (ssa_name); + if (!ssa_name_var) continue; + walk_tree2 (ssa_name_var); + } + pop_cfun (); +} + +/* Walk over declaration, locals, ssa_names, and basic blocks + * in CNODE. */ +void +GimpleWalker::_walk_cnode (cgraph_node *cnode) +{ + gcc_assert (cnode); + _walk_decl (cnode); + _walk_locals (cnode); + _walk_ssa_names (cnode); + _walk_bb (cnode); +} + +/* Walk over each local declaration in CNODE. */ +void +GimpleWalker::_walk_locals (cgraph_node *cnode) +{ + const_tree decl = cnode->decl; + gcc_assert (decl); + function *func = DECL_STRUCT_FUNCTION (decl); + gcc_assert (func); + int i = 0; + tree var_decl = NULL; + FOR_EACH_LOCAL_DECL (func, i, var_decl) + { + gcc_assert (var_decl); + walk_tree2 (var_decl); + } +} + +/* Walk over all basic blocks in CNODE. */ +void +GimpleWalker::_walk_bb (cgraph_node *cnode) +{ + gcc_assert (cnode); + cnode->get_untransformed_body (); + const_tree decl = cnode->decl; + gcc_assert (decl); + function *func = DECL_STRUCT_FUNCTION (decl); + gcc_assert (func); + basic_block bb = NULL; + push_cfun (func); + FOR_EACH_BB_FN (bb, func) + { + _walk (bb); } + pop_cfun (); +} + +/* Walk over CNODE->decl. */ +void +GimpleWalker::_walk_decl (cgraph_node *cnode) +{ + const_tree decl = cnode->decl; + gcc_assert (decl); + walk_tree2 (decl); +} - field_offsets_t field_offset = i->second; - for (tree field = TYPE_FIELDS(record); field; field = DECL_CHAIN(field)) +/* Walk over each gimple statement in BB. */ +void +GimpleWalker::_walk (basic_block bb) +{ + gcc_assert (bb); + gimple_stmt_iterator gsi = gsi_start_bb (bb); + while (!gsi_end_p (gsi)) { - unsigned f_offset = tree_to_uhwi(DECL_FIELD_OFFSET(field)); - unsigned f_offset_2 = tree_to_uhwi(DECL_FIELD_BIT_OFFSET(field)); - f_offset = f_offset * 8 + f_offset_2; - bool in_set2 = field_offset.find(f_offset) != field_offset.end(); - if (in_set2) { - field_offset.erase(f_offset); + this->_deleted = false; + gimple *stmt = gsi_stmt (gsi); + walk_gimple (stmt); + // If it is not deleted just continue. + if (!this->_deleted) + { + gsi_next (&gsi); continue; } - to_keep.insert(record); - field_offset.insert(f_offset); - has_fields_that_can_be_deleted = true; - log("%s.%s may be deleted\n", TypeStringifier::get_type_identifier(record).c_str(), TypeStringifier::get_field_identifier(field).c_str()); + + // Otherwise remove statement. + unlink_stmt_vdef (stmt); + gsi_remove (&gsi, true); } - record_field_offset_map[record] = field_offset; + + // TODO: Maybe outline to its own function? + for (gimple_stmt_iterator gsi = gsi_start_phis (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + walk_gimple (stmt); + } +} + +// call preorder callback +// walk subtrees +// call postorder callback +void +GimpleWalker::walk_gimple (gimple *stmt) +{ + _walk_pre_gimple (stmt); + _walk_gimple (stmt); + _walk_post_gimple (stmt); +} + +/* Switch for different gimple instruction types. */ +void +GimpleWalker::_walk_gimple (gimple *stmt) +{ +// Do not use _walk_pre (s) +// Subtle but important distinction, +// we want to differentiate calling here stamtent from s +// TODO: Maybe delete though? +// This could also be the source for the double insertion to stack bug? + if (gassign *_gassign = dyn_cast<gassign*>(stmt)) + { + _walk_pre_gimple(stmt); + walk_gassign(_gassign); + _walk_post_gimple(stmt); + return; } - for (auto i = to_erase.begin(), e = to_erase.end(); i != e; ++i) + if (greturn *_greturn = dyn_cast<greturn*>(stmt)) { - const_tree record = *i; - record_field_offset_map.erase(record); + _walk_pre_gimple(stmt); + walk_greturn(_greturn); + _walk_post_gimple(stmt); + return; } - if (!has_fields_that_can_be_deleted) return; + if (gcond *_gcond = dyn_cast<gcond*>(stmt)) + { + _walk_pre_gimple(stmt); + walk_gcond(_gcond); + _walk_post_gimple(stmt); + return; + } - TypeReconstructor reconstructor(record_field_offset_map); - TypeStringifier stringifier; + if (gcall *_gcall = dyn_cast<gcall*>(stmt)) + { + _walk_pre_gimple(stmt); + walk_gcall(_gcall); + _walk_post_gimple(stmt); + return; + } - // TODO: - // Here, what we want to do is we want to rewrite only the - // types which we believe we can rewrite, that and all types which - // point to those types... - // - // Otherwise, it will lead to difficulties in the future since - // we could be modifying many different types. - // So we have to make sure that we are only modifying the types of interest. - for (auto i = types.points_to_record.cbegin(), e = types.points_to_record.cend(); i != e; ++i) + if (glabel *_glabel = dyn_cast<glabel*>(stmt)) { - const_tree record = *i; - std::string name_from = stringifier.stringify(TYPE_MAIN_VARIANT(record)); - bool points_to_record = false; - const_tree tt = record; - - - //TODO: - //This is our little hack to make sure that we are - //only modifying types which are of interest. - //However, we really shouldn't. - //Let's clean the input to reconstructor.walk - while (TREE_TYPE(tt)) { tt = TREE_TYPE(tt); }; - points_to_record = TREE_CODE(tt) == RECORD_TYPE; - if (!points_to_record) continue; - - bool in_map = record_field_offset_map.find(tt) != record_field_offset_map.end(); - if (!in_map) continue; - - // We need to walk over the type main variant first... - reconstructor.walk(TYPE_MAIN_VARIANT(record)); - log("walking main variant %s\n", name_from.c_str()); + _walk_pre_gimple(stmt); + walk_glabel(_glabel); + _walk_post_gimple(stmt); + return; } - for (auto i = types.points_to_record.cbegin(), e = types.points_to_record.cend(); i != e; ++i) + if (gswitch *_gswitch = dyn_cast<gswitch*>(stmt)) { - const_tree record = *i; - std::string name_from = stringifier.stringify(record); - bool points_to_record = false; - const_tree tt = record; - - - //TODO: - //This is our little hack to make sure that we are - //only modifying types which are of interest. - //However, we really shouldn't. - //Let's clean the input to reconstructor.walk - while (TREE_TYPE(tt)) { tt = TREE_TYPE(tt); }; - points_to_record = TREE_CODE(tt) == RECORD_TYPE; - if (!points_to_record) continue; - - bool in_map = record_field_offset_map.find(tt) != record_field_offset_map.end(); - if (!in_map) continue; - - // We need to walk over the type main variant first... - reconstructor.walk(record); - log("walking non main%s\n", name_from.c_str()); + _walk_pre_gimple(stmt); + walk_gswitch(_gswitch); + _walk_post_gimple(stmt); + return; } - TypeReconstructor::reorg_record_map_t map = reconstructor.get_map(); - TypeReconstructor::reorg_field_map_t field_map = reconstructor.get_field_map(); + if (gphi *_gphi = dyn_cast<gphi*>(stmt)) + { + _walk_pre_gimple(stmt); + walk_gphi(_gphi); + _walk_post_gimple(stmt); + return; + } + + const enum gimple_code code = gimple_code (stmt); + switch (code) + { + case GIMPLE_PREDICT: + case GIMPLE_DEBUG: + case GIMPLE_NOP: + // I think it is safe to skip these + // but it would also be nice to walk their sub-trees + return; + break; + default: + break; + } + + // Break if something is unexpected. + const char *name = gimple_code_name[code]; + log ("gimple code name %s\n", name); + gcc_unreachable (); +} - for (auto i = map.cbegin(), e = map.cend(); i != e; ++i) +void +GimpleWalker::walk_tree2 (const_tree t) +{ + _walk_pre_tree (t); + _walk_tree (t); + _walk_post_tree (t); +} + +void +GimpleWalker::_walk_tree (const_tree t) +{} + +void +GimpleWalker::walk_gassign (gassign *g) +{ + _walk_pre_gassign (g); + _walk_gassign (g); + _walk_post_gassign (g); +} + +void +GimpleWalker::_walk_gassign (gassign *g) +{} + +void +GimpleWalker::walk_greturn (greturn *g) +{ + _walk_pre_greturn (g); + _walk_greturn (g); + _walk_post_greturn (g); +} + +void +GimpleWalker::_walk_greturn (greturn *g) +{} + +void +GimpleWalker::walk_gcond (gcond *g) +{ + _walk_pre_gcond (g); + _walk_gcond (g); + _walk_post_gcond (g); +} + +void +GimpleWalker::_walk_gcond (gcond *g) +{} + +void +GimpleWalker::walk_gcall (gcall *g) +{ + _walk_pre_gcall (g); + _walk_gcall (g); + _walk_post_gcall (g); +} + +void +GimpleWalker::_walk_gcall (gcall *g) +{} + +void +GimpleWalker::walk_glabel (glabel *g) +{ + _walk_pre_glabel (g); + _walk_glabel (g); + _walk_post_glabel (g); +} + +void +GimpleWalker::_walk_glabel (glabel *g) +{} + +void +GimpleWalker::walk_gswitch (gswitch *g) +{ + _walk_pre_gswitch (g); + _walk_gswitch (g); + _walk_post_gswitch (g); +} + +void +GimpleWalker::_walk_gswitch (gswitch *g) +{ +} + +void +GimpleWalker::walk_gphi (gphi *g) +{ + _walk_pre_gphi (g); + _walk_gphi (g); + _walk_post_gphi (g); +} + +void +GimpleWalker::_walk_gphi (gphi *g) +{ +} + + +void +TypeCollector::collect (const_tree t) +{ + const bool in_set = ptrset.in_universe (t); + // Early memoization... + + if (in_set) + return; + + // TODO: Can we move this to the beginning + // of the function. + gcc_assert (t); + + // This is the map that holds the types + // we will encounter in this walk. + // It should be empty at the beginning. + // It maps from tree -> bool. + // The boolean will be updated to show + // whether a record is reachable from + // the type. + gcc_assert (ptr.empty ()); + walk (t); +} + +// Make sure partitions are mutually exclusive. +void +TypeCollector::_sanity_check () +{ + for (tset_t::iterator i = ptrset.points_to_record.begin (), + e = ptrset.points_to_record.end (); + i != e; ++i) + { + for (tset_t::iterator j = ptrset.complement.begin (), f = ptrset.complement.end (); + j != f; ++j) + { + const_tree type_ptr = *i; + gcc_assert (type_ptr); + const_tree type_com = *j; + gcc_assert (type_com); + const bool valid_sets = type_ptr != type_com; + if (valid_sets) + continue; + + // Normally, we want a stronger type comparison + // that is not just the pointer address + // but this is the first sanity check and then we will need to + // determine the stronger type comparison. But first we will need to + // fix the types... + gcc_unreachable (); + } + } +} + +/* Determine if T is already memoized in the TypeCollector. */ +bool +TypeCollector::is_memoized (const_tree t) +{ + /* If we haven't seen it then no. */ + const bool in_set = ptrset.in_universe (t); + if (!in_set) + return false; + + // If the memoized type points to a record + // we must update all types that can refer + // to memoized type. + const bool points_to_record = ptrset.in_points_to_record (t); + for (std::map<const_tree, bool>::iterator i = ptr.begin (), e = ptr.end (); i != e; ++i) + { + i->second |= points_to_record; + } + return true; +} + +void +TypeCollector::_walk_VOID_TYPE_pre (const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_VOID_TYPE_post (const_tree t) +{ + _collect_simple (t); +} + +void +TypeCollector::_walk_INTEGER_TYPE_pre (const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_INTEGER_TYPE_post (const_tree t) +{ + _collect_simple (t); +} + +void +TypeCollector::_walk_REAL_TYPE_pre (const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_REAL_TYPE_post (const_tree t) +{ + _collect_simple (t); +} + +void +TypeCollector::_walk_FIXED_POINT_TYPE_pre (const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_FIXED_POINT_TYPE_post (const_tree t) +{ + _collect_simple (t); +} + +void +TypeCollector::_walk_COMPLEX_TYPE_pre (const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_COMPLEX_TYPE_post (const_tree t) +{ + _collect_simple (t); +} + +void +TypeCollector::_walk_ENUMERAL_TYPE_pre (const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_ENUMERAL_TYPE_post (const_tree t) +{ + _collect_simple (t); +} + +void +TypeCollector::_walk_BOOLEAN_TYPE_pre (const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_BOOLEAN_TYPE_post (const_tree t) +{ + _collect_simple (t); +} + +void +TypeCollector::_collect_simple (const_tree t) +{ + // Insert into persistent set. + ptrset.insert (t, ptr[t]); + // erase from current working set. + ptr.erase (t); +} + +void +TypeCollector::_walk_ARRAY_TYPE_pre (const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_ARRAY_TYPE_post (const_tree t) +{ + _collect_simple (t); +} + +void +TypeCollector::_walk_POINTER_TYPE_pre (const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_POINTER_TYPE_post (const_tree t) +{ + _collect_simple (t); +} + +void +TypeCollector::_walk_REFERENCE_TYPE_pre (const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_REFERENCE_TYPE_post (const_tree t) +{ + _collect_simple (t); +} + +void +TypeCollector::_walk_RECORD_TYPE_post (const_tree t) +{ + // All in ptr point to record + for (std::map<const_tree, bool>::iterator i = ptr.begin (), e = ptr.end (); i != e; ++i) + { + i->second = true; + } + _collect_simple (t); +} + +void +TypeCollector::_walk_RECORD_TYPE_pre (const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_UNION_TYPE_pre (const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_UNION_TYPE_post (const_tree t) +{ + _collect_simple (t); +} + +void +TypeCollector::_walk_FUNCTION_TYPE_post (const_tree t) +{ + _collect_simple (t); +} + +void +TypeCollector::_walk_FUNCTION_TYPE_pre (const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_METHOD_TYPE_post (const_tree t) +{ + _collect_simple (t); +} + +void +TypeCollector::_walk_METHOD_TYPE_pre (const_tree t) +{ + ptr[t] = false; +} + +inline void +ExprCollector::_walk_pre (const_tree e) +{ + const_tree t = TREE_TYPE (e); + gcc_assert (t); + typeCollector.collect (t); +} + +/* + * For global variables T, this method will collect + * and partition trees corresponding to types + * into std::sets. Concretely speaking, this class + * partitions trees into two sets: + * * reach a RECORD_TYPE + * * does not reach a RECORD_TYPE. + */ +void +GimpleTypeCollector::_walk_pre_tree (const_tree t) +{ + exprCollector.walk (t); +} + +/* + * For assignment statements S, this method will collect + * and partition trees corresponding to types + * into std::sets. Concretely speaking, this class + * partitions trees into two sets: + * * reach a RECORD_TYPE + * * does not reach a RECORD_TYPE + * The types reachable from the lhs and rhs are placed + * in these sets. + */ +void +GimpleTypeCollector::_walk_pre_gassign (gassign *s) +{ + const_tree lhs = gimple_assign_lhs (s); + exprCollector.walk (lhs); + + const enum gimple_rhs_class gclass = gimple_assign_rhs_class (s); + switch (gclass) + { + case GIMPLE_TERNARY_RHS: + { + const_tree rhs = gimple_assign_rhs3 (s); + exprCollector.walk (rhs); + } + /* fall-through */ + case GIMPLE_BINARY_RHS: + { + const_tree rhs = gimple_assign_rhs2 (s); + exprCollector.walk (rhs); + } + /* fall-through */ + case GIMPLE_UNARY_RHS: + case GIMPLE_SINGLE_RHS: + { + const_tree rhs = gimple_assign_rhs1 (s); + exprCollector.walk (rhs); + } + break; + default: + gcc_unreachable (); + break; + } +} + +void +GimpleTypeCollector::_walk_pre_greturn (greturn *s) +{ + const_tree retval = gimple_return_retval (s); + if (!retval) + return; + + exprCollector.walk (retval); +} + +void +GimpleTypeCollector::_walk_pre_gcall (gcall *s) +{ + // Walk over the arguments. + unsigned n = gimple_call_num_args (s); + for (unsigned i = 0; i < n; i++) + { + const_tree a = gimple_call_arg (s, i); + exprCollector.walk (a); + } + + // Walk over the return type if it exists. + const_tree lhs = gimple_call_lhs (s); + if (!lhs) + return; + + exprCollector.walk (lhs); +} + +// Print working set. +void +GimpleTypeCollector::print_collected () +{ + tpartitions_t sets = get_record_reaching_trees (); +} + +/* Walk over LHS and RHS of conditions. */ +void +GimpleTypeCollector::_walk_pre_gcond (gcond *s) +{ + const_tree lhs = gimple_cond_lhs (s); + exprCollector.walk (lhs); + const_tree rhs = gimple_cond_rhs (s); + exprCollector.walk (rhs); +} + +bool +TypeEscaper::is_memoized (__attribute__ ((unused)) const_tree t) +{ + // Can't memoize here because + // information is propagated up and down + // the type. + return false; +} + +tpartitions_t +TypeEscaper::get_sets () +{ + place_escaping_types_in_set (); + return _ptrset; +} + +/* From a map of TREE -> BOOL, the key represents a tree type + * and the value represents whether the tree escapes. + * Partition this map into sets. + */ +void +TypeEscaper::place_escaping_types_in_set () +{ + TypeStringifier stringifier; + for (typemap::iterator i = calc.begin (), e = calc.end (); i != e; ++i) + { + const_tree type = i->first; + + // We should only track interesting types + // Types which are not in points_to_record are the ones + // that are pointed to by records. + // I think it is possible to prune them ahead of time... + if (!_ptrset.in_points_to_record (type)) + continue; + + const Reason reason = i->second; + // std::string name = stringifier.stringify(type); + reason.is_escaping () ? _ptrset.escaping.insert (type) + : _ptrset.non_escaping.insert (type); + } +} + +void +TypeEscaper::update (const_tree t, Reason r) +{ + gcc_assert (t); + _reason = r; + walk (t); +} + +void +TypeEscaper::_update (const_tree t) +{ + gcc_assert (t); + const bool already_in_typemap = calc.find (t) != calc.end (); + // Do we have to invalidate all types which point to a volatile type? + // Or do we have to invalidate all types pointed to by a volatile type? + // Or do we only invalidate all types which are volatile. + // This is only the third option. + const bool is_volatile = TYPE_VOLATILE (t); + Reason _is_volatile; + _is_volatile.type_is_volatile = is_volatile; + Reason _inner = _reason | _is_volatile; + // always OR + already_in_typemap ? calc[t] |= _inner : calc[t] = _inner; +} + +void +TypeEscaper::_walk_ARRAY_TYPE_pre (const_tree t) +{ + _update (t); +} + +void +TypeEscaper::_walk_ARRAY_TYPE_post (const_tree t) +{ + _update (t); +} + +void +TypeEscaper::_walk_POINTER_TYPE_pre (const_tree t) +{ + _update (t); +} + +void +TypeEscaper::_walk_POINTER_TYPE_post (const_tree t) +{ + _update (t); +} + +void +TypeEscaper::_walk_REFERENCE_TYPE_pre (const_tree t) +{ + _update (t); +} + +void +TypeEscaper::_walk_RECORD_TYPE_pre (const_tree t) +{ + // we are entering a record + _inside_record++; + _update (t); +} + +void +TypeEscaper::_walk_RECORD_TYPE_post (const_tree t) +{ + _update (t); // This is in case there was a union + // we are exiting a record + _inside_record--; +} + +/* Mark as escaping because of union + * and propagate up and down. + */ +void +TypeEscaper::_walk_UNION_TYPE_pre (const_tree t) +{ + _inside_union++; + bool is_escaping = _inside_union > 0; + _reason.type_is_in_union |= is_escaping; + _update (t); +} + +/* Mark bit fields as escaping and propagate up + * and down. + */ +void +TypeEscaper::_walk_field_pre (const_tree t) +{ + _reason.type_is_in_union |= DECL_BIT_FIELD (t); +} + +void +TypeEscaper::_walk_UNION_TYPE_post (const_tree t) +{ + _inside_union--; + _update (t); +} + +/* Mark as escaping because RECORD has a function pointer + * propagate up and down. + */ +void +TypeEscaper::_walk_FUNCTION_TYPE_pre (__attribute__ ((unused)) const_tree t) +{ + _reason.type_is_in_union |= _inside_record > 0; +} + +/* Mark as escaping because RECORD has a function pointer + * propagate up and down. + */ +void +TypeEscaper::_walk_METHOD_TYPE_pre (__attribute__ ((unused)) const_tree t) +{ + _reason.type_is_in_union |= _inside_record > 0; +} + +/* Print escaping reasons. */ +void +TypeEscaper::print_reasons () +{ + TypeStringifier stringifier; + for (typemap::iterator i = calc.begin (), e = calc.end (); i != e; ++i) + { + const_tree t = i->first; + std::string name = stringifier.stringify (t); + Reason r = i->second; + log ("%s reason: ", name.c_str ()); + r.print (); + } +} + +tpartitions_t +ExprEscaper::get_sets () +{ + return typeEscaper.get_sets (); +} + +void +ExprEscaper::print_reasons () +{ + typeEscaper.print_reasons (); +} + +/* Propagate reason to subexpressions. */ +void +ExprEscaper::update (const_tree t, Reason r) +{ + gcc_assert (t); + _r = r; + walk (t); +} + +/* Propagate reason to type of subexpressions. */ +void +ExprEscaper::_walk_pre (const_tree e) +{ + _stack.push (e); + const_tree t = TREE_TYPE (e); + + gcc_assert (t); + typeEscaper.update (t, _r); +} + +void +ExprEscaper::_walk_post (__attribute__ ((unused)) const_tree e) +{ + _stack.pop (); +} + +/* Capture casting on LHS. */ +void +ExprEscaper::_walk_SSA_NAME_pre (const_tree e) +{ + const_tree ssa_type = TREE_TYPE (e); + + if (_stack.size () < 4) + return; + + // TODO: + // It appears that we have a bug, where we are + // storing expressions twice on the stack + const_tree this_expr = _stack.top (); + _stack.pop (); + const_tree twice = _stack.top (); + _stack.pop (); + const_tree prev_expr = _stack.top (); + _stack.push (twice); + _stack.push (this_expr); + if (TREE_CODE (prev_expr) != MEM_REF) + return; + + tree op1 = TREE_OPERAND (prev_expr, 1); + gcc_assert (TREE_CODE (op1) == INTEGER_CST); + const_tree mref_type = TREE_TYPE (op1); + + Reason old_reason = _r; + TypeIncompleteEquality structuralEquality; + // we need to make sure that both of them point to structs? + if (TREE_CODE (TREE_TYPE (mref_type)) == INTEGER_TYPE) + return; + + _r.type_is_casted = !structuralEquality.equal (mref_type, ssa_type); + typeEscaper.update (mref_type, _r); + typeEscaper.update (ssa_type, _r); + _r = old_reason; +} + +/* Mark constructors as escaping. */ +void +ExprEscaper::_walk_CONSTRUCTOR_pre (const_tree e) +{ + if (TREE_CLOBBER_P (e)) + return; + + // TODO: Instead of overloading global_is_visible field + // with has a constructor, make a field that denotes that + // a this has a constructor. + // Or better yet... modify the constructors! + _r.global_is_visible = true; + const_tree t = TREE_TYPE (e); + typeEscaper.update (t, _r); +} + +tpartitions_t +GimpleEscaper::get_sets () +{ + return exprEscaper.get_sets (); +} + +void +GimpleEscaper::print_reasons () +{ + exprEscaper.print_reasons (); +} + +/* Initialize undefined set of functions. */ +void +GimpleEscaper::_init () +{ + cgraph_node *cnode = NULL; + FOR_EACH_FUNCTION (cnode) + { + gcc_assert (cnode); + const bool filter = GimpleEscaper::filter_known_function (cnode); + if (filter) + continue; + + const_tree decl = cnode->decl; + gcc_assert (decl); + undefined.insert (decl); + } + + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cnode) + { + gcc_assert (cnode); + cnode->get_untransformed_body (); + const_tree decl = cnode->decl; + gcc_assert (decl); + undefined.erase (decl); + } +} + +/* Mark cnode graphs escaping if they are externally visible. */ +bool +GimpleEscaper::is_function_escaping (cgraph_node *cnode) +{ + const bool filter = GimpleEscaper::filter_known_function (cnode); + if (filter) + return false; + + return cnode->externally_visible; +} + +/* Mark fndecl as escaping is they are externally visible or + * there is no fndecl. */ +bool +GimpleEscaper::is_function_escaping (const_tree fndecl) +{ + if (!fndecl) + return true; + + if (!TREE_PUBLIC (fndecl) && !DECL_EXTERNAL (fndecl)) + return false; + + return true; +} + +/* Mark variable as escaping if it is externally visible. */ +bool +GimpleEscaper::is_variable_escaping (varpool_node *vnode) +{ + gcc_assert (vnode); + const bool retval = vnode->externally_visible; + const bool retval2 = vnode->externally_visible_p(); + log("%s externally_visible = %d %d\n", vnode->name(), retval, retval2); + return retval; +} + +/* Walk global variable VNODE. */ +void +GimpleEscaper::_walk_global (varpool_node *vnode) +{ + gcc_assert (vnode); + const_tree var_decl = vnode->decl; + Reason reason; + const bool is_escaping = is_variable_escaping (vnode); + reason.global_is_visible = is_escaping; + + // TODO: Instead of overloading the semantic meaning of global is visible + // make different fields for CONSTRUCTOR and for CONSTRUCTOR is not in linking + // unit + // TODO: Once we are able to rewrite the CONSTRUCTOR we can get rid of marking + // types with bracket constructors as illegal. + tree initial = DECL_INITIAL (var_decl); + const bool constructor = initial ? TREE_CODE (initial) == CONSTRUCTOR : false; + const bool error_mark = initial ? TREE_CODE (initial) == ERROR_MARK : false; + reason.global_is_visible + |= constructor || error_mark; // static initialization... + + exprEscaper.update (var_decl, reason); + GimpleWalker::_walk_global (vnode); +} + +/* Return true if FNDECL is a known function. */ +bool +GimpleEscaper::filter_known_function (const_tree fndecl) +{ + assert_is_type (fndecl, FUNCTION_DECL); + if (fndecl_built_in_p (fndecl)) + { + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_FREE: + case BUILT_IN_MALLOC: + case BUILT_IN_REALLOC: + case BUILT_IN_CALLOC: + case BUILT_IN_MEMSET: + return true; + break; + default: + break; + } + } + + return false; +} + +/* Return True if NODE points to a known FUNCTION_DECL. */ +bool +GimpleEscaper::filter_known_function (cgraph_node *node) +{ + if (!node) + return false; + return filter_known_function (node->decl); +} + +/* Mark Variable declaration of unknown location as escaping. */ +void +GimpleEscaper::_walk_pre_tree (const_tree t) +{ + // Is any global variable escaping? + Reason reason; + if (TREE_CODE (t) == VAR_DECL) + { + if (DECL_SOURCE_LOCATION (t) == UNKNOWN_LOCATION) + // Detecting some builtin types + // I think fprofile-generate inserts some builtin types which + // cannot be detected in any other way... + // TODO: Maybe add a new reason instead of overloading is_indirect. + reason.is_indirect = true; + } + exprEscaper.update (t, reason); +} + +void +GimpleEscaper::_walk_pre_gassign (gassign *s) +{ + Reason reason; + const enum gimple_rhs_class code = gimple_assign_rhs_class (s); + switch (code) + { + case GIMPLE_TERNARY_RHS: + { + const_tree rhs3 = gimple_assign_rhs3 (s); + exprEscaper.update (rhs3, reason); + } + /* fall-through */ + case GIMPLE_BINARY_RHS: + { + const_tree rhs2 = gimple_assign_rhs2 (s); + exprEscaper.update (rhs2, reason); + } + /* fall-through */ + case GIMPLE_UNARY_RHS: + case GIMPLE_SINGLE_RHS: + { + const_tree rhs1 = gimple_assign_rhs1 (s); + exprEscaper.update (rhs1, reason); + const_tree lhs = gimple_assign_lhs (s); + if (!lhs) + break; + exprEscaper.update (lhs, reason); + } + break; + default: + gcc_unreachable (); + break; + } +} + +void +GimpleEscaper::_walk_pre_greturn (greturn *s) +{ + Reason reason; + const_tree val = gimple_return_retval (s); + if (!val) + return; + exprEscaper.update (val, reason); +} + +void +GimpleEscaper::_walk_pre_gcond (gcond *s) +{ + Reason reason; + const_tree lhs = gimple_cond_lhs (s); + const_tree rhs = gimple_cond_rhs (s); + gcc_assert (lhs && rhs); + exprEscaper.update (lhs, reason); + exprEscaper.update (rhs, reason); +} + +void +GimpleEscaper::_walk_pre_gcall (gcall *s) +{ + const_tree fn = gimple_call_fndecl (s); + // gcc_assert (fn); + // The above will not always be true + // It will be false when we have an indirect function + cgraph_node *node = fn ? cgraph_node::get (fn) : NULL; + TypeStringifier stringifier; + const bool _is_function_escaping + = node ? is_function_escaping (node) : is_function_escaping (fn); + const bool is_undefined = undefined.find (fn) != undefined.end (); + log ("is undefined %s\n", is_undefined ? "t" : "f"); + const bool _is_escaping = is_undefined || _is_function_escaping; + + Reason arg_reason; + arg_reason.parameter_is_visible = _is_escaping; + arg_reason.is_indirect = !fn; + unsigned n = gimple_call_num_args (s); + for (unsigned i = 0; i < n; i++) + { + const_tree a = gimple_call_arg (s, i); + gcc_assert (a); + if (arg_reason.is_escaping ()) + { + std::string name = stringifier.stringify (TREE_TYPE (a)); + log ("escaping parameter %s\n", name.c_str ()); + } + exprEscaper.typeEscaper.update (TREE_TYPE (a), arg_reason); + } + + const_tree lhs = gimple_call_lhs (s); + if (!lhs) + return; + Reason return_reason; + return_reason.return_is_visible = _is_escaping; + return_reason.is_indirect = !fn; + exprEscaper.update (lhs, return_reason); +} + +/* Determine if cast comes from a known function. + * Do this by following the use-def chain. */ +bool +GimpleCaster::follow_def_to_find_if_really_cast (const_tree rhs) +{ + gimple *def_for_rhs = SSA_NAME_DEF_STMT (rhs); + gcall *is_call = dyn_cast<gcall *> (def_for_rhs); + if (!is_call) + return true; + + const_tree fn = gimple_call_fndecl (is_call); + if (!fn) + return true; + + bool known_function = GimpleEscaper::filter_known_function (fn); + return !known_function; +} + +void +GimpleCaster::_walk_pre_gassign (gassign *s) +{ + const enum gimple_rhs_class code = gimple_assign_rhs_class (s); + const bool valid_input = GIMPLE_SINGLE_RHS == code; + if (!valid_input) + return; + + // I originally was using gimple_assign_cast_p + // but that proved to be insufficient... + // So we have to use our equality comparison... + TypeIncompleteEquality equality; + const_tree lhs = gimple_assign_lhs (s); + const_tree rhs = gimple_assign_rhs1 (s); + gcc_assert (lhs && rhs); + Reason reason; + const_tree t_lhs = TREE_TYPE (lhs); + const_tree t_rhs = TREE_TYPE (rhs); + gcc_assert (t_lhs && t_rhs); + bool is_cast = !equality.equal (t_lhs, t_rhs); + // If it is cast, we might need to look at the definition of rhs + // If the definition comes from a known function... then we are good... + bool is_ssa = TREE_CODE (rhs) == SSA_NAME; + is_cast = is_ssa ? follow_def_to_find_if_really_cast (rhs) : is_cast; + + reason.type_is_casted = is_cast; + if (!flag_ipa_type_escape_analysis_keep_casts) + exprEscaper.typeEscaper.update (TREE_TYPE (lhs), reason); + if (!flag_ipa_type_escape_analysis_keep_casts) + exprEscaper.typeEscaper.update (TREE_TYPE (rhs), reason); + + GimpleEscaper::_walk_pre_gassign (s); +} + +/* Check to see if arguments are casted. */ +void +GimpleCaster::_walk_pre_gcall (gcall *s) +{ + GimpleEscaper::_walk_pre_gcall (s); + if (flag_ipa_type_escape_analysis_keep_casts) + return; + + const_tree fn = gimple_call_fndecl (s); + // If there's no function declaration, how do we + // know the argument types? + if (!fn) + return; + + cgraph_node *node = cgraph_node::get (fn); + const bool known_function = GimpleEscaper::filter_known_function (node) + || GimpleEscaper::filter_known_function (fn); + if (known_function) + return; + + const_tree f_t = TREE_TYPE (fn); + TypeIncompleteEquality equality; + + unsigned i = 0; + for (tree a = TYPE_ARG_TYPES (f_t); NULL_TREE != a; a = TREE_CHAIN (a)) + { + const_tree formal_t = TREE_VALUE (a); + // There seems to be a final VOID_TYPE at the end of some functions? + const enum tree_code code = TREE_CODE (formal_t); + const bool is_void = VOID_TYPE == code; + if (is_void) + continue; + + const_tree real = gimple_call_arg (s, i); + const_tree real_t = TREE_TYPE (real); + const bool is_casted = !equality.equal (formal_t, real_t); + Reason arg_reason; + arg_reason.type_is_casted = is_casted; + exprEscaper.typeEscaper.update (TREE_TYPE (real), arg_reason); + i++; + } + + const_tree lhs = gimple_call_lhs (s); + if (!lhs) + return; + + const_tree r_t = TREE_TYPE (f_t); + const_tree l_t TREE_TYPE (lhs); + const bool is_casted = !equality.equal (r_t, l_t); + Reason ret_reason; + ret_reason.type_is_casted = is_casted; + exprEscaper.update (lhs, ret_reason); +} + +bool +TypeAccessor::is_memoized (const_tree t) +{ + return memoized_map.find (t) != memoized_map.end (); +} + +/* Add all fields in struct to memoized map. */ +void +TypeAccessor::_walk_RECORD_TYPE_pre (const_tree t) +{ + add_all_fields_in_struct (t); + memoized_map.insert (t); +} + +/* Initialize all fields as neither read nor written. */ +void +TypeAccessor::add_all_fields_in_struct (const_tree t) +{ + const enum tree_code c = TREE_CODE (t); + const bool is_record = RECORD_TYPE == c; + if (!is_record) + return; + + const bool record_already_in_map = _map.find (t) != _map.end (); + field_access_map_t field_map; + field_map = record_already_in_map ? _map[t] : field_map; + + // Let's add all fields to the field map as empty. + for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field)) + { + const bool field_already_in_map_2 + = field_map.find (field) != field_map.end (); + if (field_already_in_map_2) + continue; + field_map[field] = Empty; + } + + _map[t] = field_map; +} + +record_field_map_t +ExprAccessor::get_map () +{ + return record_field_map; +} + +void +ExprAccessor::add_all_fields_in_struct (const_tree t) +{ + _typeAccessor.walk (t); +} + +void +ExprAccessor::_walk_pre (const_tree e) +{ + _stack.push (e); + const_tree t = TREE_TYPE (e); + add_all_fields_in_struct (t); +} + +void +ExprAccessor::_walk_post (__attribute__ ((unused)) const_tree e) +{ + _stack.pop (); +} + +void +ExprAccessor::update (const_tree e, unsigned access) +{ + _access = access; + walk (e); +} + +/* Check if we are accessing a field + * through pointer arithmetic. If this is happening + * it is likely the result of an optimization. + * Since we are not modifying these types of expressions yet + * we must mark all fields leading to the accessed fields + * as possibly READ. + + * TODO: If we modify this expression later on, we could + * make our transformation more powerful and precise by + * not marking all fields leading up to this one as possibly + * read. + */ +void +ExprAccessor::_walk_ADDR_EXPR_pre (__attribute__ ((unused)) const_tree e) +{ + log ("expr accessor mem ref\n"); + log ("stack size = %d\n", _stack.size ()); + + if (_stack.size () < 4) + return; + + // TODO: Fix error with double pushing + const_tree addr_expr = _stack.top (); + _stack.pop (); + const_tree twice = _stack.top (); + _stack.pop (); + const_tree prev_expr = _stack.top (); + _stack.push (addr_expr); + _stack.push (twice); + log ("prev_expr code = %s\n", get_tree_code_name (TREE_CODE (prev_expr))); + if (TREE_CODE (prev_expr) != MEM_REF) + return; + + tree op_0 = TREE_OPERAND (addr_expr, 0); + const_tree addr_expr_t = TREE_TYPE (op_0); + + TypeStringifier stringifier; + std::string name = stringifier.stringify (addr_expr_t); + log ("accessing a field through memref...? %s\n", name.c_str ()); + if (TREE_CODE (addr_expr_t) != RECORD_TYPE) + return; + + // We are accessing a field of a record through pointer arithmetic... + // So what field offset are we computing... + const_tree mref_expr = prev_expr; + tree offset = TREE_OPERAND (mref_expr, 1); + gcc_assert (TREE_CODE (offset) == INTEGER_CST); + tree type_size_tree = TYPE_SIZE_UNIT (addr_expr_t); + int type_size_int = tree_to_shwi (type_size_tree); + // TODO: Very recently inserted this assertion so we need to test this + gcc_assert (type_size_int > 0); + unsigned offset_int = tree_to_uhwi (offset) % type_size_int; + // We need to get the field that corresponds to the offset_int + const bool record_already_in_map + = record_field_map.find (addr_expr_t) != record_field_map.end (); + field_access_map_t field_map; + field_map = record_already_in_map ? record_field_map[addr_expr_t] : field_map; + + tree field = NULL; + for (field = TYPE_FIELDS (addr_expr_t); field; field = DECL_CHAIN (field)) + { + log ("ever inside?\n"); + unsigned f_byte_offset = tree_to_uhwi (DECL_FIELD_OFFSET (field)); + unsigned f_offset = f_byte_offset; + log ("offset field %d, offset by pointer %d\n", f_offset, offset_int); + + // NOTE: ALL FIELDS BEFORE THIS ONE NEED TO EXIST + // Otherwise, this pointer arithmetic is invalid... + // After the transformation + const bool field_already_in_map + = field_map.find (field) != field_map.end (); + unsigned prev_access = field_already_in_map ? field_map[field] : Empty; + + prev_access |= Read; + field_map[field] = prev_access; + add_all_fields_in_struct (addr_expr_t); + record_field_map[addr_expr_t] = field_map; + + if (f_offset == offset_int) + break; + } +} + +/* Find out if we are taking the address of a FIELD_DECL. + * If this is the case, it means that all FIELDS in this + * RECORD_TYPE should be marked as READ for safety. + */ +void +ExprAccessor::_walk_COMPONENT_REF_pre (const_tree e) +{ + log ("in component_ref pre\n"); + assert_is_type (e, COMPONENT_REF); + const_tree op0 = TREE_OPERAND (e, 0); + gcc_assert (op0); + const_tree op0_t = TREE_TYPE (op0); + gcc_assert (op0_t); + // op0_t can either be a RECORD_TYPE or a UNION_TYPE. + const enum tree_code code = TREE_CODE (op0_t); + const bool is_record = RECORD_TYPE == code; + const bool is_union = UNION_TYPE == code; + const bool valid = is_record != is_union; + gcc_assert (valid); + + const_tree op1 = TREE_OPERAND (e, 1); + assert_is_type (op1, FIELD_DECL); + log ("%s.%s\n", TypeStringifier::get_type_identifier (op0_t), + TypeStringifier::get_field_identifier (op1)); + const bool record_already_in_map + = record_field_map.find (op0_t) != record_field_map.end (); + field_access_map_t field_map; + field_map = record_already_in_map ? record_field_map[op0_t] : field_map; + const bool field_already_in_map = field_map.find (op1) != field_map.end (); + unsigned prev_access = field_already_in_map ? field_map[op1] : Empty; + + prev_access |= _access; + field_map[op1] = prev_access; + add_all_fields_in_struct (op0_t); + record_field_map[op0_t] = field_map; + + if (_stack.size () < 4) + return; + + const_tree this_expr = _stack.top (); + _stack.pop (); + const_tree twice = _stack.top (); + _stack.pop (); + const_tree prev_expr = _stack.top (); + _stack.push (twice); + _stack.push (this_expr); + if (TREE_CODE (prev_expr) != ADDR_EXPR) + return; + + log ("we are taking address of a component?\n"); + const_tree t = op0_t; + if (TREE_CODE (t) != RECORD_TYPE) + return; + + /* If we are taking the address of a component, we need to mark the whole + * RECORD_TYPE as escaping due to pointer arithmetic. + * TODO: Maybe add a flag to enable and disable this for debugging and + * testing. + */ + for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field)) { - const_tree o_record = i->first; - tree r_record = i->second; - tree m_record = TYPE_MAIN_VARIANT(r_record); - std::string name_from = stringifier.stringify(o_record); - std::string name_to = stringifier.stringify(r_record); - std::string name_to_mv = stringifier.stringify(m_record); - bool c_f = TYPE_CACHED_VALUES_P(o_record); - bool c_t = TYPE_CACHED_VALUES_P(r_record); - bool c_m = TYPE_CACHED_VALUES_P(m_record); - TYPE_CACHED_VALUES_P((tree)o_record) = false; - TYPE_CACHED_VALUES_P((tree)m_record) = false; - - log("map f: %s TYPE_CACHED_VALUES_P %s \n", name_from.c_str(), c_f ? "t" : "f"); - log("map t: %s TYPE_CACHED_VALUES_P %s \n", name_to.c_str(), c_t ? "t" : "f"); - log("map m: %s TYPE_CACHED_VALUES_P %s \n", name_to_mv.c_str(), c_m ? "t" : "f"); - bool in_map = map.find(m_record) != map.end(); - if (!in_map) continue; - tree mm_record = map[m_record]; - std::string name_to_mmv = stringifier.stringify(mm_record); - bool c_m2 = TYPE_CACHED_VALUES_P(mm_record); - log("map m2: %s TYPE_CACHED_VALUES_P %s\n", name_to_mmv.c_str(), c_m2 ? "t" : "f"); - // TODO: This is a hack... - TYPE_MAIN_VARIANT(r_record) = mm_record; - // Do we need to layout the type again? + log ("ever inside?\n"); + const bool field_already_in_map + = field_map.find (field) != field_map.end (); + unsigned prev_access = field_already_in_map ? field_map[field] : Empty; + + prev_access |= Read; + field_map[field] = prev_access; + add_all_fields_in_struct (t); + record_field_map[t] = field_map; } +} + +/* Print results. */ +void +ExprAccessor::print_accesses () +{ + for (std::map<const_tree, field_access_map_t>::const_iterator i + = record_field_map.begin (), + e = record_field_map.end (); + i != e; ++i) + { + const_tree record = i->first; + field_access_map_t field_map = i->second; + for (std::map<const_tree, unsigned>::const_iterator j + = field_map.begin (), + f = field_map.end (); + j != f; ++j) + { + const_tree field = j->first; + const std::string name_r + = TypeStringifier::get_type_identifier (record); + const std::string name_f + = TypeStringifier::get_field_identifier (field); + unsigned access = j->second; + log ("%s.%s = 0x%04x\n", name_r.c_str (), name_f.c_str (), access); + } + } +} + +/* RECORD_TYPE -> (FIELD_DECL -> bitflag) + * bitflag specifies if field is read, written or neither. + */ +record_field_map_t +GimpleAccesser::get_map () +{ + return exprAccessor.get_map (); +} + +void +GimpleAccesser::print_accesses () +{ + exprAccessor.print_accesses (); +} + +/* Mark RHS as Read and LHS as Write. */ +void +GimpleAccesser::_walk_pre_gassign (gassign *s) +{ + // There seems to be quite a bit of code duplication here... + const enum gimple_rhs_class code = gimple_assign_rhs_class (s); + switch (code) + { + case GIMPLE_TERNARY_RHS: + { + const_tree rhs3 = gimple_assign_rhs3 (s); + gcc_assert (rhs3); + exprAccessor.update (rhs3, Read); + } + /* fall-through */ + case GIMPLE_BINARY_RHS: + { + const_tree rhs2 = gimple_assign_rhs2 (s); + gcc_assert (rhs2); + exprAccessor.update (rhs2, Read); + } + /* fall-through */ + case GIMPLE_UNARY_RHS: + case GIMPLE_SINGLE_RHS: + { + const_tree rhs1 = gimple_assign_rhs1 (s); + exprAccessor.update (rhs1, Read); + const_tree lhs = gimple_assign_lhs (s); + if (!lhs) + break; + exprAccessor.update (lhs, Write); + break; + } + default: + gcc_unreachable (); + break; + } +} + +/* Mark RHS as Read and LHS as Written. */ +void +GimpleAccesser::_walk_pre_gcall (gcall *s) +{ + unsigned n = gimple_call_num_args (s); + for (unsigned i = 0; i < n; i++) + { + const_tree a = gimple_call_arg (s, i); + gcc_assert (a); + exprAccessor.update (a, Read); + } + + const_tree lhs = gimple_call_lhs (s); + if (!lhs) + return; + exprAccessor.update (lhs, Write); +} + +/* Mark as Read. */ +void +GimpleAccesser::_walk_pre_greturn (greturn *s) +{ + const_tree val = gimple_return_retval (s); + if (!val) + return; + exprAccessor.update (val, Read); +} + +/* Mark as Read. */ +void +GimpleAccesser::_walk_pre_gcond (gcond *s) +{ + const_tree lhs = gimple_cond_lhs (s); + const_tree rhs = gimple_cond_rhs (s); + gcc_assert (lhs && rhs); + exprAccessor.update (lhs, Read); + exprAccessor.update (rhs, Read); +} + +/* Print Reasons why a type might be escaping. */ +void +Reason::print () const +{ + log ("e=%d g=%d p=%d r=%d c=%d v=%d u=%d i=%d\n", this->is_escaping (), + this->global_is_visible, this->parameter_is_visible, + this->return_is_visible, this->type_is_casted, this->type_is_volatile, + this->type_is_in_union, this->is_indirect); +} + +/* Or an escaping Reason. */ +Reason +Reason::operator| (const Reason &other) +{ + Reason retval; + retval.global_is_visible = this->global_is_visible | other.global_is_visible; + retval.parameter_is_visible + = this->parameter_is_visible | other.parameter_is_visible; + retval.return_is_visible = this->return_is_visible | other.return_is_visible; + retval.type_is_casted = this->type_is_casted | other.type_is_casted; + retval.type_is_volatile = this->type_is_volatile | other.type_is_volatile; + retval.type_is_in_union = this->type_is_in_union | other.type_is_in_union; + retval.is_indirect = this->is_indirect | other.is_indirect; + return retval; +} + +/* Or an escaping Reason. */ +Reason & +Reason::operator|= (const Reason &other) +{ + this->global_is_visible |= other.global_is_visible; + this->parameter_is_visible |= other.parameter_is_visible; + this->return_is_visible |= other.return_is_visible; + this->type_is_casted |= other.type_is_casted; + this->type_is_volatile |= other.type_is_volatile; + this->type_is_in_union |= other.type_is_in_union; + this->is_indirect |= other.is_indirect; + return *this; +} + +/* Insert TYPE into a partition depending on IN_POINTS_TO_RECORD. */ +void +type_partitions_s::insert (const_tree type, bool in_points_to_record) +{ + gcc_assert (type); + this->universe.insert (type); + in_points_to_record ? this->points_to_record.insert (type) + : this->complement.insert (type); + const bool in_points_to_set = this->in_points_to_record (type); + const bool in_complement = this->in_complement (type); + const bool _xor = in_points_to_set != in_complement; + // sanity check... + gcc_assert (_xor); +} + +/* Find out whether TYPE is already in universe. */ +bool +type_partitions_s::in_universe (const_tree type) const +{ + gcc_assert (type); + const bool seen_before = this->universe.find (type) != this->universe.end (); + return seen_before; +} + +/* Find out whether TYPE is in points_to_record partition. */ +bool +type_partitions_s::in_points_to_record (const_tree type) const +{ + gcc_assert (type); + const bool seen_before + = this->points_to_record.find (type) != this->points_to_record.end (); + return seen_before; +} + +/* Find out whether TYPE is not in points to record partition. */ +bool +type_partitions_s::in_complement (const_tree type) const +{ + gcc_assert (type); + const bool seen_before + = this->complement.find (type) != this->complement.end (); + return seen_before; +} + +/* Stringify a type. */ +std::string +TypeStringifier::stringify (const_tree t) +{ + if (!dump_file) + return std::string (""); + _stringification.clear (); + gcc_assert (t); + walk (t); + return _stringification; +} + +void +TypeStringifier::_walk_VOID_TYPE_pre (const_tree t) +{ + _stringify_simple (t); +} + +void +TypeStringifier::_walk_INTEGER_TYPE_pre (const_tree t) +{ + _stringify_simple (t); +} + +void +TypeStringifier::_walk_REAL_TYPE_pre (const_tree t) +{ + _stringify_simple (t); +} + +void +TypeStringifier::_walk_FIXED_POINT_TYPE_pre (const_tree t) +{ + _stringify_simple (t); +} +void +TypeStringifier::_walk_COMPLEX_TYPE_pre (const_tree t) +{ + _stringify_simple (t); +} - GimpleTypeRewriter rewriter(map, field_map); - rewriter.walk(); - rewriter._rewrite_function_decl(); +void +TypeStringifier::_walk_OFFSET_TYPE_pre (const_tree t) +{ + _stringify_simple (t); +} - GimpleWalker walker; - walker.walk(); // Just for printing... +void +TypeStringifier::_walk_BOOLEAN_TYPE_pre (const_tree t) +{ + _stringify_simple (t); +} - log("FINISHED\n"); +void +TypeStringifier::_stringify_simple (const_tree t) +{ + gcc_assert (t); + const enum tree_code code = TREE_CODE (t); + this->_stringification += std::string (get_tree_code_name (code)); +} + +void +TypeStringifier::_walk_POINTER_TYPE_post (__attribute__ ((unused)) const_tree t) +{ + this->_stringification += std::string ("*"); +} + +void +TypeStringifier::_walk_ARRAY_TYPE_post (__attribute__ ((unused)) const_tree t) +{ + this->_stringification += std::string ("[]"); +} + +void +TypeStringifier::_walk_REFERENCE_TYPE_post (__attribute__ ((unused)) + const_tree t) +{ + this->_stringification += std::string ("&"); +} + +void +TypeStringifier::_walk_UNION_TYPE_pre (const_tree t) +{ + this->_stringification += std::string (" union "); + _stringify_aggregate_pre (t); +} + +void +TypeStringifier::_walk_UNION_TYPE_post (const_tree t) +{ + _stringify_aggregate_post (t); +} + +void +TypeStringifier::_walk_RECORD_TYPE_pre (const_tree t) +{ + this->_stringification += std::string (" record "); + _stringify_aggregate_pre (t); +} + +void +TypeStringifier::_walk_RECORD_TYPE_post (const_tree t) +{ + _stringify_aggregate_post (t); +} + +void +TypeStringifier::_stringify_aggregate_pre (const_tree t) +{ + this->_stringification + += TypeStringifier::get_type_identifier (t) + std::string (" {"); +} + +void +TypeStringifier::_stringify_aggregate_post (__attribute__ ((unused)) + const_tree t) +{ + this->_stringification += std::string ("}"); +} + +void +TypeStringifier::_walk_field_post (const_tree t) +{ + this->_stringification += std::string (" ") + + TypeStringifier::get_field_identifier (t) + + std::string (";"); +} + +void +TypeStringifier::_walk_METHOD_TYPE_pre (const_tree t) +{ + _stringify_fm_pre (t); +} + +void +TypeStringifier::_walk_METHOD_TYPE_post (const_tree t) +{ + _stringify_fm_post (t); +} + +void +TypeStringifier::_walk_FUNCTION_TYPE_pre (const_tree t) +{ + _stringify_fm_pre (t); +} + +void +TypeStringifier::_walk_FUNCTION_TYPE_post (const_tree t) +{ + _stringify_fm_post (t); +} + +void +TypeStringifier::_stringify_fm_pre (__attribute__ ((unused)) const_tree t) +{ + this->_stringification += std::string ("function { "); +} + +void +TypeStringifier::_stringify_fm_post (__attribute__ ((unused)) const_tree t) +{ + this->_stringification += std::string ("}"); +} + +void +TypeStringifier::_walk_return_pre (__attribute__ ((unused)) const_tree t) +{ + this->_stringification += std::string ("("); +} + +void +TypeStringifier::_walk_return_post (__attribute__ ((unused)) const_tree t) +{ + this->_stringification += std::string (")"); +} + +void +TypeStringifier::_walk_args_pre (__attribute__ ((unused)) const_tree t) +{ + this->_stringification += std::string ("("); +} + +void +TypeStringifier::_walk_args_post (__attribute__ ((unused)) const_tree t) +{ + this->_stringification += std::string (")"); +} + +void +TypeStringifier::_walk_arg_post (__attribute__ ((unused)) const_tree t) +{ + this->_stringification += std::string (", "); +} + +std::string +TypeStringifier::get_type_identifier (const_tree t) +{ + tree name = TYPE_NAME (t); + const bool no_name = NULL_TREE == name; + if (no_name) + return std::string (""); + + const enum tree_code name_code = TREE_CODE (name); + const bool is_name_type_decl = TYPE_DECL == name_code; + name = is_name_type_decl ? DECL_NAME (name) : name; + const char *identifier_ptr = IDENTIFIER_POINTER (name); + gcc_assert (identifier_ptr); + return std::string (identifier_ptr); +} + +std::string +TypeStringifier::get_field_identifier (const_tree t) +{ + assert_is_type (t, FIELD_DECL); + const_tree decl_name = DECL_NAME (t); + if (!decl_name) + return std::string (""); + + const char *identifier = IDENTIFIER_POINTER (decl_name); + return std::string (identifier); +} + +/* Return true if L and R have equal structural equalities. */ +bool +TypeStructuralEquality::equal (const_tree l, const_tree r) +{ + return _equal (l, r); +} + +bool +TypeStructuralEquality::_equal (const_tree l, const_tree r) +{ + bool valid_inputs = l && r; + if (!valid_inputs) + return l == r; + + bool equal_codes = _equal_code (l, r); + if (!equal_codes) + return equal_codes; + + bool recurse_l = set_l.find (l) != set_l.end (); + bool recurse_r = set_r.find (r) != set_r.end (); + // Verify that this the case every time. + bool recurse = recurse_l || recurse_r; + if (recurse) + return recurse; + + set_l.insert (l); + set_r.insert (r); + const enum tree_code code = TREE_CODE (l); + bool equal_children = false; + switch (code) + { +#define TSE_CASE(code) \ + case code: \ + equal_children = _walk_##code (l, r); \ + break + + TSE_CASE (VOID_TYPE); + TSE_CASE (INTEGER_TYPE); + TSE_CASE (REAL_TYPE); + TSE_CASE (FIXED_POINT_TYPE); + TSE_CASE (COMPLEX_TYPE); + TSE_CASE (ENUMERAL_TYPE); + TSE_CASE (BOOLEAN_TYPE); + TSE_CASE (OFFSET_TYPE); + TSE_CASE (RECORD_TYPE); + TSE_CASE (POINTER_TYPE); + TSE_CASE (REFERENCE_TYPE); + TSE_CASE (ARRAY_TYPE); + TSE_CASE (UNION_TYPE); + TSE_CASE (FUNCTION_TYPE); + TSE_CASE (METHOD_TYPE); + default: + gcc_unreachable (); + break; + } + + set_l.erase (l); + set_r.erase (r); + return equal_children; +} + +bool +TypeStructuralEquality::_equal_code (const_tree l, const_tree r) +{ + const enum tree_code code_l = TREE_CODE (l); + const enum tree_code code_r = TREE_CODE (r); + const bool equal = code_l == code_r; + return equal; +} + +#define TSE_FUNC_DEF_SIMPLE(code) \ + bool TypeStructuralEquality::_walk_##code (const_tree l, const_tree r) \ + { \ + return _equal_code (l, r); \ + } + +TSE_FUNC_DEF_SIMPLE (VOID_TYPE) +TSE_FUNC_DEF_SIMPLE (INTEGER_TYPE) +TSE_FUNC_DEF_SIMPLE (REAL_TYPE) +TSE_FUNC_DEF_SIMPLE (FIXED_POINT_TYPE) +TSE_FUNC_DEF_SIMPLE (ENUMERAL_TYPE) +TSE_FUNC_DEF_SIMPLE (BOOLEAN_TYPE) +TSE_FUNC_DEF_SIMPLE (OFFSET_TYPE) +TSE_FUNC_DEF_SIMPLE (COMPLEX_TYPE) + +bool +TypeStructuralEquality::_equal_wrapper (const_tree l, const_tree r) +{ + const_tree inner_l = TREE_TYPE (l); + const_tree inner_r = TREE_TYPE (r); + return _equal (inner_l, inner_r); +} + +#define TSE_FUNC_DEF_WRAPPER(code) \ + bool TypeStructuralEquality::_walk_##code (const_tree l, const_tree r) \ + { \ + return _equal_wrapper (l, r); \ + } + +TSE_FUNC_DEF_WRAPPER (REFERENCE_TYPE) +TSE_FUNC_DEF_WRAPPER (ARRAY_TYPE) +TSE_FUNC_DEF_WRAPPER (POINTER_TYPE) + +#define TSE_FUNC_DEF_CONTAINER(code) \ + bool TypeStructuralEquality::_walk_##code (const_tree l, const_tree r) \ + { \ + const_tree field_l = TYPE_FIELDS (l); \ + const_tree field_r = TYPE_FIELDS (r); \ + bool efield_l = field_l; \ + bool efield_r = field_r; \ + bool still_equal = efield_l == efield_r; \ + if (!still_equal) \ + return still_equal; \ + \ + while (field_l && field_r && still_equal) \ + { \ + const_tree tfield_l = TREE_TYPE (field_l); \ + const_tree tfield_r = TREE_TYPE (field_r); \ + still_equal &= _equal (tfield_l, tfield_r); \ + field_l = DECL_CHAIN (field_l); \ + field_r = DECL_CHAIN (field_r); \ + efield_l = field_l; \ + efield_r = field_r; \ + still_equal &= efield_l == efield_r; \ + } \ + return still_equal; \ + } + +TSE_FUNC_DEF_CONTAINER (RECORD_TYPE) +TSE_FUNC_DEF_CONTAINER (UNION_TYPE) + +#define TSE_FUNC_DEF_FUNC(code) \ + bool TypeStructuralEquality::_walk_##code (const_tree l, const_tree r) \ + { \ + const_tree tret_l = TREE_TYPE (l); \ + const_tree tret_r = TREE_TYPE (r); \ + bool still_equal = _equal (tret_l, tret_r); \ + if (!still_equal) \ + return still_equal; \ + \ + const_tree arg_l = TYPE_ARG_TYPES (l); \ + const_tree arg_r = TYPE_ARG_TYPES (r); \ + bool earg_l = arg_l; \ + bool earg_r = arg_r; \ + still_equal &= earg_l == earg_r; \ + while (arg_l && arg_r && still_equal) \ + { \ + const_tree targ_l = TREE_VALUE (arg_l); \ + const_tree targ_r = TREE_VALUE (arg_r); \ + still_equal &= _equal (targ_l, targ_r); \ + arg_l = TREE_CHAIN (arg_l); \ + arg_r = TREE_CHAIN (arg_r); \ + earg_l = arg_l; \ + earg_r = arg_r; \ + still_equal &= earg_l == earg_r; \ + } \ + return still_equal; \ + } + +TSE_FUNC_DEF_FUNC (FUNCTION_TYPE) +TSE_FUNC_DEF_FUNC (METHOD_TYPE) + +/* Used for comparing incomplete types. */ +bool +TypeIncompleteEquality::_equal (const_tree l, const_tree r) +{ + bool valid_inputs = l && r; + if (!valid_inputs) + return l == r; + + // If any of these are incomplete, then we can only compare using + // identifiers... + const bool complete_l = is_complete (l); + const bool complete_r = is_complete (r); + bool can_compare_structurally = complete_l && complete_r; + if (can_compare_structurally) + return TypeStructuralEquality::_equal (l, r); + + // Before comparing with identifiers + // make last attempt to compare using main variants. + const_tree m_l = TYPE_MAIN_VARIANT (l); + const_tree m_r = TYPE_MAIN_VARIANT (r); + gcc_assert (m_l && m_r); + can_compare_structurally = m_l == m_r; + if (can_compare_structurally) + return true; + + const std::string n_l = TypeStringifier::get_type_identifier (m_l); + const std::string n_r = TypeStringifier::get_type_identifier (m_r); + return n_l.compare (n_r) == 0; +} + +/* Remove non-escaping types and place in escaping types if + * there is a tree in escaping partition which is equivalent to + * another tree in non-escaping partition. + * Perform this until a fixed point is reached. + */ +static void +fix_escaping_types_in_set (tpartitions_t &types) +{ + bool fixed_point_reached = false; + TypeIncompleteEquality structuralEquality; + do + { + std::vector<const_tree> fixes; + fixed_point_reached = true; + for (std::set<const_tree>::const_iterator i = types.escaping.begin (), + e = types.escaping.end (); + i != e; ++i) + { + for (std::set<const_tree>::const_iterator j + = types.non_escaping.begin (), + f = types.non_escaping.end (); + j != f; ++j) + { + const_tree type_esc = *i; + gcc_assert (type_esc); + const_tree type_non = *j; + gcc_assert (type_non); + // There can be cases where incomplete types are marked as + // non-escaping and complete types counter parts are marked as + // escaping. + const bool equal = structuralEquality.equal (type_esc, type_non); + if (!equal) + continue; + + fixed_point_reached = false; + // Add incomplete to escaping + // delete incomplete from non_escaping + // We shouldn't do that inside our iteration loop. + fixes.push_back (type_non); + } + } + + for (std::vector<const_tree>::const_iterator i = fixes.begin (), + e = fixes.end (); + i != e; ++i) + { + const_tree escaping_type = *i; + types.escaping.insert (escaping_type); + types.non_escaping.erase (escaping_type); + } + } + while (!fixed_point_reached); +} + +simple_ipa_opt_pass * +make_pass_ipa_type_escape_analysis (gcc::context *ctx) +{ + return new pass_ipa_type_escape_analysis (ctx); } diff --git a/gcc/ipa-type-escape-analysis.h b/gcc/ipa-type-escape-analysis.h new file mode 100644 index 00000000000..6f40bd5d5e1 --- /dev/null +++ b/gcc/ipa-type-escape-analysis.h @@ -0,0 +1,1148 @@ +/* IPA Type Escape Analysis and Dead Field Elimination + Copyright (C) 2019-2020 Free Software Foundation, Inc. + + Contributed by Erick Ochoa <erick.ochoa@theobroma-systems.com> + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef GCC_IPA_TYPE_ESCAPE_ANALYSIS_H +#define GCC_IPA_TYPE_ESCAPE_ANALYSIS_H + +#include <string> +#include <map> +#include <set> + +/* Logging function, useful for debugging. */ +inline void +log (const char *const fmt, ...) +{ + if (!dump_file) + return; + + va_list args; + va_start (args, fmt); + vfprintf (dump_file, fmt, args); + fflush (dump_file); + va_end (args); +} + +/* Asserts expected gimple code is observed gimple code. */ +inline void +is_gimple_code (const gimple *stmt, const enum gimple_code ex_code) +{ + gcc_assert (stmt); + const enum gimple_code ob_code = gimple_code (stmt); + const bool succeeds = ex_code == ob_code; + gcc_assert (succeeds); +} + +/* Determine if type A is complete. */ +inline bool +is_complete (const_tree a) +{ + gcc_assert (a); + const_tree type_size = TYPE_SIZE (a); + const bool _is_complete = NULL_TREE != type_size; + return _is_complete; +} + +/* Asserts type A is complete. */ +inline void +assert_is_complete (const_tree a) +{ + const bool _is_complete = is_complete (a); + gcc_assert (_is_complete); +} + +/* Determine if type A is incomplete. */ +inline bool +is_incomplete (const_tree a) +{ + return !is_complete (a); +} + +/* Assert type A has is EXPECTED_CODE. */ +inline void +assert_is_type (const_tree a, const enum tree_code expected_code) +{ + gcc_assert (a); + const enum tree_code observed_code = TREE_CODE (a); + const bool eq_codes = observed_code == expected_code; + gcc_assert (eq_codes); +} + +/* Assert STMT is of EX_CLASS enum gimple_rhs_class. */ +inline void +assert_is_gimple_rhs_class (const gimple *stmt, + const enum gimple_rhs_class ex_class) +{ + gcc_assert (stmt); + is_gimple_code (stmt, GIMPLE_ASSIGN); + const enum gimple_rhs_class ob_class = gimple_assign_rhs_class (stmt); + const bool succeeds = ex_class == ob_class; + gcc_assert (succeeds); +} + +// TODO: Rename? +// TSET_T stands for type set. +typedef std::set<const_tree> tset_t; + +/* Base class used for visiting tree nodes starting with root T. + * It can handle recursive cases in the tree graph by holding + * a set of previously seen nodes and checking before walking + * a node. + + * Unlike walk_tree, TypeWalker allows users to create post-order + * callbacks for each type of tree. + */ +class TypeWalker +{ +public: + TypeWalker () + {}; + + /* Main interface to type walker. + * Walk type T. + */ + void walk (const_tree t); + +protected: + + /* Typeset holds previously visited nodes. */ + tset_t tset; + + /* Inner walking method, used to recurse. */ + void _walk (const_tree t); + + /* Common walking method for REFERENCE_TYPE, ARRAY_TYPE, and POINTER_TYPE. */ + void _walk_wrapper (const_tree t); + + /* Common walking method for RECORD_TYPE and UNION_TYPE. */ + void _walk_record_or_union (const_tree t); + + /* Common walking method for FUNCTION_TYPE and METHOD_TYPE. */ + virtual void _walk_function_or_method (const_tree t); + + /* If the type is memoized and we don't need to walk further down. */ + virtual bool is_memoized (__attribute__ ((unused)) const_tree t) + { + return false; + } + + /* Function definitions for different TYPEs callbacks. + _pre is the pre-order callback. + _post is the post-order callback. + If you want to find a specific type in a specific order, + (e.g. RECORD_TYPE and preorder) + you can create a derived class and implement the function + void _walk_RECORD_TYPE_pre (const_tree). + + walk_##code is the function that calls + the preorder callback + walking subtrees + and the postorder callback. + This is for each specific tree code. + + _walk_##code is the function that walks subtrees for that + specific tree code. + */ +#define TypeWalkerFuncDecl(code) \ + virtual void _walk_##code##_pre (__attribute__((unused)) const_tree t) \ + {}; \ + virtual void _walk_##code (const_tree t); \ + virtual void _walk_##code##_post (__attribute__((unused)) const_tree t) \ + {}; \ + virtual void walk_##code (const_tree t) + +// NOTE the lack of semicolon here. +// This is so that when using the macro we can use a semi-colon +// and formatting doesn't break. + + /* These are the types for which we can define a pre-order + * and post-order functions. */ + TypeWalkerFuncDecl (VOID_TYPE); + TypeWalkerFuncDecl (INTEGER_TYPE); + TypeWalkerFuncDecl (REAL_TYPE); + TypeWalkerFuncDecl (FIXED_POINT_TYPE); + TypeWalkerFuncDecl (COMPLEX_TYPE); + TypeWalkerFuncDecl (ENUMERAL_TYPE); + TypeWalkerFuncDecl (BOOLEAN_TYPE); + TypeWalkerFuncDecl (OFFSET_TYPE); + TypeWalkerFuncDecl (RECORD_TYPE); + TypeWalkerFuncDecl (POINTER_TYPE); + TypeWalkerFuncDecl (REFERENCE_TYPE); + TypeWalkerFuncDecl (ARRAY_TYPE); + TypeWalkerFuncDecl (UNION_TYPE); + TypeWalkerFuncDecl (FUNCTION_TYPE); + TypeWalkerFuncDecl (METHOD_TYPE); + + /* These are not types, but are still trees that + * can be reached from a tree type. Therefore, these + * trees also need to be walked. + */ + TypeWalkerFuncDecl (field); + TypeWalkerFuncDecl (return); + TypeWalkerFuncDecl (args); + TypeWalkerFuncDecl (arg); +}; + +/* Base class for the implementation of the visitor pattern for + * trees which are "expressions". This might be a misnomer. + * What it means is that it walks whatever can be the result of + * the trees returned by gimple_assign_rhs{1,2,3}, gimple_return, + * gimple_call... + * TODO: The expressions visited here might not be the whole set + * but it is what was found while experimentally running some C + * programs. + */ +class ExprWalker +{ +public: + ExprWalker () + {}; + + /* Walk tree E. */ + void walk (const_tree e); + +private: + + /* Virtual function to be implemented. Callback for all E in preorder. */ + virtual void _walk_pre (__attribute__ ((unused)) const_tree e) + {}; + + /* Inner method that will recurse for walking subtrees in E. */ + void _walk (const_tree e); + + /* Virtual function to be implemented. Callback for all E in postorder. */ + virtual void _walk_post (__attribute__ ((unused)) const_tree e) + {}; + + /* Walking subtrees generically. Either it is a leaf node, + (i.e., it does not have subtrees), or it has some operands. + TODO: This can probably be changed to be more general + by finding out how many operands an expression has. + + tree_code C is used to assert that we are visiting an operand + of a specific tree code. + */ + inline void _walk_leaf (const_tree e, const enum tree_code c); + inline void _walk_op_n (const_tree e, unsigned n); + inline void _walk_op_0 (const_tree e, const enum tree_code c); + inline void _walk_op_1 (const_tree e, const enum tree_code c); + + /* Virtual function declarations for the pre-order and post-order callbacks. + * _walk_##code##_pre is the preorder callback + * walk_##code will call the preorder, subtree walker, and postorder + * _walk_##code is the subtree walker + * _walk_##code##_post is the post-order callback. + */ +#define ExprWalkerFuncDecl(code) \ + virtual void _walk_##code##_pre (__attribute__ ((unused)) const_tree e) \ + {}; \ + void walk_##code (const_tree e); \ + void _walk_##code (const_tree e); \ + virtual void _walk_##code##_post (__attribute__ ((unused)) const_tree e) \ + {} + + // Some of these are not "EXPR" codes, but they are reachable + // from gimple_assign_rhs{1,2,3} and others. + ExprWalkerFuncDecl (CONSTRUCTOR); + ExprWalkerFuncDecl (INTEGER_CST); + ExprWalkerFuncDecl (REAL_CST); + ExprWalkerFuncDecl (STRING_CST); + ExprWalkerFuncDecl (BIT_FIELD_REF); + ExprWalkerFuncDecl (ARRAY_REF); + ExprWalkerFuncDecl (MEM_REF); + ExprWalkerFuncDecl (COMPONENT_REF); + ExprWalkerFuncDecl (SSA_NAME); + ExprWalkerFuncDecl (ADDR_EXPR); + ExprWalkerFuncDecl (VIEW_CONVERT_EXPR); + ExprWalkerFuncDecl (IMAGPART_EXPR); + ExprWalkerFuncDecl (FIELD_DECL); + ExprWalkerFuncDecl (VAR_DECL); + ExprWalkerFuncDecl (RESULT_DECL); + ExprWalkerFuncDecl (PARM_DECL); + ExprWalkerFuncDecl (FUNCTION_DECL); + ExprWalkerFuncDecl (LE_EXPR); + ExprWalkerFuncDecl (LT_EXPR); + ExprWalkerFuncDecl (EQ_EXPR); + ExprWalkerFuncDecl (GT_EXPR); + ExprWalkerFuncDecl (GE_EXPR); + ExprWalkerFuncDecl (NE_EXPR); +}; + +/* Base class for applying the visitor pattern to gimple_stmts. + * This class visits everything it cans during LTO. + * That includes global variables, and all function declarations that + * are in the current partition. + */ +class GimpleWalker +{ +public: + GimpleWalker () : _deleted (false) + {}; + + /* Walk the entire code, therefore no input is needed. */ + void walk (); + +protected: + /* _DELETED is set by overwritten functions that + * delete a specific gimple stmt. This tells the + * gimple walker to remove the gimple stmt. + */ + bool _deleted; + + /* Walk global variables. */ + void _walk_globals (); + + /* Walk individual global variable V. */ + virtual void _walk_global (varpool_node *v); + + /* Will walk declarations, locals, ssa names, and basic blocks. */ + void _walk_cnode (cgraph_node *cnode); + + /* This will walk the CNODE->decl. */ + void _walk_decl (cgraph_node *cnode); + + /* Walk ssa_names in CNODE. */ + void _walk_ssa_names (cgraph_node *cnode); + + /* Walk local variables in CNODE. */ + void _walk_locals (cgraph_node *cnode); + + /* Iterate over all basic blocks in CNODE. */ + void _walk_bb (cgraph_node *cnode); + + /* Iterate over all gimple_stmt in BB. */ + void _walk (basic_block bb); + + /* Function declarations for virtual functions. + * These include the pre-order callbacks, walk subtrees, + * and post-order callbacks. + */ + virtual void _walk_pre_tree(__attribute__((unused)) const_tree t) + {}; + void walk_tree2(const_tree t); + void _walk_tree(const_tree t); + virtual void _walk_post_tree(__attribute__((unused)) const_tree t) + {}; + + virtual void _walk_pre_gimple(__attribute__((unused)) gimple* g) + {}; + void walk_gimple(gimple* g); + void _walk_gimple(gimple* g); + virtual void _walk_post_gimple(__attribute__((unused)) gimple* g) + {}; + + virtual void _walk_pre_gassign(__attribute__((unused)) gassign* g) + {}; + void walk_gassign(gassign* g); + void _walk_gassign(gassign* g); + virtual void _walk_post_gassign(__attribute__((unused)) gassign* g) + {}; + + virtual void _walk_pre_greturn(__attribute__((unused)) greturn* g) + {}; + void walk_greturn(greturn* g); + void _walk_greturn(greturn* g); + virtual void _walk_post_greturn(__attribute__((unused)) greturn* g) + {}; + + virtual void _walk_pre_gcond(__attribute__((unused)) gcond* g) + {}; + void walk_gcond(gcond* g); + void _walk_gcond(gcond* g); + virtual void _walk_post_gcond(__attribute__((unused)) gcond* g) + {}; + + virtual void _walk_pre_glabel(__attribute__((unused)) glabel* g) + {}; + void walk_glabel(glabel* g); + void _walk_glabel(glabel* g); + virtual void _walk_post_glabel(__attribute__((unused)) glabel* g) + {}; + + virtual void _walk_pre_gcall (__attribute__((unused)) gcall *g) + {}; + void walk_gcall (gcall *g); + void _walk_gcall (gcall *g); + virtual void _walk_post_gcall (__attribute__((unused)) gcall *g) + {}; + + virtual void _walk_pre_gswitch(__attribute__((unused)) gswitch* g) + {}; + void walk_gswitch(gswitch* g); + void _walk_gswitch(gswitch* g); + virtual void _walk_post_gswitch(__attribute__((unused)) gswitch* g) + {}; + + virtual void _walk_pre_gphi(__attribute__((unused)) gphi* g) + {}; + void walk_gphi(gphi* g); + void _walk_gphi(gphi* g); + virtual void _walk_post_gphi(__attribute__((unused)) gphi* g) + {}; + +}; + +/* TYPE_PARTITIONS_S is a structure that is shared + * across most of the type escape analysis. It holds + * 4 different partitions. + + * 1. points to record + * 2. complement of points to record + * 3. escaping trees + * 4. non escaping trees + + * It also has a set of all types seen so far called universe. + + * Ideally 1 union 2 should be universe and 3 union 4 should be universe. + */ +struct type_partitions_s +{ + /* The set of all types which have been seen. */ + tset_t universe; + + /* The set of all types which somehow refer to a RECORD_TYPE. */ + tset_t points_to_record; + + /* The complement of points_to_record. */ + tset_t complement; + + /* The set of all escaping types. */ + tset_t escaping; + + /* The set of all non escaping types. */ + tset_t non_escaping; + + /* Determine if we have seen this type before. */ + bool in_universe (const_tree) const; + + /* Determine if tree points to a record. */ + bool in_points_to_record (const_tree) const; + + /* Determine if tree does not point to a record. */ + bool in_complement (const_tree) const; + + /* Insert either in points to record or complement. */ + void insert (const_tree, bool); +}; + +typedef struct type_partitions_s tpartitions_t; + +/* TypeCollector is a derived class from TypeWalker + * that collects all types reachable from T into the partitions + * points_to_record or not points to record. + */ +class TypeCollector : public TypeWalker +{ +public: + TypeCollector () + {}; + + /* Main interface. */ + void collect (const_tree t); + + /* Collect the result after walking all trees. */ + tpartitions_t get_record_reaching_trees () + { + _sanity_check (); + return ptrset; + } + +private: + /* PTR stands for points to record. Before walking into a RECORD_TYPE + * tree, the value is always false. Once a RECORD_TYPE is visited, + * update all values on map to be true. At post-order keys + * will be erased. + * Invariants: + * before pre-order of root T map is empty + * after post-order of root T map is empty + + * In other words, the contents are reset after every + * call to collect. + */ + std::map<const_tree, bool> ptr; + + /* The type partition set that will hold partitions for + * points to record or does not point to record. + * Will be updated during post-order with the keys and values + * from PTR. + * This datastructure persists across calls to collect. + */ + tpartitions_t ptrset; + + /* Sanity check determines that the partitions are mutually + * exclusive. + */ + void _sanity_check (); + + /* Store T into partition depending on PTR. */ + void _collect_simple (const_tree t); + + /* If the value is in PTRSET, no need to visit the lower nodes. */ + virtual bool is_memoized (const_tree t); + + /* These functions insert and erase elements in PTR. + + * We probably don't need to create a _pre and _post + * function for all of them. We could probably substitute + * one for a general *_pre and *_post method that triggers + * for all different type T. However, we want to avoid + * collecting FIELD_DECL, ARGS, and some other none-types. + */ + virtual void _walk_VOID_TYPE_pre (const_tree t); + virtual void _walk_VOID_TYPE_post (const_tree t); + virtual void _walk_INTEGER_TYPE_pre (const_tree t); + virtual void _walk_INTEGER_TYPE_post (const_tree t); + virtual void _walk_REAL_TYPE_pre (const_tree t); + virtual void _walk_REAL_TYPE_post (const_tree t); + virtual void _walk_FIXED_POINT_TYPE_pre (const_tree t); + virtual void _walk_FIXED_POINT_TYPE_post (const_tree t); + virtual void _walk_COMPLEX_TYPE_pre (const_tree t); + virtual void _walk_COMPLEX_TYPE_post (const_tree t); + virtual void _walk_ENUMERAL_TYPE_pre (const_tree t); + virtual void _walk_ENUMERAL_TYPE_post (const_tree t); + virtual void _walk_BOOLEAN_TYPE_pre (const_tree t); + virtual void _walk_BOOLEAN_TYPE_post (const_tree t); + virtual void _walk_ARRAY_TYPE_pre (const_tree t); + virtual void _walk_ARRAY_TYPE_post (const_tree t); + virtual void _walk_POINTER_TYPE_pre (const_tree t); + virtual void _walk_POINTER_TYPE_post (const_tree t); + virtual void _walk_REFERENCE_TYPE_pre (const_tree t); + virtual void _walk_REFERENCE_TYPE_post (const_tree t); + virtual void _walk_UNION_TYPE_pre (const_tree t); + virtual void _walk_UNION_TYPE_post (const_tree t); + virtual void _walk_FUNCTION_TYPE_pre (const_tree t); + virtual void _walk_FUNCTION_TYPE_post (const_tree t); + virtual void _walk_METHOD_TYPE_pre (const_tree t); + virtual void _walk_METHOD_TYPE_post (const_tree t); + + /* When a RECORD_TYPE is found, update all values in PTR to true. */ + virtual void _walk_RECORD_TYPE_pre (const_tree t); + virtual void _walk_RECORD_TYPE_post (const_tree t); +}; + +/* Derived class from TypeWalker. This class + * will recursively print all type trees unlike just printing + * the identifier. + * TODO: Is this already implemented with debug_tree / print_tree? + * If so, we can probably delete this... + */ +class TypeStringifier : public TypeWalker +{ +public: + TypeStringifier () + {}; + + /* Main method, returns a stringified version of T. */ + std::string stringify (const_tree t); + + /* Only get type identifier. */ + static std::string get_type_identifier (const_tree t); + + /* If field is not anonymous, return field identifier. */ + static std::string get_field_identifier (const_tree t); + +private: + /* Working string... will hold result for stringify. */ + std::string _stringification; + + /* Append get_tree_code_name. */ + void _stringify_simple (const_tree t); + + /* Append identifier and "{". */ + void _stringify_aggregate_pre (const_tree t); + + /* Append "}". */ + void _stringify_aggregate_post (const_tree t); + + /* Append "function {". */ + // TODO: For C++ we will need to change this for methods. + void _stringify_fm_pre (const_tree t); + virtual void _walk_METHOD_TYPE_pre (const_tree t); + virtual void _walk_METHOD_TYPE_post (const_tree t); + virtual void _walk_FUNCTION_TYPE_pre (const_tree t); + virtual void _walk_FUNCTION_TYPE_post (const_tree t); + + /* Append "}". */ + void _stringify_fm_post (const_tree t); + + /* Most of the pre-order walk can probably be replaced by + * a catch all pre-order call back. + * TODO: implement that... + */ + virtual void _walk_VOID_TYPE_pre (const_tree t); + virtual void _walk_INTEGER_TYPE_pre (const_tree t); + virtual void _walk_REAL_TYPE_pre (const_tree t); + virtual void _walk_FIXED_POINT_TYPE_pre (const_tree t); + virtual void _walk_COMPLEX_TYPE_pre (const_tree t); + virtual void _walk_BOOLEAN_TYPE_pre (const_tree t); + virtual void _walk_OFFSET_TYPE_pre (const_tree t); + virtual void _walk_return_pre (const_tree t); + virtual void _walk_args_pre (const_tree t); + + /* Append "*". */ + virtual void _walk_POINTER_TYPE_post (const_tree t); + + /* Append "&". */ + virtual void _walk_REFERENCE_TYPE_post (const_tree t); + + /* Append "[]". */ + virtual void _walk_ARRAY_TYPE_post (const_tree t); + + /* Append "record" */ + virtual void _walk_RECORD_TYPE_pre (const_tree t); + virtual void _walk_RECORD_TYPE_post (const_tree t); + + /* Append "union" */ + virtual void _walk_UNION_TYPE_pre (const_tree t); + virtual void _walk_UNION_TYPE_post (const_tree t); + + /* Append "," */ + virtual void _walk_field_post (const_tree t); + virtual void _walk_return_post (const_tree t); + + /* Append "," */ + virtual void _walk_args_post (const_tree t); + virtual void _walk_arg_post (const_tree t); +}; + +/* ExprCollector is an implementation of ExprWalker. It walks + * all trees in an expression and calls type collector on + * the types for all types of nested expressions. + */ +class ExprCollector : public ExprWalker +{ +public: + ExprCollector () + {}; + + /* Holds the result after collecting from all trees. */ + tpartitions_t get_record_reaching_trees () + { + return typeCollector.get_record_reaching_trees (); + } + +private: + TypeCollector typeCollector; + + /* Catch all callback for all nested expressions E. */ + virtual void _walk_pre (const_tree e); +}; + +/* Derived from GimpleWalker. Its purpose is to walk all gimple + * possible and call expression collector to collect types + * on global variables, assign statement, return statement, + * condition statement, and call statements. + */ +class GimpleTypeCollector : public GimpleWalker +{ +public: + GimpleTypeCollector () + {}; + + /* This holds the result after walking the whole program. */ + tpartitions_t get_record_reaching_trees () + { + return exprCollector.get_record_reaching_trees (); + } + + // Print final results. + // TODO: I believe this could be made const. + void print_collected (); + +private: + ExprCollector exprCollector; + + /* Call back for global variables. */ + virtual void _walk_pre_tree (const_tree); + + /* Call back for gassign. */ + virtual void _walk_pre_gassign (gassign *s); + + /* Call back for greturn. */ + virtual void _walk_pre_greturn (greturn *s); + + /* Call back for gcond. */ + virtual void _walk_pre_gcond (gcond *s); + + /* Call back for gcall. */ + virtual void _walk_pre_gcall (gcall *s); +}; + +// Reason for why a type is escaping +// Used in a map with corresponding trees as keys. +// TODO: Add bit_field +// TODO: Add has function pointer +// TODO: Add has constructor +struct Reason +{ + // Determines whether a type is escaping. + inline bool is_escaping () const + { + return this->global_is_visible || this->parameter_is_visible + || this->return_is_visible || this->type_is_casted + || this->type_is_volatile || this->type_is_in_union + || this->is_indirect; + } + + // Escapes because a global variable is visible. + bool global_is_visible : 1; + + // Escapes because a parameter is used in an + // externally visible function. + bool parameter_is_visible : 1; + + // Escapes because return value is from + // an externally visible function. + bool return_is_visible : 1; + + // Escapes because of casting. + bool type_is_casted : 1; + + // Escapes because it is volatile. + bool type_is_volatile : 1; + + // Escapes because it is in union. + bool type_is_in_union : 1; + + // Escapes because is in indirect function call. + bool is_indirect : 1; + + // Merge two reason. + Reason operator| (const Reason &); + Reason &operator|= (const Reason &); + + // Print reasons a type is escaping. + void print () const; + + // Initialize as non-escaping by default. + Reason () + : global_is_visible (0), parameter_is_visible (0), return_is_visible (0), + type_is_casted (0), type_is_volatile (0), type_is_in_union (0), + is_indirect (0) +{}; +}; + +typedef std::map<const_tree, Reason> typemap; + +/* Type Escaper propagates information on whether a type escapes + * to all types reachable by a root type. It also propagates + * information up if a union is found. At the moment + * we don't transform types which point to unions. + * We also don't transform RECORD_TYPEs which have function pointers. + * This can possible be removed. But it is future work. + * Do not also modify types with bit fields. + */ +class TypeEscaper : public TypeWalker +{ +public: + TypeEscaper (tpartitions_t &p) + : _ptrset (p), _inside_union (0), _inside_record (0) + {}; + + // Hold the partitions for escaping non escaping. + tpartitions_t &_ptrset; + + // Have information that matches a tree type with + // why a type is escaping. + typemap calc; + + // Get partitions after calculating escaping types. + tpartitions_t get_sets (); + + // Print reasons why a type is escaping. + void print_reasons (); + + // Update type T with escaping reason R. + void update (const_tree t, Reason r); + +private: + // TODO: we can probably reduce the amount of functions + // by adding a catch all pre-order callback... + virtual void _walk_POINTER_TYPE_pre (const_tree t); + virtual void _walk_POINTER_TYPE_post (const_tree t); + virtual void _walk_REFERENCE_TYPE_pre (const_tree t); + virtual void _walk_ARRAY_TYPE_pre (const_tree t); + virtual void _walk_ARRAY_TYPE_post (const_tree t); + virtual void _walk_RECORD_TYPE_pre (const_tree t); + virtual void _walk_RECORD_TYPE_post (const_tree t); + virtual void _walk_field_pre (const_tree t); + virtual bool is_memoized (const_tree t); + + /* Mark escaping reason as having a function pointer in a structure, + * propagate up and down. */ + virtual void _walk_METHOD_TYPE_pre (const_tree t); + virtual void _walk_FUNCTION_TYPE_pre (const_tree t); + + /* Mark escaping reason as having a union and propagate up and down. */ + virtual void _walk_UNION_TYPE_pre (const_tree t); + virtual void _walk_UNION_TYPE_post (const_tree t); + + // Record how many nested unions the current context is in. + unsigned _inside_union; + + // Record how many nested records the current context is in. + unsigned _inside_record; + + // Reason to be propagated to all trees reachable by root T + // Can be updated during the walk. + Reason _reason; + + // Recursive inner function. + void _update (const_tree t); + + // Final method that places types from calc to partitions. + void place_escaping_types_in_set (); +}; + +/* Visit all expressions and update reasons why they might be deleted. */ +class ExprEscaper : public ExprWalker +{ +public: + ExprEscaper (tpartitions_t &types) : typeEscaper (types) + {}; + + /* Main interface: T escapes because R. */ + void update (const_tree t, Reason r); + + /* Will be used to propagate escaping reasons to Types. */ + TypeEscaper typeEscaper; + + /* Holds the end result. */ + tpartitions_t get_sets (); + + /* Print end result. */ + void print_reasons (); + +private: + // Keep track of the current expressions. The top of the stack + // is the subexpression being examined. + // The bottom of the stack is the expression called on the update + // function. + std::stack<const_tree> _stack; + + // Reason to propagate across all subexpressions. + Reason _r; + + // push to stack. + virtual void _walk_pre (const_tree e); + + // pop to stack. + virtual void _walk_post (const_tree e); + + // Check if there is a cast between the + // expression (MEM_REF (SSA_NAME)) + // SSA_NAME is the subexpression of MEM_REF. + virtual void _walk_SSA_NAME_pre (const_tree e); + + // If the expression E is a constructor then we need + // to mark these types as escaping because we cannot + // deal with constructors at the moment. + virtual void _walk_CONSTRUCTOR_pre (const_tree e); +}; + +// Do a type structural equality for two types. +class TypeStructuralEquality +{ +public: + TypeStructuralEquality () + {}; + + // Return TRUE if A and B have equal structures + bool equal (const_tree a, const_tree b); + +protected: + // Recursive _equal + virtual bool _equal (const_tree a, const_tree b); + +private: + // Use to limit recursion if we are revisiting a node + typedef std::set<const_tree> tset_t; + + // Limit recursion for LHS + tset_t set_l; + + // Limit recursion for RHS + tset_t set_r; + + // Determine if the code is equal + bool _equal_code (const_tree a, const_tree b); + + // Wrapper around POINTER_TYPE, ARRAY_TYPE and REFERENCE_TYPE + bool _equal_wrapper (const_tree a, const_tree b); + + // Different types we are comparing +#define TSE_FUNC_DECL(code) \ + virtual bool _walk_##code (const_tree l, const_tree r) + + // Current types that can be compared. + TSE_FUNC_DECL (VOID_TYPE); + TSE_FUNC_DECL (COMPLEX_TYPE); + TSE_FUNC_DECL (INTEGER_TYPE); + TSE_FUNC_DECL (REAL_TYPE); + TSE_FUNC_DECL (FIXED_POINT_TYPE); + TSE_FUNC_DECL (POINTER_TYPE); + TSE_FUNC_DECL (ENUMERAL_TYPE); + TSE_FUNC_DECL (BOOLEAN_TYPE); + TSE_FUNC_DECL (OFFSET_TYPE); + TSE_FUNC_DECL (RECORD_TYPE); + TSE_FUNC_DECL (REFERENCE_TYPE); + TSE_FUNC_DECL (ARRAY_TYPE); + TSE_FUNC_DECL (UNION_TYPE); + TSE_FUNC_DECL (FUNCTION_TYPE); + TSE_FUNC_DECL (METHOD_TYPE); +}; + +// Check for equality even when a type is incomplete. +// When one type is incomplete and MAIN_VARIANTS are different +// compare only with identifiers. +// Unsound but it is as sound as it can be. +class TypeIncompleteEquality : public TypeStructuralEquality +{ +public: + TypeIncompleteEquality () + {}; + +protected: + virtual bool _equal (const_tree l, const_tree r); +}; + +/* Inspect gimple code and find reasons why types might escape given a gimple + * stmt. */ +class GimpleEscaper : public GimpleWalker +{ +public: + GimpleEscaper (tpartitions_t &types) : exprEscaper (types) + { + _init (); + }; + + /* Propagate escaping reason to subexpressions. */ + ExprEscaper exprEscaper; + + /* Obtain final result. */ + tpartitions_t get_sets (); + + /* Print final result. */ + void print_reasons (); + +protected: + /* Set of undefined functions, this set is filled with + * functions obtained via FOR_EACH_FUNCTION_WITH_GIMPLE_BODY. */ + typedef std::set<const_tree> undefset; + undefset undefined; + + /* Initializes undefined. */ + void _init (); + + /* Return true if it is a known builtin function. */ + static bool filter_known_function (cgraph_node *); + static bool filter_known_function (const_tree); + + /* Return true if function is externally visible. */ + static bool is_function_escaping (cgraph_node *); + static bool is_function_escaping (const_tree); + + /* Return true if variable is externally visible. */ + static bool is_variable_escaping (varpool_node *); + + /* Marks global variables with CONSTRUCTORS and ERROR_MARKs as escaping. */ + virtual void _walk_global (varpool_node *); + + /* Do not escape on assignments. */ + virtual void _walk_pre_gassign (gassign *s); + + /* Do not escape return values. */ + virtual void _walk_pre_greturn (greturn *s); + + /* Do not escape gcond. */ + virtual void _walk_pre_gcond (gcond *s); + + /* Mark arguments and return type as escaping + * if callsite is undefined, indirect or externally visible. */ + virtual void _walk_pre_gcall (gcall *s); + + /* Mark T as escaping if is in UNKNOWN_LOCATION. + * This is a way of finding + * types introduced by profiling and mark them as escaping. + * TODO: Improve this. + */ + virtual void _walk_pre_tree (const_tree t); +}; + +/* + * GimpleCaster is intended to walk gimple + * and update a map that will hold information + * on whether a type was casted or not. + */ +class GimpleCaster : public GimpleEscaper +{ +public: + GimpleCaster (tpartitions_t &types) : GimpleEscaper (types) +{}; + +private: + /* Determine if cast comes from a known function. */ + static bool follow_def_to_find_if_really_cast (const_tree); + + /* If arguments are casted, mark them as escaping. + * Assignments from malloc and other known functions + * are allowed. + * */ + virtual void _walk_pre_gcall (gcall *s); + + /* If assignment is casted, mark them as escaping. + * Assignments from malloc and other known functions + * are allowed. + */ + virtual void _walk_pre_gassign (gassign *s); +}; + +// Bitflags used for determining if a field is +// never accessed, read or written. +// TODO: Place on their own namespace? +const unsigned Empty = 0x0u; +const unsigned Read = 0x01u; +const unsigned Write = 0x02u; + +// maps FIELD_DECL -> bitflag. +typedef std::map<const_tree, unsigned> field_access_map_t; + +// maps RECORD_TYPE -> (FIELD_DECL -> bitflag). +typedef std::map<const_tree, field_access_map_t> record_field_map_t; + +// Class used to determine if a FIELD is read, written or never accessed. +class TypeAccessor : public TypeWalker +{ +public: + TypeAccessor (record_field_map_t &map) : _map (map) + {}; + +private: + // maps RECORD -> (FIELD_DECL -> bitflag). + record_field_map_t &_map; + + // set of trees which are memoized and we don't need to look into them. + std::set<const_tree> memoized_map; + + // If a RECORD_TYPE is walked into, add all fields in struct to + // record_field_map. + virtual void _walk_RECORD_TYPE_pre (const_tree t); + void add_all_fields_in_struct (const_tree t); + + bool is_memoized (const_tree t); +}; + +// Determine if a FIELD is read, written or never accessed from +// an expression. +class ExprAccessor : public ExprWalker +{ +public: + ExprAccessor () : _typeAccessor (record_field_map) + {}; + + // Expr E is accessed in A manner. + void update (const_tree e, unsigned a); + + // Print results. + void print_accesses (); + + // Add all fields to map. Initialize with empty. + void add_all_fields_in_struct (const_tree t); + + // Get final results. + record_field_map_t get_map (); + +private: + // Access {"Read", "Write", "Neither"} to propagate to all subexpressions. + unsigned _access; + + // Stack to keep track of how current subexpression was reached. + std::stack<const_tree> _stack; + + // Holds main results. + record_field_map_t record_field_map; + + // Aids expr-accessor in updating types. + TypeAccessor _typeAccessor; + + // Mark FIELD_DECL as read. + // If ADDR_EXPR is parent expression that means + // The address of a field is taken. Mark + // all fields as possibly read. + virtual void _walk_COMPONENT_REF_pre (const_tree e); + + // Check if parent expression is MEM_REF. + // This means that an optimization was made + // and a FIELD_DECL is accessed via pointer + // arithmetic. Mark all fields from start to the one + // accessed as read. + // TODO: We don't necessarily need to mark them as + // possibly read if we update these expressions to + // point to the correct address in the future. + virtual void _walk_ADDR_EXPR_pre (const_tree e); + + // Push to stack. + virtual void _walk_pre (const_tree t); + + // Pop from stack. + virtual void _walk_post (const_tree t); +}; + +/* Walk all gimple and determine if fields were accessed. */ +class GimpleAccesser : public GimpleWalker +{ +public: + GimpleAccesser () + {}; + + /* Print final results. */ + void print_accesses (); + + /* Get final results. */ + record_field_map_t get_map (); + +private: + /* Navigate expressions in gimple statements. */ + ExprAccessor exprAccessor; + + /* Mark all RHS expressions reachable from S as Read. + all all LHS expressions reachable from S as Written. */ + virtual void _walk_pre_gcall (gcall *s); + + /* Mark all RHS expressions reachable from S as Read. + Mark all LHS expressions reachable from S as Written. */ + virtual void _walk_pre_gassign (gassign *s); + + /* Mark all all expressions reachable from S as read. */ + virtual void _walk_pre_greturn (greturn *s); + + /* Mark all expressions reachable from S as read. */ + virtual void _walk_pre_gcond (gcond *s); + + // Do we need a glabel? I don't think so... + // But we might need a gswitch. +}; + +typedef std::set<unsigned> field_offsets_t; + +typedef std::map<const_tree, field_offsets_t> record_field_offset_map_t; + + +#endif /* GCC_IPA_TYPE_ESCAPE_ANALYSIS_H */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-00-simple-read-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-00-simple-read-0.c new file mode 100644 index 00000000000..89e658ec73d --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-00-simple-read-0.c @@ -0,0 +1,22 @@ +/* { dg-do link } */ +/* { dg-options "-fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-access-analysis " } */ + +#include <stdio.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct astruct_s astruct; + +int +main () +{ + printf ("%d\n", astruct.a); + printf ("%d\n", astruct.a); +} + +// This means a read. +/* { dg-final { scan-wpa-ipa-dump "astruct_s.a = 0x0001" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-01-simple-write-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-01-simple-write-0.c new file mode 100644 index 00000000000..2ec0cf11049 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-01-simple-write-0.c @@ -0,0 +1,22 @@ +/* { dg-do link } */ +/* { dg-options "-fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-access-analysis " } */ + +#include <stdio.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct astruct_s astruct; + +int +main () +{ + astruct.a++; + astruct.a = 3; +} + +// This means that this is only a write and not a read. +/* { dg-final { scan-wpa-ipa-dump "astruct_s.a = 0x0003" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-02-pointer-read-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-02-pointer-read-0.c new file mode 100644 index 00000000000..b2ba8963b1e --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-02-pointer-read-0.c @@ -0,0 +1,22 @@ +/* { dg-do link } */ +/* { dg-options "-fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-access-analysis " } */ + +#include <stdio.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct astruct_s *astruct = NULL; + +int +main () +{ + printf ("%d\n", astruct->a); + printf ("%d\n", astruct->a); +} + +// So, even if we have a pointer COMPONENT_REF counter still works... +/* { dg-final { scan-wpa-ipa-dump "astruct_s.a = 0x0001" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-03-pointer-write-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-03-pointer-write-0.c new file mode 100644 index 00000000000..c8a966f45e9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-03-pointer-write-0.c @@ -0,0 +1,22 @@ +/* { dg-do link } */ +/* { dg-options "-fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-access-analysis " } */ + +#include <stdio.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct astruct_s *astruct; + +int +main () +{ + astruct->a++; + astruct->a = 3; +} + +// This says that only writes are happening even if astruct is a pointer +/* { dg-final { scan-wpa-ipa-dump "astruct_s.a = 0x0003" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-04-gimple-cond-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-04-gimple-cond-0.c new file mode 100644 index 00000000000..799457794d7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-04-gimple-cond-0.c @@ -0,0 +1,24 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-access-analysis " } */ + +#include <stdio.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct astruct_s astruct; + +int +main () +{ + if (astruct.a) + { + puts ("hello world"); + } +} + +// This says that astruct_s.a is read in a conditional +/* { dg-final { scan-wpa-ipa-dump "astruct_s.a = 0x0001" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-ea-00-collect-global-record-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-ea-00-collect-global-record-0.c new file mode 100644 index 00000000000..e4e62d4f127 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-00-collect-global-record-0.c @@ -0,0 +1,20 @@ +/* { dg-do link } */ +/* { dg-options "-fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-escape-analysis" } */ + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct astruct_s astruct; + +int +main () +{ + astruct.a = 0; +} + +// Please note that braces and semicollons are replaced with dots in order +// to parse correctly +/* { dg-final { scan-ipa-dump "collected: record astruct_s .boolean_type a.boolean_type b.boolean_type c.." "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-ea-01-collect-global-pointers-to-record-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-ea-01-collect-global-pointers-to-record-0.c new file mode 100644 index 00000000000..692daf9dc93 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-01-collect-global-pointers-to-record-0.c @@ -0,0 +1,23 @@ +/* { dg-do link } */ +/* { dg-options "-fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-escape-analysis" } */ + +#include <stddef.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct astruct_s *astruct; + +int +main () +{ + astruct = NULL; +} + +// This is for the structure +/* { dg-final { scan-ipa-dump "collected: record astruct_s .boolean_type a.boolean_type b.boolean_type c.." "type-escape-analysis" } } */ +// This one has an extra dot to mark the * for the pointer +/* { dg-final { scan-ipa-dump "collected: record astruct_s .boolean_type a.boolean_type b.boolean_type c..." "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-ea-02-collect-global-array-to-record-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-ea-02-collect-global-array-to-record-0.c new file mode 100644 index 00000000000..0f9b3438d3d --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-02-collect-global-array-to-record-0.c @@ -0,0 +1,23 @@ +/* { dg-do link } */ +/* { dg-options "-fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-escape-analysis" } */ + +#include <stddef.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct astruct_s astruct[2]; + +int +main () +{ + struct astruct_s another = astruct[0]; +} + +// This one is for the structure +/* { dg-final { scan-ipa-dump "collected: record astruct_s .boolean_type a.boolean_type b.boolean_type c.." "type-escape-analysis" } } */ +// This one is for the array. That's why it has two dots at the end. They correspond to [] +/* { dg-final { scan-ipa-dump "collected: record astruct_s .boolean_type a.boolean_type b.boolean_type c...." "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-ea-03-collect-nested-record-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-ea-03-collect-nested-record-0.c new file mode 100644 index 00000000000..efba2e5b33c --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-03-collect-nested-record-0.c @@ -0,0 +1,26 @@ +/* { dg-do link } */ +/* { dg-options "-fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-escape-analysis" } */ + +#include <stddef.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct outer_struct +{ + _Bool d; + struct astruct_s a; +}; +struct outer_struct bstruct; + +int +main () +{ + bstruct.d = 0; +} + +// We only care about collecting the inner struct for this test +/* { dg-final { scan-ipa-dump "collected: record astruct_s .boolean_type a.boolean_type b.boolean_type c.." "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-ea-04-collect-parameters-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-ea-04-collect-parameters-0.c new file mode 100644 index 00000000000..d646c8599e8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-04-collect-parameters-0.c @@ -0,0 +1,40 @@ +/* { dg-do link } */ +/* { dg-options "-fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-escape-analysis" } */ + +#include <stddef.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; + +void +record_parameter (struct astruct_s a) +{} +void +pointer_parameter (struct astruct_s *a) +{} +void +array_parameter (struct astruct_s a[]) +{} + +int +main () +{} + +// We have a complete type, probably from record_parameter +/* { dg-final { scan-wpa-ipa-dump "collected: record astruct_s .boolean_type a.boolean_type b.boolean_type c.." "type-escape-analysis" } } */ +// Now we have an incomplete struct +/* { dg-final { scan-wpa-ipa-dump "collected: record astruct_s .." "type-escape-analysis" } } */ +// This is the pointer... +/* { dg-final { scan-wpa-ipa-dump "collected: record astruct_s ..." "type-escape-analysis" } } */ +// We are missing the array parameter +// But it seems that the array parameter is passed as a pointer, which makes +// sense... array_parameter (struct astruct_s * a) +// { +// <bb 2> : +// return; +// +// } diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-ea-05-global-escapes-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-ea-05-global-escapes-0.c new file mode 100644 index 00000000000..25f6761978b --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-05-global-escapes-0.c @@ -0,0 +1,37 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-cast-analysis" } */ +/* { dg-require-effective-target lto } */ + +#include <stddef.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +__attribute__ (( + externally_visible)) struct astruct_s astruct; // This should escape +struct bstruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct bstruct_s bstruct; // This should not escape + +int +main () +{ + astruct.a = 0; + bstruct.b = 0; +} + +/* { dg-final { scan-wpa-ipa-dump "collected: record astruct_s .boolean_type a;boolean_type b;boolean_type c;." "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "collected: record bstruct_s .boolean_type a;boolean_type b;boolean_type c;." "type-escape-analysis" } } */ +// Do we find the externally visible struct? +// This says that record astruct_s is escaping because the reason g (global is +// visible) is set to true... +/* { dg-final { scan-wpa-ipa-dump " record astruct_s .boolean_type a.boolean_type b.boolean_type c.. reason: e=1 g=1 p=0 r=0 c=0 v=0 u=0 i=0" "type-escape-analysis" } } */ +// This says that record bstruct_s is not escaping +/* { dg-final { scan-wpa-ipa-dump " record bstruct_s .boolean_type a.boolean_type b.boolean_type c.. reason: e=0 g=0 p=0 r=0 c=0 v=0 u=0 i=0" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-ea-06-global-type-escapes-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-ea-06-global-type-escapes-0.c new file mode 100644 index 00000000000..9d6728d6b4d --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-06-global-type-escapes-0.c @@ -0,0 +1,42 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-cast-analysis" } */ + +#include <stddef.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +__attribute__ (( + externally_visible)) struct astruct_s astruct; // This should escape +struct bstruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct bstruct_s bstruct; // This should not escape + +int +main () +{ + astruct.a = 0; + bstruct.b = 0; +} + +// This test is pretty much the same as the previous one. +// The reason for this is because initially the previous +// test tested the escaping of variables and this one of +// types. Since, we are now not checking variables escaping +// these two tests have merged. + +/* { dg-final { scan-wpa-ipa-dump "collected: record astruct_s .boolean_type a;boolean_type b;boolean_type c;." "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "collected: record bstruct_s .boolean_type a;boolean_type b;boolean_type c;." "type-escape-analysis" } } */ +// Do we find the externally visible struct? +// This says that record astruct_s is escaping because the reason g (global is +// visible) is set to true... +/* { dg-final { scan-wpa-ipa-dump " record astruct_s .boolean_type a.boolean_type b.boolean_type c.. reason: e=1 g=1 p=0 r=0 c=0 v=0 u=0 i=0" "type-escape-analysis" } } */ +// This says that record bstruct_s is not escaping +/* { dg-final { scan-wpa-ipa-dump " record bstruct_s .boolean_type a.boolean_type b.boolean_type c.. reason: e=0 g=0 p=0 r=0 c=0 v=0 u=0 i=0" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-ea-08-parameter-escapes-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-ea-08-parameter-escapes-0.c new file mode 100644 index 00000000000..c4e8c78cd5f --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-08-parameter-escapes-0.c @@ -0,0 +1,37 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-cast-analysis" } */ +/* { dg-require-effective-target lto } */ + +#include <stddef.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct astruct_s astruct; // This should not escape +struct bstruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct bstruct_s bstruct; // This should not escape + +__attribute__ ((externally_visible)) void +escaping (struct astruct_s cstruct) +{} +void +non_escaping (struct bstruct_s dstruct) +{} + +int +main () +{ + escaping (astruct); + non_escaping (bstruct); +} + +/* { dg-final { scan-wpa-ipa-dump " record astruct_s .boolean_type a;boolean_type b;boolean_type c;. reason: e=1 g=0 p=1 r=0 c=0 v=0 u=0 i=0" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump " record bstruct_s .boolean_type a;boolean_type b;boolean_type c;. reason: e=0 g=0 p=0 r=0 c=0 v=0 u=0 i=0" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-ea-10-return-type-escapes-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-ea-10-return-type-escapes-0.c new file mode 100644 index 00000000000..55879c43719 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-10-return-type-escapes-0.c @@ -0,0 +1,42 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-cast-analysis" } */ + +#include <stddef.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct astruct_s astruct; // This should not escape +struct bstruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct bstruct_s bstruct; // This should not escape + +// This will make astruct_s escape +struct astruct_s __attribute__ ((externally_visible)) escaping () +{ + struct astruct_s a; + return a; +} +struct bstruct_s +non_escaping () +{} + +int +main () +{ + astruct = escaping (); + bstruct = non_escaping (); +} + +// This says that astruct_s escapes because it is returning from an externally +// visible function +/* { dg-final { scan-wpa-ipa-dump " record astruct_s .boolean_type a;boolean_type b;boolean_type c;. reason: e=1 g=0 p=0 r=1 c=0 v=0 u=0 i=0" "type-escape-analysis" } } */ +// This says that bstruct_s does not escape +/* { dg-final { scan-wpa-ipa-dump " record bstruct_s .boolean_type a;boolean_type b;boolean_type c;. reason: e=0 g=0 p=0 r=0 c=0 v=0 u=0 i=0" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-ea-11-cast-to-void-ptr-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-ea-11-cast-to-void-ptr-0.c new file mode 100644 index 00000000000..e4293d93bf9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-11-cast-to-void-ptr-0.c @@ -0,0 +1,44 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-cast-analysis" } */ + +#include <stddef.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct bstruct_s +{ + _Bool a; + _Bool b; + _Bool c; + _Bool d; +}; + +struct bstruct_s * +casting_to_void (struct astruct_s *s) +{ + return (struct bstruct_s *) (s); +} + +int +main () +{ + struct astruct_s astruct; + struct bstruct_s bstruct; + casting_to_void (&astruct); +} + +// The type +/* { dg-final { scan-wpa-ipa-dump " record bstruct_s .boolean_type a;boolean_type b;boolean_type c;boolean_type d;. reason: e=1 g=0 p=0 r=0 c=1 v=0 u=0 i=0" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump " record astruct_s .boolean_type a;boolean_type b;boolean_type c;. reason: e=1 g=0 p=0 r=0 c=1 v=0 u=0 i=0" "type-escape-analysis" } } */ +// The pointer +/* { dg-final { scan-wpa-ipa-dump " record bstruct_s .boolean_type a;boolean_type b;boolean_type c;boolean_type d;.. reason: e=1 g=0 p=0 r=0 c=1 v=0 u=0 i=0" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump " record astruct_s {boolean_type a;boolean_type b;boolean_type c;}* reason: e=1 g=0 p=0 r=0 c=1 v=0 u=0 i=0" "type-escape-analysis" } } */ + +// But there are incomplete types... should does be marked as escaping as well +// here? No, because at the moment, how we compute it is with the fixed point +// and at that moment, we get rid of reasons. So, at the moment, it is +// sufficient to say that only a fraction of equivalent types escape. diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-ea-12-cast-to-void-ptr-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-ea-12-cast-to-void-ptr-0.c new file mode 100644 index 00000000000..3d2c0c342fc --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-12-cast-to-void-ptr-0.c @@ -0,0 +1,38 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-cast-analysis" } */ + +#include <stddef.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct astruct_s astruct; // This should not escape +struct bstruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct bstruct_s bstruct; // This should not escape + +void +casting_to_void (struct astruct_s *s) +{ + void *nullify_non_escape = s; +} + +int +main () +{ + astruct.a = 0; + bstruct.b = 0; +} + +/* { dg-final { scan-wpa-ipa-dump "void_type. reason: e=1 g=0 p=0 r=0 c=1 v=0 u=0 i=0" "type-escape-analysis" } } */ +// base type +/* { dg-final { scan-wpa-ipa-dump " record astruct_s .boolean_type a;boolean_type b;boolean_type c;. reason: e=1 g=0 p=0 r=0 c=1 v=0 u=0" "type-escape-analysis" } } */ +// pointer +/* { dg-final { scan-wpa-ipa-dump " record astruct_s .boolean_type a;boolean_type b;boolean_type c;.. reason: e=1 g=0 p=0 r=0 c=1 v=0 u=0" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-ea-13-calling-printf-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-ea-13-calling-printf-0.c new file mode 100644 index 00000000000..3821eb9816e --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-13-calling-printf-0.c @@ -0,0 +1,27 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-cast-analysis" } */ + +#include <stddef.h> +#include <stdio.h> + +int +main (int argc, char **argv) +{ + char *filename = "helloworld.txt"; + FILE *f = fopen (filename, "r"); + fclose (f); +} + +// This is the incomplete type p=0 because it is memoized and it is an +// optimization. However, I will also match a 1 +/* { dg-final { scan-wpa-ipa-dump " record _IO_FILE .. reason: e=1 g=0 p=. r=1 c=0 v=0 u=0 i=." "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump " record _IO_FILE ... reason: e=1 g=0 p=. r=1 c=0 v=0 u=0 i=." "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump " record _IO_wide_data .. reason: e=1 g=0 p=. r=1 c=0 v=0 u=0 i=." "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump " record _IO_wide_data ... reason: e=1 g=0 p=. r=1 c=0 v=0 u=0 i=." "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump " record _IO_codecvt .. reason: e=1 g=0 p=. r=1 c=0 v=0 u=0 i=." "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump " record _IO_codecvt ... reason: e=1 g=0 p=. r=1 c=0 v=0 u=0 i=." "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "void_type. reason: e=1 g=0 p=. r=1 c=0 v=0 u=0 i=." "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump " record _IO_marker .. reason: e=1 g=0 p=. r=1 c=0 v=0 u=0 i=." "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump " record _IO_marker ... reason: e=1 g=0 p=. r=1 c=0 v=0 u=0 i=." "type-escape-analysis" } } */ +// This is the complete type... but I have condensed it to match .* +/* { dg-final { scan-wpa-ipa-dump " record FILE .* reason: e=1 g=0 p=. r=1 c=0 v=0 u=0 i=." "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-ea-14-volatile-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-ea-14-volatile-0.c new file mode 100644 index 00000000000..4607b8b068d --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-14-volatile-0.c @@ -0,0 +1,20 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-cast-analysis" } */ + +#include <stddef.h> +#include <stdio.h> + +int +main (int argc, char **argv) +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + volatile struct astruct_s astruct; +} + +// This says that astruct_s has a volatile +/* { dg-final { scan-wpa-ipa-dump " record astruct_s .boolean_type a.boolean_type b.boolean_type c.. reason: e=1 g=0 p=0 r=0 c=0 v=1 u=0 i=0" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-ea-15-union-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-ea-15-union-0.c new file mode 100644 index 00000000000..e998ec8bfcb --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-15-union-0.c @@ -0,0 +1,25 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-cast-analysis" } */ + +#include <stddef.h> +#include <stdio.h> + +int +main (int argc, char **argv) +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + union outer + { + struct astruct_s a; + double b; + }; + union outer an_outer; +} + +// This says that astruct_s is inside a union +/* { dg-final { scan-wpa-ipa-dump " record astruct_s {boolean_type a;boolean_type b;boolean_type c;} reason: e=1 g=0 p=0 r=0 c=0 v=0 u=1 i=0" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-ea-16-parameter-cast-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-ea-16-parameter-cast-0.c new file mode 100644 index 00000000000..c2ac3e3eaf3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-16-parameter-cast-0.c @@ -0,0 +1,33 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-cast-analysis -Wno-incompatible-pointer-types" } */ + +#include <stddef.h> +#include <stdio.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct bstruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +void +foo (struct bstruct_s *s){}; + +int +main (int argc, char **argv) +{ + struct astruct_s astruct; + foo (&astruct); +} + +// This says that astruct_s is casted +/* { dg-final { scan-wpa-ipa-dump " record astruct_s .boolean_type a;boolean_type b;boolean_type c;. reason: e=1 g=0 p=0 r=0 c=1 v=0 u=0 i=0" +"type-escape-analysis" } } */ +// This says that the pointer is casted +/* { dg-final { scan-wpa-ipa-dump " record astruct_s .boolean_type a;boolean_type b;boolean_type c;.. reason: e=1 g=0 p=0 r=0 c=1 v=0 u=0 i=0" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-ea-17-malloc-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-ea-17-malloc-0.c new file mode 100644 index 00000000000..6579b1926fa --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-17-malloc-0.c @@ -0,0 +1,24 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-cast-analysis -Wno-incompatible-pointer-types" } */ + +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; + +int +main (int argc, char **argv) +{ + struct astruct_s *a = malloc (sizeof (struct astruct_s)); +} + +// This says that astruct_s is not casted +/* { dg-final { scan-wpa-ipa-dump " record astruct_s .boolean_type a;boolean_type b;boolean_type c;. reason: e=0 g=0 p=0 r=0 c=0 v=0 u=0 i=0" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump " record astruct_s .boolean_type a;boolean_type b;boolean_type c;.. reason: e=0 g=0 p=0 r=0 c=0 v=0 u=0 i=0" +"type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-03-new-type-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-03-new-type-0.c new file mode 100644 index 00000000000..f3d203759e4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-03-new-type-0.c @@ -0,0 +1,21 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-access-analysis " } */ + +#include <stdio.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct astruct_s astruct; + +int +main () +{ + printf ("%d\n", astruct.a); + printf ("%d\n", astruct.c); +} + +/* { dg-final { scan-ipa-dump " record astruct_s .boolean_type a;boolean_type b;boolean_type c;. .. record astruct_s .boolean_type a;boolean_type c;." "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-04-heterogeneous-struct-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-04-heterogeneous-struct-0.c new file mode 100644 index 00000000000..40133126bb1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-04-heterogeneous-struct-0.c @@ -0,0 +1,21 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-access-analysis " } */ + +#include <stdio.h> + +struct astruct_s +{ + float a; + _Bool b; + int c; +}; +struct astruct_s astruct; + +int +main () +{ + printf ("%d\n", astruct.a); + printf ("%d\n", astruct.c); +} + +/* { dg-final { scan-ipa-dump " record astruct_s .real_type a.boolean_type b.integer_type c.. .. record astruct_s .real_type a.integer_type c.." "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-04-layout-compile-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-04-layout-compile-0.c new file mode 100644 index 00000000000..0424f4cb6ea --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-04-layout-compile-0.c @@ -0,0 +1,21 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } */ + +#include <stddef.h> +#include <assert.h> + +int +main (int argc, char **argv) +{ + struct astruct_s + { + int a; + int b; + int c; + }; + struct astruct_s astruct; + int *c = &astruct.c; + int *a = &astruct.a; + ptrdiff_t d = c - a; + assert (d == 1); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-05-field-reads-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-05-field-reads-0.c new file mode 100644 index 00000000000..18e8fe0a596 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-05-field-reads-0.c @@ -0,0 +1,20 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + _Bool a = astruct.a; + _Bool c = astruct.c; + return 0; +} + +/* { dg-final { scan-ipa-dump "replacing field a 0 with a 0" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field c 16 with a 8" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-05-nested-struct-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-05-nested-struct-0.c new file mode 100644 index 00000000000..9e5ebfddc99 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-05-nested-struct-0.c @@ -0,0 +1,30 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-access-analysis " } */ + +#include <stdio.h> + +struct astruct_s +{ + float a; + _Bool b; + int c; +}; +struct ostruct_s +{ + struct astruct_s a; + float b; + float c; +}; +struct ostruct_s ostruct; + +int +main () +{ + printf ("%d\n", ostruct.b); + printf ("%d\n", ostruct.c); + printf ("%f\n", ostruct.a.a); + printf ("%d\n", ostruct.a.c); +} + +/* { dg-final { scan-ipa-dump " record astruct_s .real_type a;boolean_type b;integer_type c;. -> record astruct_s .real_type a;integer_type c;." "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump " record ostruct_s . record astruct_s .real_type a;boolean_type b;integer_type c;. a;real_type b;real_type c;. -> record ostruct_s . record astruct_s .real_type a;integer_type c;. a;real_type b;real_type c;." "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-05-rewrite-local-decl-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-05-rewrite-local-decl-0.c new file mode 100644 index 00000000000..d383dd8643e --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-05-rewrite-local-decl-0.c @@ -0,0 +1,23 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <stdio.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + printf ("%d %d\n", astruct.a, astruct.c); + return 0; +} + +// This is different from previous because here we are replacing a local +// declaration +/* { dg-final { scan-ipa-dump "replacing field a 0 with a 0" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field c 16 with c 8" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-06-field-writes-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-06-field-writes-0.c new file mode 100644 index 00000000000..6f064c6902b --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-06-field-writes-0.c @@ -0,0 +1,24 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <stdio.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + astruct.c = 0; + printf ("%d %d\n", astruct.a, astruct.c); + return 0; +} + +/* { dg-final { scan-ipa-dump "replacing field a 0 with a 0" "type-escape-analysis" } } */ +// There's two... mmm.. not sure how to convey this here to the test? +/* { dg-final { scan-ipa-dump "replacing field c 16 with c 8" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field c 16 with c 8" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-06-pointer-struct-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-06-pointer-struct-0.c new file mode 100644 index 00000000000..4d704fd51cd --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-06-pointer-struct-0.c @@ -0,0 +1,31 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis -fprint-access-analysis " } */ + +#include <stdio.h> + +struct astruct_s +{ + float a; + _Bool b; + int c; +}; +struct ostruct_s +{ + struct astruct_s *a; + float b; + float c; +}; +struct ostruct_s ostruct; + +int +main () +{ + printf ("%d\n", ostruct.b); + printf ("%d\n", ostruct.c); + printf ("%f\n", ostruct.a->a); + printf ("%d\n", ostruct.a->c); +} + +/* { dg-final { scan-ipa-dump " record astruct_s ... -> record astruct_s ..." "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump " record ostruct_s . record astruct_s ... a;real_type b;real_type c;. -> record ostruct_s . record astruct_s ... a;real_type b;real_type c;." "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump " record astruct_s .real_type a;boolean_type b;integer_type c.. -> record astruct_s .real_type a;integer_type c.." "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-07-delete-first-field-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-07-delete-first-field-0.c new file mode 100644 index 00000000000..a6ba015276b --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-07-delete-first-field-0.c @@ -0,0 +1,23 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> +#include <stdio.h> + +int +main () +{ + struct astruct_s + { + _Bool b; + _Bool a; + _Bool c; + _Bool d; + }; + struct astruct_s astruct; + + printf ("%d %d %d\n", astruct.a, astruct.c, astruct.d); + _Bool *a_ptr = &astruct.a; + struct astruct_s *s_ptr = &astruct; + assert (a_ptr == s_ptr); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-08-modify-double-struct-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-08-modify-double-struct-0.c new file mode 100644 index 00000000000..a2572b37b04 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-08-modify-double-struct-0.c @@ -0,0 +1,33 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> +#include <stdio.h> +int +main () +{ + struct inner_s + { + _Bool a; + _Bool b; + _Bool c; + _Bool d; + }; + struct astruct_s + { + struct inner_s a; + struct inner_s b; + struct inner_s c; + struct inner_s d; + }; + struct astruct_s astruct; + // printf("%d %d %d\n", &astruct.a, &astruct.c, &astruct.d); + struct inner_s *a_ptr = &(astruct.a); + struct inner_s *c_ptr = &(astruct.c); + struct inner_s *d_ptr = &(astruct.d); + printf ("%d %d %d %d\n", a_ptr->a, a_ptr->b, a_ptr->c, a_ptr->d); + struct inner_s *c_ptr_2 = a_ptr + 1; + struct inner_s *a_ptr_2 = d_ptr - 2; + assert (c_ptr_2 == c_ptr); + assert (a_ptr == a_ptr_2); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-09-modify-int-struct-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-09-modify-int-struct-0.c new file mode 100644 index 00000000000..e5e80860034 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-09-modify-int-struct-0.c @@ -0,0 +1,22 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> + +int +main () +{ + struct astruct_s + { + int a; + int b; + int c; + int d; + }; + struct astruct_s astruct; + int *a = &(astruct.a); + int *c = &(astruct.c); + int *d = &(astruct.d); + assert (c - 1 == a); + assert (a + 2 == d); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-1-prints-structs-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-1-prints-structs-0.c new file mode 100644 index 00000000000..1f6078e842d --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-1-prints-structs-0.c @@ -0,0 +1,19 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } */ + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + return 0; +} + +/* { dg-final { scan-ipa-dump "astruct_s.a may be deleted" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "astruct_s.b may be deleted" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "astruct_s.c may be deleted" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-10-array-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-10-array-0.c new file mode 100644 index 00000000000..1e84a46cfe2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-10-array-0.c @@ -0,0 +1,23 @@ +/* { dg-do run } */ +/* { dg-options " -flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } */ + +#include <assert.h> +#include <stdio.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s a; + printf ("%d %d\n", a.a, a.c); + struct astruct_s b[2]; + a = b[0]; + _Bool *a_ptr = &a.a; + _Bool *c_ptr = &a.c; + assert (a_ptr + 1 == c_ptr); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-11-rewrites-minus-expr-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-11-rewrites-minus-expr-0.c new file mode 100644 index 00000000000..2501da43b64 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-11-rewrites-minus-expr-0.c @@ -0,0 +1,22 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } */ + +#include <assert.h> +#include <stddef.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + _Bool *c_ptr = &(astruct.c); + _Bool *a_ptr = &(astruct.a); + ptrdiff_t diff = c_ptr - a_ptr; + assert (diff == 1); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-12-delete-last-field-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-12-delete-last-field-0.c new file mode 100644 index 00000000000..5183e632228 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-12-delete-last-field-0.c @@ -0,0 +1,23 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } */ + +#include <assert.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool c; + _Bool b; + }; + struct astruct_s b[2]; + _Bool *first_of_second = &(b[1].a); + _Bool *c_0 = &(b[0].c); + _Bool *a_1 = &(b[1].a); + _Bool *a_1_from_c_0 = c_0 + 1; + _Bool test = a_1_from_c_0 == a_1; + char compile_test[test ? 1 : -1]; + assert (test); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-13-modify-size-four-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-13-modify-size-four-0.c new file mode 100644 index 00000000000..911e9cba242 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-13-modify-size-four-0.c @@ -0,0 +1,25 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } */ + +#include <assert.h> +#include <stddef.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + _Bool d; + }; + struct astruct_s astruct; + _Bool *a = &(astruct.a); + _Bool *c = &(astruct.c); + _Bool *d = &(astruct.d); + ptrdiff_t diff_1 = c - a; + ptrdiff_t diff_2 = d - a; + assert (diff_1 == 1); + assert (diff_2 == 2); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-14-rewrite-plus-expr-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-14-rewrite-plus-expr-0.c new file mode 100644 index 00000000000..eb2ce4fc975 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-14-rewrite-plus-expr-0.c @@ -0,0 +1,25 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } */ + +#include <assert.h> +#include <stddef.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + astruct.a = 1; + astruct.c = 1; + int d = astruct.a + astruct.c; + assert (d == 2); + _Bool *a = &(astruct.a); + _Bool *c = &(astruct.c); + assert (a + 1 == c); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-15-rewrite-mult-expr-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-15-rewrite-mult-expr-0.c new file mode 100644 index 00000000000..1412d503acf --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-15-rewrite-mult-expr-0.c @@ -0,0 +1,25 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } */ + +#include <stddef.h> +#include <assert.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + astruct.a = 1; + astruct.c = 1; + int d = astruct.a * astruct.c; + assert (d == 1); + _Bool *a = &(astruct.a); + _Bool *c = &(astruct.c); + assert (a + 1 == c); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-16-rewrite-field-reads-ptr-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-16-rewrite-field-reads-ptr-0.c new file mode 100644 index 00000000000..90d6073ada7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-16-rewrite-field-reads-ptr-0.c @@ -0,0 +1,24 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } */ + +#include <assert.h> +#include <stddef.h> +#include <stdio.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + struct astruct_s *astruct_p = &astruct; + printf ("%d %d\n", astruct_p->a, astruct_p->c); + _Bool *a = &(astruct.a); + _Bool *c = &(astruct.c); + assert (a + 1 == c); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-17-rewrite-field-write-ptr-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-17-rewrite-field-write-ptr-0.c new file mode 100644 index 00000000000..22dd58f46c5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-17-rewrite-field-write-ptr-0.c @@ -0,0 +1,23 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } */ + +#include <assert.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + struct astruct_s *astruct_p = &astruct; + astruct_p->c = 1; + _Bool *a = &(astruct.a); + _Bool *c_ptr = &(astruct.c); + _Bool *c = a + 1; + assert (*c == *c_ptr); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-18-field-writes-deref-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-18-field-writes-deref-0.c new file mode 100644 index 00000000000..d58955521c2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-18-field-writes-deref-0.c @@ -0,0 +1,26 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } */ + +#include <assert.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + _Bool d; + }; + struct astruct_s astruct; + _Bool *c_ptr = &astruct.c; + *c_ptr = 1; + _Bool *a_ptr = &astruct.a; + _Bool *d_ptr = &astruct.d; + a_ptr++; + d_ptr--; + assert (*a_ptr == 1); + assert (*d_ptr == 1); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-19-middle-pointer-equal-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-19-middle-pointer-equal-0.c new file mode 100644 index 00000000000..d0e477641ca --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-19-middle-pointer-equal-0.c @@ -0,0 +1,26 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } */ + +#include <assert.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + _Bool d; + }; + struct astruct_s astruct; + _Bool *a = &astruct.a; + _Bool *d = &astruct.d; + _Bool *c = &astruct.c; + _Bool *c_from_a = a + 1; + _Bool *c_from_d = d - 1; + assert (c == c_from_a); + assert (c == c_from_d); + assert (c_from_a == c_from_d); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-2-modifies-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-2-modifies-0.c new file mode 100644 index 00000000000..14638d9414d --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-2-modifies-0.c @@ -0,0 +1,20 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } */ + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + astruct.a = astruct.c; + astruct.c = astruct.a; + return 0; +} + +/* { dg-final { scan-ipa-dump "astruct_s.b may be deleted" + * "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-20-array-offset-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-20-array-offset-0.c new file mode 100644 index 00000000000..21280681b0c --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-20-array-offset-0.c @@ -0,0 +1,24 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s b[2]; + _Bool *a_0_ptr = &(b[0].a); + _Bool *c_0_ptr = &(b[0].c); + _Bool *a_1_ptr = &(b[1].a); + + _Bool *c_0_ptr_from_a_0_ptr = a_0_ptr + 1; + _Bool *c_0_ptr_from_a_1_ptr = a_1_ptr - 1; + assert (c_0_ptr_from_a_0_ptr == c_0_ptr); + assert (c_0_ptr_from_a_1_ptr == c_0_ptr); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-22-rewrites-addr-expr-read-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-22-rewrites-addr-expr-read-0.c new file mode 100644 index 00000000000..869d2b622e0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-22-rewrites-addr-expr-read-0.c @@ -0,0 +1,23 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + _Bool *a = &astruct.a; + _Bool *c = &astruct.c; + _Bool *c_1 = a + 1; + _Bool *a_1 = c - 1; + assert (c_1 == c); + assert (a_1 == a); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-23-array-cast-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-23-array-cast-0.c new file mode 100644 index 00000000000..41618e06376 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-23-array-cast-0.c @@ -0,0 +1,31 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> +#include <stdio.h> +#include <stddef.h> +#include <stdio.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s a[2]; + struct astruct_s *a_0 = &(a[0]); + struct astruct_s *a_1 = &(a[1]); + struct astruct_s *a_1_from_a_0 = a_0 + 1; + printf ("%d %d\n", a_0->a, a_0->c); + // old new + // 0 a a + // 1 b c + // 2 c a + // 3 a c + // 4 b a + // 5 c c + assert (a_1 == a_1_from_a_0); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-25-array-cast-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-25-array-cast-0.c new file mode 100644 index 00000000000..bf867b6cd43 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-25-array-cast-0.c @@ -0,0 +1,31 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } */ + +#include <assert.h> +#include <stdio.h> +#include <stddef.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + _Bool d; + }; + struct astruct_s a[2]; + struct bstruct_s + { + _Bool a; + _Bool c; + _Bool d; + }; + + struct astruct_s *a_1 = &(a[1]); + struct astruct_s *a_0 = a_1 - 1; + struct bstruct_s *b_1 = (struct bstruct_s *) a_1; + struct bstruct_s *b_0 = b_1 - 1; + assert ((struct bstruct_s *) a_0 != b_0); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-26-array-cast-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-26-array-cast-0.c new file mode 100644 index 00000000000..ceef8d65f0d --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-26-array-cast-0.c @@ -0,0 +1,24 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> +#include <stdio.h> +#include <stddef.h> + +int +main (int argc, char **argv) +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + _Bool d; + }; + struct astruct_s a[2]; + struct astruct_s *a_0 = &(a[0]); + struct astruct_s *a_1 = a_0 + argc; + ptrdiff_t d = a_1 - a_0; + printf ("%d %d %d\n", a_0->a, a_0->c, a_0->d); + printf ("%d\n", d); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-27-array-cast-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-27-array-cast-0.c new file mode 100644 index 00000000000..7e3812718f0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-27-array-cast-0.c @@ -0,0 +1,21 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> +#include <stdio.h> +#include <stddef.h> + +int +main (int argc, char **argv) +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + _Bool d; + }; + struct astruct_s a[2][2]; + struct astruct_s b = a[argc][argc]; + printf ("%d %d %d\n", b.a, b.c, b.d); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-29-heterogeneous-struct.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-29-heterogeneous-struct.c new file mode 100644 index 00000000000..ee86abde434 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-29-heterogeneous-struct.c @@ -0,0 +1,22 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> +#include <stdio.h> +#include <stddef.h> + +int +main () +{ + struct astruct_s + { + int a; + _Bool b; + int c; + }; + struct astruct_s astruct; + int *a = &(astruct.a); + int *c = &(astruct.c); + ptrdiff_t d = c - a; + assert (d == 1); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-30-heterogenous-struct-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-30-heterogenous-struct-0.c new file mode 100644 index 00000000000..611c2250411 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-30-heterogenous-struct-0.c @@ -0,0 +1,27 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> +#include <stdio.h> +#include <stddef.h> + +int +main () +{ + // unmodified { a = 1, b = 4; c = 5; d = 8; e = 12 + // modified { a = 1, c = 2; d = 4, e = 8 + struct astruct_s + { + _Bool a; + int b; + _Bool c; + int d; + _Bool e; + }; + struct astruct_s astruct; + _Bool *a = &(astruct.a); + printf ("%d %d\n", astruct.c, astruct.d); + _Bool *e = &(astruct.e); + ptrdiff_t diff = e - a; + assert (diff == 8); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-31-heterogenous-struct-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-31-heterogenous-struct-0.c new file mode 100644 index 00000000000..d5807ac51c3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-31-heterogenous-struct-0.c @@ -0,0 +1,30 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> +#include <stdio.h> +#include <stddef.h> + +int +main () +{ + // unmodified a = 0, d = 5; e = 6; f = 7; b = 8; c = 9 + // modified a = 0, d = 1, e = 2, f = 3, c = 4; + struct astruct_s + { + _Bool a; + _Bool d; + _Bool e; + _Bool f; + _Bool b; + int c; + }; + struct astruct_s astruct; + struct astruct_s *p = &astruct; + printf ("%d %d %d %d %d\n", p->a, p->d, p->e, p->f, p->c); + _Bool *a = &(p->a); + int *c = &(p->c); + ptrdiff_t d = (_Bool *) c - a; + printf ("%d\n", d); + assert (d == 4); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-33-nested-struct-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-33-nested-struct-0.c new file mode 100644 index 00000000000..9a7ea83f5ff --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-33-nested-struct-0.c @@ -0,0 +1,39 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> + +int +main () +{ + struct astruct_s + { + _Bool _a; + _Bool _b; + _Bool _c; + _Bool _d; + }; + struct outerstruct_s + { + struct astruct_s a; + struct astruct_s b; + struct astruct_s c; + struct astruct_s d; + }; + struct outerstruct_s outerstruct; + struct astruct_s a = outerstruct.a; + struct astruct_s b = outerstruct.b; + struct astruct_s c = outerstruct.c; + struct astruct_s d = outerstruct.d; + _Bool _a = a._a; + _Bool _c = a._c; + _Bool _d = a._d; +} + +/* { dg-final { scan-ipa-dump "replacing field a 0 with a 0" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field b 32 with b 32" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field c 64 with c 64" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field d 96 with d 96" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field _a 0 with _a 0" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field _c 16 with _c 8" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field _d 24 with _d 16" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-33-pointer-indirection-level-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-33-pointer-indirection-level-0.c new file mode 100644 index 00000000000..ce32ed48f24 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-33-pointer-indirection-level-0.c @@ -0,0 +1,26 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + _Bool d; + }; + struct astruct_s astruct; + struct astruct_s *p0 = &astruct; + struct astruct_s **p1 = &p0; + _Bool *a_ptr = &(astruct.a); + _Bool *c_ptr = &(astruct.c); + _Bool *c_ptr_from_1 = a_ptr + 1; + _Bool *a_ptr_2 = &((*p1)->a); + _Bool *c_ptr_from_2 = a_ptr_2 + 1; + assert (c_ptr == c_ptr_from_1); + assert (c_ptr == c_ptr_from_2); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-34-array-cast-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-34-array-cast-0.c new file mode 100644 index 00000000000..13e7863495f --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-34-array-cast-0.c @@ -0,0 +1,26 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> +#include <stdio.h> +#include <stddef.h> + +int +main (int argc, char **argv) +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + _Bool d; + }; + struct astruct_s a[2][2]; + + struct astruct_s b = a[argc][argc]; + printf ("%d %d %d\n", b.a, b.c, b.d); +} + +/* { dg-final { scan-ipa-dump "replacing field a 0 with a 0" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field c 16 with c 8" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field d 24 with d 16" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-36-arguments-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-36-arguments-0.c new file mode 100644 index 00000000000..048560aac8e --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-36-arguments-0.c @@ -0,0 +1,42 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> +#include <stdio.h> +#include <stddef.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; + _Bool d; +}; + +// PASS BY VALUE +_Bool +foo (struct astruct_s astruct) +{ + _Bool *a = &astruct.a; + assert (!*a); + _Bool *c = a + 1; + assert (*c); + _Bool *d = a + 2; + assert (*d); + return *c; +} + +int +main (int argc, char **argv) +{ + struct astruct_s astruct; + astruct.a = 0; + astruct.c = argc; + astruct.d = 1; + printf ("%d %d %d\n", astruct.a, astruct.c, astruct.d); + foo (astruct); +} + +/* { dg-final { scan-ipa-dump "replacing field a 0 with a 0" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field c 16 with c 8" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field d 24 with d 16" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-37-arguments-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-37-arguments-0.c new file mode 100644 index 00000000000..638474007d1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-37-arguments-0.c @@ -0,0 +1,43 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> +#include <stdio.h> +#include <stddef.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; + _Bool d; +}; + +// PASS BY REFERENCE +_Bool +foo (struct astruct_s *astruct) +{ + assert (astruct); + _Bool *a = &(astruct->a); + assert (!*a); + _Bool *c = a + 1; + assert (*c); + _Bool *d = a + 2; + assert (*d); + return *c; +} + +int +main (int argc, char **argv) +{ + struct astruct_s astruct; + astruct.a = 0; + astruct.c = argc; + astruct.d = 1; + printf ("%d %d %d\n", astruct.a, astruct.c, astruct.d); + foo (&astruct); +} + +/* { dg-final { scan-ipa-dump "replacing field a 0 with a 0" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field c 16 with c 8" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field d 24 with d 16" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-38-return-values-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-38-return-values-0.c new file mode 100644 index 00000000000..0235945a1ec --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-38-return-values-0.c @@ -0,0 +1,39 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> +#include <stdio.h> +#include <stddef.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; + _Bool d; +}; + +// RETURN BY VALUE +struct astruct_s +foo (_Bool c) +{ + struct astruct_s astruct; + astruct.a = 0; + astruct.c = c; + astruct.d = 1; + return astruct; +} + +int +main (int argc, char **argv) +{ + struct astruct_s astruct; + astruct = foo (argc); + printf ("%d %d %d\n", astruct.a, astruct.c, astruct.d); + _Bool *a = &(astruct.a); + assert (!*a); + _Bool *c = a + 1; + assert (*c == argc); + _Bool *d = a + 2; + assert (*d); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-39-typedef-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-39-typedef-0.c new file mode 100644 index 00000000000..cd680066b47 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-39-typedef-0.c @@ -0,0 +1,21 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <stdio.h> + +int +main () +{ + typedef struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + } astruct_s; + astruct_s astruct; + printf ("%d %d", astruct.a, astruct.c); + return 0; +} + +/* { dg-final { scan-ipa-dump "replacing field a 0 with a 0" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field c 16 with c 8" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-40-typedef-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-40-typedef-0.c new file mode 100644 index 00000000000..01ee28f0f1a --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-40-typedef-0.c @@ -0,0 +1,22 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <stdio.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + typedef struct astruct_s astruct_s; + astruct_s astruct; + printf ("%d %d", astruct.a, astruct.c); + return 0; +} + +/* { dg-final { scan-ipa-dump "replacing field a 0 with a 0" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field c 16 with c 8" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-41-deref-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-41-deref-0.c new file mode 100644 index 00000000000..31bf8cd1d21 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-41-deref-0.c @@ -0,0 +1,30 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <stdio.h> +#include <assert.h> +#include <stdbool.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + struct astruct_s *t, copy; + t = &astruct; + t->a = true; + t->c = true; + copy = *t; + printf ("%d %d", copy.a, copy.c); + assert (astruct.a == true); + assert (astruct.c == true); + return 0; +} + +/* { dg-final { scan-ipa-dump "replacing field a 0 with a 0" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field c 16 with c 8" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-42-mem-ref-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-42-mem-ref-0.c new file mode 100644 index 00000000000..b7e361bb08d --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-42-mem-ref-0.c @@ -0,0 +1,32 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <stdlib.h> +#include <stdio.h> + +typedef struct astruct astruct_t; +typedef struct astruct *astruct_p; + +struct astruct +{ + int a; + int b; + astruct_p one; + astruct_p two; + int c; + int d; +}; + +int +main () +{ + int n = 10; + astruct_p *astructs; + register astruct_t *astructnew + = (astruct_t *) malloc (n * sizeof (astruct_p)); + astructs = (astruct_p *) malloc (n * n * sizeof (astruct_p)); + astructs[n - 1][n - 1] = astructnew[0]; + printf ("%d %d %d %d\n", astructnew->a, astructnew->b, astructnew->c, + astructnew->d); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-43-args-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-43-args-0.c new file mode 100644 index 00000000000..a85d8b70d44 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-43-args-0.c @@ -0,0 +1,39 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <stdio.h> + +#include <stdlib.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; +struct wrapper_s +{ + struct astruct_s *a; +}; + +void +foo (struct wrapper_s *wrapper){}; + +void +bar (struct wrapper_s *wrapper) +{ + foo (wrapper); +}; + +int +main () +{ + struct wrapper_s a_wrapper; + _Bool is_non_null = a_wrapper.a != NULL; + printf ("%d %d %d", &a_wrapper.a, is_non_null ? a_wrapper.a->a : 0, + is_non_null ? a_wrapper.a->c : 0); + bar (&a_wrapper); +} + +/* { dg-final { scan-ipa-dump "replacing field a 0 with a 0" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "replacing field c 16 with a 8" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-44-cond-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-44-cond-0.c new file mode 100644 index 00000000000..67c1d8db6eb --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-44-cond-0.c @@ -0,0 +1,16 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +struct a +{ + struct b *b; +} c (struct a *d) +{ + while (d) + ; +} +void +main () +{} + +/* { dg-final { scan-ipa-dump "deleting all fields for struct a" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-45-phis-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-45-phis-0.c new file mode 100644 index 00000000000..cd4fc80d2f0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-45-phis-0.c @@ -0,0 +1,17 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +struct a +{ + struct a_inner *b; +} c (struct a *d, struct a *e) +{ + while (e) + d = d; +} +int +main () +{} + +/* { dg-final { scan-ipa-dump "deleting all fields for struct a" + * "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-46-static-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-46-static-0.c new file mode 100644 index 00000000000..636d8c6b5a0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-46-static-0.c @@ -0,0 +1,23 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> +#include <stddef.h> + +struct a_s +{ + _Bool a; + _Bool b; + _Bool c; +}; + +struct a_s a_t; + +int +main () +{ + _Bool *a = &(a_t.a); + _Bool *c = &(a_t.c); + ptrdiff_t diff = c - a; + assert (diff == 1); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-47-constructor-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-47-constructor-0.c new file mode 100644 index 00000000000..52dc67589cc --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-47-constructor-0.c @@ -0,0 +1,27 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> +#include <stddef.h> +#include <stdio.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s an_arc; + struct another + { + _Bool a; + struct astruct_s d; + _Bool c; + }; + struct another an_another = {0, {0, 1}, 1}; + struct astruct_s a = an_another.d; + printf ("%d %d %d %d %d", an_another.a, &a, a.a, a.c, an_another.c); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-48-function-ptr-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-48-function-ptr-0.c new file mode 100644 index 00000000000..405ccab3c34 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-48-function-ptr-0.c @@ -0,0 +1,44 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> +#include <stddef.h> +#include <stdio.h> + +struct astruct_s +{ + _Bool a; + _Bool b; + _Bool c; +}; + +_Bool +returnLast (struct astruct_s astruct) +{ + return astruct.c; +} + +_Bool +returnLast2 (struct astruct_s astruct) +{ + _Bool *ptr = &(astruct.a); + ptr = ptr + 1; + return *ptr; +} + +int +main (int argc, char **argv) +{ + _Bool (*func1) (struct astruct_s); + func1 = &returnLast; + _Bool (*func2) (struct astruct_s); + func2 = &returnLast2; + struct astruct_s astruct; + astruct.c = argc; + printf ("%d %d", astruct.a, astruct.c); + // These test means that things remain equal + // A.k.a without an optimization. + // Why? Because a function pointer can be a + // pointer to anything + assert (func1 (astruct) != func2 (astruct)); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-50-field-write-delete-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-50-field-write-delete-0.c new file mode 100644 index 00000000000..ae1b23c324a --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-50-field-write-delete-0.c @@ -0,0 +1,24 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <assert.h> +#include <stddef.h> +#include <stdbool.h> +#include <stdio.h> + +int +main (int argc, char **argv) +{ + struct astruct_s + { + _Bool a; + _Bool delete_me; + _Bool c; + }; + struct astruct_s astruct; + printf ("%d %d", astruct.a, astruct.c); + astruct.delete_me = false; + return 0; +} + +/* { dg-final { scan-ipa-dump "deleting field delete_me 8" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-51-creduce-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-51-creduce-0.c new file mode 100644 index 00000000000..1d86ad808be --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-51-creduce-0.c @@ -0,0 +1,6 @@ +/* { dg-do compile } */ +/* { dg-options "-flto -flto-partition=none -fipa-type-escape-analysis" } */ + +struct +{ +} main () {} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-52-creduce-1.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-52-creduce-1.c new file mode 100644 index 00000000000..0eeda3f82b4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-52-creduce-1.c @@ -0,0 +1,13 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <stdint.h> +union a +{ + int16_t b +} c () +{ + union a d; + -d.b; +} +main () {} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-53-csmith-2.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-53-csmith-2.c new file mode 100644 index 00000000000..87268497d39 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-53-csmith-2.c @@ -0,0 +1,6 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ +struct +{ +} a; +main () {} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-54-csmith-3.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-54-csmith-3.c new file mode 100644 index 00000000000..0b4e981deb4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-54-csmith-3.c @@ -0,0 +1,24 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <stdint.h> +// TODO: So, our analysis says that we are deleting a field "a". +// And that the field "a" is contained in struct "b". +// However, we are doing is_interesting_struct("c") == yes +// is_interesting_field("a") == yes +// and so we delete field a from struct c. +struct +{ + uint64_t a +} b[]; +struct +{ + unsigned : 5; + unsigned a +} c; +d () +{ + uint16_t e = b; + int8_t f = c.a; +} +main () {} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-55-csmith-4.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-55-csmith-4.c new file mode 100644 index 00000000000..d4f1d684614 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-55-csmith-4.c @@ -0,0 +1,9 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <stdint.h> +union a +{ + int8_t b +} c () { union a d = {4073709551608}; } +main () {} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-56-csmith-5.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-56-csmith-5.c new file mode 100644 index 00000000000..b016a3b8ea1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-56-csmith-5.c @@ -0,0 +1,25 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <stdint.h> +struct a +{ + int8_t b +}; +struct c +{ + struct a d +} e[]; + +/* Analysis failed because e[2].d was considered not read + * we were only looking at e[2].d.b which is considered read. + * So we need to recurse + */ +f () { g (e[2].d.b, 0); } + +void +g (int8_t a, int8_t b) +{ + a + b; +} +main () {} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-57-csmith-6.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-57-csmith-6.c new file mode 100644 index 00000000000..985975e01d8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-57-csmith-6.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +struct a +{ +}; +b (struct a c) {} +main () {} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-58-csmith-7.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-58-csmith-7.c new file mode 100644 index 00000000000..bd49a606df8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-58-csmith-7.c @@ -0,0 +1,15 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <stdint.h> +struct a +{ + int32_t b +} c; +d () +{ + for (;; c.b = 0) + ; +} +main () {} +/* { dg-final { scan-ipa-dump "deleting field b 0" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-59-csmith-8.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-59-csmith-8.c new file mode 100644 index 00000000000..d8c546e4d32 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-59-csmith-8.c @@ -0,0 +1,14 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +struct a +{ + signed b +}; +struct +{ + struct a b +} volatile c; +main () { c.b.b; } + +// we will do nothing because volatile diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-60-csmith-9.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-60-csmith-9.c new file mode 100644 index 00000000000..64bd2711aad --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-60-csmith-9.c @@ -0,0 +1,26 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis " } */ + +#include <stdint.h> + +void +foo (uint64_t a, uint64_t b) +{ + a + b; +} + +struct a +{ + uint64_t b; + uint8_t c +} d () +{ + // I think the problem here is with the const attribute... + const struct a e; + foo (0, e.b); + return e; +} + +main () {} + +/* { dg-final { scan-ipa-dump "a.c may be deleted" "type-escape-analysis" } } */ |