[llvm] r330723 - [llvm-mca] Refactor the Scheduler interface in preparation for PR36663.

Andrea Di Biagio via llvm-commits llvm-commits at lists.llvm.org
Tue Apr 24 07:53:16 PDT 2018


Author: adibiagio
Date: Tue Apr 24 07:53:16 2018
New Revision: 330723

URL: http://llvm.org/viewvc/llvm-project?rev=330723&view=rev
Log:
[llvm-mca] Refactor the Scheduler interface in preparation for PR36663.

Zero latency instructions are now scheduled the same way as other instructions.
Before this patch, there was a specialzed code path for those instructions.

All scheduler events are now generated from method `scheduleInstruction()` and
from method `cycleEvent()`. This will make easier to implement a "execution
stage", and let that stage publish all the scheduler events.

No functional change intended.

Modified:
    llvm/trunk/tools/llvm-mca/Scheduler.cpp
    llvm/trunk/tools/llvm-mca/Scheduler.h

Modified: llvm/trunk/tools/llvm-mca/Scheduler.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-mca/Scheduler.cpp?rev=330723&r1=330722&r2=330723&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-mca/Scheduler.cpp (original)
+++ llvm/trunk/tools/llvm-mca/Scheduler.cpp Tue Apr 24 07:53:16 2018
@@ -233,33 +233,13 @@ void Scheduler::scheduleInstruction(unsi
   assert(ReadyQueue.find(Idx) == ReadyQueue.end());
   assert(IssuedQueue.find(Idx) == IssuedQueue.end());
 
-  // Special case where MCIS is a zero-latency instruction.  A zero-latency
-  // instruction doesn't consume any scheduler resources.  That is because it
-  // doesn't need to be executed.  Most of the times, zero latency instructions
-  // are removed at register renaming stage. For example, register-register
-  // moves can be removed at register renaming stage by creating new aliases.
-  // Zero-idiom instruction (for example: a `xor reg, reg`) can also be
-  // eliminated at register renaming stage, since we know in advance that those
-  // clear their output register.
-  if (MCIS.isZeroLatency()) {
-    assert(MCIS.isReady() && "data dependent zero-latency instruction?");
-    notifyInstructionReady(Idx);
-    MCIS.execute();
-    notifyInstructionIssued(Idx, {});
-    assert(MCIS.isExecuted() && "Unexpected non-zero latency!");
-    notifyInstructionExecuted(Idx);
-    return;
-  }
-
+  // Reserve a slot in each buffered resource. Also, mark units with
+  // BufferSize=0 as reserved. Resources with a buffer size of zero will only
+  // be released after MCIS is issued, and all the ResourceCycles for those
+  // units have been consumed.
   const InstrDesc &Desc = MCIS.getDesc();
-  if (!Desc.Buffers.empty()) {
-    // Reserve a slot in each buffered resource. Also, mark units with
-    // BufferSize=0 as reserved. Resources with a buffer size of zero will only
-    // be released after MCIS is issued, and all the ResourceCycles for those
-    // units have been consumed.
-    Resources->reserveBuffers(Desc.Buffers);
-    notifyReservedBuffers(Desc.Buffers);
-  }
+  reserveBuffers(Desc.Buffers);
+  notifyReservedBuffers(Desc.Buffers);
 
   // If necessary, reserve queue entries in the load-store unit (LSU).
   bool Reserved = LSU->reserve(Idx, Desc);
@@ -270,18 +250,28 @@ void Scheduler::scheduleInstruction(unsi
   }
   notifyInstructionReady(Idx);
 
-  // Special case where the instruction is ready, and it uses an in-order
-  // dispatch/issue processor resource. The instruction is issued immediately to
-  // the pipelines. Any other in-order buffered resources (i.e. BufferSize=1)
-  // are consumed.
-  if (Resources->mustIssueImmediately(Desc)) {
-    DEBUG(dbgs() << "[SCHEDULER] Instruction " << Idx
-                 << " issued immediately\n");
-    return issueInstruction(Idx, MCIS);
+  // Don't add a zero-latency instruction to the Wait or Ready queue.
+  // A zero-latency instruction doesn't consume any scheduler resources. That is
+  // because it doesn't need to be executed, and it is often removed at register
+  // renaming stage. For example, register-register moves are often optimized at
+  // register renaming stage by simply updating register aliases. On some
+  // targets, zero-idiom instructions (for example: a xor that clears the value
+  // of a register) are treated speacially, and are often eliminated at register
+  // renaming stage.
+
+  // Instructions that use an in-order dispatch/issue processor resource must be
+  // issued immediately to the pipeline(s). Any other in-order buffered
+  // resources (i.e. BufferSize=1) is consumed.
+
+  if (!MCIS.isZeroLatency() && !Resources->mustIssueImmediately(Desc)) {
+    DEBUG(dbgs() << "[SCHEDULER] Adding " << Idx << " to the Ready Queue\n");
+    ReadyQueue[Idx] = &MCIS;
+    return;
   }
 
-  DEBUG(dbgs() << "[SCHEDULER] Adding " << Idx << " to the Ready Queue\n");
-  ReadyQueue[Idx] = &MCIS;
+  DEBUG(dbgs() << "[SCHEDULER] Instruction " << Idx << " issued immediately\n");
+  // Release buffered resources and issue MCIS to the underlying pipelines.
+  issueInstruction(Idx, MCIS);
 }
 
 void Scheduler::cycleEvent() {
@@ -291,16 +281,33 @@ void Scheduler::cycleEvent() {
   for (const ResourceRef &RR : ResourcesFreed)
     notifyResourceAvailable(RR);
 
-  updateIssuedQueue();
-  updatePendingQueue();
+  SmallVector<unsigned, 4> InstructionIDs;
+  updateIssuedQueue(InstructionIDs);
+  for (unsigned Idx : InstructionIDs)
+    notifyInstructionExecuted(Idx);
+  InstructionIDs.clear();
+
+  updatePendingQueue(InstructionIDs);
+  for (unsigned Idx : InstructionIDs)
+    notifyInstructionReady(Idx);
+  InstructionIDs.clear();
+
+  std::pair<unsigned, Instruction *> Inst = select();
+  while (Inst.second) {
+    issueInstruction(Inst.first, *Inst.second);
 
-  while (issue()) {
     // Instructions that have been issued during this cycle might have unblocked
     // other dependent instructions. Dependent instructions may be issued during
     // this same cycle if operands have ReadAdvance entries.  Promote those
     // instructions to the ReadyQueue and tell to the caller that we need
     // another round of 'issue()'.
-    promoteToReadyQueue();
+    promoteToReadyQueue(InstructionIDs);
+    for (unsigned Idx : InstructionIDs)
+      notifyInstructionReady(Idx);
+    InstructionIDs.clear();
+
+    // Select the next instruction to issue.
+    Inst = select();
   }
 }
 
@@ -336,39 +343,38 @@ bool Scheduler::canBeDispatched(unsigned
   return false;
 }
 
-void Scheduler::issueInstruction(unsigned InstrIndex, Instruction &IS) {
+void Scheduler::issueInstructionImpl(
+    unsigned InstrIndex, Instruction &IS,
+    SmallVectorImpl<std::pair<ResourceRef, double>> &UsedResources) {
   const InstrDesc &D = IS.getDesc();
 
-  if (!D.Buffers.empty()) {
-    Resources->releaseBuffers(D.Buffers);
-    notifyReleasedBuffers(D.Buffers);
-  }
-
   // Issue the instruction and collect all the consumed resources
   // into a vector. That vector is then used to notify the listener.
-  // Most instructions consume very few resurces (typically one or
-  // two resources). We use a small vector here, and conservatively
-  // initialize its capacity to 4. This should address the majority of
-  // the cases.
-  SmallVector<std::pair<ResourceRef, double>, 4> UsedResources;
   Resources->issueInstruction(InstrIndex, D, UsedResources);
+
   // Notify the instruction that it started executing.
   // This updates the internal state of each write.
   IS.execute();
 
-  notifyInstructionIssued(InstrIndex, UsedResources);
-  if (D.MaxLatency) {
-    assert(IS.isExecuting() && "A zero latency instruction?");
+  if (IS.isExecuting())
     IssuedQueue[InstrIndex] = &IS;
-    return;
-  }
+}
 
-  // A zero latency instruction which reads and/or updates registers.
-  assert(IS.isExecuted() && "Instruction still executing!");
-  notifyInstructionExecuted(InstrIndex);
+void Scheduler::issueInstruction(unsigned InstrIndex, Instruction &IS) {
+  // Release buffered resources.
+  const InstrDesc &Desc = IS.getDesc();
+  releaseBuffers(Desc.Buffers);
+  notifyReleasedBuffers(Desc.Buffers);
+
+  // Issue IS to the underlying pipelines and notify listeners.
+  SmallVector<std::pair<ResourceRef, double>, 4> Pipes;
+  issueInstructionImpl(InstrIndex, IS, Pipes);
+  notifyInstructionIssued(InstrIndex, Pipes);
+  if (IS.isExecuted())
+    notifyInstructionExecuted(InstrIndex);
 }
 
-void Scheduler::promoteToReadyQueue() {
+void Scheduler::promoteToReadyQueue(SmallVectorImpl<unsigned> &Ready) {
   // Scan the set of waiting instructions and promote them to the
   // ready queue if operands are all ready.
   for (auto I = WaitQueue.begin(), E = WaitQueue.end(); I != E;) {
@@ -387,7 +393,7 @@ void Scheduler::promoteToReadyQueue() {
       continue;
     }
 
-    notifyInstructionReady(IID);
+    Ready.emplace_back(IID);
     ReadyQueue[IID] = &Inst;
     auto ToRemove = I;
     ++I;
@@ -395,7 +401,7 @@ void Scheduler::promoteToReadyQueue() {
   }
 }
 
-bool Scheduler::issue() {
+std::pair<unsigned, Instruction *> Scheduler::select() {
   // Give priority to older instructions in the ReadyQueue. Since the ready
   // queue is ordered by key, this will always prioritize older instructions.
   const auto It = std::find_if(ReadyQueue.begin(), ReadyQueue.end(),
@@ -406,29 +412,28 @@ bool Scheduler::issue() {
                                });
 
   if (It == ReadyQueue.end())
-    return false;
+    return {0, nullptr};
 
-  // We found an instruction. Issue it, and update the ready queue.
-  const QueueEntryTy &Entry = *It;
-  issueInstruction(Entry.first, *Entry.second);
-  ReadyQueue.erase(Entry.first);
-  return true;
+  // We found an instruction to issue.
+  const QueueEntryTy Entry = *It;
+  ReadyQueue.erase(It);
+  return Entry;
 }
 
-void Scheduler::updatePendingQueue() {
+void Scheduler::updatePendingQueue(SmallVectorImpl<unsigned> &Ready) {
   // Notify to instructions in the pending queue that a new cycle just
   // started.
   for (QueueEntryTy Entry : WaitQueue)
     Entry.second->cycleEvent();
-  promoteToReadyQueue();
+  promoteToReadyQueue(Ready);
 }
 
-void Scheduler::updateIssuedQueue() {
+void Scheduler::updateIssuedQueue(SmallVectorImpl<unsigned> &Executed) {
   for (auto I = IssuedQueue.begin(), E = IssuedQueue.end(); I != E;) {
     const QueueEntryTy Entry = *I;
     Entry.second->cycleEvent();
     if (Entry.second->isExecuted()) {
-      notifyInstructionExecuted(Entry.first);
+      Executed.push_back(Entry.first);
       auto ToRemove = I;
       ++I;
       IssuedQueue.erase(ToRemove);
@@ -474,6 +479,9 @@ void Scheduler::notifyResourceAvailable(
 }
 
 void Scheduler::notifyReservedBuffers(ArrayRef<uint64_t> Buffers) {
+  if (Buffers.empty())
+    return;
+
   SmallVector<unsigned, 4> BufferIDs(Buffers.begin(), Buffers.end());
   std::transform(
       Buffers.begin(), Buffers.end(), BufferIDs.begin(),
@@ -482,6 +490,9 @@ void Scheduler::notifyReservedBuffers(Ar
 }
 
 void Scheduler::notifyReleasedBuffers(ArrayRef<uint64_t> Buffers) {
+  if (Buffers.empty())
+    return;
+
   SmallVector<unsigned, 4> BufferIDs(Buffers.begin(), Buffers.end());
   std::transform(
       Buffers.begin(), Buffers.end(), BufferIDs.begin(),

Modified: llvm/trunk/tools/llvm-mca/Scheduler.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-mca/Scheduler.h?rev=330723&r1=330722&r2=330723&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-mca/Scheduler.h (original)
+++ llvm/trunk/tools/llvm-mca/Scheduler.h Tue Apr 24 07:53:16 2018
@@ -430,18 +430,21 @@ class Scheduler {
   // Notify the Backend that buffered resources were freed.
   void notifyReleasedBuffers(llvm::ArrayRef<uint64_t> Buffers);
 
-  /// Issue the next instruction from the ReadyQueue. This method gives priority
-  /// to older instructions.
-  bool issue();
+  /// Select the next instruction to issue from the ReadyQueue.
+  /// This method gives priority to older instructions.
+  std::pair<unsigned, Instruction *> select();
 
   /// Move instructions from the WaitQueue to the ReadyQueue if input operands
   /// are all available.
-  void promoteToReadyQueue();
+  void promoteToReadyQueue(llvm::SmallVectorImpl<unsigned> &Ready);
 
   /// Issue an instruction without updating the ready queue.
-  void issueInstruction(unsigned Index, Instruction &IS);
-  void updatePendingQueue();
-  void updateIssuedQueue();
+  void issueInstructionImpl(
+      unsigned Index, Instruction &IS,
+      llvm::SmallVectorImpl<std::pair<ResourceRef, double>> &Pipes);
+
+  void updatePendingQueue(llvm::SmallVectorImpl<unsigned> &Ready);
+  void updateIssuedQueue(llvm::SmallVectorImpl<unsigned> &Executed);
 
 public:
   Scheduler(Backend *B, const llvm::MCSchedModel &Model, unsigned LoadQueueSize,
@@ -463,6 +466,20 @@ public:
   bool canBeDispatched(unsigned Idx, const InstrDesc &Desc) const;
   void scheduleInstruction(unsigned Idx, Instruction &MCIS);
 
+
+  /// Issue an instruction.
+  void issueInstruction(unsigned Index, Instruction &IS);
+
+  /// Reserve one entry in each buffered resource.
+  void reserveBuffers(llvm::ArrayRef<uint64_t> Buffers) {
+    Resources->reserveBuffers(Buffers);
+  }
+
+  /// Release buffer entries previously allocated by method reserveBuffers.
+  void releaseBuffers(llvm::ArrayRef<uint64_t> Buffers) {
+    Resources->releaseBuffers(Buffers);
+  }
+
   void cycleEvent();
 
 #ifndef NDEBUG




More information about the llvm-commits mailing list