diff --git a/llvm/include/llvm/IR/PatternMatch.h b/llvm/include/llvm/IR/PatternMatch.h index 0b13b4aad9c32..f063e6531dda4 100644 --- a/llvm/include/llvm/IR/PatternMatch.h +++ b/llvm/include/llvm/IR/PatternMatch.h @@ -1839,6 +1839,19 @@ template struct NNegZExt_match { } }; +template struct NoWrapTrunc_match { + Op_t Op; + + NoWrapTrunc_match(const Op_t &OpMatch) : Op(OpMatch) {} + + template bool match(OpTy *V) { + if (auto *I = dyn_cast(V)) + return (I->getNoWrapKind() & WrapFlags) == WrapFlags && + Op.match(I->getOperand(0)); + return false; + } +}; + /// Matches BitCast. template inline CastOperator_match @@ -1900,6 +1913,20 @@ inline CastOperator_match m_Trunc(const OpTy &Op) { return CastOperator_match(Op); } +/// Matches trunc nuw. +template +inline NoWrapTrunc_match +m_NUWTrunc(const OpTy &Op) { + return NoWrapTrunc_match(Op); +} + +/// Matches trunc nsw. +template +inline NoWrapTrunc_match +m_NSWTrunc(const OpTy &Op) { + return NoWrapTrunc_match(Op); +} + template inline match_combine_or, OpTy> m_TruncOrSelf(const OpTy &Op) { diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index 1064340cb5366..81807495956f7 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -1479,19 +1479,29 @@ Instruction *InstCombinerImpl::foldICmpTruncConstant(ICmpInst &Cmp, return nullptr; } -/// Fold icmp (trunc X), (trunc Y). -/// Fold icmp (trunc X), (zext Y). +/// Fold icmp (trunc nuw/nsw X), (trunc nuw/nsw Y). +/// Fold icmp (trunc nuw/nsw X), (zext/sext Y). Instruction * InstCombinerImpl::foldICmpTruncWithTruncOrExt(ICmpInst &Cmp, const SimplifyQuery &Q) { - if (Cmp.isSigned()) - return nullptr; - Value *X, *Y; ICmpInst::Predicate Pred; - bool YIsZext = false; + bool YIsSExt = 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))))) { + unsigned NoWrapFlags = cast(Cmp.getOperand(0))->getNoWrapKind() & + cast(Cmp.getOperand(1))->getNoWrapKind(); + if (Cmp.isSigned()) { + // For signed comparisons, both truncs must be nsw. + if (!(NoWrapFlags & TruncInst::NoSignedWrap)) + return nullptr; + } else { + // For unsigned and equality comparisons, either both must be nuw or + // both must be nsw, we don't care which. + if (!NoWrapFlags) + return nullptr; + } + if (X->getType() != Y->getType() && (!Cmp.getOperand(0)->hasOneUse() || !Cmp.getOperand(1)->hasOneUse())) return nullptr; @@ -1501,12 +1511,19 @@ InstCombinerImpl::foldICmpTruncWithTruncOrExt(ICmpInst &Cmp, Pred = Cmp.getSwappedPredicate(Pred); } } - // Try to match icmp (trunc X), (zext Y) - else if (match(&Cmp, m_c_ICmp(Pred, m_Trunc(m_Value(X)), - m_OneUse(m_ZExt(m_Value(Y)))))) - - YIsZext = true; - else + // Try to match icmp (trunc nuw X), (zext Y) + else if (!Cmp.isSigned() && + match(&Cmp, m_c_ICmp(Pred, m_NUWTrunc(m_Value(X)), + m_OneUse(m_ZExt(m_Value(Y)))))) { + // Can fold trunc nuw + zext for unsigned and equality predicates. + } + // Try to match icmp (trunc nsw X), (sext Y) + else if (match(&Cmp, m_c_ICmp(Pred, m_NSWTrunc(m_Value(X)), + m_OneUse(m_ZExtOrSExt(m_Value(Y)))))) { + // Can fold trunc nsw + zext/sext for all predicates. + YIsSExt = + isa(Cmp.getOperand(0)) || isa(Cmp.getOperand(1)); + } else return nullptr; Type *TruncTy = Cmp.getOperand(0)->getType(); @@ -1518,19 +1535,7 @@ InstCombinerImpl::foldICmpTruncWithTruncOrExt(ICmpInst &Cmp, !isDesirableIntType(X->getType()->getScalarSizeInBits())) return nullptr; - // Check if the trunc is unneeded. - KnownBits KnownX = llvm::computeKnownBits(X, /*Depth*/ 0, Q); - if (KnownX.countMaxActiveBits() > TruncBits) - return nullptr; - - if (!YIsZext) { - // If Y is also a trunc, make sure it is unneeded. - KnownBits KnownY = llvm::computeKnownBits(Y, /*Depth*/ 0, Q); - if (KnownY.countMaxActiveBits() > TruncBits) - return nullptr; - } - - Value *NewY = Builder.CreateZExtOrTrunc(Y, X->getType()); + Value *NewY = Builder.CreateIntCast(Y, X->getType(), YIsSExt); return new ICmpInst(Pred, X, NewY); } diff --git a/llvm/test/Transforms/InstCombine/icmp-of-trunc-ext.ll b/llvm/test/Transforms/InstCombine/icmp-of-trunc-ext.ll index a61694919ab04..0a8bf013dc55d 100644 --- a/llvm/test/Transforms/InstCombine/icmp-of-trunc-ext.ll +++ b/llvm/test/Transforms/InstCombine/icmp-of-trunc-ext.ll @@ -271,9 +271,7 @@ define i1 @icmp_trunc_x_zext_y_fail_multiuse(i32 %x, i8 %y) { define i1 @trunc_unsigned_nuw(i16 %x, i16 %y) { ; CHECK-LABEL: @trunc_unsigned_nuw( -; CHECK-NEXT: [[XT:%.*]] = trunc nuw i16 [[X:%.*]] to i8 -; CHECK-NEXT: [[YT:%.*]] = trunc nuw i16 [[Y:%.*]] to i8 -; CHECK-NEXT: [[C:%.*]] = icmp ult i8 [[XT]], [[YT]] +; CHECK-NEXT: [[C:%.*]] = icmp ult i16 [[X:%.*]], [[Y:%.*]] ; CHECK-NEXT: ret i1 [[C]] ; %xt = trunc nuw i16 %x to i8 @@ -284,9 +282,7 @@ define i1 @trunc_unsigned_nuw(i16 %x, i16 %y) { define i1 @trunc_unsigned_nsw(i16 %x, i16 %y) { ; CHECK-LABEL: @trunc_unsigned_nsw( -; CHECK-NEXT: [[XT:%.*]] = trunc nsw i16 [[X:%.*]] to i8 -; CHECK-NEXT: [[YT:%.*]] = trunc nsw i16 [[Y:%.*]] to i8 -; CHECK-NEXT: [[C:%.*]] = icmp ult i8 [[XT]], [[YT]] +; CHECK-NEXT: [[C:%.*]] = icmp ult i16 [[X:%.*]], [[Y:%.*]] ; CHECK-NEXT: ret i1 [[C]] ; %xt = trunc nsw i16 %x to i8 @@ -297,9 +293,7 @@ define i1 @trunc_unsigned_nsw(i16 %x, i16 %y) { define i1 @trunc_unsigned_both(i16 %x, i16 %y) { ; CHECK-LABEL: @trunc_unsigned_both( -; CHECK-NEXT: [[XT:%.*]] = trunc nuw nsw i16 [[X:%.*]] to i8 -; CHECK-NEXT: [[YT:%.*]] = trunc nuw nsw i16 [[Y:%.*]] to i8 -; CHECK-NEXT: [[C:%.*]] = icmp ult i8 [[XT]], [[YT]] +; CHECK-NEXT: [[C:%.*]] = icmp ult i16 [[X:%.*]], [[Y:%.*]] ; CHECK-NEXT: ret i1 [[C]] ; %xt = trunc nuw nsw i16 %x to i8 @@ -336,9 +330,7 @@ define i1 @trunc_signed_nuw(i16 %x, i16 %y) { define i1 @trunc_signed_nsw(i16 %x, i16 %y) { ; CHECK-LABEL: @trunc_signed_nsw( -; CHECK-NEXT: [[XT:%.*]] = trunc nsw i16 [[X:%.*]] to i8 -; CHECK-NEXT: [[YT:%.*]] = trunc nsw i16 [[Y:%.*]] to i8 -; CHECK-NEXT: [[C:%.*]] = icmp slt i8 [[XT]], [[YT]] +; CHECK-NEXT: [[C:%.*]] = icmp slt i16 [[X:%.*]], [[Y:%.*]] ; CHECK-NEXT: ret i1 [[C]] ; %xt = trunc nsw i16 %x to i8 @@ -349,9 +341,7 @@ define i1 @trunc_signed_nsw(i16 %x, i16 %y) { define i1 @trunc_signed_both(i16 %x, i16 %y) { ; CHECK-LABEL: @trunc_signed_both( -; CHECK-NEXT: [[XT:%.*]] = trunc nuw nsw i16 [[X:%.*]] to i8 -; CHECK-NEXT: [[YT:%.*]] = trunc nuw nsw i16 [[Y:%.*]] to i8 -; CHECK-NEXT: [[C:%.*]] = icmp slt i8 [[XT]], [[YT]] +; CHECK-NEXT: [[C:%.*]] = icmp slt i16 [[X:%.*]], [[Y:%.*]] ; CHECK-NEXT: ret i1 [[C]] ; %xt = trunc nuw nsw i16 %x to i8 @@ -375,9 +365,7 @@ define i1 @trunc_signed_either(i16 %x, i16 %y) { define i1 @trunc_equality_nuw(i16 %x, i16 %y) { ; CHECK-LABEL: @trunc_equality_nuw( -; CHECK-NEXT: [[XT:%.*]] = trunc nuw i16 [[X:%.*]] to i8 -; CHECK-NEXT: [[YT:%.*]] = trunc nuw i16 [[Y:%.*]] to i8 -; CHECK-NEXT: [[C:%.*]] = icmp eq i8 [[XT]], [[YT]] +; CHECK-NEXT: [[C:%.*]] = icmp eq i16 [[X:%.*]], [[Y:%.*]] ; CHECK-NEXT: ret i1 [[C]] ; %xt = trunc nuw i16 %x to i8 @@ -388,9 +376,7 @@ define i1 @trunc_equality_nuw(i16 %x, i16 %y) { define i1 @trunc_equality_nsw(i16 %x, i16 %y) { ; CHECK-LABEL: @trunc_equality_nsw( -; CHECK-NEXT: [[XT:%.*]] = trunc nsw i16 [[X:%.*]] to i8 -; CHECK-NEXT: [[YT:%.*]] = trunc nsw i16 [[Y:%.*]] to i8 -; CHECK-NEXT: [[C:%.*]] = icmp eq i8 [[XT]], [[YT]] +; CHECK-NEXT: [[C:%.*]] = icmp eq i16 [[X:%.*]], [[Y:%.*]] ; CHECK-NEXT: ret i1 [[C]] ; %xt = trunc nsw i16 %x to i8 @@ -401,9 +387,7 @@ define i1 @trunc_equality_nsw(i16 %x, i16 %y) { define i1 @trunc_equality_both(i16 %x, i16 %y) { ; CHECK-LABEL: @trunc_equality_both( -; CHECK-NEXT: [[XT:%.*]] = trunc nuw nsw i16 [[X:%.*]] to i8 -; CHECK-NEXT: [[YT:%.*]] = trunc nuw nsw i16 [[Y:%.*]] to i8 -; CHECK-NEXT: [[C:%.*]] = icmp eq i8 [[XT]], [[YT]] +; CHECK-NEXT: [[C:%.*]] = icmp eq i16 [[X:%.*]], [[Y:%.*]] ; CHECK-NEXT: ret i1 [[C]] ; %xt = trunc nuw nsw i16 %x to i8 @@ -427,9 +411,8 @@ define i1 @trunc_equality_either(i16 %x, i16 %y) { define i1 @trunc_unsigned_nuw_zext(i32 %x, i8 %y) { ; CHECK-LABEL: @trunc_unsigned_nuw_zext( -; CHECK-NEXT: [[XT:%.*]] = trunc nuw i32 [[X:%.*]] to i16 -; CHECK-NEXT: [[YE:%.*]] = zext i8 [[Y:%.*]] to i16 -; CHECK-NEXT: [[C:%.*]] = icmp ult i16 [[XT]], [[YE]] +; CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[Y:%.*]] to i32 +; CHECK-NEXT: [[C:%.*]] = icmp ugt i32 [[TMP1]], [[X:%.*]] ; CHECK-NEXT: ret i1 [[C]] ; %xt = trunc nuw i32 %x to i16 @@ -453,9 +436,8 @@ define i1 @trunc_unsigned_nuw_sext(i32 %x, i8 %y) { define i1 @trunc_unsigned_nsw_zext(i32 %x, i8 %y) { ; CHECK-LABEL: @trunc_unsigned_nsw_zext( -; CHECK-NEXT: [[XT:%.*]] = trunc nsw i32 [[X:%.*]] to i16 -; CHECK-NEXT: [[YE:%.*]] = zext i8 [[Y:%.*]] to i16 -; CHECK-NEXT: [[C:%.*]] = icmp ult i16 [[XT]], [[YE]] +; CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[Y:%.*]] to i32 +; CHECK-NEXT: [[C:%.*]] = icmp ugt i32 [[TMP1]], [[X:%.*]] ; CHECK-NEXT: ret i1 [[C]] ; %xt = trunc nsw i32 %x to i16 @@ -466,9 +448,8 @@ define i1 @trunc_unsigned_nsw_zext(i32 %x, i8 %y) { define i1 @trunc_unsigned_nsw_sext(i32 %x, i8 %y) { ; CHECK-LABEL: @trunc_unsigned_nsw_sext( -; CHECK-NEXT: [[XT:%.*]] = trunc nsw i32 [[X:%.*]] to i16 -; CHECK-NEXT: [[YE:%.*]] = sext i8 [[Y:%.*]] to i16 -; CHECK-NEXT: [[C:%.*]] = icmp ult i16 [[XT]], [[YE]] +; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[Y:%.*]] to i32 +; CHECK-NEXT: [[C:%.*]] = icmp ugt i32 [[TMP1]], [[X:%.*]] ; CHECK-NEXT: ret i1 [[C]] ; %xt = trunc nsw i32 %x to i16 @@ -479,9 +460,8 @@ define i1 @trunc_unsigned_nsw_sext(i32 %x, i8 %y) { define i1 @trunc_signed_nsw_sext(i32 %x, i8 %y) { ; CHECK-LABEL: @trunc_signed_nsw_sext( -; CHECK-NEXT: [[XT:%.*]] = trunc nsw i32 [[X:%.*]] to i16 -; CHECK-NEXT: [[YE:%.*]] = sext i8 [[Y:%.*]] to i16 -; CHECK-NEXT: [[C:%.*]] = icmp slt i16 [[XT]], [[YE]] +; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[Y:%.*]] to i32 +; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[TMP1]], [[X:%.*]] ; CHECK-NEXT: ret i1 [[C]] ; %xt = trunc nsw i32 %x to i16 @@ -492,9 +472,8 @@ define i1 @trunc_signed_nsw_sext(i32 %x, i8 %y) { define i1 @trunc_signed_nsw_zext(i32 %x, i8 %y) { ; CHECK-LABEL: @trunc_signed_nsw_zext( -; CHECK-NEXT: [[XT:%.*]] = trunc nsw i32 [[X:%.*]] to i16 -; CHECK-NEXT: [[YE:%.*]] = zext i8 [[Y:%.*]] to i16 -; CHECK-NEXT: [[C:%.*]] = icmp slt i16 [[XT]], [[YE]] +; CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[Y:%.*]] to i32 +; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[TMP1]], [[X:%.*]] ; CHECK-NEXT: ret i1 [[C]] ; %xt = trunc nsw i32 %x to i16 @@ -531,9 +510,8 @@ define i1 @trunc_signed_nuw_zext(i32 %x, i8 %y) { define i1 @trunc_equality_nuw_zext(i32 %x, i8 %y) { ; CHECK-LABEL: @trunc_equality_nuw_zext( -; CHECK-NEXT: [[XT:%.*]] = trunc nuw i32 [[X:%.*]] to i16 -; CHECK-NEXT: [[YE:%.*]] = zext i8 [[Y:%.*]] to i16 -; CHECK-NEXT: [[C:%.*]] = icmp ne i16 [[XT]], [[YE]] +; CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[Y:%.*]] to i32 +; CHECK-NEXT: [[C:%.*]] = icmp ne i32 [[TMP1]], [[X:%.*]] ; CHECK-NEXT: ret i1 [[C]] ; %xt = trunc nuw i32 %x to i16 @@ -557,9 +535,8 @@ define i1 @trunc_equality_nuw_sext(i32 %x, i8 %y) { define i1 @trunc_equality_nsw_zext(i32 %x, i8 %y) { ; CHECK-LABEL: @trunc_equality_nsw_zext( -; CHECK-NEXT: [[XT:%.*]] = trunc nsw i32 [[X:%.*]] to i16 -; CHECK-NEXT: [[YE:%.*]] = zext i8 [[Y:%.*]] to i16 -; CHECK-NEXT: [[C:%.*]] = icmp ne i16 [[XT]], [[YE]] +; CHECK-NEXT: [[TMP1:%.*]] = zext i8 [[Y:%.*]] to i32 +; CHECK-NEXT: [[C:%.*]] = icmp ne i32 [[TMP1]], [[X:%.*]] ; CHECK-NEXT: ret i1 [[C]] ; %xt = trunc nsw i32 %x to i16 @@ -570,9 +547,8 @@ define i1 @trunc_equality_nsw_zext(i32 %x, i8 %y) { define i1 @trunc_equality_nsw_sext(i32 %x, i8 %y) { ; CHECK-LABEL: @trunc_equality_nsw_sext( -; CHECK-NEXT: [[XT:%.*]] = trunc nsw i32 [[X:%.*]] to i16 -; CHECK-NEXT: [[YE:%.*]] = sext i8 [[Y:%.*]] to i16 -; CHECK-NEXT: [[C:%.*]] = icmp ne i16 [[XT]], [[YE]] +; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[Y:%.*]] to i32 +; CHECK-NEXT: [[C:%.*]] = icmp ne i32 [[TMP1]], [[X:%.*]] ; CHECK-NEXT: ret i1 [[C]] ; %xt = trunc nsw i32 %x to i16 @@ -583,9 +559,8 @@ define i1 @trunc_equality_nsw_sext(i32 %x, i8 %y) { define i1 @trunc_equality_both_sext(i32 %x, i8 %y) { ; CHECK-LABEL: @trunc_equality_both_sext( -; CHECK-NEXT: [[XT:%.*]] = trunc nuw nsw i32 [[X:%.*]] to i16 -; CHECK-NEXT: [[YE:%.*]] = sext i8 [[Y:%.*]] to i16 -; CHECK-NEXT: [[C:%.*]] = icmp ne i16 [[XT]], [[YE]] +; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[Y:%.*]] to i32 +; CHECK-NEXT: [[C:%.*]] = icmp ne i32 [[TMP1]], [[X:%.*]] ; CHECK-NEXT: ret i1 [[C]] ; %xt = trunc nuw nsw i32 %x to i16