[llvm] r330032 - [llvm-mca] Ensure that instructions with a schedule read-advance are always issued in the right order.

Andrea Di Biagio via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 13 08:19:08 PDT 2018


Author: adibiagio
Date: Fri Apr 13 08:19:07 2018
New Revision: 330032

URL: http://llvm.org/viewvc/llvm-project?rev=330032&view=rev
Log:
[llvm-mca] Ensure that instructions with a schedule read-advance are always issued in the right order.

Normally, the Scheduler prioritizes older instructions over younger instructions
during the instruction issue stage. In one particular case where a dependent
instruction had a schedule read-advance associated to one of the input operands,
this rule was not correctly applied.

This patch fixes the issue and adds a test to verify that we don't regress that
particular case.

Added:
    llvm/trunk/test/tools/llvm-mca/X86/BtVer2/read-advance-3.s
Modified:
    llvm/trunk/tools/llvm-mca/Scheduler.cpp
    llvm/trunk/tools/llvm-mca/Scheduler.h

Added: llvm/trunk/test/tools/llvm-mca/X86/BtVer2/read-advance-3.s
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-mca/X86/BtVer2/read-advance-3.s?rev=330032&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-mca/X86/BtVer2/read-advance-3.s (added)
+++ llvm/trunk/test/tools/llvm-mca/X86/BtVer2/read-advance-3.s Fri Apr 13 08:19:07 2018
@@ -0,0 +1,44 @@
+# RUN: llvm-mca -mtriple=x86_64-unknown-unknown -mcpu=btver2 -iterations=1 -resource-pressure=0 -timeline -dispatch=3 < %s | FileCheck %s
+
+  add %rdi, %rsi
+  add (%rsp), %rsi
+  add %rdx, %r8
+
+# CHECK:      Iterations:     1
+# CHECK-NEXT: Instructions:   3
+# CHECK-NEXT: Total Cycles:   7
+# CHECK-NEXT: Dispatch Width: 3
+# CHECK-NEXT: IPC:            0.43
+
+# CHECK:      Instruction Info:
+# CHECK-NEXT: [1]: #uOps
+# CHECK-NEXT: [2]: Latency
+# CHECK-NEXT: [3]: RThroughput
+# CHECK-NEXT: [4]: MayLoad
+# CHECK-NEXT: [5]: MayStore
+# CHECK-NEXT: [6]: HasSideEffects
+
+# CHECK:      [1]    [2]    [3]    [4]    [5]    [6]	Instructions:
+# CHECK-NEXT:  1      1     0.50                    	addq	%rdi, %rsi
+# CHECK-NEXT:  1      4     1.00    *               	addq	(%rsp), %rsi
+# CHECK-NEXT:  1      1     0.50                    	addq	%rdx, %r8
+
+# CHECK:      Timeline view:
+
+# CHECK:      Index	0123456
+
+# CHECK:      [0,0]	DeER ..	addq	%rdi, %rsi
+# CHECK-NEXT: [0,1]	DeeeeER	addq	(%rsp), %rsi
+# CHECK-NEXT: [0,2]	D=eE--R	addq	%rdx, %r8
+
+# CHECK:      Average Wait times (based on the timeline view):
+# CHECK-NEXT: [0]: Executions
+# CHECK-NEXT: [1]: Average time spent waiting in a scheduler's queue
+# CHECK-NEXT: [2]: Average time spent waiting in a scheduler's queue while ready
+# CHECK-NEXT: [3]: Average time elapsed from WB until retire stage
+
+# CHECK:            [0]    [1]    [2]    [3]
+# CHECK-NEXT: 0.     1     1.0    1.0    0.0    	addq	%rdi, %rsi
+# CHECK-NEXT: 1.     1     1.0    0.0    0.0    	addq	(%rsp), %rsi
+# CHECK-NEXT: 2.     1     2.0    2.0    2.0    	addq	%rdx, %r8
+

Modified: llvm/trunk/tools/llvm-mca/Scheduler.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-mca/Scheduler.cpp?rev=330032&r1=330031&r2=330032&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-mca/Scheduler.cpp (original)
+++ llvm/trunk/tools/llvm-mca/Scheduler.cpp Fri Apr 13 08:19:07 2018
@@ -277,7 +277,7 @@ void Scheduler::scheduleInstruction(unsi
   if (Resources->mustIssueImmediately(Desc)) {
     DEBUG(dbgs() << "[SCHEDULER] Instruction " << Idx
                  << " issued immediately\n");
-    return issueInstruction(MCIS, Idx);
+    return issueInstruction(Idx, MCIS);
   }
 
   DEBUG(dbgs() << "[SCHEDULER] Adding " << Idx << " to the Ready Queue\n");
@@ -293,10 +293,15 @@ void Scheduler::cycleEvent() {
 
   updateIssuedQueue();
   updatePendingQueue();
-  bool InstructionsWerePromoted = false;
-  do {
-    InstructionsWerePromoted = issue();
-  } while(InstructionsWerePromoted);
+
+  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();
+  }
 }
 
 #ifndef NDEBUG
