1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "src/compiler/graph-visualizer.h"
7 #include "src/compiler/generic-algorithm.h"
8 #include "src/compiler/generic-node.h"
9 #include "src/compiler/generic-node-inl.h"
10 #include "src/compiler/graph.h"
11 #include "src/compiler/graph-inl.h"
12 #include "src/compiler/node.h"
13 #include "src/compiler/node-properties.h"
14 #include "src/compiler/node-properties-inl.h"
15 #include "src/compiler/opcodes.h"
16 #include "src/compiler/operator.h"
17 #include "src/ostreams.h"
23 #define DEAD_COLOR "#999999"
25 class GraphVisualizer : public NullNodeVisitor {
27 GraphVisualizer(OStream& os, const Graph* graph); // NOLINT
31 GenericGraphVisit::Control Pre(Node* node);
32 GenericGraphVisit::Control PreEdge(Node* from, int index, Node* to);
35 void AnnotateNode(Node* node);
36 void PrintEdge(Node* from, int index, Node* to);
42 const Graph* const graph_;
44 DISALLOW_COPY_AND_ASSIGN(GraphVisualizer);
48 static Node* GetControlCluster(Node* node) {
49 if (OperatorProperties::IsBasicBlockBegin(node->op())) {
51 } else if (OperatorProperties::GetControlInputCount(node->op()) == 1) {
52 Node* control = NodeProperties::GetControlInput(node, 0);
53 return OperatorProperties::IsBasicBlockBegin(control->op()) ? control
61 GenericGraphVisit::Control GraphVisualizer::Pre(Node* node) {
62 if (all_nodes_.count(node) == 0) {
63 Node* control_cluster = GetControlCluster(node);
64 if (control_cluster != NULL) {
65 os_ << " subgraph cluster_BasicBlock" << control_cluster->id() << " {\n";
67 os_ << " ID" << node->id() << " [\n";
70 if (control_cluster != NULL) os_ << " }\n";
71 all_nodes_.insert(node);
72 if (use_to_def_) white_nodes_.insert(node);
74 return GenericGraphVisit::CONTINUE;
78 GenericGraphVisit::Control GraphVisualizer::PreEdge(Node* from, int index,
80 if (use_to_def_) return GenericGraphVisit::CONTINUE;
81 // When going from def to use, only consider white -> other edges, which are
82 // the dead nodes that use live nodes. We're probably not interested in
83 // dead nodes that only use other dead nodes.
84 if (white_nodes_.count(from) > 0) return GenericGraphVisit::CONTINUE;
85 return GenericGraphVisit::SKIP;
91 explicit Escaped(const OStringStream& os) : str_(os.c_str()) {}
93 friend OStream& operator<<(OStream& os, const Escaped& e) {
94 for (const char* s = e.str_; *s != '\0'; ++s) {
95 if (needs_escape(*s)) os << "\\";
102 static bool needs_escape(char ch) {
115 const char* const str_;
119 static bool IsLikelyBackEdge(Node* from, int index, Node* to) {
120 if (from->opcode() == IrOpcode::kPhi ||
121 from->opcode() == IrOpcode::kEffectPhi) {
122 Node* control = NodeProperties::GetControlInput(from, 0);
123 return control->opcode() != IrOpcode::kMerge && control != to && index != 0;
124 } else if (from->opcode() == IrOpcode::kLoop) {
132 void GraphVisualizer::AnnotateNode(Node* node) {
134 os_ << " style=\"filled\"\n"
135 << " fillcolor=\"" DEAD_COLOR "\"\n";
138 os_ << " shape=\"record\"\n";
139 switch (node->opcode()) {
141 case IrOpcode::kDead:
142 case IrOpcode::kStart:
143 os_ << " style=\"diagonals\"\n";
145 case IrOpcode::kMerge:
146 case IrOpcode::kIfTrue:
147 case IrOpcode::kIfFalse:
148 case IrOpcode::kLoop:
149 os_ << " style=\"rounded\"\n";
156 label << *node->op();
157 os_ << " label=\"{{#" << node->id() << ":" << Escaped(label);
159 InputIter i = node->inputs().begin();
160 for (int j = OperatorProperties::GetValueInputCount(node->op()); j > 0;
162 os_ << "|<I" << i.index() << ">#" << (*i)->id();
164 for (int j = OperatorProperties::GetContextInputCount(node->op()); j > 0;
166 os_ << "|<I" << i.index() << ">X #" << (*i)->id();
168 for (int j = OperatorProperties::GetEffectInputCount(node->op()); j > 0;
170 os_ << "|<I" << i.index() << ">E #" << (*i)->id();
173 if (!use_to_def_ || OperatorProperties::IsBasicBlockBegin(node->op()) ||
174 GetControlCluster(node) == NULL) {
175 for (int j = OperatorProperties::GetControlInputCount(node->op()); j > 0;
177 os_ << "|<I" << i.index() << ">C #" << (*i)->id();
182 if (FLAG_trace_turbo_types && !NodeProperties::IsControl(node)) {
183 Bounds bounds = NodeProperties::GetBounds(node);
185 bounds.upper->PrintTo(upper);
187 bounds.lower->PrintTo(lower);
188 os_ << "|" << Escaped(upper) << "|" << Escaped(lower);
194 void GraphVisualizer::PrintEdge(Node* from, int index, Node* to) {
195 bool unconstrained = IsLikelyBackEdge(from, index, to);
196 os_ << " ID" << from->id();
197 if (all_nodes_.count(to) == 0) {
198 os_ << ":I" << index << ":n -> DEAD_INPUT";
199 } else if (OperatorProperties::IsBasicBlockBegin(from->op()) ||
200 GetControlCluster(from) == NULL ||
201 (OperatorProperties::GetControlInputCount(from->op()) > 0 &&
202 NodeProperties::GetControlInput(from) != to)) {
203 os_ << ":I" << index << ":n -> ID" << to->id() << ":s";
204 if (unconstrained) os_ << " [constraint=false,style=dotted]";
206 os_ << " -> ID" << to->id() << ":s [color=transparent"
207 << (unconstrained ? ", constraint=false" : "") << "]";
213 void GraphVisualizer::Print() {
214 os_ << "digraph D {\n"
215 << " node [fontsize=8,height=0.25]\n"
216 << " rankdir=\"BT\"\n"
219 // Make sure all nodes have been output before writing out the edges.
221 // TODO(svenpanne) Remove the need for the const_casts.
222 const_cast<Graph*>(graph_)->VisitNodeInputsFromEnd(this);
223 white_nodes_.insert(const_cast<Graph*>(graph_)->start());
225 // Visit all uses of white nodes.
227 GenericGraphVisit::Visit<GraphVisualizer, NodeUseIterationTraits<Node> >(
228 const_cast<Graph*>(graph_), white_nodes_.begin(), white_nodes_.end(),
231 os_ << " DEAD_INPUT [\n"
232 << " style=\"filled\" \n"
233 << " fillcolor=\"" DEAD_COLOR "\"\n"
237 // With all the nodes written, add the edges.
238 for (NodeSetIter i = all_nodes_.begin(); i != all_nodes_.end(); ++i) {
239 Node::Inputs inputs = (*i)->inputs();
240 for (Node::Inputs::iterator iter(inputs.begin()); iter != inputs.end();
242 PrintEdge(iter.edge().from(), iter.edge().index(), iter.edge().to());
249 GraphVisualizer::GraphVisualizer(OStream& os, const Graph* graph) // NOLINT
250 : all_nodes_(NodeSet::key_compare(),
251 NodeSet::allocator_type(graph->zone())),
252 white_nodes_(NodeSet::key_compare(),
253 NodeSet::allocator_type(graph->zone())),
259 OStream& operator<<(OStream& os, const AsDOT& ad) {
260 GraphVisualizer(os, &ad.graph).Print();
265 } // namespace v8::internal::compiler