Skip to content

[mlir][arith] Fix crash when constant-folding truncf of inf to FiniteOnly float type#186191

Merged
joker-eph merged 1 commit into
llvm:mainfrom
joker-eph:fix/issue-185351
Mar 12, 2026
Merged

[mlir][arith] Fix crash when constant-folding truncf of inf to FiniteOnly float type#186191
joker-eph merged 1 commit into
llvm:mainfrom
joker-eph:fix/issue-185351

Conversation

@joker-eph

Copy link
Copy Markdown
Contributor

When arith.truncf constant-folding converts a value such as infinity to a type with fltNonfiniteBehavior::FiniteOnly (e.g. f4E2M1FN), APFloat::convert hits an llvm_unreachable("semantics don't support inf!").

The fix adds early-exit guards in convertFloatValue() to return failure() before calling APFloat::convert() when the source value is infinity or NaN and the target type cannot represent it. This makes the fold a no-op for unrepresentable special values, matching the existing behavior for lossy ordinary conversions.

Fixes #185351

Assisted-by: Claude Code

…Only float type

When arith.truncf constant-folding converts a value such as infinity
to a type with fltNonfiniteBehavior::FiniteOnly (e.g. f4E2M1FN),
APFloat::convert hits an llvm_unreachable("semantics don't support inf\!").

The fix adds early-exit guards in convertFloatValue() to return failure()
before calling APFloat::convert() when the source value is infinity or NaN
and the target type cannot represent it. This makes the fold a no-op for
unrepresentable special values, matching the existing behavior for lossy
ordinary conversions.

Fixes llvm#185351

Assisted-by: Claude Code
@llvmbot

llvmbot commented Mar 12, 2026

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-mlir

Author: Mehdi Amini (joker-eph)

Changes

When arith.truncf constant-folding converts a value such as infinity to a type with fltNonfiniteBehavior::FiniteOnly (e.g. f4E2M1FN), APFloat::convert hits an llvm_unreachable("semantics don't support inf!").

The fix adds early-exit guards in convertFloatValue() to return failure() before calling APFloat::convert() when the source value is infinity or NaN and the target type cannot represent it. This makes the fold a no-op for unrepresentable special values, matching the existing behavior for lossy ordinary conversions.

Fixes #185351

Assisted-by: Claude Code


Full diff: https://github.com/llvm/llvm-project/pull/186191.diff

2 Files Affected:

  • (modified) mlir/lib/Dialect/Arith/IR/ArithOps.cpp (+11)
  • (modified) mlir/test/Dialect/Arith/canonicalize.mlir (+26)
