[Mlir-commits] [mlir] Adding to execute_region_op some missing support (PR #164159)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Mon Oct 20 00:33:59 PDT 2025
https://github.com/ddubov100 updated https://github.com/llvm/llvm-project/pull/164159
>From e793c80a8083955191006105138b2c7cae8313dd Mon Sep 17 00:00:00 2001
From: dubov diana <ddubov at mobileye.com>
Date: Sun, 19 Oct 2025 16:20:49 +0300
Subject: [PATCH 1/3] Adding to execute_region_op some missing support
---
mlir/include/mlir/Dialect/SCF/IR/SCFOps.td | 4 +-
mlir/lib/Dialect/SCF/IR/SCF.cpp | 87 +++++++++++++++++++++-
mlir/test/Dialect/SCF/canonicalize.mlir | 22 ++++++
3 files changed, 111 insertions(+), 2 deletions(-)
diff --git a/mlir/include/mlir/Dialect/SCF/IR/SCFOps.td b/mlir/include/mlir/Dialect/SCF/IR/SCFOps.td
index fadd3fc10bfc4..70b94695f4a09 100644
--- a/mlir/include/mlir/Dialect/SCF/IR/SCFOps.td
+++ b/mlir/include/mlir/Dialect/SCF/IR/SCFOps.td
@@ -77,7 +77,9 @@ def ConditionOp : SCF_Op<"condition", [
//===----------------------------------------------------------------------===//
def ExecuteRegionOp : SCF_Op<"execute_region", [
- DeclareOpInterfaceMethods<RegionBranchOpInterface>]> {
+ DeclareOpInterfaceMethods<RegionBranchOpInterface>,
+ RecursiveMemoryEffects,
+ DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {//, SingleBlockImplicitTerminator<"scf::YieldOp">]> { //, RecursiveMemoryEffects]> {
let summary = "operation that executes its region exactly once";
let description = [{
The `scf.execute_region` operation is used to allow multiple blocks within SCF
diff --git a/mlir/lib/Dialect/SCF/IR/SCF.cpp b/mlir/lib/Dialect/SCF/IR/SCF.cpp
index a9da6c2c8320a..753fe96cd41e1 100644
--- a/mlir/lib/Dialect/SCF/IR/SCF.cpp
+++ b/mlir/lib/Dialect/SCF/IR/SCF.cpp
@@ -291,9 +291,94 @@ struct MultiBlockExecuteInliner : public OpRewritePattern<ExecuteRegionOp> {
}
};
+// Pattern to eliminate ExecuteRegionOp results when it only forwards external
+// values. It operates only on execute regions with single terminator yield
+// operation.
+struct ExecuteRegionForwardingEliminator
+ : public OpRewritePattern<ExecuteRegionOp> {
+ using OpRewritePattern<ExecuteRegionOp>::OpRewritePattern;
+
+ LogicalResult matchAndRewrite(ExecuteRegionOp op,
+ PatternRewriter &rewriter) const override {
+ if (op.getNumResults() == 0)
+ return failure();
+
+ SmallVector<Operation *> yieldOps;
+ for (Block &block : op.getRegion()) {
+ if (auto yield = dyn_cast<scf::YieldOp>(block.getTerminator())) {
+ if (yield.getResults().empty())
+ continue;
+ yieldOps.push_back(yield.getOperation());
+ }
+ }
+
+ if (yieldOps.size() != 1)
+ return failure();
+
+ auto yieldOp = dyn_cast<scf::YieldOp>(yieldOps.front());
+ auto yieldedValues = yieldOp.getOperands();
+ // Check if all yielded values are from outside the region
+ bool allExternal = true;
+ for (Value yieldedValue : yieldedValues) {
+ if (isValueFromInsideRegion(yieldedValue, op)) {
+ allExternal = false;
+ break;
+ }
+ }
+
+ if (!allExternal)
+ return failure();
+
+ // All yielded values are external - create a new execute_region with no
+ // results.
+ auto newOp = rewriter.create<ExecuteRegionOp>(op.getLoc(), TypeRange{});
+ newOp->setAttrs(op->getAttrs());
+
+ // Move the region content to the new operation
+ newOp.getRegion().takeBody(op.getRegion());
+
+ // Replace the yield operation with a new yield operation with no results.
+ rewriter.setInsertionPoint(yieldOp);
+ rewriter.eraseOp(yieldOp);
+ rewriter.create<scf::YieldOp>(yieldOp.getLoc());
+
+ // Replace the old operation with the external values directly.
+ rewriter.replaceOp(op, yieldedValues);
+ return success();
+ }
+
+private:
+ bool isValueFromInsideRegion(Value value,
+ ExecuteRegionOp executeRegionOp) const {
+ // Check if the value is defined within the execute_region
+ if (Operation *defOp = value.getDefiningOp()) {
+ return executeRegionOp.getRegion().isAncestor(defOp->getParentRegion());
+ }
+
+ // If it's a block argument, check if it's from within the region
+ if (BlockArgument blockArg = dyn_cast<BlockArgument>(value)) {
+ return executeRegionOp.getRegion().isAncestor(blockArg.getParentRegion());
+ }
+
+ return false; // Value is from outside the region
+ }
+};
+
void ExecuteRegionOp::getCanonicalizationPatterns(RewritePatternSet &results,
MLIRContext *context) {
- results.add<SingleBlockExecuteInliner, MultiBlockExecuteInliner>(context);
+ results.add<SingleBlockExecuteInliner, MultiBlockExecuteInliner,
+ ExecuteRegionForwardingEliminator>(context);
+}
+
+void ExecuteRegionOp::getEffects(
+ SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
+ &effects) {
+ if (!getNoInline())
+ return;
+ // In case there is attribute no_inline we want the region not to be inlined
+ // into the parent operation.
+ effects.emplace_back(MemoryEffects::Write::get(),
+ SideEffects::DefaultResource::get());
}
void ExecuteRegionOp::getSuccessorRegions(
diff --git a/mlir/test/Dialect/SCF/canonicalize.mlir b/mlir/test/Dialect/SCF/canonicalize.mlir
index 2bec63672e783..4529cdb4a298c 100644
--- a/mlir/test/Dialect/SCF/canonicalize.mlir
+++ b/mlir/test/Dialect/SCF/canonicalize.mlir
@@ -1604,6 +1604,28 @@ func.func @func_execute_region_inline_multi_yield() {
// -----
+// CHECK-LABEL: func.func private @canonicalize_execute_region_yeilding_external_value(
+// CHECK-SAME: %[[VAL_0:.*]]: tensor<1x120xui8>) -> tensor<1x60xui8> {
+// CHECK: %[[VAL_1:.*]] = memref.alloc() {alignment = 64 : i64} : memref<1x60xui8>
+// CHECK: scf.execute_region no_inline {
+// CHECK: scf.yield
+// CHECK: }
+// CHECK: %[[VAL_2:.*]] = bufferization.to_tensor %[[VAL_1]] : memref<1x60xui8> to tensor<1x60xui8>
+// CHECK: return %[[VAL_2]] : tensor<1x60xui8>
+// CHECK: }
+
+func.func private @canonicalize_execute_region_yeilding_external_value(%arg0: tensor<1x120xui8>) -> tensor<1x60xui8> {
+ %alloc = memref.alloc() {alignment = 64 : i64} : memref<1x60xui8>
+ %0 = bufferization.to_tensor %alloc : memref<1x60xui8> to tensor<1x60xui8>
+ %1 = scf.execute_region -> memref<1x60xui8> no_inline {
+ scf.yield %alloc : memref<1x60xui8>
+ }
+ %2 = bufferization.to_tensor %1 : memref<1x60xui8> to tensor<1x60xui8>
+ return %2 : tensor<1x60xui8>
+}
+
+// -----
+
// CHECK-LABEL: func @canonicalize_parallel_insert_slice_indices(
// CHECK-SAME: %[[arg0:.*]]: tensor<1x5xf32>, %[[arg1:.*]]: tensor<?x?xf32>
func.func @canonicalize_parallel_insert_slice_indices(
>From 33061e4763d7921db20fbaf533409f160cbd7539 Mon Sep 17 00:00:00 2001
From: ddubov100 <155631080+ddubov100 at users.noreply.github.com>
Date: Mon, 20 Oct 2025 10:33:34 +0300
Subject: [PATCH 2/3] Update mlir/lib/Dialect/SCF/IR/SCF.cpp
Co-authored-by: Mehdi Amini <joker.eph at gmail.com>
---
mlir/lib/Dialect/SCF/IR/SCF.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mlir/lib/Dialect/SCF/IR/SCF.cpp b/mlir/lib/Dialect/SCF/IR/SCF.cpp
index 753fe96cd41e1..e74679c77780a 100644
--- a/mlir/lib/Dialect/SCF/IR/SCF.cpp
+++ b/mlir/lib/Dialect/SCF/IR/SCF.cpp
@@ -315,7 +315,7 @@ struct ExecuteRegionForwardingEliminator
if (yieldOps.size() != 1)
return failure();
- auto yieldOp = dyn_cast<scf::YieldOp>(yieldOps.front());
+ auto yieldOp = cast<scf::YieldOp>(yieldOps.front());
auto yieldedValues = yieldOp.getOperands();
// Check if all yielded values are from outside the region
bool allExternal = true;
>From b6cb2c7fa45ac29b3bc8f771ee97467b1f3ceb60 Mon Sep 17 00:00:00 2001
From: ddubov100 <155631080+ddubov100 at users.noreply.github.com>
Date: Mon, 20 Oct 2025 10:33:50 +0300
Subject: [PATCH 3/3] Update mlir/lib/Dialect/SCF/IR/SCF.cpp
Co-authored-by: Mehdi Amini <joker.eph at gmail.com>
---
mlir/lib/Dialect/SCF/IR/SCF.cpp | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/mlir/lib/Dialect/SCF/IR/SCF.cpp b/mlir/lib/Dialect/SCF/IR/SCF.cpp
index e74679c77780a..f751d3105cf71 100644
--- a/mlir/lib/Dialect/SCF/IR/SCF.cpp
+++ b/mlir/lib/Dialect/SCF/IR/SCF.cpp
@@ -351,9 +351,8 @@ struct ExecuteRegionForwardingEliminator
bool isValueFromInsideRegion(Value value,
ExecuteRegionOp executeRegionOp) const {
// Check if the value is defined within the execute_region
- if (Operation *defOp = value.getDefiningOp()) {
- return executeRegionOp.getRegion().isAncestor(defOp->getParentRegion());
- }
+ if (Operation *defOp = value.getDefiningOp())
+ return executeRegionOp.getRegion() = defOp->getParentRegion();
// If it's a block argument, check if it's from within the region
if (BlockArgument blockArg = dyn_cast<BlockArgument>(value)) {
More information about the Mlir-commits
mailing list