On 5/4/2026 7:16 AM, Daniel Henrique Barboza wrote:
[ Snip]
If we follow this same logic, for the example I talked about earlier for
a 64 bit 'long' var:
_Bool f1(long a)
{
long b = a << 4;
return b == -128;
}
So with the recent patch to the RISC-V port we won't regress if this
patch is applied. However, as I feared after looking at things in depth
yesterday, we will regress cris and likely other targets.
Before this change we'd get something like this on cris-elf:
lslq 4,$r10
cmps.b -128,$r10
ret
seq $r10
Guessing that seq is in a delay slot, so we should conceptually think of
it as executing before the ret. Reasonable looking code. After the
patch for 124019 we'd generate this instead:
lslq 4,$r10 ;# 26 [c=4 l=2] ashlsi3
lsrq 4,$r10 ;# 27 [c=4 l=2] lshrsi3
cmp.d 268435448,$r10 ;# 18 [c=8 l=2] *cmpsicc/4
ret ;# 22 [c=0 l=2] *return_expanded
seq $r10 ;# 19 [c=4 l=2] *seqcc
That's worse on at least one and possibly two axis. THere's the
additional shift and I suspect that cmp.d is slower and/or encodes worse
than the cmp.b we generated before.
To recover on cris we'd need to match this kind of pattern:
(set (reg/i:QI 10 r10)
(eq:QI (and:SI (reg:SI 33 [ a ])
(const_int 268435455 [0xfffffff]))
(const_int 268435448 [0xffffff8])))
(clobber (reg:CC 19 ccr))
The trick being if we shift that second constant by 4 positions left,
the result is an easily encodable -128 and shifting reg:SI 33 left by 4
would cancel out insn 27. But more importantly I suspect we'd end up
playing wack-a-mole on a port-by-port basis. So while we've made
RISC-V immune to this problem and I can see a path to fix cris, I
suspect we'd end up having to fix a ton of ports.
So I think the transformation we want probably can't be done in gimple,
at least not without testing and fixing many of the 30+ ISA targets we
support.
So if we return to thinking about this as an RTL transformation I keep
thinking we'll want to focus on this insn:
Trying 6, 8, 12 -> 13:
6: r139:DI=r148:DI<<0x4
REG_DEAD r148:DI
8: r141:DI=sign_extend(r139:DI#0)
REG_DEAD r139:DI
12: r145:DI=r141:DI+0x80
REG_DEAD r141:DI
13: r144:DI=r145:DI==0
REG_DEAD r145:DI
Failed to match this instruction:
(set (reg:DI 144)
(eq:DI (subreg:QI (ashift:DI (reg:DI 148 [ a ])
(const_int 4 [0x4])) 0)
(const_int -128 [0xffffffffffffff80])))
We'd have to implement this as a 4->3 splitter. We'd be taking
advantage of the fact that we've got a subreg:QI wrapping one operand,
so the bits outside QI don't matter. So we could andi+addi+seq. The
mask would be 0xf since only the low 4 bits matter and the addi constant
would be 8.
I don't like the define_insn_and_split, but I don't see anything better.
Something like this:
;; So the idea here is to realize that with a single insn we
;; can mask off all the relevant bits in the source operand.
;; A second insn generates zero/non-zero
;; The third and final insn canonicalizes that result to 0/1.
;;
;; There's probably some HImode cases we could handle too. I haven't
;; thought hard about them.
(define_insn_and_split ""
[(set (match_operand:X 0 "register_operand" "=r")
(any_eq:X (subreg:QI
(ashift:X (match_operand:X 1 "register_operand" "r")
(match_operand 2 "const_int_operand")) 0)
(match_operand 3 "const_int_operand")))]
"(INTVAL (operands[2]) > 0 && INTVAL (operands[2]) < 8
&& INTVAL (operands[3]) >= -128 && INTVAL (operands[2]) <= 127
&& (INTVAL (operands[3])
& ((HOST_WIDE_INT_1U << INTVAL (operands[2])) - 1)) == 0)"
"#"
"&& 1"
[(set (match_dup 0) (and:X (match_dup 1) (match_dup 2)))
(set (match_dup 0) (plus:X (match_dup 0) (match_dup 3)))
(set (match_dup 0) (any_eq:X (match_dup 0) (const_int 0)))]
{
operands[3] = GEN_INT (-(INTVAL (operands[3]) >> INTVAL (operands[2])));
operands[2] = GEN_INT (0xff >> INTVAL (operands[2]));
}
[(set_attr "type" "slt")
(set_attr "mode" "<X:MODE>")])
Jeff