[PATCH, pretty-ipa merge 3/4] The IPA-SRA itself.

Richard Guenther rguenther@suse.de
Sun Jun 28 14:42:00 GMT 2009


On Wed, 24 Jun 2009, Martin Jambor wrote:

> Hi,
> 
> this patch  contains the implementation  of IPA-SRA which is  built on
> top of the  new intraprocedural SRA (with which it  shares most of the
> analysis stage) as it is in the pretty-ipa now.
> 
> Thanks,
> 
> Martin
> 
> 
> 2009-06-23  Martin Jambor  <mjambor@suse.cz>
> 
> 	* tree-sra.c: Include cgraph.h.
> 	(enum sra_mode): Added SRA_MODE_EARLY_IPA.
> 	(struct access): Added members bb, stmt, always_safe,
> 	grp_maybe_modified and grp_scalar_ptr.
> 	(func_param_count): New variable.
> 	(encountered_va_start): New variable.
> 	(encountered_external_throw): New variable.
> 	(no_accesses_representant): New variable.
> 	(+no_accesses_p): New function.
> 	(dump_access): Dump the new access members.
> 	(sra_initialize): Initialize encountered_va_start and
> 	encountered_external_throw.
> 	(get_ssa_base_param): New function.
> 	(create_access): Caring for INIDRECT_REFs and different handling of
> 	varialble length accesses in early IPA SRA.  Store tthe basic block and
> 	stmt - new parameter - to the new access.
> 	(disqualify_base_of_expr): Take care of INDIRECT_REFs and look through
> 	SSA_NAMES in IPA_SRA.
> 	(disqualify_direct_ptr_params): New function.
> 	(disqualify_all_direct_ptr_params): New function.
> 	(build_access_from_expr_1): Call disqualify_direct_ptr_params, handle
> 	INDIRECT_REFs and ADDR_EXPRs.  New parameter stmt passed to
> 	create_access.
> 	(disqualify_ops_if_throwing_stmt): Trigger only in intraprocedural
> 	passes.
> 	(build_accesses_from_assign): Check statements that are not single
> 	assignments for disallowed uses of parameters.  Do not create assign
> 	links in the interprocedural pass.
> 	(scan_phi_nodes): New function.
> 	(scan_function): In the interprocedural pass analysis scan phi nodes,
> 	check for externally throwing statements and va_start, make sure to
> 	disqualify all direct uses in statement types which arew not handled
> 	explicitely.
> 	(build_ref_for_offset): Not static any more.
> 	(find_param_candidates): New function.
> 	(mark_maybe_modified): New function.
> 	(analyze_modified_params): New function.
> 	(process_dominator_bb): New function.
> 	(fake_edges_required_p): 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.
> 	(is_unused_scalar_param): New function.
> 	(enum ipa_splicing_result): New type.
> 	(splice_all_param_accesses): New function.
> 	(get_param_index): New function.
> 	(turn_representatives_into_notes): 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_early_sra): New function.
> 	(ipa_early_sra_gate): New function.
> 	(pass_early_ipa_sra): New variable.
> 	* tree-pass.h (pass_early_ipa_sra): Declare.
> 	* passes.c (init_optimization_passes): Add pass_early_ipa_sra to the
> 	early passes.
> 	* Makefile.in (tree-sra.o): Add cgraph.h to dependencies.
> 
> 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,11 @@ struct access
>    /* Type.  */
>    tree type;
>  
> +  /* The basic block of this access.  */
> +  basic_block bb;
> +  /* The statement this access belongs to.  */
> +  gimple stmt;
> +
>    /* Next group representative for this aggregate. */
>    struct access *next_grp;
>  
> @@ -156,6 +163,9 @@ struct access
>  
>    /* Is this particular access write access? */
>    unsigned write : 1;
> +  /* in IPA-SRA, is it guaranteed that an access to this or bigger offset is
> +     always performed when the function is run? */
> +  unsigned always_safe : 1;
>  
>    /* Is this access currently in the work queue?  */
>    unsigned grp_queued : 1;
> @@ -178,11 +188,18 @@ struct access
>    /* Does this access and/or group contain a write access through a
>       BIT_FIELD_REF?  */
>    unsigned grp_partial_lhs : 1;
> -
>    /* Set when a scalar replacement should be created for this variable.  We do
>       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;
>  };

Please keep the vertical space.

>  typedef struct access *access_p;
> @@ -217,6 +234,27 @@ 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_va_start.  */
> +static bool encountered_va_start;
> +/* scan_function sets the following to true whenever it encounters a statement
> +   that can throw externally.  */
> +static bool encountered_external_throw;
> +
> +/* 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.  */
> @@ -263,13 +301,15 @@ 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\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);
>    else
> -    fprintf (f, ", write = %d, grp_partial_lhs = %d\n", access->write,
> -	     access->grp_partial_lhs);
> +    fprintf (f, ", write = %d, grp_partial_lhs = %d, always_safe = %d\n",
> +	     access->write, access->grp_partial_lhs, access->always_safe);
>  }
>  
>  /* Dump a subtree rooted in ACCESS to file F, indent by LEVEL.  */
> @@ -465,6 +505,8 @@ 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_va_start = false;
> +  encountered_external_throw = false;
>  }
>  
>  /* Hook fed to pointer_map_traverse, deallocate stored vectors.  */
> @@ -554,34 +596,76 @@ 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;
> +}

