diff options
author | Erick Ochoa <erick.ochoa@theobroma-systems.com> | 2020-10-08 08:59:42 +0200 |
---|---|---|
committer | Erick Ochoa <erick.ochoa@theobroma-systems.com> | 2020-10-08 08:59:42 +0200 |
commit | 0eb4d9cc6a786c2597cdca84c7057ed0650bfdc1 (patch) | |
tree | cb26f0afee739b4065766fbfa681742bce2d7f6a | |
parent | e1336703f8220dcffdeddb5e19dd032c766fbb8f (diff) |
Updating everything
65 files changed, 14667 insertions, 6 deletions
diff --git a/build.sh b/build.sh new file mode 100755 index 00000000000..97158b0cf59 --- /dev/null +++ b/build.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +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 + +make -j `nproc` +make install -j `nproc` +make check-gcc RUNTESTFLAGS="ipa.exp" +popd diff --git a/gary/error-summary b/gary/error-summary new file mode 100644 index 00000000000..f51eae94ad9 --- /dev/null +++ b/gary/error-summary @@ -0,0 +1,31 @@ +There are two errors know to date and one porblem. + +Errors: + +1) Using typedef to create differents names for the same type will +confuse the optimization into thinking that they are different +types. + +This is a know big issue and not a surprise. + +2) Some local declarations go missing for not apparient reason. +To verify this is the case in a suspicious situation uncoment +the DEBUG statemest annotated with "test_1_8" and you'll see in the +stderr output that there are no declarations associate with the +function of interest. + +This was a surprise and will require a lot of debugging to track +dowm the root case of the problem. If you wish to see my test case +I will send it to you. + +Problem: + +If you invoke any of the structure reorg optimizations +(e.g. -fipa-structure-reorg), you'll need to include the options +"-flto -flto-partition=one". The problem is that struction reorg +will unfortunenately run if only "-flto" is included with a +struct reorg option but it will be insane and not function correctly. + +I've not found a way to address this yet but I'm hopeful that this +is someing we can cope with. + diff --git a/gary/how_to_organize_reorg b/gary/how_to_organize_reorg new file mode 100644 index 00000000000..c8116eac15b --- /dev/null +++ b/gary/how_to_organize_reorg @@ -0,0 +1,40 @@ +I'll put shared types and externs in ipa-structure-reorg.h. +The externs in file ipa-structure-reorg.h will be for the API +calls. + +There will three flags associated with + +-fipa-structure-reorg +-fipa-dead-field-eliminate +-fipa-field-reorder +-fipa-instance-interleave + +Specifying any of them will run the shared qualification analysis. + +Specifying -fipa-structure-reorg will be equivalent +to invoking all the other flags. + +The other flags just run the specific structure reorg algorithm. + +The driver and shared qualification code will be in ipa-structure-reorg.c. +I'm assuming for now that each algorithm will be in it's own file +and can be invoked via from the driver like this + + if ( control code ) + { + int return_val = algorithm( info); + // typical actions dependent on the return value; + } + +Note, the info parameter (of type Info *) is how I'd like to give the +API calls access to the persistent information. Frankly, this is my +own strange idiom but it's served me well in the past and I think it +beats globals which are something I want to avoid. + +Would it be OK for our first integration of code that I create one or +two empty files. You'd supply a name that you are using now of course +so you can just subsitute your work for the stubs on itegration. + +Also, reagarding the API itself wouldn't a reasonable start just +be some of the functions in the utility section of my design? +Check them out see if that makes any sense.
\ No newline at end of file diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 79e854aa938..5fda9aacdcd 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1408,6 +1408,30 @@ OBJS = \ incpath.o \ init-regs.o \ internal-fn.o \ + ipa-prototype.o \ + ipa-type-escape-analysis.o \ + type-walker.o \ + type-reconstructor.o \ + expr-walker.o \ + gimple-walker.o \ + gimple-accesser.o \ + expr-accessor.o \ + type-accessor.o \ + type-collector.o \ + expr-collector.o \ + gimple-collector.o \ + gimple-caster.o \ + gimple-rewriter.o \ + expr-rewriter.o \ + type-escaper.o \ + type-structural-equality.o \ + type-structural-main-variant.o \ + type-canonical-equality.o \ + type-incomplete-equality.o \ + expr-escaper.o \ + gimple-escaper.o \ + type-stringifier.o \ + collect-types.o \ ipa-cp.o \ ipa-sra.o \ ipa-devirt.o \ @@ -1428,6 +1452,11 @@ OBJS = \ ipa-icf-gimple.o \ ipa-reference.o \ ipa-ref.o \ + ipa-structure-reorg.o \ + ipa-str-reorg-dead-field-eliminate.o \ + ipa-str-reorg-field-reorder.o \ + ipa-str-reorg-instance-interleave.o \ + ipa-str-reorg-utils.o \ ipa-utils.o \ ipa.o \ ira.o \ diff --git a/gcc/collect-types.c b/gcc/collect-types.c new file mode 100644 index 00000000000..f4ad113b56f --- /dev/null +++ b/gcc/collect-types.c @@ -0,0 +1,86 @@ +#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 "type-stringifier.hpp" +#include <set> + +#include "collect-types.h" + + +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); + const bool in_points_to_set = this->in_points_to_record(type); + const bool in_complement = this->in_complement(type); + const bool _xor = in_points_to_set != in_complement; + // sanity check... + gcc_assert(_xor); +} + +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 +points_to_record_sets_s::print_in_points_to_record() const +{ + TypeStringifier stringifier; + for (auto i = this->points_to_record.cbegin(), e = this->points_to_record.cend(); i != e; ++i) + { + const_tree t = *i; + gcc_assert(t); + std::string name = stringifier.stringify(t); + log("collected: %s\n", name.c_str()); + } +} diff --git a/gcc/collect-types.h b/gcc/collect-types.h new file mode 100644 index 00000000000..a2439557332 --- /dev/null +++ b/gcc/collect-types.h @@ -0,0 +1,21 @@ +#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; + typeset escaping; + typeset non_escaping; + 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); + void print_in_points_to_record() const; +}; + +typedef struct points_to_record_sets_s ptrset_t; diff --git a/gcc/common.opt b/gcc/common.opt index dd68c61ae1d..32fcce9593a 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1869,6 +1869,22 @@ fipa-struct-reorg Common Ignore Does nothing. Preserved for backward compatibility. +fipa-structure-reorg +Common Report Var(flag_ipa_structure_reorg) Init(0) Optimization +Structure reorganization optimization. + +fipa-dead-field-eliminate +Common Report Var(flag_ipa_dead_field_eliminate) Init(0) Optimization +Structure reorganization optimization, dead field elimination. + +fipa-field-reorder +Common Report Var(flag_ipa_field_reorder) Init(0) Optimization +Structure reorganization optimization, field reordering. + +fipa-instance-interleave +Common Report Var(flag_ipa_instance_interleave) Init(0) Optimization +Structure reorganization optimization, instance interleaving. + fipa-vrp Common Report Var(flag_ipa_vrp) Optimization Perform IPA Value Range Propagation. @@ -3448,4 +3464,38 @@ fipa-ra Common Report Var(flag_ipa_ra) Optimization Use caller save register across calls if possible. +fipa-typelist-struct= +Common Joined Report Var(flag_ipa_typelist_struct) Init(0) +-fipa-typelist-struct=<string> Name of struct of interest + +fipa-typelist-field= +Common Joined Report Var(flag_ipa_typelist_field) Init(0) +-fipa-typelist-field=<string> Name of struct of interest + +ftp-types-compared= +Common Joined Report Var(flag_tp_types_compared) Init(0) + +ftp-comparison-functions= +Common Joined Report Var(flag_tp_comparison_functions) Init(0) + +fipa-prototype +Common Report Var(flag_ipa_prototype) Optimization +TBD + +fipa-type-escape-analysis +Common Report Var(flag_ipa_type_escape_analysis) Optimization +This flag is only used for debugging the type escape analysis + +fprint-escape-analysis +Common Report Var(flag_print_escape_analysis) Optimization +This flag is used to print the escape analysis proper. It does not include type casting. + +fprint-cast-analysis +Common Report Var(flag_print_cast_analysis) Optimization +This flag is used to print the escape analysis including type casting. + +fprint-access-analysis +Common Report Var(flag_print_access_analysis) Optimization +This flag is used to print the access analysis (if field is read or written to). + ; This comment is to ensure we retain the blank line above. diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index bca8c856dc8..5bcf90d3140 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -475,6 +475,8 @@ Objective-C and Objective-C++ Dialects}. -fdelete-null-pointer-checks -fdevirtualize -fdevirtualize-speculatively @gol -fdevirtualize-at-ltrans -fdse @gol -fearly-inlining -fipa-sra -fexpensive-optimizations -ffat-lto-objects @gol +-fipa-structure-reorg -fipa-dead-field-eliminate @gol +-fipa-field-reorder -fipa-instance-interleave @gol -ffast-math -ffinite-math-only -ffloat-store -fexcess-precision=@var{style} @gol -ffinite-loops @gol -fforward-propagate -ffp-contract=@var{style} -ffunction-sections @gol @@ -9834,6 +9836,29 @@ by parameters passed by value. Enabled at levels @option{-O2}, @option{-O3} and @option{-Os}. +@item -fipa-structure-reorg +@opindex fipa-structure-reorg +Transforms arrays of structures with the dead field, field reordering +and instance interleaving optimizations (which can be invoked seperately +with their own flags.) + +@item -fipa-dead-field-eliminate +@opindex fipa-dead-field-eliminate +Theobroma plaese add comment here. + +@item -fipa-field-reorder +@opindex fipa-field-reorder +Theobroma plaese add comment here. + +@item -fipa-instance-interleave +@opindex fipa-instance-interleave +Transforms arrays of structures into structures of arrays. This +improves cache locality if there are loops that use only a small +number of fields from a transformed array. The performance +qualification of this optimization relies on execution counts of loops +so running with branch profiling data available will improve the +outcome. + @item -finline-limit=@var{n} @opindex finline-limit By default, GCC limits the size of functions that can be inlined. This flag diff --git a/gcc/expr-accessor.c b/gcc/expr-accessor.c new file mode 100644 index 00000000000..6169f6d6b52 --- /dev/null +++ b/gcc/expr-accessor.c @@ -0,0 +1,106 @@ +#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" // needed for gimple-iterator.h +#include "gimple-iterator.h" +#include "gimple-ssa.h" +#include <stdbool.h> + + +#include "types-inlines.h" +#include "type-stringifier.hpp" +#include "expr-accessor.hpp" +#include "expr-walker.hpp" +#include "type-accessor.hpp" + +void +ExprAccessor::add_all_fields_in_struct(const_tree t) +{ + // Inefficient + TypeAccessor typeAccessor(record_field_map); + typeAccessor.walk(t); +} + +void +ExprAccessor::_walk_pre(const_tree e) +{ + const_tree t = TREE_TYPE(e); + add_all_fields_in_struct(t); +} + +void +ExprAccessor::update(const_tree e, unsigned access) +{ + _access = access; + walk(e); +} + +void +ExprAccessor::_walk_COMPONENT_REF_pre(const_tree e) +{ + assert_is_type(e, COMPONENT_REF); + const_tree op0 = TREE_OPERAND(e, 0); + gcc_assert(op0); + const_tree op0_t = TREE_TYPE(op0); + gcc_assert(op0_t); + // op0_t can either be a RECORD_TYPE or a UNION_TYPE + const enum tree_code code = TREE_CODE(op0_t); + const bool is_record = RECORD_TYPE == code; + const bool is_union = UNION_TYPE == code; + const bool valid = is_record != is_union; + gcc_assert(valid); + + const_tree op1 = TREE_OPERAND(e, 1); + assert_is_type(op1, FIELD_DECL); + const bool record_already_in_map = record_field_map.find(op0_t) != record_field_map.end(); + field_access_map_t field_map; + field_map = record_already_in_map ? record_field_map[op0_t] : field_map; + const bool field_already_in_map = field_map.find(op1) != field_map.end(); + unsigned prev_access = field_already_in_map ? field_map[op1] : Empty; + + prev_access |= _access; + field_map[op1] = prev_access; + add_all_fields_in_struct(op0_t); + record_field_map[op0_t] = field_map; +} + +void +ExprAccessor::print_accesses() +{ + for (auto i = record_field_map.cbegin(), e = record_field_map.cend(); i != e; ++i) + { + const_tree record = i->first; + field_access_map_t field_map = i->second; + for (auto j = field_map.cbegin(), f = field_map.cend(); j != f; ++j) + { + const_tree field = j->first; + const std::string name_r = TypeStringifier::get_type_identifier(record); + const std::string name_f = TypeStringifier::get_field_identifier(field); + unsigned access = j->second; + log("%s.%s = 0x%04x\n", name_r.c_str(), name_f.c_str(), access); + } + } +} diff --git a/gcc/expr-accessor.hpp b/gcc/expr-accessor.hpp new file mode 100644 index 00000000000..bdc43e46aa7 --- /dev/null +++ b/gcc/expr-accessor.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include "ipa-prototype.h" +#include "expr-walker.hpp" +#include "type-escaper.hpp" +#include "collect-types.h" +#include "type-accessor.hpp" +#include <tuple> +#include <map> + +constexpr unsigned Empty = 0x0u; +constexpr unsigned Read = 0x01u; +constexpr unsigned Write = 0x02u; + +typedef std::map<const_tree, unsigned> field_access_map_t; +typedef std::map<const_tree, field_access_map_t> record_field_map_t; + +class ExprAccessor : public ExprWalker +{ +public: + ExprAccessor() {}; + void update(const_tree e, unsigned a); + void print_accesses(); + void add_all_fields_in_struct(const_tree t); + record_field_map_t get_map() { return record_field_map; }; +private: + unsigned _access; + record_field_map_t record_field_map; + virtual void _walk_COMPONENT_REF_pre(const_tree e); + virtual void _walk_pre(const_tree t); +}; diff --git a/gcc/expr-collector.c b/gcc/expr-collector.c new file mode 100644 index 00000000000..1271c63cfdf --- /dev/null +++ b/gcc/expr-collector.c @@ -0,0 +1,38 @@ +#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 "expr-collector.hpp" + +inline void +ExprCollector::_walk_pre(const_tree e) +{ + const_tree t = TREE_TYPE(e); + gcc_assert(t); + typeCollector.collect(t); +} diff --git a/gcc/expr-collector.hpp b/gcc/expr-collector.hpp new file mode 100644 index 00000000000..7b3ca3ccd6a --- /dev/null +++ b/gcc/expr-collector.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "expr-walker.hpp" +#include "type-collector.hpp" + +class ExprCollector : public ExprWalker { +private: + TypeCollector typeCollector; +public: + ExprCollector() {}; + ptrset_t get_pointer_set() { return typeCollector.get_pointer_set(); } +private: + virtual void _walk_pre(const_tree e) final; +}; + diff --git a/gcc/expr-escaper.c b/gcc/expr-escaper.c new file mode 100644 index 00000000000..a7750feb9e7 --- /dev/null +++ b/gcc/expr-escaper.c @@ -0,0 +1,63 @@ +#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" // needed for gimple-iterator.h +#include "gimple-iterator.h" +#include "gimple-ssa.h" +#include <stdbool.h> + + +#include "types-inlines.h" +#include "type-escaper.hpp" +#include "expr-escaper.hpp" + + +void +ExprEscaper::update(const_tree t, Reason r) +{ + gcc_assert(t); + _r = r; + walk(t); +} + +void +ExprEscaper::_walk_pre(const_tree e) +{ + const_tree t = TREE_TYPE(e); + gcc_assert(t); + typeEscaper.update(t, _r); +} + +void +ExprEscaper::_walk_CONSTRUCTOR_pre(const_tree e) +{ + if (TREE_CLOBBER_P(e)) return; + + _r.global_is_visible = true; // just for now... + const_tree t = TREE_TYPE(e); + typeEscaper.update(t, _r); +} + diff --git a/gcc/expr-escaper.hpp b/gcc/expr-escaper.hpp new file mode 100644 index 00000000000..c58e1830133 --- /dev/null +++ b/gcc/expr-escaper.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "ipa-prototype.h" +#include "expr-walker.hpp" +#include "type-escaper.hpp" +#include "collect-types.h" + +class ExprEscaper : public ExprWalker +{ +public: + TypeEscaper typeEscaper; + ExprEscaper(ptrset_t &types) : typeEscaper(types) {}; + ptrset_t get_sets() { return typeEscaper.get_sets(); }; + void update(const_tree t, Reason r); + void update_single_level(const_tree t, Reason r) { typeEscaper.update_single_level(TREE_TYPE(t), r); }; + void print_reasons() { typeEscaper.print_reasons(); }; +private: + Reason _r; + virtual void _walk_pre(const_tree e); + virtual void _walk_CONSTRUCTOR_pre(const_tree e); +}; + + diff --git a/gcc/expr-rewriter.c b/gcc/expr-rewriter.c new file mode 100644 index 00000000000..66295d32c74 --- /dev/null +++ b/gcc/expr-rewriter.c @@ -0,0 +1,432 @@ +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "options.h" +#include "cgraph.h" +#include "tree-pass.h" +#include "tree-cfg.h" +#include "tree-pretty-print.h" +#include "gimple-pretty-print.h" +#include "stringpool.h" //get_identifier +#include "basic-block.h" //needed for gimple.h +#include "function.h" //needed for gimple.h +#include "gimple.h" +#include "cfg.h" // needed for gimple-iterator.h +#include "gimple-iterator.h" +#include "stor-layout.h" // layout_type +#include "fold-const.h" //build_fold_addr_expr +#include "gimple-ssa.h" // update_stmt +#include "attribs.h" // decl_attributes +#include "gimplify.h" //unshare_expr +#include "value-range.h" // make_ssa_name dependency +#include "tree-ssanames.h" // make_ssa_name +#include "ssa.h" +#include "tree-into-ssa.h" +#include "expr-rewriter.hpp" +#include "type-stringifier.hpp" +#include <string> +#include <map> + +void +ExprTypeRewriter::_walk_PARM_DECL_post(const_tree t) +{ + tree temp = (tree)(t); + tree ttemp = TREE_TYPE(temp); + const bool is_interesting = is_interesting_type(ttemp); + if (!is_interesting) return; + relayout_decl(temp); +} + +void +ExprTypeRewriter::_walk_FUNCTION_DECL_post(const_tree t) +{ + tree fn_type = TREE_TYPE(t); + gcc_assert(t); + tree ret_type = TREE_TYPE(fn_type); + if (!ret_type) return; + + TypeStringifier stringifier; + std::string name = stringifier.stringify(ret_type); + // WARNING: You cannot use is interesting here because you haven't + // changed the return type + // This is because the return type is not an expression. + // Therefore it is awkward to do this in the expr-walker... + // const bool is_interesting = is_interesting_type(ret_type); + const bool is_interesting = _map.find(ret_type) != _map.end(); + if (!is_interesting) return; + + tree r_t = _map[ret_type]; + TREE_TYPE(fn_type) = r_t; +} + +void +ExprTypeRewriter::_walk_MEM_REF_post(const_tree e) +{ + // The second operand is a pointer constant. + // Its type specifying the type used for type based alias analysis + tree op1 = TREE_OPERAND(e, 1); + gcc_assert(TREE_CODE(op1) == INTEGER_CST); + + tree t = TREE_TYPE(op1); + const bool already_rewritten = is_interesting_type(t); + + // This is where we do the transformation + if (!already_rewritten) return; + + const_tree old_type = _imap[t]; + assert_is_type(old_type, POINTER_TYPE); + const_tree old_base_type = TREE_TYPE(old_type); + tree old_type_size_tree = TYPE_SIZE_UNIT(old_base_type); + int old_type_size_int = tree_to_shwi(old_type_size_tree); + + tree reorg_type = t; + assert_is_type(reorg_type, POINTER_TYPE); + tree reorg_base_type = TREE_TYPE(reorg_type); + tree reorg_type_size_tree = TYPE_SIZE_UNIT(reorg_base_type); + int reorg_type_size_int = tree_to_shwi(reorg_type_size_tree); + + // Let's find out what is the previous offset + int old_offset = tree_to_uhwi(op1); + int remainder = old_offset % old_type_size_int; + + int new_offset = old_offset / old_type_size_int * reorg_type_size_int + remainder; + + tree new_offset_tree = build_int_cst(TREE_TYPE(op1), new_offset); + TREE_OPERAND(e, 1) = new_offset_tree; +} + +void +ExprTypeRewriter::_walk_SSA_NAME_post(const_tree t) +{ + // Here, we need to find out + log("we are in expr-rewriter SSA_NAME_post\n"); + TypeStringifier stringifier; + std::string name = stringifier.stringify(TREE_TYPE(t)); + log("%s\n", name.c_str()); +} + +//TODO: +//Change name of this method... +bool +ExprTypeRewriter::is_interesting_type(tree t) +{ + const bool in_imap = _imap.find(t) != _imap.end(); + bool interesting = in_imap; + if (!interesting) return false; + + tree possibly_copy = (tree)_imap[t]; + const bool is_copy = possibly_copy == t; + interesting = !is_copy; + if (!interesting) return false; + + // Let's just do a quick sanity check + tree interesting_type = t; + const bool has_valid_suffix = strstr(TypeStringifier::get_type_identifier(interesting_type).c_str(), ".reorg"); + gcc_assert(has_valid_suffix); + return true; +} + +void +ExprTypeRewriter::handle_pointer_arithmetic_diff(gimple *s, tree op_0, tree op_1) +{ + + // lhs = op0 - op1 // <-- we are here + // ... SNIP ... + // var = lhs / [ex] old_struct_size // <-- we want to be here + // + // Let's explore the uses of lhs + tree lhs = gimple_assign_lhs(s); + + tree reorg_type = TREE_TYPE(op_0); + const enum tree_code code = TREE_CODE(reorg_type); + const bool is_pointer = POINTER_TYPE == code; + const bool is_array = ARRAY_TYPE == code; + const bool is_valid_input = is_pointer != is_array; + gcc_assert(is_valid_input); + + tree inner_reorg_type = TREE_TYPE(reorg_type); + gcc_assert(inner_reorg_type); + tree reorg_type_size_tree = TYPE_SIZE_UNIT(inner_reorg_type); + int reorg_type_size_int = tree_to_shwi(reorg_type_size_tree); + + tree old_type = (tree)_imap[reorg_type]; + tree inner_old_type = TREE_TYPE(old_type); + gcc_assert(old_type); + tree old_type_size_tree = TYPE_SIZE_UNIT(inner_old_type); + int old_type_size_int = tree_to_shwi(old_type_size_tree); + + + gimple *stmt; + imm_use_iterator iterator; + FOR_EACH_IMM_USE_STMT(stmt, iterator, lhs) + { + // stmt is a use of lhs + // gimple_expr_code is only valid for non-debug statements + bool is_debug = is_gimple_debug (stmt); + if (is_debug) + continue; + + enum tree_code code = gimple_expr_code (stmt); + bool is_exact_div = code == EXACT_DIV_EXPR; + if (!is_exact_div) + continue; + + tree divisor = gimple_op (stmt, 2); + enum tree_code divisor_code = TREE_CODE (divisor); + bool is_constant = divisor_code == INTEGER_CST; + if (!is_constant) + continue; + + int divisor_int = tree_to_shwi (divisor); + bool is_same_size = divisor_int == old_type_size_int; + if (!is_same_size) + continue; + + tree new_integer_cst_tree = build_int_cst(TREE_TYPE(divisor), reorg_type_size_int); + gimple_set_op (stmt, 2, new_integer_cst_tree); + } +} + +void +ExprTypeRewriter::handle_pointer_arithmetic_nonconstant(gimple *s, tree op_0, tree op_1, bool is_pointer_plus) +{ + if (!is_pointer_plus) + { + handle_pointer_arithmetic_diff(s, op_0, op_1); + return; + } + // _1 = _0 * 72 + // ... SNIP ... + // _2 = _1 + CONSTANT; + // ... SNIP ... + // _3 = &array + _2; < -- this is where we are + //enum tree_code code = TREE_CODE(op_1); + //assert_is_type(op_1, SSA_NAME); + tree new_type = TREE_TYPE(gimple_assign_lhs(s)); + + + gimple *def_for_variable = SSA_NAME_DEF_STMT(op_1); + // It is possible that we are in a negation statement... + // Example: + // _2 = _1 * 72; + // ... SNIP ... + // _3 = -_2; < -- def_for_variable **might** be this stmt. + // ... SNIP ... + // _4 = &array + _3; + // Let's find out how many operands we have + unsigned num_operands = gimple_num_ops(def_for_variable); + // Here operands is kind of a minomer. + // operand 0 is the lhs + // operand 1 is the rhs + // I.e. lhs = (unary_operator) rhs; + bool get_another_definition = num_operands == 2; + tree possibly_not_needed = get_another_definition ? gimple_op (def_for_variable, 1) : NULL; + def_for_variable = get_another_definition ? SSA_NAME_DEF_STMT(possibly_not_needed) : def_for_variable; + + // Example: + // _2 = _1 * 72; <-- Now we are here... + // ... SNIP ... + // _3 = -_2; + // ... SNIP ... + // _4 = &array + _3; + + enum gimple_code gcode = gimple_code(def_for_variable); + switch (gcode) + { + //TODO: FIXME: + //This is unsafe, waiting for the sizeof solution + case GIMPLE_COND: + case GIMPLE_CALL: + case GIMPLE_ASSIGN: + break; + default: + return; + break; + } + enum tree_code code = gimple_expr_code (def_for_variable); + const bool is_plus_expr = PLUS_EXPR == code; + + // op_0 is the variable + // That means that the reorg_type is + // The truth is that op_0 might not have the correct type + tree reorg_type_tree = new_type; + tree reorg_inner_type = TREE_TYPE(reorg_type_tree); + tree reorg_type_size_tree = TYPE_SIZE_UNIT(reorg_inner_type); + int reorg_type_size_int = tree_to_shwi(reorg_type_size_tree); + // That means that the old type is + tree old_type_tree = (tree)_imap[reorg_type_tree]; + tree old_inner_type = TREE_TYPE(old_type_tree); + tree old_type_size_tree = TYPE_SIZE_UNIT(old_inner_type); + int old_type_size_int = tree_to_shwi(old_type_size_tree); + + if (is_plus_expr) + { + // If we are here it is because we are adding an offset. + // It is usually whenever we do somehting like + // _2 = _1 + CONSTANT; <-- to change + // _3 = &array + _2; + tree constant_plus = gimple_op (def_for_variable, 2); + assert_is_type(constant_plus, INTEGER_CST); + + int old_integer_cst_int = tree_to_uhwi(constant_plus); + int modulo = old_integer_cst_int % old_type_size_int; + int new_integer_cst_int = old_integer_cst_int / old_type_size_int * reorg_type_size_int + modulo; + + tree new_integer_cst_tree = build_int_cst(TREE_TYPE(constant_plus), new_integer_cst_int); + gimple_set_op(def_for_variable, 2, new_integer_cst_tree); + + tree variable = gimple_op (def_for_variable, 1); + def_for_variable = SSA_NAME_DEF_STMT(variable); + num_operands = gimple_num_ops (def_for_variable); + get_another_definition = num_operands == 2; + def_for_variable = get_another_definition ? SSA_NAME_DEF_STMT(gimple_op(def_for_variable, 1)) : def_for_variable; + code = gimple_expr_code(def_for_variable); + + + } + + if (code == MULT_EXPR) { + + tree op_0_earlier = gimple_assign_rhs1(def_for_variable); + tree op_1_earlier = gimple_assign_rhs2(def_for_variable); + + // We should be able to just call the constant implementation + //handle_pointer_arithmetic_constants(def_for_variable, op_0, op_1); + //However... + //these variables no longer hold the type needed for them to change correctly + //so, let's do it from here... + + assert_is_type(op_1_earlier, INTEGER_CST); + + + tree old_integer_cst_tree = op_1_earlier; + int old_integer_cst_int = tree_to_uhwi(old_integer_cst_tree); + + int offset = old_integer_cst_int % old_type_size_int ; + int new_integer_cst_int = old_integer_cst_int / old_type_size_int * reorg_type_size_int + offset; + log("%d = %d / %d * %d + %d\n", new_integer_cst_int, old_integer_cst_int, old_type_size_int, reorg_type_size_int, offset); + + tree new_integer_cst_tree = build_int_cst(TREE_TYPE(old_integer_cst_tree), new_integer_cst_int); + gimple_set_op(def_for_variable, 2, new_integer_cst_tree); + } +} + +void +ExprTypeRewriter::handle_pointer_arithmetic_constants(gimple *s, tree p, tree i, bool is_pointer_plus) +{ + // So, because we have already changed the type + // tree p will either be the original type + // if we do not need to modify this expression + // How do we know if we have an original type? + // It is when we don't have a type in our map + tree possibly_reorged_type = TREE_TYPE(p); + bool is_interesting_case = is_interesting_type(possibly_reorged_type); + if (!is_interesting_case) return; + + tree reorg_type = possibly_reorged_type; // this is the type of the variable + const_tree original_type = _imap[reorg_type]; + // If we are here, that means that our type has the ".reorg" suffix + const bool has_suffix = strstr(TypeStringifier::get_type_identifier(reorg_type).c_str(), ".reorg"); + bool is_valid_input = has_suffix; + gcc_assert(is_valid_input); + + // We need to know what size is the previous original type + tree inner_reorg_type = TREE_TYPE(reorg_type); + tree inner_orig_type = TREE_TYPE(original_type); + tree old_size_tree = TYPE_SIZE_UNIT(inner_orig_type); + int old_size_int = tree_to_shwi(old_size_tree); + tree new_size_tree = TYPE_SIZE_UNIT(inner_reorg_type); + int new_size_int = tree_to_shwi(new_size_tree); + tree old_integer_cst_tree = i; + int old_integer_cst_int = tree_to_uhwi(old_integer_cst_tree); + + int offset = old_integer_cst_int % old_size_int; + const bool is_modulo = offset == 0; + is_valid_input = is_modulo; + gcc_assert(is_valid_input); + + int new_integer_cst_int = old_integer_cst_int / old_size_int * new_size_int + offset; + log("%d = %d / %d * %d\n", new_integer_cst_int, old_integer_cst_int, old_size_int, new_size_int); + + tree new_integer_cst_tree = build_int_cst(TREE_TYPE(old_integer_cst_tree), new_integer_cst_int); + gimple_set_op(s, 2, new_integer_cst_tree); + + +} + +void +ExprTypeRewriter::_walk_post(const_tree e) +{ + gcc_assert(e); + tree t = TREE_TYPE(e); + const bool in_map = _map.find(t) != _map.end(); + if (!in_map) return; + + const enum tree_code code = TREE_CODE(e); + tree r_t = _map[t]; + TREE_TYPE((tree)e) = r_t; + + return; + if (code != MEM_REF) return; + + TypeStringifier stringifier; + std::string name = stringifier.stringify(r_t); + tree m = TYPE_MAIN_VARIANT(r_t); + std::string name_m = stringifier.stringify(m); + const bool main_variant = TYPE_MAIN_VARIANT(r_t) == r_t; + log("main: %s\n", name_m.c_str()); + log("we are in memref: %s is_main_variant %s\n", name.c_str(), main_variant ? "t" : "f"); + tree type_size = TYPE_SIZE(r_t); + log("type size 1 %s?", type_size ? "t" : "f"); + if (!type_size) TYPE_SIZE(r_t) = TYPE_SIZE(m); + log("type size 2 %s?", TYPE_SIZE(r_t) ? "t" : "f"); + if (!TYPE_SIZE(r_t)) layout_type(r_t); + log("type size 3 %s?", TYPE_SIZE(r_t) ? "t" : "f"); + // still no type_size + gcc_assert(TYPE_SIZE(r_t)); + const enum tree_code cc = TREE_CODE(TYPE_SIZE(r_t)); + log("%s\n", get_tree_code_name(cc)); + + //tree type_main_variant = TYPE_MAIN_VARIANT(TREE_TYPE(e)); + //const bool do_we_have_mv_in_map = _map.find(type_main_variant) != _map.end(); + + // TODO: Fix this hack + // We need to make sure that the type main variant is already good here... + //TYPE_MAIN_VARIANT(TREE_TYPE(e)) = do_we_have_mv_in_map ? _map[type_main_variant] : TYPE_MAIN_VARIANT(TREE_TYPE(e)); + +} + +void +ExprTypeRewriter::_walk_COMPONENT_REF_post(const_tree e) +{ + + const_tree r = TREE_OPERAND(e, 0); + tree record_type = TREE_TYPE(r); + const bool in_map1 = _map.find(record_type) != _map.end(); + + const_tree f = TREE_OPERAND(e, 1); + // So, what we need is a map between this field and the new field + const bool in_map = _map2.find(f) != _map2.end(); + if (!in_map) return; + + auto p = _map2[f]; + tree n_f = p.first; + bool is_deleted = p.second; + + unsigned f_byte_offset = tree_to_uhwi(DECL_FIELD_OFFSET(f)); + unsigned f_bit_offset = tree_to_uhwi(DECL_FIELD_BIT_OFFSET(f)); + unsigned f_offset = 8 * f_byte_offset + f_bit_offset; + + unsigned nf_byte_offset = tree_to_uhwi(DECL_FIELD_OFFSET(n_f)); + unsigned nf_bit_offset = tree_to_uhwi(DECL_FIELD_BIT_OFFSET(n_f)); + unsigned nf_offset = 8 * nf_byte_offset + nf_bit_offset; + TREE_OPERAND(e, 1) = n_f; + + if (!is_deleted) return; + + _delete = true; + +} + + diff --git a/gcc/expr-rewriter.hpp b/gcc/expr-rewriter.hpp new file mode 100644 index 00000000000..23c0005bcd5 --- /dev/null +++ b/gcc/expr-rewriter.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "expr-walker.hpp" +#include "type-reconstructor.hpp" + +class ExprTypeRewriter : public ExprWalker +{ +public: + ExprTypeRewriter(TypeReconstructor::reorg_record_map_t map, TypeReconstructor::reorg_field_map_t map2) : _delete(false), _map(map), _map2(map2) { + for (auto i = map.cbegin(), e = map.cend(); i != e; ++i) + { + const_tree original = i->first; + tree modified = i->second; + _imap[modified] = original; + } + }; + void handle_pointer_arithmetic_constants(gimple *s, tree p, tree i, bool); + void handle_pointer_arithmetic_diff(gimple *s, tree p, tree i); + void handle_pointer_arithmetic_nonconstant(gimple *s, tree p, tree i, bool); + bool is_interesting_type(tree); + bool delete_statement(); + bool _delete; +private: + TypeReconstructor::reorg_record_map_t _map; + TypeReconstructor::reorg_field_map_t _map2; + std::map<tree, const_tree> _imap; + void _walk_post(const_tree e); + void _walk_MEM_REF_post(const_tree e); + void _walk_COMPONENT_REF_post(const_tree e); + void _walk_PARM_DECL_post(const_tree e); + void _walk_SSA_NAME_post(const_tree e); + void _walk_FUNCTION_DECL_post(const_tree e); +}; diff --git a/gcc/expr-walker.c b/gcc/expr-walker.c new file mode 100644 index 00000000000..f5f795dfa97 --- /dev/null +++ b/gcc/expr-walker.c @@ -0,0 +1,312 @@ +#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 "expr-walker.hpp" +#include "types-inlines.h" + +void +ExprWalker::walk(const_tree e) +{ + _walk_pre(e); + _walk(e); + _walk_post(e); +} + +void +ExprWalker::_walk(const_tree e) +{ + gcc_assert(e); + const enum tree_code code = TREE_CODE(e); + switch (code) + { + case INTEGER_CST: + walk_INTEGER_CST(e); + break; + case REAL_CST: + walk_REAL_CST(e); + break; + case STRING_CST: + walk_STRING_CST(e); + break; + case BIT_FIELD_REF: + walk_BIT_FIELD_REF(e); + break; + case ARRAY_REF: + walk_ARRAY_REF(e); + break; + case MEM_REF: + walk_MEM_REF(e); + break; + case COMPONENT_REF: + walk_COMPONENT_REF(e); + break; + case SSA_NAME: + walk_SSA_NAME(e); + break; + case ADDR_EXPR: + walk_ADDR_EXPR(e); + break; + case VIEW_CONVERT_EXPR: + walk_VIEW_CONVERT_EXPR(e); + break; + case IMAGPART_EXPR: + walk_IMAGPART_EXPR(e); + break; + case VAR_DECL: + walk_VAR_DECL(e); + break; + case FIELD_DECL: + walk_FIELD_DECL(e); + break; + case RESULT_DECL: + walk_RESULT_DECL(e); + break; + case PARM_DECL: + walk_PARM_DECL(e); + break; + case FUNCTION_DECL: + walk_FUNCTION_DECL(e); + break; + case CONSTRUCTOR: + walk_CONSTRUCTOR(e); + break; + case LE_EXPR: + walk_LE_EXPR(e); + break; + case EQ_EXPR: + walk_EQ_EXPR(e); + break; + case GT_EXPR: + walk_GT_EXPR(e); + break; + default: + { + log("missing %s\n", get_tree_code_name(code)); + gcc_unreachable(); + } + break; + } +} + +#define ExprWalkerFuncDef(code) \ +void \ +ExprWalker::walk_ ## code (const_tree e) \ +{ \ + assert_is_type(e, code); \ + _walk_pre(e); \ + _walk_ ## code ## _pre (e); \ + _walk_ ## code (e); \ + _walk_ ## code ## _post (e); \ + _walk_post(e); \ +} + +ExprWalkerFuncDef(CONSTRUCTOR) +ExprWalkerFuncDef(INTEGER_CST) +ExprWalkerFuncDef(REAL_CST) +ExprWalkerFuncDef(STRING_CST) +ExprWalkerFuncDef(BIT_FIELD_REF) +ExprWalkerFuncDef(ARRAY_REF) +ExprWalkerFuncDef(MEM_REF) +ExprWalkerFuncDef(COMPONENT_REF) +ExprWalkerFuncDef(SSA_NAME) +ExprWalkerFuncDef(ADDR_EXPR) +ExprWalkerFuncDef(VIEW_CONVERT_EXPR) +ExprWalkerFuncDef(IMAGPART_EXPR) +ExprWalkerFuncDef(FIELD_DECL) +ExprWalkerFuncDef(VAR_DECL) +ExprWalkerFuncDef(RESULT_DECL) +ExprWalkerFuncDef(PARM_DECL) +ExprWalkerFuncDef(FUNCTION_DECL) +ExprWalkerFuncDef(LE_EXPR) +ExprWalkerFuncDef(EQ_EXPR) +ExprWalkerFuncDef(GT_EXPR) + +void +ExprWalker::_walk_leaf(const_tree e, const enum tree_code c) +{ + assert_is_type(e, c); +} + +void +ExprWalker::_walk_op_n(const_tree e, unsigned n) +{ + gcc_assert(e); + const_tree op_n = TREE_OPERAND(e, n); + gcc_assert(op_n); + walk(op_n); +} + +void +ExprWalker::_walk_op_0(const_tree e, const enum tree_code c) +{ + assert_is_type(e, c); + _walk_op_n(e, 0); +} + +void +ExprWalker::_walk_op_1(const_tree e, const enum tree_code c) +{ + assert_is_type(e, c); + _walk_op_n(e, 0); + _walk_op_n(e, 1); +} + +void +ExprWalker::_walk_CONSTRUCTOR(const_tree e) +{ +#ifdef FUZZ_MODE + gcc_unreachable(); +#endif +} + +void +ExprWalker::_walk_LE_EXPR(const_tree e) +{ + _walk_op_1(e, LE_EXPR); +} + +void +ExprWalker::_walk_EQ_EXPR(const_tree e) +{ + _walk_op_1(e, EQ_EXPR); +} + +void +ExprWalker::_walk_GT_EXPR(const_tree e) +{ + _walk_op_1(e, GT_EXPR); +} + + +void +ExprWalker::_walk_INTEGER_CST(const_tree e) +{ + _walk_leaf(e, INTEGER_CST); +} + +void +ExprWalker::_walk_REAL_CST(const_tree e) +{ + _walk_leaf(e, REAL_CST); +} + +void +ExprWalker::_walk_STRING_CST(const_tree e) +{ + _walk_leaf(e, STRING_CST); +} + +void +ExprWalker::_walk_BIT_FIELD_REF(const_tree e) +{ +#ifdef FUZZ_MODE + gcc_unreachable(); +#endif +} + +void +ExprWalker::_walk_ARRAY_REF(const_tree e) +{ + _walk_op_1(e, ARRAY_REF); +} + +void +ExprWalker::_walk_MEM_REF(const_tree e) +{ + _walk_op_1(e, MEM_REF); +} + +void +ExprWalker::_walk_COMPONENT_REF(const_tree e) +{ + _walk_op_1(e, COMPONENT_REF); +} + +void +ExprWalker::_walk_SSA_NAME(const_tree e) +{ + _walk_leaf(e, SSA_NAME); +} + +void +ExprWalker::_walk_ADDR_EXPR(const_tree e) +{ + _walk_op_0(e, ADDR_EXPR); +} + +void +ExprWalker::_walk_VIEW_CONVERT_EXPR(const_tree e) +{ +#ifdef FUZZ_MODE + gcc_unreachable(); +#endif +} + +void +ExprWalker::_walk_IMAGPART_EXPR(const_tree e) +{ +#ifdef FUZZ_MODE + gcc_unreachable(); +#endif +} + +void +ExprWalker::_walk_FIELD_DECL(const_tree e) +{ + _walk_leaf(e, FIELD_DECL); +} + +void +ExprWalker::_walk_VAR_DECL(const_tree e) +{ + _walk_leaf(e, VAR_DECL); +} + +void +ExprWalker::_walk_RESULT_DECL(const_tree e) +{ + _walk_leaf(e, RESULT_DECL); +} + +void +ExprWalker::_walk_PARM_DECL(const_tree e) +{ + _walk_leaf(e, PARM_DECL); +} + +void +ExprWalker::_walk_FUNCTION_DECL(const_tree e) +{ + _walk_leaf(e, FUNCTION_DECL); + for (tree parm = DECL_ARGUMENTS(e); parm; parm = DECL_CHAIN(parm)) + { + walk(parm); + } + +} diff --git a/gcc/expr-walker.hpp b/gcc/expr-walker.hpp new file mode 100644 index 00000000000..e38084c8838 --- /dev/null +++ b/gcc/expr-walker.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include "types-inlines.h" + + +class ExprWalker +{ +public: + ExprWalker() {}; + void walk(const_tree e); +private: + virtual void _walk_pre(__attribute__((unused)) const_tree e) {}; + void _walk(const_tree e); + virtual void _walk_post(__attribute__((unused)) const_tree e) {}; + inline void _walk_leaf(const_tree e, const enum tree_code c); + inline void _walk_op_n(const_tree e, unsigned n); + inline void _walk_op_0(const_tree e, const enum tree_code c); + inline void _walk_op_1(const_tree e, const enum tree_code c); + +#define ExprWalkerFuncDecl(code) \ + virtual void _walk_ ## code ## _pre(__attribute__((unused)) const_tree e) {}; \ + void walk_ ## code (const_tree e); \ + void _walk_ ## code (const_tree e); \ + virtual void _walk_ ## code ## _post(__attribute__((unused)) const_tree e) {} + + ExprWalkerFuncDecl(CONSTRUCTOR); + ExprWalkerFuncDecl(INTEGER_CST); + ExprWalkerFuncDecl(REAL_CST); + ExprWalkerFuncDecl(STRING_CST); + ExprWalkerFuncDecl(BIT_FIELD_REF); + ExprWalkerFuncDecl(ARRAY_REF); + ExprWalkerFuncDecl(MEM_REF); + ExprWalkerFuncDecl(COMPONENT_REF); + ExprWalkerFuncDecl(SSA_NAME); + ExprWalkerFuncDecl(ADDR_EXPR); + ExprWalkerFuncDecl(VIEW_CONVERT_EXPR); + ExprWalkerFuncDecl(IMAGPART_EXPR); + ExprWalkerFuncDecl(FIELD_DECL); + ExprWalkerFuncDecl(VAR_DECL); + ExprWalkerFuncDecl(RESULT_DECL); + ExprWalkerFuncDecl(PARM_DECL); + ExprWalkerFuncDecl(FUNCTION_DECL); + ExprWalkerFuncDecl(LE_EXPR); + ExprWalkerFuncDecl(EQ_EXPR); + ExprWalkerFuncDecl(GT_EXPR); +}; + diff --git a/gcc/gimple-accesser.c b/gcc/gimple-accesser.c new file mode 100644 index 00000000000..18d74f499c0 --- /dev/null +++ b/gcc/gimple-accesser.c @@ -0,0 +1,105 @@ +#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 "gimple-accesser.hpp" + + +void +GimpleAccesser::_walk_pre(gassign *s) +{ + // There seems to be quite a bit of code duplication here... + const enum gimple_rhs_class code = gimple_assign_rhs_class(s); + switch (code) + { + case GIMPLE_TERNARY_RHS: + { + const_tree rhs3 = gimple_assign_rhs3(s); + gcc_assert(rhs3); + exprAccessor.update(rhs3, Read); + } + /* fall-through */ + case GIMPLE_BINARY_RHS: + { + const_tree rhs2 = gimple_assign_rhs2(s); + gcc_assert(rhs2); + exprAccessor.update(rhs2, Read); + } + /* fall-through */ + case GIMPLE_UNARY_RHS: + case GIMPLE_SINGLE_RHS: + { + const_tree rhs1 = gimple_assign_rhs1(s); + exprAccessor.update(rhs1, Read); + const_tree lhs = gimple_assign_lhs(s); + if (!lhs) break; + exprAccessor.update(lhs, Write); + break; + } + default: + gcc_unreachable(); + break; + } +} + +void +GimpleAccesser::_walk_pre(gcall *s) +{ + tree fndecl = gimple_call_fndecl(s); + unsigned n = gimple_call_num_args(s); + for (unsigned i = 0; i < n; i++) + { + const_tree a = gimple_call_arg(s, i); + gcc_assert(a); + exprAccessor.update(a, Read); + } + + const_tree lhs = gimple_call_lhs(s); + if (!lhs) return; + exprAccessor.update(lhs, Write); +} + +void +GimpleAccesser::_walk_pre(greturn *s) +{ + const_tree val = gimple_return_retval(s); + if (!val) return; + exprAccessor.update(val, Read); +} + +void +GimpleAccesser::_walk_pre(gcond *s) +{ + const_tree lhs = gimple_cond_lhs(s); + const_tree rhs = gimple_cond_rhs(s); + gcc_assert(lhs && rhs); + exprAccessor.update(lhs, Read); + exprAccessor.update(rhs, Read); +} diff --git a/gcc/gimple-accesser.hpp b/gcc/gimple-accesser.hpp new file mode 100644 index 00000000000..81228724daa --- /dev/null +++ b/gcc/gimple-accesser.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "gimple-walker.hpp" +#include "expr-accessor.hpp" + +/* + * GimpleAccesser is intended to walk gimple + * and update a map that will hold information + * on whether a type was casted or not. + */ +class GimpleAccesser : public GimpleWalker +{ +public: + GimpleAccesser() : GimpleWalker() {}; + void print_accesses() { exprAccessor.print_accesses(); }; + record_field_map_t get_map() { return exprAccessor.get_map(); }; +private: + ExprAccessor exprAccessor; + virtual void _walk_pre(gcall *s) final; + virtual void _walk_pre(gassign *s) final; + virtual void _walk_pre(greturn *s) final; + virtual void _walk_pre(gcond *s) final; + // Do we need a glabel? I don't think so... + // But we might need a gswitch. +}; diff --git a/gcc/gimple-caster.c b/gcc/gimple-caster.c new file mode 100644 index 00000000000..04283c9237a --- /dev/null +++ b/gcc/gimple-caster.c @@ -0,0 +1,116 @@ +#include "gimple-caster.hpp" +#include "gimple-pretty-print.h" + +#include "type-incomplete-equality.hpp" +#include "type-stringifier.hpp" + + +void +GimpleCaster::_walk_pre(gassign *s) +{ + const enum gimple_rhs_class code = gimple_assign_rhs_class(s); + const bool valid_input = GIMPLE_SINGLE_RHS == code; + if (!valid_input) return; + + // I originally was using gimple_assign_cast_p + // but that proved to be insufficient... + // So we have to use our equality comparison... + TypeIncompleteEquality equality; + const_tree lhs = gimple_assign_lhs(s); + const_tree rhs = gimple_assign_rhs1(s); + gcc_assert(lhs && rhs); + Reason reason {}; + const_tree t_lhs = TREE_TYPE(lhs); + const_tree t_rhs = TREE_TYPE(rhs); + gcc_assert(t_lhs && t_rhs); + bool is_cast = !equality.equal(t_lhs, t_rhs); + TypeStringifier stringifier; + const std::string name_l = stringifier.stringify(t_lhs); + const std::string name_r = stringifier.stringify(t_rhs); + // If it is cast, we might need to look at the definition of rhs + // If the definition comes from a known function... then we are good... + bool is_ssa = TREE_CODE(rhs) == SSA_NAME; + while (is_ssa) { + gimple *def_for_rhs = SSA_NAME_DEF_STMT(rhs); + gcall *is_call = dyn_cast<gcall*>(def_for_rhs); + // poor man's goto + if (!is_call) break; + + const_tree fn = gimple_call_fndecl(is_call); + // poor man's goto + if (!fn) break; + + bool known_function = GimpleEscaper::filter_known_function(fn); + is_cast = !known_function; + + is_ssa = false; + } + reason.type_is_casted = is_cast; + exprEscaper.update(lhs, reason); + exprEscaper.update(rhs, reason); + // TODO: + // I think this will re-do the work... But it might be necessary? + GimpleEscaper::_walk_pre(s); +} + +void +GimpleCaster::_walk_pre(gcall *s) +{ + GimpleEscaper::_walk_pre(s); + + const_tree fn = gimple_call_fndecl(s); + // If there's no function declaration, how do we + // know the argument types? + if (!fn) return; + + cgraph_node *node = cgraph_node::get(fn); + const bool known_function = GimpleEscaper::filter_known_function(node) || GimpleEscaper::filter_known_function(fn); + if (known_function) return; + + const_tree f_t = TREE_TYPE(fn); + TypeIncompleteEquality equality; + TypeStringifier stringifier; + + unsigned i = 0; + unsigned n = gimple_call_num_args(s); + for (tree a = TYPE_ARG_TYPES(f_t); NULL_TREE != a; a = TREE_CHAIN(a)) + { + const_tree formal_t = TREE_VALUE(a); + // There seems to be a final VOID_TYPE at the end of some functions? + const enum tree_code code = TREE_CODE(formal_t); + const bool is_void = VOID_TYPE == code; + if (is_void) continue; + + const_tree real = gimple_call_arg(s, i); + const_tree real_t = TREE_TYPE(real); + const bool is_casted = !equality.equal(formal_t, real_t); + const std::string name_r = stringifier.stringify(real_t); + const std::string name_f = stringifier.stringify(formal_t); + Reason arg_reason; + arg_reason.type_is_casted = is_casted; + exprEscaper.update(real, arg_reason); + i++; + } + + /* + unsigned n = gimple_call_num_args(s); + for (unsigned i = 0; i < n; i++) + { + const_tree a = gimple_call_arg(s, i); + gcc_assert(a); + exprEscaper.update(a, reason); + } + */ + + const_tree lhs = gimple_call_lhs(s); + if (!lhs) return; + + const_tree r_t = TREE_TYPE(f_t); + const_tree l_t TREE_TYPE(lhs); + const bool is_casted = !equality.equal(r_t, l_t); + const std::string name_r_t = stringifier.stringify(r_t); + const std::string name_l_t = stringifier.stringify(r_t); + Reason ret_reason; + ret_reason.type_is_casted = is_casted; + exprEscaper.update(lhs, ret_reason); +} diff --git a/gcc/gimple-caster.hpp b/gcc/gimple-caster.hpp new file mode 100644 index 00000000000..74086606862 --- /dev/null +++ b/gcc/gimple-caster.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "gimple-escaper.hpp" + +/* + * GimpleCaster is intended to walk gimple + * and update a map that will hold information + * on whether a type was casted or not. + */ +class GimpleCaster : public GimpleEscaper +{ +public: + GimpleCaster(ptrset_t &types) : GimpleEscaper(types) {}; +private: + virtual void _walk_pre(gcall *s) final; + // Find out which structs are casted. + // Technically we could find this out on parent + virtual void _walk_pre(gassign *s) final; +}; diff --git a/gcc/gimple-collector.c b/gcc/gimple-collector.c new file mode 100644 index 00000000000..3a38e2334dd --- /dev/null +++ b/gcc/gimple-collector.c @@ -0,0 +1,124 @@ +#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 <map> + +#include "collect-types.h" +#include "type-stringifier.hpp" + +#include "type-collector.hpp" +#include "expr-walker.hpp" +#include "expr-collector.hpp" +#include "gimple-collector.hpp" + +void +GimpleTypeCollector::_walk_pre(const_tree t) +{ + exprCollector.walk(t); +} + +void +GimpleTypeCollector::_walk_pre(gassign *s) +{ + const_tree lhs = gimple_assign_lhs(s); + exprCollector.walk(lhs); + + const enum gimple_rhs_class gclass = gimple_assign_rhs_class(s); + switch (gclass) + { + case GIMPLE_TERNARY_RHS: + { + const_tree rhs = gimple_assign_rhs3(s); + exprCollector.walk(rhs); + } + /* fall-through */ + case GIMPLE_BINARY_RHS: + { + const_tree rhs = gimple_assign_rhs2(s); + exprCollector.walk(rhs); + } + /* fall-through */ + case GIMPLE_UNARY_RHS: + case GIMPLE_SINGLE_RHS: + { + const_tree rhs = gimple_assign_rhs1(s); + exprCollector.walk(rhs); + } + break; + default: + gcc_unreachable(); + break; + } +} + +void +GimpleTypeCollector::_walk_pre(greturn *s) +{ + const_tree retval = gimple_return_retval(s); + if (!retval) return; + + exprCollector.walk(retval); +} + +void +GimpleTypeCollector::_walk_pre(gcond *s) +{ + const_tree lhs = gimple_cond_lhs(s); + exprCollector.walk(lhs); + const_tree rhs = gimple_cond_rhs(s); + exprCollector.walk(rhs); +} + +void +GimpleTypeCollector::_walk_pre(gcall *s) +{ + unsigned n = gimple_call_num_args(s); + for (unsigned i = 0; i < n; i++) + { + const_tree a = gimple_call_arg(s, i); + exprCollector.walk(a); + } + + const_tree lhs = gimple_call_lhs(s); + if (!lhs) return; + + exprCollector.walk(lhs); +} + +void +GimpleTypeCollector::print_collected() +{ + ptrset_t sets = get_pointer_set(); + sets.print_in_points_to_record(); + +} diff --git a/gcc/gimple-collector.hpp b/gcc/gimple-collector.hpp new file mode 100644 index 00000000000..0c7bba0720e --- /dev/null +++ b/gcc/gimple-collector.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "gimple-walker.hpp" +#include "expr-collector.hpp" + +class GimpleTypeCollector : public GimpleWalker +{ +private: + ExprCollector exprCollector; +public: + GimpleTypeCollector() {}; + ptrset_t get_pointer_set() { return exprCollector.get_pointer_set(); } + // TODO: I believe this could be made const + void print_collected(); +private: + virtual void _walk_pre(const_tree) final; + virtual void _walk_pre(gassign *s) final; + virtual void _walk_pre(greturn *s) final; + virtual void _walk_pre(gcond *s) final; + virtual void _walk_pre(gcall *s) final; +}; + diff --git a/gcc/gimple-escaper.c b/gcc/gimple-escaper.c new file mode 100644 index 00000000000..0b45cee3ee9 --- /dev/null +++ b/gcc/gimple-escaper.c @@ -0,0 +1,263 @@ +#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" // needed for gimple-iterator.h +#include "gimple-iterator.h" +#include "gimple-ssa.h" +#include "gimple-pretty-print.h" +#include <stdbool.h> + +#include "gimple-escaper.hpp" +#include "type-stringifier.hpp" +#include "type-incomplete-equality.hpp" + + +void +GimpleEscaper::_init() +{ + cgraph_node *cnode = NULL; + FOR_EACH_FUNCTION(cnode) + { + gcc_assert(cnode); + const bool filter = GimpleEscaper::filter_known_function(cnode); + if (filter) continue; + + const_tree decl = cnode->decl; + gcc_assert(decl); + undefined.insert(decl); + } + + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY(cnode) + { + gcc_assert(cnode); + cnode->get_untransformed_body(); + const_tree decl = cnode->decl; + gcc_assert(decl); + undefined.erase(decl); + } +} + +bool +GimpleEscaper::is_function_escaping(cgraph_node *cnode) +{ + const bool filter = GimpleEscaper::filter_known_function(cnode); + if (filter) return false; + + return cnode->externally_visible; +} + +bool +GimpleEscaper::is_function_escaping(const_tree fndecl) +{ + if (!fndecl) return true; + + if (!TREE_PUBLIC(fndecl) || DECL_EXTERNAL(fndecl)) return false; + + return true; +} + +bool +GimpleEscaper::is_variable_escaping(varpool_node *vnode) +{ + gcc_assert(vnode); + return vnode->externally_visible; +} + +void +GimpleEscaper::_walk_global(varpool_node *vnode) +{ + gcc_assert(vnode); + const_tree var_decl = vnode->decl; + Reason reason {} ; + const bool is_escaping = is_variable_escaping(vnode); + reason.global_is_visible = is_escaping; + + tree initial = DECL_INITIAL (var_decl); + const bool constructor = initial ? TREE_CODE (initial) == CONSTRUCTOR : false; + const bool error_mark = initial ? TREE_CODE (initial) == ERROR_MARK: false; + reason.global_is_visible |= constructor || error_mark; // static initialization... + + TypeStringifier stringifier; + std::string name = stringifier.stringify(TREE_TYPE(var_decl)); + log("%s %s\n", vnode->name(), name.c_str()); + exprEscaper.update(var_decl, reason); + GimpleWalker::_walk_global(vnode); + +} + +bool +GimpleEscaper::filter_known_function(const_tree fndecl) +{ + assert_is_type(fndecl, FUNCTION_DECL); + if (fndecl_built_in_p (fndecl)) + { + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_FREE: + case BUILT_IN_MALLOC: + case BUILT_IN_REALLOC: + case BUILT_IN_CALLOC: + case BUILT_IN_MEMSET: + return true; + break; + default: + break; + } + } + + + const_tree identifier_node = DECL_NAME(fndecl); + gcc_assert(identifier_node); + bool filter = false; + const char *_specqsort= "spec_qsort"; + const char *_med3 = "arc_compare"; + const char *_getArcPosition = "getArcPosition"; + const char *_med3_ = "med3.part.0"; + const char *_med3_2 = "med3"; + const char* name = IDENTIFIER_POINTER(identifier_node); + gcc_assert(name); + filter |= strcmp(_specqsort, name) == 0; + filter |= strcmp(_med3, name) == 0; + filter |= strcmp(_med3_, name) == 0; + filter |= strcmp(_med3_2, name) == 0; + filter |= strcmp(_getArcPosition, name) == 0; + return filter; +} + +bool +GimpleEscaper::filter_known_function(cgraph_node *node) +{ + if (!node) return false; + return filter_known_function(node->decl); +} + +void +GimpleEscaper::_walk_pre(const_tree t) +{ + // Is any global variable escaping? + Reason reason; + exprEscaper.update(t, reason); +} + +void +GimpleEscaper::_walk_pre(gassign *s) +{ + Reason reason; + const enum gimple_rhs_class code = gimple_assign_rhs_class(s); + switch (code) + { + case GIMPLE_TERNARY_RHS: + { + const_tree rhs3 = gimple_assign_rhs3(s); + exprEscaper.update(rhs3, reason); + } + /* fall-through */ + case GIMPLE_BINARY_RHS: + { + const_tree rhs2 = gimple_assign_rhs2(s); + exprEscaper.update(rhs2, reason); + } + /* fall-through */ + case GIMPLE_UNARY_RHS: + case GIMPLE_SINGLE_RHS: + { + const_tree rhs1 = gimple_assign_rhs1(s); + exprEscaper.update(rhs1, reason); + const_tree lhs = gimple_assign_lhs(s); + if (!lhs) break; + exprEscaper.update(lhs, reason); + } + break; + default: + gcc_unreachable(); + break; + } +} + +void +GimpleEscaper::_walk_pre(greturn *s) +{ + Reason reason; + const_tree val = gimple_return_retval(s); + if (!val) return; + exprEscaper.update(val, reason); +} + +void +GimpleEscaper::_walk_pre(gcond *s) +{ + Reason reason; + const_tree lhs = gimple_cond_lhs(s); + const_tree rhs = gimple_cond_rhs(s); + gcc_assert(lhs && rhs); + exprEscaper.update(lhs, reason); + exprEscaper.update(rhs, reason); +} + +void +GimpleEscaper::_walk_pre(gcall *s) +{ + const_tree fn = gimple_call_fndecl(s); + // gcc_assert(fn); + // The above will not always be true + cgraph_node *node = fn ? cgraph_node::get(fn) : NULL; + // const bool fn_and_node = fn && node; + // const bool not_function_and_not_node = !fn && !node; + // const bool test = fn_and_node ^ not_function_and_not_node; + // gcc_assert(test); + // The above is not true... + // which means that there are functions with function declarations + // but no corresponding cgraph_node. + // + // What does that mean for our analysis? + // It means that we cannot find out if a function is escaping all the time..? + // Or at least via the cnode... + // It seems to me that the correct way to deal with this is saying that + // functions which do not have a cgraph_node should be escaping, + // but this will mark some interesting types as escaping... + const bool _is_function_escaping = node ? is_function_escaping(node) : is_function_escaping(fn); + const bool is_undefined = undefined.find(fn) != undefined.end(); + const bool _is_escaping = is_undefined || _is_function_escaping; + + TypeStringifier stringifier; + Reason arg_reason; + arg_reason.parameter_is_visible = _is_escaping; + arg_reason.is_indirect = !fn; + unsigned n = gimple_call_num_args(s); + for (unsigned i = 0; i < n; i++) + { + const_tree a = gimple_call_arg(s, i); + gcc_assert(a); + exprEscaper.update(a, arg_reason); + } + + const_tree lhs = gimple_call_lhs(s); + if (!lhs) return; + Reason return_reason; + return_reason.return_is_visible = _is_escaping; + return_reason.is_indirect = !fn; + exprEscaper.update(lhs, return_reason); +} diff --git a/gcc/gimple-escaper.hpp b/gcc/gimple-escaper.hpp new file mode 100644 index 00000000000..490ebc20fe0 --- /dev/null +++ b/gcc/gimple-escaper.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "gimple-walker.hpp" +#include "expr-escaper.hpp" +#include "collect-types.h" + +class GimpleEscaper : public GimpleWalker +{ +public: + GimpleEscaper(ptrset_t &types) : exprEscaper(types) { _init(); }; + ExprEscaper exprEscaper; + ptrset_t get_sets() { return exprEscaper.get_sets(); }; + void print_reasons() { exprEscaper.print_reasons(); }; +protected: + typedef std::set<const_tree> undefset; + undefset undefined; + void _init(); + static bool filter_known_function(cgraph_node *); + static bool filter_known_function(const_tree); + static bool is_function_escaping(cgraph_node *); + static bool is_function_escaping(const_tree); + static bool is_variable_escaping(varpool_node *); + static bool _is_assignment_casted(gassign *s); + virtual void _walk_global(varpool_node *); + virtual void _walk_pre(gassign *s) ; + virtual void _walk_pre(greturn *s) ; + virtual void _walk_pre(gcond *s) ; + virtual void _walk_pre(gcall *s) ; + virtual void _walk_pre(const_tree) ; +}; diff --git a/gcc/gimple-rewriter.c b/gcc/gimple-rewriter.c new file mode 100644 index 00000000000..31fcb74ed86 --- /dev/null +++ b/gcc/gimple-rewriter.c @@ -0,0 +1,268 @@ +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "options.h" +#include "cgraph.h" +#include "tree-pass.h" +#include "tree-cfg.h" +#include "tree-pretty-print.h" +#include "gimple-pretty-print.h" +#include "stringpool.h" //get_identifier +#include "basic-block.h" //needed for gimple.h +#include "function.h" //needed for gimple.h +#include "gimple.h" +#include "cfg.h" // needed for gimple-iterator.h +#include "gimple-iterator.h" +#include "stor-layout.h" // layout_type +#include "fold-const.h" //build_fold_addr_expr +#include "gimple-ssa.h" // update_stmt +#include "attribs.h" // decl_attributes +#include "gimplify.h" //unshare_expr +#include "value-range.h" // make_ssa_name dependency +#include "tree-ssanames.h" // make_ssa_name +#include "ssa.h" +#include "tree-into-ssa.h" +#include "gimple-rewriter.hpp" +#include "type-stringifier.hpp" + +void +GimpleTypeRewriter::_walk_pre(const_tree e) +{ + // This is for local variables + // and other declarations + exprTypeRewriter.walk(e); + bool _delete = exprTypeRewriter._delete; + exprTypeRewriter._delete = false; + // I don't think it is possible here (local variable delcarations and such); + gcc_assert(!_delete); + const bool is_interesting = exprTypeRewriter.is_interesting_type(TREE_TYPE(e)); + + /* + const bool is_ssa_name = TREE_CODE(e) == SSA_NAME; + if (is_ssa_name) + { + tree l = (tree) e; + if (SSA_NAME_VAR(l) == NULL_TREE) return; + if (TREE_TYPE(l) != TREE_TYPE(SSA_NAME_VAR(l))) { log("unequal\n"); } + + TREE_TYPE(l) = TREE_TYPE(SSA_NAME_VAR(l)); + TypeStringifier stringifier; + std::string name = stringifier.stringify(TREE_TYPE(l)); + log("new name %s\n", name.c_str()); + } + */ + + const bool is_var_decl = TREE_CODE(e) == VAR_DECL; + const bool is_valid = is_interesting && is_var_decl; + if (!is_valid) return; + relayout_decl((tree)e); +} + +void +GimpleTypeRewriter::_walk_pre(gimple *s) +{ +} + +void +GimpleTypeRewriter::_walk_pre(gcall *s) +{ +} + +void +GimpleTypeRewriter::_walk_pre(greturn *s) +{ + const_tree val = gimple_return_retval(s); + if (!val) return; + log("rewriting a return value\n"); + exprTypeRewriter.walk(val); + bool _delete = exprTypeRewriter._delete; + exprTypeRewriter._delete = false; + // We can't probably have a write in a return statement. + gcc_assert(!_delete); +} + +void +GimpleTypeRewriter::handle_pointer_arithmetic(gimple *s) +{ + const enum tree_code p = POINTER_PLUS_EXPR; + const enum tree_code d = POINTER_DIFF_EXPR; + const enum tree_code e = gimple_expr_code(s); + const bool is_pointer_plus = p == e; + const bool is_pointer_diff = d == e; + bool is_valid_input = is_pointer_plus != is_pointer_diff; + gcc_assert(is_valid_input); + // TODO: Implement pointer diff + + const enum gimple_rhs_class rhs_class = gimple_assign_rhs_class(s); + is_valid_input = GIMPLE_BINARY_RHS == rhs_class; + gcc_assert(is_valid_input); + + tree op_0 = gimple_assign_rhs1(s); + tree op_1 = gimple_assign_rhs2(s); + tree lhs = gimple_assign_lhs(s); + tree op_0_t = TREE_TYPE(op_0); + tree op_1_t = TREE_TYPE(op_1); + tree lhs_t = TREE_TYPE(lhs); + const bool is_op_0_t_interesting = exprTypeRewriter.is_interesting_type(op_0_t); + const bool is_op_1_t_interesting = exprTypeRewriter.is_interesting_type(op_1_t); + const bool is_lhs_t_interesting = exprTypeRewriter.is_interesting_type(lhs_t); + bool is_interesting_case = is_op_0_t_interesting || is_op_1_t_interesting || is_lhs_t_interesting; + TypeStringifier stringifier; + std::string name_0 = stringifier.stringify(op_0_t); + std::string name_1 = stringifier.stringify(op_1_t); + std::string name_l = stringifier.stringify(lhs_t); + log("is interesting case %s\n", is_interesting_case ? "t" : "f"); + log("op_0 %s\n", name_0.c_str()); + log("op_1 %s\n", name_1.c_str()); + log("lhs_t%s\n", name_l.c_str()); + if (!is_interesting_case) return; + + const enum tree_code op_1_code = TREE_CODE(op_1); + const enum tree_code op_0_code = TREE_CODE(op_0); + const bool is_op_0_icst = INTEGER_CST == op_0_code; + const bool is_op_1_icst = INTEGER_CST == op_1_code; + const bool is_constant_case = is_op_0_icst != is_op_1_icst; + if (!is_constant_case) + { + exprTypeRewriter.handle_pointer_arithmetic_nonconstant(s, op_0, op_1, is_pointer_plus); + bool _delete = exprTypeRewriter._delete; + exprTypeRewriter._delete = false; + // probably no deletion in pointer arithmetic... + gcc_assert(!_delete); + return; + } + + tree integer_constant = is_op_0_icst ? op_0 : op_1; + tree maybe_pointer = is_op_0_icst ? op_1 : op_0; + const_tree maybe_pointer_t = TREE_TYPE(maybe_pointer); + assert_is_type(maybe_pointer_t, POINTER_TYPE); + tree pointer_variable = maybe_pointer; + + exprTypeRewriter.handle_pointer_arithmetic_constants(s, pointer_variable, integer_constant, is_pointer_plus); + bool _delete = exprTypeRewriter._delete; + exprTypeRewriter._delete = false; + // probably no deletion in pointer arithmetic + gcc_assert(!_delete); +} + + +void +GimpleTypeRewriter::_walk_pre(gassign *s) +{ + const enum gimple_rhs_class code = gimple_assign_rhs_class(s); + + switch (code) + { + case GIMPLE_TERNARY_RHS: + { + const_tree rhs3 = gimple_assign_rhs3(s); + exprTypeRewriter.walk(rhs3); + } + /* fall-through */ + case GIMPLE_BINARY_RHS: + { + const_tree rhs2 = gimple_assign_rhs2(s); + exprTypeRewriter.walk(rhs2); + } + /* fall-through */ + case GIMPLE_UNARY_RHS: + case GIMPLE_SINGLE_RHS: + { + const_tree rhs1 = gimple_assign_rhs1(s); + exprTypeRewriter.walk(rhs1); + const_tree lhs = gimple_assign_lhs(s); + if (!lhs) break; + // Here is the only place where we likely can delete a statement. + exprTypeRewriter.walk(lhs); + bool _delete = exprTypeRewriter._delete; + exprTypeRewriter._delete = false; + if (_delete) + { + _deleted = true; + } + } + break; + default: + gcc_unreachable(); + break; + } + + + const enum tree_code e_code = gimple_expr_code(s); + log("is this the statment i'm looking for? %s\n", get_tree_code_name(e_code)); + print_gimple_stmt(dump_file, s, 0); + log("\n"); + switch (e_code) + { + case POINTER_PLUS_EXPR: + case POINTER_DIFF_EXPR: + handle_pointer_arithmetic(s); + break; + case COMPONENT_REF: + { + log("i am missing a component ref\n"); + print_gimple_stmt(dump_file, s, 0); + log("\n"); + + TypeStringifier stringifier; + tree e = gimple_assign_rhs1(s); + const_tree type = TREE_TYPE(e); + std::string name = stringifier.stringify(type); + log("%s\n", name.c_str()); + } + break; + case MULT_EXPR: + { + TypeStringifier stringifier; + tree op1 = gimple_assign_rhs2(s); + tree op2 = gimple_assign_rhs1(s); + tree op1_t = TREE_TYPE(op1); + tree op2_t = TREE_TYPE(op2); + std::string op1_s = stringifier.stringify(op1_t); + std::string op2_s = stringifier.stringify(op2_t); + log("multiplication\n"); + log("%s * %s\n", op1_s.c_str(), op2_s.c_str()); + } + break; + default: + { + log("missing %s\n", get_tree_code_name(e_code)); + } + break; + } + +} + +void +GimpleTypeRewriter::_walk_pre(gcond *s) +{ +} + +void +GimpleTypeRewriter::_rewrite_function_decl() +{ + // NOTE: It seems we only need to rewrite the return type + // for now... + cgraph_node *node = NULL; + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY(node) + { + node->get_untransformed_body(); + tree fndecl = node->decl; + gcc_assert(fndecl); + exprTypeRewriter.walk(fndecl); + tree decl = DECL_RESULT(fndecl); + if (decl) exprTypeRewriter.walk(decl); + } +} + +void +GimpleTypeRewriter::_walk_pre(gphi *s) +{ + unsigned n = gimple_phi_num_args (s); + for (unsigned i = 0; i < n; i++) + { + tree a = gimple_phi_arg_def(s, i); + exprTypeRewriter.walk(a); + } +} diff --git a/gcc/gimple-rewriter.hpp b/gcc/gimple-rewriter.hpp new file mode 100644 index 00000000000..03d5bfa21ad --- /dev/null +++ b/gcc/gimple-rewriter.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "gimple-walker.hpp" +#include "expr-rewriter.hpp" +#include "type-reconstructor.hpp" + +class GimpleTypeRewriter : public GimpleWalker +{ +public: + GimpleTypeRewriter(TypeReconstructor::reorg_record_map_t map, TypeReconstructor::reorg_field_map_t map2) : exprTypeRewriter(map, map2) {}; + void _rewrite_function_decl(); +private: + ExprTypeRewriter exprTypeRewriter; + void handle_pointer_arithmetic(gimple *s); + virtual void _walk_pre(gphi* ) final; + virtual void _walk_pre(const_tree) final; + virtual void _walk_pre(gimple*) final; + virtual void _walk_pre(gcall *s) final; + virtual void _walk_pre(greturn *s) final; + virtual void _walk_pre(gassign *s) final; + virtual void _walk_pre(gcond *s) final; +}; diff --git a/gcc/gimple-walker.c b/gcc/gimple-walker.c new file mode 100644 index 00000000000..1a858419c27 --- /dev/null +++ b/gcc/gimple-walker.c @@ -0,0 +1,257 @@ +#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 <map> + +#include "collect-types.h" +#include "type-stringifier.hpp" + +#include "type-collector.hpp" +#include "expr-walker.hpp" +#include "expr-collector.hpp" +#include "gimple-walker.hpp" +#include "tree-cfg.h" + +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); +} + +void +GimpleWalker::walk() +{ + _walk_globals(); + + std::set<tree> fndecls; + cgraph_node *node = NULL; + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY(node) + { + print_function(node); + node->get_untransformed_body(); + const bool already_in_set = fndecls.find(node->decl) != fndecls.end(); + if (already_in_set) continue; + _walk_cnode(node); + fndecls.insert(node->decl); + } +} + +void +GimpleWalker::_walk_globals() +{ + varpool_node *vnode = NULL; + FOR_EACH_VARIABLE(vnode) + { + _walk_global(vnode); + } +} + +void +GimpleWalker::_walk_global(varpool_node *vnode) +{ + gcc_assert(vnode); + struct ipa_ref *ref = NULL; + for (unsigned i = 0; vnode->iterate_referring(i, ref); i++) + { + tree var_decl = vnode->decl; + walk(var_decl); + } +} + +void +GimpleWalker::_walk_ssa_names(cgraph_node *cnode) +{ + const_tree decl = cnode->decl; + gcc_assert(decl); + function *func = DECL_STRUCT_FUNCTION(decl); + gcc_assert(func); + size_t i = 0; + tree ssa_name = NULL; + push_cfun(func); + FOR_EACH_SSA_NAME(i, ssa_name, cfun) + { + gcc_assert(ssa_name); + walk(ssa_name); + } + pop_cfun(); +} + +void +GimpleWalker::_walk_cnode(cgraph_node *cnode) +{ + gcc_assert(cnode); + _walk_decl(cnode); + _walk_locals(cnode); + _walk_ssa_names(cnode); + _walk_bb(cnode); +} + + +void +GimpleWalker::_walk_decl(cgraph_node *cnode) +{ + const_tree decl = cnode->decl; + gcc_assert(decl); + walk(decl); +} + +void +GimpleWalker::_walk_locals(cgraph_node *cnode) +{ + const_tree decl = cnode->decl; + gcc_assert(decl); + function *func = DECL_STRUCT_FUNCTION(decl); + gcc_assert(func); + int i = 0; + tree var_decl = NULL; + FOR_EACH_LOCAL_DECL(func, i, var_decl) + { + gcc_assert(var_decl); + walk(var_decl); + } +} + + +void +GimpleWalker::_walk_bb(cgraph_node* cnode) +{ + gcc_assert(cnode); + cnode->get_untransformed_body(); + const_tree decl = cnode->decl; + gcc_assert(decl); + function *func = DECL_STRUCT_FUNCTION(decl); + gcc_assert(func); + basic_block bb = NULL; + push_cfun(func); + FOR_EACH_BB_FN(bb, func) + { + _walk(bb); + } + pop_cfun(); +} + +void +GimpleWalker::_walk(basic_block bb) +{ + gcc_assert(bb); + bool first = true; + gimple_stmt_iterator gsi = gsi_start_bb(bb); + + while (!gsi_end_p(gsi)) + { + gimple *stmt = gsi_stmt(gsi); + walk(stmt); + if (_deleted) unlink_stmt_vdef (stmt); + if (_deleted) { gsi_remove(&gsi, true); } + else { gsi_next(&gsi); } + _deleted = false; + } + + + for (gimple_stmt_iterator gsi = gsi_start_phis(bb); !gsi_end_p(gsi); gsi_next(&gsi)) + { + gimple *stmt = gsi_stmt(gsi); + walk(stmt); + } +} + +void +GimpleWalker::walk(gimple *stmt) +{ + _walk_pre(stmt); + _walk(stmt); + _walk_post(stmt); +} + +void +GimpleWalker::_walk(gimple *stmt) +{ + gcc_assert(stmt); + +#define GimpleWalkerWalk(type) \ + if (type s = dyn_cast< type >(stmt)) \ + { \ + _walk_pre(stmt); \ + walk(s); \ + _walk_post(stmt); \ + return; \ + } + + GimpleWalkerWalk(gassign*); + GimpleWalkerWalk(greturn*); + GimpleWalkerWalk(gcond*); + GimpleWalkerWalk(gcall*); + GimpleWalkerWalk(glabel*); + GimpleWalkerWalk(gswitch*); + GimpleWalkerWalk(gphi*); + + + const enum gimple_code code = gimple_code (stmt); + switch (code) + { + case GIMPLE_PREDICT: return; + case GIMPLE_DEBUG: return; + default: break; + } + const char* name = gimple_code_name[code]; + log("gimple code name %s\n", name); + gcc_unreachable(); +} + +#define GimpleWalkerFuncDef(type) \ +void \ +GimpleWalker::walk (type e) \ +{ \ + _walk_pre (e); \ + _walk (e); \ + _walk_post (e); \ +} \ +\ +void \ +GimpleWalker::_walk (__attribute__((unused)) type e) \ +{ \ +} + +GimpleWalkerFuncDef(const_tree) +GimpleWalkerFuncDef(gassign *) +GimpleWalkerFuncDef(greturn *) +GimpleWalkerFuncDef(gcond *) +GimpleWalkerFuncDef(gcall *) +GimpleWalkerFuncDef(glabel *) +GimpleWalkerFuncDef(gswitch *) +GimpleWalkerFuncDef(gphi *) + diff --git a/gcc/gimple-walker.hpp b/gcc/gimple-walker.hpp new file mode 100644 index 00000000000..6c3742afb0b --- /dev/null +++ b/gcc/gimple-walker.hpp @@ -0,0 +1,69 @@ +#pragma once + +#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 <set> + +class GimpleWalker +{ +public: + GimpleWalker() : _deleted(false) {}; + void walk(); + +protected: + + bool _deleted; + virtual void _walk_global(varpool_node*); + void _walk_globals(); + void _walk_ssa_names(cgraph_node *cnode); + void _walk_cnode(cgraph_node *cnode); + void _walk_decl(cgraph_node *cnode); + void _walk_locals(cgraph_node *cnode); + void _walk_bb(cgraph_node *cnode); + void _walk(basic_block bb); + +#define GimpleWalkerFuncDecl(type) \ + virtual void _walk_pre(type stmt) {}; \ + void walk(type stmt); \ + void _walk(type stmt); \ + virtual void _walk_post(type stmt) {} + + GimpleWalkerFuncDecl(const_tree); + GimpleWalkerFuncDecl(gimple*); + GimpleWalkerFuncDecl(gassign*); + GimpleWalkerFuncDecl(greturn*); + GimpleWalkerFuncDecl(gcond*); + GimpleWalkerFuncDecl(gcall*); + GimpleWalkerFuncDecl(glabel*); + GimpleWalkerFuncDecl(gswitch*); + GimpleWalkerFuncDecl(gphi*); +}; + diff --git a/gcc/gimple.h b/gcc/gimple.h index 6cc7e66059d..ea5e6100741 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -6397,6 +6397,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 * @@ -6405,15 +6422,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-prototype.c b/gcc/ipa-prototype.c new file mode 100644 index 00000000000..76d5b223900 --- /dev/null +++ b/gcc/ipa-prototype.c @@ -0,0 +1,330 @@ +#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" // needed for gimple-iterator.h +#include "gimple-iterator.h" +#include "gimple-ssa.h" +#include <stdbool.h> + + +#include "types-inlines.h" +#include "ipa-prototype.h" +#include "type-collector.hpp" +#include "type-stringifier.hpp" +#include <map> +#include <vector> +#include "type-escaper.hpp" +#include "expr-escaper.hpp" +#include "gimple-walker.hpp" +#include "gimple-collector.hpp" +#include "gimple-escaper.hpp" +#include "type-structural-equality.hpp" +#include "type-structural-main-variant.hpp" +#include "type-incomplete-equality.hpp" + +//#define OPTIMIZED +#define SANITY_CHECKS + +typedef std::set<const_tree> undefset; + +void +Reason::print() const +{ + log("e=%d g=%d p=%d r=%d c=%d v=%d u=%d i=%d\n", this->is_escaping(), this->global_is_visible, this->parameter_is_visible, this->return_is_visible, this->type_is_casted, this->type_is_volatile, this->type_is_in_union, this->is_indirect); +} + +Reason +Reason::operator|(const Reason &other) +{ + Reason retval {}; + retval.global_is_visible = this->global_is_visible | other.global_is_visible; + retval.parameter_is_visible = this->parameter_is_visible | other.parameter_is_visible; + retval.return_is_visible = this->return_is_visible | other.return_is_visible; + retval.type_is_casted = this->type_is_casted | other.type_is_casted; + retval.type_is_volatile = this->type_is_volatile | other.type_is_volatile; + retval.type_is_in_union = this->type_is_in_union | other.type_is_in_union; + retval.is_indirect = this->is_indirect | other.is_indirect; + return retval; +} + +Reason& +Reason::operator|=(const Reason &other) +{ + this->global_is_visible |= other.global_is_visible; + this->parameter_is_visible |= other.parameter_is_visible; + this->return_is_visible |= other.return_is_visible; + this->type_is_casted |= other.type_is_casted; + this->type_is_volatile |= other.type_is_volatile; + this->type_is_in_union |= other.type_is_in_union; + this->is_indirect |= other.is_indirect; + return *this; +} + + + +static inline void +assert_type_is_in_ptrset(const_tree type, ptrset_t &types) +{ +#ifdef SANITY_CHECKS + gcc_assert(types.in_points_to_record(type)); +#endif +} + +static inline void +assert_type_is_in_universe(const_tree type, ptrset_t &types) +{ +#ifdef SANITY_CHECKS + gcc_assert(types.in_universe(type)); +#endif +} + +static bool +is_variable_escaping(varpool_node *vnode) +{ + gcc_assert(vnode); + return vnode->externally_visible; +} + +static void +place_escaping_types_in_set(ptrset_t &types, typemap &calc) +{ + for (auto i = calc.cbegin(), e = calc.cend(); i != e; ++i) + { + const_tree type = i->first; + // We should have seen it before + assert_type_is_in_universe(type, types); + + // We should only track interesting types + // Types which are not in points_to_record are the ones + // that are pointed to by records. + // I think it is possible to prune them ahead of time... + if (!types.in_points_to_record(type)) continue; + + const Reason reason = i->second; + reason.is_escaping() ? types.escaping.insert(type) : types.non_escaping.insert(type); + } +} + +static void +sanity_check_escape_xor_not(ptrset_t &types) +{ + for (auto i = types.escaping.cbegin(), e = types.escaping.cend(); i != e; ++i) + { + for (auto j = types.non_escaping.cbegin(), f = types.non_escaping.cend(); j != f; ++j) + { + const_tree type_esc = *i; + gcc_assert(type_esc); + const_tree type_non = *j; + gcc_assert(type_non); + //const bool valid_sets = !eq_type_compare(type_esc, type_non); + //if (valid_sets) continue; + //log("comparing %s == %s\n", type_to_string(type_esc).c_str(), type_to_string(type_non).c_str()); + //TODO: Remove this comment once we have restricted the "repairing" of sets a bit more. + //gcc_assert(valid_sets); + } + } +} + +static void +sanity_check_escape_union_not_equals_ptrset(ptrset_t &types) +{ + typeset _union; + for (auto i = types.escaping.cbegin(), e = types.escaping.cend(); i != e; ++i) + { + const_tree type = *i; + _union.insert(type); + } + + for (auto i = types.non_escaping.cbegin(), e = types.non_escaping.cend(); i != e; ++i) + { + const_tree type = *i; + _union.insert(type); + } + + + for (auto i = types.points_to_record.cbegin(), e = types.points_to_record.cend(); i != e; ++i) + { + const_tree type = *i; + const bool in_union = _union.find(type) != _union.end(); + if (in_union) continue; + //log("this type was not found in union %s\n", type_to_string(type).c_str()); + //TODO: FIXME: This has to be enabled for the sanity check to work + //But at the moment there's one type which isn't working correctly :( + gcc_unreachable(); + } + +} + +static void +fix_escaping_types_in_set(ptrset_t &types) +{ + bool fixed_point_reached = false; + TypeIncompleteEquality structuralEquality; + TypeStringifier stringifier; + do { + std::vector<const_tree> fixes; + fixed_point_reached = true; + for (auto i = types.escaping.cbegin(), e = types.escaping.cend(); i != e; ++i) + { + for (auto j = types.non_escaping.cbegin(), f = types.non_escaping.cend(); j != f; ++j) + { + const_tree type_esc = *i; + gcc_assert(type_esc); + const_tree type_non = *j; + gcc_assert(type_non); + // There can be cases where incomplete types are marked as non-escaping + // and complete types counter parts are marked as escaping. + //const bool interesting_case = eq_type_compare(type_esc, type_non); + //TODO: We are going to need a different type comparison because this one + //fails to take into account the recursion... + TypeStringifier stringifier; + std::string type_esc_name = TypeStringifier::get_type_identifier(type_esc); + std::string type_non_name = TypeStringifier::get_type_identifier(type_non); + //std::string file("FILE"); + //bool i_care = type_esc_name.compare(file) == 0; + //i_care &= type_non_name.compare(file) == 0; + + type_esc_name = stringifier.stringify(type_esc); + type_non_name = stringifier.stringify(type_non); + + const bool equal = structuralEquality.equal(type_esc, type_non); + if (!equal) continue; + + log("recalulating %s == %s\n", type_esc_name.c_str(), type_non_name.c_str()); + fixed_point_reached = false; + // Add incomplete to escaping + // delete incomplete from non_escaping + // We shouldn't do that inside our iteration loop. + fixes.push_back(type_non); + } + } + + for (auto i = fixes.cbegin(), e = fixes.cend(); i != e; ++i) + { + const_tree escaping_type = *i; + types.escaping.insert(escaping_type); + types.non_escaping.erase(escaping_type); + } + } while (!fixed_point_reached); +} + +static void +print_escaping_types_in_set(ptrset_t &types) +{ + std::vector<const_tree> fixes; + for (auto i = types.non_escaping.cbegin(), e = types.non_escaping.cend(); i != e; ++i) + { + const_tree type_non = *i; + gcc_assert(type_non); + const enum tree_code code = TREE_CODE(type_non); + const bool is_function = FUNCTION_TYPE == code; + // I just don't want to print out functions. + if (is_function) continue; + TypeStringifier stringifier; + std::string name = stringifier.stringify(type_non); + log("non_escaping: %s \n", name.c_str()); + } + +} + +static void +print_sequal_types(ptrset_t &types) +{ + std::vector<const_tree> fixes; + TypeStructuralEquality structuralEquality; + TypeStringifier stringifier; + for (auto i = types.universe.cbegin(), e = types.universe.cend(); i != e; ++i) + { + for (auto j = types.universe.cbegin(), e = types.universe.cend(); j != e; ++j) + { + const_tree t_i = *i; + const_tree t_j = *j; + const bool eq = structuralEquality.equal(t_i, t_j); + std::string n_i = stringifier.stringify(t_i); + std::string n_j = stringifier.stringify(t_j); + log("%s = %s == %s\n", eq ? "t" : "f", n_i.c_str(), n_j.c_str()); + } + } +} + + +static unsigned int +iphw_execute() +{ + + GimpleTypeCollector collector; + collector.walk(); + ptrset_t types = collector.get_pointer_set(); + + GimpleEscaper gimpleEscaper(types); + gimpleEscaper.walk(); + + typemap eacalc; // Escape Analysis Calculation + // Intermediate results + // Do not read escape analysis results from here + //calculate_escaping_types(types, eacalc); + //print_sequal_types(types); + place_escaping_types_in_set(types, gimpleEscaper.exprEscaper.typeEscaper.calc); + fix_escaping_types_in_set(types); + // -fipa-protytpe -fdump-ipa-prototype + print_escaping_types_in_set(types); + sanity_check_escape_xor_not(types); + sanity_check_escape_union_not_equals_ptrset(types); + gcc_unreachable(); + return 0; +} + +namespace { +const pass_data pass_data_ipa_prototype = +{ + SIMPLE_IPA_PASS, + "prototype", + OPTGROUP_NONE, + TV_NONE, + (PROP_cfg | PROP_ssa), + 0, + 0, + 0, + 0, +}; + +class pass_ipa_prototype : public simple_ipa_opt_pass +{ +public: + pass_ipa_prototype (gcc::context *ctx) + : simple_ipa_opt_pass(pass_data_ipa_prototype, ctx) + {} + + virtual bool gate(function*) { return flag_ipa_prototype; } + virtual unsigned execute (function*) { return iphw_execute(); } +}; +} // anon namespace + +simple_ipa_opt_pass* +make_pass_ipa_prototype (gcc::context *ctx) +{ + return new pass_ipa_prototype (ctx); +} diff --git a/gcc/ipa-prototype.h b/gcc/ipa-prototype.h new file mode 100644 index 00000000000..f77e0de3b32 --- /dev/null +++ b/gcc/ipa-prototype.h @@ -0,0 +1,60 @@ +#pragma once + +#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 <map> + +struct Reason { + inline bool is_escaping() const { + return this->global_is_visible + || this->parameter_is_visible + || this->return_is_visible + || this->type_is_casted + || this->type_is_volatile + || this->type_is_in_union + || this->is_indirect; + } + bool global_is_visible : 1; + bool parameter_is_visible : 1; + bool return_is_visible : 1; + bool type_is_casted : 1; + bool type_is_volatile : 1; + bool type_is_in_union : 1; + bool is_indirect : 1; + Reason operator|(const Reason &); + Reason& operator|=(const Reason &); + void print() const; + Reason() : global_is_visible(0), parameter_is_visible(0), return_is_visible(0), type_is_casted(0), type_is_volatile(0), type_is_in_union(0), is_indirect(0) {}; +}; + + +typedef std::map<const_tree, Reason> typemap; + diff --git a/gcc/ipa-str-reorg-dead-field-eliminate.c b/gcc/ipa-str-reorg-dead-field-eliminate.c new file mode 100644 index 00000000000..f918974f719 --- /dev/null +++ b/gcc/ipa-str-reorg-dead-field-eliminate.c @@ -0,0 +1,2783 @@ +/* Interprocedural scalar replacement of aggregates + Copyright (C) 2019-2020 Free Software Foundation, Inc. + + Contributed by Erick Ochoa <erick.ochoa@theobroma-systems.com> + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "options.h" +#include "cgraph.h" +#include "tree-pass.h" +#include "tree-cfg.h" +#include "tree-pretty-print.h" +#include "gimple-pretty-print.h" +#include "stringpool.h" //get_identifier +#include "basic-block.h" //needed for gimple.h +#include "function.h" //needed for gimple.h +#include "gimple.h" +#include "cfg.h" // needed for gimple-iterator.h +#include "gimple-iterator.h" +#include "stor-layout.h" // layout_type +#include "fold-const.h" //build_fold_addr_expr +#include "gimple-ssa.h" // update_stmt +#include "attribs.h" // decl_attributes +#include "gimplify.h" //unshare_expr +#include "value-range.h" // make_ssa_name dependency +#include "tree-ssanames.h" // make_ssa_name +#include "ssa.h" +#include "tree-into-ssa.h" +#include <vector> // needed for ipa-structure-reorg +#include <map> +#include <set> +#include "ipa-structure-reorg.h" +#include "ipa-utils.h" +#include "ipa-str-reorg-utils.h" + +#include "gimple-caster.hpp" + +#define test_write(M, ...) \ + if (dump_file) \ + { \ + fprintf (dump_file, M, ##__VA_ARGS__); \ + } + +#define test_log(M, indent, ...) \ + if (dump_file) \ + { \ + fprintf (dump_file, "%*c" M "\n", indent, ' ', ##__VA_ARGS__); \ + } + + +/* +static void +log_expr_prologue (const int indent, const char *debug_str, tree expr) +{ + const char *expr_str = print_generic_expr_to_str (expr); + test_log ("<%s \"%s\">", indent, debug_str, expr_str); + const_tree type = TREE_TYPE (expr); + if (TREE_CODE (type) == ARRAY_TYPE) + { + const_tree domain = TYPE_DOMAIN (type); + const_tree min = TYPE_MIN_VALUE (domain); + const_tree max = TYPE_MAX_VALUE (domain); + int _min = tree_to_uhwi (min); + int _max = tree_to_uhwi (max); + test_log ("< domain = (%d,%d)>", indent, _min, _max); + } + test_log ("< type = %s>", indent, get_type_name (type)); +} + +static tree +const_to_tree (const_tree ctree) +{ + return (tree) ctree; +} + +static void +substitute_type (tree expr, const_tree new_type) +{ + gcc_assert(expr); + gcc_assert(new_type); + const_tree old_type = TREE_TYPE(expr); + TREE_TYPE(expr) = const_to_tree(new_type); +} + +static void +substitute_type (tree expr, const_tree* new_type_ptr) +{ + gcc_assert(new_type_ptr); + substitute_type(expr, *new_type_ptr); +} + +// This function should be hidden +static const_tree +get_base_type_from_ptr_or_arr_var (const_tree var, + unsigned int &indirection_level) +{ + tree ptr_or_array = TREE_TYPE (var); + const bool is_array = TREE_CODE (ptr_or_array) == ARRAY_TYPE; + const bool is_ptr = TREE_CODE (ptr_or_array) == POINTER_TYPE; + const bool is_array_or_ptr = is_array || is_ptr; + gcc_assert (is_array_or_ptr); + const_tree retval + = get_base_type_from_ptr_or_arr_type (ptr_or_array, indirection_level); + return retval; +} + +static const_tree __attribute__((unused)) +get_base_type_from_array_var (const_tree var, unsigned int &indirection_level) +{ + gcc_assert (TREE_CODE (var) != ARRAY_TYPE); + tree array_type = TREE_TYPE (var); + gcc_assert (TREE_CODE (array_type) == ARRAY_TYPE); + const_tree retval + = get_base_type_from_ptr_or_arr_var (array_type, indirection_level); + return retval; +} + +static const_tree +get_base_type_from_pointer_var (const_tree var, unsigned int &indirection_level) +{ + gcc_assert (TREE_CODE (var) != POINTER_TYPE); + tree pointer_type = TREE_TYPE (var); + gcc_assert (TREE_CODE (pointer_type) == POINTER_TYPE); + const_tree retval + = get_base_type_from_ptr_or_arr_type (pointer_type, indirection_level); + return retval; +} + +static const_tree __attribute__((unused)) +get_base_type_from_pointer_var (const_tree var) +{ + gcc_assert (TREE_CODE (var) != POINTER_TYPE); + unsigned int indirection_level; + return get_base_type_from_pointer_var (var, indirection_level); +} + +// INFO: cannot change +// tree expr to const_tree expr +static void +log_expr_epilogue (const int indent, const char *debug_str, tree expr) +{ + const char *expr_str = print_generic_expr_to_str (expr); + const_tree type = TREE_TYPE (expr); + if (TREE_CODE (type) == ARRAY_TYPE) + { + const_tree domain = TYPE_DOMAIN (type); + const_tree min = TYPE_MIN_VALUE (domain); + const_tree max = TYPE_MAX_VALUE (domain); + int _min = tree_to_uhwi (min); + int _max = tree_to_uhwi (max); + test_log ("< domain = (%d,%d)>", indent, _min, _max); + } + test_log ("< type = %s>", indent, get_type_name (type)); + test_log ("</%s \"%s\">", indent, debug_str, expr_str); +} + + + +static const char * +make_base_name_based_on_old (const char *old_name) +{ + gcc_assert (old_name); + char *ptr; + static const char *suffix = ".reorg"; + int new_size = strlen (old_name) + strlen (suffix); + int retval = asprintf (&ptr, "%s%s", old_name, suffix); + gcc_assert (retval == new_size); + return ptr; +} + +static const char * +make_base_name_based_on_old (const_tree base_type) +{ + enum tree_code base_code = TREE_CODE (base_type); + const bool is_pointer = POINTER_TYPE == base_code; + const bool is_array = ARRAY_TYPE == base_code; + const bool prohibited = is_pointer || is_array; + // This means that this is only for base types + // and structs. + // I.e. we don't want to generate names which have + //[][][] at the end or **** + gcc_assert (!prohibited); + const char *base_type_name = get_type_name (base_type); + return make_base_name_based_on_old (base_type_name); +} + +static const char * +make_record_name_based_on_old (const char *old_record_name) +{ + return make_base_name_based_on_old (old_record_name); +} + +static const char * +make_array_name_based_on_old (const_tree array) +{ + enum tree_code code = TREE_CODE (array); + gcc_assert (ARRAY_TYPE == code); + unsigned int indirection_level; + const_tree base_type + = get_base_type_from_array_type (array, indirection_level); + const char *reorged_base_type_name = make_base_name_based_on_old (base_type); + const char *suffix = make_array_postfix (indirection_level); + return make_pointer_or_array_name (reorged_base_type_name, suffix); +} + +static const char * +make_pointer_name_based_on_old (const_tree pointer) +{ + enum tree_code code = TREE_CODE (pointer); + gcc_assert (POINTER_TYPE == code); + unsigned int indirection_level; + const_tree base_type + = get_base_type_from_pointer_type (pointer, indirection_level); + const char *reorged_base_type_name = make_base_name_based_on_old (base_type); + const char *suffix = make_pointer_postfix (indirection_level); + return make_pointer_or_array_name (reorged_base_type_name, suffix); +} + +static const char * +make_record_name_based_on_old (const_tree record) +{ + enum tree_code code = TREE_CODE (record); + gcc_assert (RECORD_TYPE == code); + const char *old_name = get_type_name (record); + return make_record_name_based_on_old (old_name); +} + + +typedef hash_set<const_tree> field_set; +typedef hash_set<const_tree> tree_set; +struct delete_info { const_tree old_record; const_tree new_record; field_set* deleted_fields;}; +typedef struct delete_info delete_info_t; +typedef hash_map<const_tree, delete_info_t> t_map; + +static bool +rewrite_addr_expr_def (tree expr, t_map &type_map, + const int indent); +static bool +rewrite_addr_expr (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_addr_expr", expr); + bool retval = rewrite_addr_expr_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_addr_expr", expr); + return retval; +} + +static bool +rewrite_array_ref_def (tree expr, t_map &type_map, + const int indent); + +static bool +rewrite_array_ref (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_array_ref", expr); + bool retval = rewrite_array_ref_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_array_ref", expr); + return retval; +} + +static bool +rewrite_bin_expr_default_def (tree expr, + t_map &type_map, + const int indent); +static bool +rewrite_bin_expr_default (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_bin_expr", expr); + bool retval = rewrite_bin_expr_default_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_bin_expr", expr); + return retval; +} + +static bool +rewrite_constructor_def (tree expr, t_map &type_map, + const int indent); +static bool +rewrite_constructor (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_constructor", expr); + bool retval = rewrite_constructor_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_constructor", expr); + return retval; +} + +static bool +rewrite_field_decl_def (tree expr, t_map &type_map, + const int indent); +static bool +rewrite_field_decl (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_field_decl", expr); + bool retval = rewrite_field_decl_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_field_decl", expr); + return retval; +} + +static bool +rewrite_integer_cst_def (tree expr, t_map &type_map, + const int indent); +static bool +rewrite_integer_cst (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_integer_cst", expr); + bool retval = rewrite_integer_cst_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_integer_cst", expr); + return retval; +} + +static bool +rewrite_function_decl_def (tree expr, + t_map &type_map, + const int indent); +static bool +rewrite_function_decl (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_function_decl", expr); + bool retval = rewrite_function_decl_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_function_decl", expr); + return retval; +} + +static bool +rewrite_component_ref (tree expr, t_map &type_map, + const int indent); +static bool +rewrite_component_ref_def (tree expr, + t_map &type_map, + const int indent); +static bool +rewrite_component_ref (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_component_ref", expr); + bool retval = rewrite_component_ref_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_component_ref", expr); + return retval; +} + +static bool +rewrite_expr (tree expr, t_map &type_map, + const int indent); +static bool +rewrite_expr_def (tree expr, t_map &type_map, + const int indent); +static bool +rewrite_expr (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_expr", expr); + bool retval = rewrite_expr_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_expr", expr); + return retval; +} + +static bool +rewrite_mem_ref_def (tree expr, t_map &type_map, + const int indent); + +static bool +rewrite_mem_ref (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_mem_ref", expr); + bool retval = rewrite_mem_ref_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_mem_ref", expr); + return retval; +} + + +static bool +rewrite_ssa_name_def (tree expr, t_map &type_map, + const int indent); +static bool +rewrite_ssa_name (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_ssa_name", expr); + bool retval = rewrite_ssa_name_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_ssa_name", expr); + return retval; +} + +static bool +rewrite_var_decl_def (tree expr, t_map &type_map, + const int indent); +static bool +rewrite_var_decl (tree expr, t_map &type_map, + const int indent) +{ + log_expr_prologue (indent, "rewrite_var_decl", expr); + bool retval = rewrite_var_decl_def (expr, type_map, indent + 4); + log_expr_epilogue (indent, "rewrite_var_decl", expr); + return retval; +} + +static bool +rewrite_expr_def (tree expr, t_map &type_map, + const int indent) +{ + bool retval = false; + switch (TREE_CODE (expr)) + { + case ADDR_EXPR: + retval = rewrite_addr_expr (expr, type_map, indent); + break; + case ARRAY_REF: + retval = rewrite_array_ref (expr, type_map, indent); + break; + case COMPONENT_REF: + retval = rewrite_component_ref (expr, type_map, indent); + break; + case CONSTRUCTOR: + retval = rewrite_constructor (expr, type_map, indent); + break; + case FIELD_DECL: + retval = rewrite_field_decl (expr, type_map, indent); + break; + case FUNCTION_DECL: + retval = rewrite_function_decl (expr, type_map, indent); + break; + case INTEGER_CST: + retval = rewrite_integer_cst (expr, type_map, indent); + break; + case MEM_REF: + retval = rewrite_mem_ref (expr, type_map, indent); + break; + case MINUS_EXPR: + retval = rewrite_bin_expr_default (expr, type_map, indent); + break; + case MULT_EXPR: + retval = rewrite_bin_expr_default (expr, type_map, indent); + break; + case POINTER_DIFF_EXPR: + // not needed + break; + case POINTER_PLUS_EXPR: + // not needed + break; + case PLUS_EXPR: + // not needed + break; + case SSA_NAME: + retval = rewrite_ssa_name (expr, type_map, indent); + break; + case VAR_DECL: + retval = rewrite_var_decl (expr, type_map, indent); + break; + default: + { + //TODO: can we put an unreachable here? + enum tree_code code = TREE_CODE (expr); + test_log ("code = %s", indent, get_tree_code_name (code)); + delete_info_t *delete_info = type_map.get (TREE_TYPE(expr)); + const_tree *new_type_ptr = delete_info ? &(delete_info->new_record) : NULL; + if (!new_type_ptr) + return false; + retval = true; + substitute_type(expr, new_type_ptr); + } + break; + } + + return retval; +} + +inline static void +test_print_generic_decl (tree decl) +{ + if (!dump_file) + return; + print_generic_decl (dump_file, decl, TDF_DETAILS); +} + +inline static void +test_print_gimple_stmt (gimple *stmt) +{ + if (!dump_file) + return; + print_gimple_stmt (dump_file, stmt, 0, TDF_NONE); +} + +inline static void +print_function (cgraph_node *cnode) +{ + if (!dump_file) + return; + gcc_assert (cnode); + cnode->get_untransformed_body (); + dump_function_to_file (cnode->decl, dump_file, TDF_NONE); +} + +static unsigned +get_field_offset (const_tree field) +{ + gcc_assert (TREE_CODE (field) == FIELD_DECL); + tree cst = byte_position (field); + unsigned offset = tree_to_uhwi (cst); + return offset; +} + + +static const_tree +get_field_with_offset_unsafe (const_tree record, const unsigned offset) +{ + gcc_assert (record); + gcc_assert (TREE_CODE (record) == RECORD_TYPE); + for (tree field = TYPE_FIELDS (record); field; field = DECL_CHAIN (field)) + { + unsigned field_offset = get_field_offset (field); + if (field_offset == offset) + return field; + } + + return NULL; +} + +static const_tree __attribute__((unused)) +get_field_with_offset (const_tree record, const unsigned offset) +{ + const_tree retval = get_field_with_offset_unsafe (record, offset); + gcc_assert (retval != NULL); + return retval; +} + +static const_tree +get_field_with_name_unsafe (const_tree record, const char *identifier) +{ + gcc_assert (record); + gcc_assert (TREE_CODE (record) == RECORD_TYPE); + for (tree field = TYPE_FIELDS (record); field; field = DECL_CHAIN (field)) + { + const char *field_identifier = get_field_name (field); + const bool is_same_name = strcmp (field_identifier, identifier) == 0; + if (!is_same_name) + continue; + + gcc_assert (TREE_CODE (field) == FIELD_DECL); + return field; + } + return NULL; +} + +static const_tree +get_field_with_name (const_tree record, const char *identifier) +{ + const_tree retval = get_field_with_name_unsafe (record, identifier); + gcc_assert (retval != NULL); + return retval; +} + +static bool +filter_out_boring_type (const_tree type, hash_set<const_tree> &map); + +inline static void +is_record_p(const_tree record) +{ + gcc_assert(record); + const enum tree_code code = TREE_CODE(record); + const bool is_record = RECORD_TYPE == code; + gcc_assert(is_record); +} + +struct cstrless { + bool operator()(const char* a, const char* b) { return strcmp(a, b) < 0; } +}; +std::set<const char*, cstrless> interesting_records; +std::set<const char*, cstrless> interesting_fields; +std::set<const char*, cstrless> record_field_pair_name; +std::set<const char*, cstrless> updated_functions; + +static bool +is_interesting_struct_escape_analysis(const_tree record) +{ + is_record_p(record); + const_tree main_variant = TYPE_MAIN_VARIANT(record); + is_record_p(main_variant); + const char* const ob_type_name = get_type_name(main_variant); + const char* blacklist [5] = { "__gcov_fn_info", "__gcov_ctr_info", "__gcov_fn_info", "__gcov_info", "indirect_call_tuple" }; + //const char* whitelist [4] = { "arc", "arc_t", "node", "node_t" }; + + for (int i = 0; i < 5; i++) + { + const bool is_blacklisted = strcmp(blacklist[i], ob_type_name) == 0; + if (is_blacklisted) return false; + } + + */ + /* + bool is_whitelisted = false; + for (int i = 0; i < 4; i++) + { + is_whitelisted |= strcmp(whitelist[i], ob_type_name) == 0; + } + if (!is_whitelisted) return false; + */ + + /* + + bool in_set = +#if __cplusplus > 201703L + interesting_records.contains(get_type_name(main_variant)) +#else + interesting_records.find(get_type_name(main_variant)) != interesting_records.end() +#endif + ; + + in_set |= +#if __cplusplus > 201703L + interesting_records.contains(get_type_name(record)) +#else + interesting_records.find(get_type_name(record)) != interesting_records.end() +#endif + ; + + + log("is interesting struct ? %s\n", in_set ? "true" : "false"); + + return in_set; + +} + +static bool is_interesting_struct_manual(const_tree); + +static bool +is_interesting_struct (const_tree record_1) +{ + const bool use_escape_analysis_info = !flag_ipa_typelist_struct && !flag_ipa_typelist_field; + return use_escape_analysis_info ? is_interesting_struct_escape_analysis(record_1) : is_interesting_struct_manual(record_1); +} + +static bool +is_interesting_pair (const char* pair_name) +{ + const bool use_escape_analysis_info = !flag_ipa_typelist_struct && !flag_ipa_typelist_field; + //TODO: If we are not in escape analysis we shouldn't be calling this function. + //We need to define a better interface for specifying record type and name to avoid + //having this problem. + if (!use_escape_analysis_info) return true; + const bool in_set = +#if __cplusplus > 201703L + record_field_pair_name.contains(pair_name); +#else + record_field_pair_name.find(pair_name) != record_field_pair_name.end() +#endif + ; + + log("is interesting pair? %s\n", in_set ? "true" : "false"); + + return in_set; +} + +static bool +is_interesting_struct_manual(const_tree record_1) +{ + is_record_p(record_1); + + const_tree record = TYPE_MAIN_VARIANT(record_1); + is_record_p(record); + + const char *record_name = get_type_name (record); + const int buffer_size = 1024; + char interesting_structs[buffer_size]; + static const int cli_length = strlen (flag_ipa_typelist_struct); + gcc_assert (cli_length < buffer_size); + strcpy (interesting_structs, flag_ipa_typelist_struct); + bool retval = false; + char *save_ptr; + char *token = strtok_r (interesting_structs, ",", &save_ptr); + while (token) + { + retval |= strcmp (record_name, token) == 0; + token = strtok_r (NULL, ",", &save_ptr); + if (retval) break; + } + + + return retval; +} + + +inline static void +is_field_decl_p(const_tree field) +{ + gcc_assert(field); + const enum tree_code code = TREE_CODE(field); + const bool is_field = FIELD_DECL == code; + gcc_assert(is_field); +} + +static bool +is_interesting_field_manual (const_tree field) +{ + log ("HERE HERE %s\n", get_type_name(field)); + is_field_decl_p(field); + + const char *field_name = get_field_name (field); + const int buffer_size = 1024; + char interesting_fields[buffer_size]; + static const int cli_length = strlen (flag_ipa_typelist_field); + gcc_assert (cli_length < buffer_size); + strcpy (interesting_fields, flag_ipa_typelist_field); + bool retval = false; + char *save_ptr; + char *token = strtok_r (interesting_fields, ",", &save_ptr); + while (token) + { + retval |= strcmp (field_name, token) == 0; + token = strtok_r (NULL, ",", &save_ptr); + } + return retval; +} + +static bool +is_interesting_field_escape_analysis(const_tree field) +{ + is_field_decl_p(field); + + const char* field_name = get_field_name(field); + */ + /* + const char* whitelist [2] = { "nextin", "nextout" }; + + bool is_whitelisted = false; + for (int i = 0; i < 2; i++) + { + is_whitelisted |= strcmp(whitelist[i], field_name) == 0; + } + if (!is_whitelisted) return false; + */ + + /* + const bool in_set = +#if __cplusplus > 201703L + interesting_fields.contains(field_name); +#else + interesting_fields.find(field_name) != interesting_fields.end() +#endif + ; + + log("is interesting field? %s\n", in_set ? "true" : "false"); + + return in_set; +} + +static bool +is_interesting_field (const_tree field) +{ + const bool use_escape_analysis_info = !flag_ipa_typelist_struct && !flag_ipa_typelist_field; + return use_escape_analysis_info ? is_interesting_field_escape_analysis(field) : is_interesting_field_manual(field); +} + +static bool +filter_out_boring_record (const_tree record, hash_set<const_tree> &map) +{ + test_log ("filter_out_boring_record", 0); + enum tree_code code = TREE_CODE (record); + gcc_assert (code == RECORD_TYPE); + + for (tree field = TYPE_FIELDS (record); field; field = DECL_CHAIN (field)) + { + tree field_type = TREE_TYPE (field); + enum tree_code field_code = TREE_CODE (field); + test_log ("filter_out_boring_field %s %s", 0, get_type_name (field_type), + get_tree_code_name (field_code)); + bool is_boring = filter_out_boring_type (field_type, map); + if (!is_boring) + return false; + } + + return !is_interesting_struct (record); +} + +static bool +filter_out_boring_pointer (const_tree pointer, hash_set<const_tree> &map) +{ + test_log ("filter_out_boring_pointer", 0); + enum tree_code code = TREE_CODE (pointer); + gcc_assert (code == POINTER_TYPE); + const_tree base_type = get_base_type_from_pointer_type (pointer); + return filter_out_boring_type (base_type, map); +} + +static bool +filter_out_boring_array (const_tree array, hash_set<const_tree> &map) +{ + enum tree_code code = TREE_CODE (array); + gcc_assert (code == ARRAY_TYPE); + const_tree base_type = get_base_type_from_array_type (array); + return filter_out_boring_type (base_type, map); +} + +static bool +filter_out_boring_reference (const_tree ref, hash_set<const_tree> &map) +{ + gcc_unreachable(); + enum tree_code code = TREE_CODE (ref); + gcc_assert (code == REFERENCE_TYPE); + test_log ("in filter out boring reference", 0); + bool retval = filter_out_boring_type (TREE_TYPE (ref), map); + if (retval) + { + test_log ("we are getting a reference", 0); + } + return retval; +} + +static bool +filter_out_boring_type (const_tree type, hash_set<const_tree> &map) +{ + if (!type) + { + test_log ("filter_out_boring_type (NULL)", 0); + return true; + } + + // maybe something like, if the type is + // equivalent to one of the already + // interesting types, then it should + // also be interesting..? + // But then, imagine this happens out of order... + // We get type A which is non-interesting + // type A is equivalent to B + // then we receive type B which is interesting + // this approach would fail... + + enum tree_code code = TREE_CODE (type); + bool retval = true; + switch (code) + { + case ARRAY_TYPE: + retval = filter_out_boring_array (type, map); + break; + case POINTER_TYPE: + retval = filter_out_boring_pointer (type, map); + break; + case RECORD_TYPE: + retval = filter_out_boring_record (type, map); + break; + case REFERENCE_TYPE: + retval = filter_out_boring_reference (type, map); + break; + default: + { + test_log ("default in tree code %s", 0, get_tree_code_name (code)); + retval = true; + } + break; + } + + if (!retval) + { + test_log ("putting %s", 0, get_type_name (type)); + map.add (type); + } + return retval; +} + +static bool +filter_var_decls (tree var_decl, hash_set<const_tree> &type_map) +{ + gcc_assert (var_decl); + gcc_assert (TREE_CODE (var_decl) == VAR_DECL); + tree type = TREE_TYPE (var_decl); + return filter_out_boring_type (type, type_map); +} + +static bool +filter_parm_decls (tree parm_decl, hash_set<const_tree> &type_map) +{ + gcc_assert (parm_decl); + gcc_assert (TREE_CODE (parm_decl) == PARM_DECL); + tree type = TREE_TYPE (parm_decl); + return filter_out_boring_type (type, type_map); +} + +static void +collect_parm_declarations (cgraph_node *cnode, + bool (*filter) (tree, hash_set<const_tree> &), + hash_set<const_tree> &decl_map) +{ + gcc_assert (cnode); + // TODO: Do we need to get_untransformed_body_here? + for (tree parm = DECL_ARGUMENTS (cnode->decl); parm; parm = DECL_CHAIN (parm)) + { + bool filter_out = (*filter) (parm, decl_map); + if (filter_out) + continue; + + tree type = TREE_TYPE (parm); + bool is_inside = decl_map.contains (type); + // We already have this type. + if (is_inside) + continue; + + //const char *type_identifier = get_type_name (type); + //test_log ("collecting,%s", 0, type_identifier); + decl_map.add (type); + } +} + +static void +collect_pointer_plus (tree lhs, tree rhs1, tree rhs2, + hash_set<const_tree> &decl_map) +{ + tree lhs_type = lhs ? TREE_TYPE (lhs) : NULL; + bool is_lhs_boring + = lhs_type ? filter_out_boring_type (lhs_type, decl_map) : true; + tree rhs1_type = rhs1 ? TREE_TYPE (rhs1) : NULL; + bool is_rhs1_boring + = rhs1_type ? filter_out_boring_type (rhs1_type, decl_map) : true; + tree rhs2_type = rhs2 ? TREE_TYPE (rhs2) : NULL; + bool is_rhs2_boring + = rhs2_type ? filter_out_boring_type (rhs2_type, decl_map) : true; + + if (!is_lhs_boring) + { + test_log ("putting lhs %s", 0, get_type_name (lhs_type)); + decl_map.add (lhs_type); + } + if (!is_rhs1_boring) + { + test_log ("putting rhs1 %s", 0, get_type_name (rhs1_type)); + decl_map.add (rhs1_type); + } + if (!is_rhs2_boring) + { + test_log ("putting rhs2 %s", 0, get_type_name (rhs2_type)); + decl_map.add (rhs2_type); + } +} + +static void +collect_assign_rhs (gimple *stmt, hash_set<const_tree> &decl_map) +{ + enum tree_code code = gimple_expr_code (stmt); + switch (code) + { + case POINTER_PLUS_EXPR: + case POINTER_DIFF_EXPR: + case COMPONENT_REF: + { + tree rhs2 = gimple_assign_rhs2 (stmt); + tree rhs1 = gimple_assign_rhs1 (stmt); + tree lhs = gimple_assign_lhs (stmt); + collect_pointer_plus (lhs, rhs1, rhs2, decl_map); + } + break; + default: + { + //print_gimple_stmt (dump_file, stmt, 0); + test_log ("DEFAULT HERE: %s", 0, get_tree_code_name (code)); + } + break; + } +} + +static void +collect_assign (gimple *stmt, hash_set<const_tree> &decl_map) +{ + gcc_assert (stmt); + collect_assign_rhs (stmt, decl_map); +} + +static void +collect_stmt (gimple *stmt, hash_set<const_tree> &decl_map) +{ + gcc_assert (stmt); + const enum gimple_code code = gimple_code (stmt); + switch (code) + { + case GIMPLE_ASSIGN: + collect_assign (stmt, decl_map); + break; + default: + break; + } + return; +} + +static void +collect_basic_block (basic_block bb, hash_set<const_tree> &decl_map) +{ + gcc_assert (bb); + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + collect_stmt (stmt, decl_map); + } +} + +static void +collect_function_body (cgraph_node *cnode, hash_set<const_tree> &decl_map) +{ + gcc_assert (cnode); + cnode->get_untransformed_body (); + basic_block bb = NULL; + function *func = DECL_STRUCT_FUNCTION (cnode->decl); + push_cfun (func); + FOR_EACH_BB_FN (bb, func) + { + collect_basic_block (bb, decl_map); + } + pop_cfun (); + print_function (cnode); +} + +static void +collect_local_declarations (cgraph_node *cnode, + bool (*filter) (tree, hash_set<const_tree> &), + hash_set<const_tree> &decl_map) +{ + gcc_assert (cnode); + int i = 0; + function *func = DECL_STRUCT_FUNCTION (cnode->decl); + cnode->get_untransformed_body (); + tree var_decl = NULL; + FOR_EACH_LOCAL_DECL (func, i, var_decl) + { + bool filter_out = (*filter) (var_decl, decl_map); + if (filter_out) + continue; + + tree type = TREE_TYPE (var_decl); + //const char *type_identifier = get_type_name (type); + //test_log ("collecting,%s", 0, type_identifier); + decl_map.add (type); + } +} + +static void +collect_global_declaration (varpool_node *vnode, + bool (*filter) (tree, hash_set<const_tree> &), + hash_set<const_tree> &decl_map) +{ + test_log ("collect global declarations", 0); + int i; + struct ipa_ref *ref; + + + for (i = 0; vnode->iterate_referring (i, ref); i++) + { + bool filter_out = (*filter) (vnode->decl, decl_map); + if (filter_out) + { + test_log ("filter out", 0); + continue; + } + + tree type = TREE_TYPE (vnode->decl); + //const char *type_identifier = get_type_name (type); + //test_log ("collecting,%s", 0, type_identifier); + decl_map.add (type); + } +} + +static void +collect_global_declarations (bool (*filter) (tree, + hash_set<const_tree> &), + hash_set<const_tree> &decl_map) +{ + varpool_node *vnode; + FOR_EACH_VARIABLE (vnode) + { + collect_global_declaration (vnode, filter, decl_map); + } +} + +static void +collect_orig_structs (hash_set<const_tree> &type_map) +{ + collect_global_declarations (&filter_var_decls, type_map); + + cgraph_node *cnode = NULL; + FOR_EACH_DEFINED_FUNCTION (cnode) + { + print_function (cnode); + collect_parm_declarations (cnode, &filter_parm_decls, type_map); + collect_function_body (cnode, type_map); + collect_local_declarations (cnode, &filter_var_decls, type_map); + } +} + +static const_tree +make_new_type_based_on_old (const_tree old, + t_map *mod_type_map); + +static const_tree +get_new_type_main_variant (const_tree old, t_map *mod_type_map) +{ + const_tree old_type_main_variant = TYPE_MAIN_VARIANT(old); + gcc_assert(old != old_type_main_variant); + delete_info_t *ptr = mod_type_map->get (TYPE_MAIN_VARIANT(old_type_main_variant)); + if (ptr) { + const_tree retval = TYPE_MAIN_VARIANT(ptr->new_record); + return ptr->new_record; + } + + const_tree new_main_variant_record = make_new_type_based_on_old(old_type_main_variant, mod_type_map); + return new_main_variant_record; +} + +static const_tree +make_new_record_based_on_typedef (const_tree _typedef, t_map *mod_type_map) +{ + log("make_new_record_based_on_typedef\n"); + bool is_typedef = _typedef != TYPE_MAIN_VARIANT(_typedef); + gcc_assert(is_typedef); + tree new_main_variant = (tree) get_new_type_main_variant(_typedef, mod_type_map); + tree new_type = build_type_variant(new_main_variant, TYPE_READONLY(_typedef), TYPE_VOLATILE(_typedef)); + const char *new_name = make_record_name_based_on_old (_typedef); + tree new_record_name = get_identifier (new_name); + TYPE_NAME (new_type) = new_record_name; + layout_type(new_type); + + delete_info_t* info = mod_type_map->get(TYPE_MAIN_VARIANT(_typedef)); + gcc_assert(info); + delete_info_t new_info; + new_info.old_record = _typedef; + new_info.new_record = new_type; + new_info.deleted_fields = info->deleted_fields; + mod_type_map->put (_typedef, new_info); + return new_type; +} + +static const_tree +make_new_record_based_on_old (const_tree old, + t_map *mod_type_map) +{ + log("make_new_record_based_on_old\n %s", get_type_name(old)); + delete_info_t* info = mod_type_map->get(old); + const_tree *new_record_ptr = info ? &(info->new_record) : NULL; + + gcc_assert (!new_record_ptr); + + bool is_typedef = old != TYPE_MAIN_VARIANT(old); + if (is_typedef) { return make_new_record_based_on_typedef(old, mod_type_map); } + + bool will_definitely_change = is_interesting_struct (old); + + tree new_record = make_node (RECORD_TYPE); + const char *new_name = make_record_name_based_on_old (old); + tree new_record_name = get_identifier (new_name); + TYPE_NAME (new_record) = new_record_name; + hash_set<const_tree> *deleted_fields = new hash_set<const_tree> (); + + tree prev_new_field = NULL; + for (tree old_field = TYPE_FIELDS (old); old_field; + old_field = DECL_CHAIN (old_field)) + { + + const bool are_field_names_equal = is_interesting_field (old_field); + log("am i deleting field %s ? %s\n", get_field_name(old_field), will_definitely_change && are_field_names_equal ? "true" : "false"); + if (will_definitely_change && are_field_names_equal) + { + char *buffer; + char *buffer2; + asprintf(&buffer, "%s.%s", get_type_name(old), get_field_name(old_field)); + asprintf(&buffer2, "%s.%s", get_type_name(TYPE_MAIN_VARIANT(old)), get_field_name(old_field)); + log("buffer %s\n", buffer); + log("buffer2 %s\n", buffer2); + if (is_interesting_pair(buffer) || is_interesting_pair(buffer2)) + { + deleted_fields->add(old_field); + continue; + } + } + + tree new_field = make_node (FIELD_DECL); + DECL_NAME (new_field) = DECL_NAME (old_field); + tree old_field_type = TREE_TYPE (old_field); + const_tree new_field_type + = make_new_type_based_on_old (TREE_TYPE (old_field), mod_type_map); + test_log ("inside making new field, %s %s", 0, + get_type_name (old_field_type), get_type_name (new_field_type)); + const_tree maybe_new_field + = new_field_type == old_field ? old_field : new_field_type; + substitute_type(new_field, maybe_new_field); + DECL_SOURCE_LOCATION (new_field) = DECL_SOURCE_LOCATION (old_field); + SET_DECL_ALIGN (new_field, DECL_ALIGN (old_field)); + DECL_USER_ALIGN (new_field) = DECL_ALIGN (old_field); + TREE_ADDRESSABLE (new_field) = TREE_ADDRESSABLE (old_field); + DECL_NONADDRESSABLE_P (new_field) = !TREE_ADDRESSABLE (old_field); + TREE_THIS_VOLATILE (new_field) = TREE_THIS_VOLATILE (old_field); + // DECL_FIELD_OFFSET and DECL_FIELD_BIT_OFFSET + // are actually set by layout_type. + const bool first_new_field = !prev_new_field; + if (first_new_field) + { + TYPE_FIELDS (new_record) = new_field; + } + else + { + DECL_CHAIN (prev_new_field) = new_field; + } + + prev_new_field = new_field; + } + + layout_type (new_record); + tree new_type = build_type_variant(new_record, TYPE_READONLY(old), TYPE_VOLATILE(old)); + layout_type (new_type); + + + for (tree new_field = TYPE_FIELDS (new_record); new_field; + new_field = DECL_CHAIN (new_field)) + { + const char *new_field_name = get_field_name (new_field); + int new_offset = get_field_offset (new_field); + test_log ("offset,%s,%s,%d", 0, new_name, new_field_name, new_offset); + } + + delete_info_t new_info; + new_info.old_record = old; + new_info.new_record = new_record; + new_info.deleted_fields = deleted_fields; + mod_type_map->put (old, new_info); + return new_record; +} + +const_tree +make_new_array_based_on_old (const_tree old_type, + t_map *mod_type_map) +{ + gcc_assert (TREE_CODE (old_type) == ARRAY_TYPE); + delete_info_t *delete_info_1 = mod_type_map->get(old_type); + const_tree *new_type_ptr = delete_info_1 ? &(delete_info_1->new_record) : NULL; + gcc_assert (!new_type_ptr); + + const char *ptr = make_array_name_based_on_old (old_type); + tree new_array = make_node (ARRAY_TYPE); + TYPE_NAME (new_array) = get_identifier (ptr); + TYPE_DOMAIN (new_array) = TYPE_DOMAIN (old_type); + tree old_array_type = TREE_TYPE (old_type); + const_tree possibly_new + = make_new_type_based_on_old (old_array_type, mod_type_map); + if (possibly_new == old_array_type) + return old_type; + substitute_type(new_array, possibly_new); + layout_type (new_array); + + delete_info *ptr_2 = mod_type_map->get (old_type); + if (ptr_2) + return ptr_2->new_record; + + //HERE HERE + // struct delete_info { const_tree old_record; const_tree new_record; field_set* deleted_fields;}; + delete_info_t delete_info; + delete_info.old_record = old_type; + delete_info.new_record = new_array; + delete_info.deleted_fields = NULL; + mod_type_map->put (old_type, delete_info); + + return new_array; +} + +static const_tree +make_new_pointer_based_on_old (const_tree old, + t_map *mod_type_map) +{ + test_log ("make_new_pointer_based_on_old %s", 0, get_type_name (old)); + delete_info_t *delete_info_1 = mod_type_map->get(old); + const_tree *already_computed = delete_info_1 ? &(delete_info_1->new_record) : NULL; + gcc_assert (!already_computed); + + gcc_assert (TREE_CODE (old) == POINTER_TYPE); + + tree new_pointer = make_node (POINTER_TYPE); + const char *ptr = make_pointer_name_based_on_old (old); + + TYPE_NAME (new_pointer) = get_identifier (ptr); + SET_TYPE_MODE (new_pointer, TYPE_MODE (old)); + tree old_type = TREE_TYPE (old); + gcc_assert (old_type); + const_tree new_type = make_new_type_based_on_old (old_type, mod_type_map); + // We do not need it... + if (new_type == old_type) + return old; + + substitute_type(new_pointer, new_type); + layout_type (new_pointer); + + delete_info_t *ptr_2 = mod_type_map->get (old); + if (ptr_2) + return ptr_2->new_record; + + //HERE HERE + // struct delete_info { const_tree old_record; const_tree new_record; field_set* deleted_fields;}; + delete_info_t delete_info; + delete_info.old_record = old; + delete_info.new_record = new_pointer; + delete_info.deleted_fields = NULL; + mod_type_map->put (old, delete_info); + test_log ("putting %s %s", 0, get_type_name (old), + get_type_name (new_pointer)); + return new_pointer; +} + +static const_tree +make_new_type_based_on_old (const_tree old, + t_map *mod_type_map) +{ + if (!old) + return old; + + gcc_assert (old); + delete_info_t *delete_info = mod_type_map->get (old); + const_tree *new_base_ptr = delete_info ? &(delete_info->new_record) : NULL; + if (new_base_ptr) + return *new_base_ptr; + + switch (TREE_CODE (old)) + { + case ARRAY_TYPE: + return make_new_array_based_on_old (old, mod_type_map); + break; + case POINTER_TYPE: + return make_new_pointer_based_on_old (old, mod_type_map); + break; + case RECORD_TYPE: + return make_new_record_based_on_old (old, mod_type_map); + break; + default: + return old; + break; + } + gcc_unreachable (); + return NULL; +} + +// Non-null values for type_map +// will be the modified tree. +bool +compute_modified_structs_internal ( + const_tree const &old_type, + t_map *mod_type_map) +{ + delete_info_t *ptr = mod_type_map->get (old_type); + // already in map + if (ptr) + return true; + + const char *identifier = get_type_name (old_type); + test_log ("modifying,%s", 0, identifier); + + const_tree new_record = make_new_type_based_on_old (old_type, mod_type_map); + // We might have modified the mod_type_map inside make_new_type_based_on_old + // and therefore we no longer have the need to store this there again... + delete_info_t *ptr_2 = mod_type_map->get (old_type); + if (ptr_2) + return true; + + test_log ("new_name,%s", 0, get_type_name (new_record)); + //HERE HERE + // struct delete_info { const_tree old_record; const_tree new_record; field_set* deleted_fields;}; + delete_info_t delete_info; + delete_info.new_record = new_record; + delete_info.old_record = old_type; + delete_info.deleted_fields = NULL; + mod_type_map->put (old_type, delete_info); + return true; +} + +static void +compute_modified_structs (hash_set<const_tree> &type_map, + t_map &mod_type_map) +{ + type_map.traverse< t_map *, + compute_modified_structs_internal> (&mod_type_map); +} + +static bool +rewrite_constructor_def (tree expr, t_map &type_map, + const int indent) +{ + // These nodes represent the brace initializers + // for a structure or an array. They contain + // a sequence of component values made out of a vector + // of constructor_elt, which is a (INDEX, VALUE) pair. + tree old_rhs_type = TREE_TYPE (expr); + delete_info_t *delete_info = type_map.get (old_rhs_type); + const_tree *new_rhs_type_ptr = delete_info ? &(delete_info->new_record) : NULL; + if (!new_rhs_type_ptr) + return false; + + substitute_type(expr, new_rhs_type_ptr); + + tree new_type = TREE_TYPE (expr); + // If the TREE_TYPE of the CONSTRUCTOR is a RECORD_TYPE + // UNION_TYPE or QUAL_UNION_TYPE, then the INDEX of each node + // in the sequence will be a FIELD_DECL and the VALUE will be the + // expression used to initialize that field. + // If TREE_TYPE of the CONSTRUCTOR is an ARRAY_TYPE, then + // the INDEX of each node in the sequence will be an INTEGER_CST + // or a RANGE_EXPR of two INTEGER_CSTs. + if (TREE_CLOBBER_P (expr)) + return true; + + enum tree_code code = TREE_CODE (new_type); + bool is_record = code == RECORD_TYPE; + bool is_union = code == UNION_TYPE; + bool is_qual = code == QUAL_UNION_TYPE; + bool is_array = code == ARRAY_TYPE; + bool is_interesting = is_record || is_union || is_qual || is_array; + if (!is_interesting) + return true; + + // We have not tested this with other types... + gcc_assert (is_record); + // FIXME, we would need some time to rewrite the + // API to allow gimple_stmt_iterator be passed + // around all rewrite expressions... + // or something like that. + // Since not a lot of code uses this syntax, + // let's avoid it for now. But make a note + // we still need to fix this. + gcc_unreachable (); + + unsigned i; + tree val; + FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (expr), i, val) + { + rewrite_expr (val, type_map, indent + 4); + } + + return true; +} + +static bool +rewrite_field_decl_def (tree expr, t_map &type_map, + __attribute__((unused)) const int indent) +{ + tree old_rhs_type = TREE_TYPE (expr); + delete_info_t *delete_info = type_map.get(old_rhs_type); + const_tree *new_rhs_type_ptr = delete_info ? &(delete_info->new_record) : NULL; + if (!new_rhs_type_ptr) + return false; + + substitute_type(expr, new_rhs_type_ptr); + gcc_assert (TREE_OPERAND (expr, 0) == NULL); + return true; +} + +static bool +rewrite_integer_cst_def (tree expr, t_map &type_map, + __attribute__((unused)) const int indent) +{ + tree old_rhs_type = TREE_TYPE (expr); + delete_info_t *delete_info = type_map.get (old_rhs_type); + const_tree *new_rhs_type_ptr = delete_info ? &(delete_info->new_record) : NULL; + if (!new_rhs_type_ptr) + return false; + + substitute_type(expr, new_rhs_type_ptr); + return true; +} + +static void +rewrite_function_parms (tree expr, t_map &type_map, + const int indent); +static void +rewrite_function_return_type (tree expr, + t_map &type_map, + const int indent); + +static bool +rewrite_function_decl_def (tree expr, + t_map &type_map, + const int indent) +{ + rewrite_function_parms (expr, type_map, indent + 4); + rewrite_function_return_type (expr, type_map, indent + 4); + return false; +} + +static bool +rewrite_component_ref_def_lhs (gimple_stmt_iterator &gsi, tree expr, t_map &type_map, const int indent) +{ + gcc_assert (expr); + tree expr_type = TREE_TYPE (expr); + gcc_assert (expr_type); + delete_info_t *delete_info1 = type_map.get (expr_type); + delete_info1 = delete_info1 ? delete_info1 : type_map.get (TYPE_MAIN_VARIANT(expr_type)); + const_tree *new_expr_type_ptr = delete_info1 ? &(delete_info1->new_record) : NULL; + + tree _struct = TREE_OPERAND (expr, 0); + gcc_assert (_struct); + tree _struct_type = TREE_TYPE (_struct); + + + gcc_assert (_struct_type); + const enum tree_code code = TREE_CODE(_struct_type); + const bool is_record = RECORD_TYPE == code; + //TODO: Handle unions better + switch (code) + { + case UNION_TYPE: return false; break; + case RECORD_TYPE: break; + default: gcc_unreachable(); break; + } + gcc_assert(is_record); + + delete_info_t *delete_info = type_map.get (_struct_type); + delete_info = delete_info ? delete_info : type_map.get (TYPE_MAIN_VARIANT(_struct_type)); + const_tree *new_struct_type_ptr = delete_info ? &(delete_info->new_record) : NULL; + + if (new_expr_type_ptr) + substitute_type(expr, new_expr_type_ptr); + + bool retval = rewrite_expr (_struct, type_map, indent); + + tree new_type + = new_struct_type_ptr ? const_to_tree(*new_struct_type_ptr) : _struct_type; + tree field_decl = TREE_OPERAND (expr, 1); + bool has_been_deleted = delete_info ? delete_info->deleted_fields->contains(field_decl) : false; + if (has_been_deleted) + { + const char *field_name = get_field_name (field_decl); + test_log("we are removing field %s from struct %s", 0, field_name, get_type_name(delete_info->old_record)); + return true; + } + + + const char *field_name = get_field_name (field_decl); + if (strcmp (field_name, "anonymous") == 0) + return false; + + + const_tree new_field = get_field_with_name (new_type, field_name); + // INFO: We need to rewrite the whole operand + // not just its type. + // INFO: When rewriting an operand we **must** + // be in -flto-partition=none + // INFO: setting the type and calling relayout_decl + // doesn't work. + // TODO: FIXME: In order to remove this assertion + // you need to use clones. + gcc_assert (in_lto_p && !flag_ltrans && !flag_wpa); + TREE_OPERAND (expr, 1) = const_to_tree(new_field); + int offset = get_field_offset (new_field); + test_log ("rewrite,field_offset,%s,%d", indent, field_name, offset); + gcc_assert (TREE_OPERAND (expr, 2) == NULL); + + return false; +} + +static bool +rewrite_component_ref_def (tree expr, + t_map &type_map, + const int indent) +{ + gcc_assert (expr); + tree expr_type = TREE_TYPE (expr); + gcc_assert (expr_type); + + delete_info_t *delete_info1 = type_map.get (expr_type); + const_tree *new_expr_type_ptr = delete_info1 ? &(delete_info1->new_record) : NULL; + if (new_expr_type_ptr) + substitute_type(expr, new_expr_type_ptr); + + tree _struct = TREE_OPERAND (expr, 0); + gcc_assert (_struct); + tree _struct_type = TREE_TYPE (_struct); + gcc_assert (_struct_type); + const enum tree_code code = TREE_CODE(_struct_type); + const bool is_record = RECORD_TYPE == code; + //TODO: Handle unions better + switch (code) + { + case UNION_TYPE: return false; break; + case RECORD_TYPE: break; + default: gcc_unreachable(); break; + } + gcc_assert(is_record); + delete_info_t *delete_info = type_map.get (_struct_type); + const_tree *new_struct_type_ptr = delete_info ? &(delete_info->new_record) : NULL; + + bool retval = rewrite_expr (_struct, type_map, indent); + + tree new_type + = new_struct_type_ptr ? const_to_tree(*new_struct_type_ptr) : _struct_type; + tree field_decl = TREE_OPERAND (expr, 1); + bool has_been_deleted = delete_info ? delete_info->deleted_fields->contains(field_decl) : false; + if (has_been_deleted) + { + gcc_unreachable(); + return false; + } + + const char *field_name = get_field_name (field_decl); + if (strcmp (field_name, "anonymous") == 0) + return retval; + + const_tree new_field = get_field_with_name (new_type, field_name); + // INFO: We need to rewrite the whole operand + // not just its type. + // INFO: When rewriting an operand we **must** + // be in -flto-partition=none + // INFO: setting the type and calling relayout_decl + // doesn't work. + // TODO: FIXME: In order to remove this assertion + // you need to use clones. + gcc_assert (in_lto_p && !flag_ltrans && !flag_wpa); + TREE_OPERAND (expr, 1) = const_to_tree(new_field); + int offset = get_field_offset (new_field); + test_log ("rewrite,field_offset,%s,%d", indent, field_name, offset); + gcc_assert (TREE_OPERAND (expr, 2) == NULL); + + return true; +} + +static bool +rewrite_addr_expr_def (tree expr, t_map &type_map, + const int indent) +{ + tree type = TREE_TYPE (expr); + gcc_assert (TREE_CODE (expr) == ADDR_EXPR); + delete_info_t *delete_info = type_map.get (type); + const_tree *new_type_ptr = delete_info ? &(delete_info->new_record) : NULL; + if (new_type_ptr) + substitute_type(expr, new_type_ptr); + tree op0 = TREE_OPERAND (expr, 0); + bool retval = rewrite_expr (op0, type_map, indent); + // There isn't an operand here, but we also can't + // TREE_OPERAND(expr, 1) + // Accessing this won't fail. + return retval; +} + +static bool +rewrite_var_decl_def (tree expr, __attribute__((unused)) t_map &type_map, + __attribute__((unused)) const int indent) +{ + gcc_assert (TREE_CODE (expr) == VAR_DECL); + return false; +} + +static bool +rewrite_mem_ref_def (tree expr, t_map &type_map, + const int indent) +{ + // Do we need to modify this value so that we op1 and calculate a constant + // value? + gcc_assert (expr); + gcc_assert (TREE_CODE (expr) == MEM_REF); + tree type = TREE_TYPE (expr); + + delete_info_t *delete_info = type_map.get (type); + delete_info = delete_info ? delete_info : type_map.get (TYPE_MAIN_VARIANT(type)); + const_tree *new_type_ptr = delete_info ? &(delete_info->new_record) : NULL; + if (new_type_ptr) + substitute_type(expr, new_type_ptr); + + tree op0 = TREE_OPERAND (expr, 0); + // The first operand is the pointer being dereferenced + bool retval = rewrite_expr (op0, type_map, indent); + // The second operand is a pointer constant. Its type specifying the type + // to be used for type-based alias analysis. + tree op1 = TREE_OPERAND (expr, 1); + rewrite_expr (op1, type_map, indent); + gcc_assert (TREE_CODE (op1) == INTEGER_CST); + int old_offset = tree_to_uhwi (op1); + if (old_offset == 0 || !new_type_ptr) + return retval; + + tree old_struct_size = TYPE_SIZE_UNIT (type); + int old_type_size_int = tree_to_shwi (old_struct_size); + test_log ("old_type = %s", 0, get_type_name (type)); + test_log ("old_type_size = %ld", 0, tree_to_shwi (old_struct_size)); + tree new_struct_size = TYPE_SIZE_UNIT (const_to_tree(*new_type_ptr)); + int new_type_size_int = tree_to_shwi (new_struct_size); + int multiple = old_offset / old_type_size_int; + int offset = multiple * new_type_size_int; + int remainder = old_offset % old_type_size_int; + // FIXME: Do I need a way of mapping old_offsets to new_offsets... + // This seems insufficient. + // I need to know at which offset the discontinuity happens so that + // I can account for that. + // for now, we can only guarantee this transformation if + // old_offset is multiple of old_type_size_int; + int new_offset = offset + remainder; + if (new_offset == old_offset) + return retval; + + tree new_offset_tree = build_int_cst (TREE_TYPE (op1), new_offset); + TREE_OPERAND (expr, 1) = new_offset_tree; + gcc_assert (old_offset % old_type_size_int == 0); + + // TREE_OPERAND(expr, 2) cannot be accessed. + return retval; +} + +static bool +rewrite_ssa_name_def (tree expr, t_map &type_map, + __attribute__((unused)) const int indent) +{ + tree type = TREE_TYPE (expr); + tree ssa_name_var = SSA_NAME_VAR (expr); + delete_info_t *delete_info = type_map.get (type); + const_tree *new_type_ptr = delete_info ? &(delete_info->new_record) : NULL; + tree new_type_2 = NULL; + if (new_type_ptr) + { + new_type_2 = build_type_variant(const_to_tree(*new_type_ptr), TYPE_READONLY(type), TYPE_VOLATILE(type)); + substitute_type(expr, new_type_2); + // TODO: I had some cases where ssa_name_var was not set. + // Not sure why... + // I need to investigate + // Could this be the variables that I create? + // If so, then the temporary variables should have SSA_NAMES + // and there is something else that I need to change + if (ssa_name_var) + { + substitute_type(ssa_name_var, new_type_2); + relayout_decl (ssa_name_var); + } + } + + return new_type_2; +} + +static bool +rewrite_bin_expr_default_def (tree expr, + t_map &type_map, + const int indent) +{ + gcc_assert (expr); + tree op0 = TREE_OPERAND (expr, 0); + tree op1 = TREE_OPERAND (expr, 1); + gcc_assert (op0 && op1); + + bool retval = false; + retval |= rewrite_expr (op0, type_map, indent); + retval |= rewrite_expr (op1, type_map, indent); + // Why is this unreachable? + gcc_unreachable (); + gcc_assert (TREE_OPERAND (expr, 2) == NULL); + + return retval; +} + + +static bool +rewrite_array_ref_def (tree expr, t_map &type_map, + const int indent) +{ + gcc_assert (expr); + gcc_assert (TREE_CODE (expr) == ARRAY_REF); + tree type = TREE_TYPE (expr); + //const_tree *up_ptr = type_map.get (type); + delete_info_t *delete_info = type_map.get (type); + const_tree *up_ptr = delete_info ? &(delete_info->new_record) : NULL; + if (up_ptr) + substitute_type(expr, up_ptr); + + rewrite_expr (TREE_OPERAND (expr, 0), type_map, indent + 4); + rewrite_expr (TREE_OPERAND (expr, 1), type_map, indent + 4); + gcc_assert (TREE_OPERAND (expr, 2) == NULL); + + return true; +} + +static void +rewrite_pointer_diff_def_rhs_variable_replace_constants_implementation ( + __attribute__((unused)) gimple_stmt_iterator &gsi, tree lhs, tree pointer, __attribute__((unused)) tree variable, + const_tree old_type) +{ + // We will need this later + tree old_type_size = TYPE_SIZE_UNIT (TREE_TYPE (old_type)); + int old_type_size_int = tree_to_shwi (old_type_size); + // lhs = op0 - op1 // <-- we are here + // ... SNIP ... + // var = lhs / [ex] old_struct_size // <-- we want to be here + // + // Let's explore the uses of lhs + + gimple *stmt; + imm_use_iterator iterator; + FOR_EACH_IMM_USE_STMT (stmt, iterator, lhs) + { + // stmt is a use of lhs + // gimple_expr_code is only valid for non-debug statements + bool is_debug = is_gimple_debug (stmt); + if (is_debug) + continue; + + test_log ("is not debug", 0); + enum tree_code code = gimple_expr_code (stmt); + bool is_exact_div = code == EXACT_DIV_EXPR; + if (!is_exact_div) + continue; + + test_log ("is exact div expr", 0); + tree divisor = gimple_op (stmt, 2); + enum tree_code divisor_code = TREE_CODE (divisor); + bool is_constant = divisor_code == INTEGER_CST; + if (!is_constant) + continue; + + test_log ("is constant", 0); + int divisor_int = tree_to_shwi (divisor); + test_log ("size %d %d", 0, divisor_int, old_type_size_int); + bool is_same_size = divisor_int == old_type_size_int; + if (!is_same_size) + continue; + + const_tree new_base_type = TREE_TYPE (TREE_TYPE (pointer)); + tree new_struct_size_tree_1 = TYPE_SIZE_UNIT (new_base_type); + int new_struct_size_int = tree_to_shwi (new_struct_size_tree_1); + tree new_struct_size_const + = build_int_cst (TREE_TYPE (divisor), new_struct_size_int); + gimple_set_op (stmt, 2, new_struct_size_const); + } +} + +static tree +rewrite_pointer_plus_def_rhs_variable_replace_constants_implementation ( + __attribute__((unused)) gimple_stmt_iterator &gsi, tree lhs, __attribute__((unused)) tree pointer, tree variable, + const_tree old_type) +{ + // We need to find the definition of the 3rd operand + // _1 = _0 * 72 + // ... SNIP ... + // _2 = _1 + CONSTANT; + // ... SNIP ... + // _3 = &array + _2; < -- this is where we are + gcc_assert (TREE_CODE (variable) == SSA_NAME); + gimple *def_for_variable = SSA_NAME_DEF_STMT (variable); + + // It is also possible that we are in a negation statement. + // Example: + // _2 = _1 * 72; + // ... SNIP ... + // _3 = -_2; < -- def_for_variable **might** be this stmt. + // ... SNIP ... + // _4 = &array + _3; + // Let's find out how many operands we have + unsigned num_operands = gimple_num_ops (def_for_variable); + test_log ("how many operands ? %d", 0, num_operands); + bool need_to_skip = num_operands == 2; + gimple *to_change = need_to_skip + ? SSA_NAME_DEF_STMT (gimple_op (def_for_variable, 1)) + : def_for_variable; + + // _2 = _1 * 72; <-- to change + // _3 = &array + _2; + enum tree_code code = gimple_expr_code (to_change); + const_tree old_base_type = TREE_TYPE (old_type); + tree old_struct_size_tree_1 = TYPE_SIZE_UNIT (old_base_type); + int old_struct_size_int = tree_to_shwi (old_struct_size_tree_1); + const_tree pointer_to_unit_type = TREE_TYPE (lhs); + const_tree unit_type = TREE_TYPE (pointer_to_unit_type); + tree new_struct_size_tree_1 = TYPE_SIZE_UNIT (unit_type); + int new_struct_size_int = tree_to_shwi (new_struct_size_tree_1); + + if (code == PLUS_EXPR) + { + // If we are here it is because we are adding an offset. + // It is usually whenever we do somehting like + // _2 = _1 + CONSTANT; <-- to change + // _3 = &array + _2; + gcc_assert (code == PLUS_EXPR); + + tree constant_plus = gimple_op (to_change, 2); + gcc_assert (TREE_CODE (constant_plus) == INTEGER_CST); + + int old_unit_size_one_plus = tree_to_uhwi (constant_plus); + // This used to work for unprofiled code. + // However, with profiling, I guess we can jump wherever we want... + // So, we need to find out which field we were intending to access + // and where that field maps to the new struct... + // gcc_assert(is_modulo); + + bool is_modulo_old = old_unit_size_one_plus % old_struct_size_int == 0; + bool is_modulo_new = old_unit_size_one_plus % new_struct_size_int == 0; + // If we are not in flag_profile_use + // we will always be a modulo + if (!flag_profile_use) + gcc_assert (is_modulo_old); + + test_log ("old plus constant = %d", 0, old_unit_size_one_plus); + test_log ("old struct size int = %d", 0, old_struct_size_int); + test_log ("new struct size int = %d", 0, new_struct_size_int); + test_log ("old_unit_size_one_plus modulo new_struct_pointer_int = %d", 0, + old_unit_size_one_plus % new_struct_size_int); + + // FIXME: + // The problem is that if flag_profile_use is enabled, + // then we can potentially **predict** the offset + // to be the correct one. But not always? + // Therefore, we really do not have an idea if we are using + // a value obtained from profiling or one from "correctness". + if (flag_profile_use) + { + bool is_from_profiling = is_modulo_new; + bool is_from_correctness = is_modulo_old; + gcc_assert (is_from_correctness || is_from_profiling); + } + + int field_accessed_offset = old_unit_size_one_plus % old_struct_size_int; + + if (flag_profile_use && is_modulo_new) + gcc_assert (field_accessed_offset != 0); + + int new_plus_constant_int + = old_unit_size_one_plus / old_struct_size_int * new_struct_size_int + + field_accessed_offset; + + if (flag_profile_use && is_modulo_new) + gcc_assert(field_accessed_offset % new_struct_size_int == 0); + + tree new_plus_constant + = build_int_cst (TREE_TYPE (constant_plus), new_plus_constant_int); + gimple_set_op (to_change, 2, new_plus_constant); + + // And now we need to get the defintion of _1 + variable = gimple_op (to_change, 1); + gcc_assert (TREE_CODE (variable) == SSA_NAME); + def_for_variable = SSA_NAME_DEF_STMT (variable); + num_operands = gimple_num_ops (def_for_variable); + test_log ("how many operands ? %d", 0, num_operands); + need_to_skip = num_operands == 2; + to_change = need_to_skip + ? SSA_NAME_DEF_STMT (gimple_op (def_for_variable, 1)) + : def_for_variable; + code = gimple_expr_code (to_change); + } + + if (code == MULT_EXPR) + { + // How do we know what type we need to change? + // That is _3 (or lhs) is the result of the pointer arithmetic. + // So we need one level of dereference in order to find + // the underlying type. + + // We need to get the second operand + test_print_gimple_stmt (to_change); + tree constant = gimple_op (to_change, 2); + gcc_assert (TREE_CODE (constant) == INTEGER_CST); + + // We should have the same size... + // Is this always true? + // TODO: can we enable this assertion? + // int old_unit_size_one = tree_to_shwi (constant); + // gcc_assert(old_unit_size_one == old_struct_size_int); + + // Now we need to modify the second operand... + tree new_constant + = build_int_cst (TREE_TYPE (constant), new_struct_size_int); + gimple_set_op (to_change, 2, new_constant); + } + + return NULL; +} + +static tree +rewrite_pointer_plus_def_rhs_integer_constant (tree pointer, + tree integer_constant, + const_tree old_type) +{ + gcc_assert (TREE_CODE (integer_constant) == INTEGER_CST); + tree pointer_type = TREE_TYPE (pointer); + gcc_assert (TREE_CODE (pointer_type) == POINTER_TYPE); + + const_tree base_type = TREE_TYPE (TREE_TYPE (pointer)); + + tree size = TYPE_SIZE_UNIT (base_type); + int new_struct_size = tree_to_shwi (size); + int old_offset = tree_to_uhwi (integer_constant); + + const_tree old_base_type = TREE_TYPE (old_type); + + tree old_type_size = TYPE_SIZE_UNIT (old_base_type); + int old_struct_size = tree_to_shwi (old_type_size); + int modulo = old_offset % old_struct_size; + test_log ("old_struct_size = %d", 0, old_struct_size); + + tree integer_constant_type = TREE_TYPE (integer_constant); + + int new_offset = old_offset / old_struct_size * new_struct_size; + tree new_constant = build_int_cst (integer_constant_type, new_offset); + return new_constant; +} + +static bool +rewrite_pointer_plus_def_rhs (gimple *stmt, gimple_stmt_iterator &gsi, tree lhs, + tree op0, tree op1, + t_map &type_map, + t_map &inverse) +{ + gcc_assert (op0 && op1); + tree op0_type = TREE_TYPE (lhs); + //const_tree *new_op0_type = type_map.get (op0_type); + delete_info_t *delete_info = type_map.get (op0_type); + const_tree *new_op0_type = delete_info ? &(delete_info->new_record) : NULL; + test_log ("rewrite_pointer_plus has old_type %s", 0, + new_op0_type ? "true" : "false"); + // gcc_assert(!new_op0_type); + test_log ("rewrite_pointer_plus op0_type %s", 0, get_type_name (op0_type)); + if (new_op0_type) + test_log ("rewrite_pointer_plus new_op0_type %s", 0, + get_type_name (*new_op0_type)); + + rewrite_expr (op0, type_map, 0); + rewrite_expr (op1, type_map, 0); + + const char *new_type_name + = new_op0_type ? get_type_name (*new_op0_type) : get_type_name (op0_type); + + test_log ("rewrite_pointer_plus new_type_name %s", 0, new_type_name); + //const_tree *inverse_type_ptr = inverse.get (op0_type); + delete_info_t *delete_info_inverse = inverse.get(op0_type); + const_tree *inverse_type_ptr = delete_info_inverse ? &(delete_info_inverse->old_record) : NULL; + bool not_in_map_nor_inverse = !new_op0_type && !inverse_type_ptr; + test_log ("continuing? %s", 0, not_in_map_nor_inverse ? "false" : "true"); + if (not_in_map_nor_inverse) + return false; + + const_tree old_type + = new_op0_type ? (const_tree) op0_type : *inverse_type_ptr; + test_log ("what is the old_type name ? %s", 0, get_type_name (old_type)); + + bool is_op1_int_cst = TREE_CODE (op1) == INTEGER_CST; + tree integer_constant = is_op1_int_cst ? op1 : op0; + bool has_integer_constant = (TREE_CODE (integer_constant) == INTEGER_CST); + + if (has_integer_constant) + { + // I want to know if this is an invariant. + // TODO: Outline + gcc_assert (TREE_CODE (op1) == INTEGER_CST); + tree pointer = is_op1_int_cst ? op0 : op1; + tree new_constant + = rewrite_pointer_plus_def_rhs_integer_constant (pointer, + integer_constant, + old_type); + unsigned int operand = is_op1_int_cst ? 2 : 1; + gimple_set_op (stmt, operand, new_constant); + return true; + } + + tree variable = op1; + tree pointer = op0; + // I want to know if this is an invariant. + tree pointer_type = TREE_TYPE (pointer); + gcc_assert (TREE_CODE (pointer_type) == POINTER_TYPE); + tree new_variable + = rewrite_pointer_plus_def_rhs_variable_replace_constants_implementation ( + gsi, lhs, pointer, variable, old_type); + if (!new_variable) + return false; + unsigned int operand + = TREE_CODE (TREE_TYPE (op0)) == POINTER_TYPE ? 2 : 1; + gimple_set_op (stmt, operand, new_variable); + + return true; +} + +static bool +rewrite_pointer_diff_def_rhs (gimple_stmt_iterator &gsi, tree lhs, + tree op0, tree op1, + t_map &type_map, + t_map &inverse) +{ + gcc_assert (op0 && op1); + tree op0_type = TREE_TYPE (lhs); + //const_tree *new_op0_type = type_map.get (op0_type); + delete_info_t *delete_info = type_map.get(op0_type); + const_tree *new_op0_type = delete_info ? &(delete_info->new_record) : NULL; + test_log ("rewrite_pointer_diff has old_type %s", 0, + new_op0_type ? "true" : "false"); + test_log ("rewrite_pointer_diff op0_type %s", 0, get_type_name (op0_type)); + if (new_op0_type) + test_log ("rewrite_pointer_diff new_op0_type %s", 0, + get_type_name (*new_op0_type)); + + rewrite_expr (op0, type_map, 0); + rewrite_expr (op1, type_map, 0); + + const char *new_type_name + = new_op0_type ? get_type_name (*new_op0_type) : get_type_name (op0_type); + // TODO: I'd prefer to have a parameter + // that tells me what to change + // several stack frames above. + test_log ("rewrite_pointer_diff new_type_name %s", 0, new_type_name); + //const_tree *inverse_type_ptr = inverse.get (op0_type); + delete_info_t *delete_info_inverse = inverse.get (op0_type); + const_tree *inverse_type_ptr = delete_info_inverse ? &(delete_info_inverse->old_record) : NULL; + bool not_in_map_nor_inverse = !new_op0_type && !inverse_type_ptr; + test_log ("continuing? %s", 0, not_in_map_nor_inverse ? "false" : "true"); + if (not_in_map_nor_inverse) + return false; + + const_tree old_type + = new_op0_type ? (const_tree) op0_type : *inverse_type_ptr; + + bool is_op1_int_cst = TREE_CODE (op1) == INTEGER_CST; + tree integer_constant = is_op1_int_cst ? op1 : op0; + bool has_integer_constant = (TREE_CODE (integer_constant) == INTEGER_CST); + + // I want to know if this is an invariant. + if (has_integer_constant) + gcc_unreachable (); + + // I want to know if this is an invariant. + gcc_assert (TREE_CODE (op0_type) == POINTER_TYPE); + rewrite_pointer_diff_def_rhs_variable_replace_constants_implementation ( + gsi, lhs, op0, op1, old_type); + + return true; +} +static bool +rewrite_assign_rhs (gimple *stmt, gimple_stmt_iterator &gsi, + t_map &type_map, + t_map &inverse) +{ + // WONTFIX, It looks like there is no way to obtain + // the expression for the rhs. So instead, we have to do + // this hack. + // Which expressions can be toplevel? + bool is_stmt_rewritten = false; + enum tree_code code = gimple_expr_code (stmt); + tree lhs = gimple_assign_lhs (stmt); + switch (TREE_CODE(lhs)) + { + case COMPONENT_REF: + test_log(" I AM HERE HERE HERE", 0); + break; + default: + break; + } + + switch (code) + { + case POINTER_PLUS_EXPR: + { + tree rhs2 = gimple_assign_rhs2 (stmt); + tree rhs1 = gimple_assign_rhs1 (stmt); + tree lhs = gimple_assign_lhs (stmt); + tree lhs_type = TREE_TYPE (lhs); + gcc_assert (lhs_type); + delete_info_t *delete_info = type_map.get (lhs_type); + gcc_assert (lhs && rhs2 && rhs1); + bool retval = rewrite_pointer_plus_def_rhs (stmt, gsi, lhs, rhs1, rhs2, + type_map, inverse); + if (delete_info) + substitute_type(lhs, delete_info->new_record); + return retval; + } + case POINTER_DIFF_EXPR: + { + tree rhs2 = gimple_assign_rhs2 (stmt); + tree rhs1 = gimple_assign_rhs1 (stmt); + tree lhs = gimple_assign_lhs (stmt); + tree lhs_type = TREE_TYPE (lhs); + gcc_assert (lhs_type); + delete_info_t *delete_info = type_map.get (lhs_type); + gcc_assert (lhs && rhs2 && rhs1); + bool retval = rewrite_pointer_diff_def_rhs (gsi, lhs, rhs1, rhs2, + type_map, inverse); + if (delete_info) + substitute_type(lhs, delete_info->new_record); + return retval; + } + break; + default: + { + //print_gimple_stmt (dump_file, stmt, 0); + test_log ("DEFAULT HERE: %s", 0, get_tree_code_name (code)); + } + break; + } + + switch (gimple_assign_rhs_class (stmt)) + { + case GIMPLE_TERNARY_RHS: + { + tree rhs3 = gimple_assign_rhs3 (stmt); + is_stmt_rewritten |= rewrite_expr (rhs3, type_map, 0); + } + case GIMPLE_BINARY_RHS: + { + tree rhs2 = gimple_assign_rhs2 (stmt); + is_stmt_rewritten |= rewrite_expr (rhs2, type_map, 0); + } + case GIMPLE_SINGLE_RHS: + case GIMPLE_UNARY_RHS: + { + tree rhs1 = gimple_assign_rhs1 (stmt); + is_stmt_rewritten |= rewrite_expr (rhs1, type_map, 0); + } + break; + default: + gcc_unreachable (); + break; + } + + return is_stmt_rewritten; +} + +static void +rewrite_call_lhs (gimple *stmt, __attribute__((unused)) gimple_stmt_iterator &gsi, + t_map &type_map) +{ + gcc_assert (stmt); + test_print_gimple_stmt (stmt); + tree lhs = gimple_call_lhs (stmt); + // LHS can be null. Example `foo()` + if (!lhs) + return; + + rewrite_expr (lhs, type_map, 0); +} + +static void +rewrite_call_rhs (gimple *stmt, __attribute__((unused)) gimple_stmt_iterator &gsi, + t_map &type_map) +{ + gcc_assert (stmt); + tree fn = gimple_call_fn (stmt); + gcc_assert (fn); + gcall *call = dyn_cast<gcall *> (stmt); + tree return_type = gimple_call_return_type (call); + test_log ("what is the return type %s", 0, get_type_name (return_type)); + gcc_assert (return_type); + + unsigned args = gimple_call_num_args (stmt); + for (unsigned i = 0; i < args; i++) + { + tree arg = gimple_call_arg (stmt, i); + rewrite_expr (arg, type_map, 0); + } +} + +static void +rewrite_call (gimple *stmt, gimple_stmt_iterator &gsi, + t_map &type_map) +{ + rewrite_call_lhs (stmt, gsi, type_map); + rewrite_call_rhs (stmt, gsi, type_map); +} + +static void +rewrite_cond (gimple *stmt, __attribute((unused)) gimple_stmt_iterator &gsi, + t_map &type_map) +{ + tree lhs = gimple_cond_lhs (stmt); + tree rhs = gimple_cond_rhs (stmt); + rewrite_expr (lhs, type_map, 0); + rewrite_expr (rhs, type_map, 0); +} + +static void +rewrite_phi (gimple *stmt, __attribute__((unused)) gimple_stmt_iterator &gsi, + t_map &type_map) +{ + test_log ("rewriting phi result", 0); + tree ssa_result = gimple_phi_result (stmt); + rewrite_expr (ssa_result, type_map, 0); + unsigned args = gimple_phi_num_args (stmt); + for (unsigned i = 0; i < args; i++) + { + test_log ("rewriting phi arg", 0); + tree arg = gimple_phi_arg_def (stmt, i); + rewrite_expr (arg, type_map, 0); + } +} + +static bool +rewrite_assign_lhs (gimple *stmt, gimple_stmt_iterator &gsi, t_map &type_map, t_map &inverse) +{ + tree lhs = gimple_assign_lhs(stmt); + enum tree_code code = TREE_CODE(lhs); + bool is_component_ref = code == COMPONENT_REF; + if (is_component_ref) + { + return rewrite_component_ref_def_lhs(gsi, lhs, type_map, 0); + } + + rewrite_expr(lhs, type_map, 0); + return false; +} + +static bool +rewrite_assign (gimple *stmt, gimple_stmt_iterator &gsi, + t_map &type_map, + t_map &inverse) +{ + gcc_assert (stmt); + test_print_gimple_stmt (stmt); + bool deleted = rewrite_assign_lhs (stmt, gsi, type_map, inverse); + if (deleted) { + unlink_stmt_vdef (stmt); + gsi_remove(&gsi, true); + return deleted; + } + rewrite_assign_rhs (stmt, gsi, type_map, inverse); + test_print_gimple_stmt (stmt); + return false; +} + +static bool +rewrite_return (gimple *stmt, gimple_stmt_iterator &gsi, + t_map &type_map) +{ + gcc_assert (stmt); + test_print_gimple_stmt (stmt); + greturn *retisn = dyn_cast<greturn *> (stmt); + gcc_assert(retisn); + tree retval = gimple_return_retval(retisn); + if (!retval) return false; + + tree decl = DECL_RESULT (current_function_decl); + rewrite_expr(decl, type_map, 0); + + rewrite_expr (retval, type_map, 0); + tree retval_type = TREE_TYPE(retval); + gcc_assert(retval_type); + log("return type %s\n", get_type_name(retval_type)); + return false; +} + +static bool +rewrite_stmt (gimple *stmt, gimple_stmt_iterator &gsi, + t_map &type_map, + t_map &inverse) +{ + gcc_assert (stmt); + const enum gimple_code code = gimple_code (stmt); + bool deleted = false; + switch (code) + { + case GIMPLE_ASSIGN: + deleted = rewrite_assign (stmt, gsi, type_map, inverse); + break; + case GIMPLE_CALL: + rewrite_call (stmt, gsi, type_map); + break; + case GIMPLE_COND: + rewrite_cond (stmt, gsi, type_map); + break; + case GIMPLE_PHI: + rewrite_phi (stmt, gsi, type_map); + break; + case GIMPLE_RETURN: + rewrite_return (stmt, gsi, type_map); + break; + default: + { + const char *const gimple_code_str = gimple_code_name[code]; + test_log ("gimple_code %s", 0, gimple_code_str); + test_print_gimple_stmt (stmt); + } + break; + } + return deleted; +} + +static void +rewrite_basic_block (basic_block bb, t_map &type_map, + t_map &inverse) +{ + gcc_assert (bb); + bool is_first = true; + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + bool deleted = rewrite_stmt (stmt, gsi, type_map, inverse); + + if (deleted && !is_first) + { + gsi_prev(&gsi); + } + + while (deleted && is_first) + { + stmt = gsi_stmt(gsi); + if (!stmt) break; // we might have deleted and was end of basic block? + deleted = rewrite_stmt (stmt, gsi, type_map, inverse); + if (gsi_end_p(gsi)) break; + } + + if (deleted && gsi_end_p (gsi)) break; + + is_first = false; + } + for (gimple_stmt_iterator gsi = gsi_start_phis (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + rewrite_stmt (stmt, gsi, type_map, inverse); + } +} + +static void +rewrite_function_body (cgraph_node *cnode, + t_map &type_map, + t_map &inverse) +{ + gcc_assert (cnode); + const char* name_s = cnode->name(); + const char* dot = strstr(name_s, "."); // Updated functions have a dot on them. + // If there's a dot, we need to do a memcpy + // of the string name[0:dot] + size_t n = dot ? dot - name_s : strlen(name_s); + char precanon[100]; + const char* canonical = dot ? strncpy(precanon, name_s, n) : name_s; + bool already_processed = updated_functions.find(name_s) != updated_functions.end(); + log("original name %s\n", name_s); + log("canonical name %s\n", canonical); + log("already processed ? %s\n", already_processed ? "true" : "false"); + if (already_processed) return; + + updated_functions.insert(name_s); + cnode->get_untransformed_body (); + basic_block bb = NULL; + function *func = DECL_STRUCT_FUNCTION (cnode->decl); + push_cfun (func); + FOR_EACH_BB_FN (bb, func) + { + rewrite_basic_block (bb, type_map, inverse); + } + // TODO: + // can we move this logic into the above? + // Thanks! + size_t i; + tree name; + FOR_EACH_SSA_NAME (i, name, cfun) + { + if (SSA_NAME_VAR (name) != NULL_TREE + && TREE_TYPE (name) != TREE_TYPE (SSA_NAME_VAR (name))) + { + TREE_TYPE (name) = TREE_TYPE (SSA_NAME_VAR (name)); + test_log ("changing to %s", 0, + get_type_name (TREE_TYPE (SSA_NAME_VAR (name)))); + } + } + pop_cfun (); + print_function (cnode); +} + +static void +rewrite_local_decl (tree var_decl, t_map &type_map) +{ + gcc_assert (var_decl); + gcc_assert (TREE_CODE (var_decl) == VAR_DECL); + const_tree type = TREE_TYPE (var_decl); + gcc_assert (type); + delete_info_t *delete_info = type_map.get (type); + + if (!delete_info) + return; + + + const_tree new_type = delete_info->new_record; + gcc_assert (new_type); + test_write ("rewriting,local_decl"); + test_print_generic_decl (var_decl); + test_write (","); + //TODO: Doing an experiment, we might need to keep qualifications... + substitute_type(var_decl, new_type); + + // Keep this relayout_decl + relayout_decl (var_decl); + test_print_generic_decl (var_decl); + test_write ("\n"); +} + +static void +rewrite_local_decls (cgraph_node *cnode, + t_map &type_map) +{ + gcc_assert (cnode); + tree decl = cnode->decl; + gcc_assert (decl); + gcc_assert (TREE_CODE (decl) == FUNCTION_DECL); + function *func = DECL_STRUCT_FUNCTION (decl); + gcc_assert (func); + tree var_decl = NULL; + int i = 0; + FOR_EACH_LOCAL_DECL (func, i, var_decl) + { + rewrite_local_decl (var_decl, type_map); + } + print_function (cnode); +} + +static void +rewrite_function_parms (tree expr, t_map &type_map, + const int indent) +{ + enum tree_code code = TREE_CODE (expr); + bool is_function_decl = code == FUNCTION_DECL; + gcc_assert (is_function_decl); + + for (tree parm = DECL_ARGUMENTS (expr); parm; parm = DECL_CHAIN (parm)) + { + tree parm_type = TREE_TYPE (parm); + delete_info_t *delete_info = type_map.get (parm_type); + if (!delete_info) + continue; + + const_tree new_type = delete_info->new_record; + test_log ("before rewrite_function_arg %s", indent, get_type_name (parm_type)); + substitute_type(parm, new_type); + relayout_decl (parm); + tree parm_type_2 = TREE_TYPE (parm); + test_log ("after rewrite_function_arg %s", indent, + get_type_name (parm_type_2)); + } +} + +static void +rewrite_function_parms (cgraph_node *cnode, + t_map &type_map) +{ + rewrite_function_parms (cnode->decl, type_map, 0); +} + +static void +rewrite_function_return_type (tree expr, + t_map &type_map, + const int indent) +{ + enum tree_code code = TREE_CODE (expr); + bool is_function_decl = code == FUNCTION_DECL; + gcc_assert (is_function_decl); + + tree function_type = TREE_TYPE (expr); + + // TODO: We do not support method's yet. + gcc_assert (TREE_CODE (function_type) == FUNCTION_TYPE); + tree function_return_type = TREE_TYPE (function_type); + gcc_assert (function_return_type); + + test_log ("before rewrite_function_return_type %s", indent, + get_type_name (function_return_type)); + delete_info_t *delete_info = type_map.get (function_return_type); + if (!delete_info) + return; + + const_tree new_type = delete_info->new_record; + tree new_type_2 = build_type_variant(const_to_tree(new_type), TYPE_READONLY(function_return_type), TYPE_VOLATILE(function_return_type)); + substitute_type(function_type, new_type_2); + // Do not use relayout_decl(cnode->decl); + tree function_type_2 = TREE_TYPE (expr); + tree function_return_type_2 = TREE_TYPE (function_type_2); + test_log ("after rewrite_function_return_type %s", indent, + get_type_name (function_return_type_2)); +} + +static void +rewrite_function_return_type (cgraph_node *cnode, + t_map &type_map) +{ + gcc_assert (TREE_CODE (cnode->decl) == FUNCTION_DECL); + tree function_type = TREE_TYPE (cnode->decl); + gcc_assert (function_type); + rewrite_function_return_type (cnode->decl, type_map, 0); +} + +static void +rewrite_global_decl (varpool_node *vnode, + t_map &type_map) +{ + int i; + struct ipa_ref *ref; + + for (i = 0; vnode->iterate_referring (i, ref); i++) + { + test_log ("rewriting global declaration", 0); + rewrite_local_decl (vnode->decl, type_map); + test_print_generic_decl (vnode->decl); + } +} + +static void +rewrite_global_decls ( t_map &type_map) +{ + varpool_node *vnode; + FOR_EACH_VARIABLE (vnode) + { + rewrite_global_decl (vnode, type_map); + } +} + +static void +rewrite_function (cgraph_node *cnode, + t_map &type_map, + t_map &inverse) +{ + // rewrite_function_body should be before + // rewrite_local_decls to make work easier. + // Why? Because if we rewrite local decl types + // first, then we might need to look for + // old AND new types in rewrite_function_body. + // If we rewrite local decl types later, + // then in rewrite_function_body we only + // need to look for old types. + rewrite_function_parms (cnode, type_map); + rewrite_function_return_type (cnode, type_map); + rewrite_function_body (cnode, type_map, inverse); + rewrite_local_decls (cnode, type_map); +} + +static void +rewrite_functions ( t_map &type_map, + t_map &inverse) +{ + cgraph_node *cnode = NULL; + FOR_EACH_DEFINED_FUNCTION (cnode) + { + rewrite_function (cnode, type_map, inverse); + } +} + +static void +rewrite_references_to_modified_structs ( + t_map &type_map, + t_map &inverse) +{ + rewrite_functions (type_map, inverse); + rewrite_global_decls (type_map); +} + +bool +compute_inverse_type_map_internal ( + const_tree const &old_type, delete_info_t *delete_info, + t_map *inverse_map) +{ + gcc_assert (inverse_map && delete_info); + const_tree new_type = delete_info->new_record; + inverse_map->put (delete_info->new_record, *delete_info); + const char *old_identifier = get_type_name (old_type); + const char *new_identifier = get_type_name (new_type); + test_log ("inverse,%s,%s", 0, new_identifier, old_identifier); + return true; +} + +static void +compute_inverse_type_map ( t_map &mod_type_map, + t_map &inverse_map) +{ + mod_type_map.traverse< t_map *, + compute_inverse_type_map_internal> (&inverse_map); +} + + +static void +separate_set_of_pairs_into_pair_of_sets(const record_field_set &set_of_pairs, std::set<const char*, cstrless> &one, std::set<const char*, cstrless> &two) +{ + + for (auto it = set_of_pairs.cbegin(); it != set_of_pairs.cend() ; ++it) + { + fields record_field_pair = *it; + const_tree record = record_field_pair.first; + if (strcmp(get_record_name(record), "<name_tree_is_null>") == 0) continue; + const_tree field = record_field_pair.second; + one.insert(get_type_name(record)); + one.insert(get_type_name(TYPE_MAIN_VARIANT(record))); + two.insert(get_field_name(field)); + char *buffer; + char *buffer2; + asprintf(&buffer, "%s.%s", get_type_name(record), get_field_name(field)); + asprintf(&buffer2, "%s.%s", get_type_name(TYPE_MAIN_VARIANT(record)), get_field_name(field)); + log("putting in %s\n", buffer); + log("putting in %s\n", buffer2); + record_field_pair_name.insert(buffer); + record_field_pair_name.insert(buffer2); + } +} + +static void +iphw_execute_escape_analysis() +{ + gcc_assert(!flag_ipa_typelist_struct && !flag_ipa_typelist_field); + const record_field_set to_reorg = get_fields_to_reorg(); + separate_set_of_pairs_into_pair_of_sets(to_reorg, interesting_records, interesting_fields); + + hash_set<const_tree> orig_type_map; + collect_orig_structs (orig_type_map); + t_map mod_type_map; + compute_modified_structs (orig_type_map, mod_type_map); + t_map inverse_type_map; + compute_inverse_type_map (mod_type_map, inverse_type_map); + rewrite_references_to_modified_structs (mod_type_map, inverse_type_map); +} + +static void +iphw_execute_cli_args() +{ + gcc_assert(flag_ipa_typelist_struct && flag_ipa_typelist_field); + + hash_set<const_tree> orig_type_map; + collect_orig_structs (orig_type_map); + + // So what would a client will have to implement? + // * Pass a set of types + // * A function that translates old_types to new_types + // class TypeRewriter { + // // A set might not be + // // sufficient to say **how** the types should + // // be modified + // TypeRewriter(set, function type -> new_type); + // ~TypeRewriter(); // allows me to free memory used during + // // the pass easily + // rewrite(stmt); // maybe no since it can corrupt the function? + // rewrite(bb); // maybe no since it can corrupt the function? + // rewrite(func); + // rewrite(set(functions)); + // } + t_map mod_type_map; + compute_modified_structs (orig_type_map, mod_type_map); + t_map inverse_type_map; + compute_inverse_type_map (mod_type_map, inverse_type_map); + rewrite_references_to_modified_structs (mod_type_map, inverse_type_map); +} + +static unsigned int +iphw_execute () +{ + test_log ("Executing structreorg", 0); + const bool perform_escape_analysis = !flag_ipa_typelist_struct && !flag_ipa_typelist_field ; + perform_escape_analysis ? iphw_execute_escape_analysis() : iphw_execute_cli_args(); + return 0; +} + +int +str_reorg_dead_field_eliminate (__attribute__((unused)) Info *info) +{ + gcc_assert (in_lto_p); + return iphw_execute (); +} + +*/ + +#if USE_NEW_INTERFACE +int +str_reorg_dead_field_eliminate_qual (Info *info) +{ + GimpleCaster caster(info->sets); + caster.walk(); + // sets here now holds the types that + // are casted... + // So, maybe we want to print them? + caster.print_reasons(); + ptrset_t sets = caster.get_sets(); + return 0; +} +int +str_reorg_dead_field_eliminate_trans (Info *info) +{ + return 0; +} +#endif + + diff --git a/gcc/ipa-str-reorg-field-reorder.c b/gcc/ipa-str-reorg-field-reorder.c new file mode 100644 index 00000000000..67e2b147718 --- /dev/null +++ b/gcc/ipa-str-reorg-field-reorder.c @@ -0,0 +1,60 @@ +/* Interprocedural scalar replacement of aggregates + Copyright (C) 2019-2020 Free Software Foundation, Inc. + + Contributed by Your Name & Email address go here + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "tree.h" +#include "gimple.h" +#include "tree-pass.h" +#include "cgraph.h" +#include "gimple-iterator.h" +#include "pretty-print.h" +#include <vector> +#include <map> +#include <set> +#include "ipa-structure-reorg.h" +#include "dumpfile.h" +#include "tree-pretty-print.h" +#include "gimple-pretty-print.h" +#include "langhooks.h" + +int +str_reorg_field_reorder (Info *info) +{ + // TBD + // DEBUG ( "Running str_reorg_field_reorder\n"); + return 0; +} + +#if USE_NEW_INTERFACE +int +str_reorg_field_reorder_qual (Info *info) +{ + return 0; +} +int +str_reorg_field_reorder_trans (Info *info) +{ + return 0; +} +#endif diff --git a/gcc/ipa-str-reorg-instance-interleave.c b/gcc/ipa-str-reorg-instance-interleave.c new file mode 100644 index 00000000000..1d0213f1427 --- /dev/null +++ b/gcc/ipa-str-reorg-instance-interleave.c @@ -0,0 +1,2536 @@ +/* Interprocedural structure reorganization + Copyright (C) 2019-2020 Free Software Foundation, Inc. + + Contributed by Gary Oblock <gary@amperecomputing.com> + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "tree.h" +#include "tree-ssa.h" +#include "tree-dfa.h" +#include "gimple.h" +#include "tree-pass.h" +#include "cgraph.h" +#include "gimple-iterator.h" +#include "pretty-print.h" +#include <vector> +#include <map> +#include <set> +#include "ipa-structure-reorg.h" +#include "dumpfile.h" +#include "tree-pretty-print.h" +#include "gimple-pretty-print.h" +#include "langhooks.h" +#include "stringpool.h" +#include "stor-layout.h" +#include "diagnostic-core.h" +#include "ssa.h" +#include "tree-ssanames.h" +#include "cfghooks.h" +#include "function.h" +#include "cfgloop.h" +#include "wide-int.h" + +static void wrangle_ssa_type( tree, Info_t*); +//static bool print_internals (gimple *, void *); +static void str_reorg_instance_interleave_qual_part ( Info *); +static void str_reorg_instance_interleave_type_part ( Info *); +static void header ( bool); +static double cut_off_eq_single_pool( double); +static double alignment_effect( unsigned HOST_WIDE_INT); +static void create_new_types ( Info_t *); +static void create_a_new_type ( Info_t *, tree); +static unsigned int reorg_perf_qual ( Info *); +static tree find_coresponding_field ( tree, tree); +static void remove_default_def ( tree, struct function *); +static void set_lhs_for ( gimple *, tree); +static basic_block make_bb ( char *, basic_block); + +// These are local to this file by design +#define REORG_SP_PTR_PREFIX "_reorg_SP_ptr_type_" +#define REORG_SP_PREFIX "_reorg_base_type_" +#define REORG_SP_BASE_PREFIX "_reorg_base_var_" + +// TBD Delete all this after sending a note on it +// to the gcc mailing list. +#define USE_BUILT_IN_FREE 1 + +/* +0000 ssa_verify error loc_3 +0001 tree_class_check err in interleave +0010 ssa_verify error arr_46 +0011 ssa_verify error loc_3 +0100 ssa_verify error loc_3 +0101 ssa_verify error arr_46 +0110 ssa_verify error arr_46 +0111 ssa_verify error arr_46 +1000 ssa_verify error loc_3 +1001 bad failure: walk return bogus op +1010 bad failure: walk return bogus op +1011 bad failure: walk return bogus op +1100 bad failure: walk return bogus op +1101 bad failure: walk return bogus op +1110 bad failure: walk return bogus op +1111 bad failure: walk return bogus op +*/ + +// These are dummy values tha alway result the reorganization +#define SINGLE_POOL_RAW_SKIP_IT 0.0 +#define SINGLE_POOL_RAW_DO_IT_ALWAYS 0.0 +#define SINGLE_POOL_ABS_SKIP_IT 0.0 +#define SINGLE_POOL_ABS_DO_IT_ALWAYS 0.0 + +int +str_reorg_instance_interleave_qual ( Info *info) +{ + // this is the qualification code for instance interleaving + // + str_reorg_instance_interleave_qual_part ( info); + + // this modifiies the qualified types. + // + str_reorg_instance_interleave_type_part ( info); + return 0; +} + +int +str_reorg_instance_interleave_trans ( Info *info) +{ + if ( info->show_all_reorg_cands ) + { + fprintf ( info->reorg_dump_file, "Start of str_reorg_instance_interleave_trans:\n"); + print_program ( info->reorg_dump_file, PRINT_FORMAT, 4, info); + } + + DEBUG ("INTERNALS PRINT\n"); + DEBUG_F (apply_to_all_gimple, print_internals, true, (void *)info); + + struct cgraph_node *node; + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) { + struct function *func = DECL_STRUCT_FUNCTION ( node->decl); + // Ulgy GCC idiom with global pointer to current function. + push_cfun ( func); + if ( info->show_transforms ) + { + fprintf( info->reorg_dump_file, "Function \"%s\":\n", + //IDENTIFIER_POINTER( DECL_NAME( func))); + //IDENTIFIER_POINTER( DECL_NAME( func->decl))); + lang_hooks.decl_printable_name ( node->decl, 2)); + } + + basic_block bb; + FOR_EACH_BB_FN ( bb, func) + { + + if( info->show_transforms ) + { + fprintf( info->reorg_dump_file, " Transforming BB%i:\n", + bb->index); + } + + gimple_stmt_iterator outer_gsi; + gimple_stmt_iterator next_gsi; + for ( outer_gsi = gsi_start_bb ( bb); !gsi_end_p ( outer_gsi); outer_gsi = next_gsi ) + { + next_gsi = outer_gsi; + gsi_next ( &next_gsi); + // Every statement that uses a reorg type needs to + // be examined. Some are harmless and are skipped + // whereas others are transformed. However, anything + // else is an error. + gimple *stmt = gsi_stmt ( outer_gsi); + ReorgType_t *ri = contains_a_reorgtype( stmt, info); + if ( ri == NULL ) + { + //DEBUG_L("No Transfrom on: "); + //DEBUG_F( print_gimple_stmt, stderr, stmt, 4, TDF_SLIM); + } + else + { + //DEBUG_F( print_reorg_with_msg, stderr, ri, 0, + // "reorg from str_reorg_instance_interleave_trans"); + + enum ReorgTransformation trans = + reorg_recognize ( stmt, node, info); + // print out trans and stmt if dumping + if ( info->show_transforms ) + { + print_gimple_stmt( info->reorg_dump_file, stmt, 0); + } + + switch ( trans) + { + case ReorgT_StrAssign: + DEBUG_L("ReorgT_StrAssign\n"); + // TBD + /* + tree lhs = gimple_assign_lhs( stmt); + tree rhs = gimple_assign_rhs( stmt); + ReorgOpTrans lope = recognize_op( lhs, info); + ReorgOpTrans rope = recognize_op( rhs, info); + for each field in ri { + // lhs: ReorgT_Array & rhs ReorgT_Struct, ReorgT_Deref, ReorgT_Array + // lhs: ReorgT_Struct & rhs ReorgT_Deref, ReorgT_Array + // lhs ReorgT_Deref & rhs ReorgT_Struct, ReorgT_Array, ReorgT_Deref + A is new ssa + // Gimple for loading this element + // Question? What if the element is large? Answer is it's OK. + switch( rope) { + // Not implemented in single pool + //case ReorgT_Array: + case ReorgT_Struct: + generate A <- rhs.field + break; + case ReorgT_Deref: + B,C is new SSA + // Note simplification with type_name( rhs) + generate B <- concat( REORG_SP_PREFIX, type_name( rhs)) + and insert before stmt + generate C <- B->"f" + and insert before stmt + generate A <- C[rhs] + and insert before stmt + break + default: + internal_error( + "Reached operand default in RHS enum ReorgOpTrans"); + } + // Gimple for storing this element + switch( lope) + // Not implemented in single pool + //case ReorgT_Array: + case ReorgT_Deref: + B,C is new SSA + // Note simplification with type_name( lhs) + generate B <- concat( REORG_SP_PREFIX, type_name( lhs)) + and insert before stmt + generate C <- B->"f" + and insert before stmt + // lhs here is a simplification + generate A <- C[lhs] + and insert before stmt + break; + case ReorgT_Struct: + generate lhs.field <- A + break; + default: + internal_error( + "Reached operand default in LHS enum ReorgOpTrans"); + } + } + */ + break; + case ReorgT_ElemAssign: + { + //break; + gimple_stmt_iterator gsi = gsi_for_stmt( stmt); + + DEBUG_L("ReorgT_ElemAssign: "); + DEBUG_F( print_gimple_stmt, stderr, stmt, 0); + INDENT(2); + // Needed for helloworld + tree lhs = gimple_assign_lhs( stmt); + tree rhs = gimple_assign_rhs1( stmt); + + bool ro_on_left = tree_contains_a_reorgtype_p ( lhs, info); + + tree ro_side = ro_on_left ? lhs : rhs; + tree nonro_side = ro_on_left ? rhs : lhs; + + switch ( recognize_op ( ro_side, info) ) // "a->f" + { + case ReorgOpT_Indirect: + { + tree orig_field = TREE_OPERAND( ro_side, 1); + tree field_type = TREE_TYPE( orig_field); + tree base = ri->instance_interleave.base; + + tree base_field = + find_coresponding_field ( base, orig_field); + + tree base_field_type = TREE_TYPE( base_field); + + gcc_assert ( field_type); + tree field_val_temp = + make_temp_ssa_name( field_type, NULL, "field_val_temp"); + + tree inner_op = TREE_OPERAND( ro_side, 0); + inner_op = TREE_OPERAND( inner_op, 0); + //DEBUG_L("inner_op: "); + //DEBUG_F( print_generic_expr, stderr, inner_op, (dump_flags_t)0); + //DEBUG("\n"); + + // For either case generate common code: + + // field_array = _base.f + gcc_assert ( base_field_type); + tree field_arry_addr = + make_temp_ssa_name( base_field_type, NULL, "field_arry_addr"); + + tree rhs_faa = build3 ( COMPONENT_REF, + // ??? + base_field_type, + //ptr_type_node, // This seems bogus + base, + base_field, + // This almost certainly is bogus + // If this "works" the the types + // of fields are messed up. + //orig_field, + NULL_TREE); + + // Use this to access the array of element. + gimple *get_field_arry_addr = + gimple_build_assign( field_arry_addr, rhs_faa); + SSA_NAME_DEF_STMT ( field_arry_addr) = get_field_arry_addr; + + // index = a + gcc_assert ( sizetype); + tree index = + make_temp_ssa_name( sizetype, NULL, "index"); + gimple *get_index = + gimple_build_assign( index, CONVERT_EXPR, inner_op); + SSA_NAME_DEF_STMT ( index) = get_index; + + gimple *temp_set; + gimple *final_set; + + // offset = index * size_of_field + tree size_of_field = TYPE_SIZE_UNIT ( base_field_type); + gcc_assert ( sizetype); + tree offset = make_temp_ssa_name( sizetype, NULL, "offset"); + + gimple *get_offset = gimple_build_assign ( offset, MULT_EXPR, index, size_of_field); + SSA_NAME_DEF_STMT ( offset) = get_offset; + + // field_addr = field_array + offset + gcc_assert ( base_field_type); + tree field_addr = + make_temp_ssa_name( base_field_type, NULL, "field_addr"); + + gimple *get_field_addr = + gimple_build_assign ( field_addr, POINTER_PLUS_EXPR, field_arry_addr, offset); + SSA_NAME_DEF_STMT ( field_addr) = get_field_addr; + + if ( ro_on_left ) + { + // With: a->f = rhs + // Generate: + + // temp = rhs + temp_set = gimple_build_assign( field_val_temp, rhs); + SSA_NAME_DEF_STMT ( field_val_temp) = temp_set; + + //// field_array[index] = temp + //tree elem_to_set = + // build4 ( ARRAY_REF, field_type, field_arry_addr, index, + // NULL_TREE, NULL_TREE); + //final_set = + // gimple_build_assign( elem_to_set, field_val_temp); + + // *field_addr = temp + tree lhs_ref = build2 ( MEM_REF, field_type, field_addr, + build_int_cst (ptr_type_node, 0)); + + final_set = + gimple_build_assign( lhs_ref, field_val_temp); + } + else + { + // With: lhs = a->f + // Generate: + + // Tried other idioms here (tricky) + tree rhs_ref = build2 ( MEM_REF, field_type, field_addr, + build_int_cst (ptr_type_node, 0)); + + + // If these will actually print then things are likely sane + //DEBUG_L("rhs_ref: "); + //DEBUG_F(print_generic_expr, stderr, rhs_ref, (dump_flags_t)0); + //DEBUG("\n"); + + tree op0 = TREE_OPERAND ( rhs_ref, 0); + tree op1 = TREE_OPERAND ( rhs_ref, 1); + tree op1type = TYPE_MAIN_VARIANT (TREE_TYPE (op1)); + tree op1type_type = TREE_TYPE ( op1type); + + temp_set = + gimple_build_assign( field_val_temp, rhs_ref); + SSA_NAME_DEF_STMT ( field_val_temp) = temp_set; + + // lhs = temp + final_set = gimple_build_assign( lhs, field_val_temp); + SSA_NAME_DEF_STMT ( lhs) = final_set; + } + + //DEBUG_L("get_field_arry_addr: "); + //DEBUG_F( print_gimple_stmt, stderr, get_field_arry_addr, 0); + //DEBUG("\n"); + + //DEBUG_L("get_index: "); + //DEBUG_F( print_gimple_stmt, stderr, get_index, 0); + //DEBUG("\n"); + + //DEBUG_L("get_offset: "); + //DEBUG_F( print_gimple_stmt, stderr, get_offset, 0); + //DEBUG("\n"); + + //DEBUG_L("get_field_addr: "); + //DEBUG_F( print_gimple_stmt, stderr, get_field_addr, 0); + //DEBUG("\n"); + + //DEBUG_L("temp_set: "); + //DEBUG_F( print_gimple_stmt, stderr, temp_set, 0); + //DEBUG("\n"); + + //DEBUG_L("final_set: "); + //DEBUG_F( print_gimple_stmt, stderr, final_set, 0); + //DEBUG("\n"); + + gsi_insert_before( &gsi, get_field_arry_addr, GSI_SAME_STMT); + gsi_insert_before( &gsi, get_index, GSI_SAME_STMT); + gsi_insert_before( &gsi, get_offset, GSI_SAME_STMT); + gsi_insert_before( &gsi, get_field_addr, GSI_SAME_STMT); + gsi_insert_before( &gsi, temp_set, GSI_SAME_STMT); + gsi_insert_before( &gsi, final_set, GSI_SAME_STMT); + + + //delete stmt + gsi_remove ( &gsi, true); + } // end ReorgOpT_Indirect case + break; + case ReorgOpT_AryDir: // "x[i].f" + // Not implemented in single pool + internal_error ( "ReorgOpT_AryDir not possible"); + default: + internal_error ( + "Reached operand default for ReorgOpT_Indirect"); + + } // end recognize_op ( rhs, info) switch + + INDENT(-2); + } // end ReorgT_ElemAssign case + break; + case ReorgT_If_Null: + case ReorgT_If_NotNull: + { + DEBUG_L("ReorgT_If_(Not)Null: "); + DEBUG_F( print_gimple_stmt, stderr, stmt, 0); + /* + gimple_cond_set_rhs( stmt, + TYPE_MAX_VALUE( pointer_sized_int_node)); + */ + // TYPE_MAX_VALUE ( TREE_TYPE ( fail_val) + gimple_stmt_iterator gsi = gsi_for_stmt( stmt); + ReorgType_t *ri = contains_a_reorgtype( stmt, info); + //tree null_val = + // make_temp_ssa_name ( ri->pointer_rep, NULL, "if_cond_null"); + gcond *cond_stmt = as_a <gcond *> (stmt); + + //tree max = TYPE_MAX_VALUE ( TREE_TYPE ( ri->pointer_rep)); + tree max = TYPE_MAX_VALUE ( ri->pointer_rep); + + DEBUG_L("max: "); + DEBUG_F(print_generic_expr, stderr, max, (dump_flags_t)0); + DEBUG("\n"); + + gimple_cond_set_rhs( cond_stmt, max); + + DEBUG_L("after: "); + DEBUG_F( print_gimple_stmt, stderr, stmt, 0); + } + break; + case ReorgT_IfPtrEQ: + case ReorgT_IfPtrNE: + case ReorgT_IfPtrLT: + case ReorgT_IfPtrGT: + case ReorgT_IfPtrLE: + case ReorgT_IfPtrGE: + DEBUG_L("ReorgT_IfPtr*\n"); + // Not needed for single pool. TBD test this + break; + case ReorgT_PtrPlusInt: // "a = b + i" + { + DEBUG_L("ReorgT_PtrPlusInt: "); + DEBUG_F( print_gimple_stmt, stderr, stmt, 0); + // Needed for hellowotrld + + // Does the type of stmt need to be adjusted? I assume so. + // The ReorgType contains the type of the pointer + // if so that should probably be used. Note, the variables + // should all be of the correct type (but maybe that's + // not reflected here. Punting and assigning the types to + // the type of pointer_sized_int_node is probably not correct + // even though that's the representation. + + tree PPI_orig_lhs = gimple_assign_lhs ( stmt); + + //tree offset_type = TREE_TYPE ( TYPE_SIZE_UNIT (ri->gcc_type)); // not needed + tree type = ri->pointer_rep; + + tree str_siz = + build_int_cst ( type, int_cst_value ( TYPE_SIZE_UNIT (ri->gcc_type))); + + tree rhs1 = gimple_assign_rhs1( stmt); + tree rhs2 = gimple_assign_rhs2( stmt); + + gcc_assert ( type); + tree PPI_rhs1_cast = make_temp_ssa_name( type, NULL, "PPI_rhs1_cast"); + gimple *gPPI_rhs1_cast = gimple_build_assign ( PPI_rhs1_cast, CONVERT_EXPR, rhs1); + SSA_NAME_DEF_STMT ( PPI_rhs1_cast) = gPPI_rhs1_cast; + + tree PPI_rhs2_cast = make_temp_ssa_name( type, NULL, "PPI_rhs2_cast"); + gimple *gPPI_rhs2_cast = gimple_build_assign ( PPI_rhs2_cast, CONVERT_EXPR, rhs2); + SSA_NAME_DEF_STMT ( PPI_rhs2_cast) = gPPI_rhs2_cast; + + tree PPI_adj = make_temp_ssa_name( type, NULL, "PtrPlusInt_Adj"); + gimple *gPPI_adj = + gimple_build_assign ( PPI_adj, TRUNC_DIV_EXPR, PPI_rhs2_cast, str_siz); + SSA_NAME_DEF_STMT ( PPI_adj) = gPPI_adj; + + tree ptrplusint = make_temp_ssa_name( type, NULL, "PtrPlusInt"); + gimple *gPPI = + gimple_build_assign ( ptrplusint, PLUS_EXPR, PPI_rhs1_cast, PPI_adj); + SSA_NAME_DEF_STMT ( ptrplusint) = gPPI; + + gimple *gPPI_cast = + gimple_build_assign ( PPI_orig_lhs, CONVERT_EXPR, ptrplusint); + SSA_NAME_DEF_STMT ( PPI_orig_lhs) = gPPI_cast; + + //gimple_set_op( stmt, 2, tmp); + gimple_stmt_iterator gsi = gsi_for_stmt( stmt); + gsi_insert_before( &gsi, gPPI_rhs1_cast, GSI_SAME_STMT); + gsi_insert_before( &gsi, gPPI_rhs2_cast, GSI_SAME_STMT); + gsi_insert_before( &gsi, gPPI_adj, GSI_SAME_STMT); + gsi_insert_before( &gsi, gPPI, GSI_SAME_STMT); + gsi_insert_before( &gsi, gPPI_cast, GSI_SAME_STMT); + + gsi_remove ( &gsi, true); + + DEBUG_L(""); + DEBUG_F( print_gimple_stmt, stderr, gPPI_rhs2_cast, 0); + DEBUG_L(""); + DEBUG_F( print_gimple_stmt, stderr, gPPI_adj, 0); + DEBUG_L(""); + DEBUG_F( print_gimple_stmt, stderr, gPPI, 0); + DEBUG_L(""); + DEBUG_F( print_gimple_stmt, stderr, gPPI_cast, 0); + } + break; + case ReorgT_Ptr2Zero: // "a = 0" + DEBUG_L("ReorgT_Ptr2Zero\n"); + /* + // TBD + // Note, this is way too simple... just saying. + gimple_set_op( stmt, 1, + TYPE_MIN_VALUE( pointer_sized_int_node)); + */ + break; + case ReorgT_PtrDiff: // "i = a - b" + { + DEBUG_L("ReorgT_PtrDiff\n"); + // We basically need to modify the gimple code + // but that also means adding converts. + tree type = ri->pointer_rep; + tree str_siz = + build_int_cst ( type, int_cst_value ( TYPE_SIZE_UNIT (ri->gcc_type))); + tree rhs1 = gimple_assign_rhs1( stmt); + tree rhs2 = gimple_assign_rhs2( stmt); + tree PD_orig_lhs = gimple_assign_lhs ( stmt); + + tree PD_rhs1_cast = make_temp_ssa_name( type, NULL, "PD_rhs1_cast"); + gimple *gPD_rhs1_cast = gimple_build_assign ( PD_rhs1_cast, CONVERT_EXPR, rhs1); + SSA_NAME_DEF_STMT ( PD_rhs1_cast) = gPD_rhs1_cast; + + tree PD_rhs2_cast = make_temp_ssa_name( type, NULL, "PD_rhs2_cast"); + gimple *gPD_rhs2_cast = gimple_build_assign ( PD_rhs2_cast, CONVERT_EXPR, rhs2); + SSA_NAME_DEF_STMT ( PD_rhs2_cast) = gPD_rhs2_cast; + + tree ptrdiff = make_temp_ssa_name( type, NULL, "PtrDiff"); + gimple *gPD = + gimple_build_assign ( ptrdiff, MINUS_EXPR, PD_rhs1_cast, PD_rhs2_cast); + SSA_NAME_DEF_STMT ( ptrdiff) = gPD; + + tree PD_adjust = make_temp_ssa_name( type, NULL, "PD_adjust"); + gimple *gPD_adjust = + gimple_build_assign ( PD_adjust, MULT_EXPR, ptrdiff, str_siz); + + gimple *gPD_cast = + gimple_build_assign ( PD_orig_lhs, CONVERT_EXPR, PD_adjust); + SSA_NAME_DEF_STMT ( PD_orig_lhs) = gPD_cast; + + gimple_stmt_iterator gsi = gsi_for_stmt( stmt); + gsi_insert_before( &gsi, gPD_rhs1_cast, GSI_SAME_STMT); + gsi_insert_before( &gsi, gPD_rhs2_cast, GSI_SAME_STMT); + gsi_insert_before( &gsi, gPD, GSI_SAME_STMT); + gsi_insert_before( &gsi, gPD_adjust, GSI_SAME_STMT); + gsi_insert_before( &gsi, gPD_cast, GSI_SAME_STMT); + + gsi_remove ( &gsi, true); + + DEBUG_L(""); + DEBUG_F( print_gimple_stmt, stderr, gPD_rhs1_cast, 0); + DEBUG_L(""); + DEBUG_F( print_gimple_stmt, stderr, gPD_rhs2_cast, 0); + DEBUG_L(""); + DEBUG_F( print_gimple_stmt, stderr, gPD, 0); + DEBUG_L(""); + DEBUG_F( print_gimple_stmt, stderr, gPD_cast, 0); + } + break; + case ReorgT_Adr2Ptr: // "a = &x[i]" + DEBUG_L("ReorgT_Adr2Ptr\n"); + // TBD + /* + tree *add_stmt = + gimple_build_assign( + gimple_assign_lhs( stmt);, + PLUS_EXPR, + gimple_assign_rhs1( stmt), + gimple_assign_rhs2( stmt), + NULL_TREE, NULL_TREE); + gimple_stmt_iterator gsi = gsi_for_stmt( stmt); + gsi_insert_before( gsi, add_stmt, GSI_SAME_STMT); + // delete stmt + gsi_remove( gsi, true); + */ + break; + case ReorgT_PtrNull: // "x = a == 0" + case ReorgT_PtrNotNull: // "x = a != 0" + DEBUG_L("ReorgT_Ptr(Not)Null\n");\ + // TBD + /* + gimple_set_op( stmt, 2, + TYPE_MIN_VALUE( pointer_sized_int_node)); + */ + break; + case ReorgT_PtrEQ: // "i = a == b" + case ReorgT_PtrNE: // "i = a != b" + case ReorgT_PtrLT: // "i = a < b" + case ReorgT_PtrLE: // "i = a <= b" + case ReorgT_PtrGT: // "i = a > b" + case ReorgT_PtrGE: // "i = a >= b" + DEBUG_L("ReorgT_Ptr*\n"); + // Not needed for single pool. TBD test this + break; + case ReorgT_Malloc: + { + DEBUG_L("Transform ReorgT_Malloc\n"); + INDENT(2); + + // We need to use the user malloc function + // declaration rather than the builtin!!! + tree fndecl_malloc = gimple_call_fndecl ( stmt); + + // We need to synthesize the free function + // + tree param_type_list = NULL; + tree void_pointer_type_node = build_pointer_type ( void_type_node); + param_type_list = + tree_cons ( NULL_TREE, void_pointer_type_node, param_type_list); + //DEBUG_L("param_type_list: "); + //DEBUG_F(print_generic_expr, stderr, param_type_list, (dump_flags_t)0); + //DEBUG("\n"); + #if !USE_BUILT_IN_FREE + tree free_return_type = void_type_node; + #endif + //DEBUG_L("free_return_type: "); + //DEBUG_F(print_generic_expr, stderr, free_return_type, (dump_flags_t)0); + //DEBUG("\n") + #if USE_BUILT_IN_FREE + tree fndecl_free = builtin_decl_implicit ( BUILT_IN_FREE); + #else + tree fntype = build_function_type ( free_return_type, param_type_list); + tree fnname = get_identifier ( "free"); + tree fndecl_free = build_decl ( input_location, FUNCTION_DECL, fnname, fntype); + #endif + // Note, add it to the call graph at each call site + + // Note, unlike other simpler transformations, + // this must build new basic blocks to add new + // gimple to and use a phi for the final result. + // See appendix on malloc transformation for + // each comment starting with "FROM." + ReorgType_t *ri = contains_a_reorgtype( stmt, info); + // FROM len = val/size + tree arg = gimple_call_arg( stmt, 0); + // TBD: len is new SSA + tree val = gimple_call_lhs( stmt); + //DEBUG_L("val is: "); + //DEBUG_F( print_generic_expr, stderr, val, (dump_flags_t)-1); + //DEBUG(", tree code type: %s\n", code_str(TREE_CODE(TREE_TYPE(val)))); + //gcc_assert( TREE_CODE( TREE_TYPE(val)) == INDIRECT_REF); + gcc_assert( TREE_CODE( TREE_TYPE(val)) == POINTER_TYPE); + tree size = TYPE_SIZE_UNIT( TREE_TYPE( TREE_TYPE( val))); + // FROM len = val/size (insert before stmt) <== maybe arg/size + //tree len = make_temp_ssa_name( sizetype, NULL, "fail_val"); + // The above segfaulted ??? note, it's not an idiom seen in gcc + tree int_ptrsize_type = signed_type_for ( ptr_type_node); + //DEBUG_L("int_ptrsize_type = %p\n", TREE_TYPE ( size)); + gcc_assert ( TREE_TYPE ( size)); + tree len = make_temp_ssa_name ( TREE_TYPE ( size), NULL, "malloc_len"); + gimple_stmt_iterator gsi = gsi_for_stmt( stmt); + //gimple *glen = + // gimple_build_assign ( len, TRUNC_DIV_EXPR, val, size); + + // Cast arg to compatible type + gcc_assert( TREE_TYPE ( size)); + TREE_TYPE (arg) = TREE_TYPE ( size); + tree cast_arg = + make_temp_ssa_name( TREE_TYPE ( size), NULL, "cast_arg"); + gimple *gcast_arg = gimple_build_assign ( cast_arg, CONVERT_EXPR, arg); + SSA_NAME_DEF_STMT ( cast_arg) = gcast_arg; + gsi_insert_before( &gsi, gcast_arg, GSI_SAME_STMT); + + gimple *glen = + gimple_build_assign ( len, TRUNC_DIV_EXPR, cast_arg, size); + SSA_NAME_DEF_STMT ( len) = glen; + + gsi_insert_before( &gsi, glen, GSI_SAME_STMT); + // Note in other places in this doc this would + // be "insert glen before stmt" instead of this but + // here we need to create new basic blocks. + edge new_edge = split_block ( bb, stmt); + // FROM before_bb = edge->src // same as this bb + basic_block before_bb = new_edge->src; // + basic_block after_bb = new_edge->dest; + remove_edge ( new_edge); + basic_block prev_bb = before_bb; + + // FROM failure_bb = create_empty_block(prev_bb) + basic_block failure_bb = make_bb ( "failure_bb", prev_bb); + // I need to set the count to zero and there doesn't + // seem to be direct way of doing this... + failure_bb->count = prev_bb->count - prev_bb->count; + + // set edge probability and flags + edge fail_to_after_e = make_edge ( failure_bb, + after_bb, EDGE_FALLTHRU); + fail_to_after_e->probability = profile_probability::very_unlikely (); + fail_to_after_e->count () = failure_bb->count; + + // Note, this should remove this call from the call graph + cgraph_update_edges_for_call_stmt ( stmt, gimple_call_fndecl ( stmt), NULL); + // Now it's safe to remove it! + gsi_remove ( &gsi, true); + + tree field; + tree reorg_type = ri->gcc_type; // is this useful here? + tree reorg_pointer_type = ri->pointer_rep; + //tree base = ri->reorg_ver_type; //nopers + tree base = ri->instance_interleave.base; + + // ??? We are faking the malloc so the following seemed dubious + //tree malloc_return_type = TREE_TYPE ( arg); + //tree fail_val = + // make_temp_ssa_name ( malloc_return_type, NULL, "malloc_fail_val"); + gcc_assert ( reorg_pointer_type); + tree fail_val = + make_temp_ssa_name ( reorg_pointer_type, NULL, "malloc_fail_val"); + + // loop setup trickery for gimple idioms + // + // FROM prev_order = failure_bb + basic_block prev_order = failure_bb; + // FROM prev_bb = before_bb + prev_bb = before_bb; + int edge_flags = EDGE_FALLTHRU; + + // Generate all the real allocation code + // + // Note, I think there are ramifications of built in malloc (and free) + // so I'm going try and use the malloc in the call transformed!!! + // Actually, I ended up using the built in free and it was + // the way to go. + // + + //tree fndecl_malloc = builtin_decl_explicit( BUILT_IN_MALLOC); + + // This, after the following loop, will hold the start of the + // field related code. + tree new_ok_field_L; + + // FROM (for fields) { + bool first = true; + for( field = TYPE_FIELDS( reorg_type); + field; + field = DECL_CHAIN( field)) + { + basic_block new_bb = make_bb ( "new_bb", prev_order); + new_bb->count = prev_order->count; + // Nope! Don't do this. + //set_immediate_dominator ( CDI_DOMINATORS, new_bb, prev_bb); + //if ( first ) + // { + // first = false; + // set_immediate_dominator ( CDI_DOMINATORS, failure_bb, new_bb); + // set_immediate_dominator ( CDI_DOMINATORS, after_bb, new_bb); + // } + + tree base_field = + find_coresponding_field ( base, field); + + //DEBUG_L("base_field: %p\n", base_field); + //DEBUG_A(" : "); + //DEBUG_F(print_generic_expr, stderr, base_field, (dump_flags_t)0); + //DEBUG("\n"); + + tree base_field_type = TREE_TYPE( base_field); + //DEBUG_L("base_field_type: %p\n", base_field_type); + //DEBUG_A(" : "); + //DEBUG_F(print_generic_expr, stderr, base_field_type, (dump_flags_t)0); + //DEBUG("\n"); + + gimple_stmt_iterator gsi = gsi_start_bb ( new_bb); + // Note, switching the order of edge creation and + // setting dominator seems to make no difference + + // set edge probability and flags + + // edge_flags depends on whether or not the predecessor + // block was created in this loop. + edge ok_edge = make_edge ( prev_bb, new_bb, edge_flags); + edge_flags = EDGE_TRUE_VALUE; + + ok_edge->probability = profile_probability::very_likely (); + ok_edge->count () = prev_bb->count; + add_bb_to_loop ( new_bb, before_bb->loop_father); + + // Don't mess with the dominators. + //set_immediate_dominator ( CDI_DOMINATORS, new_bb, prev_bb); + + // create edge and set edge probability and flags + edge fail_edge = make_edge ( new_bb, failure_bb, EDGE_FALSE_VALUE); + fail_edge->probability = profile_probability::very_unlikely (); + fail_edge->count () = new_bb->count - new_bb->count; + + //tree lhs_ass = + // build3( COMPONENT_REF, ptr_type_node, base, field, NULL_TREE); + tree lhs_ass = build3( COMPONENT_REF, + base_field_type, + base, + base_field, NULL_TREE); + + //DEBUG_L("base: %p\n", base); + //DEBUG_A(" base: "); + //DEBUG_F(print_generic_expr, stderr, base, (dump_flags_t)0); + //DEBUG("\n"); + + //DEBUG_L("field: %p\n", field); + //DEBUG_A(" : "); + //DEBUG_F(print_generic_expr, stderr, field, (dump_flags_t)0); + //DEBUG("\n"); + + tree field_type = TREE_TYPE( field); + //DEBUG_L("field_type: %p\n", field_type); + //DEBUG_A(" : "); + //DEBUG_F(print_generic_expr, stderr, field_type, (dump_flags_t)0); + //DEBUG("\n"); + + //DEBUG_L("lhs_ass: %p\n", lhs_ass); + //DEBUG_A(" lhs_ass: "); + //DEBUG_F(print_generic_expr, stderr, lhs_ass, (dump_flags_t)0); + //DEBUG("\n"); + + tree lhs_ass_type = TREE_TYPE ( lhs_ass); + //DEBUG_L("lhs_ass_type: %p\n", lhs_ass_type); + //DEBUG_A(" lhs_ass_type: "); + //DEBUG_F(print_generic_expr, stderr, lhs_ass_type, (dump_flags_t)0); + //DEBUG("\n"); + + gcc_assert ( sizetype); + tree mem_size = + make_temp_ssa_name( sizetype, NULL, "malloc_mem_size"); + + // We need field_size to be of the correct type so + // we type cast. + gcc_assert ( TREE_TYPE ( mem_size)); + tree field_size = + make_temp_ssa_name( TREE_TYPE ( mem_size), NULL, "field_size"); + + // Move gprev_ok_field here + + // Move gfield_size here + gimple *gfield_size = + gimple_build_assign ( field_size, + CONVERT_EXPR, + TYPE_SIZE ( TREE_TYPE ( field))); + SSA_NAME_DEF_STMT ( field_size) = gfield_size; + + // Move gsize here + gimple *gsize = + gimple_build_assign ( mem_size, + MULT_EXPR, + field_size, + len); + SSA_NAME_DEF_STMT ( mem_size) = gsize; + + gcc_assert ( ptr_type_node); + tree res = + make_temp_ssa_name ( ptr_type_node, NULL, "res"); + + gcc_assert ( TREE_TYPE ( base_field)); + tree cast_res = + make_temp_ssa_name ( TREE_TYPE ( base_field), NULL, "cast_res"); + + tree res_type = TREE_TYPE( res); + //DEBUG_L("res_type: %p\n", res_type); + //DEBUG_A(" : "); + //DEBUG_F(print_generic_expr, stderr, res_type, (dump_flags_t)0); + //DEBUG("\n"); + + // Move malloc_call here + gcall *malloc_call = gimple_build_call( fndecl_malloc, 1, mem_size); + gimple_call_set_lhs( malloc_call, res); + SSA_NAME_DEF_STMT ( res) = malloc_call; + + cgraph_node::get ( cfun->decl)-> + create_edge ( cgraph_node::get_create ( fndecl_malloc), + malloc_call, + // Nah... lets do this a bit differently + //gimple_bb ( free_call)->count + new_bb->count + ); + + gimple *gcast_res = + gimple_build_assign ( cast_res, CONVERT_EXPR, res); + SSA_NAME_DEF_STMT ( cast_res) = gcast_res; + + // Move gset_field here + gimple *gset_field = gimple_build_assign ( lhs_ass, cast_res); + + // Move gcond here + gimple *gcond = + gimple_build_cond ( NE_EXPR, res, null_pointer_node, + NULL, NULL + ); + + // In execution order + gsi_insert_after ( &gsi, gfield_size, GSI_NEW_STMT); + gsi_insert_after ( &gsi, gsize, GSI_CONTINUE_LINKING); + gsi_insert_after ( &gsi, malloc_call, GSI_CONTINUE_LINKING); + gsi_insert_after ( &gsi, gcast_res, GSI_CONTINUE_LINKING); + gsi_insert_after ( &gsi, gset_field, GSI_CONTINUE_LINKING); + gsi_insert_after ( &gsi, gcond, GSI_CONTINUE_LINKING); + + prev_bb = new_bb; + prev_order = new_bb; + } + + // Loop cleaup fo failure code bb here. There is loop state + // overhead having nothing to do with the transformation + // that never the less must be updated. + add_bb_to_loop ( failure_bb, before_bb->loop_father); + + // create basic block for success + // + // FROM success_bb = create_empty_block(prev_bb_order); + basic_block success_bb = make_bb ( "succ_bb", prev_bb); + success_bb->count = prev_bb->count; + + // NOTE, it seems I shouldn't be attempting + // to diddle the dominator information on the fly. + // set_immediate_dominator ( CDI_DOMINATORS, success_bb, prev_bb); + + edge success_e = make_edge ( prev_bb, success_bb, EDGE_TRUE_VALUE ); + edge succ_to_after_e = make_edge ( success_bb, after_bb, EDGE_FALLTHRU); + success_e->probability = profile_probability::very_likely (); + succ_to_after_e->probability = profile_probability::always (); + success_e->count () = prev_bb->count; + succ_to_after_e->count () = prev_bb->count; + add_bb_to_loop ( success_bb, before_bb->loop_father); + + // code in success_bb + // + gcc_assert ( reorg_pointer_type); + tree success_val = + make_temp_ssa_name( reorg_pointer_type, NULL, "malloc_success_val"); + + gsi = gsi_start_bb ( success_bb); // used to be failure_bb + + gimple *set_succ = + gimple_build_assign ( success_val, + build_int_cst ( reorg_pointer_type, 0)); + SSA_NAME_DEF_STMT ( success_val) = set_succ; + + gsi_insert_after( &gsi, set_succ, GSI_NEW_STMT); + + // FROM gsi_insert_after( &gsi, new_ok_field ) + //gimple *gnew_ok_field = gimple_build_label ( new_ok_field_L); + //gsi_insert_after ( &gsi, gnew_ok_field, GSI_SAME_STMT); + + // add code to after_bb + // + // FROM gsi = gsi_start_bb( after_bb) + // Reuse gsi + //gimple_stmt_iterator gsi = gsi_start_bb( after_bb); + gsi = gsi_start_bb( after_bb); + + // FROM gsi_insert_after( &gsi, "lhs = "phi(success_val, fail_val) + + // Note, BBs have a sequence of phis which create_phi_node takes care of + // adding this phi too. + gcc_assert ( TREE_TYPE ( success_val)); + tree m_phi_val = + make_temp_ssa_name ( TREE_TYPE ( success_val), NULL, "m_phi_val"); + gphi *der_phi = create_phi_node( m_phi_val, after_bb); + add_phi_arg( der_phi, success_val, succ_to_after_e, UNKNOWN_LOCATION); + add_phi_arg( der_phi, fail_val, fail_to_after_e, UNKNOWN_LOCATION); + + gimple *gm_cast_phi_val = + gimple_build_assign ( val, CONVERT_EXPR, m_phi_val); + SSA_NAME_DEF_STMT ( val) = gm_cast_phi_val; + + //gsi_insert_after( &gsi, gm_cast_phi_val, GSI_NEW_STMT); + // TBD What does GSI_NEW_STMT do if the block isn't emply? + gsi_insert_before( &gsi, gm_cast_phi_val, GSI_NEW_STMT); + + //// FROM gsi_insert_after( &gsi, after_label) + //gimple *gafter_label = gimple_build_label( after_label_L); + //gsi_insert_after( &gsi, gafter_label, GSI_SAME_STMT); + + + // failure_bb code here + + // + // FROM fail_val is new SSA + //tree return_type = TREE_TYPE ( arg); + //tree fail_val = + // make_temp_ssa_name ( return_type, NULL, "fail_val"); + // FROM gsi = gsi_start_bb ( failure_bb) + gsi = gsi_start_bb ( failure_bb); + + // FROM gsi_insert_after( &gsi, "fail_val = minint") + gimple *gretnull = + gimple_build_assign ( fail_val, CONVERT_EXPR, + TYPE_MAX_VALUE ( TREE_TYPE ( fail_val))); + SSA_NAME_DEF_STMT ( fail_val) = gretnull; + + gsi_insert_after( &gsi, gretnull, GSI_NEW_STMT); + + for( field = TYPE_FIELDS( reorg_type); + field; + field = DECL_CHAIN( field)) { + + tree base_field = + find_coresponding_field ( base, field); + tree base_field_type = TREE_TYPE( base_field); + + gcc_assert ( base_field_type); + tree m_to_free = + make_temp_ssa_name( base_field_type, NULL, "malloc_to_free"); + + tree rhs_ass = build3( COMPONENT_REF, + base_field_type, + base, + base_field, NULL_TREE); + + gimple *gaddr2free = gimple_build_assign( m_to_free, rhs_ass); + SSA_NAME_DEF_STMT ( m_to_free) = gaddr2free; + + gsi_insert_after( &gsi, gaddr2free, GSI_CONTINUE_LINKING); + + gcc_assert ( ptr_type_node); + tree m_cast2free = + make_temp_ssa_name( ptr_type_node, NULL, "m_cast2free"); + + gimple *gm_cast2free = + gimple_build_assign( m_cast2free, CONVERT_EXPR, m_to_free); + SSA_NAME_DEF_STMT ( m_cast2free) = gm_cast2free; + + gsi_insert_after( &gsi, gm_cast2free, GSI_CONTINUE_LINKING); + + gcall *free_call = gimple_build_call( fndecl_free, 1, m_cast2free); + gsi_insert_after( &gsi, free_call, GSI_CONTINUE_LINKING); + + cgraph_node::get ( cfun->decl)-> + create_edge ( cgraph_node::get_create ( fndecl_free), + free_call, + failure_bb->count + ); + + tree lhs_ass = build3( COMPONENT_REF, + base_field_type, + base, + base_field, NULL_TREE); + + gimple *gzero = gimple_build_assign( lhs_ass, null_pointer_node); + gsi_insert_after( &gsi, gzero, GSI_CONTINUE_LINKING); + } + + //// FROM gsi_insert_after( &gsi, bad_field ) + + //DEBUG_L("End of malloc:\n"); + //DEBUG_F( print_program, PRINT_FORMAT, stderr, 4); + } + INDENT(-2); + break; + case ReorgT_Calloc: + // TBD + DEBUG_L("ReorgT_Calloc\n"); + /* + // This used to be almost a clone of the old version of + // the malloc code above and needs to transformed just like + // what was done above to malloc. + tree arg = gimple_call_arg( stmt, 0); + len is new SSA + tree val = gimple_call_lhs( stmt); + gcc_assert( TREE_CODE( TREE_TYPE(val)) == INDIRECT_REF); + tree size = TYPE_SIZE_UNIT( TREE_TYPE( TREE_TYPE( val))); + gimple *glen = + gimple_build_assign( + len, + TRUNC_DIV_EXPR, + val, + size, + NULL_TREE, NULL_TREE); + //insert glen before stmt + gimple_stmt_iterator stmt_gsi = gsi_for_stmt ( stmt); + gsi_link_before( stmt_gsi, glen, GSI_SAME_STMT); + tree lfial = create_artificial_label( UNKNOWN_LOCATION); + gimple *gfail = gimple_build_label( lfail); + tree lnotfial = create_artificial_label( UNKNOWN_LOCATION); + gimple *gnotfail = gimple_build_label( lnotfail); + tree base = ri->reorg_ver_type; + for (each element of base) // TBD <== + { + // call malloc + tree lok = create_artificial_label( UNKNOWN_LOCATION); + gimple *glok = gimple_build_label( lok); + tree *fndecl = builtin_decl_explicit( BUILT_IN_MALLOC); + mem_size is new SSA + gimple *gsize = + gimple_build_assign( + mem_size, + MULT_EXPR, + TYPE_SIZE(element), + len, + NULL_TREE, NULL_TREE); + insert gsize before stmt + gcall *call = gimple_build_call( fndecl, 1, mem_size); + mres is new SSA + gimple_call_set_lhs( call, mres) + insert call before stmt + // Set element to return value of malloc. + // Note, the devil is in the details here. + gen concat( REORG_SP_PREFIX, + type_name( lhs) ).element <- mres + and insert before stmt + // gen test of return + gimple *gcond = + gimple_build_cond( EQ_EXPR, mres, + null_pointer_node, lfail, lok); + insert gcond before stmt + insert glok before stmt + // call memset + fndecl = builtin_decl_explicit( BUILT_IN_MEMSET); + call = + gimple_build_call( fndecl, 3, mres, int_node_zero, mem_size); + insert call before stmt + } + + // fake return value of zero + gimple *gretzero = + gimple_build_assign( lhs, + build_int_cst( + TYPE_MIN_VALUE( TREE_TYPE(lhs)), 0)); + insert gretzero before stmt + gimple *ggoto = gimple_build_goto( lnotfail); + insert ggoto before stmt + insert glab1 before stmt + for each element of base { + tree fndecl = builtin_decl_explicit( BUILT_IN_FREE); + gcall *call = gimple_build_call( fndecl, 1, element); + insert call before stmt + set element to null + } + // fake return value of null + gimple *gretnull = + gimple_build_assign( lhs, + build_int_cst( + TYPE_MIN_VALUE( TREE_TYPE(lhs)))); + insert gretnull before stmt + insert gnotfail before stmt + delete stmt + */ + break; + case ReorgT_Realloc: + // TBD + DEBUG_L("ReorgT_Realloc\n"); + /* + // This used to be closely related to the old version of + // the malloc code above and needs to transformed just like + // what was done above to malloc. + tree arg = gimple_call_arg( stmt, 0); + len is new SSA + tree val = gimple_call_lhs( stmt); + gcc_assert( TREE_CODE( TREE_TYPE(val)) == INDIRECT_REF); + tree size = TYPE_SIZE_UNIT( TREE_TYPE( TREE_TYPE( val))); + gimple *glen = + gimple_build_assign( + len, + TRUNC_DIV_EXPR, + val, + size, + NULL_TREE, NULL_TREE); + insert glen before stmt + tree lfial = create_artificial_label( UNKNOWN_LOCATION); + gimple *gfail = gimple_build_label( lfail); + tree lnotfial = create_artificial_label( UNKNOWN_LOCATION); + gimple *gnotfail = gimple_build_label( lnotfail); + for each field of base { + // call malloc + tree lok = create_artificial_label( UNKNOWN_LOCATION); + gimple *gok = gimple_build_label( lok); + tree fndecl = builtin_decl_explicit( BUILT_IN_REALLOC); + // but first compute how much to malloc + mem_size, var, ptr are new SSA + gimple *gsize = + gimple_build_assign( + mem_size, + MULT_EXPR, + TYPE_SIZE(field), + len, + NULL_TREE, NULL_TREE); + insert gsize before stmt + generate ptr = base.field & insert before stmt + gcall *call + = gimple_build_call( fndecl, 3, ptr, + len, TYPE_SIZE( field)); + gimple_call_set_lhs( call, var); + insert call before stmt + // gen test of return + gimple *gcond = + gimple_build_cond( EQ_EXPR, var, + null_pointer_node, lfail, lok); + insert gcond before stmt + insert gok before stmt + generate base.field = var & insert before stmt + } + // fake return value of starting address (an index of zero) + gimple *gretzero = + gimple_build_assign( lhs, // + build_int_cst( + TYPE_MIN_VALUE( TREE_TYPE(lhs)), 0)); + insert gretzero before stmt + gimple *ggoto = gimple_build_goto( lnotfail); + insert ggoto before stmt + insert glab1 before stmt + for each element of base { + tree fndecl = builtin_decl_explicit( BUILT_IN_FREE); + gcall *call = gimple_build_call( fndecl, 1, element); + insert call before stmt + set element to null + } + // fake return value of null (minimum value under this scheme) + gimple *gretnull = + gimple_build_assign( lhs, + build_int_cst( + TYPE_MIN_VALUE( TREE_TYPE(lhs)))); + insert gretnull before stmt + insert gnotfail before stmt + delete stmt + */ + break; + case ReorgT_Free: + // TBD + DEBUG_L("ReorgT_Free\n"); + // We won't free the base because it a global. + /* + for each element of base { + tree fndecl = builtin_decl_explicit( BUILT_IN_FREE); + gcall *call = gimple_build_call( fndecl, 1, element); + insert call before stmt + } + delete stmt + */ + break; + case ReorgT_UserFunc: + { + DEBUG_L("ReorgT_UserFunc: "); + DEBUG_F( print_gimple_stmt, stderr, stmt, 0); + // Needed for helloworld. + // The type must be adjusted, but not here. + + // Note, what is done is there is a case (for + // GIMPLE_CALL) in the mini-pass to adjust a + // call's "dangling" SSA temp. I mean dangling + // in the sense that there are pointers to a + // reorg type on the left hand side of + // statements and they haven't been modified to + // use the correct reorg pointer + // represenatation, even though the right hand + // side has been. + } + break; + case ReorgT_Convert: + // Ignore type casting because another + // mini-pass sweeps up any ugly dangling types. + // TBD test this + DEBUG_L("ReorgT_Convert\n"); + break; + case ReorgT_Return: + // TBD This case probably is unnecessary. + DEBUG_L("ReorgT_Return\n"); + break; + default: + internal_error( "Invalid transformation"); + } + } + } + // Iterate over the PHIs and for any PHI that is a reorgtype, + // transform any constant zero into it's new repersentation. + // OR MAYBE... use FOR_EACH_PHI_ARG for the iterator... + + DEBUG_L("Phis with constant operands:\n"); + INDENT(4); + gphi_iterator pi; + for ( pi = gsi_start_phis (bb); !gsi_end_p (pi); gsi_next (&pi)) + { + //gimple_stmt_iterator gsi = as_a <gimple_stmt_iterator> pi; + gphi *phi = pi.phi (); + gimple *stmt = static_cast <gimple *> (phi); + + DEBUG_A("phi: "); + DEBUG_F( print_gimple_stmt, stderr, stmt, 0); + + ReorgType_t *ri = contains_a_reorgtype( stmt, info); + if ( ri != NULL && number_of_levels ( TREE_TYPE ( PHI_RESULT ( stmt))) == 1 ) + { + for (int i = 0; i < gimple_phi_num_args (phi); i++) + { + tree *arg = gimple_phi_arg_def_ptr (phi, i); + DEBUG_A("arg[%d] = ",i); + DEBUG_F(flexible_print, stderr, *arg, 1, (dump_flags_t)0); + bool int_cst = TREE_CODE ( *arg) == INTEGER_CST; + DEBUG_A("is %sinteger constant\n", int_cst ? "" : "not "); + if ( int_cst && integer_zerop ( *arg) ) + { + *arg = TYPE_MAX_VALUE ( ri->pointer_rep); + DEBUG_L("arg after = "); + DEBUG_F(flexible_print, stderr, *arg, 1, (dump_flags_t)0); + } + } + } + } + INDENT(-4); + } + pop_cfun (); + } + + DEBUG_L("after bulk of transformations\n"); + + DEBUG_F( print_program, info->reorg_dump_file, PRINT_FORMAT, 4, info); + + DEBUG ("INTERNALS PRINT\n"); + DEBUG_F (apply_to_all_gimple, print_internals, true, (void *)info); + + // A mini-pass to fixup dangling SSA temps. + + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) + { + struct function *func = DECL_STRUCT_FUNCTION ( node->decl); + push_cfun ( func); + + std::vector <tree> ssa_to_delete; + + DEBUG_L("Mini-Pass on Function %s:\n", lang_hooks.decl_printable_name ( func->decl, 2)); + + //DEBUG_L("\n"); + //DEBUG_F( wolf_fence, info); + + // We need a map of old ssa_name to new ssa_name. Not currently used. + std::map <tree,tree> ssa_map; + + // Walk function decl. + // Modify declaractions modifies the formal parameter types. + // TBD That needs to be moved here + // Never the less we need to associate new default defs with them. + // At the end of find_decls_and_types adds parameter decls. + // We'll use get_or_create_ssa_default_def on the ssa name and + // create a new decl for the decls that are pointers to + // reorg types. + // + // Then walk the gimple looking for dangling types. When we find + // one on the right hand side check to see if it's on the + // map. If not we create a ssa name and add it to the map. We + // subsitute it into operand. For the left hand side we also + // set the statement definition. + + // Default defs case first + + // For parameters + + // Note, we don't do anything unless it's necessary and it's + // not necessay if it's been done before. Hence the possibility + // of many functions mapping onto one declaration (which I + // even doubt is possible in thi case) can't be a problem. + + DEBUG_L("Dangling Types for Function Params (default defs).\n"); + INDENT(4); + tree parm; + for ( parm = DECL_ARGUMENTS ( func->decl); + parm; + parm = DECL_CHAIN ( parm) ) + { + DEBUG_A("param: "); + DEBUG_F( print_generic_decl, stderr, parm, (dump_flags_t)0); + DEBUG("\n"); + INDENT(2); + tree old_default_def = ssa_default_def ( func, parm); + DEBUG_A("old_default_def: "); + DEBUG_F( print_generic_expr, stderr, old_default_def, (dump_flags_t)0); + DEBUG("\n"); + tree new_default_def; + + // Modify prameter and do the default def stuff + + // Need to create a new decl rather than modify the existing + // one. + + if ( modify_decl_core ( &parm, info) ) + { + DEBUG_A("double check new param: "); + DEBUG_F( print_generic_decl, stderr, parm, (dump_flags_t)0); + DEBUG("\n"); + + // New default def here + + // If the variable of the decl is initialized + // then it shouldn't be associated with a default def. + if ( old_default_def ) + { + // We must delete the old one or tranversing ssa names of + // the function will stumple across an SSA name associated + // with nothing, causing grief. + // + // We do it at the bottom of this code using a + // something I had to write (there was no existing + // mechanism.) + + // Create new default def here. + // NOTE parm looks correct here but type of new_default_def + // is that of old_default_def so this might just look up + // old_default_def! + //new_default_def = get_or_create_ssa_default_def ( func, parm); + // ??? try this (in conjunction with the above) to fix things... NOPE + //set_ssa_default_def ( func, parm, new_default_def); + // ??? these instead + new_default_def = make_ssa_name_fn ( func, parm, gimple_build_nop ()); + set_ssa_default_def ( func, parm, new_default_def); + + DEBUG_A("new_default_def: "); + DEBUG_F(print_generic_expr, stderr, new_default_def, (dump_flags_t)0); + DEBUG(", TYPE: "); + DEBUG_F(print_generic_expr, stderr, TREE_TYPE(new_default_def), (dump_flags_t)0); + DEBUG("\n"); + + // TBD REMOVE DUPLICATE! + // Replace old one (not really totally hence the + // remove_default_def below.) + set_ssa_default_def ( func, parm, new_default_def); + + imm_use_iterator iter; + gimple *stmt; + use_operand_p use; + // Modify stmts using old default def to use + // new default def + FOR_EACH_IMM_USE_STMT ( stmt, iter, old_default_def) // <== use other form??? Not + { + DEBUG_A("before: "); + DEBUG_F ( print_gimple_stmt, stderr, stmt, 0); + use_operand_p use_p; + ssa_op_iter ssa_iter; + // The F_E_S_U_O macro was blowing up on a phi + //FOR_EACH_SSA_USE_OPERAND( use_p, stmt, ssa_iter, SSA_OP_USE ) + FOR_EACH_PHI_OR_STMT_USE ( use_p, stmt, ssa_iter, SSA_OP_USE ) + { + if ( use_p == NULL ) continue; + tree use = USE_FROM_PTR (use_p); + DEBUG_A("use to replace: "); + DEBUG_F( print_generic_expr, stderr, use, (dump_flags_t)0); + DEBUG("\n"); + if (use == old_default_def) + SET_USE ( use_p, new_default_def); + } + DEBUG_A("after: "); + DEBUG_F ( print_gimple_stmt, stderr, stmt, 0); + } + // Get rid of the old default def because it confuses + // + //remove_default_def ( old_default_def, func); + ssa_to_delete.push_back ( old_default_def); + release_ssa_name_fn ( func, old_default_def); + } + } + INDENT(-2); + } + INDENT(-4); + + DEBUG_L("Dangling Types for Function Local (default defs).\n"); + INDENT(4); + //DEBUG_L("\n"); + //DEBUG_F( wolf_fence, info); + + // For locals + // + // Note, the code below looks half baked and might simply not + // encountered anything that breaks it (see the code for the + // parameters above how it might need to look.) + // + + unsigned i; + tree decl; + FOR_EACH_LOCAL_DECL ( func, i, decl) + { + DEBUG_A("local: "); + DEBUG_F( print_generic_decl, stderr, decl, (dump_flags_t)0); + DEBUG("\n"); + tree old_default_def = ssa_default_def ( func, decl); + tree new_default_def; + + // Modify prameter and do the default def stuff + + // Again we'll need to create a new decl rather than modify + // the existing one. + + if ( modify_decl_core ( &decl, info) ) + { + // New default def here + + // If the variable of the decl is initialized + // then it shouldn't be associated with a default def. + if ( old_default_def ) + { + // TBD Do we delete the old one and if so, + // do we do it here and how do we do it? + + // Create new default def here. + new_default_def = get_or_create_ssa_default_def ( func, decl); + + imm_use_iterator iter; + gimple *stmt; + use_operand_p use; + // Modify stmts using old default def to use + // new default def + FOR_EACH_IMM_USE_STMT ( stmt, iter, old_default_def) + { + FOR_EACH_IMM_USE_ON_STMT ( use, iter) + { + SET_USE ( use, new_default_def); + } + // Possibly do this??? + //update_stmt ( stmt); + } + } + } + } + INDENT(-4); + + // Normal ssa name case + DEBUG_L("Dangling Types for Normal SSA Names:\n"); + //DEBUG_L("\n"); + //DEBUG_F( wolf_fence, info); + + INDENT(4); + // We use len instead of using func->length() in the for loop test + // because new ssa names are created in the loop body and we + // shouldn't process them. + unsigned int len = SSANAMES ( func)->length (); + DEBUG_L("len = %d\n",len); + for ( unsigned int i = 0; i < len; i++) + { + DEBUG_L("SSANAMES(func)[%d]\n",i); + + tree ssa_name = (*SSANAMES ( func))[i]; + + if( ssa_name == NULL ) + { + DEBUG_L("Skip, ssa_name == NULL\n"); + continue; + } + + bool a_default_def = SSA_NAME_IS_DEFAULT_DEF ( ssa_name); + gimple *defining_stmt = SSA_NAME_DEF_STMT ( ssa_name);; + bool no_defining_stmt = defining_stmt == NULL; + bool defined_by_nop = defining_stmt && gimple_code ( defining_stmt) == GIMPLE_NOP; + tree type = TREE_TYPE ( ssa_name); + tree bottom_type = base_type_of ( type); + ReorgType_t *ri = get_reorgtype_info ( bottom_type, info); + DEBUG_L("ssa_name = "); + DEBUG_F(print_generic_expr, stderr, ssa_name, (dump_flags_t)0); + DEBUG(" %s", a_default_def ? "is default_def" : ""); + DEBUG(" %s", no_defining_stmt ? "has no defining stmt" : ""); + DEBUG(" %s", defined_by_nop ? "defined by a nop" : ""); + DEBUG(", type = "); + DEBUG_F(print_generic_expr, stderr, type, (dump_flags_t)0); + DEBUG(", bottom_type = "); + DEBUG_F(print_generic_expr, stderr, bottom_type, (dump_flags_t)0); + DEBUG(", ri = %p\n",ri); + + // If it's not a dangling type we don't care + if ( ri == NULL ) + { + DEBUG_L("Skip, ri == NULL\n"); + continue; + } + + // A default def is processed seperately + if ( a_default_def ) + { + DEBUG_L("Skip default_def\n"); + continue; + } + + gcc_assert ( !no_defining_stmt); + gcc_assert ( !defined_by_nop); + + DEBUG_L("Defining stmt: "); + DEBUG_F ( print_gimple_stmt, stderr, defining_stmt, 0); + + tree new_type = ri->pointer_rep; + tree new_ssa_name = make_temp_ssa_name( new_type, NULL, "dedangled"); + DEBUG_L("new_ssa_name = "); + DEBUG_F(print_generic_expr, stderr, new_ssa_name, (dump_flags_t)0); + DEBUG("\n"); + #if DEBUGGING + for ( unsigned int j = 0; j < SSANAMES ( func)->length (); j++) + { + if ( (*SSANAMES ( func))[j] == new_ssa_name ) + { + DEBUG_L("new name at j = %d\n",j); + break; + } + } + #endif + + gimple *use_stmt; + imm_use_iterator iter; + FOR_EACH_IMM_USE_STMT ( use_stmt, iter, ssa_name) + { + DEBUG_L("use_stmt before: "); + DEBUG_F ( print_gimple_stmt, stderr, use_stmt, 0); + + // Deal with the uses + use_operand_p use_p; + ssa_op_iter ssa_iter; + // The F_E_S_U_O macro was blowing up on a phi + //FOR_EACH_SSA_USE_OPERAND( use_p, use_stmt, ssa_iter, SSA_OP_USE ) + FOR_EACH_PHI_OR_STMT_USE ( use_p, use_stmt, ssa_iter, SSA_OP_USE ) + { + DEBUG_L("use_p = %p\n",use_p); + if ( use_p == NULL ) continue; + tree use = USE_FROM_PTR (use_p); + if (use == ssa_name) + SET_USE ( use_p, new_ssa_name); + } + DEBUG_L("use_stmt after: "); + DEBUG_F ( print_gimple_stmt, stderr, use_stmt, 0); + + // Should update_stmt be called here? + // It does not seem either harm or help so I'll + // leave it in. + update_stmt ( use_stmt); + } + // Modify the LHS too + // TBD This code needs to be more general. + DEBUG_L("What is ssa_name? "); + DEBUG_F(flexible_print, stderr, ssa_name, 1, (dump_flags_t)0); + gimple *def = SSA_NAME_DEF_STMT ( ssa_name); + + DEBUG_L("def: "); + DEBUG_F ( print_gimple_stmt, stderr, def, 0); + + set_lhs_for ( def, new_ssa_name); + + update_stmt ( def); + + // This is where we know that ssa_name needs to be replaced + release_ssa_name_fn ( func, ssa_name); + + } + INDENT(-4); + + // Might be a bad idea. + #if 0 + for ( auto iter = ssa_to_delete.begin ();iter != ssa_to_delete.end (); iter++ ) + { + remove_default_def ( *iter, func); + } + #endif + + pop_cfun (); + } + + DEBUG_L("after mini-passes\n"); + + if ( info->show_all_reorg_cands ) + { + fprintf ( info->reorg_dump_file, "End of str_reorg_instance_interleave_trans:\n"); + print_program ( info->reorg_dump_file, PRINT_FORMAT, 4, info); + } + + // TBD Should this be a diagnostic or not? + DEBUG ("INTERNALS PRINT\n"); + DEBUG_F (apply_to_all_gimple, print_internals, true, (void *)info); +} + +// Note, the following code might be a bit overly simplistic. +static void +set_lhs_for ( gimple *stmt, tree ssa_name) +{ + switch ( gimple_code ( stmt)) + { + case GIMPLE_ASSIGN: + gimple_assign_set_lhs ( stmt, ssa_name); + break; + case GIMPLE_CALL: + gimple_call_set_lhs ( stmt, ssa_name); + break; + case GIMPLE_PHI: + { + gphi *phi_stmt = as_a <gphi *> ( stmt); + gimple_phi_set_result ( phi_stmt, ssa_name); + } + break; + default: + fprintf ( stderr, "error: unprecidented gimple for set_lhs_for\n "); + print_gimple_stmt( stderr, stmt, 0); + gcc_assert ( 0); + } +} + +// TBD no longer used... preserve it for a bit, then remove it. +static void +wrangle_ssa_type( tree side, Info_t *info ) +{ + tree side_type = TREE_TYPE ( side); + tree bottom_type = base_type_of ( side_type); + //DEBUG_L("op: "); + //DEBUG_F(print_generic_expr, stderr, side, (dump_flags_t)0); + //DEBUG("\n"); + //DEBUG_L("bottom_type: "); + //DEBUG_F(print_generic_expr, stderr, bottom_type, (dump_flags_t)0); + //DEBUG("\n"); + + // Maybe we sould pass in ri as an argument??? + ReorgType_t *ri = get_reorgtype_info ( bottom_type, info); + tree prev_type = side_type; + tree type = TREE_TYPE ( prev_type); + //DEBUG_L( "prev_type: %p, type: %p\n", prev_type, type); + int levels; + for ( levels = 0; TREE_CODE ( type) == POINTER_TYPE; levels++ ) + { + prev_type = type; + type = TREE_TYPE ( prev_type); + //DEBUG_L( "prev_type: %p, type: %p\n", prev_type, type); + } + + // I thought about doing this: + // Modify type of ssa temp (dicey!) + // This changes every instance of * reorg_type to the + // new pointre rep in one fell swoop. + // I sweat just thinking how crazy this is.... + // + // TREE_TYPE ( prev_type) = ri->pointer_rep; + + // TBD might use build_pointer_type to build new type for *(N)reorg_type + // to *(N-1)ri->pointer_rep + // Fakes this for levels == 1 + if ( levels == 0) + { + //DEBUG_L( "LEVELS ZERO\n"); + modify_ssa_name_type ( side, ri->pointer_rep); + //DEBUG_L("after modify_ssa_name_type\n"); + } + else + { + //DEBUG_L( "LEVELS > ZERO\n"); + gcc_assert(0); + } +} + +void +print_internal_op ( tree op) +{ + tree type = TREE_TYPE ( op); + print_generic_expr ( stderr, op, (dump_flags_t)0); + fprintf( stderr, " TYPE: "); + print_generic_expr ( stderr, type, (dump_flags_t)0); + fprintf( stderr, " MAIN_TYPE: "); + print_generic_expr ( stderr, TYPE_MAIN_VARIANT ( type), (dump_flags_t)0); + fprintf( stderr, "\n"); +} + +bool +print_internals (gimple *stmt, void *data) +{ + Info_t *info = (Info_t*)data; + + print_gimple_stmt ( stderr, stmt, TDF_SLIM); + + if ( gimple_code ( stmt) == GIMPLE_ASSIGN ) + { + tree lhs = gimple_assign_lhs( stmt); + tree rhs1 = gimple_assign_rhs1( stmt); + tree rhs2 = gimple_assign_rhs2( stmt); + tree rhs3 = gimple_assign_rhs3( stmt); + gcc_assert ( lhs); + gcc_assert ( rhs1); + + bool lhs_reorg = tree_contains_a_reorgtype_p ( lhs, info); + //DEBUG_L("rhs1 = "); + //DEBUG_F(flexible_print, stderr, rhs1, 1, (dump_flags_t)0); + bool rhs1_reorg = tree_contains_a_reorgtype_p ( rhs1, info); + bool rhs2_reorg = tree_contains_a_reorgtype_p ( rhs2, info); + bool rhs3_reorg = tree_contains_a_reorgtype_p ( rhs3, info); + + bool lhs_ssa = lhs_reorg && TREE_CODE(lhs) == SSA_NAME; + bool rhs1_ssa = rhs1_reorg && TREE_CODE(rhs1) == SSA_NAME; + bool rhs2_ssa = rhs2_reorg && TREE_CODE(rhs2) == SSA_NAME; + bool rhs3_ssa = rhs3_reorg && TREE_CODE(rhs3) == SSA_NAME; + + fprintf( stderr, " LHS%s: ", lhs_ssa ? "*" : ""); + print_generic_expr ( stderr, TREE_TYPE ( lhs), (dump_flags_t)0); + + fprintf( stderr, ", RHS1%s: ", rhs1_ssa ? "*" : ""); + print_generic_expr ( stderr, TREE_TYPE ( rhs1), (dump_flags_t)0); + + if ( rhs2 ) + { + fprintf( stderr, ", RHS2%s: ", rhs2_ssa ? "*" : ""); + print_internal_op ( rhs2); + } + + if ( rhs3 ) + { + fprintf( stderr, ", RHS3%s: ", rhs3_ssa ? "*" : ""); + print_internal_op ( rhs3); + } + fprintf ( stderr, "\n"); + } else + if ( gimple_code ( stmt) == GIMPLE_PHI ) + { + use_operand_p phi_op; + ssa_op_iter iter; + gphi *phi_stmt = dyn_cast <gphi *> ( stmt); + + tree def = PHI_RESULT ( phi_stmt); + fprintf( stderr, " OP: "); + print_internal_op ( def); + + FOR_EACH_PHI_ARG ( phi_op, phi_stmt, iter, SSA_OP_ALL_OPERANDS) + { + tree op = USE_FROM_PTR ( phi_op); + fprintf( stderr, " OP: "); + print_internal_op ( op); + } + } + else + { + ssa_op_iter iter; + tree op_def; + use_operand_p opu; + FOR_EACH_SSA_TREE_OPERAND ( op_def, stmt, iter, SSA_OP_ALL_DEFS) + { + fprintf( stderr, " DEF OP: "); + print_internal_op ( op_def); + } + FOR_EACH_SSA_USE_OPERAND ( opu, stmt, iter, SSA_OP_ALL_USES) + { + tree use_op = USE_FROM_PTR ( opu); + fprintf( stderr, " USE OP: "); + print_internal_op ( use_op); + } + } + + return false; +} + +static void +str_reorg_instance_interleave_qual_part ( Info *info) +{ + // TBD save the return value so we can bypass further + // instance interleaving if none of it is profitable. + reorg_perf_qual ( info); +} + +static void +str_reorg_instance_interleave_type_part ( Info *info) +{ + create_new_types ( info); +} + +// Typse for performance qualification + +typedef struct reorg_bb_info reorg_bb_info_t; +struct reorg_bb_info { + basic_block *bb; +}; + +typedef struct perf_bb_info perf_bb_info_t; +typedef struct acc_info acc_info_t; +typedef struct var_info var_info_t; + +struct var_info { + varpool_node *var; + sbitmap *bits; + double count; +}; + +struct acc_info { + varpool_node *v; + int field_num; +}; + +struct perf_loop_info { + std::vector <var_info_t*> *vari; + class loop *gcc_loop; +}; + +static void account_for_use( tree, std::vector <acc_info_t> *); +static bool is_array_access( tree); + +static unsigned int +reorg_perf_qual ( Info *info) +{ + DEBUG_L("reorg_perf_qual:\n"); + #if 1 + // TBD use design in doc but mark ReorgTypes + // (do_instance_interleave) that qualify instead of deleting them + // unless both dead field elimination and field reorderig are not + // viable (use do_dead_field_elim and do_field_reorder in + // Reorg_type_t.) + + // For the mean time assume if a ReorgType made it here then it's qualified. + for ( int i = 0; i < info->reorg_type->size (); i++ ) + { + (*(info->reorg_type))[i].do_instance_interleave = true; + } + #else + // We are doing a quick and dirty version of performance + // qualification for testing purposes and possibly the + // initial version of for the main branch. + auto reorg_types = info->reorg_type; + + // These are floating point numbers because of the fractional + // accesses associated the probabilistic approach taken + // below. By taken below I refer to full algorithm and the + // quick and dirty initial version. + + // reorg was not possible for these + double cache_accesses = 0.0; + + // reorg possible for these. This doesn't mean it is + // profitable or even legal for all the types lumped + // in together here. However, the sum of this and + // cache_accesses should account for all the array + // accesses in the program. + double cache_accesses_noreorg = 0.0; + + // Perf Analysis + struct cgraph_node *node; + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) { + struct function *func = DECL_STRUCT_FUNCTION ( node->decl); + // Ulgy GCC idiom with global pointer to current function. + // However, the dominace calculations other things need it. + push_cfun ( func); + + if ( dom_info_available_p ( CDI_DOMINATORS) ) + { + free_dominance_info ( CDI_DOMINATORS); + } + calculate_dominance_info (CDI_DOMINATORS); + + // TBD + std::vector<perf_loop_info> loop_perf; + loop_perf.reserve ( number_of_loops ( func)); + class loop *loop; + FOR_EACH_LOOP_FN ( func, loop, LI_ONLY_INNERMOST ) + { + loop_perf [ loop->num ].vari = new std::vector<var_info_t*>; // ??? + loop_perf [ loop->num ].gcc_loop = loop; + size_t num_bbs = loop->num_nodes; + basic_block *bbs = get_loop_body ( loop); + + // TBD Stuff here + for ( unsigned i = 0; i < loop->num_nodes; i++) + { + basic_block bb = bbs [i]; + for ( auto gsi = gsi_start_bb ( bb); !gsi_end_p ( gsi); gsi_next ( &gsi) ) + { + gimple *stmt = gsi_stmt ( gsi); + if ( contains_a_reorgtype ( stmt, info) != NULL ) + { + DEBUG_A("examine: "); + DEBUG_F ( print_gimple_stmt, stderr, stmt, 0); + INDENT(4); + unsigned n_ops = gimple_num_ops( stmt); + tree op; + unsigned ith_op; + for ( ith_op = 0; i < n_ops; i++ ) + { + op = gimple_op ( stmt, ith_op); + ReorgType_t *tri = tree_contains_a_reorgtype (op, info); + if ( tri != NULL ) + { + DEBUG_A(""); + DEBUG_F(print_reorg, stderr, 0, tri); + DEBUG(", "); + DEBUG_F(flexible_print, stderr, op, 1, (dump_flags_t)0); + } + } + INDENT(-4); + + } + } + }continue; // Testing above here + + // Obtain loop count by looking at all the block counts. + unsigned max_count = 0; + for ( unsigned i = 0; i < loop->num_nodes; i++) + { + basic_block bb = bbs [i]; + max_count = MAX( max_count, bb->count.value ()); + } + DEBUG_L("max_count = %d, nb_iterations_estimate = %ld\n", + max_count, loop->nb_iterations_estimate); + + // Originally this was done per bb but now it has to be per + // loop. TBD But perf_bb is per loop so we need something similar + // per loop. + + std::vector <var_info_t*> *pv = loop_perf [ loop->num].vari; + for ( auto pvi = pv->begin (); pvi != pv->end (); pvi++ ) + { // 676 + tree base_type = base_type_of( ( *pvi)->var->decl); + ReorgType_t *ri = get_reorgtype_info ( base_type, info); + // Reorg accounting + if( ri != NULL ) + { + double reorg_nca = 0.0; + int fi; + tree field; + for( field = TYPE_FIELDS ( ri->gcc_type), fi = 0; + field; + field = DECL_CHAIN ( field), fi++ ) // 684 + { + if ( bitmap_bit_p ( *(*pvi)->bits, fi) ) + { + unsigned HOST_WIDE_INT fld_width = + tree_to_uhwi ( DECL_SIZE ( field)); + reorg_nca += max_count * alignment_effect ( fld_width); + } + } + ri->instance_interleave.reorg_perf += reorg_nca; + } // 699 + + // regular accounting + double regular_nca = 0.0; + sbitmap cache_model = sbitmap_alloc(1); + // TBD NOTE, pv steps on the pv above. + std::vector <var_info_t*> *pv2 = loop_perf[ loop->num].vari; + for( auto pv2i = pv2->begin (); pv2i != pv2->end (); pv2i++ ) { // 704 + tree base_type = base_type_of ( (*pv2i)->var->decl); + // create a tiny model of the cache big + // enough for this record. + unsigned HOST_WIDE_INT len = + (( tree_to_uhwi ( DECL_SIZE ( base_type)) + + + param_l1_cache_line_size -1) + / + param_l1_cache_line_size) + + + 1; + cache_model = sbitmap_resize( cache_model, (unsigned) len, 0); + double accum = 0.0; + int nrbo = 0; + for ( auto field_ex = TYPE_FIELDS ( base_type); + field_ex; + field_ex = DECL_CHAIN ( field_ex) ) + { + nrbo++; + unsigned HOST_WIDE_INT base_offset = + tree_to_uhwi ( DECL_FIELD_OFFSET( field_ex)); + // Access accounting + int fi = 0; + for ( auto field = TYPE_FIELDS ( base_type); + field; + field = DECL_CHAIN ( field), fi++) + { + if ( bitmap_bit_p ( *(*pv2i)->bits, fi) ) + { + unsigned HOST_WIDE_INT fld_width, fld_offset; + fld_width = tree_to_uhwi ( DECL_SIZE ( field)); + fld_offset = tree_to_uhwi ( DECL_FIELD_OFFSET ( field)); + int chari; + for ( chari = 0; chari < fld_width; chari++ ) + { + int loc = (chari + fld_offset + base_offset) + / + param_l1_cache_line_size; + bitmap_set_bit ( cache_model, loc); + } + } + } + accum += bitmap_count_bits ( cache_model); + bitmap_clear ( cache_model); + } + regular_nca += accum / nrbo; + + } // 739 + sbitmap_free ( cache_model); + + if( ri != NULL ) { + ri->instance_interleave.regular_perf += regular_nca; + cache_accesses_noreorg += regular_nca; + } else { + cache_accesses += regular_nca; + } + } // end for each prop_var 748 + + + } // + pop_cfun (); + } + + // TBD Somebody somewhere needs to compute: + // reorg_perf + // regular_perf + // cache_accesses + // cache_accesses_noreorg + + double total_cache_accesses = + cache_accesses + cache_accesses_noreorg; + + info->total_cache_accesses = total_cache_accesses; + + // + // Decide which reorgTypes fail performance qualification + // + + // We have the total accesses per type. Use that to see + // if the type qualifies. + for ( auto reorgi = reorg_types->begin (); + reorgi != reorg_types->end (); reorgi++ ) + { + double with_opt = reorgi->instance_interleave.reorg_perf; + double wihtout_opt = reorgi->instance_interleave.regular_perf; + double raw_effect = with_opt/wihtout_opt; + double absolute_effect = + (wihtout_opt - with_opt) / total_cache_accesses; + + // Note, there would need to be a multi-pool case here if + // that is every done. + + // If the relative effect is small enough don't bother. + if ( raw_effect < SINGLE_POOL_RAW_SKIP_IT ) + { + reorgi->do_instance_interleave = false; + continue; + } + // the relative effect is big enough do it anyway + // otherwise look at the absolute effect. + if ( raw_effect >= SINGLE_POOL_RAW_DO_IT_ALWAYS ) + { + reorgi->do_instance_interleave = true; + continue; + } + if ( absolute_effect < SINGLE_POOL_ABS_SKIP_IT ) + { + reorgi->do_instance_interleave = false; + continue; + } + if ( absolute_effect >= SINGLE_POOL_ABS_DO_IT_ALWAYS ) + { + reorgi->do_instance_interleave = true; + continue; + } + + // We fitted a linear equation to the corners of the + // effects above and use it to determine when + // to disqualify a type + double cut_off = cut_off_eq_single_pool ( absolute_effect); + if ( raw_effect < cut_off ) + { + reorgi->do_instance_interleave = false; + } + } + + free_dominance_info ( CDI_DOMINATORS); + + #endif +} + +#define SINGLE_POOL_SLOPE \ + ((SINGLE_POOL_RAW_DO_IT_ALWAYS - SINGLE_POOL_RAW_SKIP_IT) \ + / \ + (SINGLE_POOL_ABS_DO_IT_ALWAYS - SINGLE_POOL_ABS_SKIP_IT)) + +#define SINGLE_POOL_INTERSECT \ + (SINGLE_POOL_RAW_SKIP_IT \ + - \ + SINGLE_POOL_SLOPE * SINGLE_POOL_ABS_SKIP_IT) + +static double +cut_off_eq_single_pool( double x) +{ + return SINGLE_POOL_SLOPE * x + SINGLE_POOL_INTERSECT; +} + +static double +alignment_effect( unsigned HOST_WIDE_INT width ) +{ + unsigned HOST_WIDE_INT times = param_l1_cache_line_size / width; // ?? + unsigned HOST_WIDE_INT rem = param_l1_cache_line_size % width; + if( rem == 0 ) { + return 1.0; + } + unsigned HOST_WIDE_INT m, n, g; + g = gcd( param_l1_cache_line_size, width); + m = param_l1_cache_line_size / g; + n = width / g; + return 1.0 + (n - 1.0)/m; +} + +static void +header ( bool initialize ) +{ + static bool emit_header; + if ( initialize ) + { + emit_header = true; + } + else + { + if ( emit_header ) + { + emit_header = false; + fprintf( stderr, "SANITY CHECKING FAILURE:\n"); + } + } +} + +// TBD I have doubts that this what is really needed +bool +is_array_access( tree acc) +{ + tree type = TREE_TYPE ( acc); + if( TREE_CODE( type) == ARRAY_TYPE ) + return true; + while( POINTER_TYPE_P( type) ) { + type = TREE_TYPE( type); + if( TREE_CODE( type) == ARRAY_TYPE ) + return true; + } + return false; +} + +static void +account_for_use( tree acc, std::vector <acc_info_t> *acc_info) +{ + // determine element of access + // find field access number i + // find var v + varpool_node *v; + int i; + // TBD + acc_info_t ai = { v, i}; + acc_info->push_back( ai); +} + + +// create_new_types has to crawl "all" the +// types, create new types and transform +// other types that must be changed. +// A type will change when it's a +// a pointer to a ReorgType or it contains +// an interior pointer to one. +static void +create_new_types ( Info_t *info) +{ + std::map < tree, BoolPair_t>::iterator tmi; + for( tmi = info->struct_types->begin (); + tmi != info->struct_types->end (); + tmi++ ) { + if ( !tmi->second.processed ) create_a_new_type ( info, tmi->first); + } +} + +static void +create_a_new_type ( Info_t *info, tree type) +{ + bool layout_changed = false; + // skip if already processed + if ( ( *( info->struct_types))[type].processed ) return; + + // Implementation note: Check this for infinite recursion. + // I don't think it's possible in a sane universe but + // pointers to reorganized types can occur, so does that + // an issue (not necessarily here.) + // Also, is this even necessary? Singletons don't expand + // and static arrays are not allowed "yet." + tree field; + tree new_fields = NULL; + for ( field = TYPE_FIELDS ( type); // ??? I speced reorg_type_prime here??? + field; + field = DECL_CHAIN ( field)) + { + // make sure all the interior types are processed + // before processing this type + if ( TREE_CODE ( field) == RECORD_TYPE ) + { + create_a_new_type ( info, field); + } + } + + ReorgType_t *ri = get_reorgtype_info ( type, info); + if ( ri != NULL ) { + // Create the new record type of the reorg type + tree reorg_type_prime = lang_hooks.types.make_type (RECORD_TYPE); + + ri->reorg_ver_type = reorg_type_prime; + //DEBUG_L("TYPE_SIZE(reorg_type_prime): %p, ", TYPE_SIZE(reorg_type_prime)); + //DEBUG_F( print_generic_expr, stderr, TYPE_SIZE(reorg_type_prime), (dump_flags_t)-1); + //DEBUG("\n"); + + /* Multi-pool only + // Create pointer_rep + // this will be a long and a pointer to the + // reorg_type_prime + tree pointer_rep = + lang_hooks.types.make_type( RECORD_TYPE); + + tree index_name = get_identifier("index"); + tree index_field = build_decl( BUILTINS_LOCATION, + FIELD_DECL, + index_name, + long_integer_type_node); + tree base_name = get_identifier("base"); + tree base_field = build_decl( BUILTINS_LOCATION, + FIELD_DECL, + base_name, + reorg_type_prime); + insert_field_into_struct( pointer_rep, index_field); + insert_field_into_struct( pointer_rep, base); + + reorg_type->pointer_rep = pointer_rep; + */ + + tree pointer_rep = make_signed_type ( TYPE_PRECISION ( pointer_sized_int_node)); + TYPE_MAIN_VARIANT ( pointer_rep) = TYPE_MAIN_VARIANT ( pointer_sized_int_node); + //DEBUG("Issue with gcc_ of reorg\n"); + //DEBUG_F(print_reorg, stderr, 2, ri); + const char *gcc_name = + identifier_to_locale ( IDENTIFIER_POINTER ( TYPE_NAME ( ri->gcc_type))); + size_t len = + strlen ( REORG_SP_PTR_PREFIX) + strlen ( gcc_name); + char *name = ( char *)alloca(len + 1); + strcpy ( name, REORG_SP_PTR_PREFIX); + strcat ( name, gcc_name); + TYPE_NAME ( pointer_rep) = get_identifier ( name); + ri->pointer_rep = pointer_rep; + //DEBUG_L("pointer_rep = "); + //DEBUG_F( print_generic_expr, stderr, pointer_rep, (dump_flags_t)-1); + //DEBUG("\n"); + //DEBUG_A("TYPE_MAIN_VARIANT ( pointer_rep) = "); + //DEBUG_F( print_generic_expr, stderr, TYPE_MAIN_VARIANT ( pointer_rep), (dump_flags_t)-1); + //DEBUG("\n"); + + // Note, we also declare a base type variable (globally.) + // This variable also belong in the ReorgType. + + // Set name of reorg_type_prime + const char *base_type_name = + identifier_to_locale ( IDENTIFIER_POINTER ( TYPE_NAME ( ri->gcc_type))); + len = strlen ( REORG_SP_PREFIX) + strlen ( base_type_name); + char *rec_name = ( char*)alloca ( len + 1); + strcpy ( rec_name, REORG_SP_PREFIX); + strcat ( rec_name, base_type_name); + + //DEBUG_L("TYPE_SIZE(reorg_type_prime): %p\n", TYPE_SIZE(reorg_type_prime)); + + // Build the new pointer type fields + TYPE_NAME ( reorg_type_prime) = get_identifier ( rec_name); + tree field; + tree new_fields = NULL; + for ( field = TYPE_FIELDS ( type); field; field = DECL_CHAIN ( field)) + { + //DEBUG_F( print_generic_decl, stderr, field, TDF_DETAILS); // example + tree tree_type = TREE_TYPE ( field); + tree new_fld_type = build_pointer_type ( tree_type); + tree new_decl = + build_decl ( DECL_SOURCE_LOCATION (field), + FIELD_DECL, DECL_NAME (field), new_fld_type); + DECL_CONTEXT ( new_decl) = reorg_type_prime; + layout_decl ( new_decl, 0); + + // We might be missing a bunch of attributes (see + // tree-nested.c:899) But we seem without without them! + + DECL_CHAIN ( new_decl) = new_fields; // <- bug: need decl, not type + new_fields = new_decl; + //DEBUG( "built new pointer type field:"); + //DEBUG_F( print_generic_decl, stderr, new_decl, TDF_DETAILS); + //DEBUG( "\n"); + } + + //DEBUG_L("TYPE_SIZE(reorg_type_prime): %p\n", TYPE_SIZE(reorg_type_prime)); + + // store reversed fields into reorg_type_prime + TYPE_FIELDS ( reorg_type_prime) = NULL; + tree next_fld; + for ( field = new_fields; + field; + field = next_fld ) + { + next_fld = DECL_CHAIN ( field); + DECL_CHAIN ( field) = TYPE_FIELDS ( reorg_type_prime); + TYPE_FIELDS ( reorg_type_prime) = field; + } + //DEBUG_L("TYPE_SIZE(reorg_type_prime): %p\n", TYPE_SIZE(reorg_type_prime)); + // Fix-up the layout + layout_type ( reorg_type_prime); + + // HERE + // Create the base element for a reorg type. This is for the single + // pool case only. + tree base_var = + build_decl ( UNKNOWN_LOCATION, VAR_DECL, NULL_TREE, ri->reorg_ver_type); + // We don't want to manually set DECL_INITIAL here! + + const char *type_name = + identifier_to_locale ( IDENTIFIER_POINTER ( TYPE_NAME ( ri->gcc_type))); + size_t tlen = strlen ( REORG_SP_BASE_PREFIX) + strlen ( type_name); + char *base_name = ( char*)alloca ( tlen + 1); + strcpy ( base_name, REORG_SP_BASE_PREFIX); + //DECL_NAME ( base_var) = get_identifier ( base_name); + + strcat ( base_name, type_name); + + DECL_NAME ( base_var) = get_identifier ( base_name); // wrong spot above??? + + TREE_STATIC ( base_var) = 1; + TREE_ADDRESSABLE ( base_var) = 1; + DECL_NONALIASED ( base_var) = 1; + SET_DECL_ALIGN ( base_var, TYPE_ALIGN ( ri->reorg_ver_type)); + + varpool_node::finalize_decl ( base_var); + + relayout_decl ( base_var); + + ri->instance_interleave.base = base_var; + } + + // Mess with the original type too because it might + // have base_type_fldinterior elements that are modified. + for ( field = TYPE_FIELDS ( type); + field; + field = DECL_CHAIN ( field)) + { + if ( TREE_CODE ( field) == RECORD_TYPE ) + { + layout_changed = + layout_changed || ( *( info->struct_types)) [ field].layout_changed; + } + else + { + // process pointers to reorg types + if ( POINTER_TYPE_P ( field) ) + { + tree field_type = TREE_TYPE ( field); + if ( is_reorg_type ( field_type, info) ) + { + // Change field type. + + // If multi-pool then set layout_changed to true. + + // The type pointed to changes for single-pool. + ReorgType_t *ri = + get_reorgtype_info ( field_type, info); + gcc_assert ( ri->pointer_rep); + TREE_TYPE ( field) = ri->pointer_rep; + } + tree base = base_type_of ( field); + if ( is_reorg_type ( base, info) ) + { + // strip off a layer of pointers + gcc_assert ( TREE_TYPE ( TREE_TYPE( field))); + TREE_TYPE ( field) = TREE_TYPE ( TREE_TYPE( field)); + } + } + } + } + + // Mark the type as processed + ( *( info->struct_types)) [ type] = { true, layout_changed}; +} + +static tree +find_coresponding_field ( tree base_decl, tree field) +{ + tree reorg_field; + for ( reorg_field = TYPE_FIELDS ( TREE_TYPE ( base_decl)); + reorg_field; + reorg_field = DECL_CHAIN ( reorg_field)) + { + const char *reorg_field_name = + lang_hooks.decl_printable_name ( reorg_field, 2); + const char *field_name = + lang_hooks.decl_printable_name ( field, 2); + //DEBUG_L("LOOK %s, %s\n", reorg_field_name, field_name); + + if ( strcmp ( reorg_field_name, field_name) == 0 ) + { + gcc_assert ( TREE_TYPE( field) == TREE_TYPE( TREE_TYPE(reorg_field))); + return reorg_field; + } + } + internal_error ( "find_coresponding_field: found no field"); +} + +static void +remove_default_def ( tree default_def, struct function *func) +{ + size_t i; + tree ssa_name; + FOR_EACH_SSA_NAME ( i, ssa_name, func) + { + if ( default_def == ssa_name ) + { + SSANAMES ( func)->unordered_remove ( i); + return; + } + } +} + +static basic_block +make_bb ( char *msg, basic_block prev_bb ) +{ + basic_block ret = create_empty_bb ( prev_bb); + DEBUG_A( "make_bb ( %s, <bb %d>/%p ): <bb %d>/%p, prev: <bb %d>/%p, next: <bb %d>/%p\n", + msg, prev_bb->index, prev_bb, + ret->index, ret, + ret->prev_bb->index, ret->prev_bb, + ret->next_bb->index, ret->next_bb); + return ret; +} diff --git a/gcc/ipa-str-reorg-utils.c b/gcc/ipa-str-reorg-utils.c new file mode 100644 index 00000000000..cc6d5162af5 --- /dev/null +++ b/gcc/ipa-str-reorg-utils.c @@ -0,0 +1,300 @@ +#include "ipa-str-reorg-utils.h" + +// This really should be inaccessible to anyone. +const_tree +get_base_type_from_ptr_or_arr_type (const_tree old_pointer_type, + const_tree pointer_type, + unsigned int &indirection_level) +{ + if (pointer_type == NULL) + { + gcc_assert (TREE_CODE (old_pointer_type) != POINTER_TYPE); + gcc_assert (TREE_CODE (old_pointer_type) != ARRAY_TYPE); + return old_pointer_type; + } + return get_base_type_from_ptr_or_arr_type (pointer_type, + TREE_TYPE (pointer_type), + ++indirection_level); +} + +const_tree +get_base_type_from_ptr_or_arr_type (const_tree ptr_or_array, + unsigned int &indirection_level) +{ + const bool is_array = TREE_CODE (ptr_or_array) == ARRAY_TYPE; + const bool is_ptr = TREE_CODE (ptr_or_array) == POINTER_TYPE; + const bool is_array_or_ptr = is_array || is_ptr; + gcc_assert (is_array_or_ptr); + indirection_level = 0; + return get_base_type_from_ptr_or_arr_type (ptr_or_array, + TREE_TYPE (ptr_or_array), + indirection_level); +} + +const_tree +get_base_type_from_array_type (const_tree array_type, + unsigned int &indirection_level) +{ + gcc_assert (TREE_CODE (array_type) == ARRAY_TYPE); + return get_base_type_from_ptr_or_arr_type (array_type, indirection_level); +} + +const_tree +get_base_type_from_array_type (const_tree array_type) +{ + gcc_assert (TREE_CODE (array_type) == ARRAY_TYPE); + unsigned int indirection_level; + return get_base_type_from_array_type (array_type, indirection_level); +} + +const_tree +get_base_type_from_pointer_type (const_tree pointer_type, + unsigned int &indirection_level) +{ + gcc_assert (TREE_CODE (pointer_type) == POINTER_TYPE); + return get_base_type_from_ptr_or_arr_type (pointer_type, indirection_level); +} + +const_tree +get_base_type_from_pointer_type (const_tree pointer_type) +{ + gcc_assert (TREE_CODE (pointer_type) == POINTER_TYPE); + unsigned int indirection_level; + return get_base_type_from_pointer_type (pointer_type, indirection_level); +} + +const_tree +get_base_type (const_tree type) +{ + enum tree_code tree_code_type = TREE_CODE(type); + switch (tree_code_type) + { + case ARRAY_TYPE: return get_base_type_from_array_type(type); break; + case POINTER_TYPE: return get_base_type_from_pointer_type(type); break; + default: return type; break; + } + + gcc_unreachable(); + return NULL; +} +const char * +make_pointer_name (const_tree pointer) +{ + gcc_assert (TREE_CODE (pointer) == POINTER_TYPE); + unsigned int indirection_level; + const_tree base_type + = get_base_type_from_pointer_type (pointer, indirection_level); + const char *pointer_name = make_pointer_name (base_type, indirection_level); + return pointer_name; +} + +const char * +make_pointer_or_array_name (const char *base_type, const char *postfix) +{ + char *ptr; + int calculated_size = strlen (base_type) + strlen (postfix); + // TODO: Do not use asprintf? + // We'll let exit() deal with freeing this memory. + int retval = asprintf (&ptr, "%s%s", base_type, postfix); + gcc_assert (retval == calculated_size); + return ptr; +} + +const char * +make_array_postfix (unsigned int indirection_level) +{ + gcc_assert (indirection_level > 0); + static const char *max_indirection_level_str_array + = "[][][][][][][][][][][][][]"; + static const size_t size_array = strlen (max_indirection_level_str_array); + static const size_t postfix_size_array = 2; + static const size_t max_indirection_level_array + = size_array / postfix_size_array; + gcc_assert (indirection_level < max_indirection_level_array); + return max_indirection_level_str_array + size_array + - (indirection_level * postfix_size_array); +} + +const char * +make_pointer_postfix (unsigned int indirection_level) +{ + gcc_assert (indirection_level > 0); + static const char *max_indirection_level_str_pointer + = "************************"; + static const size_t size_pointer = strlen (max_indirection_level_str_pointer); + static const size_t postfix_size_pointer = 1; + static const size_t max_indirection_level_pointer + = size_pointer / postfix_size_pointer; + gcc_assert (indirection_level < max_indirection_level_pointer); + return max_indirection_level_str_pointer + size_pointer + - (indirection_level * postfix_size_pointer); +} + +const char * +make_pointer_name (const char *base_type_name, + const unsigned int indirection_level) +{ + const char *postfix = make_pointer_postfix (indirection_level); + const char *ptr = make_pointer_or_array_name (base_type_name, postfix); + return ptr; +} + +const char * +make_pointer_name (const_tree base_type, const unsigned int indirection_level) +{ + const char *struct_name = get_type_name (base_type); + return make_pointer_name (struct_name, indirection_level); +} + + +const char * +make_array_name (const char *base_type_name, + const unsigned int indirection_level) +{ + const char *postfix = make_array_postfix (indirection_level); + const char *ptr = make_pointer_or_array_name (base_type_name, postfix); + return ptr; +} + +const char * +make_array_name (const_tree base_type, const unsigned int indirection_level) +{ + const char *struct_name = get_type_name (base_type); + return make_array_name (struct_name, indirection_level); +} + +// TODO: deal with anonymous structs. +// Some records don't have identifier pointers +const char * +get_record_name (const_tree record) +{ + gcc_assert (record); + gcc_assert (TREE_CODE (record) == RECORD_TYPE); + tree name_tree = TYPE_NAME (record); + // The TYPE_NAME will be NULL_TREE for a type + // that is not a built-in type, the result of a typedef + // or a named class type. + // TODO: verify that we are never changing + // <name_tree_is_null> types + if (name_tree == NULL_TREE) + { + return "<name_tree_is_null>"; + } + + if (TREE_CODE (name_tree) == TYPE_DECL) + { + tree type_name = DECL_NAME (name_tree); + return IDENTIFIER_POINTER (type_name); + } + const char *identifier = IDENTIFIER_POINTER (name_tree); + gcc_assert (identifier); + return identifier; +} + +const char * +make_array_name (const_tree array) +{ + gcc_assert (TREE_CODE (array) == ARRAY_TYPE); + unsigned int indirection_level; + const_tree base_type + = get_base_type_from_array_type (array, indirection_level); + const char *array_name = make_array_name (base_type, indirection_level); + return array_name; +} + +const char * +get_array_name (const_tree array) +{ + gcc_assert (array); + gcc_assert (TREE_CODE (array) == ARRAY_TYPE); + const bool is_modified = TYPE_NAME (array); + if (is_modified) + return IDENTIFIER_POINTER (TYPE_NAME (array)); + + return make_array_name (array); +} + +const char * +get_pointer_name (const_tree pointer) +{ + gcc_assert (pointer); + gcc_assert (TREE_CODE (pointer) == POINTER_TYPE); + const bool is_modified = TYPE_NAME (pointer); + if (is_modified) + return IDENTIFIER_POINTER (TYPE_NAME (pointer)); + + const char *new_pointer_name = make_pointer_name (pointer); + return new_pointer_name; +} + +const char * +make_reference_name (const_tree ref) +{ + gcc_assert (TREE_CODE (ref) == REFERENCE_TYPE); + const_tree base_type = TREE_TYPE (ref); + const char *old_name = get_type_name (base_type); + char *ptr; + static const char *prefix = "&"; + static const char *suffix = ".reorg"; + int new_size = strlen (prefix) + strlen (old_name) + strlen (suffix); + int retval = asprintf (&ptr, "%s%s%s", prefix, old_name, suffix); + gcc_assert (retval == new_size); + return ptr; +} + + +const char * +get_reference_name (const_tree ref) +{ + gcc_assert (ref); + gcc_assert (TREE_CODE (ref) == REFERENCE_TYPE); + const bool is_modified = TYPE_NAME (ref); + if (is_modified) + return IDENTIFIER_POINTER (TYPE_NAME (ref)); + + const char *new_pointer_name = make_reference_name (ref); + return new_pointer_name; +} + +const char * +get_type_name (const_tree type) +{ + enum tree_code code = TREE_CODE (type); + switch (code) + { + case ARRAY_TYPE: + return get_array_name (type); + break; + case POINTER_TYPE: + return get_pointer_name (type); + break; + case RECORD_TYPE: + return get_record_name (type); + break; + case REFERENCE_TYPE: + return get_reference_name (type); + break; + default: + // TODO: generalize even more? + // wait for experimental results to dictate what + // else we should specify. + return get_tree_code_name (code); + break; + } + return NULL; +} + +const char * +get_field_name (const_tree field_decl) +{ + gcc_assert (field_decl); + gcc_assert (TREE_CODE (field_decl) == FIELD_DECL); + // TODO: deal with anonymous fields. + tree id = DECL_NAME (field_decl); + if (!id) + return "anonymous"; + gcc_assert (id); + const char *identifier = IDENTIFIER_POINTER (id); + gcc_assert (identifier); + return identifier; +} diff --git a/gcc/ipa-str-reorg-utils.h b/gcc/ipa-str-reorg-utils.h new file mode 100644 index 00000000000..98b81acb565 --- /dev/null +++ b/gcc/ipa-str-reorg-utils.h @@ -0,0 +1,39 @@ +#ifndef GCC_IPA_STR_REORG_UTILS_H +#define GCC_IPA_STR_REORG_UTILS_H +#pragma once + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" + +const char * get_type_name (const_tree type); +const char * get_reference_name (const_tree ref); +const char * make_reference_name (const_tree ref); +const char * get_pointer_name (const_tree pointer); +const char * get_array_name (const_tree array); +const char * get_record_name (const_tree record); +const char * make_array_name (const_tree base_type, const unsigned int indirection_level); +const char * make_array_name (const char *base_type_name, const unsigned int indirection_level); +const char * make_pointer_name (const_tree base_type, const unsigned int indirection_level); +const char * make_pointer_name (const char *base_type_name, const unsigned int indirection_level); +const char * make_pointer_postfix (unsigned int indirection_level); +const char * make_array_postfix (unsigned int indirection_level); +const char * make_array_name (const_tree array); +const char * make_pointer_or_array_name (const char *base_type, const char *postfix); +const char * make_pointer_name (const_tree pointer); +const_tree get_base_type_from_ptr_or_arr_type (const_tree old_pointer_type, const_tree pointer_type, unsigned int &indirection_level); +const_tree get_base_type_from_ptr_or_arr_type (const_tree ptr_or_array, unsigned int &indirection_level); +const_tree get_base_type_from_array_type (const_tree array_type, unsigned int &indirection_level); +const_tree get_base_type_from_array_type (const_tree array_type); +const_tree get_base_type_from_pointer_type (const_tree pointer_type, unsigned int &indirection_level); +const_tree get_base_type_from_pointer_type (const_tree pointer_type); +const_tree get_base_type (const_tree type); +const char* get_field_name (const_tree type); + +#include <stdio.h> + +#include "types-inlines.h" + + +#endif diff --git a/gcc/ipa-structure-reorg.c b/gcc/ipa-structure-reorg.c new file mode 100644 index 00000000000..639a1e876f3 --- /dev/null +++ b/gcc/ipa-structure-reorg.c @@ -0,0 +1,2851 @@ +/* Interprocedural structure reorganization + Copyright (C) 2019-2020 Free Software Foundation, Inc. + + Contributed by Gary Oblock <gary@amperecomputing.com> + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "tree.h" +#include "tree-ssa.h" +#include "gimple.h" +#include "gimple-iterator.h" +#include "gimple-walk.h" +#include "gimple-collector.hpp" +#include "gimple-escaper.hpp" +#include "tree-pass.h" +#include "tree-cfg.h" +#include "cgraph.h" +#include "dumpfile.h" +#include "pretty-print.h" +#include "tree-pretty-print.h" +#include "gimple-pretty-print.h" +#include "langhooks.h" +#include "collect-types.h" +#include "stor-layout.h" +#include "tree-dfa.h" +#include <vector> +#include <map> +#include <set> +#include "ipa-structure-reorg.h" +#include "tree-phinodes.h" +#include "ssa-iterators.h" + + +static void setup_debug_flags ( Info *); +static void initial_debug_info ( Info *); +static void final_debug_info ( Info *); +static unsigned int reorg_analysis ( Info *); +static void reorg_analysis_debug ( Info *, ReorgType *); +static bool find_decls_and_types ( Info *); +#if USE_REORG_TYPES +static void add_reorg_type( tree, Info *); +#endif +static void disqualify_struct_in_struct_or_union ( Info *); +static void initial_reorg_debug ( Info *, ReorgType *reorg ); +static void disqualify_struct_in_struct_or_union_debug ( Info *, + ReorgType *); +static void disq_str_in_str_or_union_helper ( tree, + std::set<tree> *, + Info *); +static unsigned int reorg_qualification ( Info *); +static bool transformation_legality ( Info *); +static void transformation_legality_debug ( Info *, ReorgType *); +static bool reorg_legality ( Info *); +static void reorg_common_middle_code ( Info *); +static void modify_declarations ( Info *); +static bool modify_func_decl_core ( struct function *, Info *); +static void disqualify_all_reorgtypes_of ( gimple *, int, Info *); +static void adjust_result_decl ( struct function *); +static tree modify_func_type ( struct function *, Info *); +static bool needs_modification_p ( struct function *, Info *); +//static int number_of_levels ( tree); +//static void modify_decl_core ( tree *, Info *); +static void reorg_forbidden ( gimple *, Info *); +// Name changed and moved to its own file +//static void reorg_transformation ( Info *); +// made extern +//static void delete_reorgtype ( ReorgType_t *, Info *); +//static void undelete_reorgtype ( ReorgType_t *, Info *); +//static void remove_deleted_types ( Info *, ReorgFn); +//static enum ReorgOpTrans recognize_op ( tree, Info *); +//static ReorgTransformation reorg_recognize ( gimple *, Info *); +//static bool is_reorg_type ( tree, Info *); +//static tree base_type_of ( tree); +static bool is_user_function ( gimple *, cgraph_node *, Info *); +static bool is_reorg_alloc_trigger ( gimple *); +static ReorgType_t *find_struct_type_ptr_to_struct ( tree, Info *); +//static ReorgType_t *get_reorgtype_info ( tree, Info *); +//static void print_reorg_with_msg ( FILE *, ReorgType_t *, int, const char *); +static void dump_reorg ( ReorgType_t *reorg); +static void print_reorgs ( FILE *, int, Info *); +static void print_detailed_reorgs ( FILE *, int, Info *); +//static void print_reorg ( FILE *, int, ReorgType_t *); +static void print_progdecls ( FILE *, int, Info *); +static void print_progdecl ( FILE *, int, ProgDecl_t *); +//static void print_program ( FILE *, bool, int); +static void print_function ( FILE *, int, function *); +static ReorgType_t *get_reorgtype( gimple *stmt, Info *, int); +static int num_reorgtypes( gimple *, Info *); +static bool uses_field_of_reorgtypes( gimple *, Info *); + +//-- debugging only -- +#if DEBUGGING +//static const char *code_str( enum tree_code); +static const char *type_name_to_str( tree); +//static void handle_debug_indenting( int); +int debug_indenting = 0; +#endif +//---------------- Code Follows ---------------- + +static unsigned int +ipa_structure_reorg ( void) +{ + std::vector <ReorgType_t> Reorg_Type; + std::vector <ReorgType_t> Saved_Reorg_Type; + std::vector <ProgDecl_t> Prog_Decl; + std::map <tree,BoolPair_t> StructTypes; // TBD horrible type name + + //DEBUG_L( "Running ipa_structure_reorg\n"); + //INDENT(2); + + // TBD we must have run the IPA points-to analysis and + // be running in a single LTRANS partition. Sanity check + // these here. + + // TODO: + // Why not make this a class and avoid having all these parameters + // to initialize? + // Also, all functions should be references and not pointers... + Info info(&Reorg_Type, &Saved_Reorg_Type, &Prog_Decl, &StructTypes); + //DEBUG_L("At init dum_deleted %d\n",info.num_deleted); + + cgraph_node* node; + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) node->get_untransformed_body (); + + DEBUG_F( ssa_check, stderr, Show_everything, Do_not_fail, false, true); + + setup_debug_flags ( &info); + initial_debug_info ( &info); + + //DEBUG_L(""); + //DEBUG_F( wolf_fence, &info); + + if ( !reorg_analysis ( &info) ) + { + return true; + } + + DEBUG_L("after reorg_analysis\n"); + + bool qualified = reorg_qualification(&info); + DEBUG_L("after reorg_qualification\n"); + //DEBUG_L(""); + //DEBUG_F(wolf_fence, &info); + + + if ( qualified ) + { + if ( flag_ipa_structure_reorg || flag_ipa_dead_field_eliminate ) + { + log("before str_reorg_dead_field_eliminate_qual \n"); + str_reorg_dead_field_eliminate_qual ( &info); + // Because I just want to do this now... + #if KLUDGE + return true; + #endif + } + if ( flag_ipa_structure_reorg || flag_ipa_field_reorder ) + { + str_reorg_field_reorder_qual ( &info); + } + if ( flag_ipa_structure_reorg || flag_ipa_instance_interleave ) + { + //DEBUG_L("before str_reorg_instance_interleave_qual\n"); + str_reorg_instance_interleave_qual ( &info); + //DEBUG_L("after str_reorg_instance_interleave_qual\n"); + //DEBUG_L(""); + //DEBUG_F(wolf_fence, &info); + + } + + if ( info.show_all_reorg_cands_in_detail ) + { + fprintf ( info.reorg_dump_file, "Qualified the following types:\n"); + print_detailed_reorgs ( info.reorg_dump_file, 2, &info); + } + + reorg_common_middle_code( &info); // ??? might not amount to anything + DEBUG_L("after reorg_common_middle_code\n"); + //DEBUG_L(""); + //DEBUG_F(wolf_fence, &info); + + if ( flag_ipa_structure_reorg || flag_ipa_dead_field_eliminate ) + { + str_reorg_dead_field_eliminate_trans ( &info); + } + if ( flag_ipa_structure_reorg || flag_ipa_field_reorder ) + { + str_reorg_field_reorder_trans ( &info); + } + if ( flag_ipa_structure_reorg || flag_ipa_instance_interleave ) + { + str_reorg_instance_interleave_trans ( &info); + } + } + + final_debug_info ( &info); + + return true; +} + +static void +setup_debug_flags ( Info *info) +{ + // The normal way of doing this would be to + // set the flags with dump_file && (dump_flags & TDF_XXX + // where XXX is some level of dump control but + // I think the code here would work better + // (where each flag is set off the dump_flags.) + + if ( dump_file ) + { + info->show_all_reorg_cands = true; + info->show_all_reorg_cands_in_detail = dump_flags & TDF_DETAILS; + info->show_prog_decls = true; + info->show_delete = dump_flags & TDF_DETAILS; + info->show_new_BBs = dump_flags & TDF_DETAILS; + info->show_transforms = dump_flags & TDF_DETAILS; + info->show_bounds = dump_flags & TDF_DETAILS; + info->reorg_dump_file = dump_file; + #if DEBUGGING + info->reorg_dump_file = stderr; + #endif + } +} + +static void +initial_debug_info ( Info *info) +{ + if ( info->reorg_dump_file ) + { + print_program ( info->reorg_dump_file, PRINT_FORMAT, 0, info); + } +} + +static void +final_debug_info ( Info *info) +{ + if ( info->reorg_dump_file ) + { + print_program ( info->reorg_dump_file, PRINT_FORMAT, 0, info); + } +} + +static unsigned int +reorg_analysis ( Info *info) +{ + // TODO: + // Gary, this main "analysis" method seems to have a lot of + // instance interleave specific code. Shouldn't this method + // concretely be just the escape analysis? + + + // TODO: + // Gary, this is me adding a way to run the escape analysis... + // It is only triggered when flag_ipa_structure_reorg is + // specified since I am not sure what this function should + // concretely do. + // Eric this is not really helping me... ;-) + DEBUG_L("reorg_analysis: entered\n"); + #if INTEGRATION_FUNCTIONAL + const bool run_escape_analysis = flag_ipa_dead_field_eliminate && !flag_ipa_instance_interleave && !flag_ipa_field_reorder; + if (run_escape_analysis) + { + GimpleTypeCollector collector; + collector.walk(); + ptrset_t types = collector.get_pointer_set(); + GimpleEscaper gimpleEscaper(types); + gimpleEscaper.walk(); + info->sets = gimpleEscaper.get_sets(); + info->sets.print_in_points_to_record(); + return true; + } + #endif + struct cgraph_node *node; + + find_decls_and_types ( info); + + // Skip computing numbOfGlobalArrays initially. + + // Skip computing numbOfLocalArrays initially. + + // Compute numbOfDynmAllocs per type in regtype + FOR_EACH_FUNCTION ( node) { + basic_block bb; + + struct function *func = DECL_STRUCT_FUNCTION ( node->decl); + + // This is a work around + if ( func == NULL ) + { + //DEBUG_L(" tripped work around\n"); + continue; + } + //DEBUG_L(" bypassed work around\n"); + + // Note,there is a major design issue with the design of this code. + // This can stand as is for now but it must be fixed relatively soon. + FOR_EACH_BB_FN ( bb, func) + { + gimple_stmt_iterator gsi; + for ( gsi = gsi_start_bb ( bb); + !gsi_end_p ( gsi); + gsi_next ( &gsi) ) + { + gimple *stmt = gsi_stmt ( gsi); + DEBUG_L ( ""); + DEBUG_F ( print_gimple_stmt, stderr, stmt, 0); + INDENT(2); + if ( is_gimple_call ( stmt) ) + { + // next line has issues but the mechanism is sound + tree t = *gimple_call_lhs_ptr ( stmt); + DEBUG_A( "t %p\n", t); + // Calls to a function returning void are skipped. + if ( t != NULL ) + { + DEBUG_A( "t: "); + DEBUG_F( flexible_print, stderr, t, 1, (dump_flags_t)0); + tree type = TREE_TYPE( t); + DEBUG_A( "type: "); + DEBUG_F( flexible_print, stderr, type, 1, (dump_flags_t)0); + //tree bt = base_type_of ( t); + tree bt = base_type_of ( type); + if ( TREE_CODE( bt) != RECORD_TYPE && TREE_CODE( bt) != VOID_TYPE) + { + DEBUG_A( "TREE_CODE( bt) == %s\n", code_str( TREE_CODE ( bt))); + DEBUG_A(""); + DEBUG_F(flexible_print, stderr, bt, 1, (dump_flags_t)0); + INDENT(-2); + continue; + } + if ( TREE_CODE( bt) == VOID_TYPE ) + { + // find the use of lhs. If is an assign + // get use the base type of it's lhs. + // Otherwise never mind. + gimple *use_stmt; + use_operand_p immuse; + bool yup_a_use = single_imm_use ( t, &immuse, &use_stmt); + DEBUG_A("VOID case: %sa single imm use, ", yup_a_use ? "" : "not "); + DEBUG("%san assign\n", + yup_a_use && is_gimple_assign ( use_stmt) ? "" : "not "); + if ( TREE_CODE ( t) == SSA_NAME + && yup_a_use + && is_gimple_assign ( use_stmt) ) + { + tree use_lhs = gimple_assign_lhs ( use_stmt); + bt = base_type_of ( TREE_TYPE ( use_lhs)); + } + else + continue; + } + + // find if in reorgtypes and get the info (in one call) + ReorgType_t *ri = get_reorgtype_info ( bt, info); + if ( ri != NULL && is_reorg_alloc_trigger ( stmt) ) + { + DEBUG_L( "Found allocaion: \n"); + DEBUG_A( " Reorg: "); + DEBUG_F( print_reorg, stderr, 0, ri); + DEBUG("\n"); + // TBD this needs to increment with the execution count + // instead of one. I hope the build-in block count estimation + // will work or a DIY solution might be called for. + ri->instance_interleave.numbOfDynmAllocs++; + } + } + } + INDENT(-2); + } + } + } + + DEBUG_L( "possible deletes:\n"); + INDENT(2); + // It's LOT more clear to use an iterator here TBD + for ( int i = 0; i < info->reorg_type->size (); i++ ) + { + int n = (*(info->reorg_type))[i].instance_interleave.numbOfGlobalArrays + + (*(info->reorg_type))[i].instance_interleave.numbOfLocalArrays + + (*(info->reorg_type))[i].instance_interleave.numbOfDynmAllocs; + if ( n > 1 ) + { + (*(info->reorg_type))[i].instance_interleave.multi_pool = true; + } + // Note when multi-pools are enabled the test should be + // "n == 0" but until then... + DEBUG_A("%d pools\n",n) + if ( n != 1 ) + { + delete_reorgtype ( &(*(info->reorg_type))[i], info); + } + } + INDENT(-2); + + //DEBUG_L("after reorg_analysis\n"); + remove_deleted_types ( info, &reorg_analysis_debug); + + if ( info->show_all_reorg_cands ) + { + fprintf ( info->reorg_dump_file, "All Reorg Analysis ReorgTypes:\n"); + print_reorgs ( info->reorg_dump_file, 2, info); + } + + return !info->reorg_type->empty (); +} + +void +reorg_analysis_debug ( Info *info, ReorgType *reorg ) +{ + if ( info->show_delete ) + { + print_reorg_with_msg ( info->reorg_dump_file, reorg, 2, + "Was not allocated"); + } +} + +static bool +find_decls_and_types ( Info *info) +{ + //DEBUG_L("find_decls_and_types: entered\n"); + + // Don't keep any structure types if they aren't + // used in an array or have a pointer type (which + // hopefully will have an associated allocation.) + // Note, initially ignore the explicit statically + // allocated arrays. + // + // This initializes them too of course. + + // Find all struct types for initial ReorgTypes + // marking them all to initially be deleted. + // This is done by walking all variables. + + std::set<tree> typeset; + varpool_node *var; + FOR_EACH_VARIABLE ( var) + { + tree decl = var->decl; + //DEBUG_L( "Consider var->decl\n"); + //DEBUG_L( ""); + //DEBUG_F( print_generic_decl, stderr, decl, (dump_flags_t)-1); + tree base = base_type_of ( decl); + //DEBUG( "\n"); + //DEBUG_A("Base\n"); + //DEBUG_A( "TREE_CODE = %s, ", code_str( TREE_CODE ( base))); + //DEBUG_F( print_generic_expr, stderr, base, (dump_flags_t)-1); + //DEBUG( "\n"); + + if ( TREE_CODE ( base) == RECORD_TYPE ) + { + // skip if found before + if ( typeset.find ( base) != typeset.end () ) + { + //DEBUG_L( " not found\n"); + continue; + } else { + //DEBUG_L( " found\n") + ; + } + + #if USE_REORG_TYPES + add_reorg_type ( base, info); + #endif + typeset.insert ( base); // ??? + } + } + + // NOTE, the scheme above leaves out local variables so + // I'll repeat the for the local variable of functions. + + cgraph_node* node; + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) + { + tree decl; + unsigned i; + + node->get_untransformed_body (); + + struct function *fn = DECL_STRUCT_FUNCTION ( node->decl); + // enable this to see error in test_1_8. Note, not a bug... + //DEBUG_L( " function name = %s\n", lang_hooks.decl_printable_name ( node->decl, 2)); + if ( fn == NULL ) + { + //DEBUG_L( " EMPTY\n"); + continue; + } + + //INDENT(2); + FOR_EACH_LOCAL_DECL ( fn, i, decl) + { + tree base = base_type_of ( decl); + // enable this to see error in test_1_8. Note, not a bug... + //DEBUG_L( "Consider local var decl\n"); + //DEBUG_F( print_generic_decl, stderr, decl, (dump_flags_t)-1); + //DEBUG( "\n"); + + if ( TREE_CODE ( base) == RECORD_TYPE) + { + if ( typeset.find ( base) != typeset.end () ) + { + //INDENT(-2) + continue; + } + + #if USE_REORG_TYPES + add_reorg_type ( base, info); + #endif + typeset.insert ( base); // ??? + } + } + //INDENT(-2); + } + + // We need this later for creating new types + for ( std::set<tree>::iterator ti = typeset.begin (); + ti != typeset.end (); + ti++ ) + { + (*(info->struct_types))[*ti] = { false, false }; + } + + if ( info->show_all_reorg_cands_in_detail ) + { + fprintf ( info->reorg_dump_file, "All possible candidates:\n"); + print_reorgs ( info->reorg_dump_file, 2, info); + } + + // Scan all declarations for pointers to ReorgTypes + // and in a later version array of them. When found + // clear the deletion mark. + // Note, there is no mechanism for looking at global declarations + // so use FOR_EACH_VARIABLE instead. I'm not 100% this is the thing + // actuall do here... but... + + //DEBUG_L( "Scan all global decls for pointers to ReorgTypes (possible deletes)\n"); + //INDENT(2); + + FOR_EACH_VARIABLE ( var) + { + tree decl = var->decl; + + //DEBUG_A( "look at each global var for undelete: "); + //DEBUG_F( print_generic_decl, stderr, decl, (dump_flags_t)-1); + //DEBUG( "\n"); + + tree type = TREE_TYPE ( decl); + ReorgType_t *rtype = find_struct_type_ptr_to_struct ( type, info); + if ( rtype != NULL ) + { + undelete_reorgtype ( rtype, info); + } + } + //INDENT(-2); + + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) + { + tree decl; + unsigned i; + + // Only need to do this once in the program and it was done + // above! + //Node->get_untransformed_body (); + + struct function *fn = DECL_STRUCT_FUNCTION ( node->decl); + + //DEBUG_L( "fn %p\n", fn); + //DEBUG_A(""); + //DEBUG_F( print_generic_decl, stderr, node->decl, (dump_flags_t)-1); + //DEBUG( "\n"); + // I don't know why this is coming up null.... but I'll + // skip it for now. + if ( fn == NULL ) + { + continue; + } + + //DEBUG_L( "possible deletes:\n"); + //INDENT(2); + FOR_EACH_LOCAL_DECL ( fn, i, decl) + { + // Does this work... see tree.c:6132 + tree type = TREE_TYPE ( decl); + ReorgType_t *rtype = find_struct_type_ptr_to_struct ( type, info); + if ( rtype != NULL ) + { + undelete_reorgtype ( rtype, info); + } + } + //INDENT(-2); + } + + if ( info->show_all_reorg_cands ) + { + fprintf ( info->reorg_dump_file, "All preliminary ReorgTypes:\n"); + print_reorgs ( info->reorg_dump_file, 2, info); + } + + // Scan all types in ReorgTypes for structure fields + // and if they are pointers to a type Q in ReorgTypes + // then clear the deletion mark of Q. Note, at this + // point in the execution ReorgTypes is all the structure + // types. + // + // It would be a bit nuts to allocate memory and hang it + // off of pointer in a structure, but it's still possible. + // Note, if there are no pointers to a structure of a type + // then it is impossible to dynamically allocate memory of + // that type. This of course assumes sane programming + // practices and if they violate those structure reorg has + // every right to punt. + //DEBUG_L( "Examine imbedded pointers\n"); + //INDENT(2); + for ( std::vector<ReorgType_t>::iterator ri = info->reorg_type->begin (); + ri != info->reorg_type->end (); + ri++ ) + { + //DEBUG_A(""); + //DEBUG_F( dump_reorg, &(*ri)); + //DEBUG("\n"); + //INDENT(2); + for ( tree fld = TYPE_FIELDS ( ri->gcc_type); + fld; + fld = DECL_CHAIN ( fld) ) + { + tree field_type = TREE_TYPE( fld); + ReorgType_t *rtype = + find_struct_type_ptr_to_struct ( field_type, info); + if ( rtype != NULL ) + { + undelete_reorgtype ( rtype, info); + } + } + //INDENT(-2); + } + //INDENT(-2); + //DEBUG_L( "after Scan all types in ReorgTypes for structure fields\n"); + remove_deleted_types ( info, &initial_reorg_debug); + + // Disqualifying structures in interior to structures is optional + // (see comment at end of type escape section) but if it's not + // done it commits the optimization to do something a little too + // involved for the initial version. + disqualify_struct_in_struct_or_union ( info); + + if ( info->reorg_type->empty () ) + { + if ( info->show_all_reorg_cands_in_detail ) + { + fprintf ( info->reorg_dump_file, "find_decls_and_types: Found no types\n"); + } + return false; + } + + // initialize ids of ReorgTypes + int id = 0; + for ( std::vector<ReorgType_t>::iterator ri = info->reorg_type->begin (); + ri != info->reorg_type->end (); + ri++ ) + { + ri->id = id; + id++; + } + + // Scan all declarations. If their type is in ReorgTypes + // add them to ProgDecl. + // Note, there is no mechanism for looking at global declarations + // so use FOR_EACH_VARIABLE instead. I'm not 100% this is the thing + // actuall do here... but... + //DEBUG_L( "ProgDecl global declarations:\n"); + FOR_EACH_VARIABLE ( var) + { + tree decl = var->decl; + tree type = base_type_of ( decl); + if ( TREE_CODE ( type ) == RECORD_TYPE && + get_reorgtype_info ( type, info) != NULL ) + { + ProgDecl_t decl_info; + decl_info.gcc_decl = decl; + info->prog_decl->push_back ( decl_info); + //DEBUG_A(""); + //DEBUG_F( print_progdecl, stderr, 2, &decl_info); + } + } + + if ( info->show_all_reorg_cands_in_detail ) + { + fprintf ( info->reorg_dump_file, "find_decls_and_types: Found the following types:\n"); + print_reorgs ( info->reorg_dump_file, 2, info); + } + + if ( info->show_prog_decls ) + { + fprintf ( info->reorg_dump_file, "ProgDecls:\n"); + print_progdecls ( info->reorg_dump_file, 2, info); + } + + return true; +} + +#if USE_REORG_TYPES +static void +add_reorg_type ( tree base, Info *info) +{ + ReorgType_t rt = + { 0, true, base, NULL, NULL, false, false, false, + { 0}, { 0}, { 0, 0, 0, NULL, 0.0, 0.0, false}}; + + //DEBUG_L("add_reorg_type: "); + //DEBUG_F(print_generic_expr, base, (dump_flags_t)0); + //DEBUG("\n"); + info->reorg_type->push_back ( rt); + // Remember the intial assumption is the type added will be deleted + // and is marked to be deleted. + info->num_deleted++; +} +#endif + +void +disqualify_struct_in_struct_or_union ( Info *info) +{ + varpool_node *var; + std::set<tree> typeset; + + //DEBUG_L( "In disqualify_struct_in_struct_or_union\n"); + //INDENT(2); + + FOR_EACH_VARIABLE ( var) + { + tree decl = var->decl; + + //DEBUG( "type %s\n", TYPE_NAME( TREE_TYPE( decl))); + + tree base = base_type_of ( decl); + if ( TREE_CODE ( base) == RECORD_TYPE + || TREE_CODE ( base) == UNION_TYPE ) + { + disq_str_in_str_or_union_helper ( base, &typeset, info); + typeset.insert ( base); + } + } + //INDENT(-2); + + // Repeating the above for local variables + cgraph_node* node; + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) + { + tree decl; + unsigned i; + + node->get_untransformed_body (); + + struct function *fn = DECL_STRUCT_FUNCTION ( node->decl); + //DEBUG_L( "function name = %s\n", + // lang_hooks.decl_printable_name ( node->decl, 2)); + if ( fn == NULL ) + { + continue; + } + + //INDENT(2); + FOR_EACH_LOCAL_DECL ( fn, i, decl) + { + tree base = base_type_of ( decl); + + //DEBUG_L( "local var decl: "); + //DEBUG_F( print_generic_decl, stderr, decl, (dump_flags_t)-1); + //DEBUG( "TREE_CODE( base) = %s\n", code_str( TREE_CODE ( base))); + + if ( TREE_CODE ( base) == RECORD_TYPE + || TREE_CODE ( base) == UNION_TYPE ) + { + disq_str_in_str_or_union_helper ( base, &typeset, info); + typeset.insert ( base); + } + } + //INDENT(-2); + } + //DEBUG_L("after disqualify_struct_in_struct_or_union\n"); + remove_deleted_types ( info, + &disqualify_struct_in_struct_or_union_debug); +} + +static void +initial_reorg_debug ( Info *info, ReorgType *reorg ) +{ + if ( info->show_delete ) + { + print_reorg_with_msg ( info->reorg_dump_file, reorg, 2, + "No Pointer to Structure"); + } +} + +static void +disqualify_struct_in_struct_or_union_debug ( Info *info, + ReorgType *reorg ) +{ + if ( info->show_delete ) + { + print_reorg_with_msg ( info->reorg_dump_file, reorg, 2, + "Interior Struct or Union"); + } +} + + +static void +disq_str_in_str_or_union_helper ( tree type, + std::set<tree> *typeset, + Info *info ) +{ + //DEBUG_L( "In disq_str_in_str_or_union_helper (possibele deletes)\n"); + //INDENT(2); + + if ( typeset->find ( type) != typeset->end ()) return; + tree fld; + for ( tree fld = TYPE_FIELDS( type); fld; fld = DECL_CHAIN ( fld) ) + { + //DEBUG_A( ": ", DECL_NAME( fld)); + //DEBUG_F( print_generic_decl, stderr, fld, (dump_flags_t)-1); + //DEBUG_F( print_generic_decl, stderr, fld, (dump_flags_t)-1); + //DEBUG( " -- "); + //INDENT(2); + + // If we go to the base we end up disqualifying pointer to reorganizable + // structure. That's not what we want! + tree field_type = TREE_TYPE( fld); + //tree base = base_type_of ( field_type); + if ( TREE_CODE ( field_type) == RECORD_TYPE ) // base to field type + { + DEBUG( "RECORD\n"); + + ReorgType_t *rinfo = get_reorgtype_info ( field_type, info); // base to field type + if ( rinfo != NULL ) + { + delete_reorgtype ( rinfo, info); + } else { + disq_str_in_str_or_union_helper ( field_type, typeset, info ); // base to field type + typeset->insert ( field_type); // might be bug here -- base to field type + } + } else { + if ( TREE_CODE ( field_type) == UNION_TYPE ) { // base to field type + //DEBUG( "UNION\n"); + + disq_str_in_str_or_union_helper ( field_type, typeset, info ); // base to field type + typeset->insert ( field_type); // might be bug here -- base to field type + } else { + //DEBUG( "%s\n", code_str( TREE_CODE ( field_type)) ); // base to field type + } + } + //INDENT(-2); + } + //INDENT(-2); + + return; +} + +static unsigned int +reorg_qualification ( Info *info) +{ + // TBD + // This only does a generic legality qualification and each + // subpass does its own performance qualification. + log("before reorg_leaglity...\n"); + unsigned int retval = reorg_legality( info); + log("after reorg_leaglity...\n"); + return retval; + +} + +// Return false if nothing qualified +bool +reorg_legality( Info *info) { + log("before transformation leagality...\n"); + bool retval = transformation_legality( info); + log("after transformation leagality...\n"); + return retval; +} + +bool +Info::is_non_escaping_set_empty() +{ + bool retval = this->sets.non_escaping.empty(); + return retval; +} + +// Return false if nothing qualified +// TODO: +// What exactly is the difference between legality and +// non_escaping? +bool +transformation_legality ( Info *info) +{ + //TODO: Gary, for my purposes, I need to start running the + // code related to dead field eliminate. So, I'll add this bit + // + // TODO: + // * Legality must include type-casting + // * Legality must include address ?? + // * What about memmove? + // + // struct astruct_s { _Bool a; _Bool b; _Bool c; }; + // struct astruct_s astruct_1; + // struct astruct_s astruct_2; + // GIMPLE_IL sizeof tree expr + // memmove(&astruct_1, &astruct_2, sizeof(struct astruct_s)); + // memmove(&astruct_1.b, &astruct_2.b, 2*sizeof(_Bool)); + const bool run_escape_analysis = flag_ipa_dead_field_eliminate && !flag_ipa_instance_interleave && !flag_ipa_field_reorder; + if (run_escape_analysis) + { + bool retval = !info->is_non_escaping_set_empty(); + return retval; + } + + cgraph_node* node; + + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) + { + struct function *func = DECL_STRUCT_FUNCTION ( node->decl); + basic_block bb; + + FOR_EACH_BB_FN ( bb, func) + { + gimple_stmt_iterator gsi; + for ( gsi = gsi_start_bb ( bb); + !gsi_end_p ( gsi); + gsi_next ( &gsi) ) + { + gimple *stmt = gsi_stmt ( gsi); + int num = num_reorgtypes ( stmt, info); + if ( num != 0 ) + { + ReorgTransformation trans = reorg_recognize( stmt, node, info); + switch ( trans ) + { + case Not_Supported: + //DEBUG_L("deleting %d reorgs for unsuported stmt: ", num); + //DEBUG_F ( print_gimple_stmt, stderr, stmt, 0); + disqualify_all_reorgtypes_of ( stmt, num, info); + case ReorgT_UserFunc: + // TBD ReorgT_Ptr2Zero does not catch all cases of + // setting a reorg pointer to zero. One that I + // discoivered in a dump is phis can hold constanst. + break; + case ReorgT_Return: + // TBD ReorgT_Ptr2Zero does not catch all cases of + // setting a reorg pointer to zero. One that I + // discoivered in a dump is phis can hold constanst. + break; + case ReorgT_Convert: + // TBD Note, any conversion of an integer type to a + // reorg pointer type can obsure the zero transformation + // and needs to disqualify the type. + break; + default: + // No problem. + ; + } + } + if ( uses_field_of_reorgtypes( stmt, info) ) + { + // This will mark types to be deleted if need be. + reorg_forbidden ( stmt, info ); + } + } + } + + // TBD Walk the PHIs looking for reorg type PHIs with a + // nonzero constant. Disqualify any typeas this happens with. + } + + //DEBUG_L("after transformation_legality\n"); + remove_deleted_types ( info, &transformation_legality_debug); + + return !info->reorg_type->empty (); +} + +void +transformation_legality_debug ( Info *info, ReorgType *reorg ) +{ + if ( info->show_delete ) + { + print_reorg_with_msg ( info->reorg_dump_file, reorg, 2, + "Unallowed transformation"); + } +} + +static void +reorg_common_middle_code ( Info *info) +{ + modify_declarations( info); +} + +static void +modify_declarations ( Info *info) +{ + // For the moment we ignore initializations assuming + // all potential reorg types that had initialized + // arrays were disqualified. Note, it's the way + // to go until statically allocated arrays are optimized + // Once we attempt statically allocated arrays problems + // crop up because some initializations aren't preserved + // until LTRANS time and even those that are don't necessarily + // lend themselves to any necessary reorg transformation. + // Note, it's possible to preserve them, if that makes sense, + // in remove_unreferenced_decls. + std::vector<ProgDecl_t>::iterator pv; + for ( pv = info->prog_decl->begin (); + pv != info->prog_decl->end (); pv++ ) + { + modify_decl_core ( &( pv->gcc_decl), info); + } + + // NOTE, Call modufy_decl_core breaks hello world! + + // Modify the declaration of the function type itself. + // Note, create a new declaration if necessary. If this + // loop already created and the function is seen a second + // time reuse the previous one created. + + // Note, most of the function type stuff can use memoization but + // it's not worth doing unless it proves to be a significant + // performance issue. This is what fncache is all about. But it's + // used not to save time but to guarantee that if two functions have + // the same type before this exercise, their new types will be + // equal afterwards. + std::map <tree,tree> fncache; + + struct cgraph_node *node; + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) + + { + struct function *func = DECL_STRUCT_FUNCTION ( node->decl); + push_cfun ( func); // Is this necessary? + tree curr_func_type = TREE_TYPE ( func->decl); + + #if 0 + modify_func_decl_core ( func, info); + #else + + // TBD Check cached function type. If the decl's type + // has been modified use the cached new type. + auto location = fncache.find ( curr_func_type); + tree old_ret_type = TREE_TYPE ( TREE_TYPE ( func->decl)); + if ( location != fncache.end () ) + { + TREE_TYPE ( func->decl) = location->second; + + // The function type can be cached but this must be created + // anew for any function who's return type has changed + if ( old_ret_type != TREE_TYPE ( TREE_TYPE ( func->decl)) ) + { + adjust_result_decl ( func); + } + + pop_cfun (); + continue; + } + + // check if funtion type needs modification. + if ( needs_modification_p ( func, info) ) + { + // Create new type and set the decl's type to it. + tree new_func_type = modify_func_type ( func, info); + + if ( old_ret_type != TREE_TYPE ( new_func_type)) + { + adjust_result_decl ( func); + } + + // Add the type to the cache. + fncache [ curr_func_type] = new_func_type; + } + #endif + pop_cfun (); + } + + // Note, do not relayout functions decls.... +} + +static void +disqualify_all_reorgtypes_of ( gimple *stmt, int num, Info *info) +{ + DEBUG_L("disqualify %d reorgtypes of: ", num); + DEBUG_F ( print_gimple_stmt, stderr, stmt, 0); + int i; + for ( i = 0; i < num; i++ ) + { + ReorgType_t *reorg_type = + get_reorgtype ( stmt, info, i); + delete_reorgtype ( reorg_type, info); + } +} + +static void +adjust_result_decl ( struct function *func) +{ + tree func_decl = func->decl; + tree ret_type = TREE_TYPE ( TREE_TYPE ( func_decl)); + tree decl_result; + + decl_result = + build_decl ( DECL_SOURCE_LOCATION ( func_decl), + RESULT_DECL, NULL_TREE, ret_type); + DECL_RESULT ( func_decl) = decl_result; + DECL_CONTEXT ( decl_result) = func_decl; +} + + +static tree +modify_func_type ( struct function *func, Info *info ) +{ + tree func_type = TREE_TYPE ( func->decl); + DEBUG_L("old func_type = "); + DEBUG_F( flexible_print, stderr, func_type, 1, (dump_flags_t)0); + INDENT(4); + tree new_type; + tree func_ret_type = TREE_TYPE ( func_type); + tree base = base_type_of ( func_ret_type); + + ReorgType_t *ri = get_reorgtype_info ( base, info); + if ( ri != NULL ) + { + // TBD Do level based stuff here + if ( number_of_levels ( func_ret_type ) == 1 ) + { + func_ret_type = TYPE_MAIN_VARIANT ( ri->pointer_rep); + } + else + { + gcc_assert(0); + } + } + tree arg; + tree new_args = NULL_TREE; + //DEBUG_A("old arg = "); + //DEBUG_F( flexible_print, stderr, arg, 1, (dump_flags_t)0); + for ( arg = TYPE_ARG_TYPES ( func_type); + arg != NULL && arg != void_list_node; + arg = TREE_CHAIN ( arg)) + { + //DEBUG_L("arg: "); + //DEBUG_F( flexible_print, stderr, arg, 1, (dump_flags_t)0); + + tree type_of_arg = TREE_VALUE (arg); + //DEBUG_L("type_of_arg: "); + //DEBUG_F( flexible_print, stderr, type_of_arg, 1, (dump_flags_t)0); + base = base_type_of ( type_of_arg); + //DEBUG_L("base: "); + //DEBUG_F( flexible_print, stderr, base, 1, (dump_flags_t)0); + + tree new_arg_type; + ri = get_reorgtype_info ( base, info); + if ( ri != NULL ) + { + if ( number_of_levels ( type_of_arg ) == 1 ) + { + new_arg_type = TYPE_MAIN_VARIANT ( ri->pointer_rep); + } + else + { + gcc_assert(0); + } + } + else + { + new_arg_type = type_of_arg; + } + new_args = tree_cons ( NULL_TREE, new_arg_type, new_args); + //DEBUG_A("interim new_args = "); + //DEBUG_F( flexible_print, stderr, new_args, 1, (dump_flags_t)0); + } + //DEBUG_A("before reverse new_args = "); + //DEBUG_F( flexible_print, stderr, new_args, 1, (dump_flags_t)0); + tree last = new_args; + new_args = nreverse ( new_args); + TREE_CHAIN ( last) = void_list_node; + + DEBUG_A("new args = "); + DEBUG_F( flexible_print, stderr, new_args, 1, (dump_flags_t)0); + INDENT(-4); + + new_type = build_function_type ( func_ret_type, new_args); + DEBUG_L("new_type (func) = "); + DEBUG_F( flexible_print, stderr, new_type, 1, (dump_flags_t)0); + TREE_TYPE ( func->decl) = new_type; + return new_type; +} + +static bool +needs_modification_p ( struct function *func, Info *info ) +{ + tree func_type = TREE_TYPE ( func->decl); + tree ret_type = TREE_TYPE ( func_type); + tree base = base_type_of ( ret_type); + if ( get_reorgtype_info ( base, info) != NULL ) return true; + + tree arg; + for ( arg = TYPE_ARG_TYPES ( func_type); arg != NULL; arg = TREE_CHAIN ( arg)) + { + base = base_type_of ( ret_type); + if ( get_reorgtype_info ( base, info) != NULL ) return true; + } + + return false; +} + +int +number_of_levels ( tree type) +{ +tree prev_type; + int levels = 0; + for ( ; TREE_CODE ( type) == POINTER_TYPE; levels++ ) + { + prev_type = type; + type = TREE_TYPE ( prev_type); + //DEBUG_A( "prev_type: %p, type: %p\n", prev_type, type); + } + //DEBUG_A("number_of_levels = %d\n", levels); + return levels; +} + +static bool +modify_func_decl_core ( struct function *func, Info *info) +{ + //DEBUG_L("BEFORE modify_func_decl_core:\n"); + //INDENT(4); + //DEBUG_A("func->decl = %p, ", func->decl); + //DEBUG_F( flexible_print, stderr, func->decl, 1, (dump_flags_t)0); + //DEBUG_A("TREE_TYPE (func->decl) = %p, ", TREE_TYPE (func->decl)); + //DEBUG_F( flexible_print, stderr, TREE_TYPE (func->decl), 1, (dump_flags_t)0); + //DEBUG_A("TREE_TYPE(TREE_TYPE (func->decl)) = %p, ", TREE_TYPE(TREE_TYPE (func->decl))); + //DEBUG_F( flexible_print, stderr, TREE_TYPE(TREE_TYPE (func->decl)), 1, (dump_flags_t)0); + // TBD Implement + tree *func_type_loc = &(TREE_TYPE(TREE_TYPE (func->decl))); + tree func_type = *func_type_loc; + tree base = base_type_of ( func_type); + + ReorgType_t *ri = get_reorgtype_info ( base, info); + if ( ri == NULL ) + { + //DEBUG_A("Return as not a reorg type.\n"); + //INDENT(-4); + return false; + } + //DEBUG_A("pointer_rep = "); + //DEBUG_F( flexible_print, stderr, ri->pointer_rep, 1, (dump_flags_t)0); + //DEBUG_A("TYPE_MAIN_VARIANT( pointer_rep) = "); + //DEBUG_F( flexible_print, stderr, TYPE_MAIN_VARIANT( ri->pointer_rep), 1, (dump_flags_t)0); + + int levels = number_of_levels ( func_type); + + // TBD This code must in the near future handle an + // abritary number of levels! + if ( levels == 1 ) + { + //DEBUG_A( "levels == 1\n"); + // Why type main variant pointer_rep ??? + // We created it! + gcc_assert ( TYPE_MAIN_VARIANT ( ri->pointer_rep)); + //TREE_TYPE( *func_type_loc) = TYPE_MAIN_VARIANT ( ri->pointer_rep); + //*func_type_loc = TYPE_MAIN_VARIANT ( ri->pointer_rep); + TREE_TYPE ( TREE_TYPE ( func->decl)) = TYPE_MAIN_VARIANT ( ri->pointer_rep); + } + else + { + //DEBUG_A( "levels != 1\n"); + gcc_assert(0); + } + + //DEBUG_L("AFTER modify_func_decl_core:\n"); + //DEBUG_A("func->decl = %p, ", func->decl); + //DEBUG_F( flexible_print, stderr, func->decl, 1, (dump_flags_t)0); + //DEBUG_A("TREE_TYPE (func->decl) = %p, ", TREE_TYPE (func->decl)); + //DEBUG_F( flexible_print, stderr, TREE_TYPE (func->decl), 1, (dump_flags_t)0); + //DEBUG_A("TREE_TYPE(TREE_TYPE (func->decl)) = %p, ", TREE_TYPE(TREE_TYPE (func->decl))); + //DEBUG_F( flexible_print, stderr, TREE_TYPE(TREE_TYPE (func->decl)), 1, (dump_flags_t)0); + + //INDENT(-4); + return true; +} + +// Returns true if a modification occurred +#if 1 +// Dubious version +bool +modify_decl_core ( tree *location, Info *info) +{ + //DEBUG_L("before modify_decl_core: "); + //DEBUG_F( flexible_print, stderr, *location, 1, (dump_flags_t)0); + tree type = *location; + //DEBUG_A("type = "); + //DEBUG_F( flexible_print, stderr, type, 0, (dump_flags_t)0); + tree base = base_type_of ( type); + //DEBUG_A(", base = "); + //DEBUG_F( flexible_print, stderr, base, 1, (dump_flags_t)0); + ReorgType_t *ri = get_reorgtype_info ( base, info); + if ( ri == NULL ) + { + return false; + } + + // array case -- not doing non-dynamically + // allocated arrays yet so this case won't + // currently occur + + // borrowed from wrangle_ssa_type + tree prev_type; + int levels; + for ( levels = 0; TREE_CODE ( type) == POINTER_TYPE; levels++ ) + { + prev_type = type; + type = TREE_TYPE ( prev_type); + //DEBUG_L( "prev_type: %p, type: %p\n", prev_type, type); + } + // TBD might use build_pointer_type to build new type for *(N)reorg_type + // to *(N-1)ri->pointer_rep + // Fakes this for levels == 1 + if ( levels == 0) // How did this test ever work???? It didn't + //if ( levels == 1) + { + //DEBUG_L( "LEVEL ONE\n"); + //modify_ssa_name_type ( side, ri->pointer_rep); + gcc_assert ( TYPE_MAIN_VARIANT ( ri->pointer_rep)); + //TREE_TYPE ( *location) = TYPE_MAIN_VARIANT ( ri->pointer_rep); + TREE_TYPE(*location) = TYPE_MAIN_VARIANT ( ri->pointer_rep); + } + else + { + //DEBUG_L( "LEVEL > ONE\n"); + gcc_assert(0); + } + + if ( DECL_INITIAL ( *location) != NULL ) + { + // Note this assumes the levels code above is not general + DECL_INITIAL ( *location) = TYPE_MAIN_VARIANT ( ri->pointer_rep); + } + + relayout_decl ( *location); + + //DEBUG_L(" after modify_decl_core"); + //DEBUG_F( print_generic_decl, stderr, *location, (dump_flags_t)0); + //DEBUG("\n"); + return true; +} +#else +// Nodubious version +bool +modify_decl_core ( tree *location, Info *info) +{ + //DEBUG_L("before modify_decl_core: "); + //DEBUG_F( flexible_print, stderr, *location, 1, (dump_flags_t)0); + tree type = *location; + //DEBUG_A("type = "); + //DEBUG_F( flexible_print, stderr, type, 0, (dump_flags_t)0); + tree base = base_type_of ( type); + //DEBUG_A(", base = "); + //DEBUG_F( flexible_print, stderr, base, 1, (dump_flags_t)0); + ReorgType_t *ri = get_reorgtype_info ( base, info); + if ( ri == NULL ) + { + return false; + } + + // array case -- not doing non-dynamically + // allocated arrays yet so this case won't + // currently occur + + // borrowed from wrangle_ssa_type + tree prev_type; + int levels; + for ( levels = 0; TREE_CODE ( type) == POINTER_TYPE; levels++ ) + { + prev_type = type; + type = TREE_TYPE ( prev_type); + //DEBUG_L( "prev_type: %p, type: %p\n", prev_type, type); + } + // TBD might use build_pointer_type to build new type for *(N)reorg_type + // to *(N-1)ri->pointer_rep + // Fakes this for levels == 1 + if ( levels == 0) // How did this test ever work???? It didn't + //if ( levels == 1) + { + //DEBUG_L( "LEVEL ONE\n"); + //modify_ssa_name_type ( side, ri->pointer_rep); + gcc_assert ( TYPE_MAIN_VARIANT ( ri->pointer_rep)); + //TREE_TYPE ( *location) = TYPE_MAIN_VARIANT ( ri->pointer_rep); + TREE_TYPE(*location) = TYPE_MAIN_VARIANT ( ri->pointer_rep); + } + else + { + //DEBUG_L( "LEVEL > ONE\n"); + gcc_assert(0); + } + + if ( DECL_INITIAL ( *location) != NULL ) + { + // Note this assumes the levels code above is not general + DECL_INITIAL ( *location) = TYPE_MAIN_VARIANT ( ri->pointer_rep); + } + + relayout_decl ( *location); + + //DEBUG_L(" after modify_decl_core"); + //DEBUG_F( print_generic_decl, stderr, *location, (dump_flags_t)0); + //DEBUG("\n"); + return true; +} +#endif + +void +delete_reorgtype ( ReorgType_t *rt, Info *info ) +{ + DEBUG_L( "delete_reorgtype( %s ):", type_name_to_str( TYPE_NAME( rt->gcc_type))); + if ( !rt->delete_me ) + { + DEBUG( "TO DELETE\n"); + info->num_deleted++; + rt->delete_me = true; + } else { + DEBUG( "SKIP\n"); + } +} + +void +undelete_reorgtype ( ReorgType_t *rt, Info *info ) +{ + //DEBUG_L( "undelete_reorgtype( %s ): ", type_name_to_str( TYPE_NAME( rt->gcc_type))); + if ( rt->delete_me ) + { + //DEBUG( "UNDELETE\n"); + info->num_deleted--; + rt->delete_me = false; + } else { + //DEBUG( "SKIP\n"); + } +} + +ReorgTransformation +reorg_recognize ( gimple *stmt, cgraph_node* node, Info_t *info ) +{ + DEBUG_L ( "ReorgTransformation reorg_recognize for: "); + DEBUG_F ( print_gimple_stmt, stderr, stmt, 0); + INDENT(2); + switch ( gimple_code( stmt) ) + { + case GIMPLE_ASSIGN: + { + DEBUG_L("GIMPLE_ASSIGN:\n"); + tree lhs = gimple_assign_lhs ( stmt); + enum tree_code rhs_code = gimple_assign_rhs_code ( stmt); + + if ( gimple_assign_single_p ( stmt) ) + { + DEBUG_L("gimple_assign_single_p() = true\n"); + INDENT(2); + tree rhs = gimple_assign_rhs1 ( stmt); + enum ReorgOpTrans lhs_op = recognize_op ( lhs, info); + switch ( lhs_op ) + { + case ReorgOpT_Pointer: // "a" + DEBUG_L("case ReorgOpT_Pointer\n"); + INDENT(-4); + switch ( recognize_op ( rhs, info) ) + { + case ReorgOpT_Scalar: + if ( integer_zerop ( rhs) ) + { + return ReorgT_Ptr2Zero; + } + // If we get here this is clearly really odd code + // so we need to bail out. + return Not_Supported; + case ReorgOpT_Temp: // t + return ReorgT_ElemAssign; + case ReorgOpT_Address: // "&x[i]" + return ReorgT_Adr2Ptr; + default: + return Not_Supported; + } + case ReorgOpT_Struct: // "s" + DEBUG_L("case ReorgOpT_Struct\n"); + INDENT(-4); + switch ( recognize_op ( rhs, info) ) + { + case ReorgOpT_Deref: // "*a" + case ReorgOpT_Array: // "x[i]" + // Technically with a struct on both sides + // this could be ignored but it's + // better to do this at transform time. + // With the case commented out test_09_23 + // exposes a bug. + case ReorgOpT_Struct: // "s" + return ReorgT_StrAssign; + default: + return Not_Supported; + } + case ReorgOpT_Deref: // "*a" + DEBUG_L("case ReorgOpT_Deref\n"); + INDENT(-4); + switch ( recognize_op ( rhs, info) ) + { + case ReorgOpT_Deref: // "*a" + case ReorgOpT_Struct: // "s" + case ReorgOpT_Array: // "x[i]" + return ReorgT_StrAssign; + default: + return Not_Supported; + } + case ReorgOpT_Array: // "x[i]" + DEBUG_L("case ReorgOpT_Array\n"); + INDENT(-4); + switch ( recognize_op ( rhs, info) ) + { + case ReorgOpT_Struct: // "s" + case ReorgOpT_Deref: // "*a" + case ReorgOpT_Array: // "x[i]" + return ReorgT_StrAssign; + default: + return Not_Supported; + } + case ReorgOpT_Temp: // t + case ReorgOpT_Scalar: // "z" + DEBUG_L("case ReorgOpT_%s\n", lhs_op == ReorgOpT_Temp ? "Temp" : "Scalar"); + INDENT(-4); + switch ( recognize_op( rhs, info) ) + { + case ReorgOpT_Scalar: // "z" + case ReorgOpT_Temp: // "t" + case ReorgOpT_Indirect: // "a->f" + case ReorgOpT_AryDir: // "x[i].f" + return ReorgT_ElemAssign; + default: + return Not_Supported; + } + case ReorgOpT_Indirect: // "a->f" + case ReorgOpT_AryDir: // "x[i].f" + DEBUG_L("case ReorgOpT_%s\n", lhs_op == ReorgOpT_Indirect ? "Indirect" : "AryDir"); + INDENT(-4); + switch ( recognize_op ( rhs, info) ) + { + case ReorgOpT_Temp: // t + case ReorgOpT_Scalar: // "z" + case ReorgOpT_Indirect: // "a->f" + case ReorgOpT_AryDir: // "x[i].f" + return ReorgT_ElemAssign; + default: + return Not_Supported; + } + default: + INDENT(-4); + return Not_Supported; + } // switch ( recognize_op ( lhs, info) ) + } else { + DEBUG_L("gimple_assign_single_p() = false\n"); + INDENT(2); + tree op1 = gimple_assign_rhs1 ( stmt); + tree op2 = gimple_assign_rhs2 ( stmt); + DEBUG_L("op1 = %p, op2 = %p\n", op1, op2); + DEBUG_A(""); + DEBUG_F( print_generic_expr, stderr, op1, (dump_flags_t)-1); + DEBUG("\n"); + + if ( CONVERT_EXPR_CODE_P ( gimple_assign_rhs_code ( stmt))) + { + DEBUG_L("CONVERT_EXPR_CODE_P (...)\n"); + INDENT(-4); + return ReorgT_Convert; + } + + if ( gimple_assign_rhs3 ( stmt) != NULL ) + { + DEBUG_L("gimple_assign_rhs3 ( stmt) != NULL\n"); + INDENT(-4); + return Not_Supported; + } + + // TBD The parenthesis where a disaster here in the HL Design so + // double check this! + bool zero_case = + ( (POINTER_TYPE_P ( TREE_TYPE( op1)) && integer_zerop ( op2)) + || (POINTER_TYPE_P ( TREE_TYPE( op2)) && integer_zerop ( op1))) + && ( integer_zerop ( op1) || integer_zerop ( op2) ); + DEBUG_L("zero_case = %s\n", zero_case ? "true" : "false" ); + INDENT(-4); + switch ( rhs_code ) + { + case POINTER_PLUS_EXPR: + return ReorgT_PtrPlusInt; + case POINTER_DIFF_EXPR: + return ReorgT_PtrDiff; + case EQ_EXPR: + return zero_case ? ReorgT_PtrNull : ReorgT_PtrEQ; + case NE_EXPR: + return zero_case ? ReorgT_PtrNotNull : ReorgT_PtrNE; + case LE_EXPR: + return ReorgT_PtrLE; + case LT_EXPR: + return ReorgT_PtrLT; + case GE_EXPR: + return ReorgT_PtrGE; + case GT_EXPR: + return ReorgT_PtrGT; + default: + return Not_Supported; + } + } // } else { + } + case GIMPLE_COND: // Similar to assign cases + { + //DEBUG_L("GIMPLE_COND:\n"); + //INDENT(-2); + //tree op1 = gimple_assign_rhs1 ( stmt); + //tree op2 = gimple_assign_rhs2( stmt); + tree op1 = gimple_cond_lhs ( stmt); + tree op2 = gimple_cond_rhs ( stmt); + enum tree_code cond_code = gimple_cond_code (stmt); + // TBD The parenthesis were a disaster here in the HL Design so + // double check this! + bool zero_case = + ( POINTER_TYPE_P ( TREE_TYPE ( op1)) && integer_zerop ( op2)) + || ( POINTER_TYPE_P ( TREE_TYPE ( op2)) && integer_zerop ( op1)); + switch ( cond_code ) + { + case EQ_EXPR: + return zero_case ? ReorgT_If_Null : ReorgT_IfPtrEQ; + case NE_EXPR: + return zero_case ? ReorgT_If_NotNull : ReorgT_IfPtrNE; + case LE_EXPR: + return ReorgT_IfPtrLE; + case LT_EXPR: + return ReorgT_IfPtrLT; + case GE_EXPR: + return ReorgT_IfPtrGE; + case GT_EXPR: + return ReorgT_IfPtrGT; + default: + return Not_Supported; + } + } + case GIMPLE_CALL: + { + DEBUG_L("GIMPLE_CALL:\n"); + struct cgraph_edge *edge = node->get_edge ( stmt); + gcc_assert( edge); + DEBUG_L("called function %s gimple_body\n", + edge->callee->has_gimple_body_p() ? "has a" : "has no"); + INDENT(-2); + if ( gimple_call_builtin_p( stmt, BUILT_IN_CALLOC ) ) return ReorgT_Calloc; + if ( gimple_call_builtin_p( stmt, BUILT_IN_MALLOC ) ) return ReorgT_Malloc; + if ( gimple_call_builtin_p( stmt, BUILT_IN_REALLOC) ) return ReorgT_Realloc; + if ( gimple_call_builtin_p( stmt, BUILT_IN_FREE ) ) return ReorgT_Free; + + // Instead of just returning Not_Supported we need to + // determine if it's a user defined function in which case the + // transformation is meaningless but the type still needs to be + // adjusted (does transform really do this?) + + if ( is_user_function ( stmt, node, info) ) + { + return ReorgT_UserFunc; + } + + return Not_Supported; + } + break; + case GIMPLE_RETURN: + DEBUG_L("GIMPLE_RETURN:\n"); + INDENT(-2); + return ReorgT_Return; + break; + default: + DEBUG_L ( "didn't support: "); + DEBUG_F ( print_gimple_stmt, stderr, stmt, 0); + DEBUG( "\n"); + INDENT(-2); + return Not_Supported; + } +} + +static bool +is_user_function ( gimple *call_stmt, cgraph_node* node, Info *info) +{ + // I'm not 100% sure this is a great idea but it means that + // if we know nothing about the contents of a function + // then it shouldn't be considered a user written function + // that is part of our program. + struct cgraph_edge *ce; + ce = node->get_edge ( call_stmt); + return ce->callee->has_gimple_body_p(); +} + +void +clear_deleted_types( Info *info) +{ + info->saved_reorg_type->clear (); +} + +void +restore_deleted_types ( Info *info) +{ + while ( !info->saved_reorg_type->empty () ) + { + info->reorg_type->push_back ( info->saved_reorg_type->back () ); + info->saved_reorg_type->pop_back (); + } +} + +// This routine disqualifies any and all reorg +// types in it that deserve disqualification. +// There are bizarre circumstances that could disqualify +// many types in a single statement. +void +reorg_forbidden ( gimple *stmt, Info *info ) +{ + // The recognition of forbidden patterns must include casting a + // pointer (i.e. a pointer into a Reorg field) to something of a + // larger size (e.g. ints to longs) or doing arithmetic with them + // (e.g. bar->foo + k) since the later assumes the structure + // layout has not changed. +#if 0 + // TBD + switch( gimple_code( stmt) ) { + case : + } +#endif +} + +void +remove_deleted_types ( Info *info, ReorgFn reorg_fn) +{ + //DEBUG_L( "remove_deleted_types: %d to delete of %d types\n", info->num_deleted, info->reorg_type->size ()); + if ( info->show_delete ) + { + fprintf ( info->reorg_dump_file, "DELETING REORG TYPES:\n"); + } + + if ( info->num_deleted > 0 ) + { + // Do delete here and run reorg_fn on each + // deleted type + int n = info->reorg_type->size (); + int to = 0; + //INDENT(2); + for ( int from = 0; from < n; from++ ) + { + //DEBUG_L( "%s ", type_name_to_str( TYPE_NAME( (*(info->reorg_type))[from].gcc_type))); + //DEBUG( "< from %d, to %d > - ", from, to); + + if ( !(*(info->reorg_type))[from].delete_me ) + { + //DEBUG( "NOT DELETED %d\n", from); + + // Save copy of removed entry + (*(info->reorg_type))[from].delete_me = false; + info->saved_reorg_type->push_back ( (*(info->reorg_type))[from]); + + // Delete by not copying deleted elements + if ( from != to ) + { + if ( reorg_fn != NULL ) + { + (*reorg_fn)( info, &(*(info->reorg_type))[ from]); + } + (*(info->reorg_type))[ to] = (*(info->reorg_type))[ from]; + //DEBUG_A( " move from -> to\n"); + } + + to++; + } else { + //DEBUG( "DELETE %d\n", from); + } + } + //INDENT(-2); + info->reorg_type->resize ( n - info->num_deleted); + info->num_deleted = 0; + } +} + +enum ReorgOpTrans +recognize_op ( tree op, Info *info) +{ + DEBUG_L("recognize_op: "); + DEBUG_F( flexible_print, stderr, op, 1, TDF_DETAILS); + enum tree_code op_code = TREE_CODE ( op); + DEBUG_A("opcode = %s\n", code_str( op_code)); + if ( op_code == SSA_NAME ) + { + // We tried returning ReorgOpT_Scalar. + // It caused an assertion failue because + // it was incorrectly triggering the ReorgT_Ptr2Zero + // case with a bogus RHS. + DEBUG_L(" returns: ReorgOpT_Temp\n"); + return ReorgOpT_Temp; + } + tree type = TREE_TYPE ( op); + if ( type != NULL && POINTER_TYPE_P (type) ) + { + DEBUG_L("POINTER_TYPE_P (type) = true\n"); + if ( is_reorg_type ( type, info) ) + { + DEBUG_L(" returns: ReorgOpT_Pointer\n"); + return ReorgOpT_Pointer; + } else { + // This would be for when + // the field of a struct element + // is a pointer that's not a reorg + // point. I.e. ReorgT_ElemAssign. + DEBUG_L(" returns: ReorgOpT_Scalar\n"); + return ReorgOpT_Scalar; + } + } + // This might not occur in practice + if ( op_code == RECORD_TYPE ) + { + // The assumption here is that this + // is a reorg type. + DEBUG_L(" returns: ReorgOpT_Struct\n"); + return ReorgOpT_Struct; + } + if ( op_code == VAR_DECL ) + { + tree type = TREE_TYPE ( op); + DEBUG_L(" recursing on type :"); + DEBUG_F( flexible_print, stderr, type, 1, TDF_DETAILS); + return recognize_op ( type, info); + } + tree inner_op = TREE_OPERAND( op, 0); + tree inner_type = TREE_TYPE ( inner_op); + enum tree_code inner_op_code = TREE_CODE ( inner_op); + //DEBUG_L("inner_op = "); + //DEBUG_F( print_generic_expr, stderr, inner_op, TDF_DETAILS); + //DEBUG(", TREE_CODE = %s\n", code_str( TREE_CODE(inner_op))); + if ( op_code == ADDR_EXPR ) + { + //DEBUG_L("op_code == ADDR_EXPR\n"); + if ( inner_op_code == ARRAY_REF + && is_reorg_type ( inner_op, info) ) + { + //DEBUG_L(" returns: ReorgOpT_Address\n"); + return ReorgOpT_Address; + } + } + if ( op_code == COMPONENT_REF ) + { + //DEBUG_L("op_code == COMPONENT_REF\n"); + if ( inner_op_code == INDIRECT_REF ) + { + //DEBUG_L("TREE_CODE( inner_op) == INDIRECT_REF\n"); + if ( is_reorg_type ( base_type_of ( type), info) ) // inner_type??? + { + //DEBUG_L(" returns: ReorgOpT_Indirect\n"); + return ReorgOpT_Indirect; + } + // Just normal field reference otherwise... + //DEBUG_L(" returns: ReorgOpT_Scalar\n"); + return ReorgOpT_Scalar; + } + if ( inner_op_code == MEM_REF ) { + //DEBUG_L("TREE_CODE( inner_op) == MEM_REF\n"); + if ( is_reorg_type ( base_type_of ( inner_type), info) ) + { + //DEBUG_L(" returns: ReorgOpT_Indirect\n"); + return ReorgOpT_Indirect; + } + // Just normal field reference otherwise... + //DEBUG_L(" returns: ReorgOpT_Scalar\n"); + return ReorgOpT_Scalar; + } + DEBUG_L("TREE_CODE( inner_op) not INDIRECT_REF or MEM_REF\n"); + // Note, doesn't this ignore ARRAY_REF of this? + // I think it's OK at least until we start supporting + // multi-pools. + if ( is_reorg_type ( base_type_of ( inner_type), info) ) + { + //DEBUG_L(" returns: ReorgOpT_AryDir\n"); + return ReorgOpT_AryDir; + } + // Just normal field reference otherwise... + //DEBUG_L(" returns: ReorgOpT_Scalar\n"); + return ReorgOpT_Scalar; + } + if ( op_code == ARRAY_REF ) + { + //DEBUG_L("op_code == ARRAY_REF\n"); + if ( is_reorg_type( base_type_of ( type), info) ) + { + //DEBUG_L(" returns: ReorgOpT_Array\n"); + return ReorgOpT_Array; + } + //DEBUG_L(" returns: ReorgOpT_Scalar\n"); + return ReorgOpT_Scalar; + } + if( op_code == INDIRECT_REF ) + { + //DEBUG_L("op_code == INDIRECT_REF\n"); + // Do we want to chase the base type? + // No, we care about (and transform) just + // *r and not **...r (where r is a ReorgType.) + if( is_reorg_type ( type, info) ) + { + //DEBUG_L(" returns: ReorgOpT_Deref\n"); + return ReorgOpT_Deref; + } + //DEBUG_L(" returns: ReorgOpT_Scalar\n"); + return ReorgOpT_Scalar; + } + //DEBUG_L(" returns: ReorgOpT_Scalar\n"); + return ReorgOpT_Scalar; +} + +bool +is_reorg_type( tree rt, Info *info ) +{ + return get_reorgtype_info ( rt, info) != NULL; +} + +tree +base_type_of ( tree type) +{ + //DEBUG_L("base_type_of: "); + //DEBUG_F( print_generic_expr, stderr, type, TDF_DETAILS); + //DEBUG("\n"); + for ( ; POINTER_TYPE_P ( type) || + TREE_CODE ( type) == ARRAY_TYPE || + TREE_CODE ( type) == VAR_DECL || + TREE_CODE ( type) == PARM_DECL + ; type = TREE_TYPE ( type) ); + return type; +} + +tree +base_type_with_levels ( tree type, int *levels) +{ + //DEBUG_L("base_type_of: "); + //DEBUG_F( print_generic_expr, stderr, type, TDF_DETAILS); + //DEBUG("\n"); + int lev = 0; + bool indir; + for ( ; (indir = POINTER_TYPE_P ( type)) || + TREE_CODE ( type) == ARRAY_TYPE || + TREE_CODE ( type) == VAR_DECL || + TREE_CODE ( type) == PARM_DECL + ; type = TREE_TYPE ( type) ) + { + if ( indir ) lev++; + } + *levels = lev; + return type; +} + +// There are other allocations such as alloca or +// aligned allocs that I'm pretty sure are not +// a good fit for structure reorg optimization. +// Also, we do handle realloc but it doesn't +// create a new pool of memory so we ignore it here. +static bool +is_reorg_alloc_trigger ( gimple *stmt) +{ + return gimple_call_builtin_p ( stmt, BUILT_IN_MALLOC) + || gimple_call_builtin_p ( stmt, BUILT_IN_CALLOC); +} + +static ReorgType_t * +find_struct_type_ptr_to_struct ( tree type, Info *info) +{ + //DEBUG_L( "find_struct_type_ptr_to_struct: "); + if ( !POINTER_TYPE_P ( type) ) { + //DEBUG(" bail\n"); + + return NULL; + } + for ( ; POINTER_TYPE_P ( type); type = TREE_TYPE ( type) ); + + if ( TREE_CODE ( type) == RECORD_TYPE ) { + //DEBUG( " look for info\n"); + + return get_reorgtype_info ( type, info); + } + //DEBUG(" fell through\n"); + + return NULL; +} + +// The applied function func can be used to search because it forces +// a return if it returns true; +void +apply_to_all_gimple ( bool (*function)(gimple *, void *), bool phis_too, void *data ) +{ + struct cgraph_node *node; + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) + { + struct function *func = DECL_STRUCT_FUNCTION ( node->decl); + push_cfun ( func); + basic_block bb; + FOR_EACH_BB_FN ( bb, func) + { + if ( phis_too ) + { + gimple_seq seq = bb->il.gimple.phi_nodes; + if ( seq ) + { + gimple_stmt_iterator phii; + for ( phii = gsi_start (seq); !gsi_end_p (phii); gsi_next (&phii)) + { + gimple *phi_stmt = gsi_stmt ( phii); + if ( (*function) ( phi_stmt, data )) return; + } + } + } + gimple_stmt_iterator gsi; + for ( gsi = gsi_start_bb ( bb); !gsi_end_p ( gsi); gsi_next ( &gsi) ) + { + gimple *stmt = gsi_stmt ( gsi); + // If we are searching for something then return here because + // it's found. + if ( (*function) ( stmt, data )) return; + } + } + pop_cfun (); + } +} + +// TBD Garbage just so it will compile +// What's dicey about this is it may sort of work but then I +// can see places where it wouldn't... The language has a say +// in what types are equal so maybe language hooks are involved??? +bool same_type_p( tree a, tree b ) +{ + //DEBUG( "same_type_p:\n"); + //DEBUG( " a: TREE_CODE = %s, name = %p\n ",code_str(TREE_CODE(a)),TYPE_NAME(a)); + //DEBUG_F( print_generic_expr, stderr, a, (dump_flags_t)-1); + //DEBUG( "\n b TREE_CODE = %s, name = %p\n ",code_str(TREE_CODE(b)),TYPE_NAME(b)); + //DEBUG_F( print_generic_expr, stderr, b, (dump_flags_t)-1); + //DEBUG( "\n"); + + // This replaces part of the below + bool a_rec = TREE_CODE ( a ) == RECORD_TYPE; + bool b_rec = TREE_CODE ( b ) == RECORD_TYPE; + if ( !(a_rec && a_rec) ) return false; + + // This is too strict + //gcc_assert ( TREE_CODE ( a ) == RECORD_TYPE && TYPE_NAME ( a) != 0); + //gcc_assert ( TREE_CODE ( b ) == RECORD_TYPE && TYPE_NAME ( b) != 0); + + // We could barf here iff the type names of records are missing. + // But we'll do something dubious instead since that seems to not work. + // Isn't that grand! + if ( TYPE_NAME ( a) == 0 || TYPE_NAME ( b) == 0 ) + { + return a == b; + } + + bool ret = TYPE_NAME ( a) == TYPE_NAME ( b); + + //DEBUG( "returns %s\n", ret ? "true" : "false"); + + return ret; +} + +// May need to add secondary map container to +// look them up or even modify the container +// type of ReorgType +ReorgType_t * +get_reorgtype_info ( tree type, Info* info) +{ + DEBUG_L( "get_reorgtype_info\n"); + + // Note, I'm going to use the most stupid and slowest possible way + // to do this. The advanage is it will be super easy and almost + // certainly correct. It will also almost certainly need to be + // improved but I get something out there now. + for ( std::vector<ReorgType_t>::iterator ri = info->reorg_type->begin (); + ri != info->reorg_type->end (); + ri++ ) + { + // TBD the internal docs lie and same_type_p doesn't exist + // (at least it's not available here at LTO time) + // so this is just a place holder until I can get an answer + // from the gcc community. Note, this is a big issue. + // Remember, the same_type_p here is my own temporary hack. + DEBUG_L(""); + DEBUG_F( print_generic_expr, stderr, type, TDF_DETAILS); + DEBUG("\n"); + if ( same_type_p ( ri->gcc_type, type) ) + { + DEBUG_A( " returns %p\n", &(*ri)); + + return &(*ri); + } + } + DEBUG_A( " returns NULL\n"); + return NULL; +} + +// These are only used by the following to routines to pass +// information through a walking function +typedef struct hidden_info hidden_info_t; +struct hidden_info { + ReorgType_t *found_reorg; + Info *info; +}; + +static tree +detect_reorg ( tree *tp, int *dummy, void *data) +{ + struct walk_stmt_info *walk_data = ( struct walk_stmt_info *)data; + hidden_info_t *hi = ( hidden_info_t *)walk_data->info; + DEBUG_L( "*tp = "); + DEBUG_F( print_generic_expr, stderr, *tp, (dump_flags_t)-1); + DEBUG("\n"); + tree operand = base_type_of ( TREE_TYPE ( *tp)); + ReorgType_t *ri = get_reorgtype_info ( operand, hi->info); + if ( ri != NULL ) + { + hi->found_reorg = ri; + } + + return NULL_TREE; +} + +ReorgType_t * +contains_a_reorgtype ( gimple *stmt, Info *info) +{ + DEBUG_L ( "contains_a_reorgtype: "); + DEBUG_F ( print_gimple_stmt, stderr, stmt, 0); + INDENT(2); + + if ( gimple_code ( stmt) == GIMPLE_PHI ) + { + INDENT(-2); + tree base = base_type_of ( TREE_TYPE ( PHI_RESULT ( stmt))); + return get_reorgtype_info ( base, info); + } + else + { + // Note walk_stmt_info is compilcated, use it's info + // field for hidden_info + hidden_info_t hi = { NULL, info }; + struct walk_stmt_info walk_info; // expt + memset ( &walk_info, 0, sizeof ( walk_info)); + walk_info.info = ( void*)&hi; //expt + walk_gimple_op ( stmt, + detect_reorg, + &walk_info); + INDENT(-2); + return hi.found_reorg; + } +} + +static tree +detect_reorg_in_expr ( tree *tp, int *w_s, void *data) +{ + //DEBUG_L("*tp = "); + //DEBUG_F( flexible_print, stderr, *tp, 1, (dump_flags_t)0); + hidden_info_t *tre_hi = ( hidden_info_t *)data; + //DEBUG_L("TREE_TYPE ( *tp) = "); + //DEBUG_F( flexible_print, stderr, TREE_TYPE ( *tp), 1, (dump_flags_t)0); + tree operand = base_type_of ( TREE_TYPE ( *tp)); + //DEBUG_L("operand = %p, ", operand); + DEBUG_F( flexible_print, stderr, operand, 1, (dump_flags_t)0); + ReorgType_t *ri = get_reorgtype_info ( operand, tre_hi->info); + if ( ri != NULL ) + { + // If we found a reorg type save it and + // return with a non null value to signify + // to trip the return from the tree walk + tre_hi->found_reorg = ri; + return *tp; + } + + return NULL_TREE; +} + +bool +tree_contains_a_reorgtype_p ( tree expr, Info *info) +{ + hidden_info_t tre_hi = { NULL, info }; + // The stuff comment out was for gimple walks. Yikes! + //struct walk_stmt_info walk_info; + //memset ( &walk_info, 0, sizeof ( walk_info)); + //walk_info.info = ( void*)&tre_hi; + walk_tree_1 ( &expr, detect_reorg_in_expr, (void *)&tre_hi, NULL, NULL); + + return tre_hi.found_reorg != NULL; +} + +ReorgType_t * +tree_contains_a_reorgtype ( tree expr, Info *info) +{ + hidden_info_t tre_hi = { NULL, info }; + // The stuff comment out was for gimple walks. Yikes! + //struct walk_stmt_info walk_info; + //memset ( &walk_info, 0, sizeof ( walk_info)); + //walk_info.info = ( void*)&tre_hi; + walk_tree_1 ( &expr, detect_reorg_in_expr, (void *)&tre_hi, NULL, NULL); + + return tre_hi.found_reorg; +} + +void +print_reorg_with_msg ( FILE *file, + ReorgType_t *reorg, + int leading_space, + const char *msg ) +{ + fprintf ( file, "%*s%s:\n", leading_space, "", msg); + print_reorg ( file, leading_space + 2, reorg); +} + +static void +dump_reorg ( ReorgType_t *reorg) +{ + print_reorg ( stderr, 0, reorg); +} + +void +print_base_reorg ( FILE *file, int leading_space, ReorgType_t *reorg, bool detailed ) +{ + // TBD + // note if reorg_perf & regular_perf are nonzero + // synthesize and display absolute_effect & raw_effect + + // Note, the following is a stub. + const char *text + = identifier_to_locale ( IDENTIFIER_POINTER ( TYPE_NAME ( reorg->gcc_type))); + fprintf ( file, "%*s{ type:%s, #%d, ", leading_space, "",text, reorg->id); + + if( reorg->do_dead_field_elim ) { + fprintf ( file, "elim:{ "); + // TBD + fprintf ( file, "}, "); + } + + if( reorg->do_field_reorder ) { + fprintf ( file, "reorder:{ "); + // TBD + fprintf ( file, "}, "); + } + + if( reorg->do_instance_interleave ) { + fprintf ( file, "inter:{ "); + // TBD + fprintf ( file, "%s, ", + reorg->instance_interleave.multi_pool ? "multi" : "single" ); + // TBD When multi-pool implemented (and found) emit pointer_rep. + fprintf ( file, "}, "); + } + if ( reorg->reorg_ver_type != NULL ) + { + // TBD does this belong here? How will the clone be done with elim and + // reorder + const char *clone_name = + identifier_to_locale ( IDENTIFIER_POINTER ( TYPE_NAME ( reorg->reorg_ver_type))); + fprintf ( file, "%s%s", clone_name, reorg->pointer_rep ? ", " : ""); + } + if ( reorg->pointer_rep != NULL ) + { + // TBD does this belong here? How will the clone be done with elim and + // reorder + const char *pointer_name = + identifier_to_locale ( IDENTIFIER_POINTER ( TYPE_NAME ( reorg->pointer_rep))); + fprintf ( file, "%s", pointer_name); + } + + fprintf ( file, "}\n"); + if ( detailed ) + { + tree field; + for ( field = TYPE_FIELDS( reorg->reorg_ver_type); + field; + field = DECL_CHAIN( field)) + { + fprintf ( file, "%*s", leading_space + 4, ""); + print_generic_expr ( file, field, (dump_flags_t)0); + fprintf ( stderr, ": "); + print_generic_expr ( file, TREE_TYPE ( field), (dump_flags_t)0); + fprintf ( file, "\n"); + } + } +} + +static void +print_base_reorgs ( FILE *file, int leading_space, Info *info, bool detailed) +{ + for ( int i = 0; i < info->reorg_type->size (); i++ ) { + print_base_reorg ( file, leading_space, &(*(info->reorg_type))[i], detailed); + } +} + + +static void +print_detailed_reorgs (FILE *file, int leading_space, Info *info) +{ + print_base_reorgs ( file, leading_space, info, true); +} + +static void +print_reorgs ( FILE *file, int leading_space, Info *info) +{ + print_base_reorgs ( file, leading_space, info, false); +} + +void +print_reorg ( FILE *file, int leading_space, ReorgType_t *reorg ) +{ + print_base_reorg ( file, leading_space, reorg, false); +} + +static void +print_progdecls ( FILE *file, int leading_space, Info * info) +{ + for ( int i = 0; i < info->prog_decl->size (); i++ ) { + print_progdecl ( file, leading_space, &(*(info->prog_decl))[i]); + } +} + +static void +print_progdecl ( FILE *file, int leading_space, ProgDecl_t *progdecl ) +{ + //INDENT(leading_space); + //DEBUG_L( "print_progdecl check TREE_CODE = %s\n", code_str( TREE_CODE( progdecl->gcc_decl))); + //INDENT(-leading_space); + //DEBUG_A(""); + fprintf ( file, "%*s", leading_space, ""); + //print_generic_decl ( file, progdecl->gcc_decl, (dump_flags_t)-1); + print_generic_decl ( file, progdecl->gcc_decl, TDF_DETAILS); + fprintf ( file, "\n"); +} + +void +print_program ( FILE *file, bool my_format, int leading_space, Info_t *info) +{ + struct cgraph_node *node; + fprintf ( file, "%*sProgram:\n", leading_space, ""); + + // Print Global Decls + // + varpool_node *var; + FOR_EACH_VARIABLE ( var) + { + tree decl = var->decl; + fprintf ( file, "%*s", leading_space, ""); + print_generic_decl ( file, decl, (dump_flags_t)0); + fprintf ( file, "\n"); + } + + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) + { + struct function *func = DECL_STRUCT_FUNCTION ( node->decl); + if ( my_format ) + { + flexible_print ( file, TREE_TYPE( func->decl), 1, (dump_flags_t)0); + print_function ( file, leading_space + 4, func); + } + else + { + flexible_print ( file, TREE_TYPE( func->decl), 1, (dump_flags_t)0); + dump_function_header ( file, func->decl, (dump_flags_t)0); + dump_function_to_file ( func->decl, file, (dump_flags_t)0); + } + } + + //DEBUG ("INTERNALS PRINT\n"); + //DEBUG_F (apply_to_all_gimple, print_internals, true, (void*)info); +} + +static void +print_function ( FILE *file, int leading_space, struct function *func) +{ + basic_block bb; + + fprintf ( file, "%*sFunc: ", leading_space + 2, ""); + // Print Func Type + print_generic_expr ( file, TREE_TYPE(TREE_TYPE( func->decl)), (dump_flags_t)0); + + // Print Func Name + fprintf ( file, " %s ( ", lang_hooks.decl_printable_name ( func->decl, 2)); + + // Print Parameter Decls + tree parm; + for ( parm = DECL_ARGUMENTS ( func->decl); + parm; + parm = DECL_CHAIN ( parm) ) + { + print_generic_expr ( file, TREE_TYPE( parm), (dump_flags_t)0); + fprintf ( file, " "); + print_generic_expr ( file, parm, (dump_flags_t)0); + fprintf ( file, "; "); + } + fprintf ( file, ")\n"); + + // Print Local Decls + tree decl; + unsigned i; + FOR_EACH_LOCAL_DECL ( func, i, decl) + { + fprintf ( file, "%*s", leading_space + 6, ""); + print_generic_expr ( file, TREE_TYPE( decl), (dump_flags_t)0); + fprintf ( file, " "); + print_generic_expr ( file, decl, (dump_flags_t)0); + fprintf ( file, ";\n"); + } + + FOR_EACH_BB_FN ( bb, func) + { + // print bb num + fprintf ( file, "%*sBB %d:", leading_space + 4, "", bb->index ); + + // Tried to use function gimple_dump_bb is here instead of + // the following loop but it's worthless. + + edge e; + edge_iterator ei; + + FOR_EACH_EDGE ( e, ei, bb->succs ) + { + basic_block succ_bb = e->dest; + fprintf ( file, ", BB%d", succ_bb->index); + if ( e->flags & EDGE_TRUE_VALUE ) fprintf ( file, " true"); + if ( e->flags & EDGE_FALSE_VALUE ) fprintf ( file, " false"); + if ( e->flags & EDGE_FALLTHRU ) fprintf ( file, " fallthru"); + } + fprintf ( file, "\n"); + + gimple_seq seq = bb->il.gimple.phi_nodes; + if ( seq ) + { + gimple_stmt_iterator phii; + for ( phii = gsi_start (seq); !gsi_end_p (phii); gsi_next (&phii)) + { + fprintf ( file, "%*s", leading_space + 6, "" ); + print_gimple_stmt ( file, gsi_stmt ( phii), 0, TDF_DETAILS); + } + } + + gimple_stmt_iterator gsi; + for ( gsi = gsi_start_bb ( bb); + !gsi_end_p ( gsi); + gsi_next ( &gsi) ) + { + gimple *stmt = gsi_stmt ( gsi); + fprintf ( file, "%*s", leading_space + 6, "" ); + // Issue: for "if" this does not print the gotos. + print_gimple_stmt ( file, stmt, 0, TDF_DETAILS); + } + } +} + +ReorgType_t * +get_reorgtype( gimple *stmt, Info *info, int i) +{ + // Looking at operands of statement, when we get to + // the ith one, return it. + int num_reorgs = 0; + unsigned num_ops = gimple_num_ops ( stmt); + unsigned j; + for ( j = 0; j < num_ops; j++ ) + { + tree op = gimple_op ( stmt, j); + if ( tree_contains_a_reorgtype_p ( op, info) ) { + num_reorgs++; + if ( num_reorgs == i ) + { + return get_reorgtype_info ( op, info); + } + } + } + gcc_assert ( 0); +} + +int +num_reorgtypes( gimple *stmt, Info *info) +{ + //DEBUG_L("num_reorgtypes: "); + //DEBUG_F ( print_gimple_stmt, stderr, stmt, 0); + // Looking at operands of statement, count + // the number that have reorg types. + // Note, they may be (most likely are) the same as other + // reorg types in the statement. + int num_reorgs = 0; + unsigned num_ops = gimple_num_ops ( stmt); + unsigned i; + for ( i = 0; i < num_ops; i++ ) + { + tree op = gimple_op ( stmt, i); + //DEBUG_A("op%d: ",i); + //DEBUG_F( print_generic_expr, stderr, op, (dump_flags_t)-1); + if ( tree_contains_a_reorgtype_p ( op, info) ) + { + num_reorgs++; + //DEBUG(" reorg"); + } + //DEBUG( "\n"); + } + return num_reorgs; +} + +void +print_type ( FILE *file, tree type) +{ + const char *text + = identifier_to_locale ( IDENTIFIER_POINTER ( TYPE_NAME ( type))); + fprintf ( file, "type: %s\n", text); +} + +bool +uses_field_of_reorgtypes( gimple *stmt, Info * info) +{ + // TBD + // This is a bit more sophisticated than needed + // for "hello world" so I'll defer this for a bit. + return false; +} + +void +modify_ssa_name_type ( tree ssa_name, tree type) +{ + // This rips off the code in make_ssa_name_fn. + // Note, this probabily be made into special function + // that is part of tree-ssanames. + //DEBUG_L("modify_ssa_name_type ssa_name "); + //DEBUG_F(print_generic_expr, stderr, ssa_name, (dump_flags_t)0); + //DEBUG("\n"); + //gcc_assert ( TREE_TYPE ( type)); + //DEBUG_A("type: "); + //DEBUG_F(print_generic_expr, stderr, type, (dump_flags_t)0); + //DEBUG("\n"); + //DEBUG_A("TREE_TYPE(type): "); + //DEBUG_F(print_generic_expr, stderr, TREE_TYPE(type), (dump_flags_t)0); + //DEBUG("\n"); + //DEBUG_A("TYPE_MAIN_VARIANT(type) :"); + //DEBUG_F(print_generic_expr, stderr, TYPE_MAIN_VARIANT(type), (dump_flags_t)0);// + //DEBUG("\n"); + //DEBUG_A("ssa_defined_default_def_p(ssa_name) %s\n", + // ssa_defined_default_def_p(ssa_name) ? "true" : "false"); + + if ( TYPE_P ( type) ) + { + //DEBUG_L("TYPE_P true\n"); + gcc_assert ( TYPE_MAIN_VARIANT ( type)); + TREE_TYPE ( ssa_name) = TYPE_MAIN_VARIANT ( type); + if ( SSA_NAME_IS_DEFAULT_DEF ( ssa_name) ) + { + // Again this is what breaks (pretty-print) something + // about expecting an integer_type. + // Our _reorg_SP_ptr_type_type_t really is an integer + // type but it doesn't know it. + SET_SSA_NAME_VAR_OR_IDENTIFIER ( ssa_name, TYPE_MAIN_VARIANT ( type)); + } + else + { + // The following breaks defaults defs hence the check above. + SET_SSA_NAME_VAR_OR_IDENTIFIER ( ssa_name, NULL_TREE); + } + } + else + { + //DEBUG_L("TYPE_P false\n"); + gcc_assert ( TREE_TYPE ( type)); + TREE_TYPE ( ssa_name) = TREE_TYPE ( type); + SET_SSA_NAME_VAR_OR_IDENTIFIER ( ssa_name, type); + } +} + +//-- debugging only -- +//static const char * +#if DEBUGGING +const char * +code_str( enum tree_code tc) +{ + switch ( tc ) + { + case POINTER_TYPE: + return "POINTER_TYPE"; + case RECORD_TYPE: + return "RECORD_TYPE"; + case UNION_TYPE: + return "UNION_TYPE"; + case ARRAY_TYPE: + return "ARRAY_TYPE"; + case REFERENCE_TYPE: + return "REFERENCE_TYPE"; + case VOID_TYPE: + return "VOID_TYPE"; + case VAR_DECL: + return "VAR_DECL"; + case TYPE_DECL: + return "TYPE_DECL"; + case CONST_DECL: + return "CONST_DECL"; + case PARM_DECL: + return "PARM_DECL"; + case FIELD_DECL: + return "FIELD_DECL"; + case FUNCTION_DECL: + return "FUNCTION_DECL"; + case RESULT_DECL: + return "RESULT_DECL"; + default: + return get_tree_code_name ( tc); + switch( TREE_CODE_CLASS( tc) ) + { + case tcc_type: + return "class type"; + case tcc_declaration: + return "class declaration"; + case tcc_reference: + return "class reference"; + default: + return "unknown class"; + } + } +} +#endif + +const char * +type_name_to_str ( tree tn) +{ + gcc_assert ( tn != NULL ); + gcc_assert ( IDENTIFIER_POINTER ( tn) != NULL ); + return identifier_to_locale ( IDENTIFIER_POINTER ( tn)); +} + +#if DEBUGGING +void +handle_debug_indenting ( int amount ) +{ + debug_indenting += amount; + debug_indenting = MAX ( debug_indenting, 0); +} +#endif + +#if DEBUGGING +#if 0 +// A Wolf Fence is whatever it needs to be whenever it needs to be it. +int +wf_func ( tree *slot, tree *dummy) +{ + tree t_val = *slot; + gcc_assert( t_val->ssa_name.var); + return 0; +} + +void +wolf_fence ( + Info *info // Pass level gobal info (might not use it) + ) +{ + struct cgraph_node *node; + + //fprintf( stderr, + // "Wolf Fence: Find wolf via gcc_assert(t_val->ssa_name.var)\n"); + + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) + { + struct function *func = DECL_STRUCT_FUNCTION ( node->decl); + push_cfun ( func); + + DEFAULT_DEFS ( func)->traverse_noresize < tree *, wf_func> ( NULL); + + pop_cfun (); + } + fprintf( stderr, "No Wolf\n"); +} +#endif + +#if 0 +void +wolf_fence ( + Info *info // Pass level gobal info (might not use it) + ) +{ + struct cgraph_node *node; + + //fprintf( stderr, + // "Wolf Fence: Find wolf via gcc_assert(t_val->ssa_name.var)\n"); + + fprintf( stderr, "Wolf?\n"); + + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) + { + struct function *func = DECL_STRUCT_FUNCTION ( node->decl); + push_cfun ( func); + + unsigned int len = SSANAMES ( func)->length (); + for ( unsigned int i = 0; i < len; i++) + { + tree ssa_name = (*SSANAMES ( func))[i]; + if ( ssa_name == NULL ) continue; + if ( SSA_NAME_IS_DEFAULT_DEF ( ssa_name) ) + { + gimple *def_stmt = SSA_NAME_DEF_STMT ( ssa_name); + if ( !gimple_nop_p ( def_stmt) ) + { + fprintf ( stderr, "Wolf! : "); + print_gimple_stmt ( stderr, def_stmt, 0); + gcc_assert (0); + } + } + } + pop_cfun (); + } + fprintf( stderr, "No Wolf\n"); +} +#endif + +void +wolf_fence ( + Info *info // Pass level gobal info (might not use it) + ) +{ + if ( ssa_check ( stderr, Show_failures, Fail_1st_bad, false, false) ) + { + fprintf ( stderr, "Wolf!\n"); + gcc_assert (0); + } + + fprintf( stderr, "No Wolf\n"); +} + +// returns true for failure +bool +ssa_check ( FILE *file, Display display, Failure failure, bool types, bool header ) +{ + if ( header ) fprintf ( file, "ssa_check:\n"); + struct cgraph_node *node; + bool has_a_failure = false; + FOR_EACH_FUNCTION_WITH_GIMPLE_BODY ( node) + { + struct function *func = DECL_STRUCT_FUNCTION ( node->decl); + push_cfun ( func); + + unsigned int len = SSANAMES ( func)->length (); + for ( unsigned int i = 0; i < len; i++) + { + tree ssa_name = (*SSANAMES ( func))[i]; + if ( ssa_name == NULL ) continue; + bool a_default_def = SSA_NAME_IS_DEFAULT_DEF ( ssa_name); + gimple *defining_stmt = SSA_NAME_DEF_STMT ( ssa_name);; + bool no_defining_stmt = defining_stmt == NULL; + bool defined_by_nop = defining_stmt && gimple_code ( defining_stmt) == GIMPLE_NOP; + bool has_type = TREE_TYPE ( ssa_name) != NULL; + tree type = TREE_TYPE ( ssa_name); + tree bottom_type = base_type_of ( type); + bool fails = !has_type || + no_defining_stmt || + (a_default_def && !defined_by_nop) || + (!a_default_def && defined_by_nop); + if (fails) has_a_failure = true; + if ( display == Show_everything || (fails && display == Show_failures) ) + { + fprintf ( file, "ssa_name = "); + print_generic_expr ( file, ssa_name, (dump_flags_t)0); + fprintf ( file, "%s", has_type ? ", has no type" : ""); + fprintf ( file, "%s", a_default_def ? ", is default_def" : ""); + fprintf ( file, "%s", no_defining_stmt ? ", has no defining stmt" : ""); + fprintf ( file, "%s", defined_by_nop ? ", defined by a nop" : ""); + if (types) + { + fprintf ( file, ", type = "); + print_generic_expr ( file, type, (dump_flags_t)0); + fprintf ( file, ", bottom_type = "); + print_generic_expr ( file, bottom_type, (dump_flags_t)0); + } + fprintf ( file, "\n"); + } + if ( has_a_failure && Fail_1st_bad == failure ) break; + } + pop_cfun (); + if ( has_a_failure && Fail_1st_bad == failure ) break; + } + return failure != Do_not_fail && has_a_failure; +} + +#endif +void +flexible_print( FILE *f, tree t, int nl, dump_flags_t d) +{ + if ( DECL_P( t) ) + { + print_generic_decl(f,t,d); + } + else + { + print_generic_expr(f,t,d); + } + if ( nl ) fprintf ( f, "\n"); +} + +//---------------- Pass Control Follows ---------------- + +const pass_data pass_data_ipa_structure_reorg = +{ + IPA_PASS, /* type */ + "structure-reorg", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_IPA_STRUCTURE_REORG, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + //( TODO_dump_symtab | TODO_remove_functions ), /* todo_flags_finish */ // ??? + 0, /* todo_flags_finish */ // ??? +}; + +class pass_ipa_structure_reorg : public simple_ipa_opt_pass +{ +public: + pass_ipa_structure_reorg ( gcc::context *ctxt) + : simple_ipa_opt_pass ( pass_data_ipa_structure_reorg, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate ( function *) + { + return ( ( flag_ipa_structure_reorg || + flag_ipa_instance_interleave || + flag_ipa_field_reorder || + flag_ipa_dead_field_eliminate ) + && in_lto_p ); + } + + virtual unsigned int execute ( function *) { return ipa_structure_reorg (); } + +}; // class ipa_structure_reorg + +//ipa_opt_pass_d * +simple_ipa_opt_pass * +make_pass_ipa_structure_reorg ( gcc::context *ctxt) +{ + return new pass_ipa_structure_reorg ( ctxt); +} diff --git a/gcc/ipa-structure-reorg.h b/gcc/ipa-structure-reorg.h new file mode 100644 index 00000000000..8454c42dd89 --- /dev/null +++ b/gcc/ipa-structure-reorg.h @@ -0,0 +1,293 @@ +/* Interprocedural structure reorganization + Copyright (C) 2019-2020 Free Software Foundation, Inc. + + Contributed by Gary Oblock <gary@amperecomputing.com> + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +// This is depercated +#define USE_NEW_INTERFACE 1 +// This is when our stuff is integrated +#define INTEGRATION_FUNCTIONAL 0 +// Erick this is when you do something that really steps on some toes +#define KLUDGE 1 +#define USE_REORG_TYPES 1 +// If PRINT_FORMAT is true use pass specific print format. +#define PRINT_FORMAT false + +typedef struct RT_Elim RT_Elim; +typedef struct RT_Reorder RT_Reorder; +typedef struct RT_Interleave RT_Interleave; + +#include <set> +#include "collect-types.h" + +struct RT_Elim { + int dummy; +}; + +struct RT_Reorder { + int dummy; +}; + +struct RT_Interleave { + int numbOfGlobalArrays; // Statically allocated only + int numbOfLocalArrays; // Statically allocated only + int numbOfDynmAllocs; + tree base; + double reorg_perf; + double regular_perf; + bool multi_pool; // single pool if not set + int dummy; +}; + +typedef struct ReorgType ReorgType_t; +// TODO: Gary, is there supposed to be +// 1 ReorgType_t for every tree? +// Don't we need something that also is global? +// I have a set of trees that do not escape +// and I would like to use that for the moment. +// Not sure how it will evolve in the future... +// REPLY: Erick, I'm not sure I follow what you +// are asking. In my mind there should +// be one reorg type for every unique type and +// if some N types are equivalent then there should +// be only one reorg type for all of them. +struct ReorgType { + unsigned id; + bool delete_me; + tree gcc_type; // This info is for this type. + // TBD will field reordering and/or dead field elimination use + // use the reorg_ver_type? I think it's likely that they can. + tree reorg_ver_type; // the base type + tree pointer_rep; // new pointer format (multi-pool) + bool do_dead_field_elim; + bool do_field_reorder; + bool do_instance_interleave; + RT_Elim dead_field_elim; + RT_Reorder field_reorder; + RT_Interleave instance_interleave; +}; + +typedef struct ProgDecl ProgDecl_t; +struct ProgDecl { + tree gcc_decl; +}; + +enum ReorgOpTrans { + // TODO: What is the purpose of this enum? + // Let's try to use scalar insead of this... nope! + ReorgOpT_Temp, // SSA temp + ReorgOpT_Address, // "&x[i]" + ReorgOpT_Pointer, // "a" + ReorgOpT_Struct, // "s" + ReorgOpT_Deref, // "*a" + ReorgOpT_Array, // "x[i]" + ReorgOpT_Scalar, // "z" + ReorgOpT_Indirect, // "a->f" + ReorgOpT_AryDir // "x[i].f" +}; + +enum CompressionControl { + Initial, + Subsequent +}; + +enum ReorgTransformation { + // TODO: Gary, if these are transformation, shouldn't they have + // a pre-state and a post-state? + // Here I am only seeing statements. Not sure why + // a reorg transformation has statements / expressions. + // + // Also, why aren't we using the expressions already + // defined by GCC? For example EQ_EXPR? + // Ultimately what is the purpose of these? + ReorgT_StrAssign, // "*a = x[i]", "x[i] = y[j]", "s = *a", etc. + ReorgT_ElemAssign, // "a->f = z", "z = x[i].f", etc. + ReorgT_If_Null, // "if(a == 0)..." + ReorgT_If_NotNull, // "if(a != 0)..." + ReorgT_IfPtrEQ, // "if(a == b)..." + ReorgT_IfPtrNE, // "if(a != b)..." + ReorgT_IfPtrLT, // "if(a < b)..." + ReorgT_IfPtrGT, // "if(a > b)..." + ReorgT_IfPtrLE, // "if(a <= b)..." + ReorgT_IfPtrGE, // "if(a >= b)..." + ReorgT_PtrPlusInt, // "a = b + i" + ReorgT_Ptr2Zero, // "a = 0" + ReorgT_PtrDiff, // "i = a - b" + ReorgT_Adr2Ptr, // "a = &x[i]" + ReorgT_PtrNull, // "x = a == 0" + ReorgT_PtrNotNull, // "x = a != 0" + ReorgT_PtrEQ, // "x = a == b" + ReorgT_PtrNE, // "x = a != b" + ReorgT_PtrLT, // "x = a < b" + ReorgT_PtrLE, // "x = a <= b" + ReorgT_PtrGT, // "x = a > b" + ReorgT_PtrGE, // "x = a >= b" + ReorgT_Malloc, // + ReorgT_Calloc, // + ReorgT_Realloc, // + ReorgT_Free, // + ReorgT_UserFunc, // + ReorgT_Convert, // type casts + ReorgT_Return, // return t + Not_Supported +}; + +// Added as design bug fix +typedef struct BoolPair BoolPair_t; +struct BoolPair { + bool processed; + bool layout_changed; +}; + +typedef struct Info Info_t; +struct Info { + // TODO: What is the meaning of reorg type? + // Hasn't this meaning changed now that we have three + // transformations roughly running at the same time? + std::vector <ReorgType_t> *reorg_type; + // Added to by remove_deleted_types + std::vector <ReorgType_t> *saved_reorg_type; + std::vector <ProgDecl_t> *prog_decl; + // Gcc doesn't have global decls readily available + // so this holds them + std::map <tree,BoolPair_t> *struct_types; // desing bug fix + // ptrset_t holds types which point to records + // and types which escape + ptrset_t sets; + int num_deleted; + double total_cache_accesses; + FILE *reorg_dump_file; + // Debug flags + bool show_all_reorg_cands; + bool show_all_reorg_cands_in_detail; + bool show_prog_decls; + bool show_delete; + bool show_new_BBs; + bool show_transforms; + bool show_bounds; + bool is_non_escaping_set_empty(); + + Info (std::vector <ReorgType_t> *v1, + std::vector <ReorgType_t> *v2, + std::vector <ProgDecl_t> *v3, + std::map <tree, BoolPair_t> *v4) + : reorg_type(v1) + , saved_reorg_type(v2) + , prog_decl(v3) + , struct_types(v4) + , num_deleted(0) + , total_cache_accesses(0) + , reorg_dump_file(NULL) + , show_all_reorg_cands(false) + , show_all_reorg_cands_in_detail(false) + , show_prog_decls(false) + , show_new_BBs(false) + , show_transforms(false) + , show_bounds(false) + {}; +}; + +// This will perform a function on the supplied +// reorg type. It's primarily to support debugging. +typedef void (*ReorgFn)( Info *, ReorgType_t *); + +#define USE_NEW_INTERFACE 1 +#if USE_NEW_INTERFACE +extern int str_reorg_dead_field_eliminate_qual ( Info *); +extern int str_reorg_dead_field_eliminate_trans ( Info *); +extern int str_reorg_field_reorder_qual ( Info *); +extern int str_reorg_field_reorder_trans ( Info *); +extern int str_reorg_instance_interleave_qual ( Info *); +extern int str_reorg_instance_interleave_trans ( Info *); +#else +extern int str_reorg_dead_field_eliminate ( Info *); +extern int str_reorg_field_reorder ( Info *); +extern int str_reorg_instance_interleave ( Info *); +#endif + +extern int number_of_levels ( tree); +extern bool modify_decl_core ( tree *, Info *); +extern void delete_reorgtype ( ReorgType_t *, Info_t *); +extern void undelete_reorgtype ( ReorgType_t *, Info_t *); +extern void clear_deleted_types( Info *); +extern void restore_deleted_types ( Info *); +extern void remove_deleted_types ( Info *, ReorgFn); +extern enum ReorgOpTrans recognize_op ( tree, Info_t *); +extern ReorgTransformation reorg_recognize ( gimple *, + cgraph_node *, + Info_t *); +extern void apply_to_all_gimple ( bool (*)(gimple *, void *), bool, void *); +extern ReorgType_t *get_reorgtype_info ( tree, Info_t *); +extern void print_reorg_with_msg ( FILE *, ReorgType_t *, int, const char *); +extern ReorgType_t *contains_a_reorgtype ( gimple *, Info *); +extern bool tree_contains_a_reorgtype_p ( tree, Info *); +extern ReorgType_t *tree_contains_a_reorgtype ( tree, Info *); +extern bool is_reorg_type ( tree, Info_t *); +extern tree base_type_of ( tree); +extern tree base_type_with_levels ( tree, int *); +extern void print_reorg ( FILE *, int, ReorgType_t *); +extern void print_program ( FILE *, bool, int, Info_t *); +extern void print_type ( FILE *, tree); +extern void modify_ssa_name_type ( tree, tree); +extern bool print_internals (gimple *, void *); + + + +// I have no intention of leaving this or uses of the +// defined marcos in the code. However, some of uses +// should obviously be converted to dump file information. + +#define DEBUGGING 0 +#if DEBUGGING +enum Display { + Show_nothing, + Show_failures, + Show_everything +}; + +enum Failure { + Do_not_fail, + Fail_1st_bad, + Fail_at_End +}; + +extern int debug_indenting; +extern void handle_debug_indenting( int); +extern const char *code_str( enum tree_code); +extern void wolf_fence( Info *); +extern bool ssa_check ( FILE *, Display, Failure, bool, bool); + +// Line numbered +#define DEBUG_L(...) { fprintf( stderr, "L# %4d: %*s", __LINE__, debug_indenting, ""); fprintf( stderr, __VA_ARGS__); } +// Alinged with line numbered +#define DEBUG_A(...) { fprintf( stderr, "%*s", debug_indenting + 7, ""); fprintf( stderr, __VA_ARGS__); } +//With no indenting +#define DEBUG(...) { fprintf( stderr, __VA_ARGS__); } +#define DEBUG_F(f,...) f( __VA_ARGS__) +#define INDENT(a) handle_debug_indenting(a) +#else +#define DEBUG_L(...) +#define DEBUG_A(...) +#define DEBUG(...) +#define DEBUG_F(...) +#define INDENT(a) + +#endif +extern void flexible_print( FILE *, tree, int, dump_flags_t); + diff --git a/gcc/ipa-type-escape-analysis.c b/gcc/ipa-type-escape-analysis.c new file mode 100644 index 00000000000..45875f74443 --- /dev/null +++ b/gcc/ipa-type-escape-analysis.c @@ -0,0 +1,369 @@ +#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 "gimple-collector.hpp" +#include "gimple-escaper.hpp" +#include "gimple-caster.hpp" +#include "gimple-accesser.hpp" +#include "type-stringifier.hpp" +#include "type-incomplete-equality.hpp" +#include "type-reconstructor.hpp" +#include "gimple-rewriter.hpp" +#include <vector> + + +static unsigned int iphw_execute(); + +namespace { +/* ==What is type-escape-analysis?== + * type-escape-analysis is a SIMPLE_IPA_PASS that performs no transformations. + * type-escape-analysis only performs analysis and outputs to a WPA dump file. + * + * ==Why should we run type-escape-analysis?== + * type-escape-analysis is useful to run unit tests in gcc. + * By having the type-escape-analysis execute during WPA we are able to use + * the dejagnu framework to see if an expected output matches the observed + * output in the wpa dump file. + * + * ==How do we use type-escape-analysis?== + * Compile with + * -flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis + * + * To use type-escape-analysis in tests use the following lines + * { dg-do link } + * { dg-options "-flto -fipa-type-escape-analysis -fdump-ipa-type-escape-analysis" } + * C code to test... + * { dg-final { scan-wpa-ipa-dump "regex" "type-escape-analysis" } } + * + * ==TODO== + * At the moment, the tests are not set up to work with the current framework, + * so I will need to update them in the following day. + */ +const pass_data pass_data_ipa_type_escape_analysis = +{ + SIMPLE_IPA_PASS, + "type-escape-analysis", + OPTGROUP_NONE, + TV_NONE, + (PROP_cfg | PROP_ssa), + 0, + 0, + 0, + 0, +}; + +class pass_ipa_type_escape_analysis : public simple_ipa_opt_pass +{ +public: + pass_ipa_type_escape_analysis (gcc::context *ctx) + : simple_ipa_opt_pass(pass_data_ipa_type_escape_analysis, ctx) + {} + + virtual bool gate(function*) { return in_lto_p && flag_ipa_type_escape_analysis && flag_profile_use; } + virtual unsigned execute (function*) { return iphw_execute(); } +}; +} // anon namespace + +simple_ipa_opt_pass* +make_pass_ipa_type_escape_analysis (gcc::context *ctx) +{ + return new pass_ipa_type_escape_analysis (ctx); +} + +static void collect_types(); + +static unsigned int +iphw_execute() +{ + collect_types(); + return 0; +} + +static void +fix_escaping_types_in_set(ptrset_t &types) +{ + bool fixed_point_reached = false; + TypeIncompleteEquality structuralEquality; + TypeStringifier stringifier; + do { + std::vector<const_tree> fixes; + fixed_point_reached = true; + for (auto i = types.escaping.cbegin(), e = types.escaping.cend(); i != e; ++i) + { + for (auto j = types.non_escaping.cbegin(), f = types.non_escaping.cend(); j != f; ++j) + { + const_tree type_esc = *i; + gcc_assert(type_esc); + const_tree type_non = *j; + gcc_assert(type_non); + // There can be cases where incomplete types are marked as non-escaping + // and complete types counter parts are marked as escaping. + //const bool interesting_case = eq_type_compare(type_esc, type_non); + //TODO: We are going to need a different type comparison because this one + //fails to take into account the recursion... + std::string type_esc_name = TypeStringifier::get_type_identifier(type_esc); + std::string type_non_name = TypeStringifier::get_type_identifier(type_non); + + type_esc_name = stringifier.stringify(type_esc); + type_non_name = stringifier.stringify(type_non); + + const bool equal = structuralEquality.equal(type_esc, type_non); + if (!equal) continue; + + log("recalulating %s == %s\n", type_esc_name.c_str(), type_non_name.c_str()); + fixed_point_reached = false; + // Add incomplete to escaping + // delete incomplete from non_escaping + // We shouldn't do that inside our iteration loop. + fixes.push_back(type_non); + } + } + + for (auto i = fixes.cbegin(), e = fixes.cend(); i != e; ++i) + { + const_tree escaping_type = *i; + types.escaping.insert(escaping_type); + types.non_escaping.erase(escaping_type); + } + } while (!fixed_point_reached); +} + +static void +collect_types() +{ + GimpleTypeCollector collector; + collector.walk(); + collector.print_collected(); + ptrset_t types = collector.get_pointer_set(); + GimpleCaster caster(types); + caster.walk(); + if (flag_print_cast_analysis) caster.print_reasons(); + ptrset_t casting = caster.get_sets(); + fix_escaping_types_in_set(casting); + GimpleAccesser accesser; + accesser.walk(); + if (flag_print_access_analysis) accesser.print_accesses(); + record_field_map_t record_field_map = accesser.get_map(); + TypeIncompleteEquality equality; + bool has_fields_that_can_be_deleted = false; + typedef std::set<unsigned> field_offsets_t; + typedef std::map<const_tree, field_offsets_t> record_field_offset_map_t; + record_field_offset_map_t record_field_offset_map; + //TODO: We need to optimize this, compiling GCC is taking too long + for (auto i = record_field_map.begin(), e = record_field_map.end(); i != e; ++i) + { + const_tree r_i = i->first; + std::vector<const_tree> equivalence; + for (auto j = record_field_map.cbegin(), f = record_field_map.cend(); j != f; j++) + { + const_tree r_j = j->first; + const bool pointer_equal = r_i == r_j; + if (pointer_equal) continue; + + bool is_p_record = casting.in_points_to_record(r_i) && casting.in_points_to_record(r_j); + if (!is_p_record) continue; + + const bool are_equal = equality.equal(r_i, r_j); + if (!are_equal) continue; + + equivalence.push_back(r_j); + } + + field_offsets_t field_offset; + field_access_map_t original_field_map = record_field_map[r_i]; + for (auto j = original_field_map.begin(), f = original_field_map.end(); j != f; ++j) + { + const_tree f_k = j->first; + unsigned access = j->second; + const bool is_read = access & Read; + unsigned f_offset = tree_to_uhwi(DECL_FIELD_OFFSET(f_k)); + unsigned f_offset_2 = tree_to_uhwi(DECL_FIELD_BIT_OFFSET(f_k)); + //log("%s offset %u %u is_read %s\n", TypeStringifier::get_field_identifier(f_k).c_str(), f_offset, f_offset_2, is_read ? "t" :"f"); + if (!is_read) continue; + field_offset.insert(f_offset * 8 + f_offset_2); + } + for (auto j = equivalence.begin(), f = equivalence.end(); j != f; j++) + { + const_tree r_j = *j; + field_access_map_t equivalent_field_map = record_field_map[r_j]; + + for (auto k = equivalent_field_map.begin(), g = equivalent_field_map.end(); k != g; ++k) + { + const_tree f_k = k->first; + unsigned access = k->second; + const bool is_read = access & Read; + unsigned f_offset = tree_to_uhwi(DECL_FIELD_OFFSET(f_k)); + unsigned f_offset_2 = tree_to_uhwi(DECL_FIELD_BIT_OFFSET(f_k)); + //log("%s offset %u %u is_read %s\n", TypeStringifier::get_field_identifier(f_k).c_str(), f_offset, f_offset_2, is_read ? "t" :"f"); + if (!is_read) continue; + field_offset.insert(f_offset * 8 + f_offset_2); + } + } + record_field_offset_map[r_i] = field_offset; + } + + + const typeset &non_escaping = casting.non_escaping; + + std::vector<const_tree> to_erase; + std::set<const_tree> to_keep; + for (auto i = record_field_offset_map.begin(), e = record_field_offset_map.end(); i != e; ++i) + { + const_tree record = i->first; + const bool in_set = non_escaping.find(record) != non_escaping.end(); + if (!in_set) { + to_erase.push_back(record); + continue; + } + + field_offsets_t field_offset = i->second; + for (tree field = TYPE_FIELDS(record); field; field = DECL_CHAIN(field)) + { + unsigned f_offset = tree_to_uhwi(DECL_FIELD_OFFSET(field)); + unsigned f_offset_2 = tree_to_uhwi(DECL_FIELD_BIT_OFFSET(field)); + f_offset = f_offset * 8 + f_offset_2; + bool in_set2 = field_offset.find(f_offset) != field_offset.end(); + if (in_set2) { + field_offset.erase(f_offset); + continue; + } + to_keep.insert(record); + field_offset.insert(f_offset); + has_fields_that_can_be_deleted = true; + log("%s.%s may be deleted\n", TypeStringifier::get_type_identifier(record).c_str(), TypeStringifier::get_field_identifier(field).c_str()); + } + record_field_offset_map[record] = field_offset; + } + + for (auto i = to_erase.begin(), e = to_erase.end(); i != e; ++i) + { + const_tree record = *i; + record_field_offset_map.erase(record); + } + + if (!has_fields_that_can_be_deleted) return; + + TypeReconstructor reconstructor(record_field_offset_map); + TypeStringifier stringifier; + + // TODO: + // Here, what we want to do is we want to rewrite only the + // types which we believe we can rewrite, that and all types which + // point to those types... + // + // Otherwise, it will lead to difficulties in the future since + // we could be modifying many different types. + // So we have to make sure that we are only modifying the types of interest. + for (auto i = types.points_to_record.cbegin(), e = types.points_to_record.cend(); i != e; ++i) + { + const_tree record = *i; + std::string name_from = stringifier.stringify(TYPE_MAIN_VARIANT(record)); + bool points_to_record = false; + const_tree tt = record; + + + //TODO: + //This is our little hack to make sure that we are + //only modifying types which are of interest. + //However, we really shouldn't. + //Let's clean the input to reconstructor.walk + while (TREE_TYPE(tt)) { tt = TREE_TYPE(tt); }; + points_to_record = TREE_CODE(tt) == RECORD_TYPE; + if (!points_to_record) continue; + + bool in_map = record_field_offset_map.find(tt) != record_field_offset_map.end(); + if (!in_map) continue; + + // We need to walk over the type main variant first... + reconstructor.walk(TYPE_MAIN_VARIANT(record)); + log("walking main variant %s\n", name_from.c_str()); + } + + for (auto i = types.points_to_record.cbegin(), e = types.points_to_record.cend(); i != e; ++i) + { + const_tree record = *i; + std::string name_from = stringifier.stringify(record); + bool points_to_record = false; + const_tree tt = record; + + + //TODO: + //This is our little hack to make sure that we are + //only modifying types which are of interest. + //However, we really shouldn't. + //Let's clean the input to reconstructor.walk + while (TREE_TYPE(tt)) { tt = TREE_TYPE(tt); }; + points_to_record = TREE_CODE(tt) == RECORD_TYPE; + if (!points_to_record) continue; + + bool in_map = record_field_offset_map.find(tt) != record_field_offset_map.end(); + if (!in_map) continue; + + // We need to walk over the type main variant first... + reconstructor.walk(record); + log("walking non main%s\n", name_from.c_str()); + } + + + TypeReconstructor::reorg_record_map_t map = reconstructor.get_map(); + TypeReconstructor::reorg_field_map_t field_map = reconstructor.get_field_map(); + + for (auto i = map.cbegin(), e = map.cend(); i != e; ++i) + { + const_tree o_record = i->first; + tree r_record = i->second; + tree m_record = TYPE_MAIN_VARIANT(r_record); + std::string name_from = stringifier.stringify(o_record); + std::string name_to = stringifier.stringify(r_record); + std::string name_to_mv = stringifier.stringify(m_record); + bool c_f = TYPE_CACHED_VALUES_P(o_record); + bool c_t = TYPE_CACHED_VALUES_P(r_record); + bool c_m = TYPE_CACHED_VALUES_P(m_record); + TYPE_CACHED_VALUES_P((tree)o_record) = false; + TYPE_CACHED_VALUES_P((tree)m_record) = false; + + log("map f: %s TYPE_CACHED_VALUES_P %s \n", name_from.c_str(), c_f ? "t" : "f"); + log("map t: %s TYPE_CACHED_VALUES_P %s \n", name_to.c_str(), c_t ? "t" : "f"); + log("map m: %s TYPE_CACHED_VALUES_P %s \n", name_to_mv.c_str(), c_m ? "t" : "f"); + bool in_map = map.find(m_record) != map.end(); + if (!in_map) continue; + tree mm_record = map[m_record]; + std::string name_to_mmv = stringifier.stringify(mm_record); + bool c_m2 = TYPE_CACHED_VALUES_P(mm_record); + log("map m2: %s TYPE_CACHED_VALUES_P %s\n", name_to_mmv.c_str(), c_m2 ? "t" : "f"); + // TODO: This is a hack... + TYPE_MAIN_VARIANT(r_record) = mm_record; + // Do we need to layout the type again? + } + + + GimpleTypeRewriter rewriter(map, field_map); + rewriter.walk(); + rewriter._rewrite_function_decl(); + + GimpleWalker walker; + walker.walk(); // Just for printing... + + log("FINISHED\n"); +} diff --git a/gcc/passes.def b/gcc/passes.def index c0098d755bf..67d2520c1f8 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -171,6 +171,9 @@ along with GCC; see the file COPYING3. If not see compiled unit. */ INSERT_PASSES_AFTER (all_late_ipa_passes) NEXT_PASS (pass_materialize_all_clones); + NEXT_PASS (pass_ipa_type_escape_analysis); + NEXT_PASS (pass_ipa_structure_reorg); + NEXT_PASS (pass_ipa_prototype); NEXT_PASS (pass_ipa_pta); NEXT_PASS (pass_omp_simd_clone); TERMINATE_PASS_LIST (all_late_ipa_passes) diff --git a/gcc/timevar.def b/gcc/timevar.def index 7dd1e2685a4..4df2c473359 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -81,6 +81,7 @@ DEFTIMEVAR (TV_IPA_INLINING , "ipa inlining heuristics") DEFTIMEVAR (TV_IPA_FNSPLIT , "ipa function splitting") DEFTIMEVAR (TV_IPA_COMDATS , "ipa comdats") DEFTIMEVAR (TV_IPA_OPT , "ipa various optimizations") +DEFTIMEVAR (TV_IPA_STRUCTURE_REORG , "ipa structure reorg") DEFTIMEVAR (TV_IPA_LTO_DECOMPRESS , "lto stream decompression") DEFTIMEVAR (TV_IPA_LTO_COMPRESS , "lto stream compression") DEFTIMEVAR (TV_IPA_LTO_OUTPUT , "lto stream output") diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index f01e811917d..bcb7e921eac 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -501,6 +501,7 @@ extern ipa_opt_pass_d *make_pass_ipa_fn_summary (gcc::context *ctxt); extern ipa_opt_pass_d *make_pass_ipa_inline (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_free_lang_data (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_free_fn_summary (gcc::context *ctxt); +extern simple_ipa_opt_pass *make_pass_ipa_prototype (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); @@ -508,6 +509,8 @@ extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt); extern ipa_opt_pass_d *make_pass_ipa_odr (gcc::context *ctxt); extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt); extern ipa_opt_pass_d *make_pass_ipa_pure_const (gcc::context *ctxt); +extern simple_ipa_opt_pass *make_pass_ipa_structure_reorg (gcc::context *ctxt); +extern simple_ipa_opt_pass *make_pass_ipa_type_escape_analysis (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_pta (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_tm (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_target_clone (gcc::context *ctxt); diff --git a/gcc/type-accessor.c b/gcc/type-accessor.c new file mode 100644 index 00000000000..a332115d879 --- /dev/null +++ b/gcc/type-accessor.c @@ -0,0 +1,34 @@ +#include "type-accessor.hpp" +#include "type-stringifier.hpp" + +void +TypeAccessor::_walk_RECORD_TYPE_pre(const_tree t) +{ + log("type walking\n"); + add_all_fields_in_struct(t); +} + +void +TypeAccessor::add_all_fields_in_struct(const_tree t) +{ + TypeStringifier stringifier; + std::string name = stringifier.stringify(t); + log("am i in add all fields ? %s\n", name.c_str()); + const enum tree_code c = TREE_CODE(t); + const bool is_record = RECORD_TYPE == c; + if (!is_record) return; + + const bool record_already_in_map = _map.find(t) != _map.end(); + field_access_map_t field_map; + field_map = record_already_in_map ? _map[t] : field_map; + + // Let's add all fields to the field map as empty. + for (tree field = TYPE_FIELDS(t); field; field = DECL_CHAIN(field)) + { + const bool field_already_in_map_2 = field_map.find(field) != field_map.end(); + if (field_already_in_map_2) continue; + field_map[field] = Empty; + } + + _map[t] = field_map; +} diff --git a/gcc/type-accessor.hpp b/gcc/type-accessor.hpp new file mode 100644 index 00000000000..d86bf287f1a --- /dev/null +++ b/gcc/type-accessor.hpp @@ -0,0 +1,47 @@ +#pragma once + +#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 "expr-accessor.hpp" +#include "type-walker.hpp" + +typedef std::map<const_tree, unsigned> field_access_map_t; +typedef std::map<const_tree, field_access_map_t> record_field_map_t; + +class TypeAccessor : public TypeWalker +{ +public: + TypeAccessor(record_field_map_t &map) : _map(map) { }; +private: + record_field_map_t &_map; + virtual void _walk_RECORD_TYPE_pre(const_tree t) final; + void add_all_fields_in_struct(const_tree t); +}; diff --git a/gcc/type-canonical-equality.c b/gcc/type-canonical-equality.c new file mode 100644 index 00000000000..93221c0a86f --- /dev/null +++ b/gcc/type-canonical-equality.c @@ -0,0 +1,56 @@ +#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" // needed for gimple-iterator.h +#include "gimple-iterator.h" +#include "gimple-ssa.h" +#include <stdbool.h> + + +#include "types-inlines.h" +#include "type-structural-equality.hpp" +#include "type-structural-main-variant.hpp" +#include "type-canonical-equality.hpp" +#include "type-stringifier.hpp" + +bool +TypeCanonicalEquality::_equal(const_tree l, const_tree r) +{ + bool valid_inputs = l && r; + if (!valid_inputs) return l == r; + + const_tree canonical_l = TYPE_CANONICAL(l); + const_tree canonical_r = TYPE_CANONICAL(r); + const bool can_compare_canonical = canonical_l && canonical_r; + if (!can_compare_canonical) return TypeStructuralEquality::_equal(l, r); + + const bool different = canonical_l != canonical_r; + const std::string n_l = TypeStringifier::get_type_identifier(l); + const std::string n_r = TypeStringifier::get_type_identifier(r); + if (different) return false; + + return TypeStructuralEqualityMainVariant::_equal(l, r); +} diff --git a/gcc/type-canonical-equality.hpp b/gcc/type-canonical-equality.hpp new file mode 100644 index 00000000000..98a159b4dd0 --- /dev/null +++ b/gcc/type-canonical-equality.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include "type-structural-main-variant.hpp" + +class TypeCanonicalEquality : public TypeStructuralEqualityMainVariant { +public: + TypeCanonicalEquality() {}; +protected: + virtual bool _equal(const_tree l, const_tree r); +}; diff --git a/gcc/type-collector.c b/gcc/type-collector.c new file mode 100644 index 00000000000..e5bccb0a34a --- /dev/null +++ b/gcc/type-collector.c @@ -0,0 +1,271 @@ +#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 "type-collector.hpp" +#include "type-stringifier.hpp" +#include "types-inlines.h" + +void +TypeCollector::collect(const_tree t) +{ + const bool in_set = ptrset.in_universe(t); + // memoization... + if (in_set) return; + gcc_assert(t); + + if (!ptr.empty()) + { + TypeStringifier stringifier; + std::string in_name = stringifier.stringify(t); + gcc_unreachable(); + } + walk(t); +} + +void +TypeCollector::_sanity_check() +{ + for (auto i = ptrset.points_to_record.cbegin(), e = ptrset.points_to_record.cend(); i != e; ++i) + { + for (auto j = ptrset.complement.cbegin(), f = ptrset.complement.cend(); j != f; ++j) + { + const_tree type_ptr = *i; + gcc_assert(type_ptr); + const_tree type_com = *j; + gcc_assert(type_com); + const bool valid_sets = type_ptr != type_com; + if (valid_sets) continue; + // Normally, we want a stronger type comparison + // that is not just the pointer address + // but this is the first sanity check and then we will need to determine + // the stronger type comparison. + // But first we will need to fix the types... + TypeStringifier stringifier; + std::string name_ptr = stringifier.stringify(type_ptr); + std::string name_com = stringifier.stringify(type_com); + log("%p %s == %p %s\n", type_ptr, name_ptr.c_str(), type_com, name_com.c_str()); + gcc_unreachable(); + } + } +} + +bool +TypeCollector::is_memoized(const_tree t) +{ + const bool in_set = ptrset.in_universe(t); + if (!in_set) return false; + + const bool points_to_record = ptrset.in_points_to_record(t); + for (auto i = ptr.begin(), e = ptr.end(); i != e; ++i) + { + i->second |= points_to_record; + } + return true; +} + +void +TypeCollector::_walk_VOID_TYPE_pre(const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_VOID_TYPE_post(const_tree t) +{ + _collect_simple(t); +} + +void +TypeCollector::_walk_INTEGER_TYPE_pre(const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_INTEGER_TYPE_post(const_tree t) +{ + _collect_simple(t); +} + +void +TypeCollector::_walk_REAL_TYPE_pre(const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_REAL_TYPE_post(const_tree t) +{ + _collect_simple(t); +} + +void +TypeCollector::_walk_FIXED_POINT_TYPE_pre(const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_FIXED_POINT_TYPE_post(const_tree t) +{ + _collect_simple(t); +} + +void +TypeCollector::_walk_COMPLEX_TYPE_pre(const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_COMPLEX_TYPE_post(const_tree t) +{ + _collect_simple(t); +} + +void +TypeCollector::_walk_ENUMERAL_TYPE_pre(const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_ENUMERAL_TYPE_post(const_tree t) +{ + _collect_simple(t); +} + +void +TypeCollector::_walk_BOOLEAN_TYPE_pre(const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_BOOLEAN_TYPE_post(const_tree t) +{ + _collect_simple(t); +} + +void +TypeCollector::_collect_simple(const_tree t) +{ + ptrset.insert(t, ptr[t]); + ptr.erase(t); +} + +void +TypeCollector::_walk_ARRAY_TYPE_pre(const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_ARRAY_TYPE_post(const_tree t) +{ + _collect_simple(t); +} + +void +TypeCollector::_walk_POINTER_TYPE_pre(const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_POINTER_TYPE_post(const_tree t) +{ + _collect_simple(t); +} + +void +TypeCollector::_walk_REFERENCE_TYPE_pre(const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_REFERENCE_TYPE_post(const_tree t) +{ + _collect_simple(t); +} + +void +TypeCollector::_walk_RECORD_TYPE_post(const_tree t) +{ + // All in ptr point to record + for (auto i = ptr.begin(), e = ptr.end(); i != e; ++i) + { + i->second = true; + } + _collect_simple(t); +} + +void +TypeCollector::_walk_RECORD_TYPE_pre(const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_UNION_TYPE_pre(const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_UNION_TYPE_post(const_tree t) +{ + _collect_simple(t); +} + +void +TypeCollector::_walk_FUNCTION_TYPE_post(const_tree t) +{ + _collect_simple(t); +} + +void +TypeCollector::_walk_FUNCTION_TYPE_pre(const_tree t) +{ + ptr[t] = false; +} + +void +TypeCollector::_walk_METHOD_TYPE_post(const_tree t) +{ + _collect_simple(t); +} + +void +TypeCollector::_walk_METHOD_TYPE_pre(const_tree t) +{ + ptr[t] = false; +} diff --git a/gcc/type-collector.hpp b/gcc/type-collector.hpp new file mode 100644 index 00000000000..0252747ee9a --- /dev/null +++ b/gcc/type-collector.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include "type-walker.hpp" +#include "collect-types.h" +#include <map> +#include <stack> + +class TypeCollector : public TypeWalker { +public: + void collect(const_tree t); + TypeCollector() {}; + ptrset_t get_pointer_set() { _sanity_check(); return ptrset; } +private: + std::map<const_tree, bool> ptr; + ptrset_t ptrset; + void _sanity_check(); + void _collect_simple(const_tree t); + virtual bool is_memoized(const_tree t); + + virtual void _walk_VOID_TYPE_pre(const_tree t); + virtual void _walk_VOID_TYPE_post(const_tree t); + virtual void _walk_INTEGER_TYPE_pre(const_tree t); + virtual void _walk_INTEGER_TYPE_post(const_tree t); + virtual void _walk_REAL_TYPE_pre(const_tree t); + virtual void _walk_REAL_TYPE_post(const_tree t); + virtual void _walk_FIXED_POINT_TYPE_pre(const_tree t); + virtual void _walk_FIXED_POINT_TYPE_post(const_tree t); + virtual void _walk_COMPLEX_TYPE_pre(const_tree t); + virtual void _walk_COMPLEX_TYPE_post(const_tree t); + virtual void _walk_ENUMERAL_TYPE_pre(const_tree t); + virtual void _walk_ENUMERAL_TYPE_post(const_tree t); + virtual void _walk_BOOLEAN_TYPE_pre(const_tree t); + virtual void _walk_BOOLEAN_TYPE_post(const_tree t); + virtual void _walk_ARRAY_TYPE_pre(const_tree t); + virtual void _walk_ARRAY_TYPE_post(const_tree t); + virtual void _walk_POINTER_TYPE_pre(const_tree t); + virtual void _walk_POINTER_TYPE_post(const_tree t); + virtual void _walk_REFERENCE_TYPE_pre(const_tree t); + virtual void _walk_REFERENCE_TYPE_post(const_tree t); + virtual void _walk_RECORD_TYPE_pre(const_tree t); + virtual void _walk_RECORD_TYPE_post(const_tree t); + virtual void _walk_UNION_TYPE_pre(const_tree t); + virtual void _walk_UNION_TYPE_post(const_tree t); + virtual void _walk_FUNCTION_TYPE_pre(const_tree t); + virtual void _walk_FUNCTION_TYPE_post(const_tree t); + virtual void _walk_METHOD_TYPE_pre(const_tree t); + virtual void _walk_METHOD_TYPE_post(const_tree t); +}; + diff --git a/gcc/type-escaper.c b/gcc/type-escaper.c new file mode 100644 index 00000000000..870c2453bba --- /dev/null +++ b/gcc/type-escaper.c @@ -0,0 +1,231 @@ +#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" // needed for gimple-iterator.h +#include "gimple-iterator.h" +#include "gimple-ssa.h" +#include <stdbool.h> +#include "types-inlines.h" + +#include "type-escaper.hpp" +#include "type-stringifier.hpp" + +bool +TypeEscaper::is_memoized(const_tree t) +{ + const bool in_set = calc.find(t) != calc.end(); + if (!in_set) return false; + + const bool will_not_escape = !_reason.is_escaping(); + if (will_not_escape) return true; + + const bool already_escaping = in_set && calc[t].is_escaping(); + if (already_escaping) return true; + + return false; +} + +static inline void +assert_type_is_in_universe(const_tree type, ptrset_t &types) +{ +#ifdef SANITY_CHECKS + gcc_assert(types.in_universe(type)); +#endif +} + +ptrset_t +TypeEscaper::get_sets() +{ + place_escaping_types_in_set(); + return _ptrset; +} + +void +TypeEscaper::place_escaping_types_in_set() +{ + TypeStringifier stringifier; + for (auto i = calc.cbegin(), e = calc.cend(); i != e; ++i) + { + const_tree type = i->first; + // We should have seen it before + assert_type_is_in_universe(type, _ptrset); + + // We should only track interesting types + // Types which are not in points_to_record are the ones + // that are pointed to by records. + // I think it is possible to prune them ahead of time... + if (!_ptrset.in_points_to_record(type)) continue; + + const Reason reason = i->second; + std::string name = stringifier.stringify(type); + reason.is_escaping() ? _ptrset.escaping.insert(type) : _ptrset.non_escaping.insert(type); + } +} + +void +TypeEscaper::update(const_tree t, Reason r) +{ + gcc_assert(t); + _reason = r; + walk(t); +} + +void +TypeEscaper::update_single_level(const_tree t, Reason r) +{ + gcc_assert(t); + const bool already_in_typemap = calc.find(t) != calc.end(); + already_in_typemap ? calc[t] |= r : calc[t] = r; +} + +void +TypeEscaper::_update(const_tree t) +{ + gcc_assert(t); + // assert type is in universe + const bool already_in_typemap = calc.find(t) != calc.end(); + // Do we have to invalidate all types which point to a volatile type? + // Or do we have to invalidate all types pointed to by a volatile type? + // Or do we only invalidate all types which are volatile. + // This is only the third option. + const bool is_volatile = TYPE_VOLATILE(t); + Reason _is_volatile; + _is_volatile.type_is_volatile = is_volatile; + Reason _inner = _reason | _is_volatile; + _inner.type_is_casted = _inside_indirect_field > 0 ? false : _inner.type_is_casted; + if (_inside_function > 0) _inner.type_is_casted = false; + already_in_typemap ? calc[t] |= _inner : calc[t] = _inner; +} + +void +TypeEscaper::_walk_ARRAY_TYPE_pre(const_tree t) +{ + _update(t); +} + +void +TypeEscaper::_walk_POINTER_TYPE_pre(const_tree t) +{ + _inside_indirect_field = _inside_field > 0 ? _inside_indirect_field + 1 : _inside_indirect_field; + _update(t); +} + +void +TypeEscaper::_walk_POINTER_TYPE_post(const_tree t) +{ + _inside_indirect_field = _inside_field > 0 ? _inside_indirect_field - 1 : _inside_indirect_field; +} + +void +TypeEscaper::_walk_REFERENCE_TYPE_pre(const_tree t) +{ + _update(t); +} + +void +TypeEscaper::_walk_RECORD_TYPE_pre(const_tree t) +{ + _update(t); +} + +void +TypeEscaper::_walk_UNION_TYPE_pre(const_tree t) +{ + _inside_union++; + bool is_escaping = _inside_union > 0; + _update(t); + // After us... so that we can see what is our previous value + _reason.type_is_in_union |= is_escaping; +} + +void +TypeEscaper::_walk_field_pre(const_tree t) +{ + _inside_field++; +} + +void +TypeEscaper::_walk_field_post(const_tree t) +{ + _inside_field--; +} + +void +TypeEscaper::_walk_UNION_TYPE_post(const_tree t) +{ + _inside_union--; + Reason prev = calc[t]; + _update(t); + _reason = prev; +} + +void +TypeEscaper::_walk_FUNCTION_TYPE_pre(const_tree t) +{ + _inside_function++; +} + +void +TypeEscaper::_walk_FUNCTION_TYPE_post(const_tree t) +{ + _inside_function--; +} +void +TypeEscaper::_walk_function_or_method(const_tree t) +{ +} + + +void +TypeEscaper::_walk_FUNCTION_TYPE(const_tree t) +{ +} + +void +TypeEscaper::_walk_METHOD_TYPE(const_tree t) +{ +} + + +void +TypeEscaper::_walk_METHOD_TYPE_pre(const_tree t) +{ +} + +void +TypeEscaper::print_reasons() +{ + TypeStringifier stringifier; + for (auto i = calc.cbegin(), e = calc.cend(); i != e; ++i) + { + const_tree t = i->first; + std::string name = stringifier.stringify(t); + const bool in_universe = _ptrset.in_universe(t); + Reason r = i->second; + log("%s reason: ", name.c_str()); + r.print(); + } +} diff --git a/gcc/type-escaper.hpp b/gcc/type-escaper.hpp new file mode 100644 index 00000000000..3647374f716 --- /dev/null +++ b/gcc/type-escaper.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "type-walker.hpp" +#include "ipa-prototype.h" +#include "collect-types.h" +#include <map> + +class TypeEscaper : public TypeWalker +{ +public: + TypeEscaper(ptrset_t &p) : _ptrset(p), _inside_union(0), _inside_field(0), _inside_indirect_field(0), _inside_function(0) {}; + void update(const_tree t, Reason r); + void update_single_level(const_tree t, Reason r); + ptrset_t get_sets(); + ptrset_t &_ptrset; + typemap calc; + void print_reasons(); + virtual void _walk_POINTER_TYPE_pre(const_tree t) final; + virtual void _walk_POINTER_TYPE_post(const_tree t) final; + virtual void _walk_REFERENCE_TYPE_pre(const_tree t) final; + virtual void _walk_ARRAY_TYPE_pre(const_tree t) final; + virtual void _walk_RECORD_TYPE_pre(const_tree t) final; + virtual void _walk_UNION_TYPE_pre(const_tree t) final; + virtual void _walk_UNION_TYPE_post(const_tree t) override final; + virtual void _walk_METHOD_TYPE_pre(const_tree t) final override; + virtual void _walk_FUNCTION_TYPE_pre(const_tree t) final override; + virtual void _walk_FUNCTION_TYPE_post(const_tree t) final override; + virtual void _walk_METHOD_TYPE(const_tree t) final override; + virtual void _walk_FUNCTION_TYPE(const_tree t) final override; + virtual void _walk_function_or_method(const_tree t) final override; + virtual void _walk_field_pre(const_tree t) final; + virtual void _walk_field_post(const_tree t) final; + virtual bool is_memoized(const_tree t); + unsigned _inside_union; + unsigned _inside_field; + unsigned _inside_indirect_field; + unsigned _inside_function; + Reason _reason; + void _update(const_tree t); + void place_escaping_types_in_set(); +}; + diff --git a/gcc/type-incomplete-equality.c b/gcc/type-incomplete-equality.c new file mode 100644 index 00000000000..88072c40022 --- /dev/null +++ b/gcc/type-incomplete-equality.c @@ -0,0 +1,61 @@ +#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" // needed for gimple-iterator.h +#include "gimple-iterator.h" +#include "gimple-ssa.h" +#include <stdbool.h> + + +#include "types-inlines.h" +#include "type-structural-equality.hpp" +#include "type-structural-main-variant.hpp" +#include "type-canonical-equality.hpp" +#include "type-incomplete-equality.hpp" +#include "type-stringifier.hpp" + +bool +TypeIncompleteEquality::_equal(const_tree l, const_tree r) +{ + bool valid_inputs = l && r; + if (!valid_inputs) return l == r; + + // if any of these are incomplete, then we can only compare using identifiers... + const bool complete_l = is_complete(l); + const bool complete_r = is_complete(r); + bool can_compare_structurally = complete_l && complete_r; + if (can_compare_structurally) return TypeStructuralEquality::_equal(l, r); + + const_tree m_l = TYPE_MAIN_VARIANT(l); + const_tree m_r = TYPE_MAIN_VARIANT(r); + gcc_assert(m_l && m_r); + can_compare_structurally = m_l == m_r; + if (can_compare_structurally) return true; + + const std::string n_l = TypeStringifier::get_type_identifier(m_l); + const std::string n_r = TypeStringifier::get_type_identifier(m_r); + return n_l.compare(n_r) == 0; +} diff --git a/gcc/type-incomplete-equality.hpp b/gcc/type-incomplete-equality.hpp new file mode 100644 index 00000000000..4188f49713c --- /dev/null +++ b/gcc/type-incomplete-equality.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include "type-canonical-equality.hpp" + +class TypeIncompleteEquality : public TypeCanonicalEquality { +public: + TypeIncompleteEquality () {}; +protected: + virtual bool _equal(const_tree l, const_tree r); +}; diff --git a/gcc/type-reconstructor.c b/gcc/type-reconstructor.c new file mode 100644 index 00000000000..91d4f383ea7 --- /dev/null +++ b/gcc/type-reconstructor.c @@ -0,0 +1,412 @@ +#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" // needed for gimple-iterator.h +#include "gimple-iterator.h" +#include "gimple-ssa.h" +#include <stdbool.h> +#include "types-inlines.h" + +#include "type-reconstructor.hpp" +#include "type-stringifier.hpp" +#include "stor-layout.h" + + +// TODO: +// I think it might be possible that we need to create +// new nodes as opposed to copying the nodes. +// I say this because I think the copy is a shallow copy +// and it might be making things difficult if we don't +// know everything that is happening... +// so we might want to rewrite this class... +// +// +// TODO: +// Also, there's a bug in which the TYPE_MAIN_VARIANT is an old type... +// which is not a good thing. + +void +TypeReconstructor::set_is_not_modified_yet(const_tree t) +{ + gcc_assert(t); + const bool is_in_reorg_map = _reorg_map.find(t) != _reorg_map.end(); + modified_map[t] = false; + if (is_in_reorg_map) mark_all_pointing_here_as_modified(); + + const_tree tt = TREE_TYPE(t); + if (!tt) return; + + const bool is_in_reorg_map_2 = _reorg_map.find(tt) != _reorg_map.end(); + log ("is in reorg_map_2 ? %s\n", is_in_reorg_map_2 ? "t" : "f"); + if (!is_in_reorg_map_2) return; + + tree type = _reorg_map[tt]; + const bool is_modified = strstr(TypeStringifier::get_type_identifier(type).c_str(), ".reorg"); + log("is modified %s\n", is_modified ? "t" : "f"); + if (!is_modified) return; + + mark_all_pointing_here_as_modified(); + +} + +void +TypeReconstructor::mark_all_pointing_here_as_modified() +{ + for (auto i = modified_map.begin(), e = modified_map.end(); i != e; ++i) + { + const_tree type = i->first; + i->second = true; + } +} + +bool +TypeReconstructor::get_is_modified(const_tree t) +{ + gcc_assert(t); + const bool in_map = modified_map.find(t) != modified_map.end(); + gcc_assert(in_map); + bool retval = modified_map[t]; + modified_map.erase(t); + + bool points_to_record = false; + tree tt = (tree)t; + while (TREE_TYPE(tt)) + { + tt = TREE_TYPE(tt); + } + points_to_record = TREE_CODE(tt) == RECORD_TYPE; + + + return retval && points_to_record; +} + +bool +TypeReconstructor::is_memoized(const_tree t) +{ + const bool already_changed = _reorg_map.find(t) != _reorg_map.end(); + mark_all_pointing_here_as_modified(); + const bool has_typed_cached_values = TYPE_CACHED_VALUES_P (t); + TypeStringifier stringifier; + std::string name = stringifier.stringify(t); + return already_changed; +} + +static tree +get_new_identifier(const_tree type) +{ + const char* identifier = TypeStringifier::get_type_identifier(type).c_str(); + const bool is_new_type = strstr(identifier, "reorg"); + gcc_assert(!is_new_type); + char *new_name; + asprintf(&new_name, "%s.reorg", identifier); + return get_identifier(new_name); +} + +// Invariant for all _pre functions: +// _reorg_map[t] == NULL (due to memoization) +// +// Invariant for all _post functions: +// _reorg_map[TREE_TYPE(t)] != NULL +// unless TREE_TYPE(t) is not a type which points to a record +// a.k.a. no modifications +// +// To preserve invariant, we must include +// the following at the end of all _post functions: +// _reorg_map[t] = /* reorg type */ +// +// How information is passed? +// To further _post functions via stacks +// +// To previous _post functions via _reorg_map (and maybe others?) +// +// +void +TypeReconstructor::_walk_ARRAY_TYPE_pre(const_tree t) +{ + for_reference.push(t); + set_is_not_modified_yet(t); + + tree copy = build_variant_type_copy((tree) t); + tree domain = TYPE_DOMAIN(t); + if (domain) { + tree copy_domain = copy_node(domain); + tree min = TYPE_MIN_VALUE(domain); + tree max = TYPE_MAX_VALUE(domain); + TYPE_MIN_VALUE(copy_domain) = copy_node(min); + TYPE_MAX_VALUE(copy_domain) = copy_node(max); + } + in_progress.push(copy); + +} + +void +TypeReconstructor::_walk_ARRAY_TYPE_post(const_tree t) +{ + const_tree t2 = for_reference.top(); + gcc_assert(t2 == t); + for_reference.pop(); + tree copy = in_progress.top(); + in_progress.pop(); + + bool is_modified = get_is_modified(t); + + TREE_TYPE(copy) = build_variant_type_copy(TREE_TYPE(copy)); + copy = is_modified ? build_distinct_type_copy(copy) : copy; + TREE_TYPE(copy) = is_modified ? _reorg_map[TREE_TYPE(t)] : TREE_TYPE(copy); + TYPE_NAME(copy) = is_modified ? get_new_identifier(copy) : TYPE_NAME(copy); + // This is useful so that we go again through type layout + TYPE_SIZE(copy) = is_modified ? NULL : TYPE_SIZE(copy); + tree domain = TYPE_DOMAIN(t); + if (domain) { + tree copy_domain = copy_node(domain); + tree min = TYPE_MIN_VALUE(domain); + tree max = TYPE_MAX_VALUE(domain); + TYPE_MIN_VALUE(copy_domain) = copy_node(min); + TYPE_MAX_VALUE(copy_domain) = copy_node(max); + } + TypeStringifier stringifier; + //std::string name = stringifier.stringify(copy); + log("are we going to crash is modified %s %s\n", is_modified ? "t" : "f", TypeStringifier::get_type_identifier(copy).c_str()); + if (is_modified) layout_type(copy); + TYPE_CACHED_VALUES_P (copy) = false; + //TYPE_CACHED_VALUES (copy) = TYPE_CACHED_VALUES(t); + tree tt = (tree)t; + while (TREE_TYPE(tt)) { tt = TREE_TYPE(tt); }; + + const bool points_to_record = TREE_CODE(tt) == RECORD_TYPE; + if (!points_to_record) return; + + _reorg_map[t] = is_modified ? copy : (tree)t; +} + +void +TypeReconstructor::_walk_POINTER_TYPE_pre(const_tree t) +{ + for_reference.push(t); + set_is_not_modified_yet(t); + + tree copy = build_variant_type_copy((tree) t); + in_progress.push(copy); +} + +void +TypeReconstructor::_walk_POINTER_TYPE_post(const_tree t) +{ + const_tree t2 = for_reference.top(); + gcc_assert(t2 == t); + for_reference.pop(); + tree copy = in_progress.top(); + in_progress.pop(); + + bool is_modified = get_is_modified(t); + + copy = is_modified ? build_variant_type_copy(copy) : copy; + TREE_TYPE(copy) = is_modified ? _reorg_map[TREE_TYPE(t)] : TREE_TYPE(copy); + TYPE_NAME(copy) = is_modified ? get_new_identifier(copy) : TYPE_NAME(copy); + // This is useful so that we go again through type layout + //TYPE_SIZE(copy) = is_modified ? NULL : TYPE_SIZE(copy); + //if (is_modified) layout_type(copy); + TYPE_CACHED_VALUES_P (copy) = false; + //TYPE_CACHED_VALUES (copy) = TYPE_CACHED_VALUES(t); + //Let's just make sure that we are pointing to a a struct... + + tree tt = (tree)t; + while (TREE_TYPE(tt)) { tt = TREE_TYPE(tt); }; + const bool points_to_record = TREE_CODE(tt) == RECORD_TYPE; + if (!points_to_record) return; + + _reorg_map[t] = is_modified ? copy : (tree)t; +} + +void +TypeReconstructor::_walk_RECORD_TYPE_pre(const_tree t) +{ + const bool is_main_variant = TYPE_MAIN_VARIANT(t) == t; + if (!is_main_variant) { + const_tree main_variant = TYPE_MAIN_VARIANT(t); + _walk_RECORD_TYPE_pre(main_variant); + TypeWalker::_walk_RECORD_TYPE(main_variant); + _walk_RECORD_TYPE_post(main_variant); + } + + set_is_not_modified_yet(t); + for_reference.push(t); + // We don't know if we will modify this type t + // So, let's make a copy. Just in case. + tree copy = build_variant_type_copy((tree) t); + in_progress.push(copy); + field_list_stack.push( field_tuple_list_t() ); +} + +void +TypeReconstructor::_walk_RECORD_TYPE_post(const_tree t) +{ + const_tree t2 = for_reference.top(); + gcc_assert(t2 == t); + for_reference.pop(); + + tree copy = in_progress.top(); + in_progress.pop(); + field_tuple_list_t field_tuple_list = field_list_stack.top(); + field_list_stack.pop(); + + // So, here all the work has been done to make sure + // that the fields produced a field_tuple_list_t + // with old fields and pointers to new fields. + // There might be NULL values if new fields are eliminated. + // So, now we want to do a couple of things. + // First, is we need to change the TYPE_FIELDS + // of the copy + bool is_modified = get_is_modified(t); + tree prev_field = NULL; + for (auto i = field_tuple_list.cbegin(), e = field_tuple_list.cend(); i != e; ++i) + { + field_tuple_t field_tuple = *i; + const_tree original_field = field_tuple.first; + tree modified_field = field_tuple.second; + if (!modified_field) { + is_modified = true; + continue; + } + + tree current_field = modified_field; + if (!prev_field) { + TYPE_FIELDS(copy) = current_field; + } else { + DECL_CHAIN(prev_field) = current_field; + } + prev_field = current_field; + } + + // We only had one field + if (!prev_field && is_modified) { + log("deleting all fields for struct %s\n", TypeStringifier::get_type_identifier(copy).c_str()); + TYPE_FIELDS(copy) = NULL; + } + + + const bool is_main_variant = TYPE_MAIN_VARIANT(t) == t; + // We already must have done the main variant... + if (!is_main_variant) + { + tree main = TYPE_MAIN_VARIANT(t); + tree main_reorg = _reorg_map[main]; + TypeStringifier stringifier; + std::string main_s = stringifier.stringify(main_reorg); + log("is modified %s main variant reorged build variant type %s\n", is_modified ? "T" : "F", main_s.c_str()); + tree copy_variant = build_variant_type_copy(main_reorg); + TYPE_NAME(copy_variant) = get_new_identifier(copy); + TYPE_SIZE(copy_variant) = NULL; + TYPE_MAIN_VARIANT(copy_variant) = main_reorg; + TYPE_SIZE(main_reorg) = NULL; + layout_type(copy_variant); + _reorg_map[t] = copy_variant; + } else { + // Ok, so now that we have fixed the TYPE_FIELDS of the copy... + // We need to call layout_type + copy = is_modified ? build_distinct_type_copy(copy) : copy; + TYPE_NAME(copy) = is_modified ? get_new_identifier(copy) : TYPE_NAME(copy); + // This is useful so that we go again through type layout + TYPE_SIZE(copy) = is_modified ? NULL : TYPE_SIZE(copy); + TYPE_MAIN_VARIANT(copy) = is_modified ? copy : TYPE_MAIN_VARIANT(copy); + tree main_variant = TYPE_MAIN_VARIANT(copy); + TypeStringifier stringifier; + std::string main_s = stringifier.stringify(main_variant); + log("main variant reorged build distinct %s\n", main_s.c_str()); + if (is_modified) layout_type(copy); + _reorg_map[t] = is_modified ? copy : (tree)t; + } + + tree record = _reorg_map[t]; + for (tree field = TYPE_FIELDS(record); field; field = DECL_CHAIN(field)) + { + relayout_decl(field); + } + +} + +void +TypeReconstructor::_walk_UNION_TYPE_pre(const_tree t) +{ +} + +void +TypeReconstructor::_walk_UNION_TYPE_post(const_tree t) +{ +} + +void +TypeReconstructor::_walk_field_pre(const_tree t) +{ + for_reference.push(t); + // We don't know if we will rewrite the field + // that we are working on. So proactively, let's make + // a copy + tree copy = copy_node((tree) t); + tree type_copy = build_variant_type_copy((tree)(TREE_TYPE(t))); + TREE_TYPE(copy) = type_copy; + // To communicate this field to the other methods, + // let's put it in the "in_progress" stack. + in_progress.push(copy); +} + +void +TypeReconstructor::_walk_field_post(const_tree t) +{ + const_tree t2 = for_reference.top(); + gcc_assert(t2 == t); + for_reference.pop(); + + // Let's get the copy we were working on. + tree copy = in_progress.top(); + // Let's put the stack in the same position... + in_progress.pop(); + + // What record does this field belongs to? + const_tree record = for_reference.top(); + + field_offsets_t field_offsets = _records[record]; + // What's the field offset? + unsigned f_byte_offset = tree_to_uhwi(DECL_FIELD_OFFSET(t)); + unsigned f_bit_offset = tree_to_uhwi(DECL_FIELD_BIT_OFFSET(t)); + unsigned f_offset = 8 * f_byte_offset + f_bit_offset; + + const bool can_field_be_deleted = field_offsets.find(f_offset) != field_offsets.end(); + if (can_field_be_deleted) mark_all_pointing_here_as_modified(); + const_tree original_type = TREE_TYPE(t); + const bool type_memoized = is_memoized(original_type); + + TREE_TYPE(copy) = type_memoized ? _reorg_map[original_type] : TREE_TYPE(copy); + + field_tuple_t tuple = std::make_pair(t, can_field_be_deleted ? NULL : copy); + + // Put the field into the vector + field_tuple_list_t &field_tuple_list = field_list_stack.top(); + field_tuple_list.push_back(tuple); + const bool already_has_field = _reorg_fields.find(t) != _reorg_fields.end(); + if (already_has_field) return; + _reorg_fields[t] = std::make_pair(copy, can_field_be_deleted); +} diff --git a/gcc/type-reconstructor.hpp b/gcc/type-reconstructor.hpp new file mode 100644 index 00000000000..4623fc88346 --- /dev/null +++ b/gcc/type-reconstructor.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include <set> +#include <map> +#include <stack> +#include <vector> + +#include "type-walker.hpp" + +class TypeReconstructor : public TypeWalker +{ +public: + typedef std::map<const_tree, tree> reorg_record_map_t; + typedef std::map<const_tree, std::pair<tree, bool>> reorg_field_map_t; + typedef std::map<const_tree, bool> is_modified_map_t; + typedef std::set<unsigned> field_offsets_t; + typedef std::map<const_tree, field_offsets_t> record_field_offset_map_t; +private: + std::stack<tree> in_progress; + std::stack<const_tree> for_reference; + typedef std::pair<const_tree, tree> field_tuple_t; + typedef std::vector<field_tuple_t> field_tuple_list_t; + typedef std::stack<field_tuple_list_t> field_tuple_list_stack_t; + record_field_offset_map_t _records; + field_tuple_list_stack_t field_list_stack; + reorg_record_map_t _reorg_map; + reorg_field_map_t _reorg_fields; + is_modified_map_t modified_map; + void set_is_not_modified_yet(const_tree); + void mark_all_pointing_here_as_modified(); + bool get_is_modified(const_tree); + virtual void _walk_field_pre(const_tree); + virtual void _walk_field_post(const_tree); + virtual void _walk_RECORD_TYPE_pre(const_tree); + virtual void _walk_RECORD_TYPE_post(const_tree); + virtual void _walk_UNION_TYPE_pre(const_tree); + virtual void _walk_UNION_TYPE_post(const_tree); + virtual void _walk_ARRAY_TYPE_pre(const_tree); + virtual void _walk_ARRAY_TYPE_post(const_tree); + virtual void _walk_POINTER_TYPE_pre(const_tree); + virtual void _walk_POINTER_TYPE_post(const_tree); +public: + virtual bool is_memoized(const_tree t); + TypeReconstructor(record_field_offset_map_t records) : _records(records) {}; + reorg_record_map_t get_map() { return _reorg_map; }; + reorg_field_map_t get_field_map() { return _reorg_fields; }; +}; diff --git a/gcc/type-stringifier.c b/gcc/type-stringifier.c new file mode 100644 index 00000000000..39e4e0ec9dc --- /dev/null +++ b/gcc/type-stringifier.c @@ -0,0 +1,246 @@ +#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 "type-stringifier.hpp" +#include "types-inlines.h" +#include <string> + +std::string +TypeStringifier::stringify(const_tree t) +{ + _stringification.clear(); + gcc_assert(t); + walk(t); + return _stringification; +} + +void +TypeStringifier::_walk_VOID_TYPE_pre(const_tree t) +{ + _stringify_simple(t); +} + +void +TypeStringifier::_walk_INTEGER_TYPE_pre(const_tree t) +{ + _stringify_simple(t); +} + +void +TypeStringifier::_walk_REAL_TYPE_pre(const_tree t) +{ + _stringify_simple(t); +} + +void +TypeStringifier::_walk_FIXED_POINT_TYPE_pre(const_tree t) +{ + _stringify_simple(t); +} + +void +TypeStringifier::_walk_COMPLEX_TYPE_pre(const_tree t) +{ + _stringify_simple(t); +} + +void +TypeStringifier::_walk_OFFSET_TYPE_pre(const_tree t) +{ + _stringify_simple(t); +} + +void +TypeStringifier::_walk_BOOLEAN_TYPE_pre(const_tree t) +{ + _stringify_simple(t); +} + +void +TypeStringifier::_stringify_simple(const_tree t) +{ + gcc_assert(t); + const enum tree_code code = TREE_CODE(t); + this->_stringification += std::string(get_tree_code_name(code)); +} + +void +TypeStringifier::_walk_POINTER_TYPE_post(__attribute__((unused))const_tree t) +{ + this->_stringification += std::string("*"); +} + +void +TypeStringifier::_walk_ARRAY_TYPE_post(__attribute__((unused))const_tree t) +{ + this->_stringification += std::string("[]"); +} + +void +TypeStringifier::_walk_REFERENCE_TYPE_post(__attribute__((unused))const_tree t) +{ + this->_stringification += std::string("&"); +} + +void +TypeStringifier::_walk_UNION_TYPE_pre(const_tree t) +{ + this->_stringification += std::string(" union "); + _stringify_aggregate_pre(t); +} + +void +TypeStringifier::_walk_UNION_TYPE_post(const_tree t) +{ + _stringify_aggregate_post(t); +} + +void +TypeStringifier::_walk_RECORD_TYPE_pre(const_tree t) +{ + this->_stringification += std::string(" record "); + _stringify_aggregate_pre(t); +} + +void +TypeStringifier::_walk_RECORD_TYPE_post(const_tree t) +{ + _stringify_aggregate_post(t); +} + +void +TypeStringifier::_stringify_aggregate_pre(const_tree t) +{ + this->_stringification += TypeStringifier::get_type_identifier(t) + std::string(" {"); +} + +void +TypeStringifier::_stringify_aggregate_post(__attribute__((unused))const_tree t) +{ + this->_stringification += std::string("}"); +} + +void +TypeStringifier::_walk_field_post(const_tree t) +{ + this->_stringification += std::string(" ") + TypeStringifier::get_field_identifier(t) + std::string(";"); +} + +void +TypeStringifier::_walk_METHOD_TYPE_pre(const_tree t) +{ + _stringify_fm_pre(t); +} + +void +TypeStringifier::_walk_METHOD_TYPE_post(const_tree t) +{ + _stringify_fm_post(t); +} + +void +TypeStringifier::_walk_FUNCTION_TYPE_pre(const_tree t) +{ + _stringify_fm_pre(t); +} + +void +TypeStringifier::_walk_FUNCTION_TYPE_post(const_tree t) +{ + _stringify_fm_post(t); +} + +void +TypeStringifier::_stringify_fm_pre(__attribute__((unused)) const_tree t) +{ + this->_stringification += std::string("function { "); +} + +void +TypeStringifier::_stringify_fm_post(__attribute__((unused))const_tree t) +{ + this->_stringification += std::string("}"); +} + +void +TypeStringifier::_walk_return_pre(__attribute__((unused)) const_tree t) +{ + this->_stringification += std::string("("); +} + +void +TypeStringifier::_walk_return_post(__attribute__((unused)) const_tree t) +{ + this->_stringification += std::string(")"); +} + +void +TypeStringifier::_walk_args_pre(__attribute__((unused)) const_tree t) +{ + this->_stringification += std::string("("); +} + +void +TypeStringifier::_walk_args_post(__attribute__((unused)) const_tree t) +{ + this->_stringification += std::string(")"); +} + +void +TypeStringifier::_walk_arg_post(__attribute__((unused)) const_tree t) +{ + this->_stringification += std::string(", "); +} + +std::string +TypeStringifier::get_type_identifier(const_tree t) +{ + tree name = TYPE_NAME(t); + const bool no_name = NULL_TREE == name; + if (no_name) return std::string(""); + + const enum tree_code name_code = TREE_CODE(name); + const bool is_name_type_decl = TYPE_DECL == name_code; + name = is_name_type_decl ? DECL_NAME(name) : name; + const char* identifier_ptr = IDENTIFIER_POINTER(name); + gcc_assert(identifier_ptr); + return std::string(identifier_ptr); +} + +std::string +TypeStringifier::get_field_identifier(const_tree t) +{ + assert_is_type(t, FIELD_DECL); + const_tree decl_name = DECL_NAME(t); + if (!decl_name) return std::string(""); + + const char* identifier = IDENTIFIER_POINTER(decl_name); + return std::string(identifier); +} diff --git a/gcc/type-stringifier.hpp b/gcc/type-stringifier.hpp new file mode 100644 index 00000000000..b2e1261fc62 --- /dev/null +++ b/gcc/type-stringifier.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include "type-walker.hpp" +#include <string> + +class TypeStringifier : public TypeWalker +{ +private: + std::string _stringification; + + + void _stringify_simple(const_tree t); + void _stringify_aggregate_pre(const_tree t); + void _stringify_aggregate_post(const_tree t); + void _stringify_fm_pre(const_tree t); + void _stringify_fm_post(const_tree t); + + virtual void _walk_VOID_TYPE_pre(const_tree t); + virtual void _walk_INTEGER_TYPE_pre(const_tree t); + virtual void _walk_REAL_TYPE_pre(const_tree t); + virtual void _walk_FIXED_POINT_TYPE_pre(const_tree t); + virtual void _walk_COMPLEX_TYPE_pre(const_tree t); + virtual void _walk_BOOLEAN_TYPE_pre(const_tree t); + virtual void _walk_OFFSET_TYPE_pre(const_tree t); + virtual void _walk_POINTER_TYPE_post(const_tree t); + virtual void _walk_REFERENCE_TYPE_post(const_tree t); + virtual void _walk_ARRAY_TYPE_post(const_tree t); + virtual void _walk_RECORD_TYPE_pre(const_tree t); + virtual void _walk_RECORD_TYPE_post(const_tree t); + virtual void _walk_UNION_TYPE_pre(const_tree t); + virtual void _walk_UNION_TYPE_post(const_tree t); + virtual void _walk_field_post(const_tree t); + virtual void _walk_return_pre(const_tree t); + virtual void _walk_return_post(const_tree t); + virtual void _walk_args_pre(const_tree t); + virtual void _walk_args_post(const_tree t); + virtual void _walk_arg_post(const_tree t); + virtual void _walk_METHOD_TYPE_pre(const_tree t); + virtual void _walk_METHOD_TYPE_post(const_tree t); + virtual void _walk_FUNCTION_TYPE_pre(const_tree t); + virtual void _walk_FUNCTION_TYPE_post(const_tree t); +public: + static std::string get_type_identifier(const_tree t); + static std::string get_field_identifier(const_tree t); + std::string stringify(const_tree t); + TypeStringifier() {}; +}; + diff --git a/gcc/type-structural-equality.c b/gcc/type-structural-equality.c new file mode 100644 index 00000000000..6195c259158 --- /dev/null +++ b/gcc/type-structural-equality.c @@ -0,0 +1,199 @@ +#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" // needed for gimple-iterator.h +#include "gimple-iterator.h" +#include "gimple-ssa.h" +#include <stdbool.h> + + +#include "types-inlines.h" +#include "type-structural-equality.hpp" +#include "type-stringifier.hpp" + +bool +TypeStructuralEquality::equal(const_tree l, const_tree r) +{ + return _equal(l, r); +} + +bool +TypeStructuralEquality::_equal(const_tree l, const_tree r) +{ + bool valid_inputs = l && r; + if (!valid_inputs) return l == r; + + bool equal_codes = _equal_code(l, r); + if (!equal_codes) return equal_codes; + + bool recurse_l = set_l.find(l) != set_l.end(); + bool recurse_r = set_r.find(r) != set_r.end(); + // TODO: Is this the case every time? + bool recurse = recurse_l || recurse_r; + if (recurse) return recurse; + + set_l.insert(l); + set_r.insert(r); + const enum tree_code code = TREE_CODE(l); + bool equal_children = false; + switch(code) + { +#define TSE_CASE(code) \ + case code: \ + equal_children = _walk_ ## code (l, r); \ + break + + TSE_CASE(VOID_TYPE); + TSE_CASE(INTEGER_TYPE); + TSE_CASE(REAL_TYPE); + TSE_CASE(FIXED_POINT_TYPE); + TSE_CASE(COMPLEX_TYPE); + TSE_CASE(ENUMERAL_TYPE); + TSE_CASE(BOOLEAN_TYPE); + TSE_CASE(OFFSET_TYPE); + TSE_CASE(RECORD_TYPE); + TSE_CASE(POINTER_TYPE); + TSE_CASE(REFERENCE_TYPE); + TSE_CASE(ARRAY_TYPE); + TSE_CASE(UNION_TYPE); + TSE_CASE(FUNCTION_TYPE); + TSE_CASE(METHOD_TYPE); + default: + gcc_unreachable(); + break; + } + + set_l.erase(l); + set_r.erase(r); + return equal_children; + +} + +bool +TypeStructuralEquality::_equal_code(const_tree l, const_tree r) +{ + const enum tree_code code_l = TREE_CODE(l); + const enum tree_code code_r = TREE_CODE(r); + const bool equal = code_l == code_r; + return equal; +} + +#define TSE_FUNC_DEF_SIMPLE(code) \ +bool \ +TypeStructuralEquality::_walk_ ## code (const_tree l, const_tree r) \ +{ \ + return _equal_code(l, r); \ +} + +TSE_FUNC_DEF_SIMPLE(VOID_TYPE) +TSE_FUNC_DEF_SIMPLE(INTEGER_TYPE) +TSE_FUNC_DEF_SIMPLE(REAL_TYPE) +TSE_FUNC_DEF_SIMPLE(FIXED_POINT_TYPE) +TSE_FUNC_DEF_SIMPLE(ENUMERAL_TYPE) +TSE_FUNC_DEF_SIMPLE(BOOLEAN_TYPE) +TSE_FUNC_DEF_SIMPLE(OFFSET_TYPE) +TSE_FUNC_DEF_SIMPLE(COMPLEX_TYPE) + +bool +TypeStructuralEquality::_equal_wrapper(const_tree l, const_tree r) +{ + const_tree inner_l = TREE_TYPE(l); + const_tree inner_r = TREE_TYPE(r); + return _equal(inner_l, inner_r); +} + +#define TSE_FUNC_DEF_WRAPPER(code) \ +bool \ +TypeStructuralEquality::_walk_ ## code (const_tree l, const_tree r) \ +{ \ + return _equal_wrapper(l, r); \ +} + +TSE_FUNC_DEF_WRAPPER(REFERENCE_TYPE) +TSE_FUNC_DEF_WRAPPER(ARRAY_TYPE) +TSE_FUNC_DEF_WRAPPER(POINTER_TYPE) + +#define TSE_FUNC_DEF_CONTAINER(code) \ +bool \ +TypeStructuralEquality::_walk_ ## code (const_tree l, const_tree r) \ +{ \ + const_tree field_l = TYPE_FIELDS(l); \ + const_tree field_r = TYPE_FIELDS(r); \ + bool efield_l = field_l; \ + bool efield_r = field_r; \ + bool still_equal = efield_l == efield_r; \ + if (!still_equal) return still_equal; \ + \ + while (field_l && field_r && still_equal) \ + { \ + const_tree tfield_l = TREE_TYPE(field_l); \ + const_tree tfield_r = TREE_TYPE(field_r); \ + still_equal &= _equal(tfield_l, tfield_r); \ + field_l = DECL_CHAIN(field_l); \ + field_r = DECL_CHAIN(field_r); \ + efield_l = field_l; \ + efield_r = field_r; \ + still_equal &= efield_l == efield_r; \ + } \ + return still_equal; \ +} + +TSE_FUNC_DEF_CONTAINER(RECORD_TYPE) +TSE_FUNC_DEF_CONTAINER(UNION_TYPE) + +#define TSE_FUNC_DEF_FUNC(code) \ +bool \ +TypeStructuralEquality::_walk_ ## code (const_tree l, const_tree r) \ +{ \ + const_tree tret_l = TREE_TYPE(l); \ + const_tree tret_r = TREE_TYPE(r); \ + bool still_equal = _equal(tret_l, tret_r); \ + if (!still_equal) return still_equal; \ + \ + const_tree arg_l = TYPE_ARG_TYPES(l); \ + const_tree arg_r = TYPE_ARG_TYPES(r); \ + bool earg_l = arg_l; \ + bool earg_r = arg_r; \ + still_equal &= earg_l == earg_r; \ + while (arg_l && arg_r && still_equal) \ + { \ + const_tree targ_l = TREE_VALUE(arg_l); \ + const_tree targ_r = TREE_VALUE(arg_r); \ + still_equal &= _equal(targ_l, targ_r); \ + arg_l = TREE_CHAIN(arg_l); \ + arg_r = TREE_CHAIN(arg_r); \ + earg_l = arg_l; \ + earg_r = arg_r; \ + still_equal &= earg_l == earg_r; \ + } \ + return still_equal; \ +} + +TSE_FUNC_DEF_FUNC(FUNCTION_TYPE) +TSE_FUNC_DEF_FUNC(METHOD_TYPE) + + + diff --git a/gcc/type-structural-equality.hpp b/gcc/type-structural-equality.hpp new file mode 100644 index 00000000000..b5f1944bd31 --- /dev/null +++ b/gcc/type-structural-equality.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include <set> + +class TypeStructuralEquality { +public: + TypeStructuralEquality() {}; + bool equal(const_tree a, const_tree b); +protected: + virtual bool _equal(const_tree a, const_tree b); +private: + typedef std::set<const_tree> tset_t; + + tset_t set_l; + tset_t set_r; + bool _equal_code(const_tree a, const_tree b); + bool _equal_wrapper(const_tree a, const_tree b); + +#define TSE_FUNC_DECL(code) \ + virtual bool _walk_ ## code (const_tree l, const_tree r) + TSE_FUNC_DECL(VOID_TYPE); + TSE_FUNC_DECL(COMPLEX_TYPE); + TSE_FUNC_DECL(INTEGER_TYPE); + TSE_FUNC_DECL(REAL_TYPE); + TSE_FUNC_DECL(FIXED_POINT_TYPE); + TSE_FUNC_DECL(POINTER_TYPE); + TSE_FUNC_DECL(ENUMERAL_TYPE); + TSE_FUNC_DECL(BOOLEAN_TYPE); + TSE_FUNC_DECL(OFFSET_TYPE); + TSE_FUNC_DECL(RECORD_TYPE); + TSE_FUNC_DECL(REFERENCE_TYPE); + TSE_FUNC_DECL(ARRAY_TYPE); + TSE_FUNC_DECL(UNION_TYPE); + TSE_FUNC_DECL(FUNCTION_TYPE); + TSE_FUNC_DECL(METHOD_TYPE); + + const_tree left; +}; diff --git a/gcc/type-structural-main-variant.c b/gcc/type-structural-main-variant.c new file mode 100644 index 00000000000..9cc95d5ec63 --- /dev/null +++ b/gcc/type-structural-main-variant.c @@ -0,0 +1,49 @@ +#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" // needed for gimple-iterator.h +#include "gimple-iterator.h" +#include "gimple-ssa.h" +#include <stdbool.h> + + +#include "types-inlines.h" +#include "type-structural-equality.hpp" +#include "type-structural-main-variant.hpp" + +bool +TypeStructuralEqualityMainVariant::_equal(const_tree l, const_tree r) +{ + bool valid_inputs = l && r; + if (!valid_inputs) return l == r; + + const_tree mv_l = TYPE_MAIN_VARIANT(l); + const_tree mv_r = TYPE_MAIN_VARIANT(r); + const bool mv_equal = mv_l == mv_r; + if (mv_equal) return mv_equal; + + return TypeStructuralEquality::_equal(l, r); +} diff --git a/gcc/type-structural-main-variant.hpp b/gcc/type-structural-main-variant.hpp new file mode 100644 index 00000000000..2fa00aad350 --- /dev/null +++ b/gcc/type-structural-main-variant.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include "type-structural-equality.hpp" + +class TypeStructuralEqualityMainVariant : public TypeStructuralEquality { +public: + TypeStructuralEqualityMainVariant() {}; +protected: + virtual bool _equal(const_tree l, const_tree r); +}; diff --git a/gcc/type-walker.c b/gcc/type-walker.c new file mode 100644 index 00000000000..d7554c9e6af --- /dev/null +++ b/gcc/type-walker.c @@ -0,0 +1,282 @@ +#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 "type-walker.hpp" +#include "types-inlines.h" + +void +TypeWalker::walk(const_tree t) +{ + gcc_assert(t); + this->tset.clear(); + this->_walk(t); +} + +void +TypeWalker::_walk(const_tree type) +{ + if (!type) return; + gcc_assert(type); + + // This is an optimization + const bool _is_memoized = is_memoized(type); + if (_is_memoized) return; + + // This is for correctness... + const bool in_set = tset.find(type) != tset.end(); + if (in_set) return; + + tset.insert(type); + const enum tree_code code = TREE_CODE(type); + switch (code) + { + case VOID_TYPE: + this->walk_VOID_TYPE(type); + break; + case INTEGER_TYPE: + this->walk_INTEGER_TYPE(type); + break; + case REAL_TYPE: + this->walk_REAL_TYPE(type); + break; + case FIXED_POINT_TYPE: + this->walk_FIXED_POINT_TYPE(type); + break; + case COMPLEX_TYPE: + this->walk_COMPLEX_TYPE(type); + break; + case ENUMERAL_TYPE: + this->walk_ENUMERAL_TYPE(type); + break; + case BOOLEAN_TYPE: + this->walk_BOOLEAN_TYPE(type); + break; + case OFFSET_TYPE: + this->walk_OFFSET_TYPE(type); + break; + case RECORD_TYPE: + this->walk_RECORD_TYPE(type); + break; + case POINTER_TYPE: + this->walk_POINTER_TYPE(type); + break; + case REFERENCE_TYPE: + this->walk_REFERENCE_TYPE(type); + break; + case ARRAY_TYPE: + this->walk_ARRAY_TYPE(type); + break; + case UNION_TYPE: + this->walk_UNION_TYPE(type); + break; + case FUNCTION_TYPE: + this->walk_FUNCTION_TYPE(type); + break; + case METHOD_TYPE: + this->walk_METHOD_TYPE(type); + break; + case QUAL_UNION_TYPE: + case LANG_TYPE: + default: + { + log("missing %s\n", get_tree_code_name(code)); + gcc_unreachable(); + } + break; + } + + tset.erase(type); +} + + +#define TypeWalkerFuncDef(code) \ +void \ +TypeWalker::walk_ ## code (const_tree t) \ +{ \ + assert_is_type(t, code); \ + _walk_ ## code ## _pre(t); \ + _walk_ ## code (t); \ + _walk_ ## code ## _post(t); \ +} + +#define TypeWalkerFuncDefInternal(code) \ +void TypeWalker::_walk_ ## code (__attribute__((unused)) const_tree t) {} + +TypeWalkerFuncDef(VOID_TYPE) +TypeWalkerFuncDefInternal(VOID_TYPE) +TypeWalkerFuncDef(INTEGER_TYPE) +TypeWalkerFuncDefInternal(INTEGER_TYPE) +TypeWalkerFuncDef(REAL_TYPE) +TypeWalkerFuncDefInternal(REAL_TYPE) +TypeWalkerFuncDef(BOOLEAN_TYPE) +TypeWalkerFuncDefInternal(BOOLEAN_TYPE) +TypeWalkerFuncDef(OFFSET_TYPE) +TypeWalkerFuncDefInternal(OFFSET_TYPE) +TypeWalkerFuncDef(FIXED_POINT_TYPE) +TypeWalkerFuncDefInternal(FIXED_POINT_TYPE) +TypeWalkerFuncDef(COMPLEX_TYPE) +TypeWalkerFuncDefInternal(COMPLEX_TYPE) +TypeWalkerFuncDef(ENUMERAL_TYPE) +TypeWalkerFuncDefInternal(ENUMERAL_TYPE) + +void +TypeWalker::_walk_wrapper(const_tree t) +{ + const_tree inner_type = TREE_TYPE(t); + gcc_assert(inner_type); + _walk(inner_type); +} + +#define TypeWalkerFuncDefWrapper(code) \ +void \ +TypeWalker::_walk_ ## code (const_tree t) \ +{ \ + _walk_wrapper(t); \ +} + +TypeWalkerFuncDef(POINTER_TYPE) +TypeWalkerFuncDefWrapper(POINTER_TYPE) +TypeWalkerFuncDefWrapper(REFERENCE_TYPE) +TypeWalkerFuncDef(REFERENCE_TYPE) +TypeWalkerFuncDef(ARRAY_TYPE) +TypeWalkerFuncDefWrapper(ARRAY_TYPE) + +TypeWalkerFuncDef(RECORD_TYPE) + +void +TypeWalker::_walk_RECORD_TYPE(const_tree t) +{ + _walk_record_or_union(t); +} + +TypeWalkerFuncDef(UNION_TYPE) + +void +TypeWalker::_walk_UNION_TYPE(const_tree t) +{ + _walk_record_or_union(t); +} + +void +TypeWalker::_walk_record_or_union(const_tree t) +{ + for (tree field = TYPE_FIELDS(t); field; field = DECL_CHAIN(field)) + { + gcc_assert(field); + walk_field(field); + } +} + +void +TypeWalker::walk_field(const_tree t) +{ + _walk_field_pre(t); + _walk_field(t); + _walk_field_post(t); +} + +void +TypeWalker::_walk_field(const_tree t) +{ + const_tree inner_type = TREE_TYPE(t); + gcc_assert(inner_type); + _walk(inner_type); +} + +TypeWalkerFuncDef(FUNCTION_TYPE) + +void +TypeWalker::_walk_FUNCTION_TYPE(const_tree t) +{ + _walk_function_or_method(t); +} + +TypeWalkerFuncDef(METHOD_TYPE) + +void +TypeWalker::_walk_METHOD_TYPE(const_tree t) +{ + _walk_function_or_method(t); +} + +void +TypeWalker::_walk_function_or_method(const_tree t) +{ + const_tree ret_type = TREE_TYPE(t); + walk_return(ret_type); + walk_args(t); +} + +void +TypeWalker::walk_return(const_tree t) +{ + _walk_return_pre(t); + _walk_return(t); + _walk_return_post(t); +} + +void +TypeWalker::_walk_return(const_tree t) +{ + _walk(t); +} + +void +TypeWalker::walk_args(const_tree t) +{ + _walk_args_pre(t); + _walk_args(t); + _walk_args_post(t); +} + +void +TypeWalker::_walk_args(const_tree t) +{ + for (tree arg_node = TYPE_ARG_TYPES(t); NULL_TREE != arg_node; arg_node = TREE_CHAIN(arg_node)) + { + const_tree arg_node_type = TREE_VALUE(arg_node); + gcc_assert(arg_node_type); + walk_arg(arg_node_type); + } +} + +void +TypeWalker::walk_arg(const_tree t) +{ + _walk_arg_pre(t); + _walk_arg(t); + _walk_arg_post(t); +} + +void +TypeWalker::_walk_arg(const_tree t) +{ + _walk(t); +} diff --git a/gcc/type-walker.hpp b/gcc/type-walker.hpp new file mode 100644 index 00000000000..60c979481ee --- /dev/null +++ b/gcc/type-walker.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include <set> + +class TypeWalker { +public: + TypeWalker() {}; + void walk(const_tree t); + +protected: + typedef std::set<const_tree> tset_t; + +protected: + tset_t tset; + + void _walk(const_tree t); + void _walk_wrapper(const_tree t); + void _walk_record_or_union(const_tree t); + virtual void _walk_function_or_method(const_tree t); + virtual bool is_memoized(__attribute__((unused))const_tree t) { return false; }; + +#define TypeWalkerFuncDecl(code) \ + virtual void _walk_ ## code ## _pre(const_tree t) {}; \ + virtual void walk_ ## code (const_tree t); \ + virtual void _walk_ ## code (const_tree t); \ + virtual void _walk_ ## code ## _post(const_tree t) {} + + TypeWalkerFuncDecl(VOID_TYPE); + TypeWalkerFuncDecl(INTEGER_TYPE); + TypeWalkerFuncDecl(REAL_TYPE); + TypeWalkerFuncDecl(FIXED_POINT_TYPE); + TypeWalkerFuncDecl(COMPLEX_TYPE); + TypeWalkerFuncDecl(ENUMERAL_TYPE); + TypeWalkerFuncDecl(BOOLEAN_TYPE); + TypeWalkerFuncDecl(OFFSET_TYPE); + TypeWalkerFuncDecl(RECORD_TYPE); + TypeWalkerFuncDecl(POINTER_TYPE); + TypeWalkerFuncDecl(REFERENCE_TYPE); + TypeWalkerFuncDecl(ARRAY_TYPE); + TypeWalkerFuncDecl(UNION_TYPE); + TypeWalkerFuncDecl(FUNCTION_TYPE); + TypeWalkerFuncDecl(METHOD_TYPE); + + // These are not types... + TypeWalkerFuncDecl(field); + TypeWalkerFuncDecl(return); + TypeWalkerFuncDecl(args); + TypeWalkerFuncDecl(arg); +}; + diff --git a/gcc/types-inlines.h b/gcc/types-inlines.h new file mode 100644 index 00000000000..60666c3ce26 --- /dev/null +++ b/gcc/types-inlines.h @@ -0,0 +1,104 @@ +#pragma once + +#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" // needed for gimple-iterator.h +#include "gimple-iterator.h" +#include "gimple-ssa.h" +#include <stdbool.h> +#include <stdio.h> + + +inline void +log(const char* const fmt, ...) +{ + if (!dump_file) return; + + va_list args; + va_start(args, fmt); + vfprintf(dump_file, fmt, args); + fflush(dump_file); + va_end(args); +} + +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 +assert_is_complete(const_tree a) +{ + gcc_assert(a); + const_tree type_size = TYPE_SIZE(a); + gcc_assert(NULL_TREE != type_size); +} + +inline bool +is_complete(const_tree a) +{ + gcc_assert(a); + const_tree type_size = TYPE_SIZE(a); + const bool _is_complete = NULL_TREE != type_size; + return _is_complete; +} + +inline bool +is_incomplete(const_tree a) +{ + return !is_complete(a); +} + +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); +} + +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); +} |