blob: c44018dc30f46a551a315ba06435807175e2fe9a [file] [log] [blame]
//===--------------------- DispatchStage.cpp --------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \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 "llvm/MCA/Stages/DispatchStage.h"
#include "llvm/MCA/HWEventListener.h"
#include "llvm/MCA/HardwareUnits/Scheduler.h"
#include "llvm/Support/Debug.h"
#define DEBUG_TYPE "llvm-mca"
namespace llvm {
namespace mca {
DispatchStage::DispatchStage(const MCSubtargetInfo &Subtarget,
const MCRegisterInfo &MRI,
unsigned MaxDispatchWidth, RetireControlUnit &R,
RegisterFile &F)
: DispatchWidth(MaxDispatchWidth), AvailableEntries(MaxDispatchWidth),
CarryOver(0U), CarriedOver(), STI(Subtarget), RCU(R), PRF(F) {
if (!DispatchWidth)
DispatchWidth = Subtarget.getSchedModel().IssueWidth;
}
void DispatchStage::notifyInstructionDispatched(const InstRef &IR,
ArrayRef<unsigned> UsedRegs,
unsigned UOps) const {
LLVM_DEBUG(dbgs() << "[E] Instruction Dispatched: #" << IR << '\n');
notifyEvent<HWInstructionEvent>(
HWInstructionDispatchedEvent(IR, UsedRegs, UOps));
}
bool DispatchStage::checkPRF(const InstRef &IR) const {
SmallVector<MCPhysReg, 4> RegDefs;
for (const 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 {
const unsigned NumMicroOps = IR.getInstruction()->getNumMicroOps();
if (RCU.isAvailable(NumMicroOps))
return true;
notifyEvent<HWStallEvent>(
HWStallEvent(HWStallEvent::RetireControlUnitStall, IR));
return false;
}
bool DispatchStage::canDispatch(const InstRef &IR) const {
bool CanDispatch = checkRCU(IR);
CanDispatch &= checkPRF(IR);
CanDispatch &= checkNextStage(IR);
return CanDispatch;
}
Error DispatchStage::dispatch(InstRef IR) {
assert(!CarryOver && "Cannot dispatch another instruction!");
Instruction &IS = *IR.getInstruction();
const InstrDesc &Desc = IS.getDesc();
const unsigned NumMicroOps = IS.getNumMicroOps();
if (NumMicroOps > DispatchWidth) {
assert(AvailableEntries == DispatchWidth);
AvailableEntries = 0;
CarryOver = NumMicroOps - DispatchWidth;
CarriedOver = IR;
} else {
assert(AvailableEntries >= NumMicroOps);
AvailableEntries -= NumMicroOps;
}
// Check if this instructions ends the dispatch group.
if (Desc.EndGroup)
AvailableEntries = 0;
// Check if this is an optimizable reg-reg move.
if (IS.isOptimizableMove()) {
assert(IS.getDefs().size() == 1 && "Expected a single input!");
assert(IS.getUses().size() == 1 && "Expected a single output!");
if (PRF.tryEliminateMove(IS.getDefs()[0], IS.getUses()[0]))
IS.setEliminated();
}
// 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.
//
// We also don't update data dependencies for instructions that have been
// eliminated at register renaming stage.
if (!IS.isEliminated()) {
for (ReadState &RS : IS.getUses())
PRF.addRegisterRead(RS, STI);
}
// By default, a dependency-breaking zero-idiom is expected to be optimized
// at register renaming stage. That means, no physical register is allocated
// to the instruction.
SmallVector<unsigned, 4> RegisterFiles(PRF.getNumRegisterFiles());
for (WriteState &WS : IS.getDefs())
PRF.addRegisterWrite(WriteRef(IR.getSourceIndex(), &WS), RegisterFiles);
// Reserve entries in the reorder buffer.
unsigned RCUTokenID = RCU.dispatch(IR);
// Notify the instruction that it has been dispatched.
IS.dispatch(RCUTokenID);
// Notify listeners of the "instruction dispatched" event,
// and move IR to the next stage.
notifyInstructionDispatched(IR, RegisterFiles,
std::min(DispatchWidth, NumMicroOps));
return moveToTheNextStage(IR);
}
Error DispatchStage::cycleStart() {
// The retire stage is responsible for calling method `cycleStart`
// on the PRF.
if (!CarryOver) {
AvailableEntries = DispatchWidth;
return ErrorSuccess();
}
AvailableEntries = CarryOver >= DispatchWidth ? 0 : DispatchWidth - CarryOver;
unsigned DispatchedOpcodes = DispatchWidth - AvailableEntries;
CarryOver -= DispatchedOpcodes;
assert(CarriedOver && "Invalid dispatched instruction");
SmallVector<unsigned, 8> RegisterFiles(PRF.getNumRegisterFiles(), 0U);
notifyInstructionDispatched(CarriedOver, RegisterFiles, DispatchedOpcodes);
if (!CarryOver)
CarriedOver = InstRef();
return ErrorSuccess();
}
bool DispatchStage::isAvailable(const InstRef &IR) const {
const Instruction &Inst = *IR.getInstruction();
unsigned NumMicroOps = Inst.getNumMicroOps();
const InstrDesc &Desc = Inst.getDesc();
unsigned Required = std::min(NumMicroOps, DispatchWidth);
if (Required > AvailableEntries)
return false;
if (Desc.BeginGroup && AvailableEntries != DispatchWidth)
return false;
// The dispatch logic doesn't internally buffer instructions. It only accepts
// instructions that can be successfully moved to the next stage during this
// same cycle.
return canDispatch(IR);
}
Error DispatchStage::execute(InstRef &IR) {
assert(canDispatch(IR) && "Cannot dispatch another instruction!");
return dispatch(IR);
}
#ifndef NDEBUG
void DispatchStage::dump() const {
PRF.dump();
RCU.dump();
}
#endif
} // namespace mca
} // namespace llvm