diff mbox series

[2/2] Reimplementation of param-manipulation

Message ID 20171108205451.tofv6d52zvd322i3@virgil.suse.cz
State New
Headers show
Series [1/2] Moving parameter manipulation into its own file | expand

Commit Message

Martin Jambor Nov. 8, 2017, 8:54 p.m. UTC
Hi,

this patch is a substantial rewrite of function parameter
manipulations currently used by IPA-SRA and OpenMP SIMD cloning.  I
started it with the aim to cleanup the code but also to make the data
structures as small as possible so that, for real IPA-SRA, I can
attach them to call graph nodes instead of args_to_skip bitmaps.

That is what the ipa_param_adjustments is intended to be, although it
is not attached to a cgraph node just yet.  This class is also capable
of modifying call-sites.  Additionally, there is a new class
ipa_param_body_adjustments for modifying signatures, types and bodies
of callees, which is not expected to live long and so code clarity was
emphasized over data structure compactness.

Furthermore, the interface is cleaner, the user simply describes what
new parameters should look like and does not need to care about
various data fields required to make the changes happen like today.
Also, some functionality like SSA name remapping is now shared among
the two users while now both implement it themselves.

Although I think the patch is a clear improvement, there may be
reasons to wait with committing.  one drawback is that
ipa_param_adjustments et. al. are now in GC memory, which will be
necessary once they are attached to call graph but now it is just
useless.  It is not a lot of memory though.

In any event, this patch also passes bootstrap and testing on
x86_64-linux.

Thanks,

Martin


2017-11-07  Martin Jambor  <mjambor@suse.cz>

	* ipa-param-manipulation.h (IPA_PARAM_PREFIX_SYNTH): New.
	(IPA_PARAM_PREFIX_ISRA): Likewise.
	(IPA_PARAM_PREFIX_SIMD): Likewise.
	(IPA_PARAM_PREFIX_MASK): Likewise.
	(IPA_PARAM_MAX_INDEX_BITS): Likewise.
	(ipa_parm_op): Changed element prefixes to IPA_PARAM_OP, renamed
	IPA_PARM_OP_NONE to IPA_PARAM_OP_UNDEFINED, reordered elements.
	(ipa_parm_adjustment): Removed.
	(ipa_parm_adjustment_vec): Likewise.
	(ipa_modify_formal_parameters): Removed declaration.
	(ipa_modify_call_arguments): Likewise.
	(ipa_combine_adjustments): Likewise.
	(ipa_dump_param_adjustments): Likewise.
	(ipa_modify_expr): Likewise.
	(ipa_get_adjustment_candidate): Likewise.
	(ipa_adjusted_param): Likewise.
	(ipa_param_adjustments): Likewise.
	(ipa_param_body_replacement): Likewise.
	(ipa_param_body_adjustments): Likewise.
	* Makefile.in (GTFILES): Added ipa-param-manipulation.h.
	* ipa-param-manipulation.c: Included tree-eh.h and tree-cfg.h.
	(ipa_modify_formal_parameters): Removed.
	(ipa_modify_call_arguments): Likewise.
	(index_in_adjustments_multiple_times_p): Likewise.
	(ipa_combine_adjustments): Likewise.
	(ipa_get_adjustment_candidate): Likewise.
	(ipa_modify_expr): Likewise.
	(ipa_dump_param_adjustments): Likewise.
	(ipa_param_prefixes): New.
	(ipa_param_adjustments::ipa_param_adjustments): Likewise.
	(ipa_param_adjustments::init): Likewise.
	(ipa_param_adjustments::modify_call_arguments): Likewise.
	(modify_call_arguments): Likewise.
	(ipa_param_op_names): Likewise.
	(ipa_dump_adjusted_parameters): Likewise.
	(ipa_param_body_adjustments::ipa_param_body_adjustments): Likewise.
	(register_replacement): Likewise.
	(modify_formal_parameters): Likewise.
	(lookup_replacement): Likewise.
	(get_expr_replacement): Likewise.
	(get_replacement_ssa_base): Likewise.
	(replace_removed_params_ssa_names): Likewise.
	(modify_expr): Likewise.
	(modify_assignment): Likewise.
	(modify_cfun_body): Likewise.
	(reset_debug_stmts): Likewise.
	(perform_cfun_body_modifications): Likewise.
	* omp-simd-clone.c (simd_clone_adjust_argument_types): Adjusted to
	use new parameter manipulation structures.
	(simd_clone_init_simd_arrays): Likewise.
	(modify_stmt_info): Likewise.
	(ipa_simd_modify_stmt_ops): Likewise.
	(ipa_simd_modify_function_body): Likewise.
	(simd_clone_adjust): Likewise.
	* tree-sra.c (turn_representatives_into_adjustments): Likewise.
	(analyze_all_param_acesses): Likewise.
	(replace_removed_params_ssa_names): Removed.
	(get_adjustment_for_base): Likewise.
	(replace_removed_params_ssa_names): Likewise.
	(sra_ipa_modify_assign): Likewise.
	(ipa_sra_modify_function_body): Likewise.
	(sra_ipa_reset_debug_stmts): Likewise.
	(convert_callers_data): New type.
	(convert_callers_for_node): Adjusted to use new parameter
	manipulation structures.
	(convert_callers): Likewise.
	(modify_function): Likewise.  Flip order of modifying callers and
	callees.
	(ipa_early_sra): Adjusted to use new parameter manipulation
	structures.

	* testsuite/gcc.dg/ipa/ipa-sra-3.c: Modified dump scan.
---
 gcc/Makefile.in                      |    2 +-
 gcc/ipa-param-manipulation.c         | 1283 ++++++++++++++++++++++------------
 gcc/ipa-param-manipulation.h         |  191 +++--
 gcc/omp-simd-clone.c                 |  193 ++---
 gcc/testsuite/gcc.dg/ipa/ipa-sra-3.c |    5 +-
 gcc/tree-sra.c                       |  503 ++-----------
 6 files changed, 1116 insertions(+), 1061 deletions(-)
diff mbox series

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 1bb1d6ec0ff..3751aa3a218 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -2510,7 +2510,7 @@  GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/reload.h $(srcdir)/caller-save.c $(srcdir)/symtab.c \
   $(srcdir)/alias.c $(srcdir)/bitmap.c $(srcdir)/cselib.c $(srcdir)/cgraph.c \
   $(srcdir)/ipa-prop.c $(srcdir)/ipa-cp.c $(srcdir)/ipa-utils.h \
-  $(srcdir)/dbxout.c \
+  $(srcdir)/ipa-param-manipulation.h $(srcdir)/dbxout.c \
   $(srcdir)/signop.h \
   $(srcdir)/dwarf2out.h \
   $(srcdir)/dwarf2asm.c \
diff --git a/gcc/ipa-param-manipulation.c b/gcc/ipa-param-manipulation.c
index bedd201f6dd..1b5e28e5859 100644
--- a/gcc/ipa-param-manipulation.c
+++ b/gcc/ipa-param-manipulation.c
@@ -28,15 +28,23 @@  along with GCC; see the file COPYING3.  If not see
 #include "ssa.h"
 #include "cgraph.h"
 #include "fold-const.h"
+#include "tree-eh.h"
 #include "stor-layout.h"
 #include "gimplify.h"
 #include "gimple-iterator.h"
 #include "gimplify-me.h"
+#include "tree-cfg.h"
 #include "tree-dfa.h"
 #include "ipa-param-manipulation.h"
 #include "print-tree.h"
 #include "gimple-pretty-print.h"
 #include "builtins.h"
+#include "tree-ssa.h"
+
+const char *ipa_param_prefixes[] = {"SYNTH",
+				    "ISRA",
+				    "simd",
+				    "mask"};
 
 /* Return a heap allocated vector containing formal parameters of FNDECL.  */
 
@@ -79,336 +87,236 @@  ipa_get_vector_of_formal_parm_types (tree fntype)
   return types;
 }
 
-/* Modify the function declaration FNDECL and its type according to the plan in
-   ADJUSTMENTS.  It also sets base fields of individual adjustments structures
-   to reflect the actual parameters being modified which are determined by the
-   base_index field.  */
+/* Constructor from NEW_PARAMS showing how new parameters should look like and
+   CUR_PARAMS, which is either known vector of current parameter declarations
+   or NULL if no debug information for removed parameters should or can be
+   recorded.  */
 
-void
-ipa_modify_formal_parameters (tree fndecl, ipa_parm_adjustment_vec adjustments)
+ipa_param_adjustments
+::ipa_param_adjustments (vec<ipa_adjusted_param, va_gc> *new_params,
+			 vec<tree> *cur_params)
+  : m_adj_params (new_params), m_vanishing_decls (NULL),
+    m_vanishing_indices (NULL)
 {
-  vec<tree> oparms = ipa_get_vector_of_formal_parms (fndecl);
-  tree orig_type = TREE_TYPE (fndecl);
-  tree old_arg_types = TYPE_ARG_TYPES (orig_type);
-
-  /* The following test is an ugly hack, some functions simply don't have any
-     arguments in their type.  This is probably a bug but well... */
-  bool care_for_types = (old_arg_types != NULL_TREE);
-  bool last_parm_void;
-  vec<tree> otypes;
-  if (care_for_types)
-    {
-      last_parm_void = (TREE_VALUE (tree_last (old_arg_types))
-			== void_type_node);
-      otypes = ipa_get_vector_of_formal_parm_types (orig_type);
-      if (last_parm_void)
-	gcc_assert (oparms.length () + 1 == otypes.length ());
-      else
-	gcc_assert (oparms.length () == otypes.length ());
-    }
-  else
-    {
-      last_parm_void = false;
-      otypes.create (0);
-    }
-
-  int len = adjustments.length ();
-  tree *link = &DECL_ARGUMENTS (fndecl);
-  tree new_arg_types = NULL;
-  for (int i = 0; i < len; i++)
-    {
-      struct ipa_parm_adjustment *adj;
-      gcc_assert (link);
-
-      adj = &adjustments[i];
-      tree parm;
-      if (adj->op == IPA_PARM_OP_NEW)
-	parm = NULL;
-      else
-	parm = oparms[adj->base_index];
-      adj->base = parm;
-
-      if (adj->op == IPA_PARM_OP_COPY)
-	{
-	  if (care_for_types)
-	    new_arg_types = tree_cons (NULL_TREE, otypes[adj->base_index],
-				       new_arg_types);
-	  *link = parm;
-	  link = &DECL_CHAIN (parm);
-	}
-      else if (adj->op != IPA_PARM_OP_REMOVE)
-	{
-	  tree new_parm;
-	  tree ptype;
-
-	  if (adj->by_ref)
-	    ptype = build_pointer_type (adj->type);
-	  else
-	    {
-	      ptype = adj->type;
-	      if (is_gimple_reg_type (ptype)
-		  && TYPE_MODE (ptype) != BLKmode)
-		{
-		  unsigned malign = GET_MODE_ALIGNMENT (TYPE_MODE (ptype));
-		  if (TYPE_ALIGN (ptype) != malign)
-		    ptype = build_aligned_type (ptype, malign);
-		}
-	    }
-
-	  if (care_for_types)
-	    new_arg_types = tree_cons (NULL_TREE, ptype, new_arg_types);
-
-	  new_parm = build_decl (UNKNOWN_LOCATION, PARM_DECL, NULL_TREE,
-				 ptype);
-	  const char *prefix = adj->arg_prefix ? adj->arg_prefix : "SYNTH";
-	  DECL_NAME (new_parm) = create_tmp_var_name (prefix);
-	  DECL_ARTIFICIAL (new_parm) = 1;
-	  DECL_ARG_TYPE (new_parm) = ptype;
-	  DECL_CONTEXT (new_parm) = fndecl;
-	  TREE_USED (new_parm) = 1;
-	  DECL_IGNORED_P (new_parm) = 1;
-	  layout_decl (new_parm, 0);
+  if (cur_params)
+    init (cur_params);
+}
 
-	  if (adj->op == IPA_PARM_OP_NEW)
-	    adj->base = NULL;
-	  else
-	    adj->base = parm;
-	  adj->new_decl = new_parm;
+/* Constructor from NEW_PARAMS showing how new parameters should look like
+   and a FNDECL of the function they should be applied to.  */
 
-	  *link = new_parm;
-	  link = &DECL_CHAIN (new_parm);
-	}
-    }
+ipa_param_adjustments
+::ipa_param_adjustments (vec<ipa_adjusted_param, va_gc> *new_params,
+			 tree fndecl)
+  : m_adj_params (new_params), m_vanishing_decls (NULL),
+    m_vanishing_indices (NULL)
+{
+  vec<tree> cur_params = ipa_get_vector_of_formal_parms (fndecl);
+  init (&cur_params);
+  cur_params.release ();
+}
 
-  *link = NULL_TREE;
 
-  tree new_reversed = NULL;
-  if (care_for_types)
-    {
-      new_reversed = nreverse (new_arg_types);
-      if (last_parm_void)
-	{
-	  if (new_reversed)
-	    TREE_CHAIN (new_arg_types) = void_list_node;
-	  else
-	    new_reversed = void_list_node;
-	}
-    }
+/* Initialize m_vanishing_decls and m_vanishing_decls based on m_adj_params and
+   CUR_PARAMS.  */
 
-  /* Use copy_node to preserve as much as possible from original type
-     (debug info, attribute lists etc.)
-     Exception is METHOD_TYPEs must have THIS argument.
-     When we are asked to remove it, we need to build new FUNCTION_TYPE
-     instead.  */
-  tree new_type = NULL;
-  if (TREE_CODE (orig_type) != METHOD_TYPE
-       || (adjustments[0].op == IPA_PARM_OP_COPY
-	  && adjustments[0].base_index == 0))
-    {
-      new_type = build_distinct_type_copy (orig_type);
-      TYPE_ARG_TYPES (new_type) = new_reversed;
-    }
-  else
-    {
-      new_type
-        = build_distinct_type_copy (build_function_type (TREE_TYPE (orig_type),
-							 new_reversed));
-      TYPE_CONTEXT (new_type) = TYPE_CONTEXT (orig_type);
-      DECL_VINDEX (fndecl) = NULL_TREE;
-    }
-
-  /* When signature changes, we need to clear builtin info.  */
-  if (DECL_BUILT_IN (fndecl))
+void
+ipa_param_adjustments::init (vec<tree> *cur_params)
+{
+  unsigned cp_len = cur_params ? cur_params->length() : 0;
+  auto_vec<bool, 16> kept (cp_len);
+  kept.quick_grow_cleared (cp_len);
+  unsigned adj_len = vec_safe_length (m_adj_params);
+  for (unsigned i = 0; i < adj_len ; i++)
     {
-      DECL_BUILT_IN_CLASS (fndecl) = NOT_BUILT_IN;
-      DECL_FUNCTION_CODE (fndecl) = (enum built_in_function) 0;
+      ipa_adjusted_param *apm = &(*m_adj_params)[i];
+      gcc_assert (apm->op != IPA_PARAM_OP_UNDEFINED);
+      if (apm->op == IPA_PARAM_OP_COPY)
+	kept[apm->base_index] = true;
     }
-
-  TREE_TYPE (fndecl) = new_type;
-  DECL_VIRTUAL_P (fndecl) = 0;
-  DECL_LANG_SPECIFIC (fndecl) = NULL;
-  otypes.release ();
-  oparms.release ();
+  for (unsigned i = 0; i < cp_len; i++)
+    if (!kept[i])
+      {
+	vec_safe_push (m_vanishing_decls, (*cur_params)[i]);
+	vec_safe_push (m_vanishing_indices, i);
+      }
 }
 
