common.opt (fipa-sra): New switch.
authorMartin Jambor <mjambor@suse.cz>
Thu, 17 Sep 2009 11:35:38 +0000 (13:35 +0200)
committerMartin Jambor <jamborm@gcc.gnu.org>
Thu, 17 Sep 2009 11:35:38 +0000 (13:35 +0200)
2009-09-17  Martin Jambor  <mjambor@suse.cz>

* common.opt (fipa-sra): New switch.
* opts.c (decode_options): Turn flag_ipa_sra on for opt2.
* timevar.def (TV_IPA_SRA): New timevar.
* params.def (ipa-sra-ptr-growth-factor): New parameter.
* doc/invoke.texi: Document -fipa-sra and ipa-sra-ptr-growth-factor.
* tree-sra.c: Include cgraph.c.
(enum sra_mode): Added SRA_MODE_EARLY_IPA.
(struct access): Added fields stmt, grp_maybe_modified, grp_scalar_ptr
and grp_not_necessarilly_dereferenced.
(func_param_count): New variable.
(encountered_apply_args): New variable.
(bb_dereferences): New variable.
(final_bbs): New variable.
(no_accesses_representant): New variable.
(no_accesses_p): New function.
(dump_access): Dump the new fields.
(sra_initialize): Set encountered_apply_args to false.
(get_ssa_base_param): New function.
(mark_parm_dereference): New function.
(create_access): Caring for INIDRECT_REFs and different handling of
varialble length accesses in early IPA SRA.  Store the stmt - a new
parameter - to the new access.
(build_access_from_expr_1): New parameter stmt, passed to
create_access.  Handle INDIRECT_REFs.
(build_access_from_expr): Pass the current statement to
build_access_from_expr_1.
(disqualify_ops_if_throwing_stmt): Trigger only in intraprocedural
passes.
(build_accesses_from_assign): Pass the current statement to
build_access_from_expr_1.  Do not create assign links in IPA-SRA.
(scan_function): Call handle_ssa_defs on phi nodes.  Set bits in
final_bbs when necessary.  Check for calls to __builtin_apply_args.
Fixup EH info if anythng was changed.
(is_unused_scalar_param): New function.
(ptr_parm_has_direct_uses): New function.
(find_param_candidates): New function.
(mark_maybe_modified): New function.
(analyze_modified_params): New function.
(propagate_dereference_distances): New function.
(dump_dereferences_table): New function.
(analyze_caller_dereference_legality): New function.
(unmodified_by_ref_scalar_representative): New function.
(splice_param_accesses): New function.
(decide_one_param_reduction): New function.
(enum ipa_splicing_result): New type.
(splice_all_param_accesses): New function.
(get_param_index): New function.
(turn_representatives_into_adjustments): New function.
(analyze_all_param_acesses): New function.
(get_replaced_param_substitute): New function.
(get_adjustment_for_base): New function.
(replace_removed_params_ssa_names): New function.
(sra_ipa_reset_debug_stmts): New function.
(sra_ipa_modify_expr): New function.
(sra_ipa_modify_assign): New function.
(convert_callers): New function.
(modify_function): New function.
(ipa_sra_preliminary_function_checks): New function.
(ipa_early_sra): New function.
(ipa_early_sra_gate): New function.
(pass_early_ipa_sra): New variable.
* Makefile.in (tree-sra.o): Add cgraph.h to dependencies.

Testsuite:

* gcc.dg/struct/wo_prof_escape_arg_to_local.c: Do not run IPA-SRA.
* gcc.dg/ipa/ipa-sra-1.c: New test.
* gcc.dg/ipa/ipa-sra-2.c: New test.
* gcc.dg/ipa/ipa-sra-3.c: New test.
* gcc.dg/ipa/ipa-sra-4.c: New test.
* gcc.dg/ipa/ipa-sra-5.c: New test.
* gcc.c-torture/execute/ipa-sra-1.c: New test.
* gcc.c-torture/execute/ipa-sra-2.c: New test.

From-SVN: r151800

19 files changed:
gcc/ChangeLog
gcc/Makefile.in
gcc/common.opt
gcc/doc/invoke.texi
gcc/opts.c
gcc/params.def
gcc/passes.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.c-torture/execute/ipa-sra-1.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/ipa-sra-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/ipa/ipa-sra-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/ipa/ipa-sra-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/ipa/ipa-sra-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/ipa/ipa-sra-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/ipa/ipa-sra-5.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/struct/wo_prof_escape_arg_to_local.c
gcc/timevar.def
gcc/tree-pass.h
gcc/tree-sra.c

index 7a31855336b2bf1a4f18a4e9e0e31561fd9037c4..eaf3bafa48638161925a3eb04dcb76bf004e0327 100644 (file)
@@ -1,3 +1,68 @@
+2009-09-17  Martin Jambor  <mjambor@suse.cz>
+
+       * common.opt (fipa-sra): New switch.
+       * opts.c (decode_options): Turn flag_ipa_sra on for opt2.
+       * timevar.def (TV_IPA_SRA): New timevar.
+       * params.def (ipa-sra-ptr-growth-factor): New parameter.
+       * doc/invoke.texi: Document -fipa-sra and ipa-sra-ptr-growth-factor.
+       * tree-sra.c: Include cgraph.c.
+       (enum sra_mode): Added SRA_MODE_EARLY_IPA.
+       (struct access): Added fields stmt, grp_maybe_modified, grp_scalar_ptr
+       and grp_not_necessarilly_dereferenced.
+       (func_param_count): New variable.
+       (encountered_apply_args): New variable.
+       (bb_dereferences): New variable.
+       (final_bbs): New variable.
+       (no_accesses_representant): New variable.
+       (no_accesses_p): New function.
+       (dump_access): Dump the new fields.
+       (sra_initialize): Set encountered_apply_args to false.
+       (get_ssa_base_param): New function.
+       (mark_parm_dereference): New function.
+       (create_access): Caring for INIDRECT_REFs and different handling of
+       varialble length accesses in early IPA SRA.  Store the stmt - a new
+       parameter - to the new access.
+       (build_access_from_expr_1): New parameter stmt, passed to
+       create_access.  Handle INDIRECT_REFs.
+       (build_access_from_expr): Pass the current statement to
+       build_access_from_expr_1.
+       (disqualify_ops_if_throwing_stmt): Trigger only in intraprocedural
+       passes.
+       (build_accesses_from_assign): Pass the current statement to
+       build_access_from_expr_1.  Do not create assign links in IPA-SRA.
+       (scan_function): Call handle_ssa_defs on phi nodes.  Set bits in
+       final_bbs when necessary.  Check for calls to __builtin_apply_args.
+       Fixup EH info if anythng was changed.
+       (is_unused_scalar_param): New function.
+       (ptr_parm_has_direct_uses): New function.
+       (find_param_candidates): New function.
+       (mark_maybe_modified): New function.
+       (analyze_modified_params): New function.
+       (propagate_dereference_distances): New function.
+       (dump_dereferences_table): New function.
+       (analyze_caller_dereference_legality): New function.
+       (unmodified_by_ref_scalar_representative): New function.
+       (splice_param_accesses): New function.
+       (decide_one_param_reduction): New function.
+       (enum ipa_splicing_result): New type.
+       (splice_all_param_accesses): New function.
+       (get_param_index): New function.
+       (turn_representatives_into_adjustments): New function.
+       (analyze_all_param_acesses): New function.
+       (get_replaced_param_substitute): New function.
+       (get_adjustment_for_base): New function.
+       (replace_removed_params_ssa_names): New function.
+       (sra_ipa_reset_debug_stmts): New function.
+       (sra_ipa_modify_expr): New function.
+       (sra_ipa_modify_assign): New function.
+       (convert_callers): New function.
+       (modify_function): New function.
+       (ipa_sra_preliminary_function_checks): New function.
+       (ipa_early_sra): New function.
+       (ipa_early_sra_gate): New function.
+       (pass_early_ipa_sra): New variable.
+       * Makefile.in (tree-sra.o): Add cgraph.h to dependencies.
+       
 2009-09-17  Michael Matz  <matz@suse.de>
 
        PR middle-end/41347
index 2e406b6c63af2b772ded7ee64316b1bbf27c467d..7c0a2355482738d298251a45dffbc6198c9b7bff 100644 (file)
@@ -2891,8 +2891,9 @@ tree-ssa-ccp.o : tree-ssa-ccp.c $(TREE_FLOW_H) $(CONFIG_H) \
    $(TREE_DUMP_H) $(BASIC_BLOCK_H) $(TREE_PASS_H) langhooks.h \
    tree-ssa-propagate.h value-prof.h $(FLAGS_H) $(TARGET_H) $(TOPLEV_H)
 tree-sra.o : tree-sra.c $(CONFIG_H) $(SYSTEM_H) coretypes.h alloc-pool.h \
-   $(TM_H) $(TREE_H) $(GIMPLE_H) $(TREE_FLOW_H) $(IPA_PROP_H) $(DIAGNOSTIC_H) \
-   statistics.h $(TREE_DUMP_H) $(TIMEVAR_H) $(PARAMS_H) $(TARGET_H) $(FLAGS_H)
+   $(TM_H) $(TREE_H) $(GIMPLE_H) $(CGRAPH_H) $(TREE_FLOW_H) $(IPA_PROP_H) \
+   $(DIAGNOSTIC_H) statistics.h $(TREE_DUMP_H) $(TIMEVAR_H) $(PARAMS_H) \
+   $(TARGET_H) $(FLAGS_H)
 tree-switch-conversion.o : tree-switch-conversion.c $(CONFIG_H) $(SYSTEM_H) \
     $(TREE_H) $(TM_P_H) $(TREE_FLOW_H) $(DIAGNOSTIC_H) $(TREE_INLINE_H) \
     $(TIMEVAR_H) $(TM_H) coretypes.h $(TREE_DUMP_H) $(GIMPLE_H) \
index 5426aed11391288fe114a5eae1f927d05ae33c2f..0befadd9d254c9cdb638944df95b5b954636754a 100644 (file)
@@ -486,6 +486,10 @@ feliminate-dwarf2-dups
 Common Report Var(flag_eliminate_dwarf2_dups)
 Perform DWARF2 duplicate elimination
 
+fipa-sra
+Common Report Var(flag_ipa_sra) Init(0) Optimization
+Perform interprocedural reduction of aggregates
+
 feliminate-unused-debug-symbols
 Common Report Var(flag_debug_only_used_symbols)
 Perform unused type elimination in debug info
