Projects
Factory:RISC-V:Base
gcc
_service:tar_scm:0015-Backport-Structure-reorga...
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:tar_scm:0015-Backport-Structure-reorganization-optimization.patch of Package gcc
From 8631d4a39453bb262675bea9abb5c1b7d52af624 Mon Sep 17 00:00:00 2001 From: eastb233 <xiezhiheng@huawei.com> Date: Wed, 19 Jul 2023 10:28:04 +0800 Subject: [PATCH 15/22] [Backport] Structure reorganization optimization Reference: https://gcc.gnu.org/git/?p=gcc-old.git;a=commit;h=6e1bd1c900533c627b5e4fbbecb41dcd7974b522 Introduce structure reorganization optimization, that change C-like structures layout in order to better utilize spatial locality. This transformation is affective for programs containing arrays of structures. --- gcc/Makefile.in | 1 + gcc/common.opt | 4 +- gcc/configure | 2 +- gcc/configure.ac | 2 +- gcc/doc/invoke.texi | 23 + gcc/gimple-ssa-warn-access.cc | 8 + gcc/ipa-param-manipulation.cc | 3 +- gcc/ipa-param-manipulation.h | 3 +- gcc/ipa-struct-reorg/escapes.def | 60 + gcc/ipa-struct-reorg/ipa-struct-reorg.cc | 4015 +++++++++++++++++ gcc/ipa-struct-reorg/ipa-struct-reorg.h | 235 + gcc/params.opt | 4 + gcc/passes.def | 2 + gcc/testsuite/gcc.dg/struct/struct-reorg.exp | 35 + gcc/testsuite/gcc.dg/struct/struct_reorg-1.c | 24 + gcc/testsuite/gcc.dg/struct/struct_reorg-2.c | 29 + gcc/testsuite/gcc.dg/struct/struct_reorg-3.c | 23 + gcc/testsuite/gcc.dg/struct/struct_reorg-4.c | 59 + .../gcc.dg/struct/w_prof_global_array.c | 29 + .../gcc.dg/struct/w_prof_global_var.c | 42 + .../gcc.dg/struct/w_prof_local_array.c | 37 + .../gcc.dg/struct/w_prof_local_var.c | 40 + .../gcc.dg/struct/w_prof_single_str_global.c | 31 + gcc/testsuite/gcc.dg/struct/w_prof_two_strs.c | 64 + .../gcc.dg/struct/w_ratio_cold_str.c | 43 + .../gcc.dg/struct/wo_prof_array_field.c | 26 + .../struct/wo_prof_array_through_pointer.c | 38 + .../gcc.dg/struct/wo_prof_double_malloc.c | 29 + .../gcc.dg/struct/wo_prof_empty_str.c | 44 + .../struct/wo_prof_escape_arg_to_local.c | 44 + .../gcc.dg/struct/wo_prof_escape_return-1.c | 33 + .../gcc.dg/struct/wo_prof_escape_return.c | 32 + .../gcc.dg/struct/wo_prof_escape_str_init.c | 31 + .../struct/wo_prof_escape_substr_array.c | 33 + .../struct/wo_prof_escape_substr_pointer.c | 48 + .../struct/wo_prof_escape_substr_value.c | 45 + .../gcc.dg/struct/wo_prof_global_array.c | 32 + .../gcc.dg/struct/wo_prof_global_var.c | 45 + .../gcc.dg/struct/wo_prof_local_array.c | 40 + .../gcc.dg/struct/wo_prof_local_var.c | 43 + .../gcc.dg/struct/wo_prof_malloc_size_var-1.c | 47 + .../gcc.dg/struct/wo_prof_malloc_size_var.c | 47 + .../struct/wo_prof_mult_field_peeling.c | 42 + .../gcc.dg/struct/wo_prof_single_str_global.c | 34 + .../gcc.dg/struct/wo_prof_single_str_local.c | 34 + .../struct/wo_prof_single_str_pointer.c | 38 + .../gcc.dg/struct/wo_prof_two_strs.c | 67 + gcc/timevar.def | 1 + gcc/tree-pass.h | 1 + 49 files changed, 5686 insertions(+), 6 deletions(-) create mode 100644 gcc/ipa-struct-reorg/escapes.def create mode 100644 gcc/ipa-struct-reorg/ipa-struct-reorg.cc create mode 100644 gcc/ipa-struct-reorg/ipa-struct-reorg.h create mode 100644 gcc/testsuite/gcc.dg/struct/struct-reorg.exp create mode 100644 gcc/testsuite/gcc.dg/struct/struct_reorg-1.c create mode 100644 gcc/testsuite/gcc.dg/struct/struct_reorg-2.c create mode 100644 gcc/testsuite/gcc.dg/struct/struct_reorg-3.c create mode 100644 gcc/testsuite/gcc.dg/struct/struct_reorg-4.c create mode 100644 gcc/testsuite/gcc.dg/struct/w_prof_global_array.c create mode 100644 gcc/testsuite/gcc.dg/struct/w_prof_global_var.c create mode 100644 gcc/testsuite/gcc.dg/struct/w_prof_local_array.c create mode 100644 gcc/testsuite/gcc.dg/struct/w_prof_local_var.c create mode 100644 gcc/testsuite/gcc.dg/struct/w_prof_single_str_global.c create mode 100644 gcc/testsuite/gcc.dg/struct/w_prof_two_strs.c create mode 100644 gcc/testsuite/gcc.dg/struct/w_ratio_cold_str.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_array_field.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_array_through_pointer.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_double_malloc.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_empty_str.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_escape_arg_to_local.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_escape_return-1.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_escape_return.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_escape_str_init.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_escape_substr_array.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_escape_substr_pointer.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_escape_substr_value.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_global_array.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_global_var.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_local_array.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_local_var.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_malloc_size_var-1.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_malloc_size_var.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_mult_field_peeling.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_single_str_global.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_single_str_local.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_single_str_pointer.c create mode 100644 gcc/testsuite/gcc.dg/struct/wo_prof_two_strs.c diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 31ff95500..c863ad992 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1451,6 +1451,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 e365a48bc..b48fa3228 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1950,8 +1950,8 @@ Common Ignore Does nothing. Preserved for backward compatibility. fipa-struct-reorg -Common Ignore -Does nothing. Preserved for backward compatibility. +Common Var(flag_ipa_struct_reorg) Init(0) Optimization +Perform structure layout optimizations. fipa-vrp Common Var(flag_ipa_vrp) Optimization diff --git a/gcc/configure b/gcc/configure index c749ace01..98bbf0f85 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 992a50e7b..c74f4b555 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 ff8cd032f..e37bae5b1 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -526,6 +526,7 @@ Objective-C and Objective-C++ Dialects}. -finline-functions -finline-functions-called-once -finline-limit=@var{n} @gol -finline-small-functions -fipa-modref -fipa-cp -fipa-cp-clone @gol -fipa-bit-cp -fipa-vrp -fipa-pta -fipa-profile -fipa-pure-const @gol +-fipa-struct-reorg @gol -fipa-reference -fipa-reference-addressable @gol -fipa-stack-alignment -fipa-icf -fira-algorithm=@var{algorithm} @gol -flive-patching=@var{level} @gol @@ -11886,6 +11887,19 @@ higher. Discover which functions are pure or constant. Enabled by default at @option{-O1} and higher. +@item -fipa-struct-reorg +@opindex fipa-struct-reorg +Perform structure reorganization optimization, that change C-like structures +layout in order to better utilize spatial locality. This transformation is +affective for programs containing arrays of structures. Available in two +compilation modes: profile-based (enabled with @option{-fprofile-generate}) +or static (which uses built-in heuristics). It works only in whole program +mode, so it requires @option{-fwhole-program} to be +enabled. Structures considered @samp{cold} by this transformation are not +affected (see @option{--param struct-reorg-cold-struct-ratio=@var{value}}). + +With this flag, the program debug info reflects a new structure layout. + @item -fipa-reference @opindex fipa-reference Discover which static variables do not escape the @@ -13772,6 +13786,15 @@ In each case, the @var{value} is an integer. The following choices of @var{name} are recognized for all targets: @table @gcctabopt +@item struct-reorg-cold-struct-ratio +The threshold ratio (as a percentage) between a structure frequency +and the frequency of the hottest structure in the program. This parameter +is used by struct-reorg optimization enabled by @option{-fipa-struct-reorg}. +We say that if the ratio of a structure frequency, calculated by profiling, +to the hottest structure frequency in the program is less than this +parameter, then structure reorganization is not applied to this structure. +The default is 10. + @item predictable-branch-outcome When branch is predicted to be taken with probability lower than this threshold (in percent), then it is considered well predictable. diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc index 8d088ad33..a24645783 100644 --- a/gcc/gimple-ssa-warn-access.cc +++ b/gcc/gimple-ssa-warn-access.cc @@ -2193,6 +2193,14 @@ pass_waccess::set_pass_param (unsigned int n, bool early) bool pass_waccess::gate (function *) { + /* FIXME: In structure optimizations, some statements will be + rewritten and removed from the BB, leaving some unused SSA. + In pass waccess, it will traverse all SSA and cause ICE + when handling these unused SSA. So temporarily disable + pass waccess when enable structure optimizations. */ + if (flag_ipa_struct_reorg) + return false; + return (warn_free_nonheap_object || warn_mismatched_alloc || warn_mismatched_new_delete); diff --git a/gcc/ipa-param-manipulation.cc b/gcc/ipa-param-manipulation.cc index 38328c3e8..f9e956008 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 a9ad2b216..71f4a0a2f 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 000000000..c4c8e0739 --- /dev/null +++ b/gcc/ipa-struct-reorg/escapes.def @@ -0,0 +1,60 @@ +/* Copyright (C) 2016-2023 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* Before including this file, you should define a macro: + DEF_ESCAPE (ENUM, TEXT) + + This macro will be called once for each escape reason. The + ENUM will be of type "escape_type". The TEXT is describing + the reason for the escape. +*/ +DEF_ESCAPE (escape_marked_as_used, "Type used in variable marked as used") +DEF_ESCAPE (escape_via_global_var, "Type used via a external visible variable") +DEF_ESCAPE (escape_via_global_init, "Type used via a global init of a variable") +DEF_ESCAPE (escape_non_supported_allocator, "Type used by allocation which is not currently supported") +DEF_ESCAPE (escape_dependent_type_escapes, "Type uses a type which escapes or is used by a type which escapes") +DEF_ESCAPE (escape_var_arg_function, "Types escapes via a variable argument function") +DEF_ESCAPE (escape_bitfields, "Types has bitfields") +DEF_ESCAPE (escape_recusive_type, "Type has a recusive relationship") +DEF_ESCAPE (escape_variable_sized_array, "Type has a variable sized type") +DEF_ESCAPE (escape_external_function, "Type escapes via an external function call") +DEF_ESCAPE (escape_visible_function, "Type escapes via expternally visible function call") +DEF_ESCAPE (escape_pointer_function, "Type escapes via an function pointer call") +DEF_ESCAPE (escape_unkown_field, "Type escapes via an unkown field accessed") +DEF_ESCAPE (escape_union, "Type escapes via an union") +DEF_ESCAPE (escape_inline_asm, "Type escapes via inline-asm") +DEF_ESCAPE (escape_non_multiply_size, "Type escapes a pointer plus which is not a multiplicate of the size") +DEF_ESCAPE (escape_cast_void, "Type escapes a cast to/from void*") +DEF_ESCAPE (escape_cast_another_ptr, "Type escapes a cast to a different pointer") +DEF_ESCAPE (escape_cast_int, "Type escapes a cast from/to intergral type") +DEF_ESCAPE (escape_int_const, "Type escapes via integer constant") +DEF_ESCAPE (escape_vce, "Type escapes via a VIEW_CONVERT_EXPR") +DEF_ESCAPE (escape_array_access, "Type escapes via an array access") +DEF_ESCAPE (escape_noclonable_function, "Type escapes via a non-clonable function") +DEF_ESCAPE (escape_rescusive_type, "Recusive type") +DEF_ESCAPE (escape_user_alignment, "Type has an user alignment set") +DEF_ESCAPE (escape_volatile, "Type has an variable which is volatile") +DEF_ESCAPE (escape_non_eq, "Type has a comparison other than equals or not equals") +DEF_ESCAPE (escape_addr, "Type escapes via taking the address of field") +DEF_ESCAPE (escape_cannot_change_signature, "Type used in a call that cannot change signature") +DEF_ESCAPE (escape_non_optimize, "Type used by a function which turns off struct reorg") +DEF_ESCAPE (escape_array, "Type is used in an array [not handled yet]") +DEF_ESCAPE (escape_ptr_ptr, "Type is used in a pointer to a pointer [not handled yet]") +DEF_ESCAPE (escape_return, "Type escapes via a return [not handled yet]") + +#undef DEF_ESCAPE diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc new file mode 100644 index 000000000..238530860 --- /dev/null +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc @@ -0,0 +1,4015 @@ +/* Struct-reorg optimizations. + Copyright (C) 2016-2023 Free Software Foundation, Inc. + Contributed by Andrew Pinski <apinski@cavium.com> + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +/* This pass implements the structure reorganization organization + (struct-reorg). + + Right now it handles just splitting off the hottest fields for a struct + of 2 fields: + struct s { + type1 field1; // Hot field + type2 field2; + }; + s *v; + into: + struct s_hot { + type1 field1; + }; + struct c_cold { + type2 field2; + }; + s_hot *v_hot; + s_cold *v_cold; + + TODO: This pass can be extended to more fields, and other alogrothims + like reordering. + + This pass operate in four stages: + 1. All of the field accesses, declarations (struct types and pointers + to that type) and struct types are scanned and recorded. This includes + global declarations. Also record all allocation and freeing sites; + this is needed for the rewriting phase. + + FIXME: If there is a top-level inline-asm, the pass immediately returns. + + 2. Prune out the types which are considered escaping. + Examples of types which are considered escaping: + a. A declaration has been marked as having the attribute used or + has user defined alignment (type too). + b. Accesses are via a BIT_FIELD_REF. + FIXME: Handle VECTOR_TYPE for this case. + c. The "allocation" site is not a known builtin function. + d. Casting to/from an integer. + + 3. Analyze the types for which optimization to do. + a. Split the fields into two different structs. + (FIXME: two field case handled only) + Look at all structs which contain two fields, if one of the fields + is hotter then split it and put it on the rewritting for accesses. + Allocations and freeing are marked to split into two functions; + all uses of that type will now be considered as two. + b. Reorder fields hottest to the coldest. TODO: Implement. + + 4. Rewrite each access and allocation and free whichis marked as + rewriting. + +*/ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "tree-pass.h" +#include "cgraph.h" +#include "diagnostic-core.h" +#include "function.h" +#include "basic-block.h" +#include "gimple.h" +#include "vec.h" +#include "tree-pretty-print.h" +#include "gimple-pretty-print.h" +#include "gimple-iterator.h" +#include "cfg.h" +#include "ssa.h" +#include "tree-dfa.h" +#include "fold-const.h" +#include "tree-inline.h" +#include "stor-layout.h" +#include "tree-into-ssa.h" +#include "tree-cfg.h" +#include "alloc-pool.h" +#include "symbol-summary.h" +#include "ipa-prop.h" +#include "ipa-struct-reorg.h" +#include "tree-eh.h" +#include "bitmap.h" +#include "tree-ssa-live.h" /* For remove_unused_locals. */ +#include "ipa-param-manipulation.h" +#include "gimplify-me.h" + +namespace { + +using namespace struct_reorg; + +#define VOID_POINTER_P(type) \ + (POINTER_TYPE_P (type) && VOID_TYPE_P (TREE_TYPE (type))) + +/* Return true iff TYPE is stdarg va_list type. */ + +static inline bool +is_va_list_type (tree type) +{ + return TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (va_list_type_node); +} + +static 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. */ + +static 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. */ + +static 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. */ + +static 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. */ + +static tree +gimplify_build1 (gimple_stmt_iterator *gsi, enum tree_code code, tree type, + tree a) +{ + tree ret; + + ret = fold_build1_loc (gimple_location (gsi_stmt (*gsi)), code, type, a); + return force_gimple_operand_gsi (gsi, ret, true, NULL, true, + GSI_SAME_STMT); +} + +} // anon namespace + + +namespace struct_reorg { + +/* Constructor of srfunction. */ + +srfunction::srfunction (cgraph_node *n) + : node (n), + old (NULL), + newnode (NULL), + newf (NULL) +{} + +/* Add an ARG to the list of arguments for the function. */ + +void +srfunction::add_arg (srdecl *arg) +{ + args.safe_push (arg); +} + +/* Dump the SRFUNCTION to the file FILE. */ + +void +srfunction::dump (FILE *file) +{ + if (node) + { + fprintf (file, "function : "); + print_generic_expr (file, node->decl); + fprintf (file, " with arguments: "); + for (unsigned i = 0; i < args.length (); i++) + { + if (i == 0) + fprintf (file, "\n "); + else + fprintf (file, "\n, "); + args[i]->dump (file); + } + + fprintf (file, "\nuses globals: "); + for (unsigned i = 0; i < globals.length (); i++) + { + fprintf (file, "\n "); + globals[i]->dump (file); + } + + fprintf (file, "\ndecls: "); + } + else + fprintf (file, "globals : "); + + for (unsigned i = 0; i < decls.length (); i++) + { + fprintf (file, "\n "); + decls[i]->dump (file); + } +} + +/* Simple dump the SRFUNCTION to the file FILE; + used so it is not recusive. */ + +void +srfunction::simple_dump (FILE *file) +{ + print_generic_expr (file, node->decl); +} + +/* Constructor of FIELD. */ + +srfield::srfield (tree field, srtype *base) + : offset (int_byte_position (field)), + fieldtype (TREE_TYPE (field)), + fielddecl (field), + base (base), + type (NULL), + clusternum (0) +{ + for (int i = 0; i < max_split; i++) + newfield[i] = NULL_TREE; +} + +/* Constructor of TYPE. */ + +srtype::srtype (tree type) + : type (type), + chain_type (false), + escapes (does_not_escape), + visited (false) +{ + for (int i = 0; i < max_split; i++) + newtype[i] = NULL_TREE; + + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL) + { + if (DECL_BIT_FIELD (field)) + { + escapes = escape_bitfields; + continue; + } + else if (!DECL_SIZE (field) + || TREE_CODE (DECL_SIZE (field)) != INTEGER_CST) + { + escapes = escape_variable_sized_array; + break; + } + srfield *t = new srfield (field, this); + fields.safe_push (t); + } + } +} + +/* Mark the type as escaping type E at statement STMT. */ + +void +srtype::mark_escape (escape_type e, gimple *stmt) +{ + /* Once the type has escaped, it should never + change back to non escaping. */ + gcc_assert (e != does_not_escape); + if (has_escaped ()) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nO type: "); + simple_dump (dump_file); + fprintf (dump_file, " has already escaped."); + fprintf (dump_file, " old = \"%s\" ", + escape_type_string[escapes - 1]); + fprintf (dump_file, " new = \"%s\"\n", escape_type_string[e - 1]); + if (stmt) + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + } + return; + } + escapes = e; + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nN type: "); + simple_dump (dump_file); + fprintf (dump_file, " new = \"%s\"\n", escape_reason ()); + if (stmt) + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + } +} + +/* Add FIELD to the list of fields that use this type. */ + +void +srtype::add_field_site (srfield *field) +{ + field_sites.safe_push (field); +} + +/* Constructor of DECL. */ + +srdecl::srdecl (srtype *tp, tree decl, int argnum) + : type (tp), + decl (decl), + func (NULL_TREE), + argumentnum (argnum), + visited (false) +{ + if (TREE_CODE (decl) == SSA_NAME) + func = current_function_decl; + else if (!is_global_var (decl)) + func = DECL_CONTEXT (decl); + for (int i = 0; i < max_split; i++) + newdecl[i] = NULL_TREE; +} + +/* Find DECL in the function. */ + +srdecl * +srfunction::find_decl (tree decl) +{ + for (unsigned i = 0; i < decls.length (); i++) + if (decls[i]->decl == decl) + return decls[i]; + return NULL; +} + +/* Record DECL of the TYPE with argument num ARG. */ + +srdecl * +srfunction::record_decl (srtype *type, tree decl, int arg) +{ + // Search for the decl to see if it is already there. + srdecl *decl1 = find_decl (decl); + + if (decl1) + return decl1; + + gcc_assert (type); + + decl1 = new srdecl (type, decl, arg); + decls.safe_push (decl1); + return decl1; +} + +/* Find the field at OFF offset. */ + +srfield * +srtype::find_field (unsigned HOST_WIDE_INT off) +{ + unsigned int i; + srfield *field; + + /* FIXME: handle array/struct field inside the current struct. */ + /* NOTE This does not need to be fixed to handle libquatumn. */ + FOR_EACH_VEC_ELT (fields, i, field) + { + if (off == field->offset) + return field; + } + return NULL; +} + +/* Add the function FN to the list of functions if it + is there not already. */ + +void +srtype::add_function (srfunction *fn) +{ + unsigned decluid; + unsigned i; + decluid = DECL_UID (fn->node->decl); + + srfunction *fn1; + // Search for the decl to see if it is already there. + FOR_EACH_VEC_ELT (functions, i, fn1) + { + if (DECL_UID (fn1->node->decl) == decluid) + return; + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Recording new function: %u.\n", decluid); + + functions.safe_push (fn); +} + +/* Dump out the type structure to FILE. */ + +void +srtype::dump (FILE *f) +{ + unsigned int i; + srfield *field; + srfunction *fn; + sraccess *access; + + if (chain_type) + fprintf (f, "chain decl "); + + fprintf (f, "type : "); + print_generic_expr (f, type); + fprintf (f, "(%d) { ", TYPE_UID (type)); + if (escapes != does_not_escape) + fprintf (f, " escapes = \"%s\"\n", escape_reason ()); + fprintf (f, " fields = { "); + FOR_EACH_VEC_ELT (fields, i, field) + { + if (i == 0) + fprintf (f, "\n "); + else + fprintf (f, "\n, "); + field->dump (f); + } + fprintf (f, " }\n "); + fprintf (f, "\n accesses = {"); + FOR_EACH_VEC_ELT (accesses, i, access) + { + fprintf (f, "\n"); + access->dump (f); + } + fprintf (f, " }\n "); + fprintf (f, "\n functions = {"); + FOR_EACH_VEC_ELT (functions, i, fn) + { + fprintf (f, " \n"); + fn->simple_dump (f); + } + fprintf (f, "\n }\n"); + fprintf (f, "\n field_sites = {"); + FOR_EACH_VEC_ELT (field_sites, i, field) + { + fprintf (f, " \n"); + field->simple_dump (f); + } + fprintf (f, "\n }\n"); + fprintf (f, "}\n"); +} + +/* A simplified dump out the type structure to FILE. */ + +void +srtype::simple_dump (FILE *f) +{ + print_generic_expr (f, type); +} + +/* Analyze the type and decide what to be done with it. */ + +void +srtype::analyze (void) +{ + /* Chain decl types can't be split + so don't try. */ + if (chain_type) + return; + + /* If there is only one field then there is nothing + to be done. */ + if (fields.length () == 1) + return; + + /* For now we unconditionally split only structures with 2 fields + into 2 different structures. In future we intend to add profile + info and/or static heuristics to differentiate splitting process. */ + if (fields.length () == 2) + fields[1]->clusternum = 1; + + /* Otherwise we do nothing. */ + if (fields.length () >= 3) + return; +} + +/* Create the new fields for this field. */ + +void +srfield::create_new_fields (tree newtype[max_split], + tree newfields[max_split], + tree newlast[max_split]) +{ + tree nt[max_split]; + + for (unsigned i = 0; i < max_split; i++) + nt[i] = NULL; + + if (type == NULL) + nt[0] = fieldtype; + else + memcpy (nt, type->newtype, sizeof (type->newtype)); + + for (unsigned i = 0; i < max_split && nt[i] != NULL; i++) + { + tree field = make_node (FIELD_DECL); + if (nt[1] != NULL && DECL_NAME (fielddecl)) + { + const char *tname = IDENTIFIER_POINTER (DECL_NAME (fielddecl)); + char id[10]; + char *name; + + sprintf (id, "%d", i); + name = concat (tname, ".reorg.", id, NULL); + DECL_NAME (field) = get_identifier (name); + free (name); + } + else + DECL_NAME (field) = DECL_NAME (fielddecl); + + TREE_TYPE (field) = reconstruct_complex_type ( + TREE_TYPE (fielddecl), nt[i]); + DECL_SOURCE_LOCATION (field) = DECL_SOURCE_LOCATION (fielddecl); + SET_DECL_ALIGN (field, DECL_ALIGN (fielddecl)); + DECL_USER_ALIGN (field) = DECL_USER_ALIGN (fielddecl); + TREE_ADDRESSABLE (field) = TREE_ADDRESSABLE (fielddecl); + DECL_NONADDRESSABLE_P (field) = !TREE_ADDRESSABLE (fielddecl); + TREE_THIS_VOLATILE (field) = TREE_THIS_VOLATILE (fielddecl); + DECL_CONTEXT (field) = newtype[clusternum]; + + if (newfields[clusternum] == NULL) + newfields[clusternum] = newlast[clusternum] = field; + else + { + DECL_CHAIN (newlast[clusternum]) = field; + newlast[clusternum] = field; + } + newfield[i] = field; + } +} + +/* Create the new TYPE corresponding to THIS type. */ + +bool +srtype::create_new_type (void) +{ + /* If the type has been visited, + then return if a new type was + created or not. */ + if (visited) + return has_new_type (); + + visited = true; + + if (escapes != does_not_escape) + { + newtype[0] = type; + return false; + } + + bool createnewtype = false; + unsigned maxclusters = 0; + + /* Create a new type for each field. */ + for (unsigned i = 0; i < fields.length (); i++) + { + srfield *field = fields[i]; + if (field->type) + createnewtype |= field->type->create_new_type (); + if (field->clusternum > maxclusters) + maxclusters = field->clusternum; + } + + /* If the fields' types did have a change or + we are not splitting the struct into two clusters, + then just return false and don't change the type. */ + if (!createnewtype && maxclusters == 0) + { + newtype[0] = type; + return false; + } + + /* Should have at most max_split clusters. */ + gcc_assert (maxclusters < max_split); + + tree newfields[max_split]; + tree newlast[max_split]; + + maxclusters++; + + const char *tname = NULL; + + if (TYPE_NAME (type) != NULL) + { + if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) + tname = IDENTIFIER_POINTER (TYPE_NAME (type)); + else if (DECL_NAME (TYPE_NAME (type)) != NULL) + tname = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); + } + + for (unsigned i = 0; i < maxclusters; i++) + { + newfields[i] = NULL_TREE; + newlast[i] = NULL_TREE; + newtype[i] = make_node (RECORD_TYPE); + + char *name = NULL; + char id[10]; + sprintf (id, "%d", i); + if (tname) + { + name = concat (tname, ".reorg.", id, NULL); + TYPE_NAME (newtype[i]) = get_identifier (name); + free (name); + } + } + + for (unsigned i = 0; i < fields.length (); i++) + { + srfield *f = fields[i]; + f->create_new_fields (newtype, newfields, newlast); + } + + /* No reason to warn about these structs since the warning would + have happened already. */ + int save_warn_padded = warn_padded; + warn_padded = 0; + + for (unsigned i = 0; i < maxclusters; i++) + { + TYPE_FIELDS (newtype[i]) = newfields[i]; + layout_type (newtype[i]); + } + + warn_padded = save_warn_padded; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Created %d types:\n", maxclusters); + for (unsigned i = 0; i < maxclusters; i++) + { + print_generic_expr (dump_file, newtype[i]); + fprintf (dump_file, "\n"); + } + } + + return true; +} + +/* Helper function to copy some attributes from ORIG_DECL to the NEW_DECL. */ + +static inline void +copy_var_attributes (tree new_decl, tree orig_decl) +{ + DECL_ARTIFICIAL (new_decl) = 1; + DECL_EXTERNAL (new_decl) = DECL_EXTERNAL (orig_decl); + TREE_STATIC (new_decl) = TREE_STATIC (orig_decl); + TREE_PUBLIC (new_decl) = TREE_PUBLIC (orig_decl); + TREE_USED (new_decl) = TREE_USED (orig_decl); + DECL_CONTEXT (new_decl) = DECL_CONTEXT (orig_decl); + TREE_THIS_VOLATILE (new_decl) = TREE_THIS_VOLATILE (orig_decl); + TREE_ADDRESSABLE (new_decl) = TREE_ADDRESSABLE (orig_decl); + TREE_READONLY (new_decl) = TREE_READONLY (orig_decl); + if (is_global_var (orig_decl)) + set_decl_tls_model (new_decl, DECL_TLS_MODEL (orig_decl)); +} + +/* Create all of the new decls (SSA_NAMES included) for THIS function. */ + +void +srfunction::create_new_decls (void) +{ + /* If this function has been cloned, we don't need to + create the new decls. */ + if (newnode) + return; + + if (node) + set_cfun (DECL_STRUCT_FUNCTION (node->decl)); + + for (unsigned i = 0; i < decls.length (); i++) + { + srdecl *decl = decls[i]; + srtype *type = decl->type; + /* If the type of the decl does not change, + then don't create a new decl. */ + if (!type->has_new_type ()) + { + decl->newdecl[0] = decl->decl; + continue; + } + + /* Handle SSA_NAMEs. */ + if (TREE_CODE (decl->decl) == SSA_NAME) + { + tree newtype1[max_split]; + tree inner = SSA_NAME_VAR (decl->decl); + tree newinner[max_split]; + memset (newinner, 0, sizeof (newinner)); + for (unsigned j = 0; j < max_split && type->newtype[j]; j++) + newtype1[j] = reconstruct_complex_type (TREE_TYPE (decls[i]->decl), + type->newtype[j]); + if (inner) + { + srdecl *in = find_decl (inner); + gcc_assert (in); + memcpy (newinner, in->newdecl, sizeof (newinner)); + } + tree od = decls[i]->decl; + /* Create the new ssa names and copy some attributes + from the old one. */ + for (unsigned j = 0; j < max_split && type->newtype[j]; j++) + { + tree nd = make_ssa_name (newinner[j] ? newinner[j] + : newtype1[j]); + decl->newdecl[j] = nd; + /* If the old decl was a default definition, + handle it specially. */ + if (SSA_NAME_IS_DEFAULT_DEF (od)) + { + SSA_NAME_IS_DEFAULT_DEF (nd) = true; + SSA_NAME_DEF_STMT (nd) = gimple_build_nop (); + + /* Set the default definition for the ssaname if needed. */ + if (inner) + { + gcc_assert (newinner[j]); + set_ssa_default_def (cfun, newinner[j], nd); + } + } + SSA_NAME_OCCURS_IN_ABNORMAL_PHI (nd) + = SSA_NAME_OCCURS_IN_ABNORMAL_PHI (od); + statistics_counter_event (cfun, "Create new ssa_name", 1); + } + } + else if (TREE_CODE (decls[i]->decl) == VAR_DECL) + { + tree orig_var = decl->decl; + const char *tname = NULL; + if (DECL_NAME (orig_var)) + tname = IDENTIFIER_POINTER (DECL_NAME (orig_var)); + for (unsigned j = 0; j < max_split && type->newtype[j]; j++) + { + tree new_name = NULL; + char *name = NULL; + char id[10]; + sprintf (id, "%d", j); + if (tname) + { + name = concat (tname, ".reorg.", id, NULL); + new_name = get_identifier (name); + free (name); + } + tree newtype1 = reconstruct_complex_type (TREE_TYPE (orig_var), + type->newtype[j]); + decl->newdecl[j] = build_decl (DECL_SOURCE_LOCATION (orig_var), + VAR_DECL, new_name, newtype1); + copy_var_attributes (decl->newdecl[j], orig_var); + if (!is_global_var (orig_var)) + add_local_decl (cfun, decl->newdecl[j]); + else + varpool_node::add (decl->newdecl[j]); + statistics_counter_event (cfun, "Create new var decl", 1); + } + } + /* Paramater decls are already handled in create_new_functions. */ + else if (TREE_CODE (decls[i]->decl) == PARM_DECL) + ; + else + internal_error ("Unhandled declaration type stored"); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Created New decls for decl:\n"); + fprintf (dump_file, "\n"); + decls[i]->dump (dump_file); + fprintf (dump_file, "\n"); + for (unsigned j = 0; j < max_split && decls[i]->newdecl[j]; j++) + { + print_generic_expr (dump_file, decls[i]->newdecl[j]); + fprintf (dump_file, "\n"); + } + fprintf (dump_file, "\n"); + } + } + + set_cfun (NULL); +} + +/* Dump out the field structure to FILE. */ + +void +srfield::dump (FILE *f) +{ + fprintf (f, "field (%d) { ", DECL_UID (fielddecl)); + fprintf (f, "base = "); + base->simple_dump (f); + fprintf (f, ", offset = " HOST_WIDE_INT_PRINT_DEC, offset); + fprintf (f, ", type = "); + print_generic_expr (f, fieldtype); + if (type) + { + fprintf (f, "( srtype = "); + type->simple_dump (f); + fprintf (f, ")"); + } + fprintf (f, "\n}\n"); +} + +/* A simplified dump out the field structure to FILE. */ + +void +srfield::simple_dump (FILE *f) +{ + fprintf (f, "field (%d)", DECL_UID (fielddecl)); +} + +/* Dump out the access structure to FILE. */ + +void +sraccess::dump (FILE *f) +{ + fprintf (f, "access { "); + fprintf (f, "type = '("); + type->simple_dump (f); + fprintf (f, ")'"); + if (field) + { + fprintf (f, ", field = '("); + field->simple_dump (f); + fprintf (f, ")'"); + } + else + fprintf (f, ", whole type"); + fprintf (f, " in function: %s/%d", node->name (), node->order); + fprintf (f, ", stmt:\n"); + print_gimple_stmt (f, stmt, 0); + fprintf (f, "\n }\n"); +} + +/* Dump out the decl structure to FILE. */ + +void +srdecl::dump (FILE *file) +{ + if (!func) + fprintf (file, "global "); + if (argumentnum != -1) + fprintf (file, "argument(%d) ", argumentnum); + fprintf (file, "decl: "); + print_generic_expr (file, decl); + fprintf (file, " type: "); + type->simple_dump (file); +} + +} // namespace struct_reorg + + +namespace { + +struct ipa_struct_reorg +{ +public: + // Constructors + ipa_struct_reorg (void) + : current_function (NULL), + done_recording (false) + {} + + // Public methods + unsigned execute (void); + void mark_type_as_escape (tree type, escape_type, gimple *stmt = NULL); +private: + // Fields + auto_vec_del<srtype> types; + auto_vec_del<srfunction> functions; + srglobal globals; + srfunction *current_function; + + bool done_recording; + + // Private methods + void dump_types (FILE *f); + void dump_types_escaped (FILE *f); + void dump_functions (FILE *f); + void record_accesses (void); + void detect_cycles (void); + bool walk_field_for_cycles (srtype *); + void prune_escaped_types (void); + void propagate_escape (void); + void analyze_types (void); + void clear_visited (void); + bool create_new_types (void); + void restore_field_type (void); + void create_new_decls (void); + srdecl *find_decl (tree); + void create_new_functions (void); + void create_new_args (cgraph_node *new_node); + unsigned rewrite_functions (void); + srdecl *record_var (tree decl, + escape_type escapes = does_not_escape, + int arg = -1); + srfunction *record_function (cgraph_node *node); + srfunction *find_function (cgraph_node *node); + srtype *record_type (tree type); + void process_union (tree type); + srtype *find_type (tree type); + void maybe_record_stmt (cgraph_node *, gimple *); + void maybe_record_assign (cgraph_node *, gassign *); + void maybe_record_call (cgraph_node *, gcall *); + void maybe_record_allocation_site (cgraph_node *, gimple *); + void record_stmt_expr (tree expr, cgraph_node *node, gimple *stmt); + void mark_expr_escape (tree, escape_type, gimple *stmt); + tree allocate_size (srtype *t, gimple *stmt); + + void mark_decls_in_as_not_needed (tree fn); + + bool rewrite_stmt (gimple *, gimple_stmt_iterator *); + bool rewrite_assign (gassign *, gimple_stmt_iterator *); + bool rewrite_call (gcall *, gimple_stmt_iterator *); + bool rewrite_cond (gcond *, gimple_stmt_iterator *); + bool rewrite_debug (gimple *, gimple_stmt_iterator *); + bool rewrite_phi (gphi *); + bool rewrite_expr (tree expr, + tree newexpr[max_split], + bool ignore_missing_decl = false); + bool rewrite_lhs_rhs (tree lhs, tree rhs, tree newlhs[max_split], + tree newrhs[max_split]); + bool get_type_field (tree expr, tree &base, bool &indirect, + srtype *&type, srfield *&field, + bool &realpart, bool &imagpart, + bool &address, bool should_create = false, + bool can_escape = false); + bool wholeaccess (tree expr, tree base, tree accesstype, srtype *t); + + void check_definition (srdecl *decl, vec<srdecl *> &); + void check_uses (srdecl *decl, vec<srdecl *> &); + void check_use (srdecl *decl, gimple *stmt, vec<srdecl *> &); + void check_type_and_push (tree newdecl, srtype *type, + vec<srdecl *> &worklist, gimple *stmt); + void check_other_side (srdecl *decl, tree other, gimple *stmt, + vec<srdecl *> &worklist); + + void find_vars (gimple *stmt); + void find_var (tree expr, gimple *stmt); + void mark_types_asm (gasm *astmt); + + bool has_rewritten_type (srfunction *); + void maybe_mark_or_record_other_side (tree side, tree other, gimple *stmt); +}; + +/* Dump all of the recorded types to file F. */ + +void +ipa_struct_reorg::dump_types (FILE *f) +{ + unsigned i; + srtype *type; + FOR_EACH_VEC_ELT (types, i, type) + { + type->dump (f); + } + fprintf (f, "\n"); +} + +/* Dump all of the recorded types to file F. */ + +void +ipa_struct_reorg::dump_types_escaped (FILE *f) +{ + unsigned i; + srtype *type; + FOR_EACH_VEC_ELT (types, i, type) + { + if (type->has_escaped ()) + { + type->simple_dump (f); + fprintf (f, " has escaped: \"%s\"\n", type->escape_reason ()); + } + } + fprintf (f, "\n"); +} + +/* Dump all of the record functions to file F. */ + +void +ipa_struct_reorg::dump_functions (FILE *f) +{ + unsigned i; + srfunction *fn; + + fprintf (f, "\n\n"); + globals.dump (f); + fprintf (f, "\n\n"); + FOR_EACH_VEC_ELT (functions, i, fn) + { + fn->dump (f); + fprintf (f, "\n"); + } + fprintf (f, "\n\n"); +} + +/* Find the recorded srtype corresponding to TYPE. */ + +srtype * +ipa_struct_reorg::find_type (tree type) +{ + unsigned i; + /* Get the main variant as we are going + to find that type only. */ + type = TYPE_MAIN_VARIANT (type); + + srtype *type1; + // Search for the type to see if it is already there. + FOR_EACH_VEC_ELT (types, i, type1) + { + if (types_compatible_p (type1->type, type)) + return type1; + } + return NULL; +} + +/* Is TYPE a volatile type or one which points + to a volatile type. */ + +static bool +isvolatile_type (tree type) +{ + if (TYPE_VOLATILE (type)) + return true; + while (POINTER_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE) + { + type = TREE_TYPE (type); + if (TYPE_VOLATILE (type)) + return true; + } + return false; +} + +/* Is TYPE an array type or points to an array type. */ + +static bool +isarraytype (tree type) +{ + if (TREE_CODE (type) == ARRAY_TYPE) + return true; + while (POINTER_TYPE_P (type)) + { + type = TREE_TYPE (type); + if (TREE_CODE (type) == ARRAY_TYPE) + return true; + } + return false; +} + +/* Is TYPE a pointer to another pointer. */ + +static bool +isptrptr (tree type) +{ + bool firstptr = false; + while (POINTER_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE) + { + if (POINTER_TYPE_P (type)) + { + if (firstptr) + return true; + firstptr = true; + } + type = TREE_TYPE (type); + } + return false; +} + +/* Return the escape type which corresponds to if + this is an volatile type, an array type or a pointer + to a pointer type. */ + +static escape_type +escape_type_volatile_array_or_ptrptr (tree type) +{ + if (isvolatile_type (type)) + return escape_volatile; + if (isarraytype (type)) + return escape_array; + if (isptrptr (type)) + return escape_ptr_ptr; + return does_not_escape; +} + +/* Record TYPE if not already recorded. */ + +srtype * +ipa_struct_reorg::record_type (tree type) +{ + unsigned typeuid; + + /* Get the main variant as we are going + to record that type only. */ + type = TYPE_MAIN_VARIANT (type); + typeuid = TYPE_UID (type); + + srtype *type1; + + type1 = find_type (type); + if (type1) + return type1; + + /* If already done recording just return NULL. */ + if (done_recording) + return NULL; + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Recording new type: %u.\n", typeuid); + + type1 = new srtype (type); + types.safe_push (type1); + + /* If the type has an user alignment set, + that means the user most likely already setup the type. */ + if (TYPE_USER_ALIGN (type)) + type1->mark_escape (escape_user_alignment, NULL); + + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL) + { + tree t = TREE_TYPE (field); + process_union (t); + if (TREE_CODE (inner_type (t)) == UNION_TYPE + || TREE_CODE (inner_type (t)) == QUAL_UNION_TYPE) + type1->mark_escape (escape_union, NULL); + if (isvolatile_type (t)) + type1->mark_escape (escape_volatile, NULL); + escape_type e = escape_type_volatile_array_or_ptrptr (t); + if (e != does_not_escape) + type1->mark_escape (e, NULL); + if (handled_type (t)) + { + srtype *t1 = record_type (inner_type (t)); + srfield *f = type1->find_field (int_byte_position (field)); + /* We might have an variable sized type which + we don't set the handle. */ + if (f) + { + f->type = t1; + t1->add_field_site (f); + } + if (t1 == type1) + type1->mark_escape (escape_rescusive_type, NULL); + } + } + } + + return type1; +} + +/* Mark TYPE as escaping with ESCAPES as the reason. */ + +void +ipa_struct_reorg::mark_type_as_escape (tree type, + escape_type escapes, + gimple *stmt) +{ + if (handled_type (type)) + { + srtype *stype = record_type (inner_type (type)); + + if (!stype) + return; + + stype->mark_escape (escapes, stmt); + } +} + +/* Maybe process the union of type TYPE, such that marking all of the fields' + types as being escaping. */ + +void +ipa_struct_reorg::process_union (tree type) +{ + static hash_set<tree> unions_recorded; + + type = inner_type (type); + if (TREE_CODE (type) != UNION_TYPE + && TREE_CODE (type) != QUAL_UNION_TYPE) + return; + + type = TYPE_MAIN_VARIANT (type); + + /* We already processed this type. */ + if (unions_recorded.add (type)) + return; + + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL) + { + mark_type_as_escape (TREE_TYPE (field), escape_union); + process_union (TREE_TYPE (field)); + } + } +} + +/* Used by record_var function as a callback to walk_tree. + Mark the type as escaping if it has expressions which + cannot be converted for global initializations. */ + +static tree +record_init_types (tree *tp, int *walk_subtrees, void *data) +{ + ipa_struct_reorg *c = (ipa_struct_reorg *)data; + switch (TREE_CODE (*tp)) + { + CASE_CONVERT: + case COMPONENT_REF: + case VIEW_CONVERT_EXPR: + case ARRAY_REF: + { + tree typeouter = TREE_TYPE (*tp); + tree typeinner = TREE_TYPE (TREE_OPERAND (*tp, 0)); + c->mark_type_as_escape (typeouter, escape_via_global_init); + c->mark_type_as_escape (typeinner, escape_via_global_init); + break; + } + case INTEGER_CST: + if (!integer_zerop (*tp)) + c->mark_type_as_escape (TREE_TYPE (*tp), escape_via_global_init); + break; + case VAR_DECL: + case PARM_DECL: + case FIELD_DECL: + c->mark_type_as_escape (TREE_TYPE (*tp), escape_via_global_init); + *walk_subtrees = false; + break; + default: + *walk_subtrees = true; + break; + } + return NULL_TREE; +} + +/* Record var DECL; optionally specify the escape reason and the argument + number in a function. */ + +srdecl * +ipa_struct_reorg::record_var (tree decl, escape_type escapes, int arg) +{ + srtype *type; + srdecl *sd = NULL; + + process_union (TREE_TYPE (decl)); + + if (handled_type (TREE_TYPE (decl))) + { + type = record_type (inner_type (TREE_TYPE (decl))); + escape_type e; + + if (done_recording && !type) + return NULL; + + gcc_assert (type); + if (TREE_CODE (decl) == VAR_DECL && is_global_var (decl)) + sd = globals.record_decl (type, decl, arg); + else + { + gcc_assert (current_function); + sd = current_function->record_decl (type, decl, arg); + } + + /* If the variable has the "used" attribute, + then treat the type as escaping. */ + if (escapes != does_not_escape) + e = escapes; + else if (TREE_CODE (decl) != SSA_NAME && DECL_PRESERVE_P (decl)) + e = escape_marked_as_used; + else if (TREE_THIS_VOLATILE (decl)) + e = escape_volatile; + else if (TREE_CODE (decl) != SSA_NAME && DECL_USER_ALIGN (decl)) + e = escape_user_alignment; + else if (TREE_CODE (decl) != SSA_NAME && TREE_STATIC (decl) + && TREE_PUBLIC (decl)) + e = escape_via_global_var; + /* We don't have an initlizer. */ + else if (TREE_CODE (decl) != SSA_NAME + && DECL_INITIAL (decl) == error_mark_node) + e = escape_via_global_var; + else + e = escape_type_volatile_array_or_ptrptr (TREE_TYPE (decl)); + + if (e != does_not_escape) + type->mark_escape (e, NULL); + } + + /* Record the initial usage of variables as types escapes. */ + if (TREE_CODE (decl) != SSA_NAME && TREE_STATIC (decl) + && DECL_INITIAL (decl)) + { + walk_tree_without_duplicates (&DECL_INITIAL (decl), + record_init_types, this); + if (!integer_zerop (DECL_INITIAL (decl)) + && DECL_INITIAL (decl) != error_mark_node) + mark_type_as_escape (TREE_TYPE (decl), escape_via_global_init); + } + return sd; +} + +/* Find void* ssa_names which are used inside MEM[] or if we have &a.c, + mark the type as escaping. */ + +void +ipa_struct_reorg::find_var (tree expr, gimple *stmt) +{ + /* If we have VCE<a> mark the outer type as escaping and the inner one + Also mark the inner most operand. */ + if (TREE_CODE (expr) == VIEW_CONVERT_EXPR) + { + mark_type_as_escape (TREE_TYPE (expr), escape_vce, stmt); + mark_type_as_escape (TREE_TYPE (TREE_OPERAND (expr, 0)), + escape_vce, stmt); + } + + /* If we have &b.c then we need to mark the type of b + as escaping as tracking a will be hard. */ + if (TREE_CODE (expr) == ADDR_EXPR + || TREE_CODE (expr) == VIEW_CONVERT_EXPR) + { + tree r = TREE_OPERAND (expr, 0); + if (handled_component_p (r) + || TREE_CODE (r) == MEM_REF) + { + while (handled_component_p (r) + || TREE_CODE (r) == MEM_REF) + { + if (TREE_CODE (r) == VIEW_CONVERT_EXPR) + { + mark_type_as_escape (TREE_TYPE (r), escape_vce, stmt); + mark_type_as_escape (TREE_TYPE (TREE_OPERAND (r, 0)), + escape_vce, stmt); + } + if (TREE_CODE (r) == MEM_REF) + mark_type_as_escape (TREE_TYPE (TREE_OPERAND (r, 1)), + escape_addr, stmt); + r = TREE_OPERAND (r, 0); + } + mark_expr_escape (r, escape_addr, stmt); + } + } + + tree base; + bool indirect; + srtype *type; + srfield *field; + bool realpart, imagpart, address; + get_type_field (expr, base, indirect, type, field, + realpart, imagpart, address, true, true); +} + +void +ipa_struct_reorg::find_vars (gimple *stmt) +{ + gasm *astmt; + switch (gimple_code (stmt)) + { + case GIMPLE_ASSIGN: + if (gimple_assign_rhs_class (stmt) == GIMPLE_SINGLE_RHS + || gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) + { + tree lhs = gimple_assign_lhs (stmt); + tree rhs = gimple_assign_rhs1 (stmt); + find_var (gimple_assign_lhs (stmt), stmt); + find_var (gimple_assign_rhs1 (stmt), stmt); + if (TREE_CODE (lhs) == SSA_NAME + && VOID_POINTER_P (TREE_TYPE (lhs)) + && handled_type (TREE_TYPE (rhs))) + { + srtype *t = find_type (inner_type (TREE_TYPE (rhs))); + srdecl *d = find_decl (lhs); + if (!d && t) + current_function->record_decl (t, lhs, -1); + } + if (TREE_CODE (rhs) == SSA_NAME + && VOID_POINTER_P (TREE_TYPE (rhs)) + && handled_type (TREE_TYPE (lhs))) + { + srtype *t = find_type (inner_type (TREE_TYPE (lhs))); + srdecl *d = find_decl (rhs); + if (!d && t) + current_function->record_decl (t, rhs, -1); + } + } + break; + + case GIMPLE_CALL: + if (gimple_call_lhs (stmt)) + find_var (gimple_call_lhs (stmt), stmt); + + if (gimple_call_chain (stmt)) + find_var (gimple_call_chain (stmt), stmt); + + for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) + find_var (gimple_call_arg (stmt, i), stmt); + break; + + case GIMPLE_ASM: + astmt = as_a <gasm *> (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 <greturn *> (stmt)); + if (expr) + find_var (expr, stmt); + /* return &a; should mark the type of a as escaping + through a return. */ + if (expr && TREE_CODE (expr) == ADDR_EXPR) + { + expr = TREE_OPERAND (expr, 0); + srdecl *d = find_decl (expr); + if (d) + d->type->mark_escape (escape_return, stmt); + } + } + break; + + default: + break; + } +} + +/* Maybe record access of statement for further analaysis. */ + +void +ipa_struct_reorg::maybe_record_stmt (cgraph_node *node, gimple *stmt) +{ + switch (gimple_code (stmt)) + { + case GIMPLE_ASSIGN: + maybe_record_assign (node, as_a <gassign *> (stmt)); + break; + case GIMPLE_CALL: + maybe_record_call (node, as_a <gcall *> (stmt)); + break; + case GIMPLE_DEBUG: + break; + case GIMPLE_GOTO: + case GIMPLE_SWITCH: + break; + default: + break; + } +} + +/* This function checks whether ARG is a result of multiplication + of some number by STRUCT_SIZE. If yes, the function returns true + and this number is filled into NUM. */ + +static bool +is_result_of_mult (tree arg, tree *num, tree struct_size) +{ + if (!struct_size + || TREE_CODE (struct_size) != INTEGER_CST + || integer_zerop (struct_size)) + return false; + + /* If we have a integer, just check if it is a multiply of STRUCT_SIZE. */ + if (TREE_CODE (arg) == INTEGER_CST) + { + if (integer_zerop (size_binop (FLOOR_MOD_EXPR, arg, struct_size))) + { + *num = size_binop (FLOOR_DIV_EXPR, arg, struct_size); + return true; + } + return false; + } + gimple *size_def_stmt = SSA_NAME_DEF_STMT (arg); + + /* If the allocation statement was of the form + D.2229_10 = <alloc_func> (D.2228_9); + then size_def_stmt can be D.2228_9 = num.3_8 * 8; */ + + while (size_def_stmt && is_gimple_assign (size_def_stmt)) + { + tree lhs = gimple_assign_lhs (size_def_stmt); + + /* We expect temporary here. */ + if (!is_gimple_reg (lhs)) + return false; + + // FIXME: this should handle SHIFT also. + if (gimple_assign_rhs_code (size_def_stmt) == PLUS_EXPR) + { + tree num1, num2; + tree arg0 = gimple_assign_rhs1 (size_def_stmt); + tree arg1 = gimple_assign_rhs2 (size_def_stmt); + if (!is_result_of_mult (arg0, &num1, struct_size)) + return false; + if (!is_result_of_mult (arg1, &num2, struct_size)) + return false; + *num = size_binop (PLUS_EXPR, num1, num2); + return true; + } + else if (gimple_assign_rhs_code (size_def_stmt) == MULT_EXPR) + { + tree arg0 = gimple_assign_rhs1 (size_def_stmt); + tree arg1 = gimple_assign_rhs2 (size_def_stmt); + tree num1; + + if (is_result_of_mult (arg0, &num1, struct_size)) + { + *num = size_binop (MULT_EXPR, arg1, num1); + return true; + } + if (is_result_of_mult (arg1, &num1, struct_size)) + { + *num = size_binop (MULT_EXPR, arg0, num1); + return true; + } + + *num = NULL_TREE; + return false; + } + else if (gimple_assign_rhs_code (size_def_stmt) == SSA_NAME) + { + arg = gimple_assign_rhs1 (size_def_stmt); + size_def_stmt = SSA_NAME_DEF_STMT (arg); + } + else + { + *num = NULL_TREE; + return false; + } + } + + *num = NULL_TREE; + return false; +} + +/* Return TRUE if STMT is an allocation statement that is handled. */ + +static bool +handled_allocation_stmt (gimple *stmt) +{ + if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_MALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_CALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_ALIGNED_ALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA) + || gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN)) + return true; + return false; +} + +/* Returns the allocated size / T size for STMT. That is the number of + elements in the array allocated. */ + +tree +ipa_struct_reorg::allocate_size (srtype *type, gimple *stmt) +{ + if (!stmt + || gimple_code (stmt) != GIMPLE_CALL + || !handled_allocation_stmt (stmt)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nNot a allocate statment:\n"); + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + } + return NULL; + } + + if (type->has_escaped ()) + return NULL; + + tree struct_size = TYPE_SIZE_UNIT (type->type); + + tree size = gimple_call_arg (stmt, 0); + + if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_ALIGNED_ALLOC)) + size = gimple_call_arg (stmt, 1); + else if (gimple_call_builtin_p (stmt, BUILT_IN_CALLOC)) + { + tree arg1; + arg1 = gimple_call_arg (stmt, 1); + /* Check that second argument is a constant equal to + the size of structure. */ + if (operand_equal_p (arg1, struct_size, 0)) + return size; + /* Check that first argument is a constant equal to + the size of structure. */ + if (operand_equal_p (size, struct_size, 0)) + return arg1; + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\ncalloc the correct size:\n"); + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + } + return NULL; + } + + tree num; + if (!is_result_of_mult (size, &num, struct_size)) + return NULL; + + return num; +} + +void +ipa_struct_reorg::maybe_mark_or_record_other_side (tree side, tree other, + gimple *stmt) +{ + gcc_assert (TREE_CODE (side) == SSA_NAME || TREE_CODE (side) == ADDR_EXPR); + srtype *type = NULL; + if (handled_type (TREE_TYPE (other))) + type = record_type (inner_type (TREE_TYPE (other))); + if (TREE_CODE (side) == ADDR_EXPR) + side = TREE_OPERAND (side, 0); + srdecl *d = find_decl (side); + if (!type) + { + if (!d) + return; + if (TREE_CODE (side) == SSA_NAME + && VOID_POINTER_P (TREE_TYPE (side))) + return; + d->type->mark_escape (escape_cast_another_ptr, stmt); + return; + } + + if (!d) + { + if (VOID_POINTER_P (TREE_TYPE (side)) + && TREE_CODE (side) == SSA_NAME) + current_function->record_decl (type, side, -1); + else + type->mark_escape (escape_cast_another_ptr, stmt); + } + else if (type != d->type) + { + type->mark_escape (escape_cast_another_ptr, stmt); + d->type->mark_escape (escape_cast_another_ptr, stmt); + } +} + +/* Record accesses in an assignment statement STMT. */ + +void +ipa_struct_reorg::maybe_record_assign (cgraph_node *node, gassign *stmt) +{ + if (gimple_clobber_p (stmt)) + { + record_stmt_expr (gimple_assign_lhs (stmt), node, stmt); + return; + } + + if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) + { + tree lhs = gimple_assign_lhs (stmt); + tree rhs1 = gimple_assign_rhs1 (stmt); + tree rhs2 = gimple_assign_rhs2 (stmt); + tree num; + if (!handled_type (TREE_TYPE (lhs))) + return; + /* Check if rhs2 is a multiplication of the size of the type. */ + if (is_result_of_mult (rhs2, &num, + TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (lhs))))) + { + record_stmt_expr (lhs, node, stmt); + record_stmt_expr (rhs1, node, stmt); + } + else + { + mark_expr_escape (lhs, escape_non_multiply_size, stmt); + mark_expr_escape (rhs1, escape_non_multiply_size, stmt); + } + return; + } + /* Copies, References, Taking addresses. */ + if (gimple_assign_rhs_class (stmt) == GIMPLE_SINGLE_RHS) + { + tree lhs = gimple_assign_lhs (stmt); + tree rhs = gimple_assign_rhs1 (stmt); + /* If we have a = &b.c then we need to mark the type of b + as escaping as tracking a will be hard. */ + if (TREE_CODE (rhs) == ADDR_EXPR) + { + tree r = TREE_OPERAND (rhs, 0); + if (handled_component_p (r)) + { + while (handled_component_p (r)) + r = TREE_OPERAND (r, 0); + mark_expr_escape (r, escape_addr, stmt); + return; + } + } + if ((TREE_CODE (rhs) == SSA_NAME || TREE_CODE (rhs) == ADDR_EXPR)) + maybe_mark_or_record_other_side (rhs, lhs, stmt); + if (TREE_CODE (lhs) == SSA_NAME) + maybe_mark_or_record_other_side (lhs, rhs, stmt); + } +} + +static tree +get_ref_base_and_offset (tree &e, HOST_WIDE_INT &offset, + bool &realpart, bool &imagpart, + tree &accesstype) +{ + offset = 0; + realpart = false; + imagpart = false; + accesstype = NULL_TREE; + if (TREE_CODE (e) == REALPART_EXPR) + { + e = TREE_OPERAND (e, 0); + realpart = true; + } + if (TREE_CODE (e) == IMAGPART_EXPR) + { + e = TREE_OPERAND (e, 0); + imagpart = true; + } + tree expr = e; + while (true) + { + switch (TREE_CODE (expr)) + { + case COMPONENT_REF: + { + tree field = TREE_OPERAND (expr, 1); + tree field_off = byte_position (field); + if (TREE_CODE (field_off) != INTEGER_CST) + return NULL; + offset += tree_to_shwi (field_off); + expr = TREE_OPERAND (expr, 0); + accesstype = NULL; + break; + } + case MEM_REF: + { + tree field_off = TREE_OPERAND (expr, 1); + gcc_assert (TREE_CODE (field_off) == INTEGER_CST); + /* So we can mark the types as escaping if different. */ + accesstype = TREE_TYPE (field_off); + offset += tree_to_uhwi (field_off); + return TREE_OPERAND (expr, 0); + } + default: + return expr; + } + } +} + +/* Return true if EXPR was accessing the whole type T. */ + +bool +ipa_struct_reorg::wholeaccess (tree expr, tree base, + tree accesstype, srtype *t) +{ + if (expr == base) + return true; + + if (TREE_CODE (expr) == ADDR_EXPR && TREE_OPERAND (expr, 0) == base) + return true; + + if (!accesstype) + return false; + + if (!types_compatible_p (TREE_TYPE (expr), TREE_TYPE (accesstype))) + return false; + + if (!handled_type (TREE_TYPE (expr))) + return false; + + srtype *other_type = find_type (inner_type (TREE_TYPE (expr))); + + if (t == other_type) + return true; + + return false; +} + +bool +ipa_struct_reorg::get_type_field (tree expr, tree &base, bool &indirect, + srtype *&type, srfield *&field, + bool &realpart, bool &imagpart, + bool &address, bool should_create, + bool can_escape) +{ + HOST_WIDE_INT offset; + tree accesstype; + address = false; + bool mark_as_bit_field = false; + + if (TREE_CODE (expr) == BIT_FIELD_REF) + { + expr = TREE_OPERAND (expr, 0); + mark_as_bit_field = true; + } + + base = get_ref_base_and_offset (expr, offset, realpart, imagpart, + accesstype); + + /* Variable access, unkown type. */ + if (base == NULL) + return false; + + if (TREE_CODE (base) == ADDR_EXPR) + { + address = true; + base = TREE_OPERAND (base, 0); + } + + if (offset != 0 && accesstype) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Non zero offset (%d) with MEM.\n", (int)offset); + print_generic_expr (dump_file, expr); + fprintf (dump_file, "\n"); + print_generic_expr (dump_file, base); + fprintf (dump_file, "\n"); + } + } + + srdecl *d = find_decl (base); + srtype *t; + + if (integer_zerop (base)) + { + gcc_assert (!d); + if (!accesstype) + return false; + t = find_type (inner_type (inner_type (accesstype))); + if (!t && should_create && handled_type (accesstype)) + t = record_type (inner_type (accesstype)); + if (!t) + return false; + } + else if (!d && accesstype) + { + if (!should_create) + return false; + if (!handled_type (accesstype)) + return false; + t = find_type (inner_type (inner_type (accesstype))); + if (!t) + t = record_type (inner_type (accesstype)); + if (!t || t->has_escaped ()) + return false; + /* If base is not void* mark the type as escaping. */ + if (!VOID_POINTER_P (TREE_TYPE (base))) + { + gcc_assert (can_escape); + t->mark_escape (escape_cast_another_ptr, NULL); + return false; + } + if (TREE_CODE (base) == SSA_NAME) + current_function->record_decl (t, base, -1); + } + else if (!d) + return false; + else + t = d->type; + + if (t->has_escaped ()) + return false; + + if (mark_as_bit_field) + { + gcc_assert (can_escape); + t->mark_escape (escape_bitfields, NULL); + return false; + } + + if (wholeaccess (expr, base, accesstype, t)) + { + field = NULL; + type = t; + indirect = accesstype != NULL; + return true; + } + + srfield *f = t->find_field (offset); + if (!f) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nunkown field\n"); + print_generic_expr (dump_file, expr); + fprintf (dump_file, "\n"); + print_generic_expr (dump_file, base); + fprintf (dump_file, "\n"); + } + gcc_assert (can_escape); + t->mark_escape (escape_unkown_field, NULL); + return false; + } + if (!types_compatible_p (f->fieldtype, TREE_TYPE (expr))) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nfieldtype = "); + print_generic_expr (dump_file, f->fieldtype); + fprintf (dump_file, "\naccess type = "); + print_generic_expr (dump_file, TREE_TYPE (expr)); + fprintf (dump_file, "original expr = "); + print_generic_expr (dump_file, expr); + fprintf (dump_file, "\n"); + } + gcc_assert (can_escape); + t->mark_escape (escape_unkown_field, NULL); + return false; + } + field = f; + type = t; + indirect = accesstype != NULL; + return true; +} + +/* Mark the type used in EXPR as escaping. */ + +void +ipa_struct_reorg::mark_expr_escape (tree expr, escape_type escapes, + gimple *stmt) +{ + tree base; + bool indirect; + srtype *type; + srfield *field; + bool realpart, imagpart, address; + if (!get_type_field (expr, base, indirect, type, field, + realpart, imagpart, address)) + return; + + type->mark_escape (escapes, stmt); +} + +/* Record accesses in a call statement STMT. */ + +void +ipa_struct_reorg::maybe_record_call (cgraph_node *node, gcall *stmt) +{ + tree argtype; + tree fndecl; + escape_type escapes = does_not_escape; + bool free_or_realloc = gimple_call_builtin_p (stmt, BUILT_IN_FREE) + || gimple_call_builtin_p (stmt, BUILT_IN_REALLOC); + + /* We check allocation sites in a different location. */ + if (handled_allocation_stmt (stmt)) + return; + + /* A few cases here: + 1) assigned from the lhs + 2) Used in argument + If a function being called is global (or indirect) + then we reject the types as being escaping. */ + + if (tree chain = gimple_call_chain (stmt)) + record_stmt_expr (chain, node, stmt); + + /* Assigned from LHS. */ + if (tree lhs = gimple_call_lhs (stmt)) + { + /* FIXME: handle return types. */ + mark_type_as_escape (TREE_TYPE (lhs), escape_return); + } + + /* If we have an internal call, just record the stmt. */ + if (gimple_call_internal_p (stmt)) + { + for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) + record_stmt_expr (gimple_call_arg (stmt, i), node, stmt); + return; + } + + fndecl = gimple_call_fndecl (stmt); + + /* If we have an indrect call, just mark the types as escape. */ + if (!fndecl) + escapes = escape_pointer_function; + /* Non local functions cause escape except for calls to free + and realloc. + FIXME: should support function annotations too. */ + else if (!free_or_realloc + && !cgraph_node::local_info_node (fndecl)->local) + escapes = escape_external_function; + else if (!free_or_realloc + && !cgraph_node::local_info_node (fndecl)->can_change_signature) + escapes = escape_cannot_change_signature; + /* FIXME: we should be able to handle functions in other partitions. */ + else if (symtab_node::get (fndecl)->in_other_partition) + escapes = escape_external_function; + + if (escapes != does_not_escape) + { + for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) + mark_type_as_escape (TREE_TYPE (gimple_call_arg (stmt, i)), + escapes); + return; + } + + argtype = TYPE_ARG_TYPES (gimple_call_fntype (stmt)); + for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) + { + tree arg = gimple_call_arg (stmt, i); + if (argtype) + { + tree argtypet = TREE_VALUE (argtype); + if (!free_or_realloc + && VOID_POINTER_P (argtypet)) + mark_type_as_escape (TREE_TYPE (arg), escape_cast_void); + else + record_stmt_expr (arg, node, stmt); + } + else + mark_type_as_escape (TREE_TYPE (arg), escape_var_arg_function); + + argtype = argtype ? TREE_CHAIN (argtype) : NULL_TREE; + } +} + +void +ipa_struct_reorg::record_stmt_expr (tree expr, cgraph_node *node, gimple *stmt) +{ + tree base; + bool indirect; + srtype *type; + srfield *field; + bool realpart, imagpart, address; + if (!get_type_field (expr, base, indirect, type, field, + realpart, imagpart, address)) + return; + + if (!opt_for_fn (current_function_decl, flag_ipa_struct_reorg)) + type->mark_escape (escape_non_optimize, stmt); + + /* Record it. */ + type->add_access (new sraccess (stmt, node, type, field)); +} + +/* Find function corresponding to NODE. */ + +srfunction * +ipa_struct_reorg::find_function (cgraph_node *node) +{ + for (unsigned i = 0; i < functions.length (); i++) + if (functions[i]->node == node) + return functions[i]; + return NULL; +} + +void +ipa_struct_reorg::check_type_and_push (tree newdecl, srtype *type, + vec<srdecl *> &worklist, + gimple *stmt) +{ + if (integer_zerop (newdecl)) + return; + + if (TREE_CODE (newdecl) == ADDR_EXPR) + { + srdecl *d = find_decl (TREE_OPERAND (newdecl, 0)); + if (!d) + { + type->mark_escape (escape_cast_another_ptr, stmt); + return; + } + if (d->type == type) + return; + + srtype *type1 = d->type; + type->mark_escape (escape_cast_another_ptr, stmt); + type1->mark_escape (escape_cast_another_ptr, stmt); + return; + } + + srdecl *d = find_decl (newdecl); + if (!d) + { + if (TREE_CODE (newdecl) == INTEGER_CST) + { + type->mark_escape (escape_int_const, stmt); + return; + } + /* If we have a non void* or a decl (which is hard to track), + then mark the type as escaping. */ + if (!VOID_POINTER_P (TREE_TYPE (newdecl)) + || DECL_P (newdecl)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nunkown decl: "); + print_generic_expr (dump_file, newdecl); + fprintf (dump_file, " in type:\n"); + print_generic_expr (dump_file, TREE_TYPE (newdecl)); + fprintf (dump_file, "\n"); + } + type->mark_escape (escape_cast_another_ptr, stmt); + return; + } + /* At this point there should only be unkown void* ssa names. */ + gcc_assert (TREE_CODE (newdecl) == SSA_NAME); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nrecording unkown decl: "); + print_generic_expr (dump_file, newdecl); + fprintf (dump_file, " as type:\n"); + type->simple_dump (dump_file); + fprintf (dump_file, "\n"); + } + d = current_function->record_decl (type, newdecl, -1); + worklist.safe_push (d); + return; + } + + /* Only add to the worklist if the decl is a SSA_NAME. */ + if (TREE_CODE (newdecl) == SSA_NAME) + worklist.safe_push (d); + if (d->type == type) + return; + + srtype *type1 = d->type; + type->mark_escape (escape_cast_another_ptr, stmt); + type1->mark_escape (escape_cast_another_ptr, stmt); +} + +/* + 2) Check SSA_NAMEs for non type usages (source or use) (worlist of srdecl) + a) if the SSA_NAME is sourced from a pointer plus, record the pointer and + check to make sure the addition was a multiple of the size. + check the pointer type too. + b) If the name is sourced from an allocation check the allocation + i) Add SSA_NAME (void*) to the worklist if allocated from realloc + c) if the name is from a param, make sure the param type was of the + original type + d) if the name is from a cast/assignment, make sure it is used as that + type or void* + i) If void* then push the ssa_name into worklist +*/ +void +ipa_struct_reorg::check_definition (srdecl *decl, vec<srdecl *> &worklist) +{ + tree ssa_name = decl->decl; + srtype *type = decl->type; + + /* + c) if the name is from a param, make sure the param type was of the + original type. + */ + if (SSA_NAME_IS_DEFAULT_DEF (ssa_name)) + { + tree var = SSA_NAME_VAR (ssa_name); + if (var + && TREE_CODE (var) == PARM_DECL + && VOID_POINTER_P (TREE_TYPE (ssa_name))) + type->mark_escape (escape_cast_void, NULL); + return; + } + gimple *stmt = SSA_NAME_DEF_STMT (ssa_name); + + /* + b) If the name is sourced from an allocation check the allocation + i) Add SSA_NAME (void*) to the worklist if allocated from realloc + */ + if (gimple_code (stmt) == GIMPLE_CALL) + { + /* For realloc, check the type of the argument. */ + if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC)) + check_type_and_push (gimple_call_arg (stmt, 0), type, worklist, stmt); + + if (!handled_allocation_stmt (stmt) + || !allocate_size (type, stmt)) + type->mark_escape (escape_return, stmt); + return; + } + /* If the SSA_NAME is sourced from an inline-asm, + just mark the type as escaping. */ + if (gimple_code (stmt) == GIMPLE_ASM) + { + type->mark_escape (escape_inline_asm, stmt); + return; + } + + /* If the SSA_NAME is sourced from a PHI check add + each name to the worklist and check to make sure + they are used correctly. */ + if (gimple_code (stmt) == GIMPLE_PHI) + { + for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++) + check_type_and_push (gimple_phi_arg_def (stmt, i), + type, worklist, stmt); + return; + } + + gcc_assert (gimple_code (stmt) == GIMPLE_ASSIGN); + /* + a) if the SSA_NAME is sourced from a pointer plus, record the pointer and + check to make sure the addition was a multiple of the size. + check the pointer type too. + */ + + tree rhs = gimple_assign_rhs1 (stmt); + if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) + { + tree rhs2 = gimple_assign_rhs2 (stmt); + tree num; + if (!is_result_of_mult (rhs2, &num, TYPE_SIZE_UNIT (type->type))) + type->mark_escape (escape_non_multiply_size, stmt); + + if (TREE_CODE (rhs) == SSA_NAME) + check_type_and_push (rhs, type, worklist, stmt); + return; + } + + /* Casts between pointers and integer are escaping. */ + if (gimple_assign_cast_p (stmt)) + { + type->mark_escape (escape_cast_int, stmt); + return; + } + + /* + d) if the name is from a cast/assignment, make sure it is used as that + type or void* + i) If void* then push the ssa_name into worklist + */ + gcc_assert (gimple_assign_single_p (stmt)); + check_other_side (decl, rhs, stmt, worklist); +} + +/* Mark the types used by the inline-asm as escaping. + It is unkown what happens inside an inline-asm. */ + +void +ipa_struct_reorg::mark_types_asm (gasm *astmt) +{ + for (unsigned i = 0; i < gimple_asm_ninputs (astmt); i++) + { + tree v = TREE_VALUE (gimple_asm_input_op (astmt, i)); + /* If we have &b, just strip the & here. */ + if (TREE_CODE (v) == ADDR_EXPR) + v = TREE_OPERAND (v, 0); + mark_expr_escape (v, escape_inline_asm, astmt); + } + for (unsigned i = 0; i < gimple_asm_noutputs (astmt); i++) + { + tree v = TREE_VALUE (gimple_asm_output_op (astmt, i)); + /* If we have &b, just strip the & here. */ + if (TREE_CODE (v) == ADDR_EXPR) + v = TREE_OPERAND (v, 0); + mark_expr_escape (v, escape_inline_asm, astmt); + } +} + +void +ipa_struct_reorg::check_other_side (srdecl *decl, tree other, gimple *stmt, + vec<srdecl *> &worklist) +{ + srtype *type = decl->type; + + if (TREE_CODE (other) == SSA_NAME + || DECL_P (other) + || TREE_CODE (other) == INTEGER_CST) + { + check_type_and_push (other, type, worklist, stmt); + return; + } + + tree t = TREE_TYPE (other); + if (!handled_type (t)) + { + type->mark_escape (escape_cast_another_ptr, stmt); + return; + } + + srtype *t1 = find_type (inner_type (t)); + if (t1 == type) + { + tree base; + bool indirect; + srtype *type1; + srfield *field; + bool realpart, imagpart, address; + if (!get_type_field (other, base, indirect, type1, field, + realpart, imagpart, address)) + type->mark_escape (escape_cast_another_ptr, stmt); + + return; + } + + if (t1) + t1->mark_escape (escape_cast_another_ptr, stmt); + + type->mark_escape (escape_cast_another_ptr, stmt); +} + +void +ipa_struct_reorg::check_use (srdecl *decl, gimple *stmt, + vec<srdecl *> &worklist) +{ + srtype *type = decl->type; + + if (gimple_code (stmt) == GIMPLE_RETURN) + { + type->mark_escape (escape_return, stmt); + return; + } + /* If the SSA_NAME PHI check and add the src to the worklist and + check to make sure they are used correctly. */ + if (gimple_code (stmt) == GIMPLE_PHI) + { + check_type_and_push (gimple_phi_result (stmt), type, worklist, stmt); + return; + } + + if (gimple_code (stmt) == GIMPLE_ASM) + { + mark_types_asm (as_a <gasm *> (stmt)); + return; + } + + if (gimple_code (stmt) == GIMPLE_COND) + { + tree rhs1 = gimple_cond_lhs (stmt); + tree rhs2 = gimple_cond_rhs (stmt); + tree orhs = rhs1; + if (gimple_cond_code (stmt) != EQ_EXPR + && gimple_cond_code (stmt) != NE_EXPR) + { + mark_expr_escape (rhs1, escape_non_eq, stmt); + mark_expr_escape (rhs2, escape_non_eq, stmt); + } + if (rhs1 == decl->decl) + orhs = rhs2; + if (integer_zerop (orhs)) + return; + if (TREE_CODE (orhs) != SSA_NAME) + mark_expr_escape (rhs1, escape_non_eq, stmt); + check_type_and_push (orhs, type, worklist, stmt); + return; + } + + /* Casts between pointers and integer are escaping. */ + if (gimple_assign_cast_p (stmt)) + { + type->mark_escape (escape_cast_int, stmt); + return; + } + + /* We might have a_1 = ptr_2 == ptr_3; */ + if (is_gimple_assign (stmt) + && TREE_CODE_CLASS (gimple_assign_rhs_code (stmt)) == tcc_comparison) + { + tree rhs1 = gimple_assign_rhs1 (stmt); + tree rhs2 = gimple_assign_rhs2 (stmt); + tree orhs = rhs1; + if (gimple_assign_rhs_code (stmt) != EQ_EXPR + && gimple_assign_rhs_code (stmt) != NE_EXPR) + { + mark_expr_escape (rhs1, escape_non_eq, stmt); + mark_expr_escape (rhs2, escape_non_eq, stmt); + } + if (rhs1 == decl->decl) + orhs = rhs2; + if (integer_zerop (orhs)) + return; + if (TREE_CODE (orhs) != SSA_NAME) + mark_expr_escape (rhs1, escape_non_eq, stmt); + check_type_and_push (orhs, type, worklist, stmt); + return; + } + + if (gimple_assign_single_p (stmt)) + { + tree lhs = gimple_assign_lhs (stmt); + tree rhs = gimple_assign_rhs1 (stmt); + /* Check if we have a_1 = b_2; that a_1 is in the correct type. */ + if (decl->decl == rhs) + { + check_other_side (decl, lhs, stmt, worklist); + return; + } + } + + if (is_gimple_assign (stmt) + && gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) + { + tree rhs2 = gimple_assign_rhs2 (stmt); + tree lhs = gimple_assign_lhs (stmt); + tree num; + check_other_side (decl, lhs, stmt, worklist); + if (!is_result_of_mult (rhs2, &num, TYPE_SIZE_UNIT (type->type))) + type->mark_escape (escape_non_multiply_size, stmt); + } +} + +/* + 2) Check SSA_NAMEs for non type usages (source or use) (worlist of srdecl) + d) if the name is from a cast/assignment, make sure it is used as that + type or void* + i) If void* then push the ssa_name into worklist + e) if used in conditional check the other side + i) If the conditional is non NE/EQ then mark the type as non rejecting + f) Check if the use in a Pointer PLUS EXPR Is used by mulitplication + of its size + */ +void +ipa_struct_reorg::check_uses (srdecl *decl, vec<srdecl *> &worklist) +{ + tree ssa_name = decl->decl; + imm_use_iterator imm_iter; + use_operand_p use_p; + + FOR_EACH_IMM_USE_FAST (use_p, imm_iter, ssa_name) + { + gimple *stmt = USE_STMT (use_p); + + if (is_gimple_debug (stmt)) + continue; + + check_use (decl, stmt, worklist); + } +} + +/* Record function corresponding to NODE. */ + +srfunction * +ipa_struct_reorg::record_function (cgraph_node *node) +{ + function *fn; + tree parm, var; + unsigned int i; + srfunction *sfn; + escape_type escapes = does_not_escape; + + sfn = new srfunction (node); + functions.safe_push (sfn); + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "\nRecording accesses and types from function: %s/%u\n", + node->name (), node->order); + + /* Nodes without a body are not interesting. Especially do not + visit clones at this point for now - we get duplicate decls + there for inline clones at least. */ + if (!node->has_gimple_body_p () || node->inlined_to) + return sfn; + + node->get_body (); + fn = DECL_STRUCT_FUNCTION (node->decl); + + if (!fn) + return sfn; + + current_function = sfn; + + if (DECL_PRESERVE_P (node->decl)) + escapes = escape_marked_as_used; + else if (!node->local) + escapes = escape_visible_function; + else if (!node->can_change_signature) + escapes = escape_cannot_change_signature; + else if (!tree_versionable_function_p (node->decl)) + escapes = escape_noclonable_function; + else if (!opt_for_fn (node->decl, flag_ipa_struct_reorg)) + escapes = escape_non_optimize; + + basic_block bb; + gimple_stmt_iterator si; + + /* Record the static chain decl. */ + if (fn->static_chain_decl) + { + srdecl *sd = record_var (fn->static_chain_decl, + escapes, -2); + if (sd) + { + /* Specify that this type is used by the static + chain so it cannot be split. */ + sd->type->chain_type = true; + sfn->add_arg (sd); + sd->type->add_function (sfn); + } + } + + /* Record the arguments. */ + for (parm = DECL_ARGUMENTS (node->decl), i = 0; + parm; + parm = DECL_CHAIN (parm), i++) + { + srdecl *sd = record_var (parm, escapes, i); + if (sd) + { + sfn->add_arg (sd); + sd->type->add_function (sfn); + } + } + + /* Mark the return type as escaping. */ + { + tree return_type = TREE_TYPE (TREE_TYPE (node->decl)); + mark_type_as_escape (return_type, escape_return, NULL); + } + + /* If the cfg does not exist for the function, + don't process the function. */ + if (!fn->cfg) + { + current_function = NULL; + return sfn; + } + + /* The following order is done for recording stage: + 0) Record all variables/SSA_NAMES that are of struct type + 1) Record MEM_REF/COMPONENT_REFs + a) Record SSA_NAMEs (void*) and record that as the accessed type. + */ + + push_cfun (fn); + + FOR_EACH_LOCAL_DECL (cfun, i, var) + { + if (TREE_CODE (var) != VAR_DECL) + continue; + + record_var (var); + } + + for (i = 1; i < num_ssa_names; ++i) + { + tree name = ssa_name (i); + if (!name + || has_zero_uses (name) + || virtual_operand_p (name)) + continue; + + record_var (name); + } + + /* Find the variables which are used via MEM_REF and are void* types. */ + FOR_EACH_BB_FN (bb, cfun) + { + for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) + { + gimple *stmt = gsi_stmt (si); + find_vars (stmt); + } + } + + auto_vec<srdecl *> worklist; + for (unsigned i = 0; i < current_function->decls.length (); i++) + { + srdecl *decl = current_function->decls[i]; + if (TREE_CODE (decl->decl) == SSA_NAME) + { + decl->visited = false; + worklist.safe_push (decl); + } + } + +/* + 2) Check SSA_NAMEs for non type usages (source or use) (worlist of srdecl) + a) if the SSA_NAME is sourced from a pointer plus, record the pointer and + check to make sure the addition was a multiple of the size. + check the pointer type too. + b) If the name is sourced from an allocation check the allocation + i) Add SSA_NAME (void*) to the worklist if allocated from realloc + c) if the name is from a param, make sure the param type was of the + original type + d) if the name is used in a cast/assignment, make sure it is used as that + type or void* + i) If void* then push the ssa_name into worklist + e) if used in conditional check the other side + i) If the conditional is non NE/EQ then mark the type as non rejecting + f) Check if the use in a POinter PLUS EXPR Is used by mulitplication + of its size +*/ + + while (!worklist.is_empty ()) + { + srdecl *decl = worklist.pop (); + if (decl->visited) + continue; + decl->visited = true; + check_definition (decl, worklist); + check_uses (decl, worklist); + } + + FOR_EACH_BB_FN (bb, cfun) + { + for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) + { + gimple *stmt = gsi_stmt (si); + maybe_record_stmt (node, stmt); + } + } + + pop_cfun (); + current_function = NULL; + return sfn; +} + +/* Record all accesses for all types including global variables. */ + +void +ipa_struct_reorg::record_accesses (void) +{ + varpool_node *var; + cgraph_node *cnode; + + /* Record global (non-auto) variables first. */ + FOR_EACH_VARIABLE (var) + { + if (!var->real_symbol_p ()) + continue; + + /* Record all variables including the accesses inside a variable. */ + escape_type escapes = does_not_escape; + if (var->externally_visible || !var->definition) + escapes = escape_via_global_var; + if (var->in_other_partition) + escapes = escape_via_global_var; + if (!var->externally_visible && var->definition) + var->get_constructor (); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Recording global variable: "); + print_generic_expr (dump_file, var->decl); + fprintf (dump_file, "\n"); + } + record_var (var->decl, escapes); + } + + FOR_EACH_FUNCTION (cnode) + { + if (!cnode->real_symbol_p ()) + continue; + + /* Record accesses inside a function. */ + if (cnode->definition) + record_function (cnode); + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "all types (before pruning):\n"); + dump_types (dump_file); + fprintf (dump_file, "all functions (before pruning):\n"); + dump_functions (dump_file); + } + done_recording = true; +} + +/* A helper function to detect cycles (recusive) types. + Return TRUE if TYPE was a rescusive type. */ + +bool +ipa_struct_reorg::walk_field_for_cycles (srtype *type) +{ + unsigned i; + srfield *field; + + type->visited = true; + if (type->escaped_rescusive ()) + return true; + + if (type->has_escaped ()) + return false; + + FOR_EACH_VEC_ELT (type->fields, i, field) + { + if (!field->type) + ; + else if (field->type->visited + || walk_field_for_cycles (field->type)) + { + type->mark_escape (escape_rescusive_type, NULL); + return true; + } + } + + return false; +} + +/* Clear visited on all types. */ + +void +ipa_struct_reorg::clear_visited (void) +{ + for (unsigned i = 0; i < types.length (); i++) + types[i]->visited = false; +} + +/* Detect recusive types and mark them as escaping. */ + +void +ipa_struct_reorg::detect_cycles (void) +{ + for (unsigned i = 0; i < types.length (); i++) + { + if (types[i]->has_escaped ()) + continue; + + clear_visited (); + walk_field_for_cycles (types[i]); + } +} + +/* Propagate escaping to depdenent types. */ + +void +ipa_struct_reorg::propagate_escape (void) +{ + unsigned i; + srtype *type; + bool changed = false; + + do + { + changed = false; + FOR_EACH_VEC_ELT (types, i, type) + { + for (tree field = TYPE_FIELDS (type->type); + field; + field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) == FIELD_DECL + && handled_type (TREE_TYPE (field))) + { + tree t = inner_type (TREE_TYPE (field)); + srtype *type1 = find_type (t); + if (!type1) + continue; + if (type1->has_escaped () + && !type->has_escaped ()) + { + type->mark_escape (escape_dependent_type_escapes, NULL); + changed = true; + } + if (type->has_escaped () + && !type1->has_escaped ()) + { + type1->mark_escape (escape_dependent_type_escapes, NULL); + changed = true; + } + } + } + } + } while (changed); +} + +/* Prune the escaped types and their decls from what was recorded. */ + +void +ipa_struct_reorg::prune_escaped_types (void) +{ + detect_cycles (); + propagate_escape (); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "all types (after prop but before pruning):\n"); + dump_types (dump_file); + fprintf (dump_file, "all functions (after prop but before pruning):\n"); + dump_functions (dump_file); + } + + if (dump_file) + dump_types_escaped (dump_file); + + /* Prune the function arguments which escape + and functions which have no types as arguments. */ + for (unsigned i = 0; i < functions.length ();) + { + srfunction *function = functions[i]; + + /* Prune function arguments of types that escape. */ + for (unsigned j = 0; j < function->args.length ();) + { + if (function->args[j]->type->has_escaped ()) + function->args.ordered_remove (j); + else + j++; + } + + /* Prune global variables that the function uses of types + that escape. */ + for (unsigned j = 0; j < function->globals.length ();) + { + if (function->globals[j]->type->has_escaped ()) + function->globals.ordered_remove (j); + else + j++; + } + + /* Prune variables that the function uses of types that escape. */ + for (unsigned j = 0; j < function->decls.length ();) + { + srdecl *decl = function->decls[j]; + if (decl->type->has_escaped ()) + { + function->decls.ordered_remove (j); + delete decl; + } + else + j++; + } + + /* Prune functions which don't refer to any variables any more. */ + if (function->args.is_empty () + && function->decls.is_empty () + && function->globals.is_empty ()) + { + delete function; + functions.ordered_remove (i); + } + else + i++; + } + + /* Prune globals of types that escape, all references to those decls + will have been removed in the first loop. */ + for (unsigned j = 0; j < globals.decls.length ();) + { + srdecl *decl = globals.decls[j]; + if (decl->type->has_escaped ()) + { + globals.decls.ordered_remove (j); + delete decl; + } + else + j++; + } + + /* Prune types that escape, all references to those types + will have been removed in the above loops. */ + for (unsigned i = 0; i < types.length ();) + { + srtype *type = types[i]; + if (type->has_escaped ()) + { + /* All references to this type should have been removed now. */ + delete type; + types.ordered_remove (i); + } + else + i++; + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "all types (after pruning):\n"); + dump_types (dump_file); + fprintf (dump_file, "all functions (after pruning):\n"); + dump_functions (dump_file); + } +} + +/* Analyze all of the types. */ + +void +ipa_struct_reorg::analyze_types (void) +{ + for (unsigned i = 0; i < types.length (); i++) + { + if (!types[i]->has_escaped ()) + types[i]->analyze (); + } +} + +/* When struct A has a struct B member, B's type info + is not stored in + TYPE_FIELDS (TREE_TYPE (TYPE_FIELDS (typeA))) + Try to restore B's type information. */ + +void +ipa_struct_reorg::restore_field_type (void) +{ + for (unsigned i = 0; i < types.length (); i++) + { + for (unsigned j = 0; j < types[i]->fields.length (); j++) + { + srfield *field = types[i]->fields[j]; + if (TREE_CODE (inner_type (field->fieldtype)) == RECORD_TYPE) + { + /* If field type has TYPE_FIELDS information, + we do not need to do this. */ + if (TYPE_FIELDS (field->type->type) != NULL) + continue; + for (unsigned k = 0; k < types.length (); k++) + { + if (i == k) + continue; + const char *type1 = get_type_name (field->type->type); + const char *type2 = get_type_name (types[k]->type); + if (type1 == NULL || type2 == NULL) + continue; + if (type1 == type2 + && TYPE_FIELDS (types[k]->type)) + field->type = types[k]; + } + } + } + } +} + +/* Create all new types we want to create. */ + +bool +ipa_struct_reorg::create_new_types (void) +{ + int newtypes = 0; + clear_visited (); + for (unsigned i = 0; i < types.length (); i++) + newtypes += types[i]->create_new_type (); + + if (dump_file) + { + if (newtypes) + fprintf (dump_file, "\nNumber of structures to transform is %d\n", + newtypes); + else + fprintf (dump_file, "\nNo structures to transform.\n"); + } + + return newtypes != 0; +} + +/* Create all the new decls except for the new arguments + which create_new_functions would have created. */ + +void +ipa_struct_reorg::create_new_decls (void) +{ + globals.create_new_decls (); + for (unsigned i = 0; i < functions.length (); i++) + functions[i]->create_new_decls (); +} + +/* Create the new arguments for the function corresponding to NODE. */ + +void +ipa_struct_reorg::create_new_args (cgraph_node *new_node) +{ + tree decl = new_node->decl; + auto_vec<tree> params; + push_function_arg_decls (¶ms, decl); + vec<ipa_adjusted_param, va_gc> *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<tree> new_params; + push_function_arg_decls (&new_params, decl); + unsigned veclen = vec_safe_length (adjs); + for (unsigned i = 0; i < veclen; i++) + { + if ((*adjs)[i].op != IPA_PARAM_OP_NEW) + continue; + tree decl = params[(*adjs)[i].base_index]; + srdecl *d = find_decl (decl); + if (!d) + continue; + unsigned j = 0; + while (j < max_split && d->newdecl[j]) + j++; + d->newdecl[j] = new_params[i]; + } + + function *fn = DECL_STRUCT_FUNCTION (decl); + + if (!fn->static_chain_decl) + return; + srdecl *chain = find_decl (fn->static_chain_decl); + if (!chain) + return; + + srtype *type = chain->type; + tree orig_var = chain->decl; + const char *tname = NULL; + if (DECL_NAME (orig_var)) + tname = IDENTIFIER_POINTER (DECL_NAME (orig_var)); + gcc_assert (!type->newtype[1]); + tree new_name = NULL; + char *name = NULL; + if (tname) + { + name = concat (tname, ".reorg.0", NULL); + new_name = get_identifier (name); + free (name); + } + tree newtype1 = reconstruct_complex_type (TREE_TYPE (orig_var), + type->newtype[0]); + chain->newdecl[0] = build_decl (DECL_SOURCE_LOCATION (orig_var), + PARM_DECL, new_name, newtype1); + copy_var_attributes (chain->newdecl[0], orig_var); + fn->static_chain_decl = chain->newdecl[0]; +} + +/* Find the refered DECL in the current function or globals. + If this is a global decl, record that as being used + in the current function. */ + +srdecl * +ipa_struct_reorg::find_decl (tree decl) +{ + srdecl *d; + d = globals.find_decl (decl); + if (d) + { + /* Record the global usage in the current function. */ + if (!done_recording && current_function) + { + bool add = true; + /* No reason to add it to the current function if it is + already recorded as such. */ + for (unsigned i = 0; i < current_function->globals.length (); i++) + { + if (current_function->globals[i] == d) + { + add = false; + break; + } + } + if (add) + current_function->globals.safe_push (d); + } + return d; + } + if (current_function) + return current_function->find_decl (decl); + return NULL; +} + +/* Create new function clones for the cases where the arguments + need to be changed. */ + +void +ipa_struct_reorg::create_new_functions (void) +{ + for (unsigned i = 0; i < functions.length (); i++) + { + srfunction *f = functions[i]; + bool anyargchanges = false; + cgraph_node *new_node; + cgraph_node *node = f->node; + int newargs = 0; + if (f->old) + continue; + + if (f->args.length () == 0) + continue; + + for (unsigned j = 0; j < f->args.length (); j++) + { + srdecl *d = f->args[j]; + srtype *t = d->type; + if (t->has_new_type ()) + { + newargs += t->newtype[1] != NULL; + anyargchanges = true; + } + } + if (!anyargchanges) + continue; + + if (dump_file) + { + fprintf (dump_file, "Creating a clone of function: "); + f->simple_dump (dump_file); + fprintf (dump_file, "\n"); + } + statistics_counter_event (NULL, "Create new function", 1); + new_node = node->create_version_clone_with_body (vNULL, NULL, + NULL, NULL, NULL, + "struct_reorg"); + new_node->can_change_signature = node->can_change_signature; + new_node->make_local (); + f->newnode = new_node; + srfunction *n = record_function (new_node); + current_function = n; + n->old = f; + f->newf = n; + /* Create New arguments. */ + create_new_args (new_node); + current_function = NULL; + } +} + +bool +ipa_struct_reorg::rewrite_lhs_rhs (tree lhs, tree rhs, + tree newlhs[max_split], + tree newrhs[max_split]) +{ + bool l = rewrite_expr (lhs, newlhs); + bool r = rewrite_expr (rhs, newrhs); + + /* Handle NULL pointer specially. */ + if (l && !r && integer_zerop (rhs)) + { + r = true; + for (unsigned i = 0; i < max_split && newlhs[i]; i++) + newrhs[i] = fold_convert (TREE_TYPE (newlhs[i]), rhs); + } + + return l || r; +} + +bool +ipa_struct_reorg::rewrite_expr (tree expr, + tree newexpr[max_split], + bool ignore_missing_decl) +{ + tree base; + bool indirect; + srtype *t; + srfield *f; + bool realpart, imagpart; + bool address; + + tree newbase[max_split]; + memset (newexpr, 0, sizeof (tree[max_split])); + + if (TREE_CODE (expr) == CONSTRUCTOR) + { + srtype *t = find_type (TREE_TYPE (expr)); + if (!t) + return false; + gcc_assert (CONSTRUCTOR_NELTS (expr) == 0); + if (!t->has_new_type ()) + return false; + for (unsigned i = 0; i < max_split && t->newtype[i]; i++) + newexpr[i] = build_constructor (t->newtype[i], NULL); + return true; + } + + if (!get_type_field (expr, base, indirect, t, f, + realpart, imagpart, address)) + return false; + + /* If the type is not changed, then just return false. */ + if (!t->has_new_type ()) + return false; + + /* NULL pointer handling is "special". */ + if (integer_zerop (base)) + { + gcc_assert (indirect && !address); + for (unsigned i = 0; i < max_split && t->newtype[i]; i++) + { + tree newtype1 = reconstruct_complex_type (TREE_TYPE (base), + t->newtype[i]); + newbase[i] = fold_convert (newtype1, base); + } + } + else + { + srdecl *d = find_decl (base); + + if (!d && dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Can't find decl:\n"); + print_generic_expr (dump_file, base); + fprintf (dump_file, "\ntype:\n"); + t->dump (dump_file); + } + if (!d && ignore_missing_decl) + return true; + gcc_assert (d); + memcpy (newbase, d->newdecl, sizeof (d->newdecl)); + } + + if (f == NULL) + { + memcpy (newexpr, newbase, sizeof (newbase)); + for (unsigned i = 0; i < max_split && newexpr[i]; i++) + { + if (address) + newexpr[i] = build_fold_addr_expr (newexpr[i]); + if (indirect) + newexpr[i] = build_simple_mem_ref (newexpr[i]); + if (imagpart) + newexpr[i] = build1 (IMAGPART_EXPR, + TREE_TYPE (TREE_TYPE (newexpr[i])), + newexpr[i]); + if (realpart) + newexpr[i] = build1 (REALPART_EXPR, + TREE_TYPE (TREE_TYPE (newexpr[i])), + newexpr[i]); + } + return true; + } + + tree newdecl = newbase[f->clusternum]; + for (unsigned i = 0; i < max_split && f->newfield[i]; i++) + { + tree newbase1 = newdecl; + if (address) + newbase1 = build_fold_addr_expr (newbase1); + if (indirect) + newbase1 = build_simple_mem_ref (newbase1); + newexpr[i] = build3 (COMPONENT_REF, TREE_TYPE (f->newfield[i]), + newbase1, f->newfield[i], NULL_TREE); + if (imagpart) + newexpr[i] = build1 (IMAGPART_EXPR, + TREE_TYPE (TREE_TYPE (newexpr[i])), + newexpr[i]); + if (realpart) + newexpr[i] = build1 (REALPART_EXPR, + TREE_TYPE (TREE_TYPE (newexpr[i])), + newexpr[i]); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "cluster: %d. decl = ", (int)f->clusternum); + print_generic_expr (dump_file, newbase1); + fprintf (dump_file, "\nnewexpr = "); + print_generic_expr (dump_file, newexpr[i]); + fprintf (dump_file, "\n"); + } + } + return true; +} + +bool +ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) +{ + bool remove = false; + if (gimple_clobber_p (stmt)) + { + tree lhs = gimple_assign_lhs (stmt); + tree newlhs[max_split]; + if (!rewrite_expr (lhs, newlhs)) + return false; + for (unsigned i = 0; i < max_split && newlhs[i]; i++) + { + tree clobber = build_constructor (TREE_TYPE (newlhs[i]), NULL); + TREE_THIS_VOLATILE (clobber) = true; + gimple *newstmt = gimple_build_assign (newlhs[i], clobber); + gsi_insert_before (gsi, newstmt, GSI_SAME_STMT); + remove = true; + } + return remove; + } + + if (gimple_assign_rhs_code (stmt) == EQ_EXPR + || gimple_assign_rhs_code (stmt) == NE_EXPR) + { + tree rhs1 = gimple_assign_rhs1 (stmt); + tree rhs2 = gimple_assign_rhs2 (stmt); + tree newrhs1[max_split]; + tree newrhs2[max_split]; + tree_code rhs_code = gimple_assign_rhs_code (stmt); + tree_code code = rhs_code == EQ_EXPR ? BIT_AND_EXPR : BIT_IOR_EXPR; + if (!rewrite_lhs_rhs (rhs1, rhs2, newrhs1, newrhs2)) + return false; + tree newexpr = NULL_TREE; + for (unsigned i = 0; i < max_split && newrhs1[i]; i++) + { + tree expr = gimplify_build2 (gsi, rhs_code, boolean_type_node, + newrhs1[i], newrhs2[i]); + if (!newexpr) + newexpr = expr; + else + newexpr = gimplify_build2 (gsi, code, boolean_type_node, + newexpr, expr); + } + + if (newexpr) + { + newexpr = fold_convert (TREE_TYPE (gimple_assign_lhs (stmt)), + newexpr); + gimple_assign_set_rhs_from_tree (gsi, newexpr); + update_stmt (stmt); + } + return false; + } + + if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) + { + tree lhs = gimple_assign_lhs (stmt); + tree rhs1 = gimple_assign_rhs1 (stmt); + tree rhs2 = gimple_assign_rhs2 (stmt); + tree newlhs[max_split]; + tree newrhs[max_split]; + + if (!rewrite_lhs_rhs (lhs, rhs1, newlhs, newrhs)) + return false; + tree size = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (lhs))); + tree num; + /* Check if rhs2 is a multiplication of the size of the type. */ + if (!is_result_of_mult (rhs2, &num, size)) + internal_error ( + "The rhs of pointer is not a multiplicate and it slips through"); + + num = gimplify_build1 (gsi, NOP_EXPR, sizetype, num); + for (unsigned i = 0; i < max_split && newlhs[i]; i++) + { + gimple *new_stmt; + + tree newsize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (newlhs[i]))); + newsize = gimplify_build2 (gsi, MULT_EXPR, sizetype, num, newsize); + new_stmt = gimple_build_assign (newlhs[i], POINTER_PLUS_EXPR, + newrhs[i], newsize); + gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT); + remove = true; + } + return remove; + } + if (gimple_assign_rhs_class (stmt) == GIMPLE_SINGLE_RHS) + { + tree lhs = gimple_assign_lhs (stmt); + tree rhs = gimple_assign_rhs1 (stmt); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "rewriting statement:\n"); + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + } + tree newlhs[max_split]; + tree newrhs[max_split]; + if (!rewrite_lhs_rhs (lhs, rhs, newlhs, newrhs)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\nDid nothing to statement.\n"); + return false; + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\nreplaced with:\n"); + for (unsigned i = 0; i < max_split && (newlhs[i] || newrhs[i]); i++) + { + gimple *newstmt = gimple_build_assign (newlhs[i] ? newlhs[i] : lhs, + newrhs[i] ? newrhs[i] : rhs); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + print_gimple_stmt (dump_file, newstmt, 0); + fprintf (dump_file, "\n"); + } + gsi_insert_before (gsi, newstmt, GSI_SAME_STMT); + remove = true; + } + return remove; + } + + return remove; +} + +/* Rewrite function call statement STMT. Return TRUE if the statement + is to be removed. */ + +bool +ipa_struct_reorg::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi) +{ + /* Handled allocation calls are handled seperately from normal + function calls. */ + if (handled_allocation_stmt (stmt)) + { + tree lhs = gimple_call_lhs (stmt); + tree newrhs1[max_split]; + srdecl *decl = find_decl (lhs); + if (!decl || !decl->type) + return false; + srtype *type = decl->type; + tree num = allocate_size (type, stmt); + gcc_assert (num); + memset (newrhs1, 0, sizeof (newrhs1)); + + /* The realloc call needs to have its first argument rewritten. */ + if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC)) + { + tree rhs1 = gimple_call_arg (stmt, 0); + if (integer_zerop (rhs1)) + { + for (unsigned i = 0; i < max_split; i++) + newrhs1[i] = rhs1; + } + else if (!rewrite_expr (rhs1, newrhs1)) + internal_error ("Rewrite failed for realloc"); + } + + /* Go through each new lhs. */ + for (unsigned i = 0; i < max_split && decl->newdecl[i]; i++) + { + tree newsize = TYPE_SIZE_UNIT (type->type); + gimple *g; + /* Every allocation except for calloc needs + the size multiplied out. */ + if (!gimple_call_builtin_p (stmt, BUILT_IN_CALLOC)) + newsize = gimplify_build2 (gsi, MULT_EXPR, sizetype, num, newsize); + + if (gimple_call_builtin_p (stmt, BUILT_IN_MALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA)) + g = gimple_build_call (gimple_call_fndecl (stmt), + 1, newsize); + else if (gimple_call_builtin_p (stmt, BUILT_IN_CALLOC)) + g = gimple_build_call (gimple_call_fndecl (stmt), + 2, num, newsize); + else if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC)) + g = gimple_build_call (gimple_call_fndecl (stmt), + 2, newrhs1[i], newsize); + else + gcc_assert (false); + gimple_call_set_lhs (g, decl->newdecl[i]); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + } + return true; + } + + /* The function call free needs to be handled special. */ + if (gimple_call_builtin_p (stmt, BUILT_IN_FREE)) + { + tree expr = gimple_call_arg (stmt, 0); + tree newexpr[max_split]; + if (!rewrite_expr (expr, newexpr)) + return false; + + if (newexpr[1] == NULL) + { + gimple_call_set_arg (stmt, 0, newexpr[0]); + update_stmt (stmt); + return false; + } + + for (unsigned i = 0; i < max_split && newexpr[i]; i++) + { + gimple *g = gimple_build_call (gimple_call_fndecl (stmt), + 1, newexpr[i]); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + } + return true; + } + + /* Otherwise, look up the function to see if we have cloned it + and rewrite the arguments. */ + tree fndecl = gimple_call_fndecl (stmt); + + /* Indirect calls are already marked as escaping so ignore. */ + if (!fndecl) + return false; + + cgraph_node *node = cgraph_node::get (fndecl); + gcc_assert (node); + srfunction *f = find_function (node); + + /* Did not find the function or had not cloned it return saying don't + change the function call. */ + if (!f || !f->newf) + return false; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Changing arguments for function call :\n"); + print_gimple_expr (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + } + + /* Move over to the new function. */ + f = f->newf; + + tree chain = gimple_call_chain (stmt); + unsigned nargs = gimple_call_num_args (stmt); + auto_vec<tree> vargs (nargs); + + if (chain) + { + tree newchains[max_split]; + if (rewrite_expr (chain, newchains)) + { + /* Chain decl's type cannot be split and but it can change. */ + gcc_assert (newchains[1] == NULL); + chain = newchains[0]; + } + } + + for (unsigned i = 0; i < nargs; i++) + vargs.quick_push (gimple_call_arg (stmt, i)); + + int extraargs = 0; + + for (unsigned i = 0; i < f->args.length (); i++) + { + srdecl *d = f->args[i]; + if (d->argumentnum == -2) + continue; + gcc_assert (d->argumentnum != -1); + tree arg = vargs[d->argumentnum + extraargs]; + tree newargs[max_split]; + if (!rewrite_expr (arg, newargs)) + continue; + + /* If this ARG has a replacement handle the replacement. */ + for (unsigned j = 0; j < max_split && d->newdecl[j]; j++) + { + gcc_assert (newargs[j]); + /* If this is the first replacement of the arugment, + then just replace it. */ + if (j == 0) + vargs[d->argumentnum + extraargs] = newargs[j]; + else + { + /* More than one replacement, + we need to insert into the array. */ + extraargs++; + vargs.safe_insert (d->argumentnum + extraargs, newargs[j]); + } + } + } + + gcall *new_stmt; + + new_stmt = gimple_build_call_vec (f->node->decl, vargs); + + if (gimple_call_lhs (stmt)) + gimple_call_set_lhs (new_stmt, gimple_call_lhs (stmt)); + + gimple_set_vuse (new_stmt, gimple_vuse (stmt)); + gimple_set_vdef (new_stmt, gimple_vdef (stmt)); + + if (gimple_has_location (stmt)) + gimple_set_location (new_stmt, gimple_location (stmt)); + gimple_call_copy_flags (new_stmt, stmt); + gimple_call_set_chain (new_stmt, chain); + + gimple_set_modified (new_stmt, true); + + if (gimple_vdef (new_stmt) + && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME) + SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt; + + gsi_replace (gsi, new_stmt, false); + + /* We need to defer cleaning EH info on the new statement to + fixup-cfg. We may not have dominator information at this point + and thus would end up with unreachable blocks and have no way + to communicate that we need to run CFG cleanup then. */ + int lp_nr = lookup_stmt_eh_lp (stmt); + if (lp_nr != 0) + { + remove_stmt_from_eh_lp (stmt); + add_stmt_to_eh_lp (new_stmt, lp_nr); + } + + return false; +} + +/* Rewrite the conditional statement STMT. Return TRUE if the + old statement is to be removed. */ + +bool +ipa_struct_reorg::rewrite_cond (gcond *stmt, gimple_stmt_iterator *gsi) +{ + tree_code rhs_code = gimple_cond_code (stmt); + + /* Handle only equals or not equals conditionals. */ + if (rhs_code != EQ_EXPR + && rhs_code != NE_EXPR) + return false; + tree rhs1 = gimple_cond_lhs (stmt); + tree rhs2 = gimple_cond_rhs (stmt); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "COND: Rewriting\n"); + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + print_generic_expr (dump_file, rhs1); + fprintf (dump_file, "\n"); + print_generic_expr (dump_file, rhs2); + fprintf (dump_file, "\n"); + } + + tree newrhs1[max_split]; + tree newrhs2[max_split]; + tree_code code = rhs_code == EQ_EXPR ? BIT_AND_EXPR : BIT_IOR_EXPR; + if (!rewrite_lhs_rhs (rhs1, rhs2, newrhs1, newrhs2)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\nDid nothing to statement.\n"); + return false; + } + + tree newexpr = NULL_TREE; + for (unsigned i = 0; i < max_split && newrhs1[i]; i++) + { + tree expr = gimplify_build2 (gsi, rhs_code, boolean_type_node, + newrhs1[i], newrhs2[i]); + if (!newexpr) + newexpr = expr; + else + newexpr = gimplify_build2 (gsi, code, boolean_type_node, + newexpr, expr); + } + + if (newexpr) + { + gimple_cond_set_lhs (stmt, newexpr); + gimple_cond_set_rhs (stmt, boolean_true_node); + update_stmt (stmt); + } + return false; +} + +/* Rewrite debug statments if possible. Return TRUE if the statement + should be removed. */ + +bool +ipa_struct_reorg::rewrite_debug (gimple *stmt, gimple_stmt_iterator *) +{ + bool remove = false; + if (gimple_debug_bind_p (stmt)) + { + tree var = gimple_debug_bind_get_var (stmt); + tree newvar[max_split]; + if (rewrite_expr (var, newvar, true)) + remove = true; + if (gimple_debug_bind_has_value_p (stmt)) + { + var = gimple_debug_bind_get_value (stmt); + if (TREE_CODE (var) == POINTER_PLUS_EXPR) + var = TREE_OPERAND (var, 0); + if (rewrite_expr (var, newvar, true)) + remove = true; + } + } + else if (gimple_debug_source_bind_p (stmt)) + { + tree var = gimple_debug_source_bind_get_var (stmt); + tree newvar[max_split]; + if (rewrite_expr (var, newvar, true)) + remove = true; + var = gimple_debug_source_bind_get_value (stmt); + if (TREE_CODE (var) == POINTER_PLUS_EXPR) + var = TREE_OPERAND (var, 0); + if (rewrite_expr (var, newvar, true)) + remove = true; + } + + return remove; +} + +/* Rewrite PHI nodes, return true if the PHI was replaced. */ + +bool +ipa_struct_reorg::rewrite_phi (gphi *phi) +{ + tree newlhs[max_split]; + gphi *newphi[max_split]; + tree result = gimple_phi_result (phi); + gphi_iterator gsi; + + memset (newphi, 0, sizeof (newphi)); + + if (!rewrite_expr (result, newlhs)) + return false; + + if (newlhs[0] == NULL) + return false; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nrewriting PHI:"); + print_gimple_stmt (dump_file, phi, 0); + } + + for (unsigned i = 0; i < max_split && newlhs[i]; i++) + newphi[i] = create_phi_node (newlhs[i], gimple_bb (phi)); + + for (unsigned i = 0; i < gimple_phi_num_args (phi); i++) + { + tree newrhs[max_split]; + phi_arg_d rhs = *gimple_phi_arg (phi, i); + rewrite_expr (rhs.def, newrhs); + for (unsigned j = 0; j < max_split && newlhs[j]; j++) + { + SET_PHI_ARG_DEF (newphi[j], i, newrhs[j]); + gimple_phi_arg_set_location (newphi[j], i, rhs.locus); + update_stmt (newphi[j]); + } + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\ninto\n:"); + for (unsigned i = 0; i < max_split && newlhs[i]; i++) + { + print_gimple_stmt (dump_file, newphi[i], 0); + fprintf (dump_file, "\n"); + } + } + + gsi = gsi_for_phi (phi); + remove_phi_node (&gsi, false); + + return true; +} + +/* Rewrite gimple statement STMT, return true if the STATEMENT + is to be removed. */ + +bool +ipa_struct_reorg::rewrite_stmt (gimple *stmt, gimple_stmt_iterator *gsi) +{ + switch (gimple_code (stmt)) + { + case GIMPLE_ASSIGN: + return rewrite_assign (as_a <gassign *> (stmt), gsi); + case GIMPLE_CALL: + return rewrite_call (as_a <gcall *> (stmt), gsi); + case GIMPLE_COND: + return rewrite_cond (as_a <gcond *> (stmt), gsi); + break; + case GIMPLE_GOTO: + case GIMPLE_SWITCH: + break; + case GIMPLE_DEBUG: + case GIMPLE_ASM: + break; + default: + break; + } + return false; +} + +/* Does the function F uses any decl which has changed. */ + +bool +ipa_struct_reorg::has_rewritten_type (srfunction *f) +{ + for (unsigned i = 0; i < f->decls.length (); i++) + { + srdecl *d = f->decls[i]; + if (d->newdecl[0] != d->decl) + return true; + } + + for (unsigned i = 0; i < f->globals.length (); i++) + { + srdecl *d = f->globals[i]; + if (d->newdecl[0] != d->decl) + return true; + } + return false; +} + +/* Rewrite the functions if needed, return + the TODOs requested. */ + +unsigned +ipa_struct_reorg::rewrite_functions (void) +{ + unsigned retval = 0; + + restore_field_type (); + /* Create new types, if we did not create any new types, + then don't rewrite any accesses. */ + if (!create_new_types ()) + return 0; + + if (functions.length ()) + { + retval = TODO_remove_functions; + create_new_functions (); + } + + create_new_decls (); + + for (unsigned i = 0; i < functions.length (); i++) + { + srfunction *f = functions[i]; + if (f->newnode) + continue; + + /* Function uses no rewriten types so don't cause a rewrite. */ + if (!has_rewritten_type (f)) + continue; + + cgraph_node *node = f->node; + basic_block bb; + + push_cfun (DECL_STRUCT_FUNCTION (node->decl)); + current_function = f; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nBefore rewrite:\n"); + dump_function_to_file (current_function_decl, dump_file, + dump_flags | TDF_VOPS); + } + FOR_EACH_BB_FN (bb, cfun) + { + for (gphi_iterator si = gsi_start_phis (bb); !gsi_end_p (si);) + { + if (rewrite_phi (si.phi ())) + si = gsi_start_phis (bb); + else + gsi_next (&si); + } + + for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si);) + { + gimple *stmt = gsi_stmt (si); + if (rewrite_stmt (stmt, &si)) + gsi_remove (&si, true); + else + gsi_next (&si); + } + } + + /* Debug statements need to happen after all other statements + have changed. */ + FOR_EACH_BB_FN (bb, cfun) + { + for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si);) + { + gimple *stmt = gsi_stmt (si); + if (gimple_code (stmt) == GIMPLE_DEBUG + && rewrite_debug (stmt, &si)) + gsi_remove (&si, true); + else + gsi_next (&si); + } + } + + /* Release the old SSA_NAMES for old arguments. */ + if (f->old) + { + for (unsigned i = 0; i < f->args.length (); i++) + { + srdecl *d = f->args[i]; + if (d->newdecl[0] != d->decl) + { + tree ssa_name = ssa_default_def (cfun, d->decl); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Found "); + print_generic_expr (dump_file, ssa_name); + fprintf (dump_file, " to be released.\n"); + } + release_ssa_name (ssa_name); + } + } + } + + update_ssa (TODO_update_ssa_only_virtuals); + + if (flag_tree_pta) + compute_may_aliases (); + + remove_unused_locals (); + + cgraph_edge::rebuild_edges (); + + free_dominance_info (CDI_DOMINATORS); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nAfter rewrite:\n"); + dump_function_to_file (current_function_decl, dump_file, + dump_flags | TDF_VOPS); + } + + pop_cfun (); + current_function = NULL; + } + + return retval | TODO_verify_all; +} + +unsigned int +ipa_struct_reorg::execute (void) +{ + /* FIXME: If there is a top-level inline-asm, + the pass immediately returns. */ + if (symtab->first_asm_symbol ()) + return 0; + record_accesses (); + prune_escaped_types (); + analyze_types (); + + return rewrite_functions (); +} + +const pass_data pass_data_ipa_struct_reorg = +{ + SIMPLE_IPA_PASS, // type + "struct_reorg", // name + OPTGROUP_NONE, // optinfo_flags + TV_IPA_STRUCT_REORG, // tv_id + 0, // properties_required + 0, // properties_provided + 0, // properties_destroyed + 0, // todo_flags_start + 0, // todo_flags_finish +}; + +class pass_ipa_struct_reorg : public simple_ipa_opt_pass +{ +public: + pass_ipa_struct_reorg (gcc::context *ctxt) + : simple_ipa_opt_pass (pass_data_ipa_struct_reorg, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *); + virtual unsigned int execute (function *) + { + return ipa_struct_reorg ().execute (); + } + +}; // class pass_ipa_struct_reorg + +bool +pass_ipa_struct_reorg::gate (function *) +{ + return (optimize + && flag_ipa_struct_reorg + /* Don't bother doing anything if the program has errors. */ + && !seen_error ()); +} + +} // anon namespace + + +simple_ipa_opt_pass * +make_pass_ipa_struct_reorg (gcc::context *ctxt) +{ + return new pass_ipa_struct_reorg (ctxt); +} \ No newline at end of file diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.h b/gcc/ipa-struct-reorg/ipa-struct-reorg.h new file mode 100644 index 000000000..a58794070 --- /dev/null +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.h @@ -0,0 +1,235 @@ +/* Struct-reorg optimizations. + Copyright (C) 2016-2023 Free Software Foundation, Inc. + Contributed by Andrew Pinski <apinski@cavium.com> + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#ifndef IPA_STRUCT_REORG_H +#define IPA_STRUCT_REORG_H + +namespace struct_reorg { + +const int max_split = 2; + +template <typename type> +struct auto_vec_del : auto_vec<type *> +{ + ~auto_vec_del (); +}; + +template <typename T> +auto_vec_del<T>::~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<srdecl *> args; + auto_vec<srdecl *> globals; + auto_vec_del<srdecl> decls; + srdecl *record_decl (srtype *, tree, int arg); + + srfunction *old; + cgraph_node *newnode; + srfunction *newf; + + // Constructors + srfunction (cgraph_node *n); + + // Methods + void add_arg (srdecl *arg); + void dump (FILE *file); + void simple_dump (FILE *file); + + bool check_args (void); + void create_new_decls (void); + srdecl *find_decl (tree); +}; + +struct srglobal : private srfunction +{ + srglobal () + : srfunction (NULL) + {} + + using srfunction::dump; + using srfunction::create_new_decls; + using srfunction::find_decl; + using srfunction::record_decl; + using srfunction::decls; +}; + +struct srtype +{ + tree type; + auto_vec_del<srfield> fields; + + // array of fields that use this type. + auto_vec<srfield *> field_sites; + + // array of functions which use directly the type + auto_vec<srfunction *> functions; + + auto_vec_del<sraccess> accesses; + bool chain_type; + +private: + escape_type escapes; + +public: + tree newtype[max_split]; + bool visited; + + // Constructors + srtype (tree type); + + // Methods + void dump (FILE *file); + void simple_dump (FILE *file); + void add_function (srfunction *); + void add_access (sraccess *a) + { + accesses.safe_push (a); + } + void add_field_site (srfield *); + + srfield *find_field (unsigned HOST_WIDE_INT offset); + + bool create_new_type (void); + void analyze (void); + void mark_escape (escape_type, gimple *stmt); + bool has_escaped (void) + { + return escapes != does_not_escape; + } + const char *escape_reason (void) + { + if (!has_escaped ()) + return NULL; + return escape_type_string[escapes - 1]; + } + bool escaped_rescusive (void) + { + return escapes == escape_rescusive_type; + } + bool has_new_type (void) + { + return newtype[0] && newtype[0] != type; + } +}; + +struct srfield +{ + unsigned HOST_WIDE_INT offset; + tree fieldtype; + tree fielddecl; + srtype *base; + srtype *type; + + unsigned clusternum; + + tree newfield[max_split]; + + // Constructors + srfield (tree field, srtype *base); + + // Methods + void dump (FILE *file); + void simple_dump (FILE *file); + + void create_new_fields (tree newtype[max_split], + tree newfields[max_split], + tree newlast[max_split]); +}; + +struct sraccess +{ + gimple *stmt; + cgraph_node *node; + + srtype *type; + // NULL field means the whole type is accessed + srfield *field; + + // Constructors + sraccess (gimple *s, cgraph_node *n, srtype *t, srfield *f = NULL) + : stmt (s), + node (n), + type (t), + field (f) + {} + + // Methods + void dump (FILE *file); +}; + +struct srdecl +{ + srtype *type; + tree decl; + tree func; + /* -1 : not an argument + -2 : static chain + */ + int argumentnum; + + bool visited; + + tree newdecl[max_split]; + + // Constructors + srdecl (srtype *type, tree decl, int argumentnum = -1); + + // Methods + void dump (FILE *file); + bool has_new_decl (void) + { + return newdecl[0] && newdecl[0] != decl; + } +}; + + +} // namespace struct_reorg + +#endif diff --git a/gcc/params.opt b/gcc/params.opt index e0ff9e210..1ddf1343f 100644 --- a/gcc/params.opt +++ b/gcc/params.opt @@ -865,6 +865,10 @@ Enum(parloops_schedule_type) String(runtime) Value(PARLOOPS_SCHEDULE_RUNTIME) Common Joined UInteger Var(param_partial_inlining_entry_probability) Init(70) Optimization IntegerRange(0, 100) Param Maximum probability of the entry BB of split region (in percent relative to entry BB of the function) to make partial inlining happen. +-param=struct-reorg-cold-struct-ratio= +Common Joined UInteger Var(param_struct_reorg_cold_struct_ratio) Init(10) IntegerRange(0, 100) Param Optimization +The threshold ratio between current and hottest structure counts. + -param=predictable-branch-outcome= Common Joined UInteger Var(param_predictable_branch_outcome) Init(2) IntegerRange(0, 50) Param Optimization Maximal estimated outcome of branch considered predictable. diff --git a/gcc/passes.def b/gcc/passes.def index 375d3d62d..1c1658c4a 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -177,6 +177,8 @@ along with GCC; see the file COPYING3. If not see compiled unit. */ INSERT_PASSES_AFTER (all_late_ipa_passes) NEXT_PASS (pass_ipa_pta); + /* FIXME: this should be a normal IP pass. */ + NEXT_PASS (pass_ipa_struct_reorg); NEXT_PASS (pass_omp_simd_clone); TERMINATE_PASS_LIST (all_late_ipa_passes) diff --git a/gcc/testsuite/gcc.dg/struct/struct-reorg.exp b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp new file mode 100644 index 000000000..43913104e --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp @@ -0,0 +1,35 @@ +# Copyright (C) 1997-2023 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +load_lib gcc-dg.exp +load_lib torture-options.exp + +# Initialize `dg'. +dg-init +torture-init + +set STRUCT_REORG_TORTURE_OPTIONS [list \ + { -O3 } \ + { -Ofast } ] + +set-torture-options $STRUCT_REORG_TORTURE_OPTIONS {{}} + +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.c]] \ + "" "-fipa-struct-reorg -fdump-ipa-all -flto-partition=one -fwhole-program" + +# All done. +torture-finish +dg-finish diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-1.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-1.c new file mode 100644 index 000000000..6565fe8dd --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-1.c @@ -0,0 +1,24 @@ +// { dg-do compile } +// { dg-options "-O3 -flto-partition=one -fipa-struct-reorg -fdump-ipa-all" } + +struct a +{ + int t, t1; +}; + +static struct a *b; + +void *xmalloc(int); + + +void f(void) +{ + b = xmalloc (sizeof(*b)); +} + +int g(void) +{ + return b->t; +} + +/* { dg-final { scan-ipa-dump "No structures to transform." "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-2.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-2.c new file mode 100644 index 000000000..44babd35b --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-2.c @@ -0,0 +1,29 @@ +// { dg-do run } + +#include <assert.h> + +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 000000000..5864ad46f --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-3.c @@ -0,0 +1,23 @@ +// { dg-do compile } +// { dg-options "-O3 -flto-partition=one -fipa-struct-reorg -fdump-ipa-all" } + +#include <stdlib.h> +typedef struct { + long laststart_offset; + unsigned regnum; +} compile_stack_elt_t; +typedef struct { + compile_stack_elt_t *stack; + unsigned size; +} compile_stack_type; +void f (const char *p, const char *pend, int c) +{ + compile_stack_type compile_stack; + while (p != pend) + if (c) + compile_stack.stack = realloc (compile_stack.stack, + (compile_stack.size << 1) + * sizeof (compile_stack_elt_t)); +} + +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-4.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-4.c new file mode 100644 index 000000000..e5a8a6c84 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-4.c @@ -0,0 +1,59 @@ +/* { dg-do run } */ + +extern void abort (void); + +struct S +{ + int b; + int *c; +}; +static int d, e; + +static struct S s; + +static int * +__attribute__((noinline, const)) +foo (void) +{ + return &s.b; +} + +int * +__attribute__((noinline)) +bar (int **f) +{ + s.c = &d; + *f = &e; + /* As nothing ever takes the address of any int * field in struct S, + the write to *f can't alias with the s.c field. */ + return s.c; +} + +int +__attribute__((noinline)) +baz (int *x) +{ + s.b = 1; + *x = 4; + /* Function foo takes address of an int field in struct S, + so *x can alias with the s.b field (and it does in this testcase). */ + return s.b; +} + +int +__attribute__((noinline)) +t (void) +{ + int *f = (int *) 0; + return 10 * (bar (&f) != &d) + baz (foo ()); +} + +int +main (void) +{ + if (t () != 4) + abort (); + return 0; +} + +/* { dg-final { scan-ipa-dump "No structures to transform." "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/w_prof_global_array.c b/gcc/testsuite/gcc.dg/struct/w_prof_global_array.c new file mode 100644 index 000000000..733413a94 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/w_prof_global_array.c @@ -0,0 +1,29 @@ +#include <stdlib.h> +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 000000000..0ef686e74 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/w_prof_global_var.c @@ -0,0 +1,42 @@ +#include <stdlib.h> +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 000000000..23a53be53 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/w_prof_local_array.c @@ -0,0 +1,37 @@ +#include <stdlib.h> +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 000000000..0cbb172f2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/w_prof_local_var.c @@ -0,0 +1,40 @@ +#include <stdlib.h> +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 000000000..f900b1349 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/w_prof_single_str_global.c @@ -0,0 +1,31 @@ +#include <stdlib.h> +typedef struct +{ + int a; + int b; +}str_t; + +#define N 3 + +str_t str; + +int +main () +{ + int i; + int res = 1<<(1<<N); + str.a = 2; + + for (i = 0; i < N; i++) + str.a = str.a * str.a; + + if (str.a != res) + abort (); + + /* POSIX ignores all but the 8 low-order bits, but other + environments may not. */ + return (str.a & 255); +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/w_prof_two_strs.c b/gcc/testsuite/gcc.dg/struct/w_prof_two_strs.c new file mode 100644 index 000000000..13b4cdc70 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/w_prof_two_strs.c @@ -0,0 +1,64 @@ +#include <stdlib.h> + +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 000000000..dcc545964 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/w_ratio_cold_str.c @@ -0,0 +1,43 @@ +#include <stdlib.h> +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 000000000..6d6375fc1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_array_field.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include <stdlib.h> +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 000000000..9d3213408 --- /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 <stdlib.h> +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 000000000..d79992a53 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_double_malloc.c @@ -0,0 +1,29 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include <stdlib.h> + +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 000000000..ee9b0d765 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_empty_str.c @@ -0,0 +1,44 @@ +/* { dg-do run } */ + +#include <stdlib.h> + +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 000000000..9ebb2b4cc --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_arg_to_local.c @@ -0,0 +1,44 @@ +/* { dg-do run } */ + +#include <stdlib.h> +struct str +{ + int a; + float b; +}; + +#ifdef STACK_SIZE +#if STACK_SIZE > 8000 +#define N 1000 +#else +#define N (STACK_SIZE/8) +#endif +#else +#define N 1000 +#endif + +int +foo (struct str * p_str) +{ + static int sum = 0; + + sum = sum + p_str->a; + return sum; +} + +int +main () +{ + int i, sum; + struct str * p = malloc (N * sizeof (struct str)); + if (p == NULL) + return 0; + for (i = 0; i < N; i++) + sum = foo (p+i); + + return 0; +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" } } */ + diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_escape_return-1.c b/gcc/testsuite/gcc.dg/struct/wo_prof_escape_return-1.c new file mode 100644 index 000000000..d0dce8b53 --- /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 <stdlib.h> + +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 000000000..71167182d --- /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 <stdlib.h> + +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 000000000..74fa11f39 --- /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 <stdlib.h> +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 000000000..60d2466e1 --- /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 <stdlib.h> +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 000000000..baf617816 --- /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 <stdlib.h> +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 000000000..33fce3b23 --- /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 <stdlib.h> +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 000000000..1c5a3aa15 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_global_array.c @@ -0,0 +1,32 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include <stdlib.h> +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 000000000..a0d1467fe --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_global_var.c @@ -0,0 +1,45 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include <stdlib.h> +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 000000000..6c24e1c8b --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_local_array.c @@ -0,0 +1,40 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include <stdlib.h> +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 000000000..8f2f8143f --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_local_var.c @@ -0,0 +1,43 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include <stdlib.h> +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 000000000..98bf01a6d --- /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 <stdlib.h> +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 000000000..66b0f967c --- /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 <stdlib.h> +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 000000000..d28bcfb02 --- /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 <stdlib.h> +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 000000000..37a6a43a8 --- /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 <stdlib.h> +typedef struct +{ + int a; + int b; +}str_t; + +#define N 3 + +str_t str; + +int +main () +{ + int i; + int res = 1<<(1<<N); + str.a = 2; + + for (i = 0; i < N; i++) + str.a = str.a * str.a; + + if (str.a != res) + abort (); + + /* POSIX ignores all but the 8 low-order bits, but other + environments may not. */ + return (str.a & 255); +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "Number of structures to transform is 1" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_single_str_local.c b/gcc/testsuite/gcc.dg/struct/wo_prof_single_str_local.c new file mode 100644 index 000000000..ca9a8efcf --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_single_str_local.c @@ -0,0 +1,34 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include <stdlib.h> +typedef struct +{ + int a; + int b; +}str_t; + +#define N 3 + +int +main () +{ + int i; + int res = 1<<(1<<N); + str_t str; + + str.a = 2; + + for (i = 0; i < N; i++) + str.a = str.a * str.a; + + if (str.a != res) + abort (); + + /* POSIX ignores all but the 8 low-order bits, but other + environments may not. */ + return (str.a & 255); +} + +/*--------------------------------------------------------------------------*/ +/* { dg-final { scan-ipa-dump "No structures to transform" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/wo_prof_single_str_pointer.c b/gcc/testsuite/gcc.dg/struct/wo_prof_single_str_pointer.c new file mode 100644 index 000000000..baa95bddf --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_single_str_pointer.c @@ -0,0 +1,38 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include <stdlib.h> +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 000000000..cba92e995 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/wo_prof_two_strs.c @@ -0,0 +1,67 @@ +/* { dg-do compile } */ +/* { dg-do run } */ + +#include <stdlib.h> + +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 2dae5e1c7..366118126 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 606d1d60b..ec7be874c 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); -- 2.33.0
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2