-/* Modify actual arguments of a function call CS as indicated in ADJUSTMENTS.
-   If this is a directly recursive call, CS must be NULL.  Otherwise it must
-   contain the corresponding call graph edge.  */
+/* Modify actual arguments of a function call in statement STMT, assuming it
+   calls CALLEE_DECL.  Return the new statement that replaced the old one.  */
 
-void
-ipa_modify_call_arguments (struct cgraph_edge *cs, gcall *stmt,
-			   ipa_parm_adjustment_vec adjustments)
+gcall *
+ipa_param_adjustments::modify_call_arguments (gcall *stmt, tree callee_decl)
 {
-  struct cgraph_node *current_node = cgraph_node::get (current_function_decl);
-  vec<tree> vargs;
-  vec<tree, va_gc> **debug_args = NULL;
-  gcall *new_stmt;
-  gimple_stmt_iterator gsi, prev_gsi;
-  tree callee_decl;
-  int i, len;
+  unsigned len = vec_safe_length (m_adj_params);
+  auto_vec<tree, 16> vargs (len);
 
-  len = adjustments.length ();
-  vargs.create (len);
-  callee_decl = !cs ? gimple_call_fndecl (stmt) : cs->callee->decl;
+  cgraph_node *current_node = cgraph_node::get (current_function_decl);
   current_node->remove_stmt_references (stmt);
 
-  gsi = gsi_for_stmt (stmt);
-  prev_gsi = gsi;
+  gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
+  gimple_stmt_iterator prev_gsi = gsi;
   gsi_prev (&prev_gsi);
-  for (i = 0; i < len; i++)
+  for (unsigned i = 0; i < len; i++)
     {
-      struct ipa_parm_adjustment *adj;
+      ipa_adjusted_param *apm = &(*m_adj_params)[i];
 
-      adj = &adjustments[i];
+      gcc_assert (apm->op != IPA_PARAM_OP_UNDEFINED);
+      /* Any transformation that introduces unspecified new parameters needs
+	 to transform actual arguments itself.  */
+      gcc_assert (apm->op != IPA_PARAM_OP_NEW);
 
-      if (adj->op == IPA_PARM_OP_COPY)
+      if (apm->op == IPA_PARAM_OP_COPY)
 	{
-	  tree arg = gimple_call_arg (stmt, adj->base_index);
+	  tree arg = gimple_call_arg (stmt, apm->base_index);
 
 	  vargs.quick_push (arg);
+	  continue;
 	}
-      else if (adj->op != IPA_PARM_OP_REMOVE)
+      gcc_assert (apm->op == IPA_PARAM_OP_SPLIT);
+
+      /* We create a new parameter out of the value of the old one, we can
+	 do the following kind of transformations:
+
+	 - A scalar passed by reference is converted to a scalar passed by
+	 value.  (apm->by_ref is false and the type of the original
+	 actual argument is a pointer to a scalar).
+
+	 - A part of an aggregate is passed instead of the whole aggregate.
+	 The part can be passed either by value or by reference, this is
+	 determined by value of apm->by_ref.  Moreover, the code below
+	 handles both situations when the original aggregate is passed by
+	 value (its type is not a pointer) and when it is passed by
+	 reference (it is a pointer to an aggregate).
+
+	 When the new argument is passed by reference (apm->by_ref is true)
+	 it must be a part of an aggregate and therefore we form it by
+	 simply taking the address of a reference inside the original
+	 aggregate.  */
+
+      tree base = gimple_call_arg (stmt, apm->base_index);
+      location_t loc = DECL_P (base) ? DECL_SOURCE_LOCATION (base)
+	: EXPR_LOCATION (base);
+
+      tree off;
+      bool deref_base = false;
+      unsigned int deref_align = 0;
+      if (TREE_CODE (base) != ADDR_EXPR && POINTER_TYPE_P (TREE_TYPE (base)))
+	off = build_int_cst (apm->alias_ptr_type, apm->unit_offset);
+      else
 	{
-	  tree expr, base, off;
-	  location_t loc;
-	  unsigned int deref_align = 0;
-	  bool deref_base = false;
-
-	  /* We create a new parameter out of the value of the old one, we can
-	     do the following kind of transformations:
-
-	     - A scalar passed by reference is converted to a scalar passed by
-               value.  (adj->by_ref is false and the type of the original
-               actual argument is a pointer to a scalar).
-
-             - A part of an aggregate is passed instead of the whole aggregate.
-               The part can be passed either by value or by reference, this is
-               determined by value of adj->by_ref.  Moreover, the code below
-               handles both situations when the original aggregate is passed by
-               value (its type is not a pointer) and when it is passed by
-               reference (it is a pointer to an aggregate).
-
-	     When the new argument is passed by reference (adj->by_ref is true)
-	     it must be a part of an aggregate and therefore we form it by
-	     simply taking the address of a reference inside the original
-	     aggregate.  */
-
-	  gcc_checking_assert (adj->offset % BITS_PER_UNIT == 0);
-	  base = gimple_call_arg (stmt, adj->base_index);
-	  loc = DECL_P (base) ? DECL_SOURCE_LOCATION (base)
-			      : EXPR_LOCATION (base);
-
-	  if (TREE_CODE (base) != ADDR_EXPR
-	      && POINTER_TYPE_P (TREE_TYPE (base)))
-	    off = build_int_cst (adj->alias_ptr_type,
-				 adj->offset / BITS_PER_UNIT);
-	  else
+	  bool addrof;
+	  if (TREE_CODE (base) == ADDR_EXPR)
 	    {
-	      HOST_WIDE_INT base_offset;
-	      tree prev_base;
-	      bool addrof;
+	      base = TREE_OPERAND (base, 0);
+	      addrof = true;
+	    }
+	  else
+	    addrof = false;
 
-	      if (TREE_CODE (base) == ADDR_EXPR)
-		{
-		  base = TREE_OPERAND (base, 0);
-		  addrof = true;
-		}
-	      else
-		addrof = false;
-	      prev_base = base;
-	      base = get_addr_base_and_unit_offset (base, &base_offset);
-	      /* Aggregate arguments can have non-invariant addresses.  */
-	      if (!base)
-		{
-		  base = build_fold_addr_expr (prev_base);
-		  off = build_int_cst (adj->alias_ptr_type,
-				       adj->offset / BITS_PER_UNIT);
-		}
-	      else if (TREE_CODE (base) == MEM_REF)
-		{
-		  if (!addrof)
-		    {
-		      deref_base = true;
-		      deref_align = TYPE_ALIGN (TREE_TYPE (base));
-		    }
-		  off = build_int_cst (adj->alias_ptr_type,
-				       base_offset
-				       + adj->offset / BITS_PER_UNIT);
-		  off = int_const_binop (PLUS_EXPR, TREE_OPERAND (base, 1),
-					 off);
-		  base = TREE_OPERAND (base, 0);
-		}
-	      else
+	  tree prev_base = base;
+	  HOST_WIDE_INT base_offset;
+	  base = get_addr_base_and_unit_offset (base, &base_offset);
+
+	  /* Aggregate arguments can have non-invariant addresses.  */
+	  if (!base)
+	    {
+	      base = build_fold_addr_expr (prev_base);
+	      off = build_int_cst (apm->alias_ptr_type, apm->unit_offset);
+	    }
+	  else if (TREE_CODE (base) == MEM_REF)
+	    {
+	      if (!addrof)
 		{
-		  off = build_int_cst (adj->alias_ptr_type,
-				       base_offset
-				       + adj->offset / BITS_PER_UNIT);
-		  base = build_fold_addr_expr (base);
+		  deref_base = true;
+		  deref_align = TYPE_ALIGN (TREE_TYPE (base));
 		}
+	      off = build_int_cst (apm->alias_ptr_type,
+				   base_offset + apm->unit_offset);
+	      off = int_const_binop (PLUS_EXPR, TREE_OPERAND (base, 1),
+				     off);
+	      base = TREE_OPERAND (base, 0);
 	    }
-
-	  if (!adj->by_ref)
+	  else
 	    {
-	      tree type = adj->type;
-	      unsigned int align;
-	      unsigned HOST_WIDE_INT misalign;
+	      off = build_int_cst (apm->alias_ptr_type,
+				   base_offset + apm->unit_offset);
+	      base = build_fold_addr_expr (base);
+	    }
+	}
 
-	      if (deref_base)
-		{
-		  align = deref_align;
-		  misalign = 0;
-		}
-	      else
-		{
-		  get_pointer_alignment_1 (base, &align, &misalign);
-		  if (TYPE_ALIGN (type) > align)
-		    align = TYPE_ALIGN (type);
-		}
-	      misalign += (offset_int::from (wi::to_wide (off),
-					     SIGNED).to_short_addr ()
-			   * BITS_PER_UNIT);
-	      misalign = misalign & (align - 1);
-	      if (misalign != 0)
-		align = least_bit_hwi (misalign);
-	      if (align < TYPE_ALIGN (type))
-		type = build_aligned_type (type, align);
-	      base = force_gimple_operand_gsi (&gsi, base,
-					       true, NULL, true, GSI_SAME_STMT);
-	      expr = fold_build2_loc (loc, MEM_REF, type, base, off);
-	      REF_REVERSE_STORAGE_ORDER (expr) = adj->reverse;
-	      /* If expr is not a valid gimple call argument emit
-	         a load into a temporary.  */
-	      if (is_gimple_reg_type (TREE_TYPE (expr)))
-		{
-		  gimple *tem = gimple_build_assign (NULL_TREE, expr);
-		  if (gimple_in_ssa_p (cfun))
-		    {
-		      gimple_set_vuse (tem, gimple_vuse (stmt));
-		      expr = make_ssa_name (TREE_TYPE (expr), tem);
-		    }
-		  else
-		    expr = create_tmp_reg (TREE_TYPE (expr));
-		  gimple_assign_set_lhs (tem, expr);
-		  gsi_insert_before (&gsi, tem, GSI_SAME_STMT);
-		}
+      tree expr;
+      if (!apm->by_ref)
+	{
+	  tree type = apm->type;
+	  unsigned int align;
+	  unsigned HOST_WIDE_INT misalign;
+
+	  if (deref_base)
+	    {
+	      align = deref_align;
+	      misalign = 0;
 	    }
 	  else
 	    {
-	      expr = fold_build2_loc (loc, MEM_REF, adj->type, base, off);
-	      REF_REVERSE_STORAGE_ORDER (expr) = adj->reverse;
-	      expr = build_fold_addr_expr (expr);
-	      expr = force_gimple_operand_gsi (&gsi, expr,
-					       true, NULL, true, GSI_SAME_STMT);
+	      get_pointer_alignment_1 (base, &align, &misalign);
+	      /* All users must make sure that we can be optimistic when it
+		 comes to alignment in this case (by inspecting the final users
+		 of these new parameters).  */
+	      if (TYPE_ALIGN (type) > align)
+		align = TYPE_ALIGN (type);
+	    }
+	  misalign += (offset_int::from (wi::to_wide (off),
+					 SIGNED).to_short_addr ()
+		       * BITS_PER_UNIT);
+	  misalign = misalign & (align - 1);
+	  if (misalign != 0)
+	    align = least_bit_hwi (misalign);
+	  if (align < TYPE_ALIGN (type))
+	    type = build_aligned_type (type, align);
+	  base = force_gimple_operand_gsi (&gsi, base,
+					   true, NULL, true, GSI_SAME_STMT);
+	  expr = fold_build2_loc (loc, MEM_REF, type, base, off);
+	  REF_REVERSE_STORAGE_ORDER (expr) = apm->reverse;
+	  /* If expr is not a valid gimple call argument emit
+	     a load into a temporary.  */
+	  if (is_gimple_reg_type (TREE_TYPE (expr)))
+	    {
+	      gimple *tem = gimple_build_assign (NULL_TREE, expr);
+	      if (gimple_in_ssa_p (cfun))
+		{
+		  gimple_set_vuse (tem, gimple_vuse (stmt));
+		  expr = make_ssa_name (TREE_TYPE (expr), tem);
+		}
+	      else
+		expr = create_tmp_reg (TREE_TYPE (expr));
+	      gimple_assign_set_lhs (tem, expr);
+	      gsi_insert_before (&gsi, tem, GSI_SAME_STMT);
 	    }
-	  vargs.quick_push (expr);
 	}
-      if (adj->op != IPA_PARM_OP_COPY && MAY_HAVE_DEBUG_STMTS)
+      else
 	{
-	  unsigned int ix;
-	  tree ddecl = NULL_TREE, origin = DECL_ORIGIN (adj->base), arg;
-	  gimple *def_temp;
+	  expr = fold_build2_loc (loc, MEM_REF, apm->type, base, off);
+	  REF_REVERSE_STORAGE_ORDER (expr) = apm->reverse;
+	  expr = build_fold_addr_expr (expr);
+	  expr = force_gimple_operand_gsi (&gsi, expr,
+					   true, NULL, true, GSI_SAME_STMT);
+	}
+      vargs.quick_push (expr);
+    }
+
+  if (callee_decl && MAY_HAVE_DEBUG_STMTS && m_vanishing_decls)
+    {
+      vec<tree, va_gc> **debug_args = NULL;
+      unsigned van_len = vec_safe_length (m_vanishing_decls);
+      for (unsigned i = 0; i < van_len; i++)
+	{
+	  tree vanished_decl = (*m_vanishing_decls)[i];
+	  if (!vanished_decl)
+	    continue;
+	  tree origin = DECL_ORIGIN (vanished_decl);
+	  tree arg = gimple_call_arg (stmt, (*m_vanishing_indices)[i]);
 
-	  arg = gimple_call_arg (stmt, adj->base_index);
 	  if (!useless_type_conversion_p (TREE_TYPE (origin), TREE_TYPE (arg)))
 	    {
 	      if (!fold_convertible_p (TREE_TYPE (origin), arg))
@@ -418,6 +326,8 @@  ipa_modify_call_arguments (struct cgraph_edge *cs, gcall *stmt,
 	    }
 	  if (debug_args == NULL)
 	    debug_args = decl_debug_args_insert (callee_decl);
+	  unsigned int ix;
+	  tree ddecl = NULL_TREE;
 	  for (ix = 0; vec_safe_iterate (*debug_args, ix, &ddecl); ix += 2)
 	    if (ddecl == origin)
 	      {
@@ -434,7 +344,8 @@  ipa_modify_call_arguments (struct cgraph_edge *cs, gcall *stmt,
 	      vec_safe_push (*debug_args, origin);
 	      vec_safe_push (*debug_args, ddecl);
 	    }
-	  def_temp = gimple_build_debug_bind (ddecl, unshare_expr (arg), stmt);
+	  gimple *def_temp = gimple_build_debug_bind (ddecl,
+						      unshare_expr (arg), stmt);
 	  gsi_insert_before (&gsi, def_temp, GSI_SAME_STMT);
 	}
     }
@@ -445,8 +356,8 @@  ipa_modify_call_arguments (struct cgraph_edge *cs, gcall *stmt,
       print_gimple_stmt (dump_file, gsi_stmt (gsi), 0);
     }
 
-  new_stmt = gimple_build_call_vec (callee_decl, vargs);
-  vargs.release ();
+  gcall *new_stmt = gimple_build_call_vec (callee_decl, vargs);
+
   if (gimple_call_lhs (stmt))
     gimple_call_set_lhs (new_stmt, gimple_call_lhs (stmt));
 
@@ -472,120 +383,264 @@  ipa_modify_call_arguments (struct cgraph_edge *cs, gcall *stmt,
       fprintf (dump_file, "\n");
     }
   gsi_replace (&gsi, new_stmt, true);
-  if (cs)
-    cs->set_call_stmt (new_stmt);
   do
     {
       current_node->record_stmt_references (gsi_stmt (gsi));
       gsi_prev (&gsi);
     }
   while (gsi_stmt (gsi) != gsi_stmt (prev_gsi));
+  return new_stmt;
 }
 
-/* Return true iff BASE_INDEX is in ADJUSTMENTS more than once.  */
+void
+ipa_param_adjustments::modify_call_arguments (struct cgraph_edge *cs)
+{
+  gcall *old_call = cs->call_stmt;
+  tree callee_decl = cs->callee ? cs->callee->decl : NULL;
+  /* TODO: Optionally set current_function_decl */
+  gcall *new_call = modify_call_arguments (old_call, callee_decl);
+  cs->set_call_stmt (new_call);
+}
+
+/* Names of parameters for dumping.  Keep in sync with enum ipa_parm_op.  */
+
+static const char *ipa_param_op_names[] = {"IPA_PARAM_OP_UNDEFINED",
+					   "IPA_PARAM_OP_COPY",
+					   "IPA_PARAM_OP_NEW",
+					   "IPA_PARAM_OP_SPLIT"};
+
+/* Dump the adjustments in the vector ADJUSTMENTS to dump_file in a human
+   friendly way, assuming they are meant to be applied to FNDECL.  */
 
-static bool
-index_in_adjustments_multiple_times_p (int base_index,
-				       ipa_parm_adjustment_vec adjustments)
+void
+ipa_dump_adjusted_parameters (FILE *f,
+			      vec<ipa_adjusted_param, va_gc> *adj_params)
 {
-  int i, len = adjustments.length ();
-  bool one = false;
+  unsigned i, len = vec_safe_length (adj_params);
+  bool first = true;
 
+  fprintf (f, "IPA adjusted parameters: ");
   for (i = 0; i < len; i++)
     {
-      struct ipa_parm_adjustment *adj;
-      adj = &adjustments[i];
+      struct ipa_adjusted_param *apm;
+      apm = &(*adj_params)[i];
+
+      if (!first)
+	fprintf (f, "                         ");
+      else
+	first = false;
 
-      if (adj->base_index == base_index)
+      fprintf (f, "%i. %s", i, ipa_param_op_names[apm->op]);
+      switch (apm->op)
 	{
-	  if (one)
-	    return true;
-	  else
-	    one = true;
+	case IPA_PARAM_OP_UNDEFINED:
+	  break;
+
+	case IPA_PARAM_OP_COPY:
+	  fprintf (f, ", base_index: %u", apm->base_index);
+	  break;
+
+	case IPA_PARAM_OP_SPLIT:
+	  fprintf (f, ", offset: %u", apm->unit_offset);
+	  /* fall-through */
+	case IPA_PARAM_OP_NEW:
+	  fprintf (f, ", base_index: %u", apm->base_index);
+	  print_node_brief (f, ", type: ", apm->type, 0);
+	  print_node_brief (f, ", type: ", apm->alias_ptr_type, 0);
+	  fprintf (f, " prefix: %s, reverse: %u, by_ref: %u",
+		   ipa_param_prefixes[apm->param_prefix_index],
+		   apm->reverse, apm->by_ref);
+	  break;
 	}
+      fprintf (f, "\n");
     }
-  return false;
 }
 
-/* Return adjustments that should have the same effect on function parameters
-   and call arguments as if they were first changed according to adjustments in
-   INNER and then by adjustments in OUTER.  */
 
-ipa_parm_adjustment_vec
-ipa_combine_adjustments (ipa_parm_adjustment_vec inner,
-			 ipa_parm_adjustment_vec outer)
+/* Constructor of ipa_param_body_adjustments performing all necessary
+   initializations.  */
+
+ipa_param_body_adjustments
+::ipa_param_body_adjustments (vec<ipa_adjusted_param, va_gc> *adj_params,
+			      tree fndecl)
+  : m_adj_params (adj_params), m_fndecl (fndecl), m_oparms (),
+    m_replacements (), m_new_decls (vec_safe_length (adj_params)),
+    m_new_types (vec_safe_length (adj_params)), m_reset_debug_decls (),
+    m_removed_decls (), m_removed_map (), m_method2func ()
 {
-  int i, outlen = outer.length ();
-  int inlen = inner.length ();
-  int removals = 0;
-  ipa_parm_adjustment_vec adjustments, tmp;
+  for (tree parm = DECL_ARGUMENTS (fndecl); parm; parm = DECL_CHAIN (parm))
+    m_oparms.safe_push (parm);
 
-  tmp.create (inlen);
-  for (i = 0; i < inlen; i++)
-    {
-      struct ipa_parm_adjustment *n;
-      n = &inner[i];
+  bool care_for_types = (TYPE_ARG_TYPES (TREE_TYPE (m_fndecl)) != NULL_TREE);
+  vec<tree> otypes;
+  if (care_for_types)
+    otypes = ipa_get_vector_of_formal_parm_types (TREE_TYPE (m_fndecl));
+  else
+    otypes = vNULL;
 
-      if (n->op == IPA_PARM_OP_REMOVE)
-	removals++;
-      else
-	{
-	  /* FIXME: Handling of new arguments are not implemented yet.  */
-	  gcc_assert (n->op != IPA_PARM_OP_NEW);
-	  tmp.quick_push (*n);
-	}
-    }
+  auto_vec<bool, 16> kept (m_oparms.length ());
+  kept.quick_grow_cleared (m_oparms.length ());
+  m_method2func = (TREE_CODE (TREE_TYPE (m_fndecl)) == METHOD_TYPE);
 
-  adjustments.create (outlen + removals);
-  for (i = 0; i < outlen; i++)
+  unsigned adj_len = vec_safe_length (m_adj_params);
+  for (unsigned i = 0; i < adj_len ; i++)
     {
-      struct ipa_parm_adjustment r;
-      struct ipa_parm_adjustment *out = &outer[i];
-      struct ipa_parm_adjustment *in = &tmp[out->base_index];
-
-      memset (&r, 0, sizeof (r));
-      gcc_assert (in->op != IPA_PARM_OP_REMOVE);
-      if (out->op == IPA_PARM_OP_REMOVE)
+      ipa_adjusted_param *apm = &(*m_adj_params)[i];
+      tree new_parm, new_type;
+      if (apm->op == IPA_PARAM_OP_COPY)
+	{
+	  kept[apm->base_index] = true;
+	  if (care_for_types)
+	    new_type = otypes[apm->base_index];
+	  else
+	    new_type = NULL;
+	  new_parm = m_oparms[apm->base_index];
+	  if (apm->base_index == 0)
+	    m_method2func = false;
+	}
+      else if (apm->op == IPA_PARAM_OP_NEW
+	  || apm->op == IPA_PARAM_OP_SPLIT)
 	{
-	  if (!index_in_adjustments_multiple_times_p (in->base_index, tmp))
+	  if (apm->by_ref)
 	    {
-	      r.op = IPA_PARM_OP_REMOVE;
-	      adjustments.quick_push (r);
+	      new_type = build_pointer_type (apm->type);
+	      if (is_gimple_reg_type (new_type)
+		  && TYPE_MODE (new_type) != BLKmode)
+		{
+		  unsigned malign = GET_MODE_ALIGNMENT (TYPE_MODE (new_type));
+		  if (TYPE_ALIGN (new_type) != malign)
+		    new_type = build_aligned_type (new_type, malign);
+		}
 	    }
-	  continue;
-	}
+	  else
+	    new_type = apm->type;
+	  new_parm = build_decl (UNKNOWN_LOCATION, PARM_DECL, NULL_TREE,
+				 new_type);
+	  const char *prefix = ipa_param_prefixes[apm->param_prefix_index];
+	  DECL_NAME (new_parm) = create_tmp_var_name (prefix);
+	  DECL_ARTIFICIAL (new_parm) = 1;
+	  DECL_ARG_TYPE (new_parm) = new_type;
+	  DECL_CONTEXT (new_parm) = m_fndecl;
+	  TREE_USED (new_parm) = 1;
+	  DECL_IGNORED_P (new_parm) = 1;
+	  layout_decl (new_parm, 0);
+
+	  if (apm->op == IPA_PARAM_OP_SPLIT)
+	    register_replacement (apm, new_parm);
+        }
       else
-	{
-	  /* FIXME: Handling of new arguments are not implemented yet.  */
-	  gcc_assert (out->op != IPA_PARM_OP_NEW);
-	}
+	gcc_unreachable ();
+      m_new_decls.quick_push (new_parm);
+      if (care_for_types)
+	m_new_types.quick_push (new_type);
+    }
 
-      r.base_index = in->base_index;
-      r.type = out->type;
+  unsigned op_len = m_oparms.length ();
+  for (unsigned i = 0; i < op_len; i++)
+    if (!kept[i])
+      {
+	if (is_gimple_reg (m_oparms[i]))
+	  m_reset_debug_decls.safe_push (m_oparms[i]);
+	m_removed_decls.safe_push(m_oparms[i]);
+	m_removed_map.put (m_oparms[i], m_removed_decls.length () - 1);
+      }
 
-      /* FIXME:  Create nonlocal value too.  */
+  otypes.release ();
+}
 
-      if (in->op == IPA_PARM_OP_COPY && out->op == IPA_PARM_OP_COPY)
-	r.op = IPA_PARM_OP_COPY;
-      else if (in->op == IPA_PARM_OP_COPY)
-	r.offset = out->offset;
-      else if (out->op == IPA_PARM_OP_COPY)
-	r.offset = in->offset;
-      else
-	r.offset = in->offset + out->offset;
-      adjustments.quick_push (r);
+/* Register that REPLACEMENT should replace parameter described in APM.  */
+
+void
+ipa_param_body_adjustments::register_replacement (ipa_adjusted_param *apm,
+						  tree replacement)
+{
+  gcc_checking_assert (apm->op == IPA_PARAM_OP_SPLIT
+		       || apm->op == IPA_PARAM_OP_NEW);
+  ipa_param_body_replacement psr;
+  psr.base = m_oparms[apm->base_index];
+  psr.unit_offset = apm->unit_offset;
+  psr.repl = replacement;
+  psr.by_ref = apm->by_ref;
+  psr.reverse = apm->reverse;
+  m_replacements.safe_push (psr);
+}
+
+/* Modify the function declaration FNDECL and its type according to the plan in
+   ADJUSTMENTS.  It also sets base fields of individual adjustments structures
+   to reflect the actual parameters being modified which are determined by the
+   base_index field.  */
+
+void
+ipa_param_body_adjustments::modify_formal_parameters ()
+{
+  tree orig_type = TREE_TYPE (m_fndecl);
+  tree old_arg_types = TYPE_ARG_TYPES (orig_type);
+
+  bool care_for_types = (old_arg_types != NULL_TREE);
+  bool last_parm_void = care_for_types
+    && (TREE_VALUE (tree_last (old_arg_types)) == void_type_node);;
+
+  unsigned len = vec_safe_length (m_adj_params);
+  gcc_assert (len == m_new_decls.length());
+  gcc_assert (len == m_new_types.length() || !care_for_types);
+  tree *link = &DECL_ARGUMENTS (m_fndecl);
+  tree new_arg_types = NULL;
+
+  for (unsigned i = 0; i < len; i++)
+    {
+      tree new_decl = m_new_decls[i];
+
+      if (care_for_types)
+	new_arg_types = tree_cons (NULL_TREE, m_new_types[i], new_arg_types);
+      *link = new_decl;
+      link = &DECL_CHAIN (new_decl);
     }
+  *link = NULL_TREE;
 
-  for (i = 0; i < inlen; i++)
+  tree new_reversed;
+  if (care_for_types)
+    {
+      new_reversed = nreverse (new_arg_types);
+      if (last_parm_void)
+	{
+	  if (new_reversed)
+	    TREE_CHAIN (new_arg_types) = void_list_node;
+	  else
+	    new_reversed = void_list_node;
+	}
+    }
+  else
+    new_reversed = NULL;
+
+  /* Use copy_node to preserve as much as possible from original type
+     (debug info, attribute lists etc.)
+     Exception is METHOD_TYPEs which must have THIS argument and when we are
+     asked to remove it, we need to build new FUNCTION_TYPE instead.  */
+  tree new_type = NULL;
+  if (m_method2func)
+    {
+      new_type
+        = build_distinct_type_copy (build_function_type (TREE_TYPE (orig_type),
+							 new_reversed));
+      TYPE_CONTEXT (new_type) = TYPE_CONTEXT (orig_type);
+      DECL_VINDEX (m_fndecl) = NULL_TREE;
+    }
+  else
     {
-      struct ipa_parm_adjustment *n = &inner[i];
+      new_type = build_distinct_type_copy (orig_type);
+      TYPE_ARG_TYPES (new_type) = new_reversed;
+    }
 
-      if (n->op == IPA_PARM_OP_REMOVE)
-	adjustments.quick_push (*n);
+  /* When signature changes, we need to clear builtin info.  */
+  if (DECL_BUILT_IN (m_fndecl))
+    {
+      DECL_BUILT_IN_CLASS (m_fndecl) = NOT_BUILT_IN;
+      DECL_FUNCTION_CODE (m_fndecl) = (enum built_in_function) 0;
     }
 
-  tmp.release ();
-  return adjustments;
+  TREE_TYPE (m_fndecl) = new_type;
+  DECL_VIRTUAL_P (m_fndecl) = 0;
+  DECL_LANG_SPECIFIC (m_fndecl) = NULL;
 }
 
 /* If T is an SSA_NAME, return NULL if it is not a default def or
@@ -606,162 +661,460 @@  get_ssa_base_param (tree t, bool ignore_default_def)
   return t;
 }
 
-/* Given an expression, return an adjustment entry specifying the
-   transformation to be done on EXPR.  If no suitable adjustment entry
-   was found, returns NULL.
+/* Given BASE and UNIT_OFFSET, find the corresponding record among replacement
+   structures.  */
 
-   If IGNORE_DEFAULT_DEF is set, consider SSA_NAMEs which are not a
-   default def, otherwise bail on them.
-
-   If CONVERT is non-NULL, this function will set *CONVERT if the
-   expression provided is a component reference.  ADJUSTMENTS is the
-   adjustments vector.  */
-
-ipa_parm_adjustment *
-ipa_get_adjustment_candidate (tree **expr, bool *convert,
-			      ipa_parm_adjustment_vec adjustments,
-			      bool ignore_default_def)
+ipa_param_body_replacement *
+ipa_param_body_adjustments::lookup_replacement (tree base, unsigned unit_offset)
 {
-  if (TREE_CODE (**expr) == BIT_FIELD_REF
-      || TREE_CODE (**expr) == IMAGPART_EXPR
-      || TREE_CODE (**expr) == REALPART_EXPR)
+  unsigned int len = m_replacements.length ();
+  for (unsigned i = 0; i < len; i++)
     {
-      *expr = &TREE_OPERAND (**expr, 0);
-      if (convert)
-	*convert = true;
+      ipa_param_body_replacement *pbr = &m_replacements[i];
+
+      if (pbr->base == base
+	  && (pbr->unit_offset == unit_offset))
+	return pbr;
     }
+  return NULL;
+}
+
 
+/* Given an expression, return the structure describing how it should be
+   replaced if it accesses a part of a split parameter or NULL otherwise.
+
+   Do not free the result, it will be deallocated when the object is destroyed.
+
+   If IGNORE_DEFAULT_DEF is cleared, consider only SSA_NAMEs of PARM_DECLs
+   which are default definitions, if set, consider all SSA_NAMEs of
+   PARM_DECLs.  */
+
+ipa_param_body_replacement *
+ipa_param_body_adjustments::get_expr_replacement (tree expr,
+						  bool ignore_default_def)
+{
   HOST_WIDE_INT offset, size, max_size;
   bool reverse;
   tree base
-    = get_ref_base_and_extent (**expr, &offset, &size, &max_size, &reverse);
+    = get_ref_base_and_extent (expr, &offset, &size, &max_size, &reverse);
   if (!base || size == -1 || max_size == -1)
     return NULL;
 
+  if ((offset % BITS_PER_UNIT) != 0)
+    return NULL;
+
   if (TREE_CODE (base) == MEM_REF)
     {
       offset += mem_ref_offset (base).to_short_addr () * BITS_PER_UNIT;
       base = TREE_OPERAND (base, 0);
     }
 
+  if (offset < 0 || (offset / BITS_PER_UNIT) > UINT_MAX)
+    return NULL;
+  unsigned unit_offset = offset / BITS_PER_UNIT;
+
   base = get_ssa_base_param (base, ignore_default_def);
   if (!base || TREE_CODE (base) != PARM_DECL)
     return NULL;
 
-  struct ipa_parm_adjustment *cand = NULL;
-  unsigned int len = adjustments.length ();
-  for (unsigned i = 0; i < len; i++)
-    {
-      struct ipa_parm_adjustment *adj = &adjustments[i];
+  return lookup_replacement (base, unit_offset);
+}
 
-      if (adj->base == base
-	  && (adj->offset == offset || adj->op == IPA_PARM_OP_REMOVE))
-	{
-	  cand = adj;
-	  break;
-	}
+/* Given OLD_DECL, which is a PARM_DECL of a parameter that is being removed
+   (which includes it being split or replaced), return a new variable that
+   should be used for any SSA names that will remain in the function that
+   previously belonged to OLD_DECL.  */
+
+tree
+ipa_param_body_adjustments::get_replacement_ssa_base (tree old_decl)
+{
+  unsigned *idx = m_removed_map.get (old_decl);
+  if (!idx)
+    return NULL;
+
+  tree repl;
+  if (TREE_CODE (m_removed_decls [*idx]) == PARM_DECL)
+    {
+      gcc_assert (m_removed_decls [*idx] == old_decl);
+      repl = copy_var_decl (old_decl, DECL_NAME (old_decl),
+			    TREE_TYPE (old_decl));
+      m_removed_decls [*idx] = repl;
     }
+  else
+    repl = m_removed_decls [*idx];
+  return repl;
+}
+
+/* If OLD_NAME, which is being defined by statement STMT, is an SSA_NAME of a
+   parameter which is to be removed because its value is not used, create a new
+   SSA_NAME relating to a replacement VAR_DECL, replace all uses of the
+   original with it and return it.  If there is no need to re-map, return NULL.
+   ADJUSTMENTS is a pointer to a vector of IPA-SRA adjustments.  */
 
-  if (!cand || cand->op == IPA_PARM_OP_COPY || cand->op == IPA_PARM_OP_REMOVE)
+tree
+ipa_param_body_adjustments::replace_removed_params_ssa_names (tree old_name,
+							      gimple *stmt)
+{
+  if (TREE_CODE (old_name) != SSA_NAME)
+    return NULL;
+
+  tree decl = SSA_NAME_VAR (old_name);
+  if (decl == NULL_TREE
+      || TREE_CODE (decl) != PARM_DECL)
     return NULL;
-  return cand;
+
+  tree repl = get_replacement_ssa_base (decl);
+  if (!repl)
+    return NULL;
+
+  tree new_name = make_ssa_name (repl, stmt);
+  SSA_NAME_OCCURS_IN_ABNORMAL_PHI (new_name)
+    = SSA_NAME_OCCURS_IN_ABNORMAL_PHI (old_name);
+
+  if (dump_file)
+    {
+      fprintf (dump_file, "replacing an SSA name of a removed param ");
+      print_generic_expr (dump_file, old_name);
+      fprintf (dump_file, " with ");
+      print_generic_expr (dump_file, new_name);
+      fprintf (dump_file, "\n");
+    }
+
+  replace_uses_by (old_name, new_name);
+  return new_name;
 }
 
-/* If the expression *EXPR should be replaced by a reduction of a parameter, do
-   so.  ADJUSTMENTS is a pointer to a vector of adjustments.  CONVERT
-   specifies whether the function should care about type incompatibility the
-   current and new expressions.  If it is false, the function will leave
-   incompatibility issues to the caller.  Return true iff the expression
-   was modified. */
+/* If the expression *EXPR_P should be replaced by a reduction of a parameter,
+   do so.  CONVERT specifies whether the function should care about type
+   incompatibility of the current and new expressions.  If it is false, the
+   function will leave incompatibility issues to the caller, but it will be
+   overridden if BIT_FIELD_REF, IMAGPART_EXPR or REALPART_EXPR is encountered.
+   Return true iff the expression was modified. */
 
 bool
-ipa_modify_expr (tree *expr, bool convert,
-		 ipa_parm_adjustment_vec adjustments)
+ipa_param_body_adjustments::modify_expr (tree *expr_p, bool convert)
 {
-  struct ipa_parm_adjustment *cand
-    = ipa_get_adjustment_candidate (&expr, &convert, adjustments, false);
-  if (!cand)
+  tree expr = *expr_p;
+
+  if (TREE_CODE (expr) == BIT_FIELD_REF
+      || TREE_CODE (expr) == IMAGPART_EXPR
+      || TREE_CODE (expr) == REALPART_EXPR)
+    {
+      expr_p = &TREE_OPERAND (expr, 0);
+      expr = *expr_p;
+      convert = true;
+    }
+
+  ipa_param_body_replacement *pbr = get_expr_replacement (expr, false);
+  if (!pbr)
     return false;
 
-  tree src;
-  if (cand->by_ref)
+  tree repl;
+  if (pbr->by_ref)
     {
-      src = build_simple_mem_ref (cand->new_decl);
-      REF_REVERSE_STORAGE_ORDER (src) = cand->reverse;
+      repl = build_simple_mem_ref (pbr->repl);
+      REF_REVERSE_STORAGE_ORDER (repl) = pbr->reverse;
     }
   else
-    src = cand->new_decl;
+    repl = pbr->repl;
 
   if (dump_file && (dump_flags & TDF_DETAILS))
     {
       fprintf (dump_file, "About to replace expr ");
-      print_generic_expr (dump_file, *expr);
+      print_generic_expr (dump_file, expr);
       fprintf (dump_file, " with ");
-      print_generic_expr (dump_file, src);
+      print_generic_expr (dump_file, repl);
       fprintf (dump_file, "\n");
     }
 
-  if (convert && !useless_type_conversion_p (TREE_TYPE (*expr), cand->type))
+  if (convert && !useless_type_conversion_p (TREE_TYPE (expr),
+					     TREE_TYPE (repl)))
     {
-      tree vce = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (*expr), src);
-      *expr = vce;
+      tree vce = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (expr), repl);
+      *expr_p = vce;
     }
   else
-    *expr = src;
+    *expr_p = repl;
   return true;
 }
 
