diff options
Diffstat (limited to 'llvm/lib/CodeGen')
-rw-r--r-- | llvm/lib/CodeGen/AsmPrinter/WasmException.cpp | 4 | ||||
-rw-r--r-- | llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp | 38 | ||||
-rw-r--r-- | llvm/lib/CodeGen/WasmEHPrepare.cpp | 169 |
3 files changed, 105 insertions, 106 deletions
diff --git a/llvm/lib/CodeGen/AsmPrinter/WasmException.cpp b/llvm/lib/CodeGen/AsmPrinter/WasmException.cpp index 3f863ae4d69..444b0ed17b6 100644 --- a/llvm/lib/CodeGen/AsmPrinter/WasmException.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/WasmException.cpp @@ -18,10 +18,10 @@ using namespace llvm; void WasmException::endModule() { - // This is the symbol used in 'throw' and 'if_except' instruction to denote + // This is the symbol used in 'throw' and 'br_on_exn' instruction to denote // this is a C++ exception. This symbol has to be emitted somewhere once in // the module. Check if the symbol has already been created, i.e., we have at - // least one 'throw' or 'if_except' instruction in the module, and emit the + // least one 'throw' or 'br_on_exn' instruction in the module, and emit the // symbol only if so. SmallString<60> NameStr; Mangler::getNameWithPrefix(NameStr, "__cpp_exception", Asm->getDataLayout()); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 9849725ad1f..155d65f127e 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -1456,6 +1456,36 @@ void SelectionDAGBuilder::visitCleanupPad(const CleanupPadInst &CPI) { } } +// For wasm, there's alwyas a single catch pad attached to a catchswitch, and +// the control flow always stops at the single catch pad, as it does for a +// cleanup pad. In case the exception caught is not of the types the catch pad +// catches, it will be rethrown by a rethrow. +static void findWasmUnwindDestinations( + FunctionLoweringInfo &FuncInfo, const BasicBlock *EHPadBB, + BranchProbability Prob, + SmallVectorImpl<std::pair<MachineBasicBlock *, BranchProbability>> + &UnwindDests) { + while (EHPadBB) { + const Instruction *Pad = EHPadBB->getFirstNonPHI(); + if (isa<CleanupPadInst>(Pad)) { + // Stop on cleanup pads. + UnwindDests.emplace_back(FuncInfo.MBBMap[EHPadBB], Prob); + UnwindDests.back().first->setIsEHScopeEntry(); + break; + } else if (auto *CatchSwitch = dyn_cast<CatchSwitchInst>(Pad)) { + // Add the catchpad handlers to the possible destinations. We don't + // continue to the unwind destination of the catchswitch for wasm. + for (const BasicBlock *CatchPadBB : CatchSwitch->handlers()) { + UnwindDests.emplace_back(FuncInfo.MBBMap[CatchPadBB], Prob); + UnwindDests.back().first->setIsEHScopeEntry(); + } + break; + } else { + continue; + } + } +} + /// When an invoke or a cleanupret unwinds to the next EH pad, there are /// many places it could ultimately go. In the IR, we have a single unwind /// destination, but in the machine CFG, we enumerate all the possible blocks. @@ -1476,6 +1506,11 @@ static void findUnwindDestinations( bool IsWasmCXX = Personality == EHPersonality::Wasm_CXX; bool IsSEH = isAsynchronousEHPersonality(Personality); + if (IsWasmCXX) { + findWasmUnwindDestinations(FuncInfo, EHPadBB, Prob, UnwindDests); + return; + } + while (EHPadBB) { const Instruction *Pad = EHPadBB->getFirstNonPHI(); BasicBlock *NewEHPadBB = nullptr; @@ -1488,8 +1523,7 @@ static void findUnwindDestinations( // personalities. UnwindDests.emplace_back(FuncInfo.MBBMap[EHPadBB], Prob); UnwindDests.back().first->setIsEHScopeEntry(); - if (!IsWasmCXX) - UnwindDests.back().first->setIsEHFuncletEntry(); + UnwindDests.back().first->setIsEHFuncletEntry(); break; } else if (auto *CatchSwitch = dyn_cast<CatchSwitchInst>(Pad)) { // Add the catchpad handlers to the possible destinations. diff --git a/llvm/lib/CodeGen/WasmEHPrepare.cpp b/llvm/lib/CodeGen/WasmEHPrepare.cpp index cc78cb8cc58..271f3d4568d 100644 --- a/llvm/lib/CodeGen/WasmEHPrepare.cpp +++ b/llvm/lib/CodeGen/WasmEHPrepare.cpp @@ -7,7 +7,8 @@ //===----------------------------------------------------------------------===// // // This transformation is designed for use by code generators which use -// WebAssembly exception handling scheme. +// WebAssembly exception handling scheme. This currently supports C++ +// exceptions. // // WebAssembly exception handling uses Windows exception IR for the middle level // representation. This pass does the following transformation for every @@ -22,53 +23,20 @@ // // - After: // catchpad ... -// exn = wasm.catch(0); // 0 is a tag for C++ -// wasm.landingpad.index(index); +// exn = wasm.extract.exception(); // // Only add below in case it's not a single catch (...) +// wasm.landingpad.index(index); // __wasm_lpad_context.lpad_index = index; // __wasm_lpad_context.lsda = wasm.lsda(); // _Unwind_CallPersonality(exn); -// int selector = __wasm.landingpad_context.selector; +// selector = __wasm.landingpad_context.selector; // ... // -// Also, does the following for a cleanuppad block with a call to -// __clang_call_terminate(): -// - Before: -// cleanuppad ... -// exn = wasm.get.exception(); -// __clang_call_terminate(exn); -// -// - After: -// cleanuppad ... -// exn = wasm.catch(0); // 0 is a tag for C++ -// __clang_call_terminate(exn); -// -// -// * Background: WebAssembly EH instructions -// WebAssembly's try and catch instructions are structured as follows: -// try -// instruction* -// catch (C++ tag) -// instruction* -// ... -// catch_all -// instruction* -// try_end -// -// A catch instruction in WebAssembly does not correspond to a C++ catch clause. -// In WebAssembly, there is a single catch instruction for all C++ exceptions. -// There can be more catch instructions for exceptions in other languages, but -// they are not generated for now. catch_all catches all exceptions including -// foreign exceptions (e.g. JavaScript). We turn catchpads into catch (C++ tag) -// and cleanuppads into catch_all, with one exception: cleanuppad with a call to -// __clang_call_terminate should be both in catch (C++ tag) and catch_all. -// // // * Background: Direct personality function call // In WebAssembly EH, the VM is responsible for unwinding the stack once an // exception is thrown. After the stack is unwound, the control flow is -// transfered to WebAssembly 'catch' instruction, which returns a caught -// exception object. +// transfered to WebAssembly 'catch' instruction. // // Unwinding the stack is not done by libunwind but the VM, so the personality // function in libcxxabi cannot be called from libunwind during the unwinding @@ -137,18 +105,18 @@ class WasmEHPrepare : public FunctionPass { Value *SelectorField = nullptr; // selector Function *ThrowF = nullptr; // wasm.throw() intrinsic - Function *CatchF = nullptr; // wasm.catch.extract() intrinsic + Function *RethrowF = nullptr; // wasm.rethrow() intrinsic Function *LPadIndexF = nullptr; // wasm.landingpad.index() intrinsic Function *LSDAF = nullptr; // wasm.lsda() intrinsic Function *GetExnF = nullptr; // wasm.get.exception() intrinsic + Function *ExtractExnF = nullptr; // wasm.extract.exception() intrinsic Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic Function *CallPersonalityF = nullptr; // _Unwind_CallPersonality() wrapper - Function *ClangCallTermF = nullptr; // __clang_call_terminate() function bool prepareEHPads(Function &F); bool prepareThrows(Function &F); - void prepareEHPad(BasicBlock *BB, unsigned Index); + void prepareEHPad(BasicBlock *BB, bool NeedLSDA, unsigned Index = 0); void prepareTerminateCleanupPad(BasicBlock *BB); public: @@ -208,25 +176,29 @@ bool WasmEHPrepare::prepareThrows(Function &F) { // wasm.throw() intinsic, which will be lowered to wasm 'throw' instruction. ThrowF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_throw); - - // Insert an unreachable instruction after a call to @llvm.wasm.throw and - // delete all following instructions within the BB, and delete all the dead - // children of the BB as well. - for (User *U : ThrowF->users()) { - // A call to @llvm.wasm.throw() is only generated from - // __builtin_wasm_throw() builtin call within libcxxabi, and cannot be an - // InvokeInst. - auto *ThrowI = cast<CallInst>(U); - if (ThrowI->getFunction() != &F) - continue; - Changed = true; - auto *BB = ThrowI->getParent(); - SmallVector<BasicBlock *, 4> Succs(succ_begin(BB), succ_end(BB)); - auto &InstList = BB->getInstList(); - InstList.erase(std::next(BasicBlock::iterator(ThrowI)), InstList.end()); - IRB.SetInsertPoint(BB); - IRB.CreateUnreachable(); - eraseDeadBBsAndChildren(Succs); + // wasm.rethrow() intinsic, which will be lowered to wasm 'rethrow' + // instruction. + RethrowF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_rethrow); + + // Insert an unreachable instruction after a call to @llvm.wasm.throw / + // @llvm.wasm.rethrow and delete all following instructions within the BB, and + // delete all the dead children of the BB as well. + for (auto L : {ThrowF->users(), RethrowF->users()}) { + for (User *U : L) { + // A call to @llvm.wasm.throw() is only generated from __cxa_throw() + // builtin call within libcxxabi, and cannot be an InvokeInst. + auto *ThrowI = cast<CallInst>(U); + if (ThrowI->getFunction() != &F) + continue; + Changed = true; + auto *BB = ThrowI->getParent(); + SmallVector<BasicBlock *, 4> Succs(succ_begin(BB), succ_end(BB)); + auto &InstList = BB->getInstList(); + InstList.erase(std::next(BasicBlock::iterator(ThrowI)), InstList.end()); + IRB.SetInsertPoint(BB); + IRB.CreateUnreachable(); + eraseDeadBBsAndChildren(Succs); + } } return Changed; @@ -262,8 +234,6 @@ bool WasmEHPrepare::prepareEHPads(Function &F) { SelectorField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 2, "selector_gep"); - // wasm.catch() intinsic, which will be lowered to wasm 'catch' instruction. - CatchF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_catch); // wasm.landingpad.index() intrinsic, which is to specify landingpad index LPadIndexF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_landingpad_index); // wasm.lsda() intrinsic. Returns the address of LSDA table for the current @@ -274,75 +244,70 @@ bool WasmEHPrepare::prepareEHPads(Function &F) { GetExnF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_exception); GetSelectorF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_ehselector); + // wasm.extract.exception() is the same as wasm.get.exception() but it does + // not take a token argument. This will be lowered down to EXTRACT_EXCEPTION + // pseudo instruction in instruction selection, which will be expanded using + // 'br_on_exn' instruction later. + ExtractExnF = + Intrinsic::getDeclaration(&M, Intrinsic::wasm_extract_exception); + // _Unwind_CallPersonality() wrapper function, which calls the personality CallPersonalityF = cast<Function>(M.getOrInsertFunction( "_Unwind_CallPersonality", IRB.getInt32Ty(), IRB.getInt8PtrTy())); CallPersonalityF->setDoesNotThrow(); - // __clang_call_terminate() function, which is inserted by clang in case a - // cleanup throws - ClangCallTermF = M.getFunction("__clang_call_terminate"); - unsigned Index = 0; for (auto *BB : CatchPads) { auto *CPI = cast<CatchPadInst>(BB->getFirstNonPHI()); // In case of a single catch (...), we don't need to emit LSDA if (CPI->getNumArgOperands() == 1 && cast<Constant>(CPI->getArgOperand(0))->isNullValue()) - prepareEHPad(BB, -1); + prepareEHPad(BB, false); else - prepareEHPad(BB, Index++); + prepareEHPad(BB, true, Index++); } - if (!ClangCallTermF) - return !CatchPads.empty(); - - // Cleanuppads will turn into catch_all later, but cleanuppads with a call to - // __clang_call_terminate() is a special case. __clang_call_terminate() takes - // an exception object, so we have to duplicate call in both 'catch <C++ tag>' - // and 'catch_all' clauses. Here we only insert a call to catch; the - // duplication will be done later. In catch_all, the exception object will be - // set to null. + // Cleanup pads don't need LSDA. for (auto *BB : CleanupPads) - for (auto &I : *BB) - if (auto *CI = dyn_cast<CallInst>(&I)) - if (CI->getCalledValue() == ClangCallTermF) - prepareEHPad(BB, -1); + prepareEHPad(BB, false); return true; } -void WasmEHPrepare::prepareEHPad(BasicBlock *BB, unsigned Index) { +// Prepare an EH pad for Wasm EH handling. If NeedLSDA is false, Index is +// ignored. +void WasmEHPrepare::prepareEHPad(BasicBlock *BB, bool NeedLSDA, + unsigned Index) { assert(BB->isEHPad() && "BB is not an EHPad!"); IRBuilder<> IRB(BB->getContext()); - IRB.SetInsertPoint(&*BB->getFirstInsertionPt()); - // The argument to wasm.catch() is the tag for C++ exceptions, which we set to - // 0 for this module. - // Pseudocode: void *exn = wasm.catch(0); - Instruction *Exn = IRB.CreateCall(CatchF, IRB.getInt32(0), "exn"); - // Replace the return value of wasm.get.exception() with the return value from - // wasm.catch(). + auto *FPI = cast<FuncletPadInst>(BB->getFirstNonPHI()); Instruction *GetExnCI = nullptr, *GetSelectorCI = nullptr; for (auto &U : FPI->uses()) { if (auto *CI = dyn_cast<CallInst>(U.getUser())) { if (CI->getCalledValue() == GetExnF) GetExnCI = CI; - else if (CI->getCalledValue() == GetSelectorF) + if (CI->getCalledValue() == GetSelectorF) GetSelectorCI = CI; } } - assert(GetExnCI && "wasm.get.exception() call does not exist"); - GetExnCI->replaceAllUsesWith(Exn); + // Cleanup pads w/o __clang_call_terminate call do not have any of + // wasm.get.exception() or wasm.get.ehselector() calls. We need to do nothing. + if (!GetExnCI) { + assert(!GetSelectorCI && + "wasm.get.ehselector() cannot exist w/o wasm.get.exception()"); + return; + } + + Instruction *ExtractExnCI = IRB.CreateCall(ExtractExnF, {}, "exn"); + GetExnCI->replaceAllUsesWith(ExtractExnCI); GetExnCI->eraseFromParent(); // In case it is a catchpad with single catch (...) or a cleanuppad, we don't // need to call personality function because we don't need a selector. - if (FPI->getNumArgOperands() == 0 || - (FPI->getNumArgOperands() == 1 && - cast<Constant>(FPI->getArgOperand(0))->isNullValue())) { + if (!NeedLSDA) { if (GetSelectorCI) { assert(GetSelectorCI->use_empty() && "wasm.get.ehselector() still has uses!"); @@ -350,7 +315,7 @@ void WasmEHPrepare::prepareEHPad(BasicBlock *BB, unsigned Index) { } return; } - IRB.SetInsertPoint(Exn->getNextNode()); + IRB.SetInsertPoint(ExtractExnCI->getNextNode()); // This is to create a map of <landingpad EH label, landingpad index> in // SelectionDAGISel, which is to be used in EHStreamer to emit LSDA tables. @@ -372,8 +337,8 @@ void WasmEHPrepare::prepareEHPad(BasicBlock *BB, unsigned Index) { IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField); // Pseudocode: _Unwind_CallPersonality(exn); - CallInst *PersCI = - IRB.CreateCall(CallPersonalityF, Exn, OperandBundleDef("funclet", CPI)); + CallInst *PersCI = IRB.CreateCall(CallPersonalityF, ExtractExnCI, + OperandBundleDef("funclet", CPI)); PersCI->setDoesNotThrow(); // Pseudocode: int selector = __wasm.landingpad_context.selector; @@ -387,15 +352,15 @@ void WasmEHPrepare::prepareEHPad(BasicBlock *BB, unsigned Index) { } void llvm::calculateWasmEHInfo(const Function *F, WasmEHFuncInfo &EHInfo) { + // If an exception is not caught by a catchpad (i.e., it is a foreign + // exception), it will unwind to its parent catchswitch's unwind destination. + // We don't record an unwind destination for cleanuppads because every + // exception should be caught by it. for (const auto &BB : *F) { if (!BB.isEHPad()) continue; const Instruction *Pad = BB.getFirstNonPHI(); - // If an exception is not caught by a catchpad (i.e., it is a foreign - // exception), it will unwind to its parent catchswitch's unwind - // destination. We don't record an unwind destination for cleanuppads - // because every exception should be caught by it. if (const auto *CatchPad = dyn_cast<CatchPadInst>(Pad)) { const auto *UnwindBB = CatchPad->getCatchSwitch()->getUnwindDest(); if (!UnwindBB) |