//-- SystemZMachineScheduler.cpp - SystemZ Scheduler Interface -*- C++ -*---==// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // -------------------------- Post RA scheduling ---------------------------- // // SystemZPostRASchedStrategy is a scheduling strategy which is plugged into // the MachineScheduler. It has a sorted Available set of SUs and a pickNode() // implementation that looks to optimize decoder grouping and balance the // usage of processor resources. Scheduler states are saved for the end // region of each MBB, so that a successor block can learn from it. //===----------------------------------------------------------------------===// #include "SystemZMachineScheduler.h" using namespace llvm; #define DEBUG_TYPE "machine-scheduler" #ifndef NDEBUG // Print the set of SUs void SystemZPostRASchedStrategy::SUSet:: dump(SystemZHazardRecognizer &HazardRec) const { dbgs() << "{"; for (auto &SU : *this) { HazardRec.dumpSU(SU, dbgs()); if (SU != *rbegin()) dbgs() << ", "; } dbgs() << "}\n"; } #endif // Try to find a single predecessor that would be interesting for the // scheduler in the top-most region of MBB. static MachineBasicBlock *getSingleSchedPred(MachineBasicBlock *MBB, const MachineLoop *Loop) { MachineBasicBlock *PredMBB = nullptr; if (MBB->pred_size() == 1) PredMBB = *MBB->pred_begin(); // The loop header has two predecessors, return the latch, but not for a // single block loop. if (MBB->pred_size() == 2 && Loop != nullptr && Loop->getHeader() == MBB) { for (auto I = MBB->pred_begin(); I != MBB->pred_end(); ++I) if (Loop->contains(*I)) PredMBB = (*I == MBB ? nullptr : *I); } assert ((PredMBB == nullptr || !Loop || Loop->contains(PredMBB)) && "Loop MBB should not consider predecessor outside of loop."); return PredMBB; } void SystemZPostRASchedStrategy:: advanceTo(MachineBasicBlock::iterator NextBegin) { MachineBasicBlock::iterator LastEmittedMI = HazardRec->getLastEmittedMI(); MachineBasicBlock::iterator I = ((LastEmittedMI != nullptr && LastEmittedMI->getParent() == MBB) ? std::next(LastEmittedMI) : MBB->begin()); for (; I != NextBegin; ++I) { if (I->isPosition() || I->isDebugValue()) continue; HazardRec->emitInstruction(&*I); } } void SystemZPostRASchedStrategy::enterMBB(MachineBasicBlock *NextMBB) { assert ((SchedStates.find(NextMBB) == SchedStates.end()) && "Entering MBB twice?"); DEBUG(dbgs() << "+++ Entering " << printMBBReference(*NextMBB)); MBB = NextMBB; /// Create a HazardRec for MBB, save it in SchedStates and set HazardRec to /// point to it. HazardRec = SchedStates[MBB] = new SystemZHazardRecognizer(TII, &SchedModel); DEBUG (const MachineLoop *Loop = MLI->getLoopFor(MBB); if(Loop && Loop->getHeader() == MBB) dbgs() << " (Loop header)"; dbgs() << ":\n";); // Try to take over the state from a single predecessor, if it has been // scheduled. If this is not possible, we are done. MachineBasicBlock *SinglePredMBB = getSingleSchedPred(MBB, MLI->getLoopFor(MBB)); if (SinglePredMBB == nullptr || SchedStates.find(SinglePredMBB) == SchedStates.end()) return; DEBUG(dbgs() << "+++ Continued scheduling from " << printMBBReference(*SinglePredMBB) << "\n";); HazardRec->copyState(SchedStates[SinglePredMBB]); // Emit incoming terminator(s). Be optimistic and assume that branch // prediction will generally do "the right thing". for (MachineBasicBlock::iterator I = SinglePredMBB->getFirstTerminator(); I != SinglePredMBB->end(); I++) { DEBUG (dbgs() << "+++ Emitting incoming branch: "; I->dump();); bool TakenBranch = (I->isBranch() && (TII->getBranchInfo(*I).Target->isReg() || // Relative branch TII->getBranchInfo(*I).Target->getMBB() == MBB)); HazardRec->emitInstruction(&*I, TakenBranch); if (TakenBranch) break; } } void SystemZPostRASchedStrategy::leaveMBB() { DEBUG(dbgs() << "+++ Leaving " << printMBBReference(*MBB) << "\n";); // Advance to first terminator. The successor block will handle terminators // dependent on CFG layout (T/NT branch etc). advanceTo(MBB->getFirstTerminator()); } SystemZPostRASchedStrategy:: SystemZPostRASchedStrategy(const MachineSchedContext *C) : MLI(C->MLI), TII(static_cast (C->MF->getSubtarget().getInstrInfo())), MBB(nullptr), HazardRec(nullptr) { const TargetSubtargetInfo *ST = &C->MF->getSubtarget(); SchedModel.init(ST->getSchedModel(), ST, TII); } SystemZPostRASchedStrategy::~SystemZPostRASchedStrategy() { // Delete hazard recognizers kept around for each MBB. for (auto I : SchedStates) { SystemZHazardRecognizer *hazrec = I.second; delete hazrec; } } void SystemZPostRASchedStrategy::initPolicy(MachineBasicBlock::iterator Begin, MachineBasicBlock::iterator End, unsigned NumRegionInstrs) { // Don't emit the terminators. if (Begin->isTerminator()) return; // Emit any instructions before start of region. advanceTo(Begin); } // Pick the next node to schedule. SUnit *SystemZPostRASchedStrategy::pickNode(bool &IsTopNode) { // Only scheduling top-down. IsTopNode = true; if (Available.empty()) return nullptr; // If only one choice, return it. if (Available.size() == 1) { DEBUG (dbgs() << "+++ Only one: "; HazardRec->dumpSU(*Available.begin(), dbgs()); dbgs() << "\n";); return *Available.begin(); } // All nodes that are possible to schedule are stored by in the // Available set. DEBUG(dbgs() << "+++ Available: "; Available.dump(*HazardRec);); Candidate Best; for (auto *SU : Available) { // SU is the next candidate to be compared against current Best. Candidate c(SU, *HazardRec); // Remeber which SU is the best candidate. if (Best.SU == nullptr || c < Best) { Best = c; DEBUG(dbgs() << "+++ Best sofar: "; HazardRec->dumpSU(Best.SU, dbgs()); if (Best.GroupingCost != 0) dbgs() << "\tGrouping cost:" << Best.GroupingCost; if (Best.ResourcesCost != 0) dbgs() << " Resource cost:" << Best.ResourcesCost; dbgs() << " Height:" << Best.SU->getHeight(); dbgs() << "\n";); } // Once we know we have seen all SUs that affect grouping or use unbuffered // resources, we can stop iterating if Best looks good. if (!SU->isScheduleHigh && Best.noCost()) break; } assert (Best.SU != nullptr); return Best.SU; } SystemZPostRASchedStrategy::Candidate:: Candidate(SUnit *SU_, SystemZHazardRecognizer &HazardRec) : Candidate() { SU = SU_; // Check the grouping cost. For a node that must begin / end a // group, it is positive if it would do so prematurely, or negative // if it would fit naturally into the schedule. GroupingCost = HazardRec.groupingCost(SU); // Check the resources cost for this SU. ResourcesCost = HazardRec.resourcesCost(SU); } bool SystemZPostRASchedStrategy::Candidate:: operator<(const Candidate &other) { // Check decoder grouping. if (GroupingCost < other.GroupingCost) return true; if (GroupingCost > other.GroupingCost) return false; // Compare the use of resources. if (ResourcesCost < other.ResourcesCost) return true; if (ResourcesCost > other.ResourcesCost) return false; // Higher SU is otherwise generally better. if (SU->getHeight() > other.SU->getHeight()) return true; if (SU->getHeight() < other.SU->getHeight()) return false; // If all same, fall back to original order. if (SU->NodeNum < other.SU->NodeNum) return true; return false; } void SystemZPostRASchedStrategy::schedNode(SUnit *SU, bool IsTopNode) { DEBUG(dbgs() << "+++ Scheduling SU(" << SU->NodeNum << ")\n";); // Remove SU from Available set and update HazardRec. Available.erase(SU); HazardRec->EmitInstruction(SU); } void SystemZPostRASchedStrategy::releaseTopNode(SUnit *SU) { // Set isScheduleHigh flag on all SUs that we want to consider first in // pickNode(). const MCSchedClassDesc *SC = HazardRec->getSchedClass(SU); bool AffectsGrouping = (SC->isValid() && (SC->BeginGroup || SC->EndGroup)); SU->isScheduleHigh = (AffectsGrouping || SU->isUnbuffered); // Put all released SUs in the Available set. Available.insert(SU); }