[Bug rtl-optimization/3507] appalling optimisation with sub/cmp on multiple targets

ubizjak at gmail dot com via Gcc-bugs <[email protected]>
Newsgroups gmane.comp.gcc.bugs
Message-ID <[email protected]/bugzilla/>
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)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.