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( 116 unsigned Reg, function_ref<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 std::set<unsigned> ClauseDefs; 161 std::set<unsigned> ClauseUses; 162 163 for (MachineInstr *MI : EmittedInstrs) { 164 165 // When we hit a non-SMEM instruction then we have passed the start of the 166 // clause and we can stop. 167 if (!MI || !SIInstrInfo::isSMRD(*MI)) 168 break; 169 170 addRegsToSet(MI->defs(), ClauseDefs); 171 addRegsToSet(MI->uses(), ClauseUses); 172 } 173 174 if (ClauseDefs.empty()) 175 return 0; 176 177 // FIXME: When we support stores, we need to make sure not to put loads and 178 // stores in the same clause if they use the same address. For now, just 179 // start a new clause whenever we see a store. 180 if (SMEM->mayStore()) 181 return 1; 182 183 addRegsToSet(SMEM->defs(), ClauseDefs); 184 addRegsToSet(SMEM->uses(), ClauseUses); 185 186 std::vector<unsigned> Result(std::max(ClauseDefs.size(), ClauseUses.size())); 187 std::vector<unsigned>::iterator End; 188 189 End = std::set_intersection(ClauseDefs.begin(), ClauseDefs.end(), 190 ClauseUses.begin(), ClauseUses.end(), Result.begin()); 191 192 // If the set of defs and uses intersect then we cannot add this instruction 193 // to the clause, so we have a hazard. 194 if (End != Result.begin()) 195 return 1; 196 197 return 0; 198 } 199 200 int GCNHazardRecognizer::checkSMRDHazards(MachineInstr *SMRD) { 201 const AMDGPUSubtarget &ST = MF.getSubtarget<AMDGPUSubtarget>(); 202 const SIInstrInfo *TII = static_cast<const SIInstrInfo*>(ST.getInstrInfo()); 203 int WaitStatesNeeded = 0; 204 205 WaitStatesNeeded = checkSMEMSoftClauseHazards(SMRD); 206 207 // This SMRD hazard only affects SI. 208 if (ST.getGeneration() != AMDGPUSubtarget::SOUTHERN_ISLANDS) 209 return WaitStatesNeeded; 210 211 // A read of an SGPR by SMRD instruction requires 4 wait states when the 212 // SGPR was written by a VALU instruction. 213 int SmrdSgprWaitStates = 4; 214 auto IsHazardDefFn = [TII] (MachineInstr *MI) { return TII->isVALU(*MI); }; 215 216 for (const MachineOperand &Use : SMRD->uses()) { 217 if (!Use.isReg()) 218 continue; 219 int WaitStatesNeededForUse = 220 SmrdSgprWaitStates - getWaitStatesSinceDef(Use.getReg(), IsHazardDefFn); 221 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); 222 } 223 return WaitStatesNeeded; 224 } 225 226 int GCNHazardRecognizer::checkVMEMHazards(MachineInstr* VMEM) { 227 const AMDGPUSubtarget &ST = MF.getSubtarget<AMDGPUSubtarget>(); 228 const SIInstrInfo *TII = static_cast<const SIInstrInfo*>(ST.getInstrInfo()); 229 230 if (ST.getGeneration() < AMDGPUSubtarget::VOLCANIC_ISLANDS) 231 return 0; 232 233 const SIRegisterInfo &TRI = TII->getRegisterInfo(); 234 235 // A read of an SGPR by a VMEM instruction requires 5 wait states when the 236 // SGPR was written by a VALU Instruction. 237 int VmemSgprWaitStates = 5; 238 int WaitStatesNeeded = 0; 239 auto IsHazardDefFn = [TII] (MachineInstr *MI) { return TII->isVALU(*MI); }; 240 241 for (const MachineOperand &Use : VMEM->uses()) { 242 if (!Use.isReg() || TRI.isVGPR(MF.getRegInfo(), Use.getReg())) 243 continue; 244 245 int WaitStatesNeededForUse = 246 VmemSgprWaitStates - getWaitStatesSinceDef(Use.getReg(), IsHazardDefFn); 247 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); 248 } 249 return WaitStatesNeeded; 250 } 251 252 int GCNHazardRecognizer::checkDPPHazards(MachineInstr *DPP) { 253 const AMDGPUSubtarget &ST = MF.getSubtarget<AMDGPUSubtarget>(); 254 const SIRegisterInfo *TRI = 255 static_cast<const SIRegisterInfo*>(ST.getRegisterInfo()); 256 257 // Check for DPP VGPR read after VALU VGPR write. 258 int DppVgprWaitStates = 2; 259 int WaitStatesNeeded = 0; 260 261 for (const MachineOperand &Use : DPP->uses()) { 262 if (!Use.isReg() || !TRI->isVGPR(MF.getRegInfo(), Use.getReg())) 263 continue; 264 int WaitStatesNeededForUse = 265 DppVgprWaitStates - getWaitStatesSinceDef(Use.getReg()); 266 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); 267 } 268 269 return WaitStatesNeeded; 270 } 271