summaryrefslogtreecommitdiff
path: root/gcc/tree-tailcall.c
diff options
context:
space:
mode:
authorRichard Sandiford <richard.sandiford@arm.com>2019-08-09 09:37:55 +0000
committerRichard Sandiford <rsandifo@gcc.gnu.org>2019-08-09 09:37:55 +0000
commit97bf048c04d93ba1e0265893fdb06f8991c149f7 (patch)
treea9411dc3fa2b7e713504601c237f7d2978768f42 /gcc/tree-tailcall.c
parentc787deb0124b667802d8519bc285894bb6d771d7 (diff)
Reject tail calls that read from an escaped RESULT_DECL (PR90313)
In this PR we have two return paths from a function "map". The common code sets <result> to the value returned by one path, while the other path does: <retval> = map (&<retval>, ...); We treated this call as tail recursion, losing the copy semantics on the value returned by the recursive call. We'd correctly reject the same thing for variables: local = map (&local, ...); The problem is that RESULT_DECLs didn't get the same treatment. 2019-08-09 Richard Sandiford <richard.sandiford@arm.com> gcc/ PR middle-end/90313 * tree-tailcall.c (find_tail_calls): Reject calls that might read from an escaped RESULT_DECL. gcc/testsuite/ PR middle-end/90313 * g++.dg/torture/pr90313.cc: New test. From-SVN: r274234
Diffstat (limited to 'gcc/tree-tailcall.c')
-rw-r--r--gcc/tree-tailcall.c29
1 files changed, 29 insertions, 0 deletions
diff --git a/gcc/tree-tailcall.c b/gcc/tree-tailcall.c
index a99cd113ce6..a4b563efd73 100644
--- a/gcc/tree-tailcall.c
+++ b/gcc/tree-tailcall.c
@@ -491,6 +491,35 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
&& !stmt_can_throw_external (cfun, stmt))
return;
+ /* If the function returns a value, then at present, the tail call
+ must return the same type of value. There is conceptually a copy
+ between the object returned by the tail call candidate and the
+ object returned by CFUN itself.
+
+ This means that if we have:
+
+ lhs = f (&<retval>); // f reads from <retval>
+ // (lhs is usually also <retval>)
+
+ there is a copy between the temporary object returned by f and lhs,
+ meaning that any use of <retval> in f occurs before the assignment
+ to lhs begins. Thus the <retval> that is live on entry to the call
+ to f is really an independent local variable V that happens to be
+ stored in the RESULT_DECL rather than a local VAR_DECL.
+
+ Turning this into a tail call would remove the copy and make the
+ lifetimes of the return value and V overlap. The same applies to
+ tail recursion, since if f can read from <retval>, we have to assume
+ that CFUN might already have written to <retval> before the call.
+
+ The problem doesn't apply when <retval> is passed by value, but that
+ isn't a case we handle anyway. */
+ tree result_decl = DECL_RESULT (cfun->decl);
+ if (result_decl
+ && may_be_aliased (result_decl)
+ && ref_maybe_used_by_stmt_p (call, result_decl))
+ return;
+
/* We found the call, check whether it is suitable. */
tail_recursion = false;
func = gimple_call_fndecl (call);