LLVM 24.0.0git
SIInsertHardClauses.cpp
Go to the documentation of this file.
1//===- SIInsertHardClauses.cpp - Insert Hard Clauses ----------------------===//
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
10/// Insert s_clause instructions to form hard clauses.
11///
12/// Clausing load instructions can give cache coherency benefits. Before gfx10,
13/// the hardware automatically detected "soft clauses", which were sequences of
14/// memory instructions of the same type. In gfx10 this detection was removed,
15/// and the s_clause instruction was introduced to explicitly mark "hard
16/// clauses".
17///
18/// It's the scheduler's job to form the clauses by putting similar memory
19/// instructions next to each other. Our job is just to insert an s_clause
20/// instruction to mark the start of each clause.
21///
22/// Note that hard clauses are very similar to, but logically distinct from, the
23/// groups of instructions that have to be restartable when XNACK is enabled.
24/// The rules are slightly different in each case. For example an s_nop
25/// instruction breaks a restartable group, but can appear in the middle of a
26/// hard clause. (Before gfx10 there wasn't a distinction, and both were called
27/// "soft clauses" or just "clauses".)
28///
29/// The SIFormMemoryClauses pass and GCNHazardRecognizer deal with restartable
30/// groups, not hard clauses.
31//
32//===----------------------------------------------------------------------===//
33
34#include "AMDGPU.h"
35#include "GCNSubtarget.h"
39
40using namespace llvm;
41
42#define DEBUG_TYPE "si-insert-hard-clauses"
43
44namespace {
45
46enum HardClauseType {
47 // For targets where loads, stores and atomics share one clause type per
48 // memory class (see hasMixedLoadStoreClauses):
49
50 // Texture, buffer, global or scratch memory instructions.
51 HARDCLAUSE_VMEM,
52 // Flat (not global or scratch) memory instructions.
53 HARDCLAUSE_FLAT,
54
55 // For targets where each memory class is typed separately for loads,
56 // stores and atomics:
57
58 // Texture memory instructions.
59 HARDCLAUSE_MIMG_LOAD,
60 HARDCLAUSE_MIMG_STORE,
61 HARDCLAUSE_MIMG_ATOMIC,
62 HARDCLAUSE_MIMG_SAMPLE,
63 // Buffer, global or scratch memory instructions.
64 HARDCLAUSE_VMEM_LOAD,
65 HARDCLAUSE_VMEM_STORE,
66 HARDCLAUSE_VMEM_ATOMIC,
67 // Flat (not global or scratch) memory instructions.
68 HARDCLAUSE_FLAT_LOAD,
69 HARDCLAUSE_FLAT_STORE,
70 HARDCLAUSE_FLAT_ATOMIC,
71 // BVH instructions.
72 HARDCLAUSE_BVH,
73
74 // Common:
75
76 // Instructions that access LDS.
77 HARDCLAUSE_LDS,
78 // Scalar memory instructions.
79 HARDCLAUSE_SMEM,
80 // VALU instructions.
81 HARDCLAUSE_VALU,
82 LAST_REAL_HARDCLAUSE_TYPE = HARDCLAUSE_VALU,
83
84 // Internal instructions, which are allowed in the middle of a hard clause,
85 // except for s_waitcnt.
86 HARDCLAUSE_INTERNAL,
87 // Meta instructions that do not result in any ISA like KILL.
88 HARDCLAUSE_IGNORE,
89 // Instructions that are not allowed in a hard clause: SALU, export, branch,
90 // message, GDS, s_waitcnt and anything else not mentioned above.
91 HARDCLAUSE_ILLEGAL,
92};
93
94class SIInsertHardClauses {
95public:
96 const GCNSubtarget *ST = nullptr;
97
98 HardClauseType getHardClauseType(const MachineInstr &MI) {
99 if (MI.mayLoad() || (MI.mayStore() && ST->shouldClusterStores())) {
100 if (ST->hasMixedLoadStoreClauses()) {
103 if (ST->hasNSAClauseBug()) {
104 const AMDGPU::MIMGInfo *Info = AMDGPU::getMIMGInfo(MI.getOpcode());
105 if (Info && Info->MIMGEncoding == AMDGPU::MIMGEncGfx10NSA)
106 return HARDCLAUSE_ILLEGAL;
107 }
108 return HARDCLAUSE_VMEM;
109 }
111 return HARDCLAUSE_FLAT;
112 } else {
113 if (SIInstrInfo::isMIMG(MI)) {
114 const AMDGPU::MIMGInfo *Info = AMDGPU::getMIMGInfo(MI.getOpcode());
115 const AMDGPU::MIMGBaseOpcodeInfo *BaseInfo =
116 AMDGPU::getMIMGBaseOpcodeInfo(Info->BaseOpcode);
117 if (BaseInfo->BVH)
118 return HARDCLAUSE_BVH;
119 if (BaseInfo->Sampler || BaseInfo->MSAA)
120 return HARDCLAUSE_MIMG_SAMPLE;
121 return MI.mayLoad() ? MI.mayStore() ? HARDCLAUSE_MIMG_ATOMIC
122 : HARDCLAUSE_MIMG_LOAD
123 : HARDCLAUSE_MIMG_STORE;
124 }
127 return MI.mayLoad() ? MI.mayStore() ? HARDCLAUSE_VMEM_ATOMIC
128 : HARDCLAUSE_VMEM_LOAD
129 : HARDCLAUSE_VMEM_STORE;
130 }
131 if (SIInstrInfo::isFLAT(MI)) {
132 return MI.mayLoad() ? MI.mayStore() ? HARDCLAUSE_FLAT_ATOMIC
133 : HARDCLAUSE_FLAT_LOAD
134 : HARDCLAUSE_FLAT_STORE;
135 }
136 }
137 // TODO: LDS
139 return HARDCLAUSE_SMEM;
140 }
141
142 // Don't form VALU clauses. It's not clear what benefit they give, if any.
143
144 // In practice s_nop is the only internal instruction we're likely to see.
145 // It's safe to treat the rest as illegal.
146 if (MI.getOpcode() == AMDGPU::S_NOP)
147 return HARDCLAUSE_INTERNAL;
148 if (MI.isMetaInstruction())
149 return HARDCLAUSE_IGNORE;
150 return HARDCLAUSE_ILLEGAL;
151 }
152
153 // Track information about a clause as we discover it.
154 struct ClauseInfo {
155 // The type of all (non-internal) instructions in the clause.
156 HardClauseType Type = HARDCLAUSE_ILLEGAL;
157 // The first (necessarily non-internal) instruction in the clause.
158 MachineInstr *First = nullptr;
159 // The last non-internal instruction in the clause.
160 MachineInstr *Last = nullptr;
161 // The length of the clause including any internal instructions in the
162 // middle (but not at the end) of the clause.
163 unsigned Length = 0;
164 // Internal instructions at the and of a clause should not be included in
165 // the clause. Count them in TrailingInternalLength until a new memory
166 // instruction is added.
167 unsigned TrailingInternalLength = 0;
168 // The base operands of *Last.
170 };
171
172 bool emitClause(const ClauseInfo &CI, const SIInstrInfo *SII) {
173 if (CI.First == CI.Last)
174 return false;
175 assert(CI.Length <= ST->maxHardClauseLength() &&
176 "Hard clause is too long!");
177
178 auto &MBB = *CI.First->getParent();
179 auto ClauseMI =
180 BuildMI(MBB, *CI.First, DebugLoc(), SII->get(AMDGPU::S_CLAUSE))
181 .addImm(CI.Length - 1);
182 finalizeBundle(MBB, ClauseMI->getIterator(),
183 std::next(CI.Last->getIterator()));
184 return true;
185 }
186
187 // \return if scopes are different on gfx1250 and disallowed to be claused.
188 bool isIncompatibleScope(const MachineInstr &MI1, const MachineInstr &MI2,
189 const SIInstrInfo *SII) const {
190 assert(ST->getGeneration() == AMDGPUSubtarget::GFX12 &&
191 ST->hasGFX1250Insts());
192 int CPol1 = 0, CPol2 = 0;
193 if (const MachineOperand *Op =
194 SII->getNamedOperand(MI1, AMDGPU::OpName::cpol)) {
195 CPol1 = Op->getImm() & AMDGPU::CPol::SCOPE;
196 }
197 if (const MachineOperand *Op =
198 SII->getNamedOperand(MI2, AMDGPU::OpName::cpol)) {
199 CPol2 = Op->getImm() & AMDGPU::CPol::SCOPE;
200 }
201 return CPol1 != CPol2;
202 }
203
204 bool run(MachineFunction &MF) {
205 ST = &MF.getSubtarget<GCNSubtarget>();
206 if (!ST->hasHardClauses())
207 return false;
208
209 unsigned MaxClauseLength =
210 ST->getCLOpts().hard_clause_length_limit.value_or(
212 "amdgpu-hard-clause-length-limit", 255));
213 MaxClauseLength = std::min(MaxClauseLength, ST->maxHardClauseLength());
214 if (MaxClauseLength <= 1)
215 return false;
216
217 const SIInstrInfo *SII = ST->getInstrInfo();
218
219 bool Changed = false;
220 for (auto &MBB : MF) {
221 ClauseInfo CI;
222 unsigned ExistingClauseRemaining = 0;
223 for (auto &MI : MBB) {
224 HardClauseType Type;
225 if (ExistingClauseRemaining) {
226 if (!MI.isMetaInstruction())
227 ExistingClauseRemaining--;
228 Type = HARDCLAUSE_ILLEGAL;
229 } else if (MI.getOpcode() == AMDGPU::S_CLAUSE) {
230 // Respect existing explicit clauses. Re-clausing instructions that
231 // are already covered by an S_CLAUSE can create nested clauses.
232 ExistingClauseRemaining = (MI.getOperand(0).getImm() & 63) + 1;
233 Type = HARDCLAUSE_ILLEGAL;
234 } else {
235 Type = getHardClauseType(MI);
236 }
237
238 int64_t Dummy1;
239 bool Dummy2;
242 if (Type <= LAST_REAL_HARDCLAUSE_TYPE) {
243 if (!SII->getMemOperandsWithOffsetWidth(MI, BaseOps, Dummy1, Dummy2,
244 Dummy3)) {
245 // We failed to get the base operands, so we'll never clause this
246 // instruction with any other, so pretend it's illegal.
247 Type = HARDCLAUSE_ILLEGAL;
248 }
249 }
250
251 if (CI.Length == MaxClauseLength ||
252 (CI.Length && Type != HARDCLAUSE_INTERNAL &&
253 Type != HARDCLAUSE_IGNORE &&
254 (Type != CI.Type ||
255 // shouldClusterMemOps() would also apply the pre-RA memory
256 // cluster budget, which must not limit clauses formed after
257 // register allocation.
258 !SII->memOpsHaveSameBase(CI.BaseOps, BaseOps))) ||
259 (CI.Length && ST->hasGFX1250_STRICT() &&
260 isIncompatibleScope(MI, *CI.Last, SII))) {
261 // Finish the current clause.
262 Changed |= emitClause(CI, SII);
263 CI = ClauseInfo();
264 }
265
266 if (CI.Length) {
267 // Extend the current clause.
268 if (Type != HARDCLAUSE_IGNORE) {
269 if (Type == HARDCLAUSE_INTERNAL) {
270 ++CI.TrailingInternalLength;
271 } else {
272 ++CI.Length;
273 CI.Length += CI.TrailingInternalLength;
274 CI.TrailingInternalLength = 0;
275 CI.Last = &MI;
276 CI.BaseOps = std::move(BaseOps);
277 }
278 }
279 } else if (Type <= LAST_REAL_HARDCLAUSE_TYPE) {
280 // Start a new clause.
281 CI = ClauseInfo{Type, &MI, &MI, 1, 0, std::move(BaseOps)};
282 }
283 }
284
285 // Finish the last clause in the basic block if any.
286 if (CI.Length)
287 Changed |= emitClause(CI, SII);
288 }
289
290 return Changed;
291 }
292};
293
294class SIInsertHardClausesLegacy : public MachineFunctionPass {
295public:
296 static char ID;
297 SIInsertHardClausesLegacy() : MachineFunctionPass(ID) {}
298
299 bool runOnMachineFunction(MachineFunction &MF) override {
300 if (skipFunction(MF.getFunction()))
301 return false;
302
303 return SIInsertHardClauses().run(MF);
304 }
305
306 void getAnalysisUsage(AnalysisUsage &AU) const override {
307 AU.setPreservesCFG();
309 }
310};
311
312} // namespace
313
317 if (!SIInsertHardClauses().run(MF))
318 return PreservedAnalyses::all();
319
321 PA.preserveSet<CFGAnalyses>();
322 return PA;
323}
324
325char SIInsertHardClausesLegacy::ID = 0;
326
327char &llvm::SIInsertHardClausesID = SIInsertHardClausesLegacy::ID;
328
329INITIALIZE_PASS(SIInsertHardClausesLegacy, DEBUG_TYPE, "SI Insert Hard Clauses",
330 false, false)
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock & MBB
AMD GCN specific subclass of TargetSubtarget.
#define DEBUG_TYPE
IRTranslator LLVM IR MI
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
This file defines the SmallVector class.
Represent the analysis usage information of a pass.
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
Definition Pass.cpp:280
Represents analyses that only rely on functions' control flow.
Definition Analysis.h:73
A debug info location.
Definition DebugLoc.h:126
uint64_t getFnAttributeAsParsedInteger(StringRef Kind, uint64_t Default=0) const
For a string attribute Kind, parse attribute as an integer.
Definition Function.cpp:778
static LocationSize precise(uint64_t Value)
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.
Function & getFunction()
Return the LLVM function that this machine code represents.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
Representation of each machine instruction.
MachineOperand class - Representation of each machine instruction operand.
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses run(MachineFunction &MF, MachineFunctionAnalysisManager &MFAM)
static bool isVMEM(const MachineInstr &MI)
static bool isSMRD(const MachineInstr &MI)
bool getMemOperandsWithOffsetWidth(const MachineInstr &LdSt, SmallVectorImpl< const MachineOperand * > &BaseOps, int64_t &Offset, bool &OffsetIsScalable, LocationSize &Width) const final
static bool isSegmentSpecificFLAT(const MachineInstr &MI)
static bool isMIMG(const MachineInstr &MI)
bool memOpsHaveSameBase(ArrayRef< const MachineOperand * > BaseOps1, ArrayRef< const MachineOperand * > BaseOps2) const
Return true if the memory operations described by BaseOps1 and BaseOps2 (as returned by getMemOperand...
static bool isFLAT(const MachineInstr &MI)
LLVM_READONLY MachineOperand * getNamedOperand(MachineInstr &MI, AMDGPU::OpName OperandName) const
Returns the operand named Op.
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
LLVM_READONLY const MIMGInfo * getMIMGInfo(unsigned Opc)
LLVM_READONLY const MIMGBaseOpcodeInfo * getMIMGBaseOpcodeInfo(unsigned BaseOpcode)
This is an optimization pass for GlobalISel generic memory operations.
@ Length
Definition DWP.cpp:577
LLVM_ABI void finalizeBundle(MachineBasicBlock &MBB, MachineBasicBlock::instr_iterator FirstMI, MachineBasicBlock::instr_iterator LastMI)
finalizeBundle - Finalize a machine instruction bundle which includes a sequence of instructions star...
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
AnalysisManager< MachineFunction > MachineFunctionAnalysisManager
LLVM_ABI PreservedAnalyses getMachineFunctionPassPreservedAnalyses()
Returns the minimum set of Analyses that all machine function passes must preserve.
MachineInstr * getImm(const MachineOperand &MO, const MachineRegisterInfo *MRI)
@ First
Helpers to iterate all locations in the MemoryEffectsBase class.
Definition ModRef.h:74
DWARFExpression::Operation Op
char & SIInsertHardClausesID