xref: /freebsd/contrib/llvm-project/llvm/lib/Target/Hexagon/HexagonFixupHwLoops.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
1*0b57cec5SDimitry Andric //===---- HexagonFixupHwLoops.cpp - Fixup HW loops too far from LOOPn. ----===//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric // The loop start address in the LOOPn instruction is encoded as a distance
8*0b57cec5SDimitry Andric // from the LOOPn instruction itself. If the start address is too far from
9*0b57cec5SDimitry Andric // the LOOPn instruction, the instruction needs to use a constant extender.
10*0b57cec5SDimitry Andric // This pass will identify and convert such LOOPn instructions to a proper
11*0b57cec5SDimitry Andric // form.
12*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
13*0b57cec5SDimitry Andric 
14*0b57cec5SDimitry Andric #include "Hexagon.h"
15*0b57cec5SDimitry Andric #include "HexagonTargetMachine.h"
16*0b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h"
17*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineFunction.h"
18*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h"
19*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h"
20*0b57cec5SDimitry Andric #include "llvm/CodeGen/Passes.h"
21*0b57cec5SDimitry Andric #include "llvm/CodeGen/TargetInstrInfo.h"
22*0b57cec5SDimitry Andric #include "llvm/Support/MathExtras.h"
23*0b57cec5SDimitry Andric #include "llvm/PassSupport.h"
24*0b57cec5SDimitry Andric 
25*0b57cec5SDimitry Andric using namespace llvm;
26*0b57cec5SDimitry Andric 
27*0b57cec5SDimitry Andric static cl::opt<unsigned> MaxLoopRange(
28*0b57cec5SDimitry Andric     "hexagon-loop-range", cl::Hidden, cl::init(200),
29*0b57cec5SDimitry Andric     cl::desc("Restrict range of loopN instructions (testing only)"));
30*0b57cec5SDimitry Andric 
31*0b57cec5SDimitry Andric namespace llvm {
32*0b57cec5SDimitry Andric   FunctionPass *createHexagonFixupHwLoops();
33*0b57cec5SDimitry Andric   void initializeHexagonFixupHwLoopsPass(PassRegistry&);
34*0b57cec5SDimitry Andric }
35*0b57cec5SDimitry Andric 
36*0b57cec5SDimitry Andric namespace {
37*0b57cec5SDimitry Andric   struct HexagonFixupHwLoops : public MachineFunctionPass {
38*0b57cec5SDimitry Andric   public:
39*0b57cec5SDimitry Andric     static char ID;
40*0b57cec5SDimitry Andric 
41*0b57cec5SDimitry Andric     HexagonFixupHwLoops() : MachineFunctionPass(ID) {
42*0b57cec5SDimitry Andric       initializeHexagonFixupHwLoopsPass(*PassRegistry::getPassRegistry());
43*0b57cec5SDimitry Andric     }
44*0b57cec5SDimitry Andric 
45*0b57cec5SDimitry Andric     bool runOnMachineFunction(MachineFunction &MF) override;
46*0b57cec5SDimitry Andric 
47*0b57cec5SDimitry Andric     MachineFunctionProperties getRequiredProperties() const override {
48*0b57cec5SDimitry Andric       return MachineFunctionProperties().set(
49*0b57cec5SDimitry Andric           MachineFunctionProperties::Property::NoVRegs);
50*0b57cec5SDimitry Andric     }
51*0b57cec5SDimitry Andric 
52*0b57cec5SDimitry Andric     StringRef getPassName() const override {
53*0b57cec5SDimitry Andric       return "Hexagon Hardware Loop Fixup";
54*0b57cec5SDimitry Andric     }
55*0b57cec5SDimitry Andric 
56*0b57cec5SDimitry Andric     void getAnalysisUsage(AnalysisUsage &AU) const override {
57*0b57cec5SDimitry Andric       AU.setPreservesCFG();
58*0b57cec5SDimitry Andric       MachineFunctionPass::getAnalysisUsage(AU);
59*0b57cec5SDimitry Andric     }
60*0b57cec5SDimitry Andric 
61*0b57cec5SDimitry Andric   private:
62*0b57cec5SDimitry Andric     /// Check the offset between each loop instruction and
63*0b57cec5SDimitry Andric     /// the loop basic block to determine if we can use the LOOP instruction
64*0b57cec5SDimitry Andric     /// or if we need to set the LC/SA registers explicitly.
65*0b57cec5SDimitry Andric     bool fixupLoopInstrs(MachineFunction &MF);
66*0b57cec5SDimitry Andric 
67*0b57cec5SDimitry Andric     /// Replace loop instruction with the constant extended
68*0b57cec5SDimitry Andric     /// version if the loop label is too far from the loop instruction.
69*0b57cec5SDimitry Andric     void useExtLoopInstr(MachineFunction &MF,
70*0b57cec5SDimitry Andric                          MachineBasicBlock::iterator &MII);
71*0b57cec5SDimitry Andric   };
72*0b57cec5SDimitry Andric 
73*0b57cec5SDimitry Andric   char HexagonFixupHwLoops::ID = 0;
74*0b57cec5SDimitry Andric }
75*0b57cec5SDimitry Andric 
76*0b57cec5SDimitry Andric INITIALIZE_PASS(HexagonFixupHwLoops, "hwloopsfixup",
77*0b57cec5SDimitry Andric                 "Hexagon Hardware Loops Fixup", false, false)
78*0b57cec5SDimitry Andric 
79*0b57cec5SDimitry Andric FunctionPass *llvm::createHexagonFixupHwLoops() {
80*0b57cec5SDimitry Andric   return new HexagonFixupHwLoops();
81*0b57cec5SDimitry Andric }
82*0b57cec5SDimitry Andric 
83*0b57cec5SDimitry Andric /// Returns true if the instruction is a hardware loop instruction.
84*0b57cec5SDimitry Andric static bool isHardwareLoop(const MachineInstr &MI) {
85*0b57cec5SDimitry Andric   return MI.getOpcode() == Hexagon::J2_loop0r ||
86*0b57cec5SDimitry Andric          MI.getOpcode() == Hexagon::J2_loop0i ||
87*0b57cec5SDimitry Andric          MI.getOpcode() == Hexagon::J2_loop1r ||
88*0b57cec5SDimitry Andric          MI.getOpcode() == Hexagon::J2_loop1i;
89*0b57cec5SDimitry Andric }
90*0b57cec5SDimitry Andric 
91*0b57cec5SDimitry Andric bool HexagonFixupHwLoops::runOnMachineFunction(MachineFunction &MF) {
92*0b57cec5SDimitry Andric   if (skipFunction(MF.getFunction()))
93*0b57cec5SDimitry Andric     return false;
94*0b57cec5SDimitry Andric   return fixupLoopInstrs(MF);
95*0b57cec5SDimitry Andric }
96*0b57cec5SDimitry Andric 
97*0b57cec5SDimitry Andric /// For Hexagon, if the loop label is to far from the
98*0b57cec5SDimitry Andric /// loop instruction then we need to set the LC0 and SA0 registers
99*0b57cec5SDimitry Andric /// explicitly instead of using LOOP(start,count).  This function
100*0b57cec5SDimitry Andric /// checks the distance, and generates register assignments if needed.
101*0b57cec5SDimitry Andric ///
102*0b57cec5SDimitry Andric /// This function makes two passes over the basic blocks.  The first
103*0b57cec5SDimitry Andric /// pass computes the offset of the basic block from the start.
104*0b57cec5SDimitry Andric /// The second pass checks all the loop instructions.
105*0b57cec5SDimitry Andric bool HexagonFixupHwLoops::fixupLoopInstrs(MachineFunction &MF) {
106*0b57cec5SDimitry Andric 
107*0b57cec5SDimitry Andric   // Offset of the current instruction from the start.
108*0b57cec5SDimitry Andric   unsigned InstOffset = 0;
109*0b57cec5SDimitry Andric   // Map for each basic block to it's first instruction.
110*0b57cec5SDimitry Andric   DenseMap<const MachineBasicBlock *, unsigned> BlockToInstOffset;
111*0b57cec5SDimitry Andric 
112*0b57cec5SDimitry Andric   const HexagonInstrInfo *HII =
113*0b57cec5SDimitry Andric       static_cast<const HexagonInstrInfo *>(MF.getSubtarget().getInstrInfo());
114*0b57cec5SDimitry Andric 
115*0b57cec5SDimitry Andric   // First pass - compute the offset of each basic block.
116*0b57cec5SDimitry Andric   for (const MachineBasicBlock &MBB : MF) {
117*0b57cec5SDimitry Andric     if (MBB.getAlignment()) {
118*0b57cec5SDimitry Andric       // Although we don't know the exact layout of the final code, we need
119*0b57cec5SDimitry Andric       // to account for alignment padding somehow. This heuristic pads each
120*0b57cec5SDimitry Andric       // aligned basic block according to the alignment value.
121*0b57cec5SDimitry Andric       int ByteAlign = (1u << MBB.getAlignment()) - 1;
122*0b57cec5SDimitry Andric       InstOffset = (InstOffset + ByteAlign) & ~(ByteAlign);
123*0b57cec5SDimitry Andric     }
124*0b57cec5SDimitry Andric 
125*0b57cec5SDimitry Andric     BlockToInstOffset[&MBB] = InstOffset;
126*0b57cec5SDimitry Andric     for (const MachineInstr &MI : MBB)
127*0b57cec5SDimitry Andric       InstOffset += HII->getSize(MI);
128*0b57cec5SDimitry Andric   }
129*0b57cec5SDimitry Andric 
130*0b57cec5SDimitry Andric   // Second pass - check each loop instruction to see if it needs to be
131*0b57cec5SDimitry Andric   // converted.
132*0b57cec5SDimitry Andric   bool Changed = false;
133*0b57cec5SDimitry Andric   for (MachineBasicBlock &MBB : MF) {
134*0b57cec5SDimitry Andric     InstOffset = BlockToInstOffset[&MBB];
135*0b57cec5SDimitry Andric 
136*0b57cec5SDimitry Andric     // Loop over all the instructions.
137*0b57cec5SDimitry Andric     MachineBasicBlock::iterator MII = MBB.begin();
138*0b57cec5SDimitry Andric     MachineBasicBlock::iterator MIE = MBB.end();
139*0b57cec5SDimitry Andric     while (MII != MIE) {
140*0b57cec5SDimitry Andric       unsigned InstSize = HII->getSize(*MII);
141*0b57cec5SDimitry Andric       if (MII->isMetaInstruction()) {
142*0b57cec5SDimitry Andric         ++MII;
143*0b57cec5SDimitry Andric         continue;
144*0b57cec5SDimitry Andric       }
145*0b57cec5SDimitry Andric       if (isHardwareLoop(*MII)) {
146*0b57cec5SDimitry Andric         assert(MII->getOperand(0).isMBB() &&
147*0b57cec5SDimitry Andric                "Expect a basic block as loop operand");
148*0b57cec5SDimitry Andric         MachineBasicBlock *TargetBB = MII->getOperand(0).getMBB();
149*0b57cec5SDimitry Andric         unsigned Diff = AbsoluteDifference(InstOffset,
150*0b57cec5SDimitry Andric                                            BlockToInstOffset[TargetBB]);
151*0b57cec5SDimitry Andric         if (Diff > MaxLoopRange) {
152*0b57cec5SDimitry Andric           useExtLoopInstr(MF, MII);
153*0b57cec5SDimitry Andric           MII = MBB.erase(MII);
154*0b57cec5SDimitry Andric           Changed = true;
155*0b57cec5SDimitry Andric         } else {
156*0b57cec5SDimitry Andric           ++MII;
157*0b57cec5SDimitry Andric         }
158*0b57cec5SDimitry Andric       } else {
159*0b57cec5SDimitry Andric         ++MII;
160*0b57cec5SDimitry Andric       }
161*0b57cec5SDimitry Andric       InstOffset += InstSize;
162*0b57cec5SDimitry Andric     }
163*0b57cec5SDimitry Andric   }
164*0b57cec5SDimitry Andric 
165*0b57cec5SDimitry Andric   return Changed;
166*0b57cec5SDimitry Andric }
167*0b57cec5SDimitry Andric 
168*0b57cec5SDimitry Andric /// Replace loop instructions with the constant extended version.
169*0b57cec5SDimitry Andric void HexagonFixupHwLoops::useExtLoopInstr(MachineFunction &MF,
170*0b57cec5SDimitry Andric                                           MachineBasicBlock::iterator &MII) {
171*0b57cec5SDimitry Andric   const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
172*0b57cec5SDimitry Andric   MachineBasicBlock *MBB = MII->getParent();
173*0b57cec5SDimitry Andric   DebugLoc DL = MII->getDebugLoc();
174*0b57cec5SDimitry Andric   MachineInstrBuilder MIB;
175*0b57cec5SDimitry Andric   unsigned newOp;
176*0b57cec5SDimitry Andric   switch (MII->getOpcode()) {
177*0b57cec5SDimitry Andric   case Hexagon::J2_loop0r:
178*0b57cec5SDimitry Andric     newOp = Hexagon::J2_loop0rext;
179*0b57cec5SDimitry Andric     break;
180*0b57cec5SDimitry Andric   case Hexagon::J2_loop0i:
181*0b57cec5SDimitry Andric     newOp = Hexagon::J2_loop0iext;
182*0b57cec5SDimitry Andric     break;
183*0b57cec5SDimitry Andric   case Hexagon::J2_loop1r:
184*0b57cec5SDimitry Andric     newOp = Hexagon::J2_loop1rext;
185*0b57cec5SDimitry Andric     break;
186*0b57cec5SDimitry Andric   case Hexagon::J2_loop1i:
187*0b57cec5SDimitry Andric     newOp = Hexagon::J2_loop1iext;
188*0b57cec5SDimitry Andric     break;
189*0b57cec5SDimitry Andric   default:
190*0b57cec5SDimitry Andric     llvm_unreachable("Invalid Hardware Loop Instruction.");
191*0b57cec5SDimitry Andric   }
192*0b57cec5SDimitry Andric   MIB = BuildMI(*MBB, MII, DL, TII->get(newOp));
193*0b57cec5SDimitry Andric 
194*0b57cec5SDimitry Andric   for (unsigned i = 0; i < MII->getNumOperands(); ++i)
195*0b57cec5SDimitry Andric     MIB.add(MII->getOperand(i));
196*0b57cec5SDimitry Andric }
197