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,28 @@ /// 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 sge CR2. + /// Does not depend on strictness/direction of the predicate. + static bool + areInsensitiveToSignednessOfInvertedICmpPredicate(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 inverted, + /// otherwise returns CmpInst::Predicate::BAD_ICMP_PREDICATE. + static CmpInst::Predicate + getEquivalentPredWithFlippedSignedness(CmpInst::Predicate Pred, + const ConstantRange &CR1, + const ConstantRange &CR2); + /// 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,42 @@ return makeAllowedICmpRegion(Pred, C); } +bool ConstantRange::areInsensitiveToSignednessOfICmpPredicate( + const ConstantRange &CR1, const ConstantRange &CR2) { + if (CR1.isEmptySet() || CR2.isEmptySet()) + return true; + + return (CR1.isAllNonNegative() && CR2.isAllNonNegative()) || + (CR1.isAllNegative() && CR2.isAllNegative()); +} + +bool ConstantRange::areInsensitiveToSignednessOfInvertedICmpPredicate( + const ConstantRange &CR1, const ConstantRange &CR2) { + if (CR1.isEmptySet() || CR2.isEmptySet()) + return true; + + return (CR1.isAllNonNegative() && CR2.isAllNegative()) || + (CR1.isAllNegative() && CR2.isAllNonNegative()); +} + +CmpInst::Predicate ConstantRange::getEquivalentPredWithFlippedSignedness( + CmpInst::Predicate Pred, const ConstantRange &CR1, + const ConstantRange &CR2) { + assert(CmpInst::isIntPredicate(Pred) && CmpInst::isRelational(Pred) && + "Only for relational integer predicates!"); + + CmpInst::Predicate FlippedSignednessPred = + CmpInst::getFlippedSignednessPredicate(Pred); + + if (areInsensitiveToSignednessOfICmpPredicate(CR1, CR2)) + return FlippedSignednessPred; + + if (areInsensitiveToSignednessOfInvertedICmpPredicate(CR1, CR2)) + return CmpInst::getInversePredicate(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,80 @@ [](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); + 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_inclusive(ICmpInst::Predicate::FIRST_ICMP_PREDICATE, + ICmpInst::Predicate::LAST_ICMP_PREDICATE)) { + if (ICmpInst::isEquality(Pred)) + continue; + ICmpInst::Predicate FlippedSignednessPred = + ICmpInst::getFlippedSignednessPredicate(Pred); + testConstantRangeICmpPredEquivalence(Pred, [FlippedSignednessPred]( + const ConstantRange &CR1, + const ConstantRange &CR2) { + return std::make_pair( + FlippedSignednessPred, + ConstantRange::areInsensitiveToSignednessOfICmpPredicate(CR1, CR2)); + }); + } +} + +TEST_F(ConstantRangeTest, areInsensitiveToSignednessOfInvertedICmpPredicate) { + for (auto Pred : seq_inclusive(ICmpInst::Predicate::FIRST_ICMP_PREDICATE, + ICmpInst::Predicate::LAST_ICMP_PREDICATE)) { + if (ICmpInst::isEquality(Pred)) + continue; + ICmpInst::Predicate InvertedFlippedSignednessPred = + ICmpInst::getInversePredicate( + ICmpInst::getFlippedSignednessPredicate(Pred)); + testConstantRangeICmpPredEquivalence( + Pred, [InvertedFlippedSignednessPred](const ConstantRange &CR1, + const ConstantRange &CR2) { + return std::make_pair( + InvertedFlippedSignednessPred, + ConstantRange::areInsensitiveToSignednessOfInvertedICmpPredicate( + CR1, CR2)); + }); + } +} + +TEST_F(ConstantRangeTest, getEquivalentPredWithFlippedSignedness) { + for (auto Pred : seq_inclusive(ICmpInst::Predicate::FIRST_ICMP_PREDICATE, + ICmpInst::Predicate::LAST_ICMP_PREDICATE)) { + if (ICmpInst::isEquality(Pred)) + continue; + testConstantRangeICmpPredEquivalence( + Pred, [Pred](const ConstantRange &CR1, const ConstantRange &CR2) { + return std::make_pair( + ConstantRange::getEquivalentPredWithFlippedSignedness(Pred, CR1, + CR2), + /*ExpectedEquivalent=*/true); + }); + } +} + +} // anonymous namespace