LLVM 24.0.0git
AMDGPUCoExecSchedStrategy.cpp
Go to the documentation of this file.
1//===- AMDGPUCoExecSchedStrategy.cpp - CoExec Scheduling Strategy ---------===//
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/// Coexecution-focused scheduling strategy for AMDGPU.
11//
12//===----------------------------------------------------------------------===//
13
16#include "AMDGPUIGroupLP.h"
17#include "GCNHazardRecognizer.h"
18#include "llvm/Support/Debug.h"
19
20using namespace llvm;
21using namespace llvm::AMDGPU;
22
23#define DEBUG_TYPE "machine-scheduler"
24
26 "amdgpu-block-carried-latency", cl::Hidden, cl::init(CarriedLatency::Off),
27 cl::desc("Estimate block-carried latency and include it in the effective "
28 "candidate stall cost."),
31 "Disabled - do not pad latency."),
33 "Only pad latency for memory fence (e.g. those surrounding "
34 "barrier_signal/wait)."),
37 "Pad latency for any SU with an incoming ds_load dependency.")));
38
39// Default VGPR threshold percent for coexec scheduler.
40static constexpr unsigned DefaultCoExecVGPRThresholdPercent = 100;
41
42namespace {
43
44// Used to disable post-RA scheduling with function level granularity.
45class GCNNoopPostScheduleDAG final : public ScheduleDAGInstrs {
46public:
47 explicit GCNNoopPostScheduleDAG(MachineSchedContext *C)
48 : ScheduleDAGInstrs(*C->MF, C->MLI, /*RemoveKillFlags=*/true) {}
49
50 // Do nothing.
51 void schedule() override {}
52};
53
54} // namespace
55
57 // pickOnlyChoice() releases pending instructions and checks for new hazards.
58 SUnit *OnlyChoice = Zone.pickOnlyChoice();
59 if (!Zone.Pending.empty())
60 return nullptr;
61
62 return OnlyChoice;
63}
64
65/// Apply \p ExtraBits to every slot in \p Info starting with \p StartIndex
66/// Used by MFMA co-exec rules, because MFMA co-exec slots are incremental, i.e.
67/// for every slot N it supports all instructions which were supported by the
68/// previous slot N-1 and may support something extra.
71 unsigned StartIndex) {
72 for (unsigned Index = StartIndex; Index < Info.TotalWindow; ++Index)
73 Info.Slots[Index].Mask |= ExtraBits;
74}
75
76/// Get co-execution info for a gfx950 MFMA instruction.
77/// The occupancy (cycles until the next MFMA may issue) is expressed as the
78/// first stage carrying the WMMA bit.
80 using namespace llvm;
81 using namespace llvm::AMDGPU;
82 CoExecInfo Res;
83 for (unsigned I = 0; I < MaxCoExecStages; ++I)
85
86 // TODO: Implement proper patterns support (for debugging purposes).
87 // Existing pattern letters are WMMA-specific and will probably be confusing
88 // if used as-is for MFMA. Inventing new MFMA-specific letters is an option,
89 // but perhaps the pattern should be instead dynamically reconstructed when
90 // needed by printing specific slots in full instead of a key for them.
91 Res.Pattern = "undefinedundefinedundefinedundefined";
92
93 switch (Opcode) {
94 // 4-cycle occupancy, 8-cycle window.
95 case V_MFMA_F32_16X16X128_F8F6F4_f4_f4_e64:
96 case V_MFMA_F32_16X16X128_F8F6F4_f4_f4_vgprcd_e64:
97 case V_MFMA_F32_16X16X128_F8F6F4_f4_f4_gfx940_acd:
98 case V_MFMA_F32_16X16X128_F8F6F4_f4_f4_gfx940_vcd:
99 case V_MFMA_F32_16X16X128_F8F6F4_f4_f6_e64:
100 case V_MFMA_F32_16X16X128_F8F6F4_f4_f6_vgprcd_e64:
101 case V_MFMA_F32_16X16X128_F8F6F4_f4_f6_gfx940_acd:
102 case V_MFMA_F32_16X16X128_F8F6F4_f4_f6_gfx940_vcd:
103 case V_MFMA_F32_16X16X128_F8F6F4_f6_f4_e64:
104 case V_MFMA_F32_16X16X128_F8F6F4_f6_f4_vgprcd_e64:
105 case V_MFMA_F32_16X16X128_F8F6F4_f6_f4_gfx940_acd:
106 case V_MFMA_F32_16X16X128_F8F6F4_f6_f4_gfx940_vcd:
107 case V_MFMA_F32_16X16X128_F8F6F4_f6_f6_e64:
108 case V_MFMA_F32_16X16X128_F8F6F4_f6_f6_vgprcd_e64:
109 case V_MFMA_F32_16X16X128_F8F6F4_f6_f6_gfx940_acd:
110 case V_MFMA_F32_16X16X128_F8F6F4_f6_f6_gfx940_vcd:
111 case V_MFMA_F32_16X16X32_BF16_e64:
112 case V_MFMA_F32_16X16X32_BF16_vgprcd_e64:
113 case V_MFMA_F32_16X16X32_BF16_gfx940_acd:
114 case V_MFMA_F32_16X16X32_BF16_gfx940_vcd:
115 case V_MFMA_I32_16X16X64_I8_e64:
116 case V_MFMA_I32_16X16X64_I8_vgprcd_e64:
117 case V_MFMA_I32_16X16X64_I8_gfx940_acd:
118 case V_MFMA_I32_16X16X64_I8_gfx940_vcd:
119 case V_MFMA_F32_16X16X32_F16_e64:
120 case V_MFMA_F32_16X16X32_F16_vgprcd_e64:
121 case V_MFMA_F32_16X16X32_F16_gfx940_acd:
122 case V_MFMA_F32_16X16X32_F16_gfx940_vcd:
123 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f4_e64:
124 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f4_vgprcd_e64:
125 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f4_gfx940_acd:
126 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f4_gfx940_vcd:
127 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f6_e64:
128 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f6_vgprcd_e64:
129 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f6_gfx940_acd:
130 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f6_gfx940_vcd:
131 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f4_e64:
132 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f4_vgprcd_e64:
133 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f4_gfx940_acd:
134 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f4_gfx940_vcd:
135 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f6_e64:
136 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f6_vgprcd_e64:
137 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f6_gfx940_acd:
138 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f6_gfx940_vcd:
139 // GFX9 Shader Programming Guide lists those SMFMAC separately, but for
140 // intended purposes here all those instructions are the same. This comment
141 // is to simplify reverse mapping to the SPG.
142 case V_SMFMAC_F32_16X16X64_BF16_e64:
143 case V_SMFMAC_F32_16X16X64_BF16_gfx940:
144 case V_SMFMAC_I32_16X16X128_I8_e64:
145 case V_SMFMAC_I32_16X16X128_I8_gfx940:
146 case V_SMFMAC_F32_16X16X128_BF8_BF8_e64:
147 case V_SMFMAC_F32_16X16X128_BF8_BF8_gfx940:
148 case V_SMFMAC_F32_16X16X128_BF8_FP8_e64:
149 case V_SMFMAC_F32_16X16X128_BF8_FP8_gfx940:
150 case V_SMFMAC_F32_16X16X128_FP8_BF8_e64:
151 case V_SMFMAC_F32_16X16X128_FP8_BF8_gfx940:
152 case V_SMFMAC_F32_16X16X128_FP8_FP8_e64:
153 case V_SMFMAC_F32_16X16X128_FP8_FP8_gfx940:
154 case V_SMFMAC_F32_16X16X64_F16_e64:
155 case V_SMFMAC_F32_16X16X64_F16_gfx940:
156 Res.TotalWindow = 8;
160 return Res;
161
162 // 8-cycle occupancy, 12-cycle window.
163 case V_MFMA_F32_16X16X128_F8F6F4_f4_f8_e64:
164 case V_MFMA_F32_16X16X128_F8F6F4_f4_f8_vgprcd_e64:
165 case V_MFMA_F32_16X16X128_F8F6F4_f4_f8_gfx940_acd:
166 case V_MFMA_F32_16X16X128_F8F6F4_f4_f8_gfx940_vcd:
167 case V_MFMA_F32_16X16X128_F8F6F4_f6_f8_e64:
168 case V_MFMA_F32_16X16X128_F8F6F4_f6_f8_vgprcd_e64:
169 case V_MFMA_F32_16X16X128_F8F6F4_f6_f8_gfx940_acd:
170 case V_MFMA_F32_16X16X128_F8F6F4_f6_f8_gfx940_vcd:
171 case V_MFMA_F32_16X16X128_F8F6F4_f8_f4_e64:
172 case V_MFMA_F32_16X16X128_F8F6F4_f8_f4_vgprcd_e64:
173 case V_MFMA_F32_16X16X128_F8F6F4_f8_f4_gfx940_acd:
174 case V_MFMA_F32_16X16X128_F8F6F4_f8_f4_gfx940_vcd:
175 case V_MFMA_F32_16X16X128_F8F6F4_f8_f6_e64:
176 case V_MFMA_F32_16X16X128_F8F6F4_f8_f6_vgprcd_e64:
177 case V_MFMA_F32_16X16X128_F8F6F4_f8_f6_gfx940_acd:
178 case V_MFMA_F32_16X16X128_F8F6F4_f8_f6_gfx940_vcd:
179 case V_MFMA_F32_16X16X128_F8F6F4_f8_f8_e64:
180 case V_MFMA_F32_16X16X128_F8F6F4_f8_f8_vgprcd_e64:
181 case V_MFMA_F32_16X16X128_F8F6F4_f8_f8_gfx940_acd:
182 case V_MFMA_F32_16X16X128_F8F6F4_f8_f8_gfx940_vcd:
183 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f8_e64:
184 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f8_vgprcd_e64:
185 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f8_gfx940_acd:
186 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f8_gfx940_vcd:
187 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f8_e64:
188 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f8_vgprcd_e64:
189 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f8_gfx940_acd:
190 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f8_gfx940_vcd:
191 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f4_e64:
192 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f4_vgprcd_e64:
193 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f4_gfx940_acd:
194 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f4_gfx940_vcd:
195 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f6_e64:
196 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f6_vgprcd_e64:
197 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f6_gfx940_acd:
198 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f6_gfx940_vcd:
199 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f8_e64:
200 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f8_vgprcd_e64:
201 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f8_gfx940_acd:
202 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f8_gfx940_vcd:
203 Res.TotalWindow = 12;
208 return Res;
209
210 // 4-cycle occupancy, 8-cycle window.
211 case V_MFMA_F32_32X32X64_F8F6F4_f4_f4_e64:
212 case V_MFMA_F32_32X32X64_F8F6F4_f4_f4_mac_e64:
213 case V_MFMA_F32_32X32X64_F8F6F4_f4_f4_mac_vgprcd_e64:
214 case V_MFMA_F32_32X32X64_F8F6F4_f4_f4_vgprcd_e64:
215 case V_MFMA_F32_32X32X64_F8F6F4_f4_f4_gfx940_acd:
216 case V_MFMA_F32_32X32X64_F8F6F4_f4_f4_gfx940_vcd:
217 case V_MFMA_F32_32X32X64_F8F6F4_f4_f6_e64:
218 case V_MFMA_F32_32X32X64_F8F6F4_f4_f6_mac_e64:
219 case V_MFMA_F32_32X32X64_F8F6F4_f4_f6_mac_vgprcd_e64:
220 case V_MFMA_F32_32X32X64_F8F6F4_f4_f6_vgprcd_e64:
221 case V_MFMA_F32_32X32X64_F8F6F4_f4_f6_gfx940_acd:
222 case V_MFMA_F32_32X32X64_F8F6F4_f4_f6_gfx940_vcd:
223 case V_MFMA_F32_32X32X64_F8F6F4_f6_f4_e64:
224 case V_MFMA_F32_32X32X64_F8F6F4_f6_f4_mac_e64:
225 case V_MFMA_F32_32X32X64_F8F6F4_f6_f4_mac_vgprcd_e64:
226 case V_MFMA_F32_32X32X64_F8F6F4_f6_f4_vgprcd_e64:
227 case V_MFMA_F32_32X32X64_F8F6F4_f6_f4_gfx940_acd:
228 case V_MFMA_F32_32X32X64_F8F6F4_f6_f4_gfx940_vcd:
229 case V_MFMA_F32_32X32X64_F8F6F4_f6_f6_e64:
230 case V_MFMA_F32_32X32X64_F8F6F4_f6_f6_mac_e64:
231 case V_MFMA_F32_32X32X64_F8F6F4_f6_f6_mac_vgprcd_e64:
232 case V_MFMA_F32_32X32X64_F8F6F4_f6_f6_vgprcd_e64:
233 case V_MFMA_F32_32X32X64_F8F6F4_f6_f6_gfx940_acd:
234 case V_MFMA_F32_32X32X64_F8F6F4_f6_f6_gfx940_vcd:
235 case V_MFMA_F32_32X32X16_BF16_e64:
236 case V_MFMA_F32_32X32X16_BF16_mac_e64:
237 case V_MFMA_F32_32X32X16_BF16_mac_vgprcd_e64:
238 case V_MFMA_F32_32X32X16_BF16_vgprcd_e64:
239 case V_MFMA_F32_32X32X16_BF16_gfx940_acd:
240 case V_MFMA_F32_32X32X16_BF16_gfx940_vcd:
241 case V_MFMA_I32_32X32X32_I8_e64:
242 case V_MFMA_I32_32X32X32_I8_mac_e64:
243 case V_MFMA_I32_32X32X32_I8_mac_vgprcd_e64:
244 case V_MFMA_I32_32X32X32_I8_vgprcd_e64:
245 case V_MFMA_I32_32X32X32_I8_gfx940_acd:
246 case V_MFMA_I32_32X32X32_I8_gfx940_vcd:
247 case V_MFMA_F32_32X32X16_F16_e64:
248 case V_MFMA_F32_32X32X16_F16_mac_e64:
249 case V_MFMA_F32_32X32X16_F16_mac_vgprcd_e64:
250 case V_MFMA_F32_32X32X16_F16_vgprcd_e64:
251 case V_MFMA_F32_32X32X16_F16_gfx940_acd:
252 case V_MFMA_F32_32X32X16_F16_gfx940_vcd:
253 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f4_gfx940_acd:
254 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f4_gfx940_vcd:
255 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f6_gfx940_acd:
256 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f6_gfx940_vcd:
257 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f4_gfx940_acd:
258 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f4_gfx940_vcd:
259 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f6_gfx940_acd:
260 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f6_gfx940_vcd:
261 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f4_e64:
262 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f6_e64:
263 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f4_e64:
264 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f6_e64:
265 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f4_vgprcd_e64:
266 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f6_vgprcd_e64:
267 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f4_vgprcd_e64:
268 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f6_vgprcd_e64:
269 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f4_mac_e64:
270 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f6_mac_e64:
271 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f4_mac_e64:
272 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f6_mac_e64:
273 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f4_mac_vgprcd_e64:
274 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f6_mac_vgprcd_e64:
275 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f4_mac_vgprcd_e64:
276 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f6_mac_vgprcd_e64:
277 Res.TotalWindow = 8;
281 return Res;
282
283 // 16-cycle occupancy, 20-cycle window.
284 case V_MFMA_F32_32X32X64_F8F6F4_f4_f8_e64:
285 case V_MFMA_F32_32X32X64_F8F6F4_f4_f8_mac_e64:
286 case V_MFMA_F32_32X32X64_F8F6F4_f4_f8_mac_vgprcd_e64:
287 case V_MFMA_F32_32X32X64_F8F6F4_f4_f8_vgprcd_e64:
288 case V_MFMA_F32_32X32X64_F8F6F4_f4_f8_gfx940_acd:
289 case V_MFMA_F32_32X32X64_F8F6F4_f4_f8_gfx940_vcd:
290 case V_MFMA_F32_32X32X64_F8F6F4_f6_f8_e64:
291 case V_MFMA_F32_32X32X64_F8F6F4_f6_f8_mac_e64:
292 case V_MFMA_F32_32X32X64_F8F6F4_f6_f8_mac_vgprcd_e64:
293 case V_MFMA_F32_32X32X64_F8F6F4_f6_f8_vgprcd_e64:
294 case V_MFMA_F32_32X32X64_F8F6F4_f6_f8_gfx940_acd:
295 case V_MFMA_F32_32X32X64_F8F6F4_f6_f8_gfx940_vcd:
296 case V_MFMA_F32_32X32X64_F8F6F4_f8_f4_e64:
297 case V_MFMA_F32_32X32X64_F8F6F4_f8_f4_mac_e64:
298 case V_MFMA_F32_32X32X64_F8F6F4_f8_f4_mac_vgprcd_e64:
299 case V_MFMA_F32_32X32X64_F8F6F4_f8_f4_vgprcd_e64:
300 case V_MFMA_F32_32X32X64_F8F6F4_f8_f4_gfx940_acd:
301 case V_MFMA_F32_32X32X64_F8F6F4_f8_f4_gfx940_vcd:
302 case V_MFMA_F32_32X32X64_F8F6F4_f8_f6_e64:
303 case V_MFMA_F32_32X32X64_F8F6F4_f8_f6_mac_e64:
304 case V_MFMA_F32_32X32X64_F8F6F4_f8_f6_mac_vgprcd_e64:
305 case V_MFMA_F32_32X32X64_F8F6F4_f8_f6_vgprcd_e64:
306 case V_MFMA_F32_32X32X64_F8F6F4_f8_f6_gfx940_acd:
307 case V_MFMA_F32_32X32X64_F8F6F4_f8_f6_gfx940_vcd:
308 case V_MFMA_F32_32X32X64_F8F6F4_f8_f8_e64:
309 case V_MFMA_F32_32X32X64_F8F6F4_f8_f8_mac_e64:
310 case V_MFMA_F32_32X32X64_F8F6F4_f8_f8_mac_vgprcd_e64:
311 case V_MFMA_F32_32X32X64_F8F6F4_f8_f8_vgprcd_e64:
312 case V_MFMA_F32_32X32X64_F8F6F4_f8_f8_gfx940_acd:
313 case V_MFMA_F32_32X32X64_F8F6F4_f8_f8_gfx940_vcd:
314 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f8_e64:
315 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f8_e64:
316 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f4_e64:
317 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f6_e64:
318 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f8_e64:
319 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f8_vgprcd_e64:
320 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f8_vgprcd_e64:
321 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f4_vgprcd_e64:
322 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f6_vgprcd_e64:
323 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f8_vgprcd_e64:
324 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f8_mac_e64:
325 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f8_mac_e64:
326 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f4_mac_e64:
327 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f6_mac_e64:
328 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f8_mac_e64:
329 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f8_mac_vgprcd_e64:
330 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f8_mac_vgprcd_e64:
331 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f4_mac_vgprcd_e64:
332 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f6_mac_vgprcd_e64:
333 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f8_mac_vgprcd_e64:
334 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f8_gfx940_acd:
335 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f8_gfx940_vcd:
336 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f8_gfx940_acd:
337 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f8_gfx940_vcd:
338 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f4_gfx940_acd:
339 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f4_gfx940_vcd:
340 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f6_gfx940_acd:
341 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f6_gfx940_vcd:
342 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f8_gfx940_acd:
343 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f8_gfx940_vcd:
344 Res.TotalWindow = 20;
349 return Res;
350
351 // 9-cycle occupancy, 12-cycle window.
352 case V_SMFMAC_F32_32X32X32_BF16_e64:
353 case V_SMFMAC_F32_32X32X32_BF16_gfx940:
354 case V_SMFMAC_I32_32X32X64_I8_e64:
355 case V_SMFMAC_I32_32X32X64_I8_gfx940:
356 case V_SMFMAC_F32_32X32X64_BF8_BF8_e64:
357 case V_SMFMAC_F32_32X32X64_BF8_BF8_gfx940:
358 case V_SMFMAC_F32_32X32X64_BF8_FP8_e64:
359 case V_SMFMAC_F32_32X32X64_BF8_FP8_gfx940:
360 case V_SMFMAC_F32_32X32X64_FP8_BF8_e64:
361 case V_SMFMAC_F32_32X32X64_FP8_BF8_gfx940:
362 case V_SMFMAC_F32_32X32X64_FP8_FP8_e64:
363 case V_SMFMAC_F32_32X32X64_FP8_FP8_gfx940:
364 case V_SMFMAC_F32_32X32X32_F16_e64:
365 case V_SMFMAC_F32_32X32X32_F16_gfx940:
366 Res.TotalWindow = 12;
370 return Res;
371
372 // 18-cycle occupancy, 19-cycle window.
373 case V_MFMA_F64_16X16X4F64_e64:
374 case V_MFMA_F64_16X16X4F64_mac_e64:
375 case V_MFMA_F64_16X16X4F64_mac_vgprcd_e64:
376 case V_MFMA_F64_16X16X4F64_vgprcd_e64:
377 Res.TotalWindow = 19;
380 return Res;
381
382 default:
383 // Default fallback: permissive 8-cycle pattern
384 return CoExecInfo::build(0, 9, "AAAAAAAAA");
385 }
386}
387
389 const SIInstrInfo &SII) {
390 if (MI.isDebugInstr())
392
393 unsigned Opc = MI.getOpcode();
394
395 // Check for specific opcodes first.
396 if (Opc == AMDGPU::ATOMIC_FENCE || Opc == AMDGPU::S_BARRIER_WAIT ||
397 Opc == AMDGPU::S_BARRIER_SIGNAL_IMM || SII.isWaitcnt(Opc))
399
400 if (SII.isLDSDMA(MI))
402
403 if (SII.isMFMA(MI)) {
404 // TODO: Consider further sub-classifying this (XDL, XDL2x, S/DGEMM).
405 // GFX9 SPG sub-classifies MFMA into XDL, XDL2x and S/DGEMM, because only
406 // certain sub-classes can be co-executed in certain slots. For now, we
407 // simply treat them all as one to simplify the change and leave the rest
408 // to a follow-up fine-tuning.
410 }
411
412 if (SII.isWMMA(MI) || SII.isSWMMAC(MI))
414
415 if (SII.isTRANS(MI))
417
418 if (SII.isVALU(MI, /*AllowLDSDMA=*/false)) {
419 if (SII.getBlockingCycles(MI) > 1)
421
423 }
424
425 if (SII.isSMRD(MI))
427
428 if (SII.isDS(MI))
430
431 if (SII.isVMEM(MI))
433
434 if (SII.isSALU(MI))
436
438}
439
441 for (SUnit *PrioritySU : PrioritySUs) {
442 if (!PrioritySU->isTopReady())
443 return PrioritySU;
444 }
445
446 if (!LookDeep)
447 return nullptr;
448
449 unsigned MinDepth = std::numeric_limits<unsigned int>::max();
450 SUnit *TargetSU = nullptr;
451 for (auto *SU : AllSUs) {
452 if (SU->isScheduled)
453 continue;
454
455 if (SU->isTopReady())
456 continue;
457
458 if (SU->getDepth() < MinDepth) {
459 MinDepth = SU->getDepth();
460 TargetSU = SU;
461 }
462 }
463 return TargetSU;
464}
465
466void HardwareUnitInfo::insert(SUnit *SU, unsigned BlockingCycles) {
467 if (!AllSUs.insert(SU))
468 llvm_unreachable("HardwareUnit already contains SU!");
469
470 TotalCycles += BlockingCycles;
471
472 if (PrioritySUs.empty()) {
473 PrioritySUs.insert(SU);
474 return;
475 }
476 unsigned SUDepth = SU->getDepth();
477 unsigned CurrDepth = (*PrioritySUs.begin())->getDepth();
478 if (SUDepth > CurrDepth)
479 return;
480
481 if (SUDepth == CurrDepth) {
482 PrioritySUs.insert(SU);
483 return;
484 }
485
486 // SU is lower depth and should be prioritized.
487 PrioritySUs.clear();
488 PrioritySUs.insert(SU);
489}
490
491void HardwareUnitInfo::markScheduled(SUnit *SU, unsigned BlockingCycles) {
492 // We may want to ignore some HWUIs (e.g. InstructionFlavor::Other). To do so,
493 // we just clear the HWUI. However, we still have instructions which map to
494 // this HWUI. Don't bother managing the state for these HWUI.
495 if (TotalCycles == 0)
496 return;
497
498 ScheduledSUs.push_back(SU);
499 AllSUs.remove(SU);
500 PrioritySUs.remove(SU);
501
502 // BufferSize 0 is unlimited, while size 1 has no parallel buffering. In
503 // either case, each SU uses the HardwareUnit for BlockingCycles.
504 if (BufferSize <= 1 || (ScheduledSUs.size() % BufferSize == 0))
505 TotalCycles -= std::min(TotalCycles, BlockingCycles);
506
507 if (AllSUs.empty())
508 return;
509 if (PrioritySUs.empty()) {
510 for (auto SU : AllSUs) {
511 if (PrioritySUs.empty()) {
512 PrioritySUs.insert(SU);
513 continue;
514 }
515 unsigned SUDepth = SU->getDepth();
516 unsigned CurrDepth = (*PrioritySUs.begin())->getDepth();
517 if (SUDepth > CurrDepth)
518 continue;
519
520 if (SUDepth == CurrDepth) {
521 PrioritySUs.insert(SU);
522 continue;
523 }
524
525 // SU is lower depth and should be prioritized.
526 PrioritySUs.clear();
527 PrioritySUs.insert(SU);
528 }
529 }
530}
531
533 if (BufferSize == 0 || AllSUs.empty())
534 return;
535
536 // We estimate the amount of cycles it takes to free up a slot in the buffer
537 // as the average cycles per SU.
538 BufferCycles = TotalCycles / AllSUs.size();
539 // A single-entry buffer does not reduce TotalCycles.
540 if (BufferSize == 1)
541 return;
542
543 // The TotalCycles is normalized against the BufferSize.
544 // This provides an estimate of the TotalCycles which is not always accurate
545 // -- particularly in cases where we have fewer instructions than the
546 // BufferSize. For example, if we have 2 instructions which each take 50
547 // cycles and a BufferSize of 16, then a TotalCycles of 51 cycles would be
548 // somewhat accurate. This normalization calculates TotalCycles as 6. However,
549 // if we have 64 of these instructions, our normalized estimate of 200 is more
550 // reasonable, given the more accurate measure is 264. Having a completely
551 // accurate measure is not very important, since this metric is mainly used to
552 // compare the relative demand per HardwareUnit across the region. The simpler
553 // estimate makes managing the metric incrementally during scheduling much
554 // simpler.
555 TotalCycles /= BufferSize;
556}
557
560 for (HardwareUnitInfo &HWUICand : HWUInfo) {
561 if (HWUICand.getType() == Flavor) {
562 return &HWUICand;
563 }
564 }
565 return nullptr;
566}
567
569 const MachineInstr *MI) {
570 unsigned ReleaseAtCycle = 0;
571 for (TargetSchedModel::ProcResIter PI = SchedModel->getWriteProcResBegin(SC),
572 PE = SchedModel->getWriteProcResEnd(SC);
573 PI != PE; ++PI) {
574 ReleaseAtCycle =
575 std::max(ReleaseAtCycle, static_cast<unsigned>(PI->ReleaseAtCycle));
576 }
577 ReleaseAtCycle = std::max(ReleaseAtCycle, SII->getBlockingCycles(*MI));
578 return ReleaseAtCycle;
579}
580
582 assert(SchedModel && SchedModel->hasInstrSchedModel());
583 if (MI->mayLoadOrStore())
584 return SchedModel->computeInstrLatency(MI, false);
585 return getMaxBlockingCycles(SchedModel->resolveSchedClass(MI), MI);
586}
587
594
597 const TargetRegisterInfo *TRI,
598 const MachineLoopInfo *LoopInfo) {
599 DAG = SchedDAG;
601 MLI = LoopInfo;
602 assert(SchedModel && SchedModel->hasInstrSchedModel());
603
604 SRI = static_cast<const SIRegisterInfo *>(TRI);
605 SII = static_cast<const SIInstrInfo *>(DAG->TII);
606
607 HWUInfo.resize(static_cast<int>(InstructionFlavor::NUM_FLAVORS));
608
609 for (unsigned I = 0; I < HWUInfo.size(); I++) {
610 HWUInfo[I].reset();
611 HWUInfo[I].setType(I);
612 }
613
614 HWUInfo[static_cast<int>(InstructionFlavor::WMMA)].setProducesCoexecWindow(
615 true);
617 .setProducesCoexecWindow(true);
618 HWUInfo[static_cast<int>(InstructionFlavor::TRANS)].setProducesCoexecWindow(
619 true);
620 HWUInfo[static_cast<int>(InstructionFlavor::DS)].setBufferSize(
622
624}
625
628 return 0;
629
630 MachineInstr *MI = SU->getInstr();
631 unsigned CarriedLatency = 0;
632 const InstructionFlavor Flavor = classifyFlavor(*MI, *SII);
633 if (Flavor == InstructionFlavor::Fence) {
634 MachineBasicBlock *MBB = MI->getParent();
635 // Scan each direct predecessor back to its nearest Fence or block start for
636 // DS instructions.
637 for (auto PredMBB : MBB->predecessors()) {
638 auto I = PredMBB->rbegin();
639 auto E = PredMBB->rend();
640 for (; I != E; I++) {
641 const InstructionFlavor ItFlavor = classifyFlavor(*I, *SII);
642 if (ItFlavor == InstructionFlavor::Fence)
643 break;
644
645 // Found carried latency.
646 if (ItFlavor == InstructionFlavor::DS)
648 }
649 }
650 }
651
653 return CarriedLatency;
654
655 for (MachineOperand &Op : MI->all_uses()) {
656 auto Reg = Op.getReg();
657 if (!Reg.isVirtual())
658 continue;
659
660 for (MachineInstr &Def : DAG->MRI.def_instructions(Reg)) {
661 // We don't have the proper modelling to accurately measure all carried
662 // latency. Just try to measure carried latency for long latency loads to
663 // avoid long stalls.
664 if (!Def.mayLoad())
665 continue;
666
667 unsigned Latency = getHWUICyclesForMI(&Def);
668
669 // Load is carried across block.
670 if (Def.getParent() != MI->getParent()) {
671 bool FoundUseInDefBlock = false;
672 for (MachineInstr &Use : DAG->MRI.use_nodbg_instructions(Reg)) {
673 if (Use.getParent() != Def.getParent())
674 continue;
675
676 SlotIndex DefIdx = DAG->getLIS()->getInstructionIndex(Def);
677 SlotIndex UseIdx = DAG->getLIS()->getInstructionIndex(Use);
678 // We have a use of this load in the def block that occurs after the
679 // load. In this case we must wait for the load in the def block, and
680 // we do not have any carried latency from this load.
681 if (SlotIndex::isEarlierInstr(DefIdx, UseIdx)) {
682 FoundUseInDefBlock = true;
683 break;
684 }
685 }
686 if (!FoundUseInDefBlock)
688
689 continue;
690 }
691
692 assert(Def.getParent() == MI->getParent());
693 // Load is in the same block.
694 SlotIndex LoadIdx = DAG->getLIS()->getInstructionIndex(Def);
695 SlotIndex UseIdx = DAG->getLIS()->getInstructionIndex(*MI);
696 // The load occurs after this use -- the latency is carried across loop
697 // backedge.
698 if (SlotIndex::isEarlierInstr(UseIdx, LoadIdx))
700 }
701 }
702 return CarriedLatency;
703}
704
706 CarriedLatencies.clear();
708
709 if (!SchedModel || !SchedModel->hasInstrSchedModel())
710 return;
711
712 // In some cases, we may end up with loads at the end of a predecssor block.
713 // In these cases, it is preferable to defer scheduling a fence which
714 // corresponds with a waitcnt for those loads until the latency of the load
715 // has cleared. Unfortunately, there is no reliable way to determine whether
716 // or not a predecessor block will end up scheduling loads at the end. Here,
717 // we inspect the dependency structure to define a rough heuristic: if we
718 // must schedule ds_loads after wmma, then we carry the latency of ds_loads
719 // to successor block fences. Currently this only checks for the loop-carried
720 // case, as this will generally have the largest impact on performance.
721 // TODO: 1. extend to different memory instructions, 2. teach carried
722 // latencies about fence legalization.
723 auto mustHaveDSAfter = [this]() {
724 if (!DAG->SUnits.size())
725 return false;
726
727 MachineBasicBlock *MBB = DAG->begin()->getParent();
728 MachineLoop *Loop = MLI->getLoopFor(MBB);
729 if (!Loop || Loop->getNumBlocks() != 1)
730 return false;
731
732 SmallVector<SUnit *, 16> RegionWMMAs;
733
734 for (auto &SU : DAG->SUnits) {
735 if (!SU.getInstr())
736 continue;
737 MachineInstr *MI = SU.getInstr();
738 const InstructionFlavor Flavor = classifyFlavor(*MI, *SII);
739 if (Flavor == InstructionFlavor::WMMA)
740 RegionWMMAs.push_back(&SU);
741 }
742
743 bool MustHaveDSAfter = RegionWMMAs.size();
744
745 for (SUnit *SU : RegionWMMAs) {
746 bool HasDSSucc = false;
747 for (auto &Succ : SU->Succs) {
748 if (!Succ.getSUnit()->getInstr())
749 continue;
750 if (classifyFlavor(*Succ.getSUnit()->getInstr(), *SII) ==
752 HasDSSucc = true;
753 break;
754 }
755 }
756
757 if (!HasDSSucc) {
758 MustHaveDSAfter = false;
759 break;
760 }
761 }
762
763 return MustHaveDSAfter;
764 };
765
766 RegionCarriedLatency = BlockCarriedLatency.getNumOccurrences()
768 : mustHaveDSAfter() ? CarriedLatency::Fence
770
771 for (auto &SU : DAG->SUnits) {
772 MachineInstr *MI = SU.getInstr();
773 const InstructionFlavor Flavor = classifyFlavor(*MI, *SII);
774 HWUInfo[static_cast<int>(Flavor)].insert(&SU, getHWUICyclesForMI(MI));
775 unsigned CarriedLatency = getCarriedLatency(&SU);
776 if (CarriedLatency)
778 }
779
780 for (auto &HWUI : HWUInfo)
781 HWUI.finalizeCycles();
782
784}
785
787 MachineBasicBlock *BB = DAG->begin()->getParent();
788 dbgs() << "\n=== Region: " << DAG->MF.getName() << " BB" << BB->getNumber()
789 << " (" << DAG->SUnits.size() << " SUs) ===\n";
790
791 dbgs() << "\nHWUI Resource Pressure:\n";
792 for (auto &HWUI : HWUInfo) {
793 if (HWUI.getTotalCycles() == 0)
794 continue;
795
796 StringRef Name = getFlavorName(HWUI.getType());
797 dbgs() << " " << Name << ": " << HWUI.getTotalCycles() << " cycles, "
798 << HWUI.size() << " instrs\n";
799 }
800 dbgs() << "\n";
801}
802
804 // Highest priority should be first.
806 // Prefer CoexecWindow producers
807 if (A.producesCoexecWindow() != B.producesCoexecWindow())
808 return A.producesCoexecWindow();
809
810 // Prefer more demanded resources
811 if (A.getTotalCycles() != B.getTotalCycles())
812 return A.getTotalCycles() > B.getTotalCycles();
813
814 // In ties -- prefer the resource with more instructions
815 if (A.size() != B.size())
816 return A.size() < B.size();
817
818 // Default to Flavor order
819 return static_cast<unsigned>(A.getType()) <
820 static_cast<unsigned>(B.getType());
821 });
822}
823
825 SUnit *SU) {
826 assert(Zone.isTop() && "effective stall comparison requires top boundary");
827 if (!SU)
828 return 0;
829
830 MachineInstr *MI = SU->getInstr();
831 unsigned CurrCycle = Zone.getCurrCycle();
832 unsigned Stall = 0;
833
834 // Query SchedModel for resource stalls (unbuffered resources).
835 if (SchedModel->hasInstrSchedModel() && SU->hasReservedResource) {
836 const MCSchedClassDesc *SC = DAG->getSchedClass(SU);
837 for (const MCWriteProcResEntry &PE :
838 make_range(SchedModel->getWriteProcResBegin(SC),
839 SchedModel->getWriteProcResEnd(SC))) {
840 unsigned NextAvail =
841 Zone.getNextResourceCycle(SC, PE.ProcResourceIdx, PE.ReleaseAtCycle,
842 PE.AcquireAtCycle)
843 .first;
844 if (NextAvail > CurrCycle)
845 Stall = std::max(Stall, NextAvail - CurrCycle);
846 }
847 }
848
849 // Query HazardRecognizer for sequence-dependent hazard penalties.
850 if (Zone.HazardRec && Zone.HazardRec->isEnabled()) {
851 auto *HR = static_cast<GCNHazardRecognizer *>(Zone.HazardRec.get());
852 Stall = std::max(Stall, HR->getHazardWaitStates(MI));
853 }
854
855 return Stall;
856}
857
860 if (!Zone.isTop())
861 return {};
862
863 auto getBufferFullStalls = [this, &Zone](SUnit *SU) -> unsigned {
865 *SU->getInstr(), *static_cast<const SIInstrInfo *>(DAG->TII));
866 HardwareUnitInfo *HWUI = getHWUIFromFlavor(Flavor);
867
868 // A BufferSize of 0 means "unlimited" buffer, thus we will never fill it.
869 if (HWUI->getBufferSize() == 0)
870 return 0;
871
872 unsigned CurrCycle = Zone.getCurrCycle();
873 unsigned BufferReadyCycle = HWUI->getBufferAvailableCycle(CurrCycle);
874 if (BufferReadyCycle <= CurrCycle)
875 return 0;
876
877 return BufferReadyCycle - CurrCycle;
878 };
879
880 unsigned CurrCycle = Zone.getCurrCycle();
881
882 auto getFenceStalls = [this, &CurrCycle](SUnit *SU) -> unsigned {
884 *SU->getInstr(), *static_cast<const SIInstrInfo *>(DAG->TII));
885
886 if (Flavor != InstructionFlavor::Fence)
887 return 0;
888
889 HardwareUnitInfo *ConsumerHWUI = getHWUIFromFlavor(Flavor);
891
892 SUnit *LastProducer = ProducerHWUI->getLastScheduledSU();
893 if (!LastProducer)
894 return 0;
895
896 SUnit *LastConsumer = ConsumerHWUI->getLastScheduledSU();
897 unsigned LastConsumerCycle = LastConsumer ? LastConsumer->TopReadyCycle : 0;
898 unsigned LastProducerCycle = LastProducer->TopReadyCycle;
899
900 if (LastProducerCycle < LastConsumerCycle)
901 return 0;
902
903 unsigned FenceStallFinish =
904 LastProducerCycle + getHWUICyclesForMI(LastProducer->getInstr());
905 return FenceStallFinish <= CurrCycle ? 0 : FenceStallFinish - CurrCycle;
906 };
907
908 unsigned ReadyCycle = SU->TopReadyCycle;
909 StallCosts Costs;
910 Costs.Ready = ReadyCycle > CurrCycle ? ReadyCycle - CurrCycle : 0;
911 Costs.Structural = getStructuralStallCycles(Zone, SU);
912 Costs.Latency = Zone.getLatencyStallCycles(SU);
913 unsigned CarriedLatency = CarriedLatencies.lookup_or(SU->getInstr(), 0);
914 Costs.Carried = CarriedLatency > CurrCycle ? CarriedLatency - CurrCycle : 0;
915 Costs.Buffer = getBufferFullStalls(SU);
916 Costs.Fence = getFenceStalls(SU);
917 Costs.Effective = std::max({Costs.Ready, Costs.Structural, Costs.Latency,
918 Costs.Carried, Costs.Buffer, Costs.Fence});
919 return Costs;
920}
921
925 // Only implemented for top-down scheduling
926 if (!Zone.isTop())
927 return 0;
928
929 StallCosts TryCosts = getStallCosts(TryCand.SU, Zone);
930 StallCosts CandCosts = getStallCosts(Cand.SU, Zone);
931
932 LLVM_DEBUG(if (TryCosts.Effective || CandCosts.Effective) {
933 dbgs() << "Effective stalls: try=" << TryCosts.Effective
934 << " (ready=" << TryCosts.Ready << ", struct=" << TryCosts.Structural
935 << ", lat=" << TryCosts.Latency << ", carried=" << TryCosts.Carried
936 << ", buffer=" << TryCosts.Buffer << ", fence=" << TryCosts.Fence
937 << ") cand=" << CandCosts.Effective << " (ready=" << CandCosts.Ready
938 << ", struct=" << CandCosts.Structural
939 << ", lat=" << CandCosts.Latency << ", carried=" << CandCosts.Carried
940 << ", buffer=" << CandCosts.Buffer << ", fence=" << CandCosts.Fence
941 << ")\n";
942 });
943
944 return tryLess(TryCosts.Effective, CandCosts.Effective, TryCand, Cand,
946}
947
951
952 InstructionFlavor TryFlavor = classifyFlavor(*TryCand.SU->getInstr(), *SII);
953
954 InstructionFlavor CandFlavor = classifyFlavor(*Cand.SU->getInstr(), *SII);
955
956 bool TryIsMemoryPipeline = TryFlavor == InstructionFlavor::DMA ||
957 TryFlavor == InstructionFlavor::Fence;
958 bool CandIsMemoryPipeline = CandFlavor == InstructionFlavor::DMA ||
959 CandFlavor == InstructionFlavor::Fence;
960
961 if (!(TryIsMemoryPipeline || CandIsMemoryPipeline))
962 return false;
963
964 if (TryIsMemoryPipeline)
965 TryIsMemoryPipeline &= getStallCosts(TryCand.SU, Zone).Effective == 0;
966
967 if (CandIsMemoryPipeline)
968 CandIsMemoryPipeline &= getStallCosts(Cand.SU, Zone).Effective == 0;
969
970 if (TryIsMemoryPipeline == CandIsMemoryPipeline)
971 return false;
972
973 if (CandIsMemoryPipeline) {
976
977 return true;
978 }
979
981 return true;
982}
983
987
988 auto HasPrioritySU = [this, &Cand, &TryCand](unsigned ResourceIdx) {
989 const HardwareUnitInfo &HWUI = HWUInfo[ResourceIdx];
990
991 auto CandFlavor = classifyFlavor(*Cand.SU->getInstr(), *SII);
992 auto TryCandFlavor = classifyFlavor(*TryCand.SU->getInstr(), *SII);
993 bool LookDeep = (CandFlavor == InstructionFlavor::DS ||
994 TryCandFlavor == InstructionFlavor::DS) &&
996 auto *TargetSU = HWUI.getNextTargetSU(LookDeep);
997
998 // If we do not have a TargetSU for this resource, then it is not critical.
999 if (!TargetSU)
1000 return false;
1001
1002 return true;
1003 };
1004
1005 auto TryEnablesResource = [&Cand, &TryCand, this](unsigned ResourceIdx) {
1006 const HardwareUnitInfo &HWUI = HWUInfo[ResourceIdx];
1007 auto CandFlavor = classifyFlavor(*Cand.SU->getInstr(), *SII);
1008
1009 // We want to ensure our DS order matches WMMA order.
1010 bool LookDeep = CandFlavor == InstructionFlavor::DS &&
1012 auto *TargetSU = HWUI.getNextTargetSU(LookDeep);
1013
1014 bool CandEnables =
1015 TargetSU != Cand.SU && DAG->IsReachable(TargetSU, Cand.SU);
1016 bool TryCandEnables =
1017 TargetSU != TryCand.SU && DAG->IsReachable(TargetSU, TryCand.SU);
1018
1019 if (!CandEnables && !TryCandEnables)
1020 return false;
1021
1022 if (CandEnables && !TryCandEnables) {
1025
1026 return true;
1027 }
1028
1029 if (!CandEnables && TryCandEnables) {
1031 return true;
1032 }
1033
1034 // Both enable, prefer the critical path.
1035 unsigned CandHeight = Cand.SU->getHeight();
1036 unsigned TryCandHeight = TryCand.SU->getHeight();
1037
1038 if (CandHeight > TryCandHeight) {
1041
1042 return true;
1043 }
1044
1045 if (CandHeight < TryCandHeight) {
1047 return true;
1048 }
1049
1050 // Same critical path, just prefer original candidate.
1053
1054 return true;
1055 };
1056
1057 for (unsigned I = 0; I < HWUInfo.size(); I++) {
1058 // If we have encountered a resource that is not critical, then neither
1059 // candidate enables a critical resource
1060 if (!HasPrioritySU(I))
1061 continue;
1062
1063 bool Enabled = TryEnablesResource(I);
1064 // If neither has enabled the resource, continue to the next resource
1065 if (Enabled)
1066 return true;
1067 }
1068 return false;
1069}
1070
1074 for (unsigned I = 0; I < HWUInfo.size(); I++) {
1075 const HardwareUnitInfo &HWUI = HWUInfo[I];
1076
1077 bool CandUsesCrit = HWUI.contains(Cand.SU);
1078 bool TryCandUsesCrit = HWUI.contains(TryCand.SU);
1079
1080 if (!CandUsesCrit && !TryCandUsesCrit)
1081 continue;
1082
1083 if (CandUsesCrit != TryCandUsesCrit) {
1084 if (CandUsesCrit) {
1087 return true;
1088 }
1090 return true;
1091 }
1092
1093 // Otherwise, both use the critical resource
1094 // For longer latency InstructionFlavors, we should prioritize first by
1095 // their enablement of critical resources
1096 if (HWUI.getType() == InstructionFlavor::DS) {
1097 if (tryCriticalResourceDependency(TryCand, Cand, Zone))
1098 return true;
1099 }
1100
1101 // Prioritize based on HWUI priorities.
1102 SUnit *Match = HWUI.getHigherPriority(Cand.SU, TryCand.SU);
1103 if (Match) {
1104 if (Match == Cand.SU) {
1107 return true;
1108 }
1110 return true;
1111 }
1112 }
1113
1114 return false;
1115}
1116
1130
1133 unsigned NumRegionInstrs) {
1137 report_fatal_error("CoExecSchedStrategy only support TopDown scheduling.");
1138 RegionPolicy.OnlyTopDown = true;
1139 RegionPolicy.OnlyBottomUp = false;
1140 RegionPolicy.ShouldTrackLaneMasks = true;
1141}
1142
1144 // Coexecution scheduling strategy is only done top-down to support new
1145 // resource balancing heuristics.
1146 RegionPolicy.OnlyTopDown = true;
1147 RegionPolicy.OnlyBottomUp = false;
1148
1150 Heurs.initialize(DAG, SchedModel, TRI, Context->MLI);
1151
1152 // Replace the default hazard recognizer with our PreRA one so that pre-RA
1153 // scheduling accounts for WMMA co-execution slot constraints. This must
1154 // happen after GCNSchedStrategy::initialize() because
1155 // GenericScheduler::initialize() calls SchedBoundary::reset(), which deletes
1156 // and recreates the hazard recognizer each region.
1157 Top.HazardRec = std::make_unique<GCNHazardRecognizer>(
1159}
1160
1162 Heurs.updateForScheduling(SU);
1163 GCNSchedStrategy::schedNode(SU, IsTopNode);
1164}
1165
1167 assert(RegionPolicy.OnlyTopDown && !RegionPolicy.OnlyBottomUp &&
1168 "coexec scheduler only supports top-down scheduling");
1169
1170 if (DAG->top() == DAG->bottom()) {
1171 assert(Top.Available.empty() && Top.Pending.empty() &&
1172 Bot.Available.empty() && Bot.Pending.empty() && "ReadyQ garbage");
1173 return nullptr;
1174 }
1175
1176 bool PickedPending = false;
1177 SUnit *SU = nullptr;
1178#ifndef NDEBUG
1179 SchedCandidate *PickedCand = nullptr;
1180#endif
1181 do {
1182 PickedPending = false;
1183 SU = pickOnlyChoice(Top);
1184 if (!SU) {
1185 CandPolicy NoPolicy;
1186 TopCand.reset(NoPolicy);
1187 pickNodeFromQueue(Top, NoPolicy, DAG->getTopRPTracker(), TopCand,
1188 PickedPending, /*IsBottomUp=*/false);
1189 assert(TopCand.Reason != NoCand && "failed to find a candidate");
1190 SU = TopCand.SU;
1191#ifndef NDEBUG
1192 PickedCand = &TopCand;
1193#endif
1194 }
1195 IsTopNode = true;
1196 } while (SU->isScheduled);
1197
1198 LLVM_DEBUG(if (PickedCand) dumpPickSummary(SU, IsTopNode, *PickedCand));
1199
1200 if (PickedPending) {
1201 unsigned ReadyCycle = SU->TopReadyCycle;
1202 unsigned CurrentCycle = Top.getCurrCycle();
1203 if (ReadyCycle > CurrentCycle)
1204 Top.bumpCycle(ReadyCycle);
1205
1206 // checkHazard() does not expose the exact cycle where the hazard clears.
1207 while (Top.checkHazard(SU))
1208 Top.bumpCycle(Top.getCurrCycle() + 1);
1209
1210 Top.releasePending();
1211 }
1212
1213 if (SU->isTopReady())
1214 Top.removeReady(SU);
1215 if (SU->isBottomReady())
1216 Bot.removeReady(SU);
1217
1218 LLVM_DEBUG(dbgs() << "Scheduling " << *SU << " " << *SU->getInstr());
1219
1220 assert(IsTopNode && "coexec scheduler must only schedule from top boundary");
1221 return SU;
1222}
1223
1225 SchedBoundary &Zone, const CandPolicy &ZonePolicy,
1226 const RegPressureTracker &RPTracker, SchedCandidate &Cand,
1227 bool &PickedPending, bool IsBottomUp) {
1228 assert(Zone.isTop() && "coexec scheduler only supports top boundary");
1229 assert(!IsBottomUp && "coexec scheduler only supports top-down scheduling");
1230
1231 const SIRegisterInfo *SRI = static_cast<const SIRegisterInfo *>(TRI);
1233 unsigned SGPRPressure = 0;
1234 unsigned VGPRPressure = 0;
1235 unsigned AGPRPressure = 0;
1236 PickedPending = false;
1237 if (DAG->isTrackingPressure()) {
1238 if (!useGCNTrackers()) {
1239 SGPRPressure = Pressure[AMDGPU::RegisterPressureSets::SReg_32];
1240 VGPRPressure = Pressure[AMDGPU::RegisterPressureSets::VGPR_32];
1241 AGPRPressure = Pressure[AMDGPU::RegisterPressureSets::AGPR_32];
1242 } else {
1243 SGPRPressure = DownwardTracker.getPressure().getSGPRNum();
1244 VGPRPressure = DownwardTracker.getPressure().getArchVGPRNum();
1245 AGPRPressure = DownwardTracker.getPressure().getAGPRNum();
1246 }
1247 }
1248
1249 auto EvaluateQueue = [&](ReadyQueue &Q, bool FromPending) {
1250 for (SUnit *SU : Q) {
1251 SchedCandidate TryCand(ZonePolicy);
1252 initCandidate(TryCand, SU, Zone.isTop(), RPTracker, SRI, SGPRPressure,
1253 VGPRPressure, AGPRPressure, IsBottomUp);
1254 SchedBoundary *ZoneArg = Cand.AtTop == TryCand.AtTop ? &Zone : nullptr;
1255 tryCandidateCoexec(Cand, TryCand, ZoneArg);
1256 if (TryCand.Reason != NoCand) {
1257 if (TryCand.ResDelta == SchedResourceDelta())
1258 TryCand.initResourceDelta(Zone.DAG, SchedModel);
1259 LLVM_DEBUG(printCandidateDecision(Cand, TryCand));
1260 PickedPending = FromPending;
1261 Cand.setBest(TryCand);
1262 } else {
1263 LLVM_DEBUG(printCandidateDecision(TryCand, Cand));
1264 }
1265 }
1266 };
1267
1268 LLVM_DEBUG(dbgs() << "Available Q:\n");
1269 EvaluateQueue(Zone.Available, /*FromPending=*/false);
1270
1271 LLVM_DEBUG(dbgs() << "Pending Q:\n");
1272 EvaluateQueue(Zone.Pending, /*FromPending=*/true);
1273}
1274
1275#ifndef NDEBUG
1277 SchedCandidate &Cand) {
1278 const SIInstrInfo *SII = static_cast<const SIInstrInfo *>(DAG->TII);
1279 unsigned Cycle = IsTopNode ? Top.getCurrCycle() : Bot.getCurrCycle();
1280
1281 dbgs() << "=== Pick @ Cycle " << Cycle << " ===\n";
1282
1283 const InstructionFlavor Flavor = classifyFlavor(*SU->getInstr(), *SII);
1284 dbgs() << "Picked: " << *SU << " ";
1285 SU->getInstr()->print(dbgs(), /*IsStandalone=*/true, /*SkipOpers=*/false,
1286 /*SkipDebugLoc=*/true);
1287 dbgs() << " [" << getFlavorName(Flavor) << "]\n";
1288
1289 dbgs() << " Reason: ";
1292 else if (Cand.Reason != NoCand)
1294 else
1295 dbgs() << "Unknown";
1296 dbgs() << "\n\n";
1297
1299}
1300#endif
1301
1303 SchedCandidate &TryCand,
1304 SchedBoundary *Zone) {
1305 // Initialize the candidate if needed.
1306 if (!Cand.isValid()) {
1307 TryCand.Reason = FirstValid;
1308 return true;
1309 }
1310
1311 // Bias PhysReg Defs and copies to their uses and defined respectively.
1312 if (tryGreater(biasPhysReg(TryCand.SU, TryCand.AtTop),
1313 biasPhysReg(Cand.SU, Cand.AtTop), TryCand, Cand, PhysReg))
1314 return TryCand.Reason != NoCand;
1315
1316 // Avoid exceeding the target's limit.
1317 if (DAG->isTrackingPressure() &&
1318 tryPressure(TryCand.RPDelta.Excess, Cand.RPDelta.Excess, TryCand, Cand,
1319 RegExcess, TRI, DAG->MF))
1320 return TryCand.Reason != NoCand;
1321
1322 // We only compare a subset of features when comparing nodes between
1323 // Top and Bottom boundary. Some properties are simply incomparable, in many
1324 // other instances we should only override the other boundary if something
1325 // is a clear good pick on one boundary. Skip heuristics that are more
1326 // "tie-breaking" in nature.
1327 bool SameBoundary = Zone != nullptr;
1328 if (SameBoundary) {
1329 // Compare candidates by the stall they would introduce if
1330 // scheduled in the current cycle.
1331 if (Heurs.tryEffectiveStall(TryCand, Cand, *Zone)) {
1333 return TryCand.Reason != NoCand;
1334 }
1335
1336 if (Heurs.tryMemoryPipeline(TryCand, Cand, *Zone)) {
1338 return TryCand.Reason != NoCand;
1339 }
1340
1341 Heurs.sortHWUIResources();
1342 if (Heurs.tryCriticalResource(TryCand, Cand, Zone)) {
1344 return TryCand.Reason != NoCand;
1345 }
1346
1347 if (Heurs.tryCriticalResourceDependency(TryCand, Cand, Zone)) {
1349 return TryCand.Reason != NoCand;
1350 }
1351 }
1352
1353 // Keep clustered nodes together to encourage downstream peephole
1354 // optimizations which may reduce resource requirements.
1355 //
1356 // This is a best effort to set things up for a post-RA pass. Optimizations
1357 // like generating loads of multiple registers should ideally be done within
1358 // the scheduler pass by combining the loads during DAG postprocessing.
1359 unsigned CandZoneCluster = Cand.AtTop ? TopClusterID : BotClusterID;
1360 unsigned TryCandZoneCluster = TryCand.AtTop ? TopClusterID : BotClusterID;
1361 bool CandIsClusterSucc =
1362 isTheSameCluster(CandZoneCluster, Cand.SU->ParentClusterIdx);
1363 bool TryCandIsClusterSucc =
1364 isTheSameCluster(TryCandZoneCluster, TryCand.SU->ParentClusterIdx);
1365
1366 if (tryGreater(TryCandIsClusterSucc, CandIsClusterSucc, TryCand, Cand,
1367 Cluster))
1368 return TryCand.Reason != NoCand;
1369
1370 if (SameBoundary) {
1371 // Weak edges are for clustering and other constraints.
1372 if (tryLess(getWeakLeft(TryCand.SU, TryCand.AtTop),
1373 getWeakLeft(Cand.SU, Cand.AtTop), TryCand, Cand, Weak))
1374 return TryCand.Reason != NoCand;
1375 }
1376
1377 // Avoid increasing the max pressure of the entire region.
1378 if (DAG->isTrackingPressure() &&
1379 tryPressure(TryCand.RPDelta.CurrentMax, Cand.RPDelta.CurrentMax, TryCand,
1380 Cand, RegMax, TRI, DAG->MF))
1381 return TryCand.Reason != NoCand;
1382
1383 if (SameBoundary) {
1384 // Avoid serializing long latency dependence chains.
1385 // For acyclic path limited loops, latency was already checked above.
1386 if (!RegionPolicy.DisableLatencyHeuristic && TryCand.Policy.ReduceLatency &&
1387 !Rem.IsAcyclicLatencyLimited && tryLatency(TryCand, Cand, *Zone))
1388 return TryCand.Reason != NoCand;
1389
1390 // Fall through to original instruction order.
1391 if ((Zone->isTop() && TryCand.SU->NodeNum < Cand.SU->NodeNum) ||
1392 (!Zone->isTop() && TryCand.SU->NodeNum > Cand.SU->NodeNum)) {
1393 TryCand.Reason = NodeOrder;
1394 return true;
1395 }
1396 }
1397
1398 return false;
1399}
1400
1403 LLVM_DEBUG(dbgs() << "AMDGPU coexec preRA scheduler selected for "
1404 << C->MF->getName() << '\n');
1406 C, std::make_unique<AMDGPUCoExecSchedStrategy>(C));
1409 return DAG;
1410}
1411
1414 LLVM_DEBUG(dbgs() << "AMDGPU nop postRA scheduler selected for "
1415 << C->MF->getName() << '\n');
1416 return new GCNNoopPostScheduleDAG(C);
1417}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static cl::opt< CarriedLatency > BlockCarriedLatency("amdgpu-block-carried-latency", cl::Hidden, cl::init(CarriedLatency::Off), cl::desc("Estimate block-carried latency and include it in the effective " "candidate stall cost."), cl::values(clEnumValN(CarriedLatency::Off, "off", "Disabled - do not pad latency."), clEnumValN(CarriedLatency::Fence, "fence", "Only pad latency for memory fence (e.g. those surrounding " "barrier_signal/wait)."), clEnumValN(CarriedLatency::All, "all", "Pad latency for any SU with an incoming ds_load dependency.")))
static SUnit * pickOnlyChoice(SchedBoundary &Zone)
static constexpr unsigned DefaultCoExecVGPRThresholdPercent
static void allowCoExec(llvm::AMDGPU::CoExecInfo &Info, llvm::AMDGPU::CoExecMaskT ExtraBits, unsigned StartIndex)
Apply ExtraBits to every slot in Info starting with StartIndex Used by MFMA co-exec rules,...
Coexecution-focused scheduling strategy for AMDGPU.
MachineBasicBlock & MBB
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
#define clEnumValN(ENUMVAL, FLAGNAME, DESC)
IRTranslator LLVM IR MI
#define I(x, y, z)
Definition MD5.cpp:57
Register const TargetRegisterInfo * TRI
#define LLVM_DEBUG(...)
Definition Debug.h:119
void initPolicy(MachineBasicBlock::iterator Begin, MachineBasicBlock::iterator End, unsigned NumRegionInstrs) override
Optionally override the per-region scheduling policy.
SUnit * pickNode(bool &IsTopNode) override
Pick the next node to schedule, or return NULL.
void pickNodeFromQueue(SchedBoundary &Zone, const CandPolicy &ZonePolicy, const RegPressureTracker &RPTracker, SchedCandidate &Cand, bool &PickedPending, bool IsBottomUp)
void initialize(ScheduleDAGMI *DAG) override
Initialize the strategy after building the DAG for a new region.
void schedNode(SUnit *SU, bool IsTopNode) override
Notify MachineSchedStrategy that ScheduleDAGMI has scheduled an instruction and updated scheduled/rem...
AMDGPUCoExecSchedStrategy(const MachineSchedContext *C)
void dumpPickSummary(SUnit *SU, bool IsTopNode, SchedCandidate &Cand)
bool tryCandidateCoexec(SchedCandidate &Cand, SchedCandidate &TryCand, SchedBoundary *Zone)
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
void updateForScheduling(SUnit *SU)
Update the state to reflect that SU is going to be scheduled.
unsigned getCarriedLatency(SUnit *SU)
Estimate the block carried latency from loads for a given SU.
HardwareUnitInfo * getHWUIFromFlavor(AMDGPU::InstructionFlavor Flavor)
Given a Flavor , find the corresponding HardwareUnit.
DenseMap< MachineInstr *, unsigned > CarriedLatencies
StallCosts getStallCosts(SUnit *SU, SchedBoundary &Zone)
void initialize(ScheduleDAGMI *DAG, const TargetSchedModel *SchedModel, const TargetRegisterInfo *TRI, const MachineLoopInfo *MLI)
void sortHWUIResources()
Sort the HardwarUnitInfo vector.
void collectRegionSummary()
Walk over the region and collect characteristics for the various heuristics.
bool tryCriticalResource(GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, SchedBoundary *Zone) const
Check for critical resource consumption.
bool tryCriticalResourceDependency(GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, SchedBoundary *Zone) const
Check for dependencies of instructions that use prioritized HardwareUnits.
bool tryEffectiveStall(GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, SchedBoundary &Zone)
AMDGPU::CarriedLatency RegionCarriedLatency
unsigned getHWUICyclesForMI(MachineInstr *MI)
Compute the blocking cycles for the appropriate HardwareUnit given an MI.
SmallVector< HardwareUnitInfo, 8 > HWUInfo
const TargetSchedModel * SchedModel
unsigned getMaxBlockingCycles(const MCSchedClassDesc *SC, const MachineInstr *MI)
bool tryMemoryPipeline(GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, SchedBoundary &Zone)
Prioritize instructions involved the memory pipeline.
unsigned getStructuralStallCycles(SchedBoundary &Zone, SUnit *SU)
GCNSchedStrategy(const MachineSchedContext *C)
SmallVector< GCNSchedStageID, 4 > SchedStages
void schedNode(SUnit *SU, bool IsTopNode) override
Notify MachineSchedStrategy that ScheduleDAGMI has scheduled an instruction and updated scheduled/rem...
std::vector< unsigned > Pressure
void initialize(ScheduleDAGMI *DAG) override
Initialize the strategy after building the DAG for a new region.
void printCandidateDecision(const SchedCandidate &Current, const SchedCandidate &Preferred)
GCNManualDownwardRPTracker DownwardTracker
void initCandidate(SchedCandidate &Cand, SUnit *SU, bool AtTop, const RegPressureTracker &RPTracker, const SIRegisterInfo *SRI, unsigned SGPRPressure, unsigned VGPRPressure, unsigned AGPRPressure, bool IsBottomUp)
MachineSchedPolicy RegionPolicy
const TargetSchedModel * SchedModel
static const char * getReasonStr(GenericSchedulerBase::CandReason Reason)
const MachineSchedContext * Context
const TargetRegisterInfo * TRI
SchedCandidate TopCand
Candidate last picked from Top boundary.
ScheduleDAGMILive * DAG
HardwareUnitInfo is a wrapper class which maps to some real hardware resource.
void markScheduled(SUnit *SU, unsigned BlockingCycles)
Update the state for SU being scheduled by removing it from the AllSUs and reducing its BlockingCycle...
SUnit * getNextTargetSU(bool LookDeep=false) const
void insert(SUnit *SU, unsigned BlockingCycles)
Insert the SU into AllSUs and account its BlockingCycles into the TotalCycles.
void finalizeCycles()
After we've collected all the region pressure for this HWUI, correct for any specifics of the behavio...
AMDGPU::InstructionFlavor getType() const
unsigned getBufferAvailableCycle(unsigned CurrCycle)
SUnit * getHigherPriority(SUnit *SU, SUnit *Other) const
unsigned getNumBlocks() const
Get the number of blocks in this loop in constant time.
Represents a single loop in the control flow graph.
Definition LoopInfo.h:40
int getNumber() const
MachineBasicBlocks are uniquely numbered at the function level, unless they're not in a MachineFuncti...
MachineInstrBundleIterator< MachineInstr > iterator
Representation of each machine instruction.
LLVM_ABI void print(raw_ostream &OS, bool IsStandalone=true, bool SkipOpers=false, bool SkipDebugLoc=false, bool AddNewLine=true, const TargetInstrInfo *TII=nullptr) const
Print this MI to OS.
MachineOperand class - Representation of each machine instruction operand.
virtual void initPolicy(MachineBasicBlock::iterator Begin, MachineBasicBlock::iterator End, unsigned NumRegionInstrs)
Optionally override the per-region scheduling policy.
Helpers for implementing custom MachineSchedStrategy classes.
Track the current register pressure at some position in the instruction stream, and remember the high...
const std::vector< unsigned > & getRegSetPressureAtPos() const
Get the register set pressure at the current position, which may be less than the pressure across the...
static bool isDS(const MachineInstr &MI)
static bool isVMEM(const MachineInstr &MI)
static bool isSMRD(const MachineInstr &MI)
static bool isSALU(const MachineInstr &MI)
static bool isSWMMAC(const MachineInstr &MI)
unsigned getBlockingCycles(const MachineInstr &MI) const
static bool isVALU(const MachineInstr &MI, bool AllowLDSDMA)
static bool isTRANS(const MachineInstr &MI)
static bool isWaitcnt(unsigned Opcode)
static bool isMFMA(const MachineInstr &MI)
static bool isWMMA(const MachineInstr &MI)
static bool isLDSDMA(const MachineInstr &MI)
Scheduling unit. This is a node in the scheduling DAG.
unsigned TopReadyCycle
Cycle relative to start when node is ready.
unsigned NodeNum
Entry # of node in the node vector.
unsigned getHeight() const
Returns the height of this node, which is the length of the maximum path down to any node which has n...
unsigned getDepth() const
Returns the depth of this node, which is the length of the maximum path up to any node which has no p...
bool isScheduled
True once scheduled.
unsigned ParentClusterIdx
The parent cluster id.
bool hasReservedResource
Uses a reserved resource.
bool isBottomReady() const
bool isTopReady() const
MachineInstr * getInstr() const
Returns the representative MachineInstr for this SUnit.
Each Scheduling boundary is associated with ready queues.
LLVM_ABI unsigned getLatencyStallCycles(SUnit *SU)
Get the difference between the given SUnit's ready time and the current cycle.
LLVM_ABI SUnit * pickOnlyChoice()
Call this before applying any other heuristics to the Available queue.
unsigned getCurrCycle() const
Number of cycles to issue the instructions scheduled in this zone.
std::unique_ptr< ScheduleHazardRecognizer > HazardRec
LLVM_ABI std::pair< unsigned, unsigned > getNextResourceCycle(const MCSchedClassDesc *SC, unsigned PIdx, unsigned ReleaseAtCycle, unsigned AcquireAtCycle)
Compute the next cycle at which the given processor resource can be scheduled.
A ScheduleDAG for scheduling lists of MachineInstr.
ScheduleDAGMILive is an implementation of ScheduleDAGInstrs that schedules machine instructions while...
ScheduleDAGMI is an implementation of ScheduleDAGInstrs that simply schedules machine instructions ac...
void addMutation(std::unique_ptr< ScheduleDAGMutation > Mutation)
Add a postprocessing step to the DAG builder.
SlotIndex - An opaque wrapper around machine indexes.
Definition SlotIndexes.h:66
static bool isEarlierInstr(SlotIndex A, SlotIndex B)
isEarlierInstr - Return true if A refers to an instruction earlier than B.
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
TargetRegisterInfo base class - We assume that the target defines a static array of TargetRegisterDes...
Provide an instruction scheduling machine model to CodeGen passes.
const MCWriteProcResEntry * ProcResIter
A Use represents the edge between a Value definition and its users.
Definition Use.h:35
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
InstructionFlavor
Classification of instructions by execution characteristics.
constexpr unsigned MaxCoExecStages
Max stages: INT8 16x16x64 = 17 cycles, round up for safety.
CoExecInfo getMFMACoExecInfo(unsigned Opcode)
Get co-execution info for a gfx950 MFMA instruction.
CoExecMask CoExecMaskT
InstructionFlavor classifyFlavor(const MachineInstr &MI, const SIInstrInfo &SII)
Classify MI into the execution flavor that drives both the scheduler's slot preferences and the hazar...
StringRef getReasonName(AMDGPUSchedReason R)
StringRef getFlavorName(InstructionFlavor F)
ValuesClass values(OptsTy... Options)
Helper to build a ValuesClass by forwarding a variable number of arguments as an initializer list to ...
initializer< Ty > init(const Ty &Val)
This is an optimization pass for GlobalISel generic memory operations.
LLVM_ABI int biasPhysReg(const SUnit *SU, bool isTop, bool BiasPRegsExtra=false)
Minimize physical register live ranges.
std::unique_ptr< ScheduleDAGMutation > createAMDGPUBarrierLatencyDAGMutation(MachineFunction *MF)
LLVM_ABI unsigned getWeakLeft(const SUnit *SU, bool isTop)
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
std::unique_ptr< ScheduleDAGMutation > createIGroupLPDAGMutation(AMDGPU::SchedulingPhase Phase)
Phase specifes whether or not this is a reentry into the IGroupLPDAGMutation.
LLVM_ABI bool tryPressure(const PressureChange &TryP, const PressureChange &CandP, GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, GenericSchedulerBase::CandReason Reason, const TargetRegisterInfo *TRI, const MachineFunction &MF)
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1652
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
cl::opt< unsigned, false, VGPRThresholdParser > VGPRThresholdPercentOpt
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
ScheduleDAGInstrs * createGCNNoopPostMachineScheduler(MachineSchedContext *C)
LLVM_ABI bool tryLatency(GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, SchedBoundary &Zone)
ScheduleDAGInstrs * createGCNCoExecMachineScheduler(MachineSchedContext *C)
bool isTheSameCluster(unsigned A, unsigned B)
Return whether the input cluster ID's are the same and valid.
DWARFExpression::Operation Op
LLVM_ABI bool tryGreater(int TryVal, int CandVal, GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, GenericSchedulerBase::CandReason Reason)
LLVM_ABI MISched::Direction getPreRADirection()
Returns -misched-prera-direction.
LLVM_ABI bool tryLess(int TryVal, int CandVal, GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, GenericSchedulerBase::CandReason Reason)
Return true if this heuristic determines order.
@ Enabled
Convert any .debug_str_offsets tables to DWARF64 if needed.
Definition DWP.h:31
@ End
Run the machine verifier once, at the end of the pipeline.
Definition CodeGen.h:267
Co-execution characteristics for a multi-cycle instruction.
unsigned TotalWindow
Number of cycles in the co-execution window, counting any trailing vacant stages.
StringRef Pattern
Pattern string for display (e.g., "0EIIEEIIV").
static CoExecInfo build(unsigned UnitOccupancy, unsigned TotalWindow, const char *Pattern)
Build a CoExecInfo from an occupancy and stage pattern.
CoExecSlotInfo Slots[MaxCoExecStages]
Per-stage slot info (capability mask).
Policy for scheduling the next instruction in the candidate's zone.
Store the state used by GenericScheduler heuristics, required for the lifetime of one invocation of p...
LLVM_ABI void initResourceDelta(const ScheduleDAGMI *DAG, const TargetSchedModel *SchedModel)
Status of an instruction's critical resource consumption.
Summarize the scheduling resources required for an instruction of a particular scheduling class.
Definition MCSchedule.h:130
Identify one of the processor resource kinds consumed by a particular scheduling class for the specif...
Definition MCSchedule.h:75
MachineSchedContext provides enough context from the MachineScheduler pass for the target to instanti...