diff --git a/gcc/cfgexpand.cc b/gcc/cfgexpand.cc
index e58327b239b..feed001f3c9 100644
--- a/gcc/cfgexpand.cc
+++ b/gcc/cfgexpand.cc
@@ -863,7 +863,8 @@ update_alias_info_with_stack_vars (void)
 
       add_partitioned_vars_to_ptset (&cfun->gimple_df->escaped,
 				     decls_to_partitions, &visited, temp);
-
+      add_partitioned_vars_to_ptset (&cfun->gimple_df->escaped_return,
+				     decls_to_partitions, &visited, temp);
       delete decls_to_partitions;
       BITMAP_FREE (temp);
     }
diff --git a/gcc/gimple-ssa.h b/gcc/gimple-ssa.h
index f2cffa2b159..79637058f70 100644
--- a/gcc/gimple-ssa.h
+++ b/gcc/gimple-ssa.h
@@ -76,8 +76,10 @@ struct GTY(()) gimple_df {
   /* Artificial variable used for the virtual operand FUD chain.  */
   tree vop;
 
-  /* The PTA solution for the ESCAPED artificial variable.  */
+  /* The PTA solution for the ESCAPED and ESCAPED_RETURN artificial
+     variables.  */
   struct pt_solution escaped;
+  struct pt_solution escaped_return;
 
   /* A map of decls to artificial ssa-names that point to the partition
      of the decl.  */
diff --git a/gcc/ipa-icf.cc b/gcc/ipa-icf.cc
index bbdfd445397..c72c9d57a80 100644
--- a/gcc/ipa-icf.cc
+++ b/gcc/ipa-icf.cc
@@ -3506,6 +3506,7 @@ sem_item_optimizer::fixup_points_to_sets (void)
 	    && SSA_NAME_PTR_INFO (name))
 	  fixup_pt_set (&SSA_NAME_PTR_INFO (name)->pt);
       fixup_pt_set (&fn->gimple_df->escaped);
+      fixup_pt_set (&fn->gimple_df->escaped_return);
 
        /* The above gets us to 99% I guess, at least catching the
 	  address compares.  Below also gets us aliasing correct
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pta-return-1.c b/gcc/testsuite/gcc.dg/tree-ssa/pta-return-1.c
new file mode 100644
index 00000000000..9c2416e7810
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pta-return-1.c
@@ -0,0 +1,16 @@
+/* PR112653 */
+/* { dg-do compile } */
+/* { dg-options "-O -fdump-tree-optimized" } */
+
+char *test;
+char *
+copy_test ()
+{
+  char *test2 = __builtin_malloc (1000);
+  __builtin_memmove (test2, test, 1000);
+  return test2;
+}
+
+/* We should be able to turn the memmove into memcpy by means of alias
+   analysis.  */
+/* { dg-final { scan-tree-dump "memcpy" "optimized" } } */
diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc
index 0b14118b94b..59847166842 100644
--- a/gcc/tree-inline.cc
+++ b/gcc/tree-inline.cc
@@ -5147,7 +5147,10 @@ expand_call_inline (basic_block bb, gimple *stmt, copy_body_data *id,
 
   /* Reset the escaped solution.  */
   if (cfun->gimple_df)
-    pt_solution_reset (&cfun->gimple_df->escaped);
+    {
+      pt_solution_reset (&cfun->gimple_df->escaped);
+      pt_solution_reset (&cfun->gimple_df->escaped_return);
+    }
 
   /* Add new automatic variables to IFN_GOMP_SIMT_ENTER arguments.  */
   if (id->dst_simt_vars && id->dst_simt_vars->length () > 0)
diff --git a/gcc/tree-parloops.cc b/gcc/tree-parloops.cc
index 80f3dd6dce2..50e61f00413 100644
--- a/gcc/tree-parloops.cc
+++ b/gcc/tree-parloops.cc
@@ -4147,7 +4147,10 @@ parallelize_loops (bool oacc_kernels_p)
      which local variables will escape.  Reset the points-to solution
      for ESCAPED.  */
   if (changed)
-    pt_solution_reset (&cfun->gimple_df->escaped);
+    {
+      pt_solution_reset (&cfun->gimple_df->escaped);
+      pt_solution_reset (&cfun->gimple_df->escaped_return);
+    }
 
   return changed;
 }
