diff --git a/gcc/cgraph.cc b/gcc/cgraph.cc
index d565c005f622fd2da7bdddeeccbe2a7950aaae83..0ac8f73204b2418ffab9d2c3d25d0976a5e53037 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -1403,11 +1403,17 @@ cgraph_edge::redirect_callee (cgraph_node *n)
    speculative indirect call, remove "speculative" of the indirect call and
    also redirect stmt to it's final direct target.
 
+   When called from within tree-inline, KILLED_SSAs has to contain the pointer
+   to killed_new_ssa_names within the copy_body_data structure and SSAs
+   discovered to be useless (if LHS is removed) will be added to it, otherwise
+   it needs to be NULL.
+
    It is up to caller to iteratively transform each "speculative"
    direct call as appropriate.  */
 
 gimple *
-cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge *e)
+cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge *e,
+					   hash_set <tree> *killed_ssas)
 {
   tree decl = gimple_call_fndecl (e->call_stmt);
   gcall *new_stmt;
@@ -1527,7 +1533,7 @@ cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge *e)
 	remove_stmt_from_eh_lp (e->call_stmt);
 
       tree old_fntype = gimple_call_fntype (e->call_stmt);
-      new_stmt = padjs->modify_call (e, false);
+      new_stmt = padjs->modify_call (e, false, killed_ssas);
       cgraph_node *origin = e->callee;
       while (origin->clone_of)
 	origin = origin->clone_of;
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index b4f028b3f3034056de1050ea1ab93a682197d0e1..47f35e8078df288ef01d25a72583f1988c09ef3a 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -1837,9 +1837,16 @@ public:
      speculative indirect call, remove "speculative" of the indirect call and
      also redirect stmt to it's final direct target.
 
+     When called from within tree-inline, KILLED_SSAs has to contain the
+     pointer to killed_new_ssa_names within the copy_body_data structure and
+     SSAs discovered to be useless (if LHS is removed) will be added to it,
+     otherwise it needs to be NULL.
+
      It is up to caller to iteratively transform each "speculative"
      direct call as appropriate.  */
-  static gimple *redirect_call_stmt_to_callee (cgraph_edge *e);
+  static gimple *redirect_call_stmt_to_callee (cgraph_edge *e,
+					       hash_set <tree>
+					       *killed_ssas = nullptr);
 
   /* Create clone of edge in the node N represented
      by CALL_EXPR the callgraph.  */
diff --git a/gcc/ipa-param-manipulation.cc b/gcc/ipa-param-manipulation.cc
index 8772476ca40fa9e4d176fb8c367b5a94fbd388be..02f71a42237e1d1ad79be05c6d8d4da743ad1d39 100644
--- a/gcc/ipa-param-manipulation.cc
+++ b/gcc/ipa-param-manipulation.cc
@@ -593,14 +593,65 @@ isra_get_ref_base_and_offset (tree expr, tree *base_p, unsigned *unit_offset_p)
   return true;
 }
 
