This is an archive of the discontinued LLVM Phabricator instance.

[IR] Fix the definition of 'shl nsw'
Needs ReviewPublic

Authored by majnemer on Apr 7 2015, 9:53 PM.

Details

Summary

Provide a definition for 'shl nsw' which makes it compatible with 'mul nsw'.

Diff Detail

Event Timeline

majnemer updated this revision to Diff 23396.Apr 7 2015, 9:53 PM
majnemer retitled this revision from to [IR] Fix the definition of 'shl nsw'.
majnemer updated this object.
majnemer added reviewers: rsmith, nicholas, nlewycky, sanjoy.
majnemer added a subscriber: Unknown Object (MLST).
sanjoy edited edge metadata.Apr 7 2015, 10:10 PM

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.

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.

majnemer updated this revision to Diff 23399.Apr 7 2015, 11:00 PM
majnemer edited edge metadata.
  • Address Sanjoy's review comments.
majnemer updated this revision to Diff 23400.Apr 7 2015, 11:06 PM
  • Let the wording do the right thing for -1.
sanjoy added inline comments.Apr 7 2015, 11:21 PM
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.

majnemer added inline comments.Apr 7 2015, 11:44 PM
docs/LangRef.rst
5061–5062

Done.

5063

Done.

5064

Done.

lib/Analysis/InstructionSimplify.cpp
2303

i1 is handled earlier. I will add an assert to make that clear.

test/Transforms/InstCombine/cast.ll
783

I am changing these tests to as they were back in r219566.

majnemer updated this revision to Diff 23403.Apr 7 2015, 11:44 PM
  • Address Sanjoy's review comments.
  • Let the wording do the right thing for -1.
  • Address Sanjoy's latest review comments.

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.

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.

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.

The explicit definition gets hairy very quickly. It didn't correctly handle shl nsw i32 1073741824, 1 or shl nsw i32 -1, 31.

The version we looked at we specifically had handle -1. I don't think it is hard to make it handle (1 << 30).

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.

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.

sanjoy added a comment.Apr 8 2015, 2:39 AM

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
sanjoy added a comment.Apr 8 2015, 6:42 PM

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

Is this abandoned?

sanjoy resigned from this revision.Jun 22 2015, 10:25 AM
sanjoy removed a reviewer: 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.