42d355fb1dba50a18943b4da4621f43039771a45
[platform/upstream/nodejs.git] / deps / v8 / src / compiler / graph-visualizer.cc
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
5 #include "src/compiler/graph-visualizer.h"
6
7 #include <sstream>
8 #include <string>
9
10 #include "src/code-stubs.h"
11 #include "src/compiler/all-nodes.h"
12 #include "src/compiler/graph.h"
13 #include "src/compiler/node.h"
14 #include "src/compiler/node-properties.h"
15 #include "src/compiler/opcodes.h"
16 #include "src/compiler/operator.h"
17 #include "src/compiler/operator-properties.h"
18 #include "src/compiler/register-allocator.h"
19 #include "src/compiler/schedule.h"
20 #include "src/compiler/scheduler.h"
21 #include "src/ostreams.h"
22
23 namespace v8 {
24 namespace internal {
25 namespace compiler {
26
27
28 FILE* OpenVisualizerLogFile(CompilationInfo* info, const char* phase,
29                             const char* suffix, const char* mode) {
30   EmbeddedVector<char, 256> filename;
31   SmartArrayPointer<char> function_name;
32   if (!info->shared_info().is_null()) {
33     function_name = info->shared_info()->DebugName()->ToCString();
34     if (strlen(function_name.get()) > 0) {
35       SNPrintF(filename, "turbo-%s", function_name.get());
36     } else {
37       SNPrintF(filename, "turbo-%p", static_cast<void*>(info));
38     }
39   } else {
40     SNPrintF(filename, "turbo-none-%s", phase);
41   }
42   std::replace(filename.start(), filename.start() + filename.length(), ' ',
43                '_');
44
45   EmbeddedVector<char, 256> full_filename;
46   if (phase == NULL) {
47     SNPrintF(full_filename, "%s.%s", filename.start(), suffix);
48   } else {
49     SNPrintF(full_filename, "%s-%s.%s", filename.start(), phase, suffix);
50   }
51   return base::OS::FOpen(full_filename.start(), mode);
52 }
53
54
55 static int SafeId(Node* node) { return node == NULL ? -1 : node->id(); }
56 static const char* SafeMnemonic(Node* node) {
57   return node == NULL ? "null" : node->op()->mnemonic();
58 }
59
60 #define DEAD_COLOR "#999999"
61
62 class Escaped {
63  public:
64   explicit Escaped(const std::ostringstream& os,
65                    const char* escaped_chars = "<>|{}")
66       : str_(os.str()), escaped_chars_(escaped_chars) {}
67
68   friend std::ostream& operator<<(std::ostream& os, const Escaped& e) {
69     for (std::string::const_iterator i = e.str_.begin(); i != e.str_.end();
70          ++i) {
71       if (e.needs_escape(*i)) os << "\\";
72       os << *i;
73     }
74     return os;
75   }
76
77  private:
78   bool needs_escape(char ch) const {
79     for (size_t i = 0; i < strlen(escaped_chars_); ++i) {
80       if (ch == escaped_chars_[i]) return true;
81     }
82     return false;
83   }
84
85   const std::string str_;
86   const char* const escaped_chars_;
87 };
88
89 class JSONGraphNodeWriter {
90  public:
91   JSONGraphNodeWriter(std::ostream& os, Zone* zone, const Graph* graph,
92                       const SourcePositionTable* positions)
93       : os_(os), all_(zone, graph), positions_(positions), first_node_(true) {}
94
95   void Print() {
96     for (Node* const node : all_.live) PrintNode(node);
97     os_ << "\n";
98   }
99
100   void PrintNode(Node* node) {
101     if (first_node_) {
102       first_node_ = false;
103     } else {
104       os_ << ",\n";
105     }
106     std::ostringstream label;
107     label << *node->op();
108     os_ << "{\"id\":" << SafeId(node) << ",\"label\":\"" << Escaped(label, "\"")
109         << "\"";
110     IrOpcode::Value opcode = node->opcode();
111     if (IrOpcode::IsPhiOpcode(opcode)) {
112       os_ << ",\"rankInputs\":[0," << NodeProperties::FirstControlIndex(node)
113           << "]";
114       os_ << ",\"rankWithInput\":[" << NodeProperties::FirstControlIndex(node)
115           << "]";
116     } else if (opcode == IrOpcode::kIfTrue || opcode == IrOpcode::kIfFalse ||
117                opcode == IrOpcode::kLoop) {
118       os_ << ",\"rankInputs\":[" << NodeProperties::FirstControlIndex(node)
119           << "]";
120     }
121     if (opcode == IrOpcode::kBranch) {
122       os_ << ",\"rankInputs\":[0]";
123     }
124     SourcePosition position = positions_->GetSourcePosition(node);
125     if (!position.IsUnknown()) {
126       DCHECK(!position.IsInvalid());
127       os_ << ",\"pos\":" << position.raw();
128     }
129     os_ << ",\"opcode\":\"" << IrOpcode::Mnemonic(node->opcode()) << "\"";
130     os_ << ",\"control\":" << (NodeProperties::IsControl(node) ? "true"
131                                                                : "false");
132     os_ << "}";
133   }
134
135  private:
136   std::ostream& os_;
137   AllNodes all_;
138   const SourcePositionTable* positions_;
139   bool first_node_;
140
141   DISALLOW_COPY_AND_ASSIGN(JSONGraphNodeWriter);
142 };
143
144
145 class JSONGraphEdgeWriter {
146  public:
147   JSONGraphEdgeWriter(std::ostream& os, Zone* zone, const Graph* graph)
148       : os_(os), all_(zone, graph), first_edge_(true) {}
149
150   void Print() {
151     for (Node* const node : all_.live) PrintEdges(node);
152     os_ << "\n";
153   }
154
155   void PrintEdges(Node* node) {
156     for (int i = 0; i < node->InputCount(); i++) {
157       Node* input = node->InputAt(i);
158       if (input == NULL) continue;
159       PrintEdge(node, i, input);
160     }
161   }
162
163   void PrintEdge(Node* from, int index, Node* to) {
164     if (first_edge_) {
165       first_edge_ = false;
166     } else {
167       os_ << ",\n";
168     }
169     const char* edge_type = NULL;
170     if (index < NodeProperties::FirstValueIndex(from)) {
171       edge_type = "unknown";
172     } else if (index < NodeProperties::FirstContextIndex(from)) {
173       edge_type = "value";
174     } else if (index < NodeProperties::FirstFrameStateIndex(from)) {
175       edge_type = "context";
176     } else if (index < NodeProperties::FirstEffectIndex(from)) {
177       edge_type = "frame-state";
178     } else if (index < NodeProperties::FirstControlIndex(from)) {
179       edge_type = "effect";
180     } else {
181       edge_type = "control";
182     }
183     os_ << "{\"source\":" << SafeId(to) << ",\"target\":" << SafeId(from)
184         << ",\"index\":" << index << ",\"type\":\"" << edge_type << "\"}";
185   }
186
187  private:
188   std::ostream& os_;
189   AllNodes all_;
190   bool first_edge_;
191
192   DISALLOW_COPY_AND_ASSIGN(JSONGraphEdgeWriter);
193 };
194
195
196 std::ostream& operator<<(std::ostream& os, const AsJSON& ad) {
197   Zone tmp_zone;
198   os << "{\n\"nodes\":[";
199   JSONGraphNodeWriter(os, &tmp_zone, &ad.graph, ad.positions).Print();
200   os << "],\n\"edges\":[";
201   JSONGraphEdgeWriter(os, &tmp_zone, &ad.graph).Print();
202   os << "]}";
203   return os;
204 }
205
206
207 class GraphVisualizer {
208  public:
209   GraphVisualizer(std::ostream& os, Zone* zone, const Graph* graph)
210       : all_(zone, graph), os_(os) {}
211
212   void Print();
213
214   void PrintNode(Node* node, bool gray);
215
216  private:
217   void PrintEdge(Edge edge);
218
219   AllNodes all_;
220   std::ostream& os_;
221
222   DISALLOW_COPY_AND_ASSIGN(GraphVisualizer);
223 };
224
225
226 static Node* GetControlCluster(Node* node) {
227   if (OperatorProperties::IsBasicBlockBegin(node->op())) {
228     return node;
229   } else if (node->op()->ControlInputCount() == 1) {
230     Node* control = NodeProperties::GetControlInput(node, 0);
231     return control != NULL &&
232                    OperatorProperties::IsBasicBlockBegin(control->op())
233                ? control
234                : NULL;
235   } else {
236     return NULL;
237   }
238 }
239
240
241 void GraphVisualizer::PrintNode(Node* node, bool gray) {
242   Node* control_cluster = GetControlCluster(node);
243   if (control_cluster != NULL) {
244     os_ << "  subgraph cluster_BasicBlock" << control_cluster->id() << " {\n";
245   }
246   os_ << "  ID" << SafeId(node) << " [\n";
247
248   os_ << "    shape=\"record\"\n";
249   switch (node->opcode()) {
250     case IrOpcode::kEnd:
251     case IrOpcode::kDead:
252     case IrOpcode::kStart:
253       os_ << "    style=\"diagonals\"\n";
254       break;
255     case IrOpcode::kMerge:
256     case IrOpcode::kIfTrue:
257     case IrOpcode::kIfFalse:
258     case IrOpcode::kLoop:
259       os_ << "    style=\"rounded\"\n";
260       break;
261     default:
262       break;
263   }
264
265   if (gray) {
266     os_ << "    style=\"filled\"\n"
267         << "    fillcolor=\"" DEAD_COLOR "\"\n";
268   }
269
270   std::ostringstream label;
271   label << *node->op();
272   os_ << "    label=\"{{#" << SafeId(node) << ":" << Escaped(label);
273
274   auto i = node->input_edges().begin();
275   for (int j = node->op()->ValueInputCount(); j > 0; ++i, j--) {
276     os_ << "|<I" << (*i).index() << ">#" << SafeId((*i).to());
277   }
278   for (int j = OperatorProperties::GetContextInputCount(node->op()); j > 0;
279        ++i, j--) {
280     os_ << "|<I" << (*i).index() << ">X #" << SafeId((*i).to());
281   }
282   for (int j = OperatorProperties::GetFrameStateInputCount(node->op()); j > 0;
283        ++i, j--) {
284     os_ << "|<I" << (*i).index() << ">F #" << SafeId((*i).to());
285   }
286   for (int j = node->op()->EffectInputCount(); j > 0; ++i, j--) {
287     os_ << "|<I" << (*i).index() << ">E #" << SafeId((*i).to());
288   }
289
290   if (OperatorProperties::IsBasicBlockBegin(node->op()) ||
291       GetControlCluster(node) == NULL) {
292     for (int j = node->op()->ControlInputCount(); j > 0; ++i, j--) {
293       os_ << "|<I" << (*i).index() << ">C #" << SafeId((*i).to());
294     }
295   }
296   os_ << "}";
297
298   if (FLAG_trace_turbo_types && NodeProperties::IsTyped(node)) {
299     Bounds bounds = NodeProperties::GetBounds(node);
300     std::ostringstream upper;
301     bounds.upper->PrintTo(upper);
302     std::ostringstream lower;
303     bounds.lower->PrintTo(lower);
304     os_ << "|" << Escaped(upper) << "|" << Escaped(lower);
305   }
306   os_ << "}\"\n";
307
308   os_ << "  ]\n";
309   if (control_cluster != NULL) os_ << "  }\n";
310 }
311
312
313 static bool IsLikelyBackEdge(Node* from, int index, Node* to) {
314   if (NodeProperties::IsPhi(from)) {
315     Node* control = NodeProperties::GetControlInput(from, 0);
316     return control != NULL && control->opcode() != IrOpcode::kMerge &&
317            control != to && index != 0;
318   } else if (from->opcode() == IrOpcode::kLoop) {
319     return index != 0;
320   } else {
321     return false;
322   }
323 }
324
325
326 void GraphVisualizer::PrintEdge(Edge edge) {
327   Node* from = edge.from();
328   int index = edge.index();
329   Node* to = edge.to();
330
331   if (!all_.IsLive(to)) return;  // skip inputs that point to dead or NULL.
332
333   bool unconstrained = IsLikelyBackEdge(from, index, to);
334   os_ << "  ID" << SafeId(from);
335
336   if (OperatorProperties::IsBasicBlockBegin(from->op()) ||
337       GetControlCluster(from) == NULL ||
338       (from->op()->ControlInputCount() > 0 &&
339        NodeProperties::GetControlInput(from) != to)) {
340     os_ << ":I" << index << ":n -> ID" << SafeId(to) << ":s"
341         << "[" << (unconstrained ? "constraint=false, " : "")
342         << (NodeProperties::IsControlEdge(edge) ? "style=bold, " : "")
343         << (NodeProperties::IsEffectEdge(edge) ? "style=dotted, " : "")
344         << (NodeProperties::IsContextEdge(edge) ? "style=dashed, " : "") << "]";
345   } else {
346     os_ << " -> ID" << SafeId(to) << ":s [color=transparent, "
347         << (unconstrained ? "constraint=false, " : "")
348         << (NodeProperties::IsControlEdge(edge) ? "style=dashed, " : "") << "]";
349   }
350   os_ << "\n";
351 }
352
353
354 void GraphVisualizer::Print() {
355   os_ << "digraph D {\n"
356       << "  node [fontsize=8,height=0.25]\n"
357       << "  rankdir=\"BT\"\n"
358       << "  ranksep=\"1.2 equally\"\n"
359       << "  overlap=\"false\"\n"
360       << "  splines=\"true\"\n"
361       << "  concentrate=\"true\"\n"
362       << "  \n";
363
364   // Make sure all nodes have been output before writing out the edges.
365   for (Node* const node : all_.live) PrintNode(node, false);
366   for (Node* const node : all_.gray) PrintNode(node, true);
367
368   // With all the nodes written, add the edges.
369   for (Node* const node : all_.live) {
370     for (Edge edge : node->use_edges()) {
371       PrintEdge(edge);
372     }
373   }
374   os_ << "}\n";
375 }
376
377
378 std::ostream& operator<<(std::ostream& os, const AsDOT& ad) {
379   Zone tmp_zone;
380   GraphVisualizer(os, &tmp_zone, &ad.graph).Print();
381   return os;
382 }
383
384
385 class GraphC1Visualizer {
386  public:
387   GraphC1Visualizer(std::ostream& os, Zone* zone);  // NOLINT
388
389   void PrintCompilation(const CompilationInfo* info);
390   void PrintSchedule(const char* phase, const Schedule* schedule,
391                      const SourcePositionTable* positions,
392                      const InstructionSequence* instructions);
393   void PrintAllocator(const char* phase, const RegisterAllocator* allocator);
394   Zone* zone() const { return zone_; }
395
396  private:
397   void PrintIndent();
398   void PrintStringProperty(const char* name, const char* value);
399   void PrintLongProperty(const char* name, int64_t value);
400   void PrintIntProperty(const char* name, int value);
401   void PrintBlockProperty(const char* name, BasicBlock::Id block_id);
402   void PrintNodeId(Node* n);
403   void PrintNode(Node* n);
404   void PrintInputs(Node* n);
405   template <typename InputIterator>
406   void PrintInputs(InputIterator* i, int count, const char* prefix);
407   void PrintType(Node* node);
408
409   void PrintLiveRange(LiveRange* range, const char* type);
410   class Tag FINAL BASE_EMBEDDED {
411    public:
412     Tag(GraphC1Visualizer* visualizer, const char* name) {
413       name_ = name;
414       visualizer_ = visualizer;
415       visualizer->PrintIndent();
416       visualizer_->os_ << "begin_" << name << "\n";
417       visualizer->indent_++;
418     }
419
420     ~Tag() {
421       visualizer_->indent_--;
422       visualizer_->PrintIndent();
423       visualizer_->os_ << "end_" << name_ << "\n";
424       DCHECK(visualizer_->indent_ >= 0);
425     }
426
427    private:
428     GraphC1Visualizer* visualizer_;
429     const char* name_;
430   };
431
432   std::ostream& os_;
433   int indent_;
434   Zone* zone_;
435
436   DISALLOW_COPY_AND_ASSIGN(GraphC1Visualizer);
437 };
438
439
440 void GraphC1Visualizer::PrintIndent() {
441   for (int i = 0; i < indent_; i++) {
442     os_ << "  ";
443   }
444 }
445
446
447 GraphC1Visualizer::GraphC1Visualizer(std::ostream& os, Zone* zone)
448     : os_(os), indent_(0), zone_(zone) {}
449
450
451 void GraphC1Visualizer::PrintStringProperty(const char* name,
452                                             const char* value) {
453   PrintIndent();
454   os_ << name << " \"" << value << "\"\n";
455 }
456
457
458 void GraphC1Visualizer::PrintLongProperty(const char* name, int64_t value) {
459   PrintIndent();
460   os_ << name << " " << static_cast<int>(value / 1000) << "\n";
461 }
462
463
464 void GraphC1Visualizer::PrintBlockProperty(const char* name,
465                                            BasicBlock::Id block_id) {
466   PrintIndent();
467   os_ << name << " \"B" << block_id << "\"\n";
468 }
469
470
471 void GraphC1Visualizer::PrintIntProperty(const char* name, int value) {
472   PrintIndent();
473   os_ << name << " " << value << "\n";
474 }
475
476
477 void GraphC1Visualizer::PrintCompilation(const CompilationInfo* info) {
478   Tag tag(this, "compilation");
479   if (info->IsOptimizing()) {
480     Handle<String> name = info->function()->debug_name();
481     PrintStringProperty("name", name->ToCString().get());
482     PrintIndent();
483     os_ << "method \"" << name->ToCString().get() << ":"
484         << info->optimization_id() << "\"\n";
485   } else {
486     CodeStub::Major major_key = info->code_stub()->MajorKey();
487     PrintStringProperty("name", CodeStub::MajorName(major_key, false));
488     PrintStringProperty("method", "stub");
489   }
490   PrintLongProperty("date",
491                     static_cast<int64_t>(base::OS::TimeCurrentMillis()));
492 }
493
494
495 void GraphC1Visualizer::PrintNodeId(Node* n) { os_ << "n" << SafeId(n); }
496
497
498 void GraphC1Visualizer::PrintNode(Node* n) {
499   PrintNodeId(n);
500   os_ << " " << *n->op() << " ";
501   PrintInputs(n);
502 }
503
504
505 template <typename InputIterator>
506 void GraphC1Visualizer::PrintInputs(InputIterator* i, int count,
507                                     const char* prefix) {
508   if (count > 0) {
509     os_ << prefix;
510   }
511   while (count > 0) {
512     os_ << " ";
513     PrintNodeId(**i);
514     ++(*i);
515     count--;
516   }
517 }
518
519
520 void GraphC1Visualizer::PrintInputs(Node* node) {
521   auto i = node->inputs().begin();
522   PrintInputs(&i, node->op()->ValueInputCount(), " ");
523   PrintInputs(&i, OperatorProperties::GetContextInputCount(node->op()),
524               " Ctx:");
525   PrintInputs(&i, OperatorProperties::GetFrameStateInputCount(node->op()),
526               " FS:");
527   PrintInputs(&i, node->op()->EffectInputCount(), " Eff:");
528   PrintInputs(&i, node->op()->ControlInputCount(), " Ctrl:");
529 }
530
531
532 void GraphC1Visualizer::PrintType(Node* node) {
533   if (NodeProperties::IsTyped(node)) {
534     Bounds bounds = NodeProperties::GetBounds(node);
535     os_ << " type:";
536     bounds.upper->PrintTo(os_);
537     os_ << "..";
538     bounds.lower->PrintTo(os_);
539   }
540 }
541
542
543 void GraphC1Visualizer::PrintSchedule(const char* phase,
544                                       const Schedule* schedule,
545                                       const SourcePositionTable* positions,
546                                       const InstructionSequence* instructions) {
547   Tag tag(this, "cfg");
548   PrintStringProperty("name", phase);
549   const BasicBlockVector* rpo = schedule->rpo_order();
550   for (size_t i = 0; i < rpo->size(); i++) {
551     BasicBlock* current = (*rpo)[i];
552     Tag block_tag(this, "block");
553     PrintBlockProperty("name", current->id());
554     PrintIntProperty("from_bci", -1);
555     PrintIntProperty("to_bci", -1);
556
557     PrintIndent();
558     os_ << "predecessors";
559     for (BasicBlock* predecessor : current->predecessors()) {
560       os_ << " \"B" << predecessor->id() << "\"";
561     }
562     os_ << "\n";
563
564     PrintIndent();
565     os_ << "successors";
566     for (BasicBlock* successor : current->successors()) {
567       os_ << " \"B" << successor->id() << "\"";
568     }
569     os_ << "\n";
570
571     PrintIndent();
572     os_ << "xhandlers\n";
573
574     PrintIndent();
575     os_ << "flags\n";
576
577     if (current->dominator() != NULL) {
578       PrintBlockProperty("dominator", current->dominator()->id());
579     }
580
581     PrintIntProperty("loop_depth", current->loop_depth());
582
583     const InstructionBlock* instruction_block =
584         instructions->InstructionBlockAt(current->GetRpoNumber());
585     if (instruction_block->code_start() >= 0) {
586       int first_index = instruction_block->first_instruction_index();
587       int last_index = instruction_block->last_instruction_index();
588       PrintIntProperty("first_lir_id", LifetimePosition::FromInstructionIndex(
589                                            first_index).Value());
590       PrintIntProperty("last_lir_id", LifetimePosition::FromInstructionIndex(
591                                           last_index).Value());
592     }
593
594     {
595       Tag states_tag(this, "states");
596       Tag locals_tag(this, "locals");
597       int total = 0;
598       for (BasicBlock::const_iterator i = current->begin(); i != current->end();
599            ++i) {
600         if ((*i)->opcode() == IrOpcode::kPhi) total++;
601       }
602       PrintIntProperty("size", total);
603       PrintStringProperty("method", "None");
604       int index = 0;
605       for (BasicBlock::const_iterator i = current->begin(); i != current->end();
606            ++i) {
607         if ((*i)->opcode() != IrOpcode::kPhi) continue;
608         PrintIndent();
609         os_ << index << " ";
610         PrintNodeId(*i);
611         os_ << " [";
612         PrintInputs(*i);
613         os_ << "]\n";
614         index++;
615       }
616     }
617
618     {
619       Tag HIR_tag(this, "HIR");
620       for (BasicBlock::const_iterator i = current->begin(); i != current->end();
621            ++i) {
622         Node* node = *i;
623         if (node->opcode() == IrOpcode::kPhi) continue;
624         int uses = node->UseCount();
625         PrintIndent();
626         os_ << "0 " << uses << " ";
627         PrintNode(node);
628         if (FLAG_trace_turbo_types) {
629           os_ << " ";
630           PrintType(node);
631         }
632         if (positions != NULL) {
633           SourcePosition position = positions->GetSourcePosition(node);
634           if (!position.IsUnknown()) {
635             DCHECK(!position.IsInvalid());
636             os_ << " pos:" << position.raw();
637           }
638         }
639         os_ << " <|@\n";
640       }
641
642       BasicBlock::Control control = current->control();
643       if (control != BasicBlock::kNone) {
644         PrintIndent();
645         os_ << "0 0 ";
646         if (current->control_input() != NULL) {
647           PrintNode(current->control_input());
648         } else {
649           os_ << -1 - current->id().ToInt() << " Goto";
650         }
651         os_ << " ->";
652         for (BasicBlock* successor : current->successors()) {
653           os_ << " B" << successor->id();
654         }
655         if (FLAG_trace_turbo_types && current->control_input() != NULL) {
656           os_ << " ";
657           PrintType(current->control_input());
658         }
659         os_ << " <|@\n";
660       }
661     }
662
663     if (instructions != NULL) {
664       Tag LIR_tag(this, "LIR");
665       for (int j = instruction_block->first_instruction_index();
666            j <= instruction_block->last_instruction_index(); j++) {
667         PrintIndent();
668         PrintableInstruction printable = {RegisterConfiguration::ArchDefault(),
669                                           instructions->InstructionAt(j)};
670         os_ << j << " " << printable << " <|@\n";
671       }
672     }
673   }
674 }
675
676
677 void GraphC1Visualizer::PrintAllocator(const char* phase,
678                                        const RegisterAllocator* allocator) {
679   Tag tag(this, "intervals");
680   PrintStringProperty("name", phase);
681
682   for (auto range : allocator->fixed_double_live_ranges()) {
683     PrintLiveRange(range, "fixed");
684   }
685
686   for (auto range : allocator->fixed_live_ranges()) {
687     PrintLiveRange(range, "fixed");
688   }
689
690   for (auto range : allocator->live_ranges()) {
691     PrintLiveRange(range, "object");
692   }
693 }
694
695
696 void GraphC1Visualizer::PrintLiveRange(LiveRange* range, const char* type) {
697   if (range != NULL && !range->IsEmpty()) {
698     PrintIndent();
699     os_ << range->id() << " " << type;
700     if (range->HasRegisterAssigned()) {
701       InstructionOperand op = range->GetAssignedOperand();
702       int assigned_reg = op.index();
703       if (op.IsDoubleRegister()) {
704         os_ << " \"" << DoubleRegister::AllocationIndexToString(assigned_reg)
705             << "\"";
706       } else {
707         DCHECK(op.IsRegister());
708         os_ << " \"" << Register::AllocationIndexToString(assigned_reg) << "\"";
709       }
710     } else if (range->IsSpilled()) {
711       int index = -1;
712       if (range->TopLevel()->HasSpillRange()) {
713         index = kMaxInt;  // This hasn't been set yet.
714       } else {
715         index = range->TopLevel()->GetSpillOperand()->index();
716       }
717       if (range->TopLevel()->Kind() == DOUBLE_REGISTERS) {
718         os_ << " \"double_stack:" << index << "\"";
719       } else if (range->TopLevel()->Kind() == GENERAL_REGISTERS) {
720         os_ << " \"stack:" << index << "\"";
721       } else {
722         os_ << " \"const(nostack):" << index << "\"";
723       }
724     }
725     int parent_index = -1;
726     if (range->IsChild()) {
727       parent_index = range->parent()->id();
728     } else {
729       parent_index = range->id();
730     }
731     InstructionOperand* op = range->FirstHint();
732     int hint_index = -1;
733     if (op != NULL && op->IsUnallocated()) {
734       hint_index = UnallocatedOperand::cast(op)->virtual_register();
735     }
736     os_ << " " << parent_index << " " << hint_index;
737     UseInterval* cur_interval = range->first_interval();
738     while (cur_interval != NULL && range->Covers(cur_interval->start())) {
739       os_ << " [" << cur_interval->start().Value() << ", "
740           << cur_interval->end().Value() << "[";
741       cur_interval = cur_interval->next();
742     }
743
744     UsePosition* current_pos = range->first_pos();
745     while (current_pos != NULL) {
746       if (current_pos->RegisterIsBeneficial() || FLAG_trace_all_uses) {
747         os_ << " " << current_pos->pos().Value() << " M";
748       }
749       current_pos = current_pos->next();
750     }
751
752     os_ << " \"\"\n";
753   }
754 }
755
756
757 std::ostream& operator<<(std::ostream& os, const AsC1VCompilation& ac) {
758   Zone tmp_zone;
759   GraphC1Visualizer(os, &tmp_zone).PrintCompilation(ac.info_);
760   return os;
761 }
762
763
764 std::ostream& operator<<(std::ostream& os, const AsC1V& ac) {
765   Zone tmp_zone;
766   GraphC1Visualizer(os, &tmp_zone)
767       .PrintSchedule(ac.phase_, ac.schedule_, ac.positions_, ac.instructions_);
768   return os;
769 }
770
771
772 std::ostream& operator<<(std::ostream& os, const AsC1VAllocator& ac) {
773   Zone tmp_zone;
774   GraphC1Visualizer(os, &tmp_zone).PrintAllocator(ac.phase_, ac.allocator_);
775   return os;
776 }
777
778 const int kUnvisited = 0;
779 const int kOnStack = 1;
780 const int kVisited = 2;
781
782 std::ostream& operator<<(std::ostream& os, const AsRPO& ar) {
783   Zone local_zone;
784   ZoneVector<byte> state(ar.graph.NodeCount(), kUnvisited, &local_zone);
785   ZoneStack<Node*> stack(&local_zone);
786
787   stack.push(ar.graph.end());
788   state[ar.graph.end()->id()] = kOnStack;
789   while (!stack.empty()) {
790     Node* n = stack.top();
791     bool pop = true;
792     for (Node* const i : n->inputs()) {
793       if (state[i->id()] == kUnvisited) {
794         state[i->id()] = kOnStack;
795         stack.push(i);
796         pop = false;
797         break;
798       }
799     }
800     if (pop) {
801       state[n->id()] = kVisited;
802       stack.pop();
803       os << "#" << n->id() << ":" << *n->op() << "(";
804       int j = 0;
805       for (Node* const i : n->inputs()) {
806         if (j++ > 0) os << ", ";
807         os << "#" << SafeId(i) << ":" << SafeMnemonic(i);
808       }
809       os << ")" << std::endl;
810     }
811   }
812   return os;
813 }
814 }
815 }
816 }  // namespace v8::internal::compiler