[Mlir-commits] [mlir] [mlir][arith] Honor finite min/max reduction identities (PR #232257)

Anutosh Bhat llvmlistbot at llvm.org
Thu Oct 15 03:16:33 PDT 2026


https://github.com/anutosh491 created https://github.com/llvm/llvm-project/pull/232257

I hit this while compiling a JAX transformer block through IREE/WebGPU in JupyterLite. `linalg.softmax` requests a finite reduction initializer, but `getIdentityValueAttr` returns NaN for `maxnumf`, which Tint rejects.

Honor `useOnlyFiniteValue` for `maxnumf` and `minnumf`, preserving the existing NaN identities when the flag is false. Update the existing softmax check and add finite min/max reduction cases.

>From d02e28cbcd168eb05b79a35c17bc4a962e3dc994 Mon Sep 17 00:00:00 2001
From: anutosh491 <andersonbhat491 at gmail.com>
Date: Thu, 15 Oct 2026 15:44:54 +0530
Subject: [PATCH] [mlir][arith] Honor finite min/max reduction identities

---
 mlir/lib/Dialect/Arith/IR/ArithOps.cpp        |  8 +++-
 .../Linalg/transform-op-decompose.mlir        |  2 +-
 .../Linalg/transform-op-split-reduction.mlir  | 44 +++++++++++++++++++
 3 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
index 40fcfa5c458819..09aea5c8eba837 100644
--- a/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ArithOps.cpp
@@ -3316,7 +3316,9 @@ TypedAttr mlir::arith::getIdentityValueAttr(AtomicRMWKind kind, Type resultType,
   case AtomicRMWKind::maxnumf: {
     const llvm::fltSemantics &semantic =
         llvm::cast<FloatType>(resultType).getFloatSemantics();
-    APFloat identity = APFloat::getNaN(semantic, /*Negative=*/true);
+    APFloat identity = useOnlyFiniteValue
+                           ? APFloat::getLargest(semantic, /*Negative=*/true)
+                           : APFloat::getNaN(semantic, /*Negative=*/true);
     return builder.getFloatAttr(resultType, identity);
   }
   case AtomicRMWKind::addf:
@@ -3345,7 +3347,9 @@ TypedAttr mlir::arith::getIdentityValueAttr(AtomicRMWKind kind, Type resultType,
   case AtomicRMWKind::minnumf: {
     const llvm::fltSemantics &semantic =
         llvm::cast<FloatType>(resultType).getFloatSemantics();
-    APFloat identity = APFloat::getNaN(semantic, /*Negative=*/false);
+    APFloat identity = useOnlyFiniteValue
+                           ? APFloat::getLargest(semantic, /*Negative=*/false)
+                           : APFloat::getNaN(semantic, /*Negative=*/false);
     return builder.getFloatAttr(resultType, identity);
   }
   case AtomicRMWKind::mins:
diff --git a/mlir/test/Dialect/Linalg/transform-op-decompose.mlir b/mlir/test/Dialect/Linalg/transform-op-decompose.mlir
index 3897f8502bb04e..05a01b0a20eb36 100644
--- a/mlir/test/Dialect/Linalg/transform-op-decompose.mlir
+++ b/mlir/test/Dialect/Linalg/transform-op-decompose.mlir
@@ -369,7 +369,7 @@ func.func @softmax(%arg0: tensor<2x16x32xf32>, %dst: tensor<2x16x32xf32>) -> ten
 // CHECK-LABEL:      func.func @softmax(
 // CHECK-SAME:           %[[ARG0:[a-zA-Z0-9_]+]]: tensor<2x16x32xf32>, %[[DST:[a-zA-Z0-9_]+]]: tensor<2x16x32xf32>) -> tensor<2x16x32xf32> {
 // CHECK-DAG:        %[[D1:.+]] = tensor.empty() : tensor<2x16xf32>
-// CHECK-DAG:        %[[CST:.+]] = arith.constant 0xFFC00000 : f32
+// CHECK-DAG:        %[[CST:.+]] = arith.constant -3.40282347E+38 : f32
 // CHECK:        %[[D2:.+]] = linalg.fill ins(%[[CST]] : f32) outs(%[[D1]] : tensor<2x16xf32>) -> tensor<2x16xf32>
 // CHECK:        %[[D3:.+]] = linalg.generic {indexing_maps = [#[[$MAP]], #[[$MAP1]]], iterator_types = ["parallel",
 // CHECK-SAME:     "parallel", "reduction"]} ins(%[[ARG0]] : tensor<2x16x32xf32>) outs(%[[D2]] : tensor<2x16xf32>) {
diff --git a/mlir/test/Dialect/Linalg/transform-op-split-reduction.mlir b/mlir/test/Dialect/Linalg/transform-op-split-reduction.mlir
index 82111f911aa244..de23fc1af6dc07 100644
--- a/mlir/test/Dialect/Linalg/transform-op-split-reduction.mlir
+++ b/mlir/test/Dialect/Linalg/transform-op-split-reduction.mlir
@@ -499,3 +499,47 @@ module attributes {transform.with_named_sequence} {
       transform.yield
   }
 }
+
+// -----
+
+// Finite identities must also be used for NaN-ignoring min/max reductions.
+// CHECK-LABEL: func.func @maxnumf_ninf
+// CHECK: %[[MAX_INIT:.*]] = arith.constant -3.40282347E+38 : f32
+// CHECK: linalg.fill ins(%[[MAX_INIT]] : f32)
+// CHECK: arith.maxnumf {{.*}} fastmath<nnan,ninf> : f32
+func.func @maxnumf_ninf(%in: tensor<32xf32>, %out: tensor<f32>) -> tensor<f32> {
+  %r = linalg.generic {
+    indexing_maps = [affine_map<(d0) -> (d0)>, affine_map<(d0) -> ()>],
+    iterator_types = ["reduction"]
+  } ins(%in : tensor<32xf32>) outs(%out : tensor<f32>) {
+  ^bb0(%value: f32, %acc: f32):
+    %max = arith.maxnumf %value, %acc fastmath<nnan,ninf> : f32
+    linalg.yield %max : f32
+  } -> tensor<f32>
+  return %r : tensor<f32>
+}
+
+// CHECK-LABEL: func.func @minnumf_ninf
+// CHECK: %[[MIN_INIT:.*]] = arith.constant 3.40282347E+38 : f32
+// CHECK: linalg.fill ins(%[[MIN_INIT]] : f32)
+// CHECK: arith.minnumf {{.*}} fastmath<nnan,ninf> : f32
+func.func @minnumf_ninf(%in: tensor<32xf32>, %out: tensor<f32>) -> tensor<f32> {
+  %r = linalg.generic {
+    indexing_maps = [affine_map<(d0) -> (d0)>, affine_map<(d0) -> ()>],
+    iterator_types = ["reduction"]
+  } ins(%in : tensor<32xf32>) outs(%out : tensor<f32>) {
+  ^bb0(%value: f32, %acc: f32):
+    %min = arith.minnumf %value, %acc fastmath<nnan,ninf> : f32
+    linalg.yield %min : f32
+  } -> tensor<f32>
+  return %r : tensor<f32>
+}
+
+module attributes {transform.with_named_sequence} {
+  transform.named_sequence @__transform_main(%root: !transform.any_op {transform.readonly}) {
+    %ops = transform.structured.match ops{["linalg.generic"]} in %root : (!transform.any_op) -> !transform.any_op
+    %split:4 = transform.structured.split_reduction %ops <split_factor = 4, insert_split_dimension = 0, inner_parallel>
+      : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op, !transform.any_op)
+    transform.yield
+  }
+}



More information about the Mlir-commits mailing list