Index: src/compiler/graph-visualizer.cc |
diff --git a/src/compiler/graph-visualizer.cc b/src/compiler/graph-visualizer.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..032d3d95c6bb1a72e971f64f9f534b32180e2a72 |
--- /dev/null |
+++ b/src/compiler/graph-visualizer.cc |
@@ -0,0 +1,260 @@ |
+// Copyright 2013 the V8 project authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "src/compiler/graph-visualizer.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" |
+#include "src/compiler/graph-inl.h" |
+#include "src/compiler/node.h" |
+#include "src/compiler/node-properties.h" |
+#include "src/compiler/node-properties-inl.h" |
+#include "src/compiler/opcodes.h" |
+#include "src/compiler/operator.h" |
+#include "src/ostreams.h" |
+ |
+namespace v8 { |
+namespace internal { |
+namespace compiler { |
+ |
+#define DEAD_COLOR "#999999" |
+ |
+class GraphVisualizer : public NullNodeVisitor { |
+ public: |
+ GraphVisualizer(OStream& os, const Graph* graph); // NOLINT |
+ |
+ void Print(); |
+ |
+ GenericGraphVisit::Control Pre(Node* node); |
+ GenericGraphVisit::Control PreEdge(Node* from, int index, Node* to); |
+ |
+ private: |
+ void AnnotateNode(Node* node); |
+ void PrintEdge(Node* from, int index, Node* to); |
+ |
+ NodeSet all_nodes_; |
+ NodeSet white_nodes_; |
+ bool use_to_def_; |
+ OStream& os_; |
+ const Graph* const graph_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(GraphVisualizer); |
+}; |
+ |
+ |
+static Node* GetControlCluster(Node* node) { |
+ if (NodeProperties::IsBasicBlockBegin(node)) { |
+ return node; |
+ } else if (NodeProperties::GetControlInputCount(node) == 1) { |
+ Node* control = NodeProperties::GetControlInput(node, 0); |
+ return NodeProperties::IsBasicBlockBegin(control) ? control : NULL; |
+ } else { |
+ return NULL; |
+ } |
+} |
+ |
+ |
+GenericGraphVisit::Control 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); |
+ } |
+ return GenericGraphVisit::CONTINUE; |
+} |
+ |
+ |
+GenericGraphVisit::Control GraphVisualizer::PreEdge(Node* from, int index, |
+ Node* to) { |
+ if (use_to_def_) return GenericGraphVisit::CONTINUE; |
+ // When going from def to use, only consider white -> other edges, which are |
+ // the dead nodes that use live nodes. We're probably not interested in |
+ // dead nodes that only use other dead nodes. |
+ if (white_nodes_.count(from) > 0) return GenericGraphVisit::CONTINUE; |
+ return GenericGraphVisit::SKIP; |
+} |
+ |
+ |
+class Escaped { |
+ public: |
+ explicit Escaped(const OStringStream& os) : str_(os.c_str()) {} |
+ |
+ friend OStream& operator<<(OStream& os, const Escaped& e) { |
+ for (const char* s = e.str_; *s != '\0'; ++s) { |
+ if (needs_escape(*s)) os << "\\"; |
+ os << *s; |
+ } |
+ return os; |
+ } |
+ |
+ private: |
+ static bool needs_escape(char ch) { |
+ switch (ch) { |
+ case '>': |
+ case '<': |
+ case '|': |
+ case '}': |
+ case '{': |
+ return true; |
+ default: |
+ return false; |
+ } |
+ } |
+ |
+ const char* const str_; |
+}; |
+ |
+ |
+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"; |
+ } |
+ |
+ os_ << " shape=\"record\"\n"; |
+ switch (node->opcode()) { |
+ case IrOpcode::kEnd: |
+ case IrOpcode::kDead: |
+ case IrOpcode::kStart: |
+ os_ << " style=\"diagonals\"\n"; |
+ break; |
+ case IrOpcode::kMerge: |
+ case IrOpcode::kIfTrue: |
+ case IrOpcode::kIfFalse: |
+ case IrOpcode::kLoop: |
+ os_ << " style=\"rounded\"\n"; |
+ break; |
+ default: |
+ break; |
+ } |
+ |
+ OStringStream label; |
+ label << *node->op(); |
+ os_ << " label=\"{{#" << node->id() << ":" << Escaped(label); |
+ |
+ InputIter i = node->inputs().begin(); |
+ for (int j = NodeProperties::GetValueInputCount(node); j > 0; ++i, j--) { |
+ os_ << "|<I" << i.index() << ">#" << (*i)->id(); |
+ } |
+ for (int j = NodeProperties::GetContextInputCount(node); j > 0; ++i, j--) { |
+ os_ << "|<I" << i.index() << ">X #" << (*i)->id(); |
+ } |
+ for (int j = NodeProperties::GetEffectInputCount(node); j > 0; ++i, j--) { |
+ os_ << "|<I" << i.index() << ">E #" << (*i)->id(); |
+ } |
+ |
+ if (!use_to_def_ || NodeProperties::IsBasicBlockBegin(node) || |
+ GetControlCluster(node) == NULL) { |
+ for (int j = NodeProperties::GetControlInputCount(node); j > 0; ++i, j--) { |
+ os_ << "|<I" << i.index() << ">C #" << (*i)->id(); |
+ } |
+ } |
+ os_ << "}"; |
+ |
+ if (FLAG_trace_turbo_types && !NodeProperties::IsControl(node)) { |
+ Bounds bounds = NodeProperties::GetBounds(node); |
+ OStringStream upper; |
+ bounds.upper->PrintTo(upper); |
+ OStringStream lower; |
+ bounds.lower->PrintTo(lower); |
+ os_ << "|" << Escaped(upper) << "|" << Escaped(lower); |
+ } |
+ os_ << "}\"\n"; |
+} |
+ |
+ |
+void GraphVisualizer::PrintEdge(Node* from, int index, Node* to) { |
+ bool unconstrained = IsLikelyBackEdge(from, index, to); |
+ os_ << " ID" << from->id(); |
+ if (all_nodes_.count(to) == 0) { |
+ os_ << ":I" << index << ":n -> DEAD_INPUT"; |
+ } else if (NodeProperties::IsBasicBlockBegin(from) || |
+ GetControlCluster(from) == NULL || |
+ (NodeProperties::GetControlInputCount(from) > 0 && |
+ NodeProperties::GetControlInput(from) != to)) { |
+ os_ << ":I" << index << ":n -> ID" << to->id() << ":s"; |
+ if (unconstrained) os_ << " [constraint=false,style=dotted]"; |
+ } else { |
+ os_ << " -> ID" << to->id() << ":s [color=transparent" |
+ << (unconstrained ? ", constraint=false" : "") << "]"; |
+ } |
+ os_ << "\n"; |
+} |
+ |
+ |
+void GraphVisualizer::Print() { |
+ os_ << "digraph D {\n" |
+ << " node [fontsize=8,height=0.25]\n" |
+ << " rankdir=\"BT\"\n" |
+ << " \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_), white_nodes_.begin(), white_nodes_.end(), |
+ this); |
+ |
+ os_ << " DEAD_INPUT [\n" |
+ << " style=\"filled\" \n" |
+ << " fillcolor=\"" DEAD_COLOR "\"\n" |
+ << " ]\n" |
+ << "\n"; |
+ |
+ // 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().from(), iter.edge().index(), iter.edge().to()); |
+ } |
+ } |
+ os_ << "}\n"; |
+} |
+ |
+ |
+GraphVisualizer::GraphVisualizer(OStream& os, const Graph* graph) // NOLINT |
+ : all_nodes_(NodeSet::key_compare(), |
+ NodeSet::allocator_type(graph->zone())), |
+ white_nodes_(NodeSet::key_compare(), |
+ NodeSet::allocator_type(graph->zone())), |
+ use_to_def_(true), |
+ os_(os), |
+ graph_(graph) {} |
+ |
+ |
+OStream& operator<<(OStream& os, const AsDOT& ad) { |
+ GraphVisualizer(os, &ad.graph).Print(); |
+ return os; |
+} |
+} |
+} |
+} // namespace v8::internal::compiler |