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