graph-visualizer.cc 21.3 KB
Newer Older
1 2 3 4 5 6
// Copyright 2013 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/compiler/graph-visualizer.h"

7
#include <memory>
8 9 10
#include <sstream>
#include <string>

11
#include "src/code-stubs.h"
12
#include "src/compiler/all-nodes.h"
13 14 15 16 17
#include "src/compiler/graph.h"
#include "src/compiler/node.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/opcodes.h"
#include "src/compiler/operator.h"
18
#include "src/compiler/operator-properties.h"
19 20 21
#include "src/compiler/register-allocator.h"
#include "src/compiler/schedule.h"
#include "src/compiler/scheduler.h"
22
#include "src/interpreter/bytecodes.h"
23 24 25 26 27 28
#include "src/ostreams.h"

namespace v8 {
namespace internal {
namespace compiler {

29 30 31
std::unique_ptr<char[]> GetVisualizerLogFileName(CompilationInfo* info,
                                                 const char* phase,
                                                 const char* suffix) {
32
  EmbeddedVector<char, 256> filename(0);
33
  std::unique_ptr<char[]> debug_name = info->GetDebugName();
34 35 36 37
  if (strlen(debug_name.get()) > 0) {
    SNPrintF(filename, "turbo-%s", debug_name.get());
  } else if (info->has_shared_info()) {
    SNPrintF(filename, "turbo-%p", static_cast<void*>(info));
38 39 40
  } else {
    SNPrintF(filename, "turbo-none-%s", phase);
  }
41 42 43 44 45 46 47 48 49 50 51 52 53 54
  EmbeddedVector<char, 256> source_file(0);
  bool source_available = false;
  if (FLAG_trace_file_names && info->parse_info()) {
    Object* source_name = info->script()->name();
    if (source_name->IsString()) {
      String* str = String::cast(source_name);
      if (str->length() > 0) {
        SNPrintF(source_file, "%s", str->ToCString().get());
        std::replace(source_file.start(),
                     source_file.start() + source_file.length(), '/', '_');
        source_available = true;
      }
    }
  }
55 56 57 58
  std::replace(filename.start(), filename.start() + filename.length(), ' ',
               '_');

  EmbeddedVector<char, 256> full_filename;
59
  if (phase == nullptr && !source_available) {
60
    SNPrintF(full_filename, "%s.%s", filename.start(), suffix);
61
  } else if (phase != nullptr && !source_available) {
62
    SNPrintF(full_filename, "%s-%s.%s", filename.start(), phase, suffix);
63 64 65 66 67 68
  } else if (phase == nullptr && source_available) {
    SNPrintF(full_filename, "%s_%s.%s", filename.start(), source_file.start(),
             suffix);
  } else {
    SNPrintF(full_filename, "%s_%s-%s.%s", filename.start(),
             source_file.start(), phase, suffix);
69
  }
70 71 72 73

  char* buffer = new char[full_filename.length() + 1];
  memcpy(buffer, full_filename.start(), full_filename.length());
  buffer[full_filename.length()] = '\0';
74
  return std::unique_ptr<char[]>(buffer);
75 76 77
}


78
static int SafeId(Node* node) { return node == nullptr ? -1 : node->id(); }
79
static const char* SafeMnemonic(Node* node) {
80
  return node == nullptr ? "null" : node->op()->mnemonic();
81
}
82

83 84
#define DEAD_COLOR "#999999"

85 86
class Escaped {
 public:
87 88 89 90 91 92 93 94 95
  explicit Escaped(const std::ostringstream& os,
                   const char* escaped_chars = "<>|{}")
      : str_(os.str()), escaped_chars_(escaped_chars) {}

  friend std::ostream& operator<<(std::ostream& os, const Escaped& e) {
    for (std::string::const_iterator i = e.str_.begin(); i != e.str_.end();
         ++i) {
      if (e.needs_escape(*i)) os << "\\";
      os << *i;
96 97 98 99 100 101 102 103 104 105 106 107
    }
    return os;
  }

