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 
139d7a2eea6SDan Gohman   // DFS through Loop's body, looking for 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;
208*25528d6dSFrancis 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