1*0b57cec5SDimitry Andric //===- HexagonMachineScheduler.h - Custom Hexagon MI scheduler --*- 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 // Custom Hexagon MI scheduler. 10*0b57cec5SDimitry Andric // 11*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 12*0b57cec5SDimitry Andric 13*0b57cec5SDimitry Andric #ifndef LLVM_LIB_TARGET_HEXAGON_HEXAGONMACHINESCHEDULER_H 14*0b57cec5SDimitry Andric #define LLVM_LIB_TARGET_HEXAGON_HEXAGONMACHINESCHEDULER_H 15*0b57cec5SDimitry Andric 16*0b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h" 17*0b57cec5SDimitry Andric #include "llvm/ADT/Twine.h" 18*0b57cec5SDimitry Andric #include "llvm/CodeGen/DFAPacketizer.h" 19*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineScheduler.h" 20*0b57cec5SDimitry Andric #include "llvm/CodeGen/RegisterPressure.h" 21*0b57cec5SDimitry Andric #include "llvm/CodeGen/ScheduleHazardRecognizer.h" 22*0b57cec5SDimitry Andric #include "llvm/CodeGen/TargetInstrInfo.h" 23*0b57cec5SDimitry Andric #include "llvm/CodeGen/TargetSchedule.h" 24*0b57cec5SDimitry Andric #include "llvm/CodeGen/TargetSubtargetInfo.h" 25*0b57cec5SDimitry Andric #include <algorithm> 26*0b57cec5SDimitry Andric #include <cassert> 27*0b57cec5SDimitry Andric #include <limits> 28*0b57cec5SDimitry Andric #include <memory> 29*0b57cec5SDimitry Andric #include <vector> 30*0b57cec5SDimitry Andric 31*0b57cec5SDimitry Andric namespace llvm { 32*0b57cec5SDimitry Andric 33*0b57cec5SDimitry Andric class SUnit; 34*0b57cec5SDimitry Andric 35*0b57cec5SDimitry Andric class VLIWResourceModel { 36*0b57cec5SDimitry Andric /// ResourcesModel - Represents VLIW state. 37*0b57cec5SDimitry Andric /// Not limited to VLIW targets per se, but assumes 38*0b57cec5SDimitry Andric /// definition of DFA by a target. 39*0b57cec5SDimitry Andric DFAPacketizer *ResourcesModel; 40*0b57cec5SDimitry Andric 41*0b57cec5SDimitry Andric const TargetSchedModel *SchedModel; 42*0b57cec5SDimitry Andric 43*0b57cec5SDimitry Andric /// Local packet/bundle model. Purely 44*0b57cec5SDimitry Andric /// internal to the MI schedulre at the time. 45*0b57cec5SDimitry Andric std::vector<SUnit *> Packet; 46*0b57cec5SDimitry Andric 47*0b57cec5SDimitry Andric /// Total packets created. 48*0b57cec5SDimitry Andric unsigned TotalPackets = 0; 49*0b57cec5SDimitry Andric 50*0b57cec5SDimitry Andric public: 51*0b57cec5SDimitry Andric VLIWResourceModel(const TargetSubtargetInfo &STI, const TargetSchedModel *SM) 52*0b57cec5SDimitry Andric : SchedModel(SM) { 53*0b57cec5SDimitry Andric ResourcesModel = STI.getInstrInfo()->CreateTargetScheduleState(STI); 54*0b57cec5SDimitry Andric 55*0b57cec5SDimitry Andric // This hard requirement could be relaxed, 56*0b57cec5SDimitry Andric // but for now do not let it proceed. 57*0b57cec5SDimitry Andric assert(ResourcesModel && "Unimplemented CreateTargetScheduleState."); 58*0b57cec5SDimitry Andric 59*0b57cec5SDimitry Andric Packet.resize(SchedModel->getIssueWidth()); 60*0b57cec5SDimitry Andric Packet.clear(); 61*0b57cec5SDimitry Andric ResourcesModel->clearResources(); 62*0b57cec5SDimitry Andric } 63*0b57cec5SDimitry Andric 64*0b57cec5SDimitry Andric ~VLIWResourceModel() { 65*0b57cec5SDimitry Andric delete ResourcesModel; 66*0b57cec5SDimitry Andric } 67*0b57cec5SDimitry Andric 68*0b57cec5SDimitry Andric void resetPacketState() { 69*0b57cec5SDimitry Andric Packet.clear(); 70*0b57cec5SDimitry Andric } 71*0b57cec5SDimitry Andric 72*0b57cec5SDimitry Andric void resetDFA() { 73*0b57cec5SDimitry Andric ResourcesModel->clearResources(); 74*0b57cec5SDimitry Andric } 75*0b57cec5SDimitry Andric 76*0b57cec5SDimitry Andric void reset() { 77*0b57cec5SDimitry Andric Packet.clear(); 78*0b57cec5SDimitry Andric ResourcesModel->clearResources(); 79*0b57cec5SDimitry Andric } 80*0b57cec5SDimitry Andric 81*0b57cec5SDimitry Andric bool isResourceAvailable(SUnit *SU, bool IsTop); 82*0b57cec5SDimitry Andric bool reserveResources(SUnit *SU, bool IsTop); 83*0b57cec5SDimitry Andric unsigned getTotalPackets() const { return TotalPackets; } 84*0b57cec5SDimitry Andric bool isInPacket(SUnit *SU) const { return is_contained(Packet, SU); } 85*0b57cec5SDimitry Andric }; 86*0b57cec5SDimitry Andric 87*0b57cec5SDimitry Andric /// Extend the standard ScheduleDAGMI to provide more context and override the 88*0b57cec5SDimitry Andric /// top-level schedule() driver. 89*0b57cec5SDimitry Andric class VLIWMachineScheduler : public ScheduleDAGMILive { 90*0b57cec5SDimitry Andric public: 91*0b57cec5SDimitry Andric VLIWMachineScheduler(MachineSchedContext *C, 92*0b57cec5SDimitry Andric std::unique_ptr<MachineSchedStrategy> S) 93*0b57cec5SDimitry Andric : ScheduleDAGMILive(C, std::move(S)) {} 94*0b57cec5SDimitry Andric 95*0b57cec5SDimitry Andric /// Schedule - This is called back from ScheduleDAGInstrs::Run() when it's 96*0b57cec5SDimitry Andric /// time to do some work. 97*0b57cec5SDimitry Andric void schedule() override; 98*0b57cec5SDimitry Andric 99*0b57cec5SDimitry Andric RegisterClassInfo *getRegClassInfo() { return RegClassInfo; } 100*0b57cec5SDimitry Andric int getBBSize() { return BB->size(); } 101*0b57cec5SDimitry Andric }; 102*0b57cec5SDimitry Andric 103*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 104*0b57cec5SDimitry Andric // ConvergingVLIWScheduler - Implementation of the standard 105*0b57cec5SDimitry Andric // MachineSchedStrategy. 106*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 107*0b57cec5SDimitry Andric 108*0b57cec5SDimitry Andric /// ConvergingVLIWScheduler shrinks the unscheduled zone using heuristics 109*0b57cec5SDimitry Andric /// to balance the schedule. 110*0b57cec5SDimitry Andric class ConvergingVLIWScheduler : public MachineSchedStrategy { 111*0b57cec5SDimitry Andric /// Store the state used by ConvergingVLIWScheduler heuristics, required 112*0b57cec5SDimitry Andric /// for the lifetime of one invocation of pickNode(). 113*0b57cec5SDimitry Andric struct SchedCandidate { 114*0b57cec5SDimitry Andric // The best SUnit candidate. 115*0b57cec5SDimitry Andric SUnit *SU = nullptr; 116*0b57cec5SDimitry Andric 117*0b57cec5SDimitry Andric // Register pressure values for the best candidate. 118*0b57cec5SDimitry Andric RegPressureDelta RPDelta; 119*0b57cec5SDimitry Andric 120*0b57cec5SDimitry Andric // Best scheduling cost. 121*0b57cec5SDimitry Andric int SCost = 0; 122*0b57cec5SDimitry Andric 123*0b57cec5SDimitry Andric SchedCandidate() = default; 124*0b57cec5SDimitry Andric }; 125*0b57cec5SDimitry Andric /// Represent the type of SchedCandidate found within a single queue. 126*0b57cec5SDimitry Andric enum CandResult { 127*0b57cec5SDimitry Andric NoCand, NodeOrder, SingleExcess, SingleCritical, SingleMax, MultiPressure, 128*0b57cec5SDimitry Andric BestCost, Weak}; 129*0b57cec5SDimitry Andric 130*0b57cec5SDimitry Andric /// Each Scheduling boundary is associated with ready queues. It tracks the 131*0b57cec5SDimitry Andric /// current cycle in whichever direction at has moved, and maintains the state 132*0b57cec5SDimitry Andric /// of "hazards" and other interlocks at the current cycle. 133*0b57cec5SDimitry Andric struct VLIWSchedBoundary { 134*0b57cec5SDimitry Andric VLIWMachineScheduler *DAG = nullptr; 135*0b57cec5SDimitry Andric const TargetSchedModel *SchedModel = nullptr; 136*0b57cec5SDimitry Andric 137*0b57cec5SDimitry Andric ReadyQueue Available; 138*0b57cec5SDimitry Andric ReadyQueue Pending; 139*0b57cec5SDimitry Andric bool CheckPending = false; 140*0b57cec5SDimitry Andric 141*0b57cec5SDimitry Andric ScheduleHazardRecognizer *HazardRec = nullptr; 142*0b57cec5SDimitry Andric VLIWResourceModel *ResourceModel = nullptr; 143*0b57cec5SDimitry Andric 144*0b57cec5SDimitry Andric unsigned CurrCycle = 0; 145*0b57cec5SDimitry Andric unsigned IssueCount = 0; 146*0b57cec5SDimitry Andric unsigned CriticalPathLength = 0; 147*0b57cec5SDimitry Andric 148*0b57cec5SDimitry Andric /// MinReadyCycle - Cycle of the soonest available instruction. 149*0b57cec5SDimitry Andric unsigned MinReadyCycle = std::numeric_limits<unsigned>::max(); 150*0b57cec5SDimitry Andric 151*0b57cec5SDimitry Andric // Remember the greatest min operand latency. 152*0b57cec5SDimitry Andric unsigned MaxMinLatency = 0; 153*0b57cec5SDimitry Andric 154*0b57cec5SDimitry Andric /// Pending queues extend the ready queues with the same ID and the 155*0b57cec5SDimitry Andric /// PendingFlag set. 156*0b57cec5SDimitry Andric VLIWSchedBoundary(unsigned ID, const Twine &Name) 157*0b57cec5SDimitry Andric : Available(ID, Name+".A"), 158*0b57cec5SDimitry Andric Pending(ID << ConvergingVLIWScheduler::LogMaxQID, Name+".P") {} 159*0b57cec5SDimitry Andric 160*0b57cec5SDimitry Andric ~VLIWSchedBoundary() { 161*0b57cec5SDimitry Andric delete ResourceModel; 162*0b57cec5SDimitry Andric delete HazardRec; 163*0b57cec5SDimitry Andric } 164*0b57cec5SDimitry Andric 165*0b57cec5SDimitry Andric void init(VLIWMachineScheduler *dag, const TargetSchedModel *smodel) { 166*0b57cec5SDimitry Andric DAG = dag; 167*0b57cec5SDimitry Andric SchedModel = smodel; 168*0b57cec5SDimitry Andric CurrCycle = 0; 169*0b57cec5SDimitry Andric IssueCount = 0; 170*0b57cec5SDimitry Andric // Initialize the critical path length limit, which used by the scheduling 171*0b57cec5SDimitry Andric // cost model to determine the value for scheduling an instruction. We use 172*0b57cec5SDimitry Andric // a slightly different heuristic for small and large functions. For small 173*0b57cec5SDimitry Andric // functions, it's important to use the height/depth of the instruction. 174*0b57cec5SDimitry Andric // For large functions, prioritizing by height or depth increases spills. 175*0b57cec5SDimitry Andric CriticalPathLength = DAG->getBBSize() / SchedModel->getIssueWidth(); 176*0b57cec5SDimitry Andric if (DAG->getBBSize() < 50) 177*0b57cec5SDimitry Andric // We divide by two as a cheap and simple heuristic to reduce the 178*0b57cec5SDimitry Andric // critcal path length, which increases the priority of using the graph 179*0b57cec5SDimitry Andric // height/depth in the scheduler's cost computation. 180*0b57cec5SDimitry Andric CriticalPathLength >>= 1; 181*0b57cec5SDimitry Andric else { 182*0b57cec5SDimitry Andric // For large basic blocks, we prefer a larger critical path length to 183*0b57cec5SDimitry Andric // decrease the priority of using the graph height/depth. 184*0b57cec5SDimitry Andric unsigned MaxPath = 0; 185*0b57cec5SDimitry Andric for (auto &SU : DAG->SUnits) 186*0b57cec5SDimitry Andric MaxPath = std::max(MaxPath, isTop() ? SU.getHeight() : SU.getDepth()); 187*0b57cec5SDimitry Andric CriticalPathLength = std::max(CriticalPathLength, MaxPath) + 1; 188*0b57cec5SDimitry Andric } 189*0b57cec5SDimitry Andric } 190*0b57cec5SDimitry Andric 191*0b57cec5SDimitry Andric bool isTop() const { 192*0b57cec5SDimitry Andric return Available.getID() == ConvergingVLIWScheduler::TopQID; 193*0b57cec5SDimitry Andric } 194*0b57cec5SDimitry Andric 195*0b57cec5SDimitry Andric bool checkHazard(SUnit *SU); 196*0b57cec5SDimitry Andric 197*0b57cec5SDimitry Andric void releaseNode(SUnit *SU, unsigned ReadyCycle); 198*0b57cec5SDimitry Andric 199*0b57cec5SDimitry Andric void bumpCycle(); 200*0b57cec5SDimitry Andric 201*0b57cec5SDimitry Andric void bumpNode(SUnit *SU); 202*0b57cec5SDimitry Andric 203*0b57cec5SDimitry Andric void releasePending(); 204*0b57cec5SDimitry Andric 205*0b57cec5SDimitry Andric void removeReady(SUnit *SU); 206*0b57cec5SDimitry Andric 207*0b57cec5SDimitry Andric SUnit *pickOnlyChoice(); 208*0b57cec5SDimitry Andric 209*0b57cec5SDimitry Andric bool isLatencyBound(SUnit *SU) { 210*0b57cec5SDimitry Andric if (CurrCycle >= CriticalPathLength) 211*0b57cec5SDimitry Andric return true; 212*0b57cec5SDimitry Andric unsigned PathLength = isTop() ? SU->getHeight() : SU->getDepth(); 213*0b57cec5SDimitry Andric return CriticalPathLength - CurrCycle <= PathLength; 214*0b57cec5SDimitry Andric } 215*0b57cec5SDimitry Andric }; 216*0b57cec5SDimitry Andric 217*0b57cec5SDimitry Andric VLIWMachineScheduler *DAG = nullptr; 218*0b57cec5SDimitry Andric const TargetSchedModel *SchedModel = nullptr; 219*0b57cec5SDimitry Andric 220*0b57cec5SDimitry Andric // State of the top and bottom scheduled instruction boundaries. 221*0b57cec5SDimitry Andric VLIWSchedBoundary Top; 222*0b57cec5SDimitry Andric VLIWSchedBoundary Bot; 223*0b57cec5SDimitry Andric 224*0b57cec5SDimitry Andric /// List of pressure sets that have a high pressure level in the region. 225*0b57cec5SDimitry Andric std::vector<bool> HighPressureSets; 226*0b57cec5SDimitry Andric 227*0b57cec5SDimitry Andric public: 228*0b57cec5SDimitry Andric /// SUnit::NodeQueueId: 0 (none), 1 (top), 2 (bot), 3 (both) 229*0b57cec5SDimitry Andric enum { 230*0b57cec5SDimitry Andric TopQID = 1, 231*0b57cec5SDimitry Andric BotQID = 2, 232*0b57cec5SDimitry Andric LogMaxQID = 2 233*0b57cec5SDimitry Andric }; 234*0b57cec5SDimitry Andric 235*0b57cec5SDimitry Andric ConvergingVLIWScheduler() : Top(TopQID, "TopQ"), Bot(BotQID, "BotQ") {} 236*0b57cec5SDimitry Andric 237*0b57cec5SDimitry Andric void initialize(ScheduleDAGMI *dag) override; 238*0b57cec5SDimitry Andric 239*0b57cec5SDimitry Andric SUnit *pickNode(bool &IsTopNode) override; 240*0b57cec5SDimitry Andric 241*0b57cec5SDimitry Andric void schedNode(SUnit *SU, bool IsTopNode) override; 242*0b57cec5SDimitry Andric 243*0b57cec5SDimitry Andric void releaseTopNode(SUnit *SU) override; 244*0b57cec5SDimitry Andric 245*0b57cec5SDimitry Andric void releaseBottomNode(SUnit *SU) override; 246*0b57cec5SDimitry Andric 247*0b57cec5SDimitry Andric unsigned reportPackets() { 248*0b57cec5SDimitry Andric return Top.ResourceModel->getTotalPackets() + 249*0b57cec5SDimitry Andric Bot.ResourceModel->getTotalPackets(); 250*0b57cec5SDimitry Andric } 251*0b57cec5SDimitry Andric 252*0b57cec5SDimitry Andric protected: 253*0b57cec5SDimitry Andric SUnit *pickNodeBidrectional(bool &IsTopNode); 254*0b57cec5SDimitry Andric 255*0b57cec5SDimitry Andric int pressureChange(const SUnit *SU, bool isBotUp); 256*0b57cec5SDimitry Andric 257*0b57cec5SDimitry Andric int SchedulingCost(ReadyQueue &Q, 258*0b57cec5SDimitry Andric SUnit *SU, SchedCandidate &Candidate, 259*0b57cec5SDimitry Andric RegPressureDelta &Delta, bool verbose); 260*0b57cec5SDimitry Andric 261*0b57cec5SDimitry Andric CandResult pickNodeFromQueue(VLIWSchedBoundary &Zone, 262*0b57cec5SDimitry Andric const RegPressureTracker &RPTracker, 263*0b57cec5SDimitry Andric SchedCandidate &Candidate); 264*0b57cec5SDimitry Andric #ifndef NDEBUG 265*0b57cec5SDimitry Andric void traceCandidate(const char *Label, const ReadyQueue &Q, SUnit *SU, 266*0b57cec5SDimitry Andric int Cost, PressureChange P = PressureChange()); 267*0b57cec5SDimitry Andric 268*0b57cec5SDimitry Andric void readyQueueVerboseDump(const RegPressureTracker &RPTracker, 269*0b57cec5SDimitry Andric SchedCandidate &Candidate, ReadyQueue &Q); 270*0b57cec5SDimitry Andric #endif 271*0b57cec5SDimitry Andric }; 272*0b57cec5SDimitry Andric 273*0b57cec5SDimitry Andric } // end namespace llvm 274*0b57cec5SDimitry Andric 275*0b57cec5SDimitry Andric #endif // LLVM_LIB_TARGET_HEXAGON_HEXAGONMACHINESCHEDULER_H 276