diff options
author | Erick Ochoa <erick.ochoa@theobroma-systems.com> | 2020-02-04 17:03:26 +0100 |
---|---|---|
committer | Erick Ochoa <erick.ochoa@theobroma-systems.com> | 2020-05-14 14:45:30 +0200 |
commit | c64247c9fa452ffc9d587fa98a07216700ae16ba (patch) | |
tree | f64075bea8e0bf4d59e066dd46b5921a70e2ba69 /gcc/ipa-str-reorg-dead-field-eliminate.c | |
parent | bf8660842c4163d55a2f54ae179b157d8aa61fd8 (diff) |
Initial merge
Diffstat (limited to 'gcc/ipa-str-reorg-dead-field-eliminate.c')
-rw-r--r-- | gcc/ipa-str-reorg-dead-field-eliminate.c | 1766 |
1 files changed, 1753 insertions, 13 deletions
diff --git a/gcc/ipa-str-reorg-dead-field-eliminate.c b/gcc/ipa-str-reorg-dead-field-eliminate.c index 6ce0c377934..c7a3396a673 100644 --- a/gcc/ipa-str-reorg-dead-field-eliminate.c +++ b/gcc/ipa-str-reorg-dead-field-eliminate.c @@ -22,26 +22,1766 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #include "system.h" #include "coretypes.h" -#include "backend.h" #include "tree.h" -#include "gimple.h" -#include "tree-pass.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 "pretty-print.h" -#include <vector> +#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 <vector> // needed for ipa-structure-reorg #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" +#include "ipa-structure-reorg.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 const char* get_type_name(const_tree type); +static const char* get_record_name(const_tree record); +//INFO: cannot change +//tree expr to const_tree expr +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)); +} + +// This really should be inaccessible to anyone. +static 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); +} + +static 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); +} + + +static 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); +} + +static 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); +} + +static 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); +} + +static 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); +} + +// 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 +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; +} + +//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_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); +} + +static 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); +} + +static 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; +} + +static 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; +} + +static 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; +} + +static 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); +} + +static 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); +} + +static 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; +} + +static 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; +} + +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); +} + +static bool rewrite_addr_expr_def(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent); +static bool +rewrite_addr_expr(tree expr, hash_map<const_tree, const_tree> &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, hash_map<const_tree, const_tree> &type_map, const int indent); + +static bool +rewrite_array_ref(tree expr, hash_map<const_tree, const_tree> &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, hash_map<const_tree, const_tree> &type_map, const int indent); +static bool +rewrite_bin_expr_default(tree expr, hash_map<const_tree, const_tree> &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, hash_map<const_tree, const_tree> &type_map, const int indent); +static bool +rewrite_constructor(tree expr, hash_map<const_tree, const_tree> &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, hash_map<const_tree, const_tree> &type_map, const int indent); +static bool +rewrite_field_decl(tree expr, hash_map<const_tree, const_tree> &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_component_ref_def(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent); +static bool +rewrite_component_ref(tree expr, hash_map<const_tree, const_tree> &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_def(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent); +static bool +rewrite_expr(tree expr, hash_map<const_tree, const_tree> &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, hash_map<const_tree, const_tree> &type_map, const int indent); + +static bool rewrite_mem_ref(tree expr, hash_map<const_tree, const_tree> &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_plus_expr_def(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent); +static bool +rewrite_plus_expr(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent) +{ + log_expr_prologue(indent, "rewrite_plux_expr", expr); + bool retval = rewrite_plus_expr_def(expr, type_map, indent+4); + log_expr_epilogue(indent, "rewrite_plus_expr", expr); + return retval; +} + +static bool rewrite_pointer_diff_def(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent); +static bool +rewrite_pointer_diff(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent) +{ + log_expr_prologue(indent, "rewrite_pointer_diff", expr); + bool retval = rewrite_pointer_diff_def(expr, type_map, indent+4); + log_expr_epilogue(indent, "rewrite_pointer_diff", expr); + return retval; +} + +static bool rewrite_pointer_plus_def(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent); +static bool +rewrite_pointer_plus(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent) +{ + log_expr_prologue(indent, "rewrite_pointer_plus", expr); + bool retval = rewrite_pointer_plus_def(expr, type_map, indent+4); + log_expr_epilogue(indent, "rewrite_pointer_plus", expr); + return retval; +} + +static bool rewrite_ssa_name_def(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent); +static bool +rewrite_ssa_name(tree expr, hash_map<const_tree, const_tree> &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, hash_map<const_tree, const_tree> &type_map, const int indent); +static bool +rewrite_var_decl(tree expr, hash_map<const_tree, const_tree> &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, hash_map<const_tree, const_tree> &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 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: + retval = rewrite_pointer_diff(expr, type_map, indent); + break; + case POINTER_PLUS_EXPR: + retval = rewrite_pointer_plus(expr, type_map, indent); + break; + case PLUS_EXPR: + retval = rewrite_plus_expr(expr, type_map, indent); + 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: + { + enum tree_code code = TREE_CODE(expr); + test_log("code = %s", indent, get_tree_code_name(code)); + retval = false; + } + 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 int +get_field_offset(const_tree field) +{ + gcc_assert(TREE_CODE(field) == FIELD_DECL); + tree cst = byte_position(field); + int offset = tree_to_shwi(cst); + return offset; +} + +static 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; +} + +static const_tree +get_field_with_name(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; + } + //WONTFIX, INFO + //WE MUST ALWAYS SUCCEED. + gcc_unreachable(); + return NULL; +} + +static const char* +get_record_name(const_tree record) +{ + gcc_assert(record); + gcc_assert(TREE_CODE(record) == RECORD_TYPE); + //TODO: deal with anonymous structs. + //Some records don't have identifier pointers + tree name_tree = TYPE_NAME(record); + if (TREE_CODE(name_tree) == TYPE_DECL) { + //TODO: For now + gcc_unreachable(); + // Maybe? + tree original_type = DECL_ORIGINAL_TYPE(name_tree); + tree type_name = DECL_NAME(name_tree); + return IDENTIFIER_POINTER(type_name); + } + gcc_assert(name_tree); + const char* identifier = IDENTIFIER_POINTER(name_tree); + gcc_assert(identifier); + return identifier; +} + +static 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); +} + +static 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; +} + +static 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; + 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; +} + +static bool filter_out_boring_type(const_tree type, hash_map<const_tree, tree> &map); + +static bool +is_interesting_struct(const_tree record) +{ + enum tree_code code = TREE_CODE(record); + gcc_assert(code == RECORD_TYPE); + + 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); + } + return retval; +} + +static bool +is_interesting_field(const_tree field) +{ + enum tree_code code = TREE_CODE(field); + gcc_assert(code == FIELD_DECL); + + 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 +filter_out_boring_record(const_tree record, hash_map<const_tree, 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); + 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_map<const_tree, 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_map<const_tree, 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_type(const_tree type, hash_map<const_tree, 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... + test_log("filter_out_boring_type %s", 0, get_type_name(type)); + + 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; + 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.put(type, NULL); + } + return retval; +} + +static bool +filter_var_decls(tree var_decl, hash_map<const_tree, 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_map<const_tree, 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_map<const_tree, tree>&), hash_map<const_tree, 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); + tree *ptr = decl_map.get(type); + // We already have this type. + if (ptr) continue; + + const char* type_identifier = get_type_name(type); + test_log("collecting,%s", 0, type_identifier); + decl_map.put(type, NULL); + } +} + +//TODO: It would be nice to have this as an iterator +//TODO: Maybe after the filter, there should be +//a function that takes a type and gets you +//an identifier string. +static void +collect_local_declarations(cgraph_node* cnode, bool (*filter)(tree, hash_map<const_tree, tree>&), hash_map<const_tree, tree> &decl_map) +{ + gcc_assert(cnode); + int i = 0; + function *func = DECL_STRUCT_FUNCTION(cnode->decl); + //TODO: What condition do I need to check for this one? + 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.put(type, NULL); + } +} + +static void +collect_orig_structs(hash_map<const_tree, tree> &type_map) +{ + cgraph_node *cnode = NULL; + FOR_EACH_DEFINED_FUNCTION(cnode) + { + print_function(cnode); + collect_parm_declarations(cnode, &filter_parm_decls, type_map); + collect_local_declarations(cnode, &filter_var_decls, type_map); + } +} + + + +static const_tree make_new_type_based_on_old(const_tree old, hash_map<const_tree, const_tree>* mod_type_map); + +/* + * 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, + hash_map<const_tree, const_tree>* mod_type_map) +{ + const_tree *new_record_ptr = mod_type_map->get(old); + gcc_assert(!new_record_ptr); + + //TODO: You will not have this guarantee in the end, + //we need an API to let this method know which structs will be + //deleted, or someting similar to that. + const char* old_name = get_type_name(old); + 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_name); + tree new_record_name = get_identifier(new_name); + TYPE_NAME(new_record) = new_record_name; + + 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); + + if (are_field_names_equal && will_definitely_change) { + 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)); + TREE_TYPE(new_field) = (tree) new_field_type; + 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); + + + 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); + } + + const_tree *ptr = mod_type_map->get(old); + if (ptr) return *ptr; + + + //WONTFIX: Think about: Create pointers and arrays to record up to a limit... + //Will this ensure that typedefs shouldn't matter, up to the limit? + //No, because we do not have the old type to provide a map between the old + //and the new... + + mod_type_map->put(old, new_record); + return new_record; +} + +const_tree +make_new_array_based_on_old(const_tree old_type, + hash_map<const_tree, const_tree>* mod_type_map) +{ + gcc_assert(TREE_CODE(old_type) == ARRAY_TYPE); + const_tree *new_type_ptr = mod_type_map->get(old_type); + 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_TYPE(new_array) = (tree) make_new_type_based_on_old(TREE_TYPE(old_type), mod_type_map); + layout_type(new_array); + + const_tree *ptr_2 = mod_type_map->get(old_type); + if (ptr_2) return *ptr_2; + + mod_type_map->put(old_type, new_array); + + return new_array; +} + + +static const_tree +make_new_pointer_based_on_old(const_tree old, + hash_map<const_tree, const_tree>* mod_type_map) +{ + test_log("make_new_pointer_based_on_old %s", 0, get_type_name(old)); + const_tree *already_computed = mod_type_map->get(old); + 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; + + TREE_TYPE(new_pointer) = (tree) new_type; + layout_type(new_pointer); + + const_tree *ptr_2 = mod_type_map->get(old); + if (ptr_2) return *ptr_2; + + mod_type_map->put(old, new_pointer); + test_log("putting %s %s", 0, get_type_name(old), get_type_name(new_pointer)); + return new_pointer; + +} + +//TODO: +//This needs a new parameter that allows +//us to say **how** we modify the type. +//Not all information can be derived +//only from the old type. +//This is only possible now because we are +//basically hard coding examples. +static const_tree +make_new_type_based_on_old(const_tree old, + hash_map<const_tree, const_tree>* mod_type_map) +{ + + if (!old) return old; + + gcc_assert(old); + const_tree *new_base_ptr = mod_type_map->get(old); + 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, + tree* new_type, + hash_map<const_tree, const_tree>* mod_type_map) +{ + + const_tree *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... + const_tree *ptr_2 = mod_type_map->get(old_type); + if (ptr_2) return true; + + test_log("new_name,%s", 0, get_type_name(new_record)); + mod_type_map->put(old_type, new_record); + return true; +} + +static void +compute_modified_structs(hash_map<const_tree, tree> &type_map, + hash_map<const_tree, const_tree> &mod_type_map) +{ + type_map.traverse<hash_map<const_tree, const_tree>*, compute_modified_structs_internal>(&mod_type_map); +} + + +static bool +rewrite_constructor_def(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent) +{ + tree old_rhs_type = TREE_TYPE(expr); + const_tree* new_rhs_type_ptr = type_map.get(old_rhs_type); + if (!new_rhs_type_ptr) return false; + + tree new_rhs_type = (tree) *new_rhs_type_ptr; + TREE_TYPE(expr) = new_rhs_type; + return true; +} + + +static bool +rewrite_field_decl_def(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent) +{ + tree old_rhs_type = TREE_TYPE(expr); + const_tree* new_rhs_type_ptr = type_map.get(old_rhs_type); + if (!new_rhs_type_ptr) return false; + + tree new_rhs_type = (tree) *new_rhs_type_ptr; + TREE_TYPE(expr) = new_rhs_type; + return true; +} + + +static bool +rewrite_component_ref_def(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent) +{ + gcc_assert(expr); + tree expr_type = TREE_TYPE(expr); + gcc_assert(expr_type); + const_tree *new_expr_type_ptr = type_map.get(expr_type); + if (new_expr_type_ptr) TREE_TYPE(expr) = (tree) *new_expr_type_ptr; + + tree _struct = TREE_OPERAND(expr, 0); + gcc_assert(_struct); + tree _struct_type = TREE_TYPE(_struct); + gcc_assert(_struct_type); + const_tree* new_struct_type_ptr = type_map.get(_struct_type); + + bool retval = rewrite_expr(_struct, type_map, indent); + + tree new_type = new_struct_type_ptr ? (tree)*new_struct_type_ptr : _struct_type; + tree field_decl = TREE_OPERAND(expr, 1); + const char* field_name = get_field_name(field_decl); + 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) = (tree) new_field; + int offset = get_field_offset(new_field); + test_log("rewrite,field_offset,%s,%d", indent, field_name, offset); + + return true; +} + +static bool +rewrite_addr_expr_def(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent) +{ + tree type = TREE_TYPE(expr); + gcc_assert(TREE_CODE(expr) == ADDR_EXPR); + const_tree *new_type_ptr = type_map.get(type); + if (new_type_ptr) TREE_TYPE(expr) = (tree) *new_type_ptr; + tree op0 = TREE_OPERAND(expr, 0); + bool retval = rewrite_expr(op0, type_map, indent); + return retval; +} + +static bool +rewrite_var_decl_def(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent) +{ + tree type = TREE_TYPE(expr); + gcc_assert(TREE_CODE(expr) == VAR_DECL); + //tree *new_type_ptr = type_map.get(type); + //if (!new_type_ptr) return false; + //TREE_TYPE(expr) = *new_type_ptr; + return false; +} + +static bool +rewrite_mem_ref_def(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent) +{ + gcc_assert(expr); + gcc_assert(TREE_CODE(expr) == MEM_REF); + tree type = TREE_TYPE(expr); + const_tree *new_type_ptr = type_map.get(type); + if (new_type_ptr) TREE_TYPE(expr) = (tree) *new_type_ptr; + + const char* old_expr = print_generic_expr_to_str(expr); + tree op0 = TREE_OPERAND(expr, 0); + bool retval = rewrite_expr(op0, type_map, indent); + const char* new_expr = print_generic_expr_to_str(expr); + return retval; +} + + +static bool +rewrite_ssa_name_def(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent) +{ + const char* old_expr = print_generic_expr_to_str(expr); + tree type = TREE_TYPE(expr); + tree ssa_name_var = SSA_NAME_VAR(expr); + const_tree *new_type_ptr = type_map.get(type); + if (new_type_ptr) { + TREE_TYPE(expr) = (tree) *new_type_ptr; + //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) TREE_TYPE(ssa_name_var) = (tree) *new_type_ptr; + if (ssa_name_var) relayout_decl(ssa_name_var); + } + return new_type_ptr; +} + +static bool +rewrite_bin_expr_default_def(tree expr, hash_map<const_tree, const_tree> &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); + + return retval; +} + +static bool +rewrite_pointer_plus_def(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent) +{ + gcc_assert(expr); + gcc_assert(TREE_CODE(expr) == POINTER_PLUS_EXPR); + const char* old_expr = print_generic_expr_to_str(expr); + tree type = TREE_TYPE(expr); + const_tree *new_type_ptr = type_map.get(type); + if (new_type_ptr) TREE_TYPE(expr) = (tree) *new_type_ptr; + return true; +} + +static bool +rewrite_plus_expr_def(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent) +{ + gcc_assert(expr); + gcc_assert(TREE_CODE(expr) == PLUS_EXPR); + const char* old_expr = print_generic_expr_to_str(expr); + bool retval = rewrite_bin_expr_default(expr, type_map, indent); + return retval; +} + +static bool +rewrite_pointer_diff_def(tree expr, hash_map<const_tree, const_tree> &type_map, const int indent) +{ + bool retval = false; + return retval; +} + + +static bool +rewrite_array_ref_def(tree expr, hash_map<const_tree, const_tree> &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); + if (up_ptr) TREE_TYPE(expr) = (tree) *up_ptr; + tree old_rhs_type = TREE_TYPE(expr); + const_tree* new_rhs_type_ptr = type_map.get(old_rhs_type); + + rewrite_expr(TREE_OPERAND(expr, 0), type_map, indent+4); + rewrite_expr(TREE_OPERAND(expr, 1), type_map, indent+4); + + return true; +} + + +//TODO: +//rewrite_pointer_plus_def_rhs_constant is a special case of +//rewrite_pointer_plus_def_rhs_variable. +//Maybe variable should subsume constant? +static tree +rewrite_pointer_plus_def_rhs_variable(gimple_stmt_iterator &gsi, tree pointer, tree variable, const_tree old_type) +{ + tree pointer_type = TREE_TYPE(pointer); + gcc_assert(TREE_CODE(pointer_type) == POINTER_TYPE); + enum tree_code code = TREE_CODE(variable); + gcc_assert(TREE_CODE(variable) == SSA_NAME); + tree variable_type = TREE_TYPE(variable); + gcc_assert(TREE_CODE(variable_type) == INTEGER_TYPE); + + // the variable holds the offset + // variable = anotherthing * old_type_size + // result = pointer + variable + // Let's first create a temporary variable + // variable = anotherthing * old_type_size; + // tmpvar = variable / old_struct_size; + tree tmpvar = create_tmp_var(TREE_TYPE(variable)); + // Good. + // Now, we can make an integer constant + // that is old_struct_size; + // tmpvar = variable / old_struct_size; + unsigned int indirection_level = 1; + const_tree old_base_type = get_base_type_from_pointer_type(old_type, indirection_level); + 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); + tree old_struct_size = build_int_cst(TREE_TYPE(variable), old_struct_size_int); + + // Good. + // Now we need to create a division operation between the two + // Because there is no pointer division. + gassign *assign_stmt_1 = gimple_build_assign(tmpvar, TRUNC_DIV_EXPR, variable, old_struct_size); + // Now we need to insert assign_stmt_1 before us... + gsi_insert_before(&gsi, assign_stmt_1, GSI_SAME_STMT); + + // Ok now we need a new temporary variable + // tmpvar2 = tmpvar * new_struct_size; + tree tmpvar2 = create_tmp_var(TREE_TYPE(variable)); + // Good. + // Now we need we need another integer constant + unsigned int indirection_level2 = 0; + const_tree new_base_type = get_base_type_from_pointer_var(pointer, indirection_level); + 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 = build_int_cst(TREE_TYPE(variable), new_struct_size_int); + + //Good. Now we need to multiply... + // tmpvar2 = tmpvar * new_struct_size; + gassign *assign_stmt_2 = gimple_build_assign(tmpvar2, MULT_EXPR, tmpvar, new_struct_size); + + // Now we need to insert assign_stmt_2 before us... + gsi_insert_before(&gsi, assign_stmt_2, GSI_SAME_STMT); + return tmpvar2; +} + +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); + + unsigned int indirection_level_1 = 0; + const_tree base_type = get_base_type_from_pointer_var(pointer, indirection_level_1); + + tree size = TYPE_SIZE_UNIT(base_type); + int new_struct_size = tree_to_shwi(size); + int old_offset = tree_to_uhwi(integer_constant); + + unsigned int indirection_level_2 = 1; + const_tree old_base_type = get_base_type_from_pointer_type(old_type, indirection_level_2); + + tree old_type_size = TYPE_SIZE_UNIT(old_base_type); + int old_struct_size = tree_to_shwi(old_type_size); + test_log("old_struct_size = %d", 0, old_struct_size); + + bool addition = old_offset > 0; + 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, hash_map<const_tree, const_tree> &type_map, hash_map<const_tree, const_tree>& inverse) +{ + gcc_assert(op0 && op1); + tree op0_type = TREE_TYPE(op0); + const_tree *new_op0_type = type_map.get(op0_type); + test_log("rewrite_pointer_plus has old_type %s", 0, new_op0_type ? "true" : "false"); + //gcc_assert(!new_op0_type); + + rewrite_expr_def(op0, type_map, 0); + rewrite_expr_def(op1, type_map, 0); + + tree rhs_type = TREE_TYPE(op0); + 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. + const_tree * old_type_ptr = inverse.get(op0_type); + if (!old_type_ptr) return false; + const_tree old_type = *old_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_assert(TREE_CODE(op1) == INTEGER_CST); + + switch(has_integer_constant) + { + case true: + { + tree pointer = is_op1_int_cst ? op0 : op1; + tree pointer_type = TREE_TYPE(pointer); + 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); + } + break; + case false: + { + 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(gsi, pointer, variable, old_type); + unsigned int operand = TREE_CODE(TREE_TYPE(op0)) == POINTER_TYPE ? 2 : 1; + gimple_set_op(stmt, operand, new_variable); + } + break; + default: + gcc_unreachable(); + break; + } + + return true; +} + +static bool +rewrite_assign_rhs(gimple *stmt, gimple_stmt_iterator &gsi, hash_map<const_tree, const_tree> &type_map, hash_map<const_tree, const_tree> &inverse) +{ + bool is_stmt_rewritten = false; + //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? + enum tree_code code = gimple_expr_code(stmt); + 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); + const_tree* new_type_ptr = 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 (new_type_ptr) TREE_TYPE(lhs) = (tree)*new_type_ptr; + return retval; + } + break; + case POINTER_DIFF_EXPR: + { + // It's weird. Test case #4 + // uses this, but the answer + // is to do nothing... + } + default: + 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, gimple_stmt_iterator &gsi, hash_map<const_tree, const_tree> &type_map, hash_map<const_tree, const_tree> &inverse) +{ + gcc_assert(stmt); + test_print_gimple_stmt(stmt); + tree lhs = gimple_call_lhs (stmt); + //LHS can be null. Example `foo()` + if (!lhs) return; + + //TODO: is there anything else I need to do? + rewrite_expr(lhs, type_map, 0); +} + +static void +rewrite_call_rhs(gimple *stmt, gimple_stmt_iterator &gsi, hash_map<const_tree, const_tree> &type_map, hash_map<const_tree, const_tree> &inverse) +{ + gcc_assert(stmt); + // PRETTY SURE YOU MUST HAVE A FUNCTION + // TO CALL + 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)); + //TODO: What happens with void? + gcc_assert(return_type); + + const_tree *new_type = type_map.get(return_type); + if (!new_type) return; + + /* + * This is not the way to do it. + TREE_TYPE(fn) = (tree) *new_type; + gcall *call_new = dyn_cast<gcall*>(stmt); + tree return_type_new = gimple_call_return_type(call); + test_log("after what is the return type %s", 0, get_type_name(return_type_new)); + */ + + + +} + +static void +rewrite_call(gimple *stmt, gimple_stmt_iterator &gsi, hash_map<const_tree, const_tree> &type_map, hash_map<const_tree, const_tree> &inverse) +{ + rewrite_call_lhs(stmt, gsi, type_map, inverse); + rewrite_call_rhs(stmt, gsi, type_map, inverse); +} + + +static void +rewrite_assign(gimple *stmt, gimple_stmt_iterator &gsi, hash_map<const_tree, const_tree> &type_map, hash_map<const_tree, const_tree> &inverse) +{ + gcc_assert(stmt); + test_print_gimple_stmt(stmt); + tree lhs = gimple_assign_lhs (stmt); + const bool lhs_rewrite = rewrite_expr(lhs, type_map, 0); + const bool rhs_rewrite = rewrite_assign_rhs(stmt, gsi, type_map, inverse); + test_print_gimple_stmt(stmt); +} + +static void +rewrite_stmt(gimple *stmt, gimple_stmt_iterator &gsi, hash_map<const_tree, const_tree> &type_map, hash_map<const_tree, const_tree> &inverse) +{ + gcc_assert(stmt); + const enum gimple_code code = gimple_code(stmt); + switch (code) + { + case GIMPLE_ASSIGN: + rewrite_assign(stmt, gsi, type_map, inverse); + break; + case GIMPLE_CALL: + rewrite_call(stmt, gsi, type_map, inverse); + break; + default: + { + const char* const gimple_code_str = gimple_code_name[code]; + test_log("gimple_code %s", 0, gimple_code_str); + } + break; + } + return; +} + +static void +rewrite_basic_block(basic_block bb, hash_map<const_tree, const_tree> &type_map, hash_map<const_tree, const_tree> &inverse) +{ + gcc_assert(bb); + for (gimple_stmt_iterator gsi = gsi_start_bb(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, hash_map<const_tree, const_tree> &type_map, hash_map<const_tree, const_tree> &inverse) +{ + 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) + { + rewrite_basic_block(bb, type_map, inverse); + } + pop_cfun(); + print_function(cnode); +} + +static void +rewrite_local_decl(tree var_decl, hash_map<const_tree, const_tree> &type_map) +{ + gcc_assert(var_decl); + gcc_assert(TREE_CODE(var_decl) == VAR_DECL); + const_tree type = TREE_TYPE(var_decl); + gcc_assert(type); + const_tree* new_type_ptr = type_map.get(type); + + if (!new_type_ptr) return; + + const_tree new_type = *new_type_ptr; + gcc_assert(new_type); + //FIXME: We know these are record types + //make more general later. + const char* identifier_old = get_type_name(type); + const char* identifier_new = get_type_name(new_type); + test_write("rewriting,local_decl"); + test_print_generic_decl(var_decl); + test_write(","); + TREE_TYPE(var_decl) = (tree) 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, hash_map<const_tree, const_tree> &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(cgraph_node *cnode, hash_map<const_tree, const_tree> &type_map) +{ + //TODO: Do we need to do get_untransformed_body here? + for (tree parm = DECL_ARGUMENTS(cnode->decl); parm; parm = DECL_CHAIN(parm)) + { + tree parm_type = TREE_TYPE(parm); + const_tree *type_ptr = type_map.get(parm_type); + if (!type_ptr) continue; + + test_log("before rewrite_function_arg %s", 0, get_type_name(parm_type)); + TREE_TYPE(parm) = (tree) *type_ptr; + relayout_decl(parm); + tree parm_type_2 = TREE_TYPE(parm); + test_log("after rewrite_function_arg %s", 0, get_type_name(parm_type_2)); + } +} + +static void +rewrite_function_return_type(cgraph_node *cnode, hash_map<const_tree, const_tree> &type_map) +{ + + gcc_assert(TREE_CODE(cnode->decl) == FUNCTION_DECL); + tree function_type = TREE_TYPE(cnode->decl); + gcc_assert(function_type); + // TODO: We do not support method's yet. + gcc_assert(TREE_CODE(function_type) == FUNCTION_TYPE); + tree function_return_type = TREE_TYPE(function_type); + //TODO: What happens with void return + gcc_assert(function_return_type); + + test_log("before rewrite_function_return_type %s", 0, get_type_name(function_return_type)); + const_tree *new_type_ptr = type_map.get(function_return_type); + if (!new_type_ptr) return; + + TREE_TYPE(function_type) = (tree) *new_type_ptr; + //relayout_decl(cnode->decl); + tree function_type_2 = TREE_TYPE(cnode->decl); + tree function_return_type_2 = TREE_TYPE(function_type_2); + test_log("after rewrite_function_return_type %s", 0, get_type_name(function_return_type_2)); +} + +static void +rewrite_function(cgraph_node *cnode, hash_map<const_tree, const_tree> &type_map, hash_map<const_tree, const_tree> &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(hash_map<const_tree, const_tree> &type_map, hash_map<const_tree, const_tree> &inverse) +{ + cgraph_node *cnode = NULL; + FOR_EACH_DEFINED_FUNCTION(cnode) + { + rewrite_function(cnode, type_map, inverse); + } +} + +//TODO: +//Get rid of this function but make sure test still pass +bool +rewrite_references_to_modified_structs_internal(const_tree const &old_type, + const_tree* new_type, + __attribute__((unused))void*) +{ + const char* identifier = get_type_name(old_type); + test_log("rewriting,%s,%s", 0, identifier, get_type_name(*new_type)); + return true; +} + +static void +rewrite_references_to_modified_structs(hash_map<const_tree, const_tree> &type_map, hash_map<const_tree, const_tree> &inverse) +{ + type_map.traverse<void*, rewrite_references_to_modified_structs_internal>(NULL); + //FIXME: You'll need to iterate over more than just + //defined functions. + rewrite_functions(type_map, inverse); +} + +bool +compute_inverse_type_map_internal(const_tree const & old_type, + const_tree* new_type, + hash_map<const_tree, const_tree>* inverse_map) +{ + gcc_assert(new_type && *new_type); + gcc_assert(inverse_map); + inverse_map->put(*new_type, old_type); + 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(hash_map<const_tree, const_tree> &mod_type_map, + hash_map<const_tree, const_tree> &inverse_map) +{ + mod_type_map.traverse<hash_map<const_tree, const_tree>*, compute_inverse_type_map_internal>(&inverse_map); +} + +static unsigned int +iphw_execute() +{ + test_log("Executing structreorg", 0); + + //TODO: change orig_type_map to hash_set. + hash_map<const_tree, tree> orig_type_map; + collect_orig_structs(orig_type_map); + + //TODO: ideally mod_type_map would be a bidirectional map. + //but we work with what we have. + hash_map<const_tree, const_tree> mod_type_map; + compute_modified_structs(orig_type_map, mod_type_map); + hash_map<const_tree, const_tree> inverse_type_map; + compute_inverse_type_map(mod_type_map, inverse_type_map); + rewrite_references_to_modified_structs(mod_type_map, inverse_type_map); + return 0; +} int str_reorg_dead_field_eliminate( Info *info) { - // TBD - //DEBUG ( "Running str_reorg_dead_field_eliminate\n"); - return 0; + gcc_assert(in_lto_p && flag_ipa_typelist); + return iphw_execute(); } |