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