Revert "[InstCombine] Fix #163110: Support peeling off matching shifts from icmp operands via canEvaluateShifted" (#190638)

Reverts llvm/llvm-project#165975

Breaks Sanitizer bots:
https://lab.llvm.org/buildbot/#/builders/52/builds/16329
This commit is contained in:
Kirill Stoimenov 2026-04-06 11:30:36 -07:00 committed by GitHub
parent 8d442bc5b5
commit cdbb1f5014
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 45 additions and 468 deletions

View File

@ -7698,34 +7698,6 @@ Instruction *InstCombinerImpl::foldICmpCommutative(CmpPredicate Pred,
}
}
// icmp (shl nsw/nuw X, L), (add nsw/nuw (shl nsw/nuw Y, L), K)
// -> icmp X, (add nsw/nuw Y, K >> L)
// We use AShr for nsw and LShr for nuw to safely peel off the shift.
Value *X;
uint64_t ShAmt;
if (match(Op0, m_NUWShl(m_Value(X), m_ConstantInt(ShAmt))) &&
!CxtI.isSigned()) {
if (ShAmt >= X->getType()->getScalarSizeInBits())
return nullptr;
if (canEvaluateShifted(Op1, ShAmt, /*IsLeftShift=*/false,
ShiftSemantics::Unsigned, &CxtI)) {
Value *NewOp1 = getShiftedValue(Op1, ShAmt, /*IsLeftShift=*/false,
ShiftSemantics::Unsigned);
return new ICmpInst(Pred, X, NewOp1);
}
}
if (match(Op0, m_NSWShl(m_Value(X), m_ConstantInt(ShAmt))) &&
!CxtI.isUnsigned()) {
if (ShAmt >= X->getType()->getScalarSizeInBits())
return nullptr;
if (canEvaluateShifted(Op1, ShAmt, /*IsLeftShift=*/false,
ShiftSemantics::Signed, &CxtI)) {
Value *NewOp1 = getShiftedValue(Op1, ShAmt, /*IsLeftShift=*/false,
ShiftSemantics::Signed);
return new ICmpInst(Pred, X, NewOp1);
}
}
return nullptr;
}

View File

@ -58,15 +58,6 @@ class ProfileSummaryInfo;
class TargetLibraryInfo;
class User;
/// Enum to specify how shift operations should be evaluated in
/// canEvaluateShifted.
/// Lossy: Allows lossy transformations
/// Signed: Requires lossless transformation, using ashr to restore for shl,
/// or represents ashr handling for right shifts
/// Unsigned: Requires lossless transformation, using lshr to restore for shl,
/// or represents lshr handling for right shifts
enum class ShiftSemantics { Lossy, Signed, Unsigned };
class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
: public InstCombiner,
public InstVisitor<InstCombinerImpl, Instruction *> {
@ -441,11 +432,6 @@ private:
bool InvertFalseVal = false);
Value *getSelectCondition(Value *A, Value *B, bool ABIsTheSame);
bool canEvaluateShifted(Value *V, unsigned NumBits, bool IsLeftShift,
ShiftSemantics Semantics, Instruction *CxtI);
Value *getShiftedValue(Value *V, unsigned NumBits, bool IsLeftShift,
ShiftSemantics Semantics);
Instruction *foldLShrOverflowBit(BinaryOperator &I);
Instruction *foldExtractOfOverflowIntrinsic(ExtractValueInst &EV);
Instruction *foldIntrinsicWithOverflowCommon(IntrinsicInst *II);

View File

