-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[ConstantFPRange] Add support for cast operations #162686
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-llvm-ir Author: Yingwei Zheng (dtcxzyw) ChangesThis patch adds support for fpext/fptrunc operations. It assumes the round-to-nearest rounding mode. I noticed that finite-only semantics are not supported by the current representation of constant FP ranges. It should be okay for now, as we don't expose these types in the IR. Full diff: https://github.com/llvm/llvm-project/pull/162686.diff 3 Files Affected:
diff --git a/llvm/include/llvm/IR/ConstantFPRange.h b/llvm/include/llvm/IR/ConstantFPRange.h
index 930c6f98c033f..21ef7a1bfdc4c 100644
--- a/llvm/include/llvm/IR/ConstantFPRange.h
+++ b/llvm/include/llvm/IR/ConstantFPRange.h
@@ -200,6 +200,9 @@ class [[nodiscard]] ConstantFPRange {
/// with another range. The resultant range is guaranteed to include the
/// elements of both sets, but may contain more.
LLVM_ABI ConstantFPRange unionWith(const ConstantFPRange &CR) const;
+
+ /// Return a new range in the specified format.
+ LLVM_ABI ConstantFPRange cast(const fltSemantics &DstSem) const;
};
inline raw_ostream &operator<<(raw_ostream &OS, const ConstantFPRange &CR) {
diff --git a/llvm/lib/IR/ConstantFPRange.cpp b/llvm/lib/IR/ConstantFPRange.cpp
index 7509188128524..9a81c07c4a616 100644
--- a/llvm/lib/IR/ConstantFPRange.cpp
+++ b/llvm/lib/IR/ConstantFPRange.cpp
@@ -391,3 +391,21 @@ ConstantFPRange ConstantFPRange::unionWith(const ConstantFPRange &CR) const {
return ConstantFPRange(minnum(Lower, CR.Lower), maxnum(Upper, CR.Upper),
MayBeQNaN | CR.MayBeQNaN, MayBeSNaN | CR.MayBeSNaN);
}
+
+ConstantFPRange ConstantFPRange::cast(const fltSemantics &DstSem) const {
+ bool LosesInfo;
+ APFloat NewLower = Lower;
+ APFloat NewUpper = Upper;
+ // For conservative, return full range if conversion is invalid.
+ if (NewLower.convert(DstSem, APFloat::rmNearestTiesToEven, &LosesInfo) ==
+ APFloat::opInvalidOp ||
+ NewLower.isNaN())
+ return getFull(DstSem);
+ if (NewUpper.convert(DstSem, APFloat::rmNearestTiesToEven, &LosesInfo) ==
+ APFloat::opInvalidOp ||
+ NewUpper.isNaN())
+ return getFull(DstSem);
+ return ConstantFPRange(std::move(NewLower), std::move(NewUpper),
+ /*MayBeQNaNVal=*/MayBeQNaN || MayBeSNaN,
+ /*MayBeSNaNVal=*/false);
+}
diff --git a/llvm/unittests/IR/ConstantFPRangeTest.cpp b/llvm/unittests/IR/ConstantFPRangeTest.cpp
index 255f62d77b748..54b7019f5ef49 100644
--- a/llvm/unittests/IR/ConstantFPRangeTest.cpp
+++ b/llvm/unittests/IR/ConstantFPRangeTest.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/IR/ConstantFPRange.h"
+#include "llvm/ADT/APFloat.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Operator.h"
#include "gtest/gtest.h"
@@ -767,4 +768,88 @@ TEST_F(ConstantFPRangeTest, makeExactFCmpRegion) {
}
}
+TEST_F(ConstantFPRangeTest, cast) {
+ const fltSemantics &F16Sem = APFloat::IEEEhalf();
+ const fltSemantics &BF16Sem = APFloat::BFloat();
+ const fltSemantics &F32Sem = APFloat::IEEEsingle();
+ const fltSemantics &F8NanOnlySem = APFloat::Float8E4M3FN();
+ // normal -> normal (exact)
+ EXPECT_EQ(ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0)).cast(F32Sem),
+ ConstantFPRange::getNonNaN(APFloat(1.0f), APFloat(2.0f)));
+ EXPECT_EQ(
+ ConstantFPRange::getNonNaN(APFloat(-2.0f), APFloat(-1.0f)).cast(Sem),
+ ConstantFPRange::getNonNaN(APFloat(-2.0), APFloat(-1.0)));
+ // normal -> normal (inexact)
+ EXPECT_EQ(
+ ConstantFPRange::getNonNaN(APFloat(3.141592653589793),
+ APFloat(6.283185307179586))
+ .cast(F32Sem),
+ ConstantFPRange::getNonNaN(APFloat(3.14159274f), APFloat(6.28318548f)));
+ // normal -> subnormal
+ EXPECT_EQ(ConstantFPRange::getNonNaN(APFloat(-5e-8), APFloat(5e-8))
+ .cast(F16Sem)
+ .classify(),
+ fcSubnormal | fcZero);
+ // normal -> zero
+ EXPECT_EQ(ConstantFPRange::getNonNaN(
+ APFloat::getSmallestNormalized(Sem, /*Negative=*/true),
+ APFloat::getSmallestNormalized(Sem, /*Negative=*/false))
+ .cast(F32Sem)
+ .classify(),
+ fcZero);
+ // normal -> inf
+ EXPECT_EQ(ConstantFPRange::getNonNaN(APFloat(-65536.0), APFloat(65536.0))
+ .cast(F16Sem),
+ ConstantFPRange::getNonNaN(F16Sem));
+ // nan -> qnan
+ EXPECT_EQ(
+ ConstantFPRange::getNaNOnly(Sem, /*MayBeQNaN=*/true, /*MayBeSNaN=*/false)
+ .cast(F32Sem),
+ ConstantFPRange::getNaNOnly(F32Sem, /*MayBeQNaN=*/true,
+ /*MayBeSNaN=*/false));
+ EXPECT_EQ(
+ ConstantFPRange::getNaNOnly(Sem, /*MayBeQNaN=*/false, /*MayBeSNaN=*/true)
+ .cast(F32Sem),
+ ConstantFPRange::getNaNOnly(F32Sem, /*MayBeQNaN=*/true,
+ /*MayBeSNaN=*/false));
+ EXPECT_EQ(
+ ConstantFPRange::getNaNOnly(Sem, /*MayBeQNaN=*/true, /*MayBeSNaN=*/true)
+ .cast(F32Sem),
+ ConstantFPRange::getNaNOnly(F32Sem, /*MayBeQNaN=*/true,
+ /*MayBeSNaN=*/false));
+ // For BF16 -> F32, signaling bit is still lost.
+ EXPECT_EQ(ConstantFPRange::getNaNOnly(BF16Sem, /*MayBeQNaN=*/true,
+ /*MayBeSNaN=*/true)
+ .cast(F32Sem),
+ ConstantFPRange::getNaNOnly(F32Sem, /*MayBeQNaN=*/true,
+ /*MayBeSNaN=*/false));
+ // inf -> nan only (return full set for now)
+ EXPECT_EQ(ConstantFPRange::getNonNaN(APFloat::getInf(Sem, /*Negative=*/true),
+ APFloat::getInf(Sem, /*Negative=*/false))
+ .cast(F8NanOnlySem),
+ ConstantFPRange::getFull(F8NanOnlySem));
+
+ EnumerateValuesInConstantFPRange(
+ ConstantFPRange::getFull(APFloat::Float8E4M3()),
+ [&](const APFloat &V) {
+ bool LosesInfo = false;
+
+ APFloat DoubleV = V;
+ DoubleV.convert(Sem, APFloat::rmNearestTiesToEven, &LosesInfo);
+ ConstantFPRange DoubleCR = ConstantFPRange(V).cast(Sem);
+ EXPECT_TRUE(DoubleCR.contains(DoubleV))
+ << "Casting " << V << " to double failed. " << DoubleCR
+ << " doesn't contain " << DoubleV;
+
+ auto &FP4Sem = APFloat::Float4E2M1FN();
+ APFloat FP4V = V;
+ FP4V.convert(FP4Sem, APFloat::rmNearestTiesToEven, &LosesInfo);
+ ConstantFPRange FP4CR = ConstantFPRange(V).cast(FP4Sem);
+ EXPECT_TRUE(FP4CR.contains(FP4V))
+ << "Casting " << V << " to FP4E2M1FN failed. " << FP4CR
+ << " doesn't contain " << FP4V;
+ },
+ /*IgnoreNaNPayload=*/true);
+}
+
} // anonymous namespace
|
llvm/lib/IR/ConstantFPRange.cpp
Outdated
@@ -391,3 +391,21 @@ ConstantFPRange ConstantFPRange::unionWith(const ConstantFPRange &CR) const { | |||
return ConstantFPRange(minnum(Lower, CR.Lower), maxnum(Upper, CR.Upper), | |||
MayBeQNaN | CR.MayBeQNaN, MayBeSNaN | CR.MayBeSNaN); | |||
} | |||
|
|||
ConstantFPRange ConstantFPRange::cast(const fltSemantics &DstSem) const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just pass through the rounding mode argument? Is there anything more to it necessary than just passing it through to convert?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there anything more to it necessary than just passing it through to convert?
No. The invariant Lower <= Upper
holds for all rounding modes.
This patch adds support for fpext/fptrunc operations.
I noticed that finite-only semantics are not supported by the current representation of constant FP ranges. It should be okay for now, as we don't expose these types in the IR.