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 #include "llvm/MC/MCAsmInfo.h"
23 #include "llvm/Target/TargetMachine.h"
24 
25 using namespace llvm;
26 
27 #define DEBUG_TYPE "wasm-exception-info"
28 
29 char WebAssemblyExceptionInfo::ID = 0;
30 
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)
37 
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;
52 }
53 
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);
64 
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);
73   }
74   return false;
75 }
76 
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));
89   }
90 
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.
106   //
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.
112   //
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.
116   //
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   DenseMap<WebAssemblyException *, WebAssemblyException *> UnwindWEMap;
124   for (auto *DomNode : depth_first(&MDT)) {
125     MachineBasicBlock *EHPad = DomNode->getBlock();
126     if (!EHPad->isEHPad())
127       continue;
128     if (!EHInfo->hasUnwindDest(EHPad))
129       continue;
130     auto *UnwindDest = EHInfo->getUnwindDest(EHPad);
131     auto *WE = getExceptionFor(EHPad);
132     auto *UnwindWE = getExceptionFor(UnwindDest);
133     if (WE->contains(UnwindWE)) {
134       UnwindWEMap[WE] = UnwindWE;
135       LLVM_DEBUG(dbgs() << "ExceptionInfo fix: " << WE->getEHPad()->getNumber()
136                         << "." << WE->getEHPad()->getName()
137                         << "'s exception is taken out of "
138                         << UnwindWE->getEHPad()->getNumber() << "."
139                         << UnwindWE->getEHPad()->getName() << "'s exception\n");
140       if (WE->getParentException())
141         UnwindWE->setParentException(WE->getParentException());
142       else
143         UnwindWE->setParentException(nullptr);
144     }
145   }
146 
147   // Add BBs to exceptions' block set first
148   for (auto *DomNode : post_order(&MDT)) {
149     MachineBasicBlock *MBB = DomNode->getBlock();
150     WebAssemblyException *WE = getExceptionFor(MBB);
151     for (; WE; WE = WE->getParentException())
152       WE->addToBlocksSet(MBB);
153   }
154 
155   // After fixing subexception relationship between unwind destinations above,
156   // there can still be remaining discrepancies.
157   //
158   // For example, suppose Exception A is dominated by EHPad A and Exception B is
159   // dominated by EHPad B. EHPad A's unwind destination is EHPad B, but because
160   // EHPad B is dominated by EHPad A, the initial grouping makes Exception B a
161   // subexception of Exception A, and we fix it by taking Exception B out of
162   // Exception A above. But there can still be remaining BBs within Exception A
163   // that are reachable from Exception B. These BBs semantically don't belong
164   // to Exception A and were not a part of 'catch' clause or cleanup code in the
165   // original code, but they just happened to be grouped within Exception A
166   // because they were dominated by EHPad A. We fix this case by taking those
167   // BBs out of the incorrect exception and all its subexceptions that it
168   // belongs to.
169   for (auto &KV : UnwindWEMap) {
170     WebAssemblyException *WE = KV.first;
171     WebAssemblyException *UnwindWE = KV.second;
172 
173     for (auto *MBB : WE->getBlocksSet()) {
174       if (MBB->isEHPad()) {
175         // If this assertion is triggered, it would be a violation of scoping
176         // rules in ll files, because this means an instruction in an outer
177         // scope tries to unwind to an EH pad in an inner scope.
178         assert(!isReachableAmongDominated(UnwindWE->getEHPad(), MBB,
179                                           WE->getEHPad(), MDT) &&
180                "Outer scope unwinds to inner scope. Bug in scope rules?");
181         continue;
182       }
183       if (isReachableAmongDominated(UnwindWE->getEHPad(), MBB, WE->getEHPad(),
184                                     MDT)) {
185         LLVM_DEBUG(dbgs() << "Remainder BB: " << MBB->getNumber() << "."
186                           << MBB->getName() << " is ");
187         WebAssemblyException *InnerWE = getExceptionFor(MBB);
188         while (InnerWE != WE) {
189           LLVM_DEBUG(dbgs()
190                      << "  removed from " << InnerWE->getEHPad()->getNumber()
191                      << "." << InnerWE->getEHPad()->getName()
192                      << "'s exception\n");
193           InnerWE->removeFromBlocksSet(MBB);
194           InnerWE = InnerWE->getParentException();
195         }
196         WE->removeFromBlocksSet(MBB);
197         LLVM_DEBUG(dbgs() << "  removed from " << WE->getEHPad()->getNumber()
198                           << "." << WE->getEHPad()->getName()
199                           << "'s exception\n");
200         changeExceptionFor(MBB, WE->getParentException());
201         if (WE->getParentException())
202           WE->getParentException()->addToBlocksSet(MBB);
203       }
204     }
205   }
206 
207   // Add BBs to exceptions' block vector
208   for (auto DomNode : post_order(&MDT)) {
209     MachineBasicBlock *MBB = DomNode->getBlock();
210     WebAssemblyException *WE = getExceptionFor(MBB);
211     for (; WE; WE = WE->getParentException())
212       WE->addToBlocksVector(MBB);
213   }
214 
215   SmallVector<WebAssemblyException*, 8> ExceptionPointers;
216   ExceptionPointers.reserve(Exceptions.size());
217 
218   // Add subexceptions to exceptions
219   for (auto &WE : Exceptions) {
220     ExceptionPointers.push_back(WE.get());
221     if (WE->getParentException())
222       WE->getParentException()->getSubExceptions().push_back(std::move(WE));
223     else
224       addTopLevelException(std::move(WE));
225   }
226 
227   // For convenience, Blocks and SubExceptions are inserted in postorder.
228   // Reverse the lists.
229   for (auto *WE : ExceptionPointers) {
230     WE->reverseBlock();
231     std::reverse(WE->getSubExceptions().begin(), WE->getSubExceptions().end());
232   }
233 }
234 
235 void WebAssemblyExceptionInfo::releaseMemory() {
236   BBMap.clear();
237   TopLevelExceptions.clear();
238 }
239 
240 void WebAssemblyExceptionInfo::getAnalysisUsage(AnalysisUsage &AU) const {
241   AU.setPreservesAll();
242   AU.addRequired<MachineDominatorTree>();
243   AU.addRequired<MachineDominanceFrontier>();
244   MachineFunctionPass::getAnalysisUsage(AU);
245 }
246 
247 void WebAssemblyExceptionInfo::discoverAndMapException(
248     WebAssemblyException *WE, const MachineDominatorTree &MDT,
249     const MachineDominanceFrontier &MDF) {
250   unsigned NumBlocks = 0;
251   unsigned NumSubExceptions = 0;
252 
253   // Map blocks that belong to a catchpad / cleanuppad
254   MachineBasicBlock *EHPad = WE->getEHPad();
255   SmallVector<MachineBasicBlock *, 8> WL;
256   WL.push_back(EHPad);
257   while (!WL.empty()) {
258     MachineBasicBlock *MBB = WL.pop_back_val();
259 
260     // Find its outermost discovered exception. If this is a discovered block,
261     // check if it is already discovered to be a subexception of this exception.
262     WebAssemblyException *SubE = getOutermostException(MBB);
263     if (SubE) {
264       if (SubE != WE) {
265         // Discover a subexception of this exception.
266         SubE->setParentException(WE);
267         ++NumSubExceptions;
268         NumBlocks += SubE->getBlocksVector().capacity();
269         // All blocks that belong to this subexception have been already
270         // discovered. Skip all of them. Add the subexception's landing pad's
271         // dominance frontier to the worklist.
272         for (auto &Frontier : MDF.find(SubE->getEHPad())->second)
273           if (MDT.dominates(EHPad, Frontier))
274             WL.push_back(Frontier);
275       }
276       continue;
277     }
278 
279     // This is an undiscovered block. Map it to the current exception.
280     changeExceptionFor(MBB, WE);
281     ++NumBlocks;
282 
283     // Add successors dominated by the current BB to the worklist.
284     for (auto *Succ : MBB->successors())
285       if (MDT.dominates(EHPad, Succ))
286         WL.push_back(Succ);
287   }
288 
289   WE->getSubExceptions().reserve(NumSubExceptions);
290   WE->reserveBlocks(NumBlocks);
291 }
292 
293 WebAssemblyException *
294 WebAssemblyExceptionInfo::getOutermostException(MachineBasicBlock *MBB) const {
295   WebAssemblyException *WE = getExceptionFor(MBB);
296   if (WE) {
297     while (WebAssemblyException *Parent = WE->getParentException())
298       WE = Parent;
299   }
300   return WE;
301 }
302 
303 void WebAssemblyException::print(raw_ostream &OS, unsigned Depth) const {
304   OS.indent(Depth * 2) << "Exception at depth " << getExceptionDepth()
305                        << " containing: ";
306 
307   for (unsigned I = 0; I < getBlocks().size(); ++I) {
308     MachineBasicBlock *MBB = getBlocks()[I];
309     if (I)
310       OS << ", ";
311     OS << "%bb." << MBB->getNumber();
312     if (const auto *BB = MBB->getBasicBlock())
313       if (BB->hasName())
314         OS << "." << BB->getName();
315 
316     if (getEHPad() == MBB)
317       OS << " (landing-pad)";
318   }
319   OS << "\n";
320 
321   for (auto &SubE : SubExceptions)
322     SubE->print(OS, Depth + 2);
323 }
324 
325 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
326 LLVM_DUMP_METHOD void WebAssemblyException::dump() const { print(dbgs()); }
327 #endif
328 
329 raw_ostream &operator<<(raw_ostream &OS, const WebAssemblyException &WE) {
330   WE.print(OS);
331   return OS;
332 }
333 
334 void WebAssemblyExceptionInfo::print(raw_ostream &OS, const Module *) const {
335   for (auto &WE : TopLevelExceptions)
336     WE->print(OS);
337 }
338