Index: llvm/trunk/tools/llvm-mca/Backend.h =================================================================== --- llvm/trunk/tools/llvm-mca/Backend.h +++ llvm/trunk/tools/llvm-mca/Backend.h @@ -94,15 +94,14 @@ unsigned getMaxUsedRegisterMappings() const { return DU->getMaxUsedRegisterMappings(); } - void getBuffersUsage(std::vector &Usage) const { - return HWS->getBuffersUsage(Usage); - } void addEventListener(HWEventListener *Listener); void notifyCycleBegin(unsigned Cycle); void notifyInstructionEvent(const HWInstructionEvent &Event); void notifyStallEvent(const HWStallEvent &Event); void notifyResourceAvailable(const ResourceRef &RR); + void notifyReservedBuffers(llvm::ArrayRef Buffers); + void notifyReleasedBuffers(llvm::ArrayRef Buffers); void notifyCycleEnd(unsigned Cycle); }; Index: llvm/trunk/tools/llvm-mca/Backend.cpp =================================================================== --- llvm/trunk/tools/llvm-mca/Backend.cpp +++ llvm/trunk/tools/llvm-mca/Backend.cpp @@ -75,6 +75,16 @@ Listener->onResourceAvailable(RR); } +void Backend::notifyReservedBuffers(ArrayRef Buffers) { + for (HWEventListener *Listener : Listeners) + Listener->onReservedBuffers(Buffers); +} + +void Backend::notifyReleasedBuffers(ArrayRef Buffers) { + for (HWEventListener *Listener : Listeners) + Listener->onReleasedBuffers(Buffers); +} + void Backend::notifyCycleEnd(unsigned Cycle) { DEBUG(dbgs() << "[E] Cycle end: " << Cycle << "\n\n"); for (HWEventListener *Listener : Listeners) Index: llvm/trunk/tools/llvm-mca/BackendStatistics.h =================================================================== --- llvm/trunk/tools/llvm-mca/BackendStatistics.h +++ llvm/trunk/tools/llvm-mca/BackendStatistics.h @@ -85,6 +85,17 @@ // is one counter for every generic stall kind (see class HWStallEvent). llvm::SmallVector HWStalls; + // Tracks the usage of a scheduler's queue. + struct BufferUsage { + unsigned SlotsInUse; + unsigned MaxUsedSlots; + }; + + // There is a map entry for each buffered resource in the scheduling model. + // Every time a buffer is consumed/freed, this view updates the corresponding + // entry. + llvm::DenseMap BufferedResources; + void updateHistograms() { DispatchGroupSizePerCycle[NumDispatched]++; IssuedPerCycle[NumIssued]++; @@ -107,8 +118,8 @@ unsigned Cycles) const; void printIssuePerCycle(const Histogram &IssuePerCycle, unsigned TotalCycles) const; - void printSchedulerUsage(llvm::raw_ostream &OS, const llvm::MCSchedModel &SM, - const llvm::ArrayRef &Usage) const; + void printSchedulerUsage(llvm::raw_ostream &OS, + const llvm::MCSchedModel &SM) const; public: BackendStatistics(const Backend &backend, const llvm::MCSubtargetInfo &sti) @@ -126,6 +137,14 @@ HWStalls[Event.Type]++; } + // Increases the number of used scheduler queue slots of every buffered + // resource in the Buffers set. + void onReservedBuffers(llvm::ArrayRef Buffers); + + // Decreases by one the number of used scheduler queue slots of every + // buffered resource in the Buffers set. + void onReleasedBuffers(llvm::ArrayRef Buffers); + void printView(llvm::raw_ostream &OS) const override { printDispatchStalls(OS); printRATStatistics(OS, B.getTotalRegisterMappingsCreated(), @@ -134,12 +153,9 @@ printSchedulerStatistics(OS); printRetireUnitStatistics(OS); - std::vector Usage; - B.getBuffersUsage(Usage); - printSchedulerUsage(OS, STI.getSchedModel(), Usage); + printSchedulerUsage(OS, STI.getSchedModel()); } }; - } // namespace mca #endif Index: llvm/trunk/tools/llvm-mca/BackendStatistics.cpp =================================================================== --- llvm/trunk/tools/llvm-mca/BackendStatistics.cpp +++ llvm/trunk/tools/llvm-mca/BackendStatistics.cpp @@ -36,6 +36,29 @@ } } +void BackendStatistics::onReservedBuffers(ArrayRef Buffers) { + for (const unsigned Buffer : Buffers) { + if (BufferedResources.find(Buffer) != BufferedResources.end()) { + BufferUsage &BU = BufferedResources[Buffer]; + BU.SlotsInUse++; + BU.MaxUsedSlots = std::max(BU.MaxUsedSlots, BU.SlotsInUse); + continue; + } + + BufferedResources.insert( + std::pair(Buffer, {1U, 1U})); + } +} + +void BackendStatistics::onReleasedBuffers(ArrayRef Buffers) { + for (const unsigned Buffer : Buffers) { + assert(BufferedResources.find(Buffer) != BufferedResources.end() && + "Buffered resource not in map?"); + BufferUsage &BU = BufferedResources[Buffer]; + BU.SlotsInUse--; + } +} + void BackendStatistics::printRetireUnitStatistics(llvm::raw_ostream &OS) const { std::string Buffer; raw_string_ostream TempStream(Buffer); @@ -126,15 +149,13 @@ OS << Buffer; } -void BackendStatistics::printSchedulerUsage( - raw_ostream &OS, const MCSchedModel &SM, - const ArrayRef &Usage) const { - +void BackendStatistics::printSchedulerUsage(raw_ostream &OS, + const MCSchedModel &SM) const { std::string Buffer; raw_string_ostream TempStream(Buffer); TempStream << "\n\nScheduler's queue usage:\n"; // Early exit if no buffered resources were consumed. - if (Usage.empty()) { + if (BufferedResources.empty()) { TempStream << "No scheduler resources used.\n"; TempStream.flush(); OS << Buffer; @@ -143,17 +164,15 @@ for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) { const MCProcResourceDesc &ProcResource = *SM.getProcResource(I); - if (!ProcResource.BufferSize) + if (ProcResource.BufferSize <= 0) continue; - for (const BufferUsageEntry &Entry : Usage) - if (I == Entry.first) - TempStream << ProcResource.Name << ", " << Entry.second << '/' - << ProcResource.BufferSize << '\n'; + const BufferUsage &BU = BufferedResources.lookup(I); + TempStream << ProcResource.Name << ", " << BU.MaxUsedSlots << '/' + << ProcResource.BufferSize << '\n'; } TempStream.flush(); OS << Buffer; } - } // namespace mca Index: llvm/trunk/tools/llvm-mca/HWEventListener.h =================================================================== --- llvm/trunk/tools/llvm-mca/HWEventListener.h +++ llvm/trunk/tools/llvm-mca/HWEventListener.h @@ -104,6 +104,11 @@ using ResourceRef = std::pair; virtual void onResourceAvailable(const ResourceRef &RRef) {} + // Events generated by the Scheduler when buffered resources are + // consumed/freed. + virtual void onReservedBuffers(llvm::ArrayRef Buffers) {} + virtual void onReleasedBuffers(llvm::ArrayRef Buffers) {} + virtual ~HWEventListener() {} private: Index: llvm/trunk/tools/llvm-mca/Scheduler.h =================================================================== --- llvm/trunk/tools/llvm-mca/Scheduler.h +++ llvm/trunk/tools/llvm-mca/Scheduler.h @@ -138,11 +138,6 @@ // Available slots in the buffer (zero, if this is not a buffered resource). unsigned AvailableSlots; - // Maximum number of buffer slots seen used during one cycle. - // This helps tracking dynamic dispatch stalls caused by the lack of - // entries in the scheduler's queue. - unsigned MaxUsedSlots; - // True if this is resource is currently unavailable. // An instruction may "reserve" a resource for a number of cycles. // During those cycles, the reserved resource cannot be used for other @@ -182,14 +177,12 @@ ReadyMask = ResourceSizeMask; BufferSize = Desc.BufferSize; AvailableSlots = BufferSize == -1 ? 0U : static_cast(BufferSize); - MaxUsedSlots = 0; Unavailable = false; } unsigned getProcResourceID() const { return ProcResourceDescIndex; } uint64_t getResourceMask() const { return ResourceMask; } int getBufferSize() const { return BufferSize; } - unsigned getMaxUsedSlots() const { return MaxUsedSlots; } bool isBuffered() const { return BufferSize > 0; } bool isInOrder() const { return BufferSize == 1; } @@ -244,8 +237,6 @@ void reserveBuffer() { if (AvailableSlots) AvailableSlots--; - unsigned UsedSlots = static_cast(BufferSize) - AvailableSlots; - MaxUsedSlots = std::max(MaxUsedSlots, UsedSlots); } void releaseBuffer() { @@ -339,8 +330,12 @@ // Returns RS_BUFFER_AVAILABLE if buffered resources are not reserved, and if // there are enough available slots in the buffers. - ResourceStateEvent - canBeDispatched(const llvm::ArrayRef Buffers) const; + ResourceStateEvent canBeDispatched(llvm::ArrayRef Buffers) const; + + // Return the processor resource identifier associated to this Mask. + unsigned resolveResourceMask(uint64_t Mask) const { + return Resources.find(Mask)->second->getProcResourceID(); + } // Consume a slot in every buffered resource from array 'Buffers'. Resource // units that are dispatch hazards (i.e. BufferSize=0) are marked as reserved. @@ -372,15 +367,6 @@ void cycleEvent(llvm::SmallVectorImpl &ResourcesFreed); - void getBuffersUsage(std::vector &Usage) const { - for (const std::pair &Resource : Resources) { - const ResourceState &RS = *Resource.second; - if (RS.isBuffered()) - Usage.emplace_back(std::pair(RS.getProcResourceID(), - RS.getMaxUsedSlots())); - } - } - #ifndef NDEBUG void dump() const { for (const std::pair &Resource : Resources) @@ -439,6 +425,11 @@ void notifyInstructionReady(unsigned Index); void notifyResourceAvailable(const ResourceRef &RR); + // Notify the Backend that buffered resources were consumed. + void notifyReservedBuffers(llvm::ArrayRef Buffers); + // Notify the Backend that buffered resources were freed. + void notifyReleasedBuffers(llvm::ArrayRef Buffers); + /// Issue instructions from the ready queue by giving priority to older /// instructions. void issue(); @@ -498,10 +489,6 @@ void cycleEvent(unsigned Cycle); - void getBuffersUsage(std::vector &Usage) const { - Resources->getBuffersUsage(Usage); - } - #ifndef NDEBUG void dump() const; #endif Index: llvm/trunk/tools/llvm-mca/Scheduler.cpp =================================================================== --- llvm/trunk/tools/llvm-mca/Scheduler.cpp +++ llvm/trunk/tools/llvm-mca/Scheduler.cpp @@ -181,7 +181,6 @@ void ResourceManager::issueInstruction( unsigned Index, const InstrDesc &Desc, SmallVectorImpl> &Pipes) { - releaseBuffers(Desc.Buffers); for (const std::pair &R : Desc.Resources) { const CycleSegment &CS = R.second.CS; if (!CS.size()) { @@ -252,11 +251,14 @@ // Consume entries in the reservation stations. const InstrDesc &Desc = MCIS.getDesc(); - // Reserve a slot in each buffered resource. Also, mark units with - // 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. - Resources->reserveBuffers(Desc.Buffers); + if (!Desc.Buffers.empty()) { + // Reserve a slot in each buffered resource. Also, mark units with + // 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. + Resources->reserveBuffers(Desc.Buffers); + notifyReservedBuffers(Desc.Buffers); + } bool MayLoad = Desc.MayLoad; bool MayStore = Desc.MayStore; @@ -331,6 +333,13 @@ } void Scheduler::issueInstruction(Instruction &IS, unsigned InstrIndex) { + const InstrDesc &D = IS.getDesc(); + + if (!D.Buffers.empty()) { + Resources->releaseBuffers(D.Buffers); + notifyReleasedBuffers(D.Buffers); + } + // Issue the instruction and collect all the consumed resources // into a vector. That vector is then used to notify the listener. // Most instructions consume very few resurces (typically one or @@ -338,8 +347,6 @@ // initialize its capacity to 4. This should address the majority of // the cases. SmallVector, 4> UsedResources; - - const InstrDesc &D = IS.getDesc(); Resources->issueInstruction(InstrIndex, D, UsedResources); // Notify the instruction that it started executing. // This updates the internal state of each write. @@ -417,13 +424,14 @@ void Scheduler::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'; - }); + 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)); } @@ -446,4 +454,20 @@ void Scheduler::notifyResourceAvailable(const ResourceRef &RR) { Owner->notifyResourceAvailable(RR); } + +void Scheduler::notifyReservedBuffers(ArrayRef Buffers) { + SmallVector BufferIDs(Buffers.begin(), Buffers.end()); + std::transform( + Buffers.begin(), Buffers.end(), BufferIDs.begin(), + [&](uint64_t Op) { return Resources->resolveResourceMask(Op); }); + Owner->notifyReservedBuffers(BufferIDs); +} + +void Scheduler::notifyReleasedBuffers(ArrayRef Buffers) { + SmallVector BufferIDs(Buffers.begin(), Buffers.end()); + std::transform( + Buffers.begin(), Buffers.end(), BufferIDs.begin(), + [&](uint64_t Op) { return Resources->resolveResourceMask(Op); }); + Owner->notifyReleasedBuffers(BufferIDs); +} } // namespace mca