[Mlir-commits] [mlir] [mlir][tosa] Preserve all-NaN max-pool windows (PR #225744)
Jack Frankland
llvmlistbot at llvm.org
Thu Sep 24 03:14:32 PDT 2026
https://github.com/FranklandJack updated https://github.com/llvm/llvm-project/pull/225744
>From d03592f45eb5f0f899592398736d7f6dfcf00630 Mon Sep 17 00:00:00 2001
From: Jack Frankland <jack.frankland at arm.com>
Date: Wed, 23 Sep 2026 09:39:09 +0100
Subject: [PATCH] [mlir][tosa] Preserve all-NaN max-pool windows
Initialize IGNORE-mode floating-point max pooling with NaN and select
the first finite input. This keeps an all-NaN window NaN instead of
returning the lowest finite value.
Assited-by: Codex
---
.../TosaToLinalg/TosaToLinalgNamed.cpp | 31 +++++++++++++------
.../TosaToLinalg/tosa-to-linalg-named.mlir | 18 +++++++++++
2 files changed, 39 insertions(+), 10 deletions(-)
diff --git a/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgNamed.cpp b/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgNamed.cpp
index 4a11a927090bf..c6a931ac4f528 100644
--- a/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgNamed.cpp
+++ b/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgNamed.cpp
@@ -742,16 +742,21 @@ class MaxPool2dConverter : public OpConversionPattern<tosa::MaxPool2dOp> {
if (!resultTy)
return rewriter.notifyMatchFailure(op, "failed to convert type");
Type resultETy = inputTy.getElementType();
+ NanPropagationMode nanMode = op.getNanMode();
SmallVector<Value> dynamicDims =
computeDynamicOutputSizes(op, adaptor, rewriter);
// Determine what the initial value needs to be for the max pool op.
TypedAttr initialAttr;
- if (resultETy.isF32() || resultETy.isBF16() || resultETy.isF16())
+ if (resultETy.isF32() || resultETy.isBF16() || resultETy.isF16()) {
+ const llvm::fltSemantics &semantics =
+ cast<FloatType>(resultETy).getFloatSemantics();
initialAttr = rewriter.getFloatAttr(
- resultETy, APFloat::getLargest(
- cast<FloatType>(resultETy).getFloatSemantics(), true));
+ resultETy, nanMode == NanPropagationMode::IGNORE
+ ? APFloat::getNaN(semantics)
+ : APFloat::getLargest(semantics, true));
+ }
else if (isUnsigned)
initialAttr = rewriter.getIntegerAttr(
@@ -805,7 +810,6 @@ class MaxPool2dConverter : public OpConversionPattern<tosa::MaxPool2dOp> {
ValueRange{paddedInput, fakeWindowDims}, filledEmptyTensor, strideAttr,
dilationAttr);
- NanPropagationMode nanMode = op.getNanMode();
rewriter.replaceOp(op, resultOp);
// NaN propagation has no meaning for non floating point types.
@@ -817,8 +821,9 @@ class MaxPool2dConverter : public OpConversionPattern<tosa::MaxPool2dOp> {
//
// In the case of "IGNORE" we need to insert a compare and select. Since
// we've already produced a named op we will just take its body and modify
- // it to include the appropriate checks. If the current value is NaN the
- // old value of pool will be taken otherwise we use the result.
+ // it to include the appropriate checks. A NaN accumulator represents a
+ // window with no finite values yet. The first finite input replaces it;
+ // input NaNs are ignored. An all-NaN window therefore remains NaN.
if (nanMode == NanPropagationMode::IGNORE) {
auto genericOp = linalg::GenericOp::create(
rewriter, loc, resultOp.getType(0), resultOp.getInputs(),
@@ -831,12 +836,18 @@ class MaxPool2dConverter : public OpConversionPattern<tosa::MaxPool2dOp> {
auto &oldMaxOp = *resultOp.getBlock()->begin();
map.map(oldArgs, blockArgs);
auto *newOp = opBuilder.clone(oldMaxOp, map);
- Value isNaN =
+ Value inputIsNaN =
arith::CmpFOp::create(opBuilder, loc, arith::CmpFPredicate::UNO,
blockArgs.front(), blockArgs.front());
- auto selectOp = arith::SelectOp::create(
- opBuilder, loc, isNaN, blockArgs.back(), newOp->getResult(0));
- linalg::YieldOp::create(opBuilder, loc, selectOp.getResult());
+ Value accumulatorIsNaN =
+ arith::CmpFOp::create(opBuilder, loc, arith::CmpFPredicate::UNO,
+ blockArgs.back(), blockArgs.back());
+ Value finiteMaximum =
+ arith::SelectOp::create(opBuilder, loc, accumulatorIsNaN,
+ blockArgs.front(), newOp->getResult(0));
+ Value result = arith::SelectOp::create(
+ opBuilder, loc, inputIsNaN, blockArgs.back(), finiteMaximum);
+ linalg::YieldOp::create(opBuilder, loc, result);
});
rewriter.replaceOp(resultOp, genericOp);
}
diff --git a/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg-named.mlir b/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg-named.mlir
index a4abc687bfe38..effb2dd38c132 100644
--- a/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg-named.mlir
+++ b/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg-named.mlir
@@ -106,6 +106,24 @@ func.func @max_pool(%arg0: tensor<1x6x34x62xf32>) -> () {
return
}
+// -----
+
+// Lower IGNORE mode with a NaN accumulator so an all-NaN window stays NaN.
+// CHECK-LABEL: @max_pool_ignore
+func.func @max_pool_ignore(%arg0: tensor<1x4x4x1xf32>) -> tensor<1x3x3x1xf32> {
+ // CHECK: arith.constant {{.*}} : f32
+ // CHECK: linalg.fill
+ // CHECK: linalg.generic
+ // CHECK: arith.cmpf uno
+ // CHECK: arith.cmpf uno
+ // CHECK: arith.select
+ // CHECK: arith.select
+ // CHECK-NOT: tosa.max_pool2d
+ %0 = tosa.max_pool2d %arg0 kernel([2, 2]) stride([1, 1]) pad([0, 0, 0, 0]) nan_mode<IGNORE> :
+ (tensor<1x4x4x1xf32>) -> tensor<1x3x3x1xf32>
+ return %0 : tensor<1x3x3x1xf32>
+}
+
// CHECK-LABEL: @max_pool_padded
func.func @max_pool_padded(%arg0: tensor<1x6x34x62xf32>) -> () {
// CHECK-DAG: [[CONST:%.+]] = arith.constant -3.40282347E+38 : f32
More information about the Mlir-commits
mailing list