xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/ARM/ARMBlockPlacement.cpp (revision 4652422eb477731f284b1345afeefef7f269da50)
1e8d8bef9SDimitry Andric //===-- ARMBlockPlacement.cpp - ARM block placement pass ------------===//
2e8d8bef9SDimitry Andric //
3e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e8d8bef9SDimitry Andric //
7e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===//
8e8d8bef9SDimitry Andric //
9e8d8bef9SDimitry Andric // This pass re-arranges machine basic blocks to suit target requirements.
10e8d8bef9SDimitry Andric // Currently it only moves blocks to fix backwards WLS branches.
11e8d8bef9SDimitry Andric //
12e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===//
13e8d8bef9SDimitry Andric 
14e8d8bef9SDimitry Andric #include "ARM.h"
15e8d8bef9SDimitry Andric #include "ARMBaseInstrInfo.h"
16e8d8bef9SDimitry Andric #include "ARMBasicBlockInfo.h"
17e8d8bef9SDimitry Andric #include "ARMSubtarget.h"
18e8d8bef9SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h"
19e8d8bef9SDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h"
20e8d8bef9SDimitry Andric #include "llvm/CodeGen/MachineLoopInfo.h"
21e8d8bef9SDimitry Andric 
22e8d8bef9SDimitry Andric using namespace llvm;
23e8d8bef9SDimitry Andric 
24e8d8bef9SDimitry Andric #define DEBUG_TYPE "arm-block-placement"
25e8d8bef9SDimitry Andric #define DEBUG_PREFIX "ARM Block Placement: "
26e8d8bef9SDimitry Andric 
27e8d8bef9SDimitry Andric namespace llvm {
28e8d8bef9SDimitry Andric class ARMBlockPlacement : public MachineFunctionPass {
29e8d8bef9SDimitry Andric private:
30e8d8bef9SDimitry Andric   const ARMBaseInstrInfo *TII;
31e8d8bef9SDimitry Andric   std::unique_ptr<ARMBasicBlockUtils> BBUtils = nullptr;
32e8d8bef9SDimitry Andric   MachineLoopInfo *MLI = nullptr;
33e8d8bef9SDimitry Andric 
34e8d8bef9SDimitry Andric public:
35e8d8bef9SDimitry Andric   static char ID;
36e8d8bef9SDimitry Andric   ARMBlockPlacement() : MachineFunctionPass(ID) {}
37e8d8bef9SDimitry Andric 
38e8d8bef9SDimitry Andric   bool runOnMachineFunction(MachineFunction &MF) override;
39e8d8bef9SDimitry Andric   void moveBasicBlock(MachineBasicBlock *BB, MachineBasicBlock *After);
40e8d8bef9SDimitry Andric   bool blockIsBefore(MachineBasicBlock *BB, MachineBasicBlock *Other);
41e8d8bef9SDimitry Andric 
42e8d8bef9SDimitry Andric   void getAnalysisUsage(AnalysisUsage &AU) const override {
43e8d8bef9SDimitry Andric     AU.setPreservesCFG();
44e8d8bef9SDimitry Andric     AU.addRequired<MachineLoopInfo>();
45e8d8bef9SDimitry Andric     MachineFunctionPass::getAnalysisUsage(AU);
46e8d8bef9SDimitry Andric   }
47e8d8bef9SDimitry Andric };
48e8d8bef9SDimitry Andric 
49e8d8bef9SDimitry Andric } // namespace llvm
50e8d8bef9SDimitry Andric 
51e8d8bef9SDimitry Andric FunctionPass *llvm::createARMBlockPlacementPass() {
52e8d8bef9SDimitry Andric   return new ARMBlockPlacement();
53e8d8bef9SDimitry Andric }
54e8d8bef9SDimitry Andric 
55e8d8bef9SDimitry Andric char ARMBlockPlacement::ID = 0;
56e8d8bef9SDimitry Andric 
57e8d8bef9SDimitry Andric INITIALIZE_PASS(ARMBlockPlacement, DEBUG_TYPE, "ARM block placement", false,
58e8d8bef9SDimitry Andric                 false)
59e8d8bef9SDimitry Andric 
60e8d8bef9SDimitry Andric bool ARMBlockPlacement::runOnMachineFunction(MachineFunction &MF) {
61e8d8bef9SDimitry Andric   if (skipFunction(MF.getFunction()))
62e8d8bef9SDimitry Andric       return false;
63e8d8bef9SDimitry Andric   const ARMSubtarget &ST = static_cast<const ARMSubtarget &>(MF.getSubtarget());
64e8d8bef9SDimitry Andric   if (!ST.hasLOB())
65e8d8bef9SDimitry Andric     return false;
66e8d8bef9SDimitry Andric   LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Running on " << MF.getName() << "\n");
67e8d8bef9SDimitry Andric   MLI = &getAnalysis<MachineLoopInfo>();
68e8d8bef9SDimitry Andric   TII = static_cast<const ARMBaseInstrInfo *>(ST.getInstrInfo());
69e8d8bef9SDimitry Andric   BBUtils = std::unique_ptr<ARMBasicBlockUtils>(new ARMBasicBlockUtils(MF));
70e8d8bef9SDimitry Andric   MF.RenumberBlocks();
71e8d8bef9SDimitry Andric   BBUtils->computeAllBlockSizes();
72e8d8bef9SDimitry Andric   BBUtils->adjustBBOffsetsAfter(&MF.front());
73e8d8bef9SDimitry Andric   bool Changed = false;
74e8d8bef9SDimitry Andric 
75e8d8bef9SDimitry Andric   // Find loops with a backwards branching WLS.
76e8d8bef9SDimitry Andric   // This requires looping over the loops in the function, checking each
77e8d8bef9SDimitry Andric   // preheader for a WLS and if its target is before the preheader. If moving
78e8d8bef9SDimitry Andric   // the target block wouldn't produce another backwards WLS or a new forwards
79e8d8bef9SDimitry Andric   // LE branch then move the target block after the preheader.
80e8d8bef9SDimitry Andric   for (auto *ML : *MLI) {
81e8d8bef9SDimitry Andric     MachineBasicBlock *Preheader = ML->getLoopPredecessor();
82e8d8bef9SDimitry Andric     if (!Preheader)
83e8d8bef9SDimitry Andric       continue;
84e8d8bef9SDimitry Andric 
85e8d8bef9SDimitry Andric     for (auto &Terminator : Preheader->terminators()) {
86e8d8bef9SDimitry Andric       if (Terminator.getOpcode() != ARM::t2WhileLoopStart)
87e8d8bef9SDimitry Andric         continue;
88e8d8bef9SDimitry Andric       MachineBasicBlock *LoopExit = Terminator.getOperand(1).getMBB();
89e8d8bef9SDimitry Andric       // We don't want to move the function's entry block.
90e8d8bef9SDimitry Andric       if (!LoopExit->getPrevNode())
91e8d8bef9SDimitry Andric         continue;
92e8d8bef9SDimitry Andric       if (blockIsBefore(Preheader, LoopExit))
93e8d8bef9SDimitry Andric         continue;
94e8d8bef9SDimitry Andric       LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Found a backwards WLS from "
95e8d8bef9SDimitry Andric                         << Preheader->getFullName() << " to "
96e8d8bef9SDimitry Andric                         << LoopExit->getFullName() << "\n");
97e8d8bef9SDimitry Andric 
98e8d8bef9SDimitry Andric       // Make sure that moving the target block doesn't cause any of its WLSs
99e8d8bef9SDimitry Andric       // that were previously not backwards to become backwards
100e8d8bef9SDimitry Andric       bool CanMove = true;
101e8d8bef9SDimitry Andric       for (auto &LoopExitTerminator : LoopExit->terminators()) {
102e8d8bef9SDimitry Andric         if (LoopExitTerminator.getOpcode() != ARM::t2WhileLoopStart)
103e8d8bef9SDimitry Andric           continue;
104e8d8bef9SDimitry Andric         // An example loop structure where the LoopExit can't be moved, since
105e8d8bef9SDimitry Andric         // bb1's WLS will become backwards once it's moved after bb3 bb1: -
106e8d8bef9SDimitry Andric         // LoopExit
107e8d8bef9SDimitry Andric         //      WLS bb2  - LoopExit2
108e8d8bef9SDimitry Andric         // bb2:
109e8d8bef9SDimitry Andric         //      ...
110e8d8bef9SDimitry Andric         // bb3:          - Preheader
111e8d8bef9SDimitry Andric         //      WLS bb1
112e8d8bef9SDimitry Andric         // bb4:          - Header
113e8d8bef9SDimitry Andric         MachineBasicBlock *LoopExit2 =
114e8d8bef9SDimitry Andric             LoopExitTerminator.getOperand(1).getMBB();
115e8d8bef9SDimitry Andric         // If the WLS from LoopExit to LoopExit2 is already backwards then
116e8d8bef9SDimitry Andric         // moving LoopExit won't affect it, so it can be moved. If LoopExit2 is
117e8d8bef9SDimitry Andric         // after the Preheader then moving will keep it as a forward branch, so
118e8d8bef9SDimitry Andric         // it can be moved. If LoopExit2 is between the Preheader and LoopExit
119e8d8bef9SDimitry Andric         // then moving LoopExit will make it a backwards branch, so it can't be
120e8d8bef9SDimitry Andric         // moved since we'd fix one and introduce one backwards branch.
121e8d8bef9SDimitry Andric         // TODO: Analyse the blocks to make a decision if it would be worth
122e8d8bef9SDimitry Andric         // moving LoopExit even if LoopExit2 is between the Preheader and
123e8d8bef9SDimitry Andric         // LoopExit.
124e8d8bef9SDimitry Andric         if (!blockIsBefore(LoopExit2, LoopExit) &&
125e8d8bef9SDimitry Andric             (LoopExit2 == Preheader || blockIsBefore(LoopExit2, Preheader))) {
126e8d8bef9SDimitry Andric           LLVM_DEBUG(dbgs() << DEBUG_PREFIX
127e8d8bef9SDimitry Andric                             << "Can't move the target block as it would "
128e8d8bef9SDimitry Andric                                "introduce a new backwards WLS branch\n");
129e8d8bef9SDimitry Andric           CanMove = false;
130e8d8bef9SDimitry Andric           break;
131e8d8bef9SDimitry Andric         }
132e8d8bef9SDimitry Andric       }
133e8d8bef9SDimitry Andric 
134e8d8bef9SDimitry Andric       if (CanMove) {
135e8d8bef9SDimitry Andric         // Make sure no LEs become forwards.
136e8d8bef9SDimitry Andric         // An example loop structure where the LoopExit can't be moved, since
137e8d8bef9SDimitry Andric         // bb2's LE will become forwards once bb1 is moved after bb3.
138e8d8bef9SDimitry Andric         // bb1:           - LoopExit
139e8d8bef9SDimitry Andric         // bb2:
140e8d8bef9SDimitry Andric         //      LE  bb1  - Terminator
141e8d8bef9SDimitry Andric         // bb3:          - Preheader
142e8d8bef9SDimitry Andric         //      WLS bb1
143e8d8bef9SDimitry Andric         // bb4:          - Header
144e8d8bef9SDimitry Andric         for (auto It = LoopExit->getIterator(); It != Preheader->getIterator();
145e8d8bef9SDimitry Andric              It++) {
146e8d8bef9SDimitry Andric           MachineBasicBlock *MBB = &*It;
147e8d8bef9SDimitry Andric           for (auto &Terminator : MBB->terminators()) {
148*4652422eSDimitry Andric             if (Terminator.getOpcode() != ARM::t2LoopEndDec)
149e8d8bef9SDimitry Andric               continue;
150e8d8bef9SDimitry Andric             MachineBasicBlock *LETarget = Terminator.getOperand(2).getMBB();
151e8d8bef9SDimitry Andric             // The LE will become forwards branching if it branches to LoopExit
152e8d8bef9SDimitry Andric             // which isn't allowed by the architecture, so we should avoid
153e8d8bef9SDimitry Andric             // introducing these.
154e8d8bef9SDimitry Andric             // TODO: Analyse the blocks to make a decision if it would be worth
155e8d8bef9SDimitry Andric             // moving LoopExit even if we'd introduce a forwards LE
156e8d8bef9SDimitry Andric             if (LETarget == LoopExit) {
157e8d8bef9SDimitry Andric               LLVM_DEBUG(dbgs() << DEBUG_PREFIX
158e8d8bef9SDimitry Andric                                 << "Can't move the target block as it would "
159e8d8bef9SDimitry Andric                                    "introduce a new forwards LE branch\n");
160e8d8bef9SDimitry Andric               CanMove = false;
161e8d8bef9SDimitry Andric               break;
162e8d8bef9SDimitry Andric             }
163e8d8bef9SDimitry Andric           }
164e8d8bef9SDimitry Andric         }
165e8d8bef9SDimitry Andric 
166e8d8bef9SDimitry Andric         if (!CanMove)
167e8d8bef9SDimitry Andric           break;
168e8d8bef9SDimitry Andric       }
169e8d8bef9SDimitry Andric 
170e8d8bef9SDimitry Andric       if (CanMove) {
171e8d8bef9SDimitry Andric         moveBasicBlock(LoopExit, Preheader);
172e8d8bef9SDimitry Andric         Changed = true;
173e8d8bef9SDimitry Andric         break;
174e8d8bef9SDimitry Andric       }
175e8d8bef9SDimitry Andric     }
176e8d8bef9SDimitry Andric   }
177e8d8bef9SDimitry Andric 
178e8d8bef9SDimitry Andric   return Changed;
179e8d8bef9SDimitry Andric }
180e8d8bef9SDimitry Andric 
181e8d8bef9SDimitry Andric bool ARMBlockPlacement::blockIsBefore(MachineBasicBlock *BB,
182e8d8bef9SDimitry Andric                                       MachineBasicBlock *Other) {
183e8d8bef9SDimitry Andric   return BBUtils->getOffsetOf(Other) > BBUtils->getOffsetOf(BB);
184e8d8bef9SDimitry Andric }
185e8d8bef9SDimitry Andric 
186e8d8bef9SDimitry Andric void ARMBlockPlacement::moveBasicBlock(MachineBasicBlock *BB,
187e8d8bef9SDimitry Andric                                        MachineBasicBlock *After) {
188e8d8bef9SDimitry Andric   LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Moving " << BB->getName() << " after "
189e8d8bef9SDimitry Andric                     << After->getName() << "\n");
190e8d8bef9SDimitry Andric   MachineBasicBlock *BBPrevious = BB->getPrevNode();
191e8d8bef9SDimitry Andric   assert(BBPrevious && "Cannot move the function entry basic block");
192e8d8bef9SDimitry Andric   MachineBasicBlock *AfterNext = After->getNextNode();
193e8d8bef9SDimitry Andric   MachineBasicBlock *BBNext = BB->getNextNode();
194e8d8bef9SDimitry Andric 
195e8d8bef9SDimitry Andric   BB->moveAfter(After);
196e8d8bef9SDimitry Andric 
197e8d8bef9SDimitry Andric   auto FixFallthrough = [&](MachineBasicBlock *From, MachineBasicBlock *To) {
198e8d8bef9SDimitry Andric     LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Checking for fallthrough from "
199e8d8bef9SDimitry Andric                       << From->getName() << " to " << To->getName() << "\n");
200e8d8bef9SDimitry Andric     assert(From->isSuccessor(To) &&
201e8d8bef9SDimitry Andric            "'To' is expected to be a successor of 'From'");
202e8d8bef9SDimitry Andric     MachineInstr &Terminator = *(--From->terminators().end());
203e8d8bef9SDimitry Andric     if (!Terminator.isUnconditionalBranch()) {
204e8d8bef9SDimitry Andric       // The BB doesn't have an unconditional branch so it relied on
205e8d8bef9SDimitry Andric       // fall-through. Fix by adding an unconditional branch to the moved BB.
206e8d8bef9SDimitry Andric       MachineInstrBuilder MIB =
207*4652422eSDimitry Andric           BuildMI(From, Terminator.getDebugLoc(), TII->get(ARM::t2B));
208e8d8bef9SDimitry Andric       MIB.addMBB(To);
209e8d8bef9SDimitry Andric       MIB.addImm(ARMCC::CondCodes::AL);
210e8d8bef9SDimitry Andric       MIB.addReg(ARM::NoRegister);
211e8d8bef9SDimitry Andric       LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Adding unconditional branch from "
212e8d8bef9SDimitry Andric                         << From->getName() << " to " << To->getName() << ": "
213e8d8bef9SDimitry Andric                         << *MIB.getInstr());
214e8d8bef9SDimitry Andric     }
215e8d8bef9SDimitry Andric   };
216e8d8bef9SDimitry Andric 
217e8d8bef9SDimitry Andric   // Fix fall-through to the moved BB from the one that used to be before it.
218e8d8bef9SDimitry Andric   if (BBPrevious->isSuccessor(BB))
219e8d8bef9SDimitry Andric     FixFallthrough(BBPrevious, BB);
220e8d8bef9SDimitry Andric   // Fix fall through from the destination BB to the one that used to follow.
221e8d8bef9SDimitry Andric   if (AfterNext && After->isSuccessor(AfterNext))
222e8d8bef9SDimitry Andric     FixFallthrough(After, AfterNext);
223e8d8bef9SDimitry Andric   // Fix fall through from the moved BB to the one that used to follow.
224e8d8bef9SDimitry Andric   if (BBNext && BB->isSuccessor(BBNext))
225e8d8bef9SDimitry Andric     FixFallthrough(BB, BBNext);
226e8d8bef9SDimitry Andric 
227e8d8bef9SDimitry Andric   BBUtils->adjustBBOffsetsAfter(After);
228e8d8bef9SDimitry Andric }
229