Index: src/compiler/graph-visualizer.cc |
diff --git a/src/compiler/graph-visualizer.cc b/src/compiler/graph-visualizer.cc |
index e0cf8f05e455e5051e1576955d74c80b96605595..707a695b0d40585dd834528af60e3f1e46f90f1d 100644 |
--- a/src/compiler/graph-visualizer.cc |
+++ b/src/compiler/graph-visualizer.cc |
@@ -8,7 +8,6 @@ |
#include <string> |
#include "src/code-stubs.h" |
-#include "src/compiler/generic-algorithm.h" |
#include "src/compiler/generic-node.h" |
#include "src/compiler/generic-node-inl.h" |
#include "src/compiler/graph.h" |
@@ -27,8 +26,61 @@ namespace v8 { |
namespace internal { |
namespace compiler { |
+static int SafeId(Node* node) { return node == NULL ? -1 : node->id(); } |
+ |
#define DEAD_COLOR "#999999" |
+class AllNodes { |
+ public: |
+ enum State { kDead, kGray, kLive }; |
+ |
+ AllNodes(Zone* local_zone, const Graph* graph) |
+ : state(graph->NodeCount(), kDead, local_zone), |
+ live(local_zone), |
+ gray(local_zone) { |
+ Node* end = graph->end(); |
+ state[end->id()] = kLive; |
+ live.push_back(end); |
+ // Find all live nodes reachable from end. |
+ for (size_t i = 0; i < live.size(); i++) { |
+ for (Node* const input : live[i]->inputs()) { |
+ if (input == NULL) { |
+ // TODO(titzer): print a warning. |
+ continue; |
+ } |
+ if (input->id() >= graph->NodeCount()) { |
+ // TODO(titzer): print a warning. |
+ continue; |
+ } |
+ if (state[input->id()] != kLive) { |
+ live.push_back(input); |
+ state[input->id()] = kLive; |
+ } |
+ } |
+ } |
+ |
+ // Find all nodes that are not reachable from end that use live nodes. |
+ for (size_t i = 0; i < live.size(); i++) { |
+ for (Node* const use : live[i]->uses()) { |
+ if (state[use->id()] == kDead) { |
+ gray.push_back(use); |
+ state[use->id()] = kGray; |
+ } |
+ } |
+ } |
+ } |
+ |
+ bool IsLive(Node* node) { |
+ return node != NULL && node->id() < static_cast<int>(state.size()) && |
+ state[node->id()] == kLive; |
+ } |
+ |
+ ZoneVector<State> state; |
+ NodeVector live; |
+ NodeVector gray; |
+}; |
+ |
+ |
class Escaped { |
public: |
explicit Escaped(const std::ostringstream& os, |
@@ -56,104 +108,104 @@ class Escaped { |
const char* const escaped_chars_; |
}; |
-class JSONGraphNodeWriter : public NullNodeVisitor { |
+class JSONGraphNodeWriter { |
public: |
- JSONGraphNodeWriter(std::ostream& os, Zone* zone, |
- const Graph* graph) // NOLINT |
- : os_(os), |
- graph_(graph), |
- first_node_(true) {} |
+ JSONGraphNodeWriter(std::ostream& os, Zone* zone, const Graph* graph) |
+ : os_(os), all_(zone, graph), first_node_(true) {} |
- void Print() { const_cast<Graph*>(graph_)->VisitNodeInputsFromEnd(this); } |
+ void Print() { |
+ for (Node* const node : all_.live) PrintNode(node); |
+ } |
- void Pre(Node* node); |
+ void PrintNode(Node* node) { |
+ if (first_node_) { |
+ first_node_ = false; |
+ } else { |
+ os_ << ","; |
+ } |
+ std::ostringstream label; |
+ label << *node->op(); |
+ os_ << "{\"id\":" << SafeId(node) << ",\"label\":\"" << Escaped(label, "\"") |
+ << "\""; |
+ IrOpcode::Value opcode = node->opcode(); |
+ if (opcode == IrOpcode::kPhi || opcode == IrOpcode::kEffectPhi) { |
+ os_ << ",\"rankInputs\":[0," << NodeProperties::FirstControlIndex(node) |
+ << "]"; |
+ os_ << ",\"rankWithInput\":[" << NodeProperties::FirstControlIndex(node) |
+ << "]"; |
+ } else if (opcode == IrOpcode::kIfTrue || opcode == IrOpcode::kIfFalse || |
+ opcode == IrOpcode::kLoop) { |
+ os_ << ",\"rankInputs\":[" << NodeProperties::FirstControlIndex(node) |
+ << "]"; |
+ } |
+ if (opcode == IrOpcode::kBranch) { |
+ os_ << ",\"rankInputs\":[0]"; |
+ } |
+ os_ << ",\"opcode\":\"" << IrOpcode::Mnemonic(node->opcode()) << "\""; |
+ os_ << ",\"control\":" << (NodeProperties::IsControl(node) ? "true" |
+ : "false"); |
+ os_ << "}"; |
+ } |
private: |
std::ostream& os_; |
- const Graph* const graph_; |
+ AllNodes all_; |
bool first_node_; |
DISALLOW_COPY_AND_ASSIGN(JSONGraphNodeWriter); |
}; |
-void JSONGraphNodeWriter::Pre(Node* node) { |
- if (first_node_) { |
- first_node_ = false; |
- } else { |
- os_ << ","; |
- } |
- std::ostringstream label; |
- label << *node->op(); |
- os_ << "{\"id\":" << node->id() << ",\"label\":\"" << Escaped(label, "\"") |
- << "\""; |
- IrOpcode::Value opcode = node->opcode(); |
- if (opcode == IrOpcode::kPhi || opcode == IrOpcode::kEffectPhi) { |
- os_ << ",\"rankInputs\":[0," << NodeProperties::FirstControlIndex(node) |
- << "]"; |
- os_ << ",\"rankWithInput\":[" << NodeProperties::FirstControlIndex(node) |
- << "]"; |
- } else if (opcode == IrOpcode::kIfTrue || opcode == IrOpcode::kIfFalse || |
- opcode == IrOpcode::kLoop) { |
- os_ << ",\"rankInputs\":[" << NodeProperties::FirstControlIndex(node) |
- << "]"; |
- } |
- if (opcode == IrOpcode::kBranch) { |
- os_ << ",\"rankInputs\":[0]"; |
- } |
- os_ << ",\"opcode\":\"" << IrOpcode::Mnemonic(node->opcode()) << "\""; |
- os_ << ",\"control\":" << (NodeProperties::IsControl(node) ? "true" |
- : "false"); |
- os_ << "}"; |
-} |
- |
- |
-class JSONGraphEdgeWriter : public NullNodeVisitor { |
+class JSONGraphEdgeWriter { |
public: |
- JSONGraphEdgeWriter(std::ostream& os, Zone* zone, |
- const Graph* graph) // NOLINT |
- : os_(os), |
- graph_(graph), |
- first_edge_(true) {} |
+ JSONGraphEdgeWriter(std::ostream& os, Zone* zone, const Graph* graph) |
+ : os_(os), all_(zone, graph), first_edge_(true) {} |
- void Print() { const_cast<Graph*>(graph_)->VisitNodeInputsFromEnd(this); } |
+ void Print() { |
+ for (Node* const node : all_.live) PrintEdges(node); |
+ } |
- void PreEdge(Node* from, int index, Node* to); |
+ void PrintEdges(Node* node) { |
+ for (int i = 0; i < node->InputCount(); i++) { |
+ Node* input = node->InputAt(i); |
+ if (input == NULL) continue; |
+ PrintEdge(node, i, input); |
+ } |
+ } |
+ |
+ void PrintEdge(Node* from, int index, Node* to) { |
+ if (first_edge_) { |
+ first_edge_ = false; |
+ } else { |
+ os_ << ","; |
+ } |
+ const char* edge_type = NULL; |
+ if (index < NodeProperties::FirstValueIndex(from)) { |
+ edge_type = "unknown"; |
+ } else if (index < NodeProperties::FirstContextIndex(from)) { |
+ edge_type = "value"; |
+ } else if (index < NodeProperties::FirstFrameStateIndex(from)) { |
+ edge_type = "context"; |
+ } else if (index < NodeProperties::FirstEffectIndex(from)) { |
+ edge_type = "frame-state"; |
+ } else if (index < NodeProperties::FirstControlIndex(from)) { |
+ edge_type = "effect"; |
+ } else { |
+ edge_type = "control"; |
+ } |
+ os_ << "{\"source\":" << SafeId(to) << ",\"target\":" << SafeId(from) |
+ << ",\"index\":" << index << ",\"type\":\"" << edge_type << "\"}"; |
+ } |
private: |
std::ostream& os_; |
- const Graph* const graph_; |
+ AllNodes all_; |
bool first_edge_; |
DISALLOW_COPY_AND_ASSIGN(JSONGraphEdgeWriter); |
}; |
-void JSONGraphEdgeWriter::PreEdge(Node* from, int index, Node* to) { |
- if (first_edge_) { |
- first_edge_ = false; |
- } else { |
- os_ << ","; |
- } |
- const char* edge_type = NULL; |
- if (index < NodeProperties::FirstValueIndex(from)) { |
- edge_type = "unknown"; |
- } else if (index < NodeProperties::FirstContextIndex(from)) { |
- edge_type = "value"; |
- } else if (index < NodeProperties::FirstFrameStateIndex(from)) { |
- edge_type = "context"; |
- } else if (index < NodeProperties::FirstEffectIndex(from)) { |
- edge_type = "frame-state"; |
- } else if (index < NodeProperties::FirstControlIndex(from)) { |
- edge_type = "effect"; |
- } else { |
- edge_type = "control"; |
- } |
- os_ << "{\"source\":" << to->id() << ",\"target\":" << from->id() |
- << ",\"index\":" << index << ",\"type\":\"" << edge_type << "\"}"; |
-} |
- |
- |
std::ostream& operator<<(std::ostream& os, const AsJSON& ad) { |
Zone tmp_zone(ad.graph.zone()->isolate()); |
os << "{\"nodes\":["; |
@@ -165,22 +217,19 @@ std::ostream& operator<<(std::ostream& os, const AsJSON& ad) { |
} |
-class GraphVisualizer : public NullNodeVisitor { |
+class GraphVisualizer { |
public: |
GraphVisualizer(std::ostream& os, Zone* zone, const Graph* graph); // NOLINT |
void Print(); |
- void Pre(Node* node); |
+ void PrintNode(Node* node, bool gray); |
private: |
- void AnnotateNode(Node* node); |
void PrintEdge(Node::Edge edge); |
Zone* zone_; |
- NodeSet all_nodes_; |
- NodeSet white_nodes_; |
- bool use_to_def_; |
+ AllNodes all_; |
std::ostream& os_; |
const Graph* const graph_; |
@@ -193,48 +242,22 @@ static Node* GetControlCluster(Node* node) { |
return node; |
} else if (node->op()->ControlInputCount() == 1) { |
Node* control = NodeProperties::GetControlInput(node, 0); |
- return OperatorProperties::IsBasicBlockBegin(control->op()) ? control |
- : NULL; |
+ return control != NULL && |
+ OperatorProperties::IsBasicBlockBegin(control->op()) |
+ ? control |
+ : NULL; |
} else { |
return NULL; |
} |
} |
-void GraphVisualizer::Pre(Node* node) { |
- if (all_nodes_.count(node) == 0) { |
- Node* control_cluster = GetControlCluster(node); |
- if (control_cluster != NULL) { |
- os_ << " subgraph cluster_BasicBlock" << control_cluster->id() << " {\n"; |
- } |
- os_ << " ID" << node->id() << " [\n"; |
- AnnotateNode(node); |
- os_ << " ]\n"; |
- if (control_cluster != NULL) os_ << " }\n"; |
- all_nodes_.insert(node); |
- if (use_to_def_) white_nodes_.insert(node); |
- } |
-} |
- |
- |
-static bool IsLikelyBackEdge(Node* from, int index, Node* to) { |
- if (from->opcode() == IrOpcode::kPhi || |
- from->opcode() == IrOpcode::kEffectPhi) { |
- Node* control = NodeProperties::GetControlInput(from, 0); |
- return control->opcode() != IrOpcode::kMerge && control != to && index != 0; |
- } else if (from->opcode() == IrOpcode::kLoop) { |
- return index != 0; |
- } else { |
- return false; |
- } |
-} |
- |
- |
-void GraphVisualizer::AnnotateNode(Node* node) { |
- if (!use_to_def_) { |
- os_ << " style=\"filled\"\n" |
- << " fillcolor=\"" DEAD_COLOR "\"\n"; |
+void GraphVisualizer::PrintNode(Node* node, bool gray) { |
+ Node* control_cluster = GetControlCluster(node); |
+ if (control_cluster != NULL) { |
+ os_ << " subgraph cluster_BasicBlock" << control_cluster->id() << " {\n"; |
} |
+ os_ << " ID" << SafeId(node) << " [\n"; |
os_ << " shape=\"record\"\n"; |
switch (node->opcode()) { |
@@ -253,30 +276,35 @@ void GraphVisualizer::AnnotateNode(Node* node) { |
break; |
} |
+ if (gray) { |
+ os_ << " style=\"filled\"\n" |
+ << " fillcolor=\"" DEAD_COLOR "\"\n"; |
+ } |
+ |
std::ostringstream label; |
label << *node->op(); |
- os_ << " label=\"{{#" << node->id() << ":" << Escaped(label); |
+ os_ << " label=\"{{#" << SafeId(node) << ":" << Escaped(label); |
InputIter i = node->inputs().begin(); |
for (int j = node->op()->ValueInputCount(); j > 0; ++i, j--) { |
- os_ << "|<I" << i.index() << ">#" << (*i)->id(); |
+ os_ << "|<I" << i.index() << ">#" << SafeId(*i); |
} |
for (int j = OperatorProperties::GetContextInputCount(node->op()); j > 0; |
++i, j--) { |
- os_ << "|<I" << i.index() << ">X #" << (*i)->id(); |
+ os_ << "|<I" << i.index() << ">X #" << SafeId(*i); |
} |
for (int j = OperatorProperties::GetFrameStateInputCount(node->op()); j > 0; |
++i, j--) { |
- os_ << "|<I" << i.index() << ">F #" << (*i)->id(); |
+ os_ << "|<I" << i.index() << ">F #" << SafeId(*i); |
} |
for (int j = node->op()->EffectInputCount(); j > 0; ++i, j--) { |
- os_ << "|<I" << i.index() << ">E #" << (*i)->id(); |
+ os_ << "|<I" << i.index() << ">E #" << SafeId(*i); |
} |
- if (!use_to_def_ || OperatorProperties::IsBasicBlockBegin(node->op()) || |
+ if (OperatorProperties::IsBasicBlockBegin(node->op()) || |
GetControlCluster(node) == NULL) { |
for (int j = node->op()->ControlInputCount(); j > 0; ++i, j--) { |
- os_ << "|<I" << i.index() << ">C #" << (*i)->id(); |
+ os_ << "|<I" << i.index() << ">C #" << SafeId(*i); |
} |
} |
os_ << "}"; |
@@ -290,6 +318,23 @@ void GraphVisualizer::AnnotateNode(Node* node) { |
os_ << "|" << Escaped(upper) << "|" << Escaped(lower); |
} |
os_ << "}\"\n"; |
+ |
+ os_ << " ]\n"; |
+ if (control_cluster != NULL) os_ << " }\n"; |
+} |
+ |
+ |
+static bool IsLikelyBackEdge(Node* from, int index, Node* to) { |
+ if (from->opcode() == IrOpcode::kPhi || |
+ from->opcode() == IrOpcode::kEffectPhi) { |
+ Node* control = NodeProperties::GetControlInput(from, 0); |
+ return control != NULL && control->opcode() != IrOpcode::kMerge && |
+ control != to && index != 0; |
+ } else if (from->opcode() == IrOpcode::kLoop) { |
+ return index != 0; |
+ } else { |
+ return false; |
+ } |
} |
@@ -297,21 +342,23 @@ void GraphVisualizer::PrintEdge(Node::Edge edge) { |
Node* from = edge.from(); |
int index = edge.index(); |
Node* to = edge.to(); |
+ |
+ if (!all_.IsLive(to)) return; // skip inputs that point to dead or NULL. |
+ |
bool unconstrained = IsLikelyBackEdge(from, index, to); |
- os_ << " ID" << from->id(); |
- if (all_nodes_.count(to) == 0) { |
- os_ << ":I" << index << ":n -> DEAD_INPUT"; |
- } else if (OperatorProperties::IsBasicBlockBegin(from->op()) || |
- GetControlCluster(from) == NULL || |
- (from->op()->ControlInputCount() > 0 && |
- NodeProperties::GetControlInput(from) != to)) { |
- os_ << ":I" << index << ":n -> ID" << to->id() << ":s" |
+ os_ << " ID" << SafeId(from); |
+ |
+ if (OperatorProperties::IsBasicBlockBegin(from->op()) || |
+ GetControlCluster(from) == NULL || |
+ (from->op()->ControlInputCount() > 0 && |
+ NodeProperties::GetControlInput(from) != to)) { |
+ os_ << ":I" << index << ":n -> ID" << SafeId(to) << ":s" |
<< "[" << (unconstrained ? "constraint=false, " : "") |
<< (NodeProperties::IsControlEdge(edge) ? "style=bold, " : "") |
<< (NodeProperties::IsEffectEdge(edge) ? "style=dotted, " : "") |
<< (NodeProperties::IsContextEdge(edge) ? "style=dashed, " : "") << "]"; |
} else { |
- os_ << " -> ID" << to->id() << ":s [color=transparent, " |
+ os_ << " -> ID" << SafeId(to) << ":s [color=transparent, " |
<< (unconstrained ? "constraint=false, " : "") |
<< (NodeProperties::IsControlEdge(edge) ? "style=dashed, " : "") << "]"; |
} |
@@ -330,29 +377,13 @@ void GraphVisualizer::Print() { |
<< " \n"; |
// Make sure all nodes have been output before writing out the edges. |
- use_to_def_ = true; |
- // TODO(svenpanne) Remove the need for the const_casts. |
- const_cast<Graph*>(graph_)->VisitNodeInputsFromEnd(this); |
- white_nodes_.insert(const_cast<Graph*>(graph_)->start()); |
- |
- // Visit all uses of white nodes. |
- use_to_def_ = false; |
- GenericGraphVisit::Visit<GraphVisualizer, NodeUseIterationTraits<Node> >( |
- const_cast<Graph*>(graph_), zone_, white_nodes_.begin(), |
- white_nodes_.end(), this); |
- |
- os_ << " DEAD_INPUT [\n" |
- << " style=\"filled\" \n" |
- << " fillcolor=\"" DEAD_COLOR "\"\n" |
- << " ]\n" |
- << "\n"; |
+ for (Node* const node : all_.live) PrintNode(node, false); |
+ for (Node* const node : all_.gray) PrintNode(node, true); |
// With all the nodes written, add the edges. |
- for (NodeSetIter i = all_nodes_.begin(); i != all_nodes_.end(); ++i) { |
- Node::Inputs inputs = (*i)->inputs(); |
- for (Node::Inputs::iterator iter(inputs.begin()); iter != inputs.end(); |
- ++iter) { |
- PrintEdge(iter.edge()); |
+ for (Node* const node : all_.live) { |
+ for (UseIter i = node->uses().begin(); i != node->uses().end(); ++i) { |
+ PrintEdge(i.edge()); |
} |
} |
os_ << "}\n"; |
@@ -362,9 +393,7 @@ void GraphVisualizer::Print() { |
GraphVisualizer::GraphVisualizer(std::ostream& os, Zone* zone, |
const Graph* graph) // NOLINT |
: zone_(zone), |
- all_nodes_(NodeSet::key_compare(), NodeSet::allocator_type(zone)), |
- white_nodes_(NodeSet::key_compare(), NodeSet::allocator_type(zone)), |
- use_to_def_(true), |
+ all_(zone, graph), |
os_(os), |
graph_(graph) {} |
@@ -485,7 +514,7 @@ void GraphC1Visualizer::PrintCompilation(const CompilationInfo* info) { |
} |
-void GraphC1Visualizer::PrintNodeId(Node* n) { os_ << "n" << n->id(); } |
+void GraphC1Visualizer::PrintNodeId(Node* n) { os_ << "n" << SafeId(n); } |
void GraphC1Visualizer::PrintNode(Node* n) { |