+/* Remove all statements that use NAME directly or indirectly.  KILLED_SSAS
+   contains the SSA_NAMEs that are already being or have been processed and new
+   ones need to be added to it.  The function only has to process situations
+   handled by ssa_name_only_returned_p in ipa-sra.cc with the exception that it
+   can assume it must never reach a use in a return statement.  */
+
+static void
+purge_all_uses (tree name, hash_set <tree> *killed_ssas)
+{
+  imm_use_iterator imm_iter;
+  gimple *stmt;
+  auto_vec <tree, 4> worklist;
+
+  worklist.safe_push (name);
+  while (!worklist.is_empty ())
+    {
+      tree cur_name = worklist.pop ();
+      FOR_EACH_IMM_USE_STMT (stmt, imm_iter, cur_name)
+	{
+	  if (gimple_debug_bind_p (stmt))
+	    {
+	      /* When runing within tree-inline, we will never end up here but
+		 adding the SSAs to killed_ssas will do the trick in this case
+		 and the respective debug statements will get reset. */
+	      gimple_debug_bind_reset_value (stmt);
+	      update_stmt (stmt);
+	      continue;
+	    }
+
+	  tree lhs = NULL_TREE;
+	  if (is_gimple_assign (stmt))
+	    lhs = gimple_assign_lhs (stmt);
+	  else if (gimple_code (stmt) == GIMPLE_PHI)
+	    lhs = gimple_phi_result (stmt);
+	  gcc_assert (lhs
+		      && (TREE_CODE (lhs) == SSA_NAME)
+		      && !gimple_vdef (stmt));
+	  if (!killed_ssas->add (lhs))
+	    {
+	      worklist.safe_push (lhs);
+	      gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
+	      gsi_remove (&gsi, true);
+	    }
+	}
+    }
+}
+
 /* Modify actual arguments of a function call in statement currently belonging
    to CS, and make it call CS->callee->decl.  Return the new statement that
    replaced the old one.  When invoked, cfun and current_function_decl have to
-   be set to the caller.  */
+   be set to the caller.  When called from within tree-inline, KILLED_SSAs has
+   to contain the pointer to killed_new_ssa_names within the copy_body_data
+   structure and SSAs discovered to be useless (if LHS is removed) will be
+   added to it, otherwise it needs to be NULL.  */
 
 gcall *
 ipa_param_adjustments::modify_call (cgraph_edge *cs,
-				    bool update_references)
+				    bool update_references,
+				    hash_set <tree> *killed_ssas)
 {
   gcall *stmt = cs->call_stmt;
   tree callee_decl = cs->callee->decl;
@@ -910,32 +961,20 @@ ipa_param_adjustments::modify_call (cgraph_edge *cs,
 
   gcall *new_stmt = gimple_build_call_vec (callee_decl, vargs);
 
-  tree ssa_to_remove = NULL;
+  hash_set <tree> *ssas_to_remove = NULL;
   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)
+	  if (!killed_ssas)
 	    {
-	      FOR_EACH_IMM_USE_ON_STMT (use_p, ui)
-		{
-		  SET_USE (use_p, repl);
-		}
-	      update_stmt (using_stmt);
+	      ssas_to_remove = new hash_set<tree> (8);
+	      killed_ssas = ssas_to_remove;
 	    }
-	  ssa_to_remove = lhs;
+	  killed_ssas->add (lhs);
+	  purge_all_uses (lhs, killed_ssas);
 	}
     }
 
@@ -954,8 +993,11 @@ ipa_param_adjustments::modify_call (cgraph_edge *cs,
       fprintf (dump_file, "\n");
     }
   gsi_replace (&gsi, new_stmt, true);
-  if (ssa_to_remove)
-    release_ssa_name (ssa_to_remove);
+  if (ssas_to_remove)
+    {
+      ipa_release_ssas_in_hash (ssas_to_remove);
+      delete ssas_to_remove;
+    }
   if (update_references)
     do
       {
@@ -2552,4 +2594,30 @@ ipa_edge_modifications_finalize ()
   ipa_edge_modifications = NULL;
 }
 
+/* Helper used to sort a vector of SSA_NAMES. */
 
+static int
+compare_ssa_versions (const void *va, const void *vb)
+{
+  const_tree const a = *(const_tree const*)va;
+  const_tree const b = *(const_tree const*)vb;
+
+  if (SSA_NAME_VERSION (a) < SSA_NAME_VERSION (b))
+    return -1;
+  if (SSA_NAME_VERSION (a) > SSA_NAME_VERSION (b))
+    return 1;
+  return 0;
+}
+
+/* Call release_ssa_name on all elements in KILLED_SSAS in a defined order.  */
+
+void
+ipa_release_ssas_in_hash (hash_set <tree> *killed_ssas)
+{
+  auto_vec<tree, 16> ssas_to_release;
+  for (tree sn : *killed_ssas)
+    ssas_to_release.safe_push (sn);
+  ssas_to_release.qsort (compare_ssa_versions);
+  for (tree sn : ssas_to_release)
+    release_ssa_name (sn);
+}
diff --git a/gcc/ipa-param-manipulation.h b/gcc/ipa-param-manipulation.h
index ddfed454c26f61a2a85b97a4908c65518262bd65..8dd5e5bdeaee660cdb0283c4d032d87ae7116141 100644
--- a/gcc/ipa-param-manipulation.h
+++ b/gcc/ipa-param-manipulation.h
@@ -224,7 +224,8 @@ public:
 
   /* Modify a call statement arguments (and possibly remove the return value)
      as described in the data fields of this class.  */
