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