Huh.  Well, let's see how you use that beast ...

>  /* 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 && TREE_CODE (base) == INDIRECT_REF)
> +    {
> +      base = get_ssa_base_param (TREE_OPERAND (base, 0));

if that remains the only use please inline it here.

> +      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)
> +  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;
> +	}

Why distinguish SRA_MODE_EARLY_IPA for these disqualifications?  At
least size < 0 / size != max_size seem to be common code (of course
I might miss sth, the unified diff is confusing here).

>      }
>  
>    access = (struct access *) pool_alloc (access_pool);
> @@ -594,6 +678,8 @@ create_access (tree expr, bool write)
>    access->type = TREE_TYPE (expr);
>    access->write = write;
>    access->grp_unscalarizable_region = unscalarizable_region;
> +  access->stmt = stmt;
> +  access->bb = gimple_bb (stmt);

So bb is indeed redundant information.

>    slot = pointer_map_contains (base_access_vec, base);
>    if (slot)
> @@ -619,16 +705,67 @@ disqualify_base_of_expr (tree t, const c
>    while (handled_component_p (t))
>      t = TREE_OPERAND (t, 0);
>  
> -  if (DECL_P (t))
> +  while (TREE_CODE (t) == INDIRECT_REF)
> +    t = TREE_OPERAND (t, 0);

No need for a loop here.  Use

     if (INDIRECT_REF_P (t))
       t = TREE_OPERAND (t, 0);

> +  if (sra_mode == SRA_MODE_EARLY_IPA)
> +    t = get_ssa_base_param (t);

in fact, INDIRECT_REF seems to be relevant for SRA_MODE_EARLY_IPA only,
so put it inside that if as well.  This is the 2nd use with
INDIRECT_REFs, maybe the helper should just cover the INDIRECT_REF
as well.

> +  if (t && DECL_P (t))
>      disqualify_candidate (t, reason);
>  }
>  
> +/* See if OP is an undereferenced use of pointer parameters and if it is,
> +   exclude it from the candidates and return true, otherwise return false.  */
> +
> +static bool
> +disqualify_direct_ptr_params (tree op)
> +{
> +  bool addr_taken;
> +
> +  if (!op)
> +    return false;
> +  if (TREE_CODE (op) == ADDR_EXPR)
> +    {
> +      do
> +	op = TREE_OPERAND (op, 0);
> +      while (handled_component_p (op));

Add {} braces.

> +      addr_taken = true;

op can be still *parm here, so

> +    }
> +  else

this else looks wrong and above or in that fn you should strip
INDIRECT_REFs.  Or use disqualify_base_of_expr above like you do below
for ADDR_EXPRs.

> +    {
> +      op = get_ssa_base_param (op);
> +      addr_taken = false;
> +    }
> +
> +  if (op && TREE_CODE (op) == PARM_DECL
> +      && (addr_taken || POINTER_TYPE_P (TREE_TYPE (op))))
> +    {
> +      disqualify_candidate (op, " Direct use of its pointer value or "
> +			    "invariant addr_expr.");
> +      return true;
> +    }
> +  return false;
> +}
> +
> +/* A callback for walk_gimple_op.  Disqualifies SSA_NAMEs of default_defs of
> +   params and does not descend any further into the tree structure.  */
> +
> +static tree
> +disqualify_all_direct_ptr_params (tree *tp, int *walk_subtrees,
> +				  void *data ATTRIBUTE_UNUSED)
> +{
> +  *walk_subtrees = 0;
> +  disqualify_direct_ptr_params (*tp);
> +  return NULL_TREE;
> +}
> +
>  /* Scan expression EXPR and create access structures for all accesses to
>     candidates for scalarization.  Return the created access or NULL if none is
>     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;
> @@ -644,6 +781,9 @@ build_access_from_expr_1 (tree *expr_ptr
>    else
>      partial_ref = false;
>  
> +  if (sra_mode == SRA_MODE_EARLY_IPA)
> +    disqualify_direct_ptr_params (expr);
> +
>    /* We need to dive through V_C_Es in order to get the size of its parameter
>       and not the result type.  Ada produces such statements.  We are also
>       capable of handling the topmost V_C_E but not any of those buried in other
> @@ -660,13 +800,23 @@ 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;
> +
> +    case ADDR_EXPR:
> +      if (sra_mode == SRA_MODE_EARLY_IPA)
> +	disqualify_base_of_expr (TREE_OPERAND (expr, 0),
> +				 "Is used in an ADDR_EXPR.");
>        break;
>  
>      default:
> @@ -688,7 +838,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 +849,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)
> @@ -731,8 +882,26 @@ build_accesses_from_assign (gimple *stmt
>    tree *lhs_ptr, *rhs_ptr;
>    struct access *lacc, *racc;
>  
> -  if (!gimple_assign_single_p (stmt))
> -    return SRA_SA_NONE;
> +  if (sra_mode == SRA_MODE_EARLY_IPA)
> +    {
> +      if (TREE_CODE (gimple_assign_rhs1 (stmt)) == CONSTRUCTOR)
> +	{
> +	  disqualify_base_of_expr (gimple_assign_lhs (stmt),
> +				   "Assignment to a constructor.");

"... from a constructor."

> +	  return SRA_SA_NONE;
> +	}
> +
> +      if (!gimple_assign_single_p (stmt))
> +	{
> +	  disqualify_direct_ptr_params (gimple_assign_rhs1 (stmt));
> +	  if (gimple_assign_rhs2 (stmt))
> +	    disqualify_direct_ptr_params (gimple_assign_rhs2 (stmt));
> +	  return SRA_SA_NONE;
> +	}

This again looks like poor factoring to me.

> +    }
> +  else
> +    if (!gimple_assign_single_p (stmt))
> +      return SRA_SA_NONE;
>  
>    lhs_ptr = gimple_assign_lhs_ptr (stmt);
>    rhs_ptr = gimple_assign_rhs1_ptr (stmt);
> @@ -740,10 +909,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))
> @@ -766,6 +936,52 @@ build_accesses_from_assign (gimple *stmt
>    return (lacc || racc) ? SRA_SA_PROCESSED : SRA_SA_NONE;
>  }
>  
> +/* If ANALYSIS_STAGE is true disqualify all parameters that have their address
> +   taken in a phi node of basic block BB and, if non-NULL, call HANDLE_SSA_DEFS
> +   on each such phi node.  Return true iff any call to HANDLE_SSA_DEFS did
> +   so.  */

