Skip to content

Commit

Permalink
[ConstantFPRange] Implement `ConstantFPRange::makeSatisfyingFCmpRegio…
Browse files Browse the repository at this point in the history
…n` (#110891)

This patch adds support for `ConstantFPRange::makeSatisfyingFCmpRegion`.
We only check the optimality for cases where the result can be
represented by a ConstantFPRange.

This patch also adds some tests for `ConstantFPRange::fcmp` because it
depends on `makeSatisfyingFCmpRegion`. Unfortunately we cannot
exhaustively test this function due to time limit. I just pick some
interesting ranges instead.
  • Loading branch information
dtcxzyw authored Oct 8, 2024
1 parent e98875a commit 4647a46
Show file tree
Hide file tree
Showing 2 changed files with 221 additions and 2 deletions.
44 changes: 42 additions & 2 deletions llvm/lib/IR/ConstantFPRange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ConstantFPRange>
Expand Down
179 changes: 179 additions & 0 deletions llvm/unittests/IR/ConstantFPRangeTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ConstantFPRange> 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

0 comments on commit 4647a46

Please sign in to comment.