diff --git a/gcc/testsuite/c-c++-common/musttail14.c b/gcc/testsuite/c-c++-common/musttail14.c new file mode 100644 index 0000000000000000000000000000000000000000..e2ab20bc5a46c9ac32f63510b24526e80afafdc4 --- /dev/null +++ b/gcc/testsuite/c-c++-common/musttail14.c @@ -0,0 +1,90 @@ +/* PR tree-optimization/118430 */ +/* { dg-do compile { target musttail } } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ +/* { dg-final { scan-tree-dump-times " bar \\\(\[^\n\r]\*\\\); \\\[tail call\\\] \\\[must tail call\\\]" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-times " freddy \\\(\[^\n\r]\*\\\); \\\[tail call\\\] \\\[must tail call\\\]" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-times " (?:bar|freddy) \\\(\[^\n\r]\*\\\); \\\[tail call\\\]" 2 "optimized" } } */ + +__attribute__ ((noipa)) void +foo (int x) +{ + (void) x; +} + +__attribute__ ((noinline)) int +bar (int x) +{ + foo (x); + return 1; +} + +__attribute__ ((noinline)) int +baz (int *x) +{ + foo (*x); + return 2; +} + +__attribute__((noipa)) int +qux (int x) +{ + { + int v; + foo (x); + baz (&v); + } + [[gnu::musttail]] + return bar (x); +} + +__attribute__((noipa)) int +corge (int x) +{ + { + int v; + foo (x); + baz (&v); + } + return bar (x) + 1; +} + +__attribute__ ((noinline)) float +freddy (int x) +{ + foo (x); + return 1.75f; +} + +__attribute__((noipa)) float +garply (int x) +{ + { + int v; + foo (x); + baz (&v); + } + [[gnu::musttail]] + return freddy (x); +} + +__attribute__((noipa)) float +quux (int x) +{ + { + int v; + foo (x); + baz (&v); + } + return freddy (x) + 0.25f; +} + +int v; + +int +main () +{ + qux (v); + corge (v); + garply (v); + quux (v); +} diff --git a/gcc/testsuite/c-c++-common/pr118430.c b/gcc/testsuite/c-c++-common/pr118430.c new file mode 100644 index 0000000000000000000000000000000000000000..e14592f798078c765f4d31429b96ff5c5cad3bc7 --- /dev/null +++ b/gcc/testsuite/c-c++-common/pr118430.c @@ -0,0 +1,89 @@ +/* PR tree-optimization/118430 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ +/* { dg-final { scan-tree-dump-times " bar \\\(\[^\n\r]\*\\\); \\\[tail call\\\]" 2 "optimized" } } */ +/* { dg-final { scan-tree-dump-times " freddy \\\(\[^\n\r]\*\\\); \\\[tail call\\\]" 2 "optimized" } } */ + +__attribute__ ((noipa)) void +foo (int x) +{ + (void) x; +} + +__attribute__ ((noinline)) int +bar (int x) +{ + foo (x); + return 1; +} + +__attribute__ ((noinline)) int +baz (int *x) +{ + foo (*x); + return 2; +} + +__attribute__((noipa)) int +qux (int x) +{ + { + int v; + foo (x); + baz (&v); + } + return bar (x); +} + +__attribute__((noipa)) int +corge (int x) +{ + { + int v; + foo (x); + baz (&v); + } + bar (x); + return 1; +} + +__attribute__ ((noinline)) float +freddy (int x) +{ + foo (x); + return 1.75f; +} + +__attribute__((noipa)) float +garply (int x) +{ + { + int v; + foo (x); + baz (&v); + } + return freddy (x); +} + +__attribute__((noipa)) float +quux (int x) +{ + { + int v; + foo (x); + baz (&v); + } + freddy (x); + return 1.75f; +} + +int v; + +int +main () +{ + qux (v); + corge (v); + garply (v); + quux (v); +} diff --git a/gcc/tree-tailcall.cc b/gcc/tree-tailcall.cc index d6d7eb4b47c37a690de57bf411566ecf17d8ec44..f97df31eb3cf6279a53811755dd56157fc9692fd 100644 --- a/gcc/tree-tailcall.cc +++ b/gcc/tree-tailcall.cc @@ -45,6 +45,12 @@ along with GCC; see the file COPYING3. If not see #include "ipa-utils.h" #include "tree-ssa-live.h" #include "diagnostic-core.h" +#include "gimple-range.h" +#include "alloc-pool.h" +#include "sreal.h" +#include "symbol-summary.h" +#include "ipa-cp.h" +#include "ipa-prop.h" /* The file implements the tail recursion elimination. It is also used to analyze the tail calls in general, passing the results to the rtl level @@ -483,7 +489,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, { if (dump_file) fprintf (dump_file, "Basic block %d has extra exit edges\n", - bb->index); + bb->index); return; } if (!cfun->has_musttail) @@ -517,7 +523,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, if (bad_stmt) { maybe_error_musttail (call, - _("memory reference or volatile after call")); + _("memory reference or volatile after " + "call")); return; } ass_var = gimple_call_lhs (call); @@ -597,10 +604,10 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, { if (stmt == last_stmt) maybe_error_musttail (call, - _("call may throw exception that does not propagate")); + _("call may throw exception that does not " + "propagate")); else - maybe_error_musttail (call, - _("code between call and return")); + maybe_error_musttail (call, _("code between call and return")); return; } @@ -715,7 +722,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, { if (local_live_vars) BITMAP_FREE (local_live_vars); - maybe_error_musttail (call, _("call invocation refers to locals")); + maybe_error_musttail (call, + _("call invocation refers to locals")); return; } else @@ -724,7 +732,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, if (bitmap_bit_p (local_live_vars, *v)) { BITMAP_FREE (local_live_vars); - maybe_error_musttail (call, _("call invocation refers to locals")); + maybe_error_musttail (call, + _("call invocation refers to locals")); return; } } @@ -833,15 +842,39 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, && (ret_var != ass_var && !(is_empty_type (TREE_TYPE (ret_var)) && !ass_var))) { - maybe_error_musttail (call, _("call uses return slot")); - return; + bool ok = false; + value_range val; + tree valr; + /* If IPA-VRP proves called function always returns a singleton range, + the return value is replaced by the only value in that range. + For tail call purposes, pretend such replacement didn't happen. */ + if (ass_var == NULL_TREE + && !tail_recursion + && TREE_CONSTANT (ret_var)) + if (tree type = gimple_range_type (call)) + if (tree callee = gimple_call_fndecl (call)) + if ((INTEGRAL_TYPE_P (type) || SCALAR_FLOAT_TYPE_P (type)) + && useless_type_conversion_p (TREE_TYPE (TREE_TYPE (callee)), + type) + && useless_type_conversion_p (TREE_TYPE (ret_var), type) + && ipa_return_value_range (val, callee) + && val.singleton_p (&valr) + && operand_equal_p (ret_var, valr, 0)) + ok = true; + if (!ok) + { + maybe_error_musttail (call, + _("call and return value are different")); + return; + } } /* If this is not a tail recursive call, we cannot handle addends or multiplicands. */ if (!tail_recursion && (m || a)) { - maybe_error_musttail (call, _("operations after non tail recursive call")); + maybe_error_musttail (call, + _("operations after non tail recursive call")); return; } @@ -849,7 +882,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, if (m && POINTER_TYPE_P (TREE_TYPE (DECL_RESULT (current_function_decl)))) { maybe_error_musttail (call, - _("tail recursion with pointers can only use additions")); + _("tail recursion with pointers can only use " + "additions")); return; }