This is an archive of the discontinued LLVM Phabricator instance.

[InstCombine] decomposeSimpleLinearExpr should bail out on negative operands.
ClosedPublic

Authored by w2yehia on May 27 2022, 7:59 AM.

Details

Summary

InstCombine tries to rewrite

%prod = mul nsw i64 %X,   Scale
%acc = add nsw i64 %prod,   Offset
%0 = alloca i8, i64 %acc, align 4
%1 = bitcast i8* %0 to i32*
Use ( %1 )

into

%prod = mul nsw i64 %X,   Scale/4
%acc = add nsw i64 %prod,   Offset/4
%0 = alloca i32, i64 %acc, align 4
Use (%0)

But it assumes Scale is unsigned.

For now, bail out on negative operands to avoid an incorrect transformation.

Diff Detail

Event Timeline

w2yehia created this revision.May 27 2022, 7:59 AM
Herald added a project: Restricted Project. · View Herald TranscriptMay 27 2022, 7:59 AM
Herald added a subscriber: hiraditya. · View Herald Transcript
w2yehia requested review of this revision.May 27 2022, 7:59 AM
Herald added a project: Restricted Project. · View Herald TranscriptMay 27 2022, 7:59 AM
w2yehia updated this revision to Diff 432578.May 27 2022, 9:12 AM
This comment was removed by w2yehia.
w2yehia edited the summary of this revision. (Show Details)May 27 2022, 9:13 AM
nikic added a subscriber: nikic.
nikic added inline comments.
llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
40

Isn't the actual bug here? The && !OBI->hasNoSignedWrap() shouldn't be there. The number of elements is an unsigned value, so we should be checking for no unsigned wrap, not "either no unsigned or no signed wrap".

nikic retitled this revision from decomposeSimpleLinearExpr should bail out on negative operands. to [InstCombine] decomposeSimpleLinearExpr should bail out on negative operands..May 27 2022, 9:23 AM
w2yehia added inline comments.May 27 2022, 10:18 AM
llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
40

i'm not that familiar with the implications of (not) having nsw, so you are likely correct.
Does the presence of nsw indicate the operands of the multiplication are signed?

nikic added inline comments.May 30 2022, 3:52 AM
llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
40

nsw indicates that there is no signed overflow, while we need no unsigned overflow here.

w2yehia added inline comments.Jun 2 2022, 5:59 AM
llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
40

Thanks for reviewing. I had to read up more about nsw/nuw and signedness in llvm in general.
I couldn't conclude that the presence of nuw indicates that the operands of the mul can be safely interpreted as unsigned.
I might be asking the wrong question, so can you please tell me why checking for nuw is necessary and sufficient in order to make the code (in decomposeSimpleLinearExpr and PromoteCastOfAllocation) to be sound?

w2yehia added inline comments.
llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
40

@nikic I will implement your suggestion. I had to convince myself (and get educated on these flags)

After brainstorming with @sfertile about the implications of the nuw/nsw flags, we concluded that doing only the && !OBI->hasNoSignedWrap() is not a sufficient condition to safely interpret Scale (2nd) operand of the mul as unsigned in isolation.
Consider this pseudo-llvm where the -4 is intended to be signed:

if ( %X == 1 || %X == 0) {
  %0 = mul nuw i32 %X, -4   // 1 * 4294967292 = 4294967292 (no unsigned wrap)
  ..

However, because this exit condition applies to all visited instructions (add, mul, shl), having the nuw means you cannot having something like 24 - 4 because it unsigned overflows. So if we expand the above example:

if ( %X == 1 || %X == 0) {
  %0 = mul nuw i32 %X, -4   // 1 * 4294967292 = 4294967292 (no unsigned wrap)
  %1 = add nuw i32 %0, C    // 4294967292 + C
   alloca i8, %1

The addition 4294967292 + C must overflow in order to produce a positive signed number. If it does not overflow then the user-intended result is simply a large unsigned number and storing it in an unsigned is fine.

w2yehia added inline comments.Jun 6 2022, 6:27 AM
llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp
40

basically, if you have a complex expression involving add/mul/shl and one of the operands is negative, then in order to bring back the result to a positive value an unsigned overflow has to occur:

  1. add i32 -1, C // (unsigned) -1 + C must overflow
  2. mul i32 -1, C // (unsigned) -1 * C must overflow
  3. shl i32 -1, C // (unsigned) -1 << C must overflow
w2yehia updated this revision to Diff 434461.Jun 6 2022, 7:10 AM

As per review, remove !OBI->hasNoSignedWrap() from the early exit condition. Also the original fix is no longer needed so remove it as well.

nikic added inline comments.Jun 6 2022, 7:21 AM
llvm/test/Transforms/InstCombine/neg-alloca.ll
2

Please use the llvm/utils/update_test_checks.py script to generate CHECK lines.

Also, I think that this test case may be reduced, I don't think you really need much more than then mul+add+alloca+bitcast sequence?

w2yehia added inline comments.Jun 6 2022, 11:39 AM
llvm/test/Transforms/InstCombine/neg-alloca.ll
2

I reduced the testcase, and made the RUN command canonical (based on suggestions in update_test_checks.py).
I think the current CHECKs capture the required functionality best.

w2yehia updated this revision to Diff 434552.Jun 6 2022, 11:39 AM
w2yehia added inline comments.Jun 6 2022, 12:56 PM
llvm/test/Transforms/InstCombine/neg-alloca.ll
2

I think the current CHECKs capture the required functionality best.

Sorry wrote in a rush. I mean that we want the test not to be fragile (that's obvious) and so having too much CHECKs is not good. I think it's enough to CHECK that the negative operand remains and the alloca type is the same.

@nikic any chance you can review this today? thanks!

nikic added inline comments.Jun 7 2022, 7:12 AM
llvm/test/Transforms/InstCombine/neg-alloca.ll
2

We generally prefer to always use auto-generated check lines, even if they contain "unnecessary" instructions, because it makes it easier to update tests in the future.

w2yehia updated this revision to Diff 434866.Jun 7 2022, 10:21 AM

use update_test_checks.py to generate the CHECKs

w2yehia added inline comments.Jun 7 2022, 10:22 AM
llvm/test/Transforms/InstCombine/neg-alloca.ll
2

fair enough. Thanks.

nikic accepted this revision.Jun 7 2022, 2:26 PM

LGTM, though patch description needs an update.

This revision is now accepted and ready to land.Jun 7 2022, 2:26 PM
This revision was landed with ongoing or failed builds.Jun 7 2022, 5:58 PM
This revision was automatically updated to reflect the committed changes.