|  | //===---- HexagonFixupHwLoops.cpp - Fixup HW loops too far from LOOPn. ----===// | 
|  | // | 
|  | // 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 | 
|  | // | 
|  | // The loop start address in the LOOPn instruction is encoded as a distance | 
|  | // from the LOOPn instruction itself. If the start address is too far from | 
|  | // the LOOPn instruction, the instruction needs to use a constant extender. | 
|  | // This pass will identify and convert such LOOPn instructions to a proper | 
|  | // form. | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "Hexagon.h" | 
|  | #include "HexagonTargetMachine.h" | 
|  | #include "llvm/ADT/DenseMap.h" | 
|  | #include "llvm/CodeGen/MachineFunction.h" | 
|  | #include "llvm/CodeGen/MachineFunctionPass.h" | 
|  | #include "llvm/CodeGen/MachineInstrBuilder.h" | 
|  | #include "llvm/CodeGen/Passes.h" | 
|  | #include "llvm/CodeGen/TargetInstrInfo.h" | 
|  | #include "llvm/Support/MathExtras.h" | 
|  | #include "llvm/Pass.h" | 
|  |  | 
|  | using namespace llvm; | 
|  |  | 
|  | static cl::opt<unsigned> MaxLoopRange( | 
|  | "hexagon-loop-range", cl::Hidden, cl::init(200), | 
|  | cl::desc("Restrict range of loopN instructions (testing only)")); | 
|  |  | 
|  | namespace llvm { | 
|  | FunctionPass *createHexagonFixupHwLoops(); | 
|  | void initializeHexagonFixupHwLoopsPass(PassRegistry&); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | struct HexagonFixupHwLoops : public MachineFunctionPass { | 
|  | public: | 
|  | static char ID; | 
|  |  | 
|  | HexagonFixupHwLoops() : MachineFunctionPass(ID) { | 
|  | initializeHexagonFixupHwLoopsPass(*PassRegistry::getPassRegistry()); | 
|  | } | 
|  |  | 
|  | bool runOnMachineFunction(MachineFunction &MF) override; | 
|  |  | 
|  | MachineFunctionProperties getRequiredProperties() const override { | 
|  | return MachineFunctionProperties().set( | 
|  | MachineFunctionProperties::Property::NoVRegs); | 
|  | } | 
|  |  | 
|  | StringRef getPassName() const override { | 
|  | return "Hexagon Hardware Loop Fixup"; | 
|  | } | 
|  |  | 
|  | void getAnalysisUsage(AnalysisUsage &AU) const override { | 
|  | AU.setPreservesCFG(); | 
|  | MachineFunctionPass::getAnalysisUsage(AU); | 
|  | } | 
|  |  | 
|  | private: | 
|  | /// Check the offset between each loop instruction and | 
|  | /// the loop basic block to determine if we can use the LOOP instruction | 
|  | /// or if we need to set the LC/SA registers explicitly. | 
|  | bool fixupLoopInstrs(MachineFunction &MF); | 
|  |  | 
|  | /// Replace loop instruction with the constant extended | 
|  | /// version if the loop label is too far from the loop instruction. | 
|  | void useExtLoopInstr(MachineFunction &MF, | 
|  | MachineBasicBlock::iterator &MII); | 
|  | }; | 
|  |  | 
|  | char HexagonFixupHwLoops::ID = 0; | 
|  | } | 
|  |  | 
|  | INITIALIZE_PASS(HexagonFixupHwLoops, "hwloopsfixup", | 
|  | "Hexagon Hardware Loops Fixup", false, false) | 
|  |  | 
|  | FunctionPass *llvm::createHexagonFixupHwLoops() { | 
|  | return new HexagonFixupHwLoops(); | 
|  | } | 
|  |  | 
|  | /// Returns true if the instruction is a hardware loop instruction. | 
|  | static bool isHardwareLoop(const MachineInstr &MI) { | 
|  | return MI.getOpcode() == Hexagon::J2_loop0r || | 
|  | MI.getOpcode() == Hexagon::J2_loop0i || | 
|  | MI.getOpcode() == Hexagon::J2_loop1r || | 
|  | MI.getOpcode() == Hexagon::J2_loop1i; | 
|  | } | 
|  |  | 
|  | bool HexagonFixupHwLoops::runOnMachineFunction(MachineFunction &MF) { | 
|  | if (skipFunction(MF.getFunction())) | 
|  | return false; | 
|  | return fixupLoopInstrs(MF); | 
|  | } | 
|  |  | 
|  | /// For Hexagon, if the loop label is to far from the | 
|  | /// loop instruction then we need to set the LC0 and SA0 registers | 
|  | /// explicitly instead of using LOOP(start,count).  This function | 
|  | /// checks the distance, and generates register assignments if needed. | 
|  | /// | 
|  | /// This function makes two passes over the basic blocks.  The first | 
|  | /// pass computes the offset of the basic block from the start. | 
|  | /// The second pass checks all the loop instructions. | 
|  | bool HexagonFixupHwLoops::fixupLoopInstrs(MachineFunction &MF) { | 
|  |  | 
|  | // Offset of the current instruction from the start. | 
|  | unsigned InstOffset = 0; | 
|  | // Map for each basic block to it's first instruction. | 
|  | DenseMap<const MachineBasicBlock *, unsigned> BlockToInstOffset; | 
|  |  | 
|  | const HexagonInstrInfo *HII = | 
|  | static_cast<const HexagonInstrInfo *>(MF.getSubtarget().getInstrInfo()); | 
|  |  | 
|  | // First pass - compute the offset of each basic block. | 
|  | for (const MachineBasicBlock &MBB : MF) { | 
|  | if (MBB.getAlignment() != Align(1)) { | 
|  | // Although we don't know the exact layout of the final code, we need | 
|  | // to account for alignment padding somehow. This heuristic pads each | 
|  | // aligned basic block according to the alignment value. | 
|  | InstOffset = alignTo(InstOffset, MBB.getAlignment()); | 
|  | } | 
|  |  | 
|  | BlockToInstOffset[&MBB] = InstOffset; | 
|  | for (const MachineInstr &MI : MBB) | 
|  | InstOffset += HII->getSize(MI); | 
|  | } | 
|  |  | 
|  | // Second pass - check each loop instruction to see if it needs to be | 
|  | // converted. | 
|  | bool Changed = false; | 
|  | for (MachineBasicBlock &MBB : MF) { | 
|  | InstOffset = BlockToInstOffset[&MBB]; | 
|  |  | 
|  | // Loop over all the instructions. | 
|  | MachineBasicBlock::iterator MII = MBB.begin(); | 
|  | MachineBasicBlock::iterator MIE = MBB.end(); | 
|  | while (MII != MIE) { | 
|  | unsigned InstSize = HII->getSize(*MII); | 
|  | if (MII->isMetaInstruction()) { | 
|  | ++MII; | 
|  | continue; | 
|  | } | 
|  | if (isHardwareLoop(*MII)) { | 
|  | assert(MII->getOperand(0).isMBB() && | 
|  | "Expect a basic block as loop operand"); | 
|  | MachineBasicBlock *TargetBB = MII->getOperand(0).getMBB(); | 
|  | unsigned Diff = AbsoluteDifference(InstOffset, | 
|  | BlockToInstOffset[TargetBB]); | 
|  | if (Diff > MaxLoopRange) { | 
|  | useExtLoopInstr(MF, MII); | 
|  | MII = MBB.erase(MII); | 
|  | Changed = true; | 
|  | } else { | 
|  | ++MII; | 
|  | } | 
|  | } else { | 
|  | ++MII; | 
|  | } | 
|  | InstOffset += InstSize; | 
|  | } | 
|  | } | 
|  |  | 
|  | return Changed; | 
|  | } | 
|  |  | 
|  | /// Replace loop instructions with the constant extended version. | 
|  | void HexagonFixupHwLoops::useExtLoopInstr(MachineFunction &MF, | 
|  | MachineBasicBlock::iterator &MII) { | 
|  | const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); | 
|  | MachineBasicBlock *MBB = MII->getParent(); | 
|  | DebugLoc DL = MII->getDebugLoc(); | 
|  | MachineInstrBuilder MIB; | 
|  | unsigned newOp; | 
|  | switch (MII->getOpcode()) { | 
|  | case Hexagon::J2_loop0r: | 
|  | newOp = Hexagon::J2_loop0rext; | 
|  | break; | 
|  | case Hexagon::J2_loop0i: | 
|  | newOp = Hexagon::J2_loop0iext; | 
|  | break; | 
|  | case Hexagon::J2_loop1r: | 
|  | newOp = Hexagon::J2_loop1rext; | 
|  | break; | 
|  | case Hexagon::J2_loop1i: | 
|  | newOp = Hexagon::J2_loop1iext; | 
|  | break; | 
|  | default: | 
|  | llvm_unreachable("Invalid Hardware Loop Instruction."); | 
|  | } | 
|  | MIB = BuildMI(*MBB, MII, DL, TII->get(newOp)); | 
|  |  | 
|  | for (unsigned i = 0; i < MII->getNumOperands(); ++i) | 
|  | MIB.add(MII->getOperand(i)); | 
|  | } |