 private:
  bool needs_escape(char ch) const {
    for (size_t i = 0; i < strlen(escaped_chars_); ++i) {
      if (ch == escaped_chars_[i]) return true;
    }
    return false;
  }

108
  const std::string str_;
109 110 111
  const char* const escaped_chars_;
};

112
class JSONGraphNodeWriter {
113
 public:
danno's avatar
danno committed
114 115 116
  JSONGraphNodeWriter(std::ostream& os, Zone* zone, const Graph* graph,
                      const SourcePositionTable* positions)
      : os_(os), all_(zone, graph), positions_(positions), first_node_(true) {}
117

118 119
  void Print() {
    for (Node* const node : all_.live) PrintNode(node);
danno's avatar
danno committed
120
    os_ << "\n";
121
  }
122

123 124 125 126
  void PrintNode(Node* node) {
    if (first_node_) {
      first_node_ = false;
    } else {
danno's avatar
danno committed
127
      os_ << ",\n";
128
    }
129 130 131
    std::ostringstream label, title;
    node->op()->PrintTo(label, Operator::PrintVerbosity::kSilent);
    node->op()->PrintTo(title, Operator::PrintVerbosity::kVerbose);
132
    os_ << "{\"id\":" << SafeId(node) << ",\"label\":\"" << Escaped(label, "\"")
133 134
        << "\""
        << ",\"title\":\"" << Escaped(title, "\"") << "\"";
135
    IrOpcode::Value opcode = node->opcode();
136
    if (IrOpcode::IsPhiOpcode(opcode)) {
137 138 139 140 141 142 143 144 145 146 147 148
      os_ << ",\"rankInputs\":[0," << NodeProperties::FirstControlIndex(node)
          << "]";
      os_ << ",\"rankWithInput\":[" << NodeProperties::FirstControlIndex(node)
          << "]";
    } else if (opcode == IrOpcode::kIfTrue || opcode == IrOpcode::kIfFalse ||
               opcode == IrOpcode::kLoop) {
      os_ << ",\"rankInputs\":[" << NodeProperties::FirstControlIndex(node)
          << "]";
    }
    if (opcode == IrOpcode::kBranch) {
      os_ << ",\"rankInputs\":[0]";
    }
danno's avatar
danno committed
149
    SourcePosition position = positions_->GetSourcePosition(node);
150
    if (position.IsKnown()) {
danno's avatar
danno committed
151 152
      os_ << ",\"pos\":" << position.raw();
    }
153 154 155
    os_ << ",\"opcode\":\"" << IrOpcode::Mnemonic(node->opcode()) << "\"";
    os_ << ",\"control\":" << (NodeProperties::IsControl(node) ? "true"
                                                               : "false");
156
    if (NodeProperties::IsTyped(node)) {
157 158 159 160
      Type* type = NodeProperties::GetType(node);
      std::ostringstream type_out;
      type->PrintTo(type_out);
      os_ << ",\"type\":\"" << Escaped(type_out, "\"") << "\"";
161
    }
162 163
    os_ << "}";
  }
164 165

 private:
166
  std::ostream& os_;
167
  AllNodes all_;
danno's avatar
danno committed
168
  const SourcePositionTable* positions_;
169 170 171 172 173 174
  bool first_node_;

  DISALLOW_COPY_AND_ASSIGN(JSONGraphNodeWriter);
};


175
class JSONGraphEdgeWriter {
176
 public:
177 178
  JSONGraphEdgeWriter(std::ostream& os, Zone* zone, const Graph* graph)
      : os_(os), all_(zone, graph), first_edge_(true) {}
179

180 181
  void Print() {
    for (Node* const node : all_.live) PrintEdges(node);
danno's avatar
danno committed
182
    os_ << "\n";
183 184 185 186 187
  }

  void PrintEdges(Node* node) {
    for (int i = 0; i < node->InputCount(); i++) {
      Node* input = node->InputAt(i);
188
      if (input == nullptr) continue;
189 190 191
      PrintEdge(node, i, input);
    }
  }
192

193 194 195 196
  void PrintEdge(Node* from, int index, Node* to) {
    if (first_edge_) {
      first_edge_ = false;
    } else {
danno's avatar
danno committed
197
      os_ << ",\n";
198
    }
199
    const char* edge_type = nullptr;
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
    if (index < NodeProperties::FirstValueIndex(from)) {
      edge_type = "unknown";
    } else if (index < NodeProperties::FirstContextIndex(from)) {
      edge_type = "value";
    } else if (index < NodeProperties::FirstFrameStateIndex(from)) {
      edge_type = "context";
    } else if (index < NodeProperties::FirstEffectIndex(from)) {
      edge_type = "frame-state";
    } else if (index < NodeProperties::FirstControlIndex(from)) {
      edge_type = "effect";
    } else {
      edge_type = "control";
    }
    os_ << "{\"source\":" << SafeId(to) << ",\"target\":" << SafeId(from)
        << ",\"index\":" << index << ",\"type\":\"" << edge_type << "\"}";
  }
216 217

