1d7a2eea6SDan Gohman //=- WebAssemblyFixIrreducibleControlFlow.cpp - Fix irreducible control flow -// 2d7a2eea6SDan Gohman // 3d7a2eea6SDan Gohman // The LLVM Compiler Infrastructure 4d7a2eea6SDan Gohman // 5d7a2eea6SDan Gohman // This file is distributed under the University of Illinois Open Source 6d7a2eea6SDan Gohman // License. See LICENSE.TXT for details. 7d7a2eea6SDan Gohman // 8d7a2eea6SDan Gohman //===----------------------------------------------------------------------===// 9d7a2eea6SDan Gohman /// 10d7a2eea6SDan Gohman /// \file 115f8f34e4SAdrian Prantl /// This file implements a pass that transforms irreducible control flow 12d7a2eea6SDan Gohman /// into reducible control flow. Irreducible control flow means multiple-entry 13d7a2eea6SDan Gohman /// loops; they appear as CFG cycles that are not recorded in MachineLoopInfo 14d7a2eea6SDan Gohman /// due to being unnatural. 15d7a2eea6SDan Gohman /// 16ddfa1a6cSDan Gohman /// Note that LLVM has a generic pass that lowers irreducible control flow, but 17ddfa1a6cSDan Gohman /// it linearizes control flow, turning diamonds into two triangles, which is 18ddfa1a6cSDan Gohman /// both unnecessary and undesirable for WebAssembly. 19ddfa1a6cSDan Gohman /// 20d7a2eea6SDan Gohman /// TODO: The transformation implemented here handles all irreducible control 21d7a2eea6SDan Gohman /// flow, without exponential code-size expansion, though it does so by creating 22d7a2eea6SDan Gohman /// inefficient code in many cases. Ideally, we should add other 23d7a2eea6SDan Gohman /// transformations, including code-duplicating cases, which can be more 24d7a2eea6SDan Gohman /// efficient in common cases, and they can fall back to this conservative 25d7a2eea6SDan Gohman /// implementation as needed. 26d7a2eea6SDan Gohman /// 27d7a2eea6SDan Gohman //===----------------------------------------------------------------------===// 28d7a2eea6SDan Gohman 29d7a2eea6SDan Gohman #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 306bda14b3SChandler Carruth #include "WebAssembly.h" 31d7a2eea6SDan Gohman #include "WebAssemblyMachineFunctionInfo.h" 32d7a2eea6SDan Gohman #include "WebAssemblySubtarget.h" 33d7a2eea6SDan Gohman #include "llvm/ADT/PriorityQueue.h" 34d7a2eea6SDan Gohman #include "llvm/ADT/SCCIterator.h" 35d7a2eea6SDan Gohman #include "llvm/ADT/SetVector.h" 36d7a2eea6SDan Gohman #include "llvm/CodeGen/MachineDominators.h" 37d7a2eea6SDan Gohman #include "llvm/CodeGen/MachineFunction.h" 38d7a2eea6SDan Gohman #include "llvm/CodeGen/MachineInstrBuilder.h" 39d7a2eea6SDan Gohman #include "llvm/CodeGen/MachineLoopInfo.h" 40d7a2eea6SDan Gohman #include "llvm/CodeGen/MachineRegisterInfo.h" 41d7a2eea6SDan Gohman #include "llvm/CodeGen/Passes.h" 42d7a2eea6SDan Gohman #include "llvm/Support/Debug.h" 43d7a2eea6SDan Gohman #include "llvm/Support/raw_ostream.h" 44d7a2eea6SDan Gohman using namespace llvm; 45d7a2eea6SDan Gohman 46d7a2eea6SDan Gohman #define DEBUG_TYPE "wasm-fix-irreducible-control-flow" 47d7a2eea6SDan Gohman 48d7a2eea6SDan Gohman namespace { 49d7a2eea6SDan Gohman class WebAssemblyFixIrreducibleControlFlow final : public MachineFunctionPass { 50117296c0SMehdi Amini StringRef getPassName() const override { 51d7a2eea6SDan Gohman return "WebAssembly Fix Irreducible Control Flow"; 52d7a2eea6SDan Gohman } 53d7a2eea6SDan Gohman 54d7a2eea6SDan Gohman void getAnalysisUsage(AnalysisUsage &AU) const override { 55d7a2eea6SDan Gohman AU.setPreservesCFG(); 56d7a2eea6SDan Gohman AU.addRequired<MachineDominatorTree>(); 57d7a2eea6SDan Gohman AU.addPreserved<MachineDominatorTree>(); 58d7a2eea6SDan Gohman AU.addRequired<MachineLoopInfo>(); 59d7a2eea6SDan Gohman AU.addPreserved<MachineLoopInfo>(); 60d7a2eea6SDan Gohman MachineFunctionPass::getAnalysisUsage(AU); 61d7a2eea6SDan Gohman } 62d7a2eea6SDan Gohman 63d7a2eea6SDan Gohman bool runOnMachineFunction(MachineFunction &MF) override; 64d7a2eea6SDan Gohman 65d7a2eea6SDan Gohman bool VisitLoop(MachineFunction &MF, MachineLoopInfo &MLI, MachineLoop *Loop); 66d7a2eea6SDan Gohman 67d7a2eea6SDan Gohman public: 68d7a2eea6SDan Gohman static char ID; // Pass identification, replacement for typeid 69d7a2eea6SDan Gohman WebAssemblyFixIrreducibleControlFlow() : MachineFunctionPass(ID) {} 70d7a2eea6SDan Gohman }; 71d7a2eea6SDan Gohman } // end anonymous namespace 72d7a2eea6SDan Gohman 73d7a2eea6SDan Gohman char WebAssemblyFixIrreducibleControlFlow::ID = 0; 7440926451SJacob Gravelle INITIALIZE_PASS(WebAssemblyFixIrreducibleControlFlow, DEBUG_TYPE, 7540926451SJacob Gravelle "Removes irreducible control flow", false, false) 7640926451SJacob Gravelle 77d7a2eea6SDan Gohman FunctionPass *llvm::createWebAssemblyFixIrreducibleControlFlow() { 78d7a2eea6SDan Gohman return new WebAssemblyFixIrreducibleControlFlow(); 79d7a2eea6SDan Gohman } 80d7a2eea6SDan Gohman 81d7a2eea6SDan Gohman namespace { 82d7a2eea6SDan Gohman 83d7a2eea6SDan Gohman /// A utility for walking the blocks of a loop, handling a nested inner 84d7a2eea6SDan Gohman /// loop as a monolithic conceptual block. 85d7a2eea6SDan Gohman class MetaBlock { 86d7a2eea6SDan Gohman MachineBasicBlock *Block; 87d7a2eea6SDan Gohman SmallVector<MachineBasicBlock *, 2> Preds; 88d7a2eea6SDan Gohman SmallVector<MachineBasicBlock *, 2> Succs; 89d7a2eea6SDan Gohman 90d7a2eea6SDan Gohman public: 91d7a2eea6SDan Gohman explicit MetaBlock(MachineBasicBlock *MBB) 92d7a2eea6SDan Gohman : Block(MBB), Preds(MBB->pred_begin(), MBB->pred_end()), 93d7a2eea6SDan Gohman Succs(MBB->succ_begin(), MBB->succ_end()) {} 94d7a2eea6SDan Gohman 95d7a2eea6SDan Gohman explicit MetaBlock(MachineLoop *Loop) : Block(Loop->getHeader()) { 96d7a2eea6SDan Gohman Loop->getExitBlocks(Succs); 97d7a2eea6SDan Gohman for (MachineBasicBlock *Pred : Block->predecessors()) 98d7a2eea6SDan Gohman if (!Loop->contains(Pred)) 99d7a2eea6SDan Gohman Preds.push_back(Pred); 100d7a2eea6SDan Gohman } 101d7a2eea6SDan Gohman 102d7a2eea6SDan Gohman MachineBasicBlock *getBlock() const { return Block; } 103d7a2eea6SDan Gohman 104d7a2eea6SDan Gohman const SmallVectorImpl<MachineBasicBlock *> &predecessors() const { 105d7a2eea6SDan Gohman return Preds; 106d7a2eea6SDan Gohman } 107d7a2eea6SDan Gohman const SmallVectorImpl<MachineBasicBlock *> &successors() const { 108d7a2eea6SDan Gohman return Succs; 109d7a2eea6SDan Gohman } 110d7a2eea6SDan Gohman 111d7a2eea6SDan Gohman bool operator==(const MetaBlock &MBB) { return Block == MBB.Block; } 112d7a2eea6SDan Gohman bool operator!=(const MetaBlock &MBB) { return Block != MBB.Block; } 113d7a2eea6SDan Gohman }; 114d7a2eea6SDan Gohman 115da323e88SDan Gohman class SuccessorList final : public MetaBlock { 116d7a2eea6SDan Gohman size_t Index; 117d7a2eea6SDan Gohman size_t Num; 118d7a2eea6SDan Gohman 119d7a2eea6SDan Gohman public: 120d7a2eea6SDan Gohman explicit SuccessorList(MachineBasicBlock *MBB) 121d7a2eea6SDan Gohman : MetaBlock(MBB), Index(0), Num(successors().size()) {} 122d7a2eea6SDan Gohman 123d7a2eea6SDan Gohman explicit SuccessorList(MachineLoop *Loop) 124d7a2eea6SDan Gohman : MetaBlock(Loop), Index(0), Num(successors().size()) {} 125d7a2eea6SDan Gohman 126d7a2eea6SDan Gohman bool HasNext() const { return Index != Num; } 127d7a2eea6SDan Gohman 128d7a2eea6SDan Gohman MachineBasicBlock *Next() { 129d7a2eea6SDan Gohman assert(HasNext()); 130d7a2eea6SDan Gohman return successors()[Index++]; 131d7a2eea6SDan Gohman } 132d7a2eea6SDan Gohman }; 133d7a2eea6SDan Gohman 134d7a2eea6SDan Gohman } // end anonymous namespace 135d7a2eea6SDan Gohman 136d7a2eea6SDan Gohman bool WebAssemblyFixIrreducibleControlFlow::VisitLoop(MachineFunction &MF, 137d7a2eea6SDan Gohman MachineLoopInfo &MLI, 138d7a2eea6SDan Gohman MachineLoop *Loop) { 139d7a2eea6SDan Gohman MachineBasicBlock *Header = Loop ? Loop->getHeader() : &*MF.begin(); 140d7a2eea6SDan Gohman SetVector<MachineBasicBlock *> RewriteSuccs; 141d7a2eea6SDan Gohman 1420909ca13SHiroshi Inoue // DFS through Loop's body, looking for irreducible control flow. Loop is 143d7a2eea6SDan Gohman // natural, and we stay in its body, and we treat any nested loops 144d7a2eea6SDan Gohman // monolithically, so any cycles we encounter indicate irreducibility. 145d7a2eea6SDan Gohman SmallPtrSet<MachineBasicBlock *, 8> OnStack; 146d7a2eea6SDan Gohman SmallPtrSet<MachineBasicBlock *, 8> Visited; 147d7a2eea6SDan Gohman SmallVector<SuccessorList, 4> LoopWorklist; 148d7a2eea6SDan Gohman LoopWorklist.push_back(SuccessorList(Header)); 149d7a2eea6SDan Gohman OnStack.insert(Header); 150d7a2eea6SDan Gohman Visited.insert(Header); 151d7a2eea6SDan Gohman while (!LoopWorklist.empty()) { 152d7a2eea6SDan Gohman SuccessorList &Top = LoopWorklist.back(); 153d7a2eea6SDan Gohman if (Top.HasNext()) { 154d7a2eea6SDan Gohman MachineBasicBlock *Next = Top.Next(); 155d7a2eea6SDan Gohman if (Next == Header || (Loop && !Loop->contains(Next))) 156d7a2eea6SDan Gohman continue; 157d7a2eea6SDan Gohman if (LLVM_LIKELY(OnStack.insert(Next).second)) { 158d7a2eea6SDan Gohman if (!Visited.insert(Next).second) { 159d7a2eea6SDan Gohman OnStack.erase(Next); 160d7a2eea6SDan Gohman continue; 161d7a2eea6SDan Gohman } 162d7a2eea6SDan Gohman MachineLoop *InnerLoop = MLI.getLoopFor(Next); 163d7a2eea6SDan Gohman if (InnerLoop != Loop) 164d7a2eea6SDan Gohman LoopWorklist.push_back(SuccessorList(InnerLoop)); 165d7a2eea6SDan Gohman else 166d7a2eea6SDan Gohman LoopWorklist.push_back(SuccessorList(Next)); 167d7a2eea6SDan Gohman } else { 168d7a2eea6SDan Gohman RewriteSuccs.insert(Top.getBlock()); 169d7a2eea6SDan Gohman } 170d7a2eea6SDan Gohman continue; 171d7a2eea6SDan Gohman } 172d7a2eea6SDan Gohman OnStack.erase(Top.getBlock()); 173d7a2eea6SDan Gohman LoopWorklist.pop_back(); 174d7a2eea6SDan Gohman } 175d7a2eea6SDan Gohman 176d7a2eea6SDan Gohman // Most likely, we didn't find any irreducible control flow. 177d7a2eea6SDan Gohman if (LLVM_LIKELY(RewriteSuccs.empty())) 178d7a2eea6SDan Gohman return false; 179d7a2eea6SDan Gohman 180*d34e60caSNicola Zaghen LLVM_DEBUG(dbgs() << "Irreducible control flow detected!\n"); 181d7a2eea6SDan Gohman 182d7a2eea6SDan Gohman // Ok. We have irreducible control flow! Create a dispatch block which will 183d7a2eea6SDan Gohman // contains a jump table to any block in the problematic set of blocks. 184d7a2eea6SDan Gohman MachineBasicBlock *Dispatch = MF.CreateMachineBasicBlock(); 185d7a2eea6SDan Gohman MF.insert(MF.end(), Dispatch); 186d7a2eea6SDan Gohman MLI.changeLoopFor(Dispatch, Loop); 187d7a2eea6SDan Gohman 188d7a2eea6SDan Gohman // Add the jump table. 189d7a2eea6SDan Gohman const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 190d7a2eea6SDan Gohman MachineInstrBuilder MIB = BuildMI(*Dispatch, Dispatch->end(), DebugLoc(), 191d7a2eea6SDan Gohman TII.get(WebAssembly::BR_TABLE_I32)); 192d7a2eea6SDan Gohman 193d7a2eea6SDan Gohman // Add the register which will be used to tell the jump table which block to 194d7a2eea6SDan Gohman // jump to. 195d7a2eea6SDan Gohman MachineRegisterInfo &MRI = MF.getRegInfo(); 196d7a2eea6SDan Gohman unsigned Reg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); 197d7a2eea6SDan Gohman MIB.addReg(Reg); 198d7a2eea6SDan Gohman 199d7a2eea6SDan Gohman // Collect all the blocks which need to have their successors rewritten, 200d7a2eea6SDan Gohman // add the successors to the jump table, and remember their index. 201d7a2eea6SDan Gohman DenseMap<MachineBasicBlock *, unsigned> Indices; 202d7a2eea6SDan Gohman SmallVector<MachineBasicBlock *, 4> SuccWorklist(RewriteSuccs.begin(), 203d7a2eea6SDan Gohman RewriteSuccs.end()); 204d7a2eea6SDan Gohman while (!SuccWorklist.empty()) { 205d7a2eea6SDan Gohman MachineBasicBlock *MBB = SuccWorklist.pop_back_val(); 206d7a2eea6SDan Gohman auto Pair = Indices.insert(std::make_pair(MBB, 0)); 207d7a2eea6SDan Gohman if (!Pair.second) 208d7a2eea6SDan Gohman continue; 209d7a2eea6SDan Gohman 210f456290fSDan Gohman unsigned Index = MIB.getInstr()->getNumExplicitOperands() - 1; 211*d34e60caSNicola Zaghen LLVM_DEBUG(dbgs() << printMBBReference(*MBB) << " has index " << Index 212*d34e60caSNicola Zaghen << "\n"); 213d7a2eea6SDan Gohman 214d7a2eea6SDan Gohman Pair.first->second = Index; 215d7a2eea6SDan Gohman for (auto Pred : MBB->predecessors()) 216d7a2eea6SDan Gohman RewriteSuccs.insert(Pred); 217d7a2eea6SDan Gohman 218d7a2eea6SDan Gohman MIB.addMBB(MBB); 219d7a2eea6SDan Gohman Dispatch->addSuccessor(MBB); 220d7a2eea6SDan Gohman 221d7a2eea6SDan Gohman MetaBlock Meta(MBB); 222d7a2eea6SDan Gohman for (auto *Succ : Meta.successors()) 223d7a2eea6SDan Gohman if (Succ != Header && (!Loop || Loop->contains(Succ))) 224d7a2eea6SDan Gohman SuccWorklist.push_back(Succ); 225d7a2eea6SDan Gohman } 226d7a2eea6SDan Gohman 227d7a2eea6SDan Gohman // Rewrite the problematic successors for every block in RewriteSuccs. 228d7a2eea6SDan Gohman // For simplicity, we just introduce a new block for every edge we need to 229d7a2eea6SDan Gohman // rewrite. Fancier things are possible. 230d7a2eea6SDan Gohman for (MachineBasicBlock *MBB : RewriteSuccs) { 231d7a2eea6SDan Gohman DenseMap<MachineBasicBlock *, MachineBasicBlock *> Map; 232d7a2eea6SDan Gohman for (auto *Succ : MBB->successors()) { 233d7a2eea6SDan Gohman if (!Indices.count(Succ)) 234d7a2eea6SDan Gohman continue; 235d7a2eea6SDan Gohman 236d7a2eea6SDan Gohman MachineBasicBlock *Split = MF.CreateMachineBasicBlock(); 237d7a2eea6SDan Gohman MF.insert(MBB->isLayoutSuccessor(Succ) ? MachineFunction::iterator(Succ) 238d7a2eea6SDan Gohman : MF.end(), 239d7a2eea6SDan Gohman Split); 240d7a2eea6SDan Gohman MLI.changeLoopFor(Split, Loop); 241d7a2eea6SDan Gohman 242d7a2eea6SDan Gohman // Set the jump table's register of the index of the block we wish to 243d7a2eea6SDan Gohman // jump to, and jump to the jump table. 244d7a2eea6SDan Gohman BuildMI(*Split, Split->end(), DebugLoc(), TII.get(WebAssembly::CONST_I32), 245d7a2eea6SDan Gohman Reg) 246d7a2eea6SDan Gohman .addImm(Indices[Succ]); 247d7a2eea6SDan Gohman BuildMI(*Split, Split->end(), DebugLoc(), TII.get(WebAssembly::BR)) 248d7a2eea6SDan Gohman .addMBB(Dispatch); 249d7a2eea6SDan Gohman Split->addSuccessor(Dispatch); 250d7a2eea6SDan Gohman Map[Succ] = Split; 251d7a2eea6SDan Gohman } 252d7a2eea6SDan Gohman // Remap the terminator operands and the successor list. 253d7a2eea6SDan Gohman for (MachineInstr &Term : MBB->terminators()) 254d7a2eea6SDan Gohman for (auto &Op : Term.explicit_uses()) 255d7a2eea6SDan Gohman if (Op.isMBB() && Indices.count(Op.getMBB())) 256d7a2eea6SDan Gohman Op.setMBB(Map[Op.getMBB()]); 257d7a2eea6SDan Gohman for (auto Rewrite : Map) 258d7a2eea6SDan Gohman MBB->replaceSuccessor(Rewrite.first, Rewrite.second); 259d7a2eea6SDan Gohman } 260d7a2eea6SDan Gohman 261d7a2eea6SDan Gohman // Create a fake default label, because br_table requires one. 262d7a2eea6SDan Gohman MIB.addMBB(MIB.getInstr() 263d7a2eea6SDan Gohman ->getOperand(MIB.getInstr()->getNumExplicitOperands() - 1) 264d7a2eea6SDan Gohman .getMBB()); 265d7a2eea6SDan Gohman 266d7a2eea6SDan Gohman return true; 267d7a2eea6SDan Gohman } 268d7a2eea6SDan Gohman 269d7a2eea6SDan Gohman bool WebAssemblyFixIrreducibleControlFlow::runOnMachineFunction( 270d7a2eea6SDan Gohman MachineFunction &MF) { 271*d34e60caSNicola Zaghen LLVM_DEBUG(dbgs() << "********** Fixing Irreducible Control Flow **********\n" 272d7a2eea6SDan Gohman "********** Function: " 273d7a2eea6SDan Gohman << MF.getName() << '\n'); 274d7a2eea6SDan Gohman 275d7a2eea6SDan Gohman bool Changed = false; 276d7a2eea6SDan Gohman auto &MLI = getAnalysis<MachineLoopInfo>(); 277d7a2eea6SDan Gohman 278d7a2eea6SDan Gohman // Visit the function body, which is identified as a null loop. 279d7a2eea6SDan Gohman Changed |= VisitLoop(MF, MLI, nullptr); 280d7a2eea6SDan Gohman 281d7a2eea6SDan Gohman // Visit all the loops. 282d7a2eea6SDan Gohman SmallVector<MachineLoop *, 8> Worklist(MLI.begin(), MLI.end()); 283d7a2eea6SDan Gohman while (!Worklist.empty()) { 284d7a2eea6SDan Gohman MachineLoop *CurLoop = Worklist.pop_back_val(); 285d7a2eea6SDan Gohman Worklist.append(CurLoop->begin(), CurLoop->end()); 286d7a2eea6SDan Gohman Changed |= VisitLoop(MF, MLI, CurLoop); 287d7a2eea6SDan Gohman } 288d7a2eea6SDan Gohman 289d7a2eea6SDan Gohman // If we made any changes, completely recompute everything. 290d7a2eea6SDan Gohman if (LLVM_UNLIKELY(Changed)) { 291*d34e60caSNicola Zaghen LLVM_DEBUG(dbgs() << "Recomputing dominators and loops.\n"); 292d7a2eea6SDan Gohman MF.getRegInfo().invalidateLiveness(); 293d7a2eea6SDan Gohman MF.RenumberBlocks(); 294d7a2eea6SDan Gohman getAnalysis<MachineDominatorTree>().runOnMachineFunction(MF); 295d7a2eea6SDan Gohman MLI.runOnMachineFunction(MF); 296d7a2eea6SDan Gohman } 297d7a2eea6SDan Gohman 298d7a2eea6SDan Gohman return Changed; 299d7a2eea6SDan Gohman } 300