diff --git a/gcc/ipa-param-manipulation.cc b/gcc/ipa-param-manipulation.cc
index cee0e23f946..e92cfc0b6d5 100644
--- a/gcc/ipa-param-manipulation.cc
+++ b/gcc/ipa-param-manipulation.cc
@@ -46,7 +46,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-phinodes.h"
 #include "cfgexpand.h"
 #include "attribs.h"
-
+#include "ipa-prop.h"
 
 /* Actual prefixes of different newly synthetized parameters.  Keep in sync
    with IPA_PARAM_PREFIX_* defines.  */
@@ -449,39 +449,6 @@ ipa_param_adjustments::get_updated_indices (vec<int> *new_indices)
     }
 }
 
-/* If a parameter with original INDEX has survived intact, return its new
-   index.  Otherwise return -1.  In that case, if it has been split and there
-   is a new parameter representing a portion at unit OFFSET for which a value
-   of a TYPE can be substituted, store its new index into SPLIT_INDEX,
-   otherwise store -1 there.  */
-int
-ipa_param_adjustments::get_updated_index_or_split (int index,
-						   unsigned unit_offset,
-						   tree type, int *split_index)
-{
-  unsigned adj_len = vec_safe_length (m_adj_params);
-  for (unsigned i = 0; i < adj_len ; i++)
-    {
-      ipa_adjusted_param *apm = &(*m_adj_params)[i];
-      if (apm->base_index != index)
-	continue;
-      if (apm->op == IPA_PARAM_OP_COPY)
-	return i;
-      if (apm->op == IPA_PARAM_OP_SPLIT
-	  && apm->unit_offset == unit_offset)
-	{
-	  if (useless_type_conversion_p (apm->type, type))
-	    *split_index = i;
-	  else
-	    *split_index = -1;
-	  return -1;
-	}
-    }
-
-  *split_index = -1;
-  return -1;
-}
-
 /* Return the original index for the given new parameter index.  Return a
    negative number if not available.  */
 
@@ -1020,6 +987,21 @@ ipa_param_adjustments::debug ()
   dump (stderr);
 }
 
+/* Register a REPLACEMENT for accesses to BASE at UNIT_OFFSET.  */
+
+void
+ipa_param_body_adjustments::register_replacement (tree base,
+						  unsigned unit_offset,
+						  tree replacement)
+{
+  ipa_param_body_replacement psr;
+  psr.base = base;
+  psr.repl = replacement;
+  psr.dummy = NULL_TREE;
+  psr.unit_offset = unit_offset;
+  m_replacements.safe_push (psr);
+}
+
 /* Register that REPLACEMENT should replace parameter described in APM.  */
 
 void
