diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 597dc01328b..9573f58221c 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -2528,7 +2528,7 @@ GTFILES = $(CPPLIB_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/cgraph.c b/gcc/cgraph.c
index ea8ab38d806..a4e2ba1ecb1 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -1342,7 +1342,7 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
   if (flag_checking && decl)
     {
       cgraph_node *node = cgraph_node::get (decl);
-      gcc_assert (!node || !node->clone.combined_args_to_skip);
+      gcc_assert (!node || !node->clone.param_adjustments);
     }
 
   if (symtab->dump_file)
@@ -1350,25 +1350,36 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
       fprintf (symtab->dump_file, "updating call of %s -> %s: ",
 	       e->caller->dump_name (), e->callee->dump_name ());
       print_gimple_stmt (symtab->dump_file, e->call_stmt, 0, dump_flags);
-      if (e->callee->clone.combined_args_to_skip)
+      if (e->callee->clone.param_adjustments)
+	e->callee->clone.param_adjustments->dump (symtab->dump_file);
+      unsigned performed_len
+	= vec_safe_length (e->caller->clone.performed_splits);
+      if (performed_len > 0)
+	fprintf (symtab->dump_file, "Performed splits records:\n");
+      for (unsigned i = 0; i < performed_len; i++)
 	{
-	  fprintf (symtab->dump_file, " combined args to skip: ");
-	  dump_bitmap (symtab->dump_file,
-		       e->callee->clone.combined_args_to_skip);
+	  ipa_param_performed_split *sm
+	    = &(*e->caller->clone.performed_splits)[i];
+	  print_node_brief (symtab->dump_file, "  dummy_decl: ", sm->dummy_decl,
+			    TDF_UID);
+	  fprintf (symtab->dump_file, ", unit_offset: %u\n", sm->unit_offset);
 	}
     }
 
-  if (e->callee->clone.combined_args_to_skip)
+  if (ipa_param_adjustments *padjs = e->callee->clone.param_adjustments)
     {
-      int lp_nr;
+      /* We need to defer cleaning EH info on the new statement to
+         fixup-cfg.  We may not have dominator information at this point
+	 and thus would end up with unreachable blocks and have no way
+	 to communicate that we need to run CFG cleanup then.  */
+      int lp_nr = lookup_stmt_eh_lp (e->call_stmt);
+      if (lp_nr != 0)
+	remove_stmt_from_eh_lp (e->call_stmt);
 
-      new_stmt = e->call_stmt;
-      if (e->callee->clone.combined_args_to_skip)
-	new_stmt
-	  = gimple_call_copy_skip_args (new_stmt,
-					e->callee->clone.combined_args_to_skip);
       tree old_fntype = gimple_call_fntype (e->call_stmt);
-      gimple_call_set_fndecl (new_stmt, e->callee->decl);
+      new_stmt = padjs->modify_call (e->call_stmt,
+				     e->caller->clone.performed_splits,
+				     e->callee->decl, false);
       cgraph_node *origin = e->callee;
       while (origin->clone_of)
 	origin = origin->clone_of;
@@ -1379,92 +1390,12 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
 	gimple_call_set_fntype (new_stmt, TREE_TYPE (e->callee->decl));
       else
 	{
-	  bitmap skip = e->callee->clone.combined_args_to_skip;
-	  tree t = cgraph_build_function_type_skip_args (old_fntype, skip,
-							 false);
-	  gimple_call_set_fntype (new_stmt, t);
-	}
-
-      if (gimple_vdef (new_stmt)
-	  && TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME)
-	SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
-
-      gsi = gsi_for_stmt (e->call_stmt);
-
-      /* For optimized away parameters, add on the caller side
-	 before the call
-	 DEBUG D#X => parm_Y(D)
-	 stmts and associate D#X with parm in decl_debug_args_lookup
-	 vector to say for debug info that if parameter parm had been passed,
-	 it would have value parm_Y(D).  */
-      if (e->callee->clone.combined_args_to_skip && MAY_HAVE_DEBUG_BIND_STMTS)
-	{
-	  vec<tree, va_gc> **debug_args
-	    = decl_debug_args_lookup (e->callee->decl);
-	  tree old_decl = gimple_call_fndecl (e->call_stmt);
-	  if (debug_args && old_decl)
-	    {
-	      tree parm;
-	      unsigned i = 0, num;
-	      unsigned len = vec_safe_length (*debug_args);
-	      unsigned nargs = gimple_call_num_args (e->call_stmt);
-	      for (parm = DECL_ARGUMENTS (old_decl), num = 0;
-		   parm && num < nargs;
-		   parm = DECL_CHAIN (parm), num++)
-		if (bitmap_bit_p (e->callee->clone.combined_args_to_skip, num)
-		    && is_gimple_reg (parm))
-		  {
-		    unsigned last = i;
-
-		    while (i < len && (**debug_args)[i] != DECL_ORIGIN (parm))
-		      i += 2;
-		    if (i >= len)
-		      {
-			i = 0;
-			while (i < last
-			       && (**debug_args)[i] != DECL_ORIGIN (parm))
-			  i += 2;
-			if (i >= last)
-			  continue;
-		      }
-		    tree ddecl = (**debug_args)[i + 1];
-		    tree arg = gimple_call_arg (e->call_stmt, num);
-		    if (!useless_type_conversion_p (TREE_TYPE (ddecl),
-						    TREE_TYPE (arg)))
-		      {
-			tree rhs1;
-			if (!fold_convertible_p (TREE_TYPE (ddecl), arg))
-			  continue;
-			if (TREE_CODE (arg) == SSA_NAME
-			    && gimple_assign_cast_p (SSA_NAME_DEF_STMT (arg))
-			    && (rhs1
-				= gimple_assign_rhs1 (SSA_NAME_DEF_STMT (arg)))
-			    && useless_type_conversion_p (TREE_TYPE (ddecl),
-							  TREE_TYPE (rhs1)))
-			  arg = rhs1;
-			else
-			  arg = fold_convert (TREE_TYPE (ddecl), arg);
-		      }
-
-		    gimple *def_temp
-		      = gimple_build_debug_bind (ddecl, unshare_expr (arg),
-						 e->call_stmt);
-		    gsi_insert_before (&gsi, def_temp, GSI_SAME_STMT);
-		  }
-	    }
+	  tree new_fntype = padjs->build_new_function_type (old_fntype, true);
+	  gimple_call_set_fntype (new_stmt, new_fntype);
 	}
 
-      gsi_replace (&gsi, new_stmt, false);
-      /* We need to defer cleaning EH info on the new statement to
-         fixup-cfg.  We may not have dominator information at this point
-	 and thus would end up with unreachable blocks and have no way
-	 to communicate that we need to run CFG cleanup then.  */
-      lp_nr = lookup_stmt_eh_lp (e->call_stmt);
       if (lp_nr != 0)
-	{
-	  remove_stmt_from_eh_lp (e->call_stmt);
-	  add_stmt_to_eh_lp (new_stmt, lp_nr);
-	}
+	add_stmt_to_eh_lp (new_stmt, lp_nr);
     }
   else
     {
@@ -3014,8 +2945,8 @@ clone_of_p (cgraph_node *node, cgraph_node *node2)
 	return true;
       node = node->callees->callee->ultimate_alias_target ();
 
-      if (!node2->clone.args_to_skip
-	  || !bitmap_bit_p (node2->clone.args_to_skip, 0))
+      if (!node2->clone.param_adjustments
+	  || node2->clone.param_adjustments->first_param_intact_p ())
 	return false;
       if (node2->former_clone_of == node->decl)
 	return true;
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 4c54210123a..1da6cab54b0 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -24,6 +24,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "profile-count.h"
 #include "ipa-ref.h"
 #include "plugin-api.h"
+#include "ipa-param-manipulation.h"
 
 extern void debuginfo_early_init (void);
 extern void debuginfo_init (void);
@@ -740,23 +741,31 @@ struct GTY(()) cgraph_global_info {
    will be replaced by another tree while versioning.  */
 struct GTY(()) ipa_replace_map
 {
-  /* The tree that will be replaced.  */
-  tree old_tree;
   /* The new (replacing) tree.  */
   tree new_tree;
   /* Parameter number to replace, when old_tree is NULL.  */
   int parm_num;
-  /* True when a substitution should be done, false otherwise.  */
-  bool replace_p;
-  /* True when we replace a reference to old_tree.  */
-  bool ref_p;
 };
 
 struct GTY(()) cgraph_clone_info
 {
+  /* Constants discovered by IPA-CP, i.e. which parameter should be replaced
+     with what.  */
   vec<ipa_replace_map *, va_gc> *tree_map;
-  bitmap args_to_skip;
-  bitmap combined_args_to_skip;
+  /* Parameter modification that IPA-SRA decided to perform.  */
+  ipa_param_adjustments *param_adjustments;
+  /* Lists of dummy-decl and offset pairs representing split formal parameters
+     in the caller.  Offsets of all new replacements are enumerated, those
+     coming from the same original parameter have the same dummy decl stored
+     along with them.
+
+     Dummy decls sit in call statement arguments followed by new parameter
+     decls (or their SSA names) in between (caller) clone materialization and
+     call redirection.  Redirection then recognizes the dummy variable and
+     together with the stored offsets can reconstruct what exactly the new
+     parameter decls represent and can leave in place only those that the
+     callee expects.  */
+  vec<ipa_param_performed_split, va_gc> *performed_splits;
 };
 
 enum cgraph_simd_clone_arg_type
@@ -976,15 +985,16 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
 			     vec<cgraph_edge *> redirect_callers,
 			     bool call_duplication_hook,
 			     cgraph_node *new_inlined_to,
-			     bitmap args_to_skip, const char *suffix = NULL);
+			     ipa_param_adjustments *param_adjustments,
+			     const char *suffix = NULL);
 
   /* Create callgraph node clone with new declaration.  The actual body will be
      copied later at compilation stage.  The name of the new clone will be
      constructed from the name of the original node, SUFFIX and NUM_SUFFIX.  */
   cgraph_node *create_virtual_clone (vec<cgraph_edge *> redirect_callers,
 				     vec<ipa_replace_map *, va_gc> *tree_map,
-				     bitmap args_to_skip, const char * suffix,
-				     unsigned num_suffix);
+				     ipa_param_adjustments *param_adjustments,
+				     const char * suffix, unsigned num_suffix);
 
   /* cgraph node being removed from symbol table; see if its entry can be
    replaced by other inline clone.  */
@@ -1033,9 +1043,10 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
      Return the new version's cgraph node.  */
   cgraph_node *create_version_clone_with_body
     (vec<cgraph_edge *> redirect_callers,
-     vec<ipa_replace_map *, va_gc> *tree_map, bitmap args_to_skip,
-     bool skip_return, bitmap bbs_to_copy, basic_block new_entry_block,
-     const char *clone_name, tree target_attributes = NULL_TREE);
+     vec<ipa_replace_map *, va_gc> *tree_map,
+     ipa_param_adjustments *param_adjustments,
+     bitmap bbs_to_copy, basic_block new_entry_block, const char *clone_name,
+     tree target_attributes = NULL_TREE);
 
   /* Insert a new cgraph_function_version_info node into cgraph_fnver_htab
      corresponding to cgraph_node.  */
@@ -2459,14 +2470,12 @@ tree clone_function_name (tree decl, const char *suffix,
 tree clone_function_name (tree decl, const char *suffix);
 
 void tree_function_versioning (tree, tree, vec<ipa_replace_map *, va_gc> *,
-			       bool, bitmap, bool, bitmap, basic_block);
+			       ipa_param_adjustments *,
+			       bool, bitmap, basic_block);
 
 void dump_callgraph_transformation (const cgraph_node *original,
 				    const cgraph_node *clone,
 				    const char *suffix);
-tree cgraph_build_function_type_skip_args (tree orig_type, bitmap args_to_skip,
-					   bool skip_return);
-
 /* In cgraphbuild.c  */
 int compute_call_stmt_bb_frequency (tree, basic_block bb);
 void record_references_in_initializer (tree, bool);
diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
index fa753697c78..909407b9a71 100644
--- a/gcc/cgraphclones.c
+++ b/gcc/cgraphclones.c
@@ -142,96 +142,6 @@ cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
   return new_edge;
 }
 
-/* Build variant of function type ORIG_TYPE skipping ARGS_TO_SKIP and the
-   return value if SKIP_RETURN is true.  */
-
-tree
-cgraph_build_function_type_skip_args (tree orig_type, bitmap args_to_skip,
-				      bool skip_return)
-{
-  tree new_type = NULL;
-  tree args, new_args = NULL;
-  tree new_reversed;
-  int i = 0;
-
-  for (args = TYPE_ARG_TYPES (orig_type); args && args != void_list_node;
-       args = TREE_CHAIN (args), i++)
-    if (!args_to_skip || !bitmap_bit_p (args_to_skip, i))
-      new_args = tree_cons (NULL_TREE, TREE_VALUE (args), new_args);
-
-  new_reversed = nreverse (new_args);
-  if (args)
-    {
-      if (new_reversed)
-        TREE_CHAIN (new_args) = void_list_node;
-      else
-	new_reversed = void_list_node;
-    }
-
-  /* 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.  */
-  if (TREE_CODE (orig_type) != METHOD_TYPE
-      || !args_to_skip
-      || !bitmap_bit_p (args_to_skip, 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);
-    }
-
-  if (skip_return)
-    TREE_TYPE (new_type) = void_type_node;
-
-  return new_type;
-}
-
-/* Build variant of function decl ORIG_DECL skipping ARGS_TO_SKIP and the
-   return value if SKIP_RETURN is true.
-
-   Arguments from DECL_ARGUMENTS list can't be removed now, since they are
-   linked by TREE_CHAIN directly.  The caller is responsible for eliminating
-   them when they are being duplicated (i.e. copy_arguments_for_versioning).  */
-
-static tree
-build_function_decl_skip_args (tree orig_decl, bitmap args_to_skip,
-			       bool skip_return)
-{
-  tree new_decl = copy_node (orig_decl);
-  tree new_type;
-
-  new_type = TREE_TYPE (orig_decl);
-  if (prototype_p (new_type)
-      || (skip_return && !VOID_TYPE_P (TREE_TYPE (new_type))))
-    new_type
-      = cgraph_build_function_type_skip_args (new_type, args_to_skip,
-					      skip_return);
-  TREE_TYPE (new_decl) = new_type;
-
-  /* For declarations setting DECL_VINDEX (i.e. methods)
-     we expect first argument to be THIS pointer.   */
-  if (args_to_skip && bitmap_bit_p (args_to_skip, 0))
-    DECL_VINDEX (new_decl) = NULL_TREE;
-
-  /* When signature changes, we need to clear builtin info.  */
-  if (fndecl_built_in_p (new_decl)
-      && args_to_skip
-      && !bitmap_empty_p (args_to_skip))
-    set_decl_built_in_function (new_decl, NOT_BUILT_IN, 0);
-  /* The FE might have information and assumptions about the other
-     arguments.  */
-  DECL_LANG_SPECIFIC (new_decl) = NULL;
-  return new_decl;
-}
-
 /* Set flags of NEW_NODE and its decl.  NEW_NODE is a newly created private
    clone or its thunk.  */
 
@@ -281,35 +191,21 @@ duplicate_thunk_for_node (cgraph_node *thunk, cgraph_node *node)
       return cs->caller;
 
   tree new_decl;
-  if (!node->clone.args_to_skip)
-    new_decl = copy_node (thunk->decl);
-  else
+  if (node->clone.param_adjustments)
     {
       /* We do not need to duplicate this_adjusting thunks if we have removed
 	 this.  */
       if (thunk->thunk.this_adjusting
-	  && bitmap_bit_p (node->clone.args_to_skip, 0))
+	  && !node->clone.param_adjustments->first_param_intact_p ())
 	return node;
 
-      new_decl = build_function_decl_skip_args (thunk->decl,
-						node->clone.args_to_skip,
-						false);
-    }
-
-  tree *link = &DECL_ARGUMENTS (new_decl);
-  int i = 0;
-  for (tree pd = DECL_ARGUMENTS (thunk->decl); pd; pd = DECL_CHAIN (pd), i++)
-    {
-      if (!node->clone.args_to_skip
-	  || !bitmap_bit_p (node->clone.args_to_skip, i))
-	{
-	  tree nd = copy_node (pd);
-	  DECL_CONTEXT (nd) = new_decl;
-	  *link = nd;
-	  link = &DECL_CHAIN (nd);
-	}
+      new_decl = copy_node (thunk->decl);
+      ipa_param_body_adjustments body_adj (node->clone.param_adjustments,
+					   new_decl);
+      body_adj.modify_formal_parameters ();
     }
-  *link = NULL_TREE;
+  else
+    new_decl = copy_node (thunk->decl);
 
   gcc_checking_assert (!DECL_STRUCT_FUNCTION (new_decl));
   gcc_checking_assert (!DECL_INITIAL (new_decl));
@@ -331,8 +227,7 @@ duplicate_thunk_for_node (cgraph_node *thunk, cgraph_node *node)
   new_thunk->thunk = thunk->thunk;
   new_thunk->unique_name = in_lto_p;
   new_thunk->former_clone_of = thunk->decl;
-  new_thunk->clone.args_to_skip = node->clone.args_to_skip;
-  new_thunk->clone.combined_args_to_skip = node->clone.combined_args_to_skip;
+  new_thunk->clone.param_adjustments = node->clone.param_adjustments;
 
   cgraph_edge *e = new_thunk->create_edge (node, NULL, new_thunk->count);
   symtab->call_edge_duplication_hooks (thunk->callees, e);
