diff options
author | Richard Sandiford <richard.sandiford@arm.com> | 2019-08-09 09:37:55 +0000 |
---|---|---|
committer | Richard Sandiford <rsandifo@gcc.gnu.org> | 2019-08-09 09:37:55 +0000 |
commit | 97bf048c04d93ba1e0265893fdb06f8991c149f7 (patch) | |
tree | a9411dc3fa2b7e713504601c237f7d2978768f42 /gcc/tree-tailcall.c | |
parent | c787deb0124b667802d8519bc285894bb6d771d7 (diff) | |
download | gcc-97bf048c04d93ba1e0265893fdb06f8991c149f7.zip gcc-97bf048c04d93ba1e0265893fdb06f8991c149f7.tar.gz gcc-97bf048c04d93ba1e0265893fdb06f8991c149f7.tar.bz2 |
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.c | 29 |
1 files changed, 29 insertions, 0 deletions
diff --git a/gcc/tree-tailcall.c b/gcc/tree-tailcall.c index a99cd11..a4b563e 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); |