diff options
Diffstat (limited to 'llvm/lib/Target/SystemZ/SystemZMachineScheduler.cpp')
| -rw-r--r-- | llvm/lib/Target/SystemZ/SystemZMachineScheduler.cpp | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/llvm/lib/Target/SystemZ/SystemZMachineScheduler.cpp b/llvm/lib/Target/SystemZ/SystemZMachineScheduler.cpp new file mode 100644 index 000000000000..3fc25034dded --- /dev/null +++ b/llvm/lib/Target/SystemZ/SystemZMachineScheduler.cpp @@ -0,0 +1,260 @@ +//-- 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 (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->isDebugInstr()) + continue; + HazardRec->emitInstruction(&*I); + } +} + +void SystemZPostRASchedStrategy::initialize(ScheduleDAGMI *dag) { + 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 (MachineBasicBlock::iterator I = SinglePredMBB->getFirstTerminator(); + I != SinglePredMBB->end(); I++) { + LLVM_DEBUG(dbgs() << "** Emitting incoming branch: "; I->dump();); + bool TakenBranch = (I->isBranch() && + (TII->getBranchInfo(*I).isIndirect() || + TII->getBranchInfo(*I).getMBBTarget() == MBB)); + HazardRec->emitInstruction(&*I, 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); +} |
