xref: /freebsd/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyExceptionInfo.cpp (revision d35b039af944f68fe99bb3ad2f0c6d5ec7917096)
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)
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.
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  
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  
271  void WebAssemblyExceptionInfo::releaseMemory() {
272    BBMap.clear();
273    TopLevelExceptions.clear();
274  }
275  
276  void WebAssemblyExceptionInfo::getAnalysisUsage(AnalysisUsage &AU) const {
277    AU.setPreservesAll();
278    AU.addRequired<MachineDominatorTreeWrapperPass>();
279    AU.addRequired<MachineDominanceFrontier>();
280    MachineFunctionPass::getAnalysisUsage(AU);
281  }
282  
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 *
330  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  
339  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)
362  LLVM_DUMP_METHOD void WebAssemblyException::dump() const { print(dbgs()); }
363  #endif
364  
365  raw_ostream &operator<<(raw_ostream &OS, const WebAssemblyException &WE) {
366    WE.print(OS);
367    return OS;
368  }
369  
370  void WebAssemblyExceptionInfo::print(raw_ostream &OS, const Module *) const {
371    for (auto &WE : TopLevelExceptions)
372      WE->print(OS);
373  }
374