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 if (SIInstrInfo::isDPP(*MI) && checkDPPHazards(MI) > 0) 51 return NoopHazard; 52 53 return NoHazard; 54 } 55 56 unsigned GCNHazardRecognizer::PreEmitNoops(SUnit *SU) { 57 return PreEmitNoops(SU->getInstr()); 58 } 59 60 unsigned GCNHazardRecognizer::PreEmitNoops(MachineInstr *MI) { 61 if (SIInstrInfo::isSMRD(*MI)) 62 return std::max(0, checkSMRDHazards(MI)); 63 64 if (SIInstrInfo::isVMEM(*MI)) 65 return std::max(0, checkVMEMHazards(MI)); 66 67 if (SIInstrInfo::isDPP(*MI)) 68 return std::max(0, checkDPPHazards(MI)); 69 70 return 0; 71 } 72 73 void GCNHazardRecognizer::EmitNoop() { 74 EmittedInstrs.push_front(nullptr); 75 } 76 77 void GCNHazardRecognizer::AdvanceCycle() { 78 79 // When the scheduler detects a stall, it will call AdvanceCycle() without 80 // emitting any instructions. 81 if (!CurrCycleInstr) 82 return; 83 84 const SIInstrInfo *TII = 85 static_cast<const SIInstrInfo*>(MF.getSubtarget().getInstrInfo()); 86 unsigned NumWaitStates = TII->getNumWaitStates(*CurrCycleInstr); 87 88 // Keep track of emitted instructions 89 EmittedInstrs.push_front(CurrCycleInstr); 90 91 // Add a nullptr for each additional wait state after the first. Make sure 92 // not to add more than getMaxLookAhead() items to the list, since we 93 // truncate the list to that size right after this loop. 94 for (unsigned i = 1, e = std::min(NumWaitStates, getMaxLookAhead()); 95 i < e; ++i) { 96 EmittedInstrs.push_front(nullptr); 97 } 98 99 // getMaxLookahead() is the largest number of wait states we will ever need 100 // to insert, so there is no point in keeping track of more than that many 101 // wait states. 102 EmittedInstrs.resize(getMaxLookAhead()); 103 104 CurrCycleInstr = nullptr; 105 } 106 107 void GCNHazardRecognizer::RecedeCycle() { 108 llvm_unreachable("hazard recognizer does not support bottom-up scheduling."); 109 } 110 111 //===----------------------------------------------------------------------===// 112 // Helper Functions 113 //===----------------------------------------------------------------------===// 114 115 int GCNHazardRecognizer::getWaitStatesSinceDef(unsigned Reg, 116 std::function<bool(MachineInstr*)> IsHazardDef ) { 117 const TargetRegisterInfo *TRI = 118 MF.getSubtarget<AMDGPUSubtarget>().getRegisterInfo(); 119 120 int WaitStates = -1; 121 for (MachineInstr *MI : EmittedInstrs) { 122 ++WaitStates; 123 if (!MI || !IsHazardDef(MI)) 124 continue; 125 if (MI->modifiesRegister(Reg, TRI)) 126 return WaitStates; 127 } 128 return std::numeric_limits<int>::max(); 129 } 130 131 //===----------------------------------------------------------------------===// 132 // No-op Hazard Detection 133 //===----------------------------------------------------------------------===// 134 135 static void addRegsToSet(iterator_range<MachineInstr::const_mop_iterator> Ops, 136 std::set<unsigned> &Set) { 137 for (const MachineOperand &Op : Ops) { 138 if (Op.isReg()) 139 Set.insert(Op.getReg()); 140 } 141 } 142 143 int GCNHazardRecognizer::checkSMEMSoftClauseHazards(MachineInstr *SMEM) { 144 const AMDGPUSubtarget &ST = MF.getSubtarget<AMDGPUSubtarget>(); 145 146 // SMEM soft clause are only present on VI+ 147 if (ST.getGeneration() < AMDGPUSubtarget::VOLCANIC_ISLANDS) 148 return 0; 149 150 // A soft-clause is any group of consecutive SMEM instructions. The 151 // instructions in this group may return out of order and/or may be 152 // replayed (i.e. the same instruction issued more than once). 153 // 154 // In order to handle these situations correctly we need to make sure 155 // that when a clause has more than one instruction, no instruction in the 156 // clause writes to a register that is read another instruction in the clause 157 // (including itself). If we encounter this situaion, we need to break the 158 // clause by inserting a non SMEM instruction. 159 160 const SIInstrInfo *TII = static_cast<const SIInstrInfo*>(ST.getInstrInfo()); 161 std::set<unsigned> ClauseDefs; 162 std::set<unsigned> ClauseUses; 163 164 for (MachineInstr *MI : EmittedInstrs) { 165 166 // When we hit a non-SMEM instruction then we have passed the start of the 167 // clause and we can stop. 168 if (!MI || !TII->isSMRD(*MI)) 169 break; 170 171 addRegsToSet(MI->defs(), ClauseDefs); 172 addRegsToSet(MI->uses(), ClauseUses); 173 } 174 175 if (ClauseDefs.empty()) 176 return 0; 177 178 // FIXME: When we support stores, we need to make sure not to put loads and 179 // stores in the same clause if they use the same address. For now, just 180 // start a new clause whenever we see a store. 181 if (SMEM->mayStore()) 182 return 1; 183 184 addRegsToSet(SMEM->defs(), ClauseDefs); 185 addRegsToSet(SMEM->uses(), ClauseUses); 186 187 std::vector<unsigned> Result(std::max(ClauseDefs.size(), ClauseUses.size())); 188 std::vector<unsigned>::iterator End; 189 190 End = std::set_intersection(ClauseDefs.begin(), ClauseDefs.end(), 191 ClauseUses.begin(), ClauseUses.end(), Result.begin()); 192 193 // If the set of defs and uses intersect then we cannot add this instruction 194 // to the clause, so we have a hazard. 195 if (End != Result.begin()) 196 return 1; 197 198 return 0; 199 } 200 201 int GCNHazardRecognizer::checkSMRDHazards(MachineInstr *SMRD) { 202 const AMDGPUSubtarget &ST = MF.getSubtarget<AMDGPUSubtarget>(); 203 const SIInstrInfo *TII = static_cast<const SIInstrInfo*>(ST.getInstrInfo()); 204 int WaitStatesNeeded = 0; 205 206 WaitStatesNeeded = checkSMEMSoftClauseHazards(SMRD); 207 208 // This SMRD hazard only affects SI. 209 if (ST.getGeneration() != AMDGPUSubtarget::SOUTHERN_ISLANDS) 210 return WaitStatesNeeded; 211 212 // A read of an SGPR by SMRD instruction requires 4 wait states when the 213 // SGPR was written by a VALU instruction. 214 int SmrdSgprWaitStates = 4; 215 auto IsHazardDefFn = [TII] (MachineInstr *MI) { return TII->isVALU(*MI); }; 216 217 for (const MachineOperand &Use : SMRD->uses()) { 218 if (!Use.isReg()) 219 continue; 220 int WaitStatesNeededForUse = 221 SmrdSgprWaitStates - getWaitStatesSinceDef(Use.getReg(), IsHazardDefFn); 222 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); 223 } 224 return WaitStatesNeeded; 225 } 226 227 int GCNHazardRecognizer::checkVMEMHazards(MachineInstr* VMEM) { 228 const AMDGPUSubtarget &ST = MF.getSubtarget<AMDGPUSubtarget>(); 229 const SIInstrInfo *TII = static_cast<const SIInstrInfo*>(ST.getInstrInfo()); 230 231 if (ST.getGeneration() < AMDGPUSubtarget::VOLCANIC_ISLANDS) 232 return 0; 233 234 const SIRegisterInfo &TRI = TII->getRegisterInfo(); 235 236 // A read of an SGPR by a VMEM instruction requires 5 wait states when the 237 // SGPR was written by a VALU Instruction. 238 int VmemSgprWaitStates = 5; 239 int WaitStatesNeeded = 0; 240 auto IsHazardDefFn = [TII] (MachineInstr *MI) { return TII->isVALU(*MI); }; 241 242 for (const MachineOperand &Use : VMEM->uses()) { 243 if (!Use.isReg() || TRI.isVGPR(MF.getRegInfo(), Use.getReg())) 244 continue; 245 246 int WaitStatesNeededForUse = 247 VmemSgprWaitStates - getWaitStatesSinceDef(Use.getReg(), IsHazardDefFn); 248 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); 249 } 250 return WaitStatesNeeded; 251 } 252 253 int GCNHazardRecognizer::checkDPPHazards(MachineInstr *DPP) { 254 const AMDGPUSubtarget &ST = MF.getSubtarget<AMDGPUSubtarget>(); 255 const SIRegisterInfo *TRI = 256 static_cast<const SIRegisterInfo*>(ST.getRegisterInfo()); 257 258 // Check for DPP VGPR read after VALU VGPR write. 259 int DppVgprWaitStates = 2; 260 int WaitStatesNeeded = 0; 261 262 for (const MachineOperand &Use : DPP->uses()) { 263 if (!Use.isReg() || !TRI->isVGPR(MF.getRegInfo(), Use.getReg())) 264 continue; 265 int WaitStatesNeededForUse = 266 DppVgprWaitStates - getWaitStatesSinceDef(Use.getReg()); 267 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); 268 } 269 270 return WaitStatesNeeded; 271 } 272