| //===-- HexagonMachineScheduler.h - Custom Hexagon MI scheduler. ----===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Custom Hexagon MI scheduler. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef HEXAGONASMPRINTER_H |
| #define HEXAGONASMPRINTER_H |
| |
| #include "llvm/CodeGen/LiveIntervalAnalysis.h" |
| #include "llvm/CodeGen/MachineScheduler.h" |
| #include "llvm/CodeGen/Passes.h" |
| #include "llvm/CodeGen/RegisterClassInfo.h" |
| #include "llvm/CodeGen/RegisterPressure.h" |
| #include "llvm/CodeGen/ResourcePriorityQueue.h" |
| #include "llvm/CodeGen/ScheduleDAGInstrs.h" |
| #include "llvm/CodeGen/ScheduleHazardRecognizer.h" |
| #include "llvm/Analysis/AliasAnalysis.h" |
| #include "llvm/Target/TargetInstrInfo.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include "llvm/ADT/OwningPtr.h" |
| #include "llvm/ADT/PriorityQueue.h" |
| |
| using namespace llvm; |
| |
| //===----------------------------------------------------------------------===// |
| // MachineSchedStrategy - Interface to a machine scheduling algorithm. |
| //===----------------------------------------------------------------------===// |
| |
| namespace llvm { |
| class VLIWMachineScheduler; |
| |
| /// MachineSchedStrategy - Interface used by VLIWMachineScheduler to drive the selected |
| /// scheduling algorithm. |
| /// |
| /// If this works well and targets wish to reuse VLIWMachineScheduler, we may expose it |
| /// in ScheduleDAGInstrs.h |
| class MachineSchedStrategy { |
| public: |
| virtual ~MachineSchedStrategy() {} |
| |
| /// Initialize the strategy after building the DAG for a new region. |
| virtual void initialize(VLIWMachineScheduler *DAG) = 0; |
| |
| /// Pick the next node to schedule, or return NULL. Set IsTopNode to true to |
| /// schedule the node at the top of the unscheduled region. Otherwise it will |
| /// be scheduled at the bottom. |
| virtual SUnit *pickNode(bool &IsTopNode) = 0; |
| |
| /// Notify MachineSchedStrategy that VLIWMachineScheduler has scheduled a node. |
| virtual void schedNode(SUnit *SU, bool IsTopNode) = 0; |
| |
| /// When all predecessor dependencies have been resolved, free this node for |
| /// top-down scheduling. |
| virtual void releaseTopNode(SUnit *SU) = 0; |
| /// When all successor dependencies have been resolved, free this node for |
| /// bottom-up scheduling. |
| virtual void releaseBottomNode(SUnit *SU) = 0; |
| }; |
| |
| //===----------------------------------------------------------------------===// |
| // ConvergingVLIWScheduler - Implementation of the standard MachineSchedStrategy. |
| //===----------------------------------------------------------------------===// |
| |
| /// ReadyQueue encapsulates vector of "ready" SUnits with basic convenience |
| /// methods for pushing and removing nodes. ReadyQueue's are uniquely identified |
| /// by an ID. SUnit::NodeQueueId is a mask of the ReadyQueues the SUnit is in. |
| class ReadyQueue { |
| unsigned ID; |
| std::string Name; |
| std::vector<SUnit*> Queue; |
| |
| public: |
| ReadyQueue(unsigned id, const Twine &name): ID(id), Name(name.str()) {} |
| |
| unsigned getID() const { return ID; } |
| |
| StringRef getName() const { return Name; } |
| |
| // SU is in this queue if it's NodeQueueID is a superset of this ID. |
| bool isInQueue(SUnit *SU) const { return (SU->NodeQueueId & ID); } |
| |
| bool empty() const { return Queue.empty(); } |
| |
| unsigned size() const { return Queue.size(); } |
| |
| typedef std::vector<SUnit*>::iterator iterator; |
| |
| iterator begin() { return Queue.begin(); } |
| |
| iterator end() { return Queue.end(); } |
| |
| iterator find(SUnit *SU) { |
| return std::find(Queue.begin(), Queue.end(), SU); |
| } |
| |
| void push(SUnit *SU) { |
| Queue.push_back(SU); |
| SU->NodeQueueId |= ID; |
| } |
| |
| void remove(iterator I) { |
| (*I)->NodeQueueId &= ~ID; |
| *I = Queue.back(); |
| Queue.pop_back(); |
| } |
| |
| void dump() { |
| dbgs() << Name << ": "; |
| for (unsigned i = 0, e = Queue.size(); i < e; ++i) |
| dbgs() << Queue[i]->NodeNum << " "; |
| dbgs() << "\n"; |
| } |
| }; |
| |
| /// ConvergingVLIWScheduler shrinks the unscheduled zone using heuristics to balance |
| /// the schedule. |
| class ConvergingVLIWScheduler : public MachineSchedStrategy { |
| |
| /// Store the state used by ConvergingVLIWScheduler heuristics, required for the |
| /// lifetime of one invocation of pickNode(). |
| struct SchedCandidate { |
| // The best SUnit candidate. |
| SUnit *SU; |
| |
| // Register pressure values for the best candidate. |
| RegPressureDelta RPDelta; |
| |
| // Best scheduling cost. |
| int SCost; |
| |
| SchedCandidate(): SU(NULL), SCost(0) {} |
| }; |
| /// Represent the type of SchedCandidate found within a single queue. |
| enum CandResult { |
| NoCand, NodeOrder, SingleExcess, SingleCritical, SingleMax, MultiPressure, |
| BestCost}; |
| |
| /// Each Scheduling boundary is associated with ready queues. It tracks the |
| /// current cycle in whichever direction at has moved, and maintains the state |
| /// of "hazards" and other interlocks at the current cycle. |
| struct SchedBoundary { |
| VLIWMachineScheduler *DAG; |
| |
| ReadyQueue Available; |
| ReadyQueue Pending; |
| bool CheckPending; |
| |
| ScheduleHazardRecognizer *HazardRec; |
| |
| unsigned CurrCycle; |
| unsigned IssueCount; |
| |
| /// MinReadyCycle - Cycle of the soonest available instruction. |
| unsigned MinReadyCycle; |
| |
| // Remember the greatest min operand latency. |
| unsigned MaxMinLatency; |
| |
| /// Pending queues extend the ready queues with the same ID and the |
| /// PendingFlag set. |
| SchedBoundary(unsigned ID, const Twine &Name): |
| DAG(0), Available(ID, Name+".A"), |
| Pending(ID << ConvergingVLIWScheduler::LogMaxQID, Name+".P"), |
| CheckPending(false), HazardRec(0), CurrCycle(0), IssueCount(0), |
| MinReadyCycle(UINT_MAX), MaxMinLatency(0) {} |
| |
| ~SchedBoundary() { delete HazardRec; } |
| |
| bool isTop() const { |
| return Available.getID() == ConvergingVLIWScheduler::TopQID; |
| } |
| |
| bool checkHazard(SUnit *SU); |
| |
| void releaseNode(SUnit *SU, unsigned ReadyCycle); |
| |
| void bumpCycle(); |
| |
| void bumpNode(SUnit *SU); |
| |
| void releasePending(); |
| |
| void removeReady(SUnit *SU); |
| |
| SUnit *pickOnlyChoice(); |
| }; |
| |
| VLIWMachineScheduler *DAG; |
| const TargetRegisterInfo *TRI; |
| |
| // State of the top and bottom scheduled instruction boundaries. |
| SchedBoundary Top; |
| SchedBoundary Bot; |
| |
| public: |
| /// SUnit::NodeQueueId: 0 (none), 1 (top), 2 (bot), 3 (both) |
| enum { |
| TopQID = 1, |
| BotQID = 2, |
| LogMaxQID = 2 |
| }; |
| |
| ConvergingVLIWScheduler(): |
| DAG(0), TRI(0), Top(TopQID, "TopQ"), Bot(BotQID, "BotQ") {} |
| |
| virtual void initialize(VLIWMachineScheduler *dag); |
| |
| virtual SUnit *pickNode(bool &IsTopNode); |
| |
| virtual void schedNode(SUnit *SU, bool IsTopNode); |
| |
| virtual void releaseTopNode(SUnit *SU); |
| |
| virtual void releaseBottomNode(SUnit *SU); |
| |
| protected: |
| SUnit *pickNodeBidrectional(bool &IsTopNode); |
| |
| int SchedulingCost(ReadyQueue &Q, |
| SUnit *SU, SchedCandidate &Candidate, |
| RegPressureDelta &Delta, bool verbose); |
| |
| CandResult pickNodeFromQueue(ReadyQueue &Q, |
| const RegPressureTracker &RPTracker, |
| SchedCandidate &Candidate); |
| #ifndef NDEBUG |
| void traceCandidate(const char *Label, const ReadyQueue &Q, SUnit *SU, |
| PressureElement P = PressureElement()); |
| #endif |
| }; |
| |
| class VLIWResourceModel { |
| /// ResourcesModel - Represents VLIW state. |
| /// Not limited to VLIW targets per say, but assumes |
| /// definition of DFA by a target. |
| DFAPacketizer *ResourcesModel; |
| |
| const InstrItineraryData *InstrItins; |
| |
| /// Local packet/bundle model. Purely |
| /// internal to the MI schedulre at the time. |
| std::vector<SUnit*> Packet; |
| |
| /// Total packets created. |
| unsigned TotalPackets; |
| |
| public: |
| VLIWResourceModel(MachineSchedContext *C, const InstrItineraryData *IID) : |
| InstrItins(IID), TotalPackets(0) { |
| const TargetMachine &TM = C->MF->getTarget(); |
| ResourcesModel = TM.getInstrInfo()->CreateTargetScheduleState(&TM,NULL); |
| |
| // This hard requirement could be relaxed, but for now do not let it proceed. |
| assert(ResourcesModel && "Unimplemented CreateTargetScheduleState."); |
| |
| Packet.resize(InstrItins->SchedModel->IssueWidth); |
| Packet.clear(); |
| ResourcesModel->clearResources(); |
| } |
| |
| ~VLIWResourceModel() { |
| delete ResourcesModel; |
| } |
| |
| void resetPacketState() { |
| Packet.clear(); |
| } |
| |
| void resetDFA() { |
| ResourcesModel->clearResources(); |
| } |
| |
| bool isResourceAvailable(SUnit *SU); |
| void reserveResources(SUnit *SU); |
| unsigned getTotalPackets() const { return TotalPackets; } |
| }; |
| |
| class VLIWMachineScheduler : public ScheduleDAGInstrs { |
| /// AA - AliasAnalysis for making memory reference queries. |
| AliasAnalysis *AA; |
| |
| RegisterClassInfo *RegClassInfo; |
| MachineSchedStrategy *SchedImpl; |
| |
| /// state separatly for top/bottom sectioins. |
| VLIWResourceModel *TopResourceModel; |
| VLIWResourceModel *BotResourceModel; |
| |
| MachineBasicBlock::iterator LiveRegionEnd; |
| |
| /// Register pressure in this region computed by buildSchedGraph. |
| IntervalPressure RegPressure; |
| RegPressureTracker RPTracker; |
| |
| /// List of pressure sets that exceed the target's pressure limit before |
| /// scheduling, listed in increasing set ID order. Each pressure set is paired |
| /// with its max pressure in the currently scheduled regions. |
| std::vector<PressureElement> RegionCriticalPSets; |
| |
| /// The top of the unscheduled zone. |
| MachineBasicBlock::iterator CurrentTop; |
| IntervalPressure TopPressure; |
| RegPressureTracker TopRPTracker; |
| |
| /// The bottom of the unscheduled zone. |
| MachineBasicBlock::iterator CurrentBottom; |
| IntervalPressure BotPressure; |
| RegPressureTracker BotRPTracker; |
| |
| #ifndef NDEBUG |
| /// The number of instructions scheduled so far. Used to cut off the |
| /// scheduler at the point determined by misched-cutoff. |
| unsigned NumInstrsScheduled; |
| #endif |
| |
| /// Total packets in the region. |
| unsigned TotalPackets; |
| |
| const MachineLoopInfo *MLI; |
| public: |
| VLIWMachineScheduler(MachineSchedContext *C, MachineSchedStrategy *S): |
| ScheduleDAGInstrs(*C->MF, *C->MLI, *C->MDT, /*IsPostRA=*/false, C->LIS), |
| AA(C->AA), RegClassInfo(C->RegClassInfo), SchedImpl(S), |
| RPTracker(RegPressure), CurrentTop(), TopRPTracker(TopPressure), |
| CurrentBottom(), BotRPTracker(BotPressure), MLI(C->MLI) { |
| |
| TopResourceModel = new VLIWResourceModel(C, InstrItins); |
| BotResourceModel = new VLIWResourceModel(C, InstrItins); |
| |
| #ifndef NDEBUG |
| NumInstrsScheduled = 0; |
| #endif |
| TotalPackets = 0; |
| } |
| |
| virtual ~VLIWMachineScheduler() { |
| delete SchedImpl; |
| delete TopResourceModel; |
| delete BotResourceModel; |
| } |
| |
| MachineBasicBlock::iterator top() const { return CurrentTop; } |
| MachineBasicBlock::iterator bottom() const { return CurrentBottom; } |
| |
| /// Implement the ScheduleDAGInstrs interface for handling the next scheduling |
| /// region. This covers all instructions in a block, while schedule() may only |
| /// cover a subset. |
| void enterRegion(MachineBasicBlock *bb, |
| MachineBasicBlock::iterator begin, |
| MachineBasicBlock::iterator end, |
| unsigned endcount); |
| |
| /// Schedule - This is called back from ScheduleDAGInstrs::Run() when it's |
| /// time to do some work. |
| void schedule(); |
| |
| unsigned CurCycle; |
| |
| /// Get current register pressure for the top scheduled instructions. |
| const IntervalPressure &getTopPressure() const { return TopPressure; } |
| const RegPressureTracker &getTopRPTracker() const { return TopRPTracker; } |
| |
| /// Get current register pressure for the bottom scheduled instructions. |
| const IntervalPressure &getBotPressure() const { return BotPressure; } |
| const RegPressureTracker &getBotRPTracker() const { return BotRPTracker; } |
| |
| /// Get register pressure for the entire scheduling region before scheduling. |
| const IntervalPressure &getRegPressure() const { return RegPressure; } |
| |
| const std::vector<PressureElement> &getRegionCriticalPSets() const { |
| return RegionCriticalPSets; |
| } |
| |
| VLIWResourceModel *getTopResourceModel() { return TopResourceModel; } |
| VLIWResourceModel *getBotResourceModel() { return BotResourceModel; } |
| |
| /// getIssueWidth - Return the max instructions per scheduling group. |
| unsigned getIssueWidth() const { |
| return (InstrItins && InstrItins->SchedModel) |
| ? InstrItins->SchedModel->IssueWidth : 1; |
| } |
| |
| /// getNumMicroOps - Return the number of issue slots required for this MI. |
| unsigned getNumMicroOps(MachineInstr *MI) const { |
| if (!InstrItins) return 1; |
| int UOps = InstrItins->getNumMicroOps(MI->getDesc().getSchedClass()); |
| return (UOps >= 0) ? UOps : TII->getNumMicroOps(InstrItins, MI); |
| } |
| |
| private: |
| void scheduleNodeTopDown(SUnit *SU); |
| void listScheduleTopDown(); |
| |
| void initRegPressure(); |
| void updateScheduledPressure(std::vector<unsigned> NewMaxPressure); |
| |
| void moveInstruction(MachineInstr *MI, MachineBasicBlock::iterator InsertPos); |
| bool checkSchedLimit(); |
| |
| void releaseRoots(); |
| |
| void releaseSucc(SUnit *SU, SDep *SuccEdge); |
| void releaseSuccessors(SUnit *SU); |
| void releasePred(SUnit *SU, SDep *PredEdge); |
| void releasePredecessors(SUnit *SU); |
| |
| void placeDebugValues(); |
| }; |
| } // namespace |
| |
| |
| #endif |