This is a preparatory cleanup before i add more

of this fold to deal with comparisons with non-zero.

In essence, the current lowering is:

Name: (X % C1) == 0 -> X * C3 <= C4 Pre: (C1 u>> countTrailingZeros(C1)) * C3 == 1 %zz = and i8 C3, 0 ; trick alive into making C3 avaliable in precondition %o0 = urem i8 %x, C1 %r = icmp eq i8 %o0, 0 => %zz = and i8 C3, 0 ; and silence it from complaining about said reg %C4 = -1 /u C1 %n0 = mul i8 %x, C3 %n1 = lshr i8 %n0, countTrailingZeros(C1) ; rotate right %n2 = shl i8 %n0, ((8-countTrailingZeros(C1)) %u 8) ; rotate right %n3 = or i8 %n1, %n2 ; rotate right %r = icmp ule i8 %n3, %C4

https://rise4fun.com/Alive/oqd

It kinda just works, really no weird edge-cases.

But it isn't all that great for when comparing with non-zero.

In particular, given `(X % C1) == C2`, there will be problems

in the always-false tautological case where `C2 u>= C1`:

https://rise4fun.com/Alive/pH3

That case is tautological, always-false:

Name: (X % Y) u>= Y %o0 = urem i8 %x, %y %r = icmp uge i8 %o0, %y => %r = false

https://rise4fun.com/Alive/ofu

While we can't/shouldn't get such tautological case normally,

we do deal with non-splat vectors, so unless we want to give up

in this case, we need to fixup/short-circuit such lanes.

There are two lowering variants:

- We can blend between whatever computed result and the correct tautological result

Name: (X % C1) == C2 -> X * C3 <= C4 || false Pre: (C2 == 0 || C1 u<= C2) && (C1 u>> countTrailingZeros(C1)) * C3 == 1 %zz = and i8 C3, 0 ; trick alive into making C3 avaliable in precondition %o0 = urem i8 %x, C1 %r = icmp eq i8 %o0, C2 => %zz = and i8 C3, 0 ; and silence it from complaining about said reg %C4 = -1 /u C1 %n0 = mul i8 %x, C3 %n1 = lshr i8 %n0, countTrailingZeros(C1) ; rotate right %n2 = shl i8 %n0, ((8-countTrailingZeros(C1)) %u 8) ; rotate right %n3 = or i8 %n1, %n2 ; rotate right %is_tautologically_false = icmp ule i8 C1, C2 %res = icmp ule i8 %n3, %C4 %r = select i1 %is_tautologically_false, i1 0, i1 %res

https://rise4fun.com/Alive/PjT5

https://rise4fun.com/Alive/1KV

- We can invert the comparison result

Name: (X % C1) == C2 -> X * C3 <= C4 || false Pre: (C2 == 0 || C1 u<= C2) && (C1 u>> countTrailingZeros(C1)) * C3 == 1 %zz = and i8 C3, 0 ; trick alive into making C3 avaliable in precondition %o0 = urem i8 %x, C1 %r = icmp eq i8 %o0, C2 => %zz = and i8 C3, 0 ; and silence it from complaining about said reg %C4 = -1 /u C1 %n0 = mul i8 %x, C3 %n1 = lshr i8 %n0, countTrailingZeros(C1) ; rotate right %n2 = shl i8 %n0, ((8-countTrailingZeros(C1)) %u 8) ; rotate right %n3 = or i8 %n1, %n2 ; rotate right %is_tautologically_false = icmp ule i8 C1, C2 %C4_fixed = select i1 %is_tautologically_false, i8 -1, i8 %C4 %res = icmp ule i8 %n3, %C4_fixed %r = xor i1 %res, %is_tautologically_false

https://rise4fun.com/Alive/2xC

https://rise4fun.com/Alive/jpb5

- We can expand into
`and`/`or`:

https://rise4fun.com/Alive/WGn

https://rise4fun.com/Alive/lcb5

Blend-one is likely better since we avoid having to load the

replacement from constant pool. `xor` is second best since

it's still pretty general. I'm not adding `and`/`or` variants.