| //-- SystemZMachineScheduler.cpp - SystemZ Scheduler Interface -*- C++ -*---==// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // -------------------------- 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" | |
| #include "llvm/CodeGen/MachineLoopInfo.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 (MachineBasicBlock *Pred : MBB->predecessors()) | |
| if (Loop->contains(Pred)) | |
| PredMBB = (Pred == MBB ? nullptr : Pred); | |
| } | |
| 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->isDebugInstr()) | |
| continue; | |
| HazardRec->emitInstruction(&*I); | |
| } | |
| } | |
| void SystemZPostRASchedStrategy::initialize(ScheduleDAGMI *dag) { | |
| Available.clear(); // -misched-cutoff. | |
| LLVM_DEBUG(HazardRec->dumpState();); | |
| } | |
| void SystemZPostRASchedStrategy::enterMBB(MachineBasicBlock *NextMBB) { | |
| assert ((SchedStates.find(NextMBB) == SchedStates.end()) && | |
| "Entering MBB twice?"); | |
| LLVM_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); | |
| LLVM_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; | |
| LLVM_DEBUG(dbgs() << "** Continued scheduling from " | |
| << printMBBReference(*SinglePredMBB) << "\n";); | |
| HazardRec->copyState(SchedStates[SinglePredMBB]); | |
| LLVM_DEBUG(HazardRec->dumpState();); | |
| // Emit incoming terminator(s). Be optimistic and assume that branch | |
| // prediction will generally do "the right thing". | |
| for (MachineInstr &MI : SinglePredMBB->terminators()) { | |
| LLVM_DEBUG(dbgs() << "** Emitting incoming branch: "; MI.dump();); | |
| bool TakenBranch = (MI.isBranch() && | |
| (TII->getBranchInfo(MI).isIndirect() || | |
| TII->getBranchInfo(MI).getMBBTarget() == MBB)); | |
| HazardRec->emitInstruction(&MI, TakenBranch); | |
| if (TakenBranch) | |
| break; | |
| } | |
| } | |
| void SystemZPostRASchedStrategy::leaveMBB() { | |
| LLVM_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<const SystemZInstrInfo *> | |
| (C->MF->getSubtarget().getInstrInfo())), | |
| MBB(nullptr), HazardRec(nullptr) { | |
| const TargetSubtargetInfo *ST = &C->MF->getSubtarget(); | |
| SchedModel.init(ST); | |
| } | |
| 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) { | |
| LLVM_DEBUG(dbgs() << "** Only one: "; | |
| HazardRec->dumpSU(*Available.begin(), dbgs()); dbgs() << "\n";); | |
| return *Available.begin(); | |
| } | |
| // All nodes that are possible to schedule are stored in the Available set. | |
| LLVM_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; | |
| LLVM_DEBUG(dbgs() << "** Best so far: ";); | |
| } else | |
| LLVM_DEBUG(dbgs() << "** Tried : ";); | |
| LLVM_DEBUG(HazardRec->dumpSU(c.SU, dbgs()); c.dumpCosts(); | |
| dbgs() << " Height:" << c.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) { | |
| LLVM_DEBUG(dbgs() << "** Scheduling SU(" << SU->NodeNum << ") "; | |
| if (Available.size() == 1) dbgs() << "(only one) "; | |
| Candidate c(SU, *HazardRec); c.dumpCosts(); dbgs() << "\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); | |
| } |