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 11d7a2eea6SDan Gohman /// \brief 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; 74d7a2eea6SDan Gohman FunctionPass *llvm::createWebAssemblyFixIrreducibleControlFlow() { 75d7a2eea6SDan Gohman return new WebAssemblyFixIrreducibleControlFlow(); 76d7a2eea6SDan Gohman } 77d7a2eea6SDan Gohman 78d7a2eea6SDan Gohman namespace { 79d7a2eea6SDan Gohman 80d7a2eea6SDan Gohman /// A utility for walking the blocks of a loop, handling a nested inner 81d7a2eea6SDan Gohman /// loop as a monolithic conceptual block. 82d7a2eea6SDan Gohman class MetaBlock { 83d7a2eea6SDan Gohman MachineBasicBlock *Block; 84d7a2eea6SDan Gohman SmallVector<MachineBasicBlock *, 2> Preds; 85d7a2eea6SDan Gohman SmallVector<MachineBasicBlock *, 2> Succs; 86d7a2eea6SDan Gohman 87d7a2eea6SDan Gohman public: 88d7a2eea6SDan Gohman explicit MetaBlock(MachineBasicBlock *MBB) 89d7a2eea6SDan Gohman : Block(MBB), Preds(MBB->pred_begin(), MBB->pred_end()), 90d7a2eea6SDan Gohman Succs(MBB->succ_begin(), MBB->succ_end()) {} 91d7a2eea6SDan Gohman 92d7a2eea6SDan Gohman explicit MetaBlock(MachineLoop *Loop) : Block(Loop->getHeader()) { 93d7a2eea6SDan Gohman Loop->getExitBlocks(Succs); 94d7a2eea6SDan Gohman for (MachineBasicBlock *Pred : Block->predecessors()) 95d7a2eea6SDan Gohman if (!Loop->contains(Pred)) 96d7a2eea6SDan Gohman Preds.push_back(Pred); 97d7a2eea6SDan Gohman } 98d7a2eea6SDan Gohman 99d7a2eea6SDan Gohman MachineBasicBlock *getBlock() const { return Block; } 100d7a2eea6SDan Gohman 101d7a2eea6SDan Gohman const SmallVectorImpl<MachineBasicBlock *> &predecessors() const { 102d7a2eea6SDan Gohman return Preds; 103d7a2eea6SDan Gohman } 104d7a2eea6SDan Gohman const SmallVectorImpl<MachineBasicBlock *> &successors() const { 105d7a2eea6SDan Gohman return Succs; 106d7a2eea6SDan Gohman } 107d7a2eea6SDan Gohman 108d7a2eea6SDan Gohman bool operator==(const MetaBlock &MBB) { return Block == MBB.Block; } 109d7a2eea6SDan Gohman bool operator!=(const MetaBlock &MBB) { return Block != MBB.Block; } 110d7a2eea6SDan Gohman }; 111d7a2eea6SDan Gohman 112da323e88SDan Gohman class SuccessorList final : public MetaBlock { 113d7a2eea6SDan Gohman size_t Index; 114d7a2eea6SDan Gohman size_t Num; 115d7a2eea6SDan Gohman 116d7a2eea6SDan Gohman public: 117d7a2eea6SDan Gohman explicit SuccessorList(MachineBasicBlock *MBB) 118d7a2eea6SDan Gohman : MetaBlock(MBB), Index(0), Num(successors().size()) {} 119d7a2eea6SDan Gohman 120d7a2eea6SDan Gohman explicit SuccessorList(MachineLoop *Loop) 121d7a2eea6SDan Gohman : MetaBlock(Loop), Index(0), Num(successors().size()) {} 122d7a2eea6SDan Gohman 123d7a2eea6SDan Gohman bool HasNext() const { return Index != Num; } 124d7a2eea6SDan Gohman 125d7a2eea6SDan Gohman MachineBasicBlock *Next() { 126d7a2eea6SDan Gohman assert(HasNext()); 127d7a2eea6SDan Gohman return successors()[Index++]; 128d7a2eea6SDan Gohman } 129d7a2eea6SDan Gohman }; 130d7a2eea6SDan Gohman 131d7a2eea6SDan Gohman } // end anonymous namespace 132d7a2eea6SDan Gohman 133d7a2eea6SDan Gohman bool WebAssemblyFixIrreducibleControlFlow::VisitLoop(MachineFunction &MF, 134d7a2eea6SDan Gohman MachineLoopInfo &MLI, 135d7a2eea6SDan Gohman MachineLoop *Loop) { 136d7a2eea6SDan Gohman MachineBasicBlock *Header = Loop ? Loop->getHeader() : &*MF.begin(); 137d7a2eea6SDan Gohman SetVector<MachineBasicBlock *> RewriteSuccs; 138d7a2eea6SDan Gohman 139*0909ca13SHiroshi Inoue // DFS through Loop's body, looking for irreducible control flow. Loop is 140d7a2eea6SDan Gohman // natural, and we stay in its body, and we treat any nested loops 141d7a2eea6SDan Gohman // monolithically, so any cycles we encounter indicate irreducibility. 142d7a2eea6SDan Gohman SmallPtrSet<MachineBasicBlock *, 8> OnStack; 143d7a2eea6SDan Gohman SmallPtrSet<MachineBasicBlock *, 8> Visited; 144d7a2eea6SDan Gohman SmallVector<SuccessorList, 4> LoopWorklist; 145d7a2eea6SDan Gohman LoopWorklist.push_back(SuccessorList(Header)); 146d7a2eea6SDan Gohman OnStack.insert(Header); 147d7a2eea6SDan Gohman Visited.insert(Header); 148d7a2eea6SDan Gohman while (!LoopWorklist.empty()) { 149d7a2eea6SDan Gohman SuccessorList &Top = LoopWorklist.back(); 150d7a2eea6SDan Gohman if (Top.HasNext()) { 151d7a2eea6SDan Gohman MachineBasicBlock *Next = Top.Next(); 152d7a2eea6SDan Gohman if (Next == Header || (Loop && !Loop->contains(Next))) 153d7a2eea6SDan Gohman continue; 154d7a2eea6SDan Gohman if (LLVM_LIKELY(OnStack.insert(Next).second)) { 155d7a2eea6SDan Gohman if (!Visited.insert(Next).second) { 156d7a2eea6SDan Gohman OnStack.erase(Next); 157d7a2eea6SDan Gohman continue; 158d7a2eea6SDan Gohman } 159d7a2eea6SDan Gohman MachineLoop *InnerLoop = MLI.getLoopFor(Next); 160d7a2eea6SDan Gohman if (InnerLoop != Loop) 161d7a2eea6SDan Gohman LoopWorklist.push_back(SuccessorList(InnerLoop)); 162d7a2eea6SDan Gohman else 163d7a2eea6SDan Gohman LoopWorklist.push_back(SuccessorList(Next)); 164d7a2eea6SDan Gohman } else { 165d7a2eea6SDan Gohman RewriteSuccs.insert(Top.getBlock()); 166d7a2eea6SDan Gohman } 167d7a2eea6SDan Gohman continue; 168d7a2eea6SDan Gohman } 169d7a2eea6SDan Gohman OnStack.erase(Top.getBlock()); 170d7a2eea6SDan Gohman LoopWorklist.pop_back(); 171d7a2eea6SDan Gohman } 172d7a2eea6SDan Gohman 173d7a2eea6SDan Gohman // Most likely, we didn't find any irreducible control flow. 174d7a2eea6SDan Gohman if (LLVM_LIKELY(RewriteSuccs.empty())) 175d7a2eea6SDan Gohman return false; 176d7a2eea6SDan Gohman 177d7a2eea6SDan Gohman DEBUG(dbgs() << "Irreducible control flow detected!\n"); 178d7a2eea6SDan Gohman 179d7a2eea6SDan Gohman // Ok. We have irreducible control flow! Create a dispatch block which will 180d7a2eea6SDan Gohman // contains a jump table to any block in the problematic set of blocks. 181d7a2eea6SDan Gohman MachineBasicBlock *Dispatch = MF.CreateMachineBasicBlock(); 182d7a2eea6SDan Gohman MF.insert(MF.end(), Dispatch); 183d7a2eea6SDan Gohman MLI.changeLoopFor(Dispatch, Loop); 184d7a2eea6SDan Gohman 185d7a2eea6SDan Gohman // Add the jump table. 186d7a2eea6SDan Gohman const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 187d7a2eea6SDan Gohman MachineInstrBuilder MIB = BuildMI(*Dispatch, Dispatch->end(), DebugLoc(), 188d7a2eea6SDan Gohman TII.get(WebAssembly::BR_TABLE_I32)); 189d7a2eea6SDan Gohman 190d7a2eea6SDan Gohman // Add the register which will be used to tell the jump table which block to 191d7a2eea6SDan Gohman // jump to. 192d7a2eea6SDan Gohman MachineRegisterInfo &MRI = MF.getRegInfo(); 193d7a2eea6SDan Gohman unsigned Reg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); 194d7a2eea6SDan Gohman MIB.addReg(Reg); 195d7a2eea6SDan Gohman 196d7a2eea6SDan Gohman // Collect all the blocks which need to have their successors rewritten, 197d7a2eea6SDan Gohman // add the successors to the jump table, and remember their index. 198d7a2eea6SDan Gohman DenseMap<MachineBasicBlock *, unsigned> Indices; 199d7a2eea6SDan Gohman SmallVector<MachineBasicBlock *, 4> SuccWorklist(RewriteSuccs.begin(), 200d7a2eea6SDan Gohman RewriteSuccs.end()); 201d7a2eea6SDan Gohman while (!SuccWorklist.empty()) { 202d7a2eea6SDan Gohman MachineBasicBlock *MBB = SuccWorklist.pop_back_val(); 203d7a2eea6SDan Gohman auto Pair = Indices.insert(std::make_pair(MBB, 0)); 204d7a2eea6SDan Gohman if (!Pair.second) 205d7a2eea6SDan Gohman continue; 206d7a2eea6SDan Gohman 207f456290fSDan Gohman unsigned Index = MIB.getInstr()->getNumExplicitOperands() - 1; 20825528d6dSFrancis Visoiu Mistrih DEBUG(dbgs() << printMBBReference(*MBB) << " has index " << Index << "\n"); 209d7a2eea6SDan Gohman 210d7a2eea6SDan Gohman Pair.first->second = Index; 211d7a2eea6SDan Gohman for (auto Pred : MBB->predecessors()) 212d7a2eea6SDan Gohman RewriteSuccs.insert(Pred); 213d7a2eea6SDan Gohman 214d7a2eea6SDan Gohman MIB.addMBB(MBB); 215d7a2eea6SDan Gohman Dispatch->addSuccessor(MBB); 216d7a2eea6SDan Gohman 217d7a2eea6SDan Gohman MetaBlock Meta(MBB); 218d7a2eea6SDan Gohman for (auto *Succ : Meta.successors()) 219d7a2eea6SDan Gohman if (Succ != Header && (!Loop || Loop->contains(Succ))) 220d7a2eea6SDan Gohman SuccWorklist.push_back(Succ); 221d7a2eea6SDan Gohman } 222d7a2eea6SDan Gohman 223d7a2eea6SDan Gohman // Rewrite the problematic successors for every block in RewriteSuccs. 224d7a2eea6SDan Gohman // For simplicity, we just introduce a new block for every edge we need to 225d7a2eea6SDan Gohman // rewrite. Fancier things are possible. 226d7a2eea6SDan Gohman for (MachineBasicBlock *MBB : RewriteSuccs) { 227d7a2eea6SDan Gohman DenseMap<MachineBasicBlock *, MachineBasicBlock *> Map; 228d7a2eea6SDan Gohman for (auto *Succ : MBB->successors()) { 229d7a2eea6SDan Gohman if (!Indices.count(Succ)) 230d7a2eea6SDan Gohman continue; 231d7a2eea6SDan Gohman 232d7a2eea6SDan Gohman MachineBasicBlock *Split = MF.CreateMachineBasicBlock(); 233d7a2eea6SDan Gohman MF.insert(MBB->isLayoutSuccessor(Succ) ? MachineFunction::iterator(Succ) 234d7a2eea6SDan Gohman : MF.end(), 235d7a2eea6SDan Gohman Split); 236d7a2eea6SDan Gohman MLI.changeLoopFor(Split, Loop); 237d7a2eea6SDan Gohman 238d7a2eea6SDan Gohman // Set the jump table's register of the index of the block we wish to 239d7a2eea6SDan Gohman // jump to, and jump to the jump table. 240d7a2eea6SDan Gohman BuildMI(*Split, Split->end(), DebugLoc(), TII.get(WebAssembly::CONST_I32), 241d7a2eea6SDan Gohman Reg) 242d7a2eea6SDan Gohman .addImm(Indices[Succ]); 243d7a2eea6SDan Gohman BuildMI(*Split, Split->end(), DebugLoc(), TII.get(WebAssembly::BR)) 244d7a2eea6SDan Gohman .addMBB(Dispatch); 245d7a2eea6SDan Gohman Split->addSuccessor(Dispatch); 246d7a2eea6SDan Gohman Map[Succ] = Split; 247d7a2eea6SDan Gohman } 248d7a2eea6SDan Gohman // Remap the terminator operands and the successor list. 249d7a2eea6SDan Gohman for (MachineInstr &Term : MBB->terminators()) 250d7a2eea6SDan Gohman for (auto &Op : Term.explicit_uses()) 251d7a2eea6SDan Gohman if (Op.isMBB() && Indices.count(Op.getMBB())) 252d7a2eea6SDan Gohman Op.setMBB(Map[Op.getMBB()]); 253d7a2eea6SDan Gohman for (auto Rewrite : Map) 254d7a2eea6SDan Gohman MBB->replaceSuccessor(Rewrite.first, Rewrite.second); 255d7a2eea6SDan Gohman } 256d7a2eea6SDan Gohman 257d7a2eea6SDan Gohman // Create a fake default label, because br_table requires one. 258d7a2eea6SDan Gohman MIB.addMBB(MIB.getInstr() 259d7a2eea6SDan Gohman ->getOperand(MIB.getInstr()->getNumExplicitOperands() - 1) 260d7a2eea6SDan Gohman .getMBB()); 261d7a2eea6SDan Gohman 262d7a2eea6SDan Gohman return true; 263d7a2eea6SDan Gohman } 264d7a2eea6SDan Gohman 265d7a2eea6SDan Gohman bool WebAssemblyFixIrreducibleControlFlow::runOnMachineFunction( 266d7a2eea6SDan Gohman MachineFunction &MF) { 267d7a2eea6SDan Gohman DEBUG(dbgs() << "********** Fixing Irreducible Control Flow **********\n" 268d7a2eea6SDan Gohman "********** Function: " 269d7a2eea6SDan Gohman << MF.getName() << '\n'); 270d7a2eea6SDan Gohman 271d7a2eea6SDan Gohman bool Changed = false; 272d7a2eea6SDan Gohman auto &MLI = getAnalysis<MachineLoopInfo>(); 273d7a2eea6SDan Gohman 274d7a2eea6SDan Gohman // Visit the function body, which is identified as a null loop. 275d7a2eea6SDan Gohman Changed |= VisitLoop(MF, MLI, nullptr); 276d7a2eea6SDan Gohman 277d7a2eea6SDan Gohman // Visit all the loops. 278d7a2eea6SDan Gohman SmallVector<MachineLoop *, 8> Worklist(MLI.begin(), MLI.end()); 279d7a2eea6SDan Gohman while (!Worklist.empty()) { 280d7a2eea6SDan Gohman MachineLoop *CurLoop = Worklist.pop_back_val(); 281d7a2eea6SDan Gohman Worklist.append(CurLoop->begin(), CurLoop->end()); 282d7a2eea6SDan Gohman Changed |= VisitLoop(MF, MLI, CurLoop); 283d7a2eea6SDan Gohman } 284d7a2eea6SDan Gohman 285d7a2eea6SDan Gohman // If we made any changes, completely recompute everything. 286d7a2eea6SDan Gohman if (LLVM_UNLIKELY(Changed)) { 287d7a2eea6SDan Gohman DEBUG(dbgs() << "Recomputing dominators and loops.\n"); 288d7a2eea6SDan Gohman MF.getRegInfo().invalidateLiveness(); 289d7a2eea6SDan Gohman MF.RenumberBlocks(); 290d7a2eea6SDan Gohman getAnalysis<MachineDominatorTree>().runOnMachineFunction(MF); 291d7a2eea6SDan Gohman MLI.runOnMachineFunction(MF); 292d7a2eea6SDan Gohman } 293d7a2eea6SDan Gohman 294d7a2eea6SDan Gohman return Changed; 295d7a2eea6SDan Gohman } 296