@@ -415,7 +310,11 @@ dump_callgraph_transformation (const cgraph_node *original,
    If the new node is being inlined into another one, NEW_INLINED_TO should be
    the outline function the new one is (even indirectly) inlined to.  All hooks
    will see this in node's global.inlined_to, when invoked.  Can be NULL if the
-   node is not inlined.  */
+   node is not inlined.
+
+   If PARAM_ADJUSTMENTS is non-NULL, the parameter manipulation information
+   will be overwritten by the new structure.  Otherwise the new node will
+   share parameter manipulation information with the original node.  */
 
 cgraph_node *
 cgraph_node::create_clone (tree new_decl, profile_count prof_count,
@@ -423,7 +322,8 @@ cgraph_node::create_clone (tree new_decl, profile_count prof_count,
 			   vec<cgraph_edge *> redirect_callers,
 			   bool call_duplication_hook,
 			   cgraph_node *new_inlined_to,
-			   bitmap args_to_skip, const char *suffix)
+			   ipa_param_adjustments *param_adjustments,
+			   const char *suffix)
 {
   cgraph_node *new_node = symtab->create_empty ();
   cgraph_edge *e;
@@ -467,19 +367,13 @@ cgraph_node::create_clone (tree new_decl, profile_count prof_count,
   new_node->merged_comdat = merged_comdat;
   new_node->thunk = thunk;
 
+  if (param_adjustments)
+    new_node->clone.param_adjustments = param_adjustments;
+  else
+    new_node->clone.param_adjustments = clone.param_adjustments;
   new_node->clone.tree_map = NULL;
-  new_node->clone.args_to_skip = args_to_skip;
+  new_node->clone.performed_splits = vec_safe_copy (clone.performed_splits);
   new_node->split_part = split_part;
-  if (!args_to_skip)
-    new_node->clone.combined_args_to_skip = clone.combined_args_to_skip;
-  else if (clone.combined_args_to_skip)
-    {
-      new_node->clone.combined_args_to_skip = BITMAP_GGC_ALLOC ();
-      bitmap_ior (new_node->clone.combined_args_to_skip,
-		  clone.combined_args_to_skip, args_to_skip);
-    }
-  else
-    new_node->clone.combined_args_to_skip = args_to_skip;
 
   FOR_EACH_VEC_ELT (redirect_callers, i, e)
     {
@@ -621,8 +515,8 @@ clone_function_name (tree decl, const char *suffix)
 cgraph_node *
 cgraph_node::create_virtual_clone (vec<cgraph_edge *> redirect_callers,
 				   vec<ipa_replace_map *, va_gc> *tree_map,
-				   bitmap args_to_skip, const char * suffix,
-				   unsigned num_suffix)
+				   ipa_param_adjustments *param_adjustments,
+				   const char * suffix, unsigned num_suffix)
 {
   tree old_decl = decl;
   cgraph_node *new_node = NULL;
@@ -632,13 +526,16 @@ cgraph_node::create_virtual_clone (vec<cgraph_edge *> redirect_callers,
   char *name;
 
   gcc_checking_assert (local.versionable);
-  gcc_assert (local.can_change_signature || !args_to_skip);
+  /* TODO: It would be nice if we could recognize that param_adjustments do not
+     actually perform any changes, but at the moment let's require it simply
+     does not exist.  */
+  gcc_assert (local.can_change_signature || !param_adjustments);
 
   /* Make a new FUNCTION_DECL tree node */
-  if (!args_to_skip)
+  if (!param_adjustments)
     new_decl = copy_node (old_decl);
   else
-    new_decl = build_function_decl_skip_args (old_decl, args_to_skip, false);
+    new_decl = param_adjustments->adjust_decl (old_decl);
 
   /* These pointers represent function body and will be populated only when clone
      is materialized.  */
@@ -662,7 +559,8 @@ cgraph_node::create_virtual_clone (vec<cgraph_edge *> redirect_callers,
   SET_DECL_RTL (new_decl, NULL);
 
   new_node = create_clone (new_decl, count, false,
-			   redirect_callers, false, NULL, args_to_skip, suffix);
+			   redirect_callers, false, NULL, param_adjustments,
+			   suffix);
 
   /* Update the properties.
      Make clone visible only within this translation unit.  Make sure
@@ -1021,9 +919,10 @@ cgraph_node::create_version_clone (tree new_decl,
 cgraph_node *
 cgraph_node::create_version_clone_with_body
   (vec<cgraph_edge *> redirect_callers,
-   vec<ipa_replace_map *, va_gc> *tree_map, bitmap args_to_skip,
-   bool skip_return, bitmap bbs_to_copy, basic_block new_entry_block,
-   const char *suffix, tree target_attributes)
+   vec<ipa_replace_map *, va_gc> *tree_map,
+   ipa_param_adjustments *param_adjustments,
+   bitmap bbs_to_copy, basic_block new_entry_block, const char *suffix,
+   tree target_attributes)
 {
   tree old_decl = decl;
   cgraph_node *new_version_node = NULL;
@@ -1032,14 +931,16 @@ cgraph_node::create_version_clone_with_body
   if (!tree_versionable_function_p (old_decl))
     return NULL;
 
-  gcc_assert (local.can_change_signature || !args_to_skip);
+  /* TODO: Restore an assert that we do not change signature if
+     local.can_change_signature is false.  We cannot just check that
+     param_adjustments is NULL because unfortunately ipa-split removes return
+     values from such functions.  */
 
   /* Make a new FUNCTION_DECL tree node for the new version. */
-  if (!args_to_skip && !skip_return)
-    new_decl = copy_node (old_decl);
+  if (param_adjustments)
+    new_decl = param_adjustments->adjust_decl (old_decl);
   else
-    new_decl
-      = build_function_decl_skip_args (old_decl, args_to_skip, skip_return);
+    new_decl = copy_node (old_decl);
 
   /* Generate a new name for the new version. */
   DECL_NAME (new_decl) = clone_function_name_numbered (old_decl, suffix);
@@ -1076,8 +977,8 @@ cgraph_node::create_version_clone_with_body
     new_version_node->ipa_transforms_to_apply
       = ipa_transforms_to_apply.copy ();
   /* Copy the OLD_VERSION_NODE function tree to the new version.  */
-  tree_function_versioning (old_decl, new_decl, tree_map, false, args_to_skip,
-			    skip_return, bbs_to_copy, new_entry_block);
+  tree_function_versioning (old_decl, new_decl, tree_map, param_adjustments,
+			    false, bbs_to_copy, new_entry_block);
 
   /* Update the new version's properties.
      Make The new version visible only within this translation unit.  Make sure
@@ -1117,9 +1018,8 @@ cgraph_materialize_clone (cgraph_node *node)
     node->former_clone_of = node->clone_of->former_clone_of;
   /* Copy the OLD_VERSION_NODE function tree to the new version.  */
   tree_function_versioning (node->clone_of->decl, node->decl,
-  			    node->clone.tree_map, true,
-			    node->clone.args_to_skip, false,
-			    NULL, NULL);
+			    node->clone.tree_map, node->clone.param_adjustments,
+			    true, NULL, NULL);
   if (symtab->dump_file)
     {
       dump_function_to_file (node->clone_of->decl, symtab->dump_file,
@@ -1194,28 +1094,15 @@ symbol_table::materialize_all_clones (void)
 			    {
 			      ipa_replace_map *replace_info;
 			      replace_info = (*node->clone.tree_map)[i];
-			      print_generic_expr (symtab->dump_file,
-						  replace_info->old_tree);
-			      fprintf (symtab->dump_file, " -> ");
+			      fprintf (symtab->dump_file, "%i -> ",
+				       (*node->clone.tree_map)[i]->parm_num);
 			      print_generic_expr (symtab->dump_file,
 						  replace_info->new_tree);
-			      fprintf (symtab->dump_file, "%s%s;",
-			      	       replace_info->replace_p ? "(replace)":"",
-				       replace_info->ref_p ? "(ref)":"");
 			    }
 			  fprintf (symtab->dump_file, "\n");
 			}
-		      if (node->clone.args_to_skip)
-			{
-			  fprintf (symtab->dump_file, "   args_to_skip: ");
-			  dump_bitmap (symtab->dump_file,
-				       node->clone.args_to_skip);
-			}
-		      if (node->clone.args_to_skip)
-			{
-			  fprintf (symtab->dump_file, "   combined_args_to_skip:");
-			  dump_bitmap (symtab->dump_file, node->clone.combined_args_to_skip);
-			}
+		      if (node->clone.param_adjustments)
+			node->clone.param_adjustments->dump (symtab->dump_file);
 		    }
 		  cgraph_materialize_clone (node);
 		  stabilized = false;
diff --git a/gcc/coretypes.h b/gcc/coretypes.h
index eac2f3931aa..53e47450708 100644
--- a/gcc/coretypes.h
+++ b/gcc/coretypes.h
@@ -141,6 +141,7 @@ struct gomp_teams;
 struct symtab_node;
   struct cgraph_node;
   struct varpool_node;
+struct cgraph_edge;
 
 union section;
 typedef union section section;
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 0046064fea1..52d5cfad1be 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -1184,7 +1184,10 @@ initialize_node_lattices (struct cgraph_node *node)
   int i;
 
   gcc_checking_assert (node->has_gimple_body_p ());
-  if (node->local.local)
+
+  if (!ipa_get_param_count (info))
+    disable = true;
+  else if (node->local.local)
     {
       int caller_count = 0;
       node->call_for_symbol_thunks_and_aliases (count_callers, &caller_count,
@@ -1206,32 +1209,72 @@ initialize_node_lattices (struct cgraph_node *node)
 	disable = true;
     }
 
-  for (i = 0; i < ipa_get_param_count (info); i++)
+  if (dump_file && (dump_flags & TDF_DETAILS)
+      && !node->alias && !node->thunk.thunk_p)
     {
-      class ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
-      plats->m_value_range.init ();
+      fprintf (dump_file, "Initializing lattices of %s\n",
+	       node->dump_name ());
+      if (disable || variable)
+	fprintf (dump_file, "  Marking all lattices as %s\n",
+		 disable ? "BOTTOM" : "VARIABLE");
     }
 
-  if (disable || variable)
+  auto_vec<bool, 16> surviving_params;
+  bool pre_modified = false;
+  if (!disable && node->clone.param_adjustments)
     {
-      for (i = 0; i < ipa_get_param_count (info); i++)
+      /* At the moment all IPA optimizations should use the number of
+	 parameters of the prevailing decl as the m_always_copy_start.
+	 Handling any other value would complicate the code below, so for the
+	 time bing let's only assert it is so.  */
+      gcc_assert ((node->clone.param_adjustments->m_always_copy_start
+		   == ipa_get_param_count (info))
+		  || node->clone.param_adjustments->m_always_copy_start < 0);
+
+      pre_modified = true;
+      node->clone.param_adjustments->get_surviving_params (&surviving_params);
+
+      if (dump_file && (dump_flags & TDF_DETAILS)
+	  && !node->alias && !node->thunk.thunk_p)
 	{
-	  class ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
-	  if (disable)
+	  bool first = true;
+	  for (int j = 0; j < ipa_get_param_count (info); j++)
 	    {
-	      plats->itself.set_to_bottom ();
-	      plats->ctxlat.set_to_bottom ();
-	      set_agg_lats_to_bottom (plats);
-	      plats->bits_lattice.set_to_bottom ();
-	      plats->m_value_range.set_to_bottom ();
+	      if (j < (int) surviving_params.length ()
+		  && surviving_params[j])
+		continue;
+	      if (first)
+		{
+		  fprintf (dump_file,
+			   "  The following parameters are dead on arrival:");
+		  first = false;
+		}
+	      fprintf (dump_file, " %u", j);
 	    }
-	  else
+	  if (!first)
+	      fprintf (dump_file, "\n");
+	}
+    }
+
+  for (i = 0; i < ipa_get_param_count (info); i++)
+    {
+      ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
+      if (disable
+	  || (pre_modified && (surviving_params.length () <= (unsigned) i
+			       || !surviving_params[i])))
+	{
+	  plats->itself.set_to_bottom ();
+	  plats->ctxlat.set_to_bottom ();
+	  set_agg_lats_to_bottom (plats);
+	  plats->bits_lattice.set_to_bottom ();
+	  plats->m_value_range.set_to_bottom ();
+	}
+      else
+	{
+	  plats->m_value_range.init ();
+	  if (variable)
 	    set_all_contains_variable (plats);
 	}
-      if (dump_file && (dump_flags & TDF_DETAILS)
-	  && !node->alias && !node->thunk.thunk_p)
-	fprintf (dump_file, "Marking all lattices of %s as %s\n",
-		 node->dump_name (), disable ? "BOTTOM" : "VARIABLE");
     }
 
   for (ie = node->indirect_calls; ie; ie = ie->next_callee)
@@ -3656,12 +3699,8 @@ get_replacement_map (class ipa_node_params *info, tree value, int parm_num)
       print_generic_expr (dump_file, value);
       fprintf (dump_file, "\n");
     }
-  replace_map->old_tree = NULL;
   replace_map->parm_num = parm_num;
   replace_map->new_tree = value;
-  replace_map->replace_p = true;
-  replace_map->ref_p = false;
-
   return replace_map;
 }
 
@@ -3799,6 +3838,35 @@ update_specialized_profile (struct cgraph_node *new_node,
     dump_profile_updates (orig_node, new_node);
 }
 
+/* Return true if we would like to remove a parameter from NODE when cloning it
+   with KNOWN_CSTS scalar constants.  */
+
+static bool
+want_remove_some_param_p (cgraph_node *node, vec<tree> known_csts)
+{
+  auto_vec<bool, 16> surviving;
+  bool filled_vec = false;
+  ipa_node_params *info = IPA_NODE_REF (node);
+  int i, count = ipa_get_param_count (info);
+
+  for (i = 0; i < count; i++)
+    {
+      if (!known_csts[i] && ipa_is_param_used (info, i))
+       continue;
+
+      if (!filled_vec)
+       {
+         if (!node->clone.param_adjustments)
+           return true;
+         node->clone.param_adjustments->get_surviving_params (&surviving);
+         filled_vec = true;
+       }
+      if (surviving.length() < (unsigned) i &&  surviving[i])
+       return true;
+    }
+  return false;
+}
+
 /* Create a specialized version of NODE with known constants in KNOWN_CSTS,
    known contexts in KNOWN_CONTEXTS and known aggregate values in AGGVALS and
    redirect all edges in CALLERS to it.  */
@@ -3812,31 +3880,65 @@ create_specialized_node (struct cgraph_node *node,
 {
   class ipa_node_params *new_info, *info = IPA_NODE_REF (node);
   vec<ipa_replace_map *, va_gc> *replace_trees = NULL;
+  vec<ipa_adjusted_param, va_gc> *new_params = NULL;
   struct ipa_agg_replacement_value *av;
   struct cgraph_node *new_node;
   int i, count = ipa_get_param_count (info);
-  bitmap args_to_skip;
-
+  ipa_param_adjustments *old_adjustments = node->clone.param_adjustments;
+  ipa_param_adjustments *new_adjustments;
   gcc_assert (!info->ipcp_orig_node);
+  gcc_assert (node->local.can_change_signature
+	      || !old_adjustments);
 
-  if (node->local.can_change_signature)
+  if (old_adjustments)
     {
-      args_to_skip = BITMAP_GGC_ALLOC ();
-      for (i = 0; i < count; i++)
+      /* At the moment all IPA optimizations should use the number of
+	 parameters of the prevailing decl as the m_always_copy_start.
+	 Handling any other value would complicate the code below, so for the
+	 time bing let's only assert it is so.  */
+      gcc_assert (old_adjustments->m_always_copy_start == count
+		  || old_adjustments->m_always_copy_start < 0);
+      int old_adj_count = vec_safe_length (old_adjustments->m_adj_params);
+      for (i = 0; i < old_adj_count; i++)
 	{
-	  tree t = known_csts[i];
+	  ipa_adjusted_param *old_adj = &(*old_adjustments->m_adj_params)[i];
+	  if (!node->local.can_change_signature
+	      || old_adj->op != IPA_PARAM_OP_COPY
+	      || (!known_csts[old_adj->base_index]
+		  && ipa_is_param_used (info, old_adj->base_index)))
+	    {
+	      ipa_adjusted_param new_adj = *old_adj;
 
-	  if (t || !ipa_is_param_used (info, i))
-	    bitmap_set_bit (args_to_skip, i);
+	      new_adj.prev_clone_adjustment = true;
+	      new_adj.prev_clone_index = i;
+	      vec_safe_push (new_params, new_adj);
+	    }
 	}
+      bool skip_return = old_adjustments->m_skip_return;
+      new_adjustments = (new (ggc_alloc <ipa_param_adjustments> ())
+			 ipa_param_adjustments (new_params, count,
+						skip_return));
     }
-  else
+  else if (node->local.can_change_signature
+	   && want_remove_some_param_p (node, known_csts))
     {
-      args_to_skip = NULL;
-      if (dump_file && (dump_flags & TDF_DETAILS))
-	fprintf (dump_file, "      cannot change function signature\n");
+      ipa_adjusted_param adj;
+      memset (&adj, 0, sizeof (adj));
+      adj.op = IPA_PARAM_OP_COPY;
+      for (i = 0; i < count; i++)
+	if (!known_csts[i] && ipa_is_param_used (info, i))
+	  {
+	    adj.base_index = i;
+	    adj.prev_clone_index = i;
+	    vec_safe_push (new_params, adj);
+	  }
+      new_adjustments = (new (ggc_alloc <ipa_param_adjustments> ())
+			 ipa_param_adjustments (new_params, count, false));
     }
+  else
+    new_adjustments = NULL;
 
+  replace_trees = vec_safe_copy (node->clone.tree_map);
   for (i = 0; i < count; i++)
     {
       tree t = known_csts[i];
@@ -3865,7 +3967,7 @@ create_specialized_node (struct cgraph_node *node,
 			       IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (
 				 node->decl)));
   new_node = node->create_virtual_clone (callers, replace_trees,
-					 args_to_skip, "constprop",
+					 new_adjustments, "constprop",
 					 suffix_counter);
   suffix_counter++;
 
diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c
index 278bf606661..d1733a99a73 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -616,9 +616,7 @@ ipa_fn_summary_t::duplicate (cgraph_node *src,
 
 	  for (j = 0; vec_safe_iterate (dst->clone.tree_map, j, &r); j++)
 	    {
-	      if (((!r->old_tree && r->parm_num == i)
-		   || (r->old_tree && r->old_tree == ipa_get_param (parms_info, i)))
-		   && r->replace_p && !r->ref_p)
+	      if (r->parm_num == i)
 		{
 		  known_vals[i] = r->new_tree;
 		  break;
diff --git a/gcc/ipa-inline-transform.c b/gcc/ipa-inline-transform.c
index 897c563f19a..d2299601edd 100644
--- a/gcc/ipa-inline-transform.c
+++ b/gcc/ipa-inline-transform.c
@@ -617,8 +617,7 @@ save_inline_function_body (struct cgraph_node *node)
 
   /* Copy the OLD_VERSION_NODE function tree to the new version.  */
   tree_function_versioning (node->decl, first_clone->decl,
-			    NULL, true, NULL, false,
-			    NULL, NULL);
+			    NULL, NULL, true, NULL, NULL);
 
   /* The function will be short lived and removed after we inline all the clones,
      but make it internal so we won't confuse ourself.  */
diff --git a/gcc/ipa-param-manipulation.c b/gcc/ipa-param-manipulation.c
index 1af6d050c48..4b3216a9a90 100644
--- a/gcc/ipa-param-manipulation.c
+++ b/gcc/ipa-param-manipulation.c
@@ -22,170 +22,201 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "backend.h"
-#include "rtl.h"
 #include "tree.h"
 #include "gimple.h"
 #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"
+#include "tree-inline.h"
 
-/* Return a heap allocated vector containing formal parameters of FNDECL.  */
 
-vec<tree>
-ipa_get_vector_of_formal_parms (tree fndecl)
+/* Actual prefixes of different newly synthetized parameters.  Keep in sync
+   with IPA_PARAM_PREFIX_* defines.  */
+
+static const char *ipa_param_prefixes[IPA_PARAM_PREFIX_COUNT]
+  = {"SYNTH",
+     "ISRA",
+     "simd",
+     "mask"};
+
+/* Names of parameters for dumping.  Keep in sync with enum ipa_parm_op.  */
+
+static const char *ipa_param_op_names[IPA_PARAM_PREFIX_COUNT]
+  = {"IPA_PARAM_OP_UNDEFINED",
+     "IPA_PARAM_OP_COPY",
+     "IPA_PARAM_OP_NEW",
+     "IPA_PARAM_OP_SPLIT"};
+
+/* Fill an empty vector ARGS with PARM_DECLs representing formal parameters of
+   FNDECL.  The function should not be called during LTO WPA phase except for
+   thunks (or functions with bodies streamed in). */
+
+void
+push_function_arg_decls (vec<tree> *args, tree fndecl)
 {
-  vec<tree> args;
   int count;
   tree parm;
 
-  gcc_assert (!flag_wpa);
+  /* Safety check that we do not attempt to use the function in WPA, except
+     when the function is a thunk and then we have DECL_ARGUMENTS or when we
+     have already explicitely loaded its body.  */
+  gcc_assert (!flag_wpa
+	      || DECL_ARGUMENTS (fndecl)
+	      || gimple_has_body_p (fndecl));
   count = 0;
   for (parm = DECL_ARGUMENTS (fndecl); parm; parm = DECL_CHAIN (parm))
     count++;
 
-  args.create (count);
+  args->reserve_exact (count);
   for (parm = DECL_ARGUMENTS (fndecl); parm; parm = DECL_CHAIN (parm))
-    args.quick_push (parm);
-
-  return args;
+    args->quick_push (parm);
 }
 
-/* Return a heap allocated vector containing types of formal parameters of
+/* Fill an empty vector TYPES with trees representing formal parameters of
    function type FNTYPE.  */
 
-vec<tree>
-ipa_get_vector_of_formal_parm_types (tree fntype)
+void
+push_function_arg_types (vec<tree> *types, tree fntype)
 {
-  vec<tree> types;
   int count = 0;
   tree t;
 
   for (t = TYPE_ARG_TYPES (fntype); t; t = TREE_CHAIN (t))
     count++;
 
-  types.create (count);
+  types->reserve_exact (count);
   for (t = TYPE_ARG_TYPES (fntype); t; t = TREE_CHAIN (t))
-    types.quick_push (TREE_VALUE (t));
-
-  return types;
+    types->quick_push (TREE_VALUE (t));
 }
 
-/* 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.  */
+/* Dump the adjustments in the vector ADJUSTMENTS to dump_file in a human
+   friendly way, assuming they are meant to be applied to FNDECL.  */
 
 void
-ipa_modify_formal_parameters (tree fndecl, ipa_parm_adjustment_vec adjustments)
-{
-  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);
-    }
+ipa_dump_adjusted_parameters (FILE *f,
+			      vec<ipa_adjusted_param, va_gc> *adj_params)
+{
+  unsigned i, len = vec_safe_length (adj_params);
+  bool first = true;
 
-  int len = adjustments.length ();
-  tree *link = &DECL_ARGUMENTS (fndecl);
-  tree new_arg_types = NULL;
-  for (int i = 0; i < len; i++)
+  fprintf (f, "    IPA adjusted parameters: ");
+  for (i = 0; i < len; i++)
     {
-      struct ipa_parm_adjustment *adj;
-      gcc_assert (link);
+      struct ipa_adjusted_param *apm;
+      apm = &(*adj_params)[i];
 
-      adj = &adjustments[i];
-      tree parm;
-      if (adj->op == IPA_PARM_OP_NEW)
-	parm = NULL;
+      if (!first)
+	fprintf (f, "                             ");
       else
-	parm = oparms[adj->base_index];
-      adj->base = parm;
+	first = false;
 
-      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)
+      fprintf (f, "%i. %s %s", i, ipa_param_op_names[apm->op],
+	       apm->prev_clone_adjustment ? "prev_clone_adjustment " : "");
+      switch (apm->op)
 	{
-	  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);
-		}
-	    }
+	case IPA_PARAM_OP_UNDEFINED:
+	  break;
 
-	  if (care_for_types)
-	    new_arg_types = tree_cons (NULL_TREE, ptype, new_arg_types);
+	case IPA_PARAM_OP_COPY:
+	  fprintf (f, ", base_index: %u", apm->base_index);
+	  fprintf (f, ", prev_clone_index: %u", apm->prev_clone_index);
+	  break;
 
-	  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);
+	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);
+	  fprintf (f, ", prev_clone_index: %u", apm->prev_clone_index);
+	  print_node_brief (f, ", type: ", apm->type, 0);
+	  print_node_brief (f, ", alias type: ", apm->alias_ptr_type, 0);
+	  fprintf (f, " prefix: %s",
+		   ipa_param_prefixes[apm->param_prefix_index]);
+	  if (apm->reverse)
+	    fprintf (f, ", reverse-sso");
+	  break;
+	}
+      fprintf (f, "\n");
+    }
+}
 