-/* Dump the adjustments in the vector ADJUSTMENTS to dump_file in a human
-   friendly way, assuming they are meant to be applied to FNDECL.  */
+/* If the statement STMT contains any expressions that need to replaced with a
+   different one as noted by ADJUSTMENTS, do so.  Handle any potential type
+   incompatibilities (GSI is used to accommodate conversion statements and must
+   point to the statement).  Return true iff the statement was modified.  */
 
-void
-ipa_dump_param_adjustments (FILE *file, ipa_parm_adjustment_vec adjustments,
-			    tree fndecl)
+bool
+ipa_param_body_adjustments::modify_assignment (gimple *stmt,
+					       gimple_stmt_iterator *gsi)
 {
-  int i, len = adjustments.length ();
-  bool first = true;
-  vec<tree> parms = ipa_get_vector_of_formal_parms (fndecl);
+  tree *lhs_p, *rhs_p;
+  bool any;
 
-  fprintf (file, "IPA param adjustments: ");
-  for (i = 0; i < len; i++)
-    {
-      struct ipa_parm_adjustment *adj;
-      adj = &adjustments[i];
+  if (!gimple_assign_single_p (stmt))
+    return false;
 
-      if (!first)
-	fprintf (file, "                 ");
-      else
-	first = false;
+  rhs_p = gimple_assign_rhs1_ptr (stmt);
+  lhs_p = gimple_assign_lhs_ptr (stmt);
+
+  any = modify_expr (lhs_p, false);
+  any |= modify_expr (rhs_p, false);
+  if (any)
+    {
+      tree new_rhs = NULL_TREE;
 
-      fprintf (file, "%i. base_index: %i - ", i, adj->base_index);
-      print_generic_expr (file, parms[adj->base_index]);
-      if (adj->base)
+      if (!useless_type_conversion_p (TREE_TYPE (*lhs_p), TREE_TYPE (*rhs_p)))
 	{
-	  fprintf (file, ", base: ");
-	  print_generic_expr (file, adj->base);
+	  if (TREE_CODE (*rhs_p) == CONSTRUCTOR)
+	    {
+	      /* V_C_Es of constructors can cause trouble (PR 42714).  */
+	      if (is_gimple_reg_type (TREE_TYPE (*lhs_p)))
+		*rhs_p = build_zero_cst (TREE_TYPE (*lhs_p));
+	      else
+		*rhs_p = build_constructor (TREE_TYPE (*lhs_p),
+					    NULL);
+	    }
+	  else
+	    new_rhs = fold_build1_loc (gimple_location (stmt),
+				       VIEW_CONVERT_EXPR, TREE_TYPE (*lhs_p),
+				       *rhs_p);
 	}
-      if (adj->new_decl)
+      else if (REFERENCE_CLASS_P (*rhs_p)
+	       && is_gimple_reg_type (TREE_TYPE (*lhs_p))
+	       && !is_gimple_reg (*lhs_p))
+	/* This can happen when an assignment in between two single field
+	   structures is turned into an assignment in between two pointers to
+	   scalars (PR 42237).  */
+	new_rhs = *rhs_p;
+
+      if (new_rhs)
 	{
-	  fprintf (file, ", new_decl: ");
-	  print_generic_expr (file, adj->new_decl);
+	  tree tmp = force_gimple_operand_gsi (gsi, new_rhs, true, NULL_TREE,
+					       true, GSI_SAME_STMT);
+
+	  gimple_assign_set_rhs_from_tree (gsi, tmp);
 	}
-      if (adj->new_ssa_base)
+
+      return true;
+    }
+
+  return any;
+}
+
+/* Traverse body of the current function and perform the requested adjustments.
+   Return true iff the CFG has been changed.  */
+
+bool
+ipa_param_body_adjustments::modify_cfun_body ()
+{
+  bool cfg_changed = false;
+  basic_block bb;
+
+  FOR_EACH_BB_FN (bb, cfun)
+    {
+      gimple_stmt_iterator gsi;
+
+      for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
 	{
-	  fprintf (file, ", new_ssa_base: ");
-	  print_generic_expr (file, adj->new_ssa_base);
+	  gphi *phi = as_a <gphi *> (gsi_stmt (gsi));
+	  tree new_lhs, old_lhs = gimple_phi_result (phi);
+	  new_lhs = replace_removed_params_ssa_names (old_lhs, phi);
+	  if (new_lhs)
+	    {
+	      gimple_phi_set_result (phi, new_lhs);
+	      release_ssa_name (old_lhs);
+	    }
 	}
 
-      if (adj->op == IPA_PARM_OP_COPY)
-	fprintf (file, ", copy_param");
-      else if (adj->op == IPA_PARM_OP_REMOVE)
-	fprintf (file, ", remove_param");
-      else
-	fprintf (file, ", offset %li", (long) adj->offset);
-      if (adj->by_ref)
-	fprintf (file, ", by_ref");
-      print_node_brief (file, ", type: ", adj->type, 0);
-      fprintf (file, "\n");
+      gsi = gsi_start_bb (bb);
+      while (!gsi_end_p (gsi))
+	{
+	  gimple *stmt = gsi_stmt (gsi);
+	  bool modified = false;
+	  tree *t;
+
+	  switch (gimple_code (stmt))
+	    {
+	    case GIMPLE_RETURN:
+	      t = gimple_return_retval_ptr (as_a <greturn *> (stmt));
+	      if (*t != NULL_TREE)
+		modified |= modify_expr (t, true);
+	      break;
+
+	    case GIMPLE_ASSIGN:
+	      modified |= modify_assignment (stmt, &gsi);
+	      break;
+
+	    case GIMPLE_CALL:
+	      /* Operands must be processed before the lhs.  */
+	      for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
+		{
+		  t = gimple_call_arg_ptr (stmt, i);
+		  modified |= modify_expr (t, true);
+		}
+
+	      if (gimple_call_lhs (stmt))
+		{
+		  t = gimple_call_lhs_ptr (stmt);
+		  modified |= modify_expr (t, false);
+		}
+	      break;
+
+	    case GIMPLE_ASM:
+	      {
+		gasm *asm_stmt = as_a <gasm *> (stmt);
+		for (unsigned i = 0; i < gimple_asm_ninputs (asm_stmt); i++)
+		  {
+		    t = &TREE_VALUE (gimple_asm_input_op (asm_stmt, i));
+		    modified |= modify_expr (t, true);
+		  }
+		for (unsigned i = 0; i < gimple_asm_noutputs (asm_stmt); i++)
+		  {
+		    t = &TREE_VALUE (gimple_asm_output_op (asm_stmt, i));
+		    modified |= modify_expr (t, false);
+		  }
+	      }
+	      break;
+
+	    default:
+	      break;
+	    }
+
+	  def_operand_p defp;
+	  ssa_op_iter iter;
+	  FOR_EACH_SSA_DEF_OPERAND (defp, stmt, iter, SSA_OP_DEF)
+	    {
+	      tree old_def = DEF_FROM_PTR (defp);
+	      if (tree new_def = replace_removed_params_ssa_names (old_def,
+								   stmt))
+		{
+		  SET_DEF (defp, new_def);
+		  release_ssa_name (old_def);
+		  modified = true;
+		}
+	    }
+
+	  if (modified)
+	    {
+	      update_stmt (stmt);
+	      if (maybe_clean_eh_stmt (stmt)
+		  && gimple_purge_dead_eh_edges (gimple_bb (stmt)))
+		cfg_changed = true;
+	    }
+	  gsi_next (&gsi);
+	}
     }
-  parms.release ();
+
+  return cfg_changed;
+}
+
+/* Call gimple_debug_bind_reset_value on all debug statements describing
+   gimple register parameters that are being removed or replaced.  */
+
+void
+ipa_param_body_adjustments::reset_debug_stmts ()
+{
+  int i, len;
+  gimple_stmt_iterator *gsip = NULL, gsi;
+
+  if (MAY_HAVE_DEBUG_STMTS && single_succ_p (ENTRY_BLOCK_PTR_FOR_FN (cfun)))
+    {
+      gsi = gsi_after_labels (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
+      gsip = &gsi;
+    }
+  len = m_reset_debug_decls.length ();
+  for (i = 0; i < len; i++)
+    {
+      imm_use_iterator ui;
+      gimple *stmt;
+      gdebug *def_temp;
+      tree name, vexpr, copy = NULL_TREE;
+      use_operand_p use_p;
+      tree decl = m_reset_debug_decls[i];
+
+      gcc_checking_assert (is_gimple_reg (decl));
+      name = ssa_default_def (cfun, decl);
+      vexpr = NULL;
+      if (name)
+	FOR_EACH_IMM_USE_STMT (stmt, ui, name)
+	  {
+	    if (gimple_clobber_p (stmt))
+	      {
+		gimple_stmt_iterator cgsi = gsi_for_stmt (stmt);
+		unlink_stmt_vdef (stmt);
+		gsi_remove (&cgsi, true);
+		release_defs (stmt);
+		continue;
+	      }
+	    /* All other users must have been removed by function body
+	       modification.  */
+	    gcc_assert (is_gimple_debug (stmt));
+	    if (vexpr == NULL && gsip != NULL)
+	      {
+		vexpr = make_node (DEBUG_EXPR_DECL);
+		def_temp = gimple_build_debug_source_bind (vexpr, decl, NULL);
+		DECL_ARTIFICIAL (vexpr) = 1;
+		TREE_TYPE (vexpr) = TREE_TYPE (name);
+		SET_DECL_MODE (vexpr, DECL_MODE (decl));
+		gsi_insert_before (gsip, def_temp, GSI_SAME_STMT);
+	      }
+	    if (vexpr)
+	      {
+		FOR_EACH_IMM_USE_ON_STMT (use_p, ui)
+		  SET_USE (use_p, vexpr);
+	      }
+	    else
+	      gimple_debug_bind_reset_value (stmt);
+	    update_stmt (stmt);
+	  }
+      /* Create a VAR_DECL for debug info purposes.  */
+      if (!DECL_IGNORED_P (decl))
+	{
+	  copy = build_decl (DECL_SOURCE_LOCATION (current_function_decl),
+			     VAR_DECL, DECL_NAME (decl),
+			     TREE_TYPE (decl));
+	  if (DECL_PT_UID_SET_P (decl))
+	    SET_DECL_PT_UID (copy, DECL_PT_UID (decl));
+	  TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl);
+	  TREE_READONLY (copy) = TREE_READONLY (decl);
+	  TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
+	  DECL_GIMPLE_REG_P (copy) = DECL_GIMPLE_REG_P (decl);
+	  DECL_ARTIFICIAL (copy) = DECL_ARTIFICIAL (decl);
+	  DECL_IGNORED_P (copy) = DECL_IGNORED_P (decl);
+	  DECL_ABSTRACT_ORIGIN (copy) = DECL_ORIGIN (decl);
+	  DECL_SEEN_IN_BIND_EXPR_P (copy) = 1;
+	  SET_DECL_RTL (copy, 0);
+	  TREE_USED (copy) = 1;
+	  DECL_CONTEXT (copy) = current_function_decl;
+	  add_local_decl (cfun, copy);
+	  DECL_CHAIN (copy) =
+	    BLOCK_VARS (DECL_INITIAL (current_function_decl));
+	  BLOCK_VARS (DECL_INITIAL (current_function_decl)) = copy;
+	}
+      if (gsip != NULL && copy && target_for_debug_bind (decl))
+	{
+	  gcc_assert (TREE_CODE (decl) == PARM_DECL);
+	  if (vexpr)
+	    def_temp = gimple_build_debug_bind (copy, vexpr, NULL);
+	  else
+	    def_temp = gimple_build_debug_source_bind (copy, decl,
+						       NULL);
+	  gsi_insert_before (gsip, def_temp, GSI_SAME_STMT);
+	}
+    }
+}
+
+/* Perform all necessary body changes to change signature, body and debug info
+   of fun according to adjustments passed at construction.  Return true if CFG
+   was changed in any way.  */
+
+bool
+ipa_param_body_adjustments::perform_cfun_body_modifications ()
+{
+  bool cfg_changed;
+  modify_formal_parameters ();
+  cfg_changed = modify_cfun_body ();
+  reset_debug_stmts ();
+
+  return cfg_changed;
 }
 
diff --git a/gcc/ipa-param-manipulation.h b/gcc/ipa-param-manipulation.h
index 364e4489c29..1f64dd99201 100644
--- a/gcc/ipa-param-manipulation.h
+++ b/gcc/ipa-param-manipulation.h
@@ -21,100 +21,157 @@  along with GCC; see the file COPYING3.  If not see
 #ifndef IPA_PARAM_MANIPULATION_H
 #define IPA_PARAM_MANIPULATION_H
 
+/* Indices into ipa_param_prefixes to identify a human-readable prefix for newly
+   synthesized parameters.  Keep in sync with the array.  */
+#define IPA_PARAM_PREFIX_SYNTH  0
+#define IPA_PARAM_PREFIX_ISRA   1
+#define IPA_PARAM_PREFIX_SIMD   2
+#define IPA_PARAM_PREFIX_MASK   3
+
+/* We do not support manipulating functions with more than
+   1<<IPA_PARAM_MAX_INDEX_BITS parameters.  */
+#define IPA_PARAM_MAX_INDEX_BITS 16
+
 /* Operation to be performed for the parameter in ipa_parm_adjustment
    below.  */
-enum ipa_parm_op {
-  IPA_PARM_OP_NONE,
-
-  /* This describes a brand new parameter.
 
-     The field `type' should be set to the new type, `arg_prefix'
-     should be set to the string prefix for the new DECL_NAME, and
-     `new_decl' will ultimately hold the newly created argument.  */
-  IPA_PARM_OP_NEW,
+enum ipa_parm_op {
+  /* Do not use or you will trigger an assert.  */
+  IPA_PARAM_OP_UNDEFINED,
 
   /* This new parameter is an unmodified parameter at index base_index. */
-  IPA_PARM_OP_COPY,
+  IPA_PARAM_OP_COPY,
+
+  /* This describes a brand new parameter.  If it somehow relates to any
+     original parameters, the user needs to manage the transition itself.  */
+  IPA_PARAM_OP_NEW,
 
-  /* This adjustment describes a parameter that is about to be removed
-     completely.  Most users will probably need to book keep those so that they
-     don't leave behinfd any non default def ssa names belonging to them.  */
-  IPA_PARM_OP_REMOVE
+    /* Split parameter as indicated by fields base_index, offset, type and
+     by_ref.  */
+  IPA_PARAM_OP_SPLIT
 };
 
-/* Structure to describe transformations of formal parameters and actual
-   arguments.  Each instance describes one new parameter and they are meant to
-   be stored in a vector.  Additionally, most users will probably want to store
-   adjustments about parameters that are being removed altogether so that SSA
-   names belonging to them can be replaced by SSA names of an artificial
-   variable.  */
-struct ipa_parm_adjustment
-{
-  /* The original PARM_DECL itself, helpful for processing of the body of the
-     function itself.  Intended for traversing function bodies.
-     ipa_modify_formal_parameters, ipa_modify_call_arguments and
-     ipa_combine_adjustments ignore this and use base_index.
-     ipa_modify_formal_parameters actually sets this.  */
-  tree base;
+/* Structure that describes one parameter of a function after transformation.
+   Omitted parameters will be removed.  */
 
+struct GTY(()) ipa_adjusted_param
+{
   /* Type of the new parameter.  However, if by_ref is true, the real type will
-     be a pointer to this type.  */
+     be a pointer to this type.  Required for all operations except
+     IPA_PARM_OP_COPY when the original type will be preserved.  */
   tree type;
 
-  /* Alias refrerence type to be used in MEM_REFs when adjusting caller
-     arguments.  */
+  /* Alias reference type to be used in MEM_REFs when adjusting caller
+     arguments.  Required for IPA_PARM_OP_SPLIT operation.  */
   tree alias_ptr_type;
 
-  /* The new declaration when creating/replacing a parameter.  Created
-     by ipa_modify_formal_parameters, useful for functions modifying
-     the body accordingly.  For brand new arguments, this is the newly
-     created argument.  */
-  tree new_decl;
-
-  /* New declaration of a substitute variable that we may use to replace all
-     non-default-def ssa names when a parm decl is going away.  */
-  tree new_ssa_base;
-
-  /* If non-NULL and the original parameter is to be removed (copy_param below
-     is NULL), this is going to be its nonlocalized vars value.  */
-  tree nonlocal_value;
-
-  /* This holds the prefix to be used for the new DECL_NAME.  */
-  const char *arg_prefix;
-
   /* Offset into the original parameter (for the cases when the new parameter
-     is a component of an original one).  */
-  HOST_WIDE_INT offset;
+     is a component of an original one).  Required for IPA_PARM_OP_SPLIT
+     operation.  */
+  unsigned unit_offset;
+
+  /* Specify the operation, if any, to be performed on the parameter.  */
+  enum ipa_parm_op op : 2;
 
-  /* Zero based index of the original parameter this one is based on.  */
-  int base_index;
+  /* Zero based index of the original parameter this one is based on.  Required
+     for IPA_PARAM_OP_COPY and IPA_PARAM_OP_SPLIT, users of IPA_PARAM_OP_NEW
+     only need to specify it if they use replacement lookup provided by
+     ipa_param_body_adjustments.  */
+  unsigned base_index : IPA_PARAM_MAX_INDEX_BITS;
 
-  /* Whether this parameter is a new parameter, a copy of an old one,
-     or one about to be removed.  */
-  enum ipa_parm_op op;
+  /* Index into ipa_param_prefixes specifying a prefix to be used with
+     DECL_NAMEs of newly synthesized parameters.  */
+  unsigned param_prefix_index : 2;
 
   /* Storage order of the original parameter (for the cases when the new
      parameter is a component of an original one).  */
   unsigned reverse : 1;
 
-  /* The parameter is to be passed by reference.  */
+  /* Set when the parameter is to be passed by reference.  */
   unsigned by_ref : 1;
+
+  /* A bit free for the user.  */
+  unsigned user_flag : 1;
 };
 
-typedef vec<ipa_parm_adjustment> ipa_parm_adjustment_vec;
+void ipa_dump_adjusted_parameters (FILE *f,
+				   vec<ipa_adjusted_param, va_gc> *adj_params);
+
+/* Class used to record planned modifications to parameters of a function and
+   also to perform necessary modifications at the caller side at the gimple
+   level.  */
+
+class GTY(()) ipa_param_adjustments
+{
+public:
+  ipa_param_adjustments (vec<ipa_adjusted_param, va_gc> *new_params,
+			 tree fndecl);
+  ipa_param_adjustments (vec<ipa_adjusted_param, va_gc> *new_params,
+			 vec<tree> *cur_params = NULL);
+
+  gcall *modify_call_arguments (gcall *stmt, tree callee_decl);
+  void modify_call_arguments (struct cgraph_edge *cs);
+
+  vec<ipa_adjusted_param, va_gc> *m_adj_params;
+  vec<tree, va_gc> *m_vanishing_decls;
+  vec<unsigned, va_gc> *m_vanishing_indices;
+private:
+  ipa_param_adjustments () {}
+
+  void init (vec<tree> *cur_params);
+};
+
+/* Structure used to map expressions accessing split or replaced parameters to
+   new PARM_DECLs.  TODO: Even though there usually be only few, but should we
+   use a hash?  */
+
+struct ipa_param_body_replacement
+{
+  tree base, repl;
+  unsigned unit_offset;
+  bool by_ref;
+  bool reverse;
+};
+
+
+/* Class used when actually performing adjustments to formal parameters of
+   a function to map accesses that need to be replaced to replacements.  */
+
+class ipa_param_body_adjustments
+{
+public:
+  ipa_param_body_adjustments (vec<ipa_adjusted_param, va_gc> *adj_params,
+			      tree fndecl);
+
+  bool perform_cfun_body_modifications ();
+  void modify_formal_parameters ();
+  void register_replacement (ipa_adjusted_param *apm, tree replacement);
+  ipa_param_body_replacement *lookup_replacement (tree base,
+						  unsigned unit_offset);
+  ipa_param_body_replacement *get_expr_replacement (tree expr,
+						    bool ignore_default_def);
+  tree get_replacement_ssa_base (tree old_decl);
+
+  vec<ipa_adjusted_param, va_gc> *m_adj_params;
+private:
+  tree replace_removed_params_ssa_names (tree old_name, gimple *stmt);
+  bool modify_expr (tree *expr_p, bool convert);
+  bool modify_assignment (gimple *stmt, gimple_stmt_iterator *gsi);
+  bool modify_cfun_body ();
+  void reset_debug_stmts ();
+
+  tree m_fndecl;
+  auto_vec<tree, 16> m_oparms;
+  auto_vec<ipa_param_body_replacement, 16> m_replacements;
+  auto_vec<tree, 16> m_new_decls;
+  auto_vec<tree, 16> m_new_types;
+  auto_vec<tree, 16> m_reset_debug_decls;
+  auto_vec<tree, 16> m_removed_decls;
+  hash_map<tree, unsigned> m_removed_map;
+  bool m_method2func;
+};
 
 vec<tree> ipa_get_vector_of_formal_parms (tree fndecl);
 vec<tree> ipa_get_vector_of_formal_parm_types (tree fntype);
-void ipa_modify_formal_parameters (tree fndecl, ipa_parm_adjustment_vec);
-void ipa_modify_call_arguments (struct cgraph_edge *, gcall *,
-				ipa_parm_adjustment_vec);
-ipa_parm_adjustment_vec ipa_combine_adjustments (ipa_parm_adjustment_vec,
-						 ipa_parm_adjustment_vec);
-void ipa_dump_param_adjustments (FILE *, ipa_parm_adjustment_vec, tree);
-
-bool ipa_modify_expr (tree *, bool, ipa_parm_adjustment_vec);
-ipa_parm_adjustment *ipa_get_adjustment_candidate (tree **, bool *,
-						   ipa_parm_adjustment_vec,
-						   bool);
 
 #endif	/* IPA_PARAM_MANIPULATION_H */
diff --git a/gcc/omp-simd-clone.c b/gcc/omp-simd-clone.c
index b8386037dfd..760cf6b5161 100644
--- a/gcc/omp-simd-clone.c
+++ b/gcc/omp-simd-clone.c
@@ -558,28 +558,27 @@  create_tmp_simd_array (const char *prefix, tree type, int simdlen)
    Returns an adjustment vector that will be filled describing how the
    argument types will be adjusted.  */
 
-static ipa_parm_adjustment_vec
+static ipa_param_body_adjustments *
 simd_clone_adjust_argument_types (struct cgraph_node *node)
 {
   vec<tree> args;
-  ipa_parm_adjustment_vec adjustments;
 
   if (node->definition)
     args = ipa_get_vector_of_formal_parms (node->decl);
   else
     args = simd_clone_vector_of_formal_parm_types (node->decl);
-  adjustments.create (args.length ());
+  vec<ipa_adjusted_param, va_gc> *new_params = NULL;
+  vec_safe_reserve_exact (new_params, args.length ());
   unsigned i, j, veclen;
-  struct ipa_parm_adjustment adj;
   struct cgraph_simd_clone *sc = node->simdclone;
 
   for (i = 0; i < sc->nargs; ++i)
     {
-      memset (&adj, 0, sizeof (adj));
+      ipa_adjusted_param apm;
+      memset (&apm, 0, sizeof (apm));
       tree parm = args[i];
       tree parm_type = node->definition ? TREE_TYPE (parm) : parm;
-      adj.base_index = i;
-      adj.base = parm;
+      apm.base_index = i;
 
       sc->args[i].orig_arg = node->definition ? parm : NULL_TREE;
       sc->args[i].orig_type = parm_type;
@@ -588,7 +587,7 @@  simd_clone_adjust_argument_types (struct cgraph_node *node)
 	{
 	default:
 	  /* No adjustment necessary for scalar arguments.  */
-	  adj.op = IPA_PARM_OP_COPY;
+	  apm.op = IPA_PARAM_OP_COPY;
 	  break;
 	case SIMD_CLONE_ARG_TYPE_LINEAR_UVAL_CONSTANT_STEP:
 	case SIMD_CLONE_ARG_TYPE_LINEAR_UVAL_VARIABLE_STEP:
@@ -597,7 +596,7 @@  simd_clone_adjust_argument_types (struct cgraph_node *node)
 	      = create_tmp_simd_array (IDENTIFIER_POINTER (DECL_NAME (parm)),
 				       TREE_TYPE (parm_type),
 				       sc->simdlen);
-	  adj.op = IPA_PARM_OP_COPY;
+	  apm.op = IPA_PARAM_OP_COPY;
 	  break;
 	case SIMD_CLONE_ARG_TYPE_LINEAR_VAL_CONSTANT_STEP:
 	case SIMD_CLONE_ARG_TYPE_LINEAR_VAL_VARIABLE_STEP:
@@ -609,22 +608,24 @@  simd_clone_adjust_argument_types (struct cgraph_node *node)
 	  veclen /= GET_MODE_BITSIZE (SCALAR_TYPE_MODE (parm_type));
 	  if (veclen > sc->simdlen)
 	    veclen = sc->simdlen;
-	  adj.arg_prefix = "simd";
+	  apm.op = IPA_PARAM_OP_NEW;
+	  apm.param_prefix_index = IPA_PARAM_PREFIX_SIMD;
 	  if (POINTER_TYPE_P (parm_type))
-	    adj.type = build_vector_type (pointer_sized_int_node, veclen);
+	    apm.type = build_vector_type (pointer_sized_int_node, veclen);
 	  else
-	    adj.type = build_vector_type (parm_type, veclen);
-	  sc->args[i].vector_type = adj.type;
+	    apm.type = build_vector_type (parm_type, veclen);
+	  sc->args[i].vector_type = apm.type;
 	  for (j = veclen; j < sc->simdlen; j += veclen)
 	    {
-	      adjustments.safe_push (adj);
+	      vec_safe_push (new_params, apm);
 	      if (j == veclen)
 		{
-		  memset (&adj, 0, sizeof (adj));
-		  adj.op = IPA_PARM_OP_NEW;
-		  adj.arg_prefix = "simd";
-		  adj.base_index = i;
-		  adj.type = sc->args[i].vector_type;
+		  memset (&apm, 0, sizeof (apm));
+		  apm.op = IPA_PARAM_OP_NEW;
+		  apm.user_flag = 1;
+		  apm.param_prefix_index = IPA_PARAM_PREFIX_SIMD;
+		  apm.base_index = i;
+		  apm.type = sc->args[i].vector_type;
 		}
 	    }
 
@@ -634,18 +635,19 @@  simd_clone_adjust_argument_types (struct cgraph_node *node)
 				       ? IDENTIFIER_POINTER (DECL_NAME (parm))
 				       : NULL, parm_type, sc->simdlen);
 	}
-      adjustments.safe_push (adj);
+      vec_safe_push (new_params, apm);
     }
 
   if (sc->inbranch)
     {
       tree base_type = simd_clone_compute_base_data_type (sc->origin, sc);
+      ipa_adjusted_param apm;
+      memset (&apm, 0, sizeof (apm));
+      apm.op = IPA_PARAM_OP_NEW;
+      apm.user_flag = 1;
+      apm.param_prefix_index = IPA_PARAM_PREFIX_MASK;
 
-      memset (&adj, 0, sizeof (adj));
-      adj.op = IPA_PARM_OP_NEW;
-      adj.arg_prefix = "mask";
-
-      adj.base_index = i;
+      apm.base_index = i;
       if (INTEGRAL_TYPE_P (base_type) || POINTER_TYPE_P (base_type))
 	veclen = sc->vecsize_int;
       else
@@ -654,16 +656,16 @@  simd_clone_adjust_argument_types (struct cgraph_node *node)
       if (veclen > sc->simdlen)
 	veclen = sc->simdlen;
       if (sc->mask_mode != VOIDmode)
-	adj.type
+	apm.type
 	  = lang_hooks.types.type_for_mode (sc->mask_mode, 1);
       else if (POINTER_TYPE_P (base_type))
-	adj.type = build_vector_type (pointer_sized_int_node, veclen);
+	apm.type = build_vector_type (pointer_sized_int_node, veclen);
       else
-	adj.type = build_vector_type (base_type, veclen);
-      adjustments.safe_push (adj);
+	apm.type = build_vector_type (base_type, veclen);
+      vec_safe_push (new_params, apm);
 
       for (j = veclen; j < sc->simdlen; j += veclen)
-	adjustments.safe_push (adj);
+	vec_safe_push (new_params, apm);
 
       /* We have previously allocated one extra entry for the mask.  Use
 	 it and fill it.  */
@@ -679,7 +681,7 @@  simd_clone_adjust_argument_types (struct cgraph_node *node)
 	      = create_tmp_simd_array ("mask", base_type, sc->simdlen);
 	  else if (veclen < sc->simdlen)
 	    sc->args[i].simd_array
-	      = create_tmp_simd_array ("mask", adj.type, sc->simdlen / veclen);
+	      = create_tmp_simd_array ("mask", apm.type, sc->simdlen / veclen);
 	  else
 	    sc->args[i].simd_array = NULL_TREE;
 	}
@@ -688,7 +690,14 @@  simd_clone_adjust_argument_types (struct cgraph_node *node)
     }
 
   if (node->definition)
-    ipa_modify_formal_parameters (node->decl, adjustments);
+    {
+      args.release ();
+      ipa_param_body_adjustments *adjustments
+	= new ipa_param_body_adjustments (new_params, node->decl);
+
+      adjustments->modify_formal_parameters ();
+      return adjustments;
+    }
   else
     {
       tree new_arg_types = NULL_TREE, new_reversed;
@@ -697,15 +706,15 @@  simd_clone_adjust_argument_types (struct cgraph_node *node)
 	last_parm_void = true;
 
       gcc_assert (TYPE_ARG_TYPES (TREE_TYPE (node->decl)));
-      j = adjustments.length ();
+      j = vec_safe_length (new_params);
       for (i = 0; i < j; i++)
 	{
-	  struct ipa_parm_adjustment *adj = &adjustments[i];
+	  struct ipa_adjusted_param *apm = &(*new_params)[i];
 	  tree ptype;
-	  if (adj->op == IPA_PARM_OP_COPY)
-	    ptype = args[adj->base_index];
+	  if (apm->op == IPA_PARAM_OP_COPY)
+	    ptype = args[apm->base_index];
 	  else
-	    ptype = adj->type;
+	    ptype = apm->type;
 	  new_arg_types = tree_cons (NULL_TREE, ptype, new_arg_types);
 	}
       new_reversed = nreverse (new_arg_types);
@@ -720,11 +729,9 @@  simd_clone_adjust_argument_types (struct cgraph_node *node)
       tree new_type = build_distinct_type_copy (TREE_TYPE (node->decl));
       TYPE_ARG_TYPES (new_type) = new_reversed;
       TREE_TYPE (node->decl) = new_type;
-
-      adjustments.release ();
+      args.release ();
+      return NULL;
     }
-  args.release ();
-  return adjustments;
 }
 
 /* Initialize and copy the function arguments in NODE to their
@@ -733,7 +740,7 @@  simd_clone_adjust_argument_types (struct cgraph_node *node)
 
 static gimple_seq
 simd_clone_init_simd_arrays (struct cgraph_node *node,
-			     ipa_parm_adjustment_vec adjustments)
+			     ipa_param_body_adjustments *adjustments)
 {
   gimple_seq seq = NULL;
   unsigned i = 0, j = 0, k;
@@ -742,7 +749,7 @@  simd_clone_init_simd_arrays (struct cgraph_node *node,
        arg;
        arg = DECL_CHAIN (arg), i++, j++)
     {
-      if (adjustments[j].op == IPA_PARM_OP_COPY
+      if ((*adjustments->m_adj_params)[j].op == IPA_PARAM_OP_COPY
 	  || POINTER_TYPE_P (TREE_TYPE (arg)))
 	continue;
 
@@ -807,7 +814,7 @@  simd_clone_init_simd_arrays (struct cgraph_node *node,
 /* Callback info for ipa_simd_modify_stmt_ops below.  */
 
 struct modify_stmt_info {
-  ipa_parm_adjustment_vec adjustments;
+  ipa_param_body_adjustments *adjustments;
   gimple *stmt;
   /* True if the parent statement was modified by
      ipa_simd_modify_stmt_ops.  */
@@ -827,9 +834,15 @@  ipa_simd_modify_stmt_ops (tree *tp, int *walk_subtrees, void *data)
   tree *orig_tp = tp;
   if (TREE_CODE (*tp) == ADDR_EXPR)
     tp = &TREE_OPERAND (*tp, 0);
-  struct ipa_parm_adjustment *cand = NULL;
+
+  if (TREE_CODE (*tp) == BIT_FIELD_REF
+      || TREE_CODE (*tp) == IMAGPART_EXPR
+      || TREE_CODE (*tp) == REALPART_EXPR)
+    tp = &TREE_OPERAND (*tp, 0);
+
+  ipa_param_body_replacement *pbr = NULL;
   if (TREE_CODE (*tp) == PARM_DECL)
-    cand = ipa_get_adjustment_candidate (&tp, NULL, info->adjustments, true);
+    pbr = info->adjustments->get_expr_replacement (*tp, true);
   else
     {
       if (TYPE_P (*tp))
@@ -837,8 +850,8 @@  ipa_simd_modify_stmt_ops (tree *tp, int *walk_subtrees, void *data)
     }
 
   tree repl = NULL_TREE;
-  if (cand)
-    repl = unshare_expr (cand->new_decl);
+  if (pbr)
+    repl = unshare_expr (pbr->repl);
   else
     {
       if (tp != orig_tp)
@@ -904,70 +917,57 @@  ipa_simd_modify_stmt_ops (tree *tp, int *walk_subtrees, void *data)
 
 static void
 ipa_simd_modify_function_body (struct cgraph_node *node,
-			       ipa_parm_adjustment_vec adjustments,
+			       ipa_param_body_adjustments *adjustments,
 			       tree retval_array, tree iter)
 {
   basic_block bb;
-  unsigned int i, j, l;
+  unsigned int i, j;
+
 
-  /* Re-use the adjustments array, but this time use it to replace
-     every function argument use to an offset into the corresponding
-     simd_array.  */
+  /* Register replacements for every function argument use to an offset into
+     the corresponding simd_array.  */
   for (i = 0, j = 0; i < node->simdclone->nargs; ++i, ++j)
     {
-      if (!node->simdclone->args[i].vector_arg)
+      if (!node->simdclone->args[i].vector_arg
+	  || (*adjustments->m_adj_params)[j].user_flag)
 	continue;
 
       tree basetype = TREE_TYPE (node->simdclone->args[i].orig_arg);
       tree vectype = TREE_TYPE (node->simdclone->args[i].vector_arg);
-      adjustments[j].new_decl
-	= build4 (ARRAY_REF,
-		  basetype,
-		  node->simdclone->args[i].simd_array,
-		  iter,
-		  NULL_TREE, NULL_TREE);
-      if (adjustments[j].op == IPA_PARM_OP_NONE
-	  && TYPE_VECTOR_SUBPARTS (vectype) < node->simdclone->simdlen)
+      tree r = build4 (ARRAY_REF, basetype, node->simdclone->args[i].simd_array,
+		  iter, NULL_TREE, NULL_TREE);
+      adjustments->register_replacement (&(*adjustments->m_adj_params)[j], r);
+
+      if (TYPE_VECTOR_SUBPARTS (vectype) < node->simdclone->simdlen)
 	j += node->simdclone->simdlen / TYPE_VECTOR_SUBPARTS (vectype) - 1;
     }
 
-  l = adjustments.length ();
   tree name;
-
   FOR_EACH_SSA_NAME (i, name, cfun)
     {
+      tree base_var;
       if (SSA_NAME_VAR (name)
-	  && TREE_CODE (SSA_NAME_VAR (name)) == PARM_DECL)
+	  && TREE_CODE (SSA_NAME_VAR (name)) == PARM_DECL
+	  && (base_var
+	      = adjustments->get_replacement_ssa_base (SSA_NAME_VAR (name))))
 	{
-	  for (j = 0; j < l; j++)
-	    if (SSA_NAME_VAR (name) == adjustments[j].base
-		&& adjustments[j].new_decl)
-	      {
-		tree base_var;
-		if (adjustments[j].new_ssa_base == NULL_TREE)
-		  {
-		    base_var
-		      = copy_var_decl (adjustments[j].base,
-				       DECL_NAME (adjustments[j].base),
-				       TREE_TYPE (adjustments[j].base));
-		    adjustments[j].new_ssa_base = base_var;
-		  }
-		else
-		  base_var = adjustments[j].new_ssa_base;
-		if (SSA_NAME_IS_DEFAULT_DEF (name))
-		  {
-		    bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun));
-		    gimple_stmt_iterator gsi = gsi_after_labels (bb);
-		    tree new_decl = unshare_expr (adjustments[j].new_decl);
-		    set_ssa_default_def (cfun, adjustments[j].base, NULL_TREE);
-		    SET_SSA_NAME_VAR_OR_IDENTIFIER (name, base_var);
-		    SSA_NAME_IS_DEFAULT_DEF (name) = 0;
-		    gimple *stmt = gimple_build_assign (name, new_decl);
-		    gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
-		  }
-		else
-		  SET_SSA_NAME_VAR_OR_IDENTIFIER (name, base_var);
-	      }
+	  if (SSA_NAME_IS_DEFAULT_DEF (name))
+	    {
+	      tree old_decl = SSA_NAME_VAR (name);
+	      bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun));
+	      gimple_stmt_iterator gsi = gsi_after_labels (bb);
+	      ipa_param_body_replacement *pbr
+		= adjustments->lookup_replacement (old_decl, 0);
+	      gcc_checking_assert (pbr && !pbr->by_ref && !pbr->reverse);
+	      tree repl = unshare_expr (pbr->repl);
+	      set_ssa_default_def (cfun, old_decl, NULL_TREE);
+	      SET_SSA_NAME_VAR_OR_IDENTIFIER (name, base_var);
+	      SSA_NAME_IS_DEFAULT_DEF (name) = 0;
+	      gimple *stmt = gimple_build_assign (name, repl);
+	      gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
+	    }
+	  else
+	    SET_SSA_NAME_VAR_OR_IDENTIFIER (name, base_var);
 	}
     }
 
