[Mlir-commits] [mlir] MLIR][Affine] Support bound computation for local variables in getBoundForAffineExpr (PR #212167)
lonely eagle
llvmlistbot at llvm.org
Sun Jul 26 20:07:16 PDT 2026
https://github.com/linuxlonelyeagle created https://github.com/llvm/llvm-project/pull/212167
This PR extends `getBoundForAffineExpr` to recursively compute upper and lower bounds for local variables (`flattener.localExprs`).
>From f4daf40a051826d8d1fc935938e572895c0f0f20 Mon Sep 17 00:00:00 2001
From: linuxlonelyeagle <2020382038 at qq.com>
Date: Sun, 26 Jul 2026 15:37:39 +0000
Subject: [PATCH] add compute local var logic in the getBoundForAffineExpr.
---
mlir/lib/IR/AffineExpr.cpp | 56 +++++++++++++++-------
mlir/test/Dialect/Affine/canonicalize.mlir | 18 +++++++
2 files changed, 57 insertions(+), 17 deletions(-)
diff --git a/mlir/lib/IR/AffineExpr.cpp b/mlir/lib/IR/AffineExpr.cpp
index da91066815ca4..fbc218f149de0 100644
--- a/mlir/lib/IR/AffineExpr.cpp
+++ b/mlir/lib/IR/AffineExpr.cpp
@@ -17,6 +17,7 @@
#include "mlir/IR/IntegerSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVectorExtras.h"
+#include "llvm/Support/LogicalResult.h"
#include "llvm/Support/MathExtras.h"
#include <numeric>
#include <optional>
@@ -1710,25 +1711,46 @@ std::optional<int64_t> mlir::getBoundForAffineExpr(
if (failed(simpleResult))
return std::nullopt;
ArrayRef<int64_t> flattenedExpr = flattener.operandExprStack.back();
- // TODO: Handle local variables. We can get hold of flattener.localExprs and
- // get bound on the local expr recursively.
- if (flattener.numLocals > 0)
- return std::nullopt;
int64_t bound = 0;
- // Substitute the constant lower or upper bound for the dimensional or
- // symbolic input depending on `isUpper` to determine the bound.
- for (unsigned i = 0, e = numDims + numSymbols; i < e; ++i) {
- if (flattenedExpr[i] > 0) {
- auto &constBound = isUpper ? constUpperBounds[i] : constLowerBounds[i];
- if (!constBound)
- return std::nullopt;
- bound += *constBound * flattenedExpr[i];
- } else if (flattenedExpr[i] < 0) {
- auto &constBound = isUpper ? constLowerBounds[i] : constUpperBounds[i];
- if (!constBound)
- return std::nullopt;
- bound += *constBound * flattenedExpr[i];
+
+ // Helper to retrieve the bound for a dimension, symbol, or local variable.
+ // Returns std::nullopt if the required bound is not available.
+ auto getBoundForIndex = [&](unsigned index,
+ bool wantUpper) -> std::optional<int64_t> {
+ unsigned numDimsAndSymbols = numDims + numSymbols;
+
+ // Dimensions and symbols: retrieve directly from the input bounds arrays.
+ if (index < numDimsAndSymbols) {
+ const std::optional<int64_t> &bound =
+ wantUpper ? constUpperBounds[index] : constLowerBounds[index];
+ return bound;
}
+
+ // Local variables, recursively compute the bound on the underlying local
+ // expression.
+ unsigned localIndex = index - numDimsAndSymbols;
+ return getBoundForAffineExpr(flattener.localExprs[localIndex], numDims,
+ numSymbols, constLowerBounds, constUpperBounds,
+ wantUpper);
+ };
+
+ // Accumulate bounds for dimensions, symbols, and local variables.
+ unsigned totalVars = numDims + numSymbols + flattener.numLocals;
+ for (unsigned i = 0, e = totalVars; i < e; ++i) {
+ int64_t flattenedValue = flattenedExpr[i];
+ if (flattenedValue == 0)
+ continue;
+
+ // Determine the bound direction needed for the term. If coefficient > 0, we
+ // need upper bound for max (isUpper=true) and lower bound for min
+ // (isUpper=false). If coefficient < 0, the direction flips.
+ bool wantUpper = (flattenedValue > 0) == isUpper;
+
+ std::optional<int64_t> constBound = getBoundForIndex(i, wantUpper);
+ if (!constBound)
+ return std::nullopt;
+
+ bound += *constBound * flattenedValue;
}
// Constant term.
bound += flattenedExpr.back();
diff --git a/mlir/test/Dialect/Affine/canonicalize.mlir b/mlir/test/Dialect/Affine/canonicalize.mlir
index 7d236ef3c2421..95f51a51f695d 100644
--- a/mlir/test/Dialect/Affine/canonicalize.mlir
+++ b/mlir/test/Dialect/Affine/canonicalize.mlir
@@ -2614,3 +2614,21 @@ func.func @split_delinearize_spanning_final_part_vector(
%1:4 = affine.delinearize_index %0 into (2, 3, 8, 4) : vector<4xindex>, vector<4xindex>, vector<4xindex>, vector<4xindex>
return %1#0, %1#1, %1#2, %1#3 : vector<4xindex>, vector<4xindex>, vector<4xindex>, vector<4xindex>
}
+
+// -----
+
+// CHECK-DAG: #[[$MAP:.*]] = affine_map<(d0, d1) -> (d0 + (d0 + d1) floordiv 4 + 2)>
+// CHECK-LABEL: func @simplify_loop_ub_with_local_var
+// CHECK: affine.for %[[VAL_0:.*]] = 0 to 4 {
+// CHECK: affine.for %[[VAL_1:.*]] = 0 to 4 {
+// CHECK: affine.for %[[VAL_2:.*]] = 0 to #[[$MAP]](%[[VAL_0]], %[[VAL_1]]) {
+func.func @simplify_loop_ub_with_local_var(%arg0: index) {
+ affine.for %arg1 = 0 to 4 {
+ affine.for %arg2 = 0 to 4 {
+ affine.for %arg3 = 0 to min affine_map<(d0, d1) -> (10, (d0 + d1) floordiv 4 + d0 + 2)>(%arg1, %arg2) {
+ "test.foo"(%arg3) : (index) -> ()
+ }
+ }
+ }
+ return
+}
More information about the Mlir-commits
mailing list