1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-25 04:02:41 +01:00

[MCA] Introduce class LSUnitBase and let LSUnit derive from it.

Class LSUnitBase provides a abstract interface for all the concrete LS units in
llvm-mca.

Methods exposed by the public abstract LSUnitBase interface are:
 - Status isAvailable(const InstRef&);
 - void dispatch(const InstRef &);
 - const InstRef &isReady(const InstRef &);

LSUnitBase standardises the API, but not the data structures internally used by
LS units. This allows for more flexibility.
Previously, only method `isReady()` was declared virtual by class LSUnit.
Also, derived classes had to inherit all the internal data members of LSUnit.

No functional change intended.

llvm-svn: 361496
This commit is contained in:
Andrea Di Biagio 2019-05-23 13:42:47 +00:00
parent 1504131bb7
commit aba464655a
3 changed files with 195 additions and 125 deletions

View File

@ -18,13 +18,83 @@
#include "llvm/ADT/SmallSet.h"
#include "llvm/MC/MCSchedule.h"
#include "llvm/MCA/HardwareUnits/HardwareUnit.h"
#include "llvm/MCA/Instruction.h"
namespace llvm {
namespace mca {
class InstRef;
class Scheduler;
/// Abstract base interface for LS (load/store) units in llvm-mca.
class LSUnitBase : public HardwareUnit {
/// Load queue size.
///
/// A value of zero for this field means that the load queue is unbounded.
/// Processor models can declare the size of a load queue via tablegen (see
/// the definition of tablegen class LoadQueue in
/// llvm/Target/TargetSchedule.td).
unsigned LQSize;
/// Load queue size.
///
/// A value of zero for this field means that the store queue is unbounded.
/// Processor models can declare the size of a store queue via tablegen (see
/// the definition of tablegen class StoreQueue in
/// llvm/Target/TargetSchedule.td).
unsigned SQSize;
/// True if loads don't alias with stores.
///
/// By default, the LS unit assumes that loads and stores don't alias with
/// eachother. If this field is set to false, then loads are always assumed to
/// alias with stores.
const bool NoAlias;
public:
LSUnitBase(const MCSchedModel &SM, unsigned LoadQueueSize,
unsigned StoreQueueSize, bool AssumeNoAlias);
virtual ~LSUnitBase();
/// Returns the total number of entries in the load queue.
unsigned getLoadQueueSize() const { return LQSize; }
/// Returns the total number of entries in the store queue.
unsigned getStoreQueueSize() const { return SQSize; }
bool assumeNoAlias() const { return NoAlias; }
enum Status {
LSU_AVAILABLE = 0,
LSU_LQUEUE_FULL, // Load Queue unavailable
LSU_SQUEUE_FULL // Store Queue unavailable
};
/// This method checks the availability of the load/store buffers.
///
/// Returns LSU_AVAILABLE if there are enough load/store queue entries to
/// accomodate instruction IR. By default, LSU_AVAILABLE is returned if IR is
/// not a memory operation.
virtual Status isAvailable(const InstRef &IR) const = 0;
/// Allocates LS resources for instruction IR.
///
/// This method assumes that a previous call to `isAvailable(IR)` succeeded
/// with a LSUnitBase::Status value of LSU_AVAILABLE.
virtual void dispatch(const InstRef &IR) = 0;
/// Check if a peviously dispatched instruction IR is now ready for execution.
///
/// Instruction IR is assumed to be a memory operation. If IR is still waiting
/// on another memory instruction M, then M is returned to the caller. If IR
/// depends on more than one memory operations, then this method returns one
/// of them.
///
/// Derived classes can implement memory consistency rules for simulated
/// processor within this member function.
virtual const InstRef &isReady(const InstRef &IR) const = 0;
};
/// A Load/Store Unit implementing a load and store queues.
///
/// This class implements a load queue and a store queue to emulate the
@ -88,18 +158,7 @@ class Scheduler;
/// A load/store barrier is "executed" when it becomes the oldest entry in
/// the load/store queue(s). That also means, all the older loads/stores have
/// already been executed.
class LSUnit : public HardwareUnit {
// Load queue size.
// LQ_Size == 0 means that there are infinite slots in the load queue.
unsigned LQ_Size;
// Store queue size.
// SQ_Size == 0 means that there are infinite slots in the store queue.
unsigned SQ_Size;
// If true, loads will never alias with stores. This is the default.
bool NoAlias;
class LSUnit : public LSUnitBase {
// When a `MayLoad` instruction is dispatched to the schedulers for execution,
// the LSUnit reserves an entry in the `LoadQueue` for it.
//
@ -138,68 +197,75 @@ class LSUnit : public HardwareUnit {
// alternative approaches that let instructions specify the number of
// load/store queue entries which they consume at dispatch stage (See
// PR39830).
SmallSet<unsigned, 16> LoadQueue;
SmallSet<unsigned, 16> StoreQueue;
SmallSet<InstRef, 16> LoadQueue;
SmallSet<InstRef, 16> StoreQueue;
void assignLQSlot(unsigned Index);
void assignSQSlot(unsigned Index);
void assignLQSlot(const InstRef &IR);
void assignSQSlot(const InstRef &IR);
// An instruction that both 'mayStore' and 'HasUnmodeledSideEffects' is
// conservatively treated as a store barrier. It forces older store to be
// executed before newer stores are issued.
SmallSet<unsigned, 8> StoreBarriers;
SmallSet<InstRef, 8> StoreBarriers;
// An instruction that both 'MayLoad' and 'HasUnmodeledSideEffects' is
// conservatively treated as a load barrier. It forces older loads to execute
// before newer loads are issued.
SmallSet<unsigned, 8> LoadBarriers;
SmallSet<InstRef, 8> LoadBarriers;
bool isSQEmpty() const { return StoreQueue.empty(); }
bool isLQEmpty() const { return LoadQueue.empty(); }
bool isSQFull() const { return SQ_Size != 0 && StoreQueue.size() == SQ_Size; }
bool isLQFull() const { return LQ_Size != 0 && LoadQueue.size() == LQ_Size; }
bool isSQFull() const {
return getStoreQueueSize() != 0 && StoreQueue.size() == getStoreQueueSize();
}
bool isLQFull() const {
return getLoadQueueSize() != 0 && LoadQueue.size() == getLoadQueueSize();
}
public:
LSUnit(const MCSchedModel &SM, unsigned LQ = 0, unsigned SQ = 0,
bool AssumeNoAlias = false);
LSUnit(const MCSchedModel &SM)
: LSUnit(SM, /* LQSize */ 0, /* SQSize */ 0, /* NoAlias */ false) {}
LSUnit(const MCSchedModel &SM, unsigned LQ, unsigned SQ)
: LSUnit(SM, LQ, SQ, /* NoAlias */ false) {}
LSUnit(const MCSchedModel &SM, unsigned LQ, unsigned SQ, bool AssumeNoAlias)
: LSUnitBase(SM, LQ, SQ, AssumeNoAlias) {}
#ifndef NDEBUG
void dump() const;
#endif
enum Status { LSU_AVAILABLE = 0, LSU_LQUEUE_FULL, LSU_SQUEUE_FULL };
/// Returns LSU_AVAILABLE if there are enough load/store queue entries to
/// accomodate instruction IR.
Status isAvailable(const InstRef &IR) const override;
// Returns LSU_AVAILABLE if there are enough load/store queue entries to serve
// IR. It also returns LSU_AVAILABLE if IR is not a memory operation.
Status isAvailable(const InstRef &IR) const;
/// Allocates LS resources for instruction IR.
///
/// This method assumes that a previous call to `isAvailable(IR)` succeeded
/// returning LSU_AVAILABLE.
void dispatch(const InstRef &IR) override;
// Allocates load/store queue resources for IR.
//
// This method assumes that a previous call to `isAvailable(IR)` returned
// LSU_AVAILABLE, and that IR is a memory operation.
void dispatch(const InstRef &IR);
/// Check if a peviously dispatched instruction IR is now ready for execution.
///
/// Rules are:
/// By default, rules are:
/// 1. A store may not pass a previous store.
/// 2. A load may not pass a previous store unless flag 'NoAlias' is set.
/// 3. A load may pass a previous load.
/// 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.
const InstRef &isReady(const InstRef &IR) const override;
// By default, rules are:
// 1. A store may not pass a previous store.
// 2. A load may not pass a previous store unless flag 'NoAlias' is set.
// 3. A load may pass a previous load.
// 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.
//
// Returns an instruction identifier. If IR is ready, then this method returns
// `IR.getSourceIndex()`. Otherwise it returns the instruction ID of the
// dependent (i.e. conflicting) memory instruction.
virtual unsigned isReady(const InstRef &IR) const;
// Load and store instructions are tracked by their corresponding queues from
// dispatch until the "instruction executed" event.
// Only when a load instruction reaches the 'Executed' stage, its value
// becomes available to the users. At that point, the load no longer needs to
// be tracked by the load queue.
// FIXME: For simplicity, we optimistically assume a similar behavior for
// store instructions. In practice, store operations don't tend to leave the
// store queue until they reach the 'Retired' stage (See PR39830).
/// Instruction executed event handler.
///
/// Load and store instructions are tracked by their corresponding queues from
/// dispatch until "instruction executed" event.
/// When a load instruction Ld reaches the 'Executed' stage, its value
/// is propagated to all the dependent users, and the LS unit stops tracking
/// Ld.
/// FIXME: For simplicity, we optimistically assume a similar behavior for
/// store instructions. In practice, store operations don't tend to leave the
/// store queue until they reach the 'Retired' stage (See PR39830).
void onInstructionExecuted(const InstRef &IR);
};

View File

@ -21,48 +21,48 @@
namespace llvm {
namespace mca {
LSUnit::LSUnit(const MCSchedModel &SM, unsigned LQ, unsigned SQ,
bool AssumeNoAlias)
: LQ_Size(LQ), SQ_Size(SQ), NoAlias(AssumeNoAlias) {
LSUnitBase::LSUnitBase(const MCSchedModel &SM, unsigned LQ, unsigned SQ,
bool AssumeNoAlias)
: LQSize(LQ), SQSize(SQ), NoAlias(AssumeNoAlias) {
if (SM.hasExtraProcessorInfo()) {
const MCExtraProcessorInfo &EPI = SM.getExtraProcessorInfo();
if (!LQ_Size && EPI.LoadQueueID) {
if (!LQSize && EPI.LoadQueueID) {
const MCProcResourceDesc &LdQDesc = *SM.getProcResource(EPI.LoadQueueID);
LQ_Size = LdQDesc.BufferSize;
LQSize = LdQDesc.BufferSize;
}
if (!SQ_Size && EPI.StoreQueueID) {
if (!SQSize && EPI.StoreQueueID) {
const MCProcResourceDesc &StQDesc = *SM.getProcResource(EPI.StoreQueueID);
SQ_Size = StQDesc.BufferSize;
SQSize = StQDesc.BufferSize;
}
}
}
LSUnitBase::~LSUnitBase() {}
#ifndef NDEBUG
void LSUnit::dump() const {
dbgs() << "[LSUnit] LQ_Size = " << LQ_Size << '\n';
dbgs() << "[LSUnit] SQ_Size = " << SQ_Size << '\n';
dbgs() << "[LSUnit] LQ_Size = " << getLoadQueueSize() << '\n';
dbgs() << "[LSUnit] SQ_Size = " << getStoreQueueSize() << '\n';
dbgs() << "[LSUnit] NextLQSlotIdx = " << LoadQueue.size() << '\n';
dbgs() << "[LSUnit] NextSQSlotIdx = " << StoreQueue.size() << '\n';
}
#endif
void LSUnit::assignLQSlot(unsigned Index) {
assert(!isLQFull());
assert(LoadQueue.count(Index) == 0);
void LSUnit::assignLQSlot(const InstRef &IR) {
assert(!isLQFull() && "Load Queue is full!");
LLVM_DEBUG(dbgs() << "[LSUnit] - AssignLQSlot <Idx=" << Index
LLVM_DEBUG(dbgs() << "[LSUnit] - AssignLQSlot <Idx=" << IR.getSourceIndex()
<< ",slot=" << LoadQueue.size() << ">\n");
LoadQueue.insert(Index);
LoadQueue.insert(IR);
}
void LSUnit::assignSQSlot(unsigned Index) {
assert(!isSQFull());
assert(StoreQueue.count(Index) == 0);
void LSUnit::assignSQSlot(const InstRef &IR) {
assert(!isSQFull() && "Store Queue is full!");
LLVM_DEBUG(dbgs() << "[LSUnit] - AssignSQSlot <Idx=" << Index
LLVM_DEBUG(dbgs() << "[LSUnit] - AssignSQSlot <Idx=" << IR.getSourceIndex()
<< ",slot=" << StoreQueue.size() << ">\n");
StoreQueue.insert(Index);
StoreQueue.insert(IR);
}
void LSUnit::dispatch(const InstRef &IR) {
@ -70,17 +70,16 @@ void LSUnit::dispatch(const InstRef &IR) {
unsigned IsMemBarrier = Desc.HasSideEffects;
assert((Desc.MayLoad || Desc.MayStore) && "Not a memory operation!");
const unsigned Index = IR.getSourceIndex();
if (Desc.MayLoad) {
if (IsMemBarrier)
LoadBarriers.insert(Index);
assignLQSlot(Index);
LoadBarriers.insert(IR);
assignLQSlot(IR);
}
if (Desc.MayStore) {
if (IsMemBarrier)
StoreBarriers.insert(Index);
assignSQSlot(Index);
StoreBarriers.insert(IR);
assignSQSlot(IR);
}
}
@ -93,65 +92,67 @@ LSUnit::Status LSUnit::isAvailable(const InstRef &IR) const {
return LSUnit::LSU_AVAILABLE;
}
unsigned LSUnit::isReady(const InstRef &IR) const {
const InstRef &LSUnit::isReady(const InstRef &IR) const {
const InstrDesc &Desc = IR.getInstruction()->getDesc();
const unsigned Index = IR.getSourceIndex();
bool IsALoad = Desc.MayLoad;
bool IsAStore = Desc.MayStore;
assert((IsALoad || IsAStore) && "Not a memory operation!");
assert((!IsALoad || LoadQueue.count(Index) == 1) && "Load not in queue!");
assert((!IsAStore || StoreQueue.count(Index) == 1) && "Store not in queue!");
if (IsALoad && !LoadBarriers.empty()) {
unsigned LoadBarrierIndex = *LoadBarriers.begin();
const InstRef &LoadBarrier = *LoadBarriers.begin();
// A younger load cannot pass a older load barrier.
if (Index > LoadBarrierIndex)
return LoadBarrierIndex;
if (Index > LoadBarrier.getSourceIndex())
return LoadBarrier;
// A load barrier cannot pass a older load.
if (Index == LoadBarrierIndex && Index != *LoadQueue.begin())
return *LoadQueue.begin();
if (Index == LoadBarrier.getSourceIndex()) {
const InstRef &Load = *LoadQueue.begin();
if (Index != Load.getSourceIndex())
return Load;
}
}
if (IsAStore && !StoreBarriers.empty()) {
unsigned StoreBarrierIndex = *StoreBarriers.begin();
const InstRef &StoreBarrier = *StoreBarriers.begin();
// A younger store cannot pass a older store barrier.
if (Index > StoreBarrierIndex)
return StoreBarrierIndex;
if (Index > StoreBarrier.getSourceIndex())
return StoreBarrier;
// A store barrier cannot pass a older store.
if (Index == StoreBarrierIndex && Index != *StoreQueue.begin())
return *StoreQueue.begin();
if (Index == StoreBarrier.getSourceIndex()) {
const InstRef &Store = *StoreQueue.begin();
if (Index != Store.getSourceIndex())
return Store;
}
}
// A load may not pass a previous store unless flag 'NoAlias' is set.
// A load may pass a previous load.
if (NoAlias && IsALoad)
return Index;
if (assumeNoAlias() && IsALoad)
return IR;
if (StoreQueue.size()) {
// A load may not pass a previous store.
// A store may not pass a previous store.
if (Index > *StoreQueue.begin())
return *StoreQueue.begin();
const InstRef &Store = *StoreQueue.begin();
if (Index > Store.getSourceIndex())
return Store;
}
// Okay, we are older than the oldest store in the queue.
// If there are no pending loads, then we can say for sure that this
// instruction is ready.
if (isLQEmpty())
return Index;
return IR;
// Check if there are no older loads.
if (Index <= *LoadQueue.begin())
return Index;
const InstRef &Load = *LoadQueue.begin();
if (Index <= Load.getSourceIndex())
return IR;
// There is at least one younger load.
//
// A load may pass a previous load.
if (IsALoad)
return Index;
return IR;
// A store may not pass a previous load.
return *LoadQueue.begin();
return Load;
}
void LSUnit::onInstructionExecuted(const InstRef &IR) {
@ -161,29 +162,35 @@ void LSUnit::onInstructionExecuted(const InstRef &IR) {
bool IsAStore = Desc.MayStore;
if (IsALoad) {
if (LoadQueue.erase(Index)) {
if (LoadQueue.erase(IR)) {
LLVM_DEBUG(dbgs() << "[LSUnit]: Instruction idx=" << Index
<< " has been removed from the load queue.\n");
}
if (!LoadBarriers.empty() && Index == *LoadBarriers.begin()) {
LLVM_DEBUG(
dbgs() << "[LSUnit]: Instruction idx=" << Index
<< " has been removed from the set of load barriers.\n");
LoadBarriers.erase(Index);
if (!LoadBarriers.empty()) {
const InstRef &LoadBarrier = *LoadBarriers.begin();
if (Index == LoadBarrier.getSourceIndex()) {
LLVM_DEBUG(
dbgs() << "[LSUnit]: Instruction idx=" << Index
<< " has been removed from the set of load barriers.\n");
LoadBarriers.erase(IR);
}
}
}
if (IsAStore) {
if (StoreQueue.erase(Index)) {
if (StoreQueue.erase(IR)) {
LLVM_DEBUG(dbgs() << "[LSUnit]: Instruction idx=" << Index
<< " has been removed from the store queue.\n");
}
if (!StoreBarriers.empty() && Index == *StoreBarriers.begin()) {
LLVM_DEBUG(
dbgs() << "[LSUnit]: Instruction idx=" << Index
<< " has been removed from the set of store barriers.\n");
StoreBarriers.erase(Index);
if (!StoreBarriers.empty()) {
const InstRef &StoreBarrier = *StoreBarriers.begin();
if (Index == StoreBarrier.getSourceIndex()) {
LLVM_DEBUG(
dbgs() << "[LSUnit]: Instruction idx=" << Index
<< " has been removed from the set of store barriers.\n");
StoreBarriers.erase(IR);
}
}
}
}

View File

@ -119,9 +119,9 @@ bool Scheduler::promoteToReadySet(SmallVectorImpl<InstRef> &Ready) {
// Check if there are still unsolved memory dependencies.
Instruction &IS = *IR.getInstruction();
if (IS.isMemOp()) {
unsigned CriticalMemDep = LSU.isReady(IR);
if (CriticalMemDep != IR.getSourceIndex()) {
IS.setCriticalMemDep(CriticalMemDep);
const InstRef &CriticalMemDep = LSU.isReady(IR);
if (CriticalMemDep != IR) {
IS.setCriticalMemDep(CriticalMemDep.getSourceIndex());
++I;
continue;
}
@ -158,7 +158,7 @@ bool Scheduler::promoteToPendingSet(SmallVectorImpl<InstRef> &Pending) {
break;
// Check if this instruction is now ready. In case, force
// a transition in state using method 'update()'.
// a transition in state using method 'updateDispatched()'.
Instruction &IS = *IR.getInstruction();
if (IS.isDispatched() && !IS.updateDispatched()) {
++I;
@ -242,12 +242,10 @@ void Scheduler::analyzeDataDependencies(SmallVectorImpl<InstRef> &RegDeps,
if (Resources->checkAvailability(IS.getDesc()))
continue;
if (IS.isReady() ||
(IS.isMemOp() && LSU.isReady(IR) != IR.getSourceIndex())) {
if (IS.isReady() || (IS.isMemOp() && LSU.isReady(IR) != IR))
MemDeps.emplace_back(IR);
} else {
else
RegDeps.emplace_back(IR);
}
}
}
@ -304,8 +302,7 @@ bool Scheduler::dispatch(const InstRef &IR) {
// Memory operations that are not in a ready state are initially assigned to
// the WaitSet.
if (!IS.isReady() ||
(IS.isMemOp() && LSU.isReady(IR) != IR.getSourceIndex())) {
if (!IS.isReady() || (IS.isMemOp() && LSU.isReady(IR) != IR)) {
LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR << " to the WaitSet\n");
WaitSet.push_back(IR);
return false;