Commit a2ceaa23 authored by baptiste.afsa's avatar baptiste.afsa Committed by Commit bot

[turbofan] Refactoring around the instruction scheduler.

Extract the logic to find out the best candidate out of the core of the
scheduler. It allows more flexibility and make it easy to change the
policy use to schedule the basic blocks.

This patch also provide a new algorithm to randomly schedule the code
in order to perform stress tests on the scheduler.

R=jarin@chromium.org

Review URL: https://codereview.chromium.org/1714753004

Cr-Commit-Position: refs/heads/master@{#34156}
parent 6cecb3eb
......@@ -5,11 +5,57 @@
#include "src/compiler/instruction-scheduler.h"
#include "src/base/adapters.h"
#include "src/base/utils/random-number-generator.h"
namespace v8 {
namespace internal {
namespace compiler {
// Compare the two nodes and return true if node1 is a better candidate than
// node2 (i.e. node1 should be scheduled before node2).
bool InstructionScheduler::CriticalPathFirstQueue::CompareNodes(
ScheduleGraphNode *node1, ScheduleGraphNode *node2) const {
return node1->total_latency() > node2->total_latency();
}
InstructionScheduler::ScheduleGraphNode*
InstructionScheduler::CriticalPathFirstQueue::PopBestCandidate(int cycle) {
DCHECK(!IsEmpty());
auto candidate = nodes_.end();
for (auto iterator = nodes_.begin(); iterator != nodes_.end(); ++iterator) {
// We only consider instructions that have all their operands ready and
// we try to schedule the critical path first.
if (cycle >= (*iterator)->start_cycle()) {
if ((candidate == nodes_.end()) || CompareNodes(*iterator, *candidate)) {
candidate = iterator;
}
}
}
if (candidate != nodes_.end()) {
ScheduleGraphNode *result = *candidate;
nodes_.erase(candidate);
return result;
}
return nullptr;
}
InstructionScheduler::ScheduleGraphNode*
InstructionScheduler::StressSchedulerQueue::PopBestCandidate(int cycle) {
DCHECK(!IsEmpty());
// Choose a random element from the ready list.
auto candidate = nodes_.begin();
std::advance(candidate, isolate()->random_number_generator()->NextInt(
static_cast<int>(nodes_.size())));
ScheduleGraphNode *result = *candidate;
nodes_.erase(candidate);
return result;
}
InstructionScheduler::ScheduleGraphNode::ScheduleGraphNode(
Zone* zone,
Instruction* instr)
......@@ -50,7 +96,11 @@ void InstructionScheduler::StartBlock(RpoNumber rpo) {
void InstructionScheduler::EndBlock(RpoNumber rpo) {
ScheduleBlock();
if (FLAG_turbo_stress_instruction_scheduling) {
ScheduleBlock<StressSchedulerQueue>();
} else {
ScheduleBlock<CriticalPathFirstQueue>();
}
sequence()->EndBlock(rpo);
graph_.clear();
last_side_effect_instr_ = nullptr;
......@@ -110,14 +160,9 @@ void InstructionScheduler::AddInstruction(Instruction* instr) {
}
bool InstructionScheduler::CompareNodes(ScheduleGraphNode *node1,
ScheduleGraphNode *node2) const {
return node1->total_latency() > node2->total_latency();
}
template <typename QueueType>
void InstructionScheduler::ScheduleBlock() {
ZoneLinkedList<ScheduleGraphNode*> ready_list(zone());
QueueType ready_list(this);
// Compute total latencies so that we can schedule the critical path first.
ComputeTotalLatencies();
......@@ -125,43 +170,28 @@ void InstructionScheduler::ScheduleBlock() {
// Add nodes which don't have dependencies to the ready list.
for (auto node : graph_) {
if (!node->HasUnscheduledPredecessor()) {
ready_list.push_back(node);
ready_list.AddNode(node);
}
}
// Go through the ready list and schedule the instructions.
int cycle = 0;
while (!ready_list.empty()) {
auto candidate = ready_list.end();
for (auto iterator = ready_list.begin(); iterator != ready_list.end();
++iterator) {
// Look for the best candidate to schedule.
// We only consider instructions that have all their operands ready and
// we try to schedule the critical path first (we look for the instruction
// with the highest latency on the path to reach the end of the graph).
if (cycle >= (*iterator)->start_cycle()) {
if ((candidate == ready_list.end()) ||
CompareNodes(*iterator, *candidate)) {
candidate = iterator;
}
}
}
while (!ready_list.IsEmpty()) {
auto candidate = ready_list.PopBestCandidate(cycle);
if (candidate != ready_list.end()) {
sequence()->AddInstruction((*candidate)->instruction());
if (candidate != nullptr) {
sequence()->AddInstruction(candidate->instruction());
for (auto successor : (*candidate)->successors()) {
for (auto successor : candidate->successors()) {
successor->DropUnscheduledPredecessor();
successor->set_start_cycle(
std::max(successor->start_cycle(),
cycle + (*candidate)->latency()));
cycle + candidate->latency()));
if (!successor->HasUnscheduledPredecessor()) {
ready_list.push_back(successor);
ready_list.AddNode(successor);
}
}
ready_list.erase(candidate);
}
cycle++;
......
......@@ -90,11 +90,66 @@ class InstructionScheduler final : public ZoneObject {
int start_cycle_;
};
// Compare the two nodes and return true if node1 is a better candidate than
// node2 (i.e. node1 should be scheduled before node2).
bool CompareNodes(ScheduleGraphNode *node1, ScheduleGraphNode *node2) const;
// Keep track of all nodes ready to be scheduled (i.e. all their dependencies
// have been scheduled. Note that this class is inteded to be extended by
// concrete implementation of the scheduling queue which define the policy
// to pop node from the queue.
class SchedulingQueueBase {
public:
explicit SchedulingQueueBase(InstructionScheduler* scheduler)
: scheduler_(scheduler),
nodes_(scheduler->zone()) {
}
void AddNode(ScheduleGraphNode* node) {
nodes_.push_back(node);
}
bool IsEmpty() const {
return nodes_.empty();
}
protected:
InstructionScheduler* scheduler_;
ZoneLinkedList<ScheduleGraphNode*> nodes_;
};
// A scheduling queue which prioritize nodes on the critical path (we look
// for the instruction with the highest latency on the path to reach the end
// of the graph).
class CriticalPathFirstQueue : public SchedulingQueueBase {
public:
explicit CriticalPathFirstQueue(InstructionScheduler* scheduler)
: SchedulingQueueBase(scheduler) { }
// Look for the best candidate to schedule, remove it from the queue and
// return it.
ScheduleGraphNode* PopBestCandidate(int cycle);
private:
// Compare the two nodes and return true if node1 is a better candidate than
// node2 (i.e. node1 should be scheduled before node2).
bool CompareNodes(ScheduleGraphNode *node1, ScheduleGraphNode *node2) const;
};
// A queue which pop a random node from the queue to perform stress tests on
// the scheduler.
class StressSchedulerQueue : public SchedulingQueueBase {
public:
explicit StressSchedulerQueue(InstructionScheduler* scheduler)
: SchedulingQueueBase(scheduler) { }
ScheduleGraphNode* PopBestCandidate(int cycle);
private:
Isolate *isolate() {
return scheduler_->isolate();
}
};
// Perform scheduling for the current block.
// Perform scheduling for the current block specifying the queue type to
// use to determine the next best candidate.
template <typename QueueType>
void ScheduleBlock();
// Return the scheduling properties of the given instruction.
......@@ -134,6 +189,7 @@ class InstructionScheduler final : public ZoneObject {
Zone* zone() { return zone_; }
InstructionSequence* sequence() { return sequence_; }
Isolate* isolate() { return sequence()->isolate(); }
Zone* zone_;
InstructionSequence* sequence_;
......
......@@ -472,6 +472,8 @@ DEFINE_BOOL(turbo_preserve_shared_code, false, "keep context-independent code")
DEFINE_BOOL(turbo_escape, false, "enable escape analysis")
DEFINE_BOOL(turbo_instruction_scheduling, false,
"enable instruction scheduling in TurboFan")
DEFINE_BOOL(turbo_stress_instruction_scheduling, false,
"randomly schedule instructions to stress dependency tracking")
// Flags for native WebAssembly.
DEFINE_BOOL(expose_wasm, false, "expose WASM interface to JavaScript")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment