On Mon, 7 Apr 2025, Jakub Jelinek wrote: > Hi! > > 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. > > Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
OK. I do wonder with all these patches whether it would be better to preserve the LHS on musttail calls instead? > 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. > > --- gcc/tree-tailcall.cc.jj 2025-04-04 20:52:34.450015821 +0200 > +++ gcc/tree-tailcall.cc 2025-04-05 14:50:50.106693562 +0200 > @@ -920,6 +920,7 @@ find_tail_calls (basic_block bb, struct > 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 > { > 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 > /* 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 > 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, > --- gcc/testsuite/g++.dg/opt/pr119613.C.jj 2025-04-04 20:51:42.482706589 > +0200 > +++ gcc/testsuite/g++.dg/opt/pr119613.C 2025-04-05 14:57:31.157353618 > +0200 > @@ -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 () {} }; > --- gcc/testsuite/g++.dg/opt/pr119614.C.jj 2025-04-05 14:57:16.276551780 > +0200 > +++ gcc/testsuite/g++.dg/opt/pr119614.C 2025-04-05 14:56:46.020954674 > +0200 > @@ -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 (); > +} > > Jakub > > -- Richard Biener <rguent...@suse.de> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg, Germany; GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)