@@ -1029,12 +1011,8 @@ ipa_param_body_adjustments::register_replacement (ipa_adjusted_param *apm,
   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 = NULL_TREE;
-  psr.unit_offset = apm->unit_offset;
-  m_replacements.safe_push (psr);
+  register_replacement (m_oparms[apm->prev_clone_index], apm->unit_offset,
+			replacement);
 }
 
 /* Copy or not, as appropriate given m_id and decl context, a pre-existing
@@ -1386,23 +1364,73 @@ ipa_param_body_adjustments::common_initialization (tree old_fndecl,
 	gcc_unreachable ();
     }
 
+  auto_vec <int, 16> index_mapping;
+  bool need_remap = false;
+  if (m_id)
+    {
+      clone_info *cinfo = clone_info::get (m_id->src_node);
+      if (cinfo && cinfo->param_adjustments)
+	{
+	  cinfo->param_adjustments->get_updated_indices (&index_mapping);
+	  need_remap = true;
+	}
+
+      if (ipcp_transformation *ipcp_ts
+	  = ipcp_get_transformation_summary (m_id->src_node))
+	{
+	  for (const ipa_argagg_value &av : ipcp_ts->m_agg_values)
+	    {
+	      int parm_num = av.index;
+
+	      if (need_remap)
+		{
+		  /* FIXME: We cannot handle the situation when IPA-CP
+		     identified that a parameter is a pointer to a global
+		     variable and at the same time the variable has some known
+		     constant contents (PR 107640).  The best place to make
+		     sure we don't drop such constants on the floor probably is
+		     not here, but we have to make sure that it does not
+		     confuse the remapping.  */
+		  if (parm_num >= (int) index_mapping.length ())
+		    continue;
+		  parm_num = index_mapping[parm_num];
+		  if (parm_num < 0)
+		    continue;
+		}
+
+	      if (!kept[parm_num])
+		{
+		  /* IPA-CP has detected an aggregate constant in a parameter
+		     that will not be kept, which means that IPA-SRA would have
+		     split it if there wasn't a constant.  Because we are about
+		     to remove the original, this is the last chance where we
+		     can substitute the uses with a constant (for values passed
+		     by reference) or do the split but initialize the
+		     replacement with a constant (for split aggregates passed
+		     by value).  */
+
+		  tree repl;
+		  if (av.by_ref)
+		    repl = av.value;
+		  else
+		    {
+		      repl = create_tmp_var (TREE_TYPE (av.value),
+					     "removed_ipa_cp");
+		      gimple *init_stmt = gimple_build_assign (repl, av.value);
+		      m_split_agg_csts_inits.safe_push (init_stmt);
+		    }
+		  register_replacement (m_oparms[parm_num], av.unit_offset,
+					repl);
+		  split[parm_num] = true;
+		}
+	    }
+	}
+    }
+
   if (tree_map)
     {
       /* Do not treat parameters which were replaced with a constant as
 	 completely vanished.  */
-      auto_vec <int, 16> index_mapping;
-      bool need_remap = false;
-
-      if (m_id)
-	{
-	  clone_info *cinfo = clone_info::get (m_id->src_node);
-	  if (cinfo && cinfo->param_adjustments)
-	    {
-	      cinfo->param_adjustments->get_updated_indices (&index_mapping);
-	      need_remap = true;
-	    }
-	}
-
       for (unsigned i = 0; i < tree_map->length (); i++)
 	{
 	  int parm_num = (*tree_map)[i]->parm_num;
@@ -1473,8 +1501,9 @@ ipa_param_body_adjustments
   : m_adj_params (adj_params), m_adjustments (NULL), m_reset_debug_decls (),
     m_dead_stmts (), m_dead_ssas (), m_dead_ssa_debug_equiv (),
     m_dead_stmt_debug_equiv (), 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)
+    m_new_decls (), m_new_types (), m_replacements (),
+    m_split_agg_csts_inits (), m_removed_decls (), m_removed_map (),
+    m_method2func (false)
 {
   common_initialization (fndecl, NULL, NULL);
 }
@@ -1491,7 +1520,8 @@ ipa_param_body_adjustments
     m_reset_debug_decls (), m_dead_stmts (), m_dead_ssas (),
     m_dead_ssa_debug_equiv (), m_dead_stmt_debug_equiv (), 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)
+    m_split_agg_csts_inits (), m_removed_decls (), m_removed_map (),
+    m_method2func (false)
 {
   common_initialization (fndecl, NULL, NULL);
 }
@@ -1514,7 +1544,8 @@ ipa_param_body_adjustments
     m_reset_debug_decls (), m_dead_stmts (), m_dead_ssas (),
     m_dead_ssa_debug_equiv (), m_dead_stmt_debug_equiv (), 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)
+    m_split_agg_csts_inits (), m_removed_decls (), m_removed_map (),
+    m_method2func (false)
 {
   common_initialization (old_fndecl, vars, tree_map);
 }
@@ -2383,6 +2414,16 @@ ipa_param_body_adjustments::perform_cfun_body_modifications ()
 }
 
 
+/* If there are any initialization statements that need to be emitted into
+   the basic block BB right at ther start of the new function, do so.  */
+void
+ipa_param_body_adjustments::append_init_stmts (basic_block bb)
+{
+  gimple_stmt_iterator si = gsi_last_bb (bb);
+  while (!m_split_agg_csts_inits.is_empty ())
+    gsi_insert_after (&si, m_split_agg_csts_inits.pop (), GSI_NEW_STMT);
+}
+
 /* Deallocate summaries which otherwise stay alive until the end of
    compilation.  */
 
diff --git a/gcc/ipa-param-manipulation.h b/gcc/ipa-param-manipulation.h
index e5654f4ff70..e20d34918b3 100644
--- a/gcc/ipa-param-manipulation.h
+++ b/gcc/ipa-param-manipulation.h
@@ -236,13 +236,6 @@ public:
   void get_surviving_params (vec<bool> *surviving_params);
   /* Fill a vector with new indices of surviving original parameters.  */
   void get_updated_indices (vec<int> *new_indices);
