Home | History | Annotate | Line # | Download | only in WebAssembly
      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 "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"
     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   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());
    143     }
    144   }
    145 
    146   // After fixing subexception relationship between unwind destinations above,
    147   // there can still be remaining discrepancies.
    148   //
    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.
    160   //
    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);
    169 
    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());
    187       }
    188     }
    189   }
    190 
    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);
    199   }
    200 
    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;
    207 
    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;
    214       }
    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();
    227         }
    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);
    235       }
    236     }
    237   }
    238 
    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);
    245   }
    246 
    247   SmallVector<WebAssemblyException*, 8> ExceptionPointers;
    248   ExceptionPointers.reserve(Exceptions.size());
    249 
    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));
    257   }
    258 
    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());
    264   }
    265 }
    266 
    267 void WebAssemblyExceptionInfo::releaseMemory() {
    268   BBMap.clear();
    269   TopLevelExceptions.clear();
    270 }
    271 
    272 void WebAssemblyExceptionInfo::getAnalysisUsage(AnalysisUsage &AU) const {
    273   AU.setPreservesAll();
    274   AU.addRequired<MachineDominatorTree>();
    275   AU.addRequired<MachineDominanceFrontier>();
    276   MachineFunctionPass::getAnalysisUsage(AU);
    277 }
    278 
    279 void WebAssemblyExceptionInfo::discoverAndMapException(
    280     WebAssemblyException *WE, const MachineDominatorTree &MDT,
    281     const MachineDominanceFrontier &MDF) {
    282   unsigned NumBlocks = 0;
    283   unsigned NumSubExceptions = 0;
    284 
    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();
    291 
    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);
    307       }
    308       continue;
    309     }
    310 
    311     // This is an undiscovered block. Map it to the current exception.
    312     changeExceptionFor(MBB, WE);
    313     ++NumBlocks;
    314 
    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);
    319   }
    320 
    321   WE->getSubExceptions().reserve(NumSubExceptions);
    322   WE->reserveBlocks(NumBlocks);
    323 }
    324 
    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;
    331   }
    332   return WE;
    333 }
    334 
    335 void WebAssemblyException::print(raw_ostream &OS, unsigned Depth) const {
    336   OS.indent(Depth * 2) << "Exception at depth " << getExceptionDepth()
    337                        << " containing: ";
    338 
    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();
    347 
    348     if (getEHPad() == MBB)
    349       OS << " (landing-pad)";
    350   }
    351   OS << "\n";
    352 
    353   for (auto &SubE : SubExceptions)
    354     SubE->print(OS, Depth + 2);
    355 }
    356 
    357 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
    358 LLVM_DUMP_METHOD void WebAssemblyException::dump() const { print(dbgs()); }
    359 #endif
    360 
    361 raw_ostream &operator<<(raw_ostream &OS, const WebAssemblyException &WE) {
    362   WE.print(OS);
    363   return OS;
    364 }
    365 
    366 void WebAssemblyExceptionInfo::print(raw_ostream &OS, const Module *) const {
    367   for (auto &WE : TopLevelExceptions)
    368     WE->print(OS);
    369 }
    370