Skip to content

Commit aa26a6d

Browse files
committed
[ConstantFPRange] Add support for add/sub
1 parent 0b462f6 commit aa26a6d

File tree

3 files changed

+221
-24
lines changed

3 files changed

+221
-24
lines changed

llvm/include/llvm/IR/ConstantFPRange.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,14 @@ class [[nodiscard]] ConstantFPRange {
222222
LLVM_ABI ConstantFPRange
223223
cast(const fltSemantics &DstSem,
224224
APFloat::roundingMode RM = APFloat::rmNearestTiesToEven) const;
225+
226+
/// Return a new range representing the possible values resulting
227+
/// from an addition of a value in this range and a value in \p Other.
228+
LLVM_ABI ConstantFPRange add(const ConstantFPRange &Other) const;
229+
230+
/// Return a new range representing the possible values resulting
231+
/// from a subtraction of a value in this range and a value in \p Other.
232+
LLVM_ABI ConstantFPRange sub(const ConstantFPRange &Other) const;
225233
};
226234

227235
inline raw_ostream &operator<<(raw_ostream &OS, const ConstantFPRange &CR) {

llvm/lib/IR/ConstantFPRange.cpp

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -414,15 +414,31 @@ ConstantFPRange ConstantFPRange::negate() const {
414414
return ConstantFPRange(-Upper, -Lower, MayBeQNaN, MayBeSNaN);
415415
}
416416

417+
// Return true if the finite part is not empty after removing infinities.
418+
static bool removeInf(APFloat &Lower, APFloat &Upper, bool &HasPosInf,
419+
bool &HasNegInf) {
420+
assert(strictCompare(Lower, Upper) != APFloat::cmpGreaterThan &&
421+
"Non-NaN part is empty.");
422+
auto &Sem = Lower.getSemantics();
423+
if (Lower.isNegInfinity()) {
424+
Lower = APFloat::getLargest(Sem, /*Negative=*/true);
425+
HasNegInf = true;
426+
}
427+
if (Upper.isPosInfinity()) {
428+
Upper = APFloat::getLargest(Sem, /*Negative=*/false);
429+
HasPosInf = true;
430+
}
431+
return strictCompare(Lower, Upper) != APFloat::cmpGreaterThan;
432+
}
433+
417434
ConstantFPRange ConstantFPRange::getWithoutInf() const {
418435
if (isNaNOnly())
419436
return *this;
420437
APFloat NewLower = Lower;
421438
APFloat NewUpper = Upper;
422-
if (Lower.isNegInfinity())
423-
NewLower = APFloat::getLargest(getSemantics(), /*Negative=*/true);
424-
if (Upper.isPosInfinity())
425-
NewUpper = APFloat::getLargest(getSemantics(), /*Negative=*/false);
439+
bool UnusedFlag;
440+
removeInf(NewLower, NewUpper, /*HasPosInf=*/UnusedFlag,
441+
/*HasNegInf=*/UnusedFlag);
426442
canonicalizeRange(NewLower, NewUpper);
427443
return ConstantFPRange(std::move(NewLower), std::move(NewUpper), MayBeQNaN,
428444
MayBeSNaN);
@@ -444,3 +460,45 @@ ConstantFPRange ConstantFPRange::cast(const fltSemantics &DstSem,
444460
/*MayBeQNaNVal=*/MayBeQNaN || MayBeSNaN,
445461
/*MayBeSNaNVal=*/false);
446462
}
463+
464+
ConstantFPRange ConstantFPRange::add(const ConstantFPRange &Other) const {
465+
bool MayBeQNaN =
466+
(MayBeQNaN || MayBeSNaN) && (Other.MayBeQNaN || Other.MayBeSNaN);
467+
if (isNaNOnly() || Other.isNaNOnly())
468+
return getNaNOnly(getSemantics(), /*MayBeQNaN=*/MayBeQNaN,
469+
/*MayBeSNaN=*/false);
470+
bool LHSHasNegInf = false, LHSHasPosInf = false;
471+
APFloat LHSLower = Lower, LHSUpper = Upper;
472+
bool LHSFiniteIsNonEmpty =
473+
removeInf(LHSLower, LHSUpper, LHSHasPosInf, LHSHasNegInf);
474+
bool RHSHasNegInf = false, RHSHasPosInf = false;
475+
APFloat RHSLower = Other.Lower, RHSUpper = Other.Upper;
476+
bool RHSFiniteIsNonEmpty =
477+
removeInf(RHSLower, RHSUpper, RHSHasPosInf, RHSHasNegInf);
478+
// -inf + +inf = QNaN
479+
MayBeQNaN |= (LHSHasNegInf && RHSHasPosInf) || (LHSHasPosInf && RHSHasNegInf);
480+
// +inf + finite/+inf = +inf, -inf + finite/-inf = -inf
481+
bool HasNegInf = (LHSHasNegInf && (RHSFiniteIsNonEmpty || RHSHasNegInf)) ||
482+
(RHSHasNegInf && (LHSFiniteIsNonEmpty || LHSHasNegInf));
483+
bool HasPosInf = (LHSHasPosInf && (RHSFiniteIsNonEmpty || RHSHasPosInf)) ||
484+
(RHSHasPosInf && (LHSFiniteIsNonEmpty || LHSHasPosInf));
485+
if (LHSFiniteIsNonEmpty && RHSFiniteIsNonEmpty) {
486+
APFloat NewLower =
487+
HasNegInf ? APFloat::getInf(LHSLower.getSemantics(), /*Negative=*/true)
488+
: LHSLower + RHSLower;
489+
APFloat NewUpper =
490+
HasPosInf ? APFloat::getInf(LHSUpper.getSemantics(), /*Negative=*/false)
491+
: LHSUpper + RHSUpper;
492+
return ConstantFPRange(NewLower, NewUpper, MayBeQNaN, /*MayBeSNaN=*/false);
493+
}
494+
// If both HasNegInf and HasPosInf are true, the non-NaN part is empty.
495+
return ConstantFPRange(
496+
APFloat::getInf(Lower.getSemantics(), /*Negative=*/HasNegInf),
497+
APFloat::getInf(Upper.getSemantics(), /*Negative=*/!HasPosInf), MayBeQNaN,
498+
/*MayBeSNaN=*/false);
499+
}
500+
501+
ConstantFPRange ConstantFPRange::sub(const ConstantFPRange &Other) const {
502+
// fsub X, Y = fadd X, (fneg Y)
503+
return add(Other.negate());
504+
}

llvm/unittests/IR/ConstantFPRangeTest.cpp

Lines changed: 151 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -79,26 +79,35 @@ static void strictNext(APFloat &V) {
7979
V.next(/*nextDown=*/false);
8080
}
8181

82+
enum class SparseLevel {
83+
Dense,
84+
SpecialValuesWithAllPowerOfTwos,
85+
SpecialValuesOnly,
86+
};
87+
8288
template <typename Fn>
83-
static void EnumerateConstantFPRangesImpl(Fn TestFn, bool Exhaustive,
89+
static void EnumerateConstantFPRangesImpl(Fn TestFn, SparseLevel Level,
8490
bool MayBeQNaN, bool MayBeSNaN) {
8591
const fltSemantics &Sem = APFloat::Float8E4M3();
8692
APFloat PosInf = APFloat::getInf(Sem, /*Negative=*/false);
8793
APFloat NegInf = APFloat::getInf(Sem, /*Negative=*/true);
8894
TestFn(ConstantFPRange(PosInf, NegInf, MayBeQNaN, MayBeSNaN));
8995

90-
if (!Exhaustive) {
96+
if (Level != SparseLevel::Dense) {
9197
SmallVector<APFloat, 36> Values;
9298
Values.push_back(APFloat::getInf(Sem, /*Negative=*/true));
9399
Values.push_back(APFloat::getLargest(Sem, /*Negative=*/true));
94100
unsigned BitWidth = APFloat::semanticsSizeInBits(Sem);
95101
unsigned Exponents = APFloat::semanticsMaxExponent(Sem) -
96102
APFloat::semanticsMinExponent(Sem) + 3;
97103
unsigned MantissaBits = APFloat::semanticsPrecision(Sem) - 1;
98-
// Add -2^(max exponent), -2^(max exponent-1), ..., -2^(min exponent)
99-
for (unsigned M = Exponents - 2; M != 0; --M)
100-
Values.push_back(
101-
APFloat(Sem, APInt(BitWidth, (M + Exponents) << MantissaBits)));
104+
if (Level == SparseLevel::SpecialValuesWithAllPowerOfTwos) {
105+
// Add -2^(max exponent), -2^(max exponent-1), ..., -2^(min exponent)
106+
for (unsigned M = Exponents - 2; M != 0; --M)
107+
Values.push_back(
108+
APFloat(Sem, APInt(BitWidth, (M + Exponents) << MantissaBits)));
109+
}
110+
Values.push_back(APFloat::getSmallestNormalized(Sem, /*Negative=*/true));
102111
Values.push_back(APFloat::getSmallest(Sem, /*Negative=*/true));
103112
Values.push_back(APFloat::getZero(Sem, /*Negative=*/true));
104113
size_t E = Values.size();
@@ -127,26 +136,30 @@ static void EnumerateConstantFPRangesImpl(Fn TestFn, bool Exhaustive,
127136
}
128137

129138
template <typename Fn>
130-
static void EnumerateConstantFPRanges(Fn TestFn, bool Exhaustive) {
131-
EnumerateConstantFPRangesImpl(TestFn, Exhaustive, /*MayBeQNaN=*/false,
139+
static void EnumerateConstantFPRanges(Fn TestFn, SparseLevel Level,
140+
bool IgnoreNaNs = false) {
141+
EnumerateConstantFPRangesImpl(TestFn, Level, /*MayBeQNaN=*/false,
132142
/*MayBeSNaN=*/false);
133-
EnumerateConstantFPRangesImpl(TestFn, Exhaustive, /*MayBeQNaN=*/false,
143+
if (IgnoreNaNs)
144+
return;
145+
EnumerateConstantFPRangesImpl(TestFn, Level, /*MayBeQNaN=*/false,
134146
/*MayBeSNaN=*/true);
135-
EnumerateConstantFPRangesImpl(TestFn, Exhaustive, /*MayBeQNaN=*/true,
147+
EnumerateConstantFPRangesImpl(TestFn, Level, /*MayBeQNaN=*/true,
136148
/*MayBeSNaN=*/false);
137-
EnumerateConstantFPRangesImpl(TestFn, Exhaustive, /*MayBeQNaN=*/true,
149+
EnumerateConstantFPRangesImpl(TestFn, Level, /*MayBeQNaN=*/true,
138150
/*MayBeSNaN=*/true);
139151
}
140152

141153
template <typename Fn>
142154
static void EnumerateTwoInterestingConstantFPRanges(Fn TestFn,
143-
bool Exhaustive) {
155+
SparseLevel Level) {
144156
EnumerateConstantFPRanges(
145157
[&](const ConstantFPRange &CR1) {
146158
EnumerateConstantFPRanges(
147-
[&](const ConstantFPRange &CR2) { TestFn(CR1, CR2); }, Exhaustive);
159+
[&](const ConstantFPRange &CR2) { TestFn(CR1, CR2); }, Level,
160+
/*IgnoreNaNs=*/true);
148161
},
149-
Exhaustive);
162+
Level, /*IgnoreNaNs=*/true);
150163
}
151164

152165
template <typename Fn>
@@ -348,16 +361,25 @@ TEST_F(ConstantFPRangeTest, ExhaustivelyEnumerate) {
348361
constexpr unsigned Expected = 4 * ((NNaNValues + 1) * NNaNValues / 2 + 1);
349362
unsigned Count = 0;
350363
EnumerateConstantFPRanges([&](const ConstantFPRange &) { ++Count; },
351-
/*Exhaustive=*/true);
364+
SparseLevel::Dense);
352365
EXPECT_EQ(Expected, Count);
353366
}
354367

355368
TEST_F(ConstantFPRangeTest, Enumerate) {
356-
constexpr unsigned NNaNValues = 2 * ((1 << 4) - 2 + 4);
369+
constexpr unsigned NNaNValues = 2 * ((1 << 4) - 2 + 5);
370+
constexpr unsigned Expected = 4 * ((NNaNValues + 1) * NNaNValues / 2 + 1);
371+
unsigned Count = 0;
372+
EnumerateConstantFPRanges([&](const ConstantFPRange &) { ++Count; },
373+
SparseLevel::SpecialValuesWithAllPowerOfTwos);
374+
EXPECT_EQ(Expected, Count);
375+
}
376+
377+
TEST_F(ConstantFPRangeTest, EnumerateWithSpecialValuesOnly) {
378+
constexpr unsigned NNaNValues = 2 * 5;
357379
constexpr unsigned Expected = 4 * ((NNaNValues + 1) * NNaNValues / 2 + 1);
358380
unsigned Count = 0;
359381
EnumerateConstantFPRanges([&](const ConstantFPRange &) { ++Count; },
360-
/*Exhaustive=*/false);
382+
SparseLevel::SpecialValuesOnly);
361383
EXPECT_EQ(Expected, Count);
362384
}
363385

@@ -459,7 +481,7 @@ TEST_F(ConstantFPRangeTest, FPClassify) {
459481
EXPECT_EQ(SignBit, CR.getSignBit()) << CR;
460482
EXPECT_EQ(Mask, CR.classify()) << CR;
461483
},
462-
/*Exhaustive=*/true);
484+
SparseLevel::Dense);
463485
#endif
464486
}
465487

@@ -560,7 +582,7 @@ TEST_F(ConstantFPRangeTest, makeAllowedFCmpRegion) {
560582
<< "Suboptimal result for makeAllowedFCmpRegion(" << Pred << ", "
561583
<< CR << ")";
562584
},
563-
/*Exhaustive=*/false);
585+
SparseLevel::SpecialValuesWithAllPowerOfTwos);
564586
}
565587
#endif
566588
}
@@ -671,7 +693,7 @@ TEST_F(ConstantFPRangeTest, makeSatisfyingFCmpRegion) {
671693
<< ", " << CR << ")";
672694
}
673695
},
674-
/*Exhaustive=*/false);
696+
SparseLevel::SpecialValuesWithAllPowerOfTwos);
675697
}
676698
#endif
677699
}
@@ -925,4 +947,113 @@ TEST_F(ConstantFPRangeTest, cast) {
925947
/*IgnoreNaNPayload=*/true);
926948
}
927949

