1b915e9e0SDimitry Andric //-- SystemZMachineScheduler.cpp - SystemZ Scheduler Interface -*- C++ -*---==//
2b915e9e0SDimitry Andric //
3e6d15924SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e6d15924SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5e6d15924SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b915e9e0SDimitry Andric //
7b915e9e0SDimitry Andric //===----------------------------------------------------------------------===//
8b915e9e0SDimitry Andric //
9b915e9e0SDimitry Andric // -------------------------- Post RA scheduling ---------------------------- //
10b915e9e0SDimitry Andric // SystemZPostRASchedStrategy is a scheduling strategy which is plugged into
11b915e9e0SDimitry Andric // the MachineScheduler. It has a sorted Available set of SUs and a pickNode()
12b915e9e0SDimitry Andric // implementation that looks to optimize decoder grouping and balance the
13044eb2f6SDimitry Andric // usage of processor resources. Scheduler states are saved for the end
14044eb2f6SDimitry Andric // region of each MBB, so that a successor block can learn from it.
15b915e9e0SDimitry Andric //===----------------------------------------------------------------------===//
16b915e9e0SDimitry Andric
17b915e9e0SDimitry Andric #include "SystemZMachineScheduler.h"
181d5ae102SDimitry Andric #include "llvm/CodeGen/MachineLoopInfo.h"
19b915e9e0SDimitry Andric
20b915e9e0SDimitry Andric using namespace llvm;
21b915e9e0SDimitry Andric
22ca089b24SDimitry Andric #define DEBUG_TYPE "machine-scheduler"
23b915e9e0SDimitry Andric
24b915e9e0SDimitry Andric #ifndef NDEBUG
25b915e9e0SDimitry Andric // Print the set of SUs
26b915e9e0SDimitry Andric void SystemZPostRASchedStrategy::SUSet::
dump(SystemZHazardRecognizer & HazardRec) const2708bbd35aSDimitry Andric dump(SystemZHazardRecognizer &HazardRec) const {
28b915e9e0SDimitry Andric dbgs() << "{";
29b915e9e0SDimitry Andric for (auto &SU : *this) {
30b915e9e0SDimitry Andric HazardRec.dumpSU(SU, dbgs());
31b915e9e0SDimitry Andric if (SU != *rbegin())
32b915e9e0SDimitry Andric dbgs() << ", ";
33b915e9e0SDimitry Andric }
34b915e9e0SDimitry Andric dbgs() << "}\n";
35b915e9e0SDimitry Andric }
36b915e9e0SDimitry Andric #endif
37b915e9e0SDimitry Andric
38044eb2f6SDimitry Andric // Try to find a single predecessor that would be interesting for the
39044eb2f6SDimitry Andric // scheduler in the top-most region of MBB.
getSingleSchedPred(MachineBasicBlock * MBB,const MachineLoop * Loop)40044eb2f6SDimitry Andric static MachineBasicBlock *getSingleSchedPred(MachineBasicBlock *MBB,
41044eb2f6SDimitry Andric const MachineLoop *Loop) {
42044eb2f6SDimitry Andric MachineBasicBlock *PredMBB = nullptr;
43044eb2f6SDimitry Andric if (MBB->pred_size() == 1)
44044eb2f6SDimitry Andric PredMBB = *MBB->pred_begin();
45044eb2f6SDimitry Andric
46044eb2f6SDimitry Andric // The loop header has two predecessors, return the latch, but not for a
47044eb2f6SDimitry Andric // single block loop.
48044eb2f6SDimitry Andric if (MBB->pred_size() == 2 && Loop != nullptr && Loop->getHeader() == MBB) {
49c0981da4SDimitry Andric for (MachineBasicBlock *Pred : MBB->predecessors())
50c0981da4SDimitry Andric if (Loop->contains(Pred))
51c0981da4SDimitry Andric PredMBB = (Pred == MBB ? nullptr : Pred);
52044eb2f6SDimitry Andric }
53044eb2f6SDimitry Andric
54044eb2f6SDimitry Andric assert ((PredMBB == nullptr || !Loop || Loop->contains(PredMBB))
55044eb2f6SDimitry Andric && "Loop MBB should not consider predecessor outside of loop.");
56044eb2f6SDimitry Andric
57044eb2f6SDimitry Andric return PredMBB;
58044eb2f6SDimitry Andric }
59044eb2f6SDimitry Andric
60044eb2f6SDimitry Andric void SystemZPostRASchedStrategy::
advanceTo(MachineBasicBlock::iterator NextBegin)61044eb2f6SDimitry Andric advanceTo(MachineBasicBlock::iterator NextBegin) {
62044eb2f6SDimitry Andric MachineBasicBlock::iterator LastEmittedMI = HazardRec->getLastEmittedMI();
63044eb2f6SDimitry Andric MachineBasicBlock::iterator I =
64044eb2f6SDimitry Andric ((LastEmittedMI != nullptr && LastEmittedMI->getParent() == MBB) ?
65044eb2f6SDimitry Andric std::next(LastEmittedMI) : MBB->begin());
66044eb2f6SDimitry Andric
67044eb2f6SDimitry Andric for (; I != NextBegin; ++I) {
68eb11fae6SDimitry Andric if (I->isPosition() || I->isDebugInstr())
69044eb2f6SDimitry Andric continue;
70044eb2f6SDimitry Andric HazardRec->emitInstruction(&*I);
71044eb2f6SDimitry Andric }
72044eb2f6SDimitry Andric }
73044eb2f6SDimitry Andric
initialize(ScheduleDAGMI * dag)74eb11fae6SDimitry Andric void SystemZPostRASchedStrategy::initialize(ScheduleDAGMI *dag) {
75b60736ecSDimitry Andric Available.clear(); // -misched-cutoff.
76eb11fae6SDimitry Andric LLVM_DEBUG(HazardRec->dumpState(););
77eb11fae6SDimitry Andric }
78eb11fae6SDimitry Andric
enterMBB(MachineBasicBlock * NextMBB)79044eb2f6SDimitry Andric void SystemZPostRASchedStrategy::enterMBB(MachineBasicBlock *NextMBB) {
80044eb2f6SDimitry Andric assert ((SchedStates.find(NextMBB) == SchedStates.end()) &&
81044eb2f6SDimitry Andric "Entering MBB twice?");
82eb11fae6SDimitry Andric LLVM_DEBUG(dbgs() << "** Entering " << printMBBReference(*NextMBB));
83044eb2f6SDimitry Andric
84044eb2f6SDimitry Andric MBB = NextMBB;
85eb11fae6SDimitry Andric
86044eb2f6SDimitry Andric /// Create a HazardRec for MBB, save it in SchedStates and set HazardRec to
87044eb2f6SDimitry Andric /// point to it.
88044eb2f6SDimitry Andric HazardRec = SchedStates[MBB] = new SystemZHazardRecognizer(TII, &SchedModel);
89eb11fae6SDimitry Andric LLVM_DEBUG(const MachineLoop *Loop = MLI->getLoopFor(MBB);
90eb11fae6SDimitry Andric if (Loop && Loop->getHeader() == MBB) dbgs() << " (Loop header)";
91044eb2f6SDimitry Andric dbgs() << ":\n";);
92044eb2f6SDimitry Andric
93044eb2f6SDimitry Andric // Try to take over the state from a single predecessor, if it has been
94044eb2f6SDimitry Andric // scheduled. If this is not possible, we are done.
95044eb2f6SDimitry Andric MachineBasicBlock *SinglePredMBB =
96044eb2f6SDimitry Andric getSingleSchedPred(MBB, MLI->getLoopFor(MBB));
97044eb2f6SDimitry Andric if (SinglePredMBB == nullptr ||
98044eb2f6SDimitry Andric SchedStates.find(SinglePredMBB) == SchedStates.end())
99044eb2f6SDimitry Andric return;
100044eb2f6SDimitry Andric
101eb11fae6SDimitry Andric LLVM_DEBUG(dbgs() << "** Continued scheduling from "
102044eb2f6SDimitry Andric << printMBBReference(*SinglePredMBB) << "\n";);
103044eb2f6SDimitry Andric
104044eb2f6SDimitry Andric HazardRec->copyState(SchedStates[SinglePredMBB]);
105eb11fae6SDimitry Andric LLVM_DEBUG(HazardRec->dumpState(););
106044eb2f6SDimitry Andric
107044eb2f6SDimitry Andric // Emit incoming terminator(s). Be optimistic and assume that branch
108044eb2f6SDimitry Andric // prediction will generally do "the right thing".
109c0981da4SDimitry Andric for (MachineInstr &MI : SinglePredMBB->terminators()) {
110c0981da4SDimitry Andric LLVM_DEBUG(dbgs() << "** Emitting incoming branch: "; MI.dump(););
111c0981da4SDimitry Andric bool TakenBranch = (MI.isBranch() &&
112c0981da4SDimitry Andric (TII->getBranchInfo(MI).isIndirect() ||
113c0981da4SDimitry Andric TII->getBranchInfo(MI).getMBBTarget() == MBB));
114c0981da4SDimitry Andric HazardRec->emitInstruction(&MI, TakenBranch);
115044eb2f6SDimitry Andric if (TakenBranch)
116044eb2f6SDimitry Andric break;
117044eb2f6SDimitry Andric }
118044eb2f6SDimitry Andric }
119044eb2f6SDimitry Andric
leaveMBB()120044eb2f6SDimitry Andric void SystemZPostRASchedStrategy::leaveMBB() {
121eb11fae6SDimitry Andric LLVM_DEBUG(dbgs() << "** Leaving " << printMBBReference(*MBB) << "\n";);
122044eb2f6SDimitry Andric
123044eb2f6SDimitry Andric // Advance to first terminator. The successor block will handle terminators
124044eb2f6SDimitry Andric // dependent on CFG layout (T/NT branch etc).
125044eb2f6SDimitry Andric advanceTo(MBB->getFirstTerminator());
126044eb2f6SDimitry Andric }
127044eb2f6SDimitry Andric
128b915e9e0SDimitry Andric SystemZPostRASchedStrategy::
SystemZPostRASchedStrategy(const MachineSchedContext * C)129b915e9e0SDimitry Andric SystemZPostRASchedStrategy(const MachineSchedContext *C)
130044eb2f6SDimitry Andric : MLI(C->MLI),
131044eb2f6SDimitry Andric TII(static_cast<const SystemZInstrInfo *>
132044eb2f6SDimitry Andric (C->MF->getSubtarget().getInstrInfo())),
133044eb2f6SDimitry Andric MBB(nullptr), HazardRec(nullptr) {
134044eb2f6SDimitry Andric const TargetSubtargetInfo *ST = &C->MF->getSubtarget();
135eb11fae6SDimitry Andric SchedModel.init(ST);
136044eb2f6SDimitry Andric }
137b915e9e0SDimitry Andric
~SystemZPostRASchedStrategy()138044eb2f6SDimitry Andric SystemZPostRASchedStrategy::~SystemZPostRASchedStrategy() {
139044eb2f6SDimitry Andric // Delete hazard recognizers kept around for each MBB.
140044eb2f6SDimitry Andric for (auto I : SchedStates) {
141044eb2f6SDimitry Andric SystemZHazardRecognizer *hazrec = I.second;
142044eb2f6SDimitry Andric delete hazrec;
143044eb2f6SDimitry Andric }
144044eb2f6SDimitry Andric }
145044eb2f6SDimitry Andric
initPolicy(MachineBasicBlock::iterator Begin,MachineBasicBlock::iterator End,unsigned NumRegionInstrs)146044eb2f6SDimitry Andric void SystemZPostRASchedStrategy::initPolicy(MachineBasicBlock::iterator Begin,
147044eb2f6SDimitry Andric MachineBasicBlock::iterator End,
148044eb2f6SDimitry Andric unsigned NumRegionInstrs) {
149044eb2f6SDimitry Andric // Don't emit the terminators.
150044eb2f6SDimitry Andric if (Begin->isTerminator())
151044eb2f6SDimitry Andric return;
152044eb2f6SDimitry Andric
153044eb2f6SDimitry Andric // Emit any instructions before start of region.
154044eb2f6SDimitry Andric advanceTo(Begin);
155b915e9e0SDimitry Andric }
156b915e9e0SDimitry Andric
157b915e9e0SDimitry Andric // Pick the next node to schedule.
pickNode(bool & IsTopNode)158b915e9e0SDimitry Andric SUnit *SystemZPostRASchedStrategy::pickNode(bool &IsTopNode) {
159b915e9e0SDimitry Andric // Only scheduling top-down.
160b915e9e0SDimitry Andric IsTopNode = true;
161b915e9e0SDimitry Andric
162b915e9e0SDimitry Andric if (Available.empty())
163b915e9e0SDimitry Andric return nullptr;
164b915e9e0SDimitry Andric
165b915e9e0SDimitry Andric // If only one choice, return it.
166b915e9e0SDimitry Andric if (Available.size() == 1) {
167eb11fae6SDimitry Andric LLVM_DEBUG(dbgs() << "** Only one: ";
168044eb2f6SDimitry Andric HazardRec->dumpSU(*Available.begin(), dbgs()); dbgs() << "\n";);
169b915e9e0SDimitry Andric return *Available.begin();
170b915e9e0SDimitry Andric }
171b915e9e0SDimitry Andric
172b7eb8e35SDimitry Andric // All nodes that are possible to schedule are stored in the Available set.
173eb11fae6SDimitry Andric LLVM_DEBUG(dbgs() << "** Available: "; Available.dump(*HazardRec););
174b915e9e0SDimitry Andric
175b915e9e0SDimitry Andric Candidate Best;
176b915e9e0SDimitry Andric for (auto *SU : Available) {
177b915e9e0SDimitry Andric
178b915e9e0SDimitry Andric // SU is the next candidate to be compared against current Best.
179044eb2f6SDimitry Andric Candidate c(SU, *HazardRec);
180b915e9e0SDimitry Andric
181b915e9e0SDimitry Andric // Remeber which SU is the best candidate.
182b915e9e0SDimitry Andric if (Best.SU == nullptr || c < Best) {
183b915e9e0SDimitry Andric Best = c;
184eb11fae6SDimitry Andric LLVM_DEBUG(dbgs() << "** Best so far: ";);
185eb11fae6SDimitry Andric } else
186eb11fae6SDimitry Andric LLVM_DEBUG(dbgs() << "** Tried : ";);
187eb11fae6SDimitry Andric LLVM_DEBUG(HazardRec->dumpSU(c.SU, dbgs()); c.dumpCosts();
188eb11fae6SDimitry Andric dbgs() << " Height:" << c.SU->getHeight(); dbgs() << "\n";);
189b915e9e0SDimitry Andric
190b915e9e0SDimitry Andric // Once we know we have seen all SUs that affect grouping or use unbuffered
191b915e9e0SDimitry Andric // resources, we can stop iterating if Best looks good.
192b915e9e0SDimitry Andric if (!SU->isScheduleHigh && Best.noCost())
193b915e9e0SDimitry Andric break;
194b915e9e0SDimitry Andric }
195b915e9e0SDimitry Andric
196b915e9e0SDimitry Andric assert (Best.SU != nullptr);
197b915e9e0SDimitry Andric return Best.SU;
198b915e9e0SDimitry Andric }
199b915e9e0SDimitry Andric
200b915e9e0SDimitry Andric SystemZPostRASchedStrategy::Candidate::
Candidate(SUnit * SU_,SystemZHazardRecognizer & HazardRec)201b915e9e0SDimitry Andric Candidate(SUnit *SU_, SystemZHazardRecognizer &HazardRec) : Candidate() {
202b915e9e0SDimitry Andric SU = SU_;
203b915e9e0SDimitry Andric
204b915e9e0SDimitry Andric // Check the grouping cost. For a node that must begin / end a
205b915e9e0SDimitry Andric // group, it is positive if it would do so prematurely, or negative
206b915e9e0SDimitry Andric // if it would fit naturally into the schedule.
207b915e9e0SDimitry Andric GroupingCost = HazardRec.groupingCost(SU);
208b915e9e0SDimitry Andric
209b915e9e0SDimitry Andric // Check the resources cost for this SU.
210b915e9e0SDimitry Andric ResourcesCost = HazardRec.resourcesCost(SU);
211b915e9e0SDimitry Andric }
212b915e9e0SDimitry Andric
213b915e9e0SDimitry Andric bool SystemZPostRASchedStrategy::Candidate::
operator <(const Candidate & other)214b915e9e0SDimitry Andric operator<(const Candidate &other) {
215b915e9e0SDimitry Andric
216b915e9e0SDimitry Andric // Check decoder grouping.
217b915e9e0SDimitry Andric if (GroupingCost < other.GroupingCost)
218b915e9e0SDimitry Andric return true;
219b915e9e0SDimitry Andric if (GroupingCost > other.GroupingCost)
220b915e9e0SDimitry Andric return false;
221b915e9e0SDimitry Andric
222b915e9e0SDimitry Andric // Compare the use of resources.
223b915e9e0SDimitry Andric if (ResourcesCost < other.ResourcesCost)
224b915e9e0SDimitry Andric return true;
225b915e9e0SDimitry Andric if (ResourcesCost > other.ResourcesCost)
226b915e9e0SDimitry Andric return false;
227b915e9e0SDimitry Andric
228b915e9e0SDimitry Andric // Higher SU is otherwise generally better.
229b915e9e0SDimitry Andric if (SU->getHeight() > other.SU->getHeight())
230b915e9e0SDimitry Andric return true;
231b915e9e0SDimitry Andric if (SU->getHeight() < other.SU->getHeight())
232b915e9e0SDimitry Andric return false;
233b915e9e0SDimitry Andric
234b915e9e0SDimitry Andric // If all same, fall back to original order.
235b915e9e0SDimitry Andric if (SU->NodeNum < other.SU->NodeNum)
236b915e9e0SDimitry Andric return true;
237b915e9e0SDimitry Andric
238b915e9e0SDimitry Andric return false;
239b915e9e0SDimitry Andric }
240b915e9e0SDimitry Andric
schedNode(SUnit * SU,bool IsTopNode)241b915e9e0SDimitry Andric void SystemZPostRASchedStrategy::schedNode(SUnit *SU, bool IsTopNode) {
242eb11fae6SDimitry Andric LLVM_DEBUG(dbgs() << "** Scheduling SU(" << SU->NodeNum << ") ";
243eb11fae6SDimitry Andric if (Available.size() == 1) dbgs() << "(only one) ";
244eb11fae6SDimitry Andric Candidate c(SU, *HazardRec); c.dumpCosts(); dbgs() << "\n";);
245b915e9e0SDimitry Andric
246b915e9e0SDimitry Andric // Remove SU from Available set and update HazardRec.
247b915e9e0SDimitry Andric Available.erase(SU);
248044eb2f6SDimitry Andric HazardRec->EmitInstruction(SU);
249b915e9e0SDimitry Andric }
250b915e9e0SDimitry Andric
releaseTopNode(SUnit * SU)251b915e9e0SDimitry Andric void SystemZPostRASchedStrategy::releaseTopNode(SUnit *SU) {
252b915e9e0SDimitry Andric // Set isScheduleHigh flag on all SUs that we want to consider first in
253b915e9e0SDimitry Andric // pickNode().
254044eb2f6SDimitry Andric const MCSchedClassDesc *SC = HazardRec->getSchedClass(SU);
255b915e9e0SDimitry Andric bool AffectsGrouping = (SC->isValid() && (SC->BeginGroup || SC->EndGroup));
256b915e9e0SDimitry Andric SU->isScheduleHigh = (AffectsGrouping || SU->isUnbuffered);
257b915e9e0SDimitry Andric
258b915e9e0SDimitry Andric // Put all released SUs in the Available set.
259b915e9e0SDimitry Andric Available.insert(SU);
260b915e9e0SDimitry Andric }
261