From 97bf048c04d93ba1e0265893fdb06f8991c149f7 Mon Sep 17 00:00:00 2001 From: Richard Sandiford Date: Fri, 9 Aug 2019 09:37:55 +0000 Subject: [PATCH] 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 to the value returned by one path, while the other path does: = map (&, ...); 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 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 --- gcc/ChangeLog | 6 +++++ gcc/testsuite/ChangeLog | 5 ++++ gcc/testsuite/g++.dg/torture/pr90313.cc | 33 +++++++++++++++++++++++++ gcc/tree-tailcall.c | 29 ++++++++++++++++++++++ 4 files changed, 73 insertions(+) create mode 100644 gcc/testsuite/g++.dg/torture/pr90313.cc diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 8bc3d91816c..beffd1b91c0 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2019-08-09 Richard Sandiford + + PR middle-end/90313 + * tree-tailcall.c (find_tail_calls): Reject calls that might + read from an escaped RESULT_DECL. + 2019-08-09 Martin Liska * doc/invoke.texi: Document the option value. diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 58e5fe500f9..7ab92df5a56 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2019-08-09 Richard Sandiford + + PR middle-end/90313 + * g++.dg/torture/pr90313.cc: New test. + 2019-08-09 Martin Liska * g++.dg/lto/devirt-19_0.C: Add -flto=auto. diff --git a/gcc/testsuite/g++.dg/torture/pr90313.cc b/gcc/testsuite/g++.dg/torture/pr90313.cc new file mode 100644 index 00000000000..d9f183a2939 --- /dev/null +++ b/gcc/testsuite/g++.dg/torture/pr90313.cc @@ -0,0 +1,33 @@ +// { dg-do run } + +#include + +namespace std { + template struct array { + T elems[N]; + const T &operator[](size_t i) const { return elems[i]; } + }; +} + +using Coordinates = std::array; + +Coordinates map(const Coordinates &c, size_t level) +{ + Coordinates result{ c[1], c[2], c[0] }; + + if (level != 0) + result = map (result, level - 1); + + return result; +} + +int main() +{ + Coordinates vecOfCoordinates = { 1.0, 2.0, 3.0 }; + + auto result = map(vecOfCoordinates, 1); + if (result[0] != 3 || result[1] != 1 || result[2] != 2) + __builtin_abort (); + + return 0; +} 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 (&); // f reads from + // (lhs is usually also ) + + there is a copy between the temporary object returned by f and lhs, + meaning that any use of in f occurs before the assignment + to lhs begins. Thus the 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 , we have to assume + that CFUN might already have written to before the call. + + The problem doesn't apply when 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); -- 2.30.2