Why no NSW/NUW with SHL instruction?

I’m curious why there is not always a nsw/nuw flag with shl instruction? For example, I have a signed int I’m shifting left (want to generate an arithmetic left shift in backend for signed int shift lefts and logical for unsigned, at the moment)… but shl on a signed int does not always have nsw/nuw flag present, why is this?

Thank you.

What is the difference between logical and arithmetic left shift on your target? LLVM only makes that distinction for right shift, and not for left shift.

There is a difference in the backend, whether the overflow flag should be thrown or not. We would like to produce a signed arithmetic left shift for an int that is signed in C (ie signed int).

for example, a small test case:

signed x = -2;
unsigned y =1;

int foo() {
x = x << y;
return x;
}

Thank you.

So again, I’m not sure why this is the case. I know this has probably been discussed ad nauseam (as evidenced by the multiple posts asking about this, including mine), but it seems pretty clear cut that you’d want separate shl, one for logical (non-overflow) and one for arithmetic (overflow). In fact, you might want a logical, signed arith and unsigned arith, at least in the hardware.

I don’t care if clang only produces shl, I’m more curious how to get the signed info to the backend for shl when it’s not producing a nsw/nuw flag? I’m currently not sure how this would be done, is there another arch that manages this somehow?

Thanks.

I think the point is that the only difference between the two is the overflow flag, which is not something that C exposes, and LLVM IR tends to be driven by the needs of the front ends.

If you wanted an IR instruction that represented a left shift that set a flag, then you'd need something that returned both the result and the overflow flag (look at the existing overflow checked addition intrinsics for inspiration). This sort of thing tends only to be added when someone needs it. If the first someone to need it is you, then... patches welcome. I think that you'd probably want to implement it as a shift-with-overflow intrinsic rather than a variation on the shl instruction and you'd probably need to provide some expansion for it for processors that don't have a direct mapping.

David

LLVM defines left shift overflow to produce an undefined value, so I don't
think it matters which machine instruction you use to implement shl, as
long as it doesn't trap. The user can't rely on the result being any thing
in particular. You should be able to just always emit logical or arithmetic
left shift and the program will behave correctly.

I think this is kind of like asking what to do with the 32 high bits in edx
after 32-bit multiplication on x86. LLVM doesn't define a way to get at
them, so you can just drop it on the floor. LLVM doesn't define what
happens on shl overflow, so you can produce any result you like.

So why in some cases does it still produce the nsw/nuw flag? Just curious.

We are going with intrinsics, but I’m still curious. Thanks.

So why in some cases does it still produce the nsw/nuw flag? Just curious.

Let's say you had:
%mul = mul nsw i32 %V, 2

This can safely be transformed into:
%mul = shl nsw i32 %V, 1

However, let's take the example:
%mul = mul nsw i32 %V, -2147483648

We cannot transform this into:
%mul = shl nsw i32 %V, 31

If we did this, then we would have created a poison value where non
previously existed.
This is because when %V is 1, 1 * -2147483648 is just -2147483648 but 1 <<
31 violates NSW.

Instead, we end up with:
%mul = shl i32 %V, 31

I don't think it's correct to see 'shl nsw' as an arithmetic left-shift and
'shl nuw' as a logical shift.
Where would that leave 'shl nsw nuw' ?

Finally, nsw and nuw flags are permitted to be ignored or otherwise dropped
when optimizing or performing analysis.

So is that the argument for adding an ashl? :slight_smile: