[clang] [CIR] Implement flattening for cleanup scopes with multiple exits (PR #180627)

Henrich Lauko via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 10 02:03:39 PST 2026


================
@@ -768,94 +862,104 @@ class CIRCleanupScopeOpFlattening
     rewriter.setInsertionPointToEnd(currentBlock);
     cir::BrOp::create(rewriter, loc, bodyEntry);
 
-    // Create a block for the exit terminator (after cleanup, before continue).
+    // Create the exit/dispatch block (after cleanup, before continue).
     mlir::Block *exitBlock = rewriter.createBlock(continueBlock);
 
     // Rewrite the cleanup region's yield to branch to exit block.
     rewriter.setInsertionPoint(cleanupYield);
     rewriter.replaceOpWithNewOp<cir::BrOp>(cleanupYield, exitBlock);
 
-    // Put the appropriate terminator in the exit block.
-    rewriter.setInsertionPointToEnd(exitBlock);
-    mlir::LogicalResult result =
-        llvm::TypeSwitch<mlir::Operation *, mlir::LogicalResult>(exitOp)
-            .Case<cir::YieldOp>([&](auto) {
-              // Yield becomes a branch to continue block.
-              cir::BrOp::create(rewriter, loc, continueBlock);
-              return mlir::success();
-            })
-            .Case<cir::BreakOp>([&](auto) {
-              // Break is preserved for later lowering by enclosing switch/loop.
-              cir::BreakOp::create(rewriter, loc);
-              return mlir::success();
-            })
-            .Case<cir::ContinueOp>([&](auto) {
-              // Continue is preserved for later lowering by enclosing loop.
-              cir::ContinueOp::create(rewriter, loc);
-              return mlir::success();
-            })
-            .Case<cir::ReturnOp>([&](auto &returnOp) {
-              // Return from the cleanup exit. Note, if this is a return inside
-              // a nested cleanup scope, the flattening of the outer scope will
-              // handle branching through the outer cleanup.
-              if (returnOp.hasOperand())
-                cir::ReturnOp::create(rewriter, loc, returnOp.getOperands());
-              else
-                cir::ReturnOp::create(rewriter, loc);
-              return mlir::success();
-            })
-            .Case<cir::GotoOp>([&](auto &gotoOp) {
-              // Correct goto handling requires determining whether the goto
-              // branches out of the cleanup scope or stays within it.
-              // Although the goto necessarily exits the cleanup scope in the
-              // case where it is the only exit from the scope, it is left
-              // as unimplemented for now so that it can be generalized when
-              // multi-exit flattening is implemented.
-              cir::UnreachableOp::create(rewriter, loc);
-              return gotoOp.emitError(
-                  "goto in cleanup scope is not yet implemented");
-            })
-            .Default([&](mlir::Operation *op) {
-              cir::UnreachableOp::create(rewriter, loc);
-              return op->emitError(
-                  "unexpected terminator in cleanup scope body");
-            });
-
-    // Replace body exit with branch to cleanup entry.
-    rewriter.setInsertionPoint(exitOp);
-    rewriter.replaceOpWithNewOp<cir::BrOp>(exitOp, cleanupEntry);
+    mlir::LogicalResult result = mlir::success();
+    if (isMultiExit) {
+      // Build the dispatch switch in the exit block.
+      rewriter.setInsertionPointToEnd(exitBlock);
+
+      // Load the destination slot value.
+      auto slotValue = cir::LoadOp::create(
+          rewriter, loc, destSlot, /*isDeref=*/false,
+          /*isVolatile=*/false, /*alignment=*/mlir::IntegerAttr(),
+          cir::SyncScopeKindAttr(), cir::MemOrderAttr());
+
+      // Create destination blocks for each exit and collect switch case info.
+      llvm::SmallVector<mlir::APInt, 8> caseValues;
+      llvm::SmallVector<mlir::Block *, 8> caseDestinations;
+      llvm::SmallVector<mlir::ValueRange, 8> caseOperands;
+      cir::IntType s32Type =
+          cir::IntType::get(rewriter.getContext(), 32, /*isSigned=*/true);
+
+      for (const CleanupExit &exit : exits) {
+        // Create a block for this destination.
+        mlir::Block *destBlock = rewriter.createBlock(continueBlock);
+        rewriter.setInsertionPointToEnd(destBlock);
+        result =
+            createExitTerminator(exit.exitOp, loc, continueBlock, rewriter);
+
+        // Add to switch cases.
----------------
xlauko wrote:

the following code executes even on failure of createExitTerminator, shouldn;t we bail out sooner?

https://github.com/llvm/llvm-project/pull/180627


More information about the cfe-commits mailing list