[clang] [CodeGen] Implement Objective-C WebAssembly exception handling support (PR #215562)

Heejin Ahn via cfe-commits cfe-commits at lists.llvm.org
Sat Aug 22 01:29:58 PDT 2026


Hendrik =?utf-8?q?Hübner?= <hhuebner at hhuebnerMacBookPro.local>,
Hendrik =?utf-8?q?Hübner?= <hhuebner at hhuebnerMacBookPro.local>,
Hendrik =?utf-8?q?Hübner?= <hhuebner at MacBookPro.lan>,
Hendrik =?utf-8?q?Hübner?= <hhuebner at MacBookPro.lan>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/215562 at github.com>


================
@@ -182,61 +195,122 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
       Handler.TypeInfo = GetEHType(CatchDecl->getType());
     }
 
+    // Create a new catch scope
     EHCatchScope *Catch = CGF.EHStack.pushCatch(Handlers.size());
     for (unsigned I = 0, E = Handlers.size(); I != E; ++I)
       Catch->setHandler(I, { Handlers[I].TypeInfo, Handlers[I].Flags }, Handlers[I].Block);
   }
 
-  if (useFunclets)
+  if (IsMSVC) {
     if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt()) {
-        CodeGenFunction HelperCGF(CGM, /*suppressNewContext=*/true);
-        if (!CGF.CurSEHParent)
-            CGF.CurSEHParent = cast<NamedDecl>(CGF.CurFuncDecl);
-        // Outline the finally block.
-        const Stmt *FinallyBlock = Finally->getFinallyBody();
-        HelperCGF.startOutlinedSEHHelper(CGF, /*isFilter*/false, FinallyBlock);
-
-        // Emit the original filter expression, convert to i32, and return.
-        HelperCGF.EmitStmt(FinallyBlock);
+      CodeGenFunction HelperCGF(CGM, /*suppressNewContext=*/true);
+      if (!CGF.CurSEHParent)
+        CGF.CurSEHParent = cast<NamedDecl>(CGF.CurFuncDecl);
+      // Outline the finally block.
+      const Stmt *FinallyBlock = Finally->getFinallyBody();
+      HelperCGF.startOutlinedSEHHelper(CGF, /*isFilter*/ false, FinallyBlock);
 
-        HelperCGF.FinishFunction(FinallyBlock->getEndLoc());
+      // Emit the original filter expression, convert to i32, and return.
+      HelperCGF.EmitStmt(FinallyBlock);
 
-        llvm::Function *FinallyFunc = HelperCGF.CurFn;
+      HelperCGF.FinishFunction(FinallyBlock->getEndLoc());
 
+      llvm::Function *FinallyFunc = HelperCGF.CurFn;
 
-        // Push a cleanup for __finally blocks.
-        CGF.pushSEHCleanup(NormalAndEHCleanup, FinallyFunc);
+      // Push a cleanup for __finally blocks.
+      CGF.pushSEHCleanup(NormalAndEHCleanup, FinallyFunc);
     }
-
+  }
 
   // Emit the try body.
   CGF.EmitStmt(S.getTryBody());
 
+  // lpad or catch.dispatch (the dispatch block) has now been emitted
+  //
+  // Here an example:
+  // void may_throw();
+  // @try {
+  //    may_throw();
+  // } @catch(id a) {
+  // } @catch(id b) {
+  // [...]
+  //
+  // With funclet-based exception handling, the dispatch block is created in
+  // getEHDispatchBlock() <- getInvokeDestImpl() <- EmitCall().
+  // The following IR is emitted in this case:
+  // On aarch64-linux-gnu (landing-pad based)
+  //   %call = invoke i32 @may_throw()
+  //      to label %invoke.cont unwind label %lpad, !dbg !19
+  // On aarch64-pc-windows-msvc (funclet based)
+  // %call = invoke i32 @may_throw()
+  //      to label %invoke.cont unwind label %catch.dispatch, !dbg !17
+
   // Leave the try.
-  if (S.getNumCatchStmts())
-    CGF.popCatchScope();
+  llvm::BasicBlock *DispatchBlock = nullptr;
+  if (S.getNumCatchStmts()) {
+    // The dispatch block that was created during the emission of the try block
+    // was cached. We retrieve it when popping the current catch scope.
+    DispatchBlock = CGF.popCatchScope();
+  }
+
+  // On Windows and WASM, the new exception handling instructions are used.
+  //
+  // Continuing with the previous example, on Windows, we emit one catchpad for
+  // every catch handler. This is not the case for WASM where all catch handlers
+  // merged into one big catchpad:
+  //
+  // catch.dispatch:
+  // %0 = catchswitch within none [label %catch.start] unwind to caller
+  // catch.start:
+  //   %1 = catchpad within %0 [ptr @__objc_id_type_info, ptr null]
+  //   [...]
+  //   br i1 %matches, label %catch, label %catch2
+  //
+  // We save the old funclet pad here before we traverse each catch handler.
+  SaveAndRestore RestoreCurrentFuncletPad(CGF.CurrentFuncletPad);
+  llvm::BasicBlock *WasmCatchStartBlock = nullptr;
+  llvm::CatchPadInst *CPI = nullptr;
+  if (!!DispatchBlock && IsWasm) {
----------------
aheejin wrote:

```suggestion
  if (DispatchBlock && IsWasm) {
```
Do we need this `!!`?

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


More information about the cfe-commits mailing list