@ -538,7 +538,6 @@ Instruction *InstCombinerImpl::commonShiftTransforms(BinaryOperator &I) {
/// Return true if we can simplify two logical (either left or right) shifts
/// that have constant shift amounts: OuterShift (InnerShift X, C1), C2.
static bool canEvaluateShiftedShift(unsigned OuterShAmt, bool IsOuterShl,
ShiftSemantics Semantics,
Instruction *InnerShift,
InstCombinerImpl &IC, Instruction *CxtI) {
assert(InnerShift->isLogicalShift() && "Unexpected instruction type");
@ -552,10 +551,6 @@ static bool canEvaluateShiftedShift(unsigned OuterShAmt, bool IsOuterShl,
// shl (shl X, C1), C2 --> shl X, C1 + C2
// lshr (lshr X, C1), C2 --> lshr X, C1 + C2
bool IsInnerShl = InnerShift->getOpcode() == Instruction::Shl;
if (!IsOuterShl && Semantics == ShiftSemantics::Signed)
return IsInnerShl && cast<BinaryOperator>(InnerShift)->hasNoSignedWrap() &&
*InnerShiftConst == OuterShAmt;
if (IsInnerShl == IsOuterShl)
return true;
@ -585,30 +580,20 @@ static bool canEvaluateShiftedShift(unsigned OuterShAmt, bool IsOuterShl,
}
/// See if we can compute the specified value, but shifted logically to the left
/// or right by some number of bits. This should return true if the
/// transformation is valid. If the Semantics is not lossy,
/// we must get the same value when we shift this value and then shift back.
/// This is used to eliminate extraneous shifting from things like:
/// or right by some number of bits. This should return true if the expression
/// can be computed for the same cost as the current expression tree. This is
/// used to eliminate extraneous shifting from things like:
/// %C = shl i128 %A, 64
/// %D = shl i128 %B, 96
/// %E = or i128 %C, %D
/// %F = lshr i128 %E, 64
/// where the client will ask if E can be computed shifted right by 64-bits. If
/// this succeeds, getShiftedValue() will be called to produce the value.
bool InstCombinerImpl::canEvaluateShifted(Value *V, unsigned NumBits,
bool IsLeftShift,
ShiftSemantics Semantics,
Instruction *CxtI) {
// We can always evaluate immediate constants shifted left. For right shifts,
// the constant must be a multiple of 2^NumBits to avoid losing information.
if (match(V, m_ImmConstant())) {
if (Semantics == ShiftSemantics::Lossy)
return true;
const APInt *C;
if (match(V, m_APIntAllowPoison(C)) && !IsLeftShift)
return C->countr_zero() >= NumBits;
return false;
}
static bool canEvaluateShifted(Value *V, unsigned NumBits, bool IsLeftShift,
InstCombinerImpl &IC, Instruction *CxtI) {
// We can always evaluate immediate constants.
if (match(V, m_ImmConstant()))
return true;
Instruction *I = dyn_cast<Instruction>(V);
if (!I) return false;
@ -623,22 +608,19 @@ bool InstCombinerImpl::canEvaluateShifted(Value *V, unsigned NumBits,
case Instruction::Or:
case Instruction::Xor:
// Bitwise operators can all arbitrarily be arbitrarily evaluated shifted.
return canEvaluateShifted(I->getOperand(0), NumBits, IsLeftShift, Semantics,
I) &&
canEvaluateShifted(I->getOperand(1), NumBits, IsLeftShift, Semantics,
I);
return canEvaluateShifted(I->getOperand(0), NumBits, IsLeftShift, IC, I) &&
canEvaluateShifted(I->getOperand(1), NumBits, IsLeftShift, IC, I);
case Instruction::Shl:
case Instruction::LShr:
return canEvaluateShiftedShift(NumBits, IsLeftShift, Semantics, I, *this,
CxtI);
return canEvaluateShiftedShift(NumBits, IsLeftShift, I, IC, CxtI);
case Instruction::Select: {
SelectInst *SI = cast<SelectInst>(I);
Value *TrueVal = SI->getTrueValue();
Value *FalseVal = SI->getFalseValue();
return canEvaluateShifted(TrueVal, NumBits, IsLeftShift, Semantics, SI) &&
canEvaluateShifted(FalseVal, NumBits, IsLeftShift, Semantics, SI);
return canEvaluateShifted(TrueVal, NumBits, IsLeftShift, IC, SI) &&
canEvaluateShifted(FalseVal, NumBits, IsLeftShift, IC, SI);
}
case Instruction::PHI: {
// We can change a phi if we can change all operands. Note that we never
@ -646,48 +628,23 @@ bool InstCombinerImpl::canEvaluateShifted(Value *V, unsigned NumBits,
// instructions with a single use.
PHINode *PN = cast<PHINode>(I);
for (Value *IncValue : PN->incoming_values())
if (!canEvaluateShifted(IncValue, NumBits, IsLeftShift, Semantics, PN))
if (!canEvaluateShifted(IncValue, NumBits, IsLeftShift, IC, PN))
return false;
return true;
}
case Instruction::Mul: {
const APInt *MulConst;
// We can fold (shr (mul X, -(1 << C)), C) -> (and (neg X), C`)
return !IsLeftShift && Semantics == ShiftSemantics::Unsigned &&
match(I->getOperand(1), m_APInt(MulConst)) &&
return !IsLeftShift && match(I->getOperand(1), m_APInt(MulConst)) &&
MulConst->isNegatedPowerOf2() && MulConst->countr_zero() == NumBits;
}
case Instruction::Add: {
auto *BinOp = cast<BinaryOperator>(I);
// Left shift case
if (IsLeftShift) {
if (Semantics == ShiftSemantics::Lossy)
return canEvaluateShifted(I->getOperand(0), NumBits, IsLeftShift,
Semantics, I) &&
canEvaluateShifted(I->getOperand(1), NumBits, IsLeftShift,
Semantics, I);
return false;
}
if (Semantics == ShiftSemantics::Lossy)
return false;
bool WrapRequired =
(Semantics == ShiftSemantics::Signed && BinOp->hasNoSignedWrap()) ||
(Semantics == ShiftSemantics::Unsigned && BinOp->hasNoUnsignedWrap());
return WrapRequired &&
canEvaluateShifted(I->getOperand(0), NumBits, IsLeftShift, Semantics,
I) &&
canEvaluateShifted(I->getOperand(1), NumBits, IsLeftShift, Semantics,
I);
}
}
}
/// Fold OuterShift (InnerShift X, C1), C2.
/// See canEvaluateShiftedShift() for the constraints on these instructions.
static Value *foldShiftedShift(BinaryOperator *InnerShift, unsigned OuterShAmt,
bool IsOuterShl, ShiftSemantics Semantics,
bool IsOuterShl,
InstCombiner::BuilderTy &Builder) {
bool IsInnerShl = InnerShift->getOpcode() == Instruction::Shl;
Type *ShType = InnerShift->getType();
@ -725,16 +682,6 @@ static Value *foldShiftedShift(BinaryOperator *InnerShift, unsigned OuterShAmt,
// lshr (shl X, C), C --> and X, C'
// shl (lshr X, C), C --> and X, C'
if (InnerShAmt == OuterShAmt) {
if (!IsOuterShl && Semantics == ShiftSemantics::Signed) {
assert(IsInnerShl && InnerShift->hasNoSignedWrap() &&
"Signed Semantics should have nsw and inner shl per "
"canEvaluateShiftedShift");
return InnerShift->getOperand(0);
}
if (!IsOuterShl && Semantics == ShiftSemantics::Unsigned && IsInnerShl &&
InnerShift->hasNoUnsignedWrap())
return InnerShift->getOperand(0);
APInt Mask = IsInnerShl
? APInt::getLowBitsSet(TypeWidth, TypeWidth - OuterShAmt)
: APInt::getHighBitsSet(TypeWidth, TypeWidth - OuterShAmt);
@ -759,21 +706,18 @@ static Value *foldShiftedShift(BinaryOperator *InnerShift, unsigned OuterShAmt,
/// When canEvaluateShifted() returns true for an expression, this function
/// inserts the new computation that produces the shifted value.
Value *InstCombinerImpl::getShiftedValue(Value *V, unsigned NumBits,
bool IsLeftShift,
ShiftSemantics Semantics) {
static Value *getShiftedValue(Value *V, unsigned NumBits, bool isLeftShift,
InstCombinerImpl &IC, const DataLayout &DL) {
// We can always evaluate constants shifted.
if (Constant *C = dyn_cast<Constant>(V)) {
Instruction::BinaryOps ShiftOp =
IsLeftShift ? Instruction::Shl
: (Semantics == ShiftSemantics::Signed ? Instruction::AShr
: Instruction::LShr);
return Builder.CreateBinOp(ShiftOp, C,
ConstantInt::get(C->getType(), NumBits));
if (isLeftShift)
return IC.Builder.CreateShl(C, NumBits);
else
return IC.Builder.CreateLShr(C, NumBits);
}
Instruction *I = cast<Instruction>(V);
addToWorklist(I);
IC.addToWorklist(I);
switch (I->getOpcode()) {
default: llvm_unreachable("Inconsistency with CanEvaluateShifted");
@ -782,21 +726,21 @@ Value *InstCombinerImpl::getShiftedValue(Value *V, unsigned NumBits,
case Instruction::Xor:
// Bitwise operators can all arbitrarily be arbitrarily evaluated shifted.
I->setOperand(
0, getShiftedValue(I->getOperand(0), NumBits, IsLeftShift, Semantics));
0, getShiftedValue(I->getOperand(0), NumBits, isLeftShift, IC, DL));
I->setOperand(
1, getShiftedValue(I->getOperand(1), NumBits, IsLeftShift, Semantics));
1, getShiftedValue(I->getOperand(1), NumBits, isLeftShift, IC, DL));
return I;
case Instruction::Shl:
case Instruction::LShr:
return foldShiftedShift(cast<BinaryOperator>(I), NumBits, IsLeftShift,
Semantics, Builder);
return foldShiftedShift(cast<BinaryOperator>(I), NumBits, isLeftShift,
IC.Builder);
case Instruction::Select:
I->setOperand(
1, getShiftedValue(I->getOperand(1), NumBits, IsLeftShift, Semantics));
1, getShiftedValue(I->getOperand(1), NumBits, isLeftShift, IC, DL));
I->setOperand(
2, getShiftedValue(I->getOperand(2), NumBits, IsLeftShift, Semantics));
2, getShiftedValue(I->getOperand(2), NumBits, isLeftShift, IC, DL));
return I;
case Instruction::PHI: {
// We can change a phi if we can change all operands. Note that we never
@ -805,28 +749,19 @@ Value *InstCombinerImpl::getShiftedValue(Value *V, unsigned NumBits,
PHINode *PN = cast<PHINode>(I);
for (unsigned i = 0, e = PN->getNumIncomingValues(); i != e; ++i)
PN->setIncomingValue(i, getShiftedValue(PN->getIncomingValue(i), NumBits,
IsLeftShift, Semantics));
isLeftShift, IC, DL));
return PN;
}
case Instruction::Mul: {
assert(!IsLeftShift && "Unexpected shift direction!");
assert(!isLeftShift && "Unexpected shift direction!");
auto *Neg = BinaryOperator::CreateNeg(I->getOperand(0));
InsertNewInstWith(Neg, I->getIterator());
IC.InsertNewInstWith(Neg, I->getIterator());
unsigned TypeWidth = I->getType()->getScalarSizeInBits();
APInt Mask = APInt::getLowBitsSet(TypeWidth, TypeWidth - NumBits);
auto *And = BinaryOperator::CreateAnd(Neg,
ConstantInt::get(I->getType(), Mask));
And->takeName(I);
return InsertNewInstWith(And, I->getIterator());
}
case Instruction::Add: {
if (IsLeftShift)
I->dropPoisonGeneratingFlags();
I->setOperand(
0, getShiftedValue(I->getOperand(0), NumBits, IsLeftShift, Semantics));
I->setOperand(
1, getShiftedValue(I->getOperand(1), NumBits, IsLeftShift, Semantics));
return I;
return IC.InsertNewInstWith(And, I->getIterator());
}
}
}
@ -897,20 +832,15 @@ Instruction *InstCombinerImpl::FoldShiftByConstant(Value *Op0, Constant *C1,
// See if we can propagate this shift into the input, this covers the trivial
// cast of lshr(shl(x,c1),c2) as well as other more complex cases.
if (I.getOpcode() != Instruction::AShr) {
bool IsLeftShift = I.getOpcode() == Instruction::Shl;
ShiftSemantics Semantics =
IsLeftShift ? ShiftSemantics::Lossy : ShiftSemantics::Unsigned;
if (canEvaluateShifted(Op0, Op1C->getZExtValue(), IsLeftShift, Semantics,
&I)) {
LLVM_DEBUG(
dbgs() << "ICE: GetShiftedValue propagating shift through expression"
" to eliminate shift:\n IN: "
<< *Op0 << "\n SH: " << I << "\n");
if (I.getOpcode() != Instruction::AShr &&
canEvaluateShifted(Op0, Op1C->getZExtValue(), IsLeftShift, *this, &I)) {
LLVM_DEBUG(
dbgs() << "ICE: GetShiftedValue propagating shift through expression"
" to eliminate shift:\n IN: "
<< *Op0 << "\n SH: " << I << "\n");
return replaceInstUsesWith(I, getShiftedValue(Op0, Op1C->getZExtValue(),
IsLeftShift, Semantics));
}
return replaceInstUsesWith(
I, getShiftedValue(Op0, Op1C->getZExtValue(), IsLeftShift, *this, DL));
}
if (Instruction *FoldedShift = foldBinOpIntoSelectOrPhi(I))

View File

@ -538,9 +538,9 @@ define <2 x i43> @lshr_shl_eq_amt_multi_use_splat_vec(<2 x i43> %A) {
define i37 @test25(i37 %AA, i37 %BB) {
; CHECK-LABEL: @test25(
; CHECK-NEXT: [[D:%.*]] = and i37 [[AA:%.*]], -131072
; CHECK-NEXT: [[F:%.*]] = and i37 [[C2:%.*]], -131072
; CHECK-NEXT: [[E1:%.*]] = add i37 [[F]], [[D]]
; CHECK-NEXT: ret i37 [[E1]]
; CHECK-NEXT: [[C2:%.*]] = add i37 [[BB:%.*]], [[D]]
; CHECK-NEXT: [[F:%.*]] = and i37 [[C2]], -131072
; CHECK-NEXT: ret i37 [[F]]
;
%C = lshr i37 %BB, 17
%D = lshr i37 %AA, 17

View File

@ -917,7 +917,7 @@ define i1 @shl_failed_to_simplify(i8 %a, i1 %cond) {
define i1 @shl_nuw_ne(i8 %a, i8 %b, i8 %c, i1 %cond) {
; CHECK-LABEL: @shl_nuw_ne(
; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[COND:%.*]], i8 [[B:%.*]], i8 4
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[A:%.*]], [[TMP1]]
; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[TMP1]], [[A:%.*]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shl_a = shl nuw i8 %a, 3

View File

@ -1,311 +0,0 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
; Test case: Fold (X << 5) == ((Y << 5) + 32) into X == (Y + 1).
; This corresponds to the provided alive2 proof.
declare void @use_i64(i64)
define i1 @shl_add_const_eq_base(i64 %v0, i64 %v3) {
; CHECK-LABEL: @shl_add_const_eq_base(
; CHECK-NEXT: [[V5:%.*]] = add nsw i64 [[V3:%.*]], 1
; CHECK-NEXT: [[V6:%.*]] = icmp eq i64 [[V1:%.*]], [[V5]]
; CHECK-NEXT: ret i1 [[V6]]
;
%v1 = shl nsw i64 %v0, 5
%v4 = shl nsw i64 %v3, 5
%v5 = add nsw i64 %v4, 32
%v6 = icmp eq i64 %v1, %v5
ret i1 %v6
}
; Test: icmp ne
define i1 @shl_add_const_ne(i64 %v0, i64 %v3) {
; CHECK-LABEL: @shl_add_const_ne(
; CHECK-NEXT: [[V5:%.*]] = add nsw i64 [[V3:%.*]], 1
; CHECK-NEXT: [[V6:%.*]] = icmp ne i64 [[V1:%.*]], [[V5]]
; CHECK-NEXT: ret i1 [[V6]]
;
%v1 = shl nsw i64 %v0, 5
%v4 = shl nsw i64 %v3, 5
%v5 = add nsw i64 %v4, 32
%v6 = icmp ne i64 %v1, %v5 ; Note: icmp ne
ret i1 %v6
}
; Test: shl amounts do not match (5 vs 4).
define i1 @shl_add_const_eq_mismatch_shl_amt(i64 %v0, i64 %v3) {
; CHECK-LABEL: @shl_add_const_eq_mismatch_shl_amt(
; CHECK-NEXT: [[V1:%.*]] = shl nsw i64 [[V0:%.*]], 5
; CHECK-NEXT: [[V4:%.*]] = shl nsw i64 [[V3:%.*]], 4
; CHECK-NEXT: [[V5:%.*]] = add nsw i64 [[V4]], 16
; CHECK-NEXT: [[V6:%.*]] = icmp eq i64 [[V1]], [[V5]]
; CHECK-NEXT: ret i1 [[V6]]
;
%v1 = shl nsw i64 %v0, 5
%v4 = shl nsw i64 %v3, 4 ; Shift amount mismatch
%v5 = add nsw i64 %v4, 16
%v6 = icmp eq i64 %v1, %v5
ret i1 %v6
}
; Test: Constant K is a multiple of 2^L (64 vs 32). Should simplify to K/2^L = 2.
define i1 @shl_add_const_eq_k_multiple_of_pow2(i64 %v0, i64 %v3) {
; CHECK-LABEL: @shl_add_const_eq_k_multiple_of_pow2(
; CHECK-NEXT: [[V5:%.*]] = add nsw i64 [[V3:%.*]], 2
; CHECK-NEXT: [[V6:%.*]] = icmp eq i64 [[V1:%.*]], [[V5]]
; CHECK-NEXT: ret i1 [[V6]]
;
%v1 = shl nsw i64 %v0, 5
%v4 = shl nsw i64 %v3, 5
%v5 = add nsw i64 %v4, 64 ; Constant mismatch
%v6 = icmp eq i64 %v1, %v5
ret i1 %v6
}
; Test: Missing NSW flag on one of the shl instructions.
define i1 @shl_add_const_eq_no_nsw_on_v1(i64 %v0, i64 %v3) {
; CHECK-LABEL: @shl_add_const_eq_no_nsw_on_v1(
; CHECK-NEXT: [[V1:%.*]] = shl i64 [[V0:%.*]], 5
; CHECK-NEXT: [[V4:%.*]] = shl nsw i64 [[V3:%.*]], 5
; CHECK-NEXT: [[V5:%.*]] = add nsw i64 [[V4]], 32
; CHECK-NEXT: [[V6:%.*]] = icmp eq i64 [[V1]], [[V5]]
; CHECK-NEXT: ret i1 [[V6]]
;
%v1 = shl i64 %v0, 5 ; Missing nsw
%v4 = shl nsw i64 %v3, 5
%v5 = add nsw i64 %v4, 32
%v6 = icmp eq i64 %v1, %v5
ret i1 %v6
}
; Test: Lower bit width (i8) and different shift amount (3). Constant is 8.
define i1 @shl_add_const_eq_i8(i8 %v0, i8 %v3) {
; CHECK-LABEL: @shl_add_const_eq_i8(
; CHECK-NEXT: [[TMP1:%.*]] = add nsw i8 [[V3:%.*]], 1
; CHECK-NEXT: [[V6:%.*]] = icmp eq i8 [[V0:%.*]], [[TMP1]]
; CHECK-NEXT: ret i1 [[V6]]
;
%v1 = shl nsw i8 %v0, 3
%v4 = shl nsw i8 %v3, 3
%v5 = add nsw i8 %v4, 8 ; 2^3 = 8
%v6 = icmp eq i8 %v1, %v5
ret i1 %v6
}
; Test: i32 bit width and larger shift amount (10). Constant is 1024.
define i1 @shl_add_const_eq_i32(i32 %v0, i32 %v3) {
; CHECK-LABEL: @shl_add_const_eq_i32(
; CHECK-NEXT: [[TMP1:%.*]] = add nsw i32 [[V3:%.*]], 1
; CHECK-NEXT: [[V6:%.*]] = icmp eq i32 [[V0:%.*]], [[TMP1]]
; CHECK-NEXT: ret i1 [[V6]]
;
%v1 = shl nsw i32 %v0, 10
%v4 = shl nsw i32 %v3, 10
%v5 = add nsw i32 %v4, 1024 ; 2^10 = 1024
%v6 = icmp eq i32 %v1, %v5
ret i1 %v6
}
; Test: Multi-use case. The optimization should still occur if applicable,
; but the extraneous call must be preserved.
define i1 @shl_add_const_eq_multi_use(i64 %v0, i64 %v3) {
; CHECK-LABEL: @shl_add_const_eq_multi_use(
; CHECK-NEXT: [[V1:%.*]] = shl nsw i64 [[V0:%.*]], 5
; CHECK-NEXT: call void @use_i64(i64 [[V1]])
; CHECK-NEXT: [[V5:%.*]] = add nsw i64 [[V3:%.*]], 1
; CHECK-NEXT: [[V6:%.*]] = icmp eq i64 [[V0]], [[V5]]
; CHECK-NEXT: ret i1 [[V6]]
;
%v1 = shl nsw i64 %v0, 5
call void @use_i64(i64 %v1) ; Additional use of v1
%v4 = shl nsw i64 %v3, 5
%v5 = add nsw i64 %v4, 32
%v6 = icmp eq i64 %v1, %v5
ret i1 %v6
}
; Test: Vector splat. Should fold once optimization is applied.
define <2 x i1> @shl_add_const_eq_vec_splat(<2 x i64> %v0, <2 x i64> %v3) {
; CHECK-LABEL: @shl_add_const_eq_vec_splat(
; CHECK-NEXT: [[V5:%.*]] = add nsw <2 x i64> [[V3:%.*]], splat (i64 1)
; CHECK-NEXT: [[V6:%.*]] = icmp eq <2 x i64> [[V1:%.*]], [[V5]]
; CHECK-NEXT: ret <2 x i1> [[V6]]
;
%v1 = shl nsw <2 x i64> %v0, <i64 5, i64 5>
%v4 = shl nsw <2 x i64> %v3, <i64 5, i64 5>
%v5 = add nsw <2 x i64> %v4, <i64 32, i64 32>
%v6 = icmp eq <2 x i64> %v1, %v5
ret <2 x i1> %v6
}
; Test: Vector splat with poison. Should fold once optimization is applied.
define <2 x i1> @shl_add_const_eq_vec_splat_poison(<2 x i64> %v0, <2 x i64> %v3) {
; CHECK-LABEL: @shl_add_const_eq_vec_splat_poison(
; CHECK-NEXT: [[V5:%.*]] = add nsw <2 x i64> [[V3:%.*]], <i64 1, i64 poison>
; CHECK-NEXT: [[V6:%.*]] = icmp eq <2 x i64> [[V1:%.*]], [[V5]]
; CHECK-NEXT: ret <2 x i1> [[V6]]
;
%v1 = shl nsw <2 x i64> %v0, <i64 5, i64 5>
%v4 = shl nsw <2 x i64> %v3, <i64 5, i64 5>
%v5 = add nsw <2 x i64> %v4, <i64 32, i64 poison>
%v6 = icmp eq <2 x i64> %v1, %v5
ret <2 x i1> %v6
}
; Test: Vector non-splat (should not fold).
define <2 x i1> @shl_add_const_eq_vec_non_splat(<2 x i64> %v0, <2 x i64> %v3) {
; CHECK-LABEL: @shl_add_const_eq_vec_non_splat(
; CHECK-NEXT: [[V1:%.*]] = shl nsw <2 x i64> [[V0:%.*]], <i64 5, i64 6>
; CHECK-NEXT: [[V4:%.*]] = shl nsw <2 x i64> [[V3:%.*]], <i64 5, i64 6>
; CHECK-NEXT: [[V5:%.*]] = add nsw <2 x i64> [[V4]], <i64 32, i64 64>
; CHECK-NEXT: [[V6:%.*]] = icmp eq <2 x i64> [[V1]], [[V5]]
; CHECK-NEXT: ret <2 x i1> [[V6]]
;
%v1 = shl nsw <2 x i64> %v0, <i64 5, i64 6>
%v4 = shl nsw <2 x i64> %v3, <i64 5, i64 6>
%v5 = add nsw <2 x i64> %v4, <i64 32, i64 64>
%v6 = icmp eq <2 x i64> %v1, %v5
ret <2 x i1> %v6
}
; Test: Commutative (shl on the right side).
define i1 @shl_add_const_eq_commutative(i64 %v0, i64 %v3) {
; CHECK-LABEL: @shl_add_const_eq_commutative(
; CHECK-NEXT: [[V5:%.*]] = add nsw i64 [[V3:%.*]], 1
; CHECK-NEXT: [[V6:%.*]] = icmp eq i64 [[V0:%.*]], [[V5]]
; CHECK-NEXT: ret i1 [[V6]]
;
%v1 = shl nsw i64 %v0, 5
%v4 = shl nsw i64 %v3, 5
%v5 = add nsw i64 %v4, 32
%v6 = icmp eq i64 %v5, %v1
ret i1 %v6
}
; Test: Variable shift amount with nuw (Logical)
define i1 @icmp_shl_var_amount_nuw(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_shl_var_amount_nuw(
; CHECK-NEXT: [[Y:%.*]] = add nuw i32 [[Y1:%.*]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X:%.*]], [[Y]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shlx = shl nuw i32 %x, 5
%shly = shl nuw i32 %y, 5
%add = add nuw i32 %shly, 32
%cmp = icmp eq i32 %shlx, %add
ret i1 %cmp
}
; Test: Variable shift amount with nsw (Arithmetic)
define i1 @icmp_shl_var_amount_nsw(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_shl_var_amount_nsw(
; CHECK-NEXT: [[Y:%.*]] = add nsw i32 [[Y1:%.*]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[X:%.*]], [[Y]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shlx = shl nsw i32 %x, 5
%shly = shl nsw i32 %y, 5
%add = add nsw i32 %shly, 32
%cmp = icmp eq i32 %shlx, %add
ret i1 %cmp
}
; Test: ult (Unsigned Less Than) with nuw
define i1 @icmp_shl_nuw_ult(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_shl_nuw_ult(
; CHECK-NEXT: [[CMP:%.*]] = icmp ule i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shlx = shl nuw i32 %x, 5
%shly = shl nuw i32 %y, 5
%add = add nuw i32 %shly, 32
%cmp = icmp ult i32 %shlx, %add
ret i1 %cmp
}
; Test: ugt (Unsigned Greater Than) with nuw
define i1 @icmp_shl_nuw_ugt(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_shl_nuw_ugt(
; CHECK-NEXT: [[TMP1:%.*]] = add nuw i32 [[Y:%.*]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[X:%.*]], [[TMP1]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shlx = shl nuw i32 %x, 5
%shly = shl nuw i32 %y, 5
%add = add nuw i32 %shly, 32
%cmp = icmp ugt i32 %shlx, %add
ret i1 %cmp
}
; Test: slt (Signed Less Than) with nsw
define i1 @icmp_shl_nsw_slt(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_shl_nsw_slt(
; CHECK-NEXT: [[CMP:%.*]] = icmp sle i32 [[X:%.*]], [[Y:%.*]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shlx = shl nsw i32 %x, 5
%shly = shl nsw i32 %y, 5
%add = add nsw i32 %shly, 32
%cmp = icmp slt i32 %shlx, %add
ret i1 %cmp
}
; Test: sgt (Signed Greater Than) with nsw
define i1 @icmp_shl_nsw_sgt(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_shl_nsw_sgt(
; CHECK-NEXT: [[TMP1:%.*]] = add nsw i32 [[Y:%.*]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[X:%.*]], [[TMP1]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shlx = shl nsw i32 %x, 5
%shly = shl nsw i32 %y, 5
%add = add nsw i32 %shly, 32
%cmp = icmp sgt i32 %shlx, %add
ret i1 %cmp
}
; Test: sle (Signed Less or Equal) with nsw
define i1 @icmp_shl_nsw_sle(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_shl_nsw_sle(
; CHECK-NEXT: [[TMP1:%.*]] = add nsw i32 [[Y:%.*]], 1
; CHECK-NEXT: [[CMP:%.*]] = icmp sle i32 [[X:%.*]], [[TMP1]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shlx = shl nsw i32 %x, 5
%shly = shl nsw i32 %y, 5
%add = add nsw i32 %shly, 32
%cmp = icmp sle i32 %shlx, %add
ret i1 %cmp
}
; NOTE: This is a regression test for a miscompile discovered by fuzzing.
define i1 @icmp_slt_shl_nsw_add_nsw_fuzz(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_slt_shl_nsw_add_nsw_fuzz(
; CHECK-NEXT: [[SHLX:%.*]] = shl nsw i32 [[X:%.*]], 5
; CHECK-NEXT: [[SHLY:%.*]] = shl nuw i32 [[Y:%.*]], 5
; CHECK-NEXT: [[CMP:%.*]] = icmp sle i32 [[SHLX]], [[SHLY]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shlx = shl nsw i32 %x, 5
%shly = shl nuw i32 %y, 5
%add = add nsw i32 32, %shly
%cmp = icmp slt i32 %shlx, %add
ret i1 %cmp
}
; NOTE: This is a regression test for a miscompile discovered by fuzzing (unsigned comparison).
define i1 @icmp_ugt_shl_nuw_add_nsw_fuzz(i32 %x, i32 %y) {
; CHECK-LABEL: @icmp_ugt_shl_nuw_add_nsw_fuzz(
; CHECK-NEXT: [[SHLX:%.*]] = shl nuw i32 [[X:%.*]], 5
; CHECK-NEXT: [[SHLY:%.*]] = shl nsw i32 [[Y:%.*]], 5
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[SHLY]], 32
; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[SHLX]], [[ADD]]
; CHECK-NEXT: ret i1 [[CMP]]
;
%shlx = shl nuw i32 %x, 5
%shly = shl nsw i32 %y, 5
%add = add nsw i32 %shly, 32
%cmp = icmp ugt i32 %shlx, %add
ret i1 %cmp
}