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 ST(MF.getSubtarget<SISubtarget>()) { 30 MaxLookAhead = 5; 31 } 32 33 void GCNHazardRecognizer::EmitInstruction(SUnit *SU) { 34 EmitInstruction(SU->getInstr()); 35 } 36 37 void GCNHazardRecognizer::EmitInstruction(MachineInstr *MI) { 38 CurrCycleInstr = MI; 39 } 40 41 ScheduleHazardRecognizer::HazardType 42 GCNHazardRecognizer::getHazardType(SUnit *SU, int Stalls) { 43 MachineInstr *MI = SU->getInstr(); 44 45 if (SIInstrInfo::isSMRD(*MI) && checkSMRDHazards(MI) > 0) 46 return NoopHazard; 47 48 if (SIInstrInfo::isVMEM(*MI) && checkVMEMHazards(MI) > 0) 49 return NoopHazard; 50 51 if (SIInstrInfo::isDPP(*MI) && checkDPPHazards(MI) > 0) 52 return NoopHazard; 53 54 return NoHazard; 55 } 56 57 unsigned GCNHazardRecognizer::PreEmitNoops(SUnit *SU) { 58 return PreEmitNoops(SU->getInstr()); 59 } 60 61 unsigned GCNHazardRecognizer::PreEmitNoops(MachineInstr *MI) { 62 if (SIInstrInfo::isSMRD(*MI)) 63 return std::max(0, checkSMRDHazards(MI)); 64 65 if (SIInstrInfo::isVMEM(*MI)) 66 return std::max(0, checkVMEMHazards(MI)); 67 68 if (SIInstrInfo::isDPP(*MI)) 69 return std::max(0, checkDPPHazards(MI)); 70 71 return 0; 72 } 73 74 void GCNHazardRecognizer::EmitNoop() { 75 EmittedInstrs.push_front(nullptr); 76 } 77 78 void GCNHazardRecognizer::AdvanceCycle() { 79 80 // When the scheduler detects a stall, it will call AdvanceCycle() without 81 // emitting any instructions. 82 if (!CurrCycleInstr) 83 return; 84 85 const SIInstrInfo *TII = ST.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 SIRegisterInfo *TRI = ST.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 static void addRegsToSet(iterator_range<MachineInstr::const_mop_iterator> Ops, 135 std::set<unsigned> &Set) { 136 for (const MachineOperand &Op : Ops) { 137 if (Op.isReg()) 138 Set.insert(Op.getReg()); 139 } 140 } 141 142 int GCNHazardRecognizer::checkSMEMSoftClauseHazards(MachineInstr *SMEM) { 143 // SMEM soft clause are only present on VI+ 144 if (ST.getGeneration() < SISubtarget::VOLCANIC_ISLANDS) 145 return 0; 146 147 // A soft-clause is any group of consecutive SMEM instructions. The 148 // instructions in this group may return out of order and/or may be 149 // replayed (i.e. the same instruction issued more than once). 150 // 151 // In order to handle these situations correctly we need to make sure 152 // that when a clause has more than one instruction, no instruction in the 153 // clause writes to a register that is read another instruction in the clause 154 // (including itself). If we encounter this situaion, we need to break the 155 // clause by inserting a non SMEM instruction. 156 157 std::set<unsigned> ClauseDefs; 158 std::set<unsigned> ClauseUses; 159 160 for (MachineInstr *MI : EmittedInstrs) { 161 162 // When we hit a non-SMEM instruction then we have passed the start of the 163 // clause and we can stop. 164 if (!MI || !SIInstrInfo::isSMRD(*MI)) 165 break; 166 167 addRegsToSet(MI->defs(), ClauseDefs); 168 addRegsToSet(MI->uses(), ClauseUses); 169 } 170 171 if (ClauseDefs.empty()) 172 return 0; 173 174 // FIXME: When we support stores, we need to make sure not to put loads and 175 // stores in the same clause if they use the same address. For now, just 176 // start a new clause whenever we see a store. 177 if (SMEM->mayStore()) 178 return 1; 179 180 addRegsToSet(SMEM->defs(), ClauseDefs); 181 addRegsToSet(SMEM->uses(), ClauseUses); 182 183 std::vector<unsigned> Result(std::max(ClauseDefs.size(), ClauseUses.size())); 184 std::vector<unsigned>::iterator End; 185 186 End = std::set_intersection(ClauseDefs.begin(), ClauseDefs.end(), 187 ClauseUses.begin(), ClauseUses.end(), Result.begin()); 188 189 // If the set of defs and uses intersect then we cannot add this instruction 190 // to the clause, so we have a hazard. 191 if (End != Result.begin()) 192 return 1; 193 194 return 0; 195 } 196 197 int GCNHazardRecognizer::checkSMRDHazards(MachineInstr *SMRD) { 198 const SISubtarget &ST = MF.getSubtarget<SISubtarget>(); 199 const SIInstrInfo *TII = ST.getInstrInfo(); 200 int WaitStatesNeeded = 0; 201 202 WaitStatesNeeded = checkSMEMSoftClauseHazards(SMRD); 203 204 // This SMRD hazard only affects SI. 205 if (ST.getGeneration() != SISubtarget::SOUTHERN_ISLANDS) 206 return WaitStatesNeeded; 207 208 // A read of an SGPR by SMRD instruction requires 4 wait states when the 209 // SGPR was written by a VALU instruction. 210 int SmrdSgprWaitStates = 4; 211 auto IsHazardDefFn = [TII] (MachineInstr *MI) { return TII->isVALU(*MI); }; 212 213 for (const MachineOperand &Use : SMRD->uses()) { 214 if (!Use.isReg()) 215 continue; 216 int WaitStatesNeededForUse = 217 SmrdSgprWaitStates - getWaitStatesSinceDef(Use.getReg(), IsHazardDefFn); 218 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); 219 } 220 return WaitStatesNeeded; 221 } 222 223 int GCNHazardRecognizer::checkVMEMHazards(MachineInstr* VMEM) { 224 const SIInstrInfo *TII = ST.getInstrInfo(); 225 226 if (ST.getGeneration() < SISubtarget::VOLCANIC_ISLANDS) 227 return 0; 228 229 const SIRegisterInfo &TRI = TII->getRegisterInfo(); 230 231 // A read of an SGPR by a VMEM instruction requires 5 wait states when the 232 // SGPR was written by a VALU Instruction. 233 int VmemSgprWaitStates = 5; 234 int WaitStatesNeeded = 0; 235 auto IsHazardDefFn = [TII] (MachineInstr *MI) { return TII->isVALU(*MI); }; 236 237 for (const MachineOperand &Use : VMEM->uses()) { 238 if (!Use.isReg() || TRI.isVGPR(MF.getRegInfo(), Use.getReg())) 239 continue; 240 241 int WaitStatesNeededForUse = 242 VmemSgprWaitStates - getWaitStatesSinceDef(Use.getReg(), IsHazardDefFn); 243 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); 244 } 245 return WaitStatesNeeded; 246 } 247 248 int GCNHazardRecognizer::checkDPPHazards(MachineInstr *DPP) { 249 const SIRegisterInfo *TRI = ST.getRegisterInfo(); 250 251 // Check for DPP VGPR read after VALU VGPR write. 252 int DppVgprWaitStates = 2; 253 int WaitStatesNeeded = 0; 254 255 for (const MachineOperand &Use : DPP->uses()) { 256 if (!Use.isReg() || !TRI->isVGPR(MF.getRegInfo(), Use.getReg())) 257 continue; 258 int WaitStatesNeededForUse = 259 DppVgprWaitStates - getWaitStatesSinceDef(Use.getReg()); 260 WaitStatesNeeded = std::max(WaitStatesNeeded, WaitStatesNeededForUse); 261 } 262 263 return WaitStatesNeeded; 264 } 265