diff options
Diffstat (limited to 'llvm/tools')
| -rw-r--r-- | llvm/tools/llvm-mca/Backend.cpp | 15 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/Backend.h | 8 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/Dispatch.cpp | 54 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/Dispatch.h | 22 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/HWEventListener.h | 31 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/Instruction.h | 31 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/InstructionTables.cpp | 7 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/LSUnit.cpp | 10 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/LSUnit.h | 7 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/ResourcePressureView.cpp | 2 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/RetireControlUnit.cpp | 9 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/RetireControlUnit.h | 4 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/Scheduler.cpp | 132 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/Scheduler.h | 24 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/SourceMgr.h | 6 | ||||
| -rw-r--r-- | llvm/tools/llvm-mca/TimelineView.cpp | 15 |
16 files changed, 204 insertions, 173 deletions
diff --git a/llvm/tools/llvm-mca/Backend.cpp b/llvm/tools/llvm-mca/Backend.cpp index f000837be59..de345108fae 100644 --- a/llvm/tools/llvm-mca/Backend.cpp +++ b/llvm/tools/llvm-mca/Backend.cpp @@ -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(); } diff --git a/llvm/tools/llvm-mca/Backend.h b/llvm/tools/llvm-mca/Backend.h index d294b610731..fee3ca2a948 100644 --- a/llvm/tools/llvm-mca/Backend.h +++ b/llvm/tools/llvm-mca/Backend.h @@ -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); diff --git a/llvm/tools/llvm-mca/Dispatch.cpp b/llvm/tools/llvm-mca/Dispatch.cpp index 4984485d882..b077f0af21d 100644 --- a/llvm/tools/llvm-mca/Dispatch.cpp +++ b/llvm/tools/llvm-mca/Dispatch.cpp @@ -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 diff --git a/llvm/tools/llvm-mca/Dispatch.h b/llvm/tools/llvm-mca/Dispatch.h index 205c12c3079..b6df6ad2fdb 100644 --- a/llvm/tools/llvm-mca/Dispatch.h +++ b/llvm/tools/llvm-mca/Dispatch.h @@ -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); diff --git a/llvm/tools/llvm-mca/HWEventListener.h b/llvm/tools/llvm-mca/HWEventListener.h index 4b58115af5d..ce4cf21f1e7 100644 --- a/llvm/tools/llvm-mca/HWEventListener.h +++ b/llvm/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 <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 { diff --git a/llvm/tools/llvm-mca/Instruction.h b/llvm/tools/llvm-mca/Instruction.h index 21fec94eeb5..e90f515d772 100644 --- a/llvm/tools/llvm-mca/Instruction.h +++ b/llvm/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 <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. diff --git a/llvm/tools/llvm-mca/InstructionTables.cpp b/llvm/tools/llvm-mca/InstructionTables.cpp index ae96568f166..79ca87048af 100644 --- a/llvm/tools/llvm-mca/InstructionTables.cpp +++ b/llvm/tools/llvm-mca/InstructionTables.cpp @@ -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(); diff --git a/llvm/tools/llvm-mca/LSUnit.cpp b/llvm/tools/llvm-mca/LSUnit.cpp index c2358308270..1b6d8485cfe 100644 --- a/llvm/tools/llvm-mca/LSUnit.cpp +++ b/llvm/tools/llvm-mca/LSUnit.cpp @@ -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 diff --git a/llvm/tools/llvm-mca/LSUnit.h b/llvm/tools/llvm-mca/LSUnit.h index d291a09eb0e..85911070f15 100644 --- a/llvm/tools/llvm-mca/LSUnit.h +++ b/llvm/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 @@ 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 diff --git a/llvm/tools/llvm-mca/ResourcePressureView.cpp b/llvm/tools/llvm-mca/ResourcePressureView.cpp index b3b478b4677..6fd23ccf346 100644 --- a/llvm/tools/llvm-mca/ResourcePressureView.cpp +++ b/llvm/tools/llvm-mca/ResourcePressureView.cpp @@ -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()); diff --git a/llvm/tools/llvm-mca/RetireControlUnit.cpp b/llvm/tools/llvm-mca/RetireControlUnit.cpp index bd0fc28c6ba..da93c2c11c1 100644 --- a/llvm/tools/llvm-mca/RetireControlUnit.cpp +++ b/llvm/tools/llvm-mca/RetireControlUnit.cpp @@ -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; } diff --git a/llvm/tools/llvm-mca/RetireControlUnit.h b/llvm/tools/llvm-mca/RetireControlUnit.h index 0c74442aefe..8958755fd6e 100644 --- a/llvm/tools/llvm-mca/RetireControlUnit.h +++ b/llvm/tools/llvm-mca/RetireControlUnit.h @@ -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(); diff --git a/llvm/tools/llvm-mca/Scheduler.cpp b/llvm/tools/llvm-mca/Scheduler.cpp index a42cbef2eff..5df677d741b 100644 --- a/llvm/tools/llvm-mca/Scheduler.cpp +++ b/llvm/tools/llvm-mca/Scheduler.cpp @@ -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) { diff --git a/llvm/tools/llvm-mca/Scheduler.h b/llvm/tools/llvm-mca/Scheduler.h index 25bc87e3eba..b4bd5ef930b 100644 --- a/llvm/tools/llvm-mca/Scheduler.h +++ b/llvm/tools/llvm-mca/Scheduler.h @@ -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) { diff --git a/llvm/tools/llvm-mca/SourceMgr.h b/llvm/tools/llvm-mca/SourceMgr.h index 4d4781f3b3a..afde35f7e00 100644 --- a/llvm/tools/llvm-mca/SourceMgr.h +++ b/llvm/tools/llvm-mca/SourceMgr.h @@ -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 { diff --git a/llvm/tools/llvm-mca/TimelineView.cpp b/llvm/tools/llvm-mca/TimelineView.cpp index 01fb84e79ab..952b5bd6e7b 100644 --- a/llvm/tools/llvm-mca/TimelineView.cpp +++ b/llvm/tools/llvm-mca/TimelineView.cpp @@ -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; |

