diff --git a/llvm/include/llvm/IR/ConstantRange.h b/llvm/include/llvm/IR/ConstantRange.h index d086c25390fd2..92e9341352c17 100644 --- a/llvm/include/llvm/IR/ConstantRange.h +++ b/llvm/include/llvm/IR/ConstantRange.h @@ -128,6 +128,12 @@ class [[nodiscard]] ConstantRange { /// NOTE: false does not mean that inverse predicate holds! bool icmp(CmpInst::Predicate Pred, const ConstantRange &Other) const; + /// Does the predicate \p Pred or its inverse hold between ranges this and \p + /// Other? Returns `true` if the predicate always holds, `false` if the + /// inverse always holds, or `std::nullopt` otherwise. + std::optional icmpOrInverse(CmpInst::Predicate Pred, + const ConstantRange &Other) const; + /// Return true iff CR1 ult CR2 is equivalent to CR1 slt CR2. /// Does not depend on strictness/direction of the predicate. static bool diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp index d69747e30f884..dae4f37908cf2 100644 --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -3783,13 +3783,9 @@ static Value *simplifyICmpInst(CmpPredicate Pred, Value *LHS, Value *RHS, // If both operands have range metadata, use the metadata // to simplify the comparison. if (std::optional RhsCr = getRange(RHS, Q.IIQ)) - if (std::optional LhsCr = getRange(LHS, Q.IIQ)) { - if (LhsCr->icmp(Pred, *RhsCr)) - return ConstantInt::getTrue(ITy); - - if (LhsCr->icmp(CmpInst::getInversePredicate(Pred), *RhsCr)) - return ConstantInt::getFalse(ITy); - } + if (std::optional LhsCr = getRange(LHS, Q.IIQ)) + if (auto Res = LhsCr->icmpOrInverse(Pred, *RhsCr)) + return ConstantInt::getBool(ITy, *Res); // Compare of cast, for example (zext X) != 0 -> X != 0 if (isa(LHS) && (isa(RHS) || isa(RHS))) { diff --git a/llvm/lib/IR/ConstantRange.cpp b/llvm/lib/IR/ConstantRange.cpp index 3566435398992..c5380ab2ffeda 100644 --- a/llvm/lib/IR/ConstantRange.cpp +++ b/llvm/lib/IR/ConstantRange.cpp @@ -274,6 +274,16 @@ bool ConstantRange::icmp(CmpInst::Predicate Pred, } } +std::optional +ConstantRange::icmpOrInverse(CmpInst::Predicate Pred, + const ConstantRange &Other) const { + if (icmp(Pred, Other)) + return true; + if (icmp(CmpInst::getInversePredicate(Pred), Other)) + return false; + return std::nullopt; +} + /// Exact mul nuw region for single element RHS. static ConstantRange makeExactMulNUWRegion(const APInt &V) { unsigned BitWidth = V.getBitWidth(); diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index 2e45725759949..eb239810d625d 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -3133,7 +3133,10 @@ Instruction *InstCombinerImpl::foldICmpAddConstant(ICmpInst &Cmp, if (ICmpInst::isUnsigned(Pred) && Add->hasNoSignedWrap() && C.isNonNegative() && (C - *C2).isNonNegative() && - computeConstantRange(X, /*ForSigned=*/true).add(*C2).isAllNonNegative()) + computeConstantRange(X, /*ForSigned=*/true, /*UseInstrInfo=*/true, &AC, + Add, &DT) + .add(*C2) + .isAllNonNegative()) return new ICmpInst(ICmpInst::getSignedPredicate(Pred), X, ConstantInt::get(Ty, C - *C2)); @@ -7025,6 +7028,66 @@ static Instruction *canonicalizeICmpBool(ICmpInst &I, } } +// (icmp X, Y) --> (icmp slt/sgt X, 0/-1) iff Y is outside the signed range of X +static ICmpInst *canonicalizeSignBitCheck(ICmpInst::Predicate Pred, Value *X, + const ConstantRange &XRange, + const ConstantRange &YRange) { + if (XRange.isSignWrappedSet()) + return nullptr; + unsigned BitWidth = XRange.getBitWidth(); + APInt SMin = APInt::getSignedMinValue(BitWidth); + APInt Zero = APInt::getZero(BitWidth); + auto NegResult = + XRange.intersectWith(ConstantRange(SMin, Zero), ConstantRange::Signed) + .icmpOrInverse(Pred, YRange); + if (!NegResult) + return nullptr; + auto PosResult = + XRange.intersectWith(ConstantRange(Zero, SMin), ConstantRange::Signed) + .icmpOrInverse(Pred, YRange); + if (!PosResult) + return nullptr; + assert(NegResult != PosResult && + "Known result should been simplified already."); + Type *Ty = X->getType(); + if (*NegResult) + return new ICmpInst(ICmpInst::ICMP_SLT, X, ConstantInt::getNullValue(Ty)); + return new ICmpInst(ICmpInst::ICMP_SGT, X, ConstantInt::getAllOnesValue(Ty)); +} + +// Try to fold an icmp using the constant ranges of its operands. +Instruction *InstCombinerImpl::foldICmpUsingConstantRanges(ICmpInst &Cmp) { + Value *X = Cmp.getOperand(0); + if (!X->getType()->isIntOrIntVectorTy()) + return nullptr; + Value *Y = Cmp.getOperand(1); + ICmpInst::Predicate Pred = Cmp.getPredicate(); + ConstantRange XRange = computeConstantRange( + X, ICmpInst::isSigned(Pred), /*UseInstrInfo=*/true, &AC, &Cmp, &DT); + if (XRange.isFullSet()) + return nullptr; // early out if we don't have any information + ConstantRange YRange = computeConstantRange( + Y, ICmpInst::isSigned(Pred), /*UseInstrInfo=*/true, &AC, &Cmp, &DT); + if (YRange.isFullSet()) + return nullptr; // early out if we don't have any information + if (auto Res = XRange.icmpOrInverse(Pred, YRange)) + return replaceInstUsesWith(Cmp, ConstantInt::getBool(Cmp.getType(), *Res)); + if (ICmpInst::isUnsigned(Pred)) { + // Check if this icmp is actually a sign bit check. + const APInt *C; + bool IgnoreTrueIfSigned; + if (!match(Y, m_APInt(C)) || + !isSignBitCheck(Pred, *C, IgnoreTrueIfSigned)) { + if (ICmpInst *Res = canonicalizeSignBitCheck(Pred, X, XRange, YRange)) + return Res; + if (ICmpInst *Res = canonicalizeSignBitCheck( + ICmpInst::getSwappedPredicate(Pred), Y, YRange, XRange)) + return Res; + } + } + return nullptr; +} + // Transform pattern like: // (1 << Y) u<= X or ~(-1 << Y) u< X or ((1 << Y)+(-1)) u< X // (1 << Y) u> X or ~(-1 << Y) u>= X or ((1 << Y)+(-1)) u>= X @@ -7397,6 +7460,9 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) { if (Instruction *Res = canonicalizeICmpPredicate(I)) return Res; + if (Instruction *Res = foldICmpUsingConstantRanges(I)) + return Res; + if (Instruction *Res = foldICmpWithConstant(I)) return Res; diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h index 83e1da98deeda..577a053e59e93 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -668,6 +668,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final Instruction *foldICmpWithCastOp(ICmpInst &ICmp); Instruction *foldICmpWithZextOrSext(ICmpInst &ICmp); + Instruction *foldICmpUsingConstantRanges(ICmpInst &Cmp); Instruction *foldICmpUsingKnownBits(ICmpInst &Cmp); Instruction *foldICmpWithDominatingICmp(ICmpInst &Cmp); Instruction *foldICmpWithConstant(ICmpInst &Cmp); diff --git a/llvm/test/Transforms/InstCombine/add.ll b/llvm/test/Transforms/InstCombine/add.ll index 222f87fa3a5f1..495f99824652d 100644 --- a/llvm/test/Transforms/InstCombine/add.ll +++ b/llvm/test/Transforms/InstCombine/add.ll @@ -3018,6 +3018,32 @@ define i32 @floor_sdiv_wrong_op(i32 %x, i32 %y) { ret i32 %r } +define i32 @floor_sdiv_using_srem_by_8(i32 %x) { +; CHECK-LABEL: @floor_sdiv_using_srem_by_8( +; CHECK-NEXT: [[F:%.*]] = ashr i32 [[X:%.*]], 3 +; CHECK-NEXT: ret i32 [[F]] +; + %d = sdiv i32 %x, 8 + %r = srem i32 %x, 8 + %i = icmp ugt i32 %r, -2147483648 + %s = sext i1 %i to i32 + %f = add i32 %d, %s + ret i32 %f +} + +define i32 @floor_sdiv_using_srem_by_2(i32 %x) { +; CHECK-LABEL: @floor_sdiv_using_srem_by_2( +; CHECK-NEXT: [[F:%.*]] = ashr i32 [[X:%.*]], 1 +; CHECK-NEXT: ret i32 [[F]] +; + %d = sdiv i32 %x, 2 + %r = srem i32 %x, 2 + %i = icmp ugt i32 %r, -2147483648 + %s = sext i1 %i to i32 + %f = add i32 %d, %s + ret i32 %f +} + ; (X s>> (BW - 1)) + (zext (X s> 0)) --> (X s>> (BW - 1)) | (zext (X != 0)) define i8 @signum_i8_i8(i8 %x) { diff --git a/llvm/test/Transforms/InstCombine/icmp-dom.ll b/llvm/test/Transforms/InstCombine/icmp-dom.ll index 3cf3a7af77041..66e9e514a9022 100644 --- a/llvm/test/Transforms/InstCombine/icmp-dom.ll +++ b/llvm/test/Transforms/InstCombine/icmp-dom.ll @@ -381,11 +381,11 @@ falselabel: define i8 @PR48900_alt(i8 %i, ptr %p) { ; CHECK-LABEL: @PR48900_alt( -; CHECK-NEXT: [[SMAX:%.*]] = call i8 @llvm.smax.i8(i8 [[I:%.*]], i8 -127) -; CHECK-NEXT: [[I4:%.*]] = icmp ugt i8 [[SMAX]], -128 +; CHECK-NEXT: [[I4:%.*]] = icmp slt i8 [[I:%.*]], 0 ; CHECK-NEXT: br i1 [[I4]], label [[TRUELABEL:%.*]], label [[FALSELABEL:%.*]] ; CHECK: truelabel: -; CHECK-NEXT: [[UMIN:%.*]] = call i8 @llvm.smin.i8(i8 [[SMAX]], i8 -126) +; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i8 [[I]], -126 +; CHECK-NEXT: [[UMIN:%.*]] = select i1 [[TMP1]], i8 -127, i8 -126 ; CHECK-NEXT: ret i8 [[UMIN]] ; CHECK: falselabel: ; CHECK-NEXT: ret i8 0 diff --git a/llvm/test/Transforms/InstCombine/icmp-srem.ll b/llvm/test/Transforms/InstCombine/icmp-srem.ll new file mode 100644 index 0000000000000..9ab92f15ae7d2 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/icmp-srem.ll @@ -0,0 +1,468 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +define i1 @icmp_ugt_sremsmin_smin(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ugt_sremsmin_smin( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[C:%.*]] = icmp ugt i32 [[X]], -2147483648 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, -2147483648 + %c = icmp ugt i32 %r, -2147483648 + ret i1 %c +} + +define i1 @icmp_ugt_sremsmin_sminp1(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ugt_sremsmin_sminp1( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], -2147483648 +; CHECK-NEXT: [[C:%.*]] = icmp ugt i32 [[R]], -2147483647 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, -2147483648 + %c = icmp ugt i32 %r, -2147483647 + ret i1 %c +} + +define i1 @icmp_ugt_sremsmin_smaxm1(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ugt_sremsmin_smaxm1( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], -2147483648 +; CHECK-NEXT: [[C:%.*]] = icmp ugt i32 [[R]], 2147483646 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, -2147483648 + %c = icmp ugt i32 %r, 2147483646 + ret i1 %c +} + +define i1 @icmp_ugt_sremsmin_smax(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ugt_sremsmin_smax( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[C:%.*]] = icmp ugt i32 [[X]], -2147483648 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, -2147483648 + %c = icmp ugt i32 %r, 2147483647 + ret i1 %c +} + +define i1 @icmp_ult_sremsmin_smin(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ult_sremsmin_smin( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], -2147483648 +; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[R]], -1 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, -2147483648 + %c = icmp ult i32 %r, -2147483648 + ret i1 %c +} + +define i1 @icmp_ult_sremsmin_sminp1(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ult_sremsmin_sminp1( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], -2147483648 +; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[R]], -1 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, -2147483648 + %c = icmp ult i32 %r, -2147483647 + ret i1 %c +} + +define i1 @icmp_ult_sremsmin_sminp2(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ult_sremsmin_sminp2( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], -2147483648 +; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[R]], -2147483646 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, -2147483648 + %c = icmp ult i32 %r, -2147483646 + ret i1 %c +} + +define i1 @icmp_ult_sremsmin_smax(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ult_sremsmin_smax( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], -2147483648 +; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[R]], 2147483647 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, -2147483648 + %c = icmp ult i32 %r, 2147483647 + ret i1 %c +} + +define i1 @icmp_ugt_srem5_smin(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ugt_srem5_smin( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 5 +; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[R]], 0 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 5 + %c = icmp ugt i32 %r, -2147483648 + ret i1 %c +} + +define i1 @icmp_ugt_srem5_m5(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ugt_srem5_m5( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 5 +; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[R]], 0 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 5 + %c = icmp ugt i32 %r, -5 + ret i1 %c +} + +define i1 @icmp_ugt_srem5_m4(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ugt_srem5_m4( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 5 +; CHECK-NEXT: [[C:%.*]] = icmp ugt i32 [[R]], -4 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 5 + %c = icmp ugt i32 %r, -4 + ret i1 %c +} + +define i1 @icmp_ugt_srem5_3(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ugt_srem5_3( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 5 +; CHECK-NEXT: [[C:%.*]] = icmp ugt i32 [[R]], 3 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 5 + %c = icmp ugt i32 %r, 3 + ret i1 %c +} + +define i1 @icmp_ugt_srem5_4(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ugt_srem5_4( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 5 +; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[R]], 0 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 5 + %c = icmp ugt i32 %r, 4 + ret i1 %c +} + +define i1 @icmp_ugt_srem5_smaxm1(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ugt_srem5_smaxm1( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 5 +; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[R]], 0 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 5 + %c = icmp ugt i32 %r, 2147483646 + ret i1 %c +} + +define i1 @icmp_ult_srem5_sminp1(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ult_srem5_sminp1( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 5 +; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[R]], -1 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 5 + %c = icmp ult i32 %r, -2147483647 + ret i1 %c +} + +define i1 @icmp_ult_srem5_m4(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ult_srem5_m4( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 5 +; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[R]], -1 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 5 + %c = icmp ult i32 %r, -4 + ret i1 %c +} + +define i1 @icmp_ult_srem5_m3(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ult_srem5_m3( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 5 +; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[R]], -3 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 5 + %c = icmp ult i32 %r, -3 + ret i1 %c +} + +define i1 @icmp_ult_srem5_4(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ult_srem5_4( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 5 +; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[R]], 4 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 5 + %c = icmp ult i32 %r, 4 + ret i1 %c +} + +define i1 @icmp_ult_srem5_5(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ult_srem5_5( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 5 +; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[R]], -1 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 5 + %c = icmp ult i32 %r, 5 + ret i1 %c +} + +define i1 @icmp_ult_srem5_smax(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ult_srem5_smax( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 5 +; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[R]], -1 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 5 + %c = icmp ult i32 %r, 2147483647 + ret i1 %c +} + +define i1 @icmp_ugt_sremsmax_smin(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ugt_sremsmax_smin( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 2147483647 +; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[R]], 0 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 2147483647 + %c = icmp ugt i32 %r, -2147483648 + ret i1 %c +} + +define i1 @icmp_ugt_sremsmax_sminp1(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ugt_sremsmax_sminp1( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 2147483647 +; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[R]], 0 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 2147483647 + %c = icmp ugt i32 %r, -2147483647 + ret i1 %c +} + +define i1 @icmp_ugt_sremsmax_sminp2(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ugt_sremsmax_sminp2( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 2147483647 +; CHECK-NEXT: [[C:%.*]] = icmp ugt i32 [[R]], -2147483646 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 2147483647 + %c = icmp ugt i32 %r, -2147483646 + ret i1 %c +} + +define i1 @icmp_ugt_sremsmax_smaxm2(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ugt_sremsmax_smaxm2( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 2147483647 +; CHECK-NEXT: [[C:%.*]] = icmp ugt i32 [[R]], 2147483645 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 2147483647 + %c = icmp ugt i32 %r, 2147483645 + ret i1 %c +} + +define i1 @icmp_ugt_sremsmax_smaxm1(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ugt_sremsmax_smaxm1( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 2147483647 +; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[R]], 0 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 2147483647 + %c = icmp ugt i32 %r, 2147483646 + ret i1 %c +} + +define i1 @icmp_ugt_sremsmax_smax(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ugt_sremsmax_smax( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 2147483647 +; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[R]], 0 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 2147483647 + %c = icmp ugt i32 %r, 2147483647 + ret i1 %c +} + +define i1 @icmp_ult_sremsmax_smin(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ult_sremsmax_smin( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 2147483647 +; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[R]], -1 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 2147483647 + %c = icmp ult i32 %r, -2147483648 + ret i1 %c +} + +define i1 @icmp_ult_sremsmax_sminp1(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ult_sremsmax_sminp1( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 2147483647 +; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[R]], -1 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 2147483647 + %c = icmp ult i32 %r, -2147483647 + ret i1 %c +} + +define i1 @icmp_ult_sremsmax_sminp2(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ult_sremsmax_sminp2( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 2147483647 +; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[R]], -1 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 2147483647 + %c = icmp ult i32 %r, -2147483646 + ret i1 %c +} + +define i1 @icmp_ult_sremsmax_sminp3(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ult_sremsmax_sminp3( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 2147483647 +; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[R]], -2147483645 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 2147483647 + %c = icmp ult i32 %r, -2147483645 + ret i1 %c +} + +define i1 @icmp_ult_sremsmax_smaxm1(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ult_sremsmax_smaxm1( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 2147483647 +; CHECK-NEXT: [[C:%.*]] = icmp ult i32 [[R]], 2147483646 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 2147483647 + %c = icmp ult i32 %r, 2147483646 + ret i1 %c +} + +define i1 @icmp_ult_sremsmax_smax(i32 %x) { +; CHECK-LABEL: define i1 @icmp_ult_sremsmax_smax( +; CHECK-SAME: i32 [[X:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 2147483647 +; CHECK-NEXT: [[C:%.*]] = icmp sgt i32 [[R]], -1 +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 2147483647 + %c = icmp ult i32 %r, 2147483647 + ret i1 %c +} + +define i1 @icmp_slt_srem_pos_range(i32 %x, i32 range(i32 999, -2147483648) %y) { +; CHECK-LABEL: define i1 @icmp_slt_srem_pos_range( +; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 999, -2147483648) [[Y:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 1000 +; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[R]], [[Y]] +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 1000 + %c = icmp slt i32 %r, %y + ret i1 %c +} + +define i1 @icmp_sle_srem_pos_range(i32 %x, i32 range(i32 999, -2147483648) %y) { +; CHECK-LABEL: define i1 @icmp_sle_srem_pos_range( +; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 999, -2147483648) [[Y:%.*]]) { +; CHECK-NEXT: ret i1 true +; + %r = srem i32 %x, 1000 + %c = icmp sle i32 %r, %y + ret i1 %c +} + +define i1 @icmp_sgt_srem_pos_range(i32 %x, i32 range(i32 999, -2147483648) %y) { +; CHECK-LABEL: define i1 @icmp_sgt_srem_pos_range( +; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 999, -2147483648) [[Y:%.*]]) { +; CHECK-NEXT: ret i1 false +; + %r = srem i32 %x, 1000 + %c = icmp sgt i32 %r, %y + ret i1 %c +} + +define i1 @icmp_sge_srem_pos_range(i32 %x, i32 range(i32 999, -2147483648) %y) { +; CHECK-LABEL: define i1 @icmp_sge_srem_pos_range( +; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 999, -2147483648) [[Y:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], 1000 +; CHECK-NEXT: [[C:%.*]] = icmp sge i32 [[R]], [[Y]] +; CHECK-NEXT: ret i1 [[C]] +; + %r = srem i32 %x, 1000 + %c = icmp sge i32 %r, %y + ret i1 %c +} + +define i1 @icmp_slt_srem_neg_range(i32 %x, i32 range(i32 -2147483648, -999) %y) { +; CHECK-LABEL: define i1 @icmp_slt_srem_neg_range( +; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 -2147483648, -999) [[Y:%.*]]) { +; CHECK-NEXT: ret i1 false +; + %r = srem i32 %x, 1000 + %c = icmp slt i32 %r, %y + ret i1 %c +} + +define i1 @icmp_sle_srem_neg_range(i32 %x, i32 range(i32 -2147483648, -999) %y) { +; CHECK-LABEL: define i1 @icmp_sle_srem_neg_range( +; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 -2147483648, -999) [[Y:%.*]]) { +; CHECK-NEXT: ret i1 false +; + %r = srem i32 %x, 1000 + %c = icmp sle i32 %r, %y + ret i1 %c +} + +define i1 @icmp_sgt_srem_neg_range(i32 %x, i32 range(i32 -2147483648, -999) %y) { +; CHECK-LABEL: define i1 @icmp_sgt_srem_neg_range( +; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 -2147483648, -999) [[Y:%.*]]) { +; CHECK-NEXT: ret i1 true +; + %r = srem i32 %x, 1000 + %c = icmp sgt i32 %r, %y + ret i1 %c +} + +define i1 @icmp_sge_srem_neg_range(i32 %x, i32 range(i32 -2147483648, -999) %y) { +; CHECK-LABEL: define i1 @icmp_sge_srem_neg_range( +; CHECK-SAME: i32 [[X:%.*]], i32 range(i32 -2147483648, -999) [[Y:%.*]]) { +; CHECK-NEXT: ret i1 true +; + %r = srem i32 %x, 1000 + %c = icmp sge i32 %r, %y + ret i1 %c +} diff --git a/llvm/test/Transforms/InstCombine/smin-icmp.ll b/llvm/test/Transforms/InstCombine/smin-icmp.ll index f05c1698f1a98..dda59cefbc6f1 100644 --- a/llvm/test/Transforms/InstCombine/smin-icmp.ll +++ b/llvm/test/Transforms/InstCombine/smin-icmp.ll @@ -965,9 +965,9 @@ define void @eq_smin_v2i32_constant(<2 x i32> %y) { ; CHECK-NEXT: call void @use_v2i1(<2 x i1> [[CMP4]]) ; CHECK-NEXT: [[CMP5:%.*]] = icmp ult <2 x i32> [[COND]], splat (i32 10) ; CHECK-NEXT: call void @use_v2i1(<2 x i1> [[CMP5]]) -; CHECK-NEXT: [[CMP6:%.*]] = icmp ult <2 x i32> [[COND]], splat (i32 11) +; CHECK-NEXT: [[CMP6:%.*]] = icmp sgt <2 x i32> [[Y]], splat (i32 -1) ; CHECK-NEXT: call void @use_v2i1(<2 x i1> [[CMP6]]) -; CHECK-NEXT: [[CMP7:%.*]] = icmp ugt <2 x i32> [[COND]], splat (i32 10) +; CHECK-NEXT: [[CMP7:%.*]] = icmp slt <2 x i32> [[Y]], zeroinitializer ; CHECK-NEXT: call void @use_v2i1(<2 x i1> [[CMP7]]) ; CHECK-NEXT: [[CMP8:%.*]] = icmp ugt <2 x i32> [[COND]], splat (i32 9) ; CHECK-NEXT: call void @use_v2i1(<2 x i1> [[CMP8]]) @@ -1004,18 +1004,17 @@ define void @eq_smin_v2i32_constant(<2 x i32> %y) { ; icmp pred smin(C1, Y), C2 where C1 < C2 define void @slt_smin_v2i32_constant(<2 x i32> %y) { ; CHECK-LABEL: @slt_smin_v2i32_constant( -; CHECK-NEXT: [[COND:%.*]] = call <2 x i32> @llvm.smin.v2i32(<2 x i32> [[Y:%.*]], <2 x i32> splat (i32 5)) ; CHECK-NEXT: call void @use_v2i1(<2 x i1> splat (i1 true)) ; CHECK-NEXT: call void @use_v2i1(<2 x i1> splat (i1 true)) ; CHECK-NEXT: call void @use_v2i1(<2 x i1> zeroinitializer) ; CHECK-NEXT: call void @use_v2i1(<2 x i1> zeroinitializer) -; CHECK-NEXT: [[CMP5:%.*]] = icmp ult <2 x i32> [[COND]], splat (i32 10) +; CHECK-NEXT: [[CMP5:%.*]] = icmp sgt <2 x i32> [[Y:%.*]], splat (i32 -1) ; CHECK-NEXT: call void @use_v2i1(<2 x i1> [[CMP5]]) -; CHECK-NEXT: [[CMP6:%.*]] = icmp ult <2 x i32> [[COND]], splat (i32 11) +; CHECK-NEXT: [[CMP6:%.*]] = icmp sgt <2 x i32> [[Y]], splat (i32 -1) ; CHECK-NEXT: call void @use_v2i1(<2 x i1> [[CMP6]]) -; CHECK-NEXT: [[CMP7:%.*]] = icmp ugt <2 x i32> [[COND]], splat (i32 10) +; CHECK-NEXT: [[CMP7:%.*]] = icmp slt <2 x i32> [[Y]], zeroinitializer ; CHECK-NEXT: call void @use_v2i1(<2 x i1> [[CMP7]]) -; CHECK-NEXT: [[CMP8:%.*]] = icmp ugt <2 x i32> [[COND]], splat (i32 9) +; CHECK-NEXT: [[CMP8:%.*]] = icmp slt <2 x i32> [[Y]], zeroinitializer ; CHECK-NEXT: call void @use_v2i1(<2 x i1> [[CMP8]]) ; CHECK-NEXT: call void @use_v2i1(<2 x i1> zeroinitializer) ; CHECK-NEXT: call void @use_v2i1(<2 x i1> splat (i1 true)) diff --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp index c390ffea1c352..b4f4ee1a04902 100644 --- a/llvm/unittests/IR/ConstantRangeTest.cpp +++ b/llvm/unittests/IR/ConstantRangeTest.cpp @@ -1694,12 +1694,23 @@ void ICmpTestImpl(CmpInst::Predicate Pred) { EnumerateTwoInterestingConstantRanges( [&](const ConstantRange &CR1, const ConstantRange &CR2) { bool Exhaustive = true; + bool ExhaustiveInverse = true; ForeachNumInConstantRange(CR1, [&](const APInt &N1) { ForeachNumInConstantRange(CR2, [&](const APInt &N2) { - Exhaustive &= ICmpInst::compare(N1, N2, Pred); + bool Res = ICmpInst::compare(N1, N2, Pred); + Exhaustive &= Res; + ExhaustiveInverse &= !Res; }); }); + + std::optional ExhaustiveOrInverse; + if (Exhaustive) // Expect true if Exhaustive && ExhaustiveInverse. + ExhaustiveOrInverse = true; + else if (ExhaustiveInverse) + ExhaustiveOrInverse = false; + EXPECT_EQ(CR1.icmp(Pred, CR2), Exhaustive); + EXPECT_EQ(CR1.icmpOrInverse(Pred, CR2), ExhaustiveOrInverse); }); }