On 5/4/2026 7:16 AM, Daniel Henrique Barboza wrote:

So I was looking at a potential RISC-V test regression that would be related to this patch (ps: it isn't) and then I noticed that the RISC-V code being
generated got worse with this patch.

Consider this test:

_Bool f1(long a)
{
    long b = a << 4;
    return b == -128;
}

Generated RISC-V code from trunk:

f1:
.LFB0:
        .cfi_startproc
        slli    a0,a0,4 # 6     [c=4 l=4]  ashldi3
        addi    a0,a0,128       # 7     [c=4 l=4]  *adddi3/1
        seqz    a0,a0   # 16    [c=4 l=4]  *seq_zero_didi
        ret
That's reasonable code generation.  We certainly don't want to go backwards, particularly when ints and longs are more common than char/short in C/C++ code.  I don't offhand see a way to improve on that sequence.

We can think of masking those upper 4 bits as a shift pair.  Shift left to wipe the bits, then shift right logical to put the remaining bits back in position.  That looks like this in RTL:

(insn 6 5 7 (set (reg:DI 139)
        (ashift:DI (reg/v:DI 136 [ a ])
            (const_int 4 [0x4]))) "j.c":4:14 -1
     (nil))

(insn 7 6 8 (set (reg:DI 138 [ _4 ])
        (lshiftrt:DI (reg:DI 139)
            (const_int 4 [0x4]))) "j.c":4:14 -1
     (expr_list:REG_EQUAL (and:DI (reg/v:DI 136 [ a ])
            (const_int 1152921504606846975 [0xfffffffffffffff]))
        (nil)))
That's basically what I'd expect to see.  I realize that as long as it stays in that form, we're going to generate worse code.  But I'm really just looking to see if the building blocks look correct.

The equality test against -128 gets implemented like this:

(insn 8 7 9 (set (reg:DI 144)
        (const_int 1152921504606846976 [0x1000000000000000])) "j.c":4:14 -1
     (nil))

(insn 9 8 10 (set (reg:DI 143)
        (plus:DI (reg:DI 144)
            (const_int -8 [0xfffffffffffffff8]))) "j.c":4:14 -1
     (expr_list:REG_EQUAL (const_int 1152921504606846968 [0xffffffffffffff8])
        (nil)))

(insn 10 9 11 (set (reg:DI 142)
        (minus:DI (reg:DI 138 [ _4 ])
            (reg:DI 143))) "j.c":4:14 -1
     (expr_list:REG_EQUAL (plus:DI (reg:DI 138 [ _4 ])
            (const_int -1152921504606846968 [0xf000000000000008]))
        (nil)))

(insn 11 10 12 (set (reg:DI 141)
        (eq:DI (reg:DI 142)
            (const_int 0 [0]))) "j.c":4:14 -1
     (nil))
Ugh.  Yea, I guess that's the code generation we'd get.  I don't like it, but I can see how we got there.

The RTL simplifiers eventually are able to clean all that up and recover this:

(set (reg:DI 141)
    (eq:DI (reg:DI 139)
        (const_int -128 [0xffffffffffffff80])))

Of course we don't have an instruction with those precise semantics, but it's just a 2 instruction sequence.  So we could reconstitute the desired form using a define_split.   In fact, I've pondered having a define_split for precisely this kind of scenario before, I just wasn't sure if we were reasonably likely to see that RTL as a result of a 3->2 or 4->2 combination.  A quick and dirty splitter does seem to work.

Let me ponder this a bit more.

jeff

Reply via email to