Do you at any point handle address takens on params?  If not why not
check TREE_ADDRESSABLE on the param?  Note that you will never
see &param->x in PHI nodes.

That said, the following function looks superfluous to me.

> +static bool
> +scan_phi_nodes (basic_block bb, bool analysis_stage,
> +		bool (*handle_ssa_defs)(gimple, void *), void *data)
> +{
> +  gimple_stmt_iterator gsi;
> +  bool ret = false;
> +  for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
> +    {
> +      gimple phi = gsi_stmt (gsi);
> +      use_operand_p arg_p;
> +      ssa_op_iter i;
> +      bool any = false;
> +
> +      if (analysis_stage)
> +	FOR_EACH_PHI_ARG (arg_p, phi, i, SSA_OP_USE)
> +	  {
> +	    tree op = USE_FROM_PTR (arg_p);
> +	    if (TREE_CODE (op) == ADDR_EXPR)
> +	      {
> +		op = TREE_OPERAND (op, 0);
> +		if (DECL_P (op))
> +		  disqualify_candidate (op,
> +					"Address taken in a phi node.");
> +	      }
> +	    else
> +	      disqualify_direct_ptr_params (op);
> +	  }
> +
> +      if (handle_ssa_defs)
> +	ret |= handle_ssa_defs (phi, data);
> +      if (any)
> +	{
> +	  ret = true;
> +
> +	  if (!analysis_stage)
> +	    update_stmt (phi);
> +	}
> +    }
> +  return ret;
> +}
> +
>  /* Callback of walk_stmt_load_store_addr_ops visit_addr used to determine
>     GIMPLE_ASM operands with memory constrains which cannot be scalarized.  */
>  
> @@ -811,6 +1027,9 @@ scan_function (bool (*scan_expr) (tree *
>      {
>        bool bb_changed = false;
>  
> +      if (sra_mode == SRA_MODE_EARLY_IPA)
> +	scan_phi_nodes (bb, analysis_stage, handle_ssa_defs, data);
> +
>        gsi = gsi_start_bb (bb);
>        while (!gsi_end_p (gsi))
>  	{
> @@ -818,6 +1037,8 @@ scan_function (bool (*scan_expr) (tree *
>  	  enum scan_assign_result assign_result;
>  	  bool any = false, deleted = false;
>  
> +	  if (stmt_can_throw_external (stmt))
> +	    encountered_external_throw = true;
>  	  switch (gimple_code (stmt))
>  	    {
>  	    case GIMPLE_RETURN:
> @@ -835,6 +1056,11 @@ scan_function (bool (*scan_expr) (tree *
>  	      break;
>  
>  	    case GIMPLE_CALL:
> +	      if (analysis_stage
> +		  && (gimple_call_fndecl (stmt)
> +		      == built_in_decls[BUILT_IN_VA_START]))
> +		encountered_va_start = true;

So, va-start is special exactly why?  Shouldn't you instead simply
disqualify all functions with varargs only or do you want to
catch functions with a va_list param as well?

>  	      /* Operands must be processed before the lhs.  */
>  	      for (i = 0; i < gimple_call_num_args (stmt); i++)
>  		{
> @@ -873,19 +1099,21 @@ scan_function (bool (*scan_expr) (tree *
>  		}
>  
>  	    default:
> +	      if (analysis_stage && sra_mode == SRA_MODE_EARLY_IPA)
> +		walk_gimple_op (stmt, disqualify_all_direct_ptr_params, NULL);

too much random disqualification again ;)  But this complaint I already
had for the intra-SRA, no? ;)

>  	      break;
>  	    }
>  
>  	  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);

No need for the other SRAs?  Why would you ever be able to SRA
sth whos access may throw?

>  		}
>  	    }
>  	  if (deleted)
> @@ -896,7 +1124,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);

Likewise.

>      }
>  
> @@ -2382,6 +2610,1003 @@ struct gimple_opt_pass pass_sra_early =
>   }
>  };
>  
> +
> +/* 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))
> +	continue;
> +
> +      type = TREE_TYPE (parm);
> +      if (POINTER_TYPE_P (type))
> +	{
> +	  type = TREE_TYPE (type);
> +
> +	  if ((!is_gimple_reg_type (type) && !AGGREGATE_TYPE_P (type))

is_gimple_reg_type is !AGGREGATE_TYPE_P () at the moment, so the
above cannot be true ;)

> +	      || TREE_CODE (type) == FUNCTION_TYPE
> +	      || TYPE_VOLATILE (type))
> +	    continue;
> +	}
> +      else if (!AGGREGATE_TYPE_P (type))
> +	continue;

ok, so unused scalar parameters that are not pointers are dealt with
in some other way?

> +      if (!COMPLETE_TYPE_P (type)
> +	  || TREE_ADDRESSABLE (type)
> +	  || !host_integerp (TYPE_SIZE (type), 1)
> +          || tree_low_cst (TYPE_SIZE (type), 1) == 0)
> +	continue;
> +
> +      if (AGGREGATE_TYPE_P (type)
> +	  && type_internals_preclude_sra_p (type))
> +	continue;
> +
> +      bitmap_set_bit (candidate_bitmap, DECL_UID (parm));
> +      ret = true;
> +      if (dump_file)
> +	{
> +	  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;
> +}

Comment missing before function.

> +static bool
> +mark_maybe_modified (tree ref 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;
> +	  access = VEC_index (access_p, access_vec, j);
> +
> +	  walk_aliased_vdefs (access->expr, gimple_vuse (access->stmt),
> +			      mark_maybe_modified, repr, NULL);

Wow, this is going to be expensive.  And I don't understand
what you are doing here anyway.  You seem to be marking repr (the
representative for a param) as modified whenever you find a 
reaching definition of an access of it (the base access vector includes
stores, does it?).

I don't see how this can be complete.  As you handle pointer-to-struct
params you can get aliases and so need to walk _all_ accesses, not
just that with the exact same base as the param.  Or do you think
you have excluded aliases somehow?  Imagine

 int foo (struct X *p, struct X *q)
 {
   q->x = 0;
   return p->x;
 }

you cannot pass p->x instead of p.

> +	  if (repr->grp_maybe_modified)
> +	    break;
> +	}
> +    }
> +}
> +
> +/* Process BB which is a dominator of EXIT for parameter PARM by searching for
> +   an access to parm that dereference it and if there is one, marking all
> +   accesses to that or smaller offset as possible to dereference.  */

I think we discussed at the summit that this sounds fishy.  What you
need to prove is that there is that on every path from function entry
to exit there is at least one dereference of parm.

This is trivially the case if you find one dereference that
post-dominates the entry basic-block and dominates the exit
basic-block (which is only not true if there is no path to the
exit at all).  It might be true in non-trivial ways as well,
but likely proving it would be equivalent to solving the halting
problem.

> +static void
> +process_dominator_bb (tree parm, basic_block bb)
> +{
> +  int i, access_count;
> +  VEC (access_p, heap) *access_vec;
> +  bool hit = false;
> +  HOST_WIDE_INT offset = 0;
> +
> +  access_vec = get_base_access_vector (parm);
> +  if (!access_vec)
> +    return;
> +  access_count = VEC_length (access_p, access_vec);
> +
> +  for (i = 0; i < access_count; i++)
> +    {
> +      struct access *access = VEC_index (access_p, access_vec, i);
> +
> +      if (access->bb != bb)
> +	continue;
> +
> +      hit = true;
> +      if (access->offset > offset)
> +	offset = access->offset;
> +    }
> +
> +  if (!hit)
> +    return;
> +
> +  for (i = 0; i < access_count; i++)
> +    {
> +      struct access *access = VEC_index (access_p, access_vec, i);
> +
> +      if (access->offset <= offset)
> +	access->always_safe = 1;
> +    }
> +  return;
> +}
> +
> +/* Determine whether we would need to add fake edges in order to guarantee
> +   dereference legality in callers.  See the fixme in a comment in
> +   analyze_caller_dereference_legality for some insight why we do not actually
> +   add the edges. */
> +static bool
> +fake_edges_required_p (void)
> +{
> +  basic_block bb;
> +
> +  if (encountered_external_throw)
> +    return true;
> +
> +  FOR_EACH_BB (bb)
> +  {
> +    edge_iterator ei;
> +    edge e;
> +
> +    FOR_EACH_EDGE (e, ei, bb->succs)
> +      {
> +	if (e->flags & EDGE_DFS_BACK)

This flag is not always up-to-date.

> +	  return true;
> +      }
> +  }
> +  return false;
> +}
> +
> +/* Determine what reduced parameters passed by reference are definitely
> +   dereferenced so that the dereferencing can be safely moved to the caller. */
> +
> +static void
> +analyze_caller_dereference_legality (void)
> +{
> +  basic_block entry = ENTRY_BLOCK_PTR_FOR_FUNCTION (cfun);
> +  basic_block bb = EXIT_BLOCK_PTR_FOR_FUNCTION (cfun);
> +
> +  /* FIXME: Dominance does not work for the EXIT block.  Until this is fixed,
> +     we can use instead it's only predecessor If it has only one.  In other
> +     cases, we'll just check the first basic block.
> +
> +     Moreover, when there are statements which can throw externally or loops
> +     (which might just never terminate) we would normally need to add a fake
> +     edge from such block to the exit block.  That would, however, make the
> +     exit block have multiple predecessors and so in such cases, we also just
> +     check the first basic block.
> +  */
> +  if (!single_pred_p (bb) || fake_edges_required_p ())
> +    {
> +      tree parm;
> +      for (parm = DECL_ARGUMENTS (current_function_decl);
> +	   parm;
> +	   parm = TREE_CHAIN (parm))
> +	{
> +	  if (bitmap_bit_p (candidate_bitmap, DECL_UID (parm)))
> +	    process_dominator_bb (parm, single_succ (entry));

no need to do this if parm is not a pointer.

> +	}
> +
> +      return;
> +    }
> +
> +  bb = single_pred (bb);
> +  while (bb && bb != entry)
> +    {
> +      tree parm;
> +      for (parm = DECL_ARGUMENTS (current_function_decl);
> +	   parm;
> +	   parm = TREE_CHAIN (parm))
> +	{
> +	  if (bitmap_bit_p (candidate_bitmap, DECL_UID (parm)))
> +	    process_dominator_bb (parm, bb);

likewise.

> +	}
> +
> +      bb = get_immediate_dominator (CDI_DOMINATORS, bb);
> +    }
> +
> +  return;
> +}
> +
> +/* 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 no
> +   accesses or 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);
> +
> +  /* Sort by <OFFSET, SIZE>.  */
> +  qsort (VEC_address (access_p, access_vec), access_count, sizeof (access_p),
> +	 compare_access_positions);
> +
> +  if (dump_file)
> +    {
> +      fprintf (dump_file, "Splicing PARAM accesses for ");
> +      print_generic_expr (dump_file, parm, 0);
> +      fprintf (dump_file, " (UID: %u): \n", DECL_UID (parm));
> +      for (i = 0; i < access_count; i++)
> +	dump_access (dump_file, VEC_index (access_p, access_vec, i), false);
> +    }
> +
> +  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;
> +  bool by_ref;
> +  tree parm;
> +
> +  parm = repr->base;
> +  gcc_assert (TREE_CODE (parm) == PARM_DECL);
> +  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->always_safe))
> +	total_size += repr->size;
> +      else
> +	total_size += cur_parm_size;
> +    }
> +
> +  gcc_assert (new_param_count > 0);
> +  /* FIXME: 2 probably needs to be replaced by a parameter */
> +  if (total_size < agg_size
> +      && total_size <= 2 * cur_parm_size)
> +    {
> +      if (dump_file)
> +	fprintf (dump_file, "    ....will be split into %i components\n",
> +		 new_param_count);
> +      return new_param_count;
> +    }
> +  else
> +    return 0;
> +}
> +
> +/* 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)));
> +}
> +
> +/* 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 i 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 notes.
> +   REPRESENTATIVES are pointers to first representatives of each param
> +   accesses, NOTE_COUNT is the expected final number of notes.  */
> +
> +static VEC (ipa_parm_note_t, heap) *
> +turn_representatives_into_notes (VEC (access_p, heap) *representatives,
> +				 int note_count)
> +{
> +  VEC (tree, heap) *parms;
> +  VEC (ipa_parm_note_t, heap) *notes;
> +  tree parm;
> +  int i;
> +
> +  gcc_assert (note_count > 0);
> +  parms = ipa_get_vector_of_formal_parms (current_function_decl);
> +  notes = VEC_alloc (ipa_parm_note_t, heap, note_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_note *note;
> +
> +	  note = VEC_quick_push (ipa_parm_note_t, notes, NULL);
> +	  memset (note, 0, sizeof (*note));
> +	  note->base_index = get_param_index (parm, parms);
> +	  note->base = parm;
> +	  if (!repr)
> +	    note->copy_param = 1;
> +	  else
> +	    note->remove_param = 1;
> +	}
> +      else
> +	{
> +	  struct ipa_parm_note *note;
> +	  int index = get_param_index (parm, parms);
> +
> +	  for (; repr; repr = repr->next_grp)
> +	    {
> +	      note = VEC_quick_push (ipa_parm_note_t, notes, NULL);
> +	      memset (note, 0, sizeof (*note));
> +	      gcc_assert (repr->base == parm);
> +	      note->base_index = index;
> +	      note->base = repr->base;
> +	      note->type = repr->type;
> +	      note->offset = repr->offset;
> +	      note->by_ref = (POINTER_TYPE_P (TREE_TYPE (repr->base))
> +			      && (repr->grp_maybe_modified
> +				  || !repr->always_safe));
> +
> +	    }
> +	}
> +    }
> +  VEC_free (tree, heap, parms);
> +  return notes;
> +}
> +
> +/* Analyze the collected accesses and produce a plan what to do with the
> +   parameters in the form of notes, NULL meaning nothing.  */
> +
> +static VEC (ipa_parm_note_t, heap) *
> +analyze_all_param_acesses (void)
> +{
> +  enum ipa_splicing_result repr_state;
> +  bool proceed = false;
> +  int i, note_count = 0;
> +  VEC (access_p, heap) *representatives;
> +  VEC (ipa_parm_note_t, heap) *notes;
> +
> +  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 ();
> +      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)
> +	    {
> +	      note_count++;
> +	      if (!repr->always_safe || repr->grp_maybe_modified)
> +		VEC_replace (access_p, representatives, i, NULL);
> +	      else
> +		proceed = true;
> +	    }
> +	  else
> +	    {
> +	      int new_components = decide_one_param_reduction (repr);
> +
> +	      if (new_components == 0)
> +		{
> +		  VEC_replace (access_p, representatives, i, NULL);
> +		  note_count++;
> +		}
> +	      else
> +		{
> +		  note_count += new_components;
> +		  proceed = true;
> +		}
> +	    }
> +	}
> +      else
> +	{
> +	  if (no_accesses_p (repr))
> +	    proceed = true;
> +	  note_count++;
> +	}
> +    }
> +
> +  if (!proceed && dump_file)
> +    fprintf (dump_file, "NOT proceeding to change params.\n");
> +
> +  if (proceed)
> +    notes = turn_representatives_into_notes (representatives, note_count);
> +  else
> +    notes = NULL;
> +
> +  VEC_free (access_p, heap, representatives);
> +  return notes;
> +}
> +
> +/* If a parameter replacement identified by NOTE 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_note *note)
> +{
> +  tree repl;
> +  if (!note->new_ssa_base)
> +    {
> +      char *pretty_name = make_fancy_name (note->base);
> +
> +      repl = make_rename_temp (TREE_TYPE (note->base), "ISR");
> +      DECL_NAME (repl) = get_identifier (pretty_name);
> +      obstack_free (&name_obstack, pretty_name);
> +
> +      get_var_ann (repl);
> +      add_referenced_var (repl);
> +      note->new_ssa_base = repl;
> +    }
> +  else
> +    repl = note->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 a note vector.  */
> +
> +static bool
> +replace_removed_params_ssa_names (gimple stmt, void *data)
> +{
> +  VEC (ipa_parm_note_t, heap) *notes = (VEC (ipa_parm_note_t, heap) *) data;
> +  tree lhs, decl;
> +  int i, len;
> +
> +  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_note_t, notes);
> +  for (i = 0; i < len; i++)
> +    {
> +      tree repl, name;
> +      struct ipa_parm_note *note = VEC_index (ipa_parm_note_t, notes, i);
> +
> +      if (note->copy_param || note->base != decl)
> +	continue;
> +
> +      gcc_assert (!SSA_NAME_IS_DEFAULT_DEF (lhs));
> +      repl = get_replaced_param_substitute (note);
> +      name = make_ssa_name (repl, stmt);
> +
> +      if (dump_file)
> +	{
> +	  fprintf (dump_file, "replacing SSA name of 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
> +   notes.  */
> +
> +static bool
> +sra_ipa_modify_expr (tree *expr, gimple_stmt_iterator *gsi ATTRIBUTE_UNUSED,
> +		     bool write ATTRIBUTE_UNUSED, void *data)
> +{
> +  VEC (ipa_parm_note_t, heap) *notes = (VEC (ipa_parm_note_t, heap) *) data;
> +  int i, len = VEC_length (ipa_parm_note_t, notes);
> +  struct ipa_parm_note *note, *cand = NULL;
> +  HOST_WIDE_INT offset, size, max_size;
> +  tree base, src;
> +
> +  while (TREE_CODE (*expr) == NOP_EXPR
> +	 || TREE_CODE (*expr) == VIEW_CONVERT_EXPR)
> +    expr = &TREE_OPERAND (*expr, 0);
> +
> +  if (handled_component_p (*expr))
> +    {
> +      base = get_ref_base_and_extent (*expr, &offset, &size, &max_size);
> +      if (!base || size == -1 || max_size == -1)
> +	return false;
> +
> +      if (TREE_CODE (base) == INDIRECT_REF)
> +	base = TREE_OPERAND (base, 0);
> +
> +      base = get_ssa_base_param (base);
> +      if (!base || TREE_CODE (base) == INTEGER_CST)
> +	return false;
> +    }
> +  else if (TREE_CODE (*expr) == INDIRECT_REF)
> +    {
> +      tree tree_size;
> +      base = TREE_OPERAND (*expr, 0);
> +
> +      base = get_ssa_base_param (base);
> +      if (!base || TREE_CODE (base) == INTEGER_CST)
> +	return false;
> +
> +      offset = 0;
> +      tree_size = TYPE_SIZE (TREE_TYPE (base));
> +      if (tree_size && host_integerp (tree_size, 1))
> +	size = max_size = tree_low_cst (tree_size, 1);
> +      else
> +	return false;
> +    }
> +  else
> +    return false;
> +
> +  gcc_assert (DECL_P (base));
> +  for (i = 0; i < len; i++)
> +    {
> +      note = VEC_index (ipa_parm_note_t, notes, i);
> +
> +      if (note->base == base &&
> +	  (note->offset == offset || note->remove_param))
> +	{
> +	  cand = note;
> +	  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)
> +    {
> +      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_rhs2 (stmt)
> +      || TREE_CODE (gimple_assign_rhs1 (stmt)) == CONSTRUCTOR)
> +    return SRA_SA_NONE;
> +
> +  /* The order of processing rhs and lhs is important.  */
> +  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 NOTES.  */
> +
> +static void
> +convert_callers (struct cgraph_node *node, VEC (ipa_parm_note_t, heap) *notes)
> +{
> +  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, "Checking call %s -> %s\n",
> +		 cgraph_node_name (cs->caller),
> +		 cgraph_node_name (cs->callee));
> +
> +      ipa_modify_call_arguments (cs, cs->call_stmt, notes);
> +      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, "Checking recursive call");
> +	      ipa_modify_call_arguments (NULL, stmt, notes);
> +	    }
> +	}
> +    }
> +
> +  return;
> +}
> +
> +/* Perform all the modification required in IPA-SRA for NODE to have parameters
> +   as given in NOTES.  */
> +
> +static void
> +modify_function (struct cgraph_node *node, VEC (ipa_parm_note_t, heap) *notes)
> +{
> +  ipa_modify_formal_parameters (current_function_decl, notes, "ISRA");
> +  scan_function (sra_ipa_modify_expr, sra_ipa_modify_assign,
> +		 replace_removed_params_ssa_names, false, notes);
> +  convert_callers (node, notes);
> +  cgraph_make_node_local (node);
> +  return;
> +}
> +
> +/* Perform early interprocedural SRA.  */
> +
> +static unsigned int
> +ipa_early_sra (void)
> +{
> +  struct cgraph_node *node = cgraph_node (current_function_decl);
> +  VEC (ipa_parm_note_t, heap) *notes;
> +  int ret = 0;
> +
> +  if (!cgraph_node_can_be_local_p (node))
> +    {
> +      if (dump_file)
> +	fprintf (dump_file, "Function not local to this compilation unit.\n");
> +      return 0;
> +    }
> +
> +  if (DECL_VIRTUAL_P (current_function_decl))
> +    {
> +      if (dump_file)
> +	fprintf (dump_file, "Function is a virtual method.\n");
> +      return 0;
> +    }
> +
> +  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 0;
> +    }
> +
> +  if (!node->callers)
> +    {
> +      if (dump_file)
> +	fprintf (dump_file,
> +		 "Function has no callers in this compilation unit.\n");
> +      return 0;
> +    }
> +
> +  sra_initialize ();
> +  sra_mode = SRA_MODE_EARLY_IPA;
> +
> +  find_param_candidates ();
> +  scan_function (build_access_from_expr, build_accesses_from_assign,
> +		 NULL, true, NULL);
> +  if (encountered_va_start)
> +    {
> +      if (dump_file)
> +	fprintf (dump_file, "Function calls va_start().\n\n");
> +      goto out;
> +    }
> +
> +  notes = analyze_all_param_acesses ();
> +  if (!notes)
> +    goto out;
> +  if (dump_file)
> +    ipa_dump_param_notes (dump_file, notes, current_function_decl);
> +
> +  modify_function (node, notes);
> +  VEC_free (ipa_parm_note_t, heap, notes);
> +  ret = TODO_update_ssa;
> +
> + out:
> +  sra_deinitialize ();
> +  return ret;
> +}
> +
> +/* Return if early ipa sra shall be performed.  */
> +static bool
> +ipa_early_sra_gate (void)
> +{
> +  return flag_early_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 */
> + }
> +};
> +
>  
>  struct gimple_opt_pass pass_sra =
>  {
> 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
> @@ -2768,8 +2768,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) \



More information about the Gcc-patches mailing list