@@ -317,7 +322,8 @@ bool Scheduler::canBeDispatched(unsigned
     Type = HWStallEvent::StoreQueueFull;
   else {
     switch (Resources->canBeDispatched(Desc.Buffers)) {
-    default: return true;
+    default:
+      return true;
     case ResourceStateEvent::RS_BUFFER_UNAVAILABLE:
       Type = HWStallEvent::SchedulerQueueFull;
       break;
@@ -330,7 +336,7 @@ bool Scheduler::canBeDispatched(unsigned
   return false;
 }
 
-void Scheduler::issueInstruction(Instruction &IS, unsigned InstrIndex) {
+void Scheduler::issueInstruction(unsigned InstrIndex, Instruction &IS) {
   const InstrDesc &D = IS.getDesc();
 
   if (!D.Buffers.empty()) {
@@ -362,65 +368,51 @@ void Scheduler::issueInstruction(Instruc
   notifyInstructionExecuted(InstrIndex);
 }
 
-bool Scheduler::promoteToReadyQueue() {
+void Scheduler::promoteToReadyQueue() {
   // Scan the set of waiting instructions and promote them to the
   // ready queue if operands are all ready.
-  bool InstructionsWerePromoted = false;
   for (auto I = WaitQueue.begin(), E = WaitQueue.end(); I != E;) {
     const QueueEntryTy &Entry = *I;
+    unsigned IID = Entry.first;
+    Instruction &Inst = *Entry.second;
 
     // Check if this instruction is now ready. In case, force
     // a transition in state using method 'update()'.
-    Entry.second->update();
-    bool IsReady = Entry.second->isReady();
+    Inst.update();
 
-    const InstrDesc &Desc = Entry.second->getDesc();
+    const InstrDesc &Desc = Inst.getDesc();
     bool IsMemOp = Desc.MayLoad || Desc.MayStore;
-    if (IsReady && IsMemOp)
-      IsReady &= LSU->isReady(Entry.first);
-
-    if (IsReady) {
-      notifyInstructionReady(Entry.first);
-      ReadyQueue[Entry.first] = Entry.second;
-      auto ToRemove = I;
-      ++I;
-      WaitQueue.erase(ToRemove);
-      InstructionsWerePromoted = true;
-    } else {
+    if (!Inst.isReady() || (IsMemOp && !LSU->isReady(IID))) {
       ++I;
+      continue;
     }
+
+    notifyInstructionReady(IID);
+    ReadyQueue[IID] = &Inst;
+    auto ToRemove = I;
+    ++I;
+    WaitQueue.erase(ToRemove);
   }
-  
-  return InstructionsWerePromoted;
 }
 
-
 bool Scheduler::issue() {
-  std::vector<unsigned> ToRemove;
-  for (const QueueEntryTy QueueEntry : ReadyQueue) {
-    // Give priority to older instructions in ReadyQueue. The ready queue is
-    // ordered by key, and therefore older instructions are visited first.
-    Instruction &IS = *QueueEntry.second;
-    const InstrDesc &D = IS.getDesc();
-    if (!Resources->canBeIssued(D))
-      continue;
-    unsigned InstrIndex = QueueEntry.first;
-    issueInstruction(IS, InstrIndex);
-    ToRemove.emplace_back(InstrIndex);
-  }
+  // 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(),
+                               [&](const QueueEntryTy &Entry) {
+                                 const Instruction &IS = *Entry.second;
+                                 const InstrDesc &D = IS.getDesc();
+                                 return Resources->canBeIssued(D);
+                               });
 
-  if (ToRemove.empty())
+  if (It == ReadyQueue.end())
     return false;
 
-  for (const unsigned InstrIndex : ToRemove)
-    ReadyQueue.erase(InstrIndex);
-
-  // 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()'.
-  return promoteToReadyQueue();
+  // 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;
 }
 
 void Scheduler::updatePendingQueue() {
@@ -428,7 +420,6 @@ void Scheduler::updatePendingQueue() {
   // started.
   for (QueueEntryTy Entry : WaitQueue)
     Entry.second->cycleEvent();
-
   promoteToReadyQueue();
 }
 

Modified: llvm/trunk/tools/llvm-mca/Scheduler.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-mca/Scheduler.h?rev=330032&r1=330031&r2=330032&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-mca/Scheduler.h (original)
+++ llvm/trunk/tools/llvm-mca/Scheduler.h Fri Apr 13 08:19:07 2018
@@ -430,17 +430,16 @@ class Scheduler {
   // Notify the Backend that buffered resources were freed.
   void notifyReleasedBuffers(llvm::ArrayRef<uint64_t> Buffers);
 
-  /// Issue instructions from the ReadyQueue by giving priority to older
-  /// instructions. This method returns true if at least one instruction has
-  /// been promoted in the process from the WaitQueue to the ReadyQueue.
+  /// Issue the next instruction from the ReadyQueue. This method gives priority
+  /// to older instructions.
   bool issue();
 
-  /// Scans the WaitQueue in search of instructions that can be moved to
-  /// the ReadyQueue.
-  bool promoteToReadyQueue();
+  /// Move instructions from the WaitQueue to the ReadyQueue if input operands
+  /// are all available.
+  void promoteToReadyQueue();
 
   /// Issue an instruction without updating the ready queue.
-  void issueInstruction(Instruction &IS, unsigned InstrIndex);
+  void issueInstruction(unsigned Index, Instruction &IS);
   void updatePendingQueue();
   void updateIssuedQueue();
 




More information about the llvm-commits mailing list