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