[clang] [CodeGen] Implement Objective-C WebAssembly exception handling support (PR #215562)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 11 06:40:06 PDT 2026
Hendrik =?utf-8?q?Hübner?= <hhuebner at hhuebnerMacBookPro.local>,
Hendrik =?utf-8?q?Hübner?= <hhuebner at hhuebnerMacBookPro.local>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/215562 at github.com>
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Hendrik Hübner (HendrikHuebner)
<details>
<summary>Changes</summary>
This PR adds WebAssembly exception handling support for Objective-C/C++ `@<!-- -->try`/`@<!-- -->catch`. `@<!-- -->finally` will be implemented in a subsequent patch.
This PR is based on #<!-- -->183753 and was co-authored by @<!-- -->hmelder. I addressed the review comments on the old PR, fixed an issue and added more tests.
The FileCheck assertions were generated with AI assistance.
---
Patch is 28.86 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/215562.diff
8 Files Affected:
- (modified) clang/lib/CodeGen/CGException.cpp (+33-23)
- (modified) clang/lib/CodeGen/CGObjCGNU.cpp (+8-9)
- (modified) clang/lib/CodeGen/CGObjCRuntime.cpp (+106-25)
- (modified) clang/lib/CodeGen/CodeGenFunction.h (+9-1)
- (modified) clang/lib/Driver/ToolChains/Clang.cpp (+2-1)
- (added) clang/test/CodeGenObjC/wasm32-eh-arc.m (+29)
- (added) clang/test/CodeGenObjC/wasm32-eh.m (+202)
- (added) clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm (+66)
``````````diff
diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp
index b0fb3b4d85d15..f8391a0d041f2 100644
--- a/clang/lib/CodeGen/CGException.cpp
+++ b/clang/lib/CodeGen/CGException.cpp
@@ -150,6 +150,8 @@ static const EHPersonality &getObjCPersonality(const TargetInfo &Target,
const llvm::Triple &T = Target.getTriple();
if (T.isWindowsMSVCEnvironment())
return EHPersonality::MSVC_CxxFrameHandler3;
+ if (T.isWasm())
+ return EHPersonality::GNU_Wasm_CPlusPlus;
switch (L.ObjCRuntime.getKind()) {
case ObjCRuntime::FragileMacOSX:
@@ -161,7 +163,7 @@ static const EHPersonality &getObjCPersonality(const TargetInfo &Target,
case ObjCRuntime::GNUstep:
if (T.isOSCygMing())
return EHPersonality::GNU_CPlusPlus_SEH;
- else if (L.ObjCRuntime.getVersion() >= VersionTuple(1, 7))
+ if (L.ObjCRuntime.getVersion() >= VersionTuple(1, 7))
return EHPersonality::GNUstep_ObjC;
[[fallthrough]];
case ObjCRuntime::GCC:
@@ -200,8 +202,11 @@ static const EHPersonality &getCXXPersonality(const TargetInfo &Target,
static const EHPersonality &getObjCXXPersonality(const TargetInfo &Target,
const CodeGenOptions &CGOpts,
const LangOptions &L) {
- if (Target.getTriple().isWindowsMSVCEnvironment())
+ auto Triple = Target.getTriple();
+ if (Triple.isWindowsMSVCEnvironment())
return EHPersonality::MSVC_CxxFrameHandler3;
+ if (Triple.isWasm())
+ return EHPersonality::GNU_Wasm_CPlusPlus;
switch (L.ObjCRuntime.getKind()) {
// In the fragile ABI, just use C++ exception handling and hope
@@ -218,8 +223,9 @@ static const EHPersonality &getObjCXXPersonality(const TargetInfo &Target,
return getObjCPersonality(Target, CGOpts, L);
case ObjCRuntime::GNUstep:
- return Target.getTriple().isOSCygMing() ? EHPersonality::GNU_CPlusPlus_SEH
- : EHPersonality::GNU_ObjCXX;
+ if (Triple.isOSCygMing())
+ return EHPersonality::GNU_CPlusPlus_SEH;
+ return EHPersonality::GNU_ObjCXX;
// The GCC runtime's personality function inherently doesn't support
// mixed EH. Use the ObjC personality just to avoid returning null.
@@ -1207,11 +1213,30 @@ static void emitCatchDispatchBlock(CodeGenFunction &CGF,
}
}
-void CodeGenFunction::popCatchScope() {
+llvm::BasicBlock *CodeGenFunction::popCatchScope() {
EHCatchScope &catchScope = cast<EHCatchScope>(*EHStack.begin());
+ llvm::BasicBlock *dispatchBlock = catchScope.getCachedEHDispatchBlock();
if (catchScope.hasEHBranches())
emitCatchDispatchBlock(*this, catchScope);
EHStack.popCatch();
+ return dispatchBlock;
+}
+
+void CodeGenFunction::WasmEmitFallthroughRethrow(
+ llvm::BasicBlock *WasmCatchStartBlock) {
+ assert(WasmCatchStartBlock);
+ // Navigate for the "rethrow" block. For CXX exceptions this was created in
+ // emitWasmCatchPadBlock(). Wasm uses landingpad-style conditional branches
+ // to compare selectors, so we follow the false destination for each of the
+ // cond branches to reach the rethrow block.
+ llvm::BasicBlock *RethrowBlock = WasmCatchStartBlock;
+ while (llvm::Instruction *TI = RethrowBlock->getTerminatorOrNull())
+ RethrowBlock = cast<llvm::CondBrInst>(TI)->getSuccessor(1);
+ assert(RethrowBlock != WasmCatchStartBlock && RethrowBlock->empty());
+ Builder.SetInsertPoint(RethrowBlock);
+ llvm::Function *RethrowInCatchFn =
+ CGM.getIntrinsic(llvm::Intrinsic::wasm_rethrow);
+ EmitNoreturnRuntimeCallOrInvoke(RethrowInCatchFn, {});
}
void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) {
@@ -1320,24 +1345,9 @@ void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) {
Builder.CreateBr(ContBB);
}
- // Because in wasm we merge all catch clauses into one big catchpad, in case
- // none of the types in catch handlers matches after we test against each of
- // them, we should unwind to the next EH enclosing scope. We generate a call
- // to rethrow function here to do that.
- if (EHPersonality::get(*this).isWasmPersonality() && !HasCatchAll) {
- assert(WasmCatchStartBlock);
- // Navigate for the "rethrow" block we created in emitWasmCatchPadBlock().
- // Wasm uses landingpad-style conditional branches to compare selectors, so
- // we follow the false destination for each of the cond branches to reach
- // the rethrow block.
- llvm::BasicBlock *RethrowBlock = WasmCatchStartBlock;
- while (llvm::Instruction *TI = RethrowBlock->getTerminatorOrNull())
- RethrowBlock = cast<llvm::CondBrInst>(TI)->getSuccessor(1);
- assert(RethrowBlock != WasmCatchStartBlock && RethrowBlock->empty());
- Builder.SetInsertPoint(RethrowBlock);
- llvm::Function *RethrowInCatchFn =
- CGM.getIntrinsic(llvm::Intrinsic::wasm_rethrow);
- EmitNoreturnRuntimeCallOrInvoke(RethrowInCatchFn, {});
+ if (EHPersonality::get(*this).isWasmPersonality() && !HasCatchAll &&
+ WasmCatchStartBlock) {
+ WasmEmitFallthroughRethrow(WasmCatchStartBlock);
}
EmitBlock(ContBB);
diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp
index 32a1afe310629..e4581feb5a21c 100644
--- a/clang/lib/CodeGen/CGObjCGNU.cpp
+++ b/clang/lib/CodeGen/CGObjCGNU.cpp
@@ -2373,12 +2373,13 @@ CGObjCGNU::CGObjCGNU(CodeGenModule &cgm, unsigned runtimeABIVersion,
MetaClassPtrAlias(nullptr), RuntimeVersion(runtimeABIVersion),
ProtocolVersion(protocolClassVersion), ClassABIVersion(classABI) {
+ auto Triple = cgm.getContext().getTargetInfo().getTriple();
+
msgSendMDKind = VMContext.getMDKindID("GNUObjCMessageSend");
- usesSEHExceptions =
- cgm.getContext().getTargetInfo().getTriple().isWindowsMSVCEnvironment();
+ usesSEHExceptions = Triple.isWindowsMSVCEnvironment();
usesCxxExceptions =
- cgm.getContext().getTargetInfo().getTriple().isOSCygMing() &&
- isRuntime(ObjCRuntime::GNUstep, 2);
+ (Triple.isOSCygMing() && isRuntime(ObjCRuntime::GNUstep, 2)) ||
+ Triple.isWasm();
CodeGenTypes &Types = CGM.getTypes();
IntTy = cast<llvm::IntegerType>(
@@ -4155,8 +4156,7 @@ llvm::Function *CGObjCGNU::ModuleInitFunction() {
if (!ClassAliases.empty()) {
llvm::Type *ArgTypes[2] = {PtrTy, PtrToInt8Ty};
llvm::FunctionType *RegisterAliasTy =
- llvm::FunctionType::get(Builder.getVoidTy(),
- ArgTypes, false);
+ llvm::FunctionType::get(Builder.getVoidTy(), ArgTypes, false);
llvm::Function *RegisterAlias = llvm::Function::Create(
RegisterAliasTy,
llvm::GlobalValue::ExternalWeakLinkage, "class_registerAlias_np",
@@ -4338,15 +4338,14 @@ void CGObjCGNU::EmitThrowStmt(CodeGenFunction &CGF,
// that was passed into the `@catch` block, then this code path is not
// reached and we will instead call `objc_exception_throw` with an explicit
// argument.
- llvm::CallBase *Throw = CGF.EmitRuntimeCallOrInvoke(ExceptionReThrowFn);
- Throw->setDoesNotReturn();
+ CGF.EmitNoreturnRuntimeCallOrInvoke(ExceptionReThrowFn, {});
} else {
ExceptionAsObject = CGF.Builder.CreateBitCast(ExceptionAsObject, IdTy);
llvm::CallBase *Throw =
CGF.EmitRuntimeCallOrInvoke(ExceptionThrowFn, ExceptionAsObject);
Throw->setDoesNotReturn();
+ CGF.Builder.CreateUnreachable();
}
- CGF.Builder.CreateUnreachable();
if (ClearInsertionPoint)
CGF.Builder.ClearInsertionPoint();
}
diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp b/clang/lib/CodeGen/CGObjCRuntime.cpp
index a83a4ce67a9c6..263b81331084d 100644
--- a/clang/lib/CodeGen/CGObjCRuntime.cpp
+++ b/clang/lib/CodeGen/CGObjCRuntime.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "CGObjCRuntime.h"
+#include "Address.h"
#include "CGCXXABI.h"
#include "CGCleanup.h"
#include "CGRecordLayout.h"
@@ -23,6 +24,8 @@
#include "clang/CodeGen/CGFunctionInfo.h"
#include "clang/CodeGen/CodeGenABITypes.h"
#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/SaveAndRestore.h"
using namespace clang;
@@ -148,12 +151,23 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
Cont = CGF.getJumpDestInCurrentScope("eh.cont");
bool useFunclets = EHPersonality::get(CGF).usesFuncletPads();
+ bool IsWasm = EHPersonality::get(CGF).isWasmPersonality();
+ bool IsMSVC = EHPersonality::get(CGF).isMSVCPersonality();
CodeGenFunction::FinallyInfo FinallyInfo;
- if (!useFunclets)
- if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt())
- FinallyInfo.enter(CGF, Finally->getFinallyBody(),
- beginCatchFn, endCatchFn, exceptionRethrowFn);
+ if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt()) {
+ if (!useFunclets) {
+ // The finally statement is executed as a cleanup for the normal and
+ // exceptional control flow out of a try-catch block. This is all
+ // implemented in FinallyInfo. Here we enter a new EHCatchScope.
+ FinallyInfo.enter(CGF, Finally->getFinallyBody(), beginCatchFn,
+ endCatchFn, exceptionRethrowFn);
+ } else {
+ CGF.ErrorUnsupported(
+ Finally,
+ "@finally is not implemented for funclet based exception handling");
+ }
+ }
SmallVector<CatchHandler, 8> Handlers;
@@ -182,61 +196,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);
+ 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);
+ // Emit the original filter expression, convert to i32, and return.
+ HelperCGF.EmitStmt(FinallyBlock);
- HelperCGF.FinishFunction(FinallyBlock->getEndLoc());
+ HelperCGF.FinishFunction(FinallyBlock->getEndLoc());
- llvm::Function *FinallyFunc = HelperCGF.CurFn;
+ 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) {
+ auto *CatchSwitch =
+ cast<llvm::CatchSwitchInst>(DispatchBlock->getFirstNonPHIIt());
+ WasmCatchStartBlock = CatchSwitch->hasUnwindDest()
+ ? CatchSwitch->getSuccessor(1)
+ : CatchSwitch->getSuccessor(0);
+ CPI = cast<llvm::CatchPadInst>(WasmCatchStartBlock->getFirstNonPHIIt());
+ CGF.CurrentFuncletPad = CPI;
+ }
// Remember where we were.
CGBuilderTy::InsertPoint SavedIP = CGF.Builder.saveAndClearIP();
- // Emit the handlers.
+ // Emit the handlers. If there is no catch-all handler, we need to emit a
+ // fallthrough block in WASM. We therefore need to know if we have a
+ // catch-all handler in this catch scope.
+ bool HasCatchAll = false;
for (CatchHandler &Handler : Handlers) {
+ HasCatchAll |= Handler.TypeInfo == nullptr;
CGF.EmitBlock(Handler.Block);
CodeGenFunction::LexicalScope Cleanups(CGF, Handler.Body->getSourceRange());
SaveAndRestore RevertAfterScope(CGF.CurrentFuncletPad);
- if (useFunclets) {
+ if (IsMSVC) {
llvm::BasicBlock::iterator CPICandidate =
Handler.Block->getFirstNonPHIIt();
if (CPICandidate != Handler.Block->end()) {
- if (auto *CPI = dyn_cast_or_null<llvm::CatchPadInst>(CPICandidate)) {
+ CPI = dyn_cast_or_null<llvm::CatchPadInst>(CPICandidate);
+ if (!!CPI) {
CGF.CurrentFuncletPad = CPI;
CPI->setOperand(2, CGF.getExceptionSlot().emitRawPointer(CGF));
- CGF.EHStack.pushCleanup<CatchRetScope>(NormalCleanup, CPI);
}
}
}
+ if (!!CPI) {
+ // A catchpad requires a matching catchret instruction. We emit this in
+ // form of a cleanup.
+ CGF.EHStack.pushCleanup<CatchRetScope>(NormalCleanup, CPI);
+ }
+
llvm::Value *RawExn = CGF.getExceptionFromSlot();
// Enter the catch.
@@ -262,6 +337,8 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
EmitInitOfCatchParam(CGF, CastExn, CatchParam);
}
+ // The body of the handler might have more try-catch blocks, so we need to
+ // save the current exception before emitting the body.
CGF.ObjCEHValueStack.push_back(Exn);
CGF.EmitStmt(Handler.Body);
CGF.ObjCEHValueStack.pop_back();
@@ -272,6 +349,10 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
CGF.EmitBranchThroughCleanup(Cont);
}
+ if (IsWasm && !HasCatchAll && WasmCatchStartBlock) {
+ CGF.WasmEmitFallthroughRethrow(WasmCatchStartBlock);
+ }
+
// Go back to the try-statement fallthrough.
CGF.Builder.restoreIP(SavedIP);
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index e89d754c309c8..b907a1c86c2e7 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -1326,7 +1326,15 @@ class CodeGenFunction : public CodeGenTypeCache {
/// popCatchScope - Pops the catch scope at the top of the EHScope
/// stack, emitting any required code (other than the catch handlers
/// themselves).
- void popCatchScope();
+ llvm::BasicBlock *popCatchScope();
+
+ // This function should be called after emitting all catch clauses and none
+ // of them were 'catch-all' clauses.
+ // Because in wasm we merge all catch clauses into one big catchpad, in case
+ // none of the types in catch handlers matches after we test against each of
+ // them, we should unwind to the next EH enclosing scope. We generate a call
+ // to rethrow function here to do that.
+ void WasmEmitFallthroughRethrow(llvm::BasicBlock *WasmCatchStartBlock);
llvm::BasicBlock *getEHResumeBlock(bool isCleanup);
llvm::BasicBlock *getEHDispatchBlock(EHScopeStack::stable_iterator scope);
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index d2e22920aa432..efbf5d28f56fe 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -8638,7 +8638,8 @@ ObjCRuntime Clang::AddObjCRuntimeArgs(const ArgList &args,
if ((runtime.getKind() == ObjCRuntime::GNUstep) &&
(runtime.getVersion() >= VersionTuple(2, 0)))
if (!getToolChain().getTriple().isOSBinFormatELF() &&
- !getToolChain().getTriple().isOSBinFormatCOFF()) {
+ !getToolChain().getTriple().isOSBinFormatCOFF() &&
+ !getToolChain().getTriple().isOSBinFormatWasm()) {
getToolChain().getDriver().Diag(
diag::err_drv_gnustep_objc_runtime_incompatible_binary)
<< runtime.getVersion().getMajor();
diff --git a/clang/test/CodeGenObjC/wasm32-eh-arc.m b/clang/test/CodeGenObjC/wasm32-eh-arc.m
new file mode 100644
index 0000000000000..1100e3bfbf28d
--- /dev/null
+++ b/clang/test/CodeGenObjC/wasm32-eh-arc.m
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -triple wasm32-unknown-emscripten -fobjc-runtime=gnustep-2.2 -fobjc-arc -fexceptions -fobjc-exceptions -exception-model=wasm -mllvm -wasm-enable-eh -emit-llvm -o - %s | FileCheck --enable-var-scope %s
+__attribute__((objc_root_class)) @interface Object @end
+extern void mayThrowObjC();
+
+int arcRethrow(Object *value) {
+ @try {
+ mayThrowObjC();
+ } @catch (id caught) {
+ @throw;
+ }
+ return 0;
+}
+
+// CHECK-LABEL: define{{.*}} @arcRethrow
+// CHECK: invoke void @mayThrowObjC()
+// CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[CATCH_DISPATCH:.*]]
+// CHECK: [[CATCH_DISPATCH]]:
+// CHECK-NEXT: [[CATCHSWITCH:%.*]] = catchswitch within none [label %[[CATCH_START:.*]]] unwind to caller
+// CHECK: [[CATCH_START]]:
+// CHECK-NEXT: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr @__objc_id_type_info]
+// CHECK: br i1 %{{.*}}, label %[[CATCH:.*]], label %[[RETHROW:.*]]
+// CHECK: [[RETHROW]]:
+// CHECK-NEXT: call void @llvm.wasm.rethrow()
+// CHECK-NEXT: unreachable
+// CHECK: [[INVOKE_CONT]]:
+// CHECK: br label %{{.*}}
+// CHECK: [[CATCH]]:
+// CHECK: invoke void @__cxa_rethrow(){{.*}}[ "funclet"(token [[CATCHPAD]]) ]
+// CHECK-NEXT: to label %unreachable unwind label
diff --git a/clang/test/CodeGenObjC/wasm32-eh.m b/clang/test/CodeGenObjC/wasm32-eh.m
new file mode 100644
index 0000000000000..e35d81de51bf5
--- /dev/null
+++ b/clang/test/CodeGenObjC/wasm32-eh.m
@@ -0,0 +1,202 @@
+// RUN: %clang_cc1 -triple wasm32-unknown-emscripten -fobjc-exceptions -fexceptions -exception-model=wasm -mllvm -wasm-enable-eh -emit-llvm -fobjc-runtime=gnustep-2.2 -o - %s | FileCheck --enable-var-scope %s
+
+__attribute__((objc_root_class)) @interface Object
+ at end
+
+ at interface ExceptionA : Object
+ at end
+
+ at interface ExceptionB : Object
+ at end
+
+void mayThrow(void) {
+ @throw (id)1;
+}
+
+int basicCatchAll(void) {
+ @try {
+ mayThrow();
+ } @catch (...) {
+ return 1;
+ }
+ return 0;
+}
+
+// CHECK-LABEL: define{{.*}} @basicCatchAll
+// CHECK: invoke void @mayThrow()
+// CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[CATCH_DISPATCH:.*]]
+// CHECK: [[CATCH_DISPATCH]]:
+// CHECK-NEXT: [[CATCHSWITCH:%.*]] = catchswitch within none [label %[[CATCH_START:.*]]] unwind to caller
+// CHECK: [[CATCH_START]]:
+// CHECK-NEXT: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr null]
+// CHECK: br l...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/215562
More information about the cfe-commits
mailing list