https://github.com/HendrikHuebner created https://github.com/llvm/llvm-project/pull/218967
None From e99c076eba76a2784b86a71e501b65b52a10d777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hendrik=20H=C3=BCbner?= <[email protected]> Date: Fri, 21 Aug 2026 15:10:49 +0200 Subject: [PATCH 1/2] [CodeGen][ObjC] Use C++-based EH for WASM targets Squash HendrikHuebner:objc-wasm-exception-handling into a single reviewable commit. --- clang/lib/CodeGen/CGException.cpp | 56 ++--- clang/lib/CodeGen/CGObjCGNU.cpp | 17 +- clang/lib/CodeGen/CGObjCRuntime.cpp | 130 +++++++++--- clang/lib/CodeGen/CodeGenFunction.h | 10 +- clang/lib/Driver/ToolChains/Clang.cpp | 3 +- clang/test/CodeGenObjC/wasm32-eh-arc.m | 29 +++ clang/test/CodeGenObjC/wasm32-eh.m | 202 +++++++++++++++++++ clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm | 66 ++++++ 8 files changed, 454 insertions(+), 59 deletions(-) create mode 100644 clang/test/CodeGenObjC/wasm32-eh-arc.m create mode 100644 clang/test/CodeGenObjC/wasm32-eh.m create mode 100644 clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm 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..442c2990d54d8 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,22 @@ 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 if (IsWasm) { + CGF.ErrorUnsupported(Finally, + "@finally is not implemented for WebAssembly"); + } + } SmallVector<CatchHandler, 8> Handlers; @@ -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) { + 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 +336,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 +348,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 +@end + +@interface ExceptionA : Object +@end + +@interface ExceptionB : Object +@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 label %[[CATCH_ALL:.*]] +// CHECK: [[INVOKE_CONT]]: +// CHECK: br label %[[EH_CONT:.*]] +// CHECK: [[CATCH_ALL]]: +// CHECK: call ptr @__cxa_begin_catch +// CHECK: call void @__cxa_end_catch() +// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST:.*]] +// CHECK: [[CATCHRET_DEST]]: +// CHECK-NEXT: br label %return + +int twoTypedHandlers(void) { + @try { + mayThrow(); + } @catch (ExceptionA *exception) { + return 1; + } @catch (ExceptionB *exception) { + return 2; + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @twoTypedHandlers +// 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 @__objc_eh_typeinfo_ExceptionA, ptr @__objc_eh_typeinfo_ExceptionB] +// CHECK: br i1 %{{.*}}, label %[[CATCH:.*]], label %[[CATCH_FALLTHROUGH:.*]] +// CHECK: [[CATCH_FALLTHROUGH]]: +// CHECK: br i1 %{{.*}}, label %[[CATCH2:.*]], label %[[RETHROW:.*]] +// CHECK: [[RETHROW]]: +// CHECK-NEXT: call void @llvm.wasm.rethrow() +// CHECK-NEXT: unreachable +// CHECK: [[INVOKE_CONT]]: +// CHECK: br label %[[EH_CONT:.*]] +// CHECK: [[EH_CONT]]: +// CHECK: br label %return +// CHECK: [[CATCH]]: +// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST:.*]] +// CHECK: [[CATCHRET_DEST]]: +// CHECK-NEXT: br label %return +// CHECK: [[CATCH2]]: +// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST2:.*]] +// CHECK: [[CATCHRET_DEST2]]: +// CHECK-NEXT: br label %return + +int typedHandlerAndCatchAll(void) { + @try { + mayThrow(); + } @catch (ExceptionA *exception) { + return 1; + } @catch (...) { + return 2; + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @typedHandlerAndCatchAll +// 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 @__objc_eh_typeinfo_ExceptionA, ptr null] +// CHECK: br i1 %{{.*}}, label %[[CATCH:.*]], label %[[CATCH_ALL:.*]] +// CHECK: [[INVOKE_CONT]]: +// CHECK: br label %[[EH_CONT:.*]] +// CHECK: [[EH_CONT]]: +// CHECK: br label %return +// CHECK: [[CATCH]]: +// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST:.*]] +// CHECK: [[CATCHRET_DEST]]: +// CHECK-NEXT: br label %return +// CHECK: [[CATCH_ALL]]: +// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST_ALL:.*]] +// CHECK: [[CATCHRET_DEST_ALL]]: +// CHECK-NEXT: br label %return + +int nestedTryCatch(void) { + @try { + @try { + mayThrow(); + } @catch (ExceptionA *exception) { + return 1; + } + } @catch (...) { + return 2; + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @nestedTryCatch +// 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 label %[[CATCH_DISPATCH1:.*]] +// CHECK: [[CATCH_START]]: +// CHECK-NEXT: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr @__objc_eh_typeinfo_ExceptionA] +// CHECK: br i1 %{{.*}}, label %[[CATCH:.*]], label %[[RETHROW:.*]] +// CHECK: [[RETHROW]]: +// CHECK: invoke void @llvm.wasm.rethrow(){{.*}}[ "funclet"(token [[CATCHPAD]]) ] +// CHECK-NEXT: to label %[[UNREACHABLE:.*]] unwind label %[[CATCH_DISPATCH1]] +// CHECK: [[CATCH_DISPATCH1]]: +// CHECK-NEXT: [[CATCHSWITCH1:%.*]] = catchswitch within none [label %[[CATCH_START2:.*]]] unwind to caller +// CHECK: [[CATCH_START2]]: +// CHECK-NEXT: [[CATCHPAD1:%.*]] = catchpad within [[CATCHSWITCH1]] [ptr null] +// CHECK: [[INVOKE_CONT]]: +// CHECK: br label %[[EH_CONT:.*]] +// CHECK: [[EH_CONT]]: +// CHECK: br label %[[EH_CONT2:.*]] +// CHECK: [[EH_CONT2]]: +// CHECK: br label %return +// CHECK: [[CATCH]]: +// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST:.*]] +// CHECK: [[CATCHRET_DEST]]: +// CHECK-NEXT: br label %return +// CHECK: [[CATCH_ALL:.*]]: +// CHECK: catchret from [[CATCHPAD1]] to label %[[CATCHRET_DEST_ALL:.*]] +// CHECK: [[CATCHRET_DEST_ALL]]: +// CHECK-NEXT: br label %return + +int emptyCatch(void) { + @try { + mayThrow(); + } @catch (ExceptionA *exception) { + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @emptyCatch +// 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 @__objc_eh_typeinfo_ExceptionA] +// 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 %[[EH_CONT:.*]] +// CHECK: [[EH_CONT]]: +// CHECK: [[CATCH]]: +// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST:.*]] +// CHECK: [[CATCHRET_DEST]]: +// CHECK-NEXT: br label %[[EH_CONT]] + +int explicitRethrow(void) { + @try { + mayThrow(); + } @catch (...) { + @throw; + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @explicitRethrow +// CHECK: invoke void @mayThrow() +// CHECK-NEXT: to label %{{.*}} 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 label %[[CATCH_ALL:.*]] +// CHECK: [[CATCH_ALL]]: +// CHECK: invoke void @__cxa_rethrow(){{.*}}[ "funclet"(token [[CATCHPAD]]) ] +// CHECK-NEXT: to label %[[UNREACHABLE:.*]] unwind label %{{.*}} +// CHECK: [[UNREACHABLE]]: +// CHECK-NEXT: unreachable diff --git a/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm b/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm new file mode 100644 index 0000000000000..4b989e6122fdd --- /dev/null +++ b/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm @@ -0,0 +1,66 @@ +// RUN: %clang_cc1 -target-feature +exception-handling -triple wasm32-unknown-emscripten -fobjc-runtime=gnustep-2.2 -fexceptions -fobjc-exceptions -fcxx-exceptions -exception-model=wasm -mllvm -wasm-enable-eh -emit-llvm -o - %s | FileCheck --enable-var-scope %s + +struct ThrowingDestructor { + ~ThrowingDestructor() noexcept(false); +}; + +extern void mayThrowCXX(); + +int cxxDestructorsAroundCatch() { + try { + ThrowingDestructor guard; + mayThrowCXX(); + } catch (...) { + ThrowingDestructor caught; + return 1; + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @_Z25cxxDestructorsAroundCatchv +// CHECK: invoke void @_Z{{[0-9]+}}mayThrowCXXv() +// CHECK: [[CLEANUPPAD:%.*]] = cleanuppad within none [] +// CHECK: invoke{{.*}} @_ZN18ThrowingDestructorD1Ev{{.*}}[ "funclet"(token [[CLEANUPPAD]]) ] +// CHECK: cleanupret from [[CLEANUPPAD]] unwind label %{{.*}} +// CHECK: [[CATCHSWITCH:%.*]] = catchswitch within none [label %{{.*}}] unwind to caller +// CHECK: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr null] +// CHECK: invoke{{.*}} @_ZN18ThrowingDestructorD1Ev{{.*}}[ "funclet"(token [[CATCHPAD]]) ] +// CHECK: catchret from [[CATCHPAD]] to label %{{.*}} + +__attribute__((objc_root_class)) @interface Object +@end + +extern void mayThrowObjC(); + +int combinedCxxObjcEH() { + @try { + try { + mayThrowCXX(); + } catch (Object *exception) { + @try { + mayThrowObjC(); + } @catch (Object *nestedException) { + return 1; + } + return 2; + } catch (int value) { + return value; + } + } @catch (...) { + return 3; + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @_Z{{[0-9]+}}combinedCxxObjcEHv +// CHECK: invoke void @_Z{{[0-9]+}}mayThrowCXXv() +// CHECK: [[CATCHSWITCH:%.*]] = catchswitch within none [label %{{.*}}] unwind label %{{.*}} +// CHECK: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr @__objc_eh_typeinfo_Object, ptr @_ZTIi] +// CHECK: invoke void @_Z{{[0-9]+}}mayThrowObjCv() [ "funclet"(token [[CATCHPAD]]) ] +// CHECK: [[NESTED_SWITCH:%.*]] = catchswitch within [[CATCHPAD]] [label %{{.*}}] unwind label %{{.*}} +// CHECK: [[NESTED_PAD:%.*]] = catchpad within [[NESTED_SWITCH]] [ptr @__objc_eh_typeinfo_Object] +// CHECK: invoke void @llvm.wasm.rethrow(){{.*}}[ "funclet"(token [[NESTED_PAD]]) ] +// CHECK: catchret from [[CATCHPAD]] to label %{{.*}} +// CHECK: invoke void @llvm.wasm.rethrow(){{.*}}[ "funclet"(token [[CATCHPAD]]) ] +// CHECK: [[OUTER_SWITCH:%.*]] = catchswitch within none [label %{{.*}}] unwind to caller +// CHECK: [[OUTER_PAD:%.*]] = catchpad within [[OUTER_SWITCH]] [ptr null] From 11bf4e8db15ed99e41a386ada1cb2af2c5b5968e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hendrik=20H=C3=BCbner?= <[email protected]> Date: Fri, 21 Aug 2026 15:11:49 +0200 Subject: [PATCH 2/2] [CodeGen][ObjC] Implement @finally for WebAssembly EH Add WebAssembly @finally lowering, funclet-aware cleanup handling, and focused CodeGen/Sema coverage. --- clang/lib/CodeGen/CGException.cpp | 40 +++- clang/lib/CodeGen/CGObjCRuntime.cpp | 10 +- .../CodeGenObjC/gnustep2-wasm32-arc-rethrow.m | 17 ++ clang/test/CodeGenObjC/gnustep2-wasm32-eh.m | 35 +++ .../CodeGenObjC/gnustep2-wasm32-empty-catch.m | 15 ++ .../CodeGenObjC/gnustep2-wasm32-finally.m | 176 +++++++++++++++ clang/test/CodeGenObjC/wasm32-eh-arc.m | 29 --- clang/test/CodeGenObjC/wasm32-eh.m | 202 ------------------ .../gnustep2-wasm32-arc-cxx-catch.mm | 21 ++ .../gnustep2-wasm32-cxx-destructor.mm | 28 +++ clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm | 66 ------ clang/test/SemaObjC/wasm-try-goto.m | 48 +++++ 12 files changed, 378 insertions(+), 309 deletions(-) create mode 100644 clang/test/CodeGenObjC/gnustep2-wasm32-arc-rethrow.m create mode 100644 clang/test/CodeGenObjC/gnustep2-wasm32-eh.m create mode 100644 clang/test/CodeGenObjC/gnustep2-wasm32-empty-catch.m create mode 100644 clang/test/CodeGenObjC/gnustep2-wasm32-finally.m delete mode 100644 clang/test/CodeGenObjC/wasm32-eh-arc.m delete mode 100644 clang/test/CodeGenObjC/wasm32-eh.m create mode 100644 clang/test/CodeGenObjCXX/gnustep2-wasm32-arc-cxx-catch.mm create mode 100644 clang/test/CodeGenObjCXX/gnustep2-wasm32-cxx-destructor.mm delete mode 100644 clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm create mode 100644 clang/test/SemaObjC/wasm-try-goto.m diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index f8391a0d041f2..ac362e9108d8c 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -1415,14 +1415,19 @@ namespace { CGF.Builder.CreateCondBr(ShouldRethrow, RethrowBB, ContBB); CGF.EmitBlock(RethrowBB); - if (SavedExnVar) { - CGF.EmitRuntimeCallOrInvoke(RethrowFn, - CGF.Builder.CreateAlignedLoad(CGF.Int8PtrTy, SavedExnVar, - CGF.getPointerAlign())); + if (EHPersonality::get(CGF).isWasmPersonality()) { + CGF.EmitNoreturnRuntimeCallOrInvoke(RethrowFn, {}); } else { - CGF.EmitRuntimeCallOrInvoke(RethrowFn); + if (SavedExnVar) { + CGF.EmitRuntimeCallOrInvoke( + RethrowFn, + CGF.Builder.CreateAlignedLoad(CGF.Int8PtrTy, SavedExnVar, + CGF.getPointerAlign())); + } else { + CGF.EmitRuntimeCallOrInvoke(RethrowFn); + } + CGF.Builder.CreateUnreachable(); } - CGF.Builder.CreateUnreachable(); CGF.EmitBlock(ContBB); @@ -1508,15 +1513,31 @@ void CodeGenFunction::FinallyInfo::exit(CodeGenFunction &CGF) { EHCatchScope &catchScope = cast<EHCatchScope>(*CGF.EHStack.begin()); llvm::BasicBlock *catchBB = catchScope.getHandler(0).Block; - CGF.popCatchScope(); + llvm::BasicBlock *DispatchBlock = CGF.popCatchScope(); + + llvm::CatchPadInst *CPI = nullptr; // If there are any references to the catch-all block, emit it. if (catchBB->use_empty()) { delete catchBB; } else { + SaveAndRestore RestoreCurrentFuncletPad(CGF.CurrentFuncletPad); + if (EHPersonality::get(CGF).isWasmPersonality() && DispatchBlock) { + auto *CatchSwitch = + cast<llvm::CatchSwitchInst>(DispatchBlock->getFirstNonPHIIt()); + llvm::BasicBlock *CatchStartBlock = + CatchSwitch->hasUnwindDest() ? CatchSwitch->getSuccessor(1) + : CatchSwitch->getSuccessor(0); + CPI = cast<llvm::CatchPadInst>(CatchStartBlock->getFirstNonPHIIt()); + CGF.CurrentFuncletPad = CPI; + } + CGBuilderTy::InsertPoint savedIP = CGF.Builder.saveAndClearIP(); CGF.EmitBlock(catchBB); + if (CPI) + CGF.EHStack.pushCleanup<CatchRetScope>(NormalCleanup, CPI); + llvm::Value *exn = nullptr; // If there's a begin-catch function, call it. @@ -1537,6 +1558,11 @@ void CodeGenFunction::FinallyInfo::exit(CodeGenFunction &CGF) { // Thread a jump through the finally cleanup. CGF.EmitBranchThroughCleanup(RethrowDest); + // The catchret must be emitted while the catchpad is active. The branch + // through the finally cleanup is then resolved after leaving the catchpad. + if (CPI) + CGF.PopCleanupBlock(); + CGF.Builder.restoreIP(savedIP); } diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp b/clang/lib/CodeGen/CGObjCRuntime.cpp index 442c2990d54d8..4d3c0caf3520b 100644 --- a/clang/lib/CodeGen/CGObjCRuntime.cpp +++ b/clang/lib/CodeGen/CGObjCRuntime.cpp @@ -156,15 +156,12 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, CodeGenFunction::FinallyInfo FinallyInfo; if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt()) { - if (!useFunclets) { + if (!useFunclets || IsWasm) { // 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 if (IsWasm) { - CGF.ErrorUnsupported(Finally, - "@finally is not implemented for WebAssembly"); } } @@ -267,6 +264,7 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, // br i1 %matches, label %catch, label %catch2 // // We save the old funclet pad here before we traverse each catch handler. + llvm::Instruction *SavedFuncletPad = CGF.CurrentFuncletPad; SaveAndRestore RestoreCurrentFuncletPad(CGF.CurrentFuncletPad); llvm::BasicBlock *WasmCatchStartBlock = nullptr; llvm::CatchPadInst *CPI = nullptr; @@ -356,8 +354,10 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, CGF.Builder.restoreIP(SavedIP); // Pop out of the finally. - if (!useFunclets && S.getFinallyStmt()) + if ((!useFunclets || IsWasm) && S.getFinallyStmt()) { + CGF.CurrentFuncletPad = SavedFuncletPad; FinallyInfo.exit(CGF); + } if (Cont.isValid()) CGF.EmitBlock(Cont.getBlock()); diff --git a/clang/test/CodeGenObjC/gnustep2-wasm32-arc-rethrow.m b/clang/test/CodeGenObjC/gnustep2-wasm32-arc-rethrow.m new file mode 100644 index 0000000000000..7ab24ddee96ef --- /dev/null +++ b/clang/test/CodeGenObjC/gnustep2-wasm32-arc-rethrow.m @@ -0,0 +1,17 @@ +// 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 %s +__attribute__((objc_root_class)) @interface Object @end +extern void mayThrowObjC(); + +int arcRethrow(Object *value) { + @try { + mayThrowObjC(); + } @catch (...) { + @throw; + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @arcRethrow +// CHECK: [[CATCHPAD:%.*]] = catchpad within +// CHECK: invoke void @__cxa_rethrow(){{.*}}[ "funclet"(token [[CATCHPAD]]) ] +// CHECK-NEXT: to label %unreachable unwind label diff --git a/clang/test/CodeGenObjC/gnustep2-wasm32-eh.m b/clang/test/CodeGenObjC/gnustep2-wasm32-eh.m new file mode 100644 index 0000000000000..9b4482be569a0 --- /dev/null +++ b/clang/test/CodeGenObjC/gnustep2-wasm32-eh.m @@ -0,0 +1,35 @@ +// 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 %s + +void may_throw(void) { + @throw (id) 1; +} + +int main(void) { + int retval = 0; + @try { + may_throw(); + // CHECK: invoke void @may_throw() + // CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[CATCH_DISPATCH:.*]] + } + // Check that the dispatch block has been emitted correctly. + // CHECK: [[CATCH_DISPATCH]]: + // CHECK-NEXT: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCH_START:.*]] unwind to caller + + + // The native WASM EH uses the new exception handling IR instructions + // (catchswitch, catchpad, etc.) that are also used when targeting Windows MSVC. + // For SEH, we emit a catchpad instruction for each catch statement. On WASM, we + // merge all catch statements into one big catch block. + + // CHECK: catchpad within %[[CATCHSWITCH]] [ptr @__objc_id_type_info, ptr null] + + // We use the cxa functions instead of objc_{begin,end}_catch. + // CHECK: call ptr @__cxa_begin_catch + @catch(id a) { + retval = 1; + } + @catch(...) { + retval = 2; + } + return retval; +} diff --git a/clang/test/CodeGenObjC/gnustep2-wasm32-empty-catch.m b/clang/test/CodeGenObjC/gnustep2-wasm32-empty-catch.m new file mode 100644 index 0000000000000..1391c94354d73 --- /dev/null +++ b/clang/test/CodeGenObjC/gnustep2-wasm32-empty-catch.m @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -triple wasm32-unknown-emscripten -fobjc-runtime=gnustep-2.2 -fexceptions -fobjc-exceptions -exception-model=wasm -mllvm -wasm-enable-eh -emit-llvm -o - %s | FileCheck %s +__attribute__((objc_root_class)) @interface Object @end + +int emptyCatch(Object *object) { + @try { + (void)object; + } @catch (Object *caught) { + return caught != (Object *)0; + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @emptyCatch +// CHECK-NOT: catchswitch +// CHECK: ret i32 diff --git a/clang/test/CodeGenObjC/gnustep2-wasm32-finally.m b/clang/test/CodeGenObjC/gnustep2-wasm32-finally.m new file mode 100644 index 0000000000000..33766c842b378 --- /dev/null +++ b/clang/test/CodeGenObjC/gnustep2-wasm32-finally.m @@ -0,0 +1,176 @@ +// RUN: %clang_cc1 -target-feature +exception-handling -triple wasm32-unknown-emscripten -fobjc-runtime=gnustep-2.2 -fexceptions -fobjc-exceptions -exception-model=wasm -mllvm -wasm-enable-eh -emit-llvm -o - %s | FileCheck %s + +__attribute__((objc_root_class)) @interface Object @end +extern void mayThrowObjC(); + +int finallySimple(Object *object) { + int value = 0; + @try { + mayThrowObjC(); + value = 1; + } @catch (...) { + value = 2; + } @finally { + value += object != (Object *)0; + } + return value; +} + +int finallyNoCatch(Object *object) { + int value = 0; + @try { + mayThrowObjC(); + value = 1; + } @finally { + value += object != (Object *)0; + } + return value; +} + +int throwInTryFinally(Object *object) { + int value = 0; + @try { + @throw (id)0; + } @catch (...) { + value = 1; + } @finally { + value += object != (Object *)0; + } + return value; +} + +int throwInCatchFinally(Object *object) { + @try { + mayThrowObjC(); + } @catch (...) { + @throw; + } @finally { + (void)object; + } +} + +int throwInFinally(Object *object) { + @try { + mayThrowObjC(); + } @catch (...) { + } @finally { + @throw object; + } +} + +int nestedTryCatchFinally(Object *object) { + int value = 0; + @try { + @try { + mayThrowObjC(); + } @catch (...) { + value = 1; + } @finally { + value += 2; + } + } @catch (...) { + value = 3; + } @finally { + value += object != (Object *)0; + } + return value; +} + +int gotoOutFinally(Object *object) { + int value = 0; + @try { + value = 1; + goto done; + } @finally { + value += object != (Object *)0; + } +done: + return value; +} + +int gotoOutCatchFinally(Object *object) { + int value = 0; + @try { + mayThrowObjC(); + } @catch (...) { + value = 1; + goto done; + } @finally { + value += object != (Object *)0; + } +done: + return value; +} + +// CHECK-LABEL: define{{.*}} @finallySimple +// CHECK: invoke void @mayThrowObjC() +// CHECK-NEXT: to label %{{.*}} unwind label %{{.*}} +// CHECK: catch.dispatch: +// CHECK: catchswitch within none +// CHECK: catch.start: +// CHECK: [[TRY_PAD:%.*]] = catchpad within +// CHECK: invoke.cont: +// CHECK: br label %cleanup +// CHECK: cleanup: +// CHECK: [[FINALLY_SHOULD_THROW:%.*]] = load i1, ptr {{.*}}finally.for-eh +// CHECK: br i1 [[FINALLY_SHOULD_THROW]], label %finally.rethrow, label %finally.cont +// CHECK: finally.rethrow: +// CHECK-NEXT: invoke void @__cxa_rethrow() +// CHECK-NEXT: to label %unreachable unwind label %ehcleanup +// CHECK: catch: +// CHECK: call ptr @__cxa_begin_catch(ptr {{.*}}){{.*}}[ "funclet"(token [[TRY_PAD]]) ] +// CHECK: invoke void @__cxa_end_catch() [ "funclet"(token [[TRY_PAD]]) ] +// CHECK: catchswitch within none +// CHECK: catch.start{{.*}}: +// CHECK: [[FINALLY_PAD:%.*]] = catchpad within +// CHECK: catchret from [[TRY_PAD]] +// CHECK: finally.catchall: +// CHECK: call ptr @__cxa_begin_catch(ptr {{.*}}){{.*}}[ "funclet"(token [[FINALLY_PAD]]) ] +// CHECK: catchret from [[FINALLY_PAD]] +// CHECK: ehcleanup: +// CHECK-NEXT: [[CLEANUP_PAD:%.*]] = cleanuppad within none [] +// CHECK: invoke void @__cxa_end_catch() [ "funclet"(token [[CLEANUP_PAD]]) ] + +// CHECK-LABEL: define{{.*}} @finallyNoCatch +// CHECK: invoke void @mayThrowObjC() +// CHECK: catchswitch within none +// CHECK: catchpad within +// CHECK: finally.catchall: +// CHECK: call ptr @__cxa_begin_catch +// CHECK: catchret from +// CHECK: br label %cleanup + +// CHECK-LABEL: define{{.*}} @throwInTryFinally +// CHECK: invoke void @objc_exception_throw +// CHECK: catchpad within +// CHECK: finally.catchall: +// CHECK: call ptr @__cxa_begin_catch +// CHECK: invoke void @__cxa_rethrow() + +// CHECK-LABEL: define{{.*}} @throwInCatchFinally +// CHECK: catchswitch within none +// CHECK: invoke void @__cxa_rethrow() +// CHECK: finally.catchall: +// CHECK: catchret from + +// CHECK-LABEL: define{{.*}} @throwInFinally +// CHECK: invoke void @objc_exception_throw +// CHECK: catchswitch within none +// CHECK: finally.catchall: + +// CHECK-LABEL: define{{.*}} @nestedTryCatchFinally +// CHECK: catchswitch within none +// CHECK: catchswitch within none +// CHECK: finally.catchall{{.*}}: +// CHECK: finally.catchall{{.*}}: + +// CHECK-LABEL: define{{.*}} @gotoOutFinally +// CHECK: store i32 1, ptr %value +// CHECK: store i32 3, ptr %cleanup.dest.slot +// CHECK: br i1 {{.*}}, label %finally.rethrow, label %finally.cont +// CHECK: ret i32 + +// CHECK-LABEL: define{{.*}} @gotoOutCatchFinally +// CHECK: catchswitch within none +// CHECK: catchret from +// CHECK: br label %cleanup diff --git a/clang/test/CodeGenObjC/wasm32-eh-arc.m b/clang/test/CodeGenObjC/wasm32-eh-arc.m deleted file mode 100644 index 1100e3bfbf28d..0000000000000 --- a/clang/test/CodeGenObjC/wasm32-eh-arc.m +++ /dev/null @@ -1,29 +0,0 @@ -// 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 deleted file mode 100644 index e35d81de51bf5..0000000000000 --- a/clang/test/CodeGenObjC/wasm32-eh.m +++ /dev/null @@ -1,202 +0,0 @@ -// 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 -@end - -@interface ExceptionA : Object -@end - -@interface ExceptionB : Object -@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 label %[[CATCH_ALL:.*]] -// CHECK: [[INVOKE_CONT]]: -// CHECK: br label %[[EH_CONT:.*]] -// CHECK: [[CATCH_ALL]]: -// CHECK: call ptr @__cxa_begin_catch -// CHECK: call void @__cxa_end_catch() -// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST:.*]] -// CHECK: [[CATCHRET_DEST]]: -// CHECK-NEXT: br label %return - -int twoTypedHandlers(void) { - @try { - mayThrow(); - } @catch (ExceptionA *exception) { - return 1; - } @catch (ExceptionB *exception) { - return 2; - } - return 0; -} - -// CHECK-LABEL: define{{.*}} @twoTypedHandlers -// 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 @__objc_eh_typeinfo_ExceptionA, ptr @__objc_eh_typeinfo_ExceptionB] -// CHECK: br i1 %{{.*}}, label %[[CATCH:.*]], label %[[CATCH_FALLTHROUGH:.*]] -// CHECK: [[CATCH_FALLTHROUGH]]: -// CHECK: br i1 %{{.*}}, label %[[CATCH2:.*]], label %[[RETHROW:.*]] -// CHECK: [[RETHROW]]: -// CHECK-NEXT: call void @llvm.wasm.rethrow() -// CHECK-NEXT: unreachable -// CHECK: [[INVOKE_CONT]]: -// CHECK: br label %[[EH_CONT:.*]] -// CHECK: [[EH_CONT]]: -// CHECK: br label %return -// CHECK: [[CATCH]]: -// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST:.*]] -// CHECK: [[CATCHRET_DEST]]: -// CHECK-NEXT: br label %return -// CHECK: [[CATCH2]]: -// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST2:.*]] -// CHECK: [[CATCHRET_DEST2]]: -// CHECK-NEXT: br label %return - -int typedHandlerAndCatchAll(void) { - @try { - mayThrow(); - } @catch (ExceptionA *exception) { - return 1; - } @catch (...) { - return 2; - } - return 0; -} - -// CHECK-LABEL: define{{.*}} @typedHandlerAndCatchAll -// 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 @__objc_eh_typeinfo_ExceptionA, ptr null] -// CHECK: br i1 %{{.*}}, label %[[CATCH:.*]], label %[[CATCH_ALL:.*]] -// CHECK: [[INVOKE_CONT]]: -// CHECK: br label %[[EH_CONT:.*]] -// CHECK: [[EH_CONT]]: -// CHECK: br label %return -// CHECK: [[CATCH]]: -// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST:.*]] -// CHECK: [[CATCHRET_DEST]]: -// CHECK-NEXT: br label %return -// CHECK: [[CATCH_ALL]]: -// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST_ALL:.*]] -// CHECK: [[CATCHRET_DEST_ALL]]: -// CHECK-NEXT: br label %return - -int nestedTryCatch(void) { - @try { - @try { - mayThrow(); - } @catch (ExceptionA *exception) { - return 1; - } - } @catch (...) { - return 2; - } - return 0; -} - -// CHECK-LABEL: define{{.*}} @nestedTryCatch -// 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 label %[[CATCH_DISPATCH1:.*]] -// CHECK: [[CATCH_START]]: -// CHECK-NEXT: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr @__objc_eh_typeinfo_ExceptionA] -// CHECK: br i1 %{{.*}}, label %[[CATCH:.*]], label %[[RETHROW:.*]] -// CHECK: [[RETHROW]]: -// CHECK: invoke void @llvm.wasm.rethrow(){{.*}}[ "funclet"(token [[CATCHPAD]]) ] -// CHECK-NEXT: to label %[[UNREACHABLE:.*]] unwind label %[[CATCH_DISPATCH1]] -// CHECK: [[CATCH_DISPATCH1]]: -// CHECK-NEXT: [[CATCHSWITCH1:%.*]] = catchswitch within none [label %[[CATCH_START2:.*]]] unwind to caller -// CHECK: [[CATCH_START2]]: -// CHECK-NEXT: [[CATCHPAD1:%.*]] = catchpad within [[CATCHSWITCH1]] [ptr null] -// CHECK: [[INVOKE_CONT]]: -// CHECK: br label %[[EH_CONT:.*]] -// CHECK: [[EH_CONT]]: -// CHECK: br label %[[EH_CONT2:.*]] -// CHECK: [[EH_CONT2]]: -// CHECK: br label %return -// CHECK: [[CATCH]]: -// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST:.*]] -// CHECK: [[CATCHRET_DEST]]: -// CHECK-NEXT: br label %return -// CHECK: [[CATCH_ALL:.*]]: -// CHECK: catchret from [[CATCHPAD1]] to label %[[CATCHRET_DEST_ALL:.*]] -// CHECK: [[CATCHRET_DEST_ALL]]: -// CHECK-NEXT: br label %return - -int emptyCatch(void) { - @try { - mayThrow(); - } @catch (ExceptionA *exception) { - } - return 0; -} - -// CHECK-LABEL: define{{.*}} @emptyCatch -// 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 @__objc_eh_typeinfo_ExceptionA] -// 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 %[[EH_CONT:.*]] -// CHECK: [[EH_CONT]]: -// CHECK: [[CATCH]]: -// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST:.*]] -// CHECK: [[CATCHRET_DEST]]: -// CHECK-NEXT: br label %[[EH_CONT]] - -int explicitRethrow(void) { - @try { - mayThrow(); - } @catch (...) { - @throw; - } - return 0; -} - -// CHECK-LABEL: define{{.*}} @explicitRethrow -// CHECK: invoke void @mayThrow() -// CHECK-NEXT: to label %{{.*}} 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 label %[[CATCH_ALL:.*]] -// CHECK: [[CATCH_ALL]]: -// CHECK: invoke void @__cxa_rethrow(){{.*}}[ "funclet"(token [[CATCHPAD]]) ] -// CHECK-NEXT: to label %[[UNREACHABLE:.*]] unwind label %{{.*}} -// CHECK: [[UNREACHABLE]]: -// CHECK-NEXT: unreachable diff --git a/clang/test/CodeGenObjCXX/gnustep2-wasm32-arc-cxx-catch.mm b/clang/test/CodeGenObjCXX/gnustep2-wasm32-arc-cxx-catch.mm new file mode 100644 index 0000000000000..846b13afc8549 --- /dev/null +++ b/clang/test/CodeGenObjCXX/gnustep2-wasm32-arc-cxx-catch.mm @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -target-feature +exception-handling -triple wasm32-unknown-emscripten -fobjc-runtime=gnustep-2.2 -fobjc-arc -fexceptions -fobjc-exceptions -fcxx-exceptions -exception-model=wasm -mllvm -wasm-enable-eh -emit-llvm -o - %s | FileCheck %s + +__attribute__((objc_root_class)) @interface Object @end +extern void mayThrowCXX(); + +int arcObjectAcrossCxxCatch(Object *object) { + try { + Object *local = object; + mayThrowCXX(); + (void)local; + } catch (...) { + return object != (Object *)0; + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @_Z23arcObjectAcrossCxxCatchP6Object +// CHECK: [[CATCHPAD:%.*]] = catchpad within +// CHECK: call void @__cxa_end_catch() [ "funclet"(token [[CATCHPAD]]) ] +// CHECK: catchret from [[CATCHPAD]] +// CHECK: call void @llvm.objc.storeStrong(ptr {{.*}}, ptr null) diff --git a/clang/test/CodeGenObjCXX/gnustep2-wasm32-cxx-destructor.mm b/clang/test/CodeGenObjCXX/gnustep2-wasm32-cxx-destructor.mm new file mode 100644 index 0000000000000..104937143c639 --- /dev/null +++ b/clang/test/CodeGenObjCXX/gnustep2-wasm32-cxx-destructor.mm @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -target-feature +exception-handling -triple wasm32-unknown-emscripten -fobjc-runtime=gnustep-2.2 -fexceptions -fobjc-exceptions -fcxx-exceptions -exception-model=wasm -mllvm -wasm-enable-eh -emit-llvm -o - %s | FileCheck %s + +struct Guard { + ~Guard() noexcept(false); +}; + +extern void mayThrowCXX(); + +int cxxDestructorsAroundCatch() { + try { + Guard guard; + mayThrowCXX(); + } catch (...) { + Guard caught; + return 1; + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @_Z25cxxDestructorsAroundCatchv +// CHECK: [[TRY_CLEANUP:%.*]] = cleanuppad within none [] +// CHECK: invoke{{.*}} @_ZN5GuardD1Ev{{.*}}[ "funclet"(token [[TRY_CLEANUP]]) ] +// CHECK: cleanupret from [[TRY_CLEANUP]] +// CHECK: [[CATCHPAD:%.*]] = catchpad within +// CHECK: invoke{{.*}} @_ZN5GuardD1Ev{{.*}}[ "funclet"(token [[CATCHPAD]]) ] +// CHECK: catchret from [[CATCHPAD]] +// CHECK: [[CATCH_CLEANUP:%.*]] = cleanuppad within [[CATCHPAD]] [] +// CHECK: cleanupret from [[CATCH_CLEANUP]] diff --git a/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm b/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm deleted file mode 100644 index 4b989e6122fdd..0000000000000 --- a/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm +++ /dev/null @@ -1,66 +0,0 @@ -// RUN: %clang_cc1 -target-feature +exception-handling -triple wasm32-unknown-emscripten -fobjc-runtime=gnustep-2.2 -fexceptions -fobjc-exceptions -fcxx-exceptions -exception-model=wasm -mllvm -wasm-enable-eh -emit-llvm -o - %s | FileCheck --enable-var-scope %s - -struct ThrowingDestructor { - ~ThrowingDestructor() noexcept(false); -}; - -extern void mayThrowCXX(); - -int cxxDestructorsAroundCatch() { - try { - ThrowingDestructor guard; - mayThrowCXX(); - } catch (...) { - ThrowingDestructor caught; - return 1; - } - return 0; -} - -// CHECK-LABEL: define{{.*}} @_Z25cxxDestructorsAroundCatchv -// CHECK: invoke void @_Z{{[0-9]+}}mayThrowCXXv() -// CHECK: [[CLEANUPPAD:%.*]] = cleanuppad within none [] -// CHECK: invoke{{.*}} @_ZN18ThrowingDestructorD1Ev{{.*}}[ "funclet"(token [[CLEANUPPAD]]) ] -// CHECK: cleanupret from [[CLEANUPPAD]] unwind label %{{.*}} -// CHECK: [[CATCHSWITCH:%.*]] = catchswitch within none [label %{{.*}}] unwind to caller -// CHECK: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr null] -// CHECK: invoke{{.*}} @_ZN18ThrowingDestructorD1Ev{{.*}}[ "funclet"(token [[CATCHPAD]]) ] -// CHECK: catchret from [[CATCHPAD]] to label %{{.*}} - -__attribute__((objc_root_class)) @interface Object -@end - -extern void mayThrowObjC(); - -int combinedCxxObjcEH() { - @try { - try { - mayThrowCXX(); - } catch (Object *exception) { - @try { - mayThrowObjC(); - } @catch (Object *nestedException) { - return 1; - } - return 2; - } catch (int value) { - return value; - } - } @catch (...) { - return 3; - } - return 0; -} - -// CHECK-LABEL: define{{.*}} @_Z{{[0-9]+}}combinedCxxObjcEHv -// CHECK: invoke void @_Z{{[0-9]+}}mayThrowCXXv() -// CHECK: [[CATCHSWITCH:%.*]] = catchswitch within none [label %{{.*}}] unwind label %{{.*}} -// CHECK: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr @__objc_eh_typeinfo_Object, ptr @_ZTIi] -// CHECK: invoke void @_Z{{[0-9]+}}mayThrowObjCv() [ "funclet"(token [[CATCHPAD]]) ] -// CHECK: [[NESTED_SWITCH:%.*]] = catchswitch within [[CATCHPAD]] [label %{{.*}}] unwind label %{{.*}} -// CHECK: [[NESTED_PAD:%.*]] = catchpad within [[NESTED_SWITCH]] [ptr @__objc_eh_typeinfo_Object] -// CHECK: invoke void @llvm.wasm.rethrow(){{.*}}[ "funclet"(token [[NESTED_PAD]]) ] -// CHECK: catchret from [[CATCHPAD]] to label %{{.*}} -// CHECK: invoke void @llvm.wasm.rethrow(){{.*}}[ "funclet"(token [[CATCHPAD]]) ] -// CHECK: [[OUTER_SWITCH:%.*]] = catchswitch within none [label %{{.*}}] unwind to caller -// CHECK: [[OUTER_PAD:%.*]] = catchpad within [[OUTER_SWITCH]] [ptr null] diff --git a/clang/test/SemaObjC/wasm-try-goto.m b/clang/test/SemaObjC/wasm-try-goto.m new file mode 100644 index 0000000000000..2b6535feeb791 --- /dev/null +++ b/clang/test/SemaObjC/wasm-try-goto.m @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -triple wasm32-unknown-emscripten -fobjc-runtime=gnustep-2.2 -fobjc-exceptions -fsyntax-only -verify %s + +void gotoIntoTry(void) { + goto try_label; // expected-error{{cannot jump from this goto statement to its label}} + @try { // expected-note{{jump bypasses initialization of @try block}} + try_label: + ; + } @finally { + } +} + +void gotoIntoCatch(void) { + goto catch_label; // expected-error{{cannot jump from this goto statement to its label}} + @try { + } @catch (...) { // expected-note{{jump bypasses initialization of @catch block}} + catch_label: + ; + } @finally { + } +} + +void gotoIntoFinally(void) { + goto finally_label; // expected-error{{cannot jump from this goto statement to its label}} + @try { + } @finally { // expected-note{{jump bypasses initialization of @finally block}} + finally_label: + ; + } +} + +void gotoOutOfFinally(void) { + @try { + goto done; + } @finally { + } +done: + ; +} + +void gotoOutOfCatch(void) { + @try { + } @catch (...) { + goto done; + } @finally { + } +done: + ; +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
