[Mlir-commits] [mlir] [mlir][arith] Don't fold to negative values of unsigned float types (PR #230974)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Sun Oct 11 07:45:45 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir-arith
@llvm/pr-subscribers-mlir
Author: SomnusGG (SomnusGG)
<details>
<summary>Changes</summary>
Some float types, such as f8E8M0FNU, have no sign bit
(`APFloat::semanticsHasSignedRepr` is false). Three folders could produce a
negative value of such a type and crashed:
* `arith.subf` with `lhs < rhs` called `APFloat::subtract`, which hits
`llvm_unreachable("This floating point format does not support signed
values")`.
* `arith.negf` and `arith.sitofp` of a negative integer created a negative
f8E8M0FNU attribute. Printing that attribute hit the same
`llvm_unreachable` in `APFloat::convertFromString`.
Reproducer (`mlir-opt -canonicalize`):
%a = arith.constant 2.0 : f8E8M0FNU
%0 = arith.negf %a : f8E8M0FNU
These folders now leave the op alone when the result would be negative.
This matches how `arith.truncf` already treats a negative value converted to
f8E8M0FNU (see `@<!-- -->truncFPConstantE8M0Negative`). Non-negative results,
such as `subf 4.0, 2.0`, still fold.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---
Full diff: https://github.com/llvm/llvm-project/pull/230974.diff
2 Files Affected:
- (modified) mlir/lib/Dialect/Arith/IR/ArithOps.cpp (+21-4)
- (modified) mlir/test/Dialect/Arith/canonicalize.mlir (+61)
``````````diff
diff --git a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
index ece86f2e597d6..95e5c1538b66b 100644
--- a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
@@ -1267,8 +1267,13 @@ OpFoldResult arith::NegFOp::fold(FoldAdaptor adaptor) {
/// negf(negf(x)) -> x
if (auto op = this->getOperand().getDefiningOp<arith::NegFOp>())
return op.getOperand();
- return constFoldUnaryOp<FloatAttr>(adaptor.getOperands(),
- [](const APFloat &a) { return -a; });
+ return constFoldUnaryOpConditional<FloatAttr>(
+ adaptor.getOperands(), [](const APFloat &a) -> std::optional<APFloat> {
+ // Types such as f8E8M0FNU cannot represent a negated value.
+ if (!APFloat::semanticsHasSignedRepr(a.getSemantics()))
+ return std::nullopt;
+ return -a;
+ });
}
//===----------------------------------------------------------------------===//
@@ -1332,8 +1337,14 @@ OpFoldResult arith::SubFOp::fold(FoldAdaptor adaptor) {
return getLhs();
auto rm = getRoundingmode();
- return constFoldBinaryOp<FloatAttr>(
- adaptor.getOperands(), [rm](const APFloat &a, const APFloat &b) {
+ return constFoldBinaryOpConditional<FloatAttr>(
+ adaptor.getOperands(),
+ [rm](const APFloat &a, const APFloat &b) -> std::optional<APFloat> {
+ // A negative difference is not representable in types such as
+ // f8E8M0FNU, and APFloat::subtract does not support computing it.
+ if (!APFloat::semanticsHasSignedRepr(a.getSemantics()) &&
+ a.compare(b) == APFloat::cmpLessThan)
+ return std::nullopt;
APFloat result(a);
result.subtract(b, convertArithRoundingModeToLLVMIR(rm));
return result;
@@ -2293,6 +2304,12 @@ OpFoldResult arith::SIToFPOp::fold(FoldAdaptor adaptor) {
FloatType floatTy = llvm::cast<FloatType>(resEleType);
APFloat apf(floatTy.getFloatSemantics(),
APInt::getZero(floatTy.getWidth()));
+ // Types such as f8E8M0FNU cannot represent a negative value.
+ if (a.isNegative() &&
+ !APFloat::semanticsHasSignedRepr(floatTy.getFloatSemantics())) {
+ castStatus = false;
+ return apf;
+ }
apf.convertFromAPInt(a, /*IsSigned=*/true,
APFloat::rmNearestTiesToEven);
return apf;
diff --git a/mlir/test/Dialect/Arith/canonicalize.mlir b/mlir/test/Dialect/Arith/canonicalize.mlir
index 9c512cd605d7d..0da65a4a0f402 100644
--- a/mlir/test/Dialect/Arith/canonicalize.mlir
+++ b/mlir/test/Dialect/Arith/canonicalize.mlir
@@ -1765,6 +1765,67 @@ func.func @truncFPConstantE8M0Zero() -> f8E8M0FNU {
return %0 : f8E8M0FNU
}
+// The negation of an f8E8M0FNU value is not representable, so it is NOT
+// folded. Folding used to create a negative f8E8M0FNU attribute, and printing
+// it asserted in APFloat.
+// CHECK-LABEL: @negfConstantE8M0
+// CHECK: %[[CST:.+]] = arith.constant 2.000000e+00 : f8E8M0FNU
+// CHECK: %[[RES:.+]] = arith.negf %[[CST]] : f8E8M0FNU
+// CHECK: return %[[RES]]
+func.func @negfConstantE8M0() -> f8E8M0FNU {
+ %cst = arith.constant 2.000000e+00 : f8E8M0FNU
+ %0 = arith.negf %cst : f8E8M0FNU
+ return %0 : f8E8M0FNU
+}
+
+// A negative difference of f8E8M0FNU values is not representable, so it is
+// NOT folded. Folding used to assert in APFloat::subtract.
+// CHECK-LABEL: @subfConstantE8M0Negative
+// CHECK-DAG: %[[LHS:.+]] = arith.constant 2.000000e+00 : f8E8M0FNU
+// CHECK-DAG: %[[RHS:.+]] = arith.constant 4.000000e+00 : f8E8M0FNU
+// CHECK: %[[RES:.+]] = arith.subf %[[LHS]], %[[RHS]] : f8E8M0FNU
+// CHECK: return %[[RES]]
+func.func @subfConstantE8M0Negative() -> f8E8M0FNU {
+ %lhs = arith.constant 2.000000e+00 : f8E8M0FNU
+ %rhs = arith.constant 4.000000e+00 : f8E8M0FNU
+ %0 = arith.subf %lhs, %rhs : f8E8M0FNU
+ return %0 : f8E8M0FNU
+}
+
+// A non-negative difference is still folded.
+// CHECK-LABEL: @subfConstantE8M0
+// CHECK: %[[CST:.+]] = arith.constant 2.000000e+00 : f8E8M0FNU
+// CHECK: return %[[CST]]
+func.func @subfConstantE8M0() -> f8E8M0FNU {
+ %lhs = arith.constant 4.000000e+00 : f8E8M0FNU
+ %rhs = arith.constant 2.000000e+00 : f8E8M0FNU
+ %0 = arith.subf %lhs, %rhs : f8E8M0FNU
+ return %0 : f8E8M0FNU
+}
+
+// A negative integer is not representable in f8E8M0FNU, so it is NOT folded.
+// Folding used to create a negative f8E8M0FNU attribute, and printing it
+// asserted in APFloat.
+// CHECK-LABEL: @sitofpConstantE8M0Negative
+// CHECK: %[[CST:.+]] = arith.constant -4 : i32
+// CHECK: %[[RES:.+]] = arith.sitofp %[[CST]] : i32 to f8E8M0FNU
+// CHECK: return %[[RES]]
+func.func @sitofpConstantE8M0Negative() -> f8E8M0FNU {
+ %cst = arith.constant -4 : i32
+ %0 = arith.sitofp %cst : i32 to f8E8M0FNU
+ return %0 : f8E8M0FNU
+}
+
+// A positive integer is still folded.
+// CHECK-LABEL: @sitofpConstantE8M0
+// CHECK: %[[CST:.+]] = arith.constant 4.000000e+00 : f8E8M0FNU
+// CHECK: return %[[CST]]
+func.func @sitofpConstantE8M0() -> f8E8M0FNU {
+ %cst = arith.constant 4 : i32
+ %0 = arith.sitofp %cst : i32 to f8E8M0FNU
+ return %0 : f8E8M0FNU
+}
+
// Unlike arith.scaling_extf, this divides by 2^scale: 6.0 / 2^1.
// CHECK-LABEL: @scalingTruncFConstant
// CHECK: %[[cres:.+]] = arith.constant 3.000000e+00 : f4E2M1FN
``````````
</details>
https://github.com/llvm/llvm-project/pull/230974
More information about the Mlir-commits
mailing list