Skip to content

Commit 08d300f

Browse files
committed
[ConstantFPRange] Add support for cast operations
1 parent 01e19e8 commit 08d300f

File tree

3 files changed

+97
-0
lines changed

3 files changed

+97
-0
lines changed

llvm/include/llvm/IR/ConstantFPRange.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@ class [[nodiscard]] ConstantFPRange {
216216
/// Get the range without infinities. It is useful when we apply ninf flag to
217217
/// range of operands/results.
218218
LLVM_ABI ConstantFPRange getWithoutInf() const;
219+
220+
/// Return a new range in the specified format.
221+
LLVM_ABI ConstantFPRange cast(const fltSemantics &DstSem) const;
219222
};
220223

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

llvm/lib/IR/ConstantFPRange.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,3 +425,19 @@ ConstantFPRange ConstantFPRange::getWithoutInf() const {
425425
return ConstantFPRange(std::move(NewLower), std::move(NewUpper), MayBeQNaN,
426426
MayBeSNaN);
427427
}
428+
429+
ConstantFPRange ConstantFPRange::cast(const fltSemantics &DstSem) const {
430+
bool LosesInfo;
431+
APFloat NewLower = Lower;
432+
APFloat NewUpper = Upper;
433+
// For conservative, return full range if conversion is invalid.
434+
if (NewLower.convert(DstSem, APFloat::rmNearestTiesToEven, &LosesInfo) ==
435+
APFloat::opInvalidOp)
436+
return getFull(DstSem);
437+
if (NewUpper.convert(DstSem, APFloat::rmNearestTiesToEven, &LosesInfo) ==
438+
APFloat::opInvalidOp)
439+
return getFull(DstSem);
440+
return ConstantFPRange(std::move(NewLower), std::move(NewUpper),
441+
/*MayBeQNaNVal=*/MayBeQNaN || MayBeSNaN,
442+
/*MayBeSNaNVal=*/false);
443+
}

llvm/unittests/IR/ConstantFPRangeTest.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,4 +818,82 @@ TEST_F(ConstantFPRangeTest, getWithout) {
818818
APFloat::getLargest(Sem, /*Negative=*/true), APFloat(3.0)));
819819
}
820820

821+
TEST_F(ConstantFPRangeTest, cast) {
822+
const fltSemantics &F16Sem = APFloat::IEEEhalf();
823+
const fltSemantics &BF16Sem = APFloat::BFloat();
824+
const fltSemantics &F32Sem = APFloat::IEEEsingle();
825+
// normal -> normal (exact)
826+
EXPECT_EQ(ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0)).cast(F32Sem),
827+
ConstantFPRange::getNonNaN(APFloat(1.0f), APFloat(2.0f)));
828+
EXPECT_EQ(
829+
ConstantFPRange::getNonNaN(APFloat(-2.0f), APFloat(-1.0f)).cast(Sem),
830+
ConstantFPRange::getNonNaN(APFloat(-2.0), APFloat(-1.0)));
831+
// normal -> normal (inexact)
832+
EXPECT_EQ(
833+
ConstantFPRange::getNonNaN(APFloat(3.141592653589793),
834+
APFloat(6.283185307179586))
835+
.cast(F32Sem),
836+
ConstantFPRange::getNonNaN(APFloat(3.14159274f), APFloat(6.28318548f)));
837+
// normal -> subnormal
838+
EXPECT_EQ(ConstantFPRange::getNonNaN(APFloat(-5e-8), APFloat(5e-8))
839+
.cast(F16Sem)
840+
.classify(),
841+
fcSubnormal | fcZero);
842+
// normal -> zero
843+
EXPECT_EQ(ConstantFPRange::getNonNaN(
844+
APFloat::getSmallestNormalized(Sem, /*Negative=*/true),
845+
APFloat::getSmallestNormalized(Sem, /*Negative=*/false))
846+
.cast(F32Sem)
847+
.classify(),
848+
fcZero);
849+
// normal -> inf
850+
EXPECT_EQ(ConstantFPRange::getNonNaN(APFloat(-65536.0), APFloat(65536.0))
851+
.cast(F16Sem),
852+
ConstantFPRange::getNonNaN(F16Sem));
853+
// nan -> qnan
854+
EXPECT_EQ(
855+
ConstantFPRange::getNaNOnly(Sem, /*MayBeQNaN=*/true, /*MayBeSNaN=*/false)
856+
.cast(F32Sem),
857+
ConstantFPRange::getNaNOnly(F32Sem, /*MayBeQNaN=*/true,
858+
/*MayBeSNaN=*/false));
859+
EXPECT_EQ(
860+
ConstantFPRange::getNaNOnly(Sem, /*MayBeQNaN=*/false, /*MayBeSNaN=*/true)
861+
.cast(F32Sem),
862+
ConstantFPRange::getNaNOnly(F32Sem, /*MayBeQNaN=*/true,
863+
/*MayBeSNaN=*/false));
864+
EXPECT_EQ(
865+
ConstantFPRange::getNaNOnly(Sem, /*MayBeQNaN=*/true, /*MayBeSNaN=*/true)
866+
.cast(F32Sem),
867+
ConstantFPRange::getNaNOnly(F32Sem, /*MayBeQNaN=*/true,
868+
/*MayBeSNaN=*/false));
869+
// For BF16 -> F32, signaling bit is still lost.
870+
EXPECT_EQ(ConstantFPRange::getNaNOnly(BF16Sem, /*MayBeQNaN=*/true,
871+
/*MayBeSNaN=*/true)
872+
.cast(F32Sem),
873+
ConstantFPRange::getNaNOnly(F32Sem, /*MayBeQNaN=*/true,
874+
/*MayBeSNaN=*/false));
875+
876+
EnumerateValuesInConstantFPRange(
877+
ConstantFPRange::getFull(APFloat::Float8E4M3()),
878+
[&](const APFloat &V) {
879+
bool LosesInfo = false;
880+
881+
APFloat DoubleV = V;
882+
DoubleV.convert(Sem, APFloat::rmNearestTiesToEven, &LosesInfo);
883+
ConstantFPRange DoubleCR = ConstantFPRange(V).cast(Sem);
884+
EXPECT_TRUE(DoubleCR.contains(DoubleV))
885+
<< "Casting " << V << " to double failed. " << DoubleCR
886+
<< " doesn't contain " << DoubleV;
887+
888+
auto &FP4Sem = APFloat::Float4E2M1FN();
889+
APFloat FP4V = V;
890+
FP4V.convert(FP4Sem, APFloat::rmNearestTiesToEven, &LosesInfo);
891+
ConstantFPRange FP4CR = ConstantFPRange(V).cast(FP4Sem);
892+
EXPECT_TRUE(FP4CR.contains(FP4V))
893+
<< "Casting " << V << " to FP4E2M1FN failed. " << FP4CR
894+
<< " doesn't contain " << FP4V;
895+
},
896+
/*IgnoreNaNPayload=*/true);
897+
}
898+
821899
} // anonymous namespace

0 commit comments

Comments
 (0)