diff --git a/gcc/Makefile.in b/gcc/Makefile.in index db2a0e1bda80cfedf36e4c1bdd3f09a709a597d4..d304334709ee64d958d3cfaf6101f35e518608c2 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 e365a48bca6a281adbbf54e7e12bc44e5a70df75..c4a473ee2f9c737e96cf5b64b570a1b9ea4a3494 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1949,9 +1949,18 @@ 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 Ignore -Does nothing. Preserved for backward compatibility. +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, 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/configure b/gcc/configure index c749ace011dbdbe2632413cc6860a5e8597508e4..98bbf0f857b47e141d58e3a34e6fc936d602b235 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 992a50e7b20f7c147a333f9598407ea5920ad777..c74f4b55527d45e222aad15ba66dc203820b66a8 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 ff8cd032faba32a37693fbfba57db506d8b1cabb..20dd24df569f4a6adf77b2f4ed8eb0b4a3f1b4e3 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -526,6 +526,8 @@ 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 -flive-patching=@var{level} @gol @@ -11886,6 +11888,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 +13787,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 8d088ad33f2fa7122600df48c8ac372006c89be1..7f5c92c96f9406b1f62cdfd5963096a5404b549d 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 || flag_ipa_reorder_fields) + return false; + return (warn_free_nonheap_object || warn_mismatched_alloc || warn_mismatched_new_delete); diff --git a/gcc/ipa-free-lang-data.cc b/gcc/ipa-free-lang-data.cc index a742156858cdd79589e267cb86920db7779d7f0c..801e95ceaeb8b1f316e8f1545d39739ae4d90282 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. */ @@ -102,6 +105,13 @@ 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 && lang_c_p () + && flag_lto_partition == LTO_PARTITION_ONE) + 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 +350,12 @@ 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 && lang_c_p () + && flag_lto_partition == LTO_PARTITION_ONE) + 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-param-manipulation.cc b/gcc/ipa-param-manipulation.cc index 38328c3e8d0aef7715cd6b9357d174b063ed8c2a..f9e956008d88a2b566f2f8bbe44ee028b2e91264 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 a9ad2b216bea4001d48ba06ab09fabbcd0d22478..71f4a0a2f08ec5dcac4c119dfbf91e7a20b6e0f9 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 0000000000000000000000000000000000000000..996a09bace265dfd1ee69fc51d1ed49a242afd3e --- /dev/null +++ b/gcc/ipa-struct-reorg/escapes.def @@ -0,0 +1,65 @@ +/* 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]") +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 new file mode 100644 index 0000000000000000000000000000000000000000..a6eb8f7a804a6fb6b94d6a96b9c80a1d61bbc06b --- /dev/null +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc @@ -0,0 +1,7493 @@ +/* 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 "gimple-walk.h" +#include "cfg.h" +#include "cfghooks.h" /* For split_block. */ +#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" +#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; +using namespace struct_relayout; + +#define VOID_POINTER_P(type) \ + (POINTER_TYPE_P (type) && VOID_TYPE_P (TREE_TYPE (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) +{ + 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); +} + +/* 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 the struct layout optimize level, + which should be the same as the option -fstruct-reorg=. */ + +enum struct_layout_opt_level +{ + NONE = 0, + STRUCT_SPLIT = 1 << 0, + COMPLETE_STRUCT_RELAYOUT = 1 << 1, + STRUCT_REORDER_FIELDS = 1 << 2, + 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); + +static unsigned int current_layout_opt_level; +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 + +namespace struct_reorg { + +hash_map > fields_to_finish; + +/* Constructor of srfunction. */ + +srfunction::srfunction (cgraph_node *n) + : node (n), + old (NULL), + newnode (NULL), + newf (NULL), + is_safe_func (false) +{ +} + +/* 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), + field_access (EMPTY_FIELD) +{ + 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), + 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++) + 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); + } + } +} + +/* 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 +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"); + } +} + +/* 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 +srtype::add_field_site (srfield *field) +{ + field_sites.safe_push (field); +} + +/* Constructor of DECL. */ + +srdecl::srdecl (srtype *tp, tree decl, int argnum, tree orig_type) + : type (tp), + decl (decl), + func (NULL_TREE), + argumentnum (argnum), + visited (false), + orig_type (orig_type) +{ + 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, tree orig_type) +{ + // Search for the decl to see if it is already there. + srdecl *decl1 = find_decl (decl); + + if (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); + + 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; +} + +/* 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\"", escape_reason ()); + fprintf (f, "\nfields = {\n"); + FOR_EACH_VEC_ELT (fields, i, field) + field->dump (f); + fprintf (f, "}\n "); + + fprintf (f, "\naccesses = {\n"); + FOR_EACH_VEC_ELT (accesses, i, access) + access->dump (f); + fprintf (f, "}\n "); + + fprintf (f, "\nfunctions = {\n"); + FOR_EACH_VEC_ELT (functions, i, fn) + fn->simple_dump (f); + fprintf (f, "}\n"); + fprintf (f, "}\n"); +} + +/* A simplified dump out the type structure to FILE. */ + +void +srtype::simple_dump (FILE *f) +{ + print_generic_expr (f, type); + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) + fprintf (f, "(%d)", TYPE_UID (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) + { + /* 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) + 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]) +{ + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) + { + create_new_reorder_fields (newtype, newfields, newlast); + return; + } + + 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; + } +} + +/* 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); + } + + if (type == NULL) + { + DECL_NAME (field) = DECL_NAME (fielddecl); + /* Common members do not need to reconstruct. + Otherwise, int* -> int** or void* -> void**. */ + 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); + 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); + 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 +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 + && current_layout_opt_level < STRUCT_REORDER_FIELDS) + { + newtype[0] = type; + return false; + } + + /* 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]; + + maxclusters++; + + const char *tname = get_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]) = build_decl (UNKNOWN_LOCATION, + TYPE_DECL, + get_identifier (name), + newtype[i]); + free (name); + } + } + + for (unsigned i = 0; i < fields.length (); i++) + { + srfield *f = fields[i]; + if (current_layout_opt_level & DEAD_FIELD_ELIMINATION + && !(f->field_access & READ_FIELD)) + continue; + 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]); + if (TYPE_NAME (newtype[i]) != NULL) + layout_decl (TYPE_NAME (newtype[i]), 0); + } + + warn_padded = save_warn_padded; + + 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_layout_opt_level & DEAD_FIELD_ELIMINATION + && 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); + 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"); + } + } + + 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 ( + isptrptr (decls[i]->orig_type) ? decls[i]->orig_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"); + 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); + fprintf (f, "}\n"); +} + +/* A simplified dump out the field structure to FILE. */ + +void +srfield::simple_dump (FILE *f) +{ + if (fielddecl) + 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"); + +} + +/* 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_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 +{ +public: + // Constructors + ipa_struct_reorg (void) + : current_function (NULL), + done_recording (false) + {} + + // Fields + auto_vec_del types; + auto_vec_del functions; + srglobal globals; + srfunction *current_function; + hash_set safe_functions; + auto_vec ext_func_types; + + bool done_recording; + + // Methods + unsigned execute (unsigned int opt); + void mark_type_as_escape (tree type, escape_type escapes, + 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); + void detect_cycles (void); + 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); + 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); + 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); + 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); + bool handled_allocation_stmt (gimple *stmt); + tree allocate_size (srtype *t, srdecl *decl, 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& 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, 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); + void mark_types_asm (gasm *astmt); + + bool has_rewritten_type (srfunction *); + 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); + + // 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 +{ +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. */ + +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 +ipa_struct_reorg::dump_types (FILE *f) +{ + unsigned i; + 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"); + } +} + +/* 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) +{ + if (type == NULL) + return false; + 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; +} + +/* Adding node to map and stack. */ + +bool +add_node (tree node, int layers, hash_map &map, + auto_vec &stack) +{ + 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; +} + +/* Check the number of pointer layers of the gimple phi in definition. */ + +bool +check_def_phi (tree def_node, hash_map &ptr_layers) +{ + 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; +} + +/* Check the number of pointer layers of the gimple assign in definition. */ + +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; +} + +/* Check node definition. */ + +bool +check_node_def (hash_map &ptr_layers) +{ + bool res = true; + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\n======== check node definition ========\n"); + for (unsigned i = 1; i < num_ssa_names; ++i) + { + tree name = ssa_name (i); + if (name && ptr_layers.get (name) != 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; +} + +/* 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; +} + +/* Check and record a single node. */ + +bool +check_record_single_node (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); + gcc_assert (rhs_class == GIMPLE_SINGLE_RHS || rhs_class == GIMPLE_UNARY_RHS); + + if ((TREE_CODE (rhs1) != SSA_NAME && TREE_CODE (rhs1) != MEM_REF) + || (TREE_CODE (lhs) != SSA_NAME && TREE_CODE (lhs) != MEM_REF)) + return false; + + 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; +} + +/* Check and record multiple nodes. */ + +bool +check_record_mult_node (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_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; + + 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; +} + +/* Check whether gimple assign is correctly used and record node. */ + +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; + + 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 (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; +} + +/* Check the use of callee. */ + +bool +check_callee (cgraph_node *node, gimple *stmt, + hash_map &ptr_layers, int input_layers) +{ + /* 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++) + { + 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_layout_opt_level < 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_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_layout_opt_level >= 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_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)) + 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)); + 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)); + + /* 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))); + 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)); + + /* Separate instance is hard to trace in complete struct + relayout optimization. */ + if (current_layout_opt_level >= COMPLETE_STRUCT_RELAYOUT + && TREE_CODE (TREE_TYPE (decl)) == RECORD_TYPE) + e = escape_separate_instance; + + 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); + tree orig_type = TREE_TYPE (expr); + 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); + 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); + } + } + + tree base; + bool indirect; + 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, escape_from_base, 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 + || gimple_assign_rhs_code (stmt) == NOP_EXPR) + { + 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); + + /* 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; + if (current_layout_opt_level >= 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)) && 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, + 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, + 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)) && 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, + 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, + isptrptr (TREE_TYPE (lhs)) ? TREE_TYPE (lhs) : NULL); + } + } + } + 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 + || 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_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))), + 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, + 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: + 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; + } +} + +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 +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; + } + if (current_layout_opt_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. */ + +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. */ + +static bool +trace_calculate_diff (gimple *size_def_stmt, tree *num) +{ + 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) + { + *num = NULL_TREE; + return true; + } + *num = NULL_TREE; + return false; +} + +/* 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) + 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 + 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. + 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_layout_opt_level >= STRUCT_REORDER_FIELDS) + return trace_calculate_negate (size_def_stmt, num, struct_size); + else if (rhs_code == NOP_EXPR + && current_layout_opt_level >= STRUCT_REORDER_FIELDS) + return trace_calculate_diff (size_def_stmt, num); + else + { + *num = NULL_TREE; + return false; + } + } + + *num = NULL_TREE; + return false; +} + +/* Return TRUE if STMT is an allocation statement that is handled. */ + +bool +ipa_struct_reorg::handled_allocation_stmt (gimple *stmt) +{ + 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 + || current_layout_opt_level & POINTER_COMPRESSION_SAFE) + && gimple_call_builtin_p (stmt, BUILT_IN_CALLOC)) + return true; + 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) + || 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, srdecl *decl, 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); + + /* 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) + || 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 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)) + { + 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) + { + /* MEM[(struct arc *)_1].head = _2; _2 = calloc (100, 104). */ + if (VOID_POINTER_P (TREE_TYPE (side)) + && TREE_CODE (side) == SSA_NAME) + { + /* 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) + { + 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; + 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)))) + 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); +} + +/* 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. */ + /* 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); + } + 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); + } +} + +static bool +check_mem_ref_offset (tree expr, tree *num) +{ + 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); + /* 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 *num) +{ + 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: + { + /* 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, num)) + 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& escape_from_base, bool should_create, + bool can_escape) +{ + tree num = NULL_TREE; + 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; + } + + /* ref is classified into two types: COMPONENT_REF or MER_REF. */ + base = get_ref_base_and_offset (expr, offset, realpart, imagpart, + accesstype, &num); + + /* 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; + } + /* 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) + 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. + 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_layout_opt_level >= STRUCT_REORDER_FIELDS) + { + 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); + } + } + } + else if (!d) + return false; + else + t = d->type; + + if (t->has_escaped ()) + { + escape_from_base = true; + return false; + } + + if (mark_as_bit_field) + { + gcc_assert (can_escape); + t->mark_escape (escape_bitfields, NULL); + 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_layout_opt_level > STRUCT_SPLIT + && (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; + 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); + } + 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, "\noriginal expr = "); + print_generic_expr (dump_file, expr); + } + 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; + bool escape_from_base = false; + if (!get_type_field (expr, base, indirect, type, field, + realpart, imagpart, address, escape_from_base)) + 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); + srdecl *d = current_function->find_decl ( + 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++) + { + tree arg = gimple_call_arg (stmt, i); + if (argtype) + { + tree argtypet = TREE_VALUE (argtype); + /* callee_func (_1, _2); + Check the callee func, instead of current func. */ + if (!(free_or_realloc + || (current_layout_opt_level >= 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, stmt); + 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; + bool escape_from_base = false; + if (!get_type_field (expr, base, indirect, type, field, + realpart, imagpart, address, escape_from_base)) + return; + + if (current_layout_opt_level > NONE) + { + 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, srdecl *decl, + vec &worklist, gimple *stmt) +{ + srtype *type = decl->type; + 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 + && cmp_ptr_layers (TREE_TYPE (newdecl), TREE_TYPE (decl->decl))) + 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 (replace_type_map.get (type->type) == NULL + && (!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); + 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; + type->mark_escape (escape_cast_another_ptr, stmt); + type1->mark_escape (escape_cast_another_ptr, stmt); +} + +void +ipa_struct_reorg::check_alloc_num (gimple *stmt, srtype *type) +{ + if (current_layout_opt_level >= 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; + + 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; + } + } +} + +/* 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_layout_opt_level >= 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 + 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, SSA_NAME_DEF_STMT (ssa_name)); + return; + } + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS + && SSA_NAME_VAR (ssa_name) + && VOID_POINTER_P (TREE_TYPE (SSA_NAME_VAR (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); + + /* + 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) + 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) + { + 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), + decl, worklist, stmt); + return; + } + if (gimple_code (stmt) == GIMPLE_ASSIGN) + check_definition_assign (decl, 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, decl, 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)); + /* 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. */ + if (current_layout_opt_level == 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_layout_opt_level >= 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_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)) + || !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; + } + else if (b && a == NULL && TREE_CODE (a_expr) != INTEGER_CST) + { + b->type->mark_escape (escape_cast_another_ptr, stmt); + return; + } + else if (a == NULL && b == NULL) + return; + + if (cmp_ptr_layers (TREE_TYPE (a_expr), TREE_TYPE (b_expr))) + return; + + if (a) + a->type->mark_escape (escape_cast_another_ptr, stmt); + if (b) + b->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), decl, 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; + enum tree_code code = gimple_cond_code (stmt); + 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))) + { + 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, 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; + } + + /* 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; + enum tree_code code = gimple_assign_rhs_code (stmt); + 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))) + { + 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, decl, 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; + } + check_ptr_layers (lhs, rhs, stmt); + } + + 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); + 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; + } + +} + +/* + 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) + { + if (current_layout_opt_level < STRUCT_REORDER_FIELDS) + escapes = escape_visible_function; + else if (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; + + if (current_layout_opt_level > NONE) + { + 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_layout_opt_level >= STRUCT_REORDER_FIELDS) + { + current_function->is_safe_func = safe_functions.contains (node); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + 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) + { + 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; +} + + +/* 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 +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); + } + + /* Add a safe func mechanism. */ + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) + record_safe_func_with_void_ptr_parm (); + + FOR_EACH_FUNCTION (cnode) + { + if (!cnode->real_symbol_p ()) + continue; + + /* 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)) + { + 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"); + dump_functions (dump_file); + } + /* If record_var () is called later, new types will not be recorded. */ + 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) + ; + /* If there are two members of the same structure pointer 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); +} + +/* 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_layout_opt_level == STRUCT_SPLIT) + { + /* 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_layout_opt_level >= 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, "==============================================\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"); + 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 () + && current_layout_opt_level < STRUCT_REORDER_FIELDS) + { + 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. */ + /* 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_layout_opt_level < STRUCT_REORDER_FIELDS) + { + 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, "==============================================\n\n"); + fprintf (dump_file, "========= all types (after pruning): =========\n\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 (); + } +} + +/* 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 (); + + /* 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++) + { + auto_vec *fields = fields_to_finish.get (types[i]->type); + if (fields) + { + for (unsigned j = 0; j < fields->length (); j++) + { + tree field = (*fields)[j]; + 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]); + } + } + } + } + for (unsigned i = 0; i < types.length (); i++) + layout_type (types[i]->newtype[0]); + } + + 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; + bool escape_from_base = false; + + 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, escape_from_base)) + 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) + { + if (current_layout_opt_level >= 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) + 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; +} + +/* 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) +{ + bool remove = false; + + if (current_layout_opt_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); + 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 ((current_layout_opt_level < STRUCT_REORDER_FIELDS + && (gimple_assign_rhs_code (stmt) == EQ_EXPR + || gimple_assign_rhs_code (stmt) == NE_EXPR)) + || (current_layout_opt_level >= 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); + 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 (current_layout_opt_level >= 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; + 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"); + + /* Add the judgment of num, support for POINTER_DIFF_EXPR. + _6 = _4 + _5; + _5 = (long unsigned int) _3; + _3 = _1 - old_2. */ + 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++) + { + gimple *new_stmt; + + 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_layout_opt_level >= 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); + tree rhs = gimple_assign_rhs1 (stmt); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nrewriting stamtenet:\n"); + print_gimple_stmt (dump_file, stmt, 0); + } + 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++) + { + 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)) + { + 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, decl, 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++) + { + /* 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. */ + 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); + 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; + } + + /* 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); + + /* Add a safe func mechanism. */ + if (current_layout_opt_level >= 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) + 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_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 + 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 true; +} + +/* 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 ((current_layout_opt_level < STRUCT_REORDER_FIELDS + && (rhs_code != EQ_EXPR && rhs_code != NE_EXPR)) + || (current_layout_opt_level >= STRUCT_REORDER_FIELDS + && TREE_CODE_CLASS (rhs_code) != tcc_comparison)) + return false; + tree lhs = gimple_cond_lhs (stmt); + tree rhs = gimple_cond_rhs (stmt); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + 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, rhs); + 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, "Did nothing to statement.\n"); + return false; + } + + /* 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++) + { + 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; +} + +/* Rewrite debug statments if possible. Return TRUE if the statement + should be removed. */ + +bool +ipa_struct_reorg::rewrite_debug (gimple *stmt, gimple_stmt_iterator *) +{ + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) + /* Delete debug gimple now. */ + return true; + 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:\n"); + 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); + + /* 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]); + gimple_phi_arg_set_location (newphi[j], i, rhs.locus); + update_stmt (newphi[j]); + } + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "into:\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; + + /* Create new types, if we did not create any new types, + then don't rewrite any accesses. */ + if (!create_new_types ()) + { + if (current_layout_opt_level >= 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_layout_opt_level >= 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_layout_opt_level >= STRUCT_REORDER_FIELDS) + { + prune_escaped_types (); + } + } + + if (current_layout_opt_level >= 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 (); + + 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 (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", + 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) + { + 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 (current_layout_opt_level >= POINTER_COMPRESSION_SAFE) + loop_optimizer_finalize (); + + if (dump_file) + { + 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); + } + + pop_cfun (); + current_function = NULL; + } + + return retval | TODO_verify_all; +} + +unsigned int +ipa_struct_reorg::execute_struct_relayout (void) +{ + 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; + if (get_type_name (types[i]->type) == NULL) + 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; +} + +/* 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) +{ + unsigned int ret = 0; + + if (dump_file) + fprintf (dump_file, "\n\n====== ipa_struct_reorg level %d ======\n\n", + ret); + + if (opt != COMPLETE_STRUCT_RELAYOUT) + { + 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_layout_opt_level == STRUCT_SPLIT) + analyze_types (); + + if (opt >= POINTER_COMPRESSION_SAFE) + check_and_prune_struct_for_pointer_compression (); + ret = rewrite_functions (); + } + else + { + if (dump_file) + fprintf (dump_file, "\n\nTry Complete Struct Relayout:\n"); + current_layout_opt_level = COMPLETE_STRUCT_RELAYOUT; + if (symtab->first_asm_symbol ()) + return 0; + record_accesses (); + prune_escaped_types (); + + ret = execute_struct_relayout (); + } + + return ret; +} + +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 *) + { + unsigned int ret = 0; + unsigned int ret_reorg = 0; + 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; + // FALLTHRU + case 1: + level |= COMPLETE_STRUCT_RELAYOUT; + level |= STRUCT_SPLIT; + break; + 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) + 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 + +bool +pass_ipa_struct_reorg::gate (function *) +{ + return (optimize >= 3 + && flag_ipa_struct_reorg + /* 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 + + +simple_ipa_opt_pass * +make_pass_ipa_struct_reorg (gcc::context *ctxt) +{ + return new pass_ipa_struct_reorg (ctxt); +} 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 0000000000000000000000000000000000000000..6c4469597c4ba98b340e4c1fc9e4e8c903ae690b --- /dev/null +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.h @@ -0,0 +1,290 @@ +/* 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, tree orig_type = NULL); + + srfunction *old; + cgraph_node *newnode; + srfunction *newf; + + bool is_safe_func; + + // 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]; + 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; + + // 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); + 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; + } + 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; + } +}; + +/* 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; + tree fieldtype; + tree fielddecl; + srtype *base; + srtype *type; + + unsigned clusternum; + + tree newfield[max_split]; + unsigned field_access; /* FIELD_DECL -> bitflag (use for dfe). */ + + // 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]); + 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 +{ + 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]; + + /* Auxiliary record complete original type information of the void* type. */ + tree orig_type; + + // Constructors + srdecl (srtype *type, tree decl, int argumentnum = -1, tree orgtype = NULL); + + // Methods + void dump (FILE *file); + bool has_new_decl (void) + { + return newdecl[0] && newdecl[0] != decl; + } +}; + + +} // 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/opts.cc b/gcc/opts.cc index a97630d1c9ad52c02ef6581b9d72fc0759ddf722..5a285c96b5610774294596333fc0c3965c56eb26 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/params.opt b/gcc/params.opt index e0ff9e210544bf5c16e60e3caca4963d841d4acc..d2196dc68067241fb645f78f7df45fdb8349f6d6 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. @@ -1201,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. diff --git a/gcc/passes.def b/gcc/passes.def index 375d3d62d519329d62b4ba0ccd994503ac14710b..1c1658c4a85e91e400ebeb0b63b1b7261ba31042 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/symbol-summary.h b/gcc/symbol-summary.h index c54d3084cc472dacb54eff8e9a87ade6078e442e..3fe64047c8bae98c1587ccce8927716b77539859 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/g++.dg/struct/no-body-function.cpp b/gcc/testsuite/g++.dg/struct/no-body-function.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4e56e73fcaedea15e944313cff4b6a009a0dcbb4 --- /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 0000000000000000000000000000000000000000..6ab71abe140f981e4b663a92df41742e8125bba9 --- /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 0000000000000000000000000000000000000000..72b7db8a9cea780675adea2628cb6101adbd7c69 --- /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 0000000000000000000000000000000000000000..771164a96e7091622d21e06e32e5f40c015fbdcc --- /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 0000000000000000000000000000000000000000..e3ffe138819a340ae6ea0a292bc84a64d4568b0b --- /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 0000000000000000000000000000000000000000..811030bf167fbdc6d886c11eb12a6bc44c00ade7 --- /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 0000000000000000000000000000000000000000..63bb695ae14a30c8c938f35df304fa6f304de74b --- /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 0000000000000000000000000000000000000000..0f75d5d121cf057d8f898e5a037cdca6d1b7a0ef --- /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 0000000000000000000000000000000000000000..3dcb674c6e9a610734be8b3d351dfd2a6701809f --- /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 0000000000000000000000000000000000000000..6907158c9b0dc6d3d251b29dcda3bda946a779e9 --- /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 0000000000000000000000000000000000000000..9e5e05838e64436500aed343354ed59496ec3ae5 --- /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/dfe_DTE_verify.c b/gcc/testsuite/gcc.dg/struct/dfe_DTE_verify.c new file mode 100644 index 0000000000000000000000000000000000000000..10b5be86d3cf3a759b5a99fdc0ef7df712f62ac1 --- /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 "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 new file mode 100644 index 0000000000000000000000000000000000000000..5ecfa9fe1339a1ba8c3c1418c8337eef29c66fbe --- /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 "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 new file mode 100644 index 0000000000000000000000000000000000000000..d217f7bd80e719ca96ab92b9ecdbd394a2c1a6ab --- /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 "struct_reorg" } } */ 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 0000000000000000000000000000000000000000..f9e2cf471c837b4d49c41736a1f611059fffbb49 --- /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 "struct_reorg" } } */ 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 0000000000000000000000000000000000000000..c86c4bb3cd0c8eb95f9e9d2e3bbe3e08b8e2f275 --- /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 "struct_reorg" } } */ 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 0000000000000000000000000000000000000000..8484d29d256b70941f9d75c2673fbee3c3341018 --- /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 "struct_reorg" } } */ 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 0000000000000000000000000000000000000000..300b2dac4db1ea877c4eef600b150fc9d49f3804 --- /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 "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 new file mode 100644 index 0000000000000000000000000000000000000000..9397b98eaef0eed1b594cb13fd9e87dccd78e037 --- /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 "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 new file mode 100644 index 0000000000000000000000000000000000000000..0ae75e13e8f5b4d89640b65e269bcaaacd37c0d5 --- /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 "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 new file mode 100644 index 0000000000000000000000000000000000000000..512fb37a7f48fff187d2ec3b52491b27cf97a359 --- /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 "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 new file mode 100644 index 0000000000000000000000000000000000000000..6148770282cfb758be4c84e58f9673045c150302 --- /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 "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 new file mode 100644 index 0000000000000000000000000000000000000000..0570d8952094d075339cf02baabc45842ef22b58 --- /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 "struct_reorg" } } */ 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 0000000000000000000000000000000000000000..a9338d1182d3d99f0ae7a4dbb74bd69756dca62e --- /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 "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 new file mode 100644 index 0000000000000000000000000000000000000000..e59a4b48429bb8e638c1ab3c15f151b915f631c5 --- /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 "struct_reorg" } } */ 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 0000000000000000000000000000000000000000..48cd7932a670b4d18deaf2f393fc31eb737aaba1 --- /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 "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 new file mode 100644 index 0000000000000000000000000000000000000000..1b6a462e2715c36ddb361b5d0306cf2e45e4c3f2 --- /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." "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 new file mode 100644 index 0000000000000000000000000000000000000000..346c7126446a35b7dc5c5ff8caefad78ff6b3dde --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..b876fef861fa2f6507ec37d8b2f030da26d4ca74 --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..7d7641f011d6c9f9efb5285134ea92fa2db36a1f --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..63fb3f8284cb9d6218abc2419593cc30c2969632 --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..8c431e15ffd3d259ef81ac89a457dc5b68de36b7 --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..efc95a4cd56e1bb10a0db989b77e0d201b360fdd --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..2a9bea78369abb1ab4f7547f3a3ba96102f598d5 --- /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." "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 new file mode 100644 index 0000000000000000000000000000000000000000..75fc10575d58ccbc49955c8497807668462fd766 --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..9fb06877bcbf068e2d47a91b5a214ed1b8f89c6b --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..e8eb0eaa09a549805d5486c8726e4b82003842b3 --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..bd535afd08d1ed5dc3657a4089f38e314e821a86 --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..11393a197a357dcdaae6353cb199aac31eef9c89 --- /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." "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 new file mode 100644 index 0000000000000000000000000000000000000000..ed32f244108933036762862c6bce3322f8c92966 --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..4d5f25aa1645e702617101b7da8c1201f3daacea --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..b3891fde9280142a50ed3fcb023c6541f7aa4d42 --- /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." "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 new file mode 100644 index 0000000000000000000000000000000000000000..4df79e4f0e8d2a35d638127f23172907226ade87 --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..49d2106d1dc6d083c6f6debc5ff42031b3b86d76 --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..f71c7894f361b9a6b8eaa5071328c005f1a5fe63 --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..721cee2c6ae74564564001f4b608194c52cdaeac --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..3871d3d99f14ef43d01d76700c340257916f5b49 --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..5ad206433e02387b2da88df97ccb873e13a4dda4 --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..a002f98892ea734e8da409ec912b81de0855aa8d --- /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" "struct_reorg" } } */ \ 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 0000000000000000000000000000000000000000..2bb326ff20057364ebbde5039f97f4003dc4447d --- /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 0000000000000000000000000000000000000000..f77a062bda6c0dee37a9ada6905b50d4bddd9db5 --- /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" "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 new file mode 100644 index 0000000000000000000000000000000000000000..3ed92f2834a9995e3689165ba226558a9f866b7b --- /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" "struct_reorg" } } */ \ No newline at end of file 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 0000000000000000000000000000000000000000..9d58edab80a1ab7482ecd96ace02fa9ad30b57b9 --- /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 0000000000000000000000000000000000000000..a99ee0de48429404d55247d572777321b7d009d3 --- /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 0000000000000000000000000000000000000000..fb135ef0bb5ccc12fde41eeb59c3784aef79d05b --- /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 0000000000000000000000000000000000000000..9a4b10d9aef61d9a651b203876095294891e69a8 --- /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 0000000000000000000000000000000000000000..9a82da0d6e15d8440523691e7fdb9b8d6652c188 --- /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" } } */ 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 0000000000000000000000000000000000000000..278c4e4f586126563637a8423a8fbae8a2248b55 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp @@ -0,0 +1,52 @@ +# 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 {{}} + +# -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" + +# -fipa-struct-reorg=3 +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/dfe*.c]] \ + "" "-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/struct_reorg-1.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-1.c new file mode 100644 index 0000000000000000000000000000000000000000..23444fe8b0de3ff8c48eae1589393417b2fc2608 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-1.c @@ -0,0 +1,30 @@ +// { dg-do compile } +// { dg-options "-O3 -flto-partition=one -fipa-struct-reorg -fdump-ipa-all -fwhole-program" } + +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; +} + +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-2.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-2.c new file mode 100644 index 0000000000000000000000000000000000000000..44babd35b04fcc1aa1afa55c5b24a29c65b72e2e --- /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 0000000000000000000000000000000000000000..2d1f95c99350b95d2d5e08a3fa533eec2f37fea7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-3.c @@ -0,0 +1,28 @@ +// { dg-do compile } +// { dg-options "-O3 -flto-partition=one -fipa-struct-reorg -fdump-ipa-all -fwhole-program" } + +#include +typedef struct { + long laststart_offset; + unsigned regnum; +} compile_stack_elt_t; +typedef struct { + compile_stack_elt_t *stack; + unsigned size; +} compile_stack_type; +__attribute__((noinline)) 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)); +} + +int main() +{ + f (NULL, NULL, 1); +} + +/* { 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 0000000000000000000000000000000000000000..e5a8a6c847fef2eecad98db5c3f29526f3c39cee --- /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/struct_reorg-5.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-5.c new file mode 100644 index 0000000000000000000000000000000000000000..273baa9a368c43c70bb280e68aaecc44dd9e960a --- /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 0000000000000000000000000000000000000000..455f9b501d66c68780db79ae563963db093f3216 --- /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 0000000000000000000000000000000000000000..afc0bd86ca5aba475bbe1de806f3ce06cc7228a5 --- /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 0000000000000000000000000000000000000000..9bcfaf3681b16e913c367f0349cf69c02c5da9e2 --- /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 0000000000000000000000000000000000000000..052f4e3bdc180d0c241cfae8d5f3f2a065e040ca --- /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; +} 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 0000000000000000000000000000000000000000..733413a94d01d133eb280c256f479be8c93c15d5 --- /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 0000000000000000000000000000000000000000..0ef686e74e736eb4152ac2f0ec85483d8d0600ba --- /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 0000000000000000000000000000000000000000..23a53be5386214264383182ac3dcfbfcb474beab --- /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 0000000000000000000000000000000000000000..0cbb172f269d6c748479d950b00611d6f53e7cf5 --- /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 0000000000000000000000000000000000000000..f900b1349ac9ea9148b7d9c4c684131ea8fbb416 --- /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 0000000000000000000000000000000000000000..dcc545964fd7116206fe21ba4bef3ad72b056269 --- /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 0000000000000000000000000000000000000000..6d6375fc1ea41657d3bc8937c30957c7a3a79b4e --- /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 0000000000000000000000000000000000000000..9d32134089f859f9fe72894d486ba8ef0486666a --- /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 0000000000000000000000000000000000000000..d79992a5302e64c677212bd79f7d9451bd4c3ba7 --- /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 0000000000000000000000000000000000000000..ee9b0d76595817903ba6695f2b029ffa51a44ba8 --- /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 0000000000000000000000000000000000000000..9ebb2b4cc969632eb88ccc942208f46df22a27b4 --- /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_replace_type.c b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_replace_type.c new file mode 100644 index 0000000000000000000000000000000000000000..fa8c66b9e92e3264258f9431140ace34bfc23057 --- /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" } } */ 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 0000000000000000000000000000000000000000..d0dce8b536f182b6595368c499f69e1528845d40 --- /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 0000000000000000000000000000000000000000..71167182d50a919da1cce2ff306b493fe3de038d --- /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 0000000000000000000000000000000000000000..74fa11f3940cabafe7ffdbeec46f54cb883ea499 --- /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 0000000000000000000000000000000000000000..60d2466e1daffc8364b38b2ff31d7a68ec60f4a7 --- /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 0000000000000000000000000000000000000000..baf617816d6cec401e3e42d07d0841a58b7a8437 --- /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 0000000000000000000000000000000000000000..33fce3b235048a331ea9e70a62cc124e780c958e --- /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 0000000000000000000000000000000000000000..1c5a3aa15e5fee1e954efd0c5dec835e5dd69f55 --- /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 0000000000000000000000000000000000000000..a0d1467fe9cc305b57a5eb87c7103b8ad4048b95 --- /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 0000000000000000000000000000000000000000..6c24e1c8b054e552decde5f8ad44d67ff6e24b96 --- /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 0000000000000000000000000000000000000000..8f2f8143f65b35f1615bbd82e5c044a1d7153158 --- /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 0000000000000000000000000000000000000000..98bf01a6d07762795e054b4cd5d89d0b394838da --- /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 0000000000000000000000000000000000000000..66b0f967c80a78b18cf074eb69fc8ff74786b2aa --- /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 0000000000000000000000000000000000000000..d28bcfb023741ac1378c55f2e431fe291db7c7b5 --- /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 0000000000000000000000000000000000000000..37a6a43a8595f5afcde51750d9e67876eb79d4a6 --- /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 0000000000000000000000000000000000000000..cba92e99555cbaadf2f57e8b9a59d36b5751b1cf --- /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 2dae5e1c760a80579145fe581baaf33011a0d736..366118126114f4eb8aafc5d96c08ccc973aa0c19 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 606d1d60b856b9529e1052efc4a0c2989e7f01c7..ec7be874cad34e695387865721f09bb10ece624e 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);