diff --git a/llvm/include/llvm/IR/ConstantRange.h b/llvm/include/llvm/IR/ConstantRange.h --- a/llvm/include/llvm/IR/ConstantRange.h +++ b/llvm/include/llvm/IR/ConstantRange.h @@ -128,6 +128,26 @@ /// NOTE: false does not mean that inverse predicate holds! bool icmp(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 + areInsensitiveToSignednessOfICmpPredicate(const ConstantRange &CR1, + const ConstantRange &CR2); + + /// Return true iff CR1 ult CR2 is equivalent to CR1 sgt CR2. + /// Does not depend on strictness/direction of the predicate. + static bool + areInsensitiveToSignednessOfSwappedICmpPredicate(const ConstantRange &CR1, + const ConstantRange &CR2); + + /// If the comparison between constant ranges this and Other + /// is insensitive to the signedness of the comparison predicate, + /// return a predicate equivalent to \p Pred, with flipped signedness + /// (i.e. unsigned instead of signed or vice versa), and maybe swapped. + CmpInst::Predicate + getEquivalentPredWithFlippedSignedness(CmpInst::Predicate Pred, + const ConstantRange &Other) const; + /// Produce the largest range containing all X such that "X BinOp Y" is /// guaranteed not to wrap (overflow) for *all* Y in Other. However, there may /// be *some* Y in Other for which additional X not contained in the result diff --git a/llvm/lib/IR/ConstantRange.cpp b/llvm/lib/IR/ConstantRange.cpp --- a/llvm/lib/IR/ConstantRange.cpp +++ b/llvm/lib/IR/ConstantRange.cpp @@ -147,6 +147,65 @@ return makeAllowedICmpRegion(Pred, C); } +bool ConstantRange::areInsensitiveToSignednessOfICmpPredicate( + const ConstantRange &CR1, const ConstantRange &CR2) { + if (CR1.isEmptySet() || CR2.isEmptySet()) + return true; + + if (CR1.isFullSet() || CR2.isFullSet()) + return false; + if (CR1.isWrappedSet() || CR2.isWrappedSet()) + return false; + if (CR1.isSignWrappedSet() || CR2.isSignWrappedSet()) + return false; + + if (CR1.contains(CR2) || CR2.contains(CR1)) + return true; + + if (CR1.isAllNonNegative() && CR2.isAllNonNegative()) + return true; + + if (CR1.isAllNegative() && CR2.isAllNegative()) + return !CR1.isUpperWrapped() || !CR2.isUpperWrapped(); + + return false; +} + +bool ConstantRange::areInsensitiveToSignednessOfSwappedICmpPredicate( + const ConstantRange &CR1, const ConstantRange &CR2) { + if (CR1.isEmptySet() || CR2.isEmptySet()) + return true; + if (CR1.isFullSet() || CR2.isFullSet()) + return false; + + bool TrulyEquivalent = true; + + for (auto N1 : {CR1.getUnsignedMin(), CR1.getUnsignedMax(), + CR1.getSignedMin(), CR1.getSignedMax()}) + for (auto N2 : {CR2.getUnsignedMin(), CR2.getUnsignedMax(), + CR2.getSignedMin(), CR2.getSignedMax()}) + TrulyEquivalent &= N1.slt(N2) == N1.ugt(N2); + + return TrulyEquivalent; +} + +CmpInst::Predicate ConstantRange::getEquivalentPredWithFlippedSignedness( + CmpInst::Predicate Pred, const ConstantRange &Other) const { + assert(CmpInst::isIntPredicate(Pred) && CmpInst::isRelational(Pred) && + "Only for relational integer predicates!"); + + CmpInst::Predicate FlippedSignednessPred = + CmpInst::getFlippedSignednessPredicate(Pred); + + if (areInsensitiveToSignednessOfICmpPredicate(*this, Other)) + return FlippedSignednessPred; + + if (areInsensitiveToSignednessOfSwappedICmpPredicate(*this, Other)) + return CmpInst::getSwappedPredicate(FlippedSignednessPred); + + return CmpInst::Predicate::BAD_ICMP_PREDICATE; +} + bool ConstantRange::getEquivalentICmp(CmpInst::Predicate &Pred, APInt &RHS) const { bool Success = false; diff --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp --- a/llvm/unittests/IR/ConstantRangeTest.cpp +++ b/llvm/unittests/IR/ConstantRangeTest.cpp @@ -2505,4 +2505,86 @@ [](const APInt &N) { return ~N; }, PreferSmallest); } -} // anonymous namespace +template +void testConstantRangeICmpPredEquivalence(ICmpInst::Predicate SrcPred, T Func) { + unsigned Bits = 4; + EnumerateTwoConstantRanges(Bits, [&](const ConstantRange &CR1, + const ConstantRange &CR2) { + ICmpInst::Predicate TgtPred; + bool ExpectedEquivalent; + std::tie(TgtPred, ExpectedEquivalent) = Func(CR1, CR2, SrcPred); + if (TgtPred == CmpInst::Predicate::BAD_ICMP_PREDICATE) + return; + bool TrulyEquivalent = true; + ForeachNumInConstantRange(CR1, [&](const APInt &N1) { + if (!TrulyEquivalent) + return; + ForeachNumInConstantRange(CR2, [&](const APInt &N2) { + if (!TrulyEquivalent) + return; + TrulyEquivalent &= ICmpInst::compare(N1, N2, SrcPred) == + ICmpInst::compare(N1, N2, TgtPred); + }); + }); + ASSERT_EQ(TrulyEquivalent, ExpectedEquivalent); + }); +} + +TEST_F(ConstantRangeTest, areInsensitiveToSignednessOfICmpPredicate) { + for (auto Pred : seq((unsigned)ICmpInst::Predicate::FIRST_ICMP_PREDICATE, + ICmpInst::Predicate::LAST_ICMP_PREDICATE + 1)) { + if (ICmpInst::isEquality((ICmpInst::Predicate)Pred)) + continue; + ICmpInst::Predicate FlippedSignednessPred = + ICmpInst::getFlippedSignednessPredicate((ICmpInst::Predicate)Pred); + testConstantRangeICmpPredEquivalence( + (ICmpInst::Predicate)Pred, + [FlippedSignednessPred](const ConstantRange &CR1, + const ConstantRange &CR2, + ICmpInst::Predicate SrcPred) { + return std::make_pair( + FlippedSignednessPred, + ConstantRange::areInsensitiveToSignednessOfICmpPredicate(CR1, + CR2)); + }); + } +} + +TEST_F(ConstantRangeTest, areInsensitiveToSignednessOfSwappedICmpPredicate) { + for (auto Pred : seq((unsigned)ICmpInst::Predicate::FIRST_ICMP_PREDICATE, + ICmpInst::Predicate::LAST_ICMP_PREDICATE + 1)) { + if (ICmpInst::isEquality((ICmpInst::Predicate)Pred)) + continue; + ICmpInst::Predicate FlippedSignednessPred = + ICmpInst::getFlippedSignednessPredicate((ICmpInst::Predicate)Pred); + ICmpInst::Predicate SwappedFlippedSignednessPred = + ICmpInst::getSwappedPredicate(FlippedSignednessPred); + testConstantRangeICmpPredEquivalence( + (ICmpInst::Predicate)Pred, + [SwappedFlippedSignednessPred](const ConstantRange &CR1, + const ConstantRange &CR2, + ICmpInst::Predicate SrcPred) { + return std::make_pair( + SwappedFlippedSignednessPred, + ConstantRange::areInsensitiveToSignednessOfSwappedICmpPredicate( + CR1, CR2)); + }); + } +} + +TEST_F(ConstantRangeTest, getEquivalentPredWithFlippedSignedness) { + for (auto Pred : seq((unsigned)ICmpInst::Predicate::FIRST_ICMP_PREDICATE, + ICmpInst::Predicate::LAST_ICMP_PREDICATE + 1)) { + if (ICmpInst::isEquality((ICmpInst::Predicate)Pred)) + continue; + testConstantRangeICmpPredEquivalence( + (ICmpInst::Predicate)Pred, + [](const ConstantRange &CR1, const ConstantRange &CR2, + ICmpInst::Predicate SrcPred) { + return std::make_pair( + CR1.getEquivalentPredWithFlippedSignedness(SrcPred, CR2), true); + }); + } +} + +} // anonymous namespace