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.

Reply via email to