//===-- ARMBlockPlacement.cpp - ARM block placement pass ------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This pass re-arranges machine basic blocks to suit target requirements. // Currently it only moves blocks to fix backwards WLS branches. // //===----------------------------------------------------------------------===// #include "ARM.h" #include "ARMBaseInstrInfo.h" #include "ARMBasicBlockInfo.h" #include "ARMSubtarget.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineLoopInfo.h" using namespace llvm; #define DEBUG_TYPE "arm-block-placement" #define DEBUG_PREFIX "ARM Block Placement: " namespace llvm { class ARMBlockPlacement : public MachineFunctionPass { private: const ARMBaseInstrInfo *TII; std::unique_ptr BBUtils = nullptr; MachineLoopInfo *MLI = nullptr; public: static char ID; ARMBlockPlacement() : MachineFunctionPass(ID) {} bool runOnMachineFunction(MachineFunction &MF) override; void moveBasicBlock(MachineBasicBlock *BB, MachineBasicBlock *After); bool blockIsBefore(MachineBasicBlock *BB, MachineBasicBlock *Other); void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesCFG(); AU.addRequired(); MachineFunctionPass::getAnalysisUsage(AU); } }; } // namespace llvm FunctionPass *llvm::createARMBlockPlacementPass() { return new ARMBlockPlacement(); } char ARMBlockPlacement::ID = 0; INITIALIZE_PASS(ARMBlockPlacement, DEBUG_TYPE, "ARM block placement", false, false) bool ARMBlockPlacement::runOnMachineFunction(MachineFunction &MF) { if (skipFunction(MF.getFunction())) return false; const ARMSubtarget &ST = static_cast(MF.getSubtarget()); if (!ST.hasLOB()) return false; LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Running on " << MF.getName() << "\n"); MLI = &getAnalysis(); TII = static_cast(ST.getInstrInfo()); BBUtils = std::unique_ptr(new ARMBasicBlockUtils(MF)); MF.RenumberBlocks(); BBUtils->computeAllBlockSizes(); BBUtils->adjustBBOffsetsAfter(&MF.front()); bool Changed = false; // Find loops with a backwards branching WLS. // This requires looping over the loops in the function, checking each // preheader for a WLS and if its target is before the preheader. If moving // the target block wouldn't produce another backwards WLS or a new forwards // LE branch then move the target block after the preheader. for (auto *ML : *MLI) { MachineBasicBlock *Preheader = ML->getLoopPredecessor(); if (!Preheader) continue; for (auto &Terminator : Preheader->terminators()) { if (Terminator.getOpcode() != ARM::t2WhileLoopStart) continue; MachineBasicBlock *LoopExit = Terminator.getOperand(1).getMBB(); // We don't want to move the function's entry block. if (!LoopExit->getPrevNode()) continue; if (blockIsBefore(Preheader, LoopExit)) continue; LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Found a backwards WLS from " << Preheader->getFullName() << " to " << LoopExit->getFullName() << "\n"); // Make sure that moving the target block doesn't cause any of its WLSs // that were previously not backwards to become backwards bool CanMove = true; for (auto &LoopExitTerminator : LoopExit->terminators()) { if (LoopExitTerminator.getOpcode() != ARM::t2WhileLoopStart) continue; // An example loop structure where the LoopExit can't be moved, since // bb1's WLS will become backwards once it's moved after bb3 bb1: - // LoopExit // WLS bb2 - LoopExit2 // bb2: // ... // bb3: - Preheader // WLS bb1 // bb4: - Header MachineBasicBlock *LoopExit2 = LoopExitTerminator.getOperand(1).getMBB(); // If the WLS from LoopExit to LoopExit2 is already backwards then // moving LoopExit won't affect it, so it can be moved. If LoopExit2 is // after the Preheader then moving will keep it as a forward branch, so // it can be moved. If LoopExit2 is between the Preheader and LoopExit // then moving LoopExit will make it a backwards branch, so it can't be // moved since we'd fix one and introduce one backwards branch. // TODO: Analyse the blocks to make a decision if it would be worth // moving LoopExit even if LoopExit2 is between the Preheader and // LoopExit. if (!blockIsBefore(LoopExit2, LoopExit) && (LoopExit2 == Preheader || blockIsBefore(LoopExit2, Preheader))) { LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Can't move the target block as it would " "introduce a new backwards WLS branch\n"); CanMove = false; break; } } if (CanMove) { // Make sure no LEs become forwards. // An example loop structure where the LoopExit can't be moved, since // bb2's LE will become forwards once bb1 is moved after bb3. // bb1: - LoopExit // bb2: // LE bb1 - Terminator // bb3: - Preheader // WLS bb1 // bb4: - Header for (auto It = LoopExit->getIterator(); It != Preheader->getIterator(); It++) { MachineBasicBlock *MBB = &*It; for (auto &Terminator : MBB->terminators()) { if (Terminator.getOpcode() != ARM::t2LoopEnd && Terminator.getOpcode() != ARM::t2LoopEndDec) continue; MachineBasicBlock *LETarget = Terminator.getOperand(2).getMBB(); // The LE will become forwards branching if it branches to LoopExit // which isn't allowed by the architecture, so we should avoid // introducing these. // TODO: Analyse the blocks to make a decision if it would be worth // moving LoopExit even if we'd introduce a forwards LE if (LETarget == LoopExit) { LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Can't move the target block as it would " "introduce a new forwards LE branch\n"); CanMove = false; break; } } } if (!CanMove) break; } if (CanMove) { moveBasicBlock(LoopExit, Preheader); Changed = true; break; } } } return Changed; } bool ARMBlockPlacement::blockIsBefore(MachineBasicBlock *BB, MachineBasicBlock *Other) { return BBUtils->getOffsetOf(Other) > BBUtils->getOffsetOf(BB); } void ARMBlockPlacement::moveBasicBlock(MachineBasicBlock *BB, MachineBasicBlock *After) { LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Moving " << BB->getName() << " after " << After->getName() << "\n"); MachineBasicBlock *BBPrevious = BB->getPrevNode(); assert(BBPrevious && "Cannot move the function entry basic block"); MachineBasicBlock *AfterNext = After->getNextNode(); MachineBasicBlock *BBNext = BB->getNextNode(); BB->moveAfter(After); auto FixFallthrough = [&](MachineBasicBlock *From, MachineBasicBlock *To) { LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Checking for fallthrough from " << From->getName() << " to " << To->getName() << "\n"); assert(From->isSuccessor(To) && "'To' is expected to be a successor of 'From'"); MachineInstr &Terminator = *(--From->terminators().end()); if (!Terminator.isUnconditionalBranch()) { // The BB doesn't have an unconditional branch so it relied on // fall-through. Fix by adding an unconditional branch to the moved BB. unsigned BrOpc = BBUtils->isBBInRange(&Terminator, To, 254) ? ARM::tB : ARM::t2B; MachineInstrBuilder MIB = BuildMI(From, Terminator.getDebugLoc(), TII->get(BrOpc)); MIB.addMBB(To); MIB.addImm(ARMCC::CondCodes::AL); MIB.addReg(ARM::NoRegister); LLVM_DEBUG(dbgs() << DEBUG_PREFIX << "Adding unconditional branch from " << From->getName() << " to " << To->getName() << ": " << *MIB.getInstr()); } }; // Fix fall-through to the moved BB from the one that used to be before it. if (BBPrevious->isSuccessor(BB)) FixFallthrough(BBPrevious, BB); // Fix fall through from the destination BB to the one that used to follow. if (AfterNext && After->isSuccessor(AfterNext)) FixFallthrough(After, AfterNext); // Fix fall through from the moved BB to the one that used to follow. if (BBNext && BB->isSuccessor(BBNext)) FixFallthrough(BB, BBNext); BBUtils->adjustBBOffsetsAfter(After); }