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"
36#include "llvm/ADT/SmallVector.h"
37#include "llvm/CodeGen/MachineFunctionPass.h"
38#include "llvm/CodeGen/MachinePassManager.h"
39
40using namespace llvm;
41
42#define DEBUG_TYPE "si-insert-hard-clauses"
43
44static cl::opt<unsigned>
45 HardClauseLengthLimit("amdgpu-hard-clause-length-limit",
46 cl::desc("Maximum number of memory instructions to "
47 "place in the same hard clause"),
48 cl::Hidden);
49
50namespace {
51
52enum HardClauseType {
53 // For targets where loads, stores and atomics share one clause type per
54 // memory class (see hasMixedLoadStoreClauses):
55
56 // Texture, buffer, global or scratch memory instructions.
57 HARDCLAUSE_VMEM,
58 // Flat (not global or scratch) memory instructions.
59 HARDCLAUSE_FLAT,
60
61 // For targets where each memory class is typed separately for loads,
62 // stores and atomics:
63
64 // Texture memory instructions.
65 HARDCLAUSE_MIMG_LOAD,
66 HARDCLAUSE_MIMG_STORE,
67 HARDCLAUSE_MIMG_ATOMIC,
68 HARDCLAUSE_MIMG_SAMPLE,
69 // Buffer, global or scratch memory instructions.
70 HARDCLAUSE_VMEM_LOAD,
71 HARDCLAUSE_VMEM_STORE,
72 HARDCLAUSE_VMEM_ATOMIC,
73 // Flat (not global or scratch) memory instructions.
74 HARDCLAUSE_FLAT_LOAD,
75 HARDCLAUSE_FLAT_STORE,
76 HARDCLAUSE_FLAT_ATOMIC,
77 // BVH instructions.
78 HARDCLAUSE_BVH,
79
80 // Common:
81
82 // Instructions that access LDS.
83 HARDCLAUSE_LDS,
84 // Scalar memory instructions.
85 HARDCLAUSE_SMEM,
86 // VALU instructions.
87 HARDCLAUSE_VALU,
88 LAST_REAL_HARDCLAUSE_TYPE = HARDCLAUSE_VALU,
89
90 // Internal instructions, which are allowed in the middle of a hard clause,
91 // except for s_waitcnt.
92 HARDCLAUSE_INTERNAL,
93 // Meta instructions that do not result in any ISA like KILL.
94 HARDCLAUSE_IGNORE,
95 // Instructions that are not allowed in a hard clause: SALU, export, branch,
96 // message, GDS, s_waitcnt and anything else not mentioned above.
97 HARDCLAUSE_ILLEGAL,
98};
99
100class SIInsertHardClauses {
101public:
102 const GCNSubtarget *ST = nullptr;
103
104 HardClauseType getHardClauseType(const MachineInstr &MI) {
105 if (MI.mayLoad() || (MI.mayStore() && ST->shouldClusterStores())) {
106 if (ST->hasMixedLoadStoreClauses()) {
107 if ((SIInstrInfo::isVMEM(MI) && !SIInstrInfo::isFLAT(MI)) ||
108 SIInstrInfo::isSegmentSpecificFLAT(MI)) {
109 if (ST->hasNSAClauseBug()) {
110 const AMDGPU::MIMGInfo *Info = AMDGPU::getMIMGInfo(Opc: MI.getOpcode());
111 if (Info && Info->MIMGEncoding == AMDGPU::MIMGEncGfx10NSA)
112 return HARDCLAUSE_ILLEGAL;
113 }
114 return HARDCLAUSE_VMEM;
115 }
116 if (SIInstrInfo::isFLAT(MI))
117 return HARDCLAUSE_FLAT;
118 } else {
119 if (SIInstrInfo::isMIMG(MI)) {
120 const AMDGPU::MIMGInfo *Info = AMDGPU::getMIMGInfo(Opc: MI.getOpcode());
121 const AMDGPU::MIMGBaseOpcodeInfo *BaseInfo =
122 AMDGPU::getMIMGBaseOpcodeInfo(BaseOpcode: Info->BaseOpcode);
123 if (BaseInfo->BVH)
124 return HARDCLAUSE_BVH;
125 if (BaseInfo->Sampler || BaseInfo->MSAA)
126 return HARDCLAUSE_MIMG_SAMPLE;
127 return MI.mayLoad() ? MI.mayStore() ? HARDCLAUSE_MIMG_ATOMIC
128 : HARDCLAUSE_MIMG_LOAD
129 : HARDCLAUSE_MIMG_STORE;
130 }
131 if ((SIInstrInfo::isVMEM(MI) && !SIInstrInfo::isFLAT(MI)) ||
132 SIInstrInfo::isSegmentSpecificFLAT(MI)) {
133 return MI.mayLoad() ? MI.mayStore() ? HARDCLAUSE_VMEM_ATOMIC
134 : HARDCLAUSE_VMEM_LOAD
135 : HARDCLAUSE_VMEM_STORE;
136 }
137 if (SIInstrInfo::isFLAT(MI)) {
138 return MI.mayLoad() ? MI.mayStore() ? HARDCLAUSE_FLAT_ATOMIC
139 : HARDCLAUSE_FLAT_LOAD
140 : HARDCLAUSE_FLAT_STORE;
141 }
142 }
143 // TODO: LDS
144 if (SIInstrInfo::isSMRD(MI))
145 return HARDCLAUSE_SMEM;
146 }
147
148 // Don't form VALU clauses. It's not clear what benefit they give, if any.
149
150 // In practice s_nop is the only internal instruction we're likely to see.
151 // It's safe to treat the rest as illegal.
152 if (MI.getOpcode() == AMDGPU::S_NOP)
153 return HARDCLAUSE_INTERNAL;
154 if (MI.isMetaInstruction())
155 return HARDCLAUSE_IGNORE;
156 return HARDCLAUSE_ILLEGAL;
157 }
158
159 // Track information about a clause as we discover it.
160 struct ClauseInfo {
161 // The type of all (non-internal) instructions in the clause.
162 HardClauseType Type = HARDCLAUSE_ILLEGAL;
163 // The first (necessarily non-internal) instruction in the clause.
164 MachineInstr *First = nullptr;
165 // The last non-internal instruction in the clause.
166 MachineInstr *Last = nullptr;
167 // The length of the clause including any internal instructions in the
168 // middle (but not at the end) of the clause.
169 unsigned Length = 0;
170 // Internal instructions at the and of a clause should not be included in
171 // the clause. Count them in TrailingInternalLength until a new memory
172 // instruction is added.
173 unsigned TrailingInternalLength = 0;
174 // The base operands of *Last.
175 SmallVector<const MachineOperand *, 4> BaseOps;
176 };
177
178 bool emitClause(const ClauseInfo &CI, const SIInstrInfo *SII) {
179 if (CI.First == CI.Last)
180 return false;
181 assert(CI.Length <= ST->maxHardClauseLength() &&
182 "Hard clause is too long!");
183
184 auto &MBB = *CI.First->getParent();
185 auto ClauseMI =
186 BuildMI(BB&: MBB, I&: *CI.First, MIMD: DebugLoc(), MCID: SII->get(Opcode: AMDGPU::S_CLAUSE))
187 .addImm(Val: CI.Length - 1);
188 finalizeBundle(MBB, FirstMI: ClauseMI->getIterator(),
189 LastMI: std::next(x: CI.Last->getIterator()));
190 return true;
191 }
192
193 // \return if scopes are different on gfx1250 and disallowed to be claused.
194 bool isIncompatibleScope(const MachineInstr &MI1, const MachineInstr &MI2,
195 const SIInstrInfo *SII) const {
196 assert(ST->getGeneration() == AMDGPUSubtarget::GFX12 &&
197 ST->hasGFX1250Insts());
198 int CPol1 = 0, CPol2 = 0;
199 if (const MachineOperand *Op =
200 SII->getNamedOperand(MI: MI1, OperandName: AMDGPU::OpName::cpol)) {
201 CPol1 = Op->getImm() & AMDGPU::CPol::SCOPE;
202 }
203 if (const MachineOperand *Op =
204 SII->getNamedOperand(MI: MI2, OperandName: AMDGPU::OpName::cpol)) {
205 CPol2 = Op->getImm() & AMDGPU::CPol::SCOPE;
206 }
207 return CPol1 != CPol2;
208 }
209
210 bool run(MachineFunction &MF) {
211 ST = &MF.getSubtarget<GCNSubtarget>();
212 if (!ST->hasHardClauses())
213 return false;
214
215 unsigned MaxClauseLength = MF.getFunction().getFnAttributeAsParsedInteger(
216 Kind: "amdgpu-hard-clause-length-limit", Default: 255);
217 if (HardClauseLengthLimit.getNumOccurrences())
218 MaxClauseLength = HardClauseLengthLimit;
219 MaxClauseLength = std::min(a: MaxClauseLength, b: ST->maxHardClauseLength());
220 if (MaxClauseLength <= 1)
221 return false;
222
223 const SIInstrInfo *SII = ST->getInstrInfo();
224
225 bool Changed = false;
226 for (auto &MBB : MF) {
227 ClauseInfo CI;
228 unsigned ExistingClauseRemaining = 0;
229 for (auto &MI : MBB) {
230 HardClauseType Type;
231 if (ExistingClauseRemaining) {
232 if (!MI.isMetaInstruction())
233 ExistingClauseRemaining--;
234 Type = HARDCLAUSE_ILLEGAL;
235 } else if (MI.getOpcode() == AMDGPU::S_CLAUSE) {
236 // Respect existing explicit clauses. Re-clausing instructions that
237 // are already covered by an S_CLAUSE can create nested clauses.
238 ExistingClauseRemaining = (MI.getOperand(i: 0).getImm() & 63) + 1;
239 Type = HARDCLAUSE_ILLEGAL;
240 } else {
241 Type = getHardClauseType(MI);
242 }
243
244 int64_t Dummy1;
245 bool Dummy2;
246 LocationSize Dummy3 = LocationSize::precise(Value: 0);
247 SmallVector<const MachineOperand *, 4> BaseOps;
248 if (Type <= LAST_REAL_HARDCLAUSE_TYPE) {
249 if (!SII->getMemOperandsWithOffsetWidth(LdSt: MI, BaseOps, Offset&: Dummy1, OffsetIsScalable&: Dummy2,
250 Width&: Dummy3)) {
251 // We failed to get the base operands, so we'll never clause this
252 // instruction with any other, so pretend it's illegal.
253 Type = HARDCLAUSE_ILLEGAL;
254 }
255 }
256
257 if (CI.Length == MaxClauseLength ||
258 (CI.Length && Type != HARDCLAUSE_INTERNAL &&
259 Type != HARDCLAUSE_IGNORE &&
260 (Type != CI.Type ||
261 // shouldClusterMemOps() would also apply the pre-RA memory
262 // cluster budget, which must not limit clauses formed after
263 // register allocation.
264 !SII->memOpsHaveSameBase(BaseOps1: CI.BaseOps, BaseOps2: BaseOps))) ||
265 (CI.Length && ST->hasGFX1250_STRICT() &&
266 isIncompatibleScope(MI1: MI, MI2: *CI.Last, SII))) {
267 // Finish the current clause.
268 Changed |= emitClause(CI, SII);
269 CI = ClauseInfo();
270 }
271
272 if (CI.Length) {
273 // Extend the current clause.
274 if (Type != HARDCLAUSE_IGNORE) {
275 if (Type == HARDCLAUSE_INTERNAL) {
276 ++CI.TrailingInternalLength;
277 } else {
278 ++CI.Length;
279 CI.Length += CI.TrailingInternalLength;
280 CI.TrailingInternalLength = 0;
281 CI.Last = &MI;
282 CI.BaseOps = std::move(BaseOps);
283 }
284 }
285 } else if (Type <= LAST_REAL_HARDCLAUSE_TYPE) {
286 // Start a new clause.
287 CI = ClauseInfo{.Type: Type, .First: &MI, .Last: &MI, .Length: 1, .TrailingInternalLength: 0, .BaseOps: std::move(BaseOps)};
288 }
289 }
290
291 // Finish the last clause in the basic block if any.
292 if (CI.Length)
293 Changed |= emitClause(CI, SII);
294 }
295
296 return Changed;
297 }
298};
299
300class SIInsertHardClausesLegacy : public MachineFunctionPass {
301public:
302 static char ID;
303 SIInsertHardClausesLegacy() : MachineFunctionPass(ID) {}
304
305 bool runOnMachineFunction(MachineFunction &MF) override {
306 if (skipFunction(F: MF.getFunction()))
307 return false;
308
309 return SIInsertHardClauses().run(MF);
310 }
311
312 void getAnalysisUsage(AnalysisUsage &AU) const override {
313 AU.setPreservesCFG();
314 MachineFunctionPass::getAnalysisUsage(AU);
315 }
316};
317
318} // namespace
319
320PreservedAnalyses
321llvm::SIInsertHardClausesPass::run(MachineFunction &MF,
322 MachineFunctionAnalysisManager &MFAM) {
323 if (!SIInsertHardClauses().run(MF))
324 return PreservedAnalyses::all();
325
326 auto PA = getMachineFunctionPassPreservedAnalyses();
327 PA.preserveSet<CFGAnalyses>();
328 return PA;
329}
330
331char SIInsertHardClausesLegacy::ID = 0;
332
333char &llvm::SIInsertHardClausesID = SIInsertHardClausesLegacy::ID;
334
335INITIALIZE_PASS(SIInsertHardClausesLegacy, DEBUG_TYPE, "SI Insert Hard Clauses",
336 false, false)
337