index 4adc11457006ec56e1a6be065af49b9a943655fe..5a96820ba14d5ae2c28cf9ef22b5088025bac6a2 100644 (file)
@@ -335,7 +335,7 @@ Objective-C and Objective-C++ Dialects}.
 -fcse-follow-jumps -fcse-skip-blocks -fcx-fortran-rules -fcx-limited-range @gol
 -fdata-sections -fdce -fdce @gol
 -fdelayed-branch -fdelete-null-pointer-checks -fdse -fdse @gol
--fearly-inlining -fexpensive-optimizations -ffast-math @gol
+-fearly-inlining -fipa-sra -fexpensive-optimizations -ffast-math @gol
 -ffinite-math-only -ffloat-store -fexcess-precision=@var{style} @gol
 -fforward-propagate -ffunction-sections @gol
 -fgcse -fgcse-after-reload -fgcse-las -fgcse-lm @gol
@@ -5659,6 +5659,7 @@ also turns on the following optimization flags:
 -fgcse  -fgcse-lm  @gol
 -finline-small-functions @gol
 -findirect-inlining @gol
+-fipa-sra @gol
 -foptimize-sibling-calls @gol
 -fpeephole2 @gol
 -fregmove @gol
@@ -5819,6 +5820,14 @@ having large chains of nested wrapper functions.
 
 Enabled by default.
 
+@item -fipa-sra
+@opindex fipa-sra
+Perform interprocedural scalar replacement of aggregates, removal of
+unused parameters and replacement of parameters passed by reference
+by parameters passed by value.
+
+Enabled at levels @option{-O2}, @option{-O3} and @option{-Os}.
+
 @item -finline-limit=@var{n}
 @opindex finline-limit
 By default, GCC limits the size of functions that can be inlined.  This flag
@@ -8127,6 +8136,12 @@ the parameter is reserved exclusively for debug insns created by
 @option{-fvar-tracking-assignments}, but debug insns may get
 (non-overlapping) uids above it if the reserved range is exhausted.
 
+@item ipa-sra-ptr-growth-factor
+IPA-SRA will replace a pointer to an aggregate with one or more new
+parameters only when their cumulative size is less or equal to
+@option{ipa-sra-ptr-growth-factor} times the size of the original
+pointer parameter.
+
 @end table
 @end table
 
index 878635f8ca331a601079a1403d09cd31b75e11a2..cf49ef8f36c57d8429af0ee94a26014c753bdbea 100644 (file)
@@ -898,6 +898,7 @@ decode_options (unsigned int argc, const char **argv)
   flag_tree_pre = opt2;
   flag_tree_switch_conversion = 1;
   flag_ipa_cp = opt2;
+  flag_ipa_sra = opt2;
 
   /* Track fields in field-sensitive alias analysis.  */
   set_param_value ("max-fields-for-field-sensitive",
index 488a0d303c71ca40b6f8f1b37678ff88d5c04ace..051398573e69aa8fe773c95a7b16c990563314ed 100644 (file)
@@ -759,6 +759,12 @@ DEFPARAM (PARAM_MIN_NONDEBUG_INSN_UID,
          "The minimum UID to be used for a nondebug insn",
          0, 1, 0)
 
+DEFPARAM (PARAM_IPA_SRA_PTR_GROWTH_FACTOR,
+         "ipa-sra-ptr-growth-factor",
+         "maximum allowed growth of size of new parameters ipa-sra replaces "
+         "a pointer to an aggregate with",
+         2, 0, 0)
+
 /*
 Local variables:
 mode:c
index fa2de0376120b72ab33909cd9fea42784922752a..abfa04b912779885f3df68bc910397778029bb42 100644 (file)
@@ -566,6 +566,7 @@ init_optimization_passes (void)
          NEXT_PASS (pass_copy_prop);
          NEXT_PASS (pass_merge_phi);
          NEXT_PASS (pass_cd_dce);
+         NEXT_PASS (pass_early_ipa_sra);
          NEXT_PASS (pass_tail_recursion);
          NEXT_PASS (pass_convert_switch);
           NEXT_PASS (pass_cleanup_eh);
index 691e328af1d72e3624a49c1fa76910d26e8da02d..5245de8199b88479b338cb2cd3d6f7dcfae162d4 100644 (file)
@@ -1,3 +1,14 @@
+2009-09-17  Martin Jambor  <mjambor@suse.cz>
+
+       * gcc.dg/struct/wo_prof_escape_arg_to_local.c: Do not run IPA-SRA.
+       * gcc.dg/ipa/ipa-sra-1.c: New test.
+       * gcc.dg/ipa/ipa-sra-2.c: New test.
+       * gcc.dg/ipa/ipa-sra-3.c: New test.
+       * gcc.dg/ipa/ipa-sra-4.c: New test.
+       * gcc.dg/ipa/ipa-sra-5.c: New test.
+       * gcc.c-torture/execute/ipa-sra-1.c: New test.
+       * gcc.c-torture/execute/ipa-sra-2.c: New test.
+
 2009-09-17  Michael Matz  <matz@suse.de>
 
        PR middle-end/41347
diff --git a/gcc/testsuite/gcc.c-torture/execute/ipa-sra-1.c b/gcc/testsuite/gcc.c-torture/execute/ipa-sra-1.c
new file mode 100644 (file)
index 0000000..777ed2a
--- /dev/null
@@ -0,0 +1,29 @@
+/* Trivially making sure IPA-SRA does not introduce segfaults where they should
+   not be.  */
+
+struct bovid
+{
+  float red;
+  int green;
+  void *blue;
+};
+
+static int
+__attribute__((noinline))
+ox (int fail, struct bovid *cow)
+{
+  int r;
+  if (fail)
+    r = cow->red;
+  else
+    r = 0;
+  return r;
+}
+
+int main (int argc, char *argv[])
+{
+  int r;
+
+  r = ox ((argc > 2000), (void *) 0);
+  return r;
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/ipa-sra-2.c b/gcc/testsuite/gcc.c-torture/execute/ipa-sra-2.c
new file mode 100644 (file)
index 0000000..9d5ae44
--- /dev/null
@@ -0,0 +1,41 @@
+struct big
+{
+  int data[1000000];
+};
+
+struct small
+{
+  int data[10];
+};
+
+union both
+{
+  struct big big;
+  struct small small;
+};
+
+extern void *malloc(__SIZE_TYPE__);
+extern void free (void *);
+
+static int
+__attribute__((noinline))
+foo (int fail, union both *agg)
+{
+  int r;
+  if (fail)
+    r = agg->big.data[999999];
+  else
+    r = agg->small.data[0];
+  return r;
+}
+
+int main (int argc, char *argv[])
+{
+  union both *agg = malloc (sizeof (struct small));
+  int r;
+
+  r = foo ((argc > 2000), agg);
+
+  free (agg);
+  return r;
+}
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-sra-1.c b/gcc/testsuite/gcc.dg/ipa/ipa-sra-1.c
new file mode 100644 (file)
index 0000000..2dea78d
--- /dev/null
@@ -0,0 +1,41 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -fdump-tree-eipa_sra-details"  } */
+
+struct bovid
+{
+  float red;
+  int green;
+  void *blue;
+};
+
+extern int printf (const char *, ...);
+extern void abort (void);
+
+static int
+__attribute__((noinline))
+ox (struct bovid cow)
+{
+  if (cow.green != 6)
+    abort ();
+
+  printf ("green: %f\nblue: %p\nblue again: %p\n", cow.green,
+         cow.blue, cow.blue);
+  return 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+  struct bovid cow;
+
+  cow.red = 7.4;
+  cow.green = 6;
+  cow.blue = &cow;
+
+  ox (cow);
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump "About to replace expr cow.green with ISRA" "eipa_sra"  } } */
+/* { dg-final { scan-tree-dump "About to replace expr cow.blue with ISRA" "eipa_sra"  } } */
+/* { dg-final { cleanup-tree-dump "eipa_sra" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-sra-2.c b/gcc/testsuite/gcc.dg/ipa/ipa-sra-2.c
new file mode 100644 (file)
index 0000000..0e38201
--- /dev/null
@@ -0,0 +1,52 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-eipa_sra-details"  } */
+
+struct bovid
+{
+  float red;
+  int green;
+  void *blue;
+};
+
+static int
+__attribute__((noinline))
+ox (struct bovid *cow)
+{
+  cow->red = cow->red + cow->green + cow->green;
+  return 0;
+}
+
+int something;
+
+static int
+__attribute__((noinline))
+ox_improved (struct bovid *calf)
+{
+  if (something > 0)
+    calf->red = calf->red + calf->green;
+  else
+    calf->red = calf->green + 87;
+  something = 77;
+  return 0;
+}
+
+
+int main (int argc, char *argv[])
+{
+  struct bovid cow;
+
+  cow.red = 7.4;
+  cow.green = 6;
+  cow.blue = &cow;
+
+  ox (&cow);
+
+  ox_improved (&cow);
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump "About to replace expr cow_.*D.->red with \\*ISRA" "eipa_sra"  } } */
+/* { dg-final { scan-tree-dump "About to replace expr cow_.*D.->green with ISRA" "eipa_sra"  } } */
+/* { dg-final { scan-tree-dump "About to replace expr calf_.*D.->red with \\*ISRA" "eipa_sra"  } } */
+/* { dg-final { scan-tree-dump "About to replace expr calf_.*D.->green with ISRA" "eipa_sra"  } } */
+/* { dg-final { cleanup-tree-dump "eipa_sra" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-sra-3.c b/gcc/testsuite/gcc.dg/ipa/ipa-sra-3.c
new file mode 100644 (file)
index 0000000..f4e91bb
--- /dev/null
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-eipa_sra-details" } */
+
+struct bovid
+{
+  float red;
+  int green;
+  void *blue;
+};
+
+extern void foo (float, void *, void *, long);
+
+static int
+__attribute__((noinline))
+ox (struct bovid cow, int z, struct bovid calf, long l)
+{
+  foo (cow.red, cow.blue, cow.blue, l);
+  return 0;
+}
+
+void caller (void)
+{
+  struct bovid cow, calf;
+
+  cow.red = 7.4;
+  cow.green = 6;
+  cow.blue = &cow;
+
+  calf.red = 8.4;
+  calf.green = 5;
+  calf.blue = &cow;
+
+  ox (cow,4,calf,2);
+  return;
+}
+
+/* { dg-final { scan-tree-dump "base: z, remove_param" "eipa_sra"  } } */
+/* { dg-final { scan-tree-dump "base: calf, remove_param" "eipa_sra"  } } */
+/* { dg-final { cleanup-tree-dump "eipa_sra" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-sra-4.c b/gcc/testsuite/gcc.dg/ipa/ipa-sra-4.c
new file mode 100644 (file)
index 0000000..a832329
--- /dev/null
@@ -0,0 +1,68 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-eipa_sra-details" } */
+
+static int
+__attribute__((noinline))
+ox (int *i)
+{
+  return *i+4**i;
+}
+
+int *holder;
+
+static int
+__attribute__((noinline))
+ox_ctrl_1 (int *j)
+{
+  holder = j;
+  return *j+4 * *j+1;
+}
+
+static void
+__attribute__((noinline))
+ox_ctrl_2 (int *k)
+{
+  *k = 8;
+}
+
+static int zzz[10];
+
+static int
+__attribute__((noinline))
+ox_improved (int recurse, int *l)
+{
+  int r = 0;
+
+  r = *l;
+
+  if (recurse)
+    {
+      if (recurse > 2)
+       l = &zzz[3];
+      else
+       l = zzz;
+
+      ox_improved (0, l);
+    }
+
+  return r;
+}
+
+void caller (void)
+{
+  int a = 1;
+  int b = 10;
+  int c;
+
+  ox (&a);
+  ox_ctrl_1 (&a);
+  ox_ctrl_2 (&a);
+  *holder = ox_improved (1, &b);
+  return;
+}
+
+/* { dg-final { scan-tree-dump "About to replace expr \\*i_.*D. with ISRA" "eipa_sra"  } } */
+/* { dg-final { scan-tree-dump "About to replace expr \\*l_.*D. with ISRA" "eipa_sra"  } } */
+/* { dg-final { scan-tree-dump-times "About to replace expr \*j_.*D. with ISRA" 0 "eipa_sra"  } } */
+/* { dg-final { scan-tree-dump-times "About to replace expr \*k_.*D. with ISRA" 0 "eipa_sra"  } } */
+/* { dg-final { cleanup-tree-dump "eipa_sra" } } */
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-sra-5.c b/gcc/testsuite/gcc.dg/ipa/ipa-sra-5.c
new file mode 100644 (file)
index 0000000..317e9b5
--- /dev/null
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-eipa_sra-details" } */
+
+static int *
+__attribute__((noinline,used))
+  ox (int *i, int *j)
+{
+  return i;
+}
+
+int a;
+
+int *caller (void)
+{
+  int b = 10;
+
+  return ox (&a, &b);
+}
+/* { dg-final { scan-tree-dump-times "base: j, remove_param" 0 "eipa_sra"  } } */
+/* { dg-final { cleanup-tree-dump "eipa_sra" } } */
index 43ca58b9ef2f7ff45ec3c2882673874d3092b3e4..29630252e00acb2d6e547e1cf1f65363a5400a4c 100644 (file)
@@ -1,4 +1,4 @@
-/* { dg-options "-O3 -fno-inline -fipa-type-escape -fdump-ipa-all -fipa-struct-reorg -fwhole-program -combine" } */
+/* { dg-options "-O3 -fno-inline -fno-ipa-sra -fipa-type-escape -fdump-ipa-all -fipa-struct-reorg -fwhole-program -combine" } */
 /* { dg-do compile } */
 /* { dg-do run } */
 
