1*0b57cec5SDimitry Andric //-- SystemZMachineScheduler.cpp - SystemZ Scheduler Interface -*- C++ -*---==//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric // -------------------------- Post RA scheduling ---------------------------- //
10*0b57cec5SDimitry Andric // SystemZPostRASchedStrategy is a scheduling strategy which is plugged into
11*0b57cec5SDimitry Andric // the MachineScheduler. It has a sorted Available set of SUs and a pickNode()
12*0b57cec5SDimitry Andric // implementation that looks to optimize decoder grouping and balance the
13*0b57cec5SDimitry Andric // usage of processor resources. Scheduler states are saved for the end
14*0b57cec5SDimitry Andric // region of each MBB, so that a successor block can learn from it.
15*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
16*0b57cec5SDimitry Andric
17*0b57cec5SDimitry Andric #include "SystemZMachineScheduler.h"
18*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineLoopInfo.h"
19*0b57cec5SDimitry Andric
20*0b57cec5SDimitry Andric using namespace llvm;
21*0b57cec5SDimitry Andric
22*0b57cec5SDimitry Andric #define DEBUG_TYPE "machine-scheduler"
23*0b57cec5SDimitry Andric
24*0b57cec5SDimitry Andric #ifndef NDEBUG
25*0b57cec5SDimitry Andric // Print the set of SUs
26*0b57cec5SDimitry Andric void SystemZPostRASchedStrategy::SUSet::
dump(SystemZHazardRecognizer & HazardRec) const27*0b57cec5SDimitry Andric dump(SystemZHazardRecognizer &HazardRec) const {
28*0b57cec5SDimitry Andric dbgs() << "{";
29*0b57cec5SDimitry Andric for (auto &SU : *this) {
30*0b57cec5SDimitry Andric HazardRec.dumpSU(SU, dbgs());
31*0b57cec5SDimitry Andric if (SU != *rbegin())
32*0b57cec5SDimitry Andric dbgs() << ", ";
33*0b57cec5SDimitry Andric }
34*0b57cec5SDimitry Andric dbgs() << "}\n";
35*0b57cec5SDimitry Andric }
36*0b57cec5SDimitry Andric #endif
37*0b57cec5SDimitry Andric
38*0b57cec5SDimitry Andric // Try to find a single predecessor that would be interesting for the
39*0b57cec5SDimitry Andric // scheduler in the top-most region of MBB.
getSingleSchedPred(MachineBasicBlock * MBB,const MachineLoop * Loop)40*0b57cec5SDimitry Andric static MachineBasicBlock *getSingleSchedPred(MachineBasicBlock *MBB,
41*0b57cec5SDimitry Andric const MachineLoop *Loop) {
42*0b57cec5SDimitry Andric MachineBasicBlock *PredMBB = nullptr;
43*0b57cec5SDimitry Andric if (MBB->pred_size() == 1)
44*0b57cec5SDimitry Andric PredMBB = *MBB->pred_begin();
45*0b57cec5SDimitry Andric
46*0b57cec5SDimitry Andric // The loop header has two predecessors, return the latch, but not for a
47*0b57cec5SDimitry Andric // single block loop.
48*0b57cec5SDimitry Andric if (MBB->pred_size() == 2 && Loop != nullptr && Loop->getHeader() == MBB) {
49*0b57cec5SDimitry Andric for (MachineBasicBlock *Pred : MBB->predecessors())
50*0b57cec5SDimitry Andric if (Loop->contains(Pred))
51*0b57cec5SDimitry Andric PredMBB = (Pred == MBB ? nullptr : Pred);
52*0b57cec5SDimitry Andric }
53*0b57cec5SDimitry Andric
54*0b57cec5SDimitry Andric assert ((PredMBB == nullptr || !Loop || Loop->contains(PredMBB))
55*0b57cec5SDimitry Andric && "Loop MBB should not consider predecessor outside of loop.");
56*0b57cec5SDimitry Andric
57*0b57cec5SDimitry Andric return PredMBB;
58*0b57cec5SDimitry Andric }
59*0b57cec5SDimitry Andric
60*0b57cec5SDimitry Andric void SystemZPostRASchedStrategy::
advanceTo(MachineBasicBlock::iterator NextBegin)61*0b57cec5SDimitry Andric advanceTo(MachineBasicBlock::iterator NextBegin) {
62*0b57cec5SDimitry Andric MachineBasicBlock::iterator LastEmittedMI = HazardRec->getLastEmittedMI();
63*0b57cec5SDimitry Andric MachineBasicBlock::iterator I =
64*0b57cec5SDimitry Andric ((LastEmittedMI != nullptr && LastEmittedMI->getParent() == MBB) ?
65*0b57cec5SDimitry Andric std::next(LastEmittedMI) : MBB->begin());
66*0b57cec5SDimitry Andric
67*0b57cec5SDimitry Andric for (; I != NextBegin; ++I) {
68*0b57cec5SDimitry Andric if (I->isPosition() || I->isDebugInstr())
69*0b57cec5SDimitry Andric continue;
70*0b57cec5SDimitry Andric HazardRec->emitInstruction(&*I);
71*0b57cec5SDimitry Andric }
72*0b57cec5SDimitry Andric }
73*0b57cec5SDimitry Andric
initialize(ScheduleDAGMI * dag)74*0b57cec5SDimitry Andric void SystemZPostRASchedStrategy::initialize(ScheduleDAGMI *dag) {
75*0b57cec5SDimitry Andric Available.clear(); // -misched-cutoff.
76*0b57cec5SDimitry Andric LLVM_DEBUG(HazardRec->dumpState(););
77*0b57cec5SDimitry Andric }
78*0b57cec5SDimitry Andric
enterMBB(MachineBasicBlock * NextMBB)79*0b57cec5SDimitry Andric void SystemZPostRASchedStrategy::enterMBB(MachineBasicBlock *NextMBB) {
80*0b57cec5SDimitry Andric assert ((SchedStates.find(NextMBB) == SchedStates.end()) &&
81*0b57cec5SDimitry Andric "Entering MBB twice?");
82*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "** Entering " << printMBBReference(*NextMBB));
83*0b57cec5SDimitry Andric
84*0b57cec5SDimitry Andric MBB = NextMBB;
85*0b57cec5SDimitry Andric
86*0b57cec5SDimitry Andric /// Create a HazardRec for MBB, save it in SchedStates and set HazardRec to
87*0b57cec5SDimitry Andric /// point to it.
88*0b57cec5SDimitry Andric HazardRec = SchedStates[MBB] = new SystemZHazardRecognizer(TII, &SchedModel);
89*0b57cec5SDimitry Andric LLVM_DEBUG(const MachineLoop *Loop = MLI->getLoopFor(MBB);
90*0b57cec5SDimitry Andric if (Loop && Loop->getHeader() == MBB) dbgs() << " (Loop header)";
91*0b57cec5SDimitry Andric dbgs() << ":\n";);
92*0b57cec5SDimitry Andric
93*0b57cec5SDimitry Andric // Try to take over the state from a single predecessor, if it has been
94*0b57cec5SDimitry Andric // scheduled. If this is not possible, we are done.
95*0b57cec5SDimitry Andric MachineBasicBlock *SinglePredMBB =
96*0b57cec5SDimitry Andric getSingleSchedPred(MBB, MLI->getLoopFor(MBB));
97*0b57cec5SDimitry Andric if (SinglePredMBB == nullptr ||
98*0b57cec5SDimitry Andric SchedStates.find(SinglePredMBB) == SchedStates.end())
99*0b57cec5SDimitry Andric return;
100*0b57cec5SDimitry Andric
101*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "** Continued scheduling from "
102*0b57cec5SDimitry Andric << printMBBReference(*SinglePredMBB) << "\n";);
103*0b57cec5SDimitry Andric
104*0b57cec5SDimitry Andric HazardRec->copyState(SchedStates[SinglePredMBB]);
105*0b57cec5SDimitry Andric LLVM_DEBUG(HazardRec->dumpState(););
106*0b57cec5SDimitry Andric
107*0b57cec5SDimitry Andric // Emit incoming terminator(s). Be optimistic and assume that branch
108*0b57cec5SDimitry Andric // prediction will generally do "the right thing".
109*0b57cec5SDimitry Andric for (MachineInstr &MI : SinglePredMBB->terminators()) {
110*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "** Emitting incoming branch: "; MI.dump(););
111*0b57cec5SDimitry Andric bool TakenBranch = (MI.isBranch() &&
112*0b57cec5SDimitry Andric (TII->getBranchInfo(MI).isIndirect() ||
113*0b57cec5SDimitry Andric TII->getBranchInfo(MI).getMBBTarget() == MBB));
114*0b57cec5SDimitry Andric HazardRec->emitInstruction(&MI, TakenBranch);
115*0b57cec5SDimitry Andric if (TakenBranch)
116*0b57cec5SDimitry Andric break;
117*0b57cec5SDimitry Andric }
118*0b57cec5SDimitry Andric }
119*0b57cec5SDimitry Andric
leaveMBB()120*0b57cec5SDimitry Andric void SystemZPostRASchedStrategy::leaveMBB() {
121*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "** Leaving " << printMBBReference(*MBB) << "\n";);
122*0b57cec5SDimitry Andric
123*0b57cec5SDimitry Andric // Advance to first terminator. The successor block will handle terminators
124*0b57cec5SDimitry Andric // dependent on CFG layout (T/NT branch etc).
125*0b57cec5SDimitry Andric advanceTo(MBB->getFirstTerminator());
126*0b57cec5SDimitry Andric }
127*0b57cec5SDimitry Andric
128*0b57cec5SDimitry Andric SystemZPostRASchedStrategy::
SystemZPostRASchedStrategy(const MachineSchedContext * C)129*0b57cec5SDimitry Andric SystemZPostRASchedStrategy(const MachineSchedContext *C)
130*0b57cec5SDimitry Andric : MLI(C->MLI),
131*0b57cec5SDimitry Andric TII(static_cast<const SystemZInstrInfo *>
132*0b57cec5SDimitry Andric (C->MF->getSubtarget().getInstrInfo())),
133*0b57cec5SDimitry Andric MBB(nullptr), HazardRec(nullptr) {
134*0b57cec5SDimitry Andric const TargetSubtargetInfo *ST = &C->MF->getSubtarget();
135*0b57cec5SDimitry Andric SchedModel.init(ST);
136*0b57cec5SDimitry Andric }
137*0b57cec5SDimitry Andric
~SystemZPostRASchedStrategy()138*0b57cec5SDimitry Andric SystemZPostRASchedStrategy::~SystemZPostRASchedStrategy() {
139*0b57cec5SDimitry Andric // Delete hazard recognizers kept around for each MBB.
140*0b57cec5SDimitry Andric for (auto I : SchedStates) {
141*0b57cec5SDimitry Andric SystemZHazardRecognizer *hazrec = I.second;
142*0b57cec5SDimitry Andric delete hazrec;
143*0b57cec5SDimitry Andric }
144*0b57cec5SDimitry Andric }
145*0b57cec5SDimitry Andric
initPolicy(MachineBasicBlock::iterator Begin,MachineBasicBlock::iterator End,unsigned NumRegionInstrs)146*0b57cec5SDimitry Andric void SystemZPostRASchedStrategy::initPolicy(MachineBasicBlock::iterator Begin,
147*0b57cec5SDimitry Andric MachineBasicBlock::iterator End,
148*0b57cec5SDimitry Andric unsigned NumRegionInstrs) {
149*0b57cec5SDimitry Andric // Don't emit the terminators.
150*0b57cec5SDimitry Andric if (Begin->isTerminator())
151*0b57cec5SDimitry Andric return;
152*0b57cec5SDimitry Andric
153*0b57cec5SDimitry Andric // Emit any instructions before start of region.
154*0b57cec5SDimitry Andric advanceTo(Begin);
155*0b57cec5SDimitry Andric }
156*0b57cec5SDimitry Andric
157*0b57cec5SDimitry Andric // Pick the next node to schedule.
pickNode(bool & IsTopNode)158*0b57cec5SDimitry Andric SUnit *SystemZPostRASchedStrategy::pickNode(bool &IsTopNode) {
159*0b57cec5SDimitry Andric // Only scheduling top-down.
160*0b57cec5SDimitry Andric IsTopNode = true;
161*0b57cec5SDimitry Andric
162*0b57cec5SDimitry Andric if (Available.empty())
163*0b57cec5SDimitry Andric return nullptr;
164*0b57cec5SDimitry Andric
165*0b57cec5SDimitry Andric // If only one choice, return it.
166*0b57cec5SDimitry Andric if (Available.size() == 1) {
167*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "** Only one: ";
168*0b57cec5SDimitry Andric HazardRec->dumpSU(*Available.begin(), dbgs()); dbgs() << "\n";);
169*0b57cec5SDimitry Andric return *Available.begin();
170*0b57cec5SDimitry Andric }
171*0b57cec5SDimitry Andric
172*0b57cec5SDimitry Andric // All nodes that are possible to schedule are stored in the Available set.
173*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "** Available: "; Available.dump(*HazardRec););
174*0b57cec5SDimitry Andric
175*0b57cec5SDimitry Andric Candidate Best;
176*0b57cec5SDimitry Andric for (auto *SU : Available) {
177*0b57cec5SDimitry Andric
178*0b57cec5SDimitry Andric // SU is the next candidate to be compared against current Best.
179*0b57cec5SDimitry Andric Candidate c(SU, *HazardRec);
180*0b57cec5SDimitry Andric
181*0b57cec5SDimitry Andric // Remeber which SU is the best candidate.
182*0b57cec5SDimitry Andric if (Best.SU == nullptr || c < Best) {
183*0b57cec5SDimitry Andric Best = c;
184*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "** Best so far: ";);
185*0b57cec5SDimitry Andric } else
186*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "** Tried : ";);
187*0b57cec5SDimitry Andric LLVM_DEBUG(HazardRec->dumpSU(c.SU, dbgs()); c.dumpCosts();
188*0b57cec5SDimitry Andric dbgs() << " Height:" << c.SU->getHeight(); dbgs() << "\n";);
189*0b57cec5SDimitry Andric
190*0b57cec5SDimitry Andric // Once we know we have seen all SUs that affect grouping or use unbuffered
191*0b57cec5SDimitry Andric // resources, we can stop iterating if Best looks good.
192*0b57cec5SDimitry Andric if (!SU->isScheduleHigh && Best.noCost())
193*0b57cec5SDimitry Andric break;
194*0b57cec5SDimitry Andric }
195*0b57cec5SDimitry Andric
196*0b57cec5SDimitry Andric assert (Best.SU != nullptr);
197*0b57cec5SDimitry Andric return Best.SU;
198*0b57cec5SDimitry Andric }
199*0b57cec5SDimitry Andric
200*0b57cec5SDimitry Andric SystemZPostRASchedStrategy::Candidate::
Candidate(SUnit * SU_,SystemZHazardRecognizer & HazardRec)201*0b57cec5SDimitry Andric Candidate(SUnit *SU_, SystemZHazardRecognizer &HazardRec) : Candidate() {
202*0b57cec5SDimitry Andric SU = SU_;
203*0b57cec5SDimitry Andric
204*0b57cec5SDimitry Andric // Check the grouping cost. For a node that must begin / end a
205*0b57cec5SDimitry Andric // group, it is positive if it would do so prematurely, or negative
206*0b57cec5SDimitry Andric // if it would fit naturally into the schedule.
207*0b57cec5SDimitry Andric GroupingCost = HazardRec.groupingCost(SU);
208*0b57cec5SDimitry Andric
209*0b57cec5SDimitry Andric // Check the resources cost for this SU.
210*0b57cec5SDimitry Andric ResourcesCost = HazardRec.resourcesCost(SU);
211*0b57cec5SDimitry Andric }
212*0b57cec5SDimitry Andric
213*0b57cec5SDimitry Andric bool SystemZPostRASchedStrategy::Candidate::
operator <(const Candidate & other)214*0b57cec5SDimitry Andric operator<(const Candidate &other) {
215*0b57cec5SDimitry Andric
216*0b57cec5SDimitry Andric // Check decoder grouping.
217*0b57cec5SDimitry Andric if (GroupingCost < other.GroupingCost)
218*0b57cec5SDimitry Andric return true;
219*0b57cec5SDimitry Andric if (GroupingCost > other.GroupingCost)
220*0b57cec5SDimitry Andric return false;
221*0b57cec5SDimitry Andric
222*0b57cec5SDimitry Andric // Compare the use of resources.
223*0b57cec5SDimitry Andric if (ResourcesCost < other.ResourcesCost)
224*0b57cec5SDimitry Andric return true;
225*0b57cec5SDimitry Andric if (ResourcesCost > other.ResourcesCost)
226*0b57cec5SDimitry Andric return false;
227*0b57cec5SDimitry Andric
228*0b57cec5SDimitry Andric // Higher SU is otherwise generally better.
229*0b57cec5SDimitry Andric if (SU->getHeight() > other.SU->getHeight())
230*0b57cec5SDimitry Andric return true;
231*0b57cec5SDimitry Andric if (SU->getHeight() < other.SU->getHeight())
232*0b57cec5SDimitry Andric return false;
233*0b57cec5SDimitry Andric
234*0b57cec5SDimitry Andric // If all same, fall back to original order.
235*0b57cec5SDimitry Andric if (SU->NodeNum < other.SU->NodeNum)
236*0b57cec5SDimitry Andric return true;
237*0b57cec5SDimitry Andric
238*0b57cec5SDimitry Andric return false;
239*0b57cec5SDimitry Andric }
240*0b57cec5SDimitry Andric
schedNode(SUnit * SU,bool IsTopNode)241*0b57cec5SDimitry Andric void SystemZPostRASchedStrategy::schedNode(SUnit *SU, bool IsTopNode) {
242*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "** Scheduling SU(" << SU->NodeNum << ") ";
243*0b57cec5SDimitry Andric if (Available.size() == 1) dbgs() << "(only one) ";
244*0b57cec5SDimitry Andric Candidate c(SU, *HazardRec); c.dumpCosts(); dbgs() << "\n";);
245*0b57cec5SDimitry Andric
246*0b57cec5SDimitry Andric // Remove SU from Available set and update HazardRec.
247*0b57cec5SDimitry Andric Available.erase(SU);
248*0b57cec5SDimitry Andric HazardRec->EmitInstruction(SU);
249*0b57cec5SDimitry Andric }
250*0b57cec5SDimitry Andric
releaseTopNode(SUnit * SU)251*0b57cec5SDimitry Andric void SystemZPostRASchedStrategy::releaseTopNode(SUnit *SU) {
252*0b57cec5SDimitry Andric // Set isScheduleHigh flag on all SUs that we want to consider first in
253*0b57cec5SDimitry Andric // pickNode().
254*0b57cec5SDimitry Andric const MCSchedClassDesc *SC = HazardRec->getSchedClass(SU);
255*0b57cec5SDimitry Andric bool AffectsGrouping = (SC->isValid() && (SC->BeginGroup || SC->EndGroup));
256*0b57cec5SDimitry Andric SU->isScheduleHigh = (AffectsGrouping || SU->isUnbuffered);
257*0b57cec5SDimitry Andric
258*0b57cec5SDimitry Andric // Put all released SUs in the Available set.
259*0b57cec5SDimitry Andric Available.insert(SU);
260 }
261