diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -1544,6 +1544,67 @@ return nullptr; } +/// Fold icmp (trunc X), (trunc Y). +/// Fold icmp (trunc X), zext(Y). +static Instruction *foldICmpTruncWithTruncOrExt(ICmpInst &Cmp, + InstCombinerImpl &IC, + const SimplifyQuery &Q) { + if (!Cmp.isEquality()) + return nullptr; + + Value *X, *Y; + ICmpInst::Predicate Pred; + bool YIsZext = false; + // Try to match icmp (trunc X), (trunc Y) + if (match(&Cmp, m_ICmp(Pred, m_Trunc(m_Value(X)), m_Trunc(m_Value(Y))))) { + if (X->getType() != Y->getType()) { + if (!(Cmp.getOperand(0)->hasOneUse() && Cmp.getOperand(1)->hasOneUse())) + return nullptr; + + if (X->getType()->getScalarSizeInBits() > + Y->getType()->getScalarSizeInBits()) + std::swap(X, Y); + } + + } + // Try to match icmp (trunc X), (zext Y) + else if (match(&Cmp, + m_c_ICmp(Pred, m_Trunc(m_Value(X)), m_ZExt(m_Value(Y))))) { + + if (!(isa(Cmp.getOperand(0)) ? Cmp.getOperand(0) + : Cmp.getOperand(1)) + ->hasOneUse()) + return nullptr; + YIsZext = true; + } else { + return nullptr; + } + + Type *TruncTy, *BaseTy; + TruncTy = Cmp.getOperand(0)->getType(); + BaseTy = X->getType(); + + unsigned TruncBits = TruncTy->getScalarSizeInBits(); + unsigned BaseBits = BaseTy->getScalarSizeInBits(); + + // Check if the trunc is unneeded. + KnownBits KnownX = + computeKnownBits(X, Q.DL, 0, Q.AC, Q.CxtI, Q.DT); + if (KnownX.Zero.countl_one() < BaseBits - TruncBits) + return nullptr; + + if (!YIsZext) { + // If Y is also a trunc, make sure it is unneeded. + KnownBits KnownY = computeKnownBits(Y, Q.DL, 0, Q.AC, Q.CxtI, Q.DT); + if (KnownY.Zero.countl_one() < BaseBits - TruncBits) + return nullptr; + } + + Value *NewY = IC.Builder.CreateZExtOrTrunc(Y, BaseTy); + return IC.replaceInstUsesWith( + Cmp, IC.Builder.CreateICmp(Pred, X, NewY)); +} + /// Fold icmp (xor X, Y), C. Instruction *InstCombinerImpl::foldICmpXorConstant(ICmpInst &Cmp, BinaryOperator *Xor, @@ -6451,6 +6512,9 @@ if (Instruction *Res = foldICmpUsingKnownBits(I)) return Res; + if (Instruction *Res = foldICmpTruncWithTruncOrExt(I, *this, Q)) + return Res; + // Test if the ICmpInst instruction is used exclusively by a select as // part of a minimum or maximum operation. If so, refrain from doing // any other folding. This helps out other analyses which understand diff --git a/llvm/test/Transforms/InstCombine/eq-of-parts.ll b/llvm/test/Transforms/InstCombine/eq-of-parts.ll --- a/llvm/test/Transforms/InstCombine/eq-of-parts.ll +++ b/llvm/test/Transforms/InstCombine/eq-of-parts.ll @@ -584,17 +584,8 @@ define i1 @eq_shift_in_zeros(i32 %x, i32 %y) { ; CHECK-LABEL: @eq_shift_in_zeros( -; CHECK-NEXT: [[X_321:%.*]] = lshr i32 [[X:%.*]], 8 -; CHECK-NEXT: [[X_1:%.*]] = trunc i32 [[X_321]] to i8 -; CHECK-NEXT: [[X_32:%.*]] = lshr i32 [[X]], 16 -; CHECK-NEXT: [[X_2:%.*]] = trunc i32 [[X_32]] to i24 -; CHECK-NEXT: [[Y_321:%.*]] = lshr i32 [[Y:%.*]], 8 -; CHECK-NEXT: [[Y_1:%.*]] = trunc i32 [[Y_321]] to i8 -; CHECK-NEXT: [[Y_32:%.*]] = lshr i32 [[Y]], 16 -; CHECK-NEXT: [[Y_2:%.*]] = trunc i32 [[Y_32]] to i24 -; CHECK-NEXT: [[C_1:%.*]] = icmp eq i8 [[X_1]], [[Y_1]] -; CHECK-NEXT: [[C_2:%.*]] = icmp eq i24 [[X_2]], [[Y_2]] -; CHECK-NEXT: [[C_210:%.*]] = and i1 [[C_2]], [[C_1]] +; CHECK-NEXT: [[C_210_UNSHIFTED:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[C_210:%.*]] = icmp ult i32 [[C_210_UNSHIFTED]], 256 ; CHECK-NEXT: ret i1 [[C_210]] ; %x.321 = lshr i32 %x, 8 @@ -1249,17 +1240,8 @@ define i1 @ne_shift_in_zeros(i32 %x, i32 %y) { ; CHECK-LABEL: @ne_shift_in_zeros( -; CHECK-NEXT: [[X_321:%.*]] = lshr i32 [[X:%.*]], 8 -; CHECK-NEXT: [[X_1:%.*]] = trunc i32 [[X_321]] to i8 -; CHECK-NEXT: [[X_32:%.*]] = lshr i32 [[X]], 16 -; CHECK-NEXT: [[X_2:%.*]] = trunc i32 [[X_32]] to i24 -; CHECK-NEXT: [[Y_321:%.*]] = lshr i32 [[Y:%.*]], 8 -; CHECK-NEXT: [[Y_1:%.*]] = trunc i32 [[Y_321]] to i8 -; CHECK-NEXT: [[Y_32:%.*]] = lshr i32 [[Y]], 16 -; CHECK-NEXT: [[Y_2:%.*]] = trunc i32 [[Y_32]] to i24 -; CHECK-NEXT: [[C_1:%.*]] = icmp ne i8 [[X_1]], [[Y_1]] -; CHECK-NEXT: [[C_2:%.*]] = icmp ne i24 [[X_2]], [[Y_2]] -; CHECK-NEXT: [[C_210:%.*]] = or i1 [[C_2]], [[C_1]] +; CHECK-NEXT: [[C_210_UNSHIFTED:%.*]] = xor i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[C_210:%.*]] = icmp ugt i32 [[C_210_UNSHIFTED]], 255 ; CHECK-NEXT: ret i1 [[C_210]] ; %x.321 = lshr i32 %x, 8 diff --git a/llvm/test/Transforms/InstCombine/icmp-of-trunc-ext.ll b/llvm/test/Transforms/InstCombine/icmp-of-trunc-ext.ll --- a/llvm/test/Transforms/InstCombine/icmp-of-trunc-ext.ll +++ b/llvm/test/Transforms/InstCombine/icmp-of-trunc-ext.ll @@ -9,9 +9,7 @@ ; CHECK-NEXT: [[Y_LB_ONLY:%.*]] = icmp ult i32 [[Y:%.*]], 65536 ; CHECK-NEXT: call void @llvm.assume(i1 [[X_LB_ONLY]]) ; CHECK-NEXT: call void @llvm.assume(i1 [[Y_LB_ONLY]]) -; CHECK-NEXT: [[X16:%.*]] = trunc i32 [[X]] to i16 -; CHECK-NEXT: [[Y16:%.*]] = trunc i32 [[Y]] to i16 -; CHECK-NEXT: [[R:%.*]] = icmp eq i16 [[X16]], [[Y16]] +; CHECK-NEXT: [[R:%.*]] = icmp eq i32 [[X]], [[Y]] ; CHECK-NEXT: ret i1 [[R]] ; %x_lb_only = icmp ult i32 %x, 65536 @@ -30,9 +28,8 @@ ; CHECK-NEXT: [[Y_LB_ONLY:%.*]] = icmp ult i64 [[Y:%.*]], 65536 ; CHECK-NEXT: call void @llvm.assume(i1 [[X_LB_ONLY]]) ; CHECK-NEXT: call void @llvm.assume(i1 [[Y_LB_ONLY]]) -; CHECK-NEXT: [[X16:%.*]] = trunc i32 [[X]] to i16 -; CHECK-NEXT: [[Y16:%.*]] = trunc i64 [[Y]] to i16 -; CHECK-NEXT: [[R:%.*]] = icmp eq i16 [[Y16]], [[X16]] +; CHECK-NEXT: [[TMP1:%.*]] = trunc i64 [[Y]] to i32 +; CHECK-NEXT: [[R:%.*]] = icmp eq i32 [[TMP1]], [[X]] ; CHECK-NEXT: ret i1 [[R]] ; %x_lb_only = icmp ult i32 %x, 12345 @@ -70,9 +67,8 @@ ; CHECK-LABEL: @icmp_trunc_x_zext_y( ; CHECK-NEXT: [[X_LB_ONLY:%.*]] = icmp ult i32 [[X:%.*]], 65536 ; CHECK-NEXT: call void @llvm.assume(i1 [[X_LB_ONLY]]) -; CHECK-NEXT: [[X16:%.*]] = trunc i32 [[X]] to i16 -; CHECK-NEXT: [[Y16:%.*]] = zext i8 [[Y:%.*]] to i16 -; CHECK-NEXT: [[R:%.*]] = icmp eq i16 [[X16]], [[Y16]] +; CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[Y:%.*]] to i32 +; CHECK-NEXT: [[R:%.*]] = icmp eq i32 [[TMP1]], [[X]] ; CHECK-NEXT: ret i1 [[R]] ; %x_lb_only = icmp ult i32 %x, 65536 @@ -87,9 +83,8 @@ ; CHECK-LABEL: @icmp_trunc_x_zext_y_2( ; CHECK-NEXT: [[X_LB_ONLY:%.*]] = icmp ult i64 [[X:%.*]], 65536 ; CHECK-NEXT: call void @llvm.assume(i1 [[X_LB_ONLY]]) -; CHECK-NEXT: [[X16:%.*]] = trunc i64 [[X]] to i16 -; CHECK-NEXT: [[Y16:%.*]] = zext i8 [[Y:%.*]] to i16 -; CHECK-NEXT: [[R:%.*]] = icmp eq i16 [[Y16]], [[X16]] +; CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[Y:%.*]] to i64 +; CHECK-NEXT: [[R:%.*]] = icmp eq i64 [[TMP1]], [[X]] ; CHECK-NEXT: ret i1 [[R]] ; %x_lb_only = icmp ult i64 %x, 65536