WasmEHPrepare.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  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/TargetLowering.h"
  80. #include "llvm/CodeGen/TargetSubtargetInfo.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. auto &InstList = BB->getInstList();
  169. InstList.erase(std::next(BasicBlock::iterator(ThrowI)), InstList.end());
  170. IRB.SetInsertPoint(BB);
  171. IRB.CreateUnreachable();
  172. eraseDeadBBsAndChildren(Succs);
  173. }
  174. return Changed;
  175. }
  176. bool WasmEHPrepare::prepareEHPads(Function &F) {
  177. Module &M = *F.getParent();
  178. IRBuilder<> IRB(F.getContext());
  179. SmallVector<BasicBlock *, 16> CatchPads;
  180. SmallVector<BasicBlock *, 16> CleanupPads;
  181. for (BasicBlock &BB : F) {
  182. if (!BB.isEHPad())
  183. continue;
  184. auto *Pad = BB.getFirstNonPHI();
  185. if (isa<CatchPadInst>(Pad))
  186. CatchPads.push_back(&BB);
  187. else if (isa<CleanupPadInst>(Pad))
  188. CleanupPads.push_back(&BB);
  189. }
  190. if (CatchPads.empty() && CleanupPads.empty())
  191. return false;
  192. assert(F.hasPersonalityFn() && "Personality function not found");
  193. // __wasm_lpad_context global variable
  194. LPadContextGV = cast<GlobalVariable>(
  195. M.getOrInsertGlobal("__wasm_lpad_context", LPadContextTy));
  196. LPadIndexField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 0,
  197. "lpad_index_gep");
  198. LSDAField =
  199. IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 1, "lsda_gep");
  200. SelectorField = IRB.CreateConstGEP2_32(LPadContextTy, LPadContextGV, 0, 2,
  201. "selector_gep");
  202. // wasm.landingpad.index() intrinsic, which is to specify landingpad index
  203. LPadIndexF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_landingpad_index);
  204. // wasm.lsda() intrinsic. Returns the address of LSDA table for the current
  205. // function.
  206. LSDAF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_lsda);
  207. // wasm.get.exception() and wasm.get.ehselector() intrinsics. Calls to these
  208. // are generated in clang.
  209. GetExnF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_exception);
  210. GetSelectorF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_get_ehselector);
  211. // wasm.catch() will be lowered down to wasm 'catch' instruction in
  212. // instruction selection.
  213. CatchF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_catch);
  214. // _Unwind_CallPersonality() wrapper function, which calls the personality
  215. CallPersonalityF = M.getOrInsertFunction(
  216. "_Unwind_CallPersonality", IRB.getInt32Ty(), IRB.getInt8PtrTy());
  217. if (Function *F = dyn_cast<Function>(CallPersonalityF.getCallee()))
  218. F->setDoesNotThrow();
  219. unsigned Index = 0;
  220. for (auto *BB : CatchPads) {
  221. auto *CPI = cast<CatchPadInst>(BB->getFirstNonPHI());
  222. // In case of a single catch (...), we don't need to emit a personalify
  223. // function call
  224. if (CPI->getNumArgOperands() == 1 &&
  225. cast<Constant>(CPI->getArgOperand(0))->isNullValue())
  226. prepareEHPad(BB, false);
  227. else
  228. prepareEHPad(BB, true, Index++);
  229. }
  230. // Cleanup pads don't need a personality function call.
  231. for (auto *BB : CleanupPads)
  232. prepareEHPad(BB, false);
  233. return true;
  234. }
  235. // Prepare an EH pad for Wasm EH handling. If NeedPersonality is false, Index is
  236. // ignored.
  237. void WasmEHPrepare::prepareEHPad(BasicBlock *BB, bool NeedPersonality,
  238. unsigned Index) {
  239. assert(BB->isEHPad() && "BB is not an EHPad!");
  240. IRBuilder<> IRB(BB->getContext());
  241. IRB.SetInsertPoint(&*BB->getFirstInsertionPt());
  242. auto *FPI = cast<FuncletPadInst>(BB->getFirstNonPHI());
  243. Instruction *GetExnCI = nullptr, *GetSelectorCI = nullptr;
  244. for (auto &U : FPI->uses()) {
  245. if (auto *CI = dyn_cast<CallInst>(U.getUser())) {
  246. if (CI->getCalledOperand() == GetExnF)
  247. GetExnCI = CI;
  248. if (CI->getCalledOperand() == GetSelectorF)
  249. GetSelectorCI = CI;
  250. }
  251. }
  252. // Cleanup pads do not have any of wasm.get.exception() or
  253. // wasm.get.ehselector() calls. We need to do nothing.
  254. if (!GetExnCI) {
  255. assert(!GetSelectorCI &&
  256. "wasm.get.ehselector() cannot exist w/o wasm.get.exception()");
  257. return;
  258. }
  259. // Replace wasm.get.exception intrinsic with wasm.catch intrinsic, which will
  260. // be lowered to wasm 'catch' instruction. We do this mainly because
  261. // instruction selection cannot handle wasm.get.exception intrinsic's token
  262. // argument.
  263. Instruction *CatchCI =
  264. IRB.CreateCall(CatchF, {IRB.getInt32(WebAssembly::CPP_EXCEPTION)}, "exn");
  265. GetExnCI->replaceAllUsesWith(CatchCI);
  266. GetExnCI->eraseFromParent();
  267. // In case it is a catchpad with single catch (...) or a cleanuppad, we don't
  268. // need to call personality function because we don't need a selector.
  269. if (!NeedPersonality) {
  270. if (GetSelectorCI) {
  271. assert(GetSelectorCI->use_empty() &&
  272. "wasm.get.ehselector() still has uses!");
  273. GetSelectorCI->eraseFromParent();
  274. }
  275. return;
  276. }
  277. IRB.SetInsertPoint(CatchCI->getNextNode());
  278. // This is to create a map of <landingpad EH label, landingpad index> in
  279. // SelectionDAGISel, which is to be used in EHStreamer to emit LSDA tables.
  280. // Pseudocode: wasm.landingpad.index(Index);
  281. IRB.CreateCall(LPadIndexF, {FPI, IRB.getInt32(Index)});
  282. // Pseudocode: __wasm_lpad_context.lpad_index = index;
  283. IRB.CreateStore(IRB.getInt32(Index), LPadIndexField);
  284. auto *CPI = cast<CatchPadInst>(FPI);
  285. // TODO Sometimes storing the LSDA address every time is not necessary, in
  286. // case it is already set in a dominating EH pad and there is no function call
  287. // between from that EH pad to here. Consider optimizing those cases.
  288. // Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
  289. IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
  290. // Pseudocode: _Unwind_CallPersonality(exn);
  291. CallInst *PersCI = IRB.CreateCall(CallPersonalityF, CatchCI,
  292. OperandBundleDef("funclet", CPI));
  293. PersCI->setDoesNotThrow();
  294. // Pseudocode: int selector = __wasm_lpad_context.selector;
  295. Instruction *Selector =
  296. IRB.CreateLoad(IRB.getInt32Ty(), SelectorField, "selector");
  297. // Replace the return value from wasm.get.ehselector() with the selector value
  298. // loaded from __wasm_lpad_context.selector.
  299. assert(GetSelectorCI && "wasm.get.ehselector() call does not exist");
  300. GetSelectorCI->replaceAllUsesWith(Selector);
  301. GetSelectorCI->eraseFromParent();
  302. }
  303. void llvm::calculateWasmEHInfo(const Function *F, WasmEHFuncInfo &EHInfo) {
  304. // If an exception is not caught by a catchpad (i.e., it is a foreign
  305. // exception), it will unwind to its parent catchswitch's unwind destination.
  306. // We don't record an unwind destination for cleanuppads because every
  307. // exception should be caught by it.
  308. for (const auto &BB : *F) {
  309. if (!BB.isEHPad())
  310. continue;
  311. const Instruction *Pad = BB.getFirstNonPHI();
  312. if (const auto *CatchPad = dyn_cast<CatchPadInst>(Pad)) {
  313. const auto *UnwindBB = CatchPad->getCatchSwitch()->getUnwindDest();
  314. if (!UnwindBB)
  315. continue;
  316. const Instruction *UnwindPad = UnwindBB->getFirstNonPHI();
  317. if (const auto *CatchSwitch = dyn_cast<CatchSwitchInst>(UnwindPad))
  318. // Currently there should be only one handler per a catchswitch.
  319. EHInfo.setUnwindDest(&BB, *CatchSwitch->handlers().begin());
  320. else // cleanuppad
  321. EHInfo.setUnwindDest(&BB, UnwindBB);
  322. }
  323. }
  324. }