-  /* If a parameter with original INDEX has survived intact, return its new
-     index.  Otherwise return -1.  In that case, if it has been split and there
-     is a new parameter representing a portion at UNIT_OFFSET for which a value
-     of a TYPE can be substituted, store its new index into SPLIT_INDEX,
-     otherwise store -1 there.  */
-  int get_updated_index_or_split (int index, unsigned unit_offset, tree type,
-				  int *split_index);
   /* Return the original index for the given new parameter index.  Return a
      negative number if not available.  */
   int get_original_index (int newidx);
@@ -321,6 +314,8 @@ public:
 
   /* Change the PARM_DECLs.  */
   void modify_formal_parameters ();
+  /* Register a REPLACEMENT for accesses to BASE at UNIT_OFFSET.  */
+  void register_replacement (tree base, unsigned unit_offset, tree replacement);
   /* Register a replacement decl for the transformation done in APM.  */
   void register_replacement (ipa_adjusted_param *apm, tree replacement);
   /* Lookup a replacement for a given offset within a given parameter.  */
@@ -340,6 +335,10 @@ public:
      they are mapped to.  */
   void remap_with_debug_expressions (tree *t);
 
+  /* If there are any initialization statements that need to be emitted into
+     the basic block BB right at ther start of the new function, do so.  */
+  void append_init_stmts (basic_block bb);
+
   /* Pointers to data structures defining how the function should be
      modified.  */
   vec<ipa_adjusted_param, va_gc> *m_adj_params;
@@ -405,6 +404,12 @@ private:
 
   auto_vec<ipa_param_body_replacement, 16> m_replacements;
 
+  /* List of initialization assignments to be put at the beginning of the
+     cloned function to deal with split aggregates which however have known
+     constant value and so their PARM_DECL disappears.  */
+
+  auto_vec<gimple *, 8> m_split_agg_csts_inits;
+
   /* 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
diff --git a/gcc/ipa-prop.cc b/gcc/ipa-prop.cc
index cfd12a97b36..1cc9cf3ad22 100644
--- a/gcc/ipa-prop.cc
+++ b/gcc/ipa-prop.cc
@@ -5516,29 +5516,21 @@ ipcp_read_transformation_summaries (void)
     }
 }
 
-/* Adjust the aggregate replacements in TS to reflect parameters skipped in
-   NODE but also if any parameter was IPA-SRAed into a scalar go ahead with
-   substitution of the default_definitions of that new param with the
-   appropriate constant.
+/* Adjust the aggregate replacements in TS to reflect any parameter removals
+   which might have already taken place.  If after adjustments there are no
+   aggregate replacements left, the m_agg_values will be set to NULL.  In other
+   cases, it may be shrunk.  */
 
