Index: tools/llvm-mca/Backend.h =================================================================== --- tools/llvm-mca/Backend.h +++ tools/llvm-mca/Backend.h @@ -80,13 +80,9 @@ 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: tools/llvm-mca/Backend.cpp =================================================================== --- tools/llvm-mca/Backend.cpp +++ tools/llvm-mca/Backend.cpp @@ -32,16 +32,15 @@ notifyCycleBegin(Cycle); while (SM.hasNext()) { - InstRef IR = SM.peekNext(); - std::unique_ptr NewIS = IB.createInstruction(*IR.second); + SourceRef SR = SM.peekNext(); + std::unique_ptr 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: tools/llvm-mca/Dispatch.h =================================================================== --- tools/llvm-mca/Dispatch.h +++ tools/llvm-mca/Dispatch.h @@ -189,12 +189,12 @@ std::unique_ptr 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 UsedPhysRegs); public: @@ -214,14 +214,12 @@ 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 &Vec, unsigned RegID) const { @@ -235,9 +233,9 @@ 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: tools/llvm-mca/Dispatch.cpp =================================================================== --- tools/llvm-mca/Dispatch.cpp +++ tools/llvm-mca/Dispatch.cpp @@ -252,50 +252,48 @@ } #endif -void DispatchUnit::notifyInstructionDispatched(unsigned Index, +void DispatchUnit::notifyInstructionDispatched(const InstRef &IR, ArrayRef 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 FreedRegs(RAT->getNumRegisterFiles()); - for (const std::unique_ptr &WS : IS.getDefs()) + for (const std::unique_ptr &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 RegDefs; - for (const std::unique_ptr &RegDef : Instr.getDefs()) + for (const std::unique_ptr &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 @@ 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 @@ // 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 &RS : NewInst->getUses()) + for (std::unique_ptr &RS : IS.getUses()) updateRAWDependencies(*RS, STI); // Allocate new mappings. SmallVector RegisterFiles(RAT->getNumRegisterFiles()); - for (std::unique_ptr &WS : NewInst->getDefs()) + for (std::unique_ptr &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: tools/llvm-mca/HWEventListener.h =================================================================== --- tools/llvm-mca/HWEventListener.h +++ tools/llvm-mca/HWEventListener.h @@ -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 @@ -48,30 +48,30 @@ 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; - HWInstructionIssuedEvent(unsigned Index, + HWInstructionIssuedEvent(const InstRef &IR, llvm::ArrayRef> UR) - : HWInstructionEvent(HWInstructionEvent::Issued, Index), - UsedResources(UR) {} + : HWInstructionEvent(HWInstructionEvent::Issued, IR), UsedResources(UR) {} llvm::ArrayRef> UsedResources; }; class HWInstructionDispatchedEvent : public HWInstructionEvent { public: - HWInstructionDispatchedEvent(unsigned Index, llvm::ArrayRef Regs) - : HWInstructionEvent(HWInstructionEvent::Dispatched, Index), + HWInstructionDispatchedEvent(const InstRef &IR, llvm::ArrayRef 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 @@ class HWInstructionRetiredEvent : public HWInstructionEvent { public: - HWInstructionRetiredEvent(unsigned Index, llvm::ArrayRef Regs) - : HWInstructionEvent(HWInstructionEvent::Retired, Index), + HWInstructionRetiredEvent(const InstRef &IR, llvm::ArrayRef 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 @@ 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: tools/llvm-mca/Instruction.h =================================================================== --- tools/llvm-mca/Instruction.h +++ tools/llvm-mca/Instruction.h @@ -17,6 +17,7 @@ #define LLVM_TOOLS_LLVM_MCA_INSTRUCTION_H #include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" #include #include #include @@ -30,6 +31,36 @@ 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 { +public: + InstRef() : std::pair(0, nullptr) {} + InstRef(unsigned Index, Instruction *I) + : std::pair(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: tools/llvm-mca/InstructionTables.cpp =================================================================== --- tools/llvm-mca/InstructionTables.cpp +++ tools/llvm-mca/InstructionTables.cpp @@ -30,8 +30,8 @@ // Create an instruction descriptor for every instruction in the sequence. while (S.hasNext()) { UsedResources.clear(); - InstRef IR = S.peekNext(); - std::unique_ptr Inst = IB.createInstruction(*IR.second); + SourceRef SR = S.peekNext(); + std::unique_ptr Inst = IB.createInstruction(*SR.second); const InstrDesc &Desc = Inst->getDesc(); // Now identify the resources consumed by this instruction. for (const std::pair Resource : Desc.Resources) { @@ -70,7 +70,8 @@ } // 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 &Listener : Views) Listener->onInstructionEvent(Event); S.updateNext(); Index: tools/llvm-mca/LSUnit.h =================================================================== --- tools/llvm-mca/LSUnit.h +++ tools/llvm-mca/LSUnit.h @@ -24,6 +24,7 @@ namespace mca { +class InstRef; struct InstrDesc; /// A Load/Store Unit implementing a load and store queues. @@ -132,7 +133,7 @@ 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 @@ // 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: tools/llvm-mca/LSUnit.cpp =================================================================== --- tools/llvm-mca/LSUnit.cpp +++ tools/llvm-mca/LSUnit.cpp @@ -50,13 +50,15 @@ 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 @@ 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 @@ return !IsAStore; } -void LSUnit::onInstructionExecuted(unsigned Index) { +void LSUnit::onInstructionExecuted(const InstRef &IR) { + const unsigned Index = IR.getSourceIndex(); std::set::iterator it = LoadQueue.find(Index); if (it != LoadQueue.end()) { DEBUG(dbgs() << "[LSUnit]: Instruction idx=" << Index Index: tools/llvm-mca/ResourcePressureView.cpp =================================================================== --- tools/llvm-mca/ResourcePressureView.cpp +++ tools/llvm-mca/ResourcePressureView.cpp @@ -44,7 +44,7 @@ if (Event.Type != HWInstructionEvent::Issued) return; const auto &IssueEvent = static_cast(Event); - unsigned SourceIdx = Event.Index % Source.size(); + const unsigned SourceIdx = Event.IR.getSourceIndex() % Source.size(); for (const std::pair &Use : IssueEvent.UsedResources) { const ResourceRef &RR = Use.first; assert(Resource2VecIndex.find(RR.first) != Resource2VecIndex.end()); Index: tools/llvm-mca/RetireControlUnit.h =================================================================== --- tools/llvm-mca/RetireControlUnit.h +++ tools/llvm-mca/RetireControlUnit.h @@ -47,7 +47,7 @@ // 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 @@ } // 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: tools/llvm-mca/RetireControlUnit.cpp =================================================================== --- tools/llvm-mca/RetireControlUnit.cpp +++ tools/llvm-mca/RetireControlUnit.cpp @@ -28,7 +28,7 @@ } // 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(Queue.size())); @@ -37,7 +37,7 @@ // 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 @@ 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::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: tools/llvm-mca/Scheduler.h =================================================================== --- tools/llvm-mca/Scheduler.h +++ tools/llvm-mca/Scheduler.h @@ -419,10 +419,10 @@ std::map IssuedQueue; void - notifyInstructionIssued(unsigned Index, + notifyInstructionIssued(const InstRef &IR, llvm::ArrayRef> 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 @@ /// Select the next instruction to issue from the ReadyQueue. /// This method gives priority to older instructions. - std::pair select(); + InstRef select(); /// Move instructions from the WaitQueue to the ReadyQueue if input operands /// are all available. - void promoteToReadyQueue(llvm::SmallVectorImpl &Ready); + void promoteToReadyQueue(llvm::SmallVectorImpl &Ready); /// Issue an instruction without updating the ready queue. void issueInstructionImpl( - unsigned Index, Instruction &IS, + InstRef &IR, llvm::SmallVectorImpl> &Pipes); - void updatePendingQueue(llvm::SmallVectorImpl &Ready); - void updateIssuedQueue(llvm::SmallVectorImpl &Executed); + void updatePendingQueue(llvm::SmallVectorImpl &Ready); + void updateIssuedQueue(llvm::SmallVectorImpl &Executed); public: Scheduler(Backend *B, const llvm::MCSchedModel &Model, unsigned LoadQueueSize, @@ -456,18 +456,18 @@ 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 Buffers) { Index: tools/llvm-mca/Scheduler.cpp =================================================================== --- tools/llvm-mca/Scheduler.cpp +++ tools/llvm-mca/Scheduler.cpp @@ -228,7 +228,8 @@ 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 @@ // 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 @@ // 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 @@ for (const ResourceRef &RR : ResourcesFreed) notifyResourceAvailable(RR); - SmallVector InstructionIDs; + SmallVector 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 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 @@ // 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 @@ } #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 @@ } } - Owner->notifyStallEvent(HWStallEvent(Type, Index)); + Owner->notifyStallEvent(HWStallEvent(Type, IR)); return false; } void Scheduler::issueInstructionImpl( - unsigned InstrIndex, Instruction &IS, + InstRef &IR, SmallVectorImpl> &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 @@ // 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, 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 &Ready) { +void Scheduler::promoteToReadyQueue(SmallVectorImpl &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 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 @@ 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 &Ready) { +void Scheduler::updatePendingQueue(SmallVectorImpl &Ready) { // Notify to instructions in the pending queue that a new cycle just // started. for (QueueEntryTy Entry : WaitQueue) @@ -429,12 +430,13 @@ promoteToReadyQueue(Ready); } -void Scheduler::updateIssuedQueue(SmallVectorImpl &Executed) { +void Scheduler::updateIssuedQueue(SmallVectorImpl &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::notifyInstructionIssued( - unsigned Index, ArrayRef> Used) { + const InstRef &IR, ArrayRef> Used) { DEBUG({ - dbgs() << "[E] Instruction Issued: " << Index << '\n'; + dbgs() << "[E] Instruction Issued: " << IR << '\n'; for (const std::pair &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: tools/llvm-mca/SourceMgr.h =================================================================== --- tools/llvm-mca/SourceMgr.h +++ tools/llvm-mca/SourceMgr.h @@ -21,7 +21,7 @@ namespace mca { -typedef std::pair InstRef; +typedef std::pair SourceRef; class SourceMgr { using InstVec = std::vector>; @@ -43,9 +43,9 @@ 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: tools/llvm-mca/TimelineView.cpp =================================================================== --- tools/llvm-mca/TimelineView.cpp +++ tools/llvm-mca/TimelineView.cpp @@ -35,15 +35,16 @@ } 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 @@ 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;