 private:
218
  std::ostream& os_;
219
  AllNodes all_;
220 221 222 223 224 225
  bool first_edge_;

  DISALLOW_COPY_AND_ASSIGN(JSONGraphEdgeWriter);
};


226
std::ostream& operator<<(std::ostream& os, const AsJSON& ad) {
227 228
  base::AccountingAllocator allocator;
  Zone tmp_zone(&allocator);
danno's avatar
danno committed
229 230 231
  os << "{\n\"nodes\":[";
  JSONGraphNodeWriter(os, &tmp_zone, &ad.graph, ad.positions).Print();
  os << "],\n\"edges\":[";
232 233 234 235 236 237
  JSONGraphEdgeWriter(os, &tmp_zone, &ad.graph).Print();
  os << "]}";
  return os;
}


238 239 240 241 242 243 244 245
class GraphC1Visualizer {
 public:
  GraphC1Visualizer(std::ostream& os, Zone* zone);  // NOLINT

  void PrintCompilation(const CompilationInfo* info);
  void PrintSchedule(const char* phase, const Schedule* schedule,
                     const SourcePositionTable* positions,
                     const InstructionSequence* instructions);
246
  void PrintLiveRanges(const char* phase, const RegisterAllocationData* data);
247 248 249 250 251 252 253
  Zone* zone() const { return zone_; }

 private:
  void PrintIndent();
  void PrintStringProperty(const char* name, const char* value);
  void PrintLongProperty(const char* name, int64_t value);
  void PrintIntProperty(const char* name, int value);
254
  void PrintBlockProperty(const char* name, int rpo_number);
255 256 257
  void PrintNodeId(Node* n);
  void PrintNode(Node* n);
  void PrintInputs(Node* n);
258 259
  template <typename InputIterator>
  void PrintInputs(InputIterator* i, int count, const char* prefix);
260 261
  void PrintType(Node* node);

262 263
  void PrintLiveRange(const LiveRange* range, const char* type, int vreg);
  void PrintLiveRangeChain(const TopLevelLiveRange* range, const char* type);
264

265
  class Tag final BASE_EMBEDDED {
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
   public:
    Tag(GraphC1Visualizer* visualizer, const char* name) {
      name_ = name;
      visualizer_ = visualizer;
      visualizer->PrintIndent();
      visualizer_->os_ << "begin_" << name << "\n";
      visualizer->indent_++;
    }

    ~Tag() {
      visualizer_->indent_--;
      visualizer_->PrintIndent();
      visualizer_->os_ << "end_" << name_ << "\n";
      DCHECK(visualizer_->indent_ >= 0);
    }

   private:
    GraphC1Visualizer* visualizer_;
    const char* name_;
  };

  std::ostream& os_;
  int indent_;
  Zone* zone_;

