https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120629
--- Comment #25 from GCC Commits <cvs-commit at gcc dot gnu.org> --- The master branch has been updated by Jakub Jelinek <ja...@gcc.gnu.org>: https://gcc.gnu.org/g:701a7cceba56217b466b5854d1e145bbf52679ac commit r16-1482-g701a7cceba56217b466b5854d1e145bbf52679ac Author: Jakub Jelinek <ja...@redhat.com> Date: Thu Jun 12 15:51:51 2025 +0200 expand: Fix up edge splitting for GIMPLE_COND expansion if there are any PHIs [PR120629] My r16-1398 PR120434 ranger during expansion change broke profiled lto bootstrap on x86_64-linux, the following testcase is reduced from that. The problem is during expand_gimple_cond, if we are unlucky that neither of edge_true and edge_false point to the next basic block, the code effectively attempts to split the false_edge and make the new bb BB_RTL with some extra instructions which just arranges to jump. It does it by creating a new bb, redirecting the false_edge and then creating a new edge from the new bb to the dest. Note, we don't have GIMPLE cfg hooks installed anymore and even if we would, the 3 calls aren't the same as one split_edge with transformation of the new bb into BB_RTL and adding it some BB_HEAD/BB_END. If false_edge->dest is BB_RTL or doesn't have PHI nodes (which before my patch was always the case because), then this works fine, but with PHI nodes on false_edge->dest redirect_edge_succ will remove the false_edge from dest->preds (unordered remove which moves into its place the last edge in the vector) and the new make_edge will then add the new edge as last in the vector. So, unless false_edge is the last edge in the dest->preds vector this effectively swaps the last edge in the vector with false_edge/its new replacement. gimple_split_edge solves this by temporarily clearing phi_nodes on dest (not needed when we don't have GIMPLE hooks), then making the new edge first and redirecting the old edge (plus restoring phi_nodes on dest). That way the redirection replaces the old edge with the new one and PHI arguments don't need adjustment. At the cost of temporarily needing one more edge in the vector and so if unlucky reallocation. Doing it like that is one of the options (i.e. just move the make_single_succ_edge call). This patch instead keeps doing what it did and just swaps two edges again if needed to restore the PHI behavior - remember edge_false->dest_idx first if there are PHI nodes in edge_false->dest and afterwards if new edge's dest_idx is different from the remembered one, swap the new edge with EDGE_PRED (dest, old_dest_idx). That way PHI arguments are maintained properly as well. Without this we sometimes just swap PHI arguments. In particular we had # ivtmp.24_52 = PHI <ivtmp.24_49(10), 1(6)> on bb 8 (dest) and edge_false is the 10->8 edge. We create a new BB_RTL bb 15 on this edge, redirect the 10->8 edge to 10->15 which does unordered_remove and so the bb8->preds edge vec is just 6->8, PHIs not touched as in IR_RTL_CFGRTL mode. Then a new 15->8 edge is created. Without the patch we get # ivtmp.24_52 = PHI <ivtmp.24_49(6), 1(15)> which is wrong, while with this patch we get # ivtmp.24_52 = PHI <ivtmp.24_49(15), 1(6)> which matches just the addition of (for ranger uninteresting) BB_RTL on the 10->15->8 edge. 2025-06-12 Jakub Jelinek <ja...@redhat.com> PR middle-end/120629 * cfgexpand.cc (expand_gimple_cond): If dest bb isn't BB_RTL, has any PHI nodes and false_edge->dest_idx before redirection is different from make_single_succ_edge result's dest_idx, swap the latter with the former last pred edge and their dest_idx members. * g++.dg/opt/pr120629.C: New test.