[CodeGen][ObjC] Try/Catch support for WebAssembly - #171038
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.
TODO: This is taken from CGException.cpp, we may want to refactor it into a shared helper.
There was a problem hiding this comment.
+1 on finding a way to reuse the same code in CGException.cpp. I'm not familiar with his CGObjRuntime.cpp, but can't ObjC (at least partly) use functions in CGException.cpp? What's the reason ObjC maintains separate EH handling routines?
You can test this locally with the following command:git-clang-format --diff origin/main HEAD --extensions cpp,h -- 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 dcff49453..bdb59b1c9 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 660b33284..096b1c0cd 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -1326,7 +1326,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 |
aheejin
left a comment
There was a problem hiding this comment.
It looks you have code for catch handling but for cleanups (= destructors). Do you need to handle them as well?
| static const EHPersonality GNU_ObjC_SJLJ; | ||
| static const EHPersonality GNU_ObjC_SEH; | ||
| static const EHPersonality GNUstep_ObjC; | ||
| static const EHPersonality GNUstep_Wasm_ObjC; |
| // 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.
+1 on finding a way to reuse the same code in CGException.cpp. I'm not familiar with his CGObjRuntime.cpp, but can't ObjC (at least partly) use functions in CGException.cpp? What's the reason ObjC maintains separate EH handling routines?
🐧 Linux x64 Test ResultsThe build failed before running any tests. Click on a failure below to see the details. tools/clang/lib/CodeGen/CMakeFiles/obj.clangCodeGen.dir/CGObjCRuntime.cpp.oIf these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the |
🪟 Windows x64 Test ResultsThe build failed before running any tests. Click on a failure below to see the details. [code=1] tools/clang/lib/CodeGen/CMakeFiles/obj.clangCodeGen.dir/CGObjCRuntime.cpp.objIf these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the |
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).