| //=- 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; |
| } |