> On 12 Aug 2026, at 19:02, Jeffrey Law <[email protected]> wrote: > > > > On 8/9/2026 1:25 PM, [email protected] wrote: >> From: Kyrylo Tkachov <[email protected]> >> >> 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. Condition codes are recognised by mode class, as in cc_in_cond, >> which covers ports that do not define TARGET_FLAGS_REGNUM. 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. >> Ok for trunk? >> Thanks, >> Kyrill >> >> gcc/ChangeLog: >> >> PR rtl-optimization/126501 >> PR rtl-optimization/126747 >> * ifcvt.cc (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]> > So the concern I have is the test for whether or not a register is a flags > register. My worry is targets which don't have flags registers, but > instead put them in GPRs. In that case we're going to get MODE_INT back, not > MODE_CC. Note that this is potentially meaningfully different than > cc_in_cond where we're taking the mode from an operand of a larger RTX. That > can well give us a hard register in a mode other than reg_raw_mode.
This is all a bit arcane stuff :) I’ve done a bit of digging and extended the CC register recording to handle the fixed registers advertised by the backends. I think that should do the right thing? Attached patch implements that. Thanks, Kyrill > > Jeff
0001-ifcvt-Do-not-clobber-a-live-condition-code-register-.patch
Description: 0001-ifcvt-Do-not-clobber-a-live-condition-code-register-.patch
