[Mlir-commits] [mlir] [mlir][tosa] Add allow-non-finites option for float min/max reduction (PR #222140)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Tue Sep 8 13:53:07 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir-tosa

Author: Hariprasad Ravishankar (hariprasadravi)

<details>
<summary>Changes</summary>

TosaToLinalg seeds float min/max reductions with the largest finite value of the element type, but the TOSA spec asks for the infinite bound. [§2.9.4](https://www.mlplatform.org/tosa/tosa_spec_1_0_2.html#_reduce_min) seeds REDUCE_MIN with `maximum_s<in_out_t>()`, [§4.5.1](https://www.mlplatform.org/tosa/tosa_spec_1_0_2.html#_arithmetic_helpers) defines that as the type maximum after `make_signed`, [§4.5.2](https://www.mlplatform.org/tosa/tosa_spec_1_0_2.html#_type_conversion_helpers) makes `make_signed` a no-op for floats, and Table 5 in [§1.9](https://www.mlplatform.org/tosa/tosa_spec_1_0_2.html#_supported_number_formats) gives that maximum as `+infinity` for fp16, bf16, fp32 and fp64. REDUCE_MAX and ARGMAX mirror it. The symptom is a clamp: reduce an all-`+Inf` tensor and you get `3.40282347E+38` back.

This adds an `allow-non-finites` option to `tosa-to-linalg` that selects the conforming seed.

**Why a flag rather than just fixing the seed.** Non-finite values aren't always wanted. TOSA has a format that can't represent them at all — Table 5 bounds fp8e4m3_t at ±448, "This format has no encoding for infinities" — and embedded code generation often disables them wholesale to avoid the support code, as MATLAB Coder's `SupportNonFinite` does. llvm/torch-mlir#<!-- -->4363 added the same toggle under the same name to ConvertTorchToLinalg and friends; today setting it `true` is silently dropped for graphs routed through TOSA, so the two backends disagree on the same model. This makes the flag mean something on both paths.

**Why the default is false.** It keeps this pass's current output. torch-mlir defaults to `true` because it already emitted `-inf`, so in both projects the default is the no-op, and setting the flag the same way on both sides is what makes the paths agree.

**The `semanticsHasInf` guard.** `APFloat::getInf` is `llvm_unreachable` under FiniteOnly semantics and silently returns a NaN under NanOnly semantics, e.g. f8E4M3FN. Without the guard every fp8 min/max reduction would return NaN through NaN-propagating `arith.minimumf`, which is worse than the bug being fixed. Tests pin f8E4M3FN to the ±448 fallback with the option both on and off.

Three smaller notes. The flag governs only identities the pass invents, not the NaN that `nan_mode = IGNORE` is required to produce. ARGMAX is updated for uniformity but is NFC in effect, since its running max only feeds the `UGT`/`OGT` predicate and its value output is discarded. `max_pool2d` in TosaToLinalgNamed.cpp is wrong the same way but left alone here, because its initial value doubles as the `tensor.pad` padding value and needs its own review.

Every pre-existing test in tosa-to-linalg.mlir now runs under both settings, via a second RUN line sharing the CHECK prefix with only the four seed constants split into FINITE/NONFINITE. New cases cover f16, bf16 and f64 min/max, argmax f16, both f8E4M3FN fallbacks, and the IGNORE seed. check-mlir-conversion is green.


---

Patch is 20.87 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/222140.diff


5 Files Affected:

- (modified) mlir/include/mlir/Conversion/Passes.td (+8-1) 
- (modified) mlir/include/mlir/Conversion/TosaToLinalg/TosaToLinalg.h (+5-3) 
- (modified) mlir/lib/Conversion/TosaToLinalg/TosaToLinalg.cpp (+58-22) 
- (modified) mlir/lib/Conversion/TosaToLinalg/TosaToLinalgPass.cpp (+11-4) 
- (modified) mlir/test/Conversion/TosaToLinalg/tosa-to-linalg.mlir (+172-5) 


``````````diff
diff --git a/mlir/include/mlir/Conversion/Passes.td b/mlir/include/mlir/Conversion/Passes.td
index f13cb9a801139..24c264c11a816 100644
--- a/mlir/include/mlir/Conversion/Passes.td
+++ b/mlir/include/mlir/Conversion/Passes.td
@@ -1421,7 +1421,14 @@ def TosaToLinalg
            "Disable tosa decompositions pass">,
     Option<"aggressiveReduceConstant", "aggressive-reduce-constant",
            "bool", /*default=*/"false",
-           "Always perform the reduce constant optimization">
+           "Always perform the reduce constant optimization">,
+    Option<"allowNonFinites", "allow-non-finites", "bool",
+           /*default=*/"false",
+           "When enabled, float min/max reductions are seeded with the "
+           "infinite identity mandated by the TOSA specification. When "
+           "disabled (default), the largest finite value is used instead. "
+           "This controls only compiler-generated identity values; NaN "
+           "results required by nan_mode are unaffected.">
   ];
 }
 
