Index: tools/llvm-mca/Backend.h =================================================================== --- tools/llvm-mca/Backend.h +++ tools/llvm-mca/Backend.h @@ -23,6 +23,7 @@ namespace mca { class HWEventListener; +class HWInstructionEvent; /// \brief An out of order backend for a specific subtarget. /// @@ -71,6 +72,7 @@ RegisterFileSize, MaxRetirePerCycle, DispatchWidth, HWS.get())), SM(Source), Cycles(0) { IB = llvm::make_unique(MCII, getProcResourceMasks()); + HWS->setDispatchUnit(DU.get()); } void run() { @@ -80,6 +82,13 @@ unsigned getNumIterations() const { return SM.getNumIterations(); } unsigned getNumInstructions() const { return SM.size(); } + 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 *Instructions.find(Index)->second; + } + void eraseInstruction(unsigned Index) { Instructions.erase(Index); } unsigned getNumCycles() const { return Cycles; } unsigned getTotalRegisterMappingsCreated() const { return DU->getTotalRegisterMappingsCreated(); @@ -125,14 +134,8 @@ void addEventListener(HWEventListener *Listener); void notifyCycleBegin(unsigned Cycle); - void notifyInstructionDispatched(unsigned Index); - void notifyInstructionReady(unsigned Index); - void notifyInstructionIssued( - unsigned Index, - const llvm::ArrayRef> &Used); - void notifyInstructionExecuted(unsigned Index); + void notifyInstructionEvent(const HWInstructionEvent &Event); void notifyResourceAvailable(const ResourceRef &RR); - void notifyInstructionRetired(unsigned Index); void notifyCycleEnd(unsigned Cycle); }; Index: tools/llvm-mca/Backend.cpp =================================================================== --- tools/llvm-mca/Backend.cpp +++ tools/llvm-mca/Backend.cpp @@ -65,50 +65,9 @@ HWS->cycleEvent(Cycle); } -void Backend::notifyInstructionDispatched(unsigned Index) { - DEBUG(dbgs() << "[E] Instruction Dispatched: " << Index << '\n'); +void Backend::notifyInstructionEvent(const HWInstructionEvent &Event) { for (HWEventListener *Listener : Listeners) - Listener->onInstructionDispatched(Index); -} - -void Backend::notifyInstructionReady(unsigned Index) { - DEBUG(dbgs() << "[E] Instruction Ready: " << Index << '\n'); - for (HWEventListener *Listener : Listeners) - Listener->onInstructionReady(Index); -} - -void Backend::notifyInstructionIssued( - unsigned Index, const ArrayRef> &Used) { - DEBUG( - dbgs() << "[E] Instruction Issued: " << Index << '\n'; - for (const std::pair &Resource : Used) { - dbgs() << "[E] Resource Used: [" << Resource.first.first << '.' - << Resource.first.second << "]\n"; - dbgs() << " cycles: " << Resource.second << '\n'; - } - ); - - for (HWEventListener *Listener : Listeners) - Listener->onInstructionIssued(Index, Used); -} - -void Backend::notifyInstructionExecuted(unsigned Index) { - DEBUG(dbgs() << "[E] Instruction Executed: " << Index << '\n'); - for (HWEventListener *Listener : Listeners) - Listener->onInstructionExecuted(Index); - - const Instruction &IS = *Instructions[Index]; - DU->onInstructionExecuted(IS.getRCUTokenID()); -} - -void Backend::notifyInstructionRetired(unsigned Index) { - DEBUG(dbgs() << "[E] Instruction Retired: " << Index << '\n'); - for (HWEventListener *Listener : Listeners) - Listener->onInstructionRetired(Index); - - const Instruction &IS = *Instructions[Index]; - DU->invalidateRegisterMappings(IS); - Instructions.erase(Index); + Listener->onInstructionEvent(Event); } void Backend::notifyResourceAvailable(const ResourceRef &RR) { Index: tools/llvm-mca/BackendStatistics.h =================================================================== --- tools/llvm-mca/BackendStatistics.h +++ tools/llvm-mca/BackendStatistics.h @@ -110,14 +110,7 @@ BackendStatistics(const Backend &backend) : B(backend), NumDispatched(0), NumIssued(0), NumRetired(0) {} - void onInstructionDispatched(unsigned Index) override { NumDispatched++; } - void - onInstructionIssued(unsigned Index, - const llvm::ArrayRef> - & /* unused */) override { - NumIssued++; - } - void onInstructionRetired(unsigned Index) override { NumRetired++; } + void onInstructionEvent(const HWInstructionEvent &Event) override; void onCycleBegin(unsigned Cycle) override { NumCycles++; } Index: tools/llvm-mca/BackendStatistics.cpp =================================================================== --- tools/llvm-mca/BackendStatistics.cpp +++ tools/llvm-mca/BackendStatistics.cpp @@ -20,6 +20,22 @@ namespace mca { +void BackendStatistics::onInstructionEvent(const HWInstructionEvent &Event) { + switch (Event.Type) { + case HWInstructionEvent::Retired: + ++NumRetired; + break; + case HWInstructionEvent::Issued: + ++NumIssued; + break; + case HWInstructionEvent::Dispatched: + ++NumDispatched; + break; + default: + break; + } +} + void BackendStatistics::printRetireUnitStatistics(llvm::raw_ostream &OS) const { std::string Buffer; raw_string_ostream TempStream(Buffer); Index: tools/llvm-mca/Dispatch.cpp =================================================================== --- tools/llvm-mca/Dispatch.cpp +++ tools/llvm-mca/Dispatch.cpp @@ -15,6 +15,7 @@ #include "Dispatch.h" #include "Backend.h" +#include "HWEventListener.h" #include "Scheduler.h" #include "llvm/Support/Debug.h" @@ -150,11 +151,19 @@ } void DispatchUnit::notifyInstructionDispatched(unsigned Index) { - Owner->notifyInstructionDispatched(Index); + DEBUG(dbgs() << "[E] Instruction Dispatched: " << Index << '\n'); + Owner->notifyInstructionEvent( + HWInstructionEvent(HWInstructionEvent::Dispatched, Index)); } void DispatchUnit::notifyInstructionRetired(unsigned Index) { - Owner->notifyInstructionRetired(Index); + DEBUG(dbgs() << "[E] Instruction Retired: " << Index << '\n'); + Owner->notifyInstructionEvent( + HWInstructionEvent(HWInstructionEvent::Retired, Index)); + + const Instruction &IS = Owner->getInstruction(Index); + invalidateRegisterMappings(IS); + Owner->eraseInstruction(Index); } void RetireControlUnit::cycleEvent() { @@ -243,7 +252,7 @@ // Reserve slots in the RCU. unsigned RCUTokenID = RCU->reserveSlot(IID, NumMicroOps); NewInst->setRCUTokenID(RCUTokenID); - Owner->notifyInstructionDispatched(IID); + notifyInstructionDispatched(IID); SC->scheduleInstruction(IID, NewInst); return RCUTokenID; Index: tools/llvm-mca/HWEventListener.h =================================================================== --- tools/llvm-mca/HWEventListener.h +++ tools/llvm-mca/HWEventListener.h @@ -21,28 +21,67 @@ namespace mca { -class HWEventListener { +// An HWInstructionEvent represents state changes of instructions that +// listeners might be interested in. Listeners can choose to ignore any event +// they are not interested in. +class HWInstructionEvent { public: - // Events generated by the Retire Control Unit. - virtual void onInstructionRetired(unsigned Index) {}; + // This is the list of event types that are shared by all targets, that + // generic subtarget-agnostic classes (e.g. Backend, HWInstructionEvent, ...) + // and generic Views can manipulate. + // Subtargets are free to define additional event types, that are goin to be + // handled by generic components as opaque values, but can still be + // emitted by subtarget-specific pipeline components (e.g. Scheduler, + // DispatchUnit, ...) and interpreted by subtarget-specific EventListener + // implementations. + enum GenericEventType { + Invalid = 0, + // Events generated by the Retire Control Unit. + Retired, + // Events generated by the Scheduler. + Ready, + Issued, + Executed, + // Events generated by the Dispatch logic. + Dispatched, + + LastGenericEventType, + }; - // Events generated by the Scheduler. + HWInstructionEvent(unsigned Type, unsigned Index) + : Type(Type), Index(Index) {} + + // 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; +}; + +class HWInstructionIssuedEvent : public HWInstructionEvent { +public: using ResourceRef = std::pair; - virtual void - onInstructionIssued(unsigned Index, - const llvm::ArrayRef> &Used) {} - virtual void onInstructionExecuted(unsigned Index) {} - virtual void onInstructionReady(unsigned Index) {} - virtual void onResourceAvailable(const ResourceRef &RRef) {}; + HWInstructionIssuedEvent(unsigned Index, + llvm::ArrayRef> UR) + : HWInstructionEvent(HWInstructionEvent::Issued, Index), + UsedResources(UR) {} - // Events generated by the Dispatch logic. - virtual void onInstructionDispatched(unsigned Index) {} + const llvm::ArrayRef> UsedResources; +}; - // Generic events generated by the Backend. +class HWEventListener { +public: + // Generic events generated by the backend pipeline. virtual void onCycleBegin(unsigned Cycle) {} virtual void onCycleEnd(unsigned Cycle) {} - virtual ~HWEventListener() = default; + virtual void onInstructionEvent(const HWInstructionEvent &Event) {} + + using ResourceRef = std::pair; + virtual void onResourceAvailable(const ResourceRef &RRef) {} + + virtual ~HWEventListener() {} + +private: virtual void anchor(); }; Index: tools/llvm-mca/ResourcePressureView.h =================================================================== --- tools/llvm-mca/ResourcePressureView.h +++ tools/llvm-mca/ResourcePressureView.h @@ -97,9 +97,7 @@ initialize(ProcResourceMasks); } - void onInstructionIssued( - unsigned Index, - const llvm::ArrayRef> &Used) override; + void onInstructionEvent(const HWInstructionEvent &Event) override; void printView(llvm::raw_ostream &OS) const override { unsigned Executions = Source.getNumIterations(); Index: tools/llvm-mca/ResourcePressureView.cpp =================================================================== --- tools/llvm-mca/ResourcePressureView.cpp +++ tools/llvm-mca/ResourcePressureView.cpp @@ -42,10 +42,13 @@ std::fill(ResourceUsage.begin(), ResourceUsage.end(), 0); } -void ResourcePressureView::onInstructionIssued( - unsigned Index, const ArrayRef> &Used) { - unsigned SourceIdx = Index % Source.size(); - for (const std::pair &Use : Used) { +void ResourcePressureView::onInstructionEvent(const HWInstructionEvent &Event) { + // We're only interested in Issue events. + if (Event.Type != HWInstructionEvent::Issued) + return; + const auto &IssueEvent = static_cast(Event); + unsigned SourceIdx = Event.Index % Source.size(); + for (const std::pair &Use : IssueEvent.UsedResources) { const ResourceRef &RR = Use.first; assert(Resource2VecIndex.find(RR.first) != Resource2VecIndex.end()); unsigned R2VIndex = Resource2VecIndex[RR.first]; Index: tools/llvm-mca/Scheduler.h =================================================================== --- tools/llvm-mca/Scheduler.h +++ tools/llvm-mca/Scheduler.h @@ -24,6 +24,8 @@ namespace mca { class Backend; +class DispatchUnit; + /// Used to notify the internal state of a processor resource. /// /// A processor resource is available if it is not reserved, and there are @@ -450,7 +452,10 @@ std::unique_ptr LSU; // The Backend gets notified when instructions are ready/issued/executed. - Backend *Owner; + Backend *const Owner; + + // The dispatch unit gets notified when instructions are executed. + DispatchUnit *DU; using QueueEntryTy = std::pair; std::map WaitQueue; @@ -481,6 +486,8 @@ AssumeNoAlias)), Owner(B) {} + void setDispatchUnit(DispatchUnit *DispUnit) { DU = DispUnit; } + /// Scheduling events. /// /// The DispatchUnit is responsible for querying the Scheduler before Index: tools/llvm-mca/Scheduler.cpp =================================================================== --- tools/llvm-mca/Scheduler.cpp +++ tools/llvm-mca/Scheduler.cpp @@ -13,6 +13,7 @@ #include "Scheduler.h" #include "Backend.h" +#include "HWEventListener.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" @@ -441,16 +442,30 @@ void Scheduler::notifyInstructionIssued( unsigned Index, const ArrayRef> &Used) { - Owner->notifyInstructionIssued(Index, Used); + DEBUG(dbgs() << "[E] Instruction Issued: " << Index << '\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)); } void Scheduler::notifyInstructionExecuted(unsigned Index) { LSU->onInstructionExecuted(Index); - Owner->notifyInstructionExecuted(Index); + DEBUG(dbgs() << "[E] Instruction Executed: " << Index << '\n'); + Owner->notifyInstructionEvent( + HWInstructionEvent(HWInstructionEvent::Executed, Index)); + + const Instruction &IS = Owner->getInstruction(Index); + DU->onInstructionExecuted(IS.getRCUTokenID()); } void Scheduler::notifyInstructionReady(unsigned Index) { - Owner->notifyInstructionReady(Index); + DEBUG(dbgs() << "[E] Instruction Ready: " << Index << '\n'); + Owner->notifyInstructionEvent( + HWInstructionEvent(HWInstructionEvent::Ready, Index)); } void Scheduler::notifyResourceAvailable(const ResourceRef &RR) { Index: tools/llvm-mca/TimelineView.h =================================================================== --- tools/llvm-mca/TimelineView.h +++ tools/llvm-mca/TimelineView.h @@ -162,14 +162,8 @@ void initialize(unsigned MaxIterations); // Event handlers. - void onInstructionDispatched(unsigned Index) override; - void onInstructionReady(unsigned Index) override; - void onInstructionIssued( - unsigned Index, - const llvm::ArrayRef> &Used) override; - void onInstructionExecuted(unsigned Index) override; - void onInstructionRetired(unsigned Index) override; void onCycleBegin(unsigned Cycle) override { CurrentCycle = Cycle; } + void onInstructionEvent(const HWInstructionEvent &Event) override; // print functionalities. void printTimeline(llvm::raw_ostream &OS) const; Index: tools/llvm-mca/TimelineView.cpp =================================================================== --- tools/llvm-mca/TimelineView.cpp +++ tools/llvm-mca/TimelineView.cpp @@ -34,53 +34,42 @@ std::fill(WaitTime.begin(), WaitTime.end(), NullWTEntry); } -void TimelineView::onInstructionDispatched(unsigned Index) { - if (CurrentCycle >= MaxCycle || Index >= Timeline.size()) +void TimelineView::onInstructionEvent(const HWInstructionEvent &Event) { + if (CurrentCycle >= MaxCycle || Event.Index >= Timeline.size()) return; - Timeline[Index].CycleDispatched = CurrentCycle; - LastCycle = std::max(LastCycle, CurrentCycle); -} - -void TimelineView::onInstructionReady(unsigned Index) { - if (CurrentCycle >= MaxCycle || Index >= Timeline.size()) - return; - Timeline[Index].CycleReady = CurrentCycle; - LastCycle = std::max(LastCycle, CurrentCycle); -} - -void TimelineView::onInstructionIssued( - unsigned Index, - const ArrayRef> & /* Unused */) { - if (CurrentCycle >= MaxCycle || Index >= Timeline.size()) - return; - Timeline[Index].CycleIssued = CurrentCycle; - LastCycle = std::max(LastCycle, CurrentCycle); -} - -void TimelineView::onInstructionExecuted(unsigned Index) { - if (CurrentCycle >= MaxCycle || Index >= Timeline.size()) - return; - Timeline[Index].CycleExecuted = CurrentCycle; - LastCycle = std::max(LastCycle, CurrentCycle); -} - -void TimelineView::onInstructionRetired(unsigned Index) { - if (CurrentCycle >= MaxCycle || Index >= Timeline.size()) + switch (Event.Type) { + case HWInstructionEvent::Retired: { + TimelineViewEntry &TVEntry = Timeline[Event.Index]; + TVEntry.CycleRetired = CurrentCycle; + + // Update the WaitTime entry which corresponds to this Index. + WaitTimeEntry &WTEntry = WaitTime[Event.Index % AsmSequence.size()]; + WTEntry.Executions++; + WTEntry.CyclesSpentInSchedulerQueue += + TVEntry.CycleIssued - TVEntry.CycleDispatched; + assert(TVEntry.CycleDispatched <= TVEntry.CycleReady); + WTEntry.CyclesSpentInSQWhileReady += + TVEntry.CycleIssued - TVEntry.CycleReady; + WTEntry.CyclesSpentAfterWBAndBeforeRetire += + (TVEntry.CycleRetired - 1) - TVEntry.CycleExecuted; + break; + } + case HWInstructionEvent::Ready: + Timeline[Event.Index].CycleReady = CurrentCycle; + break; + case HWInstructionEvent::Issued: + Timeline[Event.Index].CycleIssued = CurrentCycle; + break; + case HWInstructionEvent::Executed: + Timeline[Event.Index].CycleExecuted = CurrentCycle; + break; + case HWInstructionEvent::Dispatched: + Timeline[Event.Index].CycleDispatched = CurrentCycle; + break; + default: return; - TimelineViewEntry &TVEntry = Timeline[Index]; - TVEntry.CycleRetired = CurrentCycle; + } LastCycle = std::max(LastCycle, CurrentCycle); - - // Update the WaitTime entry which corresponds to this Index. - - WaitTimeEntry &WTEntry = WaitTime[Index % AsmSequence.size()]; - WTEntry.Executions++; - WTEntry.CyclesSpentInSchedulerQueue += - TVEntry.CycleIssued - TVEntry.CycleDispatched; - assert(TVEntry.CycleDispatched <= TVEntry.CycleReady); - WTEntry.CyclesSpentInSQWhileReady += TVEntry.CycleIssued - TVEntry.CycleReady; - WTEntry.CyclesSpentAfterWBAndBeforeRetire += - (TVEntry.CycleRetired - 1) - TVEntry.CycleExecuted; } void TimelineView::printWaitTimeEntry(raw_string_ostream &OS,