LLVM 24.0.0git
SIFormMemoryClauses.cpp
Go to the documentation of this file.
1//===-- SIFormMemoryClauses.cpp -------------------------------------------===//
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/// \file This pass extends the live ranges of registers used as pointers in
10/// sequences of adjacent SMEM and VMEM instructions if XNACK is enabled. A
11/// load that would overwrite a pointer would require breaking the soft clause.
12/// Artificially extend the live ranges of the pointer operands by adding
13/// implicit-def early-clobber operands throughout the soft clause.
14///
15//===----------------------------------------------------------------------===//
16
17#include "SIFormMemoryClauses.h"
18#include "AMDGPU.h"
19#include "GCNRegPressure.h"
22
23using namespace llvm;
24
25#define DEBUG_TYPE "si-form-memory-clauses"
26
27// Clauses longer then 15 instructions would overflow one of the counters
28// and stall. They can stall even earlier if there are outstanding counters.
30MaxClause("amdgpu-max-memory-clause", cl::Hidden, cl::init(15),
31 cl::desc("Maximum length of a memory clause, instructions"));
32
33namespace {
34
35class SIFormMemoryClausesImpl {
37
38 bool canBundle(const MachineInstr &MI, const RegUse &Defs,
39 const RegUse &Uses) const;
40 bool checkPressure(const MachineInstr &MI, GCNDownwardRPTracker &RPT);
41 void collectRegUses(const MachineInstr &MI, RegUse &Defs, RegUse &Uses) const;
42 bool processRegUses(const MachineInstr &MI, RegUse &Defs, RegUse &Uses,
44
45 const GCNSubtarget *ST;
46 const SIRegisterInfo *TRI;
49 LiveIntervals *LIS;
50
51 unsigned LastRecordedOccupancy;
52 unsigned MaxVGPRs;
53 unsigned MaxSGPRs;
54
55public:
56 SIFormMemoryClausesImpl(LiveIntervals *LS) : LIS(LS) {}
57 bool run(MachineFunction &MF);
58};
59
60class SIFormMemoryClausesLegacy : public MachineFunctionPass {
61public:
62 static char ID;
63
64 SIFormMemoryClausesLegacy() : MachineFunctionPass(ID) {}
65
66 bool runOnMachineFunction(MachineFunction &MF) override;
67
68 StringRef getPassName() const override {
69 return "SI Form memory clauses";
70 }
71
72 void getAnalysisUsage(AnalysisUsage &AU) const override {
73 AU.addRequired<LiveIntervalsWrapperPass>();
74 AU.setPreservesAll();
76 }
77
78 MachineFunctionProperties getClearedProperties() const override {
79 return MachineFunctionProperties().setIsSSA();
80 }
81};
82
83} // End anonymous namespace.
84
85INITIALIZE_PASS_BEGIN(SIFormMemoryClausesLegacy, DEBUG_TYPE,
86 "SI Form memory clauses", false, false)
88INITIALIZE_PASS_END(SIFormMemoryClausesLegacy, DEBUG_TYPE,
89 "SI Form memory clauses", false, false)
90
91char SIFormMemoryClausesLegacy::ID = 0;
92
93char &llvm::SIFormMemoryClausesID = SIFormMemoryClausesLegacy::ID;
94
98
99static bool isSMEMClauseInst(const MachineInstr &MI) {
100 return SIInstrInfo::isSMRD(MI);
101}
102
103// There no sense to create store clauses, they do not define anything,
104// thus there is nothing to set early-clobber.
105static bool isValidClauseInst(const MachineInstr &MI, bool IsVMEMClause) {
106 assert(!MI.isDebugInstr() && "debug instructions should not reach here");
107 if (MI.isBundled())
108 return false;
109 if (!MI.mayLoad() || MI.mayStore())
110 return false;
112 return false;
113 if (IsVMEMClause && !isVMEMClauseInst(MI))
114 return false;
115 if (!IsVMEMClause && !isSMEMClauseInst(MI))
116 return false;
117 // If this is a load instruction where the result has been coalesced with an operand, then we cannot clause it.
118 for (const MachineOperand &ResMO : MI.defs()) {
119 Register ResReg = ResMO.getReg();
120 for (const MachineOperand &MO : MI.all_uses()) {
121 if (MO.getReg() == ResReg)
122 return false;
123 }
124 break; // Only check the first def.
125 }
126 return true;
127}
128
130 RegState S = {};
131 if (MO.isImplicit())
133 if (MO.isDead())
134 S |= RegState::Dead;
135 if (MO.isUndef())
136 S |= RegState::Undef;
137 if (MO.isKill())
138 S |= RegState::Kill;
139 if (MO.isEarlyClobber())
141 if (MO.getReg().isPhysical() && MO.isRenamable())
143 return S;
144}
145
146// Returns false if there is a use of a def already in the map.
147// In this case we must break the clause.
148bool SIFormMemoryClausesImpl::canBundle(const MachineInstr &MI,
149 const RegUse &Defs,
150 const RegUse &Uses) const {
151 // Check interference with defs.
152 for (const MachineOperand &MO : MI.operands()) {
153 // TODO: Prologue/Epilogue Insertion pass does not process bundled
154 // instructions.
155 if (MO.isFI())
156 return false;
157
158 if (!MO.isReg())
159 continue;
160
161 Register Reg = MO.getReg();
162
163 // If it is tied we will need to write same register as we read.
164 if (MO.isTied())
165 return false;
166
167 const RegUse &Map = MO.isDef() ? Uses : Defs;
168 auto Conflict = Map.find(Reg);
169 if (Conflict == Map.end())
170 continue;
171
172 if (Reg.isPhysical())
173 return false;
174
175 LaneBitmask Mask = TRI->getSubRegIndexLaneMask(MO.getSubReg());
176 if ((Conflict->second.second & Mask).any())
177 return false;
178 }
179
180 return true;
181}
182
183// Since all defs in the clause are early clobber we can run out of registers.
184// Function returns false if pressure would hit the limit if instruction is
185// bundled into a memory clause.
186bool SIFormMemoryClausesImpl::checkPressure(const MachineInstr &MI,
187 GCNDownwardRPTracker &RPT) {
188 // NB: do not retire the uses of MI. Since all defs will be marked
189 // early-clobber they will all stay alive at least to the end of the clause.
190 // Therefore we should not decrease pressure even if load pointer becomes
191 // dead and could otherwise be reused for destination. The tracker is reset
192 // after the clause is formed, so its position does not need to be updated.
193 RPT.addDefs(MI);
194 GCNRegPressure MaxPressure = RPT.moveMaxPressure();
195 unsigned Occupancy = MaxPressure.getOccupancy(
196 *ST,
197 MI.getMF()->getInfo<SIMachineFunctionInfo>()->getDynamicVGPRBlockSize());
198
199 // Don't push over half the register budget. We don't want to introduce
200 // spilling just to form a soft clause.
201 //
202 // FIXME: This pressure check is fundamentally broken. First, this is checking
203 // the global pressure, not the pressure at this specific point in the
204 // program. Second, it's not accounting for the increased liveness of the use
205 // operands due to the early clobber we will introduce. Third, the pressure
206 // tracking does not account for the alignment requirements for SGPRs, or the
207 // fragmentation of registers the allocator will need to satisfy.
208 if (Occupancy >= MFI->getMinAllowedOccupancy() &&
209 MaxPressure.getVGPRNum(ST->hasGFX90AInsts()) <= MaxVGPRs / 2 &&
210 MaxPressure.getSGPRNum() <= MaxSGPRs / 2) {
211 LastRecordedOccupancy = Occupancy;
212 return true;
213 }
214 return false;
215}
216
217// Collect register defs and uses along with their lane masks and states.
218void SIFormMemoryClausesImpl::collectRegUses(const MachineInstr &MI,
219 RegUse &Defs, RegUse &Uses) const {
220 for (const MachineOperand &MO : MI.operands()) {
221 if (!MO.isReg())
222 continue;
223 Register Reg = MO.getReg();
224 if (!Reg)
225 continue;
226
227 LaneBitmask Mask = Reg.isVirtual()
228 ? TRI->getSubRegIndexLaneMask(MO.getSubReg())
230 RegUse &Map = MO.isDef() ? Defs : Uses;
231
232 RegState State = getMopState(MO);
233 auto [Loc, Inserted] = Map.try_emplace(Reg, State, Mask);
234 if (!Inserted) {
235 Loc->second.first |= State;
236 Loc->second.second |= Mask;
237 }
238 }
239}
240
241// Check register def/use conflicts, occupancy limits and collect def/use maps.
242// Return true if instruction can be bundled with previous. If it cannot
243// def/use maps are not updated.
244bool SIFormMemoryClausesImpl::processRegUses(const MachineInstr &MI,
245 RegUse &Defs, RegUse &Uses,
246 GCNDownwardRPTracker &RPT) {
247 if (!canBundle(MI, Defs, Uses))
248 return false;
249
250 if (!checkPressure(MI, RPT))
251 return false;
252
253 collectRegUses(MI, Defs, Uses);
254 return true;
255}
256
257bool SIFormMemoryClausesImpl::run(MachineFunction &MF) {
258 ST = &MF.getSubtarget<GCNSubtarget>();
259 if (!ST->isXNACKEnabled())
260 return false;
261
262 const SIInstrInfo *TII = ST->getInstrInfo();
263 TRI = ST->getRegisterInfo();
264 MRI = &MF.getRegInfo();
265 MFI = MF.getInfo<SIMachineFunctionInfo>();
266 SlotIndexes *Ind = LIS->getSlotIndexes();
267 bool Changed = false;
268
269 MaxVGPRs = TRI->getAllocatableSet(MF, &AMDGPU::VGPR_32RegClass).count();
270 MaxSGPRs = TRI->getAllocatableSet(MF, &AMDGPU::SGPR_32RegClass).count();
271 unsigned FuncMaxClause = MF.getFunction().getFnAttributeAsParsedInteger(
272 "amdgpu-max-memory-clause", MaxClause);
273
274 for (MachineBasicBlock &MBB : MF) {
275 GCNDownwardRPTracker RPT(*LIS);
277 for (auto I = MBB.instr_begin(), E = MBB.instr_end(); I != E; I = Next) {
278 MachineInstr &MI = *I;
279 Next = std::next(I);
280
281 if (MI.isMetaInstruction())
282 continue;
283
284 bool IsVMEM = isVMEMClauseInst(MI);
285
286 if (!isValidClauseInst(MI, IsVMEM))
287 continue;
288
289 if (!RPT.getNext().isValid())
290 RPT.reset(MI, MBB.end());
291 else { // Advance the state to the current MI.
293 RPT.advanceBeforeNext();
294 }
295
296 const GCNRPTracker::LiveRegSet LiveRegsCopy(RPT.getLiveRegs());
297 RegUse Defs, Uses;
298 if (!processRegUses(MI, Defs, Uses, RPT)) {
299 RPT.reset(MI, MBB.end(), &LiveRegsCopy);
300 continue;
301 }
302
303 MachineBasicBlock::iterator LastClauseInst = Next;
304 unsigned Length = 1;
305 for ( ; Next != E && Length < FuncMaxClause; ++Next) {
306 // Debug instructions should not change the kill insertion.
307 if (Next->isMetaInstruction())
308 continue;
309
310 if (!isValidClauseInst(*Next, IsVMEM))
311 break;
312
313 // A load from pointer which was loaded inside the same bundle is an
314 // impossible clause because we will need to write and read the same
315 // register inside. In this case processRegUses will return false.
316 if (!processRegUses(*Next, Defs, Uses, RPT))
317 break;
318
319 LastClauseInst = Next;
320 ++Length;
321 }
322 if (Length < 2) {
323 RPT.reset(MI, MBB.end(), &LiveRegsCopy);
324 continue;
325 }
326
327 Changed = true;
328 MFI->limitOccupancy(LastRecordedOccupancy);
329
330 assert(!LastClauseInst->isMetaInstruction());
331
332 SlotIndex ClauseLiveInIdx = LIS->getInstructionIndex(MI);
333 SlotIndex ClauseLiveOutIdx =
334 LIS->getInstructionIndex(*LastClauseInst).getNextIndex();
335
336 // Track the last inserted kill.
337 MachineInstrBuilder Kill;
338
339 // Insert one kill per register, with operands covering all necessary
340 // subregisters.
341 for (auto &&R : Uses) {
342 Register Reg = R.first;
343 if (Reg.isPhysical())
344 continue;
345
346 // Collect the register operands we should extend the live ranges of.
348 const LiveInterval &LI = LIS->getInterval(R.first);
349
350 if (!LI.hasSubRanges()) {
351 if (!LI.liveAt(ClauseLiveOutIdx)) {
352 KillOps.emplace_back(R.second.first, AMDGPU::NoSubRegister);
353 }
354 } else {
355 LaneBitmask KilledMask;
356 for (const LiveInterval::SubRange &SR : LI.subranges()) {
357 if (SR.liveAt(ClauseLiveInIdx) && !SR.liveAt(ClauseLiveOutIdx))
358 KilledMask |= SR.LaneMask;
359 }
360
361 if (KilledMask.none())
362 continue;
363
364 SmallVector<unsigned> KilledIndexes;
365 bool Success = TRI->getCoveringSubRegIndexes(
366 MRI->getRegClass(Reg), KilledMask, KilledIndexes);
367 (void)Success;
368 assert(Success && "Failed to find subregister mask to cover lanes");
369 for (unsigned SubReg : KilledIndexes) {
370 KillOps.emplace_back(R.second.first, SubReg);
371 }
372 }
373
374 if (KillOps.empty())
375 continue;
376
377 // We only want to extend the live ranges of used registers. If they
378 // already have existing uses beyond the bundle, we don't need the kill.
379 //
380 // It's possible all of the use registers were already live past the
381 // bundle.
382 Kill = BuildMI(*MI.getParent(), std::next(LastClauseInst),
383 DebugLoc(), TII->get(AMDGPU::KILL));
384 for (auto [State, SubReg] : KillOps)
385 Kill.addUse(Reg, State, SubReg);
386 Ind->insertMachineInstrInMaps(*Kill);
387 }
388
389 // Restore the state after processing the end of the bundle.
390 RPT.reset(MI, MBB.end(), &LiveRegsCopy);
391
392 if (!Kill)
393 continue;
394
395 for (auto &&R : Defs) {
396 Register Reg = R.first;
397 Uses.erase(Reg);
398 if (Reg.isPhysical())
399 continue;
400 LIS->removeInterval(Reg);
402 }
403
404 for (auto &&R : Uses) {
405 Register Reg = R.first;
406 if (Reg.isPhysical())
407 continue;
408 LIS->removeInterval(Reg);
410 }
411 }
412 }
413
414 return Changed;
415}
416
417bool SIFormMemoryClausesLegacy::runOnMachineFunction(MachineFunction &MF) {
418 if (skipFunction(MF.getFunction()))
419 return false;
420
421 LiveIntervals *LIS = &getAnalysis<LiveIntervalsWrapperPass>().getLIS();
422 return SIFormMemoryClausesImpl(LIS).run(MF);
423}
424
425PreservedAnalyses
429 SIFormMemoryClausesImpl(&LIS).run(MF);
430 return PreservedAnalyses::all();
431}
#define Success
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
aarch64 promote const
MachineBasicBlock & MBB
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
This file defines the GCNRegPressure class, which tracks registry pressure by bookkeeping number of S...
#define DEBUG_TYPE
const HexagonInstrInfo * TII
IRTranslator LLVM IR MI
#define I(x, y, z)
Definition MD5.cpp:57
Register Reg
Register const TargetRegisterInfo * TRI
Promote Memory to Register
Definition Mem2Reg.cpp:110
#define INITIALIZE_PASS_DEPENDENCY(depName)
Definition PassSupport.h:42
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition PassSupport.h:44
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition PassSupport.h:39
MachineRegisterInfo & MRI
Remove Loads Into Fake Uses
static cl::opt< unsigned > MaxClause("amdgpu-max-memory-clause", cl::Hidden, cl::init(15), cl::desc("Maximum length of a memory clause, instructions"))
static bool isVMEMClauseInst(const MachineInstr &MI)
static bool isValidClauseInst(const MachineInstr &MI, bool IsVMEMClause)
static RegState getMopState(const MachineOperand &MO)
static bool isSMEMClauseInst(const MachineInstr &MI)
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
AnalysisUsage & addRequired()
void setPreservesAll()
Set by analyses that do not transform their input at all.
uint64_t getFnAttributeAsParsedInteger(StringRef Kind, uint64_t Default=0) const
For a string attribute Kind, parse attribute as an integer.
Definition Function.cpp:778
Downward RP tracker that relies on program order to advance down the instruction stream.
bool advance()
Advance to the state at the next MI in program order.
GCNRegPressure moveMaxPressure()
return MaxPressure and clear it.
MachineBasicBlock::const_iterator getNext() const
Returns the next MI that will be advanced over.
void addDefs(const MachineInstr &MI)
Adds all lanes (dead or not) defined by MI's register operands to CurPressure.
bool advanceBeforeNext()
Move to the state right before NextMI or the end of the MBB.
bool reset(const MachineInstr &MI, MachineBasicBlock::const_iterator End, const LiveRegSet *LiveRegsCopy=nullptr)
Reset tracker to the point before the MI (filling LiveRegs upon this point using LiveRegsCopy if prov...
const decltype(LiveRegs) & getLiveRegs() const
DenseMap< unsigned, LaneBitmask > LiveRegSet
const SIInstrInfo * getInstrInfo() const override
const SIRegisterInfo * getRegisterInfo() const override
bool isXNACKEnabled() const
bool hasSubRanges() const
Returns true if subregister liveness information is available.
iterator_range< subrange_iterator > subranges()
LLVM_ABI Result run(MachineFunction &MF, MachineFunctionAnalysisManager &MFAM)
SlotIndexes * getSlotIndexes() const
SlotIndex getInstructionIndex(const MachineInstr &Instr) const
Returns the base index of the given instruction.
LiveInterval & getInterval(Register Reg)
void removeInterval(Register Reg)
Interval removal.
LiveInterval & createAndComputeVirtRegInterval(Register Reg)
bool liveAt(SlotIndex index) const
MachineInstrBundleIterator< const MachineInstr > const_iterator
Instructions::iterator instr_iterator
MachineInstrBundleIterator< MachineInstr > iterator
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.
Function & getFunction()
Return the LLVM function that this machine code represents.
Ty * getInfo()
getInfo - Keep track of various per-function pieces of information for backends that would like to do...
Representation of each machine instruction.
MachineOperand class - Representation of each machine instruction operand.
LLVM_ABI bool isRenamable() const
isRenamable - Returns true if this register may be renamed, i.e.
bool isEarlyClobber() const
Register getReg() const
getReg - Returns the register number.
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
const TargetRegisterClass * getRegClass(Register Reg) const
Return the register class of the specified virtual register.
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
Wrapper class representing virtual and physical registers.
Definition Register.h:20
constexpr bool isVirtual() const
Return true if the specified register number is in the virtual register namespace.
Definition Register.h:79
constexpr bool isPhysical() const
Return true if the specified register number is in the physical register namespace.
Definition Register.h:83
PreservedAnalyses run(MachineFunction &MF, MachineFunctionAnalysisManager &MFAM)
static bool isVMEM(const MachineInstr &MI)
static bool isSMRD(const MachineInstr &MI)
static bool isAtomic(const MachineInstr &MI)
This class keeps track of the SPI_SP_INPUT_ADDR config register, which tells the hardware which inter...
void limitOccupancy(const MachineFunction &MF)
SlotIndex getNextIndex() const
Returns the next index.
reference emplace_back(ArgTypes &&... Args)
constexpr std::underlying_type_t< E > Mask()
Get a bitmask with 1s in all places up to the high-order bit of E's largest value.
initializer< Ty > init(const Ty &Val)
PointerTypeMap run(const Module &M)
Compute the PointerTypeMap for the module M.
This is an optimization pass for GlobalISel generic memory operations.
@ Length
Definition DWP.cpp:577
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
RegState
Flags to represent properties of register accesses.
@ Implicit
Not emitted register (e.g. carry, or temporary result).
@ Dead
Unused definition.
@ Kill
The last use of a register.
@ Undef
Value of the register doesn't matter.
@ EarlyClobber
Register definition happens before uses.
@ Renamable
Register that may be renamed.
AnalysisManager< MachineFunction > MachineFunctionAnalysisManager
char & SIFormMemoryClausesID
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Next
Definition InstrProf.h:147
unsigned getVGPRNum(bool UnifiedVGPRFile) const
unsigned getOccupancy(const GCNSubtarget &ST, unsigned DynamicVGPRBlockSize) const
unsigned getSGPRNum() const
static constexpr LaneBitmask getAll()
Definition LaneBitmask.h:82
constexpr bool none() const
Definition LaneBitmask.h:52