notifyCycleBegin(Cycle);
while (SM.hasNext()) {
- InstRef IR = SM.peekNext();
- std::unique_ptr<Instruction> NewIS = IB.createInstruction(*IR.second);
+ SourceRef SR = SM.peekNext();
+ std::unique_ptr<Instruction> NewIS = IB.createInstruction(*SR.second);
const InstrDesc &Desc = NewIS->getDesc();
- if (!DU->isAvailable(Desc.NumMicroOps) ||
- !DU->canDispatch(IR.first, *NewIS))
- break;
-
Instruction *IS = NewIS.get();
- Instructions[IR.first] = std::move(NewIS);
- DU->dispatch(IR.first, IS, STI);
+ InstRef IR(SR.first, IS);
+ if (!DU->isAvailable(Desc.NumMicroOps) || !DU->canDispatch(IR))
+ break;
+ Instructions[SR.first] = std::move(NewIS);
+ DU->dispatch(IR, STI);
SM.updateNext();
}
runCycle(Cycles++);
}
- const Instruction &getInstruction(unsigned Index) const {
- const auto It = Instructions.find(Index);
- assert(It != Instructions.end() && "no running instructions with index");
- assert(It->second);
- return *It->second;
+ void eraseInstruction(const InstRef &IR) {
+ Instructions.erase(IR.getSourceIndex());
}
- void eraseInstruction(unsigned Index) { Instructions.erase(Index); }
void addEventListener(HWEventListener *Listener);
void notifyCycleBegin(unsigned Cycle);
}
#endif
-void DispatchUnit::notifyInstructionDispatched(unsigned Index,
+void DispatchUnit::notifyInstructionDispatched(const InstRef &IR,
ArrayRef<unsigned> UsedRegs) {
- DEBUG(dbgs() << "[E] Instruction Dispatched: " << Index << '\n');
- Owner->notifyInstructionEvent(HWInstructionDispatchedEvent(Index, UsedRegs));
+ DEBUG(dbgs() << "[E] Instruction Dispatched: " << IR << '\n');
+ Owner->notifyInstructionEvent(HWInstructionDispatchedEvent(IR, UsedRegs));
}
-void DispatchUnit::notifyInstructionRetired(unsigned Index) {
- DEBUG(dbgs() << "[E] Instruction Retired: " << Index << '\n');
- const Instruction &IS = Owner->getInstruction(Index);
+void DispatchUnit::notifyInstructionRetired(const InstRef &IR) {
+ DEBUG(dbgs() << "[E] Instruction Retired: " << IR << '\n');
SmallVector<unsigned, 4> FreedRegs(RAT->getNumRegisterFiles());
- for (const std::unique_ptr<WriteState> &WS : IS.getDefs())
+ for (const std::unique_ptr<WriteState> &WS : IR.getInstruction()->getDefs())
RAT->invalidateRegisterMapping(*WS.get(), FreedRegs);
-
- Owner->notifyInstructionEvent(HWInstructionRetiredEvent(Index, FreedRegs));
- Owner->eraseInstruction(Index);
+ Owner->notifyInstructionEvent(HWInstructionRetiredEvent(IR, FreedRegs));
+ Owner->eraseInstruction(IR);
}
-bool DispatchUnit::checkRAT(unsigned Index, const Instruction &Instr) {
+bool DispatchUnit::checkRAT(const InstRef &IR) {
SmallVector<unsigned, 4> RegDefs;
- for (const std::unique_ptr<WriteState> &RegDef : Instr.getDefs())
+ for (const std::unique_ptr<WriteState> &RegDef :
+ IR.getInstruction()->getDefs())
RegDefs.emplace_back(RegDef->getRegisterID());
unsigned RegisterMask = RAT->isAvailable(RegDefs);
// A mask with all zeroes means: register files are available.
if (RegisterMask) {
- Owner->notifyStallEvent(
- HWStallEvent(HWStallEvent::RegisterFileStall, Index));
+ Owner->notifyStallEvent(HWStallEvent(HWStallEvent::RegisterFileStall, IR));
return false;
}
return true;
}
-bool DispatchUnit::checkRCU(unsigned Index, const InstrDesc &Desc) {
- unsigned NumMicroOps = Desc.NumMicroOps;
+bool DispatchUnit::checkRCU(const InstRef &IR) {
+ const unsigned NumMicroOps = IR.getInstruction()->getDesc().NumMicroOps;
if (RCU->isAvailable(NumMicroOps))
return true;
Owner->notifyStallEvent(
- HWStallEvent(HWStallEvent::RetireControlUnitStall, Index));
+ HWStallEvent(HWStallEvent::RetireControlUnitStall, IR));
return false;
}
-bool DispatchUnit::checkScheduler(unsigned Index, const InstrDesc &Desc) {
- return SC->canBeDispatched(Index, Desc);
+bool DispatchUnit::checkScheduler(const InstRef &IR) {
+ return SC->canBeDispatched(IR);
}
void DispatchUnit::updateRAWDependencies(ReadState &RS,
DependentWrites.clear();
}
-void DispatchUnit::dispatch(unsigned IID, Instruction *NewInst,
- const MCSubtargetInfo &STI) {
+void DispatchUnit::dispatch(InstRef IR, const MCSubtargetInfo &STI) {
assert(!CarryOver && "Cannot dispatch another instruction!");
- unsigned NumMicroOps = NewInst->getDesc().NumMicroOps;
+ Instruction &IS = *IR.getInstruction();
+ const InstrDesc &Desc = IS.getDesc();
+ const unsigned NumMicroOps = Desc.NumMicroOps;
if (NumMicroOps > DispatchWidth) {
assert(AvailableEntries == DispatchWidth);
AvailableEntries = 0;
// instruction. The assumption is that a zero-latency instruction doesn't
// require to be issued to the scheduler for execution. More importantly, it
// doesn't have to wait on the register input operands.
- const InstrDesc &Desc = NewInst->getDesc();
if (Desc.MaxLatency || !Desc.Resources.empty())
- for (std::unique_ptr<ReadState> &RS : NewInst->getUses())
+ for (std::unique_ptr<ReadState> &RS : IS.getUses())
updateRAWDependencies(*RS, STI);
// Allocate new mappings.
SmallVector<unsigned, 4> RegisterFiles(RAT->getNumRegisterFiles());
- for (std::unique_ptr<WriteState> &WS : NewInst->getDefs())
+ for (std::unique_ptr<WriteState> &WS : IS.getDefs())
RAT->addRegisterMapping(*WS, RegisterFiles);
// Reserve slots in the RCU, and notify the instruction that it has been
// dispatched to the schedulers for execution.
- NewInst->dispatch(RCU->reserveSlot(IID, NumMicroOps));
+ IS.dispatch(RCU->reserveSlot(IR, NumMicroOps));
// Notify listeners of the "instruction dispatched" event.
- notifyInstructionDispatched(IID, RegisterFiles);
+ notifyInstructionDispatched(IR, RegisterFiles);
// Now move the instruction into the scheduler's queue.
// The scheduler is responsible for checking if this is a zero-latency
// instruction that doesn't consume pipeline/scheduler resources.
- SC->scheduleInstruction(IID, *NewInst);
+ SC->scheduleInstruction(IR);
}
#ifndef NDEBUG
std::unique_ptr<RetireControlUnit> RCU;
Backend *Owner;
- bool checkRAT(unsigned Index, const Instruction &Inst);
- bool checkRCU(unsigned Index, const InstrDesc &Desc);
- bool checkScheduler(unsigned Index, const InstrDesc &Desc);
+ bool checkRAT(const InstRef &IR);
+ bool checkRCU(const InstRef &IR);
+ bool checkScheduler(const InstRef &IR);
void updateRAWDependencies(ReadState &RS, const llvm::MCSubtargetInfo &STI);
- void notifyInstructionDispatched(unsigned IID,
+ void notifyInstructionDispatched(const InstRef &IR,
llvm::ArrayRef<unsigned> UsedPhysRegs);
public:
bool isRCUEmpty() const { return RCU->isEmpty(); }
- bool canDispatch(unsigned Index, const Instruction &Inst) {
- const InstrDesc &Desc = Inst.getDesc();
- assert(isAvailable(Desc.NumMicroOps));
- return checkRCU(Index, Desc) && checkRAT(Index, Inst) &&
- checkScheduler(Index, Desc);
+ bool canDispatch(const InstRef &IR) {
+ assert(isAvailable(IR.getInstruction()->getDesc().NumMicroOps));
+ return checkRCU(IR) && checkRAT(IR) && checkScheduler(IR);
}
- void dispatch(unsigned IID, Instruction *I, const llvm::MCSubtargetInfo &STI);
+ void dispatch(InstRef IR, const llvm::MCSubtargetInfo &STI);
void collectWrites(llvm::SmallVectorImpl<WriteState *> &Vec,
unsigned RegID) const {
CarryOver = CarryOver >= DispatchWidth ? CarryOver - DispatchWidth : 0U;
}
- void notifyInstructionRetired(unsigned Index);
+ void notifyInstructionRetired(const InstRef &IR);
- void notifyDispatchStall(unsigned Index, unsigned EventType);
+ void notifyDispatchStall(const InstRef &IR, unsigned EventType);
void onInstructionExecuted(unsigned TokenID) {
RCU->onInstructionExecuted(TokenID);
-
//===----------------------- HWEventListener.h ------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
#ifndef LLVM_TOOLS_LLVM_MCA_HWEVENTLISTENER_H
#define LLVM_TOOLS_LLVM_MCA_HWEVENTLISTENER_H
+#include "Instruction.h"
#include "llvm/ADT/ArrayRef.h"
#include <utility>
LastGenericEventType,
};
- HWInstructionEvent(unsigned type, unsigned index)
- : Type(type), Index(index) {}
+ HWInstructionEvent(unsigned type, const InstRef &Inst)
+ : Type(type), IR(Inst) {}
// The event type. The exact meaning depends on the subtarget.
const unsigned Type;
- // The index of the instruction in the source manager.
- const unsigned Index;
+
+ // The instruction this event was generated for.
+ const InstRef &IR;
};
class HWInstructionIssuedEvent : public HWInstructionEvent {
public:
using ResourceRef = std::pair<uint64_t, uint64_t>;
- HWInstructionIssuedEvent(unsigned Index,
+ HWInstructionIssuedEvent(const InstRef &IR,
llvm::ArrayRef<std::pair<ResourceRef, double>> UR)
- : HWInstructionEvent(HWInstructionEvent::Issued, Index),
- UsedResources(UR) {}
+ : HWInstructionEvent(HWInstructionEvent::Issued, IR), UsedResources(UR) {}
llvm::ArrayRef<std::pair<ResourceRef, double>> UsedResources;
};
class HWInstructionDispatchedEvent : public HWInstructionEvent {
public:
- HWInstructionDispatchedEvent(unsigned Index, llvm::ArrayRef<unsigned> Regs)
- : HWInstructionEvent(HWInstructionEvent::Dispatched, Index),
+ HWInstructionDispatchedEvent(const InstRef &IR, llvm::ArrayRef<unsigned> Regs)
+ : HWInstructionEvent(HWInstructionEvent::Dispatched, IR),
UsedPhysRegs(Regs) {}
// Number of physical register allocated for this instruction. There is one
// entry per register file.
class HWInstructionRetiredEvent : public HWInstructionEvent {
public:
- HWInstructionRetiredEvent(unsigned Index, llvm::ArrayRef<unsigned> Regs)
- : HWInstructionEvent(HWInstructionEvent::Retired, Index),
+ HWInstructionRetiredEvent(const InstRef &IR, llvm::ArrayRef<unsigned> Regs)
+ : HWInstructionEvent(HWInstructionEvent::Retired, IR),
FreedPhysRegs(Regs) {}
// Number of register writes that have been architecturally committed. There
// is one entry per register file.
LastGenericEvent
};
- HWStallEvent(unsigned type, unsigned index) : Type(type), Index(index) {}
+ HWStallEvent(unsigned type, const InstRef &Inst) : Type(type), IR(Inst) {}
// The exact meaning of the stall event type depends on the subtarget.
const unsigned Type;
- // The index of the instruction in the source manager.
- const unsigned Index;
+
+ // The instruction this event was generated for.
+ const InstRef &IR;
};
class HWEventListener {
#define LLVM_TOOLS_LLVM_MCA_INSTRUCTION_H
#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <set>
#include <vector>
constexpr int UNKNOWN_CYCLES = -512;
+class Instruction;
+
+/// 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 : public std::pair<unsigned, Instruction *> {
+public:
+ InstRef() : std::pair<unsigned, Instruction *>(0, nullptr) {}
+ InstRef(unsigned Index, Instruction *I)
+ : std::pair<unsigned, Instruction *>(Index, I) {}
+
+ unsigned getSourceIndex() const { return first; }
+ Instruction *getInstruction() { return second; }
+ const Instruction *getInstruction() const { return second; }
+
+ /// Returns true if this InstRef has been populated.
+ bool isValid() const { return second != nullptr; }
+
+#ifndef NDEBUG
+ void print(llvm::raw_ostream &OS) const { OS << getSourceIndex(); }
+#endif
+};
+
+#ifndef NDEBUG
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const InstRef &IR) {
+ IR.print(OS);
+ return OS;
+}
+#endif
+
/// A register write descriptor.
struct WriteDescriptor {
// Operand index. -1 if this is an implicit write.
// Create an instruction descriptor for every instruction in the sequence.
while (S.hasNext()) {
UsedResources.clear();
- InstRef IR = S.peekNext();
- std::unique_ptr<Instruction> Inst = IB.createInstruction(*IR.second);
+ SourceRef SR = S.peekNext();
+ std::unique_ptr<Instruction> Inst = IB.createInstruction(*SR.second);
const InstrDesc &Desc = Inst->getDesc();
// Now identify the resources consumed by this instruction.
for (const std::pair<uint64_t, ResourceUsage> Resource : Desc.Resources) {
}
// Now send a fake instruction issued event to all the views.
- HWInstructionIssuedEvent Event(IR.first, UsedResources);
+ InstRef IR(SR.first, Inst.get());
+ HWInstructionIssuedEvent Event(IR, UsedResources);
for (std::unique_ptr<View> &Listener : Views)
Listener->onInstructionEvent(Event);
S.updateNext();
StoreQueue.insert(Index);
}
-bool LSUnit::reserve(unsigned Index, const InstrDesc &Desc) {
+bool LSUnit::reserve(const InstRef &IR) {
+ const InstrDesc Desc = IR.getInstruction()->getDesc();
unsigned MayLoad = Desc.MayLoad;
unsigned MayStore = Desc.MayStore;
unsigned IsMemBarrier = Desc.HasSideEffects;
if (!MayLoad && !MayStore)
return false;
+ const unsigned Index = IR.getSourceIndex();
if (MayLoad) {
if (IsMemBarrier)
LoadBarriers.insert(Index);
return true;
}
-bool LSUnit::isReady(unsigned Index) const {
+bool LSUnit::isReady(const InstRef &IR) const {
+ const unsigned Index = IR.getSourceIndex();
bool IsALoad = LoadQueue.count(Index) != 0;
bool IsAStore = StoreQueue.count(Index) != 0;
assert((IsALoad || IsAStore) && "Instruction is not in queue!");
return !IsAStore;
}
-void LSUnit::onInstructionExecuted(unsigned Index) {
+void LSUnit::onInstructionExecuted(const InstRef &IR) {
+ const unsigned Index = IR.getSourceIndex();
std::set<unsigned>::iterator it = LoadQueue.find(Index);
if (it != LoadQueue.end()) {
DEBUG(dbgs() << "[LSUnit]: Instruction idx=" << Index
namespace mca {
+class InstRef;
struct InstrDesc;
/// A Load/Store Unit implementing a load and store queues.
bool isLQFull() const { return LQ_Size != 0 && LoadQueue.size() == LQ_Size; }
// Returns true if this instruction has been successfully enqueued.
- bool reserve(unsigned Index, const InstrDesc &Desc);
+ bool reserve(const InstRef &IR);
// The rules are:
// 1. A store may not pass a previous store.
// 4. A store may not pass a previous load (regardless of flag 'NoAlias').
// 5. A load has to wait until an older load barrier is fully executed.
// 6. A store has to wait until an older store barrier is fully executed.
- bool isReady(unsigned Index) const;
- void onInstructionExecuted(unsigned Index);
+ bool isReady(const InstRef &IR) const;
+ void onInstructionExecuted(const InstRef &IR);
};
} // namespace mca
if (Event.Type != HWInstructionEvent::Issued)
return;
const auto &IssueEvent = static_cast<const HWInstructionIssuedEvent &>(Event);
- unsigned SourceIdx = Event.Index % Source.size();
+ const unsigned SourceIdx = Event.IR.getSourceIndex() % Source.size();
for (const std::pair<ResourceRef, double> &Use : IssueEvent.UsedResources) {
const ResourceRef &RR = Use.first;
assert(Resource2VecIndex.find(RR.first) != Resource2VecIndex.end());
}
// Reserves a number of slots, and returns a new token.
-unsigned RetireControlUnit::reserveSlot(unsigned Index, unsigned NumMicroOps) {
+unsigned RetireControlUnit::reserveSlot(const InstRef &IR, unsigned NumMicroOps) {
assert(isAvailable(NumMicroOps));
unsigned NormalizedQuantity =
std::min(NumMicroOps, static_cast<unsigned>(Queue.size()));
// resources, they still consume one slot in the retire queue.
NormalizedQuantity = std::max(NormalizedQuantity, 1U);
unsigned TokenID = NextAvailableSlotIdx;
- Queue[NextAvailableSlotIdx] = {Index, NormalizedQuantity, false};
+ Queue[NextAvailableSlotIdx] = {IR, NormalizedQuantity, false};
NextAvailableSlotIdx += NormalizedQuantity;
NextAvailableSlotIdx %= Queue.size();
AvailableSlots -= NormalizedQuantity;
break;
RUToken &Current = Queue[CurrentInstructionSlotIdx];
assert(Current.NumSlots && "Reserved zero slots?");
+ assert(Current.IR.isValid() && "Invalid RUToken in the RCU queue.");
if (!Current.Executed)
break;
- Owner->notifyInstructionRetired(Current.Index);
+ Owner->notifyInstructionRetired(Current.IR);
CurrentInstructionSlotIdx += Current.NumSlots;
CurrentInstructionSlotIdx %= Queue.size();
AvailableSlots += Current.NumSlots;
void RetireControlUnit::onInstructionExecuted(unsigned TokenID) {
assert(Queue.size() > TokenID);
- assert(Queue[TokenID].Executed == false && Queue[TokenID].Index != ~0U);
+ assert(Queue[TokenID].Executed == false && Queue[TokenID].IR.isValid());
Queue[TokenID].Executed = true;
}
// Note that the size of the reorder buffer is defined by the scheduling
// model via field 'NumMicroOpBufferSize'.
struct RUToken {
- unsigned Index; // Instruction index.
+ InstRef IR;
unsigned NumSlots; // Slots reserved to this instruction.
bool Executed; // True if the instruction is past the WB stage.
};
}
// Reserves a number of slots, and returns a new token.
- unsigned reserveSlot(unsigned Index, unsigned NumMicroOps);
+ unsigned reserveSlot(const InstRef &IS, unsigned NumMicroOps);
/// Retires instructions in program order.
void cycleEvent();
BusyResources.erase(RF);
}
-void Scheduler::scheduleInstruction(unsigned Idx, Instruction &MCIS) {
+void Scheduler::scheduleInstruction(InstRef &IR) {
+ const unsigned Idx = IR.getSourceIndex();
assert(WaitQueue.find(Idx) == WaitQueue.end());
assert(ReadyQueue.find(Idx) == ReadyQueue.end());
assert(IssuedQueue.find(Idx) == IssuedQueue.end());
// BufferSize=0 as reserved. Resources with a buffer size of zero will only
// be released after MCIS is issued, and all the ResourceCycles for those
// units have been consumed.
- const InstrDesc &Desc = MCIS.getDesc();
+ const InstrDesc &Desc = IR.getInstruction()->getDesc();
reserveBuffers(Desc.Buffers);
notifyReservedBuffers(Desc.Buffers);
// If necessary, reserve queue entries in the load-store unit (LSU).
- bool Reserved = LSU->reserve(Idx, Desc);
- if (!MCIS.isReady() || (Reserved && !LSU->isReady(Idx))) {
+ bool Reserved = LSU->reserve(IR);
+ if (!IR.getInstruction()->isReady() || (Reserved && !LSU->isReady(IR))) {
DEBUG(dbgs() << "[SCHEDULER] Adding " << Idx << " to the Wait Queue\n");
- WaitQueue[Idx] = &MCIS;
+ WaitQueue[Idx] = IR.getInstruction();
return;
}
- notifyInstructionReady(Idx);
+ notifyInstructionReady(IR);
// Don't add a zero-latency instruction to the Wait or Ready queue.
// A zero-latency instruction doesn't consume any scheduler resources. That is
// resources (i.e. BufferSize=1) is consumed.
if (!IsZeroLatency && !Resources->mustIssueImmediately(Desc)) {
- DEBUG(dbgs() << "[SCHEDULER] Adding " << Idx << " to the Ready Queue\n");
- ReadyQueue[Idx] = &MCIS;
+ DEBUG(dbgs() << "[SCHEDULER] Adding " << IR << " to the Ready Queue\n");
+ ReadyQueue[IR.getSourceIndex()] = IR.getInstruction();
return;
}
- DEBUG(dbgs() << "[SCHEDULER] Instruction " << Idx << " issued immediately\n");
+ DEBUG(dbgs() << "[SCHEDULER] Instruction " << IR << " issued immediately\n");
// Release buffered resources and issue MCIS to the underlying pipelines.
- issueInstruction(Idx, MCIS);
+ issueInstruction(IR);
}
void Scheduler::cycleEvent() {
for (const ResourceRef &RR : ResourcesFreed)
notifyResourceAvailable(RR);
- SmallVector<unsigned, 4> InstructionIDs;
+ SmallVector<InstRef, 4> InstructionIDs;
updateIssuedQueue(InstructionIDs);
- for (unsigned Idx : InstructionIDs)
- notifyInstructionExecuted(Idx);
+ for (const InstRef &IR : InstructionIDs)
+ notifyInstructionExecuted(IR);
InstructionIDs.clear();
updatePendingQueue(InstructionIDs);
- for (unsigned Idx : InstructionIDs)
- notifyInstructionReady(Idx);
+ for (const InstRef &IR : InstructionIDs)
+ notifyInstructionReady(IR);
InstructionIDs.clear();
- std::pair<unsigned, Instruction *> Inst = select();
- while (Inst.second) {
- issueInstruction(Inst.first, *Inst.second);
+ InstRef IR = select();
+ while (IR.isValid()) {
+ issueInstruction(IR);
// Instructions that have been issued during this cycle might have unblocked
// other dependent instructions. Dependent instructions may be issued during
// instructions to the ReadyQueue and tell to the caller that we need
// another round of 'issue()'.
promoteToReadyQueue(InstructionIDs);
- for (unsigned Idx : InstructionIDs)
- notifyInstructionReady(Idx);
+ for (const InstRef &I : InstructionIDs)
+ notifyInstructionReady(I);
InstructionIDs.clear();
// Select the next instruction to issue.
- Inst = select();
+ IR = select();
}
}
}
#endif
-bool Scheduler::canBeDispatched(unsigned Index, const InstrDesc &Desc) const {
+bool Scheduler::canBeDispatched(const InstRef &IR) const {
HWStallEvent::GenericEventType Type = HWStallEvent::Invalid;
+ const InstrDesc &Desc = IR.getInstruction()->getDesc();
if (Desc.MayLoad && LSU->isLQFull())
Type = HWStallEvent::LoadQueueFull;
}
}
- Owner->notifyStallEvent(HWStallEvent(Type, Index));
+ Owner->notifyStallEvent(HWStallEvent(Type, IR));
return false;
}
void Scheduler::issueInstructionImpl(
- unsigned InstrIndex, Instruction &IS,
+ InstRef &IR,
SmallVectorImpl<std::pair<ResourceRef, double>> &UsedResources) {
- const InstrDesc &D = IS.getDesc();
+ Instruction *IS = IR.getInstruction();
+ const InstrDesc &D = IS->getDesc();
// Issue the instruction and collect all the consumed resources
// into a vector. That vector is then used to notify the listener.
// Notify the instruction that it started executing.
// This updates the internal state of each write.
- IS.execute();
+ IS->execute();
- if (IS.isExecuting())
- IssuedQueue[InstrIndex] = &IS;
+ if (IS->isExecuting())
+ IssuedQueue[IR.getSourceIndex()] = IS;
}
-void Scheduler::issueInstruction(unsigned InstrIndex, Instruction &IS) {
+void Scheduler::issueInstruction(InstRef &IR) {
// Release buffered resources.
- const InstrDesc &Desc = IS.getDesc();
+ const InstrDesc &Desc = IR.getInstruction()->getDesc();
releaseBuffers(Desc.Buffers);
notifyReleasedBuffers(Desc.Buffers);
// Issue IS to the underlying pipelines and notify listeners.
SmallVector<std::pair<ResourceRef, double>, 4> Pipes;
- issueInstructionImpl(InstrIndex, IS, Pipes);
- notifyInstructionIssued(InstrIndex, Pipes);
- if (IS.isExecuted())
- notifyInstructionExecuted(InstrIndex);
+ issueInstructionImpl(IR, Pipes);
+ notifyInstructionIssued(IR, Pipes);
+ if (IR.getInstruction()->isExecuted())
+ notifyInstructionExecuted(IR);
}
-void Scheduler::promoteToReadyQueue(SmallVectorImpl<unsigned> &Ready) {
+void Scheduler::promoteToReadyQueue(SmallVectorImpl<InstRef> &Ready) {
// Scan the set of waiting instructions and promote them to the
// ready queue if operands are all ready.
for (auto I = WaitQueue.begin(), E = WaitQueue.end(); I != E;) {
- const QueueEntryTy &Entry = *I;
- unsigned IID = Entry.first;
- Instruction &Inst = *Entry.second;
+ const unsigned IID = I->first;
+ Instruction *IS = I->second;
// Check if this instruction is now ready. In case, force
// a transition in state using method 'update()'.
- Inst.update();
+ IS->update();
- const InstrDesc &Desc = Inst.getDesc();
+ const InstrDesc &Desc = IS->getDesc();
bool IsMemOp = Desc.MayLoad || Desc.MayStore;
- if (!Inst.isReady() || (IsMemOp && !LSU->isReady(IID))) {
+ if (!IS->isReady() || (IsMemOp && !LSU->isReady({IID, IS}))) {
++I;
continue;
}
- Ready.emplace_back(IID);
- ReadyQueue[IID] = &Inst;
+ Ready.emplace_back(IID, IS);
+ ReadyQueue[IID] = IS;
auto ToRemove = I;
++I;
WaitQueue.erase(ToRemove);
}
}
-std::pair<unsigned, Instruction *> Scheduler::select() {
+InstRef Scheduler::select() {
// Give priority to older instructions in the ReadyQueue. Since the ready
// queue is ordered by key, this will always prioritize older instructions.
const auto It = std::find_if(ReadyQueue.begin(), ReadyQueue.end(),
[&](const QueueEntryTy &Entry) {
- const Instruction &IS = *Entry.second;
- const InstrDesc &D = IS.getDesc();
+ const InstrDesc &D = Entry.second->getDesc();
return Resources->canBeIssued(D);
});
return {0, nullptr};
// We found an instruction to issue.
- const QueueEntryTy Entry = *It;
+ InstRef IR(It->first, It->second);
ReadyQueue.erase(It);
- return Entry;
+ return IR;
}
-void Scheduler::updatePendingQueue(SmallVectorImpl<unsigned> &Ready) {
+void Scheduler::updatePendingQueue(SmallVectorImpl<InstRef> &Ready) {
// Notify to instructions in the pending queue that a new cycle just
// started.
for (QueueEntryTy Entry : WaitQueue)
promoteToReadyQueue(Ready);
}
-void Scheduler::updateIssuedQueue(SmallVectorImpl<unsigned> &Executed) {
+void Scheduler::updateIssuedQueue(SmallVectorImpl<InstRef> &Executed) {
for (auto I = IssuedQueue.begin(), E = IssuedQueue.end(); I != E;) {
const QueueEntryTy Entry = *I;
- Entry.second->cycleEvent();
- if (Entry.second->isExecuted()) {
- Executed.push_back(Entry.first);
+ Instruction *IS = Entry.second;
+ IS->cycleEvent();
+ if (IS->isExecuted()) {
+ Executed.push_back({Entry.first, Entry.second});
auto ToRemove = I;
++I;
IssuedQueue.erase(ToRemove);
}
void Scheduler::notifyInstructionIssued(
- unsigned Index, ArrayRef<std::pair<ResourceRef, double>> Used) {
+ const InstRef &IR, ArrayRef<std::pair<ResourceRef, double>> Used) {
DEBUG({
- dbgs() << "[E] Instruction Issued: " << Index << '\n';
+ dbgs() << "[E] Instruction Issued: " << IR << '\n';
for (const std::pair<ResourceRef, unsigned> &Resource : Used) {
dbgs() << "[E] Resource Used: [" << Resource.first.first << '.'
<< Resource.first.second << "]\n";
dbgs() << " cycles: " << Resource.second << '\n';
}
});
- Owner->notifyInstructionEvent(HWInstructionIssuedEvent(Index, Used));
+ Owner->notifyInstructionEvent(HWInstructionIssuedEvent(IR, Used));
}
-void Scheduler::notifyInstructionExecuted(unsigned Index) {
- LSU->onInstructionExecuted(Index);
- DEBUG(dbgs() << "[E] Instruction Executed: " << Index << '\n');
+void Scheduler::notifyInstructionExecuted(const InstRef &IR) {
+ LSU->onInstructionExecuted(IR);
+ DEBUG(dbgs() << "[E] Instruction Executed: " << IR << '\n');
Owner->notifyInstructionEvent(
- HWInstructionEvent(HWInstructionEvent::Executed, Index));
-
- const Instruction &IS = Owner->getInstruction(Index);
- DU->onInstructionExecuted(IS.getRCUTokenID());
+ HWInstructionEvent(HWInstructionEvent::Executed, IR));
+ DU->onInstructionExecuted(IR.getInstruction()->getRCUTokenID());
}
-void Scheduler::notifyInstructionReady(unsigned Index) {
- DEBUG(dbgs() << "[E] Instruction Ready: " << Index << '\n');
+void Scheduler::notifyInstructionReady(const InstRef &IR) {
+ DEBUG(dbgs() << "[E] Instruction Ready: " << IR << '\n');
Owner->notifyInstructionEvent(
- HWInstructionEvent(HWInstructionEvent::Ready, Index));
+ HWInstructionEvent(HWInstructionEvent::Ready, IR));
}
void Scheduler::notifyResourceAvailable(const ResourceRef &RR) {
std::map<unsigned, Instruction *> IssuedQueue;
void
- notifyInstructionIssued(unsigned Index,
+ notifyInstructionIssued(const InstRef &IR,
llvm::ArrayRef<std::pair<ResourceRef, double>> Used);
- void notifyInstructionExecuted(unsigned Index);
- void notifyInstructionReady(unsigned Index);
+ void notifyInstructionExecuted(const InstRef &IR);
+ void notifyInstructionReady(const InstRef &IR);
void notifyResourceAvailable(const ResourceRef &RR);
// Notify the Backend that buffered resources were consumed.
/// Select the next instruction to issue from the ReadyQueue.
/// This method gives priority to older instructions.
- std::pair<unsigned, Instruction *> select();
+ InstRef select();
/// Move instructions from the WaitQueue to the ReadyQueue if input operands
/// are all available.
- void promoteToReadyQueue(llvm::SmallVectorImpl<unsigned> &Ready);
+ void promoteToReadyQueue(llvm::SmallVectorImpl<InstRef> &Ready);
/// Issue an instruction without updating the ready queue.
void issueInstructionImpl(
- unsigned Index, Instruction &IS,
+ InstRef &IR,
llvm::SmallVectorImpl<std::pair<ResourceRef, double>> &Pipes);
- void updatePendingQueue(llvm::SmallVectorImpl<unsigned> &Ready);
- void updateIssuedQueue(llvm::SmallVectorImpl<unsigned> &Executed);
+ void updatePendingQueue(llvm::SmallVectorImpl<InstRef> &Ready);
+ void updateIssuedQueue(llvm::SmallVectorImpl<InstRef> &Executed);
public:
Scheduler(Backend *B, const llvm::MCSchedModel &Model, unsigned LoadQueueSize,
void setDispatchUnit(DispatchUnit *DispUnit) { DU = DispUnit; }
- /// Check if instruction at index Idx can be dispatched.
+ /// Check if the instruction in 'IR' can be dispatched.
///
/// The DispatchUnit is responsible for querying the Scheduler before
/// dispatching new instructions. Queries are performed through method
/// `Scheduler::CanBeDispatched`. If scheduling resources are available,
/// and the instruction can be dispatched, then this method returns true.
/// Otherwise, a generic HWStallEvent is notified to the listeners.
- bool canBeDispatched(unsigned Idx, const InstrDesc &Desc) const;
- void scheduleInstruction(unsigned Idx, Instruction &MCIS);
+ bool canBeDispatched(const InstRef &IR) const;
+ void scheduleInstruction(InstRef &IR);
/// Issue an instruction.
- void issueInstruction(unsigned Index, Instruction &IS);
+ void issueInstruction(InstRef &IR);
/// Reserve one entry in each buffered resource.
void reserveBuffers(llvm::ArrayRef<uint64_t> Buffers) {
namespace mca {
-typedef std::pair<unsigned, const llvm::MCInst *> InstRef;
+typedef std::pair<unsigned, const llvm::MCInst *> SourceRef;
class SourceMgr {
using InstVec = std::vector<std::unique_ptr<const llvm::MCInst>>;
bool hasNext() { return Current < (Iterations * size()); }
void updateNext() { Current++; }
- const InstRef peekNext() const {
+ const SourceRef peekNext() const {
unsigned Index = getCurrentInstructionIndex();
- return InstRef(Current, Sequence[Index].get());
+ return SourceRef(Current, Sequence[Index].get());
}
unsigned getCurrentInstructionIndex() const {
}
void TimelineView::onInstructionEvent(const HWInstructionEvent &Event) {
- if (CurrentCycle >= MaxCycle || Event.Index >= Timeline.size())
+ const unsigned Index = Event.IR.getSourceIndex();
+ if (CurrentCycle >= MaxCycle || Index >= Timeline.size())
return;
switch (Event.Type) {
case HWInstructionEvent::Retired: {
- TimelineViewEntry &TVEntry = Timeline[Event.Index];
+ TimelineViewEntry &TVEntry = Timeline[Index];
TVEntry.CycleRetired = CurrentCycle;
// Update the WaitTime entry which corresponds to this Index.
- WaitTimeEntry &WTEntry = WaitTime[Event.Index % AsmSequence.size()];
+ WaitTimeEntry &WTEntry = WaitTime[Index % AsmSequence.size()];
WTEntry.Executions++;
WTEntry.CyclesSpentInSchedulerQueue +=
TVEntry.CycleIssued - TVEntry.CycleDispatched;
break;
}
case HWInstructionEvent::Ready:
- Timeline[Event.Index].CycleReady = CurrentCycle;
+ Timeline[Index].CycleReady = CurrentCycle;
break;
case HWInstructionEvent::Issued:
- Timeline[Event.Index].CycleIssued = CurrentCycle;
+ Timeline[Index].CycleIssued = CurrentCycle;
break;
case HWInstructionEvent::Executed:
- Timeline[Event.Index].CycleExecuted = CurrentCycle;
+ Timeline[Index].CycleExecuted = CurrentCycle;
break;
case HWInstructionEvent::Dispatched:
- Timeline[Event.Index].CycleDispatched = CurrentCycle;
+ Timeline[Index].CycleDispatched = CurrentCycle;
break;
default:
return;