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