https://github.com/HendrikHuebner updated https://github.com/llvm/llvm-project/pull/218967
From ff84a9a27ac676d6d57e3c2a6c4cfdd1c168ac6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hendrik=20H=C3=BCbner?= <[email protected]> Date: Wed, 26 Aug 2026 19:44:16 +0200 Subject: [PATCH 1/7] [CodeGen][ObjC] Implement @finally for WebAssembly EH --- clang/lib/CodeGen/CGException.cpp | 24 ++ clang/lib/CodeGen/CGObjCRuntime.cpp | 13 +- .../CodeGenObjC/gnustep2-wasm32-finally.m | 294 ++++++++++++++++++ clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm | 155 +++++++++ 4 files changed, 479 insertions(+), 7 deletions(-) create mode 100644 clang/test/CodeGenObjC/gnustep2-wasm32-finally.m diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index bc09fe767de45..ffa6af9a55ce6 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -1506,15 +1506,34 @@ void CodeGenFunction::FinallyInfo::exit(CodeGenFunction &CGF) { EHCatchScope &catchScope = cast<EHCatchScope>(*CGF.EHStack.begin()); llvm::BasicBlock *catchBB = catchScope.getHandler(0).Block; + llvm::BasicBlock *DispatchBlock = nullptr; + if (catchScope.hasEHBranches()) + DispatchBlock = catchScope.getCachedEHDispatchBlock(); 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. @@ -1535,6 +1554,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 099622f690678..e539e905a5d00 100644 --- a/clang/lib/CodeGen/CGObjCRuntime.cpp +++ b/clang/lib/CodeGen/CGObjCRuntime.cpp @@ -150,22 +150,18 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, if (S.getNumCatchStmts()) 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 (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt()) { - if (!useFunclets) { + if (!IsMSVC) { // 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"); - } else if (IsMSVC) { + } else { CodeGenFunction HelperCGF(CGM, /*suppressNewContext=*/true); if (!CGF.CurSEHParent) CGF.CurSEHParent = cast<NamedDecl>(CGF.CurFuncDecl); @@ -224,6 +220,7 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, } // 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; @@ -312,8 +309,10 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, CGF.Builder.restoreIP(SavedIP); // Pop out of the finally. - if (!useFunclets && S.getFinallyStmt()) + if (!IsMSVC && S.getFinallyStmt()) { + CGF.CurrentFuncletPad = SavedFuncletPad; FinallyInfo.exit(CGF); + } if (Cont.isValid()) CGF.EmitBlock(Cont.getBlock()); diff --git a/clang/test/CodeGenObjC/gnustep2-wasm32-finally.m b/clang/test/CodeGenObjC/gnustep2-wasm32-finally.m new file mode 100644 index 0000000000000..2c3ca912b595f --- /dev/null +++ b/clang/test/CodeGenObjC/gnustep2-wasm32-finally.m @@ -0,0 +1,294 @@ +// REQUIRES: webassembly-registered-target +// 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(); + +void emptyFinally(void) { + @try { + mayThrowObjC(); + } @finally { + } +} + +// CHECK-LABEL: define{{.*}} @emptyFinally +// CHECK: catch.dispatch: +// CHECK-NEXT: [[EMPTY_SWITCH:%.*]] = catchswitch within none [label %catch.start] unwind to caller +// CHECK: catch.start: +// CHECK-NEXT: [[EMPTY_PAD:%.*]] = catchpad within [[EMPTY_SWITCH]] [ptr null] +// CHECK: br label %finally.catchall +// CHECK: {{^}}cleanup: +// CHECK: %finally.shouldthrow = load i1, ptr %finally.for-eh +// CHECK-NEXT: br i1 %finally.shouldthrow, label %finally.rethrow, label %finally.cont +// CHECK: finally.rethrow: +// CHECK-NEXT: invoke void @__cxa_rethrow() +// CHECK: finally.cont: +// CHECK: finally.catchall: +// CHECK-NEXT: %exn = load ptr, ptr %exn.slot +// CHECK-NEXT: %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn) +// CHECK-NEXT: store i1 true, ptr %finally.for-eh +// CHECK-NEXT: store i32 2, ptr %cleanup.dest.slot +// CHECK-NEXT: catchret from [[EMPTY_PAD]] to label %{{.*}} +// CHECK: ehcleanup: +// CHECK-NEXT: [[EMPTY_CLEANUP:%.*]] = cleanuppad within none [] +// CHECK-NEXT: %finally.endcatch = load i1, ptr %finally.for-eh +// CHECK-NEXT: br i1 %finally.endcatch, label %{{.*}}, label %finally.cleanup.cont +// CHECK: finally.cleanup.cont: +// CHECK-NEXT: cleanupret from [[EMPTY_CLEANUP]] unwind to caller + +int finallySimple(Object *object) { + int value = 0; + @try { + mayThrowObjC(); + value = 1; + } @catch (...) { + value = 2; + } @finally { + value += object != (Object *)0; + } + return value; +} + +// CHECK-LABEL: define{{.*}} @finallySimple +// CHECK: invoke void @mayThrowObjC() +// CHECK-NEXT: to label %invoke.cont unwind label %catch.dispatch +// CHECK: catch.dispatch: +// CHECK-NEXT: [[SIMPLE_SWITCH:%.*]] = catchswitch within none [label %catch.start] unwind label %catch.dispatch2 +// CHECK: catch.start: +// CHECK-NEXT: [[SIMPLE_PAD:%.*]] = catchpad within [[SIMPLE_SWITCH]] [ptr null] +// CHECK: br label %catch +// CHECK: invoke.cont: +// CHECK-NEXT: store i32 1, ptr %value +// CHECK-NEXT: store i32 0, ptr %cleanup.dest.slot +// CHECK-NEXT: br label %cleanup +// CHECK: cleanup: +// CHECK: %add = add nsw i32 +// CHECK-NEXT: store i32 %add, ptr %value +// CHECK-NEXT: %finally.shouldthrow = load i1, ptr %finally.for-eh +// CHECK-NEXT: br i1 %finally.shouldthrow, label %finally.rethrow, label %finally.cont +// CHECK: finally.rethrow: +// CHECK-NEXT: invoke void @__cxa_rethrow() +// CHECK-NEXT: to label %unreachable unwind label %ehcleanup +// CHECK: finally.cont: +// CHECK-NEXT: store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot +// CHECK-NEXT: %cleanup.dest = load i32, ptr %cleanup.dest.slot +// CHECK-NEXT: switch i32 %cleanup.dest, label %unreachable [ +// CHECK-NEXT: i32 0, label %cleanup.cont +// CHECK-NEXT: i32 2, label %eh.cont +// CHECK-NEXT: i32 3, label %unreachable +// CHECK-NEXT: ] +// CHECK: cleanup.cont: +// CHECK-NEXT: br label %eh.cont +// CHECK: eh.cont: +// CHECK-NEXT: %{{.*}} = load i32, ptr %value +// CHECK-NEXT: ret i32 %{{.*}} +// CHECK: catch: +// CHECK-NEXT: %exn = load ptr, ptr %exn.slot +// CHECK-NEXT: %exn.adjusted = call ptr @__cxa_begin_catch(ptr %exn) +// CHECK-NEXT: store i32 2, ptr %value +// CHECK-NEXT: invoke void @__cxa_end_catch() +// CHECK-NEXT: to label %invoke.cont1 unwind label %catch.dispatch2 +// CHECK: catch.dispatch2: +// CHECK-NEXT: [[SIMPLE_FINALLY_SWITCH:%.*]] = catchswitch within none [label %catch.start3] unwind to caller +// CHECK: catch.start3: +// CHECK-NEXT: [[SIMPLE_FINALLY_PAD:%.*]] = catchpad within [[SIMPLE_FINALLY_SWITCH]] [ptr null] +// CHECK: br label %finally.catchall +// CHECK: invoke.cont1: +// CHECK-NEXT: catchret from [[SIMPLE_PAD]] to label %catchret.dest +// CHECK: catchret.dest: +// CHECK-NEXT: store i32 2, ptr %cleanup.dest.slot +// CHECK-NEXT: br label %cleanup +// CHECK: finally.catchall: +// CHECK-NEXT: %exn4 = load ptr, ptr %exn.slot +// CHECK-NEXT: %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn4) +// CHECK-NEXT: store i1 true, ptr %finally.for-eh +// CHECK-NEXT: store i32 3, ptr %cleanup.dest.slot +// CHECK-NEXT: catchret from [[SIMPLE_FINALLY_PAD]] to label %catchret.dest5 +// CHECK: catchret.dest5: +// CHECK-NEXT: br label %cleanup +// CHECK: ehcleanup: +// CHECK-NEXT: [[SIMPLE_CLEANUP:%.*]] = cleanuppad within none [] +// CHECK-NEXT: %finally.endcatch = load i1, ptr %finally.for-eh +// CHECK-NEXT: br i1 %finally.endcatch, label %finally.endcatch6, label %finally.cleanup.cont +// CHECK: finally.endcatch6: +// CHECK-NEXT: invoke void @__cxa_end_catch() +// CHECK-NEXT: to label %invoke.cont7 unwind label %terminate +// CHECK: invoke.cont7: +// CHECK-NEXT: br label %finally.cleanup.cont +// CHECK: finally.cleanup.cont: +// CHECK-NEXT: cleanupret from [[SIMPLE_CLEANUP]] unwind to caller + +int finallyNoCatch(Object *object) { + int value = 0; + @try { + mayThrowObjC(); + value = 1; + } @finally { + value += object != (Object *)0; + } + return value; +} + +// CHECK-LABEL: define{{.*}} @finallyNoCatch +// CHECK: catch.dispatch: +// CHECK-NEXT: [[NO_CATCH_SWITCH:%.*]] = catchswitch within none [label %catch.start] unwind to caller +// CHECK: catch.start: +// CHECK-NEXT: [[NO_CATCH_PAD:%.*]] = catchpad within [[NO_CATCH_SWITCH]] [ptr null] +// CHECK: br label %finally.catchall +// CHECK: {{^}}cleanup: +// CHECK: %finally.shouldthrow = load i1, ptr %finally.for-eh +// CHECK-NEXT: br i1 %finally.shouldthrow, label %finally.rethrow, label %finally.cont +// CHECK: finally.rethrow: +// CHECK-NEXT: invoke void @__cxa_rethrow() +// CHECK: finally.cont: +// CHECK: finally.catchall: +// CHECK-NEXT: %exn = load ptr, ptr %exn.slot +// CHECK-NEXT: %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn) +// CHECK-NEXT: store i1 true, ptr %finally.for-eh +// CHECK-NEXT: store i32 2, ptr %cleanup.dest.slot +// CHECK-NEXT: catchret from [[NO_CATCH_PAD]] to label %{{.*}} +// CHECK: ehcleanup: +// CHECK-NEXT: [[NO_CATCH_CLEANUP:%.*]] = cleanuppad within none [] +// CHECK-NEXT: %finally.endcatch = load i1, ptr %finally.for-eh +// CHECK-NEXT: br i1 %finally.endcatch, label %{{.*}}, label %finally.cleanup.cont +// CHECK: finally.cleanup.cont: +// CHECK-NEXT: cleanupret from [[NO_CATCH_CLEANUP]] unwind to caller + +int throwInCatchFinally(Object *object) { + @try { + mayThrowObjC(); + } @catch (...) { + @throw; + } @finally { + (void)object; + } +} + +// CHECK-LABEL: define{{.*}} @throwInCatchFinally +// CHECK: {{^}}cleanup: +// CHECK: %finally.shouldthrow = load i1, ptr %finally.for-eh +// CHECK-NEXT: br i1 %finally.shouldthrow, label %finally.rethrow, label %finally.cont +// CHECK: finally.rethrow: +// CHECK-NEXT: invoke void @__cxa_rethrow() +// CHECK: finally.cont: +// CHECK: catch: +// CHECK: invoke void @__cxa_rethrow() +// CHECK-NEXT: to label %unreachable unwind label %ehcleanup +// CHECK: catch.dispatch{{[0-9]+}}: +// CHECK-NEXT: [[CATCH_FINALLY_SWITCH:%.*]] = catchswitch within none [label %catch.start{{[0-9]+}}] unwind to caller +// CHECK: catch.start{{[0-9]+}}: +// CHECK: [[CATCH_FINALLY_PAD:%.*]] = catchpad within [[CATCH_FINALLY_SWITCH]] [ptr null] +// CHECK: br label %finally.catchall +// CHECK: finally.catchall: +// CHECK: %{{.*}} = call ptr @__cxa_begin_catch(ptr %{{.*}}) +// CHECK-NEXT: store i1 true, ptr %finally.for-eh +// CHECK-NEXT: store i32 3, ptr %cleanup.dest.slot +// CHECK-NEXT: catchret from [[CATCH_FINALLY_PAD]] to label %{{.*}} +// CHECK: ehcleanup{{[0-9]+}}: +// CHECK-NEXT: [[CATCH_FINALLY_CLEANUP:%.*]] = cleanuppad within none [] +// CHECK: finally.cleanup.cont: +// CHECK-NEXT: cleanupret from [[CATCH_FINALLY_CLEANUP]] unwind to caller + +int throwInFinally(Object *object) { + @try { + mayThrowObjC(); + } @finally { + @throw object; + } +} + +// CHECK-LABEL: define{{.*}} @throwInFinally +// CHECK: cleanup: +// CHECK: invoke void @objc_exception_throw(ptr %{{.*}}) +// CHECK-NEXT: to label %invoke.cont1 unwind label %ehcleanup +// CHECK: invoke.cont1: +// CHECK-NEXT: unreachable +// CHECK: finally.catchall: +// CHECK: catchret from %{{.*}} to label %{{.*}} +// CHECK: ehcleanup: +// CHECK: [[THROW_CLEANUP:%.*]] = cleanuppad within none [] +// CHECK: finally.cleanup.cont: +// CHECK-NEXT: cleanupret from [[THROW_CLEANUP]] unwind to caller +// CHECK-NOT: finally.rethrow: + +int throwInFinallyNoException(Object *object) { + @try { + } @finally { + @throw object; + } +} + +// CHECK-LABEL: define{{.*}} @throwInFinallyNoException +// CHECK: entry: +// CHECK: invoke void @objc_exception_throw(ptr %{{.*}}) +// CHECK-NEXT: to label %invoke.cont unwind label %ehcleanup +// CHECK: invoke.cont: +// CHECK-NEXT: unreachable +// CHECK: ehcleanup: +// CHECK-NEXT: [[NO_EXCEPTION_CLEANUP:%.*]] = cleanuppad within none [] +// CHECK-NEXT: %finally.endcatch = load i1, ptr %finally.for-eh +// CHECK-NEXT: br i1 %finally.endcatch, label %{{.*}}, label %finally.cleanup.cont +// CHECK: finally.cleanup.cont: +// CHECK-NEXT: cleanupret from [[NO_EXCEPTION_CLEANUP]] unwind to caller +// CHECK-NOT: catchswitch within none +// CHECK-NOT: finally.rethrow: + +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; +} + +// CHECK-LABEL: define{{.*}} @nestedTryCatchFinally +// CHECK: {{^}}cleanup: +// CHECK: %finally.shouldthrow = load i1, ptr %finally.for-eh1 +// CHECK-NEXT: br i1 %finally.shouldthrow, label %finally.rethrow, label %finally.cont +// CHECK: finally.rethrow: +// CHECK-NEXT: invoke void @__cxa_rethrow() +// CHECK: {{^}}cleanup{{[0-9]+}}: +// CHECK: %finally.shouldthrow{{[0-9]+}} = load i1, ptr %finally.for-eh +// CHECK-NEXT: br i1 %finally.shouldthrow{{[0-9]+}}, label %finally.rethrow{{[0-9]+}}, label %finally.cont{{[0-9]+}} +// CHECK: finally.rethrow{{[0-9]+}}: +// CHECK-NEXT: invoke void @__cxa_rethrow() +// CHECK: finally.catchall: +// CHECK: catchret from %{{.*}} to label %{{.*}} +// CHECK: finally.catchall{{[0-9]+}}: +// CHECK: catchret from %{{.*}} to label %{{.*}} + +int gotoOutFinally(Object *object) { + int value = 0; + @try { + value = 1; + goto done; + } @finally { + value += object != (Object *)0; + } +done: + return value; +} + +// CHECK-LABEL: define{{.*}} @gotoOutFinally +// CHECK: entry: +// CHECK: store i32 1, ptr %value +// CHECK-NEXT: store i32 3, ptr %cleanup.dest.slot +// CHECK: %finally.shouldthrow = load i1, ptr %finally.for-eh +// CHECK-NEXT: br i1 %finally.shouldthrow, label %finally.rethrow, label %finally.cont +// CHECK: finally.rethrow: +// CHECK-NEXT: invoke void @__cxa_rethrow() +// CHECK: finally.cont: +// CHECK: i32 3, label %done +// CHECK: ehcleanup: +// CHECK: finally.cleanup.cont: +// CHECK-NEXT: cleanupret from %{{.*}} unwind to caller diff --git a/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm b/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm index 3af09f7055b98..958c656bbf1fd 100644 --- a/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm +++ b/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm @@ -33,6 +33,161 @@ int cxxDestructorsAroundCatch() { extern void mayThrowObjC(); +int cleanupInTryFinally() { + @try { + ThrowingDestructor object; + mayThrowObjC(); + } @finally { + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @_Z{{[0-9]+}}cleanupInTryFinallyv +// CHECK: invoke void @_Z{{[0-9]+}}mayThrowObjCv() +// CHECK-NEXT: to label %invoke.cont unwind label %ehcleanup +// CHECK: invoke.cont: +// CHECK-NEXT: %{{.*}} = invoke noundef ptr @_ZN18ThrowingDestructorD1Ev{{.*}}%object +// CHECK-NEXT: to label %invoke.cont1 unwind label %catch.dispatch +// CHECK: invoke.cont1: +// CHECK-NEXT: store i32 0, ptr %cleanup.dest.slot +// CHECK-NEXT: br label %cleanup +// CHECK: cleanup: +// CHECK-NEXT: %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot +// CHECK-NEXT: %finally.shouldthrow = load i1, ptr %finally.for-eh +// CHECK-NEXT: br i1 %finally.shouldthrow, label %finally.rethrow, label %finally.cont +// CHECK: finally.rethrow: +// CHECK-NEXT: invoke void @__cxa_rethrow() +// CHECK-NEXT: to label %unreachable unwind label %ehcleanup4 +// CHECK: finally.cont: +// CHECK-NEXT: store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot +// CHECK-NEXT: %cleanup.dest = load i32, ptr %cleanup.dest.slot +// CHECK-NEXT: switch i32 %cleanup.dest, label %unreachable [ +// CHECK-NEXT: i32 0, label %cleanup.cont +// CHECK-NEXT: i32 2, label %unreachable +// CHECK-NEXT: ] +// CHECK: cleanup.cont: +// CHECK-NEXT: ret i32 0 +// CHECK: ehcleanup: +// CHECK-NEXT: [[TRY_CLEANUP:%.*]] = cleanuppad within none [] +// CHECK-NEXT: %{{.*}} = invoke noundef ptr @_ZN18ThrowingDestructorD1Ev{{.*}}[ "funclet"(token [[TRY_CLEANUP]]) ] +// CHECK-NEXT: to label %invoke.cont2 unwind label %terminate +// CHECK: invoke.cont2: +// CHECK-NEXT: cleanupret from [[TRY_CLEANUP]] unwind label %catch.dispatch +// CHECK: catch.dispatch: +// CHECK-NEXT: [[TRY_SWITCH:%.*]] = catchswitch within none [label %catch.start] unwind to caller +// CHECK: catch.start: +// CHECK-NEXT: [[TRY_PAD:%.*]] = catchpad within [[TRY_SWITCH]] [ptr null] +// CHECK: br label %finally.catchall +// CHECK: finally.catchall: +// CHECK-NEXT: %exn = load ptr, ptr %exn.slot +// CHECK-NEXT: %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn) +// CHECK-NEXT: store i1 true, ptr %finally.for-eh +// CHECK-NEXT: store i32 2, ptr %cleanup.dest.slot +// CHECK-NEXT: catchret from [[TRY_PAD]] to label %catchret.dest +// CHECK: catchret.dest: +// CHECK-NEXT: br label %cleanup +// CHECK: ehcleanup4: +// CHECK-NEXT: [[TRY_FINALLY_CLEANUP:%.*]] = cleanuppad within none [] +// CHECK-NEXT: %finally.endcatch = load i1, ptr %finally.for-eh +// CHECK-NEXT: br i1 %finally.endcatch, label %finally.endcatch5, label %finally.cleanup.cont +// CHECK: finally.endcatch5: +// CHECK-NEXT: invoke void @__cxa_end_catch() +// CHECK-NEXT: to label %invoke.cont6 unwind label %terminate7 +// CHECK: invoke.cont6: +// CHECK-NEXT: br label %finally.cleanup.cont +// CHECK: finally.cleanup.cont: +// CHECK-NEXT: cleanupret from [[TRY_FINALLY_CLEANUP]] unwind to caller + +int cleanupInCatchFinally() { + @try { + mayThrowObjC(); + } @catch (...) { + ThrowingDestructor object; + return 1; + } @finally { + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @_Z{{[0-9]+}}cleanupInCatchFinallyv +// CHECK: invoke void @_Z{{[0-9]+}}mayThrowObjCv() +// CHECK-NEXT: to label %invoke.cont unwind label %catch.dispatch +// CHECK: catch.dispatch: +// CHECK-NEXT: [[CATCH_SWITCH:%.*]] = catchswitch within none [label %catch.start] unwind label %catch.dispatch4 +// CHECK: catch.start: +// CHECK-NEXT: [[CATCH_PAD:%.*]] = catchpad within [[CATCH_SWITCH]] [ptr null] +// CHECK: br label %catch +// CHECK: invoke.cont: +// CHECK-NEXT: store i32 0, ptr %cleanup.dest.slot +// CHECK-NEXT: br label %cleanup +// CHECK: cleanup: +// CHECK-NEXT: %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot +// CHECK-NEXT: %finally.shouldthrow = load i1, ptr %finally.for-eh +// CHECK-NEXT: br i1 %finally.shouldthrow, label %finally.rethrow, label %finally.cont +// CHECK: finally.rethrow: +// CHECK-NEXT: invoke void @__cxa_rethrow() +// CHECK-NEXT: to label %unreachable unwind label %ehcleanup8 +// CHECK: finally.cont: +// CHECK-NEXT: store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot +// CHECK-NEXT: %cleanup.dest = load i32, ptr %cleanup.dest.slot +// CHECK-NEXT: switch i32 %cleanup.dest, label %unreachable [ +// CHECK-NEXT: i32 0, label %cleanup.cont +// CHECK-NEXT: i32 1, label %return +// CHECK-NEXT: i32 3, label %unreachable +// CHECK-NEXT: ] +// CHECK: cleanup.cont: +// CHECK-NEXT: br label %eh.cont +// CHECK: eh.cont: +// CHECK-NEXT: store i32 0, ptr %retval +// CHECK-NEXT: br label %return +// CHECK: catch: +// CHECK-NEXT: %exn = load ptr, ptr %exn.slot +// CHECK-NEXT: %exn.adjusted = call ptr @__cxa_begin_catch(ptr %exn) +// CHECK-NEXT: store i32 1, ptr %retval +// CHECK-NEXT: store i32 1, ptr %cleanup.dest.slot +// CHECK-NEXT: %{{.*}} = invoke noundef ptr @_ZN18ThrowingDestructorD1Ev{{.*}}[ "funclet"(token [[CATCH_PAD]]) ] +// CHECK-NEXT: to label %invoke.cont1 unwind label %ehcleanup +// CHECK: invoke.cont1: +// CHECK-NEXT: invoke void @__cxa_end_catch() +// CHECK-NEXT: to label %invoke.cont2 unwind label %catch.dispatch4 +// CHECK: invoke.cont2: +// CHECK-NEXT: catchret from [[CATCH_PAD]] to label %catchret.dest +// CHECK: ehcleanup: +// CHECK-NEXT: [[CATCH_CLEANUP:%.*]] = cleanuppad within [[CATCH_PAD]] [] +// CHECK-NEXT: invoke void @__cxa_end_catch() +// CHECK-NEXT: to label %invoke.cont3 unwind label %terminate +// CHECK: invoke.cont3: +// CHECK-NEXT: cleanupret from [[CATCH_CLEANUP]] unwind label %catch.dispatch4 +// CHECK: catch.dispatch4: +// CHECK-NEXT: [[FINALLY_SWITCH:%.*]] = catchswitch within none [label %catch.start5] unwind to caller +// CHECK: catch.start5: +// CHECK-NEXT: [[FINALLY_PAD:%.*]] = catchpad within [[FINALLY_SWITCH]] [ptr null] +// CHECK: br label %finally.catchall +// CHECK: catchret.dest: +// CHECK-NEXT: br label %cleanup +// CHECK: finally.catchall: +// CHECK-NEXT: %exn6 = load ptr, ptr %exn.slot +// CHECK-NEXT: %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn6) +// CHECK-NEXT: store i1 true, ptr %finally.for-eh +// CHECK-NEXT: store i32 3, ptr %cleanup.dest.slot +// CHECK-NEXT: catchret from [[FINALLY_PAD]] to label %catchret.dest7 +// CHECK: catchret.dest7: +// CHECK-NEXT: br label %cleanup +// CHECK: ehcleanup8: +// CHECK-NEXT: [[CATCH_FINALLY_CLEANUP:%.*]] = cleanuppad within none [] +// CHECK-NEXT: %finally.endcatch = load i1, ptr %finally.for-eh +// CHECK-NEXT: br i1 %finally.endcatch, label %finally.endcatch9, label %finally.cleanup.cont +// CHECK: finally.endcatch9: +// CHECK-NEXT: invoke void @__cxa_end_catch() +// CHECK-NEXT: to label %invoke.cont10 unwind label %terminate11 +// CHECK: invoke.cont10: +// CHECK-NEXT: br label %finally.cleanup.cont +// CHECK: finally.cleanup.cont: +// CHECK-NEXT: cleanupret from [[CATCH_FINALLY_CLEANUP]] unwind to caller +// CHECK: return: +// CHECK-NEXT: %{{.*}} = load i32, ptr %retval +// CHECK-NEXT: ret i32 %{{.*}} + int combinedCxxObjcEH() { @try { try { From 7dc5898b823046c3a7dcf997b543d9e097d1f2b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hendrik=20H=C3=BCbner?= <[email protected]> Date: Thu, 27 Aug 2026 11:26:41 +0200 Subject: [PATCH 2/7] Simplify asserts and fix --- clang/lib/CodeGen/CGException.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index ffa6af9a55ce6..830b77e44ef0b 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -1412,15 +1412,15 @@ namespace { CGF.Builder.CreateCondBr(ShouldRethrow, RethrowBB, ContBB); CGF.EmitBlock(RethrowBB); - if (SavedExnVar) { - CGF.EmitRuntimeCallOrInvoke(RethrowFn, CGF.Builder.CreateAlignedLoad( - CGF.Int8PtrTy, SavedExnVar, - CGF.getPointerAlign())); - + if (!SavedExnVar) { + CGF.EmitNoreturnRuntimeCallOrInvoke(RethrowFn, {}); } else { - CGF.EmitRuntimeCallOrInvoke(RethrowFn); + CGF.EmitRuntimeCallOrInvoke( + RethrowFn, + CGF.Builder.CreateAlignedLoad(CGF.Int8PtrTy, SavedExnVar, + CGF.getPointerAlign())); + CGF.Builder.CreateUnreachable(); } - CGF.Builder.CreateUnreachable(); CGF.EmitBlock(ContBB); From 63c493e38e1fc8ad56222d5739eae8a188c0dc5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hendrik=20H=C3=BCbner?= <[email protected]> Date: Thu, 27 Aug 2026 11:30:33 +0200 Subject: [PATCH 3/7] fmt --- clang/lib/CodeGen/CGException.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index 830b77e44ef0b..263ae25bb0b8d 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -1415,10 +1415,9 @@ namespace { if (!SavedExnVar) { CGF.EmitNoreturnRuntimeCallOrInvoke(RethrowFn, {}); } else { - CGF.EmitRuntimeCallOrInvoke( - RethrowFn, - CGF.Builder.CreateAlignedLoad(CGF.Int8PtrTy, SavedExnVar, - CGF.getPointerAlign())); + CGF.EmitRuntimeCallOrInvoke(RethrowFn, CGF.Builder.CreateAlignedLoad( + CGF.Int8PtrTy, SavedExnVar, + CGF.getPointerAlign())); CGF.Builder.CreateUnreachable(); } From d1f13d272500e85181fd0f3d3e91d5c2e35ab200 Mon Sep 17 00:00:00 2001 From: Hendrik Huebner <[email protected]> Date: Sun, 27 Sep 2026 10:12:21 +0200 Subject: [PATCH 4/7] [CodeGen][Objective-C] Address review comments --- clang/lib/CodeGen/CGException.cpp | 12 +-- clang/lib/CodeGen/CGObjCRuntime.cpp | 146 ++++++++++++++-------------- 2 files changed, 79 insertions(+), 79 deletions(-) diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index 263ae25bb0b8d..ee419356ede27 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -1412,13 +1412,13 @@ namespace { CGF.Builder.CreateCondBr(ShouldRethrow, RethrowBB, ContBB); CGF.EmitBlock(RethrowBB); - if (!SavedExnVar) { - CGF.EmitNoreturnRuntimeCallOrInvoke(RethrowFn, {}); + if (SavedExnVar) { + CGF.EmitNoreturnRuntimeCallOrInvoke( + RethrowFn, {CGF.Builder.CreateAlignedLoad( + CGF.Int8PtrTy, SavedExnVar, + CGF.getPointerAlign())}); } else { - CGF.EmitRuntimeCallOrInvoke(RethrowFn, CGF.Builder.CreateAlignedLoad( - CGF.Int8PtrTy, SavedExnVar, - CGF.getPointerAlign())); - CGF.Builder.CreateUnreachable(); + CGF.EmitNoreturnRuntimeCallOrInvoke(RethrowFn, {}); } CGF.EmitBlock(ContBB); diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp b/clang/lib/CodeGen/CGObjCRuntime.cpp index e539e905a5d00..f3d341d0f3796 100644 --- a/clang/lib/CodeGen/CGObjCRuntime.cpp +++ b/clang/lib/CodeGen/CGObjCRuntime.cpp @@ -220,97 +220,97 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, } // 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; - 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; - } + { + 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. 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 (IsMSVC) { - llvm::BasicBlock::iterator CPICandidate = - Handler.Block->getFirstNonPHIIt(); - if (CPICandidate != Handler.Block->end()) { - if ((CPI = dyn_cast_or_null<llvm::CatchPadInst>(CPICandidate))) { - CGF.CurrentFuncletPad = CPI; - CPI->setOperand(2, CGF.getExceptionSlot().emitRawPointer(CGF)); + // Remember where we were. + CGBuilderTy::InsertPoint SavedIP = CGF.Builder.saveAndClearIP(); + + // 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 (IsMSVC) { + llvm::BasicBlock::iterator CPICandidate = + Handler.Block->getFirstNonPHIIt(); + if (CPICandidate != Handler.Block->end()) { + if ((CPI = dyn_cast_or_null<llvm::CatchPadInst>(CPICandidate))) { + CGF.CurrentFuncletPad = CPI; + CPI->setOperand(2, CGF.getExceptionSlot().emitRawPointer(CGF)); + } } } - } - if (CPI) { - // A catchpad requires a matching catchret instruction. We emit this in - // form of a cleanup. - 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(); + llvm::Value *RawExn = CGF.getExceptionFromSlot(); - // Enter the catch. - llvm::Value *Exn = RawExn; - if (beginCatchFn) - Exn = CGF.EmitNounwindRuntimeCall(beginCatchFn, RawExn, "exn.adjusted"); + // Enter the catch. + llvm::Value *Exn = RawExn; + if (beginCatchFn) + Exn = CGF.EmitNounwindRuntimeCall(beginCatchFn, RawExn, "exn.adjusted"); - if (endCatchFn) { - // Add a cleanup to leave the catch. - bool EndCatchMightThrow = (Handler.Variable == nullptr); + if (endCatchFn) { + // Add a cleanup to leave the catch. + bool EndCatchMightThrow = (Handler.Variable == nullptr); - CGF.EHStack.pushCleanup<CallObjCEndCatch>(NormalAndEHCleanup, - EndCatchMightThrow, - endCatchFn); - } + CGF.EHStack.pushCleanup<CallObjCEndCatch>(NormalAndEHCleanup, + EndCatchMightThrow, + endCatchFn); + } - // Bind the catch parameter if it exists. - if (const VarDecl *CatchParam = Handler.Variable) { - llvm::Type *CatchType = CGF.ConvertType(CatchParam->getType()); - llvm::Value *CastExn = CGF.Builder.CreateBitCast(Exn, CatchType); + // Bind the catch parameter if it exists. + if (const VarDecl *CatchParam = Handler.Variable) { + llvm::Type *CatchType = CGF.ConvertType(CatchParam->getType()); + llvm::Value *CastExn = CGF.Builder.CreateBitCast(Exn, CatchType); - CGF.EmitAutoVarDecl(*CatchParam); - EmitInitOfCatchParam(CGF, CastExn, CatchParam); - } + CGF.EmitAutoVarDecl(*CatchParam); + 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(); + // 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(); - // Leave any cleanups associated with the catch. - Cleanups.ForceCleanup(); + // Leave any cleanups associated with the catch. + Cleanups.ForceCleanup(); - CGF.EmitBranchThroughCleanup(Cont); - } + CGF.EmitBranchThroughCleanup(Cont); + } - if (IsWasm && !HasCatchAll && WasmCatchStartBlock) { - CGF.WasmEmitFallthroughRethrow(WasmCatchStartBlock); - } + if (IsWasm && !HasCatchAll && WasmCatchStartBlock) { + CGF.WasmEmitFallthroughRethrow(WasmCatchStartBlock); + } - // Go back to the try-statement fallthrough. - CGF.Builder.restoreIP(SavedIP); + // Go back to the try-statement fallthrough. + CGF.Builder.restoreIP(SavedIP); + } // Pop out of the finally. if (!IsMSVC && S.getFinallyStmt()) { - CGF.CurrentFuncletPad = SavedFuncletPad; FinallyInfo.exit(CGF); } From 5570152ce96cbe5e15e0740ddccb12de9c2407b1 Mon Sep 17 00:00:00 2001 From: Hendrik Huebner <[email protected]> Date: Sun, 27 Sep 2026 21:21:17 +0200 Subject: [PATCH 5/7] [CodeGen][Objective-C] Format review fixes --- clang/lib/CodeGen/CGException.cpp | 6 +++--- clang/lib/CodeGen/CGObjCRuntime.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index ee419356ede27..9198e6f8881f6 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -1414,9 +1414,9 @@ namespace { CGF.EmitBlock(RethrowBB); if (SavedExnVar) { CGF.EmitNoreturnRuntimeCallOrInvoke( - RethrowFn, {CGF.Builder.CreateAlignedLoad( - CGF.Int8PtrTy, SavedExnVar, - CGF.getPointerAlign())}); + RethrowFn, + {CGF.Builder.CreateAlignedLoad(CGF.Int8PtrTy, SavedExnVar, + CGF.getPointerAlign())}); } else { CGF.EmitNoreturnRuntimeCallOrInvoke(RethrowFn, {}); } diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp b/clang/lib/CodeGen/CGObjCRuntime.cpp index f3d341d0f3796..b4b1b54dd7581 100644 --- a/clang/lib/CodeGen/CGObjCRuntime.cpp +++ b/clang/lib/CodeGen/CGObjCRuntime.cpp @@ -245,7 +245,8 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, HasCatchAll |= Handler.TypeInfo == nullptr; CGF.EmitBlock(Handler.Block); - CodeGenFunction::LexicalScope Cleanups(CGF, Handler.Body->getSourceRange()); + CodeGenFunction::LexicalScope Cleanups(CGF, + Handler.Body->getSourceRange()); SaveAndRestore RevertAfterScope(CGF.CurrentFuncletPad); if (IsMSVC) { llvm::BasicBlock::iterator CPICandidate = @@ -275,9 +276,8 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, // Add a cleanup to leave the catch. bool EndCatchMightThrow = (Handler.Variable == nullptr); - CGF.EHStack.pushCleanup<CallObjCEndCatch>(NormalAndEHCleanup, - EndCatchMightThrow, - endCatchFn); + CGF.EHStack.pushCleanup<CallObjCEndCatch>( + NormalAndEHCleanup, EndCatchMightThrow, endCatchFn); } // Bind the catch parameter if it exists. From 62e5d18be822bb16f3f0f02314f881429ea22972 Mon Sep 17 00:00:00 2001 From: Hendrik Huebner <[email protected]> Date: Fri, 9 Oct 2026 20:56:10 +0200 Subject: [PATCH 6/7] [CodeGen][Objective-C] Use driver flag for Wasm finally test --- clang/test/CodeGenObjC/gnustep2-wasm32-finally.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/CodeGenObjC/gnustep2-wasm32-finally.m b/clang/test/CodeGenObjC/gnustep2-wasm32-finally.m index 2c3ca912b595f..85d3875cb27c2 100644 --- a/clang/test/CodeGenObjC/gnustep2-wasm32-finally.m +++ b/clang/test/CodeGenObjC/gnustep2-wasm32-finally.m @@ -1,5 +1,5 @@ // REQUIRES: webassembly-registered-target -// 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 +// RUN: %clang -target wasm32-unknown-emscripten -fobjc-runtime=gnustep-2.2 -fexceptions -fobjc-exceptions -fwasm-exceptions -emit-llvm -S -o - %s | FileCheck %s __attribute__((objc_root_class)) @interface Object @end extern void mayThrowObjC(); From cdf2ff6e010369841a5852e1f324816e93ace9d5 Mon Sep 17 00:00:00 2001 From: Hendrik Huebner <[email protected]> Date: Sun, 11 Oct 2026 00:10:59 +0200 Subject: [PATCH 7/7] Remove unrelated formatting from Wasm finally merge --- clang/lib/CodeGen/CGObjCRuntime.cpp | 115 ++++++++++++++-------------- 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp b/clang/lib/CodeGen/CGObjCRuntime.cpp index 5ecbc9979cb29..862d6056be334 100644 --- a/clang/lib/CodeGen/CGObjCRuntime.cpp +++ b/clang/lib/CodeGen/CGObjCRuntime.cpp @@ -45,9 +45,10 @@ uint64_t CGObjCRuntime::ComputeIvarBaseOffset(CodeGen::CodeGenModule &CGM, CGM.getContext().getCharWidth(); } -unsigned CGObjCRuntime::ComputeBitfieldBitOffset(CodeGen::CodeGenModule &CGM, - const ObjCInterfaceDecl *ID, - const ObjCIvarDecl *Ivar) { +unsigned CGObjCRuntime::ComputeBitfieldBitOffset( + CodeGen::CodeGenModule &CGM, + const ObjCInterfaceDecl *ID, + const ObjCIvarDecl *Ivar) { return CGM.getContext().lookupFieldBitOffset(ID, Ivar); } @@ -100,11 +101,10 @@ LValue CGObjCRuntime::EmitValueForIvarAtOffset(CodeGen::CodeGenFunction &CGF, // layout object. However, this is blocked on other cleanups to the // Objective-C code, so for now we just live with allocating a bunch of these // objects. - CGBitFieldInfo *Info = - new (CGF.CGM.getContext()) CGBitFieldInfo(CGBitFieldInfo::MakeInfo( - CGF.CGM.getTypes(), Ivar, BitOffset, BitFieldSize, - CGF.CGM.getContext().toBits(StorageSize), - CharUnits::fromQuantity(0))); + CGBitFieldInfo *Info = new (CGF.CGM.getContext()) CGBitFieldInfo( + CGBitFieldInfo::MakeInfo(CGF.CGM.getTypes(), Ivar, BitOffset, BitFieldSize, + CGF.CGM.getContext().toBits(StorageSize), + CharUnits::fromQuantity(0))); Address Addr = Address(V, llvm::Type::getIntNTy(CGF.getLLVMContext(), Info->StorageSize), @@ -116,29 +116,29 @@ LValue CGObjCRuntime::EmitValueForIvarAtOffset(CodeGen::CodeGenFunction &CGF, } namespace { -struct CatchHandler { - const VarDecl *Variable; - const Stmt *Body; - llvm::BasicBlock *Block; - llvm::Constant *TypeInfo; - /// Flags used to differentiate cleanups and catchalls in Windows SEH - unsigned Flags; -}; - -struct CallObjCEndCatch final : EHScopeStack::Cleanup { - CallObjCEndCatch(bool MightThrow, llvm::FunctionCallee Fn) - : MightThrow(MightThrow), Fn(Fn) {} - bool MightThrow; - llvm::FunctionCallee Fn; - - void Emit(CodeGenFunction &CGF, Flags flags) override { - if (MightThrow) - CGF.EmitRuntimeCallOrInvoke(Fn); - else - CGF.EmitNounwindRuntimeCall(Fn); - } -}; -} // namespace + struct CatchHandler { + const VarDecl *Variable; + const Stmt *Body; + llvm::BasicBlock *Block; + llvm::Constant *TypeInfo; + /// Flags used to differentiate cleanups and catchalls in Windows SEH + unsigned Flags; + }; + + struct CallObjCEndCatch final : EHScopeStack::Cleanup { + CallObjCEndCatch(bool MightThrow, llvm::FunctionCallee Fn) + : MightThrow(MightThrow), Fn(Fn) {} + bool MightThrow; + llvm::FunctionCallee Fn; + + void Emit(CodeGenFunction &CGF, Flags flags) override { + if (MightThrow) + CGF.EmitRuntimeCallOrInvoke(Fn); + else + CGF.EmitNounwindRuntimeCall(Fn); + } + }; +} void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, const ObjCAtTryStmt &S, @@ -225,8 +225,7 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, // 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); + Catch->setHandler(I, { Handlers[I].TypeInfo, Handlers[I].Flags }, Handlers[I].Block); } // Emit the try body. @@ -298,8 +297,9 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, // Add a cleanup to leave the catch. bool EndCatchMightThrow = (Handler.Variable == nullptr); - CGF.EHStack.pushCleanup<CallObjCEndCatch>( - NormalAndEHCleanup, EndCatchMightThrow, endCatchFn); + CGF.EHStack.pushCleanup<CallObjCEndCatch>(NormalAndEHCleanup, + EndCatchMightThrow, + endCatchFn); } // Bind the catch parameter if it exists. @@ -344,7 +344,8 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, CGF.EmitBlock(Cont.getBlock()); } -void CGObjCRuntime::EmitInitOfCatchParam(CodeGenFunction &CGF, llvm::Value *exn, +void CGObjCRuntime::EmitInitOfCatchParam(CodeGenFunction &CGF, + llvm::Value *exn, const VarDecl *paramDecl) { Address paramAddr = CGF.GetAddrOfLocalVar(paramDecl); @@ -368,17 +369,17 @@ void CGObjCRuntime::EmitInitOfCatchParam(CodeGenFunction &CGF, llvm::Value *exn, } namespace { -struct CallSyncExit final : EHScopeStack::Cleanup { - llvm::FunctionCallee SyncExitFn; - llvm::Value *SyncArg; - CallSyncExit(llvm::FunctionCallee SyncExitFn, llvm::Value *SyncArg) - : SyncExitFn(SyncExitFn), SyncArg(SyncArg) {} - - void Emit(CodeGenFunction &CGF, Flags flags) override { - CGF.EmitNounwindRuntimeCall(SyncExitFn, SyncArg); - } -}; -} // namespace + struct CallSyncExit final : EHScopeStack::Cleanup { + llvm::FunctionCallee SyncExitFn; + llvm::Value *SyncArg; + CallSyncExit(llvm::FunctionCallee SyncExitFn, llvm::Value *SyncArg) + : SyncExitFn(SyncExitFn), SyncArg(SyncArg) {} + + void Emit(CodeGenFunction &CGF, Flags flags) override { + CGF.EmitNounwindRuntimeCall(SyncExitFn, SyncArg); + } + }; +} void CGObjCRuntime::EmitAtSynchronizedStmt(CodeGenFunction &CGF, const ObjCAtSynchronizedStmt &S, @@ -417,7 +418,8 @@ void CGObjCRuntime::EmitAtSynchronizedStmt(CodeGenFunction &CGF, /// \param callArgs - the actual arguments, including implicit ones CGObjCRuntime::MessageSendInfo CGObjCRuntime::getMessageSendInfo(const ObjCMethodDecl *method, - QualType resultType, CallArgList &callArgs) { + QualType resultType, + CallArgList &callArgs) { unsigned ProgramAS = CGM.getDataLayout().getProgramAddressSpace(); llvm::PointerType *signatureType = @@ -428,7 +430,7 @@ CGObjCRuntime::getMessageSendInfo(const ObjCMethodDecl *method, // If there's a method, use information from that. if (method) { const CGFunctionInfo &signature = - CGM.getTypes().arrangeObjCMessageSendSignature(method, callArgs[0].Ty); + CGM.getTypes().arrangeObjCMessageSendSignature(method, callArgs[0].Ty); const CGFunctionInfo &signatureForCall = CGM.getTypes().arrangeCall(signature, callArgs, /*ABIInfoFD=*/nullptr); @@ -438,7 +440,7 @@ CGObjCRuntime::getMessageSendInfo(const ObjCMethodDecl *method, // There's no method; just use a default CC. const CGFunctionInfo &argsInfo = - CGM.getTypes().arrangeUnprototypedObjCMessageSend(resultType, callArgs); + CGM.getTypes().arrangeUnprototypedObjCMessageSend(resultType, callArgs); return MessageSendInfo(argsInfo, signatureType); } @@ -490,16 +492,17 @@ bool CGObjCRuntime::isWeakLinkedClass(const ObjCInterfaceDecl *ID) { return false; } -void CGObjCRuntime::destroyCalleeDestroyedArguments( - CodeGenFunction &CGF, const ObjCMethodDecl *method, - const CallArgList &callArgs) { +void CGObjCRuntime::destroyCalleeDestroyedArguments(CodeGenFunction &CGF, + const ObjCMethodDecl *method, + const CallArgList &callArgs) { CallArgList::const_iterator I = callArgs.begin(); - for (auto i = method->param_begin(), e = method->param_end(); i != e; - ++i, ++I) { + for (auto i = method->param_begin(), e = method->param_end(); + i != e; ++i, ++I) { const ParmVarDecl *param = (*i); if (param->hasAttr<NSConsumedAttr>()) { RValue RV = I->getRValue(CGF); - assert(RV.isScalar() && "NullReturnState::complete - arg not on object"); + assert(RV.isScalar() && + "NullReturnState::complete - arg not on object"); CGF.EmitARCRelease(RV.getScalarVal(), ARCImpreciseLifetime); } else { QualType QT = param->getType(); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
