//===-- WebAssemblyCFGStackify.cpp - CFG Stackification -------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief This file implements a CFG stacking pass. /// /// This pass reorders the blocks in a function to put them into a reverse /// post-order [0], with special care to keep the order as similar as possible /// to the original order, and to keep loops contiguous even in the case of /// split backedges. /// /// Then, it inserts BLOCK and LOOP markers to mark the start of scopes, since /// scope boundaries serve as the labels for WebAssembly's control transfers. /// /// This is sufficient to convert arbitrary CFGs into a form that works on /// WebAssembly, provided that all loops are single-entry. /// /// [0] https://en.wikipedia.org/wiki/Depth-first_search#Vertex_orderings /// //===----------------------------------------------------------------------===// #include "WebAssembly.h" #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" #include "WebAssemblySubtarget.h" #include "llvm/ADT/SCCIterator.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineLoopInfo.h" #include "llvm/CodeGen/Passes.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; #define DEBUG_TYPE "wasm-cfg-stackify" namespace { class WebAssemblyCFGStackify final : public MachineFunctionPass { const char *getPassName() const override { return "WebAssembly CFG Stackify"; } void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesCFG(); AU.addRequired(); AU.addPreserved(); MachineFunctionPass::getAnalysisUsage(AU); } bool runOnMachineFunction(MachineFunction &MF) override; public: static char ID; // Pass identification, replacement for typeid WebAssemblyCFGStackify() : MachineFunctionPass(ID) {} }; } // end anonymous namespace char WebAssemblyCFGStackify::ID = 0; FunctionPass *llvm::createWebAssemblyCFGStackify() { return new WebAssemblyCFGStackify(); } static void EliminateMultipleEntryLoops(MachineFunction &MF, const MachineLoopInfo &MLI) { SmallPtrSet InSet; for (scc_iterator I = scc_begin(&MF), E = scc_end(&MF); I != E; ++I) { const std::vector &CurrentSCC = *I; // Skip trivial SCCs. if (CurrentSCC.size() == 1) continue; InSet.insert(CurrentSCC.begin(), CurrentSCC.end()); MachineBasicBlock *Header = nullptr; for (MachineBasicBlock *MBB : CurrentSCC) { for (MachineBasicBlock *Pred : MBB->predecessors()) { if (InSet.count(Pred)) continue; if (!Header) { Header = MBB; break; } // TODO: Implement multiple-entry loops. report_fatal_error("multiple-entry loops are not supported yet"); } } assert(MLI.isLoopHeader(Header)); InSet.clear(); } } namespace { /// Post-order traversal stack entry. struct POStackEntry { MachineBasicBlock *MBB; SmallVector Succs; POStackEntry(MachineBasicBlock *MBB, MachineFunction &MF, const MachineLoopInfo &MLI); }; } // end anonymous namespace POStackEntry::POStackEntry(MachineBasicBlock *MBB, MachineFunction &MF, const MachineLoopInfo &MLI) : MBB(MBB), Succs(MBB->successors()) { // RPO is not a unique form, since at every basic block with multiple // successors, the DFS has to pick which order to visit the successors in. // Sort them strategically (see below). MachineLoop *Loop = MLI.getLoopFor(MBB); MachineFunction::iterator Next = next(MachineFunction::iterator(MBB)); MachineBasicBlock *LayoutSucc = Next == MF.end() ? nullptr : &*Next; std::stable_sort( Succs.begin(), Succs.end(), [=, &MLI](const MachineBasicBlock *A, const MachineBasicBlock *B) { if (A == B) return false; // Keep loops contiguous by preferring the block that's in the same // loop. MachineLoop *LoopA = MLI.getLoopFor(A); MachineLoop *LoopB = MLI.getLoopFor(B); if (LoopA == Loop && LoopB != Loop) return true; if (LoopA != Loop && LoopB == Loop) return false; // Minimize perturbation by preferring the block which is the immediate // layout successor. if (A == LayoutSucc) return true; if (B == LayoutSucc) return false; // TODO: More sophisticated orderings may be profitable here. return false; }); } /// Sort the blocks in RPO, taking special care to make sure that loops are /// contiguous even in the case of split backedges. static void SortBlocks(MachineFunction &MF, const MachineLoopInfo &MLI) { // Note that we do our own RPO rather than using // "llvm/ADT/PostOrderIterator.h" because we want control over the order that // successors are visited in (see above). Also, we can sort the blocks in the // MachineFunction as we go. SmallPtrSet Visited; SmallVector Stack; MachineBasicBlock *Entry = MF.begin(); Visited.insert(Entry); Stack.push_back(POStackEntry(Entry, MF, MLI)); for (;;) { POStackEntry &Entry = Stack.back(); SmallVectorImpl &Succs = Entry.Succs; if (!Succs.empty()) { MachineBasicBlock *Succ = Succs.pop_back_val(); if (Visited.insert(Succ).second) Stack.push_back(POStackEntry(Succ, MF, MLI)); continue; } // Put the block in its position in the MachineFunction. MachineBasicBlock &MBB = *Entry.MBB; MBB.moveBefore(MF.begin()); // Branch instructions may utilize a fallthrough, so update them if a // fallthrough has been added or removed. if (!MBB.empty() && MBB.back().isTerminator() && !MBB.back().isBranch() && !MBB.back().isBarrier()) report_fatal_error( "Non-branch terminator with fallthrough cannot yet be rewritten"); if (MBB.empty() || !MBB.back().isTerminator() || MBB.back().isBranch()) MBB.updateTerminator(); Stack.pop_back(); if (Stack.empty()) break; } // Now that we've sorted the blocks in RPO, renumber them. MF.RenumberBlocks(); #ifndef NDEBUG for (auto &MBB : MF) if (MachineLoop *Loop = MLI.getLoopFor(&MBB)) { // Assert that loops are contiguous. assert(Loop->getHeader() == Loop->getTopBlock()); assert((Loop->getHeader() == &MBB || Loop->contains( MLI.getLoopFor(prev(MachineFunction::iterator(&MBB))))) && "Loop isn't contiguous"); } else { // Assert that non-loops have no backedge predecessors. for (auto Pred : MBB.predecessors()) assert(Pred->getNumber() < MBB.getNumber() && "CFG still has multiple-entry loops"); } #endif } /// Insert BLOCK markers at appropriate places. static void PlaceBlockMarkers(MachineBasicBlock &MBB, MachineBasicBlock &Succ, MachineFunction &MF, const MachineLoopInfo &MLI, const WebAssemblyInstrInfo &TII) { // Backward branches are loop backedges, and we place the LOOP markers // separately. So only consider forward branches here. if (Succ.getNumber() <= MBB.getNumber()) return; // Place the BLOCK for a forward branch. For simplicity, we just insert // blocks immediately inside loop boundaries. MachineLoop *Loop = MLI.getLoopFor(&Succ); MachineBasicBlock &Header = *(Loop ? Loop->getHeader() : &MF.front()); MachineBasicBlock::iterator InsertPos = Header.begin(), End = Header.end(); if (InsertPos != End) { if (InsertPos->getOpcode() == WebAssembly::LOOP) ++InsertPos; int SuccNumber = Succ.getNumber(); // Position the BLOCK in nesting order. for (; InsertPos != End && InsertPos->getOpcode() == WebAssembly::BLOCK; ++InsertPos) { int N = InsertPos->getOperand(0).getMBB()->getNumber(); if (N < SuccNumber) break; // If there's already a BLOCK for Succ, we don't need another. if (N == SuccNumber) return; } } BuildMI(Header, InsertPos, DebugLoc(), TII.get(WebAssembly::BLOCK)) .addMBB(&Succ); } /// Insert LOOP and BLOCK markers at appropriate places. static void PlaceMarkers(MachineFunction &MF, const MachineLoopInfo &MLI, const WebAssemblyInstrInfo &TII) { for (auto &MBB : MF) { // Place the LOOP for loops. if (MachineLoop *Loop = MLI.getLoopFor(&MBB)) if (Loop->getHeader() == &MBB) { // The operand of a LOOP is the first block after the loop. If the loop // is the bottom of the function, insert a dummy block at the end. MachineBasicBlock *Bottom = Loop->getBottomBlock(); auto Iter = next(MachineFunction::iterator(Bottom)); if (Iter == MF.end()) { MF.push_back(MF.CreateMachineBasicBlock()); Iter = next(MachineFunction::iterator(Bottom)); } BuildMI(MBB, MBB.begin(), DebugLoc(), TII.get(WebAssembly::LOOP)) .addMBB(Iter); } // Check for forward branches and switches that need BLOCKS placed. for (auto &Term : MBB.terminators()) for (auto &MO : Term.operands()) if (MO.isMBB()) PlaceBlockMarkers(MBB, *MO.getMBB(), MF, MLI, TII); } } bool WebAssemblyCFGStackify::runOnMachineFunction(MachineFunction &MF) { DEBUG(dbgs() << "********** CFG Stackifying **********\n" "********** Function: " << MF.getName() << '\n'); const auto &MLI = getAnalysis(); const auto &TII = *MF.getSubtarget().getInstrInfo(); // RPO sorting needs all loops to be single-entry. EliminateMultipleEntryLoops(MF, MLI); // Sort the blocks in RPO, with contiguous loops. SortBlocks(MF, MLI); // Place the BLOCK and LOOP markers to indicate the beginnings of scopes. PlaceMarkers(MF, MLI, TII); return true; }