From bae5500345704fe5fa7f883a418397379fce0553 Mon Sep 17 00:00:00 2001 From: Erick Ochoa Date: Tue, 10 Mar 2020 14:57:28 +0100 Subject: Adds test to count field read --- gcc/ipa-hello-world.c | 151 ++++++++++++++++++--- gcc/ipa-type-escape-analysis.c | 39 ++++-- gcc/ipa-type-escape-analysis.h | 21 +++ .../ipa/ipa-access-counter-00-simple-read-0.c | 17 +++ 4 files changed, 194 insertions(+), 34 deletions(-) create mode 100644 gcc/ipa-type-escape-analysis.h create mode 100644 gcc/testsuite/gcc.dg/ipa/ipa-access-counter-00-simple-read-0.c diff --git a/gcc/ipa-hello-world.c b/gcc/ipa-hello-world.c index 9040a9360a6..cb69b1ea713 100644 --- a/gcc/ipa-hello-world.c +++ b/gcc/ipa-hello-world.c @@ -21,29 +21,142 @@ #include "tree-ssa-ccp.h" #include "stringpool.h" #include "attribs.h" +#include "tree-cfg.h" +#include +#include -static unsigned int -iphw_execute() +#include "ipa-type-escape-analysis.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); +} + +typedef std::pair fields; +typedef std::pair 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; + 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 field_access_counter; +typedef std::set record_set; + +/* 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(record_set &non_escaping_records) { - cgraph_node *node; - FOR_EACH_FUNCTION(node) + field_access_counter counter; + cgraph_node *cnode = NULL; + FOR_EACH_DEFINED_FUNCTION(cnode) { - if (dump_file) fprintf(dump_file, "the name of the function is %s\n", node->dump_asm_name()); - - if (strcmp(node->dump_asm_name(), "bar/49") != 0) continue; - - node->get_untransformed_body(); - fprintf(dump_file, "we are where we need to be\n"); - cgraph_node *clone = node->create_version_clone_with_body( - node->collect_callers(), - NULL, - NULL, - NULL, - NULL, - "helloworld", - NULL); + gcc_assert(cnode); + print_function(cnode); } + return counter; +} + +bool +_calculate_non_escaping_records(const_tree const &type, escaping_info *info, record_set *non_escaping_records) +{ + gcc_assert(info); + gcc_assert(non_escaping_records); + + enum tree_code tree_code_type = TREE_CODE(type); + const bool is_record_type = RECORD_TYPE == tree_code_type; + 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(&non_escaping_records); + return non_escaping_records; +} + +static unsigned int +iphw_execute() +{ + type_map escaping_types; + calculate_escaping_types(escaping_types); + record_set non_escaping_records = calculate_non_escaping_records(escaping_types); + field_access_counter counter = count_access_for_types(non_escaping_records); return 0; } @@ -51,7 +164,7 @@ namespace { const pass_data pass_data_ipa_hello_world = { SIMPLE_IPA_PASS, - "hello_world", + "hello-world", OPTGROUP_NONE, TV_NONE, (PROP_cfg | PROP_ssa), diff --git a/gcc/ipa-type-escape-analysis.c b/gcc/ipa-type-escape-analysis.c index a838f4d445f..b005fe362ec 100644 --- a/gcc/ipa-type-escape-analysis.c +++ b/gcc/ipa-type-escape-analysis.c @@ -30,6 +30,7 @@ #include #include "ipa-str-reorg-utils.h" +#include "ipa-type-escape-analysis.h" // First we need to collect all types void @@ -44,17 +45,6 @@ log (const char * const format, ...) } -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; -typedef hash_map type_map; 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); @@ -458,7 +448,7 @@ collect_types(type_map &escape_map) } bool -_print_types( const_tree const &type, escaping_info *info, __attribute((unused)) void*) +_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; @@ -705,6 +695,7 @@ is_any_variable_escaping(type_map &escape_map) } } +//TODO: place in header file inline static void print_function (cgraph_node *cnode) { @@ -847,15 +838,33 @@ cast_to_void_in_program(type_map &escape_map) } } -static unsigned int -iphw_execute() +/* 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) { - 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; } 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 type_map; +void calculate_escaping_types(type_map&); + +#endif 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..c9e68989142 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipa-access-counter-00-simple-read-0.c @@ -0,0 +1,17 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-hello-world -fdump-ipa-hello-world" } */ + +#include + +struct astruct_s { _Bool a; _Bool b; _Bool c;}; +struct astruct_s astruct; + + +int +main () +{ + 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" } } */ -- cgit v1.2.3