index 7e3d702dd5ad142dc5a0a2c648cd00f2c957e323..92f280d4a73d3cac40e4ed7e6bdb50fb5dcb5873 100644 (file)
@@ -46,6 +46,7 @@ DEFTIMEVAR (TV_IPA_REFERENCE         , "ipa reference")
 DEFTIMEVAR (TV_IPA_PURE_CONST        , "ipa pure const")
 DEFTIMEVAR (TV_IPA_TYPE_ESCAPE       , "ipa type escape")
 DEFTIMEVAR (TV_IPA_PTA               , "ipa points-to")
+DEFTIMEVAR (TV_IPA_SRA               , "ipa SRA")
 DEFTIMEVAR (TV_IPA_FREE_LANG_DATA    , "ipa free lang data")
 /* Time spent by constructing CFG.  */
 DEFTIMEVAR (TV_CFG                   , "cfg construction")
index 9ea70e35fb7e2906ae4e02508c665fff90f16266..97f1d340a1577aa566bcce6202e5165c4e3d791e 100644 (file)
@@ -327,6 +327,7 @@ extern struct gimple_opt_pass pass_cleanup_eh;
 extern struct gimple_opt_pass pass_fixup_cfg;
 extern struct gimple_opt_pass pass_sra;
 extern struct gimple_opt_pass pass_sra_early;
+extern struct gimple_opt_pass pass_early_ipa_sra;
 extern struct gimple_opt_pass pass_tail_recursion;
 extern struct gimple_opt_pass pass_tail_calls;
 extern struct gimple_opt_pass pass_tree_loop;
index 00686c8a2190a6731276f883913be1150f3fefbc..92dab57a2f8582b3c61e23cdd60c74b0bd2dbe89 100644 (file)
@@ -78,6 +78,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tm.h"
 #include "tree.h"
 #include "gimple.h"
+#include "cgraph.h"
 #include "tree-flow.h"
 #include "ipa-prop.h"
 #include "diagnostic.h"
@@ -89,8 +90,9 @@ along with GCC; see the file COPYING3.  If not see
 #include "flags.h"
 
 /* Enumeration of all aggregate reductions we can do.  */
-enum sra_mode { SRA_MODE_EARLY_INTRA, /* early intraprocedural SRA */
-               SRA_MODE_INTRA };            /* late intraprocedural SRA */
+enum sra_mode { SRA_MODE_EARLY_IPA,   /* early call regularization */
+               SRA_MODE_EARLY_INTRA, /* early intraprocedural SRA */
+               SRA_MODE_INTRA };     /* late intraprocedural SRA */
 
 /* Global variable describing which aggregate reduction we are performing at
    the moment.  */
@@ -128,6 +130,9 @@ struct access
   /* Type.  */
   tree type;
 
+  /* The statement this access belongs to.  */
+  gimple stmt;
+
   /* Next group representative for this aggregate. */
   struct access *next_grp;
 
@@ -159,26 +164,33 @@ struct access
 
   /* Is this access currently in the work queue?  */
   unsigned grp_queued : 1;
+
   /* Does this group contain a write access?  This flag is propagated down the
      access tree.  */
   unsigned grp_write : 1;
+
   /* Does this group contain a read access?  This flag is propagated down the
      access tree.  */
   unsigned grp_read : 1;
+
   /* Other passes of the analysis use this bit to make function
      analyze_access_subtree create scalar replacements for this group if
      possible.  */
   unsigned grp_hint : 1;
+
   /* Is the subtree rooted in this access fully covered by scalar
      replacements?  */
   unsigned grp_covered : 1;
+
   /* If set to true, this access and all below it in an access tree must not be
      scalarized.  */
   unsigned grp_unscalarizable_region : 1;
+
   /* Whether data have been written to parts of the aggregate covered by this
      access which is not to be scalarized.  This flag is propagated up in the
      access tree.  */
   unsigned grp_unscalarized_data : 1;
+
   /* Does this access and/or group contain a write access through a
      BIT_FIELD_REF?  */
   unsigned grp_partial_lhs : 1;
@@ -187,6 +199,19 @@ struct access
      the decision and creation at different places because create_tmp_var
      cannot be called from within FOR_EACH_REFERENCED_VAR. */
   unsigned grp_to_be_replaced : 1;
+
+  /* Is it possible that the group refers to data which might be (directly or
+     otherwise) modified?  */
+  unsigned grp_maybe_modified : 1;
+
+  /* Set when this is a representative of a pointer to scalar (i.e. by
+     reference) parameter which we consider for turning into a plain scalar
+     (i.e. a by value parameter).  */
+  unsigned grp_scalar_ptr : 1;
+
+  /* Set when we discover that this pointer is not safe to dereference in the
+     caller.  */
+  unsigned grp_not_necessarilly_dereferenced : 1;
 };
 
 typedef struct access *access_p;
@@ -212,8 +237,9 @@ static alloc_pool link_pool;
 /* Base (tree) -> Vector (VEC(access_p,heap) *) map.  */
 static struct pointer_map_t *base_access_vec;
 
-/* Bitmap of bases (candidates).  */
+/* Bitmap of candidates.  */
 static bitmap candidate_bitmap;
+
 /* Obstack for creation of fancy names.  */
 static struct obstack name_obstack;
 
@@ -221,12 +247,42 @@ static struct obstack name_obstack;
    propagated to their assignment counterparts. */
 static struct access *work_queue_head;
 
+/* Number of parameters of the analyzed function when doing early ipa SRA.  */
+static int func_param_count;
+
+/* scan_function sets the following to true if it encounters a call to
+   __builtin_apply_args.  */
+static bool encountered_apply_args;
+
+/* This is a table in which for each basic block and parameter there is a
+   distance (offset + size) in that parameter which is dereferenced and
+   accessed in that BB.  */
+static HOST_WIDE_INT *bb_dereferences;
+/* Bitmap of BBs that can cause the function to "stop" progressing by
+   returning, throwing externally, looping infinitely or calling a function
+   which might abort etc.. */
+static bitmap final_bbs;
+
+/* Representative of no accesses at all. */
+static struct access  no_accesses_representant;
+
+/* Predicate to test the special value.  */
+
+static inline bool
+no_accesses_p (struct access *access)
+{
+  return access == &no_accesses_representant;
+}
+
 /* Dump contents of ACCESS to file F in a human friendly way.  If GRP is true,
    representative fields are dumped, otherwise those which only describe the
    individual access are.  */
 
 static struct
 {
+  /* Number of processed aggregates is readily available in
+     analyze_all_variable_accesses and so is not stored here.  */
+
   /* Number of created scalar replacements.  */
   int replacements;
 
@@ -248,8 +304,19 @@ static struct
      references.  */
   int separate_lhs_rhs_handling;
 
-  /* Number of processed aggregates is readily available in
-     analyze_all_variable_accesses and so is not stored here.  */
+  /* Number of parameters that were removed because they were unused.  */
+  int deleted_unused_parameters;
+
+  /* Number of scalars passed as parameters by reference that have been
+     converted to be passed by value.  */
+  int scalar_by_ref_to_by_val;
+
+  /* Number of aggregate parameters that were replaced by one or more of their
+     components.  */
+  int aggregate_params_reduced;
+
+  /* Numbber of components created when splitting aggregate parameters.  */
+  int param_reductions_created;
 } sra_stats;
 
 static void
