[Mlir-commits] [mlir] [mlir][affine] Allow affine.min/max results as dimension IDs (PR #233411)
lonely eagle
llvmlistbot at llvm.org
Mon Oct 19 04:58:16 PDT 2026
https://github.com/linuxlonelyeagle created https://github.com/llvm/llvm-project/pull/233411
An `affine.for` lower bound can express a maximum, and an upper bound can
express a minimum. When splitting a tiled loop into source-only and shared
iterations, the source-only loop may need a maximum *inside* its upper-bound
minimum:
```mlir
// Lower bound of the destination point loop within this tile.
%cut = affine.max affine_map<(d0) -> (3, d0 * 4)>(%tile)
affine.for %i = max affine_map<(d0) -> (1, d0 * 4)>(%tile)
to min affine_map<(d0, d1) -> (4, d0 * 4 + 4, d1)>(%tile, %cut) {
// Source-only iterations.
}
```
The verifier currently rejects `%cut` as a dimension ID in the loop bound,
even though its operand `%tile` is a valid dimension ID. This change allows
`affine.min` and `affine.max` results to be used as dimension IDs when all
their operands are valid dimensions. The change also updates the Affine
dialect documentation and adds positive and negative verifier tests.
>From b3e1e95426e70ff948907a7ab2cdaa964831216a Mon Sep 17 00:00:00 2001
From: linuxlonelyeagle <2020382038 at qq.com>
Date: Mon, 19 Oct 2026 11:46:26 +0000
Subject: [PATCH 1/2] An `affine.for` lower bound can express a maximum, and an
upper bound can express a minimum. When splitting a tiled loop into
source-only and shared iterations, the source-only loop may need a maximum
*inside* its upper-bound minimum:
```mlir
// Lower bound of the destination point loop within this tile.
%cut = affine.max affine_map<(d0) -> (3, d0 * 4)>(%tile)
affine.for %i = max affine_map<(d0) -> (1, d0 * 4)>(%tile)
to min affine_map<(d0, d1) -> (4, d0 * 4 + 4, d1)>(%tile, %cut) {
// Source-only iterations.
}
```
The verifier currently rejects `%cut` as a dimension ID in the loop bound,
even though its operand `%tile` is a valid dimension ID. This change allows
`affine.min` and `affine.max` results to be used as dimension IDs when all
their operands are valid dimensions. The change also updates the Affine
dialect documentation and adds positive and negative verifier tests.
---
mlir/docs/Dialects/Affine.md | 6 ++++--
mlir/lib/Dialect/Affine/IR/AffineOps.cpp | 7 ++++++-
mlir/test/Dialect/Affine/invalid.mlir | 26 ++++++++++++++++++++++++
mlir/test/Dialect/Affine/ops.mlir | 18 ++++++++++++++++
4 files changed, 54 insertions(+), 3 deletions(-)
diff --git a/mlir/docs/Dialects/Affine.md b/mlir/docs/Dialects/Affine.md
index 90aa43a85d877..2d889d16c1926 100644
--- a/mlir/docs/Dialects/Affine.md
+++ b/mlir/docs/Dialects/Affine.md
@@ -83,8 +83,10 @@ location of the SSA use. Dimensions may be bound not only to anything that a
symbol is bound to, but also to induction variables of enclosing
[`affine.for`](#affinefor-affineaffineforop) and
[`affine.parallel`](#affineparallel-affineaffineparallelop) operations, and the result
-of an [`affine.apply` operation](#affineapply-affineaffineapplyop) (which recursively
-may use other dimensions and symbols).
+of an [`affine.apply` operation](#affineapply-affineaffineapplyop), or an
+[`affine.min`](#affinemin-affineaffineminop) or
+[`affine.max`](#affinemax-affineaffinemaxop) operation, whose operands are
+valid dimensions (which recursively may use other dimensions and symbols).
### Affine Expressions
diff --git a/mlir/lib/Dialect/Affine/IR/AffineOps.cpp b/mlir/lib/Dialect/Affine/IR/AffineOps.cpp
index 5f5a49a68eb67..16c7e44711629 100644
--- a/mlir/lib/Dialect/Affine/IR/AffineOps.cpp
+++ b/mlir/lib/Dialect/Affine/IR/AffineOps.cpp
@@ -309,6 +309,8 @@ bool mlir::affine::isValidDim(Value value) {
// *) It is valid as a symbol.
// *) It is an induction variable.
// *) It is the result of an affine apply operation with dimension id operands.
+// *) It is the result of an affine min or max operation with dimension id
+// operands.
// *) It is the result of a more specialized index transformation (ex.
// delinearize_index or linearize_index) with dimension id operands.
bool mlir::affine::isValidDim(Value value, Region *region) {
@@ -330,6 +332,10 @@ bool mlir::affine::isValidDim(Value value, Region *region) {
// Affine apply operation is ok if all of its operands are ok.
if (auto applyOp = dyn_cast<AffineApplyOp>(op))
return applyOp.isValidDim(region);
+ // Affine min and max results are valid dimensions when their operands are.
+ if (isa<AffineMinOp, AffineMaxOp>(op))
+ return llvm::all_of(op->getOperands(),
+ [&](Value arg) { return ::isValidDim(arg, region); });
// delinearize_index and linearize_index are special forms of apply
// and so are valid dimensions if all their arguments are valid dimensions.
if (isa<AffineDelinearizeIndexOp, AffineLinearizeIndexOp>(op))
@@ -5090,7 +5096,6 @@ SmallVector<OpFoldResult> AffineDelinearizeIndexOp::getPaddedBasis() {
}
namespace {
-
// Drops delinearization indices that correspond to unit-extent basis
struct DropUnitExtentBasis
: public OpRewritePattern<affine::AffineDelinearizeIndexOp> {
diff --git a/mlir/test/Dialect/Affine/invalid.mlir b/mlir/test/Dialect/Affine/invalid.mlir
index 87a40e7382b2e..17fa1f0fdfd91 100644
--- a/mlir/test/Dialect/Affine/invalid.mlir
+++ b/mlir/test/Dialect/Affine/invalid.mlir
@@ -61,6 +61,32 @@ func.func @affine_for_upper_bound_invalid_sym() {
// -----
+func.func @affine_max_of_invalid_dim(%n: index) {
+ affine.for %tile = 0 to 4 {
+ %one = arith.constant 1 : index
+ %non_affine = arith.addi %tile, %one : index
+ %cut = affine.max affine_map<(d0) -> (3, d0)>(%non_affine)
+ // expected-error at +1 {{operand cannot be used as a dimension id}}
+ affine.for %point = affine_map<(d0) -> (d0)>(%cut) to %n {
+ }
+ }
+ return
+}
+
+// -----
+
+func.func @affine_max_of_dim_is_not_symbol(%n: index) {
+ affine.for %tile = 0 to 4 {
+ %cut = affine.max affine_map<(d0) -> (3, d0 * 4)>(%tile)
+ // expected-error at +1 {{operand cannot be used as a symbol}}
+ affine.for %point = affine_map<()[s0] -> (s0)>()[%cut] to %n {
+ }
+ }
+ return
+}
+
+// -----
+
#set0 = affine_set<(i)[N] : (i >= 0, N - i >= 0)>
func.func @affine_if_invalid_sym() {
diff --git a/mlir/test/Dialect/Affine/ops.mlir b/mlir/test/Dialect/Affine/ops.mlir
index 54e35b9ad06b4..8d418ae024628 100644
--- a/mlir/test/Dialect/Affine/ops.mlir
+++ b/mlir/test/Dialect/Affine/ops.mlir
@@ -100,6 +100,24 @@ func.func @affine_max(%arg0 : index, %arg1 : index, %arg2 : index) {
// -----
+// CHECK-LABEL: func.func @affine_min_max_dim_bounds
+func.func @affine_min_max_dim_bounds(%n: index) {
+ affine.for %tile = 0 to 4 {
+ %lower = affine.max affine_map<(d0) -> (3, d0 * 4)>(%tile)
+ %upper = affine.min affine_map<(d0)[s0] -> (d0 * 4 + 4, s0)>(%tile)[%n]
+ affine.for %point = affine_map<(d0) -> (d0)>(%lower)
+ to affine_map<(d0) -> (d0)>(%upper) {
+ }
+ }
+ return
+}
+
+// CHECK: affine.max
+// CHECK: affine.min
+// CHECK: affine.for %{{.*}} = #{{.*}}(%{{.*}}) to #{{.*}}(%{{.*}})
+
+// -----
+
func.func @valid_symbols(%arg0: index, %arg1: index, %arg2: index) {
%c1 = arith.constant 1 : index
%c0 = arith.constant 0 : index
>From 0ef33b75b84304bfb949be344f4a24f26291e48b Mon Sep 17 00:00:00 2001
From: linuxlonelyeagle <2020382038 at qq.com>
Date: Mon, 19 Oct 2026 11:56:47 +0000
Subject: [PATCH 2/2] fix test.
---
mlir/test/Dialect/Affine/ops.mlir | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/mlir/test/Dialect/Affine/ops.mlir b/mlir/test/Dialect/Affine/ops.mlir
index 8d418ae024628..7ea7510d8ae5e 100644
--- a/mlir/test/Dialect/Affine/ops.mlir
+++ b/mlir/test/Dialect/Affine/ops.mlir
@@ -100,11 +100,14 @@ func.func @affine_max(%arg0 : index, %arg1 : index, %arg2 : index) {
// -----
-// CHECK-LABEL: func.func @affine_min_max_dim_bounds
+// CHECK-LABEL: @affine_min_max_dim_bounds
func.func @affine_min_max_dim_bounds(%n: index) {
affine.for %tile = 0 to 4 {
+ // CHECK: %[[LOWER:.*]] = affine.max
%lower = affine.max affine_map<(d0) -> (3, d0 * 4)>(%tile)
+ // CHECK: %[[UPPER:.*]] = affine.min
%upper = affine.min affine_map<(d0)[s0] -> (d0 * 4 + 4, s0)>(%tile)[%n]
+ // CHECK: affine.for %{{.*}} = #{{.*}}(%[[LOWER]]) to #{{.*}}(%[[UPPER]])
affine.for %point = affine_map<(d0) -> (d0)>(%lower)
to affine_map<(d0) -> (d0)>(%upper) {
}
@@ -112,10 +115,6 @@ func.func @affine_min_max_dim_bounds(%n: index) {
return
}
-// CHECK: affine.max
-// CHECK: affine.min
-// CHECK: affine.for %{{.*}} = #{{.*}}(%{{.*}}) to #{{.*}}(%{{.*}})
-
// -----
func.func @valid_symbols(%arg0: index, %arg1: index, %arg2: index) {
More information about the Mlir-commits
mailing list