aboutsummaryrefslogtreecommitdiff
path: root/lib/CodeGen/MachineScheduler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/CodeGen/MachineScheduler.cpp')
-rw-r--r--lib/CodeGen/MachineScheduler.cpp281
1 files changed, 232 insertions, 49 deletions
diff --git a/lib/CodeGen/MachineScheduler.cpp b/lib/CodeGen/MachineScheduler.cpp
index 30ae42d8bf..efc7bd76ce 100644
--- a/lib/CodeGen/MachineScheduler.cpp
+++ b/lib/CodeGen/MachineScheduler.cpp
@@ -20,6 +20,7 @@
#include "llvm/CodeGen/MachineScheduler.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/ScheduleDAGInstrs.h"
+#include "llvm/CodeGen/ScheduleHazardRecognizer.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Support/CommandLine.h"
@@ -302,6 +303,9 @@ public:
/// be scheduled at the bottom.
virtual SUnit *pickNode(bool &IsTopNode) = 0;
+ /// Notify MachineSchedStrategy that ScheduleDAGMI has scheduled a node.
+ virtual void schedNode(SUnit *SU, bool IsTopNode) = 0;
+
/// When all predecessor dependencies have been resolved, free this node for
/// top-down scheduling.
virtual void releaseTopNode(SUnit *SU) = 0;
@@ -409,6 +413,8 @@ protected:
/// ReleaseSucc - Decrement the NumPredsLeft count of a successor. When
/// NumPredsLeft reaches zero, release the successor node.
+///
+/// FIXME: Adjust SuccSU height based on MinLatency.
void ScheduleDAGMI::releaseSucc(SUnit *SU, SDep *SuccEdge) {
SUnit *SuccSU = SuccEdge->getSUnit();
@@ -435,6 +441,8 @@ void ScheduleDAGMI::releaseSuccessors(SUnit *SU) {
/// ReleasePred - Decrement the NumSuccsLeft count of a predecessor. When
/// NumSuccsLeft reaches zero, release the predecessor node.
+///
+/// FIXME: Adjust PredSU height based on MinLatency.
void ScheduleDAGMI::releasePred(SUnit *SU, SDep *PredEdge) {
SUnit *PredSU = PredEdge->getSUnit();
@@ -613,7 +621,7 @@ void ScheduleDAGMI::schedule() {
bool IsTopNode = false;
while (SUnit *SU = SchedImpl->pickNode(IsTopNode)) {
DEBUG(dbgs() << "*** " << (IsTopNode ? "Top" : "Bottom")
- << " Scheduling Instruction:\n"; SU->dump(this));
+ << " Scheduling Instruction");
if (!checkSchedLimit())
break;
@@ -660,6 +668,8 @@ void ScheduleDAGMI::schedule() {
releasePredecessors(SU);
}
SU->isScheduled = true;
+ SchedImpl->schedNode(SU, IsTopNode);
+ DEBUG(SU->dump(this));
}
assert(CurrentTop == CurrentBottom && "Nonempty unscheduled zone.");
@@ -755,11 +765,45 @@ class ConvergingScheduler : public MachineSchedStrategy {
enum CandResult {
NoCand, NodeOrder, SingleExcess, SingleCritical, SingleMax, MultiPressure };
+ struct SchedBoundary {
+ ReadyQueue Available;
+ ReadyQueue Pending;
+ bool CheckPending;
+
+ ScheduleHazardRecognizer *HazardRec;
+
+ unsigned CurrCycle;
+ unsigned IssueCount;
+
+ /// MinReadyCycle - Cycle of the soonest available instruction.
+ unsigned MinReadyCycle;
+
+ /// Pending queues extend the ready queues with the same ID.
+ SchedBoundary(unsigned ID):
+ Available(ID), Pending(ID), CheckPending(false), HazardRec(0),
+ CurrCycle(0), IssueCount(0), MinReadyCycle(UINT_MAX) {}
+
+ ~SchedBoundary() { delete HazardRec; }
+
+ bool isTop() const { return Available.ID == ConvergingScheduler::TopQID; }
+
+ void releaseNode(SUnit *SU, unsigned ReadyCycle);
+
+ void bumpCycle();
+
+ void releasePending();
+
+ void removeReady(SUnit *SU);
+
+ SUnit *pickOnlyChoice();
+ };
+
ScheduleDAGMI *DAG;
const TargetRegisterInfo *TRI;
- ReadyQueue TopQueue;
- ReadyQueue BotQueue;
+ // State of the top and bottom scheduled instruction boundaries.
+ SchedBoundary Top;
+ SchedBoundary Bot;
public:
/// SUnit::NodeQueueId = 0 (none), = 1 (top), = 2 (bottom), = 3 (both)
@@ -768,7 +812,7 @@ public:
BotQID = 2
};
- ConvergingScheduler(): DAG(0), TRI(0), TopQueue(TopQID), BotQueue(BotQID) {}
+ ConvergingScheduler(): DAG(0), TRI(0), Top(TopQID), Bot(BotQID) {}
static const char *getQName(unsigned ID) {
switch(ID) {
@@ -778,24 +822,16 @@ public:
};
}
- virtual void initialize(ScheduleDAGMI *dag) {
- DAG = dag;
- TRI = DAG->TRI;
-
- assert((!ForceTopDown || !ForceBottomUp) &&
- "-misched-topdown incompatible with -misched-bottomup");
- }
+ virtual void initialize(ScheduleDAGMI *dag);
virtual SUnit *pickNode(bool &IsTopNode);
- virtual void releaseTopNode(SUnit *SU) {
- if (!SU->isScheduled)
- TopQueue.push(SU);
- }
- virtual void releaseBottomNode(SUnit *SU) {
- if (!SU->isScheduled)
- BotQueue.push(SU);
- }
+ virtual void schedNode(SUnit *SU, bool IsTopNode);
+
+ virtual void releaseTopNode(SUnit *SU);
+
+ virtual void releaseBottomNode(SUnit *SU);
+
protected:
SUnit *pickNodeBidrectional(bool &IsTopNode);
@@ -809,11 +845,131 @@ protected:
};
} // namespace
+void ConvergingScheduler::initialize(ScheduleDAGMI *dag) {
+ DAG = dag;
+ TRI = DAG->TRI;
+
+ // Initialize the HazardRecognizers.
+ const TargetMachine &TM = DAG->MF.getTarget();
+ const InstrItineraryData *Itin = TM.getInstrItineraryData();
+ Top.HazardRec = TM.getInstrInfo()->CreateTargetMIHazardRecognizer(Itin, DAG);
+ Bot.HazardRec = TM.getInstrInfo()->CreateTargetMIHazardRecognizer(Itin, DAG);
+
+ assert((!ForceTopDown || !ForceBottomUp) &&
+ "-misched-topdown incompatible with -misched-bottomup");
+}
+
+void ConvergingScheduler::releaseTopNode(SUnit *SU) {
+ Top.releaseNode(SU, SU->getDepth());
+}
+
+void ConvergingScheduler::releaseBottomNode(SUnit *SU) {
+ Bot.releaseNode(SU, SU->getHeight());
+}
+
+void ConvergingScheduler::SchedBoundary::releaseNode(SUnit *SU,
+ unsigned ReadyCycle) {
+ if (SU->isScheduled)
+ return;
+
+ 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 (HazardRec->isEnabled()
+ && HazardRec->getHazardType(SU) != ScheduleHazardRecognizer::NoHazard)
+ Pending.push(SU);
+ else
+ Available.push(SU);
+}
+
+/// Move the boundary of scheduled code by one cycle.
+void ConvergingScheduler::SchedBoundary::bumpCycle() {
+ IssueCount = 0;
+
+ assert(MinReadyCycle < UINT_MAX && "MinReadyCycle uninitialized");
+ unsigned NextCycle = std::max(CurrCycle + 1, MinReadyCycle);
+
+ if (!HazardRec->isEnabled()) {
+ // Bypass lots of virtual calls in case of long latency.
+ CurrCycle = NextCycle;
+ }
+ else {
+ for (; CurrCycle != NextCycle; ++CurrCycle) {
+ if (isTop())
+ HazardRec->AdvanceCycle();
+ else
+ HazardRec->RecedeCycle();
+ }
+ }
+ CheckPending = true;
+
+ DEBUG(dbgs() << "*** " << getQName(Available.ID) << " cycle "
+ << CurrCycle << '\n');
+}
+
+/// Release pending ready nodes in to the available queue. This makes them
+/// visible to heuristics.
+void ConvergingScheduler::SchedBoundary::releasePending() {
+ // If the available queue is empty, it is safe to reset MinReadyCycle.
+ if (Available.empty())
+ MinReadyCycle = UINT_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->getHeight() : SU->getDepth();
+
+ if (ReadyCycle < MinReadyCycle)
+ MinReadyCycle = ReadyCycle;
+
+ if (ReadyCycle > CurrCycle)
+ continue;
+
+ if (HazardRec->isEnabled()
+ && HazardRec->getHazardType(SU) != ScheduleHazardRecognizer::NoHazard)
+ continue;
+
+ Available.push(SU);
+ Pending.remove(Pending.begin()+i);
+ --i; --e;
+ }
+ CheckPending = false;
+}
+
+/// Remove SU from the ready set for this boundary.
+void ConvergingScheduler::SchedBoundary::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 *ConvergingScheduler::SchedBoundary::pickOnlyChoice() {
+ if (CheckPending)
+ releasePending();
+
+ for (unsigned i = 0; Available.empty(); ++i) {
+ assert(i <= HazardRec->getMaxLookAhead() && "permanent hazard"); (void)i;
+ bumpCycle();
+ releasePending();
+ }
+ if (Available.size() == 1)
+ return *Available.begin();
+ return NULL;
+}
+
#ifndef NDEBUG
-void ConvergingScheduler::
-traceCandidate(const char *Label, unsigned QID, SUnit *SU,
- PressureElement P) {
- dbgs() << Label << getQName(QID) << " ";
+void ConvergingScheduler::traceCandidate(const char *Label, unsigned QID,
+ SUnit *SU, PressureElement P) {
+ dbgs() << Label << " " << getQName(QID) << " ";
if (P.isValid())
dbgs() << TRI->getRegPressureSetName(P.PSetID) << ":" << P.UnitIncrease
<< " ";
@@ -862,7 +1018,6 @@ pickNodeFromQueue(ReadyQueue &Q, const 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(),
@@ -938,18 +1093,18 @@ pickNodeFromQueue(ReadyQueue &Q, const RegPressureTracker &RPTracker,
SUnit *ConvergingScheduler::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 (BotQueue.size() == 1) {
+ if (SUnit *SU = Bot.pickOnlyChoice()) {
IsTopNode = false;
- return *BotQueue.begin();
+ return SU;
}
- if (TopQueue.size() == 1) {
+ if (SUnit *SU = Top.pickOnlyChoice()) {
IsTopNode = true;
- return *TopQueue.begin();
+ return SU;
}
- SchedCandidate BotCandidate;
+ SchedCandidate BotCand;
// Prefer bottom scheduling when heuristics are silent.
- CandResult BotResult =
- pickNodeFromQueue(BotQueue, DAG->getBotRPTracker(), BotCandidate);
+ CandResult BotResult = pickNodeFromQueue(Bot.Available,
+ 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
@@ -961,42 +1116,43 @@ SUnit *ConvergingScheduler::pickNodeBidrectional(bool &IsTopNode) {
// direction first to provide more freedom in the other direction.
if (BotResult == SingleExcess || BotResult == SingleCritical) {
IsTopNode = false;
- return BotCandidate.SU;
+ return BotCand.SU;
}
// Check if the top Q has a better candidate.
- SchedCandidate TopCandidate;
- CandResult TopResult =
- pickNodeFromQueue(TopQueue, DAG->getTopRPTracker(), TopCandidate);
+ SchedCandidate TopCand;
+ CandResult TopResult = pickNodeFromQueue(Top.Available,
+ DAG->getTopRPTracker(), TopCand);
assert(TopResult != NoCand && "failed to find the first candidate");
if (TopResult == SingleExcess || TopResult == SingleCritical) {
IsTopNode = true;
- return TopCandidate.SU;
+ return TopCand.SU;
}
// If either Q has a single candidate that minimizes pressure above the
// original region's pressure pick it.
if (BotResult == SingleMax) {
IsTopNode = false;
- return BotCandidate.SU;
+ return BotCand.SU;
}
if (TopResult == SingleMax) {
IsTopNode = true;
- return TopCandidate.SU;
+ return TopCand.SU;
}
// Check for a salient pressure difference and pick the best from either side.
- if (compareRPDelta(TopCandidate.RPDelta, BotCandidate.RPDelta)) {
+ if (compareRPDelta(TopCand.RPDelta, BotCand.RPDelta)) {
IsTopNode = true;
- return TopCandidate.SU;
+ return TopCand.SU;
}
// Otherwise prefer the bottom candidate in node order.
IsTopNode = false;
- return BotCandidate.SU;
+ return BotCand.SU;
}
/// Pick the best node to balance the schedule. Implements MachineSchedStrategy.
SUnit *ConvergingScheduler::pickNode(bool &IsTopNode) {
if (DAG->top() == DAG->bottom()) {
- assert(TopQueue.empty() && BotQueue.empty() && "ReadyQueue garbage");
+ assert(Top.Available.empty() && Top.Pending.empty() &&
+ Bot.Available.empty() && Bot.Pending.empty() && "ReadyQ garbage");
return NULL;
}
SUnit *SU;
@@ -1011,15 +1167,40 @@ SUnit *ConvergingScheduler::pickNode(bool &IsTopNode) {
else {
SU = pickNodeBidrectional(IsTopNode);
}
- if (SU->isTopReady()) {
- assert(!TopQueue.empty() && "bad ready count");
- TopQueue.remove(TopQueue.find(SU));
+ if (SU->isTopReady())
+ Top.removeReady(SU);
+ if (SU->isBottomReady())
+ Bot.removeReady(SU);
+ return SU;
+}
+
+/// Update the scheduler's state after scheduling a node. This is the same node
+/// that was just returned by pickNode(). However, ScheduleDAGMI needs to update
+/// it's state based on the current cycle before MachineSchedStrategy.
+void ConvergingScheduler::schedNode(SUnit *SU, bool IsTopNode) {
+ DEBUG(dbgs() << " in cycle " << (IsTopNode ? Top.CurrCycle : Bot.CurrCycle)
+ << '\n');
+
+ // Update the reservation table.
+ if (IsTopNode && Top.HazardRec->isEnabled()) {
+ Top.HazardRec->EmitInstruction(SU);
+ if (Top.HazardRec->atIssueLimit()) {
+ DEBUG(dbgs() << "*** Max instrs at cycle " << Top.CurrCycle << '\n');
+ Top.bumpCycle();
+ }
}
- if (SU->isBottomReady()) {
- assert(!BotQueue.empty() && "bad ready count");
- BotQueue.remove(BotQueue.find(SU));
+ else if (Bot.HazardRec->isEnabled()) {
+ if (SU->isCall) {
+ // Calls are scheduled with their preceding instructions. For bottom-up
+ // scheduling, clear the pipeline state before emitting.
+ Bot.HazardRec->Reset();
+ }
+ Bot.HazardRec->EmitInstruction(SU);
+ if (Bot.HazardRec->atIssueLimit()) {
+ DEBUG(dbgs() << "*** Max instrs at cycle " << Bot.CurrCycle << '\n');
+ Bot.bumpCycle();
+ }
}
- return SU;
}
/// Create the standard converging machine scheduler. This will be used as the
@@ -1099,6 +1280,8 @@ public:
return SU;
}
+ virtual void schedNode(SUnit *SU, bool IsTopNode) {}
+
virtual void releaseTopNode(SUnit *SU) {
TopQ.push(SU);
}