@@ -268,11 +335,13 @@ dump_access (FILE *f, struct access *access, bool grp)
     fprintf (f, ", grp_write = %d, grp_read = %d, grp_hint = %d, "
             "grp_covered = %d, grp_unscalarizable_region = %d, "
             "grp_unscalarized_data = %d, grp_partial_lhs = %d, "
-            "grp_to_be_replaced = %d\n",
+            "grp_to_be_replaced = %d\n grp_maybe_modified = %d, "
+            "grp_not_necessarilly_dereferenced = %d\n",
             access->grp_write, access->grp_read, access->grp_hint,
             access->grp_covered, access->grp_unscalarizable_region,
             access->grp_unscalarized_data, access->grp_partial_lhs,
-            access->grp_to_be_replaced);
+            access->grp_to_be_replaced, access->grp_maybe_modified,
+            access->grp_not_necessarilly_dereferenced);
   else
     fprintf (f, ", write = %d, grp_partial_lhs = %d\n", access->write,
             access->grp_partial_lhs);
@@ -471,6 +540,7 @@ sra_initialize (void)
   link_pool = create_alloc_pool ("SRA links", sizeof (struct assign_link), 16);
   base_access_vec = pointer_map_create ();
   memset (&sra_stats, 0, sizeof (sra_stats));
+  encountered_apply_args = false;
 }
 
 /* Hook fed to pointer_map_traverse, deallocate stored vectors.  */
@@ -560,34 +630,105 @@ type_internals_preclude_sra_p (tree type)
     }
 }
 
+/* If T is an SSA_NAME, return NULL if it is not a default def or return its
+   base variable if it is.  Return T if it is not an SSA_NAME.  */
+
+static tree
+get_ssa_base_param (tree t)
+{
+  if (TREE_CODE (t) == SSA_NAME)
+    {
+      if (SSA_NAME_IS_DEFAULT_DEF (t))
+       return SSA_NAME_VAR (t);
+      else
+       return NULL_TREE;
+    }
+  return t;
+}
+
+/* Mark a dereference of BASE of distance DIST in a basic block tht STMT
+   belongs to, unless the BB has already been marked as a potentially
+   final.  */
+
+static void
+mark_parm_dereference (tree base, HOST_WIDE_INT dist, gimple stmt)
+{
+  basic_block bb = gimple_bb (stmt);
+  int idx, parm_index = 0;
+  tree parm;
+
+  if (bitmap_bit_p (final_bbs, bb->index))
+    return;
+
+  for (parm = DECL_ARGUMENTS (current_function_decl);
+       parm && parm != base;
+       parm = TREE_CHAIN (parm))
+    parm_index++;
+
+  gcc_assert (parm_index < func_param_count);
+
+  idx = bb->index * func_param_count + parm_index;
+  if (bb_dereferences[idx] < dist)
+    bb_dereferences[idx] = dist;
+}
+
 /* Create and insert access for EXPR. Return created access, or NULL if it is
    not possible.  */
 
 static struct access *
-create_access (tree expr, bool write)
+create_access (tree expr, gimple stmt, bool write)
 {
   struct access *access;
   void **slot;
   VEC (access_p,heap) *vec;
   HOST_WIDE_INT offset, size, max_size;
   tree base = expr;
-  bool unscalarizable_region = false;
+  bool ptr, unscalarizable_region = false;
 
   base = get_ref_base_and_extent (expr, &offset, &size, &max_size);
 
+  if (sra_mode == SRA_MODE_EARLY_IPA && INDIRECT_REF_P (base))
+    {
+      base = get_ssa_base_param (TREE_OPERAND (base, 0));
+      if (!base)
+       return NULL;
+      ptr = true;
+    }
+  else
+    ptr = false;
+
   if (!DECL_P (base) || !bitmap_bit_p (candidate_bitmap, DECL_UID (base)))
     return NULL;
 
-  if (size != max_size)
+  if (sra_mode == SRA_MODE_EARLY_IPA)
     {
-      size = max_size;
-      unscalarizable_region = true;
-    }
+      if (size < 0 || size != max_size)
+       {
+         disqualify_candidate (base, "Encountered a variable sized access.");
+         return NULL;
+       }
+      if ((offset % BITS_PER_UNIT) != 0 || (size % BITS_PER_UNIT) != 0)
+       {
+         disqualify_candidate (base,
+                               "Encountered an acces not aligned to a byte.");
+         return NULL;
+       }
 
-  if (size < 0)
+      if (ptr)
+       mark_parm_dereference (base, offset + size, stmt);
+    }
+  else
     {
-      disqualify_candidate (base, "Encountered an unconstrained access.");
-      return NULL;
+      if (size != max_size)
+       {
+         size = max_size;
+         unscalarizable_region = true;
+       }
+      if (size < 0)
+       {
+         disqualify_candidate (base, "Encountered an unconstrained access.");
+         return NULL;
+       }
     }
 
   access = (struct access *) pool_alloc (access_pool);
@@ -600,6 +741,7 @@ create_access (tree expr, bool write)
   access->type = TREE_TYPE (expr);
   access->write = write;
   access->grp_unscalarizable_region = unscalarizable_region;
+  access->stmt = stmt;
 
   slot = pointer_map_contains (base_access_vec, base);
   if (slot)
@@ -625,7 +767,14 @@ disqualify_base_of_expr (tree t, const char *reason)
   while (handled_component_p (t))
     t = TREE_OPERAND (t, 0);
 
-  if (DECL_P (t))
+  if (sra_mode == SRA_MODE_EARLY_IPA)
+    {
+      if (INDIRECT_REF_P (t))
+       t = TREE_OPERAND (t, 0);
+      t = get_ssa_base_param (t);
+    }
+
+  if (t && DECL_P (t))
     disqualify_candidate (t, reason);
 }
 
@@ -634,7 +783,7 @@ disqualify_base_of_expr (tree t, const char *reason)
    created.  */
 
 static struct access *
