[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 ®istry) 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