-  gcall *modify_call (cgraph_edge *cs, bool update_references);
+  gcall *modify_call (cgraph_edge *cs, bool update_references,
+		      hash_set <tree> *killed_ssas);
   /* Return if the first parameter is left intact.  */
   bool first_param_intact_p ();
   /* Build a function type corresponding to the modified call.  */
@@ -442,6 +443,6 @@ void push_function_arg_decls (vec<tree> *args, tree fndecl);
 void push_function_arg_types (vec<tree> *types, tree fntype);
 void ipa_verify_edge_has_no_modifications (cgraph_edge *cs);
 void ipa_edge_modifications_finalize ();
-
+void ipa_release_ssas_in_hash (hash_set <tree> *killed_ssas);
 
 #endif	/* IPA_PARAM_MANIPULATION_H */
diff --git a/gcc/testsuite/gcc.dg/ipa/pr108007.c b/gcc/testsuite/gcc.dg/ipa/pr108007.c
new file mode 100644
index 0000000000000000000000000000000000000000..77fc95975cfd4080b71052fd5d1c92373f0171d5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/pr108007.c
@@ -0,0 +1,32 @@
+/* { dg-do run } */
+/* { dg-options "-Os -fno-dce -fno-tree-dce -g" } */
+
+/* This tests that when IPA-SRA removes a LHS of a call statement which, in the
+   original source, is fed into a useless operation which however can trap when
+   given nonsensical input, that we remove it even when the user has turned off
+   normal DCE.  */
+
+int a, b, d, e, f = 10000000, h;
+short c, g;
+static int *i() {
+  g = f;
+ L:
+  h = e = ~g;
+  g = ~f % g & e;
+  if (!g)
+    goto L;
+  c++;
+  while (g < 1)
+    ;
+  return &a;
+}
+static void k() {
+  int *l, m = 2;
+  l = i();
+  for (; d < 1; d++)
+    m |= *l >= b;
+}
+int main() {
+  k();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/ipa/pr112616.c b/gcc/testsuite/gcc.dg/ipa/pr112616.c
new file mode 100644
index 0000000000000000000000000000000000000000..5f730da71e711f43e9a60b788905dafdab7741cd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/ipa/pr112616.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+unsigned a;
+int b, d, e, f = 2, g, h = 1, *i = &b;
+volatile int c = 1;
+static int *o() {
+  long m = ~a;
+  int j = f / b, k = f - 1, n = m << -1 / ~g / k;
+  if (j && n)
+    c;
+  return &e;
+}
+static long p() {
+  int *q = 0, **r = &q;
+  if (c) {
+    *i = h;
+    *r = o();
+  }
+  return *q;
+}
+int main() {
+  p();
+  int *l = 0;
+  if (d)
+    c = *l;
+  return 0;
+}
diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc
index 17628a34c70226b9ae9a79d2a87ab8c0bcb91ab8..1a2541ad95238ef1b0cee94edbbcc38c92ad6d8b 100644
--- a/gcc/tree-inline.cc
+++ b/gcc/tree-inline.cc
@@ -2988,20 +2988,19 @@ redirect_all_calls (copy_body_data * id, basic_block bb)
 	  struct cgraph_edge *edge = id->dst_node->get_edge (stmt);
 	  if (edge)
 	    {
+	      if (!id->killed_new_ssa_names)
+		id->killed_new_ssa_names = new hash_set<tree> (16);
 	      gimple *new_stmt
-		= cgraph_edge::redirect_call_stmt_to_callee (edge);
-	      /* 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.  */
+		= cgraph_edge::redirect_call_stmt_to_callee (edge,
+		    id->killed_new_ssa_names);
 	      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);
-		}
+		/* In case of IPA-SRA removing the LHS, the name should have
+		   been already added to the hash.  But in case of redirecting
+		   to builtin_unreachable it was not and the name still should
+		   be pruned from debug statements.  */
+		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);
@@ -3328,8 +3327,12 @@ 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;
+  if (id->killed_new_ssa_names)
+    {
+      ipa_release_ssas_in_hash (id->killed_new_ssa_names);
+      delete id->killed_new_ssa_names;
+      id->killed_new_ssa_names = NULL;
+    }
 
   return body;
 }