-	  if (adj->op == IPA_PARM_OP_NEW)
-	    adj->base = NULL;
-	  else
-	    adj->base = parm;
-	  adj->new_decl = new_parm;
+/* Fill NEW_TYPES with types of a function after its current OTYPES have been
+   modified as described in ADJ_PARAMS.  When USE_PREV_INDICES is true, use
+   prev_clone_index from ADJ_PARAMS as opposed to base_index when the parameter
+   is false.  */
 
-	  *link = new_parm;
-	  link = &DECL_CHAIN (new_parm);
+static void
+fill_vector_of_new_param_types (vec<tree> *new_types, vec<tree> *otypes,
+				vec<ipa_adjusted_param, va_gc> *adj_params,
+				bool use_prev_indices)
+{
+  unsigned adj_len = vec_safe_length (adj_params);
+  new_types->reserve_exact (adj_len);
+  for (unsigned i = 0; i < adj_len ; i++)
+    {
+      ipa_adjusted_param *apm = &(*adj_params)[i];
+      if (apm->op == IPA_PARAM_OP_COPY)
+	{
+	  unsigned index
+	    = use_prev_indices ? apm->prev_clone_index : apm->base_index;
+	  /* The following needs to be handled gracefully because of type
+	     mismatches.  This happens with LTO but apparently also in Fortran
+	     with -fcoarray=lib -O2 -lcaf_single -latomic.  */
+	  if (index >= otypes->length ())
+	    continue;
+	  new_types->quick_push ((*otypes)[index]);
 	}
+      else if (apm->op == IPA_PARAM_OP_NEW
+	       || apm->op == IPA_PARAM_OP_SPLIT)
+	{
+	  tree ntype = apm->type;
+	  if (is_gimple_reg_type (ntype)
+	      && TYPE_MODE (ntype) != BLKmode)
+	    {
+	      unsigned malign = GET_MODE_ALIGNMENT (TYPE_MODE (ntype));
+	      if (TYPE_ALIGN (ntype) != malign)
+		ntype = build_aligned_type (ntype, malign);
+	    }
+	  new_types->quick_push (ntype);
+	}
+      else
+	gcc_unreachable ();
     }
+}
 
-  *link = NULL_TREE;
+/* Build and return a function type just like ORIG_TYPE but with parameter
+   types given in NEW_PARAM_TYPES - which can be NULL if, but only if,
+   ORIG_TYPE itself has NULL TREE_ARG_TYPEs.  If METHOD2FUNC is true, also make
+   it a FUNCTION_TYPE instead of FUNCTION_TYPE.  */
 
-  tree new_reversed = NULL;
-  if (care_for_types)
+static tree
+build_adjusted_function_type (tree orig_type, vec<tree> *new_param_types,
+			      bool method2func, bool skip_return)
+{
+  tree new_arg_types = NULL;
+  if (TYPE_ARG_TYPES (orig_type))
     {
-      new_reversed = nreverse (new_arg_types);
+      gcc_checking_assert (new_param_types);
+      bool last_parm_void = (TREE_VALUE (tree_last (TYPE_ARG_TYPES (orig_type)))
+			     == void_type_node);
+      unsigned len = new_param_types->length ();
+      for (unsigned i = 0; i < len; i++)
+	new_arg_types = tree_cons (NULL_TREE, (*new_param_types)[i],
+				   new_arg_types);
+
+      tree new_reversed = nreverse (new_arg_types);
       if (last_parm_void)
 	{
 	  if (new_reversed)
@@ -193,224 +224,567 @@ ipa_modify_formal_parameters (tree fndecl, ipa_parm_adjustment_vec adjustments)
 	  else
 	    new_reversed = void_list_node;
 	}
+      new_arg_types = new_reversed;
     }
 
-  /* 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.  */
+  /* Use build_distinct_type_copy to preserve as much as possible from original
+     type (debug info, attribute lists etc.).  The one 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 (TREE_CODE (orig_type) != METHOD_TYPE
-       || (adjustments[0].op == IPA_PARM_OP_COPY
-	  && adjustments[0].base_index == 0))
+  if (method2func)
+    {
+      tree ret_type;
+      if (skip_return)
+	ret_type = void_type_node;
+      else
+	ret_type = TREE_TYPE (orig_type);
+
+      new_type
+	= build_distinct_type_copy (build_function_type (ret_type,
+							 new_arg_types));
+      TYPE_CONTEXT (new_type) = TYPE_CONTEXT (orig_type);
+    }
+  else
     {
       new_type = build_distinct_type_copy (orig_type);
-      TYPE_ARG_TYPES (new_type) = new_reversed;
+      TYPE_ARG_TYPES (new_type) = new_arg_types;
+      if (skip_return)
+	TREE_TYPE (new_type) = void_type_node;
+    }
+
+  return new_type;
+}
+
+/* Return the maximum index in any IPA_PARAM_OP_COPY adjustment or -1 if there
+   is none.  */
+
+int
+ipa_param_adjustments::get_max_base_index ()
+{
+  unsigned adj_len = vec_safe_length (m_adj_params);
+  int max_index = -1;
+  for (unsigned i = 0; i < adj_len ; i++)
+    {
+      ipa_adjusted_param *apm = &(*m_adj_params)[i];
+      if (apm->op == IPA_PARAM_OP_COPY
+	  && max_index < apm->base_index)
+	max_index = apm->base_index;
+    }
+  return max_index;
+}
+
+
+/* Fill SURVIVING_PARAMS with an array of bools where each one says whether a
+   parameter that originally was at that position still survives in the given
+   clone or is removed/replaced.  If the final array is smaller than an index
+   of an original parameter, that parameter also did not survive.  That a
+   parameter survives does not mean it has the same index as before.  */
+
+void
+ipa_param_adjustments::get_surviving_params (vec<bool> *surviving_params)
+{
+  unsigned adj_len = vec_safe_length (m_adj_params);
+  int max_index = get_max_base_index ();
+
+  if (max_index < 0)
+    return;
+  surviving_params->reserve_exact (max_index + 1);
+  surviving_params->quick_grow_cleared (max_index + 1);
+  for (unsigned i = 0; i < adj_len ; i++)
+    {
+      ipa_adjusted_param *apm = &(*m_adj_params)[i];
+      if (apm->op == IPA_PARAM_OP_COPY)
+	(*surviving_params)[apm->base_index] = true;
+    }
+}
+
+/* Fill NEW_INDICES with new indices of each surviving parameter or -1 for
+   those which do not survive.  Any parameter outside of lenght of the vector
+   does not survive.  There is currently no support for a parameter to be
+   copied to two distinct new parameters.  */
+
+void
+ipa_param_adjustments::get_updated_indices (vec<int> *new_indices)
+{
+  unsigned adj_len = vec_safe_length (m_adj_params);
+  int max_index = get_max_base_index ();
+
+  if (max_index < 0)
+    return;
+  unsigned res_len = max_index + 1;
+  new_indices->reserve_exact (res_len);
+  for (unsigned i = 0; i < res_len ; i++)
+    new_indices->quick_push (-1);
+  for (unsigned i = 0; i < adj_len ; i++)
+    {
+      ipa_adjusted_param *apm = &(*m_adj_params)[i];
+      if (apm->op == IPA_PARAM_OP_COPY)
+	(*new_indices)[apm->base_index] = i;
+    }
+}
+
+/* Return true if the first parameter (assuming there was one) survives the
+   transformation intact and remains the first one.  */
+
+bool
+ipa_param_adjustments::first_param_intact_p ()
+{
+  return (!vec_safe_is_empty (m_adj_params)
+	  && (*m_adj_params)[0].op == IPA_PARAM_OP_COPY
+	  && (*m_adj_params)[0].base_index == 0);
+}
+
+/* Return true if we have to change what has formerly been a method into a
+   function.  */
+
+bool
+ipa_param_adjustments::method2func_p (tree orig_type)
+{
+  return ((TREE_CODE (orig_type) == METHOD_TYPE) && !first_param_intact_p ());
+}
+
+/* Given function type OLD_TYPE, return a new type derived from it after
+   performing all atored modifications.  TYPE_ORIGINAL_P should be true when
+   OLD_TYPE refers to the type before any IPA transformations, as opposed to a
+   type that can be an intermediate one in between various IPA
+   transformations.  */
+
+tree
+ipa_param_adjustments::build_new_function_type (tree old_type,
+						bool type_original_p)
+{
+  auto_vec<tree,16> new_param_types, *new_param_types_p;
+  if (prototype_p (old_type))
+    {
+      auto_vec<tree, 16> otypes;
+      push_function_arg_types (&otypes, old_type);
+      fill_vector_of_new_param_types (&new_param_types, &otypes, m_adj_params,
+				      !type_original_p);
+      new_param_types_p = &new_param_types;
     }
   else
+    new_param_types_p = NULL;
+
+  return build_adjusted_function_type (old_type, new_param_types_p,
+				       method2func_p (old_type), m_skip_return);
+}
+
+/* Build variant of function decl ORIG_DECL which has no return value if
+   M_SKIP_RETURN is true and, if ORIG_DECL's types or parameters is known, has
+   this type adjusted as indicated in M_ADJ_PARAMS. Arguments from
+   DECL_ARGUMENTS list are not processed now, since they are linked by
+   TREE_CHAIN directly and not accessible in LTO during WPA.  The caller is
+   responsible for eliminating them when clones are properly materialized.  */
+
+tree
+ipa_param_adjustments::adjust_decl (tree orig_decl)
+{
+  tree new_decl = copy_node (orig_decl);
+  tree orig_type = TREE_TYPE (orig_decl);
+  if (prototype_p (orig_type)
+      || (m_skip_return && !VOID_TYPE_P (TREE_TYPE (orig_type))))
     {
-      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;
+      tree new_type = build_new_function_type (orig_type, false);
+      TREE_TYPE (new_decl) = new_type;
     }
+  if (method2func_p (orig_type))
+    DECL_VINDEX (new_decl) = NULL_TREE;
 
   /* When signature changes, we need to clear builtin info.  */
-  if (fndecl_built_in_p (fndecl))
-    set_decl_built_in_function (fndecl, NOT_BUILT_IN, 0);
+  if (fndecl_built_in_p (new_decl))
+    set_decl_built_in_function (new_decl, NOT_BUILT_IN, 0);
+
+  DECL_VIRTUAL_P (new_decl) = 0;
+  DECL_LANG_SPECIFIC (new_decl) = NULL;
 
-  TREE_TYPE (fndecl) = new_type;
-  DECL_VIRTUAL_P (fndecl) = 0;
-  DECL_LANG_SPECIFIC (fndecl) = NULL;
-  otypes.release ();
-  oparms.release ();
+  return new_decl;
 }
 
-/* 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.  */
+/* Wrapper around get_base_ref_and_offset for cases interesting for IPA-SRA
+   transformations.  Return true if EXPR has an interesting form and fill in
+   *BASE_P and *UNIT_OFFSET_P with the appropriate info.  */
 
-void
-ipa_modify_call_arguments (struct cgraph_edge *cs, gcall *stmt,
-			   ipa_parm_adjustment_vec adjustments)
-{
-  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;
+static bool
+isra_get_ref_base_and_offset (tree expr, tree *base_p, unsigned *unit_offset_p)
+{
+  HOST_WIDE_INT offset, size;
+  bool reverse;
+  tree base
+    = get_ref_base_and_extent_hwi (expr, &offset, &size, &reverse);
+  if (!base || size < 0)
+    return false;
 
-  len = adjustments.length ();
-  vargs.create (len);
-  callee_decl = !cs ? gimple_call_fndecl (stmt) : cs->callee->decl;
-  current_node->remove_stmt_references (stmt);
+  if ((offset % BITS_PER_UNIT) != 0)
+    return false;
 
-  gsi = gsi_for_stmt (stmt);
-  prev_gsi = gsi;
-  gsi_prev (&prev_gsi);
-  for (i = 0; i < len; i++)
+  if (TREE_CODE (base) == MEM_REF)
     {
-      struct ipa_parm_adjustment *adj;
+      poly_int64 plmoff = mem_ref_offset (base).force_shwi ();
+      HOST_WIDE_INT moff;
+      bool is_cst = plmoff.is_constant (&moff);
+      if (!is_cst)
+	return false;
+      offset += moff * BITS_PER_UNIT;
+      base = TREE_OPERAND (base, 0);
+    }
 
-      adj = &adjustments[i];
+  if (offset < 0 || (offset / BITS_PER_UNIT) > UINT_MAX)
+    return false;
 
-      if (adj->op == IPA_PARM_OP_COPY)
-	{
-	  tree arg = gimple_call_arg (stmt, adj->base_index);
+  *base_p = base;
+  *unit_offset_p = offset / BITS_PER_UNIT;
+  return true;
+}
 
-	  vargs.quick_push (arg);
+/* Return true if EXPR describes a transitive split (i.e. one that happened for
+   both the caller and the callee) as recorded in PERFORMED_SPLITS.  In that
+   case, store index of the respective record in PERFORMED_SPLITS into
+   *SM_IDX_P and the unit offset from all handled components in EXPR into
+   *UNIT_OFFSET_P.  */
+
+static bool
+transitive_split_p (vec<ipa_param_performed_split, va_gc> *performed_splits,
+		    tree expr, unsigned *sm_idx_p, unsigned *unit_offset_p)
+{
+  tree base;
+  if (!isra_get_ref_base_and_offset (expr, &base, unit_offset_p))
+    return false;
+
+  if (TREE_CODE (base) == SSA_NAME)
+    {
+      base = SSA_NAME_VAR (base);
+      if (!base)
+	return false;
+    }
+
+  unsigned len = vec_safe_length (performed_splits);
+  for (unsigned i = 0 ; i < len; i++)
+    {
+      ipa_param_performed_split *sm = &(*performed_splits)[i];
+      if (sm->dummy_decl == base)
+	{
+	  *sm_idx_p = i;
+	  return true;
 	}
-      else if (adj->op != IPA_PARM_OP_REMOVE)
+    }
+  return false;
+}
+
+/* Structure to hold declarations representing transitive IPA-SRA splits.  In
+   essence, if we need to pass UNIT_OFFSET of a parameter which originally has
+   number BASE_INDEX, we should pass down REPL.  */
+
+struct transitive_split_map
+{
+  tree repl;
+  unsigned base_index;
+  unsigned unit_offset;
+};
+
+/* If call STMT contains any parameters representing transitive splits as
+   described by PERFORMED_SPLITS, return the number of extra parameters that
+   were addded during clone materialization and fill in INDEX_MAP with adjusted
+   indices of corresponding original parameters and TRANS_MAP with description
+   of all transitive replacement descriptions.  Otherwise return zero. */
+
+static unsigned
+init_transitive_splits (vec<ipa_param_performed_split, va_gc> *performed_splits,
+			gcall *stmt, vec <unsigned> *index_map,
+			auto_vec <transitive_split_map> *trans_map)
+{
+  unsigned phony_arguments = 0;
+  unsigned stmt_idx = 0, base_index = 0;
+  unsigned nargs = gimple_call_num_args (stmt);
+  while (stmt_idx < nargs)
+    {
+      unsigned unit_offset_delta;
+      tree base_arg = gimple_call_arg (stmt, stmt_idx);
+
+      if (phony_arguments > 0)
+	index_map->safe_push (stmt_idx);
+
+      unsigned sm_idx;
+      stmt_idx++;
+      if (transitive_split_p (performed_splits, base_arg, &sm_idx,
+			      &unit_offset_delta))
 	{
-	  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.  */
-
-	  poly_int64 byte_offset = exact_div (adj->offset, BITS_PER_UNIT);
-	  base = gimple_call_arg (stmt, adj->base_index);
-	  loc = gimple_location (stmt);
-
-	  if (TREE_CODE (base) != ADDR_EXPR
-	      && POINTER_TYPE_P (TREE_TYPE (base)))
-	    off = build_int_cst (adj->alias_ptr_type, byte_offset);
-	  else
+	  if (phony_arguments == 0)
+	    /* We have optimistically avoided constructing index_map do far but
+	       now it is clear it will be necessary, so let's create the easy
+	       bit we skipped until now.  */
+	    for (unsigned k = 0; k < stmt_idx; k++)
+	      index_map->safe_push (k);
+
+	  tree dummy = (*performed_splits)[sm_idx].dummy_decl;
+	  for (unsigned j = sm_idx; j < performed_splits->length (); j++)
 	    {
-	      poly_int64 base_offset;
-	      tree prev_base;
-	      bool addrof;
+	      ipa_param_performed_split *caller_split
+		= &(*performed_splits)[j];
+	      if (caller_split->dummy_decl != dummy)
+		break;
 
-	      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, byte_offset);
-		}
-	      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 + byte_offset);
-		  off = int_const_binop (PLUS_EXPR, TREE_OPERAND (base, 1),
-					 off);
-		  base = TREE_OPERAND (base, 0);
-		}
-	      else
+	      tree arg = gimple_call_arg (stmt, stmt_idx);
+	      struct transitive_split_map tsm;
+	      tsm.repl = arg;
+	      tsm.base_index = base_index;
+	      if (caller_split->unit_offset >= unit_offset_delta)
 		{
-		  off = build_int_cst (adj->alias_ptr_type,
-				       base_offset + byte_offset);
-		  base = build_fold_addr_expr (base);
+		  tsm.unit_offset
+		    = (caller_split->unit_offset - unit_offset_delta);
+		  trans_map->safe_push (tsm);
 		}
+
+	      phony_arguments++;
+	      stmt_idx++;
 	    }
+	}
+      base_index++;
+    }
+  return phony_arguments;
+}
 
-	  if (!adj->by_ref)
+/* Modify actual arguments of a function call in statement STMT, assuming it
+   calls CALLEE_DECL.  CALLER_ADJ must be the description of parameter
+   adjustments of the caller or NULL if there are none.  Return the new
+   statement that replaced the old one.  When invoked, cfun and
+   current_function_decl have to be set to the caller.  */
+
+gcall *
+ipa_param_adjustments::modify_call (gcall *stmt,
+				    vec<ipa_param_performed_split,
+				        va_gc> *performed_splits,
+				    tree callee_decl, bool update_references)
+{
+  unsigned len = vec_safe_length (m_adj_params);
+  auto_vec<tree, 16> vargs (len);
+  tree old_decl = gimple_call_fndecl (stmt);
+  unsigned old_nargs = gimple_call_num_args (stmt);
+  auto_vec<bool, 16> kept (old_nargs);
+  kept.quick_grow_cleared (old_nargs);
+
+  auto_vec <unsigned, 16> index_map;
+  auto_vec <transitive_split_map> trans_map;
+  bool transitive_remapping = false;
+  if (performed_splits)
+    {
+      unsigned removed = init_transitive_splits (performed_splits,
+						 stmt, &index_map, &trans_map);
+      if (removed > 0)
+	{
+	  transitive_remapping = true;
+	  old_nargs -= removed;
+	}
+    }
+
+  cgraph_node *current_node = cgraph_node::get (current_function_decl);
+  if (update_references)
+    current_node->remove_stmt_references (stmt);
+
+  gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
+  gimple_stmt_iterator prev_gsi = gsi;
+  gsi_prev (&prev_gsi);
+  for (unsigned i = 0; i < len; i++)
+    {
+      ipa_adjusted_param *apm = &(*m_adj_params)[i];
+      if (apm->op == IPA_PARAM_OP_COPY)
+	{
+	  unsigned index = apm->base_index;
+	  if (index >= old_nargs)
+	    /* Can happen if the original call has argument mismatch,
+	       ignore.  */
+	    continue;
+	  if (transitive_remapping)
+	    index = index_map[apm->base_index];
+
+	  tree arg = gimple_call_arg (stmt, index);
+
+	  vargs.quick_push (arg);
+	  kept[index] = true;
+	  continue;
+	}
+
+      /* At the moment the only user of IPA_PARAM_OP_NEW modifies calls itself.
+	 If we ever want to support it during WPA IPA stage, we'll need a
+	 mechanism to call into the IPA passes that introduced them.  Currently
+	 we simply mandate that IPA infrastructure understands all argument
+	 modifications.  Remember, edge redirection/modification is done only
+	 once, not in steps for each pass modifying the callee like clone
+	 materialization.  */
+      gcc_assert (apm->op == IPA_PARAM_OP_SPLIT);
+
+      /* We have to handle transitive changes differently using the maps we
+	 have created before.  So look into them first.  */
+      tree repl = NULL_TREE;
+      for (unsigned j = 0; j < trans_map.length (); j++)
+	if (trans_map[j].base_index == apm->base_index
+	    && trans_map[j].unit_offset == apm->unit_offset)
+	  {
+	    repl = trans_map[j].repl;
+	    break;
+	  }
+      if (repl)
+	{
+	  vargs.quick_push (repl);
+	  continue;
+	}
+
+      unsigned index = apm->base_index;
+      if (index >= old_nargs)
+	/* Can happen if the original call has argument mismatch, ignore.  */
+	continue;
+      if (transitive_remapping)
+	index = index_map[apm->base_index];
+      tree base = gimple_call_arg (stmt, index);
+
+      /* 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, potentially as a part of a larger
+	 aggregate, is converted to a scalar passed by value.
+
+	 - A part of an aggregate is passed instead of the whole aggregate.  */
+
+      location_t loc = gimple_location (stmt);
+      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
+	{
+	  bool addrof;
+	  if (TREE_CODE (base) == ADDR_EXPR)
 	    {
-	      tree type = adj->type;
-	      unsigned int align;
-	      unsigned HOST_WIDE_INT misalign;
+	      base = TREE_OPERAND (base, 0);
+	      addrof = true;
+	    }
+	  else
+	    addrof = false;
 
-	      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)))
+	  tree prev_base = base;
+	  poly_int64 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)
 		{
-		  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);
-		  gimple_set_location (tem, loc);
-		  gsi_insert_before (&gsi, tem, GSI_SAME_STMT);
+		  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);
 	    }
 	  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);
