Skip to content

Commit

Permalink
[WebAssembly] Use Windows EH instructions for Wasm EH
Browse files Browse the repository at this point in the history
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
  • Loading branch information
aheejin committed May 31, 2018
1 parent 9a6c0bd commit c647919
Show file tree
Hide file tree
Showing 8 changed files with 588 additions and 23 deletions.
11 changes: 11 additions & 0 deletions clang/lib/CodeGen/CGCXXABI.h
Expand Up @@ -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);
}
};
}
}

Expand Down
13 changes: 9 additions & 4 deletions clang/lib/CodeGen/CGCleanup.cpp
Expand Up @@ -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) {
Expand Down
7 changes: 6 additions & 1 deletion clang/lib/CodeGen/CGCleanup.h
Expand Up @@ -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; }
};
}
Expand Down
171 changes: 168 additions & 3 deletions clang/lib/CodeGen/CGException.cpp
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -1039,14 +1156,32 @@ 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
// particular block in the catch dispatch. However, in the case of
// 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);

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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();

Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/CodeGenFunction.h
Expand Up @@ -877,7 +877,8 @@ class CodeGenFunction : public CodeGenTypeCache {

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 {
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/CodeGen/ItaniumCXXABI.cpp
Expand Up @@ -469,6 +469,7 @@ class WebAssemblyCXXABI final : public ItaniumCXXABI {
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 {
Expand Down Expand Up @@ -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);
}
14 changes: 0 additions & 14 deletions clang/lib/CodeGen/MicrosoftCXXABI.cpp
Expand Up @@ -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
Expand Down

0 comments on commit c647919

Please sign in to comment.