| //=- WebAssemblyFixIrreducibleControlFlow.cpp - Fix irreducible control flow -// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// | |
| /// \file | |
| /// This file implements a pass that transforms irreducible control flow | |
| /// into reducible control flow. Irreducible control flow means multiple-entry | |
| /// loops; they appear as CFG cycles that are not recorded in MachineLoopInfo | |
| /// due to being unnatural. | |
| /// | |
| /// Note that LLVM has a generic pass that lowers irreducible control flow, but | |
| /// it linearizes control flow, turning diamonds into two triangles, which is | |
| /// both unnecessary and undesirable for WebAssembly. | |
| /// | |
| /// TODO: The transformation implemented here handles all irreducible control | |
| /// flow, without exponential code-size expansion, though it does so by creating | |
| /// inefficient code in many cases. Ideally, we should add other | |
| /// transformations, including code-duplicating cases, which can be more | |
| /// efficient in common cases, and they can fall back to this conservative | |
| /// implementation as needed. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" | |
| #include "WebAssembly.h" | |
| #include "WebAssemblyMachineFunctionInfo.h" | |
| #include "WebAssemblySubtarget.h" | |
| #include "llvm/ADT/PriorityQueue.h" | |
| #include "llvm/ADT/SCCIterator.h" | |
| #include "llvm/ADT/SetVector.h" | |
| #include "llvm/CodeGen/MachineDominators.h" | |
| #include "llvm/CodeGen/MachineFunction.h" | |
| #include "llvm/CodeGen/MachineInstrBuilder.h" | |
| #include "llvm/CodeGen/MachineLoopInfo.h" | |
| #include "llvm/CodeGen/MachineRegisterInfo.h" | |
| #include "llvm/CodeGen/Passes.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| using namespace llvm; | |
| #define DEBUG_TYPE "wasm-fix-irreducible-control-flow" | |
| namespace { | |
| class WebAssemblyFixIrreducibleControlFlow final : public MachineFunctionPass { | |
| StringRef getPassName() const override { | |
| return "WebAssembly Fix Irreducible Control Flow"; | |
| } | |
| void getAnalysisUsage(AnalysisUsage &AU) const override { | |
| AU.setPreservesCFG(); | |
| AU.addRequired<MachineDominatorTree>(); | |
| AU.addPreserved<MachineDominatorTree>(); | |
| AU.addRequired<MachineLoopInfo>(); | |
| AU.addPreserved<MachineLoopInfo>(); | |
| MachineFunctionPass::getAnalysisUsage(AU); | |
| } | |
| bool runOnMachineFunction(MachineFunction &MF) override; | |
| bool VisitLoop(MachineFunction &MF, MachineLoopInfo &MLI, MachineLoop *Loop); | |
| public: | |
| static char ID; // Pass identification, replacement for typeid | |
| WebAssemblyFixIrreducibleControlFlow() : MachineFunctionPass(ID) {} | |
| }; | |
| } // end anonymous namespace | |
| char WebAssemblyFixIrreducibleControlFlow::ID = 0; | |
| INITIALIZE_PASS(WebAssemblyFixIrreducibleControlFlow, DEBUG_TYPE, | |
| "Removes irreducible control flow", false, false) | |
| FunctionPass *llvm::createWebAssemblyFixIrreducibleControlFlow() { | |
| return new WebAssemblyFixIrreducibleControlFlow(); | |
| } | |
| namespace { | |
| /// A utility for walking the blocks of a loop, handling a nested inner | |
| /// loop as a monolithic conceptual block. | |
| class MetaBlock { | |
| MachineBasicBlock *Block; | |
| SmallVector<MachineBasicBlock *, 2> Preds; | |
| SmallVector<MachineBasicBlock *, 2> Succs; | |
| public: | |
| explicit MetaBlock(MachineBasicBlock *MBB) | |
| : Block(MBB), Preds(MBB->pred_begin(), MBB->pred_end()), | |
| Succs(MBB->succ_begin(), MBB->succ_end()) {} | |
| explicit MetaBlock(MachineLoop *Loop) : Block(Loop->getHeader()) { | |
| Loop->getExitBlocks(Succs); | |
| for (MachineBasicBlock *Pred : Block->predecessors()) | |
| if (!Loop->contains(Pred)) | |
| Preds.push_back(Pred); | |
| } | |
| MachineBasicBlock *getBlock() const { return Block; } | |
| const SmallVectorImpl<MachineBasicBlock *> &predecessors() const { | |
| return Preds; | |
| } | |
| const SmallVectorImpl<MachineBasicBlock *> &successors() const { | |
| return Succs; | |
| } | |
| bool operator==(const MetaBlock &MBB) { return Block == MBB.Block; } | |
| bool operator!=(const MetaBlock &MBB) { return Block != MBB.Block; } | |
| }; | |
| class SuccessorList final : public MetaBlock { | |
| size_t Index; | |
| size_t Num; | |
| public: | |
| explicit SuccessorList(MachineBasicBlock *MBB) | |
| : MetaBlock(MBB), Index(0), Num(successors().size()) {} | |
| explicit SuccessorList(MachineLoop *Loop) | |
| : MetaBlock(Loop), Index(0), Num(successors().size()) {} | |
| bool HasNext() const { return Index != Num; } | |
| MachineBasicBlock *Next() { | |
| assert(HasNext()); | |
| return successors()[Index++]; | |
| } | |
| }; | |
| } // end anonymous namespace | |
| bool WebAssemblyFixIrreducibleControlFlow::VisitLoop(MachineFunction &MF, | |
| MachineLoopInfo &MLI, | |
| MachineLoop *Loop) { | |
| MachineBasicBlock *Header = Loop ? Loop->getHeader() : &*MF.begin(); | |
| SetVector<MachineBasicBlock *> RewriteSuccs; | |
| // DFS through Loop's body, looking for irreducible control flow. Loop is | |
| // natural, and we stay in its body, and we treat any nested loops | |
| // monolithically, so any cycles we encounter indicate irreducibility. | |
| SmallPtrSet<MachineBasicBlock *, 8> OnStack; | |
| SmallPtrSet<MachineBasicBlock *, 8> Visited; | |
| SmallVector<SuccessorList, 4> LoopWorklist; | |
| LoopWorklist.push_back(SuccessorList(Header)); | |
| OnStack.insert(Header); | |
| Visited.insert(Header); | |
| while (!LoopWorklist.empty()) { | |
| SuccessorList &Top = LoopWorklist.back(); | |
| if (Top.HasNext()) { | |
| MachineBasicBlock *Next = Top.Next(); | |
| if (Next == Header || (Loop && !Loop->contains(Next))) | |
| continue; | |
| if (LLVM_LIKELY(OnStack.insert(Next).second)) { | |
| if (!Visited.insert(Next).second) { | |
| OnStack.erase(Next); | |
| continue; | |
| } | |
| MachineLoop *InnerLoop = MLI.getLoopFor(Next); | |
| if (InnerLoop != Loop) | |
| LoopWorklist.push_back(SuccessorList(InnerLoop)); | |
| else | |
| LoopWorklist.push_back(SuccessorList(Next)); | |
| } else { | |
| RewriteSuccs.insert(Top.getBlock()); | |
| } | |
| continue; | |
| } | |
| OnStack.erase(Top.getBlock()); | |
| LoopWorklist.pop_back(); | |
| } | |
| // Most likely, we didn't find any irreducible control flow. | |
| if (LLVM_LIKELY(RewriteSuccs.empty())) | |
| return false; | |
| LLVM_DEBUG(dbgs() << "Irreducible control flow detected!\n"); | |
| // Ok. We have irreducible control flow! Create a dispatch block which will | |
| // contains a jump table to any block in the problematic set of blocks. | |
| MachineBasicBlock *Dispatch = MF.CreateMachineBasicBlock(); | |
| MF.insert(MF.end(), Dispatch); | |
| MLI.changeLoopFor(Dispatch, Loop); | |
| // Add the jump table. | |
| const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); | |
| MachineInstrBuilder MIB = BuildMI(*Dispatch, Dispatch->end(), DebugLoc(), | |
| TII.get(WebAssembly::BR_TABLE_I32)); | |
| // Add the register which will be used to tell the jump table which block to | |
| // jump to. | |
| MachineRegisterInfo &MRI = MF.getRegInfo(); | |
| unsigned Reg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); | |
| MIB.addReg(Reg); | |
| // Collect all the blocks which need to have their successors rewritten, | |
| // add the successors to the jump table, and remember their index. | |
| DenseMap<MachineBasicBlock *, unsigned> Indices; | |
| SmallVector<MachineBasicBlock *, 4> SuccWorklist(RewriteSuccs.begin(), | |
| RewriteSuccs.end()); | |
| while (!SuccWorklist.empty()) { | |
| MachineBasicBlock *MBB = SuccWorklist.pop_back_val(); | |
| auto Pair = Indices.insert(std::make_pair(MBB, 0)); | |
| if (!Pair.second) | |
| continue; | |
| unsigned Index = MIB.getInstr()->getNumExplicitOperands() - 1; | |
| LLVM_DEBUG(dbgs() << printMBBReference(*MBB) << " has index " << Index | |
| << "\n"); | |
| Pair.first->second = Index; | |
| for (auto Pred : MBB->predecessors()) | |
| RewriteSuccs.insert(Pred); | |
| MIB.addMBB(MBB); | |
| Dispatch->addSuccessor(MBB); | |
| MetaBlock Meta(MBB); | |
| for (auto *Succ : Meta.successors()) | |
| if (Succ != Header && (!Loop || Loop->contains(Succ))) | |
| SuccWorklist.push_back(Succ); | |
| } | |
| // Rewrite the problematic successors for every block in RewriteSuccs. | |
| // For simplicity, we just introduce a new block for every edge we need to | |
| // rewrite. Fancier things are possible. | |
| for (MachineBasicBlock *MBB : RewriteSuccs) { | |
| DenseMap<MachineBasicBlock *, MachineBasicBlock *> Map; | |
| for (auto *Succ : MBB->successors()) { | |
| if (!Indices.count(Succ)) | |
| continue; | |
| MachineBasicBlock *Split = MF.CreateMachineBasicBlock(); | |
| MF.insert(MBB->isLayoutSuccessor(Succ) ? MachineFunction::iterator(Succ) | |
| : MF.end(), | |
| Split); | |
| MLI.changeLoopFor(Split, Loop); | |
| // Set the jump table's register of the index of the block we wish to | |
| // jump to, and jump to the jump table. | |
| BuildMI(*Split, Split->end(), DebugLoc(), TII.get(WebAssembly::CONST_I32), | |
| Reg) | |
| .addImm(Indices[Succ]); | |
| BuildMI(*Split, Split->end(), DebugLoc(), TII.get(WebAssembly::BR)) | |
| .addMBB(Dispatch); | |
| Split->addSuccessor(Dispatch); | |
| Map[Succ] = Split; | |
| } | |
| // Remap the terminator operands and the successor list. | |
| for (MachineInstr &Term : MBB->terminators()) | |
| for (auto &Op : Term.explicit_uses()) | |
| if (Op.isMBB() && Indices.count(Op.getMBB())) | |
| Op.setMBB(Map[Op.getMBB()]); | |
| for (auto Rewrite : Map) | |
| MBB->replaceSuccessor(Rewrite.first, Rewrite.second); | |
| } | |
| // Create a fake default label, because br_table requires one. | |
| MIB.addMBB(MIB.getInstr() | |
| ->getOperand(MIB.getInstr()->getNumExplicitOperands() - 1) | |
| .getMBB()); | |
| return true; | |
| } | |
| bool WebAssemblyFixIrreducibleControlFlow::runOnMachineFunction( | |
| MachineFunction &MF) { | |
| LLVM_DEBUG(dbgs() << "********** Fixing Irreducible Control Flow **********\n" | |
| "********** Function: " | |
| << MF.getName() << '\n'); | |
| bool Changed = false; | |
| auto &MLI = getAnalysis<MachineLoopInfo>(); | |
| // Visit the function body, which is identified as a null loop. | |
| Changed |= VisitLoop(MF, MLI, nullptr); | |
| // Visit all the loops. | |
| SmallVector<MachineLoop *, 8> Worklist(MLI.begin(), MLI.end()); | |
| while (!Worklist.empty()) { | |
| MachineLoop *CurLoop = Worklist.pop_back_val(); | |
| Worklist.append(CurLoop->begin(), CurLoop->end()); | |
| Changed |= VisitLoop(MF, MLI, CurLoop); | |
| } | |
| // If we made any changes, completely recompute everything. | |
| if (LLVM_UNLIKELY(Changed)) { | |
| LLVM_DEBUG(dbgs() << "Recomputing dominators and loops.\n"); | |
| MF.getRegInfo().invalidateLiveness(); | |
| MF.RenumberBlocks(); | |
| getAnalysis<MachineDominatorTree>().runOnMachineFunction(MF); | |
| MLI.runOnMachineFunction(MF); | |
| } | |
| return Changed; | |
| } |