diff options
author | Erick Ochoa <erick.ochoa@theobroma-systems.com> | 2020-05-14 14:48:22 +0200 |
---|---|---|
committer | Erick Ochoa <erick.ochoa@theobroma-systems.com> | 2020-05-14 14:48:22 +0200 |
commit | cf730672e416091269ef355bd4a8be5cf2a23785 (patch) | |
tree | 5c68343de00c674213e714e81eb7782869a4dc78 | |
parent | 5e74c695ff9bef9ba000884c1d7e2b8762e90fc3 (diff) | |
parent | a22362346872039fa01a22fc8dc2c26de16d24a0 (diff) |
Merge branch 'erick/type-escape-analysis-refactor' into erick/type-escape-analysis-merge
-rwxr-xr-x | build.sh | 15 | ||||
-rw-r--r-- | gcc/Makefile.in | 4 | ||||
-rw-r--r-- | gcc/collect-types.c | 422 | ||||
-rw-r--r-- | gcc/collect-types.h | 19 | ||||
-rw-r--r-- | gcc/common.opt | 10 | ||||
-rw-r--r-- | gcc/compare-types.c | 457 | ||||
-rw-r--r-- | gcc/compare-types.h | 11 | ||||
-rw-r--r-- | gcc/gimple.h | 28 | ||||
-rw-r--r-- | gcc/ipa-escape-analysis.c | 821 | ||||
-rw-r--r-- | gcc/name-types.c | 420 | ||||
-rw-r--r-- | gcc/name-types.h | 11 | ||||
-rw-r--r-- | gcc/passes.def | 1 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/ipa/type-playground-00.c | 21 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/ipa/type-playground-01.c | 19 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/ipa/type-playground-02.c | 19 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/ipa/type-playground-03.c | 25 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/ipa/type-playground-04.c | 25 | ||||
-rw-r--r-- | gcc/tree-pass.h | 1 | ||||
-rw-r--r-- | gcc/types-inlines.h | 37 |
19 files changed, 2350 insertions, 16 deletions
@@ -3,16 +3,11 @@ set -e set -u -installdir=${1:-"gcc-inst"} -mkdir -p $HOME/code/gcc-build/ -mkdir -p $HOME/code/${installdir}/ -pushd $HOME/code/gcc-build/ -if find . -mindepth 1 -print -quit 2>/dev/null | grep -q . ; then - echo "not empty?" -else - $OLDPWD/configure --disable-bootstrap --disable-libsanitizer --enable-__cxa_atexit --enable-shared --disable-libsanitizer --enable-languages=c,c++,fortran --enable-lto --enable-gold --enable-linker-build-id --with-cpu-emag --prefix="$HOME/code/${installdir}/" -fi - +suffix=${1:-""} +mkdir -p $HOME/code/gcc-build${suffix}/ +mkdir -p $HOME/code/gcc-inst${suffix}/ +pushd $HOME/code/gcc-build${suffix}/ +$OLDPWD/configure --disable-bootstrap --disable-libsanitizer --enable-__cxa_atexit --enable-shared --disable-libsanitizer --enable-languages=c,c++,fortran --enable-lto --enable-gold --enable-linker-build-id --with-cpu-emag --prefix="$HOME/code/gcc-inst${suffix}/" make -j `nproc` make install -j `nproc` make check-gcc RUNTESTFLAGS="ipa.exp" diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 082d48a496d..419f64ec70e 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1405,6 +1405,10 @@ OBJS = \ internal-fn.o \ ipa-type-escape-analysis.o \ ipa-hello-world.o \ + ipa-escape-analysis.o \ + compare-types.o \ + collect-types.o \ + name-types.o \ ipa-cp.o \ ipa-sra.o \ ipa-devirt.o \ diff --git a/gcc/collect-types.c b/gcc/collect-types.c new file mode 100644 index 00000000000..0ce6cccb025 --- /dev/null +++ b/gcc/collect-types.c @@ -0,0 +1,422 @@ +#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-ssa-alias.h" +#include "tree-ssanames.h" +#include "gimple.h" +#include "cfg.h" +#include "gimple-iterator.h" +#include "gimple-ssa.h" + +#include "compare-types.h" +#include "types-inlines.h" +#include "name-types.h" +#include <set> + +#include "collect-types.h" + +static bool filter_boring_types(const_tree type, ptrset_t &); + +static bool +filter_boring_types_wrapper(const_tree type, ptrset_t &types) +{ + gcc_assert(type); + const_tree inner_type = TREE_TYPE(type); + gcc_assert(inner_type); + return filter_boring_types(inner_type, types); +} + +static bool +filter_boring_types_array(const_tree type, ptrset_t &types) +{ + assert_is_type(type, ARRAY_TYPE); + return filter_boring_types_wrapper(type, types); +} + +static bool +filter_boring_types_reference(const_tree type, ptrset_t &types) +{ + assert_is_type(type, REFERENCE_TYPE); + return filter_boring_types_wrapper(type, types); +} + +static bool +filter_boring_types_pointer(const_tree type, ptrset_t &types) +{ + assert_is_type(type, POINTER_TYPE); + return filter_boring_types_wrapper(type, types); +} + +static bool +filter_boring_types_record(const_tree type, ptrset_t &types) +{ + assert_is_type(type, RECORD_TYPE); + return false; +} + +static bool +filter_boring_types_unions(const_tree type, ptrset_t &types) +{ + const enum tree_code code = TREE_CODE(type); + const bool is_union = UNION_TYPE == code; + const bool is_qual_union = QUAL_UNION_TYPE == code; + const bool is_valid_input = is_union || is_qual_union; + gcc_assert(is_valid_input); + + for (tree field = TYPE_FIELDS(type) ; field ; field = DECL_CHAIN(field)) + { + const_tree field_type = TREE_TYPE(field); + gcc_assert(field_type); + const bool is_boring = filter_boring_types(field_type, types); + if (!is_boring) return is_boring; + } + + return true; +} + +static bool +filter_boring_types_union(const_tree type, ptrset_t &types) +{ + assert_is_type(type, UNION_TYPE); + return filter_boring_types_unions(type, types); +} + +static bool +filter_boring_types_qual_union(const_tree type, ptrset_t &types) +{ + assert_is_type(type, QUAL_UNION_TYPE); + return filter_boring_types_unions(type, types); +} + +static bool +filter_boring_types_function_or_method(const_tree type, ptrset_t &types) +{ + const enum tree_code code = TREE_CODE(type); + const bool is_function = FUNCTION_TYPE == code; + const bool is_method = METHOD_TYPE == code; + const bool is_valid_input = is_function || is_method; + gcc_assert(is_valid_input); + + const_tree ret_type = TREE_TYPE(type); + gcc_assert(ret_type); + const bool retval_is_boring = filter_boring_types(ret_type, types); + if (!retval_is_boring) return retval_is_boring; + + tree arg_node = TYPE_ARG_TYPES(type); + while (NULL_TREE != arg_node) + { + tree arg_node_type = TREE_VALUE(arg_node); + gcc_assert(arg_node_type); + const bool arg_is_boring = filter_boring_types(arg_node_type, types); + arg_node = TREE_CHAIN(arg_node); + if (!arg_is_boring) return arg_is_boring; + } + + return true; +} + +static bool +filter_boring_types_function(const_tree type, ptrset_t &types) +{ + assert_is_type(type, FUNCTION_TYPE); + return filter_boring_types_function_or_method(type, types); +} + +static bool +filter_boring_types_method(const_tree type, ptrset_t &types) +{ + assert_is_type(type, METHOD_TYPE); + return filter_boring_types_function_or_method(type, types); +} + +static bool +filter_boring_types(const_tree type, ptrset_t &types) +{ + // boring types are those that do not point to + // a struct + gcc_assert(type); + + // Optimization to speed up the filter. + // Memoization + const bool seen_before = types.in_universe(type); + if (seen_before) + { + const bool in_points_to_record = types.in_points_to_record(type); + const bool in_complement = types.in_complement(type); + const bool _xor = in_points_to_record != in_complement; + gcc_assert(_xor); + return in_complement; + } + + + bool is_boring = true; + enum tree_code code = TREE_CODE(type); + switch (code) + { + case ARRAY_TYPE: + is_boring = filter_boring_types_array(type, types); + break; + case RECORD_TYPE: + is_boring = filter_boring_types_record(type, types); + break; + case UNION_TYPE: + is_boring = filter_boring_types_union(type, types); + break; + case QUAL_UNION_TYPE: + is_boring = filter_boring_types_qual_union(type, types); + break; + case REFERENCE_TYPE: + is_boring = filter_boring_types_reference(type, types); + break; + case POINTER_TYPE: + is_boring = filter_boring_types_pointer(type, types); + break; + case FUNCTION_TYPE: + is_boring = filter_boring_types_function(type, types); + break; + case METHOD_TYPE: + is_boring = filter_boring_types_method(type, types); + break; + default: + // I will probably need to fuzz. + break; + } + + // This should be one the two only times we call insert! + types.insert(type, !is_boring); + + return is_boring; +} + +static const unsigned filter_buffer_size = 1; +typedef bool (*filter_t)(const_tree, ptrset_t&); +const extern filter_t filter[filter_buffer_size] = +{ + filter_boring_types, +}; + +static void +collect_types_from_record_or_union_type(const_tree type, ptrset_t &types) +{ + gcc_assert(type); + const enum tree_code code = TREE_CODE(type); + const bool is_union = UNION_TYPE == code; + const bool is_record = RECORD_TYPE == code; + const bool is_valid_input = is_union || is_record; + gcc_assert(is_valid_input); + + for (tree field = TYPE_FIELDS(type) ; field ; field = DECL_CHAIN(field)) + { + const_tree field_type = TREE_TYPE(field); + gcc_assert(field_type); + collect_types(field_type, types); + } +} + +static void +collect_types_from_record_type(const_tree type, ptrset_t &types) +{ + assert_is_type(type, RECORD_TYPE); + collect_types_from_record_or_union_type(type, types); +} + +static void +collect_types_from_union_type(const_tree type, ptrset_t &types) +{ + assert_is_type(type, UNION_TYPE); + collect_types_from_record_or_union_type(type, types); +} + +static void +collect_types_from_wrapper_type(const_tree type, ptrset_t &types) +{ + gcc_assert(type); + const_tree inner_type = TREE_TYPE(type); + gcc_assert(inner_type); + collect_types(inner_type, types); +} + +static void +collect_types_from_pointer_type(const_tree type, ptrset_t &types) +{ + assert_is_type(type, POINTER_TYPE); + collect_types_from_wrapper_type(type, types); +} + +static void +collect_types_from_reference_type(const_tree type, ptrset_t &types) +{ + assert_is_type(type, REFERENCE_TYPE); + collect_types_from_wrapper_type(type, types); +} + +static void +collect_types_from_array_type(const_tree type, ptrset_t &types) +{ + assert_is_type(type, ARRAY_TYPE); + collect_types_from_wrapper_type(type, types); +} + +static void +collect_types_from_function_or_method_type(const_tree type, ptrset_t &types) +{ + gcc_assert(type); + const enum tree_code code = TREE_CODE(type); + const bool is_function = FUNCTION_TYPE == code; + const bool is_method = METHOD_TYPE == code; + const bool is_valid_input = is_function || is_method; + gcc_assert(is_valid_input); + + const_tree ret_type = TREE_TYPE(type); + gcc_assert(ret_type); + collect_types(ret_type, types); + + tree arg_node = TYPE_ARG_TYPES(type); + while (NULL_TREE != arg_node) + { + tree arg_node_type = TREE_VALUE(arg_node); + gcc_assert(arg_node_type); + collect_types(arg_node_type, types); + arg_node = TREE_CHAIN(arg_node); + } +} + +static void +collect_types_from_function_type(const_tree type, ptrset_t &types) +{ + assert_is_type(type, FUNCTION_TYPE); + collect_types_from_function_or_method_type(type, types); +} + +static void +collect_types_from_method_type(const_tree type, ptrset_t &types) +{ + assert_is_type(type, METHOD_TYPE); + collect_types_from_function_or_method_type(type, types); +} + +void +points_to_record_sets_s::insert(const_tree type, bool in_points_to_record) +{ + gcc_assert(type); + this->universe.insert(type); + in_points_to_record ? this->points_to_record.insert(type) : this->complement.insert(type); + // let's just double check... + const bool in_points_to_set = this->in_points_to_record(type); + const bool in_complement = this->in_complement(type); + const bool _xor = in_points_to_set != in_complement; + gcc_assert(_xor); +} + +bool +points_to_record_sets_s::in_universe(const_tree type) const +{ + gcc_assert(type); + const bool seen_before = this->universe.find(type) != this->universe.end(); + return seen_before; +} + +bool +points_to_record_sets_s::in_points_to_record(const_tree type) const +{ + gcc_assert(type); + const bool seen_before = this->points_to_record.find(type) != this->points_to_record.end(); + return seen_before; +} + +bool +points_to_record_sets_s::in_complement(const_tree type) const +{ + gcc_assert(type); + const bool seen_before = this->complement.find(type) != this->complement.end(); + return seen_before; +} + +void +collect_types(const_tree type, ptrset_t &types) +{ + if (!type) return; + + // This should be the only case we call to find if + // we have seen the type before! + const bool in_set = types.in_universe(type); + // memoized. + if (in_set) return; + + // we should have filters here + // such that if we are processing a type + // which we know somehow that it is not a type we are interested + // then we just return immediately. + // maybe even add it to a set of blacklisted types so that we + // memoize and do things faster... + bool is_boring = false; + for (unsigned i = 0; i < filter_buffer_size; i++) + { + is_boring |= filter[i](type, types); + } + + const enum tree_code code = TREE_CODE(type); + switch (code) + { + case VOID_TYPE: + case INTEGER_TYPE: + case REAL_TYPE: + case FIXED_POINT_TYPE: + case COMPLEX_TYPE: + case ENUMERAL_TYPE: + case BOOLEAN_TYPE: + case OFFSET_TYPE: + // simple cases and we want to allow them + break; + case RECORD_TYPE: + collect_types_from_record_type(type, types); + break; + case POINTER_TYPE: + collect_types_from_pointer_type(type, types); + break; + case REFERENCE_TYPE: + collect_types_from_reference_type(type, types); + break; + case ARRAY_TYPE: + collect_types_from_array_type(type, types); + break; + case UNION_TYPE: + collect_types_from_union_type(type, types); + break; + case FUNCTION_TYPE: + collect_types_from_function_type(type, types); + break; + case METHOD_TYPE: + collect_types_from_method_type(type, types); + break; + case QUAL_UNION_TYPE: + case LANG_TYPE: + default: + log("%s\n", get_tree_code_name(code)); + gcc_unreachable(); + break; + } + + // This should be one the two only times we call insert! + types.insert(type, !is_boring); +} diff --git a/gcc/collect-types.h b/gcc/collect-types.h new file mode 100644 index 00000000000..883c08d36dc --- /dev/null +++ b/gcc/collect-types.h @@ -0,0 +1,19 @@ +#pragma once + +#include "tree.h" +#include <set> + +typedef std::set<const_tree> typeset; +struct points_to_record_sets_s { + typeset universe; + typeset points_to_record; + typeset complement; + bool in_universe(const_tree) const; + bool in_points_to_record(const_tree) const; + bool in_complement(const_tree) const; + void insert(const_tree, bool); +}; + +typedef struct points_to_record_sets_s ptrset_t; + +extern void collect_types(const_tree type, ptrset_t &ptrset_t); diff --git a/gcc/common.opt b/gcc/common.opt index 4aca7c9547a..86aeb0d39e7 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -3445,4 +3445,14 @@ 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 + +ftp-types-compared= +Common Joined Report Var(flag_tp_types_compared) Init(0) + +ftp-comparison-functions= +Common Joined Report Var(flag_tp_comparison_functions) Init(0) + ; This comment is to ensure we retain the blank line above. diff --git a/gcc/compare-types.c b/gcc/compare-types.c new file mode 100644 index 00000000000..4be9d28151d --- /dev/null +++ b/gcc/compare-types.c @@ -0,0 +1,457 @@ +#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-ssa-alias.h" +#include "tree-ssanames.h" +#include "gimple.h" +#include "cfg.h" +#include "gimple-iterator.h" +#include "gimple-ssa.h" + +#include "compare-types.h" +#include "types-inlines.h" +#include "name-types.h" + +static bool +is_incomplete_type(const_tree a) +{ + gcc_assert(a); + const_tree type_size = TYPE_SIZE(a); + return NULL_TREE == type_size; +} + +bool +eq_main_variant(const_tree a, const_tree b) +{ + gcc_assert(a && b); + const_tree main_variant_a = TYPE_MAIN_VARIANT(a); + const_tree main_variant_b = TYPE_MAIN_VARIANT(b); + gcc_assert(main_variant_a && main_variant_b); + const bool are_equal = main_variant_a == main_variant_b; + return are_equal; +} + +bool +eq_canonical_internal(const_tree a, const_tree b) +{ + gcc_assert(a && b); + const_tree canonical_a = TYPE_CANONICAL(a); + const_tree canonical_b = TYPE_CANONICAL(b); + const bool are_equal = canonical_a == canonical_b; + return are_equal; +} + +static bool +eq_record_or_union_types(const_tree a, const_tree b, const bool force_structural) +{ + gcc_assert(a && b); + + tree field_a = TYPE_FIELDS(a); + tree field_b = TYPE_FIELDS(b); + while (field_a || field_b) + { + const bool different_lengths = (bool)field_a != (bool)field_b; + if (different_lengths) return false; + + const_tree field_type_a = TREE_TYPE(field_a); + const_tree field_type_b = TREE_TYPE(field_b); + gcc_assert(field_type_a && field_type_b); + const bool same_field_types = eq_types(field_type_a, field_type_b, force_structural); + if (!same_field_types) return false; + + field_a = DECL_CHAIN(field_a); + field_b = DECL_CHAIN(field_b); + } + + return true; +} + +static bool +eq_record_types(const_tree a, const_tree b, const bool force_structural) +{ + gcc_assert(a && b); + assert_is_type(a, RECORD_TYPE); + assert_is_type(b, RECORD_TYPE); + return eq_record_or_union_types(a, b, force_structural); +} + +static bool +eq_union_types(const_tree a, const_tree b, const bool force_structural) +{ + gcc_assert(a && b); + assert_is_type(a, UNION_TYPE); + assert_is_type(b, UNION_TYPE); + return eq_record_or_union_types(a, b, force_structural); +} + +static bool +eq_wrapper_types(const_tree a, const_tree b, const bool force_structural) +{ + gcc_assert(a && b); + const_tree inner_type_a = TREE_TYPE(a); + const_tree inner_type_b = TREE_TYPE(b); + gcc_assert(inner_type_a && inner_type_b); + return eq_types(inner_type_a, inner_type_b, force_structural); +} + +static bool +eq_pointer_types(const_tree a, const_tree b, const bool force_structural) +{ + gcc_assert(a && b); + assert_is_type(a, POINTER_TYPE); + assert_is_type(b, POINTER_TYPE); + return eq_wrapper_types(a, b, force_structural); +} + +static bool +eq_reference_types(const_tree a, const_tree b, const bool force_structural) +{ + gcc_assert(a && b); + assert_is_type(a, REFERENCE_TYPE); + assert_is_type(b, REFERENCE_TYPE); + return eq_wrapper_types(a, b, force_structural); +} + +static bool +eq_array_types(const_tree a, const_tree b, const bool force_structural) +{ + gcc_assert(a && b); + assert_is_type(a, ARRAY_TYPE); + assert_is_type(b, ARRAY_TYPE); + return eq_wrapper_types(a, b, force_structural); +} + +static bool +eq_function_or_method_types(const_tree a, const_tree b, const bool force_structural) +{ + gcc_assert(a && b); + const_tree return_type_a = TREE_TYPE(a); + const_tree return_type_b = TREE_TYPE(b); + const bool is_return_equal = eq_types(return_type_a, return_type_b, force_structural); + if (!is_return_equal) return false; + + tree parm_a = TYPE_ARG_TYPES(a); + tree parm_b = TYPE_ARG_TYPES(b); + + while (parm_a || parm_b) + { + const bool different_lengths = (bool)parm_a != (bool)parm_b; + if (different_lengths) return false; + + const_tree parm_type_a = TREE_VALUE(parm_a); + const_tree parm_type_b = TREE_VALUE(parm_b); + gcc_assert(parm_type_a && parm_type_b); + const bool same_field_types = eq_types(parm_type_a, parm_type_b, force_structural); + if (!same_field_types) return false; + + parm_a = TREE_CHAIN(parm_a); + parm_b = TREE_CHAIN(parm_b); + } + + return true; +} + +static bool +eq_function_types(const_tree a, const_tree b, const bool force_structural) +{ + gcc_assert(a && b); + assert_is_type(a, FUNCTION_TYPE); + assert_is_type(b, FUNCTION_TYPE); + return eq_function_or_method_types(a, b, force_structural); +} + +static bool +eq_method_types(const_tree a, const_tree b, const bool force_structural) +{ + gcc_assert(a && b); + assert_is_type(a, METHOD_TYPE); + assert_is_type(b, METHOD_TYPE); + + const_tree base_type_a = TYPE_METHOD_BASETYPE(a); + const_tree base_type_b = TYPE_METHOD_BASETYPE(b); + const bool eq_base_types = eq_types(base_type_a, base_type_b, force_structural); + return eq_base_types && eq_function_or_method_types(a, b, force_structural); +} + +static bool eq_structural(const_tree a, const_tree b, const bool force_structural = false); + +bool +eq_pointer(const_tree a, const_tree b) +{ + return a == b; +} + +bool +eq_identifier(const_tree a, const_tree b) +{ + std::string name_a = get_type_identifier(a); + std::string name_b = get_type_identifier(b); + return name_a.compare(name_b) == 0; +} + +static bool +eq_structural(const_tree a, const_tree b, const bool force_structural) +{ + gcc_assert(a && b); + const enum tree_code code_a = TREE_CODE(a); + const enum tree_code code_b = TREE_CODE(b); + const bool equal_code = code_a == code_b; + if (!equal_code) return false; + + const enum tree_code code = code_a; + switch (code) + { + //FIXME: Just because two codes are the same + //doesn't mean they are the same type + //E.g. short != int + // float != double + // enum a != enum b + case VOID_TYPE: + case INTEGER_TYPE: + case REAL_TYPE: + case FIXED_POINT_TYPE: + case COMPLEX_TYPE: + case ENUMERAL_TYPE: + case BOOLEAN_TYPE: + case OFFSET_TYPE: + return true; + break; + default: + break; + } + + switch (code) + { + case RECORD_TYPE: + return eq_record_types(a, b, force_structural); + break; + case POINTER_TYPE: + return eq_pointer_types(a, b, force_structural); + break; + case REFERENCE_TYPE: + return eq_reference_types(a, b, force_structural); + break; + case ARRAY_TYPE: + return eq_array_types(a, b, force_structural); + break; + case UNION_TYPE: + return eq_union_types(a, b, force_structural); + break; + case FUNCTION_TYPE: + return eq_function_types(a, b, force_structural); + break; + case METHOD_TYPE: + return eq_method_types(a, b, force_structural); + break; + case QUAL_UNION_TYPE: + case LANG_TYPE: + default: + // Unimplemented + gcc_unreachable(); + break; + } + + gcc_unreachable(); + return false; +} + +bool +eq_canonical(const_tree a, const_tree b) +{ + gcc_assert(a && b); + const bool struct_eq_a = TYPE_STRUCTURAL_EQUALITY_P(a); + const bool struct_eq_b = TYPE_STRUCTURAL_EQUALITY_P(b); + const bool use_structural_equality = struct_eq_a || struct_eq_b; + const bool are_equal = use_structural_equality ? eq_structural(a, b) : eq_canonical_internal(a, b); + return are_equal; +} + + +bool +eq_type_compare(const_tree a, const_tree b) +{ + return eq_types(a, b); +} + +bool +eq_type_structural(const_tree a, const_tree b) +{ + return eq_types(a, b, true); +} + +bool +eq_types(const_tree a, const_tree b, const bool force_structural) +{ + gcc_assert(a && b); + // This should be before comparing incomplete types. + // This is not enabled by default, and it is used for + // testing structural equality limitations. + if (force_structural) return eq_structural(a, b, force_structural); + + // eq_structural (a-incomplete, a-complete) = false + // eq_main_variant(a-incomplete, a-complete) = false + // eq_canonical (a-incomplete, a-complete) = false + // Fallback to eq_identifier only here. + // This should be the only static call to eq_identifier! + const bool is_a_incomplete = is_incomplete_type(a); + const bool is_b_incomplete = is_incomplete_type(b); + const bool compare_incomplete_types = is_a_incomplete || is_b_incomplete; + if (compare_incomplete_types) return eq_identifier(a, b); + + + if (eq_main_variant(a, b)) return true; + + if (!eq_canonical(a, b)) return false; + + // optimistic... + // maybe we should have a MAYBE? + //gcc_unreachable(); + return false; +} + +namespace test_type_equality { + +static void +test_main_variant() +{ + tree void_a = make_node(VOID_TYPE); + tree void_b = build_variant_type_copy(void_a); + const bool expected = true; + const bool observed = eq_types(void_a, void_b); + const bool success = expected == observed; + gcc_assert(success); +} + +static void +test_pointer_types_eq() +{ + tree pointer_a = make_node(POINTER_TYPE); + tree inner_type = make_node(INTEGER_TYPE); + TREE_TYPE(pointer_a) = inner_type; + tree pointer_b = build_variant_type_copy(pointer_a); + const bool expected = true; + const bool observed = eq_types(pointer_a, pointer_b); + const bool success = expected == observed; + gcc_assert(success); +} + +static void +test_pointer_types_ne() +{ + tree pointer_a = make_node(POINTER_TYPE); + tree inner_type_a = make_node(INTEGER_TYPE); + TREE_TYPE(pointer_a) = inner_type_a; + tree pointer_b = make_node(POINTER_TYPE); + tree inner_type_b = make_node(VOID_TYPE); + TREE_TYPE(pointer_b) = inner_type_b; + const bool expected = false; + const bool observed = eq_types(pointer_a, pointer_b, true); + const bool success = expected == observed; + gcc_assert(success); +} + +static void +test_pointer_types_eq_structurally() +{ + tree pointer_a = make_node(POINTER_TYPE); + tree inner_type_a = make_node(INTEGER_TYPE); + TREE_TYPE(pointer_a) = inner_type_a; + tree pointer_b = make_node(POINTER_TYPE); + tree inner_type_b = make_node(INTEGER_TYPE); + TREE_TYPE(pointer_b) = inner_type_b; + const bool expected = true; + const bool observed = eq_types(pointer_a, pointer_b, true); + const bool success = expected == observed; + gcc_assert(success); +} + +static void +test_void_eq() +{ + tree void_a = make_node(VOID_TYPE); + tree void_b = build_variant_type_copy(void_a); + const bool expected = true; + const bool observed = eq_types(void_a, void_b, true); + const bool success = expected == observed; + gcc_assert(success); +} + +static void +test_structural_equality_different_types() +{ + tree type_a = make_node(VOID_TYPE); + tree type_b = make_node(RECORD_TYPE); + const bool expected = false; + const bool observed = eq_types(type_a, type_b, true); + const bool success = expected == observed; + gcc_assert(success); +} + +static void +test_different_record_types() +{ + tree type_a = make_node(RECORD_TYPE); + tree type_b = make_node(RECORD_TYPE); + tree field_a = make_node(FIELD_DECL); + tree field_b = make_node(FIELD_DECL); + tree type_field_a = make_node(VOID_TYPE); + tree type_field_b = make_node(RECORD_TYPE); + TREE_TYPE(field_a) = type_field_a; + TREE_TYPE(field_b) = type_field_b; + TYPE_FIELDS(type_a) = field_a; + TYPE_FIELDS(type_b) = field_b; + const bool expected = false; + const bool observed = eq_types(type_a, type_b, true); + const bool success = expected == observed; + gcc_assert(success); +} + +static void +test_same_record_types() +{ + tree type_a = make_node(RECORD_TYPE); + tree field_a = make_node(FIELD_DECL); + tree type_field_a = make_node(VOID_TYPE); + TREE_TYPE(field_a) = type_field_a; + TYPE_FIELDS(type_a) = field_a; + tree type_b = build_variant_type_copy(type_a); + const bool expected = true; + const bool observed = eq_types(type_a, type_b, true); + const bool success = expected == observed; + gcc_assert(success); +} + +void +run_tests() +{ + test_main_variant(); + test_void_eq(); + test_structural_equality_different_types(); + test_different_record_types(); + test_same_record_types(); + test_pointer_types_eq_structurally(); + test_pointer_types_ne(); + test_pointer_types_eq(); + +} +} // test_type_equality diff --git a/gcc/compare-types.h b/gcc/compare-types.h new file mode 100644 index 00000000000..2c690fc3081 --- /dev/null +++ b/gcc/compare-types.h @@ -0,0 +1,11 @@ +#pragma once + +namespace test_type_equality { void run_tests(); }; +extern bool eq_identifier(const_tree a, const_tree b); +extern bool eq_pointer(const_tree a, const_tree b); +extern bool eq_main_variant(const_tree a, const_tree b); +extern bool eq_canonical(const_tree a, const_tree b); +extern bool eq_canonical_internal(const_tree a, const_tree b); +extern bool eq_type_compare(const_tree a, const_tree b); +extern bool eq_type_structural(const_tree a, const_tree b); +extern bool eq_types(const_tree a, const_tree b, const bool force_structural = false); diff --git a/gcc/gimple.h b/gcc/gimple.h index ca7fec6247e..e5b594a5b01 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -6494,6 +6494,23 @@ gimple_transaction_set_subcode (gtransaction *transaction_stmt, transaction_stmt->subcode = subcode; } + +/* Return the return value for GIMPLE_RETURN GS. */ + +static inline tree +gimple_return_retval (const greturn *gs) +{ + return gs->op[0]; +} + +static inline tree +gimple_return_retval(const gimple *gs) +{ + GIMPLE_CHECK(gs, GIMPLE_RETURN); + const greturn *gr = dyn_cast<const greturn *> (gs); + return gimple_return_retval (gr); +} + /* Return a pointer to the return value for GIMPLE_RETURN GS. */ static inline tree * @@ -6502,15 +6519,14 @@ gimple_return_retval_ptr (greturn *gs) return &gs->op[0]; } -/* Return the return value for GIMPLE_RETURN GS. */ - -static inline tree -gimple_return_retval (const greturn *gs) +static inline tree * +gimple_return_retval_ptr (gimple *gs) { - return gs->op[0]; + GIMPLE_CHECK(gs, GIMPLE_RETURN); + greturn *gr = dyn_cast<greturn *> (gs); + return gimple_return_retval_ptr (gr); } - /* Set RETVAL to be the return value for GIMPLE_RETURN GS. */ static inline void diff --git a/gcc/ipa-escape-analysis.c b/gcc/ipa-escape-analysis.c new file mode 100644 index 00000000000..04023a406a5 --- /dev/null +++ b/gcc/ipa-escape-analysis.c @@ -0,0 +1,821 @@ +#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-ssa-alias.h" +#include "tree-ssanames.h" +#include "gimple.h" +#include "cfg.h" +#include "gimple-iterator.h" +#include "gimple-ssa.h" + +#include "compare-types.h" +#include "types-inlines.h" +#include <set> +#include <string> + +#include "collect-types.h" +#include "name-types.h" + +//#define FUZZ_MODE 1 + +namespace type_playground { +enum type_comparison_func_enum { + EQ_POINTER = 0, + EQ_IDENTIFIER, + EQ_MAIN_VARIANT, + EQ_CANONICAL, + EQ_STRUCTURAL, + EQ_CANONICAL_STRICT, + EQ_COMPARE, + EQ_END +}; +static unsigned const type_comparisons = EQ_END; +static const char* names[type_comparisons] = { + "EQ_POINTER", + "EQ_IDENTIFIER", + "EQ_MAIN_VARIANT", + "EQ_CANONICAL", + "EQ_CANONICAL_STRICT", + "EQ_STRUCTURAL", + "EQ_COMPARE" +}; +typedef bool (*type_comparison_func_t)(const_tree, const_tree); +static type_comparison_func_t comparisons[type_comparisons] = { + eq_pointer, + eq_identifier, + eq_main_variant, + eq_canonical, + eq_canonical_internal, + eq_type_structural, + eq_type_compare +}; +} +static void collect_types_from_expr(const_tree expr, ptrset_t &types); + +inline void +is_gimple_code(gimple *stmt, const enum gimple_code ex_code) +{ + gcc_assert(stmt); + const enum gimple_code ob_code = gimple_code(stmt); + const bool succeeds = ex_code == ob_code; + gcc_assert(succeeds); +} + +inline void +is_gimple_rhs_class(gimple *stmt, const enum gimple_rhs_class ex_class) +{ + gcc_assert(stmt); + is_gimple_code(stmt, GIMPLE_ASSIGN); + const enum gimple_rhs_class ob_class = gimple_assign_rhs_class(stmt); + const bool succeeds = ex_class == ob_class; + gcc_assert(succeeds); +} + +static void +collect_types_from_op(const_tree expr, ptrset_t &types, unsigned n) +{ + gcc_assert(expr); + const_tree op_n = TREE_OPERAND(expr, n); + gcc_assert(op_n); + collect_types_from_expr(op_n, types); +} + +static void +collect_types_from_op0(const_tree expr, ptrset_t &types, const enum tree_code ex_code) +{ + assert_is_type(expr, ex_code); + collect_types_from_op(expr, types, 0); +} + +static void +collect_types_from_op1(const_tree expr, ptrset_t &types, const enum tree_code ex_code) +{ + assert_is_type(expr, ex_code); + collect_types_from_op(expr, types, 0); + collect_types_from_op(expr, types, 1); +} + +static void +collect_types_from_addr_expr(const_tree expr, ptrset_t &types) +{ + collect_types_from_op0(expr, types, ADDR_EXPR); +} + +static void +collect_types_from_component_ref(const_tree expr, ptrset_t &types) +{ + collect_types_from_op1(expr, types, COMPONENT_REF); +} + +static void +collect_types_from_mem_ref(const_tree expr, ptrset_t &types) +{ + collect_types_from_op1(expr, types, MEM_REF); +} + +static void +collect_types_from_array_ref(const_tree expr, ptrset_t &types) +{ + collect_types_from_op1(expr, types, ARRAY_REF); +} + +static void +collect_types_from_leaf_expr(const_tree expr, ptrset_t &types, const enum tree_code ex_code) +{ + assert_is_type(expr, ex_code); + const_tree type = TREE_TYPE(expr); + gcc_assert(type); + collect_types(type, types); +} + +static void +collect_types_from_ssa_name(const_tree expr, ptrset_t &types) +{ + collect_types_from_leaf_expr(expr, types, SSA_NAME); +} + +static void +collect_types_from_var_decl(const_tree expr, ptrset_t &types) +{ + collect_types_from_leaf_expr(expr, types, VAR_DECL); +} + +static void +collect_types_from_field_decl(const_tree expr, ptrset_t &types) +{ + collect_types_from_leaf_expr(expr, types, FIELD_DECL); +} + +static void +collect_types_from_integer_cst(const_tree expr, ptrset_t &types) +{ + collect_types_from_leaf_expr(expr, types, INTEGER_CST); +} + +static void +collect_types_from_constructor_array(const_tree expr, ptrset_t &types) +{ + assert_is_type(expr, CONSTRUCTOR); + const_tree type = TREE_TYPE(expr); + assert_is_type(type, ARRAY_TYPE); + //TODO: Collect types from here +#ifdef FUZZ_MODE + gcc_unreachable(); +#endif +} + +static void +collect_types_from_constructor_record_or_union(const_tree expr, ptrset_t &types) +{ + assert_is_type(expr, CONSTRUCTOR); + const_tree type = TREE_TYPE(expr); + const enum tree_code code = TREE_CODE(type); + const bool is_record = RECORD_TYPE == code; + const bool is_union = UNION_TYPE == code; + const bool is_qual_union = QUAL_UNION_TYPE == code; + const bool is_valid_input = is_record || is_union || is_qual_union; + gcc_assert(is_valid_input); + //TODO: Collect types from here +#ifdef FUZZ_MODE + gcc_unreachable(); +#endif +} + +static void +collect_types_from_constructor(const_tree expr, ptrset_t &types) +{ + // https://gcc.gnu.org/onlinedocs/gccint/Unary-and-Binary-Expressions.html#Unary-and-Binary-Expressions + // These nodes represent the brace-enclosed 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. + // 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 the 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. A single INTEGER_CST indicates which element of + // the array is being assigned to. A RANGE_EXPR indicates an inclusive range + // of elements to initialize. In both cases the VALUE is the + // corresponding initializer. It is re-evaluated for each element of + // a RANGE_EXPR. If the INDEX is NULL_TREE, then the initializer + // is for the next available array element. + assert_is_type(expr, CONSTRUCTOR); + const_tree type = TREE_TYPE(expr); + gcc_assert(type); + const enum tree_code code = TREE_CODE(type); + switch (code) + { + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + case ARRAY_TYPE: + break; + default: + // TODO: I should fuzz this, I got a constructor of an integer type? +#ifdef FUZZ_MODE + gcc_unreachable(); +#endif + return; + break; + } + + const bool is_array = ARRAY_TYPE == code; + is_array ? collect_types_from_constructor_array(expr, types) : collect_types_from_constructor_record_or_union(expr, types); +} + +static void +collect_types_from_parm_decl(const_tree expr, ptrset_t &types) +{ + collect_types_from_leaf_expr(expr, types, PARM_DECL); +} + +static void +collect_types_from_real_cst(const_tree expr, ptrset_t &types) +{ + collect_types_from_leaf_expr(expr, types, REAL_CST); +} + +static void +collect_types_from_string_cst(const_tree expr, ptrset_t &types) +{ + collect_types_from_leaf_expr(expr, types, STRING_CST); +} + +static void +collect_types_from_bit_field_ref(const_tree expr, ptrset_t &types) +{ + // TODO: How to collect types from bit_field_ref? +#ifdef FUZZ_MODE + gcc_unreachable(); +#endif +} + +static void +collect_types_from_view_convert_expr(const_tree expr, ptrset_t &types) +{ + // VIEW_CONVERT_EXPR is used to interpret the bit representation + // of a register as a register of a different type. + // It is unspecified if this is allowed to change the + // register size. If disallowed this case needs to go + // through an integer mode and an intermediate BIT_FIELD_REF. + // https://gcc.gnu.org/wiki/MemRef + //TODO: How to collect types from VIEW_CONVERT_EXPR? +#ifdef FUZZ_MODE + gcc_unreachable(); +#endif +} + +static void +collect_types_from_result_decl(const_tree expr, ptrset_t &types) +{ + collect_types_from_leaf_expr(expr, types, RESULT_DECL); +} + +static void +collect_types_from_expr(const_tree expr, ptrset_t &types) +{ + // These are the codes I saw using csmith to fuzz. + gcc_assert(expr); + const_tree type = TREE_TYPE(expr); + gcc_assert(type); + collect_types(type, types); + const enum tree_code code = TREE_CODE(expr); + switch (code) + { + case ADDR_EXPR: + collect_types_from_addr_expr(expr, types); + break; + case BIT_FIELD_REF: + collect_types_from_bit_field_ref(expr, types); + break; + case ARRAY_REF: + collect_types_from_array_ref(expr, types); + break; + case MEM_REF: + collect_types_from_mem_ref(expr, types); + break; + case COMPONENT_REF: + collect_types_from_component_ref(expr, types); + break; + case SSA_NAME: + collect_types_from_ssa_name(expr, types); + break; + case VAR_DECL: + collect_types_from_var_decl(expr, types); + break; + case FIELD_DECL: + collect_types_from_field_decl(expr, types); + break; + case VIEW_CONVERT_EXPR: + collect_types_from_view_convert_expr(expr, types); + break; + case INTEGER_CST: + collect_types_from_integer_cst(expr, types); + break; + case CONSTRUCTOR: + collect_types_from_constructor(expr, types); + break; + case RESULT_DECL: + collect_types_from_result_decl(expr, types); + break; + case PARM_DECL: + collect_types_from_parm_decl(expr, types); + break; + case REAL_CST: + collect_types_from_real_cst(expr, types); + break; + case STRING_CST: + collect_types_from_string_cst(expr, types); + break; + default: + log("tree_code: %s\n", get_tree_code_name(code)); + gcc_unreachable(); + break; + } +} + +static void +collect_types_from_stmt_assign_lhs(gimple *stmt, ptrset_t &types) +{ + is_gimple_code(stmt, GIMPLE_ASSIGN); + const_tree lhs = gimple_assign_lhs(stmt); + gcc_assert(lhs); + collect_types_from_expr(lhs, types); +} + +static void +collect_types_from_stmt_assign_rhs3(gimple *stmt, ptrset_t &types) +{ + is_gimple_rhs_class(stmt, GIMPLE_TERNARY_RHS); + const_tree rhs = gimple_assign_rhs3(stmt); + gcc_assert(rhs); + collect_types_from_expr(rhs, types); +} + +static void +collect_types_from_stmt_assign_rhs2(gimple *stmt, ptrset_t &types) +{ + is_gimple_code(stmt, GIMPLE_ASSIGN); + const enum gimple_rhs_class gclass = gimple_assign_rhs_class(stmt); + const bool is_ternary = GIMPLE_TERNARY_RHS == gclass; + const bool is_binary = GIMPLE_BINARY_RHS == gclass; + const bool is_valid_input = is_ternary || is_binary; + gcc_assert(is_valid_input); + const_tree rhs = gimple_assign_rhs2(stmt); + gcc_assert(rhs); + collect_types_from_expr(rhs, types); +} + +static void +collect_types_from_stmt_assign_rhs1(gimple *stmt, ptrset_t &types) +{ + is_gimple_code(stmt, GIMPLE_ASSIGN); + const enum gimple_rhs_class gclass = gimple_assign_rhs_class(stmt); + const bool is_ternary = GIMPLE_TERNARY_RHS == gclass; + const bool is_binary = GIMPLE_BINARY_RHS == gclass; + const bool is_unary = GIMPLE_UNARY_RHS == gclass; + const bool is_single = GIMPLE_SINGLE_RHS == gclass; + const bool is_valid_input = is_ternary || is_binary || is_unary || is_single; + gcc_assert(is_valid_input); + const_tree rhs = gimple_assign_rhs1(stmt); + gcc_assert(rhs); + collect_types_from_expr(rhs, types); +} + +static void +collect_types_from_stmt_assign_rhs(gimple *stmt, ptrset_t &types) +{ + is_gimple_code(stmt, GIMPLE_ASSIGN); + const enum gimple_rhs_class gclass = gimple_assign_rhs_class(stmt); + switch (gclass) + { + case GIMPLE_TERNARY_RHS: + collect_types_from_stmt_assign_rhs3(stmt, types); + /* fall-through */ + case GIMPLE_BINARY_RHS: + collect_types_from_stmt_assign_rhs2(stmt, types); + /* fall-through */ + case GIMPLE_UNARY_RHS: + case GIMPLE_SINGLE_RHS: + collect_types_from_stmt_assign_rhs1(stmt, types); + break; + default: + gcc_unreachable(); + break; + } +} + +static void +collect_types_from_stmt_assign(gimple *stmt, ptrset_t &types) +{ + is_gimple_code(stmt, GIMPLE_ASSIGN); + collect_types_from_stmt_assign_lhs(stmt, types); + collect_types_from_stmt_assign_rhs(stmt, types); +} + +static void +collect_types_from_stmt_call_lhs(gimple *stmt, ptrset_t &types) +{ + is_gimple_code(stmt, GIMPLE_CALL); + const_tree lhs = gimple_call_lhs(stmt); + if (!lhs) return; + collect_types_from_expr(lhs, types); +} + +static void +collect_types_from_stmt_call_rhs(gimple *stmt, ptrset_t &types) +{ + is_gimple_code(stmt, GIMPLE_CALL); + unsigned num_args = gimple_call_num_args(stmt); + for (unsigned i = 0; i < num_args; i++) + { + const_tree arg_i = gimple_call_arg(stmt, i); + collect_types_from_expr(arg_i, types); + } +} + +static void +collect_types_from_stmt_call(gimple *stmt, ptrset_t &types) +{ + is_gimple_code(stmt, GIMPLE_CALL); + collect_types_from_stmt_call_lhs(stmt, types); + collect_types_from_stmt_call_rhs(stmt, types); +} + +static void +collect_types_from_stmt_cond_lhs(gimple *stmt, ptrset_t &types) +{ + is_gimple_code(stmt, GIMPLE_COND); + const_tree lhs = gimple_cond_lhs(stmt); + gcc_assert(lhs); + collect_types_from_expr(lhs, types); +} + +static void +collect_types_from_stmt_cond_rhs(gimple *stmt, ptrset_t &types) +{ + is_gimple_code(stmt, GIMPLE_COND); + const_tree rhs = gimple_cond_rhs(stmt); + gcc_assert(rhs); + collect_types_from_expr(rhs, types); +} + +static void +collect_types_from_stmt_cond(gimple *stmt, ptrset_t &types) +{ + is_gimple_code(stmt, GIMPLE_COND); + collect_types_from_stmt_cond_lhs(stmt, types); + collect_types_from_stmt_cond_rhs(stmt, types); +} + +static void +collect_types_from_stmt_return(gimple *stmt, ptrset_t &types) +{ + is_gimple_code(stmt, GIMPLE_RETURN); + const_tree retval = gimple_return_retval(stmt); + gcc_assert(retval); + collect_types_from_expr(retval, types); +} + +static void +collect_types_from_stmt(gimple *stmt, ptrset_t &types) +{ + gcc_assert(stmt); + const enum gimple_code code = gimple_code(stmt); + switch (code) { + case GIMPLE_ASSIGN: + collect_types_from_stmt_assign(stmt, types); + break; + case GIMPLE_CALL: + collect_types_from_stmt_call(stmt, types); + break; + case GIMPLE_COND: + collect_types_from_stmt_cond(stmt, types); + break; + case GIMPLE_RETURN: + collect_types_from_stmt_return(stmt, types); + break; + case GIMPLE_LABEL: + case GIMPLE_PREDICT: +#ifdef FUZZ_MODE + gcc_unreachable(); +#endif + break; + default: + { + const char* name = gimple_code_name[code]; + log("gimple code name %s\n", name); + gcc_unreachable(); + } + break; + } +} + +static void +collect_types_from_cnode_decl(cgraph_node *cnode, ptrset_t &types) +{ + gcc_assert(cnode); + const_tree decl = cnode->decl; + gcc_assert(decl); + const_tree decl_type = TREE_TYPE(decl); + gcc_assert(decl_type); + function *func = DECL_STRUCT_FUNCTION (decl); + gcc_assert(func); + push_cfun(func); + // This will collect return, arguments and decl_type itself + collect_types(decl_type, types); + pop_cfun(); +} + +static void +collect_types_from_cnode_locals(cgraph_node *cnode, ptrset_t &types) +{ + gcc_assert(cnode); + const_tree decl = cnode->decl; + gcc_assert(decl); + function *func = DECL_STRUCT_FUNCTION (decl); + gcc_assert(func); + int i = 0; + tree var_decl = NULL; + FOR_EACH_LOCAL_DECL(func, i, var_decl) + { + gcc_assert(var_decl); + const_tree var_decl_type = TREE_TYPE(var_decl); + collect_types(var_decl_type, types); + } +} + +static void +collect_types_from_cnode_ssa_names(cgraph_node *cnode, ptrset_t &types) +{ + gcc_assert(cnode); + const_tree decl = cnode->decl; + gcc_assert(decl); + function *func = DECL_STRUCT_FUNCTION (decl); + gcc_assert(func); + size_t i = 0; + tree ssa_name = NULL; + push_cfun(func); + FOR_EACH_SSA_NAME(i, ssa_name, cfun) + { + gcc_assert(ssa_name); + const_tree ssa_name_type = TREE_TYPE(ssa_name); + collect_types(ssa_name_type, types); + } + pop_cfun(); +} + +static void +collect_types_from_bb(basic_block bb, ptrset_t &types) +{ + gcc_assert(bb); + for (auto gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) + { + gimple *stmt = gsi_stmt(gsi); + gcc_assert(stmt); + collect_types_from_stmt(stmt, types); + } +} + +static void +collect_types_from_cnode_bb(cgraph_node *cnode, ptrset_t &types) +{ + gcc_assert(cnode); + cnode->get_untransformed_body(); + tree decl = cnode->decl; + gcc_assert(decl); + function *func = DECL_STRUCT_FUNCTION(decl); + gcc_assert(func); + basic_block bb = NULL; + push_cfun(func); + FOR_EACH_BB_FN(bb, func) + { + gcc_assert(bb); + collect_types_from_bb(bb, types); + } + pop_cfun(); +} + +static void +collect_types_from_global(varpool_node *vnode, ptrset_t &types) +{ + gcc_assert(vnode); + struct ipa_ref *ref = NULL; + for (unsigned i = 0; vnode->iterate_referring(i, ref); i++) + { + tree decl = vnode->decl; + gcc_assert(decl); + collect_types_from_var_decl(decl, types); + } +} + +static void +collect_types_from_globals(ptrset_t &types) +{ + varpool_node *vnode = NULL; + FOR_EACH_VARIABLE (vnode) + { + collect_types_from_global(vnode, types); + } +} + +static void +collect_types_from_functions_with_gimple_body(cgraph_node *cnode, ptrset_t &types) +{ + gcc_assert(cnode); + collect_types_from_cnode_decl(cnode, types); + collect_types_from_cnode_locals(cnode, types); + collect_types_from_cnode_ssa_names(cnode, types); + collect_types_from_cnode_bb(cnode, types); +} + +static void +print_types_in_set(ptrset_t &types) +{ + for (auto it = types.points_to_record.cbegin(); it != types.points_to_record.cend(); ++it) + { + const_tree type = *it; + std::string name = type_to_string(type); + log("name: %s\n", name.c_str()); + } +} + +static const bool +filter_comparisons_functions_char(const char* function_name) +{ + // Everything should run + if (!flag_tp_comparison_functions) return true; + + const size_t buffer_size = 1024; + const size_t cli_length = strlen(flag_tp_comparison_functions); + gcc_assert(buffer_size > cli_length); + char whitelist[buffer_size]; + strcpy(whitelist, flag_tp_comparison_functions); + + + char* saveptr = whitelist; + char* token = NULL; + while (token = strtok_r(saveptr, ",", &saveptr)) + { + const bool name_allowed = strcmp(token, function_name) == 0; + if (name_allowed) return true; + } + + return false; +} + +static const bool +filter_comparisons_functions_int(unsigned function_id) +{ + if (!flag_tp_comparison_functions) return true; + + const char* function_name = type_playground::names[function_id]; + return filter_comparisons_functions_char(function_name); +} + +static const bool +filter_comparisons_functions(type_playground::type_comparison_func_t func) +{ + if (!flag_tp_comparison_functions) return true; + + for (unsigned i = 0; i < type_playground::type_comparisons; i++) + { + if (func != type_playground::comparisons[i]) continue; + + return filter_comparisons_functions_int(i); + } + + return false; +} + + +static void +compare_types_in_set(ptrset_t &types) +{ + for (auto i = types.points_to_record.cbegin(); i != types.points_to_record.cend(); ++i) + { + for (auto j = types.points_to_record.cbegin(); j != types.points_to_record.cend(); ++j) + { + const_tree a = *i; + const_tree b = *j; + log("%s x %s = ", type_to_string(a).c_str(), type_to_string(b).c_str()); + for (unsigned k = 0; k < type_playground::type_comparisons; k++) + { + type_playground::type_comparison_func_t comparison = type_playground::comparisons[k]; + const bool allowed_to_run = filter_comparisons_functions(comparison); + if (!allowed_to_run) continue; + + const bool result = comparison(a, b); + log("%s, ", result ? "t" : "f"); + } + log("\n"); + } + } +} + +static void +filter_out_types_in_set(ptrset_t &types) +{ + // compare everything + if (!flag_tp_types_compared) return; + + const size_t buffer_size = 1024; + const size_t cli_length = strlen(flag_tp_comparison_functions); + gcc_assert(buffer_size > cli_length); + char whitelist[buffer_size]; + strcpy(whitelist, flag_tp_types_compared); + + bool name_allowed = false; + for (auto it = types.points_to_record.cbegin(); it != types.points_to_record.cend(); name_allowed ? ++it : it = types.points_to_record.erase(it)) + { + const_tree type = *it; + std::string observed_name = get_type_identifier(type); + char* saveptr = whitelist; + char* expected_name = NULL; + name_allowed = false; + + while (expected_name = strtok_r(saveptr, ",", &saveptr)) + { + name_allowed |= strcmp(expected_name, observed_name.c_str()) == 0; + } + } + +} + +static void +collect_types(ptrset_t &types) +{ + collect_types_from_globals(types); + + cgraph_node *node = NULL; + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY(node) + { + node->get_untransformed_body(); + collect_types_from_functions_with_gimple_body(node, types); + // We still need to collect types from + // the function signatures of functions without gimple bodies... + } +} + +static unsigned int +iphw_execute() +{ + //test_type_equality::run_tests(); + //test_naming_types::run_tests(); + ptrset_t types; + + collect_types(types); + filter_out_types_in_set(types); + compare_types_in_set(types); + return 0; +} + +namespace { +const pass_data pass_data_ipa_escape_analysis = +{ + SIMPLE_IPA_PASS, + "escape-analysis", + OPTGROUP_NONE, + TV_NONE, + (PROP_cfg | PROP_ssa), + 0, + 0, + TODO_rebuild_alias, + 0, +}; + +class pass_ipa_escape_analysis : public simple_ipa_opt_pass +{ +public: + pass_ipa_escape_analysis (gcc::context *ctx) + : simple_ipa_opt_pass(pass_data_ipa_escape_analysis, ctx) + {} + + virtual bool gate(function*) { return flag_ipa_escape_analysis; } + virtual unsigned execute (function*) { return iphw_execute(); } +}; +} // anon namespace + +simple_ipa_opt_pass* +make_pass_ipa_escape_analysis (gcc::context *ctx) +{ + return new pass_ipa_escape_analysis (ctx); +} diff --git a/gcc/name-types.c b/gcc/name-types.c new file mode 100644 index 00000000000..800ba6cb1d2 --- /dev/null +++ b/gcc/name-types.c @@ -0,0 +1,420 @@ +#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-ssa-alias.h" +#include "tree-ssanames.h" +#include "gimple.h" +#include "cfg.h" +#include "gimple-iterator.h" +#include "gimple-ssa.h" + +#include "types-inlines.h" +#include <set> +#include <string> + +#include "name-types.h" + +// Using std::string because it will manage memory for us +// And we don't know whether the user will free the memory. +// Also that's why we are returning a value rather than a +// pointer... + +static const std::string +type_to_string_simple(const_tree type) +{ + gcc_assert(type); + const enum tree_code code = TREE_CODE(type); + bool valid_input = false; + switch (code) + { + case VOID_TYPE: + case INTEGER_TYPE: + case REAL_TYPE: + case FIXED_POINT_TYPE: + case COMPLEX_TYPE: + case ENUMERAL_TYPE: + case BOOLEAN_TYPE: + case OFFSET_TYPE: + valid_input = true; + break; + default: + valid_input = false; + break; + } + gcc_assert(valid_input); + + + return std::string(get_tree_code_name(code)); +} + +static const std::string +type_to_string_get_field_name(const_tree field) +{ + assert_is_type(field, FIELD_DECL); + const_tree decl_name = DECL_NAME(field); + if (!decl_name) return std::string(""); + + const char* identifier = IDENTIFIER_POINTER(decl_name); + return std::string(identifier); +} + +static const unsigned +type_to_string_get_field_offset(const_tree field) +{ + assert_is_type(field, FIELD_DECL); + tree cst = byte_position(field); + gcc_assert(cst); + unsigned offset = tree_to_uhwi (cst); + return offset; +} + +static const unsigned +type_to_string_get_type_size(const_tree type) +{ + gcc_assert(type); + const_tree type_size = TYPE_SIZE(type); + const bool is_incomplete = NULL_TREE == type_size; + if (is_incomplete) return 0; + + const bool fits_uhwi = tree_fits_uhwi_p(type_size); + if (fits_uhwi) return tree_to_uhwi (type_size); + + const bool fits_shwi = tree_fits_shwi_p(type_size); + if (fits_shwi) return tree_to_shwi (type_size); + + return 0; +} + +const std::string +get_type_identifier(const_tree type) +{ + tree name = TYPE_NAME(type); + // 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. + const bool no_name = NULL_TREE == name; + if (no_name) return std::string(""); + + const enum tree_code name_code = TREE_CODE(name); + const bool is_name_type_decl = TYPE_DECL == name_code; + name = is_name_type_decl ? DECL_NAME(name) : name; + const char* identifier_ptr = IDENTIFIER_POINTER(name); + gcc_assert(identifier_ptr); + return std::string(identifier_ptr); +} + +static const std::string +type_to_string_get_record_name(const_tree type) +{ + assert_is_type(type, RECORD_TYPE); + tree name = TYPE_NAME(type); + // 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. + const bool no_name = NULL_TREE == name; + if (no_name) return std::string(""); + + const enum tree_code name_code = TREE_CODE(name); + const bool is_name_type_decl = TYPE_DECL == name_code; + name = is_name_type_decl ? DECL_NAME(name) : name; + const char* identifier_ptr = IDENTIFIER_POINTER(name); + gcc_assert(identifier_ptr); + return std::string(identifier_ptr); +} + +static const std::string +type_to_string_get_record_or_union_name(const_tree type) +{ + const enum tree_code code = TREE_CODE(type); + const bool is_record = RECORD_TYPE == code; + const bool is_union = UNION_TYPE == code; + const bool is_valid_input = is_record || is_union; + gcc_assert(is_valid_input); + tree name = TYPE_NAME(type); + + // 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. + const bool no_name = NULL_TREE == name; + if (no_name) return std::string(""); + + const enum tree_code name_code = TREE_CODE(name); + const bool is_name_type_decl = TYPE_DECL == name_code; + name = is_name_type_decl ? DECL_NAME(name) : name; + const char* identifier_ptr = IDENTIFIER_POINTER(name); + gcc_assert(identifier_ptr); + return std::string(identifier_ptr); +} + +/* + * The idea behind this function is to receive either + * a record or a union type and return a string that + * represents this type. + * The output should look something like this: + * 0: astruct { + * 0: int a + * 8: int b + * 16: bstruct* + * 24: cstruct { + * 0: int a + * } + * 32: { + * } + * } + * The question I have now... is... should we follos bstruct* + * to see what it points to? Because, maybe there's two bstruct*s + * defined in different TU? + * So maybe more like this: + * 0: astruct { + * 0: int a + * 8: int b + * 16: bstruct { + * 0: int a + * }* + * 24: cstruct { + * 0: int a + * } + * 32: { + * } + */ +static const std::string +type_to_string_record_or_union(const_tree type) +{ + gcc_assert(type); + const enum tree_code code = TREE_CODE(type); + const bool is_record = RECORD_TYPE == code; + const bool is_union = UNION_TYPE == code; + const bool is_valid_input = is_record || is_union; + gcc_assert(is_valid_input); + + std::string name = type_to_string_get_record_or_union_name(type); + std::string aggregate = name + std::string(" {"); + + for (tree field = TYPE_FIELDS(type); field; field = DECL_CHAIN(field)) + { + std::string field_name = type_to_string_get_field_name(field); + const_tree field_type = TREE_TYPE(field); + const unsigned field_offset = type_to_string_get_field_offset(field); + std::string field_type_name = type_to_string(field_type, field_offset); + aggregate += field_type_name + std::string(";"); + } + + aggregate += "}"; + return aggregate; +} + +static const std::string +type_to_string_record(const_tree type) +{ + assert_is_type(type, RECORD_TYPE); + return type_to_string_record_or_union(type); +} + +static const std::string +type_to_string_union(const_tree type) +{ + assert_is_type(type, UNION_TYPE); + return type_to_string_record_or_union(type); +} + +static const std::string +type_to_string_wrapper(const_tree type) +{ + gcc_assert(type); + const enum tree_code code = TREE_CODE(type); + bool is_valid_input = false; + switch (code) + { + case POINTER_TYPE: + case ARRAY_TYPE: + case REFERENCE_TYPE: + is_valid_input = true; + break; + default: + is_valid_input = false; + break; + } + gcc_assert(is_valid_input); + + const_tree inner_type = TREE_TYPE(type); + return type_to_string(inner_type, 0, false); +} + +static const std::string +type_to_string_pointer(const_tree type) +{ + assert_is_type(type, POINTER_TYPE); + return type_to_string_wrapper(type) + std::string("*"); +} + +static const std::string +type_to_string_array(const_tree type) +{ + assert_is_type(type, ARRAY_TYPE); + return type_to_string_wrapper(type) + std::string("[]"); +} + +static const std::string +type_to_string_reference(const_tree type) +{ + assert_is_type(type, REFERENCE_TYPE); + return type_to_string_wrapper(type) + std::string("&"); +} + +static const std::string +type_to_string_function_or_method(const_tree type) +{ + gcc_assert(type); + const enum tree_code code = TREE_CODE(type); + const bool is_function = FUNCTION_TYPE == code; + const bool is_method = METHOD_TYPE == code; + const bool is_valid = is_function || is_method; + gcc_assert(is_valid); + + const_tree return_type = TREE_TYPE(type); + gcc_assert(return_type); + + const std::string return_type_name = type_to_string(return_type, 0, false); + + std::string aggregate(""); + + for(tree param = TYPE_ARG_TYPES(type); param; param = TREE_CHAIN(param)) + { + const_tree param_type = TREE_VALUE(param); + //TODO: Why no param type? + //gcc_assert(param_type); + const bool has_more_params = TREE_CHAIN(param); + aggregate += type_to_string(param_type); + if (!has_more_params) break; + + aggregate += std::string(", "); + } + + //TODO: Add base_type for method types + std::string retval = return_type_name + std::string("(") + aggregate + std::string(")"); + return retval; +} + +static const std::string +type_to_string_function(const_tree type) +{ + assert_is_type(type, FUNCTION_TYPE); + return type_to_string_function_or_method(type); +} + +static const std::string +type_to_string_method(const_tree type) +{ + assert_is_type(type, METHOD_TYPE); + return type_to_string_function_or_method(type); +} + + +const std::string +type_to_string(const_tree type, const unsigned field_offset, const bool add_prelude) +{ + if (!type) return std::string("why"); + gcc_assert(type); + const enum tree_code code = TREE_CODE(type); + const unsigned size = type_to_string_get_type_size(type); + std::string prelude = add_prelude ? std::to_string(field_offset) + std::string(",") + std::to_string(field_offset + size) + std::string(":") : std::string(""); + + switch (code) + { + case VOID_TYPE: + case INTEGER_TYPE: + case REAL_TYPE: + case FIXED_POINT_TYPE: + case COMPLEX_TYPE: + case ENUMERAL_TYPE: + case BOOLEAN_TYPE: + case OFFSET_TYPE: + return prelude + type_to_string_simple(type); + break; + default: + break; + } + + switch (code) + { + case RECORD_TYPE: + return prelude + type_to_string_record(type); + break; + case UNION_TYPE: + return prelude + type_to_string_union(type); + break; + case POINTER_TYPE: + return prelude + type_to_string_pointer(type); + break; + case REFERENCE_TYPE: + return prelude + type_to_string_reference(type); + break; + case ARRAY_TYPE: + return prelude + type_to_string_array(type); + break; + case FUNCTION_TYPE: + return prelude + type_to_string_function(type); + break; + case METHOD_TYPE: + return prelude + type_to_string_method(type); + break; + case QUAL_UNION_TYPE: + case LANG_TYPE: + default: + // unimplemented + gcc_unreachable(); + break; + } + + gcc_unreachable(); + return std::string("you shouldn't ever see this"); +} + +namespace test_naming_types { + +void test_generic_to_string(const enum tree_code code, const char * expected_char_array) +{ + std::string expected_value(expected_char_array); + tree type = make_node(code); + std::string observed_value = type_to_string(type); + const bool success = expected_value.compare(observed_value) == 0; + gcc_assert(success); + +} +void test_simple_to_string() +{ + test_generic_to_string(VOID_TYPE, "0:void_type"); + test_generic_to_string(INTEGER_TYPE, "0:integer_type"); + test_generic_to_string(REAL_TYPE, "0:real_type"); + test_generic_to_string(FIXED_POINT_TYPE, "0:fixed_point_type"); + test_generic_to_string(COMPLEX_TYPE, "0:complex_type"); + test_generic_to_string(ENUMERAL_TYPE, "0:enumeral_type"); + test_generic_to_string(BOOLEAN_TYPE, "0:boolean_type"); + test_generic_to_string(OFFSET_TYPE, "0:offset_type"); +} + +void run_tests() +{ + test_simple_to_string(); +} +}; diff --git a/gcc/name-types.h b/gcc/name-types.h new file mode 100644 index 00000000000..538956fdd5d --- /dev/null +++ b/gcc/name-types.h @@ -0,0 +1,11 @@ +#pragma once + +#include "tree.h" +#include <string> + +const std::string type_to_string(const_tree type, const unsigned field_offset=0, const bool add_prelude=true); +const std::string get_type_identifier(const_tree type); + +namespace test_naming_types { +void run_tests(); +}; diff --git a/gcc/passes.def b/gcc/passes.def index 8a54178a7e0..e0d2711c432 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -151,6 +151,7 @@ along with GCC; see the file COPYING3. If not see 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); NEXT_PASS (pass_ipa_cdtor_merge); diff --git a/gcc/testsuite/gcc.dg/ipa/type-playground-00.c b/gcc/testsuite/gcc.dg/ipa/type-playground-00.c new file mode 100644 index 00000000000..6bddfac9939 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/type-playground-00.c @@ -0,0 +1,21 @@ +/* { dg-do link } */ +/* { dg-options "-flto -fipa-escape-analysis -fdump-ipa-escape-analysis -ftp-comparison-functions=EQ_POINTER -ftp-types-compared=int " } */ + +int +main () +{ + int a = 0; + const int b = 0; + return 0; +} + +// This is proof that comparing trees using only +// pointer equality yields different trees +// when types have different attributes. +// i.e. +// TREE_TYPE(a) == TREE_TYPE(b) +// evaluates to false + + +/* { dg-final { scan-wpa-ipa-dump "0,32:integer_type x 0,32:integer_type = t," "escape-analysis" } } */ + diff --git a/gcc/testsuite/gcc.dg/ipa/type-playground-01.c b/gcc/testsuite/gcc.dg/ipa/type-playground-01.c new file mode 100644 index 00000000000..f85ecc13ef1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/type-playground-01.c @@ -0,0 +1,19 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-escape-analysis -fdump-ipa-escape-analysis -ftp-comparison-functions=EQ_STRUCTURAL -ftp-types-compared=astruct_s" } */ + +struct astruct_s { + struct astruct_s* a; +}; + +int +main(int argc, char* argv) +{ + struct astruct_s a; +} + +// This is proof that an incomplete type cannot be compared +// via structural equality. + +// 0,64:astruct_s {0,64:astruct_s {}*;} x 0,0:astruct_s {} = f, +// I am not an expert on regex +/* { dg-final { scan-wpa-ipa-dump "0,64:astruct_s .0,64:astruct_s ..... x 0,0:astruct_s .. = f," "escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/type-playground-02.c b/gcc/testsuite/gcc.dg/ipa/type-playground-02.c new file mode 100644 index 00000000000..128dc8f3b82 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/type-playground-02.c @@ -0,0 +1,19 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-escape-analysis -fdump-ipa-escape-analysis -ftp-comparison-functions=EQ_MAIN_VARIANT -ftp-types-compared=astruct_s" } */ + +struct astruct_s { + struct astruct_s* a; +}; + +int +main(int argc, char* argv) +{ + struct astruct_s a; +} + +// This is proof that an incomplete type cannot be compared +// via main variant equality. + +// 0,64:astruct_s {0,64:astruct_s {}*;} x 0,0:astruct_s {} = f, +// I am not an expert on regex +/* { dg-final { scan-wpa-ipa-dump "0,64:astruct_s .0,64:astruct_s ..... x 0,0:astruct_s .. = f," "escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/type-playground-03.c b/gcc/testsuite/gcc.dg/ipa/type-playground-03.c new file mode 100644 index 00000000000..4ffe949f826 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/type-playground-03.c @@ -0,0 +1,25 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-escape-analysis -fdump-ipa-escape-analysis -ftp-comparison-functions=EQ_IDENTIFIER -ftp-types-compared=astruct_s" } */ + +struct astruct_s { + struct astruct_s* a; +}; + +int +main(int argc, char* argv) +{ + struct astruct_s a; +} + +// This is proof that an incomplete type **CAN** be compared +// via identifier identity. +// Intuitevely, identifier identity may give false positives (and maybe false negatives too?) +// So, it must be the last resort comparisons. +// +// If we say that an identifier comparison may give false positives and false negatives +// then maybe it would be useful to documents attempts to explicitly produce false positives +// and false negatives to better understand what causes them. + +// 0,64:astruct_s {0,64:astruct_s {}*;} x 0,0:astruct_s {} = t, +// I am not an expert on regex +/* { dg-final { scan-wpa-ipa-dump "0,64:astruct_s .0,64:astruct_s ..... x 0,0:astruct_s .. = t," "escape-analysis" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/type-playground-04.c b/gcc/testsuite/gcc.dg/ipa/type-playground-04.c new file mode 100644 index 00000000000..7b490f6eca3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/type-playground-04.c @@ -0,0 +1,25 @@ +/* { dg-do run } */ +/* { dg-options "-flto -fipa-escape-analysis -fdump-ipa-escape-analysis -ftp-comparison-functions=EQ_CANONICAL -ftp-types-compared=astruct_s" } */ + +struct astruct_s { + struct astruct_s* a; +}; + +int +main(int argc, char* argv) +{ + struct astruct_s a; +} + +// This is proof that an incomplete type **CAN** be compared +// via identifier identity. +// Intuitevely, identifier identity may give false positives (and maybe false negatives too?) +// So, it must be the last resort comparisons. +// +// If we say that an identifier comparison may give false positives and false negatives +// then maybe it would be useful to documents attempts to explicitly produce false positives +// and false negatives to better understand what causes them. + +// 0,64:astruct_s {0,64:astruct_s {}*;} x 0,0:astruct_s {} = f, +// I am not an expert on regex +/* { dg-final { scan-wpa-ipa-dump "0,64:astruct_s .0,64:astruct_s ..... x 0,0:astruct_s .. = f," "escape-analysis" } } */ diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 0c14a827a2e..a9a9c96a4dc 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -503,6 +503,7 @@ 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); extern ipa_opt_pass_d *make_pass_ipa_icf (gcc::context *ctxt); diff --git a/gcc/types-inlines.h b/gcc/types-inlines.h new file mode 100644 index 00000000000..340108fcf8f --- /dev/null +++ b/gcc/types-inlines.h @@ -0,0 +1,37 @@ +#pragma once + + +inline void +log(const char* const fmt, ...) +{ + if (!dump_file) return; + + va_list args; + va_start(args, fmt); + vfprintf(dump_file, fmt, args); + va_end(args); +} + +inline void +assert_is_complete(const_tree a) +{ + gcc_assert(a); + const_tree type_size = TYPE_SIZE(a); + gcc_assert(NULL_TREE != type_size); +} + +inline void +assert_is_type(const_tree a, const enum tree_code expected_code) +{ + gcc_assert(a); + const enum tree_code observed_code = TREE_CODE(a); + const bool eq_codes = observed_code == expected_code; + gcc_assert(eq_codes); +} + +inline void +assert_is_complete_type(const_tree a, const enum tree_code expected_code) +{ + //assert_is_complete(a); + assert_is_type(a, expected_code); +} |