[PATCH 3/5] The IPA-SRA itself.

Martin Jambor mjambor@suse.cz
Fri Jul 10 16:51:00 GMT 2009


Hi,

On Thu, Jul 09, 2009 at 07:43:32PM +0200, Martin Jambor wrote:
> Hi,
> 
> this patch  contains the implementation  of IPA-SRA which is  built on
> top of the new intraprocedural SRA.
> 
> This is a re-submission in  which I incorporated most of the feedback.
> Disqualification of  addressable types and  direct uses are  done when
> selecting candidates  in the initial  phase.  The check whether  it is
> legal  to move  dereferences to  the caller  has been  re-written from
> scratch.  It now does not utilize dominators but rather propagates the
> dereference  information across the  control flow  graph in  a fashion
> similar  to the simplest  constant-propagation algorithms.   In short,
> for each  BB and pointer  candidate I note down  in local_dereferences
> whether (and  how much of it)  it was dereferenced  there.  Then, when
> actually  checking   the  dereference   info,  I  propagate   this  in
> global_dereferences to other basic blocks if all of their predecessors
> also   are   marked   as   having  dereferenced   the   parameter   in
> global_dereferences.  I propagate this information until it stabilizes
> and  then check  it for  BBs which  can abort  the progressing  of the
> functions  (returns, calls of  non-pure functions,  potential infinite
> loops, external exceptions).
> 

After running the CSiBE test, I amended the patch a little bit to be a
bit stricter when optimizing a function for size.  The changes are all
confined to the function decide_one_param_reduction and are small.

Additionally, I now  attach a context diff (created  by converting the
unified one with emacs) as I understand some people prefer them.

Thanks,

Martin

2009-07-10  Martin Jambor  <mjambor@suse.cz>

	* 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.
	(local_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_offsets): 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.
	(replace_removed_params_ssa_names): 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.



Index: mine/gcc/tree-sra.c
===================================================================
--- mine.orig/gcc/tree-sra.c
+++ mine/gcc/tree-sra.c
@@ -78,6 +78,7 @@ along with GCC; see the file COPYING3.
 #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.
 #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,22 +164,28 @@ 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;
+
   /* 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;
@@ -183,6 +194,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;
@@ -208,8 +232,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;
 
@@ -217,12 +242,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 *local_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;
 
@@ -244,8 +299,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
@@ -263,13 +329,17 @@ dump_access (FILE *f, struct access *acc
   if (grp)
     fprintf (f, ", grp_write = %d, grp_read = %d, grp_covered = %d, "
 	     "grp_unscalarizable_region = %d, grp_unscalarized_data = %d, "
-	     "grp_partial_lhs = %d, grp_to_be_replaced = %d\n",
+	     "grp_partial_lhs = %d, grp_to_be_replaced = %d, "
+	     "grp_maybe_modified = %d, "
+	     "grp_not_necessarilly_dereferenced = %d\n",
 	     access->grp_write, access->grp_read, access->grp_covered,
 	     access->grp_unscalarizable_region, access->grp_unscalarized_data,
-	     access->grp_partial_lhs, access->grp_to_be_replaced);
+	     access->grp_partial_lhs, 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);
+    fprintf (f, ", write = %d, grp_partial_lhs = %d\n",
+	     access->write, access->grp_partial_lhs);
 }
 
 /* Dump a subtree rooted in ACCESS to file F, indent by LEVEL.  */
@@ -465,6 +535,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.  */
@@ -554,34 +625,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 (local_dereferences[idx] < dist)
+    local_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);
@@ -594,6 +736,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)
@@ -619,7 +762,14 @@ disqualify_base_of_expr (tree t, const c
   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);
 }
 
@@ -628,7 +778,7 @@ disqualify_base_of_expr (tree t, const c
    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;
@@ -660,13 +810,17 @@ build_access_from_expr_1 (tree *expr_ptr
 
   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:
@@ -688,7 +842,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
@@ -699,7 +853,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)
@@ -740,10 +895,11 @@ build_accesses_from_assign (gimple *stmt
   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))
