Skip to content

Commit 4647a46

Browse files
authored
[ConstantFPRange] Implement ConstantFPRange::makeSatisfyingFCmpRegion (#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.
1 parent e98875a commit 4647a46

File tree

2 files changed

+221
-2
lines changed

2 files changed

+221
-2
lines changed

llvm/lib/IR/ConstantFPRange.cpp

+42-2
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,48 @@ ConstantFPRange::makeAllowedFCmpRegion(FCmpInst::Predicate Pred,
221221
ConstantFPRange
222222
ConstantFPRange::makeSatisfyingFCmpRegion(FCmpInst::Predicate Pred,
223223
const ConstantFPRange &Other) {
224-
// TODO
225-
return getEmpty(Other.getSemantics());
224+
if (Other.isEmptySet())
225+
return getFull(Other.getSemantics());
226+
if (Other.containsNaN() && FCmpInst::isOrdered(Pred))
227+
return getEmpty(Other.getSemantics());
228+
if (Other.isNaNOnly() && FCmpInst::isUnordered(Pred))
229+
return getFull(Other.getSemantics());
230+
231+
switch (Pred) {
232+
case FCmpInst::FCMP_TRUE:
233+
return getFull(Other.getSemantics());
234+
case FCmpInst::FCMP_FALSE:
235+
return getEmpty(Other.getSemantics());
236+
case FCmpInst::FCMP_ORD:
237+
return getNonNaN(Other.getSemantics());
238+
case FCmpInst::FCMP_UNO:
239+
return getNaNOnly(Other.getSemantics(), /*MayBeQNaN=*/true,
240+
/*MayBeSNaN=*/true);
241+
case FCmpInst::FCMP_OEQ:
242+
case FCmpInst::FCMP_UEQ:
243+
return setNaNField(Other.isSingleElement(/*ExcludesNaN=*/true) ||
244+
((Other.classify() & ~fcNan) == fcZero)
245+
? extendZeroIfEqual(Other, Pred)
246+
: getEmpty(Other.getSemantics()),
247+
Pred);
248+
case FCmpInst::FCMP_ONE:
249+
case FCmpInst::FCMP_UNE:
250+
return getEmpty(Other.getSemantics());
251+
case FCmpInst::FCMP_OLT:
252+
case FCmpInst::FCMP_OLE:
253+
case FCmpInst::FCMP_ULT:
254+
case FCmpInst::FCMP_ULE:
255+
return setNaNField(
256+
extendZeroIfEqual(makeLessThan(Other.getLower(), Pred), Pred), Pred);
257+
case FCmpInst::FCMP_OGT:
258+
case FCmpInst::FCMP_OGE:
259+
case FCmpInst::FCMP_UGT:
260+
case FCmpInst::FCMP_UGE:
261+
return setNaNField(
262+
extendZeroIfEqual(makeGreaterThan(Other.getUpper(), Pred), Pred), Pred);
263+
default:
264+
llvm_unreachable("Unexpected predicate");
265+
}
226266
}
227267

228268
std::optional<ConstantFPRange>

llvm/unittests/IR/ConstantFPRangeTest.cpp

+179
Original file line numberDiff line numberDiff line change
@@ -564,4 +564,183 @@ TEST_F(ConstantFPRangeTest, makeAllowedFCmpRegion) {
564564
#endif
565565
}
566566

567+
TEST_F(ConstantFPRangeTest, makeSatisfyingFCmpRegion) {
568+
EXPECT_EQ(ConstantFPRange::makeSatisfyingFCmpRegion(
569+
FCmpInst::FCMP_OLE,
570+
ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0))),
571+
ConstantFPRange::getNonNaN(APFloat::getInf(Sem, /*Negative=*/true),
572+
APFloat(1.0)));
573+
EXPECT_EQ(
574+
ConstantFPRange::makeSatisfyingFCmpRegion(
575+
FCmpInst::FCMP_OLT, ConstantFPRange::getNonNaN(
576+
APFloat::getSmallest(Sem, /*Negative=*/false),
577+
APFloat::getInf(Sem, /*Negative=*/false))),
578+
ConstantFPRange::getNonNaN(APFloat::getInf(Sem, /*Negative=*/true),
579+
APFloat::getZero(Sem, /*Negative=*/false)));
580+
EXPECT_EQ(
581+
ConstantFPRange::makeSatisfyingFCmpRegion(
582+
FCmpInst::FCMP_OGT, ConstantFPRange::getNonNaN(
583+
APFloat::getZero(Sem, /*Negative=*/true),
584+
APFloat::getZero(Sem, /*Negative=*/false))),
585+
ConstantFPRange::getNonNaN(APFloat::getSmallest(Sem, /*Negative=*/false),
586+
APFloat::getInf(Sem, /*Negative=*/false)));
587+
EXPECT_EQ(ConstantFPRange::makeSatisfyingFCmpRegion(
588+
FCmpInst::FCMP_OGE,
589+
ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0))),
590+
ConstantFPRange::getNonNaN(
591+
APFloat(2.0), APFloat::getInf(Sem, /*Negative=*/false)));
592+
EXPECT_EQ(ConstantFPRange::makeSatisfyingFCmpRegion(
593+
FCmpInst::FCMP_OEQ,
594+
ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0))),
595+
ConstantFPRange::getEmpty(Sem));
596+
EXPECT_EQ(ConstantFPRange::makeSatisfyingFCmpRegion(
597+
FCmpInst::FCMP_OEQ,
598+
ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(1.0))),
599+
ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(1.0)));
600+
601+
#if defined(EXPENSIVE_CHECKS)
602+
for (auto Pred : FCmpInst::predicates()) {
603+
EnumerateConstantFPRanges(
604+
[Pred](const ConstantFPRange &CR) {
605+
ConstantFPRange Res =
606+
ConstantFPRange::makeSatisfyingFCmpRegion(Pred, CR);
607+
// Super set of the optimal set excluding NaNs
608+
ConstantFPRange SuperSet(CR.getSemantics());
609+
bool ContainsSNaN = false;
610+
bool ContainsQNaN = false;
611+
unsigned NonNaNValsInOptimalSet = 0;
612+
EnumerateValuesInConstantFPRange(
613+
ConstantFPRange::getFull(CR.getSemantics()),
614+
[&](const APFloat &V) {
615+
if (AnyOfValueInConstantFPRange(
616+
CR,
617+
[&](const APFloat &U) {
618+
return !FCmpInst::compare(V, U, Pred);
619+
},
620+
/*IgnoreNaNPayload=*/true)) {
621+
EXPECT_FALSE(Res.contains(V))
622+
<< "Wrong result for makeSatisfyingFCmpRegion(" << Pred
623+
<< ", " << CR << "). The result " << Res
624+
<< " should not contain " << V;
625+
} else {
626+
if (V.isNaN()) {
627+
if (V.isSignaling())
628+
ContainsSNaN = true;
629+
else
630+
ContainsQNaN = true;
631+
} else {
632+
SuperSet = SuperSet.unionWith(ConstantFPRange(V));
633+
++NonNaNValsInOptimalSet;
634+
}
635+
}
636+
},
637+
/*IgnoreNaNPayload=*/true);
638+
639+
// Check optimality
640+
641+
// The usefullness of making the result optimal for one/une is
642+
// questionable.
643+
if (Pred == FCmpInst::FCMP_ONE || Pred == FCmpInst::FCMP_UNE)
644+
return;
645+
646+
EXPECT_FALSE(ContainsSNaN && !Res.containsSNaN())
647+
<< "Suboptimal result for makeSatisfyingFCmpRegion(" << Pred
648+
<< ", " << CR << "), should contain SNaN, but got " << Res;
649+
EXPECT_FALSE(ContainsQNaN && !Res.containsQNaN())
650+
<< "Suboptimal result for makeSatisfyingFCmpRegion(" << Pred
651+
<< ", " << CR << "), should contain QNaN, but got " << Res;
652+
653+
// We only care about the cases where the result is representable by
654+
// ConstantFPRange.
655+
unsigned NonNaNValsInSuperSet = 0;
656+
EnumerateValuesInConstantFPRange(
657+
SuperSet,
658+
[&](const APFloat &V) {
659+
if (!V.isNaN())
660+
++NonNaNValsInSuperSet;
661+
},
662+
/*IgnoreNaNPayload=*/true);
663+
664+
if (NonNaNValsInSuperSet == NonNaNValsInOptimalSet) {
665+
ConstantFPRange Optimal =
666+
ConstantFPRange(SuperSet.getLower(), SuperSet.getUpper(),
667+
ContainsQNaN, ContainsSNaN);
668+
EXPECT_EQ(Res, Optimal)
669+
<< "Suboptimal result for makeSatisfyingFCmpRegion(" << Pred
670+
<< ", " << CR << ")";
671+
}
672+
},
673+
/*Exhaustive=*/false);
674+
}
675+
#endif
676+
}
677+
678+
TEST_F(ConstantFPRangeTest, fcmp) {
679+
std::vector<ConstantFPRange> InterestingRanges;
680+
const fltSemantics &Sem = APFloat::Float8E4M3();
681+
auto FpImm = [&](double V) {
682+
bool ignored;
683+
APFloat APF(V);
684+
APF.convert(Sem, APFloat::rmNearestTiesToEven, &ignored);
685+
return APF;
686+
};
687+
688+
InterestingRanges.push_back(ConstantFPRange::getEmpty(Sem));
689+
InterestingRanges.push_back(ConstantFPRange::getFull(Sem));
690+
InterestingRanges.push_back(ConstantFPRange::getFinite(Sem));
691+
InterestingRanges.push_back(ConstantFPRange(FpImm(1.0)));
692+
InterestingRanges.push_back(
693+
ConstantFPRange(APFloat::getZero(Sem, /*Negative=*/false)));
694+
InterestingRanges.push_back(
695+
ConstantFPRange(APFloat::getZero(Sem, /*Negative=*/true)));
696+
InterestingRanges.push_back(
697+
ConstantFPRange(APFloat::getInf(Sem, /*Negative=*/false)));
698+
InterestingRanges.push_back(
699+
ConstantFPRange(APFloat::getInf(Sem, /*Negative=*/true)));
700+
InterestingRanges.push_back(
701+
ConstantFPRange(APFloat::getSmallest(Sem, /*Negative=*/false)));
702+
InterestingRanges.push_back(
703+
ConstantFPRange(APFloat::getSmallest(Sem, /*Negative=*/true)));
704+
InterestingRanges.push_back(
705+
ConstantFPRange(APFloat::getLargest(Sem, /*Negative=*/false)));
706+
InterestingRanges.push_back(
707+
ConstantFPRange(APFloat::getLargest(Sem, /*Negative=*/true)));
708+
InterestingRanges.push_back(
709+
ConstantFPRange::getNaNOnly(Sem, /*MayBeQNaN=*/true, /*MayBeSNaN=*/true));
710+
InterestingRanges.push_back(
711+
ConstantFPRange::getNonNaN(FpImm(0.0), FpImm(1.0)));
712+
InterestingRanges.push_back(
713+
ConstantFPRange::getNonNaN(FpImm(2.0), FpImm(3.0)));
714+
InterestingRanges.push_back(
715+
ConstantFPRange::getNonNaN(FpImm(-1.0), FpImm(1.0)));
716+
InterestingRanges.push_back(
717+
ConstantFPRange::getNonNaN(FpImm(-1.0), FpImm(-0.0)));
718+
InterestingRanges.push_back(ConstantFPRange::getNonNaN(
719+
APFloat::getInf(Sem, /*Negative=*/true), FpImm(-1.0)));
720+
InterestingRanges.push_back(ConstantFPRange::getNonNaN(
721+
FpImm(1.0), APFloat::getInf(Sem, /*Negative=*/false)));
722+
723+
for (auto &LHS : InterestingRanges) {
724+
for (auto &RHS : InterestingRanges) {
725+
for (auto Pred : FCmpInst::predicates()) {
726+
if (LHS.fcmp(Pred, RHS)) {
727+
EnumerateValuesInConstantFPRange(
728+
LHS,
729+
[&](const APFloat &LHSC) {
730+
EnumerateValuesInConstantFPRange(
731+
RHS,
732+
[&](const APFloat &RHSC) {
733+
EXPECT_TRUE(FCmpInst::compare(LHSC, RHSC, Pred))
734+
<< LHS << " " << Pred << " " << RHS
735+
<< " doesn't hold";
736+
},
737+
/*IgnoreNaNPayload=*/true);
738+
},
739+
/*IgnoreNaNPayload=*/true);
740+
}
741+
}
742+
}
743+
}
744+
}
745+
567746
} // anonymous namespace

0 commit comments

Comments
 (0)