950+
TEST_F(ConstantFPRangeTest, add) {
951+
EXPECT_EQ(Full.add(Full), ConstantFPRange::getNonNaN(Sem).unionWith(QNaN));
952+
EXPECT_EQ(Full.add(Empty), Empty);
953+
EXPECT_EQ(Empty.add(Full), Empty);
954+
EXPECT_EQ(Empty.add(Empty), Empty);
955+
EXPECT_EQ(One.add(One), ConstantFPRange(APFloat(2.0)));
956+
EXPECT_EQ(Some.add(Some),
957+
ConstantFPRange::getNonNaN(APFloat(-6.0), APFloat(6.0)));
958+
EXPECT_EQ(SomePos.add(SomeNeg),
959+
ConstantFPRange::getNonNaN(APFloat(-3.0), APFloat(3.0)));
960+
EXPECT_EQ(PosInf.add(PosInf), PosInf);
961+
EXPECT_EQ(NegInf.add(NegInf), NegInf);
962+
EXPECT_EQ(PosInf.add(Finite.unionWith(PosInf)), PosInf);
963+
EXPECT_EQ(NegInf.add(Finite.unionWith(NegInf)), NegInf);
964+
EXPECT_EQ(PosInf.add(Finite.unionWith(NegInf)), PosInf.unionWith(QNaN));
965+
EXPECT_EQ(NegInf.add(Finite.unionWith(PosInf)), NegInf.unionWith(QNaN));
966+
EXPECT_EQ(PosInf.add(NegInf), QNaN);
967+
EXPECT_EQ(NegInf.add(PosInf), QNaN);
968+
EXPECT_EQ(PosZero.add(NegZero), PosZero);
969+
EXPECT_EQ(PosZero.add(Zero), PosZero);
970+
EXPECT_EQ(NegZero.add(NegZero), NegZero);
971+
EXPECT_EQ(NegZero.add(Zero), Zero);
972+
EXPECT_EQ(NaN.add(NaN), QNaN);
973+
974+
#if defined(EXPENSIVE_CHECKS)
975+
EnumerateTwoInterestingConstantFPRanges(
976+
[](const ConstantFPRange &LHS, const ConstantFPRange &RHS) {
977+
ConstantFPRange Res = LHS.add(RHS);
978+
ConstantFPRange Expected =
979+
ConstantFPRange::getEmpty(LHS.getSemantics());
980+
EnumerateValuesInConstantFPRange(
981+
LHS,
982+
[&](const APFloat &LHSC) {
983+
EnumerateValuesInConstantFPRange(
984+
RHS,
985+
[&](const APFloat &RHSC) {
986+
APFloat Sum = LHSC + RHSC;
987+
EXPECT_TRUE(Res.contains(Sum))
988+
<< "Wrong result for " << LHS << " + " << RHS
989+
<< ". The result " << Res << " should contain " << Sum;
990+
if (!Expected.contains(Sum))
991+
Expected = Expected.unionWith(ConstantFPRange(Sum));
992+
},
993+
/*IgnoreNaNPayload=*/true);
994+
},
995+
/*IgnoreNaNPayload=*/true);
996+
EXPECT_EQ(Res, Expected)
997+
<< "Suboptimal result for " << LHS << " + " << RHS << ". Expected "
998+
<< Expected << ", but got " << Res;
999+
},
1000+
SparseLevel::SpecialValuesOnly);
1001+
#endif
1002+
}
1003+
1004+
TEST_F(ConstantFPRangeTest, sub) {
1005+
EXPECT_EQ(Full.sub(Full), ConstantFPRange::getNonNaN(Sem).unionWith(QNaN));
1006+
EXPECT_EQ(Full.sub(Empty), Empty);
1007+
EXPECT_EQ(Empty.sub(Full), Empty);
1008+
EXPECT_EQ(Empty.sub(Empty), Empty);
1009+
EXPECT_EQ(One.sub(One), ConstantFPRange(APFloat(0.0)));
1010+
EXPECT_EQ(Some.sub(Some),
1011+
ConstantFPRange::getNonNaN(APFloat(-6.0), APFloat(6.0)));
1012+
EXPECT_EQ(SomePos.sub(SomeNeg),
1013+
ConstantFPRange::getNonNaN(APFloat(0.0), APFloat(6.0)));
1014+
EXPECT_EQ(PosInf.sub(NegInf), PosInf);
1015+
EXPECT_EQ(NegInf.sub(PosInf), NegInf);
1016+
EXPECT_EQ(PosInf.sub(Finite.unionWith(NegInf)), PosInf);
1017+
EXPECT_EQ(NegInf.sub(Finite.unionWith(PosInf)), NegInf);
1018+
EXPECT_EQ(PosInf.sub(Finite.unionWith(PosInf)), PosInf.unionWith(QNaN));
1019+
EXPECT_EQ(NegInf.sub(Finite.unionWith(NegInf)), NegInf.unionWith(QNaN));
1020+
EXPECT_EQ(PosInf.sub(PosInf), QNaN);
1021+
EXPECT_EQ(NegInf.sub(NegInf), QNaN);
1022+
EXPECT_EQ(PosZero.sub(NegZero), PosZero);
1023+
EXPECT_EQ(PosZero.sub(Zero), PosZero);
1024+
EXPECT_EQ(NegZero.sub(NegZero), PosZero);
1025+
EXPECT_EQ(NegZero.sub(PosZero), NegZero);
1026+
EXPECT_EQ(NegZero.sub(Zero), Zero);
1027+
EXPECT_EQ(NaN.sub(NaN), QNaN);
1028+
1029+
#if defined(EXPENSIVE_CHECKS)
1030+
EnumerateTwoInterestingConstantFPRanges(
1031+
[](const ConstantFPRange &LHS, const ConstantFPRange &RHS) {
1032+
ConstantFPRange Res = LHS.sub(RHS);
1033+
ConstantFPRange Expected =
1034+
ConstantFPRange::getEmpty(LHS.getSemantics());
1035+
EnumerateValuesInConstantFPRange(
1036+
LHS,
1037+
[&](const APFloat &LHSC) {
1038+
EnumerateValuesInConstantFPRange(
1039+
RHS,
1040+
[&](const APFloat &RHSC) {
1041+
APFloat Diff = LHSC - RHSC;
1042+
EXPECT_TRUE(Res.contains(Diff))
1043+
<< "Wrong result for " << LHS << " - " << RHS
1044+
<< ". The result " << Res << " should contain " << Diff;
1045+
if (!Expected.contains(Diff))
1046+
Expected = Expected.unionWith(ConstantFPRange(Diff));
1047+
},
1048+
/*IgnoreNaNPayload=*/true);
1049+
},
1050+
/*IgnoreNaNPayload=*/true);
1051+
EXPECT_EQ(Res, Expected)
1052+
<< "Suboptimal result for " << LHS << " - " << RHS << ". Expected "
1053+
<< Expected << ", but got " << Res;
1054+
},
1055+
SparseLevel::SpecialValuesOnly);
1056+
#endif
1057+
}
1058+
9281059
} // anonymous namespace

0 commit comments

Comments
 (0)