aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/testsuite/c-c++-common/musttail26.c33
-rw-r--r--gcc/tree-inline.cc9
2 files changed, 42 insertions, 0 deletions
diff --git a/gcc/testsuite/c-c++-common/musttail26.c b/gcc/testsuite/c-c++-common/musttail26.c
new file mode 100644
index 0000000..3d0f8c9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/musttail26.c
@@ -0,0 +1,33 @@
+/* PR ipa/119376 */
+/* { dg-do compile { target musttail } } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-times " \[^\n\r]* = foo \\\(3, \[^\n\r]*\\\); \\\[tail call\\\] \\\[must tail call\\\]" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " \[^\n\r]* = foo \\\(4, \[^\n\r]*\\\); \\\[tail call\\\] \\\[must tail call\\\]" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-not " foo \\\(\[12], \[^\n\r]*\\\); \\\[tail call\\\]" "optimized" } } */
+
+int foo (int, int);
+int v, w[10];
+
+static inline __attribute__((always_inline)) int
+bar (int x, int y)
+{
+ [[gnu::musttail]] return foo (x, y);
+}
+
+static int
+baz (int x, int y)
+{
+ [[gnu::musttail]] return foo (x, x + y + (v | y) * (v & y));
+}
+
+int
+qux (int x, int y)
+{
+ w[0] = bar (1, x + y);
+ w[1] = baz (2, x + y);
+ if (x == 42)
+ [[gnu::musttail]] return bar (3, x + y);
+ if (x == -42)
+ [[gnu::musttail]] return baz (4, x + y);
+ return 0;
+}
diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc
index 5b35390..05843b8 100644
--- a/gcc/tree-inline.cc
+++ b/gcc/tree-inline.cc
@@ -1892,6 +1892,15 @@ remap_gimple_stmt (gimple *stmt, copy_body_data *id)
gimple_call_set_tail (call_stmt, false);
if (gimple_call_from_thunk_p (call_stmt))
gimple_call_set_from_thunk (call_stmt, false);
+ /* Silently clear musttail flag when inlining a function
+ with must tail call from a non-musttail call. The inlining
+ removes one frame so acts like musttail's intent, and we
+ can be inlining a function with musttail calls in the middle
+ of caller where musttail will always error. */
+ if (gimple_call_must_tail_p (call_stmt)
+ && id->call_stmt
+ && !gimple_call_must_tail_p (id->call_stmt))
+ gimple_call_set_must_tail (call_stmt, false);
if (gimple_call_internal_p (call_stmt))
switch (gimple_call_internal_fn (call_stmt))
{