-build_access_from_expr_1 (tree *expr_ptr, bool write)
+build_access_from_expr_1 (tree *expr_ptr, gimple stmt, bool write)
 {
   struct access *ret = NULL;
   tree expr = *expr_ptr;
@@ -666,13 +815,17 @@ build_access_from_expr_1 (tree *expr_ptr, bool write)
 
   switch (TREE_CODE (expr))
     {
+    case INDIRECT_REF:
+      if (sra_mode != SRA_MODE_EARLY_IPA)
+       return NULL;
+      /* fall through */
     case VAR_DECL:
     case PARM_DECL:
     case RESULT_DECL:
     case COMPONENT_REF:
     case ARRAY_REF:
     case ARRAY_RANGE_REF:
-      ret = create_access (expr, write);
+      ret = create_access (expr, stmt, write);
       break;
 
     default:
@@ -694,7 +847,7 @@ build_access_from_expr (tree *expr_ptr,
                        gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED, bool write,
                        void *data ATTRIBUTE_UNUSED)
 {
-  return build_access_from_expr_1 (expr_ptr, write) != NULL;
+  return build_access_from_expr_1 (expr_ptr, gsi_stmt (*gsi), write) != NULL;
 }
 
 /* Disqualify LHS and RHS for scalarization if STMT must end its basic block in
@@ -705,7 +858,8 @@ build_access_from_expr (tree *expr_ptr,
 static bool
 disqualify_ops_if_throwing_stmt (gimple stmt, tree lhs, tree rhs)
 {
-  if (stmt_can_throw_internal (stmt) || stmt_ends_bb_p (stmt))
+  if ((sra_mode == SRA_MODE_EARLY_INTRA || sra_mode == SRA_MODE_INTRA)
+      && (stmt_can_throw_internal (stmt) || stmt_ends_bb_p (stmt)))
     {
       disqualify_base_of_expr (lhs, "LHS of a throwing stmt.");
       if (rhs)
@@ -746,10 +900,11 @@ build_accesses_from_assign (gimple *stmt_ptr,
   if (disqualify_ops_if_throwing_stmt (stmt, *lhs_ptr, *rhs_ptr))
     return SRA_SA_NONE;
 
-  racc = build_access_from_expr_1 (rhs_ptr, false);
-  lacc = build_access_from_expr_1 (lhs_ptr, true);
+  racc = build_access_from_expr_1 (rhs_ptr, stmt, false);
+  lacc = build_access_from_expr_1 (lhs_ptr, stmt, true);
 
   if (lacc && racc
+      && (sra_mode == SRA_MODE_EARLY_INTRA || sra_mode == SRA_MODE_INTRA)
       && !lacc->grp_unscalarizable_region
       && !racc->grp_unscalarizable_region
       && AGGREGATE_TYPE_P (TREE_TYPE (*lhs_ptr))
@@ -792,12 +947,11 @@ asm_visit_addr (gimple stmt ATTRIBUTE_UNUSED, tree op,
    those deemed entirely unsuitable for some reason (all operands in such
    statements and expression are removed from candidate_bitmap).  SCAN_ASSIGN
    is a callback called on all assign statements, HANDLE_SSA_DEFS is a callback
-   called on assign statements and those call statements which have a lhs and
-   it is the only callback which can be NULL. ANALYSIS_STAGE is true when
-   running in the analysis stage of a pass and thus no statement is being
-   modified.  DATA is a pointer passed to all callbacks.  If any single
-   callback returns true, this function also returns true, otherwise it returns
-   false.  */
+   called on assign statements and those call statements which have a lhs, it
+   can be NULL.  ANALYSIS_STAGE is true when running in the analysis stage of a
+   pass and thus no statement is being modified.  DATA is a pointer passed to
+   all callbacks.  If any single callback returns true, this function also
+   returns true, otherwise it returns false.  */
 
 static bool
 scan_function (bool (*scan_expr) (tree *, gimple_stmt_iterator *, bool, void *),
@@ -817,6 +971,10 @@ scan_function (bool (*scan_expr) (tree *, gimple_stmt_iterator *, bool, void *),
     {
       bool bb_changed = false;
 
+      if (handle_ssa_defs)
+       for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+         ret |= handle_ssa_defs (gsi_stmt (gsi), data);
+
       gsi = gsi_start_bb (bb);
       while (!gsi_end_p (gsi))
        {
@@ -824,12 +982,16 @@ scan_function (bool (*scan_expr) (tree *, gimple_stmt_iterator *, bool, void *),
          enum scan_assign_result assign_result;
          bool any = false, deleted = false;
 
+         if (analysis_stage && final_bbs && stmt_can_throw_external (stmt))
+           bitmap_set_bit (final_bbs, bb->index);
          switch (gimple_code (stmt))
            {
            case GIMPLE_RETURN:
              t = gimple_return_retval_ptr (stmt);
              if (*t != NULL_TREE)
                any |= scan_expr (t, &gsi, false, data);
+             if (analysis_stage && final_bbs)
+               bitmap_set_bit (final_bbs, bb->index);
              break;
 
            case GIMPLE_ASSIGN:
@@ -848,6 +1010,21 @@ scan_function (bool (*scan_expr) (tree *, gimple_stmt_iterator *, bool, void *),
                  any |= scan_expr (argp, &gsi, false, data);
                }
 
+             if (analysis_stage)
+               {
+                 tree dest = gimple_call_fndecl (stmt);
+                 int flags = gimple_call_flags (stmt);
+
+                 if (dest
+                     && DECL_BUILT_IN_CLASS (dest) == BUILT_IN_NORMAL
+                     && DECL_FUNCTION_CODE (dest) == BUILT_IN_APPLY_ARGS)
+                   encountered_apply_args = true;
+
+                 if (final_bbs
+                     && (flags & (ECF_CONST | ECF_PURE)) == 0)
+                   bitmap_set_bit (final_bbs, bb->index);
+               }
+
              if (gimple_call_lhs (stmt))
                {
                  tree *lhs_ptr = gimple_call_lhs_ptr (stmt);
@@ -863,10 +1040,13 @@ scan_function (bool (*scan_expr) (tree *, gimple_stmt_iterator *, bool, void *),
              break;
 
            case GIMPLE_ASM:
-
              if (analysis_stage)
-               walk_stmt_load_store_addr_ops (stmt, NULL, NULL, NULL,
-                                              asm_visit_addr);
+               {
+                 walk_stmt_load_store_addr_ops (stmt, NULL, NULL, NULL,
+                                                asm_visit_addr);
+                 if (final_bbs)
+                   bitmap_set_bit (final_bbs, bb->index);
+               }
              for (i = 0; i < gimple_asm_ninputs (stmt); i++)
                {
                  tree *op = &TREE_VALUE (gimple_asm_input_op (stmt, i));
@@ -877,6 +1057,7 @@ scan_function (bool (*scan_expr) (tree *, gimple_stmt_iterator *, bool, void *),
                  tree *op = &TREE_VALUE (gimple_asm_output_op (stmt, i));
                  any |= scan_expr (op, &gsi, true, data);
                }
+             break;
 
            default:
              break;
@@ -885,10 +1066,10 @@ scan_function (bool (*scan_expr) (tree *, gimple_stmt_iterator *, bool, void *),
          if (any)
            {
              ret = true;
-             bb_changed = true;
 
              if (!analysis_stage)
                {
+                 bb_changed = true;
                  update_stmt (stmt);
                  maybe_clean_eh_stmt (stmt);
                }
@@ -901,7 +1082,7 @@ scan_function (bool (*scan_expr) (tree *, gimple_stmt_iterator *, bool, void *),
              ret = true;
            }
        }
-      if (!analysis_stage && bb_changed)
+      if (!analysis_stage && bb_changed && sra_mode == SRA_MODE_EARLY_IPA)
        gimple_purge_dead_eh_edges (bb);
     }
 
@@ -2372,8 +2553,7 @@ perform_intra_sra (void)
   if (!analyze_all_variable_accesses ())
     goto out;
 
-  scan_function (sra_modify_expr, sra_modify_assign, NULL,
-                false, NULL);
+  scan_function (sra_modify_expr, sra_modify_assign, NULL, false, NULL);
   initialize_parameter_reductions ();
 
   statistics_counter_event (cfun, "Scalar replacements created",
@@ -2440,7 +2620,6 @@ struct gimple_opt_pass pass_sra_early =
  }
 };
 
-
 struct gimple_opt_pass pass_sra =
 {
  {
@@ -2462,3 +2641,1188 @@ struct gimple_opt_pass pass_sra =
   | TODO_verify_ssa                    /* todo_flags_finish */
  }
 };
+
+
+/* Return true iff PARM (which must be a parm_decl) is an unused scalar
+   parameter.  */
+
+static bool
+is_unused_scalar_param (tree parm)
+{
+  tree name;
+  return (is_gimple_reg (parm)
+         && (!(name = gimple_default_def (cfun, parm))
+             || has_zero_uses (name)));
+}
+
+/* Scan immediate uses of a default definition SSA name of a parameter PARM and
+   examine whether there are any direct or otherwise infeasible ones.  If so,
+   return true, otherwise return false.  PARM must be a gimple register with a
+   non-NULL default definition.  */
+
+static bool
+ptr_parm_has_direct_uses (tree parm)
+{
+  imm_use_iterator ui;
+  gimple stmt;
+  tree name = gimple_default_def (cfun, parm);
+  bool ret = false;
+
+  FOR_EACH_IMM_USE_STMT (stmt, ui, name)
+    {
+      if (gimple_assign_single_p (stmt))
+       {
+         tree rhs = gimple_assign_rhs1 (stmt);
+         if (rhs == name)
+           ret = true;
+         else if (TREE_CODE (rhs) == ADDR_EXPR)
+           {
+             do
+               {
+                 rhs = TREE_OPERAND (rhs, 0);
+               }
+             while (handled_component_p (rhs));
+             if (INDIRECT_REF_P (rhs) && TREE_OPERAND (rhs, 0) == name)
+               ret = true;
+           }
+       }
+      else if (gimple_code (stmt) == GIMPLE_RETURN)
+       {
+         tree t = gimple_return_retval (stmt);
+         if (t == name)
+           ret = true;
+       }
+      else if (is_gimple_call (stmt))
+       {
+         unsigned i;
+         for (i = 0; i < gimple_call_num_args (stmt); i++)
+           {
+             tree arg = gimple_call_arg (stmt, i);
+             if (arg == name)
+               {
+                 ret = true;
+                 break;
+               }
+           }
+       }
+      else if (!is_gimple_debug (stmt))
+       ret = true;
+
+      if (ret)
+       BREAK_FROM_IMM_USE_STMT (ui);
+    }
+
+  return ret;
+}
+
+/* Identify candidates for reduction for IPA-SRA based on their type and mark
+   them in candidate_bitmap.  Note that these do not necessarily include
+   parameter which are unused and thus can be removed.  Return true iff any
+   such candidate has been found.  */
+
+static bool
+find_param_candidates (void)
+{
+  tree parm;
+  int count = 0;
+  bool ret = false;
+
+  for (parm = DECL_ARGUMENTS (current_function_decl);
+       parm;
+       parm = TREE_CHAIN (parm))
+    {
+      tree type;
+
+      count++;
+      if (TREE_THIS_VOLATILE (parm)
+         || TREE_ADDRESSABLE (parm))
+       continue;
+
+      if (is_unused_scalar_param (parm))
+       {
+         ret = true;
+         continue;
+       }
+
+      type = TREE_TYPE (parm);
+      if (POINTER_TYPE_P (type))
+       {
+         type = TREE_TYPE (type);
+
+         if (TREE_CODE (type) == FUNCTION_TYPE
+             || TYPE_VOLATILE (type)
+             || !is_gimple_reg (parm)
+             || ptr_parm_has_direct_uses (parm))
+           continue;
+       }
+      else if (!AGGREGATE_TYPE_P (type))
+       continue;
+
+      if (!COMPLETE_TYPE_P (type)
+         || !host_integerp (TYPE_SIZE (type), 1)
+          || tree_low_cst (TYPE_SIZE (type), 1) == 0
+         || (AGGREGATE_TYPE_P (type)
+             && type_internals_preclude_sra_p (type)))
+       continue;
+
+      bitmap_set_bit (candidate_bitmap, DECL_UID (parm));
+      ret = true;
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       {
+         fprintf (dump_file, "Candidate (%d): ", DECL_UID (parm));
+         print_generic_expr (dump_file, parm, 0);
+         fprintf (dump_file, "\n");
+       }
+    }
+
+  func_param_count = count;
+  return ret;
+}
+
+/* Callback of walk_aliased_vdefs, marks the access passed as DATA as
+   maybe_modified. */
+
+static bool
+mark_maybe_modified (ao_ref *ao ATTRIBUTE_UNUSED, tree vdef ATTRIBUTE_UNUSED,
+                    void *data)
+{
+  struct access *repr = (struct access *) data;
+
+  repr->grp_maybe_modified = 1;
+  return true;
+}
+
+/* Analyze what representatives (in linked lists accessible from
+   REPRESENTATIVES) can be modified by side effects of statements in the
+   current function.  */
+
+static void
+analyze_modified_params (VEC (access_p, heap) *representatives)
+{
+  int i;
+
+  for (i = 0; i < func_param_count; i++)
+    {
+      struct access *repr = VEC_index (access_p, representatives, i);
+      VEC (access_p, heap) *access_vec;
+      int j, access_count;
+      tree parm;
+
+      if (!repr || no_accesses_p (repr))
+       continue;
+      parm = repr->base;
+      if (!POINTER_TYPE_P (TREE_TYPE (parm))
+         || repr->grp_maybe_modified)
+       continue;
+
+      access_vec = get_base_access_vector (parm);
+      access_count = VEC_length (access_p, access_vec);
+      for (j = 0; j < access_count; j++)
+       {
+         struct access *access;
+         ao_ref ar;
+
+         /* All accesses are read ones, otherwise grp_maybe_modified would be
+            trivially set.  */
+         access = VEC_index (access_p, access_vec, j);
+         ao_ref_init (&ar, access->expr);
+         walk_aliased_vdefs (&ar, gimple_vuse (access->stmt),
+                             mark_maybe_modified, repr, NULL);
+         if (repr->grp_maybe_modified)
+           break;
+       }
+    }
+}
+
+/* Propagate distances in bb_dereferences in the opposite direction than the
+   control flow edges, in each step storing the maximum of the current value
+   and the minimum of all successors.  These steps are repeated until the table
+   stabilizes.  Note that BBs which might terminate the functions (according to
+   final_bbs bitmap) never updated in this way.  */
+
+static void
+propagate_dereference_distances (void)
+{
+  VEC (basic_block, heap) *queue;
+  basic_block bb;
+
+  queue = VEC_alloc (basic_block, heap, last_basic_block_for_function (cfun));
+  VEC_quick_push (basic_block, queue, ENTRY_BLOCK_PTR);
+  FOR_EACH_BB (bb)
+    {
+      VEC_quick_push (basic_block, queue, bb);
+      bb->aux = bb;
+    }
+
+  while (!VEC_empty (basic_block, queue))
+    {
+      edge_iterator ei;
+      edge e;
+      bool change = false;
+      int i;
+
+      bb = VEC_pop (basic_block, queue);
+      bb->aux = NULL;
+
+      if (bitmap_bit_p (final_bbs, bb->index))
+       continue;
+
+      for (i = 0; i < func_param_count; i++)
+       {
+         int idx = bb->index * func_param_count + i;
+         bool first = true;
+         HOST_WIDE_INT inh = 0;
+
+         FOR_EACH_EDGE (e, ei, bb->succs)
+         {
+           int succ_idx = e->dest->index * func_param_count + i;
+
+           if (e->src == EXIT_BLOCK_PTR)
+             continue;
+
+           if (first)
+             {
+               first = false;
+               inh = bb_dereferences [succ_idx];
+             }
+           else if (bb_dereferences [succ_idx] < inh)
+             inh = bb_dereferences [succ_idx];
+         }
+
+         if (!first && bb_dereferences[idx] < inh)
+           {
+             bb_dereferences[idx] = inh;
+             change = true;
+           }
+       }
+
+      if (change && !bitmap_bit_p (final_bbs, bb->index))
+       FOR_EACH_EDGE (e, ei, bb->preds)
+         {
+           if (e->src->aux)
+             continue;
+
+           e->src->aux = e->src;
+           VEC_quick_push (basic_block, queue, e->src);
+         }
+    }
+
+  VEC_free (basic_block, heap, queue);
+}
+
+/* Dump a dereferences TABLE with heading STR to file F.  */
+
+static void
+dump_dereferences_table (FILE *f, const char *str, HOST_WIDE_INT *table)
+{
+  basic_block bb;
+
+  fprintf (dump_file, str);
+  FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR, next_bb)
+    {
+      fprintf (f, "%4i  %i   ", bb->index, bitmap_bit_p (final_bbs, bb->index));
+      if (bb != EXIT_BLOCK_PTR)
+       {
+         int i;
+         for (i = 0; i < func_param_count; i++)
+           {
+             int idx = bb->index * func_param_count + i;
+             fprintf (f, " %4" HOST_WIDE_INT_PRINT "d", table[idx]);
+           }
+       }
+      fprintf (f, "\n");
+    }
+  fprintf (dump_file, "\n");
+}
+
+/* Determine what (parts of) parameters passed by reference that are not
+   assigned to are not certainly dereferenced in this function and thus the
+   dereferencing cannot be safely moved to the caller without potentially
+   introducing a segfault.  Mark such REPRESENTATIVES as
+   grp_not_necessarilly_dereferenced.
+
+   The dereferenced maximum "distance," i.e. the offset + size of the accessed
+   part is calculated rather than simple booleans are calculated for each
+   pointer parameter to handle cases when only a fraction of the whole
+   aggregate is allocated (see testsuite/gcc.c-torture/execute/ipa-sra-2.c for
+   an example).
+
+   The maximum dereference distances for each pointer parameter and BB are
+   already stored in bb_dereference.  This routine simply propagates these
+   values upwards by propagate_dereference_distances and then compares the
+   distances of individual parameters in the ENTRY BB to the equivalent
+   distances of each representative of a (fraction of a) parameter.  */
+
+static void
+analyze_caller_dereference_legality (VEC (access_p, heap) *representatives)
+{
+  int i;
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    dump_dereferences_table (dump_file,
+                            "Dereference table before propagation:\n",
+                            bb_dereferences);
+
+  propagate_dereference_distances ();
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    dump_dereferences_table (dump_file,
+                            "Dereference table after propagation:\n",
+                            bb_dereferences);
+
+  for (i = 0; i < func_param_count; i++)
+    {
+      struct access *repr = VEC_index (access_p, representatives, i);
+      int idx = ENTRY_BLOCK_PTR->index * func_param_count + i;
+
+      if (!repr || no_accesses_p (repr))
+       continue;
+
+      do
+       {
+         if ((repr->offset + repr->size) > bb_dereferences[idx])
+           repr->grp_not_necessarilly_dereferenced = 1;
+         repr = repr->next_grp;
+       }
+      while (repr);
+    }
+}
+
+/* Return the representative access for the parameter declaration PARM if it is
+   a scalar passed by reference which is not written to and the pointer value
+   is not used directly.  Thus, if it is legal to dereference it in the caller
+   and we can rule out modifications through aliases, such parameter should be
+   turned into one passed by value.  Return NULL otherwise.  */
+
+static struct access *
+unmodified_by_ref_scalar_representative (tree parm)
+{
+  int i, access_count;
+  struct access *access;
+  VEC (access_p, heap) *access_vec;
+
+  access_vec = get_base_access_vector (parm);
+  gcc_assert (access_vec);
+  access_count = VEC_length (access_p, access_vec);
+
+  for (i = 0; i < access_count; i++)
+    {
+      access = VEC_index (access_p, access_vec, i);
+      if (access->write)
+       return NULL;
+    }
+
+  access = VEC_index (access_p, access_vec, 0);
+  access->grp_read = 1;
+  access->grp_scalar_ptr = 1;
+  return access;
+}
+
+/* Sort collected accesses for parameter PARM, identify representatives for
+   each accessed region and link them together.  Return NULL if there are
+   different but overlapping accesses, return the special ptr value meaning
+   there are no accesses for this parameter if that is the case and return the
+   first representative otherwise.  Set *RO_GRP if there is a group of accesses
+   with only read (i.e. no write) accesses.  */
+
+static struct access *
+splice_param_accesses (tree parm, bool *ro_grp)
+{
+  int i, j, access_count, group_count;
+  int agg_size, total_size = 0;
+  struct access *access, *res, **prev_acc_ptr = &res;
+  VEC (access_p, heap) *access_vec;
+
+  access_vec = get_base_access_vector (parm);
+  if (!access_vec)
+    return &no_accesses_representant;
+  access_count = VEC_length (access_p, access_vec);
+
+  qsort (VEC_address (access_p, access_vec), access_count, sizeof (access_p),
+        compare_access_positions);
+
+  i = 0;
+  total_size = 0;
+  group_count = 0;
+  while (i < access_count)
+    {
+      bool modification;
+      access = VEC_index (access_p, access_vec, i);
+      modification = access->write;
+
+      /* Access is about to become group representative unless we find some
+        nasty overlap which would preclude us from breaking this parameter
+        apart. */
+
+      j = i + 1;
+      while (j < access_count)
+       {
+         struct access *ac2 = VEC_index (access_p, access_vec, j);
+         if (ac2->offset != access->offset)
+           {
+             /* All or nothing law for parameters. */
+             if (access->offset + access->size > ac2->offset)
+               return NULL;
+             else
+               break;
+           }
+         else if (ac2->size != access->size)
+           return NULL;
+
+         modification |= ac2->write;
+         j++;
+       }
+
+      group_count++;
+      access->grp_maybe_modified = modification;
+      if (!modification)
+       *ro_grp = true;
+      *prev_acc_ptr = access;
+      prev_acc_ptr = &access->next_grp;
+      total_size += access->size;
+      i = j;
+    }
+
+  if (POINTER_TYPE_P (TREE_TYPE (parm)))
+    agg_size = tree_low_cst (TYPE_SIZE (TREE_TYPE (TREE_TYPE (parm))), 1);
+  else
+    agg_size = tree_low_cst (TYPE_SIZE (TREE_TYPE (parm)), 1);
+  if (total_size >= agg_size)
+    return NULL;
+
+  gcc_assert (group_count > 0);
+  return res;
+}
+
+/* Decide whether parameters with representative accesses given by REPR should
+   be reduced into components.  */
+
+static int
+decide_one_param_reduction (struct access *repr)
+{
+  int total_size, cur_parm_size, agg_size, new_param_count, parm_size_limit;
+  bool by_ref;
+  tree parm;
+
+  parm = repr->base;
+  cur_parm_size = tree_low_cst (TYPE_SIZE (TREE_TYPE (parm)), 1);
+  gcc_assert (cur_parm_size > 0);
+
+  if (POINTER_TYPE_P (TREE_TYPE (parm)))
+    {
+      by_ref = true;
+      agg_size = tree_low_cst (TYPE_SIZE (TREE_TYPE (TREE_TYPE (parm))), 1);
+    }
+  else
+    {
+      by_ref = false;
+      agg_size = cur_parm_size;
+    }
+
+  if (dump_file)
+    {
+      struct access *acc;
+      fprintf (dump_file, "Evaluating PARAM group sizes for ");
+      print_generic_expr (dump_file, parm, 0);
+      fprintf (dump_file, " (UID: %u): \n", DECL_UID (parm));
+      for (acc = repr; acc; acc = acc->next_grp)
+       dump_access (dump_file, acc, true);
+    }
+
+  total_size = 0;
+  new_param_count = 0;
+
+  for (; repr; repr = repr->next_grp)
+    {
+      gcc_assert (parm == repr->base);
+      new_param_count++;
+
+      if (!by_ref || (!repr->grp_maybe_modified
+                     && !repr->grp_not_necessarilly_dereferenced))
+       total_size += repr->size;
+      else
+       total_size += cur_parm_size;
+    }
+
+  gcc_assert (new_param_count > 0);
+
+  if (optimize_function_for_size_p (cfun))
+    parm_size_limit = cur_parm_size;
+  else
+    parm_size_limit = (PARAM_VALUE (PARAM_IPA_SRA_PTR_GROWTH_FACTOR)
+                       * cur_parm_size);
+
+  if (total_size < agg_size
+      && total_size <= parm_size_limit)
+    {
+      if (dump_file)
+       fprintf (dump_file, "    ....will be split into %i components\n",
+                new_param_count);
+      return new_param_count;
+    }
+  else
+    return 0;
+}
+
+/* The order of the following enums is important, we need to do extra work for
+   UNUSED_PARAMS, BY_VAL_ACCESSES and UNMODIF_BY_REF_ACCESSES.  */
+enum ipa_splicing_result { NO_GOOD_ACCESS, UNUSED_PARAMS, BY_VAL_ACCESSES,
+                         MODIF_BY_REF_ACCESSES, UNMODIF_BY_REF_ACCESSES };
+
+/* Identify representatives of all accesses to all candidate parameters for
+   IPA-SRA.  Return result based on what representatives have been found. */
+
+static enum ipa_splicing_result
+splice_all_param_accesses (VEC (access_p, heap) **representatives)
+{
+  enum ipa_splicing_result result = NO_GOOD_ACCESS;
+  tree parm;
+  struct access *repr;
+
+  *representatives = VEC_alloc (access_p, heap, func_param_count);
+
+  for (parm = DECL_ARGUMENTS (current_function_decl);
+       parm;
+       parm = TREE_CHAIN (parm))
+    {
+      if (is_unused_scalar_param (parm))
+       {
+         VEC_quick_push (access_p, *representatives,
+                         &no_accesses_representant);
+         if (result == NO_GOOD_ACCESS)
+           result = UNUSED_PARAMS;
+       }
+      else if (POINTER_TYPE_P (TREE_TYPE (parm))
+              && is_gimple_reg_type (TREE_TYPE (TREE_TYPE (parm)))
+              && bitmap_bit_p (candidate_bitmap, DECL_UID (parm)))
+       {
+         repr = unmodified_by_ref_scalar_representative (parm);
+         VEC_quick_push (access_p, *representatives, repr);
+         if (repr)
+           result = UNMODIF_BY_REF_ACCESSES;
+       }
+      else if (bitmap_bit_p (candidate_bitmap, DECL_UID (parm)))
+       {
+         bool ro_grp = false;
+         repr = splice_param_accesses (parm, &ro_grp);
+         VEC_quick_push (access_p, *representatives, repr);
+
+         if (repr && !no_accesses_p (repr))
+           {
+             if (POINTER_TYPE_P (TREE_TYPE (parm)))
+               {
+                 if (ro_grp)
+                   result = UNMODIF_BY_REF_ACCESSES;
+                 else if (result < MODIF_BY_REF_ACCESSES)
+                   result = MODIF_BY_REF_ACCESSES;
+               }
+             else if (result < BY_VAL_ACCESSES)
+               result = BY_VAL_ACCESSES;
+           }
+         else if (no_accesses_p (repr) && (result == NO_GOOD_ACCESS))
+           result = UNUSED_PARAMS;
+       }
+      else
+       VEC_quick_push (access_p, *representatives, NULL);
+    }
+
+  if (result == NO_GOOD_ACCESS)
+    {
+      VEC_free (access_p, heap, *representatives);
+      *representatives = NULL;
+      return NO_GOOD_ACCESS;
+    }
+
+  return result;
+}
+
+/* Return the index of BASE in PARMS.  Abort if it is not found.  */
+
+static inline int
+get_param_index (tree base, VEC(tree, heap) *parms)
+{
+  int i, len;
+
+  len = VEC_length (tree, parms);
+  for (i = 0; i < len; i++)
+    if (VEC_index (tree, parms, i) == base)
+      return i;
+  gcc_unreachable ();
+}
+
+/* Convert the decisions made at the representative level into compact
+   parameter adjustments.  REPRESENTATIVES are pointers to first
+   representatives of each param accesses, ADJUSTMENTS_COUNT is the expected
+   final number of adjustments.  */
+
+static ipa_parm_adjustment_vec
+turn_representatives_into_adjustments (VEC (access_p, heap) *representatives,
+                                      int adjustments_count)
+{
+  VEC (tree, heap) *parms;
+  ipa_parm_adjustment_vec adjustments;
+  tree parm;
+  int i;
+
+  gcc_assert (adjustments_count > 0);
+  parms = ipa_get_vector_of_formal_parms (current_function_decl);
+  adjustments = VEC_alloc (ipa_parm_adjustment_t, heap, adjustments_count);
+  parm = DECL_ARGUMENTS (current_function_decl);
+  for (i = 0; i < func_param_count; i++, parm = TREE_CHAIN (parm))
+    {
+      struct access *repr = VEC_index (access_p, representatives, i);
+
+      if (!repr || no_accesses_p (repr))
+       {
+         struct ipa_parm_adjustment *adj;
+
+         adj = VEC_quick_push (ipa_parm_adjustment_t, adjustments, NULL);
+         memset (adj, 0, sizeof (*adj));
+         adj->base_index = get_param_index (parm, parms);
+         adj->base = parm;
+         if (!repr)
+           adj->copy_param = 1;
+         else
+           adj->remove_param = 1;
+       }
+      else
+       {
+         struct ipa_parm_adjustment *adj;
+         int index = get_param_index (parm, parms);
+
+         for (; repr; repr = repr->next_grp)
+           {
+             adj = VEC_quick_push (ipa_parm_adjustment_t, adjustments, NULL);
+             memset (adj, 0, sizeof (*adj));
+             gcc_assert (repr->base == parm);
+             adj->base_index = index;
+             adj->base = repr->base;
+             adj->type = repr->type;
+             adj->offset = repr->offset;
+             adj->by_ref = (POINTER_TYPE_P (TREE_TYPE (repr->base))
+                            && (repr->grp_maybe_modified
+                                || repr->grp_not_necessarilly_dereferenced));
+
+           }
+       }
+    }
+  VEC_free (tree, heap, parms);
+  return adjustments;
+}
+
+/* Analyze the collected accesses and produce a plan what to do with the
+   parameters in the form of adjustments, NULL meaning nothing.  */
+
+static ipa_parm_adjustment_vec
+analyze_all_param_acesses (void)
+{
+  enum ipa_splicing_result repr_state;
+  bool proceed = false;
+  int i, adjustments_count = 0;
+  VEC (access_p, heap) *representatives;
+  ipa_parm_adjustment_vec adjustments;
+
+  repr_state = splice_all_param_accesses (&representatives);
+  if (repr_state == NO_GOOD_ACCESS)
+    return NULL;
+
+  /* If there are any parameters passed by reference which are not modified
+     directly, we need to check whether they can be modified indirectly.  */
+  if (repr_state == UNMODIF_BY_REF_ACCESSES)
+    {
+      analyze_caller_dereference_legality (representatives);
+      analyze_modified_params (representatives);
+    }
+
+  for (i = 0; i < func_param_count; i++)
+    {
+      struct access *repr = VEC_index (access_p, representatives, i);
+
+      if (repr && !no_accesses_p (repr))
+       {
+         if (repr->grp_scalar_ptr)
+           {
+             adjustments_count++;
+             if (repr->grp_not_necessarilly_dereferenced
+                 || repr->grp_maybe_modified)
+               VEC_replace (access_p, representatives, i, NULL);
+             else
+               {
+                 proceed = true;
+                 sra_stats.scalar_by_ref_to_by_val++;
+               }
+           }
+         else
+           {
+             int new_components = decide_one_param_reduction (repr);
+
+             if (new_components == 0)
+               {
+                 VEC_replace (access_p, representatives, i, NULL);
+                 adjustments_count++;
+               }
+             else
+               {
+                 adjustments_count += new_components;
+                 sra_stats.aggregate_params_reduced++;
+                 sra_stats.param_reductions_created += new_components;
+                 proceed = true;
+               }
+           }
+       }
+      else
+       {
+         if (no_accesses_p (repr))
+           {
+             proceed = true;
+             sra_stats.deleted_unused_parameters++;
+           }
+         adjustments_count++;
+       }
+    }
+
+  if (!proceed && dump_file)
+    fprintf (dump_file, "NOT proceeding to change params.\n");
+
+  if (proceed)
+    adjustments = turn_representatives_into_adjustments (representatives,
+                                                        adjustments_count);
+  else
+    adjustments = NULL;
+
+  VEC_free (access_p, heap, representatives);
+  return adjustments;
+}
+
+/* If a parameter replacement identified by ADJ does not yet exist in the form
+   of declaration, create it and record it, otherwise return the previously
+   created one.  */
+
+static tree
+get_replaced_param_substitute (struct ipa_parm_adjustment *adj)
+{
+  tree repl;
+  if (!adj->new_ssa_base)
+    {
+      char *pretty_name = make_fancy_name (adj->base);
+
+      repl = make_rename_temp (TREE_TYPE (adj->base), "ISR");
+      DECL_NAME (repl) = get_identifier (pretty_name);
+      obstack_free (&name_obstack, pretty_name);
+
+      get_var_ann (repl);
+      add_referenced_var (repl);
+      adj->new_ssa_base = repl;
+    }
+  else
+    repl = adj->new_ssa_base;
+  return repl;
+}
+
+/* Find the first adjustment for a particular parameter BASE in a vector of
+   ADJUSTMENTS which is not a copy_param.  Return NULL if there is no such
+   adjustment. */
+
+static struct ipa_parm_adjustment *
+get_adjustment_for_base (ipa_parm_adjustment_vec adjustments, tree base)
+{
+  int i, len;
+
+  len = VEC_length (ipa_parm_adjustment_t, adjustments);
+  for (i = 0; i < len; i++)
+    {
+      struct ipa_parm_adjustment *adj;
+
+      adj = VEC_index (ipa_parm_adjustment_t, adjustments, i);
+      if (!adj->copy_param && adj->base == base)
+       return adj;
+    }
+
+  return NULL;
+}
+
+/* Callback for scan_function.  If the statement STMT defines an SSA_NAME of a
+   parameter which is to be removed because its value is not used, replace the
+   SSA_NAME with a one relating to a created VAR_DECL and replace all of its
+   uses too.  DATA is a pointer to an adjustments vector.  */
+
+static bool
+replace_removed_params_ssa_names (gimple stmt, void *data)
+{
+  VEC (ipa_parm_adjustment_t, heap) *adjustments;
+  struct ipa_parm_adjustment *adj;
+  tree lhs, decl, repl, name;
+
+  adjustments = (VEC (ipa_parm_adjustment_t, heap) *) data;
+  if (gimple_code (stmt) == GIMPLE_PHI)
+    lhs = gimple_phi_result (stmt);
+  else if (is_gimple_assign (stmt))
+    lhs = gimple_assign_lhs (stmt);
+  else if (is_gimple_call (stmt))
+    lhs = gimple_call_lhs (stmt);
+  else
+    gcc_unreachable ();
+
+  if (TREE_CODE (lhs) != SSA_NAME)
+    return false;
+  decl = SSA_NAME_VAR (lhs);
+  if (TREE_CODE (decl) != PARM_DECL)
+    return false;
+
+  adj = get_adjustment_for_base (adjustments, decl);
+  if (!adj)
+    return false;
+
+  repl = get_replaced_param_substitute (adj);
+  name = make_ssa_name (repl, stmt);
+
+  if (dump_file)
+    {
+      fprintf (dump_file, "replacing an SSA name of a removed param ");
+      print_generic_expr (dump_file, lhs, 0);
+      fprintf (dump_file, " with ");
+      print_generic_expr (dump_file, name, 0);
+      fprintf (dump_file, "\n");
+    }
+
+  if (is_gimple_assign (stmt))
+    gimple_assign_set_lhs (stmt, name);
+  else if (is_gimple_call (stmt))
+    gimple_call_set_lhs (stmt, name);
+  else
+    gimple_phi_set_result (stmt, name);
+
+  replace_uses_by (lhs, name);
+  return true;
+}
+
+/* Callback for scan_function.  If the expression *EXPR should be replaced by a
+   reduction of a parameter, do so.  DATA is a pointer to a vector of
+   adjustments.  */
+
+static bool
+sra_ipa_modify_expr (tree *expr, gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED,
+                    bool write ATTRIBUTE_UNUSED, void *data)
+{
+  ipa_parm_adjustment_vec adjustments;
+  int i, len;
+  struct ipa_parm_adjustment *adj, *cand = NULL;
+  HOST_WIDE_INT offset, size, max_size;
+  tree base, src;
+
+  adjustments = (VEC (ipa_parm_adjustment_t, heap) *) data;
+  len = VEC_length (ipa_parm_adjustment_t, adjustments);
+
+  if (TREE_CODE (*expr) == BIT_FIELD_REF
+      || TREE_CODE (*expr) == IMAGPART_EXPR
+      || TREE_CODE (*expr) == REALPART_EXPR)
+    expr = &TREE_OPERAND (*expr, 0);
+  while (TREE_CODE (*expr) == NOP_EXPR
+        || TREE_CODE (*expr) == VIEW_CONVERT_EXPR)
+    expr = &TREE_OPERAND (*expr, 0);
+
+  base = get_ref_base_and_extent (*expr, &offset, &size, &max_size);
+  if (!base || size == -1 || max_size == -1)
+    return false;
+
+  if (INDIRECT_REF_P (base))
+    base = TREE_OPERAND (base, 0);
+
+  base = get_ssa_base_param (base);
+  if (!base || TREE_CODE (base) != PARM_DECL)
+    return false;
+
+  for (i = 0; i < len; i++)
+    {
+      adj = VEC_index (ipa_parm_adjustment_t, adjustments, i);
+
+      if (adj->base == base &&
+         (adj->offset == offset || adj->remove_param))
+       {
+         cand = adj;
+         break;
+       }
+    }
+  if (!cand || cand->copy_param || cand->remove_param)
+    return false;
+
+  if (cand->by_ref)
+    {
+      tree folded;
+      src = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (cand->reduction)),
+                   cand->reduction);
+      folded = gimple_fold_indirect_ref (src);
+      if (folded)
+        src = folded;
+    }
+  else
+    src = cand->reduction;
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "About to replace expr ");
+      print_generic_expr (dump_file, *expr, 0);
+      fprintf (dump_file, " with ");
+      print_generic_expr (dump_file, src, 0);
+      fprintf (dump_file, "\n");
+    }
+
+  if (!useless_type_conversion_p (TREE_TYPE (*expr), cand->type))
+    {
+      tree vce = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (*expr), src);
+      *expr = vce;
+    }
+    else
+      *expr = src;
+  return true;
+}
+
+/* Callback for scan_function to process assign statements.  Performs
+   essentially the same function like sra_ipa_modify_expr.  */
+
+static enum scan_assign_result
+sra_ipa_modify_assign (gimple *stmt_ptr,
+                      gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED, void *data)
+{
+  gimple stmt = *stmt_ptr;
+  bool any = false;
+
+  if (!gimple_assign_single_p (stmt))
+    return SRA_SA_NONE;
+
+  any |= sra_ipa_modify_expr (gimple_assign_rhs1_ptr (stmt), gsi, false,
+                             data);
+  any |= sra_ipa_modify_expr (gimple_assign_lhs_ptr (stmt), gsi, true, data);
+
+  return any ? SRA_SA_PROCESSED : SRA_SA_NONE;
+}
+
+/* Call gimple_debug_bind_reset_value on all debug statements describing
+   gimple register parameters that are being removed or replaced.  */
+
+static void
+sra_ipa_reset_debug_stmts (ipa_parm_adjustment_vec adjustments)
+{
+  int i, len;
+
+  len = VEC_length (ipa_parm_adjustment_t, adjustments);
+  for (i = 0; i < len; i++)
+    {
+      struct ipa_parm_adjustment *adj;
+      imm_use_iterator ui;
+      gimple stmt;
+      tree name;
+
+      adj = VEC_index (ipa_parm_adjustment_t, adjustments, i);
+      if (adj->copy_param || !is_gimple_reg (adj->base))
+       continue;
+      name = gimple_default_def (cfun, adj->base);
+      if (!name)
+       continue;
+      FOR_EACH_IMM_USE_STMT (stmt, ui, name)
+       {
+         /* All other users must have been removed by scan_function.  */
+         gcc_assert (is_gimple_debug (stmt));
+         gimple_debug_bind_reset_value (stmt);
+         update_stmt (stmt);
+       }
+    }
+}
+
+/* Convert all callers of NODE to pass parameters as given in ADJUSTMENTS.  */
+
+static void
+convert_callers (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments)
+{
+  tree old_cur_fndecl = current_function_decl;
+  struct cgraph_edge *cs;
+  basic_block this_block;
+
+  for (cs = node->callers; cs; cs = cs->next_caller)
+    {
+      current_function_decl = cs->caller->decl;
+      push_cfun (DECL_STRUCT_FUNCTION (cs->caller->decl));
+
+      if (dump_file)
+       fprintf (dump_file, "Adjusting call %s -> %s\n",
+                cgraph_node_name (cs->caller),
+                cgraph_node_name (cs->callee));
+
+      ipa_modify_call_arguments (cs, cs->call_stmt, adjustments);
+      compute_inline_parameters (cs->caller);
+
+      pop_cfun ();
+    }
+  current_function_decl = old_cur_fndecl;
+  FOR_EACH_BB (this_block)
+    {
+      gimple_stmt_iterator gsi;
+
+      for (gsi = gsi_start_bb (this_block); !gsi_end_p (gsi); gsi_next (&gsi))
+        {
+         gimple stmt = gsi_stmt (gsi);
+         if (gimple_code (stmt) == GIMPLE_CALL
+             && gimple_call_fndecl (stmt) == node->decl)
+           {
+             if (dump_file)
+               fprintf (dump_file, "Adjusting recursive call");
+             ipa_modify_call_arguments (NULL, stmt, adjustments);
+           }
+       }
+    }
+
+  return;
+}
+
+/* Perform all the modification required in IPA-SRA for NODE to have parameters
+   as given in ADJUSTMENTS.  */
+
+static void
+modify_function (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments)
+{
+  ipa_modify_formal_parameters (current_function_decl, adjustments, "ISRA");
+  scan_function (sra_ipa_modify_expr, sra_ipa_modify_assign,
+                replace_removed_params_ssa_names, false, adjustments);
+  sra_ipa_reset_debug_stmts (adjustments);
+  convert_callers (node, adjustments);
+  cgraph_make_node_local (node);
+  return;
+}
+
+/* Return false the function is apparently unsuitable for IPA-SRA based on it's
+   attributes, return true otherwise.  NODE is the cgraph node of the current
+   function.  */
+
+static bool
+ipa_sra_preliminary_function_checks (struct cgraph_node *node)
+{
+  if (!cgraph_node_can_be_local_p (node))
+    {
+      if (dump_file)
+       fprintf (dump_file, "Function not local to this compilation unit.\n");
+      return false;
+    }
+
+  if (DECL_VIRTUAL_P (current_function_decl))
+    {
+      if (dump_file)
+       fprintf (dump_file, "Function is a virtual method.\n");
+      return false;
+    }
+
+  if ((DECL_COMDAT (node->decl) || DECL_EXTERNAL (node->decl))
+      && node->global.size >= MAX_INLINE_INSNS_AUTO)
+    {
+      if (dump_file)
+       fprintf (dump_file, "Function too big to be made truly local.\n");
+      return false;
+    }
+
+  if (!node->callers)
+    {
+      if (dump_file)
+       fprintf (dump_file,
+                "Function has no callers in this compilation unit.\n");
+      return false;
+    }
+
+  if (cfun->stdarg)
+    {
+      if (dump_file)
+       fprintf (dump_file, "Function uses stdarg. \n");
+      return false;
+    }
+
+  return true;
+}
+
+/* Perform early interprocedural SRA.  */
+
+static unsigned int
+ipa_early_sra (void)
+{
+  struct cgraph_node *node = cgraph_node (current_function_decl);
+  ipa_parm_adjustment_vec adjustments;
+  int ret = 0;
+
+  if (!ipa_sra_preliminary_function_checks (node))
+    return 0;
+
+  sra_initialize ();
+  sra_mode = SRA_MODE_EARLY_IPA;
+
+  if (!find_param_candidates ())
+    {
+      if (dump_file)
+       fprintf (dump_file, "Function has no IPA-SRA candidates.\n");
+      goto simple_out;
+    }
+
+  bb_dereferences = XCNEWVEC (HOST_WIDE_INT,
+                                func_param_count
+                                * last_basic_block_for_function (cfun));
+  final_bbs = BITMAP_ALLOC (NULL);
+
+  scan_function (build_access_from_expr, build_accesses_from_assign,
+                NULL, true, NULL);
+  if (encountered_apply_args)
+    {
+      if (dump_file)
+       fprintf (dump_file, "Function calls  __builtin_apply_args().\n");
+      goto out;
+    }
+
+  adjustments = analyze_all_param_acesses ();
+  if (!adjustments)
+    goto out;
+  if (dump_file)
+    ipa_dump_param_adjustments (dump_file, adjustments, current_function_decl);
+
+  modify_function (node, adjustments);
+  VEC_free (ipa_parm_adjustment_t, heap, adjustments);
+  ret = TODO_update_ssa;
+
+  statistics_counter_event (cfun, "Unused parameters deleted",
+                           sra_stats.deleted_unused_parameters);
+  statistics_counter_event (cfun, "Scalar parameters converted to by-value",
+                           sra_stats.scalar_by_ref_to_by_val);
+  statistics_counter_event (cfun, "Aggregate parameters broken up",
+                           sra_stats.aggregate_params_reduced);
+  statistics_counter_event (cfun, "Aggregate parameter components created",
+                           sra_stats.param_reductions_created);
+
+ out:
+  BITMAP_FREE (final_bbs);
+  free (bb_dereferences);
+ simple_out:
+  sra_deinitialize ();
+  return ret;
+}
+
+/* Return if early ipa sra shall be performed.  */
+static bool
+ipa_early_sra_gate (void)
+{
+  return flag_ipa_sra;
+}
+
+struct gimple_opt_pass pass_early_ipa_sra =
+{
+ {
+  GIMPLE_PASS,
+  "eipa_sra",                          /* name */
+  ipa_early_sra_gate,                  /* gate */
+  ipa_early_sra,                       /* execute */
+  NULL,                                        /* sub */
+  NULL,                                        /* next */
+  0,                                   /* static_pass_number */
+  TV_IPA_SRA,                          /* tv_id */
+  0,                                   /* properties_required */
+  0,                                   /* properties_provided */
+  0,                                   /* properties_destroyed */
+  0,                                   /* todo_flags_start */
+  TODO_dump_func | TODO_dump_cgraph    /* todo_flags_finish */
+ }
+};
+
+