Skip to content

Commit f3374f0

Browse files
committedAug 21, 2018
[llvm-mca] Add the ability to customize the instruction selection strategy in the Scheduler.
The constructor of Scheduler now accepts a SchedulerStrategy object, which is used internally by method Scheduler::select() to drive the instruction selection process. The goal of this patch is to enable the definition of custom selection strategies while reusing the same algorithms implemented by class Scheduler. The motivation is that, on some targets, the default strategy may not well approximate the selection logic in the hardware schedulers. This patch also adds the ability to pass a ResourceManager object to the constructor of Scheduler. This gives a bit more flexibility to the design, and potentially it allows to expose processor resources to SchedulerStrategy objects. Differential Revision: https://reviews.llvm.org/D51051 llvm-svn: 340314
1 parent 9848e0c commit f3374f0

File tree

2 files changed

+64
-34
lines changed

2 files changed

+64
-34
lines changed
 

‎llvm/tools/llvm-mca/Scheduler.cpp

+11-24
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,10 @@ void ResourceManager::releaseResource(uint64_t ResourceID) {
248248
Resource.clearReserved();
249249
}
250250

251+
// Anchor the vtable of SchedulerStrategy and DefaultSchedulerStrategy.
252+
SchedulerStrategy::~SchedulerStrategy() = default;
253+
DefaultSchedulerStrategy::~DefaultSchedulerStrategy() = default;
254+
251255
#ifndef NDEBUG
252256
void Scheduler::dump() const {
253257
dbgs() << "[SCHEDULER]: WaitSet size is: " << WaitSet.size() << '\n';
@@ -259,7 +263,7 @@ void Scheduler::dump() const {
259263

260264
Scheduler::Status Scheduler::isAvailable(const InstRef &IR) const {
261265
const InstrDesc &Desc = IR.getInstruction()->getDesc();
262-
266+
263267
switch (Resources->canBeDispatched(Desc.Buffers)) {
264268
case ResourceStateEvent::RS_BUFFER_UNAVAILABLE:
265269
return Scheduler::SC_BUFFERS_FULL;
@@ -335,7 +339,7 @@ void Scheduler::promoteToReadySet(SmallVectorImpl<InstRef> &Ready) {
335339
if (!IS.isReady())
336340
IS.update();
337341

338-
// Check f there are still unsolved data dependencies.
342+
// Check if there are still unsolved data dependencies.
339343
if (!isReady(IR)) {
340344
++I;
341345
continue;
@@ -354,30 +358,13 @@ void Scheduler::promoteToReadySet(SmallVectorImpl<InstRef> &Ready) {
354358

355359
InstRef Scheduler::select() {
356360
unsigned QueueIndex = ReadySet.size();
357-
int Rank = std::numeric_limits<int>::max();
358-
359361
for (unsigned I = 0, E = ReadySet.size(); I != E; ++I) {
360362
const InstRef &IR = ReadySet[I];
361-
const unsigned IID = IR.getSourceIndex();
362-
const Instruction &IS = *IR.getInstruction();
363-
364-
// Compute a rank value based on the age of an instruction (i.e. its source
365-
// index) and its number of users. The lower the rank value, the better.
366-
int CurrentRank = IID - IS.getNumUsers();
367-
368-
// We want to prioritize older instructions over younger instructions to
369-
// minimize the pressure on the reorder buffer. We also want to
370-
// rank higher the instructions with more users to better expose ILP.
371-
if (CurrentRank == Rank)
372-
if (IID > ReadySet[QueueIndex].getSourceIndex())
373-
continue;
374-
375-
if (CurrentRank <= Rank) {
376-
const InstrDesc &D = IS.getDesc();
377-
if (Resources->canBeIssued(D)) {
378-
Rank = CurrentRank;
363+
if (QueueIndex == ReadySet.size() ||
364+
Strategy->compare(IR, ReadySet[QueueIndex])) {
365+
const InstrDesc &D = IR.getInstruction()->getDesc();
366+
if (Resources->canBeIssued(D))
379367
QueueIndex = I;
380-
}
381368
}
382369
}
383370

@@ -430,7 +417,7 @@ void Scheduler::cycleEvent(SmallVectorImpl<ResourceRef> &Freed,
430417

431418
for (InstRef &IR : WaitSet)
432419
IR.getInstruction()->cycleEvent();
433-
420+
434421
promoteToReadySet(Ready);
435422
}
436423

‎llvm/tools/llvm-mca/Scheduler.h

+53-10
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,42 @@ class ResourceManager {
339339
#endif
340340
}; // namespace mca
341341

342+
class SchedulerStrategy {
343+
public:
344+
SchedulerStrategy() = default;
345+
virtual ~SchedulerStrategy();
346+
347+
/// Returns true if Lhs should take priority over Rhs.
348+
///
349+
/// This method is used by class Scheduler to select the "best" ready
350+
/// instruction to issue to the underlying pipelines.
351+
virtual bool compare(const InstRef &Lhs, const InstRef &Rhs) const = 0;
352+
};
353+
354+
/// Default instruction selection strategy used by class Scheduler.
355+
class DefaultSchedulerStrategy : public SchedulerStrategy {
356+
/// This method ranks instructions based on their age, and the number of known
357+
/// users. The lower the rank value, the better.
358+
int computeRank(const InstRef &Lhs) const {
359+
return Lhs.getSourceIndex() - Lhs.getInstruction()->getNumUsers();
360+
}
361+
362+
public:
363+
DefaultSchedulerStrategy() = default;
364+
virtual ~DefaultSchedulerStrategy();
365+
366+
bool compare(const InstRef &Lhs, const InstRef &Rhs) const override {
367+
int LhsRank = computeRank(Lhs);
368+
int RhsRank = computeRank(Rhs);
369+
370+
/// Prioritize older instructions over younger instructions to minimize the
371+
/// pressure on the reorder buffer.
372+
if (LhsRank == RhsRank)
373+
return Lhs.getSourceIndex() < Rhs.getSourceIndex();
374+
return LhsRank < RhsRank;
375+
}
376+
};
377+
342378
/// Class Scheduler is responsible for issuing instructions to pipeline
343379
/// resources.
344380
///
@@ -363,9 +399,11 @@ class ResourceManager {
363399
/// transition (i.e. from state IS_READY, to state IS_EXECUTING). An Instruction
364400
/// leaves the IssuedSet when it reaches the write-back stage.
365401
class Scheduler : public HardwareUnit {
366-
const llvm::MCSchedModel &SM;
367402
LSUnit *LSU;
368403

404+
// Instruction selection strategy for this Scheduler.
405+
std::unique_ptr<SchedulerStrategy> Strategy;
406+
369407
// Hardware resources that are managed by this scheduler.
370408
std::unique_ptr<ResourceManager> Resources;
371409

@@ -389,8 +427,17 @@ class Scheduler : public HardwareUnit {
389427

390428
public:
391429
Scheduler(const llvm::MCSchedModel &Model, LSUnit *Lsu)
392-
: SM(Model), LSU(Lsu), Resources(llvm::make_unique<ResourceManager>(SM)) {
393-
}
430+
: LSU(Lsu),
431+
Strategy(llvm::make_unique<DefaultSchedulerStrategy>()),
432+
Resources(llvm::make_unique<ResourceManager>(Model)) {}
433+
Scheduler(const llvm::MCSchedModel &Model, LSUnit *Lsu,
434+
std::unique_ptr<SchedulerStrategy> SelectStrategy)
435+
: LSU(Lsu), Strategy(std::move(SelectStrategy)),
436+
Resources(llvm::make_unique<ResourceManager>(Model)) {}
437+
Scheduler(std::unique_ptr<ResourceManager> RM, LSUnit *Lsu,
438+
std::unique_ptr<SchedulerStrategy> SelectStrategy)
439+
: LSU(Lsu), Strategy(std::move(SelectStrategy)),
440+
Resources(std::move(RM)) {}
394441

395442
// Stalls generated by the scheduler.
396443
enum Status {
@@ -450,13 +497,9 @@ class Scheduler : public HardwareUnit {
450497
return Resources->resolveResourceMask(Mask);
451498
}
452499

453-
/// Select the next instruction to issue from the ReadySet.
454-
///
455-
/// The default implementation of this method ranks instructions based on
456-
/// their age, and the number of known users. It prioritizes older
457-
/// instructions over younger instructions to minimize the pressure on the
458-
/// reorder buffer. It also gives a little priority boost to instructions
459-
/// with multiple users to better expose ILP.
500+
/// Select the next instruction to issue from the ReadySet. Returns an invalid
501+
/// instruction reference if there are no ready instructions, or if processor
502+
/// resources are not available.
460503
InstRef select();
461504

462505
#ifndef NDEBUG

0 commit comments

Comments
 (0)
Please sign in to comment.