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