1//===- HexagonHVXSaveRemark.cpp - Remark on HVX saves around calls --------===//
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//===----------------------------------------------------------------------===//
8//
9// Diagnostic pass that emits optimization remarks when HVX vector registers
10// are live across function calls. All HVX registers are caller-saved
11// (Section 5.3 of the Hexagon ABI), so every HVX value that is live across a
12// call requires a save/restore pair on the stack. Each HVX vector is 64 or
13// 128 bytes (depending on the mode), making this overhead expensive. The
14// remarks help programmers identify call sites where inlining, hoisting, or
15// sinking the call could reduce the save/restore cost.
16//
17// The pass runs before register allocation while values are still in virtual
18// registers. A backward liveness scan over each basic block counts the HVX
19// virtual registers (and their corresponding byte cost) live at each call
20// instruction.
21//
22//===----------------------------------------------------------------------===//
23
24#include "HexagonSubtarget.h"
25#include "llvm/ADT/SmallSet.h"
26#include "llvm/ADT/SmallVector.h"
27#include "llvm/Analysis/OptimizationRemarkEmitter.h"
28#include "llvm/CodeGen/MachineFunction.h"
29#include "llvm/CodeGen/MachineFunctionPass.h"
30#include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
31#include "llvm/CodeGen/MachineRegisterInfo.h"
32#include "llvm/Pass.h"
33#include "llvm/Support/CommandLine.h"
34#include "llvm/Support/Debug.h"
35
36using namespace llvm;
37
38#define DEBUG_TYPE "hexagon-hvx-save"
39
40static cl::opt<unsigned> HVXSaveThreshold(
41 "hexagon-hvx-save-threshold", cl::Hidden, cl::init(Val: 128 * 8),
42 cl::desc("Minimum number of bytes of HVX caller-saved register data live "
43 "across a call to trigger a remark (default: 8 x 128-byte "
44 "vectors)"));
45
46namespace {
47
48struct HexagonHVXSaveRemark : public MachineFunctionPass {
49 static char ID;
50
51 HexagonHVXSaveRemark() : MachineFunctionPass(ID) {}
52
53 // Returns the number of HVX vectors represented by VReg: 2 for HvxWR
54 // (vector pair), 1 for HvxVR (single vector), 0 for non-HVX registers.
55 static unsigned hvxVecCount(Register VReg, const MachineRegisterInfo &MRI) {
56 const TargetRegisterClass *RC = MRI.getRegClass(Reg: VReg);
57 if (RC == &Hexagon::HvxWRRegClass)
58 return 2;
59 if (RC == &Hexagon::HvxVRRegClass)
60 return 1;
61 return 0;
62 }
63
64 bool runOnMachineFunction(MachineFunction &MF) override {
65 auto &MORE = getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE();
66 if (!MORE.allowExtraAnalysis(DEBUG_TYPE))
67 return false;
68
69 const HexagonSubtarget &HST = MF.getSubtarget<HexagonSubtarget>();
70 if (!HST.useHVXOps())
71 return false;
72
73 const MachineRegisterInfo &MRI = MF.getRegInfo();
74 unsigned HVXLen = HST.getVectorLength();
75
76 // Compute LiveOut[B] for each block: the set of HVX virtual registers
77 // that are live on exit from B. We use a standard backward dataflow
78 // fixed-point:
79 //
80 // LiveIn[B] = UEVar[B] union (LiveOut[B] - Def[B])
81 // LiveOut[B] = union over successors S of LiveIn[S]
82 //
83 // where UEVar[B] is the set of HVX vregs that are used in B before any
84 // definition of that vreg in B (upward-exposed uses), and Def[B] is the
85 // set of HVX vregs defined in B.
86 //
87 // Because MachineBasicBlock::liveins() only contains physical registers,
88 // we cannot seed cross-block virtual register liveness from successor
89 // liveins -- we must compute it ourselves.
90
91 unsigned NumBlocks = MF.getNumBlockIDs();
92 using VRegSet = SmallSet<Register, 8>;
93
94 // Per-block UEVar and Def sets (HVX vregs only).
95 SmallVector<VRegSet, 16> UEVar(NumBlocks), BlockDef(NumBlocks);
96
97 for (const MachineBasicBlock &MBB : MF) {
98 unsigned BN = MBB.getNumber();
99 VRegSet Defs;
100 for (const MachineInstr &MI : MBB) {
101 for (const MachineOperand &MO : MI.operands()) {
102 if (!MO.isReg())
103 continue;
104 Register R = MO.getReg();
105 if (!R.isVirtual() || !hvxVecCount(VReg: R, MRI))
106 continue;
107 if (MO.isDef()) {
108 Defs.insert(V: R);
109 } else if (MO.isUse() && !Defs.count(V: R)) {
110 UEVar[BN].insert(V: R); // upward-exposed use
111 }
112 }
113 }
114 BlockDef[BN] = Defs;
115 }
116
117 // LiveOut[B] and LiveIn[B] maps.
118 SmallVector<VRegSet, 16> LiveOut(NumBlocks), LiveIn(NumBlocks);
119
120 // Seed LiveIn from UEVar and iterate until stable.
121 for (unsigned I = 0; I < NumBlocks; ++I)
122 LiveIn[I] = UEVar[I];
123
124 bool Changed = true;
125 while (Changed) {
126 Changed = false;
127 for (const MachineBasicBlock &MBB : MF) {
128 unsigned BN = MBB.getNumber();
129
130 // LiveOut[B] = union of LiveIn[S] for each successor S.
131 VRegSet NewLiveOut;
132 for (const MachineBasicBlock *Succ : MBB.successors())
133 for (Register R : LiveIn[Succ->getNumber()])
134 NewLiveOut.insert(V: R);
135
136 if (NewLiveOut != LiveOut[BN]) {
137 LiveOut[BN] = NewLiveOut;
138 Changed = true;
139 }
140
141 // LiveIn[B] = UEVar[B] union (LiveOut[B] - Def[B]).
142 VRegSet NewLiveIn = UEVar[BN];
143 for (Register R : LiveOut[BN])
144 if (!BlockDef[BN].count(V: R))
145 NewLiveIn.insert(V: R);
146
147 if (NewLiveIn != LiveIn[BN]) {
148 LiveIn[BN] = NewLiveIn;
149 Changed = true;
150 }
151 }
152 }
153
154 // Now do the backward scan over each block, seeded from LiveOut[B].
155 for (const MachineBasicBlock &MBB : MF) {
156 // Backward liveness scan over virtual registers. We track which
157 // virtual registers are live at each point, then at call instructions
158 // count those with HVX register classes.
159 //
160 // When walking backwards:
161 // - a def removes a vreg from the live set
162 // - a use adds a vreg to the live set
163 // At each call, the live set holds vregs live after the call (i.e., the
164 // values that must survive across it and therefore need save/restore).
165 VRegSet LiveVRegs = LiveOut[MBB.getNumber()];
166
167 for (const MachineInstr &MI : llvm::reverse(C: MBB)) {
168 if (MI.isCall()) {
169 // Count HVX virtual registers live after (and thus across) this
170 // call. HvxVR holds one vector (HVXLen bytes); HvxWR holds two
171 // (2 * HVXLen bytes).
172 unsigned NumVecs = 0;
173 for (Register VReg : LiveVRegs)
174 NumVecs += hvxVecCount(VReg, MRI);
175 unsigned TotalBytes = NumVecs * HVXLen;
176
177 LLVM_DEBUG(dbgs() << "HVXSaveRemark: call in " << MF.getName()
178 << " has " << NumVecs << " HVX vector(s) live ("
179 << TotalBytes << " bytes)\n");
180
181 if (TotalBytes >= HVXSaveThreshold) {
182 MORE.emit(RemarkBuilder: [&]() {
183 MachineOptimizationRemarkAnalysis R(
184 DEBUG_TYPE, "HVXSaveAroundCall", MI.getDebugLoc(), &MBB);
185 R << ore::NV("NumVecs", NumVecs)
186 << " HVX caller-saved register(s) ("
187 << ore::NV("TotalBytes", TotalBytes)
188 << " bytes) live across call";
189 return R;
190 });
191 }
192 }
193
194 // Update liveness: defs kill vregs, uses add them.
195 for (const MachineOperand &MO : MI.operands()) {
196 if (!MO.isReg() || !MO.getReg().isVirtual())
197 continue;
198 if (MO.isDef())
199 LiveVRegs.erase(V: MO.getReg());
200 else if (MO.isUse())
201 LiveVRegs.insert(V: MO.getReg());
202 }
203 }
204 }
205
206 return false;
207 }
208
209 StringRef getPassName() const override { return "Hexagon HVX Save Remarks"; }
210
211 void getAnalysisUsage(AnalysisUsage &AU) const override {
212 AU.addRequired<MachineOptimizationRemarkEmitterPass>();
213 AU.setPreservesAll();
214 MachineFunctionPass::getAnalysisUsage(AU);
215 }
216};
217
218char HexagonHVXSaveRemark::ID = 0;
219
220} // end anonymous namespace
221
222INITIALIZE_PASS(HexagonHVXSaveRemark, DEBUG_TYPE, "Hexagon HVX Save Remarks",
223 false, false)
224
225FunctionPass *llvm::createHexagonHVXSaveRemark() {
226 return new HexagonHVXSaveRemark();
227}
228