WasmEHPrepare.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  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.extract.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.landingpad_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/ADT/SetVector.h"
  80. #include "llvm/ADT/Statistic.h"
  81. #include "llvm/ADT/Triple.h"
  82. #include "llvm/CodeGen/Passes.h"
  83. #include "llvm/CodeGen/TargetLowering.h"
  84. #include "llvm/CodeGen/TargetSubtargetInfo.h"
  85. #include "llvm/CodeGen/WasmEHFuncInfo.h"
  86. #include "llvm/IR/Dominators.h"
  87. #include "llvm/IR/IRBuilder.h"
  88. #include "llvm/IR/Intrinsics.h"
  89. #include "llvm/Pass.h"
  90. #include "llvm/Transforms/Utils/BasicBlockUtils.h"
  91. using namespace llvm;
  92. #define DEBUG_TYPE "wasmehprepare"
  93. namespace {
  94. class WasmEHPrepare : public FunctionPass {
  95. Type *LPadContextTy = nullptr; // type of 'struct _Unwind_LandingPadContext'
  96. GlobalVariable *LPadContextGV = nullptr; // __wasm_lpad_context
  97. // Field addresses of struct _Unwind_LandingPadContext
  98. Value *LPadIndexField = nullptr; // lpad_index field
  99. Value *LSDAField = nullptr; // lsda field
  100. Value *SelectorField = nullptr; // selector
  101. Function *ThrowF = nullptr; // wasm.throw() intrinsic
  102. Function *LPadIndexF = nullptr; // wasm.landingpad.index() intrinsic
  103. Function *LSDAF = nullptr; // wasm.lsda() intrinsic
  104. Function *GetExnF = nullptr; // wasm.get.exception() intrinsic
  105. Function *ExtractExnF = nullptr; // wasm.extract.exception() intrinsic
  106. Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
  107. FunctionCallee CallPersonalityF =
  108. nullptr; // _Unwind_CallPersonality() wrapper
  109. bool prepareEHPads(Function &F);
  110. bool prepareThrows(Function &F);
  111. void prepareEHPad(BasicBlock *BB, bool NeedLSDA, unsigned Index = 0);
  112. void prepareTerminateCleanupPad(BasicBlock *BB);
  113. public:
  114. static char ID; // Pass identification, replacement for typeid
  115. WasmEHPrepare() : FunctionPass(ID) {}
  116. bool doInitialization(Module &M) override;
  117. bool runOnFunction(Function &F) override;
  118. StringRef getPassName() const override {
  119. return "WebAssembly Exception handling preparation";
  120. }
  121. };
  122. } // end anonymous namespace
  123. char WasmEHPrepare::ID = 0;
  124. INITIALIZE_PASS(WasmEHPrepare, DEBUG_TYPE, "Prepare WebAssembly exceptions",
  125. false, false)
  126. FunctionPass *llvm::createWasmEHPass() { return new WasmEHPrepare(); }
  127. bool WasmEHPrepare::doInitialization(Module &M) {
  128. IRBuilder<> IRB(M.getContext());
  129. LPadContextTy = StructType::get(IRB.getInt32Ty(), // lpad_index
  130. IRB.getInt8PtrTy(), // lsda
  131. IRB.getInt32Ty() // selector
  132. );
  133. return false;
  134. }
  135. // Erase the specified BBs if the BB does not have any remaining predecessors,
  136. // and also all its dead children.
  137. template <typename Container>
  138. static void eraseDeadBBsAndChildren(const Container &BBs) {
  139. SmallVector<BasicBlock *, 8> WL(BBs.begin(), BBs.end());
  140. while (!WL.empty()) {
  141. auto *BB = WL.pop_back_val();
  142. if (pred_begin(BB) != pred_end(BB))
  143. continue;
  144. WL.append(succ_begin(BB), succ_end(BB));
  145. DeleteDeadBlock(BB);
  146. }
  147. }
  148. bool WasmEHPrepare::runOnFunction(Function &F) {
  149. bool Changed = false;
  150. Changed |= prepareThrows(F);
  151. Changed |= prepareEHPads(F);
  152. return Changed;
  153. }
  154. bool WasmEHPrepare::prepareThrows(Function &F) {
  155. Module &M = *F.getParent();
  156. IRBuilder<> IRB(F.getContext());
  157. bool Changed = false;
  158. // wasm.throw() intinsic, which will be lowered to wasm 'throw' instruction.
  159. ThrowF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_throw);
  160. // Insert an unreachable instruction after a call to @llvm.wasm.throw and
  161. // delete all following instructions within the BB, and delete all the dead
  162. // children of the BB as well.
  163. for (User *U : ThrowF->users()) {
  164. // A call to @llvm.wasm.throw() is only generated from __cxa_throw()
  165. // builtin call within libcxxabi, and cannot be an InvokeInst.
  166. auto *ThrowI = cast<CallInst>(U);
  167. if (ThrowI->getFunction() != &F)
  168. continue;
  169. Changed = true;
  170. auto *BB = ThrowI->getParent();
  171. SmallVector<BasicBlock *, 4> Succs(succ_begin(BB), succ_end(BB));
  172. auto &InstList = BB->getInstList();
  173. InstList.erase(std::next(BasicBlock::iterator(ThrowI)), InstList.end());
  174. IRB.SetInsertPoint(BB);
  175. IRB.CreateUnreachable();
  176. eraseDeadBBsAndChildren(Succs);
  177. }
  178. return Changed;
  179. }
  180. bool WasmEHPrepare::prepareEHPads(Function &F) {
  181. Module &M = *F.getParent();
  182. IRBuilder<> IRB(F.getContext());
  183. SmallVector<BasicBlock *, 16> CatchPads;
  184. SmallVector<BasicBlock *, 16> CleanupPads;
  185. for (BasicBlock &BB : F) {
  186. if (!BB.isEHPad())
  187. continue;
  188. auto *Pad = BB.getFirstNonPHI();
  189. if (isa<CatchPadInst>(Pad))
  190. CatchPads.push_back(&BB);
  191. else if (isa<CleanupPadInst>(Pad))
  192. CleanupPads.push_back(&BB);
  193. }
  194. if (CatchPads.empty() && CleanupPads.empty())
  195. return false;
  196. assert(F.hasPersonalityFn() && "Personality function not found");
  197. // __wasm_lpad_context global variable
  198. LPadContextGV = cast<GlobalVariable>(
  199. M.getOrInsertGlobal("__wasm_lpad_context", LPadContextTy));
  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.extract.exception() is the same as wasm.get.exception() but it does
  216. // not take a token argument. This will be lowered down to EXTRACT_EXCEPTION
  217. // pseudo instruction in instruction selection, which will be expanded using
  218. // 'br_on_exn' instruction later.
  219. ExtractExnF =
  220. Intrinsic::getDeclaration(&M, Intrinsic::wasm_extract_exception);
  221. // _Unwind_CallPersonality() wrapper function, which calls the personality
  222. CallPersonalityF = M.getOrInsertFunction(
  223. "_Unwind_CallPersonality", IRB.getInt32Ty(), IRB.getInt8PtrTy());
  224. if (Function *F = dyn_cast<Function>(CallPersonalityF.getCallee()))
  225. F->setDoesNotThrow();
  226. unsigned Index = 0;
  227. for (auto *BB : CatchPads) {
  228. auto *CPI = cast<CatchPadInst>(BB->getFirstNonPHI());
  229. // In case of a single catch (...), we don't need to emit LSDA
  230. if (CPI->getNumArgOperands() == 1 &&
  231. cast<Constant>(CPI->getArgOperand(0))->isNullValue())
  232. prepareEHPad(BB, false);
  233. else
  234. prepareEHPad(BB, true, Index++);
  235. }
  236. // Cleanup pads don't need LSDA.
  237. for (auto *BB : CleanupPads)
  238. prepareEHPad(BB, false);
  239. return true;
  240. }
  241. // Prepare an EH pad for Wasm EH handling. If NeedLSDA is false, Index is
  242. // ignored.
  243. void WasmEHPrepare::prepareEHPad(BasicBlock *BB, bool NeedLSDA,
  244. unsigned Index) {
  245. assert(BB->isEHPad() && "BB is not an EHPad!");
  246. IRBuilder<> IRB(BB->getContext());
  247. IRB.SetInsertPoint(&*BB->getFirstInsertionPt());
  248. auto *FPI = cast<FuncletPadInst>(BB->getFirstNonPHI());
  249. Instruction *GetExnCI = nullptr, *GetSelectorCI = nullptr;
  250. for (auto &U : FPI->uses()) {
  251. if (auto *CI = dyn_cast<CallInst>(U.getUser())) {
  252. if (CI->getCalledValue() == GetExnF)
  253. GetExnCI = CI;
  254. if (CI->getCalledValue() == GetSelectorF)
  255. GetSelectorCI = CI;
  256. }
  257. }
  258. // Cleanup pads w/o __clang_call_terminate call do not have any of
  259. // wasm.get.exception() or wasm.get.ehselector() calls. We need to do nothing.
  260. if (!GetExnCI) {
  261. assert(!GetSelectorCI &&
  262. "wasm.get.ehselector() cannot exist w/o wasm.get.exception()");
  263. return;
  264. }
  265. Instruction *ExtractExnCI = IRB.CreateCall(ExtractExnF, {}, "exn");
  266. GetExnCI->replaceAllUsesWith(ExtractExnCI);
  267. GetExnCI->eraseFromParent();
  268. // In case it is a catchpad with single catch (...) or a cleanuppad, we don't
  269. // need to call personality function because we don't need a selector.
  270. if (!NeedLSDA) {
  271. if (GetSelectorCI) {
  272. assert(GetSelectorCI->use_empty() &&
  273. "wasm.get.ehselector() still has uses!");
  274. GetSelectorCI->eraseFromParent();
  275. }
  276. return;
  277. }
  278. IRB.SetInsertPoint(ExtractExnCI->getNextNode());
  279. // This is to create a map of <landingpad EH label, landingpad index> in
  280. // SelectionDAGISel, which is to be used in EHStreamer to emit LSDA tables.
  281. // Pseudocode: wasm.landingpad.index(Index);
  282. IRB.CreateCall(LPadIndexF, {FPI, IRB.getInt32(Index)});
  283. // Pseudocode: __wasm_lpad_context.lpad_index = index;
  284. IRB.CreateStore(IRB.getInt32(Index), LPadIndexField);
  285. // Store LSDA address only if this catchpad belongs to a top-level
  286. // catchswitch. If there is another catchpad that dominates this pad, we don't
  287. // need to store LSDA address again, because they are the same throughout the
  288. // function and have been already stored before.
  289. // TODO Can we not store LSDA address in user function but make libcxxabi
  290. // compute it?
  291. auto *CPI = cast<CatchPadInst>(FPI);
  292. if (isa<ConstantTokenNone>(CPI->getCatchSwitch()->getParentPad()))
  293. // Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
  294. IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
  295. // Pseudocode: _Unwind_CallPersonality(exn);
  296. CallInst *PersCI = IRB.CreateCall(CallPersonalityF, ExtractExnCI,
  297. OperandBundleDef("funclet", CPI));
  298. PersCI->setDoesNotThrow();
  299. // Pseudocode: int selector = __wasm.landingpad_context.selector;
  300. Instruction *Selector =
  301. IRB.CreateLoad(IRB.getInt32Ty(), SelectorField, "selector");
  302. // Replace the return value from wasm.get.ehselector() with the selector value
  303. // loaded from __wasm_lpad_context.selector.
  304. assert(GetSelectorCI && "wasm.get.ehselector() call does not exist");
  305. GetSelectorCI->replaceAllUsesWith(Selector);
  306. GetSelectorCI->eraseFromParent();
  307. }
  308. void llvm::calculateWasmEHInfo(const Function *F, WasmEHFuncInfo &EHInfo) {
  309. // If an exception is not caught by a catchpad (i.e., it is a foreign
  310. // exception), it will unwind to its parent catchswitch's unwind destination.
  311. // We don't record an unwind destination for cleanuppads because every
  312. // exception should be caught by it.
  313. for (const auto &BB : *F) {
  314. if (!BB.isEHPad())
  315. continue;
  316. const Instruction *Pad = BB.getFirstNonPHI();
  317. if (const auto *CatchPad = dyn_cast<CatchPadInst>(Pad)) {
  318. const auto *UnwindBB = CatchPad->getCatchSwitch()->getUnwindDest();
  319. if (!UnwindBB)
  320. continue;
  321. const Instruction *UnwindPad = UnwindBB->getFirstNonPHI();
  322. if (const auto *CatchSwitch = dyn_cast<CatchSwitchInst>(UnwindPad))
  323. // Currently there should be only one handler per a catchswitch.
  324. EHInfo.setEHPadUnwindDest(&BB, *CatchSwitch->handlers().begin());
  325. else // cleanuppad
  326. EHInfo.setEHPadUnwindDest(&BB, UnwindBB);
  327. }
  328. }
  329. }