WebAssemblyLateEHPrepare.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. //=== WebAssemblyLateEHPrepare.cpp - WebAssembly Exception Preparation -===//
  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. /// \file
  10. /// \brief Does various transformations for exception handling.
  11. ///
  12. //===----------------------------------------------------------------------===//
  13. #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
  14. #include "Utils/WebAssemblyUtilities.h"
  15. #include "WebAssembly.h"
  16. #include "WebAssemblySubtarget.h"
  17. #include "llvm/ADT/SmallPtrSet.h"
  18. #include "llvm/CodeGen/MachineFunctionPass.h"
  19. #include "llvm/CodeGen/MachineInstrBuilder.h"
  20. #include "llvm/CodeGen/WasmEHFuncInfo.h"
  21. #include "llvm/MC/MCAsmInfo.h"
  22. #include "llvm/Support/Debug.h"
  23. #include "llvm/Target/TargetMachine.h"
  24. using namespace llvm;
  25. #define DEBUG_TYPE "wasm-late-eh-prepare"
  26. namespace {
  27. class WebAssemblyLateEHPrepare final : public MachineFunctionPass {
  28. StringRef getPassName() const override {
  29. return "WebAssembly Late Prepare Exception";
  30. }
  31. bool runOnMachineFunction(MachineFunction &MF) override;
  32. bool removeUnreachableEHPads(MachineFunction &MF);
  33. void recordCatchRetBBs(MachineFunction &MF);
  34. bool hoistCatches(MachineFunction &MF);
  35. bool addCatchAlls(MachineFunction &MF);
  36. bool replaceFuncletReturns(MachineFunction &MF);
  37. bool removeUnnecessaryUnreachables(MachineFunction &MF);
  38. bool restoreStackPointer(MachineFunction &MF);
  39. MachineBasicBlock *getMatchingEHPad(MachineInstr *MI);
  40. SmallPtrSet<MachineBasicBlock *, 8> CatchRetBBs;
  41. public:
  42. static char ID; // Pass identification, replacement for typeid
  43. WebAssemblyLateEHPrepare() : MachineFunctionPass(ID) {}
  44. };
  45. } // end anonymous namespace
  46. char WebAssemblyLateEHPrepare::ID = 0;
  47. INITIALIZE_PASS(WebAssemblyLateEHPrepare, DEBUG_TYPE,
  48. "WebAssembly Late Exception Preparation", false, false)
  49. FunctionPass *llvm::createWebAssemblyLateEHPrepare() {
  50. return new WebAssemblyLateEHPrepare();
  51. }
  52. // Returns the nearest EH pad that dominates this instruction. This does not use
  53. // dominator analysis; it just does BFS on its predecessors until arriving at an
  54. // EH pad. This assumes valid EH scopes so the first EH pad it arrives in all
  55. // possible search paths should be the same.
  56. // Returns nullptr in case it does not find any EH pad in the search, or finds
  57. // multiple different EH pads.
  58. MachineBasicBlock *
  59. WebAssemblyLateEHPrepare::getMatchingEHPad(MachineInstr *MI) {
  60. MachineFunction *MF = MI->getParent()->getParent();
  61. SmallVector<MachineBasicBlock *, 2> WL;
  62. SmallPtrSet<MachineBasicBlock *, 2> Visited;
  63. WL.push_back(MI->getParent());
  64. MachineBasicBlock *EHPad = nullptr;
  65. while (!WL.empty()) {
  66. MachineBasicBlock *MBB = WL.pop_back_val();
  67. if (!Visited.insert(MBB).second)
  68. continue;
  69. if (MBB->isEHPad()) {
  70. if (EHPad && EHPad != MBB)
  71. return nullptr;
  72. EHPad = MBB;
  73. continue;
  74. }
  75. if (MBB == &MF->front())
  76. return nullptr;
  77. for (auto *Pred : MBB->predecessors())
  78. if (!CatchRetBBs.count(Pred)) // We don't go into child scopes
  79. WL.push_back(Pred);
  80. }
  81. return EHPad;
  82. }
  83. // Erase the specified BBs if the BB does not have any remaining predecessors,
  84. // and also all its dead children.
  85. template <typename Container>
  86. static void eraseDeadBBsAndChildren(const Container &MBBs) {
  87. SmallVector<MachineBasicBlock *, 8> WL(MBBs.begin(), MBBs.end());
  88. SmallPtrSet<MachineBasicBlock *, 8> Deleted;
  89. while (!WL.empty()) {
  90. MachineBasicBlock *MBB = WL.pop_back_val();
  91. if (Deleted.count(MBB) || !MBB->pred_empty())
  92. continue;
  93. SmallVector<MachineBasicBlock *, 4> Succs(MBB->successors());
  94. WL.append(MBB->succ_begin(), MBB->succ_end());
  95. for (auto *Succ : Succs)
  96. MBB->removeSuccessor(Succ);
  97. // To prevent deleting the same BB multiple times, which can happen when
  98. // 'MBBs' contain both a parent and a child
  99. Deleted.insert(MBB);
  100. MBB->eraseFromParent();
  101. }
  102. }
  103. bool WebAssemblyLateEHPrepare::runOnMachineFunction(MachineFunction &MF) {
  104. LLVM_DEBUG(dbgs() << "********** Late EH Prepare **********\n"
  105. "********** Function: "
  106. << MF.getName() << '\n');
  107. if (MF.getTarget().getMCAsmInfo()->getExceptionHandlingType() !=
  108. ExceptionHandling::Wasm)
  109. return false;
  110. bool Changed = false;
  111. if (MF.getFunction().hasPersonalityFn()) {
  112. Changed |= removeUnreachableEHPads(MF);
  113. recordCatchRetBBs(MF);
  114. Changed |= hoistCatches(MF);
  115. Changed |= addCatchAlls(MF);
  116. Changed |= replaceFuncletReturns(MF);
  117. }
  118. Changed |= removeUnnecessaryUnreachables(MF);
  119. if (MF.getFunction().hasPersonalityFn())
  120. Changed |= restoreStackPointer(MF);
  121. return Changed;
  122. }
  123. // Remove unreachable EH pads and its children. If they remain, CFG
  124. // stackification can be tricky.
  125. bool WebAssemblyLateEHPrepare::removeUnreachableEHPads(MachineFunction &MF) {
  126. SmallVector<MachineBasicBlock *, 4> ToDelete;
  127. for (auto &MBB : MF)
  128. if (MBB.isEHPad() && MBB.pred_empty())
  129. ToDelete.push_back(&MBB);
  130. eraseDeadBBsAndChildren(ToDelete);
  131. return !ToDelete.empty();
  132. }
  133. // Record which BB ends with catchret instruction, because this will be replaced
  134. // with 'br's later. This set of catchret BBs is necessary in 'getMatchingEHPad'
  135. // function.
  136. void WebAssemblyLateEHPrepare::recordCatchRetBBs(MachineFunction &MF) {
  137. CatchRetBBs.clear();
  138. for (auto &MBB : MF) {
  139. auto Pos = MBB.getFirstTerminator();
  140. if (Pos == MBB.end())
  141. continue;
  142. MachineInstr *TI = &*Pos;
  143. if (TI->getOpcode() == WebAssembly::CATCHRET)
  144. CatchRetBBs.insert(&MBB);
  145. }
  146. }
  147. // Hoist catch instructions to the beginning of their matching EH pad BBs in
  148. // case,
  149. // (1) catch instruction is not the first instruction in EH pad.
  150. // ehpad:
  151. // some_other_instruction
  152. // ...
  153. // %exn = catch 0
  154. // (2) catch instruction is in a non-EH pad BB. For example,
  155. // ehpad:
  156. // br bb0
  157. // bb0:
  158. // %exn = catch 0
  159. bool WebAssemblyLateEHPrepare::hoistCatches(MachineFunction &MF) {
  160. bool Changed = false;
  161. SmallVector<MachineInstr *, 16> Catches;
  162. for (auto &MBB : MF)
  163. for (auto &MI : MBB)
  164. if (WebAssembly::isCatch(MI.getOpcode()))
  165. Catches.push_back(&MI);
  166. for (auto *Catch : Catches) {
  167. MachineBasicBlock *EHPad = getMatchingEHPad(Catch);
  168. assert(EHPad && "No matching EH pad for catch");
  169. auto InsertPos = EHPad->begin();
  170. // Skip EH_LABELs in the beginning of an EH pad if present. We don't use
  171. // these labels at the moment, but other targets also seem to have an
  172. // EH_LABEL instruction in the beginning of an EH pad.
  173. while (InsertPos != EHPad->end() && InsertPos->isEHLabel())
  174. InsertPos++;
  175. if (InsertPos == Catch)
  176. continue;
  177. Changed = true;
  178. EHPad->insert(InsertPos, Catch->removeFromParent());
  179. }
  180. return Changed;
  181. }
  182. // Add catch_all to beginning of cleanup pads.
  183. bool WebAssemblyLateEHPrepare::addCatchAlls(MachineFunction &MF) {
  184. bool Changed = false;
  185. const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
  186. for (auto &MBB : MF) {
  187. if (!MBB.isEHPad())
  188. continue;
  189. auto InsertPos = MBB.begin();
  190. // Skip EH_LABELs in the beginning of an EH pad if present.
  191. while (InsertPos != MBB.end() && InsertPos->isEHLabel())
  192. InsertPos++;
  193. // This runs after hoistCatches(), so we assume that if there is a catch,
  194. // that should be the first non-EH-label instruction in an EH pad.
  195. if (InsertPos == MBB.end() ||
  196. !WebAssembly::isCatch(InsertPos->getOpcode())) {
  197. Changed = true;
  198. BuildMI(MBB, InsertPos,
  199. InsertPos == MBB.end() ? DebugLoc() : InsertPos->getDebugLoc(),
  200. TII.get(WebAssembly::CATCH_ALL));
  201. }
  202. }
  203. return Changed;
  204. }
  205. // Replace pseudo-instructions catchret and cleanupret with br and rethrow
  206. // respectively.
  207. bool WebAssemblyLateEHPrepare::replaceFuncletReturns(MachineFunction &MF) {
  208. bool Changed = false;
  209. const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
  210. for (auto &MBB : MF) {
  211. auto Pos = MBB.getFirstTerminator();
  212. if (Pos == MBB.end())
  213. continue;
  214. MachineInstr *TI = &*Pos;
  215. switch (TI->getOpcode()) {
  216. case WebAssembly::CATCHRET: {
  217. // Replace a catchret with a branch
  218. MachineBasicBlock *TBB = TI->getOperand(0).getMBB();
  219. if (!MBB.isLayoutSuccessor(TBB))
  220. BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::BR))
  221. .addMBB(TBB);
  222. TI->eraseFromParent();
  223. Changed = true;
  224. break;
  225. }
  226. case WebAssembly::CLEANUPRET: {
  227. // Replace a cleanupret with a rethrow. For C++ support, currently
  228. // rethrow's immediate argument is always 0 (= the latest exception).
  229. BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW))
  230. .addImm(0);
  231. TI->eraseFromParent();
  232. Changed = true;
  233. break;
  234. }
  235. }
  236. }
  237. return Changed;
  238. }
  239. // Remove unnecessary unreachables after a throw or rethrow.
  240. bool WebAssemblyLateEHPrepare::removeUnnecessaryUnreachables(
  241. MachineFunction &MF) {
  242. bool Changed = false;
  243. for (auto &MBB : MF) {
  244. for (auto &MI : MBB) {
  245. if (MI.getOpcode() != WebAssembly::THROW &&
  246. MI.getOpcode() != WebAssembly::RETHROW)
  247. continue;
  248. Changed = true;
  249. // The instruction after the throw should be an unreachable or a branch to
  250. // another BB that should eventually lead to an unreachable. Delete it
  251. // because throw itself is a terminator, and also delete successors if
  252. // any.
  253. MBB.erase(std::next(MI.getIterator()), MBB.end());
  254. SmallVector<MachineBasicBlock *, 8> Succs(MBB.successors());
  255. for (auto *Succ : Succs)
  256. if (!Succ->isEHPad())
  257. MBB.removeSuccessor(Succ);
  258. eraseDeadBBsAndChildren(Succs);
  259. }
  260. }
  261. return Changed;
  262. }
  263. // After the stack is unwound due to a thrown exception, the __stack_pointer
  264. // global can point to an invalid address. This inserts instructions that
  265. // restore __stack_pointer global.
  266. bool WebAssemblyLateEHPrepare::restoreStackPointer(MachineFunction &MF) {
  267. const auto *FrameLowering = static_cast<const WebAssemblyFrameLowering *>(
  268. MF.getSubtarget().getFrameLowering());
  269. if (!FrameLowering->needsPrologForEH(MF))
  270. return false;
  271. bool Changed = false;
  272. for (auto &MBB : MF) {
  273. if (!MBB.isEHPad())
  274. continue;
  275. Changed = true;
  276. // Insert __stack_pointer restoring instructions at the beginning of each EH
  277. // pad, after the catch instruction. Here it is safe to assume that SP32
  278. // holds the latest value of __stack_pointer, because the only exception for
  279. // this case is when a function uses the red zone, but that only happens
  280. // with leaf functions, and we don't restore __stack_pointer in leaf
  281. // functions anyway.
  282. auto InsertPos = MBB.begin();
  283. // Skip EH_LABELs in the beginning of an EH pad if present.
  284. while (InsertPos != MBB.end() && InsertPos->isEHLabel())
  285. InsertPos++;
  286. assert(InsertPos != MBB.end() &&
  287. WebAssembly::isCatch(InsertPos->getOpcode()) &&
  288. "catch/catch_all should be present in every EH pad at this point");
  289. ++InsertPos; // Skip the catch instruction
  290. FrameLowering->writeSPToGlobal(FrameLowering->getSPReg(MF), MF, MBB,
  291. InsertPos, MBB.begin()->getDebugLoc());
  292. }
  293. return Changed;
  294. }