代码拉取完成,页面将自动刷新
同步操作将从 src-openEuler/gcc 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
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
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。