diff --git a/mlir/include/mlir/Conversion/TosaToLinalg/TosaToLinalg.h b/mlir/include/mlir/Conversion/TosaToLinalg/TosaToLinalg.h
index c1d28528a2f90..06914be338aaf 100644
--- a/mlir/include/mlir/Conversion/TosaToLinalg/TosaToLinalg.h
+++ b/mlir/include/mlir/Conversion/TosaToLinalg/TosaToLinalg.h
@@ -24,7 +24,8 @@ namespace mlir {
 
 namespace tosa {
 
-std::unique_ptr<Pass> createTosaToLinalg();
+std::unique_ptr<Pass>
+createTosaToLinalg(const TosaToLinalgOptions &options = TosaToLinalgOptions());
 std::unique_ptr<Pass> createTosaToLinalgNamed(
     const TosaToLinalgNamedOptions &options = TosaToLinalgNamedOptions());
 
@@ -46,8 +47,9 @@ void addTosaToLinalgPasses(
 void registerTosaToLinalgPipelines();
 
 /// Populates conversion passes from TOSA dialect to Linalg dialect.
-void populateTosaToLinalgConversionPatterns(const TypeConverter &converter,
-                                            RewritePatternSet *patterns);
+void populateTosaToLinalgConversionPatterns(
+    const TypeConverter &converter, RewritePatternSet *patterns,
+    const TosaToLinalgOptions &options = TosaToLinalgOptions());
 
 /// Populates conversion passes from TOSA dialect to Linalg named operations.
 void populateTosaToLinalgNamedConversionPatterns(
diff --git a/mlir/lib/Conversion/TosaToLinalg/TosaToLinalg.cpp b/mlir/lib/Conversion/TosaToLinalg/TosaToLinalg.cpp
index b7eb0a3aed546..300ce91819e19 100644
--- a/mlir/lib/Conversion/TosaToLinalg/TosaToLinalg.cpp
+++ b/mlir/lib/Conversion/TosaToLinalg/TosaToLinalg.cpp
@@ -1056,10 +1056,26 @@ elementwiseMatchAndRewriteHelper(Operation *operation, ValueRange operands,
                                     targetShape, converter);
 }
 
+// Returns the identity value to seed a float min/max reduction with. TOSA seeds
+// REDUCE_MIN with maximum_s<in_out_t>() and REDUCE_MAX/ARGMAX with
+// minimum_s<in_out_t>(), and for floating-point types those bounds are
+// +/-infinity rather than the largest finite value. Only use them when the
+// caller opted in *and* the format can represent them: APFloat::getInf() is
+// unreachable for FiniteOnly semantics and silently returns a NaN for NanOnly
+// semantics such as f8E4M3FN, which would poison the whole reduction through
+// NaN-propagating arith.minimumf/arith.maximumf.
+static APFloat getFloatMinMaxIdentity(const llvm::fltSemantics &semantics,
+                                      bool negative, bool allowNonFinites) {
+  if (allowNonFinites && APFloat::semanticsHasInf(semantics))
+    return APFloat::getInf(semantics, negative);
+  return APFloat::getLargest(semantics, negative);
+}
+
 // Returns the constant initial value for a given reduction operation. The
 // attribute type varies depending on the element type required.
 static TypedAttr createInitialValueForReduceOp(Operation *op, Type elementTy,
-                                               PatternRewriter &rewriter) {
+                                               PatternRewriter &rewriter,
+                                               bool allowNonFinites) {
   if (isa<tosa::ReduceSumOp>(op) && isa<FloatType>(elementTy))
     return rewriter.getFloatAttr(elementTy, 0.0);
 
@@ -1074,8 +1090,9 @@ static TypedAttr createInitialValueForReduceOp(Operation *op, Type elementTy,
 
   if (isa<tosa::ReduceMinOp>(op) && isa<FloatType>(elementTy))
     return rewriter.getFloatAttr(
-        elementTy, APFloat::getLargest(
-                       cast<FloatType>(elementTy).getFloatSemantics(), false));
+        elementTy,
+        getFloatMinMaxIdentity(cast<FloatType>(elementTy).getFloatSemantics(),
+                               /*negative=*/false, allowNonFinites));
 
   if (isa<tosa::ReduceMinOp>(op) && isa<IntegerType>(elementTy))
     return rewriter.getIntegerAttr(
@@ -1083,8 +1100,9 @@ static TypedAttr createInitialValueForReduceOp(Operation *op, Type elementTy,
 
   if (isa<tosa::ReduceMaxOp>(op) && isa<FloatType>(elementTy))
     return rewriter.getFloatAttr(
-        elementTy, APFloat::getLargest(
-                       cast<FloatType>(elementTy).getFloatSemantics(), true));
+        elementTy,
+        getFloatMinMaxIdentity(cast<FloatType>(elementTy).getFloatSemantics(),
+                               /*negative=*/true, allowNonFinites));
 
   if (isa<tosa::ReduceMaxOp>(op) && isa<IntegerType>(elementTy))
     return rewriter.getIntegerAttr(
@@ -1098,8 +1116,9 @@ static TypedAttr createInitialValueForReduceOp(Operation *op, Type elementTy,
 
   if (isa<tosa::ArgMaxOp>(op) && isa<FloatType>(elementTy))
     return rewriter.getFloatAttr(
-        elementTy, APFloat::getLargest(
-                       cast<FloatType>(elementTy).getFloatSemantics(), true));
+        elementTy,
+        getFloatMinMaxIdentity(cast<FloatType>(elementTy).getFloatSemantics(),
+                               /*negative=*/true, allowNonFinites));
 
   if (isa<tosa::ArgMaxOp>(op) && isa<IntegerType>(elementTy))
     return rewriter.getIntegerAttr(
@@ -1161,7 +1180,8 @@ static Value createLinalgBodyCalculationForReduceOp(Operation *op,
 // that reduces across the specified axis.
 template <typename OpTy>
 static LogicalResult reduceMatchAndRewriteHelper(OpTy op, uint64_t axis,
-                                                 PatternRewriter &rewriter) {
+                                                 PatternRewriter &rewriter,
+                                                 bool allowNonFinites) {
   auto loc = op->getLoc();
   auto inputTy = dyn_cast<RankedTensorType>(op->getOperand(0).getType());
   auto resultTy = dyn_cast<RankedTensorType>(op->getResult(0).getType());
@@ -1195,7 +1215,8 @@ static LogicalResult reduceMatchAndRewriteHelper(OpTy op, uint64_t axis,
       tensor::EmptyOp::create(rewriter, loc, reduceShape, accTy, dynDims)
           .getResult();
 
-  auto fillValueAttr = createInitialValueForReduceOp(op, accTy, rewriter);
+  auto fillValueAttr =
+      createInitialValueForReduceOp(op, accTy, rewriter, allowNonFinites);
   if (!fillValueAttr)
     return rewriter.notifyMatchFailure(
         op, "No initial value found for reduction operation");
@@ -2238,12 +2259,17 @@ class IdentityNConverter : public OpRewritePattern<SrcOp> {
 template <typename SrcOp>
 class ReduceConverter : public OpRewritePattern<SrcOp> {
 public:
-  using OpRewritePattern<SrcOp>::OpRewritePattern;
+  ReduceConverter(MLIRContext *context, bool allowNonFinites)
+      : OpRewritePattern<SrcOp>(context), allowNonFinites(allowNonFinites) {}
 
   LogicalResult matchAndRewrite(SrcOp reduceOp,
                                 PatternRewriter &rewriter) const final {
-    return reduceMatchAndRewriteHelper(reduceOp, reduceOp.getAxis(), rewriter);
+    return reduceMatchAndRewriteHelper(reduceOp, reduceOp.getAxis(), rewriter,
+                                       allowNonFinites);
   }
+
+private:
+  bool allowNonFinites;
 };
 
 class ReverseConverter : public OpRewritePattern<tosa::ReverseOp> {
@@ -2387,7 +2413,9 @@ struct TileConverter : public OpConversionPattern<tosa::TileOp> {
 // current value exceeds the running max.
 class ArgMaxConverter : public OpRewritePattern<tosa::ArgMaxOp> {
 public:
-  using OpRewritePattern<tosa::ArgMaxOp>::OpRewritePattern;
+  ArgMaxConverter(MLIRContext *context, bool allowNonFinites)
+      : OpRewritePattern<tosa::ArgMaxOp>(context),
+        allowNonFinites(allowNonFinites) {}
 
   LogicalResult matchAndRewrite(tosa::ArgMaxOp argmaxOp,
                                 PatternRewriter &rewriter) const final {
@@ -2429,8 +2457,8 @@ class ArgMaxConverter : public OpRewritePattern<tosa::ArgMaxOp> {
         tensor::EmptyOp::create(rewriter, loc, resultTy.getShape(), inElementTy,
                                 dynDims)
             .getResult();
-    auto fillValueMaxAttr =
-        createInitialValueForReduceOp(argmaxOp, inElementTy, rewriter);
+    auto fillValueMaxAttr = createInitialValueForReduceOp(
+        argmaxOp, inElementTy, rewriter, allowNonFinites);
 
     if (!fillValueMaxAttr)
       return rewriter.notifyMatchFailure(
@@ -2518,6 +2546,9 @@ class ArgMaxConverter : public OpRewritePattern<tosa::ArgMaxOp> {
     rewriter.replaceOp(argmaxOp, linalgOp.getResult(0));
     return success();
   }
+
+private:
+  bool allowNonFinites;
 };
 
 class GatherConverter : public OpConversionPattern<tosa::GatherOp> {
@@ -3061,7 +3092,8 @@ struct FFT2dConverter final : OpRewritePattern<FFT2dOp> {
 } // namespace
 
 void mlir::tosa::populateTosaToLinalgConversionPatterns(
-    const TypeConverter &converter, RewritePatternSet *patterns) {
+    const TypeConverter &converter, RewritePatternSet *patterns,
+    const TosaToLinalgOptions &options) {
 
   // We have multiple resize coverters to handle degenerate cases.
   patterns->add<GenericResizeConverter>(patterns->getContext(),
@@ -3115,13 +3147,6 @@ void mlir::tosa::populateTosaToLinalgConversionPatterns(
 
   patterns->add<
       IdentityNConverter<tosa::IdentityOp>,
-      ReduceConverter<tosa::ReduceAllOp>,
-      ReduceConverter<tosa::ReduceAnyOp>,
-      ReduceConverter<tosa::ReduceMinOp>,
-      ReduceConverter<tosa::ReduceMaxOp>,
-      ReduceConverter<tosa::ReduceSumOp>,
-      ReduceConverter<tosa::ReduceProductOp>,
-      ArgMaxConverter,
       GatherConverter,
       RescaleConverter,
       ReverseConverter,
@@ -3129,5 +3154,16 @@ void mlir::tosa::populateTosaToLinalgConversionPatterns(
       FFT2dConverter,
       TableConverter,
       TileConverter>(patterns->getContext());
+
+  // Reductions seeded with a float min/max identity need to know whether
+  // non-finite values are available on the target.
+  patterns->add<
+      ReduceConverter<tosa::ReduceAllOp>,
+      ReduceConverter<tosa::ReduceAnyOp>,
+      ReduceConverter<tosa::ReduceMinOp>,
+      ReduceConverter<tosa::ReduceMaxOp>,
+      ReduceConverter<tosa::ReduceSumOp>,
+      ReduceConverter<tosa::ReduceProductOp>,
+      ArgMaxConverter>(patterns->getContext(), options.allowNonFinites);
   // clang-format on
 }
diff --git a/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgPass.cpp b/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgPass.cpp
index ca09299619bdb..0ce1cd14ea7f1 100644
--- a/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgPass.cpp
+++ b/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgPass.cpp
@@ -38,6 +38,9 @@ using namespace mlir;
 namespace {
 struct TosaToLinalg : public impl::TosaToLinalgBase<TosaToLinalg> {
 public:
+  TosaToLinalg(const TosaToLinalgOptions &options)
+      : impl::TosaToLinalgBase<TosaToLinalg>(options) {}
+
   void getDependentDialects(DialectRegistry &registry) const override {
     registry
         .insert<arith::ArithDialect, linalg::LinalgDialect, math::MathDialect,
@@ -68,15 +71,19 @@ struct TosaToLinalg : public impl::TosaToLinalgBase<TosaToLinalg> {
     tosa::populateTosaTypeConversion(converter);
 
     FunctionOpInterface func = getOperation();
-    mlir::tosa::populateTosaToLinalgConversionPatterns(converter, &patterns);
+    TosaToLinalgOptions options;
+    options.allowNonFinites = allowNonFinites;
+    mlir::tosa::populateTosaToLinalgConversionPatterns(converter, &patterns,
+                                                       options);
     if (failed(applyFullConversion(func, target, std::move(patterns))))
       signalPassFailure();
   }
 };
 } // namespace
 
-std::unique_ptr<Pass> mlir::tosa::createTosaToLinalg() {
-  return std::make_unique<TosaToLinalg>();
+std::unique_ptr<Pass>
+mlir::tosa::createTosaToLinalg(const TosaToLinalgOptions &options) {
+  return std::make_unique<TosaToLinalg>(options);
 }
 
 void mlir::tosa::addTosaToLinalgPasses(
@@ -114,7 +121,7 @@ void mlir::tosa::addTosaToLinalgPasses(
   }
   if (validationOptions)
     pm.addPass(tosa::createTosaValidation(*validationOptions));
-  pm.addNestedPass<func::FuncOp>(tosa::createTosaToLinalg());
+  pm.addNestedPass<func::FuncOp>(tosa::createTosaToLinalg(options));
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg.mlir b/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg.mlir
index a803ee7d99153..811e6a2ebf914 100644
--- a/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg.mlir
+++ b/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg.mlir
@@ -1,4 +1,5 @@
-// RUN: mlir-opt --split-input-file -pass-pipeline="builtin.module(func.func(tosa-to-linalg))" %s -verify-diagnostics -o -| FileCheck %s
+// RUN: mlir-opt --split-input-file -pass-pipeline="builtin.module(func.func(tosa-to-linalg))" %s -verify-diagnostics -o -| FileCheck %s --check-prefixes=CHECK,FINITE
+// RUN: mlir-opt --split-input-file -pass-pipeline="builtin.module(func.func(tosa-to-linalg{allow-non-finites=true}))" %s -verify-diagnostics -o -| FileCheck %s --check-prefixes=CHECK,NONFINITE
 
 // CHECK: #[[$MAP0:.*]] = affine_map<() -> ()>
 
@@ -1014,13 +1015,15 @@ func.func @reduce_float(%arg0: tensor<5x4xf32>) -> () {
   // CHECK: arith.mulf
   %2 = tosa.reduce_product %arg0 {axis = 0 : i32} : (tensor<5x4xf32>) -> tensor<1x4xf32>
 
-  // CHECK: arith.constant 3.40282347E+38 : f32
+  // FINITE: arith.constant 3.40282347E+38 : f32
+  // NONFINITE: arith.constant 0x7F800000 : f32
   // CHECK: linalg.fill
   // CHECK: linalg.reduce
   // CHECK: arith.minimumf
   %3 = tosa.reduce_min %arg0 {axis = 0 : i32} : (tensor<5x4xf32>) -> tensor<1x4xf32>
 
-  // CHECK: arith.constant -3.40282347E+38 : f32
+  // FINITE: arith.constant -3.40282347E+38 : f32
+  // NONFINITE: arith.constant 0xFF800000 : f32
   // CHECK: linalg.fill
   // CHECK: linalg.reduce
   // CHECK: arith.maximumf
@@ -1100,7 +1103,8 @@ func.func @reduce_float_dyn_multiple(%arg0: tensor<?x?xf32>) -> () {
   // CHECK: %[[C0:.+]] = arith.constant 0
   // CHECK: %[[DYN:.+]] = tensor.dim %[[ARG0]], %[[C0]]
   // CHECK: %[[INIT:.+]] = tensor.empty(%[[DYN]])
-  // CHECK: %[[CMIN:.+]] = arith.constant -3.40282347E+38
+  // FINITE: %[[CMIN:.+]] = arith.constant -3.40282347E+38
+  // NONFINITE: %[[CMIN:.+]] = arith.constant 0xFF800000
   // CHECK: %[[FILL:.+]] = linalg.fill ins(%[[CMIN]]{{.*}}outs(%[[INIT]]
   // CHECK: %[[REDUCE:.+]] = linalg.reduce ins(%[[ARG0]] : tensor<?x?xf32>) outs(%[[FILL]] : tensor<?xf32>) dimensions = [1]
   // CHECK:  (%[[ARG1:.*]]: f32, %[[ARG2:.*]]: f32) {
@@ -1814,7 +1818,8 @@ func.func @argmax(%arg0 : tensor<3x2xi32>, %arg1 : tensor<6xf32>) -> () {
   // CHECK:   linalg.yield [[SELECT_IDX]], [[SELECT_VAL]]
   %1 = tosa.argmax %arg0 { axis = 1 : i32} : (tensor<3x2xi32>)  -> tensor<3xi32>
 
-  // CHECK: arith.constant -3.40282347E+38 : f32
+  // FINITE: arith.constant -3.40282347E+38 : f32
+  // NONFINITE: arith.constant 0xFF800000 : f32
   // CHECK: linalg.index
   // CHECK: arith.index_cast
   // CHECK: arith.cmpf ugt
@@ -2679,3 +2684,165 @@ func.func @negate_i64_no_noop_cast(%arg0: tensor<4xi64>, %in_zp: tensor<1xi64>,
   %neg = tosa.negate %arg0, %in_zp, %out_zp : (tensor<4xi64>, tensor<1xi64>, tensor<1xi64>) -> tensor<4xi64>
   return %neg : tensor<4xi64>
 }
+
+// -----
+
+// The TOSA identity for a float REDUCE_MIN is maximum_s<in_out_t>(), which
+// Table 5 of the specification gives as +infinity for fp16_t/bf16_t/fp32_t.
+// CHECK-LABEL: @reduce_min_f16
+func.func @reduce_min_f16(%arg0: tensor<5x4xf16>) -> () {
+  // FINITE: arith.constant 6.550400e+04 : f16
+  // NONFINITE: arith.constant 0x7C00 : f16
+  // CHECK: linalg.fill
+  // CHECK: linalg.reduce
+  // CHECK: arith.minimumf
+  %0 = tosa.reduce_min %arg0 {axis = 0 : i32} : (tensor<5x4xf16>) -> tensor<1x4xf16>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: @reduce_max_f16
+func.func @reduce_max_f16(%arg0: tensor<5x4xf16>) -> () {
+  // FINITE: arith.constant -6.550400e+04 : f16
+  // NONFINITE: arith.constant 0xFC00 : f16
+  // CHECK: linalg.fill
+  // CHECK: linalg.reduce
+  // CHECK: arith.maximumf
+  %0 = tosa.reduce_max %arg0 {axis = 0 : i32} : (tensor<5x4xf16>) -> tensor<1x4xf16>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: @reduce_min_bf16
+func.func @reduce_min_bf16(%arg0: tensor<5x4xbf16>) -> () {
+  // FINITE: arith.constant 3.389530e+38 : bf16
+  // NONFINITE: arith.constant 0x7F80 : bf16
+  // CHECK: linalg.fill
+  // CHECK: linalg.reduce
+  // CHECK: arith.minimumf
+  %0 = tosa.reduce_min %arg0 {axis = 0 : i32} : (tensor<5x4xbf16>) -> tensor<1x4xbf16>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: @reduce_max_bf16
+func.func @reduce_max_bf16(%arg0: tensor<5x4xbf16>) -> () {
+  // FINITE: arith.constant -3.389530e+38 : bf16
+  // NONFINITE: arith.constant 0xFF80 : bf16
+  // CHECK: linalg.fill
+  // CHECK: linalg.reduce
+  // CHECK: arith.maximumf
+  %0 = tosa.reduce_max %arg0 {axis = 0 : i32} : (tensor<5x4xbf16>) -> tensor<1x4xbf16>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: @reduce_min_f64
+func.func @reduce_min_f64(%arg0: tensor<5x4xf64>) -> () {
+  // FINITE: arith.constant 1.7976931348623157E+308 : f64
+  // NONFINITE: arith.constant 0x7FF0000000000000 : f64
+  // CHECK: linalg.fill
+  // CHECK: linalg.reduce
+  // CHECK: arith.minimumf
+  %0 = tosa.reduce_min %arg0 {axis = 0 : i32} : (tensor<5x4xf64>) -> tensor<1x4xf64>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: @reduce_max_f64
+func.func @reduce_max_f64(%arg0: tensor<5x4xf64>) -> () {
+  // FINITE: arith.constant -1.7976931348623157E+308 : f64
+  // NONFINITE: arith.constant 0xFFF0000000000000 : f64
+  // CHECK: linalg.fill
+  // CHECK: linalg.reduce
+  // CHECK: arith.maximumf
+  %0 = tosa.reduce_max %arg0 {axis = 0 : i32} : (tensor<5x4xf64>) -> tensor<1x4xf64>
+  return
+}
+
+// -----
+
+// f8E4M3FN has NanOnly semantics: it has no encoding for an infinity, and
+// APFloat::getInf() would silently hand back a NaN that arith.minimumf then
+// propagates through the whole reduction. Both configurations must therefore
+// keep the finite identity, so this is deliberately checked with a shared
+// prefix rather than a FINITE/NONFINITE pair.
+// CHECK-LABEL: @reduce_min_f8E4M3FN_stays_finite
+func.func @reduce_min_f8E4M3FN_stays_finite(%arg0: tensor<5x4xf8E4M3FN>) -> () {
+  // CHECK: arith.constant 4.480000e+02 : f8E4M3FN
+  // CHECK-NOT: arith.constant 0x
+  // CHECK: linalg.fill
+  // CHECK: linalg.reduce
+  // CHECK: arith.minimumf
+  %0 = tosa.reduce_min %arg0 {axis = 0 : i32} : (tensor<5x4xf8E4M3FN>) -> tensor<1x4xf8E4M3FN>
+  return
+}
+
+// -----
+
+// CHECK-LABEL: @reduce_max_f8E4M3FN_stays_finite
+func.func @reduce_max_f8E4M3FN_stays_finite(%arg0: tensor<5x4xf8E4M3FN>) -> () {
+  // CHECK: arith.constant -4.480000e+02 : f8E4M3FN
+  // CHECK-NOT: arith.constant 0x
+  // CHECK: linalg.fill
+  // CHECK: linalg.reduce
+  // CHECK: arith.maximumf
+  %0 = tosa.reduce_max %arg0 {axis = 0 : i32} : (tensor<5x4xf8E4M3FN>) -> tensor<1x4xf8E4M3FN>
+  return
+}
+
+// -----
+
+// The seed feeds the nan_mode = IGNORE path too, where it must remain a min/max
+// identity rather than a NaN: the accumulator only reaches the final select
+// unchanged when every lane was NaN, and that select overwrites it with a NaN.
+// CHECK-LABEL: @reduce_min_nan_ignore_seed
+func.func @reduce_min_nan_ignore_seed(%arg0: tensor<5x4xf32>) -> () {
+  // FINITE: arith.constant 3.40282347E+38 : f32
+  // NONFINITE: arith.constant 0x7F800000 : f32
+  // CHECK: linalg.fill
+  // CHECK: linalg.reduce
+  // CHECK: arith.minimumf
+  // CHECK: arith.cmpf uno
+  // CHECK: arith.select
+  // CHECK: arith.andi
+  // CHECK: arith.constant 0x7FC00000 : f32
+  // CHECK: linalg.select
+  %0 = tosa.reduce_min %arg0 {axis = 0 : i32, nan_mode = IGNORE} : (tensor<5x4xf32>) -> tensor<1x4xf32>
+  return
+}
+
+// -----
+
+// CHECK...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/222140


More information about the Mlir-commits mailing list