[llvm-mca] Avoid exposing index values in the MCA interfaces.
authorMatt Davis <Matthew.Davis@sony.com>
Mon, 7 May 2018 18:29:15 +0000 (18:29 +0000)
committerMatt Davis <Matthew.Davis@sony.com>
Mon, 7 May 2018 18:29:15 +0000 (18:29 +0000)
Summary:
This patch eliminates many places where we originally needed to  pass index
values to represent an instruction.  The index is still used as a key, in various parts of
MCA.  I'm  not comfortable eliminating the index just yet.    By burying the index in
the instruction, we can avoid exposing that value in many places.

Eventually, we should consider removing the Instructions list in the Backend
all together,   it's only used to hold and reclaim the memory for the allocated
Instruction instances.  Instead we could pass around a smart pointer.  But that's
a separate discussion/patch.

Reviewers: andreadb, courbet, RKSimon

Reviewed By: andreadb

Subscribers: javed.absar, tschuett, gbedwell, llvm-commits

Differential Revision: https://reviews.llvm.org/D46367

llvm-svn: 331660

16 files changed:
llvm/tools/llvm-mca/Backend.cpp
llvm/tools/llvm-mca/Backend.h
llvm/tools/llvm-mca/Dispatch.cpp
llvm/tools/llvm-mca/Dispatch.h
llvm/tools/llvm-mca/HWEventListener.h
llvm/tools/llvm-mca/Instruction.h
llvm/tools/llvm-mca/InstructionTables.cpp
llvm/tools/llvm-mca/LSUnit.cpp
llvm/tools/llvm-mca/LSUnit.h
llvm/tools/llvm-mca/ResourcePressureView.cpp
llvm/tools/llvm-mca/RetireControlUnit.cpp
llvm/tools/llvm-mca/RetireControlUnit.h
llvm/tools/llvm-mca/Scheduler.cpp
llvm/tools/llvm-mca/Scheduler.h
llvm/tools/llvm-mca/SourceMgr.h
llvm/tools/llvm-mca/TimelineView.cpp

index f000837..de34510 100644 (file)
@@ -32,16 +32,15 @@ void Backend::runCycle(unsigned Cycle) {
   notifyCycleBegin(Cycle);
 
   while (SM.hasNext()) {
-    InstRef IR = SM.peekNext();
-    std::unique_ptr<Instruction> NewIS = IB.createInstruction(*IR.second);
+    SourceRef SR = SM.peekNext();
+    std::unique_ptr<Instruction> NewIS = IB.createInstruction(*SR.second);
     const InstrDesc &Desc = NewIS->getDesc();
-    if (!DU->isAvailable(Desc.NumMicroOps) ||
-        !DU->canDispatch(IR.first, *NewIS))
-      break;
-
     Instruction *IS = NewIS.get();
-    Instructions[IR.first] = std::move(NewIS);
-    DU->dispatch(IR.first, IS, STI);
+    InstRef IR(SR.first, IS);
+    if (!DU->isAvailable(Desc.NumMicroOps) || !DU->canDispatch(IR))
+      break;
+    Instructions[SR.first] = std::move(NewIS);
+    DU->dispatch(IR, STI);
     SM.updateNext();
   }
 
index d294b61..fee3ca2 100644 (file)
@@ -80,13 +80,9 @@ public:
       runCycle(Cycles++);
   }
 
