[Mlir-commits] [mlir] [mlir][openacc] Support terminators with results in wrapMultiBlockRegionWithSCFExecuteRegion (PR #185950)

Delaram Talaashrafi llvmlistbot at llvm.org
Wed Mar 11 12:51:45 PDT 2026


https://github.com/delaram-talaashrafi updated https://github.com/llvm/llvm-project/pull/185950

>From 43500a798d770669b4494d05714ae9e461e15b45 Mon Sep 17 00:00:00 2001
From: Delaram Talaashrafi <dtalaashrafi at rome5.pgi.net>
Date: Wed, 11 Mar 2026 09:38:11 -0700
Subject: [PATCH 1/2] [mlir][openacc] Support terminators with results in
 wrapMultiBlockRegionWithSCFExecuteRegion

When wrapping a multi-block region in `scf.execute_region`, this change gets the result types
from the operands of the terminators (`func.return` or `acc.yield`) and creates the `execute_region`
with those types. It then replaces each such terminator with `scf.yield(operands)`, ensuring that
multiple returns or yields with results are handled correctly.
---
 .../OpenACC/Utils/OpenACCUtilsLoop.cpp        | 42 +++++----
 .../Dialect/OpenACC/OpenACCUtilsLoopTest.cpp  | 88 ++++++++++++++-----
 2 files changed, 90 insertions(+), 40 deletions(-)

diff --git a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtilsLoop.cpp b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtilsLoop.cpp
index de4f0a26a0f11..ab696deba2ff7 100644
--- a/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtilsLoop.cpp
+++ b/mlir/lib/Dialect/OpenACC/Utils/OpenACCUtilsLoop.cpp
@@ -173,29 +173,33 @@ scf::ExecuteRegionOp
 wrapMultiBlockRegionWithSCFExecuteRegion(Region &region, IRMapping &mapping,
                                          Location loc, RewriterBase &rewriter,
                                          bool convertFuncReturn) {
-  auto exeRegionOp = scf::ExecuteRegionOp::create(rewriter, loc, TypeRange{});
+  SmallVector<Operation *> terminators;
+  for (Block &block : region.getBlocks()) {
+    if (block.empty())
+      continue;
+    Operation *term = block.getTerminator();
+    if ((convertFuncReturn && isa<func::ReturnOp>(*term)) ||
+        isa<acc::YieldOp>(*term))
+      terminators.push_back(term);
+  }
+  SmallVector<Type> resultTypes;
+  if (!terminators.empty())
+    for (Value operand : terminators.front()->getOperands())
+      resultTypes.push_back(operand.getType());
+
+  auto exeRegionOp =
+      scf::ExecuteRegionOp::create(rewriter, loc, TypeRange(resultTypes));
 
   rewriter.cloneRegionBefore(region, exeRegionOp.getRegion(),
                              exeRegionOp.getRegion().end(), mapping);
 
-  SmallVector<Block *, 8> blocks(
-      llvm::make_pointer_range(exeRegionOp.getRegion().getBlocks()));
-
-  for (Block *block : blocks) {
-    if (block->empty())
-      continue;
-    Operation *blockTerminator = block->getTerminator();
-    if ((convertFuncReturn && isa<func::ReturnOp>(*blockTerminator)) ||
-        isa<acc::YieldOp>(*blockTerminator)) {
-      if (blockTerminator->getNumOperands()) {
-        region.getParentOp()->emitError(
-            "region with results not yet supported");
-        return nullptr;
-      }
-      rewriter.setInsertionPointToEnd(block);
-      (void)scf::YieldOp::create(rewriter, blockTerminator->getLoc());
-      rewriter.eraseOp(blockTerminator);
-    }
+  for (Operation *term : terminators) {
+    Operation *blockTerminator = mapping.lookup(term);
+    assert(blockTerminator && "expected terminator to be in mapping");
+    rewriter.setInsertionPoint(blockTerminator);
+    (void)scf::YieldOp::create(rewriter, blockTerminator->getLoc(),
+                               blockTerminator->getOperands());
+    rewriter.eraseOp(blockTerminator);
   }
 
   return exeRegionOp;
diff --git a/mlir/unittests/Dialect/OpenACC/OpenACCUtilsLoopTest.cpp b/mlir/unittests/Dialect/OpenACC/OpenACCUtilsLoopTest.cpp
index 3b1558e4f4146..307fc3a9b1633 100644
--- a/mlir/unittests/Dialect/OpenACC/OpenACCUtilsLoopTest.cpp
+++ b/mlir/unittests/Dialect/OpenACC/OpenACCUtilsLoopTest.cpp
@@ -803,19 +803,77 @@ TEST_F(OpenACCUtilsLoopTest,
   EXPECT_EQ(mulCount, 1u);
 }
 
-//===----------------------------------------------------------------------===//
-// Error Case Tests
-//===----------------------------------------------------------------------===//
+TEST_F(OpenACCUtilsLoopTest,
+       WrapMultiBlockRegionWithSCFExecuteRegionFuncMultipleReturnsWithResults) {
+  // Create a function with multiple blocks that each end with func.return
+  // with results. wrapMultiBlockRegionWithSCFExecuteRegion(..., true) should
+  // replace each func.return with scf.yield and produce an execute_region
+  // with the same result types.
+  OwningOpRef<ModuleOp> module = ModuleOp::create(loc);
+  b.setInsertionPointToStart(module->getBody());
+
+  Type i32 = b.getI32Type();
+  auto funcType = b.getFunctionType({}, {i32});
+  auto funcOp = func::FuncOp::create(b, loc, "test_func", funcType);
+  Block *entry = funcOp.addEntryBlock();
+  Block *thenBlock = b.createBlock(&funcOp.getBody(), funcOp.getBody().end());
+  Block *elseBlock = b.createBlock(&funcOp.getBody(), funcOp.getBody().end());
+
+  b.setInsertionPointToEnd(entry);
+  Value cond =
+      arith::ConstantOp::create(b, loc, b.getI1Type(), b.getBoolAttr(true));
+  cf::CondBranchOp::create(b, loc, cond, thenBlock, elseBlock);
+
+  b.setInsertionPointToEnd(thenBlock);
+  Value thenV = createI32Constant(1);
+  func::ReturnOp::create(b, loc, ValueRange{thenV});
+
+  b.setInsertionPointToEnd(elseBlock);
+  Value elseV = createI32Constant(3);
+  func::ReturnOp::create(b, loc, ValueRange{elseV});
+
+  Region &region = funcOp.getBody();
+  EXPECT_EQ(region.getBlocks().size(), 3u);
+
+  b.setInsertionPointAfter(funcOp);
+  IRMapping mapping;
+  scf::ExecuteRegionOp exeRegionOp = wrapMultiBlockRegionWithSCFExecuteRegion(
+      region, mapping, loc, b, /*convertFuncReturn=*/true);
+  
+  ASSERT_TRUE(exeRegionOp);
+
+  // execute_region should have one result (same as func.return operands)
+  EXPECT_EQ(exeRegionOp.getNumResults(), 1u);
+  EXPECT_TRUE(exeRegionOp.getResult(0).getType().isSignlessInteger(32));
+
+  // Cloned region should have 3 blocks
+  EXPECT_EQ(exeRegionOp.getRegion().getBlocks().size(), 3u);
+
+  // Entry block: cond_br unchanged
+  Block &exeEntry = exeRegionOp.getRegion().front();
+  EXPECT_TRUE(isa<cf::CondBranchOp>(exeEntry.getTerminator()));
+
+  // Both then and else blocks should now have scf.yield with one operand
+  unsigned yieldCount = 0;
+  for (Block &block : exeRegionOp.getRegion().getBlocks()) {
+    if (isa<scf::YieldOp>(block.getTerminator())) {
+      EXPECT_EQ(block.getTerminator()->getNumOperands(), 1u);
+      yieldCount++;
+    }
+  }
+  EXPECT_EQ(yieldCount, 2u) << "both return blocks should be scf.yield";
+}
 
-TEST_F(OpenACCUtilsLoopTest, UnstructuredLoopWithYieldOperandsReturnsNullptr) {
+TEST_F(OpenACCUtilsLoopTest, UnstructuredLoopWithYieldOperandsSucceeds) {
   auto [module, funcOp] = createModuleWithFunc();
 
   Value c0 = createIndexConstant(0);
   Value c10 = createIndexConstant(10);
   Value c1 = createIndexConstant(1);
 
-  // Create an unstructured loop where the yield has operands (simulating
-  // a loop with results, which is not yet supported)
+  // Create an unstructured loop where the yield has operands (loop with
+  // results); conversion should succeed and produce execute_region with
+  // results.
   auto loopOp = acc::LoopOp::create(b, loc, {c0}, {c10}, {c1},
                                     acc::LoopParMode::loop_independent);
   loopOp.setInclusiveUpperboundAttr(b.getDenseBoolArrayAttr({true}));
@@ -832,28 +890,16 @@ TEST_F(OpenACCUtilsLoopTest, UnstructuredLoopWithYieldOperandsReturnsNullptr) {
     cf::BranchOp::create(b, loc, exitBlock);
 
     b.setInsertionPointToEnd(exitBlock);
-    // Create a yield with operands - this triggers the error
     Value result = createI32Constant(42);
     acc::YieldOp::create(b, loc, ValueRange{result});
   }
-  // InsertionGuard restores insertion point to after loopOp
-
-  // Use a diagnostic handler to capture the error
-  std::string errorMsg;
-  ScopedDiagnosticHandler handler(&context, [&](Diagnostic &diag) {
-    if (diag.getSeverity() == DiagnosticSeverity::Error) {
-      llvm::raw_string_ostream os(errorMsg);
-      os << diag;
-    }
-    return success();
-  });
 
   scf::ExecuteRegionOp exeRegionOp =
       convertUnstructuredACCLoopToSCFExecuteRegion(loopOp, b);
 
-  // Should return nullptr due to unsupported loop with results
-  EXPECT_FALSE(exeRegionOp);
-  EXPECT_TRUE(errorMsg.find("not yet supported") != std::string::npos);
+  ASSERT_TRUE(exeRegionOp);
+  EXPECT_EQ(exeRegionOp.getNumResults(), 1u);
+  EXPECT_TRUE(exeRegionOp.getResult(0).getType().isSignlessInteger(32));
 }
 
 //===----------------------------------------------------------------------===//

>From 9643746c3c8807dd212a5fa4da0d2a037e07c4da Mon Sep 17 00:00:00 2001
From: Delaram Talaashrafi <dtalaashrafi at rome5.pgi.net>
Date: Wed, 11 Mar 2026 09:40:22 -0700
Subject: [PATCH 2/2] Format

---
 mlir/unittests/Dialect/OpenACC/OpenACCUtilsLoopTest.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mlir/unittests/Dialect/OpenACC/OpenACCUtilsLoopTest.cpp b/mlir/unittests/Dialect/OpenACC/OpenACCUtilsLoopTest.cpp
index 307fc3a9b1633..3443ac7964bc2 100644
--- a/mlir/unittests/Dialect/OpenACC/OpenACCUtilsLoopTest.cpp
+++ b/mlir/unittests/Dialect/OpenACC/OpenACCUtilsLoopTest.cpp
@@ -839,7 +839,7 @@ TEST_F(OpenACCUtilsLoopTest,
   IRMapping mapping;
   scf::ExecuteRegionOp exeRegionOp = wrapMultiBlockRegionWithSCFExecuteRegion(
       region, mapping, loc, b, /*convertFuncReturn=*/true);
-  
+
   ASSERT_TRUE(exeRegionOp);
 
   // execute_region should have one result (same as func.return operands)



More information about the Mlir-commits mailing list