@@ -1097,8 +1097,9 @@  simd_clone_adjust (struct cgraph_node *node)
   targetm.simd_clone.adjust (node);
 
   tree retval = simd_clone_adjust_return_type (node);
-  ipa_parm_adjustment_vec adjustments
+  ipa_param_body_adjustments *adjustments
     = simd_clone_adjust_argument_types (node);
+  gcc_assert (adjustments);
 
   push_gimplify_context ();
 
@@ -1110,7 +1111,7 @@  simd_clone_adjust (struct cgraph_node *node)
   tree iter1 = make_ssa_name (iter);
   tree iter2 = NULL_TREE;
   ipa_simd_modify_function_body (node, adjustments, retval, iter1);
-  adjustments.release ();
+  delete adjustments;
 
   /* Initialize the iteration variable.  */
   basic_block entry_bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun));
diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-sra-3.c b/gcc/testsuite/gcc.dg/ipa/ipa-sra-3.c
index 23dec2a661e..62d128acffd 100644
--- a/gcc/testsuite/gcc.dg/ipa/ipa-sra-3.c
+++ b/gcc/testsuite/gcc.dg/ipa/ipa-sra-3.c
@@ -34,5 +34,6 @@  void caller (void)
   return;
 }
 
-/* { dg-final { scan-tree-dump "base: z, remove_param" "eipa_sra"  } } */
-/* { dg-final { scan-tree-dump "base: calf, remove_param" "eipa_sra"  } } */
+/* { dg-final { scan-tree-dump "ox.isra" "eipa_sra"  } } */
+/* { dg-final { scan-tree-dump-not "\\nox.isra\[^;)]*z" "eipa_sra" } } */
+/* { dg-final { scan-tree-dump-not "\\nox.isra\[^;)]*calf" "eipa_sra" } } */
diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c
index db490b20c3e..01447295597 100644
--- a/gcc/tree-sra.c
+++ b/gcc/tree-sra.c
@@ -4697,84 +4697,81 @@  get_param_index (tree base, vec<tree> parms)
   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.  */
