[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 &region = 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 = &region.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