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