diff --git a/gcc/tree-sra.cc b/gcc/tree-sra.cc
index 3a0d52675fe..0349410972e 100644
--- a/gcc/tree-sra.cc
+++ b/gcc/tree-sra.cc
@@ -2026,7 +2026,7 @@ maybe_add_sra_candidate (tree var)
        /* There are cases where non-addressable variables fail the
 	  pt_solutions_check test, e.g in gcc.dg/uninit-40.c. */
        || (TREE_ADDRESSABLE (var)
-	   && pt_solution_includes (&cfun->gimple_df->escaped, var))
+	   && pt_solution_includes (&cfun->gimple_df->escaped_return, var))
        || (TREE_CODE (var) == RESULT_DECL
 	   && !DECL_BY_REFERENCE (var)
 	   && aggregate_value_p (var, current_function_decl)))
diff --git a/gcc/tree-ssa-alias.cc b/gcc/tree-ssa-alias.cc
index 373940b5f6c..a708f7e8d28 100644
--- a/gcc/tree-ssa-alias.cc
+++ b/gcc/tree-ssa-alias.cc
@@ -496,7 +496,8 @@ ref_may_alias_global_p_1 (tree base, bool escaped_local_p)
   if (DECL_P (base))
     return (is_global_var (base)
 	    || (escaped_local_p
-		&& pt_solution_includes (&cfun->gimple_df->escaped, base)));
+		&& pt_solution_includes (&cfun->gimple_df->escaped_return,
+					 base)));
   else if (TREE_CODE (base) == MEM_REF
 	   || TREE_CODE (base) == TARGET_MEM_REF)
     return ptr_deref_may_alias_global_p (TREE_OPERAND (base, 0),
@@ -579,6 +580,9 @@ dump_alias_info (FILE *file)
   fprintf (file, "\nESCAPED");
   dump_points_to_solution (file, &cfun->gimple_df->escaped);
 
+  fprintf (file, "\nESCAPED_RETURN");
+  dump_points_to_solution (file, &cfun->gimple_df->escaped_return);
+
   fprintf (file, "\n\nFlow-insensitive points-to information\n\n");
 
   FOR_EACH_SSA_NAME (i, ptr, cfun)
diff --git a/gcc/tree-ssa-structalias.cc b/gcc/tree-ssa-structalias.cc
index ee9313c59ca..6db10d5fe11 100644
--- a/gcc/tree-ssa-structalias.cc
+++ b/gcc/tree-ssa-structalias.cc
@@ -365,8 +365,8 @@ vi_next (varinfo_t vi)
 /* Static IDs for the special variables.  Variable ID zero is unused
    and used as terminator for the sub-variable chain.  */
 enum { nothing_id = 1, anything_id = 2, string_id = 3,
-       escaped_id = 4, nonlocal_id = 5,
-       storedanything_id = 6, integer_id = 7 };
+       escaped_id = 4, nonlocal_id = 5, escaped_return_id = 6,
+       storedanything_id = 7, integer_id = 8 };
 
 /* Return a new variable info structure consisting for a variable
    named NAME, and using constraint graph node NODE.  Append it
@@ -5221,23 +5221,18 @@ find_func_aliases (struct function *fn, gimple *origt)
 	   && gimple_return_retval (as_a <greturn *> (t)) != NULL_TREE)
     {
       greturn *return_stmt = as_a <greturn *> (t);
-      fi = NULL;
-      if (!in_ipa_mode
-	  && SSA_VAR_P (gimple_return_retval (return_stmt)))
-	{
-	  /* We handle simple returns by post-processing the solutions.  */
-	  ;
-	}
-      if (!(fi = get_vi_for_tree (fn->decl)))
-	make_escape_constraint (gimple_return_retval (return_stmt));
-      else if (in_ipa_mode)
+      tree retval = gimple_return_retval (return_stmt);
+      if (!in_ipa_mode)
+	make_constraint_to (escaped_return_id, retval);
+      else
 	{
 	  struct constraint_expr lhs ;
 	  struct constraint_expr *rhsp;
 	  unsigned i;
 
+	  fi = lookup_vi_for_tree (fn->decl);
 	  lhs = get_function_part_constraint (fi, fi_result);
-	  get_constraint_for_rhs (gimple_return_retval (return_stmt), &rhsc);
+	  get_constraint_for_rhs (retval, &rhsc);
 	  FOR_EACH_VEC_ELT (rhsc, i, rhsp)
 	    process_constraint (new_constraint (lhs, *rhsp));
 	}
