1d88c1a5aSDimitry Andric //===-- GCNSchedStrategy.cpp - GCN Scheduler Strategy ---------------------===// 2d88c1a5aSDimitry Andric // 3d88c1a5aSDimitry Andric // The LLVM Compiler Infrastructure 4d88c1a5aSDimitry Andric // 5d88c1a5aSDimitry Andric // This file is distributed under the University of Illinois Open Source 6d88c1a5aSDimitry Andric // License. See LICENSE.TXT for details. 7d88c1a5aSDimitry Andric // 8d88c1a5aSDimitry Andric //===----------------------------------------------------------------------===// 9d88c1a5aSDimitry Andric // 10d88c1a5aSDimitry Andric /// \file 11d88c1a5aSDimitry Andric /// This contains a MachineSchedStrategy implementation for maximizing wave 12d88c1a5aSDimitry Andric /// occupancy on GCN hardware. 13d88c1a5aSDimitry Andric //===----------------------------------------------------------------------===// 14d88c1a5aSDimitry Andric 15d88c1a5aSDimitry Andric #include "GCNSchedStrategy.h" 16d88c1a5aSDimitry Andric #include "AMDGPUSubtarget.h" 17d88c1a5aSDimitry Andric #include "SIInstrInfo.h" 18d88c1a5aSDimitry Andric #include "SIMachineFunctionInfo.h" 19d88c1a5aSDimitry Andric #include "SIRegisterInfo.h" 20d88c1a5aSDimitry Andric #include "llvm/CodeGen/RegisterClassInfo.h" 217a7e6055SDimitry Andric #include "llvm/Support/MathExtras.h" 22d88c1a5aSDimitry Andric 23c4394386SDimitry Andric #define DEBUG_TYPE "machine-scheduler" 24d88c1a5aSDimitry Andric 25d88c1a5aSDimitry Andric using namespace llvm; 26d88c1a5aSDimitry Andric 27d88c1a5aSDimitry Andric GCNMaxOccupancySchedStrategy::GCNMaxOccupancySchedStrategy( 28d88c1a5aSDimitry Andric const MachineSchedContext *C) : 297a7e6055SDimitry Andric GenericScheduler(C), TargetOccupancy(0), MF(nullptr) { } 30d88c1a5aSDimitry Andric 31d88c1a5aSDimitry Andric static unsigned getMaxWaves(unsigned SGPRs, unsigned VGPRs, 32d88c1a5aSDimitry Andric const MachineFunction &MF) { 33d88c1a5aSDimitry Andric 34d88c1a5aSDimitry Andric const SISubtarget &ST = MF.getSubtarget<SISubtarget>(); 35d88c1a5aSDimitry Andric const SIMachineFunctionInfo *MFI = MF.getInfo<SIMachineFunctionInfo>(); 36d88c1a5aSDimitry Andric unsigned MinRegOccupancy = std::min(ST.getOccupancyWithNumSGPRs(SGPRs), 37d88c1a5aSDimitry Andric ST.getOccupancyWithNumVGPRs(VGPRs)); 38d88c1a5aSDimitry Andric return std::min(MinRegOccupancy, 397a7e6055SDimitry Andric ST.getOccupancyWithLocalMemSize(MFI->getLDSSize(), 402cab237bSDimitry Andric MF.getFunction())); 417a7e6055SDimitry Andric } 427a7e6055SDimitry Andric 437a7e6055SDimitry Andric void GCNMaxOccupancySchedStrategy::initialize(ScheduleDAGMI *DAG) { 447a7e6055SDimitry Andric GenericScheduler::initialize(DAG); 457a7e6055SDimitry Andric 467a7e6055SDimitry Andric const SIRegisterInfo *SRI = static_cast<const SIRegisterInfo*>(TRI); 477a7e6055SDimitry Andric 487a7e6055SDimitry Andric MF = &DAG->MF; 497a7e6055SDimitry Andric 507a7e6055SDimitry Andric const SISubtarget &ST = MF->getSubtarget<SISubtarget>(); 517a7e6055SDimitry Andric 527a7e6055SDimitry Andric // FIXME: This is also necessary, because some passes that run after 537a7e6055SDimitry Andric // scheduling and before regalloc increase register pressure. 547a7e6055SDimitry Andric const int ErrorMargin = 3; 557a7e6055SDimitry Andric 567a7e6055SDimitry Andric SGPRExcessLimit = Context->RegClassInfo 577a7e6055SDimitry Andric ->getNumAllocatableRegs(&AMDGPU::SGPR_32RegClass) - ErrorMargin; 587a7e6055SDimitry Andric VGPRExcessLimit = Context->RegClassInfo 597a7e6055SDimitry Andric ->getNumAllocatableRegs(&AMDGPU::VGPR_32RegClass) - ErrorMargin; 607a7e6055SDimitry Andric if (TargetOccupancy) { 617a7e6055SDimitry Andric SGPRCriticalLimit = ST.getMaxNumSGPRs(TargetOccupancy, true); 627a7e6055SDimitry Andric VGPRCriticalLimit = ST.getMaxNumVGPRs(TargetOccupancy); 637a7e6055SDimitry Andric } else { 647a7e6055SDimitry Andric SGPRCriticalLimit = SRI->getRegPressureSetLimit(DAG->MF, 657a7e6055SDimitry Andric SRI->getSGPRPressureSet()); 667a7e6055SDimitry Andric VGPRCriticalLimit = SRI->getRegPressureSetLimit(DAG->MF, 677a7e6055SDimitry Andric SRI->getVGPRPressureSet()); 687a7e6055SDimitry Andric } 697a7e6055SDimitry Andric 707a7e6055SDimitry Andric SGPRCriticalLimit -= ErrorMargin; 717a7e6055SDimitry Andric VGPRCriticalLimit -= ErrorMargin; 72d88c1a5aSDimitry Andric } 73d88c1a5aSDimitry Andric 74d88c1a5aSDimitry Andric void GCNMaxOccupancySchedStrategy::initCandidate(SchedCandidate &Cand, SUnit *SU, 75d88c1a5aSDimitry Andric bool AtTop, const RegPressureTracker &RPTracker, 76d88c1a5aSDimitry Andric const SIRegisterInfo *SRI, 777a7e6055SDimitry Andric unsigned SGPRPressure, 787a7e6055SDimitry Andric unsigned VGPRPressure) { 79d88c1a5aSDimitry Andric 80d88c1a5aSDimitry Andric Cand.SU = SU; 81d88c1a5aSDimitry Andric Cand.AtTop = AtTop; 82d88c1a5aSDimitry Andric 83d88c1a5aSDimitry Andric // getDownwardPressure() and getUpwardPressure() make temporary changes to 84d88c1a5aSDimitry Andric // the the tracker, so we need to pass those function a non-const copy. 85d88c1a5aSDimitry Andric RegPressureTracker &TempTracker = const_cast<RegPressureTracker&>(RPTracker); 86d88c1a5aSDimitry Andric 87d88c1a5aSDimitry Andric std::vector<unsigned> Pressure; 88d88c1a5aSDimitry Andric std::vector<unsigned> MaxPressure; 89d88c1a5aSDimitry Andric 90d88c1a5aSDimitry Andric if (AtTop) 91d88c1a5aSDimitry Andric TempTracker.getDownwardPressure(SU->getInstr(), Pressure, MaxPressure); 92d88c1a5aSDimitry Andric else { 93d88c1a5aSDimitry Andric // FIXME: I think for bottom up scheduling, the register pressure is cached 94d88c1a5aSDimitry Andric // and can be retrieved by DAG->getPressureDif(SU). 95d88c1a5aSDimitry Andric TempTracker.getUpwardPressure(SU->getInstr(), Pressure, MaxPressure); 96d88c1a5aSDimitry Andric } 97d88c1a5aSDimitry Andric 987a7e6055SDimitry Andric unsigned NewSGPRPressure = Pressure[SRI->getSGPRPressureSet()]; 997a7e6055SDimitry Andric unsigned NewVGPRPressure = Pressure[SRI->getVGPRPressureSet()]; 100d88c1a5aSDimitry Andric 101d88c1a5aSDimitry Andric // If two instructions increase the pressure of different register sets 102d88c1a5aSDimitry Andric // by the same amount, the generic scheduler will prefer to schedule the 103d88c1a5aSDimitry Andric // instruction that increases the set with the least amount of registers, 104d88c1a5aSDimitry Andric // which in our case would be SGPRs. This is rarely what we want, so 105d88c1a5aSDimitry Andric // when we report excess/critical register pressure, we do it either 106d88c1a5aSDimitry Andric // only for VGPRs or only for SGPRs. 107d88c1a5aSDimitry Andric 108d88c1a5aSDimitry Andric // FIXME: Better heuristics to determine whether to prefer SGPRs or VGPRs. 1097a7e6055SDimitry Andric const unsigned MaxVGPRPressureInc = 16; 110d88c1a5aSDimitry Andric bool ShouldTrackVGPRs = VGPRPressure + MaxVGPRPressureInc >= VGPRExcessLimit; 111d88c1a5aSDimitry Andric bool ShouldTrackSGPRs = !ShouldTrackVGPRs && SGPRPressure >= SGPRExcessLimit; 112d88c1a5aSDimitry Andric 113d88c1a5aSDimitry Andric 114d88c1a5aSDimitry Andric // FIXME: We have to enter REG-EXCESS before we reach the actual threshold 115d88c1a5aSDimitry Andric // to increase the likelihood we don't go over the limits. We should improve 116d88c1a5aSDimitry Andric // the analysis to look through dependencies to find the path with the least 117d88c1a5aSDimitry Andric // register pressure. 118d88c1a5aSDimitry Andric 119d88c1a5aSDimitry Andric // We only need to update the RPDelata for instructions that increase 120d88c1a5aSDimitry Andric // register pressure. Instructions that decrease or keep reg pressure 121d88c1a5aSDimitry Andric // the same will be marked as RegExcess in tryCandidate() when they 122d88c1a5aSDimitry Andric // are compared with instructions that increase the register pressure. 123d88c1a5aSDimitry Andric if (ShouldTrackVGPRs && NewVGPRPressure >= VGPRExcessLimit) { 124d88c1a5aSDimitry Andric Cand.RPDelta.Excess = PressureChange(SRI->getVGPRPressureSet()); 125d88c1a5aSDimitry Andric Cand.RPDelta.Excess.setUnitInc(NewVGPRPressure - VGPRExcessLimit); 126d88c1a5aSDimitry Andric } 127d88c1a5aSDimitry Andric 128d88c1a5aSDimitry Andric if (ShouldTrackSGPRs && NewSGPRPressure >= SGPRExcessLimit) { 129d88c1a5aSDimitry Andric Cand.RPDelta.Excess = PressureChange(SRI->getSGPRPressureSet()); 1307a7e6055SDimitry Andric Cand.RPDelta.Excess.setUnitInc(NewSGPRPressure - SGPRExcessLimit); 131d88c1a5aSDimitry Andric } 132d88c1a5aSDimitry Andric 133d88c1a5aSDimitry Andric // Register pressure is considered 'CRITICAL' if it is approaching a value 134d88c1a5aSDimitry Andric // that would reduce the wave occupancy for the execution unit. When 135d88c1a5aSDimitry Andric // register pressure is 'CRITICAL', increading SGPR and VGPR pressure both 136d88c1a5aSDimitry Andric // has the same cost, so we don't need to prefer one over the other. 137d88c1a5aSDimitry Andric 138d88c1a5aSDimitry Andric int SGPRDelta = NewSGPRPressure - SGPRCriticalLimit; 139d88c1a5aSDimitry Andric int VGPRDelta = NewVGPRPressure - VGPRCriticalLimit; 140d88c1a5aSDimitry Andric 141d88c1a5aSDimitry Andric if (SGPRDelta >= 0 || VGPRDelta >= 0) { 142d88c1a5aSDimitry Andric if (SGPRDelta > VGPRDelta) { 143d88c1a5aSDimitry Andric Cand.RPDelta.CriticalMax = PressureChange(SRI->getSGPRPressureSet()); 144d88c1a5aSDimitry Andric Cand.RPDelta.CriticalMax.setUnitInc(SGPRDelta); 145d88c1a5aSDimitry Andric } else { 146d88c1a5aSDimitry Andric Cand.RPDelta.CriticalMax = PressureChange(SRI->getVGPRPressureSet()); 147d88c1a5aSDimitry Andric Cand.RPDelta.CriticalMax.setUnitInc(VGPRDelta); 148d88c1a5aSDimitry Andric } 149d88c1a5aSDimitry Andric } 150d88c1a5aSDimitry Andric } 151d88c1a5aSDimitry Andric 152d88c1a5aSDimitry Andric // This function is mostly cut and pasted from 153d88c1a5aSDimitry Andric // GenericScheduler::pickNodeFromQueue() 154d88c1a5aSDimitry Andric void GCNMaxOccupancySchedStrategy::pickNodeFromQueue(SchedBoundary &Zone, 155d88c1a5aSDimitry Andric const CandPolicy &ZonePolicy, 156d88c1a5aSDimitry Andric const RegPressureTracker &RPTracker, 157d88c1a5aSDimitry Andric SchedCandidate &Cand) { 158d88c1a5aSDimitry Andric const SIRegisterInfo *SRI = static_cast<const SIRegisterInfo*>(TRI); 159d88c1a5aSDimitry Andric ArrayRef<unsigned> Pressure = RPTracker.getRegSetPressureAtPos(); 160d88c1a5aSDimitry Andric unsigned SGPRPressure = Pressure[SRI->getSGPRPressureSet()]; 161d88c1a5aSDimitry Andric unsigned VGPRPressure = Pressure[SRI->getVGPRPressureSet()]; 162d88c1a5aSDimitry Andric ReadyQueue &Q = Zone.Available; 163d88c1a5aSDimitry Andric for (SUnit *SU : Q) { 164d88c1a5aSDimitry Andric 165d88c1a5aSDimitry Andric SchedCandidate TryCand(ZonePolicy); 166d88c1a5aSDimitry Andric initCandidate(TryCand, SU, Zone.isTop(), RPTracker, SRI, 1677a7e6055SDimitry Andric SGPRPressure, VGPRPressure); 168d88c1a5aSDimitry Andric // Pass SchedBoundary only when comparing nodes from the same boundary. 169d88c1a5aSDimitry Andric SchedBoundary *ZoneArg = Cand.AtTop == TryCand.AtTop ? &Zone : nullptr; 170d88c1a5aSDimitry Andric GenericScheduler::tryCandidate(Cand, TryCand, ZoneArg); 171d88c1a5aSDimitry Andric if (TryCand.Reason != NoCand) { 172d88c1a5aSDimitry Andric // Initialize resource delta if needed in case future heuristics query it. 173d88c1a5aSDimitry Andric if (TryCand.ResDelta == SchedResourceDelta()) 174d88c1a5aSDimitry Andric TryCand.initResourceDelta(Zone.DAG, SchedModel); 175d88c1a5aSDimitry Andric Cand.setBest(TryCand); 176d88c1a5aSDimitry Andric } 177d88c1a5aSDimitry Andric } 178d88c1a5aSDimitry Andric } 179d88c1a5aSDimitry Andric 180d88c1a5aSDimitry Andric // This function is mostly cut and pasted from 181d88c1a5aSDimitry Andric // GenericScheduler::pickNodeBidirectional() 182d88c1a5aSDimitry Andric SUnit *GCNMaxOccupancySchedStrategy::pickNodeBidirectional(bool &IsTopNode) { 183d88c1a5aSDimitry Andric // Schedule as far as possible in the direction of no choice. This is most 184d88c1a5aSDimitry Andric // efficient, but also provides the best heuristics for CriticalPSets. 185d88c1a5aSDimitry Andric if (SUnit *SU = Bot.pickOnlyChoice()) { 186d88c1a5aSDimitry Andric IsTopNode = false; 187d88c1a5aSDimitry Andric return SU; 188d88c1a5aSDimitry Andric } 189d88c1a5aSDimitry Andric if (SUnit *SU = Top.pickOnlyChoice()) { 190d88c1a5aSDimitry Andric IsTopNode = true; 191d88c1a5aSDimitry Andric return SU; 192d88c1a5aSDimitry Andric } 193d88c1a5aSDimitry Andric // Set the bottom-up policy based on the state of the current bottom zone and 194d88c1a5aSDimitry Andric // the instructions outside the zone, including the top zone. 195d88c1a5aSDimitry Andric CandPolicy BotPolicy; 196d88c1a5aSDimitry Andric setPolicy(BotPolicy, /*IsPostRA=*/false, Bot, &Top); 197d88c1a5aSDimitry Andric // Set the top-down policy based on the state of the current top zone and 198d88c1a5aSDimitry Andric // the instructions outside the zone, including the bottom zone. 199d88c1a5aSDimitry Andric CandPolicy TopPolicy; 200d88c1a5aSDimitry Andric setPolicy(TopPolicy, /*IsPostRA=*/false, Top, &Bot); 201d88c1a5aSDimitry Andric 202d88c1a5aSDimitry Andric // See if BotCand is still valid (because we previously scheduled from Top). 203d88c1a5aSDimitry Andric DEBUG(dbgs() << "Picking from Bot:\n"); 204d88c1a5aSDimitry Andric if (!BotCand.isValid() || BotCand.SU->isScheduled || 205d88c1a5aSDimitry Andric BotCand.Policy != BotPolicy) { 206d88c1a5aSDimitry Andric BotCand.reset(CandPolicy()); 207d88c1a5aSDimitry Andric pickNodeFromQueue(Bot, BotPolicy, DAG->getBotRPTracker(), BotCand); 208d88c1a5aSDimitry Andric assert(BotCand.Reason != NoCand && "failed to find the first candidate"); 209d88c1a5aSDimitry Andric } else { 210d88c1a5aSDimitry Andric DEBUG(traceCandidate(BotCand)); 211d88c1a5aSDimitry Andric } 212d88c1a5aSDimitry Andric 213d88c1a5aSDimitry Andric // Check if the top Q has a better candidate. 214d88c1a5aSDimitry Andric DEBUG(dbgs() << "Picking from Top:\n"); 215d88c1a5aSDimitry Andric if (!TopCand.isValid() || TopCand.SU->isScheduled || 216d88c1a5aSDimitry Andric TopCand.Policy != TopPolicy) { 217d88c1a5aSDimitry Andric TopCand.reset(CandPolicy()); 218d88c1a5aSDimitry Andric pickNodeFromQueue(Top, TopPolicy, DAG->getTopRPTracker(), TopCand); 219d88c1a5aSDimitry Andric assert(TopCand.Reason != NoCand && "failed to find the first candidate"); 220d88c1a5aSDimitry Andric } else { 221d88c1a5aSDimitry Andric DEBUG(traceCandidate(TopCand)); 222d88c1a5aSDimitry Andric } 223d88c1a5aSDimitry Andric 224d88c1a5aSDimitry Andric // Pick best from BotCand and TopCand. 225d88c1a5aSDimitry Andric DEBUG( 226d88c1a5aSDimitry Andric dbgs() << "Top Cand: "; 227d88c1a5aSDimitry Andric traceCandidate(TopCand); 2287a7e6055SDimitry Andric dbgs() << "Bot Cand: "; 2297a7e6055SDimitry Andric traceCandidate(BotCand); 230d88c1a5aSDimitry Andric ); 231d88c1a5aSDimitry Andric SchedCandidate Cand; 232d88c1a5aSDimitry Andric if (TopCand.Reason == BotCand.Reason) { 233d88c1a5aSDimitry Andric Cand = BotCand; 234d88c1a5aSDimitry Andric GenericSchedulerBase::CandReason TopReason = TopCand.Reason; 235d88c1a5aSDimitry Andric TopCand.Reason = NoCand; 236d88c1a5aSDimitry Andric GenericScheduler::tryCandidate(Cand, TopCand, nullptr); 237d88c1a5aSDimitry Andric if (TopCand.Reason != NoCand) { 238d88c1a5aSDimitry Andric Cand.setBest(TopCand); 239d88c1a5aSDimitry Andric } else { 240d88c1a5aSDimitry Andric TopCand.Reason = TopReason; 241d88c1a5aSDimitry Andric } 242d88c1a5aSDimitry Andric } else { 243d88c1a5aSDimitry Andric if (TopCand.Reason == RegExcess && TopCand.RPDelta.Excess.getUnitInc() <= 0) { 244d88c1a5aSDimitry Andric Cand = TopCand; 245d88c1a5aSDimitry Andric } else if (BotCand.Reason == RegExcess && BotCand.RPDelta.Excess.getUnitInc() <= 0) { 246d88c1a5aSDimitry Andric Cand = BotCand; 247d88c1a5aSDimitry Andric } else if (TopCand.Reason == RegCritical && TopCand.RPDelta.CriticalMax.getUnitInc() <= 0) { 248d88c1a5aSDimitry Andric Cand = TopCand; 249d88c1a5aSDimitry Andric } else if (BotCand.Reason == RegCritical && BotCand.RPDelta.CriticalMax.getUnitInc() <= 0) { 250d88c1a5aSDimitry Andric Cand = BotCand; 251d88c1a5aSDimitry Andric } else { 2527a7e6055SDimitry Andric if (BotCand.Reason > TopCand.Reason) { 253d88c1a5aSDimitry Andric Cand = TopCand; 254d88c1a5aSDimitry Andric } else { 255d88c1a5aSDimitry Andric Cand = BotCand; 256d88c1a5aSDimitry Andric } 257d88c1a5aSDimitry Andric } 258d88c1a5aSDimitry Andric } 259d88c1a5aSDimitry Andric DEBUG( 260d88c1a5aSDimitry Andric dbgs() << "Picking: "; 261d88c1a5aSDimitry Andric traceCandidate(Cand); 262d88c1a5aSDimitry Andric ); 263d88c1a5aSDimitry Andric 264d88c1a5aSDimitry Andric IsTopNode = Cand.AtTop; 265d88c1a5aSDimitry Andric return Cand.SU; 266d88c1a5aSDimitry Andric } 267d88c1a5aSDimitry Andric 268d88c1a5aSDimitry Andric // This function is mostly cut and pasted from 269d88c1a5aSDimitry Andric // GenericScheduler::pickNode() 270d88c1a5aSDimitry Andric SUnit *GCNMaxOccupancySchedStrategy::pickNode(bool &IsTopNode) { 271d88c1a5aSDimitry Andric if (DAG->top() == DAG->bottom()) { 272d88c1a5aSDimitry Andric assert(Top.Available.empty() && Top.Pending.empty() && 273d88c1a5aSDimitry Andric Bot.Available.empty() && Bot.Pending.empty() && "ReadyQ garbage"); 274d88c1a5aSDimitry Andric return nullptr; 275d88c1a5aSDimitry Andric } 276d88c1a5aSDimitry Andric SUnit *SU; 277d88c1a5aSDimitry Andric do { 278d88c1a5aSDimitry Andric if (RegionPolicy.OnlyTopDown) { 279d88c1a5aSDimitry Andric SU = Top.pickOnlyChoice(); 280d88c1a5aSDimitry Andric if (!SU) { 281d88c1a5aSDimitry Andric CandPolicy NoPolicy; 282d88c1a5aSDimitry Andric TopCand.reset(NoPolicy); 283d88c1a5aSDimitry Andric pickNodeFromQueue(Top, NoPolicy, DAG->getTopRPTracker(), TopCand); 284d88c1a5aSDimitry Andric assert(TopCand.Reason != NoCand && "failed to find a candidate"); 285d88c1a5aSDimitry Andric SU = TopCand.SU; 286d88c1a5aSDimitry Andric } 287d88c1a5aSDimitry Andric IsTopNode = true; 288d88c1a5aSDimitry Andric } else if (RegionPolicy.OnlyBottomUp) { 289d88c1a5aSDimitry Andric SU = Bot.pickOnlyChoice(); 290d88c1a5aSDimitry Andric if (!SU) { 291d88c1a5aSDimitry Andric CandPolicy NoPolicy; 292d88c1a5aSDimitry Andric BotCand.reset(NoPolicy); 293d88c1a5aSDimitry Andric pickNodeFromQueue(Bot, NoPolicy, DAG->getBotRPTracker(), BotCand); 294d88c1a5aSDimitry Andric assert(BotCand.Reason != NoCand && "failed to find a candidate"); 295d88c1a5aSDimitry Andric SU = BotCand.SU; 296d88c1a5aSDimitry Andric } 297d88c1a5aSDimitry Andric IsTopNode = false; 298d88c1a5aSDimitry Andric } else { 299d88c1a5aSDimitry Andric SU = pickNodeBidirectional(IsTopNode); 300d88c1a5aSDimitry Andric } 301d88c1a5aSDimitry Andric } while (SU->isScheduled); 302d88c1a5aSDimitry Andric 303d88c1a5aSDimitry Andric if (SU->isTopReady()) 304d88c1a5aSDimitry Andric Top.removeReady(SU); 305d88c1a5aSDimitry Andric if (SU->isBottomReady()) 306d88c1a5aSDimitry Andric Bot.removeReady(SU); 307d88c1a5aSDimitry Andric 308d88c1a5aSDimitry Andric DEBUG(dbgs() << "Scheduling SU(" << SU->NodeNum << ") " << *SU->getInstr()); 309d88c1a5aSDimitry Andric return SU; 310d88c1a5aSDimitry Andric } 3117a7e6055SDimitry Andric 3127a7e6055SDimitry Andric GCNScheduleDAGMILive::GCNScheduleDAGMILive(MachineSchedContext *C, 3137a7e6055SDimitry Andric std::unique_ptr<MachineSchedStrategy> S) : 3147a7e6055SDimitry Andric ScheduleDAGMILive(C, std::move(S)), 3157a7e6055SDimitry Andric ST(MF.getSubtarget<SISubtarget>()), 3167a7e6055SDimitry Andric MFI(*MF.getInfo<SIMachineFunctionInfo>()), 3177a7e6055SDimitry Andric StartingOccupancy(ST.getOccupancyWithLocalMemSize(MFI.getLDSSize(), 3182cab237bSDimitry Andric MF.getFunction())), 3195517e702SDimitry Andric MinOccupancy(StartingOccupancy), Stage(0), RegionIdx(0) { 3207a7e6055SDimitry Andric 3217a7e6055SDimitry Andric DEBUG(dbgs() << "Starting occupancy is " << StartingOccupancy << ".\n"); 3227a7e6055SDimitry Andric } 3237a7e6055SDimitry Andric 3247a7e6055SDimitry Andric void GCNScheduleDAGMILive::schedule() { 3255517e702SDimitry Andric if (Stage == 0) { 3265517e702SDimitry Andric // Just record regions at the first pass. 3275517e702SDimitry Andric Regions.push_back(std::make_pair(RegionBegin, RegionEnd)); 3285517e702SDimitry Andric return; 3295517e702SDimitry Andric } 3305517e702SDimitry Andric 3317a7e6055SDimitry Andric std::vector<MachineInstr*> Unsched; 3327a7e6055SDimitry Andric Unsched.reserve(NumRegionInstrs); 3332cab237bSDimitry Andric for (auto &I : *this) { 3347a7e6055SDimitry Andric Unsched.push_back(&I); 3352cab237bSDimitry Andric } 3367a7e6055SDimitry Andric 3375517e702SDimitry Andric GCNRegPressure PressureBefore; 3387a7e6055SDimitry Andric if (LIS) { 3395517e702SDimitry Andric PressureBefore = Pressure[RegionIdx]; 3405517e702SDimitry Andric 3415517e702SDimitry Andric DEBUG(dbgs() << "Pressure before scheduling:\nRegion live-ins:"; 3425517e702SDimitry Andric GCNRPTracker::printLiveRegs(dbgs(), LiveIns[RegionIdx], MRI); 3435517e702SDimitry Andric dbgs() << "Region live-in pressure: "; 3445517e702SDimitry Andric llvm::getRegPressure(MRI, LiveIns[RegionIdx]).print(dbgs()); 3455517e702SDimitry Andric dbgs() << "Region register pressure: "; 3465517e702SDimitry Andric PressureBefore.print(dbgs())); 3477a7e6055SDimitry Andric } 3487a7e6055SDimitry Andric 3497a7e6055SDimitry Andric ScheduleDAGMILive::schedule(); 3505517e702SDimitry Andric Regions[RegionIdx] = std::make_pair(RegionBegin, RegionEnd); 3517a7e6055SDimitry Andric 3527a7e6055SDimitry Andric if (!LIS) 3537a7e6055SDimitry Andric return; 3547a7e6055SDimitry Andric 3557a7e6055SDimitry Andric // Check the results of scheduling. 3567a7e6055SDimitry Andric GCNMaxOccupancySchedStrategy &S = (GCNMaxOccupancySchedStrategy&)*SchedImpl; 3577a7e6055SDimitry Andric auto PressureAfter = getRealRegPressure(); 3587a7e6055SDimitry Andric 3595517e702SDimitry Andric DEBUG(dbgs() << "Pressure after scheduling: "; PressureAfter.print(dbgs())); 3605517e702SDimitry Andric 3615517e702SDimitry Andric if (PressureAfter.getSGPRNum() <= S.SGPRCriticalLimit && 3625517e702SDimitry Andric PressureAfter.getVGPRNum() <= S.VGPRCriticalLimit) { 3635517e702SDimitry Andric Pressure[RegionIdx] = PressureAfter; 3647a7e6055SDimitry Andric DEBUG(dbgs() << "Pressure in desired limits, done.\n"); 3657a7e6055SDimitry Andric return; 3667a7e6055SDimitry Andric } 3675517e702SDimitry Andric unsigned WavesAfter = getMaxWaves(PressureAfter.getSGPRNum(), 3685517e702SDimitry Andric PressureAfter.getVGPRNum(), MF); 3695517e702SDimitry Andric unsigned WavesBefore = getMaxWaves(PressureBefore.getSGPRNum(), 3705517e702SDimitry Andric PressureBefore.getVGPRNum(), MF); 3717a7e6055SDimitry Andric DEBUG(dbgs() << "Occupancy before scheduling: " << WavesBefore << 3727a7e6055SDimitry Andric ", after " << WavesAfter << ".\n"); 3737a7e6055SDimitry Andric 3747a7e6055SDimitry Andric // We could not keep current target occupancy because of the just scheduled 3757a7e6055SDimitry Andric // region. Record new occupancy for next scheduling cycle. 3767a7e6055SDimitry Andric unsigned NewOccupancy = std::max(WavesAfter, WavesBefore); 3777a7e6055SDimitry Andric if (NewOccupancy < MinOccupancy) { 3787a7e6055SDimitry Andric MinOccupancy = NewOccupancy; 3797a7e6055SDimitry Andric DEBUG(dbgs() << "Occupancy lowered for the function to " 3807a7e6055SDimitry Andric << MinOccupancy << ".\n"); 3817a7e6055SDimitry Andric } 3827a7e6055SDimitry Andric 3835517e702SDimitry Andric if (WavesAfter >= WavesBefore) { 3845517e702SDimitry Andric Pressure[RegionIdx] = PressureAfter; 3857a7e6055SDimitry Andric return; 3865517e702SDimitry Andric } 3877a7e6055SDimitry Andric 3887a7e6055SDimitry Andric DEBUG(dbgs() << "Attempting to revert scheduling.\n"); 3897a7e6055SDimitry Andric RegionEnd = RegionBegin; 3907a7e6055SDimitry Andric for (MachineInstr *MI : Unsched) { 3912cab237bSDimitry Andric if (MI->isDebugValue()) 3922cab237bSDimitry Andric continue; 3932cab237bSDimitry Andric 3947a7e6055SDimitry Andric if (MI->getIterator() != RegionEnd) { 3957a7e6055SDimitry Andric BB->remove(MI); 3967a7e6055SDimitry Andric BB->insert(RegionEnd, MI); 3972cab237bSDimitry Andric if (!MI->isDebugValue()) 3987a7e6055SDimitry Andric LIS->handleMove(*MI, true); 3997a7e6055SDimitry Andric } 4007a7e6055SDimitry Andric // Reset read-undef flags and update them later. 4017a7e6055SDimitry Andric for (auto &Op : MI->operands()) 4027a7e6055SDimitry Andric if (Op.isReg() && Op.isDef()) 4037a7e6055SDimitry Andric Op.setIsUndef(false); 4047a7e6055SDimitry Andric RegisterOperands RegOpers; 4057a7e6055SDimitry Andric RegOpers.collect(*MI, *TRI, MRI, ShouldTrackLaneMasks, false); 4062cab237bSDimitry Andric if (!MI->isDebugValue()) { 4077a7e6055SDimitry Andric if (ShouldTrackLaneMasks) { 4087a7e6055SDimitry Andric // Adjust liveness and add missing dead+read-undef flags. 4097a7e6055SDimitry Andric SlotIndex SlotIdx = LIS->getInstructionIndex(*MI).getRegSlot(); 4107a7e6055SDimitry Andric RegOpers.adjustLaneLiveness(*LIS, MRI, SlotIdx, MI); 4117a7e6055SDimitry Andric } else { 4127a7e6055SDimitry Andric // Adjust for missing dead-def flags. 4137a7e6055SDimitry Andric RegOpers.detectDeadDefs(*MI, *LIS); 4147a7e6055SDimitry Andric } 4152cab237bSDimitry Andric } 4167a7e6055SDimitry Andric RegionEnd = MI->getIterator(); 4177a7e6055SDimitry Andric ++RegionEnd; 4187a7e6055SDimitry Andric DEBUG(dbgs() << "Scheduling " << *MI); 4197a7e6055SDimitry Andric } 4207a7e6055SDimitry Andric RegionBegin = Unsched.front()->getIterator(); 4215517e702SDimitry Andric Regions[RegionIdx] = std::make_pair(RegionBegin, RegionEnd); 4227a7e6055SDimitry Andric 4237a7e6055SDimitry Andric placeDebugValues(); 4247a7e6055SDimitry Andric } 4257a7e6055SDimitry Andric 4265517e702SDimitry Andric GCNRegPressure GCNScheduleDAGMILive::getRealRegPressure() const { 4275517e702SDimitry Andric GCNDownwardRPTracker RPTracker(*LIS); 4285517e702SDimitry Andric RPTracker.advance(begin(), end(), &LiveIns[RegionIdx]); 4295517e702SDimitry Andric return RPTracker.moveMaxPressure(); 4307a7e6055SDimitry Andric } 4317a7e6055SDimitry Andric 4325517e702SDimitry Andric void GCNScheduleDAGMILive::computeBlockPressure(const MachineBasicBlock *MBB) { 4335517e702SDimitry Andric GCNDownwardRPTracker RPTracker(*LIS); 4347a7e6055SDimitry Andric 4355517e702SDimitry Andric // If the block has the only successor then live-ins of that successor are 4365517e702SDimitry Andric // live-outs of the current block. We can reuse calculated live set if the 4375517e702SDimitry Andric // successor will be sent to scheduling past current block. 4385517e702SDimitry Andric const MachineBasicBlock *OnlySucc = nullptr; 4395517e702SDimitry Andric if (MBB->succ_size() == 1 && !(*MBB->succ_begin())->empty()) { 4405517e702SDimitry Andric SlotIndexes *Ind = LIS->getSlotIndexes(); 4415517e702SDimitry Andric if (Ind->getMBBStartIdx(MBB) < Ind->getMBBStartIdx(*MBB->succ_begin())) 4425517e702SDimitry Andric OnlySucc = *MBB->succ_begin(); 4437a7e6055SDimitry Andric } 4447a7e6055SDimitry Andric 4455517e702SDimitry Andric // Scheduler sends regions from the end of the block upwards. 4465517e702SDimitry Andric size_t CurRegion = RegionIdx; 4475517e702SDimitry Andric for (size_t E = Regions.size(); CurRegion != E; ++CurRegion) 4485517e702SDimitry Andric if (Regions[CurRegion].first->getParent() != MBB) 4495517e702SDimitry Andric break; 4505517e702SDimitry Andric --CurRegion; 4517a7e6055SDimitry Andric 4525517e702SDimitry Andric auto I = MBB->begin(); 4535517e702SDimitry Andric auto LiveInIt = MBBLiveIns.find(MBB); 4545517e702SDimitry Andric if (LiveInIt != MBBLiveIns.end()) { 4555517e702SDimitry Andric auto LiveIn = std::move(LiveInIt->second); 4565517e702SDimitry Andric RPTracker.reset(*MBB->begin(), &LiveIn); 4575517e702SDimitry Andric MBBLiveIns.erase(LiveInIt); 4585517e702SDimitry Andric } else { 4595517e702SDimitry Andric I = Regions[CurRegion].first; 4605517e702SDimitry Andric RPTracker.reset(*I); 4617a7e6055SDimitry Andric } 4627a7e6055SDimitry Andric 4635517e702SDimitry Andric for ( ; ; ) { 4645517e702SDimitry Andric I = RPTracker.getNext(); 4657a7e6055SDimitry Andric 4665517e702SDimitry Andric if (Regions[CurRegion].first == I) { 4675517e702SDimitry Andric LiveIns[CurRegion] = RPTracker.getLiveRegs(); 4685517e702SDimitry Andric RPTracker.clearMaxPressure(); 4697a7e6055SDimitry Andric } 4707a7e6055SDimitry Andric 4715517e702SDimitry Andric if (Regions[CurRegion].second == I) { 4725517e702SDimitry Andric Pressure[CurRegion] = RPTracker.moveMaxPressure(); 4735517e702SDimitry Andric if (CurRegion-- == RegionIdx) 4745517e702SDimitry Andric break; 4757a7e6055SDimitry Andric } 4765517e702SDimitry Andric RPTracker.advanceToNext(); 4775517e702SDimitry Andric RPTracker.advanceBeforeNext(); 4787a7e6055SDimitry Andric } 4797a7e6055SDimitry Andric 4805517e702SDimitry Andric if (OnlySucc) { 4815517e702SDimitry Andric if (I != MBB->end()) { 4825517e702SDimitry Andric RPTracker.advanceToNext(); 4835517e702SDimitry Andric RPTracker.advance(MBB->end()); 4847a7e6055SDimitry Andric } 4855517e702SDimitry Andric RPTracker.reset(*OnlySucc->begin(), &RPTracker.getLiveRegs()); 4865517e702SDimitry Andric RPTracker.advanceBeforeNext(); 4875517e702SDimitry Andric MBBLiveIns[OnlySucc] = RPTracker.moveLiveRegs(); 4887a7e6055SDimitry Andric } 4897a7e6055SDimitry Andric } 4907a7e6055SDimitry Andric 4917a7e6055SDimitry Andric void GCNScheduleDAGMILive::finalizeSchedule() { 4925517e702SDimitry Andric GCNMaxOccupancySchedStrategy &S = (GCNMaxOccupancySchedStrategy&)*SchedImpl; 4935517e702SDimitry Andric DEBUG(dbgs() << "All regions recorded, starting actual scheduling.\n"); 4945517e702SDimitry Andric 4955517e702SDimitry Andric LiveIns.resize(Regions.size()); 4965517e702SDimitry Andric Pressure.resize(Regions.size()); 4975517e702SDimitry Andric 4985517e702SDimitry Andric do { 4995517e702SDimitry Andric Stage++; 5005517e702SDimitry Andric RegionIdx = 0; 5015517e702SDimitry Andric MachineBasicBlock *MBB = nullptr; 5025517e702SDimitry Andric 5035517e702SDimitry Andric if (Stage > 1) { 5047a7e6055SDimitry Andric // Retry function scheduling if we found resulting occupancy and it is 5057a7e6055SDimitry Andric // lower than used for first pass scheduling. This will give more freedom 5067a7e6055SDimitry Andric // to schedule low register pressure blocks. 5077a7e6055SDimitry Andric // Code is partially copied from MachineSchedulerBase::scheduleRegions(). 5087a7e6055SDimitry Andric 5097a7e6055SDimitry Andric if (!LIS || StartingOccupancy <= MinOccupancy) 5105517e702SDimitry Andric break; 5117a7e6055SDimitry Andric 5125517e702SDimitry Andric DEBUG(dbgs() 5135517e702SDimitry Andric << "Retrying function scheduling with lowest recorded occupancy " 5147a7e6055SDimitry Andric << MinOccupancy << ".\n"); 5157a7e6055SDimitry Andric 5167a7e6055SDimitry Andric S.setTargetOccupancy(MinOccupancy); 5175517e702SDimitry Andric } 5187a7e6055SDimitry Andric 5197a7e6055SDimitry Andric for (auto Region : Regions) { 5207a7e6055SDimitry Andric RegionBegin = Region.first; 5217a7e6055SDimitry Andric RegionEnd = Region.second; 5227a7e6055SDimitry Andric 5237a7e6055SDimitry Andric if (RegionBegin->getParent() != MBB) { 5247a7e6055SDimitry Andric if (MBB) finishBlock(); 5257a7e6055SDimitry Andric MBB = RegionBegin->getParent(); 5267a7e6055SDimitry Andric startBlock(MBB); 5275517e702SDimitry Andric if (Stage == 1) 5285517e702SDimitry Andric computeBlockPressure(MBB); 5297a7e6055SDimitry Andric } 5307a7e6055SDimitry Andric 5317a7e6055SDimitry Andric unsigned NumRegionInstrs = std::distance(begin(), end()); 5327a7e6055SDimitry Andric enterRegion(MBB, begin(), end(), NumRegionInstrs); 5337a7e6055SDimitry Andric 5347a7e6055SDimitry Andric // Skip empty scheduling regions (0 or 1 schedulable instructions). 5357a7e6055SDimitry Andric if (begin() == end() || begin() == std::prev(end())) { 5367a7e6055SDimitry Andric exitRegion(); 5377a7e6055SDimitry Andric continue; 5387a7e6055SDimitry Andric } 5395517e702SDimitry Andric 5407a7e6055SDimitry Andric DEBUG(dbgs() << "********** MI Scheduling **********\n"); 5412cab237bSDimitry Andric DEBUG(dbgs() << MF.getName() << ":" << printMBBReference(*MBB) << " " 5422cab237bSDimitry Andric << MBB->getName() << "\n From: " << *begin() << " To: "; 5437a7e6055SDimitry Andric if (RegionEnd != MBB->end()) dbgs() << *RegionEnd; 5447a7e6055SDimitry Andric else dbgs() << "End"; 5457a7e6055SDimitry Andric dbgs() << " RegionInstrs: " << NumRegionInstrs << '\n'); 5467a7e6055SDimitry Andric 5477a7e6055SDimitry Andric schedule(); 5487a7e6055SDimitry Andric 5497a7e6055SDimitry Andric exitRegion(); 5505517e702SDimitry Andric ++RegionIdx; 5517a7e6055SDimitry Andric } 5527a7e6055SDimitry Andric finishBlock(); 5535517e702SDimitry Andric 5545517e702SDimitry Andric } while (Stage < 2); 5557a7e6055SDimitry Andric } 556