• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/interpreter/bytecodes.h"
22 #include "src/ostreams.h"
23 
24 namespace v8 {
25 namespace internal {
26 namespace compiler {
27 
GetVisualizerLogFileName(CompilationInfo * info,const char * phase,const char * suffix)28 base::SmartArrayPointer<const char> GetVisualizerLogFileName(
29     CompilationInfo* info, const char* phase, const char* suffix) {
30   EmbeddedVector<char, 256> filename(0);
31   base::SmartArrayPointer<char> debug_name = info->GetDebugName();
32   if (strlen(debug_name.get()) > 0) {
33     SNPrintF(filename, "turbo-%s", debug_name.get());
34   } else if (info->has_shared_info()) {
35     SNPrintF(filename, "turbo-%p", static_cast<void*>(info));
36   } else {
37     SNPrintF(filename, "turbo-none-%s", phase);
38   }
39   EmbeddedVector<char, 256> source_file(0);
40   bool source_available = false;
41   if (FLAG_trace_file_names && info->parse_info()) {
42     Object* source_name = info->script()->name();
43     if (source_name->IsString()) {
44       String* str = String::cast(source_name);
45       if (str->length() > 0) {
46         SNPrintF(source_file, "%s", str->ToCString().get());
47         std::replace(source_file.start(),
48                      source_file.start() + source_file.length(), '/', '_');
49         source_available = true;
50       }
51     }
52   }
53   std::replace(filename.start(), filename.start() + filename.length(), ' ',
54                '_');
55 
56   EmbeddedVector<char, 256> full_filename;
57   if (phase == nullptr && !source_available) {
58     SNPrintF(full_filename, "%s.%s", filename.start(), suffix);
59   } else if (phase != nullptr && !source_available) {
60     SNPrintF(full_filename, "%s-%s.%s", filename.start(), phase, suffix);
61   } else if (phase == nullptr && source_available) {
62     SNPrintF(full_filename, "%s_%s.%s", filename.start(), source_file.start(),
63              suffix);
64   } else {
65     SNPrintF(full_filename, "%s_%s-%s.%s", filename.start(),
66              source_file.start(), phase, suffix);
67   }
68 
69   char* buffer = new char[full_filename.length() + 1];
70   memcpy(buffer, full_filename.start(), full_filename.length());
71   buffer[full_filename.length()] = '\0';
72   return base::SmartArrayPointer<const char>(buffer);
73 }
74 
75 
SafeId(Node * node)76 static int SafeId(Node* node) { return node == nullptr ? -1 : node->id(); }
SafeMnemonic(Node * node)77 static const char* SafeMnemonic(Node* node) {
78   return node == nullptr ? "null" : node->op()->mnemonic();
79 }
80 
81 #define DEAD_COLOR "#999999"
82 
83 class Escaped {
84  public:
Escaped(const std::ostringstream & os,const char * escaped_chars="<>|{}")85   explicit Escaped(const std::ostringstream& os,
86                    const char* escaped_chars = "<>|{}")
87       : str_(os.str()), escaped_chars_(escaped_chars) {}
88 
operator <<(std::ostream & os,const Escaped & e)89   friend std::ostream& operator<<(std::ostream& os, const Escaped& e) {
90     for (std::string::const_iterator i = e.str_.begin(); i != e.str_.end();
91          ++i) {
92       if (e.needs_escape(*i)) os << "\\";
93       os << *i;
94     }
95     return os;
96   }
97 
98  private:
needs_escape(char ch) const99   bool needs_escape(char ch) const {
100     for (size_t i = 0; i < strlen(escaped_chars_); ++i) {
101       if (ch == escaped_chars_[i]) return true;
102     }
103     return false;
104   }
105 
106   const std::string str_;
107   const char* const escaped_chars_;
108 };
109 
110 class JSONGraphNodeWriter {
111  public:
JSONGraphNodeWriter(std::ostream & os,Zone * zone,const Graph * graph,const SourcePositionTable * positions)112   JSONGraphNodeWriter(std::ostream& os, Zone* zone, const Graph* graph,
113                       const SourcePositionTable* positions)
114       : os_(os), all_(zone, graph), positions_(positions), first_node_(true) {}
115 
Print()116   void Print() {
117     for (Node* const node : all_.live) PrintNode(node);
118     os_ << "\n";
119   }
120 
PrintNode(Node * node)121   void PrintNode(Node* node) {
122     if (first_node_) {
123       first_node_ = false;
124     } else {
125       os_ << ",\n";
126     }
127     std::ostringstream label;
128     label << *node->op();
129     os_ << "{\"id\":" << SafeId(node) << ",\"label\":\"" << Escaped(label, "\"")
130         << "\"";
131     IrOpcode::Value opcode = node->opcode();
132     if (IrOpcode::IsPhiOpcode(opcode)) {
133       os_ << ",\"rankInputs\":[0," << NodeProperties::FirstControlIndex(node)
134           << "]";
135       os_ << ",\"rankWithInput\":[" << NodeProperties::FirstControlIndex(node)
136           << "]";
137     } else if (opcode == IrOpcode::kIfTrue || opcode == IrOpcode::kIfFalse ||
138                opcode == IrOpcode::kLoop) {
139       os_ << ",\"rankInputs\":[" << NodeProperties::FirstControlIndex(node)
140           << "]";
141     }
142     if (opcode == IrOpcode::kBranch) {
143       os_ << ",\"rankInputs\":[0]";
144     }
145     SourcePosition position = positions_->GetSourcePosition(node);
146     if (position.IsKnown()) {
147       os_ << ",\"pos\":" << position.raw();
148     }
149     os_ << ",\"opcode\":\"" << IrOpcode::Mnemonic(node->opcode()) << "\"";
150     os_ << ",\"control\":" << (NodeProperties::IsControl(node) ? "true"
151                                                                : "false");
152     if (NodeProperties::IsTyped(node)) {
153       Type* type = NodeProperties::GetType(node);
154       std::ostringstream type_out;
155       type->PrintTo(type_out);
156       os_ << ",\"type\":\"" << Escaped(type_out, "\"") << "\"";
157     }
158     os_ << "}";
159   }
160 
161  private:
162   std::ostream& os_;
163   AllNodes all_;
164   const SourcePositionTable* positions_;
165   bool first_node_;
166 
167   DISALLOW_COPY_AND_ASSIGN(JSONGraphNodeWriter);
168 };
169 
170 
171 class JSONGraphEdgeWriter {
172  public:
JSONGraphEdgeWriter(std::ostream & os,Zone * zone,const Graph * graph)173   JSONGraphEdgeWriter(std::ostream& os, Zone* zone, const Graph* graph)
174       : os_(os), all_(zone, graph), first_edge_(true) {}
175 
Print()176   void Print() {
177     for (Node* const node : all_.live) PrintEdges(node);
178     os_ << "\n";
179   }
180 
PrintEdges(Node * node)181   void PrintEdges(Node* node) {
182     for (int i = 0; i < node->InputCount(); i++) {
183       Node* input = node->InputAt(i);
184       if (input == nullptr) continue;
185       PrintEdge(node, i, input);
186     }
187   }
188 
PrintEdge(Node * from,int index,Node * to)189   void PrintEdge(Node* from, int index, Node* to) {
190     if (first_edge_) {
191       first_edge_ = false;
192     } else {
193       os_ << ",\n";
194     }
195     const char* edge_type = nullptr;
196     if (index < NodeProperties::FirstValueIndex(from)) {
197       edge_type = "unknown";
198     } else if (index < NodeProperties::FirstContextIndex(from)) {
199       edge_type = "value";
200     } else if (index < NodeProperties::FirstFrameStateIndex(from)) {
201       edge_type = "context";
202     } else if (index < NodeProperties::FirstEffectIndex(from)) {
203       edge_type = "frame-state";
204     } else if (index < NodeProperties::FirstControlIndex(from)) {
205       edge_type = "effect";
206     } else {
207       edge_type = "control";
208     }
209     os_ << "{\"source\":" << SafeId(to) << ",\"target\":" << SafeId(from)
210         << ",\"index\":" << index << ",\"type\":\"" << edge_type << "\"}";
211   }
212 
213  private:
214   std::ostream& os_;
215   AllNodes all_;
216   bool first_edge_;
217 
218   DISALLOW_COPY_AND_ASSIGN(JSONGraphEdgeWriter);
219 };
220 
221 
operator <<(std::ostream & os,const AsJSON & ad)222 std::ostream& operator<<(std::ostream& os, const AsJSON& ad) {
223   base::AccountingAllocator allocator;
224   Zone tmp_zone(&allocator);
225   os << "{\n\"nodes\":[";
226   JSONGraphNodeWriter(os, &tmp_zone, &ad.graph, ad.positions).Print();
227   os << "],\n\"edges\":[";
228   JSONGraphEdgeWriter(os, &tmp_zone, &ad.graph).Print();
229   os << "]}";
230   return os;
231 }
232 
233 
234 class GraphC1Visualizer {
235  public:
236   GraphC1Visualizer(std::ostream& os, Zone* zone);  // NOLINT
237 
238   void PrintCompilation(const CompilationInfo* info);
239   void PrintSchedule(const char* phase, const Schedule* schedule,
240                      const SourcePositionTable* positions,
241                      const InstructionSequence* instructions);
242   void PrintLiveRanges(const char* phase, const RegisterAllocationData* data);
zone() const243   Zone* zone() const { return zone_; }
244 
245  private:
246   void PrintIndent();
247   void PrintStringProperty(const char* name, const char* value);
248   void PrintLongProperty(const char* name, int64_t value);
249   void PrintIntProperty(const char* name, int value);
250   void PrintBlockProperty(const char* name, int rpo_number);
251   void PrintNodeId(Node* n);
252   void PrintNode(Node* n);
253   void PrintInputs(Node* n);
254   template <typename InputIterator>
255   void PrintInputs(InputIterator* i, int count, const char* prefix);
256   void PrintType(Node* node);
257 
258   void PrintLiveRange(const LiveRange* range, const char* type, int vreg);
259   void PrintLiveRangeChain(const TopLevelLiveRange* range, const char* type);
260 
261   class Tag final BASE_EMBEDDED {
262    public:
Tag(GraphC1Visualizer * visualizer,const char * name)263     Tag(GraphC1Visualizer* visualizer, const char* name) {
264       name_ = name;
265       visualizer_ = visualizer;
266       visualizer->PrintIndent();
267       visualizer_->os_ << "begin_" << name << "\n";
268       visualizer->indent_++;
269     }
270 
~Tag()271     ~Tag() {
272       visualizer_->indent_--;
273       visualizer_->PrintIndent();
274       visualizer_->os_ << "end_" << name_ << "\n";
275       DCHECK(visualizer_->indent_ >= 0);
276     }
277 
278    private:
279     GraphC1Visualizer* visualizer_;
280     const char* name_;
281   };
282 
283   std::ostream& os_;
284   int indent_;
285   Zone* zone_;
286 
287   DISALLOW_COPY_AND_ASSIGN(GraphC1Visualizer);
288 };
289 
290 
PrintIndent()291 void GraphC1Visualizer::PrintIndent() {
292   for (int i = 0; i < indent_; i++) {
293     os_ << "  ";
294   }
295 }
296 
297 
GraphC1Visualizer(std::ostream & os,Zone * zone)298 GraphC1Visualizer::GraphC1Visualizer(std::ostream& os, Zone* zone)
299     : os_(os), indent_(0), zone_(zone) {}
300 
301 
PrintStringProperty(const char * name,const char * value)302 void GraphC1Visualizer::PrintStringProperty(const char* name,
303                                             const char* value) {
304   PrintIndent();
305   os_ << name << " \"" << value << "\"\n";
306 }
307 
308 
PrintLongProperty(const char * name,int64_t value)309 void GraphC1Visualizer::PrintLongProperty(const char* name, int64_t value) {
310   PrintIndent();
311   os_ << name << " " << static_cast<int>(value / 1000) << "\n";
312 }
313 
314 
PrintBlockProperty(const char * name,int rpo_number)315 void GraphC1Visualizer::PrintBlockProperty(const char* name, int rpo_number) {
316   PrintIndent();
317   os_ << name << " \"B" << rpo_number << "\"\n";
318 }
319 
320 
PrintIntProperty(const char * name,int value)321 void GraphC1Visualizer::PrintIntProperty(const char* name, int value) {
322   PrintIndent();
323   os_ << name << " " << value << "\n";
324 }
325 
326 
PrintCompilation(const CompilationInfo * info)327 void GraphC1Visualizer::PrintCompilation(const CompilationInfo* info) {
328   Tag tag(this, "compilation");
329   base::SmartArrayPointer<char> name = info->GetDebugName();
330   if (info->IsOptimizing()) {
331     PrintStringProperty("name", name.get());
332     PrintIndent();
333     os_ << "method \"" << name.get() << ":" << info->optimization_id()
334         << "\"\n";
335   } else {
336     PrintStringProperty("name", name.get());
337     PrintStringProperty("method", "stub");
338   }
339   PrintLongProperty("date",
340                     static_cast<int64_t>(base::OS::TimeCurrentMillis()));
341 }
342 
343 
PrintNodeId(Node * n)344 void GraphC1Visualizer::PrintNodeId(Node* n) { os_ << "n" << SafeId(n); }
345 
346 
PrintNode(Node * n)347 void GraphC1Visualizer::PrintNode(Node* n) {
348   PrintNodeId(n);
349   os_ << " " << *n->op() << " ";
350   PrintInputs(n);
351 }
352 
353 
354 template <typename InputIterator>
PrintInputs(InputIterator * i,int count,const char * prefix)355 void GraphC1Visualizer::PrintInputs(InputIterator* i, int count,
356                                     const char* prefix) {
357   if (count > 0) {
358     os_ << prefix;
359   }
360   while (count > 0) {
361     os_ << " ";
362     PrintNodeId(**i);
363     ++(*i);
364     count--;
365   }
366 }
367 
368 
PrintInputs(Node * node)369 void GraphC1Visualizer::PrintInputs(Node* node) {
370   auto i = node->inputs().begin();
371   PrintInputs(&i, node->op()->ValueInputCount(), " ");
372   PrintInputs(&i, OperatorProperties::GetContextInputCount(node->op()),
373               " Ctx:");
374   PrintInputs(&i, OperatorProperties::GetFrameStateInputCount(node->op()),
375               " FS:");
376   PrintInputs(&i, node->op()->EffectInputCount(), " Eff:");
377   PrintInputs(&i, node->op()->ControlInputCount(), " Ctrl:");
378 }
379 
380 
PrintType(Node * node)381 void GraphC1Visualizer::PrintType(Node* node) {
382   if (NodeProperties::IsTyped(node)) {
383     Type* type = NodeProperties::GetType(node);
384     os_ << " type:";
385     type->PrintTo(os_);
386   }
387 }
388 
389 
PrintSchedule(const char * phase,const Schedule * schedule,const SourcePositionTable * positions,const InstructionSequence * instructions)390 void GraphC1Visualizer::PrintSchedule(const char* phase,
391                                       const Schedule* schedule,
392                                       const SourcePositionTable* positions,
393                                       const InstructionSequence* instructions) {
394   Tag tag(this, "cfg");
395   PrintStringProperty("name", phase);
396   const BasicBlockVector* rpo = schedule->rpo_order();
397   for (size_t i = 0; i < rpo->size(); i++) {
398     BasicBlock* current = (*rpo)[i];
399     Tag block_tag(this, "block");
400     PrintBlockProperty("name", current->rpo_number());
401     PrintIntProperty("from_bci", -1);
402     PrintIntProperty("to_bci", -1);
403 
404     PrintIndent();
405     os_ << "predecessors";
406     for (BasicBlock* predecessor : current->predecessors()) {
407       os_ << " \"B" << predecessor->rpo_number() << "\"";
408     }
409     os_ << "\n";
410 
411     PrintIndent();
412     os_ << "successors";
413     for (BasicBlock* successor : current->successors()) {
414       os_ << " \"B" << successor->rpo_number() << "\"";
415     }
416     os_ << "\n";
417 
418     PrintIndent();
419     os_ << "xhandlers\n";
420 
421     PrintIndent();
422     os_ << "flags\n";
423 
424     if (current->dominator() != nullptr) {
425       PrintBlockProperty("dominator", current->dominator()->rpo_number());
426     }
427 
428     PrintIntProperty("loop_depth", current->loop_depth());
429 
430     const InstructionBlock* instruction_block =
431         instructions->InstructionBlockAt(
432             RpoNumber::FromInt(current->rpo_number()));
433     if (instruction_block->code_start() >= 0) {
434       int first_index = instruction_block->first_instruction_index();
435       int last_index = instruction_block->last_instruction_index();
436       PrintIntProperty(
437           "first_lir_id",
438           LifetimePosition::GapFromInstructionIndex(first_index).value());
439       PrintIntProperty("last_lir_id",
440                        LifetimePosition::InstructionFromInstructionIndex(
441                            last_index).value());
442     }
443 
444     {
445       Tag states_tag(this, "states");
446       Tag locals_tag(this, "locals");
447       int total = 0;
448       for (BasicBlock::const_iterator i = current->begin(); i != current->end();
449            ++i) {
450         if ((*i)->opcode() == IrOpcode::kPhi) total++;
451       }
452       PrintIntProperty("size", total);
453       PrintStringProperty("method", "None");
454       int index = 0;
455       for (BasicBlock::const_iterator i = current->begin(); i != current->end();
456            ++i) {
457         if ((*i)->opcode() != IrOpcode::kPhi) continue;
458         PrintIndent();
459         os_ << index << " ";
460         PrintNodeId(*i);
461         os_ << " [";
462         PrintInputs(*i);
463         os_ << "]\n";
464         index++;
465       }
466     }
467 
468     {
469       Tag HIR_tag(this, "HIR");
470       for (BasicBlock::const_iterator i = current->begin(); i != current->end();
471            ++i) {
472         Node* node = *i;
473         if (node->opcode() == IrOpcode::kPhi) continue;
474         int uses = node->UseCount();
475         PrintIndent();
476         os_ << "0 " << uses << " ";
477         PrintNode(node);
478         if (FLAG_trace_turbo_types) {
479           os_ << " ";
480           PrintType(node);
481         }
482         if (positions != nullptr) {
483           SourcePosition position = positions->GetSourcePosition(node);
484           if (position.IsKnown()) {
485             os_ << " pos:" << position.raw();
486           }
487         }
488         os_ << " <|@\n";
489       }
490 
491       BasicBlock::Control control = current->control();
492       if (control != BasicBlock::kNone) {
493         PrintIndent();
494         os_ << "0 0 ";
495         if (current->control_input() != nullptr) {
496           PrintNode(current->control_input());
497         } else {
498           os_ << -1 - current->rpo_number() << " Goto";
499         }
500         os_ << " ->";
501         for (BasicBlock* successor : current->successors()) {
502           os_ << " B" << successor->rpo_number();
503         }
504         if (FLAG_trace_turbo_types && current->control_input() != nullptr) {
505           os_ << " ";
506           PrintType(current->control_input());
507         }
508         os_ << " <|@\n";
509       }
510     }
511 
512     if (instructions != nullptr) {
513       Tag LIR_tag(this, "LIR");
514       for (int j = instruction_block->first_instruction_index();
515            j <= instruction_block->last_instruction_index(); j++) {
516         PrintIndent();
517         PrintableInstruction printable = {RegisterConfiguration::Turbofan(),
518                                           instructions->InstructionAt(j)};
519         os_ << j << " " << printable << " <|@\n";
520       }
521     }
522   }
523 }
524 
525 
PrintLiveRanges(const char * phase,const RegisterAllocationData * data)526 void GraphC1Visualizer::PrintLiveRanges(const char* phase,
527                                         const RegisterAllocationData* data) {
528   Tag tag(this, "intervals");
529   PrintStringProperty("name", phase);
530 
531   for (const TopLevelLiveRange* range : data->fixed_double_live_ranges()) {
532     PrintLiveRangeChain(range, "fixed");
533   }
534 
535   for (const TopLevelLiveRange* range : data->fixed_live_ranges()) {
536     PrintLiveRangeChain(range, "fixed");
537   }
538 
539   for (const TopLevelLiveRange* range : data->live_ranges()) {
540     PrintLiveRangeChain(range, "object");
541   }
542 }
543 
PrintLiveRangeChain(const TopLevelLiveRange * range,const char * type)544 void GraphC1Visualizer::PrintLiveRangeChain(const TopLevelLiveRange* range,
545                                             const char* type) {
546   if (range == nullptr || range->IsEmpty()) return;
547   int vreg = range->vreg();
548   for (const LiveRange* child = range; child != nullptr;
549        child = child->next()) {
550     PrintLiveRange(child, type, vreg);
551   }
552 }
553 
PrintLiveRange(const LiveRange * range,const char * type,int vreg)554 void GraphC1Visualizer::PrintLiveRange(const LiveRange* range, const char* type,
555                                        int vreg) {
556   if (range != nullptr && !range->IsEmpty()) {
557     PrintIndent();
558     os_ << vreg << ":" << range->relative_id() << " " << type;
559     if (range->HasRegisterAssigned()) {
560       AllocatedOperand op = AllocatedOperand::cast(range->GetAssignedOperand());
561       const auto config = RegisterConfiguration::Turbofan();
562       if (op.IsRegister()) {
563         os_ << " \"" << config->GetGeneralRegisterName(op.register_code())
564             << "\"";
565       } else if (op.IsDoubleRegister()) {
566         os_ << " \"" << config->GetDoubleRegisterName(op.register_code())
567             << "\"";
568       } else {
569         DCHECK(op.IsFloatRegister());
570         os_ << " \"" << config->GetFloatRegisterName(op.register_code())
571             << "\"";
572       }
573     } else if (range->spilled()) {
574       const TopLevelLiveRange* top = range->TopLevel();
575       int index = -1;
576       if (top->HasSpillRange()) {
577         index = kMaxInt;  // This hasn't been set yet.
578       } else if (top->GetSpillOperand()->IsConstant()) {
579         os_ << " \"const(nostack):"
580             << ConstantOperand::cast(top->GetSpillOperand())->virtual_register()
581             << "\"";
582       } else {
583         index = AllocatedOperand::cast(top->GetSpillOperand())->index();
584         if (top->kind() == FP_REGISTERS) {
585           os_ << " \"double_stack:" << index << "\"";
586         } else if (top->kind() == GENERAL_REGISTERS) {
587           os_ << " \"stack:" << index << "\"";
588         }
589       }
590     }
591 
592     os_ << " " << vreg;
593     for (const UseInterval* interval = range->first_interval();
594          interval != nullptr; interval = interval->next()) {
595       os_ << " [" << interval->start().value() << ", "
596           << interval->end().value() << "[";
597     }
598 
599     UsePosition* current_pos = range->first_pos();
600     while (current_pos != nullptr) {
601       if (current_pos->RegisterIsBeneficial() || FLAG_trace_all_uses) {
602         os_ << " " << current_pos->pos().value() << " M";
603       }
604       current_pos = current_pos->next();
605     }
606 
607     os_ << " \"\"\n";
608   }
609 }
610 
611 
operator <<(std::ostream & os,const AsC1VCompilation & ac)612 std::ostream& operator<<(std::ostream& os, const AsC1VCompilation& ac) {
613   base::AccountingAllocator allocator;
614   Zone tmp_zone(&allocator);
615   GraphC1Visualizer(os, &tmp_zone).PrintCompilation(ac.info_);
616   return os;
617 }
618 
619 
operator <<(std::ostream & os,const AsC1V & ac)620 std::ostream& operator<<(std::ostream& os, const AsC1V& ac) {
621   base::AccountingAllocator allocator;
622   Zone tmp_zone(&allocator);
623   GraphC1Visualizer(os, &tmp_zone)
624       .PrintSchedule(ac.phase_, ac.schedule_, ac.positions_, ac.instructions_);
625   return os;
626 }
627 
628 
operator <<(std::ostream & os,const AsC1VRegisterAllocationData & ac)629 std::ostream& operator<<(std::ostream& os,
630                          const AsC1VRegisterAllocationData& ac) {
631   base::AccountingAllocator allocator;
632   Zone tmp_zone(&allocator);
633   GraphC1Visualizer(os, &tmp_zone).PrintLiveRanges(ac.phase_, ac.data_);
634   return os;
635 }
636 
637 const int kUnvisited = 0;
638 const int kOnStack = 1;
639 const int kVisited = 2;
640 
operator <<(std::ostream & os,const AsRPO & ar)641 std::ostream& operator<<(std::ostream& os, const AsRPO& ar) {
642   base::AccountingAllocator allocator;
643   Zone local_zone(&allocator);
644 
645   // Do a post-order depth-first search on the RPO graph. For every node,
646   // print:
647   //
648   //   - the node id
649   //   - the operator mnemonic
650   //   - in square brackets its parameter (if present)
651   //   - in parentheses the list of argument ids and their mnemonics
652   //   - the node type (if it is typed)
653 
654   // Post-order guarantees that all inputs of a node will be printed before
655   // the node itself, if there are no cycles. Any cycles are broken
656   // arbitrarily.
657 
658   ZoneVector<byte> state(ar.graph.NodeCount(), kUnvisited, &local_zone);
659   ZoneStack<Node*> stack(&local_zone);
660 
661   stack.push(ar.graph.end());
662   state[ar.graph.end()->id()] = kOnStack;
663   while (!stack.empty()) {
664     Node* n = stack.top();
665     bool pop = true;
666     for (Node* const i : n->inputs()) {
667       if (state[i->id()] == kUnvisited) {
668         state[i->id()] = kOnStack;
669         stack.push(i);
670         pop = false;
671         break;
672       }
673     }
674     if (pop) {
675       state[n->id()] = kVisited;
676       stack.pop();
677       os << "#" << n->id() << ":" << *n->op() << "(";
678       // Print the inputs.
679       int j = 0;
680       for (Node* const i : n->inputs()) {
681         if (j++ > 0) os << ", ";
682         os << "#" << SafeId(i) << ":" << SafeMnemonic(i);
683       }
684       os << ")";
685       // Print the node type, if any.
686       if (NodeProperties::IsTyped(n)) {
687         os << "  [Type: ";
688         NodeProperties::GetType(n)->PrintTo(os);
689         os << "]";
690       }
691       os << std::endl;
692     }
693   }
694   return os;
695 }
696 }  // namespace compiler
697 }  // namespace internal
698 }  // namespace v8
699