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
15#include "AMDGPUIGroupLP.h"
16#include "GCNHazardRecognizer.h"
17#include "llvm/Support/Debug.h"
18
19using namespace llvm;
20using namespace llvm::AMDGPU;
21
22#define DEBUG_TYPE "machine-scheduler"
23
24namespace {
25
26// Used to disable post-RA scheduling with function level granularity.
27class GCNNoopPostScheduleDAG final : public ScheduleDAGInstrs {
28public:
29 explicit GCNNoopPostScheduleDAG(MachineSchedContext *C)
30 : ScheduleDAGInstrs(*C->MF, C->MLI, /*RemoveKillFlags=*/true) {}
31
32 // Do nothing.
33 void schedule() override {}
34};
35
36} // namespace
37
39 // pickOnlyChoice() releases pending instructions and checks for new hazards.
40 SUnit *OnlyChoice = Zone.pickOnlyChoice();
41 if (!Zone.Pending.empty())
42 return nullptr;
43
44 return OnlyChoice;
45}
46
47/// Apply \p ExtraBits to every slot in \p Info starting with \p StartIndex
48/// Used by MFMA co-exec rules, because MFMA co-exec slots are incremental, i.e.
49/// for every slot N it supports all instructions which were supported by the
50/// previous slot N-1 and may support something extra.
53 unsigned StartIndex) {
54 for (unsigned Index = StartIndex; Index < Info.TotalWindow; ++Index)
55 Info.Slots[Index].Mask |= ExtraBits;
56}
57
58/// Get co-execution info for a gfx950 MFMA instruction.
59/// The occupancy (cycles until the next MFMA may issue) is expressed as the
60/// first stage carrying the WMMA bit.
62 using namespace llvm;
63 using namespace llvm::AMDGPU;
64 CoExecInfo Res;
65 for (unsigned I = 0; I < MaxCoExecStages; ++I)
67
68 // TODO: Implement proper patterns support (for debugging purposes).
69 // Existing pattern letters are WMMA-specific and will probably be confusing
70 // if used as-is for MFMA. Inventing new MFMA-specific letters is an option,
71 // but perhaps the pattern should be instead dynamically reconstructed when
72 // needed by printing specific slots in full instead of a key for them.
73 Res.Pattern = "undefinedundefinedundefinedundefined";
74
75 switch (Opcode) {
76 // 4-cycle occupancy, 8-cycle window.
77 case V_MFMA_F32_16X16X128_F8F6F4_f4_f4_e64:
78 case V_MFMA_F32_16X16X128_F8F6F4_f4_f4_vgprcd_e64:
79 case V_MFMA_F32_16X16X128_F8F6F4_f4_f4_gfx940_acd:
80 case V_MFMA_F32_16X16X128_F8F6F4_f4_f4_gfx940_vcd:
81 case V_MFMA_F32_16X16X128_F8F6F4_f4_f6_e64:
82 case V_MFMA_F32_16X16X128_F8F6F4_f4_f6_vgprcd_e64:
83 case V_MFMA_F32_16X16X128_F8F6F4_f4_f6_gfx940_acd:
84 case V_MFMA_F32_16X16X128_F8F6F4_f4_f6_gfx940_vcd:
85 case V_MFMA_F32_16X16X128_F8F6F4_f6_f4_e64:
86 case V_MFMA_F32_16X16X128_F8F6F4_f6_f4_vgprcd_e64:
87 case V_MFMA_F32_16X16X128_F8F6F4_f6_f4_gfx940_acd:
88 case V_MFMA_F32_16X16X128_F8F6F4_f6_f4_gfx940_vcd:
89 case V_MFMA_F32_16X16X128_F8F6F4_f6_f6_e64:
90 case V_MFMA_F32_16X16X128_F8F6F4_f6_f6_vgprcd_e64:
91 case V_MFMA_F32_16X16X128_F8F6F4_f6_f6_gfx940_acd:
92 case V_MFMA_F32_16X16X128_F8F6F4_f6_f6_gfx940_vcd:
93 case V_MFMA_F32_16X16X32_BF16_e64:
94 case V_MFMA_F32_16X16X32_BF16_vgprcd_e64:
95 case V_MFMA_F32_16X16X32_BF16_gfx940_acd:
96 case V_MFMA_F32_16X16X32_BF16_gfx940_vcd:
97 case V_MFMA_I32_16X16X64_I8_e64:
98 case V_MFMA_I32_16X16X64_I8_vgprcd_e64:
99 case V_MFMA_I32_16X16X64_I8_gfx940_acd:
100 case V_MFMA_I32_16X16X64_I8_gfx940_vcd:
101 case V_MFMA_F32_16X16X32_F16_e64:
102 case V_MFMA_F32_16X16X32_F16_vgprcd_e64:
103 case V_MFMA_F32_16X16X32_F16_gfx940_acd:
104 case V_MFMA_F32_16X16X32_F16_gfx940_vcd:
105 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f4_e64:
106 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f4_vgprcd_e64:
107 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f4_gfx940_acd:
108 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f4_gfx940_vcd:
109 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f6_e64:
110 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f6_vgprcd_e64:
111 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f6_gfx940_acd:
112 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f6_gfx940_vcd:
113 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f4_e64:
114 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f4_vgprcd_e64:
115 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f4_gfx940_acd:
116 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f4_gfx940_vcd:
117 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f6_e64:
118 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f6_vgprcd_e64:
119 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f6_gfx940_acd:
120 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f6_gfx940_vcd:
121 // GFX9 Shader Programming Guide lists those SMFMAC separately, but for
122 // intended purposes here all those instructions are the same. This comment
123 // is to simplify reverse mapping to the SPG.
124 case V_SMFMAC_F32_16X16X64_BF16_e64:
125 case V_SMFMAC_F32_16X16X64_BF16_gfx940:
126 case V_SMFMAC_I32_16X16X128_I8_e64:
127 case V_SMFMAC_I32_16X16X128_I8_gfx940:
128 case V_SMFMAC_F32_16X16X128_BF8_BF8_e64:
129 case V_SMFMAC_F32_16X16X128_BF8_BF8_gfx940:
130 case V_SMFMAC_F32_16X16X128_BF8_FP8_e64:
131 case V_SMFMAC_F32_16X16X128_BF8_FP8_gfx940:
132 case V_SMFMAC_F32_16X16X128_FP8_BF8_e64:
133 case V_SMFMAC_F32_16X16X128_FP8_BF8_gfx940:
134 case V_SMFMAC_F32_16X16X128_FP8_FP8_e64:
135 case V_SMFMAC_F32_16X16X128_FP8_FP8_gfx940:
136 case V_SMFMAC_F32_16X16X64_F16_e64:
137 case V_SMFMAC_F32_16X16X64_F16_gfx940:
138 Res.TotalWindow = 8;
142 return Res;
143
144 // 8-cycle occupancy, 12-cycle window.
145 case V_MFMA_F32_16X16X128_F8F6F4_f4_f8_e64:
146 case V_MFMA_F32_16X16X128_F8F6F4_f4_f8_vgprcd_e64:
147 case V_MFMA_F32_16X16X128_F8F6F4_f4_f8_gfx940_acd:
148 case V_MFMA_F32_16X16X128_F8F6F4_f4_f8_gfx940_vcd:
149 case V_MFMA_F32_16X16X128_F8F6F4_f6_f8_e64:
150 case V_MFMA_F32_16X16X128_F8F6F4_f6_f8_vgprcd_e64:
151 case V_MFMA_F32_16X16X128_F8F6F4_f6_f8_gfx940_acd:
152 case V_MFMA_F32_16X16X128_F8F6F4_f6_f8_gfx940_vcd:
153 case V_MFMA_F32_16X16X128_F8F6F4_f8_f4_e64:
154 case V_MFMA_F32_16X16X128_F8F6F4_f8_f4_vgprcd_e64:
155 case V_MFMA_F32_16X16X128_F8F6F4_f8_f4_gfx940_acd:
156 case V_MFMA_F32_16X16X128_F8F6F4_f8_f4_gfx940_vcd:
157 case V_MFMA_F32_16X16X128_F8F6F4_f8_f6_e64:
158 case V_MFMA_F32_16X16X128_F8F6F4_f8_f6_vgprcd_e64:
159 case V_MFMA_F32_16X16X128_F8F6F4_f8_f6_gfx940_acd:
160 case V_MFMA_F32_16X16X128_F8F6F4_f8_f6_gfx940_vcd:
161 case V_MFMA_F32_16X16X128_F8F6F4_f8_f8_e64:
162 case V_MFMA_F32_16X16X128_F8F6F4_f8_f8_vgprcd_e64:
163 case V_MFMA_F32_16X16X128_F8F6F4_f8_f8_gfx940_acd:
164 case V_MFMA_F32_16X16X128_F8F6F4_f8_f8_gfx940_vcd:
165 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f8_e64:
166 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f8_vgprcd_e64:
167 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f8_gfx940_acd:
168 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f4_f8_gfx940_vcd:
169 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f8_e64:
170 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f8_vgprcd_e64:
171 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f8_gfx940_acd:
172 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f6_f8_gfx940_vcd:
173 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f4_e64:
174 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f4_vgprcd_e64:
175 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f4_gfx940_acd:
176 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f4_gfx940_vcd:
177 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f6_e64:
178 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f6_vgprcd_e64:
179 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f6_gfx940_acd:
180 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f6_gfx940_vcd:
181 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f8_e64:
182 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f8_vgprcd_e64:
183 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f8_gfx940_acd:
184 case V_MFMA_SCALE_F32_16X16X128_F8F6F4_f8_f8_gfx940_vcd:
185 Res.TotalWindow = 12;
190 return Res;
191
192 // 4-cycle occupancy, 8-cycle window.
193 case V_MFMA_F32_32X32X64_F8F6F4_f4_f4_e64:
194 case V_MFMA_F32_32X32X64_F8F6F4_f4_f4_mac_e64:
195 case V_MFMA_F32_32X32X64_F8F6F4_f4_f4_mac_vgprcd_e64:
196 case V_MFMA_F32_32X32X64_F8F6F4_f4_f4_vgprcd_e64:
197 case V_MFMA_F32_32X32X64_F8F6F4_f4_f4_gfx940_acd:
198 case V_MFMA_F32_32X32X64_F8F6F4_f4_f4_gfx940_vcd:
199 case V_MFMA_F32_32X32X64_F8F6F4_f4_f6_e64:
200 case V_MFMA_F32_32X32X64_F8F6F4_f4_f6_mac_e64:
201 case V_MFMA_F32_32X32X64_F8F6F4_f4_f6_mac_vgprcd_e64:
202 case V_MFMA_F32_32X32X64_F8F6F4_f4_f6_vgprcd_e64:
203 case V_MFMA_F32_32X32X64_F8F6F4_f4_f6_gfx940_acd:
204 case V_MFMA_F32_32X32X64_F8F6F4_f4_f6_gfx940_vcd:
205 case V_MFMA_F32_32X32X64_F8F6F4_f6_f4_e64:
206 case V_MFMA_F32_32X32X64_F8F6F4_f6_f4_mac_e64:
207 case V_MFMA_F32_32X32X64_F8F6F4_f6_f4_mac_vgprcd_e64:
208 case V_MFMA_F32_32X32X64_F8F6F4_f6_f4_vgprcd_e64:
209 case V_MFMA_F32_32X32X64_F8F6F4_f6_f4_gfx940_acd:
210 case V_MFMA_F32_32X32X64_F8F6F4_f6_f4_gfx940_vcd:
211 case V_MFMA_F32_32X32X64_F8F6F4_f6_f6_e64:
212 case V_MFMA_F32_32X32X64_F8F6F4_f6_f6_mac_e64:
213 case V_MFMA_F32_32X32X64_F8F6F4_f6_f6_mac_vgprcd_e64:
214 case V_MFMA_F32_32X32X64_F8F6F4_f6_f6_vgprcd_e64:
215 case V_MFMA_F32_32X32X64_F8F6F4_f6_f6_gfx940_acd:
216 case V_MFMA_F32_32X32X64_F8F6F4_f6_f6_gfx940_vcd:
217 case V_MFMA_F32_32X32X16_BF16_e64:
218 case V_MFMA_F32_32X32X16_BF16_mac_e64:
219 case V_MFMA_F32_32X32X16_BF16_mac_vgprcd_e64:
220 case V_MFMA_F32_32X32X16_BF16_vgprcd_e64:
221 case V_MFMA_F32_32X32X16_BF16_gfx940_acd:
222 case V_MFMA_F32_32X32X16_BF16_gfx940_vcd:
223 case V_MFMA_I32_32X32X32_I8_e64:
224 case V_MFMA_I32_32X32X32_I8_mac_e64:
225 case V_MFMA_I32_32X32X32_I8_mac_vgprcd_e64:
226 case V_MFMA_I32_32X32X32_I8_vgprcd_e64:
227 case V_MFMA_I32_32X32X32_I8_gfx940_acd:
228 case V_MFMA_I32_32X32X32_I8_gfx940_vcd:
229 case V_MFMA_F32_32X32X16_F16_e64:
230 case V_MFMA_F32_32X32X16_F16_mac_e64:
231 case V_MFMA_F32_32X32X16_F16_mac_vgprcd_e64:
232 case V_MFMA_F32_32X32X16_F16_vgprcd_e64:
233 case V_MFMA_F32_32X32X16_F16_gfx940_acd:
234 case V_MFMA_F32_32X32X16_F16_gfx940_vcd:
235 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f4_gfx940_acd:
236 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f4_gfx940_vcd:
237 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f6_gfx940_acd:
238 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f6_gfx940_vcd:
239 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f4_gfx940_acd:
240 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f4_gfx940_vcd:
241 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f6_gfx940_acd:
242 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f6_gfx940_vcd:
243 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f4_e64:
244 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f6_e64:
245 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f4_e64:
246 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f6_e64:
247 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f4_vgprcd_e64:
248 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f6_vgprcd_e64:
249 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f4_vgprcd_e64:
250 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f6_vgprcd_e64:
251 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f4_mac_e64:
252 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f6_mac_e64:
253 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f4_mac_e64:
254 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f6_mac_e64:
255 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f4_mac_vgprcd_e64:
256 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f6_mac_vgprcd_e64:
257 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f4_mac_vgprcd_e64:
258 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f6_mac_vgprcd_e64:
259 Res.TotalWindow = 8;
263 return Res;
264
265 // 16-cycle occupancy, 20-cycle window.
266 case V_MFMA_F32_32X32X64_F8F6F4_f4_f8_e64:
267 case V_MFMA_F32_32X32X64_F8F6F4_f4_f8_mac_e64:
268 case V_MFMA_F32_32X32X64_F8F6F4_f4_f8_mac_vgprcd_e64:
269 case V_MFMA_F32_32X32X64_F8F6F4_f4_f8_vgprcd_e64:
270 case V_MFMA_F32_32X32X64_F8F6F4_f4_f8_gfx940_acd:
271 case V_MFMA_F32_32X32X64_F8F6F4_f4_f8_gfx940_vcd:
272 case V_MFMA_F32_32X32X64_F8F6F4_f6_f8_e64:
273 case V_MFMA_F32_32X32X64_F8F6F4_f6_f8_mac_e64:
274 case V_MFMA_F32_32X32X64_F8F6F4_f6_f8_mac_vgprcd_e64:
275 case V_MFMA_F32_32X32X64_F8F6F4_f6_f8_vgprcd_e64:
276 case V_MFMA_F32_32X32X64_F8F6F4_f6_f8_gfx940_acd:
277 case V_MFMA_F32_32X32X64_F8F6F4_f6_f8_gfx940_vcd:
278 case V_MFMA_F32_32X32X64_F8F6F4_f8_f4_e64:
279 case V_MFMA_F32_32X32X64_F8F6F4_f8_f4_mac_e64:
280 case V_MFMA_F32_32X32X64_F8F6F4_f8_f4_mac_vgprcd_e64:
281 case V_MFMA_F32_32X32X64_F8F6F4_f8_f4_vgprcd_e64:
282 case V_MFMA_F32_32X32X64_F8F6F4_f8_f4_gfx940_acd:
283 case V_MFMA_F32_32X32X64_F8F6F4_f8_f4_gfx940_vcd:
284 case V_MFMA_F32_32X32X64_F8F6F4_f8_f6_e64:
285 case V_MFMA_F32_32X32X64_F8F6F4_f8_f6_mac_e64:
286 case V_MFMA_F32_32X32X64_F8F6F4_f8_f6_mac_vgprcd_e64:
287 case V_MFMA_F32_32X32X64_F8F6F4_f8_f6_vgprcd_e64:
288 case V_MFMA_F32_32X32X64_F8F6F4_f8_f6_gfx940_acd:
289 case V_MFMA_F32_32X32X64_F8F6F4_f8_f6_gfx940_vcd:
290 case V_MFMA_F32_32X32X64_F8F6F4_f8_f8_e64:
291 case V_MFMA_F32_32X32X64_F8F6F4_f8_f8_mac_e64:
292 case V_MFMA_F32_32X32X64_F8F6F4_f8_f8_mac_vgprcd_e64:
293 case V_MFMA_F32_32X32X64_F8F6F4_f8_f8_vgprcd_e64:
294 case V_MFMA_F32_32X32X64_F8F6F4_f8_f8_gfx940_acd:
295 case V_MFMA_F32_32X32X64_F8F6F4_f8_f8_gfx940_vcd:
296 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f8_e64:
297 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f8_e64:
298 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f4_e64:
299 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f6_e64:
300 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f8_e64:
301 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f8_vgprcd_e64:
302 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f8_vgprcd_e64:
303 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f4_vgprcd_e64:
304 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f6_vgprcd_e64:
305 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f8_vgprcd_e64:
306 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f8_mac_e64:
307 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f8_mac_e64:
308 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f4_mac_e64:
309 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f6_mac_e64:
310 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f8_mac_e64:
311 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f8_mac_vgprcd_e64:
312 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f8_mac_vgprcd_e64:
313 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f4_mac_vgprcd_e64:
314 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f6_mac_vgprcd_e64:
315 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f8_mac_vgprcd_e64:
316 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f8_gfx940_acd:
317 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f4_f8_gfx940_vcd:
318 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f8_gfx940_acd:
319 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f6_f8_gfx940_vcd:
320 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f4_gfx940_acd:
321 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f4_gfx940_vcd:
322 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f6_gfx940_acd:
323 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f6_gfx940_vcd:
324 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f8_gfx940_acd:
325 case V_MFMA_SCALE_F32_32X32X64_F8F6F4_f8_f8_gfx940_vcd:
326 Res.TotalWindow = 20;
331 return Res;
332
333 // 9-cycle occupancy, 12-cycle window.
334 case V_SMFMAC_F32_32X32X32_BF16_e64:
335 case V_SMFMAC_F32_32X32X32_BF16_gfx940:
336 case V_SMFMAC_I32_32X32X64_I8_e64:
337 case V_SMFMAC_I32_32X32X64_I8_gfx940:
338 case V_SMFMAC_F32_32X32X64_BF8_BF8_e64:
339 case V_SMFMAC_F32_32X32X64_BF8_BF8_gfx940:
340 case V_SMFMAC_F32_32X32X64_BF8_FP8_e64:
341 case V_SMFMAC_F32_32X32X64_BF8_FP8_gfx940:
342 case V_SMFMAC_F32_32X32X64_FP8_BF8_e64:
343 case V_SMFMAC_F32_32X32X64_FP8_BF8_gfx940:
344 case V_SMFMAC_F32_32X32X64_FP8_FP8_e64:
345 case V_SMFMAC_F32_32X32X64_FP8_FP8_gfx940:
346 case V_SMFMAC_F32_32X32X32_F16_e64:
347 case V_SMFMAC_F32_32X32X32_F16_gfx940:
348 Res.TotalWindow = 12;
352 return Res;
353
354 // 18-cycle occupancy, 19-cycle window.
355 case V_MFMA_F64_16X16X4F64_e64:
356 case V_MFMA_F64_16X16X4F64_mac_e64:
357 case V_MFMA_F64_16X16X4F64_mac_vgprcd_e64:
358 case V_MFMA_F64_16X16X4F64_vgprcd_e64:
359 Res.TotalWindow = 19;
362 return Res;
363
364 default:
365 // Default fallback: permissive 8-cycle pattern
366 return CoExecInfo::build(9, "AAAAAAAAA");
367 }
368}
369
371 const SIInstrInfo &SII) {
372 if (MI.isDebugInstr())
374
375 unsigned Opc = MI.getOpcode();
376
377 // Check for specific opcodes first.
378 if (Opc == AMDGPU::ATOMIC_FENCE || Opc == AMDGPU::S_WAIT_ASYNCCNT ||
379 Opc == AMDGPU::S_WAIT_TENSORCNT || Opc == AMDGPU::S_BARRIER_WAIT ||
380 Opc == AMDGPU::S_BARRIER_SIGNAL_IMM)
382
383 if (SII.isLDSDMA(MI))
385
386 if (SII.isMFMA(MI)) {
387 // TODO: Consider further sub-classifying this (XDL, XDL2x, S/DGEMM).
388 // GFX9 SPG sub-classifies MFMA into XDL, XDL2x and S/DGEMM, because only
389 // certain sub-classes can be co-executed in certain slots. For now, we
390 // simply treat them all as one to simplify the change and leave the rest
391 // to a follow-up fine-tuning.
393 }
394
395 if (SII.isWMMA(MI) || SII.isSWMMAC(MI))
397
398 if (SII.isTRANS(MI))
400
401 if (SII.isVALU(MI, /*AllowLDSDMA=*/true))
403
404 if (SII.isSMRD(MI))
406
407 if (SII.isDS(MI))
409
410 if (SII.isVMEM(MI))
412
413 if (SII.isSALU(MI))
415
417}
418
420 for (SUnit *PrioritySU : PrioritySUs) {
421 if (!PrioritySU->isTopReady())
422 return PrioritySU;
423 }
424
425 if (!LookDeep)
426 return nullptr;
427
428 unsigned MinDepth = std::numeric_limits<unsigned int>::max();
429 SUnit *TargetSU = nullptr;
430 for (auto *SU : AllSUs) {
431 if (SU->isScheduled)
432 continue;
433
434 if (SU->isTopReady())
435 continue;
436
437 if (SU->getDepth() < MinDepth) {
438 MinDepth = SU->getDepth();
439 TargetSU = SU;
440 }
441 }
442 return TargetSU;
443}
444
445void HardwareUnitInfo::insert(SUnit *SU, unsigned BlockingCycles) {
446 if (!AllSUs.insert(SU))
447 llvm_unreachable("HardwareUnit already contains SU!");
448
449 TotalCycles += BlockingCycles;
450
451 if (PrioritySUs.empty()) {
452 PrioritySUs.insert(SU);
453 return;
454 }
455 unsigned SUDepth = SU->getDepth();
456 unsigned CurrDepth = (*PrioritySUs.begin())->getDepth();
457 if (SUDepth > CurrDepth)
458 return;
459
460 if (SUDepth == CurrDepth) {
461 PrioritySUs.insert(SU);
462 return;
463 }
464
465 // SU is lower depth and should be prioritized.
466 PrioritySUs.clear();
467 PrioritySUs.insert(SU);
468}
469
470void HardwareUnitInfo::markScheduled(SUnit *SU, unsigned BlockingCycles) {
471 // We may want to ignore some HWUIs (e.g. InstructionFlavor::Other). To do so,
472 // we just clear the HWUI. However, we still have instructions which map to
473 // this HWUI. Don't bother managing the state for these HWUI.
474 if (TotalCycles == 0)
475 return;
476
477 ScheduledSUs.push_back(SU);
478 AllSUs.remove(SU);
479 PrioritySUs.remove(SU);
480
481 // BufferSize 0 is unlimited, while size 1 has no parallel buffering. In
482 // either case, each SU uses the HardwareUnit for BlockingCycles.
483 if (BufferSize <= 1 || (ScheduledSUs.size() % BufferSize == 0))
484 TotalCycles -= std::min(TotalCycles, BlockingCycles);
485
486 if (AllSUs.empty())
487 return;
488 if (PrioritySUs.empty()) {
489 for (auto SU : AllSUs) {
490 if (PrioritySUs.empty()) {
491 PrioritySUs.insert(SU);
492 continue;
493 }
494 unsigned SUDepth = SU->getDepth();
495 unsigned CurrDepth = (*PrioritySUs.begin())->getDepth();
496 if (SUDepth > CurrDepth)
497 continue;
498
499 if (SUDepth == CurrDepth) {
500 PrioritySUs.insert(SU);
501 continue;
502 }
503
504 // SU is lower depth and should be prioritized.
505 PrioritySUs.clear();
506 PrioritySUs.insert(SU);
507 }
508 }
509}
510
512 if (BufferSize == 0 || AllSUs.empty())
513 return;
514
515 // We estimate the amount of cycles it takes to free up a slot in the buffer
516 // as the average cycles per SU.
517 BufferCycles = TotalCycles / AllSUs.size();
518 // A single-entry buffer does not reduce TotalCycles.
519 if (BufferSize == 1)
520 return;
521
522 // The TotalCycles is normalized against the BufferSize.
523 // This provides an estimate of the TotalCycles which is not always accurate
524 // -- particularly in cases where we have fewer instructions than the
525 // BufferSize. For example, if we have 2 instructions which each take 50
526 // cycles and a BufferSize of 16, then a TotalCycles of 51 cycles would be
527 // somewhat accurate. This normalization calculates TotalCycles as 6. However,
528 // if we have 64 of these instructions, our normalized estimate of 200 is more
529 // reasonable, given the more accurate measure is 264. Having a completely
530 // accurate measure is not very important, since this metric is mainly used to
531 // compare the relative demand per HardwareUnit across the region. The simpler
532 // estimate makes managing the metric incrementally during scheduling much
533 // simpler.
534 TotalCycles /= BufferSize;
535}
536
539 for (HardwareUnitInfo &HWUICand : HWUInfo) {
540 if (HWUICand.getType() == Flavor) {
541 return &HWUICand;
542 }
543 }
544 return nullptr;
545}
546
548 assert(SchedModel && SchedModel->hasInstrSchedModel());
549 MachineInstr *MI = SU->getInstr();
550 if (SII->isDS(*MI))
551 return SchedModel->computeInstrLatency(MI);
552
553 unsigned ReleaseAtCycle = 0;
554 const MCSchedClassDesc *SC = DAG->getSchedClass(SU);
555 for (TargetSchedModel::ProcResIter PI = SchedModel->getWriteProcResBegin(SC),
556 PE = SchedModel->getWriteProcResEnd(SC);
557 PI != PE; ++PI) {
558 ReleaseAtCycle = std::max(ReleaseAtCycle, (unsigned)PI->ReleaseAtCycle);
559 }
560 return ReleaseAtCycle;
561}
562
569
572 const TargetRegisterInfo *TRI) {
573 DAG = SchedDAG;
575 assert(SchedModel && SchedModel->hasInstrSchedModel());
576
577 SRI = static_cast<const SIRegisterInfo *>(TRI);
578 SII = static_cast<const SIInstrInfo *>(DAG->TII);
579
581
582 for (unsigned I = 0; I < HWUInfo.size(); I++) {
583 HWUInfo[I].reset();
584 HWUInfo[I].setType(I);
585 }
586
587 HWUInfo[(int)InstructionFlavor::WMMA].setProducesCoexecWindow(true);
588 HWUInfo[(int)InstructionFlavor::MultiCycleVALU].setProducesCoexecWindow(true);
589 HWUInfo[(int)InstructionFlavor::TRANS].setProducesCoexecWindow(true);
591
593}
594
596 if (!SchedModel || !SchedModel->hasInstrSchedModel())
597 return;
598
599 for (auto &SU : DAG->SUnits) {
600 const InstructionFlavor Flavor = classifyFlavor(*SU.getInstr(), *SII);
601 HWUInfo[(int)(Flavor)].insert(&SU, getHWUICyclesForInst(&SU));
602 }
603
604 for (auto &HWUI : HWUInfo)
605 HWUI.finalizeCycles();
606
608}
609
611 MachineBasicBlock *BB = DAG->begin()->getParent();
612 dbgs() << "\n=== Region: " << DAG->MF.getName() << " BB" << BB->getNumber()
613 << " (" << DAG->SUnits.size() << " SUs) ===\n";
614
615 dbgs() << "\nHWUI Resource Pressure:\n";
616 for (auto &HWUI : HWUInfo) {
617 if (HWUI.getTotalCycles() == 0)
618 continue;
619
620 StringRef Name = getFlavorName(HWUI.getType());
621 dbgs() << " " << Name << ": " << HWUI.getTotalCycles() << " cycles, "
622 << HWUI.size() << " instrs\n";
623 }
624 dbgs() << "\n";
625}
626
628 // Highest priority should be first.
630 // Prefer CoexecWindow producers
631 if (A.producesCoexecWindow() != B.producesCoexecWindow())
632 return A.producesCoexecWindow();
633
634 // Prefer more demanded resources
635 if (A.getTotalCycles() != B.getTotalCycles())
636 return A.getTotalCycles() > B.getTotalCycles();
637
638 // In ties -- prefer the resource with more instructions
639 if (A.size() != B.size())
640 return A.size() < B.size();
641
642 // Default to Flavor order
643 return static_cast<unsigned>(A.getType()) <
644 static_cast<unsigned>(B.getType());
645 });
646}
647
651
652 auto HasPrioritySU = [this, &Cand, &TryCand](unsigned ResourceIdx) {
653 const HardwareUnitInfo &HWUI = HWUInfo[ResourceIdx];
654
655 auto CandFlavor = classifyFlavor(*Cand.SU->getInstr(), *SII);
656 auto TryCandFlavor = classifyFlavor(*TryCand.SU->getInstr(), *SII);
657 bool LookDeep = (CandFlavor == InstructionFlavor::DS ||
658 TryCandFlavor == InstructionFlavor::DS) &&
660 auto *TargetSU = HWUI.getNextTargetSU(LookDeep);
661
662 // If we do not have a TargetSU for this resource, then it is not critical.
663 if (!TargetSU)
664 return false;
665
666 return true;
667 };
668
669 auto TryEnablesResource = [&Cand, &TryCand, this](unsigned ResourceIdx) {
670 const HardwareUnitInfo &HWUI = HWUInfo[ResourceIdx];
671 auto CandFlavor = classifyFlavor(*Cand.SU->getInstr(), *SII);
672
673 // We want to ensure our DS order matches WMMA order.
674 bool LookDeep = CandFlavor == InstructionFlavor::DS &&
676 auto *TargetSU = HWUI.getNextTargetSU(LookDeep);
677
678 bool CandEnables =
679 TargetSU != Cand.SU && DAG->IsReachable(TargetSU, Cand.SU);
680 bool TryCandEnables =
681 TargetSU != TryCand.SU && DAG->IsReachable(TargetSU, TryCand.SU);
682
683 if (!CandEnables && !TryCandEnables)
684 return false;
685
686 if (CandEnables && !TryCandEnables) {
689
690 return true;
691 }
692
693 if (!CandEnables && TryCandEnables) {
695 return true;
696 }
697
698 // Both enable, prefer the critical path.
699 unsigned CandHeight = Cand.SU->getHeight();
700 unsigned TryCandHeight = TryCand.SU->getHeight();
701
702 if (CandHeight > TryCandHeight) {
705
706 return true;
707 }
708
709 if (CandHeight < TryCandHeight) {
711 return true;
712 }
713
714 // Same critical path, just prefer original candidate.
717
718 return true;
719 };
720
721 for (unsigned I = 0; I < HWUInfo.size(); I++) {
722 // If we have encountered a resource that is not critical, then neither
723 // candidate enables a critical resource
724 if (!HasPrioritySU(I))
725 continue;
726
727 bool Enabled = TryEnablesResource(I);
728 // If neither has enabled the resource, continue to the next resource
729 if (Enabled)
730 return true;
731 }
732 return false;
733}
734
738 for (unsigned I = 0; I < HWUInfo.size(); I++) {
739 const HardwareUnitInfo &HWUI = HWUInfo[I];
740
741 bool CandUsesCrit = HWUI.contains(Cand.SU);
742 bool TryCandUsesCrit = HWUI.contains(TryCand.SU);
743
744 if (!CandUsesCrit && !TryCandUsesCrit)
745 continue;
746
747 if (CandUsesCrit != TryCandUsesCrit) {
748 if (CandUsesCrit) {
751 return true;
752 }
754 return true;
755 }
756
757 // Otherwise, both use the critical resource
758 // For longer latency InstructionFlavors, we should prioritize first by
759 // their enablement of critical resources
760 if (HWUI.getType() == InstructionFlavor::DS) {
761 if (tryCriticalResourceDependency(TryCand, Cand, Zone))
762 return true;
763 }
764
765 // Prioritize based on HWUI priorities.
766 SUnit *Match = HWUI.getHigherPriority(Cand.SU, TryCand.SU);
767 if (Match) {
768 if (Match == Cand.SU) {
771 return true;
772 }
774 return true;
775 }
776 }
777
778 return false;
779}
780
790
793 unsigned NumRegionInstrs) {
797 "coexec scheduler only supports top-down scheduling");
798 RegionPolicy.OnlyTopDown = true;
799 RegionPolicy.OnlyBottomUp = false;
800 RegionPolicy.ShouldTrackLaneMasks = true;
801}
802
804 // Coexecution scheduling strategy is only done top-down to support new
805 // resource balancing heuristics.
806 RegionPolicy.OnlyTopDown = true;
807 RegionPolicy.OnlyBottomUp = false;
808
810 Heurs.initialize(DAG, SchedModel, TRI);
811
812 // Replace the default hazard recognizer with our PreRA one so that pre-RA
813 // scheduling accounts for WMMA co-execution slot constraints. This must
814 // happen after GCNSchedStrategy::initialize() because
815 // GenericScheduler::initialize() calls SchedBoundary::reset(), which deletes
816 // and recreates the hazard recognizer each region.
817 Top.HazardRec = std::make_unique<GCNHazardRecognizer>(
819}
820
822 Heurs.updateForScheduling(SU);
823 GCNSchedStrategy::schedNode(SU, IsTopNode);
824}
825
827 assert(RegionPolicy.OnlyTopDown && !RegionPolicy.OnlyBottomUp &&
828 "coexec scheduler only supports top-down scheduling");
829
830 if (DAG->top() == DAG->bottom()) {
831 assert(Top.Available.empty() && Top.Pending.empty() &&
832 Bot.Available.empty() && Bot.Pending.empty() && "ReadyQ garbage");
833 return nullptr;
834 }
835
836 bool PickedPending = false;
837 SUnit *SU = nullptr;
838#ifndef NDEBUG
839 SchedCandidate *PickedCand = nullptr;
840#endif
841 do {
842 PickedPending = false;
843 SU = pickOnlyChoice(Top);
844 if (!SU) {
845 CandPolicy NoPolicy;
846 TopCand.reset(NoPolicy);
847 pickNodeFromQueue(Top, NoPolicy, DAG->getTopRPTracker(), TopCand,
848 PickedPending, /*IsBottomUp=*/false);
849 assert(TopCand.Reason != NoCand && "failed to find a candidate");
850 SU = TopCand.SU;
851#ifndef NDEBUG
852 PickedCand = &TopCand;
853#endif
854 }
855 IsTopNode = true;
856 } while (SU->isScheduled);
857
858 LLVM_DEBUG(if (PickedCand) dumpPickSummary(SU, IsTopNode, *PickedCand));
859
860 if (PickedPending) {
861 unsigned ReadyCycle = SU->TopReadyCycle;
862 unsigned CurrentCycle = Top.getCurrCycle();
863 if (ReadyCycle > CurrentCycle)
864 Top.bumpCycle(ReadyCycle);
865
866 // checkHazard() does not expose the exact cycle where the hazard clears.
867 while (Top.checkHazard(SU))
868 Top.bumpCycle(Top.getCurrCycle() + 1);
869
870 Top.releasePending();
871 }
872
873 if (SU->isTopReady())
874 Top.removeReady(SU);
875 if (SU->isBottomReady())
876 Bot.removeReady(SU);
877
878 LLVM_DEBUG(dbgs() << "Scheduling SU(" << SU->NodeNum << ") "
879 << *SU->getInstr());
880
881 assert(IsTopNode && "coexec scheduler must only schedule from top boundary");
882 return SU;
883}
884
886 SchedBoundary &Zone, const CandPolicy &ZonePolicy,
887 const RegPressureTracker &RPTracker, SchedCandidate &Cand,
888 bool &PickedPending, bool IsBottomUp) {
889 assert(Zone.isTop() && "coexec scheduler only supports top boundary");
890 assert(!IsBottomUp && "coexec scheduler only supports top-down scheduling");
891
892 const SIRegisterInfo *SRI = static_cast<const SIRegisterInfo *>(TRI);
894 unsigned SGPRPressure = 0;
895 unsigned VGPRPressure = 0;
896 PickedPending = false;
897 if (DAG->isTrackingPressure()) {
898 if (!useGCNTrackers()) {
899 SGPRPressure = Pressure[AMDGPU::RegisterPressureSets::SReg_32];
900 VGPRPressure = Pressure[AMDGPU::RegisterPressureSets::VGPR_32];
901 } else {
902 SGPRPressure = DownwardTracker.getPressure().getSGPRNum();
903 VGPRPressure = DownwardTracker.getPressure().getArchVGPRNum();
904 }
905 }
906
907 auto EvaluateQueue = [&](ReadyQueue &Q, bool FromPending) {
908 for (SUnit *SU : Q) {
909 SchedCandidate TryCand(ZonePolicy);
910 initCandidate(TryCand, SU, Zone.isTop(), RPTracker, SRI, SGPRPressure,
911 VGPRPressure, IsBottomUp);
912 SchedBoundary *ZoneArg = Cand.AtTop == TryCand.AtTop ? &Zone : nullptr;
913 tryCandidateCoexec(Cand, TryCand, ZoneArg);
914 if (TryCand.Reason != NoCand) {
915 if (TryCand.ResDelta == SchedResourceDelta())
916 TryCand.initResourceDelta(Zone.DAG, SchedModel);
917 LLVM_DEBUG(printCandidateDecision(Cand, TryCand));
918 PickedPending = FromPending;
919 Cand.setBest(TryCand);
920 } else {
921 LLVM_DEBUG(printCandidateDecision(TryCand, Cand));
922 }
923 }
924 };
925
926 LLVM_DEBUG(dbgs() << "Available Q:\n");
927 EvaluateQueue(Zone.Available, /*FromPending=*/false);
928
929 LLVM_DEBUG(dbgs() << "Pending Q:\n");
930 EvaluateQueue(Zone.Pending, /*FromPending=*/true);
931}
932
933#ifndef NDEBUG
935 SchedCandidate &Cand) {
936 const SIInstrInfo *SII = static_cast<const SIInstrInfo *>(DAG->TII);
937 unsigned Cycle = IsTopNode ? Top.getCurrCycle() : Bot.getCurrCycle();
938
939 dbgs() << "=== Pick @ Cycle " << Cycle << " ===\n";
940
941 const InstructionFlavor Flavor = classifyFlavor(*SU->getInstr(), *SII);
942 dbgs() << "Picked: SU(" << SU->NodeNum << ") ";
943 SU->getInstr()->print(dbgs(), /*IsStandalone=*/true, /*SkipOpers=*/false,
944 /*SkipDebugLoc=*/true);
945 dbgs() << " [" << getFlavorName(Flavor) << "]\n";
946
947 dbgs() << " Reason: ";
950 else if (Cand.Reason != NoCand)
952 else
953 dbgs() << "Unknown";
954 dbgs() << "\n\n";
955
957}
958#endif
959
961 SchedCandidate &TryCand,
962 SchedBoundary *Zone) {
963 // Initialize the candidate if needed.
964 if (!Cand.isValid()) {
965 TryCand.Reason = FirstValid;
966 return true;
967 }
968
969 // Bias PhysReg Defs and copies to their uses and defined respectively.
970 if (tryGreater(biasPhysReg(TryCand.SU, TryCand.AtTop),
971 biasPhysReg(Cand.SU, Cand.AtTop), TryCand, Cand, PhysReg))
972 return TryCand.Reason != NoCand;
973
974 // Avoid exceeding the target's limit.
975 if (DAG->isTrackingPressure() &&
976 tryPressure(TryCand.RPDelta.Excess, Cand.RPDelta.Excess, TryCand, Cand,
977 RegExcess, TRI, DAG->MF))
978 return TryCand.Reason != NoCand;
979
980 // We only compare a subset of features when comparing nodes between
981 // Top and Bottom boundary. Some properties are simply incomparable, in many
982 // other instances we should only override the other boundary if something
983 // is a clear good pick on one boundary. Skip heuristics that are more
984 // "tie-breaking" in nature.
985 bool SameBoundary = Zone != nullptr;
986 if (SameBoundary) {
987 // Compare candidates by the stall they would introduce if
988 // scheduled in the current cycle.
989 if (tryEffectiveStall(Cand, TryCand, *Zone))
990 return TryCand.Reason != NoCand;
991
992 Heurs.sortHWUIResources();
993 if (Heurs.tryCriticalResource(TryCand, Cand, Zone)) {
995 return TryCand.Reason != NoCand;
996 }
997
998 if (Heurs.tryCriticalResourceDependency(TryCand, Cand, Zone)) {
1000 return TryCand.Reason != NoCand;
1001 }
1002 }
1003
1004 // Keep clustered nodes together to encourage downstream peephole
1005 // optimizations which may reduce resource requirements.
1006 //
1007 // This is a best effort to set things up for a post-RA pass. Optimizations
1008 // like generating loads of multiple registers should ideally be done within
1009 // the scheduler pass by combining the loads during DAG postprocessing.
1010 unsigned CandZoneCluster = Cand.AtTop ? TopClusterID : BotClusterID;
1011 unsigned TryCandZoneCluster = TryCand.AtTop ? TopClusterID : BotClusterID;
1012 bool CandIsClusterSucc =
1013 isTheSameCluster(CandZoneCluster, Cand.SU->ParentClusterIdx);
1014 bool TryCandIsClusterSucc =
1015 isTheSameCluster(TryCandZoneCluster, TryCand.SU->ParentClusterIdx);
1016
1017 if (tryGreater(TryCandIsClusterSucc, CandIsClusterSucc, TryCand, Cand,
1018 Cluster))
1019 return TryCand.Reason != NoCand;
1020
1021 if (SameBoundary) {
1022 // Weak edges are for clustering and other constraints.
1023 if (tryLess(getWeakLeft(TryCand.SU, TryCand.AtTop),
1024 getWeakLeft(Cand.SU, Cand.AtTop), TryCand, Cand, Weak))
1025 return TryCand.Reason != NoCand;
1026 }
1027
1028 // Avoid increasing the max pressure of the entire region.
1029 if (DAG->isTrackingPressure() &&
1030 tryPressure(TryCand.RPDelta.CurrentMax, Cand.RPDelta.CurrentMax, TryCand,
1031 Cand, RegMax, TRI, DAG->MF))
1032 return TryCand.Reason != NoCand;
1033
1034 if (SameBoundary) {
1035 // Avoid serializing long latency dependence chains.
1036 // For acyclic path limited loops, latency was already checked above.
1037 if (!RegionPolicy.DisableLatencyHeuristic && TryCand.Policy.ReduceLatency &&
1038 !Rem.IsAcyclicLatencyLimited && tryLatency(TryCand, Cand, *Zone))
1039 return TryCand.Reason != NoCand;
1040
1041 // Fall through to original instruction order.
1042 if ((Zone->isTop() && TryCand.SU->NodeNum < Cand.SU->NodeNum) ||
1043 (!Zone->isTop() && TryCand.SU->NodeNum > Cand.SU->NodeNum)) {
1044 TryCand.Reason = NodeOrder;
1045 return true;
1046 }
1047 }
1048
1049 return false;
1050}
1051
1053 SchedCandidate &TryCand,
1054 SchedBoundary &Zone) {
1055 auto getBufferFullStalls = [this, &Zone](SUnit *SU) -> unsigned {
1057 *SU->getInstr(), *static_cast<const SIInstrInfo *>(DAG->TII));
1058 HardwareUnitInfo *HWUI = Heurs.getHWUIFromFlavor(Flavor);
1059
1060 // A BufferSize of 0 is unlimited, so it has no FIFO scheduling cost.
1061 if (HWUI->getBufferSize() == 0)
1062 return 0;
1063
1064 // getBufferAvailableCycle assumes top-down scheduling.
1065 assert(Zone.isTop());
1066 unsigned CurrCycle = Zone.getCurrCycle();
1067 unsigned BufferReadyCycle = HWUI->getBufferAvailableCycle(CurrCycle);
1068 if (BufferReadyCycle <= CurrCycle)
1069 return 0;
1070
1071 return BufferReadyCycle - CurrCycle;
1072 };
1073
1074 // Treat structural and latency stalls as a single scheduling cost for the
1075 // current cycle.
1076 struct StallCosts {
1077 unsigned Ready = 0;
1078 unsigned Structural = 0;
1079 unsigned Latency = 0;
1080 unsigned Effective = 0;
1081 unsigned Buffer = 0;
1082 };
1083
1084 unsigned CurrCycle = Zone.getCurrCycle();
1085 auto GetStallCosts = [&](SUnit *SU) {
1086 unsigned ReadyCycle = Zone.isTop() ? SU->TopReadyCycle : SU->BotReadyCycle;
1087 StallCosts Costs;
1088 Costs.Ready = ReadyCycle > CurrCycle ? ReadyCycle - CurrCycle : 0;
1089 Costs.Structural = getStructuralStallCycles(Zone, SU);
1090 Costs.Latency = Zone.getLatencyStallCycles(SU);
1091 Costs.Buffer = getBufferFullStalls(SU);
1092 Costs.Effective =
1093 std::max({Costs.Ready, Costs.Structural, Costs.Latency, Costs.Buffer});
1094 return Costs;
1095 };
1096
1097 StallCosts TryCosts = GetStallCosts(TryCand.SU);
1098 StallCosts CandCosts = GetStallCosts(Cand.SU);
1099
1100 LLVM_DEBUG(if (TryCosts.Effective || CandCosts.Effective) {
1101 dbgs() << "Effective stalls: try=" << TryCosts.Effective
1102 << " (ready=" << TryCosts.Ready << ", struct=" << TryCosts.Structural
1103 << ", lat=" << TryCosts.Latency << ", buffer=" << TryCosts.Buffer
1104 << ") cand=" << CandCosts.Effective << " (ready=" << CandCosts.Ready
1105 << ", struct=" << CandCosts.Structural
1106 << ", lat=" << CandCosts.Latency << ", buffer=" << CandCosts.Buffer
1107 << ")\n";
1108 });
1109
1110 return tryLess(TryCosts.Effective, CandCosts.Effective, TryCand, Cand, Stall);
1111}
1112
1115 LLVM_DEBUG(dbgs() << "AMDGPU coexec preRA scheduler selected for "
1116 << C->MF->getName() << '\n');
1118 C, std::make_unique<AMDGPUCoExecSchedStrategy>(C));
1120 return DAG;
1121}
1122
1125 LLVM_DEBUG(dbgs() << "AMDGPU nop postRA scheduler selected for "
1126 << C->MF->getName() << '\n');
1127 return new GCNNoopPostScheduleDAG(C);
1128}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static SUnit * pickOnlyChoice(SchedBoundary &Zone)
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.
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")
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)
bool tryEffectiveStall(SchedCandidate &Cand, SchedCandidate &TryCand, SchedBoundary &Zone)
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.
HardwareUnitInfo * getHWUIFromFlavor(AMDGPU::InstructionFlavor Flavor)
Given a Flavor , find the corresponding HardwareUnit.
void sortHWUIResources()
Sort the HardwarUnitInfo vector.
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.
SmallVector< HardwareUnitInfo, 8 > HWUInfo
const TargetSchedModel * SchedModel
void collectHWUIPressure()
Walk over the region and collect total usage per HardwareUnit.
void initialize(ScheduleDAGMI *DAG, const TargetSchedModel *SchedModel, const TargetRegisterInfo *TRI)
unsigned getHWUICyclesForInst(SUnit *SU)
Compute the blocking cycles for the appropriate HardwareUnit given an SU.
GCNDownwardRPTracker DownwardTracker
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)
unsigned getStructuralStallCycles(SchedBoundary &Zone, SUnit *SU) const
Estimate how many cycles SU must wait due to structural hazards at the current boundary cycle.
void initCandidate(SchedCandidate &Cand, SUnit *SU, bool AtTop, const RegPressureTracker &RPTracker, const SIRegisterInfo *SRI, unsigned SGPRPressure, unsigned VGPRPressure, bool IsBottomUp)
MachineSchedPolicy RegionPolicy
const TargetSchedModel * SchedModel
static const char * getReasonStr(GenericSchedulerBase::CandReason Reason)
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
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.
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)
static bool isVALU(const MachineInstr &MI, bool AllowLDSDMA)
static bool isTRANS(const MachineInstr &MI)
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 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.
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.
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
#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
constexpr StringRef getFlavorName(InstructionFlavor F)
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)
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.
LLVM_ABI unsigned getWeakLeft(const SUnit *SU, bool isTop)
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:1636
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
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.
LLVM_ABI bool tryGreater(int TryVal, int CandVal, GenericSchedulerBase::SchedCandidate &TryCand, GenericSchedulerBase::SchedCandidate &Cand, GenericSchedulerBase::CandReason Reason)
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
LLVM_ABI cl::opt< MISched::Direction > PreRADirection
Co-execution characteristics for a multi-cycle instruction.
static CoExecInfo build(unsigned TotalWindow, const char *Pattern)
Build a CoExecInfo from a pattern string.
unsigned TotalWindow
Number of cycles in the co-execution window, counting any trailing vacant stages.
StringRef Pattern
Pattern string for display (e.g., "0EIIEEIIV").
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:129
MachineSchedContext provides enough context from the MachineScheduler pass for the target to instanti...