Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[InstCombine] Miscompile of (x & (~(-1 << x))) << x #49122

Closed
meheff mannequin opened this issue Mar 30, 2021 · 14 comments
Closed

[InstCombine] Miscompile of (x & (~(-1 << x))) << x #49122

meheff mannequin opened this issue Mar 30, 2021 · 14 comments
Assignees
Labels
bugzilla Issues migrated from bugzilla

Comments

@meheff
Copy link
Mannequin

meheff mannequin commented Mar 30, 2021

Bugzilla Link 49778
Resolution FIXED
Resolved on May 03, 2021 16:55
Version trunk
OS All
Blocks #48246 #48661
CC @LebedevRI,@RKSimon,@rotateright,@tstellar
Fixed by commit(s) 5352490 dceb3e5 2760a80 907a751 4a4b1c7 c27ad80

Extended Description

This expression is miscompiled in the case where x is zext of a single bit value. Here's before and after from a single instcombine transform extracted using debug_counter and fed to Alive:


define i32 @​src(i1 %x2) {
%0:
%x13 = zext i1 %x2 to i32
%_7 = shl i32 4294967295, %x13
%mask = xor i32 %_7, 4294967295
%_8 = and i32 %mask, %x13
%_9 = shl i32 %_8, %x13
ret i32 %_9
}
=>
define i32 @​tgt(i1 %x2) {
%0:
%x13 = zext i1 %x2 to i32
%1 = shl i32 %x13, %x13
%_9 = and i32 %1, 0
ret i32 %_9
}
Transformation doesn't verify!
ERROR: Value mismatch

Example:
i1 %x2 = #x1 (1)

Source:
i32 %x13 = #x00000001 (1)
i32 %_7 = #xfffffffe (4294967294, -2)
i32 %mask = #x00000001 (1)
i32 %_8 = #x00000001 (1)
i32 %_9 = #x00000002 (2)

Target:
i32 %x13 = #x00000001 (1)
i32 %1 = #x00000002 (2)
i32 %_9 = #x00000000 (0)
Source value: #x00000002 (2)
Target value: #x00000000 (0)

I suspect the problematic transform is this one:

// b) (x & (~(-1 << MaskShAmt))) << ShiftShAmt

Looks like maybe(?) NewMask is incorrectly being computed as zero:

return BinaryOperator::Create(Instruction::And, NewShift, NewMask);

@meheff
Copy link
Mannequin Author

meheff mannequin commented Mar 30, 2021

assigned to @LebedevRI

@LebedevRI
Copy link
Member

Does not repro on main: https://godbolt.org/z/zdfbEKns9

@meheff
Copy link
Mannequin Author

meheff mannequin commented Mar 30, 2021

Does not repro on main: https://godbolt.org/z/zdfbEKns9

Looks like the link you sent shows that it does repro? The optimized version is unconditionally zero. It shouldn't be.

@meheff
Copy link
Mannequin Author

meheff mannequin commented Mar 31, 2021

Just to clarify my earlier comment, the posted code snippet can conceivably be simplified to:

%x13 = zext i1 %x2 to i32
%result = shl i32 %x13, %x13
ret i32 %result

Which looks like what that instcombine transformation might be trying to accomplish. The truth table for this is:

f(0) => 0
f(1) => 2

However, it is being replaced with zero by the optimizer because of the addition of an AND with zero.

@meheff
Copy link
Mannequin Author

meheff mannequin commented Apr 2, 2021

I think the bug lies here:

auto *SumOfShAmts = dyn_cast_or_null(SimplifyAddInst(
MaskShAmt, ShiftShAmt, /IsNSW=/false, /IsNUW=/false, Q));

auto *SumOfShAmts = dyn_cast_or_null<Constant>(SimplifyAddInst(

It's trying to simplify the sum of the two shift values (MaskShAmt and ShiftShAmt) in the expression:

(x & (~(-1 << MaskShAmt))) << ShiftShAmt

Where the code again is:

%x13 = zext i1 %x2 to i32
%_7 = shl i32 4294967295, %x13
%mask = xor i32 %_7, 4294967295
%_8 = and i32 %mask, %x13
%_9 = shl i32 %_8, %x13

In this case both MaskShAmt and ShiftShAmt in the expression above are i1 %x2 (the matching looks through zexts). So, of course, it can simplify i1 %x2 + %x2 to zero, but that is incorrect for the purposes of the transformation because it overflows the type i1 which results in the wrong mask being generated.

@meheff
Copy link
Mannequin Author

meheff mannequin commented Apr 2, 2021

fwiw, a C repro:

#include <stdio.h>
#include <stdbool.h>

unsigned int f(bool b) {
unsigned int ub = b;
return (ub & (~((unsigned int) -1 << ub))) << ub;
}

int main(int argc, char** argv) {
printf("f(0) = %d\n", f(0));
printf("f(1) = %d\n", f(1));
return 0;
}

$ clang -O2 foo.c && ./a.out
f(0) = 0
f(1) = 0
$ gcc -O2 foo.c && ./a.out
f(0) = 0
f(1) = 2

https://godbolt.org/z/jT1sKa1rq
https://godbolt.org/z/v1j1TMzv7

@rotateright
Copy link
Contributor

I'm not sure if this would be considered a legitimate/complete fix, but we are missing this simplification:
https://alive2.llvm.org/ce/z/bBuyks

define i32 @​src(i1 %b) {
%z = zext i1 %b to i32
%s1 = shl i32 -1, %z
%x1 = xor i32 %s1, -1
ret i32 %x1
}

define i32 @​tgt(i1 %b) {
%z = zext i1 %b to i32
ret i32 %z
}

If we had that, it might guarantee that the bug in instcombine is covered up?

@rotateright
Copy link
Contributor

If the only known problem case is with a bool type, there's a 1-line fix?
We can deal with the missed optimization in another patch.

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp
index 2007cf0bbc9c..eba29e8e24e1 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp
@@ -222,7 +222,8 @@ dropRedundantMaskingOfLeftShiftInput(BinaryOperator *OuterShift,

 // We have two shift amounts from two different shifts. The types of those
 // shift amounts may not match. If that's the case let's bailout now.
  • if (MaskShAmt->getType() != ShiftShAmt->getType())
  • if (MaskShAmt->getType() != ShiftShAmt->getType() ||

  •    MaskShAmt->getType()->getScalarSizeInBits() == 1)
     return nullptr;
    

    // Can we simplify (MaskShAmt+ShiftShAmt) ?

@LebedevRI
Copy link
Member

The fix would consist of copypasting 781d077.

@LebedevRI
Copy link
Member

Sorry for the delay, fixed.
This should probably be backported.

@meheff
Copy link
Mannequin Author

meheff mannequin commented Apr 7, 2021

Thanks for the quick fix!

@tstellar
Copy link
Collaborator

tstellar commented May 1, 2021

There are 3 commits listed in the "Fixed By Commits" field. Do these all need to be backported?

@LebedevRI
Copy link
Member

There are 3 commits listed in the "Fixed By Commits" field. Do these all
need to be backported?

Yep

@tstellar
Copy link
Collaborator

tstellar commented May 3, 2021

Merged: c27ad80

@llvmbot llvmbot transferred this issue from llvm/llvm-bugzilla-archive Dec 11, 2021
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugzilla Issues migrated from bugzilla
Projects
None yet
Development

No branches or pull requests

3 participants