[Mlir-commits] [mlir] [mlir][affine] add remove-single-iteration-loop pass. (PR #129270)
lonely eagle
llvmlistbot at llvm.org
Wed May 20 07:22:25 PDT 2026
https://github.com/linuxlonelyeagle updated https://github.com/llvm/llvm-project/pull/129270
>From 9b99abb5a8bbabb2aa23a8373b036a4f664b0093 Mon Sep 17 00:00:00 2001
From: linuxlonelyeagle <2020382038 at qq.com>
Date: Wed, 20 May 2026 13:23:38 +0000
Subject: [PATCH 1/2] imporve affine promoteIfSingleIteration.
---
mlir/lib/Dialect/Affine/Utils/LoopUtils.cpp | 66 ++++++++++++++++++++-
1 file changed, 65 insertions(+), 1 deletion(-)
diff --git a/mlir/lib/Dialect/Affine/Utils/LoopUtils.cpp b/mlir/lib/Dialect/Affine/Utils/LoopUtils.cpp
index 8f1249e3afaf0..30f1f710b901a 100644
--- a/mlir/lib/Dialect/Affine/Utils/LoopUtils.cpp
+++ b/mlir/lib/Dialect/Affine/Utils/LoopUtils.cpp
@@ -22,10 +22,12 @@
#include "mlir/IR/IRMapping.h"
#include "mlir/IR/IntegerSet.h"
#include "mlir/IR/OperationSupport.h"
+#include "mlir/Interfaces/ValueBoundsOpInterface.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/DebugLog.h"
+#include "llvm/Support/LogicalResult.h"
#include "llvm/Support/raw_ostream.h"
#include <optional>
@@ -114,12 +116,74 @@ static void replaceIterArgsAndYieldResults(AffineForOp forOp) {
std::get<0>(e).replaceAllUsesWith(std::get<1>(e));
}
+/// Return true if we can prove that the we always run at least the first
+/// iteration of the ForOp.
+static bool alwaysRunsFirstIteration(AffineForOp op) {
+ // Can't perform the analysis if the loops's bounds aren't index-typed.
+ if (!op.getInductionVar().getType().isIndex())
+ return false;
+ if (op.getLowerBoundMap().getNumResults() != 1 ||
+ op.getUpperBoundMap().getNumResults() != 1)
+ return false;
+
+ SmallVector<Value> lowerMapOperands = op.getLowerBoundOperands();
+ SmallVector<Value> upperMapOperands = op.getUpperBoundOperands();
+ ValueBoundsConstraintSet::Variable lower(op.getLowerBoundMap(),
+ lowerMapOperands);
+ ValueBoundsConstraintSet::Variable upper(op.getUpperBoundMap(),
+ upperMapOperands);
+ FailureOr<bool> isLb = ValueBoundsConstraintSet::compare(
+ lower, ValueBoundsConstraintSet::LT, upper);
+ return isLb.value_or(false);
+}
+
+/// Return true if we can prove that the we never run more than one iteration of
+/// the ForOp.
+static bool neverRunsSecondIteration(AffineForOp op) {
+ // Can't perform the analysis if the loops's bounds aren't index-typed.
+ if (!op.getInductionVar().getType().isIndex())
+ return false;
+
+ if (op.getLowerBoundMap().getNumResults() != 1 ||
+ op.getUpperBoundMap().getNumResults() != 1)
+ return false;
+
+ // The loop will only loop once if the inducation variable for the next time
+ // in the loop is greater than or equal to upper.
+ MLIRContext *context = op.getContext();
+ SmallVector<Value> lowerMapOperands = op.getLowerBoundOperands();
+ SmallVector<Value> upperMapOperands = op.getUpperBoundOperands();
+ SmallVector<AffineExpr> results;
+ AffineMap lowerMap = op.getLowerBoundMap();
+ for (AffineExpr expr : lowerMap.getResults())
+ results.push_back(expr + op.getStep().getSExtValue());
+
+ AffineMap nextItMap = AffineMap::get(
+ lowerMap.getNumDims(), lowerMap.getNumSymbols(), results, context);
+ ValueBoundsConstraintSet::Variable nextItVar(nextItMap, lowerMapOperands);
+ ValueBoundsConstraintSet::Variable upperVar(op.getUpperBoundMap(),
+ upperMapOperands);
+ FailureOr<bool> isUpperUnderNextIter = ValueBoundsConstraintSet::compare(
+ nextItVar, ValueBoundsConstraintSet::GE, upperVar);
+ return isUpperUnderNextIter.value_or(false);
+}
+
/// Promotes the loop body of a forOp to its containing block if the forOp
/// was known to have a single iteration.
LogicalResult mlir::affine::promoteIfSingleIteration(AffineForOp forOp) {
std::optional<uint64_t> tripCount = getConstantTripCount(forOp);
- if (!tripCount || *tripCount != 1)
+
+ // Only allow loops that are guaranteed to execute exactly once. If the trip
+ // count is constant, it must be exactly. If the trip count is dynamic, verify
+ // via affine analysis that it always runs the first iteration but never
+ // reaches the second.
+ if (tripCount && *tripCount != 1) {
return failure();
+ }
+ if (!tripCount &&
+ !(alwaysRunsFirstIteration(forOp) && neverRunsSecondIteration(forOp))) {
+ return failure();
+ }
// TODO: extend this for arbitrary affine bounds.
if (forOp.getLowerBoundMap().getNumResults() != 1)
>From 9f29a65196ed4fe5731d0632549ebb84439cfedd Mon Sep 17 00:00:00 2001
From: linuxlonelyeagle <2020382038 at qq.com>
Date: Wed, 20 May 2026 13:41:42 +0000
Subject: [PATCH 2/2] add test.
---
.../Dialect/Affine/affine-loop-normalize.mlir | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/mlir/test/Dialect/Affine/affine-loop-normalize.mlir b/mlir/test/Dialect/Affine/affine-loop-normalize.mlir
index 7d90efec0c21b..8dcd206237f3c 100644
--- a/mlir/test/Dialect/Affine/affine-loop-normalize.mlir
+++ b/mlir/test/Dialect/Affine/affine-loop-normalize.mlir
@@ -323,3 +323,21 @@ func.func @multi_level_tiled_matmul() {
}
return
}
+
+// -----
+
+// PROMOTE-SINGLE-ITER-LABEL: func @bound_value_promote_single_iter
+
+func.func @bound_value_promote_single_iter() -> index {
+ %c0 = arith.constant 0 :index
+ %bound = test.value_with_bounds { min = 0 : index, max = 1 : index}
+ %res = affine.for %iv = %bound to 2 step 2 iter_args(%arg = %c0) -> index {
+ %sum = arith.addi %arg, %c0 : index
+ affine.yield %sum : index
+ }
+ return %res : index
+}
+// PROMOTE-SINGLE-ITER-NEXT: %[[C0:.*]] = arith.constant 0 : index
+// PROMOTE-SINGLE-ITER-NEXT: %[[VALUE_WITH_BOUNDS_0:.*]] = test.value_with_bounds
+// PROMOTE-SINGLE-ITER-NEXT: %[[ADD:.*]] = arith.addi %[[C0]], %[[C0]] : index
+// PROMOTE-SINGLE-ITER-NEXT: return %[[ADD]] : index
More information about the Mlir-commits
mailing list