Commit 6bbf6c5c authored by titzer@chromium.org's avatar titzer@chromium.org

Schedule floating control.

This CL makes several changes to the scheduling algorithm to handle control
flow that is not connected to End. Such control nodes constitute "floating
control islands" that must be linearized by the schedule. This is done
by considering such nodes to be schedulable, and then editing the control
dependencies after a first pass of scheduling. Then a subsequent pass of
scheduling will place all nodes correctly into the fully connected graph.

R=mstarzinger@chromium.org, rossberg@chromium.org
BUG=

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23411 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent ecca77ce
......@@ -42,7 +42,6 @@ class NodeProperties {
static inline Bounds GetBounds(Node* node);
static inline void SetBounds(Node* node, Bounds bounds);
private:
static inline int FirstValueIndex(Node* node);
static inline int FirstContextIndex(Node* node);
static inline int FirstFrameStateIndex(Node* node);
......
......@@ -87,6 +87,7 @@ void Pipeline::VerifyAndPrintGraph(Graph* graph, const char* phase) {
if (FLAG_trace_turbo) {
char buffer[256];
Vector<char> filename(buffer, sizeof(buffer));
if (!info_->shared_info().is_null()) {
SmartArrayPointer<char> functionname =
info_->shared_info()->DebugName()->ToCString();
if (strlen(functionname.get()) > 0) {
......@@ -94,6 +95,9 @@ void Pipeline::VerifyAndPrintGraph(Graph* graph, const char* phase) {
} else {
SNPrintF(filename, "turbo-%p-%s.dot", static_cast<void*>(info_), phase);
}
} else {
SNPrintF(filename, "turbo-none-%s.dot", phase);
}
std::replace(filename.start(), filename.start() + filename.length(), ' ',
'_');
FILE* file = base::OS::FOpen(filename.start(), "w+");
......
......@@ -40,6 +40,7 @@ class BasicBlockData {
};
int32_t rpo_number_; // special RPO number of the block.
BasicBlock* dominator_; // Immediate dominator of the block.
BasicBlock* loop_header_; // Pointer to dominating loop header basic block,
// NULL if none. For loop headers, this points to
// enclosing loop header.
......@@ -55,6 +56,7 @@ class BasicBlockData {
explicit BasicBlockData(Zone* zone)
: rpo_number_(-1),
dominator_(NULL),
loop_header_(NULL),
loop_depth_(0),
loop_end_(-1),
......@@ -160,8 +162,7 @@ class Schedule : public GenericGraph<BasicBlock> {
zone_(zone),
all_blocks_(zone),
nodeid_to_block_(zone),
rpo_order_(zone),
immediate_dominator_(zone) {
rpo_order_(zone) {
SetStart(NewBasicBlock()); // entry.
SetEnd(NewBasicBlock()); // exit.
}
......@@ -174,10 +175,6 @@ class Schedule : public GenericGraph<BasicBlock> {
return NULL;
}
BasicBlock* dominator(BasicBlock* block) {
return immediate_dominator_[block->id()];
}
bool IsScheduled(Node* node) {
int length = static_cast<int>(nodeid_to_block_.size());
if (node->id() >= length) return false;
......@@ -212,8 +209,8 @@ class Schedule : public GenericGraph<BasicBlock> {
// doesn't actually add the node to the block.
inline void PlanNode(BasicBlock* block, Node* node) {
if (FLAG_trace_turbo_scheduler) {
PrintF("Planning node %d for future add to block %d\n", node->id(),
block->id());
PrintF("Planning #%d:%s for future add to B%d\n", node->id(),
node->op()->mnemonic(), block->id());
}
DCHECK(this->block(node) == NULL);
SetBlockForNode(block, node);
......@@ -222,7 +219,8 @@ class Schedule : public GenericGraph<BasicBlock> {
// BasicBlock building: add a node to the end of the block.
inline void AddNode(BasicBlock* block, Node* node) {
if (FLAG_trace_turbo_scheduler) {
PrintF("Adding node %d to block %d\n", node->id(), block->id());
PrintF("Adding #%d:%s to B%d\n", node->id(), node->op()->mnemonic(),
block->id());
}
DCHECK(this->block(node) == NULL || this->block(node) == block);
block->nodes_.push_back(node);
......@@ -247,6 +245,7 @@ class Schedule : public GenericGraph<BasicBlock> {
AddSuccessor(block, deopt_block);
AddSuccessor(block, cont_block);
SetControlInput(block, call);
SetBlockForNode(block, call);
}
// BasicBlock building: add a branch at the end of {block}.
......@@ -258,15 +257,22 @@ class Schedule : public GenericGraph<BasicBlock> {
AddSuccessor(block, tblock);
AddSuccessor(block, fblock);
SetControlInput(block, branch);
if (branch->opcode() == IrOpcode::kBranch) {
// TODO(titzer): require a Branch node here. (sloppy tests).
SetBlockForNode(block, branch);
}
}
// BasicBlock building: add a return at the end of {block}.
void AddReturn(BasicBlock* block, Node* input) {
// TODO(titzer): require a Return node here.
DCHECK(block->control_ == BasicBlock::kNone);
block->control_ = BasicBlock::kReturn;
SetControlInput(block, input);
if (block != end()) AddSuccessor(block, end());
if (input->opcode() == IrOpcode::kReturn) {
// TODO(titzer): require a Return node here. (sloppy tests).
SetBlockForNode(block, input);
}
}
// BasicBlock building: add a throw at the end of {block}.
......@@ -315,9 +321,6 @@ class Schedule : public GenericGraph<BasicBlock> {
BasicBlockVector all_blocks_; // All basic blocks in the schedule.
BasicBlockVector nodeid_to_block_; // Map from node to containing block.
BasicBlockVector rpo_order_; // Reverse-post-order block list.
BasicBlockVector immediate_dominator_; // Maps to a block's immediate
// dominator, indexed by block
// id.
};
OStream& operator<<(OStream& os, const Schedule& s);
......
This diff is collapsed.
......@@ -31,19 +31,37 @@ class Scheduler {
static void ComputeCFG(Graph* graph, Schedule* schedule);
private:
enum Placement { kUnknown, kSchedulable, kFixed };
// Per-node data tracked during scheduling.
struct SchedulerData {
int unscheduled_count_; // Number of unscheduled uses of this node.
int minimum_rpo_; // Minimum legal RPO placement.
bool is_connected_control_; // {true} if control-connected to the end node.
bool is_floating_control_; // {true} if control, but not control-connected
// to the end node.
Placement placement_ : 3; // Whether the node is fixed, schedulable,
// or not yet known.
};
Zone* zone_;
Graph* graph_;
Schedule* schedule_;
IntVector unscheduled_uses_;
NodeVectorVector scheduled_nodes_;
NodeVector schedule_root_nodes_;
IntVector schedule_early_rpo_index_;
ZoneVector<SchedulerData> node_data_;
bool has_floating_control_;
Scheduler(Zone* zone, Graph* graph, Schedule* schedule);
bool IsBasicBlockBegin(Node* node);
bool HasFixedSchedulePosition(Node* node);
bool IsScheduleRoot(Node* node);
SchedulerData* GetData(Node* node) {
DCHECK(node->id() < static_cast<int>(node_data_.size()));
return &node_data_[node->id()];
}
void BuildCFG();
Placement GetPlacement(Node* node);
int GetRPONumber(BasicBlock* block) {
DCHECK(block->rpo_number_ >= 0 &&
......@@ -52,12 +70,11 @@ class Scheduler {
return block->rpo_number_;
}
void PrepareAuxiliaryNodeData();
void PrepareAuxiliaryBlockData();
void GenerateImmediateDominatorTree();
BasicBlock* GetCommonDominator(BasicBlock* b1, BasicBlock* b2);
friend class CFGBuilder;
friend class ScheduleEarlyNodeVisitor;
void ScheduleEarly();
......@@ -66,6 +83,10 @@ class Scheduler {
friend class ScheduleLateNodeVisitor;
void ScheduleLate();
bool ConnectFloatingControl();
void ConnectFloatingControlSubgraph(BasicBlock* block, Node* node);
};
}
}
......
......@@ -259,7 +259,7 @@ static bool HasDominatingDef(Schedule* schedule, Node* node,
if (block->nodes_[use_pos] == node) return true;
use_pos--;
}
block = schedule->dominator(block);
block = block->dominator_;
if (block == NULL) break;
use_pos = static_cast<int>(block->nodes_.size()) - 1;
if (node == block->control_input_) return true;
......@@ -308,7 +308,7 @@ void ScheduleVerifier::Run(Schedule* schedule) {
for (size_t b = 0; b < rpo_order->size(); b++) {
BasicBlock* block = rpo_order->at(b);
CHECK_EQ(static_cast<int>(b), block->rpo_number_);
BasicBlock* dom = schedule->dominator(block);
BasicBlock* dom = block->dominator_;
if (b == 0) {
// All blocks except start should have a dominator.
CHECK_EQ(NULL, dom);
......@@ -365,7 +365,7 @@ void ScheduleVerifier::Run(Schedule* schedule) {
BasicBlock* block = queue.front();
queue.pop();
BitVector* block_doms = dominators[block->id()];
BasicBlock* idom = schedule->dominator(block);
BasicBlock* idom = block->dominator_;
if (idom != NULL && !block_doms->Contains(idom->id())) {
V8_Fatal(__FILE__, __LINE__, "Block B%d is not dominated by B%d",
block->id(), idom->id());
......@@ -375,14 +375,14 @@ void ScheduleVerifier::Run(Schedule* schedule) {
BitVector* succ_doms = dominators[succ->id()];
if (succ_doms == NULL) {
// First time visiting the node. S.vec = B U B.vec
// First time visiting the node. S.doms = B U B.doms
succ_doms = new (zone) BitVector(count, zone);
succ_doms->CopyFrom(*block_doms);
succ_doms->Add(block->id());
dominators[succ->id()] = succ_doms;
queue.push(succ);
} else {
// Nth time visiting the successor. S.vec = S.vec ^ (B U B.vec)
// Nth time visiting the successor. S.doms = S.doms ^ (B U B.doms)
bool had = succ_doms->Contains(block->id());
if (had) succ_doms->Remove(block->id());
if (succ_doms->IntersectIsChanged(*block_doms)) queue.push(succ);
......@@ -395,7 +395,7 @@ void ScheduleVerifier::Run(Schedule* schedule) {
for (BasicBlockVector::iterator b = rpo_order->begin();
b != rpo_order->end(); ++b) {
BasicBlock* block = *b;
BasicBlock* idom = schedule->dominator(block);
BasicBlock* idom = block->dominator_;
if (idom == NULL) continue;
BitVector* block_doms = dominators[block->id()];
......
......@@ -16,10 +16,12 @@
#include "src/compiler/operator.h"
#include "src/compiler/schedule.h"
#include "src/compiler/scheduler.h"
#include "src/compiler/verifier.h"
using namespace v8::internal;
using namespace v8::internal::compiler;
// TODO(titzer): pull RPO tests out to their own file.
struct TestLoop {
int count;
BasicBlock** nodes;
......@@ -65,6 +67,42 @@ static void CheckLoopContains(BasicBlock** blocks, int body_size) {
}
static int GetScheduledNodeCount(Schedule* schedule) {
int node_count = 0;
for (BasicBlockVectorIter i = schedule->rpo_order()->begin();
i != schedule->rpo_order()->end(); ++i) {
BasicBlock* block = *i;
for (BasicBlock::const_iterator j = block->begin(); j != block->end();
++j) {
++node_count;
}
BasicBlock::Control control = block->control_;
if (control != BasicBlock::kNone) {
++node_count;
}
}
return node_count;
}
static Schedule* ComputeAndVerifySchedule(int expected, Graph* graph) {
if (FLAG_trace_turbo) {
OFStream os(stdout);
os << AsDOT(*graph);
}
Schedule* schedule = Scheduler::ComputeSchedule(graph);
if (FLAG_trace_turbo_scheduler) {
OFStream os(stdout);
os << *schedule << endl;
}
ScheduleVerifier::Run(schedule);
CHECK_EQ(expected, GetScheduledNodeCount(schedule));
return schedule;
}
TEST(RPODegenerate1) {
HandleAndZoneScope scope;
Schedule schedule(scope.main_zone());
......@@ -605,36 +643,6 @@ TEST(BuildScheduleOneParameter) {
}
static int GetScheduledNodeCount(Schedule* schedule) {
int node_count = 0;
for (BasicBlockVectorIter i = schedule->rpo_order()->begin();
i != schedule->rpo_order()->end(); ++i) {
BasicBlock* block = *i;
for (BasicBlock::const_iterator j = block->begin(); j != block->end();
++j) {
++node_count;
}
BasicBlock::Control control = block->control_;
if (control != BasicBlock::kNone) {
++node_count;
}
}
return node_count;
}
static void PrintGraph(Graph* graph) {
OFStream os(stdout);
os << AsDOT(*graph);
}
static void PrintSchedule(Schedule* schedule) {
OFStream os(stdout);
os << *schedule << endl;
}
TEST(BuildScheduleIfSplit) {
HandleAndZoneScope scope;
Graph graph(scope.main_zone());
......@@ -658,14 +666,7 @@ TEST(BuildScheduleIfSplit) {
Node* merge = graph.NewNode(builder.Merge(2), ret1, ret2);
graph.SetEnd(graph.NewNode(builder.End(), merge));
PrintGraph(&graph);
Schedule* schedule = Scheduler::ComputeSchedule(&graph);
PrintSchedule(schedule);
CHECK_EQ(13, GetScheduledNodeCount(schedule));
ComputeAndVerifySchedule(13, &graph);
}
......@@ -811,13 +812,7 @@ TEST(BuildScheduleIfSplitWithEffects) {
graph.SetStart(n0);
graph.SetEnd(n23);
PrintGraph(&graph);
Schedule* schedule = Scheduler::ComputeSchedule(&graph);
PrintSchedule(schedule);
CHECK_EQ(20, GetScheduledNodeCount(schedule));
ComputeAndVerifySchedule(20, &graph);
}
......@@ -930,13 +925,7 @@ TEST(BuildScheduleSimpleLoop) {
graph.SetStart(n0);
graph.SetEnd(n20);
PrintGraph(&graph);
Schedule* schedule = Scheduler::ComputeSchedule(&graph);
PrintSchedule(schedule);
CHECK_EQ(19, GetScheduledNodeCount(schedule));
ComputeAndVerifySchedule(19, &graph);
}
......@@ -1184,13 +1173,7 @@ TEST(BuildScheduleComplexLoops) {
graph.SetStart(n0);
graph.SetEnd(n46);
PrintGraph(&graph);
Schedule* schedule = Scheduler::ComputeSchedule(&graph);
PrintSchedule(schedule);
CHECK_EQ(46, GetScheduledNodeCount(schedule));
ComputeAndVerifySchedule(46, &graph);
}
......@@ -1520,13 +1503,7 @@ TEST(BuildScheduleBreakAndContinue) {
graph.SetStart(n0);
graph.SetEnd(n58);
PrintGraph(&graph);
Schedule* schedule = Scheduler::ComputeSchedule(&graph);
PrintSchedule(schedule);
CHECK_EQ(62, GetScheduledNodeCount(schedule));
ComputeAndVerifySchedule(62, &graph);
}
......@@ -1651,14 +1628,7 @@ TEST(BuildScheduleSimpleLoopWithCodeMotion) {
graph.SetStart(n0);
graph.SetEnd(n22);
PrintGraph(&graph);
Schedule* schedule = Scheduler::ComputeSchedule(&graph);
PrintSchedule(schedule);
CHECK_EQ(19, GetScheduledNodeCount(schedule));
Schedule* schedule = ComputeAndVerifySchedule(19, &graph);
// Make sure the integer-only add gets hoisted to a different block that the
// JSAdd.
CHECK(schedule->block(n19) != schedule->block(n20));
......@@ -1771,11 +1741,7 @@ TEST(BuildScheduleTrivialLazyDeoptCall) {
graph.SetStart(start_node);
graph.SetEnd(end_node);
PrintGraph(&graph);
Schedule* schedule = Scheduler::ComputeSchedule(&graph);
PrintSchedule(schedule);
Schedule* schedule = ComputeAndVerifySchedule(12, &graph);
// Tests:
// Continuation and deopt have basic blocks.
......@@ -1806,4 +1772,83 @@ TEST(BuildScheduleTrivialLazyDeoptCall) {
CHECK_EQ(state_node, deopt_block->nodes_[4]);
}
static Node* CreateDiamond(Graph* graph, CommonOperatorBuilder* common,
Node* cond) {
Node* tv = graph->NewNode(common->Int32Constant(6));
Node* fv = graph->NewNode(common->Int32Constant(7));
Node* br = graph->NewNode(common->Branch(), cond, graph->start());
Node* t = graph->NewNode(common->IfTrue(), br);
Node* f = graph->NewNode(common->IfFalse(), br);
Node* m = graph->NewNode(common->Merge(2), t, f);
Node* phi = graph->NewNode(common->Phi(2), tv, fv, m);
return phi;
}
TEST(FloatingDiamond1) {
HandleAndZoneScope scope;
Graph graph(scope.main_zone());
CommonOperatorBuilder common(scope.main_zone());
Node* start = graph.NewNode(common.Start(1));
graph.SetStart(start);
Node* p0 = graph.NewNode(common.Parameter(0), start);
Node* d1 = CreateDiamond(&graph, &common, p0);
Node* ret = graph.NewNode(common.Return(), d1, start, start);
Node* end = graph.NewNode(common.End(), ret, start);
graph.SetEnd(end);
ComputeAndVerifySchedule(13, &graph);
}
TEST(FloatingDiamond2) {
HandleAndZoneScope scope;
Graph graph(scope.main_zone());
CommonOperatorBuilder common(scope.main_zone());
MachineOperatorBuilder machine(scope.main_zone());
Node* start = graph.NewNode(common.Start(2));
graph.SetStart(start);
Node* p0 = graph.NewNode(common.Parameter(0), start);
Node* p1 = graph.NewNode(common.Parameter(1), start);
Node* d1 = CreateDiamond(&graph, &common, p0);
Node* d2 = CreateDiamond(&graph, &common, p1);
Node* add = graph.NewNode(machine.Int32Add(), d1, d2);
Node* ret = graph.NewNode(common.Return(), add, start, start);
Node* end = graph.NewNode(common.End(), ret, start);
graph.SetEnd(end);
ComputeAndVerifySchedule(24, &graph);
}
TEST(FloatingDiamond3) {
HandleAndZoneScope scope;
Graph graph(scope.main_zone());
CommonOperatorBuilder common(scope.main_zone());
MachineOperatorBuilder machine(scope.main_zone());
Node* start = graph.NewNode(common.Start(2));
graph.SetStart(start);
Node* p0 = graph.NewNode(common.Parameter(0), start);
Node* p1 = graph.NewNode(common.Parameter(1), start);
Node* d1 = CreateDiamond(&graph, &common, p0);
Node* d2 = CreateDiamond(&graph, &common, p1);
Node* add = graph.NewNode(machine.Int32Add(), d1, d2);
Node* d3 = CreateDiamond(&graph, &common, add);
Node* ret = graph.NewNode(common.Return(), d3, start, start);
Node* end = graph.NewNode(common.End(), ret, start);
graph.SetEnd(end);
ComputeAndVerifySchedule(33, &graph);
}
#endif
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