xref: /llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp (revision ea8c6375e3330f181105106b3adb84ff9fa76a7c)
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 
14 #include "WebAssemblyExceptionInfo.h"
15 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
16 #include "WebAssemblyUtilities.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 
23 using namespace llvm;
24 
25 #define DEBUG_TYPE "wasm-exception-info"
26 
27 char WebAssemblyExceptionInfo::ID = 0;
28 
29 INITIALIZE_PASS_BEGIN(WebAssemblyExceptionInfo, DEBUG_TYPE,
30                       "WebAssembly Exception Information", true, true)
31 INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree)
32 INITIALIZE_PASS_DEPENDENCY(MachineDominanceFrontier)
33 INITIALIZE_PASS_END(WebAssemblyExceptionInfo, DEBUG_TYPE,
34                     "WebAssembly Exception Information", true, true)
35 
36 bool WebAssemblyExceptionInfo::runOnMachineFunction(MachineFunction &MF) {
37   LLVM_DEBUG(dbgs() << "********** Exception Info Calculation **********\n"
38                        "********** Function: "
39                     << MF.getName() << '\n');
40   releaseMemory();
41   auto &MDT = getAnalysis<MachineDominatorTree>();
42   auto &MDF = getAnalysis<MachineDominanceFrontier>();
43   recalculate(MF, MDT, MDF);
44   return false;
45 }
46 
47 void WebAssemblyExceptionInfo::recalculate(
48     MachineFunction &MF, MachineDominatorTree &MDT,
49     const MachineDominanceFrontier &MDF) {
50   // Postorder traversal of the dominator tree.
51   SmallVector<std::unique_ptr<WebAssemblyException>, 8> Exceptions;
52   for (auto DomNode : post_order(&MDT)) {
53     MachineBasicBlock *EHPad = DomNode->getBlock();
54     if (!EHPad->isEHPad())
55       continue;
56     auto WE = std::make_unique<WebAssemblyException>(EHPad);
57     discoverAndMapException(WE.get(), MDT, MDF);
58     Exceptions.push_back(std::move(WE));
59   }
60 
61   // WasmEHFuncInfo contains a map of <catchpad, its next unwind destination>,
62   // which means, if an exception is not caught by the catchpad, it should end
63   // up in the next unwind destination stored in this data structure. (It is
64   // written as catchswitch's 'unwind' destination in ll files.) The below is an
65   // intuitive example of their relationship in C++ code:
66   // try {
67   //   try {
68   //   } catch (int) { // catchpad
69   //      ...          // this catch (int) { ... } is grouped as an exception
70   //   }
71   // } catch (...) {   // next unwind destination
72   // }
73   // (The example is try-catches for illustration purpose, but the unwind
74   // destination can be also a cleanuppad generated by destructor calls.) So the
75   // unwind destination is in the outside of the catchpad's exception.
76   //
77   // We group exceptions in this analysis simply by including all BBs dominated
78   // by an EH pad. But in case the EH pad's unwind destination does not have any
79   // children outside of the exception, that unwind destination ends up also
80   // being dominated by the EH pad and included in the exception, which is not
81   // semantically correct, because it unwinds/rethrows into an inner scope.
82   //
83   // Here we extract those unwind destinations from their (incorrect) parent
84   // exception. Note that the unwind destinations may not be an immediate
85   // children of the parent exception, so we have to traverse the parent chain.
86   const auto *EHInfo = MF.getWasmEHFuncInfo();
87   for (auto &MBB : MF) {
88     if (!MBB.isEHPad())
89       continue;
90     auto *EHPad = &MBB;
91     if (!EHInfo->hasUnwindDest(EHPad))
92       continue;
93     auto *UnwindDest = EHInfo->getUnwindDest(EHPad);
94     auto *WE = getExceptionFor(EHPad);
95     auto *UnwindDestWE = getExceptionFor(UnwindDest);
96     if (WE->contains(UnwindDestWE)) {
97       if (WE->getParentException())
98         UnwindDestWE->setParentException(WE->getParentException());
99       else
100         UnwindDestWE->setParentException(nullptr);
101     }
102   }
103 
104   // Add BBs to exceptions
105   for (auto DomNode : post_order(&MDT)) {
106     MachineBasicBlock *MBB = DomNode->getBlock();
107     WebAssemblyException *WE = getExceptionFor(MBB);
108     for (; WE; WE = WE->getParentException())
109       WE->addBlock(MBB);
110   }
111 
112   SmallVector<WebAssemblyException*, 8> ExceptionPointers;
113   ExceptionPointers.reserve(Exceptions.size());
114 
115   // Add subexceptions to exceptions
116   for (auto &WE : Exceptions) {
117     ExceptionPointers.push_back(WE.get());
118     if (WE->getParentException())
119       WE->getParentException()->getSubExceptions().push_back(std::move(WE));
120     else
121       addTopLevelException(std::move(WE));
122   }
123 
124   // For convenience, Blocks and SubExceptions are inserted in postorder.
125   // Reverse the lists.
126   for (auto *WE : ExceptionPointers) {
127     WE->reverseBlock();
128     std::reverse(WE->getSubExceptions().begin(), WE->getSubExceptions().end());
129   }
130 }
131 
132 void WebAssemblyExceptionInfo::releaseMemory() {
133   BBMap.clear();
134   TopLevelExceptions.clear();
135 }
136 
137 void WebAssemblyExceptionInfo::getAnalysisUsage(AnalysisUsage &AU) const {
138   AU.setPreservesAll();
139   AU.addRequired<MachineDominatorTree>();
140   AU.addRequired<MachineDominanceFrontier>();
141   MachineFunctionPass::getAnalysisUsage(AU);
142 }
143 
144 void WebAssemblyExceptionInfo::discoverAndMapException(
145     WebAssemblyException *WE, const MachineDominatorTree &MDT,
146     const MachineDominanceFrontier &MDF) {
147   unsigned NumBlocks = 0;
148   unsigned NumSubExceptions = 0;
149 
150   // Map blocks that belong to a catchpad / cleanuppad
151   MachineBasicBlock *EHPad = WE->getEHPad();
152   SmallVector<MachineBasicBlock *, 8> WL;
153   WL.push_back(EHPad);
154   while (!WL.empty()) {
155     MachineBasicBlock *MBB = WL.pop_back_val();
156 
157     // Find its outermost discovered exception. If this is a discovered block,
158     // check if it is already discovered to be a subexception of this exception.
159     WebAssemblyException *SubE = getOutermostException(MBB);
160     if (SubE) {
161       if (SubE != WE) {
162         // Discover a subexception of this exception.
163         SubE->setParentException(WE);
164         ++NumSubExceptions;
165         NumBlocks += SubE->getBlocksVector().capacity();
166         // All blocks that belong to this subexception have been already
167         // discovered. Skip all of them. Add the subexception's landing pad's
168         // dominance frontier to the worklist.
169         for (auto &Frontier : MDF.find(SubE->getEHPad())->second)
170           if (MDT.dominates(EHPad, Frontier))
171             WL.push_back(Frontier);
172       }
173       continue;
174     }
175 
176     // This is an undiscovered block. Map it to the current exception.
177     changeExceptionFor(MBB, WE);
178     ++NumBlocks;
179 
180     // Add successors dominated by the current BB to the worklist.
181     for (auto *Succ : MBB->successors())
182       if (MDT.dominates(EHPad, Succ))
183         WL.push_back(Succ);
184   }
185 
186   WE->getSubExceptions().reserve(NumSubExceptions);
187   WE->reserveBlocks(NumBlocks);
188 }
189 
190 WebAssemblyException *
191 WebAssemblyExceptionInfo::getOutermostException(MachineBasicBlock *MBB) const {
192   WebAssemblyException *WE = getExceptionFor(MBB);
193   if (WE) {
194     while (WebAssemblyException *Parent = WE->getParentException())
195       WE = Parent;
196   }
197   return WE;
198 }
199 
200 void WebAssemblyException::print(raw_ostream &OS, unsigned Depth) const {
201   OS.indent(Depth * 2) << "Exception at depth " << getExceptionDepth()
202                        << " containing: ";
203 
204   for (unsigned I = 0; I < getBlocks().size(); ++I) {
205     MachineBasicBlock *MBB = getBlocks()[I];
206     if (I)
207       OS << ", ";
208     OS << "%bb." << MBB->getNumber();
209     if (const auto *BB = MBB->getBasicBlock())
210       if (BB->hasName())
211         OS << "." << BB->getName();
212 
213     if (getEHPad() == MBB)
214       OS << " (landing-pad)";
215   }
216   OS << "\n";
217 
218   for (auto &SubE : SubExceptions)
219     SubE->print(OS, Depth + 2);
220 }
221 
222 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
223 LLVM_DUMP_METHOD void WebAssemblyException::dump() const { print(dbgs()); }
224 #endif
225 
226 raw_ostream &operator<<(raw_ostream &OS, const WebAssemblyException &WE) {
227   WE.print(OS);
228   return OS;
229 }
230 
231 void WebAssemblyExceptionInfo::print(raw_ostream &OS, const Module *) const {
232   for (auto &WE : TopLevelExceptions)
233     WE->print(OS);
234 }
235