| //===---------------------- ExecuteStage.cpp --------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| /// \file |
| /// |
| /// This file defines the execution stage of an instruction pipeline. |
| /// |
| /// The ExecuteStage is responsible for managing the hardware scheduler |
| /// and issuing notifications that an instruction has been executed. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "ExecuteStage.h" |
| #include "Scheduler.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/Support/Debug.h" |
| |
| #define DEBUG_TYPE "llvm-mca" |
| |
| namespace mca { |
| |
| using namespace llvm; |
| |
| // Reclaim the simulated resources used by the scheduler. |
| void ExecuteStage::reclaimSchedulerResources() { |
| SmallVector<ResourceRef, 8> ResourcesFreed; |
| HWS.reclaimSimulatedResources(ResourcesFreed); |
| for (const ResourceRef &RR : ResourcesFreed) |
| notifyResourceAvailable(RR); |
| } |
| |
| // Update the scheduler's instruction queues. |
| void ExecuteStage::updateSchedulerQueues() { |
| SmallVector<InstRef, 4> InstructionIDs; |
| HWS.updateIssuedQueue(InstructionIDs); |
| for (const InstRef &IR : InstructionIDs) |
| notifyInstructionExecuted(IR); |
| InstructionIDs.clear(); |
| |
| HWS.updatePendingQueue(InstructionIDs); |
| for (const InstRef &IR : InstructionIDs) |
| notifyInstructionReady(IR); |
| } |
| |
| // Issue instructions that are waiting in the scheduler's ready queue. |
| void ExecuteStage::issueReadyInstructions() { |
| SmallVector<InstRef, 4> InstructionIDs; |
| InstRef IR = HWS.select(); |
| while (IR.isValid()) { |
| SmallVector<std::pair<ResourceRef, double>, 4> Used; |
| HWS.issueInstruction(IR, Used); |
| |
| // Reclaim instruction resources and perform notifications. |
| const InstrDesc &Desc = IR.getInstruction()->getDesc(); |
| notifyReleasedBuffers(Desc.Buffers); |
| notifyInstructionIssued(IR, Used); |
| if (IR.getInstruction()->isExecuted()) |
| notifyInstructionExecuted(IR); |
| |
| // Instructions that have been issued during this cycle might have unblocked |
| // other dependent instructions. Dependent instructions may be issued during |
| // this same cycle if operands have ReadAdvance entries. Promote those |
| // instructions to the ReadyQueue and tell to the caller that we need |
| // another round of 'issue()'. |
| HWS.promoteToReadyQueue(InstructionIDs); |
| for (const InstRef &I : InstructionIDs) |
| notifyInstructionReady(I); |
| InstructionIDs.clear(); |
| |
| // Select the next instruction to issue. |
| IR = HWS.select(); |
| } |
| } |
| |
| // The following routine is the maintenance routine of the ExecuteStage. |
| // It is responsible for updating the hardware scheduler (HWS), including |
| // reclaiming the HWS's simulated hardware resources, as well as updating the |
| // HWS's queues. |
| // |
| // This routine also processes the instructions that are ready for issuance. |
| // These instructions are managed by the HWS's ready queue and can be accessed |
| // via the Scheduler::select() routine. |
| // |
| // Notifications are issued to this stage's listeners when instructions are |
| // moved between the HWS's queues. In particular, when an instruction becomes |
| // ready or executed. |
| void ExecuteStage::cycleStart() { |
| reclaimSchedulerResources(); |
| updateSchedulerQueues(); |
| issueReadyInstructions(); |
| } |
| |
| // Schedule the instruction for execution on the hardware. |
| bool ExecuteStage::execute(InstRef &IR) { |
| #ifndef NDEBUG |
| // Ensure that the HWS has not stored this instruction in its queues. |
| HWS.sanityCheck(IR); |
| #endif |
| // 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. |
| const InstrDesc &Desc = IR.getInstruction()->getDesc(); |
| HWS.reserveBuffers(Desc.Buffers); |
| notifyReservedBuffers(Desc.Buffers); |
| |
| // Obtain a slot in the LSU. If we cannot reserve resources, return true, so |
| // that succeeding stages can make progress. |
| if (!HWS.reserveResources(IR)) |
| return true; |
| |
| // If we did not return early, then the scheduler is ready for execution. |
| 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 |
| // because it doesn't need to be executed, and it is often removed at register |
| // renaming stage. For example, register-register moves are often optimized at |
| // register renaming stage by simply updating register aliases. On some |
| // targets, zero-idiom instructions (for example: a xor that clears the value |
| // of a register) are treated specially, and are often eliminated at register |
| // renaming stage. |
| // |
| // Instructions that use an in-order dispatch/issue processor resource must be |
| // issued immediately to the pipeline(s). Any other in-order buffered |
| // resources (i.e. BufferSize=1) is consumed. |
| // |
| // If we cannot issue immediately, the HWS will add IR to its ready queue for |
| // execution later, so we must return early here. |
| if (!HWS.issueImmediately(IR)) |
| return true; |
| |
| LLVM_DEBUG(dbgs() << "[SCHEDULER] Instruction #" << IR |
| << " issued immediately\n"); |
| |
| // Issue IR. The resources for this issuance will be placed in 'Used.' |
| SmallVector<std::pair<ResourceRef, double>, 4> Used; |
| HWS.issueInstruction(IR, Used); |
| |
| // Perform notifications. |
| notifyReleasedBuffers(Desc.Buffers); |
| notifyInstructionIssued(IR, Used); |
| if (IR.getInstruction()->isExecuted()) |
| notifyInstructionExecuted(IR); |
| |
| return true; |
| } |
| |
| void ExecuteStage::notifyInstructionExecuted(const InstRef &IR) { |
| HWS.onInstructionExecuted(IR); |
| LLVM_DEBUG(dbgs() << "[E] Instruction Executed: #" << IR << '\n'); |
| notifyEvent<HWInstructionEvent>( |
| HWInstructionEvent(HWInstructionEvent::Executed, IR)); |
| RCU.onInstructionExecuted(IR.getInstruction()->getRCUTokenID()); |
| } |
| |
| void ExecuteStage::notifyInstructionReady(const InstRef &IR) { |
| LLVM_DEBUG(dbgs() << "[E] Instruction Ready: #" << IR << '\n'); |
| notifyEvent<HWInstructionEvent>( |
| HWInstructionEvent(HWInstructionEvent::Ready, IR)); |
| } |
| |
| void ExecuteStage::notifyResourceAvailable(const ResourceRef &RR) { |
| LLVM_DEBUG(dbgs() << "[E] Resource Available: [" << RR.first << '.' |
| << RR.second << "]\n"); |
| for (HWEventListener *Listener : getListeners()) |
| Listener->onResourceAvailable(RR); |
| } |
| |
| void ExecuteStage::notifyInstructionIssued( |
| const InstRef &IR, ArrayRef<std::pair<ResourceRef, double>> Used) { |
| LLVM_DEBUG({ |
| dbgs() << "[E] Instruction Issued: #" << IR << '\n'; |
| for (const std::pair<ResourceRef, unsigned> &Resource : Used) { |
| dbgs() << "[E] Resource Used: [" << Resource.first.first << '.' |
| << Resource.first.second << "], "; |
| dbgs() << "cycles: " << Resource.second << '\n'; |
| } |
| }); |
| notifyEvent<HWInstructionEvent>(HWInstructionIssuedEvent(IR, Used)); |
| } |
| |
| void ExecuteStage::notifyReservedBuffers(ArrayRef<uint64_t> Buffers) { |
| if (Buffers.empty()) |
| return; |
| |
| SmallVector<unsigned, 4> BufferIDs(Buffers.begin(), Buffers.end()); |
| std::transform(Buffers.begin(), Buffers.end(), BufferIDs.begin(), |
| [&](uint64_t Op) { return HWS.getResourceID(Op); }); |
| for (HWEventListener *Listener : getListeners()) |
| Listener->onReservedBuffers(BufferIDs); |
| } |
| |
| void ExecuteStage::notifyReleasedBuffers(ArrayRef<uint64_t> Buffers) { |
| if (Buffers.empty()) |
| return; |
| |
| SmallVector<unsigned, 4> BufferIDs(Buffers.begin(), Buffers.end()); |
| std::transform(Buffers.begin(), Buffers.end(), BufferIDs.begin(), |
| [&](uint64_t Op) { return HWS.getResourceID(Op); }); |
| for (HWEventListener *Listener : getListeners()) |
| Listener->onReleasedBuffers(BufferIDs); |
| } |
| |
| } // namespace mca |