forked from OSchip/llvm-project
[WebAssembly] Use Windows EH instructions for Wasm EH
Summary: Because wasm control flow needs to be structured, using WinEH instructions to support wasm EH brings several benefits. This patch makes wasm EH uses Windows EH instructions, with some changes: 1. Because wasm uses a single catch block to catch all C++ exceptions, this merges all catch clauses into a single catchpad, within which we test the EH selector as in Itanium EH. 2. Generates a call to `__clang_call_terminate` in case a cleanup throws. Wasm does not have a runtime to handle this. 3. In case there is no catch-all clause, inserts a call to `__cxa_rethrow` at the end of a catchpad in order to unwind to an enclosing EH scope. Reviewers: majnemer, dschuff Subscribers: jfb, sbc100, jgravelle-google, sunfish, cfe-commits Differential Revision: https://reviews.llvm.org/D44931 llvm-svn: 333703
This commit is contained in:
parent
9a6c0bdcbd
commit
c647919933
|
@ -607,6 +607,17 @@ CGCXXABI *CreateItaniumCXXABI(CodeGenModule &CGM);
|
|||
/// Creates a Microsoft-family ABI.
|
||||
CGCXXABI *CreateMicrosoftCXXABI(CodeGenModule &CGM);
|
||||
|
||||
struct CatchRetScope final : EHScopeStack::Cleanup {
|
||||
llvm::CatchPadInst *CPI;
|
||||
|
||||
CatchRetScope(llvm::CatchPadInst *CPI) : CPI(CPI) {}
|
||||
|
||||
void Emit(CodeGenFunction &CGF, Flags flags) override {
|
||||
llvm::BasicBlock *BB = CGF.createBasicBlock("catchret.dest");
|
||||
CGF.Builder.CreateCatchRet(CPI, BB);
|
||||
CGF.EmitBlock(BB);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -971,16 +971,21 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) {
|
|||
SaveAndRestore<llvm::Instruction *> RestoreCurrentFuncletPad(
|
||||
CurrentFuncletPad);
|
||||
llvm::CleanupPadInst *CPI = nullptr;
|
||||
if (!EHPersonality::get(*this).usesFuncletPads()) {
|
||||
EHStack.pushTerminate();
|
||||
PushedTerminate = true;
|
||||
} else {
|
||||
|
||||
const EHPersonality &Personality = EHPersonality::get(*this);
|
||||
if (Personality.usesFuncletPads()) {
|
||||
llvm::Value *ParentPad = CurrentFuncletPad;
|
||||
if (!ParentPad)
|
||||
ParentPad = llvm::ConstantTokenNone::get(CGM.getLLVMContext());
|
||||
CurrentFuncletPad = CPI = Builder.CreateCleanupPad(ParentPad);
|
||||
}
|
||||
|
||||
// Non-MSVC personalities need to terminate when an EH cleanup throws.
|
||||
if (!Personality.isMSVCPersonality()) {
|
||||
EHStack.pushTerminate();
|
||||
PushedTerminate = true;
|
||||
}
|
||||
|
||||
// We only actually emit the cleanup code if the cleanup is either
|
||||
// active or was used before it was deactivated.
|
||||
if (EHActiveFlag.isValid() || IsActive) {
|
||||
|
|
|
@ -627,16 +627,21 @@ struct EHPersonality {
|
|||
static const EHPersonality MSVC_except_handler;
|
||||
static const EHPersonality MSVC_C_specific_handler;
|
||||
static const EHPersonality MSVC_CxxFrameHandler3;
|
||||
static const EHPersonality GNU_Wasm_CPlusPlus;
|
||||
|
||||
/// Does this personality use landingpads or the family of pad instructions
|
||||
/// designed to form funclets?
|
||||
bool usesFuncletPads() const { return isMSVCPersonality(); }
|
||||
bool usesFuncletPads() const {
|
||||
return isMSVCPersonality() || isWasmPersonality();
|
||||
}
|
||||
|
||||
bool isMSVCPersonality() const {
|
||||
return this == &MSVC_except_handler || this == &MSVC_C_specific_handler ||
|
||||
this == &MSVC_CxxFrameHandler3;
|
||||
}
|
||||
|
||||
bool isWasmPersonality() const { return this == &GNU_Wasm_CPlusPlus; }
|
||||
|
||||
bool isMSVCXXPersonality() const { return this == &MSVC_CxxFrameHandler3; }
|
||||
};
|
||||
}
|
||||
|
|
|
@ -111,6 +111,8 @@ const EHPersonality
|
|||
EHPersonality::MSVC_C_specific_handler = { "__C_specific_handler", nullptr };
|
||||
const EHPersonality
|
||||
EHPersonality::MSVC_CxxFrameHandler3 = { "__CxxFrameHandler3", nullptr };
|
||||
const EHPersonality
|
||||
EHPersonality::GNU_Wasm_CPlusPlus = { "__gxx_wasm_personality_v0", nullptr };
|
||||
|
||||
static const EHPersonality &getCPersonality(const llvm::Triple &T,
|
||||
const LangOptions &L) {
|
||||
|
@ -161,6 +163,9 @@ static const EHPersonality &getCXXPersonality(const llvm::Triple &T,
|
|||
return EHPersonality::MSVC_CxxFrameHandler3;
|
||||
if (L.SEHExceptions)
|
||||
return EHPersonality::GNU_CPlusPlus_SEH;
|
||||
if (T.getArch() == llvm::Triple::wasm32 ||
|
||||
T.getArch() == llvm::Triple::wasm64)
|
||||
return EHPersonality::GNU_Wasm_CPlusPlus;
|
||||
return EHPersonality::GNU_CPlusPlus;
|
||||
}
|
||||
|
||||
|
@ -574,7 +579,7 @@ void CodeGenFunction::EnterCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) {
|
|||
llvm::BasicBlock *
|
||||
CodeGenFunction::getEHDispatchBlock(EHScopeStack::stable_iterator si) {
|
||||
if (EHPersonality::get(*this).usesFuncletPads())
|
||||
return getMSVCDispatchBlock(si);
|
||||
return getFuncletEHDispatchBlock(si);
|
||||
|
||||
// The dispatch block for the end of the scope chain is a block that
|
||||
// just resumes unwinding.
|
||||
|
@ -622,7 +627,7 @@ CodeGenFunction::getEHDispatchBlock(EHScopeStack::stable_iterator si) {
|
|||
}
|
||||
|
||||
llvm::BasicBlock *
|
||||
CodeGenFunction::getMSVCDispatchBlock(EHScopeStack::stable_iterator SI) {
|
||||
CodeGenFunction::getFuncletEHDispatchBlock(EHScopeStack::stable_iterator SI) {
|
||||
// Returning nullptr indicates that the previous dispatch block should unwind
|
||||
// to caller.
|
||||
if (SI == EHStack.stable_end())
|
||||
|
@ -916,10 +921,121 @@ static void emitCatchPadBlock(CodeGenFunction &CGF, EHCatchScope &CatchScope) {
|
|||
CGF.Builder.restoreIP(SavedIP);
|
||||
}
|
||||
|
||||
// Wasm uses Windows-style EH instructions, but it merges all catch clauses into
|
||||
// one big catchpad, within which we use Itanium's landingpad-style selector
|
||||
// comparison instructions.
|
||||
static void emitWasmCatchPadBlock(CodeGenFunction &CGF,
|
||||
EHCatchScope &CatchScope) {
|
||||
llvm::BasicBlock *DispatchBlock = CatchScope.getCachedEHDispatchBlock();
|
||||
assert(DispatchBlock);
|
||||
|
||||
CGBuilderTy::InsertPoint SavedIP = CGF.Builder.saveIP();
|
||||
CGF.EmitBlockAfterUses(DispatchBlock);
|
||||
|
||||
llvm::Value *ParentPad = CGF.CurrentFuncletPad;
|
||||
if (!ParentPad)
|
||||
ParentPad = llvm::ConstantTokenNone::get(CGF.getLLVMContext());
|
||||
llvm::BasicBlock *UnwindBB =
|
||||
CGF.getEHDispatchBlock(CatchScope.getEnclosingEHScope());
|
||||
|
||||
unsigned NumHandlers = CatchScope.getNumHandlers();
|
||||
llvm::CatchSwitchInst *CatchSwitch =
|
||||
CGF.Builder.CreateCatchSwitch(ParentPad, UnwindBB, NumHandlers);
|
||||
|
||||
// We don't use a landingpad instruction, so generate intrinsic calls to
|
||||
// provide exception and selector values.
|
||||
llvm::BasicBlock *WasmCatchStartBlock = CGF.createBasicBlock("catch.start");
|
||||
CatchSwitch->addHandler(WasmCatchStartBlock);
|
||||
CGF.EmitBlockAfterUses(WasmCatchStartBlock);
|
||||
|
||||
// Create a catchpad instruction.
|
||||
SmallVector<llvm::Value *, 4> CatchTypes;
|
||||
for (unsigned I = 0, E = NumHandlers; I < E; ++I) {
|
||||
const EHCatchScope::Handler &Handler = CatchScope.getHandler(I);
|
||||
CatchTypeInfo TypeInfo = Handler.Type;
|
||||
if (!TypeInfo.RTTI)
|
||||
TypeInfo.RTTI = llvm::Constant::getNullValue(CGF.VoidPtrTy);
|
||||
CatchTypes.push_back(TypeInfo.RTTI);
|
||||
}
|
||||
auto *CPI = CGF.Builder.CreateCatchPad(CatchSwitch, CatchTypes);
|
||||
|
||||
// Create calls to wasm.get.exception and wasm.get.ehselector intrinsics.
|
||||
// Before they are lowered appropriately later, they provide values for the
|
||||
// exception and selector.
|
||||
llvm::Value *GetExnFn =
|
||||
CGF.CGM.getIntrinsic(llvm::Intrinsic::wasm_get_exception);
|
||||
llvm::Value *GetSelectorFn =
|
||||
CGF.CGM.getIntrinsic(llvm::Intrinsic::wasm_get_ehselector);
|
||||
llvm::CallInst *Exn = CGF.Builder.CreateCall(GetExnFn, CPI);
|
||||
CGF.Builder.CreateStore(Exn, CGF.getExceptionSlot());
|
||||
llvm::CallInst *Selector = CGF.Builder.CreateCall(GetSelectorFn, CPI);
|
||||
|
||||
llvm::Value *TypeIDFn = CGF.CGM.getIntrinsic(llvm::Intrinsic::eh_typeid_for);
|
||||
|
||||
// If there's only a single catch-all, branch directly to its handler.
|
||||
if (CatchScope.getNumHandlers() == 1 &&
|
||||
CatchScope.getHandler(0).isCatchAll()) {
|
||||
CGF.Builder.CreateBr(CatchScope.getHandler(0).Block);
|
||||
CGF.Builder.restoreIP(SavedIP);
|
||||
return;
|
||||
}
|
||||
|
||||
// Test against each of the exception types we claim to catch.
|
||||
for (unsigned I = 0, E = NumHandlers;; ++I) {
|
||||
assert(I < E && "ran off end of handlers!");
|
||||
const EHCatchScope::Handler &Handler = CatchScope.getHandler(I);
|
||||
CatchTypeInfo TypeInfo = Handler.Type;
|
||||
if (!TypeInfo.RTTI)
|
||||
TypeInfo.RTTI = llvm::Constant::getNullValue(CGF.VoidPtrTy);
|
||||
|
||||
// Figure out the next block.
|
||||
llvm::BasicBlock *NextBlock;
|
||||
|
||||
bool EmitNextBlock = false, NextIsEnd = false;
|
||||
|
||||
// If this is the last handler, we're at the end, and the next block is a
|
||||
// block that contains a call to the rethrow function, so we can unwind to
|
||||
// the enclosing EH scope. The call itself will be generated later.
|
||||
if (I + 1 == E) {
|
||||
NextBlock = CGF.createBasicBlock("rethrow");
|
||||
EmitNextBlock = true;
|
||||
NextIsEnd = true;
|
||||
|
||||
// If the next handler is a catch-all, we're at the end, and the
|
||||
// next block is that handler.
|
||||
} else if (CatchScope.getHandler(I + 1).isCatchAll()) {
|
||||
NextBlock = CatchScope.getHandler(I + 1).Block;
|
||||
NextIsEnd = true;
|
||||
|
||||
// Otherwise, we're not at the end and we need a new block.
|
||||
} else {
|
||||
NextBlock = CGF.createBasicBlock("catch.fallthrough");
|
||||
EmitNextBlock = true;
|
||||
}
|
||||
|
||||
// Figure out the catch type's index in the LSDA's type table.
|
||||
llvm::CallInst *TypeIndex = CGF.Builder.CreateCall(TypeIDFn, TypeInfo.RTTI);
|
||||
TypeIndex->setDoesNotThrow();
|
||||
|
||||
llvm::Value *MatchesTypeIndex =
|
||||
CGF.Builder.CreateICmpEQ(Selector, TypeIndex, "matches");
|
||||
CGF.Builder.CreateCondBr(MatchesTypeIndex, Handler.Block, NextBlock);
|
||||
|
||||
if (EmitNextBlock)
|
||||
CGF.EmitBlock(NextBlock);
|
||||
if (NextIsEnd)
|
||||
break;
|
||||
}
|
||||
|
||||
CGF.Builder.restoreIP(SavedIP);
|
||||
}
|
||||
|
||||
/// Emit the structure of the dispatch block for the given catch scope.
|
||||
/// It is an invariant that the dispatch block already exists.
|
||||
static void emitCatchDispatchBlock(CodeGenFunction &CGF,
|
||||
EHCatchScope &catchScope) {
|
||||
if (EHPersonality::get(CGF).isWasmPersonality())
|
||||
return emitWasmCatchPadBlock(CGF, catchScope);
|
||||
if (EHPersonality::get(CGF).usesFuncletPads())
|
||||
return emitCatchPadBlock(CGF, catchScope);
|
||||
|
||||
|
@ -1007,6 +1123,7 @@ void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) {
|
|||
unsigned NumHandlers = S.getNumHandlers();
|
||||
EHCatchScope &CatchScope = cast<EHCatchScope>(*EHStack.begin());
|
||||
assert(CatchScope.getNumHandlers() == NumHandlers);
|
||||
llvm::BasicBlock *DispatchBlock = CatchScope.getCachedEHDispatchBlock();
|
||||
|
||||
// If the catch was not required, bail out now.
|
||||
if (!CatchScope.hasEHBranches()) {
|
||||
|
@ -1039,6 +1156,22 @@ void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) {
|
|||
doImplicitRethrow = isa<CXXDestructorDecl>(CurCodeDecl) ||
|
||||
isa<CXXConstructorDecl>(CurCodeDecl);
|
||||
|
||||
// Wasm uses Windows-style EH instructions, but merges all catch clauses into
|
||||
// one big catchpad. So we save the old funclet pad here before we traverse
|
||||
// each catch handler.
|
||||
SaveAndRestore<llvm::Instruction *> RestoreCurrentFuncletPad(
|
||||
CurrentFuncletPad);
|
||||
llvm::BasicBlock *WasmCatchStartBlock = nullptr;
|
||||
if (EHPersonality::get(*this).isWasmPersonality()) {
|
||||
auto *CatchSwitch =
|
||||
cast<llvm::CatchSwitchInst>(DispatchBlock->getFirstNonPHI());
|
||||
WasmCatchStartBlock = CatchSwitch->hasUnwindDest()
|
||||
? CatchSwitch->getSuccessor(1)
|
||||
: CatchSwitch->getSuccessor(0);
|
||||
auto *CPI = cast<llvm::CatchPadInst>(WasmCatchStartBlock->getFirstNonPHI());
|
||||
CurrentFuncletPad = CPI;
|
||||
}
|
||||
|
||||
// Perversely, we emit the handlers backwards precisely because we
|
||||
// want them to appear in source order. In all of these cases, the
|
||||
// catch block will have exactly one predecessor, which will be a
|
||||
|
@ -1046,7 +1179,9 @@ void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) {
|
|||
// a catch-all, one of the dispatch blocks will branch to two
|
||||
// different handlers, and EmitBlockAfterUses will cause the second
|
||||
// handler to be moved before the first.
|
||||
bool HasCatchAll = false;
|
||||
for (unsigned I = NumHandlers; I != 0; --I) {
|
||||
HasCatchAll |= Handlers[I - 1].isCatchAll();
|
||||
llvm::BasicBlock *CatchBlock = Handlers[I-1].Block;
|
||||
EmitBlockAfterUses(CatchBlock);
|
||||
|
||||
|
@ -1091,6 +1226,27 @@ 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::TerminatorInst *TI = RethrowBlock->getTerminator()) {
|
||||
auto *BI = cast<llvm::BranchInst>(TI);
|
||||
assert(BI->isConditional());
|
||||
RethrowBlock = BI->getSuccessor(1);
|
||||
}
|
||||
assert(RethrowBlock != WasmCatchStartBlock && RethrowBlock->empty());
|
||||
Builder.SetInsertPoint(RethrowBlock);
|
||||
CGM.getCXXABI().emitRethrow(*this, /*isNoReturn=*/true);
|
||||
}
|
||||
|
||||
EmitBlock(ContBB);
|
||||
incrementProfileCounter(&S);
|
||||
}
|
||||
|
@ -1369,8 +1525,17 @@ llvm::BasicBlock *CodeGenFunction::getTerminateFunclet() {
|
|||
CurrentFuncletPad = Builder.CreateCleanupPad(ParentPad);
|
||||
|
||||
// Emit the __std_terminate call.
|
||||
llvm::Value *Exn = nullptr;
|
||||
// In case of wasm personality, we need to pass the exception value to
|
||||
// __clang_call_terminate function.
|
||||
if (getLangOpts().CPlusPlus &&
|
||||
EHPersonality::get(*this).isWasmPersonality()) {
|
||||
llvm::Value *GetExnFn =
|
||||
CGM.getIntrinsic(llvm::Intrinsic::wasm_get_exception);
|
||||
Exn = Builder.CreateCall(GetExnFn, CurrentFuncletPad);
|
||||
}
|
||||
llvm::CallInst *terminateCall =
|
||||
CGM.getCXXABI().emitTerminateForUnexpectedException(*this, nullptr);
|
||||
CGM.getCXXABI().emitTerminateForUnexpectedException(*this, Exn);
|
||||
terminateCall->setDoesNotReturn();
|
||||
Builder.CreateUnreachable();
|
||||
|
||||
|
|
|
@ -877,7 +877,8 @@ public:
|
|||
|
||||
llvm::BasicBlock *getEHResumeBlock(bool isCleanup);
|
||||
llvm::BasicBlock *getEHDispatchBlock(EHScopeStack::stable_iterator scope);
|
||||
llvm::BasicBlock *getMSVCDispatchBlock(EHScopeStack::stable_iterator scope);
|
||||
llvm::BasicBlock *
|
||||
getFuncletEHDispatchBlock(EHScopeStack::stable_iterator scope);
|
||||
|
||||
/// An object to manage conditionally-evaluated expressions.
|
||||
class ConditionalEvaluation {
|
||||
|
|
|
@ -469,6 +469,7 @@ public:
|
|||
explicit WebAssemblyCXXABI(CodeGen::CodeGenModule &CGM)
|
||||
: ItaniumCXXABI(CGM, /*UseARMMethodPtrABI=*/true,
|
||||
/*UseARMGuardVarABI=*/true) {}
|
||||
void emitBeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *C) override;
|
||||
|
||||
private:
|
||||
bool HasThisReturn(GlobalDecl GD) const override {
|
||||
|
@ -4098,3 +4099,10 @@ ItaniumCXXABI::LoadVTablePtr(CodeGenFunction &CGF, Address This,
|
|||
const CXXRecordDecl *RD) {
|
||||
return {CGF.GetVTablePtr(This, CGM.Int8PtrTy, RD), RD};
|
||||
}
|
||||
|
||||
void WebAssemblyCXXABI::emitBeginCatch(CodeGenFunction &CGF,
|
||||
const CXXCatchStmt *C) {
|
||||
CGF.EHStack.pushCleanup<CatchRetScope>(
|
||||
NormalCleanup, cast<llvm::CatchPadInst>(CGF.CurrentFuncletPad));
|
||||
ItaniumCXXABI::emitBeginCatch(CGF, C);
|
||||
}
|
||||
|
|
|
@ -859,20 +859,6 @@ void MicrosoftCXXABI::emitRethrow(CodeGenFunction &CGF, bool isNoReturn) {
|
|||
CGF.EmitRuntimeCallOrInvoke(Fn, Args);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct CatchRetScope final : EHScopeStack::Cleanup {
|
||||
llvm::CatchPadInst *CPI;
|
||||
|
||||
CatchRetScope(llvm::CatchPadInst *CPI) : CPI(CPI) {}
|
||||
|
||||
void Emit(CodeGenFunction &CGF, Flags flags) override {
|
||||
llvm::BasicBlock *BB = CGF.createBasicBlock("catchret.dest");
|
||||
CGF.Builder.CreateCatchRet(CPI, BB);
|
||||
CGF.EmitBlock(BB);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void MicrosoftCXXABI::emitBeginCatch(CodeGenFunction &CGF,
|
||||
const CXXCatchStmt *S) {
|
||||
// In the MS ABI, the runtime handles the copy, and the catch handler is
|
||||
|
|
|
@ -0,0 +1,384 @@
|
|||
// RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -emit-llvm -o - -std=c++11 | FileCheck %s
|
||||
// RUN: %clang_cc1 %s -triple wasm64-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -emit-llvm -o - -std=c++11 | FileCheck %s
|
||||
|
||||
void may_throw();
|
||||
void dont_throw() noexcept;
|
||||
|
||||
struct Cleanup {
|
||||
~Cleanup() { dont_throw(); }
|
||||
};
|
||||
|
||||
// Multiple catch clauses w/o catch-all
|
||||
void test0() {
|
||||
try {
|
||||
may_throw();
|
||||
} catch (int) {
|
||||
dont_throw();
|
||||
} catch (double) {
|
||||
dont_throw();
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: define void @_Z5test0v() {{.*}} personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*)
|
||||
|
||||
// CHECK: %[[INT_ALLOCA:.*]] = alloca i32
|
||||
// CHECK: invoke void @_Z9may_throwv()
|
||||
// CHECK-NEXT: to label %[[NORMAL_BB:.*]] unwind label %[[CATCHDISPATCH_BB:.*]]
|
||||
|
||||
// CHECK: [[CATCHDISPATCH_BB]]:
|
||||
// CHECK-NEXT: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller
|
||||
|
||||
// CHECK: [[CATCHSTART_BB]]:
|
||||
// CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTId to i8*)]
|
||||
// CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.get.exception(token %[[CATCHPAD]])
|
||||
// CHECK-NEXT: store i8* %[[EXN]], i8** %exn.slot
|
||||
// CHECK-NEXT: %[[SELECTOR:.*]] = call i32 @llvm.wasm.get.ehselector(token %[[CATCHPAD]])
|
||||
// CHECK-NEXT: %[[TYPEID:.*]] = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #2
|
||||
// CHECK-NEXT: %[[MATCHES:.*]] = icmp eq i32 %[[SELECTOR]], %[[TYPEID]]
|
||||
// CHECK-NEXT: br i1 %[[MATCHES]], label %[[CATCH_INT_BB:.*]], label %[[CATCH_FALLTHROUGH_BB:.*]]
|
||||
|
||||
// CHECK: [[CATCH_INT_BB]]:
|
||||
// CHECK-NEXT: %[[EXN:.*]] = load i8*, i8** %exn.slot
|
||||
// CHECK-NEXT: %[[ADDR:.*]] = call i8* @__cxa_begin_catch(i8* %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
|
||||
// CHECK-NEXT: %[[ADDR_CAST:.*]] = bitcast i8* %[[ADDR]] to i32*
|
||||
// CHECK-NEXT: %[[INT_VAL:.*]] = load i32, i32* %[[ADDR_CAST]]
|
||||
// CHECK-NEXT: store i32 %[[INT_VAL]], i32* %[[INT_ALLOCA]]
|
||||
// CHECK-NEXT: call void @_Z10dont_throwv() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
|
||||
// CHECK-NEXT: call void @__cxa_end_catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
|
||||
// CHECK-NEXT: catchret from %[[CATCHPAD]] to label %[[CATCHRET_DEST_BB0:.*]]
|
||||
|
||||
// CHECK: [[CATCHRET_DEST_BB0]]:
|
||||
// CHECK-NEXT: br label %[[TRY_CONT_BB:.*]]
|
||||
|
||||
// CHECK: [[CATCH_FALLTHROUGH_BB]]
|
||||
// CHECK-NEXT: %[[TYPEID:.*]] = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*)) #2
|
||||
// CHECK-NEXT: %[[MATCHES:.*]] = icmp eq i32 %[[SELECTOR]], %[[TYPEID]]
|
||||
// CHECK-NEXT: br i1 %[[MATCHES]], label %[[CATCH_FLOAT_BB:.*]], label %[[RETHROW_BB:.*]]
|
||||
|
||||
// CHECK: [[CATCH_FLOAT_BB]]:
|
||||
// CHECK: catchret from %[[CATCHPAD]] to label %[[CATCHRET_DEST_BB1:.*]]
|
||||
|
||||
// CHECK: [[CATCHRET_DEST_BB1]]:
|
||||
// CHECK-NEXT: br label %[[TRY_CONT_BB]]
|
||||
|
||||
// CHECK: [[RETHROW_BB]]:
|
||||
// CHECK-NEXT: call void @__cxa_rethrow() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
|
||||
// CHECK-NEXT: unreachable
|
||||
|
||||
// Single catch-all
|
||||
void test1() {
|
||||
try {
|
||||
may_throw();
|
||||
} catch (...) {
|
||||
dont_throw();
|
||||
}
|
||||
}
|
||||
|
||||
// CATCH-LABEL: @_Z5test1v()
|
||||
|
||||
// CHECK: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller
|
||||
|
||||
// CHECK: [[CATCHSTART_BB]]:
|
||||
// CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* null]
|
||||
// CHECK: br label %[[CATCH_ALL_BB:.*]]
|
||||
|
||||
// CHECK: [[CATCH_ALL_BB]]:
|
||||
// CHECK: catchret from %[[CATCHPAD]] to label
|
||||
|
||||
// Multiple catch clauses w/ catch-all
|
||||
void test2() {
|
||||
try {
|
||||
may_throw();
|
||||
} catch (int) {
|
||||
dont_throw();
|
||||
} catch (...) {
|
||||
dont_throw();
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @_Z5test2v()
|
||||
|
||||
// CHECK: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller
|
||||
|
||||
// CHECK: [[CATCHSTART_BB]]:
|
||||
// CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*), i8* null]
|
||||
// CHECK: br i1 %{{.*}}, label %[[CATCH_INT_BB:.*]], label %[[CATCH_ALL_BB:.*]]
|
||||
|
||||
// CHECK: [[CATCH_INT_BB]]:
|
||||
// CHECK: catchret from %[[CATCHPAD]] to label
|
||||
|
||||
// CHECK: [[CATCH_ALL_BB]]:
|
||||
// CHECK: catchret from %[[CATCHPAD]] to label
|
||||
|
||||
// Cleanup
|
||||
void test3() {
|
||||
Cleanup c;
|
||||
may_throw();
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @_Z5test3v()
|
||||
|
||||
// CHECK: invoke void @_Z9may_throwv()
|
||||
// CHECK-NEXT: to label {{.*}} unwind label %[[EHCLEANUP_BB:.*]]
|
||||
|
||||
// CHECK: [[EHCLEANUP_BB]]:
|
||||
// CHECK-NEXT: %[[CLEANUPPAD:.*]] = cleanuppad within none []
|
||||
// CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD]]) ]
|
||||
// CHECK-NEXT: cleanupret from %[[CLEANUPPAD]] unwind to caller
|
||||
|
||||
// Possibly throwing function call within a catch
|
||||
void test4() {
|
||||
try {
|
||||
may_throw();
|
||||
} catch (int) {
|
||||
may_throw();
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @_Z5test4v()
|
||||
|
||||
// CHECK: %[[CATCHSWITCH]] = catchswitch within none [label %[[CATCHSTART_BB]]] unwind to caller
|
||||
|
||||
// CHECK: [[CATCHSTART_BB]]:
|
||||
// CHECK: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*)]
|
||||
|
||||
// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ]
|
||||
// CHECK-NEXT: to label %[[INVOKE_CONT_BB:.*]] unwind label %[[EHCLEANUP_BB:.*]]
|
||||
|
||||
// CHECK: [[INVOKE_CONT_BB]]:
|
||||
// CHECK-NEXT: call void @__cxa_end_catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
|
||||
// CHECK-NEXT: catchret from %[[CATCHPAD]] to label
|
||||
|
||||
// CHECK: [[EHCLEANUP_BB]]:
|
||||
// CHECK-NEXT: %[[CLEANUPPAD:.*]] = cleanuppad within %[[CATCHPAD]] []
|
||||
// CHECK-NEXT: call void @__cxa_end_catch() {{.*}} [ "funclet"(token %[[CLEANUPPAD]]) ]
|
||||
// CHECK-NEXT: cleanupret from %[[CLEANUPPAD]] unwind to caller
|
||||
|
||||
// Possibly throwing function call within a catch-all
|
||||
void test5() {
|
||||
try {
|
||||
may_throw();
|
||||
} catch (...) {
|
||||
may_throw();
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @_Z5test5v()
|
||||
|
||||
// CHECK: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB]]] unwind to caller
|
||||
|
||||
// CHECK: [[CATCHSTART_BB]]:
|
||||
// CHECK: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* null]
|
||||
|
||||
// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ]
|
||||
// CHECK-NEXT: to label %[[INVOKE_CONT_BB0:.*]] unwind label %[[EHCLEANUP_BB:.*]]
|
||||
|
||||
// CHECK: [[INVOKE_CONT_BB0]]:
|
||||
// CHECK-NEXT: call void @__cxa_end_catch() [ "funclet"(token %[[CATCHPAD]]) ]
|
||||
// CHECK-NEXT: catchret from %[[CATCHPAD]] to label
|
||||
|
||||
// CHECK: [[EHCLEANUP_BB]]:
|
||||
// CHECK-NEXT: %[[CLEANUPPAD0:.*]] = cleanuppad within %[[CATCHPAD]] []
|
||||
// CHECK-NEXT: invoke void @__cxa_end_catch() [ "funclet"(token %[[CLEANUPPAD0]]) ]
|
||||
// CHECK-NEXT: to label %[[INVOKE_CONT_BB1:.*]] unwind label %[[TERMINATE_BB:.*]]
|
||||
|
||||
// CHECK: [[INVOKE_CONT_BB1]]:
|
||||
// CHECK-NEXT: cleanupret from %[[CLEANUPPAD0]] unwind to caller
|
||||
|
||||
// CHECK: [[TERMINATE_BB]]:
|
||||
// CHECK-NEXT: %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CLEANUPPAD0]] []
|
||||
// CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.get.exception(token %[[CLEANUPPAD1]])
|
||||
// CHECK-NEXT: call void @__clang_call_terminate(i8* %[[EXN]]) {{.*}} [ "funclet"(token %[[CLEANUPPAD1]]) ]
|
||||
// CHECK-NEXT: unreachable
|
||||
|
||||
// CHECK-LABEL: define {{.*}} void @__clang_call_terminate(i8*)
|
||||
// CHECK-NEXT: call i8* @__cxa_begin_catch(i8* %{{.*}})
|
||||
// CHECK-NEXT: call void @_ZSt9terminatev()
|
||||
// CHECK-NEXT: unreachable
|
||||
|
||||
// Try-catch with cleanups
|
||||
void test6() {
|
||||
Cleanup c1;
|
||||
try {
|
||||
Cleanup c2;
|
||||
may_throw();
|
||||
} catch (int) {
|
||||
Cleanup c3;
|
||||
may_throw();
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @_Z5test6v()
|
||||
// CHECK: invoke void @_Z9may_throwv()
|
||||
// CHECK-NEXT: to label %{{.*}} unwind label %[[EHCLEANUP_BB0:.*]]
|
||||
|
||||
// CHECK: [[EHCLEANUP_BB0]]:
|
||||
// CHECK-NEXT: %[[CLEANUPPAD0:.*]] = cleanuppad within none []
|
||||
// CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* {{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD0]]) ]
|
||||
// CHECK-NEXT: cleanupret from %[[CLEANUPPAD0]] unwind label %[[CATCH_DISPATCH_BB:.*]]
|
||||
|
||||
// CHECK: [[CATCH_DISPATCH_BB]]:
|
||||
// CHECK-NEXT: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind label %[[EHCLEANUP_BB1:.*]]
|
||||
|
||||
// CHECK: [[CATCHSTART_BB]]:
|
||||
// CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*)]
|
||||
// CHECK: br i1 %{{.*}}, label %[[CATCH_INT_BB:.*]], label %[[RETHROW_BB:.*]]
|
||||
|
||||
// CHECK: [[CATCH_INT_BB]]:
|
||||
// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ]
|
||||
// CHECK-NEXT: to label %[[INVOKE_CONT_BB:.*]] unwind label %[[EHCLEANUP_BB2:.*]]
|
||||
|
||||
// CHECK: [[INVOKE_CONT_BB]]:
|
||||
// CHECK: catchret from %[[CATCHPAD]] to label %{{.*}}
|
||||
|
||||
// CHECK: [[RETHROW_BB]]:
|
||||
// CHECK-NEXT: invoke void @__cxa_rethrow() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
|
||||
// CHECK-NEXT: to label %[[UNREACHABLE_BB:.*]] unwind label %[[EHCLEANUP_BB1:.*]]
|
||||
|
||||
// CHECK: [[EHCLEANUP_BB2]]:
|
||||
// CHECK-NEXT: %[[CLEANUPPAD2:.*]] = cleanuppad within %[[CATCHPAD]] []
|
||||
// CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD2]]) ]
|
||||
// CHECK-NEXT: cleanupret from %[[CLEANUPPAD2]] unwind label %[[EHCLEANUP_BB3:.*]]
|
||||
|
||||
// CHECK: [[EHCLEANUP_BB3]]:
|
||||
// CHECK-NEXT: %[[CLEANUPPAD3:.*]] = cleanuppad within %[[CATCHPAD]] []
|
||||
// CHECK: cleanupret from %[[CLEANUPPAD3]] unwind label %[[EHCLEANUP_BB1:.*]]
|
||||
|
||||
// CHECK: [[EHCLEANUP_BB1]]:
|
||||
// CHECK-NEXT: %[[CLEANUPPAD1:.*]] = cleanuppad within none []
|
||||
// CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD1]]) ]
|
||||
// CHECK-NEXT: cleanupret from %[[CLEANUPPAD1]] unwind to caller
|
||||
|
||||
// CHECK: [[UNREACHABLE_BB]]:
|
||||
// CHECK-NEXT: unreachable
|
||||
|
||||
// Nested try-catches within a try with cleanups
|
||||
void test7() {
|
||||
Cleanup c1;
|
||||
may_throw();
|
||||
try {
|
||||
Cleanup c2;
|
||||
may_throw();
|
||||
try {
|
||||
Cleanup c3;
|
||||
may_throw();
|
||||
} catch (int) {
|
||||
may_throw();
|
||||
} catch (double) {
|
||||
may_throw();
|
||||
}
|
||||
} catch (int) {
|
||||
may_throw();
|
||||
} catch (...) {
|
||||
may_throw();
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @_Z5test7v()
|
||||
// CHECK: invoke void @_Z9may_throwv()
|
||||
|
||||
// CHECK: invoke void @_Z9may_throwv()
|
||||
|
||||
// CHECK: invoke void @_Z9may_throwv()
|
||||
|
||||
// CHECK: %[[CLEANUPPAD0:.*]] = cleanuppad within none []
|
||||
// CHECK: cleanupret from %[[CLEANUPPAD0]] unwind label
|
||||
|
||||
// CHECK: %[[CATCHSWITCH0:.*]] = catchswitch within none
|
||||
|
||||
// CHECK: %[[CATCHPAD0:.*]] = catchpad within %[[CATCHSWITCH0]] [i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTId to i8*)]
|
||||
|
||||
// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ]
|
||||
|
||||
// CHECK: catchret from %[[CATCHPAD0]] to label
|
||||
|
||||
// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ]
|
||||
|
||||
// CHECK: catchret from %[[CATCHPAD0]] to label
|
||||
|
||||
// CHECK: invoke void @__cxa_rethrow() {{.*}} [ "funclet"(token %[[CATCHPAD0]]) ]
|
||||
|
||||
// CHECK: %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CATCHPAD0]] []
|
||||
// CHECK: cleanupret from %[[CLEANUPPAD1]] unwind label
|
||||
|
||||
// CHECK: %[[CLEANUPPAD2:.*]] = cleanuppad within %[[CATCHPAD0]] []
|
||||
// CHECK: cleanupret from %[[CLEANUPPAD2]] unwind label
|
||||
|
||||
// CHECK: %[[CLEANUPPAD3:.*]] = cleanuppad within none []
|
||||
// CHECK: cleanupret from %[[CLEANUPPAD3]] unwind label
|
||||
|
||||
// CHECK: %[[CATCHSWITCH1:.*]] = catchswitch within none
|
||||
|
||||
// CHECK: %[[CATCHPAD1:.*]] = catchpad within %[[CATCHSWITCH1]] [i8* bitcast (i8** @_ZTIi to i8*), i8* null]
|
||||
|
||||
// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ]
|
||||
|
||||
// CHECK: catchret from %[[CATCHPAD1]] to label
|
||||
|
||||
// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ]
|
||||
|
||||
// CHECK: invoke void @__cxa_end_catch() [ "funclet"(token %[[CATCHPAD1]]) ]
|
||||
|
||||
// CHECK: catchret from %[[CATCHPAD1]] to label
|
||||
|
||||
// CHECK: %[[CLEANUPPAD4:.*]] = cleanuppad within %[[CATCHPAD1]] []
|
||||
// CHECK: invoke void @__cxa_end_catch() [ "funclet"(token %[[CLEANUPPAD4]]) ]
|
||||
|
||||
// CHECK: cleanupret from %[[CLEANUPPAD4]] unwind label
|
||||
|
||||
// CHECK: %[[CLEANUPPAD5:.*]] = cleanuppad within %[[CATCHPAD1]] []
|
||||
// CHECK: cleanupret from %[[CLEANUPPAD5]] unwind label
|
||||
|
||||
// CHECK: %[[CLEANUPPAD6:.*]] = cleanuppad within none []
|
||||
// CHECK: cleanupret from %[[CLEANUPPAD6]] unwind to caller
|
||||
|
||||
// CHECK: unreachable
|
||||
|
||||
// CHECK: %[[CLEANUPPAD7:.*]] = cleanuppad within %[[CLEANUPPAD4]] []
|
||||
// CHECK: call void @__clang_call_terminate(i8* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD7]]) ]
|
||||
// CHECK: unreachable
|
||||
|
||||
// Nested try-catches within a catch
|
||||
void test8() {
|
||||
try {
|
||||
may_throw();
|
||||
} catch (int) {
|
||||
try {
|
||||
may_throw();
|
||||
} catch (int) {
|
||||
may_throw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @_Z5test8v()
|
||||
// CHECK: invoke void @_Z9may_throwv()
|
||||
|
||||
// CHECK: %[[CATCHSWITCH0:.*]] = catchswitch within none
|
||||
|
||||
// CHECK: %[[CATCHPAD0:.*]] = catchpad within %[[CATCHSWITCH0]] [i8* bitcast (i8** @_ZTIi to i8*)]
|
||||
|
||||
// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ]
|
||||
|
||||
// CHECK: %[[CATCHSWITCH1:.*]] = catchswitch within %[[CATCHPAD0]]
|
||||
|
||||
// CHECK: %[[CATCHPAD1:.*]] = catchpad within %[[CATCHSWITCH1]] [i8* bitcast (i8** @_ZTIi to i8*)]
|
||||
|
||||
// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ]
|
||||
|
||||
// CHECK: catchret from %[[CATCHPAD1]] to label
|
||||
|
||||
// CHECK: invoke void @__cxa_rethrow() {{.*}} [ "funclet"(token %[[CATCHPAD1]]) ]
|
||||
|
||||
// CHECK: catchret from %[[CATCHPAD0]] to label
|
||||
|
||||
// CHECK: call void @__cxa_rethrow() {{.*}} [ "funclet"(token %[[CATCHPAD0]]) ]
|
||||
// CHECK: unreachable
|
||||
|
||||
// CHECK: %[[CLEANUPPAD0:.*]] = cleanuppad within %[[CATCHPAD1]] []
|
||||
// CHECK: cleanupret from %[[CLEANUPPAD0]] unwind label
|
||||
|
||||
// CHECK: %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CATCHPAD0]] []
|
||||
// CHECK: cleanupret from %[[CLEANUPPAD1]] unwind to caller
|
||||
|
||||
// CHECK: unreachable
|
Loading…
Reference in New Issue