@@ -811,6 +967,10 @@ scan_function (bool (*scan_expr) (tree *
     {
       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))
 	{
@@ -818,12 +978,16 @@ scan_function (bool (*scan_expr) (tree *
 	  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:
@@ -842,6 +1006,21 @@ scan_function (bool (*scan_expr) (tree *
 		  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);
@@ -857,10 +1036,13 @@ scan_function (bool (*scan_expr) (tree *
 	      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));
@@ -871,7 +1053,6 @@ scan_function (bool (*scan_expr) (tree *
 		  tree *op = &TREE_VALUE (gimple_asm_output_op (stmt, i));
 		  any |= scan_expr (op, &gsi, true, data);
 		}
-
 	    default:
 	      break;
 	    }
@@ -879,13 +1060,13 @@ scan_function (bool (*scan_expr) (tree *
 	  if (any)
 	    {
 	      ret = true;
-	      bb_changed = true;
 
 	      if (!analysis_stage)
 		{
+		  bb_changed = true;
 		  update_stmt (stmt);
-		  if (!stmt_could_throw_p (stmt))
-		    remove_stmt_from_eh_region (stmt);
+		  if (sra_mode == SRA_MODE_EARLY_IPA)
+		    maybe_clean_or_replace_eh_stmt (stmt, stmt);
 		}
 	    }
 	  if (deleted)
@@ -896,7 +1077,7 @@ scan_function (bool (*scan_expr) (tree *
 	      ret = true;
 	    }
 	}
-      if (!analysis_stage && bb_changed)
+      if (!analysis_stage && bb_changed && sra_mode == SRA_MODE_EARLY_IPA)
 	gimple_purge_dead_eh_edges (bb);
     }
 
@@ -2395,7 +2576,6 @@ struct gimple_opt_pass pass_sra_early =
  }
 };
 
-
 struct gimple_opt_pass pass_sra =
 {
  {
@@ -2417,3 +2597,1148 @@ 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
+	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;
+	}
+    }
+}
+
+/* Fill in the GLOBAL_DEREFERENCES table, which has the same structure as the
+   global local_dereferences by propagating the local dereferences.  */
+
+static void
+propagate_dereference_offsets (HOST_WIDE_INT *global_dereferences)
+{
+  VEC (basic_block, heap) *queue;
+  basic_block bb;
+
+  queue = VEC_alloc (basic_block, heap, last_basic_block_for_function (cfun));
+  FOR_EACH_BB_REVERSE (bb)
+    if (bb != ENTRY_BLOCK_PTR && bb != EXIT_BLOCK_PTR)
+      {
+	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;
+
+      for (i = 0; i < func_param_count; i++)
+	{
+	  int idx = bb->index * func_param_count + i;
+	  bool first = true;
+	  HOST_WIDE_INT val, inh = 0;
+
+	  FOR_EACH_EDGE (e, ei, bb->preds)
+	  {
+	    int pred_idx = e->src->index * func_param_count + i;
+
+	    if (e->src == ENTRY_BLOCK_PTR
+		|| bitmap_bit_p (final_bbs, e->src->index))
+	      continue;
+
+	    if (first)
+	      {
+		first = false;
+		inh = global_dereferences [pred_idx];
+	      }
+	    else if (global_dereferences [pred_idx] < inh)
+	      inh = global_dereferences [pred_idx];
+	  }
+
+	  if (local_dereferences[idx] <= inh)
+	    val = inh;
+	  else
+	    val = local_dereferences[idx];
+
+	  if (val != global_dereferences[idx])
+	    {
+	      global_dereferences[idx] = val;
+	      change = true;
+	    }
+	}
+
+      if (change && !bitmap_bit_p (final_bbs, bb->index))
+	FOR_EACH_EDGE (e, ei, bb->succs)
+	  {
+	    if (e->dest == EXIT_BLOCK_PTR
+		|| e->dest->aux)
+	      continue;
+
+	    e->dest->aux = e->dest;
+	    VEC_quick_push (basic_block, queue, e->dest);
+	  }
+    }
+
+  VEC_free (basic_block, heap, queue);
+}
+
+/* Dump a dereferences TABLE to file F.  */
+
+static void
+dump_dereferences_table (FILE *f, HOST_WIDE_INT *table)
+{
+  basic_block bb;
+
+  FOR_EACH_BB (bb)
+    {
+      fprintf (f, "%4i  %i   ", bb->index, bitmap_bit_p (final_bbs, bb->index));
+      if (bb != ENTRY_BLOCK_PTR && 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");
+    }
+}
+
+/* Determine what reduced parameters passed by reference are not definitely
+   dereferenced and thus the dereferencing cannot be safely moved to the
+   caller.  Mark such REPRESENTATIVES as grp_not_necessarilly_dereferenced.  */
+
+static void
+analyze_caller_dereference_legality (VEC (access_p, heap) *representatives)
+{
+  HOST_WIDE_INT *global_dereferences;
+  edge_iterator ei;
+  basic_block bb;
+  edge e;
+  int i;
+
+  global_dereferences = XCNEWVEC (HOST_WIDE_INT,
+				 func_param_count
+				 * last_basic_block_for_function (cfun));
+  mark_dfs_back_edges ();
+  FOR_EACH_BB (bb)
+    {
+      bb->aux = NULL;
+      FOR_EACH_EDGE (e, ei, bb->succs)
+	if (e->flags & EDGE_DFS_BACK)
+	  bitmap_set_bit (final_bbs, bb->index);
+    }
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "Local dereference table:\n");
+      dump_dereferences_table (dump_file, local_dereferences);
+      fprintf (dump_file, "\n");
+    }
+
+  propagate_dereference_offsets (global_dereferences);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "Global dereference table:\n");
+      dump_dereferences_table (dump_file, global_dereferences);
+      fprintf (dump_file, "\n");
+    }
+
+  FOR_EACH_BB (bb)
+    {
+      gcc_assert (bb->aux == NULL);
+      if (bitmap_bit_p (final_bbs, bb->index))
+	for (i = 0; i < func_param_count; i++)
+	  {
+	    struct access *repr = VEC_index (access_p, representatives, i);
+	    int idx = bb->index * func_param_count + i;
+
+	    if (!repr || no_accesses_p (repr))
+	      continue;
+
+	    do
+	      {
+		if ((repr->offset + repr->size) > global_dereferences[idx])
+		  repr->grp_not_necessarilly_dereferenced = 1;
+		repr = repr->next_grp;
+	      }
+	    while (repr);
+	  }
+    }
+
+  free (global_dereferences);
+}
+
+/* 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.  If non-NULL, 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)
+	*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;
+}
+
+/* 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;
+  tree lhs, decl;
+  int i, len;
+
+  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;
+
+  len = VEC_length (ipa_parm_adjustment_t, adjustments);
+  for (i = 0; i < len; i++)
+    {
+      tree repl, name;
+      struct ipa_parm_adjustment *adj;
+
+      adj = VEC_index (ipa_parm_adjustment_t, adjustments, i);
+      if (adj->copy_param || adj->base != decl)
+	continue;
+
+      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;
+    }
+  return false;
+}
+
+/* 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)
+{
+  VEC (ipa_parm_adjustment_t, heap) *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);
+  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;
+}
+
+/* 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);
+  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;
+    }
+
+  local_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 (local_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 */
+ }
+};
+
+
Index: mine/gcc/tree-pass.h
===================================================================
--- mine.orig/gcc/tree-pass.h
+++ mine/gcc/tree-pass.h
@@ -327,6 +327,7 @@ extern struct gimple_opt_pass pass_clean
 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: mine/gcc/passes.c
===================================================================
--- mine.orig/gcc/passes.c
+++ mine/gcc/passes.c
@@ -563,6 +563,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: mine/gcc/Makefile.in
===================================================================
--- mine.orig/gcc/Makefile.in
+++ mine/gcc/Makefile.in
@@ -2801,8 +2801,9 @@ tree-ssa-ccp.o : tree-ssa-ccp.c $(TREE_F
    $(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) \
-------------- next part --------------
A non-text attachment was scrubbed...
Name: ipa_sra_context.diff
Type: text/x-patch
Size: 56433 bytes
Desc: not available
URL: <http://gcc.gnu.org/pipermail/gcc-patches/attachments/20090710/007823a9/attachment.bin>


More information about the Gcc-patches mailing list