+/* Convert the decisions made at the representative level into description of
+   new arguments for parameter manipulation.  REPRESENTATIVES are pointers to
+   first representatives of each param accesses, EXPECTED_COUNT is the expected
+   final number of new number of parameters.  */
 
-static ipa_parm_adjustment_vec
+static vec<ipa_adjusted_param, va_gc> *
 turn_representatives_into_adjustments (vec<access_p> representatives,
-				       int adjustments_count)
+				       int expected_count)
 {
   vec<tree> parms;
-  ipa_parm_adjustment_vec adjustments;
+  vec<ipa_adjusted_param, va_gc> *adj_params = NULL;
   tree parm;
   int i;
 
-  gcc_assert (adjustments_count > 0);
   parms = ipa_get_vector_of_formal_parms (current_function_decl);
-  adjustments.create (adjustments_count);
+  vec_safe_reserve_exact (adj_params, expected_count);
   parm = DECL_ARGUMENTS (current_function_decl);
   for (i = 0; i < func_param_count; i++, parm = DECL_CHAIN (parm))
     {
       struct access *repr = representatives[i];
 
-      if (!repr || no_accesses_p (repr))
+      if (!repr)
 	{
-	  struct ipa_parm_adjustment adj;
+	  struct ipa_adjusted_param apm;
 
-	  memset (&adj, 0, sizeof (adj));
-	  adj.base_index = get_param_index (parm, parms);
-	  adj.base = parm;
-	  if (!repr)
-	    adj.op = IPA_PARM_OP_COPY;
-	  else
-	    adj.op = IPA_PARM_OP_REMOVE;
-	  adj.arg_prefix = "ISRA";
-	  adjustments.quick_push (adj);
+	  memset (&apm, 0, sizeof (apm));
+	  apm.op = IPA_PARAM_OP_COPY;
+	  apm.base_index = get_param_index (parm, parms);
+	  apm.param_prefix_index = IPA_PARAM_PREFIX_ISRA;
+	  adj_params->quick_push (apm);
 	}
+      else if (no_accesses_p (repr))
+	continue;
       else
 	{
-	  struct ipa_parm_adjustment adj;
 	  int index = get_param_index (parm, parms);
 
 	  for (; repr; repr = repr->next_grp)
 	    {
-	      memset (&adj, 0, sizeof (adj));
+	      struct ipa_adjusted_param apm;
+	      memset (&apm, 0, sizeof (apm));
 	      gcc_assert (repr->base == parm);
-	      adj.base_index = index;
-	      adj.base = repr->base;
-	      adj.type = repr->type;
-	      adj.alias_ptr_type = reference_alias_ptr_type (repr->expr);
-	      adj.offset = repr->offset;
-	      adj.reverse = repr->reverse;
-	      adj.by_ref = (POINTER_TYPE_P (TREE_TYPE (repr->base))
+	      apm.op = IPA_PARAM_OP_SPLIT;
+	      apm.base_index = index;
+	      apm.type = repr->type;
+	      apm.alias_ptr_type = reference_alias_ptr_type (repr->expr);
+	      apm.unit_offset = repr->offset / BITS_PER_UNIT;
+	      apm.reverse = repr->reverse;
+	      apm.by_ref = (POINTER_TYPE_P (TREE_TYPE (repr->base))
 			    && (repr->grp_maybe_modified
 				|| repr->grp_not_necessarilly_dereferenced));
-	      adj.arg_prefix = "ISRA";
-	      adjustments.quick_push (adj);
+	      apm.param_prefix_index = IPA_PARAM_PREFIX_ISRA;
+	      adj_params->quick_push (apm);
 	    }
 	}
     }
   parms.release ();
-  return adjustments;
+  return adj_params;
 }
 
 /* Analyze the collected accesses and produce a plan what to do with the
-   parameters in the form of adjustments, NULL meaning nothing.  */
+   parameters in the form of a vector of adjusted parameters and store it to
+   ADJ_PARAMS_P or return NULL if no adjustments are to be made.  */
 
-static ipa_parm_adjustment_vec
-analyze_all_param_acesses (void)
+static bool
+analyze_all_param_acesses (vec<ipa_adjusted_param, va_gc> **adj_params_p)
 {
   enum ipa_splicing_result repr_state;
   bool proceed = false;
-  int i, adjustments_count = 0;
+  int i, expected_count = 0;
   vec<access_p> representatives;
-  ipa_parm_adjustment_vec adjustments;
 
   repr_state = splice_all_param_accesses (representatives);
   if (repr_state == NO_GOOD_ACCESS)
-    return ipa_parm_adjustment_vec ();
+    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.  */
@@ -4792,7 +4789,7 @@  analyze_all_param_acesses (void)
 	{
 	  if (repr->grp_scalar_ptr)
 	    {
-	      adjustments_count++;
+	      expected_count++;
 	      if (repr->grp_not_necessarilly_dereferenced
 		  || repr->grp_maybe_modified)
 		representatives[i] = NULL;
@@ -4809,11 +4806,11 @@  analyze_all_param_acesses (void)
 	      if (new_components == 0)
 		{
 		  representatives[i] = NULL;
-		  adjustments_count++;
+		  expected_count++;
 		}
 	      else
 		{
-		  adjustments_count += new_components;
+		  expected_count += new_components;
 		  sra_stats.aggregate_params_reduced++;
 		  sra_stats.param_reductions_created += new_components;
 		  proceed = true;
@@ -4827,7 +4824,8 @@  analyze_all_param_acesses (void)
 	      proceed = true;
 	      sra_stats.deleted_unused_parameters++;
 	    }
-	  adjustments_count++;
+	  else
+	    expected_count++;
 	}
     }
 
@@ -4835,372 +4833,10 @@  analyze_all_param_acesses (void)
     fprintf (dump_file, "NOT proceeding to change params.\n");
 
   if (proceed)
-    adjustments = turn_representatives_into_adjustments (representatives,
-							 adjustments_count);
-  else
-    adjustments = ipa_parm_adjustment_vec ();
-
+    *adj_params_p = turn_representatives_into_adjustments (representatives,
+							   expected_count);
   representatives.release ();
-  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 = create_tmp_reg (TREE_TYPE (adj->base), "ISR");
-      DECL_NAME (repl) = get_identifier (pretty_name);
-      DECL_NAMELESS (repl) = 1;
-      obstack_free (&name_obstack, pretty_name);
-
-      adj->new_ssa_base = repl;
-    }
-  else
-    repl = adj->new_ssa_base;
-  return repl;
-}
-
-/* Find the first adjustment for a particular parameter BASE in a vector of
-   ADJUSTMENTS which is not a copy_param.  Return NULL if there is no such
-   adjustment. */
-
-static struct ipa_parm_adjustment *
-get_adjustment_for_base (ipa_parm_adjustment_vec adjustments, tree base)
-{
-  int i, len;
-
-  len = adjustments.length ();
-  for (i = 0; i < len; i++)
-    {
-      struct ipa_parm_adjustment *adj;
-
-      adj = &adjustments[i];
-      if (adj->op != IPA_PARM_OP_COPY && adj->base == base)
-	return adj;
-    }
-
-  return NULL;
-}
-
-/* If OLD_NAME, which is being defined by statement STMT, is an SSA_NAME of a
-   parameter which is to be removed because its value is not used, create a new
-   SSA_NAME relating to a replacement VAR_DECL, replace all uses of the
-   original with it and return it.  If there is no need to re-map, return NULL.
-   ADJUSTMENTS is a pointer to a vector of IPA-SRA adjustments.  */
-
-static tree
-replace_removed_params_ssa_names (tree old_name, gimple *stmt,
-				  ipa_parm_adjustment_vec adjustments)
-{
-  struct ipa_parm_adjustment *adj;
-  tree decl, repl, new_name;
-
-  if (TREE_CODE (old_name) != SSA_NAME)
-    return NULL;
-
-  decl = SSA_NAME_VAR (old_name);
-  if (decl == NULL_TREE
-      || TREE_CODE (decl) != PARM_DECL)
-    return NULL;
-
-  adj = get_adjustment_for_base (adjustments, decl);
-  if (!adj)
-    return NULL;
-
-  repl = get_replaced_param_substitute (adj);
-  new_name = make_ssa_name (repl, stmt);
-  SSA_NAME_OCCURS_IN_ABNORMAL_PHI (new_name)
-    = SSA_NAME_OCCURS_IN_ABNORMAL_PHI (old_name);
-
-  if (dump_file)
-    {
-      fprintf (dump_file, "replacing an SSA name of a removed param ");
-      print_generic_expr (dump_file, old_name);
-      fprintf (dump_file, " with ");
-      print_generic_expr (dump_file, new_name);
-      fprintf (dump_file, "\n");
-    }
-
-  replace_uses_by (old_name, new_name);
-  return new_name;
-}
-
-/* If the statement STMT contains any expressions that need to replaced with a
-   different one as noted by ADJUSTMENTS, do so.  Handle any potential type
-   incompatibilities (GSI is used to accommodate conversion statements and must
-   point to the statement).  Return true iff the statement was modified.  */
-
-static bool
-sra_ipa_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi,
-		       ipa_parm_adjustment_vec adjustments)
-{
-  tree *lhs_p, *rhs_p;
-  bool any;
-
-  if (!gimple_assign_single_p (stmt))
-    return false;
-
-  rhs_p = gimple_assign_rhs1_ptr (stmt);
-  lhs_p = gimple_assign_lhs_ptr (stmt);
-
-  any = ipa_modify_expr (rhs_p, false, adjustments);
-  any |= ipa_modify_expr (lhs_p, false, adjustments);
-  if (any)
-    {
-      tree new_rhs = NULL_TREE;
-
-      if (!useless_type_conversion_p (TREE_TYPE (*lhs_p), TREE_TYPE (*rhs_p)))
-	{
-	  if (TREE_CODE (*rhs_p) == CONSTRUCTOR)
-	    {
-	      /* V_C_Es of constructors can cause trouble (PR 42714).  */
-	      if (is_gimple_reg_type (TREE_TYPE (*lhs_p)))
-		*rhs_p = build_zero_cst (TREE_TYPE (*lhs_p));
-	      else
-		*rhs_p = build_constructor (TREE_TYPE (*lhs_p),
-					    NULL);
-	    }
-	  else
-	    new_rhs = fold_build1_loc (gimple_location (stmt),
-				       VIEW_CONVERT_EXPR, TREE_TYPE (*lhs_p),
-				       *rhs_p);
-	}
-      else if (REFERENCE_CLASS_P (*rhs_p)
-	       && is_gimple_reg_type (TREE_TYPE (*lhs_p))
-	       && !is_gimple_reg (*lhs_p))
-	/* This can happen when an assignment in between two single field
-	   structures is turned into an assignment in between two pointers to
-	   scalars (PR 42237).  */
-	new_rhs = *rhs_p;
-
-      if (new_rhs)
-	{
-	  tree tmp = force_gimple_operand_gsi (gsi, new_rhs, true, NULL_TREE,
-					       true, GSI_SAME_STMT);
-
-	  gimple_assign_set_rhs_from_tree (gsi, tmp);
-	}
-
-      return true;
-    }
-
-  return false;
-}
-
-/* Traverse the function body and all modifications as described in
-   ADJUSTMENTS.  Return true iff the CFG has been changed.  */
-
-bool
-ipa_sra_modify_function_body (ipa_parm_adjustment_vec adjustments)
-{
-  bool cfg_changed = false;
-  basic_block bb;
-
-  FOR_EACH_BB_FN (bb, cfun)
-    {
-      gimple_stmt_iterator gsi;
-
-      for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi))
-	{
-	  gphi *phi = as_a <gphi *> (gsi_stmt (gsi));
-	  tree new_lhs, old_lhs = gimple_phi_result (phi);
-	  new_lhs = replace_removed_params_ssa_names (old_lhs, phi, adjustments);
-	  if (new_lhs)
-	    {
-	      gimple_phi_set_result (phi, new_lhs);
-	      release_ssa_name (old_lhs);
-	    }
-	}
-
-      gsi = gsi_start_bb (bb);
-      while (!gsi_end_p (gsi))
-	{
-	  gimple *stmt = gsi_stmt (gsi);
-	  bool modified = false;
-	  tree *t;
-	  unsigned i;
-
-	  switch (gimple_code (stmt))
-	    {
-	    case GIMPLE_RETURN:
-	      t = gimple_return_retval_ptr (as_a <greturn *> (stmt));
-	      if (*t != NULL_TREE)
-		modified |= ipa_modify_expr (t, true, adjustments);
-	      break;
-
-	    case GIMPLE_ASSIGN:
-	      modified |= sra_ipa_modify_assign (stmt, &gsi, adjustments);
-	      break;
-
-	    case GIMPLE_CALL:
-	      /* Operands must be processed before the lhs.  */
-	      for (i = 0; i < gimple_call_num_args (stmt); i++)
-		{
-		  t = gimple_call_arg_ptr (stmt, i);
-		  modified |= ipa_modify_expr (t, true, adjustments);
-		}
-
-	      if (gimple_call_lhs (stmt))
-		{
-		  t = gimple_call_lhs_ptr (stmt);
-		  modified |= ipa_modify_expr (t, false, adjustments);
-		}
-	      break;
-
-	    case GIMPLE_ASM:
-	      {
-		gasm *asm_stmt = as_a <gasm *> (stmt);
-		for (i = 0; i < gimple_asm_ninputs (asm_stmt); i++)
-		  {
-		    t = &TREE_VALUE (gimple_asm_input_op (asm_stmt, i));
-		    modified |= ipa_modify_expr (t, true, adjustments);
-		  }
-		for (i = 0; i < gimple_asm_noutputs (asm_stmt); i++)
-		  {
-		    t = &TREE_VALUE (gimple_asm_output_op (asm_stmt, i));
-		    modified |= ipa_modify_expr (t, false, adjustments);
-		  }
-	      }
-	      break;
-
-	    default:
-	      break;
-	    }
-
-	  def_operand_p defp;
-	  ssa_op_iter iter;
-	  FOR_EACH_SSA_DEF_OPERAND (defp, stmt, iter, SSA_OP_DEF)
-	    {
-	      tree old_def = DEF_FROM_PTR (defp);
-	      if (tree new_def = replace_removed_params_ssa_names (old_def, stmt,
-								   adjustments))
-		{
-		  SET_DEF (defp, new_def);
-		  release_ssa_name (old_def);
-		  modified = true;
-		}
-	    }
-
-	  if (modified)
-	    {
-	      update_stmt (stmt);
-	      if (maybe_clean_eh_stmt (stmt)
-		  && gimple_purge_dead_eh_edges (gimple_bb (stmt)))
-		cfg_changed = true;
-	    }
-	  gsi_next (&gsi);
-	}
-    }
-
-  return cfg_changed;
-}
-
-/* Call gimple_debug_bind_reset_value on all debug statements describing
-   gimple register parameters that are being removed or replaced.  */
-
-static void
-sra_ipa_reset_debug_stmts (ipa_parm_adjustment_vec adjustments)
-{
-  int i, len;
-  gimple_stmt_iterator *gsip = NULL, gsi;
-
-  if (MAY_HAVE_DEBUG_STMTS && single_succ_p (ENTRY_BLOCK_PTR_FOR_FN (cfun)))
-    {
-      gsi = gsi_after_labels (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
-      gsip = &gsi;
-    }
-  len = adjustments.length ();
-  for (i = 0; i < len; i++)
-    {
-      struct ipa_parm_adjustment *adj;
-      imm_use_iterator ui;
-      gimple *stmt;
-      gdebug *def_temp;
-      tree name, vexpr, copy = NULL_TREE;
-      use_operand_p use_p;
-
-      adj = &adjustments[i];
-      if (adj->op == IPA_PARM_OP_COPY || !is_gimple_reg (adj->base))
-	continue;
-      name = ssa_default_def (cfun, adj->base);
-      vexpr = NULL;
-      if (name)
-	FOR_EACH_IMM_USE_STMT (stmt, ui, name)
-	  {
-	    if (gimple_clobber_p (stmt))
-	      {
-		gimple_stmt_iterator cgsi = gsi_for_stmt (stmt);
-		unlink_stmt_vdef (stmt);
-		gsi_remove (&cgsi, true);
-		release_defs (stmt);
-		continue;
-	      }
-	    /* All other users must have been removed by
-	       ipa_sra_modify_function_body.  */
-	    gcc_assert (is_gimple_debug (stmt));
-	    if (vexpr == NULL && gsip != NULL)
-	      {
-		gcc_assert (TREE_CODE (adj->base) == PARM_DECL);
-		vexpr = make_node (DEBUG_EXPR_DECL);
-		def_temp = gimple_build_debug_source_bind (vexpr, adj->base,
-							   NULL);
-		DECL_ARTIFICIAL (vexpr) = 1;
-		TREE_TYPE (vexpr) = TREE_TYPE (name);
-		SET_DECL_MODE (vexpr, DECL_MODE (adj->base));
-		gsi_insert_before (gsip, def_temp, GSI_SAME_STMT);
-	      }
-	    if (vexpr)
-	      {
-		FOR_EACH_IMM_USE_ON_STMT (use_p, ui)
-		  SET_USE (use_p, vexpr);
-	      }
-	    else
-	      gimple_debug_bind_reset_value (stmt);
-	    update_stmt (stmt);
-	  }
-      /* Create a VAR_DECL for debug info purposes.  */
-      if (!DECL_IGNORED_P (adj->base))
-	{
-	  copy = build_decl (DECL_SOURCE_LOCATION (current_function_decl),
-			     VAR_DECL, DECL_NAME (adj->base),
-			     TREE_TYPE (adj->base));
-	  if (DECL_PT_UID_SET_P (adj->base))
-	    SET_DECL_PT_UID (copy, DECL_PT_UID (adj->base));
-	  TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (adj->base);
-	  TREE_READONLY (copy) = TREE_READONLY (adj->base);
-	  TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (adj->base);
-	  DECL_GIMPLE_REG_P (copy) = DECL_GIMPLE_REG_P (adj->base);
-	  DECL_ARTIFICIAL (copy) = DECL_ARTIFICIAL (adj->base);
-	  DECL_IGNORED_P (copy) = DECL_IGNORED_P (adj->base);
-	  DECL_ABSTRACT_ORIGIN (copy) = DECL_ORIGIN (adj->base);
-	  DECL_SEEN_IN_BIND_EXPR_P (copy) = 1;
-	  SET_DECL_RTL (copy, 0);
-	  TREE_USED (copy) = 1;
-	  DECL_CONTEXT (copy) = current_function_decl;
-	  add_local_decl (cfun, copy);
-	  DECL_CHAIN (copy) =
-	    BLOCK_VARS (DECL_INITIAL (current_function_decl));
-	  BLOCK_VARS (DECL_INITIAL (current_function_decl)) = copy;
-	}
-      if (gsip != NULL && copy && target_for_debug_bind (adj->base))
-	{
-	  gcc_assert (TREE_CODE (adj->base) == PARM_DECL);
-	  if (vexpr)
-	    def_temp = gimple_build_debug_bind (copy, vexpr, NULL);
-	  else
-	    def_temp = gimple_build_debug_source_bind (copy, adj->base,
-						       NULL);
-	  gsi_insert_before (gsip, def_temp, GSI_SAME_STMT);
-	}
-    }
+  return proceed;
 }
 
 /* Return false if all callers have at least as many actual arguments as there
@@ -5233,13 +4869,21 @@  some_callers_have_no_vuse_p (struct cgraph_node *node,
   return false;
 }
 
+struct convert_callers_data
+{
+  /* Declaration of the callee.  */
+  tree callee_fndecl;
+  /* List of adjusted parameters together with their operations.  */
+  vec<ipa_adjusted_param, va_gc> *adj_params;
+};
+
 /* Convert all callers of NODE.  */
 
 static bool
 convert_callers_for_node (struct cgraph_node *node,
 		          void *data)
 {
-  ipa_parm_adjustment_vec *adjustments = (ipa_parm_adjustment_vec *) data;
+  struct convert_callers_data *ccd = (struct convert_callers_data *) data;
   bitmap recomputed_callers = BITMAP_ALLOC (NULL);
   struct cgraph_edge *cs;
 
@@ -5251,8 +4895,8 @@  convert_callers_for_node (struct cgraph_node *node,
 	fprintf (dump_file, "Adjusting call %s -> %s\n",
 		 cs->caller->dump_name (), cs->callee->dump_name ());
 
-      ipa_modify_call_arguments (cs, cs->call_stmt, *adjustments);
-
+      ipa_param_adjustments adjustments (ccd->adj_params, ccd->callee_fndecl);
+      adjustments.modify_call_arguments (cs);
       pop_cfun ();
     }
 
@@ -5265,16 +4909,17 @@  convert_callers_for_node (struct cgraph_node *node,
   return true;
 }
 
-/* Convert all callers of NODE to pass parameters as given in ADJUSTMENTS.  */
+/* Convert all callers of NODE to pass parameters as given in ADJ_PARAMS.  */
 
 static void
 convert_callers (struct cgraph_node *node, tree old_decl,
-		 ipa_parm_adjustment_vec adjustments)
+		 vec<ipa_adjusted_param, va_gc> *adj_params)
 {
   basic_block this_block;
-
-  node->call_for_symbol_and_aliases (convert_callers_for_node,
-				     &adjustments, false);
+  struct convert_callers_data ccd;
+  ccd.callee_fndecl = node->decl;
+  ccd.adj_params = adj_params;
+  node->call_for_symbol_and_aliases (convert_callers_for_node, &ccd, false);
 
   if (!encountered_recursive_call)
     return;
@@ -5295,8 +4940,8 @@  convert_callers (struct cgraph_node *node, tree old_decl,
 	    {
 	      if (dump_file)
 		fprintf (dump_file, "Adjusting recursive call");
-	      gimple_call_set_fndecl (stmt, node->decl);
-	      ipa_modify_call_arguments (NULL, stmt, adjustments);
+	      ipa_param_adjustments adjustments (adj_params);
+	      adjustments.modify_call_arguments (stmt, node->decl);
 	    }
 	}
     }
