1008 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1008 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- VLIWMachineScheduler.cpp - VLIW-Focused Scheduling Pass ------------===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| //
 | |
| // MachineScheduler schedules machine instructions after phi elimination. It
 | |
| // preserves LiveIntervals so it can be invoked before register allocation.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "llvm/CodeGen/VLIWMachineScheduler.h"
 | |
| #include "llvm/ADT/SmallVector.h"
 | |
| #include "llvm/CodeGen/DFAPacketizer.h"
 | |
| #include "llvm/CodeGen/MachineBasicBlock.h"
 | |
| #include "llvm/CodeGen/MachineFunction.h"
 | |
| #include "llvm/CodeGen/MachineInstr.h"
 | |
| #include "llvm/CodeGen/MachineLoopInfo.h"
 | |
| #include "llvm/CodeGen/RegisterClassInfo.h"
 | |
| #include "llvm/CodeGen/RegisterPressure.h"
 | |
| #include "llvm/CodeGen/ScheduleDAG.h"
 | |
| #include "llvm/CodeGen/ScheduleHazardRecognizer.h"
 | |
| #include "llvm/CodeGen/TargetInstrInfo.h"
 | |
| #include "llvm/CodeGen/TargetOpcodes.h"
 | |
| #include "llvm/CodeGen/TargetRegisterInfo.h"
 | |
| #include "llvm/CodeGen/TargetSchedule.h"
 | |
| #include "llvm/CodeGen/TargetSubtargetInfo.h"
 | |
| #include "llvm/Support/CommandLine.h"
 | |
| #include "llvm/Support/Debug.h"
 | |
| #include "llvm/Support/raw_ostream.h"
 | |
| #include <algorithm>
 | |
| #include <cassert>
 | |
| #include <iomanip>
 | |
| #include <limits>
 | |
| #include <memory>
 | |
| #include <sstream>
 | |
| 
 | |
| using namespace llvm;
 | |
| 
 | |
| #define DEBUG_TYPE "machine-scheduler"
 | |
| 
 | |
| static cl::opt<bool> IgnoreBBRegPressure("ignore-bb-reg-pressure", cl::Hidden,
 | |
|                                          cl::init(false));
 | |
| 
 | |
| static cl::opt<bool> UseNewerCandidate("use-newer-candidate", cl::Hidden,
 | |
|                                        cl::init(true));
 | |
| 
 | |
| static cl::opt<unsigned> SchedDebugVerboseLevel("misched-verbose-level",
 | |
|                                                 cl::Hidden, cl::init(1));
 | |
| 
 | |
| // Check if the scheduler should penalize instructions that are available to
 | |
| // early due to a zero-latency dependence.
 | |
| static cl::opt<bool> CheckEarlyAvail("check-early-avail", cl::Hidden,
 | |
|                                      cl::init(true));
 | |
| 
 | |
| // This value is used to determine if a register class is a high pressure set.
 | |
| // We compute the maximum number of registers needed and divided by the total
 | |
| // available. Then, we compare the result to this value.
 | |
| static cl::opt<float> RPThreshold("vliw-misched-reg-pressure", cl::Hidden,
 | |
|                                   cl::init(0.75f),
 | |
|                                   cl::desc("High register pressure threhold."));
 | |
| 
 | |
| VLIWResourceModel::VLIWResourceModel(const TargetSubtargetInfo &STI,
 | |
|                                      const TargetSchedModel *SM)
 | |
|     : TII(STI.getInstrInfo()), SchedModel(SM) {
 | |
|   ResourcesModel = createPacketizer(STI);
 | |
| 
 | |
|   // This hard requirement could be relaxed,
 | |
|   // but for now do not let it proceed.
 | |
|   assert(ResourcesModel && "Unimplemented CreateTargetScheduleState.");
 | |
| 
 | |
|   Packet.reserve(SchedModel->getIssueWidth());
 | |
|   Packet.clear();
 | |
|   ResourcesModel->clearResources();
 | |
| }
 | |
| 
 | |
| void VLIWResourceModel::reset() {
 | |
|   Packet.clear();
 | |
|   ResourcesModel->clearResources();
 | |
| }
 | |
| 
 | |
| VLIWResourceModel::~VLIWResourceModel() { delete ResourcesModel; }
 | |
| 
 | |
| /// Return true if there is a dependence between SUd and SUu.
 | |
