#ifndef LLVM_MCA_INSTRUCTION_H
#define LLVM_MCA_INSTRUCTION_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/MC/MCRegister.h"
#include "llvm/Support/MathExtras.h"
#ifndef NDEBUG
#include "llvm/Support/raw_ostream.h"
#endif
#include <memory>
namespace llvm {
namespace mca {
constexpr int UNKNOWN_CYCLES = -512;
class MCAOperand {
enum MCAOperandType : unsigned char {
kInvalid, kRegister, kImmediate, kSFPImmediate, kDFPImmediate, };
MCAOperandType Kind;
union {
unsigned RegVal;
int64_t ImmVal;
uint32_t SFPImmVal;
uint64_t FPImmVal;
};
unsigned Index;
public:
MCAOperand() : Kind(kInvalid), FPImmVal(), Index() {}
bool isValid() const { return Kind != kInvalid; }
bool isReg() const { return Kind == kRegister; }
bool isImm() const { return Kind == kImmediate; }
bool isSFPImm() const { return Kind == kSFPImmediate; }
bool isDFPImm() const { return Kind == kDFPImmediate; }
unsigned getReg() const {
assert(isReg() && "This is not a register operand!");
return RegVal;
}
int64_t getImm() const {
assert(isImm() && "This is not an immediate");
return ImmVal;
}
uint32_t getSFPImm() const {
assert(isSFPImm() && "This is not an SFP immediate");
return SFPImmVal;
}
uint64_t getDFPImm() const {
assert(isDFPImm() && "This is not an FP immediate");
return FPImmVal;
}
void setIndex(const unsigned Idx) { Index = Idx; }
unsigned getIndex() const { return Index; }
static MCAOperand createReg(unsigned Reg) {
MCAOperand Op;
Op.Kind = kRegister;
Op.RegVal = Reg;
return Op;
}
static MCAOperand createImm(int64_t Val) {
MCAOperand Op;
Op.Kind = kImmediate;
Op.ImmVal = Val;
return Op;
}
static MCAOperand createSFPImm(uint32_t Val) {
MCAOperand Op;
Op.Kind = kSFPImmediate;
Op.SFPImmVal = Val;
return Op;
}
static MCAOperand createDFPImm(uint64_t Val) {
MCAOperand Op;
Op.Kind = kDFPImmediate;
Op.FPImmVal = Val;
return Op;
}
static MCAOperand createInvalid() {
MCAOperand Op;
Op.Kind = kInvalid;
Op.FPImmVal = 0;
return Op;
}
};
struct WriteDescriptor {
int OpIndex;
unsigned Latency;
MCPhysReg RegisterID;
unsigned SClassOrWriteResourceID;
bool IsOptionalDef;
bool isImplicitWrite() const { return OpIndex < 0; };
};
struct ReadDescriptor {
int OpIndex;
unsigned UseIndex;
MCPhysReg RegisterID;
unsigned SchedClassID;
bool isImplicitRead() const { return OpIndex < 0; };
};
class ReadState;
struct CriticalDependency {
unsigned IID;
MCPhysReg RegID;
unsigned Cycles;
};
class WriteState {
const WriteDescriptor *WD;
int CyclesLeft;
MCPhysReg RegisterID;
unsigned PRFID;
bool ClearsSuperRegs;
bool WritesZero;
bool IsEliminated;
const WriteState *DependentWrite;
WriteState *PartialWrite;
unsigned DependentWriteCyclesLeft;
CriticalDependency CRD;
SmallVector<std::pair<ReadState *, int>, 4> Users;
public:
WriteState(const WriteDescriptor &Desc, MCPhysReg RegID,
bool clearsSuperRegs = false, bool writesZero = false)
: WD(&Desc), CyclesLeft(UNKNOWN_CYCLES), RegisterID(RegID), PRFID(0),
ClearsSuperRegs(clearsSuperRegs), WritesZero(writesZero),
IsEliminated(false), DependentWrite(nullptr), PartialWrite(nullptr),
DependentWriteCyclesLeft(0), CRD() {}
WriteState(const WriteState &Other) = default;
WriteState &operator=(const WriteState &Other) = default;
int getCyclesLeft() const { return CyclesLeft; }
unsigned getWriteResourceID() const { return WD->SClassOrWriteResourceID; }
MCPhysReg getRegisterID() const { return RegisterID; }
void setRegisterID(const MCPhysReg RegID) { RegisterID = RegID; }
unsigned getRegisterFileID() const { return PRFID; }
unsigned getLatency() const { return WD->Latency; }
unsigned getDependentWriteCyclesLeft() const {
return DependentWriteCyclesLeft;
}
const WriteState *getDependentWrite() const { return DependentWrite; }
const CriticalDependency &getCriticalRegDep() const { return CRD; }
void addUser(unsigned IID, ReadState *Use, int ReadAdvance);
void addUser(unsigned IID, WriteState *Use);
unsigned getNumUsers() const {
unsigned NumUsers = Users.size();
if (PartialWrite)
++NumUsers;
return NumUsers;
}
bool clearsSuperRegisters() const { return ClearsSuperRegs; }
bool isWriteZero() const { return WritesZero; }
bool isEliminated() const { return IsEliminated; }
bool isReady() const {
if (DependentWrite)
return false;
unsigned CyclesLeft = getDependentWriteCyclesLeft();
return !CyclesLeft || CyclesLeft < getLatency();
}
bool isExecuted() const {
return CyclesLeft != UNKNOWN_CYCLES && CyclesLeft <= 0;
}
void setDependentWrite(const WriteState *Other) { DependentWrite = Other; }
void writeStartEvent(unsigned IID, MCPhysReg RegID, unsigned Cycles);
void setWriteZero() { WritesZero = true; }
void setEliminated() {
assert(Users.empty() && "Write is in an inconsistent state.");
CyclesLeft = 0;
IsEliminated = true;
}
void setPRF(unsigned PRF) { PRFID = PRF; }
void cycleEvent();
void onInstructionIssued(unsigned IID);
#ifndef NDEBUG
void dump() const;
#endif
};
class ReadState {
const ReadDescriptor *RD;
MCPhysReg RegisterID;
unsigned PRFID;
unsigned DependentWrites;
int CyclesLeft;
unsigned TotalCycles;
CriticalDependency CRD;
bool IsReady;
bool IsZero;
bool IndependentFromDef;
public:
ReadState(const ReadDescriptor &Desc, MCPhysReg RegID)
: RD(&Desc), RegisterID(RegID), PRFID(0), DependentWrites(0),
CyclesLeft(UNKNOWN_CYCLES), TotalCycles(0), CRD(), IsReady(true),
IsZero(false), IndependentFromDef(false) {}
const ReadDescriptor &getDescriptor() const { return *RD; }
unsigned getSchedClass() const { return RD->SchedClassID; }
MCPhysReg getRegisterID() const { return RegisterID; }
unsigned getRegisterFileID() const { return PRFID; }
const CriticalDependency &getCriticalRegDep() const { return CRD; }
bool isPending() const { return !IndependentFromDef && CyclesLeft > 0; }
bool isReady() const { return IsReady; }
bool isImplicitRead() const { return RD->isImplicitRead(); }
bool isIndependentFromDef() const { return IndependentFromDef; }
void setIndependentFromDef() { IndependentFromDef = true; }
void cycleEvent();
void writeStartEvent(unsigned IID, MCPhysReg RegID, unsigned Cycles);
void setDependentWrites(unsigned Writes) {
DependentWrites = Writes;
IsReady = !Writes;
}
bool isReadZero() const { return IsZero; }
void setReadZero() { IsZero = true; }
void setPRF(unsigned ID) { PRFID = ID; }
};
class CycleSegment {
unsigned Begin; unsigned End; bool Reserved;
public:
CycleSegment(unsigned StartCycle, unsigned EndCycle, bool IsReserved = false)
: Begin(StartCycle), End(EndCycle), Reserved(IsReserved) {}
bool contains(unsigned Cycle) const { return Cycle >= Begin && Cycle < End; }
bool startsAfter(const CycleSegment &CS) const { return End <= CS.Begin; }
bool endsBefore(const CycleSegment &CS) const { return Begin >= CS.End; }
bool overlaps(const CycleSegment &CS) const {
return !startsAfter(CS) && !endsBefore(CS);
}
bool isExecuting() const { return Begin == 0 && End != 0; }
bool isExecuted() const { return End == 0; }
bool operator<(const CycleSegment &Other) const {
return Begin < Other.Begin;
}
CycleSegment &operator--() {
if (Begin)
Begin--;
if (End)
End--;
return *this;
}
bool isValid() const { return Begin <= End; }
unsigned size() const { return End - Begin; };
void subtract(unsigned Cycles) {
assert(End >= Cycles);
End -= Cycles;
}
unsigned begin() const { return Begin; }
unsigned end() const { return End; }
void setEnd(unsigned NewEnd) { End = NewEnd; }
bool isReserved() const { return Reserved; }
void setReserved() { Reserved = true; }
};
struct ResourceUsage {
CycleSegment CS;
unsigned NumUnits;
ResourceUsage(CycleSegment Cycles, unsigned Units = 1)
: CS(Cycles), NumUnits(Units) {}
unsigned size() const { return CS.size(); }
bool isReserved() const { return CS.isReserved(); }
void setReserved() { CS.setReserved(); }
};
struct InstrDesc {
SmallVector<WriteDescriptor, 2> Writes; SmallVector<ReadDescriptor, 4> Reads;
SmallVector<std::pair<uint64_t, ResourceUsage>, 4> Resources;
uint64_t UsedBuffers;
uint64_t UsedProcResUnits;
uint64_t ImplicitlyUsedProcResUnits;
uint64_t UsedProcResGroups;
unsigned MaxLatency;
unsigned NumMicroOps;
unsigned SchedClassID;
unsigned MustIssueImmediately : 1;
unsigned IsRecyclable : 1;
bool isZeroLatency() const { return !MaxLatency && Resources.empty(); }
InstrDesc() = default;
InstrDesc(const InstrDesc &Other) = delete;
InstrDesc &operator=(const InstrDesc &Other) = delete;
};
class InstructionBase {
const InstrDesc &Desc;
bool IsOptimizableMove;
SmallVector<WriteState, 2> Defs;
SmallVector<ReadState, 4> Uses;
std::vector<MCAOperand> Operands;
unsigned Opcode;
bool IsALoadBarrier : 1;
bool IsAStoreBarrier : 1;
bool MayLoad : 1;
bool MayStore : 1;
bool HasSideEffects : 1;
bool BeginGroup : 1;
bool EndGroup : 1;
bool RetireOOO : 1;
public:
InstructionBase(const InstrDesc &D, const unsigned Opcode)
: Desc(D), IsOptimizableMove(false), Operands(0), Opcode(Opcode),
IsALoadBarrier(false), IsAStoreBarrier(false) {}
SmallVectorImpl<WriteState> &getDefs() { return Defs; }
ArrayRef<WriteState> getDefs() const { return Defs; }
SmallVectorImpl<ReadState> &getUses() { return Uses; }
ArrayRef<ReadState> getUses() const { return Uses; }
const InstrDesc &getDesc() const { return Desc; }
unsigned getLatency() const { return Desc.MaxLatency; }
unsigned getNumMicroOps() const { return Desc.NumMicroOps; }
unsigned getOpcode() const { return Opcode; }
bool isALoadBarrier() const { return IsALoadBarrier; }
bool isAStoreBarrier() const { return IsAStoreBarrier; }
void setLoadBarrier(bool IsBarrier) { IsALoadBarrier = IsBarrier; }
void setStoreBarrier(bool IsBarrier) { IsAStoreBarrier = IsBarrier; }
const MCAOperand *getOperand(const unsigned Idx) const {
auto It = std::find_if(
Operands.begin(), Operands.end(),
[&Idx](const MCAOperand &Op) { return Op.getIndex() == Idx; });
if (It == Operands.end())
return nullptr;
return &(*It);
}
unsigned getNumOperands() const { return Operands.size(); }
void addOperand(const MCAOperand Op) { Operands.push_back(Op); }
bool hasDependentUsers() const {
return any_of(Defs,
[](const WriteState &Def) { return Def.getNumUsers() > 0; });
}
unsigned getNumUsers() const {
unsigned NumUsers = 0;
for (const WriteState &Def : Defs)
NumUsers += Def.getNumUsers();
return NumUsers;
}
bool isOptimizableMove() const { return IsOptimizableMove; }
void setOptimizableMove() { IsOptimizableMove = true; }
void clearOptimizableMove() { IsOptimizableMove = false; }
bool isMemOp() const { return MayLoad || MayStore; }
void setMayLoad(bool newVal) { MayLoad = newVal; }
void setMayStore(bool newVal) { MayStore = newVal; }
void setHasSideEffects(bool newVal) { HasSideEffects = newVal; }
void setBeginGroup(bool newVal) { BeginGroup = newVal; }
void setEndGroup(bool newVal) { EndGroup = newVal; }
void setRetireOOO(bool newVal) { RetireOOO = newVal; }
bool getMayLoad() const { return MayLoad; }
bool getMayStore() const { return MayStore; }
bool getHasSideEffects() const { return HasSideEffects; }
bool getBeginGroup() const { return BeginGroup; }
bool getEndGroup() const { return EndGroup; }
bool getRetireOOO() const { return RetireOOO; }
};
class Instruction : public InstructionBase {
enum InstrStage {
IS_INVALID, IS_DISPATCHED, IS_PENDING, IS_READY, IS_EXECUTING, IS_EXECUTED, IS_RETIRED };
enum InstrStage Stage;
int CyclesLeft;
unsigned RCUTokenID;
unsigned LSUTokenID;
uint64_t UsedBuffers;
CriticalDependency CriticalRegDep;
CriticalDependency CriticalMemDep;
uint64_t CriticalResourceMask;
bool IsEliminated;
public:
Instruction(const InstrDesc &D, const unsigned Opcode)
: InstructionBase(D, Opcode), Stage(IS_INVALID),
CyclesLeft(UNKNOWN_CYCLES), RCUTokenID(0), LSUTokenID(0),
UsedBuffers(D.UsedBuffers), CriticalRegDep(), CriticalMemDep(),
CriticalResourceMask(0), IsEliminated(false) {}
void reset();
unsigned getRCUTokenID() const { return RCUTokenID; }
unsigned getLSUTokenID() const { return LSUTokenID; }
void setLSUTokenID(unsigned LSUTok) { LSUTokenID = LSUTok; }
uint64_t getUsedBuffers() const { return UsedBuffers; }
void setUsedBuffers(uint64_t Mask) { UsedBuffers = Mask; }
void clearUsedBuffers() { UsedBuffers = 0ULL; }
int getCyclesLeft() const { return CyclesLeft; }
void dispatch(unsigned RCUTokenID);
void execute(unsigned IID);
void update();
bool updateDispatched();
bool updatePending();
bool isInvalid() const { return Stage == IS_INVALID; }
bool isDispatched() const { return Stage == IS_DISPATCHED; }
bool isPending() const { return Stage == IS_PENDING; }
bool isReady() const { return Stage == IS_READY; }
bool isExecuting() const { return Stage == IS_EXECUTING; }
bool isExecuted() const { return Stage == IS_EXECUTED; }
bool isRetired() const { return Stage == IS_RETIRED; }
bool isEliminated() const { return IsEliminated; }
void forceExecuted();
void setEliminated() { IsEliminated = true; }
void retire() {
assert(isExecuted() && "Instruction is in an invalid state!");
Stage = IS_RETIRED;
}
const CriticalDependency &getCriticalRegDep() const { return CriticalRegDep; }
const CriticalDependency &getCriticalMemDep() const { return CriticalMemDep; }
const CriticalDependency &computeCriticalRegDep();
void setCriticalMemDep(const CriticalDependency &MemDep) {
CriticalMemDep = MemDep;
}
uint64_t getCriticalResourceMask() const { return CriticalResourceMask; }
void setCriticalResourceMask(uint64_t ResourceMask) {
CriticalResourceMask = ResourceMask;
}
void cycleEvent();
};
class InstRef {
std::pair<unsigned, Instruction *> Data;
public:
InstRef() : Data(std::make_pair(0, nullptr)) {}
InstRef(unsigned Index, Instruction *I) : Data(std::make_pair(Index, I)) {}
bool operator==(const InstRef &Other) const { return Data == Other.Data; }
bool operator!=(const InstRef &Other) const { return Data != Other.Data; }
bool operator<(const InstRef &Other) const {
return Data.first < Other.Data.first;
}
unsigned getSourceIndex() const { return Data.first; }
Instruction *getInstruction() { return Data.second; }
const Instruction *getInstruction() const { return Data.second; }
explicit operator bool() const { return Data.second != nullptr; }
void invalidate() { Data.second = nullptr; }
#ifndef NDEBUG
void print(raw_ostream &OS) const { OS << getSourceIndex(); }
#endif
};
#ifndef NDEBUG
inline raw_ostream &operator<<(raw_ostream &OS, const InstRef &IR) {
IR.print(OS);
return OS;
}
#endif
} }
#endif