LLVM 23.0.0git
HexagonHVXSaveRemark.cpp
Go to the documentation of this file.
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"
33#include "llvm/Pass.h"
35#include "llvm/Support/Debug.h"
36
37using namespace llvm;
38
39#define DEBUG_TYPE "hexagon-hvx-save"
40
42 "hexagon-hvx-save-threshold", cl::Hidden, cl::init(128 * 8),
43 cl::desc("Minimum number of bytes of HVX caller-saved register data live "
44 "across a call to trigger a remark (default: 8 x 128-byte "
45 "vectors)"));
46
47namespace {
48
49struct HexagonHVXSaveRemark : public MachineFunctionPass {
50 static char ID;
51
52 HexagonHVXSaveRemark() : MachineFunctionPass(ID) {}
53
54 // Returns the number of HVX vectors represented by VReg: 2 for HvxWR
55 // (vector pair), 1 for HvxVR (single vector), 0 for non-HVX registers.
56 static unsigned hvxVecCount(Register VReg, const MachineRegisterInfo &MRI) {
57 const TargetRegisterClass *RC = MRI.getRegClass(VReg);
58 if (RC == &Hexagon::HvxWRRegClass)
59 return 2;
60 if (RC == &Hexagon::HvxVRRegClass)
61 return 1;
62 return 0;
63 }
64
65 bool runOnMachineFunction(MachineFunction &MF) override {
66 auto &MORE = getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE();
67 if (!MORE.allowExtraAnalysis(DEBUG_TYPE))
68 return false;
69
70 const HexagonSubtarget &HST = MF.getSubtarget<HexagonSubtarget>();
71 if (!HST.useHVXOps())
72 return false;
73
74 const MachineRegisterInfo &MRI = MF.getRegInfo();
75 unsigned HVXLen = HST.getVectorLength();
76
77 // Compute LiveOut[B] for each block: the set of HVX virtual registers
78 // that are live on exit from B. We use a standard backward dataflow
79 // fixed-point:
80 //
81 // LiveIn[B] = UEVar[B] union (LiveOut[B] - Def[B])
82 // LiveOut[B] = union over successors S of LiveIn[S]
83 //
84 // where UEVar[B] is the set of HVX vregs that are used in B before any
85 // definition of that vreg in B (upward-exposed uses), and Def[B] is the
86 // set of HVX vregs defined in B.
87 //
88 // Because MachineBasicBlock::liveins() only contains physical registers,
89 // we cannot seed cross-block virtual register liveness from successor
90 // liveins -- we must compute it ourselves.
91
92 unsigned NumBlocks = MF.getNumBlockIDs();
93 using VRegSet = SmallSet<Register, 8>;
94
95 // Per-block UEVar and Def sets (HVX vregs only).
96 SmallVector<VRegSet, 16> UEVar(NumBlocks), BlockDef(NumBlocks);
97
98 for (const MachineBasicBlock &MBB : MF) {
99 unsigned BN = MBB.getNumber();
100 VRegSet Defs;
101 for (const MachineInstr &MI : MBB) {
102 for (const MachineOperand &MO : MI.operands()) {
103 if (!MO.isReg())
104 continue;
105 Register R = MO.getReg();
106 if (!R.isVirtual() || !hvxVecCount(R, MRI))
107 continue;
108 if (MO.isDef()) {
109 Defs.insert(R);
110 } else if (MO.isUse() && !Defs.count(R)) {
111 UEVar[BN].insert(R); // upward-exposed use
112 }
113 }
114 }
115 BlockDef[BN] = Defs;
116 }
117
118 // LiveOut[B] and LiveIn[B] maps.
119 SmallVector<VRegSet, 16> LiveOut(NumBlocks), LiveIn(NumBlocks);
120
121 // Seed LiveIn from UEVar and iterate until stable.
122 for (unsigned I = 0; I < NumBlocks; ++I)
123 LiveIn[I] = UEVar[I];
124
125 bool Changed = true;
126 while (Changed) {
127 Changed = false;
128 for (const MachineBasicBlock &MBB : MF) {
129 unsigned BN = MBB.getNumber();
130
131 // LiveOut[B] = union of LiveIn[S] for each successor S.
132 VRegSet NewLiveOut;
133 for (const MachineBasicBlock *Succ : MBB.successors())
134 for (Register R : LiveIn[Succ->getNumber()])
135 NewLiveOut.insert(R);
136
137 if (NewLiveOut != LiveOut[BN]) {
138 LiveOut[BN] = NewLiveOut;
139 Changed = true;
140 }
141
142 // LiveIn[B] = UEVar[B] union (LiveOut[B] - Def[B]).
143 VRegSet NewLiveIn = UEVar[BN];
144 for (Register R : LiveOut[BN])
145 if (!BlockDef[BN].count(R))
146 NewLiveIn.insert(R);
147
148 if (NewLiveIn != LiveIn[BN]) {
149 LiveIn[BN] = NewLiveIn;
150 Changed = true;
151 }
152 }
153 }
154
155 // Now do the backward scan over each block, seeded from LiveOut[B].
156 for (const MachineBasicBlock &MBB : MF) {
157 // Backward liveness scan over virtual registers. We track which
158 // virtual registers are live at each point, then at call instructions
159 // count those with HVX register classes.
160 //
161 // When walking backwards:
162 // - a def removes a vreg from the live set
163 // - a use adds a vreg to the live set
164 // At each call, the live set holds vregs live after the call (i.e., the
165 // values that must survive across it and therefore need save/restore).
166 VRegSet LiveVRegs = LiveOut[MBB.getNumber()];
167
168 for (const MachineInstr &MI : llvm::reverse(MBB)) {
169 if (MI.isCall()) {
170 // Count HVX virtual registers live after (and thus across) this
171 // call. HvxVR holds one vector (HVXLen bytes); HvxWR holds two
172 // (2 * HVXLen bytes).
173 unsigned NumVecs = 0;
174 for (Register VReg : LiveVRegs)
175 NumVecs += hvxVecCount(VReg, MRI);
176 unsigned TotalBytes = NumVecs * HVXLen;
177
178 LLVM_DEBUG(dbgs() << "HVXSaveRemark: call in " << MF.getName()
179 << " has " << NumVecs << " HVX vector(s) live ("
180 << TotalBytes << " bytes)\n");
181
182 if (TotalBytes >= HVXSaveThreshold) {
183 MORE.emit([&]() {
184 MachineOptimizationRemarkAnalysis R(
185 DEBUG_TYPE, "HVXSaveAroundCall", MI.getDebugLoc(), &MBB);
186 R << ore::NV("NumVecs", NumVecs)
187 << " HVX caller-saved register(s) ("
188 << ore::NV("TotalBytes", TotalBytes)
189 << " bytes) live across call";
190 return R;
191 });
192 }
193 }
194
195 // Update liveness: defs kill vregs, uses add them.
196 for (const MachineOperand &MO : MI.operands()) {
197 if (!MO.isReg() || !MO.getReg().isVirtual())
198 continue;
199 if (MO.isDef())
200 LiveVRegs.erase(MO.getReg());
201 else if (MO.isUse())
202 LiveVRegs.insert(MO.getReg());
203 }
204 }
205 }
206
207 return false;
208 }
209
210 StringRef getPassName() const override { return "Hexagon HVX Save Remarks"; }
211
212 void getAnalysisUsage(AnalysisUsage &AU) const override {
213 AU.addRequired<MachineOptimizationRemarkEmitterPass>();
214 AU.setPreservesAll();
216 }
217};
218
219char HexagonHVXSaveRemark::ID = 0;
220
221} // end anonymous namespace
222
223INITIALIZE_PASS(HexagonHVXSaveRemark, DEBUG_TYPE, "Hexagon HVX Save Remarks",
224 false, false)
225
227 return new HexagonHVXSaveRemark();
228}
MachineBasicBlock & MBB
#define DEBUG_TYPE
static cl::opt< unsigned > HVXSaveThreshold("hexagon-hvx-save-threshold", cl::Hidden, cl::init(128 *8), cl::desc("Minimum number of bytes of HVX caller-saved register data live " "across a call to trigger a remark (default: 8 x 128-byte " "vectors)"))
#define DEBUG_TYPE
IRTranslator LLVM IR MI
#define I(x, y, z)
Definition MD5.cpp:57
===- MachineOptimizationRemarkEmitter.h - Opt Diagnostics -*- C++ -*-—===//
Promote Memory to Register
Definition Mem2Reg.cpp:110
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
This file defines the SmallSet class.
This file defines the SmallVector class.
#define LLVM_DEBUG(...)
Definition Debug.h:119
AnalysisUsage & addRequired()
void setPreservesAll()
Set by analyses that do not transform their input at all.
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
unsigned getVectorLength() const
int getNumber() const
MachineBasicBlocks are uniquely numbered at the function level, unless they're not in a MachineFuncti...
iterator_range< succ_iterator > successors()
MachineFunctionPass - This class adapts the FunctionPass interface to allow convenient creation of pa...
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - Subclasses that override getAnalysisUsage must call this.
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
unsigned getNumBlockIDs() const
getNumBlockIDs - Return the number of MBB ID's allocated.
const TargetRegisterClass * getRegClass(Register Reg) const
Return the register class of the specified virtual register.
Changed
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
initializer< Ty > init(const Ty &Val)
DiagnosticInfoOptimizationBase::Argument NV
This is an optimization pass for GlobalISel generic memory operations.
auto reverse(ContainerTy &&C)
Definition STLExtras.h:407
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
auto count(R &&Range, const E &Element)
Wrapper function around std::count to count the number of times an element Element occurs in the give...
Definition STLExtras.h:2012
FunctionPass * createHexagonHVXSaveRemark()
#define MORE()
Definition regcomp.c:246