[Mlir-commits] [mlir] [mlir][OpenACC] Unwrap multi-block regions in ACCRegionUnwrapConversion (PR #236289)
Akshay Khadse
llvmlistbot at llvm.org
Fri Oct 30 05:13:27 PDT 2026
https://github.com/akshaykhadse updated https://github.com/llvm/llvm-project/pull/236289
>From 7435c9eb0f3b7f8c38160e3d7e051b66f752ce8a Mon Sep 17 00:00:00 2001
From: akshaykhadse <akshayskhadse at gmail.com>
Date: Fri, 30 Oct 2026 06:29:38 -0400
Subject: [PATCH] [mlir][OpenACC] Unwrap multi-block regions in
ACCRegionUnwrapConversion
ACCRegionUnwrapConversion inlines the region of a structured construct
like acc.host_data or acc.data into the parent block when specializing
for host or device. It asserted that the region has exactly one block,
but these ops accept any region, so a multi-block body crashed the
acc-specialize-for-host and acc-specialize-for-device passes.
Wrap a multi-block region in scf.execute_region with the existing
wrapMultiBlockRegionWithSCFExecuteRegion utility instead, and erase the
op when the region is empty. acc-specialize-for-device now declares the
SCF dialect as a dependent dialect, like acc-specialize-for-host already
does.
Fixes #236287
---
.../Transforms/ACCSpecializePatterns.h | 24 ++++++--
.../mlir/Dialect/OpenACC/Transforms/Passes.td | 2 +
.../Transforms/ACCSpecializeForDevice.cpp | 1 +
.../OpenACC/acc-specialize-for-device.mlir | 32 ++++++++++
.../acc-specialize-for-host-fallback.mlir | 59 +++++++++++++++++++
5 files changed, 113 insertions(+), 5 deletions(-)
diff --git a/mlir/include/mlir/Dialect/OpenACC/Transforms/ACCSpecializePatterns.h b/mlir/include/mlir/Dialect/OpenACC/Transforms/ACCSpecializePatterns.h
index cde85fe839bf55..528e2194cc02b9 100644
--- a/mlir/include/mlir/Dialect/OpenACC/Transforms/ACCSpecializePatterns.h
+++ b/mlir/include/mlir/Dialect/OpenACC/Transforms/ACCSpecializePatterns.h
@@ -30,6 +30,7 @@
#define MLIR_DIALECT_OPENACC_TRANSFORMS_ACCSPECIALIZEPATTERNS_H
#include "mlir/Dialect/OpenACC/OpenACC.h"
+#include "mlir/Dialect/OpenACC/OpenACCUtilsLoop.h"
#include "mlir/IR/PatternMatch.h"
namespace mlir {
@@ -85,9 +86,10 @@ class ACCOpEraseConversion : public OpRewritePattern<OpTy> {
/// Pattern to unwrap a region from an ACC op and erase the wrapper.
/// Moves the region's contents to the parent block and removes the wrapper op.
-/// Used for structured data constructs (acc.data, acc.host_data,
-/// acc.kernel_environment, acc.declare) and compute constructs (acc.parallel,
-/// acc.serial, acc.kernels).
+/// A multi-block region is wrapped in scf.execute_region instead, since its
+/// blocks cannot be moved into the parent block. Used for structured data
+/// constructs (acc.data, acc.host_data, acc.kernel_environment, acc.declare)
+/// and compute constructs (acc.parallel, acc.serial, acc.kernels).
template <typename OpTy>
class ACCRegionUnwrapConversion : public OpRewritePattern<OpTy> {
using OpRewritePattern<OpTy>::OpRewritePattern;
@@ -95,8 +97,20 @@ class ACCRegionUnwrapConversion : public OpRewritePattern<OpTy> {
public:
LogicalResult matchAndRewrite(OpTy op,
PatternRewriter &rewriter) const override {
- assert(op.getRegion().hasOneBlock() && "expected one block");
- Block *block = &op.getRegion().front();
+ Region ®ion = op.getRegion();
+ if (region.empty()) {
+ rewriter.eraseOp(op);
+ return success();
+ }
+ if (!region.hasOneBlock()) {
+ rewriter.setInsertionPoint(op);
+ IRMapping mapping;
+ wrapMultiBlockRegionWithSCFExecuteRegion(region, mapping, op.getLoc(),
+ rewriter);
+ rewriter.eraseOp(op);
+ return success();
+ }
+ Block *block = ®ion.front();
// Erase the terminator (acc.yield or acc.terminator) before unwrapping
rewriter.eraseOp(block->getTerminator());
rewriter.inlineBlockBefore(block, op);
diff --git a/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.td b/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.td
index 202028994003dd..1b62b27bb41c73 100644
--- a/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/OpenACC/Transforms/Passes.td
@@ -324,6 +324,8 @@ def ACCSpecializeForDevice : Pass<"acc-specialize-for-device", "mlir::func::Func
(folded if known): acc.on_device
}];
let options = [ AccTheDeviceTypesOption ];
+ // Multi-block regions are unwrapped into scf.execute_region.
+ let dependentDialects = ["mlir::scf::SCFDialect"];
}
def ACCSpecializeForHost : Pass<"acc-specialize-for-host", "mlir::func::FuncOp"> {
diff --git a/mlir/lib/Dialect/OpenACC/Transforms/ACCSpecializeForDevice.cpp b/mlir/lib/Dialect/OpenACC/Transforms/ACCSpecializeForDevice.cpp
index 526415189607e4..bc8eb685e67a0e 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/ACCSpecializeForDevice.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/ACCSpecializeForDevice.cpp
@@ -65,6 +65,7 @@
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/OpenACC/OpenACC.h"
#include "mlir/Dialect/OpenACC/Transforms/ACCSpecializePatterns.h"
+#include "mlir/Dialect/SCF/IR/SCF.h"
#include "mlir/IR/MLIRContext.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
diff --git a/mlir/test/Dialect/OpenACC/acc-specialize-for-device.mlir b/mlir/test/Dialect/OpenACC/acc-specialize-for-device.mlir
index 9ec271485e5ad6..30812845ca595b 100644
--- a/mlir/test/Dialect/OpenACC/acc-specialize-for-device.mlir
+++ b/mlir/test/Dialect/OpenACC/acc-specialize-for-device.mlir
@@ -176,6 +176,38 @@ func.func @data_construct(%arg0 : memref<i32>) attributes {acc.specialized_routi
return
}
+// A multi-block region cannot be inlined into the parent block, so it is
+// wrapped in scf.execute_region.
+func.func private @then_fn()
+func.func private @else_fn()
+
+acc.routine @acc_routine_data_multi_block func(@data_multi_block) worker
+// CHECK-LABEL: func.func @data_multi_block(
+// CHECK-NOT: acc.data
+// CHECK: scf.execute_region {
+// CHECK-NEXT: cf.cond_br %{{.*}}, ^[[THEN:bb[0-9]+]], ^[[ELSE:bb[0-9]+]]
+// CHECK-NEXT: ^[[THEN]]:
+// CHECK-NEXT: call @then_fn()
+// CHECK-NEXT: scf.yield
+// CHECK-NEXT: ^[[ELSE]]:
+// CHECK-NEXT: call @else_fn()
+// CHECK-NEXT: scf.yield
+// CHECK-NEXT: }
+// CHECK-NEXT: return
+func.func @data_multi_block(%arg0 : memref<i32>, %cond : i1) attributes {acc.specialized_routine = #acc.specialized_routine<@acc_routine_data_multi_block, <worker>, "data_multi_block">} {
+ %d = acc.create varPtr(%arg0 : memref<i32>) -> memref<i32>
+ acc.data dataOperands(%d : memref<i32>) {
+ cf.cond_br %cond, ^bb1, ^bb2
+ ^bb1:
+ func.call @then_fn() : () -> ()
+ acc.terminator
+ ^bb2:
+ func.call @else_fn() : () -> ()
+ acc.terminator
+ }
+ return
+}
+
acc.routine @acc_routine_parallel func(@parallel_construct) worker
// CHECK-LABEL: func.func @parallel_construct
// CHECK-NOT: acc.parallel
diff --git a/mlir/test/Dialect/OpenACC/acc-specialize-for-host-fallback.mlir b/mlir/test/Dialect/OpenACC/acc-specialize-for-host-fallback.mlir
index b0f24cc9368231..cb01e3cb81bcab 100644
--- a/mlir/test/Dialect/OpenACC/acc-specialize-for-host-fallback.mlir
+++ b/mlir/test/Dialect/OpenACC/acc-specialize-for-host-fallback.mlir
@@ -141,6 +141,65 @@ func.func @kernels_construct(%arg0 : memref<i32>) attributes {acc.routine_info =
return
}
+// A multi-block region cannot be inlined into the parent block, so it is
+// wrapped in scf.execute_region.
+func.func private @then_fn()
+func.func private @else_fn()
+
+acc.routine @acc_routine_host_data_multi_block func(@host_data_multi_block) seq
+// CHECK-LABEL: func.func @host_data_multi_block(
+// CHECK-NOT: acc.host_data
+// CHECK: scf.execute_region {
+// CHECK-NEXT: cf.cond_br %{{.*}}, ^[[THEN:bb[0-9]+]], ^[[ELSE:bb[0-9]+]]
+// CHECK-NEXT: ^[[THEN]]:
+// CHECK-NEXT: call @then_fn()
+// CHECK-NEXT: scf.yield
+// CHECK-NEXT: ^[[ELSE]]:
+// CHECK-NEXT: call @else_fn()
+// CHECK-NEXT: scf.yield
+// CHECK-NEXT: }
+// CHECK-NEXT: return
+func.func @host_data_multi_block(%arg0 : memref<i32>, %cond : i1) attributes {acc.routine_info = #acc.routine_info<[@acc_routine_host_data_multi_block]>} {
+ %0 = acc.use_device varPtr(%arg0 : memref<i32>) -> memref<i32>
+ acc.host_data dataOperands(%0 : memref<i32>) {
+ cf.cond_br %cond, ^bb1, ^bb2
+ ^bb1:
+ func.call @then_fn() : () -> ()
+ acc.terminator
+ ^bb2:
+ func.call @else_fn() : () -> ()
+ acc.terminator
+ }
+ return
+}
+
+acc.routine @acc_routine_data_multi_block func(@data_multi_block) seq
+// CHECK-LABEL: func.func @data_multi_block(
+// CHECK-NOT: acc.data
+// CHECK: scf.execute_region {
+// CHECK-NEXT: cf.cond_br %{{.*}}, ^[[THEN:bb[0-9]+]], ^[[ELSE:bb[0-9]+]]
+// CHECK-NEXT: ^[[THEN]]:
+// CHECK-NEXT: call @then_fn()
+// CHECK-NEXT: scf.yield
+// CHECK-NEXT: ^[[ELSE]]:
+// CHECK-NEXT: call @else_fn()
+// CHECK-NEXT: scf.yield
+// CHECK-NEXT: }
+// CHECK-NEXT: return
+func.func @data_multi_block(%arg0 : memref<i32>, %cond : i1) attributes {acc.routine_info = #acc.routine_info<[@acc_routine_data_multi_block]>} {
+ %0 = acc.create varPtr(%arg0 : memref<i32>) -> memref<i32>
+ acc.data dataOperands(%0 : memref<i32>) {
+ cf.cond_br %cond, ^bb1, ^bb2
+ ^bb1:
+ func.call @then_fn() : () -> ()
+ acc.terminator
+ ^bb2:
+ func.call @else_fn() : () -> ()
+ acc.terminator
+ }
+ return
+}
+
//===----------------------------------------------------------------------===//
// Declare enter/exit - erased (host fallback)
//===----------------------------------------------------------------------===//
More information about the Mlir-commits
mailing list