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:
parent
8d442bc5b5
commit
cdbb1f5014
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user