xref: /freebsd/contrib/llvm-project/llvm/lib/Target/Hexagon/HexagonFixupHwLoops.cpp (revision 5ffd83dbcc34f10e07f6d3e968ae6365869615f4)
10b57cec5SDimitry Andric //===---- HexagonFixupHwLoops.cpp - Fixup HW loops too far from LOOPn. ----===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric // The loop start address in the LOOPn instruction is encoded as a distance
80b57cec5SDimitry Andric // from the LOOPn instruction itself. If the start address is too far from
90b57cec5SDimitry Andric // the LOOPn instruction, the instruction needs to use a constant extender.
100b57cec5SDimitry Andric // This pass will identify and convert such LOOPn instructions to a proper
110b57cec5SDimitry Andric // form.
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "Hexagon.h"
150b57cec5SDimitry Andric #include "HexagonTargetMachine.h"
160b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h"
170b57cec5SDimitry Andric #include "llvm/CodeGen/MachineFunction.h"
180b57cec5SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h"
190b57cec5SDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h"
200b57cec5SDimitry Andric #include "llvm/CodeGen/Passes.h"
210b57cec5SDimitry Andric #include "llvm/CodeGen/TargetInstrInfo.h"
220b57cec5SDimitry Andric #include "llvm/Support/MathExtras.h"
23*5ffd83dbSDimitry Andric #include "llvm/Pass.h"
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric using namespace llvm;
260b57cec5SDimitry Andric 
270b57cec5SDimitry Andric static cl::opt<unsigned> MaxLoopRange(
280b57cec5SDimitry Andric     "hexagon-loop-range", cl::Hidden, cl::init(200),
290b57cec5SDimitry Andric     cl::desc("Restrict range of loopN instructions (testing only)"));
300b57cec5SDimitry Andric 
310b57cec5SDimitry Andric namespace llvm {
320b57cec5SDimitry Andric   FunctionPass *createHexagonFixupHwLoops();
330b57cec5SDimitry Andric   void initializeHexagonFixupHwLoopsPass(PassRegistry&);
340b57cec5SDimitry Andric }
350b57cec5SDimitry Andric 
360b57cec5SDimitry Andric namespace {
370b57cec5SDimitry Andric   struct HexagonFixupHwLoops : public MachineFunctionPass {
380b57cec5SDimitry Andric   public:
390b57cec5SDimitry Andric     static char ID;
400b57cec5SDimitry Andric 
410b57cec5SDimitry Andric     HexagonFixupHwLoops() : MachineFunctionPass(ID) {
420b57cec5SDimitry Andric       initializeHexagonFixupHwLoopsPass(*PassRegistry::getPassRegistry());
430b57cec5SDimitry Andric     }
440b57cec5SDimitry Andric 
450b57cec5SDimitry Andric     bool runOnMachineFunction(MachineFunction &MF) override;
460b57cec5SDimitry Andric 
470b57cec5SDimitry Andric     MachineFunctionProperties getRequiredProperties() const override {
480b57cec5SDimitry Andric       return MachineFunctionProperties().set(
490b57cec5SDimitry Andric           MachineFunctionProperties::Property::NoVRegs);
500b57cec5SDimitry Andric     }
510b57cec5SDimitry Andric 
520b57cec5SDimitry Andric     StringRef getPassName() const override {
530b57cec5SDimitry Andric       return "Hexagon Hardware Loop Fixup";
540b57cec5SDimitry Andric     }
550b57cec5SDimitry Andric 
560b57cec5SDimitry Andric     void getAnalysisUsage(AnalysisUsage &AU) const override {
570b57cec5SDimitry Andric       AU.setPreservesCFG();
580b57cec5SDimitry Andric       MachineFunctionPass::getAnalysisUsage(AU);
590b57cec5SDimitry Andric     }
600b57cec5SDimitry Andric 
610b57cec5SDimitry Andric   private:
620b57cec5SDimitry Andric     /// Check the offset between each loop instruction and
630b57cec5SDimitry Andric     /// the loop basic block to determine if we can use the LOOP instruction
640b57cec5SDimitry Andric     /// or if we need to set the LC/SA registers explicitly.
650b57cec5SDimitry Andric     bool fixupLoopInstrs(MachineFunction &MF);
660b57cec5SDimitry Andric 
670b57cec5SDimitry Andric     /// Replace loop instruction with the constant extended
680b57cec5SDimitry Andric     /// version if the loop label is too far from the loop instruction.
690b57cec5SDimitry Andric     void useExtLoopInstr(MachineFunction &MF,
700b57cec5SDimitry Andric                          MachineBasicBlock::iterator &MII);
710b57cec5SDimitry Andric   };
720b57cec5SDimitry Andric 
730b57cec5SDimitry Andric   char HexagonFixupHwLoops::ID = 0;
740b57cec5SDimitry Andric }
750b57cec5SDimitry Andric 
760b57cec5SDimitry Andric INITIALIZE_PASS(HexagonFixupHwLoops, "hwloopsfixup",
770b57cec5SDimitry Andric                 "Hexagon Hardware Loops Fixup", false, false)
780b57cec5SDimitry Andric 
790b57cec5SDimitry Andric FunctionPass *llvm::createHexagonFixupHwLoops() {
800b57cec5SDimitry Andric   return new HexagonFixupHwLoops();
810b57cec5SDimitry Andric }
820b57cec5SDimitry Andric 
830b57cec5SDimitry Andric /// Returns true if the instruction is a hardware loop instruction.
840b57cec5SDimitry Andric static bool isHardwareLoop(const MachineInstr &MI) {
850b57cec5SDimitry Andric   return MI.getOpcode() == Hexagon::J2_loop0r ||
860b57cec5SDimitry Andric          MI.getOpcode() == Hexagon::J2_loop0i ||
870b57cec5SDimitry Andric          MI.getOpcode() == Hexagon::J2_loop1r ||
880b57cec5SDimitry Andric          MI.getOpcode() == Hexagon::J2_loop1i;
890b57cec5SDimitry Andric }
900b57cec5SDimitry Andric 
910b57cec5SDimitry Andric bool HexagonFixupHwLoops::runOnMachineFunction(MachineFunction &MF) {
920b57cec5SDimitry Andric   if (skipFunction(MF.getFunction()))
930b57cec5SDimitry Andric     return false;
940b57cec5SDimitry Andric   return fixupLoopInstrs(MF);
950b57cec5SDimitry Andric }
960b57cec5SDimitry Andric 
970b57cec5SDimitry Andric /// For Hexagon, if the loop label is to far from the
980b57cec5SDimitry Andric /// loop instruction then we need to set the LC0 and SA0 registers
990b57cec5SDimitry Andric /// explicitly instead of using LOOP(start,count).  This function
1000b57cec5SDimitry Andric /// checks the distance, and generates register assignments if needed.
1010b57cec5SDimitry Andric ///
1020b57cec5SDimitry Andric /// This function makes two passes over the basic blocks.  The first
1030b57cec5SDimitry Andric /// pass computes the offset of the basic block from the start.
1040b57cec5SDimitry Andric /// The second pass checks all the loop instructions.
1050b57cec5SDimitry Andric bool HexagonFixupHwLoops::fixupLoopInstrs(MachineFunction &MF) {
1060b57cec5SDimitry Andric 
1070b57cec5SDimitry Andric   // Offset of the current instruction from the start.
1080b57cec5SDimitry Andric   unsigned InstOffset = 0;
1090b57cec5SDimitry Andric   // Map for each basic block to it's first instruction.
1100b57cec5SDimitry Andric   DenseMap<const MachineBasicBlock *, unsigned> BlockToInstOffset;
1110b57cec5SDimitry Andric 
1120b57cec5SDimitry Andric   const HexagonInstrInfo *HII =
1130b57cec5SDimitry Andric       static_cast<const HexagonInstrInfo *>(MF.getSubtarget().getInstrInfo());
1140b57cec5SDimitry Andric 
1150b57cec5SDimitry Andric   // First pass - compute the offset of each basic block.
1160b57cec5SDimitry Andric   for (const MachineBasicBlock &MBB : MF) {
117*5ffd83dbSDimitry Andric     if (MBB.getAlignment() != Align(1)) {
1180b57cec5SDimitry Andric       // Although we don't know the exact layout of the final code, we need
1190b57cec5SDimitry Andric       // to account for alignment padding somehow. This heuristic pads each
1200b57cec5SDimitry Andric       // aligned basic block according to the alignment value.
1218bcb0991SDimitry Andric       InstOffset = alignTo(InstOffset, MBB.getAlignment());
1220b57cec5SDimitry Andric     }
1230b57cec5SDimitry Andric 
1240b57cec5SDimitry Andric     BlockToInstOffset[&MBB] = InstOffset;
1250b57cec5SDimitry Andric     for (const MachineInstr &MI : MBB)
1260b57cec5SDimitry Andric       InstOffset += HII->getSize(MI);
1270b57cec5SDimitry Andric   }
1280b57cec5SDimitry Andric 
1290b57cec5SDimitry Andric   // Second pass - check each loop instruction to see if it needs to be
1300b57cec5SDimitry Andric   // converted.
1310b57cec5SDimitry Andric   bool Changed = false;
1320b57cec5SDimitry Andric   for (MachineBasicBlock &MBB : MF) {
1330b57cec5SDimitry Andric     InstOffset = BlockToInstOffset[&MBB];
1340b57cec5SDimitry Andric 
1350b57cec5SDimitry Andric     // Loop over all the instructions.
1360b57cec5SDimitry Andric     MachineBasicBlock::iterator MII = MBB.begin();
1370b57cec5SDimitry Andric     MachineBasicBlock::iterator MIE = MBB.end();
1380b57cec5SDimitry Andric     while (MII != MIE) {
1390b57cec5SDimitry Andric       unsigned InstSize = HII->getSize(*MII);
1400b57cec5SDimitry Andric       if (MII->isMetaInstruction()) {
1410b57cec5SDimitry Andric         ++MII;
1420b57cec5SDimitry Andric         continue;
1430b57cec5SDimitry Andric       }
1440b57cec5SDimitry Andric       if (isHardwareLoop(*MII)) {
1450b57cec5SDimitry Andric         assert(MII->getOperand(0).isMBB() &&
1460b57cec5SDimitry Andric                "Expect a basic block as loop operand");
1470b57cec5SDimitry Andric         MachineBasicBlock *TargetBB = MII->getOperand(0).getMBB();
1480b57cec5SDimitry Andric         unsigned Diff = AbsoluteDifference(InstOffset,
1490b57cec5SDimitry Andric                                            BlockToInstOffset[TargetBB]);
1500b57cec5SDimitry Andric         if (Diff > MaxLoopRange) {
1510b57cec5SDimitry Andric           useExtLoopInstr(MF, MII);
1520b57cec5SDimitry Andric           MII = MBB.erase(MII);
1530b57cec5SDimitry Andric           Changed = true;
1540b57cec5SDimitry Andric         } else {
1550b57cec5SDimitry Andric           ++MII;
1560b57cec5SDimitry Andric         }
1570b57cec5SDimitry Andric       } else {
1580b57cec5SDimitry Andric         ++MII;
1590b57cec5SDimitry Andric       }
1600b57cec5SDimitry Andric       InstOffset += InstSize;
1610b57cec5SDimitry Andric     }
1620b57cec5SDimitry Andric   }
1630b57cec5SDimitry Andric 
1640b57cec5SDimitry Andric   return Changed;
1650b57cec5SDimitry Andric }
1660b57cec5SDimitry Andric 
1670b57cec5SDimitry Andric /// Replace loop instructions with the constant extended version.
1680b57cec5SDimitry Andric void HexagonFixupHwLoops::useExtLoopInstr(MachineFunction &MF,
1690b57cec5SDimitry Andric                                           MachineBasicBlock::iterator &MII) {
1700b57cec5SDimitry Andric   const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
1710b57cec5SDimitry Andric   MachineBasicBlock *MBB = MII->getParent();
1720b57cec5SDimitry Andric   DebugLoc DL = MII->getDebugLoc();
1730b57cec5SDimitry Andric   MachineInstrBuilder MIB;
1740b57cec5SDimitry Andric   unsigned newOp;
1750b57cec5SDimitry Andric   switch (MII->getOpcode()) {
1760b57cec5SDimitry Andric   case Hexagon::J2_loop0r:
1770b57cec5SDimitry Andric     newOp = Hexagon::J2_loop0rext;
1780b57cec5SDimitry Andric     break;
1790b57cec5SDimitry Andric   case Hexagon::J2_loop0i:
1800b57cec5SDimitry Andric     newOp = Hexagon::J2_loop0iext;
1810b57cec5SDimitry Andric     break;
1820b57cec5SDimitry Andric   case Hexagon::J2_loop1r:
1830b57cec5SDimitry Andric     newOp = Hexagon::J2_loop1rext;
1840b57cec5SDimitry Andric     break;
1850b57cec5SDimitry Andric   case Hexagon::J2_loop1i:
1860b57cec5SDimitry Andric     newOp = Hexagon::J2_loop1iext;
1870b57cec5SDimitry Andric     break;
1880b57cec5SDimitry Andric   default:
1890b57cec5SDimitry Andric     llvm_unreachable("Invalid Hardware Loop Instruction.");
1900b57cec5SDimitry Andric   }
1910b57cec5SDimitry Andric   MIB = BuildMI(*MBB, MII, DL, TII->get(newOp));
1920b57cec5SDimitry Andric 
1930b57cec5SDimitry Andric   for (unsigned i = 0; i < MII->getNumOperands(); ++i)
1940b57cec5SDimitry Andric     MIB.add(MII->getOperand(i));
1950b57cec5SDimitry Andric }
196