[Mlir-commits] [mlir] [Affine] Replace recursive toAffineExpr with iterative traversal (PR #154976)

Samarth Narang llvmlistbot at llvm.org
Fri Aug 22 09:21:02 PDT 2025


https://github.com/snarang181 created https://github.com/llvm/llvm-project/pull/154976

Switch `toAffineExpr` from a recursive implementation to an explicit stack-based post-order traversal.

>From 166b338c0f1f3ffc0aa456aa656274bd9b82c943 Mon Sep 17 00:00:00 2001
From: Samarth Narang <snarang at umass.edu>
Date: Fri, 22 Aug 2025 12:19:49 -0400
Subject: [PATCH] [Affine] Replace recursive toAffineExpr with iterative
 traversal

Switch `toAffineExpr` from a recursive implementation to an explicit
stack-based post-order traversal.
---
 .../Affine/Transforms/RaiseMemrefDialect.cpp  | 142 ++++++++++++------
 1 file changed, 100 insertions(+), 42 deletions(-)

diff --git a/mlir/lib/Dialect/Affine/Transforms/RaiseMemrefDialect.cpp b/mlir/lib/Dialect/Affine/Transforms/RaiseMemrefDialect.cpp
index 3fc2664aefdfb..ea8fcad3d2ab2 100644
--- a/mlir/lib/Dialect/Affine/Transforms/RaiseMemrefDialect.cpp
+++ b/mlir/lib/Dialect/Affine/Transforms/RaiseMemrefDialect.cpp
@@ -65,59 +65,117 @@ findInListOrAdd(Value value, llvm::SmallVectorImpl<Value> &dims,
 
 /// Convert a value to an affine expr if possible. Adds dims and symbols
 /// if needed.
-static AffineExpr toAffineExpr(Value value,
+static AffineExpr toAffineExpr(Value root,
                                llvm::SmallVectorImpl<Value> &affineDims,
                                llvm::SmallVectorImpl<Value> &affineSymbols) {
   using namespace matchers;
-  IntegerAttr::ValueType cst;
-  if (matchPattern(value, m_ConstantInt(&cst))) {
-    return getAffineConstantExpr(cst.getSExtValue(), value.getContext());
-  }
 
-  Operation *definingOp = value.getDefiningOp();
-  if (llvm::isa_and_nonnull<arith::AddIOp>(definingOp) ||
-      llvm::isa_and_nonnull<arith::MulIOp>(definingOp)) {
-    // TODO: replace recursion with explicit stack.
-    // For the moment this can be tolerated as we only recurse on
-    // arith.addi and arith.muli, so there cannot be any infinite
-    // recursion. The depth of these expressions should be in most
-    // cases very manageable, as affine expressions should be as
-    // simple as `a + b * c`.
-    AffineExpr lhsE =
-        toAffineExpr(definingOp->getOperand(0), affineDims, affineSymbols);
-    AffineExpr rhsE =
-        toAffineExpr(definingOp->getOperand(1), affineDims, affineSymbols);
-
-    if (lhsE && rhsE) {
-      AffineExprKind kind;
-      if (isa<arith::AddIOp>(definingOp)) {
-        kind = mlir::AffineExprKind::Add;
-      } else {
-        kind = mlir::AffineExprKind::Mul;
+  // Table for already-built subexpressions.
+  llvm::DenseMap<Value, AffineExpr> built;
+
+  // Post-order traversal stack: (value, state)
+  // state = 0 -> first-time seen (push the children).
+  // state = 1 -> children have been processed (build the node).
+  llvm::SmallVector<std::pair<Value, unsigned>, 16> stack;
+  stack.emplace_back(root, 0); // push the root value onto the stack.
+
+  auto makeLeaf = [&](Value v) -> AffineExpr {
+    // Constant?
+    IntegerAttr::ValueType cst;
+    if (matchPattern(root, m_ConstantInt(&cst)))
+      return getAffineConstantExpr(cst.getSExtValue(), v.getContext());
+
+    // Symbol?
+    if (auto symIx = findInListOrAdd(
+            v, affineSymbols, [](Value x) { return isValidSymbol(x); })) {
+      return getAffineSymbolExpr(*symIx, v.getContext());
+    }
 
-        if (!lhsE.isSymbolicOrConstant() && !rhsE.isSymbolicOrConstant()) {
-          // This is not an affine expression, give up.
-          return {};
-        }
+    // Dimension?
+    if (auto dimIx = findInListOrAdd(v, affineDims,
+                                     [](Value x) { return isValidDim(x); })) {
+      return getAffineDimExpr(*dimIx, v.getContext());
+    }
+
+    // Not representable.
+    return AffineExpr();
+  };
+
+  while (!stack.empty()) {
+    auto [v, state] = stack.back();
+    stack.pop_back();
+
+    // If we already built the current value, nothing more to do.
+    if (built.count(v))
+      continue;
+
+    if (state == 0) {
+      Operation *def = v.getDefiningOp();
+
+      // If not an addi/muli, we'll handle it as a leaf in state == 1,
+      bool isAddOrMul = llvm::isa_and_nonnull<arith::AddIOp>(def) ||
+                        llvm::isa_and_nonnull<arith::MulIOp>(def);
+
+      if (!isAddOrMul) {
+        // Defer to leaf handling.
+        stack.emplace_back(v, 1);
+        continue;
       }
-      return getAffineBinaryOpExpr(kind, lhsE, rhsE);
+
+      // For addi/muli, push the ops.
+      stack.emplace_back(v, 1);
+      Value lhs = def->getOperand(0);
+      Value rhs = def->getOperand(1);
+
+      if (!built.count(lhs))
+        stack.emplace_back(lhs, 0);
+      if (!built.count(rhs))
+        stack.emplace_back(rhs, 0);
+      continue;
     }
-    return {};
-  }
+    // state == 1, time to build the node (either add/mul or a leaf).
+    Operation *def = v.getDefiningOp();
+    if (llvm::isa_and_nonnull<arith::AddIOp>(def) ||
+        llvm::isa_and_nonnull<arith::MulIOp>(def)) {
+      Value lhsV = def->getOperand(0);
+      Value rhsV = def->getOperand(1);
+
+      auto itL = built.find(lhsV);
+      auto itR = built.find(rhsV);
+      if (itL == built.end() || itR == built.end()) {
+        // Child failed to build.
+        return AffineExpr();
+      }
 
-  if (auto dimIx = findInListOrAdd(value, affineSymbols, [](Value v) {
-        return affine::isValidSymbol(v);
-      })) {
-    return getAffineSymbolExpr(*dimIx, value.getContext());
-  }
+      AffineExpr lhsE = itL->second;
+      AffineExpr rhsE = itR->second;
 
-  if (auto dimIx = findInListOrAdd(
-          value, affineDims, [](Value v) { return affine::isValidDim(v); })) {
+      if (!lhsE || !rhsE)
+        return AffineExpr();
 
-    return getAffineDimExpr(*dimIx, value.getContext());
-  }
+      AffineExprKind kind;
+      if (llvm::isa<arith::AddIOp>(def)) {
+        kind = AffineExprKind::Add;
+      } else {
+        kind = AffineExprKind::Mul;
+        // Enforce restriction: one side must by symbolic or constant.
+        if (!lhsE.isSymbolicOrConstant() && !rhsE.isSymbolicOrConstant())
+          return AffineExpr();
+      }
 
-  return {};
+      built[v] = getAffineBinaryOpExpr(kind, lhsE, rhsE);
+      continue;
+    }
+
+    // Not addi/muli: treat as leaf.
+    AffineExpr leaf = makeLeaf(v);
+    if (!leaf)
+      return AffineExpr();
+    built[v] = leaf;
+  }
+  auto it = built.find(root);
+  return it == built.end() ? AffineExpr()
+                           : it->second; // Return the root expression.
 }
 
 static LogicalResult



More information about the Mlir-commits mailing list