  DISALLOW_COPY_AND_ASSIGN(GraphC1Visualizer);
};


void GraphC1Visualizer::PrintIndent() {
  for (int i = 0; i < indent_; i++) {
    os_ << "  ";
  }
}


GraphC1Visualizer::GraphC1Visualizer(std::ostream& os, Zone* zone)
    : os_(os), indent_(0), zone_(zone) {}


void GraphC1Visualizer::PrintStringProperty(const char* name,
                                            const char* value) {
  PrintIndent();
  os_ << name << " \"" << value << "\"\n";
}


void GraphC1Visualizer::PrintLongProperty(const char* name, int64_t value) {
  PrintIndent();
  os_ << name << " " << static_cast<int>(value / 1000) << "\n";
}


319
void GraphC1Visualizer::PrintBlockProperty(const char* name, int rpo_number) {
320
  PrintIndent();
321
  os_ << name << " \"B" << rpo_number << "\"\n";
322 323 324 325 326 327 328 329 330 331 332
}


void GraphC1Visualizer::PrintIntProperty(const char* name, int value) {
  PrintIndent();
  os_ << name << " " << value << "\n";
}


void GraphC1Visualizer::PrintCompilation(const CompilationInfo* info) {
  Tag tag(this, "compilation");
333
  std::unique_ptr<char[]> name = info->GetDebugName();
334
  if (info->IsOptimizing()) {
335
    PrintStringProperty("name", name.get());
336
    PrintIndent();
337 338
    os_ << "method \"" << name.get() << ":" << info->optimization_id()
        << "\"\n";
339
  } else {
340
    PrintStringProperty("name", name.get());
341 342 343 344 345 346 347
    PrintStringProperty("method", "stub");
  }
  PrintLongProperty("date",
                    static_cast<int64_t>(base::OS::TimeCurrentMillis()));
}


348
void GraphC1Visualizer::PrintNodeId(Node* n) { os_ << "n" << SafeId(n); }
349 350 351 352 353 354 355 356 357


void GraphC1Visualizer::PrintNode(Node* n) {
  PrintNodeId(n);
  os_ << " " << *n->op() << " ";
  PrintInputs(n);
}


358 359
template <typename InputIterator>
void GraphC1Visualizer::PrintInputs(InputIterator* i, int count,
360 361 362 363 364 365 366 367 368 369 370 371 372 373
                                    const char* prefix) {
  if (count > 0) {
    os_ << prefix;
  }
  while (count > 0) {
    os_ << " ";
    PrintNodeId(**i);
    ++(*i);
    count--;
  }
}


void GraphC1Visualizer::PrintInputs(Node* node) {
danno's avatar
danno committed
374
  auto i = node->inputs().begin();
375
  PrintInputs(&i, node->op()->ValueInputCount(), " ");
376 377 378 379
  PrintInputs(&i, OperatorProperties::GetContextInputCount(node->op()),
              " Ctx:");
  PrintInputs(&i, OperatorProperties::GetFrameStateInputCount(node->op()),
              " FS:");
380 381
  PrintInputs(&i, node->op()->EffectInputCount(), " Eff:");
  PrintInputs(&i, node->op()->ControlInputCount(), " Ctrl:");
382 383 384 385
}


void GraphC1Visualizer::PrintType(Node* node) {
386
  if (NodeProperties::IsTyped(node)) {
387
    Type* type = NodeProperties::GetType(node);
388
    os_ << " type:";
389
    type->PrintTo(os_);
390
  }
391 392 393 394 395 396 397 398 399 400 401 402 403
}


void GraphC1Visualizer::PrintSchedule(const char* phase,
                                      const Schedule* schedule,
                                      const SourcePositionTable* positions,
                                      const InstructionSequence* instructions) {
  Tag tag(this, "cfg");
  PrintStringProperty("name", phase);
  const BasicBlockVector* rpo = schedule->rpo_order();
  for (size_t i = 0; i < rpo->size(); i++) {
    BasicBlock* current = (*rpo)[i];
    Tag block_tag(this, "block");
404
    PrintBlockProperty("name", current->rpo_number());
405 406 407 408 409
    PrintIntProperty("from_bci", -1);
    PrintIntProperty("to_bci", -1);

    PrintIndent();
    os_ << "predecessors";
410
    for (BasicBlock* predecessor : current->predecessors()) {
411
      os_ << " \"B" << predecessor->rpo_number() << "\"";
412 413 414 415 416
    }
    os_ << "\n";

    PrintIndent();
    os_ << "successors";
417
    for (BasicBlock* successor : current->successors()) {
418
      os_ << " \"B" << successor->rpo_number() << "\"";
419 420 421 422 423 424 425 426 427
    }
    os_ << "\n";

    PrintIndent();
    os_ << "xhandlers\n";

    PrintIndent();
    os_ << "flags\n";

428
    if (current->dominator() != nullptr) {
429
      PrintBlockProperty("dominator", current->dominator()->rpo_number());
430 431 432 433
    }

    PrintIntProperty("loop_depth", current->loop_depth());

434
    const InstructionBlock* instruction_block =
435 436
        instructions->InstructionBlockAt(
            RpoNumber::FromInt(current->rpo_number()));
437 438 439
    if (instruction_block->code_start() >= 0) {
      int first_index = instruction_block->first_instruction_index();
      int last_index = instruction_block->last_instruction_index();
440 441
      PrintIntProperty(
          "first_lir_id",
442
          LifetimePosition::GapFromInstructionIndex(first_index).value());
443 444
      PrintIntProperty("last_lir_id",
                       LifetimePosition::InstructionFromInstructionIndex(
445
                           last_index).value());
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
    }

    {
      Tag states_tag(this, "states");
      Tag locals_tag(this, "locals");
      int total = 0;
      for (BasicBlock::const_iterator i = current->begin(); i != current->end();
           ++i) {
        if ((*i)->opcode() == IrOpcode::kPhi) total++;
      }
      PrintIntProperty("size", total);
      PrintStringProperty("method", "None");
      int index = 0;
      for (BasicBlock::const_iterator i = current->begin(); i != current->end();
           ++i) {
        if ((*i)->opcode() != IrOpcode::kPhi) continue;
        PrintIndent();
        os_ << index << " ";
        PrintNodeId(*i);
        os_ << " [";
        PrintInputs(*i);
        os_ << "]\n";
        index++;
      }
    }

    {
      Tag HIR_tag(this, "HIR");
      for (BasicBlock::const_iterator i = current->begin(); i != current->end();
           ++i) {
        Node* node = *i;
        if (node->opcode() == IrOpcode::kPhi) continue;
        int uses = node->UseCount();
        PrintIndent();
        os_ << "0 " << uses << " ";
        PrintNode(node);
        if (FLAG_trace_turbo_types) {
          os_ << " ";
          PrintType(node);
        }
486
        if (positions != nullptr) {
487
          SourcePosition position = positions->GetSourcePosition(node);
488
          if (position.IsKnown()) {
489 490 491 492 493 494 495 496 497 498
            os_ << " pos:" << position.raw();
          }
        }
        os_ << " <|@\n";
      }

      BasicBlock::Control control = current->control();
      if (control != BasicBlock::kNone) {
        PrintIndent();
        os_ << "0 0 ";
499
        if (current->control_input() != nullptr) {
500 501
          PrintNode(current->control_input());
        } else {
502
          os_ << -1 - current->rpo_number() << " Goto";
503 504
        }
        os_ << " ->";
505
        for (BasicBlock* successor : current->successors()) {
506
          os_ << " B" << successor->rpo_number();
507
        }
508
        if (FLAG_trace_turbo_types && current->control_input() != nullptr) {
509 510 511 512 513 514 515
          os_ << " ";
          PrintType(current->control_input());
        }
        os_ << " <|@\n";
      }
    }

516
    if (instructions != nullptr) {
517
      Tag LIR_tag(this, "LIR");
518 519
      for (int j = instruction_block->first_instruction_index();
           j <= instruction_block->last_instruction_index(); j++) {
520
        PrintIndent();
521 522
        PrintableInstruction printable = {RegisterConfiguration::Turbofan(),
                                          instructions->InstructionAt(j)};
523
        os_ << j << " " << printable << " <|@\n";
524 525 526 527 528 529
      }
    }
  }
}


530 531
void GraphC1Visualizer::PrintLiveRanges(const char* phase,
                                        const RegisterAllocationData* data) {
532 533 534
  Tag tag(this, "intervals");
  PrintStringProperty("name", phase);

535
  for (const TopLevelLiveRange* range : data->fixed_double_live_ranges()) {
536
    PrintLiveRangeChain(range, "fixed");
537 538
  }

539
  for (const TopLevelLiveRange* range : data->fixed_live_ranges()) {
540
    PrintLiveRangeChain(range, "fixed");
541 542
  }

543
  for (const TopLevelLiveRange* range : data->live_ranges()) {
544 545 546 547
    PrintLiveRangeChain(range, "object");
  }
}

548
void GraphC1Visualizer::PrintLiveRangeChain(const TopLevelLiveRange* range,
549
                                            const char* type) {
550
  if (range == nullptr || range->IsEmpty()) return;
551
  int vreg = range->vreg();
552 553
  for (const LiveRange* child = range; child != nullptr;
       child = child->next()) {
554
    PrintLiveRange(child, type, vreg);
555 556 557
  }
}

558
void GraphC1Visualizer::PrintLiveRange(const LiveRange* range, const char* type,
559
                                       int vreg) {
560
  if (range != nullptr && !range->IsEmpty()) {
561
    PrintIndent();
562
    os_ << vreg << ":" << range->relative_id() << " " << type;
563
    if (range->HasRegisterAssigned()) {
564
      AllocatedOperand op = AllocatedOperand::cast(range->GetAssignedOperand());
565
      const auto config = RegisterConfiguration::Turbofan();
566
      if (op.IsRegister()) {
567 568
        os_ << " \"" << config->GetGeneralRegisterName(op.register_code())
            << "\"";
569
      } else if (op.IsDoubleRegister()) {
570 571
        os_ << " \"" << config->GetDoubleRegisterName(op.register_code())
            << "\"";
572
      } else {
573
        DCHECK(op.IsFloatRegister());
574 575
        os_ << " \"" << config->GetFloatRegisterName(op.register_code())
            << "\"";
576
      }
577
    } else if (range->spilled()) {
578
      const TopLevelLiveRange* top = range->TopLevel();
579
      int index = -1;
580
      if (top->HasSpillRange()) {
581
        index = kMaxInt;  // This hasn't been set yet.
582 583 584 585
      } else if (top->GetSpillOperand()->IsConstant()) {
        os_ << " \"const(nostack):"
            << ConstantOperand::cast(top->GetSpillOperand())->virtual_register()
            << "\"";
586
      } else {
587
        index = AllocatedOperand::cast(top->GetSpillOperand())->index();
588 589 590
        if (IsFloatingPoint(top->representation())) {
          os_ << " \"fp_stack:" << index << "\"";
        } else {
591 592
          os_ << " \"stack:" << index << "\"";
        }
593 594
      }
    }
595 596

    os_ << " " << vreg;
597 598
    for (const UseInterval* interval = range->first_interval();
         interval != nullptr; interval = interval->next()) {
599 600
      os_ << " [" << interval->start().value() << ", "
          << interval->end().value() << "[";
601 602 603
    }

    UsePosition* current_pos = range->first_pos();
604
    while (current_pos != nullptr) {
605
      if (current_pos->RegisterIsBeneficial() || FLAG_trace_all_uses) {
606
        os_ << " " << current_pos->pos().value() << " M";
607 608 609 610 611 612 613 614 615 616
      }
      current_pos = current_pos->next();
    }

    os_ << " \"\"\n";
  }
}


std::ostream& operator<<(std::ostream& os, const AsC1VCompilation& ac) {
617 618
  base::AccountingAllocator allocator;
  Zone tmp_zone(&allocator);
619 620 621 622 623 624
  GraphC1Visualizer(os, &tmp_zone).PrintCompilation(ac.info_);
  return os;
}


std::ostream& operator<<(std::ostream& os, const AsC1V& ac) {
625 626
  base::AccountingAllocator allocator;
  Zone tmp_zone(&allocator);
627 628 629 630 631 632
  GraphC1Visualizer(os, &tmp_zone)
      .PrintSchedule(ac.phase_, ac.schedule_, ac.positions_, ac.instructions_);
  return os;
}


633 634
std::ostream& operator<<(std::ostream& os,
                         const AsC1VRegisterAllocationData& ac) {
635 636
  base::AccountingAllocator allocator;
  Zone tmp_zone(&allocator);
637
  GraphC1Visualizer(os, &tmp_zone).PrintLiveRanges(ac.phase_, ac.data_);
638 639
  return os;
}
640 641 642 643 644 645

const int kUnvisited = 0;
const int kOnStack = 1;
const int kVisited = 2;

std::ostream& operator<<(std::ostream& os, const AsRPO& ar) {
646 647
  base::AccountingAllocator allocator;
  Zone local_zone(&allocator);
648 649 650 651 652 653 654 655 656 657 658 659 660 661

  // Do a post-order depth-first search on the RPO graph. For every node,
  // print:
  //
  //   - the node id
  //   - the operator mnemonic
  //   - in square brackets its parameter (if present)
  //   - in parentheses the list of argument ids and their mnemonics
  //   - the node type (if it is typed)

  // Post-order guarantees that all inputs of a node will be printed before
  // the node itself, if there are no cycles. Any cycles are broken
  // arbitrarily.

662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
  ZoneVector<byte> state(ar.graph.NodeCount(), kUnvisited, &local_zone);
  ZoneStack<Node*> stack(&local_zone);

  stack.push(ar.graph.end());
  state[ar.graph.end()->id()] = kOnStack;
  while (!stack.empty()) {
    Node* n = stack.top();
    bool pop = true;
    for (Node* const i : n->inputs()) {
      if (state[i->id()] == kUnvisited) {
        state[i->id()] = kOnStack;
        stack.push(i);
        pop = false;
        break;
      }
    }
    if (pop) {
      state[n->id()] = kVisited;
      stack.pop();
681
      os << "#" << n->id() << ":" << *n->op() << "(";
682
      // Print the inputs.
683 684 685 686 687
      int j = 0;
      for (Node* const i : n->inputs()) {
        if (j++ > 0) os << ", ";
        os << "#" << SafeId(i) << ":" << SafeMnemonic(i);
      }
688
      os << ")";
689
      // Print the node type, if any.
690 691 692 693 694 695
      if (NodeProperties::IsTyped(n)) {
        os << "  [Type: ";
        NodeProperties::GetType(n)->PrintTo(os);
        os << "]";
      }
      os << std::endl;
696 697 698 699
    }
  }
  return os;
}
700 701 702
}  // namespace compiler
}  // namespace internal
}  // namespace v8