https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126501

--- Comment #3 from GCC Commits <cvs-commit at gcc dot gnu.org> ---
The master branch has been updated by Kyrylo Tkachov <[email protected]>:

https://gcc.gnu.org/g:eef42c68c7f66835747f82e4532a26d7a0f6bb98

commit r17-3556-geef42c68c7f66835747f82e4532a26d7a0f6bb98
Author: Kyrylo Tkachov <[email protected]>
Date:   Thu Jul 30 12:20:54 2026 +0200

    ifcvt: Do not clobber a live condition-code register [PR126501, PR126747]

    If-conversion emits its replacement sequence at the end of the test block.
    Expanding a conditional move there can require a fresh comparison, which
    writes a condition-code register.  end_ifcvt_sequence already rejects a
    sequence that would destroy the condition code tested by the branch, but
    cc_in_cond only reports that register when the branch reads it directly.
    A branch such as AArch64 CBZ tests a general register, so the guard was
    inert and an unrelated live condition code could be destroyed.

    For the testcase at -O2 on aarch64, late-combine sinks a cset into the
    join block, which leaves the flags live across the branch:

      bb2:  cmp   w3, 1        // sets cc
            cset  w4, ls
            cbz   w1, .L2      // does not touch cc
      bb5:  cinc  w2, w2, ls   // reads cc

    ce2 then if-converted bb3 and bb4 and inserted "cmp w1, 0" ahead of the
    branch, so cinc read the wrong flags:

            cmp   w3, 1
            cset  w4, ls
            cmp   w1, 0        // clobbers the live flags
            csel  w2, w2, w3, eq
            cinc  w2, w2, ls   // reads cmp w1, 0

    rtl.h documents that ports in the "lowered" form, which includes aarch64
    before register allocation, may keep the flags live between instructions,
    so if-conversion has to respect that.  Reject a generated sequence that
    writes a condition-code register while it is live on exit from the test
    block.  Recognise condition-code registers from the mode of each store
    destination and from the target's fixed flags and condition-code register
    hooks.  The destination mode preserves condition-code semantics that
    reg_raw_mode can lose when a hard register also holds integers.  The hooks
    cover condition state that is itself represented in an integer mode.  DF
    liveness is already up to date here and the same paths query it for
pseudos.

    noce_convert_multiple_sets validates its sequence itself rather than
    through end_ifcvt_sequence, and reaches noce_emit_cmove in the same way,
    so it gets the same check.

    PR126747 is the same defect reached from a different direction.  At -Os the
    multiplication overflow idiom becomes one .MUL_OVERFLOW, so both arms read
a
    single cset, and late-combine folds it into the second one:

      bb2:  cmp   xzr, x0, lsr 32   // sets cc
            cset  w3, ne
            cbz   w0, .L4           // does not touch cc
      bb5:  cinc  w0, w0, ne        // reads cc

    ce2 if-converted bb3 the same way and "cmp w0, 0" landed ahead of the cinc,
so
    foo (1, 1) returned 1 instead of 0.

    Bootstrapped and tested on aarch64-none-linux-gnu.

    gcc/ChangeLog:

            PR rtl-optimization/126501
            PR rtl-optimization/126747
            * ifcvt.cc (noce_cc_reg_set): New struct.
            (noce_record_cc_reg_set): New function.
            (noce_clobbers_live_cc_p): New function.
            (end_ifcvt_sequence): Use it to reject sequences that clobber a
            condition-code register that is live out of the test block.
            (noce_convert_multiple_sets): Likewise.

    gcc/testsuite/ChangeLog:

            PR rtl-optimization/126501
            PR rtl-optimization/126747
            * gcc.c-torture/execute/pr126501.c: New test.
            * gcc.c-torture/execute/pr126747.c: New test.

    Signed-off-by: Kyrylo Tkachov <[email protected]>
  • [Bug rtl-optimization/126501] [... cvs-commit at gcc dot gnu.org via Gcc-bugs

Reply via email to