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" 21d88c1a5aSDimitry Andric 22d88c1a5aSDimitry Andric #define DEBUG_TYPE "misched" 23d88c1a5aSDimitry Andric 24d88c1a5aSDimitry Andric using namespace llvm; 25d88c1a5aSDimitry Andric 26d88c1a5aSDimitry Andric GCNMaxOccupancySchedStrategy::GCNMaxOccupancySchedStrategy( 27d88c1a5aSDimitry Andric const MachineSchedContext *C) : 28d88c1a5aSDimitry Andric GenericScheduler(C) { } 29d88c1a5aSDimitry Andric 30d88c1a5aSDimitry Andric static unsigned getMaxWaves(unsigned SGPRs, unsigned VGPRs, 31d88c1a5aSDimitry Andric const MachineFunction &MF) { 32d88c1a5aSDimitry Andric 33d88c1a5aSDimitry Andric const SISubtarget &ST = MF.getSubtarget<SISubtarget>(); 34d88c1a5aSDimitry Andric const SIMachineFunctionInfo *MFI = MF.getInfo<SIMachineFunctionInfo>(); 35d88c1a5aSDimitry Andric unsigned MinRegOccupancy = std::min(ST.getOccupancyWithNumSGPRs(SGPRs), 36d88c1a5aSDimitry Andric ST.getOccupancyWithNumVGPRs(VGPRs)); 37d88c1a5aSDimitry Andric return std::min(MinRegOccupancy, 38d88c1a5aSDimitry Andric ST.getOccupancyWithLocalMemSize(MFI->getLDSSize())); 39d88c1a5aSDimitry Andric } 40d88c1a5aSDimitry Andric 41d88c1a5aSDimitry Andric void GCNMaxOccupancySchedStrategy::initCandidate(SchedCandidate &Cand, SUnit *SU, 42d88c1a5aSDimitry Andric bool AtTop, const RegPressureTracker &RPTracker, 43d88c1a5aSDimitry Andric const SIRegisterInfo *SRI, 44d88c1a5aSDimitry Andric int SGPRPressure, 45d88c1a5aSDimitry Andric int VGPRPressure, 46d88c1a5aSDimitry Andric int SGPRExcessLimit, 47d88c1a5aSDimitry Andric int VGPRExcessLimit, 48d88c1a5aSDimitry Andric int SGPRCriticalLimit, 49d88c1a5aSDimitry Andric int VGPRCriticalLimit) { 50d88c1a5aSDimitry Andric 51d88c1a5aSDimitry Andric Cand.SU = SU; 52d88c1a5aSDimitry Andric Cand.AtTop = AtTop; 53d88c1a5aSDimitry Andric 54d88c1a5aSDimitry Andric // getDownwardPressure() and getUpwardPressure() make temporary changes to 55d88c1a5aSDimitry Andric // the the tracker, so we need to pass those function a non-const copy. 56d88c1a5aSDimitry Andric RegPressureTracker &TempTracker = const_cast<RegPressureTracker&>(RPTracker); 57d88c1a5aSDimitry Andric 58d88c1a5aSDimitry Andric std::vector<unsigned> Pressure; 59d88c1a5aSDimitry Andric std::vector<unsigned> MaxPressure; 60d88c1a5aSDimitry Andric 61d88c1a5aSDimitry Andric if (AtTop) 62d88c1a5aSDimitry Andric TempTracker.getDownwardPressure(SU->getInstr(), Pressure, MaxPressure); 63d88c1a5aSDimitry Andric else { 64d88c1a5aSDimitry Andric // FIXME: I think for bottom up scheduling, the register pressure is cached 65d88c1a5aSDimitry Andric // and can be retrieved by DAG->getPressureDif(SU). 66d88c1a5aSDimitry Andric TempTracker.getUpwardPressure(SU->getInstr(), Pressure, MaxPressure); 67d88c1a5aSDimitry Andric } 68d88c1a5aSDimitry Andric 69d88c1a5aSDimitry Andric int NewSGPRPressure = Pressure[SRI->getSGPRPressureSet()]; 70d88c1a5aSDimitry Andric int NewVGPRPressure = Pressure[SRI->getVGPRPressureSet()]; 71d88c1a5aSDimitry Andric 72d88c1a5aSDimitry Andric // If two instructions increase the pressure of different register sets 73d88c1a5aSDimitry Andric // by the same amount, the generic scheduler will prefer to schedule the 74d88c1a5aSDimitry Andric // instruction that increases the set with the least amount of registers, 75d88c1a5aSDimitry Andric // which in our case would be SGPRs. This is rarely what we want, so 76d88c1a5aSDimitry Andric // when we report excess/critical register pressure, we do it either 77d88c1a5aSDimitry Andric // only for VGPRs or only for SGPRs. 78d88c1a5aSDimitry Andric 79d88c1a5aSDimitry Andric // FIXME: Better heuristics to determine whether to prefer SGPRs or VGPRs. 80d88c1a5aSDimitry Andric const int MaxVGPRPressureInc = 16; 81d88c1a5aSDimitry Andric bool ShouldTrackVGPRs = VGPRPressure + MaxVGPRPressureInc >= VGPRExcessLimit; 82d88c1a5aSDimitry Andric bool ShouldTrackSGPRs = !ShouldTrackVGPRs && SGPRPressure >= SGPRExcessLimit; 83d88c1a5aSDimitry Andric 84d88c1a5aSDimitry Andric 85d88c1a5aSDimitry Andric // FIXME: We have to enter REG-EXCESS before we reach the actual threshold 86d88c1a5aSDimitry Andric // to increase the likelihood we don't go over the limits. We should improve 87d88c1a5aSDimitry Andric // the analysis to look through dependencies to find the path with the least 88d88c1a5aSDimitry Andric // register pressure. 89d88c1a5aSDimitry Andric // FIXME: This is also necessary, because some passes that run after 90d88c1a5aSDimitry Andric // scheduling and before regalloc increase register pressure. 91d88c1a5aSDimitry Andric const int ErrorMargin = 3; 92d88c1a5aSDimitry Andric VGPRExcessLimit -= ErrorMargin; 93d88c1a5aSDimitry Andric SGPRExcessLimit -= ErrorMargin; 94d88c1a5aSDimitry Andric 95d88c1a5aSDimitry Andric // We only need to update the RPDelata for instructions that increase 96d88c1a5aSDimitry Andric // register pressure. Instructions that decrease or keep reg pressure 97d88c1a5aSDimitry Andric // the same will be marked as RegExcess in tryCandidate() when they 98d88c1a5aSDimitry Andric // are compared with instructions that increase the register pressure. 99d88c1a5aSDimitry Andric if (ShouldTrackVGPRs && NewVGPRPressure >= VGPRExcessLimit) { 100d88c1a5aSDimitry Andric Cand.RPDelta.Excess = PressureChange(SRI->getVGPRPressureSet()); 101d88c1a5aSDimitry Andric Cand.RPDelta.Excess.setUnitInc(NewVGPRPressure - VGPRExcessLimit); 102d88c1a5aSDimitry Andric } 103d88c1a5aSDimitry Andric 104d88c1a5aSDimitry Andric if (ShouldTrackSGPRs && NewSGPRPressure >= SGPRExcessLimit) { 105d88c1a5aSDimitry Andric Cand.RPDelta.Excess = PressureChange(SRI->getSGPRPressureSet()); 106d88c1a5aSDimitry Andric Cand.RPDelta.Excess.setUnitInc(NewSGPRPressure = SGPRExcessLimit); 107d88c1a5aSDimitry Andric } 108d88c1a5aSDimitry Andric 109d88c1a5aSDimitry Andric // Register pressure is considered 'CRITICAL' if it is approaching a value 110d88c1a5aSDimitry Andric // that would reduce the wave occupancy for the execution unit. When 111d88c1a5aSDimitry Andric // register pressure is 'CRITICAL', increading SGPR and VGPR pressure both 112d88c1a5aSDimitry Andric // has the same cost, so we don't need to prefer one over the other. 113d88c1a5aSDimitry Andric 114d88c1a5aSDimitry Andric VGPRCriticalLimit -= ErrorMargin; 115d88c1a5aSDimitry Andric SGPRCriticalLimit -= ErrorMargin; 116d88c1a5aSDimitry Andric 117d88c1a5aSDimitry Andric int SGPRDelta = NewSGPRPressure - SGPRCriticalLimit; 118d88c1a5aSDimitry Andric int VGPRDelta = NewVGPRPressure - VGPRCriticalLimit; 119d88c1a5aSDimitry Andric 120d88c1a5aSDimitry Andric if (SGPRDelta >= 0 || VGPRDelta >= 0) { 121d88c1a5aSDimitry Andric if (SGPRDelta > VGPRDelta) { 122d88c1a5aSDimitry Andric Cand.RPDelta.CriticalMax = PressureChange(SRI->getSGPRPressureSet()); 123d88c1a5aSDimitry Andric Cand.RPDelta.CriticalMax.setUnitInc(SGPRDelta); 124d88c1a5aSDimitry Andric } else { 125d88c1a5aSDimitry Andric Cand.RPDelta.CriticalMax = PressureChange(SRI->getVGPRPressureSet()); 126d88c1a5aSDimitry Andric Cand.RPDelta.CriticalMax.setUnitInc(VGPRDelta); 127d88c1a5aSDimitry Andric } 128d88c1a5aSDimitry Andric } 129d88c1a5aSDimitry Andric } 130d88c1a5aSDimitry Andric 131d88c1a5aSDimitry Andric // This function is mostly cut and pasted from 132d88c1a5aSDimitry Andric // GenericScheduler::pickNodeFromQueue() 133d88c1a5aSDimitry Andric void GCNMaxOccupancySchedStrategy::pickNodeFromQueue(SchedBoundary &Zone, 134d88c1a5aSDimitry Andric const CandPolicy &ZonePolicy, 135d88c1a5aSDimitry Andric const RegPressureTracker &RPTracker, 136d88c1a5aSDimitry Andric SchedCandidate &Cand) { 137d88c1a5aSDimitry Andric const SISubtarget &ST = DAG->MF.getSubtarget<SISubtarget>(); 138d88c1a5aSDimitry Andric const SIRegisterInfo *SRI = static_cast<const SIRegisterInfo*>(TRI); 139d88c1a5aSDimitry Andric ArrayRef<unsigned> Pressure = RPTracker.getRegSetPressureAtPos(); 140d88c1a5aSDimitry Andric unsigned SGPRPressure = Pressure[SRI->getSGPRPressureSet()]; 141d88c1a5aSDimitry Andric unsigned VGPRPressure = Pressure[SRI->getVGPRPressureSet()]; 142d88c1a5aSDimitry Andric unsigned SGPRExcessLimit = 143d88c1a5aSDimitry Andric Context->RegClassInfo->getNumAllocatableRegs(&AMDGPU::SGPR_32RegClass); 144d88c1a5aSDimitry Andric unsigned VGPRExcessLimit = 145d88c1a5aSDimitry Andric Context->RegClassInfo->getNumAllocatableRegs(&AMDGPU::VGPR_32RegClass); 146d88c1a5aSDimitry Andric unsigned MaxWaves = getMaxWaves(SGPRPressure, VGPRPressure, DAG->MF); 147d88c1a5aSDimitry Andric unsigned SGPRCriticalLimit = SRI->getMaxNumSGPRs(ST, MaxWaves, true); 148d88c1a5aSDimitry Andric unsigned VGPRCriticalLimit = SRI->getMaxNumVGPRs(MaxWaves); 149d88c1a5aSDimitry Andric 150d88c1a5aSDimitry Andric ReadyQueue &Q = Zone.Available; 151d88c1a5aSDimitry Andric for (SUnit *SU : Q) { 152d88c1a5aSDimitry Andric 153d88c1a5aSDimitry Andric SchedCandidate TryCand(ZonePolicy); 154d88c1a5aSDimitry Andric initCandidate(TryCand, SU, Zone.isTop(), RPTracker, SRI, 155d88c1a5aSDimitry Andric SGPRPressure, VGPRPressure, 156d88c1a5aSDimitry Andric SGPRExcessLimit, VGPRExcessLimit, 157d88c1a5aSDimitry Andric SGPRCriticalLimit, VGPRCriticalLimit); 158d88c1a5aSDimitry Andric // Pass SchedBoundary only when comparing nodes from the same boundary. 159d88c1a5aSDimitry Andric SchedBoundary *ZoneArg = Cand.AtTop == TryCand.AtTop ? &Zone : nullptr; 160d88c1a5aSDimitry Andric GenericScheduler::tryCandidate(Cand, TryCand, ZoneArg); 161d88c1a5aSDimitry Andric if (TryCand.Reason != NoCand) { 162d88c1a5aSDimitry Andric // Initialize resource delta if needed in case future heuristics query it. 163d88c1a5aSDimitry Andric if (TryCand.ResDelta == SchedResourceDelta()) 164d88c1a5aSDimitry Andric TryCand.initResourceDelta(Zone.DAG, SchedModel); 165d88c1a5aSDimitry Andric Cand.setBest(TryCand); 166d88c1a5aSDimitry Andric } 167d88c1a5aSDimitry Andric } 168d88c1a5aSDimitry Andric } 169d88c1a5aSDimitry Andric 170d88c1a5aSDimitry Andric static int getBidirectionalReasonRank(GenericSchedulerBase::CandReason Reason) { 171d88c1a5aSDimitry Andric switch (Reason) { 172d88c1a5aSDimitry Andric default: 173d88c1a5aSDimitry Andric return Reason; 174d88c1a5aSDimitry Andric case GenericSchedulerBase::RegCritical: 175d88c1a5aSDimitry Andric case GenericSchedulerBase::RegExcess: 176d88c1a5aSDimitry Andric return -Reason; 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(BotCand); 228d88c1a5aSDimitry Andric dbgs() << "Bot Cand: "; 229d88c1a5aSDimitry Andric traceCandidate(TopCand); 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 { 252d88c1a5aSDimitry Andric int TopRank = getBidirectionalReasonRank(TopCand.Reason); 253d88c1a5aSDimitry Andric int BotRank = getBidirectionalReasonRank(BotCand.Reason); 254d88c1a5aSDimitry Andric if (TopRank > BotRank) { 255d88c1a5aSDimitry Andric Cand = TopCand; 256d88c1a5aSDimitry Andric } else { 257d88c1a5aSDimitry Andric Cand = BotCand; 258d88c1a5aSDimitry Andric } 259d88c1a5aSDimitry Andric } 260d88c1a5aSDimitry Andric } 261d88c1a5aSDimitry Andric DEBUG( 262d88c1a5aSDimitry Andric dbgs() << "Picking: "; 263d88c1a5aSDimitry Andric traceCandidate(Cand); 264d88c1a5aSDimitry Andric ); 265d88c1a5aSDimitry Andric 266d88c1a5aSDimitry Andric IsTopNode = Cand.AtTop; 267d88c1a5aSDimitry Andric return Cand.SU; 268d88c1a5aSDimitry Andric } 269d88c1a5aSDimitry Andric 270d88c1a5aSDimitry Andric // This function is mostly cut and pasted from 271d88c1a5aSDimitry Andric // GenericScheduler::pickNode() 272d88c1a5aSDimitry Andric SUnit *GCNMaxOccupancySchedStrategy::pickNode(bool &IsTopNode) { 273d88c1a5aSDimitry Andric if (DAG->top() == DAG->bottom()) { 274d88c1a5aSDimitry Andric assert(Top.Available.empty() && Top.Pending.empty() && 275d88c1a5aSDimitry Andric Bot.Available.empty() && Bot.Pending.empty() && "ReadyQ garbage"); 276d88c1a5aSDimitry Andric return nullptr; 277d88c1a5aSDimitry Andric } 278d88c1a5aSDimitry Andric SUnit *SU; 279d88c1a5aSDimitry Andric do { 280d88c1a5aSDimitry Andric if (RegionPolicy.OnlyTopDown) { 281d88c1a5aSDimitry Andric SU = Top.pickOnlyChoice(); 282d88c1a5aSDimitry Andric if (!SU) { 283d88c1a5aSDimitry Andric CandPolicy NoPolicy; 284d88c1a5aSDimitry Andric TopCand.reset(NoPolicy); 285d88c1a5aSDimitry Andric pickNodeFromQueue(Top, NoPolicy, DAG->getTopRPTracker(), TopCand); 286d88c1a5aSDimitry Andric assert(TopCand.Reason != NoCand && "failed to find a candidate"); 287d88c1a5aSDimitry Andric SU = TopCand.SU; 288d88c1a5aSDimitry Andric } 289d88c1a5aSDimitry Andric IsTopNode = true; 290d88c1a5aSDimitry Andric } else if (RegionPolicy.OnlyBottomUp) { 291d88c1a5aSDimitry Andric SU = Bot.pickOnlyChoice(); 292d88c1a5aSDimitry Andric if (!SU) { 293d88c1a5aSDimitry Andric CandPolicy NoPolicy; 294d88c1a5aSDimitry Andric BotCand.reset(NoPolicy); 295d88c1a5aSDimitry Andric pickNodeFromQueue(Bot, NoPolicy, DAG->getBotRPTracker(), BotCand); 296d88c1a5aSDimitry Andric assert(BotCand.Reason != NoCand && "failed to find a candidate"); 297d88c1a5aSDimitry Andric SU = BotCand.SU; 298d88c1a5aSDimitry Andric } 299d88c1a5aSDimitry Andric IsTopNode = false; 300d88c1a5aSDimitry Andric } else { 301d88c1a5aSDimitry Andric SU = pickNodeBidirectional(IsTopNode); 302d88c1a5aSDimitry Andric } 303d88c1a5aSDimitry Andric } while (SU->isScheduled); 304d88c1a5aSDimitry Andric 305d88c1a5aSDimitry Andric if (SU->isTopReady()) 306d88c1a5aSDimitry Andric Top.removeReady(SU); 307d88c1a5aSDimitry Andric if (SU->isBottomReady()) 308d88c1a5aSDimitry Andric Bot.removeReady(SU); 309d88c1a5aSDimitry Andric 310d88c1a5aSDimitry Andric DEBUG(dbgs() << "Scheduling SU(" << SU->NodeNum << ") " << *SU->getInstr()); 311d88c1a5aSDimitry Andric return SU; 312d88c1a5aSDimitry Andric } 313