WasmEHPrepare.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. //===-- WasmEHPrepare - Prepare excepton handling for WebAssembly --------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This transformation is designed for use by code generators which use
  10. // WebAssembly exception handling scheme. This currently supports C++
  11. // exceptions.
  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(WebAssembly::CPP_EXCEPTION);
  27. // // Only add below in case it's not a single catch (...)
  28. // wasm.landingpad.index(index);
  29. // __wasm_lpad_context.lpad_index = index;
  30. // __wasm_lpad_context.lsda = wasm.lsda();
  31. // _Unwind_CallPersonality(exn);
  32. // selector = __wasm_lpad_context.selector;
  33. // ...
  34. //
  35. //
  36. // * Background: Direct personality function call
  37. // In WebAssembly EH, the VM is responsible for unwinding the stack once an
  38. // exception is thrown. After the stack is unwound, the control flow is
  39. // transfered to WebAssembly 'catch' instruction.
  40. //
  41. // Unwinding the stack is not done by libunwind but the VM, so the personality
  42. // function in libcxxabi cannot be called from libunwind during the unwinding
  43. // process. So after a catch instruction, we insert a call to a wrapper function
  44. // in libunwind that in turn calls the real personality function.
  45. //
  46. // In Itanium EH, if the personality function decides there is no matching catch
  47. // clause in a call frame and no cleanup action to perform, the unwinder doesn't
  48. // stop there and continues unwinding. But in Wasm EH, the unwinder stops at
  49. // every call frame with a catch intruction, after which the personality
  50. // function is called from the compiler-generated user code here.
  51. //
  52. // In libunwind, we have this struct that serves as a communincation channel
  53. // between the compiler-generated user code and the personality function in
  54. // libcxxabi.
  55. //
  56. // struct _Unwind_LandingPadContext {
  57. // uintptr_t lpad_index;
  58. // uintptr_t lsda;
  59. // uintptr_t selector;
  60. // };
  61. // struct _Unwind_LandingPadContext __wasm_lpad_context = ...;
  62. //
  63. // And this wrapper in libunwind calls the personality function.
  64. //
  65. // _Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) {
  66. // struct _Unwind_Exception *exception_obj =
  67. // (struct _Unwind_Exception *)exception_ptr;
  68. // _Unwind_Reason_Code ret = __gxx_personality_v0(
  69. // 1, _UA_CLEANUP_PHASE, exception_obj->exception_class, exception_obj,
  70. // (struct _Unwind_Context *)__wasm_lpad_context);
  71. // return ret;
  72. // }
  73. //
  74. // We pass a landing pad index, and the address of LSDA for the current function
  75. // to the wrapper function _Unwind_CallPersonality in libunwind, and we retrieve
  76. // the selector after it returns.
  77. //
  78. //===----------------------------------------------------------------------===//
  79. #include "llvm/CodeGen/MachineBasicBlock.h"
  80. #include "llvm/CodeGen/Passes.h"
  81. #include "llvm/CodeGen/WasmEHFuncInfo.h"
  82. #include "llvm/IR/IRBuilder.h"
  83. #include "llvm/IR/IntrinsicsWebAssembly.h"
  84. #include "llvm/InitializePasses.h"
  85. #include "llvm/Transforms/Utils/BasicBlockUtils.h"
  86. using namespace llvm;
  87. #define DEBUG_TYPE "wasmehprepare"
  88. namespace {
  89. class WasmEHPrepare : public FunctionPass {
  90. Type *LPadContextTy = nullptr; // type of 'struct _Unwind_LandingPadContext'
  91. GlobalVariable *LPadContextGV = nullptr; // __wasm_lpad_context
  92. // Field addresses of struct _Unwind_LandingPadContext
  93. Value *LPadIndexField = nullptr; // lpad_index field
  94. Value *LSDAField = nullptr; // lsda field
  95. Value *SelectorField = nullptr; // selector
  96. Function *ThrowF = nullptr; // wasm.throw() intrinsic
  97. Function *LPadIndexF = nullptr; // wasm.landingpad.index() intrinsic
  98. Function *LSDAF = nullptr; // wasm.lsda() intrinsic
  99. Function *GetExnF = nullptr; // wasm.get.exception() intrinsic
  100. Function *CatchF = nullptr; // wasm.catch() intrinsic
  101. Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
  102. FunctionCallee CallPersonalityF =
  103. nullptr; // _Unwind_CallPersonality() wrapper
  104. bool prepareThrows(Function &F);
  105. bool prepareEHPads(Function &F);
  106. void prepareEHPad(BasicBlock *BB, bool NeedPersonality, unsigned Index = 0);
  107. public:
  108. static char ID; // Pass identification, replacement for typeid
  109. WasmEHPrepare() : FunctionPass(ID) {}
  110. bool doInitialization(Module &M) override;
  111. bool runOnFunction(Function &F) override;
  112. StringRef getPassName() const override {
  113. return "WebAssembly Exception handling preparation";
  114. }
  115. };
  116. } // end anonymous namespace
  117. char WasmEHPrepare::ID = 0;
  118. INITIALIZE_PASS_BEGIN(WasmEHPrepare, DEBUG_TYPE,
  119. "Prepare WebAssembly exceptions", false, false)
  120. INITIALIZE_PASS_END(WasmEHPrepare, DEBUG_TYPE, "Prepare WebAssembly exceptions",
  121. false, false)
  122. FunctionPass *llvm::createWasmEHPass() { return new WasmEHPrepare(); }
  123. bool WasmEHPrepare::doInitialization(Module &M) {
  124. IRBuilder<> IRB(M.getContext());
  125. LPadContextTy = StructType::get(IRB.getInt32Ty(), // lpad_index
  126. IRB.getInt8PtrTy(), // lsda
  127. IRB.getInt32Ty() // selector
  128. );
  129. return false;
  130. }
  131. // Erase the specified BBs if the BB does not have any remaining predecessors,
  132. // and also all its dead children.
  133. template <typename Container>
  134. static void eraseDeadBBsAndChildren(const Container &BBs) {
  135. SmallVector<BasicBlock *, 8> WL(BBs.begin(), BBs.end());
  136. while (!WL.empty()) {
  137. auto *BB = WL.pop_back_val();
  138. if (!pred_empty(BB))
  139. continue;
  140. WL.append(succ_begin(BB), succ_end(BB));
  141. DeleteDeadBlock(BB);
  142. }
  143. }
  144. bool WasmEHPrepare::runOnFunction(Function &F) {
  145. bool Changed = false;
  146. Changed |= prepareThrows(F);
  147. Changed |= prepareEHPads(F);
  148. return Changed;
  149. }
  150. bool WasmEHPrepare::prepareThrows(Function &F) {
  151. Module &M = *F.getParent();
  152. IRBuilder<> IRB(F.getContext());
  153. bool Changed = false;
  154. // wasm.throw() intinsic, which will be lowered to wasm 'throw' instruction.
  155. ThrowF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_throw);
  156. // Insert an unreachable instruction after a call to @llvm.wasm.throw and
  157. // delete all following instructions within the BB, and delete all the dead
  158. // children of the BB as well.
  159. for (User *U : ThrowF->users()) {
  160. // A call to @llvm.wasm.throw() is only generated from __cxa_throw()
  161. // builtin call within libcxxabi, and cannot be an InvokeInst.
  162. auto *ThrowI = cast<CallInst>(U);
  163. if (ThrowI->getFunction() != &F)
  164. continue;
  165. Changed = true;
  166. auto *BB = ThrowI->getParent();
  167. SmallVector<BasicBlock *, 4> Succs(successors(BB));
  168. BB->erase(std::next(BasicBlock::iterator(ThrowI)), BB->end());
  169. IRB.SetInsertPoint(BB);
  170. IRB.CreateUnreachable();
  171. eraseDeadBBsAndChildren(Succs);
  172. }
  173. return Changed;
  174. }
  175. bool WasmEHPrepare::prepareEHPads(Function &F) {
  176. Module &M = *F.getParent();
  177. IRBuilder<> IRB(F.getContext());
  178. SmallVector<BasicBlock *, 16> CatchPads;
  179. SmallVector<BasicBlock *, 16> CleanupPads;
  180. for (BasicBlock &BB : F) {
  181. if (!BB.isEHPad())
  182. continue;
  183. auto *Pad = BB.getFirstNonPHI();
  184. if (isa<CatchPadInst>(Pad))
  185. CatchPads.push_back(&BB);
  186. else if (isa<CleanupPadInst>(Pad))
  187. CleanupPads.push_back(&BB);
  188. }
  189. if (CatchPads.empty() && CleanupPads.empty())
  190. return false;
  191. assert(F.hasPersonalityFn() && "Personality function not found");
  192. // __wasm_lpad_context global variable.
  193. // This variable should be thread local. If the target does not support TLS,
  194. // we depend on CoalesceFeaturesAndStripAtomics to downgrade it to
  195. // non-thread-local ones, in which case we don't allow this object to be
  196. // linked with other objects using shared memory.
  197. LPadContextGV = cast<GlobalVariable>(
  198. M.getOrInsertGlobal("__wasm_lpad_context", LPadContextTy));
  199. LPadContextGV->setThreadLocalMode(GlobalValue::GeneralDynamicTLSModel);
  200. LPadIndexField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 0,
  201. "lpad_index_gep");
  202. LSDAField =
  203. IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 1, "lsda_gep");
  204. SelectorField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 2,
  205. "selector_gep");
  206. // wasm.landingpad.index() intrinsic, which is to specify landingpad index
  207. LPadIndexF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_landingpad_index);
  208. // wasm.lsda() intrinsic. Returns the address of LSDA table for the current
  209. // function.
  210. LSDAF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_lsda);
  211. // wasm.get.exception() and wasm.get.ehselector() intrinsics. Calls to these
  212. // are generated in clang.
  213. GetExnF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_exception);
  214. GetSelectorF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_ehselector);
  215. // wasm.catch() will be lowered down to wasm 'catch' instruction in
  216. // instruction selection.
  217. CatchF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_catch);
  218. // _Unwind_CallPersonality() wrapper function, which calls the personality
  219. CallPersonalityF = M.getOrInsertFunction(
  220. "_Unwind_CallPersonality", IRB.getInt32Ty(), IRB.getInt8PtrTy());
  221. if (Function *F = dyn_cast<Function>(CallPersonalityF.getCallee()))
  222. F->setDoesNotThrow();
  223. unsigned Index = 0;
  224. for (auto *BB : CatchPads) {
  225. auto *CPI = cast<CatchPadInst>(BB->getFirstNonPHI());
  226. // In case of a single catch (...), we don't need to emit a personalify
  227. // function call
  228. if (CPI->arg_size() == 1 &&
  229. cast<Constant>(CPI->getArgOperand(0))->isNullValue())
  230. prepareEHPad(BB, false);
  231. else
  232. prepareEHPad(BB, true, Index++);
  233. }
  234. // Cleanup pads don't need a personality function call.
  235. for (auto *BB : CleanupPads)
  236. prepareEHPad(BB, false);
  237. return true;
  238. }
  239. // Prepare an EH pad for Wasm EH handling. If NeedPersonality is false, Index is
  240. // ignored.
  241. void WasmEHPrepare::prepareEHPad(BasicBlock *BB, bool NeedPersonality,
  242. unsigned Index) {
  243. assert(BB->isEHPad() && "BB is not an EHPad!");
  244. IRBuilder<> IRB(BB->getContext());
  245. IRB.SetInsertPoint(&*BB->getFirstInsertionPt());
  246. auto *FPI = cast<FuncletPadInst>(BB->getFirstNonPHI());
  247. Instruction *GetExnCI = nullptr, *GetSelectorCI = nullptr;
  248. for (auto &U : FPI->uses()) {
  249. if (auto *CI = dyn_cast<CallInst>(U.getUser())) {
  250. if (CI->getCalledOperand() == GetExnF)
  251. GetExnCI = CI;
  252. if (CI->getCalledOperand() == GetSelectorF)
  253. GetSelectorCI = CI;
  254. }
  255. }
  256. // Cleanup pads do not have any of wasm.get.exception() or
  257. // wasm.get.ehselector() calls. We need to do nothing.
  258. if (!GetExnCI) {
  259. assert(!GetSelectorCI &&
  260. "wasm.get.ehselector() cannot exist w/o wasm.get.exception()");
  261. return;
  262. }
  263. // Replace wasm.get.exception intrinsic with wasm.catch intrinsic, which will
  264. // be lowered to wasm 'catch' instruction. We do this mainly because
  265. // instruction selection cannot handle wasm.get.exception intrinsic's token
  266. // argument.
  267. Instruction *CatchCI =
  268. IRB.CreateCall(CatchF, {IRB.getInt32(WebAssembly::CPP_EXCEPTION)}, "exn");
  269. GetExnCI->replaceAllUsesWith(CatchCI);
  270. GetExnCI->eraseFromParent();
  271. // In case it is a catchpad with single catch (...) or a cleanuppad, we don't
  272. // need to call personality function because we don't need a selector.
  273. if (!NeedPersonality) {
  274. if (GetSelectorCI) {
  275. assert(GetSelectorCI->use_empty() &&
  276. "wasm.get.ehselector() still has uses!");
  277. GetSelectorCI->eraseFromParent();
  278. }
  279. return;
  280. }
  281. IRB.SetInsertPoint(CatchCI->getNextNode());
  282. // This is to create a map of <landingpad EH label, landingpad index> in
  283. // SelectionDAGISel, which is to be used in EHStreamer to emit LSDA tables.
  284. // Pseudocode: wasm.landingpad.index(Index);
  285. IRB.CreateCall(LPadIndexF, {FPI, IRB.getInt32(Index)});
  286. // Pseudocode: __wasm_lpad_context.lpad_index = index;
  287. IRB.CreateStore(IRB.getInt32(Index), LPadIndexField);
  288. auto *CPI = cast<CatchPadInst>(FPI);
  289. // TODO Sometimes storing the LSDA address every time is not necessary, in
  290. // case it is already set in a dominating EH pad and there is no function call
  291. // between from that EH pad to here. Consider optimizing those cases.
  292. // Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
  293. IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
  294. // Pseudocode: _Unwind_CallPersonality(exn);
  295. CallInst *PersCI = IRB.CreateCall(CallPersonalityF, CatchCI,
  296. OperandBundleDef("funclet", CPI));
  297. PersCI->setDoesNotThrow();
  298. // Pseudocode: int selector = __wasm_lpad_context.selector;
  299. Instruction *Selector =
  300. IRB.CreateLoad(IRB.getInt32Ty(), SelectorField, "selector");
  301. // Replace the return value from wasm.get.ehselector() with the selector value
  302. // loaded from __wasm_lpad_context.selector.
  303. assert(GetSelectorCI && "wasm.get.ehselector() call does not exist");
  304. GetSelectorCI->replaceAllUsesWith(Selector);
  305. GetSelectorCI->eraseFromParent();
  306. }
  307. void llvm::calculateWasmEHInfo(const Function *F, WasmEHFuncInfo &EHInfo) {
  308. // If an exception is not caught by a catchpad (i.e., it is a foreign
  309. // exception), it will unwind to its parent catchswitch's unwind destination.
  310. // We don't record an unwind destination for cleanuppads because every
  311. // exception should be caught by it.
  312. for (const auto &BB : *F) {
  313. if (!BB.isEHPad())
  314. continue;
  315. const Instruction *Pad = BB.getFirstNonPHI();
  316. if (const auto *CatchPad = dyn_cast<CatchPadInst>(Pad)) {
  317. const auto *UnwindBB = CatchPad->getCatchSwitch()->getUnwindDest();
  318. if (!UnwindBB)
  319. continue;
  320. const Instruction *UnwindPad = UnwindBB->getFirstNonPHI();
  321. if (const auto *CatchSwitch = dyn_cast<CatchSwitchInst>(UnwindPad))
  322. // Currently there should be only one handler per a catchswitch.
  323. EHInfo.setUnwindDest(&BB, *CatchSwitch->handlers().begin());
  324. else // cleanuppad
  325. EHInfo.setUnwindDest(&BB, UnwindBB);
  326. }
  327. }
  328. }