Provide a definition for 'shl nsw' which makes it compatible with 'mul nsw'.
Diff Detail
Event Timeline
Just to be clear, you're explicitly making shl nsw X Y be poison less often than mul nsw X (1<<Y)? The example I have in mind is (for i8) X = -1, Y = 7. If so, that might be worth stating in the LangRef, maybe even with an example or two.
Er, the idea is that they will be poison at exactly the same rate. mul nsw i8 -1, 128 is poison because 128 can't be represented in i8. shl nsw i8 -1, 7 should be poison as well. I see the typo you are getting at, "it shifts out any non-sign bits" is wrong. I'll try to rephrase it so it is correct.
Also, I'd vote for just directly stating that "X << Y" sign overflows if and only if "X * (1 << Y)" sign overflows. What it actually implies for shifting out sign bits etc. should be stated as a corollary.
docs/LangRef.rst | ||
---|---|---|
5061–5062 | I'd prefer just removing this, and defining nuw directly, like nsw. | |
5063 | Should be `nsw` here. | |
5064 | I'd use "if and only if" here. | |
5080 | Should we add an example of a poison-value creating shl? | |
lib/Analysis/InstructionSimplify.cpp | ||
2303 | Is this correct for i1? | |
test/Transforms/InstCombine/cast.ll | ||
783 | Why do we need to change this test? |
FWIW, I really liked having a definition explicit in terms of what kinds of bits can be shifted out. I think its good to *both* define this from first principles of where bits move in the shift operation, *and* how it should relate to an NSW multiply by a power of two.
Unsure when after when we chatted this wording went away, but I'd really like to keep it.
- Address Sanjoy's review comments.
- Let the wording do the right thing for -1.
- Address Sanjoy's latest review comments.
The explicit definition gets hairy very quickly. It didn't correctly handle shl nsw i32 1073741824, 1 or shl nsw i32 -1, 31.
I'm fine keeping the sign-bit based definition as a corollary,
especially if we can prove and list the edge cases.
I don't think that _is_ the first-principles definition though, given
that we're adding exceptions to it just so that multiplication based
definition works.
The version we looked at we specifically had handle -1. I don't think it is hard to make it handle (1 << 30).
Not the first-principles *motivation*. I'm not suggesting that the bit-math is what is the primary motivation or the most important consequences. I'm suggesting that it doesn't rely on reasoning about anything other than the basic bit shifting operation, and I find it very nice to define in those terms as the basis, and merely state the equivalence that is intended (and motivating that particular definition). We can (and probably should!) even call out that this *is* a strange definition but that it is specifically chosen to establish this equivalence.
One way to phrase the overflow behavior in terms of bits shifted out
is:
T << S as N-bit integers sign-overflows if and only if
a. S != N and the bits shifted out are not all equal to the sign bit of (T << S). b. S == N and the bits shifted out are not all zero.
[(a) is the definition of sign-overflow as in the LangRef today. (b)
is LangRef's definition for unsigned-wrap.]
(b) is equivalent to "S == N and T != 1".
We can now prove that "T << S" sign-overflows iff "T * (1 << S)"
sign-overflows:
If S != N:
Sext(T * (1 << S)) == Sext(T) * Sext(1 << S)
<=> Sext(T * (1 << S)) == Sext(T) * Zext(1 << S)
<=> Sext(T * (1 << S)) == Sext(T) << S -- (A)
<=> Sext(T << S) == Sext(T) << S
<=> (a)
if S == N:
Sext(T * (1 << S)) == Sext(T) * Sext(1 << S)
<=> Sext(T * (1 << N)) == Sext(T) * Sext(1 << N)
<=> T == 1
<=> (b)
For (A) we use the identity "Sext(T) << S == Sext(T) * Zext(1 << S)".
This really is the interesting difference between shifting and
multiplication -- roughly speaking, shifting always treats its second
argument as unsigned. More precisely, if we define a "half-nsw
multiplication", one where "sext(A *hnsw B) == sext(A) * zext(B)" then
the current definition of an "shl nsw A B" in the LangRef is
equivalent to "A *hnsw B".
- Sanjoy
the current definition of an "shl nsw A B" in the LangRef is
equivalent to "A *hnsw B".
Um, should be 'equivalent to "A *hnsw (1 << B)"'.
- Sanjoy
Resigning so that this does not show up in my pending reviews list. Feel free to add me back if this change is being revived.
I'd prefer just removing this, and defining nuw directly, like nsw.