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