V8 Project
graph-visualizer.cc
Go to the documentation of this file.
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.
4 
6 
10 #include "src/compiler/graph.h"
11 #include "src/compiler/graph-inl.h"
12 #include "src/compiler/node.h"
15 #include "src/compiler/opcodes.h"
16 #include "src/compiler/operator.h"
17 #include "src/ostreams.h"
18 
19 namespace v8 {
20 namespace internal {
21 namespace compiler {
22 
23 #define DEAD_COLOR "#999999"
24 
25 class Escaped {
26  public:
27  explicit Escaped(const OStringStream& os, const char* escaped_chars = "<>|{}")
28  : str_(os.c_str()), escaped_chars_(escaped_chars) {}
29 
30  friend OStream& operator<<(OStream& os, const Escaped& e) {
31  for (const char* s = e.str_; *s != '\0'; ++s) {
32  if (e.needs_escape(*s)) os << "\\";
33  os << *s;
34  }
35  return os;
36  }
37 
38  private:
39  bool needs_escape(char ch) const {
40  for (size_t i = 0; i < strlen(escaped_chars_); ++i) {
41  if (ch == escaped_chars_[i]) return true;
42  }
43  return false;
44  }
45 
46  const char* const str_;
47  const char* const escaped_chars_;
48 };
49 
51  public:
52  JSONGraphNodeWriter(OStream& os, Zone* zone, const Graph* graph) // NOLINT
53  : os_(os),
54  graph_(graph),
55  first_node_(true) {}
56 
57  void Print() { const_cast<Graph*>(graph_)->VisitNodeInputsFromEnd(this); }
58 
59  GenericGraphVisit::Control Pre(Node* node);
60 
61  private:
63  const Graph* const graph_;
65 
67 };
68 
69 
71  if (first_node_) {
72  first_node_ = false;
73  } else {
74  os_ << ",";
75  }
76  OStringStream label;
77  label << *node->op();
78  os_ << "{\"id\":" << node->id() << ",\"label\":\"" << Escaped(label, "\"")
79  << "\"";
80  IrOpcode::Value opcode = node->opcode();
81  if (opcode == IrOpcode::kPhi || opcode == IrOpcode::kEffectPhi) {
82  os_ << ",\"rankInputs\":[0," << NodeProperties::FirstControlIndex(node)
83  << "]";
84  os_ << ",\"rankWithInput\":[" << NodeProperties::FirstControlIndex(node)
85  << "]";
86  } else if (opcode == IrOpcode::kIfTrue || opcode == IrOpcode::kIfFalse ||
87  opcode == IrOpcode::kLoop) {
88  os_ << ",\"rankInputs\":[" << NodeProperties::FirstControlIndex(node)
89  << "]";
90  }
91  if (opcode == IrOpcode::kBranch) {
92  os_ << ",\"rankInputs\":[0]";
93  }
94  os_ << ",\"opcode\":\"" << IrOpcode::Mnemonic(node->opcode()) << "\"";
95  os_ << ",\"control\":" << (NodeProperties::IsControl(node) ? "true"
96  : "false");
97  os_ << "}";
99 }
100 
101 
103  public:
104  JSONGraphEdgeWriter(OStream& os, Zone* zone, const Graph* graph) // NOLINT
105  : os_(os),
106  graph_(graph),
107  first_edge_(true) {}
108 
109  void Print() { const_cast<Graph*>(graph_)->VisitNodeInputsFromEnd(this); }
110 
111  GenericGraphVisit::Control PreEdge(Node* from, int index, Node* to);
112 
113  private:
115  const Graph* const graph_;
117 
119 };
120 
121 
123  Node* to) {
124  if (first_edge_) {
125  first_edge_ = false;
126  } else {
127  os_ << ",";
128  }
129  const char* edge_type = NULL;
130  if (index < NodeProperties::FirstValueIndex(from)) {
131  edge_type = "unknown";
132  } else if (index < NodeProperties::FirstContextIndex(from)) {
133  edge_type = "value";
134  } else if (index < NodeProperties::FirstFrameStateIndex(from)) {
135  edge_type = "context";
136  } else if (index < NodeProperties::FirstEffectIndex(from)) {
137  edge_type = "frame-state";
138  } else if (index < NodeProperties::FirstControlIndex(from)) {
139  edge_type = "effect";
140  } else {
141  edge_type = "control";
142  }
143  os_ << "{\"source\":" << to->id() << ",\"target\":" << from->id()
144  << ",\"index\":" << index << ",\"type\":\"" << edge_type << "\"}";
146 }
147 
148 
149 OStream& operator<<(OStream& os, const AsJSON& ad) {
150  Zone tmp_zone(ad.graph.zone()->isolate());
151  os << "{\"nodes\":[";
152  JSONGraphNodeWriter(os, &tmp_zone, &ad.graph).Print();
153  os << "],\"edges\":[";
154  JSONGraphEdgeWriter(os, &tmp_zone, &ad.graph).Print();
155  os << "]}";
156  return os;
157 }
158 
159 
161  public:
162  GraphVisualizer(OStream& os, Zone* zone, const Graph* graph); // NOLINT
163 
164  void Print();
165 
166  GenericGraphVisit::Control Pre(Node* node);
167  GenericGraphVisit::Control PreEdge(Node* from, int index, Node* to);
168 
169  private:
170  void AnnotateNode(Node* node);
171  void PrintEdge(Node::Edge edge);
172 
178  const Graph* const graph_;
179 
181 };
182 
183 
184 static Node* GetControlCluster(Node* node) {
185  if (OperatorProperties::IsBasicBlockBegin(node->op())) {
186  return node;
187  } else if (OperatorProperties::GetControlInputCount(node->op()) == 1) {
188  Node* control = NodeProperties::GetControlInput(node, 0);
189  return OperatorProperties::IsBasicBlockBegin(control->op()) ? control
190  : NULL;
191  } else {
192  return NULL;
193  }
194 }
195 
196 
198  if (all_nodes_.count(node) == 0) {
199  Node* control_cluster = GetControlCluster(node);
200  if (control_cluster != NULL) {
201  os_ << " subgraph cluster_BasicBlock" << control_cluster->id() << " {\n";
202  }
203  os_ << " ID" << node->id() << " [\n";
204  AnnotateNode(node);
205  os_ << " ]\n";
206  if (control_cluster != NULL) os_ << " }\n";
207  all_nodes_.insert(node);
208  if (use_to_def_) white_nodes_.insert(node);
209  }
211 }
212 
213 
215  Node* to) {
217  // When going from def to use, only consider white -> other edges, which are
218  // the dead nodes that use live nodes. We're probably not interested in
219  // dead nodes that only use other dead nodes.
220  if (white_nodes_.count(from) > 0) return GenericGraphVisit::CONTINUE;
222 }
223 
224 
225 static bool IsLikelyBackEdge(Node* from, int index, Node* to) {
226  if (from->opcode() == IrOpcode::kPhi ||
227  from->opcode() == IrOpcode::kEffectPhi) {
228  Node* control = NodeProperties::GetControlInput(from, 0);
229  return control->opcode() != IrOpcode::kMerge && control != to && index != 0;
230  } else if (from->opcode() == IrOpcode::kLoop) {
231  return index != 0;
232  } else {
233  return false;
234  }
235 }
236 
237 
239  if (!use_to_def_) {
240  os_ << " style=\"filled\"\n"
241  << " fillcolor=\"" DEAD_COLOR "\"\n";
242  }
243 
244  os_ << " shape=\"record\"\n";
245  switch (node->opcode()) {
246  case IrOpcode::kEnd:
247  case IrOpcode::kDead:
248  case IrOpcode::kStart:
249  os_ << " style=\"diagonals\"\n";
250  break;
251  case IrOpcode::kMerge:
252  case IrOpcode::kIfTrue:
253  case IrOpcode::kIfFalse:
254  case IrOpcode::kLoop:
255  os_ << " style=\"rounded\"\n";
256  break;
257  default:
258  break;
259  }
260 
261  OStringStream label;
262  label << *node->op();
263  os_ << " label=\"{{#" << node->id() << ":" << Escaped(label);
264 
265  InputIter i = node->inputs().begin();
266  for (int j = OperatorProperties::GetValueInputCount(node->op()); j > 0;
267  ++i, j--) {
268  os_ << "|<I" << i.index() << ">#" << (*i)->id();
269  }
270  for (int j = OperatorProperties::GetContextInputCount(node->op()); j > 0;
271  ++i, j--) {
272  os_ << "|<I" << i.index() << ">X #" << (*i)->id();
273  }
274  for (int j = OperatorProperties::GetFrameStateInputCount(node->op()); j > 0;
275  ++i, j--) {
276  os_ << "|<I" << i.index() << ">F #" << (*i)->id();
277  }
278  for (int j = OperatorProperties::GetEffectInputCount(node->op()); j > 0;
279  ++i, j--) {
280  os_ << "|<I" << i.index() << ">E #" << (*i)->id();
281  }
282 
284  GetControlCluster(node) == NULL) {
285  for (int j = OperatorProperties::GetControlInputCount(node->op()); j > 0;
286  ++i, j--) {
287  os_ << "|<I" << i.index() << ">C #" << (*i)->id();
288  }
289  }
290  os_ << "}";
291 
292  if (FLAG_trace_turbo_types && !NodeProperties::IsControl(node)) {
293  Bounds bounds = NodeProperties::GetBounds(node);
294  OStringStream upper;
295  bounds.upper->PrintTo(upper);
296  OStringStream lower;
297  bounds.lower->PrintTo(lower);
298  os_ << "|" << Escaped(upper) << "|" << Escaped(lower);
299  }
300  os_ << "}\"\n";
301 }
302 
303 
304 void GraphVisualizer::PrintEdge(Node::Edge edge) {
305  Node* from = edge.from();
306  int index = edge.index();
307  Node* to = edge.to();
308  bool unconstrained = IsLikelyBackEdge(from, index, to);
309  os_ << " ID" << from->id();
310  if (all_nodes_.count(to) == 0) {
311  os_ << ":I" << index << ":n -> DEAD_INPUT";
312  } else if (OperatorProperties::IsBasicBlockBegin(from->op()) ||
313  GetControlCluster(from) == NULL ||
314  (OperatorProperties::GetControlInputCount(from->op()) > 0 &&
316  os_ << ":I" << index << ":n -> ID" << to->id() << ":s"
317  << "[" << (unconstrained ? "constraint=false, " : "")
318  << (NodeProperties::IsControlEdge(edge) ? "style=bold, " : "")
319  << (NodeProperties::IsEffectEdge(edge) ? "style=dotted, " : "")
320  << (NodeProperties::IsContextEdge(edge) ? "style=dashed, " : "") << "]";
321  } else {
322  os_ << " -> ID" << to->id() << ":s [color=transparent, "
323  << (unconstrained ? "constraint=false, " : "")
324  << (NodeProperties::IsControlEdge(edge) ? "style=dashed, " : "") << "]";
325  }
326  os_ << "\n";
327 }
328 
329 
331  os_ << "digraph D {\n"
332  << " node [fontsize=8,height=0.25]\n"
333  << " rankdir=\"BT\"\n"
334  << " ranksep=\"1.2 equally\"\n"
335  << " overlap=\"false\"\n"
336  << " splines=\"true\"\n"
337  << " concentrate=\"true\"\n"
338  << " \n";
339 
340  // Make sure all nodes have been output before writing out the edges.
341  use_to_def_ = true;
342  // TODO(svenpanne) Remove the need for the const_casts.
343  const_cast<Graph*>(graph_)->VisitNodeInputsFromEnd(this);
344  white_nodes_.insert(const_cast<Graph*>(graph_)->start());
345 
346  // Visit all uses of white nodes.
347  use_to_def_ = false;
348  GenericGraphVisit::Visit<GraphVisualizer, NodeUseIterationTraits<Node> >(
349  const_cast<Graph*>(graph_), zone_, white_nodes_.begin(),
350  white_nodes_.end(), this);
351 
352  os_ << " DEAD_INPUT [\n"
353  << " style=\"filled\" \n"
354  << " fillcolor=\"" DEAD_COLOR "\"\n"
355  << " ]\n"
356  << "\n";
357 
358  // With all the nodes written, add the edges.
359  for (NodeSetIter i = all_nodes_.begin(); i != all_nodes_.end(); ++i) {
360  Node::Inputs inputs = (*i)->inputs();
361  for (Node::Inputs::iterator iter(inputs.begin()); iter != inputs.end();
362  ++iter) {
363  PrintEdge(iter.edge());
364  }
365  }
366  os_ << "}\n";
367 }
368 
369 
371  const Graph* graph) // NOLINT
372  : zone_(zone),
373  all_nodes_(NodeSet::key_compare(), NodeSet::allocator_type(zone)),
374  white_nodes_(NodeSet::key_compare(), NodeSet::allocator_type(zone)),
375  use_to_def_(true),
376  os_(os),
377  graph_(graph) {}
378 
379 
380 OStream& operator<<(OStream& os, const AsDOT& ad) {
381  Zone tmp_zone(ad.graph.zone()->isolate());
382  GraphVisualizer(os, &tmp_zone, &ad.graph).Print();
383  return os;
384 }
385 }
386 }
387 } // namespace v8::internal::compiler
Isolate * isolate() const
Definition: zone.h:68
Escaped(const OStringStream &os, const char *escaped_chars="<>|{}")
friend OStream & operator<<(OStream &os, const Escaped &e)
GenericGraphVisit::Control PreEdge(Node *from, int index, Node *to)
GenericGraphVisit::Control Pre(Node *node)
GraphVisualizer(OStream &os, Zone *zone, const Graph *graph)
static const char * Mnemonic(Value val)
Definition: opcodes.h:256
GenericGraphVisit::Control PreEdge(Node *from, int index, Node *to)
JSONGraphEdgeWriter(OStream &os, Zone *zone, const Graph *graph)
JSONGraphNodeWriter(OStream &os, Zone *zone, const Graph *graph)
GenericGraphVisit::Control Pre(Node *node)
static bool IsContextEdge(Node::Edge edge)
static bool IsControlEdge(Node::Edge edge)
static bool IsEffectEdge(Node::Edge edge)
static Node * GetControlInput(Node *node, int index=0)
static int GetEffectInputCount(const Operator *op)
static int GetFrameStateInputCount(const Operator *op)
static int GetContextInputCount(const Operator *op)
static bool IsBasicBlockBegin(const Operator *op)
static int GetValueInputCount(const Operator *op)
static int GetControlInputCount(const Operator *op)
enable harmony numeric enable harmony object literal extensions Optimize object Array DOM strings and string trace pretenuring decisions of HAllocate instructions Enables optimizations which favor memory size over execution speed maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining trace the tracking of allocation sites deoptimize every n garbage collections perform array bounds checks elimination analyze liveness of environment slots and zap dead values flushes the cache of optimized code for closures on every GC allow uint32 values on optimize frames if they are used only in safe operations track concurrent recompilation artificial compilation delay in ms do not emit check maps for constant values that have a leaf deoptimize the optimized code if the layout of the maps changes enable context specialization in TurboFan execution budget before interrupt is triggered max percentage of megamorphic generic ICs to allow optimization enable use of SAHF instruction if enable use of VFP3 instructions if available enable use of NEON instructions if enable use of SDIV and UDIV instructions if enable use of MLS instructions if enable loading bit constant by means of movw movt instruction enable unaligned accesses for enable use of d16 d31 registers on ARM this requires VFP3 force all emitted branches to be in long enable alignment of csp to bytes on platforms which prefer the register to always be expose gc extension under the specified name show built in functions in stack traces use random jit cookie to mask large constants minimum length for automatic enable preparsing CPU profiler sampling interval in microseconds trace out of bounds accesses to external arrays default size of stack region v8 is allowed to maximum length of function source code printed in a stack trace min size of a semi the new space consists of two semi spaces print one trace line following each garbage collection do not print trace line after scavenger collection print cumulative GC statistics in only print modified registers Trace simulator debug messages Implied by trace sim abort randomize hashes to avoid predictable hash Fixed seed to use to hash property Print the time it takes to deserialize the snapshot A filename with extra code to be included in the A file to write the raw snapshot bytes to(mksnapshot only)") DEFINE_STRING(raw_context_file
enable harmony numeric enable harmony object literal extensions true
enable harmony numeric enable harmony object literal extensions Optimize object Array DOM strings and string trace pretenuring decisions of HAllocate instructions Enables optimizations which favor memory size over execution speed maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining trace the tracking of allocation sites deoptimize every n garbage collections perform array bounds checks elimination analyze liveness of environment slots and zap dead values flushes the cache of optimized code for closures on every GC allow uint32 values on optimize frames if they are used only in safe operations track concurrent recompilation artificial compilation delay in ms do not emit check maps for constant values that have a leaf deoptimize the optimized code if the layout of the maps changes enable context specialization in TurboFan execution budget before interrupt is triggered max percentage of megamorphic generic ICs to allow optimization enable use of SAHF instruction if enable use of VFP3 instructions if available enable use of NEON instructions if enable use of SDIV and UDIV instructions if enable use of MLS instructions if enable loading bit constant by means of movw movt instruction enable unaligned accesses for enable use of d16 d31 registers on ARM this requires VFP3 force all emitted branches to be in long enable alignment of csp to bytes on platforms which prefer the register to always be NULL
#define DEAD_COLOR
NodeSet::iterator NodeSetIter
Definition: node.h:69
std::ostream & operator<<(std::ostream &os, const MachineType &type)
static bool IsLikelyBackEdge(Node *from, int index, Node *to)
static Node * GetControlCluster(Node *node)
Node::Inputs::iterator InputIter
Definition: node.h:82
std::set< Node *, std::less< Node * >, zone_allocator< Node * > > NodeSet
Definition: node.h:68
Debugger support for the V8 JavaScript engine.
Definition: accessors.cc:20