+	      off = build_int_cst (apm->alias_ptr_type,
+				   base_offset + apm->unit_offset);
+	      base = build_fold_addr_expr (base);
 	    }
-	  vargs.quick_push (expr);
 	}
-      if (adj->op != IPA_PARM_OP_COPY && MAY_HAVE_DEBUG_BIND_STMTS)
+
+      tree type = apm->type;
+      unsigned int align;
+      unsigned HOST_WIDE_INT misalign;
+
+      if (deref_base)
 	{
-	  unsigned int ix;
-	  tree ddecl = NULL_TREE, origin = DECL_ORIGIN (adj->base), arg;
-	  gimple *def_temp;
+	  align = deref_align;
+	  misalign = 0;
+	}
+      else
+	{
+	  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);
+      tree 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 (m_always_copy_start >= 0)
+    for (unsigned i = m_always_copy_start; i < old_nargs; i++)
+      vargs.safe_push (gimple_call_arg (stmt, i));
+
+  /* For optimized away parameters, add on the caller side
+     before the call
+     DEBUG D#X => parm_Y(D)
+     stmts and associate D#X with parm in decl_debug_args_lookup
+     vector to say for debug info that if parameter parm had been passed,
+     it would have value parm_Y(D).  */
+  if (MAY_HAVE_DEBUG_BIND_STMTS && old_decl && callee_decl)
+    {
+      vec<tree, va_gc> **debug_args = NULL;
+      unsigned i = 0;
+      for (tree old_parm = DECL_ARGUMENTS (old_decl);
+	   old_parm && i < old_nargs && ((int) i) < m_always_copy_start;
+	   old_parm = DECL_CHAIN (old_parm), i++)
+	{
+	  if (!is_gimple_reg (old_parm) || kept[i])
+	    continue;
+	  tree origin = DECL_ORIGIN (old_parm);
+	  tree arg = gimple_call_arg (stmt, 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))
 		continue;
-	      arg = fold_convert_loc (gimple_location (stmt),
-				      TREE_TYPE (origin), arg);
+	      tree rhs1;
+	      if (TREE_CODE (arg) == SSA_NAME
+		  && gimple_assign_cast_p (SSA_NAME_DEF_STMT (arg))
+		  && (rhs1
+		      = gimple_assign_rhs1 (SSA_NAME_DEF_STMT (arg)))
+		  && useless_type_conversion_p (TREE_TYPE (origin),
+						TREE_TYPE (rhs1)))
+		arg = rhs1;
+	      else
+		arg = fold_convert_loc (gimple_location (stmt),
+					TREE_TYPE (origin), arg);
 	    }
 	  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)
 	      {
@@ -427,7 +801,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);
 	}
     }
@@ -438,10 +813,34 @@ 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 ();
-  if (gimple_call_lhs (stmt))
-    gimple_call_set_lhs (new_stmt, gimple_call_lhs (stmt));
+  gcall *new_stmt = gimple_build_call_vec (callee_decl, vargs);
+
+  if (tree lhs = gimple_call_lhs (stmt))
+    {
+      if (!m_skip_return)
+	gimple_call_set_lhs (new_stmt, lhs);
+      else if (TREE_CODE (lhs) == SSA_NAME)
+	{
+	  /* LHS should now by a default-def SSA.  Unfortunately default-def
+	     SSA_NAMEs need a backing variable (or at least some code examining
+	     SSAs assumes it is non-NULL).  So we either have to re-use the
+	     decl we have at hand or introdice a new one.  */
+	  tree repl = create_tmp_var (TREE_TYPE (lhs), "removed_return");
+	  repl = get_or_create_ssa_default_def (cfun, repl);
+	  SSA_NAME_IS_DEFAULT_DEF (repl) = true;
+	  imm_use_iterator ui;
+	  use_operand_p use_p;
+	  gimple *using_stmt;
+	  FOR_EACH_IMM_USE_STMT (using_stmt, ui, lhs)
+	    {
+	      FOR_EACH_IMM_USE_ON_STMT (use_p, ui)
+		{
+		  SET_USE (use_p, repl);
+		}
+	      update_stmt (using_stmt);
+	    }
+	}
+    }
 
   gimple_set_block (new_stmt, gimple_block (stmt));
   if (gimple_has_location (stmt))
@@ -458,120 +857,398 @@ 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));
+  if (update_references)
+    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.  */
+/* Dump information contained in the object in textual form to F.  */
 
-static bool
-index_in_adjustments_multiple_times_p (int base_index,
-				       ipa_parm_adjustment_vec adjustments)
+void
+ipa_param_adjustments::dump (FILE *f)
 {
-  int i, len = adjustments.length ();
-  bool one = false;
+  fprintf (f, "    m_always_copy_start: %i\n", m_always_copy_start);
+  ipa_dump_adjusted_parameters (f, m_adj_params);
+  if (m_skip_return)
+    fprintf (f, "     Will SKIP return.\n");
+}
 
-  for (i = 0; i < len; i++)
-    {
-      struct ipa_parm_adjustment *adj;
-      adj = &adjustments[i];
+/* Dump information contained in the object in textual form to stderr.  */
 
-      if (adj->base_index == base_index)
-	{
-	  if (one)
-	    return true;
-	  else
-	    one = true;
-	}
-    }
-  return false;
+void
+ipa_param_adjustments::debug ()
+{
+  dump (stderr);
 }
 
-/* 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.  */
+/* Register that REPLACEMENT should replace parameter described in APM and
+   optionally as DUMMY to mark transitive splits accross calls.  */
 
-ipa_parm_adjustment_vec
-ipa_combine_adjustments (ipa_parm_adjustment_vec inner,
-			 ipa_parm_adjustment_vec outer)
+void
+ipa_param_body_adjustments::register_replacement (ipa_adjusted_param *apm,
+						  tree replacement,
+						  tree dummy)
 {
-  int i, outlen = outer.length ();
-  int inlen = inner.length ();
-  int removals = 0;
-  ipa_parm_adjustment_vec adjustments, tmp;
+  gcc_checking_assert (apm->op == IPA_PARAM_OP_SPLIT
+		       || apm->op == IPA_PARAM_OP_NEW);
+  gcc_checking_assert (!apm->prev_clone_adjustment);
+  ipa_param_body_replacement psr;
+  psr.base = m_oparms[apm->prev_clone_index];
+  psr.repl = replacement;
+  psr.dummy = dummy;
+  psr.unit_offset = apm->unit_offset;
+  m_replacements.safe_push (psr);
+}
+
+/* Copy or not, as appropriate given ID, a pre-existing PARM_DECL T so that
+   it can be included in the parameters of the modified function.  */
 
-  tmp.create (inlen);
-  for (i = 0; i < inlen; i++)
+static tree
+carry_over_param (tree t, struct copy_body_data *id)
+{
+  tree new_parm;
+  if (id)
     {
-      struct ipa_parm_adjustment *n;
-      n = &inner[i];
+      new_parm = remap_decl (t, id);
+      if (TREE_CODE (new_parm) != PARM_DECL)
+	new_parm = id->copy_decl (t, id);
+    }
+  else
+    new_parm = t;
+  return new_parm;
+}
 
-      if (n->op == IPA_PARM_OP_REMOVE)
-	removals++;
-      else
+/* Common initialization performed by all ipa_param_body_adjustments
+   constructors.  OLD_FNDECL is the declaration we take original arguments
+   from, (it may be the same as M_FNDECL).  VARS, if non-NULL, is a pointer to
+   a chained list of new local variables.  TREE_MAP is the IPA-CP produced
+   mapping of trees to constants.
+
+   The function is rather long but it really onlu initializes all data members
+   of the class.  It creates new param DECLs, finds their new types,   */
+
+void
+ipa_param_body_adjustments::common_initialization (tree old_fndecl,
+						   tree *vars,
+						   vec<ipa_replace_map *,
+						       va_gc> *tree_map)
+{
+  push_function_arg_decls (&m_oparms, old_fndecl);
+  auto_vec<tree,16> otypes;
+  if (TYPE_ARG_TYPES (TREE_TYPE (old_fndecl)) != NULL_TREE)
+    push_function_arg_types (&otypes, TREE_TYPE (old_fndecl));
+  else
+    {
+      auto_vec<tree,16> oparms;
+      push_function_arg_decls (&oparms, old_fndecl);
+      unsigned ocount = oparms.length ();
+      otypes.reserve_exact (ocount);
+      for (unsigned i = 0; i < ocount; i++)
+	otypes.quick_push (TREE_TYPE (oparms[i]));
+    }
+  fill_vector_of_new_param_types (&m_new_types, &otypes, m_adj_params, true);
+
+  auto_vec<bool, 16> kept;
+  kept.reserve_exact (m_oparms.length ());
+  kept.quick_grow_cleared (m_oparms.length ());
+  auto_vec<tree, 16> isra_dummy_decls;
+  isra_dummy_decls.reserve_exact (m_oparms.length ());
+  isra_dummy_decls.quick_grow_cleared (m_oparms.length ());
+
+  unsigned adj_len = vec_safe_length (m_adj_params);
+  m_method2func = ((TREE_CODE (TREE_TYPE (m_fndecl)) == METHOD_TYPE)
+		   && (adj_len == 0
+		       || (*m_adj_params)[0].op != IPA_PARAM_OP_COPY
+		       || (*m_adj_params)[0].base_index != 0));
+
+  /* The main job of the this function is to go over the vector of adjusted
+     parameters and create declarations or find corresponding old ones and push
+     them to m_new_decls.  For IPA-SRA replacements it also creates
+     corresponding m_id->dst_node->clone.performed_splits entries.  */
+
+  m_new_decls.reserve_exact (adj_len);
+  for (unsigned i = 0; i < adj_len ; i++)
+    {
+      ipa_adjusted_param *apm = &(*m_adj_params)[i];
+      unsigned prev_index = apm->prev_clone_index;
+      tree new_parm;
+      if (apm->op == IPA_PARAM_OP_COPY
+	  || apm->prev_clone_adjustment)
 	{
-	  /* FIXME: Handling of new arguments are not implemented yet.  */
-	  gcc_assert (n->op != IPA_PARM_OP_NEW);
-	  tmp.quick_push (*n);
+	  kept[prev_index] = true;
+	  new_parm = carry_over_param (m_oparms[prev_index], m_id);
+	  m_new_decls.quick_push (new_parm);
 	}
+      else if (apm->op == IPA_PARAM_OP_NEW
+	       || apm->op == IPA_PARAM_OP_SPLIT)
+	{
+	  tree new_type = m_new_types[i];
+	  gcc_checking_assert (new_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;
+	  /* We assume all newly created arguments are not addressable.  */
+	  if (TREE_CODE (new_type) == COMPLEX_TYPE
+	      || TREE_CODE (new_type) == VECTOR_TYPE)
+	    DECL_GIMPLE_REG_P (new_parm) = 1;
+	  layout_decl (new_parm, 0);
+	  m_new_decls.quick_push (new_parm);
+
+	  if (apm->op == IPA_PARAM_OP_SPLIT)
+	    {
+	      m_split_modifications_p = true;
+
+	      if (m_id)
+		{
+		  tree dummy_decl;
+		  if (!isra_dummy_decls[prev_index])
+		    {
+		      dummy_decl = copy_decl_to_var (m_oparms[prev_index],
+						     m_id);
+		      /* Any attempt to remap this dummy in this particular
+			 instance of clone materialization should yield
+			 itself.  */
+		      insert_decl_map (m_id, dummy_decl, dummy_decl);
+
+		      DECL_CHAIN (dummy_decl) = *vars;
+		      *vars = dummy_decl;
+		      isra_dummy_decls[prev_index] = dummy_decl;
+		    }
+		  else
+		    dummy_decl = isra_dummy_decls[prev_index];
+
+		  register_replacement (apm, new_parm, dummy_decl);
+		  ipa_param_performed_split ps;
+		  ps.dummy_decl = dummy_decl;
+		  ps.unit_offset = apm->unit_offset;
+		  vec_safe_push (m_id->dst_node->clone.performed_splits, ps);
+		}
+	      else
+		register_replacement (apm, new_parm);
+	    }
+        }
+      else
+	gcc_unreachable ();
     }
 
-  adjustments.create (outlen + removals);
-  for (i = 0; i < outlen; i++)
+
+  /* As part of body modifications, we will also have to replace remaining uses
+     of remaining uses of removed PARM_DECLs (which do not however use the
+     initial value) with their VAR_DECL copies.
+
+     We do this differently with and without m_id.  With m_id, we rely on its
+     mapping and create a replacement straight away.  Without it, we have our
+     own mechanism for which we have to populate m_removed_decls vector.  Just
+     don't mix them, that is why you should not call
+     replace_removed_params_ssa_names or perform_cfun_body_modifications when
+     you construct with ID not equal to NULL.  */
+
+  unsigned op_len = m_oparms.length ();
+  for (unsigned i = 0; i < op_len; i++)
+    if (!kept[i])
+      {
+	if (m_id)
+	  {
+	    if (!m_id->decl_map->get (m_oparms[i]))
+	      {
+		/* TODO: Perhaps at least aggregate-type params could re-use
+		   their isra_dummy_decl here?  */
+		tree var = copy_decl_to_var (m_oparms[i], m_id);
+		insert_decl_map (m_id, m_oparms[i], var);
+		/* Declare this new variable.  */
+		DECL_CHAIN (var) = *vars;
+		*vars = var;
+	      }
+	  }
+	else
+	  {
+	    m_removed_decls.safe_push (m_oparms[i]);
+	    m_removed_map.put (m_oparms[i], m_removed_decls.length () - 1);
+	  }
+      }
+
+  if (!MAY_HAVE_DEBUG_STMTS)
+    return;
+
+  /* Finally, when generating debug info, we fill vector m_reset_debug_decls
+    with removed parameters declarations.  We do this in order to re-map their
+    debug bind statements and create debug decls for them.  */
+
+  if (tree_map)
     {
-      struct ipa_parm_adjustment r;
-      struct ipa_parm_adjustment *out = &outer[i];
-      struct ipa_parm_adjustment *in = &tmp[out->base_index];
+      /* Do not output debuginfo for parameter declarations as if they vanished
+	 when they were in fact replaced by a constant.  */
+      auto_vec <int, 16> index_mapping;
+      bool need_remap = false;
 
-      memset (&r, 0, sizeof (r));
-      gcc_assert (in->op != IPA_PARM_OP_REMOVE);
-      if (out->op == IPA_PARM_OP_REMOVE)
+      if (m_id && m_id->src_node->clone.param_adjustments)
 	{
-	  if (!index_in_adjustments_multiple_times_p (in->base_index, tmp))
-	    {
-	      r.op = IPA_PARM_OP_REMOVE;
-	      adjustments.quick_push (r);
-	    }
-	  continue;
+	  ipa_param_adjustments *prev_adjustments
+	    = m_id->src_node->clone.param_adjustments;
+	  prev_adjustments->get_updated_indices (&index_mapping);
+	  need_remap = true;
 	}
-      else
+
+      for (unsigned i = 0; i < tree_map->length (); i++)
 	{
-	  /* FIXME: Handling of new arguments are not implemented yet.  */
-	  gcc_assert (out->op != IPA_PARM_OP_NEW);
+	  int parm_num = (*tree_map)[i]->parm_num;
+	  gcc_assert (parm_num >= 0);
+	  if (need_remap)
+	    parm_num = index_mapping[parm_num];
+	  kept[parm_num] = true;
 	}
+    }
+
+  for (unsigned i = 0; i < op_len; i++)
+    if (!kept[i] && is_gimple_reg (m_oparms[i]))
+      m_reset_debug_decls.safe_push (m_oparms[i]);
+}
 
-      r.base_index = in->base_index;
-      r.type = out->type;
+/* Constructor of ipa_param_body_adjustments from a simple list of
+   modifications to parameters listed in ADJ_PARAMS which will prepare ground
+   for modification of parameters of fndecl.  Return value of the function will
+   not be removed and the object will assume it does not run as a part of
+   tree-function_versioning.  */
+
+ipa_param_body_adjustments
+::ipa_param_body_adjustments (vec<ipa_adjusted_param, va_gc> *adj_params,
+			      tree fndecl)
+  : m_adj_params (adj_params), m_adjustments (NULL), m_reset_debug_decls (),
+    m_split_modifications_p (false), m_fndecl (fndecl), m_id (NULL),
+    m_oparms (), m_new_decls (), m_new_types (), m_replacements (),
+    m_removed_decls (), m_removed_map (), m_method2func (false)
+{
+  common_initialization (fndecl, NULL, NULL);
+}
 
-      /* FIXME:  Create nonlocal value too.  */
+/* Constructor of ipa_param_body_adjustments from ipa_param_adjustments in
+   ADJUSTMENTS which will prepare ground for modification of parameters of
+   fndecl.  The object will assume it does not run as a part of
+   tree-function_versioning.  */
+
+ipa_param_body_adjustments
+::ipa_param_body_adjustments (ipa_param_adjustments *adjustments,
+			      tree fndecl)
+  : m_adj_params (adjustments->m_adj_params), m_adjustments (adjustments),
+    m_reset_debug_decls (), m_split_modifications_p (false), m_fndecl (fndecl),
+    m_id (NULL), m_oparms (), m_new_decls (), m_new_types (),
+    m_replacements (), m_removed_decls (), m_removed_map (),
+    m_method2func (false)
+{
+  common_initialization (fndecl, NULL, NULL);
+}
 
-      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);
+/* Constructor of ipa_param_body_adjustments which sets it up as a part of
+   running tree_function_versioning.  Planned modifications to the function are
+   in ADJUSTMENTS.  FNDECL designates the new function clone which is being
+   modified.  OLD_FNDECL is the function of which FNDECL is a clone (and which
+   at the time of invocation still share DECL_ARGUMENTS).  ID is the
+   copy_body_data structure driving the wholy body copying process.  VARS is a
+   pointer to the head of the list of new local variables, TREE_MAP is the map
+   that drives tree substitution in the cloning process.  */
+
+ipa_param_body_adjustments
+::ipa_param_body_adjustments (ipa_param_adjustments *adjustments,
+			      tree fndecl, tree old_fndecl,
+			      copy_body_data *id, tree *vars,
+			      vec<ipa_replace_map *, va_gc> *tree_map)
+  : m_adj_params (adjustments->m_adj_params), m_adjustments (adjustments),
+    m_reset_debug_decls (), m_split_modifications_p (false), m_fndecl (fndecl),
+    m_id (id), m_oparms (), m_new_decls (), m_new_types (), m_replacements (),
+    m_removed_decls (), m_removed_map (), m_method2func (false)
+{
+  common_initialization (old_fndecl, vars, tree_map);
+}
+
+/* Chain new param decls up and return them.  */
+
+tree
+ipa_param_body_adjustments::get_new_param_chain ()
+{
+  tree result;
+  tree *link = &result;
+
+  unsigned len = vec_safe_length (m_adj_params);
+  for (unsigned i = 0; i < len; i++)
+    {
+      tree new_decl = m_new_decls[i];
+      *link = new_decl;
+      link = &DECL_CHAIN (new_decl);
     }
+  *link = NULL_TREE;
+  return result;
+}
+
+/* Modify the function parameters FNDECL and its type according to the plan in
+   ADJUSTMENTS.  This function needs to be called when the decl has not already
+   been processed with ipa_param_adjustments::adjust_decl, otherwise just
+   seting DECL_ARGUMENTS to whatever get_new_param_chain will do is enough.  */
+
+void
+ipa_param_body_adjustments::modify_formal_parameters ()
+{
+  tree orig_type = TREE_TYPE (m_fndecl);
+  DECL_ARGUMENTS (m_fndecl) = get_new_param_chain ();
+
+  /* When signature changes, we need to clear builtin info.  */
+  if (fndecl_built_in_p (m_fndecl))
+    set_decl_built_in_function (m_fndecl, NOT_BUILT_IN, 0);
+
+  /* At this point, removing return value is only implemented when going
+     through tree_function_versioning, not when modifying function body
+     directly.  */
+  gcc_assert (!m_adjustments || !m_adjustments->m_skip_return);
+  tree new_type = build_adjusted_function_type (orig_type, &m_new_types,
+						m_method2func, false);
+
+  TREE_TYPE (m_fndecl) = new_type;
+  DECL_VIRTUAL_P (m_fndecl) = 0;
+  DECL_LANG_SPECIFIC (m_fndecl) = NULL;
+  if (m_method2func)
+    DECL_VINDEX (m_fndecl) = NULL_TREE;
+}
 
