xref: /llvm-project/llvm/lib/Target/AMDGPU/GCNHazardRecognizer.cpp (revision cb6ba62d6fce87cc28a5076ccebe05b740d2340d)
1 //===-- 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 
20 using namespace llvm;
21 
22 //===----------------------------------------------------------------------===//
23 // Hazard Recoginizer Implementation
24 //===----------------------------------------------------------------------===//
25 
26 GCNHazardRecognizer::GCNHazardRecognizer(const MachineFunction &MF) :
27   CurrCycleInstr(nullptr),
28   MF(MF) {
29   MaxLookAhead = 5;
30 }
31 
32 void GCNHazardRecognizer::EmitInstruction(SUnit *SU) {
33   EmitInstruction(SU->getInstr());
34 }
35 
36 void GCNHazardRecognizer::EmitInstruction(MachineInstr *MI) {
37   CurrCycleInstr = MI;
38 }
39 
40 ScheduleHazardRecognizer::HazardType
41 GCNHazardRecognizer::getHazardType(SUnit *SU, int Stalls) {
42   const SIInstrInfo *TII =
43       static_cast<const SIInstrInfo*>(MF.getSubtarget().getInstrInfo());
44   MachineInstr *MI = SU->getInstr();
45 
46   if (TII->isSMRD(*MI) && checkSMRDHazards(MI) > 0)
47     return NoopHazard;
48 
49   if (TII->isVMEM(*MI) && checkVMEMHazards(MI) > 0)
50     return NoopHazard;
51 
52   return NoHazard;
53 }
54 
55 unsigned GCNHazardRecognizer::PreEmitNoops(SUnit *SU) {
56   return PreEmitNoops(SU->getInstr());
57 }
58 
59 unsigned GCNHazardRecognizer::PreEmitNoops(MachineInstr *MI) {
60   const SIInstrInfo *TII =
61       static_cast<const SIInstrInfo*>(MF.getSubtarget().getInstrInfo());
62 
63   if (TII->isSMRD(*MI))
64     return std::max(0, checkSMRDHazards(MI));
65 
66   if (TII->isVMEM(*MI))
67     return std::max(0, checkVMEMHazards(MI));
68 
69   return 0;
70 }
71 
72 void GCNHazardRecognizer::EmitNoop() {
73   EmittedInstrs.push_front(nullptr);
74 }
75 
76 void GCNHazardRecognizer::AdvanceCycle() {
77 
78   // When the scheduler detects a stall, it will call AdvanceCycle() without
79   // emitting any instructions.
80   if (!CurrCycleInstr)
81     return;
82 
83   const SIInstrInfo *TII =
84       static_cast<const SIInstrInfo*>(MF.getSubtarget().getInstrInfo());
85   unsigned NumWaitStates = TII->getNumWaitStates(*CurrCycleInstr);
86 
87   // Keep track of emitted instructions
88   EmittedInstrs.push_front(CurrCycleInstr);
89 
90   // Add a nullptr for each additional wait state after the first.  Make sure
91   // not to add more than getMaxLookAhead() items to the list, since we
92   // truncate the list to that size right after this loop.
93   for (unsigned i = 1, e = std::min(NumWaitStates, getMaxLookAhead());
94        i < e; ++i) {
95     EmittedInstrs.push_front(nullptr);
96   }
97 
98   // getMaxLookahead() is the largest number of wait states we will ever need
99   // to insert, so there is no point in keeping track of more than that many
100   // wait states.
101   EmittedInstrs.resize(getMaxLookAhead());
102 
103   CurrCycleInstr = nullptr;
104 }
105 
106 void GCNHazardRecognizer::RecedeCycle() {
107   llvm_unreachable("hazard recognizer does not support bottom-up scheduling.");
108 }
109 
110 //===----------------------------------------------------------------------===//
111 // Helper Functions
112 //===----------------------------------------------------------------------===//
113 
114 int GCNHazardRecognizer::getWaitStatesSinceDef(unsigned Reg,
115                               std::function<bool(MachineInstr*)> IsHazardDef ) {
116   const TargetRegisterInfo *TRI =
117       MF.getSubtarget<AMDGPUSubtarget>().getRegisterInfo();
118 
119   int WaitStates = -1;
120   for (MachineInstr *MI : EmittedInstrs) {
121     ++WaitStates;
122     if (!MI || !IsHazardDef(MI))
123       continue;
124     if (MI->modifiesRegister(Reg, TRI))
125       return WaitStates;
126   }
127   return std::numeric_limits<int>::max();
128 }
129 
130 //===----------------------------------------------------------------------===//
131 // No-op Hazard Detection
132 //===----------------------------------------------------------------------===//
133 
134 int GCNHazardRecognizer::checkSMRDHazards(MachineInstr *SMRD) {
135   const AMDGPUSubtarget &ST = MF.getSubtarget<AMDGPUSubtarget>();
136   const SIInstrInfo *TII = static_cast<const SIInstrInfo*>(ST.getInstrInfo());
137 
138   // This SMRD hazard only affects SI.
139   if (ST.getGeneration() != AMDGPUSubtarget::SOUTHERN_ISLANDS)
140     return 0;
141 
142   // A read of an SGPR by SMRD instruction requires 4 wait states when the
143   // SGPR was written by a VALU instruction.
144   int SmrdSgprWaitStates = 4;
145   int WaitStatesNeeded = 0;
146   auto IsHazardDefFn = [TII] (MachineInstr *MI) { return TII->isVALU(*MI); };
147 
148   for (const MachineOperand &Use : SMRD->uses()) {
149     if (!Use.isReg())
150       continue;
151     int WaitStatesNeededForUse =
152         SmrdSgprWaitStates - getWaitStatesSinceDef(Use.getReg(), IsHazardDefFn);
153     WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
154   }
155   return WaitStatesNeeded;
156 }
157 
158 int GCNHazardRecognizer::checkVMEMHazards(MachineInstr* VMEM) {
159   const AMDGPUSubtarget &ST = MF.getSubtarget<AMDGPUSubtarget>();
160   const SIInstrInfo *TII = static_cast<const SIInstrInfo*>(ST.getInstrInfo());
161 
162   if (ST.getGeneration() < AMDGPUSubtarget::VOLCANIC_ISLANDS)
163     return 0;
164 
165   const SIRegisterInfo &TRI = TII->getRegisterInfo();
166 
167   // A read of an SGPR by a VMEM instruction requires 5 wait states when the
168   // SGPR was written by a VALU Instruction.
169   int VmemSgprWaitStates = 5;
170   int WaitStatesNeeded = 0;
171   auto IsHazardDefFn = [TII] (MachineInstr *MI) { return TII->isVALU(*MI); };
172 
173   for (const MachineOperand &Use : VMEM->uses()) {
174     if (!Use.isReg() || TRI.isVGPR(MF.getRegInfo(), Use.getReg()))
175       continue;
176 
177     int WaitStatesNeededForUse =
178         VmemSgprWaitStates - getWaitStatesSinceDef(Use.getReg(), IsHazardDefFn);
179     WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse);
180   }
181   return WaitStatesNeeded;
182 }
183