Viewing file: Instruction.h (26.58 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
//===--------------------- Instruction.h ------------------------*- 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 defines abstractions used by the Pipeline to model register reads, /// register writes and instructions. /// //===----------------------------------------------------------------------===//
#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" // definition of MCPhysReg. #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;
/// A representation of an mca::Instruction operand /// for use in mca::CustomBehaviour. class MCAOperand { // This class is mostly copied from MCOperand within // MCInst.h except that we don't keep track of // expressions or sub-instructions. enum MCAOperandType : unsigned char { kInvalid, ///< Uninitialized, Relocatable immediate, or Sub-instruction. kRegister, ///< Register operand. kImmediate, ///< Immediate operand. kSFPImmediate, ///< Single-floating-point immediate operand. kDFPImmediate, ///< Double-Floating-point immediate operand. }; MCAOperandType Kind;
union { unsigned RegVal; int64_t ImmVal; uint32_t SFPImmVal; uint64_t FPImmVal; };
// We only store specific operands for specific instructions // so an instruction's operand 3 may be stored within the list // of MCAOperand as element 0. This Index attribute keeps track // of the original index (3 for this example). 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; }
/// Returns the register number. 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; } };
/// A register write descriptor. struct WriteDescriptor { // Operand index. The index is negative for implicit writes only. // For implicit writes, the actual operand index is computed performing // a bitwise not of the OpIndex. int OpIndex; // Write latency. Number of cycles before write-back stage. unsigned Latency; // This field is set to a value different than zero only if this // is an implicit definition. MCPhysReg RegisterID; // Instruction itineraries would set this field to the SchedClass ID. // Otherwise, it defaults to the WriteResourceID from the MCWriteLatencyEntry // element associated to this write. // When computing read latencies, this value is matched against the // "ReadAdvance" information. The hardware backend may implement // dedicated forwarding paths to quickly propagate write results to dependent // instructions waiting in the reservation station (effectively bypassing the // write-back stage). unsigned SClassOrWriteResourceID; // True only if this is a write obtained from an optional definition. // Optional definitions are allowed to reference regID zero (i.e. "no // register"). bool IsOptionalDef;
bool isImplicitWrite() const { return OpIndex < 0; }; };
/// A register read descriptor. struct ReadDescriptor { // A MCOperand index. This is used by the Dispatch logic to identify register // reads. Implicit reads have negative indices. The actual operand index of an // implicit read is the bitwise not of field OpIndex. int OpIndex; // The actual "UseIdx". This is used to query the ReadAdvance table. Explicit // uses always come first in the sequence of uses. unsigned UseIndex; // This field is only set if this is an implicit read. MCPhysReg RegisterID; // Scheduling Class Index. It is used to query the scheduling model for the // MCSchedClassDesc object. unsigned SchedClassID;
bool isImplicitRead() const { return OpIndex < 0; }; };
class ReadState;
/// A critical data dependency descriptor. /// /// Field RegID is set to the invalid register for memory dependencies. struct CriticalDependency { unsigned IID; MCPhysReg RegID; unsigned Cycles; };
/// Tracks uses of a register definition (e.g. register write). /// /// Each implicit/explicit register write is associated with an instance of /// this class. A WriteState object tracks the dependent users of a /// register write. It also tracks how many cycles are left before the write /// back stage. class WriteState { const WriteDescriptor *WD; // On instruction issue, this field is set equal to the write latency. // Before instruction issue, this field defaults to -512, a special // value that represents an "unknown" number of cycles. int CyclesLeft;
// Actual register defined by this write. This field is only used // to speedup queries on the register file. // For implicit writes, this field always matches the value of // field RegisterID from WD. MCPhysReg RegisterID;
// Physical register file that serves register RegisterID. unsigned PRFID;
// True if this write implicitly clears the upper portion of RegisterID's // super-registers. bool ClearsSuperRegs;
// True if this write is from a dependency breaking zero-idiom instruction. bool WritesZero;
// True if this write has been eliminated at register renaming stage. // Example: a register move doesn't consume scheduler/pipleline resources if // it is eliminated at register renaming stage. It still consumes // decode bandwidth, and ROB entries. bool IsEliminated;
// This field is set if this is a partial register write, and it has a false // dependency on any previous write of the same register (or a portion of it). // DependentWrite must be able to complete before this write completes, so // that we don't break the WAW, and the two writes can be merged together. const WriteState *DependentWrite;
// A partial write that is in a false dependency with this write. WriteState *PartialWrite; unsigned DependentWriteCyclesLeft;
// Critical register dependency for this write. CriticalDependency CRD;
// A list of dependent reads. Users is a set of dependent // reads. A dependent read is added to the set only if CyclesLeft // is "unknown". As soon as CyclesLeft is 'known', each user in the set // gets notified with the actual CyclesLeft.
// The 'second' element of a pair is a "ReadAdvance" number of cycles. 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; }
// This method adds Use to the set of data dependent reads. IID is the // instruction identifier associated with this write. ReadAdvance is the // number of cycles to subtract from the latency of this data dependency. // Use is in a RAW dependency with this write. void addUser(unsigned IID, ReadState *Use, int ReadAdvance);
// Use is a younger register write that is in a false dependency with this // write. IID is the instruction identifier associated with this write. 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; }
// On every cycle, update CyclesLeft and notify dependent users. void cycleEvent(); void onInstructionIssued(unsigned IID);
#ifndef NDEBUG void dump() const; #endif };
/// Tracks register operand latency in cycles. /// /// A read may be dependent on more than one write. This occurs when some /// writes only partially update the register associated to this read. class ReadState { const ReadDescriptor *RD; // Physical register identified associated to this read. MCPhysReg RegisterID; // Physical register file that serves register RegisterID. unsigned PRFID; // Number of writes that contribute to the definition of RegisterID. // In the absence of partial register updates, the number of DependentWrites // cannot be more than one. unsigned DependentWrites; // Number of cycles left before RegisterID can be read. This value depends on // the latency of all the dependent writes. It defaults to UNKNOWN_CYCLES. // It gets set to the value of field TotalCycles only when the 'CyclesLeft' of // every dependent write is known. int CyclesLeft; // This field is updated on every writeStartEvent(). When the number of // dependent writes (i.e. field DependentWrite) is zero, this value is // propagated to field CyclesLeft. unsigned TotalCycles; // Longest register dependency. CriticalDependency CRD; // This field is set to true only if there are no dependent writes, and // there are no `CyclesLeft' to wait. bool IsReady; // True if this is a read from a known zero register. bool IsZero; // True if this register read is from a dependency-breaking instruction. 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; } };
/// A sequence of cycles. /// /// This class can be used as a building block to construct ranges of cycles. class CycleSegment { unsigned Begin; // Inclusive. unsigned End; // Exclusive. bool Reserved; // Resources associated to this segment must be 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; } };
/// Helper used by class InstrDesc to describe how hardware resources /// are used. /// /// This class describes how many resource units of a specific resource kind /// (and how many cycles) are "used" by an instruction. 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(); } };
/// An instruction descriptor struct InstrDesc { SmallVector<WriteDescriptor, 2> Writes; // Implicit writes are at the end. SmallVector<ReadDescriptor, 4> Reads; // Implicit reads are at the end.
// For every resource used by an instruction of this kind, this vector // reports the number of "consumed cycles". SmallVector<std::pair<uint64_t, ResourceUsage>, 4> Resources;
// A bitmask of used hardware buffers. uint64_t UsedBuffers;
// A bitmask of used processor resource units. uint64_t UsedProcResUnits;
// A bitmask of used processor resource groups. uint64_t UsedProcResGroups;
unsigned MaxLatency; // Number of MicroOps for this instruction. unsigned NumMicroOps; // SchedClassID used to construct this InstrDesc. // This information is currently used by views to do fast queries on the // subtarget when computing the reciprocal throughput. unsigned SchedClassID;
// True if all buffered resources are in-order, and there is at least one // buffer which is a dispatch hazard (BufferSize = 0). unsigned MustIssueImmediately : 1;
// True if the corresponding mca::Instruction can be recycled. Currently only // instructions that are neither variadic nor have any variant can be // recycled. unsigned IsRecyclable : 1;
// True if some of the consumed group resources are partially overlapping. unsigned HasPartiallyOverlappingGroups : 1;
// A zero latency instruction doesn't consume any scheduler resources. bool isZeroLatency() const { return !MaxLatency && Resources.empty(); }
InstrDesc() = default; InstrDesc(const InstrDesc &Other) = delete; InstrDesc &operator=(const InstrDesc &Other) = delete; };
/// Base class for instructions consumed by the simulation pipeline. /// /// This class tracks data dependencies as well as generic properties /// of the instruction. class InstructionBase { const InstrDesc &Desc;
// This field is set for instructions that are candidates for move // elimination. For more information about move elimination, see the // definition of RegisterMappingTracker in RegisterFile.h bool IsOptimizableMove;
// Output dependencies. // One entry per each implicit and explicit register definition. SmallVector<WriteState, 2> Defs;
// Input dependencies. // One entry per each implicit and explicit register use. SmallVector<ReadState, 4> Uses;
// List of operands which can be used by mca::CustomBehaviour std::vector<MCAOperand> Operands;
// Instruction opcode which can be used by mca::CustomBehaviour unsigned Opcode;
// Flags used by the LSUnit. bool IsALoadBarrier : 1; bool IsAStoreBarrier : 1; // Flags copied from the InstrDesc and potentially modified by // CustomBehaviour or (more likely) InstrPostProcess. 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; }
/// Return the MCAOperand which corresponds to index Idx within the original /// MCInst. const MCAOperand *getOperand(const unsigned Idx) const { auto It = llvm::find_if(Operands, [&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; }
// Returns true if this instruction is a candidate for move elimination. bool isOptimizableMove() const { return IsOptimizableMove; } void setOptimizableMove() { IsOptimizableMove = true; } void clearOptimizableMove() { IsOptimizableMove = false; } bool isMemOp() const { return MayLoad || MayStore; }
// Getters and setters for general instruction flags. 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; } };
/// An instruction propagated through the simulated instruction pipeline. /// /// This class is used to monitor changes to the internal state of instructions /// that are sent to the various components of the simulated hardware pipeline. class Instruction : public InstructionBase { enum InstrStage { IS_INVALID, // Instruction in an invalid state. IS_DISPATCHED, // Instruction dispatched but operands are not ready. IS_PENDING, // Instruction is not ready, but operand latency is known. IS_READY, // Instruction dispatched and operands ready. IS_EXECUTING, // Instruction issued. IS_EXECUTED, // Instruction executed. Values are written back. IS_RETIRED // Instruction retired. };
// The current instruction stage. enum InstrStage Stage;
// This value defaults to the instruction latency. This instruction is // considered executed when field CyclesLeft goes to zero. int CyclesLeft;
// Retire Unit token ID for this instruction. unsigned RCUTokenID;
// LS token ID for this instruction. // This field is set to the invalid null token if this is not a memory // operation. unsigned LSUTokenID;
// A resource mask which identifies buffered resources consumed by this // instruction at dispatch stage. In the absence of macro-fusion, this value // should always match the value of field `UsedBuffers` from the instruction // descriptor (see field InstrBase::Desc). uint64_t UsedBuffers;
// Critical register dependency. CriticalDependency CriticalRegDep;
// Critical memory dependency. CriticalDependency CriticalMemDep;
// A bitmask of busy processor resource units. // This field is set to zero only if execution is not delayed during this // cycle because of unavailable pipeline resources. uint64_t CriticalResourceMask;
// True if this instruction has been optimized at register renaming stage. 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; }
// Transition to the dispatch stage, and assign a RCUToken to this // instruction. The RCUToken is used to track the completion of every // register write performed by this instruction. void dispatch(unsigned RCUTokenID);
// Instruction issued. Transition to the IS_EXECUTING state, and update // all the register definitions. void execute(unsigned IID);
// Force a transition from the IS_DISPATCHED state to the IS_READY or // IS_PENDING state. State transitions normally occur either at the beginning // of a new cycle (see method cycleEvent()), or as a result of another issue // event. This method is called every time the instruction might have changed // in state. It internally delegates to method updateDispatched() and // updateWaiting(). 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; }
// Forces a transition from state IS_DISPATCHED to state IS_EXECUTED. 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(); };
/// An InstRef contains both a SourceMgr index and Instruction pair. The index /// is used as a unique identifier for the instruction. MCA will make use of /// this index as a key throughout MCA. 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; }
/// Returns true if this references a valid instruction. explicit operator bool() const { return Data.second != nullptr; }
/// Invalidate this reference. 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
} // namespace mca } // namespace llvm
#endif // LLVM_MCA_INSTRUCTION_H
|