diff options
102 files changed, 7796 insertions, 2 deletions
@@ -12,4 +12,3 @@ make -j `nproc` make install -j `nproc` make check-gcc RUNTESTFLAGS="ipa.exp" popd - diff --git a/gary/error-summary b/gary/error-summary new file mode 100644 index 00000000000..f51eae94ad9 --- /dev/null +++ b/gary/error-summary @@ -0,0 +1,31 @@ +There are two errors know to date and one porblem. + +Errors: + +1) Using typedef to create differents names for the same type will +confuse the optimization into thinking that they are different +types. + +This is a know big issue and not a surprise. + +2) Some local declarations go missing for not apparient reason. +To verify this is the case in a suspicious situation uncoment +the DEBUG statemest annotated with "test_1_8" and you'll see in the +stderr output that there are no declarations associate with the +function of interest. + +This was a surprise and will require a lot of debugging to track +dowm the root case of the problem. If you wish to see my test case +I will send it to you. + +Problem: + +If you invoke any of the structure reorg optimizations +(e.g. -fipa-structure-reorg), you'll need to include the options +"-flto -flto-partition=one". The problem is that struction reorg +will unfortunenately run if only "-flto" is included with a +struct reorg option but it will be insane and not function correctly. + +I've not found a way to address this yet but I'm hopeful that this +is someing we can cope with. + diff --git a/gary/how_to_organize_reorg b/gary/how_to_organize_reorg new file mode 100644 index 00000000000..c8116eac15b --- /dev/null +++ b/gary/how_to_organize_reorg @@ -0,0 +1,40 @@ +I'll put shared types and externs in ipa-structure-reorg.h. +The externs in file ipa-structure-reorg.h will be for the API +calls. + +There will three flags associated with + +-fipa-structure-reorg +-fipa-dead-field-eliminate +-fipa-field-reorder +-fipa-instance-interleave + +Specifying any of them will run the shared qualification analysis. + +Specifying -fipa-structure-reorg will be equivalent +to invoking all the other flags. + +The other flags just run the specific structure reorg algorithm. + +The driver and shared qualification code will be in ipa-structure-reorg.c. +I'm assuming for now that each algorithm will be in it's own file +and can be invoked via from the driver like this + + if ( control code ) + { + int return_val = algorithm( info); + // typical actions dependent on the return value; + } + +Note, the info parameter (of type Info *) is how I'd like to give the +API calls access to the persistent information. Frankly, this is my +own strange idiom but it's served me well in the past and I think it +beats globals which are something I want to avoid. + +Would it be OK for our first integration of code that I create one or +two empty files. You'd supply a name that you are using now of course +so you can just subsitute your work for the stubs on itegration. + +Also, reagarding the API itself wouldn't a reasonable start just +be some of the functions in the utility section of my design? +Check them out see if that makes any sense.
\ No newline at end of file diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 523e3045fe8..419f64ec70e 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1403,6 +1403,8 @@ OBJS = \ incpath.o \ init-regs.o \ internal-fn.o \ + ipa-type-escape-analysis.o \ + ipa-hello-world.o \ ipa-escape-analysis.o \ compare-types.o \ collect-types.o \ @@ -1428,6 +1430,11 @@ OBJS = \ ipa-reference.o \ ipa-hsa.o \ ipa-ref.o \ + ipa-structure-reorg.o \ + ipa-str-reorg-dead-field-eliminate.o \ + ipa-str-reorg-field-reorder.o \ + ipa-str-reorg-instance-interleave.o \ + ipa-str-reorg-utils.o \ ipa-utils.o \ ipa.o \ ira.o \ diff --git a/gcc/common.opt b/gcc/common.opt index 5c626286a16..86aeb0d39e7 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1838,6 +1838,22 @@ fipa-struct-reorg Common Ignore Does nothing. Preserved for backward compatibility. +fipa-structure-reorg +Common Report Var(flag_ipa_structure_reorg) Init(0) Optimization +Structure reorganization optimization. + +fipa-dead-field-eliminate +Common Report Var(flag_ipa_dead_field_eliminate) Init(0) Optimization +Structure reorganization optimization, dead field elimination. + +fipa-field-reorder +Common Report Var(flag_ipa_field_reorder) Init(0) Optimization +Structure reorganization optimization, field reordering. + +fipa-instance-interleave +Common Report Var(flag_ipa_instance_interleave) Init(0) Optimization +Structure reorganization optimization, instance interleaving. + fipa-vrp Common Report Var(flag_ipa_vrp) Optimization Perform IPA Value Range Propagation. @@ -3413,6 +3429,22 @@ 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 + +fipa-type-escape-analysis +Common Report Var(flag_ipa_type_escape_analysis) Optimization +Type escape analysis + +fipa-hello-world +Common Report Var(flag_ipa_hello_world) Optimization +Hello world + fipa-escape-analysis Common Report Var(flag_ipa_escape_analysis) Optimization TBD diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 850aeac033d..29d1fd2fff5 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -469,6 +469,8 @@ Objective-C and Objective-C++ Dialects}. -fdelete-null-pointer-checks -fdevirtualize -fdevirtualize-speculatively @gol -fdevirtualize-at-ltrans -fdse @gol -fearly-inlining -fipa-sra -fexpensive-optimizations -ffat-lto-objects @gol +-fipa-structure-reorg -fipa-dead-field-eliminate @gol +-fipa-field-reorder -fipa-instance-interleave @gol -ffast-math -ffinite-math-only -ffloat-store -fexcess-precision=@var{style} @gol -ffinite-loops @gol -fforward-propagate -ffp-contract=@var{style} -ffunction-sections @gol @@ -9438,6 +9440,29 @@ by parameters passed by value. Enabled at levels @option{-O2}, @option{-O3} and @option{-Os}. +@item -fipa-structure-reorg +@opindex fipa-structure-reorg +Transforms arrays of structures with the dead field, field reordering +and instance interleaving optimizations (which can be invoked seperately +with their own flags.) + +@item -fipa-dead-field-eliminate +@opindex fipa-dead-field-eliminate +Theobroma plaese add comment here. + +@item -fipa-field-reorder +@opindex fipa-field-reorder +Theobroma plaese add comment here. + +@item -fipa-instance-interleave +@opindex fipa-instance-interleave +Transforms arrays of structures into structures of arrays. This +improves cache locality if there are loops that use only a small +number of fields from a transformed array. The performance +qualification of this optimization relies on execution counts of loops +so running with branch profiling data available will improve the +outcome. + @item -finline-limit=@var{n} @opindex finline-limit By default, GCC limits the size of functions that can be inlined. This flag diff --git a/gcc/ipa-hello-world.c b/gcc/ipa-hello-world.c new file mode 100644 index 00000000000..e75de90bee5 --- /dev/null +++ b/gcc/ipa-hello-world.c @@ -0,0 +1,569 @@ +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#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-cfg.h" +#include "gimple.h" +#include "cfg.h" // needed for gimple-iterator.h +#include "gimple-iterator.h" + +#include <map> +#include <set> + +#include "ipa-type-escape-analysis.h" +#include "ipa-str-reorg-utils.h" +#include "ipa-hello-world.h" + + +//TODO: place in header file +inline static void +print_function (cgraph_node *cnode) +{ + if (!dump_file) + return; + gcc_assert (cnode); + cnode->get_untransformed_body (); + dump_function_to_file (cnode->decl, dump_file, TDF_NONE); +} + +//TODO: do not use pair. +//This names are unintelligible +typedef std::pair<unsigned /* reads */, unsigned /* writes */> accesses; +struct field_comparator +{ + bool operator()(const fields &left, const fields &right) const + { + const_tree left_record = left.first; + gcc_assert(left_record); + const_tree right_record = right.first; + gcc_assert(right_record); + // Make sure that we are only comparing valid types... + const enum tree_code tree_code_left_record = TREE_CODE(left_record); + const enum tree_code tree_code_right_record = TREE_CODE(right_record); + const bool is_left_record_type = RECORD_TYPE == tree_code_left_record; + const bool is_right_record_type = RECORD_TYPE == tree_code_right_record; + gcc_assert(is_left_record_type); + gcc_assert(is_right_record_type); + const bool are_left_and_right_valid = is_left_record_type && is_right_record_type; + gcc_assert(are_left_and_right_valid); + + // Handle typedefs: + // Get the main variants of the main types + const_tree main_variant_left = TYPE_MAIN_VARIANT(left_record); + gcc_assert(main_variant_left); + const_tree main_variant_right = TYPE_MAIN_VARIANT(right_record); + gcc_assert(main_variant_right); + // If they are not equal, we can do a comparison of the types here... + const bool are_main_variants_equal = main_variant_left == main_variant_right; + const bool left_type_less_than_right_type = main_variant_left < main_variant_right; + if (!are_main_variants_equal) return left_type_less_than_right_type; + + // If they are equal, that means that we are comparing fields defined in the same record + const_tree left_field = left.second; + gcc_assert(left_field); + const_tree right_field = right.second; + gcc_assert(right_field); + // Make sure that they are valid + const enum tree_code tree_code_left_field = TREE_CODE(left_field); + const enum tree_code tree_code_right_field = TREE_CODE(right_field); + const bool is_left_field_field_decl = FIELD_DECL == tree_code_left_field; + const bool is_right_field_field_decl = FIELD_DECL == tree_code_right_field; + const bool are_left_and_right_field_decls = is_left_field_field_decl && is_right_field_field_decl; + gcc_assert(are_left_and_right_field_decls); + + // Compare on the field offset. + const_tree left_constant = byte_position(left_field); + gcc_assert(left_constant); + const_tree right_constant = byte_position(right_field); + gcc_assert(right_constant); + unsigned left_offset = tree_to_uhwi(left_constant); + unsigned right_offset = tree_to_uhwi(right_constant); + const bool left_offset_less_than_right_offset = left_offset < right_offset; + return left_offset_less_than_right_offset; + } +}; + +typedef std::map<fields, accesses, field_comparator> field_access_counter; +typedef std::set<const_tree> record_set; + +enum access_code { READ_ACCESS, WRITE_ACCESS }; + +void count_access_for_types_in_expr(const_tree expr, const record_set &non_escaping_records, field_access_counter &counter, const enum access_code access); + +void +count_access_for_type_in_component_ref(const_tree component_ref, const record_set &non_escaping_records, field_access_counter &counter, const enum access_code access) +{ + log("types in component_ref\n"); + gcc_assert(component_ref); + enum tree_code tree_code_component_ref = TREE_CODE(component_ref); + const bool is_component_ref = COMPONENT_REF == tree_code_component_ref; + gcc_assert(is_component_ref); + + const_tree _struct = TREE_OPERAND(component_ref, 0); + gcc_assert(_struct); + + log ("going in recursion\n"); + count_access_for_types_in_expr(_struct, non_escaping_records, counter, READ_ACCESS); + const_tree tree_type_struct = TREE_TYPE(_struct); + gcc_assert(tree_type_struct); + enum tree_code tree_type_struct_code = TREE_CODE(tree_type_struct); + const bool is_record_type = RECORD_TYPE == tree_type_struct_code; + //TODO: Also write something for UNION_TYPE + switch (tree_type_struct_code) + { + case UNION_TYPE: return; break; + case RECORD_TYPE: break; + default: gcc_unreachable(); break; + } + gcc_assert(is_record_type); + + //FIXME: Future proofing or making things more difficult to read? + bool in_set = +#if __cplusplus > 201703L + non_escaping_records.contains(tree_type_struct) +#else + non_escaping_records.find(tree_type_struct) != non_escaping_records.end() +#endif + ; + + in_set |= +#if __cplusplus > 201703L + non_escaping_records.contains(TYPE_MAIN_VARIANT(tree_type_struct)) +#else + non_escaping_records.find(TYPE_MAIN_VARIANT(tree_type_struct)) != non_escaping_records.end() +#endif + ; + + + log("%s is in non_escaping_records ? %s\n", get_type_name(tree_type_struct), in_set ? "true" : "false"); + log("access is %s\n", access == READ_ACCESS ? "read_access" : "write_access"); + log("%s is in non_escaping set %s\n", get_type_name(tree_type_struct), in_set ? "true" : "false"); + if (!in_set) return; + + const_tree field = TREE_OPERAND(component_ref, 1); + gcc_assert(field); + enum tree_code tree_code_field = TREE_CODE(field); + const bool is_field_decl = FIELD_DECL == tree_code_field; + gcc_assert(is_field_decl); + + const std::pair<const_tree, const_tree> struct_field_pair = std::make_pair(tree_type_struct, field); + accesses &access_counter = counter[struct_field_pair]; + + switch (access) + { + case READ_ACCESS: + //TODO: do not use pair. + //This names are unintelligible + access_counter.first++; + log("%s.%s read %d\n", get_type_name(tree_type_struct), get_field_name(field), access_counter.first); + break; + case WRITE_ACCESS: + //TODO: do not use pair. + //This names are unintelligible + access_counter.second++; + log("%s.%s write %d\n", get_type_name(tree_type_struct), get_field_name(field), access_counter.second); + break; + default: + gcc_unreachable(); + break; + } +} + +static inline void +is_addr_expr_p(const_tree expr) +{ + gcc_assert(expr); + const enum tree_code code = TREE_CODE(expr); + const bool is_addr_expr = ADDR_EXPR == code; + gcc_assert(is_addr_expr); +} + + +void +count_access_for_type_in_addr_expr(const_tree expr, const record_set &non_escaping_records, field_access_counter &counter, const enum access_code access) +{ + is_addr_expr_p(expr); + const_tree op0 = TREE_OPERAND(expr, 0); + count_access_for_types_in_expr(op0, non_escaping_records, counter, access); +} + +static inline void +is_array_expr_p(const_tree expr) +{ + gcc_assert(expr); + const enum tree_code code = TREE_CODE(expr); + const bool is_addr_expr = ARRAY_REF == code; + gcc_assert(is_addr_expr); +} + +void +count_access_for_type_in_array_expr(const_tree expr, const record_set &non_escaping_records, field_access_counter &counter, const enum access_code access) +{ + is_array_expr_p(expr); + const_tree op0 = TREE_OPERAND(expr, 0); + const_tree op1 = TREE_OPERAND(expr, 1); + count_access_for_types_in_expr(op0, non_escaping_records, counter, READ_ACCESS); + count_access_for_types_in_expr(op1, non_escaping_records, counter, READ_ACCESS); +} + +void +count_access_for_types_in_expr(const_tree expr, const record_set &non_escaping_records, field_access_counter &counter, const enum access_code access) +{ + log("types in expr\n"); + gcc_assert(expr); + enum tree_code tree_code_expr = TREE_CODE(expr); + switch (tree_code_expr) + { + case COMPONENT_REF: count_access_for_type_in_component_ref(expr, non_escaping_records, counter, access); break; + case ADDR_EXPR: count_access_for_type_in_addr_expr(expr, non_escaping_records, counter, access); break; + case ARRAY_REF : count_access_for_type_in_array_expr(expr, non_escaping_records, counter, access); break; + default: break; + } +} + +void +count_access_for_types_in_lhs(gimple *stmt, const record_set &non_escaping_records, field_access_counter &counter) +{ + gcc_assert(stmt); + const enum gimple_code gimple_code_stmt = gimple_code(stmt); + const bool is_assign = GIMPLE_ASSIGN == gimple_code_stmt; + const bool is_call = GIMPLE_CALL == gimple_code_stmt; + const bool is_valid = is_assign || is_call; + gcc_assert(is_valid); + + + const_tree lhs = is_assign ? gimple_assign_lhs (stmt) : gimple_call_lhs (stmt); + /* GIMPLE_CALL might have a lhs null. + * E.g. + * foo() + */ + if (!lhs) return; + + count_access_for_types_in_expr(lhs, non_escaping_records, counter, WRITE_ACCESS); +} + +void +count_access_for_types_in_rhs(gimple *stmt, const record_set &non_escaping_records, field_access_counter &counter) +{ + gcc_assert(stmt); + const enum gimple_code gimple_code_stmt = gimple_code(stmt); + const bool is_assign = GIMPLE_ASSIGN == gimple_code_stmt; + gcc_assert(is_assign); + + log("types in rhs\n"); + const enum gimple_rhs_class gimple_rhs_class_stmt = gimple_assign_rhs_class(stmt); + switch (gimple_rhs_class_stmt) + { + case GIMPLE_TERNARY_RHS: + { + log("gimple_ternary_rhs\n"); + } + /* fall through */ + case GIMPLE_BINARY_RHS: + { + log("gimple_binary_rhs\n"); + } + /* fall through */ + case GIMPLE_SINGLE_RHS: + case GIMPLE_UNARY_RHS: + { + const_tree rhs1 = gimple_assign_rhs1(stmt); + gcc_assert(rhs1); + count_access_for_types_in_expr(rhs1, non_escaping_records, counter, READ_ACCESS); + log("gimple_single_rhs\n"); + } + break; + default: + gcc_unreachable(); + break; + } +} + +inline static void +is_gimple_assign_p(gimple *stmt) +{ + gcc_assert(stmt); + const enum gimple_code code = gimple_code(stmt); + const bool is_assign = GIMPLE_ASSIGN == code; + gcc_assert(is_assign); +} + +inline static void +is_gimple_call_p(gimple *stmt) +{ + gcc_assert(stmt); + const enum gimple_code code = gimple_code(stmt); + const bool is_call = GIMPLE_CALL == code; + gcc_assert(is_call); + +} + +void +count_access_for_types_in_assign(gimple *stmt, const record_set &non_escaping_records, field_access_counter &counter) +{ + is_gimple_assign_p(stmt); + + count_access_for_types_in_rhs(stmt, non_escaping_records, counter); + count_access_for_types_in_lhs(stmt, non_escaping_records, counter); +} + +static void +count_access_for_types_in_call_rhs(gimple *stmt, const record_set &non_escaping_records, field_access_counter &counter) +{ + is_gimple_call_p(stmt); + unsigned args = gimple_call_num_args (stmt); + + + + for (unsigned i = 0; i < args; i++) + { + const_tree arg = gimple_call_arg (stmt, i); + count_access_for_types_in_expr(arg, non_escaping_records, counter, READ_ACCESS); + } + +} + +void +count_access_for_types_in_call(gimple *stmt, const record_set &non_escaping_records, field_access_counter &counter) +{ + is_gimple_call_p(stmt); + count_access_for_types_in_lhs(stmt, non_escaping_records, counter); + + /* TODO: We need to iterate over each argument and find out if it is a read */ + count_access_for_types_in_call_rhs(stmt, non_escaping_records, counter); +} + +void +count_access_for_types_in_stmt(gimple *stmt, const record_set &non_escaping_records, field_access_counter &counter) +{ + gcc_assert(stmt); + const enum gimple_code gimple_code_stmt = gimple_code(stmt); + switch(gimple_code_stmt) + { + case GIMPLE_ASSIGN: + count_access_for_types_in_assign(stmt, non_escaping_records, counter); + break; + case GIMPLE_CALL: + count_access_for_types_in_call(stmt, non_escaping_records, counter); + break; + default: + break; + } +} + +void +count_access_for_types_in_bb(basic_block bb, const record_set &non_escaping_records, field_access_counter &counter) +{ + gcc_assert(bb); + for (gimple_stmt_iterator gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) + { + gimple *stmt = gsi_stmt(gsi); + count_access_for_types_in_stmt(stmt, non_escaping_records, counter); + } +} + +void +count_access_for_types_in_function(cgraph_node *cnode, const record_set &non_escaping_records, field_access_counter &counter) +{ + gcc_assert(cnode); + tree decl = cnode->decl; + gcc_assert(decl); + function *func = DECL_STRUCT_FUNCTION (decl); + gcc_assert(func); + push_cfun(func); + basic_block bb = NULL; + FOR_EACH_BB_FN (bb, func) + { + gcc_assert(bb); + count_access_for_types_in_bb(bb, non_escaping_records, counter); + } + pop_cfun(); +} + +void +init_field_access_counter(field_access_counter &counter, const record_set &non_escaping_records) +{ + for (auto it = non_escaping_records.cbegin(); it != non_escaping_records.cend(); ++it) + { + const_tree record = *it; + gcc_assert(record); + enum tree_code tree_code_record_type = TREE_CODE(record); + const bool is_record_type = RECORD_TYPE == tree_code_record_type; + gcc_assert(is_record_type); + + for (tree field = TYPE_FIELDS (record); field; field = DECL_CHAIN (field)) + { + gcc_assert(field); + const std::pair<const_tree, const_tree> struct_field_pair = std::make_pair(record, field); + counter[struct_field_pair] = { 0, 0 }; + } + } +} + +/* INFO: + * Yes, I know we are returning a std::map. + * Bad pattern? Maybe, but this will only be called once + * and I rather pass by value because that allows + * me to have a pure function and not worry about + * garbage collection + * + * TODO: I'd like to change type_map for a std::map + * TODO: I'd like to make this a template that can work + * for std::map and std::set + */ +field_access_counter +count_access_for_types_in_linking_unit(const record_set &non_escaping_records) +{ + field_access_counter counter; + init_field_access_counter(counter, non_escaping_records); + cgraph_node *cnode = NULL; + FOR_EACH_DEFINED_FUNCTION(cnode) + { + gcc_assert(cnode); + log("printing a defined function\n"); + print_function(cnode); + count_access_for_types_in_function(cnode, non_escaping_records, counter); + } + return counter; +} + +bool +_calculate_non_escaping_records(const_tree const &type, escaping_info *info, record_set *non_escaping_records) +{ + + gcc_assert(info); + log("NOW: %s is escaping %s\n", get_type_name(type), info->is_escaping ? "true" : "false"); + if (info->is_escaping) return true; + gcc_assert(non_escaping_records); + + enum tree_code tree_code_type = TREE_CODE(type); + const bool is_record_type = RECORD_TYPE == tree_code_type; + log("NOW: %s is record %s\n", get_type_name(type), is_record_type ? "true" : "false"); + if (!is_record_type) return true; + + non_escaping_records->insert(type); + return true; +} + +/* + * Yes, again, we are passing a std::set + * as a value. I don\t care too much since + * this is only called once... + */ +static record_set +calculate_non_escaping_records(type_map &escaping_type_info) +{ + // We are going to have to traverse the type_map... + // This is why I don't really like hash_set... + record_set non_escaping_records; + escaping_type_info.traverse<record_set*, _calculate_non_escaping_records>(&non_escaping_records); + return non_escaping_records; +} + +static field_access_counter +count_field_accesses() +{ + + type_map escaping_types; + calculate_escaping_types(escaping_types); + const record_set non_escaping_records = calculate_non_escaping_records(escaping_types); + field_access_counter counter = count_access_for_types_in_linking_unit(non_escaping_records); + return counter; +} + +record_field_set +get_fields_to_reorg() +{ + const field_access_counter counter = count_field_accesses(); + record_field_set records_and_fields_to_reorg; + for (auto it = counter.cbegin(); it != counter.cend(); ++it) + { + fields record_field_pair = it->first; + accesses counter = it->second; + const_tree record = record_field_pair.first; + const_tree field = record_field_pair.second; + // We are interested in reads == 0; + unsigned reads = counter.first; + log("final count for %s.%s = %d\n", get_type_name(record), get_field_name(field), reads); + if (0 != reads) continue; + + records_and_fields_to_reorg.insert(record_field_pair); + } + + return records_and_fields_to_reorg; +} + +void +print_record_field_set(const record_field_set &to_reorg) +{ + log_2("I am about to print\n"); + for (auto it = to_reorg.cbegin(); it != to_reorg.cend(); ++it) + { + fields record_field_pair = *it; + const_tree record = record_field_pair.first; + const_tree field = record_field_pair.second; + // TODO: Some fields / records might be anonymous + log_2("will eliminate %s.%s\n", get_type_name(record), get_field_name(field)); + } +} + + +static unsigned int +iphw_execute() +{ + const record_field_set to_reorg = get_fields_to_reorg(); + log("I am about to enter print function\n"); + print_record_field_set(to_reorg); + return 0; +} + +namespace { +const pass_data pass_data_ipa_hello_world = +{ + SIMPLE_IPA_PASS, + "hello-world", + OPTGROUP_NONE, + TV_NONE, + (PROP_cfg | PROP_ssa), + 0, + 0, + 0, + 0, +}; + +class pass_ipa_hello_world : public simple_ipa_opt_pass +{ +public: + pass_ipa_hello_world (gcc::context *ctx) + : simple_ipa_opt_pass(pass_data_ipa_hello_world, ctx) + {} + + virtual bool gate(function*) { return flag_ipa_hello_world; } + virtual unsigned execute (function*) { return iphw_execute(); } +}; +} // anon namespace + +simple_ipa_opt_pass* +make_pass_ipa_hello_world (gcc::context *ctx) +{ + return new pass_ipa_hello_world (ctx); +} diff --git a/gcc/ipa-hello-world.h b/gcc/ipa-hello-world.h new file mode 100644 index 00000000000..aec99da3260 --- /dev/null +++ b/gcc/ipa-hello-world.h @@ -0,0 +1,19 @@ +#ifndef GCC_IPA_HELLO_WORLD_H +#define GCC_IPA_HELLO_WORLD_H +#pragma once + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" + +#include <set> + +//TODO: do not use pair. +//This names are unintelligible +typedef std::pair<const_tree /* record */, const_tree /* field */> fields; +typedef std::set<fields> record_field_set; +record_field_set get_fields_to_reorg(); +void print_record_field_set(const record_field_set &to_reorg); + +#endif diff --git a/gcc/ipa-str-reorg-dead-field-eliminate.c b/gcc/ipa-str-reorg-dead-field-eliminate.c new file mode 100644 index 00000000000..f86ed1f866a --- /dev/null +++ b/gcc/ipa-str-reorg-dead-field-eliminate.c @@ -0,0 +1,2763 @@ +/* Interprocedural scalar replacement of aggregates + 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/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "options.h" +#include "cgraph.h" +#include "tree-pass.h" +#include "tree-cfg.h" +#include "tree-pretty-print.h" +#include "gimple-pretty-print.h" +#include "stringpool.h" //get_identifier +#include "basic-block.h" //needed for gimple.h +#include "function.h" //needed for gimple.h +#include "gimple.h" +#include "cfg.h" // needed for gimple-iterator.h +#include "gimple-iterator.h" +#include "stor-layout.h" // layout_type +#include "fold-const.h" //build_fold_addr_expr +#include "gimple-ssa.h" // update_stmt +#include "attribs.h" // decl_attributes +#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 <vector> // needed for ipa-structure-reorg +#include <map> +#include <set> +#include "ipa-structure-reorg.h" +#include "ipa-utils.h" +#include "ipa-str-reorg-utils.h" +#include "ipa-hello-world.h" + +#define test_write(M, ...) \ + if (dump_file) \ + { \ + fprintf (dump_file, M, ##__VA_ARGS__); \ + } + +#define test_log(M, indent, ...) \ + if (dump_file) \ + { \ + fprintf (dump_file, "%*c" M "\n", indent, ' ', ##__VA_ARGS__); \ + } + + +static void +log_expr_prologue (const int indent, const char *debug_str, tree expr) +{ + const char *expr_str = print_generic_expr_to_str (expr); + test_log ("<%s \"%s\">", indent, debug_str, expr_str); + const_tree type = TREE_TYPE (expr); + if (TREE_CODE (type) == ARRAY_TYPE) + { + const_tree domain = TYPE_DOMAIN (type); + const_tree min = TYPE_MIN_VALUE (domain); + const_tree max = TYPE_MAX_VALUE (domain); + int _min = tree_to_uhwi (min); + int _max = tree_to_uhwi (max); + test_log ("< domain = (%d,%d)>", indent, _min, _max); + } + test_log ("< type = %s>", indent, get_type_name (type)); +} + +static tree +const_to_tree (const_tree ctree) +{ + return (tree) ctree; +} + +static void +substitute_type (tree expr, const_tree new_type) +{ + gcc_assert(expr); + gcc_assert(new_type); + const_tree old_type = TREE_TYPE(expr); + TREE_TYPE(expr) = const_to_tree(new_type); +} + +static void +substitute_type (tree expr, const_tree* new_type_ptr) +{ + gcc_assert(new_type_ptr); + substitute_type(expr, *new_type_ptr); +} + +// This function should be hidden +static const_tree +get_base_type_from_ptr_or_arr_var (const_tree var, + unsigned int &indirection_level) +{ + tree ptr_or_array = TREE_TYPE (var); + const bool is_array = TREE_CODE (ptr_or_array) == ARRAY_TYPE; + const bool is_ptr = TREE_CODE (ptr_or_array) == POINTER_TYPE; + const bool is_array_or_ptr = is_array || is_ptr; + gcc_assert (is_array_or_ptr); + const_tree retval + = get_base_type_from_ptr_or_arr_type (ptr_or_array, indirection_level); + return retval; +} + +static const_tree __attribute__((unused)) +get_base_type_from_array_var (const_tree var, unsigned int &indirection_level) +{ + gcc_assert (TREE_CODE (var) != ARRAY_TYPE); + tree array_type = TREE_TYPE (var); + gcc_assert (TREE_CODE (array_type) == ARRAY_TYPE); + const_tree retval + = get_base_type_from_ptr_or_arr_var (array_type, indirection_level); + return retval; +} + +static const_tree +get_base_type_from_pointer_var (const_tree var, unsigned int &indirection_level) +{ + gcc_assert (TREE_CODE (var) != POINTER_TYPE); + tree pointer_type = TREE_TYPE (var); + gcc_assert (TREE_CODE (pointer_type) == POINTER_TYPE); + const_tree retval + = get_base_type_from_ptr_or_arr_type (pointer_type, indirection_level); + return retval; +} + +static const_tree __attribute__((unused)) +get_base_type_from_pointer_var (const_tree var) +{ + gcc_assert (TREE_CODE (var) != POINTER_TYPE); + unsigned int indirection_level; + return get_base_type_from_pointer_var (var, indirection_level); +} + +// INFO: cannot change +// tree expr to const_tree expr +static void +log_expr_epilogue (const int indent, const char *debug_str, tree expr) +{ + const char *expr_str = print_generic_expr_to_str (expr); + const_tree type = TREE_TYPE (expr); + if (TREE_CODE (type) == ARRAY_TYPE) + { + const_tree domain = TYPE_DOMAIN (type); + const_tree min = TYPE_MIN_VALUE (domain); + const_tree max = TYPE_MAX_VALUE (domain); + int _min = tree_to_uhwi (min); + int _max = tree_to_uhwi (max); + test_log ("< domain = (%d,%d)>", indent, _min, _max); + } + test_log ("< type = %s>", indent, get_type_name (type)); + test_log ("</%s \"%s\">", indent, debug_str, expr_str); +} + + + +static const char * +make_base_name_based_on_old (const char *old_name) +{ + gcc_assert (old_name); + char *ptr; + static const char *suffix = ".reorg"; + int new_size = strlen (old_name) + strlen (suffix); + int retval = asprintf (&ptr, "%s%s", old_name, suffix); + gcc_assert (retval == new_size); + return ptr; +} + +static const char * +make_base_name_based_on_old (const_tree base_type) +{ + enum tree_code base_code = TREE_CODE (base_type); + const bool is_pointer = POINTER_TYPE == base_code; + const bool is_array = ARRAY_TYPE == base_code; + const bool prohibited = is_pointer || is_array; + // This means that this is only for base types + // and structs. + // I.e. we don't want to generate names which have + //[][][] at the end or **** + gcc_assert (!prohibited); + const char *base_type_name = get_type_name (base_type); + return make_base_name_based_on_old (base_type_name); +} + +static const char * +make_record_name_based_on_old (const char *old_record_name) +{ + return make_base_name_based_on_old (old_record_name); +} + +static const char * +make_array_name_based_on_old (const_tree array) +{ + enum tree_code code = TREE_CODE (array); + gcc_assert (ARRAY_TYPE == code); + unsigned int indirection_level; + const_tree base_type + = get_base_type_from_array_type (array, indirection_level); + const char *reorged_base_type_name = make_base_name_based_on_old (base_type); + const char *suffix = make_array_postfix (indirection_level); + return make_pointer_or_array_name (reorged_base_type_name, suffix); +} + +static const char * +make_pointer_name_based_on_old (const_tree pointer) +{ + enum tree_code code = TREE_CODE (pointer); + gcc_assert (POINTER_TYPE == code); + unsigned int indirection_level; + const_tree base_type + = get_base_type_from_pointer_type (pointer, indirection_level); + const char *reorged_base_type_name = make_base_name_based_on_old (base_type); + const char *suffix = make_pointer_postfix (indirection_level); + return make_pointer_or_array_name (reorged_base_type_name, suffix); +} + +static const char * +make_record_name_based_on_old (const_tree record) +{ + enum tree_code code = TREE_CODE (record); + gcc_assert (RECORD_TYPE == code); + const char *old_name = get_type_name (record); + return make_record_name_based_on_old (old_name); +} + + +typedef hash_set<const_tree> field_set; +typedef hash_set<const_tree> tree_set; +struct delete_info { const_tree old_record; const_tree new_record; field_set* deleted_fields;}; +typedef struct delete_info delete_info_t; +typedef hash_map<const_tree, delete_info_t> t_map; + +static bool +rewrite_addr_expr_def (tree expr, t_map &type_map, + const int indent); +static bool +rewrite_addr_expr (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_addr_expr", expr); + bool retval = rewrite_addr_expr_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_addr_expr", expr); + return retval; +} + +static bool +rewrite_array_ref_def (tree expr, t_map &type_map, + const int indent); + +static bool +rewrite_array_ref (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_array_ref", expr); + bool retval = rewrite_array_ref_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_array_ref", expr); + return retval; +} + +static bool +rewrite_bin_expr_default_def (tree expr, + t_map &type_map, + const int indent); +static bool +rewrite_bin_expr_default (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_bin_expr", expr); + bool retval = rewrite_bin_expr_default_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_bin_expr", expr); + return retval; +} + +static bool +rewrite_constructor_def (tree expr, t_map &type_map, + const int indent); +static bool +rewrite_constructor (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_constructor", expr); + bool retval = rewrite_constructor_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_constructor", expr); + return retval; +} + +static bool +rewrite_field_decl_def (tree expr, t_map &type_map, + const int indent); +static bool +rewrite_field_decl (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_field_decl", expr); + bool retval = rewrite_field_decl_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_field_decl", expr); + return retval; +} + +static bool +rewrite_integer_cst_def (tree expr, t_map &type_map, + const int indent); +static bool +rewrite_integer_cst (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_integer_cst", expr); + bool retval = rewrite_integer_cst_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_integer_cst", expr); + return retval; +} + +static bool +rewrite_function_decl_def (tree expr, + t_map &type_map, + const int indent); +static bool +rewrite_function_decl (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_function_decl", expr); + bool retval = rewrite_function_decl_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_function_decl", expr); + return retval; +} + +static bool +rewrite_component_ref (tree expr, t_map &type_map, + const int indent); +static bool +rewrite_component_ref_def (tree expr, + t_map &type_map, + const int indent); +static bool +rewrite_component_ref (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_component_ref", expr); + bool retval = rewrite_component_ref_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_component_ref", expr); + return retval; +} + +static bool +rewrite_expr (tree expr, t_map &type_map, + const int indent); +static bool +rewrite_expr_def (tree expr, t_map &type_map, + const int indent); +static bool +rewrite_expr (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_expr", expr); + bool retval = rewrite_expr_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_expr", expr); + return retval; +} + +static bool +rewrite_mem_ref_def (tree expr, t_map &type_map, + const int indent); + +static bool +rewrite_mem_ref (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_mem_ref", expr); + bool retval = rewrite_mem_ref_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_mem_ref", expr); + return retval; +} + + +static bool +rewrite_ssa_name_def (tree expr, t_map &type_map, + const int indent); +static bool +rewrite_ssa_name (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_ssa_name", expr); + bool retval = rewrite_ssa_name_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_ssa_name", expr); + return retval; +} + +static bool +rewrite_var_decl_def (tree expr, t_map &type_map, + const int indent); +static bool +rewrite_var_decl (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_var_decl", expr); + bool retval = rewrite_var_decl_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_var_decl", expr); + return retval; +} + +static bool +rewrite_expr_def (tree expr, t_map &type_map, + const int indent) +{ + bool retval = false; + switch (TREE_CODE (expr)) + { + case ADDR_EXPR: + retval = rewrite_addr_expr (expr, type_map, indent); + break; + case ARRAY_REF: + retval = rewrite_array_ref (expr, type_map, indent); + break; + case COMPONENT_REF: + retval = rewrite_component_ref (expr, type_map, indent); + break; + case CONSTRUCTOR: + retval = rewrite_constructor (expr, type_map, indent); + break; + case FIELD_DECL: + retval = rewrite_field_decl (expr, type_map, indent); + break; + case FUNCTION_DECL: + retval = rewrite_function_decl (expr, type_map, indent); + break; + case INTEGER_CST: + retval = rewrite_integer_cst (expr, type_map, indent); + break; + case MEM_REF: + retval = rewrite_mem_ref (expr, type_map, indent); + break; + case MINUS_EXPR: + retval = rewrite_bin_expr_default (expr, type_map, indent); + break; + case MULT_EXPR: + retval = rewrite_bin_expr_default (expr, type_map, indent); + break; + case POINTER_DIFF_EXPR: + // not needed + break; + case POINTER_PLUS_EXPR: + // not needed + break; + case PLUS_EXPR: + // not needed + break; + case SSA_NAME: + retval = rewrite_ssa_name (expr, type_map, indent); + break; + case VAR_DECL: + retval = rewrite_var_decl (expr, type_map, indent); + break; + default: + { + //TODO: can we put an unreachable here? + enum tree_code code = TREE_CODE (expr); + test_log ("code = %s", indent, get_tree_code_name (code)); + delete_info_t *delete_info = type_map.get (TREE_TYPE(expr)); + const_tree *new_type_ptr = delete_info ? &(delete_info->new_record) : NULL; + if (!new_type_ptr) + return false; + retval = true; + substitute_type(expr, new_type_ptr); + } + break; + } + + return retval; +} + +inline static void +test_print_generic_decl (tree decl) +{ + if (!dump_file) + return; + print_generic_decl (dump_file, decl, TDF_DETAILS); +} + +inline static void +test_print_gimple_stmt (gimple *stmt) +{ + if (!dump_file) + return; + print_gimple_stmt (dump_file, stmt, 0, TDF_NONE); +} + +inline static void +print_function (cgraph_node *cnode) +{ + if (!dump_file) + return; + gcc_assert (cnode); + cnode->get_untransformed_body (); + dump_function_to_file (cnode->decl, dump_file, TDF_NONE); +} + +static unsigned +get_field_offset (const_tree field) +{ + gcc_assert (TREE_CODE (field) == FIELD_DECL); + tree cst = byte_position (field); + unsigned offset = tree_to_uhwi (cst); + return offset; +} + + +static const_tree +get_field_with_offset_unsafe (const_tree record, const unsigned offset) +{ + gcc_assert (record); + gcc_assert (TREE_CODE (record) == RECORD_TYPE); + for (tree field = TYPE_FIELDS (record); field; field = DECL_CHAIN (field)) + { + unsigned field_offset = get_field_offset (field); + if (field_offset == offset) + return field; + } + + return NULL; +} + +static const_tree __attribute__((unused)) +get_field_with_offset (const_tree record, const unsigned offset) +{ + const_tree retval = get_field_with_offset_unsafe (record, offset); + gcc_assert (retval != NULL); + return retval; +} + +static const_tree +get_field_with_name_unsafe (const_tree record, const char *identifier) +{ + gcc_assert (record); + gcc_assert (TREE_CODE (record) == RECORD_TYPE); + for (tree field = TYPE_FIELDS (record); field; field = DECL_CHAIN (field)) + { + const char *field_identifier = get_field_name (field); + const bool is_same_name = strcmp (field_identifier, identifier) == 0; + if (!is_same_name) + continue; + + gcc_assert (TREE_CODE (field) == FIELD_DECL); + return field; + } + return NULL; +} + +static const_tree +get_field_with_name (const_tree record, const char *identifier) +{ + const_tree retval = get_field_with_name_unsafe (record, identifier); + gcc_assert (retval != NULL); + return retval; +} + +static bool +filter_out_boring_type (const_tree type, hash_set<const_tree> &map); + +inline static void +is_record_p(const_tree record) +{ + gcc_assert(record); + const enum tree_code code = TREE_CODE(record); + const bool is_record = RECORD_TYPE == code; + gcc_assert(is_record); +} + +struct cstrless { + bool operator()(const char* a, const char* b) { return strcmp(a, b) < 0; } +}; +std::set<const char*, cstrless> interesting_records; +std::set<const char*, cstrless> interesting_fields; +std::set<const char*, cstrless> record_field_pair_name; +std::set<const char*, cstrless> updated_functions; + +static bool +is_interesting_struct_escape_analysis(const_tree record) +{ + is_record_p(record); + const_tree main_variant = TYPE_MAIN_VARIANT(record); + is_record_p(main_variant); + const char* const ob_type_name = get_type_name(main_variant); + const char* blacklist [5] = { "__gcov_fn_info", "__gcov_ctr_info", "__gcov_fn_info", "__gcov_info", "indirect_call_tuple" }; + //const char* whitelist [4] = { "arc", "arc_t", "node", "node_t" }; + + for (int i = 0; i < 5; i++) + { + const bool is_blacklisted = strcmp(blacklist[i], ob_type_name) == 0; + if (is_blacklisted) return false; + } + + /* + bool is_whitelisted = false; + for (int i = 0; i < 4; i++) + { + is_whitelisted |= strcmp(whitelist[i], ob_type_name) == 0; + } + if (!is_whitelisted) return false; + */ + + + bool in_set = +#if __cplusplus > 201703L + interesting_records.contains(get_type_name(main_variant)) +#else + interesting_records.find(get_type_name(main_variant)) != interesting_records.end() +#endif + ; + + in_set |= +#if __cplusplus > 201703L + interesting_records.contains(get_type_name(record)) +#else + interesting_records.find(get_type_name(record)) != interesting_records.end() +#endif + ; + + + log("is interesting struct ? %s\n", in_set ? "true" : "false"); + + return in_set; + +} + +static bool is_interesting_struct_manual(const_tree); + +static bool +is_interesting_struct (const_tree record_1) +{ + const bool use_escape_analysis_info = !flag_ipa_typelist_struct && !flag_ipa_typelist_field; + return use_escape_analysis_info ? is_interesting_struct_escape_analysis(record_1) : is_interesting_struct_manual(record_1); +} + +static bool +is_interesting_pair (const char* pair_name) +{ + const bool use_escape_analysis_info = !flag_ipa_typelist_struct && !flag_ipa_typelist_field; + //TODO: If we are not in escape analysis we shouldn't be calling this function. + //We need to define a better interface for specifying record type and name to avoid + //having this problem. + if (!use_escape_analysis_info) return true; + const bool in_set = +#if __cplusplus > 201703L + record_field_pair_name.contains(pair_name); +#else + record_field_pair_name.find(pair_name) != record_field_pair_name.end() +#endif + ; + + log("is interesting pair? %s\n", in_set ? "true" : "false"); + + return in_set; +} + +static bool +is_interesting_struct_manual(const_tree record_1) +{ + is_record_p(record_1); + + const_tree record = TYPE_MAIN_VARIANT(record_1); + is_record_p(record); + + const char *record_name = get_type_name (record); + const int buffer_size = 1024; + char interesting_structs[buffer_size]; + static const int cli_length = strlen (flag_ipa_typelist_struct); + gcc_assert (cli_length < buffer_size); + strcpy (interesting_structs, flag_ipa_typelist_struct); + bool retval = false; + char *save_ptr; + char *token = strtok_r (interesting_structs, ",", &save_ptr); + while (token) + { + retval |= strcmp (record_name, token) == 0; + token = strtok_r (NULL, ",", &save_ptr); + if (retval) break; + } + + + return retval; +} + + +inline static void +is_field_decl_p(const_tree field) +{ + gcc_assert(field); + const enum tree_code code = TREE_CODE(field); + const bool is_field = FIELD_DECL == code; + gcc_assert(is_field); +} + +static bool +is_interesting_field_manual (const_tree field) +{ + log ("HERE HERE %s\n", get_type_name(field)); + is_field_decl_p(field); + + const char *field_name = get_field_name (field); + const int buffer_size = 1024; + char interesting_fields[buffer_size]; + static const int cli_length = strlen (flag_ipa_typelist_field); + gcc_assert (cli_length < buffer_size); + strcpy (interesting_fields, flag_ipa_typelist_field); + bool retval = false; + char *save_ptr; + char *token = strtok_r (interesting_fields, ",", &save_ptr); + while (token) + { + retval |= strcmp (field_name, token) == 0; + token = strtok_r (NULL, ",", &save_ptr); + } + return retval; +} + +static bool +is_interesting_field_escape_analysis(const_tree field) +{ + is_field_decl_p(field); + + const char* field_name = get_field_name(field); + /* + const char* whitelist [2] = { "nextin", "nextout" }; + + bool is_whitelisted = false; + for (int i = 0; i < 2; i++) + { + is_whitelisted |= strcmp(whitelist[i], field_name) == 0; + } + if (!is_whitelisted) return false; + */ + + const bool in_set = +#if __cplusplus > 201703L + interesting_fields.contains(field_name); +#else + interesting_fields.find(field_name) != interesting_fields.end() +#endif + ; + + log("is interesting field? %s\n", in_set ? "true" : "false"); + + return in_set; +} + +static bool +is_interesting_field (const_tree field) +{ + const bool use_escape_analysis_info = !flag_ipa_typelist_struct && !flag_ipa_typelist_field; + return use_escape_analysis_info ? is_interesting_field_escape_analysis(field) : is_interesting_field_manual(field); +} + +static bool +filter_out_boring_record (const_tree record, hash_set<const_tree> &map) +{ + test_log ("filter_out_boring_record", 0); + enum tree_code code = TREE_CODE (record); + gcc_assert (code == RECORD_TYPE); + + for (tree field = TYPE_FIELDS (record); field; field = DECL_CHAIN (field)) + { + tree field_type = TREE_TYPE (field); + enum tree_code field_code = TREE_CODE (field); + test_log ("filter_out_boring_field %s %s", 0, get_type_name (field_type), + get_tree_code_name (field_code)); + bool is_boring = filter_out_boring_type (field_type, map); + if (!is_boring) + return false; + } + + return !is_interesting_struct (record); +} + +static bool +filter_out_boring_pointer (const_tree pointer, hash_set<const_tree> &map) +{ + test_log ("filter_out_boring_pointer", 0); + enum tree_code code = TREE_CODE (pointer); + gcc_assert (code == POINTER_TYPE); + const_tree base_type = get_base_type_from_pointer_type (pointer); + return filter_out_boring_type (base_type, map); +} + +static bool +filter_out_boring_array (const_tree array, hash_set<const_tree> &map) +{ + enum tree_code code = TREE_CODE (array); + gcc_assert (code == ARRAY_TYPE); + const_tree base_type = get_base_type_from_array_type (array); + return filter_out_boring_type (base_type, map); +} + +static bool +filter_out_boring_reference (const_tree ref, hash_set<const_tree> &map) +{ + gcc_unreachable(); + enum tree_code code = TREE_CODE (ref); + gcc_assert (code == REFERENCE_TYPE); + test_log ("in filter out boring reference", 0); + bool retval = filter_out_boring_type (TREE_TYPE (ref), map); + if (retval) + { + test_log ("we are getting a reference", 0); + } + return retval; +} + +static bool +filter_out_boring_type (const_tree type, hash_set<const_tree> &map) +{ + if (!type) + { + test_log ("filter_out_boring_type (NULL)", 0); + return true; + } + + // maybe something like, if the type is + // equivalent to one of the already + // interesting types, then it should + // also be interesting..? + // But then, imagine this happens out of order... + // We get type A which is non-interesting + // type A is equivalent to B + // then we receive type B which is interesting + // this approach would fail... + + enum tree_code code = TREE_CODE (type); + bool retval = true; + switch (code) + { + case ARRAY_TYPE: + retval = filter_out_boring_array (type, map); + break; + case POINTER_TYPE: + retval = filter_out_boring_pointer (type, map); + break; + case RECORD_TYPE: + retval = filter_out_boring_record (type, map); + break; + case REFERENCE_TYPE: + retval = filter_out_boring_reference (type, map); + break; + default: + { + test_log ("default in tree code %s", 0, get_tree_code_name (code)); + retval = true; + } + break; + } + + if (!retval) + { + test_log ("putting %s", 0, get_type_name (type)); + map.add (type); + } + return retval; +} + +static bool +filter_var_decls (tree var_decl, hash_set<const_tree> &type_map) +{ + gcc_assert (var_decl); + gcc_assert (TREE_CODE (var_decl) == VAR_DECL); + tree type = TREE_TYPE (var_decl); + return filter_out_boring_type (type, type_map); +} + +static bool +filter_parm_decls (tree parm_decl, hash_set<const_tree> &type_map) +{ + gcc_assert (parm_decl); + gcc_assert (TREE_CODE (parm_decl) == PARM_DECL); + tree type = TREE_TYPE (parm_decl); + return filter_out_boring_type (type, type_map); +} + +static void +collect_parm_declarations (cgraph_node *cnode, + bool (*filter) (tree, hash_set<const_tree> &), + hash_set<const_tree> &decl_map) +{ + gcc_assert (cnode); + // TODO: Do we need to get_untransformed_body_here? + for (tree parm = DECL_ARGUMENTS (cnode->decl); parm; parm = DECL_CHAIN (parm)) + { + bool filter_out = (*filter) (parm, decl_map); + if (filter_out) + continue; + + tree type = TREE_TYPE (parm); + bool is_inside = decl_map.contains (type); + // We already have this type. + if (is_inside) + continue; + + //const char *type_identifier = get_type_name (type); + //test_log ("collecting,%s", 0, type_identifier); + decl_map.add (type); + } +} + +static void +collect_pointer_plus (tree lhs, tree rhs1, tree rhs2, + hash_set<const_tree> &decl_map) +{ + tree lhs_type = lhs ? TREE_TYPE (lhs) : NULL; + bool is_lhs_boring + = lhs_type ? filter_out_boring_type (lhs_type, decl_map) : true; + tree rhs1_type = rhs1 ? TREE_TYPE (rhs1) : NULL; + bool is_rhs1_boring + = rhs1_type ? filter_out_boring_type (rhs1_type, decl_map) : true; + tree rhs2_type = rhs2 ? TREE_TYPE (rhs2) : NULL; + bool is_rhs2_boring + = rhs2_type ? filter_out_boring_type (rhs2_type, decl_map) : true; + + if (!is_lhs_boring) + { + test_log ("putting lhs %s", 0, get_type_name (lhs_type)); + decl_map.add (lhs_type); + } + if (!is_rhs1_boring) + { + test_log ("putting rhs1 %s", 0, get_type_name (rhs1_type)); + decl_map.add (rhs1_type); + } + if (!is_rhs2_boring) + { + test_log ("putting rhs2 %s", 0, get_type_name (rhs2_type)); + decl_map.add (rhs2_type); + } +} + +static void +collect_assign_rhs (gimple *stmt, hash_set<const_tree> &decl_map) +{ + enum tree_code code = gimple_expr_code (stmt); + switch (code) + { + case POINTER_PLUS_EXPR: + case POINTER_DIFF_EXPR: + case COMPONENT_REF: + { + tree rhs2 = gimple_assign_rhs2 (stmt); + tree rhs1 = gimple_assign_rhs1 (stmt); + tree lhs = gimple_assign_lhs (stmt); + collect_pointer_plus (lhs, rhs1, rhs2, decl_map); + } + break; + default: + { + //print_gimple_stmt (dump_file, stmt, 0); + test_log ("DEFAULT HERE: %s", 0, get_tree_code_name (code)); + } + break; + } +} + +static void +collect_assign (gimple *stmt, hash_set<const_tree> &decl_map) +{ + gcc_assert (stmt); + collect_assign_rhs (stmt, decl_map); +} + +static void +collect_stmt (gimple *stmt, hash_set<const_tree> &decl_map) +{ + gcc_assert (stmt); + const enum gimple_code code = gimple_code (stmt); + switch (code) + { + case GIMPLE_ASSIGN: + collect_assign (stmt, decl_map); + break; + default: + break; + } + return; +} + +static void +collect_basic_block (basic_block bb, hash_set<const_tree> &decl_map) +{ + gcc_assert (bb); + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + collect_stmt (stmt, decl_map); + } +} + +static void +collect_function_body (cgraph_node *cnode, hash_set<const_tree> &decl_map) +{ + gcc_assert (cnode); + cnode->get_untransformed_body (); + basic_block bb = NULL; + function *func = DECL_STRUCT_FUNCTION (cnode->decl); + push_cfun (func); + FOR_EACH_BB_FN (bb, func) + { + collect_basic_block (bb, decl_map); + } + pop_cfun (); + print_function (cnode); +} + +static void +collect_local_declarations (cgraph_node *cnode, + bool (*filter) (tree, hash_set<const_tree> &), + hash_set<const_tree> &decl_map) +{ + gcc_assert (cnode); + int i = 0; + function *func = DECL_STRUCT_FUNCTION (cnode->decl); + cnode->get_untransformed_body (); + tree var_decl = NULL; + FOR_EACH_LOCAL_DECL (func, i, var_decl) + { + bool filter_out = (*filter) (var_decl, decl_map); + if (filter_out) + continue; + + tree type = TREE_TYPE (var_decl); + //const char *type_identifier = get_type_name (type); + //test_log ("collecting,%s", 0, type_identifier); + decl_map.add (type); + } +} + +static void +collect_global_declaration (varpool_node *vnode, + bool (*filter) (tree, hash_set<const_tree> &), + hash_set<const_tree> &decl_map) +{ + test_log ("collect global declarations", 0); + int i; + struct ipa_ref *ref; + + /* Only IPA_REF_STORE and IPA_REF_LOAD left. */ + for (i = 0; vnode->iterate_referring (i, ref); i++) + { + bool filter_out = (*filter) (vnode->decl, decl_map); + if (filter_out) + { + test_log ("filter out", 0); + continue; + } + + tree type = TREE_TYPE (vnode->decl); + //const char *type_identifier = get_type_name (type); + //test_log ("collecting,%s", 0, type_identifier); + decl_map.add (type); + } +} + +static void +collect_global_declarations (bool (*filter) (tree, + hash_set<const_tree> &), + hash_set<const_tree> &decl_map) +{ + varpool_node *vnode; + FOR_EACH_VARIABLE (vnode) + { + collect_global_declaration (vnode, filter, decl_map); + } +} + +static void +collect_orig_structs (hash_set<const_tree> &type_map) +{ + collect_global_declarations (&filter_var_decls, type_map); + + cgraph_node *cnode = NULL; + FOR_EACH_DEFINED_FUNCTION (cnode) + { + print_function (cnode); + collect_parm_declarations (cnode, &filter_parm_decls, type_map); + collect_function_body (cnode, type_map); + collect_local_declarations (cnode, &filter_var_decls, type_map); + } +} + +static const_tree +make_new_type_based_on_old (const_tree old, + t_map *mod_type_map); + +static const_tree +get_new_type_main_variant (const_tree old, t_map *mod_type_map) +{ + const_tree old_type_main_variant = TYPE_MAIN_VARIANT(old); + gcc_assert(old != old_type_main_variant); + delete_info_t *ptr = mod_type_map->get (TYPE_MAIN_VARIANT(old_type_main_variant)); + if (ptr) { + const_tree retval = TYPE_MAIN_VARIANT(ptr->new_record); + return ptr->new_record; + } + + const_tree new_main_variant_record = make_new_type_based_on_old(old_type_main_variant, mod_type_map); + return new_main_variant_record; +} + +static const_tree +make_new_record_based_on_typedef (const_tree _typedef, t_map *mod_type_map) +{ + log("make_new_record_based_on_typedef\n"); + bool is_typedef = _typedef != TYPE_MAIN_VARIANT(_typedef); + gcc_assert(is_typedef); + tree new_main_variant = (tree) get_new_type_main_variant(_typedef, mod_type_map); + tree new_type = build_type_variant(new_main_variant, TYPE_READONLY(_typedef), TYPE_VOLATILE(_typedef)); + const char *new_name = make_record_name_based_on_old (_typedef); + tree new_record_name = get_identifier (new_name); + TYPE_NAME (new_type) = new_record_name; + layout_type(new_type); + + delete_info_t* info = mod_type_map->get(TYPE_MAIN_VARIANT(_typedef)); + gcc_assert(info); + delete_info_t new_info; + new_info.old_record = _typedef; + new_info.new_record = new_type; + new_info.deleted_fields = info->deleted_fields; + mod_type_map->put (_typedef, new_info); + return new_type; +} + +/* + * This method will copy the parameter + * and delete the field named "delete_me" + * int the copy. The offsets will be adjusted + * to account for the deleted parameter + */ +static const_tree +make_new_record_based_on_old (const_tree old, + t_map *mod_type_map) +{ + log("make_new_record_based_on_old\n %s", get_type_name(old)); + delete_info_t* info = mod_type_map->get(old); + const_tree *new_record_ptr = info ? &(info->new_record) : NULL; + + gcc_assert (!new_record_ptr); + + bool is_typedef = old != TYPE_MAIN_VARIANT(old); + if (is_typedef) { return make_new_record_based_on_typedef(old, mod_type_map); } + + bool will_definitely_change = is_interesting_struct (old); + + tree new_record = make_node (RECORD_TYPE); + const char *new_name = make_record_name_based_on_old (old); + tree new_record_name = get_identifier (new_name); + TYPE_NAME (new_record) = new_record_name; + hash_set<const_tree> *deleted_fields = new hash_set<const_tree> (); + + tree prev_new_field = NULL; + for (tree old_field = TYPE_FIELDS (old); old_field; + old_field = DECL_CHAIN (old_field)) + { + + const bool are_field_names_equal = is_interesting_field (old_field); + log("am i deleting field %s ? %s\n", get_field_name(old_field), will_definitely_change && are_field_names_equal ? "true" : "false"); + if (will_definitely_change && are_field_names_equal) + { + char *buffer; + char *buffer2; + asprintf(&buffer, "%s.%s", get_type_name(old), get_field_name(old_field)); + asprintf(&buffer2, "%s.%s", get_type_name(TYPE_MAIN_VARIANT(old)), get_field_name(old_field)); + log("buffer %s\n", buffer); + log("buffer2 %s\n", buffer2); + if (is_interesting_pair(buffer) || is_interesting_pair(buffer2)) + { + deleted_fields->add(old_field); + continue; + } + } + + tree new_field = make_node (FIELD_DECL); + DECL_NAME (new_field) = DECL_NAME (old_field); + tree old_field_type = TREE_TYPE (old_field); + const_tree new_field_type + = make_new_type_based_on_old (TREE_TYPE (old_field), mod_type_map); + test_log ("inside making new field, %s %s", 0, + get_type_name (old_field_type), get_type_name (new_field_type)); + const_tree maybe_new_field + = new_field_type == old_field ? old_field : new_field_type; + substitute_type(new_field, maybe_new_field); + DECL_SOURCE_LOCATION (new_field) = DECL_SOURCE_LOCATION (old_field); + SET_DECL_ALIGN (new_field, DECL_ALIGN (old_field)); + DECL_USER_ALIGN (new_field) = DECL_ALIGN (old_field); + TREE_ADDRESSABLE (new_field) = TREE_ADDRESSABLE (old_field); + DECL_NONADDRESSABLE_P (new_field) = !TREE_ADDRESSABLE (old_field); + TREE_THIS_VOLATILE (new_field) = TREE_THIS_VOLATILE (old_field); + // DECL_FIELD_OFFSET and DECL_FIELD_BIT_OFFSET + // are actually set by layout_type. + const bool first_new_field = !prev_new_field; + if (first_new_field) + { + TYPE_FIELDS (new_record) = new_field; + } + else + { + DECL_CHAIN (prev_new_field) = new_field; + } + + prev_new_field = new_field; + } + + layout_type (new_record); + tree new_type = build_type_variant(new_record, TYPE_READONLY(old), TYPE_VOLATILE(old)); + layout_type (new_type); + + + for (tree new_field = TYPE_FIELDS (new_record); new_field; + new_field = DECL_CHAIN (new_field)) + { + const char *new_field_name = get_field_name (new_field); + int new_offset = get_field_offset (new_field); + test_log ("offset,%s,%s,%d", 0, new_name, new_field_name, new_offset); + } + + delete_info_t new_info; + new_info.old_record = old; + new_info.new_record = new_record; + new_info.deleted_fields = deleted_fields; + mod_type_map->put (old, new_info); + return new_record; +} + +const_tree +make_new_array_based_on_old (const_tree old_type, + t_map *mod_type_map) +{ + gcc_assert (TREE_CODE (old_type) == ARRAY_TYPE); + delete_info_t *delete_info_1 = mod_type_map->get(old_type); + const_tree *new_type_ptr = delete_info_1 ? &(delete_info_1->new_record) : NULL; + gcc_assert (!new_type_ptr); + + const char *ptr = make_array_name_based_on_old (old_type); + tree new_array = make_node (ARRAY_TYPE); + TYPE_NAME (new_array) = get_identifier (ptr); + TYPE_DOMAIN (new_array) = TYPE_DOMAIN (old_type); + tree old_array_type = TREE_TYPE (old_type); + const_tree possibly_new + = make_new_type_based_on_old (old_array_type, mod_type_map); + if (possibly_new == old_array_type) + return old_type; + substitute_type(new_array, possibly_new); + layout_type (new_array); + + delete_info *ptr_2 = mod_type_map->get (old_type); + if (ptr_2) + return ptr_2->new_record; + + //HERE HERE + // struct delete_info { const_tree old_record; const_tree new_record; field_set* deleted_fields;}; + delete_info_t delete_info; + delete_info.old_record = old_type; + delete_info.new_record = new_array; + delete_info.deleted_fields = NULL; + mod_type_map->put (old_type, delete_info); + + return new_array; +} + +static const_tree +make_new_pointer_based_on_old (const_tree old, + t_map *mod_type_map) +{ + test_log ("make_new_pointer_based_on_old %s", 0, get_type_name (old)); + delete_info_t *delete_info_1 = mod_type_map->get(old); + const_tree *already_computed = delete_info_1 ? &(delete_info_1->new_record) : NULL; + gcc_assert (!already_computed); + + gcc_assert (TREE_CODE (old) == POINTER_TYPE); + + tree new_pointer = make_node (POINTER_TYPE); + const char *ptr = make_pointer_name_based_on_old (old); + + TYPE_NAME (new_pointer) = get_identifier (ptr); + SET_TYPE_MODE (new_pointer, TYPE_MODE (old)); + tree old_type = TREE_TYPE (old); + gcc_assert (old_type); + const_tree new_type = make_new_type_based_on_old (old_type, mod_type_map); + // We do not need it... + if (new_type == old_type) + return old; + + substitute_type(new_pointer, new_type); + layout_type (new_pointer); + + delete_info_t *ptr_2 = mod_type_map->get (old); + if (ptr_2) + return ptr_2->new_record; + + //HERE HERE + // struct delete_info { const_tree old_record; const_tree new_record; field_set* deleted_fields;}; + delete_info_t delete_info; + delete_info.old_record = old; + delete_info.new_record = new_pointer; + delete_info.deleted_fields = NULL; + mod_type_map->put (old, delete_info); + test_log ("putting %s %s", 0, get_type_name (old), + get_type_name (new_pointer)); + return new_pointer; +} + +static const_tree +make_new_type_based_on_old (const_tree old, + t_map *mod_type_map) +{ + if (!old) + return old; + + gcc_assert (old); + delete_info_t *delete_info = mod_type_map->get (old); + const_tree *new_base_ptr = delete_info ? &(delete_info->new_record) : NULL; + if (new_base_ptr) + return *new_base_ptr; + + switch (TREE_CODE (old)) + { + case ARRAY_TYPE: + return make_new_array_based_on_old (old, mod_type_map); + break; + case POINTER_TYPE: + return make_new_pointer_based_on_old (old, mod_type_map); + break; + case RECORD_TYPE: + return make_new_record_based_on_old (old, mod_type_map); + break; + default: + return old; + break; + } + gcc_unreachable (); + return NULL; +} + +// Non-null values for type_map +// will be the modified tree. +bool +compute_modified_structs_internal ( + const_tree const &old_type, + t_map *mod_type_map) +{ + delete_info_t *ptr = mod_type_map->get (old_type); + // already in map + if (ptr) + return true; + + const char *identifier = get_type_name (old_type); + test_log ("modifying,%s", 0, identifier); + + const_tree new_record = make_new_type_based_on_old (old_type, mod_type_map); + // We might have modified the mod_type_map inside make_new_type_based_on_old + // and therefore we no longer have the need to store this there again... + delete_info_t *ptr_2 = mod_type_map->get (old_type); + if (ptr_2) + return true; + + test_log ("new_name,%s", 0, get_type_name (new_record)); + //HERE HERE + // struct delete_info { const_tree old_record; const_tree new_record; field_set* deleted_fields;}; + delete_info_t delete_info; + delete_info.new_record = new_record; + delete_info.old_record = old_type; + delete_info.deleted_fields = NULL; + mod_type_map->put (old_type, delete_info); + return true; +} + +static void +compute_modified_structs (hash_set<const_tree> &type_map, + t_map &mod_type_map) +{ + type_map.traverse< t_map *, + compute_modified_structs_internal> (&mod_type_map); +} + +static bool +rewrite_constructor_def (tree expr, t_map &type_map, + const int indent) +{ + // These nodes represent the brace initializers + // for a structure or an array. They contain + // a sequence of component values made out of a vector + // of constructor_elt, which is a (INDEX, VALUE) pair. + tree old_rhs_type = TREE_TYPE (expr); + delete_info_t *delete_info = type_map.get (old_rhs_type); + const_tree *new_rhs_type_ptr = delete_info ? &(delete_info->new_record) : NULL; + if (!new_rhs_type_ptr) + return false; + + substitute_type(expr, new_rhs_type_ptr); + + tree new_type = TREE_TYPE (expr); + // If the TREE_TYPE of the CONSTRUCTOR is a RECORD_TYPE + // UNION_TYPE or QUAL_UNION_TYPE, then the INDEX of each node + // in the sequence will be a FIELD_DECL and the VALUE will be the + // expression used to initialize that field. + // If TREE_TYPE of the CONSTRUCTOR is an ARRAY_TYPE, then + // the INDEX of each node in the sequence will be an INTEGER_CST + // or a RANGE_EXPR of two INTEGER_CSTs. + if (TREE_CLOBBER_P (expr)) + return true; + + enum tree_code code = TREE_CODE (new_type); + bool is_record = code == RECORD_TYPE; + bool is_union = code == UNION_TYPE; + bool is_qual = code == QUAL_UNION_TYPE; + bool is_array = code == ARRAY_TYPE; + bool is_interesting = is_record || is_union || is_qual || is_array; + if (!is_interesting) + return true; + + // We have not tested this with other types... + gcc_assert (is_record); + // FIXME, we would need some time to rewrite the + // API to allow gimple_stmt_iterator be passed + // around all rewrite expressions... + // or something like that. + // Since not a lot of code uses this syntax, + // let's avoid it for now. But make a note + // we still need to fix this. + gcc_unreachable (); + + unsigned i; + tree val; + FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (expr), i, val) + { + rewrite_expr (val, type_map, indent + 4); + } + + return true; +} + +static bool +rewrite_field_decl_def (tree expr, t_map &type_map, + __attribute__((unused)) const int indent) +{ + tree old_rhs_type = TREE_TYPE (expr); + delete_info_t *delete_info = type_map.get(old_rhs_type); + const_tree *new_rhs_type_ptr = delete_info ? &(delete_info->new_record) : NULL; + if (!new_rhs_type_ptr) + return false; + + substitute_type(expr, new_rhs_type_ptr); + gcc_assert (TREE_OPERAND (expr, 0) == NULL); + return true; +} + +static bool +rewrite_integer_cst_def (tree expr, t_map &type_map, + __attribute__((unused)) const int indent) +{ + tree old_rhs_type = TREE_TYPE (expr); + delete_info_t *delete_info = type_map.get (old_rhs_type); + const_tree *new_rhs_type_ptr = delete_info ? &(delete_info->new_record) : NULL; + if (!new_rhs_type_ptr) + return false; + + substitute_type(expr, new_rhs_type_ptr); + return true; +} + +static void +rewrite_function_parms (tree expr, t_map &type_map, + const int indent); +static void +rewrite_function_return_type (tree expr, + t_map &type_map, + const int indent); + +static bool +rewrite_function_decl_def (tree expr, + t_map &type_map, + const int indent) +{ + rewrite_function_parms (expr, type_map, indent + 4); + rewrite_function_return_type (expr, type_map, indent + 4); + return false; +} + +static bool +rewrite_component_ref_def_lhs (gimple_stmt_iterator &gsi, tree expr, t_map &type_map, const int indent) +{ + gcc_assert (expr); + tree expr_type = TREE_TYPE (expr); + gcc_assert (expr_type); + delete_info_t *delete_info1 = type_map.get (expr_type); + delete_info1 = delete_info1 ? delete_info1 : type_map.get (TYPE_MAIN_VARIANT(expr_type)); + const_tree *new_expr_type_ptr = delete_info1 ? &(delete_info1->new_record) : NULL; + + tree _struct = TREE_OPERAND (expr, 0); + gcc_assert (_struct); + tree _struct_type = TREE_TYPE (_struct); + + + gcc_assert (_struct_type); + const enum tree_code code = TREE_CODE(_struct_type); + const bool is_record = RECORD_TYPE == code; + //TODO: Handle unions better + switch (code) + { + case UNION_TYPE: return false; break; + case RECORD_TYPE: break; + default: gcc_unreachable(); break; + } + gcc_assert(is_record); + + delete_info_t *delete_info = type_map.get (_struct_type); + delete_info = delete_info ? delete_info : type_map.get (TYPE_MAIN_VARIANT(_struct_type)); + const_tree *new_struct_type_ptr = delete_info ? &(delete_info->new_record) : NULL; + + if (new_expr_type_ptr) + substitute_type(expr, new_expr_type_ptr); + + bool retval = rewrite_expr (_struct, type_map, indent); + + tree new_type + = new_struct_type_ptr ? const_to_tree(*new_struct_type_ptr) : _struct_type; + tree field_decl = TREE_OPERAND (expr, 1); + bool has_been_deleted = delete_info ? delete_info->deleted_fields->contains(field_decl) : false; + if (has_been_deleted) + { + const char *field_name = get_field_name (field_decl); + test_log("we are removing field %s from struct %s", 0, field_name, get_type_name(delete_info->old_record)); + return true; + } + + + const char *field_name = get_field_name (field_decl); + if (strcmp (field_name, "anonymous") == 0) + return false; + + + const_tree new_field = get_field_with_name (new_type, field_name); + // INFO: We need to rewrite the whole operand + // not just its type. + // INFO: When rewriting an operand we **must** + // be in -flto-partition=none + // INFO: setting the type and calling relayout_decl + // doesn't work. + // TODO: FIXME: In order to remove this assertion + // you need to use clones. + gcc_assert (in_lto_p && !flag_ltrans && !flag_wpa); + TREE_OPERAND (expr, 1) = const_to_tree(new_field); + int offset = get_field_offset (new_field); + test_log ("rewrite,field_offset,%s,%d", indent, field_name, offset); + gcc_assert (TREE_OPERAND (expr, 2) == NULL); + + return false; +} + +static bool +rewrite_component_ref_def (tree expr, + t_map &type_map, + const int indent) +{ + gcc_assert (expr); + tree expr_type = TREE_TYPE (expr); + gcc_assert (expr_type); + + delete_info_t *delete_info1 = type_map.get (expr_type); + const_tree *new_expr_type_ptr = delete_info1 ? &(delete_info1->new_record) : NULL; + if (new_expr_type_ptr) + substitute_type(expr, new_expr_type_ptr); + + tree _struct = TREE_OPERAND (expr, 0); + gcc_assert (_struct); + tree _struct_type = TREE_TYPE (_struct); + gcc_assert (_struct_type); + const enum tree_code code = TREE_CODE(_struct_type); + const bool is_record = RECORD_TYPE == code; + //TODO: Handle unions better + switch (code) + { + case UNION_TYPE: return false; break; + case RECORD_TYPE: break; + default: gcc_unreachable(); break; + } + gcc_assert(is_record); + delete_info_t *delete_info = type_map.get (_struct_type); + const_tree *new_struct_type_ptr = delete_info ? &(delete_info->new_record) : NULL; + + bool retval = rewrite_expr (_struct, type_map, indent); + + tree new_type + = new_struct_type_ptr ? const_to_tree(*new_struct_type_ptr) : _struct_type; + tree field_decl = TREE_OPERAND (expr, 1); + bool has_been_deleted = delete_info ? delete_info->deleted_fields->contains(field_decl) : false; + if (has_been_deleted) + { + gcc_unreachable(); + return false; + } + + const char *field_name = get_field_name (field_decl); + if (strcmp (field_name, "anonymous") == 0) + return retval; + + const_tree new_field = get_field_with_name (new_type, field_name); + // INFO: We need to rewrite the whole operand + // not just its type. + // INFO: When rewriting an operand we **must** + // be in -flto-partition=none + // INFO: setting the type and calling relayout_decl + // doesn't work. + // TODO: FIXME: In order to remove this assertion + // you need to use clones. + gcc_assert (in_lto_p && !flag_ltrans && !flag_wpa); + TREE_OPERAND (expr, 1) = const_to_tree(new_field); + int offset = get_field_offset (new_field); + test_log ("rewrite,field_offset,%s,%d", indent, field_name, offset); + gcc_assert (TREE_OPERAND (expr, 2) == NULL); + + return true; +} + +static bool +rewrite_addr_expr_def (tree expr, t_map &type_map, + const int indent) +{ + tree type = TREE_TYPE (expr); + gcc_assert (TREE_CODE (expr) == ADDR_EXPR); + delete_info_t *delete_info = type_map.get (type); + const_tree *new_type_ptr = delete_info ? &(delete_info->new_record) : NULL; + if (new_type_ptr) + substitute_type(expr, new_type_ptr); + tree op0 = TREE_OPERAND (expr, 0); + bool retval = rewrite_expr (op0, type_map, indent); + // There isn't an operand here, but we also can't + // TREE_OPERAND(expr, 1) + // Accessing this won't fail. + return retval; +} + +static bool +rewrite_var_decl_def (tree expr, __attribute__((unused)) t_map &type_map, + __attribute__((unused)) const int indent) +{ + gcc_assert (TREE_CODE (expr) == VAR_DECL); + return false; +} + +static bool +rewrite_mem_ref_def (tree expr, t_map &type_map, + const int indent) +{ + // Do we need to modify this value so that we op1 and calculate a constant + // value? + gcc_assert (expr); + gcc_assert (TREE_CODE (expr) == MEM_REF); + tree type = TREE_TYPE (expr); + + delete_info_t *delete_info = type_map.get (type); + delete_info = delete_info ? delete_info : type_map.get (TYPE_MAIN_VARIANT(type)); + const_tree *new_type_ptr = delete_info ? &(delete_info->new_record) : NULL; + if (new_type_ptr) + substitute_type(expr, new_type_ptr); + + tree op0 = TREE_OPERAND (expr, 0); + // The first operand is the pointer being dereferenced + bool retval = rewrite_expr (op0, type_map, indent); + // The second operand is a pointer constant. Its type specifying the type + // to be used for type-based alias analysis. + tree op1 = TREE_OPERAND (expr, 1); + rewrite_expr (op1, type_map, indent); + gcc_assert (TREE_CODE (op1) == INTEGER_CST); + int old_offset = tree_to_uhwi (op1); + if (old_offset == 0 || !new_type_ptr) + return retval; + + tree old_struct_size = TYPE_SIZE_UNIT (type); + int old_type_size_int = tree_to_shwi (old_struct_size); + test_log ("old_type = %s", 0, get_type_name (type)); + test_log ("old_type_size = %ld", 0, tree_to_shwi (old_struct_size)); + tree new_struct_size = TYPE_SIZE_UNIT (const_to_tree(*new_type_ptr)); + int new_type_size_int = tree_to_shwi (new_struct_size); + int multiple = old_offset / old_type_size_int; + int offset = multiple * new_type_size_int; + int remainder = old_offset % old_type_size_int; + // FIXME: Do I need a way of mapping old_offsets to new_offsets... + // This seems insufficient. + // I need to know at which offset the discontinuity happens so that + // I can account for that. + // for now, we can only guarantee this transformation if + // old_offset is multiple of old_type_size_int; + int new_offset = offset + remainder; + if (new_offset == old_offset) + return retval; + + tree new_offset_tree = build_int_cst (TREE_TYPE (op1), new_offset); + TREE_OPERAND (expr, 1) = new_offset_tree; + gcc_assert (old_offset % old_type_size_int == 0); + + // TREE_OPERAND(expr, 2) cannot be accessed. + return retval; +} + +static bool +rewrite_ssa_name_def (tree expr, t_map &type_map, + __attribute__((unused)) const int indent) +{ + tree type = TREE_TYPE (expr); + tree ssa_name_var = SSA_NAME_VAR (expr); + delete_info_t *delete_info = type_map.get (type); + const_tree *new_type_ptr = delete_info ? &(delete_info->new_record) : NULL; + tree new_type_2 = NULL; + if (new_type_ptr) + { + new_type_2 = build_type_variant(const_to_tree(*new_type_ptr), TYPE_READONLY(type), TYPE_VOLATILE(type)); + substitute_type(expr, new_type_2); + // TODO: I had some cases where ssa_name_var was not set. + // Not sure why... + // I need to investigate + // Could this be the variables that I create? + // If so, then the temporary variables should have SSA_NAMES + // and there is something else that I need to change + if (ssa_name_var) + { + substitute_type(ssa_name_var, new_type_2); + relayout_decl (ssa_name_var); + } + } + + return new_type_2; +} + +static bool +rewrite_bin_expr_default_def (tree expr, + t_map &type_map, + const int indent) +{ + gcc_assert (expr); + tree op0 = TREE_OPERAND (expr, 0); + tree op1 = TREE_OPERAND (expr, 1); + gcc_assert (op0 && op1); + + bool retval = false; + retval |= rewrite_expr (op0, type_map, indent); + retval |= rewrite_expr (op1, type_map, indent); + // Why is this unreachable? + gcc_unreachable (); + gcc_assert (TREE_OPERAND (expr, 2) == NULL); + + return retval; +} + + +static bool +rewrite_array_ref_def (tree expr, t_map &type_map, + const int indent) +{ + gcc_assert (expr); + gcc_assert (TREE_CODE (expr) == ARRAY_REF); + tree type = TREE_TYPE (expr); + //const_tree *up_ptr = type_map.get (type); + delete_info_t *delete_info = type_map.get (type); + const_tree *up_ptr = delete_info ? &(delete_info->new_record) : NULL; + if (up_ptr) + substitute_type(expr, up_ptr); + + rewrite_expr (TREE_OPERAND (expr, 0), type_map, indent + 4); + rewrite_expr (TREE_OPERAND (expr, 1), type_map, indent + 4); + gcc_assert (TREE_OPERAND (expr, 2) == NULL); + + return true; +} + +static void +rewrite_pointer_diff_def_rhs_variable_replace_constants_implementation ( + __attribute__((unused)) gimple_stmt_iterator &gsi, tree lhs, tree pointer, __attribute__((unused)) tree variable, + const_tree old_type) +{ + // We will need this later + tree old_type_size = TYPE_SIZE_UNIT (TREE_TYPE (old_type)); + int old_type_size_int = tree_to_shwi (old_type_size); + // lhs = op0 - op1 // <-- we are here + // ... SNIP ... + // var = lhs / [ex] old_struct_size // <-- we want to be here + // + // Let's explore the uses of lhs + + gimple *stmt; + imm_use_iterator iterator; + FOR_EACH_IMM_USE_STMT (stmt, iterator, lhs) + { + // stmt is a use of lhs + // gimple_expr_code is only valid for non-debug statements + bool is_debug = is_gimple_debug (stmt); + if (is_debug) + continue; + + test_log ("is not debug", 0); + enum tree_code code = gimple_expr_code (stmt); + bool is_exact_div = code == EXACT_DIV_EXPR; + if (!is_exact_div) + continue; + + test_log ("is exact div expr", 0); + tree divisor = gimple_op (stmt, 2); + enum tree_code divisor_code = TREE_CODE (divisor); + bool is_constant = divisor_code == INTEGER_CST; + if (!is_constant) + continue; + + test_log ("is constant", 0); + int divisor_int = tree_to_shwi (divisor); + test_log ("size %d %d", 0, divisor_int, old_type_size_int); + bool is_same_size = divisor_int == old_type_size_int; + if (!is_same_size) + continue; + + const_tree new_base_type = TREE_TYPE (TREE_TYPE (pointer)); + tree new_struct_size_tree_1 = TYPE_SIZE_UNIT (new_base_type); + int new_struct_size_int = tree_to_shwi (new_struct_size_tree_1); + tree new_struct_size_const + = build_int_cst (TREE_TYPE (divisor), new_struct_size_int); + gimple_set_op (stmt, 2, new_struct_size_const); + } +} + +static tree +rewrite_pointer_plus_def_rhs_variable_replace_constants_implementation ( + __attribute__((unused)) gimple_stmt_iterator &gsi, tree lhs, __attribute__((unused)) tree pointer, tree variable, + const_tree old_type) +{ + // We need to find the definition of the 3rd operand + // _1 = _0 * 72 + // ... SNIP ... + // _2 = _1 + CONSTANT; + // ... SNIP ... + // _3 = &array + _2; < -- this is where we are + gcc_assert (TREE_CODE (variable) == SSA_NAME); + gimple *def_for_variable = SSA_NAME_DEF_STMT (variable); + + // It is also possible that we are in a negation statement. + // Example: + // _2 = _1 * 72; + // ... SNIP ... + // _3 = -_2; < -- def_for_variable **might** be this stmt. + // ... SNIP ... + // _4 = &array + _3; + // Let's find out how many operands we have + unsigned num_operands = gimple_num_ops (def_for_variable); + test_log ("how many operands ? %d", 0, num_operands); + bool need_to_skip = num_operands == 2; + gimple *to_change = need_to_skip + ? SSA_NAME_DEF_STMT (gimple_op (def_for_variable, 1)) + : def_for_variable; + + // _2 = _1 * 72; <-- to change + // _3 = &array + _2; + enum tree_code code = gimple_expr_code (to_change); + const_tree old_base_type = TREE_TYPE (old_type); + tree old_struct_size_tree_1 = TYPE_SIZE_UNIT (old_base_type); + int old_struct_size_int = tree_to_shwi (old_struct_size_tree_1); + const_tree pointer_to_unit_type = TREE_TYPE (lhs); + const_tree unit_type = TREE_TYPE (pointer_to_unit_type); + tree new_struct_size_tree_1 = TYPE_SIZE_UNIT (unit_type); + int new_struct_size_int = tree_to_shwi (new_struct_size_tree_1); + + if (code == PLUS_EXPR) + { + // If we are here it is because we are adding an offset. + // It is usually whenever we do somehting like + // _2 = _1 + CONSTANT; <-- to change + // _3 = &array + _2; + gcc_assert (code == PLUS_EXPR); + + tree constant_plus = gimple_op (to_change, 2); + gcc_assert (TREE_CODE (constant_plus) == INTEGER_CST); + + int old_unit_size_one_plus = tree_to_uhwi (constant_plus); + // This used to work for unprofiled code. + // However, with profiling, I guess we can jump wherever we want... + // So, we need to find out which field we were intending to access + // and where that field maps to the new struct... + // gcc_assert(is_modulo); + + bool is_modulo_old = old_unit_size_one_plus % old_struct_size_int == 0; + bool is_modulo_new = old_unit_size_one_plus % new_struct_size_int == 0; + // If we are not in flag_profile_use + // we will always be a modulo + if (!flag_profile_use) + gcc_assert (is_modulo_old); + + test_log ("old plus constant = %d", 0, old_unit_size_one_plus); + test_log ("old struct size int = %d", 0, old_struct_size_int); + test_log ("new struct size int = %d", 0, new_struct_size_int); + test_log ("old_unit_size_one_plus modulo new_struct_pointer_int = %d", 0, + old_unit_size_one_plus % new_struct_size_int); + + // FIXME: + // The problem is that if flag_profile_use is enabled, + // then we can potentially **predict** the offset + // to be the correct one. But not always? + // Therefore, we really do not have an idea if we are using + // a value obtained from profiling or one from "correctness". + if (flag_profile_use) + { + bool is_from_profiling = is_modulo_new; + bool is_from_correctness = is_modulo_old; + gcc_assert (is_from_correctness || is_from_profiling); + } + + int field_accessed_offset = old_unit_size_one_plus % old_struct_size_int; + + if (flag_profile_use && is_modulo_new) + gcc_assert (field_accessed_offset != 0); + + int new_plus_constant_int + = old_unit_size_one_plus / old_struct_size_int * new_struct_size_int + + field_accessed_offset; + + if (flag_profile_use && is_modulo_new) + gcc_assert(field_accessed_offset % new_struct_size_int == 0); + + tree new_plus_constant + = build_int_cst (TREE_TYPE (constant_plus), new_plus_constant_int); + gimple_set_op (to_change, 2, new_plus_constant); + + // And now we need to get the defintion of _1 + variable = gimple_op (to_change, 1); + gcc_assert (TREE_CODE (variable) == SSA_NAME); + def_for_variable = SSA_NAME_DEF_STMT (variable); + num_operands = gimple_num_ops (def_for_variable); + test_log ("how many operands ? %d", 0, num_operands); + need_to_skip = num_operands == 2; + to_change = need_to_skip + ? SSA_NAME_DEF_STMT (gimple_op (def_for_variable, 1)) + : def_for_variable; + code = gimple_expr_code (to_change); + } + + if (code == MULT_EXPR) + { + // How do we know what type we need to change? + // That is _3 (or lhs) is the result of the pointer arithmetic. + // So we need one level of dereference in order to find + // the underlying type. + + // We need to get the second operand + test_print_gimple_stmt (to_change); + tree constant = gimple_op (to_change, 2); + gcc_assert (TREE_CODE (constant) == INTEGER_CST); + + // We should have the same size... + // Is this always true? + // TODO: can we enable this assertion? + // int old_unit_size_one = tree_to_shwi (constant); + // gcc_assert(old_unit_size_one == old_struct_size_int); + + // Now we need to modify the second operand... + tree new_constant + = build_int_cst (TREE_TYPE (constant), new_struct_size_int); + gimple_set_op (to_change, 2, new_constant); + } + + return NULL; +} + +static tree +rewrite_pointer_plus_def_rhs_integer_constant (tree pointer, + tree integer_constant, + const_tree old_type) +{ + gcc_assert (TREE_CODE (integer_constant) == INTEGER_CST); + tree pointer_type = TREE_TYPE (pointer); + gcc_assert (TREE_CODE (pointer_type) == POINTER_TYPE); + + const_tree base_type = TREE_TYPE (TREE_TYPE (pointer)); + + tree size = TYPE_SIZE_UNIT (base_type); + int new_struct_size = tree_to_shwi (size); + int old_offset = tree_to_uhwi (integer_constant); + + const_tree old_base_type = TREE_TYPE (old_type); + + tree old_type_size = TYPE_SIZE_UNIT (old_base_type); + int old_struct_size = tree_to_shwi (old_type_size); + int modulo = old_offset % old_struct_size; + test_log ("old_struct_size = %d", 0, old_struct_size); + + tree integer_constant_type = TREE_TYPE (integer_constant); + + int new_offset = old_offset / old_struct_size * new_struct_size; + tree new_constant = build_int_cst (integer_constant_type, new_offset); + return new_constant; +} + +static bool +rewrite_pointer_plus_def_rhs (gimple *stmt, gimple_stmt_iterator &gsi, tree lhs, + tree op0, tree op1, + t_map &type_map, + t_map &inverse) +{ + gcc_assert (op0 && op1); + tree op0_type = TREE_TYPE (lhs); + //const_tree *new_op0_type = type_map.get (op0_type); + delete_info_t *delete_info = type_map.get (op0_type); + const_tree *new_op0_type = delete_info ? &(delete_info->new_record) : NULL; + test_log ("rewrite_pointer_plus has old_type %s", 0, + new_op0_type ? "true" : "false"); + // gcc_assert(!new_op0_type); + test_log ("rewrite_pointer_plus op0_type %s", 0, get_type_name (op0_type)); + if (new_op0_type) + test_log ("rewrite_pointer_plus new_op0_type %s", 0, + get_type_name (*new_op0_type)); + + rewrite_expr (op0, type_map, 0); + rewrite_expr (op1, type_map, 0); + + const char *new_type_name + = new_op0_type ? get_type_name (*new_op0_type) : get_type_name (op0_type); + + test_log ("rewrite_pointer_plus new_type_name %s", 0, new_type_name); + //const_tree *inverse_type_ptr = inverse.get (op0_type); + delete_info_t *delete_info_inverse = inverse.get(op0_type); + const_tree *inverse_type_ptr = delete_info_inverse ? &(delete_info_inverse->old_record) : NULL; + bool not_in_map_nor_inverse = !new_op0_type && !inverse_type_ptr; + test_log ("continuing? %s", 0, not_in_map_nor_inverse ? "false" : "true"); + if (not_in_map_nor_inverse) + return false; + + const_tree old_type + = new_op0_type ? (const_tree) op0_type : *inverse_type_ptr; + test_log ("what is the old_type name ? %s", 0, get_type_name (old_type)); + + bool is_op1_int_cst = TREE_CODE (op1) == INTEGER_CST; + tree integer_constant = is_op1_int_cst ? op1 : op0; + bool has_integer_constant = (TREE_CODE (integer_constant) == INTEGER_CST); + + if (has_integer_constant) + { + // I want to know if this is an invariant. + // TODO: Outline + gcc_assert (TREE_CODE (op1) == INTEGER_CST); + tree pointer = is_op1_int_cst ? op0 : op1; + tree new_constant + = rewrite_pointer_plus_def_rhs_integer_constant (pointer, + integer_constant, + old_type); + unsigned int operand = is_op1_int_cst ? 2 : 1; + gimple_set_op (stmt, operand, new_constant); + return true; + } + + tree variable = op1; + tree pointer = op0; + // I want to know if this is an invariant. + tree pointer_type = TREE_TYPE (pointer); + gcc_assert (TREE_CODE (pointer_type) == POINTER_TYPE); + tree new_variable + = rewrite_pointer_plus_def_rhs_variable_replace_constants_implementation ( + gsi, lhs, pointer, variable, old_type); + if (!new_variable) + return false; + unsigned int operand + = TREE_CODE (TREE_TYPE (op0)) == POINTER_TYPE ? 2 : 1; + gimple_set_op (stmt, operand, new_variable); + + return true; +} + +static bool +rewrite_pointer_diff_def_rhs (gimple_stmt_iterator &gsi, tree lhs, + tree op0, tree op1, + t_map &type_map, + t_map &inverse) +{ + gcc_assert (op0 && op1); + tree op0_type = TREE_TYPE (lhs); + //const_tree *new_op0_type = type_map.get (op0_type); + delete_info_t *delete_info = type_map.get(op0_type); + const_tree *new_op0_type = delete_info ? &(delete_info->new_record) : NULL; + test_log ("rewrite_pointer_diff has old_type %s", 0, + new_op0_type ? "true" : "false"); + test_log ("rewrite_pointer_diff op0_type %s", 0, get_type_name (op0_type)); + if (new_op0_type) + test_log ("rewrite_pointer_diff new_op0_type %s", 0, + get_type_name (*new_op0_type)); + + rewrite_expr (op0, type_map, 0); + rewrite_expr (op1, type_map, 0); + + const char *new_type_name + = new_op0_type ? get_type_name (*new_op0_type) : get_type_name (op0_type); + // TODO: I'd prefer to have a parameter + // that tells me what to change + // several stack frames above. + test_log ("rewrite_pointer_diff new_type_name %s", 0, new_type_name); + //const_tree *inverse_type_ptr = inverse.get (op0_type); + delete_info_t *delete_info_inverse = inverse.get (op0_type); + const_tree *inverse_type_ptr = delete_info_inverse ? &(delete_info_inverse->old_record) : NULL; + bool not_in_map_nor_inverse = !new_op0_type && !inverse_type_ptr; + test_log ("continuing? %s", 0, not_in_map_nor_inverse ? "false" : "true"); + if (not_in_map_nor_inverse) + return false; + + const_tree old_type + = new_op0_type ? (const_tree) op0_type : *inverse_type_ptr; + + bool is_op1_int_cst = TREE_CODE (op1) == INTEGER_CST; + tree integer_constant = is_op1_int_cst ? op1 : op0; + bool has_integer_constant = (TREE_CODE (integer_constant) == INTEGER_CST); + + // I want to know if this is an invariant. + if (has_integer_constant) + gcc_unreachable (); + + // I want to know if this is an invariant. + gcc_assert (TREE_CODE (op0_type) == POINTER_TYPE); + rewrite_pointer_diff_def_rhs_variable_replace_constants_implementation ( + gsi, lhs, op0, op1, old_type); + + return true; +} +static bool +rewrite_assign_rhs (gimple *stmt, gimple_stmt_iterator &gsi, + t_map &type_map, + t_map &inverse) +{ + // WONTFIX, It looks like there is no way to obtain + // the expression for the rhs. So instead, we have to do + // this hack. + // Which expressions can be toplevel? + bool is_stmt_rewritten = false; + enum tree_code code = gimple_expr_code (stmt); + tree lhs = gimple_assign_lhs (stmt); + switch (TREE_CODE(lhs)) + { + case COMPONENT_REF: + test_log(" I AM HERE HERE HERE", 0); + break; + default: + break; + } + + switch (code) + { + case POINTER_PLUS_EXPR: + { + tree rhs2 = gimple_assign_rhs2 (stmt); + tree rhs1 = gimple_assign_rhs1 (stmt); + tree lhs = gimple_assign_lhs (stmt); + tree lhs_type = TREE_TYPE (lhs); + gcc_assert (lhs_type); + delete_info_t *delete_info = type_map.get (lhs_type); + gcc_assert (lhs && rhs2 && rhs1); + bool retval = rewrite_pointer_plus_def_rhs (stmt, gsi, lhs, rhs1, rhs2, + type_map, inverse); + if (delete_info) + substitute_type(lhs, delete_info->new_record); + return retval; + } + case POINTER_DIFF_EXPR: + { + tree rhs2 = gimple_assign_rhs2 (stmt); + tree rhs1 = gimple_assign_rhs1 (stmt); + tree lhs = gimple_assign_lhs (stmt); + tree lhs_type = TREE_TYPE (lhs); + gcc_assert (lhs_type); + delete_info_t *delete_info = type_map.get (lhs_type); + gcc_assert (lhs && rhs2 && rhs1); + bool retval = rewrite_pointer_diff_def_rhs (gsi, lhs, rhs1, rhs2, + type_map, inverse); + if (delete_info) + substitute_type(lhs, delete_info->new_record); + return retval; + } + break; + default: + { + //print_gimple_stmt (dump_file, stmt, 0); + test_log ("DEFAULT HERE: %s", 0, get_tree_code_name (code)); + } + break; + } + + switch (gimple_assign_rhs_class (stmt)) + { + case GIMPLE_TERNARY_RHS: + { + tree rhs3 = gimple_assign_rhs3 (stmt); + is_stmt_rewritten |= rewrite_expr (rhs3, type_map, 0); + } + /* fall through */ + case GIMPLE_BINARY_RHS: + { + tree rhs2 = gimple_assign_rhs2 (stmt); + is_stmt_rewritten |= rewrite_expr (rhs2, type_map, 0); + } + /* fall through */ + case GIMPLE_SINGLE_RHS: + case GIMPLE_UNARY_RHS: + { + tree rhs1 = gimple_assign_rhs1 (stmt); + is_stmt_rewritten |= rewrite_expr (rhs1, type_map, 0); + } + break; + default: + gcc_unreachable (); + break; + } + + return is_stmt_rewritten; +} + +static void +rewrite_call_lhs (gimple *stmt, __attribute__((unused)) gimple_stmt_iterator &gsi, + t_map &type_map) +{ + gcc_assert (stmt); + test_print_gimple_stmt (stmt); + tree lhs = gimple_call_lhs (stmt); + // LHS can be null. Example `foo()` + if (!lhs) + return; + + rewrite_expr (lhs, type_map, 0); +} + +static void +rewrite_call_rhs (gimple *stmt, __attribute__((unused)) gimple_stmt_iterator &gsi, + t_map &type_map) +{ + gcc_assert (stmt); + tree fn = gimple_call_fn (stmt); + gcc_assert (fn); + gcall *call = dyn_cast<gcall *> (stmt); + tree return_type = gimple_call_return_type (call); + test_log ("what is the return type %s", 0, get_type_name (return_type)); + gcc_assert (return_type); + + unsigned args = gimple_call_num_args (stmt); + for (unsigned i = 0; i < args; i++) + { + tree arg = gimple_call_arg (stmt, i); + rewrite_expr (arg, type_map, 0); + } +} + +static void +rewrite_call (gimple *stmt, gimple_stmt_iterator &gsi, + t_map &type_map) +{ + rewrite_call_lhs (stmt, gsi, type_map); + rewrite_call_rhs (stmt, gsi, type_map); +} + +static void +rewrite_cond (gimple *stmt, __attribute((unused)) gimple_stmt_iterator &gsi, + t_map &type_map) +{ + tree lhs = gimple_cond_lhs (stmt); + tree rhs = gimple_cond_rhs (stmt); + rewrite_expr (lhs, type_map, 0); + rewrite_expr (rhs, type_map, 0); +} + +static void +rewrite_phi (gimple *stmt, __attribute__((unused)) gimple_stmt_iterator &gsi, + t_map &type_map) +{ + test_log ("rewriting phi result", 0); + tree ssa_result = gimple_phi_result (stmt); + rewrite_expr (ssa_result, type_map, 0); + unsigned args = gimple_phi_num_args (stmt); + for (unsigned i = 0; i < args; i++) + { + test_log ("rewriting phi arg", 0); + tree arg = gimple_phi_arg_def (stmt, i); + rewrite_expr (arg, type_map, 0); + } +} + +static bool +rewrite_assign_lhs (gimple *stmt, gimple_stmt_iterator &gsi, t_map &type_map, t_map &inverse) +{ + tree lhs = gimple_assign_lhs(stmt); + enum tree_code code = TREE_CODE(lhs); + bool is_component_ref = code == COMPONENT_REF; + if (is_component_ref) + { + return rewrite_component_ref_def_lhs(gsi, lhs, type_map, 0); + } + + rewrite_expr(lhs, type_map, 0); + return false; +} + +static bool +rewrite_assign (gimple *stmt, gimple_stmt_iterator &gsi, + t_map &type_map, + t_map &inverse) +{ + gcc_assert (stmt); + test_print_gimple_stmt (stmt); + bool deleted = rewrite_assign_lhs (stmt, gsi, type_map, inverse); + if (deleted) { + unlink_stmt_vdef (stmt); + gsi_remove(&gsi, true); + return deleted; + } + rewrite_assign_rhs (stmt, gsi, type_map, inverse); + test_print_gimple_stmt (stmt); + return false; +} + +static bool +rewrite_return (gimple *stmt, gimple_stmt_iterator &gsi, + t_map &type_map) +{ + gcc_assert (stmt); + test_print_gimple_stmt (stmt); + greturn *retisn = dyn_cast<greturn *> (stmt); + gcc_assert(retisn); + tree retval = gimple_return_retval(retisn); + if (!retval) return false; + + tree decl = DECL_RESULT (current_function_decl); + rewrite_expr(decl, type_map, 0); + + rewrite_expr (retval, type_map, 0); + tree retval_type = TREE_TYPE(retval); + gcc_assert(retval_type); + log("return type %s\n", get_type_name(retval_type)); + return false; +} + +static bool +rewrite_stmt (gimple *stmt, gimple_stmt_iterator &gsi, + t_map &type_map, + t_map &inverse) +{ + gcc_assert (stmt); + const enum gimple_code code = gimple_code (stmt); + bool deleted = false; + switch (code) + { + case GIMPLE_ASSIGN: + deleted = rewrite_assign (stmt, gsi, type_map, inverse); + break; + case GIMPLE_CALL: + rewrite_call (stmt, gsi, type_map); + break; + case GIMPLE_COND: + rewrite_cond (stmt, gsi, type_map); + break; + case GIMPLE_PHI: + rewrite_phi (stmt, gsi, type_map); + break; + case GIMPLE_RETURN: + rewrite_return (stmt, gsi, type_map); + break; + default: + { + const char *const gimple_code_str = gimple_code_name[code]; + test_log ("gimple_code %s", 0, gimple_code_str); + test_print_gimple_stmt (stmt); + } + break; + } + return deleted; +} + +static void +rewrite_basic_block (basic_block bb, t_map &type_map, + t_map &inverse) +{ + gcc_assert (bb); + bool is_first = true; + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + bool deleted = rewrite_stmt (stmt, gsi, type_map, inverse); + + if (deleted && !is_first) + { + gsi_prev(&gsi); + } + + while (deleted && is_first) + { + stmt = gsi_stmt(gsi); + if (!stmt) break; // we might have deleted and was end of basic block? + deleted = rewrite_stmt (stmt, gsi, type_map, inverse); + if (gsi_end_p(gsi)) break; + } + + if (deleted && gsi_end_p (gsi)) break; + + is_first = false; + } + for (gimple_stmt_iterator gsi = gsi_start_phis (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + rewrite_stmt (stmt, gsi, type_map, inverse); + } +} + +static void +rewrite_function_body (cgraph_node *cnode, + t_map &type_map, + t_map &inverse) +{ + gcc_assert (cnode); + const char* name_s = cnode->name(); + const char* dot = strstr(name_s, "."); // Updated functions have a dot on them. + // If there's a dot, we need to do a memcpy + // of the string name[0:dot] + size_t n = dot ? dot - name_s : strlen(name_s); + char precanon[100]; + const char* canonical = dot ? strncpy(precanon, name_s, n) : name_s; + bool already_processed = updated_functions.find(name_s) != updated_functions.end(); + log("original name %s\n", name_s); + log("canonical name %s\n", canonical); + log("already processed ? %s\n", already_processed ? "true" : "false"); + if (already_processed) return; + + updated_functions.insert(name_s); + cnode->get_untransformed_body (); + basic_block bb = NULL; + function *func = DECL_STRUCT_FUNCTION (cnode->decl); + push_cfun (func); + FOR_EACH_BB_FN (bb, func) + { + rewrite_basic_block (bb, type_map, inverse); + } + // TODO: + // can we move this logic into the above? + // Thanks! + size_t i; + tree name; + FOR_EACH_SSA_NAME (i, name, cfun) + { + if (SSA_NAME_VAR (name) != NULL_TREE + && TREE_TYPE (name) != TREE_TYPE (SSA_NAME_VAR (name))) + { + TREE_TYPE (name) = TREE_TYPE (SSA_NAME_VAR (name)); + test_log ("changing to %s", 0, + get_type_name (TREE_TYPE (SSA_NAME_VAR (name)))); + } + } + pop_cfun (); + print_function (cnode); +} + +static void +rewrite_local_decl (tree var_decl, t_map &type_map) +{ + gcc_assert (var_decl); + gcc_assert (TREE_CODE (var_decl) == VAR_DECL); + const_tree type = TREE_TYPE (var_decl); + gcc_assert (type); + delete_info_t *delete_info = type_map.get (type); + + if (!delete_info) + return; + + + const_tree new_type = delete_info->new_record; + gcc_assert (new_type); + test_write ("rewriting,local_decl"); + test_print_generic_decl (var_decl); + test_write (","); + //TODO: Doing an experiment, we might need to keep qualifications... + substitute_type(var_decl, new_type); + + // Keep this relayout_decl + relayout_decl (var_decl); + test_print_generic_decl (var_decl); + test_write ("\n"); +} + +static void +rewrite_local_decls (cgraph_node *cnode, + t_map &type_map) +{ + gcc_assert (cnode); + tree decl = cnode->decl; + gcc_assert (decl); + gcc_assert (TREE_CODE (decl) == FUNCTION_DECL); + function *func = DECL_STRUCT_FUNCTION (decl); + gcc_assert (func); + tree var_decl = NULL; + int i = 0; + FOR_EACH_LOCAL_DECL (func, i, var_decl) + { + rewrite_local_decl (var_decl, type_map); + } + print_function (cnode); +} + +static void +rewrite_function_parms (tree expr, t_map &type_map, + const int indent) +{ + enum tree_code code = TREE_CODE (expr); + bool is_function_decl = code == FUNCTION_DECL; + gcc_assert (is_function_decl); + + for (tree parm = DECL_ARGUMENTS (expr); parm; parm = DECL_CHAIN (parm)) + { + tree parm_type = TREE_TYPE (parm); + delete_info_t *delete_info = type_map.get (parm_type); + if (!delete_info) + continue; + + const_tree new_type = delete_info->new_record; + test_log ("before rewrite_function_arg %s", indent, get_type_name (parm_type)); + substitute_type(parm, new_type); + relayout_decl (parm); + tree parm_type_2 = TREE_TYPE (parm); + test_log ("after rewrite_function_arg %s", indent, + get_type_name (parm_type_2)); + } +} + +static void +rewrite_function_parms (cgraph_node *cnode, + t_map &type_map) +{ + rewrite_function_parms (cnode->decl, type_map, 0); +} + +static void +rewrite_function_return_type (tree expr, + t_map &type_map, + const int indent) +{ + enum tree_code code = TREE_CODE (expr); + bool is_function_decl = code == FUNCTION_DECL; + gcc_assert (is_function_decl); + + tree function_type = TREE_TYPE (expr); + + // TODO: We do not support method's yet. + gcc_assert (TREE_CODE (function_type) == FUNCTION_TYPE); + tree function_return_type = TREE_TYPE (function_type); + gcc_assert (function_return_type); + + test_log ("before rewrite_function_return_type %s", indent, + get_type_name (function_return_type)); + delete_info_t *delete_info = type_map.get (function_return_type); + if (!delete_info) + return; + + const_tree new_type = delete_info->new_record; + tree new_type_2 = build_type_variant(const_to_tree(new_type), TYPE_READONLY(function_return_type), TYPE_VOLATILE(function_return_type)); + substitute_type(function_type, new_type_2); + // Do not use relayout_decl(cnode->decl); + tree function_type_2 = TREE_TYPE (expr); + tree function_return_type_2 = TREE_TYPE (function_type_2); + test_log ("after rewrite_function_return_type %s", indent, + get_type_name (function_return_type_2)); +} + +static void +rewrite_function_return_type (cgraph_node *cnode, + t_map &type_map) +{ + gcc_assert (TREE_CODE (cnode->decl) == FUNCTION_DECL); + tree function_type = TREE_TYPE (cnode->decl); + gcc_assert (function_type); + rewrite_function_return_type (cnode->decl, type_map, 0); +} + +static void +rewrite_global_decl (varpool_node *vnode, + t_map &type_map) +{ + int i; + struct ipa_ref *ref; + + /* Only IPA_REF_STORE and IPA_REF_LOAD left. */ + for (i = 0; vnode->iterate_referring (i, ref); i++) + { + test_log ("rewriting global declaration", 0); + rewrite_local_decl (vnode->decl, type_map); + test_print_generic_decl (vnode->decl); + } +} + +static void +rewrite_global_decls ( t_map &type_map) +{ + varpool_node *vnode; + FOR_EACH_VARIABLE (vnode) + { + rewrite_global_decl (vnode, type_map); + } +} + +static void +rewrite_function (cgraph_node *cnode, + t_map &type_map, + t_map &inverse) +{ + // rewrite_function_body should be before + // rewrite_local_decls to make work easier. + // Why? Because if we rewrite local decl types + // first, then we might need to look for + // old AND new types in rewrite_function_body. + // If we rewrite local decl types later, + // then in rewrite_function_body we only + // need to look for old types. + rewrite_function_parms (cnode, type_map); + rewrite_function_return_type (cnode, type_map); + rewrite_function_body (cnode, type_map, inverse); + rewrite_local_decls (cnode, type_map); +} + +static void +rewrite_functions ( t_map &type_map, + t_map &inverse) +{ + cgraph_node *cnode = NULL; + FOR_EACH_DEFINED_FUNCTION (cnode) + { + rewrite_function (cnode, type_map, inverse); + } +} + +static void +rewrite_references_to_modified_structs ( + t_map &type_map, + t_map &inverse) +{ + rewrite_functions (type_map, inverse); + rewrite_global_decls (type_map); +} + +bool +compute_inverse_type_map_internal ( + const_tree const &old_type, delete_info_t *delete_info, + t_map *inverse_map) +{ + gcc_assert (inverse_map && delete_info); + const_tree new_type = delete_info->new_record; + inverse_map->put (delete_info->new_record, *delete_info); + const char *old_identifier = get_type_name (old_type); + const char *new_identifier = get_type_name (new_type); + test_log ("inverse,%s,%s", 0, new_identifier, old_identifier); + return true; +} + +static void +compute_inverse_type_map ( t_map &mod_type_map, + t_map &inverse_map) +{ + mod_type_map.traverse< t_map *, + compute_inverse_type_map_internal> (&inverse_map); +} + + +static void +separate_set_of_pairs_into_pair_of_sets(const record_field_set &set_of_pairs, std::set<const char*, cstrless> &one, std::set<const char*, cstrless> &two) +{ + + for (auto it = set_of_pairs.cbegin(); it != set_of_pairs.cend() ; ++it) + { + fields record_field_pair = *it; + const_tree record = record_field_pair.first; + if (strcmp(get_record_name(record), "<name_tree_is_null>") == 0) continue; + const_tree field = record_field_pair.second; + one.insert(get_type_name(record)); + one.insert(get_type_name(TYPE_MAIN_VARIANT(record))); + two.insert(get_field_name(field)); + char *buffer; + char *buffer2; + asprintf(&buffer, "%s.%s", get_type_name(record), get_field_name(field)); + asprintf(&buffer2, "%s.%s", get_type_name(TYPE_MAIN_VARIANT(record)), get_field_name(field)); + log("putting in %s\n", buffer); + log("putting in %s\n", buffer2); + record_field_pair_name.insert(buffer); + record_field_pair_name.insert(buffer2); + } +} + +static void +iphw_execute_escape_analysis() +{ + gcc_assert(!flag_ipa_typelist_struct && !flag_ipa_typelist_field); + const record_field_set to_reorg = get_fields_to_reorg(); + print_record_field_set(to_reorg); + //separate_set_of_pairs_into_pair_of_sets(to_reorg, interesting_records, interesting_fields); + + //hash_set<const_tree> orig_type_map; + //collect_orig_structs (orig_type_map); + //t_map mod_type_map; + //compute_modified_structs (orig_type_map, mod_type_map); + //t_map inverse_type_map; + //compute_inverse_type_map (mod_type_map, inverse_type_map); + //rewrite_references_to_modified_structs (mod_type_map, inverse_type_map); +} + +static void +iphw_execute_cli_args() +{ + gcc_assert(flag_ipa_typelist_struct && flag_ipa_typelist_field); + + hash_set<const_tree> orig_type_map; + collect_orig_structs (orig_type_map); + + // So what would a client will have to implement? + // * Pass a set of types + // * A function that translates old_types to new_types + // class TypeRewriter { + // // A set might not be + // // sufficient to say **how** the types should + // // be modified + // TypeRewriter(set, function type -> new_type); + // ~TypeRewriter(); // allows me to free memory used during + // // the pass easily + // rewrite(stmt); // maybe no since it can corrupt the function? + // rewrite(bb); // maybe no since it can corrupt the function? + // rewrite(func); + // rewrite(set(functions)); + // } + t_map mod_type_map; + compute_modified_structs (orig_type_map, mod_type_map); + t_map inverse_type_map; + compute_inverse_type_map (mod_type_map, inverse_type_map); + rewrite_references_to_modified_structs (mod_type_map, inverse_type_map); +} + +static unsigned int +iphw_execute () +{ + test_log ("Executing structreorg", 0); + const bool perform_escape_analysis = !flag_ipa_typelist_struct && !flag_ipa_typelist_field ; + perform_escape_analysis ? iphw_execute_escape_analysis() : iphw_execute_cli_args(); + return 0; +} + +int +str_reorg_dead_field_eliminate (__attribute__((unused)) Info *info) +{ + gcc_assert (in_lto_p); + return iphw_execute (); +} diff --git a/gcc/ipa-str-reorg-field-reorder.c b/gcc/ipa-str-reorg-field-reorder.c new file mode 100644 index 00000000000..5c9a2bb0b9b --- /dev/null +++ b/gcc/ipa-str-reorg-field-reorder.c @@ -0,0 +1,47 @@ +/* Interprocedural scalar replacement of aggregates + Copyright (C) 2019-2020 Free Software Foundation, Inc. + + Contributed by Your Name & Email address go here + +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/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "tree.h" +#include "gimple.h" +#include "tree-pass.h" +#include "cgraph.h" +#include "gimple-iterator.h" +#include "pretty-print.h" +#include <vector> +#include <map> +#include <set> +#include "ipa-structure-reorg.h" +#include "dumpfile.h" +#include "tree-pretty-print.h" +#include "gimple-pretty-print.h" +#include "langhooks.h" + +int +str_reorg_field_reorder (Info *info) +{ + // TBD + // DEBUG ( "Running str_reorg_field_reorder\n"); + return 0; +} diff --git a/gcc/ipa-str-reorg-instance-interleave.c b/gcc/ipa-str-reorg-instance-interleave.c new file mode 100644 index 00000000000..2b5d4afd81b --- /dev/null +++ b/gcc/ipa-str-reorg-instance-interleave.c @@ -0,0 +1,48 @@ +/* Interprocedural scalar replacement of aggregates + Copyright (C) 2019-2020 Free Software Foundation, Inc. + + Contributed by Gary Oblock <goblock@marvell.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/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "tree.h" +#include "gimple.h" +#include "tree-pass.h" +#include "cgraph.h" +#include "gimple-iterator.h" +#include "pretty-print.h" +#include <vector> +#include <map> +#include <set> +#include "ipa-structure-reorg.h" +#include "dumpfile.h" +#include "tree-pretty-print.h" +#include "gimple-pretty-print.h" +#include "langhooks.h" + +int +// Returns one if something was done and zero otherwise. +str_reorg_instance_interleave (Info *info) +{ + // TBD + // DEBUG ( "Running str_reorg_instance_interleave\n"); + return 0; +} diff --git a/gcc/ipa-str-reorg-utils.c b/gcc/ipa-str-reorg-utils.c new file mode 100644 index 00000000000..cc6d5162af5 --- /dev/null +++ b/gcc/ipa-str-reorg-utils.c @@ -0,0 +1,300 @@ +#include "ipa-str-reorg-utils.h" + +// This really should be inaccessible to anyone. +const_tree +get_base_type_from_ptr_or_arr_type (const_tree old_pointer_type, + const_tree pointer_type, + unsigned int &indirection_level) +{ + if (pointer_type == NULL) + { + gcc_assert (TREE_CODE (old_pointer_type) != POINTER_TYPE); + gcc_assert (TREE_CODE (old_pointer_type) != ARRAY_TYPE); + return old_pointer_type; + } + return get_base_type_from_ptr_or_arr_type (pointer_type, + TREE_TYPE (pointer_type), + ++indirection_level); +} + +const_tree +get_base_type_from_ptr_or_arr_type (const_tree ptr_or_array, + unsigned int &indirection_level) +{ + const bool is_array = TREE_CODE (ptr_or_array) == ARRAY_TYPE; + const bool is_ptr = TREE_CODE (ptr_or_array) == POINTER_TYPE; + const bool is_array_or_ptr = is_array || is_ptr; + gcc_assert (is_array_or_ptr); + indirection_level = 0; + return get_base_type_from_ptr_or_arr_type (ptr_or_array, + TREE_TYPE (ptr_or_array), + indirection_level); +} + +const_tree +get_base_type_from_array_type (const_tree array_type, + unsigned int &indirection_level) +{ + gcc_assert (TREE_CODE (array_type) == ARRAY_TYPE); + return get_base_type_from_ptr_or_arr_type (array_type, indirection_level); +} + +const_tree +get_base_type_from_array_type (const_tree array_type) +{ + gcc_assert (TREE_CODE (array_type) == ARRAY_TYPE); + unsigned int indirection_level; + return get_base_type_from_array_type (array_type, indirection_level); +} + +const_tree +get_base_type_from_pointer_type (const_tree pointer_type, + unsigned int &indirection_level) +{ + gcc_assert (TREE_CODE (pointer_type) == POINTER_TYPE); + return get_base_type_from_ptr_or_arr_type (pointer_type, indirection_level); +} + +const_tree +get_base_type_from_pointer_type (const_tree pointer_type) +{ + gcc_assert (TREE_CODE (pointer_type) == POINTER_TYPE); + unsigned int indirection_level; + return get_base_type_from_pointer_type (pointer_type, indirection_level); +} + +const_tree +get_base_type (const_tree type) +{ + enum tree_code tree_code_type = TREE_CODE(type); + switch (tree_code_type) + { + case ARRAY_TYPE: return get_base_type_from_array_type(type); break; + case POINTER_TYPE: return get_base_type_from_pointer_type(type); break; + default: return type; break; + } + + gcc_unreachable(); + return NULL; +} +const char * +make_pointer_name (const_tree pointer) +{ + gcc_assert (TREE_CODE (pointer) == POINTER_TYPE); + unsigned int indirection_level; + const_tree base_type + = get_base_type_from_pointer_type (pointer, indirection_level); + const char *pointer_name = make_pointer_name (base_type, indirection_level); + return pointer_name; +} + +const char * +make_pointer_or_array_name (const char *base_type, const char *postfix) +{ + char *ptr; + int calculated_size = strlen (base_type) + strlen (postfix); + // TODO: Do not use asprintf? + // We'll let exit() deal with freeing this memory. + int retval = asprintf (&ptr, "%s%s", base_type, postfix); + gcc_assert (retval == calculated_size); + return ptr; +} + +const char * +make_array_postfix (unsigned int indirection_level) +{ + gcc_assert (indirection_level > 0); + static const char *max_indirection_level_str_array + = "[][][][][][][][][][][][][]"; + static const size_t size_array = strlen (max_indirection_level_str_array); + static const size_t postfix_size_array = 2; + static const size_t max_indirection_level_array + = size_array / postfix_size_array; + gcc_assert (indirection_level < max_indirection_level_array); + return max_indirection_level_str_array + size_array + - (indirection_level * postfix_size_array); +} + +const char * +make_pointer_postfix (unsigned int indirection_level) +{ + gcc_assert (indirection_level > 0); + static const char *max_indirection_level_str_pointer + = "************************"; + static const size_t size_pointer = strlen (max_indirection_level_str_pointer); + static const size_t postfix_size_pointer = 1; + static const size_t max_indirection_level_pointer + = size_pointer / postfix_size_pointer; + gcc_assert (indirection_level < max_indirection_level_pointer); + return max_indirection_level_str_pointer + size_pointer + - (indirection_level * postfix_size_pointer); +} + +const char * +make_pointer_name (const char *base_type_name, + const unsigned int indirection_level) +{ + const char *postfix = make_pointer_postfix (indirection_level); + const char *ptr = make_pointer_or_array_name (base_type_name, postfix); + return ptr; +} + +const char * +make_pointer_name (const_tree base_type, const unsigned int indirection_level) +{ + const char *struct_name = get_type_name (base_type); + return make_pointer_name (struct_name, indirection_level); +} + + +const char * +make_array_name (const char *base_type_name, + const unsigned int indirection_level) +{ + const char *postfix = make_array_postfix (indirection_level); + const char *ptr = make_pointer_or_array_name (base_type_name, postfix); + return ptr; +} + +const char * +make_array_name (const_tree base_type, const unsigned int indirection_level) +{ + const char *struct_name = get_type_name (base_type); + return make_array_name (struct_name, indirection_level); +} + +// TODO: deal with anonymous structs. +// Some records don't have identifier pointers +const char * +get_record_name (const_tree record) +{ + gcc_assert (record); + gcc_assert (TREE_CODE (record) == RECORD_TYPE); + tree name_tree = TYPE_NAME (record); + // The TYPE_NAME will be NULL_TREE for a type + // that is not a built-in type, the result of a typedef + // or a named class type. + // TODO: verify that we are never changing + // <name_tree_is_null> types + if (name_tree == NULL_TREE) + { + return "<name_tree_is_null>"; + } + + if (TREE_CODE (name_tree) == TYPE_DECL) + { + tree type_name = DECL_NAME (name_tree); + return IDENTIFIER_POINTER (type_name); + } + const char *identifier = IDENTIFIER_POINTER (name_tree); + gcc_assert (identifier); + return identifier; +} + +const char * +make_array_name (const_tree array) +{ + gcc_assert (TREE_CODE (array) == ARRAY_TYPE); + unsigned int indirection_level; + const_tree base_type + = get_base_type_from_array_type (array, indirection_level); + const char *array_name = make_array_name (base_type, indirection_level); + return array_name; +} + +const char * +get_array_name (const_tree array) +{ + gcc_assert (array); + gcc_assert (TREE_CODE (array) == ARRAY_TYPE); + const bool is_modified = TYPE_NAME (array); + if (is_modified) + return IDENTIFIER_POINTER (TYPE_NAME (array)); + + return make_array_name (array); +} + +const char * +get_pointer_name (const_tree pointer) +{ + gcc_assert (pointer); + gcc_assert (TREE_CODE (pointer) == POINTER_TYPE); + const bool is_modified = TYPE_NAME (pointer); + if (is_modified) + return IDENTIFIER_POINTER (TYPE_NAME (pointer)); + + const char *new_pointer_name = make_pointer_name (pointer); + return new_pointer_name; +} + +const char * +make_reference_name (const_tree ref) +{ + gcc_assert (TREE_CODE (ref) == REFERENCE_TYPE); + const_tree base_type = TREE_TYPE (ref); + const char *old_name = get_type_name (base_type); + char *ptr; + static const char *prefix = "&"; + static const char *suffix = ".reorg"; + int new_size = strlen (prefix) + strlen (old_name) + strlen (suffix); + int retval = asprintf (&ptr, "%s%s%s", prefix, old_name, suffix); + gcc_assert (retval == new_size); + return ptr; +} + + +const char * +get_reference_name (const_tree ref) +{ + gcc_assert (ref); + gcc_assert (TREE_CODE (ref) == REFERENCE_TYPE); + const bool is_modified = TYPE_NAME (ref); + if (is_modified) + return IDENTIFIER_POINTER (TYPE_NAME (ref)); + + const char *new_pointer_name = make_reference_name (ref); + return new_pointer_name; +} + +const char * +get_type_name (const_tree type) +{ + enum tree_code code = TREE_CODE (type); + switch (code) + { + case ARRAY_TYPE: + return get_array_name (type); + break; + case POINTER_TYPE: + return get_pointer_name (type); + break; + case RECORD_TYPE: + return get_record_name (type); + break; + case REFERENCE_TYPE: + return get_reference_name (type); + break; + default: + // TODO: generalize even more? + // wait for experimental results to dictate what + // else we should specify. + return get_tree_code_name (code); + break; + } + return NULL; +} + +const char * +get_field_name (const_tree field_decl) +{ + gcc_assert (field_decl); + gcc_assert (TREE_CODE (field_decl) == FIELD_DECL); + // TODO: deal with anonymous fields. + tree id = DECL_NAME (field_decl); + if (!id) + return "anonymous"; + gcc_assert (id); + const char *identifier = IDENTIFIER_POINTER (id); + gcc_assert (identifier); + return identifier; +} diff --git a/gcc/ipa-str-reorg-utils.h b/gcc/ipa-str-reorg-utils.h new file mode 100644 index 00000000000..0197e88e769 --- /dev/null +++ b/gcc/ipa-str-reorg-utils.h @@ -0,0 +1,58 @@ +#ifndef GCC_IPA_STR_REORG_UTILS_H +#define GCC_IPA_STR_REORG_UTILS_H +#pragma once + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" + +const char * get_type_name (const_tree type); +const char * get_reference_name (const_tree ref); +const char * make_reference_name (const_tree ref); +const char * get_pointer_name (const_tree pointer); +const char * get_array_name (const_tree array); +const char * get_record_name (const_tree record); +const char * make_array_name (const_tree base_type, const unsigned int indirection_level); +const char * make_array_name (const char *base_type_name, const unsigned int indirection_level); +const char * make_pointer_name (const_tree base_type, const unsigned int indirection_level); +const char * make_pointer_name (const char *base_type_name, const unsigned int indirection_level); +const char * make_pointer_postfix (unsigned int indirection_level); +const char * make_array_postfix (unsigned int indirection_level); +const char * make_array_name (const_tree array); +const char * make_pointer_or_array_name (const char *base_type, const char *postfix); +const char * make_pointer_name (const_tree pointer); +const_tree get_base_type_from_ptr_or_arr_type (const_tree old_pointer_type, const_tree pointer_type, unsigned int &indirection_level); +const_tree get_base_type_from_ptr_or_arr_type (const_tree ptr_or_array, unsigned int &indirection_level); +const_tree get_base_type_from_array_type (const_tree array_type, unsigned int &indirection_level); +const_tree get_base_type_from_array_type (const_tree array_type); +const_tree get_base_type_from_pointer_type (const_tree pointer_type, unsigned int &indirection_level); +const_tree get_base_type_from_pointer_type (const_tree pointer_type); +const_tree get_base_type (const_tree type); +const char* get_field_name (const_tree type); + +#include <stdio.h> + +inline void +log (const char * const format, ...) +{ + if (!dump_file) return; + + va_list args; + va_start(args, format); + vfprintf(dump_file, format, args); + va_end(args); +} + +inline void +log_2 ( const char * const format, ...) +{ + if (!dump_file) return; + + va_list args; + va_start(args, format); + vfprintf(dump_file, format, args); + va_end(args); +} + +#endif diff --git a/gcc/ipa-structure-reorg.c b/gcc/ipa-structure-reorg.c new file mode 100644 index 00000000000..10e092b664d --- /dev/null +++ b/gcc/ipa-structure-reorg.c @@ -0,0 +1,879 @@ +/* Interprocedural scalar replacement of aggregates + Copyright (C) 2019-2020 Free Software Foundation, Inc. + + Contributed by Gary Oblock <goblock@marvell.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/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "tree.h" +#include "gimple.h" +#include "tree-pass.h" +#include "cgraph.h" +#include "gimple-iterator.h" +#include "pretty-print.h" +#include <vector> +#include <map> +#include <set> +#include "ipa-structure-reorg.h" +#include "dumpfile.h" +#include "tree-pretty-print.h" +#include "gimple-pretty-print.h" +#include "langhooks.h" +// TBD more includes go here + +static void +setup_debug_flags (Info *); +static void +final_debug_info (Info *); +static unsigned int +reorg_analysis (Info_t *); +static void +reorg_analysis_debug (Info *, ReorgType *); +static bool +find_decls_and_types (Info_t *); +static void +disqualify_struct_in_struct_or_union (Info_t *); +static void +initial_reorg_debug (Info *info, ReorgType *reorg); +static void +disqualify_struct_in_struct_or_union_debug (Info *, ReorgType *); +static void +disq_str_in_str_or_union_helper (tree, std::set<tree> *, Info_t *); +static unsigned int +reorg_qualification (Info_t *); +// Name changed and moved to its own file +// static void reorg_transformation ( Info_t *); +// made extern +// static void delete_reorgtype ( ReorgType_t *, Info_t *); +// static void undelete_reorgtype ( ReorgType_t *, Info_t *); +// static void remove_deleted_types ( Info *, ReorgFn); +static tree base_type_of (tree); +static bool +is_reorg_alloc_trigger (gimple *); +static ReorgType_t * +find_struct_type_ptr_to_struct (tree, Info *); +static ReorgType_t * +get_reorgtype_info (tree type, Info_t *info); +static void +print_reorg_with_msg (FILE *, ReorgType_t *, int, const char *); +static void +dump_reorg (ReorgType_t *reorg); +static void +print_reorgs (FILE *, int, Info *); +static void +print_reorg (FILE *, int, ReorgType_t *); +//-- debugging only -- +static const char *code_str (enum tree_code); + +//---------------- Code Follows ---------------- + +static unsigned int +ipa_structure_reorg (void) +{ + std::vector<ReorgType_t> Reorg_Type; + std::vector<ReorgType_t> Saved_Reorg_Type; + std::vector<ProgDecl_t> Prog_Decl; + std::map<tree, BoolPair_t> StructTypes; // TBD horrible type name + + // DEBUG( "Running ipa_structure_reorg\n"); + + // TBD we must have run the IPA points-to analysis and + // be running in a single LTRANS partition. Sanity check + // these here. + + Info_t info = {&Reorg_Type, &Saved_Reorg_Type, + &Prog_Decl, &StructTypes, + 0, 0.0, + false, false, + false, false, + false, false}; + + setup_debug_flags (&info); + + if (flag_ipa_dead_field_eliminate) + { + str_reorg_dead_field_eliminate (&info); + } + + return true; + + if (reorg_qualification (&info)) + { + // Each of these do their own performance qualification and + // if they delete any types they must invoke restore_deleted_types + // so the next subpass has all the types to consider. + // Also, if they change the shape of a type the must create my + // type and update the ReorgTypes to reflect this. + + if (flag_ipa_structure_reorg || flag_ipa_field_reorder) + { + str_reorg_field_reorder (&info); + } + if (flag_ipa_structure_reorg || flag_ipa_instance_interleave) + { + str_reorg_instance_interleave (&info); + } + } + + final_debug_info (&info); + + return true; +} + +static void +setup_debug_flags (Info *info) +{ + // The normal way of doing this would be to + // set the flags with dump_file && (dump_flags & TDF_XXX + // where XXX is some level of dump control but + // I think the code here would work better + // (where each flag is set off the dump_flags.) + + if (dump_file) + { + info->show_all_reorg_cands = true; + info->show_all_reorg_cands_in_detail = dump_flags & TDF_DETAILS; + info->show_delete = dump_flags & TDF_DETAILS; + info->show_new_BBs = dump_flags & TDF_DETAILS; + info->show_transforms = dump_flags & TDF_DETAILS; + info->show_bounds = dump_flags & TDF_DETAILS; + } +} + +static void +final_debug_info (Info *info) +{ + // TBD +} + +static unsigned int +reorg_analysis (Info_t *info) +{ + struct cgraph_node *node; + + find_decls_and_types (info); + + // Skip computing numbOfGlobalArrays initially. + + // Skip computing numbOfLocalArrays initially. + + // Compute numbOfDynmAllocs per type in regtype + FOR_EACH_FUNCTION (node) + { + basic_block bb; + + struct function *func = DECL_STRUCT_FUNCTION (node->decl); + + // This is a work around + if (func == NULL) + { + continue; + } + + FOR_EACH_BB_FN (bb, func) + { + gimple_stmt_iterator gsi; + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + // DEBUG_F ( print_gimple_stmt, stderr, stmt, (dump_flags_t)-1); + // DEBUG ( "\n"); + if (is_gimple_call (stmt)) + { + // next line has issues but the mechanism is sound + tree t = *gimple_call_lhs_ptr (stmt); + // DEBUG( "t %p\n", t); + // Calls to a function returning void are skipped. + if (t != NULL) + { + tree bt = base_type_of (t); + if (TREE_CODE (bt) != RECORD_TYPE) + { + continue; + } + // find if in reorgtypes and get the info (in one call) + ReorgType_t *ri = get_reorgtype_info (bt, info); + if (ri != NULL && is_reorg_alloc_trigger (stmt)) + { + ri->numbOfDynmAllocs++; + } + } + } + } + } + } + + // It's LOT clear to use an iterator here TBD + for (int i = 0; i < info->reorg_type->size (); i++) + { + int n = (*(info->reorg_type))[i].numbOfGlobalArrays + + (*(info->reorg_type))[i].numbOfLocalArrays + + (*(info->reorg_type))[i].numbOfDynmAllocs; + if (n > 1) + { + (*(info->reorg_type))[i].multi_pool = true; + } + if (n == 0) + { + delete_reorgtype (&(*(info->reorg_type))[i], info); + } + } + + remove_deleted_types (info, &reorg_analysis_debug); + + return !info->reorg_type->empty (); +} + +void +reorg_analysis_debug (Info *info, ReorgType *reorg) +{ + if (info->show_delete) + { + print_reorg_with_msg (dump_file, reorg, 2, "Was not allocated"); + } +} + +static bool +find_decls_and_types (Info_t *info) +{ + // Don't keep any structure types if they aren't + // used in an array or have a pointer type (which + // hopefully will have an associated allocation.) + // Note, initially ignore the explicit statically + // allocated arrays. + // + // This initializes them too of course. + + // Find all struct types for initial ReorgTypes + // marking them all to initially be deleted. + // This is done by walking all variables. + std::set<tree> typeset; + varpool_node *var; + FOR_EACH_VARIABLE (var) + { + tree decl = var->decl; + // DEBUG( "L# %d Consider var->decl\n", __LINE__); + // DEBUG_F( print_generic_decl, stderr, decl, (dump_flags_t)-1); + tree base = base_type_of (decl); + // DEBUG( "\nBase\n"); + // DEBUG( "TREE_CODE = %s\n", code_str( TREE_CODE ( base))); + // DEBUG_F( print_generic_expr, stderr, base, (dump_flags_t)-1); + // DEBUG( "\n"); + + if (TREE_CODE (base) == RECORD_TYPE) + { + // skip if found before + if (typeset.find (base) != typeset.end ()) + { + // DEBUG( "not found\n"); + continue; + } + else + { + // DEBUG( "found\n") + ; + } + ReorgType_t rt + = {0, base, 0, 0, 0, 0.0, 0.0, false, true, NULL, NULL}; + info->reorg_type->push_back (rt); + typeset.insert (base); // ??? + } + } + + // NOTE, the scheme above leaves out local variables so + // I'll repeat the for the local variable of functions. + // TBD Am I doing something actually, after this, that + // is actually done here or above... it seems likely. + + cgraph_node *node; + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) + { + tree decl; + unsigned i; + + node->get_untransformed_body (); + + struct function *fn = DECL_STRUCT_FUNCTION (node->decl); + // enable this to see error in test_1_8 + // DEBUG( "L# %d, function name = %s\n", + //__LINE__, lang_hooks.decl_printable_name ( node->decl, 2)); + if (fn == NULL) + { + // DEBUG( " EMPTY\n"); + continue; + } + + FOR_EACH_LOCAL_DECL (fn, i, decl) + { + tree base = base_type_of (decl); + // enable this to see error in test_1_8 + // DEBUG( "L# %d Consider local var decl\n", __LINE__); + // DEBUG_F( print_generic_decl, stderr, decl, (dump_flags_t)-1); + // DEBUG( "\n"); + + if (TREE_CODE (base) == RECORD_TYPE) + { + if (typeset.find (base) != typeset.end ()) + { + continue; + } + + ReorgType_t rt + = {0, base, 0, 0, 0, 0.0, 0.0, false, true, NULL, NULL}; + info->reorg_type->push_back (rt); + typeset.insert (base); // ??? + } + } + } + + // We need this later for creating new types + for (std::set<tree>::iterator ti = typeset.begin (); ti != typeset.end (); + ti++) + { + (*(info->struct_types))[*ti] = {false, false}; + } + + if (info->show_all_reorg_cands_in_detail) + { + fprintf (dump_file, "All possible candidates:\n"); + print_reorgs (dump_file, 2, info); + } + + // Scan all declarations for pointers to ReorgTypes + // and in a later version array of them. When found + // clear the deletion mark. + // Note, there is no mechanism for looking at global declarations + // so use FOR_EACH_VARIABLE instead. I'm not 100% this is the thing + // actuall do here... but... + FOR_EACH_VARIABLE (var) + { + tree decl = var->decl; + ReorgType_t *rtype = find_struct_type_ptr_to_struct (decl, info); + if (rtype != NULL) + { + undelete_reorgtype (rtype, info); + } + } + + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) + { + tree decl; + unsigned i; + + // Only need to do this once in the program and it was done + // above! + // Node->get_untransformed_body (); + + struct function *fn = DECL_STRUCT_FUNCTION (node->decl); + + // DEBUG( "fn %p\n", fn); + // DEBUG_F( print_generic_decl, stderr, node->decl, (dump_flags_t)-1); + // DEBUG( "\n"); + // I don't know why this is coming up null.... but I'll + // skip it for now. + if (fn == NULL) + { + continue; + } + + FOR_EACH_LOCAL_DECL (fn, i, decl) + { + // Does this work... see tree.c:6132 + ReorgType_t *rtype = find_struct_type_ptr_to_struct (decl, info); + if (rtype != NULL) + { + undelete_reorgtype (rtype, info); + } + } + } + + if (info->show_all_reorg_cands) + { + fprintf (dump_file, "All preliminary ReorgTypes:\n"); + // print_reorgs ( dump_file, 2, info); + } + + // Scan all types in ReorgTypes for structure fields + // and if they are pointers to a type Q in ReorgTypes + // then clear the deletion mark of Q. Note, at this + // point in the execution ReorgTypes is all the structure + // types. + // + // It would be a bit nuts to allocate memory and hang it + // off of pointer in a structure, but it's still possible. + // Note, if there are no pointers to a structure of a type + // then it is impossible to dynamically allocate memory of + // that type. This of course assumes sane programming + // practices and if they violate those structure reorg has + // every right to punt. + for (std::vector<ReorgType_t>::iterator ri = info->reorg_type->begin (); + ri != info->reorg_type->end (); ri++) + { + for (tree fld = TYPE_FIELDS (ri->gcc_type); fld; fld = DECL_CHAIN (fld)) + { + ReorgType_t *rtype = find_struct_type_ptr_to_struct (fld, info); + if (rtype != NULL) + { + undelete_reorgtype (rtype, info); + } + } + } + + remove_deleted_types (info, &initial_reorg_debug); + + // Disqualifying structures in interior to structures is optional + // (see comment at end of type escape section) but if it's not + // done it commits the optimization to do something a little too + // involved for the initial version. + disqualify_struct_in_struct_or_union (info); + + if (info->reorg_type->empty ()) + { + if (info->show_all_reorg_cands_in_detail) + { + fprintf (dump_file, "find_decls_and_types: Found no types\n"); + } + return false; + } + + // initialize ids of ReorgTypes + int id = 0; + for (std::vector<ReorgType_t>::iterator ri = info->reorg_type->begin (); + ri != info->reorg_type->end (); ri++) + { + ri->id = id; + id++; + } + + // Scan all declarations. If their type is in ReorgTypes + // add them to ProgDecl. + // Note, there is no mechanism for looking at global declarations + // so use FOR_EACH_VARIABLE instead. I'm not 100% this is the thing + // actuall do here... but... + FOR_EACH_VARIABLE (var) + { + tree decl = var->decl; + tree type = base_type_of (decl); + if (TREE_CODE (type) == RECORD_TYPE + && get_reorgtype_info (type, info) != NULL) + { + ProgDecl_t decl_info; + decl_info.gcc_decl = decl; + info->prog_decl->push_back (decl_info); + } + } + + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) + { + // local declarations + unsigned i; + tree decl; + struct function *fn = DECL_STRUCT_FUNCTION (node->decl); + FOR_EACH_LOCAL_DECL (fn, i, decl) + { + tree type = base_type_of (decl); + if (TREE_CODE (type) == RECORD_TYPE + && get_reorgtype_info (type, info) != NULL) + { + ProgDecl_t decl_info; + decl_info.gcc_decl = decl; + info->prog_decl->push_back (decl_info); + } + } + + // parameter declarations + tree parm; + for (parm = DECL_ARGUMENTS (fn->decl); parm; parm = DECL_CHAIN (parm)) + { + tree decl = TREE_TYPE (parm); + tree type = base_type_of (decl); + if (TREE_CODE (type) == RECORD_TYPE + && get_reorgtype_info (type, info) != NULL) + { + ProgDecl_t decl_info; + decl_info.gcc_decl = decl; + info->prog_decl->push_back (decl_info); + } + } + } + + if (info->show_all_reorg_cands_in_detail) + { + fprintf (dump_file, "find_decls_and_types: Found the following types:\n"); + print_reorgs (dump_file, 2, info); + } + + return true; +} + +void +disqualify_struct_in_struct_or_union (Info_t *info) +{ + varpool_node *var; + std::set<tree> typeset; + FOR_EACH_VARIABLE (var) + { + tree decl = var->decl; + tree base = base_type_of (decl); + if (TREE_CODE (base) == RECORD_TYPE || TREE_CODE (base) == UNION_TYPE) + { + disq_str_in_str_or_union_helper (base, &typeset, info); + typeset.insert (base); + } + } + + remove_deleted_types (info, &disqualify_struct_in_struct_or_union_debug); +} + +static void +initial_reorg_debug (Info *info, ReorgType *reorg) +{ + if (info->show_delete) + { + print_reorg_with_msg (dump_file, reorg, 2, "No Pointer to Structure"); + } +} + +static void +disqualify_struct_in_struct_or_union_debug (Info *info, ReorgType *reorg) +{ + if (info->show_delete) + { + print_reorg_with_msg (dump_file, reorg, 2, "Interior Struct or Union"); + } +} + +static void +disq_str_in_str_or_union_helper (tree type, std::set<tree> *typeset, + Info_t *info) +{ + if (typeset->find (type) != typeset->end ()) + return; + tree fld; + for (tree fld = TYPE_FIELDS (type); fld; fld = DECL_CHAIN (fld)) + { + if (TREE_CODE (fld) == RECORD_TYPE) + { + ReorgType_t *rinfo = get_reorgtype_info (fld, info); + if (rinfo != NULL) + { + delete_reorgtype (rinfo, info); + } + else + { + disq_str_in_str_or_union_helper (fld, typeset, info); + typeset->insert (fld); // might be bug here + } + } + else + { + if (TREE_CODE (fld) == UNION_TYPE) + { + disq_str_in_str_or_union_helper (fld, typeset, info); + typeset->insert (fld); // might be bug here + } + } + } + return; +} + +static unsigned int +reorg_qualification (Info_t *info) +{ + // TBD + // This only does a generic legality qualification and each + // subpass does its own performance qualification. +} + +void +delete_reorgtype (ReorgType_t *rt, Info_t *info) +{ + if (!rt->delete_me) + { + info->num_deleted++; + rt->delete_me = true; + } +} + +void +undelete_reorgtype (ReorgType_t *rt, Info_t *info) +{ + if (rt->delete_me) + { + info->num_deleted--; + rt->delete_me = false; + } +} + +void +clear_deleted_types (Info *info) +{ + info->saved_reorg_type->clear (); +} + +void +restore_deleted_types (Info *info) +{ + while (!info->saved_reorg_type->empty ()) + { + info->reorg_type->push_back (info->saved_reorg_type->back ()); + info->saved_reorg_type->pop_back (); + } +} + +void +remove_deleted_types (Info *info, ReorgFn reorg_fn) +{ + if (info->show_delete) + { + fprintf (dump_file, "DELETING REORG TYPES:\n"); + } + + if (info->num_deleted > 0) + { + // Do delete here and run reorg_fn on each + // deleted type + int n = info->reorg_type->size (); + int to = 0; + for (int from = 0; from < n; from++) + { + if (!(*(info->reorg_type))[from].delete_me) + { + // Save copy of removed entry + (*(info->reorg_type))[from].delete_me = false; + info->saved_reorg_type->push_back ((*(info->reorg_type))[from]); + + // Delete by not copying deleted elements + if (from != to) + { + if (reorg_fn != NULL) + { + (*reorg_fn) (info, &(*(info->reorg_type))[from]); + } + (*(info->reorg_type))[to] = (*(info->reorg_type))[from]; + } + to++; + } + } + info->reorg_type->resize (n - info->num_deleted); + info->num_deleted = 0; + } +} + +static tree +base_type_of (tree type) +{ + for (; POINTER_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE + || TREE_CODE (type) == VAR_DECL; + type = TREE_TYPE (type)) + ; + return type; +} + +// There are other allocations such as alloca or +// aligned allocs that I'm pretty sure are not +// a good fit for structure reorg optimization. +// Also, we do handle realloc but it doesn't +// create a new pool of memory so we ignore it here. +static bool +is_reorg_alloc_trigger (gimple *stmt) +{ + return gimple_call_builtin_p (stmt, BUILT_IN_MALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_CALLOC); +} + +static ReorgType_t * +find_struct_type_ptr_to_struct (tree type, Info *info) +{ + if (!POINTER_TYPE_P (type)) + { + return NULL; + } + for (; POINTER_TYPE_P (type); type = TREE_TYPE (type)) + ; + + if (TREE_CODE (type) == RECORD_TYPE) + { + return get_reorgtype_info (type, info); + } + return NULL; +} + +// TBD Garbage just so it will compile +// What's dicey about this is it may sort of work but then I +// can see places where it wouldn't... The language has a say +// in what types are equal so maybe language hooks are involved??? +bool +same_type_p (tree a, tree b) +{ + // DEBUG( "same_type_p:\n"); + // DEBUG( " a: TREE_CODE = %s, name = %p\n + // ",code_str(TREE_CODE(a)),TYPE_NAME(a)); DEBUG_F( print_generic_expr, stderr, + // a, (dump_flags_t)-1); DEBUG( "\n b TREE_CODE = %s, name = %p\n + // ",code_str(TREE_CODE(b)),TYPE_NAME(b)); DEBUG_F( print_generic_expr, stderr, + // b, (dump_flags_t)-1); DEBUG( "\n"); + gcc_assert (TREE_CODE (a) == RECORD_TYPE && TYPE_NAME (a) != 0); + gcc_assert (TREE_CODE (b) == RECORD_TYPE && TYPE_NAME (b) != 0); + return TYPE_NAME (a) == TYPE_NAME (b); +} + +// May need to add secondary map container to +// look them up or even modify the container +// type of ReorgType +static ReorgType_t * +get_reorgtype_info (tree type, Info_t *info) +{ + // Note, I'm going to use the most stupid and slowest possible way + // to do this. The advanage is it will be super easy and almost + // certainly correct. It will also almost certainly need to be + // improved but I get something out there now. + for (std::vector<ReorgType_t>::iterator ri = info->reorg_type->begin (); + ri != info->reorg_type->end (); ri++) + { + // TBD the internal docs lie and same_type_p doesn't exist + // (at least it's not available here at LTO time) + // so this is just a place holder until I can get an answer + // from the gcc community. Note, this is a big issue. + // Remember, the same_type_p here is my own temporary hack. + if (same_type_p (ri->gcc_type, type)) + { + return &(*ri); + } + } + return NULL; +} + +static void +print_reorg_with_msg (FILE *file, ReorgType_t *reorg, int leading_space, + const char *msg) +{ + fprintf (file, "%*s%s:\n", leading_space, "", msg); + print_reorg (file, leading_space + 2, reorg); +} + +static void +dump_reorg (ReorgType_t *reorg) +{ + print_reorg (stderr, 0, reorg); +} + +static void +print_reorgs (FILE *file, int leading_space, Info *info) +{ + for (int i = 0; i < info->reorg_type->size (); i++) + { + print_reorg (file, leading_space, &(*(info->reorg_type))[i]); + } +} + +static void +print_reorg (FILE *file, int leading_space, ReorgType_t *reorg) +{ + // TBD + // note if reorg_perf & regular_perf are nonzero + // synthesize and display absolute_effect & raw_effect + + // Note, the following is a stub. + const char *text + = identifier_to_locale (IDENTIFIER_POINTER (TYPE_NAME (reorg->gcc_type))); + fprintf (file, "%*s{ type:%s, id:%d, ... }\n", leading_space, "", text, + reorg->id); +} + +//-- debugging only -- +static const char * +code_str (enum tree_code tc) +{ + switch (tc) + { + case POINTER_TYPE: + return "POINTER_TYPE"; + case RECORD_TYPE: + return "RECORD_TYPE"; + case UNION_TYPE: + return "UNION_TYPE"; + case ARRAY_TYPE: + return "ARRAY_TYPE"; + case REFERENCE_TYPE: + return "REFERENCE_TYPE"; + case VAR_DECL: + return "VAR_DECL"; + case TYPE_DECL: + return "TYPE_DECL"; + case CONST_DECL: + return "CONST_DECL"; + default: + switch (TREE_CODE_CLASS (tc)) + { + case tcc_type: + return "class type"; + case tcc_declaration: + return "class declaration"; + case tcc_reference: + return "class reference"; + default: + return "unknown class"; + } + } +} + +//---------------- Pass Control Follows ---------------- + +const pass_data pass_data_ipa_structure_reorg = { + SIMPLE_IPA_PASS, /* type */ + "structure-reorg", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + // TV_IPA_SRA, /* tv_id */ // TBD + TV_IPA_STRUCTURE_REORG, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + //( TODO_dump_symtab | TODO_remove_functions ), /* todo_flags_finish */ // ??? + (TODO_update_ssa | TODO_update_address_taken | TODO_cleanup_cfg + | TODO_remove_unused_locals), + /* todo_flags_finish */ // ??? +}; + +class pass_ipa_structure_reorg : public simple_ipa_opt_pass +{ +public: + pass_ipa_structure_reorg (gcc::context *ctxt) + : simple_ipa_opt_pass (pass_data_ipa_structure_reorg, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *) + { + return ((flag_ipa_structure_reorg || flag_ipa_instance_interleave + || flag_ipa_field_reorder || flag_ipa_dead_field_eliminate)); + } + + virtual unsigned int execute (function *) { return ipa_structure_reorg (); } + +}; // class ipa_structure_reorg + +// ipa_opt_pass_d * +simple_ipa_opt_pass * +make_pass_ipa_structure_reorg (gcc::context *ctxt) +{ + return new pass_ipa_structure_reorg (ctxt); +} diff --git a/gcc/ipa-structure-reorg.h b/gcc/ipa-structure-reorg.h new file mode 100644 index 00000000000..910f066b677 --- /dev/null +++ b/gcc/ipa-structure-reorg.h @@ -0,0 +1,154 @@ +/* Interprocedural scalar replacement of aggregates + Copyright (C) 2019-2020 Free Software Foundation, Inc. + + Contributed by Gary Oblock <goblock@marvell.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/>. */ + +typedef struct ReorgType ReorgType_t; +struct ReorgType +{ + unsigned id; + tree gcc_type; + int numbOfGlobalArrays; // Statically allocated only + int numbOfLocalArrays; // Statically allocated only + int numbOfDynmAllocs; + double reorg_perf; + double regular_perf; + bool multi_pool; // single pool if not set + bool delete_me; + tree clone; // the base + tree pointer_rep; // new pointer format (multi-pool) +}; + +typedef struct ProgDecl ProgDecl_t; +struct ProgDecl +{ + tree gcc_decl; +}; + +enum ReorgOpTrans +{ + ReorgT_Address, // "&x[i]" + ReorgT_Pointer, // "a" + ReorgT_Struct, // "s" + ReorgT_Deref, // "*a" + ReorgT_Array, // "x[i]" + ReorgT_Scalar, // "z" + ReorgT_Indirect, // "a->f" + ReorgT_AryDir // "x[i].f" +}; + +enum CompressionControl +{ + Initial, + Subsequent +}; + +enum ReorgTransformation +{ + ReorgT_StrAssign, // "*a = x[i]", "x[i] = y[j]", "s = *a", etc. + ReorgT_ElemAssign, // "a->f = z", "z = x[i].f", etc. + ReorgT_If_Null, // "if(a == 0)..." + ReorgT_If_NotNull, // "if(a != 0)..." + ReorgT_IfPtrEQ, // "if(a == b)..." + ReorgT_IfPtrNE, // "if(a != b)..." + ReorgT_IfPtrLT, // "if(a < b)..." + ReorgT_IfPtrGT, // "if(a > b)..." + ReorgT_IfPtrLE, // "if(a <= b)..." + ReorgT_IfPtrGE, // "if(a >= b)..." + ReorgT_PtrPlusInt, // "a = b + i" + ReorgT_Ptr2Zero, // "a = 0" + ReorgT_PtrDiff, // "i = a - b" + ReorgT_Adr2Ptr, // "a = &x[i]" + ReorgT_PtrNull, // "x = a == 0" + ReorgT_PtrNotNull, // "x = a != 0" + ReorgT_PtrEQ, // "x = a == b" + ReorgT_PtrNE, // "x = a != b" + ReorgT_PtrLT, // "x = a < b" + ReorgT_PtrLE, // "x = a <= b" + ReorgT_PtrGT, // "x = a > b" + ReorgT_PtrGE, // "x = a >= b" + ReorgT_Malloc, // + ReorgT_Calloc, // + ReorgT_Realloc, // + ReorgT_Free, // + Not_Supported +}; + +// Added as design bug fix +typedef struct BoolPair BoolPair_t; +struct BoolPair +{ + bool processed; + bool layout_changed; +}; + +typedef struct Info Info_t; +struct Info +{ + std::vector<ReorgType_t> *reorg_type; + // Added to by remove_deleted_types + std::vector<ReorgType_t> *saved_reorg_type; + std::vector<ProgDecl_t> *prog_decl; + // Gcc doesn't have global decls readily available + // so this holds them + std::map<tree, BoolPair_t> *struct_types; // desing bug fix + int num_deleted; + double total_cache_accesses; + // Debug flags + bool show_all_reorg_cands; + bool show_all_reorg_cands_in_detail; + bool show_delete; + bool show_new_BBs; + bool show_transforms; + bool show_bounds; +}; + +// This will perform a function on the supplied +// reorg type. It's primarily to support debugging. +typedef void (*ReorgFn) (Info *, ReorgType_t *); + +extern int +str_reorg_dead_field_eliminate (Info *); +extern int +str_reorg_field_reorder (Info *); +extern int +str_reorg_instance_interleave (Info *); + +extern void +delete_reorgtype (ReorgType_t *, Info_t *); +extern void +undelete_reorgtype (ReorgType_t *, Info_t *); +extern void +clear_deleted_types (Info *); +extern void +restore_deleted_types (Info *); +extern void +remove_deleted_types (Info *, ReorgFn reorg_fn); + +// I have no intention of leaving this or uses of the +// defined marcos in the code. However, some of uses +// should obviously be converted to dump file information. + +#if 1 +#define DEBUG(...) fprintf (stderr, __VA_ARGS__) +#define DEBUG_F(f, ...) f (__VA_ARGS__) +#else +#define DEBUG(...) +#define DEBUG_F(...) +#endif diff --git a/gcc/ipa-type-escape-analysis.c b/gcc/ipa-type-escape-analysis.c new file mode 100644 index 00000000000..f4db0b6183f --- /dev/null +++ b/gcc/ipa-type-escape-analysis.c @@ -0,0 +1,1029 @@ +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "options.h" +#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-cfg.h" +#include "gimple-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 "gimple.h" +#include "cfg.h" // needed for gimple-iterator.h +#include "gimple-iterator.h" +#include <stdbool.h> + +#include "ipa-str-reorg-utils.h" +#include "ipa-type-escape-analysis.h" + + +// First we need to collect all types + +static bool filter_type (type_map &escape_map, const_tree type); + +void update_escape_info (const_tree type, type_map &escape_map, bool is_escaping, escaping_reason reason); + +static const escaping_reason null_reason = { 0, 0, 0, 0 }; +inline escaping_reason +new_escaping_reason() +{ + return null_reason; +} + +//TODO: +// Should we be using C++? +escaping_reason +union_op(const escaping_reason &left, const escaping_reason &right) +{ + escaping_reason retval; + retval.global_is_visible = left.global_is_visible | right.global_is_visible; + retval.parameter_is_visible = left.parameter_is_visible | right.parameter_is_visible; + retval.return_is_visible = left.return_is_visible | right.return_is_visible;; + retval.type_is_casted = left.type_is_casted | right.type_is_casted;; + return retval; +} + +void +explain_print(escaping_reason reason) +{ + if (reason.global_is_visible) log("global is visible"); + if (reason.parameter_is_visible) log(", parameter is visible"); + if (reason.return_is_visible) log(", return is visible"); + if (reason.type_is_casted) log(", type is casted"); + log("\n"); +} + +void +update_escape_info_pointer (const_tree pointer_type, type_map &escape_map, bool is_escaping, escaping_reason reason) +{ + gcc_assert(pointer_type); + enum tree_code tree_code_pointer_type = TREE_CODE(pointer_type); + bool is_pointer_type = POINTER_TYPE == tree_code_pointer_type; + gcc_assert(is_pointer_type); + + escaping_info *info = escape_map.get(pointer_type); + if (!info) { log("no info for %s\n", get_type_name(pointer_type)); return;} + + info->is_escaping |= is_escaping; + info->reason = union_op(info->reason, reason); + tree tree_type_pointer_type = TREE_TYPE(pointer_type); + gcc_assert(tree_type_pointer_type); + update_escape_info (tree_type_pointer_type, escape_map, info->is_escaping, info->reason); +} + +void +update_escape_info_array (const_tree array_type, type_map &escape_map, bool is_escaping, escaping_reason reason) +{ + gcc_assert(array_type); + enum tree_code tree_code_array_type = TREE_CODE(array_type); + bool is_array_type = ARRAY_TYPE == tree_code_array_type; + gcc_assert(is_array_type); + + escaping_info *info = escape_map.get(array_type); + if (!info) { log("no info for %s\n", get_type_name(array_type)); return;} + + info->is_escaping |= is_escaping; + info->reason = union_op(info->reason, reason); + tree tree_type_array_type = TREE_TYPE(array_type); + gcc_assert(tree_type_array_type); + update_escape_info (tree_type_array_type, escape_map, info->is_escaping, info->reason); +} + +void +update_escape_info_record (const_tree record_type, type_map &escape_map, bool is_escaping, escaping_reason reason) +{ + gcc_assert(record_type); + enum tree_code tree_code_record_type = TREE_CODE(record_type); + bool is_record_type = RECORD_TYPE == tree_code_record_type; + gcc_assert(is_record_type); + + escaping_info *info = escape_map.get(record_type); + // we are collecting records, therefore, we **must** have + // it in the escaping info + //FIXME: Is this true? According to creduce bug, no. + gcc_assert(info); + info->is_escaping |= is_escaping; + info->reason = union_op(info->reason, reason); + + + for (tree field = TYPE_FIELDS (record_type); field; field = DECL_CHAIN (field)) + { + gcc_assert(field); + tree tree_type_field = TREE_TYPE(field); + gcc_assert(tree_type_field); + update_escape_info (tree_type_field, escape_map, info->is_escaping, info->reason); + } +} + +void +update_escape_info (const_tree type, type_map &escape_map, bool is_escaping, escaping_reason reason) +{ + // INFO: This is an optimization. + if (!is_escaping) { log("is not escaping\n"); return; } + + gcc_assert(type); + enum tree_code tree_code_type = TREE_CODE(type); + // We need to make sure that we are only dealing with + // the main type and not typedefs... + switch (tree_code_type) + { + case ARRAY_TYPE: + update_escape_info_array(type, escape_map, is_escaping, reason); + break; + case POINTER_TYPE: + update_escape_info_pointer(type, escape_map, is_escaping, reason); + break; + case RECORD_TYPE: + update_escape_info_record(type, escape_map, is_escaping, reason); + break; + default: + break; + } +} + +static bool +filter_pointer (type_map &escape_map, const_tree pointer) +{ + enum tree_code code = TREE_CODE (pointer); + gcc_assert(POINTER_TYPE == code); + const_tree base_type = TREE_TYPE(pointer); + return filter_type (escape_map, base_type); +} + +static bool +filter_array (type_map &escape_map, const_tree array) +{ + enum tree_code code = TREE_CODE (array); + gcc_assert(ARRAY_TYPE == code); + const_tree base_type = TREE_TYPE(array); + return filter_type (escape_map, base_type); +} + +static bool +filter_record (type_map &escape_map, const_tree record) +{ + log("filtering record %s\n", get_type_name(record)); + gcc_assert(record); + enum tree_code code = TREE_CODE(record); + gcc_assert(RECORD_TYPE == code); + + for (tree field = TYPE_FIELDS (record); field; field = DECL_CHAIN (field)) + { + tree field_type = TREE_TYPE(field); + gcc_assert(field_type); + filter_type(escape_map, field_type); + } + + return false; +} + +static bool +filter_type (type_map &escape_map, const_tree type) +{ + log("inside filter_type\n"); + + if (!type) return true; + + bool retval = true; + enum tree_code code = TREE_CODE(type); + switch (code) + { + case ARRAY_TYPE: retval = filter_array(escape_map, type); break; + case POINTER_TYPE: retval = filter_pointer(escape_map, type); break; + case RECORD_TYPE: retval = filter_record(escape_map, type); break; + default: break; + } + + + log("filter_type %s boring ? %s\n", get_type_name(type), retval ? "true" : "false"); + if (retval) return retval; + escaping_reason reason = new_escaping_reason(); + escaping_info info = { type , false , reason}; + escape_map.put(type, info); + return retval; +} + +static bool +filter_var_decl (type_map &escape_map, const_tree var_decl) +{ + log("filtering var_decl\n"); + gcc_assert(var_decl); + enum tree_code code = TREE_CODE(var_decl); + gcc_assert(code == VAR_DECL); + tree type = TREE_TYPE (var_decl); + gcc_assert(type); + return filter_type(escape_map, type); +} + +static bool +filter_parm_declarations (const_tree parm_decl, type_map &escape_map) +{ + gcc_assert(parm_decl); + enum tree_code code = TREE_CODE(parm_decl); + gcc_assert(PARM_DECL == code); + tree type = TREE_TYPE (parm_decl); + gcc_assert(type); + return filter_type(escape_map, type); +} + +static void +collect_global(type_map &escape_map, varpool_node *vnode) +{ + log("collect_global\n"); + gcc_assert(vnode); + + struct ipa_ref *ref = NULL; + for (int i = 0; vnode->iterate_referring (i, ref); i++) + { + log("inside vnode loop\n"); + tree decl = vnode->decl; + gcc_assert(decl); + enum tree_code code = TREE_CODE(decl); + gcc_assert(VAR_DECL == code); + bool filter_out = filter_var_decl(escape_map, decl); + if (filter_out) continue; + + tree type = TREE_TYPE(decl); + gcc_assert(type); + log("collecting global type escape analysis %s\n", get_type_name(type)); + escaping_reason reason = new_escaping_reason(); + escaping_info info = { type, false, reason }; + escape_map.put (type, info); + //escape_map.put (TYPE_MAIN_VARIANT(type), info); + } +} + +static void +collect_globals(type_map &escape_map) +{ + varpool_node *vnode; + FOR_EACH_VARIABLE (vnode) + { + collect_global(escape_map, vnode); + } +} + +static void +collect_parm_declarations (cgraph_node *cnode, type_map &escape_map) +{ + gcc_assert(cnode); + log("does function %s have parameters?\n", cnode->name()); + for (tree parm = DECL_ARGUMENTS (cnode->decl); parm; parm = DECL_CHAIN (parm)) + { + tree type = TREE_TYPE(parm); + log("about to enter parameter type %s\n", get_type_name(type)); + bool filter_out = filter_parm_declarations (parm, escape_map); + if (filter_out) continue; + + log("putting parameter type %s\n", get_type_name(type)); + gcc_assert(type); + + escaping_reason reason = new_escaping_reason(); + escaping_info info = { type, false, reason }; + escape_map.put(type, info); + } +} + +static void +collect_local_declarations (cgraph_node *cnode, type_map &escape_map) +{ + gcc_assert(cnode); + int i = 0; + function *func = DECL_STRUCT_FUNCTION (cnode->decl); + cnode->get_untransformed_body (); + tree var_decl = NULL; + FOR_EACH_LOCAL_DECL (func, i, var_decl) + { + gcc_assert(var_decl); + bool filter_out = filter_var_decl(escape_map, var_decl); + if (filter_out) continue; + + tree tree_type = TREE_TYPE(var_decl); + gcc_assert(tree_type); + escaping_reason reason = new_escaping_reason(); + escaping_info info = { tree_type, false, reason}; + escape_map.put(tree_type, info); + } +} + +static void collect_expr (const_tree expr, type_map &escape_map); + +static void +collect_expr_in_component_ref(const_tree cref, type_map &escape_map) +{ + gcc_assert(cref); + const enum tree_code code = TREE_CODE(cref); + const bool is_cref = COMPONENT_REF == code; + gcc_assert(is_cref); + + const_tree _struct = TREE_OPERAND(cref, 0); + gcc_assert(_struct); + + const_tree _struct_type = TREE_TYPE(_struct); + log("we are in collect_expr_in_component_ref %s\n", get_type_name(_struct_type)); + escaping_reason reason = new_escaping_reason(); + escaping_info info = { _struct_type, false , reason}; + escape_map.put(_struct_type, info); + + collect_expr(_struct, escape_map); + +} + +static void +collect_expr (const_tree expr, type_map &escape_map) +{ + gcc_assert(expr); + enum tree_code tree_code_expr = TREE_CODE(expr); + switch (tree_code_expr) + { + case COMPONENT_REF: + collect_expr_in_component_ref(expr, escape_map); + break; + default: + break; + } +} + +static void +collect_pointer_plus (tree lhs, tree rhs1, tree rhs2, type_map &escape_map) +{ + log("collect_pointer_plus\n"); + const_tree lhs_type = lhs ? TREE_TYPE(lhs) : NULL; + bool is_lhs_boring = lhs_type ? filter_type(escape_map, lhs_type) : true; + const_tree rhs1_type = rhs1 ? TREE_TYPE(rhs1) : NULL; + bool is_rhs1_boring = rhs1_type ? filter_type(escape_map, rhs1_type) : true; + const_tree rhs2_type = rhs2 ? TREE_TYPE(rhs2) : NULL; + bool is_rhs2_boring = rhs2_type ? filter_type(escape_map, rhs2_type) : true; + + if (!is_lhs_boring) + { + escaping_reason reason = new_escaping_reason(); + escaping_info info = { lhs_type , false, reason }; + escape_map.put(lhs_type, info); + } + if (!is_rhs1_boring) + { + collect_expr(rhs1, escape_map); + } + if (!is_rhs2_boring) + { + collect_expr(rhs2, escape_map); + } +} + +static void +collect_assign_rhs (gimple *stmt, type_map &escape_map) +{ + enum tree_code code = gimple_expr_code (stmt); + switch (code) + { + case POINTER_PLUS_EXPR: + case POINTER_DIFF_EXPR: + case COMPONENT_REF: + { + tree rhs2 = gimple_assign_rhs2(stmt); + tree rhs1 = gimple_assign_rhs1(stmt); + tree lhs = gimple_assign_lhs (stmt); + collect_pointer_plus (lhs, rhs1, rhs2, escape_map); + break; + } + default: + break; + } +} + +static void +collect_assign (gimple *stmt, type_map &escape_map) +{ + gcc_assert(stmt); + collect_assign_rhs (stmt, escape_map); +} + +static void +collect_call_lhs (gimple *stmt, type_map &escape_map) +{ + gcc_assert(stmt); + tree lhs = gimple_call_lhs (stmt); + if (!lhs) return; + + collect_expr (lhs, escape_map); +} + +static void +collect_call_rhs (gimple *stmt, type_map &escape_map) +{ + gcc_assert(stmt); + tree fn = gimple_call_fn (stmt); + + gcc_assert (fn); + gcall *call = dyn_cast <gcall *> (stmt); + //struct cgraph_node *n = cgraph_node::get (fn); + //if (n) log ("collecting arguments for %s \n", n->name()); + + tree return_type = gimple_call_return_type (call); + bool is_return_type_boring = filter_type(escape_map, return_type); + + if (!is_return_type_boring) { + escaping_reason reason = new_escaping_reason(); + escaping_info info = {return_type, false, reason}; + escape_map.put(return_type, info); + } + + unsigned args = gimple_call_num_args (stmt); + for (unsigned i = 0; i < args; i++) + { + tree arg = gimple_call_arg (stmt, i); + tree arg_type = TREE_TYPE(arg); + log("collecting argument of type %s\n", get_type_name(arg_type)); + bool is_arg_boring = filter_type(escape_map, arg_type); + if (is_arg_boring) continue; + + escaping_reason reason = new_escaping_reason(); + escaping_info info = {arg_type, false, reason}; + escape_map.put(arg_type, info); + + } +} + +static void +collect_call(gimple *stmt, type_map &escape_map) +{ + gcc_assert(stmt); + collect_call_lhs (stmt, escape_map); + collect_call_rhs (stmt, escape_map); +} + +static void +collect_stmt (gimple *stmt, type_map &escape_map) +{ + gcc_assert (stmt); + const enum gimple_code code = gimple_code (stmt); + switch (code) + { + case GIMPLE_ASSIGN: + collect_assign(stmt, escape_map); + break; + case GIMPLE_CALL: + collect_call(stmt, escape_map); + break; + default: + break; + } + + return; +} + +static void +collect_basic_block (basic_block bb, type_map &escape_map) +{ + gcc_assert(bb); + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next(&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + collect_stmt (stmt, escape_map); + } +} + +static void +collect_function_body (cgraph_node *cnode, type_map &escape_map) +{ + gcc_assert (cnode); + cnode->get_untransformed_body(); + basic_block bb = NULL; + tree decl = cnode->decl; + gcc_assert(decl); + function *func = DECL_STRUCT_FUNCTION (decl); + gcc_assert(func); + push_cfun(func); + FOR_EACH_BB_FN (bb, func) + { + gcc_assert(bb); + collect_basic_block (bb, escape_map); + } + pop_cfun(); +} + +static void +collect_return_type (cgraph_node *cnode, type_map &escape_map) +{ + gcc_assert(cnode); + + const_tree decl = cnode->decl; + const enum tree_code code = TREE_CODE(decl); + const bool is_function_decl = FUNCTION_DECL == code; + gcc_assert (is_function_decl); + + const_tree fn_type = TREE_TYPE (decl); + const enum tree_code fn_type_code = TREE_CODE(fn_type); + const bool is_fn_type = FUNCTION_TYPE == fn_type_code; + gcc_assert (is_fn_type); + + const_tree ret_type = TREE_TYPE(fn_type); + gcc_assert (ret_type); + + const bool is_boring = filter_type(escape_map, ret_type); + if (is_boring) return; + + escaping_reason reason = new_escaping_reason(); + escaping_info info = {ret_type, false, reason}; + escape_map.put(ret_type, info); + return; +} + +static void +collect_types(type_map &escape_map) +{ + collect_globals(escape_map); + + cgraph_node *cnode = NULL; + FOR_EACH_DEFINED_FUNCTION (cnode) + { + collect_function_body (cnode, escape_map); + collect_local_declarations (cnode, escape_map); + collect_parm_declarations (cnode, escape_map); + collect_return_type (cnode, escape_map); + } +} + +bool +_print_types( const_tree const &type, escaping_info *info, __attribute__((unused)) void*) +{ + log("collected,%s\n", get_type_name(type)); + bool is_escaping = info->is_escaping; + log("type %s is escaping %s : ", get_type_name(type), is_escaping ? "true" : "false"); + explain_print(info->reason); + return true; +} + +static void +print_types(type_map &escape_map) +{ + log("printing_types\n"); + escape_map.traverse<void*, _print_types> (NULL); +} + +static bool +is_variable_escaping(varpool_node *vnode) +{ + gcc_assert(vnode); + return vnode->externally_visible; +} + +static bool +is_function_escaping(const cgraph_node *cnode) +{ + gcc_assert(cnode); + return cnode->externally_visible; /* TODO: How are FOR_EACH_FUNCTION and FOR_EACH_DEFINED_FUNCTION different */ // || !cnode->definition; +} + +void calculate_escaping_parameters(const cgraph_node *cnode, type_map &escape_map, bool override = false); + +void +calculate_escaping_parameters(const cgraph_node *cnode, type_map &escape_map, bool override) +{ + gcc_assert(cnode); + tree function = cnode->decl; + gcc_assert(function); + enum tree_code code = TREE_CODE (function); + bool is_function_decl = FUNCTION_DECL == code; + gcc_assert (is_function_decl); + + bool is_escaping = is_function_escaping(cnode) || override; + function_args_iterator iter; + tree arg; + const_tree function_type = TREE_TYPE(function); + FOREACH_FUNCTION_ARGS (function_type, arg, iter) + { + //const_tree main_type = TYPE_MAIN_VARIANT(arg); + log("parameter type %s is escaping %s in function %s\n", get_type_name(arg), is_escaping ? "true" : "false", cnode->name()); + escaping_reason reason = new_escaping_reason(); + reason.parameter_is_visible = is_escaping; + update_escape_info(arg, escape_map, is_escaping, reason); + } +} +void is_return_type_escaping(const cgraph_node *cnode, type_map &escape_map, bool override = false); + +void +is_return_type_escaping(const cgraph_node *cnode, type_map &escape_map, bool override) +{ + gcc_assert(cnode); + bool is_escaping = is_function_escaping(cnode) || override; + tree function = cnode->decl; + gcc_assert(function); + enum tree_code code = TREE_CODE(function); + bool is_function = FUNCTION_DECL == code; + gcc_assert(is_function); + + tree tree_type = TREE_TYPE(function); + gcc_assert(tree_type); + tree return_type = TREE_TYPE(tree_type); + gcc_assert(return_type); + log("return type %s\n", get_type_name(return_type)); + + // void update_escaping_info (const_tree type, type_map &escape_map, bool is_escaping); +/*struct escaping_reason_s { + unsigned global_is_visible : 1; + unsigned function_is_visible : 1; +}; +*/ + escaping_reason reason = new_escaping_reason(); + reason.return_is_visible = is_escaping; + update_escape_info(return_type, escape_map, is_escaping, reason); + //escaping_info *info = escape_map.get(return_type); + // If there's no info it means it is not interesting... + //if (!info) return; + //info->is_escaping |= is_escaping; +} + + +bool +calculate_escaping_types_from_function_signatures ( + cgraph_node * const &cnode, type_map *escape_map) +{ + + gcc_assert(escape_map); + gcc_assert(cnode); + bool is_escaping = is_function_escaping(cnode); + log("function %s is escaping %s\n", cnode->name(), is_escaping ? "true" : "false"); + calculate_escaping_parameters(cnode, *escape_map, is_escaping); + is_return_type_escaping(cnode, *escape_map, is_escaping); + return true; +} + +void +find_calls_to_undefined_functions_from_call(gimple *stmt, hash_set<tree> &set, type_map &escape_map) +{ + gcc_assert(stmt); + const enum gimple_code code = gimple_code(stmt); + const bool is_gimple_call = GIMPLE_CALL == code; + gcc_assert(is_gimple_call); + + gcall *call = dyn_cast<gcall *>(stmt); + tree fn = gimple_call_fndecl(stmt); + + // Function pointer? + if (!fn) return; + + // Do we ever see a function with the name __xstat? + gcc_assert(fn); + cgraph_node *n = cgraph_node::get (fn); + //gcc_assert(n); + log("here2 %s\n", n ? n->name() : "there's no cnode!"); + log("is contained in set ? %s\n", set.contains(fn) ? "true" : "false"); + + // if fn is in set... then it means that it is undefined. + if (!set.contains(fn)) return; + + unsigned num_arguments = gimple_call_num_args(call); + for (unsigned i = 0; i < num_arguments; i++) + { + tree argument = gimple_call_arg(stmt, i); + /* + // TODO: Why is this the case? + gimple *def_for_arg = SSA_NAME_DEF_STMT(argument); + gcc_assert(def_for_arg); + const enum gimple_code code2 = gimple_code(def_for_arg); + const bool is_gimple_call2 = GIMPLE_CALL == code2; + const bool is_gimple_assign = GIMPLE_ASSIGN == code2; + const bool is_assignable = is_gimple_call2 ^ is_gimple_assign; + gcc_assert(is_assignable); + + tree arg_var = is_gimple_assign ? gimple_assign_lhs(def_for_arg) : gimple_call_lhs(def_for_arg); + gcc_assert(arg_var); + */ + tree arg_type = TREE_TYPE(argument); + gcc_assert(arg_type); + + bool is_escaping = true; + escaping_reason reason = new_escaping_reason(); + reason.parameter_is_visible = is_escaping; + update_escape_info(arg_type, escape_map, is_escaping, reason); + } +} + +void +find_calls_to_undefined_functions_from_stmt(gimple *stmt, hash_set<tree> &set, type_map &escape_map) +{ + gcc_assert(stmt); + const enum gimple_code code = gimple_code (stmt); + const bool is_gimple_call = GIMPLE_CALL == code; + if (!is_gimple_call) return; + + find_calls_to_undefined_functions_from_call(stmt, set, escape_map); +} + + +void +find_calls_to_undefined_functions_from_bb(basic_block bb, hash_set<tree> &set, type_map &escape_map) +{ + gcc_assert(bb); + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + find_calls_to_undefined_functions_from_stmt(stmt, set, escape_map); + } +} + +void +find_calls_to_undefined_functions_from_function(cgraph_node *cnode, hash_set<tree> &set, type_map &escape_map) +{ + gcc_assert(cnode); + cnode->get_untransformed_body (); + log("%s is an undefined function\n", cnode->name()); + function *func = DECL_STRUCT_FUNCTION(cnode->decl); + basic_block bb = NULL; + push_cfun(func); + FOR_EACH_BB_FN(bb, func) + { + find_calls_to_undefined_functions_from_bb(bb, set, escape_map); + } + pop_cfun(); +} + +void +is_any_function_escaping(type_map &escape_map) +{ + cgraph_node *cnode = NULL; + hash_set<tree> not_defined_functions; + + FOR_EACH_FUNCTION (cnode) + { + gcc_assert(cnode); + const char* _free = "free"; + //const char* _memset = "memset"; + const char* _malloc = "malloc"; + const char* _realloc = "realloc"; + const char* _calloc= "calloc"; + const char* ob_name = cnode->name(); + //log("for each function %s\n", cnode->name()); // We do see __xstat... + if (strcmp(ob_name, _free) == 0) continue; + //if (strcmp(ob_name, _memset) == 0) continue; + if (strcmp(ob_name, _malloc) == 0) continue; + if (strcmp(ob_name, _realloc) == 0) continue; + if (strcmp(ob_name, _calloc) == 0) continue; + + not_defined_functions.add(cnode->decl); + } + + + FOR_EACH_DEFINED_FUNCTION(cnode) + { + gcc_assert(cnode); + cnode->get_untransformed_body(); + not_defined_functions.remove(cnode->decl); + //log("for each defined function %s\n", cnode->name()); // We don't see __xstat... + calculate_escaping_types_from_function_signatures (cnode, &escape_map); + } + + // not_defined_functions.traverse<type_map *, calculate_escaping_types_from_function_signatures> (&escape_map); + //TODO: Walk gimple code and identify GIMPLE_CALL to see if function is not defined... + FOR_EACH_DEFINED_FUNCTION(cnode) + { + find_calls_to_undefined_functions_from_function(cnode, not_defined_functions, escape_map); + } +} + +void +is_any_variable_escaping(type_map &escape_map) +{ + varpool_node *vnode = NULL; + // Global variables + FOR_EACH_VARIABLE(vnode) + { + bool is_escaping = is_variable_escaping(vnode); + log("variable %s is escaping %s\n", vnode->name(), is_escaping ? "true" : "false"); + tree decl = vnode->decl; + gcc_assert(decl); + tree type = TREE_TYPE(decl); + gcc_assert(type); + // void update_escaping_info (const_tree type, type_map &escape_map, bool is_escaping); + escaping_reason reason = new_escaping_reason(); + reason.global_is_visible = is_escaping; + update_escape_info (type, escape_map, is_escaping, reason); + //escaping_info *info = escape_map.get(type); + //if (!info) return; + //info->is_escaping |= is_escaping; + } +} + +//TODO: place in header file +inline static void +print_function (cgraph_node *cnode) +{ + if (!dump_file) + return; + gcc_assert (cnode); + cnode->get_untransformed_body (); + dump_function_to_file (cnode->decl, dump_file, TDF_NONE); +} + +static bool +cast_to_void_in_assign(const_tree lhs, const_tree rhs, type_map &escape_map) +{ + gcc_assert(lhs); + gcc_assert(rhs); + const_tree tree_type_lhs = TREE_TYPE(lhs); + gcc_assert(tree_type_lhs); + const_tree tree_type_rhs = TREE_TYPE(rhs); + gcc_assert(tree_type_rhs); + const char* const type_name_lhs = get_type_name(tree_type_lhs); + const char* const type_name_rhs = get_type_name(tree_type_rhs); + enum tree_code tree_code_rhs = TREE_CODE(rhs); + bool is_ssa_name = SSA_NAME == tree_code_rhs; + bool is_var_decl = VAR_DECL == tree_code_rhs; + bool is_var_decl_or_ssa_name = is_ssa_name || is_var_decl; + log("lhs = %s, rhs = %s\n", type_name_lhs, type_name_rhs); + if (!is_var_decl_or_ssa_name) return false; + + // We need to find out the base... + const_tree base_type_lhs = get_base_type(tree_type_lhs); + gcc_assert(base_type_lhs); + const_tree base_type_rhs = get_base_type(tree_type_rhs); + gcc_assert(base_type_rhs); + // TODO: FIXME: + // Is this always correct? I think this can only happen + // if we are mallocing and friends... + enum tree_code tree_code_base_type_rhs = TREE_CODE(base_type_rhs); + if (tree_code_base_type_rhs == VOID_TYPE) return false; + + bool is_casting_stmt = TYPE_MAIN_VARIANT(base_type_lhs) != TYPE_MAIN_VARIANT(base_type_rhs); + log("is casting stmt ? %s\n", is_casting_stmt ? "true" : "false"); + if (!is_casting_stmt) return false; + + log("escaping lhs %s\n", type_name_lhs); + escaping_reason reason = new_escaping_reason(); + reason.type_is_casted = is_casting_stmt; + update_escape_info(tree_type_lhs, escape_map, is_casting_stmt, reason); + + log("escaping rhs %s\n", type_name_rhs); + update_escape_info(tree_type_rhs, escape_map, is_casting_stmt, reason); + return true; +} + +static void +cast_to_void_in_assign (gimple *stmt, type_map &escape_map) +{ + enum gimple_code gcode = gimple_code (stmt); + const char* const gcode_str = gimple_code_name[gcode]; + gcc_assert(gcode_str); + + switch (gimple_assign_rhs_class (stmt)) + { + case GIMPLE_SINGLE_RHS: + case GIMPLE_UNARY_RHS: + { + tree lhs = gimple_assign_lhs(stmt); + tree rhs = gimple_assign_rhs1(stmt); + bool retval = cast_to_void_in_assign(lhs, rhs, escape_map); + if (!retval) break; + + tree tree_type_lhs = TYPE_MAIN_VARIANT(TREE_TYPE(lhs)); + tree tree_type_rhs = TYPE_MAIN_VARIANT(TREE_TYPE(rhs)); + const char* const type_name_lhs = get_type_name(tree_type_lhs); + const char* const type_name_rhs = get_type_name(tree_type_rhs); + log("type casting %s != %s ", type_name_lhs, type_name_rhs); + if (dump_file) print_gimple_stmt (dump_file, stmt, 0, TDF_NONE); + log("\n"); + } + default: + break; + } +} + +static void +cast_to_void_in_stmt (gimple *stmt, type_map &escape_map) +{ + gcc_assert (stmt); + const enum gimple_code code = gimple_code (stmt); + switch (code) + { + case GIMPLE_ASSIGN: + cast_to_void_in_assign(stmt, escape_map); + break; + default: + break; + } + + return; +} + +static void +cast_to_void_in_bb(basic_block bb, type_map &escape_map) +{ + gcc_assert(bb); + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next(&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + cast_to_void_in_stmt (stmt, escape_map); + } +} + +static void +cast_to_void_in_function(cgraph_node *cnode, type_map &escape_map) +{ + gcc_assert (cnode); + print_function (cnode); + basic_block bb = NULL; + tree decl = cnode->decl; + gcc_assert(decl); + function *func = DECL_STRUCT_FUNCTION (decl); + gcc_assert(func); + push_cfun(func); + FOR_EACH_BB_FN (bb, func) + { + gcc_assert(bb); + cast_to_void_in_bb(bb, escape_map); + } + pop_cfun(); +} + +static void +cast_to_void_in_program(type_map &escape_map) +{ + cgraph_node *cnode = NULL; + FOR_EACH_DEFINED_FUNCTION (cnode) + { + gcc_assert(cnode); + cnode->get_untransformed_body(); + cast_to_void_in_function(cnode, escape_map); + } +} + +/* INFO: + * Yes, I know we are returning a std::map. + * Bad pattern? Maybe, but this will only be called once + * and I rather pass by value because that allows + * me to have a pure function and not worry about + * garbage collection + * + * TODO: I'd like to change type_map for a std::map + * TODO: I'd like to make this a template that can work + * for std::map and std::set + */ +void +calculate_escaping_types(type_map &escape_map) +{ + collect_types(escape_map); + is_any_variable_escaping(escape_map); + is_any_function_escaping(escape_map); + cast_to_void_in_program(escape_map); + print_types(escape_map); +} + +static unsigned int +iphw_execute() +{ + // We can ignore the return value here... + type_map escape_map; + calculate_escaping_types(escape_map); + return 0; +} + + +namespace { +const pass_data pass_data_ipa_type_escape_analysis = +{ + SIMPLE_IPA_PASS, + "type-escape-analysis", + OPTGROUP_NONE, + TV_NONE, + (PROP_cfg | PROP_ssa), + 0, + 0, + 0, + 0, +}; + +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) + {} + + virtual bool gate(function*) { return in_lto_p && flag_ipa_type_escape_analysis; } + virtual unsigned execute (function*) { return iphw_execute(); } +}; +} // anon namespace + +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..7dcda948189 --- /dev/null +++ b/gcc/ipa-type-escape-analysis.h @@ -0,0 +1,21 @@ +#ifndef GCC_IPA_TYPE_ESCAPE_ANALYSIS_H +#define GCC_IPA_TYPE_ESCAPE_ANALYSIS_H +#pragma once + +struct escaping_reason_s { + unsigned global_is_visible : 1; + unsigned parameter_is_visible : 1; + unsigned return_is_visible : 1; + unsigned type_is_casted : 1; +}; +typedef struct escaping_reason_s escaping_reason; + +struct escaping_info_s { const_tree type; bool is_escaping; escaping_reason reason; }; +typedef struct escaping_info_s escaping_info; +//TODO: Maybe change this to a std::map? +//Can we get an idea of what conventions gcc want? +//Maybe I should ask... +typedef hash_map<const_tree, escaping_info> type_map; +void calculate_escaping_types(type_map&); + +#endif diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h index 76aa6fe34b8..6dd64e2e455 100644 --- a/gcc/lto-streamer.h +++ b/gcc/lto-streamer.h @@ -237,7 +237,7 @@ enum lto_section_type LTO_section_ipa_hsa, LTO_section_lto, LTO_section_ipa_sra, - LTO_N_SECTION_TYPES /* Must be last. */ + LTO_N_SECTION_TYPES /* Must be last. */ }; /* Indices to the various function, type and symbol streams. */ diff --git a/gcc/passes.def b/gcc/passes.def index 853e512e227..e0d2711c432 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -149,6 +149,8 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_ipa_profile); NEXT_PASS (pass_ipa_icf); NEXT_PASS (pass_ipa_devirt); + NEXT_PASS (pass_ipa_type_escape_analysis); + NEXT_PASS (pass_ipa_hello_world); NEXT_PASS (pass_ipa_escape_analysis); NEXT_PASS (pass_ipa_cp); NEXT_PASS (pass_ipa_sra); @@ -172,6 +174,7 @@ along with GCC; see the file COPYING3. If not see compiled unit. */ INSERT_PASSES_AFTER (all_late_ipa_passes) NEXT_PASS (pass_materialize_all_clones); + NEXT_PASS (pass_ipa_structure_reorg); NEXT_PASS (pass_ipa_pta); NEXT_PASS (pass_omp_simd_clone); TERMINATE_PASS_LIST (all_late_ipa_passes) 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..88f96ea3be4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-00-simple-read-0.c @@ -0,0 +1,19 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-hello-world -fdump-ipa-hello-world" } */ + +#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); +} + +/* { dg-final { scan-wpa-ipa-dump "collected,astruct_s" "hello-world" } } */ +/* { dg-final { scan-wpa-ipa-dump "astruct_s.a read 1" "hello-world" } } */ +/* { dg-final { scan-wpa-ipa-dump "astruct_s.a read 2" "hello-world" } } */ 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..6e7b3236294 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-01-simple-write-0.c @@ -0,0 +1,19 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-hello-world -fdump-ipa-hello-world" } */ + +#include <stdio.h> + +struct astruct_s { _Bool a; _Bool b; _Bool c;}; +struct astruct_s astruct; + + +int +main () +{ + astruct.a++; + astruct.a = 3; +} + +/* { dg-final { scan-wpa-ipa-dump "collected,astruct_s" "hello-world" } } */ +/* { dg-final { scan-wpa-ipa-dump "astruct_s.a write 1" "hello-world" } } */ +/* { dg-final { scan-wpa-ipa-dump "astruct_s.a write 2" "hello-world" } } */ 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..1b6755565ff --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-02-pointer-read-0.c @@ -0,0 +1,19 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-hello-world -fdump-ipa-hello-world" } */ + +#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); +} + +/* { dg-final { scan-wpa-ipa-dump "collected,astruct_s" "hello-world" } } */ +/* { dg-final { scan-wpa-ipa-dump "astruct_s.a read 1" "hello-world" } } */ +/* { dg-final { scan-wpa-ipa-dump "astruct_s.a read 2" "hello-world" } } */ 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..68e2f40a12a --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-03-pointer-write-0.c @@ -0,0 +1,19 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-hello-world -fdump-ipa-hello-world" } */ + +#include <stdio.h> + +struct astruct_s { _Bool a; _Bool b; _Bool c;}; +struct astruct_s *astruct; + + +int +main () +{ + astruct->a++; + astruct->a = 3; +} + +/* { dg-final { scan-wpa-ipa-dump "collected,astruct_s" "hello-world" } } */ +/* { dg-final { scan-wpa-ipa-dump "astruct_s.a write 1" "hello-world" } } */ +/* { dg-final { scan-wpa-ipa-dump "astruct_s.a write 2" "hello-world" } } */ 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..0dd152a2783 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-04-gimple-cond-0.c @@ -0,0 +1,19 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-hello-world -fdump-ipa-hello-world" } */ + +#include <stdio.h> + +struct astruct_s { _Bool a; _Bool b; _Bool c;}; +struct astruct_s astruct; + + +int +main () +{ + if (astruct.a) { + puts("hello world"); + } +} + +/* { dg-final { scan-wpa-ipa-dump "collected,astruct_s" "hello-world" } } */ +/* { dg-final { scan-wpa-ipa-dump "astruct_s.a read 1" "hello-world" } } */ 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..f9c4af5d6cf --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-00-collect-global-record-0.c @@ -0,0 +1,13 @@ +/* { dg-do link } */ +/* { dg-options "-fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } */ + +struct astruct_s { _Bool a; _Bool b; _Bool c;}; +struct astruct_s astruct; + +int +main () +{ + astruct.a = 0; +} + +/* { dg-final { scan-ipa-dump "collected,astruct_s" "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..e1147844f18 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-01-collect-global-pointers-to-record-0.c @@ -0,0 +1,16 @@ +/* { dg-do link } */ +/* { dg-options "-fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } */ + +#include <stddef.h> + +struct astruct_s { _Bool a; _Bool b; _Bool c;}; +struct astruct_s *astruct; + +int +main () +{ + astruct = NULL; +} + +/* { dg-final { scan-ipa-dump "collected,astruct_s" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "collected,astruct_s*" "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..aed61d4ef69 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-02-collect-global-array-to-record-0.c @@ -0,0 +1,16 @@ +/* { dg-do link } */ +/* { dg-options "-fipa-type-escape-analysis -fdump-ipa-type-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]; +} + +/* { dg-final { scan-ipa-dump "collected,astruct_s" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "collected,astruct_s[]" "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..e300b0b598a --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-03-collect-nested-record-0.c @@ -0,0 +1,20 @@ +/* { dg-do link } */ +/* { dg-options "-fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } */ + +#include <stddef.h> + +struct astruct_s { _Bool a; _Bool b; _Bool c;}; +struct astruct_s astruct[2]; +struct outer_struct { _Bool d; struct astruct_s a; }; +struct outer_struct bstruct; + +int +main () +{ + bstruct.d = 0; + struct astruct_s another = astruct[0]; +} + +/* { dg-final { scan-ipa-dump "collected,astruct_s" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "collected,outer_struct" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "collected,astruct_s[]" "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..7b40c7701bf --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-04-collect-parameters-0.c @@ -0,0 +1,20 @@ +/* { dg-do link } */ +/* { dg-options "-fipa-type-escape-analysis -fdump-ipa-type-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 () +{ +} + +/* { dg-final { scan-ipa-dump "collected,astruct_s" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "collected,astruct_s*" "type-escape-analysis" } } */ +/* { dg-final { scan-ipa-dump "collected,astruct_s[]" "type-escape-analysis" } } */ 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..32a77195322 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-05-global-escapes-0.c @@ -0,0 +1,23 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-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,astruct_s" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "variable bstruct is escaping false" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "collected,bstruct_s" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "variable astruct is escaping true" "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..2e7024ab907 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-06-global-type-escapes-0.c @@ -0,0 +1,21 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-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; +} + + +/* { dg-final { scan-wpa-ipa-dump "collected,astruct_s" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "type astruct_s is escaping true" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "collected,bstruct_s" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "type bstruct_s is escaping false" "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..d1842987735 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-08-parameter-escapes-0.c @@ -0,0 +1,27 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-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() +{ + astruct.a = 0; + bstruct.b = 0; +} + + +/* { dg-final { scan-wpa-ipa-dump "collected,astruct_s" "hello-world" } } */ +/* { dg-final { scan-wpa-ipa-dump "variable bstruct is escaping false" "hello-world" } } */ +/* { dg-final { scan-wpa-ipa-dump "collected,bstruct_s" "hello-world" } } */ +/* { dg-final { scan-wpa-ipa-dump "parameter type astruct_s is escaping true" "hello-world" } } */ +/* { dg-final { scan-wpa-ipa-dump "parameter type bstruct_s is escaping false" "hello-world" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-ea-09-parameter-type-escapes-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-ea-09-parameter-type-escapes-0.c new file mode 100644 index 00000000000..79809249942 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-09-parameter-type-escapes-0.c @@ -0,0 +1,24 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-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 +__attribute__((externally_visible)) void escaping(struct astruct_s cstruct) {} +void non_escaping(struct bstruct_s dstruct) {} + +int main() +{ + astruct.a = 0; + bstruct.b = 0; +} + + +/* { dg-final { scan-wpa-ipa-dump "collected,astruct_s" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "collected,bstruct_s" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "type bstruct_s is escaping false" "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..f2c5f307f01 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-10-return-type-escapes-0.c @@ -0,0 +1,24 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-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 +__attribute__((externally_visible)) struct astruct_s escaping() { struct astruct_s a; return a; } +void non_escaping(struct bstruct_s dstruct) {} + +int main() +{ + astruct.a = 0; + bstruct.b = 0; +} + + +/* { dg-final { scan-wpa-ipa-dump "collected,astruct_s" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "collected,bstruct_s" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "type bstruct_s is escaping false" "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..5ef153eb5f9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-11-cast-to-void-ptr-0.c @@ -0,0 +1,26 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-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 "collected,astruct_s" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "type astruct_s\\\* is escaping true" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "collected,bstruct_s" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "type bstruct_s is escaping false" "type-escape-analysis" } } */ 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..1f298b1415b --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-12-cast-to-void-ptr-0.c @@ -0,0 +1,27 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-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 "collected,astruct_s" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "type astruct_s\\\* is escaping true" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "type astruct_s is escaping true" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "collected,bstruct_s" "type-escape-analysis" } } */ +/* { dg-final { scan-wpa-ipa-dump "type bstruct_s is escaping false" "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..aa3e97b5444 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-ea-13-calling-printf-0.c @@ -0,0 +1,15 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } */ + +#include <stddef.h> +#include <stdio.h> + +int main(int argc, char** argv) +{ + char *filename = "helloworld.txt"; + FILE* f = fopen(filename); + fclose(f); +} + + +/* { dg-final { scan-wpa-ipa-dump "type FILE is escaping true" "type-escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-0-runs-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-0-runs-0.c new file mode 100644 index 00000000000..22185badeab --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-0-runs-0.c @@ -0,0 +1,11 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +// Dummy test to find out if +// our optimization is registered + +int +main () +{ + return 0; +} 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..07d6ea35e0f --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-1-prints-structs-0.c @@ -0,0 +1,15 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + return 0; +} 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..af0c175515a --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-10-array-0.c @@ -0,0 +1,16 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s a; + struct astruct_s b[2]; + a = b[0]; +} 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..448db9759ef --- /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 -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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..dd14c66f600 --- /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 -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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..df1b546906c --- /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 -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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..e458b14c820 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-14-rewrite-plus-expr-0.c @@ -0,0 +1,22 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#include <assert.h> +#include <stddef.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + int d = astruct.c + 1; + _Bool *a = &(astruct.a); + _Bool *c = a + 1; + assert (*c == 1); + 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..2f7ab7d8927 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-15-rewrite-mult-expr-0.c @@ -0,0 +1,22 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#include <stddef.h> +#include <assert.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + int d = astruct.c * 2; + _Bool *a = &(astruct.a); + _Bool *c = a + 1; + assert (*c == 0); + 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..3c4b7b08dd1 --- /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 -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#include <assert.h> +#include <stddef.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + astruct.c = 1; + struct astruct_s *astruct_p = &astruct; + _Bool c = astruct_p->c; + _Bool *a = &(astruct.a); + _Bool *c_ptr = a + 1; + assert (*c_ptr == 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..f297eea6aa0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-17-rewrite-field-write-ptr-0.c @@ -0,0 +1,22 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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 = a + 1; + assert (*c == 1); + 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..bc204f4017f --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-18-field-writes-deref-0.c @@ -0,0 +1,22 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +int +main () +{ + struct astruct_s + { + _Bool e; + _Bool a; + _Bool b; + _Bool c; + _Bool d; + }; + struct astruct_s astruct; + _Bool *c_ptr = &astruct.c; + c_ptr--; + *c_ptr = 1; + _Bool *e_ptr = &astruct.e; + e_ptr++; + return *e_ptr - 1; +} 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..10f850bb801 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-19-middle-pointer-equal-0.c @@ -0,0 +1,24 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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_from_a = a + 1; + _Bool *c_from_d = d - 1; + _Bool test = c_from_a == c_from_d; + char compile_test[test ? 1 : -1]; + assert (test); +} 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..e32e18af2f4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-2-modifies-0.c @@ -0,0 +1,17 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + return 0; +} + +/* "modifying,astruct_s" "typelist" */ 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..f93b642586e --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-20-array-offset-0.c @@ -0,0 +1,23 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#include <assert.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s b[2]; + _Bool *first_of_second = &(b[1].a); + _Bool *a_ptr = &(b[0].a); + _Bool *c_from_a = ++a_ptr; + _Bool *c_from_array = --first_of_second; + _Bool test = c_from_a == c_from_array; + assert (test); + char compile_test[test ? 1 : -1]; +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-21-rewrites-clobber-type-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-21-rewrites-clobber-type-0.c new file mode 100644 index 00000000000..080d8509c26 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-21-rewrites-clobber-type-0.c @@ -0,0 +1,17 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + return 0; +} + +// CLOBBERS :( 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..68c75427640 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-22-rewrites-addr-expr-read-0.c @@ -0,0 +1,21 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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; + assert (c_1 == c); + 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..c86bce1f885 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-23-array-cast-0.c @@ -0,0 +1,28 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#include <assert.h> +#include <stdio.h> +#include <stddef.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s a[2]; + struct bstruct_s + { + _Bool a; + _Bool c; + }; + struct astruct_s *a_0 = &(a[0]); + struct astruct_s *a_1 = a_0 + 1; + struct bstruct_s *b_0 = (struct bstruct_s *) a_0; + struct bstruct_s *b_1 = b_0 + 1; + assert ((struct bstruct_s *) a_1 == b_1); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-24-array-cast-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-24-array-cast-0.c new file mode 100644 index 00000000000..f42acd18d93 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-24-array-cast-0.c @@ -0,0 +1,31 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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_0 = &(a[0]); + struct astruct_s *a_1 = a_0 + 1; + struct bstruct_s *b_0 = (struct bstruct_s *) a_0; + struct bstruct_s *b_1 = b_0 + 1; + assert ((struct bstruct_s *) a_1 == b_1); +} 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..ef4c1c10772 --- /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 -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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_0 = &(a[1]); + struct astruct_s *a_1 = a_0 - 1; + struct bstruct_s *b_0 = (struct bstruct_s *) a_0; + struct bstruct_s *b_1 = b_0 - 1; + assert ((struct bstruct_s *) a_1 == b_1); +} 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..7ec3c82a916 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-26-array-cast-0.c @@ -0,0 +1,31 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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 bstruct_s + { + _Bool a; + _Bool c; + _Bool d; + }; + + struct astruct_s *a_0 = &(a[0]); + struct astruct_s *a_1 = a_0 + argc; + struct bstruct_s *b_0 = (struct bstruct_s *) a_0; + struct bstruct_s *b_1 = b_0 + argc; + assert ((struct bstruct_s *) a_1 == b_1); +} 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..b3c8f739ef3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-27-array-cast-0.c @@ -0,0 +1,20 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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]; +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-28-array-cast-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-28-array-cast-0.c new file mode 100644 index 00000000000..aba8eefe3dc --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-28-array-cast-0.c @@ -0,0 +1,31 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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[3]; + struct bstruct_s + { + _Bool a; + _Bool c; + _Bool d; + }; + + struct astruct_s *a_0 = &(a[0]); + struct astruct_s *a_1 = a_0 + 2; + struct bstruct_s *b_0 = (struct bstruct_s *) a_0; + struct bstruct_s *b_1 = b_0 + 2; + assert ((struct bstruct_s *) a_1 == b_1); +} 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..43a74ce963c --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-29-heterogeneous-struct.c @@ -0,0 +1,22 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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-3-new-offsets-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-3-new-offsets-0.c new file mode 100644 index 00000000000..07d6ea35e0f --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-3-new-offsets-0.c @@ -0,0 +1,15 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + return 0; +} 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..49436d8bb87 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-30-heterogenous-struct-0.c @@ -0,0 +1,26 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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); + _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..4f9853af425 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-31-heterogenous-struct-0.c @@ -0,0 +1,28 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#include <assert.h> +#include <stdio.h> +#include <stddef.h> + +int +main () +{ + // unmodified a = 0, d = 1; e = 2; f = 3; b = 4; c = 8 + // 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; + _Bool *a = &(p->a); + _Bool *c = (_Bool *) &(p->c); + ptrdiff_t d = c - a; + assert (d == 4); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-32-pointer-indirection-level-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-32-pointer-indirection-level-0.c new file mode 100644 index 00000000000..687ef04ac77 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-32-pointer-indirection-level-0.c @@ -0,0 +1,23 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#include <assert.h> + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + struct astruct_s *p0 = &astruct; + struct astruct_s **p1 = &p0; + _Bool *a_ptr_0 = (_Bool *) p0; + _Bool *c_ptr_0 = a_ptr_0 + 1; + _Bool **a_ptr_1 = (_Bool **) p1; + _Bool *c_ptr_1 = (_Bool *) ((*a_ptr_1) + 1); + assert (c_ptr_0 == c_ptr_1); +} 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..ca942339efe --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-33-nested-struct-0.c @@ -0,0 +1,42 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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; + _Bool *outer_a = (_Bool *) &outerstruct.a; + _Bool *outer_b = (_Bool *) &outerstruct.b; + _Bool *outer_c = (_Bool *) &outerstruct.c; + _Bool *outer_d = (_Bool *) &outerstruct.d; + _Bool *outer_b_1 = outer_a + 3; + _Bool *outer_c_1 = outer_a + 6; + _Bool *outer_d_1 = outer_a + 9; + assert (outer_b == outer_b_1); + assert (outer_c == outer_c_1); + assert (outer_d == outer_d_1); +} + +/* " offset,astruct_s.reorg,a,0" "typelist" } } */ +/* " offset,astruct_s.reorg,c,1" "typelist" } } */ +/* " offset,astruct_s.reorg,d,2" "typelist" } } */ +/* " offset,outerstruct_s.reorg,a,0" "typelist" } } */ +/* " offset,outerstruct_s.reorg,b,3" "typelist" } } */ +/* " offset,outerstruct_s.reorg,c,6" "typelist" } } */ +/* " offset,outerstruct_s.reorg,d,9" "typelist" } } */ 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..0fc5f0f9e46 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-33-pointer-indirection-level-0.c @@ -0,0 +1,24 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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 = a_ptr + 1; + _Bool *a_ptr_2 = &((*p1)->a); + _Bool *c_ptr_2 = a_ptr_2 + 1; + assert (c_ptr == c_ptr_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..5ae01feb7bc --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-34-array-cast-0.c @@ -0,0 +1,21 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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]; +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-35-array-cast-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-35-array-cast-0.c new file mode 100644 index 00000000000..ea4d49258a2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-35-array-cast-0.c @@ -0,0 +1,38 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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 bstruct_s + { + _Bool a; + _Bool c; + _Bool d; + }; + struct bstruct_s *b_ptr = (struct bstruct_s *) &a; + // a[0][0] = b_ptr + 0 + 0; + // a[0][1] = b_ptr + 0 + sizeof(astruct_s) + // a[1][0] = b_ptr + 2*sizeof(astruct_s) + 0; + // a[1][1] = b_ptr + 2*sizeof(astruct_s) + sizeof(astruct_s); + // as a function of argc + // a[argc][argc] = b_ptr + 2*sizeof(astruct_s)* argc + sizeof(astruct_s)*argc + // TODO: Can't put size of astruct_s because + // we don't change sizeof yet... + int size = sizeof (struct bstruct_s); + struct bstruct_s *b_ptr_2 = b_ptr + 2 * argc + argc; + struct bstruct_s *b = (struct bstruct_s *) &(a[argc][argc]); + assert (b_ptr_2 == b); +} 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..2b2e70a0b16 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-36-arguments-0.c @@ -0,0 +1,37 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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; + foo (astruct); +} 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..b8a3a01ba0a --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-37-arguments-0.c @@ -0,0 +1,37 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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) +{ + _Bool *a = (_Bool *) &(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; + foo (&astruct); +} 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..0df611a7d7b --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-38-return-values-0.c @@ -0,0 +1,38 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#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); + _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..c8f0569e932 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-39-typedef-0.c @@ -0,0 +1,15 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +int +main () +{ + typedef struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + } astruct_s; + astruct_s astruct; + return 0; +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-4-layout-compile-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-4-layout-compile-0.c new file mode 100644 index 00000000000..d933f3a5e85 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-4-layout-compile-0.c @@ -0,0 +1,22 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#include <stddef.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; + char ch[d == 1 ? 1 : -1]; +} + +/* "Executing structreorg" "typelist" } } */ 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..d1f752ff005 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-40-typedef-0.c @@ -0,0 +1,16 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + typedef struct astruct_s astruct_s; + astruct_s astruct; + return 0; +} 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..a0c8d4a1f3e --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-41-deref-0.c @@ -0,0 +1,18 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + struct astruct_s *t, copy; + t = &astruct; + copy = *t; + return 0; +} 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..5cc493d2f3f --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-42-mem-ref-0.c @@ -0,0 +1,29 @@ +/* { dg-do link } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=nextout -fipa-typelist-struct=arc" } */ + +#include <stdlib.h> + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +struct arc +{ + int id; + long cost; + void *tail, *head; + short ident; + arc_p nextout, nextin; + long flow; + long org_cost; +}; + +int +main () +{ + int num_threads = 10; + arc_p *deleted_arcs; + register arc_t *arcnew = (arc_t*) malloc (num_threads * sizeof (arc_p)); + deleted_arcs = (arc_p *) malloc (num_threads * num_threads * sizeof (arc_p)); + deleted_arcs[num_threads - 1][num_threads - 1] = arcnew[0]; + 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..a3890b404a7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-43-args-0.c @@ -0,0 +1,30 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=nextout -fipa-typelist-struct=arc" } */ + +#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; + bar (&a_wrapper); +} 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..749dad5995e --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-44-cond-0.c @@ -0,0 +1,14 @@ +/* { dg-do link } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=nextout -fipa-typelist-struct=arc" } */ + +struct a +{ + struct arc *b; +} c (struct a *d) +{ + while (d) + ; +} +void +main () +{} 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..c490c17de6b --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-45-phis-0.c @@ -0,0 +1,14 @@ +/* { dg-do link } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=nextout -fipa-typelist-struct=arc" } */ + +struct a +{ + struct arc *b; +} c (struct a *d, struct a *e) +{ + while (e) + d = d; +} +int +main () +{} 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..f0befcd814c --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-46-static-0.c @@ -0,0 +1,23 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=nextout -fipa-typelist-struct=arc -o ipa-structreorg-46-static-0.exe " } */ + +#include <assert.h> +#include <stddef.h> + +struct arc +{ + _Bool a; + _Bool nextout; + _Bool c; +}; + +struct arc an_arc; + +int +main () +{ + _Bool *a = &(an_arc.a); + _Bool *c = &(an_arc.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..28d18a54957 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-47-constructor-0.c @@ -0,0 +1,24 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=nextout -fipa-typelist-struct=arc -o ipa-structreorg-46-static-0.exe " } */ + +#include <assert.h> +#include <stddef.h> + +int +main () +{ + struct arc + { + _Bool a; + _Bool nextout; + _Bool c; + }; + struct arc an_arc; + struct another + { + _Bool a; + struct arc d; + _Bool c; + }; + struct another an_another = {0, {0, 1}, 1}; +} 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..2e55ad41d34 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-48-function-ptr-0.c @@ -0,0 +1,38 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=nextout -fipa-typelist-struct=arc " } */ + +#include <assert.h> +#include <stddef.h> + +struct arc +{ + _Bool a; + _Bool nextout; + _Bool c; +}; + +_Bool +returnLast (struct arc anArc) +{ + return anArc.c; +} + +_Bool +returnLast2 (struct arc anArc) +{ + _Bool *ptr = &(anArc.a); + ptr = ptr + 1; + return *ptr; +} + +int +main (int argc, char **argv) +{ + _Bool (*func1) (struct arc); + func1 = &returnLast; + _Bool (*func2) (struct arc); + func2 = &returnLast2; + struct arc anArc; + anArc.c = argc; + assert (func1 (anArc) == func2 (anArc)); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-49-array-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-49-array-0.c new file mode 100644 index 00000000000..6f4fb557d16 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-49-array-0.c @@ -0,0 +1,48 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=nextout,nextin -fipa-typelist-struct=astruct_s " } */ + +#include <assert.h> +#include <stddef.h> + +int +main (int argc, char **argv) +{ + struct astruct_s + { + int id; + long cost; + void *tail; + void *head; + void *nextin; + void *nextout; + short ident; + long flow; + long org_cost; + }; + struct cstruct_s + { + int id; + long cost; + void *tail; + void *head; + short ident; + long flow; + long org_cost; + }; + struct bstruct_s + { + struct astruct_s *basic_arc; + }; + long num = argc; + struct astruct_s array[100]; + struct astruct_s *old_arcs = array; + struct bstruct_s b; + struct bstruct_s *c = &b; + c->basic_arc = array + num; + c->basic_arc->cost = num; + struct cstruct_s *other = ((struct cstruct_s *) (array)) + num; + size_t off = c->basic_arc - old_arcs; + size_t off2 = other - (struct cstruct_s *) old_arcs; + assert ((struct cstruct_s *) c->basic_arc == other); + assert (c->basic_arc->cost == other->cost); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-5-field-reads-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-5-field-reads-0.c new file mode 100644 index 00000000000..3e37a780774 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-5-field-reads-0.c @@ -0,0 +1,18 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + _Bool c = astruct.c; + return 0; +} + +/* "rewrite,field_offset,c,1" "typelist" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-5-rewrite-local-decl-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-5-rewrite-local-decl-0.c new file mode 100644 index 00000000000..07d6ea35e0f --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-5-rewrite-local-decl-0.c @@ -0,0 +1,15 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + return 0; +} 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..a504400aaad --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-50-field-write-delete-0.c @@ -0,0 +1,15 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=delete_me -fipa-typelist-struct=astruct_s " } */ + +#include <assert.h> +#include <stddef.h> +#include <stdbool.h> + +int +main (int argc, char **argv) +{ +struct astruct_s { _Bool a; _Bool delete_me; _Bool c;}; +struct astruct_s astruct; +astruct.delete_me = false; +return 0; +} 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..d03f511078a --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-51-creduce-0.c @@ -0,0 +1,6 @@ +/* { dg-do compile } */ +/* { dg-options "-w -flto -flto-partition=none -fipa-dead-field-eliminate" } */ + +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..6a9994f3e46 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-52-creduce-1.c @@ -0,0 +1,11 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -flto-partition=none -fipa-dead-field-eliminate" } */ + +#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..df33fb7aeb7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-53-csmith-2.c @@ -0,0 +1,5 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -flto-partition=none -fipa-dead-field-eliminate" } */ +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..f49627eeeba --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-54-csmith-3.c @@ -0,0 +1,21 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -flto-partition=none -fipa-dead-field-eliminate" } */ + +#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..e745c6aa8fb --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-55-csmith-4.c @@ -0,0 +1,10 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -flto-partition=none -fipa-dead-field-eliminate" } */ + +#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..27da3daa29d --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-56-csmith-5.c @@ -0,0 +1,19 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -flto-partition=none -fipa-dead-field-eliminate" } */ + +#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..9827d228e31 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-57-csmith-6.c @@ -0,0 +1,6 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -flto-partition=none -fipa-dead-field-eliminate" } */ + +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..3edc8a44499 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-58-csmith-7.c @@ -0,0 +1,12 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -flto-partition=none -fipa-dead-field-eliminate" } */ + +#include <stdint.h> +struct a { + int32_t b +} c; +d() { + for (;; c.b = 0) + ; +} +main() {} 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..050e3177fb1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-59-csmith-8.c @@ -0,0 +1,10 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -flto-partition=none -fipa-dead-field-eliminate" } */ + +struct a { + signed b +}; +struct { + struct a b +} volatile c; +main() { c.b.b; } diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-6-field-writes-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-6-field-writes-0.c new file mode 100644 index 00000000000..c7092516692 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-6-field-writes-0.c @@ -0,0 +1,16 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +int +main () +{ + struct astruct_s + { + _Bool a; + _Bool b; + _Bool c; + }; + struct astruct_s astruct; + astruct.c = 0; + return 0; +} 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..b1001a6286c --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-60-csmith-9.c @@ -0,0 +1,20 @@ +/* { dg-do run } */ +/* { dg-options "-w -flto -flto-partition=none -fipa-dead-field-eliminate" } */ + +#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() {} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-7-delete-first-field-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-7-delete-first-field-0.c new file mode 100644 index 00000000000..b0fdc15d63e --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-7-delete-first-field-0.c @@ -0,0 +1,23 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#include <assert.h> + +int +main () +{ + struct astruct_s + { + _Bool b; + _Bool a; + _Bool c; + _Bool d; + }; + struct astruct_s astruct; + + _Bool *a_ptr = &astruct.a; + struct astruct_s *astruct_ptr = &astruct; + _Bool test = (_Bool *) astruct_ptr == a_ptr; + char compile_test[test ? 1 : -1]; + assert (test); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-8-modify-double-struct-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-8-modify-double-struct-0.c new file mode 100644 index 00000000000..83c508ee24e --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-8-modify-double-struct-0.c @@ -0,0 +1,33 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +#include <assert.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; + struct inner_s a = astruct.a; + struct inner_s c = astruct.c; + struct inner_s d = astruct.d; + _Bool *pa = (_Bool *) &(astruct.a); + _Bool *pc = (_Bool *) &(astruct.c); + _Bool *pd = (_Bool *) &(astruct.d); + _Bool *c_1 = pa + 4; + _Bool *d_1 = pa + 8; + assert (pc == c_1); + assert (pd == d_1); +} diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-9-modify-int-struct-0.c b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-9-modify-int-struct-0.c new file mode 100644 index 00000000000..be51f9e06e9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-structreorg-9-modify-int-struct-0.c @@ -0,0 +1,18 @@ +/* { dg-do run } */ +/* { dg-options "-flto -flto-partition=none -fipa-dead-field-eliminate -fdump-ipa-structure-reorg -fipa-typelist-field=b -fipa-typelist-struct=astruct_s" } */ + +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; +} diff --git a/gcc/timevar.def b/gcc/timevar.def index f467d0251f7..28d3f8cc020 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -80,6 +80,7 @@ DEFTIMEVAR (TV_IPA_INLINING , "ipa inlining heuristics") DEFTIMEVAR (TV_IPA_FNSPLIT , "ipa function splitting") DEFTIMEVAR (TV_IPA_COMDATS , "ipa comdats") DEFTIMEVAR (TV_IPA_OPT , "ipa various optimizations") +DEFTIMEVAR (TV_IPA_STRUCTURE_REORG , "ipa structure reorg") DEFTIMEVAR (TV_IPA_LTO_DECOMPRESS , "lto stream decompression") DEFTIMEVAR (TV_IPA_LTO_COMPRESS , "lto stream compression") DEFTIMEVAR (TV_IPA_LTO_OUTPUT , "lto stream output") diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index a35ac21d324..a9a9c96a4dc 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -501,6 +501,8 @@ extern ipa_opt_pass_d *make_pass_ipa_fn_summary (gcc::context *ctxt); extern ipa_opt_pass_d *make_pass_ipa_inline (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_free_lang_data (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_free_fn_summary (gcc::context *ctxt); +extern simple_ipa_opt_pass *make_pass_ipa_type_escape_analysis (gcc::context *ctxt); +extern simple_ipa_opt_pass *make_pass_ipa_hello_world (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_escape_analysis (gcc::context *ctxt); extern ipa_opt_pass_d *make_pass_ipa_cp (gcc::context *ctxt); extern ipa_opt_pass_d *make_pass_ipa_sra (gcc::context *ctxt); @@ -509,6 +511,8 @@ extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt); extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt); extern ipa_opt_pass_d *make_pass_ipa_hsa (gcc::context *ctxt); extern ipa_opt_pass_d *make_pass_ipa_pure_const (gcc::context *ctxt); +extern simple_ipa_opt_pass * +make_pass_ipa_structure_reorg (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_pta (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_tm (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_target_clone (gcc::context *ctxt); |