--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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);
+}
#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
{
if (dump_file)
fprintf (dump_file, "Basic block %d has extra exit edges\n",
- bb->index);
+ bb->index);
return;
}
if (!cfun->has_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);
{
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;
}
{
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
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;
}
}
&& (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;
}
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;
}