Skip to content

Commit 0dc44b3

Browse files
authored
[ConstantFPRange] Add support for flushDenormals (llvm#163074)
This patch provides a helper function to handle non-IEEE denormal flushing behaviours. For the dynamic mode, it returns a union of all possible results.
1 parent 8823efe commit 0dc44b3

File tree

3 files changed

+93
-0
lines changed

3 files changed

+93
-0
lines changed

llvm/include/llvm/IR/ConstantFPRange.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ class [[nodiscard]] ConstantFPRange {
230230
/// Return a new range representing the possible values resulting
231231
/// from a subtraction of a value in this range and a value in \p Other.
232232
LLVM_ABI ConstantFPRange sub(const ConstantFPRange &Other) const;
233+
234+
/// Flush denormal values to zero according to the specified mode.
235+
/// For dynamic mode, we return the union of all possible results.
236+
LLVM_ABI void flushDenormals(DenormalMode::DenormalModeKind Mode);
233237
};
234238

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

llvm/lib/IR/ConstantFPRange.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "llvm/IR/ConstantFPRange.h"
1010
#include "llvm/ADT/APFloat.h"
11+
#include "llvm/ADT/FloatingPointMode.h"
1112
#include "llvm/Support/Debug.h"
1213
#include "llvm/Support/raw_ostream.h"
1314
#include <cassert>
@@ -506,3 +507,24 @@ ConstantFPRange ConstantFPRange::sub(const ConstantFPRange &Other) const {
506507
// fsub X, Y = fadd X, (fneg Y)
507508
return add(Other.negate());
508509
}
510+
511+
void ConstantFPRange::flushDenormals(DenormalMode::DenormalModeKind Mode) {
512+
if (Mode == DenormalMode::IEEE)
513+
return;
514+
FPClassTest Class = classify();
515+
if (!(Class & fcSubnormal))
516+
return;
517+
518+
auto &Sem = getSemantics();
519+
// PreserveSign: PosSubnormal -> PosZero, NegSubnormal -> NegZero
520+
// PositiveZero: PosSubnormal -> PosZero, NegSubnormal -> PosZero
521+
// Dynamic: PosSubnormal -> PosZero, NegSubnormal -> NegZero/PosZero
522+
bool ZeroLowerNegative =
523+
Mode != DenormalMode::PositiveZero && (Class & fcNegSubnormal);
524+
bool ZeroUpperNegative =
525+
Mode == DenormalMode::PreserveSign && !(Class & fcPosSubnormal);
526+
assert((ZeroLowerNegative || !ZeroUpperNegative) &&
527+
"ZeroLower is greater than ZeroUpper.");
528+
Lower = minnum(Lower, APFloat::getZero(Sem, ZeroLowerNegative));
529+
Upper = maxnum(Upper, APFloat::getZero(Sem, ZeroUpperNegative));
530+
}

llvm/unittests/IR/ConstantFPRangeTest.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "llvm/IR/ConstantFPRange.h"
1010
#include "llvm/ADT/APFloat.h"
11+
#include "llvm/ADT/FloatingPointMode.h"
1112
#include "llvm/IR/Instructions.h"
1213
#include "llvm/IR/Operator.h"
1314
#include "gtest/gtest.h"
@@ -1065,4 +1066,70 @@ TEST_F(ConstantFPRangeTest, sub) {
10651066
#endif
10661067
}
10671068

1069+
TEST_F(ConstantFPRangeTest, flushDenormals) {
1070+
const fltSemantics &FP8Sem = APFloat::Float8E4M3();
1071+
APFloat NormalVal = APFloat::getSmallestNormalized(FP8Sem);
1072+
APFloat Subnormal1 = NormalVal;
1073+
Subnormal1.next(/*nextDown=*/true);
1074+
APFloat Subnormal2 = APFloat::getSmallest(FP8Sem);
1075+
APFloat ZeroVal = APFloat::getZero(FP8Sem);
1076+
APFloat EdgeValues[8] = {-NormalVal, -Subnormal1, -Subnormal2, -ZeroVal,
1077+
ZeroVal, Subnormal2, Subnormal1, NormalVal};
1078+
constexpr DenormalMode::DenormalModeKind Modes[4] = {
1079+
DenormalMode::IEEE, DenormalMode::PreserveSign,
1080+
DenormalMode::PositiveZero, DenormalMode::Dynamic};
1081+
for (uint32_t I = 0; I != 8; ++I) {
1082+
for (uint32_t J = I; J != 8; ++J) {
1083+
ConstantFPRange OriginCR =
1084+
ConstantFPRange::getNonNaN(EdgeValues[I], EdgeValues[J]);
1085+
for (auto Mode : Modes) {
1086+
StringRef ModeName = denormalModeKindName(Mode);
1087+
ConstantFPRange FlushedCR = OriginCR;
1088+
FlushedCR.flushDenormals(Mode);
1089+
1090+
ConstantFPRange Expected = ConstantFPRange::getEmpty(FP8Sem);
1091+
auto CheckFlushedV = [&](const APFloat &V, const APFloat &FlushedV) {
1092+
EXPECT_TRUE(FlushedCR.contains(FlushedV))
1093+
<< "Wrong result for flushDenormal(" << V << ", " << ModeName
1094+
<< "). The result " << FlushedCR << " should contain "
1095+
<< FlushedV;
1096+
if (!Expected.contains(FlushedV))
1097+
Expected = Expected.unionWith(ConstantFPRange(FlushedV));
1098+
};
1099+
EnumerateValuesInConstantFPRange(
1100+
OriginCR,
1101+
[&](const APFloat &V) {
1102+
if (V.isDenormal()) {
1103+
switch (Mode) {
1104+
case DenormalMode::IEEE:
1105+
break;
1106+
case DenormalMode::PreserveSign:
1107+
CheckFlushedV(V, APFloat::getZero(FP8Sem, V.isNegative()));
1108+
break;
1109+
case DenormalMode::PositiveZero:
1110+
CheckFlushedV(V, APFloat::getZero(FP8Sem));
1111+
break;
1112+
case DenormalMode::Dynamic:
1113+
// PreserveSign
1114+
CheckFlushedV(V, APFloat::getZero(FP8Sem, V.isNegative()));
1115+
// PositiveZero
1116+
CheckFlushedV(V, APFloat::getZero(FP8Sem));
1117+
break;
1118+
default:
1119+
llvm_unreachable("unknown denormal mode");
1120+
}
1121+
}
1122+
// It is not mandated that flushing to zero occurs.
1123+
CheckFlushedV(V, V);
1124+
},
1125+
/*IgnoreNaNPayload=*/true);
1126+
EXPECT_EQ(FlushedCR, Expected)
1127+
<< "Suboptimal result for flushDenormal(" << OriginCR << ", "
1128+
<< ModeName << "). Expected " << Expected << ", but got "
1129+
<< FlushedCR;
1130+
}
1131+
}
1132+
}
1133+
}
1134+
10681135
} // anonymous namespace

0 commit comments

Comments
 (0)