| bool VLIWResourceModel::hasDependence(const SUnit *SUd, const SUnit *SUu) {
 | |
|   if (SUd->Succs.size() == 0)
 | |
|     return false;
 | |
| 
 | |
|   for (const auto &S : SUd->Succs) {
 | |
|     // Since we do not add pseudos to packets, might as well
 | |
|     // ignore order dependencies.
 | |
|     if (S.isCtrl())
 | |
|       continue;
 | |
| 
 | |
|     if (S.getSUnit() == SUu && S.getLatency() > 0)
 | |
|       return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| /// Check if scheduling of this SU is possible
 | |
| /// in the current packet.
 | |
| /// It is _not_ precise (statefull), it is more like
 | |
| /// another heuristic. Many corner cases are figured
 | |
| /// empirically.
 | |
| bool VLIWResourceModel::isResourceAvailable(SUnit *SU, bool IsTop) {
 | |
|   if (!SU || !SU->getInstr())
 | |
|     return false;
 | |
| 
 | |
|   // First see if the pipeline could receive this instruction
 | |
|   // in the current cycle.
 | |
|   switch (SU->getInstr()->getOpcode()) {
 | |
|   default:
 | |
|     if (!ResourcesModel->canReserveResources(*SU->getInstr()))
 | |
|       return false;
 | |
|     break;
 | |
|   case TargetOpcode::EXTRACT_SUBREG:
 | |
|   case TargetOpcode::INSERT_SUBREG:
 | |
|   case TargetOpcode::SUBREG_TO_REG:
 | |
|   case TargetOpcode::REG_SEQUENCE:
 | |
|   case TargetOpcode::IMPLICIT_DEF:
 | |
|   case TargetOpcode::COPY:
 | |
|   case TargetOpcode::INLINEASM:
 | |
|   case TargetOpcode::INLINEASM_BR:
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   // Now see if there are no other dependencies to instructions already
 | |
|   // in the packet.
 | |
|   if (IsTop) {
 | |
|     for (unsigned i = 0, e = Packet.size(); i != e; ++i)
 | |
|       if (hasDependence(Packet[i], SU))
 | |
|         return false;
 | |
|   } else {
 | |
|     for (unsigned i = 0, e = Packet.size(); i != e; ++i)
 | |
|       if (hasDependence(SU, Packet[i]))
 | |
|         return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /// Keep track of available resources.
 | |
| bool VLIWResourceModel::reserveResources(SUnit *SU, bool IsTop) {
 | |
|   bool startNewCycle = false;
 | |
|   // Artificially reset state.
 | |
|   if (!SU) {
 | |
|     reset();
 | |
|     TotalPackets++;
 | |
|     return false;
 | |
|   }
 | |
|   // If this SU does not fit in the packet or the packet is now full
 | |
|   // start a new one.
 | |
|   if (!isResourceAvailable(SU, IsTop) ||
 | |
|       Packet.size() >= SchedModel->getIssueWidth()) {
 | |
|     reset();
 | |
|     TotalPackets++;
 | |
|     startNewCycle = true;
 | |
|   }
 | |
| 
 | |
|   switch (SU->getInstr()->getOpcode()) {
 | |
|   default:
 | |
|     ResourcesModel->reserveResources(*SU->getInstr());
 | |
|     break;
 | |
|   case TargetOpcode::EXTRACT_SUBREG:
 | |
|   case TargetOpcode::INSERT_SUBREG:
 | |
|   case TargetOpcode::SUBREG_TO_REG:
 | |
|   case TargetOpcode::REG_SEQUENCE:
 | |
|   case TargetOpcode::IMPLICIT_DEF:
 | |
|   case TargetOpcode::KILL:
 | |
|   case TargetOpcode::CFI_INSTRUCTION:
 | |
|   case TargetOpcode::EH_LABEL:
 | |
|   case TargetOpcode::COPY:
 | |
|   case TargetOpcode::INLINEASM:
 | |
|   case TargetOpcode::INLINEASM_BR:
 | |
|     break;
 | |
|   }
 | |
|   Packet.push_back(SU);
 | |
| 
 | |
| #ifndef NDEBUG
 | |
|   LLVM_DEBUG(dbgs() << "Packet[" << TotalPackets << "]:\n");
 | |
|   for (unsigned i = 0, e = Packet.size(); i != e; ++i) {
 | |
|     LLVM_DEBUG(dbgs() << "\t[" << i << "] SU(");
 | |
|     LLVM_DEBUG(dbgs() << Packet[i]->NodeNum << ")\t");
 | |
|     LLVM_DEBUG(Packet[i]->getInstr()->dump());
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   return startNewCycle;
 | |
| }
 | |
| 
 | |
| DFAPacketizer *
 | |
| VLIWResourceModel::createPacketizer(const TargetSubtargetInfo &STI) const {
 | |
|   return STI.getInstrInfo()->CreateTargetScheduleState(STI);
 | |
| }
 | |
| 
 | |
| /// schedule - Called back from MachineScheduler::runOnMachineFunction
 | |
| /// after setting up the current scheduling region. [RegionBegin, RegionEnd)
 | |
| /// only includes instructions that have DAG nodes, not scheduling boundaries.
 | |
| void VLIWMachineScheduler::schedule() {
 | |
|   LLVM_DEBUG(dbgs() << "********** MI Converging Scheduling VLIW "
 | |
|                     << printMBBReference(*BB) << " " << BB->getName()
 | |
|                     << " in_func " << BB->getParent()->getName()
 | |
|                     << " at loop depth " << MLI->getLoopDepth(BB) << " \n");
 | |
| 
 | |
|   buildDAGWithRegPressure();
 | |
| 
 | |
|   Topo.InitDAGTopologicalSorting();
 | |
| 
 | |
|   // Postprocess the DAG to add platform-specific artificial dependencies.
 | |
|   postprocessDAG();
 | |
| 
 | |
|   SmallVector<SUnit *, 8> TopRoots, BotRoots;
 | |
|   findRootsAndBiasEdges(TopRoots, BotRoots);
 | |
| 
 | |
|   // Initialize the strategy before modifying the DAG.
 | |
|   SchedImpl->initialize(this);
 | |
| 
 | |
|   LLVM_DEBUG({
 | |
|     unsigned maxH = 0;
 | |
|     for (const SUnit &SU : SUnits)
 | |
|       if (SU.getHeight() > maxH)
 | |
|         maxH = SU.getHeight();
 | |
|     dbgs() << "Max Height " << maxH << "\n";
 | |
|   });
 | |
|   LLVM_DEBUG({
 | |
|     unsigned maxD = 0;
 | |
|     for (const SUnit &SU : SUnits)
 | |
|       if (SU.getDepth() > maxD)
 | |
|         maxD = SU.getDepth();
 | |
|     dbgs() << "Max Depth " << maxD << "\n";
 | |
|   });
 | |
|   LLVM_DEBUG(dump());
 | |
|   if (ViewMISchedDAGs)
 | |
|     viewGraph();
 | |
| 
 | |
|   initQueues(TopRoots, BotRoots);
 | |
| 
 | |
|   bool IsTopNode = false;
 | |
|   while (true) {
 | |
|     LLVM_DEBUG(
 | |
|         dbgs() << "** VLIWMachineScheduler::schedule picking next node\n");
 | |
|     SUnit *SU = SchedImpl->pickNode(IsTopNode);
 | |
|     if (!SU)
 | |
|       break;
 | |
| 
 | |
|     if (!checkSchedLimit())
 | |
|       break;
 | |
| 
 | |
|     scheduleMI(SU, IsTopNode);
 | |
| 
 | |
|     // Notify the scheduling strategy after updating the DAG.
 | |
|     SchedImpl->schedNode(SU, IsTopNode);
 | |
| 
 | |
|     updateQueues(SU, IsTopNode);
 | |
|   }
 | |
|   assert(CurrentTop == CurrentBottom && "Nonempty unscheduled zone.");
 | |
| 
 | |
|   placeDebugValues();
 | |
| 
 | |
|   LLVM_DEBUG({
 | |
|     dbgs() << "*** Final schedule for "
 | |
|            << printMBBReference(*begin()->getParent()) << " ***\n";
 | |
|     dumpSchedule();
 | |
|     dbgs() << '\n';
 | |
|   });
 | |
| }
 | |
| 
 | |
| void ConvergingVLIWScheduler::initialize(ScheduleDAGMI *dag) {
 | |
|   DAG = static_cast<VLIWMachineScheduler *>(dag);
 | |
|   SchedModel = DAG->getSchedModel();
 | |
| 
 | |
|   Top.init(DAG, SchedModel);
 | |
|   Bot.init(DAG, SchedModel);
 | |
| 
 | |
|   // Initialize the HazardRecognizers. If itineraries don't exist, are empty, or
 | |
|   // are disabled, then these HazardRecs will be disabled.
 | |
|   const InstrItineraryData *Itin = DAG->getSchedModel()->getInstrItineraries();
 | |
|   const TargetSubtargetInfo &STI = DAG->MF.getSubtarget();
 | |
|   const TargetInstrInfo *TII = STI.getInstrInfo();
 | |
|   delete Top.HazardRec;
 | |
|   delete Bot.HazardRec;
 | |
|   Top.HazardRec = TII->CreateTargetMIHazardRecognizer(Itin, DAG);
 | |
|   Bot.HazardRec = TII->CreateTargetMIHazardRecognizer(Itin, DAG);
 | |
| 
 | |
|   delete Top.ResourceModel;
 | |
|   delete Bot.ResourceModel;
 | |
|   Top.ResourceModel = createVLIWResourceModel(STI, DAG->getSchedModel());
 | |
|   Bot.ResourceModel = createVLIWResourceModel(STI, DAG->getSchedModel());
 | |
| 
 | |
|   const std::vector<unsigned> &MaxPressure =
 | |
|       DAG->getRegPressure().MaxSetPressure;
 | |
|   HighPressureSets.assign(MaxPressure.size(), false);
 | |
|   for (unsigned i = 0, e = MaxPressure.size(); i < e; ++i) {
 | |
|     unsigned Limit = DAG->getRegClassInfo()->getRegPressureSetLimit(i);
 | |
|     HighPressureSets[i] =
 | |
|         ((float)MaxPressure[i] > ((float)Limit * RPThreshold));
 | |
|   }
 | |
| 
 | |
|   assert((!ForceTopDown || !ForceBottomUp) &&
 | |
|          "-misched-topdown incompatible with -misched-bottomup");
 | |
| }
 | |
| 
 | |
| VLIWResourceModel *ConvergingVLIWScheduler::createVLIWResourceModel(
 | |
|     const TargetSubtargetInfo &STI, const TargetSchedModel *SchedModel) const {
 | |
|   return new VLIWResourceModel(STI, SchedModel);
 | |
| }
 | |
| 
 | |
| void ConvergingVLIWScheduler::releaseTopNode(SUnit *SU) {
 | |
|   for (const SDep &PI : SU->Preds) {
 | |
|     unsigned PredReadyCycle = PI.getSUnit()->TopReadyCycle;
 | |
|     unsigned MinLatency = PI.getLatency();
 | |
| #ifndef NDEBUG
 | |
|     Top.MaxMinLatency = std::max(MinLatency, Top.MaxMinLatency);
 | |
| #endif
 | |
|     if (SU->TopReadyCycle < PredReadyCycle + MinLatency)
 | |
|       SU->TopReadyCycle = PredReadyCycle + MinLatency;
 | |
|   }
 | |
| 
 | |
|   if (!SU->isScheduled)
 | |
|     Top.releaseNode(SU, SU->TopReadyCycle);
 | |
| }
 | |
| 
 | |
| void ConvergingVLIWScheduler::releaseBottomNode(SUnit *SU) {
 | |
|   assert(SU->getInstr() && "Scheduled SUnit must have instr");
 | |
| 
 | |
|   for (SUnit::succ_iterator I = SU->Succs.begin(), E = SU->Succs.end(); I != E;
 | |
|        ++I) {
 | |
|     unsigned SuccReadyCycle = I->getSUnit()->BotReadyCycle;
 | |
|     unsigned MinLatency = I->getLatency();
 | |
| #ifndef NDEBUG
 | |
|     Bot.MaxMinLatency = std::max(MinLatency, Bot.MaxMinLatency);
 | |
| #endif
 | |
|     if (SU->BotReadyCycle < SuccReadyCycle + MinLatency)
 | |
|       SU->BotReadyCycle = SuccReadyCycle + MinLatency;
 | |
|   }
 | |
| 
 | |
|   if (!SU->isScheduled)
 | |
|     Bot.releaseNode(SU, SU->BotReadyCycle);
 | |
| }
 | |
| 
 | |
| ConvergingVLIWScheduler::VLIWSchedBoundary::~VLIWSchedBoundary() {
 | |
|   delete ResourceModel;
 | |
|   delete HazardRec;
 | |
| }
 | |
| 
 | |
| /// Does this SU have a hazard within the current instruction group.
 | |
| ///
 | |
| /// The scheduler supports two modes of hazard recognition. The first is the
 | |
| /// ScheduleHazardRecognizer API. It is a fully general hazard recognizer that
 | |
| /// supports highly complicated in-order reservation tables
 | |
| /// (ScoreboardHazardRecognizer) and arbitrary target-specific logic.
 | |
| ///
 | |
| /// The second is a streamlined mechanism that checks for hazards based on
 | |
| /// simple counters that the scheduler itself maintains. It explicitly checks
 | |
| /// for instruction dispatch limitations, including the number of micro-ops that
 | |
| /// can dispatch per cycle.
 | |
| ///
 | |
| /// TODO: Also check whether the SU must start a new group.
 | |
| bool ConvergingVLIWScheduler::VLIWSchedBoundary::checkHazard(SUnit *SU) {
 | |
|   if (HazardRec->isEnabled())
 | |
|     return HazardRec->getHazardType(SU) != ScheduleHazardRecognizer::NoHazard;
 | |
| 
 | |
|   unsigned uops = SchedModel->getNumMicroOps(SU->getInstr());
 | |
|   if (IssueCount + uops > SchedModel->getIssueWidth())
 | |
|     return true;
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void ConvergingVLIWScheduler::VLIWSchedBoundary::releaseNode(
 | |
|     SUnit *SU, unsigned ReadyCycle) {
 | |
|   if (ReadyCycle < MinReadyCycle)
 | |
|     MinReadyCycle = ReadyCycle;
 | |
| 
 | |
|   // Check for interlocks first. For the purpose of other heuristics, an
 | |
|   // instruction that cannot issue appears as if it's not in the ReadyQueue.
 | |
|   if (ReadyCycle > CurrCycle || checkHazard(SU))
 | |
| 
 | |
|     Pending.push(SU);
 | |
|   else
 | |
|     Available.push(SU);
 | |
| }
 | |
| 
 | |
| /// Move the boundary of scheduled code by one cycle.
 | |
| void ConvergingVLIWScheduler::VLIWSchedBoundary::bumpCycle() {
 | |
|   unsigned Width = SchedModel->getIssueWidth();
 | |
|   IssueCount = (IssueCount <= Width) ? 0 : IssueCount - Width;
 | |
| 
 | |
|   assert(MinReadyCycle < std::numeric_limits<unsigned>::max() &&
 | |
|          "MinReadyCycle uninitialized");
 | |
|   unsigned NextCycle = std::max(CurrCycle + 1, MinReadyCycle);
 | |
| 
 | |
|   if (!HazardRec->isEnabled()) {
 | |
|     // Bypass HazardRec virtual calls.
 | |
|     CurrCycle = NextCycle;
 | |
|   } else {
 | |
|     // Bypass getHazardType calls in case of long latency.
 | |
|     for (; CurrCycle != NextCycle; ++CurrCycle) {
 | |
|       if (isTop())
 | |
|         HazardRec->AdvanceCycle();
 | |
|       else
 | |
|         HazardRec->RecedeCycle();
 | |
|     }
 | |
|   }
 | |
|   CheckPending = true;
 | |
| 
 | |
|   LLVM_DEBUG(dbgs() << "*** Next cycle " << Available.getName() << " cycle "
 | |
|                     << CurrCycle << '\n');
 | |
| }
 | |
| 
 | |
| /// Move the boundary of scheduled code by one SUnit.
 | |
| void ConvergingVLIWScheduler::VLIWSchedBoundary::bumpNode(SUnit *SU) {
 | |
|   bool startNewCycle = false;
 | |
| 
 | |
|   // Update the reservation table.
 | |
|   if (HazardRec->isEnabled()) {
 | |
|     if (!isTop() && SU->isCall) {
 | |
|       // Calls are scheduled with their preceding instructions. For bottom-up
 | |
|       // scheduling, clear the pipeline state before emitting.
 | |
|       HazardRec->Reset();
 | |
|     }
 | |
|     HazardRec->EmitInstruction(SU);
 | |
|   }
 | |
| 
 | |
|   // Update DFA model.
 | |
|   startNewCycle = ResourceModel->reserveResources(SU, isTop());
 | |
| 
 | |
|   // Check the instruction group dispatch limit.
 | |
|   // TODO: Check if this SU must end a dispatch group.
 | |
|   IssueCount += SchedModel->getNumMicroOps(SU->getInstr());
 | |
|   if (startNewCycle) {
 | |
|     LLVM_DEBUG(dbgs() << "*** Max instrs at cycle " << CurrCycle << '\n');
 | |
|     bumpCycle();
 | |
|   } else
 | |
|     LLVM_DEBUG(dbgs() << "*** IssueCount " << IssueCount << " at cycle "
 | |
|                       << CurrCycle << '\n');
 | |
| }
 | |
| 
 | |
| /// Release pending ready nodes in to the available queue. This makes them
 | |
| /// visible to heuristics.
 | |
| void ConvergingVLIWScheduler::VLIWSchedBoundary::releasePending() {
 | |
|   // If the available queue is empty, it is safe to reset MinReadyCycle.
 | |
|   if (Available.empty())
 | |
|     MinReadyCycle = std::numeric_limits<unsigned>::max();
 | |
| 
 | |
|   // Check to see if any of the pending instructions are ready to issue.  If
 | |
|   // so, add them to the available queue.
 | |
|   for (unsigned i = 0, e = Pending.size(); i != e; ++i) {
 | |
|     SUnit *SU = *(Pending.begin() + i);
 | |
|     unsigned ReadyCycle = isTop() ? SU->TopReadyCycle : SU->BotReadyCycle;
 | |
| 
 | |
|     if (ReadyCycle < MinReadyCycle)
 | |
|       MinReadyCycle = ReadyCycle;
 | |
| 
 | |
|     if (ReadyCycle > CurrCycle)
 | |
|       continue;
 | |
| 
 | |
|     if (checkHazard(SU))
 | |
|       continue;
 | |
| 
 | |
|     Available.push(SU);
 | |
|     Pending.remove(Pending.begin() + i);
 | |
|     --i;
 | |
|     --e;
 | |
|   }
 | |
|   CheckPending = false;
 | |
| }
 | |
| 
 | |
| /// Remove SU from the ready set for this boundary.
 | |
| void ConvergingVLIWScheduler::VLIWSchedBoundary::removeReady(SUnit *SU) {
 | |
|   if (Available.isInQueue(SU))
 | |
|     Available.remove(Available.find(SU));
 | |
|   else {
 | |
|     assert(Pending.isInQueue(SU) && "bad ready count");
 | |
|     Pending.remove(Pending.find(SU));
 | |
|   }
 | |
| }
 | |
| 
 | |
| /// If this queue only has one ready candidate, return it. As a side effect,
 | |
| /// advance the cycle until at least one node is ready. If multiple instructions
 | |
| /// are ready, return NULL.
 | |
| SUnit *ConvergingVLIWScheduler::VLIWSchedBoundary::pickOnlyChoice() {
 | |
|   if (CheckPending)
 | |
|     releasePending();
 | |
| 
 | |
|   auto AdvanceCycle = [this]() {
 | |
|     if (Available.empty())
 | |
|       return true;
 | |
|     if (Available.size() == 1 && Pending.size() > 0)
 | |
|       return !ResourceModel->isResourceAvailable(*Available.begin(), isTop()) ||
 | |
|              getWeakLeft(*Available.begin(), isTop()) != 0;
 | |
|     return false;
 | |
|   };
 | |
|   for (unsigned i = 0; AdvanceCycle(); ++i) {
 | |
|     assert(i <= (HazardRec->getMaxLookAhead() + MaxMinLatency) &&
 | |
|            "permanent hazard");
 | |
|     (void)i;
 | |
|     ResourceModel->reserveResources(nullptr, isTop());
 | |
|     bumpCycle();
 | |
|     releasePending();
 | |
|   }
 | |
|   if (Available.size() == 1)
 | |
|     return *Available.begin();
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| #ifndef NDEBUG
 | |
| void ConvergingVLIWScheduler::traceCandidate(const char *Label,
 | |
|                                              const ReadyQueue &Q, SUnit *SU,
 | |
|                                              int Cost, PressureChange P) {
 | |
|   dbgs() << Label << " " << Q.getName() << " ";
 | |
|   if (P.isValid())
 | |
|     dbgs() << DAG->TRI->getRegPressureSetName(P.getPSet()) << ":"
 | |
|            << P.getUnitInc() << " ";
 | |
|   else
 | |
|     dbgs() << "     ";
 | |
|   dbgs() << "cost(" << Cost << ")\t";
 | |
|   DAG->dumpNode(*SU);
 | |
| }
 | |
| 
 | |
| // Very detailed queue dump, to be used with higher verbosity levels.
 | |
| void ConvergingVLIWScheduler::readyQueueVerboseDump(
 | |
|     const RegPressureTracker &RPTracker, SchedCandidate &Candidate,
 | |
|     ReadyQueue &Q) {
 | |
|   RegPressureTracker &TempTracker = const_cast<RegPressureTracker &>(RPTracker);
 | |
| 
 | |
|   dbgs() << ">>> " << Q.getName() << "\n";
 | |
|   for (ReadyQueue::iterator I = Q.begin(), E = Q.end(); I != E; ++I) {
 | |
|     RegPressureDelta RPDelta;
 | |
|     TempTracker.getMaxPressureDelta((*I)->getInstr(), RPDelta,
 | |
|                                     DAG->getRegionCriticalPSets(),
 | |
|                                     DAG->getRegPressure().MaxSetPressure);
 | |
|     std::stringstream dbgstr;
 | |
|     dbgstr << "SU(" << std::setw(3) << (*I)->NodeNum << ")";
 | |
|     dbgs() << dbgstr.str();
 | |
|     SchedulingCost(Q, *I, Candidate, RPDelta, true);
 | |
|     dbgs() << "\t";
 | |
|     (*I)->getInstr()->dump();
 | |
|   }
 | |
|   dbgs() << "\n";
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /// isSingleUnscheduledPred - If SU2 is the only unscheduled predecessor
 | |
| /// of SU, return true (we may have duplicates)
 | |
| static inline bool isSingleUnscheduledPred(SUnit *SU, SUnit *SU2) {
 | |
|   if (SU->NumPredsLeft == 0)
 | |
|     return false;
 | |
| 
 | |
|   for (auto &Pred : SU->Preds) {
 | |
|     // We found an available, but not scheduled, predecessor.
 | |
|     if (!Pred.getSUnit()->isScheduled && (Pred.getSUnit() != SU2))
 | |
|       return false;
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /// isSingleUnscheduledSucc - If SU2 is the only unscheduled successor
 | |
| /// of SU, return true (we may have duplicates)
 | |
| static inline bool isSingleUnscheduledSucc(SUnit *SU, SUnit *SU2) {
 | |
|   if (SU->NumSuccsLeft == 0)
 | |
|     return false;
 | |
| 
 | |
|   for (auto &Succ : SU->Succs) {
 | |
|     // We found an available, but not scheduled, successor.
 | |
|     if (!Succ.getSUnit()->isScheduled && (Succ.getSUnit() != SU2))
 | |
|       return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /// Check if the instruction changes the register pressure of a register in the
 | |
| /// high pressure set. The function returns a negative value if the pressure
 | |
| /// decreases and a positive value is the pressure increases. If the instruction
 | |
| /// doesn't use a high pressure register or doesn't change the register
 | |
| /// pressure, then return 0.
 | |
| int ConvergingVLIWScheduler::pressureChange(const SUnit *SU, bool isBotUp) {
 | |
|   PressureDiff &PD = DAG->getPressureDiff(SU);
 | |
|   for (const auto &P : PD) {
 | |
|     if (!P.isValid())
 | |
|       continue;
 | |
|     // The pressure differences are computed bottom-up, so the comparison for
 | |
|     // an increase is positive in the bottom direction, but negative in the
 | |
|     //  top-down direction.
 | |
|     if (HighPressureSets[P.getPSet()])
 | |
|       return (isBotUp ? P.getUnitInc() : -P.getUnitInc());
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /// Single point to compute overall scheduling cost.
 | |
| /// TODO: More heuristics will be used soon.
 | |
| int ConvergingVLIWScheduler::SchedulingCost(ReadyQueue &Q, SUnit *SU,
 | |
|                                             SchedCandidate &Candidate,
 | |
|                                             RegPressureDelta &Delta,
 | |
|                                             bool verbose) {
 | |
|   // Initial trivial priority.
 | |
|   int ResCount = 1;
 | |
| 
 | |
|   // Do not waste time on a node that is already scheduled.
 | |
|   if (!SU || SU->isScheduled)
 | |
|     return ResCount;
 | |
| 
 | |
|   LLVM_DEBUG(if (verbose) dbgs()
 | |
|              << ((Q.getID() == TopQID) ? "(top|" : "(bot|"));
 | |
|   // Forced priority is high.
 | |
|   if (SU->isScheduleHigh) {
 | |
|     ResCount += PriorityOne;
 | |
|     LLVM_DEBUG(dbgs() << "H|");
 | |
|   }
 | |
| 
 | |
|   unsigned IsAvailableAmt = 0;
 | |
|   // Critical path first.
 | |
|   if (Q.getID() == TopQID) {
 | |
|     if (Top.isLatencyBound(SU)) {
 | |
|       LLVM_DEBUG(if (verbose) dbgs() << "LB|");
 | |
|       ResCount += (SU->getHeight() * ScaleTwo);
 | |
|     }
 | |
| 
 | |
|     LLVM_DEBUG(if (verbose) {
 | |
|       std::stringstream dbgstr;
 | |
|       dbgstr << "h" << std::setw(3) << SU->getHeight() << "|";
 | |
|       dbgs() << dbgstr.str();
 | |
|     });
 | |
| 
 | |
|     // If resources are available for it, multiply the
 | |
|     // chance of scheduling.
 | |
|     if (Top.ResourceModel->isResourceAvailable(SU, true)) {
 | |
|       IsAvailableAmt = (PriorityTwo + PriorityThree);
 | |
|       ResCount += IsAvailableAmt;
 | |
|       LLVM_DEBUG(if (verbose) dbgs() << "A|");
 | |
|     } else
 | |
|       LLVM_DEBUG(if (verbose) dbgs() << " |");
 | |
|   } else {
 | |
|     if (Bot.isLatencyBound(SU)) {
 | |
|       LLVM_DEBUG(if (verbose) dbgs() << "LB|");
 | |
|       ResCount += (SU->getDepth() * ScaleTwo);
 | |
|     }
 | |
| 
 | |
|     LLVM_DEBUG(if (verbose) {
 | |
|       std::stringstream dbgstr;
 | |
|       dbgstr << "d" << std::setw(3) << SU->getDepth() << "|";
 | |
|       dbgs() << dbgstr.str();
 | |
|     });
 | |
| 
 | |
|     // If resources are available for it, multiply the
 | |
|     // chance of scheduling.
 | |
|     if (Bot.ResourceModel->isResourceAvailable(SU, false)) {
 | |
|       IsAvailableAmt = (PriorityTwo + PriorityThree);
 | |
|       ResCount += IsAvailableAmt;
 | |
|       LLVM_DEBUG(if (verbose) dbgs() << "A|");
 | |
|     } else
 | |
|       LLVM_DEBUG(if (verbose) dbgs() << " |");
 | |
|   }
 | |
| 
 | |
|   unsigned NumNodesBlocking = 0;
 | |
|   if (Q.getID() == TopQID) {
 | |
|     // How many SUs does it block from scheduling?
 | |
|     // Look at all of the successors of this node.
 | |
|     // Count the number of nodes that
 | |
|     // this node is the sole unscheduled node for.
 | |
|     if (Top.isLatencyBound(SU))
 | |
|       for (const SDep &SI : SU->Succs)
 | |
|         if (isSingleUnscheduledPred(SI.getSUnit(), SU))
 | |
|           ++NumNodesBlocking;
 | |
|   } else {
 | |
|     // How many unscheduled predecessors block this node?
 | |
|     if (Bot.isLatencyBound(SU))
 | |
|       for (const SDep &PI : SU->Preds)
 | |
|         if (isSingleUnscheduledSucc(PI.getSUnit(), SU))
 | |
|           ++NumNodesBlocking;
 | |
|   }
 | |
|   ResCount += (NumNodesBlocking * ScaleTwo);
 | |
| 
 | |
|   LLVM_DEBUG(if (verbose) {
 | |
|     std::stringstream dbgstr;
 | |
|     dbgstr << "blk " << std::setw(2) << NumNodesBlocking << ")|";
 | |
|     dbgs() << dbgstr.str();
 | |
|   });
 | |
| 
 | |
|   // Factor in reg pressure as a heuristic.
 | |
|   if (!IgnoreBBRegPressure) {
 | |
|     // Decrease priority by the amount that register pressure exceeds the limit.
 | |
|     ResCount -= (Delta.Excess.getUnitInc() * PriorityOne);
 | |
|     // Decrease priority if register pressure exceeds the limit.
 | |
|     ResCount -= (Delta.CriticalMax.getUnitInc() * PriorityOne);
 | |
|     // Decrease priority slightly if register pressure would increase over the
 | |
|     // current maximum.
 | |
|     ResCount -= (Delta.CurrentMax.getUnitInc() * PriorityTwo);
 | |
|     // If there are register pressure issues, then we remove the value added for
 | |
|     // the instruction being available. The rationale is that we really don't
 | |
|     // want to schedule an instruction that causes a spill.
 | |
|     if (IsAvailableAmt && pressureChange(SU, Q.getID() != TopQID) > 0 &&
 | |
|         (Delta.Excess.getUnitInc() || Delta.CriticalMax.getUnitInc() ||
 | |
|          Delta.CurrentMax.getUnitInc()))
 | |
|       ResCount -= IsAvailableAmt;
 | |
|     LLVM_DEBUG(if (verbose) {
 | |
|       dbgs() << "RP " << Delta.Excess.getUnitInc() << "/"
 | |
|              << Delta.CriticalMax.getUnitInc() << "/"
 | |
|              << Delta.CurrentMax.getUnitInc() << ")|";
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   // Give preference to a zero latency instruction if the dependent
 | |
|   // instruction is in the current packet.
 | |
|   if (Q.getID() == TopQID && getWeakLeft(SU, true) == 0) {
 | |
|     for (const SDep &PI : SU->Preds) {
 | |
|       if (!PI.getSUnit()->getInstr()->isPseudo() && PI.isAssignedRegDep() &&
 | |
|           PI.getLatency() == 0 &&
 | |
|           Top.ResourceModel->isInPacket(PI.getSUnit())) {
 | |
|         ResCount += PriorityThree;
 | |
|         LLVM_DEBUG(if (verbose) dbgs() << "Z|");
 | |
|       }
 | |
|     }
 | |
|   } else if (Q.getID() == BotQID && getWeakLeft(SU, false) == 0) {
 | |
|     for (const SDep &SI : SU->Succs) {
 | |
|       if (!SI.getSUnit()->getInstr()->isPseudo() && SI.isAssignedRegDep() &&
 | |
|           SI.getLatency() == 0 &&
 | |
|           Bot.ResourceModel->isInPacket(SI.getSUnit())) {
 | |
|         ResCount += PriorityThree;
 | |
|         LLVM_DEBUG(if (verbose) dbgs() << "Z|");
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // If the instruction has a non-zero latency dependence with an instruction in
 | |
|   // the current packet, then it should not be scheduled yet. The case occurs
 | |
|   // when the dependent instruction is scheduled in a new packet, so the
 | |
|   // scheduler updates the current cycle and pending instructions become
 | |
|   // available.
 | |
|   if (CheckEarlyAvail) {
 | |
|     if (Q.getID() == TopQID) {
 | |
|       for (const auto &PI : SU->Preds) {
 | |
|         if (PI.getLatency() > 0 &&
 | |
|             Top.ResourceModel->isInPacket(PI.getSUnit())) {
 | |
|           ResCount -= PriorityOne;
 | |
|           LLVM_DEBUG(if (verbose) dbgs() << "D|");
 | |
|         }
 | |
|       }
 | |
|     } else {
 | |
|       for (const auto &SI : SU->Succs) {
 | |
|         if (SI.getLatency() > 0 &&
 | |
|             Bot.ResourceModel->isInPacket(SI.getSUnit())) {
 | |
|           ResCount -= PriorityOne;
 | |
|           LLVM_DEBUG(if (verbose) dbgs() << "D|");
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   LLVM_DEBUG(if (verbose) {
 | |
|     std::stringstream dbgstr;
 | |
|     dbgstr << "Total " << std::setw(4) << ResCount << ")";
 | |
|     dbgs() << dbgstr.str();
 | |
|   });
 | |
| 
 | |
|   return ResCount;
 | |
| }
 | |
| 
 | |
| /// Pick the best candidate from the top queue.
 | |
| ///
 | |
| /// TODO: getMaxPressureDelta results can be mostly cached for each SUnit during
 | |
| /// DAG building. To adjust for the current scheduling location we need to
 | |
| /// maintain the number of vreg uses remaining to be top-scheduled.
 | |
| ConvergingVLIWScheduler::CandResult
 | |
| ConvergingVLIWScheduler::pickNodeFromQueue(VLIWSchedBoundary &Zone,
 | |
|                                            const RegPressureTracker &RPTracker,
 | |
|                                            SchedCandidate &Candidate) {
 | |
|   ReadyQueue &Q = Zone.Available;
 | |
|   LLVM_DEBUG(if (SchedDebugVerboseLevel > 1)
 | |
|                  readyQueueVerboseDump(RPTracker, Candidate, Q);
 | |
|              else Q.dump(););
 | |
| 
 | |
|   // getMaxPressureDelta temporarily modifies the tracker.
 | |
|   RegPressureTracker &TempTracker = const_cast<RegPressureTracker &>(RPTracker);
 | |
| 
 | |
|   // BestSU remains NULL if no top candidates beat the best existing candidate.
 | |
|   CandResult FoundCandidate = NoCand;
 | |
|   for (ReadyQueue::iterator I = Q.begin(), E = Q.end(); I != E; ++I) {
 | |
|     RegPressureDelta RPDelta;
 | |
|     TempTracker.getMaxPressureDelta((*I)->getInstr(), RPDelta,
 | |
|                                     DAG->getRegionCriticalPSets(),
 | |
|                                     DAG->getRegPressure().MaxSetPressure);
 | |
| 
 | |
|     int CurrentCost = SchedulingCost(Q, *I, Candidate, RPDelta, false);
 | |
| 
 | |
|     // Initialize the candidate if needed.
 | |
|     if (!Candidate.SU) {
 | |
|       LLVM_DEBUG(traceCandidate("DCAND", Q, *I, CurrentCost));
 | |
|       Candidate.SU = *I;
 | |
|       Candidate.RPDelta = RPDelta;
 | |
|       Candidate.SCost = CurrentCost;
 | |
|       FoundCandidate = NodeOrder;
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     // Choose node order for negative cost candidates. There is no good
 | |
|     // candidate in this case.
 | |
|     if (CurrentCost < 0 && Candidate.SCost < 0) {
 | |
|       if ((Q.getID() == TopQID && (*I)->NodeNum < Candidate.SU->NodeNum) ||
 | |
|           (Q.getID() == BotQID && (*I)->NodeNum > Candidate.SU->NodeNum)) {
 | |
|         LLVM_DEBUG(traceCandidate("NCAND", Q, *I, CurrentCost));
 | |
|         Candidate.SU = *I;
 | |
|         Candidate.RPDelta = RPDelta;
 | |
|         Candidate.SCost = CurrentCost;
 | |
|         FoundCandidate = NodeOrder;
 | |
|       }
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     // Best cost.
 | |
|     if (CurrentCost > Candidate.SCost) {
 | |
|       LLVM_DEBUG(traceCandidate("CCAND", Q, *I, CurrentCost));
 | |
|       Candidate.SU = *I;
 | |
|       Candidate.RPDelta = RPDelta;
 | |
|       Candidate.SCost = CurrentCost;
 | |
|       FoundCandidate = BestCost;
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     // Choose an instruction that does not depend on an artificial edge.
 | |
|     unsigned CurrWeak = getWeakLeft(*I, (Q.getID() == TopQID));
 | |
|     unsigned CandWeak = getWeakLeft(Candidate.SU, (Q.getID() == TopQID));
 | |
|     if (CurrWeak != CandWeak) {
 | |
|       if (CurrWeak < CandWeak) {
 | |
|         LLVM_DEBUG(traceCandidate("WCAND", Q, *I, CurrentCost));
 | |
|         Candidate.SU = *I;
 | |
|         Candidate.RPDelta = RPDelta;
 | |
|         Candidate.SCost = CurrentCost;
 | |
|         FoundCandidate = Weak;
 | |
|       }
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     if (CurrentCost == Candidate.SCost && Zone.isLatencyBound(*I)) {
 | |
|       unsigned CurrSize, CandSize;
 | |
|       if (Q.getID() == TopQID) {
 | |
|         CurrSize = (*I)->Succs.size();
 | |
|         CandSize = Candidate.SU->Succs.size();
 | |
|       } else {
 | |
|         CurrSize = (*I)->Preds.size();
 | |
|         CandSize = Candidate.SU->Preds.size();
 | |
|       }
 | |
|       if (CurrSize > CandSize) {
 | |
|         LLVM_DEBUG(traceCandidate("SPCAND", Q, *I, CurrentCost));
 | |
|         Candidate.SU = *I;
 | |
|         Candidate.RPDelta = RPDelta;
 | |
|         Candidate.SCost = CurrentCost;
 | |
|         FoundCandidate = BestCost;
 | |
|       }
 | |
|       // Keep the old candidate if it's a better candidate. That is, don't use
 | |
|       // the subsequent tie breaker.
 | |
|       if (CurrSize != CandSize)
 | |
|         continue;
 | |
|     }
 | |
| 
 | |
|     // Tie breaker.
 | |
|     // To avoid scheduling indeterminism, we need a tie breaker
 | |
|     // for the case when cost is identical for two nodes.
 | |
|     if (UseNewerCandidate && CurrentCost == Candidate.SCost) {
 | |
|       if ((Q.getID() == TopQID && (*I)->NodeNum < Candidate.SU->NodeNum) ||
 | |
|           (Q.getID() == BotQID && (*I)->NodeNum > Candidate.SU->NodeNum)) {
 | |
|         LLVM_DEBUG(traceCandidate("TCAND", Q, *I, CurrentCost));
 | |
|         Candidate.SU = *I;
 | |
|         Candidate.RPDelta = RPDelta;
 | |
|         Candidate.SCost = CurrentCost;
 | |
|         FoundCandidate = NodeOrder;
 | |
|         continue;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Fall through to original instruction order.
 | |
|     // Only consider node order if Candidate was chosen from this Q.
 | |
|     if (FoundCandidate == NoCand)
 | |
|       continue;
 | |
|   }
 | |
|   return FoundCandidate;
 | |
| }
 | |
| 
 | |
| /// Pick the best candidate node from either the top or bottom queue.
 | |
| SUnit *ConvergingVLIWScheduler::pickNodeBidrectional(bool &IsTopNode) {
 | |
|   // Schedule as far as possible in the direction of no choice. This is most
 | |
|   // efficient, but also provides the best heuristics for CriticalPSets.
 | |
|   if (SUnit *SU = Bot.pickOnlyChoice()) {
 | |
|     LLVM_DEBUG(dbgs() << "Picked only Bottom\n");
 | |
|     IsTopNode = false;
 | |
|     return SU;
 | |
|   }
 | |
|   if (SUnit *SU = Top.pickOnlyChoice()) {
 | |
|     LLVM_DEBUG(dbgs() << "Picked only Top\n");
 | |
|     IsTopNode = true;
 | |
|     return SU;
 | |
|   }
 | |
|   SchedCandidate BotCand;
 | |
|   // Prefer bottom scheduling when heuristics are silent.
 | |
|   CandResult BotResult =
 | |
|       pickNodeFromQueue(Bot, DAG->getBotRPTracker(), BotCand);
 | |
|   assert(BotResult != NoCand && "failed to find the first candidate");
 | |
| 
 | |
|   // If either Q has a single candidate that provides the least increase in
 | |
|   // Excess pressure, we can immediately schedule from that Q.
 | |
|   //
 | |
|   // RegionCriticalPSets summarizes the pressure within the scheduled region and
 | |
|   // affects picking from either Q. If scheduling in one direction must
 | |
|   // increase pressure for one of the excess PSets, then schedule in that
 | |
|   // direction first to provide more freedom in the other direction.
 | |
|   if (BotResult == SingleExcess || BotResult == SingleCritical) {
 | |
|     LLVM_DEBUG(dbgs() << "Prefered Bottom Node\n");
 | |
|     IsTopNode = false;
 | |
|     return BotCand.SU;
 | |
|   }
 | |
|   // Check if the top Q has a better candidate.
 | |
|   SchedCandidate TopCand;
 | |
|   CandResult TopResult =
 | |
|       pickNodeFromQueue(Top, DAG->getTopRPTracker(), TopCand);
 | |
|   assert(TopResult != NoCand && "failed to find the first candidate");
 | |
| 
 | |
|   if (TopResult == SingleExcess || TopResult == SingleCritical) {
 | |
|     LLVM_DEBUG(dbgs() << "Prefered Top Node\n");
 | |
|     IsTopNode = true;
 | |
|     return TopCand.SU;
 | |
|   }
 | |
|   // If either Q has a single candidate that minimizes pressure above the
 | |
|   // original region's pressure pick it.
 | |
|   if (BotResult == SingleMax) {
 | |
|     LLVM_DEBUG(dbgs() << "Prefered Bottom Node SingleMax\n");
 | |
|     IsTopNode = false;
 | |
|     return BotCand.SU;
 | |
|   }
 | |
|   if (TopResult == SingleMax) {
 | |
|     LLVM_DEBUG(dbgs() << "Prefered Top Node SingleMax\n");
 | |
|     IsTopNode = true;
 | |
|     return TopCand.SU;
 | |
|   }
 | |
|   if (TopCand.SCost > BotCand.SCost) {
 | |
|     LLVM_DEBUG(dbgs() << "Prefered Top Node Cost\n");
 | |
|     IsTopNode = true;
 | |
|     return TopCand.SU;
 | |
|   }
 | |
|   // Otherwise prefer the bottom candidate in node order.
 | |
|   LLVM_DEBUG(dbgs() << "Prefered Bottom in Node order\n");
 | |
|   IsTopNode = false;
 | |
|   return BotCand.SU;
 | |
| }
 | |
| 
 | |
| /// Pick the best node to balance the schedule. Implements MachineSchedStrategy.
 | |
| SUnit *ConvergingVLIWScheduler::pickNode(bool &IsTopNode) {
 | |
|   if (DAG->top() == DAG->bottom()) {
 | |
|     assert(Top.Available.empty() && Top.Pending.empty() &&
 | |
|            Bot.Available.empty() && Bot.Pending.empty() && "ReadyQ garbage");
 | |
|     return nullptr;
 | |
|   }
 | |
|   SUnit *SU;
 | |
|   if (ForceTopDown) {
 | |
|     SU = Top.pickOnlyChoice();
 | |
|     if (!SU) {
 | |
|       SchedCandidate TopCand;
 | |
|       CandResult TopResult =
 | |
|           pickNodeFromQueue(Top, DAG->getTopRPTracker(), TopCand);
 | |
|       assert(TopResult != NoCand && "failed to find the first candidate");
 | |
|       (void)TopResult;
 | |
|       SU = TopCand.SU;
 | |
|     }
 | |
|     IsTopNode = true;
 | |
|   } else if (ForceBottomUp) {
 | |
|     SU = Bot.pickOnlyChoice();
 | |
|     if (!SU) {
 | |
|       SchedCandidate BotCand;
 | |
|       CandResult BotResult =
 | |
|           pickNodeFromQueue(Bot, DAG->getBotRPTracker(), BotCand);
 | |
|       assert(BotResult != NoCand && "failed to find the first candidate");
 | |
|       (void)BotResult;
 | |
|       SU = BotCand.SU;
 | |
|     }
 | |
|     IsTopNode = false;
 | |
|   } else {
 | |
|     SU = pickNodeBidrectional(IsTopNode);
 | |
|   }
 | |
|   if (SU->isTopReady())
 | |
|     Top.removeReady(SU);
 | |
|   if (SU->isBottomReady())
 | |
|     Bot.removeReady(SU);
 | |
| 
 | |
|   LLVM_DEBUG(dbgs() << "*** " << (IsTopNode ? "Top" : "Bottom")
 | |
|                     << " Scheduling instruction in cycle "
 | |
|                     << (IsTopNode ? Top.CurrCycle : Bot.CurrCycle) << " ("
 | |
|                     << reportPackets() << ")\n";
 | |
|              DAG->dumpNode(*SU));
 | |
|   return SU;
 | |
| }
 | |
| 
 | |
| /// Update the scheduler's state after scheduling a node. This is the same node
 | |
| /// that was just returned by pickNode(). However, VLIWMachineScheduler needs
 | |
| /// to update it's state based on the current cycle before MachineSchedStrategy
 | |
| /// does.
 | |
| void ConvergingVLIWScheduler::schedNode(SUnit *SU, bool IsTopNode) {
 | |
|   if (IsTopNode) {
 | |
|     Top.bumpNode(SU);
 | |
|     SU->TopReadyCycle = Top.CurrCycle;
 | |
|   } else {
 | |
|     Bot.bumpNode(SU);
 | |
|     SU->BotReadyCycle = Bot.CurrCycle;
 | |
|   }
 | |
| }
 |