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