> 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


Attachment: 0001-ifcvt-Do-not-clobber-a-live-condition-code-register-.patch
Description: 0001-ifcvt-Do-not-clobber-a-live-condition-code-register-.patch

Reply via email to