Skip to content

Commit 838b145

Browse files
committed
[ConstantFPRange] Add support for cast operations
1 parent 4154c18 commit 838b145

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
@@ -200,6 +200,9 @@ class [[nodiscard]] ConstantFPRange {
200200
/// with another range. The resultant range is guaranteed to include the
201201
/// elements of both sets, but may contain more.
202202
LLVM_ABI ConstantFPRange unionWith(const ConstantFPRange &CR) const;
203+
204+
/// Return a new range in the specified format.
205+
LLVM_ABI ConstantFPRange cast(const fltSemantics &DstSem) const;
203206
};
204207

205208
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
@@ -391,3 +391,19 @@ ConstantFPRange ConstantFPRange::unionWith(const ConstantFPRange &CR) const {
391391
return ConstantFPRange(minnum(Lower, CR.Lower), maxnum(Upper, CR.Upper),
392392
MayBeQNaN | CR.MayBeQNaN, MayBeSNaN | CR.MayBeSNaN);
393393
}
394+
395+
ConstantFPRange ConstantFPRange::cast(const fltSemantics &DstSem) const {
396+
bool LosesInfo;
397+
APFloat NewLower = Lower;
398+
APFloat NewUpper = Upper;
399+
// For conservative, return full range if conversion is invalid.
400+
if (NewLower.convert(DstSem, APFloat::rmNearestTiesToEven, &LosesInfo) ==
401+
APFloat::opInvalidOp)
402+
return getFull(DstSem);
403+
if (NewUpper.convert(DstSem, APFloat::rmNearestTiesToEven, &LosesInfo) ==
404+
APFloat::opInvalidOp)
405+
return getFull(DstSem);
406+
return ConstantFPRange(std::move(NewLower), std::move(NewUpper),
407+
/*MayBeQNaNVal=*/MayBeQNaN || MayBeSNaN,
408+
/*MayBeSNaNVal=*/false);
409+
}

llvm/unittests/IR/ConstantFPRangeTest.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,4 +767,82 @@ TEST_F(ConstantFPRangeTest, makeExactFCmpRegion) {
767767
}
768768
}
769769

770+
TEST_F(ConstantFPRangeTest, cast) {
771+
const fltSemantics &F16Sem = APFloat::IEEEhalf();
772+
const fltSemantics &BF16Sem = APFloat::BFloat();
773+
const fltSemantics &F32Sem = APFloat::IEEEsingle();
774+
// normal -> normal (exact)
775+
EXPECT_EQ(ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0)).cast(F32Sem),
776+
ConstantFPRange::getNonNaN(APFloat(1.0f), APFloat(2.0f)));
777+
EXPECT_EQ(
778+
ConstantFPRange::getNonNaN(APFloat(-2.0f), APFloat(-1.0f)).cast(Sem),
779+
ConstantFPRange::getNonNaN(APFloat(-2.0), APFloat(-1.0)));
780+
// normal -> normal (inexact)
781+
EXPECT_EQ(
782+
ConstantFPRange::getNonNaN(APFloat(3.141592653589793),
783+
APFloat(6.283185307179586))
784+
.cast(F32Sem),
785+
ConstantFPRange::getNonNaN(APFloat(3.14159274f), APFloat(6.28318548f)));
786+
// normal -> subnormal
787+
EXPECT_EQ(ConstantFPRange::getNonNaN(APFloat(-5e-8), APFloat(5e-8))
788+
.cast(F16Sem)
789+
.classify(),
790+
fcSubnormal | fcZero);
791+
// normal -> zero
792+
EXPECT_EQ(ConstantFPRange::getNonNaN(
793+
APFloat::getSmallestNormalized(Sem, /*Negative=*/true),
794+
APFloat::getSmallestNormalized(Sem, /*Negative=*/false))
795+
.cast(F32Sem)
796+
.classify(),
797+
fcZero);
798+
// normal -> inf
799+
EXPECT_EQ(ConstantFPRange::getNonNaN(APFloat(-65536.0), APFloat(65536.0))
800+
.cast(F16Sem),
801+
ConstantFPRange::getNonNaN(F16Sem));
802+
// nan -> qnan
803+
EXPECT_EQ(
804+
ConstantFPRange::getNaNOnly(Sem, /*MayBeQNaN=*/true, /*MayBeSNaN=*/false)
805+
.cast(F32Sem),
806+
ConstantFPRange::getNaNOnly(F32Sem, /*MayBeQNaN=*/true,
807+
/*MayBeSNaN=*/false));
808+
EXPECT_EQ(
809+
ConstantFPRange::getNaNOnly(Sem, /*MayBeQNaN=*/false, /*MayBeSNaN=*/true)
810+
.cast(F32Sem),
811+
ConstantFPRange::getNaNOnly(F32Sem, /*MayBeQNaN=*/true,
812+
/*MayBeSNaN=*/false));
813+
EXPECT_EQ(
814+
ConstantFPRange::getNaNOnly(Sem, /*MayBeQNaN=*/true, /*MayBeSNaN=*/true)
815+
.cast(F32Sem),
816+
ConstantFPRange::getNaNOnly(F32Sem, /*MayBeQNaN=*/true,
817+
/*MayBeSNaN=*/false));
818+
// For BF16 -> F32, signaling bit is still lost.
819+
EXPECT_EQ(ConstantFPRange::getNaNOnly(BF16Sem, /*MayBeQNaN=*/true,
820+
/*MayBeSNaN=*/true)
821+
.cast(F32Sem),
822+
ConstantFPRange::getNaNOnly(F32Sem, /*MayBeQNaN=*/true,
823+
/*MayBeSNaN=*/false));
824+
825+
EnumerateValuesInConstantFPRange(
826+
ConstantFPRange::getFull(APFloat::Float8E4M3()),
827+
[&](const APFloat &V) {
828+
bool LosesInfo = false;
829+
830+
APFloat DoubleV = V;
831+
DoubleV.convert(Sem, APFloat::rmNearestTiesToEven, &LosesInfo);
832+
ConstantFPRange DoubleCR = ConstantFPRange(V).cast(Sem);
833+
EXPECT_TRUE(DoubleCR.contains(DoubleV))
834+
<< "Casting " << V << " to double failed. " << DoubleCR
835+
<< " doesn't contain " << DoubleV;
836+
837+
auto &FP4Sem = APFloat::Float4E2M1FN();
838+
APFloat FP4V = V;
839+
FP4V.convert(FP4Sem, APFloat::rmNearestTiesToEven, &LosesInfo);
840+
ConstantFPRange FP4CR = ConstantFPRange(V).cast(FP4Sem);
841+
EXPECT_TRUE(FP4CR.contains(FP4V))
842+
<< "Casting " << V << " to FP4E2M1FN failed. " << FP4CR
843+
<< " doesn't contain " << FP4V;
844+
},
845+
/*IgnoreNaNPayload=*/true);
846+
}
847+
770848
} // anonymous namespace

0 commit comments

Comments
 (0)