-   If after adjustments there are no aggregate replacements left, the
-   m_agg_values will be set to NULL.  In other cases, it may be shrunk.
-
-   Return true if any values were already substituted for scalarized parameters
-   and update_cfg shuld be run after replace_uses_by.  */
-
-static bool
-adjust_agg_replacement_values (cgraph_node *node,
-			       ipcp_transformation *ts,
-			       const vec<ipa_param_descriptor, va_gc>
-			         &descriptors)
+static void
+adjust_agg_replacement_values (cgraph_node *node, ipcp_transformation *ts)
 {
   clone_info *cinfo = clone_info::get (node);
   if (!cinfo || !cinfo->param_adjustments)
-    return false;
+    return;
 
+  auto_vec<int, 16> new_indices;
+  cinfo->param_adjustments->get_updated_indices (&new_indices);
   bool removed_item = false;
-  bool done_replacement = false;
   unsigned dst_index = 0;
   unsigned count = ts->m_agg_values->length ();
   for (unsigned i = 0; i < count; i++)
@@ -5546,13 +5538,10 @@ adjust_agg_replacement_values (cgraph_node *node,
       ipa_argagg_value *v = &(*ts->m_agg_values)[i];
       gcc_checking_assert (v->index >= 0);
 
-      tree cst_type = TREE_TYPE (v->value);
-      int split_idx;
-      int new_idx
-	= cinfo->param_adjustments->get_updated_index_or_split (v->index,
-								v->unit_offset,
-								cst_type,
-								&split_idx);
+      int new_idx = -1;
+      if ((unsigned) v->index < new_indices.length ())
+	new_idx = new_indices[v->index];
+
       if (new_idx >= 0)
 	{
 	  v->index = new_idx;
@@ -5561,19 +5550,7 @@ adjust_agg_replacement_values (cgraph_node *node,
 	  dst_index++;
 	}
       else
-	{
-	  removed_item = true;
-	  if (split_idx >= 0)
-	    {
-	      tree parm = ipa_get_param (descriptors, split_idx);
-	      tree ddef = ssa_default_def (cfun, parm);
-	      if (ddef)
-		{
-		  replace_uses_by (ddef, v->value);
-		  done_replacement = true;
-		}
-	    }
-	}
+	removed_item = true;
     }
 
   if (dst_index == 0)
@@ -5584,7 +5561,7 @@ adjust_agg_replacement_values (cgraph_node *node,
   else if (removed_item)
     ts->m_agg_values->truncate (dst_index);
 
-  return done_replacement;
+  return;
 }
 
 /* Dominator walker driving the ipcp modification phase.  */
@@ -5953,7 +5930,6 @@ ipcp_update_vr (struct cgraph_node *node)
 unsigned int
 ipcp_transform_function (struct cgraph_node *node)
 {
-  vec<ipa_param_descriptor, va_gc> *descriptors = NULL;
   struct ipa_func_body_info fbi;
   int param_count;
 
@@ -5972,18 +5948,13 @@ ipcp_transform_function (struct cgraph_node *node)
   param_count = count_formal_params (node->decl);
   if (param_count == 0)
     return 0;
-  vec_safe_grow_cleared (descriptors, param_count, true);
-  ipa_populate_param_decls (node, *descriptors);
 
-  bool cfg_changed = adjust_agg_replacement_values (node, ts, *descriptors);
+  adjust_agg_replacement_values (node, ts);
   if (vec_safe_is_empty (ts->m_agg_values))
     {
-      vec_free (descriptors);
       if (dump_file)
 	fprintf (dump_file, "  All affected aggregate parameters were either "
 		 "removed or converted into scalars, phase done.\n");
-      if (cfg_changed)
-	delete_unreachable_blocks_update_callgraph (node, false);
       return 0;
     }
   if (dump_file)
@@ -6000,12 +5971,15 @@ ipcp_transform_function (struct cgraph_node *node)
   fbi.param_count = param_count;
   fbi.aa_walk_budget = opt_for_fn (node->decl, param_ipa_max_aa_steps);
 
+  vec<ipa_param_descriptor, va_gc> *descriptors = NULL;
+  vec_safe_grow_cleared (descriptors, param_count, true);
+  ipa_populate_param_decls (node, *descriptors);
   bool modified_mem_access = false;
   calculate_dominance_info (CDI_DOMINATORS);
   ipcp_modif_dom_walker walker (&fbi, descriptors, ts, &modified_mem_access);
   walker.walk (ENTRY_BLOCK_PTR_FOR_FN (cfun));
   free_dominance_info (CDI_DOMINATORS);
-  cfg_changed |= walker.cleanup_eh ();
+  bool cfg_changed = walker.cleanup_eh ();
 
   int i;
   struct ipa_bb_info *bi;
diff --git a/gcc/ipa-sra.cc b/gcc/ipa-sra.cc
index 2237ac6d92f..959e8829777 100644
--- a/gcc/ipa-sra.cc
+++ b/gcc/ipa-sra.cc
@@ -84,6 +84,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "internal-fn.h"
 #include "symtab-clones.h"
 #include "attribs.h"
+#include "ipa-prop.h"
 
 static void ipa_sra_summarize_function (cgraph_node *);
 
@@ -3608,13 +3609,16 @@ retval_used_p (cgraph_node *node, void *)
 /* Push into NEW_PARAMS all required parameter adjustment entries to copy or
    modify parameter which originally had index BASE_INDEX, in the adjustment
    vector of parent clone (if any) had PREV_CLONE_INDEX and was described by
-   PREV_ADJUSTMENT.  If the parent clone is the original function,
-   PREV_ADJUSTMENT is NULL and PREV_CLONE_INDEX is equal to BASE_INDEX.  */
+   PREV_ADJUSTMENT.  If IPA-CP has created a transformation summary for the
+   original node, it needs to be passed in IPCP_TS, otherwise it should be
+   NULL.  If the parent clone is the original function, PREV_ADJUSTMENT is NULL
+   and PREV_CLONE_INDEX is equal to BASE_INDEX.  */
 
 static void
 push_param_adjustments_for_index (isra_func_summary *ifs, unsigned base_index,
 				  unsigned prev_clone_index,
 				  ipa_adjusted_param *prev_adjustment,
+				  ipcp_transformation *ipcp_ts,
 				  vec<ipa_adjusted_param, va_gc> **new_params)
 {
   isra_param_desc *desc = &(*ifs->m_parameters)[base_index];
@@ -3655,6 +3659,23 @@ push_param_adjustments_for_index (isra_func_summary *ifs, unsigned base_index,
       param_access *pa = (*desc->accesses)[j];
       if (!pa->certain)
 	continue;
+
+      if (ipcp_ts)
+	{
+	  ipa_argagg_value_list avl (ipcp_ts);
+	  tree value = avl.get_value (base_index, pa->unit_offset);
+	  if (value
+	      && (tree_to_uhwi (TYPE_SIZE (TREE_TYPE (value))) / BITS_PER_UNIT
+		  == pa->unit_size))
+	    {
+	      if (dump_file)
+		fprintf (dump_file, "    - omitting component at byte "
+			 "offset %u which is known to have a constant value\n ",
+			 pa->unit_offset);
+	      continue;
+	    }
+	}
+
       if (dump_file)
 	fprintf (dump_file, "    - component at byte offset %u, "
 		 "size %u\n", pa->unit_offset, pa->unit_size);
@@ -3735,6 +3756,7 @@ process_isra_node_results (cgraph_node *node,
 	fprintf (dump_file, "  Will remove return value.\n");
     }
 
+  ipcp_transformation *ipcp_ts = ipcp_get_transformation_summary (node);
   vec<ipa_adjusted_param, va_gc> *new_params = NULL;
   if (ipa_param_adjustments *old_adjustments
 	 = cinfo ? cinfo->param_adjustments : NULL)
@@ -3744,12 +3766,12 @@ process_isra_node_results (cgraph_node *node,
 	{
 	  ipa_adjusted_param *old_adj = &(*old_adjustments->m_adj_params)[i];
 	  push_param_adjustments_for_index (ifs, old_adj->base_index, i,
-					    old_adj, &new_params);
+					    old_adj, ipcp_ts, &new_params);
 	}
     }
   else
     for (unsigned i = 0; i < param_count; i++)
-      push_param_adjustments_for_index (ifs, i, i, NULL, &new_params);
+      push_param_adjustments_for_index (ifs, i, i, NULL, ipcp_ts, &new_params);
 
   ipa_param_adjustments *new_adjustments
     = (new (ggc_alloc <ipa_param_adjustments> ())
diff --git a/gcc/testsuite/gcc.dg/ipa/pr107640-2.c b/gcc/testsuite/gcc.dg/ipa/pr107640-2.c
new file mode 100644
index 00000000000..94cbe02860d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/pr107640-2.c
@@ -0,0 +1,50 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized-slim"  } */
+
+struct S
+{
+  int a, b, c;
+};
+
+int ellide (int c);
+volatile short gi;
+
+void __attribute__((noipa))
+consume_s (struct S *p)
+{
+  gi = p->a;
+}
+
+static void __attribute__((noinline))
+foo (struct S *p, short *r)
+{
+  gi = *r;
+  if (!__builtin_constant_p (p->b))
+    ellide (1);
+  consume_s (p);
+}
+
+static void __attribute__((noinline))
+bar (short *r, struct S *p)
+{
+  gi = *r;
+  if (!__builtin_constant_p (p->c))
+    ellide (2);
+  consume_s (p);
+}
+
+struct S gs;
+
+int main (int argc, char *argv[])
+{
+  short i = 42;
+  gs.a = 10;
+  gs.b = 20;
+  foo (&gs, &i);
+  gs.b = 30;
+  gs.c = 40;
+  bar (&i, &gs);
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-not "ellide" "optimized" { xfail *-*-* } } } */
diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc
index 8091ba8f13b..15a1a389493 100644
--- a/gcc/tree-inline.cc
+++ b/gcc/tree-inline.cc
@@ -6377,6 +6377,8 @@ tree_function_versioning (tree old_decl, tree new_decl,
   bb = split_edge (single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
   while (init_stmts.length ())
     insert_init_stmt (&id, bb, init_stmts.pop ());
+  if (param_body_adjs)
+    param_body_adjs->append_init_stmts (bb);
   update_clone_info (&id);
 
   /* Remap the nonlocal_goto_save_area, if any.  */