-  for (i = 0; i < inlen; i++)
+/* Given BASE and UNIT_OFFSET, find the corresponding record among replacement
+   structures.  */
+
+ipa_param_body_replacement *
+ipa_param_body_adjustments::lookup_replacement_1 (tree base,
+						  unsigned unit_offset)
+{
+  unsigned int len = m_replacements.length ();
+  for (unsigned i = 0; i < len; i++)
     {
-      struct ipa_parm_adjustment *n = &inner[i];
+      ipa_param_body_replacement *pbr = &m_replacements[i];
 
-      if (n->op == IPA_PARM_OP_REMOVE)
-	adjustments.quick_push (*n);
+      if (pbr->base == base
+	  && (pbr->unit_offset == unit_offset))
+	return pbr;
     }
+  return NULL;
+}
 
-  tmp.release ();
-  return adjustments;
+/* Given BASE and UNIT_OFFSET, find the corresponding replacement expression
+   and return it, assuming it is known it does not hold value by reference or
+   in reverse storage order.  */
+
+tree
+ipa_param_body_adjustments::lookup_replacement (tree base, unsigned unit_offset)
+{
+  ipa_param_body_replacement *pbr = lookup_replacement_1 (base, unit_offset);
+  if (!pbr)
+    return NULL;
+  return pbr->repl;
 }
 
 /* If T is an SSA_NAME, return NULL if it is not a default def or
@@ -592,165 +1269,634 @@ 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 an expression, return the structure describing how it should be
+   replaced if it accesses a part of a split parameter or NULL otherwise.
 
-   If IGNORE_DEFAULT_DEF is set, consider SSA_NAMEs which are not a
-   default def, otherwise bail on them.
+   Do not free the result, it will be deallocated when the object is destroyed.
 
-   If CONVERT is non-NULL, this function will set *CONVERT if the
-   expression provided is a component reference.  ADJUSTMENTS is the
-   adjustments vector.  */
+   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_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::get_expr_replacement (tree expr,
+						  bool ignore_default_def)
 {
-  if (TREE_CODE (**expr) == BIT_FIELD_REF
-      || TREE_CODE (**expr) == IMAGPART_EXPR
-      || TREE_CODE (**expr) == REALPART_EXPR)
-    {
-      *expr = &TREE_OPERAND (**expr, 0);
-      if (convert)
-	*convert = true;
-    }
+  tree base;
+  unsigned unit_offset;
 
-  poly_int64 offset, size, max_size;
-  bool reverse;
-  tree base
-    = get_ref_base_and_extent (**expr, &offset, &size, &max_size, &reverse);
-  if (!base || !known_size_p (size) || !known_size_p (max_size))
+  if (!isra_get_ref_base_and_offset (expr, &base, &unit_offset))
     return NULL;
 
-  if (TREE_CODE (base) == MEM_REF)
-    {
-      offset += mem_ref_offset (base).force_shwi () * BITS_PER_UNIT;
-      base = TREE_OPERAND (base, 0);
-    }
-
   base = get_ssa_base_param (base, ignore_default_def);
   if (!base || TREE_CODE (base) != PARM_DECL)
     return NULL;
+  return lookup_replacement_1 (base, unit_offset);
+}
 
-  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];
+/* 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.  */
 
-      if (adj->base == base
-	  && (known_eq (adj->offset, offset) || adj->op == IPA_PARM_OP_REMOVE))
-	{
-	  cand = adj;
-	  break;
-	}
+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.  */
+
+tree
+ipa_param_body_adjustments::replace_removed_params_ssa_names (tree old_name,
+							      gimple *stmt)
+{
+  gcc_assert (!m_id);
+  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;
 
-  if (!cand || cand->op == IPA_PARM_OP_COPY || cand->op == IPA_PARM_OP_REMOVE)
+  tree repl = get_replacement_ssa_base (decl);
+  if (!repl)
     return NULL;
-  return cand;
+
+  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 && (dump_flags & TDF_DETAILS))
+    {
+      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, 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 - note that when the function
+   encounters a BIT_FIELD_REF, IMAGPART_EXPR or REALPART_EXPR, it will modify
+   their bases instead of the expressions themselves and then also performs any
+   necessary conversions.  */
 
 bool
-ipa_modify_expr (tree *expr, bool convert,
-		 ipa_parm_adjustment_vec adjustments)
+ipa_param_body_adjustments::modify_expression (tree *expr_p, bool convert)
 {
-  struct ipa_parm_adjustment *cand
-    = ipa_get_adjustment_candidate (&expr, &convert, adjustments, false);
-  if (!cand)
-    return false;
+  tree expr = *expr_p;
 
-  tree src;
-  if (cand->by_ref)
+  if (TREE_CODE (expr) == BIT_FIELD_REF
+      || TREE_CODE (expr) == IMAGPART_EXPR
+      || TREE_CODE (expr) == REALPART_EXPR)
     {
-      src = build_simple_mem_ref (cand->new_decl);
-      REF_REVERSE_STORAGE_ORDER (src) = cand->reverse;
+      expr_p = &TREE_OPERAND (expr, 0);
+      expr = *expr_p;
+      convert = true;
     }
-  else
-    src = cand->new_decl;
 
+  ipa_param_body_replacement *pbr = get_expr_replacement (expr, false);
+  if (!pbr)
+    return false;
+
+  tree 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 assignment statement STMT contains any expressions that need to
+   replaced with a different one as noted by ADJUSTMENTS, do so.  Handle any
+   potential type incompatibilities.  If any conversion sttements have to be
+   pre-pended to STMT, they will be added to EXTRA_STMTS.  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_seq *extra_stmts)
 {
-  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, "                 ");
+  rhs_p = gimple_assign_rhs1_ptr (stmt);
+  lhs_p = gimple_assign_lhs_ptr (stmt);
+
+  any = modify_expression (lhs_p, false);
+  any |= modify_expression (rhs_p, false);
+  if (any
+      && !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
-	first = false;
+	{
+	  tree new_rhs = fold_build1_loc (gimple_location (stmt),
+					  VIEW_CONVERT_EXPR, TREE_TYPE (*lhs_p),
+					  *rhs_p);
+	  tree tmp = force_gimple_operand (new_rhs, extra_stmts, true,
+					   NULL_TREE);
+	  gimple_assign_set_rhs1 (stmt, tmp);
+	}
+      return true;
+    }
+
+  return any;
+}
+
+/* Data passed to remap_split_decl_to_dummy through walk_tree.  */
+
+struct simple_tree_swap_info
+{
+  /* Change FROM to TO.  */
+  tree from, to;
+  /* And set DONE to true when doing so.  */
+  bool done;
+};
+
+/* Simple remapper to remap a split parameter to the same expression based on a
+   special dummy decl so that edge redirections can detect transitive splitting
+   and finish them.  */
 
-      fprintf (file, "%i. base_index: %i - ", i, adj->base_index);
-      print_generic_expr (file, parms[adj->base_index]);
-      if (adj->base)
+static tree
+remap_split_decl_to_dummy (tree *tp, int *walk_subtrees, void *data)
+{
+  tree t = *tp;
+
+  if (DECL_P (t) || TREE_CODE (t) == SSA_NAME)
+    {
+      struct simple_tree_swap_info *swapinfo
+	= (struct simple_tree_swap_info *) data;
+      if (t == swapinfo->from
+	  || (TREE_CODE (t) == SSA_NAME
+	      && SSA_NAME_VAR (t) == swapinfo->from))
 	{
-	  fprintf (file, ", base: ");
-	  print_generic_expr (file, adj->base);
+	  *tp = swapinfo->to;
+	  swapinfo->done = true;
+	}
+      *walk_subtrees = 0;
+    }
+  else if (TYPE_P (t))
+      *walk_subtrees = 0;
+  else
+    *walk_subtrees = 1;
+  return NULL_TREE;
+}
+
+
+/* If the call statement pointed at by STMT_P contains any expressions that
+   need to replaced with a different one as noted by ADJUSTMENTS, do so.  f the
+   statement needs to be rebuilt, do so.  Return true if any modifications have
+   been performed.
+
+   If the method is invoked as a part of IPA clone materialization and if any
+   parameter split is transitive, i.e. it applies to the functin that is being
+   modified and also to the callee of the statement, replace the parameter
+   passed to old callee with an equivalent expression based on a dummy decl
+   followed by PARM_DECLs representing the actual replacements.  The actual
+   replacements will be then converted into SSA_NAMEs and then
+   ipa_param_adjustments::modify_call will find the appropriate ones and leave
+   only those in the call.  */
+
+bool
+ipa_param_body_adjustments::modify_call_stmt (gcall **stmt_p)
+{
+  gcall *stmt = *stmt_p;
+  auto_vec <unsigned, 4> pass_through_args;
+  auto_vec <unsigned, 4> pass_through_pbr_indices;
+
+  if (m_split_modifications_p && m_id)
+    {
+      for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
+	{
+	  tree t = gimple_call_arg (stmt, i);
+	  gcc_assert (TREE_CODE (t) != BIT_FIELD_REF
+		      && TREE_CODE (t) != IMAGPART_EXPR
+		      && TREE_CODE (t) != REALPART_EXPR);
+
+	  tree base;
+	  unsigned unit_offset;
+	  if (!isra_get_ref_base_and_offset (t, &base, &unit_offset))
+	    continue;
+
+	  bool by_ref = false;
+	  if (TREE_CODE (base) == SSA_NAME)
+	    {
+	      if (!SSA_NAME_IS_DEFAULT_DEF (base))
+		continue;
+	      base = SSA_NAME_VAR (base);
+	      gcc_checking_assert (base);
+	      by_ref = true;
+	    }
+	  if (TREE_CODE (base) != PARM_DECL)
+	    continue;
+
+	  bool base_among_replacements = false;
+	  unsigned j, repl_list_len = m_replacements.length ();
+	  for (j = 0; j < repl_list_len; j++)
+	    {
+	      ipa_param_body_replacement *pbr = &m_replacements[j];
+	      if (pbr->base == base)
+		{
+		  base_among_replacements = true;
+		  break;
+		}
+	    }
+	  if (!base_among_replacements)
+	    continue;
+
+	  /* We still have to distinguish between an end-use that we have to
+	     transform now and a pass-through, which happens in the following
+	     two cases.  */
+
+	  /* TODO: After we adjust ptr_parm_has_nonarg_uses to also consider
+	     &MEM_REF[ssa_name + offset], we will also have to detect that case
+	     here.    */
+
+	  if (TREE_CODE (t) == SSA_NAME
+	      && SSA_NAME_IS_DEFAULT_DEF (t)
+	      && SSA_NAME_VAR (t)
+	      && TREE_CODE (SSA_NAME_VAR (t)) == PARM_DECL)
+	    {
+	      /* This must be a by_reference pass-through.  */
+	      gcc_assert (POINTER_TYPE_P (TREE_TYPE (t)));
+	      pass_through_args.safe_push (i);
+	      pass_through_pbr_indices.safe_push (j);
+	    }
+	  else if (!by_ref && AGGREGATE_TYPE_P (TREE_TYPE (t)))
+	    {
+	      /* Currently IPA-SRA guarantees the aggregate access type
+		 exactly matches in this case.  So if it does not match, it is
+		 a pass-through argument that will be sorted out at edge
+		 redirection time.  */
+	      ipa_param_body_replacement *pbr
+		= lookup_replacement_1 (base, unit_offset);
+
+	      if (!pbr
+		  || (TYPE_MAIN_VARIANT (TREE_TYPE (t))
+		      != TYPE_MAIN_VARIANT (TREE_TYPE (pbr->repl))))
+		{
+		  pass_through_args.safe_push (i);
+		  pass_through_pbr_indices.safe_push (j);
+		}
+	    }
 	}
-      if (adj->new_decl)
+    }
+
+  unsigned nargs = gimple_call_num_args (stmt);
+  if (!pass_through_args.is_empty ())
+    {
+      auto_vec<tree, 16> vargs;
+      unsigned pt_idx = 0;
+      for (unsigned i = 0; i < nargs; i++)
 	{
-	  fprintf (file, ", new_decl: ");
-	  print_generic_expr (file, adj->new_decl);
+	  if (pt_idx < pass_through_args.length ()
+	      && i == pass_through_args[pt_idx])
+	    {
+	      unsigned j = pass_through_pbr_indices[pt_idx];
+	      pt_idx++;
+	      tree base = m_replacements[j].base;
+
+	      /* Map base will get mapped to the special transitive-isra marker
+		 dummy decl. */
+	      struct simple_tree_swap_info swapinfo;
+	      swapinfo.from = base;
+	      swapinfo.to = m_replacements[j].dummy;
+	      swapinfo.done = false;
+	      tree arg = gimple_call_arg (stmt, i);
+	      walk_tree (&arg, remap_split_decl_to_dummy, &swapinfo, NULL);
+	      gcc_assert (swapinfo.done);
+	      vargs.safe_push (arg);
+	      /* Now let's push all replacements pertaining to this parameter
+		 so that all gimple register ones get correct SSA_NAMES.  Edge
+		 redirection will weed out the dummy argument as well as all
+		 unused replacements later.  */
+	      unsigned int repl_list_len = m_replacements.length ();
+	      for (; j < repl_list_len; j++)
+		{
+		  if (m_replacements[j].base != base)
+		    break;
+		  vargs.safe_push (m_replacements[j].repl);
+		}
+	    }
+	  else
+	    {
+	      tree t = gimple_call_arg (stmt, i);
+	      modify_expression (&t, true);
+	      vargs.safe_push (t);
+	    }
 	}
-      if (adj->new_ssa_base)
+      gcall *new_stmt = gimple_build_call_vec (gimple_call_fn (stmt), vargs);
+      gimple_call_set_chain (new_stmt, gimple_call_chain (stmt));
+      gimple_call_copy_flags (new_stmt, stmt);
+      if (tree lhs = gimple_call_lhs (stmt))
 	{
-	  fprintf (file, ", new_ssa_base: ");
-	  print_generic_expr (file, adj->new_ssa_base);
+	  modify_expression (&lhs, false);
+	  gimple_call_set_lhs (new_stmt, lhs);
 	}
+      *stmt_p = new_stmt;
+      return true;
+    }
 
-      if (adj->op == IPA_PARM_OP_COPY)
-	fprintf (file, ", copy_param");
-      else if (adj->op == IPA_PARM_OP_REMOVE)
-	fprintf (file, ", remove_param");
-      else
+  /* Otherwise, no need to rebuild the statement, let's just modify arguments
+     and the LHS if/as appropriate.  */
+  bool modified = false;
+  for (unsigned i = 0; i < nargs; i++)
+    {
+      tree *t = gimple_call_arg_ptr (stmt, i);
+      modified |= modify_expression (t, true);
+    }
+
+  if (gimple_call_lhs (stmt))
+    {
+      tree *t = gimple_call_lhs_ptr (stmt);
+      modified |= modify_expression (t, false);
+    }
+
+  return modified;
+}
+
+/* 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.  If any conversion sttements have to be pre-pended to
+   STMT, they will be added to EXTRA_STMTS.  Return true iff the statement was
+   modified.  */
+
+bool
+ipa_param_body_adjustments::modify_gimple_stmt (gimple **stmt,
+						gimple_seq *extra_stmts)
+{
+  bool modified = false;
+  tree *t;
+
+  switch (gimple_code (*stmt))
+    {
+    case GIMPLE_RETURN:
+      t = gimple_return_retval_ptr (as_a <greturn *> (*stmt));
+      if (m_adjustments && m_adjustments->m_skip_return)
+	*t = NULL_TREE;
+      else if (*t != NULL_TREE)
+	modified |= modify_expression (t, true);
+      break;
+
+    case GIMPLE_ASSIGN:
+      modified |= modify_assignment (*stmt, extra_stmts);
+      break;
+
+    case GIMPLE_CALL:
+      modified |= modify_call_stmt ((gcall **) stmt);
+      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_expression (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_expression (t, false);
+	  }
+      }
+      break;
+
+    default:
+      break;
+    }
+  return modified;
+}
+
+
+/* Traverse body of the current function and perform the requested adjustments
+   on its statements.  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))
+	{
+	  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);
+	    }
+	}
+
+      gsi = gsi_start_bb (bb);
+      while (!gsi_end_p (gsi))
+	{
+	  gimple *stmt = gsi_stmt (gsi);
+	  gimple *stmt_copy = stmt;
+	  gimple_seq extra_stmts = NULL;
+	  bool modified = modify_gimple_stmt (&stmt, &extra_stmts);
+	  if (stmt != stmt_copy)
+	    {
+	      gcc_checking_assert (modified);
+	      gsi_replace (&gsi, stmt, false);
+	    }
+	  if (!gimple_seq_empty_p (extra_stmts))
+	    gsi_insert_seq_before (&gsi, extra_stmts, GSI_SAME_STMT);
+
+	  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);
+	}
+    }
+
+  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))
 	{
-	  fprintf (file, ", offset ");
-	  print_dec (adj->offset, file);
+	  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);
 	}
-      if (adj->by_ref)
-	fprintf (file, ", by_ref");
-      print_node_brief (file, ", type: ", adj->type, 0);
-      fprintf (file, "\n");
     }
-  parms.release ();
+}
+
+/* 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.  The main entry point for modification of standalone
+   functions that is not part of IPA clone materialization.  */
+
+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 71fc4a201aa..b9da00bb5c9 100644
--- a/gcc/ipa-param-manipulation.h
+++ b/gcc/ipa-param-manipulation.h
@@ -16,101 +16,394 @@ for more details.
 
 You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING3.  If not see
-<http://www.gnu.org/licenses/>.  */
+<http://www.gnu.org/licenses/>.
+
+
+
+This file defines classes and other data structures that are used to manipulate
+the prototype of a function, especially to create, remove or split its formal
+parameters, but also to remove its return value, and also its call statements
+correspondingly.
+
+The most basic one is a vector of structures ipa_adjusted_param.  It is simply
+a description how the new parameters should look like after the transformation
+in what way they relate to the previous ones (if in any).  Such relation to an
+old parameter can be an outright copy or an IPA-SRA replacement. If an old
+parameter is not listed or otherwise mentioned, it is removed as unused or at
+least unnecessary.  Note that this most basic structure does not work for
+modifying calls of functions with variable number of arguments.
+
+Class ipa_param_adjustments is only a little more than a thin encapsulation of
+a vector of ipa_param_adjustments.  Along with this vector it contains an index
+of the first potential vararg argument and a boolean flag whether the return
+value should be removed or not.  Moreover, the class contains method
+modify_call which can transform a call statement so that it correctly calls a
+modified function.  These two data structures were designed to have a small
+memory footprint because they are allocated for each clone of a call graph node
+that has its prototype changed and live until the end of IPA clone
+materialization and call redirection phase.
+
+On the other hand, class ipa_param_body_adjustments can afford to allocate more
+data because its life span is much smaller, it is allocated and destroyed in
+the course of materialization of each single clone that needs it or only when a
+particular pass needs to change a function it is operating on.  This class has
+various methods required to change function declaration and the body of the
+function according to instructions given either by class ipa_param_adjustments
+or only a vector of ipa_adjusted_params.
+
+When these classes are used in the context of call graph clone materialization
+and subsequent call statement redirection - which is the point at which we
+modify arguments in call statements - they need to cooperate with each other in
+order to handle what we refer to as transitive (IPA-SRA) splits.  These are
+situations when a formal parameter of one function is split into several
+smaller ones and some of them are then passed on in a call to another function
+because the formal parameter of this callee has also been split.
+
+Consider a simple example:
+
+struct S {int a, b, c;};
+struct Z {int x; S s;};
+
+foo (S s)
+{
+  use (s.b);
+}
+
+bar (Z z)
+{
+  use (z.s.a);
+  foo (z.s);
+}
+
+baz ()
+{
+  bar (*global);
+}
+
+Both bar and foo would have their parameter split.  Foo would receive one
+replacement representing s.b.  Function bar would see its parameter split into
+one replacement representing z.s.a and another representing z.s.b which would
+be passed on to foo.  It would be a so called transitive split IPA-SRA
+replacement, one which is passed in a call as an actual argument to another
+IPA-SRA replacement in another function.
+
+Note that the call chain the example can be arbitrarily long and recursive and
+that any function in it can be cloned by another IPA pass and any number of
+adjacent functions in the call chain can be inlined into each other.  Call
+redirection takes place only after bodies of the function have been modified by
+all of the above.
+
+Call redirection has to be able to find the right decl or SSA_NAME that
+corresponds to the transitive split in the caller.  The SSA names are assigned
+right after clone materialization/ modification and cannot be "added"
+afterwards.  Moreover, if the caller has been inlined the SSA_NAMEs in question
+no longer belong to PARM_DECLs but to VAR_DECLs, indistinguishable from any
+others.
+
+Therefore, when clone materialization finds a call statement which it knows is
+a part of a transitive split, it will modify it into:
+
+  foo (DUMMY_Z_VAR.s, repl_for_a, repl_for_b, <rest of original arguments>);
+
+It will also store {DUMMY_S_VAR, 32} and {DUMMY_S_VAR, 64} representing offsets
+of z.s.a and z.s.b (assuming a 32-bit int) into foo's cgraph node
+clone->performed_splits vector (which is storing structures of type
+ipa_param_performed_split also defined in this header file).
+
+Call redirection will identify that expression DUMMY_Z_VAR.s is based on a
+variable stored in performed_splits vector and learn that the following
+arguments, already in SSA form, represent offsets 32 and 64 in a split original
+parameter.  It subtracts offset of DUMMY_Z_VAR.s from 32 and 64 and arrives at
+offsets 0 and 32 within callee's original parameter.  At this point it also
+knows from the call graph that only the bit with offset 32 is needed and so
+changes the call statement into final:
+
+bar (repl_for_b, <rest of original arguments>);  */
 
 #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.  */
