WebAssemblyExceptionInfo.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. //===--- WebAssemblyExceptionInfo.cpp - Exception Infomation --------------===//
  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 This file implements WebAssemblyException information analysis.
  11. ///
  12. //===----------------------------------------------------------------------===//
  13. #include "WebAssemblyExceptionInfo.h"
  14. #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
  15. #include "Utils/WebAssemblyUtilities.h"
  16. #include "llvm/ADT/DepthFirstIterator.h"
  17. #include "llvm/ADT/PostOrderIterator.h"
  18. #include "llvm/CodeGen/MachineDominanceFrontier.h"
  19. #include "llvm/CodeGen/MachineDominators.h"
  20. #include "llvm/CodeGen/WasmEHFuncInfo.h"
  21. #include "llvm/InitializePasses.h"
  22. #include "llvm/MC/MCAsmInfo.h"
  23. #include "llvm/Target/TargetMachine.h"
  24. using namespace llvm;
  25. #define DEBUG_TYPE "wasm-exception-info"
  26. char WebAssemblyExceptionInfo::ID = 0;
  27. INITIALIZE_PASS_BEGIN(WebAssemblyExceptionInfo, DEBUG_TYPE,
  28. "WebAssembly Exception Information", true, true)
  29. INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree)
  30. INITIALIZE_PASS_DEPENDENCY(MachineDominanceFrontier)
  31. INITIALIZE_PASS_END(WebAssemblyExceptionInfo, DEBUG_TYPE,
  32. "WebAssembly Exception Information", true, true)
  33. bool WebAssemblyExceptionInfo::runOnMachineFunction(MachineFunction &MF) {
  34. LLVM_DEBUG(dbgs() << "********** Exception Info Calculation **********\n"
  35. "********** Function: "
  36. << MF.getName() << '\n');
  37. releaseMemory();
  38. if (MF.getTarget().getMCAsmInfo()->getExceptionHandlingType() !=
  39. ExceptionHandling::Wasm ||
  40. !MF.getFunction().hasPersonalityFn())
  41. return false;
  42. auto &MDT = getAnalysis<MachineDominatorTree>();
  43. auto &MDF = getAnalysis<MachineDominanceFrontier>();
  44. recalculate(MF, MDT, MDF);
  45. LLVM_DEBUG(dump());
  46. return false;
  47. }
  48. // Check if Dst is reachable from Src using BFS. Search only within BBs
  49. // dominated by Header.
  50. static bool isReachableAmongDominated(const MachineBasicBlock *Src,
  51. const MachineBasicBlock *Dst,
  52. const MachineBasicBlock *Header,
  53. const MachineDominatorTree &MDT) {
  54. assert(MDT.dominates(Header, Dst));
  55. SmallVector<const MachineBasicBlock *, 8> WL;
  56. SmallPtrSet<const MachineBasicBlock *, 8> Visited;
  57. WL.push_back(Src);
  58. while (!WL.empty()) {
  59. const auto *MBB = WL.pop_back_val();
  60. if (MBB == Dst)
  61. return true;
  62. Visited.insert(MBB);
  63. for (auto *Succ : MBB->successors())
  64. if (!Visited.count(Succ) && MDT.dominates(Header, Succ))
  65. WL.push_back(Succ);
  66. }
  67. return false;
  68. }
  69. void WebAssemblyExceptionInfo::recalculate(
  70. MachineFunction &MF, MachineDominatorTree &MDT,
  71. const MachineDominanceFrontier &MDF) {
  72. // Postorder traversal of the dominator tree.
  73. SmallVector<std::unique_ptr<WebAssemblyException>, 8> Exceptions;
  74. for (auto *DomNode : post_order(&MDT)) {
  75. MachineBasicBlock *EHPad = DomNode->getBlock();
  76. if (!EHPad->isEHPad())
  77. continue;
  78. auto WE = std::make_unique<WebAssemblyException>(EHPad);
  79. discoverAndMapException(WE.get(), MDT, MDF);
  80. Exceptions.push_back(std::move(WE));
  81. }
  82. // WasmEHFuncInfo contains a map of <catchpad, its next unwind destination>,
  83. // which means, if an exception is not caught by the catchpad, it should end
  84. // up in the next unwind destination stored in this data structure. (It is
  85. // written as catchswitch's 'unwind' destination in ll files.) The below is an
  86. // intuitive example of their relationship in C++ code:
  87. // try {
  88. // try {
  89. // } catch (int) { // catchpad
  90. // ... // this catch (int) { ... } is grouped as an exception
  91. // }
  92. // } catch (...) { // next unwind destination
  93. // }
  94. // (The example is try-catches for illustration purpose, but the unwind
  95. // destination can be also a cleanuppad generated by destructor calls.) So the
  96. // unwind destination is in the outside of the catchpad's exception.
  97. //
  98. // We group exceptions in this analysis simply by including all BBs dominated
  99. // by an EH pad. But in case the EH pad's unwind destination does not have any
  100. // children outside of the exception, that unwind destination ends up also
  101. // being dominated by the EH pad and included in the exception, which is not
  102. // semantically correct, because it unwinds/rethrows into an inner scope.
  103. //
  104. // Here we extract those unwind destinations from their (incorrect) parent
  105. // exception. Note that the unwind destinations may not be an immediate
  106. // children of the parent exception, so we have to traverse the parent chain.
  107. //
  108. // We should traverse BBs in the preorder of the dominator tree, because
  109. // otherwise the result can be incorrect. For example, when there are three
  110. // exceptions A, B, and C and A > B > C (> is subexception relationship here),
  111. // and A's unwind destination is B and B's is C. When we visit B before A, we
  112. // end up extracting C only out of B but not out of A.
  113. const auto *EHInfo = MF.getWasmEHFuncInfo();
  114. SmallVector<std::pair<WebAssemblyException *, WebAssemblyException *>>
  115. UnwindWEVec;
  116. for (auto *DomNode : depth_first(&MDT)) {
  117. MachineBasicBlock *EHPad = DomNode->getBlock();
  118. if (!EHPad->isEHPad())
  119. continue;
  120. if (!EHInfo->hasUnwindDest(EHPad))
  121. continue;
  122. auto *UnwindDest = EHInfo->getUnwindDest(EHPad);
  123. auto *SrcWE = getExceptionFor(EHPad);
  124. auto *DstWE = getExceptionFor(UnwindDest);
  125. if (SrcWE->contains(DstWE)) {
  126. UnwindWEVec.push_back(std::make_pair(SrcWE, DstWE));
  127. LLVM_DEBUG(dbgs() << "Unwind destination ExceptionInfo fix:\n "
  128. << DstWE->getEHPad()->getNumber() << "."
  129. << DstWE->getEHPad()->getName()
  130. << "'s exception is taken out of "
  131. << SrcWE->getEHPad()->getNumber() << "."
  132. << SrcWE->getEHPad()->getName() << "'s exception\n");
  133. DstWE->setParentException(SrcWE->getParentException());
  134. }
  135. }
  136. // After fixing subexception relationship between unwind destinations above,
  137. // there can still be remaining discrepancies.
  138. //
  139. // For example, suppose Exception A is dominated by EHPad A and Exception B is
  140. // dominated by EHPad B. EHPad A's unwind destination is EHPad B, but because
  141. // EHPad B is dominated by EHPad A, the initial grouping makes Exception B a
  142. // subexception of Exception A, and we fix it by taking Exception B out of
  143. // Exception A above. But there can still be remaining BBs within Exception A
  144. // that are reachable from Exception B. These BBs semantically don't belong
  145. // to Exception A and were not a part of this 'catch' clause or cleanup code
  146. // in the original code, but they just happened to be grouped within Exception
  147. // A because they were dominated by EHPad A. We fix this case by taking those
  148. // BBs out of the incorrect exception and all its subexceptions that it
  149. // belongs to.
  150. //
  151. // 1. First, we take out remaining incorrect subexceptions. This part is
  152. // easier, because we haven't added BBs to exceptions yet, we only need to
  153. // change parent exception pointer.
  154. for (auto *DomNode : depth_first(&MDT)) {
  155. MachineBasicBlock *EHPad = DomNode->getBlock();
  156. if (!EHPad->isEHPad())
  157. continue;
  158. auto *WE = getExceptionFor(EHPad);
  159. // For each source EHPad -> unwind destination EHPad
  160. for (auto &P : UnwindWEVec) {
  161. auto *SrcWE = P.first;
  162. auto *DstWE = P.second;
  163. // If WE (the current EH pad's exception) is still contained in SrcWE but
  164. // reachable from DstWE that was taken out of SrcWE above, we have to take
  165. // out WE out of SrcWE too.
  166. if (WE != SrcWE && SrcWE->contains(WE) && !DstWE->contains(WE) &&
  167. isReachableAmongDominated(DstWE->getEHPad(), EHPad, SrcWE->getEHPad(),
  168. MDT)) {
  169. LLVM_DEBUG(dbgs() << "Remaining reachable ExceptionInfo fix:\n "
  170. << WE->getEHPad()->getNumber() << "."
  171. << WE->getEHPad()->getName()
  172. << "'s exception is taken out of "
  173. << SrcWE->getEHPad()->getNumber() << "."
  174. << SrcWE->getEHPad()->getName() << "'s exception\n");
  175. WE->setParentException(SrcWE->getParentException());
  176. }
  177. }
  178. }
  179. // Add BBs to exceptions' block set. This is a preparation to take out
  180. // remaining incorect BBs from exceptions, because we need to iterate over BBs
  181. // for each exception.
  182. for (auto *DomNode : post_order(&MDT)) {
  183. MachineBasicBlock *MBB = DomNode->getBlock();
  184. WebAssemblyException *WE = getExceptionFor(MBB);
  185. for (; WE; WE = WE->getParentException())
  186. WE->addToBlocksSet(MBB);
  187. }
  188. // 2. We take out remaining individual BBs out. Now we have added BBs to each
  189. // exceptions' BlockSet, when we take a BB out of an exception, we need to fix
  190. // those sets too.
  191. for (auto &P : UnwindWEVec) {
  192. auto *SrcWE = P.first;
  193. auto *DstWE = P.second;
  194. for (auto *MBB : SrcWE->getBlocksSet()) {
  195. if (MBB->isEHPad()) {
  196. assert(!isReachableAmongDominated(DstWE->getEHPad(), MBB,
  197. SrcWE->getEHPad(), MDT) &&
  198. "We already handled EH pads above");
  199. continue;
  200. }
  201. if (isReachableAmongDominated(DstWE->getEHPad(), MBB, SrcWE->getEHPad(),
  202. MDT)) {
  203. LLVM_DEBUG(dbgs() << "Remainder BB: " << MBB->getNumber() << "."
  204. << MBB->getName() << " is\n");
  205. WebAssemblyException *InnerWE = getExceptionFor(MBB);
  206. while (InnerWE != SrcWE) {
  207. LLVM_DEBUG(dbgs()
  208. << " removed from " << InnerWE->getEHPad()->getNumber()
  209. << "." << InnerWE->getEHPad()->getName()
  210. << "'s exception\n");
  211. InnerWE->removeFromBlocksSet(MBB);
  212. InnerWE = InnerWE->getParentException();
  213. }
  214. SrcWE->removeFromBlocksSet(MBB);
  215. LLVM_DEBUG(dbgs() << " removed from " << SrcWE->getEHPad()->getNumber()
  216. << "." << SrcWE->getEHPad()->getName()
  217. << "'s exception\n");
  218. changeExceptionFor(MBB, SrcWE->getParentException());
  219. if (SrcWE->getParentException())
  220. SrcWE->getParentException()->addToBlocksSet(MBB);
  221. }
  222. }
  223. }
  224. // Add BBs to exceptions' block vector
  225. for (auto *DomNode : post_order(&MDT)) {
  226. MachineBasicBlock *MBB = DomNode->getBlock();
  227. WebAssemblyException *WE = getExceptionFor(MBB);
  228. for (; WE; WE = WE->getParentException())
  229. WE->addToBlocksVector(MBB);
  230. }
  231. SmallVector<WebAssemblyException*, 8> ExceptionPointers;
  232. ExceptionPointers.reserve(Exceptions.size());
  233. // Add subexceptions to exceptions
  234. for (auto &WE : Exceptions) {
  235. ExceptionPointers.push_back(WE.get());
  236. if (WE->getParentException())
  237. WE->getParentException()->getSubExceptions().push_back(std::move(WE));
  238. else
  239. addTopLevelException(std::move(WE));
  240. }
  241. // For convenience, Blocks and SubExceptions are inserted in postorder.
  242. // Reverse the lists.
  243. for (auto *WE : ExceptionPointers) {
  244. WE->reverseBlock();
  245. std::reverse(WE->getSubExceptions().begin(), WE->getSubExceptions().end());
  246. }
  247. }
  248. void WebAssemblyExceptionInfo::releaseMemory() {
  249. BBMap.clear();
  250. TopLevelExceptions.clear();
  251. }
  252. void WebAssemblyExceptionInfo::getAnalysisUsage(AnalysisUsage &AU) const {
  253. AU.setPreservesAll();
  254. AU.addRequired<MachineDominatorTree>();
  255. AU.addRequired<MachineDominanceFrontier>();
  256. MachineFunctionPass::getAnalysisUsage(AU);
  257. }
  258. void WebAssemblyExceptionInfo::discoverAndMapException(
  259. WebAssemblyException *WE, const MachineDominatorTree &MDT,
  260. const MachineDominanceFrontier &MDF) {
  261. unsigned NumBlocks = 0;
  262. unsigned NumSubExceptions = 0;
  263. // Map blocks that belong to a catchpad / cleanuppad
  264. MachineBasicBlock *EHPad = WE->getEHPad();
  265. SmallVector<MachineBasicBlock *, 8> WL;
  266. WL.push_back(EHPad);
  267. while (!WL.empty()) {
  268. MachineBasicBlock *MBB = WL.pop_back_val();
  269. // Find its outermost discovered exception. If this is a discovered block,
  270. // check if it is already discovered to be a subexception of this exception.
  271. WebAssemblyException *SubE = getOutermostException(MBB);
  272. if (SubE) {
  273. if (SubE != WE) {
  274. // Discover a subexception of this exception.
  275. SubE->setParentException(WE);
  276. ++NumSubExceptions;
  277. NumBlocks += SubE->getBlocksVector().capacity();
  278. // All blocks that belong to this subexception have been already
  279. // discovered. Skip all of them. Add the subexception's landing pad's
  280. // dominance frontier to the worklist.
  281. for (auto &Frontier : MDF.find(SubE->getEHPad())->second)
  282. if (MDT.dominates(EHPad, Frontier))
  283. WL.push_back(Frontier);
  284. }
  285. continue;
  286. }
  287. // This is an undiscovered block. Map it to the current exception.
  288. changeExceptionFor(MBB, WE);
  289. ++NumBlocks;
  290. // Add successors dominated by the current BB to the worklist.
  291. for (auto *Succ : MBB->successors())
  292. if (MDT.dominates(EHPad, Succ))
  293. WL.push_back(Succ);
  294. }
  295. WE->getSubExceptions().reserve(NumSubExceptions);
  296. WE->reserveBlocks(NumBlocks);
  297. }
  298. WebAssemblyException *
  299. WebAssemblyExceptionInfo::getOutermostException(MachineBasicBlock *MBB) const {
  300. WebAssemblyException *WE = getExceptionFor(MBB);
  301. if (WE) {
  302. while (WebAssemblyException *Parent = WE->getParentException())
  303. WE = Parent;
  304. }
  305. return WE;
  306. }
  307. void WebAssemblyException::print(raw_ostream &OS, unsigned Depth) const {
  308. OS.indent(Depth * 2) << "Exception at depth " << getExceptionDepth()
  309. << " containing: ";
  310. for (unsigned I = 0; I < getBlocks().size(); ++I) {
  311. MachineBasicBlock *MBB = getBlocks()[I];
  312. if (I)
  313. OS << ", ";
  314. OS << "%bb." << MBB->getNumber();
  315. if (const auto *BB = MBB->getBasicBlock())
  316. if (BB->hasName())
  317. OS << "." << BB->getName();
  318. if (getEHPad() == MBB)
  319. OS << " (landing-pad)";
  320. }
  321. OS << "\n";
  322. for (auto &SubE : SubExceptions)
  323. SubE->print(OS, Depth + 2);
  324. }
  325. #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
  326. LLVM_DUMP_METHOD void WebAssemblyException::dump() const { print(dbgs()); }
  327. #endif
  328. raw_ostream &operator<<(raw_ostream &OS, const WebAssemblyException &WE) {
  329. WE.print(OS);
  330. return OS;
  331. }
  332. void WebAssemblyExceptionInfo::print(raw_ostream &OS, const Module *) const {
  333. for (auto &WE : TopLevelExceptions)
  334. WE->print(OS);
  335. }