//===---------------------- 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
|