https://gcc.gnu.org/g:e7c3a7ccd6209c1a906bdf59207f0fa4258b692b
commit r15-9246-ge7c3a7ccd6209c1a906bdf59207f0fa4258b692b Author: Jakub Jelinek <ja...@redhat.com> Date: Mon Apr 7 11:57:36 2025 +0200 tailc: Extend the IPA-VRP workaround [PR119614] The IPA-VRP workaround in the tailc/musttail passes was just comparing the singleton constant from a tail call candidate return with the ret_val. This unfortunately doesn't work in the following testcase, where we have <bb 5> [local count: 152205050]: baz (); [must tail call] goto <bb 4>; [100.00%] <bb 6> [local count: 762356696]: _8 = foo (); <bb 7> [local count: 1073741824]: # _3 = PHI <0B(4), _8(6)> return _3; and in the unreduced testcase even more PHIs before we reach the return stmt. Normally when the call has lhs, whenever we follow a (non-EH) successor edge, it calls propagate_through_phis and that walks the PHIs in the destination bb of the edge and when it sees a PHI whose argument matches that of the currently tracked value (ass_var), it updates ass_var to PHI result of that PHI. I think it is theoretically dangerous that it picks the first one, perhaps there could be multiple PHIs, so perhaps safer would be walk backwards from the return value up to the call. Anyway, this PR is about the IPA-VRP workaround, there ass_var is NULL because the potential tail call has no lhs, but ret_var is not TREE_CONSTANT but SSA_NAME with PHI as SSA_NAME_DEF_STMT. The following patch handles it by pushing the edges we've walked through when ass_var is NULL into a vector and if ret_var is SSA_NAME set to PHI result, it attempts to walk back from the ret_var through arguments of PHIs corresponding to the edges we've walked back until we reach a constant and compare that constant against the singleton value as well. 2025-04-07 Jakub Jelinek <ja...@redhat.com> PR tree-optimization/119614 * tree-tailcall.cc (find_tail_calls): Remember edges which have been walked through if !ass_var. Perform IPA-VRP workaround even when ret_var is not TREE_CONSTANT, in that case check in a loop if it is a PHI result and in that case look at the PHI argument from corresponding edge in the edge vector. * g++.dg/opt/pr119613.C: Change { c || c++11 } in obviously C++ only test to just c++11. * g++.dg/opt/pr119614.C: New test. Diff: --- gcc/testsuite/g++.dg/opt/pr119613.C | 2 +- gcc/testsuite/g++.dg/opt/pr119614.C | 30 +++++++++++++++++++++++ gcc/tree-tailcall.cc | 47 ++++++++++++++++++++++++++++++++----- 3 files changed, 72 insertions(+), 7 deletions(-) diff --git a/gcc/testsuite/g++.dg/opt/pr119613.C b/gcc/testsuite/g++.dg/opt/pr119613.C index 432a30cdcdb0..2ced2e8fa2a0 100644 --- a/gcc/testsuite/g++.dg/opt/pr119613.C +++ b/gcc/testsuite/g++.dg/opt/pr119613.C @@ -1,5 +1,5 @@ // PR middle-end/119613 -// { dg-do compile { target { musttail && { c || c++11 } } } } +// { dg-do compile { target { musttail && c++11 } } } // { dg-options "-O0" } struct S { S () {} }; diff --git a/gcc/testsuite/g++.dg/opt/pr119614.C b/gcc/testsuite/g++.dg/opt/pr119614.C new file mode 100644 index 000000000000..cb73fc3ec09d --- /dev/null +++ b/gcc/testsuite/g++.dg/opt/pr119614.C @@ -0,0 +1,30 @@ +// PR tree-optimization/119614 +// { dg-do compile { target musttail } } +// { dg-options "-O2" } + +struct S {} b; +char *foo (); +int e, g; +void bar (); +void corge (S); + +[[gnu::noinline]] char * +baz () +{ + bar (); + return 0; +} + +const char * +qux () +{ + if (e) + { + S a = b; + corge (a); + if (g) + return 0; + [[gnu::musttail]] return baz (); + } + return foo (); +} diff --git a/gcc/tree-tailcall.cc b/gcc/tree-tailcall.cc index c8740f9353e2..f51bb970e329 100644 --- a/gcc/tree-tailcall.cc +++ b/gcc/tree-tailcall.cc @@ -920,6 +920,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, auto_bitmap to_move_defs; auto_vec<gimple *> to_move_stmts; bool is_noreturn = gimple_call_noreturn_p (call); + auto_vec<edge> edges; abb = bb; agsi = gsi; @@ -933,6 +934,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, { edge e = single_non_eh_succ_edge (abb); ass_var = propagate_through_phis (ass_var, e); + if (!ass_var) + edges.safe_push (e); abb = e->dest; agsi = gsi_start_bb (abb); } @@ -1040,9 +1043,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, /* 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 (ass_var == NULL_TREE && !tail_recursion) if (tree type = gimple_range_type (call)) if (tree callee = gimple_call_fndecl (call)) if ((INTEGRAL_TYPE_P (type) @@ -1052,9 +1053,43 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, 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; + && val.singleton_p (&valr)) + { + tree rv = ret_var; + unsigned int i = edges.length (); + /* If ret_var is equal to valr, we can tail optimize. */ + if (operand_equal_p (ret_var, valr, 0)) + ok = true; + else + /* Otherwise, if ret_var is a PHI result, try to find out + if valr isn't propagated through PHIs on the path from + call's bb to SSA_NAME_DEF_STMT (ret_var)'s bb. */ + while (TREE_CODE (rv) == SSA_NAME + && gimple_code (SSA_NAME_DEF_STMT (rv)) == GIMPLE_PHI) + { + tree nrv = NULL_TREE; + gimple *g = SSA_NAME_DEF_STMT (rv); + for (; i; --i) + { + if (edges[i - 1]->dest == gimple_bb (g)) + { + nrv + = gimple_phi_arg_def_from_edge (g, + edges[i - 1]); + --i; + break; + } + } + if (nrv == NULL_TREE) + break; + if (operand_equal_p (nrv, valr, 0)) + { + ok = true; + break; + } + rv = nrv; + } + } if (!ok) { maybe_error_musttail (call,