| //-- 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); |
| } |