https://gcc.gnu.org/bugzilla/show_bug.cgi?id=3507
--- Comment #67 from Uroš Bizjak <ubizjak at gmail dot com> ---
(In reply to 75773-quiet from comment #0)
> unsigned long foo(unsigned long a, unsigned long b) {
> unsigned long c = a - b;
>
> if (a < b) {
> c += 100;
> }
>
> return c;
> }
We expand this testcase to:
(insn 7 4 8 2 (parallel [
(set (reg/v:DI 98 [ <retval> ])
(minus:DI (reg/v:DI 99 [ a ])
(reg/v:DI 100 [ b ])))
(clobber (reg:CC 17 flags))
]) "pr3507.c":2:16 395 {*subdi_1}
(nil))
(insn 8 7 9 2 (set (reg:CC 17 flags)
(compare:CC (reg/v:DI 99 [ a ])
(reg/v:DI 100 [ b ]))) "pr3507.c":4:5 16 {*cmpdi_1}
(nil))
(jump_insn 9 8 10 2 (set (pc)
(if_then_else (geu (reg:CC 17 flags)
(const_int 0 [0]))
(label_ref:DI 16)
(pc))) "pr3507.c":4:5 1490 {*jcc}
(int_list:REG_BR_PROB 536870913 (nil))
-> 16)
but since we have two-operand destructive operations, reload creates a copy, so
post-reload compare elimination pass gets:
28: ax:DI=di:DI
7: {ax:DI=ax:DI-si:DI;clobber flags:CC;}
30: dx:DI=ax:DI+0x64
23: flags:CC=cmp(di:DI,si:DI)
24: ax:DI={(ltu(flags:CC,0))?dx:DI:ax:DI}
where operands of (insn 7) and (insn 23) do not match anymore.
With three-operand non-destructive operations (RiscV, c.f. Comment#65 and x86
with -mapxf):
7: {ax:DI=di:DI-si:DI;clobber flags:CC;}
28: dx:DI=ax:DI+0x64
23: flags:CC=cmp(di:DI,si:DI)
24: ax:DI={(ltu(flags:CC,0))?dx:DI:ax:DI}
where post-reload compare elimination pass merges (insn 7) and (insn 23):
7: {flags:CC=cmp(di:DI,si:DI);ax:DI=di:DI-si:DI;}
28: dx:DI=ax:DI+0x64
24: ax:DI={(ltu(flags:CC,0))?dx:DI:ax:DI}
resulting in:
subq %rsi, %rdi, %rax
leaq 100(%rax), %rdx
cmovb %rdx, %rax
ret
So, the solution is to generalize CMP elimination pass to also handle pseudos
and put the pass before RTL ifcvt pass (_.ce1). ifcvt can create sequence that
clobbers flags between subtract and compare:
7: {r98:DI=r99:DI-r100:DI;clobber flags:CC;}
REG_UNUSED flags:CC
21: r103:DI=r98:DI
22: {r102:DI=r98:DI+0x64;clobber flags:CC;}
23: flags:CC=cmp(r99:DI,r100:DI)