@@ -5305,10 +4950,11 @@  convert_callers (struct cgraph_node *node, tree old_decl,
 }
 
 /* Perform all the modification required in IPA-SRA for NODE to have parameters
-   as given in ADJUSTMENTS.  Return true iff the CFG has been changed.  */
+   as given in ADJ_PARAMS.  Return true iff the CFG has been changed.  */
 
 static bool
-modify_function (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments)
+modify_function (struct cgraph_node *node,
+		 vec<ipa_adjusted_param, va_gc> *adj_params)
 {
   struct cgraph_node *new_node;
   bool cfg_changed;
@@ -5325,13 +4971,12 @@  modify_function (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments)
 						   NULL, false, NULL, NULL,
 						   "isra");
   redirect_callers.release ();
+  new_node->make_local ();
 
   push_cfun (DECL_STRUCT_FUNCTION (new_node->decl));
-  ipa_modify_formal_parameters (current_function_decl, adjustments);
-  cfg_changed = ipa_sra_modify_function_body (adjustments);
-  sra_ipa_reset_debug_stmts (adjustments);
-  convert_callers (new_node, node->decl, adjustments);
-  new_node->make_local ();
+  convert_callers (new_node, node->decl, adj_params);
+  ipa_param_body_adjustments body_adj (adj_params, new_node->decl);
+  cfg_changed = body_adj.perform_cfun_body_modifications ();
   return cfg_changed;
 }
 
@@ -5497,7 +5142,7 @@  static unsigned int
 ipa_early_sra (void)
 {
   struct cgraph_node *node = cgraph_node::get (current_function_decl);
-  ipa_parm_adjustment_vec adjustments;
+  vec<ipa_adjusted_param, va_gc> *adj_params;
   int ret = 0;
 
   if (!ipa_sra_preliminary_function_checks (node))
@@ -5552,17 +5197,15 @@  ipa_early_sra (void)
       goto out;
     }
 
-  adjustments = analyze_all_param_acesses ();
-  if (!adjustments.exists ())
+  if (!analyze_all_param_acesses (&adj_params))
     goto out;
   if (dump_file)
-    ipa_dump_param_adjustments (dump_file, adjustments, current_function_decl);
+    ipa_dump_adjusted_parameters (dump_file, adj_params);
 
-  if (modify_function (node, adjustments))
+  if (modify_function (node, adj_params))
     ret = TODO_update_ssa | TODO_cleanup_cfg;
   else
     ret = TODO_update_ssa;
-  adjustments.release ();
 
   statistics_counter_event (cfun, "Unused parameters deleted",
 			    sra_stats.deleted_unused_parameters);