[Mlir-commits] [mlir] a5a17c5 - [mlir][arith] Don't fold to negative values of unsigned float types (#230974)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Tue Oct 13 11:56:28 PDT 2026
Author: SomnusGG
Date: 2026-10-13T11:56:21-07:00
New Revision: a5a17c547afa24d28d0d36a4a771cf86174b5b86
URL: https://github.com/llvm/llvm-project/commit/a5a17c547afa24d28d0d36a4a771cf86174b5b86
DIFF: https://github.com/llvm/llvm-project/commit/a5a17c547afa24d28d0d36a4a771cf86174b5b86.diff
LOG: [mlir][arith] Don't fold to negative values of unsigned float types (#230974)
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)
---------
Co-authored-by: Claude Opus 5.5 <noreply at anthropic.com>
Added:
Modified:
mlir/lib/Dialect/Arith/IR/ArithOps.cpp
mlir/test/Dialect/Arith/canonicalize.mlir
Removed:
################################################################################
diff --git a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
index ece86f2e597d68..40fcfa5c458819 100644
--- a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
@@ -1267,8 +1267,12 @@ 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> {
+ if (!APFloat::semanticsHasSignedRepr(a.getSemantics()))
+ return std::nullopt;
+ return -a;
+ });
}
//===----------------------------------------------------------------------===//
@@ -1332,8 +1336,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
diff erence 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 +2303,11 @@ OpFoldResult arith::SIToFPOp::fold(FoldAdaptor adaptor) {
FloatType floatTy = llvm::cast<FloatType>(resEleType);
APFloat apf(floatTy.getFloatSemantics(),
APInt::getZero(floatTy.getWidth()));
+ 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 9c512cd605d7d8..0da65a4a0f4028 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
diff erence 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
diff erence 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
More information about the Mlir-commits
mailing list