diff --git a/llvm/lib/IR/ConstantFPRange.cpp b/llvm/lib/IR/ConstantFPRange.cpp index 74c9797d969f9d..d3c89daa9ce148 100644 --- a/llvm/lib/IR/ConstantFPRange.cpp +++ b/llvm/lib/IR/ConstantFPRange.cpp @@ -221,8 +221,48 @@ ConstantFPRange::makeAllowedFCmpRegion(FCmpInst::Predicate Pred, ConstantFPRange ConstantFPRange::makeSatisfyingFCmpRegion(FCmpInst::Predicate Pred, const ConstantFPRange &Other) { - // TODO - return getEmpty(Other.getSemantics()); + if (Other.isEmptySet()) + return getFull(Other.getSemantics()); + if (Other.containsNaN() && FCmpInst::isOrdered(Pred)) + return getEmpty(Other.getSemantics()); + if (Other.isNaNOnly() && FCmpInst::isUnordered(Pred)) + return getFull(Other.getSemantics()); + + switch (Pred) { + case FCmpInst::FCMP_TRUE: + return getFull(Other.getSemantics()); + case FCmpInst::FCMP_FALSE: + return getEmpty(Other.getSemantics()); + case FCmpInst::FCMP_ORD: + return getNonNaN(Other.getSemantics()); + case FCmpInst::FCMP_UNO: + return getNaNOnly(Other.getSemantics(), /*MayBeQNaN=*/true, + /*MayBeSNaN=*/true); + case FCmpInst::FCMP_OEQ: + case FCmpInst::FCMP_UEQ: + return setNaNField(Other.isSingleElement(/*ExcludesNaN=*/true) || + ((Other.classify() & ~fcNan) == fcZero) + ? extendZeroIfEqual(Other, Pred) + : getEmpty(Other.getSemantics()), + Pred); + case FCmpInst::FCMP_ONE: + case FCmpInst::FCMP_UNE: + return getEmpty(Other.getSemantics()); + case FCmpInst::FCMP_OLT: + case FCmpInst::FCMP_OLE: + case FCmpInst::FCMP_ULT: + case FCmpInst::FCMP_ULE: + return setNaNField( + extendZeroIfEqual(makeLessThan(Other.getLower(), Pred), Pred), Pred); + case FCmpInst::FCMP_OGT: + case FCmpInst::FCMP_OGE: + case FCmpInst::FCMP_UGT: + case FCmpInst::FCMP_UGE: + return setNaNField( + extendZeroIfEqual(makeGreaterThan(Other.getUpper(), Pred), Pred), Pred); + default: + llvm_unreachable("Unexpected predicate"); + } } std::optional diff --git a/llvm/unittests/IR/ConstantFPRangeTest.cpp b/llvm/unittests/IR/ConstantFPRangeTest.cpp index 3c6e468b05bfda..3ce64c64447e21 100644 --- a/llvm/unittests/IR/ConstantFPRangeTest.cpp +++ b/llvm/unittests/IR/ConstantFPRangeTest.cpp @@ -564,4 +564,183 @@ TEST_F(ConstantFPRangeTest, makeAllowedFCmpRegion) { #endif } +TEST_F(ConstantFPRangeTest, makeSatisfyingFCmpRegion) { + EXPECT_EQ(ConstantFPRange::makeSatisfyingFCmpRegion( + FCmpInst::FCMP_OLE, + ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0))), + ConstantFPRange::getNonNaN(APFloat::getInf(Sem, /*Negative=*/true), + APFloat(1.0))); + EXPECT_EQ( + ConstantFPRange::makeSatisfyingFCmpRegion( + FCmpInst::FCMP_OLT, ConstantFPRange::getNonNaN( + APFloat::getSmallest(Sem, /*Negative=*/false), + APFloat::getInf(Sem, /*Negative=*/false))), + ConstantFPRange::getNonNaN(APFloat::getInf(Sem, /*Negative=*/true), + APFloat::getZero(Sem, /*Negative=*/false))); + EXPECT_EQ( + ConstantFPRange::makeSatisfyingFCmpRegion( + FCmpInst::FCMP_OGT, ConstantFPRange::getNonNaN( + APFloat::getZero(Sem, /*Negative=*/true), + APFloat::getZero(Sem, /*Negative=*/false))), + ConstantFPRange::getNonNaN(APFloat::getSmallest(Sem, /*Negative=*/false), + APFloat::getInf(Sem, /*Negative=*/false))); + EXPECT_EQ(ConstantFPRange::makeSatisfyingFCmpRegion( + FCmpInst::FCMP_OGE, + ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0))), + ConstantFPRange::getNonNaN( + APFloat(2.0), APFloat::getInf(Sem, /*Negative=*/false))); + EXPECT_EQ(ConstantFPRange::makeSatisfyingFCmpRegion( + FCmpInst::FCMP_OEQ, + ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0))), + ConstantFPRange::getEmpty(Sem)); + EXPECT_EQ(ConstantFPRange::makeSatisfyingFCmpRegion( + FCmpInst::FCMP_OEQ, + ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(1.0))), + ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(1.0))); + +#if defined(EXPENSIVE_CHECKS) + for (auto Pred : FCmpInst::predicates()) { + EnumerateConstantFPRanges( + [Pred](const ConstantFPRange &CR) { + ConstantFPRange Res = + ConstantFPRange::makeSatisfyingFCmpRegion(Pred, CR); + // Super set of the optimal set excluding NaNs + ConstantFPRange SuperSet(CR.getSemantics()); + bool ContainsSNaN = false; + bool ContainsQNaN = false; + unsigned NonNaNValsInOptimalSet = 0; + EnumerateValuesInConstantFPRange( + ConstantFPRange::getFull(CR.getSemantics()), + [&](const APFloat &V) { + if (AnyOfValueInConstantFPRange( + CR, + [&](const APFloat &U) { + return !FCmpInst::compare(V, U, Pred); + }, + /*IgnoreNaNPayload=*/true)) { + EXPECT_FALSE(Res.contains(V)) + << "Wrong result for makeSatisfyingFCmpRegion(" << Pred + << ", " << CR << "). The result " << Res + << " should not contain " << V; + } else { + if (V.isNaN()) { + if (V.isSignaling()) + ContainsSNaN = true; + else + ContainsQNaN = true; + } else { + SuperSet = SuperSet.unionWith(ConstantFPRange(V)); + ++NonNaNValsInOptimalSet; + } + } + }, + /*IgnoreNaNPayload=*/true); + + // Check optimality + + // The usefullness of making the result optimal for one/une is + // questionable. + if (Pred == FCmpInst::FCMP_ONE || Pred == FCmpInst::FCMP_UNE) + return; + + EXPECT_FALSE(ContainsSNaN && !Res.containsSNaN()) + << "Suboptimal result for makeSatisfyingFCmpRegion(" << Pred + << ", " << CR << "), should contain SNaN, but got " << Res; + EXPECT_FALSE(ContainsQNaN && !Res.containsQNaN()) + << "Suboptimal result for makeSatisfyingFCmpRegion(" << Pred + << ", " << CR << "), should contain QNaN, but got " << Res; + + // We only care about the cases where the result is representable by + // ConstantFPRange. + unsigned NonNaNValsInSuperSet = 0; + EnumerateValuesInConstantFPRange( + SuperSet, + [&](const APFloat &V) { + if (!V.isNaN()) + ++NonNaNValsInSuperSet; + }, + /*IgnoreNaNPayload=*/true); + + if (NonNaNValsInSuperSet == NonNaNValsInOptimalSet) { + ConstantFPRange Optimal = + ConstantFPRange(SuperSet.getLower(), SuperSet.getUpper(), + ContainsQNaN, ContainsSNaN); + EXPECT_EQ(Res, Optimal) + << "Suboptimal result for makeSatisfyingFCmpRegion(" << Pred + << ", " << CR << ")"; + } + }, + /*Exhaustive=*/false); + } +#endif +} + +TEST_F(ConstantFPRangeTest, fcmp) { + std::vector InterestingRanges; + const fltSemantics &Sem = APFloat::Float8E4M3(); + auto FpImm = [&](double V) { + bool ignored; + APFloat APF(V); + APF.convert(Sem, APFloat::rmNearestTiesToEven, &ignored); + return APF; + }; + + InterestingRanges.push_back(ConstantFPRange::getEmpty(Sem)); + InterestingRanges.push_back(ConstantFPRange::getFull(Sem)); + InterestingRanges.push_back(ConstantFPRange::getFinite(Sem)); + InterestingRanges.push_back(ConstantFPRange(FpImm(1.0))); + InterestingRanges.push_back( + ConstantFPRange(APFloat::getZero(Sem, /*Negative=*/false))); + InterestingRanges.push_back( + ConstantFPRange(APFloat::getZero(Sem, /*Negative=*/true))); + InterestingRanges.push_back( + ConstantFPRange(APFloat::getInf(Sem, /*Negative=*/false))); + InterestingRanges.push_back( + ConstantFPRange(APFloat::getInf(Sem, /*Negative=*/true))); + InterestingRanges.push_back( + ConstantFPRange(APFloat::getSmallest(Sem, /*Negative=*/false))); + InterestingRanges.push_back( + ConstantFPRange(APFloat::getSmallest(Sem, /*Negative=*/true))); + InterestingRanges.push_back( + ConstantFPRange(APFloat::getLargest(Sem, /*Negative=*/false))); + InterestingRanges.push_back( + ConstantFPRange(APFloat::getLargest(Sem, /*Negative=*/true))); + InterestingRanges.push_back( + ConstantFPRange::getNaNOnly(Sem, /*MayBeQNaN=*/true, /*MayBeSNaN=*/true)); + InterestingRanges.push_back( + ConstantFPRange::getNonNaN(FpImm(0.0), FpImm(1.0))); + InterestingRanges.push_back( + ConstantFPRange::getNonNaN(FpImm(2.0), FpImm(3.0))); + InterestingRanges.push_back( + ConstantFPRange::getNonNaN(FpImm(-1.0), FpImm(1.0))); + InterestingRanges.push_back( + ConstantFPRange::getNonNaN(FpImm(-1.0), FpImm(-0.0))); + InterestingRanges.push_back(ConstantFPRange::getNonNaN( + APFloat::getInf(Sem, /*Negative=*/true), FpImm(-1.0))); + InterestingRanges.push_back(ConstantFPRange::getNonNaN( + FpImm(1.0), APFloat::getInf(Sem, /*Negative=*/false))); + + for (auto &LHS : InterestingRanges) { + for (auto &RHS : InterestingRanges) { + for (auto Pred : FCmpInst::predicates()) { + if (LHS.fcmp(Pred, RHS)) { + EnumerateValuesInConstantFPRange( + LHS, + [&](const APFloat &LHSC) { + EnumerateValuesInConstantFPRange( + RHS, + [&](const APFloat &RHSC) { + EXPECT_TRUE(FCmpInst::compare(LHSC, RHSC, Pred)) + << LHS << " " << Pred << " " << RHS + << " doesn't hold"; + }, + /*IgnoreNaNPayload=*/true); + }, + /*IgnoreNaNPayload=*/true); + } + } + } + } +} + } // anonymous namespace