LCOV - code coverage report
Current view: top level - lib/Target/WebAssembly - WebAssemblyLateEHPrepare.cpp (source / functions) Hit Total Coverage
Test: llvm-toolchain.info Lines: 45 169 26.6 %
Date: 2018-10-20 13:21:21 Functions: 7 15 46.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //=== WebAssemblyLateEHPrepare.cpp - WebAssembly Exception Preparation -===//
       2             : //
       3             : //                     The LLVM Compiler Infrastructure
       4             : //
       5             : // This file is distributed under the University of Illinois Open Source
       6             : // License. See LICENSE.TXT for details.
       7             : //
       8             : //===----------------------------------------------------------------------===//
       9             : ///
      10             : /// \file
      11             : /// \brief Does various transformations for exception handling.
      12             : ///
      13             : //===----------------------------------------------------------------------===//
      14             : 
      15             : #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
      16             : #include "WebAssembly.h"
      17             : #include "WebAssemblySubtarget.h"
      18             : #include "WebAssemblyUtilities.h"
      19             : #include "llvm/CodeGen/MachineInstrBuilder.h"
      20             : #include "llvm/CodeGen/WasmEHFuncInfo.h"
      21             : #include "llvm/MC/MCAsmInfo.h"
      22             : using namespace llvm;
      23             : 
      24             : #define DEBUG_TYPE "wasm-exception-prepare"
      25             : 
      26             : namespace {
      27             : class WebAssemblyLateEHPrepare final : public MachineFunctionPass {
      28         305 :   StringRef getPassName() const override {
      29         305 :     return "WebAssembly Prepare Exception";
      30             :   }
      31             : 
      32             :   bool runOnMachineFunction(MachineFunction &MF) override;
      33             : 
      34             :   bool replaceFuncletReturns(MachineFunction &MF);
      35             :   bool hoistCatches(MachineFunction &MF);
      36             :   bool addCatchAlls(MachineFunction &MF);
      37             :   bool addRethrows(MachineFunction &MF);
      38             :   bool ensureSingleBBTermPads(MachineFunction &MF);
      39             :   bool mergeTerminatePads(MachineFunction &MF);
      40             :   bool addCatchAllTerminatePads(MachineFunction &MF);
      41             : 
      42             : public:
      43             :   static char ID; // Pass identification, replacement for typeid
      44         305 :   WebAssemblyLateEHPrepare() : MachineFunctionPass(ID) {}
      45             : };
      46             : } // end anonymous namespace
      47             : 
      48             : char WebAssemblyLateEHPrepare::ID = 0;
      49      199030 : INITIALIZE_PASS(WebAssemblyLateEHPrepare, DEBUG_TYPE,
      50             :                 "WebAssembly Late Exception Preparation", false, false)
      51             : 
      52         305 : FunctionPass *llvm::createWebAssemblyLateEHPrepare() {
      53         305 :   return new WebAssemblyLateEHPrepare();
      54             : }
      55             : 
      56             : // Returns the nearest EH pad that dominates this instruction. This does not use
      57             : // dominator analysis; it just does BFS on its predecessors until arriving at an
      58             : // EH pad. This assumes valid EH scopes so the first EH pad it arrives in all
      59             : // possible search paths should be the same.
      60             : // Returns nullptr in case it does not find any EH pad in the search, or finds
      61             : // multiple different EH pads.
      62          14 : static MachineBasicBlock *GetMatchingEHPad(MachineInstr *MI) {
      63          14 :   MachineFunction *MF = MI->getParent()->getParent();
      64             :   SmallVector<MachineBasicBlock *, 2> WL;
      65             :   SmallPtrSet<MachineBasicBlock *, 2> Visited;
      66          14 :   WL.push_back(MI->getParent());
      67             :   MachineBasicBlock *EHPad = nullptr;
      68          28 :   while (!WL.empty()) {
      69             :     MachineBasicBlock *MBB = WL.pop_back_val();
      70          14 :     if (Visited.count(MBB))
      71             :       continue;
      72          14 :     Visited.insert(MBB);
      73          14 :     if (MBB->isEHPad()) {
      74          14 :       if (EHPad && EHPad != MBB)
      75             :         return nullptr;
      76             :       EHPad = MBB;
      77             :       continue;
      78             :     }
      79           0 :     if (MBB == &MF->front())
      80             :       return nullptr;
      81           0 :     WL.append(MBB->pred_begin(), MBB->pred_end());
      82             :   }
      83             :   return EHPad;
      84             : }
      85             : 
      86             : // Erases the given BBs and all their children from the function. If other BBs
      87             : // have the BB as a successor, the successor relationships will be deleted as
      88             : // well.
      89             : template <typename Container>
      90           5 : static void EraseBBsAndChildren(const Container &MBBs) {
      91           0 :   SmallVector<MachineBasicBlock *, 8> WL(MBBs.begin(), MBBs.end());
      92           6 :   while (!WL.empty()) {
      93             :     MachineBasicBlock *MBB = WL.pop_back_val();
      94           1 :     SmallVector<MachineBasicBlock *, 4> Preds(MBB->pred_begin(),
      95             :                                               MBB->pred_end());
      96           2 :     for (auto *Pred : Preds)
      97           1 :       Pred->removeSuccessor(MBB);
      98           1 :     SmallVector<MachineBasicBlock *, 4> Succs(MBB->succ_begin(),
      99             :                                               MBB->succ_end());
     100           1 :     WL.append(MBB->succ_begin(), MBB->succ_end());
     101           1 :     for (auto *Succ : Succs)
     102           0 :       MBB->removeSuccessor(Succ);
     103           1 :     MBB->eraseFromParent();
     104             :   }
     105           5 : }
     106           0 : 
     107           0 : bool WebAssemblyLateEHPrepare::runOnMachineFunction(MachineFunction &MF) {
     108           0 :   if (MF.getTarget().getMCAsmInfo()->getExceptionHandlingType() !=
     109             :       ExceptionHandling::Wasm)
     110           0 :     return false;
     111             : 
     112           0 :   bool Changed = false;
     113           0 :   Changed |= addRethrows(MF);
     114           0 :   if (!MF.getFunction().hasPersonalityFn())
     115             :     return Changed;
     116           0 :   Changed |= replaceFuncletReturns(MF);
     117           0 :   Changed |= hoistCatches(MF);
     118           0 :   Changed |= addCatchAlls(MF);
     119           0 :   Changed |= ensureSingleBBTermPads(MF);
     120             :   Changed |= mergeTerminatePads(MF);
     121           0 :   Changed |= addCatchAllTerminatePads(MF);
     122           5 :   return Changed;
     123             : }
     124           6 : 
     125             : bool WebAssemblyLateEHPrepare::replaceFuncletReturns(MachineFunction &MF) {
     126           1 :   bool Changed = false;
     127             :   const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
     128           2 :   auto *EHInfo = MF.getWasmEHFuncInfo();
     129           1 : 
     130           1 :   for (auto &MBB : MF) {
     131             :     auto Pos = MBB.getFirstTerminator();
     132           1 :     if (Pos == MBB.end())
     133           1 :       continue;
     134           0 :     MachineInstr *TI = &*Pos;
     135           1 : 
     136             :     switch (TI->getOpcode()) {
     137           5 :     case WebAssembly::CATCHRET: {
     138             :       // Replace a catchret with a branch
     139        2983 :       MachineBasicBlock *TBB = TI->getOperand(0).getMBB();
     140        5966 :       if (!MBB.isLayoutSuccessor(TBB))
     141             :         BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::BR))
     142             :             .addMBB(TBB);
     143             :       TI->eraseFromParent();
     144             :       Changed = true;
     145           9 :       break;
     146          18 :     }
     147             :     case WebAssembly::CLEANUPRET: {
     148           8 :       // Replace a cleanupret with a rethrow
     149           8 :       if (EHInfo->hasThrowUnwindDest(&MBB))
     150           8 :         BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW))
     151           8 :             .addMBB(EHInfo->getThrowUnwindDest(&MBB));
     152           8 :       else
     153           8 :         BuildMI(MBB, TI, TI->getDebugLoc(),
     154           8 :                 TII.get(WebAssembly::RETHROW_TO_CALLER));
     155             : 
     156             :       TI->eraseFromParent();
     157           0 :       Changed = true;
     158             :       break;
     159           0 :     }
     160           0 :     }
     161             :   }
     162           0 :   return Changed;
     163           0 : }
     164           0 : 
     165             : // Hoist catch instructions to the beginning of their matching EH pad BBs in
     166             : // case,
     167             : // (1) catch instruction is not the first instruction in EH pad.
     168           0 : // ehpad:
     169           0 : //   some_other_instruction
     170             : //   ...
     171           0 : //   %exn = catch 0
     172           0 : // (2) catch instruction is in a non-EH pad BB. For example,
     173           0 : // ehpad:
     174             : //   br bb0
     175           0 : // bb0:
     176             : //   %exn = catch 0
     177           0 : bool WebAssemblyLateEHPrepare::hoistCatches(MachineFunction &MF) {
     178             :   bool Changed = false;
     179             :   SmallVector<MachineInstr *, 16> Catches;
     180             :   for (auto &MBB : MF)
     181             :     for (auto &MI : MBB)
     182           0 :       if (WebAssembly::isCatch(MI))
     183           0 :         Catches.push_back(&MI);
     184             : 
     185             :   for (auto *Catch : Catches) {
     186           0 :     MachineBasicBlock *EHPad = GetMatchingEHPad(Catch);
     187             :     assert(EHPad && "No matching EH pad for catch");
     188           0 :     if (EHPad->begin() == Catch)
     189             :       continue;
     190           0 :     Changed = true;
     191             :     EHPad->insert(EHPad->begin(), Catch->removeFromParent());
     192             :   }
     193             :   return Changed;
     194           0 : }
     195             : 
     196             : // Add catch_all to beginning of cleanup pads.
     197             : bool WebAssemblyLateEHPrepare::addCatchAlls(MachineFunction &MF) {
     198             :   bool Changed = false;
     199             :   const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
     200             : 
     201             :   for (auto &MBB : MF) {
     202             :     if (!MBB.isEHPad())
     203             :       continue;
     204             :     // This runs after hoistCatches(), so we assume that if there is a catch,
     205             :     // that should be the first instruction in an EH pad.
     206             :     if (!WebAssembly::isCatch(*MBB.begin())) {
     207             :       Changed = true;
     208             :       BuildMI(MBB, MBB.begin(), MBB.begin()->getDebugLoc(),
     209           0 :               TII.get(WebAssembly::CATCH_ALL));
     210             :     }
     211             :   }
     212           0 :   return Changed;
     213           0 : }
     214           0 : 
     215           0 : // Add a 'rethrow' instruction after __cxa_rethrow() call
     216             : bool WebAssemblyLateEHPrepare::addRethrows(MachineFunction &MF) {
     217           0 :   bool Changed = false;
     218           0 :   const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
     219             :   auto *EHInfo = MF.getWasmEHFuncInfo();
     220           0 : 
     221           0 :   for (auto &MBB : MF)
     222             :     for (auto &MI : MBB) {
     223           0 :       // Check if it is a call to __cxa_rethrow()
     224             :       if (!MI.isCall())
     225           0 :         continue;
     226             :       MachineOperand &CalleeOp = MI.getOperand(0);
     227             :       if (!CalleeOp.isGlobal() ||
     228             :           CalleeOp.getGlobal()->getName() != WebAssembly::CxaRethrowFn)
     229           0 :         continue;
     230             : 
     231           0 :       // Now we have __cxa_rethrow() call
     232             :       Changed = true;
     233           0 :       auto InsertPt = std::next(MachineBasicBlock::iterator(MI));
     234           0 :       while (InsertPt != MBB.end() && InsertPt->isLabel()) // Skip EH_LABELs
     235           0 :         ++InsertPt;
     236             :       MachineInstr *Rethrow = nullptr;
     237             :       if (EHInfo->hasThrowUnwindDest(&MBB))
     238           0 :         Rethrow = BuildMI(MBB, InsertPt, MI.getDebugLoc(),
     239             :                           TII.get(WebAssembly::RETHROW))
     240             :                       .addMBB(EHInfo->getThrowUnwindDest(&MBB));
     241           0 :       else
     242             :         Rethrow = BuildMI(MBB, InsertPt, MI.getDebugLoc(),
     243             :                           TII.get(WebAssembly::RETHROW_TO_CALLER));
     244           0 : 
     245             :       // Becasue __cxa_rethrow does not return, the instruction after the
     246             :       // rethrow should be an unreachable or a branch to another BB that should
     247             :       // eventually lead to an unreachable. Delete it because rethrow itself is
     248           0 :       // a terminator, and also delete non-EH pad successors if any.
     249             :       MBB.erase(std::next(MachineBasicBlock::iterator(Rethrow)), MBB.end());
     250           0 :       SmallVector<MachineBasicBlock *, 8> NonPadSuccessors;
     251           0 :       for (auto *Succ : MBB.successors())
     252             :         if (!Succ->isEHPad())
     253           0 :           NonPadSuccessors.push_back(Succ);
     254           0 :       EraseBBsAndChildren(NonPadSuccessors);
     255             :     }
     256           0 :   return Changed;
     257           0 : }
     258           0 : 
     259           0 : // Terminate pads are an single-BB EH pad in the form of
     260           0 : // termpad:
     261           0 : //   %exn = catch 0
     262             : //   call @__clang_call_terminate(%exn)
     263             : //   unreachable
     264             : // (There can be set_local and get_locals before the call if we didn't run
     265           0 : // RegStackify)
     266           0 : // But code transformations can change or add more control flow, so the call to
     267             : // __clang_call_terminate() function may not be in the original EH pad anymore.
     268             : // This ensures every terminate pad is a single BB in the form illustrated
     269             : // above.
     270           0 : bool WebAssemblyLateEHPrepare::ensureSingleBBTermPads(MachineFunction &MF) {
     271           0 :   const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
     272           0 : 
     273             :   // Find calls to __clang_call_terminate()
     274           0 :   SmallVector<MachineInstr *, 8> ClangCallTerminateCalls;
     275           0 :   for (auto &MBB : MF)
     276             :     for (auto &MI : MBB)
     277             :       if (MI.isCall()) {
     278             :         const MachineOperand &CalleeOp = MI.getOperand(0);
     279             :         if (CalleeOp.isGlobal() && CalleeOp.getGlobal()->getName() ==
     280             :                                        WebAssembly::ClangCallTerminateFn)
     281           0 :           ClangCallTerminateCalls.push_back(&MI);
     282             :       }
     283           0 : 
     284           0 :   bool Changed = false;
     285           0 :   for (auto *Call : ClangCallTerminateCalls) {
     286           0 :     MachineBasicBlock *EHPad = GetMatchingEHPad(Call);
     287             :     assert(EHPad && "No matching EH pad for catch");
     288           0 : 
     289             :     // If it is already the form we want, skip it
     290             :     if (Call->getParent() == EHPad &&
     291             :         Call->getNextNode()->getOpcode() == WebAssembly::UNREACHABLE)
     292             :       continue;
     293             : 
     294             :     // In case the __clang_call_terminate() call is not in its matching EH pad,
     295             :     // move the call to the end of EH pad and add an unreachable instruction
     296             :     // after that. Delete all successors and their children if any, because here
     297             :     // the program terminates.
     298             :     Changed = true;
     299             :     MachineInstr *Catch = &*EHPad->begin();
     300             :     // This runs after hoistCatches(), so catch instruction should be at the top
     301             :     assert(WebAssembly::isCatch(*Catch));
     302           0 :     // Takes the result register of the catch instruction as argument. There may
     303           0 :     // have been some other set_local/get_locals in between, but at this point
     304             :     // we don't care.
     305             :     Call->getOperand(1).setReg(Catch->getOperand(0).getReg());
     306             :     auto InsertPos = std::next(MachineBasicBlock::iterator(Catch));
     307           0 :     EHPad->insert(InsertPos, Call->removeFromParent());
     308           0 :     BuildMI(*EHPad, InsertPos, Call->getDebugLoc(),
     309           0 :             TII.get(WebAssembly::UNREACHABLE));
     310           0 :     EHPad->erase(InsertPos, EHPad->end());
     311           0 :     EraseBBsAndChildren(EHPad->successors());
     312             :   }
     313           0 :   return Changed;
     314             : }
     315             : 
     316             : // In case there are multiple terminate pads, merge them into one for code size.
     317           0 : // This runs after ensureSingleBBTermPads() and assumes every terminate pad is a
     318           0 : // single BB.
     319             : // In principle this violates EH scope relationship because it can merge
     320             : // multiple inner EH scopes, each of which is in different outer EH scope. But
     321             : // getEHScopeMembership() function will not be called after this, so it is fine.
     322           0 : bool WebAssemblyLateEHPrepare::mergeTerminatePads(MachineFunction &MF) {
     323           0 :   SmallVector<MachineBasicBlock *, 8> TermPads;
     324             :   for (auto &MBB : MF)
     325             :     if (WebAssembly::isCatchTerminatePad(MBB))
     326             :       TermPads.push_back(&MBB);
     327             :   if (TermPads.empty())
     328             :     return false;
     329             : 
     330             :   MachineBasicBlock *UniqueTermPad = TermPads.front();
     331             :   for (auto *TermPad :
     332             :        llvm::make_range(std::next(TermPads.begin()), TermPads.end())) {
     333             :     SmallVector<MachineBasicBlock *, 2> Preds(TermPad->pred_begin(),
     334             :                                               TermPad->pred_end());
     335             :     for (auto *Pred : Preds)
     336             :       Pred->replaceSuccessor(TermPad, UniqueTermPad);
     337           0 :     TermPad->eraseFromParent();
     338           0 :   }
     339           0 :   return true;
     340             : }
     341           0 : 
     342             : // Terminate pads are cleanup pads, so they should start with a 'catch_all'
     343           0 : // instruction. But in the Itanium model, when we have a C++ exception object,
     344             : // we pass them to __clang_call_terminate function, which calls __cxa_end_catch
     345           0 : // with the passed exception pointer and then std::terminate. This is the reason
     346             : // that terminate pads are generated with not a catch_all but a catch
     347             : // instruction in clang and earlier llvm passes. Here we append a terminate pad
     348             : // with a catch_all after each existing terminate pad so we can also catch
     349             : // foreign exceptions. For every terminate pad:
     350             : //   %exn = catch 0
     351             : //   call @__clang_call_terminate(%exn)
     352             : //   unreachable
     353             : // We append this BB right after that:
     354           0 : //   catch_all
     355             : //   call @std::terminate()
     356           0 : //   unreachable
     357           0 : bool WebAssemblyLateEHPrepare::addCatchAllTerminatePads(MachineFunction &MF) {
     358           0 :   const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
     359           0 :   SmallVector<MachineBasicBlock *, 8> TermPads;
     360           0 :   for (auto &MBB : MF)
     361             :     if (WebAssembly::isCatchTerminatePad(MBB))
     362           0 :       TermPads.push_back(&MBB);
     363           0 :   if (TermPads.empty())
     364           0 :     return false;
     365             : 
     366           0 :   Function *StdTerminateFn =
     367           0 :       MF.getFunction().getParent()->getFunction(WebAssembly::StdTerminateFn);
     368           0 :   assert(StdTerminateFn && "There is no std::terminate() function");
     369           0 :   for (auto *CatchTermPad : TermPads) {
     370             :     DebugLoc DL = CatchTermPad->findDebugLoc(CatchTermPad->begin());
     371             :     auto *CatchAllTermPad = MF.CreateMachineBasicBlock();
     372             :     MF.insert(std::next(MachineFunction::iterator(CatchTermPad)),
     373             :               CatchAllTermPad);
     374             :     CatchAllTermPad->setIsEHPad();
     375             :     BuildMI(CatchAllTermPad, DL, TII.get(WebAssembly::CATCH_ALL));
     376             :     BuildMI(CatchAllTermPad, DL, TII.get(WebAssembly::CALL_VOID))
     377             :         .addGlobalAddress(StdTerminateFn);
     378             :     BuildMI(CatchAllTermPad, DL, TII.get(WebAssembly::UNREACHABLE));
     379             : 
     380             :     // Actually this CatchAllTermPad (new terminate pad with a catch_all) is not
     381             :     // a successor of an existing terminate pad. CatchAllTermPad should have all
     382             :     // predecessors CatchTermPad has instead. This is a hack to force
     383             :     // CatchAllTermPad be always sorted right after CatchTermPad; the correct
     384             :     // predecessor-successor relationships will be restored in CFGStackify pass.
     385             :     CatchTermPad->addSuccessor(CatchAllTermPad);
     386             :   }
     387             :   return true;
     388             : }

Generated by: LCOV version 1.13