| 1 | //===-- ARMBlockPlacement.cpp - ARM block placement pass ------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This pass re-arranges machine basic blocks to suit target requirements. |
| 10 | // Currently it only moves blocks to fix backwards WLS branches. |
| 11 | // |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #include "ARM.h" |
| 15 | #include "ARMBaseInstrInfo.h" |
| 16 | #include "ARMBasicBlockInfo.h" |
| 17 | #include "ARMSubtarget.h" |
| 18 | #include "MVETailPredUtils.h" |
| 19 | #include "llvm/CodeGen/LivePhysRegs.h" |
| 20 | #include "llvm/CodeGen/MachineFunctionPass.h" |
| 21 | #include "llvm/CodeGen/MachineInstrBuilder.h" |
| 22 | #include "llvm/CodeGen/MachineLoopInfo.h" |
| 23 | |
| 24 | using namespace llvm; |
| 25 | |
| 26 | #define DEBUG_TYPE "arm-block-placement" |
| 27 | #define DEBUG_PREFIX "ARM Block Placement: " |
| 28 | |
| 29 | namespace llvm { |
| 30 | class ARMBlockPlacement : public MachineFunctionPass { |
| 31 | private: |
| 32 | const ARMBaseInstrInfo *TII; |
| 33 | std::unique_ptr<ARMBasicBlockUtils> BBUtils = nullptr; |
| 34 | MachineLoopInfo *MLI = nullptr; |
| 35 | // A list of WLS instructions that need to be reverted to DLS. |
| 36 | SmallVector<MachineInstr *> RevertedWhileLoops; |
| 37 | |
| 38 | public: |
| 39 | static char ID; |
| 40 | ARMBlockPlacement() : MachineFunctionPass(ID) {} |
| 41 | |
| 42 | bool runOnMachineFunction(MachineFunction &MF) override; |
| 43 | void moveBasicBlock(MachineBasicBlock *BB, MachineBasicBlock *Before); |
| 44 | bool blockIsBefore(MachineBasicBlock *BB, MachineBasicBlock *Other); |
| 45 | bool fixBackwardsWLS(MachineLoop *ML); |
| 46 | bool processPostOrderLoops(MachineLoop *ML); |
| 47 | bool revertWhileToDoLoop(MachineInstr *WLS); |
| 48 | |
| 49 | void getAnalysisUsage(AnalysisUsage &AU) const override { |
| 50 | AU.addRequired<MachineLoopInfoWrapperPass>(); |
| 51 | MachineFunctionPass::getAnalysisUsage(AU); |
| 52 | } |
| 53 | }; |
| 54 | |
| 55 | } // namespace llvm |
| 56 | |
| 57 | FunctionPass *llvm::createARMBlockPlacementPass() { |
| 58 | return new ARMBlockPlacement(); |
| 59 | } |
| 60 | |
| 61 | char ARMBlockPlacement::ID = 0; |
| 62 | |
| 63 | INITIALIZE_PASS(ARMBlockPlacement, DEBUG_TYPE, "ARM block placement" , false, |
| 64 | false) |
| 65 | |
| 66 | static MachineInstr *findWLSInBlock(MachineBasicBlock *MBB) { |
| 67 | for (auto &Terminator : MBB->terminators()) { |
| 68 | if (isWhileLoopStart(MI: Terminator)) |
| 69 | return &Terminator; |
| 70 | } |
| 71 | return nullptr; |
| 72 | } |
| 73 | |
| 74 | /// Find WhileLoopStart in the loop predecessor BB or otherwise in its only |
| 75 | /// predecessor. If found, returns (BB, WLS Instr) pair, otherwise a null pair. |
| 76 | static MachineInstr *findWLS(MachineLoop *ML) { |
| 77 | MachineBasicBlock *Predecessor = ML->getLoopPredecessor(); |
| 78 | if (!Predecessor) |
| 79 | return nullptr; |
| 80 | MachineInstr *WlsInstr = findWLSInBlock(MBB: Predecessor); |
| 81 | if (WlsInstr) |
| 82 | return WlsInstr; |
| 83 | if (Predecessor->pred_size() == 1) |
| 84 | return findWLSInBlock(MBB: *Predecessor->pred_begin()); |
| 85 | return nullptr; |
| 86 | } |
| 87 | |
| 88 | // Revert a WhileLoopStart to an equivalent DoLoopStart and branch. Note that |
| 89 | // because of the branches this requires an extra block to be created. |
| 90 | bool ARMBlockPlacement::revertWhileToDoLoop(MachineInstr *WLS) { |
| 91 | // lr = t2WhileLoopStartTP r0, r1, TgtBB |
| 92 | // t2Br Ph |
| 93 | // -> |
| 94 | // cmp r0, 0 |
| 95 | // brcc TgtBB |
| 96 | // block2: |
| 97 | // LR = t2DoLoopStartTP r0, r1 |
| 98 | // t2Br Ph |
| 99 | MachineBasicBlock * = WLS->getParent(); |
| 100 | assert(WLS != &Preheader->back()); |
| 101 | assert(WLS->getNextNode() == &Preheader->back()); |
| 102 | MachineInstr *Br = &Preheader->back(); |
| 103 | assert(Br->getOpcode() == ARM::t2B); |
| 104 | assert(Br->getOperand(1).getImm() == 14); |
| 105 | |
| 106 | // Clear the kill flags, as the cmp/bcc will no longer kill any operands. |
| 107 | WLS->getOperand(i: 1).setIsKill(false); |
| 108 | if (WLS->getOpcode() == ARM::t2WhileLoopStartTP) |
| 109 | WLS->getOperand(i: 2).setIsKill(false); |
| 110 | |
| 111 | // Create the new block |
| 112 | MachineBasicBlock *NewBlock = Preheader->getParent()->CreateMachineBasicBlock( |
| 113 | BB: Preheader->getBasicBlock()); |
| 114 | Preheader->getParent()->insert(MBBI: ++Preheader->getIterator(), MBB: NewBlock); |
| 115 | // Move the Br to it |
| 116 | Br->removeFromParent(); |
| 117 | NewBlock->insert(I: NewBlock->end(), MI: Br); |
| 118 | // And setup the successors correctly. |
| 119 | Preheader->replaceSuccessor(Old: Br->getOperand(i: 0).getMBB(), New: NewBlock); |
| 120 | NewBlock->addSuccessor(Succ: Br->getOperand(i: 0).getMBB()); |
| 121 | |
| 122 | // Create a new DLS to replace the WLS |
| 123 | MachineInstrBuilder MIB = |
| 124 | BuildMI(BB&: *NewBlock, I: Br, MIMD: WLS->getDebugLoc(), |
| 125 | MCID: TII->get(Opcode: WLS->getOpcode() == ARM::t2WhileLoopStartTP |
| 126 | ? ARM::t2DoLoopStartTP |
| 127 | : ARM::t2DoLoopStart)); |
| 128 | MIB.add(MO: WLS->getOperand(i: 0)); |
| 129 | MIB.add(MO: WLS->getOperand(i: 1)); |
| 130 | if (WLS->getOpcode() == ARM::t2WhileLoopStartTP) |
| 131 | MIB.add(MO: WLS->getOperand(i: 2)); |
| 132 | |
| 133 | LLVM_DEBUG(dbgs() << DEBUG_PREFIX |
| 134 | << "Reverting While Loop to Do Loop: " << *WLS << "\n" ); |
| 135 | |
| 136 | RevertWhileLoopStartLR(MI: WLS, TII, BrOpc: ARM::t2Bcc, UseCmp: true); |
| 137 | |
| 138 | LivePhysRegs LiveRegs; |
| 139 | computeAndAddLiveIns(LiveRegs, MBB&: *NewBlock); |
| 140 | |
| 141 | Preheader->getParent()->RenumberBlocks(); |
| 142 | BBUtils->computeAllBlockSizes(); |
| 143 | BBUtils->adjustBBOffsetsAfter(MBB: Preheader); |
| 144 | |
| 145 | return true; |
| 146 | } |
| 147 | |
| 148 | /// Checks if loop has a backwards branching WLS, and if possible, fixes it. |
| 149 | /// This requires checking the predecessor (ie. preheader or it's predecessor) |
| 150 | /// for a WLS and if its loopExit/target is before it. |
| 151 | /// If moving the predecessor won't convert a WLS (to the predecessor) from |
| 152 | /// a forward to a backward branching WLS, then move the predecessor block |
| 153 | /// to before the loopExit/target. |
| 154 | bool ARMBlockPlacement::fixBackwardsWLS(MachineLoop *ML) { |
| 155 | MachineInstr *WlsInstr = findWLS(ML); |
| 156 | if (!WlsInstr) |
| 157 | return false; |
| 158 | |
| 159 | MachineBasicBlock *Predecessor = WlsInstr->getParent(); |
| 160 | MachineBasicBlock *LoopExit = getWhileLoopStartTargetBB(MI: *WlsInstr); |
| 161 | |
| 162 | // We don't want to move Preheader to before the function's entry block. |
| 163 | if (!LoopExit->getPrevNode()) |
| 164 | return false; |
| 165 | if (blockIsBefore(BB: Predecessor, Other: LoopExit)) |
| 166 | return false; |
| 167 | LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Found a backwards WLS from " |
| 168 | << Predecessor->getFullName() << " to " |
| 169 | << LoopExit->getFullName() << "\n" ); |
| 170 | |
| 171 | // Make sure no forward branching WLSs to the Predecessor become backwards |
| 172 | // branching. An example loop structure where the Predecessor can't be moved, |
| 173 | // since bb2's WLS will become forwards once bb3 is moved before/above bb1. |
| 174 | // |
| 175 | // bb1: - LoopExit |
| 176 | // bb2: |
| 177 | // WLS bb3 |
| 178 | // bb3: - Predecessor |
| 179 | // WLS bb1 |
| 180 | // bb4: - Header |
| 181 | for (auto It = ++LoopExit->getIterator(); It != Predecessor->getIterator(); |
| 182 | ++It) { |
| 183 | MachineBasicBlock *MBB = &*It; |
| 184 | for (auto &Terminator : MBB->terminators()) { |
| 185 | if (!isWhileLoopStart(MI: Terminator)) |
| 186 | continue; |
| 187 | MachineBasicBlock *WLSTarget = getWhileLoopStartTargetBB(MI: Terminator); |
| 188 | // TODO: Analyse the blocks to make a decision if it would be worth |
| 189 | // moving Preheader even if we'd introduce a backwards WLS |
| 190 | if (WLSTarget == Predecessor) { |
| 191 | LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Can't move Predecessor block as " |
| 192 | << "it would convert a WLS from forward to a " |
| 193 | << "backwards branching WLS\n" ); |
| 194 | RevertedWhileLoops.push_back(Elt: WlsInstr); |
| 195 | return false; |
| 196 | } |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | moveBasicBlock(BB: Predecessor, Before: LoopExit); |
| 201 | return true; |
| 202 | } |
| 203 | |
| 204 | /// Updates ordering (of WLS BB and their loopExits) in inner loops first |
| 205 | /// Returns true if any change was made in any of the loops |
| 206 | bool ARMBlockPlacement::processPostOrderLoops(MachineLoop *ML) { |
| 207 | bool Changed = false; |
| 208 | for (auto *InnerML : *ML) |
| 209 | Changed |= processPostOrderLoops(ML: InnerML); |
| 210 | return Changed | fixBackwardsWLS(ML); |
| 211 | } |
| 212 | |
| 213 | bool ARMBlockPlacement::runOnMachineFunction(MachineFunction &MF) { |
| 214 | if (skipFunction(F: MF.getFunction())) |
| 215 | return false; |
| 216 | const ARMSubtarget &ST = MF.getSubtarget<ARMSubtarget>(); |
| 217 | if (!ST.hasLOB()) |
| 218 | return false; |
| 219 | LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Running on " << MF.getName() << "\n" ); |
| 220 | MLI = &getAnalysis<MachineLoopInfoWrapperPass>().getLI(); |
| 221 | TII = static_cast<const ARMBaseInstrInfo *>(ST.getInstrInfo()); |
| 222 | BBUtils = std::make_unique<ARMBasicBlockUtils>(args&: MF); |
| 223 | MF.RenumberBlocks(); |
| 224 | BBUtils->computeAllBlockSizes(); |
| 225 | BBUtils->adjustBBOffsetsAfter(MBB: &MF.front()); |
| 226 | bool Changed = false; |
| 227 | RevertedWhileLoops.clear(); |
| 228 | |
| 229 | // Find loops with a backwards branching WLS and fix if possible. |
| 230 | for (auto *ML : *MLI) |
| 231 | Changed |= processPostOrderLoops(ML); |
| 232 | |
| 233 | // Revert any While loops still out of range to DLS loops. |
| 234 | for (auto *WlsInstr : RevertedWhileLoops) |
| 235 | Changed |= revertWhileToDoLoop(WLS: WlsInstr); |
| 236 | |
| 237 | return Changed; |
| 238 | } |
| 239 | |
| 240 | bool ARMBlockPlacement::blockIsBefore(MachineBasicBlock *BB, |
| 241 | MachineBasicBlock *Other) { |
| 242 | return BBUtils->getOffsetOf(MBB: Other) > BBUtils->getOffsetOf(MBB: BB); |
| 243 | } |
| 244 | |
| 245 | // Moves a BasicBlock before another, without changing the control flow |
| 246 | void ARMBlockPlacement::moveBasicBlock(MachineBasicBlock *BB, |
| 247 | MachineBasicBlock *Before) { |
| 248 | LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Moving " << BB->getName() << " before " |
| 249 | << Before->getName() << "\n" ); |
| 250 | MachineBasicBlock *BBPrevious = BB->getPrevNode(); |
| 251 | assert(BBPrevious && "Cannot move the function entry basic block" ); |
| 252 | MachineBasicBlock *BBNext = BB->getNextNode(); |
| 253 | |
| 254 | MachineBasicBlock *BeforePrev = Before->getPrevNode(); |
| 255 | assert(BeforePrev && |
| 256 | "Cannot move the given block to before the function entry block" ); |
| 257 | MachineFunction *F = BB->getParent(); |
| 258 | BB->moveBefore(NewAfter: Before); |
| 259 | |
| 260 | // Since only the blocks are to be moved around (but the control flow must |
| 261 | // not change), if there were any fall-throughs (to/from adjacent blocks), |
| 262 | // replace with unconditional branch to the fall through block. |
| 263 | auto FixFallthrough = [&](MachineBasicBlock *From, MachineBasicBlock *To) { |
| 264 | LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Checking for fallthrough from " |
| 265 | << From->getName() << " to " << To->getName() << "\n" ); |
| 266 | assert(From->isSuccessor(To) && |
| 267 | "'To' is expected to be a successor of 'From'" ); |
| 268 | MachineInstr &Terminator = *(--From->terminators().end()); |
| 269 | if (!TII->isPredicated(MI: Terminator) && |
| 270 | (isUncondBranchOpcode(Opc: Terminator.getOpcode()) || |
| 271 | isIndirectBranchOpcode(Opc: Terminator.getOpcode()) || |
| 272 | isJumpTableBranchOpcode(Opc: Terminator.getOpcode()) || |
| 273 | Terminator.isReturn())) |
| 274 | return; |
| 275 | // The BB doesn't have an unconditional branch so it relied on |
| 276 | // fall-through. Fix by adding an unconditional branch to the moved BB. |
| 277 | MachineInstrBuilder MIB = |
| 278 | BuildMI(BB: From, MIMD: Terminator.getDebugLoc(), MCID: TII->get(Opcode: ARM::t2B)); |
| 279 | MIB.addMBB(MBB: To); |
| 280 | MIB.addImm(Val: ARMCC::CondCodes::AL); |
| 281 | MIB.addReg(RegNo: ARM::NoRegister); |
| 282 | LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Adding unconditional branch from " |
| 283 | << From->getName() << " to " << To->getName() << ": " |
| 284 | << *MIB.getInstr()); |
| 285 | }; |
| 286 | |
| 287 | // Fix fall-through to the moved BB from the one that used to be before it. |
| 288 | if (BBPrevious->isSuccessor(MBB: BB)) |
| 289 | FixFallthrough(BBPrevious, BB); |
| 290 | // Fix fall through from the destination BB to the one that used to before it. |
| 291 | if (BeforePrev->isSuccessor(MBB: Before)) |
| 292 | FixFallthrough(BeforePrev, Before); |
| 293 | // Fix fall through from the moved BB to the one that used to follow. |
| 294 | if (BBNext && BB->isSuccessor(MBB: BBNext)) |
| 295 | FixFallthrough(BB, BBNext); |
| 296 | |
| 297 | F->RenumberBlocks(); |
| 298 | BBUtils->computeAllBlockSizes(); |
| 299 | BBUtils->adjustBBOffsetsAfter(MBB: BB); |
| 300 | } |
| 301 | |