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