blob: 100ea7e9a2d1451723ac9f635ff70748f89159e2 [file] [log] [blame]
Tom Stellardcb6ba622016-04-30 00:23:06 +00001//===-- GCNHazardRecognizers.cpp - GCN Hazard Recognizer Impls ------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements hazard recognizers for scheduling on GCN processors.
11//
12//===----------------------------------------------------------------------===//
13
14#include "GCNHazardRecognizer.h"
15#include "AMDGPUSubtarget.h"
16#include "SIInstrInfo.h"
17#include "llvm/CodeGen/ScheduleDAG.h"
18#include "llvm/Support/Debug.h"
19
20using namespace llvm;
21
22//===----------------------------------------------------------------------===//
23// Hazard Recoginizer Implementation
24//===----------------------------------------------------------------------===//
25
26GCNHazardRecognizer::GCNHazardRecognizer(const MachineFunction &MF) :
27 CurrCycleInstr(nullptr),
Matt Arsenault43e92fe2016-06-24 06:30:11 +000028 MF(MF),
29 ST(MF.getSubtarget<SISubtarget>()) {
Tom Stellardcb6ba622016-04-30 00:23:06 +000030 MaxLookAhead = 5;
31}
32
33void GCNHazardRecognizer::EmitInstruction(SUnit *SU) {
34 EmitInstruction(SU->getInstr());
35}
36
37void GCNHazardRecognizer::EmitInstruction(MachineInstr *MI) {
38 CurrCycleInstr = MI;
39}
40
Tom Stellard5ab61542016-10-07 23:42:48 +000041static bool isDivFMas(unsigned Opcode) {
42 return Opcode == AMDGPU::V_DIV_FMAS_F32 || Opcode == AMDGPU::V_DIV_FMAS_F64;
43}
44
Tom Stellard961811c2016-10-15 00:58:14 +000045static bool isSGetReg(unsigned Opcode) {
46 return Opcode == AMDGPU::S_GETREG_B32;
47}
48
49static bool isSSetReg(unsigned Opcode) {
50 return Opcode == AMDGPU::S_SETREG_B32 || Opcode == AMDGPU::S_SETREG_IMM32_B32;
51}
52
53static unsigned getHWReg(const SIInstrInfo *TII, const MachineInstr &RegInstr) {
54
55 const MachineOperand *RegOp = TII->getNamedOperand(RegInstr,
56 AMDGPU::OpName::simm16);
57 return RegOp->getImm() & AMDGPU::Hwreg::ID_MASK_;
58}
59
Tom Stellardcb6ba622016-04-30 00:23:06 +000060ScheduleHazardRecognizer::HazardType
61GCNHazardRecognizer::getHazardType(SUnit *SU, int Stalls) {
Tom Stellardcb6ba622016-04-30 00:23:06 +000062 MachineInstr *MI = SU->getInstr();
63
Aaron Ballman5c190d02016-05-02 14:48:03 +000064 if (SIInstrInfo::isSMRD(*MI) && checkSMRDHazards(MI) > 0)
Tom Stellardcb6ba622016-04-30 00:23:06 +000065 return NoopHazard;
66
Aaron Ballman5c190d02016-05-02 14:48:03 +000067 if (SIInstrInfo::isVMEM(*MI) && checkVMEMHazards(MI) > 0)
Tom Stellardcb6ba622016-04-30 00:23:06 +000068 return NoopHazard;
69
Tom Stellarda27007e2016-05-02 16:23:09 +000070 if (SIInstrInfo::isDPP(*MI) && checkDPPHazards(MI) > 0)
71 return NoopHazard;
72
Tom Stellard5ab61542016-10-07 23:42:48 +000073 if (isDivFMas(MI->getOpcode()) && checkDivFMasHazards(MI) > 0)
74 return NoopHazard;
75
Tom Stellard961811c2016-10-15 00:58:14 +000076 if (isSGetReg(MI->getOpcode()) && checkGetRegHazards(MI) > 0)
77 return NoopHazard;
78
Tom Stellard30d30822016-10-27 20:39:09 +000079 if (isSSetReg(MI->getOpcode()) && checkSetRegHazards(MI) > 0)
80 return NoopHazard;
81
Tom Stellardcb6ba622016-04-30 00:23:06 +000082 return NoHazard;
83}
84
85unsigned GCNHazardRecognizer::PreEmitNoops(SUnit *SU) {
86 return PreEmitNoops(SU->getInstr());
87}
88
89unsigned GCNHazardRecognizer::PreEmitNoops(MachineInstr *MI) {
Aaron Ballman5c190d02016-05-02 14:48:03 +000090 if (SIInstrInfo::isSMRD(*MI))
Tom Stellardcb6ba622016-04-30 00:23:06 +000091 return std::max(0, checkSMRDHazards(MI));
92
Aaron Ballman5c190d02016-05-02 14:48:03 +000093 if (SIInstrInfo::isVMEM(*MI))
Tom Stellardcb6ba622016-04-30 00:23:06 +000094 return std::max(0, checkVMEMHazards(MI));
95
Tom Stellarda27007e2016-05-02 16:23:09 +000096 if (SIInstrInfo::isDPP(*MI))
97 return std::max(0, checkDPPHazards(MI));
98
Tom Stellard5ab61542016-10-07 23:42:48 +000099 if (isDivFMas(MI->getOpcode()))
100 return std::max(0, checkDivFMasHazards(MI));
101
Tom Stellard961811c2016-10-15 00:58:14 +0000102 if (isSGetReg(MI->getOpcode()))
103 return std::max(0, checkGetRegHazards(MI));
104
Tom Stellard30d30822016-10-27 20:39:09 +0000105 if (isSSetReg(MI->getOpcode()))
106 return std::max(0, checkSetRegHazards(MI));
107
Tom Stellardcb6ba622016-04-30 00:23:06 +0000108 return 0;
109}
110
111void GCNHazardRecognizer::EmitNoop() {
112 EmittedInstrs.push_front(nullptr);
113}
114
115void GCNHazardRecognizer::AdvanceCycle() {
116
117 // When the scheduler detects a stall, it will call AdvanceCycle() without
118 // emitting any instructions.
119 if (!CurrCycleInstr)
120 return;
121
Matt Arsenault43e92fe2016-06-24 06:30:11 +0000122 const SIInstrInfo *TII = ST.getInstrInfo();
Tom Stellardcb6ba622016-04-30 00:23:06 +0000123 unsigned NumWaitStates = TII->getNumWaitStates(*CurrCycleInstr);
124
125 // Keep track of emitted instructions
126 EmittedInstrs.push_front(CurrCycleInstr);
127
128 // Add a nullptr for each additional wait state after the first. Make sure
129 // not to add more than getMaxLookAhead() items to the list, since we
130 // truncate the list to that size right after this loop.
131 for (unsigned i = 1, e = std::min(NumWaitStates, getMaxLookAhead());
132 i < e; ++i) {
133 EmittedInstrs.push_front(nullptr);
134 }
135
136 // getMaxLookahead() is the largest number of wait states we will ever need
137 // to insert, so there is no point in keeping track of more than that many
138 // wait states.
139 EmittedInstrs.resize(getMaxLookAhead());
140
141 CurrCycleInstr = nullptr;
142}
143
144void GCNHazardRecognizer::RecedeCycle() {
145 llvm_unreachable("hazard recognizer does not support bottom-up scheduling.");
146}
147
148//===----------------------------------------------------------------------===//
149// Helper Functions
150//===----------------------------------------------------------------------===//
151
Benjamin Kramerd3f4c052016-06-12 16:13:55 +0000152int GCNHazardRecognizer::getWaitStatesSinceDef(
153 unsigned Reg, function_ref<bool(MachineInstr *)> IsHazardDef) {
Matt Arsenault43e92fe2016-06-24 06:30:11 +0000154 const SIRegisterInfo *TRI = ST.getRegisterInfo();
Tom Stellardcb6ba622016-04-30 00:23:06 +0000155
156 int WaitStates = -1;
157 for (MachineInstr *MI : EmittedInstrs) {
158 ++WaitStates;
159 if (!MI || !IsHazardDef(MI))
160 continue;
161 if (MI->modifiesRegister(Reg, TRI))
162 return WaitStates;
163 }
164 return std::numeric_limits<int>::max();
165}
166
Tom Stellard961811c2016-10-15 00:58:14 +0000167int GCNHazardRecognizer::getWaitStatesSinceSetReg(
168 function_ref<bool(MachineInstr *)> IsHazard) {
169
170 int WaitStates = -1;
171 for (MachineInstr *MI : EmittedInstrs) {
172 ++WaitStates;
173 if (!MI || !isSSetReg(MI->getOpcode()) || !IsHazard(MI))
174 continue;
175 return WaitStates;
176 }
177 return std::numeric_limits<int>::max();
178}
179
Tom Stellardcb6ba622016-04-30 00:23:06 +0000180//===----------------------------------------------------------------------===//
181// No-op Hazard Detection
182//===----------------------------------------------------------------------===//
183
Tom Stellard1f520e52016-05-02 17:39:06 +0000184static void addRegsToSet(iterator_range<MachineInstr::const_mop_iterator> Ops,
185 std::set<unsigned> &Set) {
186 for (const MachineOperand &Op : Ops) {
187 if (Op.isReg())
188 Set.insert(Op.getReg());
189 }
190}
191
192int GCNHazardRecognizer::checkSMEMSoftClauseHazards(MachineInstr *SMEM) {
Tom Stellard1f520e52016-05-02 17:39:06 +0000193 // SMEM soft clause are only present on VI+
Matt Arsenault43e92fe2016-06-24 06:30:11 +0000194 if (ST.getGeneration() < SISubtarget::VOLCANIC_ISLANDS)
Tom Stellard1f520e52016-05-02 17:39:06 +0000195 return 0;
196
197 // A soft-clause is any group of consecutive SMEM instructions. The
198 // instructions in this group may return out of order and/or may be
199 // replayed (i.e. the same instruction issued more than once).
200 //
201 // In order to handle these situations correctly we need to make sure
202 // that when a clause has more than one instruction, no instruction in the
203 // clause writes to a register that is read another instruction in the clause
204 // (including itself). If we encounter this situaion, we need to break the
205 // clause by inserting a non SMEM instruction.
206
Tom Stellard1f520e52016-05-02 17:39:06 +0000207 std::set<unsigned> ClauseDefs;
208 std::set<unsigned> ClauseUses;
209
210 for (MachineInstr *MI : EmittedInstrs) {
211
212 // When we hit a non-SMEM instruction then we have passed the start of the
213 // clause and we can stop.
Aaron Ballman3bd56b32016-05-03 15:17:25 +0000214 if (!MI || !SIInstrInfo::isSMRD(*MI))
Tom Stellard1f520e52016-05-02 17:39:06 +0000215 break;
216
217 addRegsToSet(MI->defs(), ClauseDefs);
218 addRegsToSet(MI->uses(), ClauseUses);
219 }
220
221 if (ClauseDefs.empty())
222 return 0;
223
224 // FIXME: When we support stores, we need to make sure not to put loads and
225 // stores in the same clause if they use the same address. For now, just
226 // start a new clause whenever we see a store.
227 if (SMEM->mayStore())
228 return 1;
229
230 addRegsToSet(SMEM->defs(), ClauseDefs);
231 addRegsToSet(SMEM->uses(), ClauseUses);
232
233 std::vector<unsigned> Result(std::max(ClauseDefs.size(), ClauseUses.size()));
234 std::vector<unsigned>::iterator End;
235
236 End = std::set_intersection(ClauseDefs.begin(), ClauseDefs.end(),
237 ClauseUses.begin(), ClauseUses.end(), Result.begin());
238
239 // If the set of defs and uses intersect then we cannot add this instruction
240 // to the clause, so we have a hazard.
241 if (End != Result.begin())
242 return 1;
243
244 return 0;
245}
246
Tom Stellardcb6ba622016-04-30 00:23:06 +0000247int GCNHazardRecognizer::checkSMRDHazards(MachineInstr *SMRD) {
Matt Arsenault43e92fe2016-06-24 06:30:11 +0000248 const SISubtarget &ST = MF.getSubtarget<SISubtarget>();
249 const SIInstrInfo *TII = ST.getInstrInfo();
Tom Stellard1f520e52016-05-02 17:39:06 +0000250 int WaitStatesNeeded = 0;
251
252 WaitStatesNeeded = checkSMEMSoftClauseHazards(SMRD);
Tom Stellardcb6ba622016-04-30 00:23:06 +0000253
254 // This SMRD hazard only affects SI.
Matt Arsenault43e92fe2016-06-24 06:30:11 +0000255 if (ST.getGeneration() != SISubtarget::SOUTHERN_ISLANDS)
Tom Stellard1f520e52016-05-02 17:39:06 +0000256 return WaitStatesNeeded;
Tom Stellardcb6ba622016-04-30 00:23:06 +0000257
258 // A read of an SGPR by SMRD instruction requires 4 wait states when the
259 // SGPR was written by a VALU instruction.
260 int SmrdSgprWaitStates = 4;
Tom Stellardcb6ba622016-04-30 00:23:06 +0000261 auto IsHazardDefFn = [TII] (MachineInstr *MI) { return TII->isVALU(*MI); };
262
263 for (const MachineOperand &Use : SMRD->uses()) {
264 if (!Use.isReg())
265 continue;
266 int WaitStatesNeededForUse =
267 SmrdSgprWaitStates - getWaitStatesSinceDef(Use.getReg(), IsHazardDefFn);
268 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
269 }
270 return WaitStatesNeeded;
271}
272
273int GCNHazardRecognizer::checkVMEMHazards(MachineInstr* VMEM) {
Matt Arsenault43e92fe2016-06-24 06:30:11 +0000274 const SIInstrInfo *TII = ST.getInstrInfo();
Tom Stellardcb6ba622016-04-30 00:23:06 +0000275
Matt Arsenault43e92fe2016-06-24 06:30:11 +0000276 if (ST.getGeneration() < SISubtarget::VOLCANIC_ISLANDS)
Tom Stellardcb6ba622016-04-30 00:23:06 +0000277 return 0;
278
279 const SIRegisterInfo &TRI = TII->getRegisterInfo();
280
281 // A read of an SGPR by a VMEM instruction requires 5 wait states when the
282 // SGPR was written by a VALU Instruction.
283 int VmemSgprWaitStates = 5;
284 int WaitStatesNeeded = 0;
285 auto IsHazardDefFn = [TII] (MachineInstr *MI) { return TII->isVALU(*MI); };
286
287 for (const MachineOperand &Use : VMEM->uses()) {
288 if (!Use.isReg() || TRI.isVGPR(MF.getRegInfo(), Use.getReg()))
289 continue;
290
291 int WaitStatesNeededForUse =
292 VmemSgprWaitStates - getWaitStatesSinceDef(Use.getReg(), IsHazardDefFn);
293 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
294 }
295 return WaitStatesNeeded;
296}
Tom Stellarda27007e2016-05-02 16:23:09 +0000297
298int GCNHazardRecognizer::checkDPPHazards(MachineInstr *DPP) {
Matt Arsenault43e92fe2016-06-24 06:30:11 +0000299 const SIRegisterInfo *TRI = ST.getRegisterInfo();
Tom Stellarda27007e2016-05-02 16:23:09 +0000300
301 // Check for DPP VGPR read after VALU VGPR write.
302 int DppVgprWaitStates = 2;
303 int WaitStatesNeeded = 0;
304
305 for (const MachineOperand &Use : DPP->uses()) {
306 if (!Use.isReg() || !TRI->isVGPR(MF.getRegInfo(), Use.getReg()))
307 continue;
308 int WaitStatesNeededForUse =
309 DppVgprWaitStates - getWaitStatesSinceDef(Use.getReg());
310 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
311 }
312
313 return WaitStatesNeeded;
314}
Tom Stellard5ab61542016-10-07 23:42:48 +0000315
316int GCNHazardRecognizer::checkDivFMasHazards(MachineInstr *DivFMas) {
317 const SIInstrInfo *TII = ST.getInstrInfo();
318
319 // v_div_fmas requires 4 wait states after a write to vcc from a VALU
320 // instruction.
321 const int DivFMasWaitStates = 4;
322 auto IsHazardDefFn = [TII] (MachineInstr *MI) { return TII->isVALU(*MI); };
323 int WaitStatesNeeded = getWaitStatesSinceDef(AMDGPU::VCC, IsHazardDefFn);
324
325 return DivFMasWaitStates - WaitStatesNeeded;
326}
Tom Stellard961811c2016-10-15 00:58:14 +0000327
328int GCNHazardRecognizer::checkGetRegHazards(MachineInstr *GetRegInstr) {
329 const SIInstrInfo *TII = ST.getInstrInfo();
330 unsigned GetRegHWReg = getHWReg(TII, *GetRegInstr);
331
332 const int GetRegWaitStates = 2;
333 auto IsHazardFn = [TII, GetRegHWReg] (MachineInstr *MI) {
334 return GetRegHWReg == getHWReg(TII, *MI);
335 };
336 int WaitStatesNeeded = getWaitStatesSinceSetReg(IsHazardFn);
337
338 return GetRegWaitStates - WaitStatesNeeded;
339}
Tom Stellard30d30822016-10-27 20:39:09 +0000340
341int GCNHazardRecognizer::checkSetRegHazards(MachineInstr *SetRegInstr) {
342 const SIInstrInfo *TII = ST.getInstrInfo();
343 unsigned HWReg = getHWReg(TII, *SetRegInstr);
344
345 const int SetRegWaitStates =
346 ST.getGeneration() <= AMDGPUSubtarget::SEA_ISLANDS ? 1 : 2;
347 auto IsHazardFn = [TII, HWReg] (MachineInstr *MI) {
348 return HWReg == getHWReg(TII, *MI);
349 };
350 int WaitStatesNeeded = getWaitStatesSinceSetReg(IsHazardFn);
351 return SetRegWaitStates - WaitStatesNeeded;
352}