diff --git a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
index 6f368604df65a..d6a98a667da32 100644
--- a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
@@ -1451,6 +1451,17 @@ static bool checkWidthChangeCast(TypeRange inputs, TypeRange outputs) {
 static FailureOr<APFloat> convertFloatValue(
     APFloat sourceValue, const llvm::fltSemantics &targetSemantics,
     llvm::RoundingMode roundingMode = llvm::RoundingMode::NearestTiesToEven) {
+  // Reject special values that are not representable in the target type before
+  // calling APFloat::convert, which would llvm_unreachable on them.
+  using fltNonfiniteBehavior = llvm::fltNonfiniteBehavior;
+  if (sourceValue.isInfinity() &&
+      (targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::NanOnly ||
+       targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::FiniteOnly))
+    return failure();
+  if (sourceValue.isNaN() &&
+      targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::FiniteOnly)
+    return failure();
+
   bool losesInfo = false;
   auto status = sourceValue.convert(targetSemantics, roundingMode, &losesInfo);
   if (losesInfo || status != APFloat::opOK)
diff --git a/mlir/test/Dialect/Arith/canonicalize.mlir b/mlir/test/Dialect/Arith/canonicalize.mlir
index 26b04e4209a43..643e4e076e7c6 100644
--- a/mlir/test/Dialect/Arith/canonicalize.mlir
+++ b/mlir/test/Dialect/Arith/canonicalize.mlir
@@ -3527,3 +3527,29 @@ func.func @cmpi_dynamic_shape_no_fold(%arg0: tensor<?xi32>) -> tensor<?xi1> {
   return %0 : tensor<?xi1>
 }
 
+// -----
+
+// arith.truncf of infinity to a FiniteOnly float type (f4E2M1FN) must not fold,
+// since the type has no infinity representation. Previously this would crash
+// inside APFloat::convert with llvm_unreachable("semantics don't support inf!").
+
+// CHECK-LABEL: @truncf_inf_to_finite_only_no_fold
+//       CHECK:   arith.truncf
+func.func @truncf_inf_to_finite_only_no_fold() -> f4E2M1FN {
+  %inf = arith.constant 0x7F800000 : f32
+  %result = arith.truncf %inf : f32 to f4E2M1FN
+  return %result : f4E2M1FN
+}
+
+// -----
+
+// arith.truncf of negative infinity to a FiniteOnly float type must not fold.
+
+// CHECK-LABEL: @truncf_neg_inf_to_finite_only_no_fold
+//       CHECK:   arith.truncf
+func.func @truncf_neg_inf_to_finite_only_no_fold() -> f4E2M1FN {
+  %neg_inf = arith.constant 0xFF800000 : f32
+  %result = arith.truncf %neg_inf : f32 to f4E2M1FN
+  return %result : f4E2M1FN
+}
+

@llvmbot

llvmbot commented Mar 12, 2026

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-mlir-arith

Author: Mehdi Amini (joker-eph)

Changes

When arith.truncf constant-folding converts a value such as infinity to a type with fltNonfiniteBehavior::FiniteOnly (e.g. f4E2M1FN), APFloat::convert hits an llvm_unreachable("semantics don't support inf!").

The fix adds early-exit guards in convertFloatValue() to return failure() before calling APFloat::convert() when the source value is infinity or NaN and the target type cannot represent it. This makes the fold a no-op for unrepresentable special values, matching the existing behavior for lossy ordinary conversions.

Fixes #185351

Assisted-by: Claude Code


Full diff: https://github.com/llvm/llvm-project/pull/186191.diff

2 Files Affected:

  • (modified) mlir/lib/Dialect/Arith/IR/ArithOps.cpp (+11)
  • (modified) mlir/test/Dialect/Arith/canonicalize.mlir (+26)
diff --git a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
index 6f368604df65a..d6a98a667da32 100644
--- a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
@@ -1451,6 +1451,17 @@ static bool checkWidthChangeCast(TypeRange inputs, TypeRange outputs) {
 static FailureOr<APFloat> convertFloatValue(
     APFloat sourceValue, const llvm::fltSemantics &targetSemantics,
     llvm::RoundingMode roundingMode = llvm::RoundingMode::NearestTiesToEven) {
+  // Reject special values that are not representable in the target type before
+  // calling APFloat::convert, which would llvm_unreachable on them.
+  using fltNonfiniteBehavior = llvm::fltNonfiniteBehavior;
+  if (sourceValue.isInfinity() &&
+      (targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::NanOnly ||
+       targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::FiniteOnly))
+    return failure();
+  if (sourceValue.isNaN() &&
+      targetSemantics.nonFiniteBehavior == fltNonfiniteBehavior::FiniteOnly)
+    return failure();
+
   bool losesInfo = false;
   auto status = sourceValue.convert(targetSemantics, roundingMode, &losesInfo);
   if (losesInfo || status != APFloat::opOK)
diff --git a/mlir/test/Dialect/Arith/canonicalize.mlir b/mlir/test/Dialect/Arith/canonicalize.mlir
index 26b04e4209a43..643e4e076e7c6 100644
--- a/mlir/test/Dialect/Arith/canonicalize.mlir
+++ b/mlir/test/Dialect/Arith/canonicalize.mlir
@@ -3527,3 +3527,29 @@ func.func @cmpi_dynamic_shape_no_fold(%arg0: tensor<?xi32>) -> tensor<?xi1> {
   return %0 : tensor<?xi1>
 }
 
+// -----
+
+// arith.truncf of infinity to a FiniteOnly float type (f4E2M1FN) must not fold,
+// since the type has no infinity representation. Previously this would crash
+// inside APFloat::convert with llvm_unreachable("semantics don't support inf!").
+
+// CHECK-LABEL: @truncf_inf_to_finite_only_no_fold
+//       CHECK:   arith.truncf
+func.func @truncf_inf_to_finite_only_no_fold() -> f4E2M1FN {
+  %inf = arith.constant 0x7F800000 : f32
+  %result = arith.truncf %inf : f32 to f4E2M1FN
+  return %result : f4E2M1FN
+}
+
+// -----
+
+// arith.truncf of negative infinity to a FiniteOnly float type must not fold.
+
+// CHECK-LABEL: @truncf_neg_inf_to_finite_only_no_fold
+//       CHECK:   arith.truncf
+func.func @truncf_neg_inf_to_finite_only_no_fold() -> f4E2M1FN {
+  %neg_inf = arith.constant 0xFF800000 : f32
+  %result = arith.truncf %neg_inf : f32 to f4E2M1FN
+  return %result : f4E2M1FN
+}
+

@joker-eph joker-eph merged commit bd05e1b into llvm:main Mar 12, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[MLIR][Arith] UNREACHABLE in APFloat when constant-folding arith.truncf of -inf into f4E2M1FN

3 participants