diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index c6e15c734203..3ebf719fceb6 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -1912,14 +1912,42 @@ Instruction *InstCombiner::foldICmpShlConstant(ICmpInst &Cmp, Value *X = Shl->getOperand(0); Type *ShType = Shl->getType(); - // If this is a signed comparison to 0 and the shift is sign preserving, - // use the shift LHS operand instead; isSignTest may change 'Pred', so only - // do that if we're sure to not continue on in this function. - if (Shl->hasNoSignedWrap() && isSignTest(Pred, *C)) - return new ICmpInst(Pred, X, Constant::getNullValue(ShType)); + // NSW guarantees that we are only shifting out sign bits from the high bits, + // so we can ASHR the compare constant without needing a mask and eliminate + // the shift. + if (Shl->hasNoSignedWrap()) { + if (Pred == ICmpInst::ICMP_SGT) { + // icmp Pred (shl nsw X, ShiftAmt), C --> icmp Pred X, (C >>s ShiftAmt) + APInt ShiftedC = C->ashr(*ShiftAmt); + return new ICmpInst(Pred, X, ConstantInt::get(ShType, ShiftedC)); + } + if (Pred == ICmpInst::ICMP_EQ || Pred == ICmpInst::ICMP_NE) { + // This is the same code as the SGT case, but assert the pre-condition + // that is needed for this to work with equality predicates. + assert(C->ashr(*ShiftAmt).shl(*ShiftAmt) == *C && + "Compare known true or false was not folded"); + APInt ShiftedC = C->ashr(*ShiftAmt); + return new ICmpInst(Pred, X, ConstantInt::get(ShType, ShiftedC)); + } + if (Pred == ICmpInst::ICMP_SLT) { + // SLE is the same as above, but SLE is canonicalized to SLT, so convert: + // (X << S) <=s C is equiv to X <=s (C >> S) for all C + // (X << S) > S) + 1 if C > S) + 1 if C >s SMIN + assert(!C->isMinSignedValue() && "Unexpected icmp slt"); + APInt ShiftedC = (*C - 1).ashr(*ShiftAmt) + 1; + return new ICmpInst(Pred, X, ConstantInt::get(ShType, ShiftedC)); + } + // If this is a signed comparison to 0 and the shift is sign preserving, + // use the shift LHS operand instead; isSignTest may change 'Pred', so only + // do that if we're sure to not continue on in this function. + if (isSignTest(Pred, *C)) + return new ICmpInst(Pred, X, Constant::getNullValue(ShType)); + } - // A 'shl nuw' is just shifting out zeros, so adjust the compare constant - // and eliminate the shift. + // NUW guarantees that we are only shifting out zero bits from the high bits, + // so we can LSHR the compare constant without needing a mask and eliminate + // the shift. if (Shl->hasNoUnsignedWrap()) { if (Pred == ICmpInst::ICMP_UGT) { // icmp Pred (shl nuw X, ShiftAmt), C --> icmp Pred X, (C >>u ShiftAmt) @@ -1945,23 +1973,14 @@ Instruction *InstCombiner::foldICmpShlConstant(ICmpInst &Cmp, } } - if (Cmp.isEquality()) { + if (Cmp.isEquality() && Shl->hasOneUse()) { + // Strength-reduce the shift into an 'and'. + Constant *Mask = ConstantInt::get( + ShType, + APInt::getLowBitsSet(TypeBits, TypeBits - ShiftAmt->getZExtValue())); + Value *And = Builder->CreateAnd(X, Mask, Shl->getName() + ".mask"); Constant *LShrC = ConstantInt::get(ShType, C->lshr(*ShiftAmt)); - - // If the shift is NSW and we compare to 0, then it is just shifting out - // sign bits, no need for an AND either. - if (Shl->hasNoSignedWrap() && *C == 0) - return new ICmpInst(Pred, X, LShrC); - - if (Shl->hasOneUse()) { - // Otherwise, strength-reduce the shift into an 'and'. - Constant *Mask = ConstantInt::get( - ShType, - APInt::getLowBitsSet(TypeBits, TypeBits - ShiftAmt->getZExtValue())); - - Value *And = Builder->CreateAnd(X, Mask, Shl->getName() + ".mask"); - return new ICmpInst(Pred, And, LShrC); - } + return new ICmpInst(Pred, And, LShrC); } // Otherwise, if this is a comparison of the sign bit, simplify to and/test. diff --git a/llvm/test/Transforms/InstCombine/icmp-shl-nsw.ll b/llvm/test/Transforms/InstCombine/icmp-shl-nsw.ll index c12a39d3bb3a..ba05302897e9 100644 --- a/llvm/test/Transforms/InstCombine/icmp-shl-nsw.ll +++ b/llvm/test/Transforms/InstCombine/icmp-shl-nsw.ll @@ -73,8 +73,7 @@ define <2 x i1> @icmp_shl_nsw_eq_vec(<2 x i32> %x) { define i1 @icmp_sgt1(i8 %x) { ; CHECK-LABEL: @icmp_sgt1( -; CHECK-NEXT: [[SHL_MASK:%.*]] = and i8 %x, 127 -; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[SHL_MASK]], 64 +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 %x, -64 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 1 @@ -84,8 +83,7 @@ define i1 @icmp_sgt1(i8 %x) { define i1 @icmp_sgt2(i8 %x) { ; CHECK-LABEL: @icmp_sgt2( -; CHECK-NEXT: [[SHL:%.*]] = shl nsw i8 %x, 1 -; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[SHL]], -127 +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 %x, -64 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 1 @@ -95,8 +93,7 @@ define i1 @icmp_sgt2(i8 %x) { define i1 @icmp_sgt3(i8 %x) { ; CHECK-LABEL: @icmp_sgt3( -; CHECK-NEXT: [[SHL:%.*]] = shl nsw i8 %x, 1 -; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[SHL]], -16 +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 %x, -8 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 1 @@ -106,8 +103,7 @@ define i1 @icmp_sgt3(i8 %x) { define i1 @icmp_sgt4(i8 %x) { ; CHECK-LABEL: @icmp_sgt4( -; CHECK-NEXT: [[SHL:%.*]] = shl nsw i8 %x, 1 -; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[SHL]], -2 +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 %x, -1 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 1 @@ -120,8 +116,7 @@ define i1 @icmp_sgt4(i8 %x) { define i1 @icmp_sgt5(i8 %x) { ; CHECK-LABEL: @icmp_sgt5( -; CHECK-NEXT: [[SHL:%.*]] = shl nsw i8 %x, 1 -; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[SHL]], 1 +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 %x, 0 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 1 @@ -131,8 +126,7 @@ define i1 @icmp_sgt5(i8 %x) { define i1 @icmp_sgt6(i8 %x) { ; CHECK-LABEL: @icmp_sgt6( -; CHECK-NEXT: [[SHL:%.*]] = shl nsw i8 %x, 1 -; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[SHL]], 16 +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 %x, 8 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 1 @@ -142,8 +136,7 @@ define i1 @icmp_sgt6(i8 %x) { define i1 @icmp_sgt7(i8 %x) { ; CHECK-LABEL: @icmp_sgt7( -; CHECK-NEXT: [[SHL:%.*]] = shl nsw i8 %x, 1 -; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[SHL]], 124 +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 %x, 62 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 1 @@ -155,8 +148,7 @@ define i1 @icmp_sgt7(i8 %x) { define i1 @icmp_sgt8(i8 %x) { ; CHECK-LABEL: @icmp_sgt8( -; CHECK-NEXT: [[SHL_MASK:%.*]] = and i8 %x, 127 -; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[SHL_MASK]], 63 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 %x, 63 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 1 @@ -170,8 +162,7 @@ define i1 @icmp_sgt8(i8 %x) { define i1 @icmp_sgt9(i8 %x) { ; CHECK-LABEL: @icmp_sgt9( -; CHECK-NEXT: [[SHL_MASK:%.*]] = and i8 %x, 1 -; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[SHL_MASK]], 0 +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 %x, -1 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 7 @@ -181,8 +172,7 @@ define i1 @icmp_sgt9(i8 %x) { define i1 @icmp_sgt10(i8 %x) { ; CHECK-LABEL: @icmp_sgt10( -; CHECK-NEXT: [[SHL:%.*]] = shl nsw i8 %x, 7 -; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[SHL]], -127 +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 %x, -1 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 7 @@ -192,8 +182,7 @@ define i1 @icmp_sgt10(i8 %x) { define i1 @icmp_sgt11(i8 %x) { ; CHECK-LABEL: @icmp_sgt11( -; CHECK-NEXT: [[SHL:%.*]] = shl nsw i8 %x, 7 -; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[SHL]], -2 +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 %x, -1 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 7 @@ -205,8 +194,7 @@ define i1 @icmp_sgt11(i8 %x) { define <2 x i1> @icmp_sgt11_vec(<2 x i8> %x) { ; CHECK-LABEL: @icmp_sgt11_vec( -; CHECK-NEXT: [[SHL:%.*]] = shl nsw <2 x i8> %x, -; CHECK-NEXT: [[CMP:%.*]] = icmp sgt <2 x i8> [[SHL]], +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt <2 x i8> %x, ; CHECK-NEXT: ret <2 x i1> [[CMP]] ; %shl = shl nsw <2 x i8> %x, @@ -226,8 +214,7 @@ define <2 x i1> @icmp_sgt11_vec(<2 x i8> %x) { define i1 @icmp_sle1(i8 %x) { ; CHECK-LABEL: @icmp_sle1( -; CHECK-NEXT: [[SHL_MASK:%.*]] = and i8 %x, 127 -; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[SHL_MASK]], 64 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 %x, -64 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 1 @@ -237,8 +224,7 @@ define i1 @icmp_sle1(i8 %x) { define i1 @icmp_sle2(i8 %x) { ; CHECK-LABEL: @icmp_sle2( -; CHECK-NEXT: [[SHL:%.*]] = shl nsw i8 %x, 1 -; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 [[SHL]], -126 +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 %x, -63 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 1 @@ -248,8 +234,7 @@ define i1 @icmp_sle2(i8 %x) { define i1 @icmp_sle3(i8 %x) { ; CHECK-LABEL: @icmp_sle3( -; CHECK-NEXT: [[SHL:%.*]] = shl nsw i8 %x, 1 -; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 [[SHL]], -15 +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 %x, -7 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 1 @@ -259,8 +244,7 @@ define i1 @icmp_sle3(i8 %x) { define i1 @icmp_sle4(i8 %x) { ; CHECK-LABEL: @icmp_sle4( -; CHECK-NEXT: [[SHL:%.*]] = shl nsw i8 %x, 1 -; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 [[SHL]], -1 +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 %x, 0 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 1 @@ -273,8 +257,7 @@ define i1 @icmp_sle4(i8 %x) { define i1 @icmp_sle5(i8 %x) { ; CHECK-LABEL: @icmp_sle5( -; CHECK-NEXT: [[SHL:%.*]] = shl nsw i8 %x, 1 -; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 [[SHL]], 2 +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 %x, 1 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 1 @@ -284,8 +267,7 @@ define i1 @icmp_sle5(i8 %x) { define i1 @icmp_sle6(i8 %x) { ; CHECK-LABEL: @icmp_sle6( -; CHECK-NEXT: [[SHL:%.*]] = shl nsw i8 %x, 1 -; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 [[SHL]], 17 +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 %x, 9 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 1 @@ -295,8 +277,7 @@ define i1 @icmp_sle6(i8 %x) { define i1 @icmp_sle7(i8 %x) { ; CHECK-LABEL: @icmp_sle7( -; CHECK-NEXT: [[SHL:%.*]] = shl nsw i8 %x, 1 -; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 [[SHL]], 125 +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 %x, 63 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 1 @@ -308,8 +289,7 @@ define i1 @icmp_sle7(i8 %x) { define i1 @icmp_sle8(i8 %x) { ; CHECK-LABEL: @icmp_sle8( -; CHECK-NEXT: [[SHL_MASK:%.*]] = and i8 %x, 127 -; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[SHL_MASK]], 63 +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 %x, 63 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 1 @@ -323,8 +303,7 @@ define i1 @icmp_sle8(i8 %x) { define i1 @icmp_sle9(i8 %x) { ; CHECK-LABEL: @icmp_sle9( -; CHECK-NEXT: [[SHL_MASK:%.*]] = and i8 %x, 1 -; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[SHL_MASK]], 0 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 %x, -1 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 7 @@ -334,8 +313,7 @@ define i1 @icmp_sle9(i8 %x) { define i1 @icmp_sle10(i8 %x) { ; CHECK-LABEL: @icmp_sle10( -; CHECK-NEXT: [[SHL:%.*]] = shl nsw i8 %x, 7 -; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 [[SHL]], -126 +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 %x, 0 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 7 @@ -345,8 +323,7 @@ define i1 @icmp_sle10(i8 %x) { define i1 @icmp_sle11(i8 %x) { ; CHECK-LABEL: @icmp_sle11( -; CHECK-NEXT: [[SHL:%.*]] = shl nsw i8 %x, 7 -; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 [[SHL]], -1 +; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 %x, 0 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 7 @@ -359,8 +336,7 @@ define i1 @icmp_sle11(i8 %x) { define i1 @icmp_eq1(i8 %x) { ; CHECK-LABEL: @icmp_eq1( -; CHECK-NEXT: [[SHL_MASK:%.*]] = and i8 %x, 127 -; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[SHL_MASK]], 6 +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 %x, 6 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 1 @@ -370,8 +346,7 @@ define i1 @icmp_eq1(i8 %x) { define i1 @icmp_ne1(i8 %x) { ; CHECK-LABEL: @icmp_ne1( -; CHECK-NEXT: [[SHL_MASK:%.*]] = and i8 %x, 3 -; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[SHL_MASK]], 2 +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 %x, -2 ; CHECK-NEXT: ret i1 [[CMP]] ; %shl = shl nsw i8 %x, 6