10b57cec5SDimitry Andric //===-- WebAssemblyCFGStackify.cpp - CFG Stackification -------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric /// 90b57cec5SDimitry Andric /// \file 100b57cec5SDimitry Andric /// This file implements a CFG stacking pass. 110b57cec5SDimitry Andric /// 120b57cec5SDimitry Andric /// This pass inserts BLOCK, LOOP, and TRY markers to mark the start of scopes, 130b57cec5SDimitry Andric /// since scope boundaries serve as the labels for WebAssembly's control 140b57cec5SDimitry Andric /// transfers. 150b57cec5SDimitry Andric /// 160b57cec5SDimitry Andric /// This is sufficient to convert arbitrary CFGs into a form that works on 170b57cec5SDimitry Andric /// WebAssembly, provided that all loops are single-entry. 180b57cec5SDimitry Andric /// 190b57cec5SDimitry Andric /// In case we use exceptions, this pass also fixes mismatches in unwind 200b57cec5SDimitry Andric /// destinations created during transforming CFG into wasm structured format. 210b57cec5SDimitry Andric /// 220b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 230b57cec5SDimitry Andric 24fe6060f1SDimitry Andric #include "Utils/WebAssemblyTypeUtilities.h" 250b57cec5SDimitry Andric #include "WebAssembly.h" 260b57cec5SDimitry Andric #include "WebAssemblyExceptionInfo.h" 270b57cec5SDimitry Andric #include "WebAssemblyMachineFunctionInfo.h" 28e8d8bef9SDimitry Andric #include "WebAssemblySortRegion.h" 290b57cec5SDimitry Andric #include "WebAssemblySubtarget.h" 305f757f3fSDimitry Andric #include "WebAssemblyUtilities.h" 310b57cec5SDimitry Andric #include "llvm/ADT/Statistic.h" 320b57cec5SDimitry Andric #include "llvm/CodeGen/MachineDominators.h" 330b57cec5SDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h" 348bcb0991SDimitry Andric #include "llvm/CodeGen/MachineLoopInfo.h" 35fe6060f1SDimitry Andric #include "llvm/CodeGen/WasmEHFuncInfo.h" 360b57cec5SDimitry Andric #include "llvm/MC/MCAsmInfo.h" 375ffd83dbSDimitry Andric #include "llvm/Target/TargetMachine.h" 380b57cec5SDimitry Andric using namespace llvm; 39e8d8bef9SDimitry Andric using WebAssembly::SortRegionInfo; 400b57cec5SDimitry Andric 410b57cec5SDimitry Andric #define DEBUG_TYPE "wasm-cfg-stackify" 420b57cec5SDimitry Andric 43fe6060f1SDimitry Andric STATISTIC(NumCallUnwindMismatches, "Number of call unwind mismatches found"); 44fe6060f1SDimitry Andric STATISTIC(NumCatchUnwindMismatches, "Number of catch unwind mismatches found"); 450b57cec5SDimitry Andric 460b57cec5SDimitry Andric namespace { 470b57cec5SDimitry Andric class WebAssemblyCFGStackify final : public MachineFunctionPass { 480b57cec5SDimitry Andric StringRef getPassName() const override { return "WebAssembly CFG Stackify"; } 490b57cec5SDimitry Andric 500b57cec5SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override { 510fca6ea1SDimitry Andric AU.addRequired<MachineDominatorTreeWrapperPass>(); 520fca6ea1SDimitry Andric AU.addRequired<MachineLoopInfoWrapperPass>(); 530b57cec5SDimitry Andric AU.addRequired<WebAssemblyExceptionInfo>(); 540b57cec5SDimitry Andric MachineFunctionPass::getAnalysisUsage(AU); 550b57cec5SDimitry Andric } 560b57cec5SDimitry Andric 570b57cec5SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override; 580b57cec5SDimitry Andric 590b57cec5SDimitry Andric // For each block whose label represents the end of a scope, record the block 600b57cec5SDimitry Andric // which holds the beginning of the scope. This will allow us to quickly skip 610b57cec5SDimitry Andric // over scoped regions when walking blocks. 620b57cec5SDimitry Andric SmallVector<MachineBasicBlock *, 8> ScopeTops; 63e8d8bef9SDimitry Andric void updateScopeTops(MachineBasicBlock *Begin, MachineBasicBlock *End) { 64e8d8bef9SDimitry Andric int EndNo = End->getNumber(); 65e8d8bef9SDimitry Andric if (!ScopeTops[EndNo] || ScopeTops[EndNo]->getNumber() > Begin->getNumber()) 66e8d8bef9SDimitry Andric ScopeTops[EndNo] = Begin; 67e8d8bef9SDimitry Andric } 680b57cec5SDimitry Andric 690b57cec5SDimitry Andric // Placing markers. 700b57cec5SDimitry Andric void placeMarkers(MachineFunction &MF); 710b57cec5SDimitry Andric void placeBlockMarker(MachineBasicBlock &MBB); 720b57cec5SDimitry Andric void placeLoopMarker(MachineBasicBlock &MBB); 730b57cec5SDimitry Andric void placeTryMarker(MachineBasicBlock &MBB); 74fe6060f1SDimitry Andric 75fe6060f1SDimitry Andric // Exception handling related functions 76fe6060f1SDimitry Andric bool fixCallUnwindMismatches(MachineFunction &MF); 77fe6060f1SDimitry Andric bool fixCatchUnwindMismatches(MachineFunction &MF); 78fe6060f1SDimitry Andric void addTryDelegate(MachineInstr *RangeBegin, MachineInstr *RangeEnd, 79fe6060f1SDimitry Andric MachineBasicBlock *DelegateDest); 80fe6060f1SDimitry Andric void recalculateScopeTops(MachineFunction &MF); 810b57cec5SDimitry Andric void removeUnnecessaryInstrs(MachineFunction &MF); 82fe6060f1SDimitry Andric 83fe6060f1SDimitry Andric // Wrap-up 84fe6060f1SDimitry Andric using EndMarkerInfo = 85fe6060f1SDimitry Andric std::pair<const MachineBasicBlock *, const MachineInstr *>; 86fe6060f1SDimitry Andric unsigned getBranchDepth(const SmallVectorImpl<EndMarkerInfo> &Stack, 87fe6060f1SDimitry Andric const MachineBasicBlock *MBB); 88fe6060f1SDimitry Andric unsigned getDelegateDepth(const SmallVectorImpl<EndMarkerInfo> &Stack, 89fe6060f1SDimitry Andric const MachineBasicBlock *MBB); 90*415efcecSDimitry Andric unsigned getRethrowDepth(const SmallVectorImpl<EndMarkerInfo> &Stack, 91*415efcecSDimitry Andric const MachineBasicBlock *EHPadToRethrow); 920b57cec5SDimitry Andric void rewriteDepthImmediates(MachineFunction &MF); 930b57cec5SDimitry Andric void fixEndsAtEndOfFunction(MachineFunction &MF); 94fe6060f1SDimitry Andric void cleanupFunctionData(MachineFunction &MF); 950b57cec5SDimitry Andric 96fe6060f1SDimitry Andric // For each BLOCK|LOOP|TRY, the corresponding END_(BLOCK|LOOP|TRY) or DELEGATE 97fe6060f1SDimitry Andric // (in case of TRY). 980b57cec5SDimitry Andric DenseMap<const MachineInstr *, MachineInstr *> BeginToEnd; 99fe6060f1SDimitry Andric // For each END_(BLOCK|LOOP|TRY) or DELEGATE, the corresponding 100fe6060f1SDimitry Andric // BLOCK|LOOP|TRY. 1010b57cec5SDimitry Andric DenseMap<const MachineInstr *, MachineInstr *> EndToBegin; 1020b57cec5SDimitry Andric // <TRY marker, EH pad> map 1030b57cec5SDimitry Andric DenseMap<const MachineInstr *, MachineBasicBlock *> TryToEHPad; 1040b57cec5SDimitry Andric // <EH pad, TRY marker> map 1050b57cec5SDimitry Andric DenseMap<const MachineBasicBlock *, MachineInstr *> EHPadToTry; 1060b57cec5SDimitry Andric 107fe6060f1SDimitry Andric // We need an appendix block to place 'end_loop' or 'end_try' marker when the 108fe6060f1SDimitry Andric // loop / exception bottom block is the last block in a function 1090b57cec5SDimitry Andric MachineBasicBlock *AppendixBB = nullptr; 1100b57cec5SDimitry Andric MachineBasicBlock *getAppendixBlock(MachineFunction &MF) { 1110b57cec5SDimitry Andric if (!AppendixBB) { 1120b57cec5SDimitry Andric AppendixBB = MF.CreateMachineBasicBlock(); 1130b57cec5SDimitry Andric // Give it a fake predecessor so that AsmPrinter prints its label. 1140b57cec5SDimitry Andric AppendixBB->addSuccessor(AppendixBB); 1150b57cec5SDimitry Andric MF.push_back(AppendixBB); 1160b57cec5SDimitry Andric } 1170b57cec5SDimitry Andric return AppendixBB; 1180b57cec5SDimitry Andric } 1190b57cec5SDimitry Andric 120fe6060f1SDimitry Andric // Before running rewriteDepthImmediates function, 'delegate' has a BB as its 121fe6060f1SDimitry Andric // destination operand. getFakeCallerBlock() returns a fake BB that will be 122fe6060f1SDimitry Andric // used for the operand when 'delegate' needs to rethrow to the caller. This 123fe6060f1SDimitry Andric // will be rewritten as an immediate value that is the number of block depths 124fe6060f1SDimitry Andric // + 1 in rewriteDepthImmediates, and this fake BB will be removed at the end 125fe6060f1SDimitry Andric // of the pass. 126fe6060f1SDimitry Andric MachineBasicBlock *FakeCallerBB = nullptr; 127fe6060f1SDimitry Andric MachineBasicBlock *getFakeCallerBlock(MachineFunction &MF) { 128fe6060f1SDimitry Andric if (!FakeCallerBB) 129fe6060f1SDimitry Andric FakeCallerBB = MF.CreateMachineBasicBlock(); 130fe6060f1SDimitry Andric return FakeCallerBB; 131fe6060f1SDimitry Andric } 132fe6060f1SDimitry Andric 1330b57cec5SDimitry Andric // Helper functions to register / unregister scope information created by 1340b57cec5SDimitry Andric // marker instructions. 1350b57cec5SDimitry Andric void registerScope(MachineInstr *Begin, MachineInstr *End); 1360b57cec5SDimitry Andric void registerTryScope(MachineInstr *Begin, MachineInstr *End, 1370b57cec5SDimitry Andric MachineBasicBlock *EHPad); 1380b57cec5SDimitry Andric void unregisterScope(MachineInstr *Begin); 1390b57cec5SDimitry Andric 1400b57cec5SDimitry Andric public: 1410b57cec5SDimitry Andric static char ID; // Pass identification, replacement for typeid 1420b57cec5SDimitry Andric WebAssemblyCFGStackify() : MachineFunctionPass(ID) {} 1430b57cec5SDimitry Andric ~WebAssemblyCFGStackify() override { releaseMemory(); } 1440b57cec5SDimitry Andric void releaseMemory() override; 1450b57cec5SDimitry Andric }; 1460b57cec5SDimitry Andric } // end anonymous namespace 1470b57cec5SDimitry Andric 1480b57cec5SDimitry Andric char WebAssemblyCFGStackify::ID = 0; 1490b57cec5SDimitry Andric INITIALIZE_PASS(WebAssemblyCFGStackify, DEBUG_TYPE, 1500b57cec5SDimitry Andric "Insert BLOCK/LOOP/TRY markers for WebAssembly scopes", false, 1510b57cec5SDimitry Andric false) 1520b57cec5SDimitry Andric 1530b57cec5SDimitry Andric FunctionPass *llvm::createWebAssemblyCFGStackify() { 1540b57cec5SDimitry Andric return new WebAssemblyCFGStackify(); 1550b57cec5SDimitry Andric } 1560b57cec5SDimitry Andric 1570b57cec5SDimitry Andric /// Test whether Pred has any terminators explicitly branching to MBB, as 1580b57cec5SDimitry Andric /// opposed to falling through. Note that it's possible (eg. in unoptimized 1590b57cec5SDimitry Andric /// code) for a branch instruction to both branch to a block and fallthrough 1600b57cec5SDimitry Andric /// to it, so we check the actual branch operands to see if there are any 1610b57cec5SDimitry Andric /// explicit mentions. 1620b57cec5SDimitry Andric static bool explicitlyBranchesTo(MachineBasicBlock *Pred, 1630b57cec5SDimitry Andric MachineBasicBlock *MBB) { 1640b57cec5SDimitry Andric for (MachineInstr &MI : Pred->terminators()) 1650b57cec5SDimitry Andric for (MachineOperand &MO : MI.explicit_operands()) 1660b57cec5SDimitry Andric if (MO.isMBB() && MO.getMBB() == MBB) 1670b57cec5SDimitry Andric return true; 1680b57cec5SDimitry Andric return false; 1690b57cec5SDimitry Andric } 1700b57cec5SDimitry Andric 1710b57cec5SDimitry Andric // Returns an iterator to the earliest position possible within the MBB, 1720b57cec5SDimitry Andric // satisfying the restrictions given by BeforeSet and AfterSet. BeforeSet 1730b57cec5SDimitry Andric // contains instructions that should go before the marker, and AfterSet contains 1740b57cec5SDimitry Andric // ones that should go after the marker. In this function, AfterSet is only 175349cc55cSDimitry Andric // used for validation checking. 176e8d8bef9SDimitry Andric template <typename Container> 1770b57cec5SDimitry Andric static MachineBasicBlock::iterator 178e8d8bef9SDimitry Andric getEarliestInsertPos(MachineBasicBlock *MBB, const Container &BeforeSet, 179e8d8bef9SDimitry Andric const Container &AfterSet) { 1800b57cec5SDimitry Andric auto InsertPos = MBB->end(); 1810b57cec5SDimitry Andric while (InsertPos != MBB->begin()) { 1820b57cec5SDimitry Andric if (BeforeSet.count(&*std::prev(InsertPos))) { 1830b57cec5SDimitry Andric #ifndef NDEBUG 184349cc55cSDimitry Andric // Validation check 1850b57cec5SDimitry Andric for (auto Pos = InsertPos, E = MBB->begin(); Pos != E; --Pos) 1860b57cec5SDimitry Andric assert(!AfterSet.count(&*std::prev(Pos))); 1870b57cec5SDimitry Andric #endif 1880b57cec5SDimitry Andric break; 1890b57cec5SDimitry Andric } 1900b57cec5SDimitry Andric --InsertPos; 1910b57cec5SDimitry Andric } 1920b57cec5SDimitry Andric return InsertPos; 1930b57cec5SDimitry Andric } 1940b57cec5SDimitry Andric 1950b57cec5SDimitry Andric // Returns an iterator to the latest position possible within the MBB, 1960b57cec5SDimitry Andric // satisfying the restrictions given by BeforeSet and AfterSet. BeforeSet 1970b57cec5SDimitry Andric // contains instructions that should go before the marker, and AfterSet contains 1980b57cec5SDimitry Andric // ones that should go after the marker. In this function, BeforeSet is only 199349cc55cSDimitry Andric // used for validation checking. 200e8d8bef9SDimitry Andric template <typename Container> 2010b57cec5SDimitry Andric static MachineBasicBlock::iterator 202e8d8bef9SDimitry Andric getLatestInsertPos(MachineBasicBlock *MBB, const Container &BeforeSet, 203e8d8bef9SDimitry Andric const Container &AfterSet) { 2040b57cec5SDimitry Andric auto InsertPos = MBB->begin(); 2050b57cec5SDimitry Andric while (InsertPos != MBB->end()) { 2060b57cec5SDimitry Andric if (AfterSet.count(&*InsertPos)) { 2070b57cec5SDimitry Andric #ifndef NDEBUG 208349cc55cSDimitry Andric // Validation check 2090b57cec5SDimitry Andric for (auto Pos = InsertPos, E = MBB->end(); Pos != E; ++Pos) 2100b57cec5SDimitry Andric assert(!BeforeSet.count(&*Pos)); 2110b57cec5SDimitry Andric #endif 2120b57cec5SDimitry Andric break; 2130b57cec5SDimitry Andric } 2140b57cec5SDimitry Andric ++InsertPos; 2150b57cec5SDimitry Andric } 2160b57cec5SDimitry Andric return InsertPos; 2170b57cec5SDimitry Andric } 2180b57cec5SDimitry Andric 2190b57cec5SDimitry Andric void WebAssemblyCFGStackify::registerScope(MachineInstr *Begin, 2200b57cec5SDimitry Andric MachineInstr *End) { 2210b57cec5SDimitry Andric BeginToEnd[Begin] = End; 2220b57cec5SDimitry Andric EndToBegin[End] = Begin; 2230b57cec5SDimitry Andric } 2240b57cec5SDimitry Andric 225fe6060f1SDimitry Andric // When 'End' is not an 'end_try' but 'delegate, EHPad is nullptr. 2260b57cec5SDimitry Andric void WebAssemblyCFGStackify::registerTryScope(MachineInstr *Begin, 2270b57cec5SDimitry Andric MachineInstr *End, 2280b57cec5SDimitry Andric MachineBasicBlock *EHPad) { 2290b57cec5SDimitry Andric registerScope(Begin, End); 2300b57cec5SDimitry Andric TryToEHPad[Begin] = EHPad; 2310b57cec5SDimitry Andric EHPadToTry[EHPad] = Begin; 2320b57cec5SDimitry Andric } 2330b57cec5SDimitry Andric 2340b57cec5SDimitry Andric void WebAssemblyCFGStackify::unregisterScope(MachineInstr *Begin) { 2350b57cec5SDimitry Andric assert(BeginToEnd.count(Begin)); 2360b57cec5SDimitry Andric MachineInstr *End = BeginToEnd[Begin]; 2370b57cec5SDimitry Andric assert(EndToBegin.count(End)); 2380b57cec5SDimitry Andric BeginToEnd.erase(Begin); 2390b57cec5SDimitry Andric EndToBegin.erase(End); 2400b57cec5SDimitry Andric MachineBasicBlock *EHPad = TryToEHPad.lookup(Begin); 2410b57cec5SDimitry Andric if (EHPad) { 2420b57cec5SDimitry Andric assert(EHPadToTry.count(EHPad)); 2430b57cec5SDimitry Andric TryToEHPad.erase(Begin); 2440b57cec5SDimitry Andric EHPadToTry.erase(EHPad); 2450b57cec5SDimitry Andric } 2460b57cec5SDimitry Andric } 2470b57cec5SDimitry Andric 2480b57cec5SDimitry Andric /// Insert a BLOCK marker for branches to MBB (if needed). 2490b57cec5SDimitry Andric // TODO Consider a more generalized way of handling block (and also loop and 2500b57cec5SDimitry Andric // try) signatures when we implement the multi-value proposal later. 2510b57cec5SDimitry Andric void WebAssemblyCFGStackify::placeBlockMarker(MachineBasicBlock &MBB) { 2520b57cec5SDimitry Andric assert(!MBB.isEHPad()); 2530b57cec5SDimitry Andric MachineFunction &MF = *MBB.getParent(); 2540fca6ea1SDimitry Andric auto &MDT = getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree(); 2550b57cec5SDimitry Andric const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 2560b57cec5SDimitry Andric const auto &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); 2570b57cec5SDimitry Andric 2580b57cec5SDimitry Andric // First compute the nearest common dominator of all forward non-fallthrough 2590b57cec5SDimitry Andric // predecessors so that we minimize the time that the BLOCK is on the stack, 2600b57cec5SDimitry Andric // which reduces overall stack height. 2610b57cec5SDimitry Andric MachineBasicBlock *Header = nullptr; 2620b57cec5SDimitry Andric bool IsBranchedTo = false; 2630b57cec5SDimitry Andric int MBBNumber = MBB.getNumber(); 2640b57cec5SDimitry Andric for (MachineBasicBlock *Pred : MBB.predecessors()) { 2650b57cec5SDimitry Andric if (Pred->getNumber() < MBBNumber) { 2660b57cec5SDimitry Andric Header = Header ? MDT.findNearestCommonDominator(Header, Pred) : Pred; 267e8d8bef9SDimitry Andric if (explicitlyBranchesTo(Pred, &MBB)) 2680b57cec5SDimitry Andric IsBranchedTo = true; 2690b57cec5SDimitry Andric } 2700b57cec5SDimitry Andric } 2710b57cec5SDimitry Andric if (!Header) 2720b57cec5SDimitry Andric return; 2730b57cec5SDimitry Andric if (!IsBranchedTo) 2740b57cec5SDimitry Andric return; 2750b57cec5SDimitry Andric 2760b57cec5SDimitry Andric assert(&MBB != &MF.front() && "Header blocks shouldn't have predecessors"); 2770b57cec5SDimitry Andric MachineBasicBlock *LayoutPred = MBB.getPrevNode(); 2780b57cec5SDimitry Andric 2790b57cec5SDimitry Andric // If the nearest common dominator is inside a more deeply nested context, 2800b57cec5SDimitry Andric // walk out to the nearest scope which isn't more deeply nested. 2810b57cec5SDimitry Andric for (MachineFunction::iterator I(LayoutPred), E(Header); I != E; --I) { 2820b57cec5SDimitry Andric if (MachineBasicBlock *ScopeTop = ScopeTops[I->getNumber()]) { 2830b57cec5SDimitry Andric if (ScopeTop->getNumber() > Header->getNumber()) { 2840b57cec5SDimitry Andric // Skip over an intervening scope. 2850b57cec5SDimitry Andric I = std::next(ScopeTop->getIterator()); 2860b57cec5SDimitry Andric } else { 2870b57cec5SDimitry Andric // We found a scope level at an appropriate depth. 2880b57cec5SDimitry Andric Header = ScopeTop; 2890b57cec5SDimitry Andric break; 2900b57cec5SDimitry Andric } 2910b57cec5SDimitry Andric } 2920b57cec5SDimitry Andric } 2930b57cec5SDimitry Andric 2940b57cec5SDimitry Andric // Decide where in Header to put the BLOCK. 2950b57cec5SDimitry Andric 2960b57cec5SDimitry Andric // Instructions that should go before the BLOCK. 2970b57cec5SDimitry Andric SmallPtrSet<const MachineInstr *, 4> BeforeSet; 2980b57cec5SDimitry Andric // Instructions that should go after the BLOCK. 2990b57cec5SDimitry Andric SmallPtrSet<const MachineInstr *, 4> AfterSet; 3000b57cec5SDimitry Andric for (const auto &MI : *Header) { 3010b57cec5SDimitry Andric // If there is a previously placed LOOP marker and the bottom block of the 3020b57cec5SDimitry Andric // loop is above MBB, it should be after the BLOCK, because the loop is 3030b57cec5SDimitry Andric // nested in this BLOCK. Otherwise it should be before the BLOCK. 3040b57cec5SDimitry Andric if (MI.getOpcode() == WebAssembly::LOOP) { 3050b57cec5SDimitry Andric auto *LoopBottom = BeginToEnd[&MI]->getParent()->getPrevNode(); 3060b57cec5SDimitry Andric if (MBB.getNumber() > LoopBottom->getNumber()) 3070b57cec5SDimitry Andric AfterSet.insert(&MI); 3080b57cec5SDimitry Andric #ifndef NDEBUG 3090b57cec5SDimitry Andric else 3100b57cec5SDimitry Andric BeforeSet.insert(&MI); 3110b57cec5SDimitry Andric #endif 3120b57cec5SDimitry Andric } 3130b57cec5SDimitry Andric 3145ffd83dbSDimitry Andric // If there is a previously placed BLOCK/TRY marker and its corresponding 3155ffd83dbSDimitry Andric // END marker is before the current BLOCK's END marker, that should be 3165ffd83dbSDimitry Andric // placed after this BLOCK. Otherwise it should be placed before this BLOCK 3175ffd83dbSDimitry Andric // marker. 3180b57cec5SDimitry Andric if (MI.getOpcode() == WebAssembly::BLOCK || 3195ffd83dbSDimitry Andric MI.getOpcode() == WebAssembly::TRY) { 3205ffd83dbSDimitry Andric if (BeginToEnd[&MI]->getParent()->getNumber() <= MBB.getNumber()) 3210b57cec5SDimitry Andric AfterSet.insert(&MI); 3225ffd83dbSDimitry Andric #ifndef NDEBUG 3235ffd83dbSDimitry Andric else 3245ffd83dbSDimitry Andric BeforeSet.insert(&MI); 3255ffd83dbSDimitry Andric #endif 3265ffd83dbSDimitry Andric } 3270b57cec5SDimitry Andric 3280b57cec5SDimitry Andric #ifndef NDEBUG 3290b57cec5SDimitry Andric // All END_(BLOCK|LOOP|TRY) markers should be before the BLOCK. 3300b57cec5SDimitry Andric if (MI.getOpcode() == WebAssembly::END_BLOCK || 3310b57cec5SDimitry Andric MI.getOpcode() == WebAssembly::END_LOOP || 3320b57cec5SDimitry Andric MI.getOpcode() == WebAssembly::END_TRY) 3330b57cec5SDimitry Andric BeforeSet.insert(&MI); 3340b57cec5SDimitry Andric #endif 3350b57cec5SDimitry Andric 3360b57cec5SDimitry Andric // Terminators should go after the BLOCK. 3370b57cec5SDimitry Andric if (MI.isTerminator()) 3380b57cec5SDimitry Andric AfterSet.insert(&MI); 3390b57cec5SDimitry Andric } 3400b57cec5SDimitry Andric 3410b57cec5SDimitry Andric // Local expression tree should go after the BLOCK. 3420b57cec5SDimitry Andric for (auto I = Header->getFirstTerminator(), E = Header->begin(); I != E; 3430b57cec5SDimitry Andric --I) { 3440b57cec5SDimitry Andric if (std::prev(I)->isDebugInstr() || std::prev(I)->isPosition()) 3450b57cec5SDimitry Andric continue; 3460b57cec5SDimitry Andric if (WebAssembly::isChild(*std::prev(I), MFI)) 3470b57cec5SDimitry Andric AfterSet.insert(&*std::prev(I)); 3480b57cec5SDimitry Andric else 3490b57cec5SDimitry Andric break; 3500b57cec5SDimitry Andric } 3510b57cec5SDimitry Andric 3520b57cec5SDimitry Andric // Add the BLOCK. 3538bcb0991SDimitry Andric WebAssembly::BlockType ReturnType = WebAssembly::BlockType::Void; 3540b57cec5SDimitry Andric auto InsertPos = getLatestInsertPos(Header, BeforeSet, AfterSet); 3550b57cec5SDimitry Andric MachineInstr *Begin = 3560b57cec5SDimitry Andric BuildMI(*Header, InsertPos, Header->findDebugLoc(InsertPos), 3570b57cec5SDimitry Andric TII.get(WebAssembly::BLOCK)) 3580b57cec5SDimitry Andric .addImm(int64_t(ReturnType)); 3590b57cec5SDimitry Andric 3600b57cec5SDimitry Andric // Decide where in Header to put the END_BLOCK. 3610b57cec5SDimitry Andric BeforeSet.clear(); 3620b57cec5SDimitry Andric AfterSet.clear(); 3630b57cec5SDimitry Andric for (auto &MI : MBB) { 3640b57cec5SDimitry Andric #ifndef NDEBUG 3650b57cec5SDimitry Andric // END_BLOCK should precede existing LOOP and TRY markers. 3660b57cec5SDimitry Andric if (MI.getOpcode() == WebAssembly::LOOP || 3670b57cec5SDimitry Andric MI.getOpcode() == WebAssembly::TRY) 3680b57cec5SDimitry Andric AfterSet.insert(&MI); 3690b57cec5SDimitry Andric #endif 3700b57cec5SDimitry Andric 3710b57cec5SDimitry Andric // If there is a previously placed END_LOOP marker and the header of the 3720b57cec5SDimitry Andric // loop is above this block's header, the END_LOOP should be placed after 3730b57cec5SDimitry Andric // the BLOCK, because the loop contains this block. Otherwise the END_LOOP 3740b57cec5SDimitry Andric // should be placed before the BLOCK. The same for END_TRY. 3750b57cec5SDimitry Andric if (MI.getOpcode() == WebAssembly::END_LOOP || 3760b57cec5SDimitry Andric MI.getOpcode() == WebAssembly::END_TRY) { 3770b57cec5SDimitry Andric if (EndToBegin[&MI]->getParent()->getNumber() >= Header->getNumber()) 3780b57cec5SDimitry Andric BeforeSet.insert(&MI); 3790b57cec5SDimitry Andric #ifndef NDEBUG 3800b57cec5SDimitry Andric else 3810b57cec5SDimitry Andric AfterSet.insert(&MI); 3820b57cec5SDimitry Andric #endif 3830b57cec5SDimitry Andric } 3840b57cec5SDimitry Andric } 3850b57cec5SDimitry Andric 3860b57cec5SDimitry Andric // Mark the end of the block. 3870b57cec5SDimitry Andric InsertPos = getEarliestInsertPos(&MBB, BeforeSet, AfterSet); 3880b57cec5SDimitry Andric MachineInstr *End = BuildMI(MBB, InsertPos, MBB.findPrevDebugLoc(InsertPos), 3890b57cec5SDimitry Andric TII.get(WebAssembly::END_BLOCK)); 3900b57cec5SDimitry Andric registerScope(Begin, End); 3910b57cec5SDimitry Andric 3920b57cec5SDimitry Andric // Track the farthest-spanning scope that ends at this point. 393e8d8bef9SDimitry Andric updateScopeTops(Header, &MBB); 3940b57cec5SDimitry Andric } 3950b57cec5SDimitry Andric 3960b57cec5SDimitry Andric /// Insert a LOOP marker for a loop starting at MBB (if it's a loop header). 3970b57cec5SDimitry Andric void WebAssemblyCFGStackify::placeLoopMarker(MachineBasicBlock &MBB) { 3980b57cec5SDimitry Andric MachineFunction &MF = *MBB.getParent(); 3990fca6ea1SDimitry Andric const auto &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI(); 400e8d8bef9SDimitry Andric const auto &WEI = getAnalysis<WebAssemblyExceptionInfo>(); 401e8d8bef9SDimitry Andric SortRegionInfo SRI(MLI, WEI); 4020b57cec5SDimitry Andric const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 4030b57cec5SDimitry Andric 4040b57cec5SDimitry Andric MachineLoop *Loop = MLI.getLoopFor(&MBB); 4050b57cec5SDimitry Andric if (!Loop || Loop->getHeader() != &MBB) 4060b57cec5SDimitry Andric return; 4070b57cec5SDimitry Andric 4080b57cec5SDimitry Andric // The operand of a LOOP is the first block after the loop. If the loop is the 4090b57cec5SDimitry Andric // bottom of the function, insert a dummy block at the end. 410e8d8bef9SDimitry Andric MachineBasicBlock *Bottom = SRI.getBottom(Loop); 4110b57cec5SDimitry Andric auto Iter = std::next(Bottom->getIterator()); 4120b57cec5SDimitry Andric if (Iter == MF.end()) { 4130b57cec5SDimitry Andric getAppendixBlock(MF); 4140b57cec5SDimitry Andric Iter = std::next(Bottom->getIterator()); 4150b57cec5SDimitry Andric } 4160b57cec5SDimitry Andric MachineBasicBlock *AfterLoop = &*Iter; 4170b57cec5SDimitry Andric 4180b57cec5SDimitry Andric // Decide where in Header to put the LOOP. 4190b57cec5SDimitry Andric SmallPtrSet<const MachineInstr *, 4> BeforeSet; 4200b57cec5SDimitry Andric SmallPtrSet<const MachineInstr *, 4> AfterSet; 4210b57cec5SDimitry Andric for (const auto &MI : MBB) { 4220b57cec5SDimitry Andric // LOOP marker should be after any existing loop that ends here. Otherwise 4230b57cec5SDimitry Andric // we assume the instruction belongs to the loop. 4240b57cec5SDimitry Andric if (MI.getOpcode() == WebAssembly::END_LOOP) 4250b57cec5SDimitry Andric BeforeSet.insert(&MI); 4260b57cec5SDimitry Andric #ifndef NDEBUG 4270b57cec5SDimitry Andric else 4280b57cec5SDimitry Andric AfterSet.insert(&MI); 4290b57cec5SDimitry Andric #endif 4300b57cec5SDimitry Andric } 4310b57cec5SDimitry Andric 4320b57cec5SDimitry Andric // Mark the beginning of the loop. 4330b57cec5SDimitry Andric auto InsertPos = getEarliestInsertPos(&MBB, BeforeSet, AfterSet); 4340b57cec5SDimitry Andric MachineInstr *Begin = BuildMI(MBB, InsertPos, MBB.findDebugLoc(InsertPos), 4350b57cec5SDimitry Andric TII.get(WebAssembly::LOOP)) 4368bcb0991SDimitry Andric .addImm(int64_t(WebAssembly::BlockType::Void)); 4370b57cec5SDimitry Andric 4380b57cec5SDimitry Andric // Decide where in Header to put the END_LOOP. 4390b57cec5SDimitry Andric BeforeSet.clear(); 4400b57cec5SDimitry Andric AfterSet.clear(); 4410b57cec5SDimitry Andric #ifndef NDEBUG 4420b57cec5SDimitry Andric for (const auto &MI : MBB) 4430b57cec5SDimitry Andric // Existing END_LOOP markers belong to parent loops of this loop 4440b57cec5SDimitry Andric if (MI.getOpcode() == WebAssembly::END_LOOP) 4450b57cec5SDimitry Andric AfterSet.insert(&MI); 4460b57cec5SDimitry Andric #endif 4470b57cec5SDimitry Andric 4480b57cec5SDimitry Andric // Mark the end of the loop (using arbitrary debug location that branched to 4490b57cec5SDimitry Andric // the loop end as its location). 4500b57cec5SDimitry Andric InsertPos = getEarliestInsertPos(AfterLoop, BeforeSet, AfterSet); 4510b57cec5SDimitry Andric DebugLoc EndDL = AfterLoop->pred_empty() 4520b57cec5SDimitry Andric ? DebugLoc() 4530b57cec5SDimitry Andric : (*AfterLoop->pred_rbegin())->findBranchDebugLoc(); 4540b57cec5SDimitry Andric MachineInstr *End = 4550b57cec5SDimitry Andric BuildMI(*AfterLoop, InsertPos, EndDL, TII.get(WebAssembly::END_LOOP)); 4560b57cec5SDimitry Andric registerScope(Begin, End); 4570b57cec5SDimitry Andric 4580b57cec5SDimitry Andric assert((!ScopeTops[AfterLoop->getNumber()] || 4590b57cec5SDimitry Andric ScopeTops[AfterLoop->getNumber()]->getNumber() < MBB.getNumber()) && 4600b57cec5SDimitry Andric "With block sorting the outermost loop for a block should be first."); 461e8d8bef9SDimitry Andric updateScopeTops(&MBB, AfterLoop); 4620b57cec5SDimitry Andric } 4630b57cec5SDimitry Andric 4640b57cec5SDimitry Andric void WebAssemblyCFGStackify::placeTryMarker(MachineBasicBlock &MBB) { 4650b57cec5SDimitry Andric assert(MBB.isEHPad()); 4660b57cec5SDimitry Andric MachineFunction &MF = *MBB.getParent(); 4670fca6ea1SDimitry Andric auto &MDT = getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree(); 4680b57cec5SDimitry Andric const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 4690fca6ea1SDimitry Andric const auto &MLI = getAnalysis<MachineLoopInfoWrapperPass>().getLI(); 4700b57cec5SDimitry Andric const auto &WEI = getAnalysis<WebAssemblyExceptionInfo>(); 471e8d8bef9SDimitry Andric SortRegionInfo SRI(MLI, WEI); 4720b57cec5SDimitry Andric const auto &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); 4730b57cec5SDimitry Andric 4740b57cec5SDimitry Andric // Compute the nearest common dominator of all unwind predecessors 4750b57cec5SDimitry Andric MachineBasicBlock *Header = nullptr; 4760b57cec5SDimitry Andric int MBBNumber = MBB.getNumber(); 4770b57cec5SDimitry Andric for (auto *Pred : MBB.predecessors()) { 4780b57cec5SDimitry Andric if (Pred->getNumber() < MBBNumber) { 4790b57cec5SDimitry Andric Header = Header ? MDT.findNearestCommonDominator(Header, Pred) : Pred; 4800b57cec5SDimitry Andric assert(!explicitlyBranchesTo(Pred, &MBB) && 4810b57cec5SDimitry Andric "Explicit branch to an EH pad!"); 4820b57cec5SDimitry Andric } 4830b57cec5SDimitry Andric } 4840b57cec5SDimitry Andric if (!Header) 4850b57cec5SDimitry Andric return; 4860b57cec5SDimitry Andric 4870b57cec5SDimitry Andric // If this try is at the bottom of the function, insert a dummy block at the 4880b57cec5SDimitry Andric // end. 4890b57cec5SDimitry Andric WebAssemblyException *WE = WEI.getExceptionFor(&MBB); 4900b57cec5SDimitry Andric assert(WE); 491e8d8bef9SDimitry Andric MachineBasicBlock *Bottom = SRI.getBottom(WE); 4920b57cec5SDimitry Andric 4930b57cec5SDimitry Andric auto Iter = std::next(Bottom->getIterator()); 4940b57cec5SDimitry Andric if (Iter == MF.end()) { 4950b57cec5SDimitry Andric getAppendixBlock(MF); 4960b57cec5SDimitry Andric Iter = std::next(Bottom->getIterator()); 4970b57cec5SDimitry Andric } 4980b57cec5SDimitry Andric MachineBasicBlock *Cont = &*Iter; 4990b57cec5SDimitry Andric 5000b57cec5SDimitry Andric assert(Cont != &MF.front()); 5010b57cec5SDimitry Andric MachineBasicBlock *LayoutPred = Cont->getPrevNode(); 5020b57cec5SDimitry Andric 5030b57cec5SDimitry Andric // If the nearest common dominator is inside a more deeply nested context, 5040b57cec5SDimitry Andric // walk out to the nearest scope which isn't more deeply nested. 5050b57cec5SDimitry Andric for (MachineFunction::iterator I(LayoutPred), E(Header); I != E; --I) { 5060b57cec5SDimitry Andric if (MachineBasicBlock *ScopeTop = ScopeTops[I->getNumber()]) { 5070b57cec5SDimitry Andric if (ScopeTop->getNumber() > Header->getNumber()) { 5080b57cec5SDimitry Andric // Skip over an intervening scope. 5090b57cec5SDimitry Andric I = std::next(ScopeTop->getIterator()); 5100b57cec5SDimitry Andric } else { 5110b57cec5SDimitry Andric // We found a scope level at an appropriate depth. 5120b57cec5SDimitry Andric Header = ScopeTop; 5130b57cec5SDimitry Andric break; 5140b57cec5SDimitry Andric } 5150b57cec5SDimitry Andric } 5160b57cec5SDimitry Andric } 5170b57cec5SDimitry Andric 5180b57cec5SDimitry Andric // Decide where in Header to put the TRY. 5190b57cec5SDimitry Andric 5200b57cec5SDimitry Andric // Instructions that should go before the TRY. 5210b57cec5SDimitry Andric SmallPtrSet<const MachineInstr *, 4> BeforeSet; 5220b57cec5SDimitry Andric // Instructions that should go after the TRY. 5230b57cec5SDimitry Andric SmallPtrSet<const MachineInstr *, 4> AfterSet; 5240b57cec5SDimitry Andric for (const auto &MI : *Header) { 5250b57cec5SDimitry Andric // If there is a previously placed LOOP marker and the bottom block of the 5260b57cec5SDimitry Andric // loop is above MBB, it should be after the TRY, because the loop is nested 5270b57cec5SDimitry Andric // in this TRY. Otherwise it should be before the TRY. 5280b57cec5SDimitry Andric if (MI.getOpcode() == WebAssembly::LOOP) { 5290b57cec5SDimitry Andric auto *LoopBottom = BeginToEnd[&MI]->getParent()->getPrevNode(); 5300b57cec5SDimitry Andric if (MBB.getNumber() > LoopBottom->getNumber()) 5310b57cec5SDimitry Andric AfterSet.insert(&MI); 5320b57cec5SDimitry Andric #ifndef NDEBUG 5330b57cec5SDimitry Andric else 5340b57cec5SDimitry Andric BeforeSet.insert(&MI); 5350b57cec5SDimitry Andric #endif 5360b57cec5SDimitry Andric } 5370b57cec5SDimitry Andric 5380b57cec5SDimitry Andric // All previously inserted BLOCK/TRY markers should be after the TRY because 5390b57cec5SDimitry Andric // they are all nested trys. 5400b57cec5SDimitry Andric if (MI.getOpcode() == WebAssembly::BLOCK || 5410b57cec5SDimitry Andric MI.getOpcode() == WebAssembly::TRY) 5420b57cec5SDimitry Andric AfterSet.insert(&MI); 5430b57cec5SDimitry Andric 5440b57cec5SDimitry Andric #ifndef NDEBUG 5450b57cec5SDimitry Andric // All END_(BLOCK/LOOP/TRY) markers should be before the TRY. 5460b57cec5SDimitry Andric if (MI.getOpcode() == WebAssembly::END_BLOCK || 5470b57cec5SDimitry Andric MI.getOpcode() == WebAssembly::END_LOOP || 5480b57cec5SDimitry Andric MI.getOpcode() == WebAssembly::END_TRY) 5490b57cec5SDimitry Andric BeforeSet.insert(&MI); 5500b57cec5SDimitry Andric #endif 5510b57cec5SDimitry Andric 5520b57cec5SDimitry Andric // Terminators should go after the TRY. 5530b57cec5SDimitry Andric if (MI.isTerminator()) 5540b57cec5SDimitry Andric AfterSet.insert(&MI); 5550b57cec5SDimitry Andric } 5560b57cec5SDimitry Andric 5578bcb0991SDimitry Andric // If Header unwinds to MBB (= Header contains 'invoke'), the try block should 5588bcb0991SDimitry Andric // contain the call within it. So the call should go after the TRY. The 5598bcb0991SDimitry Andric // exception is when the header's terminator is a rethrow instruction, in 5608bcb0991SDimitry Andric // which case that instruction, not a call instruction before it, is gonna 5618bcb0991SDimitry Andric // throw. 5628bcb0991SDimitry Andric MachineInstr *ThrowingCall = nullptr; 5638bcb0991SDimitry Andric if (MBB.isPredecessor(Header)) { 5648bcb0991SDimitry Andric auto TermPos = Header->getFirstTerminator(); 5658bcb0991SDimitry Andric if (TermPos == Header->end() || 5668bcb0991SDimitry Andric TermPos->getOpcode() != WebAssembly::RETHROW) { 5678bcb0991SDimitry Andric for (auto &MI : reverse(*Header)) { 5688bcb0991SDimitry Andric if (MI.isCall()) { 5698bcb0991SDimitry Andric AfterSet.insert(&MI); 5708bcb0991SDimitry Andric ThrowingCall = &MI; 5718bcb0991SDimitry Andric // Possibly throwing calls are usually wrapped by EH_LABEL 5728bcb0991SDimitry Andric // instructions. We don't want to split them and the call. 5738bcb0991SDimitry Andric if (MI.getIterator() != Header->begin() && 5748bcb0991SDimitry Andric std::prev(MI.getIterator())->isEHLabel()) { 5758bcb0991SDimitry Andric AfterSet.insert(&*std::prev(MI.getIterator())); 5768bcb0991SDimitry Andric ThrowingCall = &*std::prev(MI.getIterator()); 5778bcb0991SDimitry Andric } 5788bcb0991SDimitry Andric break; 5798bcb0991SDimitry Andric } 5808bcb0991SDimitry Andric } 5818bcb0991SDimitry Andric } 5828bcb0991SDimitry Andric } 5838bcb0991SDimitry Andric 5840b57cec5SDimitry Andric // Local expression tree should go after the TRY. 5858bcb0991SDimitry Andric // For BLOCK placement, we start the search from the previous instruction of a 5868bcb0991SDimitry Andric // BB's terminator, but in TRY's case, we should start from the previous 5878bcb0991SDimitry Andric // instruction of a call that can throw, or a EH_LABEL that precedes the call, 5888bcb0991SDimitry Andric // because the return values of the call's previous instructions can be 5898bcb0991SDimitry Andric // stackified and consumed by the throwing call. 5908bcb0991SDimitry Andric auto SearchStartPt = ThrowingCall ? MachineBasicBlock::iterator(ThrowingCall) 5918bcb0991SDimitry Andric : Header->getFirstTerminator(); 5928bcb0991SDimitry Andric for (auto I = SearchStartPt, E = Header->begin(); I != E; --I) { 5930b57cec5SDimitry Andric if (std::prev(I)->isDebugInstr() || std::prev(I)->isPosition()) 5940b57cec5SDimitry Andric continue; 5950b57cec5SDimitry Andric if (WebAssembly::isChild(*std::prev(I), MFI)) 5960b57cec5SDimitry Andric AfterSet.insert(&*std::prev(I)); 5970b57cec5SDimitry Andric else 5980b57cec5SDimitry Andric break; 5990b57cec5SDimitry Andric } 6000b57cec5SDimitry Andric 6010b57cec5SDimitry Andric // Add the TRY. 6020b57cec5SDimitry Andric auto InsertPos = getLatestInsertPos(Header, BeforeSet, AfterSet); 6030b57cec5SDimitry Andric MachineInstr *Begin = 6040b57cec5SDimitry Andric BuildMI(*Header, InsertPos, Header->findDebugLoc(InsertPos), 6050b57cec5SDimitry Andric TII.get(WebAssembly::TRY)) 6068bcb0991SDimitry Andric .addImm(int64_t(WebAssembly::BlockType::Void)); 6070b57cec5SDimitry Andric 6080b57cec5SDimitry Andric // Decide where in Header to put the END_TRY. 6090b57cec5SDimitry Andric BeforeSet.clear(); 6100b57cec5SDimitry Andric AfterSet.clear(); 6110b57cec5SDimitry Andric for (const auto &MI : *Cont) { 6120b57cec5SDimitry Andric #ifndef NDEBUG 6130b57cec5SDimitry Andric // END_TRY should precede existing LOOP and BLOCK markers. 6140b57cec5SDimitry Andric if (MI.getOpcode() == WebAssembly::LOOP || 6150b57cec5SDimitry Andric MI.getOpcode() == WebAssembly::BLOCK) 6160b57cec5SDimitry Andric AfterSet.insert(&MI); 6170b57cec5SDimitry Andric 6180b57cec5SDimitry Andric // All END_TRY markers placed earlier belong to exceptions that contains 6190b57cec5SDimitry Andric // this one. 6200b57cec5SDimitry Andric if (MI.getOpcode() == WebAssembly::END_TRY) 6210b57cec5SDimitry Andric AfterSet.insert(&MI); 6220b57cec5SDimitry Andric #endif 6230b57cec5SDimitry Andric 6240b57cec5SDimitry Andric // If there is a previously placed END_LOOP marker and its header is after 6250b57cec5SDimitry Andric // where TRY marker is, this loop is contained within the 'catch' part, so 6260b57cec5SDimitry Andric // the END_TRY marker should go after that. Otherwise, the whole try-catch 6270b57cec5SDimitry Andric // is contained within this loop, so the END_TRY should go before that. 6280b57cec5SDimitry Andric if (MI.getOpcode() == WebAssembly::END_LOOP) { 6290b57cec5SDimitry Andric // For a LOOP to be after TRY, LOOP's BB should be after TRY's BB; if they 6300b57cec5SDimitry Andric // are in the same BB, LOOP is always before TRY. 6310b57cec5SDimitry Andric if (EndToBegin[&MI]->getParent()->getNumber() > Header->getNumber()) 6320b57cec5SDimitry Andric BeforeSet.insert(&MI); 6330b57cec5SDimitry Andric #ifndef NDEBUG 6340b57cec5SDimitry Andric else 6350b57cec5SDimitry Andric AfterSet.insert(&MI); 6360b57cec5SDimitry Andric #endif 6370b57cec5SDimitry Andric } 6380b57cec5SDimitry Andric 6390b57cec5SDimitry Andric // It is not possible for an END_BLOCK to be already in this block. 6400b57cec5SDimitry Andric } 6410b57cec5SDimitry Andric 6420b57cec5SDimitry Andric // Mark the end of the TRY. 6430b57cec5SDimitry Andric InsertPos = getEarliestInsertPos(Cont, BeforeSet, AfterSet); 6440b57cec5SDimitry Andric MachineInstr *End = 6450b57cec5SDimitry Andric BuildMI(*Cont, InsertPos, Bottom->findBranchDebugLoc(), 6460b57cec5SDimitry Andric TII.get(WebAssembly::END_TRY)); 6470b57cec5SDimitry Andric registerTryScope(Begin, End, &MBB); 6480b57cec5SDimitry Andric 6490b57cec5SDimitry Andric // Track the farthest-spanning scope that ends at this point. We create two 6500b57cec5SDimitry Andric // mappings: (BB with 'end_try' -> BB with 'try') and (BB with 'catch' -> BB 6510b57cec5SDimitry Andric // with 'try'). We need to create 'catch' -> 'try' mapping here too because 6520b57cec5SDimitry Andric // markers should not span across 'catch'. For example, this should not 6530b57cec5SDimitry Andric // happen: 6540b57cec5SDimitry Andric // 6550b57cec5SDimitry Andric // try 6560b57cec5SDimitry Andric // block --| (X) 6570b57cec5SDimitry Andric // catch | 6580b57cec5SDimitry Andric // end_block --| 6590b57cec5SDimitry Andric // end_try 660e8d8bef9SDimitry Andric for (auto *End : {&MBB, Cont}) 661e8d8bef9SDimitry Andric updateScopeTops(Header, End); 6620b57cec5SDimitry Andric } 6630b57cec5SDimitry Andric 6640b57cec5SDimitry Andric void WebAssemblyCFGStackify::removeUnnecessaryInstrs(MachineFunction &MF) { 6650b57cec5SDimitry Andric const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 6660b57cec5SDimitry Andric 6670b57cec5SDimitry Andric // When there is an unconditional branch right before a catch instruction and 6680b57cec5SDimitry Andric // it branches to the end of end_try marker, we don't need the branch, because 6695f757f3fSDimitry Andric // if there is no exception, the control flow transfers to that point anyway. 6700b57cec5SDimitry Andric // bb0: 6710b57cec5SDimitry Andric // try 6720b57cec5SDimitry Andric // ... 6730b57cec5SDimitry Andric // br bb2 <- Not necessary 674e8d8bef9SDimitry Andric // bb1 (ehpad): 6750b57cec5SDimitry Andric // catch 6760b57cec5SDimitry Andric // ... 677e8d8bef9SDimitry Andric // bb2: <- Continuation BB 6780b57cec5SDimitry Andric // end 679e8d8bef9SDimitry Andric // 680e8d8bef9SDimitry Andric // A more involved case: When the BB where 'end' is located is an another EH 681e8d8bef9SDimitry Andric // pad, the Cont (= continuation) BB is that EH pad's 'end' BB. For example, 682e8d8bef9SDimitry Andric // bb0: 683e8d8bef9SDimitry Andric // try 684e8d8bef9SDimitry Andric // try 685e8d8bef9SDimitry Andric // ... 686e8d8bef9SDimitry Andric // br bb3 <- Not necessary 687e8d8bef9SDimitry Andric // bb1 (ehpad): 688e8d8bef9SDimitry Andric // catch 689e8d8bef9SDimitry Andric // bb2 (ehpad): 690e8d8bef9SDimitry Andric // end 691e8d8bef9SDimitry Andric // catch 692e8d8bef9SDimitry Andric // ... 693e8d8bef9SDimitry Andric // bb3: <- Continuation BB 694e8d8bef9SDimitry Andric // end 695e8d8bef9SDimitry Andric // 696e8d8bef9SDimitry Andric // When the EH pad at hand is bb1, its matching end_try is in bb2. But it is 697e8d8bef9SDimitry Andric // another EH pad, so bb0's continuation BB becomes bb3. So 'br bb3' in the 698e8d8bef9SDimitry Andric // code can be deleted. This is why we run 'while' until 'Cont' is not an EH 699e8d8bef9SDimitry Andric // pad. 7000b57cec5SDimitry Andric for (auto &MBB : MF) { 7010b57cec5SDimitry Andric if (!MBB.isEHPad()) 7020b57cec5SDimitry Andric continue; 7030b57cec5SDimitry Andric 7040b57cec5SDimitry Andric MachineBasicBlock *TBB = nullptr, *FBB = nullptr; 7050b57cec5SDimitry Andric SmallVector<MachineOperand, 4> Cond; 7060b57cec5SDimitry Andric MachineBasicBlock *EHPadLayoutPred = MBB.getPrevNode(); 707e8d8bef9SDimitry Andric 708e8d8bef9SDimitry Andric MachineBasicBlock *Cont = &MBB; 709e8d8bef9SDimitry Andric while (Cont->isEHPad()) { 710e8d8bef9SDimitry Andric MachineInstr *Try = EHPadToTry[Cont]; 711e8d8bef9SDimitry Andric MachineInstr *EndTry = BeginToEnd[Try]; 712fe6060f1SDimitry Andric // We started from an EH pad, so the end marker cannot be a delegate 713fe6060f1SDimitry Andric assert(EndTry->getOpcode() != WebAssembly::DELEGATE); 714e8d8bef9SDimitry Andric Cont = EndTry->getParent(); 715e8d8bef9SDimitry Andric } 716e8d8bef9SDimitry Andric 7170b57cec5SDimitry Andric bool Analyzable = !TII.analyzeBranch(*EHPadLayoutPred, TBB, FBB, Cond); 7185ffd83dbSDimitry Andric // This condition means either 7195ffd83dbSDimitry Andric // 1. This BB ends with a single unconditional branch whose destinaion is 7205ffd83dbSDimitry Andric // Cont. 7215ffd83dbSDimitry Andric // 2. This BB ends with a conditional branch followed by an unconditional 7225ffd83dbSDimitry Andric // branch, and the unconditional branch's destination is Cont. 7235ffd83dbSDimitry Andric // In both cases, we want to remove the last (= unconditional) branch. 7240b57cec5SDimitry Andric if (Analyzable && ((Cond.empty() && TBB && TBB == Cont) || 7255ffd83dbSDimitry Andric (!Cond.empty() && FBB && FBB == Cont))) { 7265ffd83dbSDimitry Andric bool ErasedUncondBr = false; 7275ffd83dbSDimitry Andric (void)ErasedUncondBr; 7285ffd83dbSDimitry Andric for (auto I = EHPadLayoutPred->end(), E = EHPadLayoutPred->begin(); 7295ffd83dbSDimitry Andric I != E; --I) { 7305ffd83dbSDimitry Andric auto PrevI = std::prev(I); 7315ffd83dbSDimitry Andric if (PrevI->isTerminator()) { 7325ffd83dbSDimitry Andric assert(PrevI->getOpcode() == WebAssembly::BR); 7335ffd83dbSDimitry Andric PrevI->eraseFromParent(); 7345ffd83dbSDimitry Andric ErasedUncondBr = true; 7355ffd83dbSDimitry Andric break; 7365ffd83dbSDimitry Andric } 7375ffd83dbSDimitry Andric } 7385ffd83dbSDimitry Andric assert(ErasedUncondBr && "Unconditional branch not erased!"); 7395ffd83dbSDimitry Andric } 7400b57cec5SDimitry Andric } 7410b57cec5SDimitry Andric 7420b57cec5SDimitry Andric // When there are block / end_block markers that overlap with try / end_try 7430b57cec5SDimitry Andric // markers, and the block and try markers' return types are the same, the 7440b57cec5SDimitry Andric // block /end_block markers are not necessary, because try / end_try markers 7450b57cec5SDimitry Andric // also can serve as boundaries for branches. 7460b57cec5SDimitry Andric // block <- Not necessary 7470b57cec5SDimitry Andric // try 7480b57cec5SDimitry Andric // ... 7490b57cec5SDimitry Andric // catch 7500b57cec5SDimitry Andric // ... 7510b57cec5SDimitry Andric // end 7520b57cec5SDimitry Andric // end <- Not necessary 7530b57cec5SDimitry Andric SmallVector<MachineInstr *, 32> ToDelete; 7540b57cec5SDimitry Andric for (auto &MBB : MF) { 7550b57cec5SDimitry Andric for (auto &MI : MBB) { 7560b57cec5SDimitry Andric if (MI.getOpcode() != WebAssembly::TRY) 7570b57cec5SDimitry Andric continue; 7580b57cec5SDimitry Andric MachineInstr *Try = &MI, *EndTry = BeginToEnd[Try]; 759fe6060f1SDimitry Andric if (EndTry->getOpcode() == WebAssembly::DELEGATE) 760fe6060f1SDimitry Andric continue; 761fe6060f1SDimitry Andric 7620b57cec5SDimitry Andric MachineBasicBlock *TryBB = Try->getParent(); 7630b57cec5SDimitry Andric MachineBasicBlock *Cont = EndTry->getParent(); 7640b57cec5SDimitry Andric int64_t RetType = Try->getOperand(0).getImm(); 7650b57cec5SDimitry Andric for (auto B = Try->getIterator(), E = std::next(EndTry->getIterator()); 7660b57cec5SDimitry Andric B != TryBB->begin() && E != Cont->end() && 7670b57cec5SDimitry Andric std::prev(B)->getOpcode() == WebAssembly::BLOCK && 7680b57cec5SDimitry Andric E->getOpcode() == WebAssembly::END_BLOCK && 7690b57cec5SDimitry Andric std::prev(B)->getOperand(0).getImm() == RetType; 7700b57cec5SDimitry Andric --B, ++E) { 7710b57cec5SDimitry Andric ToDelete.push_back(&*std::prev(B)); 7720b57cec5SDimitry Andric ToDelete.push_back(&*E); 7730b57cec5SDimitry Andric } 7740b57cec5SDimitry Andric } 7750b57cec5SDimitry Andric } 7760b57cec5SDimitry Andric for (auto *MI : ToDelete) { 7770b57cec5SDimitry Andric if (MI->getOpcode() == WebAssembly::BLOCK) 7780b57cec5SDimitry Andric unregisterScope(MI); 7790b57cec5SDimitry Andric MI->eraseFromParent(); 7800b57cec5SDimitry Andric } 7810b57cec5SDimitry Andric } 7820b57cec5SDimitry Andric 7838bcb0991SDimitry Andric // When MBB is split into MBB and Split, we should unstackify defs in MBB that 7848bcb0991SDimitry Andric // have their uses in Split. 785fe6060f1SDimitry Andric static void unstackifyVRegsUsedInSplitBB(MachineBasicBlock &MBB, 786fe6060f1SDimitry Andric MachineBasicBlock &Split) { 787e8d8bef9SDimitry Andric MachineFunction &MF = *MBB.getParent(); 788e8d8bef9SDimitry Andric const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 789e8d8bef9SDimitry Andric auto &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); 790e8d8bef9SDimitry Andric auto &MRI = MF.getRegInfo(); 791e8d8bef9SDimitry Andric 7928bcb0991SDimitry Andric for (auto &MI : Split) { 7938bcb0991SDimitry Andric for (auto &MO : MI.explicit_uses()) { 794bdd1243dSDimitry Andric if (!MO.isReg() || MO.getReg().isPhysical()) 7958bcb0991SDimitry Andric continue; 7968bcb0991SDimitry Andric if (MachineInstr *Def = MRI.getUniqueVRegDef(MO.getReg())) 7978bcb0991SDimitry Andric if (Def->getParent() == &MBB) 7988bcb0991SDimitry Andric MFI.unstackifyVReg(MO.getReg()); 7998bcb0991SDimitry Andric } 8008bcb0991SDimitry Andric } 8015ffd83dbSDimitry Andric 8025ffd83dbSDimitry Andric // In RegStackify, when a register definition is used multiple times, 8035ffd83dbSDimitry Andric // Reg = INST ... 8045ffd83dbSDimitry Andric // INST ..., Reg, ... 8055ffd83dbSDimitry Andric // INST ..., Reg, ... 8065ffd83dbSDimitry Andric // INST ..., Reg, ... 8075ffd83dbSDimitry Andric // 8085ffd83dbSDimitry Andric // we introduce a TEE, which has the following form: 8095ffd83dbSDimitry Andric // DefReg = INST ... 8105ffd83dbSDimitry Andric // TeeReg, Reg = TEE_... DefReg 8115ffd83dbSDimitry Andric // INST ..., TeeReg, ... 8125ffd83dbSDimitry Andric // INST ..., Reg, ... 8135ffd83dbSDimitry Andric // INST ..., Reg, ... 8145ffd83dbSDimitry Andric // with DefReg and TeeReg stackified but Reg not stackified. 8155ffd83dbSDimitry Andric // 8165ffd83dbSDimitry Andric // But the invariant that TeeReg should be stackified can be violated while we 8175ffd83dbSDimitry Andric // unstackify registers in the split BB above. In this case, we convert TEEs 8185ffd83dbSDimitry Andric // into two COPYs. This COPY will be eventually eliminated in ExplicitLocals. 8195ffd83dbSDimitry Andric // DefReg = INST ... 8205ffd83dbSDimitry Andric // TeeReg = COPY DefReg 8215ffd83dbSDimitry Andric // Reg = COPY DefReg 8225ffd83dbSDimitry Andric // INST ..., TeeReg, ... 8235ffd83dbSDimitry Andric // INST ..., Reg, ... 8245ffd83dbSDimitry Andric // INST ..., Reg, ... 825349cc55cSDimitry Andric for (MachineInstr &MI : llvm::make_early_inc_range(MBB)) { 8265ffd83dbSDimitry Andric if (!WebAssembly::isTee(MI.getOpcode())) 8275ffd83dbSDimitry Andric continue; 8285ffd83dbSDimitry Andric Register TeeReg = MI.getOperand(0).getReg(); 8295ffd83dbSDimitry Andric Register Reg = MI.getOperand(1).getReg(); 8305ffd83dbSDimitry Andric Register DefReg = MI.getOperand(2).getReg(); 8315ffd83dbSDimitry Andric if (!MFI.isVRegStackified(TeeReg)) { 8325ffd83dbSDimitry Andric // Now we are not using TEE anymore, so unstackify DefReg too 8335ffd83dbSDimitry Andric MFI.unstackifyVReg(DefReg); 834753f127fSDimitry Andric unsigned CopyOpc = 835753f127fSDimitry Andric WebAssembly::getCopyOpcodeForRegClass(MRI.getRegClass(DefReg)); 8365ffd83dbSDimitry Andric BuildMI(MBB, &MI, MI.getDebugLoc(), TII.get(CopyOpc), TeeReg) 8375ffd83dbSDimitry Andric .addReg(DefReg); 8385ffd83dbSDimitry Andric BuildMI(MBB, &MI, MI.getDebugLoc(), TII.get(CopyOpc), Reg).addReg(DefReg); 8395ffd83dbSDimitry Andric MI.eraseFromParent(); 8405ffd83dbSDimitry Andric } 8415ffd83dbSDimitry Andric } 8428bcb0991SDimitry Andric } 8438bcb0991SDimitry Andric 844fe6060f1SDimitry Andric // Wrap the given range of instruction with try-delegate. RangeBegin and 845fe6060f1SDimitry Andric // RangeEnd are inclusive. 846fe6060f1SDimitry Andric void WebAssemblyCFGStackify::addTryDelegate(MachineInstr *RangeBegin, 847fe6060f1SDimitry Andric MachineInstr *RangeEnd, 848fe6060f1SDimitry Andric MachineBasicBlock *DelegateDest) { 849fe6060f1SDimitry Andric auto *BeginBB = RangeBegin->getParent(); 850fe6060f1SDimitry Andric auto *EndBB = RangeEnd->getParent(); 851fe6060f1SDimitry Andric MachineFunction &MF = *BeginBB->getParent(); 852fe6060f1SDimitry Andric const auto &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); 853fe6060f1SDimitry Andric const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 854fe6060f1SDimitry Andric 855fe6060f1SDimitry Andric // Local expression tree before the first call of this range should go 856fe6060f1SDimitry Andric // after the nested TRY. 857fe6060f1SDimitry Andric SmallPtrSet<const MachineInstr *, 4> AfterSet; 858fe6060f1SDimitry Andric AfterSet.insert(RangeBegin); 859fe6060f1SDimitry Andric for (auto I = MachineBasicBlock::iterator(RangeBegin), E = BeginBB->begin(); 860fe6060f1SDimitry Andric I != E; --I) { 861fe6060f1SDimitry Andric if (std::prev(I)->isDebugInstr() || std::prev(I)->isPosition()) 862fe6060f1SDimitry Andric continue; 863fe6060f1SDimitry Andric if (WebAssembly::isChild(*std::prev(I), MFI)) 864fe6060f1SDimitry Andric AfterSet.insert(&*std::prev(I)); 865fe6060f1SDimitry Andric else 866fe6060f1SDimitry Andric break; 8670b57cec5SDimitry Andric } 8680b57cec5SDimitry Andric 869fe6060f1SDimitry Andric // Create the nested try instruction. 870fe6060f1SDimitry Andric auto TryPos = getLatestInsertPos( 871fe6060f1SDimitry Andric BeginBB, SmallPtrSet<const MachineInstr *, 4>(), AfterSet); 872fe6060f1SDimitry Andric MachineInstr *Try = BuildMI(*BeginBB, TryPos, RangeBegin->getDebugLoc(), 873fe6060f1SDimitry Andric TII.get(WebAssembly::TRY)) 874fe6060f1SDimitry Andric .addImm(int64_t(WebAssembly::BlockType::Void)); 875fe6060f1SDimitry Andric 876fe6060f1SDimitry Andric // Create a BB to insert the 'delegate' instruction. 877fe6060f1SDimitry Andric MachineBasicBlock *DelegateBB = MF.CreateMachineBasicBlock(); 878fe6060f1SDimitry Andric // If the destination of 'delegate' is not the caller, adds the destination to 879fe6060f1SDimitry Andric // the BB's successors. 880fe6060f1SDimitry Andric if (DelegateDest != FakeCallerBB) 881fe6060f1SDimitry Andric DelegateBB->addSuccessor(DelegateDest); 882fe6060f1SDimitry Andric 883fe6060f1SDimitry Andric auto SplitPos = std::next(RangeEnd->getIterator()); 884fe6060f1SDimitry Andric if (SplitPos == EndBB->end()) { 885fe6060f1SDimitry Andric // If the range's end instruction is at the end of the BB, insert the new 886fe6060f1SDimitry Andric // delegate BB after the current BB. 887fe6060f1SDimitry Andric MF.insert(std::next(EndBB->getIterator()), DelegateBB); 888fe6060f1SDimitry Andric EndBB->addSuccessor(DelegateBB); 889fe6060f1SDimitry Andric 890fe6060f1SDimitry Andric } else { 891fe6060f1SDimitry Andric // When the split pos is in the middle of a BB, we split the BB into two and 892fe6060f1SDimitry Andric // put the 'delegate' BB in between. We normally create a split BB and make 893fe6060f1SDimitry Andric // it a successor of the original BB (PostSplit == true), but in case the BB 894fe6060f1SDimitry Andric // is an EH pad and the split pos is before 'catch', we should preserve the 895fe6060f1SDimitry Andric // BB's property, including that it is an EH pad, in the later part of the 896fe6060f1SDimitry Andric // BB, where 'catch' is. In this case we set PostSplit to false. 897fe6060f1SDimitry Andric bool PostSplit = true; 898fe6060f1SDimitry Andric if (EndBB->isEHPad()) { 899fe6060f1SDimitry Andric for (auto I = MachineBasicBlock::iterator(SplitPos), E = EndBB->end(); 900fe6060f1SDimitry Andric I != E; ++I) { 901fe6060f1SDimitry Andric if (WebAssembly::isCatch(I->getOpcode())) { 902fe6060f1SDimitry Andric PostSplit = false; 9030b57cec5SDimitry Andric break; 9040b57cec5SDimitry Andric } 905fe6060f1SDimitry Andric } 906fe6060f1SDimitry Andric } 907fe6060f1SDimitry Andric 908fe6060f1SDimitry Andric MachineBasicBlock *PreBB = nullptr, *PostBB = nullptr; 909fe6060f1SDimitry Andric if (PostSplit) { 910fe6060f1SDimitry Andric // If the range's end instruction is in the middle of the BB, we split the 911fe6060f1SDimitry Andric // BB into two and insert the delegate BB in between. 912fe6060f1SDimitry Andric // - Before: 913fe6060f1SDimitry Andric // bb: 914fe6060f1SDimitry Andric // range_end 915fe6060f1SDimitry Andric // other_insts 916fe6060f1SDimitry Andric // 917fe6060f1SDimitry Andric // - After: 918fe6060f1SDimitry Andric // pre_bb: (previous 'bb') 919fe6060f1SDimitry Andric // range_end 920fe6060f1SDimitry Andric // delegate_bb: (new) 921fe6060f1SDimitry Andric // delegate 922fe6060f1SDimitry Andric // post_bb: (new) 923fe6060f1SDimitry Andric // other_insts 924fe6060f1SDimitry Andric PreBB = EndBB; 925fe6060f1SDimitry Andric PostBB = MF.CreateMachineBasicBlock(); 926fe6060f1SDimitry Andric MF.insert(std::next(PreBB->getIterator()), PostBB); 927fe6060f1SDimitry Andric MF.insert(std::next(PreBB->getIterator()), DelegateBB); 928fe6060f1SDimitry Andric PostBB->splice(PostBB->end(), PreBB, SplitPos, PreBB->end()); 929fe6060f1SDimitry Andric PostBB->transferSuccessors(PreBB); 930fe6060f1SDimitry Andric } else { 931fe6060f1SDimitry Andric // - Before: 932fe6060f1SDimitry Andric // ehpad: 933fe6060f1SDimitry Andric // range_end 934fe6060f1SDimitry Andric // catch 935fe6060f1SDimitry Andric // ... 936fe6060f1SDimitry Andric // 937fe6060f1SDimitry Andric // - After: 938fe6060f1SDimitry Andric // pre_bb: (new) 939fe6060f1SDimitry Andric // range_end 940fe6060f1SDimitry Andric // delegate_bb: (new) 941fe6060f1SDimitry Andric // delegate 942fe6060f1SDimitry Andric // post_bb: (previous 'ehpad') 943fe6060f1SDimitry Andric // catch 944fe6060f1SDimitry Andric // ... 945fe6060f1SDimitry Andric assert(EndBB->isEHPad()); 946fe6060f1SDimitry Andric PreBB = MF.CreateMachineBasicBlock(); 947fe6060f1SDimitry Andric PostBB = EndBB; 948fe6060f1SDimitry Andric MF.insert(PostBB->getIterator(), PreBB); 949fe6060f1SDimitry Andric MF.insert(PostBB->getIterator(), DelegateBB); 950fe6060f1SDimitry Andric PreBB->splice(PreBB->end(), PostBB, PostBB->begin(), SplitPos); 951fe6060f1SDimitry Andric // We don't need to transfer predecessors of the EH pad to 'PreBB', 952fe6060f1SDimitry Andric // because an EH pad's predecessors are all through unwind edges and they 953fe6060f1SDimitry Andric // should still unwind to the EH pad, not PreBB. 954fe6060f1SDimitry Andric } 955fe6060f1SDimitry Andric unstackifyVRegsUsedInSplitBB(*PreBB, *PostBB); 956fe6060f1SDimitry Andric PreBB->addSuccessor(DelegateBB); 957fe6060f1SDimitry Andric PreBB->addSuccessor(PostBB); 958fe6060f1SDimitry Andric } 959fe6060f1SDimitry Andric 960fe6060f1SDimitry Andric // Add 'delegate' instruction in the delegate BB created above. 961fe6060f1SDimitry Andric MachineInstr *Delegate = BuildMI(DelegateBB, RangeEnd->getDebugLoc(), 962fe6060f1SDimitry Andric TII.get(WebAssembly::DELEGATE)) 963fe6060f1SDimitry Andric .addMBB(DelegateDest); 964fe6060f1SDimitry Andric registerTryScope(Try, Delegate, nullptr); 965fe6060f1SDimitry Andric } 966fe6060f1SDimitry Andric 967fe6060f1SDimitry Andric bool WebAssemblyCFGStackify::fixCallUnwindMismatches(MachineFunction &MF) { 968fe6060f1SDimitry Andric // Linearizing the control flow by placing TRY / END_TRY markers can create 969fe6060f1SDimitry Andric // mismatches in unwind destinations for throwing instructions, such as calls. 970fe6060f1SDimitry Andric // 971fe6060f1SDimitry Andric // We use the 'delegate' instruction to fix the unwind mismatches. 'delegate' 972fe6060f1SDimitry Andric // instruction delegates an exception to an outer 'catch'. It can target not 973fe6060f1SDimitry Andric // only 'catch' but all block-like structures including another 'delegate', 974fe6060f1SDimitry Andric // but with slightly different semantics than branches. When it targets a 975fe6060f1SDimitry Andric // 'catch', it will delegate the exception to that catch. It is being 976fe6060f1SDimitry Andric // discussed how to define the semantics when 'delegate''s target is a non-try 977fe6060f1SDimitry Andric // block: it will either be a validation failure or it will target the next 978fe6060f1SDimitry Andric // outer try-catch. But anyway our LLVM backend currently does not generate 979fe6060f1SDimitry Andric // such code. The example below illustrates where the 'delegate' instruction 980fe6060f1SDimitry Andric // in the middle will delegate the exception to, depending on the value of N. 981fe6060f1SDimitry Andric // try 982fe6060f1SDimitry Andric // try 983fe6060f1SDimitry Andric // block 984fe6060f1SDimitry Andric // try 985fe6060f1SDimitry Andric // try 986fe6060f1SDimitry Andric // call @foo 987fe6060f1SDimitry Andric // delegate N ;; Where will this delegate to? 988fe6060f1SDimitry Andric // catch ;; N == 0 989fe6060f1SDimitry Andric // end 990fe6060f1SDimitry Andric // end ;; N == 1 (invalid; will not be generated) 991fe6060f1SDimitry Andric // delegate ;; N == 2 992fe6060f1SDimitry Andric // catch ;; N == 3 993fe6060f1SDimitry Andric // end 994fe6060f1SDimitry Andric // ;; N == 4 (to caller) 995fe6060f1SDimitry Andric 996fe6060f1SDimitry Andric // 1. When an instruction may throw, but the EH pad it will unwind to can be 997fe6060f1SDimitry Andric // different from the original CFG. 998fe6060f1SDimitry Andric // 999fe6060f1SDimitry Andric // Example: we have the following CFG: 1000fe6060f1SDimitry Andric // bb0: 1001fe6060f1SDimitry Andric // call @foo ; if it throws, unwind to bb2 1002fe6060f1SDimitry Andric // bb1: 1003fe6060f1SDimitry Andric // call @bar ; if it throws, unwind to bb3 1004fe6060f1SDimitry Andric // bb2 (ehpad): 1005fe6060f1SDimitry Andric // catch 1006fe6060f1SDimitry Andric // ... 1007fe6060f1SDimitry Andric // bb3 (ehpad) 1008fe6060f1SDimitry Andric // catch 1009fe6060f1SDimitry Andric // ... 1010fe6060f1SDimitry Andric // 1011fe6060f1SDimitry Andric // And the CFG is sorted in this order. Then after placing TRY markers, it 1012fe6060f1SDimitry Andric // will look like: (BB markers are omitted) 1013fe6060f1SDimitry Andric // try 1014fe6060f1SDimitry Andric // try 1015fe6060f1SDimitry Andric // call @foo 1016fe6060f1SDimitry Andric // call @bar ;; if it throws, unwind to bb3 1017fe6060f1SDimitry Andric // catch ;; ehpad (bb2) 1018fe6060f1SDimitry Andric // ... 1019fe6060f1SDimitry Andric // end_try 1020fe6060f1SDimitry Andric // catch ;; ehpad (bb3) 1021fe6060f1SDimitry Andric // ... 1022fe6060f1SDimitry Andric // end_try 1023fe6060f1SDimitry Andric // 1024fe6060f1SDimitry Andric // Now if bar() throws, it is going to end up ip in bb2, not bb3, where it 1025fe6060f1SDimitry Andric // is supposed to end up. We solve this problem by wrapping the mismatching 1026fe6060f1SDimitry Andric // call with an inner try-delegate that rethrows the exception to the right 1027fe6060f1SDimitry Andric // 'catch'. 1028fe6060f1SDimitry Andric // 1029fe6060f1SDimitry Andric // try 1030fe6060f1SDimitry Andric // try 1031fe6060f1SDimitry Andric // call @foo 1032fe6060f1SDimitry Andric // try ;; (new) 1033fe6060f1SDimitry Andric // call @bar 1034fe6060f1SDimitry Andric // delegate 1 (bb3) ;; (new) 1035fe6060f1SDimitry Andric // catch ;; ehpad (bb2) 1036fe6060f1SDimitry Andric // ... 1037fe6060f1SDimitry Andric // end_try 1038fe6060f1SDimitry Andric // catch ;; ehpad (bb3) 1039fe6060f1SDimitry Andric // ... 1040fe6060f1SDimitry Andric // end_try 1041fe6060f1SDimitry Andric // 1042fe6060f1SDimitry Andric // --- 1043fe6060f1SDimitry Andric // 2. The same as 1, but in this case an instruction unwinds to a caller 1044fe6060f1SDimitry Andric // function and not another EH pad. 1045fe6060f1SDimitry Andric // 1046fe6060f1SDimitry Andric // Example: we have the following CFG: 1047fe6060f1SDimitry Andric // bb0: 1048fe6060f1SDimitry Andric // call @foo ; if it throws, unwind to bb2 1049fe6060f1SDimitry Andric // bb1: 1050fe6060f1SDimitry Andric // call @bar ; if it throws, unwind to caller 1051fe6060f1SDimitry Andric // bb2 (ehpad): 1052fe6060f1SDimitry Andric // catch 1053fe6060f1SDimitry Andric // ... 1054fe6060f1SDimitry Andric // 1055fe6060f1SDimitry Andric // And the CFG is sorted in this order. Then after placing TRY markers, it 1056fe6060f1SDimitry Andric // will look like: 1057fe6060f1SDimitry Andric // try 1058fe6060f1SDimitry Andric // call @foo 1059fe6060f1SDimitry Andric // call @bar ;; if it throws, unwind to caller 1060fe6060f1SDimitry Andric // catch ;; ehpad (bb2) 1061fe6060f1SDimitry Andric // ... 1062fe6060f1SDimitry Andric // end_try 1063fe6060f1SDimitry Andric // 1064fe6060f1SDimitry Andric // Now if bar() throws, it is going to end up ip in bb2, when it is supposed 1065fe6060f1SDimitry Andric // throw up to the caller. We solve this problem in the same way, but in this 1066fe6060f1SDimitry Andric // case 'delegate's immediate argument is the number of block depths + 1, 1067fe6060f1SDimitry Andric // which means it rethrows to the caller. 1068fe6060f1SDimitry Andric // try 1069fe6060f1SDimitry Andric // call @foo 1070fe6060f1SDimitry Andric // try ;; (new) 1071fe6060f1SDimitry Andric // call @bar 1072fe6060f1SDimitry Andric // delegate 1 (caller) ;; (new) 1073fe6060f1SDimitry Andric // catch ;; ehpad (bb2) 1074fe6060f1SDimitry Andric // ... 1075fe6060f1SDimitry Andric // end_try 1076fe6060f1SDimitry Andric // 1077fe6060f1SDimitry Andric // Before rewriteDepthImmediates, delegate's argument is a BB. In case of the 1078fe6060f1SDimitry Andric // caller, it will take a fake BB generated by getFakeCallerBlock(), which 1079fe6060f1SDimitry Andric // will be converted to a correct immediate argument later. 1080fe6060f1SDimitry Andric // 1081fe6060f1SDimitry Andric // In case there are multiple calls in a BB that may throw to the caller, they 1082fe6060f1SDimitry Andric // can be wrapped together in one nested try-delegate scope. (In 1, this 1083fe6060f1SDimitry Andric // couldn't happen, because may-throwing instruction there had an unwind 1084fe6060f1SDimitry Andric // destination, i.e., it was an invoke before, and there could be only one 1085fe6060f1SDimitry Andric // invoke within a BB.) 1086fe6060f1SDimitry Andric 1087fe6060f1SDimitry Andric SmallVector<const MachineBasicBlock *, 8> EHPadStack; 1088fe6060f1SDimitry Andric // Range of intructions to be wrapped in a new nested try/catch. A range 1089fe6060f1SDimitry Andric // exists in a single BB and does not span multiple BBs. 1090fe6060f1SDimitry Andric using TryRange = std::pair<MachineInstr *, MachineInstr *>; 1091fe6060f1SDimitry Andric // In original CFG, <unwind destination BB, a vector of try ranges> 1092fe6060f1SDimitry Andric DenseMap<MachineBasicBlock *, SmallVector<TryRange, 4>> UnwindDestToTryRanges; 1093fe6060f1SDimitry Andric 1094fe6060f1SDimitry Andric // Gather possibly throwing calls (i.e., previously invokes) whose current 1095fe6060f1SDimitry Andric // unwind destination is not the same as the original CFG. (Case 1) 1096fe6060f1SDimitry Andric 1097fe6060f1SDimitry Andric for (auto &MBB : reverse(MF)) { 1098fe6060f1SDimitry Andric bool SeenThrowableInstInBB = false; 1099fe6060f1SDimitry Andric for (auto &MI : reverse(MBB)) { 1100fe6060f1SDimitry Andric if (MI.getOpcode() == WebAssembly::TRY) 1101fe6060f1SDimitry Andric EHPadStack.pop_back(); 1102fe6060f1SDimitry Andric else if (WebAssembly::isCatch(MI.getOpcode())) 1103fe6060f1SDimitry Andric EHPadStack.push_back(MI.getParent()); 1104fe6060f1SDimitry Andric 1105fe6060f1SDimitry Andric // In this loop we only gather calls that have an EH pad to unwind. So 1106fe6060f1SDimitry Andric // there will be at most 1 such call (= invoke) in a BB, so after we've 1107fe6060f1SDimitry Andric // seen one, we can skip the rest of BB. Also if MBB has no EH pad 1108fe6060f1SDimitry Andric // successor or MI does not throw, this is not an invoke. 1109fe6060f1SDimitry Andric if (SeenThrowableInstInBB || !MBB.hasEHPadSuccessor() || 1110fe6060f1SDimitry Andric !WebAssembly::mayThrow(MI)) 1111fe6060f1SDimitry Andric continue; 1112fe6060f1SDimitry Andric SeenThrowableInstInBB = true; 1113fe6060f1SDimitry Andric 1114fe6060f1SDimitry Andric // If the EH pad on the stack top is where this instruction should unwind 1115fe6060f1SDimitry Andric // next, we're good. 1116fe6060f1SDimitry Andric MachineBasicBlock *UnwindDest = getFakeCallerBlock(MF); 1117fe6060f1SDimitry Andric for (auto *Succ : MBB.successors()) { 1118fe6060f1SDimitry Andric // Even though semantically a BB can have multiple successors in case an 1119fe6060f1SDimitry Andric // exception is not caught by a catchpad, in our backend implementation 1120fe6060f1SDimitry Andric // it is guaranteed that a BB can have at most one EH pad successor. For 1121fe6060f1SDimitry Andric // details, refer to comments in findWasmUnwindDestinations function in 1122fe6060f1SDimitry Andric // SelectionDAGBuilder.cpp. 1123fe6060f1SDimitry Andric if (Succ->isEHPad()) { 1124fe6060f1SDimitry Andric UnwindDest = Succ; 1125fe6060f1SDimitry Andric break; 1126fe6060f1SDimitry Andric } 1127fe6060f1SDimitry Andric } 1128fe6060f1SDimitry Andric if (EHPadStack.back() == UnwindDest) 1129fe6060f1SDimitry Andric continue; 1130fe6060f1SDimitry Andric 1131fe6060f1SDimitry Andric // Include EH_LABELs in the range before and afer the invoke 1132fe6060f1SDimitry Andric MachineInstr *RangeBegin = &MI, *RangeEnd = &MI; 1133fe6060f1SDimitry Andric if (RangeBegin->getIterator() != MBB.begin() && 1134fe6060f1SDimitry Andric std::prev(RangeBegin->getIterator())->isEHLabel()) 1135fe6060f1SDimitry Andric RangeBegin = &*std::prev(RangeBegin->getIterator()); 1136fe6060f1SDimitry Andric if (std::next(RangeEnd->getIterator()) != MBB.end() && 1137fe6060f1SDimitry Andric std::next(RangeEnd->getIterator())->isEHLabel()) 1138fe6060f1SDimitry Andric RangeEnd = &*std::next(RangeEnd->getIterator()); 1139fe6060f1SDimitry Andric 1140fe6060f1SDimitry Andric // If not, record the range. 1141fe6060f1SDimitry Andric UnwindDestToTryRanges[UnwindDest].push_back( 1142fe6060f1SDimitry Andric TryRange(RangeBegin, RangeEnd)); 1143fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << "- Call unwind mismatch: MBB = " << MBB.getName() 1144fe6060f1SDimitry Andric << "\nCall = " << MI 1145fe6060f1SDimitry Andric << "\nOriginal dest = " << UnwindDest->getName() 1146fe6060f1SDimitry Andric << " Current dest = " << EHPadStack.back()->getName() 1147fe6060f1SDimitry Andric << "\n\n"); 1148fe6060f1SDimitry Andric } 1149fe6060f1SDimitry Andric } 1150fe6060f1SDimitry Andric 1151fe6060f1SDimitry Andric assert(EHPadStack.empty()); 1152fe6060f1SDimitry Andric 1153fe6060f1SDimitry Andric // Gather possibly throwing calls that are supposed to unwind up to the caller 1154fe6060f1SDimitry Andric // if they throw, but currently unwind to an incorrect destination. Unlike the 1155fe6060f1SDimitry Andric // loop above, there can be multiple calls within a BB that unwind to the 1156fe6060f1SDimitry Andric // caller, which we should group together in a range. (Case 2) 1157fe6060f1SDimitry Andric 1158fe6060f1SDimitry Andric MachineInstr *RangeBegin = nullptr, *RangeEnd = nullptr; // inclusive 1159fe6060f1SDimitry Andric 1160fe6060f1SDimitry Andric // Record the range. 1161fe6060f1SDimitry Andric auto RecordCallerMismatchRange = [&](const MachineBasicBlock *CurrentDest) { 1162fe6060f1SDimitry Andric UnwindDestToTryRanges[getFakeCallerBlock(MF)].push_back( 1163fe6060f1SDimitry Andric TryRange(RangeBegin, RangeEnd)); 1164fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << "- Call unwind mismatch: MBB = " 1165fe6060f1SDimitry Andric << RangeBegin->getParent()->getName() 1166fe6060f1SDimitry Andric << "\nRange begin = " << *RangeBegin 1167fe6060f1SDimitry Andric << "Range end = " << *RangeEnd 1168fe6060f1SDimitry Andric << "\nOriginal dest = caller Current dest = " 1169fe6060f1SDimitry Andric << CurrentDest->getName() << "\n\n"); 1170fe6060f1SDimitry Andric RangeBegin = RangeEnd = nullptr; // Reset range pointers 1171fe6060f1SDimitry Andric }; 1172fe6060f1SDimitry Andric 1173fe6060f1SDimitry Andric for (auto &MBB : reverse(MF)) { 1174fe6060f1SDimitry Andric bool SeenThrowableInstInBB = false; 1175fe6060f1SDimitry Andric for (auto &MI : reverse(MBB)) { 1176fe6060f1SDimitry Andric bool MayThrow = WebAssembly::mayThrow(MI); 1177fe6060f1SDimitry Andric 1178fe6060f1SDimitry Andric // If MBB has an EH pad successor and this is the last instruction that 1179fe6060f1SDimitry Andric // may throw, this instruction unwinds to the EH pad and not to the 1180fe6060f1SDimitry Andric // caller. 1181fe6060f1SDimitry Andric if (MBB.hasEHPadSuccessor() && MayThrow && !SeenThrowableInstInBB) 1182fe6060f1SDimitry Andric SeenThrowableInstInBB = true; 1183fe6060f1SDimitry Andric 1184fe6060f1SDimitry Andric // We wrap up the current range when we see a marker even if we haven't 1185fe6060f1SDimitry Andric // finished a BB. 1186fe6060f1SDimitry Andric else if (RangeEnd && WebAssembly::isMarker(MI.getOpcode())) 1187fe6060f1SDimitry Andric RecordCallerMismatchRange(EHPadStack.back()); 1188fe6060f1SDimitry Andric 1189fe6060f1SDimitry Andric // If EHPadStack is empty, that means it correctly unwinds to the caller 1190fe6060f1SDimitry Andric // if it throws, so we're good. If MI does not throw, we're good too. 1191fe6060f1SDimitry Andric else if (EHPadStack.empty() || !MayThrow) { 1192fe6060f1SDimitry Andric } 1193fe6060f1SDimitry Andric 1194fe6060f1SDimitry Andric // We found an instruction that unwinds to the caller but currently has an 1195fe6060f1SDimitry Andric // incorrect unwind destination. Create a new range or increment the 1196fe6060f1SDimitry Andric // currently existing range. 1197fe6060f1SDimitry Andric else { 1198fe6060f1SDimitry Andric if (!RangeEnd) 1199fe6060f1SDimitry Andric RangeBegin = RangeEnd = &MI; 1200fe6060f1SDimitry Andric else 1201fe6060f1SDimitry Andric RangeBegin = &MI; 1202fe6060f1SDimitry Andric } 1203fe6060f1SDimitry Andric 1204fe6060f1SDimitry Andric // Update EHPadStack. 1205fe6060f1SDimitry Andric if (MI.getOpcode() == WebAssembly::TRY) 1206fe6060f1SDimitry Andric EHPadStack.pop_back(); 1207fe6060f1SDimitry Andric else if (WebAssembly::isCatch(MI.getOpcode())) 1208fe6060f1SDimitry Andric EHPadStack.push_back(MI.getParent()); 1209fe6060f1SDimitry Andric } 1210fe6060f1SDimitry Andric 1211fe6060f1SDimitry Andric if (RangeEnd) 1212fe6060f1SDimitry Andric RecordCallerMismatchRange(EHPadStack.back()); 1213fe6060f1SDimitry Andric } 1214fe6060f1SDimitry Andric 1215fe6060f1SDimitry Andric assert(EHPadStack.empty()); 1216fe6060f1SDimitry Andric 1217fe6060f1SDimitry Andric // We don't have any unwind destination mismatches to resolve. 1218fe6060f1SDimitry Andric if (UnwindDestToTryRanges.empty()) 1219fe6060f1SDimitry Andric return false; 1220fe6060f1SDimitry Andric 1221fe6060f1SDimitry Andric // Now we fix the mismatches by wrapping calls with inner try-delegates. 1222fe6060f1SDimitry Andric for (auto &P : UnwindDestToTryRanges) { 1223fe6060f1SDimitry Andric NumCallUnwindMismatches += P.second.size(); 1224fe6060f1SDimitry Andric MachineBasicBlock *UnwindDest = P.first; 1225fe6060f1SDimitry Andric auto &TryRanges = P.second; 1226fe6060f1SDimitry Andric 1227fe6060f1SDimitry Andric for (auto Range : TryRanges) { 1228fe6060f1SDimitry Andric MachineInstr *RangeBegin = nullptr, *RangeEnd = nullptr; 1229fe6060f1SDimitry Andric std::tie(RangeBegin, RangeEnd) = Range; 1230fe6060f1SDimitry Andric auto *MBB = RangeBegin->getParent(); 1231fe6060f1SDimitry Andric 1232fe6060f1SDimitry Andric // If this BB has an EH pad successor, i.e., ends with an 'invoke', now we 1233fe6060f1SDimitry Andric // are going to wrap the invoke with try-delegate, making the 'delegate' 1234fe6060f1SDimitry Andric // BB the new successor instead, so remove the EH pad succesor here. The 1235fe6060f1SDimitry Andric // BB may not have an EH pad successor if calls in this BB throw to the 1236fe6060f1SDimitry Andric // caller. 1237fe6060f1SDimitry Andric MachineBasicBlock *EHPad = nullptr; 1238fe6060f1SDimitry Andric for (auto *Succ : MBB->successors()) { 1239fe6060f1SDimitry Andric if (Succ->isEHPad()) { 1240fe6060f1SDimitry Andric EHPad = Succ; 1241fe6060f1SDimitry Andric break; 1242fe6060f1SDimitry Andric } 1243fe6060f1SDimitry Andric } 1244fe6060f1SDimitry Andric if (EHPad) 1245fe6060f1SDimitry Andric MBB->removeSuccessor(EHPad); 1246fe6060f1SDimitry Andric 1247fe6060f1SDimitry Andric addTryDelegate(RangeBegin, RangeEnd, UnwindDest); 1248fe6060f1SDimitry Andric } 1249fe6060f1SDimitry Andric } 1250fe6060f1SDimitry Andric 1251fe6060f1SDimitry Andric return true; 1252fe6060f1SDimitry Andric } 1253fe6060f1SDimitry Andric 1254fe6060f1SDimitry Andric bool WebAssemblyCFGStackify::fixCatchUnwindMismatches(MachineFunction &MF) { 1255fe6060f1SDimitry Andric // There is another kind of unwind destination mismatches besides call unwind 1256fe6060f1SDimitry Andric // mismatches, which we will call "catch unwind mismatches". See this example 1257fe6060f1SDimitry Andric // after the marker placement: 1258fe6060f1SDimitry Andric // try 1259fe6060f1SDimitry Andric // try 1260fe6060f1SDimitry Andric // call @foo 1261fe6060f1SDimitry Andric // catch __cpp_exception ;; ehpad A (next unwind dest: caller) 1262fe6060f1SDimitry Andric // ... 1263fe6060f1SDimitry Andric // end_try 1264fe6060f1SDimitry Andric // catch_all ;; ehpad B 1265fe6060f1SDimitry Andric // ... 1266fe6060f1SDimitry Andric // end_try 1267fe6060f1SDimitry Andric // 1268fe6060f1SDimitry Andric // 'call @foo's unwind destination is the ehpad A. But suppose 'call @foo' 1269fe6060f1SDimitry Andric // throws a foreign exception that is not caught by ehpad A, and its next 1270fe6060f1SDimitry Andric // destination should be the caller. But after control flow linearization, 1271fe6060f1SDimitry Andric // another EH pad can be placed in between (e.g. ehpad B here), making the 1272fe6060f1SDimitry Andric // next unwind destination incorrect. In this case, the foreign exception 1273fe6060f1SDimitry Andric // will instead go to ehpad B and will be caught there instead. In this 1274fe6060f1SDimitry Andric // example the correct next unwind destination is the caller, but it can be 1275fe6060f1SDimitry Andric // another outer catch in other cases. 1276fe6060f1SDimitry Andric // 1277fe6060f1SDimitry Andric // There is no specific 'call' or 'throw' instruction to wrap with a 1278fe6060f1SDimitry Andric // try-delegate, so we wrap the whole try-catch-end with a try-delegate and 1279fe6060f1SDimitry Andric // make it rethrow to the right destination, as in the example below: 1280fe6060f1SDimitry Andric // try 1281fe6060f1SDimitry Andric // try ;; (new) 1282fe6060f1SDimitry Andric // try 1283fe6060f1SDimitry Andric // call @foo 1284fe6060f1SDimitry Andric // catch __cpp_exception ;; ehpad A (next unwind dest: caller) 1285fe6060f1SDimitry Andric // ... 1286fe6060f1SDimitry Andric // end_try 1287fe6060f1SDimitry Andric // delegate 1 (caller) ;; (new) 1288fe6060f1SDimitry Andric // catch_all ;; ehpad B 1289fe6060f1SDimitry Andric // ... 1290fe6060f1SDimitry Andric // end_try 1291fe6060f1SDimitry Andric 1292fe6060f1SDimitry Andric const auto *EHInfo = MF.getWasmEHFuncInfo(); 129306c3fb27SDimitry Andric assert(EHInfo); 1294fe6060f1SDimitry Andric SmallVector<const MachineBasicBlock *, 8> EHPadStack; 1295fe6060f1SDimitry Andric // For EH pads that have catch unwind mismatches, a map of <EH pad, its 1296fe6060f1SDimitry Andric // correct unwind destination>. 1297fe6060f1SDimitry Andric DenseMap<MachineBasicBlock *, MachineBasicBlock *> EHPadToUnwindDest; 1298fe6060f1SDimitry Andric 1299fe6060f1SDimitry Andric for (auto &MBB : reverse(MF)) { 1300fe6060f1SDimitry Andric for (auto &MI : reverse(MBB)) { 1301fe6060f1SDimitry Andric if (MI.getOpcode() == WebAssembly::TRY) 1302fe6060f1SDimitry Andric EHPadStack.pop_back(); 1303fe6060f1SDimitry Andric else if (MI.getOpcode() == WebAssembly::DELEGATE) 1304fe6060f1SDimitry Andric EHPadStack.push_back(&MBB); 1305fe6060f1SDimitry Andric else if (WebAssembly::isCatch(MI.getOpcode())) { 1306fe6060f1SDimitry Andric auto *EHPad = &MBB; 1307fe6060f1SDimitry Andric 1308fe6060f1SDimitry Andric // catch_all always catches an exception, so we don't need to do 1309fe6060f1SDimitry Andric // anything 1310fe6060f1SDimitry Andric if (MI.getOpcode() == WebAssembly::CATCH_ALL) { 1311fe6060f1SDimitry Andric } 1312fe6060f1SDimitry Andric 1313fe6060f1SDimitry Andric // This can happen when the unwind dest was removed during the 1314fe6060f1SDimitry Andric // optimization, e.g. because it was unreachable. 1315fe6060f1SDimitry Andric else if (EHPadStack.empty() && EHInfo->hasUnwindDest(EHPad)) { 1316fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << "EHPad (" << EHPad->getName() 1317fe6060f1SDimitry Andric << "'s unwind destination does not exist anymore" 1318fe6060f1SDimitry Andric << "\n\n"); 1319fe6060f1SDimitry Andric } 1320fe6060f1SDimitry Andric 1321fe6060f1SDimitry Andric // The EHPad's next unwind destination is the caller, but we incorrectly 1322fe6060f1SDimitry Andric // unwind to another EH pad. 1323fe6060f1SDimitry Andric else if (!EHPadStack.empty() && !EHInfo->hasUnwindDest(EHPad)) { 1324fe6060f1SDimitry Andric EHPadToUnwindDest[EHPad] = getFakeCallerBlock(MF); 1325fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() 1326fe6060f1SDimitry Andric << "- Catch unwind mismatch:\nEHPad = " << EHPad->getName() 1327fe6060f1SDimitry Andric << " Original dest = caller Current dest = " 1328fe6060f1SDimitry Andric << EHPadStack.back()->getName() << "\n\n"); 1329fe6060f1SDimitry Andric } 1330fe6060f1SDimitry Andric 1331fe6060f1SDimitry Andric // The EHPad's next unwind destination is an EH pad, whereas we 1332fe6060f1SDimitry Andric // incorrectly unwind to another EH pad. 1333fe6060f1SDimitry Andric else if (!EHPadStack.empty() && EHInfo->hasUnwindDest(EHPad)) { 1334fe6060f1SDimitry Andric auto *UnwindDest = EHInfo->getUnwindDest(EHPad); 1335fe6060f1SDimitry Andric if (EHPadStack.back() != UnwindDest) { 1336fe6060f1SDimitry Andric EHPadToUnwindDest[EHPad] = UnwindDest; 1337fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << "- Catch unwind mismatch:\nEHPad = " 1338fe6060f1SDimitry Andric << EHPad->getName() << " Original dest = " 1339fe6060f1SDimitry Andric << UnwindDest->getName() << " Current dest = " 1340fe6060f1SDimitry Andric << EHPadStack.back()->getName() << "\n\n"); 1341fe6060f1SDimitry Andric } 1342fe6060f1SDimitry Andric } 1343fe6060f1SDimitry Andric 1344fe6060f1SDimitry Andric EHPadStack.push_back(EHPad); 1345fe6060f1SDimitry Andric } 1346fe6060f1SDimitry Andric } 1347fe6060f1SDimitry Andric } 1348fe6060f1SDimitry Andric 1349fe6060f1SDimitry Andric assert(EHPadStack.empty()); 1350fe6060f1SDimitry Andric if (EHPadToUnwindDest.empty()) 1351fe6060f1SDimitry Andric return false; 1352fe6060f1SDimitry Andric NumCatchUnwindMismatches += EHPadToUnwindDest.size(); 1353fe6060f1SDimitry Andric SmallPtrSet<MachineBasicBlock *, 4> NewEndTryBBs; 1354fe6060f1SDimitry Andric 1355fe6060f1SDimitry Andric for (auto &P : EHPadToUnwindDest) { 1356fe6060f1SDimitry Andric MachineBasicBlock *EHPad = P.first; 1357fe6060f1SDimitry Andric MachineBasicBlock *UnwindDest = P.second; 1358fe6060f1SDimitry Andric MachineInstr *Try = EHPadToTry[EHPad]; 1359fe6060f1SDimitry Andric MachineInstr *EndTry = BeginToEnd[Try]; 1360fe6060f1SDimitry Andric addTryDelegate(Try, EndTry, UnwindDest); 1361fe6060f1SDimitry Andric NewEndTryBBs.insert(EndTry->getParent()); 1362fe6060f1SDimitry Andric } 1363fe6060f1SDimitry Andric 1364fe6060f1SDimitry Andric // Adding a try-delegate wrapping an existing try-catch-end can make existing 1365fe6060f1SDimitry Andric // branch destination BBs invalid. For example, 1366fe6060f1SDimitry Andric // 1367fe6060f1SDimitry Andric // - Before: 1368fe6060f1SDimitry Andric // bb0: 1369fe6060f1SDimitry Andric // block 1370fe6060f1SDimitry Andric // br bb3 1371fe6060f1SDimitry Andric // bb1: 1372fe6060f1SDimitry Andric // try 1373fe6060f1SDimitry Andric // ... 1374fe6060f1SDimitry Andric // bb2: (ehpad) 1375fe6060f1SDimitry Andric // catch 1376fe6060f1SDimitry Andric // bb3: 1377fe6060f1SDimitry Andric // end_try 1378fe6060f1SDimitry Andric // end_block ;; 'br bb3' targets here 1379fe6060f1SDimitry Andric // 1380fe6060f1SDimitry Andric // Suppose this try-catch-end has a catch unwind mismatch, so we need to wrap 1381fe6060f1SDimitry Andric // this with a try-delegate. Then this becomes: 1382fe6060f1SDimitry Andric // 1383fe6060f1SDimitry Andric // - After: 1384fe6060f1SDimitry Andric // bb0: 1385fe6060f1SDimitry Andric // block 1386fe6060f1SDimitry Andric // br bb3 ;; invalid destination! 1387fe6060f1SDimitry Andric // bb1: 1388fe6060f1SDimitry Andric // try ;; (new instruction) 1389fe6060f1SDimitry Andric // try 1390fe6060f1SDimitry Andric // ... 1391fe6060f1SDimitry Andric // bb2: (ehpad) 1392fe6060f1SDimitry Andric // catch 1393fe6060f1SDimitry Andric // bb3: 1394fe6060f1SDimitry Andric // end_try ;; 'br bb3' still incorrectly targets here! 1395fe6060f1SDimitry Andric // delegate_bb: ;; (new BB) 1396fe6060f1SDimitry Andric // delegate ;; (new instruction) 1397fe6060f1SDimitry Andric // split_bb: ;; (new BB) 1398fe6060f1SDimitry Andric // end_block 1399fe6060f1SDimitry Andric // 1400fe6060f1SDimitry Andric // Now 'br bb3' incorrectly branches to an inner scope. 1401fe6060f1SDimitry Andric // 1402fe6060f1SDimitry Andric // As we can see in this case, when branches target a BB that has both 1403fe6060f1SDimitry Andric // 'end_try' and 'end_block' and the BB is split to insert a 'delegate', we 1404fe6060f1SDimitry Andric // have to remap existing branch destinations so that they target not the 1405fe6060f1SDimitry Andric // 'end_try' BB but the new 'end_block' BB. There can be multiple 'delegate's 1406fe6060f1SDimitry Andric // in between, so we try to find the next BB with 'end_block' instruction. In 1407fe6060f1SDimitry Andric // this example, the 'br bb3' instruction should be remapped to 'br split_bb'. 1408fe6060f1SDimitry Andric for (auto &MBB : MF) { 1409fe6060f1SDimitry Andric for (auto &MI : MBB) { 1410fe6060f1SDimitry Andric if (MI.isTerminator()) { 1411fe6060f1SDimitry Andric for (auto &MO : MI.operands()) { 1412fe6060f1SDimitry Andric if (MO.isMBB() && NewEndTryBBs.count(MO.getMBB())) { 1413fe6060f1SDimitry Andric auto *BrDest = MO.getMBB(); 1414fe6060f1SDimitry Andric bool FoundEndBlock = false; 1415fe6060f1SDimitry Andric for (; std::next(BrDest->getIterator()) != MF.end(); 1416fe6060f1SDimitry Andric BrDest = BrDest->getNextNode()) { 1417fe6060f1SDimitry Andric for (const auto &MI : *BrDest) { 1418fe6060f1SDimitry Andric if (MI.getOpcode() == WebAssembly::END_BLOCK) { 1419fe6060f1SDimitry Andric FoundEndBlock = true; 1420fe6060f1SDimitry Andric break; 1421fe6060f1SDimitry Andric } 1422fe6060f1SDimitry Andric } 1423fe6060f1SDimitry Andric if (FoundEndBlock) 1424fe6060f1SDimitry Andric break; 1425fe6060f1SDimitry Andric } 1426fe6060f1SDimitry Andric assert(FoundEndBlock); 1427fe6060f1SDimitry Andric MO.setMBB(BrDest); 1428fe6060f1SDimitry Andric } 1429fe6060f1SDimitry Andric } 1430fe6060f1SDimitry Andric } 1431fe6060f1SDimitry Andric } 1432fe6060f1SDimitry Andric } 1433fe6060f1SDimitry Andric 1434fe6060f1SDimitry Andric return true; 1435fe6060f1SDimitry Andric } 1436fe6060f1SDimitry Andric 1437fe6060f1SDimitry Andric void WebAssemblyCFGStackify::recalculateScopeTops(MachineFunction &MF) { 1438fe6060f1SDimitry Andric // Renumber BBs and recalculate ScopeTop info because new BBs might have been 1439fe6060f1SDimitry Andric // created and inserted during fixing unwind mismatches. 1440fe6060f1SDimitry Andric MF.RenumberBlocks(); 1441fe6060f1SDimitry Andric ScopeTops.clear(); 1442fe6060f1SDimitry Andric ScopeTops.resize(MF.getNumBlockIDs()); 1443fe6060f1SDimitry Andric for (auto &MBB : reverse(MF)) { 1444fe6060f1SDimitry Andric for (auto &MI : reverse(MBB)) { 1445fe6060f1SDimitry Andric if (ScopeTops[MBB.getNumber()]) 1446fe6060f1SDimitry Andric break; 1447fe6060f1SDimitry Andric switch (MI.getOpcode()) { 1448fe6060f1SDimitry Andric case WebAssembly::END_BLOCK: 1449fe6060f1SDimitry Andric case WebAssembly::END_LOOP: 1450fe6060f1SDimitry Andric case WebAssembly::END_TRY: 1451fe6060f1SDimitry Andric case WebAssembly::DELEGATE: 1452fe6060f1SDimitry Andric updateScopeTops(EndToBegin[&MI]->getParent(), &MBB); 1453fe6060f1SDimitry Andric break; 1454fe6060f1SDimitry Andric case WebAssembly::CATCH: 1455fe6060f1SDimitry Andric case WebAssembly::CATCH_ALL: 1456fe6060f1SDimitry Andric updateScopeTops(EHPadToTry[&MBB]->getParent(), &MBB); 1457fe6060f1SDimitry Andric break; 1458fe6060f1SDimitry Andric } 1459fe6060f1SDimitry Andric } 1460fe6060f1SDimitry Andric } 14610b57cec5SDimitry Andric } 14620b57cec5SDimitry Andric 14630b57cec5SDimitry Andric /// In normal assembly languages, when the end of a function is unreachable, 14640b57cec5SDimitry Andric /// because the function ends in an infinite loop or a noreturn call or similar, 14650b57cec5SDimitry Andric /// it isn't necessary to worry about the function return type at the end of 14660b57cec5SDimitry Andric /// the function, because it's never reached. However, in WebAssembly, blocks 14670b57cec5SDimitry Andric /// that end at the function end need to have a return type signature that 14680b57cec5SDimitry Andric /// matches the function signature, even though it's unreachable. This function 14690b57cec5SDimitry Andric /// checks for such cases and fixes up the signatures. 14700b57cec5SDimitry Andric void WebAssemblyCFGStackify::fixEndsAtEndOfFunction(MachineFunction &MF) { 14710b57cec5SDimitry Andric const auto &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); 14720b57cec5SDimitry Andric 14730b57cec5SDimitry Andric if (MFI.getResults().empty()) 14740b57cec5SDimitry Andric return; 14750b57cec5SDimitry Andric 14768bcb0991SDimitry Andric // MCInstLower will add the proper types to multivalue signatures based on the 14778bcb0991SDimitry Andric // function return type 14788bcb0991SDimitry Andric WebAssembly::BlockType RetType = 14798bcb0991SDimitry Andric MFI.getResults().size() > 1 14808bcb0991SDimitry Andric ? WebAssembly::BlockType::Multivalue 14818bcb0991SDimitry Andric : WebAssembly::BlockType( 14828bcb0991SDimitry Andric WebAssembly::toValType(MFI.getResults().front())); 14830b57cec5SDimitry Andric 1484e8d8bef9SDimitry Andric SmallVector<MachineBasicBlock::reverse_iterator, 4> Worklist; 1485e8d8bef9SDimitry Andric Worklist.push_back(MF.rbegin()->rbegin()); 1486e8d8bef9SDimitry Andric 1487e8d8bef9SDimitry Andric auto Process = [&](MachineBasicBlock::reverse_iterator It) { 1488e8d8bef9SDimitry Andric auto *MBB = It->getParent(); 1489e8d8bef9SDimitry Andric while (It != MBB->rend()) { 1490e8d8bef9SDimitry Andric MachineInstr &MI = *It++; 14910b57cec5SDimitry Andric if (MI.isPosition() || MI.isDebugInstr()) 14920b57cec5SDimitry Andric continue; 14938bcb0991SDimitry Andric switch (MI.getOpcode()) { 1494e8d8bef9SDimitry Andric case WebAssembly::END_TRY: { 1495e8d8bef9SDimitry Andric // If a 'try''s return type is fixed, both its try body and catch body 1496e8d8bef9SDimitry Andric // should satisfy the return type, so we need to search 'end' 1497e8d8bef9SDimitry Andric // instructions before its corresponding 'catch' too. 1498e8d8bef9SDimitry Andric auto *EHPad = TryToEHPad.lookup(EndToBegin[&MI]); 1499e8d8bef9SDimitry Andric assert(EHPad); 1500e8d8bef9SDimitry Andric auto NextIt = 1501e8d8bef9SDimitry Andric std::next(WebAssembly::findCatch(EHPad)->getReverseIterator()); 1502e8d8bef9SDimitry Andric if (NextIt != EHPad->rend()) 1503e8d8bef9SDimitry Andric Worklist.push_back(NextIt); 1504bdd1243dSDimitry Andric [[fallthrough]]; 1505e8d8bef9SDimitry Andric } 15068bcb0991SDimitry Andric case WebAssembly::END_BLOCK: 15078bcb0991SDimitry Andric case WebAssembly::END_LOOP: 1508fe6060f1SDimitry Andric case WebAssembly::DELEGATE: 15090b57cec5SDimitry Andric EndToBegin[&MI]->getOperand(0).setImm(int32_t(RetType)); 15100b57cec5SDimitry Andric continue; 15118bcb0991SDimitry Andric default: 1512e8d8bef9SDimitry Andric // Something other than an `end`. We're done for this BB. 15130b57cec5SDimitry Andric return; 15140b57cec5SDimitry Andric } 15150b57cec5SDimitry Andric } 1516e8d8bef9SDimitry Andric // We've reached the beginning of a BB. Continue the search in the previous 1517e8d8bef9SDimitry Andric // BB. 1518e8d8bef9SDimitry Andric Worklist.push_back(MBB->getPrevNode()->rbegin()); 1519e8d8bef9SDimitry Andric }; 1520e8d8bef9SDimitry Andric 1521e8d8bef9SDimitry Andric while (!Worklist.empty()) 1522e8d8bef9SDimitry Andric Process(Worklist.pop_back_val()); 15238bcb0991SDimitry Andric } 15240b57cec5SDimitry Andric 15250b57cec5SDimitry Andric // WebAssembly functions end with an end instruction, as if the function body 15260b57cec5SDimitry Andric // were a block. 15270b57cec5SDimitry Andric static void appendEndToFunction(MachineFunction &MF, 15280b57cec5SDimitry Andric const WebAssemblyInstrInfo &TII) { 15290b57cec5SDimitry Andric BuildMI(MF.back(), MF.back().end(), 15300b57cec5SDimitry Andric MF.back().findPrevDebugLoc(MF.back().end()), 15310b57cec5SDimitry Andric TII.get(WebAssembly::END_FUNCTION)); 15320b57cec5SDimitry Andric } 15330b57cec5SDimitry Andric 15340b57cec5SDimitry Andric /// Insert LOOP/TRY/BLOCK markers at appropriate places. 15350b57cec5SDimitry Andric void WebAssemblyCFGStackify::placeMarkers(MachineFunction &MF) { 15360b57cec5SDimitry Andric // We allocate one more than the number of blocks in the function to 15370b57cec5SDimitry Andric // accommodate for the possible fake block we may insert at the end. 15380b57cec5SDimitry Andric ScopeTops.resize(MF.getNumBlockIDs() + 1); 15390b57cec5SDimitry Andric // Place the LOOP for MBB if MBB is the header of a loop. 15400b57cec5SDimitry Andric for (auto &MBB : MF) 15410b57cec5SDimitry Andric placeLoopMarker(MBB); 15420b57cec5SDimitry Andric 15430b57cec5SDimitry Andric const MCAsmInfo *MCAI = MF.getTarget().getMCAsmInfo(); 15440b57cec5SDimitry Andric for (auto &MBB : MF) { 15450b57cec5SDimitry Andric if (MBB.isEHPad()) { 15460b57cec5SDimitry Andric // Place the TRY for MBB if MBB is the EH pad of an exception. 15470b57cec5SDimitry Andric if (MCAI->getExceptionHandlingType() == ExceptionHandling::Wasm && 15480b57cec5SDimitry Andric MF.getFunction().hasPersonalityFn()) 15490b57cec5SDimitry Andric placeTryMarker(MBB); 15500b57cec5SDimitry Andric } else { 15510b57cec5SDimitry Andric // Place the BLOCK for MBB if MBB is branched to from above. 15520b57cec5SDimitry Andric placeBlockMarker(MBB); 15530b57cec5SDimitry Andric } 15540b57cec5SDimitry Andric } 15550b57cec5SDimitry Andric // Fix mismatches in unwind destinations induced by linearizing the code. 15568bcb0991SDimitry Andric if (MCAI->getExceptionHandlingType() == ExceptionHandling::Wasm && 1557fe6060f1SDimitry Andric MF.getFunction().hasPersonalityFn()) { 1558fe6060f1SDimitry Andric bool Changed = fixCallUnwindMismatches(MF); 1559fe6060f1SDimitry Andric Changed |= fixCatchUnwindMismatches(MF); 1560fe6060f1SDimitry Andric if (Changed) 1561fe6060f1SDimitry Andric recalculateScopeTops(MF); 1562fe6060f1SDimitry Andric } 1563fe6060f1SDimitry Andric } 1564fe6060f1SDimitry Andric 1565fe6060f1SDimitry Andric unsigned WebAssemblyCFGStackify::getBranchDepth( 1566fe6060f1SDimitry Andric const SmallVectorImpl<EndMarkerInfo> &Stack, const MachineBasicBlock *MBB) { 1567fe6060f1SDimitry Andric unsigned Depth = 0; 1568fe6060f1SDimitry Andric for (auto X : reverse(Stack)) { 1569fe6060f1SDimitry Andric if (X.first == MBB) 1570fe6060f1SDimitry Andric break; 1571fe6060f1SDimitry Andric ++Depth; 1572fe6060f1SDimitry Andric } 1573fe6060f1SDimitry Andric assert(Depth < Stack.size() && "Branch destination should be in scope"); 1574fe6060f1SDimitry Andric return Depth; 1575fe6060f1SDimitry Andric } 1576fe6060f1SDimitry Andric 1577fe6060f1SDimitry Andric unsigned WebAssemblyCFGStackify::getDelegateDepth( 1578fe6060f1SDimitry Andric const SmallVectorImpl<EndMarkerInfo> &Stack, const MachineBasicBlock *MBB) { 1579fe6060f1SDimitry Andric if (MBB == FakeCallerBB) 1580fe6060f1SDimitry Andric return Stack.size(); 1581fe6060f1SDimitry Andric // Delegate's destination is either a catch or a another delegate BB. When the 1582fe6060f1SDimitry Andric // destination is another delegate, we can compute the argument in the same 1583fe6060f1SDimitry Andric // way as branches, because the target delegate BB only contains the single 1584fe6060f1SDimitry Andric // delegate instruction. 1585fe6060f1SDimitry Andric if (!MBB->isEHPad()) // Target is a delegate BB 1586fe6060f1SDimitry Andric return getBranchDepth(Stack, MBB); 1587fe6060f1SDimitry Andric 1588fe6060f1SDimitry Andric // When the delegate's destination is a catch BB, we need to use its 1589fe6060f1SDimitry Andric // corresponding try's end_try BB because Stack contains each marker's end BB. 1590fe6060f1SDimitry Andric // Also we need to check if the end marker instruction matches, because a 1591fe6060f1SDimitry Andric // single BB can contain multiple end markers, like this: 1592fe6060f1SDimitry Andric // bb: 1593fe6060f1SDimitry Andric // END_BLOCK 1594fe6060f1SDimitry Andric // END_TRY 1595fe6060f1SDimitry Andric // END_BLOCK 1596fe6060f1SDimitry Andric // END_TRY 1597fe6060f1SDimitry Andric // ... 1598fe6060f1SDimitry Andric // 1599fe6060f1SDimitry Andric // In case of branches getting the immediate that targets any of these is 1600fe6060f1SDimitry Andric // fine, but delegate has to exactly target the correct try. 1601fe6060f1SDimitry Andric unsigned Depth = 0; 1602fe6060f1SDimitry Andric const MachineInstr *EndTry = BeginToEnd[EHPadToTry[MBB]]; 1603fe6060f1SDimitry Andric for (auto X : reverse(Stack)) { 1604fe6060f1SDimitry Andric if (X.first == EndTry->getParent() && X.second == EndTry) 1605fe6060f1SDimitry Andric break; 1606fe6060f1SDimitry Andric ++Depth; 1607fe6060f1SDimitry Andric } 1608fe6060f1SDimitry Andric assert(Depth < Stack.size() && "Delegate destination should be in scope"); 1609fe6060f1SDimitry Andric return Depth; 1610fe6060f1SDimitry Andric } 1611fe6060f1SDimitry Andric 1612fe6060f1SDimitry Andric unsigned WebAssemblyCFGStackify::getRethrowDepth( 1613fe6060f1SDimitry Andric const SmallVectorImpl<EndMarkerInfo> &Stack, 1614*415efcecSDimitry Andric const MachineBasicBlock *EHPadToRethrow) { 1615fe6060f1SDimitry Andric unsigned Depth = 0; 1616fe6060f1SDimitry Andric for (auto X : reverse(Stack)) { 1617fe6060f1SDimitry Andric const MachineInstr *End = X.second; 1618fe6060f1SDimitry Andric if (End->getOpcode() == WebAssembly::END_TRY) { 1619fe6060f1SDimitry Andric auto *EHPad = TryToEHPad[EndToBegin[End]]; 1620*415efcecSDimitry Andric if (EHPadToRethrow == EHPad) 1621fe6060f1SDimitry Andric break; 1622fe6060f1SDimitry Andric } 1623fe6060f1SDimitry Andric ++Depth; 1624fe6060f1SDimitry Andric } 1625fe6060f1SDimitry Andric assert(Depth < Stack.size() && "Rethrow destination should be in scope"); 1626fe6060f1SDimitry Andric return Depth; 16270b57cec5SDimitry Andric } 16280b57cec5SDimitry Andric 16290b57cec5SDimitry Andric void WebAssemblyCFGStackify::rewriteDepthImmediates(MachineFunction &MF) { 16300b57cec5SDimitry Andric // Now rewrite references to basic blocks to be depth immediates. 1631fe6060f1SDimitry Andric SmallVector<EndMarkerInfo, 8> Stack; 16320b57cec5SDimitry Andric for (auto &MBB : reverse(MF)) { 1633349cc55cSDimitry Andric for (MachineInstr &MI : llvm::reverse(MBB)) { 16340b57cec5SDimitry Andric switch (MI.getOpcode()) { 16350b57cec5SDimitry Andric case WebAssembly::BLOCK: 16360b57cec5SDimitry Andric case WebAssembly::TRY: 1637fe6060f1SDimitry Andric assert(ScopeTops[Stack.back().first->getNumber()]->getNumber() <= 16380b57cec5SDimitry Andric MBB.getNumber() && 16390b57cec5SDimitry Andric "Block/try marker should be balanced"); 16400b57cec5SDimitry Andric Stack.pop_back(); 16410b57cec5SDimitry Andric break; 16420b57cec5SDimitry Andric 16430b57cec5SDimitry Andric case WebAssembly::LOOP: 1644fe6060f1SDimitry Andric assert(Stack.back().first == &MBB && "Loop top should be balanced"); 16450b57cec5SDimitry Andric Stack.pop_back(); 16460b57cec5SDimitry Andric break; 16470b57cec5SDimitry Andric 16480b57cec5SDimitry Andric case WebAssembly::END_BLOCK: 1649*415efcecSDimitry Andric case WebAssembly::END_TRY: 1650fe6060f1SDimitry Andric Stack.push_back(std::make_pair(&MBB, &MI)); 16510b57cec5SDimitry Andric break; 16520b57cec5SDimitry Andric 16530b57cec5SDimitry Andric case WebAssembly::END_LOOP: 1654fe6060f1SDimitry Andric Stack.push_back(std::make_pair(EndToBegin[&MI]->getParent(), &MI)); 1655fe6060f1SDimitry Andric break; 1656fe6060f1SDimitry Andric 16570b57cec5SDimitry Andric default: 16580b57cec5SDimitry Andric if (MI.isTerminator()) { 16590b57cec5SDimitry Andric // Rewrite MBB operands to be depth immediates. 16600b57cec5SDimitry Andric SmallVector<MachineOperand, 4> Ops(MI.operands()); 16610b57cec5SDimitry Andric while (MI.getNumOperands() > 0) 166281ad6265SDimitry Andric MI.removeOperand(MI.getNumOperands() - 1); 16630b57cec5SDimitry Andric for (auto MO : Ops) { 1664fe6060f1SDimitry Andric if (MO.isMBB()) { 1665fe6060f1SDimitry Andric if (MI.getOpcode() == WebAssembly::DELEGATE) 1666fe6060f1SDimitry Andric MO = MachineOperand::CreateImm( 1667fe6060f1SDimitry Andric getDelegateDepth(Stack, MO.getMBB())); 1668*415efcecSDimitry Andric else if (MI.getOpcode() == WebAssembly::RETHROW) 1669*415efcecSDimitry Andric MO = MachineOperand::CreateImm( 1670*415efcecSDimitry Andric getRethrowDepth(Stack, MO.getMBB())); 1671fe6060f1SDimitry Andric else 1672fe6060f1SDimitry Andric MO = MachineOperand::CreateImm( 1673fe6060f1SDimitry Andric getBranchDepth(Stack, MO.getMBB())); 1674fe6060f1SDimitry Andric } 16750b57cec5SDimitry Andric MI.addOperand(MF, MO); 16760b57cec5SDimitry Andric } 16770b57cec5SDimitry Andric } 1678fe6060f1SDimitry Andric 1679fe6060f1SDimitry Andric if (MI.getOpcode() == WebAssembly::DELEGATE) 1680fe6060f1SDimitry Andric Stack.push_back(std::make_pair(&MBB, &MI)); 16810b57cec5SDimitry Andric break; 16820b57cec5SDimitry Andric } 16830b57cec5SDimitry Andric } 16840b57cec5SDimitry Andric } 16850b57cec5SDimitry Andric assert(Stack.empty() && "Control flow should be balanced"); 16860b57cec5SDimitry Andric } 16870b57cec5SDimitry Andric 1688fe6060f1SDimitry Andric void WebAssemblyCFGStackify::cleanupFunctionData(MachineFunction &MF) { 1689fe6060f1SDimitry Andric if (FakeCallerBB) 16900eae32dcSDimitry Andric MF.deleteMachineBasicBlock(FakeCallerBB); 1691fe6060f1SDimitry Andric AppendixBB = FakeCallerBB = nullptr; 1692fe6060f1SDimitry Andric } 1693fe6060f1SDimitry Andric 16940b57cec5SDimitry Andric void WebAssemblyCFGStackify::releaseMemory() { 16950b57cec5SDimitry Andric ScopeTops.clear(); 16960b57cec5SDimitry Andric BeginToEnd.clear(); 16970b57cec5SDimitry Andric EndToBegin.clear(); 16980b57cec5SDimitry Andric TryToEHPad.clear(); 16990b57cec5SDimitry Andric EHPadToTry.clear(); 17000b57cec5SDimitry Andric } 17010b57cec5SDimitry Andric 17020b57cec5SDimitry Andric bool WebAssemblyCFGStackify::runOnMachineFunction(MachineFunction &MF) { 17030b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "********** CFG Stackifying **********\n" 17040b57cec5SDimitry Andric "********** Function: " 17050b57cec5SDimitry Andric << MF.getName() << '\n'); 17060b57cec5SDimitry Andric const MCAsmInfo *MCAI = MF.getTarget().getMCAsmInfo(); 17070b57cec5SDimitry Andric 17080b57cec5SDimitry Andric releaseMemory(); 17090b57cec5SDimitry Andric 17100b57cec5SDimitry Andric // Liveness is not tracked for VALUE_STACK physreg. 17110b57cec5SDimitry Andric MF.getRegInfo().invalidateLiveness(); 17120b57cec5SDimitry Andric 17130b57cec5SDimitry Andric // Place the BLOCK/LOOP/TRY markers to indicate the beginnings of scopes. 17140b57cec5SDimitry Andric placeMarkers(MF); 17150b57cec5SDimitry Andric 17160b57cec5SDimitry Andric // Remove unnecessary instructions possibly introduced by try/end_trys. 17170b57cec5SDimitry Andric if (MCAI->getExceptionHandlingType() == ExceptionHandling::Wasm && 17180b57cec5SDimitry Andric MF.getFunction().hasPersonalityFn()) 17190b57cec5SDimitry Andric removeUnnecessaryInstrs(MF); 17200b57cec5SDimitry Andric 17210b57cec5SDimitry Andric // Convert MBB operands in terminators to relative depth immediates. 17220b57cec5SDimitry Andric rewriteDepthImmediates(MF); 17230b57cec5SDimitry Andric 17240b57cec5SDimitry Andric // Fix up block/loop/try signatures at the end of the function to conform to 17250b57cec5SDimitry Andric // WebAssembly's rules. 17260b57cec5SDimitry Andric fixEndsAtEndOfFunction(MF); 17270b57cec5SDimitry Andric 17280b57cec5SDimitry Andric // Add an end instruction at the end of the function body. 17290b57cec5SDimitry Andric const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 17300b57cec5SDimitry Andric if (!MF.getSubtarget<WebAssemblySubtarget>() 17310b57cec5SDimitry Andric .getTargetTriple() 17320b57cec5SDimitry Andric .isOSBinFormatELF()) 17330b57cec5SDimitry Andric appendEndToFunction(MF, TII); 17340b57cec5SDimitry Andric 1735fe6060f1SDimitry Andric cleanupFunctionData(MF); 1736fe6060f1SDimitry Andric 17370b57cec5SDimitry Andric MF.getInfo<WebAssemblyFunctionInfo>()->setCFGStackified(); 17380b57cec5SDimitry Andric return true; 17390b57cec5SDimitry Andric } 1740