blob: 437f864b072c6375d676acb2c6614fd446222703 [file] [log] [blame]
//===---------------------- 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