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