From fc52b8f955094c11dd32ab2ad81206c7f1f1befe Mon Sep 17 00:00:00 2001 From: eastb233 Date: Wed, 19 Jul 2023 10:28:04 +0800 Subject: [PATCH 1/7] Structure reorganization optimization Reference: https://gcc.gnu.org/git/?p=gcc-old.git;a=commit;h=6e1bd1c900533c627b5e4fbbecb41dcd7974b522 Introduce structure reorganization optimization, that change C-like structures layout in order to better utilize spatial locality. This transformation is affective for programs containing arrays of structures. --- gcc/Makefile.in | 1 + gcc/common.opt | 4 +- gcc/configure | 2 +- gcc/configure.ac | 2 +- gcc/doc/invoke.texi | 23 + gcc/gimple-ssa-warn-access.cc | 8 + gcc/ipa-param-manipulation.cc | 3 +- gcc/ipa-param-manipulation.h | 3 +- gcc/ipa-struct-reorg/escapes.def | 60 + gcc/ipa-struct-reorg/ipa-struct-reorg.cc | 4015 +++++++++++++++++ gcc/ipa-struct-reorg/ipa-struct-reorg.h | 235 + gcc/params.opt | 4 + gcc/passes.def | 2 + gcc/testsuite/gcc.dg/struct/struct-reorg.exp | 35 + gcc/testsuite/gcc.dg/struct/struct_reorg-1.c | 24 + gcc/testsuite/gcc.dg/struct/struct_reorg-2.c | 29 + gcc/testsuite/gcc.dg/struct/struct_reorg-3.c | 23 + gcc/testsuite/gcc.dg/struct/struct_reorg-4.c | 59 + .../gcc.dg/struct/w_prof_global_array.c | 29 + .../gcc.dg/struct/w_prof_global_var.c | 42 + .../gcc.dg/struct/w_prof_local_array.c | 37 + .../gcc.dg/struct/w_prof_local_var.c | 40 + .../gcc.dg/struct/w_prof_single_str_global.c | 31 + gcc/testsuite/gcc.dg/struct/w_prof_two_strs.c | 64 + .../gcc.dg/struct/w_ratio_cold_str.c | 43 + .../gcc.dg/struct/wo_prof_array_field.c | 26 + .../struct/wo_prof_array_through_pointer.c | 38 + .../gcc.dg/struct/wo_prof_double_malloc.c | 29 + .../gcc.dg/struct/wo_prof_empty_str.c | 44 + .../struct/wo_prof_escape_arg_to_local.c | 44 + .../gcc.dg/struct/wo_prof_escape_return-1.c | 33 + .../gcc.dg/struct/wo_prof_escape_return.c | 32 + .../gcc.dg/struct/wo_prof_escape_str_init.c | 31 + .../struct/wo_prof_escape_substr_array.c | 33 + .../struct/wo_prof_escape_substr_pointer.c | 48 + .../struct/wo_prof_escape_substr_value.c | 45 + .../gcc.dg/struct/wo_prof_global_array.c | 32 + .../gcc.dg/struct/wo_prof_global_var.c | 45 + .../gcc.dg/struct/wo_prof_local_array.c | 40 + .../gcc.dg/struct/wo_prof_local_var.c | 43 + .../gcc.dg/struct/wo_prof_malloc_size_var-1.c | 47 + .../gcc.dg/struct/wo_prof_malloc_size_var.c | 47 + .../struct/wo_prof_mult_field_peeling.c | 42 + .../gcc.dg/struct/wo_prof_single_str_global.c | 34 + .../gcc.dg/struct/wo_prof_single_str_local.c | 34 + .../struct/wo_prof_single_str_pointer.c | 38 + .../gcc.dg/struct/wo_prof_two_strs.c | 67 + gcc/timevar.def | 1 + gcc/tree-pass.h | 1 + 49 files changed, 5686 insertions(+), 6 deletions(-) create mode 100644 gcc/ipa-struct-reorg/escapes.def create mode 100644 gcc/ipa-struct-reorg/ipa-struct-reorg.cc create mode 100644 gcc/ipa-struct-reorg/ipa-struct-reorg.h create mode 100644 gcc/testsuite/gcc.dg/struct/struct-reorg.exp create mode 100644 gcc/testsuite/gcc.dg/struct/struct_reorg-1.c create mode 100644 gcc/testsuite/gcc.dg/struct/struct_reorg-2.c create mode 100644 gcc/testsuite/gcc.dg/struct/struct_reorg-3.c create mode 100644 gcc/testsuite/gcc.dg/struct/struct_reorg-4.c create mode 100644 gcc/testsuite/gcc.dg/struct/w_prof_global_array.c create mode 100644 gcc/testsuite/gcc.dg/struct/w_prof_global_var.c create mode 100644 gcc/testsuite/gcc.dg/struct/w_prof_local_array.c create mode 100644 gcc/testsuite/gcc.dg/struct/w_prof_local_var.c create mode 100644 gcc/testsuite/gcc.dg/struct/w_prof_single_str_global.c create mode 100644 gcc/testsuite/gcc.dg/struct/w_prof_two_strs.c create mode 100644 gcc/testsuite/gcc.dg/struct/w_ratio_cold_str.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_array_field.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_array_through_pointer.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_double_malloc.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_empty_str.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_escape_arg_to_local.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_escape_return-1.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_escape_return.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_escape_str_init.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_escape_substr_array.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_escape_substr_pointer.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_escape_substr_value.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_global_array.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_global_var.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_local_array.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_local_var.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_malloc_size_var-1.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_malloc_size_var.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_mult_field_peeling.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_single_str_global.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_single_str_local.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_single_str_pointer.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_two_strs.c diff --git a/gcc/Makefile.in b/gcc/Makefile.in index db2a0e1bda8..d304334709e 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1450,6 +1450,7 @@ OBJS = \ incpath.o \ init-regs.o \ internal-fn.o \ + ipa-struct-reorg/ipa-struct-reorg.o \ ipa-cp.o \ ipa-sra.o \ ipa-devirt.o \ diff --git a/gcc/common.opt b/gcc/common.opt index e365a48bca6..b48fa322806 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1950,8 +1950,8 @@ Common Ignore Does nothing. Preserved for backward compatibility. fipa-struct-reorg -Common Ignore -Does nothing. Preserved for backward compatibility. +Common Var(flag_ipa_struct_reorg) Init(0) Optimization +Perform structure layout optimizations. fipa-vrp Common Var(flag_ipa_vrp) Optimization diff --git a/gcc/configure b/gcc/configure index c749ace011d..98bbf0f857b 100755 --- a/gcc/configure +++ b/gcc/configure @@ -34191,7 +34191,7 @@ $as_echo "$as_me: executing $ac_file commands" >&6;} "depdir":C) $SHELL $ac_aux_dir/mkinstalldirs $DEPDIR ;; "gccdepdir":C) ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs build/$DEPDIR - for lang in $subdirs c-family common analyzer rtl-ssa + for lang in $subdirs c-family common analyzer rtl-ssa ipa-struct-reorg do ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs $lang/$DEPDIR done ;; diff --git a/gcc/configure.ac b/gcc/configure.ac index 992a50e7b20..c74f4b55527 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -1340,7 +1340,7 @@ AC_CHECK_HEADERS(ext/hash_map) ZW_CREATE_DEPDIR AC_CONFIG_COMMANDS([gccdepdir],[ ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs build/$DEPDIR - for lang in $subdirs c-family common analyzer rtl-ssa + for lang in $subdirs c-family common analyzer rtl-ssa ipa-struct-reorg do ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs $lang/$DEPDIR done], [subdirs="$subdirs" ac_aux_dir=$ac_aux_dir DEPDIR=$DEPDIR]) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index ff8cd032fab..e37bae5b17f 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -526,6 +526,7 @@ Objective-C and Objective-C++ Dialects}. -finline-functions -finline-functions-called-once -finline-limit=@var{n} @gol -finline-small-functions -fipa-modref -fipa-cp -fipa-cp-clone @gol -fipa-bit-cp -fipa-vrp -fipa-pta -fipa-profile -fipa-pure-const @gol +-fipa-struct-reorg @gol -fipa-reference -fipa-reference-addressable @gol -fipa-stack-alignment -fipa-icf -fira-algorithm=@var{algorithm} @gol -flive-patching=@var{level} @gol @@ -11886,6 +11887,19 @@ higher. Discover which functions are pure or constant. Enabled by default at @option{-O1} and higher. +@item -fipa-struct-reorg +@opindex fipa-struct-reorg +Perform structure reorganization optimization, that change C-like structures +layout in order to better utilize spatial locality. This transformation is +affective for programs containing arrays of structures. Available in two +compilation modes: profile-based (enabled with @option{-fprofile-generate}) +or static (which uses built-in heuristics). It works only in whole program +mode, so it requires @option{-fwhole-program} to be +enabled. Structures considered @samp{cold} by this transformation are not +affected (see @option{--param struct-reorg-cold-struct-ratio=@var{value}}). + +With this flag, the program debug info reflects a new structure layout. + @item -fipa-reference @opindex fipa-reference Discover which static variables do not escape the @@ -13772,6 +13786,15 @@ In each case, the @var{value} is an integer. The following choices of @var{name} are recognized for all targets: @table @gcctabopt +@item struct-reorg-cold-struct-ratio +The threshold ratio (as a percentage) between a structure frequency +and the frequency of the hottest structure in the program. This parameter +is used by struct-reorg optimization enabled by @option{-fipa-struct-reorg}. +We say that if the ratio of a structure frequency, calculated by profiling, +to the hottest structure frequency in the program is less than this +parameter, then structure reorganization is not applied to this structure. +The default is 10. + @item predictable-branch-outcome When branch is predicted to be taken with probability lower than this threshold (in percent), then it is considered well predictable. diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc index 8d088ad33f2..a24645783be 100644 --- a/gcc/gimple-ssa-warn-access.cc +++ b/gcc/gimple-ssa-warn-access.cc @@ -2193,6 +2193,14 @@ pass_waccess::set_pass_param (unsigned int n, bool early) bool pass_waccess::gate (function *) { + /* FIXME: In structure optimizations, some statements will be + rewritten and removed from the BB, leaving some unused SSA. + In pass waccess, it will traverse all SSA and cause ICE + when handling these unused SSA. So temporarily disable + pass waccess when enable structure optimizations. */ + if (flag_ipa_struct_reorg) + return false; + return (warn_free_nonheap_object || warn_mismatched_alloc || warn_mismatched_new_delete); diff --git a/gcc/ipa-param-manipulation.cc b/gcc/ipa-param-manipulation.cc index 38328c3e8d0..f9e956008d8 100644 --- a/gcc/ipa-param-manipulation.cc +++ b/gcc/ipa-param-manipulation.cc @@ -55,7 +55,8 @@ static const char *ipa_param_prefixes[IPA_PARAM_PREFIX_COUNT] = {"SYNTH", "ISRA", "simd", - "mask"}; + "mask", + "struct_reorg"}; /* Names of parameters for dumping. Keep in sync with enum ipa_parm_op. */ diff --git a/gcc/ipa-param-manipulation.h b/gcc/ipa-param-manipulation.h index a9ad2b216be..71f4a0a2f08 100644 --- a/gcc/ipa-param-manipulation.h +++ b/gcc/ipa-param-manipulation.h @@ -126,6 +126,7 @@ enum ipa_param_name_prefix_indices IPA_PARAM_PREFIX_ISRA, IPA_PARAM_PREFIX_SIMD, IPA_PARAM_PREFIX_MASK, + IPA_PARAM_PREFIX_REORG, IPA_PARAM_PREFIX_COUNT }; @@ -189,7 +190,7 @@ struct GTY(()) ipa_adjusted_param /* Index into ipa_param_prefixes specifying a prefix to be used with DECL_NAMEs of newly synthesized parameters. */ - unsigned param_prefix_index : 2; + unsigned param_prefix_index : 3; /* Storage order of the original parameter (for the cases when the new parameter is a component of an original one). */ diff --git a/gcc/ipa-struct-reorg/escapes.def b/gcc/ipa-struct-reorg/escapes.def new file mode 100644 index 00000000000..c4c8e07392d --- /dev/null +++ b/gcc/ipa-struct-reorg/escapes.def @@ -0,0 +1,60 @@ +/* Copyright (C) 2016-2023 Free Software Foundation, Inc. + +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 +. */ + +/* Before including this file, you should define a macro: + DEF_ESCAPE (ENUM, TEXT) + + This macro will be called once for each escape reason. The + ENUM will be of type "escape_type". The TEXT is describing + the reason for the escape. +*/ +DEF_ESCAPE (escape_marked_as_used, "Type used in variable marked as used") +DEF_ESCAPE (escape_via_global_var, "Type used via a external visible variable") +DEF_ESCAPE (escape_via_global_init, "Type used via a global init of a variable") +DEF_ESCAPE (escape_non_supported_allocator, "Type used by allocation which is not currently supported") +DEF_ESCAPE (escape_dependent_type_escapes, "Type uses a type which escapes or is used by a type which escapes") +DEF_ESCAPE (escape_var_arg_function, "Types escapes via a variable argument function") +DEF_ESCAPE (escape_bitfields, "Types has bitfields") +DEF_ESCAPE (escape_recusive_type, "Type has a recusive relationship") +DEF_ESCAPE (escape_variable_sized_array, "Type has a variable sized type") +DEF_ESCAPE (escape_external_function, "Type escapes via an external function call") +DEF_ESCAPE (escape_visible_function, "Type escapes via expternally visible function call") +DEF_ESCAPE (escape_pointer_function, "Type escapes via an function pointer call") +DEF_ESCAPE (escape_unkown_field, "Type escapes via an unkown field accessed") +DEF_ESCAPE (escape_union, "Type escapes via an union") +DEF_ESCAPE (escape_inline_asm, "Type escapes via inline-asm") +DEF_ESCAPE (escape_non_multiply_size, "Type escapes a pointer plus which is not a multiplicate of the size") +DEF_ESCAPE (escape_cast_void, "Type escapes a cast to/from void*") +DEF_ESCAPE (escape_cast_another_ptr, "Type escapes a cast to a different pointer") +DEF_ESCAPE (escape_cast_int, "Type escapes a cast from/to intergral type") +DEF_ESCAPE (escape_int_const, "Type escapes via integer constant") +DEF_ESCAPE (escape_vce, "Type escapes via a VIEW_CONVERT_EXPR") +DEF_ESCAPE (escape_array_access, "Type escapes via an array access") +DEF_ESCAPE (escape_noclonable_function, "Type escapes via a non-clonable function") +DEF_ESCAPE (escape_rescusive_type, "Recusive type") +DEF_ESCAPE (escape_user_alignment, "Type has an user alignment set") +DEF_ESCAPE (escape_volatile, "Type has an variable which is volatile") +DEF_ESCAPE (escape_non_eq, "Type has a comparison other than equals or not equals") +DEF_ESCAPE (escape_addr, "Type escapes via taking the address of field") +DEF_ESCAPE (escape_cannot_change_signature, "Type used in a call that cannot change signature") +DEF_ESCAPE (escape_non_optimize, "Type used by a function which turns off struct reorg") +DEF_ESCAPE (escape_array, "Type is used in an array [not handled yet]") +DEF_ESCAPE (escape_ptr_ptr, "Type is used in a pointer to a pointer [not handled yet]") +DEF_ESCAPE (escape_return, "Type escapes via a return [not handled yet]") + +#undef DEF_ESCAPE diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc new file mode 100644 index 00000000000..6a1b4db2b9e --- /dev/null +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc @@ -0,0 +1,4015 @@ +/* Struct-reorg optimizations. + Copyright (C) 2016-2023 Free Software Foundation, Inc. + Contributed by Andrew Pinski + +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 +. */ + +/* This pass implements the structure reorganization organization + (struct-reorg). + + Right now it handles just splitting off the hottest fields for a struct + of 2 fields: + struct s { + type1 field1; // Hot field + type2 field2; + }; + s *v; + into: + struct s_hot { + type1 field1; + }; + struct c_cold { + type2 field2; + }; + s_hot *v_hot; + s_cold *v_cold; + + TODO: This pass can be extended to more fields, and other alogrothims + like reordering. + + This pass operate in four stages: + 1. All of the field accesses, declarations (struct types and pointers + to that type) and struct types are scanned and recorded. This includes + global declarations. Also record all allocation and freeing sites; + this is needed for the rewriting phase. + + FIXME: If there is a top-level inline-asm, the pass immediately returns. + + 2. Prune out the types which are considered escaping. + Examples of types which are considered escaping: + a. A declaration has been marked as having the attribute used or + has user defined alignment (type too). + b. Accesses are via a BIT_FIELD_REF. + FIXME: Handle VECTOR_TYPE for this case. + c. The "allocation" site is not a known builtin function. + d. Casting to/from an integer. + + 3. Analyze the types for which optimization to do. + a. Split the fields into two different structs. + (FIXME: two field case handled only) + Look at all structs which contain two fields, if one of the fields + is hotter then split it and put it on the rewritting for accesses. + Allocations and freeing are marked to split into two functions; + all uses of that type will now be considered as two. + b. Reorder fields hottest to the coldest. TODO: Implement. + + 4. Rewrite each access and allocation and free whichis marked as + rewriting. + +*/ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "tree-pass.h" +#include "cgraph.h" +#include "diagnostic-core.h" +#include "function.h" +#include "basic-block.h" +#include "gimple.h" +#include "vec.h" +#include "tree-pretty-print.h" +#include "gimple-pretty-print.h" +#include "gimple-iterator.h" +#include "cfg.h" +#include "ssa.h" +#include "tree-dfa.h" +#include "fold-const.h" +#include "tree-inline.h" +#include "stor-layout.h" +#include "tree-into-ssa.h" +#include "tree-cfg.h" +#include "alloc-pool.h" +#include "symbol-summary.h" +#include "ipa-prop.h" +#include "ipa-struct-reorg.h" +#include "tree-eh.h" +#include "bitmap.h" +#include "tree-ssa-live.h" /* For remove_unused_locals. */ +#include "ipa-param-manipulation.h" +#include "gimplify-me.h" + +namespace { + +using namespace struct_reorg; + +#define VOID_POINTER_P(type) \ + (POINTER_TYPE_P (type) && VOID_TYPE_P (TREE_TYPE (type))) + +/* Return true iff TYPE is stdarg va_list type. */ + +inline bool +is_va_list_type (tree type) +{ + return TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (va_list_type_node); +} + +const char * +get_type_name (tree type) +{ + const char *tname = NULL; + + if (type == NULL) + return NULL; + + if (TYPE_NAME (type) != NULL) + { + if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) + tname = IDENTIFIER_POINTER (TYPE_NAME (type)); + else if (DECL_NAME (TYPE_NAME (type)) != NULL) + tname = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); + } + return tname; +} + +/* Return the inner most type for arrays and pointers of TYPE. */ + +tree +inner_type (tree type) +{ + while (POINTER_TYPE_P (type) + || TREE_CODE (type) == ARRAY_TYPE) + type = TREE_TYPE (type); + return type; +} + +/* Return true if TYPE is a type which struct reorg should handled. */ + +bool +handled_type (tree type) +{ + type = inner_type (type); + if (TREE_CODE (type) == RECORD_TYPE) + return !is_va_list_type (type); + return false; +} + +/* The gimplify_buildN API is moved to tree-vect-generic.c locally + at commit b972e036f40c12b106f9070c3e8adea0eb8a45fa. + + The gimplify_buildN API is copied from gcc 10 implementation. +*/ + +/* Build a binary operation and gimplify it. Emit code before GSI. + Return the gimple_val holding the result. */ + +tree +gimplify_build2 (gimple_stmt_iterator *gsi, enum tree_code code, + tree type, tree a, tree b) +{ + tree ret; + + ret = fold_build2_loc (gimple_location (gsi_stmt (*gsi)), code, type, a, b); + return force_gimple_operand_gsi (gsi, ret, true, NULL, true, + GSI_SAME_STMT); +} + +/* Build a unary operation and gimplify it. Emit code before GSI. + Return the gimple_val holding the result. */ + +tree +gimplify_build1 (gimple_stmt_iterator *gsi, enum tree_code code, tree type, + tree a) +{ + tree ret; + + ret = fold_build1_loc (gimple_location (gsi_stmt (*gsi)), code, type, a); + return force_gimple_operand_gsi (gsi, ret, true, NULL, true, + GSI_SAME_STMT); +} + +} // anon namespace + + +namespace struct_reorg { + +/* Constructor of srfunction. */ + +srfunction::srfunction (cgraph_node *n) + : node (n), + old (NULL), + newnode (NULL), + newf (NULL) +{} + +/* Add an ARG to the list of arguments for the function. */ + +void +srfunction::add_arg (srdecl *arg) +{ + args.safe_push (arg); +} + +/* Dump the SRFUNCTION to the file FILE. */ + +void +srfunction::dump (FILE *file) +{ + if (node) + { + fprintf (file, "function : "); + print_generic_expr (file, node->decl); + fprintf (file, " with arguments: "); + for (unsigned i = 0; i < args.length (); i++) + { + if (i == 0) + fprintf (file, "\n "); + else + fprintf (file, "\n, "); + args[i]->dump (file); + } + + fprintf (file, "\nuses globals: "); + for (unsigned i = 0; i < globals.length (); i++) + { + fprintf (file, "\n "); + globals[i]->dump (file); + } + + fprintf (file, "\ndecls: "); + } + else + fprintf (file, "globals : "); + + for (unsigned i = 0; i < decls.length (); i++) + { + fprintf (file, "\n "); + decls[i]->dump (file); + } +} + +/* Simple dump the SRFUNCTION to the file FILE; + used so it is not recusive. */ + +void +srfunction::simple_dump (FILE *file) +{ + print_generic_expr (file, node->decl); +} + +/* Constructor of FIELD. */ + +srfield::srfield (tree field, srtype *base) + : offset (int_byte_position (field)), + fieldtype (TREE_TYPE (field)), + fielddecl (field), + base (base), + type (NULL), + clusternum (0) +{ + for (int i = 0; i < max_split; i++) + newfield[i] = NULL_TREE; +} + +/* Constructor of TYPE. */ + +srtype::srtype (tree type) + : type (type), + chain_type (false), + escapes (does_not_escape), + visited (false) +{ + for (int i = 0; i < max_split; i++) + newtype[i] = NULL_TREE; + + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL) + { + if (DECL_BIT_FIELD (field)) + { + escapes = escape_bitfields; + continue; + } + else if (!DECL_SIZE (field) + || TREE_CODE (DECL_SIZE (field)) != INTEGER_CST) + { + escapes = escape_variable_sized_array; + break; + } + srfield *t = new srfield (field, this); + fields.safe_push (t); + } + } +} + +/* Mark the type as escaping type E at statement STMT. */ + +void +srtype::mark_escape (escape_type e, gimple *stmt) +{ + /* Once the type has escaped, it should never + change back to non escaping. */ + gcc_assert (e != does_not_escape); + if (has_escaped ()) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nO type: "); + simple_dump (dump_file); + fprintf (dump_file, " has already escaped."); + fprintf (dump_file, " old = \"%s\" ", + escape_type_string[escapes - 1]); + fprintf (dump_file, " new = \"%s\"\n", escape_type_string[e - 1]); + if (stmt) + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + } + return; + } + escapes = e; + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nN type: "); + simple_dump (dump_file); + fprintf (dump_file, " new = \"%s\"\n", escape_reason ()); + if (stmt) + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + } +} + +/* Add FIELD to the list of fields that use this type. */ + +void +srtype::add_field_site (srfield *field) +{ + field_sites.safe_push (field); +} + +/* Constructor of DECL. */ + +srdecl::srdecl (srtype *tp, tree decl, int argnum) + : type (tp), + decl (decl), + func (NULL_TREE), + argumentnum (argnum), + visited (false) +{ + if (TREE_CODE (decl) == SSA_NAME) + func = current_function_decl; + else if (!is_global_var (decl)) + func = DECL_CONTEXT (decl); + for (int i = 0; i < max_split; i++) + newdecl[i] = NULL_TREE; +} + +/* Find DECL in the function. */ + +srdecl * +srfunction::find_decl (tree decl) +{ + for (unsigned i = 0; i < decls.length (); i++) + if (decls[i]->decl == decl) + return decls[i]; + return NULL; +} + +/* Record DECL of the TYPE with argument num ARG. */ + +srdecl * +srfunction::record_decl (srtype *type, tree decl, int arg) +{ + // Search for the decl to see if it is already there. + srdecl *decl1 = find_decl (decl); + + if (decl1) + return decl1; + + gcc_assert (type); + + decl1 = new srdecl (type, decl, arg); + decls.safe_push (decl1); + return decl1; +} + +/* Find the field at OFF offset. */ + +srfield * +srtype::find_field (unsigned HOST_WIDE_INT off) +{ + unsigned int i; + srfield *field; + + /* FIXME: handle array/struct field inside the current struct. */ + /* NOTE This does not need to be fixed to handle libquatumn. */ + FOR_EACH_VEC_ELT (fields, i, field) + { + if (off == field->offset) + return field; + } + return NULL; +} + +/* Add the function FN to the list of functions if it + is there not already. */ + +void +srtype::add_function (srfunction *fn) +{ + unsigned decluid; + unsigned i; + decluid = DECL_UID (fn->node->decl); + + srfunction *fn1; + // Search for the decl to see if it is already there. + FOR_EACH_VEC_ELT (functions, i, fn1) + { + if (DECL_UID (fn1->node->decl) == decluid) + return; + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Recording new function: %u.\n", decluid); + + functions.safe_push (fn); +} + +/* Dump out the type structure to FILE. */ + +void +srtype::dump (FILE *f) +{ + unsigned int i; + srfield *field; + srfunction *fn; + sraccess *access; + + if (chain_type) + fprintf (f, "chain decl "); + + fprintf (f, "type : "); + print_generic_expr (f, type); + fprintf (f, "(%d) { ", TYPE_UID (type)); + if (escapes != does_not_escape) + fprintf (f, " escapes = \"%s\"\n", escape_reason ()); + fprintf (f, " fields = { "); + FOR_EACH_VEC_ELT (fields, i, field) + { + if (i == 0) + fprintf (f, "\n "); + else + fprintf (f, "\n, "); + field->dump (f); + } + fprintf (f, " }\n "); + fprintf (f, "\n accesses = {"); + FOR_EACH_VEC_ELT (accesses, i, access) + { + fprintf (f, "\n"); + access->dump (f); + } + fprintf (f, " }\n "); + fprintf (f, "\n functions = {"); + FOR_EACH_VEC_ELT (functions, i, fn) + { + fprintf (f, " \n"); + fn->simple_dump (f); + } + fprintf (f, "\n }\n"); + fprintf (f, "\n field_sites = {"); + FOR_EACH_VEC_ELT (field_sites, i, field) + { + fprintf (f, " \n"); + field->simple_dump (f); + } + fprintf (f, "\n }\n"); + fprintf (f, "}\n"); +} + +/* A simplified dump out the type structure to FILE. */ + +void +srtype::simple_dump (FILE *f) +{ + print_generic_expr (f, type); +} + +/* Analyze the type and decide what to be done with it. */ + +void +srtype::analyze (void) +{ + /* Chain decl types can't be split + so don't try. */ + if (chain_type) + return; + + /* If there is only one field then there is nothing + to be done. */ + if (fields.length () == 1) + return; + + /* For now we unconditionally split only structures with 2 fields + into 2 different structures. In future we intend to add profile + info and/or static heuristics to differentiate splitting process. */ + if (fields.length () == 2) + fields[1]->clusternum = 1; + + /* Otherwise we do nothing. */ + if (fields.length () >= 3) + return; +} + +/* Create the new fields for this field. */ + +void +srfield::create_new_fields (tree newtype[max_split], + tree newfields[max_split], + tree newlast[max_split]) +{ + tree nt[max_split]; + + for (unsigned i = 0; i < max_split; i++) + nt[i] = NULL; + + if (type == NULL) + nt[0] = fieldtype; + else + memcpy (nt, type->newtype, sizeof (type->newtype)); + + for (unsigned i = 0; i < max_split && nt[i] != NULL; i++) + { + tree field = make_node (FIELD_DECL); + if (nt[1] != NULL && DECL_NAME (fielddecl)) + { + const char *tname = IDENTIFIER_POINTER (DECL_NAME (fielddecl)); + char id[10]; + char *name; + + sprintf (id, "%d", i); + name = concat (tname, ".reorg.", id, NULL); + DECL_NAME (field) = get_identifier (name); + free (name); + } + else + DECL_NAME (field) = DECL_NAME (fielddecl); + + TREE_TYPE (field) = reconstruct_complex_type ( + TREE_TYPE (fielddecl), nt[i]); + DECL_SOURCE_LOCATION (field) = DECL_SOURCE_LOCATION (fielddecl); + SET_DECL_ALIGN (field, DECL_ALIGN (fielddecl)); + DECL_USER_ALIGN (field) = DECL_USER_ALIGN (fielddecl); + TREE_ADDRESSABLE (field) = TREE_ADDRESSABLE (fielddecl); + DECL_NONADDRESSABLE_P (field) = !TREE_ADDRESSABLE (fielddecl); + TREE_THIS_VOLATILE (field) = TREE_THIS_VOLATILE (fielddecl); + DECL_CONTEXT (field) = newtype[clusternum]; + + if (newfields[clusternum] == NULL) + newfields[clusternum] = newlast[clusternum] = field; + else + { + DECL_CHAIN (newlast[clusternum]) = field; + newlast[clusternum] = field; + } + newfield[i] = field; + } +} + +/* Create the new TYPE corresponding to THIS type. */ + +bool +srtype::create_new_type (void) +{ + /* If the type has been visited, + then return if a new type was + created or not. */ + if (visited) + return has_new_type (); + + visited = true; + + if (escapes != does_not_escape) + { + newtype[0] = type; + return false; + } + + bool createnewtype = false; + unsigned maxclusters = 0; + + /* Create a new type for each field. */ + for (unsigned i = 0; i < fields.length (); i++) + { + srfield *field = fields[i]; + if (field->type) + createnewtype |= field->type->create_new_type (); + if (field->clusternum > maxclusters) + maxclusters = field->clusternum; + } + + /* If the fields' types did have a change or + we are not splitting the struct into two clusters, + then just return false and don't change the type. */ + if (!createnewtype && maxclusters == 0) + { + newtype[0] = type; + return false; + } + + /* Should have at most max_split clusters. */ + gcc_assert (maxclusters < max_split); + + tree newfields[max_split]; + tree newlast[max_split]; + + maxclusters++; + + const char *tname = NULL; + + if (TYPE_NAME (type) != NULL) + { + if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) + tname = IDENTIFIER_POINTER (TYPE_NAME (type)); + else if (DECL_NAME (TYPE_NAME (type)) != NULL) + tname = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); + } + + for (unsigned i = 0; i < maxclusters; i++) + { + newfields[i] = NULL_TREE; + newlast[i] = NULL_TREE; + newtype[i] = make_node (RECORD_TYPE); + + char *name = NULL; + char id[10]; + sprintf (id, "%d", i); + if (tname) + { + name = concat (tname, ".reorg.", id, NULL); + TYPE_NAME (newtype[i]) = get_identifier (name); + free (name); + } + } + + for (unsigned i = 0; i < fields.length (); i++) + { + srfield *f = fields[i]; + f->create_new_fields (newtype, newfields, newlast); + } + + /* No reason to warn about these structs since the warning would + have happened already. */ + int save_warn_padded = warn_padded; + warn_padded = 0; + + for (unsigned i = 0; i < maxclusters; i++) + { + TYPE_FIELDS (newtype[i]) = newfields[i]; + layout_type (newtype[i]); + } + + warn_padded = save_warn_padded; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Created %d types:\n", maxclusters); + for (unsigned i = 0; i < maxclusters; i++) + { + print_generic_expr (dump_file, newtype[i]); + fprintf (dump_file, "\n"); + } + } + + return true; +} + +/* Helper function to copy some attributes from ORIG_DECL to the NEW_DECL. */ + +static inline void +copy_var_attributes (tree new_decl, tree orig_decl) +{ + DECL_ARTIFICIAL (new_decl) = 1; + DECL_EXTERNAL (new_decl) = DECL_EXTERNAL (orig_decl); + TREE_STATIC (new_decl) = TREE_STATIC (orig_decl); + TREE_PUBLIC (new_decl) = TREE_PUBLIC (orig_decl); + TREE_USED (new_decl) = TREE_USED (orig_decl); + DECL_CONTEXT (new_decl) = DECL_CONTEXT (orig_decl); + TREE_THIS_VOLATILE (new_decl) = TREE_THIS_VOLATILE (orig_decl); + TREE_ADDRESSABLE (new_decl) = TREE_ADDRESSABLE (orig_decl); + TREE_READONLY (new_decl) = TREE_READONLY (orig_decl); + if (is_global_var (orig_decl)) + set_decl_tls_model (new_decl, DECL_TLS_MODEL (orig_decl)); +} + +/* Create all of the new decls (SSA_NAMES included) for THIS function. */ + +void +srfunction::create_new_decls (void) +{ + /* If this function has been cloned, we don't need to + create the new decls. */ + if (newnode) + return; + + if (node) + set_cfun (DECL_STRUCT_FUNCTION (node->decl)); + + for (unsigned i = 0; i < decls.length (); i++) + { + srdecl *decl = decls[i]; + srtype *type = decl->type; + /* If the type of the decl does not change, + then don't create a new decl. */ + if (!type->has_new_type ()) + { + decl->newdecl[0] = decl->decl; + continue; + } + + /* Handle SSA_NAMEs. */ + if (TREE_CODE (decl->decl) == SSA_NAME) + { + tree newtype1[max_split]; + tree inner = SSA_NAME_VAR (decl->decl); + tree newinner[max_split]; + memset (newinner, 0, sizeof (newinner)); + for (unsigned j = 0; j < max_split && type->newtype[j]; j++) + newtype1[j] = reconstruct_complex_type (TREE_TYPE (decls[i]->decl), + type->newtype[j]); + if (inner) + { + srdecl *in = find_decl (inner); + gcc_assert (in); + memcpy (newinner, in->newdecl, sizeof (newinner)); + } + tree od = decls[i]->decl; + /* Create the new ssa names and copy some attributes + from the old one. */ + for (unsigned j = 0; j < max_split && type->newtype[j]; j++) + { + tree nd = make_ssa_name (newinner[j] ? newinner[j] + : newtype1[j]); + decl->newdecl[j] = nd; + /* If the old decl was a default definition, + handle it specially. */ + if (SSA_NAME_IS_DEFAULT_DEF (od)) + { + SSA_NAME_IS_DEFAULT_DEF (nd) = true; + SSA_NAME_DEF_STMT (nd) = gimple_build_nop (); + + /* Set the default definition for the ssaname if needed. */ + if (inner) + { + gcc_assert (newinner[j]); + set_ssa_default_def (cfun, newinner[j], nd); + } + } + SSA_NAME_OCCURS_IN_ABNORMAL_PHI (nd) + = SSA_NAME_OCCURS_IN_ABNORMAL_PHI (od); + statistics_counter_event (cfun, "Create new ssa_name", 1); + } + } + else if (TREE_CODE (decls[i]->decl) == VAR_DECL) + { + tree orig_var = decl->decl; + const char *tname = NULL; + if (DECL_NAME (orig_var)) + tname = IDENTIFIER_POINTER (DECL_NAME (orig_var)); + for (unsigned j = 0; j < max_split && type->newtype[j]; j++) + { + tree new_name = NULL; + char *name = NULL; + char id[10]; + sprintf (id, "%d", j); + if (tname) + { + name = concat (tname, ".reorg.", id, NULL); + new_name = get_identifier (name); + free (name); + } + tree newtype1 = reconstruct_complex_type (TREE_TYPE (orig_var), + type->newtype[j]); + decl->newdecl[j] = build_decl (DECL_SOURCE_LOCATION (orig_var), + VAR_DECL, new_name, newtype1); + copy_var_attributes (decl->newdecl[j], orig_var); + if (!is_global_var (orig_var)) + add_local_decl (cfun, decl->newdecl[j]); + else + varpool_node::add (decl->newdecl[j]); + statistics_counter_event (cfun, "Create new var decl", 1); + } + } + /* Paramater decls are already handled in create_new_functions. */ + else if (TREE_CODE (decls[i]->decl) == PARM_DECL) + ; + else + internal_error ("Unhandled declaration type stored"); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Created New decls for decl:\n"); + fprintf (dump_file, "\n"); + decls[i]->dump (dump_file); + fprintf (dump_file, "\n"); + for (unsigned j = 0; j < max_split && decls[i]->newdecl[j]; j++) + { + print_generic_expr (dump_file, decls[i]->newdecl[j]); + fprintf (dump_file, "\n"); + } + fprintf (dump_file, "\n"); + } + } + + set_cfun (NULL); +} + +/* Dump out the field structure to FILE. */ + +void +srfield::dump (FILE *f) +{ + fprintf (f, "field (%d) { ", DECL_UID (fielddecl)); + fprintf (f, "base = "); + base->simple_dump (f); + fprintf (f, ", offset = " HOST_WIDE_INT_PRINT_DEC, offset); + fprintf (f, ", type = "); + print_generic_expr (f, fieldtype); + if (type) + { + fprintf (f, "( srtype = "); + type->simple_dump (f); + fprintf (f, ")"); + } + fprintf (f, "\n}\n"); +} + +/* A simplified dump out the field structure to FILE. */ + +void +srfield::simple_dump (FILE *f) +{ + fprintf (f, "field (%d)", DECL_UID (fielddecl)); +} + +/* Dump out the access structure to FILE. */ + +void +sraccess::dump (FILE *f) +{ + fprintf (f, "access { "); + fprintf (f, "type = '("); + type->simple_dump (f); + fprintf (f, ")'"); + if (field) + { + fprintf (f, ", field = '("); + field->simple_dump (f); + fprintf (f, ")'"); + } + else + fprintf (f, ", whole type"); + fprintf (f, " in function: %s/%d", node->name (), node->order); + fprintf (f, ", stmt:\n"); + print_gimple_stmt (f, stmt, 0); + fprintf (f, "\n }\n"); +} + +/* Dump out the decl structure to FILE. */ + +void +srdecl::dump (FILE *file) +{ + if (!func) + fprintf (file, "global "); + if (argumentnum != -1) + fprintf (file, "argument(%d) ", argumentnum); + fprintf (file, "decl: "); + print_generic_expr (file, decl); + fprintf (file, " type: "); + type->simple_dump (file); +} + +} // namespace struct_reorg + + +namespace { + +struct ipa_struct_reorg +{ +public: + // Constructors + ipa_struct_reorg (void) + : current_function (NULL), + done_recording (false) + {} + + // Public methods + unsigned execute (void); + void mark_type_as_escape (tree type, escape_type, gimple *stmt = NULL); +private: + // Fields + auto_vec_del types; + auto_vec_del functions; + srglobal globals; + srfunction *current_function; + + bool done_recording; + + // Private methods + void dump_types (FILE *f); + void dump_types_escaped (FILE *f); + void dump_functions (FILE *f); + void record_accesses (void); + void detect_cycles (void); + bool walk_field_for_cycles (srtype *); + void prune_escaped_types (void); + void propagate_escape (void); + void analyze_types (void); + void clear_visited (void); + bool create_new_types (void); + void restore_field_type (void); + void create_new_decls (void); + srdecl *find_decl (tree); + void create_new_functions (void); + void create_new_args (cgraph_node *new_node); + unsigned rewrite_functions (void); + srdecl *record_var (tree decl, + escape_type escapes = does_not_escape, + int arg = -1); + srfunction *record_function (cgraph_node *node); + srfunction *find_function (cgraph_node *node); + srtype *record_type (tree type); + void process_union (tree type); + srtype *find_type (tree type); + void maybe_record_stmt (cgraph_node *, gimple *); + void maybe_record_assign (cgraph_node *, gassign *); + void maybe_record_call (cgraph_node *, gcall *); + void maybe_record_allocation_site (cgraph_node *, gimple *); + void record_stmt_expr (tree expr, cgraph_node *node, gimple *stmt); + void mark_expr_escape (tree, escape_type, gimple *stmt); + tree allocate_size (srtype *t, gimple *stmt); + + void mark_decls_in_as_not_needed (tree fn); + + bool rewrite_stmt (gimple *, gimple_stmt_iterator *); + bool rewrite_assign (gassign *, gimple_stmt_iterator *); + bool rewrite_call (gcall *, gimple_stmt_iterator *); + bool rewrite_cond (gcond *, gimple_stmt_iterator *); + bool rewrite_debug (gimple *, gimple_stmt_iterator *); + bool rewrite_phi (gphi *); + bool rewrite_expr (tree expr, + tree newexpr[max_split], + bool ignore_missing_decl = false); + bool rewrite_lhs_rhs (tree lhs, tree rhs, tree newlhs[max_split], + tree newrhs[max_split]); + bool get_type_field (tree expr, tree &base, bool &indirect, + srtype *&type, srfield *&field, + bool &realpart, bool &imagpart, + bool &address, bool should_create = false, + bool can_escape = false); + bool wholeaccess (tree expr, tree base, tree accesstype, srtype *t); + + void check_definition (srdecl *decl, vec &); + void check_uses (srdecl *decl, vec &); + void check_use (srdecl *decl, gimple *stmt, vec &); + void check_type_and_push (tree newdecl, srtype *type, + vec &worklist, gimple *stmt); + void check_other_side (srdecl *decl, tree other, gimple *stmt, + vec &worklist); + + void find_vars (gimple *stmt); + void find_var (tree expr, gimple *stmt); + void mark_types_asm (gasm *astmt); + + bool has_rewritten_type (srfunction *); + void maybe_mark_or_record_other_side (tree side, tree other, gimple *stmt); +}; + +/* Dump all of the recorded types to file F. */ + +void +ipa_struct_reorg::dump_types (FILE *f) +{ + unsigned i; + srtype *type; + FOR_EACH_VEC_ELT (types, i, type) + { + type->dump (f); + } + fprintf (f, "\n"); +} + +/* Dump all of the recorded types to file F. */ + +void +ipa_struct_reorg::dump_types_escaped (FILE *f) +{ + unsigned i; + srtype *type; + FOR_EACH_VEC_ELT (types, i, type) + { + if (type->has_escaped ()) + { + type->simple_dump (f); + fprintf (f, " has escaped: \"%s\"\n", type->escape_reason ()); + } + } + fprintf (f, "\n"); +} + +/* Dump all of the record functions to file F. */ + +void +ipa_struct_reorg::dump_functions (FILE *f) +{ + unsigned i; + srfunction *fn; + + fprintf (f, "\n\n"); + globals.dump (f); + fprintf (f, "\n\n"); + FOR_EACH_VEC_ELT (functions, i, fn) + { + fn->dump (f); + fprintf (f, "\n"); + } + fprintf (f, "\n\n"); +} + +/* Find the recorded srtype corresponding to TYPE. */ + +srtype * +ipa_struct_reorg::find_type (tree type) +{ + unsigned i; + /* Get the main variant as we are going + to find that type only. */ + type = TYPE_MAIN_VARIANT (type); + + srtype *type1; + // Search for the type to see if it is already there. + FOR_EACH_VEC_ELT (types, i, type1) + { + if (types_compatible_p (type1->type, type)) + return type1; + } + return NULL; +} + +/* Is TYPE a volatile type or one which points + to a volatile type. */ + +static bool +isvolatile_type (tree type) +{ + if (TYPE_VOLATILE (type)) + return true; + while (POINTER_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE) + { + type = TREE_TYPE (type); + if (TYPE_VOLATILE (type)) + return true; + } + return false; +} + +/* Is TYPE an array type or points to an array type. */ + +static bool +isarraytype (tree type) +{ + 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; +} + +/* Is TYPE a pointer to another pointer. */ + +static bool +isptrptr (tree type) +{ + bool firstptr = false; + while (POINTER_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE) + { + if (POINTER_TYPE_P (type)) + { + if (firstptr) + return true; + firstptr = true; + } + type = TREE_TYPE (type); + } + return false; +} + +/* Return the escape type which corresponds to if + this is an volatile type, an array type or a pointer + to a pointer type. */ + +static escape_type +escape_type_volatile_array_or_ptrptr (tree type) +{ + if (isvolatile_type (type)) + return escape_volatile; + if (isarraytype (type)) + return escape_array; + if (isptrptr (type)) + return escape_ptr_ptr; + return does_not_escape; +} + +/* Record TYPE if not already recorded. */ + +srtype * +ipa_struct_reorg::record_type (tree type) +{ + unsigned typeuid; + + /* Get the main variant as we are going + to record that type only. */ + type = TYPE_MAIN_VARIANT (type); + typeuid = TYPE_UID (type); + + srtype *type1; + + type1 = find_type (type); + if (type1) + return type1; + + /* If already done recording just return NULL. */ + if (done_recording) + return NULL; + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Recording new type: %u.\n", typeuid); + + type1 = new srtype (type); + types.safe_push (type1); + + /* If the type has an user alignment set, + that means the user most likely already setup the type. */ + if (TYPE_USER_ALIGN (type)) + type1->mark_escape (escape_user_alignment, NULL); + + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL) + { + tree t = TREE_TYPE (field); + process_union (t); + if (TREE_CODE (inner_type (t)) == UNION_TYPE + || TREE_CODE (inner_type (t)) == QUAL_UNION_TYPE) + type1->mark_escape (escape_union, NULL); + if (isvolatile_type (t)) + type1->mark_escape (escape_volatile, NULL); + escape_type e = escape_type_volatile_array_or_ptrptr (t); + if (e != does_not_escape) + type1->mark_escape (e, NULL); + if (handled_type (t)) + { + srtype *t1 = record_type (inner_type (t)); + srfield *f = type1->find_field (int_byte_position (field)); + /* We might have an variable sized type which + we don't set the handle. */ + if (f) + { + f->type = t1; + t1->add_field_site (f); + } + if (t1 == type1) + type1->mark_escape (escape_rescusive_type, NULL); + } + } + } + + return type1; +} + +/* Mark TYPE as escaping with ESCAPES as the reason. */ + +void +ipa_struct_reorg::mark_type_as_escape (tree type, + escape_type escapes, + gimple *stmt) +{ + if (handled_type (type)) + { + srtype *stype = record_type (inner_type (type)); + + if (!stype) + return; + + stype->mark_escape (escapes, stmt); + } +} + +/* Maybe process the union of type TYPE, such that marking all of the fields' + types as being escaping. */ + +void +ipa_struct_reorg::process_union (tree type) +{ + static hash_set unions_recorded; + + type = inner_type (type); + if (TREE_CODE (type) != UNION_TYPE + && TREE_CODE (type) != QUAL_UNION_TYPE) + return; + + type = TYPE_MAIN_VARIANT (type); + + /* We already processed this type. */ + if (unions_recorded.add (type)) + return; + + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL) + { + mark_type_as_escape (TREE_TYPE (field), escape_union); + process_union (TREE_TYPE (field)); + } + } +} + +/* Used by record_var function as a callback to walk_tree. + Mark the type as escaping if it has expressions which + cannot be converted for global initializations. */ + +static tree +record_init_types (tree *tp, int *walk_subtrees, void *data) +{ + ipa_struct_reorg *c = (ipa_struct_reorg *)data; + switch (TREE_CODE (*tp)) + { + CASE_CONVERT: + case COMPONENT_REF: + case VIEW_CONVERT_EXPR: + case ARRAY_REF: + { + tree typeouter = TREE_TYPE (*tp); + tree typeinner = TREE_TYPE (TREE_OPERAND (*tp, 0)); + c->mark_type_as_escape (typeouter, escape_via_global_init); + c->mark_type_as_escape (typeinner, escape_via_global_init); + break; + } + case INTEGER_CST: + if (!integer_zerop (*tp)) + c->mark_type_as_escape (TREE_TYPE (*tp), escape_via_global_init); + break; + case VAR_DECL: + case PARM_DECL: + case FIELD_DECL: + c->mark_type_as_escape (TREE_TYPE (*tp), escape_via_global_init); + *walk_subtrees = false; + break; + default: + *walk_subtrees = true; + break; + } + return NULL_TREE; +} + +/* Record var DECL; optionally specify the escape reason and the argument + number in a function. */ + +srdecl * +ipa_struct_reorg::record_var (tree decl, escape_type escapes, int arg) +{ + srtype *type; + srdecl *sd = NULL; + + process_union (TREE_TYPE (decl)); + + if (handled_type (TREE_TYPE (decl))) + { + type = record_type (inner_type (TREE_TYPE (decl))); + escape_type e; + + if (done_recording && !type) + return NULL; + + gcc_assert (type); + if (TREE_CODE (decl) == VAR_DECL && is_global_var (decl)) + sd = globals.record_decl (type, decl, arg); + else + { + gcc_assert (current_function); + sd = current_function->record_decl (type, decl, arg); + } + + /* If the variable has the "used" attribute, + then treat the type as escaping. */ + if (escapes != does_not_escape) + e = escapes; + else if (TREE_CODE (decl) != SSA_NAME && DECL_PRESERVE_P (decl)) + e = escape_marked_as_used; + else if (TREE_THIS_VOLATILE (decl)) + e = escape_volatile; + else if (TREE_CODE (decl) != SSA_NAME && DECL_USER_ALIGN (decl)) + e = escape_user_alignment; + else if (TREE_CODE (decl) != SSA_NAME && TREE_STATIC (decl) + && TREE_PUBLIC (decl)) + e = escape_via_global_var; + /* We don't have an initlizer. */ + else if (TREE_CODE (decl) != SSA_NAME + && DECL_INITIAL (decl) == error_mark_node) + e = escape_via_global_var; + else + e = escape_type_volatile_array_or_ptrptr (TREE_TYPE (decl)); + + if (e != does_not_escape) + type->mark_escape (e, NULL); + } + + /* Record the initial usage of variables as types escapes. */ + if (TREE_CODE (decl) != SSA_NAME && TREE_STATIC (decl) + && DECL_INITIAL (decl)) + { + walk_tree_without_duplicates (&DECL_INITIAL (decl), + record_init_types, this); + if (!integer_zerop (DECL_INITIAL (decl)) + && DECL_INITIAL (decl) != error_mark_node) + mark_type_as_escape (TREE_TYPE (decl), escape_via_global_init); + } + return sd; +} + +/* Find void* ssa_names which are used inside MEM[] or if we have &a.c, + mark the type as escaping. */ + +void +ipa_struct_reorg::find_var (tree expr, gimple *stmt) +{ + /* If we have VCE mark the outer type as escaping and the inner one + Also mark the inner most operand. */ + if (TREE_CODE (expr) == VIEW_CONVERT_EXPR) + { + mark_type_as_escape (TREE_TYPE (expr), escape_vce, stmt); + mark_type_as_escape (TREE_TYPE (TREE_OPERAND (expr, 0)), + escape_vce, stmt); + } + + /* If we have &b.c then we need to mark the type of b + as escaping as tracking a will be hard. */ + if (TREE_CODE (expr) == ADDR_EXPR + || TREE_CODE (expr) == VIEW_CONVERT_EXPR) + { + tree r = TREE_OPERAND (expr, 0); + if (handled_component_p (r) + || TREE_CODE (r) == MEM_REF) + { + while (handled_component_p (r) + || TREE_CODE (r) == MEM_REF) + { + if (TREE_CODE (r) == VIEW_CONVERT_EXPR) + { + mark_type_as_escape (TREE_TYPE (r), escape_vce, stmt); + mark_type_as_escape (TREE_TYPE (TREE_OPERAND (r, 0)), + escape_vce, stmt); + } + if (TREE_CODE (r) == MEM_REF) + mark_type_as_escape (TREE_TYPE (TREE_OPERAND (r, 1)), + escape_addr, stmt); + r = TREE_OPERAND (r, 0); + } + mark_expr_escape (r, escape_addr, stmt); + } + } + + tree base; + bool indirect; + srtype *type; + srfield *field; + bool realpart, imagpart, address; + get_type_field (expr, base, indirect, type, field, + realpart, imagpart, address, true, true); +} + +void +ipa_struct_reorg::find_vars (gimple *stmt) +{ + gasm *astmt; + switch (gimple_code (stmt)) + { + case GIMPLE_ASSIGN: + if (gimple_assign_rhs_class (stmt) == GIMPLE_SINGLE_RHS + || gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) + { + tree lhs = gimple_assign_lhs (stmt); + tree rhs = gimple_assign_rhs1 (stmt); + find_var (gimple_assign_lhs (stmt), stmt); + find_var (gimple_assign_rhs1 (stmt), stmt); + if (TREE_CODE (lhs) == SSA_NAME + && VOID_POINTER_P (TREE_TYPE (lhs)) + && handled_type (TREE_TYPE (rhs))) + { + srtype *t = find_type (inner_type (TREE_TYPE (rhs))); + srdecl *d = find_decl (lhs); + if (!d && t) + current_function->record_decl (t, lhs, -1); + } + if (TREE_CODE (rhs) == SSA_NAME + && VOID_POINTER_P (TREE_TYPE (rhs)) + && handled_type (TREE_TYPE (lhs))) + { + srtype *t = find_type (inner_type (TREE_TYPE (lhs))); + srdecl *d = find_decl (rhs); + if (!d && t) + current_function->record_decl (t, rhs, -1); + } + } + break; + + case GIMPLE_CALL: + if (gimple_call_lhs (stmt)) + find_var (gimple_call_lhs (stmt), stmt); + + if (gimple_call_chain (stmt)) + find_var (gimple_call_chain (stmt), stmt); + + for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) + find_var (gimple_call_arg (stmt, i), stmt); + break; + + case GIMPLE_ASM: + astmt = as_a (stmt); + for (unsigned i = 0; i < gimple_asm_ninputs (astmt); i++) + find_var (TREE_VALUE (gimple_asm_input_op (astmt, i)), stmt); + for (unsigned i = 0; i < gimple_asm_noutputs (astmt); i++) + find_var (TREE_VALUE (gimple_asm_output_op (astmt, i)), stmt); + mark_types_asm (astmt); + break; + + case GIMPLE_RETURN: + { + tree expr = gimple_return_retval (as_a (stmt)); + if (expr) + find_var (expr, stmt); + /* return &a; should mark the type of a as escaping + through a return. */ + if (expr && TREE_CODE (expr) == ADDR_EXPR) + { + expr = TREE_OPERAND (expr, 0); + srdecl *d = find_decl (expr); + if (d) + d->type->mark_escape (escape_return, stmt); + } + } + break; + + default: + break; + } +} + +/* Maybe record access of statement for further analaysis. */ + +void +ipa_struct_reorg::maybe_record_stmt (cgraph_node *node, gimple *stmt) +{ + switch (gimple_code (stmt)) + { + case GIMPLE_ASSIGN: + maybe_record_assign (node, as_a (stmt)); + break; + case GIMPLE_CALL: + maybe_record_call (node, as_a (stmt)); + break; + case GIMPLE_DEBUG: + break; + case GIMPLE_GOTO: + case GIMPLE_SWITCH: + break; + default: + break; + } +} + +/* This function checks whether ARG is a result of multiplication + of some number by STRUCT_SIZE. If yes, the function returns true + and this number is filled into NUM. */ + +static bool +is_result_of_mult (tree arg, tree *num, tree struct_size) +{ + if (!struct_size + || TREE_CODE (struct_size) != INTEGER_CST + || integer_zerop (struct_size)) + return false; + + /* If we have a integer, just check if it is a multiply of STRUCT_SIZE. */ + if (TREE_CODE (arg) == INTEGER_CST) + { + if (integer_zerop (size_binop (FLOOR_MOD_EXPR, arg, struct_size))) + { + *num = size_binop (FLOOR_DIV_EXPR, arg, struct_size); + return true; + } + return false; + } + gimple *size_def_stmt = SSA_NAME_DEF_STMT (arg); + + /* If the allocation statement was of the form + D.2229_10 = (D.2228_9); + then size_def_stmt can be D.2228_9 = num.3_8 * 8; */ + + while (size_def_stmt && is_gimple_assign (size_def_stmt)) + { + tree lhs = gimple_assign_lhs (size_def_stmt); + + /* We expect temporary here. */ + if (!is_gimple_reg (lhs)) + return false; + + // FIXME: this should handle SHIFT also. + if (gimple_assign_rhs_code (size_def_stmt) == PLUS_EXPR) + { + tree num1, num2; + tree arg0 = gimple_assign_rhs1 (size_def_stmt); + tree arg1 = gimple_assign_rhs2 (size_def_stmt); + if (!is_result_of_mult (arg0, &num1, struct_size)) + return false; + if (!is_result_of_mult (arg1, &num2, struct_size)) + return false; + *num = size_binop (PLUS_EXPR, num1, num2); + return true; + } + else if (gimple_assign_rhs_code (size_def_stmt) == MULT_EXPR) + { + tree arg0 = gimple_assign_rhs1 (size_def_stmt); + tree arg1 = gimple_assign_rhs2 (size_def_stmt); + tree num1; + + if (is_result_of_mult (arg0, &num1, struct_size)) + { + *num = size_binop (MULT_EXPR, arg1, num1); + return true; + } + if (is_result_of_mult (arg1, &num1, struct_size)) + { + *num = size_binop (MULT_EXPR, arg0, num1); + return true; + } + + *num = NULL_TREE; + return false; + } + else if (gimple_assign_rhs_code (size_def_stmt) == SSA_NAME) + { + arg = gimple_assign_rhs1 (size_def_stmt); + size_def_stmt = SSA_NAME_DEF_STMT (arg); + } + else + { + *num = NULL_TREE; + return false; + } + } + + *num = NULL_TREE; + return false; +} + +/* Return TRUE if STMT is an allocation statement that is handled. */ + +static bool +handled_allocation_stmt (gimple *stmt) +{ + if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_MALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_CALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_ALIGNED_ALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA) + || gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN)) + return true; + return false; +} + +/* Returns the allocated size / T size for STMT. That is the number of + elements in the array allocated. */ + +tree +ipa_struct_reorg::allocate_size (srtype *type, gimple *stmt) +{ + if (!stmt + || gimple_code (stmt) != GIMPLE_CALL + || !handled_allocation_stmt (stmt)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nNot a allocate statment:\n"); + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + } + return NULL; + } + + if (type->has_escaped ()) + return NULL; + + tree struct_size = TYPE_SIZE_UNIT (type->type); + + tree size = gimple_call_arg (stmt, 0); + + if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_ALIGNED_ALLOC)) + size = gimple_call_arg (stmt, 1); + else if (gimple_call_builtin_p (stmt, BUILT_IN_CALLOC)) + { + tree arg1; + arg1 = gimple_call_arg (stmt, 1); + /* Check that second argument is a constant equal to + the size of structure. */ + if (operand_equal_p (arg1, struct_size, 0)) + return size; + /* Check that first argument is a constant equal to + the size of structure. */ + if (operand_equal_p (size, struct_size, 0)) + return arg1; + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\ncalloc the correct size:\n"); + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + } + return NULL; + } + + tree num; + if (!is_result_of_mult (size, &num, struct_size)) + return NULL; + + return num; +} + +void +ipa_struct_reorg::maybe_mark_or_record_other_side (tree side, tree other, + gimple *stmt) +{ + gcc_assert (TREE_CODE (side) == SSA_NAME || TREE_CODE (side) == ADDR_EXPR); + srtype *type = NULL; + if (handled_type (TREE_TYPE (other))) + type = record_type (inner_type (TREE_TYPE (other))); + if (TREE_CODE (side) == ADDR_EXPR) + side = TREE_OPERAND (side, 0); + srdecl *d = find_decl (side); + if (!type) + { + if (!d) + return; + if (TREE_CODE (side) == SSA_NAME + && VOID_POINTER_P (TREE_TYPE (side))) + return; + d->type->mark_escape (escape_cast_another_ptr, stmt); + return; + } + + if (!d) + { + if (VOID_POINTER_P (TREE_TYPE (side)) + && TREE_CODE (side) == SSA_NAME) + current_function->record_decl (type, side, -1); + else + type->mark_escape (escape_cast_another_ptr, stmt); + } + else if (type != d->type) + { + type->mark_escape (escape_cast_another_ptr, stmt); + d->type->mark_escape (escape_cast_another_ptr, stmt); + } +} + +/* Record accesses in an assignment statement STMT. */ + +void +ipa_struct_reorg::maybe_record_assign (cgraph_node *node, gassign *stmt) +{ + if (gimple_clobber_p (stmt)) + { + record_stmt_expr (gimple_assign_lhs (stmt), node, stmt); + return; + } + + if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) + { + tree lhs = gimple_assign_lhs (stmt); + tree rhs1 = gimple_assign_rhs1 (stmt); + tree rhs2 = gimple_assign_rhs2 (stmt); + tree num; + if (!handled_type (TREE_TYPE (lhs))) + return; + /* Check if rhs2 is a multiplication of the size of the type. */ + if (is_result_of_mult (rhs2, &num, + TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (lhs))))) + { + record_stmt_expr (lhs, node, stmt); + record_stmt_expr (rhs1, node, stmt); + } + else + { + mark_expr_escape (lhs, escape_non_multiply_size, stmt); + mark_expr_escape (rhs1, escape_non_multiply_size, stmt); + } + return; + } + /* Copies, References, Taking addresses. */ + if (gimple_assign_rhs_class (stmt) == GIMPLE_SINGLE_RHS) + { + tree lhs = gimple_assign_lhs (stmt); + tree rhs = gimple_assign_rhs1 (stmt); + /* If we have a = &b.c then we need to mark the type of b + as escaping as tracking a will be hard. */ + if (TREE_CODE (rhs) == ADDR_EXPR) + { + tree r = TREE_OPERAND (rhs, 0); + if (handled_component_p (r)) + { + while (handled_component_p (r)) + r = TREE_OPERAND (r, 0); + mark_expr_escape (r, escape_addr, stmt); + return; + } + } + if ((TREE_CODE (rhs) == SSA_NAME || TREE_CODE (rhs) == ADDR_EXPR)) + maybe_mark_or_record_other_side (rhs, lhs, stmt); + if (TREE_CODE (lhs) == SSA_NAME) + maybe_mark_or_record_other_side (lhs, rhs, stmt); + } +} + +tree +get_ref_base_and_offset (tree &e, HOST_WIDE_INT &offset, + bool &realpart, bool &imagpart, + tree &accesstype) +{ + offset = 0; + realpart = false; + imagpart = false; + accesstype = NULL_TREE; + if (TREE_CODE (e) == REALPART_EXPR) + { + e = TREE_OPERAND (e, 0); + realpart = true; + } + if (TREE_CODE (e) == IMAGPART_EXPR) + { + e = TREE_OPERAND (e, 0); + imagpart = true; + } + tree expr = e; + while (true) + { + switch (TREE_CODE (expr)) + { + case COMPONENT_REF: + { + tree field = TREE_OPERAND (expr, 1); + tree field_off = byte_position (field); + if (TREE_CODE (field_off) != INTEGER_CST) + return NULL; + offset += tree_to_shwi (field_off); + expr = TREE_OPERAND (expr, 0); + accesstype = NULL; + break; + } + case MEM_REF: + { + tree field_off = TREE_OPERAND (expr, 1); + gcc_assert (TREE_CODE (field_off) == INTEGER_CST); + /* So we can mark the types as escaping if different. */ + accesstype = TREE_TYPE (field_off); + offset += tree_to_uhwi (field_off); + return TREE_OPERAND (expr, 0); + } + default: + return expr; + } + } +} + +/* Return true if EXPR was accessing the whole type T. */ + +bool +ipa_struct_reorg::wholeaccess (tree expr, tree base, + tree accesstype, srtype *t) +{ + if (expr == base) + return true; + + if (TREE_CODE (expr) == ADDR_EXPR && TREE_OPERAND (expr, 0) == base) + return true; + + if (!accesstype) + return false; + + if (!types_compatible_p (TREE_TYPE (expr), TREE_TYPE (accesstype))) + return false; + + if (!handled_type (TREE_TYPE (expr))) + return false; + + srtype *other_type = find_type (inner_type (TREE_TYPE (expr))); + + if (t == other_type) + return true; + + return false; +} + +bool +ipa_struct_reorg::get_type_field (tree expr, tree &base, bool &indirect, + srtype *&type, srfield *&field, + bool &realpart, bool &imagpart, + bool &address, bool should_create, + bool can_escape) +{ + HOST_WIDE_INT offset; + tree accesstype; + address = false; + bool mark_as_bit_field = false; + + if (TREE_CODE (expr) == BIT_FIELD_REF) + { + expr = TREE_OPERAND (expr, 0); + mark_as_bit_field = true; + } + + base = get_ref_base_and_offset (expr, offset, realpart, imagpart, + accesstype); + + /* Variable access, unkown type. */ + if (base == NULL) + return false; + + if (TREE_CODE (base) == ADDR_EXPR) + { + address = true; + base = TREE_OPERAND (base, 0); + } + + if (offset != 0 && accesstype) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Non zero offset (%d) with MEM.\n", (int)offset); + print_generic_expr (dump_file, expr); + fprintf (dump_file, "\n"); + print_generic_expr (dump_file, base); + fprintf (dump_file, "\n"); + } + } + + srdecl *d = find_decl (base); + srtype *t; + + if (integer_zerop (base)) + { + gcc_assert (!d); + if (!accesstype) + return false; + t = find_type (inner_type (inner_type (accesstype))); + if (!t && should_create && handled_type (accesstype)) + t = record_type (inner_type (accesstype)); + if (!t) + return false; + } + else if (!d && accesstype) + { + if (!should_create) + return false; + if (!handled_type (accesstype)) + return false; + t = find_type (inner_type (inner_type (accesstype))); + if (!t) + t = record_type (inner_type (accesstype)); + if (!t || t->has_escaped ()) + return false; + /* If base is not void* mark the type as escaping. */ + if (!VOID_POINTER_P (TREE_TYPE (base))) + { + gcc_assert (can_escape); + t->mark_escape (escape_cast_another_ptr, NULL); + return false; + } + if (TREE_CODE (base) == SSA_NAME) + current_function->record_decl (t, base, -1); + } + else if (!d) + return false; + else + t = d->type; + + if (t->has_escaped ()) + return false; + + if (mark_as_bit_field) + { + gcc_assert (can_escape); + t->mark_escape (escape_bitfields, NULL); + return false; + } + + if (wholeaccess (expr, base, accesstype, t)) + { + field = NULL; + type = t; + indirect = accesstype != NULL; + return true; + } + + srfield *f = t->find_field (offset); + if (!f) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nunkown field\n"); + print_generic_expr (dump_file, expr); + fprintf (dump_file, "\n"); + print_generic_expr (dump_file, base); + fprintf (dump_file, "\n"); + } + gcc_assert (can_escape); + t->mark_escape (escape_unkown_field, NULL); + return false; + } + if (!types_compatible_p (f->fieldtype, TREE_TYPE (expr))) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nfieldtype = "); + print_generic_expr (dump_file, f->fieldtype); + fprintf (dump_file, "\naccess type = "); + print_generic_expr (dump_file, TREE_TYPE (expr)); + fprintf (dump_file, "original expr = "); + print_generic_expr (dump_file, expr); + fprintf (dump_file, "\n"); + } + gcc_assert (can_escape); + t->mark_escape (escape_unkown_field, NULL); + return false; + } + field = f; + type = t; + indirect = accesstype != NULL; + return true; +} + +/* Mark the type used in EXPR as escaping. */ + +void +ipa_struct_reorg::mark_expr_escape (tree expr, escape_type escapes, + gimple *stmt) +{ + tree base; + bool indirect; + srtype *type; + srfield *field; + bool realpart, imagpart, address; + if (!get_type_field (expr, base, indirect, type, field, + realpart, imagpart, address)) + return; + + type->mark_escape (escapes, stmt); +} + +/* Record accesses in a call statement STMT. */ + +void +ipa_struct_reorg::maybe_record_call (cgraph_node *node, gcall *stmt) +{ + tree argtype; + tree fndecl; + escape_type escapes = does_not_escape; + bool free_or_realloc = gimple_call_builtin_p (stmt, BUILT_IN_FREE) + || gimple_call_builtin_p (stmt, BUILT_IN_REALLOC); + + /* We check allocation sites in a different location. */ + if (handled_allocation_stmt (stmt)) + return; + + /* A few cases here: + 1) assigned from the lhs + 2) Used in argument + If a function being called is global (or indirect) + then we reject the types as being escaping. */ + + if (tree chain = gimple_call_chain (stmt)) + record_stmt_expr (chain, node, stmt); + + /* Assigned from LHS. */ + if (tree lhs = gimple_call_lhs (stmt)) + { + /* FIXME: handle return types. */ + mark_type_as_escape (TREE_TYPE (lhs), escape_return); + } + + /* If we have an internal call, just record the stmt. */ + if (gimple_call_internal_p (stmt)) + { + for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) + record_stmt_expr (gimple_call_arg (stmt, i), node, stmt); + return; + } + + fndecl = gimple_call_fndecl (stmt); + + /* If we have an indrect call, just mark the types as escape. */ + if (!fndecl) + escapes = escape_pointer_function; + /* Non local functions cause escape except for calls to free + and realloc. + FIXME: should support function annotations too. */ + else if (!free_or_realloc + && !cgraph_node::local_info_node (fndecl)->local) + escapes = escape_external_function; + else if (!free_or_realloc + && !cgraph_node::local_info_node (fndecl)->can_change_signature) + escapes = escape_cannot_change_signature; + /* FIXME: we should be able to handle functions in other partitions. */ + else if (symtab_node::get (fndecl)->in_other_partition) + escapes = escape_external_function; + + if (escapes != does_not_escape) + { + for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) + mark_type_as_escape (TREE_TYPE (gimple_call_arg (stmt, i)), + escapes); + return; + } + + argtype = TYPE_ARG_TYPES (gimple_call_fntype (stmt)); + for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) + { + tree arg = gimple_call_arg (stmt, i); + if (argtype) + { + tree argtypet = TREE_VALUE (argtype); + if (!free_or_realloc + && VOID_POINTER_P (argtypet)) + mark_type_as_escape (TREE_TYPE (arg), escape_cast_void); + else + record_stmt_expr (arg, node, stmt); + } + else + mark_type_as_escape (TREE_TYPE (arg), escape_var_arg_function); + + argtype = argtype ? TREE_CHAIN (argtype) : NULL_TREE; + } +} + +void +ipa_struct_reorg::record_stmt_expr (tree expr, cgraph_node *node, gimple *stmt) +{ + tree base; + bool indirect; + srtype *type; + srfield *field; + bool realpart, imagpart, address; + if (!get_type_field (expr, base, indirect, type, field, + realpart, imagpart, address)) + return; + + if (!opt_for_fn (current_function_decl, flag_ipa_struct_reorg)) + type->mark_escape (escape_non_optimize, stmt); + + /* Record it. */ + type->add_access (new sraccess (stmt, node, type, field)); +} + +/* Find function corresponding to NODE. */ + +srfunction * +ipa_struct_reorg::find_function (cgraph_node *node) +{ + for (unsigned i = 0; i < functions.length (); i++) + if (functions[i]->node == node) + return functions[i]; + return NULL; +} + +void +ipa_struct_reorg::check_type_and_push (tree newdecl, srtype *type, + vec &worklist, + gimple *stmt) +{ + if (integer_zerop (newdecl)) + return; + + if (TREE_CODE (newdecl) == ADDR_EXPR) + { + srdecl *d = find_decl (TREE_OPERAND (newdecl, 0)); + if (!d) + { + type->mark_escape (escape_cast_another_ptr, stmt); + return; + } + if (d->type == type) + return; + + srtype *type1 = d->type; + type->mark_escape (escape_cast_another_ptr, stmt); + type1->mark_escape (escape_cast_another_ptr, stmt); + return; + } + + srdecl *d = find_decl (newdecl); + if (!d) + { + if (TREE_CODE (newdecl) == INTEGER_CST) + { + type->mark_escape (escape_int_const, stmt); + return; + } + /* If we have a non void* or a decl (which is hard to track), + then mark the type as escaping. */ + if (!VOID_POINTER_P (TREE_TYPE (newdecl)) + || DECL_P (newdecl)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nunkown decl: "); + print_generic_expr (dump_file, newdecl); + fprintf (dump_file, " in type:\n"); + print_generic_expr (dump_file, TREE_TYPE (newdecl)); + fprintf (dump_file, "\n"); + } + type->mark_escape (escape_cast_another_ptr, stmt); + return; + } + /* At this point there should only be unkown void* ssa names. */ + gcc_assert (TREE_CODE (newdecl) == SSA_NAME); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nrecording unkown decl: "); + print_generic_expr (dump_file, newdecl); + fprintf (dump_file, " as type:\n"); + type->simple_dump (dump_file); + fprintf (dump_file, "\n"); + } + d = current_function->record_decl (type, newdecl, -1); + worklist.safe_push (d); + return; + } + + /* Only add to the worklist if the decl is a SSA_NAME. */ + if (TREE_CODE (newdecl) == SSA_NAME) + worklist.safe_push (d); + if (d->type == type) + return; + + srtype *type1 = d->type; + type->mark_escape (escape_cast_another_ptr, stmt); + type1->mark_escape (escape_cast_another_ptr, stmt); +} + +/* + 2) Check SSA_NAMEs for non type usages (source or use) (worlist of srdecl) + a) if the SSA_NAME is sourced from a pointer plus, record the pointer and + check to make sure the addition was a multiple of the size. + check the pointer type too. + b) If the name is sourced from an allocation check the allocation + i) Add SSA_NAME (void*) to the worklist if allocated from realloc + c) if the name is from a param, make sure the param type was of the + original type + d) if the name is from a cast/assignment, make sure it is used as that + type or void* + i) If void* then push the ssa_name into worklist +*/ +void +ipa_struct_reorg::check_definition (srdecl *decl, vec &worklist) +{ + tree ssa_name = decl->decl; + srtype *type = decl->type; + + /* + c) if the name is from a param, make sure the param type was of the + original type. + */ + if (SSA_NAME_IS_DEFAULT_DEF (ssa_name)) + { + tree var = SSA_NAME_VAR (ssa_name); + if (var + && TREE_CODE (var) == PARM_DECL + && VOID_POINTER_P (TREE_TYPE (ssa_name))) + type->mark_escape (escape_cast_void, NULL); + return; + } + gimple *stmt = SSA_NAME_DEF_STMT (ssa_name); + + /* + b) If the name is sourced from an allocation check the allocation + i) Add SSA_NAME (void*) to the worklist if allocated from realloc + */ + if (gimple_code (stmt) == GIMPLE_CALL) + { + /* For realloc, check the type of the argument. */ + if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC)) + check_type_and_push (gimple_call_arg (stmt, 0), type, worklist, stmt); + + if (!handled_allocation_stmt (stmt) + || !allocate_size (type, stmt)) + type->mark_escape (escape_return, stmt); + return; + } + /* If the SSA_NAME is sourced from an inline-asm, + just mark the type as escaping. */ + if (gimple_code (stmt) == GIMPLE_ASM) + { + type->mark_escape (escape_inline_asm, stmt); + return; + } + + /* If the SSA_NAME is sourced from a PHI check add + each name to the worklist and check to make sure + they are used correctly. */ + if (gimple_code (stmt) == GIMPLE_PHI) + { + for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++) + check_type_and_push (gimple_phi_arg_def (stmt, i), + type, worklist, stmt); + return; + } + + gcc_assert (gimple_code (stmt) == GIMPLE_ASSIGN); + /* + a) if the SSA_NAME is sourced from a pointer plus, record the pointer and + check to make sure the addition was a multiple of the size. + check the pointer type too. + */ + + tree rhs = gimple_assign_rhs1 (stmt); + if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) + { + tree rhs2 = gimple_assign_rhs2 (stmt); + tree num; + if (!is_result_of_mult (rhs2, &num, TYPE_SIZE_UNIT (type->type))) + type->mark_escape (escape_non_multiply_size, stmt); + + if (TREE_CODE (rhs) == SSA_NAME) + check_type_and_push (rhs, type, worklist, stmt); + return; + } + + /* Casts between pointers and integer are escaping. */ + if (gimple_assign_cast_p (stmt)) + { + type->mark_escape (escape_cast_int, stmt); + return; + } + + /* + d) if the name is from a cast/assignment, make sure it is used as that + type or void* + i) If void* then push the ssa_name into worklist + */ + gcc_assert (gimple_assign_single_p (stmt)); + check_other_side (decl, rhs, stmt, worklist); +} + +/* Mark the types used by the inline-asm as escaping. + It is unkown what happens inside an inline-asm. */ + +void +ipa_struct_reorg::mark_types_asm (gasm *astmt) +{ + for (unsigned i = 0; i < gimple_asm_ninputs (astmt); i++) + { + tree v = TREE_VALUE (gimple_asm_input_op (astmt, i)); + /* If we have &b, just strip the & here. */ + if (TREE_CODE (v) == ADDR_EXPR) + v = TREE_OPERAND (v, 0); + mark_expr_escape (v, escape_inline_asm, astmt); + } + for (unsigned i = 0; i < gimple_asm_noutputs (astmt); i++) + { + tree v = TREE_VALUE (gimple_asm_output_op (astmt, i)); + /* If we have &b, just strip the & here. */ + if (TREE_CODE (v) == ADDR_EXPR) + v = TREE_OPERAND (v, 0); + mark_expr_escape (v, escape_inline_asm, astmt); + } +} + +void +ipa_struct_reorg::check_other_side (srdecl *decl, tree other, gimple *stmt, + vec &worklist) +{ + srtype *type = decl->type; + + if (TREE_CODE (other) == SSA_NAME + || DECL_P (other) + || TREE_CODE (other) == INTEGER_CST) + { + check_type_and_push (other, type, worklist, stmt); + return; + } + + tree t = TREE_TYPE (other); + if (!handled_type (t)) + { + type->mark_escape (escape_cast_another_ptr, stmt); + return; + } + + srtype *t1 = find_type (inner_type (t)); + if (t1 == type) + { + tree base; + bool indirect; + srtype *type1; + srfield *field; + bool realpart, imagpart, address; + if (!get_type_field (other, base, indirect, type1, field, + realpart, imagpart, address)) + type->mark_escape (escape_cast_another_ptr, stmt); + + return; + } + + if (t1) + t1->mark_escape (escape_cast_another_ptr, stmt); + + type->mark_escape (escape_cast_another_ptr, stmt); +} + +void +ipa_struct_reorg::check_use (srdecl *decl, gimple *stmt, + vec &worklist) +{ + srtype *type = decl->type; + + if (gimple_code (stmt) == GIMPLE_RETURN) + { + type->mark_escape (escape_return, stmt); + return; + } + /* If the SSA_NAME PHI check and add the src to the worklist and + check to make sure they are used correctly. */ + if (gimple_code (stmt) == GIMPLE_PHI) + { + check_type_and_push (gimple_phi_result (stmt), type, worklist, stmt); + return; + } + + if (gimple_code (stmt) == GIMPLE_ASM) + { + mark_types_asm (as_a (stmt)); + return; + } + + if (gimple_code (stmt) == GIMPLE_COND) + { + tree rhs1 = gimple_cond_lhs (stmt); + tree rhs2 = gimple_cond_rhs (stmt); + tree orhs = rhs1; + if (gimple_cond_code (stmt) != EQ_EXPR + && gimple_cond_code (stmt) != NE_EXPR) + { + mark_expr_escape (rhs1, escape_non_eq, stmt); + mark_expr_escape (rhs2, escape_non_eq, stmt); + } + if (rhs1 == decl->decl) + orhs = rhs2; + if (integer_zerop (orhs)) + return; + if (TREE_CODE (orhs) != SSA_NAME) + mark_expr_escape (rhs1, escape_non_eq, stmt); + check_type_and_push (orhs, type, worklist, stmt); + return; + } + + /* Casts between pointers and integer are escaping. */ + if (gimple_assign_cast_p (stmt)) + { + type->mark_escape (escape_cast_int, stmt); + return; + } + + /* We might have a_1 = ptr_2 == ptr_3; */ + if (is_gimple_assign (stmt) + && TREE_CODE_CLASS (gimple_assign_rhs_code (stmt)) == tcc_comparison) + { + tree rhs1 = gimple_assign_rhs1 (stmt); + tree rhs2 = gimple_assign_rhs2 (stmt); + tree orhs = rhs1; + if (gimple_assign_rhs_code (stmt) != EQ_EXPR + && gimple_assign_rhs_code (stmt) != NE_EXPR) + { + mark_expr_escape (rhs1, escape_non_eq, stmt); + mark_expr_escape (rhs2, escape_non_eq, stmt); + } + if (rhs1 == decl->decl) + orhs = rhs2; + if (integer_zerop (orhs)) + return; + if (TREE_CODE (orhs) != SSA_NAME) + mark_expr_escape (rhs1, escape_non_eq, stmt); + check_type_and_push (orhs, type, worklist, stmt); + return; + } + + if (gimple_assign_single_p (stmt)) + { + tree lhs = gimple_assign_lhs (stmt); + tree rhs = gimple_assign_rhs1 (stmt); + /* Check if we have a_1 = b_2; that a_1 is in the correct type. */ + if (decl->decl == rhs) + { + check_other_side (decl, lhs, stmt, worklist); + return; + } + } + + if (is_gimple_assign (stmt) + && gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) + { + tree rhs2 = gimple_assign_rhs2 (stmt); + tree lhs = gimple_assign_lhs (stmt); + tree num; + check_other_side (decl, lhs, stmt, worklist); + if (!is_result_of_mult (rhs2, &num, TYPE_SIZE_UNIT (type->type))) + type->mark_escape (escape_non_multiply_size, stmt); + } +} + +/* + 2) Check SSA_NAMEs for non type usages (source or use) (worlist of srdecl) + d) if the name is from a cast/assignment, make sure it is used as that + type or void* + i) If void* then push the ssa_name into worklist + e) if used in conditional check the other side + i) If the conditional is non NE/EQ then mark the type as non rejecting + f) Check if the use in a Pointer PLUS EXPR Is used by mulitplication + of its size + */ +void +ipa_struct_reorg::check_uses (srdecl *decl, vec &worklist) +{ + tree ssa_name = decl->decl; + imm_use_iterator imm_iter; + use_operand_p use_p; + + FOR_EACH_IMM_USE_FAST (use_p, imm_iter, ssa_name) + { + gimple *stmt = USE_STMT (use_p); + + if (is_gimple_debug (stmt)) + continue; + + check_use (decl, stmt, worklist); + } +} + +/* Record function corresponding to NODE. */ + +srfunction * +ipa_struct_reorg::record_function (cgraph_node *node) +{ + function *fn; + tree parm, var; + unsigned int i; + srfunction *sfn; + escape_type escapes = does_not_escape; + + sfn = new srfunction (node); + functions.safe_push (sfn); + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "\nRecording accesses and types from function: %s/%u\n", + node->name (), node->order); + + /* Nodes without a body are not interesting. Especially do not + visit clones at this point for now - we get duplicate decls + there for inline clones at least. */ + if (!node->has_gimple_body_p () || node->inlined_to) + return sfn; + + node->get_body (); + fn = DECL_STRUCT_FUNCTION (node->decl); + + if (!fn) + return sfn; + + current_function = sfn; + + if (DECL_PRESERVE_P (node->decl)) + escapes = escape_marked_as_used; + else if (!node->local) + escapes = escape_visible_function; + else if (!node->can_change_signature) + escapes = escape_cannot_change_signature; + else if (!tree_versionable_function_p (node->decl)) + escapes = escape_noclonable_function; + else if (!opt_for_fn (node->decl, flag_ipa_struct_reorg)) + escapes = escape_non_optimize; + + basic_block bb; + gimple_stmt_iterator si; + + /* Record the static chain decl. */ + if (fn->static_chain_decl) + { + srdecl *sd = record_var (fn->static_chain_decl, + escapes, -2); + if (sd) + { + /* Specify that this type is used by the static + chain so it cannot be split. */ + sd->type->chain_type = true; + sfn->add_arg (sd); + sd->type->add_function (sfn); + } + } + + /* Record the arguments. */ + for (parm = DECL_ARGUMENTS (node->decl), i = 0; + parm; + parm = DECL_CHAIN (parm), i++) + { + srdecl *sd = record_var (parm, escapes, i); + if (sd) + { + sfn->add_arg (sd); + sd->type->add_function (sfn); + } + } + + /* Mark the return type as escaping. */ + { + tree return_type = TREE_TYPE (TREE_TYPE (node->decl)); + mark_type_as_escape (return_type, escape_return, NULL); + } + + /* If the cfg does not exist for the function, + don't process the function. */ + if (!fn->cfg) + { + current_function = NULL; + return sfn; + } + + /* The following order is done for recording stage: + 0) Record all variables/SSA_NAMES that are of struct type + 1) Record MEM_REF/COMPONENT_REFs + a) Record SSA_NAMEs (void*) and record that as the accessed type. + */ + + push_cfun (fn); + + FOR_EACH_LOCAL_DECL (cfun, i, var) + { + if (TREE_CODE (var) != VAR_DECL) + continue; + + record_var (var); + } + + for (i = 1; i < num_ssa_names; ++i) + { + tree name = ssa_name (i); + if (!name + || has_zero_uses (name) + || virtual_operand_p (name)) + continue; + + record_var (name); + } + + /* Find the variables which are used via MEM_REF and are void* types. */ + FOR_EACH_BB_FN (bb, cfun) + { + for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) + { + gimple *stmt = gsi_stmt (si); + find_vars (stmt); + } + } + + auto_vec worklist; + for (unsigned i = 0; i < current_function->decls.length (); i++) + { + srdecl *decl = current_function->decls[i]; + if (TREE_CODE (decl->decl) == SSA_NAME) + { + decl->visited = false; + worklist.safe_push (decl); + } + } + +/* + 2) Check SSA_NAMEs for non type usages (source or use) (worlist of srdecl) + a) if the SSA_NAME is sourced from a pointer plus, record the pointer and + check to make sure the addition was a multiple of the size. + check the pointer type too. + b) If the name is sourced from an allocation check the allocation + i) Add SSA_NAME (void*) to the worklist if allocated from realloc + c) if the name is from a param, make sure the param type was of the + original type + d) if the name is used in a cast/assignment, make sure it is used as that + type or void* + i) If void* then push the ssa_name into worklist + e) if used in conditional check the other side + i) If the conditional is non NE/EQ then mark the type as non rejecting + f) Check if the use in a POinter PLUS EXPR Is used by mulitplication + of its size +*/ + + while (!worklist.is_empty ()) + { + srdecl *decl = worklist.pop (); + if (decl->visited) + continue; + decl->visited = true; + check_definition (decl, worklist); + check_uses (decl, worklist); + } + + FOR_EACH_BB_FN (bb, cfun) + { + for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) + { + gimple *stmt = gsi_stmt (si); + maybe_record_stmt (node, stmt); + } + } + + pop_cfun (); + current_function = NULL; + return sfn; +} + +/* Record all accesses for all types including global variables. */ + +void +ipa_struct_reorg::record_accesses (void) +{ + varpool_node *var; + cgraph_node *cnode; + + /* Record global (non-auto) variables first. */ + FOR_EACH_VARIABLE (var) + { + if (!var->real_symbol_p ()) + continue; + + /* Record all variables including the accesses inside a variable. */ + escape_type escapes = does_not_escape; + if (var->externally_visible || !var->definition) + escapes = escape_via_global_var; + if (var->in_other_partition) + escapes = escape_via_global_var; + if (!var->externally_visible && var->definition) + var->get_constructor (); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Recording global variable: "); + print_generic_expr (dump_file, var->decl); + fprintf (dump_file, "\n"); + } + record_var (var->decl, escapes); + } + + FOR_EACH_FUNCTION (cnode) + { + if (!cnode->real_symbol_p ()) + continue; + + /* Record accesses inside a function. */ + if (cnode->definition) + record_function (cnode); + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "all types (before pruning):\n"); + dump_types (dump_file); + fprintf (dump_file, "all functions (before pruning):\n"); + dump_functions (dump_file); + } + done_recording = true; +} + +/* A helper function to detect cycles (recusive) types. + Return TRUE if TYPE was a rescusive type. */ + +bool +ipa_struct_reorg::walk_field_for_cycles (srtype *type) +{ + unsigned i; + srfield *field; + + type->visited = true; + if (type->escaped_rescusive ()) + return true; + + if (type->has_escaped ()) + return false; + + FOR_EACH_VEC_ELT (type->fields, i, field) + { + if (!field->type) + ; + else if (field->type->visited + || walk_field_for_cycles (field->type)) + { + type->mark_escape (escape_rescusive_type, NULL); + return true; + } + } + + return false; +} + +/* Clear visited on all types. */ + +void +ipa_struct_reorg::clear_visited (void) +{ + for (unsigned i = 0; i < types.length (); i++) + types[i]->visited = false; +} + +/* Detect recusive types and mark them as escaping. */ + +void +ipa_struct_reorg::detect_cycles (void) +{ + for (unsigned i = 0; i < types.length (); i++) + { + if (types[i]->has_escaped ()) + continue; + + clear_visited (); + walk_field_for_cycles (types[i]); + } +} + +/* Propagate escaping to depdenent types. */ + +void +ipa_struct_reorg::propagate_escape (void) +{ + unsigned i; + srtype *type; + bool changed = false; + + do + { + changed = false; + FOR_EACH_VEC_ELT (types, i, type) + { + for (tree field = TYPE_FIELDS (type->type); + field; + field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL + && handled_type (TREE_TYPE (field))) + { + tree t = inner_type (TREE_TYPE (field)); + srtype *type1 = find_type (t); + if (!type1) + continue; + if (type1->has_escaped () + && !type->has_escaped ()) + { + type->mark_escape (escape_dependent_type_escapes, NULL); + changed = true; + } + if (type->has_escaped () + && !type1->has_escaped ()) + { + type1->mark_escape (escape_dependent_type_escapes, NULL); + changed = true; + } + } + } + } + } while (changed); +} + +/* Prune the escaped types and their decls from what was recorded. */ + +void +ipa_struct_reorg::prune_escaped_types (void) +{ + detect_cycles (); + propagate_escape (); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "all types (after prop but before pruning):\n"); + dump_types (dump_file); + fprintf (dump_file, "all functions (after prop but before pruning):\n"); + dump_functions (dump_file); + } + + if (dump_file) + dump_types_escaped (dump_file); + + /* Prune the function arguments which escape + and functions which have no types as arguments. */ + for (unsigned i = 0; i < functions.length ();) + { + srfunction *function = functions[i]; + + /* Prune function arguments of types that escape. */ + for (unsigned j = 0; j < function->args.length ();) + { + if (function->args[j]->type->has_escaped ()) + function->args.ordered_remove (j); + else + j++; + } + + /* Prune global variables that the function uses of types + that escape. */ + for (unsigned j = 0; j < function->globals.length ();) + { + if (function->globals[j]->type->has_escaped ()) + function->globals.ordered_remove (j); + else + j++; + } + + /* Prune variables that the function uses of types that escape. */ + for (unsigned j = 0; j < function->decls.length ();) + { + srdecl *decl = function->decls[j]; + if (decl->type->has_escaped ()) + { + function->decls.ordered_remove (j); + delete decl; + } + else + j++; + } + + /* Prune functions which don't refer to any variables any more. */ + if (function->args.is_empty () + && function->decls.is_empty () + && function->globals.is_empty ()) + { + delete function; + functions.ordered_remove (i); + } + else + i++; + } + + /* Prune globals of types that escape, all references to those decls + will have been removed in the first loop. */ + for (unsigned j = 0; j < globals.decls.length ();) + { + srdecl *decl = globals.decls[j]; + if (decl->type->has_escaped ()) + { + globals.decls.ordered_remove (j); + delete decl; + } + else + j++; + } + + /* Prune types that escape, all references to those types + will have been removed in the above loops. */ + for (unsigned i = 0; i < types.length ();) + { + srtype *type = types[i]; + if (type->has_escaped ()) + { + /* All references to this type should have been removed now. */ + delete type; + types.ordered_remove (i); + } + else + i++; + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "all types (after pruning):\n"); + dump_types (dump_file); + fprintf (dump_file, "all functions (after pruning):\n"); + dump_functions (dump_file); + } +} + +/* Analyze all of the types. */ + +void +ipa_struct_reorg::analyze_types (void) +{ + for (unsigned i = 0; i < types.length (); i++) + { + if (!types[i]->has_escaped ()) + types[i]->analyze (); + } +} + +/* When struct A has a struct B member, B's type info + is not stored in + TYPE_FIELDS (TREE_TYPE (TYPE_FIELDS (typeA))) + Try to restore B's type information. */ + +void +ipa_struct_reorg::restore_field_type (void) +{ + for (unsigned i = 0; i < types.length (); i++) + { + for (unsigned j = 0; j < types[i]->fields.length (); j++) + { + srfield *field = types[i]->fields[j]; + if (TREE_CODE (inner_type (field->fieldtype)) == RECORD_TYPE) + { + /* If field type has TYPE_FIELDS information, + we do not need to do this. */ + if (TYPE_FIELDS (field->type->type) != NULL) + continue; + for (unsigned k = 0; k < types.length (); k++) + { + if (i == k) + continue; + const char *type1 = get_type_name (field->type->type); + const char *type2 = get_type_name (types[k]->type); + if (type1 == NULL || type2 == NULL) + continue; + if (type1 == type2 + && TYPE_FIELDS (types[k]->type)) + field->type = types[k]; + } + } + } + } +} + +/* Create all new types we want to create. */ + +bool +ipa_struct_reorg::create_new_types (void) +{ + int newtypes = 0; + clear_visited (); + for (unsigned i = 0; i < types.length (); i++) + newtypes += types[i]->create_new_type (); + + if (dump_file) + { + if (newtypes) + fprintf (dump_file, "\nNumber of structures to transform is %d\n", + newtypes); + else + fprintf (dump_file, "\nNo structures to transform.\n"); + } + + return newtypes != 0; +} + +/* Create all the new decls except for the new arguments + which create_new_functions would have created. */ + +void +ipa_struct_reorg::create_new_decls (void) +{ + globals.create_new_decls (); + for (unsigned i = 0; i < functions.length (); i++) + functions[i]->create_new_decls (); +} + +/* Create the new arguments for the function corresponding to NODE. */ + +void +ipa_struct_reorg::create_new_args (cgraph_node *new_node) +{ + tree decl = new_node->decl; + auto_vec params; + push_function_arg_decls (¶ms, decl); + vec *adjs = NULL; + vec_safe_reserve (adjs, params.length ()); + for (unsigned i = 0; i < params.length (); i++) + { + struct ipa_adjusted_param adj; + tree parm = params[i]; + memset (&adj, 0, sizeof (adj)); + adj.base_index = i; + adj.prev_clone_index = i; + srtype *t = find_type (inner_type (TREE_TYPE (parm))); + if (!t + || t->has_escaped () + || !t->has_new_type ()) + { + adj.op = IPA_PARAM_OP_COPY; + vec_safe_push (adjs, adj); + continue; + } + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Creating a new argument for: "); + print_generic_expr (dump_file, params[i]); + fprintf (dump_file, " in function: "); + print_generic_expr (dump_file, decl); + fprintf (dump_file, "\n"); + } + adj.op = IPA_PARAM_OP_NEW; + adj.param_prefix_index = IPA_PARAM_PREFIX_REORG; + for (unsigned j = 0; j < max_split && t->newtype[j]; j++) + { + adj.type = reconstruct_complex_type (TREE_TYPE (parm), + t->newtype[j]); + vec_safe_push (adjs, adj); + } + } + ipa_param_body_adjustments *adjustments + = new ipa_param_body_adjustments (adjs, decl); + adjustments->modify_formal_parameters (); + auto_vec new_params; + push_function_arg_decls (&new_params, decl); + unsigned veclen = vec_safe_length (adjs); + for (unsigned i = 0; i < veclen; i++) + { + if ((*adjs)[i].op != IPA_PARAM_OP_NEW) + continue; + tree decl = params[(*adjs)[i].base_index]; + srdecl *d = find_decl (decl); + if (!d) + continue; + unsigned j = 0; + while (j < max_split && d->newdecl[j]) + j++; + d->newdecl[j] = new_params[i]; + } + + function *fn = DECL_STRUCT_FUNCTION (decl); + + if (!fn->static_chain_decl) + return; + srdecl *chain = find_decl (fn->static_chain_decl); + if (!chain) + return; + + srtype *type = chain->type; + tree orig_var = chain->decl; + const char *tname = NULL; + if (DECL_NAME (orig_var)) + tname = IDENTIFIER_POINTER (DECL_NAME (orig_var)); + gcc_assert (!type->newtype[1]); + tree new_name = NULL; + char *name = NULL; + if (tname) + { + name = concat (tname, ".reorg.0", NULL); + new_name = get_identifier (name); + free (name); + } + tree newtype1 = reconstruct_complex_type (TREE_TYPE (orig_var), + type->newtype[0]); + chain->newdecl[0] = build_decl (DECL_SOURCE_LOCATION (orig_var), + PARM_DECL, new_name, newtype1); + copy_var_attributes (chain->newdecl[0], orig_var); + fn->static_chain_decl = chain->newdecl[0]; +} + +/* Find the refered DECL in the current function or globals. + If this is a global decl, record that as being used + in the current function. */ + +srdecl * +ipa_struct_reorg::find_decl (tree decl) +{ + srdecl *d; + d = globals.find_decl (decl); + if (d) + { + /* Record the global usage in the current function. */ + if (!done_recording && current_function) + { + bool add = true; + /* No reason to add it to the current function if it is + already recorded as such. */ + for (unsigned i = 0; i < current_function->globals.length (); i++) + { + if (current_function->globals[i] == d) + { + add = false; + break; + } + } + if (add) + current_function->globals.safe_push (d); + } + return d; + } + if (current_function) + return current_function->find_decl (decl); + return NULL; +} + +/* Create new function clones for the cases where the arguments + need to be changed. */ + +void +ipa_struct_reorg::create_new_functions (void) +{ + for (unsigned i = 0; i < functions.length (); i++) + { + srfunction *f = functions[i]; + bool anyargchanges = false; + cgraph_node *new_node; + cgraph_node *node = f->node; + int newargs = 0; + if (f->old) + continue; + + if (f->args.length () == 0) + continue; + + for (unsigned j = 0; j < f->args.length (); j++) + { + srdecl *d = f->args[j]; + srtype *t = d->type; + if (t->has_new_type ()) + { + newargs += t->newtype[1] != NULL; + anyargchanges = true; + } + } + if (!anyargchanges) + continue; + + if (dump_file) + { + fprintf (dump_file, "Creating a clone of function: "); + f->simple_dump (dump_file); + fprintf (dump_file, "\n"); + } + statistics_counter_event (NULL, "Create new function", 1); + new_node = node->create_version_clone_with_body (vNULL, NULL, + NULL, NULL, NULL, + "struct_reorg"); + new_node->can_change_signature = node->can_change_signature; + new_node->make_local (); + f->newnode = new_node; + srfunction *n = record_function (new_node); + current_function = n; + n->old = f; + f->newf = n; + /* Create New arguments. */ + create_new_args (new_node); + current_function = NULL; + } +} + +bool +ipa_struct_reorg::rewrite_lhs_rhs (tree lhs, tree rhs, + tree newlhs[max_split], + tree newrhs[max_split]) +{ + bool l = rewrite_expr (lhs, newlhs); + bool r = rewrite_expr (rhs, newrhs); + + /* Handle NULL pointer specially. */ + if (l && !r && integer_zerop (rhs)) + { + r = true; + for (unsigned i = 0; i < max_split && newlhs[i]; i++) + newrhs[i] = fold_convert (TREE_TYPE (newlhs[i]), rhs); + } + + return l || r; +} + +bool +ipa_struct_reorg::rewrite_expr (tree expr, + tree newexpr[max_split], + bool ignore_missing_decl) +{ + tree base; + bool indirect; + srtype *t; + srfield *f; + bool realpart, imagpart; + bool address; + + tree newbase[max_split]; + memset (newexpr, 0, sizeof (tree[max_split])); + + if (TREE_CODE (expr) == CONSTRUCTOR) + { + srtype *t = find_type (TREE_TYPE (expr)); + if (!t) + return false; + gcc_assert (CONSTRUCTOR_NELTS (expr) == 0); + if (!t->has_new_type ()) + return false; + for (unsigned i = 0; i < max_split && t->newtype[i]; i++) + newexpr[i] = build_constructor (t->newtype[i], NULL); + return true; + } + + if (!get_type_field (expr, base, indirect, t, f, + realpart, imagpart, address)) + return false; + + /* If the type is not changed, then just return false. */ + if (!t->has_new_type ()) + return false; + + /* NULL pointer handling is "special". */ + if (integer_zerop (base)) + { + gcc_assert (indirect && !address); + for (unsigned i = 0; i < max_split && t->newtype[i]; i++) + { + tree newtype1 = reconstruct_complex_type (TREE_TYPE (base), + t->newtype[i]); + newbase[i] = fold_convert (newtype1, base); + } + } + else + { + srdecl *d = find_decl (base); + + if (!d && dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Can't find decl:\n"); + print_generic_expr (dump_file, base); + fprintf (dump_file, "\ntype:\n"); + t->dump (dump_file); + } + if (!d && ignore_missing_decl) + return true; + gcc_assert (d); + memcpy (newbase, d->newdecl, sizeof (d->newdecl)); + } + + if (f == NULL) + { + memcpy (newexpr, newbase, sizeof (newbase)); + for (unsigned i = 0; i < max_split && newexpr[i]; i++) + { + if (address) + newexpr[i] = build_fold_addr_expr (newexpr[i]); + if (indirect) + newexpr[i] = build_simple_mem_ref (newexpr[i]); + if (imagpart) + newexpr[i] = build1 (IMAGPART_EXPR, + TREE_TYPE (TREE_TYPE (newexpr[i])), + newexpr[i]); + if (realpart) + newexpr[i] = build1 (REALPART_EXPR, + TREE_TYPE (TREE_TYPE (newexpr[i])), + newexpr[i]); + } + return true; + } + + tree newdecl = newbase[f->clusternum]; + for (unsigned i = 0; i < max_split && f->newfield[i]; i++) + { + tree newbase1 = newdecl; + if (address) + newbase1 = build_fold_addr_expr (newbase1); + if (indirect) + newbase1 = build_simple_mem_ref (newbase1); + newexpr[i] = build3 (COMPONENT_REF, TREE_TYPE (f->newfield[i]), + newbase1, f->newfield[i], NULL_TREE); + if (imagpart) + newexpr[i] = build1 (IMAGPART_EXPR, + TREE_TYPE (TREE_TYPE (newexpr[i])), + newexpr[i]); + if (realpart) + newexpr[i] = build1 (REALPART_EXPR, + TREE_TYPE (TREE_TYPE (newexpr[i])), + newexpr[i]); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "cluster: %d. decl = ", (int)f->clusternum); + print_generic_expr (dump_file, newbase1); + fprintf (dump_file, "\nnewexpr = "); + print_generic_expr (dump_file, newexpr[i]); + fprintf (dump_file, "\n"); + } + } + return true; +} + +bool +ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) +{ + bool remove = false; + if (gimple_clobber_p (stmt)) + { + tree lhs = gimple_assign_lhs (stmt); + tree newlhs[max_split]; + if (!rewrite_expr (lhs, newlhs)) + return false; + for (unsigned i = 0; i < max_split && newlhs[i]; i++) + { + tree clobber = build_constructor (TREE_TYPE (newlhs[i]), NULL); + TREE_THIS_VOLATILE (clobber) = true; + gimple *newstmt = gimple_build_assign (newlhs[i], clobber); + gsi_insert_before (gsi, newstmt, GSI_SAME_STMT); + remove = true; + } + return remove; + } + + if (gimple_assign_rhs_code (stmt) == EQ_EXPR + || gimple_assign_rhs_code (stmt) == NE_EXPR) + { + tree rhs1 = gimple_assign_rhs1 (stmt); + tree rhs2 = gimple_assign_rhs2 (stmt); + tree newrhs1[max_split]; + tree newrhs2[max_split]; + tree_code rhs_code = gimple_assign_rhs_code (stmt); + tree_code code = rhs_code == EQ_EXPR ? BIT_AND_EXPR : BIT_IOR_EXPR; + if (!rewrite_lhs_rhs (rhs1, rhs2, newrhs1, newrhs2)) + return false; + tree newexpr = NULL_TREE; + for (unsigned i = 0; i < max_split && newrhs1[i]; i++) + { + tree expr = gimplify_build2 (gsi, rhs_code, boolean_type_node, + newrhs1[i], newrhs2[i]); + if (!newexpr) + newexpr = expr; + else + newexpr = gimplify_build2 (gsi, code, boolean_type_node, + newexpr, expr); + } + + if (newexpr) + { + newexpr = fold_convert (TREE_TYPE (gimple_assign_lhs (stmt)), + newexpr); + gimple_assign_set_rhs_from_tree (gsi, newexpr); + update_stmt (stmt); + } + return false; + } + + if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) + { + tree lhs = gimple_assign_lhs (stmt); + tree rhs1 = gimple_assign_rhs1 (stmt); + tree rhs2 = gimple_assign_rhs2 (stmt); + tree newlhs[max_split]; + tree newrhs[max_split]; + + if (!rewrite_lhs_rhs (lhs, rhs1, newlhs, newrhs)) + return false; + tree size = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (lhs))); + tree num; + /* Check if rhs2 is a multiplication of the size of the type. */ + if (!is_result_of_mult (rhs2, &num, size)) + internal_error ( + "The rhs of pointer is not a multiplicate and it slips through"); + + num = gimplify_build1 (gsi, NOP_EXPR, sizetype, num); + for (unsigned i = 0; i < max_split && newlhs[i]; i++) + { + gimple *new_stmt; + + tree newsize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (newlhs[i]))); + newsize = gimplify_build2 (gsi, MULT_EXPR, sizetype, num, newsize); + new_stmt = gimple_build_assign (newlhs[i], POINTER_PLUS_EXPR, + newrhs[i], newsize); + gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT); + remove = true; + } + return remove; + } + if (gimple_assign_rhs_class (stmt) == GIMPLE_SINGLE_RHS) + { + tree lhs = gimple_assign_lhs (stmt); + tree rhs = gimple_assign_rhs1 (stmt); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "rewriting statement:\n"); + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + } + tree newlhs[max_split]; + tree newrhs[max_split]; + if (!rewrite_lhs_rhs (lhs, rhs, newlhs, newrhs)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\nDid nothing to statement.\n"); + return false; + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\nreplaced with:\n"); + for (unsigned i = 0; i < max_split && (newlhs[i] || newrhs[i]); i++) + { + gimple *newstmt = gimple_build_assign (newlhs[i] ? newlhs[i] : lhs, + newrhs[i] ? newrhs[i] : rhs); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + print_gimple_stmt (dump_file, newstmt, 0); + fprintf (dump_file, "\n"); + } + gsi_insert_before (gsi, newstmt, GSI_SAME_STMT); + remove = true; + } + return remove; + } + + return remove; +} + +/* Rewrite function call statement STMT. Return TRUE if the statement + is to be removed. */ + +bool +ipa_struct_reorg::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi) +{ + /* Handled allocation calls are handled seperately from normal + function calls. */ + if (handled_allocation_stmt (stmt)) + { + tree lhs = gimple_call_lhs (stmt); + tree newrhs1[max_split]; + srdecl *decl = find_decl (lhs); + if (!decl || !decl->type) + return false; + srtype *type = decl->type; + tree num = allocate_size (type, stmt); + gcc_assert (num); + memset (newrhs1, 0, sizeof (newrhs1)); + + /* The realloc call needs to have its first argument rewritten. */ + if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC)) + { + tree rhs1 = gimple_call_arg (stmt, 0); + if (integer_zerop (rhs1)) + { + for (unsigned i = 0; i < max_split; i++) + newrhs1[i] = rhs1; + } + else if (!rewrite_expr (rhs1, newrhs1)) + internal_error ("Rewrite failed for realloc"); + } + + /* Go through each new lhs. */ + for (unsigned i = 0; i < max_split && decl->newdecl[i]; i++) + { + tree newsize = TYPE_SIZE_UNIT (type->type); + gimple *g; + /* Every allocation except for calloc needs + the size multiplied out. */ + if (!gimple_call_builtin_p (stmt, BUILT_IN_CALLOC)) + newsize = gimplify_build2 (gsi, MULT_EXPR, sizetype, num, newsize); + + if (gimple_call_builtin_p (stmt, BUILT_IN_MALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA)) + g = gimple_build_call (gimple_call_fndecl (stmt), + 1, newsize); + else if (gimple_call_builtin_p (stmt, BUILT_IN_CALLOC)) + g = gimple_build_call (gimple_call_fndecl (stmt), + 2, num, newsize); + else if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC)) + g = gimple_build_call (gimple_call_fndecl (stmt), + 2, newrhs1[i], newsize); + else + gcc_assert (false); + gimple_call_set_lhs (g, decl->newdecl[i]); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + } + return true; + } + + /* The function call free needs to be handled special. */ + if (gimple_call_builtin_p (stmt, BUILT_IN_FREE)) + { + tree expr = gimple_call_arg (stmt, 0); + tree newexpr[max_split]; + if (!rewrite_expr (expr, newexpr)) + return false; + + if (newexpr[1] == NULL) + { + gimple_call_set_arg (stmt, 0, newexpr[0]); + update_stmt (stmt); + return false; + } + + for (unsigned i = 0; i < max_split && newexpr[i]; i++) + { + gimple *g = gimple_build_call (gimple_call_fndecl (stmt), + 1, newexpr[i]); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + } + return true; + } + + /* Otherwise, look up the function to see if we have cloned it + and rewrite the arguments. */ + tree fndecl = gimple_call_fndecl (stmt); + + /* Indirect calls are already marked as escaping so ignore. */ + if (!fndecl) + return false; + + cgraph_node *node = cgraph_node::get (fndecl); + gcc_assert (node); + srfunction *f = find_function (node); + + /* Did not find the function or had not cloned it return saying don't + change the function call. */ + if (!f || !f->newf) + return false; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Changing arguments for function call :\n"); + print_gimple_expr (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + } + + /* Move over to the new function. */ + f = f->newf; + + tree chain = gimple_call_chain (stmt); + unsigned nargs = gimple_call_num_args (stmt); + auto_vec vargs (nargs); + + if (chain) + { + tree newchains[max_split]; + if (rewrite_expr (chain, newchains)) + { + /* Chain decl's type cannot be split and but it can change. */ + gcc_assert (newchains[1] == NULL); + chain = newchains[0]; + } + } + + for (unsigned i = 0; i < nargs; i++) + vargs.quick_push (gimple_call_arg (stmt, i)); + + int extraargs = 0; + + for (unsigned i = 0; i < f->args.length (); i++) + { + srdecl *d = f->args[i]; + if (d->argumentnum == -2) + continue; + gcc_assert (d->argumentnum != -1); + tree arg = vargs[d->argumentnum + extraargs]; + tree newargs[max_split]; + if (!rewrite_expr (arg, newargs)) + continue; + + /* If this ARG has a replacement handle the replacement. */ + for (unsigned j = 0; j < max_split && d->newdecl[j]; j++) + { + gcc_assert (newargs[j]); + /* If this is the first replacement of the arugment, + then just replace it. */ + if (j == 0) + vargs[d->argumentnum + extraargs] = newargs[j]; + else + { + /* More than one replacement, + we need to insert into the array. */ + extraargs++; + vargs.safe_insert (d->argumentnum + extraargs, newargs[j]); + } + } + } + + gcall *new_stmt; + + new_stmt = gimple_build_call_vec (f->node->decl, vargs); + + if (gimple_call_lhs (stmt)) + gimple_call_set_lhs (new_stmt, gimple_call_lhs (stmt)); + + gimple_set_vuse (new_stmt, gimple_vuse (stmt)); + gimple_set_vdef (new_stmt, gimple_vdef (stmt)); + + if (gimple_has_location (stmt)) + gimple_set_location (new_stmt, gimple_location (stmt)); + gimple_call_copy_flags (new_stmt, stmt); + gimple_call_set_chain (new_stmt, chain); + + gimple_set_modified (new_stmt, true); + + if (gimple_vdef (new_stmt) + && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME) + SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt; + + gsi_replace (gsi, new_stmt, false); + + /* We need to defer cleaning EH info on the new statement to + fixup-cfg. We may not have dominator information at this point + and thus would end up with unreachable blocks and have no way + to communicate that we need to run CFG cleanup then. */ + int lp_nr = lookup_stmt_eh_lp (stmt); + if (lp_nr != 0) + { + remove_stmt_from_eh_lp (stmt); + add_stmt_to_eh_lp (new_stmt, lp_nr); + } + + return false; +} + +/* Rewrite the conditional statement STMT. Return TRUE if the + old statement is to be removed. */ + +bool +ipa_struct_reorg::rewrite_cond (gcond *stmt, gimple_stmt_iterator *gsi) +{ + tree_code rhs_code = gimple_cond_code (stmt); + + /* Handle only equals or not equals conditionals. */ + if (rhs_code != EQ_EXPR + && rhs_code != NE_EXPR) + return false; + tree rhs1 = gimple_cond_lhs (stmt); + tree rhs2 = gimple_cond_rhs (stmt); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "COND: Rewriting\n"); + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + print_generic_expr (dump_file, rhs1); + fprintf (dump_file, "\n"); + print_generic_expr (dump_file, rhs2); + fprintf (dump_file, "\n"); + } + + tree newrhs1[max_split]; + tree newrhs2[max_split]; + tree_code code = rhs_code == EQ_EXPR ? BIT_AND_EXPR : BIT_IOR_EXPR; + if (!rewrite_lhs_rhs (rhs1, rhs2, newrhs1, newrhs2)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\nDid nothing to statement.\n"); + return false; + } + + tree newexpr = NULL_TREE; + for (unsigned i = 0; i < max_split && newrhs1[i]; i++) + { + tree expr = gimplify_build2 (gsi, rhs_code, boolean_type_node, + newrhs1[i], newrhs2[i]); + if (!newexpr) + newexpr = expr; + else + newexpr = gimplify_build2 (gsi, code, boolean_type_node, + newexpr, expr); + } + + if (newexpr) + { + gimple_cond_set_lhs (stmt, newexpr); + gimple_cond_set_rhs (stmt, boolean_true_node); + update_stmt (stmt); + } + return false; +} + +/* Rewrite debug statments if possible. Return TRUE if the statement + should be removed. */ + +bool +ipa_struct_reorg::rewrite_debug (gimple *stmt, gimple_stmt_iterator *) +{ + bool remove = false; + if (gimple_debug_bind_p (stmt)) + { + tree var = gimple_debug_bind_get_var (stmt); + tree newvar[max_split]; + if (rewrite_expr (var, newvar, true)) + remove = true; + if (gimple_debug_bind_has_value_p (stmt)) + { + var = gimple_debug_bind_get_value (stmt); + if (TREE_CODE (var) == POINTER_PLUS_EXPR) + var = TREE_OPERAND (var, 0); + if (rewrite_expr (var, newvar, true)) + remove = true; + } + } + else if (gimple_debug_source_bind_p (stmt)) + { + tree var = gimple_debug_source_bind_get_var (stmt); + tree newvar[max_split]; + if (rewrite_expr (var, newvar, true)) + remove = true; + var = gimple_debug_source_bind_get_value (stmt); + if (TREE_CODE (var) == POINTER_PLUS_EXPR) + var = TREE_OPERAND (var, 0); + if (rewrite_expr (var, newvar, true)) + remove = true; + } + + return remove; +} + +/* Rewrite PHI nodes, return true if the PHI was replaced. */ + +bool +ipa_struct_reorg::rewrite_phi (gphi *phi) +{ + tree newlhs[max_split]; + gphi *newphi[max_split]; + tree result = gimple_phi_result (phi); + gphi_iterator gsi; + + memset (newphi, 0, sizeof (newphi)); + + if (!rewrite_expr (result, newlhs)) + return false; + + if (newlhs[0] == NULL) + return false; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nrewriting PHI:"); + print_gimple_stmt (dump_file, phi, 0); + } + + for (unsigned i = 0; i < max_split && newlhs[i]; i++) + newphi[i] = create_phi_node (newlhs[i], gimple_bb (phi)); + + for (unsigned i = 0; i < gimple_phi_num_args (phi); i++) + { + tree newrhs[max_split]; + phi_arg_d rhs = *gimple_phi_arg (phi, i); + rewrite_expr (rhs.def, newrhs); + for (unsigned j = 0; j < max_split && newlhs[j]; j++) + { + SET_PHI_ARG_DEF (newphi[j], i, newrhs[j]); + gimple_phi_arg_set_location (newphi[j], i, rhs.locus); + update_stmt (newphi[j]); + } + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\ninto\n:"); + for (unsigned i = 0; i < max_split && newlhs[i]; i++) + { + print_gimple_stmt (dump_file, newphi[i], 0); + fprintf (dump_file, "\n"); + } + } + + gsi = gsi_for_phi (phi); + remove_phi_node (&gsi, false); + + return true; +} + +/* Rewrite gimple statement STMT, return true if the STATEMENT + is to be removed. */ + +bool +ipa_struct_reorg::rewrite_stmt (gimple *stmt, gimple_stmt_iterator *gsi) +{ + switch (gimple_code (stmt)) + { + case GIMPLE_ASSIGN: + return rewrite_assign (as_a (stmt), gsi); + case GIMPLE_CALL: + return rewrite_call (as_a (stmt), gsi); + case GIMPLE_COND: + return rewrite_cond (as_a (stmt), gsi); + break; + case GIMPLE_GOTO: + case GIMPLE_SWITCH: + break; + case GIMPLE_DEBUG: + case GIMPLE_ASM: + break; + default: + break; + } + return false; +} + +/* Does the function F uses any decl which has changed. */ + +bool +ipa_struct_reorg::has_rewritten_type (srfunction *f) +{ + for (unsigned i = 0; i < f->decls.length (); i++) + { + srdecl *d = f->decls[i]; + if (d->newdecl[0] != d->decl) + return true; + } + + for (unsigned i = 0; i < f->globals.length (); i++) + { + srdecl *d = f->globals[i]; + if (d->newdecl[0] != d->decl) + return true; + } + return false; +} + +/* Rewrite the functions if needed, return + the TODOs requested. */ + +unsigned +ipa_struct_reorg::rewrite_functions (void) +{ + unsigned retval = 0; + + restore_field_type (); + /* Create new types, if we did not create any new types, + then don't rewrite any accesses. */ + if (!create_new_types ()) + return 0; + + if (functions.length ()) + { + retval = TODO_remove_functions; + create_new_functions (); + } + + create_new_decls (); + + for (unsigned i = 0; i < functions.length (); i++) + { + srfunction *f = functions[i]; + if (f->newnode) + continue; + + /* Function uses no rewriten types so don't cause a rewrite. */ + if (!has_rewritten_type (f)) + continue; + + cgraph_node *node = f->node; + basic_block bb; + + push_cfun (DECL_STRUCT_FUNCTION (node->decl)); + current_function = f; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nBefore rewrite:\n"); + dump_function_to_file (current_function_decl, dump_file, + dump_flags | TDF_VOPS); + } + FOR_EACH_BB_FN (bb, cfun) + { + for (gphi_iterator si = gsi_start_phis (bb); !gsi_end_p (si);) + { + if (rewrite_phi (si.phi ())) + si = gsi_start_phis (bb); + else + gsi_next (&si); + } + + for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si);) + { + gimple *stmt = gsi_stmt (si); + if (rewrite_stmt (stmt, &si)) + gsi_remove (&si, true); + else + gsi_next (&si); + } + } + + /* Debug statements need to happen after all other statements + have changed. */ + FOR_EACH_BB_FN (bb, cfun) + { + for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si);) + { + gimple *stmt = gsi_stmt (si); + if (gimple_code (stmt) == GIMPLE_DEBUG + && rewrite_debug (stmt, &si)) + gsi_remove (&si, true); + else + gsi_next (&si); + } + } + + /* Release the old SSA_NAMES for old arguments. */ + if (f->old) + { + for (unsigned i = 0; i < f->args.length (); i++) + { + srdecl *d = f->args[i]; + if (d->newdecl[0] != d->decl) + { + tree ssa_name = ssa_default_def (cfun, d->decl); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Found "); + print_generic_expr (dump_file, ssa_name); + fprintf (dump_file, " to be released.\n"); + } + release_ssa_name (ssa_name); + } + } + } + + update_ssa (TODO_update_ssa_only_virtuals); + + if (flag_tree_pta) + compute_may_aliases (); + + remove_unused_locals (); + + cgraph_edge::rebuild_edges (); + + free_dominance_info (CDI_DOMINATORS); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nAfter rewrite:\n"); + dump_function_to_file (current_function_decl, dump_file, + dump_flags | TDF_VOPS); + } + + pop_cfun (); + current_function = NULL; + } + + return retval | TODO_verify_all; +} + +unsigned int +ipa_struct_reorg::execute (void) +{ + /* FIXME: If there is a top-level inline-asm, + the pass immediately returns. */ + if (symtab->first_asm_symbol ()) + return 0; + record_accesses (); + prune_escaped_types (); + analyze_types (); + + return rewrite_functions (); +} + +const pass_data pass_data_ipa_struct_reorg = +{ + SIMPLE_IPA_PASS, // type + "struct_reorg", // name + OPTGROUP_NONE, // optinfo_flags + TV_IPA_STRUCT_REORG, // tv_id + 0, // properties_required + 0, // properties_provided + 0, // properties_destroyed + 0, // todo_flags_start + 0, // todo_flags_finish +}; + +class pass_ipa_struct_reorg : public simple_ipa_opt_pass +{ +public: + pass_ipa_struct_reorg (gcc::context *ctxt) + : simple_ipa_opt_pass (pass_data_ipa_struct_reorg, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *); + virtual unsigned int execute (function *) + { + return ipa_struct_reorg ().execute (); + } + +}; // class pass_ipa_struct_reorg + +bool +pass_ipa_struct_reorg::gate (function *) +{ + return (optimize + && flag_ipa_struct_reorg + /* Don't bother doing anything if the program has errors. */ + && !seen_error ()); +} + +} // anon namespace + + +simple_ipa_opt_pass * +make_pass_ipa_struct_reorg (gcc::context *ctxt) +{ + return new pass_ipa_struct_reorg (ctxt); +} \ No newline at end of file diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.h b/gcc/ipa-struct-reorg/ipa-struct-reorg.h new file mode 100644 index 00000000000..a5879407012 --- /dev/null +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.h @@ -0,0 +1,235 @@ +/* Struct-reorg optimizations. + Copyright (C) 2016-2023 Free Software Foundation, Inc. + Contributed by Andrew Pinski + +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 +. */ + +#ifndef IPA_STRUCT_REORG_H +#define IPA_STRUCT_REORG_H + +namespace struct_reorg { + +const int max_split = 2; + +template +struct auto_vec_del : auto_vec +{ + ~auto_vec_del (); +}; + +template +auto_vec_del::~auto_vec_del (void) +{ + unsigned i; + T *t; + FOR_EACH_VEC_ELT (*this, i, t) + { + delete t; + } +} + +enum escape_type +{ + does_not_escape, +#define DEF_ESCAPE(ENUM, TEXT) ENUM, +#include "escapes.def" + escape_max_escape +}; + +const char *escape_type_string[escape_max_escape - 1] = +{ +#define DEF_ESCAPE(ENUM, TEXT) TEXT, +#include "escapes.def" +}; + +struct srfield; +struct srtype; +struct sraccess; +struct srdecl; +struct srfunction; + +struct srfunction +{ + cgraph_node *node; + auto_vec args; + auto_vec globals; + auto_vec_del decls; + srdecl *record_decl (srtype *, tree, int arg); + + srfunction *old; + cgraph_node *newnode; + srfunction *newf; + + // Constructors + srfunction (cgraph_node *n); + + // Methods + void add_arg (srdecl *arg); + void dump (FILE *file); + void simple_dump (FILE *file); + + bool check_args (void); + void create_new_decls (void); + srdecl *find_decl (tree); +}; + +struct srglobal : private srfunction +{ + srglobal () + : srfunction (NULL) + {} + + using srfunction::dump; + using srfunction::create_new_decls; + using srfunction::find_decl; + using srfunction::record_decl; + using srfunction::decls; +}; + +struct srtype +{ + tree type; + auto_vec_del fields; + + // array of fields that use this type. + auto_vec field_sites; + + // array of functions which use directly the type + auto_vec functions; + + auto_vec_del accesses; + bool chain_type; + +private: + escape_type escapes; + +public: + tree newtype[max_split]; + bool visited; + + // Constructors + srtype (tree type); + + // Methods + void dump (FILE *file); + void simple_dump (FILE *file); + void add_function (srfunction *); + void add_access (sraccess *a) + { + accesses.safe_push (a); + } + void add_field_site (srfield *); + + srfield *find_field (unsigned HOST_WIDE_INT offset); + + bool create_new_type (void); + void analyze (void); + void mark_escape (escape_type, gimple *stmt); + bool has_escaped (void) + { + return escapes != does_not_escape; + } + const char *escape_reason (void) + { + if (!has_escaped ()) + return NULL; + return escape_type_string[escapes - 1]; + } + bool escaped_rescusive (void) + { + return escapes == escape_rescusive_type; + } + bool has_new_type (void) + { + return newtype[0] && newtype[0] != type; + } +}; + +struct srfield +{ + unsigned HOST_WIDE_INT offset; + tree fieldtype; + tree fielddecl; + srtype *base; + srtype *type; + + unsigned clusternum; + + tree newfield[max_split]; + + // Constructors + srfield (tree field, srtype *base); + + // Methods + void dump (FILE *file); + void simple_dump (FILE *file); + + void create_new_fields (tree newtype[max_split], + tree newfields[max_split], + tree newlast[max_split]); +}; + +struct sraccess +{ + gimple *stmt; + cgraph_node *node; + + srtype *type; + // NULL field means the whole type is accessed + srfield *field; + + // Constructors + sraccess (gimple *s, cgraph_node *n, srtype *t, srfield *f = NULL) + : stmt (s), + node (n), + type (t), + field (f) + {} + + // Methods + void dump (FILE *file); +}; + +struct srdecl +{ + srtype *type; + tree decl; + tree func; + /* -1 : not an argument + -2 : static chain + */ + int argumentnum; + + bool visited; + + tree newdecl[max_split]; + + // Constructors + srdecl (srtype *type, tree decl, int argumentnum = -1); + + // Methods + void dump (FILE *file); + bool has_new_decl (void) + { + return newdecl[0] && newdecl[0] != decl; + } +}; + + +} // namespace struct_reorg + +#endif diff --git a/gcc/params.opt b/gcc/params.opt index e0ff9e21054..1ddf1343f0a 100644 --- a/gcc/params.opt +++ b/gcc/params.opt @@ -865,6 +865,10 @@ Enum(parloops_schedule_type) String(runtime) Value(PARLOOPS_SCHEDULE_RUNTIME) Common Joined UInteger Var(param_partial_inlining_entry_probability) Init(70) Optimization IntegerRange(0, 100) Param Maximum probability of the entry BB of split region (in percent relative to entry BB of the function) to make partial inlining happen. +-param=struct-reorg-cold-struct-ratio= +Common Joined UInteger Var(param_struct_reorg_cold_struct_ratio) Init(10) IntegerRange(0, 100) Param Optimization +The threshold ratio between current and hottest structure counts. + -param=predictable-branch-outcome= Common Joined UInteger Var(param_predictable_branch_outcome) Init(2) IntegerRange(0, 50) Param Optimization Maximal estimated outcome of branch considered predictable. diff --git a/gcc/passes.def b/gcc/passes.def index 375d3d62d51..1c1658c4a85 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -177,6 +177,8 @@ along with GCC; see the file COPYING3. If not see compiled unit. */ INSERT_PASSES_AFTER (all_late_ipa_passes) NEXT_PASS (pass_ipa_pta); + /* FIXME: this should be a normal IP pass. */ + NEXT_PASS (pass_ipa_struct_reorg); NEXT_PASS (pass_omp_simd_clone); TERMINATE_PASS_LIST (all_late_ipa_passes) diff --git a/gcc/testsuite/gcc.dg/struct/struct-reorg.exp b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp new file mode 100644 index 00000000000..43913104ee9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp @@ -0,0 +1,35 @@ +# Copyright (C) 1997-2023 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 +# . + +load_lib gcc-dg.exp +load_lib torture-options.exp + +# Initialize `dg'. +dg-init +torture-init + +set STRUCT_REORG_TORTURE_OPTIONS [list \ + { -O3 } \ + { -Ofast } ] + +set-torture-options $STRUCT_REORG_TORTURE_OPTIONS {{}} + +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.c]] \ + "" "-fipa-struct-reorg -fdump-ipa-all -flto-partition=one -fwhole-program" + +# All done. +torture-finish +dg-finish diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-1.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-1.c new file mode 100644 index 00000000000..6565fe8dd63 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-1.c @@ -0,0 +1,24 @@ +// { dg-do compile } +// { dg-options "-O3 -flto-partition=one -fipa-struct-reorg -fdump-ipa-all" } + +struct a +{ + int t, t1; +}; + +static struct a *b; + +void *xmalloc(int); + + +void f(void) +{ + b = xmalloc (sizeof(*b)); +} + +int g(void) +{ + return b->t; +} + +/* { dg-final { scan-ipa-dump "No structures to transform." "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-2.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-2.c new file mode 100644 index 00000000000..44babd35b04 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-2.c @@ -0,0 +1,29 @@ +// { dg-do run } + +#include + +struct a +{ + int t; + int t1; +}; + +__attribute__((noinline)) int f(int i, int j) +{ + struct a *t; + struct a t1 = {i, j}; + t = &t1; + auto int g(void) __attribute__((noinline)); + int g(void) + { + return t->t + t->t1; + } + return g(); +} + +int main() +{ + assert (f(1, 2) == 3); +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-3.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-3.c new file mode 100644 index 00000000000..5864ad46fd3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-3.c @@ -0,0 +1,23 @@ +// { dg-do compile } +// { dg-options "-O3 -flto-partition=one -fipa-struct-reorg -fdump-ipa-all" } + +#include +typedef struct { + long laststart_offset; + unsigned regnum; +} compile_stack_elt_t; +typedef struct { + compile_stack_elt_t *stack; + unsigned size; +} compile_stack_type; +void f (const char *p, const char *pend, int c) +{ + compile_stack_type compile_stack; + while (p != pend) + if (c) + compile_stack.stack = realloc (compile_stack.stack, + (compile_stack.size << 1) + * sizeof (compile_stack_elt_t)); +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-4.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-4.c new file mode 100644 index 00000000000..e5a8a6c847f --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-4.c @@ -0,0 +1,59 @@ +/* { dg-do run } */ + +extern void abort (void); + +struct S +{ + int b; + int *c; +}; +static int d, e; + +static struct S s; + +static int * +__attribute__((noinline, const)) +foo (void) +{ + return &s.b; +} + +int * +__attribute__((noinline)) +bar (int **f) +{ + s.c = &d; + *f = &e; + /* As nothing ever takes the address of any int * field in struct S, + the write to *f can't alias with the s.c field. */ + return s.c; +} + +int +__attribute__((noinline)) +baz (int *x) +{ + s.b = 1; + *x = 4; + /* Function foo takes address of an int field in struct S, + so *x can alias with the s.b field (and it does in this testcase). */ + return s.b; +} + +int +__attribute__((noinline)) +t (void) +{ + int *f = (int *) 0; + return 10 * (bar (&f) != &d) + baz (foo ()); +} + +int +main (void) +{ + if (t () != 4) + abort (); + return 0; +} + +/* { dg-final { scan-ipa-dump "No structures to transform." "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/w_prof_global_array.c b/gcc/testsuite/gcc.dg/struct/w_prof_global_array.c new file mode 100644 index 00000000000..733413a94d0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/w_prof_global_array.c @@ -0,0 +1,29 @@ +#include +typedef struct +{ + int a; + float b; +}str_t; + +#define N 1000 +str_t A[N]; + +int +main () +{ + int i; + + for (i = 0; i < N; i++) + { + A[i].a = 0; + } + + for (i = 0; i < N; i++) + if (A[i].a != 0) + abort (); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" { xfail *-*-* } } } */ diff --git a/gcc/testsuite/gcc.dg/struct/w_prof_global_var.c b/gcc/testsuite/gcc.dg/struct/w_prof_global_var.c new file mode 100644 index 00000000000..0ef686e74e7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/w_prof_global_var.c @@ -0,0 +1,42 @@ +#include +typedef struct +{ + int a; + float b; +}str_t; + +#ifdef STACK_SIZE +#if STACK_SIZE > 8000 +#define N 1000 +#else +#define N (STACK_SIZE/8) +#endif +#else +#define N 1000 +#endif + +str_t *p; + +int +main () +{ + int i, sum; + + p = malloc (N * sizeof (str_t)); + if (p == NULL) + return 0; + for (i = 0; i < N; i++) + p[i].b = i; + + for (i = 0; i < N; i++) + p[i].a = p[i].b + 1; + + for (i = 0; i < N; i++) + if (p[i].a != p[i].b + 1) + abort (); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/w_prof_local_array.c b/gcc/testsuite/gcc.dg/struct/w_prof_local_array.c new file mode 100644 index 00000000000..23a53be5386 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/w_prof_local_array.c @@ -0,0 +1,37 @@ +#include +typedef struct +{ + int a; + float b; +}str_t; + +#ifdef STACK_SIZE +#if STACK_SIZE > 8000 +#define N 1000 +#else +#define N (STACK_SIZE/8) +#endif +#else +#define N 1000 +#endif + +int +main () +{ + int i; + str_t A[N]; + + for (i = 0; i < N; i++) + { + A[i].a = 0; + } + + for (i = 0; i < N; i++) + if (A[i].a != 0) + abort (); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" { xfail *-*-* } } } */ diff --git a/gcc/testsuite/gcc.dg/struct/w_prof_local_var.c b/gcc/testsuite/gcc.dg/struct/w_prof_local_var.c new file mode 100644 index 00000000000..0cbb172f269 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/w_prof_local_var.c @@ -0,0 +1,40 @@ +#include +typedef struct +{ + int a; + float b; +}str_t; + +#ifdef STACK_SIZE +#if STACK_SIZE > 8000 +#define N 1000 +#else +#define N (STACK_SIZE/8) +#endif +#else +#define N 1000 +#endif + +int +main () +{ + int i, sum; + + str_t * p = malloc (N * sizeof (str_t)); + if (p == NULL) + return 0; + for (i = 0; i < N; i++) + p[i].b = i; + + for (i = 0; i < N; i++) + p[i].a = p[i].b + 1; + + for (i = 0; i < N; i++) + if (p[i].a != p[i].b + 1) + abort (); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/w_prof_single_str_global.c b/gcc/testsuite/gcc.dg/struct/w_prof_single_str_global.c new file mode 100644 index 00000000000..f900b1349ac --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/w_prof_single_str_global.c @@ -0,0 +1,31 @@ +#include +typedef struct +{ + int a; + int b; +}str_t; + +#define N 3 + +str_t str; + +int +main () +{ + int i; + int res = 1<<(1< + +typedef struct +{ + int a; + float b; +}str_t1; + +typedef struct +{ + int c; + float d; +}str_t2; + +#ifdef STACK_SIZE +#if STACK_SIZE > 16000 +#define N 1000 +#else +#define N (STACK_SIZE/16) +#endif +#else +#define N 1000 +#endif + +str_t1 *p1; +str_t2 *p2; +int num; + +void +foo (void) +{ + int i; + + for (i=0; i < num; i++) + p2[i].c = 2; +} + +int +main () +{ + int i, r; + + r = rand (); + num = r > N ? N : r; + p1 = malloc (num * sizeof (str_t1)); + p2 = malloc (num * sizeof (str_t2)); + + if (p1 == NULL || p2 == NULL) + return 0; + + for (i = 0; i < num; i++) + p1[i].a = 1; + + foo (); + + for (i = 0; i < num; i++) + if (p1[i].a != 1 || p2[i].c != 2) + abort (); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/w_ratio_cold_str.c b/gcc/testsuite/gcc.dg/struct/w_ratio_cold_str.c new file mode 100644 index 00000000000..dcc545964fd --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/w_ratio_cold_str.c @@ -0,0 +1,43 @@ +#include +typedef struct +{ + int a; + int b; +}str_t1; + +typedef struct +{ + float a; + float b; +}str_t2; + +#define N1 1000 +#define N2 100 +str_t1 A1[N1]; +str_t2 A2[N2]; + +int +main () +{ + int i; + + for (i = 0; i < N1; i++) + A1[i].a = 0; + + for (i = 0; i < N2; i++) + A2[i].a = 0; + + for (i = 0; i < N1; i++) + if (A1[i].a != 0) + abort (); + + for (i = 0; i < N2; i++) + if (A2[i].a != 0) + abort (); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* Arrays are not handled. */ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" { xfail *-*-* } } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_array_field.c b/gcc/testsuite/gcc.dg/struct/wo_prof_array_field.c new file mode 100644 index 00000000000..6d6375fc1ea --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_array_field.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include +typedef struct basic +{ + int a; + int b[10]; +} type_struct; + +type_struct *str1; + +int main() +{ + int i; + + str1 = malloc (10 * sizeof (type_struct)); + + for (i=0; i<=9; i++) + str1[i].a = str1[i].b[0]; + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" { xfail *-*-* } } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_array_through_pointer.c b/gcc/testsuite/gcc.dg/struct/wo_prof_array_through_pointer.c new file mode 100644 index 00000000000..9d32134089f --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_array_through_pointer.c @@ -0,0 +1,38 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include +typedef struct +{ + int a; + float b; +}str_t; + +#ifdef STACK_SIZE +#if STACK_SIZE > 8000 +#define N 1000 +#else +#define N (STACK_SIZE/8) +#endif +#else +#define N 1000 +#endif + +int +main () +{ + int i; + str_t A[N]; + str_t *p = A; + + for (i = 0; i < N; i++) + p[i].a = 0; + + for (i = 0; i < N; i++) + if (p[i].a != 0) + abort (); + + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" { xfail *-*-* } } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_double_malloc.c b/gcc/testsuite/gcc.dg/struct/wo_prof_double_malloc.c new file mode 100644 index 00000000000..d79992a5302 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_double_malloc.c @@ -0,0 +1,29 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include + +typedef struct test_struct +{ + int a; + int b; +} type_struct; + +typedef type_struct **struct_pointer2; + +struct_pointer2 str1; + +int main() +{ + int i, j; + + str1 = malloc (2 * sizeof (type_struct *)); + + for (i = 0; i <= 1; i++) + str1[i] = malloc (2 * sizeof (type_struct)); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" { xfail *-*-* } } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_empty_str.c b/gcc/testsuite/gcc.dg/struct/wo_prof_empty_str.c new file mode 100644 index 00000000000..ee9b0d76595 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_empty_str.c @@ -0,0 +1,44 @@ +/* { dg-do run } */ + +#include + +struct S { int a; struct V *b; }; +typedef struct { int c; } T; +typedef struct { int d; int e; } U; + +void * +fn (void *x) +{ + return x; +} + +int +foo (struct S *s) +{ + T x; + + T y = *(T *)fn (&x); + return y.c; +} + +int +bar (struct S *s) +{ + U x; + + U y = *(U *)fn (&x); + return y.d + s->a; +} + +int +main () +{ + struct S s; + + foo(&s) + bar (&s); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "No structures to transform" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_escape_arg_to_local.c b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_arg_to_local.c new file mode 100644 index 00000000000..9ebb2b4cc96 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_arg_to_local.c @@ -0,0 +1,44 @@ +/* { dg-do run } */ + +#include +struct str +{ + int a; + float b; +}; + +#ifdef STACK_SIZE +#if STACK_SIZE > 8000 +#define N 1000 +#else +#define N (STACK_SIZE/8) +#endif +#else +#define N 1000 +#endif + +int +foo (struct str * p_str) +{ + static int sum = 0; + + sum = sum + p_str->a; + return sum; +} + +int +main () +{ + int i, sum; + struct str * p = malloc (N * sizeof (struct str)); + if (p == NULL) + return 0; + for (i = 0; i < N; i++) + sum = foo (p+i); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" } } */ + diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_escape_return-1.c b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_return-1.c new file mode 100644 index 00000000000..d0dce8b536f --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_return-1.c @@ -0,0 +1,33 @@ +/* { dg-do run } */ +/* { dg-additional-options "-fno-ipa-sra" } */ + +#include + +struct A { + int d; + int d1; +}; + +struct A a; + +struct A *foo () __attribute__((noinline)); +struct A *foo () +{ + a.d = 5; + return &a; +} + +int +main () +{ + a.d = 0; + foo (); + + if (a.d != 5) + abort (); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "has escaped. .Type escapes via a return" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_escape_return.c b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_return.c new file mode 100644 index 00000000000..71167182d50 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_return.c @@ -0,0 +1,32 @@ +/* { dg-do run } */ +/* { dg-additional-options "-fno-ipa-sra" } */ + +#include + +struct A { + int d; +}; + +struct A a; + +struct A foo () __attribute__((noinline)); +struct A foo () +{ + a.d = 5; + return a; +} + +int +main () +{ + a.d = 0; + foo (); + + if (a.d != 5) + abort (); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "has escaped: \"Type escapes via a return" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_escape_str_init.c b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_str_init.c new file mode 100644 index 00000000000..74fa11f3940 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_str_init.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include +typedef struct +{ + int a; + int b; +}str_t; + +#define N 2 + +str_t A[2] = {{1,1},{2,2}}; + +int +main () +{ + int i; + + for (i = 0; i < N; i++) + A[i].b = A[i].a; + + for (i = 0; i < N; i++) + if (A[i].b != A[i].a) + abort (); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "No structures to transform." "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_escape_substr_array.c b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_substr_array.c new file mode 100644 index 00000000000..60d2466e1da --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_substr_array.c @@ -0,0 +1,33 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include +typedef struct +{ + int a; + float b; +}str_t; + +#define N 1000 + +typedef struct +{ + str_t A[N]; + int c; +}str_with_substr_t; + +str_with_substr_t a; + +int +main () +{ + int i; + + for (i = 0; i < N; i++) + a.A[i].b = 0; + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" { xfail *-*-* } } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_escape_substr_pointer.c b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_substr_pointer.c new file mode 100644 index 00000000000..baf617816d6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_substr_pointer.c @@ -0,0 +1,48 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include +typedef struct +{ + int a; + float b; +}str_t; + +#ifdef STACK_SIZE +#if STACK_SIZE > 16000 +#define N 1000 +#else +#define N (STACK_SIZE/16) +#endif +#else +#define N 1000 +#endif + +typedef struct +{ + str_t * sub_str; + int c; +}str_with_substr_t; + +int foo; + +int +main (void) +{ + int i; + str_with_substr_t A[N]; + str_t a[N]; + + for (i=0; i < N; i++) + A[i].sub_str = &(a[i]); + + for (i=0; i < N; i++) + A[i].sub_str->a = 5; + + foo = A[56].sub_str->a; + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "has escaped...Type is used in an array" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_escape_substr_value.c b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_substr_value.c new file mode 100644 index 00000000000..33fce3b2350 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_substr_value.c @@ -0,0 +1,45 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include +typedef struct +{ + int a; + float b; +}str_t; + +#ifdef STACK_SIZE +#if STACK_SIZE > 8000 +#define N 1000 +#else +#define N (STACK_SIZE/8) +#endif +#else +#define N 1000 +#endif + + +typedef struct +{ + str_t sub_str; + int c; +}str_with_substr_t; + +int +main () +{ + int i; + str_with_substr_t A[N]; + + for (i = 0; i < N; i++) + A[i].sub_str.a = 5; + + for (i = 0; i < N; i++) + if (A[i].sub_str.a != 5) + abort (); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "has escaped...Type is used in an array" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_global_array.c b/gcc/testsuite/gcc.dg/struct/wo_prof_global_array.c new file mode 100644 index 00000000000..1c5a3aa15e5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_global_array.c @@ -0,0 +1,32 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include +typedef struct +{ + int a; + float b; +}str_t; + +#define N 1000 +str_t A[N]; + +int +main () +{ + int i; + + for (i = 0; i < N; i++) + { + A[i].a = 0; + } + + for (i = 0; i < N; i++) + if (A[i].a != 0) + abort (); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" { xfail *-*-* } } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_global_var.c b/gcc/testsuite/gcc.dg/struct/wo_prof_global_var.c new file mode 100644 index 00000000000..a0d1467fe9c --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_global_var.c @@ -0,0 +1,45 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include +typedef struct +{ + int a; + float b; +}str_t; + +#ifdef STACK_SIZE +#if STACK_SIZE > 8000 +#define N 1000 +#else +#define N (STACK_SIZE/8) +#endif +#else +#define N 1000 +#endif + +str_t *p; + +int +main () +{ + int i, sum; + + p = malloc (N * sizeof (str_t)); + if (p == NULL) + return 0; + for (i = 0; i < N; i++) + p[i].b = i; + + for (i = 0; i < N; i++) + p[i].b = p[i].a + 1; + + for (i = 0; i < N; i++) + if (p[i].b != p[i].a + 1) + abort (); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_local_array.c b/gcc/testsuite/gcc.dg/struct/wo_prof_local_array.c new file mode 100644 index 00000000000..6c24e1c8b05 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_local_array.c @@ -0,0 +1,40 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include +typedef struct +{ + int a; + float b; +}str_t; + +#ifdef STACK_SIZE +#if STACK_SIZE > 8000 +#define N 1000 +#else +#define N (STACK_SIZE/8) +#endif +#else +#define N 1000 +#endif + +int +main () +{ + int i; + str_t A[N]; + + for (i = 0; i < N; i++) + { + A[i].a = 0; + } + + for (i = 0; i < N; i++) + if (A[i].a != 0) + abort (); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" { xfail *-*-* } } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_local_var.c b/gcc/testsuite/gcc.dg/struct/wo_prof_local_var.c new file mode 100644 index 00000000000..8f2f8143f65 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_local_var.c @@ -0,0 +1,43 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include +typedef struct +{ + int a; + float b; +}str_t; + +#ifdef STACK_SIZE +#if STACK_SIZE > 8000 +#define N 1000 +#else +#define N (STACK_SIZE/8) +#endif +#else +#define N 1000 +#endif + +int +main () +{ + int i, sum; + + str_t * p = malloc (N * sizeof (str_t)); + if (p == NULL) + return 0; + for (i = 0; i < N; i++) + p[i].b = i; + + for (i = 0; i < N; i++) + p[i].b = p[i].a + 1; + + for (i = 0; i < N; i++) + if (p[i].b != p[i].a + 1) + abort (); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_malloc_size_var-1.c b/gcc/testsuite/gcc.dg/struct/wo_prof_malloc_size_var-1.c new file mode 100644 index 00000000000..98bf01a6d07 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_malloc_size_var-1.c @@ -0,0 +1,47 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include +typedef struct +{ + int a; + float b; +}str_t; + +#ifdef STACK_SIZE +#if STACK_SIZE > 8000 +#define N 1000 +#else +#define N (STACK_SIZE/8) +#endif +#else +#define N 1000 +#endif + +int +main () +{ + long i, num; + + num = rand(); + num = num > N ? N : num; + str_t * p = malloc (num * sizeof (str_t)); + + if (p == 0) + return 0; + + for (i = 1; i <= num; i++) + p[i-1].b = i; + + for (i = 1; i <= num; i++) + p[i-1].a = p[i-1].b + 1; + + for (i = 0; i < num; i++) + if (p[i].a != p[i].b + 1) + abort (); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_malloc_size_var.c b/gcc/testsuite/gcc.dg/struct/wo_prof_malloc_size_var.c new file mode 100644 index 00000000000..66b0f967c80 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_malloc_size_var.c @@ -0,0 +1,47 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include +typedef struct +{ + int a; + float b; +}str_t; + +#ifdef STACK_SIZE +#if STACK_SIZE > 8000 +#define N 1000 +#else +#define N (STACK_SIZE/8) +#endif +#else +#define N 1000 +#endif + +int +main () +{ + int i, num; + + num = rand(); + num = num > N ? N : num; + str_t * p = malloc (num * sizeof (str_t)); + + if (p == 0) + return 0; + + for (i = 0; i < num; i++) + p[i].b = i; + + for (i = 0; i < num; i++) + p[i].a = p[i].b + 1; + + for (i = 0; i < num; i++) + if (p[i].a != p[i].b + 1) + abort (); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_mult_field_peeling.c b/gcc/testsuite/gcc.dg/struct/wo_prof_mult_field_peeling.c new file mode 100644 index 00000000000..d28bcfb0237 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_mult_field_peeling.c @@ -0,0 +1,42 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include +typedef struct +{ + int a; + float b; + int c; + float d; +}str_t; + +#ifdef STACK_SIZE +#if STACK_SIZE > 1600 +#define N 100 +#else +#define N (STACK_SIZE/16) +#endif +#else +#define N 100 +#endif + +int +main () +{ + int i; + str_t *p = malloc (N * sizeof (str_t)); + if (p == NULL) + return 0; + for (i = 0; i < N; i++) + p[i].a = 5; + + for (i = 0; i < N; i++) + if (p[i].a != 5) + abort (); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* Two more fields structure is not splitted. */ +/* { dg-final { scan-ipa-dump "No structures to transform." "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_single_str_global.c b/gcc/testsuite/gcc.dg/struct/wo_prof_single_str_global.c new file mode 100644 index 00000000000..37a6a43a859 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_single_str_global.c @@ -0,0 +1,34 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include +typedef struct +{ + int a; + int b; +}str_t; + +#define N 3 + +str_t str; + +int +main () +{ + int i; + int res = 1<<(1< +typedef struct +{ + int a; + int b; +}str_t; + +#define N 3 + +int +main () +{ + int i; + int res = 1<<(1< +typedef struct +{ + int a; + int *b; +}str_t; + +#define N 3 + +str_t *p; + +int +main () +{ + str_t str; + int i; + int res = 1 << (1 << N); + p = &str; + str.a = 2; + + p->b = &(p->a); + + for (i=0; i < N; i++) + p->a = *(p->b)*(*(p->b)); + + if (p->a != res) + abort (); + + /* POSIX ignores all but the 8 low-order bits, but other + environments may not. */ + return (p->a & 255); +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "has escaped...Type escapes a cast to a different" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_two_strs.c b/gcc/testsuite/gcc.dg/struct/wo_prof_two_strs.c new file mode 100644 index 00000000000..cba92e99555 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_two_strs.c @@ -0,0 +1,67 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include + +typedef struct +{ + int a; + float b; +}str_t1; + +typedef struct +{ + int c; + float d; +}str_t2; + +#ifdef STACK_SIZE +#if STACK_SIZE > 16000 +#define N 1000 +#else +#define N (STACK_SIZE/16) +#endif +#else +#define N 1000 +#endif + +str_t1 *p1; +str_t2 *p2; +int num; + +void +foo (void) +{ + int i; + + for (i=0; i < num; i++) + p2[i].c = 2; +} + +int +main () +{ + int i, r; + + r = rand (); + num = r > N ? N : r; + p1 = malloc (num * sizeof (str_t1)); + p2 = malloc (num * sizeof (str_t2)); + + if (p1 == NULL || p2 == NULL) + return 0; + + for (i = 0; i < num; i++) + p1[i].a = 1; + + foo (); + + for (i = 0; i < num; i++) + if (p1[i].a != 1 || p2[i].c != 2) + abort (); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "struct_reorg" } } */ diff --git a/gcc/timevar.def b/gcc/timevar.def index 2dae5e1c760..36611812611 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -80,6 +80,7 @@ DEFTIMEVAR (TV_IPA_CONSTANT_PROP , "ipa cp") DEFTIMEVAR (TV_IPA_INLINING , "ipa inlining heuristics") DEFTIMEVAR (TV_IPA_FNSPLIT , "ipa function splitting") DEFTIMEVAR (TV_IPA_COMDATS , "ipa comdats") +DEFTIMEVAR (TV_IPA_STRUCT_REORG , "ipa struct reorg optimization") DEFTIMEVAR (TV_IPA_OPT , "ipa various optimizations") DEFTIMEVAR (TV_IPA_LTO_DECOMPRESS , "lto stream decompression") DEFTIMEVAR (TV_IPA_LTO_COMPRESS , "lto stream compression") diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 606d1d60b85..ec7be874cad 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -526,6 +526,7 @@ 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_struct_reorg (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_pta (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_tm (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_target_clone (gcc::context *ctxt); -- Gitee From ce6f1f141f13691d55fa6d16e2469c8ab32fefa3 Mon Sep 17 00:00:00 2001 From: eastb233 Date: Fri, 21 Jul 2023 10:08:05 +0800 Subject: [PATCH 2/7] Complete Structure Relayout Introduce complete structure reorganization based on original structure reorganization optimization, which change array of structure to structure of array in order to better utilize spatial locality. --- gcc/ipa-struct-reorg/escapes.def | 2 + gcc/ipa-struct-reorg/ipa-struct-reorg.cc | 994 ++++++++++++++++-- gcc/ipa-struct-reorg/ipa-struct-reorg.h | 33 + .../g++.dg/struct/no-body-function.cpp | 18 + .../g++.dg/struct/struct-reorg-1.cpp | 13 + .../g++.dg/struct/struct-reorg-2.cpp | 17 + .../g++.dg/struct/struct-reorg-3.cpp | 24 + gcc/testsuite/g++.dg/struct/struct-reorg.exp | 26 + gcc/testsuite/gcc.dg/struct/csr_1.c | 60 ++ .../gcc.dg/struct/csr_allocation-1.c | 46 + .../gcc.dg/struct/csr_allocation-2.c | 59 ++ .../gcc.dg/struct/csr_allocation-3.c | 77 ++ gcc/testsuite/gcc.dg/struct/csr_cast_int.c | 52 + .../gcc.dg/struct/csr_separate_instance.c | 48 + .../gcc.dg/struct/sr_address_of_field.c | 37 + gcc/testsuite/gcc.dg/struct/sr_convert_mem.c | 23 + gcc/testsuite/gcc.dg/struct/sr_maxmin_expr.c | 25 + gcc/testsuite/gcc.dg/struct/sr_pointer_and.c | 17 + .../gcc.dg/struct/sr_pointer_minus.c | 33 + 19 files changed, 1539 insertions(+), 65 deletions(-) create mode 100644 gcc/testsuite/g++.dg/struct/no-body-function.cpp create mode 100644 gcc/testsuite/g++.dg/struct/struct-reorg-1.cpp create mode 100644 gcc/testsuite/g++.dg/struct/struct-reorg-2.cpp create mode 100644 gcc/testsuite/g++.dg/struct/struct-reorg-3.cpp create mode 100644 gcc/testsuite/g++.dg/struct/struct-reorg.exp create mode 100644 gcc/testsuite/gcc.dg/struct/csr_1.c create mode 100644 gcc/testsuite/gcc.dg/struct/csr_allocation-1.c create mode 100644 gcc/testsuite/gcc.dg/struct/csr_allocation-2.c create mode 100644 gcc/testsuite/gcc.dg/struct/csr_allocation-3.c create mode 100644 gcc/testsuite/gcc.dg/struct/csr_cast_int.c create mode 100644 gcc/testsuite/gcc.dg/struct/csr_separate_instance.c create mode 100644 gcc/testsuite/gcc.dg/struct/sr_address_of_field.c create mode 100644 gcc/testsuite/gcc.dg/struct/sr_convert_mem.c create mode 100644 gcc/testsuite/gcc.dg/struct/sr_maxmin_expr.c create mode 100644 gcc/testsuite/gcc.dg/struct/sr_pointer_and.c create mode 100644 gcc/testsuite/gcc.dg/struct/sr_pointer_minus.c diff --git a/gcc/ipa-struct-reorg/escapes.def b/gcc/ipa-struct-reorg/escapes.def index c4c8e07392d..d825eb3e6f6 100644 --- a/gcc/ipa-struct-reorg/escapes.def +++ b/gcc/ipa-struct-reorg/escapes.def @@ -56,5 +56,7 @@ DEF_ESCAPE (escape_non_optimize, "Type used by a function which turns off struct DEF_ESCAPE (escape_array, "Type is used in an array [not handled yet]") DEF_ESCAPE (escape_ptr_ptr, "Type is used in a pointer to a pointer [not handled yet]") DEF_ESCAPE (escape_return, "Type escapes via a return [not handled yet]") +DEF_ESCAPE (escape_separate_instance, "Type escapes via a separate instance") +DEF_ESCAPE (escape_unhandled_rewrite, "Type escapes via a unhandled rewrite stmt") #undef DEF_ESCAPE diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc index 6a1b4db2b9e..46e55b203ce 100644 --- a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc @@ -104,10 +104,12 @@ along with GCC; see the file COPYING3. If not see #include "tree-ssa-live.h" /* For remove_unused_locals. */ #include "ipa-param-manipulation.h" #include "gimplify-me.h" +#include "cfgloop.h" namespace { using namespace struct_reorg; +using namespace struct_relayout; #define VOID_POINTER_P(type) \ (POINTER_TYPE_P (type) && VOID_TYPE_P (TREE_TYPE (type))) @@ -194,6 +196,14 @@ gimplify_build1 (gimple_stmt_iterator *gsi, enum tree_code code, tree type, GSI_SAME_STMT); } +enum srmode +{ + NORMAL = 0, + COMPLETE_STRUCT_RELAYOUT +}; + +static bool is_result_of_mult (tree, tree *, tree); + } // anon namespace @@ -283,7 +293,8 @@ srtype::srtype (tree type) : type (type), chain_type (false), escapes (does_not_escape), - visited (false) + visited (false), + has_alloc_array (0) { for (int i = 0; i < max_split; i++) newtype[i] = NULL_TREE; @@ -483,13 +494,6 @@ srtype::dump (FILE *f) fn->simple_dump (f); } fprintf (f, "\n }\n"); - fprintf (f, "\n field_sites = {"); - FOR_EACH_VEC_ELT (field_sites, i, field) - { - fprintf (f, " \n"); - field->simple_dump (f); - } - fprintf (f, "\n }\n"); fprintf (f, "}\n"); } @@ -631,15 +635,7 @@ srtype::create_new_type (void) maxclusters++; - const char *tname = NULL; - - if (TYPE_NAME (type) != NULL) - { - if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) - tname = IDENTIFIER_POINTER (TYPE_NAME (type)); - else if (DECL_NAME (TYPE_NAME (type)) != NULL) - tname = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); - } + const char *tname = get_type_name (type); for (unsigned i = 0; i < maxclusters; i++) { @@ -653,7 +649,10 @@ srtype::create_new_type (void) if (tname) { name = concat (tname, ".reorg.", id, NULL); - TYPE_NAME (newtype[i]) = get_identifier (name); + TYPE_NAME (newtype[i]) = build_decl (UNKNOWN_LOCATION, + TYPE_DECL, + get_identifier (name), + newtype[i]); free (name); } } @@ -673,6 +672,8 @@ srtype::create_new_type (void) { TYPE_FIELDS (newtype[i]) = newfields[i]; layout_type (newtype[i]); + if (TYPE_NAME (newtype[i]) != NULL) + layout_decl (TYPE_NAME (newtype[i]), 0); } warn_padded = save_warn_padded; @@ -841,12 +842,6 @@ srfield::dump (FILE *f) fprintf (f, ", offset = " HOST_WIDE_INT_PRINT_DEC, offset); fprintf (f, ", type = "); print_generic_expr (f, fieldtype); - if (type) - { - fprintf (f, "( srtype = "); - type->simple_dump (f); - fprintf (f, ")"); - } fprintf (f, "\n}\n"); } @@ -855,7 +850,8 @@ srfield::dump (FILE *f) void srfield::simple_dump (FILE *f) { - fprintf (f, "field (%d)", DECL_UID (fielddecl)); + if (fielddecl) + fprintf (f, "field (%d)", DECL_UID (fielddecl)); } /* Dump out the access structure to FILE. */ @@ -899,6 +895,92 @@ srdecl::dump (FILE *file) } // namespace struct_reorg +namespace struct_relayout { + +/* Complete Structure Relayout Optimization. + It reorganizes all structure members, and puts same member together. + struct s { + long a; + int b; + struct s *c; + }; + Array looks like + abcabcabcabc... + will be transformed to + aaaa...bbbb...cccc... +*/ + +#define GPTR_SIZE(i) \ + TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (gptr[i]))) + +unsigned transformed = 0; + +unsigned +csrtype::calculate_field_num (tree field_offset) +{ + if (field_offset == NULL) + return 0; + + HOST_WIDE_INT off = int_byte_position (field_offset); + unsigned i = 1; + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + { + if (off == int_byte_position (field)) + return i; + i++; + } + return 0; +} + +void +csrtype::init_type_info (void) +{ + if (!type) + return; + new_size = old_size = tree_to_uhwi (TYPE_SIZE_UNIT (type)); + + /* Close enough to pad to improve performance. + 33~63 should pad to 64 but 33~48 (first half) are too far away, and + 65~127 should pad to 128 but 65~96 (first half) are too far away. */ + if (old_size > 48 && old_size < 64) + new_size = 64; + if (old_size > 96 && old_size < 128) + new_size = 128; + + /* For performance reasons, only allow structure size + that is a power of 2 and not too big. */ + if (new_size != 1 && new_size != 2 + && new_size != 4 && new_size != 8 + && new_size != 16 && new_size != 32 + && new_size != 64 && new_size != 128) + { + new_size = 0; + field_count = 0; + return; + } + + unsigned i = 0; + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL) + i++; + field_count = i; + + struct_size = build_int_cstu (TREE_TYPE (TYPE_SIZE_UNIT (type)), + new_size); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Type: "); + print_generic_expr (dump_file, type); + fprintf (dump_file, " has %d members.\n", field_count); + fprintf (dump_file, "Modify struct size from %ld to %ld.\n", + old_size, new_size); + } +} + +} // namespace struct_relayout + + namespace { struct ipa_struct_reorg @@ -907,13 +989,10 @@ public: // Constructors ipa_struct_reorg (void) : current_function (NULL), - done_recording (false) + done_recording (false), + current_mode (NORMAL) {} - // Public methods - unsigned execute (void); - void mark_type_as_escape (tree type, escape_type, gimple *stmt = NULL); -private: // Fields auto_vec_del types; auto_vec_del functions; @@ -921,8 +1000,13 @@ private: srfunction *current_function; bool done_recording; + srmode current_mode; + + // Methods + unsigned execute (enum srmode mode); + void mark_type_as_escape (tree type, escape_type escapes, + gimple *stmt = NULL); - // Private methods void dump_types (FILE *f); void dump_types_escaped (FILE *f); void dump_functions (FILE *f); @@ -954,6 +1038,7 @@ private: void maybe_record_allocation_site (cgraph_node *, gimple *); void record_stmt_expr (tree expr, cgraph_node *node, gimple *stmt); void mark_expr_escape (tree, escape_type, gimple *stmt); + bool handled_allocation_stmt (gimple *stmt); tree allocate_size (srtype *t, gimple *stmt); void mark_decls_in_as_not_needed (tree fn); @@ -976,6 +1061,7 @@ private: bool can_escape = false); bool wholeaccess (tree expr, tree base, tree accesstype, srtype *t); + void check_alloc_num (gimple *stmt, srtype *type); void check_definition (srdecl *decl, vec &); void check_uses (srdecl *decl, vec &); void check_use (srdecl *decl, gimple *stmt, vec &); @@ -990,8 +1076,591 @@ private: bool has_rewritten_type (srfunction *); void maybe_mark_or_record_other_side (tree side, tree other, gimple *stmt); + + unsigned execute_struct_relayout (void); }; +struct ipa_struct_relayout +{ +public: + // Fields + tree gptr[max_relayout_split + 1]; + csrtype ctype; + ipa_struct_reorg *sr; + cgraph_node *current_node; + + // Constructors + ipa_struct_relayout (tree type, ipa_struct_reorg *sr_) + { + ctype.type = type; + sr = sr_; + current_node = NULL; + for (int i = 0; i < max_relayout_split + 1; i++) + gptr[i] = NULL; + } + + // Methods + tree create_new_vars (tree type, const char *name); + void create_global_ptrs (void); + unsigned int rewrite (void); + void rewrite_stmt_in_function (void); + bool rewrite_debug (gimple *stmt, gimple_stmt_iterator *gsi); + bool rewrite_stmt (gimple *stmt, gimple_stmt_iterator *gsi); + bool handled_allocation_stmt (gcall *stmt); + void init_global_ptrs (gcall *stmt, gimple_stmt_iterator *gsi); + bool check_call_uses (gcall *stmt); + bool rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi); + tree create_ssa (tree node, gimple_stmt_iterator *gsi); + bool is_candidate (tree xhs); + tree rewrite_address (tree xhs, gimple_stmt_iterator *gsi); + tree rewrite_offset (tree offset, HOST_WIDE_INT num); + bool rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi); + bool maybe_rewrite_cst (tree cst, gimple_stmt_iterator *gsi, + HOST_WIDE_INT ×); + unsigned int execute (void); +}; + +} // anon namespace + +namespace { + +/* Methods for ipa_struct_relayout. */ + +static void +set_var_attributes (tree var) +{ + if (!var) + return; + gcc_assert (TREE_CODE (var) == VAR_DECL); + + DECL_ARTIFICIAL (var) = 1; + DECL_EXTERNAL (var) = 0; + TREE_STATIC (var) = 1; + TREE_PUBLIC (var) = 0; + TREE_USED (var) = 1; + DECL_CONTEXT (var) = NULL; + TREE_THIS_VOLATILE (var) = 0; + TREE_ADDRESSABLE (var) = 0; + TREE_READONLY (var) = 0; + if (is_global_var (var)) + set_decl_tls_model (var, TLS_MODEL_NONE); +} + +tree +ipa_struct_relayout::create_new_vars (tree type, const char *name) +{ + gcc_assert (type); + tree new_type = build_pointer_type (type); + + tree new_name = NULL; + if (name) + new_name = get_identifier (name); + + tree new_var = build_decl (UNKNOWN_LOCATION, VAR_DECL, new_name, new_type); + + /* Set new_var's attributes. */ + set_var_attributes (new_var); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Created new var: "); + print_generic_expr (dump_file, new_var); + fprintf (dump_file, "\n"); + } + return new_var; +} + +void +ipa_struct_relayout::create_global_ptrs (void) +{ + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Create global gptrs: {\n"); + + char *gptr0_name = NULL; + const char *type_name = get_type_name (ctype.type); + + if (type_name) + gptr0_name = concat (type_name, "_gptr0", NULL); + tree var_gptr0 = create_new_vars (ctype.type, gptr0_name); + gptr[0] = var_gptr0; + varpool_node::add (var_gptr0); + + unsigned i = 1; + for (tree field = TYPE_FIELDS (ctype.type); field; + field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL) + { + tree type = TREE_TYPE (field); + + char *name = NULL; + char id[10] = {0}; + sprintf (id, "%d", i); + const char *decl_name = IDENTIFIER_POINTER (DECL_NAME (field)); + + if (type_name && decl_name) + name = concat (type_name, "_", decl_name, "_gptr", id, NULL); + tree var = create_new_vars (type, name); + + gptr[i] = var; + varpool_node::add (var); + i++; + } + } + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\nTotally create %d gptrs. }\n\n", i); + gcc_assert (ctype.field_count == i - 1); +} + +void +ipa_struct_relayout::rewrite_stmt_in_function (void) +{ + gcc_assert (cfun); + + basic_block bb = NULL; + gimple_stmt_iterator si; + FOR_EACH_BB_FN (bb, cfun) + { + for (si = gsi_start_bb (bb); !gsi_end_p (si);) + { + gimple *stmt = gsi_stmt (si); + if (rewrite_stmt (stmt, &si)) + gsi_remove (&si, true); + else + gsi_next (&si); + } + } + + /* Debug statements need to happen after all other statements + have changed. */ + FOR_EACH_BB_FN (bb, cfun) + { + for (si = gsi_start_bb (bb); !gsi_end_p (si);) + { + gimple *stmt = gsi_stmt (si); + if (gimple_code (stmt) == GIMPLE_DEBUG + && rewrite_debug (stmt, &si)) + gsi_remove (&si, true); + else + gsi_next (&si); + } + } +} + +unsigned int +ipa_struct_relayout::rewrite (void) +{ + cgraph_node *cnode = NULL; + function *fn = NULL; + FOR_EACH_FUNCTION (cnode) + { + if (!cnode->real_symbol_p () || !cnode->has_gimple_body_p ()) + continue; + if (cnode->definition) + { + fn = DECL_STRUCT_FUNCTION (cnode->decl); + if (fn == NULL) + continue; + + current_node = cnode; + push_cfun (fn); + + rewrite_stmt_in_function (); + + update_ssa (TODO_update_ssa_only_virtuals); + + if (flag_tree_pta) + compute_may_aliases (); + + remove_unused_locals (); + + cgraph_edge::rebuild_edges (); + + free_dominance_info (CDI_DOMINATORS); + + pop_cfun (); + current_node = NULL; + } + } + return TODO_verify_all; +} + +bool +ipa_struct_relayout::rewrite_debug (gimple *stmt ATTRIBUTE_UNUSED, + gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED) +{ + /* Delete debug gimple now. */ + return true; +} + +bool +ipa_struct_relayout::rewrite_stmt (gimple *stmt, gimple_stmt_iterator *gsi) +{ + switch (gimple_code (stmt)) + { + case GIMPLE_ASSIGN: + return rewrite_assign (as_a (stmt), gsi); + case GIMPLE_CALL: + return rewrite_call (as_a (stmt), gsi); + default: + break; + } + return false; +} + +bool +ipa_struct_relayout::handled_allocation_stmt (gcall *stmt) +{ + if (gimple_call_builtin_p (stmt, BUILT_IN_CALLOC)) + return true; + return false; +} + +void +ipa_struct_relayout::init_global_ptrs (gcall *stmt, gimple_stmt_iterator *gsi) +{ + gcc_assert (handled_allocation_stmt (stmt)); + + tree lhs = gimple_call_lhs (stmt); + + /* Case that gimple is at the end of bb. */ + if (gsi_one_before_end_p (*gsi)) + { + gassign *gptr0 = gimple_build_assign (gptr[0], lhs); + gsi_insert_after (gsi, gptr0, GSI_SAME_STMT); + } + gsi_next (gsi); + + /* Emit gimple gptr0 = _X and gptr1 = _X. */ + gassign *gptr0 = gimple_build_assign (gptr[0], lhs); + gsi_insert_before (gsi, gptr0, GSI_SAME_STMT); + gassign *gptr1 = gimple_build_assign (gptr[1], lhs); + gsi_insert_before (gsi, gptr1, GSI_SAME_STMT); + + /* Emit gimple gptr_[i] = gptr_[i-1] + _Y[gap]. */ + for (unsigned i = 2; i <= ctype.field_count; i++) + { + gimple *new_stmt = NULL; + tree gptr_i_prev_ssa = create_ssa (gptr[i-1], gsi); + tree gptr_i_ssa = make_ssa_name (TREE_TYPE (gptr[i-1])); + + /* Emit gimple _Y[gap] = N * sizeof (member). */ + tree member_gap = gimplify_build2 (gsi, MULT_EXPR, + long_unsigned_type_node, + gimple_call_arg (stmt, 0), + GPTR_SIZE (i-1)); + + new_stmt = gimple_build_assign (gptr_i_ssa, POINTER_PLUS_EXPR, + gptr_i_prev_ssa, member_gap); + gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT); + + gassign *gptr_i = gimple_build_assign (gptr[i], gptr_i_ssa); + gsi_insert_before (gsi, gptr_i, GSI_SAME_STMT); + } + gsi_prev (gsi); +} + +bool +ipa_struct_relayout::check_call_uses (gcall *stmt) +{ + gcc_assert (current_node); + srfunction *fn = sr->find_function (current_node); + tree lhs = gimple_call_lhs (stmt); + + if (fn == NULL) + return false; + + srdecl *d = fn->find_decl (lhs); + if (d == NULL) + return false; + if (types_compatible_p (d->type->type, ctype.type)) + return true; + + return false; +} + +bool +ipa_struct_relayout::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi) +{ + if (handled_allocation_stmt (stmt)) + { + /* Rewrite stmt _X = calloc (N, sizeof (struct)). */ + tree size = gimple_call_arg (stmt, 1); + if (TREE_CODE (size) != INTEGER_CST) + return false; + if (tree_to_uhwi (size) != ctype.old_size) + return false; + if (!check_call_uses (stmt)) + return false; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Rewrite allocation call:\n"); + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "to\n"); + } + + /* Modify sizeof (struct). */ + gimple_call_set_arg (stmt, 1, ctype.struct_size); + update_stmt (stmt); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + } + + init_global_ptrs (stmt, gsi); + } + return false; +} + +tree +ipa_struct_relayout::create_ssa (tree node, gimple_stmt_iterator *gsi) +{ + gcc_assert (TREE_CODE (node) == VAR_DECL); + tree node_ssa = make_ssa_name (TREE_TYPE (node)); + gassign *stmt = gimple_build_assign (node_ssa, node); + gsi_insert_before (gsi, stmt, GSI_SAME_STMT); + return node_ssa; +} + +bool +ipa_struct_relayout::is_candidate (tree xhs) +{ + if (TREE_CODE (xhs) != COMPONENT_REF) + return false; + tree mem = TREE_OPERAND (xhs, 0); + if (TREE_CODE (mem) == MEM_REF) + { + tree type = TREE_TYPE (mem); + if (types_compatible_p (type, ctype.type)) + return true; + } + return false; +} + +tree +ipa_struct_relayout::rewrite_address (tree xhs, gimple_stmt_iterator *gsi) +{ + tree mem_ref = TREE_OPERAND (xhs, 0); + tree pointer = TREE_OPERAND (mem_ref, 0); + tree pointer_offset = TREE_OPERAND (mem_ref, 1); + tree field = TREE_OPERAND (xhs, 1); + + tree pointer_ssa = fold_convert (long_unsigned_type_node, pointer); + tree gptr0_ssa = fold_convert (long_unsigned_type_node, gptr[0]); + + /* Emit gimple _X1 = ptr - gptr0. */ + tree step1 = gimplify_build2 (gsi, MINUS_EXPR, long_unsigned_type_node, + pointer_ssa, gptr0_ssa); + + /* Emit gimple _X2 = _X1 / sizeof (struct). */ + tree step2 = gimplify_build2 (gsi, TRUNC_DIV_EXPR, long_unsigned_type_node, + step1, ctype.struct_size); + + unsigned field_num = ctype.calculate_field_num (field); + gcc_assert (field_num > 0 && field_num <= ctype.field_count); + + /* Emit gimple _X3 = _X2 * sizeof (member). */ + tree step3 = gimplify_build2 (gsi, MULT_EXPR, long_unsigned_type_node, + step2, GPTR_SIZE (field_num)); + + /* Emit gimple _X4 = gptr[I]. */ + tree gptr_field_ssa = create_ssa (gptr[field_num], gsi); + tree new_address = make_ssa_name (TREE_TYPE (gptr[field_num])); + gassign *new_stmt = gimple_build_assign (new_address, POINTER_PLUS_EXPR, + gptr_field_ssa, step3); + gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT); + + /* MEM_REF with nonzero offset like + MEM[ptr + sizeof (struct)] = 0B + should be transformed to + MEM[gptr + sizeof (member)] = 0B + */ + HOST_WIDE_INT size + = tree_to_shwi (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (new_address)))); + tree new_size = rewrite_offset (pointer_offset, size); + if (new_size) + TREE_OPERAND (mem_ref, 1) = new_size; + + /* Update mem_ref pointer. */ + TREE_OPERAND (mem_ref, 0) = new_address; + + /* Update mem_ref TREE_TYPE. */ + TREE_TYPE (mem_ref) = TREE_TYPE (TREE_TYPE (new_address)); + + return mem_ref; +} + +tree +ipa_struct_relayout::rewrite_offset (tree offset, HOST_WIDE_INT num) +{ + if (TREE_CODE (offset) == INTEGER_CST) + { + bool sign = false; + HOST_WIDE_INT off = TREE_INT_CST_LOW (offset); + if (off == 0) + return NULL; + if (off < 0) + { + off = -off; + sign = true; + } + if (off % ctype.old_size == 0) + { + HOST_WIDE_INT times = off / ctype.old_size; + times = sign ? -times : times; + return build_int_cst (TREE_TYPE (offset), num * times); + } + } + return NULL; +} + +#define REWRITE_ASSIGN_TREE_IN_STMT(node) \ +do \ +{ \ + tree node = gimple_assign_##node (stmt); \ + if (node && is_candidate (node)) \ + { \ + tree mem_ref = rewrite_address (node, gsi); \ + gimple_assign_set_##node (stmt, mem_ref); \ + update_stmt (stmt); \ + } \ +} while (0) + +/* COMPONENT_REF = exp => MEM_REF = exp + / \ / \ + MEM_REF field gptr offset + / \ + pointer offset +*/ +bool +ipa_struct_relayout::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) +{ + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Maybe rewrite assign:\n"); + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "to\n"); + } + + switch (gimple_num_ops (stmt)) + { + case 4: REWRITE_ASSIGN_TREE_IN_STMT (rhs3); // FALLTHRU + case 3: + { + REWRITE_ASSIGN_TREE_IN_STMT (rhs2); + tree rhs2 = gimple_assign_rhs2 (stmt); + if (rhs2 && TREE_CODE (rhs2) == INTEGER_CST) + { + /* Handle pointer++ and pointer-- or + factor is euqal to struct size. */ + HOST_WIDE_INT times = 1; + if (maybe_rewrite_cst (rhs2, gsi, times)) + { + tree tmp = build_int_cst ( + TREE_TYPE (TYPE_SIZE_UNIT (ctype.type)), + ctype.new_size * times); + gimple_assign_set_rhs2 (stmt, tmp); + update_stmt (stmt); + } + } + } // FALLTHRU + case 2: REWRITE_ASSIGN_TREE_IN_STMT (rhs1); // FALLTHRU + case 1: REWRITE_ASSIGN_TREE_IN_STMT (lhs); // FALLTHRU + case 0: break; + default: gcc_unreachable (); + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + } + return false; +} + +bool +ipa_struct_relayout::maybe_rewrite_cst (tree cst, gimple_stmt_iterator *gsi, + HOST_WIDE_INT ×) +{ + bool ret = false; + gcc_assert (TREE_CODE (cst) == INTEGER_CST); + + gimple *stmt = gsi_stmt (*gsi); + if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) + { + tree lhs = gimple_assign_lhs (stmt); + tree rhs1 = gimple_assign_rhs1 (stmt); + if (types_compatible_p (inner_type (TREE_TYPE (rhs1)), ctype.type) + || types_compatible_p (inner_type (TREE_TYPE (lhs)), ctype.type)) + { + tree num = NULL; + if (is_result_of_mult (cst, &num, TYPE_SIZE_UNIT (ctype.type))) + { + times = TREE_INT_CST_LOW (num); + return true; + } + } + } + + if (gimple_assign_rhs_code (stmt) == MULT_EXPR) + { + if (gsi_one_before_end_p (*gsi)) + return false; + gsi_next (gsi); + gimple *stmt2 = gsi_stmt (*gsi); + + if (gimple_code (stmt2) == GIMPLE_ASSIGN + && gimple_assign_rhs_code (stmt2) == POINTER_PLUS_EXPR) + { + tree lhs = gimple_assign_lhs (stmt2); + tree rhs1 = gimple_assign_rhs1 (stmt2); + if (types_compatible_p (inner_type (TREE_TYPE (rhs1)), ctype.type) + || types_compatible_p (inner_type (TREE_TYPE (lhs)), ctype.type)) + { + tree num = NULL; + if (is_result_of_mult (cst, &num, TYPE_SIZE_UNIT (ctype.type))) + { + times = TREE_INT_CST_LOW (num); + ret = true; + } + } + } + gsi_prev (gsi); + return ret; + } + return false; +} + +unsigned int +ipa_struct_relayout::execute (void) +{ + ctype.init_type_info (); + if (ctype.field_count < min_relayout_split + || ctype.field_count > max_relayout_split) + return 0; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Complete Struct Relayout Type: "); + print_generic_expr (dump_file, ctype.type); + fprintf (dump_file, "\n"); + } + transformed++; + + create_global_ptrs (); + return rewrite (); +} + +} // anon namespace + + +namespace { + +/* Methods for ipa_struct_reorg. */ + /* Dump all of the recorded types to file F. */ void @@ -1189,7 +1858,7 @@ ipa_struct_reorg::record_type (tree type) f->type = t1; t1->add_field_site (f); } - if (t1 == type1) + if (t1 == type1 && current_mode != COMPLETE_STRUCT_RELAYOUT) type1->mark_escape (escape_rescusive_type, NULL); } } @@ -1331,6 +2000,12 @@ ipa_struct_reorg::record_var (tree decl, escape_type escapes, int arg) else e = escape_type_volatile_array_or_ptrptr (TREE_TYPE (decl)); + /* Separate instance is hard to trace in complete struct + relayout optimization. */ + if (current_mode == COMPLETE_STRUCT_RELAYOUT + && TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE) + e = escape_separate_instance; + if (e != does_not_escape) type->mark_escape (e, NULL); } @@ -1369,6 +2044,7 @@ ipa_struct_reorg::find_var (tree expr, gimple *stmt) || TREE_CODE (expr) == VIEW_CONVERT_EXPR) { tree r = TREE_OPERAND (expr, 0); + tree orig_type = TREE_TYPE (expr); if (handled_component_p (r) || TREE_CODE (r) == MEM_REF) { @@ -1382,8 +2058,18 @@ ipa_struct_reorg::find_var (tree expr, gimple *stmt) escape_vce, stmt); } if (TREE_CODE (r) == MEM_REF) - mark_type_as_escape (TREE_TYPE (TREE_OPERAND (r, 1)), - escape_addr, stmt); + { + mark_type_as_escape (TREE_TYPE (TREE_OPERAND (r, 1)), + escape_addr, stmt); + tree inner_type = TREE_TYPE (TREE_OPERAND (r, 0)); + if (orig_type != inner_type) + { + mark_type_as_escape (orig_type, + escape_cast_another_ptr, stmt); + mark_type_as_escape (inner_type, + escape_cast_another_ptr, stmt); + } + } r = TREE_OPERAND (r, 0); } mark_expr_escape (r, escape_addr, stmt); @@ -1407,7 +2093,8 @@ ipa_struct_reorg::find_vars (gimple *stmt) { case GIMPLE_ASSIGN: if (gimple_assign_rhs_class (stmt) == GIMPLE_SINGLE_RHS - || gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) + || gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR + || gimple_assign_rhs_code (stmt) == NOP_EXPR) { tree lhs = gimple_assign_lhs (stmt); tree rhs = gimple_assign_rhs1 (stmt); @@ -1432,6 +2119,32 @@ ipa_struct_reorg::find_vars (gimple *stmt) current_function->record_decl (t, rhs, -1); } } + else + { + /* Because we won't handle these stmts in rewrite phase, + just mark these types as escaped. */ + switch (gimple_num_ops (stmt)) + { + case 4: mark_type_as_escape ( + TREE_TYPE (gimple_assign_rhs3 (stmt)), + escape_unhandled_rewrite, stmt); + // FALLTHRU + case 3: mark_type_as_escape ( + TREE_TYPE (gimple_assign_rhs2 (stmt)), + escape_unhandled_rewrite, stmt); + // FALLTHRU + case 2: mark_type_as_escape ( + TREE_TYPE (gimple_assign_rhs1 (stmt)), + escape_unhandled_rewrite, stmt); + // FALLTHRU + case 1: mark_type_as_escape ( + TREE_TYPE (gimple_assign_lhs (stmt)), + escape_unhandled_rewrite, stmt); + // FALLTHRU + case 0: break; + default: gcc_unreachable (); + } + } break; case GIMPLE_CALL: @@ -1514,9 +2227,21 @@ is_result_of_mult (tree arg, tree *num, tree struct_size) /* If we have a integer, just check if it is a multiply of STRUCT_SIZE. */ if (TREE_CODE (arg) == INTEGER_CST) { - if (integer_zerop (size_binop (FLOOR_MOD_EXPR, arg, struct_size))) + bool sign = false; + HOST_WIDE_INT size = TREE_INT_CST_LOW (arg); + if (size < 0) + { + size = -size; + sign = true; + } + tree arg2 = build_int_cst (TREE_TYPE (arg), size); + if (integer_zerop (size_binop (FLOOR_MOD_EXPR, arg2, struct_size))) { - *num = size_binop (FLOOR_DIV_EXPR, arg, struct_size); + tree number = size_binop (FLOOR_DIV_EXPR, arg2, struct_size); + if (sign) + number = build_int_cst (TREE_TYPE (number), + -tree_to_shwi (number)); + *num = number; return true; } return false; @@ -1586,16 +2311,21 @@ is_result_of_mult (tree arg, tree *num, tree struct_size) /* Return TRUE if STMT is an allocation statement that is handled. */ -static bool -handled_allocation_stmt (gimple *stmt) +bool +ipa_struct_reorg::handled_allocation_stmt (gimple *stmt) { - if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC) - || gimple_call_builtin_p (stmt, BUILT_IN_MALLOC) - || gimple_call_builtin_p (stmt, BUILT_IN_CALLOC) - || gimple_call_builtin_p (stmt, BUILT_IN_ALIGNED_ALLOC) - || gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA) - || gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN)) + if (current_mode == COMPLETE_STRUCT_RELAYOUT + && gimple_call_builtin_p (stmt, BUILT_IN_CALLOC)) return true; + + if (current_mode != COMPLETE_STRUCT_RELAYOUT) + if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_MALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_CALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_ALIGNED_ALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA) + || gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN)) + return true; return false; } @@ -1636,7 +2366,7 @@ ipa_struct_reorg::allocate_size (srtype *type, gimple *stmt) the size of structure. */ if (operand_equal_p (arg1, struct_size, 0)) return size; - /* Check that first argument is a constant equal to + /* ??? Check that first argument is a constant equal to the size of structure. */ if (operand_equal_p (size, struct_size, 0)) return arg1; @@ -1751,6 +2481,25 @@ ipa_struct_reorg::maybe_record_assign (cgraph_node *node, gassign *stmt) } } +static bool +check_mem_ref_offset (tree expr) +{ + tree num = NULL; + bool ret = false; + + if (TREE_CODE (expr) != MEM_REF) + return false; + + /* Try to find the structure size. */ + tree field_off = TREE_OPERAND (expr, 1); + tree tmp = TREE_OPERAND (expr, 0); + if (TREE_CODE (tmp) == ADDR_EXPR) + tmp = TREE_OPERAND (tmp, 0); + tree size = TYPE_SIZE_UNIT (inner_type (TREE_TYPE (tmp))); + ret = is_result_of_mult (field_off, &num, size); + return ret; +} + tree get_ref_base_and_offset (tree &e, HOST_WIDE_INT &offset, bool &realpart, bool &imagpart, @@ -1792,7 +2541,8 @@ get_ref_base_and_offset (tree &e, HOST_WIDE_INT &offset, gcc_assert (TREE_CODE (field_off) == INTEGER_CST); /* So we can mark the types as escaping if different. */ accesstype = TREE_TYPE (field_off); - offset += tree_to_uhwi (field_off); + if (!check_mem_ref_offset (expr)) + offset += tree_to_uhwi (field_off); return TREE_OPERAND (expr, 0); } default: @@ -2176,6 +2926,31 @@ ipa_struct_reorg::check_type_and_push (tree newdecl, srtype *type, type1->mark_escape (escape_cast_another_ptr, stmt); } +void +ipa_struct_reorg::check_alloc_num (gimple *stmt, srtype *type) +{ + if (current_mode == COMPLETE_STRUCT_RELAYOUT + && handled_allocation_stmt (stmt)) + { + tree arg0 = gimple_call_arg (stmt, 0); + basic_block bb = gimple_bb (stmt); + cgraph_node *node = current_function->node; + if (integer_onep (arg0)) + /* Actually NOT an array, but may ruin other array. */ + type->has_alloc_array = -1; + else if (bb->loop_father != NULL + && loop_outer (bb->loop_father) != NULL) + /* The allocation is in a loop. */ + type->has_alloc_array = -2; + else if (node->callers != NULL) + type->has_alloc_array = -3; + else + type->has_alloc_array = type->has_alloc_array < 0 + ? type->has_alloc_array + : type->has_alloc_array + 1; + } +} + /* 2) Check SSA_NAMEs for non type usages (source or use) (worlist of srdecl) a) if the SSA_NAME is sourced from a pointer plus, record the pointer and @@ -2223,6 +2998,7 @@ ipa_struct_reorg::check_definition (srdecl *decl, vec &worklist) if (!handled_allocation_stmt (stmt) || !allocate_size (type, stmt)) type->mark_escape (escape_return, stmt); + check_alloc_num (stmt, type); return; } /* If the SSA_NAME is sourced from an inline-asm, @@ -2264,6 +3040,20 @@ ipa_struct_reorg::check_definition (srdecl *decl, vec &worklist) return; } + if (gimple_assign_rhs_code (stmt) == MAX_EXPR + || gimple_assign_rhs_code (stmt) == MIN_EXPR + || gimple_assign_rhs_code (stmt) == BIT_IOR_EXPR + || gimple_assign_rhs_code (stmt) == BIT_XOR_EXPR + || gimple_assign_rhs_code (stmt) == BIT_AND_EXPR) + { + tree rhs2 = gimple_assign_rhs2 (stmt); + if (TREE_CODE (rhs) == SSA_NAME) + check_type_and_push (rhs, type, worklist, stmt); + if (TREE_CODE (rhs2) == SSA_NAME) + check_type_and_push (rhs2, type, worklist, stmt); + return; + } + /* Casts between pointers and integer are escaping. */ if (gimple_assign_cast_p (stmt)) { @@ -2328,6 +3118,11 @@ ipa_struct_reorg::check_other_side (srdecl *decl, tree other, gimple *stmt, srtype *t1 = find_type (inner_type (t)); if (t1 == type) { + /* In Complete Struct Relayout, if lhs type is the same + as rhs type, we could return without any harm. */ + if (current_mode == COMPLETE_STRUCT_RELAYOUT) + return; + tree base; bool indirect; srtype *type1; @@ -2376,8 +3171,11 @@ ipa_struct_reorg::check_use (srdecl *decl, gimple *stmt, tree rhs1 = gimple_cond_lhs (stmt); tree rhs2 = gimple_cond_rhs (stmt); tree orhs = rhs1; - if (gimple_cond_code (stmt) != EQ_EXPR - && gimple_cond_code (stmt) != NE_EXPR) + enum tree_code code = gimple_cond_code (stmt); + if (code != EQ_EXPR && code != NE_EXPR + && (current_mode != COMPLETE_STRUCT_RELAYOUT + || (code != LT_EXPR && code != LE_EXPR + && code != GT_EXPR && code != GE_EXPR))) { mark_expr_escape (rhs1, escape_non_eq, stmt); mark_expr_escape (rhs2, escape_non_eq, stmt); @@ -2406,8 +3204,11 @@ ipa_struct_reorg::check_use (srdecl *decl, gimple *stmt, tree rhs1 = gimple_assign_rhs1 (stmt); tree rhs2 = gimple_assign_rhs2 (stmt); tree orhs = rhs1; - if (gimple_assign_rhs_code (stmt) != EQ_EXPR - && gimple_assign_rhs_code (stmt) != NE_EXPR) + enum tree_code code = gimple_assign_rhs_code (stmt); + if (code != EQ_EXPR && code != NE_EXPR + && (current_mode != COMPLETE_STRUCT_RELAYOUT + || (code != LT_EXPR && code != LE_EXPR + && code != GT_EXPR && code != GE_EXPR))) { mark_expr_escape (rhs1, escape_non_eq, stmt); mark_expr_escape (rhs2, escape_non_eq, stmt); @@ -2692,6 +3493,12 @@ ipa_struct_reorg::record_accesses (void) /* Record accesses inside a function. */ if (cnode->definition) record_function (cnode); + else + { + tree return_type = TREE_TYPE (TREE_TYPE (cnode->decl)); + mark_type_as_escape (return_type, escape_return, NULL); + } + } if (dump_file && (dump_flags & TDF_DETAILS)) @@ -2807,8 +3614,11 @@ ipa_struct_reorg::propagate_escape (void) void ipa_struct_reorg::prune_escaped_types (void) { - detect_cycles (); - propagate_escape (); + if (current_mode != COMPLETE_STRUCT_RELAYOUT) + { + detect_cycles (); + propagate_escape (); + } if (dump_file && (dump_flags & TDF_DETAILS)) { @@ -3954,17 +4764,66 @@ ipa_struct_reorg::rewrite_functions (void) } unsigned int -ipa_struct_reorg::execute (void) +ipa_struct_reorg::execute_struct_relayout (void) { - /* FIXME: If there is a top-level inline-asm, - the pass immediately returns. */ - if (symtab->first_asm_symbol ()) - return 0; - record_accesses (); - prune_escaped_types (); - analyze_types (); + unsigned retval = 0; + for (unsigned i = 0; i < types.length (); i++) + { + tree type = types[i]->type; + if (TYPE_FIELDS (type) == NULL) + continue; + if (types[i]->has_alloc_array != 1) + continue; + if (types[i]->chain_type) + continue; + retval |= ipa_struct_relayout (type, this).execute (); + } + + if (dump_file) + { + if (transformed) + fprintf (dump_file, "\nNumber of structures to transform in " + "Complete Structure Relayout is %d\n", transformed); + else + fprintf (dump_file, "\nNo structures to transform in " + "Complete Structure Relayout.\n"); + } + + return retval; +} + +unsigned int +ipa_struct_reorg::execute (enum srmode mode) +{ + unsigned int ret = 0; + + if (mode == NORMAL) + { + current_mode = NORMAL; + /* FIXME: If there is a top-level inline-asm, + the pass immediately returns. */ + if (symtab->first_asm_symbol ()) + return 0; + record_accesses (); + prune_escaped_types (); + analyze_types (); + + ret = rewrite_functions (); + } + else if (mode == COMPLETE_STRUCT_RELAYOUT) + { + if (dump_file) + fprintf (dump_file, "\n\nTry Complete Struct Relayout:\n"); + current_mode = COMPLETE_STRUCT_RELAYOUT; + if (symtab->first_asm_symbol ()) + return 0; + record_accesses (); + prune_escaped_types (); + + ret = execute_struct_relayout (); + } - return rewrite_functions (); + return ret; } const pass_data pass_data_ipa_struct_reorg = @@ -3991,7 +4850,11 @@ public: virtual bool gate (function *); virtual unsigned int execute (function *) { - return ipa_struct_reorg ().execute (); + unsigned int ret = 0; + ret = ipa_struct_reorg ().execute (NORMAL); + if (!ret) + ret = ipa_struct_reorg ().execute (COMPLETE_STRUCT_RELAYOUT); + return ret; } }; // class pass_ipa_struct_reorg @@ -3999,10 +4862,11 @@ public: bool pass_ipa_struct_reorg::gate (function *) { - return (optimize + return (optimize >= 3 && flag_ipa_struct_reorg /* Don't bother doing anything if the program has errors. */ - && !seen_error ()); + && !seen_error () + && flag_lto_partition == LTO_PARTITION_ONE); } } // anon namespace diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.h b/gcc/ipa-struct-reorg/ipa-struct-reorg.h index a5879407012..ef7f4c78080 100644 --- a/gcc/ipa-struct-reorg/ipa-struct-reorg.h +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.h @@ -120,6 +120,9 @@ private: public: tree newtype[max_split]; bool visited; + /* Negative number means it has illegal allocated arrays + that we do not optimize. */ + int has_alloc_array; // Constructors srtype (tree type); @@ -232,4 +235,34 @@ struct srdecl } // namespace struct_reorg + +namespace struct_relayout { + +const int min_relayout_split = 8; +const int max_relayout_split = 16; + +struct csrtype +{ + tree type; + unsigned HOST_WIDE_INT old_size; + unsigned HOST_WIDE_INT new_size; + unsigned field_count; + tree struct_size; + + // Constructors + csrtype () + : type (NULL), + old_size (0), + new_size (0), + field_count (0), + struct_size (NULL) + {} + + // Methods + unsigned calculate_field_num (tree field_offset); + void init_type_info (void); +}; + +} // namespace struct_relayout + #endif diff --git a/gcc/testsuite/g++.dg/struct/no-body-function.cpp b/gcc/testsuite/g++.dg/struct/no-body-function.cpp new file mode 100644 index 00000000000..4e56e73fcae --- /dev/null +++ b/gcc/testsuite/g++.dg/struct/no-body-function.cpp @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-std=gnu++17 -Wno-builtin-declaration-mismatch -O3 -fwhole-program -flto-partition=one -fipa-struct-reorg -S" } */ + +struct S { + int x; + double y; +}; +S f(); + +const auto [x0, y0] = f(); +const auto [x1, y1] = f(); + +static union { +int a; +double b; +}; + +const auto [x2, y2] = f(); diff --git a/gcc/testsuite/g++.dg/struct/struct-reorg-1.cpp b/gcc/testsuite/g++.dg/struct/struct-reorg-1.cpp new file mode 100644 index 00000000000..6ab71abe140 --- /dev/null +++ b/gcc/testsuite/g++.dg/struct/struct-reorg-1.cpp @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fwhole-program -flto-partition=one -fipa-struct-reorg -fdump-ipa-struct_reorg-details -S" } */ + +struct Foo { int foo; int a; }; +Foo& ignoreSetMutex = *(new Foo); + +struct Goo { int goo; int a; }; + +int main () +{ + Goo* a; + return a->goo = 90; +} diff --git a/gcc/testsuite/g++.dg/struct/struct-reorg-2.cpp b/gcc/testsuite/g++.dg/struct/struct-reorg-2.cpp new file mode 100644 index 00000000000..72b7db8a9ce --- /dev/null +++ b/gcc/testsuite/g++.dg/struct/struct-reorg-2.cpp @@ -0,0 +1,17 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -fwhole-program -flto-partition=one -fipa-struct-reorg -fdump-ipa-struct_reorg-details" } */ + +#include + +struct testg { + int b; + float c; +}; + +testg *testgvar; +int main () +{ + testgvar = (testg*) calloc(10, sizeof(testg)); + int b = testgvar->b; + return b; +} diff --git a/gcc/testsuite/g++.dg/struct/struct-reorg-3.cpp b/gcc/testsuite/g++.dg/struct/struct-reorg-3.cpp new file mode 100644 index 00000000000..771164a96e7 --- /dev/null +++ b/gcc/testsuite/g++.dg/struct/struct-reorg-3.cpp @@ -0,0 +1,24 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -fwhole-program -flto-partition=one -fipa-struct-reorg -fdump-ipa-struct_reorg-details" } */ + +#include + +struct testg { + int b; + float c; + double d; + double e; + double f; + double h; + double i; + double j; + int k; +}; + +testg *testgvar; +int main () +{ + testgvar = (testg*) calloc(10, sizeof(testg)); + int b = testgvar->b; + return b; +} diff --git a/gcc/testsuite/g++.dg/struct/struct-reorg.exp b/gcc/testsuite/g++.dg/struct/struct-reorg.exp new file mode 100644 index 00000000000..e3ffe138819 --- /dev/null +++ b/gcc/testsuite/g++.dg/struct/struct-reorg.exp @@ -0,0 +1,26 @@ +# Copyright (C) 2021-2023 Free Software Foundation, Inc. + +# This program 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 of the License, or +# (at your option) any later version. +# +# This program 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 +# . + +load_lib g++-dg.exp + +# Initialize `dg'. +dg-init + +g++-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.cpp]] \ + "" "" + +# All done. +dg-finish \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/csr_1.c b/gcc/testsuite/gcc.dg/struct/csr_1.c new file mode 100644 index 00000000000..811030bf167 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/csr_1.c @@ -0,0 +1,60 @@ +// { dg-do run } + +#include +#include + +typedef struct node node_t; +typedef struct node* node_p; + +struct node { + unsigned long a; + unsigned long b; + node_p c; + node_p d; + long e; + long f; + long g; + long h; + long i; + long j; + long k; + long l; + int m; + int n; +}; + +const int MAX = 10000; +node_p n; + +int +main () +{ + n = (node_p) calloc (MAX, sizeof (node_t)); + + for (int i = 0; i < MAX; i++) + { + n[i].a = 100; + } + for (int i = 0; i < MAX; i++) + { + if (n[i].a != 100) + { + abort (); + } + } + + for (int i = 0; i < MAX; i++) + { + n[i].l = n[i].a; + } + for (int i = 0; i < MAX; i++) + { + if (n[i].l != 100) + { + abort (); + } + } + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform in Complete Structure Relayout is 1" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/csr_allocation-1.c b/gcc/testsuite/gcc.dg/struct/csr_allocation-1.c new file mode 100644 index 00000000000..63bb695ae14 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/csr_allocation-1.c @@ -0,0 +1,46 @@ +#include +#include + +typedef struct node node_t; +typedef struct node* node_p; + +struct node { + unsigned long a; + unsigned long b; + node_p c; + node_p d; + long e; + long f; + long g; + long h; + long i; + long j; + long k; + long l; + int m; + int n; +}; + +const int MAX = 1; +node_p n; + +int +main () +{ + n = (node_p) calloc (MAX, sizeof (node_t)); + + for (int i = 0; i < MAX; i++) + { + n[i].a = 100; + } + for (int i = 0; i < MAX; i++) + { + if (n[i].a != 100) + { + abort (); + } + } + return 0; +} + +/* { dg-final { scan-ipa-dump "No structures to transform in Complete Structure Relayout." "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/csr_allocation-2.c b/gcc/testsuite/gcc.dg/struct/csr_allocation-2.c new file mode 100644 index 00000000000..0f75d5d121c --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/csr_allocation-2.c @@ -0,0 +1,59 @@ +#include +#include + +typedef struct node node_t; +typedef struct node* node_p; + +struct node { + unsigned long a; + unsigned long b; + node_p c; + node_p d; + long e; + long f; + long g; + long h; + long i; + long j; + long k; + long l; + int m; + int n; +}; + +const int MAX = 10; +node_p n; +node_p m; + +int main() +{ + int i; + for (i = 0; i < MAX / 5; i++) + { + n = (node_p) calloc(MAX, sizeof(node_t)); + if (i == 0) + { + m = n; + } + } + + for (int i = 0; i < MAX; i++) + { + n[i].a = 100; + } + for (int i = 0; i < MAX; i++) + { + m[i].a = 50; + } + + for (int i = 0; i < MAX; i++) + { + if (n[i].a != 100) + { + abort (); + } + } + return 0; +} + +/* { dg-final { scan-ipa-dump "No structures to transform in Complete Structure Relayout." "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/csr_allocation-3.c b/gcc/testsuite/gcc.dg/struct/csr_allocation-3.c new file mode 100644 index 00000000000..3dcb674c6e9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/csr_allocation-3.c @@ -0,0 +1,77 @@ +#include +#include + +typedef struct node node_t; +typedef struct node* node_p; + +struct node { + unsigned long a; + unsigned long b; + node_p c; + node_p d; + long e; + long f; + long g; + long h; + long i; + long j; + long k; + long l; + int m; + int n; +}; + +const int MAX = 10; +node_p n; +node_p m; + +void test (int, int) __attribute__((noinline)); + +void +test (int num, int flag) +{ + if (num <= 0) + { + return; + } + n = (node_p) calloc (num, sizeof (node_t)); + if (flag) + { + m = n; + } + return; +} + +int +main () +{ + test (MAX, 1); + test (MAX, 0); + + for (int i = 0; i < MAX; i++) + { + n[i].a = 100; + } + for (int i = 0; i < MAX; i++) + { + m[i].a = 50; + } + + for (int i = 0; i < MAX; i++) + { + if (n[i].a != 100) + { + abort (); + } + } + for (int i = 0; i < MAX; i++) + { + if (m[i].a != 50) + { + abort (); + } + } + return 0; +} + +/* { dg-final { scan-ipa-dump "No structures to transform in Complete Structure Relayout." "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/csr_cast_int.c b/gcc/testsuite/gcc.dg/struct/csr_cast_int.c new file mode 100644 index 00000000000..6907158c9b0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/csr_cast_int.c @@ -0,0 +1,52 @@ +// { dg-do run } + +#include +#include + +typedef struct node node_t; +typedef struct node* node_p; + +struct node { + unsigned long a; + unsigned long b; + node_p c; + node_p d; + long e; + long f; + long g; + long h; + long i; + long j; + long k; + long l; + int m; + int n; +}; + +const int MAX = 100; +node_p n; +unsigned long y; + +int +main () +{ + n = (node_p) calloc (MAX, sizeof (node_t)); + + for (int i = 0; i < MAX; i++) + { + n[i].b = 50; + } + + node_p x = &n[5]; + y = (unsigned long) x; + y += 8; + + if (*((unsigned long*) y) != 50) + { + abort (); + } + + return 0; +} + +/* { dg-final { scan-ipa-dump "struct node has escaped: \"Type escapes a cast from/to intergral type\"" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/csr_separate_instance.c b/gcc/testsuite/gcc.dg/struct/csr_separate_instance.c new file mode 100644 index 00000000000..9e5e05838e6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/csr_separate_instance.c @@ -0,0 +1,48 @@ +#include +#include + +typedef struct node node_t; +typedef struct node* node_p; + +struct node { + unsigned long a; + unsigned long b; + node_p c; + node_p d; + long e; + long f; + long g; + long h; + long i; + long j; + long k; + long l; + int m; + int n; +}; + +const int MAX = 10000; +node_p n; +node_t t; + +int +main () +{ + n = (node_p) calloc (MAX, sizeof (node_t)); + t.a = 100; + + for (int i = 0; i < MAX; i++) + { + n[i].a = t.a; + } + for (int i = 0; i < MAX; i++) + { + if (n[i].a != 100) + { + abort (); + } + } + return 0; +} + +/* { dg-final { scan-ipa-dump "struct node has escaped: \"Type escapes via a separate instance\"" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/sr_address_of_field.c b/gcc/testsuite/gcc.dg/struct/sr_address_of_field.c new file mode 100644 index 00000000000..9d58edab80a --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/sr_address_of_field.c @@ -0,0 +1,37 @@ +/* { dg-do run } */ + +static struct S { + int *p1; + int *p2; +} s; + +typedef __UINTPTR_TYPE__ uintptr_t; + +int +foo () +{ + int i = 1; + int j = 2; + struct S s; + int **p; + s.p1 = &i; + s.p2 = &j; + p = &s.p1; + uintptr_t pi = (uintptr_t) p; + pi = pi + sizeof (int *); + p = (int **)pi; + **p = 3; + return j; +} + +int +main () +{ + if (foo () != 3) + { + __builtin_abort (); + } + return 0; +} + +/* { dg-final { scan-ipa-dump "struct S has escaped: \"Type escapes via taking the address of field\"" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/sr_convert_mem.c b/gcc/testsuite/gcc.dg/struct/sr_convert_mem.c new file mode 100644 index 00000000000..a99ee0de484 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/sr_convert_mem.c @@ -0,0 +1,23 @@ +/* { dg-do compile } */ + +struct T1 { + long var1; + int var2; +}; + +struct T2 { + long var1; + int var2; +}; + +void test (void*); + +__attribute__((used)) void +foo (struct T2 *t2) +{ + struct T1* t1 = (void *)(&t2[1]); + void* data = (void *)(&t1[1]); + + test(data); + return; +} diff --git a/gcc/testsuite/gcc.dg/struct/sr_maxmin_expr.c b/gcc/testsuite/gcc.dg/struct/sr_maxmin_expr.c new file mode 100644 index 00000000000..fb135ef0bb5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/sr_maxmin_expr.c @@ -0,0 +1,25 @@ +// { dg-do compile } + +#include + +struct S { + unsigned long a; + unsigned long b; +}; + +struct S* s; +struct S* t = (struct S*) 1000; + +int +main () +{ + s = (struct S*) calloc (1000, sizeof (struct S)); + s = s > t ? s : t; + if (s == 0) + { + abort (); + } + return 0; +} + +/* { dg-final { scan-ipa-dump "No structures to transform." "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/sr_pointer_and.c b/gcc/testsuite/gcc.dg/struct/sr_pointer_and.c new file mode 100644 index 00000000000..9a4b10d9aef --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/sr_pointer_and.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ + +struct test {long val; struct test* next; }; + +unsigned long P_DATA; + +void func (struct test*); + +__attribute__((used)) static void +foo (struct test* pt) +{ + struct test t; + + t.next = (void *)((unsigned long)pt->next & P_DATA); + func(&t); + return; +} diff --git a/gcc/testsuite/gcc.dg/struct/sr_pointer_minus.c b/gcc/testsuite/gcc.dg/struct/sr_pointer_minus.c new file mode 100644 index 00000000000..9a82da0d6e1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/sr_pointer_minus.c @@ -0,0 +1,33 @@ +// { dg-do compile } + +#include + +typedef struct node node_t; +typedef struct node* node_p; + +struct node { + unsigned long a; + unsigned long b; +}; + +int max; +int x; + +node_p n; +node_p z; + +int +main () +{ + n = (node_p) calloc (max, sizeof (node_t)); + + node_p xp = &n[x]; + + if (xp - z == 10) + { + abort (); + } + return 0; +} + +/* { dg-final { scan-ipa-dump "struct node has escaped: \"Type escapes via a unhandled rewrite stmt\"" "struct_reorg" } } */ -- Gitee From 56e58ab480f29ec82c3b62d74669e42e0b1078b3 Mon Sep 17 00:00:00 2001 From: eastb233 Date: Fri, 21 Jul 2023 11:20:51 +0800 Subject: [PATCH 3/7] Some bugfix for structure reorganization Some bugfix for structure reorganization, 1. disable type simplify in LTO within optimizations 2. only enable optimizations in C language 3. use new to initialize allocated memory in symbol-summary.h 4. cover escape scenarios not considered --- gcc/ipa-free-lang-data.cc | 11 ++ gcc/ipa-struct-reorg/ipa-struct-reorg.cc | 101 +++++++++++-------- gcc/symbol-summary.h | 13 ++- gcc/testsuite/gcc.dg/struct/struct_reorg-5.c | 31 ++++++ gcc/testsuite/gcc.dg/struct/struct_reorg-6.c | 54 ++++++++++ gcc/testsuite/gcc.dg/struct/struct_reorg-7.c | 38 +++++++ gcc/testsuite/gcc.dg/struct/struct_reorg-8.c | 25 +++++ gcc/testsuite/gcc.dg/struct/struct_reorg-9.c | 54 ++++++++++ 8 files changed, 283 insertions(+), 44 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/struct/struct_reorg-5.c create mode 100644 gcc/testsuite/gcc.dg/struct/struct_reorg-6.c create mode 100644 gcc/testsuite/gcc.dg/struct/struct_reorg-7.c create mode 100644 gcc/testsuite/gcc.dg/struct/struct_reorg-8.c create mode 100644 gcc/testsuite/gcc.dg/struct/struct_reorg-9.c diff --git a/gcc/ipa-free-lang-data.cc b/gcc/ipa-free-lang-data.cc index a742156858c..5450be9fe1b 100644 --- a/gcc/ipa-free-lang-data.cc +++ b/gcc/ipa-free-lang-data.cc @@ -102,6 +102,12 @@ fld_worklist_push (tree t, class free_lang_data_d *fld) static tree fld_simplified_type_name (tree type) { + /* Simplify type will cause that struct A and struct A within + struct B are different type pointers, so skip it in structure + optimizations. */ + if (flag_ipa_struct_reorg) + return TYPE_NAME (type); + if (!TYPE_NAME (type) || TREE_CODE (TYPE_NAME (type)) != TYPE_DECL) return TYPE_NAME (type); /* Drop TYPE_DECLs in TYPE_NAME in favor of the identifier in the @@ -340,6 +346,11 @@ fld_simplified_type (tree t, class free_lang_data_d *fld) { if (!t) return t; + /* Simplify type will cause that struct A and struct A within + struct B are different type pointers, so skip it in structure + optimizations. */ + if (flag_ipa_struct_reorg) + return t; if (POINTER_TYPE_P (t)) return fld_incomplete_type_of (t, fld); /* FIXME: This triggers verification error, see PR88140. */ diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc index 46e55b203ce..202319f1ff6 100644 --- a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc @@ -105,6 +105,7 @@ along with GCC; see the file COPYING3. If not see #include "ipa-param-manipulation.h" #include "gimplify-me.h" #include "cfgloop.h" +#include "langhooks.h" namespace { @@ -196,6 +197,39 @@ gimplify_build1 (gimple_stmt_iterator *gsi, enum tree_code code, tree type, GSI_SAME_STMT); } +/* Check whether in C language or LTO with only C language. */ + +bool +lang_c_p (void) +{ + const char *language_string = lang_hooks.name; + + if (!language_string) + return false; + + if (strcmp (language_string, "GNU GIMPLE") == 0) + { + unsigned i = 0; + tree t = NULL; + const char *unit_string = NULL; + + FOR_EACH_VEC_SAFE_ELT (all_translation_units, i, t) + { + unit_string = TRANSLATION_UNIT_LANGUAGE (t); + if (!unit_string + || (strncmp (unit_string, "GNU C", 5) != 0) + || (!ISDIGIT (unit_string[5]))) + return false; + } + return true; + } + else if (strncmp (language_string, "GNU C", 5) == 0 + && ISDIGIT (language_string[5])) + return true; + + return false; +} + enum srmode { NORMAL = 0, @@ -1018,7 +1052,6 @@ public: void analyze_types (void); void clear_visited (void); bool create_new_types (void); - void restore_field_type (void); void create_new_decls (void); srdecl *find_decl (tree); void create_new_functions (void); @@ -2107,7 +2140,12 @@ ipa_struct_reorg::find_vars (gimple *stmt) srtype *t = find_type (inner_type (TREE_TYPE (rhs))); srdecl *d = find_decl (lhs); if (!d && t) - current_function->record_decl (t, lhs, -1); + { + current_function->record_decl (t, lhs, -1); + tree var = SSA_NAME_VAR (lhs); + if (var && VOID_POINTER_P (TREE_TYPE (var))) + current_function->record_decl (t, var, -1); + } } if (TREE_CODE (rhs) == SSA_NAME && VOID_POINTER_P (TREE_TYPE (rhs)) @@ -2116,7 +2154,12 @@ ipa_struct_reorg::find_vars (gimple *stmt) srtype *t = find_type (inner_type (TREE_TYPE (lhs))); srdecl *d = find_decl (rhs); if (!d && t) - current_function->record_decl (t, rhs, -1); + { + current_function->record_decl (t, rhs, -1); + tree var = SSA_NAME_VAR (rhs); + if (var && VOID_POINTER_P (TREE_TYPE (var))) + current_function->record_decl (t, var, -1); + } } } else @@ -2796,8 +2839,14 @@ ipa_struct_reorg::maybe_record_call (cgraph_node *node, gcall *stmt) if (escapes != does_not_escape) { for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) - mark_type_as_escape (TREE_TYPE (gimple_call_arg (stmt, i)), - escapes); + { + mark_type_as_escape (TREE_TYPE (gimple_call_arg (stmt, i)), + escapes); + srdecl *d = current_function->find_decl ( + gimple_call_arg (stmt, i)); + if (d) + d->type->mark_escape (escapes, stmt); + } return; } @@ -3731,42 +3780,6 @@ ipa_struct_reorg::analyze_types (void) } } -/* When struct A has a struct B member, B's type info - is not stored in - TYPE_FIELDS (TREE_TYPE (TYPE_FIELDS (typeA))) - Try to restore B's type information. */ - -void -ipa_struct_reorg::restore_field_type (void) -{ - for (unsigned i = 0; i < types.length (); i++) - { - for (unsigned j = 0; j < types[i]->fields.length (); j++) - { - srfield *field = types[i]->fields[j]; - if (TREE_CODE (inner_type (field->fieldtype)) == RECORD_TYPE) - { - /* If field type has TYPE_FIELDS information, - we do not need to do this. */ - if (TYPE_FIELDS (field->type->type) != NULL) - continue; - for (unsigned k = 0; k < types.length (); k++) - { - if (i == k) - continue; - const char *type1 = get_type_name (field->type->type); - const char *type2 = get_type_name (types[k]->type); - if (type1 == NULL || type2 == NULL) - continue; - if (type1 == type2 - && TYPE_FIELDS (types[k]->type)) - field->type = types[k]; - } - } - } - } -} - /* Create all new types we want to create. */ bool @@ -4647,7 +4660,6 @@ ipa_struct_reorg::rewrite_functions (void) { unsigned retval = 0; - restore_field_type (); /* Create new types, if we did not create any new types, then don't rewrite any accesses. */ if (!create_new_types ()) @@ -4866,7 +4878,10 @@ pass_ipa_struct_reorg::gate (function *) && flag_ipa_struct_reorg /* Don't bother doing anything if the program has errors. */ && !seen_error () - && flag_lto_partition == LTO_PARTITION_ONE); + && flag_lto_partition == LTO_PARTITION_ONE + /* Only enable struct optimizations in C since other + languages' grammar forbid. */ + && lang_c_p ()); } } // anon namespace diff --git a/gcc/symbol-summary.h b/gcc/symbol-summary.h index c54d3084cc4..3fe64047c8b 100644 --- a/gcc/symbol-summary.h +++ b/gcc/symbol-summary.h @@ -103,6 +103,12 @@ protected: /* Allocates new data that are stored within map. */ T* allocate_new () { + /* In structure optimizatons, we call new to ensure that + the allocated memory is initialized to 0. */ + if (flag_ipa_struct_reorg) + return is_ggc () ? new (ggc_internal_alloc (sizeof (T))) T () + : new T (); + /* Call gcc_internal_because we do not want to call finalizer for a type T. We call dtor explicitly. */ return is_ggc () ? new (ggc_internal_alloc (sizeof (T))) T () @@ -115,7 +121,12 @@ protected: if (is_ggc ()) ggc_delete (item); else - m_allocator.remove (item); + { + if (flag_ipa_struct_reorg) + delete item; + else + m_allocator.remove (item); + } } /* Unregister all call-graph hooks. */ diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-5.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-5.c new file mode 100644 index 00000000000..273baa9a368 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-5.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-flto -fno-use-linker-plugin" } */ + +struct D +{ + int n; + int c [8]; +}; + +struct A +{ + int i; + char *p; +}; + +struct B +{ + struct A *a; + struct D *d; +}; + +int dtInsert1 (struct B *b) +{ + struct A a = { 0, 0 }; + struct D *d; + b->a = &a; + d = b->d; + &d->c [d->n]; + return 0; +} + diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-6.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-6.c new file mode 100644 index 00000000000..455f9b501d6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-6.c @@ -0,0 +1,54 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-flto -fno-use-linker-plugin" } */ + +typedef struct basic_block_def *basic_block; +typedef struct gimple_seq_node_d *gimple_seq_node; +typedef struct gimple_seq_d *gimple_seq; +typedef struct +{ + gimple_seq_node ptr; + gimple_seq seq; + basic_block bb; +} gimple_stmt_iterator; +typedef void *gimple; +extern void exit(int); +struct gimple_seq_node_d +{ + gimple stmt; + struct gimple_seq_node_d *next; +}; +struct gimple_seq_d +{ +}; +static __inline__ gimple_stmt_iterator +gsi_start (gimple_seq seq) +{ + gimple_stmt_iterator i; + i.seq = seq; + return i; +} +static __inline__ unsigned char +gsi_end_p (gimple_stmt_iterator i) +{ + return i.ptr == ((void *)0); +} +static __inline__ void +gsi_next (gimple_stmt_iterator *i) +{ + i->ptr = i->ptr->next; +} +static __inline__ gimple +gsi_stmt (gimple_stmt_iterator i) +{ + return i.ptr->stmt; +} +void +c_warn_unused_result (gimple_seq seq) +{ + gimple_stmt_iterator i; + for (i = gsi_start (seq); !gsi_end_p (i); gsi_next (&i)) + { + gimple g = gsi_stmt (i); + if (!g) exit(0); + } +} diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-7.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-7.c new file mode 100644 index 00000000000..afc0bd86ca5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-7.c @@ -0,0 +1,38 @@ +/* { dg-do run } */ + +#include +#include + +struct gki_elem { + char *key; + int idx; +}; + +typedef struct { + struct gki_elem *table; + + int primelevel; + int nhash; + int nkeys; +} GKI; + +void * +sre_malloc(size_t size) +{ + void *ptr = malloc (size); + return ptr; +} + +__attribute__((noinline)) int +GKIStoreKey(GKI *hash) +{ + hash->table = sre_malloc(sizeof(struct gki_elem)); +} + +int +main () +{ + GKI *hash = malloc (sizeof(GKI)); + GKIStoreKey(hash); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-8.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-8.c new file mode 100644 index 00000000000..9bcfaf3681b --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-8.c @@ -0,0 +1,25 @@ +/* { dg-do run } */ + +#include +#include +#include + +typedef struct { + unsigned char blue; + unsigned char green; +} Pixel; + +typedef struct { + unsigned short colormaplength; + Pixel *colormapdata; +} TargaImage; + +TargaImage *img; + +int main() { + img = (TargaImage *) malloc( sizeof(TargaImage) ); + if (img->colormaplength > 0) { + img->colormapdata = (Pixel *) malloc(sizeof(Pixel) * img->colormaplength); + memset(img->colormapdata, 0, (sizeof(Pixel) * img->colormaplength) ); + } +} diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-9.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-9.c new file mode 100644 index 00000000000..052f4e3bdc1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-9.c @@ -0,0 +1,54 @@ +/* { dg-do run } */ + +extern void abort(void); + +struct packed_ushort { + unsigned short ucs; +} __attribute__((packed)); + +struct source { + int pos, length; +}; + +static int flag; + +static void __attribute__((noinline)) fetch(struct source *p) +{ + p->length = 128; +} + +static struct packed_ushort __attribute__((noinline)) next(struct source *p) +{ + struct packed_ushort rv; + + if (p->pos >= p->length) { + if (flag) { + flag = 0; + fetch(p); + return next(p); + } + flag = 1; + rv.ucs = 0xffff; + return rv; + } + rv.ucs = 0; + return rv; +} + +int main(void) +{ + struct source s; + int i; + + s.pos = 0; + s.length = 0; + flag = 0; + + for (i = 0; i < 16; i++) { + struct packed_ushort rv = next(&s); + if ((i == 0 && rv.ucs != 0xffff) + || (i > 0 && rv.ucs != 0)) + abort(); + } + return 0; +} -- Gitee From 36aa58ee4b7bb96518afd5778a98ed87623befda Mon Sep 17 00:00:00 2001 From: h00564365 Date: Mon, 31 Jul 2023 22:01:56 +0800 Subject: [PATCH 4/7] Structure reorder fields Introduce structure fields reordering optimization, that change fields ordering of C-like structures in order to better utilize spatial locality. --- gcc/common.opt | 4 + gcc/doc/invoke.texi | 1 + gcc/gimple-ssa-warn-access.cc | 2 +- gcc/ipa-free-lang-data.cc | 2 +- gcc/ipa-struct-reorg/escapes.def | 3 + gcc/ipa-struct-reorg/ipa-struct-reorg.cc | 2537 ++++++++++++++--- gcc/ipa-struct-reorg/ipa-struct-reorg.h | 14 +- gcc/passes.def | 1 + gcc/symbol-summary.h | 4 +- .../struct/rf_DTE_struct_instance_field.c | 75 + gcc/testsuite/gcc.dg/struct/rf_DTE_verify.c | 94 + .../gcc.dg/struct/rf_check_ptr_layers_bug.c | 24 + .../gcc.dg/struct/rf_create_fields_bug.c | 82 + .../gcc.dg/struct/rf_create_new_func_bug.c | 56 + .../gcc.dg/struct/rf_ele_minus_verify.c | 60 + .../gcc.dg/struct/rf_escape_by_base.c | 83 + .../gcc.dg/struct/rf_external_func_types.c | 69 + gcc/testsuite/gcc.dg/struct/rf_int_cast_ptr.c | 72 + .../gcc.dg/struct/rf_mem_ref_offset.c | 58 + .../struct/rf_mul_layer_ptr_record_bug.c | 30 + .../gcc.dg/struct/rf_pass_conflict.c | 109 + gcc/testsuite/gcc.dg/struct/rf_ptr2void_lto.c | 87 + gcc/testsuite/gcc.dg/struct/rf_ptr_diff.c | 71 + .../gcc.dg/struct/rf_ptr_negate_expr.c | 55 + gcc/testsuite/gcc.dg/struct/rf_ptr_offset.c | 34 + gcc/testsuite/gcc.dg/struct/rf_ptr_ptr.c | 55 + gcc/testsuite/gcc.dg/struct/rf_ptr_ptr_ptr.c | 58 + .../gcc.dg/struct/rf_rescusive_type.c | 57 + .../struct/rf_rewrite_assign_more_cmp.c | 65 + .../gcc.dg/struct/rf_rewrite_cond_bug.c | 72 + .../gcc.dg/struct/rf_rewrite_cond_more_cmp.c | 58 + .../gcc.dg/struct/rf_rewrite_phi_bug.c | 81 + gcc/testsuite/gcc.dg/struct/rf_shwi.c | 23 + gcc/testsuite/gcc.dg/struct/rf_visible_func.c | 92 + .../gcc.dg/struct/rf_void_ptr_param_func.c | 54 + gcc/testsuite/gcc.dg/struct/struct-reorg.exp | 15 +- gcc/testsuite/gcc.dg/struct/struct_reorg-1.c | 8 +- gcc/testsuite/gcc.dg/struct/struct_reorg-3.c | 9 +- gcc/timevar.def | 1 + gcc/tree-pass.h | 1 + 40 files changed, 3792 insertions(+), 484 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/struct/rf_DTE_struct_instance_field.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_DTE_verify.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_check_ptr_layers_bug.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_create_fields_bug.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_create_new_func_bug.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_ele_minus_verify.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_escape_by_base.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_external_func_types.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_int_cast_ptr.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_mem_ref_offset.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_mul_layer_ptr_record_bug.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_pass_conflict.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_ptr2void_lto.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_ptr_diff.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_ptr_negate_expr.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_ptr_offset.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_ptr_ptr.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_ptr_ptr_ptr.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_rescusive_type.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_rewrite_assign_more_cmp.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_rewrite_cond_bug.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_rewrite_cond_more_cmp.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_rewrite_phi_bug.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_shwi.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_visible_func.c create mode 100644 gcc/testsuite/gcc.dg/struct/rf_void_ptr_param_func.c diff --git a/gcc/common.opt b/gcc/common.opt index b48fa322806..81542708e23 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1949,6 +1949,10 @@ fipa-matrix-reorg Common Ignore Does nothing. Preserved for backward compatibility. +fipa-reorder-fields +Common Var(flag_ipa_reorder_fields) Init(0) Optimization +Perform structure fields reorder optimizations. + fipa-struct-reorg Common Var(flag_ipa_struct_reorg) Init(0) Optimization Perform structure layout optimizations. diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index e37bae5b17f..20dd24df569 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -526,6 +526,7 @@ Objective-C and Objective-C++ Dialects}. -finline-functions -finline-functions-called-once -finline-limit=@var{n} @gol -finline-small-functions -fipa-modref -fipa-cp -fipa-cp-clone @gol -fipa-bit-cp -fipa-vrp -fipa-pta -fipa-profile -fipa-pure-const @gol +-fipa-reorder-fields @gol -fipa-struct-reorg @gol -fipa-reference -fipa-reference-addressable @gol -fipa-stack-alignment -fipa-icf -fira-algorithm=@var{algorithm} @gol diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc index a24645783be..7f5c92c96f9 100644 --- a/gcc/gimple-ssa-warn-access.cc +++ b/gcc/gimple-ssa-warn-access.cc @@ -2198,7 +2198,7 @@ pass_waccess::gate (function *) In pass waccess, it will traverse all SSA and cause ICE when handling these unused SSA. So temporarily disable pass waccess when enable structure optimizations. */ - if (flag_ipa_struct_reorg) + if (flag_ipa_struct_reorg || flag_ipa_reorder_fields) return false; return (warn_free_nonheap_object diff --git a/gcc/ipa-free-lang-data.cc b/gcc/ipa-free-lang-data.cc index 5450be9fe1b..74965c20ab3 100644 --- a/gcc/ipa-free-lang-data.cc +++ b/gcc/ipa-free-lang-data.cc @@ -105,7 +105,7 @@ fld_simplified_type_name (tree type) /* Simplify type will cause that struct A and struct A within struct B are different type pointers, so skip it in structure optimizations. */ - if (flag_ipa_struct_reorg) + if (flag_ipa_struct_reorg || flag_ipa_reorder_fields) return TYPE_NAME (type); if (!TYPE_NAME (type) || TREE_CODE (TYPE_NAME (type)) != TYPE_DECL) diff --git a/gcc/ipa-struct-reorg/escapes.def b/gcc/ipa-struct-reorg/escapes.def index d825eb3e6f6..996a09bace2 100644 --- a/gcc/ipa-struct-reorg/escapes.def +++ b/gcc/ipa-struct-reorg/escapes.def @@ -58,5 +58,8 @@ DEF_ESCAPE (escape_ptr_ptr, "Type is used in a pointer to a pointer [not handled DEF_ESCAPE (escape_return, "Type escapes via a return [not handled yet]") DEF_ESCAPE (escape_separate_instance, "Type escapes via a separate instance") DEF_ESCAPE (escape_unhandled_rewrite, "Type escapes via a unhandled rewrite stmt") +DEF_ESCAPE (escape_via_orig_escape, "Type escapes via a original escape type") +DEF_ESCAPE (escape_instance_field, "Type escapes via a field of instance") +DEF_ESCAPE (escape_via_empty_no_orig, "Type escapes via empty and no original") #undef DEF_ESCAPE diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc index 202319f1ff6..d2c4c50c303 100644 --- a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc @@ -207,50 +207,88 @@ lang_c_p (void) if (!language_string) return false; - if (strcmp (language_string, "GNU GIMPLE") == 0) + if (lang_GNU_C ()) + return true; + else if (strcmp (language_string, "GNU GIMPLE") == 0) // for LTO check { unsigned i = 0; - tree t = NULL; - const char *unit_string = NULL; + tree t = NULL_TREE; FOR_EACH_VEC_SAFE_ELT (all_translation_units, i, t) { - unit_string = TRANSLATION_UNIT_LANGUAGE (t); - if (!unit_string - || (strncmp (unit_string, "GNU C", 5) != 0) - || (!ISDIGIT (unit_string[5]))) + language_string = TRANSLATION_UNIT_LANGUAGE (t); + if (language_string == NULL + || strncmp (language_string, "GNU C", 5) + || (language_string[5] != '\0' + && !(ISDIGIT (language_string[5])))) return false; } return true; } - else if (strncmp (language_string, "GNU C", 5) == 0 - && ISDIGIT (language_string[5])) - return true; - return false; } +/* Get the number of pointer layers. */ + +int +get_ptr_layers (tree expr) +{ + int layers = 0; + while (POINTER_TYPE_P (expr) || TREE_CODE (expr) == ARRAY_TYPE) + { + layers++; + expr = TREE_TYPE (expr); + } + return layers; +} + +/* Comparison pointer layers. */ + +bool +cmp_ptr_layers (tree a, tree b) +{ + return get_ptr_layers (a) == get_ptr_layers (b); +} + +/* Return true if the ssa_name comes from the void* parameter. */ + +bool +is_from_void_ptr_parm (tree ssa_name) +{ + gcc_assert (TREE_CODE (ssa_name) == SSA_NAME); + tree var = SSA_NAME_VAR (ssa_name); + return (var && TREE_CODE (var) == PARM_DECL + && VOID_POINTER_P (TREE_TYPE (ssa_name))); +} + enum srmode { NORMAL = 0, - COMPLETE_STRUCT_RELAYOUT + COMPLETE_STRUCT_RELAYOUT, + STRUCT_REORDER_FIELDS }; -static bool is_result_of_mult (tree, tree *, tree); +static bool is_result_of_mult (tree arg, tree *num, tree struct_size); +static bool isptrptr (tree type); -} // anon namespace +srmode current_mode; +} // anon namespace namespace struct_reorg { +hash_map > fields_to_finish; + /* Constructor of srfunction. */ srfunction::srfunction (cgraph_node *n) : node (n), old (NULL), newnode (NULL), - newf (NULL) -{} + newf (NULL), + is_safe_func (false) +{ +} /* Add an ARG to the list of arguments for the function. */ @@ -400,12 +438,13 @@ srtype::add_field_site (srfield *field) /* Constructor of DECL. */ -srdecl::srdecl (srtype *tp, tree decl, int argnum) +srdecl::srdecl (srtype *tp, tree decl, int argnum, tree orig_type) : type (tp), decl (decl), func (NULL_TREE), argumentnum (argnum), - visited (false) + visited (false), + orig_type (orig_type) { if (TREE_CODE (decl) == SSA_NAME) func = current_function_decl; @@ -429,17 +468,23 @@ srfunction::find_decl (tree decl) /* Record DECL of the TYPE with argument num ARG. */ srdecl * -srfunction::record_decl (srtype *type, tree decl, int arg) +srfunction::record_decl (srtype *type, tree decl, int arg, tree orig_type) { // Search for the decl to see if it is already there. srdecl *decl1 = find_decl (decl); if (decl1) - return decl1; + { + /* Added the orig_type information. */ + if (!decl1->orig_type && orig_type && isptrptr (orig_type)) + decl1->orig_type = orig_type; + return decl1; + } gcc_assert (type); - decl1 = new srdecl (type, decl, arg); + orig_type = isptrptr (TREE_TYPE (decl)) ? TREE_TYPE (decl) : orig_type; + decl1 = new srdecl (type, decl, arg, isptrptr (orig_type)? orig_type : NULL); decls.safe_push (decl1); return decl1; } @@ -503,31 +548,21 @@ srtype::dump (FILE *f) print_generic_expr (f, type); fprintf (f, "(%d) { ", TYPE_UID (type)); if (escapes != does_not_escape) - fprintf (f, " escapes = \"%s\"\n", escape_reason ()); - fprintf (f, " fields = { "); + fprintf (f, "escapes = \"%s\"", escape_reason ()); + fprintf (f, "\nfields = {\n"); FOR_EACH_VEC_ELT (fields, i, field) - { - if (i == 0) - fprintf (f, "\n "); - else - fprintf (f, "\n, "); - field->dump (f); - } - fprintf (f, " }\n "); - fprintf (f, "\n accesses = {"); + field->dump (f); + fprintf (f, "}\n "); + + fprintf (f, "\naccesses = {\n"); FOR_EACH_VEC_ELT (accesses, i, access) - { - fprintf (f, "\n"); - access->dump (f); - } - fprintf (f, " }\n "); - fprintf (f, "\n functions = {"); + access->dump (f); + fprintf (f, "}\n "); + + fprintf (f, "\nfunctions = {\n"); FOR_EACH_VEC_ELT (functions, i, fn) - { - fprintf (f, " \n"); - fn->simple_dump (f); - } - fprintf (f, "\n }\n"); + fn->simple_dump (f); + fprintf (f, "}\n"); fprintf (f, "}\n"); } @@ -537,6 +572,8 @@ void srtype::simple_dump (FILE *f) { print_generic_expr (f, type); + if (current_mode == STRUCT_REORDER_FIELDS) + fprintf (f, "(%d)", TYPE_UID (type)); } /* Analyze the type and decide what to be done with it. */ @@ -572,6 +609,12 @@ srfield::create_new_fields (tree newtype[max_split], tree newfields[max_split], tree newlast[max_split]) { + if (current_mode == STRUCT_REORDER_FIELDS) + { + create_new_reorder_fields (newtype, newfields, newlast); + return; + } + tree nt[max_split]; for (unsigned i = 0; i < max_split; i++) @@ -620,6 +663,104 @@ srfield::create_new_fields (tree newtype[max_split], } } +/* Reorder fields. */ + +void +srfield::reorder_fields (tree newfields[max_split], tree newlast[max_split], + tree &field) +{ + /* Reorder fields in descending. + newfields: always stores the first member of the chain + and with the largest size. + field: indicates the node to be inserted. */ + if (newfields[clusternum] == NULL) + { + newfields[clusternum] = field; + newlast[clusternum] = field; + } + else + { + tree tmp = newfields[clusternum]; + if (tree_to_uhwi (TYPE_SIZE (TREE_TYPE (field))) + > tree_to_uhwi (TYPE_SIZE (TREE_TYPE (tmp)))) + { + DECL_CHAIN (field) = tmp; + newfields[clusternum] = field; + } + else + { + while (DECL_CHAIN (tmp) + && (tree_to_uhwi (TYPE_SIZE (TREE_TYPE (field))) + <= tree_to_uhwi ( + TYPE_SIZE (TREE_TYPE (DECL_CHAIN (tmp)))))) + tmp = DECL_CHAIN (tmp); + + /* now tmp size > field size + insert field: tmp -> xx ==> tmp -> field -> xx. */ + DECL_CHAIN (field) = DECL_CHAIN (tmp); // field -> xx + DECL_CHAIN (tmp) = field; // tmp -> field + } + } +} + +/* Create the new reorder fields for this field. + newtype[max_split]: srtype's member variable, + newfields[max_split]: created by create_new_type func, + newlast[max_split]: created by create_new_type func. */ + +void +srfield::create_new_reorder_fields (tree newtype[max_split], + tree newfields[max_split], + tree newlast[max_split]) +{ + /* newtype, corresponding to newtype[max_split] in srtype. */ + tree nt = NULL_TREE; + if (type == NULL) + /* Common var. */ + nt = fieldtype; + else + { + /* RECORD_TYPE var. */ + if (type->has_escaped ()) + nt = type->type; + else + nt = type->newtype[0]; + } + tree field = make_node (FIELD_DECL); + + /* Used for recursive types. + fields_to_finish: hase_map in the format of "type: {fieldA, fieldB}", + key : indicates the original type, + vaule: filed that need to be updated to newtype. */ + if (nt == NULL) + { + nt = make_node (RECORD_TYPE); + auto_vec &fields + = fields_to_finish.get_or_insert (inner_type (type->type)); + fields.safe_push (field); + } + + DECL_NAME (field) = DECL_NAME (fielddecl); + if (type == NULL) + /* Common members do not need to reconstruct. + Otherwise, int* -> int** or void* -> void**. */ + TREE_TYPE (field) = nt; + else + TREE_TYPE (field) = reconstruct_complex_type (TREE_TYPE (fielddecl), nt); + DECL_SOURCE_LOCATION (field) = DECL_SOURCE_LOCATION (fielddecl); + SET_DECL_ALIGN (field, DECL_ALIGN (fielddecl)); + DECL_USER_ALIGN (field) = DECL_USER_ALIGN (fielddecl); + TREE_ADDRESSABLE (field) = TREE_ADDRESSABLE (fielddecl); + DECL_NONADDRESSABLE_P (field) = !TREE_ADDRESSABLE (fielddecl); + TREE_THIS_VOLATILE (field) = TREE_THIS_VOLATILE (fielddecl); + DECL_CONTEXT (field) = newtype[clusternum]; + + reorder_fields (newfields, newlast, field); + + /* srfield member variable, which stores the new field decl. */ + newfield[0] = field; +} + /* Create the new TYPE corresponding to THIS type. */ bool @@ -655,7 +796,8 @@ srtype::create_new_type (void) /* If the fields' types did have a change or we are not splitting the struct into two clusters, then just return false and don't change the type. */ - if (!createnewtype && maxclusters == 0) + if (!createnewtype && maxclusters == 0 + && current_mode != STRUCT_REORDER_FIELDS) { newtype[0] = type; return false; @@ -664,6 +806,7 @@ srtype::create_new_type (void) /* Should have at most max_split clusters. */ gcc_assert (maxclusters < max_split); + /* Record the first member of the field chain. */ tree newfields[max_split]; tree newlast[max_split]; @@ -682,7 +825,8 @@ srtype::create_new_type (void) sprintf (id, "%d", i); if (tname) { - name = concat (tname, ".reorg.", id, NULL); + name = concat (tname, current_mode == STRUCT_REORDER_FIELDS + ? ".reorder." : ".reorg.", id, NULL); TYPE_NAME (newtype[i]) = build_decl (UNKNOWN_LOCATION, TYPE_DECL, get_identifier (name), @@ -718,6 +862,7 @@ srtype::create_new_type (void) for (unsigned i = 0; i < maxclusters; i++) { print_generic_expr (dump_file, newtype[i]); + fprintf (dump_file, "(%d)", TYPE_UID (newtype[i])); fprintf (dump_file, "\n"); } } @@ -776,8 +921,12 @@ srfunction::create_new_decls (void) tree newinner[max_split]; memset (newinner, 0, sizeof (newinner)); for (unsigned j = 0; j < max_split && type->newtype[j]; j++) - newtype1[j] = reconstruct_complex_type (TREE_TYPE (decls[i]->decl), - type->newtype[j]); + { + newtype1[j] = reconstruct_complex_type ( + isptrptr (decls[i]->orig_type) ? decls[i]->orig_type + : TREE_TYPE (decls[i]->decl), + type->newtype[j]); + } if (inner) { srdecl *in = find_decl (inner); @@ -825,7 +974,8 @@ srfunction::create_new_decls (void) sprintf (id, "%d", j); if (tname) { - name = concat (tname, ".reorg.", id, NULL); + name = concat (tname, current_mode == STRUCT_REORDER_FIELDS + ? ".reorder." : ".reorg.", id, NULL); new_name = get_identifier (name); free (name); } @@ -850,7 +1000,6 @@ srfunction::create_new_decls (void) if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "Created New decls for decl:\n"); - fprintf (dump_file, "\n"); decls[i]->dump (dump_file); fprintf (dump_file, "\n"); for (unsigned j = 0; j < max_split && decls[i]->newdecl[j]; j++) @@ -876,7 +1025,7 @@ srfield::dump (FILE *f) fprintf (f, ", offset = " HOST_WIDE_INT_PRINT_DEC, offset); fprintf (f, ", type = "); print_generic_expr (f, fieldtype); - fprintf (f, "\n}\n"); + fprintf (f, "}\n"); } /* A simplified dump out the field structure to FILE. */ @@ -908,7 +1057,8 @@ sraccess::dump (FILE *f) fprintf (f, " in function: %s/%d", node->name (), node->order); fprintf (f, ", stmt:\n"); print_gimple_stmt (f, stmt, 0); - fprintf (f, "\n }\n"); + fprintf (f, "}\n"); + } /* Dump out the decl structure to FILE. */ @@ -1023,8 +1173,7 @@ public: // Constructors ipa_struct_reorg (void) : current_function (NULL), - done_recording (false), - current_mode (NORMAL) + done_recording (false) {} // Fields @@ -1032,9 +1181,10 @@ public: auto_vec_del functions; srglobal globals; srfunction *current_function; + hash_set safe_functions; + auto_vec ext_func_types; bool done_recording; - srmode current_mode; // Methods unsigned execute (enum srmode mode); @@ -1042,6 +1192,7 @@ public: gimple *stmt = NULL); void dump_types (FILE *f); + void dump_newtypes (FILE *f); void dump_types_escaped (FILE *f); void dump_functions (FILE *f); void record_accesses (void); @@ -1049,6 +1200,9 @@ public: bool walk_field_for_cycles (srtype *); void prune_escaped_types (void); void propagate_escape (void); + void propagate_escape_via_original (void); + void propagate_escape_via_empty_with_no_original (void); + void propagate_escape_via_ext_func_types (void); void analyze_types (void); void clear_visited (void); bool create_new_types (void); @@ -1060,8 +1214,11 @@ public: srdecl *record_var (tree decl, escape_type escapes = does_not_escape, int arg = -1); + void record_safe_func_with_void_ptr_parm (void); srfunction *record_function (cgraph_node *node); srfunction *find_function (cgraph_node *node); + void record_field_type (tree field, srtype *base_srtype); + void record_struct_field_types (tree base_type, srtype *base_srtype); srtype *record_type (tree type); void process_union (tree type); srtype *find_type (tree type); @@ -1072,7 +1229,7 @@ public: void record_stmt_expr (tree expr, cgraph_node *node, gimple *stmt); void mark_expr_escape (tree, escape_type, gimple *stmt); bool handled_allocation_stmt (gimple *stmt); - tree allocate_size (srtype *t, gimple *stmt); + tree allocate_size (srtype *t, srdecl *decl, gimple *stmt); void mark_decls_in_as_not_needed (tree fn); @@ -1087,21 +1244,23 @@ public: bool ignore_missing_decl = false); bool rewrite_lhs_rhs (tree lhs, tree rhs, tree newlhs[max_split], tree newrhs[max_split]); - bool get_type_field (tree expr, tree &base, bool &indirect, - srtype *&type, srfield *&field, - bool &realpart, bool &imagpart, - bool &address, bool should_create = false, - bool can_escape = false); + bool get_type_field (tree expr, tree &base, bool &indirect, srtype *&type, + srfield *&field, bool &realpart, bool &imagpart, + bool &address, bool& escape_from_base, + bool should_create = false, bool can_escape = false); bool wholeaccess (tree expr, tree base, tree accesstype, srtype *t); void check_alloc_num (gimple *stmt, srtype *type); + void check_definition_assign (srdecl *decl, vec &worklist); + void check_definition_call (srdecl *decl, vec &worklist); void check_definition (srdecl *decl, vec &); void check_uses (srdecl *decl, vec &); void check_use (srdecl *decl, gimple *stmt, vec &); - void check_type_and_push (tree newdecl, srtype *type, + void check_type_and_push (tree newdecl, srdecl *decl, vec &worklist, gimple *stmt); void check_other_side (srdecl *decl, tree other, gimple *stmt, vec &worklist); + void check_ptr_layers (tree a_expr, tree b_expr, gimple* stmt); void find_vars (gimple *stmt); void find_var (tree expr, gimple *stmt); @@ -1703,9 +1862,45 @@ ipa_struct_reorg::dump_types (FILE *f) srtype *type; FOR_EACH_VEC_ELT (types, i, type) { + fprintf (f, "======= the %dth type: ======\n", i); type->dump (f); + fprintf (f, "\n"); + } +} + +/* Dump all of the created newtypes to file F. */ + +void +ipa_struct_reorg::dump_newtypes (FILE *f) +{ + unsigned i = 0; + srtype *type = NULL; + FOR_EACH_VEC_ELT (types, i, type) + { + if (type->has_escaped ()) + { + continue; + } + fprintf (f, "======= the %dth newtype: ======\n", i); + fprintf (f, "type : "); + print_generic_expr (f, type->newtype[0]); + fprintf (f, "(%d) ", TYPE_UID (type->newtype[0])); + fprintf (f, "{ "); + fprintf (f, "\nfields = {\n"); + + for (tree field = TYPE_FIELDS (TYPE_MAIN_VARIANT (type->newtype[0])); + field; field = DECL_CHAIN (field)) + { + fprintf (f, "field (%d) ", DECL_UID (field)); + fprintf (f, "{"); + fprintf (f, "type = "); + print_generic_expr (f, TREE_TYPE (field)); + fprintf (f, "}\n"); + } + fprintf (f, "}\n "); + + fprintf (f, "\n"); } - fprintf (f, "\n"); } /* Dump all of the recorded types to file F. */ @@ -1803,6 +1998,8 @@ isarraytype (tree type) static bool isptrptr (tree type) { + if (type == NULL) + return false; bool firstptr = false; while (POINTER_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE) { @@ -1817,150 +2014,736 @@ isptrptr (tree type) return false; } -/* Return the escape type which corresponds to if - this is an volatile type, an array type or a pointer - to a pointer type. */ +/* Adding node to map and stack. */ -static escape_type -escape_type_volatile_array_or_ptrptr (tree type) +bool +add_node (tree node, int layers, hash_map &map, + auto_vec &stack) { - if (isvolatile_type (type)) - return escape_volatile; - if (isarraytype (type)) - return escape_array; - if (isptrptr (type)) - return escape_ptr_ptr; - return does_not_escape; + if (TREE_CODE (node) != SSA_NAME) + return false; + if (map.get (node) == NULL) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, " "); + fprintf (dump_file, "add node: \t\t"); + print_generic_expr (dump_file, node); + fprintf (dump_file, ",\t\tptr layers: %d: \n", layers); + } + map.put (node, layers); + stack.safe_push (node); + } + else if (*map.get (node) != layers) + return false; + return true; } -/* Record TYPE if not already recorded. */ +/* Check the number of pointer layers of the gimple phi in definition. */ -srtype * -ipa_struct_reorg::record_type (tree type) +bool +check_def_phi (tree def_node, hash_map &ptr_layers) { - unsigned typeuid; - - /* Get the main variant as we are going - to record that type only. */ - type = TYPE_MAIN_VARIANT (type); - typeuid = TYPE_UID (type); + bool res = true; + gimple *def_stmt = SSA_NAME_DEF_STMT (def_node); + for (unsigned j = 0; j < gimple_phi_num_args (def_stmt); j++) + { + tree phi_node = gimple_phi_arg_def (def_stmt, j); + if (integer_zerop (phi_node)) + continue; + if (ptr_layers.get (phi_node) == NULL) + return false; + res &= *ptr_layers.get (def_node) == *ptr_layers.get (phi_node); + } + return res; +} - srtype *type1; +/* Check the number of pointer layers of the gimple assign in definition. */ - type1 = find_type (type); - if (type1) - return type1; +bool +check_def_assign (tree def_node, hash_map &ptr_layers) +{ + bool res = true; + gimple *def_stmt = SSA_NAME_DEF_STMT (def_node); + gimple_rhs_class rhs_class = gimple_assign_rhs_class (def_stmt); + tree_code rhs_code = gimple_assign_rhs_code (def_stmt); + tree rhs1 = gimple_assign_rhs1 (def_stmt); + tree rhs1_base = TREE_CODE (rhs1) == MEM_REF ? TREE_OPERAND (rhs1, 0) : rhs1; + if (ptr_layers.get (rhs1_base) == NULL) + return false; + if (rhs_class == GIMPLE_SINGLE_RHS || rhs_class == GIMPLE_UNARY_RHS) + { + if (TREE_CODE (rhs1) == SSA_NAME) + res = *ptr_layers.get (def_node) == *ptr_layers.get (rhs1); + else if (TREE_CODE (rhs1) == MEM_REF) + res = *ptr_layers.get (def_node) + == *ptr_layers.get (TREE_OPERAND (rhs1, 0)); + else + { + return false; + } + } + else if (rhs_class == GIMPLE_BINARY_RHS) + { + if (rhs_code == POINTER_PLUS_EXPR) + res = *ptr_layers.get (def_node) == *ptr_layers.get (rhs1); + else if (rhs_code == BIT_AND_EXPR) + res = *ptr_layers.get (def_node) == *ptr_layers.get (rhs1); + else + return false; + } + else + return false; + return res; +} - /* If already done recording just return NULL. */ - if (done_recording) - return NULL; +/* Check node definition. */ +bool +check_node_def (hash_map &ptr_layers) +{ + bool res = true; if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "Recording new type: %u.\n", typeuid); - - type1 = new srtype (type); - types.safe_push (type1); - - /* If the type has an user alignment set, - that means the user most likely already setup the type. */ - if (TYPE_USER_ALIGN (type)) - type1->mark_escape (escape_user_alignment, NULL); - - for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + fprintf (dump_file, "\n======== check node definition ========\n"); + for (unsigned i = 1; i < num_ssa_names; ++i) { - if (TREE_CODE (field) == FIELD_DECL) + tree name = ssa_name (i); + if (name && ptr_layers.get (name) != NULL) { - tree t = TREE_TYPE (field); - process_union (t); - if (TREE_CODE (inner_type (t)) == UNION_TYPE - || TREE_CODE (inner_type (t)) == QUAL_UNION_TYPE) - type1->mark_escape (escape_union, NULL); - if (isvolatile_type (t)) - type1->mark_escape (escape_volatile, NULL); - escape_type e = escape_type_volatile_array_or_ptrptr (t); - if (e != does_not_escape) - type1->mark_escape (e, NULL); - if (handled_type (t)) - { - srtype *t1 = record_type (inner_type (t)); - srfield *f = type1->find_field (int_byte_position (field)); - /* We might have an variable sized type which - we don't set the handle. */ - if (f) - { - f->type = t1; - t1->add_field_site (f); - } - if (t1 == type1 && current_mode != COMPLETE_STRUCT_RELAYOUT) - type1->mark_escape (escape_rescusive_type, NULL); - } + gimple *def_stmt = SSA_NAME_DEF_STMT (name); + if (dump_file && (dump_flags & TDF_DETAILS) + && gimple_code (def_stmt) != GIMPLE_DEBUG) + print_gimple_stmt (dump_file, def_stmt, 0); + + if (gimple_code (def_stmt) == GIMPLE_PHI) + res = check_def_phi (name, ptr_layers); + else if (gimple_code (def_stmt) == GIMPLE_ASSIGN) + res = check_def_assign (name, ptr_layers); + else if (gimple_code (def_stmt) == GIMPLE_NOP) + continue; + else + return false; } } + return res; +} - return type1; +/* Check pointer usage. */ + +bool +check_record_ptr_usage (gimple *use_stmt, tree ¤t_node, + hash_map &ptr_layers, + auto_vec &ssa_name_stack) +{ + gimple_rhs_class rhs_class = gimple_assign_rhs_class (use_stmt); + tree rhs1 = gimple_assign_rhs1 (use_stmt); + tree lhs = gimple_assign_lhs (use_stmt); + if (rhs_class != GIMPLE_SINGLE_RHS + || (TREE_CODE (rhs1) != COMPONENT_REF && TREE_CODE (rhs1) != SSA_NAME) + || (TREE_CODE (lhs) != MEM_REF && TREE_CODE (lhs) != SSA_NAME)) + return false; + + bool res = true; + /* MEM[(long int *)a_1] = _1; (record). + If lhs is ssa_name, lhs cannot be the current node. + _2 = _1->flow; (No record). */ + if (TREE_CODE (rhs1) == SSA_NAME) + { + tree tmp = (rhs1 != current_node) ? rhs1 : lhs; + if (TREE_CODE (tmp) == MEM_REF) + res = add_node (TREE_OPERAND (tmp, 0), + *ptr_layers.get (current_node) + 1, + ptr_layers, ssa_name_stack); + else + res = add_node (tmp, *ptr_layers.get (current_node), + ptr_layers, ssa_name_stack); + } + else if (TREE_CODE (lhs) == SSA_NAME && TREE_CODE (rhs1) == COMPONENT_REF) + res = !(POINTER_TYPE_P (TREE_TYPE (rhs1))); + else + res = false; + return res; } -/* Mark TYPE as escaping with ESCAPES as the reason. */ +/* Check and record a single node. */ -void -ipa_struct_reorg::mark_type_as_escape (tree type, - escape_type escapes, - gimple *stmt) +bool +check_record_single_node (gimple *use_stmt, tree ¤t_node, + hash_map &ptr_layers, + auto_vec &ssa_name_stack) { - if (handled_type (type)) - { - srtype *stype = record_type (inner_type (type)); + gimple_rhs_class rhs_class = gimple_assign_rhs_class (use_stmt); + tree rhs1 = gimple_assign_rhs1 (use_stmt); + tree lhs = gimple_assign_lhs (use_stmt); + gcc_assert (rhs_class == GIMPLE_SINGLE_RHS || rhs_class == GIMPLE_UNARY_RHS); - if (!stype) - return; + if ((TREE_CODE (rhs1) != SSA_NAME && TREE_CODE (rhs1) != MEM_REF) + || (TREE_CODE (lhs) != SSA_NAME && TREE_CODE (lhs) != MEM_REF)) + return false; - stype->mark_escape (escapes, stmt); + bool res = true; + if (TREE_CODE (lhs) == SSA_NAME && TREE_CODE (rhs1) == MEM_REF) + /* add such as: _2 = MEM[(struct arc_t * *)_1]. */ + res = add_node (lhs, *ptr_layers.get (current_node) - 1, + ptr_layers, ssa_name_stack); + else if (TREE_CODE (lhs) == MEM_REF && TREE_CODE (rhs1) == SSA_NAME) + { + /* add such as: MEM[(long int *)a_1] = _1. */ + if (rhs1 == current_node) + res = add_node (TREE_OPERAND (lhs, 0), + *ptr_layers.get (current_node) + 1, + ptr_layers, ssa_name_stack); + else + res = add_node (rhs1, *ptr_layers.get (current_node) - 1, + ptr_layers, ssa_name_stack); } + else if (TREE_CODE (lhs) == SSA_NAME && TREE_CODE (rhs1) == SSA_NAME) + res = add_node (lhs, *ptr_layers.get (current_node), + ptr_layers, ssa_name_stack); + else + res = false; + + return res; } -/* Maybe process the union of type TYPE, such that marking all of the fields' - types as being escaping. */ +/* Check and record multiple nodes. */ -void -ipa_struct_reorg::process_union (tree type) +bool +check_record_mult_node (gimple *use_stmt, tree ¤t_node, + hash_map &ptr_layers, + auto_vec &ssa_name_stack) { - static hash_set unions_recorded; + gimple_rhs_class rhs_class = gimple_assign_rhs_class (use_stmt); + tree_code rhs_code = gimple_assign_rhs_code (use_stmt); + tree rhs1 = gimple_assign_rhs1 (use_stmt); + tree lhs = gimple_assign_lhs (use_stmt); + tree rhs2 = gimple_assign_rhs2 (use_stmt); + gcc_assert (rhs_class == GIMPLE_BINARY_RHS); + + if ((rhs_code != POINTER_PLUS_EXPR && rhs_code != POINTER_DIFF_EXPR + && rhs_code != BIT_AND_EXPR) + || (TREE_CODE (lhs) != SSA_NAME && TREE_CODE (rhs1) != SSA_NAME)) + return false; - type = inner_type (type); - if (TREE_CODE (type) != UNION_TYPE - && TREE_CODE (type) != QUAL_UNION_TYPE) - return; + bool res = true; + if (rhs_code == POINTER_PLUS_EXPR) + res = add_node (lhs == current_node ? rhs1 : lhs, + *ptr_layers.get (current_node), + ptr_layers, ssa_name_stack); + else if (rhs_code == POINTER_DIFF_EXPR) + res = add_node (rhs1 != current_node ? rhs1 : rhs2, + *ptr_layers.get (current_node), + ptr_layers, ssa_name_stack); + else if (rhs_code == BIT_AND_EXPR) + { + if (TREE_CODE (rhs2) != INTEGER_CST) + return false; + res = add_node (lhs == current_node ? rhs1 : lhs, + *ptr_layers.get (current_node), + ptr_layers, ssa_name_stack); + } + return res; +} - type = TYPE_MAIN_VARIANT (type); +/* Check whether gimple assign is correctly used and record node. */ - /* We already processed this type. */ - if (unions_recorded.add (type)) - return; +bool +check_record_assign (tree ¤t_node, gimple *use_stmt, + hash_map &ptr_layers, + auto_vec &ssa_name_stack) +{ + gimple_rhs_class rhs_class = gimple_assign_rhs_class (use_stmt); + if (*ptr_layers.get (current_node) == 1) + return check_record_ptr_usage (use_stmt, current_node, + ptr_layers, ssa_name_stack); + else if (*ptr_layers.get (current_node) > 1) + { + if (rhs_class != GIMPLE_BINARY_RHS + && rhs_class != GIMPLE_UNARY_RHS + && rhs_class != GIMPLE_SINGLE_RHS) + return false; - for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + if (rhs_class == GIMPLE_SINGLE_RHS || rhs_class == GIMPLE_UNARY_RHS) + return check_record_single_node (use_stmt, current_node, + ptr_layers, ssa_name_stack); + else if (rhs_class == GIMPLE_BINARY_RHS) + return check_record_mult_node (use_stmt, current_node, + ptr_layers, ssa_name_stack); + } + else + return false; + + return true; +} + +/* Check whether gimple phi is correctly used and record node. */ + +bool +check_record_phi (tree ¤t_node, gimple *use_stmt, + hash_map &ptr_layers, + auto_vec &ssa_name_stack) +{ + bool res = true; + res &= add_node (gimple_phi_result (use_stmt), *ptr_layers.get (current_node), + ptr_layers, ssa_name_stack); + + for (unsigned i = 0; i < gimple_phi_num_args (use_stmt); i++) { - if (TREE_CODE (field) == FIELD_DECL) - { - mark_type_as_escape (TREE_TYPE (field), escape_union); - process_union (TREE_TYPE (field)); - } + if (integer_zerop (gimple_phi_arg_def (use_stmt, i))) + continue; + res &= add_node (gimple_phi_arg_def (use_stmt, i), + *ptr_layers.get (current_node), + ptr_layers, ssa_name_stack); } + return res; } -/* Used by record_var function as a callback to walk_tree. - Mark the type as escaping if it has expressions which - cannot be converted for global initializations. */ +/* Check the use of callee. */ -static tree -record_init_types (tree *tp, int *walk_subtrees, void *data) +bool +check_callee (cgraph_node *node, gimple *stmt, + hash_map &ptr_layers, int input_layers) { - ipa_struct_reorg *c = (ipa_struct_reorg *)data; - switch (TREE_CODE (*tp)) + /* caller main () + { spec_qsort.constprop (_649, _651); } + def spec_qsort.constprop (void * a, size_t n) + { spec_qsort.constprop (a_1, _139); } */ + /* In safe functions, only call itself is allowed. */ + if (node->get_edge (stmt)->callee != node) + return false; + tree input_node = gimple_call_arg (stmt, 0); + if (ptr_layers.get (input_node) == NULL + || *ptr_layers.get (input_node) != input_layers) + return false; + if (SSA_NAME_VAR (input_node) != DECL_ARGUMENTS (node->decl)) + return false; + + for (unsigned i = 1; i < gimple_call_num_args (stmt); i++) { - CASE_CONVERT: - case COMPONENT_REF: - case VIEW_CONVERT_EXPR: - case ARRAY_REF: + if (ptr_layers.get (gimple_call_arg (stmt, i)) != NULL) + return false; + } + return true; +} + +/* Check the usage of input nodes and related nodes. */ + +bool +check_node_use (cgraph_node *node, tree current_node, + hash_map &ptr_layers, + auto_vec &ssa_name_stack, + int input_layers) +{ + imm_use_iterator imm_iter; + gimple *use_stmt = NULL; + bool res = true; + /* Use FOR_EACH_IMM_USE_STMT as an indirect edge + to search for possible related nodes and push to stack. */ + FOR_EACH_IMM_USE_STMT (use_stmt, imm_iter, current_node) + { + if (dump_file && (dump_flags & TDF_DETAILS) + && gimple_code (use_stmt) != GIMPLE_DEBUG) + { + fprintf (dump_file, "%*s", 4, ""); + print_gimple_stmt (dump_file, use_stmt, 0); + } + /* For other types of gimple, do not record the node. */ + if (res) + { + if (gimple_code (use_stmt) == GIMPLE_PHI) + res = check_record_phi (current_node, use_stmt, + ptr_layers, ssa_name_stack); + else if (gimple_code (use_stmt) == GIMPLE_ASSIGN) + res = check_record_assign (current_node, use_stmt, + ptr_layers, ssa_name_stack); + else if (gimple_code (use_stmt) == GIMPLE_CALL) + res = check_callee (node, use_stmt, ptr_layers, input_layers); + else if (gimple_code (use_stmt) == GIMPLE_RETURN) + res = false; + } + } + return res; +} + +/* trace the pointer layers of void node. */ + +bool +get_void_node_ptr_layers (tree input, int &input_layers) +{ + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "input type is void* node\n"); + imm_use_iterator imm_iter; + gimple *use_stmt = NULL; + FOR_EACH_IMM_USE_STMT (use_stmt, imm_iter, input) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + print_gimple_stmt (dump_file, use_stmt, 0); + if (gimple_code (use_stmt) == GIMPLE_ASSIGN + && gimple_assign_rhs_class (use_stmt) == GIMPLE_SINGLE_RHS) + { + tree rhs1 = gimple_assign_rhs1 (use_stmt); + tree lhs = gimple_assign_lhs (use_stmt); + if (TREE_CODE (lhs) == SSA_NAME && handled_type (TREE_TYPE (lhs))) + { + if (TREE_CODE (rhs1) == MEM_REF) + { + input_layers = get_ptr_layers (TREE_TYPE (lhs)) + 1; + return true; + } + } + } + } + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "end trace pointer layers of void* node\n"); + return false; +} + +/* Preparing the First Node for DFS. */ + +bool +set_init_node (cgraph_node *node, cgraph_edge *caller, + hash_map &ptr_layers, + auto_vec &ssa_name_stack, int &input_layers) +{ + /* set input_layer + caller spec_qsort.constprop (_649, _651) + |-- Obtains the actual ptr layer + from the input node. */ + caller->caller->get_untransformed_body (); + if (caller->call_stmt == NULL + || gimple_call_num_args (caller->call_stmt) == 0) + return false; + tree input = gimple_call_arg (caller->call_stmt, 0); + if (!(POINTER_TYPE_P (TREE_TYPE (input)) + || TREE_CODE (TREE_TYPE (input)) == ARRAY_TYPE)) + return false; + if (handled_type (TREE_TYPE (input))) + input_layers = get_ptr_layers (TREE_TYPE (input)); + else + { + if (VOID_POINTER_P (TREE_TYPE (input))) + { + if (!get_void_node_ptr_layers (input, input_layers)) + return false; + } + } + + /* set initial node + def spec_qsort.constprop (void * a, size_t n) + |-- Find the initial ssa_name + from the parameter node. */ + tree parm = DECL_ARGUMENTS (node->decl); + for (unsigned j = 1; j < num_ssa_names; ++j) + { + tree name = ssa_name (j); + if (!name || has_zero_uses (name) || virtual_operand_p (name)) + continue; + if (SSA_NAME_VAR (name) == parm + && gimple_code (SSA_NAME_DEF_STMT (name)) == GIMPLE_NOP) + { + if (!add_node (name, input_layers, ptr_layers, ssa_name_stack)) + return false; + } + } + return !ssa_name_stack.is_empty (); +} + +/* Check the usage of each call. */ + +bool +check_each_call (cgraph_node *node, cgraph_edge *caller) +{ + hash_map ptr_layers; + auto_vec ssa_name_stack; + int input_layers = 0; + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "======== check each call : %s/%u ========\n", + node->name (), node->order); + if (!set_init_node (node, caller, ptr_layers, ssa_name_stack, input_layers)) + return false; + int i = 0; + while (!ssa_name_stack.is_empty ()) + { + tree current_node = ssa_name_stack.pop (); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\ncur node %d: \t", i++); + print_generic_expr (dump_file, current_node); + fprintf (dump_file, ",\t\tptr layers: %d: \n", + *ptr_layers.get (current_node)); + } + if (get_ptr_layers (TREE_TYPE (current_node)) + > *ptr_layers.get (current_node)) + return false; + if (!check_node_use (node, current_node, ptr_layers, ssa_name_stack, + input_layers)) + return false; + } + + if (!check_node_def (ptr_layers)) + return false; + return true; +} + +/* Filter out function: void func (void*, int n), + and the function has no static variable, no structure-related variable, + and no global variable is used. */ + +bool +filter_func (cgraph_node *node) +{ + tree parm = DECL_ARGUMENTS (node->decl); + if (!(parm && VOID_POINTER_P (TREE_TYPE (parm)) + && VOID_TYPE_P (TREE_TYPE (TREE_TYPE (node->decl))))) + return false; + + for (parm = DECL_CHAIN (parm); parm; parm = DECL_CHAIN (parm)) + { + if (TREE_CODE (TREE_TYPE (parm)) != INTEGER_TYPE) + return false; + } + + if (DECL_STRUCT_FUNCTION (node->decl)->static_chain_decl) + return false; + + tree var = NULL_TREE; + unsigned int i = 0; + bool res = true; + FOR_EACH_LOCAL_DECL (cfun, i, var) + { + if (TREE_CODE (var) == VAR_DECL && handled_type (TREE_TYPE (var))) + res = false; + } + if (!res) + return false; + + for (unsigned j = 1; j < num_ssa_names; ++j) + { + tree name = ssa_name (j); + if (!name || has_zero_uses (name) || virtual_operand_p (name)) + continue; + tree var = SSA_NAME_VAR (name); + if (var && TREE_CODE (var) == VAR_DECL && is_global_var (var)) + return false; + } + return true; +} + +/* Check whether the function with the void* parameter and uses the input node + safely. + In these functions only component_ref can be used to dereference the last + layer of the input structure pointer. The hack operation pointer offset + after type cast cannot be used. +*/ + +bool +is_safe_func_with_void_ptr_parm (cgraph_node *node) +{ + if (!filter_func (node)) + return false; + + /* Distinguish Recursive Callers + normal_callers: main () + { spec_qsort.constprop (_649, _651); } + definition: spec_qsort.constprop (void * a, size_t n) + recursive_callers: { spec_qsort.constprop (a_1, _139); } */ + auto_vec callers = node->collect_callers (); + auto_vec normal_callers; + for (unsigned i = 0; i < callers.length (); i++) + { + if (callers[i]->caller != node) + normal_callers.safe_push (callers[i]); + } + if (normal_callers.length () == 0) + return false; + + for (unsigned i = 0; i < normal_callers.length (); i++) + { + if (!check_each_call (node, normal_callers[i])) + return false; + } + return true; +} + +/* Return the escape type which corresponds to if + this is an volatile type, an array type or a pointer + to a pointer type. */ + +static escape_type +escape_type_volatile_array_or_ptrptr (tree type) +{ + if (isvolatile_type (type)) + return escape_volatile; + if (isarraytype (type)) + return escape_array; + if (isptrptr (type) && (current_mode != STRUCT_REORDER_FIELDS)) + return escape_ptr_ptr; + return does_not_escape; +} + +/* Record field type. */ + +void +ipa_struct_reorg::record_field_type (tree field, srtype *base_srtype) +{ + tree field_type = TREE_TYPE (field); + /* The uid of the type in the structure is different + from that outside the structure. */ + srtype *field_srtype = record_type (inner_type (field_type)); + srfield *field_srfield = base_srtype->find_field (int_byte_position (field)); + /* We might have an variable sized type which we don't set the handle. */ + if (field_srfield) + { + field_srfield->type = field_srtype; + field_srtype->add_field_site (field_srfield); + } + if (field_srtype == base_srtype && current_mode != COMPLETE_STRUCT_RELAYOUT + && current_mode != STRUCT_REORDER_FIELDS) + base_srtype->mark_escape (escape_rescusive_type, NULL); + /* Types of non-pointer field are difficult to track the correctness + of the rewrite when it used by the escaped type. */ + if (current_mode == STRUCT_REORDER_FIELDS + && TREE_CODE (field_type) == RECORD_TYPE) + field_srtype->mark_escape (escape_instance_field, NULL); +} + +/* Record structure all field types. */ + +void +ipa_struct_reorg::record_struct_field_types (tree base_type, + srtype *base_srtype) +{ + for (tree field = TYPE_FIELDS (base_type); field; field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL) + { + tree field_type = TREE_TYPE (field); + process_union (field_type); + if (TREE_CODE (inner_type (field_type)) == UNION_TYPE + || TREE_CODE (inner_type (field_type)) == QUAL_UNION_TYPE) + base_srtype->mark_escape (escape_union, NULL); + if (isvolatile_type (field_type)) + base_srtype->mark_escape (escape_volatile, NULL); + escape_type e = escape_type_volatile_array_or_ptrptr (field_type); + if (e != does_not_escape) + base_srtype->mark_escape (e, NULL); + /* Types of non-pointer field are difficult to track the correctness + of the rewrite when it used by the escaped type. */ + if (current_mode == STRUCT_REORDER_FIELDS + && TREE_CODE (field_type) == RECORD_TYPE) + base_srtype->mark_escape (escape_instance_field, NULL); + if (handled_type (field_type)) + record_field_type (field, base_srtype); + } + } +} + +/* Record TYPE if not already recorded. */ + +srtype * +ipa_struct_reorg::record_type (tree type) +{ + unsigned typeuid; + + /* Get the main variant as we are going + to record that type only. */ + type = TYPE_MAIN_VARIANT (type); + typeuid = TYPE_UID (type); + + srtype *type1; + + type1 = find_type (type); + if (type1) + return type1; + + /* If already done recording just return NULL. */ + if (done_recording) + return NULL; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Recording new type: %u.\n", typeuid); + const char *type_name = get_type_name (type); + if (type_name == NULL) + fprintf (dump_file, "Recording new type NULL name\n"); + else + fprintf (dump_file, "Recording new type name: %s.\n", type_name); + } + + type1 = new srtype (type); + types.safe_push (type1); + + /* If the type has an user alignment set, + that means the user most likely already setup the type. */ + if (TYPE_USER_ALIGN (type)) + type1->mark_escape (escape_user_alignment, NULL); + + record_struct_field_types (type, type1); + + return type1; +} + +/* Mark TYPE as escaping with ESCAPES as the reason. */ + +void +ipa_struct_reorg::mark_type_as_escape (tree type, + escape_type escapes, + gimple *stmt) +{ + if (handled_type (type)) + { + srtype *stype = record_type (inner_type (type)); + + if (!stype) + return; + + stype->mark_escape (escapes, stmt); + } +} + +/* Maybe process the union of type TYPE, such that marking all of the fields' + types as being escaping. */ + +void +ipa_struct_reorg::process_union (tree type) +{ + static hash_set unions_recorded; + + type = inner_type (type); + if (TREE_CODE (type) != UNION_TYPE + && TREE_CODE (type) != QUAL_UNION_TYPE) + return; + + type = TYPE_MAIN_VARIANT (type); + + /* We already processed this type. */ + if (unions_recorded.add (type)) + return; + + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL) + { + mark_type_as_escape (TREE_TYPE (field), escape_union); + process_union (TREE_TYPE (field)); + } + } +} + +/* Used by record_var function as a callback to walk_tree. + Mark the type as escaping if it has expressions which + cannot be converted for global initializations. */ + +static tree +record_init_types (tree *tp, int *walk_subtrees, void *data) +{ + ipa_struct_reorg *c = (ipa_struct_reorg *)data; + switch (TREE_CODE (*tp)) + { + CASE_CONVERT: + case COMPONENT_REF: + case VIEW_CONVERT_EXPR: + case ARRAY_REF: { tree typeouter = TREE_TYPE (*tp); tree typeinner = TREE_TYPE (TREE_OPERAND (*tp, 0)); @@ -1996,6 +2779,8 @@ ipa_struct_reorg::record_var (tree decl, escape_type escapes, int arg) process_union (TREE_TYPE (decl)); + /* Only the structure type RECORD_TYPE is recorded. + Therefore, the void* type is filtered out. */ if (handled_type (TREE_TYPE (decl))) { type = record_type (inner_type (TREE_TYPE (decl))); @@ -2035,7 +2820,8 @@ ipa_struct_reorg::record_var (tree decl, escape_type escapes, int arg) /* Separate instance is hard to trace in complete struct relayout optimization. */ - if (current_mode == COMPLETE_STRUCT_RELAYOUT + if ((current_mode == COMPLETE_STRUCT_RELAYOUT + || current_mode == STRUCT_REORDER_FIELDS) && TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE) e = escape_separate_instance; @@ -2078,11 +2864,9 @@ ipa_struct_reorg::find_var (tree expr, gimple *stmt) { tree r = TREE_OPERAND (expr, 0); tree orig_type = TREE_TYPE (expr); - if (handled_component_p (r) - || TREE_CODE (r) == MEM_REF) + if (handled_component_p (r) || TREE_CODE (r) == MEM_REF) { - while (handled_component_p (r) - || TREE_CODE (r) == MEM_REF) + while (handled_component_p (r) || TREE_CODE (r) == MEM_REF) { if (TREE_CODE (r) == VIEW_CONVERT_EXPR) { @@ -2114,8 +2898,10 @@ ipa_struct_reorg::find_var (tree expr, gimple *stmt) srtype *type; srfield *field; bool realpart, imagpart, address; + bool escape_from_base = false; + /* The should_create flag is true, the declaration can be recorded. */ get_type_field (expr, base, indirect, type, field, - realpart, imagpart, address, true, true); + realpart, imagpart, address, escape_from_base, true, true); } void @@ -2132,36 +2918,79 @@ ipa_struct_reorg::find_vars (gimple *stmt) tree lhs = gimple_assign_lhs (stmt); tree rhs = gimple_assign_rhs1 (stmt); find_var (gimple_assign_lhs (stmt), stmt); + /* _2 = MEM[(struct arc_t * *)_1]; + records the right value _1 declaration. */ find_var (gimple_assign_rhs1 (stmt), stmt); - if (TREE_CODE (lhs) == SSA_NAME + + /* Add a safe func mechanism. */ + bool l_find = true; + bool r_find = true; + if (current_mode == STRUCT_REORDER_FIELDS) + { + l_find = !(current_function->is_safe_func + && TREE_CODE (lhs) == SSA_NAME + && is_from_void_ptr_parm (lhs)); + r_find = !(current_function->is_safe_func + && TREE_CODE (rhs) == SSA_NAME + && is_from_void_ptr_parm (rhs)); + } + + if ((TREE_CODE (lhs) == SSA_NAME) && VOID_POINTER_P (TREE_TYPE (lhs)) - && handled_type (TREE_TYPE (rhs))) + && handled_type (TREE_TYPE (rhs)) && l_find) { srtype *t = find_type (inner_type (TREE_TYPE (rhs))); srdecl *d = find_decl (lhs); if (!d && t) { - current_function->record_decl (t, lhs, -1); + current_function->record_decl (t, lhs, -1, + isptrptr (TREE_TYPE (rhs)) ? TREE_TYPE (rhs) : NULL); tree var = SSA_NAME_VAR (lhs); if (var && VOID_POINTER_P (TREE_TYPE (var))) - current_function->record_decl (t, var, -1); + current_function->record_decl (t, var, -1, + isptrptr (TREE_TYPE (rhs)) ? TREE_TYPE (rhs) : NULL); } } + /* find void ssa_name such as: + void * _1; struct arc * _2; + _2 = _1 + _3; _1 = calloc (100, 40). */ if (TREE_CODE (rhs) == SSA_NAME && VOID_POINTER_P (TREE_TYPE (rhs)) - && handled_type (TREE_TYPE (lhs))) + && handled_type (TREE_TYPE (lhs)) && r_find) { srtype *t = find_type (inner_type (TREE_TYPE (lhs))); srdecl *d = find_decl (rhs); if (!d && t) { - current_function->record_decl (t, rhs, -1); + current_function->record_decl (t, rhs, -1, + isptrptr (TREE_TYPE (lhs)) ? TREE_TYPE (lhs) : NULL); tree var = SSA_NAME_VAR (rhs); if (var && VOID_POINTER_P (TREE_TYPE (var))) - current_function->record_decl (t, var, -1); + current_function->record_decl (t, var, -1, + isptrptr (TREE_TYPE (lhs)) ? TREE_TYPE (lhs) : NULL); } } } + else if ((current_mode == STRUCT_REORDER_FIELDS) + && (gimple_assign_rhs_code (stmt) == LE_EXPR + || gimple_assign_rhs_code (stmt) == LT_EXPR + || gimple_assign_rhs_code (stmt) == GE_EXPR + || gimple_assign_rhs_code (stmt) == GT_EXPR)) + { + find_var (gimple_assign_lhs (stmt), stmt); + find_var (gimple_assign_rhs1 (stmt), stmt); + find_var (gimple_assign_rhs2 (stmt), stmt); + } + /* find void ssa_name from stmt such as: _2 = _1 - old_arcs_1. */ + else if ((current_mode == STRUCT_REORDER_FIELDS) + && gimple_assign_rhs_code (stmt) == POINTER_DIFF_EXPR + && types_compatible_p ( + TYPE_MAIN_VARIANT (TREE_TYPE (gimple_assign_rhs1 (stmt))), + TYPE_MAIN_VARIANT (TREE_TYPE (gimple_assign_rhs2 (stmt))))) + { + find_var (gimple_assign_rhs1 (stmt), stmt); + find_var (gimple_assign_rhs2 (stmt), stmt); + } else { /* Because we won't handle these stmts in rewrite phase, @@ -2232,27 +3061,134 @@ ipa_struct_reorg::find_vars (gimple *stmt) } } -/* Maybe record access of statement for further analaysis. */ +/* Maybe record access of statement for further analaysis. */ + +void +ipa_struct_reorg::maybe_record_stmt (cgraph_node *node, gimple *stmt) +{ + switch (gimple_code (stmt)) + { + case GIMPLE_ASSIGN: + maybe_record_assign (node, as_a (stmt)); + break; + case GIMPLE_CALL: + maybe_record_call (node, as_a (stmt)); + break; + case GIMPLE_DEBUG: + break; + case GIMPLE_GOTO: + case GIMPLE_SWITCH: + break; + default: + break; + } +} + +/* Calculate the multiplier. */ + +static bool +calculate_mult_num (tree arg, tree *num, tree struct_size) +{ + gcc_assert (TREE_CODE (arg) == INTEGER_CST); + bool sign = false; + HOST_WIDE_INT size = TREE_INT_CST_LOW (arg); + if (size < 0) + { + size = -size; + sign = true; + } + tree arg2 = build_int_cst (TREE_TYPE (arg), size); + if (integer_zerop (size_binop (FLOOR_MOD_EXPR, arg2, struct_size))) + { + tree number = size_binop (FLOOR_DIV_EXPR, arg2, struct_size); + if (sign) + number = build_int_cst (TREE_TYPE (number), -tree_to_shwi (number)); + *num = number; + return true; + } + return false; +} + +/* Trace and calculate the multiplier of PLUS_EXPR. */ + +static bool +trace_calculate_plus (gimple *size_def_stmt, tree *num, tree struct_size) +{ + gcc_assert (gimple_assign_rhs_code (size_def_stmt) == PLUS_EXPR); + + tree num1 = NULL_TREE; + tree num2 = NULL_TREE; + tree arg0 = gimple_assign_rhs1 (size_def_stmt); + tree arg1 = gimple_assign_rhs2 (size_def_stmt); + if (!is_result_of_mult (arg0, &num1, struct_size) || num1 == NULL_TREE) + return false; + if (!is_result_of_mult (arg1, &num2, struct_size) || num2 == NULL_TREE) + return false; + *num = size_binop (PLUS_EXPR, num1, num2); + return true; +} + +/* Trace and calculate the multiplier of MULT_EXPR. */ + +static bool +trace_calculate_mult (gimple *size_def_stmt, tree *num, tree struct_size) +{ + gcc_assert (gimple_assign_rhs_code (size_def_stmt) == MULT_EXPR); + + tree arg0 = gimple_assign_rhs1 (size_def_stmt); + tree arg1 = gimple_assign_rhs2 (size_def_stmt); + tree num1 = NULL_TREE; + + if (is_result_of_mult (arg0, &num1, struct_size) && num1 != NULL_TREE) + { + *num = size_binop (MULT_EXPR, arg1, num1); + return true; + } + if (is_result_of_mult (arg1, &num1, struct_size) && num1 != NULL_TREE) + { + *num = size_binop (MULT_EXPR, arg0, num1); + return true; + } + *num = NULL_TREE; + return false; +} + +/* Trace and calculate the multiplier of NEGATE_EXPR. */ + +static bool +trace_calculate_negate (gimple *size_def_stmt, tree *num, tree struct_size) +{ + gcc_assert (gimple_assign_rhs_code (size_def_stmt) == NEGATE_EXPR); + + /* support NEGATE_EXPR trace: _3 = -_2; _2 = _1 * 72. */ + tree num1 = NULL_TREE; + tree arg0 = gimple_assign_rhs1 (size_def_stmt); + if (!is_result_of_mult (arg0, &num1, struct_size) || num1 == NULL_TREE) + return false; + tree num0 = build_int_cst (TREE_TYPE (num1), -1); + *num = size_binop (MULT_EXPR, num0, num1); + return true; +} + +/* Trace and calculate the multiplier of POINTER_DIFF_EXPR. */ -void -ipa_struct_reorg::maybe_record_stmt (cgraph_node *node, gimple *stmt) +static bool +trace_calculate_diff (gimple *size_def_stmt, tree *num) { - switch (gimple_code (stmt)) + gcc_assert (gimple_assign_rhs_code (size_def_stmt) == NOP_EXPR); + + /* support POINTER_DIFF_EXPR trace: + _3 = (long unsigned int) _2; _2 = _1 - old_arcs_1. */ + tree arg = gimple_assign_rhs1 (size_def_stmt); + size_def_stmt = SSA_NAME_DEF_STMT (arg); + if (size_def_stmt && is_gimple_assign (size_def_stmt) + && gimple_assign_rhs_code (size_def_stmt) == POINTER_DIFF_EXPR) { - case GIMPLE_ASSIGN: - maybe_record_assign (node, as_a (stmt)); - break; - case GIMPLE_CALL: - maybe_record_call (node, as_a (stmt)); - break; - case GIMPLE_DEBUG: - break; - case GIMPLE_GOTO: - case GIMPLE_SWITCH: - break; - default: - break; + *num = NULL_TREE; + return true; } + *num = NULL_TREE; + return false; } /* This function checks whether ARG is a result of multiplication @@ -2269,26 +3205,8 @@ is_result_of_mult (tree arg, tree *num, tree struct_size) /* If we have a integer, just check if it is a multiply of STRUCT_SIZE. */ if (TREE_CODE (arg) == INTEGER_CST) - { - bool sign = false; - HOST_WIDE_INT size = TREE_INT_CST_LOW (arg); - if (size < 0) - { - size = -size; - sign = true; - } - tree arg2 = build_int_cst (TREE_TYPE (arg), size); - if (integer_zerop (size_binop (FLOOR_MOD_EXPR, arg2, struct_size))) - { - tree number = size_binop (FLOOR_DIV_EXPR, arg2, struct_size); - if (sign) - number = build_int_cst (TREE_TYPE (number), - -tree_to_shwi (number)); - *num = number; - return true; - } - return false; - } + return calculate_mult_num (arg, num, struct_size); + gimple *size_def_stmt = SSA_NAME_DEF_STMT (arg); /* If the allocation statement was of the form @@ -2304,43 +3222,20 @@ is_result_of_mult (tree arg, tree *num, tree struct_size) return false; // FIXME: this should handle SHIFT also. - if (gimple_assign_rhs_code (size_def_stmt) == PLUS_EXPR) - { - tree num1, num2; - tree arg0 = gimple_assign_rhs1 (size_def_stmt); - tree arg1 = gimple_assign_rhs2 (size_def_stmt); - if (!is_result_of_mult (arg0, &num1, struct_size)) - return false; - if (!is_result_of_mult (arg1, &num2, struct_size)) - return false; - *num = size_binop (PLUS_EXPR, num1, num2); - return true; - } - else if (gimple_assign_rhs_code (size_def_stmt) == MULT_EXPR) - { - tree arg0 = gimple_assign_rhs1 (size_def_stmt); - tree arg1 = gimple_assign_rhs2 (size_def_stmt); - tree num1; - - if (is_result_of_mult (arg0, &num1, struct_size)) - { - *num = size_binop (MULT_EXPR, arg1, num1); - return true; - } - if (is_result_of_mult (arg1, &num1, struct_size)) - { - *num = size_binop (MULT_EXPR, arg0, num1); - return true; - } - - *num = NULL_TREE; - return false; - } - else if (gimple_assign_rhs_code (size_def_stmt) == SSA_NAME) + tree_code rhs_code = gimple_assign_rhs_code (size_def_stmt); + if (rhs_code == PLUS_EXPR) + return trace_calculate_plus (size_def_stmt, num, struct_size); + else if (rhs_code == MULT_EXPR) + return trace_calculate_mult (size_def_stmt, num, struct_size); + else if (rhs_code == SSA_NAME) { arg = gimple_assign_rhs1 (size_def_stmt); size_def_stmt = SSA_NAME_DEF_STMT (arg); } + else if (rhs_code == NEGATE_EXPR && current_mode == STRUCT_REORDER_FIELDS) + return trace_calculate_negate (size_def_stmt, num, struct_size); + else if (rhs_code == NOP_EXPR && current_mode == STRUCT_REORDER_FIELDS) + return trace_calculate_diff (size_def_stmt, num); else { *num = NULL_TREE; @@ -2357,18 +3252,22 @@ is_result_of_mult (tree arg, tree *num, tree struct_size) bool ipa_struct_reorg::handled_allocation_stmt (gimple *stmt) { - if (current_mode == COMPLETE_STRUCT_RELAYOUT + if ((current_mode == STRUCT_REORDER_FIELDS) + && (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_MALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_CALLOC))) + return true; + if ((current_mode == COMPLETE_STRUCT_RELAYOUT) && gimple_call_builtin_p (stmt, BUILT_IN_CALLOC)) return true; - - if (current_mode != COMPLETE_STRUCT_RELAYOUT) - if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC) - || gimple_call_builtin_p (stmt, BUILT_IN_MALLOC) - || gimple_call_builtin_p (stmt, BUILT_IN_CALLOC) - || gimple_call_builtin_p (stmt, BUILT_IN_ALIGNED_ALLOC) - || gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA) - || gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN)) - return true; + if ((current_mode == NORMAL) + && (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_MALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_CALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_ALIGNED_ALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA) + || gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN))) + return true; return false; } @@ -2376,7 +3275,7 @@ ipa_struct_reorg::handled_allocation_stmt (gimple *stmt) elements in the array allocated. */ tree -ipa_struct_reorg::allocate_size (srtype *type, gimple *stmt) +ipa_struct_reorg::allocate_size (srtype *type, srdecl *decl, gimple *stmt) { if (!stmt || gimple_code (stmt) != GIMPLE_CALL @@ -2396,6 +3295,10 @@ ipa_struct_reorg::allocate_size (srtype *type, gimple *stmt) tree struct_size = TYPE_SIZE_UNIT (type->type); + /* Specify the correct size to relax multi-layer pointer. */ + if (TREE_CODE (decl->decl) == SSA_NAME && isptrptr (decl->orig_type)) + struct_size = TYPE_SIZE_UNIT (decl->orig_type); + tree size = gimple_call_arg (stmt, 0); if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC) @@ -2409,8 +3312,10 @@ ipa_struct_reorg::allocate_size (srtype *type, gimple *stmt) the size of structure. */ if (operand_equal_p (arg1, struct_size, 0)) return size; - /* ??? Check that first argument is a constant equal to - the size of structure. */ + /* ??? Check that first argument is a constant + equal to the size of structure. */ + /* If the allocated number is equal to the value of struct_size, + the value of arg1 is changed to the allocated number. */ if (operand_equal_p (size, struct_size, 0)) return arg1; if (dump_file && (dump_flags & TDF_DETAILS)) @@ -2453,10 +3358,16 @@ ipa_struct_reorg::maybe_mark_or_record_other_side (tree side, tree other, if (!d) { + /* MEM[(struct arc *)_1].head = _2; _2 = calloc (100, 104). */ if (VOID_POINTER_P (TREE_TYPE (side)) && TREE_CODE (side) == SSA_NAME) - current_function->record_decl (type, side, -1); + { + /* The type is other, the declaration is side. */ + current_function->record_decl (type, side, -1, + isptrptr (TREE_TYPE (other)) ? TREE_TYPE (other) : NULL); + } else + /* *_1 = &MEM[(void *)&x + 8B]. */ type->mark_escape (escape_cast_another_ptr, stmt); } else if (type != d->type) @@ -2464,6 +3375,17 @@ ipa_struct_reorg::maybe_mark_or_record_other_side (tree side, tree other, type->mark_escape (escape_cast_another_ptr, stmt); d->type->mark_escape (escape_cast_another_ptr, stmt); } + /* x_1 = y.x_nodes; void *x; + Directly mark the structure pointer type assigned + to the void* variable as escape. */ + else if (current_mode == STRUCT_REORDER_FIELDS + && TREE_CODE (side) == SSA_NAME + && VOID_POINTER_P (TREE_TYPE (side)) + && SSA_NAME_VAR (side) + && VOID_POINTER_P (TREE_TYPE (SSA_NAME_VAR (side)))) + mark_type_as_escape (TREE_TYPE (other), escape_cast_void, stmt); + + check_ptr_layers (side, other, stmt); } /* Record accesses in an assignment statement STMT. */ @@ -2486,8 +3408,11 @@ ipa_struct_reorg::maybe_record_assign (cgraph_node *node, gassign *stmt) if (!handled_type (TREE_TYPE (lhs))) return; /* Check if rhs2 is a multiplication of the size of the type. */ - if (is_result_of_mult (rhs2, &num, - TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (lhs))))) + /* The size adjustment and judgment of multi-layer pointers + are added. */ + if (is_result_of_mult (rhs2, &num, isptrptr (TREE_TYPE (lhs)) + ? TYPE_SIZE_UNIT (TREE_TYPE (lhs)) + : TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (lhs))))) { record_stmt_expr (lhs, node, stmt); record_stmt_expr (rhs1, node, stmt); @@ -2525,9 +3450,8 @@ ipa_struct_reorg::maybe_record_assign (cgraph_node *node, gassign *stmt) } static bool -check_mem_ref_offset (tree expr) +check_mem_ref_offset (tree expr, tree *num) { - tree num = NULL; bool ret = false; if (TREE_CODE (expr) != MEM_REF) @@ -2538,15 +3462,18 @@ check_mem_ref_offset (tree expr) tree tmp = TREE_OPERAND (expr, 0); if (TREE_CODE (tmp) == ADDR_EXPR) tmp = TREE_OPERAND (tmp, 0); - tree size = TYPE_SIZE_UNIT (inner_type (TREE_TYPE (tmp))); - ret = is_result_of_mult (field_off, &num, size); + /* Specify the correct size for the multi-layer pointer. */ + tree size = isptrptr (TREE_TYPE (tmp)) + ? TYPE_SIZE_UNIT (TREE_TYPE (tmp)) + : TYPE_SIZE_UNIT (inner_type (TREE_TYPE (tmp))); + ret = is_result_of_mult (field_off, num, size); return ret; } tree get_ref_base_and_offset (tree &e, HOST_WIDE_INT &offset, bool &realpart, bool &imagpart, - tree &accesstype) + tree &accesstype, tree *num) { offset = 0; realpart = false; @@ -2569,22 +3496,29 @@ get_ref_base_and_offset (tree &e, HOST_WIDE_INT &offset, { case COMPONENT_REF: { + /* x.a = _1; If expr is the lvalue of stmt, + then field type is FIELD_DECL - POINTER_TYPE - RECORD_TYPE. */ tree field = TREE_OPERAND (expr, 1); tree field_off = byte_position (field); if (TREE_CODE (field_off) != INTEGER_CST) return NULL; offset += tree_to_shwi (field_off); + /* x.a = _1; If expr is the lvalue of stmt, + then expr type is VAR_DECL - RECORD_TYPE (fetch x) */ expr = TREE_OPERAND (expr, 0); accesstype = NULL; break; } case MEM_REF: { + /* _2 = MEM[(struct s * *)_1]; + If expr is the right value of stmt,then field_off type is + INTEGER_CST - POINTER_TYPE - POINTER_TYPE - RECORD_TYPE. */ tree field_off = TREE_OPERAND (expr, 1); gcc_assert (TREE_CODE (field_off) == INTEGER_CST); /* So we can mark the types as escaping if different. */ accesstype = TREE_TYPE (field_off); - if (!check_mem_ref_offset (expr)) + if (!check_mem_ref_offset (expr, num)) offset += tree_to_uhwi (field_off); return TREE_OPERAND (expr, 0); } @@ -2626,10 +3560,11 @@ ipa_struct_reorg::wholeaccess (tree expr, tree base, bool ipa_struct_reorg::get_type_field (tree expr, tree &base, bool &indirect, srtype *&type, srfield *&field, - bool &realpart, bool &imagpart, - bool &address, bool should_create, + bool &realpart, bool &imagpart, bool &address, + bool& escape_from_base, bool should_create, bool can_escape) { + tree num = NULL_TREE; HOST_WIDE_INT offset; tree accesstype; address = false; @@ -2641,8 +3576,9 @@ ipa_struct_reorg::get_type_field (tree expr, tree &base, bool &indirect, mark_as_bit_field = true; } + /* ref is classified into two types: COMPONENT_REF or MER_REF. */ base = get_ref_base_and_offset (expr, offset, realpart, imagpart, - accesstype); + accesstype, &num); /* Variable access, unkown type. */ if (base == NULL) @@ -2680,6 +3616,8 @@ ipa_struct_reorg::get_type_field (tree expr, tree &base, bool &indirect, if (!t) return false; } + /* If no such decl is finded + or orig_type is not added to this decl, then add it. */ else if (!d && accesstype) { if (!should_create) @@ -2691,15 +3629,52 @@ ipa_struct_reorg::get_type_field (tree expr, tree &base, bool &indirect, t = record_type (inner_type (accesstype)); if (!t || t->has_escaped ()) return false; - /* If base is not void* mark the type as escaping. */ - if (!VOID_POINTER_P (TREE_TYPE (base))) + /* If base is not void* mark the type as escaping. + release INTEGER_TYPE cast to struct pointer. + (If t has escpaed above, then directly returns + and doesn't mark escape follow.). */ + /* _1 = MEM[(struct arc_t * *)a_1]. + then base a_1: ssa_name - pointer_type - integer_type. */ + if (current_mode == STRUCT_REORDER_FIELDS) { - gcc_assert (can_escape); - t->mark_escape (escape_cast_another_ptr, NULL); - return false; + bool is_int_ptr = POINTER_TYPE_P (TREE_TYPE (base)) + && (TREE_CODE (inner_type (TREE_TYPE (base))) + == INTEGER_TYPE); + if (!(VOID_POINTER_P (TREE_TYPE (base)) + || (current_function->is_safe_func && is_int_ptr))) + { + gcc_assert (can_escape); + t->mark_escape (escape_cast_another_ptr, NULL); + return false; + } + if (TREE_CODE (base) == SSA_NAME + && !(current_function->is_safe_func && is_int_ptr)) + { + /* Add a safe func mechanism. */ + if (!(current_function->is_safe_func + && is_from_void_ptr_parm (base))) + /* Add auxiliary information of the multi-layer pointer + type. */ + current_function->record_decl (t, base, -1, + isptrptr (accesstype) ? accesstype : NULL); + } + } + else + { + if (!(VOID_POINTER_P (TREE_TYPE (base)))) + { + gcc_assert (can_escape); + t->mark_escape (escape_cast_another_ptr, NULL); + return false; + } + if (TREE_CODE (base) == SSA_NAME) + { + /* Add auxiliary information of the multi-layer pointer + type. */ + current_function->record_decl (t, base, -1, + isptrptr (accesstype) ? accesstype : NULL); + } } - if (TREE_CODE (base) == SSA_NAME) - current_function->record_decl (t, base, -1); } else if (!d) return false; @@ -2707,7 +3682,10 @@ ipa_struct_reorg::get_type_field (tree expr, tree &base, bool &indirect, t = d->type; if (t->has_escaped ()) + { + escape_from_base = true; return false; + } if (mark_as_bit_field) { @@ -2716,6 +3694,17 @@ ipa_struct_reorg::get_type_field (tree expr, tree &base, bool &indirect, return false; } + /* Escape the operation of fetching field with pointer offset such as: + *(&(t->right)) = malloc (0); -> MEM[(struct node * *)_1 + 8B] = malloc (0); + */ + if (current_mode != NORMAL + && (TREE_CODE (expr) == MEM_REF) && (offset != 0)) + { + gcc_assert (can_escape); + t->mark_escape (escape_non_multiply_size, NULL); + return false; + } + if (wholeaccess (expr, base, accesstype, t)) { field = NULL; @@ -2733,7 +3722,6 @@ ipa_struct_reorg::get_type_field (tree expr, tree &base, bool &indirect, print_generic_expr (dump_file, expr); fprintf (dump_file, "\n"); print_generic_expr (dump_file, base); - fprintf (dump_file, "\n"); } gcc_assert (can_escape); t->mark_escape (escape_unkown_field, NULL); @@ -2747,9 +3735,8 @@ ipa_struct_reorg::get_type_field (tree expr, tree &base, bool &indirect, print_generic_expr (dump_file, f->fieldtype); fprintf (dump_file, "\naccess type = "); print_generic_expr (dump_file, TREE_TYPE (expr)); - fprintf (dump_file, "original expr = "); + fprintf (dump_file, "\noriginal expr = "); print_generic_expr (dump_file, expr); - fprintf (dump_file, "\n"); } gcc_assert (can_escape); t->mark_escape (escape_unkown_field, NULL); @@ -2772,8 +3759,9 @@ ipa_struct_reorg::mark_expr_escape (tree expr, escape_type escapes, srtype *type; srfield *field; bool realpart, imagpart, address; + bool escape_from_base = false; if (!get_type_field (expr, base, indirect, type, field, - realpart, imagpart, address)) + realpart, imagpart, address, escape_from_base)) return; type->mark_escape (escapes, stmt); @@ -2846,10 +3834,23 @@ ipa_struct_reorg::maybe_record_call (cgraph_node *node, gcall *stmt) gimple_call_arg (stmt, i)); if (d) d->type->mark_escape (escapes, stmt); + + if (escapes == escape_external_function + && !gimple_call_builtin_p (stmt, BUILT_IN_MEMSET)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "escape_external_function: "); + print_gimple_stmt (dump_file, stmt, 0); + } + if (d) + ext_func_types.safe_push (d->type); + } } return; } + /* get func param it's tree_list. */ argtype = TYPE_ARG_TYPES (gimple_call_fntype (stmt)); for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) { @@ -2857,9 +3858,14 @@ ipa_struct_reorg::maybe_record_call (cgraph_node *node, gcall *stmt) if (argtype) { tree argtypet = TREE_VALUE (argtype); - if (!free_or_realloc + /* callee_func (_1, _2); + Check the callee func, instead of current func. */ + if (!(free_or_realloc + || (current_mode == STRUCT_REORDER_FIELDS + && safe_functions.contains ( + node->get_edge (stmt)->callee))) && VOID_POINTER_P (argtypet)) - mark_type_as_escape (TREE_TYPE (arg), escape_cast_void); + mark_type_as_escape (TREE_TYPE (arg), escape_cast_void, stmt); else record_stmt_expr (arg, node, stmt); } @@ -2878,12 +3884,22 @@ ipa_struct_reorg::record_stmt_expr (tree expr, cgraph_node *node, gimple *stmt) srtype *type; srfield *field; bool realpart, imagpart, address; + bool escape_from_base = false; if (!get_type_field (expr, base, indirect, type, field, - realpart, imagpart, address)) + realpart, imagpart, address, escape_from_base)) return; - if (!opt_for_fn (current_function_decl, flag_ipa_struct_reorg)) - type->mark_escape (escape_non_optimize, stmt); + if (current_mode == STRUCT_REORDER_FIELDS) + { + if (!opt_for_fn (current_function_decl, flag_ipa_reorder_fields)) + type->mark_escape (escape_non_optimize, stmt); + } + else + { + if (!opt_for_fn (current_function_decl, flag_ipa_struct_reorg)) + type->mark_escape (escape_non_optimize, stmt); + } + /* Record it. */ type->add_access (new sraccess (stmt, node, type, field)); @@ -2901,10 +3917,10 @@ ipa_struct_reorg::find_function (cgraph_node *node) } void -ipa_struct_reorg::check_type_and_push (tree newdecl, srtype *type, - vec &worklist, - gimple *stmt) +ipa_struct_reorg::check_type_and_push (tree newdecl, srdecl *decl, + vec &worklist, gimple *stmt) { + srtype *type = decl->type; if (integer_zerop (newdecl)) return; @@ -2916,7 +3932,8 @@ ipa_struct_reorg::check_type_and_push (tree newdecl, srtype *type, type->mark_escape (escape_cast_another_ptr, stmt); return; } - if (d->type == type) + if (d->type == type + && cmp_ptr_layers (TREE_TYPE (newdecl), TREE_TYPE (decl->decl))) return; srtype *type1 = d->type; @@ -2967,7 +3984,9 @@ ipa_struct_reorg::check_type_and_push (tree newdecl, srtype *type, /* Only add to the worklist if the decl is a SSA_NAME. */ if (TREE_CODE (newdecl) == SSA_NAME) worklist.safe_push (d); - if (d->type == type) + tree a_decl = d->orig_type ? d->orig_type : TREE_TYPE (newdecl); + tree b_decl = decl->orig_type ? decl->orig_type : TREE_TYPE (decl->decl); + if (d->type == type && cmp_ptr_layers (a_decl, b_decl)) return; srtype *type1 = d->type; @@ -3000,6 +4019,95 @@ ipa_struct_reorg::check_alloc_num (gimple *stmt, srtype *type) } } +/* Check the definition of gimple assign. */ + +void +ipa_struct_reorg::check_definition_assign (srdecl *decl, vec &worklist) +{ + tree ssa_name = decl->decl; + srtype *type = decl->type; + gimple *stmt = SSA_NAME_DEF_STMT (ssa_name); + gcc_assert (gimple_code (stmt) == GIMPLE_ASSIGN); + /* a) if the SSA_NAME is sourced from a pointer plus, record the pointer and + check to make sure the addition was a multiple of the size. + check the pointer type too. */ + tree rhs = gimple_assign_rhs1 (stmt); + if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) + { + tree rhs2 = gimple_assign_rhs2 (stmt); + tree num = NULL_TREE; + /* Specify the correct size for the multi-layer pointer. */ + if (!is_result_of_mult (rhs2, &num, isptrptr (decl->orig_type) + ? TYPE_SIZE_UNIT (decl->orig_type) + : TYPE_SIZE_UNIT (type->type))) + type->mark_escape (escape_non_multiply_size, stmt); + + if (TREE_CODE (rhs) == SSA_NAME) + check_type_and_push (rhs, decl, worklist, stmt); + return; + } + + if (gimple_assign_rhs_code (stmt) == MAX_EXPR + || gimple_assign_rhs_code (stmt) == MIN_EXPR + || gimple_assign_rhs_code (stmt) == BIT_IOR_EXPR + || gimple_assign_rhs_code (stmt) == BIT_XOR_EXPR + || gimple_assign_rhs_code (stmt) == BIT_AND_EXPR) + { + tree rhs2 = gimple_assign_rhs2 (stmt); + if (TREE_CODE (rhs) == SSA_NAME) + check_type_and_push (rhs, decl, worklist, stmt); + if (TREE_CODE (rhs2) == SSA_NAME) + check_type_and_push (rhs2, decl, worklist, stmt); + return; + } + + /* Casts between pointers and integer are escaping. */ + if (gimple_assign_cast_p (stmt)) + { + type->mark_escape (escape_cast_int, stmt); + return; + } + + /* d) if the name is from a cast/assignment, make sure it is used as + that type or void* + i) If void* then push the ssa_name into worklist. */ + gcc_assert (gimple_assign_single_p (stmt)); + check_other_side (decl, rhs, stmt, worklist); + check_ptr_layers (decl->decl, rhs, stmt); +} + +/* Check the definition of gimple call. */ + +void +ipa_struct_reorg::check_definition_call (srdecl *decl, vec &worklist) +{ + tree ssa_name = decl->decl; + srtype *type = decl->type; + gimple *stmt = SSA_NAME_DEF_STMT (ssa_name); + gcc_assert (gimple_code (stmt) == GIMPLE_CALL); + + /* For realloc, check the type of the argument. */ + if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC)) + check_type_and_push (gimple_call_arg (stmt, 0), decl, worklist, stmt); + + if (current_mode == STRUCT_REORDER_FIELDS) + { + if (!handled_allocation_stmt (stmt)) + type->mark_escape (escape_return, stmt); + if (!allocate_size (type, decl, stmt)) + type->mark_escape (escape_non_multiply_size, stmt); + } + else + { + if (!handled_allocation_stmt (stmt) + || !allocate_size (type, decl, stmt)) + type->mark_escape (escape_return, stmt); + } + + check_alloc_num (stmt, type); + return; +} + /* 2) Check SSA_NAMEs for non type usages (source or use) (worlist of srdecl) a) if the SSA_NAME is sourced from a pointer plus, record the pointer and @@ -3029,9 +4137,12 @@ ipa_struct_reorg::check_definition (srdecl *decl, vec &worklist) if (var && TREE_CODE (var) == PARM_DECL && VOID_POINTER_P (TREE_TYPE (ssa_name))) - type->mark_escape (escape_cast_void, NULL); + type->mark_escape (escape_cast_void, SSA_NAME_DEF_STMT (ssa_name)); return; } + if (current_mode == STRUCT_REORDER_FIELDS && SSA_NAME_VAR (ssa_name) + && VOID_POINTER_P (TREE_TYPE (SSA_NAME_VAR (ssa_name)))) + type->mark_escape (escape_cast_void, SSA_NAME_DEF_STMT (ssa_name)); gimple *stmt = SSA_NAME_DEF_STMT (ssa_name); /* @@ -3039,17 +4150,7 @@ ipa_struct_reorg::check_definition (srdecl *decl, vec &worklist) i) Add SSA_NAME (void*) to the worklist if allocated from realloc */ if (gimple_code (stmt) == GIMPLE_CALL) - { - /* For realloc, check the type of the argument. */ - if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC)) - check_type_and_push (gimple_call_arg (stmt, 0), type, worklist, stmt); - - if (!handled_allocation_stmt (stmt) - || !allocate_size (type, stmt)) - type->mark_escape (escape_return, stmt); - check_alloc_num (stmt, type); - return; - } + check_definition_call (decl, worklist); /* If the SSA_NAME is sourced from an inline-asm, just mark the type as escaping. */ if (gimple_code (stmt) == GIMPLE_ASM) @@ -3065,58 +4166,11 @@ ipa_struct_reorg::check_definition (srdecl *decl, vec &worklist) { for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++) check_type_and_push (gimple_phi_arg_def (stmt, i), - type, worklist, stmt); - return; - } - - gcc_assert (gimple_code (stmt) == GIMPLE_ASSIGN); - /* - a) if the SSA_NAME is sourced from a pointer plus, record the pointer and - check to make sure the addition was a multiple of the size. - check the pointer type too. - */ - - tree rhs = gimple_assign_rhs1 (stmt); - if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) - { - tree rhs2 = gimple_assign_rhs2 (stmt); - tree num; - if (!is_result_of_mult (rhs2, &num, TYPE_SIZE_UNIT (type->type))) - type->mark_escape (escape_non_multiply_size, stmt); - - if (TREE_CODE (rhs) == SSA_NAME) - check_type_and_push (rhs, type, worklist, stmt); - return; - } - - if (gimple_assign_rhs_code (stmt) == MAX_EXPR - || gimple_assign_rhs_code (stmt) == MIN_EXPR - || gimple_assign_rhs_code (stmt) == BIT_IOR_EXPR - || gimple_assign_rhs_code (stmt) == BIT_XOR_EXPR - || gimple_assign_rhs_code (stmt) == BIT_AND_EXPR) - { - tree rhs2 = gimple_assign_rhs2 (stmt); - if (TREE_CODE (rhs) == SSA_NAME) - check_type_and_push (rhs, type, worklist, stmt); - if (TREE_CODE (rhs2) == SSA_NAME) - check_type_and_push (rhs2, type, worklist, stmt); - return; - } - - /* Casts between pointers and integer are escaping. */ - if (gimple_assign_cast_p (stmt)) - { - type->mark_escape (escape_cast_int, stmt); + decl, worklist, stmt); return; } - - /* - d) if the name is from a cast/assignment, make sure it is used as that - type or void* - i) If void* then push the ssa_name into worklist - */ - gcc_assert (gimple_assign_single_p (stmt)); - check_other_side (decl, rhs, stmt, worklist); + if (gimple_code (stmt) == GIMPLE_ASSIGN) + check_definition_assign (decl, worklist); } /* Mark the types used by the inline-asm as escaping. @@ -3149,45 +4203,121 @@ ipa_struct_reorg::check_other_side (srdecl *decl, tree other, gimple *stmt, { srtype *type = decl->type; - if (TREE_CODE (other) == SSA_NAME - || DECL_P (other) + if (TREE_CODE (other) == SSA_NAME || DECL_P (other) || TREE_CODE (other) == INTEGER_CST) { - check_type_and_push (other, type, worklist, stmt); + check_type_and_push (other, decl, worklist, stmt); return; } tree t = TREE_TYPE (other); if (!handled_type (t)) { - type->mark_escape (escape_cast_another_ptr, stmt); + type->mark_escape (escape_cast_another_ptr, stmt); + return; + } + + srtype *t1 = find_type (inner_type (t)); + if (t1 == type) + { + /* In Complete Struct Relayout, if lhs type is the same + as rhs type, we could return without any harm. */ + if (current_mode == COMPLETE_STRUCT_RELAYOUT) + return; + + tree base; + bool indirect; + srtype *type1; + srfield *field; + bool realpart, imagpart, address; + bool escape_from_base = false; + if (!get_type_field (other, base, indirect, type1, field, + realpart, imagpart, address, escape_from_base)) + { + if (current_mode == STRUCT_REORDER_FIELDS) + { + /* release INTEGER_TYPE cast to struct pointer. */ + bool cast_from_int_ptr = current_function->is_safe_func && base + && find_decl (base) == NULL && POINTER_TYPE_P (TREE_TYPE (base)) + && (TREE_CODE (inner_type (TREE_TYPE (base))) == INTEGER_TYPE); + + /* Add a safe func mechanism. */ + bool from_void_ptr_parm = current_function->is_safe_func + && TREE_CODE (base) == SSA_NAME && is_from_void_ptr_parm (base); + + /* release type is used by a type which escapes. */ + if (escape_from_base || cast_from_int_ptr || from_void_ptr_parm) + return; + } + type->mark_escape (escape_cast_another_ptr, stmt); + } + + return; + } + + if (t1) + t1->mark_escape (escape_cast_another_ptr, stmt); + + type->mark_escape (escape_cast_another_ptr, stmt); +} + + +/* Get the expr base. */ + +void +get_base (tree &base, tree expr) +{ + if (TREE_CODE (expr) == MEM_REF) + base = TREE_OPERAND (expr, 0); + else if (TREE_CODE (expr) == COMPONENT_REF) + { + base = TREE_OPERAND (expr, 0); + base = (TREE_CODE (base) == MEM_REF) ? TREE_OPERAND (base, 0) : base; + } + else if (TREE_CODE (expr) == ADDR_EXPR) + base = TREE_OPERAND (expr, 0); +} + +/* Check whether the number of pointer layers of exprs is equal, + marking unequals as escape. */ + +void +ipa_struct_reorg::check_ptr_layers (tree a_expr, tree b_expr, gimple* stmt) +{ + if (current_mode != STRUCT_REORDER_FIELDS || current_function->is_safe_func + || !(POINTER_TYPE_P (TREE_TYPE (a_expr))) + || !(POINTER_TYPE_P (TREE_TYPE (b_expr))) + || !handled_type (TREE_TYPE (a_expr)) + || !handled_type (TREE_TYPE (b_expr))) + return; + + tree a_base = a_expr; + tree b_base = b_expr; + get_base (a_base, a_expr); + get_base (b_base, b_expr); + + srdecl *a = find_decl (a_base); + srdecl *b = find_decl (b_base); + if (a && b == NULL && TREE_CODE (b_expr) != INTEGER_CST) + { + a->type->mark_escape (escape_cast_another_ptr, stmt); return; } - - srtype *t1 = find_type (inner_type (t)); - if (t1 == type) + else if (b && a == NULL && TREE_CODE (a_expr) != INTEGER_CST) { - /* In Complete Struct Relayout, if lhs type is the same - as rhs type, we could return without any harm. */ - if (current_mode == COMPLETE_STRUCT_RELAYOUT) - return; - - tree base; - bool indirect; - srtype *type1; - srfield *field; - bool realpart, imagpart, address; - if (!get_type_field (other, base, indirect, type1, field, - realpart, imagpart, address)) - type->mark_escape (escape_cast_another_ptr, stmt); - + b->type->mark_escape (escape_cast_another_ptr, stmt); return; } + else if (a == NULL && b == NULL) + return; - if (t1) - t1->mark_escape (escape_cast_another_ptr, stmt); + if (cmp_ptr_layers (TREE_TYPE (a_expr), TREE_TYPE (b_expr))) + return; - type->mark_escape (escape_cast_another_ptr, stmt); + if (a) + a->type->mark_escape (escape_cast_another_ptr, stmt); + if (b) + b->type->mark_escape (escape_cast_another_ptr, stmt); } void @@ -3205,7 +4335,7 @@ ipa_struct_reorg::check_use (srdecl *decl, gimple *stmt, check to make sure they are used correctly. */ if (gimple_code (stmt) == GIMPLE_PHI) { - check_type_and_push (gimple_phi_result (stmt), type, worklist, stmt); + check_type_and_push (gimple_phi_result (stmt), decl, worklist, stmt); return; } @@ -3221,10 +4351,15 @@ ipa_struct_reorg::check_use (srdecl *decl, gimple *stmt, tree rhs2 = gimple_cond_rhs (stmt); tree orhs = rhs1; enum tree_code code = gimple_cond_code (stmt); - if (code != EQ_EXPR && code != NE_EXPR - && (current_mode != COMPLETE_STRUCT_RELAYOUT - || (code != LT_EXPR && code != LE_EXPR - && code != GT_EXPR && code != GE_EXPR))) + if ((current_mode == NORMAL && (code != EQ_EXPR && code != NE_EXPR)) + || (current_mode == COMPLETE_STRUCT_RELAYOUT + && (code != EQ_EXPR && code != NE_EXPR + && code != LT_EXPR && code != LE_EXPR + && code != GT_EXPR && code != GE_EXPR)) + || (current_mode == STRUCT_REORDER_FIELDS + && (code != EQ_EXPR && code != NE_EXPR + && code != LT_EXPR && code != LE_EXPR + && code != GT_EXPR && code != GE_EXPR))) { mark_expr_escape (rhs1, escape_non_eq, stmt); mark_expr_escape (rhs2, escape_non_eq, stmt); @@ -3235,7 +4370,7 @@ ipa_struct_reorg::check_use (srdecl *decl, gimple *stmt, return; if (TREE_CODE (orhs) != SSA_NAME) mark_expr_escape (rhs1, escape_non_eq, stmt); - check_type_and_push (orhs, type, worklist, stmt); + check_type_and_push (orhs, decl, worklist, stmt); return; } @@ -3254,9 +4389,14 @@ ipa_struct_reorg::check_use (srdecl *decl, gimple *stmt, tree rhs2 = gimple_assign_rhs2 (stmt); tree orhs = rhs1; enum tree_code code = gimple_assign_rhs_code (stmt); - if (code != EQ_EXPR && code != NE_EXPR - && (current_mode != COMPLETE_STRUCT_RELAYOUT - || (code != LT_EXPR && code != LE_EXPR + if ((current_mode == NORMAL && (code != EQ_EXPR && code != NE_EXPR)) + || (current_mode == COMPLETE_STRUCT_RELAYOUT + && (code != EQ_EXPR && code != NE_EXPR + && code != LT_EXPR && code != LE_EXPR + && code != GT_EXPR && code != GE_EXPR)) + || (current_mode == STRUCT_REORDER_FIELDS + && (code != EQ_EXPR && code != NE_EXPR + && code != LT_EXPR && code != LE_EXPR && code != GT_EXPR && code != GE_EXPR))) { mark_expr_escape (rhs1, escape_non_eq, stmt); @@ -3268,7 +4408,7 @@ ipa_struct_reorg::check_use (srdecl *decl, gimple *stmt, return; if (TREE_CODE (orhs) != SSA_NAME) mark_expr_escape (rhs1, escape_non_eq, stmt); - check_type_and_push (orhs, type, worklist, stmt); + check_type_and_push (orhs, decl, worklist, stmt); return; } @@ -3282,6 +4422,7 @@ ipa_struct_reorg::check_use (srdecl *decl, gimple *stmt, check_other_side (decl, lhs, stmt, worklist); return; } + check_ptr_layers (lhs, rhs, stmt); } if (is_gimple_assign (stmt) @@ -3291,9 +4432,26 @@ ipa_struct_reorg::check_use (srdecl *decl, gimple *stmt, tree lhs = gimple_assign_lhs (stmt); tree num; check_other_side (decl, lhs, stmt, worklist); - if (!is_result_of_mult (rhs2, &num, TYPE_SIZE_UNIT (type->type))) + check_ptr_layers (lhs, decl->decl, stmt); + /* Specify the correct size for the multi-layer pointer. */ + if (!is_result_of_mult (rhs2, &num, isptrptr (decl->orig_type) + ? TYPE_SIZE_UNIT (decl->orig_type) + : TYPE_SIZE_UNIT (type->type))) type->mark_escape (escape_non_multiply_size, stmt); } + + if (is_gimple_assign (stmt) + && gimple_assign_rhs_code (stmt) == POINTER_DIFF_EXPR) + { + tree rhs1 = gimple_assign_rhs1 (stmt); + tree rhs2 = gimple_assign_rhs2 (stmt); + tree other = rhs1 == decl->decl ? rhs2 : rhs1; + + check_other_side (decl, other, stmt, worklist); + check_ptr_layers (decl->decl, other, stmt); + return; + } + } /* @@ -3360,17 +4518,43 @@ ipa_struct_reorg::record_function (cgraph_node *node) if (DECL_PRESERVE_P (node->decl)) escapes = escape_marked_as_used; else if (!node->local) - escapes = escape_visible_function; + { + if (current_mode != STRUCT_REORDER_FIELDS) + escapes = escape_visible_function; + if (current_mode == STRUCT_REORDER_FIELDS && node->externally_visible) + escapes = escape_visible_function; + } else if (!node->can_change_signature) escapes = escape_cannot_change_signature; else if (!tree_versionable_function_p (node->decl)) escapes = escape_noclonable_function; - else if (!opt_for_fn (node->decl, flag_ipa_struct_reorg)) - escapes = escape_non_optimize; + + if (current_mode == STRUCT_REORDER_FIELDS) + { + if (!opt_for_fn (node->decl, flag_ipa_reorder_fields)) + escapes = escape_non_optimize; + } + else if (current_mode == NORMAL || current_mode == COMPLETE_STRUCT_RELAYOUT) + { + if (!opt_for_fn (node->decl, flag_ipa_struct_reorg)) + escapes = escape_non_optimize; + } basic_block bb; gimple_stmt_iterator si; + /* Add a safe func mechanism. */ + if (current_mode == STRUCT_REORDER_FIELDS) + { + current_function->is_safe_func = safe_functions.contains (node); + if (dump_file) + { + fprintf (dump_file, "\nfunction %s/%u: is_safe_func = %d\n", + node->name (), node->order, + current_function->is_safe_func); + } + } + /* Record the static chain decl. */ if (fn->static_chain_decl) { @@ -3503,6 +4687,42 @@ ipa_struct_reorg::record_function (cgraph_node *node) return sfn; } + +/* For a function that contains the void* parameter and passes the structure + pointer, check whether the function uses the input node safely. + For these functions, the void* parameter and related ssa_name are not + recorded in record_function (), and the input structure type is not escaped. +*/ + +void +ipa_struct_reorg::record_safe_func_with_void_ptr_parm () +{ + cgraph_node *node = NULL; + FOR_EACH_FUNCTION (node) + { + if (!node->real_symbol_p ()) + continue; + if (node->definition) + { + if (!node->has_gimple_body_p () || node->inlined_to) + continue; + node->get_body (); + function *fn = DECL_STRUCT_FUNCTION (node->decl); + if (!fn) + continue; + push_cfun (fn); + if (is_safe_func_with_void_ptr_parm (node)) + { + safe_functions.add (node); + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\nfunction %s/%u is safe function.\n", + node->name (), node->order); + } + pop_cfun (); + } + } +} + /* Record all accesses for all types including global variables. */ void @@ -3534,6 +4754,10 @@ ipa_struct_reorg::record_accesses (void) record_var (var->decl, escapes); } + /* Add a safe func mechanism. */ + if (current_mode == STRUCT_REORDER_FIELDS) + record_safe_func_with_void_ptr_parm (); + FOR_EACH_FUNCTION (cnode) { if (!cnode->real_symbol_p ()) @@ -3552,11 +4776,14 @@ ipa_struct_reorg::record_accesses (void) if (dump_file && (dump_flags & TDF_DETAILS)) { - fprintf (dump_file, "all types (before pruning):\n"); + fprintf (dump_file, "\n"); + fprintf (dump_file, "==============================================\n\n"); + fprintf (dump_file, "======== all types (before pruning): ========\n\n"); dump_types (dump_file); - fprintf (dump_file, "all functions (before pruning):\n"); + fprintf (dump_file, "======= all functions (before pruning): =======\n"); dump_functions (dump_file); } + /* If record_var () is called later, new types will not be recorded. */ done_recording = true; } @@ -3580,6 +4807,7 @@ ipa_struct_reorg::walk_field_for_cycles (srtype *type) { if (!field->type) ; + /* If there are two members of the same structure pointer type? */ else if (field->type->visited || walk_field_for_cycles (field->type)) { @@ -3658,22 +4886,113 @@ ipa_struct_reorg::propagate_escape (void) } while (changed); } +/* If the original type (with members) has escaped, corresponding to the + struct pointer type (empty member) in the structure fields + should also marked as escape. */ + +void +ipa_struct_reorg::propagate_escape_via_original (void) +{ + for (unsigned i = 0; i < types.length (); i++) + { + for (unsigned j = 0; j < types.length (); j++) + { + const char *type1 = get_type_name (types[i]->type); + const char *type2 = get_type_name (types[j]->type); + if (type1 == NULL || type2 == NULL) + continue; + if (type1 == type2 && types[j]->has_escaped ()) + { + if (!types[i]->has_escaped ()) + types[i]->mark_escape (escape_via_orig_escape, NULL); + break; + } + } + } +} + +/* Marks the fileds as empty and does not have the original structure type + is escape. */ + +void +ipa_struct_reorg::propagate_escape_via_empty_with_no_original (void) +{ + for (unsigned i = 0; i < types.length (); i++) + { + if (types[i]->fields.length () == 0) + { + for (unsigned j = 0; j < types.length (); j++) + { + if (i != j && types[j]->fields.length ()) + { + const char *type1 = get_type_name (types[i]->type); + const char *type2 = get_type_name (types[j]->type); + if (type1 != NULL && type2 != NULL && type1 == type2) + break; + } + if (j == types.length () - 1) + types[i]->mark_escape (escape_via_empty_no_orig, NULL); + } + } + } +} + +/* Escape propagation is performed on types that escape through external + functions. */ + +void +ipa_struct_reorg::propagate_escape_via_ext_func_types (void) +{ + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\n propagate_escape_via_ext_func_types: \n\n"); + unsigned i = 0; + hash_set visited_types; + while (i < ext_func_types.length ()) + { + visited_types.add (ext_func_types[i]); + unsigned j = 0; + srfield * field; + FOR_EACH_VEC_ELT (ext_func_types[i]->fields, j, field) + { + if (field->type) + { + if (!field->type->has_escaped ()) + field->type->mark_escape (escape_dependent_type_escapes, NULL); + if (!visited_types.contains (field->type)) + ext_func_types.safe_push (field->type); + } + } + i++; + } +} + /* Prune the escaped types and their decls from what was recorded. */ void ipa_struct_reorg::prune_escaped_types (void) { - if (current_mode != COMPLETE_STRUCT_RELAYOUT) + if (current_mode != COMPLETE_STRUCT_RELAYOUT + && current_mode != STRUCT_REORDER_FIELDS) { + /* Detect recusive types and mark them as escaping. */ detect_cycles (); + /* If contains or is contained by the escape type, + mark them as escaping. */ propagate_escape (); } + if (current_mode == STRUCT_REORDER_FIELDS) + { + propagate_escape_via_original (); + propagate_escape_via_empty_with_no_original (); + propagate_escape_via_ext_func_types (); + } if (dump_file && (dump_flags & TDF_DETAILS)) { - fprintf (dump_file, "all types (after prop but before pruning):\n"); + fprintf (dump_file, "==============================================\n\n"); + fprintf (dump_file, "all types (after prop but before pruning): \n\n"); dump_types (dump_file); - fprintf (dump_file, "all functions (after prop but before pruning):\n"); + fprintf (dump_file, "all functions (after prop but before pruning): \n"); dump_functions (dump_file); } @@ -3721,7 +5040,8 @@ ipa_struct_reorg::prune_escaped_types (void) /* Prune functions which don't refer to any variables any more. */ if (function->args.is_empty () && function->decls.is_empty () - && function->globals.is_empty ()) + && function->globals.is_empty () + && current_mode != STRUCT_REORDER_FIELDS) { delete function; functions.ordered_remove (i); @@ -3746,24 +5066,31 @@ ipa_struct_reorg::prune_escaped_types (void) /* Prune types that escape, all references to those types will have been removed in the above loops. */ - for (unsigned i = 0; i < types.length ();) + /* The escape type is not deleted in STRUCT_REORDER_FIELDS, + Then the type that contains the escaped type fields + can find complete information. */ + if (current_mode != STRUCT_REORDER_FIELDS) { - srtype *type = types[i]; - if (type->has_escaped ()) + for (unsigned i = 0; i < types.length ();) { - /* All references to this type should have been removed now. */ - delete type; - types.ordered_remove (i); + srtype *type = types[i]; + if (type->has_escaped ()) + { + /* All references to this type should have been removed now. */ + delete type; + types.ordered_remove (i); + } + else + i++; } - else - i++; } if (dump_file && (dump_flags & TDF_DETAILS)) { - fprintf (dump_file, "all types (after pruning):\n"); + fprintf (dump_file, "==============================================\n\n"); + fprintf (dump_file, "========= all types (after pruning): =========\n\n"); dump_types (dump_file); - fprintf (dump_file, "all functions (after pruning):\n"); + fprintf (dump_file, "======== all functions (after pruning): ========\n"); dump_functions (dump_file); } } @@ -3790,6 +5117,26 @@ ipa_struct_reorg::create_new_types (void) for (unsigned i = 0; i < types.length (); i++) newtypes += types[i]->create_new_type (); + if (current_mode == STRUCT_REORDER_FIELDS) + { + for (unsigned i = 0; i < types.length (); i++) + { + auto_vec *fields = fields_to_finish.get (types[i]->type); + if (fields) + { + for (unsigned j = 0; j < fields->length (); j++) + { + tree field = (*fields)[j]; + TREE_TYPE (field) + = reconstruct_complex_type (TREE_TYPE (field), + types[i]->newtype[0]); + } + } + } + for (unsigned i = 0; i < types.length (); i++) + layout_type (types[i]->newtype[0]); + } + if (dump_file) { if (newtypes) @@ -3894,7 +5241,8 @@ ipa_struct_reorg::create_new_args (cgraph_node *new_node) char *name = NULL; if (tname) { - name = concat (tname, ".reorg.0", NULL); + name = concat (tname, current_mode == STRUCT_REORDER_FIELDS + ? ".reorder.0" : ".reorg.0", NULL); new_name = get_identifier (name); free (name); } @@ -3980,9 +5328,10 @@ ipa_struct_reorg::create_new_functions (void) fprintf (dump_file, "\n"); } statistics_counter_event (NULL, "Create new function", 1); - new_node = node->create_version_clone_with_body (vNULL, NULL, - NULL, NULL, NULL, - "struct_reorg"); + new_node = node->create_version_clone_with_body ( + vNULL, NULL, NULL, NULL, NULL, + current_mode == STRUCT_REORDER_FIELDS + ? "struct_reorder" : "struct_reorg"); new_node->can_change_signature = node->can_change_signature; new_node->make_local (); f->newnode = new_node; @@ -4026,6 +5375,7 @@ ipa_struct_reorg::rewrite_expr (tree expr, srfield *f; bool realpart, imagpart; bool address; + bool escape_from_base = false; tree newbase[max_split]; memset (newexpr, 0, sizeof (tree[max_split])); @@ -4043,8 +5393,8 @@ ipa_struct_reorg::rewrite_expr (tree expr, return true; } - if (!get_type_field (expr, base, indirect, t, f, - realpart, imagpart, address)) + if (!get_type_field (expr, base, indirect, t, f, realpart, imagpart, + address, escape_from_base)) return false; /* If the type is not changed, then just return false. */ @@ -4107,7 +5457,38 @@ ipa_struct_reorg::rewrite_expr (tree expr, if (address) newbase1 = build_fold_addr_expr (newbase1); if (indirect) - newbase1 = build_simple_mem_ref (newbase1); + { + if (current_mode == STRUCT_REORDER_FIELDS) + { + /* Supports the MEM_REF offset. + _1 = MEM[(struct arc *)ap_1 + 72B].flow; + Old rewrite: _1 = ap.reorder.0_8->flow; + New rewrite: _1 + = MEM[(struct arc.reorder.0 *)ap.reorder.0_8 + 64B].flow; + */ + HOST_WIDE_INT offset_tmp = 0; + HOST_WIDE_INT mem_offset = 0; + bool realpart_tmp = false; + bool imagpart_tmp = false; + tree accesstype_tmp = NULL_TREE; + tree num = NULL_TREE; + get_ref_base_and_offset (expr, offset_tmp, + realpart_tmp, imagpart_tmp, + accesstype_tmp, &num); + + tree ptype = TREE_TYPE (newbase1); + /* Specify the correct size for the multi-layer pointer. */ + tree size = isptrptr (ptype) ? TYPE_SIZE_UNIT (ptype) : + TYPE_SIZE_UNIT (inner_type (ptype)); + mem_offset = (num != NULL) + ? TREE_INT_CST_LOW (num) * tree_to_shwi (size) + : 0; + newbase1 = build2 (MEM_REF, TREE_TYPE (ptype), newbase1, + build_int_cst (ptype, mem_offset)); + } + else + newbase1 = build_simple_mem_ref (newbase1); + } newexpr[i] = build3 (COMPONENT_REF, TREE_TYPE (f->newfield[i]), newbase1, f->newfield[i], NULL_TREE); if (imagpart) @@ -4151,8 +5532,12 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) return remove; } - if (gimple_assign_rhs_code (stmt) == EQ_EXPR - || gimple_assign_rhs_code (stmt) == NE_EXPR) + if ((current_mode != STRUCT_REORDER_FIELDS + && (gimple_assign_rhs_code (stmt) == EQ_EXPR + || gimple_assign_rhs_code (stmt) == NE_EXPR)) + || (current_mode == STRUCT_REORDER_FIELDS + && (TREE_CODE_CLASS (gimple_assign_rhs_code (stmt)) + == tcc_comparison))) { tree rhs1 = gimple_assign_rhs1 (stmt); tree rhs2 = gimple_assign_rhs2 (stmt); @@ -4160,6 +5545,10 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) tree newrhs2[max_split]; tree_code rhs_code = gimple_assign_rhs_code (stmt); tree_code code = rhs_code == EQ_EXPR ? BIT_AND_EXPR : BIT_IOR_EXPR; + if (current_mode == STRUCT_REORDER_FIELDS + && rhs_code != EQ_EXPR && rhs_code != NE_EXPR) + code = rhs_code; + if (!rewrite_lhs_rhs (rhs1, rhs2, newrhs1, newrhs2)) return false; tree newexpr = NULL_TREE; @@ -4201,20 +5590,78 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) internal_error ( "The rhs of pointer is not a multiplicate and it slips through"); - num = gimplify_build1 (gsi, NOP_EXPR, sizetype, num); + /* Add the judgment of num, support for POINTER_DIFF_EXPR. + _6 = _4 + _5; + _5 = (long unsigned int) _3; + _3 = _1 - old_2. */ + if (current_mode != STRUCT_REORDER_FIELDS + || (current_mode == STRUCT_REORDER_FIELDS && (num != NULL))) + num = gimplify_build1 (gsi, NOP_EXPR, sizetype, num); for (unsigned i = 0; i < max_split && newlhs[i]; i++) { gimple *new_stmt; - tree newsize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (newlhs[i]))); - newsize = gimplify_build2 (gsi, MULT_EXPR, sizetype, num, newsize); - new_stmt = gimple_build_assign (newlhs[i], POINTER_PLUS_EXPR, - newrhs[i], newsize); + if (num != NULL) + { + tree newsize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (newlhs[i]))); + newsize = gimplify_build2 (gsi, MULT_EXPR, sizetype, num, + newsize); + new_stmt = gimple_build_assign (newlhs[i], POINTER_PLUS_EXPR, + newrhs[i], newsize); + } + else + new_stmt = gimple_build_assign (newlhs[i], POINTER_PLUS_EXPR, + newrhs[i], rhs2); gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT); remove = true; } return remove; } + + /* Support POINTER_DIFF_EXPR rewriting. */ + if (current_mode == STRUCT_REORDER_FIELDS + && gimple_assign_rhs_code (stmt) == POINTER_DIFF_EXPR) + { + tree rhs1 = gimple_assign_rhs1 (stmt); + tree rhs2 = gimple_assign_rhs2 (stmt); + tree newrhs1[max_split]; + tree newrhs2[max_split]; + + bool r1 = rewrite_expr (rhs1, newrhs1); + bool r2 = rewrite_expr (rhs2, newrhs2); + + if (r1 != r2) + { + /* Handle NULL pointer specially. */ + if (r1 && !r2 && integer_zerop (rhs2)) + { + r2 = true; + for (unsigned i = 0; i < max_split && newrhs1[i]; i++) + newrhs2[i] = fold_convert (TREE_TYPE (newrhs1[i]), rhs2); + } + else if (r2 && !r1 && integer_zerop (rhs1)) + { + r1 = true; + for (unsigned i = 0; i < max_split && newrhs2[i]; i++) + newrhs1[i] = fold_convert (TREE_TYPE (newrhs2[i]), rhs1); + } + else + return false; + } + else if (!r1 && !r2) + return false; + + /* The two operands always have pointer/reference type. */ + for (unsigned i = 0; i < max_split && newrhs1[i] && newrhs2[i]; i++) + { + gimple_assign_set_rhs1 (stmt, newrhs1[i]); + gimple_assign_set_rhs2 (stmt, newrhs2[i]); + update_stmt (stmt); + } + remove = false; + return remove; + } + if (gimple_assign_rhs_class (stmt) == GIMPLE_SINGLE_RHS) { tree lhs = gimple_assign_lhs (stmt); @@ -4222,9 +5669,8 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) if (dump_file && (dump_flags & TDF_DETAILS)) { - fprintf (dump_file, "rewriting statement:\n"); + fprintf (dump_file, "\nrewriting stamtenet:\n"); print_gimple_stmt (dump_file, stmt, 0); - fprintf (dump_file, "\n"); } tree newlhs[max_split]; tree newrhs[max_split]; @@ -4271,7 +5717,7 @@ ipa_struct_reorg::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi) if (!decl || !decl->type) return false; srtype *type = decl->type; - tree num = allocate_size (type, stmt); + tree num = allocate_size (type, decl, stmt); gcc_assert (num); memset (newrhs1, 0, sizeof (newrhs1)); @@ -4291,7 +5737,10 @@ ipa_struct_reorg::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi) /* Go through each new lhs. */ for (unsigned i = 0; i < max_split && decl->newdecl[i]; i++) { - tree newsize = TYPE_SIZE_UNIT (type->type); + /* Specify the correct size for the multi-layer pointer. */ + tree newsize = isptrptr (decl->orig_type) + ? TYPE_SIZE_UNIT (decl->orig_type) + : TYPE_SIZE_UNIT (type->newtype[i]); gimple *g; /* Every allocation except for calloc needs the size multiplied out. */ @@ -4352,6 +5801,23 @@ ipa_struct_reorg::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi) gcc_assert (node); srfunction *f = find_function (node); + /* Add a safe func mechanism. */ + if (current_mode == STRUCT_REORDER_FIELDS && f && f->is_safe_func) + { + tree expr = gimple_call_arg (stmt, 0); + tree newexpr[max_split]; + if (!rewrite_expr (expr, newexpr)) + return false; + + if (newexpr[1] == NULL) + { + gimple_call_set_arg (stmt, 0, newexpr[0]); + update_stmt (stmt); + return false; + } + return false; + } + /* Did not find the function or had not cloned it return saying don't change the function call. */ if (!f || !f->newf) @@ -4437,7 +5903,7 @@ ipa_struct_reorg::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi) && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME) SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt; - gsi_replace (gsi, new_stmt, false); + gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT); /* We need to defer cleaning EH info on the new statement to fixup-cfg. We may not have dominator information at this point @@ -4450,7 +5916,7 @@ ipa_struct_reorg::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi) add_stmt_to_eh_lp (new_stmt, lp_nr); } - return false; + return true; } /* Rewrite the conditional statement STMT. Return TRUE if the @@ -4462,50 +5928,52 @@ ipa_struct_reorg::rewrite_cond (gcond *stmt, gimple_stmt_iterator *gsi) tree_code rhs_code = gimple_cond_code (stmt); /* Handle only equals or not equals conditionals. */ - if (rhs_code != EQ_EXPR - && rhs_code != NE_EXPR) + if ((current_mode != STRUCT_REORDER_FIELDS + && (rhs_code != EQ_EXPR && rhs_code != NE_EXPR)) + || (current_mode == STRUCT_REORDER_FIELDS + && TREE_CODE_CLASS (rhs_code) != tcc_comparison)) return false; - tree rhs1 = gimple_cond_lhs (stmt); - tree rhs2 = gimple_cond_rhs (stmt); + tree lhs = gimple_cond_lhs (stmt); + tree rhs = gimple_cond_rhs (stmt); if (dump_file && (dump_flags & TDF_DETAILS)) { - fprintf (dump_file, "COND: Rewriting\n"); + fprintf (dump_file, "\nCOND: Rewriting\n"); print_gimple_stmt (dump_file, stmt, 0); + print_generic_expr (dump_file, lhs); fprintf (dump_file, "\n"); - print_generic_expr (dump_file, rhs1); - fprintf (dump_file, "\n"); - print_generic_expr (dump_file, rhs2); + print_generic_expr (dump_file, rhs); fprintf (dump_file, "\n"); } - tree newrhs1[max_split]; - tree newrhs2[max_split]; - tree_code code = rhs_code == EQ_EXPR ? BIT_AND_EXPR : BIT_IOR_EXPR; - if (!rewrite_lhs_rhs (rhs1, rhs2, newrhs1, newrhs2)) + tree newlhs[max_split] = {}; + tree newrhs[max_split] = {}; + if (!rewrite_lhs_rhs (lhs, rhs, newlhs, newrhs)) { if (dump_file && (dump_flags & TDF_DETAILS)) - fprintf (dump_file, "\nDid nothing to statement.\n"); + fprintf (dump_file, "Did nothing to statement.\n"); return false; } - tree newexpr = NULL_TREE; - for (unsigned i = 0; i < max_split && newrhs1[i]; i++) - { - tree expr = gimplify_build2 (gsi, rhs_code, boolean_type_node, - newrhs1[i], newrhs2[i]); - if (!newexpr) - newexpr = expr; - else - newexpr = gimplify_build2 (gsi, code, boolean_type_node, - newexpr, expr); - } - - if (newexpr) + /* Old rewrite: if (x_1 != 0B) + -> _1 = x.reorder.0_1 != 0B; if (_1 != 1) + The logic is incorrect. + New rewrite: if (x_1 != 0B) + -> if (x.reorder.0_1 != 0B)ï¼›*/ + for (unsigned i = 0; i < max_split && (newlhs[i] || newrhs[i]); i++) { - gimple_cond_set_lhs (stmt, newexpr); - gimple_cond_set_rhs (stmt, boolean_true_node); + if (newlhs[i]) + gimple_cond_set_lhs (stmt, newlhs[i]); + if (newrhs[i]) + gimple_cond_set_rhs (stmt, newrhs[i]); update_stmt (stmt); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "replaced with:\n"); + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + } } return false; } @@ -4516,6 +5984,9 @@ ipa_struct_reorg::rewrite_cond (gcond *stmt, gimple_stmt_iterator *gsi) bool ipa_struct_reorg::rewrite_debug (gimple *stmt, gimple_stmt_iterator *) { + if (current_mode == STRUCT_REORDER_FIELDS) + /* Delete debug gimple now. */ + return true; bool remove = false; if (gimple_debug_bind_p (stmt)) { @@ -4568,7 +6039,7 @@ ipa_struct_reorg::rewrite_phi (gphi *phi) if (dump_file && (dump_flags & TDF_DETAILS)) { - fprintf (dump_file, "\nrewriting PHI:"); + fprintf (dump_file, "\nrewriting PHI:\n"); print_gimple_stmt (dump_file, phi, 0); } @@ -4579,7 +6050,15 @@ ipa_struct_reorg::rewrite_phi (gphi *phi) { tree newrhs[max_split]; phi_arg_d rhs = *gimple_phi_arg (phi, i); - rewrite_expr (rhs.def, newrhs); + + /* Handling the NULL phi Node. */ + bool r = rewrite_expr (rhs.def, newrhs); + if (!r && integer_zerop (rhs.def)) + { + for (unsigned i = 0; i < max_split && newlhs[i]; i++) + newrhs[i] = fold_convert (TREE_TYPE (newlhs[i]), rhs.def); + } + for (unsigned j = 0; j < max_split && newlhs[j]; j++) { SET_PHI_ARG_DEF (newphi[j], i, newrhs[j]); @@ -4590,7 +6069,7 @@ ipa_struct_reorg::rewrite_phi (gphi *phi) if (dump_file && (dump_flags & TDF_DETAILS)) { - fprintf (dump_file, "\ninto\n:"); + fprintf (dump_file, "into:\n"); for (unsigned i = 0; i < max_split && newlhs[i]; i++) { print_gimple_stmt (dump_file, newphi[i], 0); @@ -4663,12 +6142,59 @@ ipa_struct_reorg::rewrite_functions (void) /* Create new types, if we did not create any new types, then don't rewrite any accesses. */ if (!create_new_types ()) - return 0; + { + if (current_mode == STRUCT_REORDER_FIELDS) + { + for (unsigned i = 0; i < functions.length (); i++) + { + srfunction *f = functions[i]; + cgraph_node *node = f->node; + push_cfun (DECL_STRUCT_FUNCTION (node->decl)); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nNo rewrite:\n"); + dump_function_to_file (current_function_decl, dump_file, + dump_flags | TDF_VOPS); + } + pop_cfun (); + } + } + return 0; + } + + if (current_mode == STRUCT_REORDER_FIELDS && dump_file) + { + fprintf (dump_file, "=========== all created newtypes: ===========\n\n"); + dump_newtypes (dump_file); + } if (functions.length ()) { retval = TODO_remove_functions; create_new_functions (); + if (current_mode == STRUCT_REORDER_FIELDS) + { + prune_escaped_types (); + } + } + + if (current_mode == STRUCT_REORDER_FIELDS) + { + for (unsigned i = 0; i < functions.length (); i++) + { + srfunction *f = functions[i]; + cgraph_node *node = f->node; + push_cfun (DECL_STRUCT_FUNCTION (node->decl)); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "==== Before create decls: %dth_%s ====\n\n", + i, f->node->name ()); + if (current_function_decl) + dump_function_to_file (current_function_decl, dump_file, + dump_flags | TDF_VOPS); + } + pop_cfun (); + } } create_new_decls (); @@ -4691,9 +6217,12 @@ ipa_struct_reorg::rewrite_functions (void) if (dump_file && (dump_flags & TDF_DETAILS)) { - fprintf (dump_file, "\nBefore rewrite:\n"); + fprintf (dump_file, "\nBefore rewrite: %dth_%s\n", + i, f->node->name ()); dump_function_to_file (current_function_decl, dump_file, dump_flags | TDF_VOPS); + fprintf (dump_file, "\n======== Start to rewrite: %dth_%s ========\n", + i, f->node->name ()); } FOR_EACH_BB_FN (bb, cfun) { @@ -4761,9 +6290,10 @@ ipa_struct_reorg::rewrite_functions (void) free_dominance_info (CDI_DOMINATORS); - if (dump_file && (dump_flags & TDF_DETAILS)) + if (dump_file) { - fprintf (dump_file, "\nAfter rewrite:\n"); + fprintf (dump_file, "\nAfter rewrite: %dth_%s\n", + i, f->node->name ()); dump_function_to_file (current_function_decl, dump_file, dump_flags | TDF_VOPS); } @@ -4809,16 +6339,21 @@ ipa_struct_reorg::execute (enum srmode mode) { unsigned int ret = 0; - if (mode == NORMAL) + if (dump_file) + fprintf (dump_file, "\n\n====== ipa_struct_reorg level %d ======\n\n", + mode); + + if (mode == NORMAL || mode == STRUCT_REORDER_FIELDS) { - current_mode = NORMAL; - /* FIXME: If there is a top-level inline-asm, + current_mode = mode; + /* If there is a top-level inline-asm, the pass immediately returns. */ if (symtab->first_asm_symbol ()) return 0; record_accesses (); prune_escaped_types (); - analyze_types (); + if (current_mode == NORMAL) + analyze_types (); ret = rewrite_functions (); } @@ -4881,7 +6416,55 @@ pass_ipa_struct_reorg::gate (function *) && flag_lto_partition == LTO_PARTITION_ONE /* Only enable struct optimizations in C since other languages' grammar forbid. */ - && lang_c_p ()); + && lang_c_p () + /* Only enable struct optimizations in lto or whole_program. */ + && (in_lto_p || flag_whole_program)); +} + +const pass_data pass_data_ipa_reorder_fields = +{ + SIMPLE_IPA_PASS, // type + "reorder_fields", // name + OPTGROUP_NONE, // optinfo_flags + TV_IPA_REORDER_FIELDS, // tv_id + 0, // properties_required + 0, // properties_provided + 0, // properties_destroyed + 0, // todo_flags_start + 0, // todo_flags_finish +}; + +class pass_ipa_reorder_fields : public simple_ipa_opt_pass +{ +public: + pass_ipa_reorder_fields (gcc::context *ctxt) + : simple_ipa_opt_pass (pass_data_ipa_reorder_fields, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *); + virtual unsigned int execute (function *) + { + unsigned int ret = 0; + ret = ipa_struct_reorg ().execute (STRUCT_REORDER_FIELDS); + return ret; + } + +}; // class pass_ipa_reorder_fields + +bool +pass_ipa_reorder_fields::gate (function *) +{ + return (optimize >= 3 + && flag_ipa_reorder_fields + /* Don't bother doing anything if the program has errors. */ + && !seen_error () + && flag_lto_partition == LTO_PARTITION_ONE + /* Only enable struct optimizations in C since other + languages' grammar forbid. */ + && lang_c_p () + /* Only enable struct optimizations in lto or whole_program. */ + && (in_lto_p || flag_whole_program)); } } // anon namespace @@ -4891,4 +6474,10 @@ simple_ipa_opt_pass * make_pass_ipa_struct_reorg (gcc::context *ctxt) { return new pass_ipa_struct_reorg (ctxt); -} \ No newline at end of file +} + +simple_ipa_opt_pass * +make_pass_ipa_reorder_fields (gcc::context *ctxt) +{ + return new pass_ipa_reorder_fields (ctxt); +} diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.h b/gcc/ipa-struct-reorg/ipa-struct-reorg.h index ef7f4c78080..6f85adeb4e1 100644 --- a/gcc/ipa-struct-reorg/ipa-struct-reorg.h +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.h @@ -68,12 +68,14 @@ struct srfunction auto_vec args; auto_vec globals; auto_vec_del decls; - srdecl *record_decl (srtype *, tree, int arg); + srdecl *record_decl (srtype *, tree, int arg, tree orig_type = NULL); srfunction *old; cgraph_node *newnode; srfunction *newf; + bool is_safe_func; + // Constructors srfunction (cgraph_node *n); @@ -184,6 +186,11 @@ struct srfield void create_new_fields (tree newtype[max_split], tree newfields[max_split], tree newlast[max_split]); + void reorder_fields (tree newfields[max_split], tree newlast[max_split], + tree &field); + void create_new_reorder_fields (tree newtype[max_split], + tree newfields[max_split], + tree newlast[max_split]); }; struct sraccess @@ -221,8 +228,11 @@ struct srdecl tree newdecl[max_split]; + /* Auxiliary record complete original type information of the void* type. */ + tree orig_type; + // Constructors - srdecl (srtype *type, tree decl, int argumentnum = -1); + srdecl (srtype *type, tree decl, int argumentnum = -1, tree orgtype = NULL); // Methods void dump (FILE *file); diff --git a/gcc/passes.def b/gcc/passes.def index 1c1658c4a85..cdb950220dd 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -177,6 +177,7 @@ along with GCC; see the file COPYING3. If not see compiled unit. */ INSERT_PASSES_AFTER (all_late_ipa_passes) NEXT_PASS (pass_ipa_pta); + NEXT_PASS (pass_ipa_reorder_fields); /* FIXME: this should be a normal IP pass. */ NEXT_PASS (pass_ipa_struct_reorg); NEXT_PASS (pass_omp_simd_clone); diff --git a/gcc/symbol-summary.h b/gcc/symbol-summary.h index 3fe64047c8b..6fa529eee45 100644 --- a/gcc/symbol-summary.h +++ b/gcc/symbol-summary.h @@ -105,7 +105,7 @@ protected: { /* In structure optimizatons, we call new to ensure that the allocated memory is initialized to 0. */ - if (flag_ipa_struct_reorg) + if (flag_ipa_struct_reorg || flag_ipa_reorder_fields) return is_ggc () ? new (ggc_internal_alloc (sizeof (T))) T () : new T (); @@ -122,7 +122,7 @@ protected: ggc_delete (item); else { - if (flag_ipa_struct_reorg) + if (flag_ipa_struct_reorg || flag_ipa_reorder_fields) delete item; else m_allocator.remove (item); diff --git a/gcc/testsuite/gcc.dg/struct/rf_DTE_struct_instance_field.c b/gcc/testsuite/gcc.dg/struct/rf_DTE_struct_instance_field.c new file mode 100644 index 00000000000..b95be2dabc2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_DTE_struct_instance_field.c @@ -0,0 +1,75 @@ +// escape_instance_field, "Type escapes via a field of instance". +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +typedef struct network +{ + arc_p arcs; + arc_p sorted_arcs; + int x; + node_p nodes; + node_p stop_nodes; + node_t node; +} network_t; + + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; + network_t* net_add; + node_t node; +}; + + +const int MAX = 100; + +/* let it escape_array, "Type is used in an array [not handled yet]". */ +network_t* net[2]; + +int +main () +{ + net[0] = (network_t*) calloc (1, sizeof(network_t)); + net[0]->arcs = (arc_p) calloc (MAX, sizeof (arc_t)); + + /* Contains an escape type and has structure instance field. */ + net[0]->arcs->node = net[0]->node; + + return 0; +} + +/* { dg-final { scan-ipa-dump "No structures to transform." "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_DTE_verify.c b/gcc/testsuite/gcc.dg/struct/rf_DTE_verify.c new file mode 100644 index 00000000000..3d243313ba9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_DTE_verify.c @@ -0,0 +1,94 @@ +// Verify in escape_dependent_type_escapes, +// the multi-layer dereference is rewriting correctly,and the memory access +// is correct. + +// release +// escape_dependent_type_escapes, +// "Type uses a type which escapes or is used by a type which escapes" +// avoid escape_cast_another_ptr, "Type escapes a cast to a different pointer" +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +typedef struct network +{ + arc_p arcs; + arc_p sorted_arcs; + int x; + node_p nodes; + node_p stop_nodes; +} network_t; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; + network_t* net_add; +}; + + +const int MAX = 100; + +/* let it escape_array, "Type is used in an array [not handled yet]". */ +network_t* net[2]; +arc_p stop_arcs = NULL; + +int +main () +{ + net[0] = (network_t*) calloc (1, sizeof(network_t)); + net[0]->arcs = (arc_p) calloc (MAX, sizeof (arc_t)); + stop_arcs = (arc_p) calloc (MAX, sizeof (arc_t)); + + net[0]->arcs->id = 100; + + for (unsigned i = 0; i < 3; i++) + { + net[0]->arcs->id = net[0]->arcs->id + 2; + stop_arcs->cost = net[0]->arcs->id / 2; + stop_arcs->net_add = net[0]; + printf("stop_arcs->cost = %ld\n", stop_arcs->cost); + net[0]->arcs++; + stop_arcs++; + } + + if( net[1] != 0 && stop_arcs != 0) + { + return -1; + } + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_check_ptr_layers_bug.c b/gcc/testsuite/gcc.dg/struct/rf_check_ptr_layers_bug.c new file mode 100644 index 00000000000..faaf1e3a52d --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_check_ptr_layers_bug.c @@ -0,0 +1,24 @@ +/* check_ptr_layers bugfix.*/ +/* { dg-do compile } */ +struct { + char a; +} **b = 0, *e = 0; +long c; +char d = 9; +int f; + +void g() +{ + for (; f;) + if (c) + (*e).a++; + if (!d) + for (;;) + b &&c; +} +int +main() +{ + g(); +} +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_create_fields_bug.c b/gcc/testsuite/gcc.dg/struct/rf_create_fields_bug.c new file mode 100644 index 00000000000..886706ae913 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_create_fields_bug.c @@ -0,0 +1,82 @@ +// bugfix: +// Common members do not need to reconstruct. +// Otherwise, eg:int* -> int** and void* -> void**. +/* { dg-do compile } */ + +#include +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t* cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t** org_cost; +}; + +struct a +{ + int t; + int t1; +}; + +__attribute__((noinline)) int +f(int i, int j) +{ + struct a *t = NULL; + struct a t1 = {i, j}; + t = &t1; + auto int g(void) __attribute__((noinline)); + int g(void) + { + return t->t + t->t1; + } + return g(); +} + +arc_t **ap = NULL; +const int MAX = 100; + +int +main() +{ + if (f(1, 2) != 3) + { + abort (); + } + ap = (arc_t**) malloc(MAX * sizeof(arc_t*)); + (*ap)[0].id = 300; + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_create_new_func_bug.c b/gcc/testsuite/gcc.dg/struct/rf_create_new_func_bug.c new file mode 100644 index 00000000000..f3785f392e3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_create_new_func_bug.c @@ -0,0 +1,56 @@ +/* { dg-do compile } */ + +#include +#include + +#define MallocOrDie(x) sre_malloc((x)) + +struct gki_elem { + char *key; + int idx; + struct gki_elem *nxt; +}; + +typedef struct { + struct gki_elem **table; + + int primelevel; + int nhash; + int nkeys; +} GKI; + +void +Die(char *format, ...) +{ + exit(1); +} + +void * +sre_malloc(size_t size) +{ + void *ptr; + + if ((ptr = malloc (size)) == NULL) + { + Die("malloc of %ld bytes failed", size); + } + return ptr; +} + + +__attribute__((noinline)) int +GKIStoreKey(GKI *hash, char *key) +{ + hash->table[0] = MallocOrDie(sizeof(struct gki_elem)); +} + +int +main () +{ + GKI *hash; + char *key; + GKIStoreKey(hash, key); + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_ele_minus_verify.c b/gcc/testsuite/gcc.dg/struct/rf_ele_minus_verify.c new file mode 100644 index 00000000000..1415d759ad6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_ele_minus_verify.c @@ -0,0 +1,60 @@ +// verify newarc[cmp-1].flow +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +const int MAX = 100; +arc_p ap = NULL; + +int +main () +{ + ap = (arc_p) calloc(MAX, sizeof(arc_t)); + printf("%d\n", ap[0].id); + for (int i = 1; i < MAX; i++) + { + ap[i-1].id = 500; + } + printf("%d\n", ap[0].id); + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_escape_by_base.c b/gcc/testsuite/gcc.dg/struct/rf_escape_by_base.c new file mode 100644 index 00000000000..003da0b57bf --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_escape_by_base.c @@ -0,0 +1,83 @@ +// release type is used by a type which escapes. +// avoid escape_cast_another_ptr, "Type escapes a cast to a different pointer" +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +typedef struct network +{ + arc_p arcs; + arc_p sorted_arcs; + int x; + node_p nodes; + node_p stop_nodes; +} network_t; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +const int MAX = 100; +network_t* net = NULL; +arc_p stop_arcs = NULL; +int cnt = 0; + +int +main () +{ + net = (network_t*) calloc (1, 20); + net->arcs = (arc_p) calloc (MAX, sizeof (arc_t)); + stop_arcs = (arc_p) calloc (MAX, sizeof (arc_t)); + if(!(net->arcs)) + { + return -1; + } + + for( int i = 0; i < MAX; i++, net->arcs = stop_arcs) + { + cnt++; + } + + net = (network_t*) calloc (1, 20); + if( !(net->arcs) ) + { + return -1; + } + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_external_func_types.c b/gcc/testsuite/gcc.dg/struct/rf_external_func_types.c new file mode 100644 index 00000000000..84a34f24151 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_external_func_types.c @@ -0,0 +1,69 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-shared" } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +typedef struct network +{ + int x; + arc_p arcs, sorted_arcs; + node_p nodes, stop_nodes; +} network_t; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +extern int bcf_sr_add_reader (network_t *); +extern int bcf_hdr_dup (arc_p); + +int +test () +{ + network_t *net = (network_t *) calloc (1, 20); + + if (!bcf_sr_add_reader(net)) + printf("error"); + arc_p arc = net->nodes->basic_arc; + if(!bcf_hdr_dup(arc)) + { + return -1; + } + return 0; +} + +/* { dg-final { scan-ipa-dump "No structures to transform." "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_int_cast_ptr.c b/gcc/testsuite/gcc.dg/struct/rf_int_cast_ptr.c new file mode 100644 index 00000000000..10dcf098c3c --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_int_cast_ptr.c @@ -0,0 +1,72 @@ +// release escape_cast_another_ptr, "Type escapes a cast to a different pointer" +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +typedef int cmp_t(const void *, const void *); + +__attribute__((noinline)) void +spec_qsort(void *a, cmp_t *cmp) +{ + char *pb = NULL; + while (cmp(pb, a)) + { + pb += 1; + } +} + +static int arc_compare( arc_t **a1, int a2 ) +{ + if( (*a1)->id < a2 ) + { + return -1; + } + return 1; +} + +int +main() +{ + spec_qsort(NULL, (int (*)(const void *, const void *))arc_compare); + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_mem_ref_offset.c b/gcc/testsuite/gcc.dg/struct/rf_mem_ref_offset.c new file mode 100644 index 00000000000..8d1a9a114c1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_mem_ref_offset.c @@ -0,0 +1,58 @@ +/* Supports the MEM_REF offset. + _1 = MEM[(struct arc *)ap_4 + 72B].flow; + Old rewrite:_1 = ap.reorder.0_8->flow; + New rewrite:_1 = MEM[(struct arc.reorder.0 *)ap.reorder.0_8 + 64B].flow. */ +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +int +main () +{ + const int MAX = 100; + /* A similar scenario can be reproduced only by using local variables. */ + arc_p ap = NULL; + ap = (arc_p) calloc(MAX, sizeof(arc_t)); + printf("%d\n", ap[1].flow); + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_mul_layer_ptr_record_bug.c b/gcc/testsuite/gcc.dg/struct/rf_mul_layer_ptr_record_bug.c new file mode 100644 index 00000000000..23765fc5615 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_mul_layer_ptr_record_bug.c @@ -0,0 +1,30 @@ +/* { dg-do compile } */ + +#include +#include + +typedef struct T_HASH_ENTRY +{ + unsigned int hash; + unsigned int klen; + char *key; +} iHashEntry; + +typedef struct T_HASH +{ + unsigned int size; + unsigned int fill; + unsigned int keys; + + iHashEntry **array; +} uHash; + +uHash *retval; + +int +main() { + retval->array = (iHashEntry **)calloc(sizeof(iHashEntry *), retval->size); + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_pass_conflict.c b/gcc/testsuite/gcc.dg/struct/rf_pass_conflict.c new file mode 100644 index 00000000000..54e737ee856 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_pass_conflict.c @@ -0,0 +1,109 @@ +// For testing: +/* +Compile options: gcc -O3 -g +-flto -flto-partition=one -fipa-reorder-fields -fipa-struct-reorg +-v -save-temps -fdump-ipa-all-details test.c -o test + +in COMPLETE_STRUCT_RELAYOUT pass: +N type: struct node.reorder.0 new = "Type escapes a cast to a different pointer" +copy$head_26 = test_arc.reorder.0_49->head; + +type : struct arc.reorder.0(1599) { +fields = { +field (5382) {type = cost_t} +field (5383) {type = struct node.reorder.0 *} // but node has escaped. +field (5384) {type = struct node.reorder.0 *} +field (5386) {type = struct arc.reorder.0 *} +field (5387) {type = struct arc.reorder.0 *} +field (5388) {type = flow_t} +field (5389) {type = cost_t} +field (5381) {type = int} +field (5385) {type = short int} +} + +// The types of the two types are inconsistent after the rewriting. +newarc_2(D)->tail = tail_1(D); +vs +struct_reorder.0_61(D)->tail = tail_1(D); +*/ +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +typedef struct network +{ + arc_p arcs; + arc_p sorted_arcs; + int x; + node_p nodes; + node_p stop_nodes; +} network_t; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +__attribute__((noinline)) void +replace_weaker_arc( arc_t *newarc, node_t *tail, node_t *head) +{ + printf("test"); +} + +__attribute__((noinline)) int64_t +switch_arcs(arc_t** deleted_arcs, arc_t* arcnew) +{ + int64_t count = 0; + arc_t *test_arc, copy; + + if (!test_arc->ident) + { + copy = *test_arc; + count++; + *test_arc = arcnew[0]; + replace_weaker_arc(arcnew, NULL, NULL); + } + return count; +} + +int +main () +{ + switch_arcs(NULL, NULL); + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_ptr2void_lto.c b/gcc/testsuite/gcc.dg/struct/rf_ptr2void_lto.c new file mode 100644 index 00000000000..2ae46fb3112 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_ptr2void_lto.c @@ -0,0 +1,87 @@ +// escape_cast_void, "Type escapes a cast to/from void*" +// stop_393 = net.stop_nodes; void *stop; +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +typedef struct network +{ + arc_p arcs, sorted_arcs; + int x; + node_p nodes, stop_nodes; +} network_t; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +const int MAX = 100; +network_t* net = NULL; +int cnt = 0; + +__attribute__((noinline)) int +primal_feasible (network_t *net) +{ + void* stop; + node_t *node; + + node = net->nodes; + stop = (void *)net->stop_nodes; + for( node++; node < (node_t *)stop; node++ ) + { + printf( "PRIMAL NETWORK SIMPLEX: " ); + } + return 0; +} + +int +main () +{ + net = (network_t*) calloc (1, 20); + net->nodes = calloc (MAX, sizeof (node_t)); + net->stop_nodes = calloc (MAX, sizeof (node_t)); + cnt = primal_feasible( net ); + + net = (network_t*) calloc (1, 20); + if( !(net->arcs) ) + { + return -1; + } + return cnt; +} + +/* { dg-final { scan-ipa-dump "No structures to transform." "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_ptr_diff.c b/gcc/testsuite/gcc.dg/struct/rf_ptr_diff.c new file mode 100644 index 00000000000..488575d0b85 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_ptr_diff.c @@ -0,0 +1,71 @@ +// support POINTER_DIFF_EXPR & NOP_EXPR to avoid +// escape_unhandled_rewrite, "Type escapes via a unhandled rewrite stmt" +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +typedef struct network +{ + arc_p arcs; + arc_p sorted_arcs; + int x; + node_p nodes; + node_p stop_nodes; +} network_t; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +int +main () +{ + arc_t *old_arcs; + node_t *node; + node_t *stop; + size_t off; + network_t* net; + + for( ; node->number < stop->number; node++ ) + { + off = node->basic_arc - old_arcs; + node->basic_arc = (arc_t *)(net->arcs + off); + } + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 3" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_ptr_negate_expr.c b/gcc/testsuite/gcc.dg/struct/rf_ptr_negate_expr.c new file mode 100644 index 00000000000..7b7d110df4c --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_ptr_negate_expr.c @@ -0,0 +1,55 @@ +// support NEGATE_EXPR rewriting +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +int +main () +{ + int64_t susp = 0; + const int MAX = 100; + arc_p ap = (arc_p) calloc(MAX, sizeof(arc_t)); + ap -= susp; + printf("%d\n", ap[1].flow); + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_ptr_offset.c b/gcc/testsuite/gcc.dg/struct/rf_ptr_offset.c new file mode 100644 index 00000000000..317aafa5f72 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_ptr_offset.c @@ -0,0 +1,34 @@ +/* { dg-do compile } */ + +#include +#include + +struct node +{ + struct node *left, *right; + double a, b, c, d, e, f; +} +*a; +int b, c; +void +CreateNode (struct node **p1) +{ + *p1 = calloc (10, sizeof (struct node)); +} + +int +main () +{ + a->left = 0; + struct node *t = a; + CreateNode (&t->right); + + struct node p = *a; + b = 1; + if (p.left) + b = 0; + if (b) + printf (" Tree.\n"); +} + +/* { dg-final { scan-ipa-dump "No structures to transform." "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_ptr_ptr.c b/gcc/testsuite/gcc.dg/struct/rf_ptr_ptr.c new file mode 100644 index 00000000000..01a33f66962 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_ptr_ptr.c @@ -0,0 +1,55 @@ +// release escape_ptr_ptr, "Type is used in a pointer to a pointer [not handled yet]"; +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +const int MAX = 100; +arc_t **ap = NULL; + +int +main () +{ + ap = (arc_t**) malloc(MAX * sizeof(arc_t*)); + (*ap)[0].id = 300; + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_ptr_ptr_ptr.c b/gcc/testsuite/gcc.dg/struct/rf_ptr_ptr_ptr.c new file mode 100644 index 00000000000..a38556533f1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_ptr_ptr_ptr.c @@ -0,0 +1,58 @@ +// release escape_ptr_ptr, "Type is used in a pointer to a pointer [not handled yet]" + +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +const int MAX = 100; +arc_p **ap; + + +int +main () +{ + ap = (arc_p**) calloc(MAX, sizeof(arc_p*)); + (**ap)[0].id = 500; + + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_rescusive_type.c b/gcc/testsuite/gcc.dg/struct/rf_rescusive_type.c new file mode 100644 index 00000000000..5c17ee528c8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_rescusive_type.c @@ -0,0 +1,57 @@ +// release escape_rescusive_type, "Recusive type" +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +const int MAX = 100; +arc_p ap = NULL; + +int +main () +{ + ap = (arc_p) calloc (MAX, sizeof (arc_t)); + ap[0].id = 100; + ap[0].head = (node_p) calloc (MAX, sizeof (node_t)); + + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_rewrite_assign_more_cmp.c b/gcc/testsuite/gcc.dg/struct/rf_rewrite_assign_more_cmp.c new file mode 100644 index 00000000000..710517ee9e2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_rewrite_assign_more_cmp.c @@ -0,0 +1,65 @@ +// support more gimple assign rhs code +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +__attribute__((noinline)) int +compare(arc_p p1, arc_p p2) +{ + return p1 < p2; +} + +int n = 0; +int m = 0; + +int +main () +{ + scanf ("%d %d", &n, &m); + arc_p p = calloc (10, sizeof (struct arc)); + if (compare (&p[n], &p[m])) + { + printf ("ss!"); + } + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_rewrite_cond_bug.c b/gcc/testsuite/gcc.dg/struct/rf_rewrite_cond_bug.c new file mode 100644 index 00000000000..6ed0a5d2d6b --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_rewrite_cond_bug.c @@ -0,0 +1,72 @@ +// rewrite_cond bugfixï¼› +/* +if (iterator_600 != 0B) +old rewrite: _1369 = iterator.reorder.0_1249 != 0B; if (_1369 != 1) +new rewrite: if (iterator.reorder.0_1249 != 0B) +*/ +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +typedef struct list_elem +{ + arc_t* arc; + struct list_elem* next; +}list_elem; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +int i = 0; + +int +main () +{ + register list_elem *first_list_elem; + register list_elem* iterator; + iterator = first_list_elem->next; + while (iterator) + { + iterator = iterator->next; + i++; + } + + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 3" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_rewrite_cond_more_cmp.c b/gcc/testsuite/gcc.dg/struct/rf_rewrite_cond_more_cmp.c new file mode 100644 index 00000000000..5a2dd964fc2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_rewrite_cond_more_cmp.c @@ -0,0 +1,58 @@ +// support if (_150 >= _154) +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +int +main() +{ + arc_p **ap = (arc_p**) malloc(1 * sizeof(arc_p*)); + arc_p **arcs_pointer_sorted = (arc_p**) malloc(1 * sizeof(arc_p*)); + arcs_pointer_sorted[0] = (arc_p*) calloc (1, sizeof(arc_p)); + + if (arcs_pointer_sorted >= ap) + { + return -1; + } + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_rewrite_phi_bug.c b/gcc/testsuite/gcc.dg/struct/rf_rewrite_phi_bug.c new file mode 100644 index 00000000000..faa90b42ddc --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_rewrite_phi_bug.c @@ -0,0 +1,81 @@ +/* +Exclude the rewriting error caused by +first_list_elem = (list_elem *)NULL; +rewriting PHI:first_list_elem_700 = PHI <0B(144), 0B(146)> +into: +first_list_elem.reorder.0_55 = PHI <(144), (146)> +*/ +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +typedef struct list_elem +{ + arc_t* arc; + struct list_elem* next; +}list_elem; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout, firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail, head; + short ident; + arc_p nextout, nextin; + int64_t flow; + int64_t org_cost; +}; + +const int MAX = 100; + +list_elem* new_list_elem; +list_elem* first_list_elem; + +int +main () +{ + int i = 0; + list_elem *first_list_elem; + list_elem *new_list_elem; + arc_t *arcout; + for( ; i < MAX && arcout->ident == -1; i++); + + first_list_elem = (list_elem *)NULL; + for( ; i < MAX; i++) + { + new_list_elem = (list_elem*) calloc(1, sizeof(list_elem)); + new_list_elem->next = first_list_elem; + first_list_elem = new_list_elem; + } + if (first_list_elem != 0) + { + return -1; + } + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 3" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_shwi.c b/gcc/testsuite/gcc.dg/struct/rf_shwi.c new file mode 100644 index 00000000000..2bb326ff200 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_shwi.c @@ -0,0 +1,23 @@ +/* { dg-do compile } */ + +struct foo {int dx; long dy; int dz; }; +struct goo {long offset; struct foo* pfoo; }; + +void* func (long); + +__attribute__((used)) static void +test(struct goo* g) +{ + void* pvoid; + struct foo* f; + + for (f = g->pfoo; f->dx; f++) + { + if (f->dy) + break; + } + f--; + + pvoid = func(f->dz + g->offset); + return; +} diff --git a/gcc/testsuite/gcc.dg/struct/rf_visible_func.c b/gcc/testsuite/gcc.dg/struct/rf_visible_func.c new file mode 100644 index 00000000000..8f2da99ccdf --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_visible_func.c @@ -0,0 +1,92 @@ +// release escape_visible_function, "Type escapes via expternally visible function call" +// compile options: gcc -O3 -fno-inline -fwhole-program +// -flto-partition=one -fipa-struct-reorg arc_compare.c -fdump-ipa-all -S -v +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +__attribute__((noinline)) static int +arc_compare( arc_t **a1, arc_t **a2 ) +{ + if( (*a1)->flow > (*a2)->flow ) + { + return 1; + } + if( (*a1)->flow < (*a2)->flow ) + { + return -1; + } + if( (*a1)->id < (*a2)->id ) + { + return -1; + } + + return 1; +} + +__attribute__((noinline)) void +spec_qsort(void *array, int nitems, int size, + int (*cmp)(const void*,const void*)) +{ + for (int i = 0; i < nitems - 1; i++) + { + if (cmp (array , array)) + { + printf ("CMP 1\n"); + } + else + { + printf ("CMP 2\n"); + } + } +} + +typedef int cmp_t(const void *, const void *); + +int +main () +{ + void *p = calloc (100, sizeof (arc_t **)); + spec_qsort (p, 100, 0, (int (*)(const void *, const void *))arc_compare); + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_void_ptr_param_func.c b/gcc/testsuite/gcc.dg/struct/rf_void_ptr_param_func.c new file mode 100644 index 00000000000..ee455dd1803 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_void_ptr_param_func.c @@ -0,0 +1,54 @@ +// Add a safe func mechanism. +// avoid escape_unkown_field, "Type escapes via an unkown field accessed" +// avoid escape_cast_void, "Type escapes a cast to/from void*" eg: GIMPLE_NOP +/* { dg-do compile } */ + +#include +#include + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +struct arc +{ + int id; + int64_t cost; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +void +__attribute__((noinline)) spec_qsort (void *a, size_t es) +{ + char *pa; + char *pb; + int cmp_result; + + while ((*(arc_t **)a)->id < *((int *)a)) + { + if (cmp_result == 0) + { + spec_qsort (a, es); + pa = (char *)a - es; + a += es; + *(long *)pb = *(long *)pa; + } + else + { + a -= pa - pb; + } + } +} + +int +main() +{ + arc_p **arcs_pointer_sorted; + spec_qsort (arcs_pointer_sorted[0], sizeof (arc_p)); + return 0; +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "reorder_fields" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/struct-reorg.exp b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp index 43913104ee9..5a476e8f91b 100644 --- a/gcc/testsuite/gcc.dg/struct/struct-reorg.exp +++ b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp @@ -27,8 +27,21 @@ set STRUCT_REORG_TORTURE_OPTIONS [list \ set-torture-options $STRUCT_REORG_TORTURE_OPTIONS {{}} -gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.c]] \ +# -fipa-struct-reorg +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/wo_*.c]] \ + "" "-fipa-struct-reorg -fdump-ipa-all -flto-partition=one -fwhole-program" +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/w_*.c]] \ "" "-fipa-struct-reorg -fdump-ipa-all -flto-partition=one -fwhole-program" +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/struct_reorg*.cpp]] \ + "" "-fipa-struct-reorg -fdump-ipa-all -flto-partition=one -fwhole-program" +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/sr_*.c]] \ + "" "-fipa-struct-reorg -fdump-ipa-all -flto-partition=one -fwhole-program" +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/csr_*.c]] \ + "" "-fipa-struct-reorg -fdump-ipa-all -flto-partition=one -fwhole-program" + +# -fipa-reorder-fields +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/rf_*.c]] \ + "" "-fipa-reorder-fields -fdump-ipa-all -flto-partition=one -fwhole-program" # All done. torture-finish diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-1.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-1.c index 6565fe8dd63..23444fe8b0d 100644 --- a/gcc/testsuite/gcc.dg/struct/struct_reorg-1.c +++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-1.c @@ -1,5 +1,5 @@ // { dg-do compile } -// { dg-options "-O3 -flto-partition=one -fipa-struct-reorg -fdump-ipa-all" } +// { dg-options "-O3 -flto-partition=one -fipa-struct-reorg -fdump-ipa-all -fwhole-program" } struct a { @@ -21,4 +21,10 @@ int g(void) return b->t; } +int main() +{ + f (); + return g (); +} + /* { dg-final { scan-ipa-dump "No structures to transform." "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-3.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-3.c index 5864ad46fd3..2d1f95c9935 100644 --- a/gcc/testsuite/gcc.dg/struct/struct_reorg-3.c +++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-3.c @@ -1,5 +1,5 @@ // { dg-do compile } -// { dg-options "-O3 -flto-partition=one -fipa-struct-reorg -fdump-ipa-all" } +// { dg-options "-O3 -flto-partition=one -fipa-struct-reorg -fdump-ipa-all -fwhole-program" } #include typedef struct { @@ -10,7 +10,7 @@ typedef struct { compile_stack_elt_t *stack; unsigned size; } compile_stack_type; -void f (const char *p, const char *pend, int c) +__attribute__((noinline)) void f (const char *p, const char *pend, int c) { compile_stack_type compile_stack; while (p != pend) @@ -20,4 +20,9 @@ void f (const char *p, const char *pend, int c) * sizeof (compile_stack_elt_t)); } +int main() +{ + f (NULL, NULL, 1); +} + /* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" } } */ diff --git a/gcc/timevar.def b/gcc/timevar.def index 36611812611..50d542f4484 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -80,6 +80,7 @@ DEFTIMEVAR (TV_IPA_CONSTANT_PROP , "ipa cp") DEFTIMEVAR (TV_IPA_INLINING , "ipa inlining heuristics") DEFTIMEVAR (TV_IPA_FNSPLIT , "ipa function splitting") DEFTIMEVAR (TV_IPA_COMDATS , "ipa comdats") +DEFTIMEVAR (TV_IPA_REORDER_FIELDS , "ipa struct reorder fields optimization") DEFTIMEVAR (TV_IPA_STRUCT_REORG , "ipa struct reorg optimization") DEFTIMEVAR (TV_IPA_OPT , "ipa various optimizations") DEFTIMEVAR (TV_IPA_LTO_DECOMPRESS , "lto stream decompression") diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index ec7be874cad..8c3c2e0aa02 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -526,6 +526,7 @@ 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_reorder_fields (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_struct_reorg (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_pta (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_tm (gcc::context *ctxt); -- Gitee From dc3e80a863d05c2a11e6debb27755212510c53fb Mon Sep 17 00:00:00 2001 From: Mingchuan Wu Date: Tue, 1 Aug 2023 21:40:39 +0800 Subject: [PATCH 5/7] Add Dead Field Elimination in Struct-Reorg. We can transform gimple to eliminate fields that are never read and replace the dead fields in stmt by creating a new ssa. --- gcc/common.opt | 4 + gcc/ipa-struct-reorg/ipa-struct-reorg.cc | 240 +++++++++++++++++- gcc/ipa-struct-reorg/ipa-struct-reorg.h | 8 + gcc/opts.cc | 17 ++ gcc/testsuite/gcc.dg/struct/dfe_DTE_verify.c | 86 +++++++ .../gcc.dg/struct/dfe_ele_minus_verify.c | 60 +++++ .../gcc.dg/struct/dfe_extr_board_init.c | 77 ++++++ gcc/testsuite/gcc.dg/struct/dfe_extr_claw.c | 84 ++++++ gcc/testsuite/gcc.dg/struct/dfe_extr_dtrace.c | 56 ++++ gcc/testsuite/gcc.dg/struct/dfe_extr_gc.c | 162 ++++++++++++ gcc/testsuite/gcc.dg/struct/dfe_extr_hpsa.c | 126 +++++++++ .../gcc.dg/struct/dfe_extr_mv_udc_core.c | 82 ++++++ .../gcc.dg/struct/dfe_extr_tcp_usrreq.c | 58 +++++ .../gcc.dg/struct/dfe_extr_ui_main.c | 61 +++++ .../gcc.dg/struct/dfe_mem_ref_offset.c | 58 +++++ .../struct/dfe_mul_layer_ptr_record_bug.c | 30 +++ gcc/testsuite/gcc.dg/struct/dfe_ptr_diff.c | 71 ++++++ .../gcc.dg/struct/dfe_ptr_negate_expr.c | 55 ++++ gcc/testsuite/gcc.dg/struct/dfe_ptr_ptr.c | 55 ++++ gcc/testsuite/gcc.dg/struct/struct-reorg.exp | 4 + .../struct/wo_prof_escape_replace_type.c | 49 ++++ 21 files changed, 1436 insertions(+), 7 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/struct/dfe_DTE_verify.c create mode 100644 gcc/testsuite/gcc.dg/struct/dfe_ele_minus_verify.c create mode 100644 gcc/testsuite/gcc.dg/struct/dfe_extr_board_init.c create mode 100644 gcc/testsuite/gcc.dg/struct/dfe_extr_claw.c create mode 100644 gcc/testsuite/gcc.dg/struct/dfe_extr_dtrace.c create mode 100644 gcc/testsuite/gcc.dg/struct/dfe_extr_gc.c create mode 100644 gcc/testsuite/gcc.dg/struct/dfe_extr_hpsa.c create mode 100644 gcc/testsuite/gcc.dg/struct/dfe_extr_mv_udc_core.c create mode 100644 gcc/testsuite/gcc.dg/struct/dfe_extr_tcp_usrreq.c create mode 100644 gcc/testsuite/gcc.dg/struct/dfe_extr_ui_main.c create mode 100644 gcc/testsuite/gcc.dg/struct/dfe_mem_ref_offset.c create mode 100644 gcc/testsuite/gcc.dg/struct/dfe_mul_layer_ptr_record_bug.c create mode 100644 gcc/testsuite/gcc.dg/struct/dfe_ptr_diff.c create mode 100644 gcc/testsuite/gcc.dg/struct/dfe_ptr_negate_expr.c create mode 100644 gcc/testsuite/gcc.dg/struct/dfe_ptr_ptr.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_escape_replace_type.c diff --git a/gcc/common.opt b/gcc/common.opt index 81542708e23..963d7c8ce4c 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1957,6 +1957,10 @@ fipa-struct-reorg Common Var(flag_ipa_struct_reorg) Init(0) Optimization Perform structure layout optimizations. +fipa-struct-reorg= +Common RejectNegative Joined UInteger Var(struct_layout_optimize_level) Init(0) IntegerRange(0, 3) +-fipa-struct-reorg=[0,1,2,36] adding none, struct-reorg, reorder-fields, dfe optimizations. + fipa-vrp Common Var(flag_ipa_vrp) Optimization Perform IPA Value Range Propagation. diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc index d2c4c50c303..9eea5c34b26 100644 --- a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc @@ -87,6 +87,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-pretty-print.h" #include "gimple-pretty-print.h" #include "gimple-iterator.h" +#include "gimple-walk.h" #include "cfg.h" #include "ssa.h" #include "tree-dfa.h" @@ -268,10 +269,43 @@ enum srmode STRUCT_REORDER_FIELDS }; +/* Enum the struct layout optimize level, + which should be the same as the option -fstruct-reorg=. */ + +enum struct_layout_opt_level +{ + NONE = 0, + STRUCT_REORG, + STRUCT_REORDER_FIELDS_SLO, + DEAD_FIELD_ELIMINATION +}; + static bool is_result_of_mult (tree arg, tree *num, tree struct_size); static bool isptrptr (tree type); +void get_base (tree &base, tree expr); srmode current_mode; +hash_map replace_type_map; + +/* Return true if one of these types is created by struct-reorg. */ + +static bool +is_replace_type (tree type1, tree type2) +{ + if (replace_type_map.is_empty ()) + return false; + if (type1 == NULL_TREE || type2 == NULL_TREE) + return false; + tree *type_value = replace_type_map.get (type1); + if (type_value) + if (types_compatible_p (*type_value, type2)) + return true; + type_value = replace_type_map.get (type2); + if (type_value) + if (types_compatible_p (*type_value, type1)) + return true; + return false; +} } // anon namespace @@ -353,7 +387,8 @@ srfield::srfield (tree field, srtype *base) fielddecl (field), base (base), type (NULL), - clusternum (0) + clusternum (0), + field_access (EMPTY_FIELD) { for (int i = 0; i < max_split; i++) newfield[i] = NULL_TREE; @@ -392,6 +427,25 @@ srtype::srtype (tree type) } } +/* Check it if all fields in the RECORD_TYPE are referenced. */ + +bool +srtype::has_dead_field (void) +{ + bool may_dfe = false; + srfield *this_field; + unsigned i; + FOR_EACH_VEC_ELT (fields, i, this_field) + { + if (!(this_field->field_access & READ_FIELD)) + { + may_dfe = true; + break; + } + } + return may_dfe; +} + /* Mark the type as escaping type E at statement STMT. */ void @@ -595,7 +649,17 @@ srtype::analyze (void) into 2 different structures. In future we intend to add profile info and/or static heuristics to differentiate splitting process. */ if (fields.length () == 2) - fields[1]->clusternum = 1; + { + /* Currently, when the replacement structure type exists, + we only split the replacement structure. */ + for (hash_map::iterator it = replace_type_map.begin (); + it != replace_type_map.end (); ++it) + { + if (types_compatible_p ((*it).second, this->type)) + return; + } + fields[1]->clusternum = 1; + } /* Otherwise we do nothing. */ if (fields.length () >= 3) @@ -838,6 +902,10 @@ srtype::create_new_type (void) for (unsigned i = 0; i < fields.length (); i++) { srfield *f = fields[i]; + if (current_mode == STRUCT_REORDER_FIELDS + && struct_layout_optimize_level >= DEAD_FIELD_ELIMINATION + && !(f->field_access & READ_FIELD)) + continue; f->create_new_fields (newtype, newfields, newlast); } @@ -856,6 +924,16 @@ srtype::create_new_type (void) warn_padded = save_warn_padded; + if (current_mode == STRUCT_REORDER_FIELDS + && replace_type_map.get (this->newtype[0]) == NULL) + replace_type_map.put (this->newtype[0], this->type); + if (dump_file) + { + if (current_mode == STRUCT_REORDER_FIELDS + && struct_layout_optimize_level >= DEAD_FIELD_ELIMINATION + && has_dead_field ()) + fprintf (dump_file, "Dead field elimination.\n"); + } if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "Created %d types:\n", maxclusters); @@ -1270,6 +1348,7 @@ public: void maybe_mark_or_record_other_side (tree side, tree other, gimple *stmt); unsigned execute_struct_relayout (void); + bool remove_dead_field_stmt (tree lhs); }; struct ipa_struct_relayout @@ -3061,6 +3140,119 @@ ipa_struct_reorg::find_vars (gimple *stmt) } } +static HOST_WIDE_INT +get_offset (tree op, HOST_WIDE_INT offset) +{ + switch (TREE_CODE (op)) + { + case COMPONENT_REF: + { + return int_byte_position (TREE_OPERAND (op, 1)); + } + case MEM_REF: + { + return tree_to_uhwi (TREE_OPERAND (op, 1)); + } + default: + return offset; + } + return offset; +} + +/* Record field access. */ +static void +record_field_access (tree type, HOST_WIDE_INT offset, + unsigned access, void *data) +{ + srtype *this_srtype = ((ipa_struct_reorg *)data)->find_type (type); + if (this_srtype == NULL) + return; + srfield *this_srfield = this_srtype->find_field (offset); + if (this_srfield == NULL) + return; + + this_srfield->field_access |= access; + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "record field access %d:", access); + print_generic_expr (dump_file, type); + fprintf (dump_file, " field:"); + print_generic_expr (dump_file, this_srfield->fielddecl); + fprintf (dump_file, "\n"); + } + return; + +} + +/* Update field_access in srfield. */ + +static void +update_field_access (tree node, tree op, unsigned access, void *data) +{ + HOST_WIDE_INT offset = 0; + offset = get_offset (op, offset); + tree node_type = inner_type (TREE_TYPE (node)); + record_field_access (node_type, offset, access, data); + tree base = node; + get_base (base, node); + tree base_type = inner_type (TREE_TYPE (base)); + if (!types_compatible_p (base_type, node_type)) + { + record_field_access (base_type, get_offset (node, offset), + access, data); + } + return; +} + +/* A callback for walk_stmt_load_store_ops to visit store. */ + +static bool +find_field_p_store (gimple *stmt ATTRIBUTE_UNUSED, + tree node, tree op, void *data) +{ + update_field_access (node, op, WRITE_FIELD, data); + + return false; +} + +/* A callback for walk_stmt_load_store_ops to visit load. */ + +static bool +find_field_p_load (gimple *stmt ATTRIBUTE_UNUSED, + tree node, tree op, void *data) +{ + update_field_access (node, op, READ_FIELD, data); + + return false; +} + +/* Determine whether the stmt should be deleted. */ + +bool +ipa_struct_reorg::remove_dead_field_stmt (tree lhs) +{ + tree base = NULL_TREE; + bool indirect = false; + srtype *t = NULL; + srfield *f = NULL; + bool realpart = false; + bool imagpart = false; + bool address = false; + bool escape_from_base = false; + if (!get_type_field (lhs, base, indirect, t, f, realpart, imagpart, + address, escape_from_base)) + return false; + if (t ==NULL) + return false; + if (t->newtype[0] == t->type) + return false; + if (f == NULL) + return false; + if (f->newfield[0] == NULL) + return true; + return false; +} + /* Maybe record access of statement for further analaysis. */ void @@ -3082,6 +3274,13 @@ ipa_struct_reorg::maybe_record_stmt (cgraph_node *node, gimple *stmt) default: break; } + if (current_mode == STRUCT_REORDER_FIELDS + && struct_layout_optimize_level >= DEAD_FIELD_ELIMINATION) + { + /* Look for loads and stores. */ + walk_stmt_load_store_ops (stmt, this, find_field_p_load, + find_field_p_store); + } } /* Calculate the multiplier. */ @@ -3372,8 +3571,11 @@ ipa_struct_reorg::maybe_mark_or_record_other_side (tree side, tree other, } else if (type != d->type) { - type->mark_escape (escape_cast_another_ptr, stmt); - d->type->mark_escape (escape_cast_another_ptr, stmt); + if (!is_replace_type (d->type->type, type->type)) + { + type->mark_escape (escape_cast_another_ptr, stmt); + d->type->mark_escape (escape_cast_another_ptr, stmt); + } } /* x_1 = y.x_nodes; void *x; Directly mark the structure pointer type assigned @@ -3952,8 +4154,9 @@ ipa_struct_reorg::check_type_and_push (tree newdecl, srdecl *decl, } /* If we have a non void* or a decl (which is hard to track), then mark the type as escaping. */ - if (!VOID_POINTER_P (TREE_TYPE (newdecl)) - || DECL_P (newdecl)) + if (replace_type_map.get (type->type) == NULL + && (!VOID_POINTER_P (TREE_TYPE (newdecl)) + || DECL_P (newdecl))) { if (dump_file && (dump_flags & TDF_DETAILS)) { @@ -4218,7 +4421,9 @@ ipa_struct_reorg::check_other_side (srdecl *decl, tree other, gimple *stmt, } srtype *t1 = find_type (inner_type (t)); - if (t1 == type) + /* In the other side check, escape mark is added + when the replacement struct type exists. */ + if (t1 == type || is_replace_type (inner_type (t), type->type)) { /* In Complete Struct Relayout, if lhs type is the same as rhs type, we could return without any harm. */ @@ -5515,6 +5720,27 @@ bool ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) { bool remove = false; + + if (current_mode == STRUCT_REORDER_FIELDS + && struct_layout_optimize_level >= DEAD_FIELD_ELIMINATION + && remove_dead_field_stmt (gimple_assign_lhs (stmt))) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\n rewriting statement (remove): \n"); + print_gimple_stmt (dump_file, stmt, 0); + } + /* Replace the dead field in stmt by creating a dummy ssa. */ + tree dummy_ssa = make_ssa_name (TREE_TYPE (gimple_assign_lhs (stmt))); + gimple_assign_set_lhs (stmt, dummy_ssa); + update_stmt (stmt); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "To: \n"); + print_gimple_stmt (dump_file, stmt, 0); + } + } + if (gimple_clobber_p (stmt)) { tree lhs = gimple_assign_lhs (stmt); diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.h b/gcc/ipa-struct-reorg/ipa-struct-reorg.h index 6f85adeb4e1..719f7b3088c 100644 --- a/gcc/ipa-struct-reorg/ipa-struct-reorg.h +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.h @@ -143,6 +143,7 @@ public: bool create_new_type (void); void analyze (void); + bool has_dead_field (void); void mark_escape (escape_type, gimple *stmt); bool has_escaped (void) { @@ -164,6 +165,12 @@ public: } }; +/* Bitflags used for determining if a field + is never accessed, read or written. */ +const unsigned EMPTY_FIELD = 0x0u; +const unsigned READ_FIELD = 0x01u; +const unsigned WRITE_FIELD = 0x02u; + struct srfield { unsigned HOST_WIDE_INT offset; @@ -175,6 +182,7 @@ struct srfield unsigned clusternum; tree newfield[max_split]; + unsigned field_access; /* FIELD_DECL -> bitflag (use for dfe). */ // Constructors srfield (tree field, srtype *base); diff --git a/gcc/opts.cc b/gcc/opts.cc index a97630d1c9a..5a285c96b56 100644 --- a/gcc/opts.cc +++ b/gcc/opts.cc @@ -2947,6 +2947,23 @@ common_handle_option (struct gcc_options *opts, SET_OPTION_IF_UNSET (opts, opts_set, flag_profile_correction, value); break; + case OPT_fipa_struct_reorg_: + /* No break here - do -fipa-struct-reorg processing. */ + /* FALLTHRU. */ + case OPT_fipa_struct_reorg: + opts->x_flag_ipa_struct_reorg = value; + if (value && !opts->x_struct_layout_optimize_level) + { + /* Using the -fipa-struct-reorg option is equivalent to using + -fipa-struct-reorg=1. */ + opts->x_struct_layout_optimize_level = 1; + } + break; + + case OPT_fipa_reorder_fields: + SET_OPTION_IF_UNSET (opts, opts_set, flag_ipa_struct_reorg, value); + break; + case OPT_fprofile_generate_: opts->x_profile_data_prefix = xstrdup (arg); value = true; diff --git a/gcc/testsuite/gcc.dg/struct/dfe_DTE_verify.c b/gcc/testsuite/gcc.dg/struct/dfe_DTE_verify.c new file mode 100644 index 00000000000..bd319253e0b --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfe_DTE_verify.c @@ -0,0 +1,86 @@ +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +typedef struct network +{ + arc_p arcs; + arc_p sorted_arcs; + int x; + node_p nodes; + node_p stop_nodes; +} network_t; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; + network_t* net_add; +}; + + +const int MAX = 100; + +/* let it escape_array, "Type is used in an array [not handled yet]". */ +network_t* net[2]; +arc_p stop_arcs = NULL; + +int +main () +{ + net[0] = (network_t*) calloc (1, sizeof(network_t)); + net[0]->arcs = (arc_p) calloc (MAX, sizeof (arc_t)); + stop_arcs = (arc_p) calloc (MAX, sizeof (arc_t)); + + net[0]->arcs->id = 100; + + for (unsigned i = 0; i < 3; i++) + { + net[0]->arcs->id = net[0]->arcs->id + 2; + stop_arcs->cost = net[0]->arcs->id / 2; + stop_arcs->net_add = net[0]; + printf("stop_arcs->cost = %ld\n", stop_arcs->cost); + net[0]->arcs++; + stop_arcs++; + } + + if( net[1] != 0 && stop_arcs != 0) + { + return -1; + } + return 0; +} + +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "reorder_fields" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_ele_minus_verify.c b/gcc/testsuite/gcc.dg/struct/dfe_ele_minus_verify.c new file mode 100644 index 00000000000..78b3881bdc6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfe_ele_minus_verify.c @@ -0,0 +1,60 @@ +// verify newarc[cmp-1].flow +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +const int MAX = 100; +arc_p ap = NULL; + +int +main () +{ + ap = (arc_p) calloc(MAX, sizeof(arc_t)); + printf("%d\n", ap[0].id); + for (int i = 1; i < MAX; i++) + { + ap[i-1].id = 500; + } + printf("%d\n", ap[0].id); + return 0; +} + +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "reorder_fields" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_extr_board_init.c b/gcc/testsuite/gcc.dg/struct/dfe_extr_board_init.c new file mode 100644 index 00000000000..7723c240ba7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfe_extr_board_init.c @@ -0,0 +1,77 @@ +/* { dg-do compile} */ + +#define NULL ((void*)0) +typedef unsigned long size_t; +typedef long intptr_t; +typedef unsigned long uintptr_t; +typedef long scalar_t__; +typedef int bool; +#define false 0 +#define true 1 + +typedef struct TYPE_5__ TYPE_2__; +typedef struct TYPE_4__ TYPE_1__; + +struct TYPE_4__ +{ + int Pin; + int Pull; + int Mode; + int Speed; +}; + +struct TYPE_5__ +{ + int MEMRMP; +}; +typedef TYPE_1__ GPIO_InitTypeDef; + +int BT_RST_PIN; +int BT_RST_PORT; +int CONN_POS10_PIN; +int CONN_POS10_PORT; +int GPIO_HIGH (int, int); +int GPIO_MODE_INPUT; +int GPIO_MODE_OUTPUT_PP; +int GPIO_NOPULL; +int GPIO_PULLUP; +int GPIO_SPEED_FREQ_LOW; +int HAL_GPIO_Init (int, TYPE_1__ *); +scalar_t__ IS_GPIO_RESET (int, int); +TYPE_2__ *SYSCFG; +int __HAL_RCC_GPIOB_CLK_ENABLE (); +int __HAL_RCC_GPIOC_CLK_ENABLE (); + +__attribute__((used)) static void +LBF_DFU_If_Needed (void) +{ + GPIO_InitTypeDef GPIO_InitStruct; + __HAL_RCC_GPIOC_CLK_ENABLE (); + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Pin = BT_RST_PIN; + HAL_GPIO_Init (BT_RST_PORT, &GPIO_InitStruct); + + GPIO_HIGH (BT_RST_PORT, BT_RST_PIN); + __HAL_RCC_GPIOB_CLK_ENABLE (); + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_PULLUP; + GPIO_InitStruct.Pin = CONN_POS10_PIN; + HAL_GPIO_Init (CONN_POS10_PORT, &GPIO_InitStruct); + + if (IS_GPIO_RESET (CONN_POS10_PORT, CONN_POS10_PIN)) + { + SYSCFG->MEMRMP = 0x00000001; + asm ( + "LDR R0, =0x000000\n\t" + "LDR SP, [R0, #0]\n\t" + ); + asm ( + "LDR R0, [R0, #0]\n\t" + "BX R0\n\t" + ); + } +} + +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 0 "reorder_fields" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_extr_claw.c b/gcc/testsuite/gcc.dg/struct/dfe_extr_claw.c new file mode 100644 index 00000000000..a1feac96606 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfe_extr_claw.c @@ -0,0 +1,84 @@ +/* { dg-do compile} */ + +#define NULL ((void*)0) +typedef unsigned long size_t; +typedef long intptr_t; +typedef unsigned long uintptr_t; +typedef long scalar_t__; +typedef int bool; +#define false 0 +#define true 1 + +typedef struct TYPE_2__ TYPE_1__; + +struct net_device +{ + struct claw_privbk* ml_priv; +}; +struct clawctl +{ + int linkid; +}; +struct claw_privbk +{ + int system_validate_comp; + TYPE_1__* p_env; + int ctl_bk; +}; +typedef int __u8; +struct TYPE_2__ +{ + scalar_t__ packing; + int api_type; +}; + +int CLAW_DBF_TEXT (int, int, char*); +int CONNECTION_REQUEST; +int HOST_APPL_NAME; +scalar_t__ PACKING_ASK; +scalar_t__ PACK_SEND; +int WS_APPL_NAME_IP_NAME; +int WS_APPL_NAME_PACKED; +int claw_send_control (struct net_device*, int, int, int, int, int, int); +int setup; + +__attribute__((noinline)) int +claw_send_control (struct net_device* net, int a, int b, int c, int d, int e, + int f) +{ + return net->ml_priv->system_validate_comp + a + b + c + d + f; +} + +__attribute__((used)) static int +claw_snd_conn_req (struct net_device *dev, __u8 link) +{ + int rc; + struct claw_privbk *privptr = dev->ml_priv; + struct clawctl *p_ctl; + CLAW_DBF_TEXT (2, setup, "snd_conn"); + rc = 1; + p_ctl = (struct clawctl *)&privptr->ctl_bk; + p_ctl->linkid = link; + if (privptr->system_validate_comp == 0x00) + { + return rc; + } + if (privptr->p_env->packing == PACKING_ASK) + { + rc = claw_send_control (dev, CONNECTION_REQUEST, 0, 0, 0, + WS_APPL_NAME_PACKED, WS_APPL_NAME_PACKED); + } + if (privptr->p_env->packing == PACK_SEND) + { + rc = claw_send_control (dev, CONNECTION_REQUEST, 0, 0, 0, + WS_APPL_NAME_IP_NAME, WS_APPL_NAME_IP_NAME); + } + if (privptr->p_env->packing == 0) + { + rc = claw_send_control (dev, CONNECTION_REQUEST, 0, 0, 0, + HOST_APPL_NAME, privptr->p_env->api_type); + } + return rc; +} + +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 1 "reorder_fields" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_extr_dtrace.c b/gcc/testsuite/gcc.dg/struct/dfe_extr_dtrace.c new file mode 100644 index 00000000000..fd1e936ca62 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfe_extr_dtrace.c @@ -0,0 +1,56 @@ +/* { dg-do compile} */ + +#define NULL ((void*)0) +typedef unsigned long size_t; +typedef long intptr_t; +typedef unsigned long uintptr_t; +typedef long scalar_t__; +typedef int bool; +#define false 0 +#define true 1 + +typedef struct TYPE_4__ TYPE_2__; +typedef struct TYPE_3__ TYPE_1__; + +typedef int uint8_t; +typedef int uint16_t; + +struct TYPE_4__ +{ + size_t cpu_id; +}; + +struct TYPE_3__ +{ + int cpuc_dtrace_flags; +}; + +TYPE_2__ *CPU; +volatile int CPU_DTRACE_FAULT; +TYPE_1__ *cpu_core; +scalar_t__ dtrace_load8 (uintptr_t); + +__attribute__((used)) static int +dtrace_bcmp (const void *s1, const void *s2, size_t len) +{ + volatile uint16_t *flags; + flags = (volatile uint16_t *)&cpu_core[CPU->cpu_id].cpuc_dtrace_flags; + if (s1 == s2) + return (0); + if (s1 == NULL || s2 == NULL) + return (1); + if (s1 != s2 && len != 0) + { + const uint8_t *ps1 = s1; + const uint8_t *ps2 = s2; + do + { + if (dtrace_load8 ((uintptr_t)ps1++) != *ps2++) + return (1); + } + while (--len != 0 && !(*flags & CPU_DTRACE_FAULT)); + } + return (0); +} + +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 0 "reorder_fields" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_extr_gc.c b/gcc/testsuite/gcc.dg/struct/dfe_extr_gc.c new file mode 100644 index 00000000000..b13d785a9dd --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfe_extr_gc.c @@ -0,0 +1,162 @@ +/* { dg-do compile} */ + +#define NULL ((void*)0) +typedef unsigned long size_t; +typedef long intptr_t; +typedef unsigned long uintptr_t; +typedef long scalar_t__; +typedef int bool; +#define false 0 +#define true 1 + +struct mrb_context +{ + size_t stack; + size_t stbase; + size_t stend; + size_t eidx; + int *ci; + int *cibase; + int status; +}; + +struct RObject +{ + int dummy; +}; + +struct RHash +{ + int dummy; +}; + +struct RFiber +{ + struct mrb_context *cxt; +}; + +struct RClass +{ + int dummy; +}; + +struct RBasic +{ + int tt; +}; + +struct RArray +{ + int dummy; +}; + +typedef int mrb_state; +typedef int mrb_gc; +typedef int mrb_callinfo; +size_t ARY_LEN (struct RArray *); +size_t MRB_ENV_STACK_LEN (struct RBasic *); +int MRB_FIBER_TERMINATED; + +#define MRB_TT_ARRAY 140 +#define MRB_TT_CLASS 139 +#define MRB_TT_DATA 138 +#define MRB_TT_ENV 137 +#define MRB_TT_EXCEPTION 136 +#define MRB_TT_FIBER 135 +#define MRB_TT_HASH 134 +#define MRB_TT_ICLASS 133 +#define MRB_TT_MODULE 132 +#define MRB_TT_OBJECT 131 +#define MRB_TT_PROC 130 +#define MRB_TT_RANGE 129 +#define MRB_TT_SCLASS 128 + +size_t ci_nregs (int *); +int gc_mark_children (int *, int *, struct RBasic *); +size_t mrb_gc_mark_hash_size (int *, struct RHash *); +size_t mrb_gc_mark_iv_size (int *, struct RObject *); +size_t mrb_gc_mark_mt_size (int *, struct RClass *); + +__attribute__((used)) static size_t +gc_gray_mark (mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) +{ + size_t children = 0; + gc_mark_children (mrb, gc, obj); + switch (obj->tt) + { + case MRB_TT_ICLASS: + children++; + break; + + case MRB_TT_CLASS: + case MRB_TT_SCLASS: + case MRB_TT_MODULE: + { + struct RClass *c = (struct RClass *)obj; + children += mrb_gc_mark_iv_size (mrb, (struct RObject *)obj); + children += mrb_gc_mark_mt_size (mrb, c); + children ++; + } + break; + + case MRB_TT_OBJECT: + case MRB_TT_DATA: + case MRB_TT_EXCEPTION: + children += mrb_gc_mark_iv_size (mrb, (struct RObject *)obj); + break; + + case MRB_TT_ENV: + children += MRB_ENV_STACK_LEN (obj); + break; + + case MRB_TT_FIBER: + { + struct mrb_context *c = ((struct RFiber *)obj)->cxt; + size_t i; + mrb_callinfo *ci; + if (!c || c->status == MRB_FIBER_TERMINATED) + break; + + i = c->stack - c->stbase; + if (c->ci) + { + i += ci_nregs (c->ci); + } + if (c->stbase + i > c->stend) + i = c->stend - c->stbase; + + children += i; + children += c->eidx; + if (c->cibase) + { + for (i = 0, ci = c->cibase; ci <= c->ci; i++, ci++) + ; + } + children += i; + } + break; + + case MRB_TT_ARRAY: + { + struct RArray *a = (struct RArray *)obj; + children += ARY_LEN (a); + } + break; + + case MRB_TT_HASH: + children += mrb_gc_mark_iv_size (mrb, (struct RObject *)obj); + children += mrb_gc_mark_hash_size (mrb, (struct RHash *)obj); + break; + + case MRB_TT_PROC: + case MRB_TT_RANGE: + children += 2; + break; + default: + break; + } + + return children; +} + +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 0 "reorder_fields" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_extr_hpsa.c b/gcc/testsuite/gcc.dg/struct/dfe_extr_hpsa.c new file mode 100644 index 00000000000..bc28a658a36 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfe_extr_hpsa.c @@ -0,0 +1,126 @@ +/* { dg-do compile} */ + +#define NULL ((void*)0) +typedef unsigned long size_t; +typedef long intptr_t; +typedef unsigned long uintptr_t; +typedef long scalar_t__; +typedef int bool; +#define false 0 +#define true 1 + +typedef struct TYPE_6__ TYPE_3__; +typedef struct TYPE_5__ TYPE_2__; +typedef struct TYPE_4__ TYPE_1__; + +struct io_accel2_cmd +{ + int dummy; +}; + +struct hpsa_tmf_struct +{ + int it_nexus; +}; + +struct hpsa_scsi_dev_t +{ + int nphysical_disks; + int ioaccel_handle; + struct hpsa_scsi_dev_t **phys_disk; +}; + +struct ctlr_info +{ + TYPE_3__ *pdev; + struct io_accel2_cmd *ioaccel2_cmd_pool; +}; +struct TYPE_4__ +{ + int LunAddrBytes; +}; + +struct TYPE_5__ +{ + TYPE_1__ LUN; +}; + +struct CommandList +{ + size_t cmdindex; + int cmd_type; + struct hpsa_scsi_dev_t *phys_disk; + TYPE_2__ Header; +}; + +struct TYPE_6__ +{ + int dev; +}; + +int BUG (); +#define CMD_IOACCEL1 132 +#define CMD_IOACCEL2 131 +#define CMD_IOCTL_PEND 130 +#define CMD_SCSI 129 +#define IOACCEL2_TMF 128 +int dev_err (int *, char *, int); +scalar_t__ hpsa_is_cmd_idle (struct CommandList *); +int le32_to_cpu (int); +int test_memcmp (unsigned char *, int *, int); + +__attribute__((used)) static bool +hpsa_cmd_dev_match (struct ctlr_info *h, struct CommandList *c, + struct hpsa_scsi_dev_t *dev, unsigned char *scsi3addr) +{ + int i; + bool match = false; + struct io_accel2_cmd * c2 = &h->ioaccel2_cmd_pool[c->cmdindex]; + struct hpsa_tmf_struct *ac = (struct hpsa_tmf_struct *)c2; + + if (hpsa_is_cmd_idle (c)) + return false; + + switch (c->cmd_type) + { + case CMD_SCSI: + case CMD_IOCTL_PEND: + match = !test_memcmp (scsi3addr, &c->Header.LUN.LunAddrBytes, + sizeof (c->Header.LUN.LunAddrBytes)); + break; + + case CMD_IOACCEL1: + case CMD_IOACCEL2: + if (c->phys_disk == dev) + { + match = true; + } + else + { + for (i = 0; i < dev->nphysical_disks && !match; i++) + { + match = dev->phys_disk[i] == c->phys_disk; + } + } + break; + + case IOACCEL2_TMF: + for (i = 0; i < dev->nphysical_disks && !match; i++) + { + match = dev->phys_disk[i]->ioaccel_handle == + le32_to_cpu (ac->it_nexus); + } + break; + + case 0: + match = false; + break; + default: + dev_err (&h->pdev->dev, "unexpected cmd_type: %d\n", c->cmd_type); + BUG (); + } + + return match; +} + +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 0 "reorder_fields" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_extr_mv_udc_core.c b/gcc/testsuite/gcc.dg/struct/dfe_extr_mv_udc_core.c new file mode 100644 index 00000000000..0a585ac3d5c --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfe_extr_mv_udc_core.c @@ -0,0 +1,82 @@ +/* { dg-do compile} */ + +#define NULL ((void*)0) +typedef unsigned long size_t; +typedef long intptr_t; +typedef unsigned long uintptr_t; +typedef long scalar_t__; +typedef int bool; +#define false 0 +#define true 1 + +typedef struct TYPE_4__ TYPE_2__; +typedef struct TYPE_3__ TYPE_1__; +typedef int u32; + +struct mv_udc +{ + TYPE_2__ *op_regs; + TYPE_1__ *ep_dqh; + struct mv_ep *eps; +}; + +struct mv_ep +{ + TYPE_1__ *dqh; + struct mv_udc *udc; +}; + +struct TYPE_4__ +{ + int *epctrlx; +}; + +struct TYPE_3__ +{ + int max_packet_length; + int next_dtd_ptr; +}; + +int EP0_MAX_PKT_SIZE; +int EPCTRL_RX_ENABLE; +int EPCTRL_RX_EP_TYPE_SHIFT; +int EPCTRL_TX_ENABLE; +int EPCTRL_TX_EP_TYPE_SHIFT; +int EP_QUEUE_HEAD_IOS; +int EP_QUEUE_HEAD_MAX_PKT_LEN_POS; +int EP_QUEUE_HEAD_NEXT_TERMINATE; +int USB_ENDPOINT_XFER_CONTROL; +int readl (int *); +int writel (int, int *); + +__attribute__((used)) static void +ep0_reset (struct mv_udc *udc) +{ + struct mv_ep *ep; + u32 epctrlx; + int i = 0; + for (i = 0; i < 2; i++) + { + ep = &udc->eps[i]; + ep->udc = udc; + ep->dqh = &udc->ep_dqh[i]; + ep->dqh->max_packet_length = + (EP0_MAX_PKT_SIZE << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) + | EP_QUEUE_HEAD_IOS; + ep->dqh->next_dtd_ptr = EP_QUEUE_HEAD_NEXT_TERMINATE; + epctrlx = readl (&udc->op_regs->epctrlx[0]); + if (i) + { + epctrlx |= EPCTRL_TX_ENABLE + | (USB_ENDPOINT_XFER_CONTROL << EPCTRL_TX_EP_TYPE_SHIFT); + } + else + { + epctrlx |= EPCTRL_RX_ENABLE + | (USB_ENDPOINT_XFER_CONTROL << EPCTRL_RX_EP_TYPE_SHIFT); + } + writel (epctrlx, &udc->op_regs->epctrlx[0]); + } +} + +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "reorder_fields" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_extr_tcp_usrreq.c b/gcc/testsuite/gcc.dg/struct/dfe_extr_tcp_usrreq.c new file mode 100644 index 00000000000..bddd862fec1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfe_extr_tcp_usrreq.c @@ -0,0 +1,58 @@ +/* { dg-do compile} */ + +#define NULL ((void*)0) +typedef unsigned long size_t; +typedef long intptr_t; +typedef unsigned long uintptr_t; +typedef long scalar_t__; +typedef int bool; +#define false 0 +#define true 1 + +struct tcpcb +{ + int t_state; +}; + +struct socket +{ + int dummy; +}; + +struct proc +{ + int dummy; +}; + +struct inpcb +{ + scalar_t__ inp_lport; +}; + +int COMMON_END (int); +int COMMON_START (); +int PRU_LISTEN; +int TCPS_LISTEN; +int in_pcbbind (struct inpcb *, int *, struct proc *); +struct inpcb* sotoinpcb (struct socket *); + +__attribute__((used)) static void +tcp_usr_listen (struct socket *so, struct proc *p) +{ + int error = 0; + struct inpcb *inp = sotoinpcb (so); + struct tcpcb *tp; + + COMMON_START (); + if (inp->inp_lport == 0) + { + error = in_pcbbind (inp, NULL, p); + } + if (error == 0) + { + tp->t_state = TCPS_LISTEN; + } + COMMON_END (PRU_LISTEN); +} + +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 1 "reorder_fields" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_extr_ui_main.c b/gcc/testsuite/gcc.dg/struct/dfe_extr_ui_main.c new file mode 100644 index 00000000000..1a06f5eec60 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfe_extr_ui_main.c @@ -0,0 +1,61 @@ +/* { dg-do compile} */ + +#define NULL ((void*)0) +typedef unsigned long size_t; +typedef long intptr_t; +typedef unsigned long uintptr_t; +typedef long scalar_t__; +typedef int bool; +#define false 0 +#define true 1 + +typedef struct TYPE_4__ TYPE_2__; +typedef struct TYPE_3__ TYPE_1__; + +struct TYPE_4__ +{ + size_t modCount; + TYPE_1__ *modList; +}; + +struct TYPE_3__ +{ + void *modDescr; + void *modName; +}; + +size_t MAX_MODS; +void *String_Alloc (char *); +int test_strlen (char *); +int trap_FD_GetFileList (char *, char *, char *, int); +TYPE_2__ uiInfo; + +__attribute__((used)) static void +UI_LoadMods () +{ + int numdirs; + char dirlist[2048]; + char *dirptr; + char *descptr; + int i; + int dirlen; + + uiInfo.modCount = 0; + numdirs = trap_FD_GetFileList ("$modelist", "", dirlist, sizeof (dirlist)); + dirptr = dirlist; + for (i = 0; i < numdirs; i++) + { + dirlen = test_strlen (dirptr) + 1; + descptr = dirptr + dirlen; + uiInfo.modList[uiInfo.modCount].modName = String_Alloc (dirptr); + uiInfo.modList[uiInfo.modCount].modDescr = String_Alloc (descptr); + dirptr += dirlen + test_strlen (descptr) + 1; + uiInfo.modCount++; + if (uiInfo.modCount >= MAX_MODS) + { + break; + } + } +} + +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 1 "reorder_fields" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_mem_ref_offset.c b/gcc/testsuite/gcc.dg/struct/dfe_mem_ref_offset.c new file mode 100644 index 00000000000..670e2919ac7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfe_mem_ref_offset.c @@ -0,0 +1,58 @@ +/* Supports the MEM_REF offset. + _1 = MEM[(struct arc *)ap_4 + 72B].flow; + Old rewrite:_1 = ap.reorder.0_8->flow; + New rewrite:_1 = MEM[(struct arc.reorder.0 *)ap.reorder.0_8 + 64B].flow. */ +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +int +main () +{ + const int MAX = 100; + /* A similar scenario can be reproduced only by using local variables. */ + arc_p ap = NULL; + ap = (arc_p) calloc(MAX, sizeof(arc_t)); + printf("%d\n", ap[1].flow); + return 0; +} + +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "reorder_fields" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_mul_layer_ptr_record_bug.c b/gcc/testsuite/gcc.dg/struct/dfe_mul_layer_ptr_record_bug.c new file mode 100644 index 00000000000..423435a139e --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfe_mul_layer_ptr_record_bug.c @@ -0,0 +1,30 @@ +/* { dg-do compile } */ + +#include +#include + +typedef struct T_HASH_ENTRY +{ + unsigned int hash; + unsigned int klen; + char *key; +} iHashEntry; + +typedef struct T_HASH +{ + unsigned int size; + unsigned int fill; + unsigned int keys; + + iHashEntry **array; +} uHash; + +uHash *retval; + +int +main() { + retval->array = (iHashEntry **)calloc(sizeof(iHashEntry *), retval->size); + return 0; +} + +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "reorder_fields" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_ptr_diff.c b/gcc/testsuite/gcc.dg/struct/dfe_ptr_diff.c new file mode 100644 index 00000000000..4d970161042 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfe_ptr_diff.c @@ -0,0 +1,71 @@ +// support POINTER_DIFF_EXPR & NOP_EXPR to avoid +// escape_unhandled_rewrite, "Type escapes via a unhandled rewrite stmt" +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +typedef struct network +{ + arc_p arcs; + arc_p sorted_arcs; + int x; + node_p nodes; + node_p stop_nodes; +} network_t; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +int +main () +{ + arc_t *old_arcs; + node_t *node; + node_t *stop; + size_t off; + network_t* net; + + for( ; node->number < stop->number; node++ ) + { + off = node->basic_arc - old_arcs; + node->basic_arc = (arc_t *)(net->arcs + off); + } + return 0; +} + +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 3 "reorder_fields" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_ptr_negate_expr.c b/gcc/testsuite/gcc.dg/struct/dfe_ptr_negate_expr.c new file mode 100644 index 00000000000..881a9befb88 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfe_ptr_negate_expr.c @@ -0,0 +1,55 @@ +// support NEGATE_EXPR rewriting +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +int +main () +{ + int64_t susp = 0; + const int MAX = 100; + arc_p ap = (arc_p) calloc(MAX, sizeof(arc_t)); + ap -= susp; + printf("%d\n", ap[1].flow); + return 0; +} + +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "reorder_fields" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_ptr_ptr.c b/gcc/testsuite/gcc.dg/struct/dfe_ptr_ptr.c new file mode 100644 index 00000000000..b83e270f38a --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfe_ptr_ptr.c @@ -0,0 +1,55 @@ +// release escape_ptr_ptr, "Type is used in a pointer to a pointer [not handled yet]"; +/* { dg-do compile } */ + +#include +#include + +typedef struct node node_t; +typedef struct node *node_p; + +typedef struct arc arc_t; +typedef struct arc *arc_p; + +struct node +{ + int64_t potential; + int orientation; + node_p child; + node_p pred; + node_p sibling; + node_p sibling_prev; + arc_p basic_arc; + arc_p firstout; + arc_p firstin; + arc_p arc_tmp; + int64_t flow; + int64_t depth; + int number; + int time; +}; + +struct arc +{ + int id; + int64_t cost; + node_p tail; + node_p head; + short ident; + arc_p nextout; + arc_p nextin; + int64_t flow; + int64_t org_cost; +}; + +const int MAX = 100; +arc_t **ap = NULL; + +int +main () +{ + ap = (arc_t**) malloc(MAX * sizeof(arc_t*)); + (*ap)[0].id = 300; + return 0; +} + +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "reorder_fields" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/struct-reorg.exp b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp index 5a476e8f91b..6ccb753b5ae 100644 --- a/gcc/testsuite/gcc.dg/struct/struct-reorg.exp +++ b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp @@ -43,6 +43,10 @@ gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/csr_*.c]] \ gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/rf_*.c]] \ "" "-fipa-reorder-fields -fdump-ipa-all -flto-partition=one -fwhole-program" +# -fipa-struct-reorg=3 +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/dfe*.c]] \ + "" "-fipa-reorder-fields -fipa-struct-reorg=3 -fdump-ipa-all -flto-partition=one -fwhole-program" + # All done. torture-finish dg-finish diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_escape_replace_type.c b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_replace_type.c new file mode 100644 index 00000000000..fa8c66b9e92 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_replace_type.c @@ -0,0 +1,49 @@ +/* { dg-do compile } */ + +#include + +struct AngleDef +{ + double K; + double th0; +}; +typedef struct AngleDef angldef; + +struct bndangdihe +{ + int nbond; + int nangl; + int ndihe; +}; +typedef struct bndangdihe bah; + +struct ambprmtop +{ + double *AnglK; + double *AnglEq; + bah nBAH; + angldef *AParam; + char source[512]; + char eprulesource[512]; +}; +typedef struct ambprmtop prmtop; + +static void OrderBondParameters (prmtop *tp) +{ + int i; + tp->AParam = (angldef *)malloc (tp->nBAH.nangl * sizeof (angldef)); + for (i = 0; i < tp->nBAH.nangl; i++) + { + tp->AParam[i].K = tp->AnglK[i]; + tp->AParam[i].th0 = tp->AnglEq[i]; + } +} + +void main () +{ + prmtop *tp = (prmtop *)malloc (100 * sizeof (prmtop)); + OrderBondParameters (tp); +} + +/*---------------------------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "No structures to transform" "struct_reorg" } } */ -- Gitee From bde7cdeb357a747895e2b4df0dcbecf35855e684 Mon Sep 17 00:00:00 2001 From: liyancheng <412998149@qq.com> Date: Tue, 15 Aug 2023 22:24:46 +0800 Subject: [PATCH 6/7] [Struct Reorg] Refactoring and merge reorder fields to struct reorg optimization Refactor the reorder_fields optimization into struct layout optimization. Add flag -fipa-struct-reorg=[0,1,2,3] to enable none, strcut reorg, reorder fields and dfe optimizations. --- gcc/ipa-free-lang-data.cc | 9 +- gcc/ipa-struct-reorg/ipa-struct-reorg.cc | 353 ++++++++---------- gcc/passes.def | 1 - gcc/symbol-summary.h | 4 +- gcc/testsuite/gcc.dg/struct/dfe_DTE_verify.c | 2 +- .../gcc.dg/struct/dfe_ele_minus_verify.c | 2 +- .../gcc.dg/struct/dfe_extr_board_init.c | 2 +- gcc/testsuite/gcc.dg/struct/dfe_extr_claw.c | 2 +- gcc/testsuite/gcc.dg/struct/dfe_extr_dtrace.c | 2 +- gcc/testsuite/gcc.dg/struct/dfe_extr_gc.c | 2 +- gcc/testsuite/gcc.dg/struct/dfe_extr_hpsa.c | 2 +- .../gcc.dg/struct/dfe_extr_mv_udc_core.c | 2 +- .../gcc.dg/struct/dfe_extr_tcp_usrreq.c | 2 +- .../gcc.dg/struct/dfe_extr_ui_main.c | 2 +- .../gcc.dg/struct/dfe_mem_ref_offset.c | 2 +- .../struct/dfe_mul_layer_ptr_record_bug.c | 2 +- gcc/testsuite/gcc.dg/struct/dfe_ptr_diff.c | 2 +- .../gcc.dg/struct/dfe_ptr_negate_expr.c | 2 +- gcc/testsuite/gcc.dg/struct/dfe_ptr_ptr.c | 2 +- .../struct/rf_DTE_struct_instance_field.c | 2 +- gcc/testsuite/gcc.dg/struct/rf_DTE_verify.c | 2 +- .../gcc.dg/struct/rf_check_ptr_layers_bug.c | 2 +- .../gcc.dg/struct/rf_create_fields_bug.c | 2 +- .../gcc.dg/struct/rf_create_new_func_bug.c | 2 +- .../gcc.dg/struct/rf_ele_minus_verify.c | 2 +- .../gcc.dg/struct/rf_escape_by_base.c | 2 +- .../gcc.dg/struct/rf_external_func_types.c | 2 +- gcc/testsuite/gcc.dg/struct/rf_int_cast_ptr.c | 2 +- .../gcc.dg/struct/rf_mem_ref_offset.c | 2 +- .../struct/rf_mul_layer_ptr_record_bug.c | 2 +- .../gcc.dg/struct/rf_pass_conflict.c | 2 +- gcc/testsuite/gcc.dg/struct/rf_ptr2void_lto.c | 2 +- gcc/testsuite/gcc.dg/struct/rf_ptr_diff.c | 2 +- .../gcc.dg/struct/rf_ptr_negate_expr.c | 2 +- gcc/testsuite/gcc.dg/struct/rf_ptr_offset.c | 2 +- gcc/testsuite/gcc.dg/struct/rf_ptr_ptr.c | 2 +- gcc/testsuite/gcc.dg/struct/rf_ptr_ptr_ptr.c | 2 +- .../gcc.dg/struct/rf_rescusive_type.c | 2 +- .../struct/rf_rewrite_assign_more_cmp.c | 2 +- .../gcc.dg/struct/rf_rewrite_cond_bug.c | 2 +- .../gcc.dg/struct/rf_rewrite_cond_more_cmp.c | 2 +- .../gcc.dg/struct/rf_rewrite_phi_bug.c | 2 +- gcc/testsuite/gcc.dg/struct/rf_visible_func.c | 2 +- .../gcc.dg/struct/rf_void_ptr_param_func.c | 2 +- gcc/testsuite/gcc.dg/struct/struct-reorg.exp | 2 +- gcc/timevar.def | 1 - gcc/tree-pass.h | 1 - 47 files changed, 200 insertions(+), 251 deletions(-) diff --git a/gcc/ipa-free-lang-data.cc b/gcc/ipa-free-lang-data.cc index 74965c20ab3..801e95ceaeb 100644 --- a/gcc/ipa-free-lang-data.cc +++ b/gcc/ipa-free-lang-data.cc @@ -49,6 +49,9 @@ #include "except.h" #include "ipa-utils.h" +/* Check whether in C language or LTO with only C language. */ +extern bool lang_c_p (void); + namespace { /* Data used when collecting DECLs and TYPEs for language data removal. */ @@ -105,7 +108,8 @@ fld_simplified_type_name (tree type) /* Simplify type will cause that struct A and struct A within struct B are different type pointers, so skip it in structure optimizations. */ - if (flag_ipa_struct_reorg || flag_ipa_reorder_fields) + if (flag_ipa_struct_reorg && lang_c_p () + && flag_lto_partition == LTO_PARTITION_ONE) return TYPE_NAME (type); if (!TYPE_NAME (type) || TREE_CODE (TYPE_NAME (type)) != TYPE_DECL) @@ -349,7 +353,8 @@ fld_simplified_type (tree t, class free_lang_data_d *fld) /* Simplify type will cause that struct A and struct A within struct B are different type pointers, so skip it in structure optimizations. */ - if (flag_ipa_struct_reorg) + if (flag_ipa_struct_reorg && lang_c_p () + && flag_lto_partition == LTO_PARTITION_ONE) return t; if (POINTER_TYPE_P (t)) return fld_incomplete_type_of (t, fld); diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc index 9eea5c34b26..25f3d584c91 100644 --- a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc @@ -108,6 +108,37 @@ along with GCC; see the file COPYING3. If not see #include "cfgloop.h" #include "langhooks.h" +/* Check whether in C language or LTO with only C language. */ + +bool +lang_c_p (void) +{ + const char *language_string = lang_hooks.name; + + if (!language_string) + return false; + + if (lang_GNU_C ()) + return true; + else if (strcmp (language_string, "GNU GIMPLE") == 0) // for LTO check + { + unsigned i = 0; + tree t = NULL_TREE; + + FOR_EACH_VEC_SAFE_ELT (all_translation_units, i, t) + { + language_string = TRANSLATION_UNIT_LANGUAGE (t); + if (language_string == NULL + || strncmp (language_string, "GNU C", 5) + || (language_string[5] != '\0' + && !(ISDIGIT (language_string[5])))) + return false; + } + return true; + } + return false; +} + namespace { using namespace struct_reorg; @@ -198,37 +229,6 @@ gimplify_build1 (gimple_stmt_iterator *gsi, enum tree_code code, tree type, GSI_SAME_STMT); } -/* Check whether in C language or LTO with only C language. */ - -bool -lang_c_p (void) -{ - const char *language_string = lang_hooks.name; - - if (!language_string) - return false; - - if (lang_GNU_C ()) - return true; - else if (strcmp (language_string, "GNU GIMPLE") == 0) // for LTO check - { - unsigned i = 0; - tree t = NULL_TREE; - - FOR_EACH_VEC_SAFE_ELT (all_translation_units, i, t) - { - language_string = TRANSLATION_UNIT_LANGUAGE (t); - if (language_string == NULL - || strncmp (language_string, "GNU C", 5) - || (language_string[5] != '\0' - && !(ISDIGIT (language_string[5])))) - return false; - } - return true; - } - return false; -} - /* Get the number of pointer layers. */ int @@ -262,29 +262,23 @@ is_from_void_ptr_parm (tree ssa_name) && VOID_POINTER_P (TREE_TYPE (ssa_name))); } -enum srmode -{ - NORMAL = 0, - COMPLETE_STRUCT_RELAYOUT, - STRUCT_REORDER_FIELDS -}; - /* Enum the struct layout optimize level, which should be the same as the option -fstruct-reorg=. */ enum struct_layout_opt_level { NONE = 0, - STRUCT_REORG, - STRUCT_REORDER_FIELDS_SLO, - DEAD_FIELD_ELIMINATION + STRUCT_SPLIT = 1 << 0, + COMPLETE_STRUCT_RELAYOUT = 1 << 1, + STRUCT_REORDER_FIELDS = 1 << 2, + DEAD_FIELD_ELIMINATION = 1 << 3 }; static bool is_result_of_mult (tree arg, tree *num, tree struct_size); static bool isptrptr (tree type); void get_base (tree &base, tree expr); -srmode current_mode; +static unsigned int current_layout_opt_level; hash_map replace_type_map; /* Return true if one of these types is created by struct-reorg. */ @@ -626,7 +620,7 @@ void srtype::simple_dump (FILE *f) { print_generic_expr (f, type); - if (current_mode == STRUCT_REORDER_FIELDS) + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) fprintf (f, "(%d)", TYPE_UID (type)); } @@ -673,7 +667,7 @@ srfield::create_new_fields (tree newtype[max_split], tree newfields[max_split], tree newlast[max_split]) { - if (current_mode == STRUCT_REORDER_FIELDS) + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) { create_new_reorder_fields (newtype, newfields, newlast); return; @@ -861,7 +855,7 @@ srtype::create_new_type (void) we are not splitting the struct into two clusters, then just return false and don't change the type. */ if (!createnewtype && maxclusters == 0 - && current_mode != STRUCT_REORDER_FIELDS) + && current_layout_opt_level < STRUCT_REORDER_FIELDS) { newtype[0] = type; return false; @@ -889,8 +883,7 @@ srtype::create_new_type (void) sprintf (id, "%d", i); if (tname) { - name = concat (tname, current_mode == STRUCT_REORDER_FIELDS - ? ".reorder." : ".reorg.", id, NULL); + name = concat (tname, ".reorg.", id, NULL); TYPE_NAME (newtype[i]) = build_decl (UNKNOWN_LOCATION, TYPE_DECL, get_identifier (name), @@ -902,8 +895,7 @@ srtype::create_new_type (void) for (unsigned i = 0; i < fields.length (); i++) { srfield *f = fields[i]; - if (current_mode == STRUCT_REORDER_FIELDS - && struct_layout_optimize_level >= DEAD_FIELD_ELIMINATION + if (current_layout_opt_level & DEAD_FIELD_ELIMINATION && !(f->field_access & READ_FIELD)) continue; f->create_new_fields (newtype, newfields, newlast); @@ -924,13 +916,12 @@ srtype::create_new_type (void) warn_padded = save_warn_padded; - if (current_mode == STRUCT_REORDER_FIELDS + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS && replace_type_map.get (this->newtype[0]) == NULL) replace_type_map.put (this->newtype[0], this->type); if (dump_file) { - if (current_mode == STRUCT_REORDER_FIELDS - && struct_layout_optimize_level >= DEAD_FIELD_ELIMINATION + if (current_layout_opt_level & DEAD_FIELD_ELIMINATION && has_dead_field ()) fprintf (dump_file, "Dead field elimination.\n"); } @@ -1052,8 +1043,7 @@ srfunction::create_new_decls (void) sprintf (id, "%d", j); if (tname) { - name = concat (tname, current_mode == STRUCT_REORDER_FIELDS - ? ".reorder." : ".reorg.", id, NULL); + name = concat (tname, ".reorg.", id, NULL); new_name = get_identifier (name); free (name); } @@ -1265,7 +1255,7 @@ public: bool done_recording; // Methods - unsigned execute (enum srmode mode); + unsigned execute (unsigned int opt); void mark_type_as_escape (tree type, escape_type escapes, gimple *stmt = NULL); @@ -2655,7 +2645,7 @@ escape_type_volatile_array_or_ptrptr (tree type) return escape_volatile; if (isarraytype (type)) return escape_array; - if (isptrptr (type) && (current_mode != STRUCT_REORDER_FIELDS)) + if (isptrptr (type) && (current_layout_opt_level < STRUCT_REORDER_FIELDS)) return escape_ptr_ptr; return does_not_escape; } @@ -2676,12 +2666,11 @@ ipa_struct_reorg::record_field_type (tree field, srtype *base_srtype) field_srfield->type = field_srtype; field_srtype->add_field_site (field_srfield); } - if (field_srtype == base_srtype && current_mode != COMPLETE_STRUCT_RELAYOUT - && current_mode != STRUCT_REORDER_FIELDS) + if (field_srtype == base_srtype && current_layout_opt_level == STRUCT_SPLIT) base_srtype->mark_escape (escape_rescusive_type, NULL); /* Types of non-pointer field are difficult to track the correctness of the rewrite when it used by the escaped type. */ - if (current_mode == STRUCT_REORDER_FIELDS + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS && TREE_CODE (field_type) == RECORD_TYPE) field_srtype->mark_escape (escape_instance_field, NULL); } @@ -2708,7 +2697,7 @@ ipa_struct_reorg::record_struct_field_types (tree base_type, base_srtype->mark_escape (e, NULL); /* Types of non-pointer field are difficult to track the correctness of the rewrite when it used by the escaped type. */ - if (current_mode == STRUCT_REORDER_FIELDS + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS && TREE_CODE (field_type) == RECORD_TYPE) base_srtype->mark_escape (escape_instance_field, NULL); if (handled_type (field_type)) @@ -2899,8 +2888,7 @@ ipa_struct_reorg::record_var (tree decl, escape_type escapes, int arg) /* Separate instance is hard to trace in complete struct relayout optimization. */ - if ((current_mode == COMPLETE_STRUCT_RELAYOUT - || current_mode == STRUCT_REORDER_FIELDS) + if (current_layout_opt_level >= COMPLETE_STRUCT_RELAYOUT && TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE) e = escape_separate_instance; @@ -3004,7 +2992,7 @@ ipa_struct_reorg::find_vars (gimple *stmt) /* Add a safe func mechanism. */ bool l_find = true; bool r_find = true; - if (current_mode == STRUCT_REORDER_FIELDS) + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) { l_find = !(current_function->is_safe_func && TREE_CODE (lhs) == SSA_NAME @@ -3050,7 +3038,7 @@ ipa_struct_reorg::find_vars (gimple *stmt) } } } - else if ((current_mode == STRUCT_REORDER_FIELDS) + else if ((current_layout_opt_level >= STRUCT_REORDER_FIELDS) && (gimple_assign_rhs_code (stmt) == LE_EXPR || gimple_assign_rhs_code (stmt) == LT_EXPR || gimple_assign_rhs_code (stmt) == GE_EXPR @@ -3061,7 +3049,7 @@ ipa_struct_reorg::find_vars (gimple *stmt) find_var (gimple_assign_rhs2 (stmt), stmt); } /* find void ssa_name from stmt such as: _2 = _1 - old_arcs_1. */ - else if ((current_mode == STRUCT_REORDER_FIELDS) + else if ((current_layout_opt_level >= STRUCT_REORDER_FIELDS) && gimple_assign_rhs_code (stmt) == POINTER_DIFF_EXPR && types_compatible_p ( TYPE_MAIN_VARIANT (TREE_TYPE (gimple_assign_rhs1 (stmt))), @@ -3274,8 +3262,7 @@ ipa_struct_reorg::maybe_record_stmt (cgraph_node *node, gimple *stmt) default: break; } - if (current_mode == STRUCT_REORDER_FIELDS - && struct_layout_optimize_level >= DEAD_FIELD_ELIMINATION) + if (current_layout_opt_level & DEAD_FIELD_ELIMINATION) { /* Look for loads and stores. */ walk_stmt_load_store_ops (stmt, this, find_field_p_load, @@ -3431,9 +3418,11 @@ is_result_of_mult (tree arg, tree *num, tree struct_size) arg = gimple_assign_rhs1 (size_def_stmt); size_def_stmt = SSA_NAME_DEF_STMT (arg); } - else if (rhs_code == NEGATE_EXPR && current_mode == STRUCT_REORDER_FIELDS) + else if (rhs_code == NEGATE_EXPR + && current_layout_opt_level >= STRUCT_REORDER_FIELDS) return trace_calculate_negate (size_def_stmt, num, struct_size); - else if (rhs_code == NOP_EXPR && current_mode == STRUCT_REORDER_FIELDS) + else if (rhs_code == NOP_EXPR + && current_layout_opt_level >= STRUCT_REORDER_FIELDS) return trace_calculate_diff (size_def_stmt, num); else { @@ -3451,15 +3440,15 @@ is_result_of_mult (tree arg, tree *num, tree struct_size) bool ipa_struct_reorg::handled_allocation_stmt (gimple *stmt) { - if ((current_mode == STRUCT_REORDER_FIELDS) + if ((current_layout_opt_level >= STRUCT_REORDER_FIELDS) && (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC) || gimple_call_builtin_p (stmt, BUILT_IN_MALLOC) || gimple_call_builtin_p (stmt, BUILT_IN_CALLOC))) return true; - if ((current_mode == COMPLETE_STRUCT_RELAYOUT) + if ((current_layout_opt_level == COMPLETE_STRUCT_RELAYOUT) && gimple_call_builtin_p (stmt, BUILT_IN_CALLOC)) return true; - if ((current_mode == NORMAL) + if ((current_layout_opt_level == STRUCT_SPLIT) && (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC) || gimple_call_builtin_p (stmt, BUILT_IN_MALLOC) || gimple_call_builtin_p (stmt, BUILT_IN_CALLOC) @@ -3580,7 +3569,7 @@ ipa_struct_reorg::maybe_mark_or_record_other_side (tree side, tree other, /* x_1 = y.x_nodes; void *x; Directly mark the structure pointer type assigned to the void* variable as escape. */ - else if (current_mode == STRUCT_REORDER_FIELDS + else if (current_layout_opt_level >= STRUCT_REORDER_FIELDS && TREE_CODE (side) == SSA_NAME && VOID_POINTER_P (TREE_TYPE (side)) && SSA_NAME_VAR (side) @@ -3837,7 +3826,7 @@ ipa_struct_reorg::get_type_field (tree expr, tree &base, bool &indirect, and doesn't mark escape follow.). */ /* _1 = MEM[(struct arc_t * *)a_1]. then base a_1: ssa_name - pointer_type - integer_type. */ - if (current_mode == STRUCT_REORDER_FIELDS) + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) { bool is_int_ptr = POINTER_TYPE_P (TREE_TYPE (base)) && (TREE_CODE (inner_type (TREE_TYPE (base))) @@ -3899,7 +3888,7 @@ ipa_struct_reorg::get_type_field (tree expr, tree &base, bool &indirect, /* Escape the operation of fetching field with pointer offset such as: *(&(t->right)) = malloc (0); -> MEM[(struct node * *)_1 + 8B] = malloc (0); */ - if (current_mode != NORMAL + if (current_layout_opt_level > STRUCT_SPLIT && (TREE_CODE (expr) == MEM_REF) && (offset != 0)) { gcc_assert (can_escape); @@ -4063,7 +4052,7 @@ ipa_struct_reorg::maybe_record_call (cgraph_node *node, gcall *stmt) /* callee_func (_1, _2); Check the callee func, instead of current func. */ if (!(free_or_realloc - || (current_mode == STRUCT_REORDER_FIELDS + || (current_layout_opt_level >= STRUCT_REORDER_FIELDS && safe_functions.contains ( node->get_edge (stmt)->callee))) && VOID_POINTER_P (argtypet)) @@ -4091,12 +4080,7 @@ ipa_struct_reorg::record_stmt_expr (tree expr, cgraph_node *node, gimple *stmt) realpart, imagpart, address, escape_from_base)) return; - if (current_mode == STRUCT_REORDER_FIELDS) - { - if (!opt_for_fn (current_function_decl, flag_ipa_reorder_fields)) - type->mark_escape (escape_non_optimize, stmt); - } - else + if (current_layout_opt_level > NONE) { if (!opt_for_fn (current_function_decl, flag_ipa_struct_reorg)) type->mark_escape (escape_non_optimize, stmt); @@ -4200,7 +4184,7 @@ ipa_struct_reorg::check_type_and_push (tree newdecl, srdecl *decl, void ipa_struct_reorg::check_alloc_num (gimple *stmt, srtype *type) { - if (current_mode == COMPLETE_STRUCT_RELAYOUT + if (current_layout_opt_level == COMPLETE_STRUCT_RELAYOUT && handled_allocation_stmt (stmt)) { tree arg0 = gimple_call_arg (stmt, 0); @@ -4293,7 +4277,7 @@ ipa_struct_reorg::check_definition_call (srdecl *decl, vec &worklist) if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC)) check_type_and_push (gimple_call_arg (stmt, 0), decl, worklist, stmt); - if (current_mode == STRUCT_REORDER_FIELDS) + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) { if (!handled_allocation_stmt (stmt)) type->mark_escape (escape_return, stmt); @@ -4343,7 +4327,8 @@ ipa_struct_reorg::check_definition (srdecl *decl, vec &worklist) type->mark_escape (escape_cast_void, SSA_NAME_DEF_STMT (ssa_name)); return; } - if (current_mode == STRUCT_REORDER_FIELDS && SSA_NAME_VAR (ssa_name) + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS + && SSA_NAME_VAR (ssa_name) && VOID_POINTER_P (TREE_TYPE (SSA_NAME_VAR (ssa_name)))) type->mark_escape (escape_cast_void, SSA_NAME_DEF_STMT (ssa_name)); gimple *stmt = SSA_NAME_DEF_STMT (ssa_name); @@ -4427,7 +4412,7 @@ ipa_struct_reorg::check_other_side (srdecl *decl, tree other, gimple *stmt, { /* In Complete Struct Relayout, if lhs type is the same as rhs type, we could return without any harm. */ - if (current_mode == COMPLETE_STRUCT_RELAYOUT) + if (current_layout_opt_level == COMPLETE_STRUCT_RELAYOUT) return; tree base; @@ -4439,7 +4424,7 @@ ipa_struct_reorg::check_other_side (srdecl *decl, tree other, gimple *stmt, if (!get_type_field (other, base, indirect, type1, field, realpart, imagpart, address, escape_from_base)) { - if (current_mode == STRUCT_REORDER_FIELDS) + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) { /* release INTEGER_TYPE cast to struct pointer. */ bool cast_from_int_ptr = current_function->is_safe_func && base @@ -4489,7 +4474,8 @@ get_base (tree &base, tree expr) void ipa_struct_reorg::check_ptr_layers (tree a_expr, tree b_expr, gimple* stmt) { - if (current_mode != STRUCT_REORDER_FIELDS || current_function->is_safe_func + if (current_layout_opt_level < STRUCT_REORDER_FIELDS + || current_function->is_safe_func || !(POINTER_TYPE_P (TREE_TYPE (a_expr))) || !(POINTER_TYPE_P (TREE_TYPE (b_expr))) || !handled_type (TREE_TYPE (a_expr)) @@ -4556,12 +4542,9 @@ ipa_struct_reorg::check_use (srdecl *decl, gimple *stmt, tree rhs2 = gimple_cond_rhs (stmt); tree orhs = rhs1; enum tree_code code = gimple_cond_code (stmt); - if ((current_mode == NORMAL && (code != EQ_EXPR && code != NE_EXPR)) - || (current_mode == COMPLETE_STRUCT_RELAYOUT - && (code != EQ_EXPR && code != NE_EXPR - && code != LT_EXPR && code != LE_EXPR - && code != GT_EXPR && code != GE_EXPR)) - || (current_mode == STRUCT_REORDER_FIELDS + if ((current_layout_opt_level == STRUCT_SPLIT + && (code != EQ_EXPR && code != NE_EXPR)) + || (current_layout_opt_level >= COMPLETE_STRUCT_RELAYOUT && (code != EQ_EXPR && code != NE_EXPR && code != LT_EXPR && code != LE_EXPR && code != GT_EXPR && code != GE_EXPR))) @@ -4594,15 +4577,12 @@ ipa_struct_reorg::check_use (srdecl *decl, gimple *stmt, tree rhs2 = gimple_assign_rhs2 (stmt); tree orhs = rhs1; enum tree_code code = gimple_assign_rhs_code (stmt); - if ((current_mode == NORMAL && (code != EQ_EXPR && code != NE_EXPR)) - || (current_mode == COMPLETE_STRUCT_RELAYOUT - && (code != EQ_EXPR && code != NE_EXPR - && code != LT_EXPR && code != LE_EXPR - && code != GT_EXPR && code != GE_EXPR)) - || (current_mode == STRUCT_REORDER_FIELDS + if ((current_layout_opt_level == STRUCT_SPLIT + && (code != EQ_EXPR && code != NE_EXPR)) + || (current_layout_opt_level >= COMPLETE_STRUCT_RELAYOUT && (code != EQ_EXPR && code != NE_EXPR && code != LT_EXPR && code != LE_EXPR - && code != GT_EXPR && code != GE_EXPR))) + && code != GT_EXPR && code != GE_EXPR))) { mark_expr_escape (rhs1, escape_non_eq, stmt); mark_expr_escape (rhs2, escape_non_eq, stmt); @@ -4724,9 +4704,9 @@ ipa_struct_reorg::record_function (cgraph_node *node) escapes = escape_marked_as_used; else if (!node->local) { - if (current_mode != STRUCT_REORDER_FIELDS) + if (current_layout_opt_level < STRUCT_REORDER_FIELDS) escapes = escape_visible_function; - if (current_mode == STRUCT_REORDER_FIELDS && node->externally_visible) + else if (node->externally_visible) escapes = escape_visible_function; } else if (!node->can_change_signature) @@ -4734,12 +4714,7 @@ ipa_struct_reorg::record_function (cgraph_node *node) else if (!tree_versionable_function_p (node->decl)) escapes = escape_noclonable_function; - if (current_mode == STRUCT_REORDER_FIELDS) - { - if (!opt_for_fn (node->decl, flag_ipa_reorder_fields)) - escapes = escape_non_optimize; - } - else if (current_mode == NORMAL || current_mode == COMPLETE_STRUCT_RELAYOUT) + if (current_layout_opt_level > NONE) { if (!opt_for_fn (node->decl, flag_ipa_struct_reorg)) escapes = escape_non_optimize; @@ -4749,10 +4724,10 @@ ipa_struct_reorg::record_function (cgraph_node *node) gimple_stmt_iterator si; /* Add a safe func mechanism. */ - if (current_mode == STRUCT_REORDER_FIELDS) + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) { current_function->is_safe_func = safe_functions.contains (node); - if (dump_file) + if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "\nfunction %s/%u: is_safe_func = %d\n", node->name (), node->order, @@ -4960,7 +4935,7 @@ ipa_struct_reorg::record_accesses (void) } /* Add a safe func mechanism. */ - if (current_mode == STRUCT_REORDER_FIELDS) + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) record_safe_func_with_void_ptr_parm (); FOR_EACH_FUNCTION (cnode) @@ -5176,8 +5151,7 @@ ipa_struct_reorg::propagate_escape_via_ext_func_types (void) void ipa_struct_reorg::prune_escaped_types (void) { - if (current_mode != COMPLETE_STRUCT_RELAYOUT - && current_mode != STRUCT_REORDER_FIELDS) + if (current_layout_opt_level == STRUCT_SPLIT) { /* Detect recusive types and mark them as escaping. */ detect_cycles (); @@ -5185,7 +5159,7 @@ ipa_struct_reorg::prune_escaped_types (void) mark them as escaping. */ propagate_escape (); } - if (current_mode == STRUCT_REORDER_FIELDS) + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) { propagate_escape_via_original (); propagate_escape_via_empty_with_no_original (); @@ -5246,7 +5220,7 @@ ipa_struct_reorg::prune_escaped_types (void) if (function->args.is_empty () && function->decls.is_empty () && function->globals.is_empty () - && current_mode != STRUCT_REORDER_FIELDS) + && current_layout_opt_level < STRUCT_REORDER_FIELDS) { delete function; functions.ordered_remove (i); @@ -5274,7 +5248,7 @@ ipa_struct_reorg::prune_escaped_types (void) /* The escape type is not deleted in STRUCT_REORDER_FIELDS, Then the type that contains the escaped type fields can find complete information. */ - if (current_mode != STRUCT_REORDER_FIELDS) + if (current_layout_opt_level < STRUCT_REORDER_FIELDS) { for (unsigned i = 0; i < types.length ();) { @@ -5322,7 +5296,7 @@ ipa_struct_reorg::create_new_types (void) for (unsigned i = 0; i < types.length (); i++) newtypes += types[i]->create_new_type (); - if (current_mode == STRUCT_REORDER_FIELDS) + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) { for (unsigned i = 0; i < types.length (); i++) { @@ -5446,8 +5420,7 @@ ipa_struct_reorg::create_new_args (cgraph_node *new_node) char *name = NULL; if (tname) { - name = concat (tname, current_mode == STRUCT_REORDER_FIELDS - ? ".reorder.0" : ".reorg.0", NULL); + name = concat (tname, ".reorg.0", NULL); new_name = get_identifier (name); free (name); } @@ -5534,9 +5507,7 @@ ipa_struct_reorg::create_new_functions (void) } statistics_counter_event (NULL, "Create new function", 1); new_node = node->create_version_clone_with_body ( - vNULL, NULL, NULL, NULL, NULL, - current_mode == STRUCT_REORDER_FIELDS - ? "struct_reorder" : "struct_reorg"); + vNULL, NULL, NULL, NULL, NULL, "struct_reorg"); new_node->can_change_signature = node->can_change_signature; new_node->make_local (); f->newnode = new_node; @@ -5663,7 +5634,7 @@ ipa_struct_reorg::rewrite_expr (tree expr, newbase1 = build_fold_addr_expr (newbase1); if (indirect) { - if (current_mode == STRUCT_REORDER_FIELDS) + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) { /* Supports the MEM_REF offset. _1 = MEM[(struct arc *)ap_1 + 72B].flow; @@ -5721,8 +5692,7 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) { bool remove = false; - if (current_mode == STRUCT_REORDER_FIELDS - && struct_layout_optimize_level >= DEAD_FIELD_ELIMINATION + if (current_layout_opt_level & DEAD_FIELD_ELIMINATION && remove_dead_field_stmt (gimple_assign_lhs (stmt))) { if (dump_file && (dump_flags & TDF_DETAILS)) @@ -5758,10 +5728,10 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) return remove; } - if ((current_mode != STRUCT_REORDER_FIELDS + if ((current_layout_opt_level < STRUCT_REORDER_FIELDS && (gimple_assign_rhs_code (stmt) == EQ_EXPR || gimple_assign_rhs_code (stmt) == NE_EXPR)) - || (current_mode == STRUCT_REORDER_FIELDS + || (current_layout_opt_level >= STRUCT_REORDER_FIELDS && (TREE_CODE_CLASS (gimple_assign_rhs_code (stmt)) == tcc_comparison))) { @@ -5771,7 +5741,7 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) tree newrhs2[max_split]; tree_code rhs_code = gimple_assign_rhs_code (stmt); tree_code code = rhs_code == EQ_EXPR ? BIT_AND_EXPR : BIT_IOR_EXPR; - if (current_mode == STRUCT_REORDER_FIELDS + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS && rhs_code != EQ_EXPR && rhs_code != NE_EXPR) code = rhs_code; @@ -5820,8 +5790,9 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) _6 = _4 + _5; _5 = (long unsigned int) _3; _3 = _1 - old_2. */ - if (current_mode != STRUCT_REORDER_FIELDS - || (current_mode == STRUCT_REORDER_FIELDS && (num != NULL))) + if (current_layout_opt_level < STRUCT_REORDER_FIELDS + || (current_layout_opt_level >= STRUCT_REORDER_FIELDS + && (num != NULL))) num = gimplify_build1 (gsi, NOP_EXPR, sizetype, num); for (unsigned i = 0; i < max_split && newlhs[i]; i++) { @@ -5845,7 +5816,7 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) } /* Support POINTER_DIFF_EXPR rewriting. */ - if (current_mode == STRUCT_REORDER_FIELDS + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS && gimple_assign_rhs_code (stmt) == POINTER_DIFF_EXPR) { tree rhs1 = gimple_assign_rhs1 (stmt); @@ -6028,7 +5999,8 @@ ipa_struct_reorg::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi) srfunction *f = find_function (node); /* Add a safe func mechanism. */ - if (current_mode == STRUCT_REORDER_FIELDS && f && f->is_safe_func) + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS + && f && f->is_safe_func) { tree expr = gimple_call_arg (stmt, 0); tree newexpr[max_split]; @@ -6154,9 +6126,9 @@ ipa_struct_reorg::rewrite_cond (gcond *stmt, gimple_stmt_iterator *gsi) tree_code rhs_code = gimple_cond_code (stmt); /* Handle only equals or not equals conditionals. */ - if ((current_mode != STRUCT_REORDER_FIELDS + if ((current_layout_opt_level < STRUCT_REORDER_FIELDS && (rhs_code != EQ_EXPR && rhs_code != NE_EXPR)) - || (current_mode == STRUCT_REORDER_FIELDS + || (current_layout_opt_level >= STRUCT_REORDER_FIELDS && TREE_CODE_CLASS (rhs_code) != tcc_comparison)) return false; tree lhs = gimple_cond_lhs (stmt); @@ -6210,7 +6182,7 @@ ipa_struct_reorg::rewrite_cond (gcond *stmt, gimple_stmt_iterator *gsi) bool ipa_struct_reorg::rewrite_debug (gimple *stmt, gimple_stmt_iterator *) { - if (current_mode == STRUCT_REORDER_FIELDS) + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) /* Delete debug gimple now. */ return true; bool remove = false; @@ -6369,7 +6341,7 @@ ipa_struct_reorg::rewrite_functions (void) then don't rewrite any accesses. */ if (!create_new_types ()) { - if (current_mode == STRUCT_REORDER_FIELDS) + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) { for (unsigned i = 0; i < functions.length (); i++) { @@ -6388,7 +6360,7 @@ ipa_struct_reorg::rewrite_functions (void) return 0; } - if (current_mode == STRUCT_REORDER_FIELDS && dump_file) + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS && dump_file) { fprintf (dump_file, "=========== all created newtypes: ===========\n\n"); dump_newtypes (dump_file); @@ -6398,13 +6370,13 @@ ipa_struct_reorg::rewrite_functions (void) { retval = TODO_remove_functions; create_new_functions (); - if (current_mode == STRUCT_REORDER_FIELDS) + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) { prune_escaped_types (); } } - if (current_mode == STRUCT_REORDER_FIELDS) + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) { for (unsigned i = 0; i < functions.length (); i++) { @@ -6561,33 +6533,33 @@ ipa_struct_reorg::execute_struct_relayout (void) } unsigned int -ipa_struct_reorg::execute (enum srmode mode) +ipa_struct_reorg::execute (unsigned int opt) { unsigned int ret = 0; if (dump_file) fprintf (dump_file, "\n\n====== ipa_struct_reorg level %d ======\n\n", - mode); + ret); - if (mode == NORMAL || mode == STRUCT_REORDER_FIELDS) + if (opt != COMPLETE_STRUCT_RELAYOUT) { - current_mode = mode; + current_layout_opt_level = opt; /* If there is a top-level inline-asm, the pass immediately returns. */ if (symtab->first_asm_symbol ()) return 0; record_accesses (); prune_escaped_types (); - if (current_mode == NORMAL) + if (current_layout_opt_level == STRUCT_SPLIT) analyze_types (); ret = rewrite_functions (); } - else if (mode == COMPLETE_STRUCT_RELAYOUT) + else { if (dump_file) fprintf (dump_file, "\n\nTry Complete Struct Relayout:\n"); - current_mode = COMPLETE_STRUCT_RELAYOUT; + current_layout_opt_level = COMPLETE_STRUCT_RELAYOUT; if (symtab->first_asm_symbol ()) return 0; record_accesses (); @@ -6624,10 +6596,37 @@ public: virtual unsigned int execute (function *) { unsigned int ret = 0; - ret = ipa_struct_reorg ().execute (NORMAL); - if (!ret) - ret = ipa_struct_reorg ().execute (COMPLETE_STRUCT_RELAYOUT); - return ret; + unsigned int ret_reorg = 0; + unsigned int level = 0; + switch (struct_layout_optimize_level) + { + case 3: level |= DEAD_FIELD_ELIMINATION; + // FALLTHRU + case 2: level |= STRUCT_REORDER_FIELDS; + // FALLTHRU + case 1: + level |= COMPLETE_STRUCT_RELAYOUT; + level |= STRUCT_SPLIT; + break; + case 0: break; + default: gcc_unreachable (); + } + /* Preserved for backward compatibility, reorder fields needs run before + struct split and complete struct relayout. */ + if (flag_ipa_reorder_fields && level < STRUCT_REORDER_FIELDS) + ret = ipa_struct_reorg ().execute (STRUCT_REORDER_FIELDS); + + if (level >= STRUCT_REORDER_FIELDS) + ret = ipa_struct_reorg ().execute (level); + + if (level >= COMPLETE_STRUCT_RELAYOUT) + { + /* Preserved for backward compatibility. */ + ret_reorg = ipa_struct_reorg ().execute (STRUCT_SPLIT); + if (!ret_reorg) + ret_reorg = ipa_struct_reorg ().execute (COMPLETE_STRUCT_RELAYOUT); + } + return ret | ret_reorg; } }; // class pass_ipa_struct_reorg @@ -6647,52 +6646,6 @@ pass_ipa_struct_reorg::gate (function *) && (in_lto_p || flag_whole_program)); } -const pass_data pass_data_ipa_reorder_fields = -{ - SIMPLE_IPA_PASS, // type - "reorder_fields", // name - OPTGROUP_NONE, // optinfo_flags - TV_IPA_REORDER_FIELDS, // tv_id - 0, // properties_required - 0, // properties_provided - 0, // properties_destroyed - 0, // todo_flags_start - 0, // todo_flags_finish -}; - -class pass_ipa_reorder_fields : public simple_ipa_opt_pass -{ -public: - pass_ipa_reorder_fields (gcc::context *ctxt) - : simple_ipa_opt_pass (pass_data_ipa_reorder_fields, ctxt) - {} - - /* opt_pass methods: */ - virtual bool gate (function *); - virtual unsigned int execute (function *) - { - unsigned int ret = 0; - ret = ipa_struct_reorg ().execute (STRUCT_REORDER_FIELDS); - return ret; - } - -}; // class pass_ipa_reorder_fields - -bool -pass_ipa_reorder_fields::gate (function *) -{ - return (optimize >= 3 - && flag_ipa_reorder_fields - /* Don't bother doing anything if the program has errors. */ - && !seen_error () - && flag_lto_partition == LTO_PARTITION_ONE - /* Only enable struct optimizations in C since other - languages' grammar forbid. */ - && lang_c_p () - /* Only enable struct optimizations in lto or whole_program. */ - && (in_lto_p || flag_whole_program)); -} - } // anon namespace @@ -6701,9 +6654,3 @@ make_pass_ipa_struct_reorg (gcc::context *ctxt) { return new pass_ipa_struct_reorg (ctxt); } - -simple_ipa_opt_pass * -make_pass_ipa_reorder_fields (gcc::context *ctxt) -{ - return new pass_ipa_reorder_fields (ctxt); -} diff --git a/gcc/passes.def b/gcc/passes.def index cdb950220dd..1c1658c4a85 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -177,7 +177,6 @@ along with GCC; see the file COPYING3. If not see compiled unit. */ INSERT_PASSES_AFTER (all_late_ipa_passes) NEXT_PASS (pass_ipa_pta); - NEXT_PASS (pass_ipa_reorder_fields); /* FIXME: this should be a normal IP pass. */ NEXT_PASS (pass_ipa_struct_reorg); NEXT_PASS (pass_omp_simd_clone); diff --git a/gcc/symbol-summary.h b/gcc/symbol-summary.h index 6fa529eee45..3fe64047c8b 100644 --- a/gcc/symbol-summary.h +++ b/gcc/symbol-summary.h @@ -105,7 +105,7 @@ protected: { /* In structure optimizatons, we call new to ensure that the allocated memory is initialized to 0. */ - if (flag_ipa_struct_reorg || flag_ipa_reorder_fields) + if (flag_ipa_struct_reorg) return is_ggc () ? new (ggc_internal_alloc (sizeof (T))) T () : new T (); @@ -122,7 +122,7 @@ protected: ggc_delete (item); else { - if (flag_ipa_struct_reorg || flag_ipa_reorder_fields) + if (flag_ipa_struct_reorg) delete item; else m_allocator.remove (item); diff --git a/gcc/testsuite/gcc.dg/struct/dfe_DTE_verify.c b/gcc/testsuite/gcc.dg/struct/dfe_DTE_verify.c index bd319253e0b..10b5be86d3c 100644 --- a/gcc/testsuite/gcc.dg/struct/dfe_DTE_verify.c +++ b/gcc/testsuite/gcc.dg/struct/dfe_DTE_verify.c @@ -83,4 +83,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "reorder_fields" } } */ +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_ele_minus_verify.c b/gcc/testsuite/gcc.dg/struct/dfe_ele_minus_verify.c index 78b3881bdc6..5ecfa9fe133 100644 --- a/gcc/testsuite/gcc.dg/struct/dfe_ele_minus_verify.c +++ b/gcc/testsuite/gcc.dg/struct/dfe_ele_minus_verify.c @@ -57,4 +57,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "reorder_fields" } } */ +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_extr_board_init.c b/gcc/testsuite/gcc.dg/struct/dfe_extr_board_init.c index 7723c240ba7..d217f7bd80e 100644 --- a/gcc/testsuite/gcc.dg/struct/dfe_extr_board_init.c +++ b/gcc/testsuite/gcc.dg/struct/dfe_extr_board_init.c @@ -74,4 +74,4 @@ LBF_DFU_If_Needed (void) } } -/* { dg-final { scan-ipa-dump-times "Dead field elimination" 0 "reorder_fields" } } */ +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 0 "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_extr_claw.c b/gcc/testsuite/gcc.dg/struct/dfe_extr_claw.c index a1feac96606..f9e2cf471c8 100644 --- a/gcc/testsuite/gcc.dg/struct/dfe_extr_claw.c +++ b/gcc/testsuite/gcc.dg/struct/dfe_extr_claw.c @@ -81,4 +81,4 @@ claw_snd_conn_req (struct net_device *dev, __u8 link) return rc; } -/* { dg-final { scan-ipa-dump-times "Dead field elimination" 1 "reorder_fields" } } */ +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 1 "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_extr_dtrace.c b/gcc/testsuite/gcc.dg/struct/dfe_extr_dtrace.c index fd1e936ca62..c86c4bb3cd0 100644 --- a/gcc/testsuite/gcc.dg/struct/dfe_extr_dtrace.c +++ b/gcc/testsuite/gcc.dg/struct/dfe_extr_dtrace.c @@ -53,4 +53,4 @@ dtrace_bcmp (const void *s1, const void *s2, size_t len) return (0); } -/* { dg-final { scan-ipa-dump-times "Dead field elimination" 0 "reorder_fields" } } */ +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 0 "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_extr_gc.c b/gcc/testsuite/gcc.dg/struct/dfe_extr_gc.c index b13d785a9dd..8484d29d256 100644 --- a/gcc/testsuite/gcc.dg/struct/dfe_extr_gc.c +++ b/gcc/testsuite/gcc.dg/struct/dfe_extr_gc.c @@ -159,4 +159,4 @@ gc_gray_mark (mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) return children; } -/* { dg-final { scan-ipa-dump-times "Dead field elimination" 0 "reorder_fields" } } */ +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 0 "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_extr_hpsa.c b/gcc/testsuite/gcc.dg/struct/dfe_extr_hpsa.c index bc28a658a36..300b2dac4db 100644 --- a/gcc/testsuite/gcc.dg/struct/dfe_extr_hpsa.c +++ b/gcc/testsuite/gcc.dg/struct/dfe_extr_hpsa.c @@ -123,4 +123,4 @@ hpsa_cmd_dev_match (struct ctlr_info *h, struct CommandList *c, return match; } -/* { dg-final { scan-ipa-dump-times "Dead field elimination" 0 "reorder_fields" } } */ +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 0 "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_extr_mv_udc_core.c b/gcc/testsuite/gcc.dg/struct/dfe_extr_mv_udc_core.c index 0a585ac3d5c..9397b98eaef 100644 --- a/gcc/testsuite/gcc.dg/struct/dfe_extr_mv_udc_core.c +++ b/gcc/testsuite/gcc.dg/struct/dfe_extr_mv_udc_core.c @@ -79,4 +79,4 @@ ep0_reset (struct mv_udc *udc) } } -/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "reorder_fields" } } */ +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_extr_tcp_usrreq.c b/gcc/testsuite/gcc.dg/struct/dfe_extr_tcp_usrreq.c index bddd862fec1..0ae75e13e8f 100644 --- a/gcc/testsuite/gcc.dg/struct/dfe_extr_tcp_usrreq.c +++ b/gcc/testsuite/gcc.dg/struct/dfe_extr_tcp_usrreq.c @@ -55,4 +55,4 @@ tcp_usr_listen (struct socket *so, struct proc *p) COMMON_END (PRU_LISTEN); } -/* { dg-final { scan-ipa-dump-times "Dead field elimination" 1 "reorder_fields" } } */ +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 1 "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_extr_ui_main.c b/gcc/testsuite/gcc.dg/struct/dfe_extr_ui_main.c index 1a06f5eec60..512fb37a7f4 100644 --- a/gcc/testsuite/gcc.dg/struct/dfe_extr_ui_main.c +++ b/gcc/testsuite/gcc.dg/struct/dfe_extr_ui_main.c @@ -58,4 +58,4 @@ UI_LoadMods () } } -/* { dg-final { scan-ipa-dump-times "Dead field elimination" 1 "reorder_fields" } } */ +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 1 "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_mem_ref_offset.c b/gcc/testsuite/gcc.dg/struct/dfe_mem_ref_offset.c index 670e2919ac7..6148770282c 100644 --- a/gcc/testsuite/gcc.dg/struct/dfe_mem_ref_offset.c +++ b/gcc/testsuite/gcc.dg/struct/dfe_mem_ref_offset.c @@ -55,4 +55,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "reorder_fields" } } */ +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_mul_layer_ptr_record_bug.c b/gcc/testsuite/gcc.dg/struct/dfe_mul_layer_ptr_record_bug.c index 423435a139e..0570d895209 100644 --- a/gcc/testsuite/gcc.dg/struct/dfe_mul_layer_ptr_record_bug.c +++ b/gcc/testsuite/gcc.dg/struct/dfe_mul_layer_ptr_record_bug.c @@ -27,4 +27,4 @@ main() { return 0; } -/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "reorder_fields" } } */ +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_ptr_diff.c b/gcc/testsuite/gcc.dg/struct/dfe_ptr_diff.c index 4d970161042..a9338d1182d 100644 --- a/gcc/testsuite/gcc.dg/struct/dfe_ptr_diff.c +++ b/gcc/testsuite/gcc.dg/struct/dfe_ptr_diff.c @@ -68,4 +68,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump-times "Dead field elimination" 3 "reorder_fields" } } */ +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 3 "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_ptr_negate_expr.c b/gcc/testsuite/gcc.dg/struct/dfe_ptr_negate_expr.c index 881a9befb88..e59a4b48429 100644 --- a/gcc/testsuite/gcc.dg/struct/dfe_ptr_negate_expr.c +++ b/gcc/testsuite/gcc.dg/struct/dfe_ptr_negate_expr.c @@ -52,4 +52,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "reorder_fields" } } */ +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfe_ptr_ptr.c b/gcc/testsuite/gcc.dg/struct/dfe_ptr_ptr.c index b83e270f38a..48cd7932a67 100644 --- a/gcc/testsuite/gcc.dg/struct/dfe_ptr_ptr.c +++ b/gcc/testsuite/gcc.dg/struct/dfe_ptr_ptr.c @@ -52,4 +52,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "reorder_fields" } } */ +/* { dg-final { scan-ipa-dump-times "Dead field elimination" 2 "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/rf_DTE_struct_instance_field.c b/gcc/testsuite/gcc.dg/struct/rf_DTE_struct_instance_field.c index b95be2dabc2..1b6a462e271 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_DTE_struct_instance_field.c +++ b/gcc/testsuite/gcc.dg/struct/rf_DTE_struct_instance_field.c @@ -72,4 +72,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump "No structures to transform." "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "No structures to transform." "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_DTE_verify.c b/gcc/testsuite/gcc.dg/struct/rf_DTE_verify.c index 3d243313ba9..346c7126446 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_DTE_verify.c +++ b/gcc/testsuite/gcc.dg/struct/rf_DTE_verify.c @@ -91,4 +91,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_check_ptr_layers_bug.c b/gcc/testsuite/gcc.dg/struct/rf_check_ptr_layers_bug.c index faaf1e3a52d..b876fef861f 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_check_ptr_layers_bug.c +++ b/gcc/testsuite/gcc.dg/struct/rf_check_ptr_layers_bug.c @@ -21,4 +21,4 @@ main() { g(); } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_create_fields_bug.c b/gcc/testsuite/gcc.dg/struct/rf_create_fields_bug.c index 886706ae913..7d7641f011d 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_create_fields_bug.c +++ b/gcc/testsuite/gcc.dg/struct/rf_create_fields_bug.c @@ -79,4 +79,4 @@ main() return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_create_new_func_bug.c b/gcc/testsuite/gcc.dg/struct/rf_create_new_func_bug.c index f3785f392e3..63fb3f8284c 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_create_new_func_bug.c +++ b/gcc/testsuite/gcc.dg/struct/rf_create_new_func_bug.c @@ -53,4 +53,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_ele_minus_verify.c b/gcc/testsuite/gcc.dg/struct/rf_ele_minus_verify.c index 1415d759ad6..8c431e15ffd 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_ele_minus_verify.c +++ b/gcc/testsuite/gcc.dg/struct/rf_ele_minus_verify.c @@ -57,4 +57,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_escape_by_base.c b/gcc/testsuite/gcc.dg/struct/rf_escape_by_base.c index 003da0b57bf..efc95a4cd56 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_escape_by_base.c +++ b/gcc/testsuite/gcc.dg/struct/rf_escape_by_base.c @@ -80,4 +80,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_external_func_types.c b/gcc/testsuite/gcc.dg/struct/rf_external_func_types.c index 84a34f24151..2a9bea78369 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_external_func_types.c +++ b/gcc/testsuite/gcc.dg/struct/rf_external_func_types.c @@ -66,4 +66,4 @@ test () return 0; } -/* { dg-final { scan-ipa-dump "No structures to transform." "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "No structures to transform." "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_int_cast_ptr.c b/gcc/testsuite/gcc.dg/struct/rf_int_cast_ptr.c index 10dcf098c3c..75fc10575d5 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_int_cast_ptr.c +++ b/gcc/testsuite/gcc.dg/struct/rf_int_cast_ptr.c @@ -69,4 +69,4 @@ main() return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_mem_ref_offset.c b/gcc/testsuite/gcc.dg/struct/rf_mem_ref_offset.c index 8d1a9a114c1..9fb06877bcb 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_mem_ref_offset.c +++ b/gcc/testsuite/gcc.dg/struct/rf_mem_ref_offset.c @@ -55,4 +55,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_mul_layer_ptr_record_bug.c b/gcc/testsuite/gcc.dg/struct/rf_mul_layer_ptr_record_bug.c index 23765fc5615..e8eb0eaa09a 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_mul_layer_ptr_record_bug.c +++ b/gcc/testsuite/gcc.dg/struct/rf_mul_layer_ptr_record_bug.c @@ -27,4 +27,4 @@ main() { return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_pass_conflict.c b/gcc/testsuite/gcc.dg/struct/rf_pass_conflict.c index 54e737ee856..bd535afd08d 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_pass_conflict.c +++ b/gcc/testsuite/gcc.dg/struct/rf_pass_conflict.c @@ -106,4 +106,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_ptr2void_lto.c b/gcc/testsuite/gcc.dg/struct/rf_ptr2void_lto.c index 2ae46fb3112..11393a197a3 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_ptr2void_lto.c +++ b/gcc/testsuite/gcc.dg/struct/rf_ptr2void_lto.c @@ -84,4 +84,4 @@ main () return cnt; } -/* { dg-final { scan-ipa-dump "No structures to transform." "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "No structures to transform." "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_ptr_diff.c b/gcc/testsuite/gcc.dg/struct/rf_ptr_diff.c index 488575d0b85..ed32f244108 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_ptr_diff.c +++ b/gcc/testsuite/gcc.dg/struct/rf_ptr_diff.c @@ -68,4 +68,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 3" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 3" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_ptr_negate_expr.c b/gcc/testsuite/gcc.dg/struct/rf_ptr_negate_expr.c index 7b7d110df4c..4d5f25aa164 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_ptr_negate_expr.c +++ b/gcc/testsuite/gcc.dg/struct/rf_ptr_negate_expr.c @@ -52,4 +52,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_ptr_offset.c b/gcc/testsuite/gcc.dg/struct/rf_ptr_offset.c index 317aafa5f72..b3891fde928 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_ptr_offset.c +++ b/gcc/testsuite/gcc.dg/struct/rf_ptr_offset.c @@ -31,4 +31,4 @@ main () printf (" Tree.\n"); } -/* { dg-final { scan-ipa-dump "No structures to transform." "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "No structures to transform." "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_ptr_ptr.c b/gcc/testsuite/gcc.dg/struct/rf_ptr_ptr.c index 01a33f66962..4df79e4f0e8 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_ptr_ptr.c +++ b/gcc/testsuite/gcc.dg/struct/rf_ptr_ptr.c @@ -52,4 +52,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_ptr_ptr_ptr.c b/gcc/testsuite/gcc.dg/struct/rf_ptr_ptr_ptr.c index a38556533f1..49d2106d1dc 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_ptr_ptr_ptr.c +++ b/gcc/testsuite/gcc.dg/struct/rf_ptr_ptr_ptr.c @@ -55,4 +55,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_rescusive_type.c b/gcc/testsuite/gcc.dg/struct/rf_rescusive_type.c index 5c17ee528c8..f71c7894f36 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_rescusive_type.c +++ b/gcc/testsuite/gcc.dg/struct/rf_rescusive_type.c @@ -54,4 +54,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_rewrite_assign_more_cmp.c b/gcc/testsuite/gcc.dg/struct/rf_rewrite_assign_more_cmp.c index 710517ee9e2..721cee2c6ae 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_rewrite_assign_more_cmp.c +++ b/gcc/testsuite/gcc.dg/struct/rf_rewrite_assign_more_cmp.c @@ -62,4 +62,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_rewrite_cond_bug.c b/gcc/testsuite/gcc.dg/struct/rf_rewrite_cond_bug.c index 6ed0a5d2d6b..3871d3d99f1 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_rewrite_cond_bug.c +++ b/gcc/testsuite/gcc.dg/struct/rf_rewrite_cond_bug.c @@ -69,4 +69,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 3" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 3" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_rewrite_cond_more_cmp.c b/gcc/testsuite/gcc.dg/struct/rf_rewrite_cond_more_cmp.c index 5a2dd964fc2..5ad206433e0 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_rewrite_cond_more_cmp.c +++ b/gcc/testsuite/gcc.dg/struct/rf_rewrite_cond_more_cmp.c @@ -55,4 +55,4 @@ main() return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_rewrite_phi_bug.c b/gcc/testsuite/gcc.dg/struct/rf_rewrite_phi_bug.c index faa90b42ddc..a002f98892e 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_rewrite_phi_bug.c +++ b/gcc/testsuite/gcc.dg/struct/rf_rewrite_phi_bug.c @@ -78,4 +78,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 3" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 3" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_visible_func.c b/gcc/testsuite/gcc.dg/struct/rf_visible_func.c index 8f2da99ccdf..f77a062bda6 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_visible_func.c +++ b/gcc/testsuite/gcc.dg/struct/rf_visible_func.c @@ -89,4 +89,4 @@ main () return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 2" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/rf_void_ptr_param_func.c b/gcc/testsuite/gcc.dg/struct/rf_void_ptr_param_func.c index ee455dd1803..3ed92f2834a 100644 --- a/gcc/testsuite/gcc.dg/struct/rf_void_ptr_param_func.c +++ b/gcc/testsuite/gcc.dg/struct/rf_void_ptr_param_func.c @@ -51,4 +51,4 @@ main() return 0; } -/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "reorder_fields" } } */ \ No newline at end of file +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/struct/struct-reorg.exp b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp index 6ccb753b5ae..278c4e4f586 100644 --- a/gcc/testsuite/gcc.dg/struct/struct-reorg.exp +++ b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp @@ -45,7 +45,7 @@ gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/rf_*.c]] \ # -fipa-struct-reorg=3 gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/dfe*.c]] \ - "" "-fipa-reorder-fields -fipa-struct-reorg=3 -fdump-ipa-all -flto-partition=one -fwhole-program" + "" "-fipa-struct-reorg=3 -fdump-ipa-all -flto-partition=one -fwhole-program" # All done. torture-finish diff --git a/gcc/timevar.def b/gcc/timevar.def index 50d542f4484..36611812611 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -80,7 +80,6 @@ DEFTIMEVAR (TV_IPA_CONSTANT_PROP , "ipa cp") DEFTIMEVAR (TV_IPA_INLINING , "ipa inlining heuristics") DEFTIMEVAR (TV_IPA_FNSPLIT , "ipa function splitting") DEFTIMEVAR (TV_IPA_COMDATS , "ipa comdats") -DEFTIMEVAR (TV_IPA_REORDER_FIELDS , "ipa struct reorder fields optimization") DEFTIMEVAR (TV_IPA_STRUCT_REORG , "ipa struct reorg optimization") DEFTIMEVAR (TV_IPA_OPT , "ipa various optimizations") DEFTIMEVAR (TV_IPA_LTO_DECOMPRESS , "lto stream decompression") diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 8c3c2e0aa02..ec7be874cad 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -526,7 +526,6 @@ 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_reorder_fields (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_struct_reorg (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_pta (gcc::context *ctxt); extern simple_ipa_opt_pass *make_pass_ipa_tm (gcc::context *ctxt); -- Gitee From 8934d1e38c2fbabbfc843bfdc7f1c9830f31761a Mon Sep 17 00:00:00 2001 From: liyancheng <412998149@qq.com> Date: Wed, 16 Aug 2023 21:36:31 +0800 Subject: [PATCH 7/7] [Struct Reorg] Add Safe Structure Pointer Compression Safe structure pointer compression allows safely compressing pointers stored in structure to reduce the size of structure. Add flag -fipa-struct-reorg=4 to enable safe structure pointer compression. --- gcc/common.opt | 5 +- gcc/ipa-struct-reorg/ipa-struct-reorg.cc | 911 ++++++++++++++++++++++- gcc/ipa-struct-reorg/ipa-struct-reorg.h | 4 + gcc/params.opt | 4 + 4 files changed, 885 insertions(+), 39 deletions(-) diff --git a/gcc/common.opt b/gcc/common.opt index 963d7c8ce4c..c4a473ee2f9 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1958,8 +1958,9 @@ Common Var(flag_ipa_struct_reorg) Init(0) Optimization Perform structure layout optimizations. fipa-struct-reorg= -Common RejectNegative Joined UInteger Var(struct_layout_optimize_level) Init(0) IntegerRange(0, 3) --fipa-struct-reorg=[0,1,2,36] adding none, struct-reorg, reorder-fields, dfe optimizations. +Common RejectNegative Joined UInteger Var(struct_layout_optimize_level) Init(0) IntegerRange(0, 4) +-fipa-struct-reorg=[0,1,2,3,4] adding none, struct-reorg, reorder-fields, +dfe, safe-pointer-compression optimizations. fipa-vrp Common Var(flag_ipa_vrp) Optimization diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc index 25f3d584c91..a6eb8f7a804 100644 --- a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc @@ -89,6 +89,7 @@ along with GCC; see the file COPYING3. If not see #include "gimple-iterator.h" #include "gimple-walk.h" #include "cfg.h" +#include "cfghooks.h" /* For split_block. */ #include "ssa.h" #include "tree-dfa.h" #include "fold-const.h" @@ -147,7 +148,27 @@ using namespace struct_relayout; #define VOID_POINTER_P(type) \ (POINTER_TYPE_P (type) && VOID_TYPE_P (TREE_TYPE (type))) -/* Return true iff TYPE is stdarg va_list type. */ +static void +set_var_attributes (tree var) +{ + if (!var) + return; + gcc_assert (TREE_CODE (var) == VAR_DECL); + + DECL_ARTIFICIAL (var) = 1; + DECL_EXTERNAL (var) = 0; + TREE_STATIC (var) = 1; + TREE_PUBLIC (var) = 0; + TREE_USED (var) = 1; + DECL_CONTEXT (var) = NULL; + TREE_THIS_VOLATILE (var) = 0; + TREE_ADDRESSABLE (var) = 0; + TREE_READONLY (var) = 0; + if (is_global_var (var)) + set_decl_tls_model (var, TLS_MODEL_NONE); +} + +/* Return true if TYPE is stdarg va_list type. */ inline bool is_va_list_type (tree type) @@ -271,9 +292,15 @@ enum struct_layout_opt_level STRUCT_SPLIT = 1 << 0, COMPLETE_STRUCT_RELAYOUT = 1 << 1, STRUCT_REORDER_FIELDS = 1 << 2, - DEAD_FIELD_ELIMINATION = 1 << 3 + DEAD_FIELD_ELIMINATION = 1 << 3, + POINTER_COMPRESSION_SAFE = 1 << 4 }; +/* Defines the target pointer size of compressed pointer, which should be 8, + 16, 32. */ + +static int compressed_size = 32; + static bool is_result_of_mult (tree arg, tree *num, tree struct_size); static bool isptrptr (tree type); void get_base (tree &base, tree expr); @@ -394,7 +421,10 @@ srtype::srtype (tree type) : type (type), chain_type (false), escapes (does_not_escape), + pc_gptr (NULL_TREE), visited (false), + pc_candidate (false), + has_legal_alloc_num (false), has_alloc_array (0) { for (int i = 0; i < max_split; i++) @@ -476,6 +506,31 @@ srtype::mark_escape (escape_type e, gimple *stmt) } } +/* Create a global header for compressed struct. */ + +void +srtype::create_global_ptr_for_pc () +{ + if (!pc_candidate || pc_gptr != NULL_TREE) + return; + + const char *type_name = get_type_name (type); + gcc_assert (type_name != NULL); + + char *gptr_name = concat (type_name, "_pc", NULL); + tree new_name = get_identifier (gptr_name); + tree new_type = build_pointer_type (newtype[0]); + tree new_var = build_decl (UNKNOWN_LOCATION, VAR_DECL, new_name, new_type); + set_var_attributes (new_var); + pc_gptr = new_var; + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\nType: %s has create global header for pointer" + " compression: %s\n", type_name, gptr_name); + + free (gptr_name); +} + /* Add FIELD to the list of fields that use this type. */ void @@ -798,15 +853,32 @@ srfield::create_new_reorder_fields (tree newtype[max_split], fields.safe_push (field); } - DECL_NAME (field) = DECL_NAME (fielddecl); if (type == NULL) - /* Common members do not need to reconstruct. + { + DECL_NAME (field) = DECL_NAME (fielddecl); + /* Common members do not need to reconstruct. Otherwise, int* -> int** or void* -> void**. */ - TREE_TYPE (field) = nt; + TREE_TYPE (field) = nt; + SET_DECL_ALIGN (field, DECL_ALIGN (fielddecl)); + } + else if (type->pc_candidate) + { + const char *old_name = IDENTIFIER_POINTER (DECL_NAME (fielddecl)); + char *new_name = concat (old_name, "_pc", NULL); + DECL_NAME (field) = get_identifier (new_name); + free (new_name); + TREE_TYPE (field) = make_unsigned_type (compressed_size); + SET_DECL_ALIGN (field, compressed_size); + } else - TREE_TYPE (field) = reconstruct_complex_type (TREE_TYPE (fielddecl), nt); + { + TREE_TYPE (field) = reconstruct_complex_type (TREE_TYPE (fielddecl), nt); + DECL_NAME (field) = DECL_NAME (fielddecl); + TREE_TYPE (field) = reconstruct_complex_type (TREE_TYPE (fielddecl), nt); + SET_DECL_ALIGN (field, DECL_ALIGN (fielddecl)); + } + DECL_SOURCE_LOCATION (field) = DECL_SOURCE_LOCATION (fielddecl); - SET_DECL_ALIGN (field, DECL_ALIGN (fielddecl)); DECL_USER_ALIGN (field) = DECL_USER_ALIGN (fielddecl); TREE_ADDRESSABLE (field) = TREE_ADDRESSABLE (fielddecl); DECL_NONADDRESSABLE_P (field) = !TREE_ADDRESSABLE (fielddecl); @@ -925,6 +997,10 @@ srtype::create_new_type (void) && has_dead_field ()) fprintf (dump_file, "Dead field elimination.\n"); } + + if (pc_candidate && pc_gptr == NULL_TREE) + create_global_ptr_for_pc (); + if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "Created %d types:\n", maxclusters); @@ -1339,6 +1415,30 @@ public: unsigned execute_struct_relayout (void); bool remove_dead_field_stmt (tree lhs); + + // Pointer compression methods: + void check_and_prune_struct_for_pointer_compression (void); + void try_rewrite_with_pointer_compression (gassign *, gimple_stmt_iterator *, + tree, tree, tree &, tree &); + bool safe_void_cmp_p (tree, srtype *); + bool pc_candidate_st_type_p (tree); + bool pc_candidate_tree_p (tree); + bool pc_type_conversion_candidate_p (tree); + bool pc_direct_rewrite_chance_p (tree, tree &); + bool compress_candidate_with_check (gimple_stmt_iterator *, tree, tree &); + bool compress_candidate (gassign *, gimple_stmt_iterator *, tree, tree &); + bool decompress_candidate_with_check (gimple_stmt_iterator *, tree, tree &); + bool decompress_candidate (gimple_stmt_iterator *, tree, tree, tree &, + tree &); + srtype *get_compression_candidate_type (tree); + tree compress_ptr_to_offset (tree, srtype *, gimple_stmt_iterator *); + tree decompress_offset_to_ptr (tree, srtype *, gimple_stmt_iterator *); + basic_block create_bb_for_compress_candidate (basic_block, tree, srtype *, + tree &); + basic_block create_bb_for_decompress_candidate (basic_block, tree, srtype *, + tree &); + basic_block create_bb_for_compress_nullptr (basic_block, tree &); + basic_block create_bb_for_decompress_nullptr (basic_block, tree, tree &); }; struct ipa_struct_relayout @@ -1387,26 +1487,6 @@ namespace { /* Methods for ipa_struct_relayout. */ -static void -set_var_attributes (tree var) -{ - if (!var) - return; - gcc_assert (TREE_CODE (var) == VAR_DECL); - - DECL_ARTIFICIAL (var) = 1; - DECL_EXTERNAL (var) = 0; - TREE_STATIC (var) = 1; - TREE_PUBLIC (var) = 0; - TREE_USED (var) = 1; - DECL_CONTEXT (var) = NULL; - TREE_THIS_VOLATILE (var) = 0; - TREE_ADDRESSABLE (var) = 0; - TREE_READONLY (var) = 0; - if (is_global_var (var)) - set_decl_tls_model (var, TLS_MODEL_NONE); -} - tree ipa_struct_relayout::create_new_vars (tree type, const char *name) { @@ -2989,6 +3069,19 @@ ipa_struct_reorg::find_vars (gimple *stmt) records the right value _1 declaration. */ find_var (gimple_assign_rhs1 (stmt), stmt); + /* Pointer types from non-zero pointer need to be escaped in pointer + compression and complete relayout. + e.g _1->t = (struct *) 0x400000. */ + if (current_layout_opt_level >= COMPLETE_STRUCT_RELAYOUT + && TREE_CODE (lhs) == COMPONENT_REF + && TREE_CODE (TREE_TYPE (lhs)) == POINTER_TYPE + && TREE_CODE (rhs) == INTEGER_CST + && !integer_zerop (rhs)) + { + mark_type_as_escape (inner_type (TREE_TYPE (lhs)), + escape_cast_int, stmt); + } + /* Add a safe func mechanism. */ bool l_find = true; bool r_find = true; @@ -3440,12 +3533,13 @@ is_result_of_mult (tree arg, tree *num, tree struct_size) bool ipa_struct_reorg::handled_allocation_stmt (gimple *stmt) { - if ((current_layout_opt_level >= STRUCT_REORDER_FIELDS) + if ((current_layout_opt_level & STRUCT_REORDER_FIELDS) && (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC) || gimple_call_builtin_p (stmt, BUILT_IN_MALLOC) || gimple_call_builtin_p (stmt, BUILT_IN_CALLOC))) return true; - if ((current_layout_opt_level == COMPLETE_STRUCT_RELAYOUT) + if ((current_layout_opt_level == COMPLETE_STRUCT_RELAYOUT + || current_layout_opt_level & POINTER_COMPRESSION_SAFE) && gimple_call_builtin_p (stmt, BUILT_IN_CALLOC)) return true; if ((current_layout_opt_level == STRUCT_SPLIT) @@ -3567,14 +3661,19 @@ ipa_struct_reorg::maybe_mark_or_record_other_side (tree side, tree other, } } /* x_1 = y.x_nodes; void *x; - Directly mark the structure pointer type assigned - to the void* variable as escape. */ + Mark the structure pointer type assigned + to the void* variable as escape. Unless the void* is only used to compare + with variables of the same type. */ else if (current_layout_opt_level >= STRUCT_REORDER_FIELDS && TREE_CODE (side) == SSA_NAME && VOID_POINTER_P (TREE_TYPE (side)) && SSA_NAME_VAR (side) && VOID_POINTER_P (TREE_TYPE (SSA_NAME_VAR (side)))) - mark_type_as_escape (TREE_TYPE (other), escape_cast_void, stmt); + if (current_layout_opt_level < POINTER_COMPRESSION_SAFE + || !safe_void_cmp_p (side, type)) + { + mark_type_as_escape (TREE_TYPE (other), escape_cast_void, stmt); + } check_ptr_layers (side, other, stmt); } @@ -4184,7 +4283,7 @@ ipa_struct_reorg::check_type_and_push (tree newdecl, srdecl *decl, void ipa_struct_reorg::check_alloc_num (gimple *stmt, srtype *type) { - if (current_layout_opt_level == COMPLETE_STRUCT_RELAYOUT + if (current_layout_opt_level >= COMPLETE_STRUCT_RELAYOUT && handled_allocation_stmt (stmt)) { tree arg0 = gimple_call_arg (stmt, 0); @@ -4203,6 +4302,23 @@ ipa_struct_reorg::check_alloc_num (gimple *stmt, srtype *type) type->has_alloc_array = type->has_alloc_array < 0 ? type->has_alloc_array : type->has_alloc_array + 1; + + if (current_layout_opt_level & POINTER_COMPRESSION_SAFE + && TREE_CODE (arg0) == INTEGER_CST) + { + /* Only known size during compilation can be optimized + at this level. */ + unsigned HOST_WIDE_INT max_alloc_size = 0; + switch (compressed_size) + { + case 8: max_alloc_size = 0xff; break; // max of uint8 + case 16: max_alloc_size = 0xffff; break; // max of uint16 + case 32: max_alloc_size = 0xffffffff; break; // max of uint32 + default: gcc_unreachable (); break; + } + if (tree_to_uhwi (arg0) < max_alloc_size) + type->has_legal_alloc_num = true; + } } } @@ -4330,7 +4446,13 @@ ipa_struct_reorg::check_definition (srdecl *decl, vec &worklist) if (current_layout_opt_level >= STRUCT_REORDER_FIELDS && SSA_NAME_VAR (ssa_name) && VOID_POINTER_P (TREE_TYPE (SSA_NAME_VAR (ssa_name)))) - type->mark_escape (escape_cast_void, SSA_NAME_DEF_STMT (ssa_name)); + { + if (current_layout_opt_level < POINTER_COMPRESSION_SAFE + || !safe_void_cmp_p (ssa_name, type)) + { + type->mark_escape (escape_cast_void, SSA_NAME_DEF_STMT (ssa_name)); + } + } gimple *stmt = SSA_NAME_DEF_STMT (ssa_name); /* @@ -5296,6 +5418,8 @@ ipa_struct_reorg::create_new_types (void) for (unsigned i = 0; i < types.length (); i++) newtypes += types[i]->create_new_type (); + /* Some new types may not have been created at create_new_type (), so + recreate new type for all struct fields. */ if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) { for (unsigned i = 0; i < types.length (); i++) @@ -5306,9 +5430,18 @@ ipa_struct_reorg::create_new_types (void) for (unsigned j = 0; j < fields->length (); j++) { tree field = (*fields)[j]; - TREE_TYPE (field) - = reconstruct_complex_type (TREE_TYPE (field), - types[i]->newtype[0]); + if (types[i]->pc_candidate) + { + TREE_TYPE (field) + = make_unsigned_type (compressed_size); + SET_DECL_ALIGN (field, compressed_size); + } + else + { + TREE_TYPE (field) + = reconstruct_complex_type (TREE_TYPE (field), + types[i]->newtype[0]); + } } } } @@ -5687,6 +5820,556 @@ ipa_struct_reorg::rewrite_expr (tree expr, return true; } +/* Emit a series of gimples to compress the pointer to the index relative to + the global header. The basic blocks where gsi is located must have at least + one stmt. */ + +tree +ipa_struct_reorg::compress_ptr_to_offset (tree xhs, srtype *type, + gimple_stmt_iterator *gsi) +{ + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nCompress candidate pointer:\n"); + print_generic_expr (dump_file, xhs); + fprintf (dump_file, "\nto offset:\n"); + } + + /* Emit gimple _X1 = ptr - gptr. */ + tree pointer_addr = fold_convert (long_unsigned_type_node, xhs); + tree gptr_addr = fold_convert (long_unsigned_type_node, type->pc_gptr); + tree step1 = gimplify_build2 (gsi, MINUS_EXPR, long_unsigned_type_node, + pointer_addr, gptr_addr); + + /* Emit gimple _X2 = _X1 / sizeof (struct). */ + tree step2 = gimplify_build2 (gsi, TRUNC_DIV_EXPR, long_unsigned_type_node, + step1, TYPE_SIZE_UNIT (type->newtype[0])); + + /* Emit gimple _X3 = _X2 + 1. */ + tree step3 = gimplify_build2 (gsi, PLUS_EXPR, long_unsigned_type_node, + step2, build_one_cst (long_unsigned_type_node)); + + /* Emit _X4 = (compressed_size) _X3. */ + tree step4 = gimplify_build1 (gsi, NOP_EXPR, + make_unsigned_type (compressed_size), step3); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + print_generic_expr (dump_file, step3); + fprintf (dump_file, "\n"); + } + return step4; +} + +/* Emit a series of gimples to decompress the index into the original + pointer. The basic blocks where gsi is located must have at least + one stmt. */ + +tree +ipa_struct_reorg::decompress_offset_to_ptr (tree xhs, srtype *type, + gimple_stmt_iterator *gsi) +{ + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nDecompress candidate offset:\n"); + print_generic_expr (dump_file, xhs); + fprintf (dump_file, "\nto pointer:\n"); + } + + /* Emit _X1 = xhs - 1. */ + tree offset = fold_convert (long_unsigned_type_node, xhs); + tree step1 = gimplify_build2 (gsi, MINUS_EXPR, long_unsigned_type_node, + offset, + build_one_cst (long_unsigned_type_node)); + + /* Emit _X2 = _X1 * sizeof (struct). */ + tree step2 = gimplify_build2 (gsi, MULT_EXPR, long_unsigned_type_node, + step1, TYPE_SIZE_UNIT (type->newtype[0])); + + /* Emit _X3 = phead + _X2. */ + tree gptr_addr = fold_convert (long_unsigned_type_node, type->pc_gptr); + tree step3 = gimplify_build2 (gsi, PLUS_EXPR, long_unsigned_type_node, + gptr_addr, step2); + + /* Emit _X4 = (struct *) _X3. */ + tree step4 = gimplify_build1 (gsi, NOP_EXPR, TREE_TYPE (type->pc_gptr), + step3); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + print_generic_expr (dump_file, step3); + fprintf (dump_file, "\n"); + } + return step4; +} + +/* Return the compression candidate srtype of SSA_NAME or COMPONENT_REF. */ + +srtype * +ipa_struct_reorg::get_compression_candidate_type (tree xhs) +{ + if (xhs == NULL_TREE) + return NULL; + + if (TREE_CODE (xhs) == SSA_NAME || TREE_CODE (xhs) == COMPONENT_REF) + { + srtype *access_type = find_type (inner_type (TREE_TYPE (xhs))); + if (access_type != NULL && access_type->pc_candidate) + return access_type; + } + return NULL; +} + +/* True if the input type is the candidate type for pointer compression. */ + +bool +ipa_struct_reorg::pc_candidate_st_type_p (tree type) +{ + if (type == NULL_TREE) + return false; + + if (TREE_CODE (type) == POINTER_TYPE) + { + if (TREE_CODE (TREE_TYPE (type)) == RECORD_TYPE) + { + srtype *access_type = find_type (TREE_TYPE (type)); + if (access_type != NULL && access_type->pc_candidate) + return true; + } + } + return false; +} + +/* True if the input xhs is a candidate for pointer compression. */ + +bool +ipa_struct_reorg::pc_candidate_tree_p (tree xhs) +{ + if (xhs == NULL_TREE) + return false; + + if (TREE_CODE (xhs) == COMPONENT_REF) + { + srtype *base_type = find_type (TREE_TYPE (TREE_OPERAND (xhs, 0))); + if (base_type == NULL || base_type->has_escaped ()) + return false; + + return pc_candidate_st_type_p (TREE_TYPE (xhs)); + } + return false; +} + +/* True if xhs is a component_ref that base has escaped but uses a compression + candidate type. */ + +bool +ipa_struct_reorg::pc_type_conversion_candidate_p (tree xhs) +{ + if (xhs == NULL_TREE) + return false; + + if (TREE_CODE (xhs) == COMPONENT_REF) + { + srtype *base_type = find_type (TREE_TYPE (TREE_OPERAND (xhs, 0))); + if (base_type != NULL && base_type->has_escaped ()) + return pc_candidate_st_type_p (TREE_TYPE (xhs)); + + } + return false; +} + +/* Creates a new basic block with zero for compressed null pointers. */ + +basic_block +ipa_struct_reorg::create_bb_for_compress_nullptr (basic_block last_bb, + tree &phi) +{ + basic_block new_bb = create_empty_bb (last_bb); + if (last_bb->loop_father != NULL) + { + add_bb_to_loop (new_bb, last_bb->loop_father); + loops_state_set (LOOPS_NEED_FIXUP); + } + + /* Emit phi = 0. */ + gimple_stmt_iterator gsi = gsi_last_bb (new_bb); + phi = make_ssa_name (make_unsigned_type (compressed_size)); + tree rhs = build_int_cst (make_unsigned_type (compressed_size), 0); + gimple *new_stmt = gimple_build_assign (phi, rhs); + gsi_insert_after (&gsi, new_stmt, GSI_NEW_STMT); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nCreate bb %d for compress nullptr:\n", + new_bb->index); + gimple_dump_bb (dump_file, new_bb, 0, dump_flags); + } + return new_bb; +} + +/* Create a new basic block to compress the pointer to the index relative to + the allocated memory pool header. */ + +basic_block +ipa_struct_reorg::create_bb_for_compress_candidate (basic_block last_bb, + tree new_rhs, srtype *type, + tree &phi) +{ + basic_block new_bb = create_empty_bb (last_bb); + if (last_bb->loop_father != NULL) + { + add_bb_to_loop (new_bb, last_bb->loop_father); + loops_state_set (LOOPS_NEED_FIXUP); + } + + gimple_stmt_iterator gsi = gsi_last_bb (new_bb); + /* compress_ptr_to_offset () needs at least one stmt in target bb. */ + gsi_insert_after (&gsi, gimple_build_nop (), GSI_NEW_STMT); + phi = compress_ptr_to_offset (new_rhs, type, &gsi); + /* Remove the NOP created above. */ + gsi_remove (&gsi, true); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nCreate bb %d for compress candidate:\n", + new_bb->index); + gimple_dump_bb (dump_file, new_bb, 0, dump_flags); + } + return new_bb; +} + +/* Compression can be simplified by these following cases: + 1. if rhs is NULL, uses zero to represent it. + 2. if new_rhs has been converted into INTEGER_TYPE in the previous stmt, + just use it here. For example: + _1 = t->s + -> tt->s = _1. */ + +bool +ipa_struct_reorg::pc_direct_rewrite_chance_p (tree rhs, tree &new_rhs) +{ + if (integer_zerop (rhs)) + { + new_rhs = build_int_cst (make_unsigned_type (compressed_size), 0); + return true; + } + else if (new_rhs && TREE_CODE (TREE_TYPE (new_rhs)) == INTEGER_TYPE) + { + return true; + } + return false; +} + +/* Perform pointer compression with check. The conversion will be as shown in + the following example: + Orig bb: + bb <1>: + _1->t = _2 + + will be transformed to: + bb <1>: + _3 = _2 + if (_2 == NULL) + goto bb <2> + else + goto bb <3> + + bb <2>: + _3 = 0 + goto bb <4> + + bb <3>: + ... + _4 = compress (_2) + goto bb <4> + + bb <4>: + _5 = PHI (_3, _4) + _1->t = _5 + The gsi will move to the beginning of split dst bb <4>, _1->t = _5 will be + emitted by rewrite_assign (). */ + +bool +ipa_struct_reorg::compress_candidate_with_check (gimple_stmt_iterator *gsi, + tree rhs, tree &new_rhs) +{ + tree cond_lhs = make_ssa_name (TREE_TYPE (new_rhs)); + gimple *assign_stmt = gimple_build_assign (cond_lhs, new_rhs); + gsi_insert_before (gsi, assign_stmt, GSI_SAME_STMT); + + /* Insert cond stmt. */ + tree rhs_pointer_type = build_pointer_type (TREE_TYPE (new_rhs)); + gcond *cond = gimple_build_cond (EQ_EXPR, cond_lhs, + build_int_cst (rhs_pointer_type, 0), + NULL_TREE, NULL_TREE); + gimple_set_location (cond, UNKNOWN_LOCATION); + gsi_insert_before (gsi, cond, GSI_SAME_STMT); + + gimple* cur_stmt = as_a (cond); + edge e = split_block (cur_stmt->bb, cur_stmt); + basic_block split_src_bb = e->src; + basic_block split_dst_bb = e->dest; + + /* Create bb for nullptr. */ + tree phi1 = NULL_TREE; + basic_block true_bb = create_bb_for_compress_nullptr (split_src_bb, phi1); + + /* Create bb for comprssion. */ + srtype *type = get_compression_candidate_type (rhs); + gcc_assert (type != NULL); + tree phi2 = NULL_TREE; + basic_block false_bb = create_bb_for_compress_candidate (true_bb, new_rhs, + type, phi2); + + /* Rebuild and reset cfg. */ + remove_edge_raw (e); + + edge etrue = make_edge (split_src_bb, true_bb, EDGE_TRUE_VALUE); + etrue->probability = profile_probability::unlikely (); + true_bb->count = etrue->count (); + + edge efalse = make_edge (split_src_bb, false_bb, EDGE_FALSE_VALUE); + efalse->probability = profile_probability::likely (); + false_bb->count = efalse->count (); + + edge e1 = make_single_succ_edge (true_bb, split_dst_bb, EDGE_FALLTHRU); + edge e2 = make_single_succ_edge (false_bb, split_dst_bb, EDGE_FALLTHRU); + + tree phi = make_ssa_name (make_unsigned_type (compressed_size)); + gphi *phi_node = create_phi_node (phi, split_dst_bb); + add_phi_arg (phi_node, phi1, e1, UNKNOWN_LOCATION); + add_phi_arg (phi_node, phi2, e2, UNKNOWN_LOCATION); + + if (dom_info_available_p (CDI_DOMINATORS)) + { + set_immediate_dominator (CDI_DOMINATORS, split_dst_bb, split_src_bb); + set_immediate_dominator (CDI_DOMINATORS, true_bb, split_src_bb); + set_immediate_dominator (CDI_DOMINATORS, false_bb, split_src_bb); + } + *gsi = gsi_start_bb (split_dst_bb); + new_rhs = phi; + return true; +} + +/* If there is a direct rewrite chance or simplification opportunity, perform + the simplified compression rewrite. Otherwise, create a cond expression and + two basic blocks to implement pointer compression. */ + +bool +ipa_struct_reorg::compress_candidate (gassign *stmt, gimple_stmt_iterator *gsi, + tree rhs, tree &new_rhs) +{ + if (pc_direct_rewrite_chance_p (rhs, new_rhs)) + return true; + + return compress_candidate_with_check (gsi, rhs, new_rhs); +} + +/* Create a new basic block to decompress the index to null pointer. */ + +basic_block +ipa_struct_reorg::create_bb_for_decompress_nullptr (basic_block last_bb, + tree new_rhs, + tree &phi_node) +{ + basic_block new_bb = create_empty_bb (last_bb); + if (last_bb->loop_father != NULL) + { + add_bb_to_loop (new_bb, last_bb->loop_father); + loops_state_set (LOOPS_NEED_FIXUP); + } + gimple_stmt_iterator gsi = gsi_last_bb (new_bb); + tree rhs_pointer_type = build_pointer_type (TREE_TYPE (new_rhs)); + phi_node = make_ssa_name (rhs_pointer_type); + gimple *new_stmt = gimple_build_assign (phi_node, + build_int_cst (rhs_pointer_type, 0)); + gsi_insert_after (&gsi, new_stmt, GSI_NEW_STMT); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nCreate bb %d for decompress nullptr:\n", + new_bb->index); + gimple_dump_bb (dump_file, new_bb, 0, dump_flags); + } + return new_bb; +} + +/* Create a new basic block to decompress the index into original pointer. */ + +basic_block +ipa_struct_reorg::create_bb_for_decompress_candidate (basic_block last_bb, + tree lhs, srtype *type, + tree &phi_node) +{ + basic_block new_bb = create_empty_bb (last_bb); + if (last_bb->loop_father != NULL) + { + add_bb_to_loop (new_bb, last_bb->loop_father); + loops_state_set (LOOPS_NEED_FIXUP); + } + gimple_stmt_iterator gsi = gsi_last_bb (new_bb); + /* decompress_ptr_to_offset () needs at least one stmt in target bb. */ + gsi_insert_after (&gsi, gimple_build_nop (), GSI_NEW_STMT); + phi_node = decompress_offset_to_ptr (lhs, type, &gsi); + /* Remove the NOP created above. */ + gsi_remove (&gsi, true); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nCreate bb %d for decompress candidate:\n", + new_bb->index); + gimple_dump_bb (dump_file, new_bb, 0, dump_flags); + } + return new_bb; +} + +/* Perform pointer decompression with check. The conversion will be as shown + in the following example: + Orig bb: + bb <1>: + _1 = _2->t + + will be transformed to: + bb <1>: + _3 = _2->t + if (_3 == 0) + goto bb <2> + else + goto bb <3> + + bb <2>: + _4 = NULL + goto bb <4> + + bb <3>: + ... + _5 = decompress (_3) + goto bb <4> + + bb <4>: + _6 = PHI (_4, _5) + _1 = _6 + The gsi will move to the beginning of split dst bb <4>, _1 = _6 will be + emitted by rewrite_assign (). */ + +bool +ipa_struct_reorg::decompress_candidate_with_check (gimple_stmt_iterator *gsi, + tree rhs, tree &new_rhs) +{ + /* Insert cond stmt. */ + tree cond_lhs = make_ssa_name (TREE_TYPE (new_rhs)); + gassign *cond_assign = gimple_build_assign (cond_lhs, new_rhs); + gsi_insert_before (gsi, cond_assign, GSI_SAME_STMT); + + tree pc_type = make_unsigned_type (compressed_size); + gcond *cond = gimple_build_cond (EQ_EXPR, cond_lhs, + build_int_cst (pc_type, 0), + NULL_TREE, NULL_TREE); + gimple_set_location (cond, UNKNOWN_LOCATION); + gsi_insert_before (gsi, cond, GSI_SAME_STMT); + + /* Split bb. */ + gimple* cur_stmt = as_a (cond); + edge e = split_block (cur_stmt->bb, cur_stmt); + basic_block split_src_bb = e->src; + basic_block split_dst_bb = e->dest; + + /* Create bb for decompress nullptr. */ + tree phi1 = NULL_TREE; + basic_block true_bb = create_bb_for_decompress_nullptr (split_src_bb, + new_rhs, phi1); + + /* Create bb for decomprssion candidate. */ + tree phi2 = NULL_TREE; + srtype *type = get_compression_candidate_type (rhs); + gcc_assert (type != NULL); + basic_block false_bb = create_bb_for_decompress_candidate (true_bb, cond_lhs, + type, phi2); + + /* Refresh and reset cfg. */ + remove_edge_raw (e); + + edge etrue = make_edge (split_src_bb, true_bb, EDGE_TRUE_VALUE); + etrue->probability = profile_probability::unlikely (); + true_bb->count = etrue->count (); + + edge efalse = make_edge (split_src_bb, false_bb, EDGE_FALSE_VALUE); + efalse->probability = profile_probability::likely (); + false_bb->count = efalse->count (); + + edge e1 = make_single_succ_edge (true_bb, split_dst_bb, EDGE_FALLTHRU); + edge e2 = make_single_succ_edge (false_bb, split_dst_bb, EDGE_FALLTHRU); + + tree phi = make_ssa_name (build_pointer_type (TREE_TYPE (cond_lhs))); + gphi *phi_node = create_phi_node (phi, split_dst_bb); + add_phi_arg (phi_node, phi1, e1, UNKNOWN_LOCATION); + add_phi_arg (phi_node, phi2, e2, UNKNOWN_LOCATION); + + if (dom_info_available_p (CDI_DOMINATORS)) + { + set_immediate_dominator (CDI_DOMINATORS, split_dst_bb, split_src_bb); + set_immediate_dominator (CDI_DOMINATORS, true_bb, split_src_bb); + set_immediate_dominator (CDI_DOMINATORS, false_bb, split_src_bb); + } + *gsi = gsi_start_bb (split_dst_bb); + new_rhs = phi; + return true; +} + +/* If there is a simplification opportunity, perform the simplified + decompression rewrite. Otherwise, create a cond expression and two basic + blocks to implement pointer decompression. */ + +bool +ipa_struct_reorg::decompress_candidate (gimple_stmt_iterator *gsi, + tree lhs, tree rhs, tree &new_lhs, + tree &new_rhs) +{ + // TODO: simplifiy check and rewrite will be pushed in next PR. + return decompress_candidate_with_check (gsi, rhs, new_rhs); +} + +/* Try to perform pointer compression and decompression. */ + +void +ipa_struct_reorg::try_rewrite_with_pointer_compression (gassign *stmt, + gimple_stmt_iterator + *gsi, tree lhs, + tree rhs, tree &new_lhs, + tree &new_rhs) +{ + bool l = pc_candidate_tree_p (lhs); + bool r = pc_candidate_tree_p (rhs); + if (!l && !r) + { + tree tmp_rhs = new_rhs == NULL_TREE ? rhs : new_rhs; + if (pc_type_conversion_candidate_p (lhs)) + { + /* Transfer MEM[(struct *)_1].files = _4; + to MEM[(struct *)_1].files = (struct *)_4; */ + new_rhs = fold_convert (TREE_TYPE (lhs), tmp_rhs); + } + else if (pc_type_conversion_candidate_p (rhs)) + { + /* Transfer _4 = MEM[(struct *)_1].nodes; + to _4 = (new_struct *) MEM[(struct *)_1].nodes; */ + new_rhs = fold_convert (TREE_TYPE (new_lhs), tmp_rhs); + } + } + else if (l && r) + gcc_unreachable (); + else if (l) + { + if (!compress_candidate (stmt, gsi, rhs, new_rhs)) + gcc_unreachable (); + } + else if (r) + { + if (!decompress_candidate (gsi, lhs, rhs, new_lhs, new_rhs)) + gcc_unreachable (); + } +} + bool ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) { @@ -5882,6 +6565,9 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) fprintf (dump_file, "\nreplaced with:\n"); for (unsigned i = 0; i < max_split && (newlhs[i] || newrhs[i]); i++) { + if (current_layout_opt_level >= POINTER_COMPRESSION_SAFE) + try_rewrite_with_pointer_compression (stmt, gsi, lhs, rhs, + newlhs[i], newrhs[i]); gimple *newstmt = gimple_build_assign (newlhs[i] ? newlhs[i] : lhs, newrhs[i] ? newrhs[i] : rhs); if (dump_file && (dump_flags & TDF_DETAILS)) @@ -5958,6 +6644,13 @@ ipa_struct_reorg::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi) gcc_assert (false); gimple_call_set_lhs (g, decl->newdecl[i]); gsi_insert_before (gsi, g, GSI_SAME_STMT); + if (type->pc_candidate) + { + /* Init global header for pointer compression. */ + gassign *gptr + = gimple_build_assign (type->pc_gptr, decl->newdecl[i]); + gsi_insert_before (gsi, gptr, GSI_SAME_STMT); + } } return true; } @@ -6413,6 +7106,12 @@ ipa_struct_reorg::rewrite_functions (void) push_cfun (DECL_STRUCT_FUNCTION (node->decl)); current_function = f; + if (current_layout_opt_level >= POINTER_COMPRESSION_SAFE) + { + calculate_dominance_info (CDI_DOMINATORS); + loop_optimizer_init (0); + } + if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "\nBefore rewrite: %dth_%s\n", @@ -6488,6 +7187,9 @@ ipa_struct_reorg::rewrite_functions (void) free_dominance_info (CDI_DOMINATORS); + if (current_layout_opt_level >= POINTER_COMPRESSION_SAFE) + loop_optimizer_finalize (); + if (dump_file) { fprintf (dump_file, "\nAfter rewrite: %dth_%s\n", @@ -6516,6 +7218,8 @@ ipa_struct_reorg::execute_struct_relayout (void) continue; if (types[i]->chain_type) continue; + if (get_type_name (types[i]->type) == NULL) + continue; retval |= ipa_struct_relayout (type, this).execute (); } @@ -6532,6 +7236,131 @@ ipa_struct_reorg::execute_struct_relayout (void) return retval; } +/* True if the var with void type is only used to compare with the same + target type. */ + +bool +ipa_struct_reorg::safe_void_cmp_p (tree var, srtype *type) +{ + imm_use_iterator imm_iter; + use_operand_p use_p; + FOR_EACH_IMM_USE_FAST (use_p, imm_iter, var) + { + gimple *use_stmt = USE_STMT (use_p); + if (is_gimple_debug (use_stmt)) + continue; + + if (gimple_code (use_stmt) == GIMPLE_COND) + { + tree lhs = gimple_cond_lhs (use_stmt); + tree rhs = gimple_cond_rhs (use_stmt); + tree xhs = lhs == var ? rhs : lhs; + if (types_compatible_p (inner_type (TREE_TYPE (xhs)), type->type)) + continue; + + } + return false; + } + return true; +} + +/* Mark the structure that should perform pointer compression. */ + +void +ipa_struct_reorg::check_and_prune_struct_for_pointer_compression (void) +{ + unsigned pc_transform_num = 0; + + if (dump_file) + fprintf (dump_file, "\nMark the structure that should perform pointer" + " compression:\n"); + + for (unsigned i = 0; i < types.length (); i++) + { + srtype *type = types[i]; + if (dump_file) + print_generic_expr (dump_file, type->type); + + if (type->has_escaped ()) + { + if (dump_file) + fprintf (dump_file, " has escaped by %s, skip compression.\n", + type->escape_reason ()); + continue; + } + if (TYPE_FIELDS (type->type) == NULL) + { + if (dump_file) + fprintf (dump_file, " has zero field, skip compression.\n"); + continue; + } + if (type->chain_type) + { + if (dump_file) + fprintf (dump_file, " is chain_type, skip compression.\n"); + continue; + } + if (type->has_alloc_array != 1) + { + if (dump_file) + fprintf (dump_file, " has alloc number: %d, skip compression.\n", + type->has_alloc_array); + continue; + } + if (get_type_name (type->type) == NULL) + { + if (dump_file) + fprintf (dump_file, " has empty struct name," + " skip compression.\n"); + continue; + } + if ((current_layout_opt_level & POINTER_COMPRESSION_SAFE) + && !type->has_legal_alloc_num) + { + if (dump_file) + fprintf (dump_file, " has illegal struct array size," + " skip compression.\n"); + continue; + } + pc_transform_num++; + type->pc_candidate = true; + if (dump_file) + fprintf (dump_file, " attemps to do pointer compression.\n"); + } + + if (dump_file) + { + if (pc_transform_num) + fprintf (dump_file, "\nNumber of structures to transform in " + "pointer compression is %d\n", pc_transform_num); + else + fprintf (dump_file, "\nNo structures to transform in " + "pointer compression.\n"); + } +} + +/* Init pointer size from parameter param_pointer_compression_size. */ + +static void +init_pointer_size_for_pointer_compression (void) +{ + switch (param_pointer_compression_size) + { + case 8: + compressed_size = 8; // sizeof (uint8) + break; + case 16: + compressed_size = 16; // sizeof (uint16) + break; + case 32: + compressed_size = 32; // sizeof (uint32) + break; + default: + error ("Invalid pointer compression size, using the following param: " + "\"--param pointer-compression-size=[8,16,32]\""); + } +} + unsigned int ipa_struct_reorg::execute (unsigned int opt) { @@ -6553,6 +7382,8 @@ ipa_struct_reorg::execute (unsigned int opt) if (current_layout_opt_level == STRUCT_SPLIT) analyze_types (); + if (opt >= POINTER_COMPRESSION_SAFE) + check_and_prune_struct_for_pointer_compression (); ret = rewrite_functions (); } else @@ -6600,6 +7431,8 @@ public: unsigned int level = 0; switch (struct_layout_optimize_level) { + case 4: level |= POINTER_COMPRESSION_SAFE; + // FALLTHRU case 3: level |= DEAD_FIELD_ELIMINATION; // FALLTHRU case 2: level |= STRUCT_REORDER_FIELDS; @@ -6611,6 +7444,10 @@ public: case 0: break; default: gcc_unreachable (); } + + if (level & POINTER_COMPRESSION_SAFE) + init_pointer_size_for_pointer_compression (); + /* Preserved for backward compatibility, reorder fields needs run before struct split and complete struct relayout. */ if (flag_ipa_reorder_fields && level < STRUCT_REORDER_FIELDS) diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.h b/gcc/ipa-struct-reorg/ipa-struct-reorg.h index 719f7b3088c..6c4469597c4 100644 --- a/gcc/ipa-struct-reorg/ipa-struct-reorg.h +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.h @@ -121,7 +121,10 @@ private: public: tree newtype[max_split]; + tree pc_gptr; bool visited; + bool pc_candidate; + bool has_legal_alloc_num; /* Negative number means it has illegal allocated arrays that we do not optimize. */ int has_alloc_array; @@ -145,6 +148,7 @@ public: void analyze (void); bool has_dead_field (void); void mark_escape (escape_type, gimple *stmt); + void create_global_ptr_for_pc (); bool has_escaped (void) { return escapes != does_not_escape; diff --git a/gcc/params.opt b/gcc/params.opt index 1ddf1343f0a..d2196dc6806 100644 --- a/gcc/params.opt +++ b/gcc/params.opt @@ -1205,4 +1205,8 @@ Enum(vrp_mode) String(vrp) Value(VRP_MODE_VRP) EnumValue Enum(vrp_mode) String(ranger) Value(VRP_MODE_RANGER) +-param=compressed-pointer-size= +Common Joined UInteger Var(param_pointer_compression_size) Init(32) IntegerRange(8, 32) Param Optimization +Target size of compressed pointer, which should be 8, 16 or 32. + ; This comment is to ensure we retain the blank line above. -- Gitee