#include "llvm/MCA/HardwareUnits/RegisterFile.h"
#include "llvm/MCA/Instruction.h"
#include "llvm/Support/Debug.h"
#define DEBUG_TYPE "llvm-mca"
namespace llvm {
namespace mca {
const unsigned WriteRef::INVALID_IID = std::numeric_limits<unsigned>::max();
WriteRef::WriteRef(unsigned SourceIndex, WriteState *WS)
: IID(SourceIndex), WriteBackCycle(), WriteResID(), RegisterID(),
Write(WS) {}
void WriteRef::commit() {
assert(Write && Write->isExecuted() && "Cannot commit before write back!");
RegisterID = Write->getRegisterID();
WriteResID = Write->getWriteResourceID();
Write = nullptr;
}
void WriteRef::notifyExecuted(unsigned Cycle) {
assert(Write && Write->isExecuted() && "Not executed!");
WriteBackCycle = Cycle;
}
bool WriteRef::hasKnownWriteBackCycle() const {
return isValid() && (!Write || Write->isExecuted());
}
bool WriteRef::isWriteZero() const {
assert(isValid() && "Invalid null WriteState found!");
return getWriteState()->isWriteZero();
}
unsigned WriteRef::getWriteResourceID() const {
if (Write)
return Write->getWriteResourceID();
return WriteResID;
}
MCPhysReg WriteRef::getRegisterID() const {
if (Write)
return Write->getRegisterID();
return RegisterID;
}
RegisterFile::RegisterFile(const MCSchedModel &SM, const MCRegisterInfo &mri,
unsigned NumRegs)
: MRI(mri),
RegisterMappings(mri.getNumRegs(), {WriteRef(), RegisterRenamingInfo()}),
ZeroRegisters(mri.getNumRegs(), false), CurrentCycle() {
initialize(SM, NumRegs);
}
void RegisterFile::initialize(const MCSchedModel &SM, unsigned NumRegs) {
RegisterFiles.emplace_back(NumRegs);
if (!SM.hasExtraProcessorInfo())
return;
const MCExtraProcessorInfo &Info = SM.getExtraProcessorInfo();
for (unsigned I = 1, E = Info.NumRegisterFiles; I < E; ++I) {
const MCRegisterFileDesc &RF = Info.RegisterFiles[I];
assert(RF.NumPhysRegs && "Invalid PRF with zero physical registers!");
unsigned Length = RF.NumRegisterCostEntries;
const MCRegisterCostEntry *FirstElt =
&Info.RegisterCostTable[RF.RegisterCostEntryIdx];
addRegisterFile(RF, ArrayRef<MCRegisterCostEntry>(FirstElt, Length));
}
}
void RegisterFile::cycleStart() {
for (RegisterMappingTracker &RMT : RegisterFiles)
RMT.NumMoveEliminated = 0;
}
void RegisterFile::onInstructionExecuted(Instruction *IS) {
assert(IS && IS->isExecuted() && "Unexpected internal state found!");
for (WriteState &WS : IS->getDefs()) {
if (WS.isEliminated())
return;
MCPhysReg RegID = WS.getRegisterID();
if (!RegID)
continue;
assert(WS.getCyclesLeft() != UNKNOWN_CYCLES &&
"The number of cycles should be known at this point!");
assert(WS.getCyclesLeft() <= 0 && "Invalid cycles left for this write!");
MCPhysReg RenameAs = RegisterMappings[RegID].second.RenameAs;
if (RenameAs && RenameAs != RegID)
RegID = RenameAs;
WriteRef &WR = RegisterMappings[RegID].first;
if (WR.getWriteState() == &WS)
WR.notifyExecuted(CurrentCycle);
for (MCSubRegIterator I(RegID, &MRI); I.isValid(); ++I) {
WriteRef &OtherWR = RegisterMappings[*I].first;
if (OtherWR.getWriteState() == &WS)
OtherWR.notifyExecuted(CurrentCycle);
}
if (!WS.clearsSuperRegisters())
continue;
for (MCSuperRegIterator I(RegID, &MRI); I.isValid(); ++I) {
WriteRef &OtherWR = RegisterMappings[*I].first;
if (OtherWR.getWriteState() == &WS)
OtherWR.notifyExecuted(CurrentCycle);
}
}
}
void RegisterFile::addRegisterFile(const MCRegisterFileDesc &RF,
ArrayRef<MCRegisterCostEntry> Entries) {
unsigned RegisterFileIndex = RegisterFiles.size();
RegisterFiles.emplace_back(RF.NumPhysRegs, RF.MaxMovesEliminatedPerCycle,
RF.AllowZeroMoveEliminationOnly);
if (Entries.empty())
return;
for (const MCRegisterCostEntry &RCE : Entries) {
const MCRegisterClass &RC = MRI.getRegClass(RCE.RegisterClassID);
for (const MCPhysReg Reg : RC) {
RegisterRenamingInfo &Entry = RegisterMappings[Reg].second;
IndexPlusCostPairTy &IPC = Entry.IndexPlusCost;
if (IPC.first && IPC.first != RegisterFileIndex) {
errs() << "warning: register " << MRI.getName(Reg)
<< " defined in multiple register files.";
}
IPC = std::make_pair(RegisterFileIndex, RCE.Cost);
Entry.RenameAs = Reg;
Entry.AllowMoveElimination = RCE.AllowMoveElimination;
for (MCSubRegIterator I(Reg, &MRI); I.isValid(); ++I) {
RegisterRenamingInfo &OtherEntry = RegisterMappings[*I].second;
if (!OtherEntry.IndexPlusCost.first &&
(!OtherEntry.RenameAs ||
MRI.isSuperRegister(*I, OtherEntry.RenameAs))) {
OtherEntry.IndexPlusCost = IPC;
OtherEntry.RenameAs = Reg;
}
}
}
}
}
void RegisterFile::allocatePhysRegs(const RegisterRenamingInfo &Entry,
MutableArrayRef<unsigned> UsedPhysRegs) {
unsigned RegisterFileIndex = Entry.IndexPlusCost.first;
unsigned Cost = Entry.IndexPlusCost.second;
if (RegisterFileIndex) {
RegisterMappingTracker &RMT = RegisterFiles[RegisterFileIndex];
RMT.NumUsedPhysRegs += Cost;
UsedPhysRegs[RegisterFileIndex] += Cost;
}
RegisterFiles[0].NumUsedPhysRegs += Cost;
UsedPhysRegs[0] += Cost;
}
void RegisterFile::freePhysRegs(const RegisterRenamingInfo &Entry,
MutableArrayRef<unsigned> FreedPhysRegs) {
unsigned RegisterFileIndex = Entry.IndexPlusCost.first;
unsigned Cost = Entry.IndexPlusCost.second;
if (RegisterFileIndex) {
RegisterMappingTracker &RMT = RegisterFiles[RegisterFileIndex];
RMT.NumUsedPhysRegs -= Cost;
FreedPhysRegs[RegisterFileIndex] += Cost;
}
RegisterFiles[0].NumUsedPhysRegs -= Cost;
FreedPhysRegs[0] += Cost;
}
void RegisterFile::addRegisterWrite(WriteRef Write,
MutableArrayRef<unsigned> UsedPhysRegs) {
WriteState &WS = *Write.getWriteState();
MCPhysReg RegID = WS.getRegisterID();
if (!RegID)
return;
LLVM_DEBUG({
dbgs() << "[PRF] addRegisterWrite [ " << Write.getSourceIndex() << ", "
<< MRI.getName(RegID) << "]\n";
});
bool IsWriteZero = WS.isWriteZero();
bool IsEliminated = WS.isEliminated();
bool ShouldAllocatePhysRegs = !IsWriteZero && !IsEliminated;
const RegisterRenamingInfo &RRI = RegisterMappings[RegID].second;
WS.setPRF(RRI.IndexPlusCost.first);
if (RRI.RenameAs && RRI.RenameAs != RegID) {
RegID = RRI.RenameAs;
WriteRef &OtherWrite = RegisterMappings[RegID].first;
if (!WS.clearsSuperRegisters()) {
ShouldAllocatePhysRegs = false;
WriteState *OtherWS = OtherWrite.getWriteState();
if (OtherWS && (OtherWrite.getSourceIndex() != Write.getSourceIndex())) {
assert(!IsEliminated && "Unexpected partial update!");
OtherWS->addUser(OtherWrite.getSourceIndex(), &WS);
}
}
}
MCPhysReg ZeroRegisterID =
WS.clearsSuperRegisters() ? RegID : WS.getRegisterID();
ZeroRegisters.setBitVal(ZeroRegisterID, IsWriteZero);
for (MCSubRegIterator I(ZeroRegisterID, &MRI); I.isValid(); ++I)
ZeroRegisters.setBitVal(*I, IsWriteZero);
if (!IsEliminated) {
const WriteRef &OtherWrite = RegisterMappings[RegID].first;
const WriteState *OtherWS = OtherWrite.getWriteState();
if (OtherWS && OtherWrite.getSourceIndex() == Write.getSourceIndex()) {
if (OtherWS->getLatency() > WS.getLatency()) {
if (ShouldAllocatePhysRegs)
allocatePhysRegs(RegisterMappings[RegID].second, UsedPhysRegs);
return;
}
}
RegisterMappings[RegID].first = Write;
RegisterMappings[RegID].second.AliasRegID = 0U;
for (MCSubRegIterator I(RegID, &MRI); I.isValid(); ++I) {
RegisterMappings[*I].first = Write;
RegisterMappings[*I].second.AliasRegID = 0U;
}
if (ShouldAllocatePhysRegs)
allocatePhysRegs(RegisterMappings[RegID].second, UsedPhysRegs);
}
if (!WS.clearsSuperRegisters())
return;
for (MCSuperRegIterator I(RegID, &MRI); I.isValid(); ++I) {
if (!IsEliminated) {
RegisterMappings[*I].first = Write;
RegisterMappings[*I].second.AliasRegID = 0U;
}
ZeroRegisters.setBitVal(*I, IsWriteZero);
}
}
void RegisterFile::removeRegisterWrite(
const WriteState &WS, MutableArrayRef<unsigned> FreedPhysRegs) {
if (WS.isEliminated())
return;
MCPhysReg RegID = WS.getRegisterID();
if (!RegID)
return;
assert(WS.getCyclesLeft() != UNKNOWN_CYCLES &&
"Invalidating a write of unknown cycles!");
assert(WS.getCyclesLeft() <= 0 && "Invalid cycles left for this write!");
bool ShouldFreePhysRegs = !WS.isWriteZero();
MCPhysReg RenameAs = RegisterMappings[RegID].second.RenameAs;
if (RenameAs && RenameAs != RegID) {
RegID = RenameAs;
if (!WS.clearsSuperRegisters()) {
ShouldFreePhysRegs = false;
}
}
if (ShouldFreePhysRegs)
freePhysRegs(RegisterMappings[RegID].second, FreedPhysRegs);
WriteRef &WR = RegisterMappings[RegID].first;
if (WR.getWriteState() == &WS)
WR.commit();
for (MCSubRegIterator I(RegID, &MRI); I.isValid(); ++I) {
WriteRef &OtherWR = RegisterMappings[*I].first;
if (OtherWR.getWriteState() == &WS)
OtherWR.commit();
}
if (!WS.clearsSuperRegisters())
return;
for (MCSuperRegIterator I(RegID, &MRI); I.isValid(); ++I) {
WriteRef &OtherWR = RegisterMappings[*I].first;
if (OtherWR.getWriteState() == &WS)
OtherWR.commit();
}
}
bool RegisterFile::canEliminateMove(const WriteState &WS, const ReadState &RS,
unsigned RegisterFileIndex) const {
const RegisterMapping &RMFrom = RegisterMappings[RS.getRegisterID()];
const RegisterMapping &RMTo = RegisterMappings[WS.getRegisterID()];
const RegisterMappingTracker &RMT = RegisterFiles[RegisterFileIndex];
const RegisterRenamingInfo &RRIFrom = RMFrom.second;
if (RRIFrom.IndexPlusCost.first != RegisterFileIndex)
return false;
const RegisterRenamingInfo &RRITo = RMTo.second;
if (RRITo.IndexPlusCost.first != RegisterFileIndex)
return false;
if (!RegisterMappings[RRITo.RenameAs].second.AllowMoveElimination)
return false;
if (RRITo.RenameAs && RRITo.RenameAs != WS.getRegisterID())
if (!WS.clearsSuperRegisters())
return false;
bool IsZeroMove = ZeroRegisters[RS.getRegisterID()];
return (!RMT.AllowZeroMoveEliminationOnly || IsZeroMove);
}
bool RegisterFile::tryEliminateMoveOrSwap(MutableArrayRef<WriteState> Writes,
MutableArrayRef<ReadState> Reads) {
if (Writes.size() != Reads.size())
return false;
if (Writes.empty() || Writes.size() > 2)
return false;
const RegisterRenamingInfo &RRInfo =
RegisterMappings[Writes[0].getRegisterID()].second;
unsigned RegisterFileIndex = RRInfo.IndexPlusCost.first;
RegisterMappingTracker &RMT = RegisterFiles[RegisterFileIndex];
if (RMT.MaxMoveEliminatedPerCycle &&
(RMT.NumMoveEliminated + Writes.size()) > RMT.MaxMoveEliminatedPerCycle)
return false;
for (size_t I = 0, E = Writes.size(); I < E; ++I) {
const ReadState &RS = Reads[I];
const WriteState &WS = Writes[E - (I + 1)];
if (!canEliminateMove(WS, RS, RegisterFileIndex))
return false;
}
for (size_t I = 0, E = Writes.size(); I < E; ++I) {
ReadState &RS = Reads[I];
WriteState &WS = Writes[E - (I + 1)];
const RegisterMapping &RMFrom = RegisterMappings[RS.getRegisterID()];
const RegisterMapping &RMTo = RegisterMappings[WS.getRegisterID()];
const RegisterRenamingInfo &RRIFrom = RMFrom.second;
const RegisterRenamingInfo &RRITo = RMTo.second;
MCPhysReg AliasedReg =
RRIFrom.RenameAs ? RRIFrom.RenameAs : RS.getRegisterID();
MCPhysReg AliasReg = RRITo.RenameAs ? RRITo.RenameAs : WS.getRegisterID();
const RegisterRenamingInfo &RMAlias = RegisterMappings[AliasedReg].second;
if (RMAlias.AliasRegID)
AliasedReg = RMAlias.AliasRegID;
RegisterMappings[AliasReg].second.AliasRegID = AliasedReg;
for (MCSubRegIterator I(AliasReg, &MRI); I.isValid(); ++I)
RegisterMappings[*I].second.AliasRegID = AliasedReg;
if (ZeroRegisters[RS.getRegisterID()]) {
WS.setWriteZero();
RS.setReadZero();
}
WS.setEliminated();
RMT.NumMoveEliminated++;
}
return true;
}
unsigned WriteRef::getWriteBackCycle() const {
assert(hasKnownWriteBackCycle() && "Instruction not executed!");
assert((!Write || Write->getCyclesLeft() <= 0) &&
"Inconsistent state found!");
return WriteBackCycle;
}
unsigned RegisterFile::getElapsedCyclesFromWriteBack(const WriteRef &WR) const {
assert(WR.hasKnownWriteBackCycle() && "Write hasn't been committed yet!");
return CurrentCycle - WR.getWriteBackCycle();
}
void RegisterFile::collectWrites(
const MCSubtargetInfo &STI, const ReadState &RS,
SmallVectorImpl<WriteRef> &Writes,
SmallVectorImpl<WriteRef> &CommittedWrites) const {
const ReadDescriptor &RD = RS.getDescriptor();
const MCSchedModel &SM = STI.getSchedModel();
const MCSchedClassDesc *SC = SM.getSchedClassDesc(RD.SchedClassID);
MCPhysReg RegID = RS.getRegisterID();
assert(RegID && RegID < RegisterMappings.size());
LLVM_DEBUG(dbgs() << "[PRF] collecting writes for register "
<< MRI.getName(RegID) << '\n');
const RegisterRenamingInfo &RRI = RegisterMappings[RegID].second;
if (RRI.AliasRegID)
RegID = RRI.AliasRegID;
const WriteRef &WR = RegisterMappings[RegID].first;
if (WR.getWriteState()) {
Writes.push_back(WR);
} else if (WR.hasKnownWriteBackCycle()) {
unsigned WriteResID = WR.getWriteResourceID();
int ReadAdvance = STI.getReadAdvanceCycles(SC, RD.UseIndex, WriteResID);
if (ReadAdvance < 0) {
unsigned Elapsed = getElapsedCyclesFromWriteBack(WR);
if (Elapsed < static_cast<unsigned>(-ReadAdvance))
CommittedWrites.push_back(WR);
}
}
for (MCSubRegIterator I(RegID, &MRI); I.isValid(); ++I) {
const WriteRef &WR = RegisterMappings[*I].first;
if (WR.getWriteState()) {
Writes.push_back(WR);
} else if (WR.hasKnownWriteBackCycle()) {
unsigned WriteResID = WR.getWriteResourceID();
int ReadAdvance = STI.getReadAdvanceCycles(SC, RD.UseIndex, WriteResID);
if (ReadAdvance < 0) {
unsigned Elapsed = getElapsedCyclesFromWriteBack(WR);
if (Elapsed < static_cast<unsigned>(-ReadAdvance))
CommittedWrites.push_back(WR);
}
}
}
if (Writes.size() > 1) {
sort(Writes, [](const WriteRef &Lhs, const WriteRef &Rhs) {
return Lhs.getWriteState() < Rhs.getWriteState();
});
auto It = std::unique(Writes.begin(), Writes.end());
Writes.resize(std::distance(Writes.begin(), It));
}
LLVM_DEBUG({
for (const WriteRef &WR : Writes) {
const WriteState &WS = *WR.getWriteState();
dbgs() << "[PRF] Found a dependent use of Register "
<< MRI.getName(WS.getRegisterID()) << " (defined by instruction #"
<< WR.getSourceIndex() << ")\n";
}
});
}
RegisterFile::RAWHazard
RegisterFile::checkRAWHazards(const MCSubtargetInfo &STI,
const ReadState &RS) const {
RAWHazard Hazard;
SmallVector<WriteRef, 4> Writes;
SmallVector<WriteRef, 4> CommittedWrites;
const MCSchedModel &SM = STI.getSchedModel();
const ReadDescriptor &RD = RS.getDescriptor();
const MCSchedClassDesc *SC = SM.getSchedClassDesc(RD.SchedClassID);
collectWrites(STI, RS, Writes, CommittedWrites);
for (const WriteRef &WR : Writes) {
const WriteState *WS = WR.getWriteState();
unsigned WriteResID = WS->getWriteResourceID();
int ReadAdvance = STI.getReadAdvanceCycles(SC, RD.UseIndex, WriteResID);
if (WS->getCyclesLeft() == UNKNOWN_CYCLES) {
if (Hazard.isValid())
continue;
Hazard.RegisterID = WR.getRegisterID();
Hazard.CyclesLeft = UNKNOWN_CYCLES;
continue;
}
int CyclesLeft = WS->getCyclesLeft() - ReadAdvance;
if (CyclesLeft > 0) {
if (Hazard.CyclesLeft < CyclesLeft) {
Hazard.RegisterID = WR.getRegisterID();
Hazard.CyclesLeft = CyclesLeft;
}
}
}
Writes.clear();
for (const WriteRef &WR : CommittedWrites) {
unsigned WriteResID = WR.getWriteResourceID();
int NegReadAdvance = -STI.getReadAdvanceCycles(SC, RD.UseIndex, WriteResID);
int Elapsed = static_cast<int>(getElapsedCyclesFromWriteBack(WR));
int CyclesLeft = NegReadAdvance - Elapsed;
assert(CyclesLeft > 0 && "Write should not be in the CommottedWrites set!");
if (Hazard.CyclesLeft < CyclesLeft) {
Hazard.RegisterID = WR.getRegisterID();
Hazard.CyclesLeft = CyclesLeft;
}
}
return Hazard;
}
void RegisterFile::addRegisterRead(ReadState &RS,
const MCSubtargetInfo &STI) const {
MCPhysReg RegID = RS.getRegisterID();
const RegisterRenamingInfo &RRI = RegisterMappings[RegID].second;
RS.setPRF(RRI.IndexPlusCost.first);
if (RS.isIndependentFromDef())
return;
if (ZeroRegisters[RS.getRegisterID()])
RS.setReadZero();
SmallVector<WriteRef, 4> DependentWrites;
SmallVector<WriteRef, 4> CompletedWrites;
collectWrites(STI, RS, DependentWrites, CompletedWrites);
RS.setDependentWrites(DependentWrites.size() + CompletedWrites.size());
const ReadDescriptor &RD = RS.getDescriptor();
const MCSchedModel &SM = STI.getSchedModel();
const MCSchedClassDesc *SC = SM.getSchedClassDesc(RD.SchedClassID);
for (WriteRef &WR : DependentWrites) {
unsigned WriteResID = WR.getWriteResourceID();
WriteState &WS = *WR.getWriteState();
int ReadAdvance = STI.getReadAdvanceCycles(SC, RD.UseIndex, WriteResID);
WS.addUser(WR.getSourceIndex(), &RS, ReadAdvance);
}
for (WriteRef &WR : CompletedWrites) {
unsigned WriteResID = WR.getWriteResourceID();
assert(WR.hasKnownWriteBackCycle() && "Invalid write!");
assert(STI.getReadAdvanceCycles(SC, RD.UseIndex, WriteResID) < 0);
unsigned ReadAdvance = static_cast<unsigned>(
-STI.getReadAdvanceCycles(SC, RD.UseIndex, WriteResID));
unsigned Elapsed = getElapsedCyclesFromWriteBack(WR);
assert(Elapsed < ReadAdvance && "Should not have been added to the set!");
RS.writeStartEvent(WR.getSourceIndex(), WR.getRegisterID(),
ReadAdvance - Elapsed);
}
}
unsigned RegisterFile::isAvailable(ArrayRef<MCPhysReg> Regs) const {
SmallVector<unsigned, 4> NumPhysRegs(getNumRegisterFiles());
for (const MCPhysReg RegID : Regs) {
const RegisterRenamingInfo &RRI = RegisterMappings[RegID].second;
const IndexPlusCostPairTy &Entry = RRI.IndexPlusCost;
if (Entry.first)
NumPhysRegs[Entry.first] += Entry.second;
NumPhysRegs[0] += Entry.second;
}
unsigned Response = 0;
for (unsigned I = 0, E = getNumRegisterFiles(); I < E; ++I) {
unsigned NumRegs = NumPhysRegs[I];
if (!NumRegs)
continue;
const RegisterMappingTracker &RMT = RegisterFiles[I];
if (!RMT.NumPhysRegs) {
continue;
}
if (RMT.NumPhysRegs < NumRegs) {
LLVM_DEBUG(
dbgs() << "[PRF] Not enough registers in the register file.\n");
NumRegs = RMT.NumPhysRegs;
}
if (RMT.NumPhysRegs < (RMT.NumUsedPhysRegs + NumRegs))
Response |= (1U << I);
}
return Response;
}
#ifndef NDEBUG
void WriteRef::dump() const {
dbgs() << "IID=" << getSourceIndex() << ' ';
if (isValid())
getWriteState()->dump();
else
dbgs() << "(null)";
}
void RegisterFile::dump() const {
for (unsigned I = 0, E = MRI.getNumRegs(); I < E; ++I) {
const RegisterMapping &RM = RegisterMappings[I];
const RegisterRenamingInfo &RRI = RM.second;
if (ZeroRegisters[I]) {
dbgs() << MRI.getName(I) << ", " << I
<< ", PRF=" << RRI.IndexPlusCost.first
<< ", Cost=" << RRI.IndexPlusCost.second
<< ", RenameAs=" << RRI.RenameAs << ", IsZero=" << ZeroRegisters[I]
<< ",";
RM.first.dump();
dbgs() << '\n';
}
}
for (unsigned I = 0, E = getNumRegisterFiles(); I < E; ++I) {
dbgs() << "Register File #" << I;
const RegisterMappingTracker &RMT = RegisterFiles[I];
dbgs() << "\n TotalMappings: " << RMT.NumPhysRegs
<< "\n NumUsedMappings: " << RMT.NumUsedPhysRegs << '\n';
}
}
#endif
} }