WasmEHPrepare.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. //===-- WasmEHPrepare - Prepare excepton handling for WebAssembly --------===//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. //
  10. // This transformation is designed for use by code generators which use
  11. // WebAssembly exception handling scheme.
  12. //
  13. // WebAssembly exception handling uses Windows exception IR for the middle level
  14. // representation. This pass does the following transformation for every
  15. // catchpad block:
  16. // (In C-style pseudocode)
  17. //
  18. // - Before:
  19. // catchpad ...
  20. // exn = wasm.get.exception();
  21. // selector = wasm.get.selector();
  22. // ...
  23. //
  24. // - After:
  25. // catchpad ...
  26. // exn = wasm.catch(0); // 0 is a tag for C++
  27. // wasm.landingpad.index(index);
  28. // // Only add below in case it's not a single catch (...)
  29. // __wasm_lpad_context.lpad_index = index;
  30. // __wasm_lpad_context.lsda = wasm.lsda();
  31. // _Unwind_CallPersonality(exn);
  32. // int selector = __wasm.landingpad_context.selector;
  33. // ...
  34. //
  35. // Also, does the following for a cleanuppad block with a call to
  36. // __clang_call_terminate():
  37. // - Before:
  38. // cleanuppad ...
  39. // exn = wasm.get.exception();
  40. // __clang_call_terminate(exn);
  41. //
  42. // - After:
  43. // cleanuppad ...
  44. // exn = wasm.catch(0); // 0 is a tag for C++
  45. // __clang_call_terminate(exn);
  46. //
  47. //
  48. // * Background: WebAssembly EH instructions
  49. // WebAssembly's try and catch instructions are structured as follows:
  50. // try
  51. // instruction*
  52. // catch (C++ tag)
  53. // instruction*
  54. // ...
  55. // catch_all
  56. // instruction*
  57. // try_end
  58. //
  59. // A catch instruction in WebAssembly does not correspond to a C++ catch clause.
  60. // In WebAssembly, there is a single catch instruction for all C++ exceptions.
  61. // There can be more catch instructions for exceptions in other languages, but
  62. // they are not generated for now. catch_all catches all exceptions including
  63. // foreign exceptions (e.g. JavaScript). We turn catchpads into catch (C++ tag)
  64. // and cleanuppads into catch_all, with one exception: cleanuppad with a call to
  65. // __clang_call_terminate should be both in catch (C++ tag) and catch_all.
  66. //
  67. //
  68. // * Background: Direct personality function call
  69. // In WebAssembly EH, the VM is responsible for unwinding the stack once an
  70. // exception is thrown. After the stack is unwound, the control flow is
  71. // transfered to WebAssembly 'catch' instruction, which returns a caught
  72. // exception object.
  73. //
  74. // Unwinding the stack is not done by libunwind but the VM, so the personality
  75. // function in libcxxabi cannot be called from libunwind during the unwinding
  76. // process. So after a catch instruction, we insert a call to a wrapper function
  77. // in libunwind that in turn calls the real personality function.
  78. //
  79. // In Itanium EH, if the personality function decides there is no matching catch
  80. // clause in a call frame and no cleanup action to perform, the unwinder doesn't
  81. // stop there and continues unwinding. But in Wasm EH, the unwinder stops at
  82. // every call frame with a catch intruction, after which the personality
  83. // function is called from the compiler-generated user code here.
  84. //
  85. // In libunwind, we have this struct that serves as a communincation channel
  86. // between the compiler-generated user code and the personality function in
  87. // libcxxabi.
  88. //
  89. // struct _Unwind_LandingPadContext {
  90. // uintptr_t lpad_index;
  91. // uintptr_t lsda;
  92. // uintptr_t selector;
  93. // };
  94. // struct _Unwind_LandingPadContext __wasm_lpad_context = ...;
  95. //
  96. // And this wrapper in libunwind calls the personality function.
  97. //
  98. // _Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) {
  99. // struct _Unwind_Exception *exception_obj =
  100. // (struct _Unwind_Exception *)exception_ptr;
  101. // _Unwind_Reason_Code ret = __gxx_personality_v0(
  102. // 1, _UA_CLEANUP_PHASE, exception_obj->exception_class, exception_obj,
  103. // (struct _Unwind_Context *)__wasm_lpad_context);
  104. // return ret;
  105. // }
  106. //
  107. // We pass a landing pad index, and the address of LSDA for the current function
  108. // to the wrapper function _Unwind_CallPersonality in libunwind, and we retrieve
  109. // the selector after it returns.
  110. //
  111. //===----------------------------------------------------------------------===//
  112. #include "llvm/ADT/SetVector.h"
  113. #include "llvm/ADT/Statistic.h"
  114. #include "llvm/ADT/Triple.h"
  115. #include "llvm/CodeGen/Passes.h"
  116. #include "llvm/CodeGen/TargetLowering.h"
  117. #include "llvm/CodeGen/TargetSubtargetInfo.h"
  118. #include "llvm/CodeGen/WasmEHFuncInfo.h"
  119. #include "llvm/IR/Dominators.h"
  120. #include "llvm/IR/IRBuilder.h"
  121. #include "llvm/IR/Intrinsics.h"
  122. #include "llvm/Pass.h"
  123. #include "llvm/Transforms/Utils/BasicBlockUtils.h"
  124. using namespace llvm;
  125. #define DEBUG_TYPE "wasmehprepare"
  126. namespace {
  127. class WasmEHPrepare : public FunctionPass {
  128. Type *LPadContextTy = nullptr; // type of 'struct _Unwind_LandingPadContext'
  129. GlobalVariable *LPadContextGV = nullptr; // __wasm_lpad_context
  130. // Field addresses of struct _Unwind_LandingPadContext
  131. Value *LPadIndexField = nullptr; // lpad_index field
  132. Value *LSDAField = nullptr; // lsda field
  133. Value *SelectorField = nullptr; // selector
  134. Function *CatchF = nullptr; // wasm.catch.extract() intrinsic
  135. Function *LPadIndexF = nullptr; // wasm.landingpad.index() intrinsic
  136. Function *LSDAF = nullptr; // wasm.lsda() intrinsic
  137. Function *GetExnF = nullptr; // wasm.get.exception() intrinsic
  138. Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
  139. Function *CallPersonalityF = nullptr; // _Unwind_CallPersonality() wrapper
  140. Function *ClangCallTermF = nullptr; // __clang_call_terminate() function
  141. void prepareEHPad(BasicBlock *BB, unsigned Index);
  142. void prepareTerminateCleanupPad(BasicBlock *BB);
  143. public:
  144. static char ID; // Pass identification, replacement for typeid
  145. WasmEHPrepare() : FunctionPass(ID) {}
  146. bool doInitialization(Module &M) override;
  147. bool runOnFunction(Function &F) override;
  148. StringRef getPassName() const override {
  149. return "WebAssembly Exception handling preparation";
  150. }
  151. };
  152. } // end anonymous namespace
  153. char WasmEHPrepare::ID = 0;
  154. INITIALIZE_PASS(WasmEHPrepare, DEBUG_TYPE, "Prepare WebAssembly exceptions",
  155. false, false)
  156. FunctionPass *llvm::createWasmEHPass() { return new WasmEHPrepare(); }
  157. bool WasmEHPrepare::doInitialization(Module &M) {
  158. IRBuilder<> IRB(M.getContext());
  159. LPadContextTy = StructType::get(IRB.getInt32Ty(), // lpad_index
  160. IRB.getInt8PtrTy(), // lsda
  161. IRB.getInt32Ty() // selector
  162. );
  163. return false;
  164. }
  165. bool WasmEHPrepare::runOnFunction(Function &F) {
  166. SmallVector<BasicBlock *, 16> CatchPads;
  167. SmallVector<BasicBlock *, 16> CleanupPads;
  168. for (BasicBlock &BB : F) {
  169. if (!BB.isEHPad())
  170. continue;
  171. auto *Pad = BB.getFirstNonPHI();
  172. if (isa<CatchPadInst>(Pad))
  173. CatchPads.push_back(&BB);
  174. else if (isa<CleanupPadInst>(Pad))
  175. CleanupPads.push_back(&BB);
  176. }
  177. if (CatchPads.empty() && CleanupPads.empty())
  178. return false;
  179. assert(F.hasPersonalityFn() && "Personality function not found");
  180. Module &M = *F.getParent();
  181. IRBuilder<> IRB(F.getContext());
  182. // __wasm_lpad_context global variable
  183. LPadContextGV = cast<GlobalVariable>(
  184. M.getOrInsertGlobal("__wasm_lpad_context", LPadContextTy));
  185. LPadIndexField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 0,
  186. "lpad_index_gep");
  187. LSDAField =
  188. IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 1, "lsda_gep");
  189. SelectorField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 2,
  190. "selector_gep");
  191. // wasm.catch() intinsic, which will be lowered to wasm 'catch' instruction.
  192. CatchF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_catch);
  193. // wasm.landingpad.index() intrinsic, which is to specify landingpad index
  194. LPadIndexF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_landingpad_index);
  195. // wasm.lsda() intrinsic. Returns the address of LSDA table for the current
  196. // function.
  197. LSDAF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_lsda);
  198. // wasm.get.exception() and wasm.get.ehselector() intrinsics. Calls to these
  199. // are generated in clang.
  200. GetExnF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_exception);
  201. GetSelectorF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_ehselector);
  202. // _Unwind_CallPersonality() wrapper function, which calls the personality
  203. CallPersonalityF = cast<Function>(M.getOrInsertFunction(
  204. "_Unwind_CallPersonality", IRB.getInt32Ty(), IRB.getInt8PtrTy()));
  205. CallPersonalityF->setDoesNotThrow();
  206. // __clang_call_terminate() function, which is inserted by clang in case a
  207. // cleanup throws
  208. ClangCallTermF = M.getFunction("__clang_call_terminate");
  209. unsigned Index = 0;
  210. for (auto *BB : CatchPads) {
  211. auto *CPI = cast<CatchPadInst>(BB->getFirstNonPHI());
  212. // In case of a single catch (...), we don't need to emit LSDA
  213. if (CPI->getNumArgOperands() == 1 &&
  214. cast<Constant>(CPI->getArgOperand(0))->isNullValue())
  215. prepareEHPad(BB, -1);
  216. else
  217. prepareEHPad(BB, Index++);
  218. }
  219. if (!ClangCallTermF)
  220. return !CatchPads.empty();
  221. // Cleanuppads will turn into catch_all later, but cleanuppads with a call to
  222. // __clang_call_terminate() is a special case. __clang_call_terminate() takes
  223. // an exception object, so we have to duplicate call in both 'catch <C++ tag>'
  224. // and 'catch_all' clauses. Here we only insert a call to catch; the
  225. // duplication will be done later. In catch_all, the exception object will be
  226. // set to null.
  227. for (auto *BB : CleanupPads)
  228. for (auto &I : *BB)
  229. if (auto *CI = dyn_cast<CallInst>(&I))
  230. if (CI->getCalledValue() == ClangCallTermF)
  231. prepareEHPad(BB, -1);
  232. return true;
  233. }
  234. void WasmEHPrepare::prepareEHPad(BasicBlock *BB, unsigned Index) {
  235. assert(BB->isEHPad() && "BB is not an EHPad!");
  236. IRBuilder<> IRB(BB->getContext());
  237. IRB.SetInsertPoint(&*BB->getFirstInsertionPt());
  238. // The argument to wasm.catch() is the tag for C++ exceptions, which we set to
  239. // 0 for this module.
  240. // Pseudocode: void *exn = wasm.catch(0);
  241. Instruction *Exn = IRB.CreateCall(CatchF, IRB.getInt32(0), "exn");
  242. // Replace the return value of wasm.get.exception() with the return value from
  243. // wasm.catch().
  244. auto *FPI = cast<FuncletPadInst>(BB->getFirstNonPHI());
  245. Instruction *GetExnCI = nullptr, *GetSelectorCI = nullptr;
  246. for (auto &U : FPI->uses()) {
  247. if (auto *CI = dyn_cast<CallInst>(U.getUser())) {
  248. if (CI->getCalledValue() == GetExnF)
  249. GetExnCI = CI;
  250. else if (CI->getCalledValue() == GetSelectorF)
  251. GetSelectorCI = CI;
  252. }
  253. }
  254. assert(GetExnCI && "wasm.get.exception() call does not exist");
  255. GetExnCI->replaceAllUsesWith(Exn);
  256. GetExnCI->eraseFromParent();
  257. // In case it is a catchpad with single catch (...) or a cleanuppad, we don't
  258. // need to call personality function because we don't need a selector.
  259. if (FPI->getNumArgOperands() == 0 ||
  260. (FPI->getNumArgOperands() == 1 &&
  261. cast<Constant>(FPI->getArgOperand(0))->isNullValue())) {
  262. if (GetSelectorCI) {
  263. assert(GetSelectorCI->use_empty() &&
  264. "wasm.get.ehselector() still has uses!");
  265. GetSelectorCI->eraseFromParent();
  266. }
  267. return;
  268. }
  269. IRB.SetInsertPoint(Exn->getNextNode());
  270. // This is to create a map of <landingpad EH label, landingpad index> in
  271. // SelectionDAGISel, which is to be used in EHStreamer to emit LSDA tables.
  272. // Pseudocode: wasm.landingpad.index(Index);
  273. IRB.CreateCall(LPadIndexF, IRB.getInt32(Index));
  274. // Pseudocode: __wasm_lpad_context.lpad_index = index;
  275. IRB.CreateStore(IRB.getInt32(Index), LPadIndexField);
  276. // Store LSDA address only if this catchpad belongs to a top-level
  277. // catchswitch. If there is another catchpad that dominates this pad, we don't
  278. // need to store LSDA address again, because they are the same throughout the
  279. // function and have been already stored before.
  280. // TODO Can we not store LSDA address in user function but make libcxxabi
  281. // compute it?
  282. auto *CPI = cast<CatchPadInst>(FPI);
  283. if (isa<ConstantTokenNone>(CPI->getCatchSwitch()->getParentPad()))
  284. // Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
  285. IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
  286. // Pseudocode: _Unwind_CallPersonality(exn);
  287. CallInst *PersCI =
  288. IRB.CreateCall(CallPersonalityF, Exn, OperandBundleDef("funclet", CPI));
  289. PersCI->setDoesNotThrow();
  290. // Pseudocode: int selector = __wasm.landingpad_context.selector;
  291. Instruction *Selector = IRB.CreateLoad(SelectorField, "selector");
  292. // Replace the return value from wasm.get.ehselector() with the selector value
  293. // loaded from __wasm_lpad_context.selector.
  294. assert(GetSelectorCI && "wasm.get.ehselector() call does not exist");
  295. GetSelectorCI->replaceAllUsesWith(Selector);
  296. GetSelectorCI->eraseFromParent();
  297. }
  298. void llvm::calculateWasmEHInfo(const Function *F, WasmEHFuncInfo &EHInfo) {
  299. for (const auto &BB : *F) {
  300. if (!BB.isEHPad())
  301. continue;
  302. const Instruction *Pad = BB.getFirstNonPHI();
  303. // If an exception is not caught by a catchpad (i.e., it is a foreign
  304. // exception), it will unwind to its parent catchswitch's unwind
  305. // destination. We don't record an unwind destination for cleanuppads
  306. // because every exception should be caught by it.
  307. if (const auto *CatchPad = dyn_cast<CatchPadInst>(Pad)) {
  308. const auto *UnwindBB = CatchPad->getCatchSwitch()->getUnwindDest();
  309. if (!UnwindBB)
  310. continue;
  311. const Instruction *UnwindPad = UnwindBB->getFirstNonPHI();
  312. if (const auto *CatchSwitch = dyn_cast<CatchSwitchInst>(UnwindPad))
  313. // Currently there should be only one handler per a catchswitch.
  314. EHInfo.setEHPadUnwindDest(&BB, *CatchSwitch->handlers().begin());
  315. else // cleanuppad
  316. EHInfo.setEHPadUnwindDest(&BB, UnwindBB);
  317. }
  318. }
  319. // Record the unwind destination for invoke and cleanupret instructions.
  320. for (const auto &BB : *F) {
  321. const Instruction *TI = BB.getTerminator();
  322. BasicBlock *UnwindBB = nullptr;
  323. if (const auto *Invoke = dyn_cast<InvokeInst>(TI))
  324. UnwindBB = Invoke->getUnwindDest();
  325. else if (const auto *CleanupRet = dyn_cast<CleanupReturnInst>(TI))
  326. UnwindBB = CleanupRet->getUnwindDest();
  327. if (!UnwindBB)
  328. continue;
  329. const Instruction *UnwindPad = UnwindBB->getFirstNonPHI();
  330. if (const auto *CatchSwitch = dyn_cast<CatchSwitchInst>(UnwindPad))
  331. // Currently there should be only one handler per a catchswitch.
  332. EHInfo.setThrowUnwindDest(&BB, *CatchSwitch->handlers().begin());
  333. else // cleanuppad
  334. EHInfo.setThrowUnwindDest(&BB, UnwindBB);
  335. }
  336. }