+enum ipa_param_name_prefix_indices
+  {
+   IPA_PARAM_PREFIX_SYNTH,
+   IPA_PARAM_PREFIX_ISRA,
+   IPA_PARAM_PREFIX_SIMD,
+   IPA_PARAM_PREFIX_MASK,
+   IPA_PARAM_PREFIX_COUNT
+};
+
+/* 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 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
+  /* 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,
+
+    /* Split parameter as indicated by fields base_index, offset and type.  */
+  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.  */
 
-  /* Type of the new parameter.  However, if by_ref is true, the real type will
-     be a pointer to this type.  */
+struct GTY(()) ipa_adjusted_param
+{
+  /* Type of the new parameter.  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;
+  /* Offset into the original parameter (for the cases when the new parameter
+     is a component of an original one).  Required for IPA_PARM_OP_SPLIT
+     operation.  */
+  unsigned unit_offset;
 
-  /* 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;
+  /* 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;
 
-  /* This holds the prefix to be used for the new DECL_NAME.  */
-  const char *arg_prefix;
+  /* Zero based index of the parameter this one is based on in the previous
+     clone.  If there is no previous clone, it must be equal to base_index.  */
+  unsigned prev_clone_index : IPA_PARAM_MAX_INDEX_BITS;
 
-  /* Offset into the original parameter (for the cases when the new parameter
-     is a component of an original one).  */
-  poly_int64_pod 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;
+  /* If set, this structure describes a parameter copied over from a previous
+     IPA clone, any transformations are thus not to be re-done.  */
+  unsigned prev_clone_adjustment : 1;
 
-  /* 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.  */
-  unsigned by_ref : 1;
+  /* A bit free for the user.  */
+  unsigned user_flag : 1;
+};
+
+void ipa_dump_adjusted_parameters (FILE *f,
+				   vec<ipa_adjusted_param, va_gc> *adj_params);
+
+/* Structure to remember the split performed on a node so that edge redirection
+   (i.e. splitting arguments of call statements) know how split formal
+   parameters of the caller are represented.  */
+
+struct GTY(()) ipa_param_performed_split
+{
+  /* The dummy VAR_DECL that was created instead of the split parameter that
+     sits in the call in the meantime between clone materialization and call
+     redirection.  All entries in a vector of performed splits that correspond
+     to the same dumy decl must be grouped together.  */
+  tree dummy_decl;
+  /* Offset into the original parameter.  */
+  unsigned unit_offset;
+};
+
+/* 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.  Used to describe all cgraph node clones that have their parameters
+   changed, therefore the class should only have a small memory footprint.  */
+
+class GTY(()) ipa_param_adjustments
+{
+public:
+  /* Constructor from NEW_PARAMS showing how new parameters should look like
+      plus copying any pre-existing actual arguments starting from argument
+      with index ALWAYS_COPY_START (if non-negative, negative means do not copy
+      anything beyond what is described in NEW_PARAMS), and SKIP_RETURN, which
+      indicates that the function should return void after transformation.  */
+
+  ipa_param_adjustments (vec<ipa_adjusted_param, va_gc> *new_params,
+			 int always_copy_start, bool skip_return)
+    : m_adj_params (new_params), m_always_copy_start (always_copy_start),
+    m_skip_return (skip_return)
+    {}
+
+  gcall *modify_call (gcall *stmt,
+		      vec<ipa_param_performed_split, va_gc> *performed_splits,
+		      tree callee_decl, bool update_references);
+  bool first_param_intact_p ();
+  tree build_new_function_type (tree old_type, bool type_is_original_p);
+  tree adjust_decl (tree orig_decl);
+  void get_surviving_params (vec<bool> *surviving_params);
+  void get_updated_indices (vec<int> *new_indices);
+
+  void dump (FILE *f);
+  void debug ();
+
+  /* How the known part of arguments should look like.  */
+  vec<ipa_adjusted_param, va_gc> *m_adj_params;
+
+  /* If non-negative, copy any arguments starting at this offset without any
+     modifications so that functions with variable number of arguments can be
+     modified. This number should be equal to the number of original forma
+     parameters.  */
+  int m_always_copy_start;
+  /* If true, make the function not return any value.  */
+  bool m_skip_return;
+
+private:
+  ipa_param_adjustments () {}
+
+  void init (vec<tree> *cur_params);
+  int get_max_base_index ();
+  bool method2func_p (tree orig_type);
+};
+
+/* Structure used to map expressions accessing split or replaced parameters to
+   new PARM_DECLs.  */
+
+struct ipa_param_body_replacement
+{
+  /* The old decl of the original parameter.   */
+  tree base;
+  /* The new decl it should be replaced with.  */
+  tree repl;
+  /* When modifying clones during IPA clone materialization, this is a dummy
+     decl used to mark calls in which we need to apply transitive splitting,
+     these dummy delcls are inserted as arguments to such calls and then
+     followed by all the replacements with offset info stored in
+     ipa_param_performed_split.
+
+     Users of ipa_param_body_adjustments that modify standalone functions
+     outside of IPA clone materialization can use this field for their internal
+     purposes.  */
+  tree dummy;
+  /* The offset within BASE that REPL represents.  */
+  unsigned unit_offset;
+};
+
+struct ipa_replace_map;
+
+/* Class used when actually performing adjustments to formal parameters of a
+   function to map accesses that need to be replaced to replacements.  The
+   class attempts to work in two very different sets of circumstances: as a
+   part of tree-inine.c's tree_function_versioning machinery to clone functions
+   (when M_ID is not NULL) and in s standalone fashion, modifying an existing
+   function in place (when M_ID is NULL).  While a lot of stuff handled in a
+   unified way in both modes, there are many aspects of the processs that
+   requires distinct paths.  */
+
+class ipa_param_body_adjustments
+{
+public:
+  ipa_param_body_adjustments (ipa_param_adjustments *adjustments,
+			      tree fndecl, tree old_fndecl,
+			      struct copy_body_data *id, tree *vars,
+			      vec<ipa_replace_map *, va_gc> *tree_map);
+  ipa_param_body_adjustments (ipa_param_adjustments *adjustments,
+			      tree fndecl);
+  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,
+			     tree dummy = NULL_TREE);
+  tree 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);
+  bool modify_gimple_stmt (gimple **stmt, gimple_seq *extra_stmts);
+  tree get_new_param_chain ();
+
+  /* Pointers to data structures defining how the function should be
+     modified.  */
+  vec<ipa_adjusted_param, va_gc> *m_adj_params;
+  ipa_param_adjustments *m_adjustments;
+
+  /* Vector of old parameter declarations that must have their debug bind
+     statements re-mapped and debug decls created.  */
+
+  auto_vec<tree, 16> m_reset_debug_decls;
+
+  /* Set to true if there are any IPA_PARAM_OP_SPLIT adjustments among stored
+     adjustments.  */
+  bool m_split_modifications_p;
+private:
+  void common_initialization (tree old_fndecl, tree *vars,
+			      vec<ipa_replace_map *, va_gc> *tree_map);
+  unsigned get_base_index (ipa_adjusted_param *apm);
+  ipa_param_body_replacement *lookup_replacement_1 (tree base,
+						    unsigned unit_offset);
+  tree replace_removed_params_ssa_names (tree old_name, gimple *stmt);
+  bool modify_expression (tree *expr_p, bool convert);
+  bool modify_assignment (gimple *stmt, gimple_seq *extra_stmts);
+  bool modify_call_stmt (gcall **stmt_p);
+  bool modify_cfun_body ();
+  void reset_debug_stmts ();
+
+
+  /* Declaration of the function that is being transformed.  */
+
+  tree m_fndecl;
+
+  /* If non-NULL, the tree-inline master data structure guiding materialization
+     of the current clone.  */
+  struct copy_body_data *m_id;
+
+  /* Vector of old parameter declarations (before changing them).  */
+
+  auto_vec<tree, 16> m_oparms;
+
+  /* Vector of parameter declarations the function will have after
+     transformation.  */
+
+  auto_vec<tree, 16> m_new_decls;
+
+  /* If the function type has non-NULL TYPE_ARG_TYPES, this is the vector of
+     these types after transformation, otherwise an empty one.  */
+
+  auto_vec<tree, 16> m_new_types;
+
+  /* Vector of structures telling how to replace old parameters in in the
+     function body.  TODO: Even though there usually be only few, but should we
+     use a hash?  */
+
+  auto_vec<ipa_param_body_replacement, 16> m_replacements;
+
+  /* Vector for remapping SSA_BASES from old parameter declarations that are
+     being removed as a part of the transformation.  Before a new VAR_DECL is
+     created, it holds the old PARM_DECL, once the variable is built it is
+     stored here.  */
+
+  auto_vec<tree> m_removed_decls;
+
+  /* Hash to quickly lookup the item in m_removed_decls given the old decl.  */
+
+  hash_map<tree, unsigned> m_removed_map;
+
+  /* True iff the transformed function is a class method that is about to loose
+     its this pointer and must be converted to a normal function.  */
+
+  bool m_method2func;
 };
 
-typedef vec<ipa_parm_adjustment> ipa_parm_adjustment_vec;
-
-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);
+void push_function_arg_decls (vec<tree> *args, tree fndecl);
+void push_function_arg_types (vec<tree> *types, tree fntype);
 
 #endif	/* IPA_PARAM_MANIPULATION_H */
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index 1a0e12e6c0c..edd577c6b08 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -4859,31 +4859,24 @@ adjust_agg_replacement_values (struct cgraph_node *node,
 			       struct ipa_agg_replacement_value *aggval)
 {
   struct ipa_agg_replacement_value *v;
-  int i, c = 0, d = 0, *adj;
 
-  if (!node->clone.combined_args_to_skip)
+  if (!node->clone.param_adjustments)
     return;
 
+  auto_vec<int, 16> new_indices;
+  node->clone.param_adjustments->get_updated_indices (&new_indices);
   for (v = aggval; v; v = v->next)
     {
-      gcc_assert (v->index >= 0);
-      if (c < v->index)
-	c = v->index;
-    }
-  c++;
-
-  adj = XALLOCAVEC (int, c);
-  for (i = 0; i < c; i++)
-    if (bitmap_bit_p (node->clone.combined_args_to_skip, i))
-      {
-	adj[i] = -1;
-	d++;
-      }
-    else
-      adj[i] = i - d;
+      gcc_checking_assert (v->index >= 0);
 
-  for (v = aggval; v; v = v->next)
-    v->index = adj[v->index];
+      if ((unsigned) v->index < new_indices.length ())
+	v->index = new_indices[v->index];
+      else
+	/* This can happen if we know about a constant passed by reference by
+	   an argument which is never actually used for anything, let alone
+	   loading that constant.  */
+	v->index = -1;
+    }
 }
 
 /* Dominator walker driving the ipcp modification phase.  */
