-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[CodeGen][ObjC] Try/Catch support for WebAssembly #171038
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
cc @hmelder |
| // Remember where we were. | ||
| CGBuilderTy::InsertPoint SavedIP = CGF.Builder.saveAndClearIP(); | ||
|
|
||
| // Wasm uses Windows-style EH instructions, but merges all catch clauses into |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: This is taken from CGException.cpp, we may want to refactor it into a shared helper.
You can test this locally with the following command:git-clang-format --diff origin/main HEAD --extensions h,cpp -- clang/lib/CodeGen/CGCleanup.h clang/lib/CodeGen/CGException.cpp clang/lib/CodeGen/CGObjCRuntime.cpp clang/lib/CodeGen/CodeGenFunction.h clang/lib/Driver/ToolChains/Clang.cpp --diff_from_common_commit
View the diff from clang-format here.diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp b/clang/lib/CodeGen/CGObjCRuntime.cpp
index aa4622695..4d3bf1c1e 100644
--- a/clang/lib/CodeGen/CGObjCRuntime.cpp
+++ b/clang/lib/CodeGen/CGObjCRuntime.cpp
@@ -157,8 +157,8 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
CodeGenFunction::FinallyInfo FinallyInfo;
if (!useFunclets)
if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt())
- FinallyInfo.enter(CGF, Finally->getFinallyBody(),
- beginCatchFn, endCatchFn, exceptionRethrowFn);
+ FinallyInfo.enter(CGF, Finally->getFinallyBody(), beginCatchFn,
+ endCatchFn, exceptionRethrowFn);
SmallVector<CatchHandler, 8> Handlers;
@@ -193,9 +193,9 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
if (useFunclets) {
if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt()) {
- if (hasWasmExceptions) {
- CGF.ErrorUnsupported(Finally, "@finally for WASM");
- }
+ if (hasWasmExceptions) {
+ CGF.ErrorUnsupported(Finally, "@finally for WASM");
+ }
CodeGenFunction HelperCGF(CGM, /*suppressNewContext=*/true);
if (!CGF.CurSEHParent)
@@ -217,12 +217,11 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
}
}
-
// Emit the try body.
CGF.EmitStmt(S.getTryBody());
// Leave the try.
- llvm::BasicBlock* dispatchBlock{};
+ llvm::BasicBlock *dispatchBlock{};
if (S.getNumCatchStmts())
dispatchBlock = CGF.popCatchScope();
@@ -234,14 +233,15 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
// each catch handler.
SaveAndRestore RestoreCurrentFuncletPad(CGF.CurrentFuncletPad);
llvm::BasicBlock *WasmCatchStartBlock = nullptr;
- llvm::CatchPadInst* CatchPadInst{};
+ llvm::CatchPadInst *CatchPadInst{};
if (!!dispatchBlock && hasWasmExceptions) {
auto *CatchSwitch =
cast<llvm::CatchSwitchInst>(dispatchBlock->getFirstNonPHIIt());
WasmCatchStartBlock = CatchSwitch->hasUnwindDest()
? CatchSwitch->getSuccessor(1)
: CatchSwitch->getSuccessor(0);
- CatchPadInst = cast<llvm::CatchPadInst>(WasmCatchStartBlock->getFirstNonPHIIt());
+ CatchPadInst =
+ cast<llvm::CatchPadInst>(WasmCatchStartBlock->getFirstNonPHIIt());
CGF.CurrentFuncletPad = CatchPadInst;
}
@@ -258,9 +258,11 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
llvm::BasicBlock::iterator CPICandidate =
Handler.Block->getFirstNonPHIIt();
if (CPICandidate != Handler.Block->end()) {
- if ((CatchPadInst = dyn_cast_or_null<llvm::CatchPadInst>(CPICandidate))) {
+ if ((CatchPadInst =
+ dyn_cast_or_null<llvm::CatchPadInst>(CPICandidate))) {
CGF.CurrentFuncletPad = CatchPadInst;
- CatchPadInst->setOperand(2, CGF.getExceptionSlot().emitRawPointer(CGF));
+ CatchPadInst->setOperand(2,
+ CGF.getExceptionSlot().emitRawPointer(CGF));
}
}
}
@@ -318,7 +320,7 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
assert(RethrowBlock != WasmCatchStartBlock && RethrowBlock->empty());
CGF.Builder.SetInsertPoint(RethrowBlock);
llvm::Function *RethrowInCatchFn =
- CGM.getIntrinsic(llvm::Intrinsic::wasm_rethrow);
+ CGM.getIntrinsic(llvm::Intrinsic::wasm_rethrow);
CGF.EmitNoreturnRuntimeCallOrInvoke(RethrowInCatchFn, {});
}
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 86c02f0ac..b284f58bc 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -1307,7 +1307,7 @@ public:
/// popCatchScope - Pops the catch scope at the top of the EHScope
/// stack, emitting any required code (other than the catch handlers
/// themselves).
- llvm::BasicBlock* popCatchScope();
+ llvm::BasicBlock *popCatchScope();
llvm::BasicBlock *getEHResumeBlock(bool isCleanup);
llvm::BasicBlock *getEHDispatchBlock(EHScopeStack::stable_iterator scope);
|
|
Thank you!
This is currently also broken on Windows (MSVC ABI) so we might be able to fix this one as well.
I'll test your patch tomorrow and adapt the personality function in libobjc2 if needed. |
|
Given that we emit the same IR instructions for EH on Windows. |
I'm not sure if the personality function is going to be a concern. The WebAssembly VM should handle the unwinding process (see here). However, other runtime function may need to be adapted in libobjc2, as they were for C++. |
extern void might_throw(void);
void test_simple_try_catch(void) {
@try {
might_throw();
} @catch (id e) {
} @catch (id e) {
}
}I compiled your example with The generated IR; Function Attrs: noinline optnone
define hidden void @test_simple_try_catch() #0 personality ptr @__gxx_wasm_personality_v0 {
entry:
%exn.slot = alloca ptr, align 4
%e = alloca ptr, align 4
%e5 = alloca ptr, align 4
invoke void @might_throw()
to label %invoke.cont unwind label %catch.dispatch
catch.dispatch: ; preds = %entry
%0 = catchswitch within none [label %catch.start] unwind to caller
catch.start: ; preds = %catch.dispatch
%1 = catchpad within %0 [ptr @0, ptr @0]
%2 = call ptr @llvm.wasm.get.exception(token %1)
store ptr %2, ptr %exn.slot, align 4
%3 = call i32 @llvm.wasm.get.ehselector(token %1)
%4 = call i32 @llvm.eh.typeid.for.p0(ptr @0) #5
%matches = icmp eq i32 %3, %4
br i1 %matches, label %catch, label %catch.fallthrough
catch.fallthrough: ; preds = %catch.start
%5 = call i32 @llvm.eh.typeid.for.p0(ptr @0) #5
%matches1 = icmp eq i32 %3, %5
br i1 %matches1, label %catch2, label %rethrow
rethrow: ; preds = %catch.fallthrough
call void @llvm.wasm.rethrow() #4 [ "funclet"(token %1) ]
unreachable
invoke.cont: ; preds = %entry
br label %eh.cont
eh.cont: ; preds = %invoke.cont, %catchret.dest6, %catchret.dest
ret void
catch: ; preds = %catch.start
%exn = load ptr, ptr %exn.slot, align 4
%exn.adjusted = call ptr @objc_begin_catch(ptr %exn) #5 [ "funclet"(token %1) ]
store ptr %exn.adjusted, ptr %e, align 4
call void @objc_end_catch() #5 [ "funclet"(token %1) ]
catchret from %1 to label %catchret.dest
catchret.dest: ; preds = %catch
br label %eh.cont
catch2: ; preds = %catch.fallthrough
%exn3 = load ptr, ptr %exn.slot, align 4
%exn.adjusted4 = call ptr @objc_begin_catch(ptr %exn3) #5 [ "funclet"(token %1) ]
store ptr %exn.adjusted4, ptr %e5, align 4
call void @objc_end_catch() #5 [ "funclet"(token %1) ]
catchret from %1 to label %catchret.dest6
catchret.dest6: ; preds = %catch2
br label %eh.cont
} |
AFAIK it is. On WASM, there is only one Unwind phase instead of a search and install phase. |
|
I'm currently hacking in support for the ObjC personality function (crimes are being commited) and it looks like |
Related to #169043 and part of a broader effort to support targetting WebAssembly for ObjectiveC.
This PR is work in progress and adds basic support for generating funclet-style exception handling (try/catch) for WebAssembly. For now, I've set the
gxx_wasmpersonality function. I'm not entirely sure whether we need to define out own.For these examples, the generated IR matches the IR generated from an equivalent C++ program (Aside from the exception type ID).