[llvm] [mlir] [APFloat] Don't turn a Float8E8M0FNU NaN into an Inf on conversion (PR #214919)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 7 19:57:31 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir
Author: 曾鈜寬 Tseng Hung Kuan (Tim096)
<details>
<summary>Changes</summary>
`Float8E8M0FNU` has a precision of 1, so it stores no significand bits and its NaN carries no payload. `IEEEFloat::convert` shifts the (empty) significand into the target format, which leaves the NaN exponent with an all-zero significand — and that is the encoding of an infinity in any format that has infinities.
The `APFloat` object still reports `fcNaN`, so `isNaN()` returns true, but `bitcastToAPInt()` hands back Inf bits. Anything that stores the bit pattern rather than the object — `ConstantFP`, MLIR's `FloatAttr` — therefore ends up with an infinity.
In MLIR this shows up when folding `arith.extf` on an `f8E8M0FNU` constant. The OCP MXFP spec uses the all-ones encoding as the scale of an invalid block, so this silently replaces "this block is invalid" with a value that poisons everything it is multiplied into:
```mlir
func.func @<!-- -->e8m0_nan_to_f32() -> f32 {
%c = arith.constant 0xFF : f8E8M0FNU
%0 = arith.extf %c : f8E8M0FNU to f32
return %0 : f32
}
```
```
$ mlir-opt x.mlir -canonicalize
%cst = arith.constant 0x7F800000 : f32 // +Inf, not a NaN
```
f16 gives `0x7C00`, bf16 `0x7F80` and f64 `0x7FF0000000000000` — all Inf encodings. `convert` reports `opOK` with `losesInfo == false` in every case.
`Float8E8M0FNU` is the only semantics in the table with `precision == 1`, so this is the only source format affected. The fix creates a new NaN in the target semantics when the source has no significand, next to the existing fix-up for `fltNanEncoding::NegativeZero` sources, which handles the analogous NaN-to-`-Inf` case.
Verification: exhaustively over all 256 `Float8E8M0FNU` values against f16, bf16, f32, f64 and f128, checking that the category survives, that a NaN never comes out as an Inf, that the stored bit pattern read back through the target semantics is still a NaN, and that finite scales round-trip unchanged where the target's exponent range allows it. Reverting the `APFloat.cpp` hunk makes the new unit test fail.
---
Full diff: https://github.com/llvm/llvm-project/pull/214919.diff
3 Files Affected:
- (modified) llvm/lib/Support/APFloat.cpp (+5)
- (modified) llvm/unittests/ADT/APFloatTest.cpp (+30)
- (modified) mlir/test/Dialect/Arith/canonicalize.mlir (+19)
``````````diff
diff --git a/llvm/lib/Support/APFloat.cpp b/llvm/lib/Support/APFloat.cpp
index be255bd19964b..63007d88ed4d4 100644
--- a/llvm/lib/Support/APFloat.cpp
+++ b/llvm/lib/Support/APFloat.cpp
@@ -2557,6 +2557,11 @@ APFloat::opStatus IEEEFloat::convert(const fltSemantics &toSemantics,
semantics->nanEncoding != fltNanEncoding::NegativeZero)
makeNaN(false, false);
+ // If the source has no significand, there are no payload bits to carry
+ // over, and an all-zero significand would encode an Inf. Create a new NaN.
+ if (!APFloat::hasSignificand(fromSemantics))
+ makeNaN(false, sign);
+
*losesInfo = lostFraction != lfExactlyZero || X86SpecialNan;
// For x87 extended precision, we want to make a NaN, not a special NaN if
diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp
index 55d39e7f0a414..4ef2814e1a7b3 100644
--- a/llvm/unittests/ADT/APFloatTest.cpp
+++ b/llvm/unittests/ADT/APFloatTest.cpp
@@ -2304,6 +2304,36 @@ TEST(APFloatTest, Float8E8M0FNUValues) {
EXPECT_EQ(0x1.0p-127, test.convertToDouble());
}
+// Test that a Float8E8M0FNU NaN, which has no payload bits, does not
+// become an Inf when converted to a format that has infinities.
+TEST(APFloatTest, Float8E8M0FNUNaNConvert) {
+ APFloat nan = APFloat(APFloat::Float8E8M0FNU(), "nan");
+ EXPECT_TRUE(nan.isNaN());
+ EXPECT_EQ(APInt(8, 0xff), nan.bitcastToAPInt());
+
+ const std::pair<const fltSemantics *, uint64_t> ToSemantics[] = {
+ {&APFloat::IEEEhalf(), 0x7e00},
+ {&APFloat::BFloat(), 0x7fc0},
+ {&APFloat::IEEEsingle(), 0x7fc00000},
+ {&APFloat::IEEEdouble(), 0x7ff8000000000000ULL},
+ };
+
+ for (const auto &[Sem, ExpectedBits] : ToSemantics) {
+ APFloat test = nan;
+ bool losesInfo = true;
+ APFloat::opStatus status =
+ test.convert(*Sem, APFloat::rmNearestTiesToEven, &losesInfo);
+ EXPECT_EQ(status, APFloat::opOK);
+ EXPECT_FALSE(losesInfo);
+ EXPECT_TRUE(test.isNaN());
+ EXPECT_FALSE(test.isInfinity());
+ APInt bits = test.bitcastToAPInt();
+ EXPECT_EQ(APInt(APFloat::getSizeInBits(*Sem), ExpectedBits), bits);
+ // The stored bit pattern, not just the category, has to say NaN.
+ EXPECT_TRUE(APFloat(*Sem, bits).isNaN());
+ }
+}
+
TEST(APFloatTest, getLargest) {
EXPECT_EQ(3.402823466e+38f, APFloat::getLargest(APFloat::IEEEsingle()).convertToFloat());
EXPECT_EQ(1.7976931348623158e+308, APFloat::getLargest(APFloat::IEEEdouble()).convertToDouble());
diff --git a/mlir/test/Dialect/Arith/canonicalize.mlir b/mlir/test/Dialect/Arith/canonicalize.mlir
index 0c06aa6e861a4..89632305fa99d 100644
--- a/mlir/test/Dialect/Arith/canonicalize.mlir
+++ b/mlir/test/Dialect/Arith/canonicalize.mlir
@@ -1120,6 +1120,25 @@ func.func @extFPVectorConstant() -> vector<2xf128> {
return %0 : vector<2xf128>
}
+// A f8E8M0FNU NaN has no payload bits; folding must not turn it into an Inf.
+// CHECK-LABEL: @extFPConstantE8M0NaN
+// CHECK: %[[cres:.+]] = arith.constant 0x7FC00000 : f32
+// CHECK: return %[[cres]]
+func.func @extFPConstantE8M0NaN() -> f32 {
+ %cst = arith.constant 0xFF : f8E8M0FNU
+ %0 = arith.extf %cst : f8E8M0FNU to f32
+ return %0 : f32
+}
+
+// CHECK-LABEL: @extFPVectorConstantE8M0NaN
+// CHECK: %[[cres:.+]] = arith.constant dense<[1.000000e+00, 0x7FC00000]> : vector<2xf32>
+// CHECK: return %[[cres]]
+func.func @extFPVectorConstantE8M0NaN() -> vector<2xf32> {
+ %cst = arith.constant dense<[1.000000e+00, 0xFF]> : vector<2xf8E8M0FNU>
+ %0 = arith.extf %cst : vector<2xf8E8M0FNU> to vector<2xf32>
+ return %0 : vector<2xf32>
+}
+
// CHECK-LABEL: @truncExtf
// CHECK-NOT: truncf
// CHECK: return %arg0
``````````
</details>
https://github.com/llvm/llvm-project/pull/214919
More information about the llvm-commits
mailing list