@@ -6665,6 +6660,7 @@ set_uids_in_ptset (bitmap into, bitmap from, struct pt_solution *pt,
   unsigned int i;
   bitmap_iterator bi;
   varinfo_t escaped_vi = get_varinfo (find (escaped_id));
+  varinfo_t escaped_return_vi = get_varinfo (find (escaped_return_id));
   bool everything_escaped
     = escaped_vi->solution && bitmap_bit_p (escaped_vi->solution, anything_id);
 
@@ -6682,6 +6678,9 @@ set_uids_in_ptset (bitmap into, bitmap from, struct pt_solution *pt,
 	  pt->vars_contains_escaped = true;
 	  pt->vars_contains_escaped_heap |= vi->is_heap_var;
 	}
+      if (escaped_return_vi->solution
+	  && bitmap_bit_p (escaped_return_vi->solution, i))
+	pt->vars_contains_escaped_heap |= vi->is_heap_var;
 
       if (vi->is_restrict_var)
 	pt->vars_contains_restrict = true;
@@ -7196,6 +7195,7 @@ init_base_vars (void)
   varinfo_t var_string;
   varinfo_t var_escaped;
   varinfo_t var_nonlocal;
+  varinfo_t var_escaped_return;
   varinfo_t var_storedanything;
   varinfo_t var_integer;
 
@@ -7271,6 +7271,16 @@ init_base_vars (void)
   var_nonlocal->fullsize = ~0;
   var_nonlocal->is_special_var = 1;
 
+  /* Create the ESCAPED_RETURN variable, used to represent the set of escaped
+     memory via a regular return stmt.  */
+  var_escaped_return = new_var_info (NULL_TREE, "ESCAPED_RETURN", false);
+  gcc_assert (var_escaped_return->id == escaped_return_id);
+  var_escaped_return->is_artificial_var = 1;
+  var_escaped_return->offset = 0;
+  var_escaped_return->size = ~0;
+  var_escaped_return->fullsize = ~0;
+  var_escaped_return->is_special_var = 0;
+
   /* ESCAPED = *ESCAPED, because escaped is may-deref'd at calls, etc.  */
   lhs.type = SCALAR;
   lhs.var = escaped_id;
@@ -7315,6 +7325,24 @@ init_base_vars (void)
   rhs.offset = 0;
   process_constraint (new_constraint (lhs, rhs));
 
+  /* Transitively close ESCAPED_RETURN.
+     ESCAPED_RETURN = ESCAPED_RETURN + UNKNOWN_OFFSET
+     ESCAPED_RETURN = *ESCAPED_RETURN.  */
+  lhs.type = SCALAR;
+  lhs.var = escaped_return_id;
+  lhs.offset = 0;
+  rhs.type = SCALAR;
+  rhs.var = escaped_return_id;
+  rhs.offset = UNKNOWN_OFFSET;
+  process_constraint (new_constraint (lhs, rhs));
+  lhs.type = SCALAR;
+  lhs.var = escaped_return_id;
+  lhs.offset = 0;
+  rhs.type = DEREF;
+  rhs.var = escaped_return_id;
+  rhs.offset = 0;
+  process_constraint (new_constraint (lhs, rhs));
+
   /* Create the STOREDANYTHING variable, used to represent the set of
      variables stored to *ANYTHING.  */
   var_storedanything = new_var_info (NULL_TREE, "STOREDANYTHING", false);
@@ -7555,70 +7583,6 @@ compute_points_to_sets (void)
   /* From the constraints compute the points-to sets.  */
   solve_constraints ();
 
-  /* Post-process solutions for escapes through returns.  */
-  edge_iterator ei;
-  edge e;
-  FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
-    if (greturn *ret = safe_dyn_cast <greturn *> (*gsi_last_bb (e->src)))
-      {
-	tree val = gimple_return_retval (ret);
-	/* ???  Easy to handle simple indirections with some work.
-	   Arbitrary references like foo.bar.baz are more difficult
-	   (but conservatively easy enough with just looking at the base).
-	   Mind to fixup find_func_aliases as well.  */
-	if (!val || !SSA_VAR_P (val))
-	  continue;
-	/* returns happen last in non-IPA so they only influence
-	   the ESCAPED solution and we can filter local variables.  */
-	varinfo_t escaped_vi = get_varinfo (find (escaped_id));
-	varinfo_t vi = lookup_vi_for_tree (val);
-	bitmap delta = BITMAP_ALLOC (&pta_obstack);
-	bitmap_iterator bi;
-	unsigned i;
-	for (; vi; vi = vi_next (vi))
-	  {
-	    varinfo_t part_vi = get_varinfo (find (vi->id));
-	    EXECUTE_IF_AND_COMPL_IN_BITMAP (part_vi->solution,
-					    escaped_vi->solution, 0, i, bi)
-	      {
-		varinfo_t pointed_to_vi = get_varinfo (i);
-		if (pointed_to_vi->is_global_var
-		    /* We delay marking of heap memory as global.  */
-		    || pointed_to_vi->is_heap_var)
-		  bitmap_set_bit (delta, i);
-	      }
-	  }
-
-	/* Now compute the transitive closure.  */
-	bitmap_ior_into (escaped_vi->solution, delta);
-	bitmap new_delta = BITMAP_ALLOC (&pta_obstack);
-	while (!bitmap_empty_p (delta))
-	  {
-	    EXECUTE_IF_SET_IN_BITMAP (delta, 0, i, bi)
-	      {
-		varinfo_t pointed_to_vi = get_varinfo (i);
-		pointed_to_vi = get_varinfo (find (pointed_to_vi->id));
-		unsigned j;
-		bitmap_iterator bi2;
-		EXECUTE_IF_AND_COMPL_IN_BITMAP (pointed_to_vi->solution,
-						escaped_vi->solution,
-						0, j, bi2)
-		  {
-		    varinfo_t pointed_to_vi2 = get_varinfo (j);
-		    if (pointed_to_vi2->is_global_var
-			/* We delay marking of heap memory as global.  */
-			|| pointed_to_vi2->is_heap_var)
-		      bitmap_set_bit (new_delta, j);
-		  }
-	      }
-	    bitmap_ior_into (escaped_vi->solution, new_delta);
-	    bitmap_clear (delta);
-	    std::swap (delta, new_delta);
-	  }
-	BITMAP_FREE (delta);
-	BITMAP_FREE (new_delta);
-      }
-
   if (dump_file && (dump_flags & TDF_STATS))
     dump_sa_stats (dump_file);
 
@@ -7634,6 +7598,12 @@ compute_points_to_sets (void)
      points-to solution queries.  */
   cfun->gimple_df->escaped.escaped = 0;
 
+  /* The ESCAPED_RETURN solution is what contains all memory that needs
+     to be considered global.  */
+  cfun->gimple_df->escaped_return
+    = find_what_var_points_to (cfun->decl, get_varinfo (escaped_return_id));
+  cfun->gimple_df->escaped_return.escaped = 1;
+
   /* Compute the points-to sets for pointer SSA_NAMEs.  */
   unsigned i;
   tree ptr;
diff --git a/gcc/tree-ssa.cc b/gcc/tree-ssa.cc
index 3858a63de20..9e6233fea2c 100644
--- a/gcc/tree-ssa.cc
+++ b/gcc/tree-ssa.cc
@@ -1216,6 +1216,7 @@ init_tree_ssa (struct function *fn, int size)
   fn->gimple_df = ggc_cleared_alloc<gimple_df> ();
   fn->gimple_df->default_defs = hash_table<ssa_name_hasher>::create_ggc (20);
   pt_solution_reset (&fn->gimple_df->escaped);
+  pt_solution_reset (&fn->gimple_df->escaped_return);
   init_ssanames (fn, size);
 }
 
@@ -1233,6 +1234,7 @@ delete_tree_ssa (struct function *fn)
   fn->gimple_df->default_defs->empty ();
   fn->gimple_df->default_defs = NULL;
   pt_solution_reset (&fn->gimple_df->escaped);
+  pt_solution_reset (&fn->gimple_df->escaped_return);
   if (fn->gimple_df->decls_to_pointers != NULL)
     delete fn->gimple_df->decls_to_pointers;
   fn->gimple_df->decls_to_pointers = NULL;