-  const Instruction &getInstruction(unsigned Index) const {
-    const auto It = Instructions.find(Index);
-    assert(It != Instructions.end() && "no running instructions with index");
-    assert(It->second);
-    return *It->second;
+  void eraseInstruction(const InstRef &IR) {
+    Instructions.erase(IR.getSourceIndex());
   }
-  void eraseInstruction(unsigned Index) { Instructions.erase(Index); }
 
   void addEventListener(HWEventListener *Listener);
   void notifyCycleBegin(unsigned Cycle);
index 4984485..b077f0a 100644 (file)
@@ -252,50 +252,48 @@ void RegisterFile::dump() const {
 }
 #endif
 
-void DispatchUnit::notifyInstructionDispatched(unsigned Index,
+void DispatchUnit::notifyInstructionDispatched(const InstRef &IR,
                                                ArrayRef<unsigned> UsedRegs) {
-  DEBUG(dbgs() << "[E] Instruction Dispatched: " << Index << '\n');
-  Owner->notifyInstructionEvent(HWInstructionDispatchedEvent(Index, UsedRegs));
+  DEBUG(dbgs() << "[E] Instruction Dispatched: " << IR << '\n');
+  Owner->notifyInstructionEvent(HWInstructionDispatchedEvent(IR, UsedRegs));
 }
 
-void DispatchUnit::notifyInstructionRetired(unsigned Index) {
-  DEBUG(dbgs() << "[E] Instruction Retired: " << Index << '\n');
-  const Instruction &IS = Owner->getInstruction(Index);
+void DispatchUnit::notifyInstructionRetired(const InstRef &IR) {
+  DEBUG(dbgs() << "[E] Instruction Retired: " << IR << '\n');
   SmallVector<unsigned, 4> FreedRegs(RAT->getNumRegisterFiles());
-  for (const std::unique_ptr<WriteState> &WS : IS.getDefs())
+  for (const std::unique_ptr<WriteState> &WS : IR.getInstruction()->getDefs())
     RAT->invalidateRegisterMapping(*WS.get(), FreedRegs);
-
-  Owner->notifyInstructionEvent(HWInstructionRetiredEvent(Index, FreedRegs));
-  Owner->eraseInstruction(Index);
+  Owner->notifyInstructionEvent(HWInstructionRetiredEvent(IR, FreedRegs));
+  Owner->eraseInstruction(IR);
 }
 
-bool DispatchUnit::checkRAT(unsigned Index, const Instruction &Instr) {
+bool DispatchUnit::checkRAT(const InstRef &IR) {
   SmallVector<unsigned, 4> RegDefs;
-  for (const std::unique_ptr<WriteState> &RegDef : Instr.getDefs())
+  for (const std::unique_ptr<WriteState> &RegDef :
+       IR.getInstruction()->getDefs())
     RegDefs.emplace_back(RegDef->getRegisterID());
 
   unsigned RegisterMask = RAT->isAvailable(RegDefs);
   // A mask with all zeroes means: register files are available.
   if (RegisterMask) {
-    Owner->notifyStallEvent(
-        HWStallEvent(HWStallEvent::RegisterFileStall, Index));
+    Owner->notifyStallEvent(HWStallEvent(HWStallEvent::RegisterFileStall, IR));
     return false;
   }
 
   return true;
 }
 
-bool DispatchUnit::checkRCU(unsigned Index, const InstrDesc &Desc) {
-  unsigned NumMicroOps = Desc.NumMicroOps;
+bool DispatchUnit::checkRCU(const InstRef &IR) {
+  const unsigned NumMicroOps = IR.getInstruction()->getDesc().NumMicroOps;
   if (RCU->isAvailable(NumMicroOps))
     return true;
   Owner->notifyStallEvent(
-      HWStallEvent(HWStallEvent::RetireControlUnitStall, Index));
+      HWStallEvent(HWStallEvent::RetireControlUnitStall, IR));
   return false;
 }
 
-bool DispatchUnit::checkScheduler(unsigned Index, const InstrDesc &Desc) {
-  return SC->canBeDispatched(Index, Desc);
+bool DispatchUnit::checkScheduler(const InstRef &IR) {
+  return SC->canBeDispatched(IR);
 }
 
 void DispatchUnit::updateRAWDependencies(ReadState &RS,
@@ -326,10 +324,11 @@ void DispatchUnit::updateRAWDependencies(ReadState &RS,
   DependentWrites.clear();
 }
 
-void DispatchUnit::dispatch(unsigned IID, Instruction *NewInst,
-                            const MCSubtargetInfo &STI) {
+void DispatchUnit::dispatch(InstRef IR, const MCSubtargetInfo &STI) {
   assert(!CarryOver && "Cannot dispatch another instruction!");
-  unsigned NumMicroOps = NewInst->getDesc().NumMicroOps;
+  Instruction &IS = *IR.getInstruction();
+  const InstrDesc &Desc = IS.getDesc();
+  const unsigned NumMicroOps = Desc.NumMicroOps;
   if (NumMicroOps > DispatchWidth) {
     assert(AvailableEntries == DispatchWidth);
     AvailableEntries = 0;
@@ -343,27 +342,26 @@ void DispatchUnit::dispatch(unsigned IID, Instruction *NewInst,
   // instruction. The assumption is that a zero-latency instruction doesn't
   // require to be issued to the scheduler for execution. More importantly, it
   // doesn't have to wait on the register input operands.
-  const InstrDesc &Desc = NewInst->getDesc();
   if (Desc.MaxLatency || !Desc.Resources.empty())
-    for (std::unique_ptr<ReadState> &RS : NewInst->getUses())
+    for (std::unique_ptr<ReadState> &RS : IS.getUses())
       updateRAWDependencies(*RS, STI);
 
   // Allocate new mappings.
   SmallVector<unsigned, 4> RegisterFiles(RAT->getNumRegisterFiles());
-  for (std::unique_ptr<WriteState> &WS : NewInst->getDefs())
+  for (std::unique_ptr<WriteState> &WS : IS.getDefs())
     RAT->addRegisterMapping(*WS, RegisterFiles);
 
   // Reserve slots in the RCU, and notify the instruction that it has been
   // dispatched to the schedulers for execution.
-  NewInst->dispatch(RCU->reserveSlot(IID, NumMicroOps));
+  IS.dispatch(RCU->reserveSlot(IR, NumMicroOps));
 
   // Notify listeners of the "instruction dispatched" event.
-  notifyInstructionDispatched(IID, RegisterFiles);
+  notifyInstructionDispatched(IR, RegisterFiles);
 
   // Now move the instruction into the scheduler's queue.
   // The scheduler is responsible for checking if this is a zero-latency
   // instruction that doesn't consume pipeline/scheduler resources.
-  SC->scheduleInstruction(IID, *NewInst);
+  SC->scheduleInstruction(IR);
 }
 
 #ifndef NDEBUG
index 205c12c..b6df6ad 100644 (file)
@@ -189,12 +189,12 @@ class DispatchUnit {
   std::unique_ptr<RetireControlUnit> RCU;
   Backend *Owner;
 
-  bool checkRAT(unsigned Index, const Instruction &Inst);
-  bool checkRCU(unsigned Index, const InstrDesc &Desc);
-  bool checkScheduler(unsigned Index, const InstrDesc &Desc);
+  bool checkRAT(const InstRef &IR);
+  bool checkRCU(const InstRef &IR);
+  bool checkScheduler(const InstRef &IR);
 
   void updateRAWDependencies(ReadState &RS, const llvm::MCSubtargetInfo &STI);
-  void notifyInstructionDispatched(unsigned IID,
+  void notifyInstructionDispatched(const InstRef &IR,
                                    llvm::ArrayRef<unsigned> UsedPhysRegs);
 
 public:
@@ -214,14 +214,12 @@ public:
 
   bool isRCUEmpty() const { return RCU->isEmpty(); }
 
-  bool canDispatch(unsigned Index, const Instruction &Inst) {
-    const InstrDesc &Desc = Inst.getDesc();
-    assert(isAvailable(Desc.NumMicroOps));
-    return checkRCU(Index, Desc) && checkRAT(Index, Inst) &&
-           checkScheduler(Index, Desc);
+  bool canDispatch(const InstRef &IR) {
+    assert(isAvailable(IR.getInstruction()->getDesc().NumMicroOps));
+    return checkRCU(IR) && checkRAT(IR) && checkScheduler(IR);
   }
 
-  void dispatch(unsigned IID, Instruction *I, const llvm::MCSubtargetInfo &STI);
+  void dispatch(InstRef IR, const llvm::MCSubtargetInfo &STI);
 
   void collectWrites(llvm::SmallVectorImpl<WriteState *> &Vec,
                      unsigned RegID) const {
@@ -235,9 +233,9 @@ public:
     CarryOver = CarryOver >= DispatchWidth ? CarryOver - DispatchWidth : 0U;
   }
 
-  void notifyInstructionRetired(unsigned Index);
+  void notifyInstructionRetired(const InstRef &IR);
 
-  void notifyDispatchStall(unsigned Index, unsigned EventType);
+  void notifyDispatchStall(const InstRef &IR, unsigned EventType);
 
   void onInstructionExecuted(unsigned TokenID) {
     RCU->onInstructionExecuted(TokenID);
index 4b58115..ce4cf21 100644 (file)
@@ -1,4 +1,3 @@
-
 //===----------------------- HWEventListener.h ------------------*- C++ -*-===//
 //
 //                     The LLVM Compiler Infrastructure
@@ -16,6 +15,7 @@
 #ifndef LLVM_TOOLS_LLVM_MCA_HWEVENTLISTENER_H
 #define LLVM_TOOLS_LLVM_MCA_HWEVENTLISTENER_H
 
+#include "Instruction.h"
 #include "llvm/ADT/ArrayRef.h"
 #include <utility>
 
@@ -48,30 +48,30 @@ public:
     LastGenericEventType,
   };
 
-  HWInstructionEvent(unsigned type, unsigned index)
-      : Type(type), Index(index) {}
+  HWInstructionEvent(unsigned type, const InstRef &Inst)
+      : Type(type), IR(Inst) {}
 
   // The event type. The exact meaning depends on the subtarget.
   const unsigned Type;
-  // The index of the instruction in the source manager.
-  const unsigned Index;
+
+  // The instruction this event was generated for.
+  const InstRef &IR;
 };
 
 class HWInstructionIssuedEvent : public HWInstructionEvent {
 public:
   using ResourceRef = std::pair<uint64_t, uint64_t>;
-  HWInstructionIssuedEvent(unsigned Index,
+  HWInstructionIssuedEvent(const InstRef &IR,
                            llvm::ArrayRef<std::pair<ResourceRef, double>> UR)
-      : HWInstructionEvent(HWInstructionEvent::Issued, Index),
-        UsedResources(UR) {}
+      : HWInstructionEvent(HWInstructionEvent::Issued, IR), UsedResources(UR) {}
 
   llvm::ArrayRef<std::pair<ResourceRef, double>> UsedResources;
 };
 
 class HWInstructionDispatchedEvent : public HWInstructionEvent {
 public:
-  HWInstructionDispatchedEvent(unsigned Index, llvm::ArrayRef<unsigned> Regs)
-      : HWInstructionEvent(HWInstructionEvent::Dispatched, Index),
+  HWInstructionDispatchedEvent(const InstRef &IR, llvm::ArrayRef<unsigned> Regs)
+      : HWInstructionEvent(HWInstructionEvent::Dispatched, IR),
         UsedPhysRegs(Regs) {}
   // Number of physical register allocated for this instruction. There is one
   // entry per register file.
@@ -80,8 +80,8 @@ public:
 
 class HWInstructionRetiredEvent : public HWInstructionEvent {
 public:
-  HWInstructionRetiredEvent(unsigned Index, llvm::ArrayRef<unsigned> Regs)
-      : HWInstructionEvent(HWInstructionEvent::Retired, Index),
+  HWInstructionRetiredEvent(const InstRef &IR, llvm::ArrayRef<unsigned> Regs)
+      : HWInstructionEvent(HWInstructionEvent::Retired, IR),
         FreedPhysRegs(Regs) {}
   // Number of register writes that have been architecturally committed. There
   // is one entry per register file.
@@ -105,12 +105,13 @@ public:
     LastGenericEvent
   };
 
-  HWStallEvent(unsigned type, unsigned index) : Type(type), Index(index) {}
+  HWStallEvent(unsigned type, const InstRef &Inst) : Type(type), IR(Inst) {}
 
   // The exact meaning of the stall event type depends on the subtarget.
   const unsigned Type;
-  // The index of the instruction in the source manager.
-  const unsigned Index;
+
+  // The instruction this event was generated for.
+  const InstRef &IR;
 };
 
 class HWEventListener {
index 21fec94..e90f515 100644 (file)
@@ -17,6 +17,7 @@
 #define LLVM_TOOLS_LLVM_MCA_INSTRUCTION_H
 
 #include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
 #include <memory>
 #include <set>
 #include <vector>
@@ -30,6 +31,36 @@ class ReadState;
 
 constexpr int UNKNOWN_CYCLES = -512;
 
+class Instruction;
+
+/// An InstRef contains both a SourceMgr index and Instruction pair.  The index
+/// is used as a unique identifier for the instruction.  MCA will make use of
+/// this index as a key throughout MCA.
+class InstRef : public std::pair<unsigned, Instruction *> {
+public:
+  InstRef() : std::pair<unsigned, Instruction *>(0, nullptr) {}
+  InstRef(unsigned Index, Instruction *I)
+      : std::pair<unsigned, Instruction *>(Index, I) {}
+
+  unsigned getSourceIndex() const { return first; }
+  Instruction *getInstruction() { return second; }
+  const Instruction *getInstruction() const { return second; }
+
+  /// Returns true if  this InstRef has been populated.
+  bool isValid() const { return second != nullptr; }
+
+#ifndef NDEBUG
+  void print(llvm::raw_ostream &OS) const { OS << getSourceIndex(); }
+#endif
+};
+
+#ifndef NDEBUG
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const InstRef &IR) {
+  IR.print(OS);
+  return OS;
+}
+#endif
+
 /// A register write descriptor.
 struct WriteDescriptor {
   // Operand index. -1 if this is an implicit write.
index ae96568..79ca870 100644 (file)
@@ -30,8 +30,8 @@ void InstructionTables::run() {
   // Create an instruction descriptor for every instruction in the sequence.
   while (S.hasNext()) {
     UsedResources.clear();
-    InstRef IR = S.peekNext();
-    std::unique_ptr<Instruction> Inst = IB.createInstruction(*IR.second);
+    SourceRef SR = S.peekNext();
+    std::unique_ptr<Instruction> Inst = IB.createInstruction(*SR.second);
     const InstrDesc &Desc = Inst->getDesc();
     // Now identify the resources consumed by this instruction.
     for (const std::pair<uint64_t, ResourceUsage> Resource : Desc.Resources) {
@@ -70,7 +70,8 @@ void InstructionTables::run() {
     }
 
     // Now send a fake instruction issued event to all the views.
-    HWInstructionIssuedEvent Event(IR.first, UsedResources);
+    InstRef IR(SR.first, Inst.get());
+    HWInstructionIssuedEvent Event(IR, UsedResources);
     for (std::unique_ptr<View> &Listener : Views)
       Listener->onInstructionEvent(Event);
     S.updateNext();
index c235830..1b6d848 100644 (file)
@@ -50,13 +50,15 @@ void LSUnit::assignSQSlot(unsigned Index) {
   StoreQueue.insert(Index);
 }
 
-bool LSUnit::reserve(unsigned Index, const InstrDesc &Desc) {
+bool LSUnit::reserve(const InstRef &IR) {
+  const InstrDesc Desc = IR.getInstruction()->getDesc();
   unsigned MayLoad = Desc.MayLoad;
   unsigned MayStore = Desc.MayStore;
   unsigned IsMemBarrier = Desc.HasSideEffects;
   if (!MayLoad && !MayStore)
     return false;
 
+  const unsigned Index = IR.getSourceIndex();
   if (MayLoad) {
     if (IsMemBarrier)
       LoadBarriers.insert(Index);
@@ -70,7 +72,8 @@ bool LSUnit::reserve(unsigned Index, const InstrDesc &Desc) {
   return true;
 }
 
-bool LSUnit::isReady(unsigned Index) const {
+bool LSUnit::isReady(const InstRef &IR) const {
+  const unsigned Index = IR.getSourceIndex();
   bool IsALoad = LoadQueue.count(Index) != 0;
   bool IsAStore = StoreQueue.count(Index) != 0;
   assert((IsALoad || IsAStore) && "Instruction is not in queue!");
@@ -116,7 +119,8 @@ bool LSUnit::isReady(unsigned Index) const {
   return !IsAStore;
 }
 
-void LSUnit::onInstructionExecuted(unsigned Index) {
+void LSUnit::onInstructionExecuted(const InstRef &IR) {
+  const unsigned Index = IR.getSourceIndex();
   std::set<unsigned>::iterator it = LoadQueue.find(Index);
   if (it != LoadQueue.end()) {
     DEBUG(dbgs() << "[LSUnit]: Instruction idx=" << Index
index d291a09..8591107 100644 (file)
@@ -24,6 +24,7 @@
 
 namespace mca {
 
+class InstRef;
 struct InstrDesc;
 
 /// A Load/Store Unit implementing a load and store queues.
@@ -132,7 +133,7 @@ public:
   bool isLQFull() const { return LQ_Size != 0 && LoadQueue.size() == LQ_Size; }
 
   // Returns true if this instruction has been successfully enqueued.
-  bool reserve(unsigned Index, const InstrDesc &Desc);
+  bool reserve(const InstRef &IR);
 
   // The rules are:
   // 1. A store may not pass a previous store.
@@ -141,8 +142,8 @@ public:
   // 4. A store may not pass a previous load (regardless of flag 'NoAlias').
   // 5. A load has to wait until an older load barrier is fully executed.
   // 6. A store has to wait until an older store barrier is fully executed.
-  bool isReady(unsigned Index) const;
-  void onInstructionExecuted(unsigned Index);
+  bool isReady(const InstRef &IR) const;
+  void onInstructionExecuted(const InstRef &IR);
 };
 } // namespace mca
 
index b3b478b..6fd23cc 100644 (file)
@@ -44,7 +44,7 @@ void ResourcePressureView::onInstructionEvent(const HWInstructionEvent &Event) {
   if (Event.Type != HWInstructionEvent::Issued)
     return;
   const auto &IssueEvent = static_cast<const HWInstructionIssuedEvent &>(Event);
-  unsigned SourceIdx = Event.Index % Source.size();
+  const unsigned SourceIdx = Event.IR.getSourceIndex() % Source.size();
   for (const std::pair<ResourceRef, double> &Use : IssueEvent.UsedResources) {
     const ResourceRef &RR = Use.first;
     assert(Resource2VecIndex.find(RR.first) != Resource2VecIndex.end());
index bd0fc28..da93c2c 100644 (file)
@@ -28,7 +28,7 @@ RetireControlUnit::RetireControlUnit(const llvm::MCSchedModel &SM,
 }
 
 // Reserves a number of slots, and returns a new token.
-unsigned RetireControlUnit::reserveSlot(unsigned Index, unsigned NumMicroOps) {
+unsigned RetireControlUnit::reserveSlot(const InstRef &IR, unsigned NumMicroOps) {
   assert(isAvailable(NumMicroOps));
   unsigned NormalizedQuantity =
       std::min(NumMicroOps, static_cast<unsigned>(Queue.size()));
@@ -37,7 +37,7 @@ unsigned RetireControlUnit::reserveSlot(unsigned Index, unsigned NumMicroOps) {
   // resources, they still consume one slot in the retire queue.
   NormalizedQuantity = std::max(NormalizedQuantity, 1U);
   unsigned TokenID = NextAvailableSlotIdx;
-  Queue[NextAvailableSlotIdx] = {Index, NormalizedQuantity, false};
+  Queue[NextAvailableSlotIdx] = {IR, NormalizedQuantity, false};
   NextAvailableSlotIdx += NormalizedQuantity;
   NextAvailableSlotIdx %= Queue.size();
   AvailableSlots -= NormalizedQuantity;
@@ -54,9 +54,10 @@ void RetireControlUnit::cycleEvent() {
       break;
     RUToken &Current = Queue[CurrentInstructionSlotIdx];
     assert(Current.NumSlots && "Reserved zero slots?");
+    assert(Current.IR.isValid() && "Invalid RUToken in the RCU queue.");
     if (!Current.Executed)
       break;
-    Owner->notifyInstructionRetired(Current.Index);
+    Owner->notifyInstructionRetired(Current.IR);
     CurrentInstructionSlotIdx += Current.NumSlots;
     CurrentInstructionSlotIdx %= Queue.size();
     AvailableSlots += Current.NumSlots;
@@ -66,7 +67,7 @@ void RetireControlUnit::cycleEvent() {
 
 void RetireControlUnit::onInstructionExecuted(unsigned TokenID) {
   assert(Queue.size() > TokenID);
-  assert(Queue[TokenID].Executed == false && Queue[TokenID].Index != ~0U);
+  assert(Queue[TokenID].Executed == false && Queue[TokenID].IR.isValid());
   Queue[TokenID].Executed = true;
 }
 
index 0c74442..8958755 100644 (file)
@@ -47,7 +47,7 @@ struct RetireControlUnit {
   // Note that the size of the reorder buffer is defined by the scheduling
   // model via field 'NumMicroOpBufferSize'.
   struct RUToken {
-    unsigned Index;    // Instruction index.
+    InstRef IR;
     unsigned NumSlots; // Slots reserved to this instruction.
     bool Executed;     // True if the instruction is past the WB stage.
   };
@@ -74,7 +74,7 @@ public:
   }
 
   // Reserves a number of slots, and returns a new token.
-  unsigned reserveSlot(unsigned Index, unsigned NumMicroOps);
+  unsigned reserveSlot(const InstRef &IS, unsigned NumMicroOps);
 
   /// Retires instructions in program order.
   void cycleEvent();
index a42cbef..5df677d 100644 (file)
@@ -228,7 +228,8 @@ void ResourceManager::cycleEvent(SmallVectorImpl<ResourceRef> &ResourcesFreed) {
     BusyResources.erase(RF);
 }
 
-void Scheduler::scheduleInstruction(unsigned Idx, Instruction &MCIS) {
+void Scheduler::scheduleInstruction(InstRef &IR) {
+  const unsigned Idx = IR.getSourceIndex();
   assert(WaitQueue.find(Idx) == WaitQueue.end());
   assert(ReadyQueue.find(Idx) == ReadyQueue.end());
   assert(IssuedQueue.find(Idx) == IssuedQueue.end());
@@ -237,18 +238,18 @@ void Scheduler::scheduleInstruction(unsigned Idx, Instruction &MCIS) {
   // 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();
+  const InstrDesc &Desc = IR.getInstruction()->getDesc();
   reserveBuffers(Desc.Buffers);
   notifyReservedBuffers(Desc.Buffers);
 
   // If necessary, reserve queue entries in the load-store unit (LSU).
-  bool Reserved = LSU->reserve(Idx, Desc);
-  if (!MCIS.isReady() || (Reserved && !LSU->isReady(Idx))) {
+  bool Reserved = LSU->reserve(IR);
+  if (!IR.getInstruction()->isReady() || (Reserved && !LSU->isReady(IR))) {
     DEBUG(dbgs() << "[SCHEDULER] Adding " << Idx << " to the Wait Queue\n");
-    WaitQueue[Idx] = &MCIS;
+    WaitQueue[Idx] = IR.getInstruction();
     return;
   }
-  notifyInstructionReady(Idx);
+  notifyInstructionReady(IR);
 
   // 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
@@ -265,14 +266,14 @@ void Scheduler::scheduleInstruction(unsigned Idx, Instruction &MCIS) {
   // resources (i.e. BufferSize=1) is consumed.
 
   if (!IsZeroLatency && !Resources->mustIssueImmediately(Desc)) {
-    DEBUG(dbgs() << "[SCHEDULER] Adding " << Idx << " to the Ready Queue\n");
-    ReadyQueue[Idx] = &MCIS;
+    DEBUG(dbgs() << "[SCHEDULER] Adding " << IR << " to the Ready Queue\n");
+    ReadyQueue[IR.getSourceIndex()] = IR.getInstruction();
     return;
   }
 
-  DEBUG(dbgs() << "[SCHEDULER] Instruction " << Idx << " issued immediately\n");
+  DEBUG(dbgs() << "[SCHEDULER] Instruction " << IR << " issued immediately\n");
   // Release buffered resources and issue MCIS to the underlying pipelines.
-  issueInstruction(Idx, MCIS);
+  issueInstruction(IR);
 }
 
 void Scheduler::cycleEvent() {
@@ -282,20 +283,20 @@ void Scheduler::cycleEvent() {
   for (const ResourceRef &RR : ResourcesFreed)
     notifyResourceAvailable(RR);
 
-  SmallVector<unsigned, 4> InstructionIDs;
+  SmallVector<InstRef, 4> InstructionIDs;
   updateIssuedQueue(InstructionIDs);
-  for (unsigned Idx : InstructionIDs)
-    notifyInstructionExecuted(Idx);
+  for (const InstRef &IR : InstructionIDs)
+    notifyInstructionExecuted(IR);
   InstructionIDs.clear();
 
   updatePendingQueue(InstructionIDs);
-  for (unsigned Idx : InstructionIDs)
-    notifyInstructionReady(Idx);
+  for (const InstRef &IR : InstructionIDs)
+    notifyInstructionReady(IR);
   InstructionIDs.clear();
 
-  std::pair<unsigned, Instruction *> Inst = select();
-  while (Inst.second) {
-    issueInstruction(Inst.first, *Inst.second);
+  InstRef IR = select();
+  while (IR.isValid()) {
+    issueInstruction(IR);
 
     // Instructions that have been issued during this cycle might have unblocked
     // other dependent instructions. Dependent instructions may be issued during
@@ -303,12 +304,12 @@ void Scheduler::cycleEvent() {
     // instructions to the ReadyQueue and tell to the caller that we need
     // another round of 'issue()'.
     promoteToReadyQueue(InstructionIDs);
-    for (unsigned Idx : InstructionIDs)
-      notifyInstructionReady(Idx);
+    for (const InstRef &I : InstructionIDs)
+      notifyInstructionReady(I);
     InstructionIDs.clear();
 
     // Select the next instruction to issue.
-    Inst = select();
+    IR = select();
   }
 }
 
@@ -321,8 +322,9 @@ void Scheduler::dump() const {
 }
 #endif
 
-bool Scheduler::canBeDispatched(unsigned Index, const InstrDesc &Desc) const {
+bool Scheduler::canBeDispatched(const InstRef &IR) const {
   HWStallEvent::GenericEventType Type = HWStallEvent::Invalid;
+  const InstrDesc &Desc = IR.getInstruction()->getDesc();
 
   if (Desc.MayLoad && LSU->isLQFull())
     Type = HWStallEvent::LoadQueueFull;
@@ -340,14 +342,15 @@ bool Scheduler::canBeDispatched(unsigned Index, const InstrDesc &Desc) const {
     }
   }
 
-  Owner->notifyStallEvent(HWStallEvent(Type, Index));
+  Owner->notifyStallEvent(HWStallEvent(Type, IR));
   return false;
 }
 
 void Scheduler::issueInstructionImpl(
-    unsigned InstrIndex, Instruction &IS,
+    InstRef &IR,
     SmallVectorImpl<std::pair<ResourceRef, double>> &UsedResources) {
-  const InstrDesc &D = IS.getDesc();
+  Instruction *IS = IR.getInstruction();
+  const InstrDesc &D = IS->getDesc();
 
   // Issue the instruction and collect all the consumed resources
   // into a vector. That vector is then used to notify the listener.
@@ -355,60 +358,58 @@ void Scheduler::issueInstructionImpl(
 
   // Notify the instruction that it started executing.
   // This updates the internal state of each write.
-  IS.execute();
+  IS->execute();
 
-  if (IS.isExecuting())
-    IssuedQueue[InstrIndex] = &IS;
+  if (IS->isExecuting())
+    IssuedQueue[IR.getSourceIndex()] = IS;
 }
 
-void Scheduler::issueInstruction(unsigned InstrIndex, Instruction &IS) {
+void Scheduler::issueInstruction(InstRef &IR) {
   // Release buffered resources.
-  const InstrDesc &Desc = IS.getDesc();
+  const InstrDesc &Desc = IR.getInstruction()->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);
+  issueInstructionImpl(IR, Pipes);
+  notifyInstructionIssued(IR, Pipes);
+  if (IR.getInstruction()->isExecuted())
+    notifyInstructionExecuted(IR);
 }
 
-void Scheduler::promoteToReadyQueue(SmallVectorImpl<unsigned> &Ready) {
+void Scheduler::promoteToReadyQueue(SmallVectorImpl<InstRef> &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;) {
-    const QueueEntryTy &Entry = *I;
-    unsigned IID = Entry.first;
-    Instruction &Inst = *Entry.second;
+    const unsigned IID = I->first;
+    Instruction *IS = I->second;
 
     // Check if this instruction is now ready. In case, force
     // a transition in state using method 'update()'.
-    Inst.update();
+    IS->update();
 
-    const InstrDesc &Desc = Inst.getDesc();
+    const InstrDesc &Desc = IS->getDesc();
     bool IsMemOp = Desc.MayLoad || Desc.MayStore;
-    if (!Inst.isReady() || (IsMemOp && !LSU->isReady(IID))) {
+    if (!IS->isReady() || (IsMemOp && !LSU->isReady({IID, IS}))) {
       ++I;
       continue;
     }
 
-    Ready.emplace_back(IID);
-    ReadyQueue[IID] = &Inst;
+    Ready.emplace_back(IID, IS);
+    ReadyQueue[IID] = IS;
     auto ToRemove = I;
     ++I;
     WaitQueue.erase(ToRemove);
   }
 }
 
-std::pair<unsigned, Instruction *> Scheduler::select() {
+InstRef 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(),
                                [&](const QueueEntryTy &Entry) {
-                                 const Instruction &IS = *Entry.second;
-                                 const InstrDesc &D = IS.getDesc();
+                                 const InstrDesc &D = Entry.second->getDesc();
                                  return Resources->canBeIssued(D);
                                });
 
@@ -416,12 +417,12 @@ std::pair<unsigned, Instruction *> Scheduler::select() {
     return {0, nullptr};
 
   // We found an instruction to issue.
-  const QueueEntryTy Entry = *It;
+  InstRef IR(It->first, It->second);
   ReadyQueue.erase(It);
-  return Entry;
+  return IR;
 }
 
-void Scheduler::updatePendingQueue(SmallVectorImpl<unsigned> &Ready) {
+void Scheduler::updatePendingQueue(SmallVectorImpl<InstRef> &Ready) {
   // Notify to instructions in the pending queue that a new cycle just
   // started.
   for (QueueEntryTy Entry : WaitQueue)
@@ -429,12 +430,13 @@ void Scheduler::updatePendingQueue(SmallVectorImpl<unsigned> &Ready) {
   promoteToReadyQueue(Ready);
 }
 
-void Scheduler::updateIssuedQueue(SmallVectorImpl<unsigned> &Executed) {
+void Scheduler::updateIssuedQueue(SmallVectorImpl<InstRef> &Executed) {
   for (auto I = IssuedQueue.begin(), E = IssuedQueue.end(); I != E;) {
     const QueueEntryTy Entry = *I;
-    Entry.second->cycleEvent();
-    if (Entry.second->isExecuted()) {
-      Executed.push_back(Entry.first);
+    Instruction *IS = Entry.second;
+    IS->cycleEvent();
+    if (IS->isExecuted()) {
+      Executed.push_back({Entry.first, Entry.second});
       auto ToRemove = I;
       ++I;
       IssuedQueue.erase(ToRemove);
@@ -447,32 +449,30 @@ void Scheduler::updateIssuedQueue(SmallVectorImpl<unsigned> &Executed) {
 }
 
 void Scheduler::notifyInstructionIssued(
-    unsigned Index, ArrayRef<std::pair<ResourceRef, double>> Used) {
+    const InstRef &IR, ArrayRef<std::pair<ResourceRef, double>> Used) {
   DEBUG({
-    dbgs() << "[E] Instruction Issued: " << Index << '\n';
+    dbgs() << "[E] Instruction Issued: " << IR << '\n';
     for (const std::pair<ResourceRef, unsigned> &Resource : Used) {
       dbgs() << "[E] Resource Used: [" << Resource.first.first << '.'
              << Resource.first.second << "]\n";
       dbgs() << "           cycles: " << Resource.second << '\n';
     }
   });
-  Owner->notifyInstructionEvent(HWInstructionIssuedEvent(Index, Used));
+  Owner->notifyInstructionEvent(HWInstructionIssuedEvent(IR, Used));
 }
 
-void Scheduler::notifyInstructionExecuted(unsigned Index) {
-  LSU->onInstructionExecuted(Index);
-  DEBUG(dbgs() << "[E] Instruction Executed: " << Index << '\n');
+void Scheduler::notifyInstructionExecuted(const InstRef &IR) {
+  LSU->onInstructionExecuted(IR);
+  DEBUG(dbgs() << "[E] Instruction Executed: " << IR << '\n');
   Owner->notifyInstructionEvent(
-      HWInstructionEvent(HWInstructionEvent::Executed, Index));
-
-  const Instruction &IS = Owner->getInstruction(Index);
-  DU->onInstructionExecuted(IS.getRCUTokenID());
+      HWInstructionEvent(HWInstructionEvent::Executed, IR));
+  DU->onInstructionExecuted(IR.getInstruction()->getRCUTokenID());
 }
 
-void Scheduler::notifyInstructionReady(unsigned Index) {
-  DEBUG(dbgs() << "[E] Instruction Ready: " << Index << '\n');
+void Scheduler::notifyInstructionReady(const InstRef &IR) {
+  DEBUG(dbgs() << "[E] Instruction Ready: " << IR << '\n');
   Owner->notifyInstructionEvent(
-      HWInstructionEvent(HWInstructionEvent::Ready, Index));
+      HWInstructionEvent(HWInstructionEvent::Ready, IR));
 }
 
 void Scheduler::notifyResourceAvailable(const ResourceRef &RR) {
index 25bc87e..b4bd5ef 100644 (file)
@@ -419,10 +419,10 @@ class Scheduler {
   std::map<unsigned, Instruction *> IssuedQueue;
 
   void
-  notifyInstructionIssued(unsigned Index,
+  notifyInstructionIssued(const InstRef &IR,
                           llvm::ArrayRef<std::pair<ResourceRef, double>> Used);
-  void notifyInstructionExecuted(unsigned Index);
-  void notifyInstructionReady(unsigned Index);
+  void notifyInstructionExecuted(const InstRef &IR);
+  void notifyInstructionReady(const InstRef &IR);
   void notifyResourceAvailable(const ResourceRef &RR);
 
   // Notify the Backend that buffered resources were consumed.
@@ -432,19 +432,19 @@ class Scheduler {
 
   /// Select the next instruction to issue from the ReadyQueue.
   /// This method gives priority to older instructions.
-  std::pair<unsigned, Instruction *> select();
+  InstRef select();
 
   /// Move instructions from the WaitQueue to the ReadyQueue if input operands
   /// are all available.
-  void promoteToReadyQueue(llvm::SmallVectorImpl<unsigned> &Ready);
+  void promoteToReadyQueue(llvm::SmallVectorImpl<InstRef> &Ready);
 
   /// Issue an instruction without updating the ready queue.
   void issueInstructionImpl(
-      unsigned Index, Instruction &IS,
+      InstRef &IR,
       llvm::SmallVectorImpl<std::pair<ResourceRef, double>> &Pipes);
 
-  void updatePendingQueue(llvm::SmallVectorImpl<unsigned> &Ready);
-  void updateIssuedQueue(llvm::SmallVectorImpl<unsigned> &Executed);
+  void updatePendingQueue(llvm::SmallVectorImpl<InstRef> &Ready);
+  void updateIssuedQueue(llvm::SmallVectorImpl<InstRef> &Executed);
 
 public:
   Scheduler(Backend *B, const llvm::MCSchedModel &Model, unsigned LoadQueueSize,
@@ -456,18 +456,18 @@ public:
 
   void setDispatchUnit(DispatchUnit *DispUnit) { DU = DispUnit; }
 
-  /// Check if instruction at index Idx can be dispatched.
+  /// Check if the instruction in 'IR' can be dispatched.
   ///
   /// The DispatchUnit is responsible for querying the Scheduler before
   /// dispatching new instructions. Queries are performed through method
   /// `Scheduler::CanBeDispatched`. If scheduling resources are available,
   /// and the instruction can be dispatched, then this method returns true.
   /// Otherwise, a generic HWStallEvent is notified to the listeners.
-  bool canBeDispatched(unsigned Idx, const InstrDesc &Desc) const;
-  void scheduleInstruction(unsigned Idx, Instruction &MCIS);
+  bool canBeDispatched(const InstRef &IR) const;
+  void scheduleInstruction(InstRef &IR);
 
   /// Issue an instruction.
-  void issueInstruction(unsigned Index, Instruction &IS);
+  void issueInstruction(InstRef &IR);
 
   /// Reserve one entry in each buffered resource.
   void reserveBuffers(llvm::ArrayRef<uint64_t> Buffers) {
index 4d4781f..afde35f 100644 (file)
@@ -21,7 +21,7 @@
 
 namespace mca {
 
-typedef std::pair<unsigned, const llvm::MCInst *> InstRef;
+typedef std::pair<unsigned, const llvm::MCInst *> SourceRef;
 
 class SourceMgr {
   using InstVec = std::vector<std::unique_ptr<const llvm::MCInst>>;
@@ -43,9 +43,9 @@ public:
   bool hasNext() { return Current < (Iterations * size()); }
   void updateNext() { Current++; }
 
-  const InstRef peekNext() const {
+  const SourceRef peekNext() const {
     unsigned Index = getCurrentInstructionIndex();
-    return InstRef(Current, Sequence[Index].get());
+    return SourceRef(Current, Sequence[Index].get());
   }
 
   unsigned getCurrentInstructionIndex() const {
index 01fb84e..952b5bd 100644 (file)
@@ -35,15 +35,16 @@ void TimelineView::initialize(unsigned MaxIterations) {
 }
 
 void TimelineView::onInstructionEvent(const HWInstructionEvent &Event) {
-  if (CurrentCycle >= MaxCycle || Event.Index >= Timeline.size())
+  const unsigned Index = Event.IR.getSourceIndex();
+  if (CurrentCycle >= MaxCycle || Index >= Timeline.size())
     return;
   switch (Event.Type) {
   case HWInstructionEvent::Retired: {
-    TimelineViewEntry &TVEntry = Timeline[Event.Index];
+    TimelineViewEntry &TVEntry = Timeline[Index];
     TVEntry.CycleRetired = CurrentCycle;
 
     // Update the WaitTime entry which corresponds to this Index.
-    WaitTimeEntry &WTEntry = WaitTime[Event.Index % AsmSequence.size()];
+    WaitTimeEntry &WTEntry = WaitTime[Index % AsmSequence.size()];
     WTEntry.Executions++;
     WTEntry.CyclesSpentInSchedulerQueue +=
         TVEntry.CycleIssued - TVEntry.CycleDispatched;
@@ -55,16 +56,16 @@ void TimelineView::onInstructionEvent(const HWInstructionEvent &Event) {
     break;
   }
   case HWInstructionEvent::Ready:
-    Timeline[Event.Index].CycleReady = CurrentCycle;
+    Timeline[Index].CycleReady = CurrentCycle;
     break;
   case HWInstructionEvent::Issued:
-    Timeline[Event.Index].CycleIssued = CurrentCycle;
+    Timeline[Index].CycleIssued = CurrentCycle;
     break;
   case HWInstructionEvent::Executed:
-    Timeline[Event.Index].CycleExecuted = CurrentCycle;
+    Timeline[Index].CycleExecuted = CurrentCycle;
     break;
   case HWInstructionEvent::Dispatched:
-    Timeline[Event.Index].CycleDispatched = CurrentCycle;
+    Timeline[Index].CycleDispatched = CurrentCycle;
     break;
   default:
     return;