@@ -5009,24 +5002,41 @@ ipcp_modif_dom_walker::before_dom_children (basic_block bb)
 static void
 ipcp_update_bits (struct cgraph_node *node)
 {
-  tree parm = DECL_ARGUMENTS (node->decl);
-  tree next_parm = parm;
   ipcp_transformation *ts = ipcp_get_transformation_summary (node);
 
   if (!ts || vec_safe_length (ts->bits) == 0)
     return;
-
   vec<ipa_bits *, va_gc> &bits = *ts->bits;
   unsigned count = bits.length ();
+  if (!count)
+    return;
 
-  for (unsigned i = 0; i < count; ++i, parm = next_parm)
+  auto_vec<int, 16> new_indices;
+  bool need_remapping = false;
+  if (node->clone.param_adjustments)
     {
-      if (node->clone.combined_args_to_skip
-	  && bitmap_bit_p (node->clone.combined_args_to_skip, i))
-	continue;
+      node->clone.param_adjustments->get_updated_indices (&new_indices);
+      need_remapping = true;
+    }
+  auto_vec <tree, 16> parm_decls;
+  push_function_arg_decls (&parm_decls, node->decl);
 
+  for (unsigned i = 0; i < count; ++i)
+    {
+      tree parm;
+      if (need_remapping)
+	{
+	  if (i >= new_indices.length ())
+	    continue;
+	  int idx = new_indices[i];
+	  if (idx < 0)
+	    continue;
+	  parm = parm_decls[idx];
+	}
+      else
+	parm = parm_decls[i];
       gcc_checking_assert (parm);
-      next_parm = DECL_CHAIN (parm);
+
 
       if (!bits[i]
 	  || !(INTEGRAL_TYPE_P (TREE_TYPE (parm))
@@ -5101,22 +5111,42 @@ ipcp_update_bits (struct cgraph_node *node)
 static void
 ipcp_update_vr (struct cgraph_node *node)
 {
-  tree fndecl = node->decl;
-  tree parm = DECL_ARGUMENTS (fndecl);
-  tree next_parm = parm;
   ipcp_transformation *ts = ipcp_get_transformation_summary (node);
   if (!ts || vec_safe_length (ts->m_vr) == 0)
     return;
   const vec<ipa_vr, va_gc> &vr = *ts->m_vr;
   unsigned count = vr.length ();
+  if (!count)
+    return;
 
-  for (unsigned i = 0; i < count; ++i, parm = next_parm)
+  auto_vec<int, 16> new_indices;
+  bool need_remapping = false;
+  if (node->clone.param_adjustments)
     {
-      if (node->clone.combined_args_to_skip
-	  && bitmap_bit_p (node->clone.combined_args_to_skip, i))
-	continue;
+      node->clone.param_adjustments->get_updated_indices (&new_indices);
+      need_remapping = true;
+    }
+  auto_vec <tree, 16> parm_decls;
+  push_function_arg_decls (&parm_decls, node->decl);
+
+  for (unsigned i = 0; i < count; ++i)
+    {
+      tree parm;
+      int remapped_idx;
+      if (need_remapping)
+	{
+	  if (i >= new_indices.length ())
+	    continue;
+	  remapped_idx = new_indices[i];
+	  if (remapped_idx < 0)
+	    continue;
+	}
+      else
+	remapped_idx = i;
+
+      parm = parm_decls[remapped_idx];
+
       gcc_checking_assert (parm);
-      next_parm = DECL_CHAIN (parm);
       tree ddef = ssa_default_def (DECL_STRUCT_FUNCTION (node->decl), parm);
 
       if (!ddef || !is_gimple_reg (parm))
@@ -5131,7 +5161,8 @@ ipcp_update_vr (struct cgraph_node *node)
 	    {
 	      if (dump_file)
 		{
-		  fprintf (dump_file, "Setting value range of param %u ", i);
+		  fprintf (dump_file, "Setting value range of param %u "
+			   "(now %i) ", i, remapped_idx);
 		  fprintf (dump_file, "%s[",
 			   (vr[i].type == VR_ANTI_RANGE) ? "~" : "");
 		  print_decs (vr[i].min, dump_file);
diff --git a/gcc/ipa-split.c b/gcc/ipa-split.c
index cecfe05d0a9..375a15c44b3 100644
--- a/gcc/ipa-split.c
+++ b/gcc/ipa-split.c
@@ -1326,13 +1326,38 @@ split_function (basic_block return_bb, class split_point *split_point,
 	  }
     }
 
+  ipa_param_adjustments *adjustments;
+  bool skip_return = (!split_part_return_p
+		      || !split_point->split_part_set_retval);
+  /* TODO: Perhaps get rid of args_to_skip entirely, after we make sure the
+     debug info generation and discrepancy avoiding works well too.  */
+  if ((args_to_skip && !bitmap_empty_p (args_to_skip))
+      || skip_return)
+    {
+      vec<ipa_adjusted_param, va_gc> *new_params = NULL;
+      unsigned j;
+      for (parm = DECL_ARGUMENTS (current_function_decl), j = 0;
+	   parm; parm = DECL_CHAIN (parm), j++)
+	if (!args_to_skip || !bitmap_bit_p (args_to_skip, j))
+	  {
+	    ipa_adjusted_param adj;
+	    memset (&adj, 0, sizeof (adj));
+	    adj.op = IPA_PARAM_OP_COPY;
+	    adj.base_index = j;
+	    adj.prev_clone_index = j;
+	    vec_safe_push (new_params, adj);
+	  }
+      adjustments = new ipa_param_adjustments (new_params, j, skip_return);
+    }
+  else
+    adjustments = NULL;
+
   /* Now create the actual clone.  */
   cgraph_edge::rebuild_edges ();
   node = cur_node->create_version_clone_with_body
-    (vNULL, NULL, args_to_skip,
-     !split_part_return_p || !split_point->split_part_set_retval,
+    (vNULL, NULL, adjustments,
      split_point->split_bbs, split_point->entry_bb, "part");
-
+  delete adjustments;
   node->split_part = true;
 
   if (cur_node->same_comdat_group)
@@ -1469,6 +1494,7 @@ split_function (basic_block return_bb, class split_point *split_point,
 	      = gimple_build_debug_bind (ddecl, unshare_expr (arg), call);
 	    gsi_insert_after (&gsi, def_temp, GSI_NEW_STMT);
 	  }
+      BITMAP_FREE (args_to_skip);
     }
 
   /* We avoid address being taken on any variable used by split part,
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index bc0f0107333..147975ba869 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -1804,8 +1804,7 @@ output_cgraph_opt_summary_p (struct cgraph_node *node)
 {
   return ((node->clone_of || node->former_clone_of)
 	  && (node->clone.tree_map
-	      || node->clone.args_to_skip
-	      || node->clone.combined_args_to_skip));
+	      || node->clone.param_adjustments));
 }
 
 /* Output optimization summary for EDGE to OB.  */
@@ -1822,42 +1821,53 @@ output_node_opt_summary (struct output_block *ob,
 			 struct cgraph_node *node,
 			 lto_symtab_encoder_t encoder)
 {
-  unsigned int index;
-  bitmap_iterator bi;
   struct ipa_replace_map *map;
-  struct bitpack_d bp;
   int i;
   struct cgraph_edge *e;
 
-  if (node->clone.args_to_skip)
-    {
-      streamer_write_uhwi (ob, bitmap_count_bits (node->clone.args_to_skip));
-      EXECUTE_IF_SET_IN_BITMAP (node->clone.args_to_skip, 0, index, bi)
-	streamer_write_uhwi (ob, index);
-    }
-  else
-    streamer_write_uhwi (ob, 0);
-  if (node->clone.combined_args_to_skip)
+  /* TODO: Should this code be moved to ipa-param-manipulation?  */
+  struct bitpack_d bp;
+  bp = bitpack_create (ob->main_stream);
+  bp_pack_value (&bp, (node->clone.param_adjustments != NULL), 1);
+  streamer_write_bitpack (&bp);
+  if (ipa_param_adjustments *adjustments = node->clone.param_adjustments)
     {
-      streamer_write_uhwi (ob, bitmap_count_bits (node->clone.combined_args_to_skip));
-      EXECUTE_IF_SET_IN_BITMAP (node->clone.combined_args_to_skip, 0, index, bi)
-	streamer_write_uhwi (ob, index);
+      streamer_write_uhwi (ob, vec_safe_length (adjustments->m_adj_params));
+      ipa_adjusted_param *adj;
+      FOR_EACH_VEC_SAFE_ELT (adjustments->m_adj_params, i, adj)
+	{
+	  bp = bitpack_create (ob->main_stream);
+	  bp_pack_value (&bp, adj->base_index, IPA_PARAM_MAX_INDEX_BITS);
+	  bp_pack_value (&bp, adj->prev_clone_index, IPA_PARAM_MAX_INDEX_BITS);
+	  bp_pack_value (&bp, adj->op, 2);
+	  bp_pack_value (&bp, adj->param_prefix_index, 2);
+	  bp_pack_value (&bp, adj->prev_clone_adjustment, 1);
+	  bp_pack_value (&bp, adj->reverse, 1);
+	  bp_pack_value (&bp, adj->user_flag, 1);
+	  streamer_write_bitpack (&bp);
+	  if (adj->op == IPA_PARAM_OP_SPLIT
+	      || adj->op == IPA_PARAM_OP_NEW)
+	    {
+	      stream_write_tree (ob, adj->type, true);
+	      if (adj->op == IPA_PARAM_OP_SPLIT)
+		{
+		  stream_write_tree (ob, adj->alias_ptr_type, true);
+		  streamer_write_uhwi (ob, adj->unit_offset);
+		}
+	    }
+	}
+      streamer_write_hwi (ob, adjustments->m_always_copy_start);
+      bp = bitpack_create (ob->main_stream);
+      bp_pack_value (&bp, node->clone.param_adjustments->m_skip_return, 1);
+      streamer_write_bitpack (&bp);
     }
-  else
-    streamer_write_uhwi (ob, 0);
+
   streamer_write_uhwi (ob, vec_safe_length (node->clone.tree_map));
   FOR_EACH_VEC_SAFE_ELT (node->clone.tree_map, i, map)
     {
-      /* At the moment we assume all old trees to be PARM_DECLs, because we have no
-         mechanism to store function local declarations into summaries.  */
-      gcc_assert (!map->old_tree);
       streamer_write_uhwi (ob, map->parm_num);
       gcc_assert (EXPR_LOCATION (map->new_tree) == UNKNOWN_LOCATION);
       stream_write_tree (ob, map->new_tree, true);
-      bp = bitpack_create (ob->main_stream);
-      bp_pack_value (&bp, map->replace_p, 1);
-      bp_pack_value (&bp, map->ref_p, 1);
-      streamer_write_bitpack (&bp);
     }
 
   if (lto_symtab_encoder_in_partition_p (encoder, node))
@@ -1922,26 +1932,49 @@ input_node_opt_summary (struct cgraph_node *node,
 {
   int i;
   int count;
-  int bit;
-  struct bitpack_d bp;
   struct cgraph_edge *e;
 
-  count = streamer_read_uhwi (ib_main);
-  if (count)
-    node->clone.args_to_skip = BITMAP_GGC_ALLOC ();
-  for (i = 0; i < count; i++)
-    {
-      bit = streamer_read_uhwi (ib_main);
-      bitmap_set_bit (node->clone.args_to_skip, bit);
-    }
-  count = streamer_read_uhwi (ib_main);
-  if (count)
-    node->clone.combined_args_to_skip = BITMAP_GGC_ALLOC ();
-  for (i = 0; i < count; i++)
+  /* TODO: Should this code be moved to ipa-param-manipulation?  */
+  struct bitpack_d bp;
+  bp = streamer_read_bitpack (ib_main);
+  bool have_adjustments = bp_unpack_value (&bp, 1);
+  if (have_adjustments)
     {
-      bit = streamer_read_uhwi (ib_main);
-      bitmap_set_bit (node->clone.combined_args_to_skip, bit);
+      count = streamer_read_uhwi (ib_main);
+      vec<ipa_adjusted_param, va_gc> *new_params = NULL;
+      for (i = 0; i < count; i++)
+	{
+	  ipa_adjusted_param adj;
+	  memset (&adj, 0, sizeof (adj));
+	  bp = streamer_read_bitpack (ib_main);
+	  adj.base_index = bp_unpack_value (&bp, IPA_PARAM_MAX_INDEX_BITS);
+	  adj.prev_clone_index
+	    = bp_unpack_value (&bp, IPA_PARAM_MAX_INDEX_BITS);
+	  adj.op = (enum ipa_parm_op) bp_unpack_value (&bp, 2);
+	  adj.param_prefix_index = bp_unpack_value (&bp, 2);
+	  adj.prev_clone_adjustment = bp_unpack_value (&bp, 1);
+	  adj.reverse = bp_unpack_value (&bp, 1);
+	  adj.user_flag = bp_unpack_value (&bp, 1);
+	  if (adj.op == IPA_PARAM_OP_SPLIT
+	      || adj.op == IPA_PARAM_OP_NEW)
+	    {
+	      adj.type = stream_read_tree (ib_main, data_in);
+	      if (adj.op == IPA_PARAM_OP_SPLIT)
+		{
+		  adj.alias_ptr_type = stream_read_tree (ib_main, data_in);
+		  adj.unit_offset = streamer_read_uhwi (ib_main);
+		}
+	    }
+	  vec_safe_push (new_params, adj);
+	}
+      int always_copy_start = streamer_read_hwi (ib_main);
+      bp = streamer_read_bitpack (ib_main);
+      bool skip_return = bp_unpack_value (&bp, 1);
+      node->clone.param_adjustments
+	= (new (ggc_alloc <ipa_param_adjustments> ())
+	   ipa_param_adjustments (new_params, always_copy_start, skip_return));
     }
+
   count = streamer_read_uhwi (ib_main);
   for (i = 0; i < count; i++)
     {
@@ -1949,11 +1982,7 @@ input_node_opt_summary (struct cgraph_node *node,
 
       vec_safe_push (node->clone.tree_map, map);
       map->parm_num = streamer_read_uhwi (ib_main);
-      map->old_tree = NULL;
       map->new_tree = stream_read_tree (ib_main, data_in);
-      bp = streamer_read_bitpack (ib_main);
-      map->replace_p = bp_unpack_value (&bp, 1);
-      map->ref_p = bp_unpack_value (&bp, 1);
     }
   for (e = node->callees; e; e = e->next_callee)
     input_edge_opt_summary (e, ib_main);
diff --git a/gcc/multiple_target.c b/gcc/multiple_target.c
index 7aea684a40a..968c8cfb1ab 100644
--- a/gcc/multiple_target.c
+++ b/gcc/multiple_target.c
@@ -311,9 +311,8 @@ create_target_clone (cgraph_node *node, bool definition, char *name,
   if (definition)
     {
       new_node = node->create_version_clone_with_body (vNULL, NULL,
-    						       NULL, false,
-						       NULL, NULL,
-						       name, attributes);
+    						       NULL, NULL,
+						       NULL, name, attributes);
       if (new_node == NULL)
 	return NULL;
       new_node->force_output = true;
diff --git a/gcc/omp-simd-clone.c b/gcc/omp-simd-clone.c
index a0780333941..d71a963443b 100644
--- a/gcc/omp-simd-clone.c
+++ b/gcc/omp-simd-clone.c
@@ -86,21 +86,23 @@ simd_clone_struct_copy (struct cgraph_simd_clone *to,
 			* sizeof (struct cgraph_simd_clone_arg))));
 }
 
-/* Return vector of parameter types of function FNDECL.  This uses
-   TYPE_ARG_TYPES if available, otherwise falls back to types of
+/* Fill an empty vector ARGS with parameter types of function FNDECL.  This
+   uses TYPE_ARG_TYPES if available, otherwise falls back to types of
    DECL_ARGUMENTS types.  */
 
-static vec<tree>
-simd_clone_vector_of_formal_parm_types (tree fndecl)
+static void
+simd_clone_vector_of_formal_parm_types (vec<tree> *args, tree fndecl)
 {
   if (TYPE_ARG_TYPES (TREE_TYPE (fndecl)))
-    return ipa_get_vector_of_formal_parm_types (TREE_TYPE (fndecl));
-  vec<tree> args = ipa_get_vector_of_formal_parms (fndecl);
+    {
+      push_function_arg_types (args, TREE_TYPE (fndecl));
+      return;
+    }
+  push_function_arg_decls (args, fndecl);
   unsigned int i;
   tree arg;
-  FOR_EACH_VEC_ELT (args, i, arg)
-    args[i] = TREE_TYPE (args[i]);
-  return args;
+  FOR_EACH_VEC_ELT (*args, i, arg)
+    (*args)[i] = TREE_TYPE ((*args)[i]);
 }
 
 /* Given a simd function in NODE, extract the simd specific
@@ -113,7 +115,8 @@ static struct cgraph_simd_clone *
 simd_clone_clauses_extract (struct cgraph_node *node, tree clauses,
 			    bool *inbranch_specified)
 {
-  vec<tree> args = simd_clone_vector_of_formal_parm_types (node->decl);
+  auto_vec<tree> args;
+  simd_clone_vector_of_formal_parm_types (&args, node->decl);
   tree t;
   int n;
   *inbranch_specified = false;
@@ -192,14 +195,12 @@ simd_clone_clauses_extract (struct cgraph_node *node, tree clauses,
 		  {
 		    warning_at (OMP_CLAUSE_LOCATION (t), 0,
 				"ignoring large linear step");
-		    args.release ();
 		    return NULL;
 		  }
 		else if (integer_zerop (step))
 		  {
 		    warning_at (OMP_CLAUSE_LOCATION (t), 0,
 				"ignoring zero linear step");
-		    args.release ();
 		    return NULL;
 		  }
 		else
@@ -263,7 +264,6 @@ simd_clone_clauses_extract (struct cgraph_node *node, tree clauses,
       warning_at (DECL_SOURCE_LOCATION (node->decl), 0,
 		  "ignoring %<#pragma omp declare simd%> on function "
 		  "with %<_Atomic%> qualified return type");
-      args.release ();
       return NULL;
     }
 
@@ -278,7 +278,6 @@ simd_clone_clauses_extract (struct cgraph_node *node, tree clauses,
 	return NULL;
       }
 
-  args.release ();
   return clone_info;
 }
 
@@ -303,14 +302,14 @@ simd_clone_compute_base_data_type (struct cgraph_node *node,
         such parameter.  */
   else
     {
-      vec<tree> map = simd_clone_vector_of_formal_parm_types (fndecl);
+      auto_vec<tree> map;
+      simd_clone_vector_of_formal_parm_types (&map, fndecl);
       for (unsigned int i = 0; i < clone_info->nargs; ++i)
 	if (clone_info->args[i].arg_type == SIMD_CLONE_ARG_TYPE_VECTOR)
 	  {
 	    type = map[i];
 	    break;
 	  }
-      map.release ();
     }
 
   /* c) If the characteristic data type determined by a) or b) above
@@ -441,7 +440,7 @@ simd_clone_create (struct cgraph_node *old_node)
 	return NULL;
       old_node->get_body ();
       new_node = old_node->create_version_clone_with_body (vNULL, NULL, NULL,
-							   false, NULL, NULL,
+							   NULL, NULL,
 							   "simdclone");
     }
   else
@@ -561,31 +560,33 @@ create_tmp_simd_array (const char *prefix, tree type, int simdlen)
 
    NODE is the function whose arguments are to be adjusted.
 
-   Returns an adjustment vector that will be filled describing how the
-   argument types will be adjusted.  */
+   If NODE does not represent function definition, returns NULL.  Otherwise
+   returns an adjustment class that will be filled describing how the argument
+   declarations will be remapped.  New arguments which are not to be remapped
+   are marked with USER_FLAG.  */
 
-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;
+  auto_vec<tree> args;
 
   if (node->definition)
-    args = ipa_get_vector_of_formal_parms (node->decl);
+    push_function_arg_decls (&args, node->decl);
   else
-    args = simd_clone_vector_of_formal_parm_types (node->decl);
-  adjustments.create (args.length ());
-  unsigned i, j, veclen;
-  struct ipa_parm_adjustment adj;
+    simd_clone_vector_of_formal_parm_types (&args, node->decl);
   struct cgraph_simd_clone *sc = node->simdclone;
+  vec<ipa_adjusted_param, va_gc> *new_params = NULL;
+  vec_safe_reserve (new_params, sc->nargs);
+  unsigned i, j, veclen;
 
   for (i = 0; i < sc->nargs; ++i)
     {
+      ipa_adjusted_param adj;
       memset (&adj, 0, sizeof (adj));
       tree parm = args[i];
       tree parm_type = node->definition ? TREE_TYPE (parm) : parm;
       adj.base_index = i;
-      adj.base = parm;
+      adj.prev_clone_index = i;
 
       sc->args[i].orig_arg = node->definition ? parm : NULL_TREE;
       sc->args[i].orig_type = parm_type;
@@ -594,7 +595,7 @@ simd_clone_adjust_argument_types (struct cgraph_node *node)
 	{
 	default:
 	  /* No adjustment necessary for scalar arguments.  */
-	  adj.op = IPA_PARM_OP_COPY;
+	  adj.op = IPA_PARAM_OP_COPY;
 	  break;
 	case SIMD_CLONE_ARG_TYPE_LINEAR_UVAL_CONSTANT_STEP:
 	case SIMD_CLONE_ARG_TYPE_LINEAR_UVAL_VARIABLE_STEP:
@@ -603,7 +604,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;
+	  adj.op = IPA_PARAM_OP_COPY;
 	  break;
 	case SIMD_CLONE_ARG_TYPE_LINEAR_VAL_CONSTANT_STEP:
 	case SIMD_CLONE_ARG_TYPE_LINEAR_VAL_VARIABLE_STEP:
@@ -615,7 +616,8 @@ 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";
+	  adj.op = IPA_PARAM_OP_NEW;
+	  adj.param_prefix_index = IPA_PARAM_PREFIX_SIMD;
 	  if (POINTER_TYPE_P (parm_type))
 	    adj.type = build_vector_type (pointer_sized_int_node, veclen);
 	  else
@@ -623,13 +625,15 @@ simd_clone_adjust_argument_types (struct cgraph_node *node)
 	  sc->args[i].vector_type = adj.type;
 	  for (j = veclen; j < sc->simdlen; j += veclen)
 	    {
-	      adjustments.safe_push (adj);
+	      vec_safe_push (new_params, adj);
 	      if (j == veclen)
 		{
 		  memset (&adj, 0, sizeof (adj));
-		  adj.op = IPA_PARM_OP_NEW;
-		  adj.arg_prefix = "simd";
+		  adj.op = IPA_PARAM_OP_NEW;
+		  adj.user_flag = 1;
+		  adj.param_prefix_index = IPA_PARAM_PREFIX_SIMD;
 		  adj.base_index = i;
+		  adj.prev_clone_index = i;
 		  adj.type = sc->args[i].vector_type;
 		}
 	    }
@@ -640,18 +644,20 @@ 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, adj);
     }
 
   if (sc->inbranch)
     {
       tree base_type = simd_clone_compute_base_data_type (sc->origin, sc);
-
+      ipa_adjusted_param adj;
       memset (&adj, 0, sizeof (adj));
-      adj.op = IPA_PARM_OP_NEW;
-      adj.arg_prefix = "mask";
+      adj.op = IPA_PARAM_OP_NEW;
+      adj.user_flag = 1;
+      adj.param_prefix_index = IPA_PARAM_PREFIX_MASK;
 
       adj.base_index = i;
+      adj.prev_clone_index = i;
       if (INTEGRAL_TYPE_P (base_type) || POINTER_TYPE_P (base_type))
 	veclen = sc->vecsize_int;
       else
@@ -666,10 +672,10 @@ simd_clone_adjust_argument_types (struct cgraph_node *node)
 	adj.type = build_vector_type (pointer_sized_int_node, veclen);
       else
 	adj.type = build_vector_type (base_type, veclen);
-      adjustments.safe_push (adj);
+      vec_safe_push (new_params, adj);
 
       for (j = veclen; j < sc->simdlen; j += veclen)
-	adjustments.safe_push (adj);
+	vec_safe_push (new_params, adj);
 
       /* We have previously allocated one extra entry for the mask.  Use
 	 it and fill it.  */
@@ -694,7 +700,13 @@ simd_clone_adjust_argument_types (struct cgraph_node *node)
     }
 
   if (node->definition)
-    ipa_modify_formal_parameters (node->decl, adjustments);
+    {
+      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;
@@ -703,12 +715,12 @@ 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 *adj = &(*new_params)[i];
 	  tree ptype;
-	  if (adj->op == IPA_PARM_OP_COPY)
+	  if (adj->op == IPA_PARAM_OP_COPY)
 	    ptype = args[adj->base_index];
 	  else
 	    ptype = adj->type;
@@ -723,10 +735,8 @@ simd_clone_adjust_argument_types (struct cgraph_node *node)
 	    new_reversed = void_list_node;
 	}
       TYPE_ARG_TYPES (TREE_TYPE (node->decl)) = new_reversed;
-      adjustments.release ();
+      return NULL;
     }
-  args.release ();
-  return adjustments;
 }
 
 /* Initialize and copy the function arguments in NODE to their
@@ -735,7 +745,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;
@@ -744,7 +754,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;
 
@@ -809,7 +819,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.  */
@@ -829,15 +839,26 @@ 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);
+
+  tree repl = NULL_TREE;
+  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);
+      if (pbr)
+	repl = pbr->repl;
+    }
   else if (TYPE_P (*tp))
     *walk_subtrees = 0;
 
-  tree repl = NULL_TREE;
-  if (cand)
-    repl = unshare_expr (cand->new_decl);
+  if (repl)
+    repl = unshare_expr (repl);
   else
     {
       if (tp != orig_tp)
@@ -861,13 +882,13 @@ ipa_simd_modify_stmt_ops (tree *tp, int *walk_subtrees, void *data)
   if (tp != orig_tp)
     {
       if (gimple_code (info->stmt) == GIMPLE_PHI
-	  && cand
+	  && pbr
 	  && TREE_CODE (*orig_tp) == ADDR_EXPR
 	  && TREE_CODE (TREE_OPERAND (*orig_tp, 0)) == PARM_DECL
-	  && cand->alias_ptr_type)
+	  && pbr->dummy)
 	{
-	  gcc_assert (TREE_CODE (cand->alias_ptr_type) == SSA_NAME);
-	  *orig_tp = cand->alias_ptr_type;
+	  gcc_assert (TREE_CODE (pbr->dummy) == SSA_NAME);
+	  *orig_tp = pbr->dummy;
 	  info->modified = true;
 	  return NULL_TREE;
 	}
@@ -893,10 +914,13 @@ ipa_simd_modify_stmt_ops (tree *tp, int *walk_subtrees, void *data)
 	{
 	  gsi = gsi_after_labels (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
 	  /* Cache SSA_NAME for next time.  */
-	  if (cand
+	  if (pbr
 	      && TREE_CODE (*orig_tp) == ADDR_EXPR
 	      && TREE_CODE (TREE_OPERAND (*orig_tp, 0)) == PARM_DECL)
-	    cand->alias_ptr_type = repl;
+	    {
+	      gcc_assert (!pbr->dummy);
+	      pbr->dummy = repl;
+	    }
 	}
       else
 	gsi = gsi_for_stmt (info->stmt);
@@ -926,70 +950,56 @@ 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
-	  && simd_clone_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 (simd_clone_subparts (vectype) < node->simdclone->simdlen)
 	j += node->simdclone->simdlen / simd_clone_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);
+	      tree repl = adjustments->lookup_replacement (old_decl, 0);
+	      gcc_checking_assert (repl);
+	      repl = unshare_expr (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);
 	}
     }
 
@@ -1162,8 +1172,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 ();
 
@@ -1175,7 +1186,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/trans-mem.c b/gcc/trans-mem.c
index c39d6d0235b..4a3f130e876 100644
--- a/gcc/trans-mem.c
+++ b/gcc/trans-mem.c
@@ -5009,8 +5009,7 @@ ipa_tm_create_version (struct cgraph_node *old_node)
 	}
 
       tree_function_versioning (old_decl, new_decl,
-				NULL, false, NULL,
-				false, NULL, NULL);
+				NULL,  NULL, false, NULL, NULL);
     }
 
   record_tm_clone_pair (old_decl, new_decl);
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 46bbec1acef..919201c0eb6 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -130,7 +130,6 @@ static void copy_bind_expr (tree *, int *, copy_body_data *);
 static void declare_inline_vars (tree, tree);
 static void remap_save_expr (tree *, hash_map<tree, tree> *, int *);
 static void prepend_lexical_block (tree current_block, tree new_block);
-static tree copy_decl_to_var (tree, copy_body_data *);
 static tree copy_result_decl_to_var (tree, copy_body_data *);
 static tree copy_decl_maybe_to_var (tree, copy_body_data *);
 static gimple_seq remap_gimple_stmt (gimple *, copy_body_data *);
@@ -192,7 +191,21 @@ remap_ssa_name (tree name, copy_body_data *id)
 
   n = id->decl_map->get (name);
   if (n)
-    return unshare_expr (*n);
+    {
+      /* WHen we perform edge redirection as part of CFG copy, IPA-SRA can
+	 remove an unused LHS from a call statement.  Such LHS can however
+	 still appear in debug statements, but their value is lost in this
+	 function and we do not want to map them.  */
+      if (id->killed_new_ssa_names
+	  && id->killed_new_ssa_names->contains (*n))
+	{
+	  gcc_assert (processing_debug_stmt);
+	  processing_debug_stmt = -1;
+	  return name;
+	}
+
+      return unshare_expr (*n);
+    }
 
   if (processing_debug_stmt)
     {
@@ -1902,6 +1915,21 @@ remap_gimple_stmt (gimple *stmt, copy_body_data *id)
       gcc_assert (n);
       gimple_set_block (copy, *n);
     }
+  if (id->param_body_adjs)
+    {
+      gimple_seq extra_stmts = NULL;
+      id->param_body_adjs->modify_gimple_stmt (&copy, &extra_stmts);
+      if (!gimple_seq_empty_p (extra_stmts))
+	{
+	  memset (&wi, 0, sizeof (wi));
+	  wi.info = id;
+	  for (gimple_stmt_iterator egsi = gsi_start (extra_stmts);
+	       !gsi_end_p (egsi);
+	       gsi_next (&egsi))
+	    walk_gimple_op (gsi_stmt (egsi), remap_gimple_op_r, &wi);
+	  gimple_seq_add_seq (&stmts, extra_stmts);
+	}
+    }
 
   if (id->reset_location)
     gimple_set_location (copy, input_location);
@@ -2865,10 +2893,24 @@ redirect_all_calls (copy_body_data * id, basic_block bb)
       gimple *stmt = gsi_stmt (si);
       if (is_gimple_call (stmt))
 	{
+	  tree old_lhs = gimple_call_lhs (stmt);
 	  struct cgraph_edge *edge = id->dst_node->get_edge (stmt);
 	  if (edge)
 	    {
-	      edge->redirect_call_stmt_to_callee ();
+	      gimple *new_stmt = edge->redirect_call_stmt_to_callee ();
+	      /* If IPA-SRA transformation, run as part of edge redirection,
+		 removed the LHS because it is unused, save it to
+		 killed_new_ssa_names so that we can prune it from debug
+		 statements.  */
+	      if (old_lhs
+		  && TREE_CODE (old_lhs) == SSA_NAME
+		  && !gimple_call_lhs (new_stmt))
+		{
+		  if (!id->killed_new_ssa_names)
+		    id->killed_new_ssa_names = new hash_set<tree> (16);
+		  id->killed_new_ssa_names->add (old_lhs);
+		}
+
 	      if (stmt == last && id->call_stmt && maybe_clean_eh_stmt (stmt))
 		gimple_purge_dead_eh_edges (bb);
 	    }
@@ -3189,6 +3231,8 @@ copy_body (copy_body_data *id,
   body = copy_cfg_body (id, entry_block_map, exit_block_map,
 			new_entry);
   copy_debug_stmts (id);
+  delete id->killed_new_ssa_names;
+  id->killed_new_ssa_names = NULL;
 
   return body;
 }
@@ -4907,6 +4951,38 @@ expand_call_inline (basic_block bb, gimple *stmt, copy_body_data *id)
   /* Add local vars in this inlined callee to caller.  */
   add_local_variables (id->src_cfun, cfun, id);
 
+  if (id->src_node->clone.performed_splits)
+    {
+      /* Any calls from the inlined function will be turned into calls from the
+	 function we inline into.  We must preserve notes about how to split
+	 parameters such calls should be redirected/updated.  */
+      unsigned len = vec_safe_length (id->src_node->clone.performed_splits);
+      for (unsigned i = 0; i < len; i++)
+	{
+	  ipa_param_performed_split ps
+	    = (*id->src_node->clone.performed_splits)[i];
+	  ps.dummy_decl = remap_decl (ps.dummy_decl, id);
+	  vec_safe_push (id->dst_node->clone.performed_splits, ps);
+	}
+
+      if (flag_checking)
+	{
+	  len = vec_safe_length (id->dst_node->clone.performed_splits);
+	  for (unsigned i = 0; i < len; i++)
+	    {
+	      ipa_param_performed_split *ps1
+		= &(*id->dst_node->clone.performed_splits)[i];
+	      for (unsigned j = i + 1; j < len; j++)
+		{
+		  ipa_param_performed_split *ps2
+		    = &(*id->dst_node->clone.performed_splits)[j];
+		  gcc_assert (ps1->dummy_decl != ps2->dummy_decl
+			      || ps1->unit_offset != ps2->unit_offset);
+		}
+	    }
+	}
+    }
+
   if (dump_enabled_p ())
     {
       char buf[128];
@@ -5732,7 +5808,11 @@ copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy)
   return copy;
 }
 
-static tree
+/* Create a new VAR_DECL that is indentical in all respect to DECL except that
+   DECL can be either a VAR_DECL, a PARM_DECL or RESULT_DECL.  The original
+   DECL must come from ID->src_fn and the copy will be part of ID->dst_fn.  */
+
+tree
 copy_decl_to_var (tree decl, copy_body_data *id)
 {
   tree copy, type;
@@ -5815,38 +5895,24 @@ copy_decl_maybe_to_var (tree decl, copy_body_data *id)
     return copy_decl_no_change (decl, id);
 }
 
-/* Return a copy of the function's argument tree.  */
+/* Return a copy of the function's argument tree without any modifications.  */
+
 static tree
-copy_arguments_for_versioning (tree orig_parm, copy_body_data * id,
-			       bitmap args_to_skip, tree *vars)
+copy_arguments_nochange (tree orig_parm, copy_body_data * id)
 {
   tree arg, *parg;
   tree new_parm = NULL;
-  int i = 0;
 
   parg = &new_parm;
-
-  for (arg = orig_parm; arg; arg = DECL_CHAIN (arg), i++)
-    if (!args_to_skip || !bitmap_bit_p (args_to_skip, i))
-      {
-        tree new_tree = remap_decl (arg, id);
-	if (TREE_CODE (new_tree) != PARM_DECL)
-	  new_tree = id->copy_decl (arg, id);
-        lang_hooks.dup_lang_specific_decl (new_tree);
-        *parg = new_tree;
-	parg = &DECL_CHAIN (new_tree);
-      }
-    else if (!id->decl_map->get (arg))
-      {
-	/* Make an equivalent VAR_DECL.  If the argument was used
-	   as temporary variable later in function, the uses will be
-	   replaced by local variable.  */
-	tree var = copy_decl_to_var (arg, id);
-	insert_decl_map (id, arg, var);
-        /* Declare this new variable.  */
-        DECL_CHAIN (var) = *vars;
-        *vars = var;
-      }
+  for (arg = orig_parm; arg; arg = DECL_CHAIN (arg))
+    {
+      tree new_tree = remap_decl (arg, id);
+      if (TREE_CODE (new_tree) != PARM_DECL)
+	new_tree = id->copy_decl (arg, id);
+      lang_hooks.dup_lang_specific_decl (new_tree);
+      *parg = new_tree;
+      parg = &DECL_CHAIN (new_tree);
+    }
   return new_parm;
 }
 
@@ -5882,6 +5948,18 @@ tree_versionable_function_p (tree fndecl)
 static void
 update_clone_info (copy_body_data * id)
 {
+  vec<ipa_param_performed_split, va_gc> *cur_performed_splits
+    = id->dst_node->clone.performed_splits;
+  if (cur_performed_splits)
+    {
+      unsigned len = cur_performed_splits->length ();
+      for (unsigned i = 0; i < len; i++)
+	{
+	  ipa_param_performed_split *ps = &(*cur_performed_splits)[i];
+	  ps->dummy_decl = remap_decl (ps->dummy_decl, id);
+	}
+    }
+
   struct cgraph_node *node;
   if (!id->dst_node->clones)
     return;
@@ -5895,10 +5973,55 @@ update_clone_info (copy_body_data * id)
 	    {
 	      struct ipa_replace_map *replace_info;
 	      replace_info = (*node->clone.tree_map)[i];
-	      walk_tree (&replace_info->old_tree, copy_tree_body_r, id, NULL);
 	      walk_tree (&replace_info->new_tree, copy_tree_body_r, id, NULL);
 	    }
 	}
+      if (node->clone.performed_splits)
+	{
+	  unsigned len = vec_safe_length (node->clone.performed_splits);
+	  for (unsigned i = 0; i < len; i++)
+	    {
+	      ipa_param_performed_split *ps
+		= &(*node->clone.performed_splits)[i];
+	      ps->dummy_decl = remap_decl (ps->dummy_decl, id);
+	    }
+	}
+      if (unsigned len = vec_safe_length (cur_performed_splits))
+	{
+	  /* We do not want to add current performed splits when we are saving
+	     a copy of function body for later during inlining, that would just
+	     duplicate all entries.  So let's have a look whether anything
+	     referring to the first dummy_decl is present.  */
+	  unsigned dst_len = vec_safe_length (node->clone.performed_splits);
+	  ipa_param_performed_split *first = &(*cur_performed_splits)[0];
+	  for (unsigned i = 0; i < dst_len; i++)
+	    if ((*node->clone.performed_splits)[i].dummy_decl
+		== first->dummy_decl)
+	      {
+		len = 0;
+		break;
+	      }
+
+	  for (unsigned i = 0; i < len; i++)
+	    vec_safe_push (node->clone.performed_splits,
+			   (*cur_performed_splits)[i]);
+	  if (flag_checking)
+	    {
+	      for (unsigned i = 0; i < dst_len; i++)
+		{
+		  ipa_param_performed_split *ps1
+		    = &(*node->clone.performed_splits)[i];
+		  for (unsigned j = i + 1; j < dst_len; j++)
+		    {
+		      ipa_param_performed_split *ps2
+			= &(*node->clone.performed_splits)[j];
+		      gcc_assert (ps1->dummy_decl != ps2->dummy_decl
+				  || ps1->unit_offset != ps2->unit_offset);
+		    }
+		}
+	    }
+	}
+
       if (node->clones)
 	node = node->clones;
       else if (node->next_sibling_clone)
@@ -5920,19 +6043,18 @@ update_clone_info (copy_body_data * id)
    tree with another tree while duplicating the function's
    body, TREE_MAP represents the mapping between these
    trees. If UPDATE_CLONES is set, the call_stmt fields
-   of edges of clones of the function will be updated.  
+   of edges of clones of the function will be updated.
 
-   If non-NULL ARGS_TO_SKIP determine function parameters to remove
-   from new version.
-   If SKIP_RETURN is true, the new version will return void.
-   If non-NULL BLOCK_TO_COPY determine what basic blocks to copy.
+   If non-NULL PARAM_ADJUSTMENTS determines how function prototype (i.e. the
+   function parameters and return value) should be modified).
+   If non-NULL BLOCKS_TO_COPY determine what basic blocks to copy.
    If non_NULL NEW_ENTRY determine new entry BB of the clone.
 */
 void
 tree_function_versioning (tree old_decl, tree new_decl,
 			  vec<ipa_replace_map *, va_gc> *tree_map,
-			  bool update_clones, bitmap args_to_skip,
-			  bool skip_return, bitmap blocks_to_copy,
+			  ipa_param_adjustments *param_adjustments,
+			  bool update_clones, bitmap blocks_to_copy,
 			  basic_block new_entry)
 {
   struct cgraph_node *old_version_node;
@@ -5944,7 +6066,6 @@ tree_function_versioning (tree old_decl, tree new_decl,
   basic_block old_entry_block, bb;
   auto_vec<gimple *, 10> init_stmts;
   tree vars = NULL_TREE;
-  bitmap debug_args_to_skip = args_to_skip;
 
   gcc_assert (TREE_CODE (old_decl) == FUNCTION_DECL
 	      && TREE_CODE (new_decl) == FUNCTION_DECL);
@@ -6020,96 +6141,78 @@ tree_function_versioning (tree old_decl, tree new_decl,
     DECL_STRUCT_FUNCTION (new_decl)->static_chain_decl
       = copy_static_chain (p, &id);
 
+  auto_vec<int, 16> new_param_indices;
+  ipa_param_adjustments *old_param_adjustments
+    = old_version_node->clone.param_adjustments;
+  if (old_param_adjustments)
+    old_param_adjustments->get_updated_indices (&new_param_indices);
+
   /* If there's a tree_map, prepare for substitution.  */
   if (tree_map)
     for (i = 0; i < tree_map->length (); i++)
       {
 	gimple *init;
 	replace_info = (*tree_map)[i];
-	if (replace_info->replace_p)
+
+	int p = replace_info->parm_num;
+	if (old_param_adjustments)
+	  p = new_param_indices[p];
+
+	tree parm;
+	tree req_type, new_type;
+
+	for (parm = DECL_ARGUMENTS (old_decl); p;
+	     parm = DECL_CHAIN (parm))
+	  p--;
+	tree old_tree = parm;
+	req_type = TREE_TYPE (parm);
+	new_type = TREE_TYPE (replace_info->new_tree);
+	if (!useless_type_conversion_p (req_type, new_type))
 	  {
-	    int parm_num = -1;
-	    if (!replace_info->old_tree)
-	      {
-		int p = replace_info->parm_num;
-		tree parm;
-		tree req_type, new_type;
-
-		for (parm = DECL_ARGUMENTS (old_decl); p;
-		     parm = DECL_CHAIN (parm))
-		  p--;
-		replace_info->old_tree = parm;
-		parm_num = replace_info->parm_num;
-		req_type = TREE_TYPE (parm);
-		new_type = TREE_TYPE (replace_info->new_tree);
-		if (!useless_type_conversion_p (req_type, new_type))
-		  {
-		    if (fold_convertible_p (req_type, replace_info->new_tree))
-		      replace_info->new_tree
-			= fold_build1 (NOP_EXPR, req_type,
-				       replace_info->new_tree);
-		    else if (TYPE_SIZE (req_type) == TYPE_SIZE (new_type))
-		      replace_info->new_tree
-			= fold_build1 (VIEW_CONVERT_EXPR, req_type,
-				       replace_info->new_tree);
-		    else
-		      {
-			if (dump_file)
-			  {
-			    fprintf (dump_file, "    const ");
-			    print_generic_expr (dump_file,
-						replace_info->new_tree);
-			    fprintf (dump_file,
-				     "  can't be converted to param ");
-			    print_generic_expr (dump_file, parm);
-			    fprintf (dump_file, "\n");
-			  }
-			replace_info->old_tree = NULL;
-		      }
-		  }
-	      }
+	    if (fold_convertible_p (req_type, replace_info->new_tree))
+	      replace_info->new_tree
+		= fold_build1 (NOP_EXPR, req_type, replace_info->new_tree);
+	    else if (TYPE_SIZE (req_type) == TYPE_SIZE (new_type))
+	      replace_info->new_tree
+		= fold_build1 (VIEW_CONVERT_EXPR, req_type,
+			       replace_info->new_tree);
 	    else
-	      gcc_assert (TREE_CODE (replace_info->old_tree) == PARM_DECL);
-	    if (replace_info->old_tree)
 	      {
-		init = setup_one_parameter (&id, replace_info->old_tree,
-					    replace_info->new_tree, id.src_fn,
-					    NULL,
-					    &vars);
-		if (init)
-		  init_stmts.safe_push (init);
-		if (MAY_HAVE_DEBUG_BIND_STMTS && args_to_skip)
+		if (dump_file)
 		  {
-		    if (parm_num == -1)
-		      {
-			tree parm;
-			int p;
-			for (parm = DECL_ARGUMENTS (old_decl), p = 0; parm;
-			     parm = DECL_CHAIN (parm), p++)
-			  if (parm == replace_info->old_tree)
-			    {
-			      parm_num = p;
-			      break;
-			    }
-		      }
-		    if (parm_num != -1)
-		      {
-			if (debug_args_to_skip == args_to_skip)
-			  {
-			    debug_args_to_skip = BITMAP_ALLOC (NULL);
-			    bitmap_copy (debug_args_to_skip, args_to_skip);
-			  }
-			bitmap_clear_bit (debug_args_to_skip, parm_num);
-		      }
+		    fprintf (dump_file, "    const ");
+		    print_generic_expr (dump_file,
+					replace_info->new_tree);
+		    fprintf (dump_file,
+			     "  can't be converted to param ");
+		    print_generic_expr (dump_file, parm);
+		    fprintf (dump_file, "\n");
 		  }
+		old_tree = NULL;
 	      }
 	  }
+
+	if (old_tree)
+	  {
+	    init = setup_one_parameter (&id, old_tree, replace_info->new_tree,
+					id.src_fn, NULL, &vars);
+	    if (init)
+	      init_stmts.safe_push (init);
+	  }
       }
-  /* Copy the function's arguments.  */
-  if (DECL_ARGUMENTS (old_decl) != NULL_TREE)
+
+  ipa_param_body_adjustments *param_body_adjs = NULL;
+  if (param_adjustments)
+    {
+      param_body_adjs = new ipa_param_body_adjustments (param_adjustments,
+							new_decl, old_decl,
+							&id, &vars, tree_map);
+      id.param_body_adjs = param_body_adjs;
+      DECL_ARGUMENTS (new_decl) = param_body_adjs->get_new_param_chain ();
+    }
+  else if (DECL_ARGUMENTS (old_decl) != NULL_TREE)
     DECL_ARGUMENTS (new_decl)
-      = copy_arguments_for_versioning (DECL_ARGUMENTS (old_decl), &id,
-				       args_to_skip, &vars);
+      = copy_arguments_nochange (DECL_ARGUMENTS (old_decl), &id);
 
   DECL_INITIAL (new_decl) = remap_blocks (DECL_INITIAL (id.src_fn), &id);
   BLOCK_SUPERCONTEXT (DECL_INITIAL (new_decl)) = new_decl;
@@ -6122,12 +6225,19 @@ tree_function_versioning (tree old_decl, tree new_decl,
 
   if (DECL_RESULT (old_decl) == NULL_TREE)
     ;
-  else if (skip_return && !VOID_TYPE_P (TREE_TYPE (DECL_RESULT (old_decl))))
+  else if (param_adjustments && param_adjustments->m_skip_return
+	   && !VOID_TYPE_P (TREE_TYPE (DECL_RESULT (old_decl))))
     {
+      tree resdecl_repl = copy_result_decl_to_var (DECL_RESULT (old_decl),
+						   &id);
+      declare_inline_vars (NULL, resdecl_repl);
+      insert_decl_map (&id, DECL_RESULT (old_decl), resdecl_repl);
+
       DECL_RESULT (new_decl)
 	= build_decl (DECL_SOURCE_LOCATION (DECL_RESULT (old_decl)),
 		      RESULT_DECL, NULL_TREE, void_type_node);
       DECL_CONTEXT (DECL_RESULT (new_decl)) = new_decl;
+      DECL_IS_MALLOC (new_decl) = false;
       cfun->returns_struct = 0;
       cfun->returns_pcc_struct = 0;
     }
@@ -6220,29 +6330,30 @@ tree_function_versioning (tree old_decl, tree new_decl,
 	}
     }
 
-  if (debug_args_to_skip && MAY_HAVE_DEBUG_BIND_STMTS)
+  if (param_body_adjs && MAY_HAVE_DEBUG_BIND_STMTS)
     {
-      tree parm;
       vec<tree, va_gc> **debug_args = NULL;
       unsigned int len = 0;
-      for (parm = DECL_ARGUMENTS (old_decl), i = 0;
-	   parm; parm = DECL_CHAIN (parm), i++)
-	if (bitmap_bit_p (debug_args_to_skip, i) && is_gimple_reg (parm))
-	  {
-	    tree ddecl;
+      unsigned reset_len = param_body_adjs->m_reset_debug_decls.length ();
 
-	    if (debug_args == NULL)
-	      {
-		debug_args = decl_debug_args_insert (new_decl);
-		len = vec_safe_length (*debug_args);
-	      }
-	    ddecl = make_node (DEBUG_EXPR_DECL);
-	    DECL_ARTIFICIAL (ddecl) = 1;
-	    TREE_TYPE (ddecl) = TREE_TYPE (parm);
-	    SET_DECL_MODE (ddecl, DECL_MODE (parm));
-	    vec_safe_push (*debug_args, DECL_ORIGIN (parm));
-	    vec_safe_push (*debug_args, ddecl);
-	  }
+      for (i = 0; i < reset_len; i++)
+	{
+	  tree parm = param_body_adjs->m_reset_debug_decls[i];
+	  gcc_assert (is_gimple_reg (parm));
+	  tree ddecl;
+
+	  if (debug_args == NULL)
+	    {
+	      debug_args = decl_debug_args_insert (new_decl);
+	      len = vec_safe_length (*debug_args);
+	    }
+	  ddecl = make_node (DEBUG_EXPR_DECL);
+	  DECL_ARTIFICIAL (ddecl) = 1;
+	  TREE_TYPE (ddecl) = TREE_TYPE (parm);
+	  SET_DECL_MODE (ddecl, DECL_MODE (parm));
+	  vec_safe_push (*debug_args, DECL_ORIGIN (parm));
+	  vec_safe_push (*debug_args, ddecl);
+	}
       if (debug_args != NULL)
 	{
 	  /* On the callee side, add
@@ -6268,7 +6379,7 @@ tree_function_versioning (tree old_decl, tree new_decl,
 	      if (var == NULL_TREE)
 		break;
 	      vexpr = make_node (DEBUG_EXPR_DECL);
-	      parm = (**debug_args)[i];
+	      tree parm = (**debug_args)[i];
 	      DECL_ARTIFICIAL (vexpr) = 1;
 	      TREE_TYPE (vexpr) = TREE_TYPE (parm);
 	      SET_DECL_MODE (vexpr, DECL_MODE (parm));
@@ -6280,9 +6391,7 @@ tree_function_versioning (tree old_decl, tree new_decl,
 	  while (i > len);
 	}
     }
-
-  if (debug_args_to_skip && debug_args_to_skip != args_to_skip)
-    BITMAP_FREE (debug_args_to_skip);
+  delete param_body_adjs;
   free_dominance_info (CDI_DOMINATORS);
   free_dominance_info (CDI_POST_DOMINATORS);
 
diff --git a/gcc/tree-inline.h b/gcc/tree-inline.h
index 3ede89ee697..87a149c357b 100644
--- a/gcc/tree-inline.h
+++ b/gcc/tree-inline.h
@@ -172,6 +172,15 @@ struct copy_body_data
      outside of the inlined function, this should be the number
      of basic blocks in the caller before inlining.  Zero otherwise.  */
   int add_clobbers_to_eh_landing_pads;
+
+  /* Class managing changes to function parameters and return value planned
+     during IPA stage.  */
+  class ipa_param_body_adjustments *param_body_adjs;
+
+  /* Hash set of SSA names that have been killed during call graph edge
+   redirection and should not be introduced into debug statements or NULL if no
+   SSA_NAME was deleted during redirections happened.  */
+  hash_set <tree> *killed_new_ssa_names;
 };
 
 /* Weights of constructions for estimate_num_insns.  */
@@ -240,6 +249,7 @@ extern bool debug_find_tree (tree, tree);
 extern tree copy_fn (tree, tree&, tree&);
 extern const char *copy_forbidden (struct function *fun);
 extern tree copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy);
+extern tree copy_decl_to_var (tree, copy_body_data *);
 
 /* This is in tree-inline.c since the routine uses
    data structures from the inliner.  */
