//===--------------------- DispatchStage.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 models the dispatch component of an instruction pipeline.
|
///
|
/// The DispatchStage is responsible for updating instruction dependencies
|
/// and communicating to the simulated instruction scheduler that an instruction
|
/// is ready to be scheduled for execution.
|
///
|
//===----------------------------------------------------------------------===//
|
|
#include "DispatchStage.h"
|
#include "HWEventListener.h"
|
#include "Scheduler.h"
|
#include "llvm/Support/Debug.h"
|
|
using namespace llvm;
|
|
#define DEBUG_TYPE "llvm-mca"
|
|
namespace mca {
|
|
void DispatchStage::notifyInstructionDispatched(const InstRef &IR,
|
ArrayRef<unsigned> UsedRegs) {
|
LLVM_DEBUG(dbgs() << "[E] Instruction Dispatched: #" << IR << '\n');
|
notifyEvent<HWInstructionEvent>(HWInstructionDispatchedEvent(IR, UsedRegs));
|
}
|
|
bool DispatchStage::checkPRF(const InstRef &IR) {
|
SmallVector<unsigned, 4> RegDefs;
|
for (const std::unique_ptr<WriteState> &RegDef :
|
IR.getInstruction()->getDefs())
|
RegDefs.emplace_back(RegDef->getRegisterID());
|
|
const unsigned RegisterMask = PRF.isAvailable(RegDefs);
|
// A mask with all zeroes means: register files are available.
|
if (RegisterMask) {
|
notifyEvent<HWStallEvent>(
|
HWStallEvent(HWStallEvent::RegisterFileStall, IR));
|
return false;
|
}
|
|
return true;
|
}
|
|
bool DispatchStage::checkRCU(const InstRef &IR) {
|
const unsigned NumMicroOps = IR.getInstruction()->getDesc().NumMicroOps;
|
if (RCU.isAvailable(NumMicroOps))
|
return true;
|
notifyEvent<HWStallEvent>(
|
HWStallEvent(HWStallEvent::RetireControlUnitStall, IR));
|
return false;
|
}
|
|
bool DispatchStage::checkScheduler(const InstRef &IR) {
|
HWStallEvent::GenericEventType Event;
|
const bool Ready = SC.canBeDispatched(IR, Event);
|
if (!Ready)
|
notifyEvent<HWStallEvent>(HWStallEvent(Event, IR));
|
return Ready;
|
}
|
|
void DispatchStage::updateRAWDependencies(ReadState &RS,
|
const MCSubtargetInfo &STI) {
|
SmallVector<WriteRef, 4> DependentWrites;
|
|
collectWrites(DependentWrites, RS.getRegisterID());
|
RS.setDependentWrites(DependentWrites.size());
|
// We know that this read depends on all the writes in DependentWrites.
|
// For each write, check if we have ReadAdvance information, and use it
|
// to figure out in how many cycles this read becomes available.
|
const ReadDescriptor &RD = RS.getDescriptor();
|
const MCSchedModel &SM = STI.getSchedModel();
|
const MCSchedClassDesc *SC = SM.getSchedClassDesc(RD.SchedClassID);
|
for (WriteRef &WR : DependentWrites) {
|
WriteState &WS = *WR.getWriteState();
|
unsigned WriteResID = WS.getWriteResourceID();
|
int ReadAdvance = STI.getReadAdvanceCycles(SC, RD.UseIndex, WriteResID);
|
WS.addUser(&RS, ReadAdvance);
|
}
|
}
|
|
void DispatchStage::dispatch(InstRef IR) {
|
assert(!CarryOver && "Cannot dispatch another instruction!");
|
Instruction &IS = *IR.getInstruction();
|
const InstrDesc &Desc = IS.getDesc();
|
const unsigned NumMicroOps = Desc.NumMicroOps;
|
if (NumMicroOps > DispatchWidth) {
|
assert(AvailableEntries == DispatchWidth);
|
AvailableEntries = 0;
|
CarryOver = NumMicroOps - DispatchWidth;
|
} else {
|
assert(AvailableEntries >= NumMicroOps);
|
AvailableEntries -= NumMicroOps;
|
}
|
|
// A dependency-breaking instruction doesn't have to wait on the register
|
// input operands, and it is often optimized at register renaming stage.
|
// Update RAW dependencies if this instruction is not a dependency-breaking
|
// instruction. A dependency-breaking instruction is a zero-latency
|
// instruction that doesn't consume hardware resources.
|
// An example of dependency-breaking instruction on X86 is a zero-idiom XOR.
|
bool IsDependencyBreaking = IS.isDependencyBreaking();
|
for (std::unique_ptr<ReadState> &RS : IS.getUses())
|
if (RS->isImplicitRead() || !IsDependencyBreaking)
|
updateRAWDependencies(*RS, STI);
|
|
// By default, a dependency-breaking zero-latency instruction is expected to
|
// be optimized at register renaming stage. That means, no physical register
|
// is allocated to the instruction.
|
bool ShouldAllocateRegisters =
|
!(Desc.isZeroLatency() && IsDependencyBreaking);
|
SmallVector<unsigned, 4> RegisterFiles(PRF.getNumRegisterFiles());
|
for (std::unique_ptr<WriteState> &WS : IS.getDefs()) {
|
PRF.addRegisterWrite(WriteRef(IR.first, WS.get()), RegisterFiles,
|
ShouldAllocateRegisters);
|
}
|
|
// Reserve slots in the RCU, and notify the instruction that it has been
|
// dispatched to the schedulers for execution.
|
IS.dispatch(RCU.reserveSlot(IR, NumMicroOps));
|
|
// Notify listeners of the "instruction dispatched" event.
|
notifyInstructionDispatched(IR, RegisterFiles);
|
}
|
|
void DispatchStage::cycleStart() {
|
AvailableEntries = CarryOver >= DispatchWidth ? 0 : DispatchWidth - CarryOver;
|
CarryOver = CarryOver >= DispatchWidth ? CarryOver - DispatchWidth : 0U;
|
}
|
|
bool DispatchStage::execute(InstRef &IR) {
|
const InstrDesc &Desc = IR.getInstruction()->getDesc();
|
if (!isAvailable(Desc.NumMicroOps) || !canDispatch(IR))
|
return false;
|
dispatch(IR);
|
return true;
|
}
|
|
#ifndef NDEBUG
|
void DispatchStage::dump() const {
|
PRF.dump();
|
RCU.dump();
|
}
|
#endif
|
} // namespace mca
|