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 23d88c1a5aSDimitry Andric #define DEBUG_TYPE "misched" 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(), 407a7e6055SDimitry 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(), 3187a7e6055SDimitry Andric *MF.getFunction())), 3197a7e6055SDimitry Andric MinOccupancy(StartingOccupancy), Stage(0) { 3207a7e6055SDimitry Andric 3217a7e6055SDimitry Andric DEBUG(dbgs() << "Starting occupancy is " << StartingOccupancy << ".\n"); 3227a7e6055SDimitry Andric } 3237a7e6055SDimitry Andric 3247a7e6055SDimitry Andric void GCNScheduleDAGMILive::schedule() { 3257a7e6055SDimitry Andric std::vector<MachineInstr*> Unsched; 3267a7e6055SDimitry Andric Unsched.reserve(NumRegionInstrs); 3277a7e6055SDimitry Andric for (auto &I : *this) 3287a7e6055SDimitry Andric Unsched.push_back(&I); 3297a7e6055SDimitry Andric 3307a7e6055SDimitry Andric std::pair<unsigned, unsigned> PressureBefore; 3317a7e6055SDimitry Andric if (LIS) { 3327a7e6055SDimitry Andric DEBUG(dbgs() << "Pressure before scheduling:\n"); 3337a7e6055SDimitry Andric discoverLiveIns(); 3347a7e6055SDimitry Andric PressureBefore = getRealRegPressure(); 3357a7e6055SDimitry Andric } 3367a7e6055SDimitry Andric 3377a7e6055SDimitry Andric ScheduleDAGMILive::schedule(); 3387a7e6055SDimitry Andric if (Stage == 0) 3397a7e6055SDimitry Andric Regions.push_back(std::make_pair(RegionBegin, RegionEnd)); 3407a7e6055SDimitry Andric 3417a7e6055SDimitry Andric if (!LIS) 3427a7e6055SDimitry Andric return; 3437a7e6055SDimitry Andric 3447a7e6055SDimitry Andric // Check the results of scheduling. 3457a7e6055SDimitry Andric GCNMaxOccupancySchedStrategy &S = (GCNMaxOccupancySchedStrategy&)*SchedImpl; 3467a7e6055SDimitry Andric DEBUG(dbgs() << "Pressure after scheduling:\n"); 3477a7e6055SDimitry Andric auto PressureAfter = getRealRegPressure(); 3487a7e6055SDimitry Andric LiveIns.clear(); 3497a7e6055SDimitry Andric 3507a7e6055SDimitry Andric if (PressureAfter.first <= S.SGPRCriticalLimit && 3517a7e6055SDimitry Andric PressureAfter.second <= S.VGPRCriticalLimit) { 3527a7e6055SDimitry Andric DEBUG(dbgs() << "Pressure in desired limits, done.\n"); 3537a7e6055SDimitry Andric return; 3547a7e6055SDimitry Andric } 3557a7e6055SDimitry Andric unsigned WavesAfter = getMaxWaves(PressureAfter.first, 3567a7e6055SDimitry Andric PressureAfter.second, MF); 3577a7e6055SDimitry Andric unsigned WavesBefore = getMaxWaves(PressureBefore.first, 3587a7e6055SDimitry Andric PressureBefore.second, MF); 3597a7e6055SDimitry Andric DEBUG(dbgs() << "Occupancy before scheduling: " << WavesBefore << 3607a7e6055SDimitry Andric ", after " << WavesAfter << ".\n"); 3617a7e6055SDimitry Andric 3627a7e6055SDimitry Andric // We could not keep current target occupancy because of the just scheduled 3637a7e6055SDimitry Andric // region. Record new occupancy for next scheduling cycle. 3647a7e6055SDimitry Andric unsigned NewOccupancy = std::max(WavesAfter, WavesBefore); 3657a7e6055SDimitry Andric if (NewOccupancy < MinOccupancy) { 3667a7e6055SDimitry Andric MinOccupancy = NewOccupancy; 3677a7e6055SDimitry Andric DEBUG(dbgs() << "Occupancy lowered for the function to " 3687a7e6055SDimitry Andric << MinOccupancy << ".\n"); 3697a7e6055SDimitry Andric } 3707a7e6055SDimitry Andric 3717a7e6055SDimitry Andric if (WavesAfter >= WavesBefore) 3727a7e6055SDimitry Andric return; 3737a7e6055SDimitry Andric 3747a7e6055SDimitry Andric DEBUG(dbgs() << "Attempting to revert scheduling.\n"); 3757a7e6055SDimitry Andric RegionEnd = RegionBegin; 3767a7e6055SDimitry Andric for (MachineInstr *MI : Unsched) { 3777a7e6055SDimitry Andric if (MI->getIterator() != RegionEnd) { 3787a7e6055SDimitry Andric BB->remove(MI); 3797a7e6055SDimitry Andric BB->insert(RegionEnd, MI); 3807a7e6055SDimitry Andric LIS->handleMove(*MI, true); 3817a7e6055SDimitry Andric } 3827a7e6055SDimitry Andric // Reset read-undef flags and update them later. 3837a7e6055SDimitry Andric for (auto &Op : MI->operands()) 3847a7e6055SDimitry Andric if (Op.isReg() && Op.isDef()) 3857a7e6055SDimitry Andric Op.setIsUndef(false); 3867a7e6055SDimitry Andric RegisterOperands RegOpers; 3877a7e6055SDimitry Andric RegOpers.collect(*MI, *TRI, MRI, ShouldTrackLaneMasks, false); 3887a7e6055SDimitry Andric if (ShouldTrackLaneMasks) { 3897a7e6055SDimitry Andric // Adjust liveness and add missing dead+read-undef flags. 3907a7e6055SDimitry Andric SlotIndex SlotIdx = LIS->getInstructionIndex(*MI).getRegSlot(); 3917a7e6055SDimitry Andric RegOpers.adjustLaneLiveness(*LIS, MRI, SlotIdx, MI); 3927a7e6055SDimitry Andric } else { 3937a7e6055SDimitry Andric // Adjust for missing dead-def flags. 3947a7e6055SDimitry Andric RegOpers.detectDeadDefs(*MI, *LIS); 3957a7e6055SDimitry Andric } 3967a7e6055SDimitry Andric RegionEnd = MI->getIterator(); 3977a7e6055SDimitry Andric ++RegionEnd; 3987a7e6055SDimitry Andric DEBUG(dbgs() << "Scheduling " << *MI); 3997a7e6055SDimitry Andric } 4007a7e6055SDimitry Andric RegionBegin = Unsched.front()->getIterator(); 4017a7e6055SDimitry Andric if (Stage == 0) 4027a7e6055SDimitry Andric Regions.back() = std::make_pair(RegionBegin, RegionEnd); 4037a7e6055SDimitry Andric 4047a7e6055SDimitry Andric placeDebugValues(); 4057a7e6055SDimitry Andric } 4067a7e6055SDimitry Andric 4077a7e6055SDimitry Andric static inline void setMask(const MachineRegisterInfo &MRI, 4087a7e6055SDimitry Andric const SIRegisterInfo *SRI, unsigned Reg, 4097a7e6055SDimitry Andric LaneBitmask &PrevMask, LaneBitmask NewMask, 4107a7e6055SDimitry Andric unsigned &SGPRs, unsigned &VGPRs) { 4117a7e6055SDimitry Andric int NewRegs = countPopulation(NewMask.getAsInteger()) - 4127a7e6055SDimitry Andric countPopulation(PrevMask.getAsInteger()); 4137a7e6055SDimitry Andric if (SRI->isSGPRReg(MRI, Reg)) 4147a7e6055SDimitry Andric SGPRs += NewRegs; 4157a7e6055SDimitry Andric if (SRI->isVGPR(MRI, Reg)) 4167a7e6055SDimitry Andric VGPRs += NewRegs; 4177a7e6055SDimitry Andric assert ((int)SGPRs >= 0 && (int)VGPRs >= 0); 4187a7e6055SDimitry Andric PrevMask = NewMask; 4197a7e6055SDimitry Andric } 4207a7e6055SDimitry Andric 4217a7e6055SDimitry Andric void GCNScheduleDAGMILive::discoverLiveIns() { 4227a7e6055SDimitry Andric unsigned SGPRs = 0; 4237a7e6055SDimitry Andric unsigned VGPRs = 0; 4247a7e6055SDimitry Andric 4257a7e6055SDimitry Andric const SIRegisterInfo *SRI = static_cast<const SIRegisterInfo*>(TRI); 4267a7e6055SDimitry Andric SlotIndex SI = LIS->getInstructionIndex(*begin()).getBaseIndex(); 4277a7e6055SDimitry Andric assert (SI.isValid()); 4287a7e6055SDimitry Andric 4297a7e6055SDimitry Andric DEBUG(dbgs() << "Region live-ins:"); 4307a7e6055SDimitry Andric for (unsigned I = 0, E = MRI.getNumVirtRegs(); I != E; ++I) { 4317a7e6055SDimitry Andric unsigned Reg = TargetRegisterInfo::index2VirtReg(I); 4327a7e6055SDimitry Andric if (MRI.reg_nodbg_empty(Reg)) 4337a7e6055SDimitry Andric continue; 4347a7e6055SDimitry Andric const LiveInterval &LI = LIS->getInterval(Reg); 4357a7e6055SDimitry Andric LaneBitmask LaneMask = LaneBitmask::getNone(); 4367a7e6055SDimitry Andric if (LI.hasSubRanges()) { 4377a7e6055SDimitry Andric for (const auto &S : LI.subranges()) 4387a7e6055SDimitry Andric if (S.liveAt(SI)) 4397a7e6055SDimitry Andric LaneMask |= S.LaneMask; 4407a7e6055SDimitry Andric } else if (LI.liveAt(SI)) { 4417a7e6055SDimitry Andric LaneMask = MRI.getMaxLaneMaskForVReg(Reg); 4427a7e6055SDimitry Andric } 4437a7e6055SDimitry Andric 4447a7e6055SDimitry Andric if (LaneMask.any()) { 4457a7e6055SDimitry Andric setMask(MRI, SRI, Reg, LiveIns[Reg], LaneMask, SGPRs, VGPRs); 4467a7e6055SDimitry Andric 4477a7e6055SDimitry Andric DEBUG(dbgs() << ' ' << PrintVRegOrUnit(Reg, SRI) << ':' 4487a7e6055SDimitry Andric << PrintLaneMask(LiveIns[Reg])); 4497a7e6055SDimitry Andric } 4507a7e6055SDimitry Andric } 4517a7e6055SDimitry Andric 4527a7e6055SDimitry Andric LiveInPressure = std::make_pair(SGPRs, VGPRs); 4537a7e6055SDimitry Andric 4547a7e6055SDimitry Andric DEBUG(dbgs() << "\nLive-in pressure:\nSGPR = " << SGPRs 4557a7e6055SDimitry Andric << "\nVGPR = " << VGPRs << '\n'); 4567a7e6055SDimitry Andric } 4577a7e6055SDimitry Andric 4587a7e6055SDimitry Andric std::pair<unsigned, unsigned> 4597a7e6055SDimitry Andric GCNScheduleDAGMILive::getRealRegPressure() const { 4607a7e6055SDimitry Andric unsigned SGPRs, MaxSGPRs, VGPRs, MaxVGPRs; 4617a7e6055SDimitry Andric SGPRs = MaxSGPRs = LiveInPressure.first; 4627a7e6055SDimitry Andric VGPRs = MaxVGPRs = LiveInPressure.second; 4637a7e6055SDimitry Andric 4647a7e6055SDimitry Andric const SIRegisterInfo *SRI = static_cast<const SIRegisterInfo*>(TRI); 4657a7e6055SDimitry Andric DenseMap<unsigned, LaneBitmask> LiveRegs(LiveIns); 4667a7e6055SDimitry Andric 4677a7e6055SDimitry Andric for (const MachineInstr &MI : *this) { 4687a7e6055SDimitry Andric if (MI.isDebugValue()) 4697a7e6055SDimitry Andric continue; 4707a7e6055SDimitry Andric SlotIndex SI = LIS->getInstructionIndex(MI).getBaseIndex(); 4717a7e6055SDimitry Andric assert (SI.isValid()); 4727a7e6055SDimitry Andric 4737a7e6055SDimitry Andric // Remove dead registers or mask bits. 4747a7e6055SDimitry Andric for (auto &It : LiveRegs) { 4757a7e6055SDimitry Andric if (It.second.none()) 4767a7e6055SDimitry Andric continue; 4777a7e6055SDimitry Andric const LiveInterval &LI = LIS->getInterval(It.first); 4787a7e6055SDimitry Andric if (LI.hasSubRanges()) { 4797a7e6055SDimitry Andric for (const auto &S : LI.subranges()) 4807a7e6055SDimitry Andric if (!S.liveAt(SI)) 4817a7e6055SDimitry Andric setMask(MRI, SRI, It.first, It.second, It.second & ~S.LaneMask, 4827a7e6055SDimitry Andric SGPRs, VGPRs); 4837a7e6055SDimitry Andric } else if (!LI.liveAt(SI)) { 4847a7e6055SDimitry Andric setMask(MRI, SRI, It.first, It.second, LaneBitmask::getNone(), 4857a7e6055SDimitry Andric SGPRs, VGPRs); 4867a7e6055SDimitry Andric } 4877a7e6055SDimitry Andric } 4887a7e6055SDimitry Andric 4897a7e6055SDimitry Andric // Add new registers or mask bits. 4907a7e6055SDimitry Andric for (const auto &MO : MI.defs()) { 4917a7e6055SDimitry Andric if (!MO.isReg()) 4927a7e6055SDimitry Andric continue; 4937a7e6055SDimitry Andric unsigned Reg = MO.getReg(); 4947a7e6055SDimitry Andric if (!TargetRegisterInfo::isVirtualRegister(Reg)) 4957a7e6055SDimitry Andric continue; 4967a7e6055SDimitry Andric unsigned SubRegIdx = MO.getSubReg(); 4977a7e6055SDimitry Andric LaneBitmask LaneMask = SubRegIdx != 0 4987a7e6055SDimitry Andric ? TRI->getSubRegIndexLaneMask(SubRegIdx) 4997a7e6055SDimitry Andric : MRI.getMaxLaneMaskForVReg(Reg); 5007a7e6055SDimitry Andric LaneBitmask &LM = LiveRegs[Reg]; 5017a7e6055SDimitry Andric setMask(MRI, SRI, Reg, LM, LM | LaneMask, SGPRs, VGPRs); 5027a7e6055SDimitry Andric } 5037a7e6055SDimitry Andric MaxSGPRs = std::max(MaxSGPRs, SGPRs); 5047a7e6055SDimitry Andric MaxVGPRs = std::max(MaxVGPRs, VGPRs); 5057a7e6055SDimitry Andric } 5067a7e6055SDimitry Andric 5077a7e6055SDimitry Andric DEBUG(dbgs() << "Real region's register pressure:\nSGPR = " << MaxSGPRs 5087a7e6055SDimitry Andric << "\nVGPR = " << MaxVGPRs << '\n'); 5097a7e6055SDimitry Andric 5107a7e6055SDimitry Andric return std::make_pair(MaxSGPRs, MaxVGPRs); 5117a7e6055SDimitry Andric } 5127a7e6055SDimitry Andric 5137a7e6055SDimitry Andric void GCNScheduleDAGMILive::finalizeSchedule() { 5147a7e6055SDimitry Andric // Retry function scheduling if we found resulting occupancy and it is 5157a7e6055SDimitry Andric // lower than used for first pass scheduling. This will give more freedom 5167a7e6055SDimitry Andric // to schedule low register pressure blocks. 5177a7e6055SDimitry Andric // Code is partially copied from MachineSchedulerBase::scheduleRegions(). 5187a7e6055SDimitry Andric 5197a7e6055SDimitry Andric if (!LIS || StartingOccupancy <= MinOccupancy) 5207a7e6055SDimitry Andric return; 5217a7e6055SDimitry Andric 5227a7e6055SDimitry Andric DEBUG(dbgs() << "Retrying function scheduling with lowest recorded occupancy " 5237a7e6055SDimitry Andric << MinOccupancy << ".\n"); 5247a7e6055SDimitry Andric 5257a7e6055SDimitry Andric Stage++; 5267a7e6055SDimitry Andric GCNMaxOccupancySchedStrategy &S = (GCNMaxOccupancySchedStrategy&)*SchedImpl; 5277a7e6055SDimitry Andric S.setTargetOccupancy(MinOccupancy); 5287a7e6055SDimitry Andric 5297a7e6055SDimitry Andric MachineBasicBlock *MBB = nullptr; 5307a7e6055SDimitry Andric for (auto Region : Regions) { 5317a7e6055SDimitry Andric RegionBegin = Region.first; 5327a7e6055SDimitry Andric RegionEnd = Region.second; 5337a7e6055SDimitry Andric 5347a7e6055SDimitry Andric if (RegionBegin->getParent() != MBB) { 5357a7e6055SDimitry Andric if (MBB) finishBlock(); 5367a7e6055SDimitry Andric MBB = RegionBegin->getParent(); 5377a7e6055SDimitry Andric startBlock(MBB); 5387a7e6055SDimitry Andric } 5397a7e6055SDimitry Andric 5407a7e6055SDimitry Andric unsigned NumRegionInstrs = std::distance(begin(), end()); 5417a7e6055SDimitry Andric enterRegion(MBB, begin(), end(), NumRegionInstrs); 5427a7e6055SDimitry Andric 5437a7e6055SDimitry Andric // Skip empty scheduling regions (0 or 1 schedulable instructions). 5447a7e6055SDimitry Andric if (begin() == end() || begin() == std::prev(end())) { 5457a7e6055SDimitry Andric exitRegion(); 5467a7e6055SDimitry Andric continue; 5477a7e6055SDimitry Andric } 5487a7e6055SDimitry Andric DEBUG(dbgs() << "********** MI Scheduling **********\n"); 5497a7e6055SDimitry Andric DEBUG(dbgs() << MF.getName() 5507a7e6055SDimitry Andric << ":BB#" << MBB->getNumber() << " " << MBB->getName() 5517a7e6055SDimitry Andric << "\n From: " << *begin() << " To: "; 5527a7e6055SDimitry Andric if (RegionEnd != MBB->end()) dbgs() << *RegionEnd; 5537a7e6055SDimitry Andric else dbgs() << "End"; 5547a7e6055SDimitry Andric dbgs() << " RegionInstrs: " << NumRegionInstrs << '\n'); 5557a7e6055SDimitry Andric 5567a7e6055SDimitry Andric schedule(); 5577a7e6055SDimitry Andric 5587a7e6055SDimitry Andric exitRegion(); 5597a7e6055SDimitry Andric } 5607a7e6055SDimitry Andric finishBlock(); 5617a7e6055SDimitry Andric LiveIns.shrink_and_clear(); 5627a7e6055SDimitry Andric } 563