123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- //===-- WasmEHPrepare - Prepare excepton handling for WebAssembly --------===//
- //
- // The LLVM Compiler Infrastructure
- //
- // This file is distributed under the University of Illinois Open Source
- // License. See LICENSE.TXT for details.
- //
- //===----------------------------------------------------------------------===//
- //
- // This transformation is designed for use by code generators which use
- // WebAssembly exception handling scheme.
- //
- // WebAssembly exception handling uses Windows exception IR for the middle level
- // representation. This pass does the following transformation for every
- // catchpad block:
- // (In C-style pseudocode)
- //
- // - Before:
- // catchpad ...
- // exn = wasm.get.exception();
- // selector = wasm.get.selector();
- // ...
- //
- // - After:
- // catchpad ...
- // exn = wasm.catch(0); // 0 is a tag for C++
- // wasm.landingpad.index(index);
- // // Only add below in case it's not a single catch (...)
- // __wasm_lpad_context.lpad_index = index;
- // __wasm_lpad_context.lsda = wasm.lsda();
- // _Unwind_CallPersonality(exn);
- // int 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.
- //
- // 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
- // process. So after a catch instruction, we insert a call to a wrapper function
- // in libunwind that in turn calls the real personality function.
- //
- // In Itanium EH, if the personality function decides there is no matching catch
- // clause in a call frame and no cleanup action to perform, the unwinder doesn't
- // stop there and continues unwinding. But in Wasm EH, the unwinder stops at
- // every call frame with a catch intruction, after which the personality
- // function is called from the compiler-generated user code here.
- //
- // In libunwind, we have this struct that serves as a communincation channel
- // between the compiler-generated user code and the personality function in
- // libcxxabi.
- //
- // struct _Unwind_LandingPadContext {
- // uintptr_t lpad_index;
- // uintptr_t lsda;
- // uintptr_t selector;
- // };
- // struct _Unwind_LandingPadContext __wasm_lpad_context = ...;
- //
- // And this wrapper in libunwind calls the personality function.
- //
- // _Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) {
- // struct _Unwind_Exception *exception_obj =
- // (struct _Unwind_Exception *)exception_ptr;
- // _Unwind_Reason_Code ret = __gxx_personality_v0(
- // 1, _UA_CLEANUP_PHASE, exception_obj->exception_class, exception_obj,
- // (struct _Unwind_Context *)__wasm_lpad_context);
- // return ret;
- // }
- //
- // We pass a landing pad index, and the address of LSDA for the current function
- // to the wrapper function _Unwind_CallPersonality in libunwind, and we retrieve
- // the selector after it returns.
- //
- //===----------------------------------------------------------------------===//
- #include "llvm/ADT/SetVector.h"
- #include "llvm/ADT/Statistic.h"
- #include "llvm/ADT/Triple.h"
- #include "llvm/CodeGen/Passes.h"
- #include "llvm/CodeGen/TargetLowering.h"
- #include "llvm/CodeGen/TargetSubtargetInfo.h"
- #include "llvm/CodeGen/WasmEHFuncInfo.h"
- #include "llvm/IR/Dominators.h"
- #include "llvm/IR/IRBuilder.h"
- #include "llvm/IR/Intrinsics.h"
- #include "llvm/Pass.h"
- #include "llvm/Transforms/Utils/BasicBlockUtils.h"
- using namespace llvm;
- #define DEBUG_TYPE "wasmehprepare"
- namespace {
- class WasmEHPrepare : public FunctionPass {
- Type *LPadContextTy = nullptr; // type of 'struct _Unwind_LandingPadContext'
- GlobalVariable *LPadContextGV = nullptr; // __wasm_lpad_context
- // Field addresses of struct _Unwind_LandingPadContext
- Value *LPadIndexField = nullptr; // lpad_index field
- Value *LSDAField = nullptr; // lsda field
- Value *SelectorField = nullptr; // selector
- Function *CatchF = nullptr; // wasm.catch.extract() intrinsic
- Function *LPadIndexF = nullptr; // wasm.landingpad.index() intrinsic
- Function *LSDAF = nullptr; // wasm.lsda() intrinsic
- Function *GetExnF = nullptr; // wasm.get.exception() intrinsic
- Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
- Function *CallPersonalityF = nullptr; // _Unwind_CallPersonality() wrapper
- Function *ClangCallTermF = nullptr; // __clang_call_terminate() function
- void prepareEHPad(BasicBlock *BB, unsigned Index);
- void prepareTerminateCleanupPad(BasicBlock *BB);
- public:
- static char ID; // Pass identification, replacement for typeid
- WasmEHPrepare() : FunctionPass(ID) {}
- bool doInitialization(Module &M) override;
- bool runOnFunction(Function &F) override;
- StringRef getPassName() const override {
- return "WebAssembly Exception handling preparation";
- }
- };
- } // end anonymous namespace
- char WasmEHPrepare::ID = 0;
- INITIALIZE_PASS(WasmEHPrepare, DEBUG_TYPE, "Prepare WebAssembly exceptions",
- false, false)
- FunctionPass *llvm::createWasmEHPass() { return new WasmEHPrepare(); }
- bool WasmEHPrepare::doInitialization(Module &M) {
- IRBuilder<> IRB(M.getContext());
- LPadContextTy = StructType::get(IRB.getInt32Ty(), // lpad_index
- IRB.getInt8PtrTy(), // lsda
- IRB.getInt32Ty() // selector
- );
- return false;
- }
- bool WasmEHPrepare::runOnFunction(Function &F) {
- SmallVector<BasicBlock *, 16> CatchPads;
- SmallVector<BasicBlock *, 16> CleanupPads;
- for (BasicBlock &BB : F) {
- if (!BB.isEHPad())
- continue;
- auto *Pad = BB.getFirstNonPHI();
- if (isa<CatchPadInst>(Pad))
- CatchPads.push_back(&BB);
- else if (isa<CleanupPadInst>(Pad))
- CleanupPads.push_back(&BB);
- }
- if (CatchPads.empty() && CleanupPads.empty())
- return false;
- assert(F.hasPersonalityFn() && "Personality function not found");
- Module &M = *F.getParent();
- IRBuilder<> IRB(F.getContext());
- // __wasm_lpad_context global variable
- LPadContextGV = cast<GlobalVariable>(
- M.getOrInsertGlobal("__wasm_lpad_context", LPadContextTy));
- LPadIndexField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 0,
- "lpad_index_gep");
- LSDAField =
- IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 1, "lsda_gep");
- 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
- // function.
- LSDAF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_lsda);
- // wasm.get.exception() and wasm.get.ehselector() intrinsics. Calls to these
- // are generated in clang.
- GetExnF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_exception);
- GetSelectorF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_ehselector);
- // _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);
- else
- prepareEHPad(BB, 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.
- for (auto *BB : CleanupPads)
- for (auto &I : *BB)
- if (auto *CI = dyn_cast<CallInst>(&I))
- if (CI->getCalledValue() == ClangCallTermF)
- prepareEHPad(BB, -1);
- return true;
- }
- void WasmEHPrepare::prepareEHPad(BasicBlock *BB, 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)
- GetSelectorCI = CI;
- }
- }
- assert(GetExnCI && "wasm.get.exception() call does not exist");
- GetExnCI->replaceAllUsesWith(Exn);
- 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 (GetSelectorCI) {
- assert(GetSelectorCI->use_empty() &&
- "wasm.get.ehselector() still has uses!");
- GetSelectorCI->eraseFromParent();
- }
- return;
- }
- IRB.SetInsertPoint(Exn->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.
- // Pseudocode: wasm.landingpad.index(Index);
- IRB.CreateCall(LPadIndexF, IRB.getInt32(Index));
- // Pseudocode: __wasm_lpad_context.lpad_index = index;
- IRB.CreateStore(IRB.getInt32(Index), LPadIndexField);
- // Store LSDA address only if this catchpad belongs to a top-level
- // catchswitch. If there is another catchpad that dominates this pad, we don't
- // need to store LSDA address again, because they are the same throughout the
- // function and have been already stored before.
- // TODO Can we not store LSDA address in user function but make libcxxabi
- // compute it?
- auto *CPI = cast<CatchPadInst>(FPI);
- if (isa<ConstantTokenNone>(CPI->getCatchSwitch()->getParentPad()))
- // Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
- IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
- // Pseudocode: _Unwind_CallPersonality(exn);
- CallInst *PersCI =
- IRB.CreateCall(CallPersonalityF, Exn, OperandBundleDef("funclet", CPI));
- PersCI->setDoesNotThrow();
- // Pseudocode: int selector = __wasm.landingpad_context.selector;
- Instruction *Selector = IRB.CreateLoad(SelectorField, "selector");
- // Replace the return value from wasm.get.ehselector() with the selector value
- // loaded from __wasm_lpad_context.selector.
- assert(GetSelectorCI && "wasm.get.ehselector() call does not exist");
- GetSelectorCI->replaceAllUsesWith(Selector);
- GetSelectorCI->eraseFromParent();
- }
- void llvm::calculateWasmEHInfo(const Function *F, WasmEHFuncInfo &EHInfo) {
- 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)
- continue;
- const Instruction *UnwindPad = UnwindBB->getFirstNonPHI();
- if (const auto *CatchSwitch = dyn_cast<CatchSwitchInst>(UnwindPad))
- // Currently there should be only one handler per a catchswitch.
- EHInfo.setEHPadUnwindDest(&BB, *CatchSwitch->handlers().begin());
- else // cleanuppad
- EHInfo.setEHPadUnwindDest(&BB, UnwindBB);
- }
- }
- // Record the unwind destination for invoke and cleanupret instructions.
- for (const auto &BB : *F) {
- const Instruction *TI = BB.getTerminator();
- BasicBlock *UnwindBB = nullptr;
- if (const auto *Invoke = dyn_cast<InvokeInst>(TI))
- UnwindBB = Invoke->getUnwindDest();
- else if (const auto *CleanupRet = dyn_cast<CleanupReturnInst>(TI))
- UnwindBB = CleanupRet->getUnwindDest();
- if (!UnwindBB)
- continue;
- const Instruction *UnwindPad = UnwindBB->getFirstNonPHI();
- if (const auto *CatchSwitch = dyn_cast<CatchSwitchInst>(UnwindPad))
- // Currently there should be only one handler per a catchswitch.
- EHInfo.setThrowUnwindDest(&BB, *CatchSwitch->handlers().begin());
- else // cleanuppad
- EHInfo.setThrowUnwindDest(&BB, UnwindBB);
- }
- }
|