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