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/compiler/all-nodes.h"
     13 #include "src/compiler/compiler-source-position-table.h"
     14 #include "src/compiler/graph.h"
     15 #include "src/compiler/node-origin-table.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/script-inl.h"
     26 #include "src/objects/shared-function-info.h"
     27 #include "src/optimized-compilation-info.h"
     28 #include "src/ostreams.h"
     29 #include "src/source-position.h"
     30 
     31 namespace v8 {
     32 namespace internal {
     33 namespace compiler {
     34 
     35 const char* get_cached_trace_turbo_filename(OptimizedCompilationInfo* info) {
     36   if (!info->trace_turbo_filename()) {
     37     info->set_trace_turbo_filename(
     38         GetVisualizerLogFileName(info, FLAG_trace_turbo_path, nullptr, "json"));
     39   }
     40   return info->trace_turbo_filename();
     41 }
     42 
     43 TurboJsonFile::TurboJsonFile(OptimizedCompilationInfo* info,
     44                              std::ios_base::openmode mode)
     45     : std::ofstream(get_cached_trace_turbo_filename(info), mode) {}
     46 
     47 TurboJsonFile::~TurboJsonFile() { flush(); }
     48 
     49 std::ostream& operator<<(std::ostream& out,
     50                          const SourcePositionAsJSON& asJSON) {
     51   asJSON.sp.PrintJson(out);
     52   return out;
     53 }
     54 
     55 std::ostream& operator<<(std::ostream& out, const NodeOriginAsJSON& asJSON) {
     56   asJSON.no.PrintJson(out);
     57   return out;
     58 }
     59 
     60 void JsonPrintFunctionSource(std::ostream& os, int source_id,
     61                              std::unique_ptr<char[]> function_name,
     62                              Handle<Script> script, Isolate* isolate,
     63                              Handle<SharedFunctionInfo> shared, bool with_key) {
     64   if (with_key) os << "\"" << source_id << "\" : ";
     65 
     66   os << "{ ";
     67   os << "\"sourceId\": " << source_id;
     68   os << ", \"functionName\": \"" << function_name.get() << "\" ";
     69 
     70   int start = 0;
     71   int end = 0;
     72   if (!script.is_null() && !script->IsUndefined(isolate) && !shared.is_null()) {
     73     Object* source_name = script->name();
     74     os << ", \"sourceName\": \"";
     75     if (source_name->IsString()) {
     76       os << String::cast(source_name)->ToCString().get();
     77     }
     78     os << "\"";
     79     {
     80       DisallowHeapAllocation no_allocation;
     81       start = shared->StartPosition();
     82       end = shared->EndPosition();
     83       os << ", \"sourceText\": \"";
     84       int len = shared->EndPosition() - start;
     85       String::SubStringRange source(String::cast(script->source()), start, len);
     86       for (const auto& c : source) {
     87         os << AsEscapedUC16ForJSON(c);
     88       }
     89       os << "\"";
     90     }
     91   } else {
     92     os << ", \"sourceName\": \"\"";
     93     os << ", \"sourceText\": \"\"";
     94   }
     95   os << ", \"startPosition\": " << start;
     96   os << ", \"endPosition\": " << end;
     97   os << "}";
     98 }
     99 
    100 int SourceIdAssigner::GetIdFor(Handle<SharedFunctionInfo> shared) {
    101   for (unsigned i = 0; i < printed_.size(); i++) {
    102     if (printed_.at(i).is_identical_to(shared)) {
    103       source_ids_.push_back(i);
    104       return i;
    105     }
    106   }
    107   const int source_id = static_cast<int>(printed_.size());
    108   printed_.push_back(shared);
    109   source_ids_.push_back(source_id);
    110   return source_id;
    111 }
    112 
    113 namespace {
    114 
    115 void JsonPrintInlinedFunctionInfo(
    116     std::ostream& os, int source_id, int inlining_id,
    117     const OptimizedCompilationInfo::InlinedFunctionHolder& h) {
    118   os << "\"" << inlining_id << "\" : ";
    119   os << "{ \"inliningId\" : " << inlining_id;
    120   os << ", \"sourceId\" : " << source_id;
    121   const SourcePosition position = h.position.position;
    122   if (position.IsKnown()) {
    123     os << ", \"inliningPosition\" : " << AsJSON(position);
    124   }
    125   os << "}";
    126 }
    127 
    128 }  // namespace
    129 
    130 void JsonPrintAllSourceWithPositions(std::ostream& os,
    131                                      OptimizedCompilationInfo* info,
    132                                      Isolate* isolate) {
    133   AllowDeferredHandleDereference allow_deference_for_print_code;
    134   os << "\"sources\" : {";
    135   Handle<Script> script =
    136       (info->shared_info().is_null() || !info->shared_info()->script())
    137           ? Handle<Script>()
    138           : handle(Script::cast(info->shared_info()->script()), isolate);
    139   JsonPrintFunctionSource(os, -1,
    140                           info->shared_info().is_null()
    141                               ? std::unique_ptr<char[]>(new char[1]{0})
    142                               : info->shared_info()->DebugName()->ToCString(),
    143                           script, isolate, info->shared_info(), true);
    144   const auto& inlined = info->inlined_functions();
    145   SourceIdAssigner id_assigner(info->inlined_functions().size());
    146   for (unsigned id = 0; id < inlined.size(); id++) {
    147     os << ", ";
    148     Handle<SharedFunctionInfo> shared = inlined[id].shared_info;
    149     const int source_id = id_assigner.GetIdFor(shared);
    150     JsonPrintFunctionSource(os, source_id, shared->DebugName()->ToCString(),
    151                             handle(Script::cast(shared->script()), isolate),
    152                             isolate, shared, true);
    153   }
    154   os << "}, ";
    155   os << "\"inlinings\" : {";
    156   bool need_comma = false;
    157   for (unsigned id = 0; id < inlined.size(); id++) {
    158     if (need_comma) os << ", ";
    159     const int source_id = id_assigner.GetIdAt(id);
    160     JsonPrintInlinedFunctionInfo(os, source_id, id, inlined[id]);
    161     need_comma = true;
    162   }
    163   os << "}";
    164 }
    165 
    166 std::unique_ptr<char[]> GetVisualizerLogFileName(OptimizedCompilationInfo* info,
    167                                                  const char* optional_base_dir,
    168                                                  const char* phase,
    169                                                  const char* suffix) {
    170   EmbeddedVector<char, 256> filename(0);
    171   std::unique_ptr<char[]> debug_name = info->GetDebugName();
    172   int optimization_id = info->IsOptimizing() ? info->optimization_id() : 0;
    173   if (strlen(debug_name.get()) > 0) {
    174     SNPrintF(filename, "turbo-%s-%i", debug_name.get(), optimization_id);
    175   } else if (info->has_shared_info()) {
    176     SNPrintF(filename, "turbo-%p-%i",
    177              reinterpret_cast<void*>(info->shared_info()->address()),
    178              optimization_id);
    179   } else {
    180     SNPrintF(filename, "turbo-none-%i", optimization_id);
    181   }
    182   EmbeddedVector<char, 256> source_file(0);
    183   bool source_available = false;
    184   if (FLAG_trace_file_names && info->has_shared_info() &&
    185       info->shared_info()->script()->IsScript()) {
    186     Object* source_name = Script::cast(info->shared_info()->script())->name();
    187     if (source_name->IsString()) {
    188       String* str = String::cast(source_name);
    189       if (str->length() > 0) {
    190         SNPrintF(source_file, "%s", str->ToCString().get());
    191         std::replace(source_file.start(),
    192                      source_file.start() + source_file.length(), '/', '_');
    193         source_available = true;
    194       }
    195     }
    196   }
    197   std::replace(filename.start(), filename.start() + filename.length(), ' ',
    198                '_');
    199 
    200   EmbeddedVector<char, 256> base_dir;
    201   if (optional_base_dir != nullptr) {
    202     SNPrintF(base_dir, "%s%c", optional_base_dir,
    203              base::OS::DirectorySeparator());
    204   } else {
    205     base_dir[0] = '\0';
    206   }
    207 
    208   EmbeddedVector<char, 256> full_filename;
    209   if (phase == nullptr && !source_available) {
    210     SNPrintF(full_filename, "%s%s.%s", base_dir.start(), filename.start(),
    211              suffix);
    212   } else if (phase != nullptr && !source_available) {
    213     SNPrintF(full_filename, "%s%s-%s.%s", base_dir.start(), filename.start(),
    214              phase, suffix);
    215   } else if (phase == nullptr && source_available) {
    216     SNPrintF(full_filename, "%s%s_%s.%s", base_dir.start(), filename.start(),
    217              source_file.start(), suffix);
    218   } else {
    219     SNPrintF(full_filename, "%s%s_%s-%s.%s", base_dir.start(), filename.start(),
    220              source_file.start(), phase, suffix);
    221   }
    222 
    223   char* buffer = new char[full_filename.length() + 1];
    224   memcpy(buffer, full_filename.start(), full_filename.length());
    225   buffer[full_filename.length()] = '\0';
    226   return std::unique_ptr<char[]>(buffer);
    227 }
    228 
    229 
    230 static int SafeId(Node* node) { return node == nullptr ? -1 : node->id(); }
    231 static const char* SafeMnemonic(Node* node) {
    232   return node == nullptr ? "null" : node->op()->mnemonic();
    233 }
    234 
    235 class JSONEscaped {
    236  public:
    237   explicit JSONEscaped(const std::ostringstream& os) : str_(os.str()) {}
    238 
    239   friend std::ostream& operator<<(std::ostream& os, const JSONEscaped& e) {
    240     for (char c : e.str_) PipeCharacter(os, c);
    241     return os;
    242   }
    243 
    244  private:
    245   static std::ostream& PipeCharacter(std::ostream& os, char c) {
    246     if (c == '"') return os << "\\\"";
    247     if (c == '\\') return os << "\\\\";
    248     if (c == '\b') return os << "\\b";
    249     if (c == '\f') return os << "\\f";
    250     if (c == '\n') return os << "\\n";
    251     if (c == '\r') return os << "\\r";
    252     if (c == '\t') return os << "\\t";
    253     return os << c;
    254   }
    255 
    256   const std::string str_;
    257 };
    258 
    259 class JSONGraphNodeWriter {
    260  public:
    261   JSONGraphNodeWriter(std::ostream& os, Zone* zone, const Graph* graph,
    262                       const SourcePositionTable* positions,
    263                       const NodeOriginTable* origins)
    264       : os_(os),
    265         all_(zone, graph, false),
    266         live_(zone, graph, true),
    267         positions_(positions),
    268         origins_(origins),
    269         first_node_(true) {}
    270 
    271   void Print() {
    272     for (Node* const node : all_.reachable) PrintNode(node);
    273     os_ << "\n";
    274   }
    275 
    276   void PrintNode(Node* node) {
    277     if (first_node_) {
    278       first_node_ = false;
    279     } else {
    280       os_ << ",\n";
    281     }
    282     std::ostringstream label, title, properties;
    283     node->op()->PrintTo(label, Operator::PrintVerbosity::kSilent);
    284     node->op()->PrintTo(title, Operator::PrintVerbosity::kVerbose);
    285     node->op()->PrintPropsTo(properties);
    286     os_ << "{\"id\":" << SafeId(node) << ",\"label\":\"" << JSONEscaped(label)
    287         << "\""
    288         << ",\"title\":\"" << JSONEscaped(title) << "\""
    289         << ",\"live\": " << (live_.IsLive(node) ? "true" : "false")
    290         << ",\"properties\":\"" << JSONEscaped(properties) << "\"";
    291     IrOpcode::Value opcode = node->opcode();
    292     if (IrOpcode::IsPhiOpcode(opcode)) {
    293       os_ << ",\"rankInputs\":[0," << NodeProperties::FirstControlIndex(node)
    294           << "]";
    295       os_ << ",\"rankWithInput\":[" << NodeProperties::FirstControlIndex(node)
    296           << "]";
    297     } else if (opcode == IrOpcode::kIfTrue || opcode == IrOpcode::kIfFalse ||
    298                opcode == IrOpcode::kLoop) {
    299       os_ << ",\"rankInputs\":[" << NodeProperties::FirstControlIndex(node)
    300           << "]";
    301     }
    302     if (opcode == IrOpcode::kBranch) {
    303       os_ << ",\"rankInputs\":[0]";
    304     }
    305     SourcePosition position = positions_->GetSourcePosition(node);
    306     if (position.IsKnown()) {
    307       os_ << ", \"sourcePosition\" : " << AsJSON(position);
    308     }
    309     if (origins_) {
    310       NodeOrigin origin = origins_->GetNodeOrigin(node);
    311       if (origin.IsKnown()) {
    312         os_ << ", \"origin\" : " << AsJSON(origin);
    313       }
    314     }
    315     os_ << ",\"opcode\":\"" << IrOpcode::Mnemonic(node->opcode()) << "\"";
    316     os_ << ",\"control\":" << (NodeProperties::IsControl(node) ? "true"
    317                                                                : "false");
    318     os_ << ",\"opinfo\":\"" << node->op()->ValueInputCount() << " v "
    319         << node->op()->EffectInputCount() << " eff "
    320         << node->op()->ControlInputCount() << " ctrl in, "
    321         << node->op()->ValueOutputCount() << " v "
    322         << node->op()->EffectOutputCount() << " eff "
    323         << node->op()->ControlOutputCount() << " ctrl out\"";
    324     if (NodeProperties::IsTyped(node)) {
    325       Type type = NodeProperties::GetType(node);
    326       std::ostringstream type_out;
    327       type.PrintTo(type_out);
    328       os_ << ",\"type\":\"" << JSONEscaped(type_out) << "\"";
    329     }
    330     os_ << "}";
    331   }
    332 
    333  private:
    334   std::ostream& os_;
    335   AllNodes all_;
    336   AllNodes live_;
    337   const SourcePositionTable* positions_;
    338   const NodeOriginTable* origins_;
    339   bool first_node_;
    340 
    341   DISALLOW_COPY_AND_ASSIGN(JSONGraphNodeWriter);
    342 };
    343 
    344 
    345 class JSONGraphEdgeWriter {
    346  public:
    347   JSONGraphEdgeWriter(std::ostream& os, Zone* zone, const Graph* graph)
    348       : os_(os), all_(zone, graph, false), first_edge_(true) {}
    349 
    350   void Print() {
    351     for (Node* const node : all_.reachable) PrintEdges(node);
    352     os_ << "\n";
    353   }
    354 
    355   void PrintEdges(Node* node) {
    356     for (int i = 0; i < node->InputCount(); i++) {
    357       Node* input = node->InputAt(i);
    358       if (input == nullptr) continue;
    359       PrintEdge(node, i, input);
    360     }
    361   }
    362 
    363   void PrintEdge(Node* from, int index, Node* to) {
    364     if (first_edge_) {
    365       first_edge_ = false;
    366     } else {
    367       os_ << ",\n";
    368     }
    369     const char* edge_type = nullptr;
    370     if (index < NodeProperties::FirstValueIndex(from)) {
    371       edge_type = "unknown";
    372     } else if (index < NodeProperties::FirstContextIndex(from)) {
    373       edge_type = "value";
    374     } else if (index < NodeProperties::FirstFrameStateIndex(from)) {
    375       edge_type = "context";
    376     } else if (index < NodeProperties::FirstEffectIndex(from)) {
    377       edge_type = "frame-state";
    378     } else if (index < NodeProperties::FirstControlIndex(from)) {
    379       edge_type = "effect";
    380     } else {
    381       edge_type = "control";
    382     }
    383     os_ << "{\"source\":" << SafeId(to) << ",\"target\":" << SafeId(from)
    384         << ",\"index\":" << index << ",\"type\":\"" << edge_type << "\"}";
    385   }
    386 
    387  private:
    388   std::ostream& os_;
    389   AllNodes all_;
    390   bool first_edge_;
    391 
    392   DISALLOW_COPY_AND_ASSIGN(JSONGraphEdgeWriter);
    393 };
    394 
    395 std::ostream& operator<<(std::ostream& os, const GraphAsJSON& ad) {
    396   AccountingAllocator allocator;
    397   Zone tmp_zone(&allocator, ZONE_NAME);
    398   os << "{\n\"nodes\":[";
    399   JSONGraphNodeWriter(os, &tmp_zone, &ad.graph, ad.positions, ad.origins)
    400       .Print();
    401   os << "],\n\"edges\":[";
    402   JSONGraphEdgeWriter(os, &tmp_zone, &ad.graph).Print();
    403   os << "]}";
    404   return os;
    405 }
    406 
    407 
    408 class GraphC1Visualizer {
    409  public:
    410   GraphC1Visualizer(std::ostream& os, Zone* zone);  // NOLINT
    411 
    412   void PrintCompilation(const OptimizedCompilationInfo* info);
    413   void PrintSchedule(const char* phase, const Schedule* schedule,
    414                      const SourcePositionTable* positions,
    415                      const InstructionSequence* instructions);
    416   void PrintLiveRanges(const char* phase, const RegisterAllocationData* data);
    417   Zone* zone() const { return zone_; }
    418 
    419  private:
    420   void PrintIndent();
    421   void PrintStringProperty(const char* name, const char* value);
    422   void PrintLongProperty(const char* name, int64_t value);
    423   void PrintIntProperty(const char* name, int value);
    424   void PrintBlockProperty(const char* name, int rpo_number);
    425   void PrintNodeId(Node* n);
    426   void PrintNode(Node* n);
    427   void PrintInputs(Node* n);
    428   template <typename InputIterator>
    429   void PrintInputs(InputIterator* i, int count, const char* prefix);
    430   void PrintType(Node* node);
    431 
    432   void PrintLiveRange(const LiveRange* range, const char* type, int vreg);
    433   void PrintLiveRangeChain(const TopLevelLiveRange* range, const char* type);
    434 
    435   class Tag final BASE_EMBEDDED {
    436    public:
    437     Tag(GraphC1Visualizer* visualizer, const char* name) {
    438       name_ = name;
    439       visualizer_ = visualizer;
    440       visualizer->PrintIndent();
    441       visualizer_->os_ << "begin_" << name << "\n";
    442       visualizer->indent_++;
    443     }
    444 
    445     ~Tag() {
    446       visualizer_->indent_--;
    447       visualizer_->PrintIndent();
    448       visualizer_->os_ << "end_" << name_ << "\n";
    449       DCHECK_LE(0, visualizer_->indent_);
    450     }
    451 
    452    private:
    453     GraphC1Visualizer* visualizer_;
    454     const char* name_;
    455   };
    456 
    457   std::ostream& os_;
    458   int indent_;
    459   Zone* zone_;
    460 
    461   DISALLOW_COPY_AND_ASSIGN(GraphC1Visualizer);
    462 };
    463 
    464 
    465 void GraphC1Visualizer::PrintIndent() {
    466   for (int i = 0; i < indent_; i++) {
    467     os_ << "  ";
    468   }
    469 }
    470 
    471 
    472 GraphC1Visualizer::GraphC1Visualizer(std::ostream& os, Zone* zone)
    473     : os_(os), indent_(0), zone_(zone) {}
    474 
    475 
    476 void GraphC1Visualizer::PrintStringProperty(const char* name,
    477                                             const char* value) {
    478   PrintIndent();
    479   os_ << name << " \"" << value << "\"\n";
    480 }
    481 
    482 
    483 void GraphC1Visualizer::PrintLongProperty(const char* name, int64_t value) {
    484   PrintIndent();
    485   os_ << name << " " << static_cast<int>(value / 1000) << "\n";
    486 }
    487 
    488 
    489 void GraphC1Visualizer::PrintBlockProperty(const char* name, int rpo_number) {
    490   PrintIndent();
    491   os_ << name << " \"B" << rpo_number << "\"\n";
    492 }
    493 
    494 
    495 void GraphC1Visualizer::PrintIntProperty(const char* name, int value) {
    496   PrintIndent();
    497   os_ << name << " " << value << "\n";
    498 }
    499 
    500 void GraphC1Visualizer::PrintCompilation(const OptimizedCompilationInfo* info) {
    501   Tag tag(this, "compilation");
    502   std::unique_ptr<char[]> name = info->GetDebugName();
    503   if (info->IsOptimizing()) {
    504     PrintStringProperty("name", name.get());
    505     PrintIndent();
    506     os_ << "method \"" << name.get() << ":" << info->optimization_id()
    507         << "\"\n";
    508   } else {
    509     PrintStringProperty("name", name.get());
    510     PrintStringProperty("method", "stub");
    511   }
    512   PrintLongProperty(
    513       "date",
    514       static_cast<int64_t>(V8::GetCurrentPlatform()->CurrentClockTimeMillis()));
    515 }
    516 
    517 
    518 void GraphC1Visualizer::PrintNodeId(Node* n) { os_ << "n" << SafeId(n); }
    519 
    520 
    521 void GraphC1Visualizer::PrintNode(Node* n) {
    522   PrintNodeId(n);
    523   os_ << " " << *n->op() << " ";
    524   PrintInputs(n);
    525 }
    526 
    527 
    528 template <typename InputIterator>
    529 void GraphC1Visualizer::PrintInputs(InputIterator* i, int count,
    530                                     const char* prefix) {
    531   if (count > 0) {
    532     os_ << prefix;
    533   }
    534   while (count > 0) {
    535     os_ << " ";
    536     PrintNodeId(**i);
    537     ++(*i);
    538     count--;
    539   }
    540 }
    541 
    542 
    543 void GraphC1Visualizer::PrintInputs(Node* node) {
    544   auto i = node->inputs().begin();
    545   PrintInputs(&i, node->op()->ValueInputCount(), " ");
    546   PrintInputs(&i, OperatorProperties::GetContextInputCount(node->op()),
    547               " Ctx:");
    548   PrintInputs(&i, OperatorProperties::GetFrameStateInputCount(node->op()),
    549               " FS:");
    550   PrintInputs(&i, node->op()->EffectInputCount(), " Eff:");
    551   PrintInputs(&i, node->op()->ControlInputCount(), " Ctrl:");
    552 }
    553 
    554 
    555 void GraphC1Visualizer::PrintType(Node* node) {
    556   if (NodeProperties::IsTyped(node)) {
    557     Type type = NodeProperties::GetType(node);
    558     os_ << " type:" << type;
    559   }
    560 }
    561 
    562 
    563 void GraphC1Visualizer::PrintSchedule(const char* phase,
    564                                       const Schedule* schedule,
    565                                       const SourcePositionTable* positions,
    566                                       const InstructionSequence* instructions) {
    567   Tag tag(this, "cfg");
    568   PrintStringProperty("name", phase);
    569   const BasicBlockVector* rpo = schedule->rpo_order();
    570   for (size_t i = 0; i < rpo->size(); i++) {
    571     BasicBlock* current = (*rpo)[i];
    572     Tag block_tag(this, "block");
    573     PrintBlockProperty("name", current->rpo_number());
    574     PrintIntProperty("from_bci", -1);
    575     PrintIntProperty("to_bci", -1);
    576 
    577     PrintIndent();
    578     os_ << "predecessors";
    579     for (BasicBlock* predecessor : current->predecessors()) {
    580       os_ << " \"B" << predecessor->rpo_number() << "\"";
    581     }
    582     os_ << "\n";
    583 
    584     PrintIndent();
    585     os_ << "successors";
    586     for (BasicBlock* successor : current->successors()) {
    587       os_ << " \"B" << successor->rpo_number() << "\"";
    588     }
    589     os_ << "\n";
    590 
    591     PrintIndent();
    592     os_ << "xhandlers\n";
    593 
    594     PrintIndent();
    595     os_ << "flags\n";
    596 
    597     if (current->dominator() != nullptr) {
    598       PrintBlockProperty("dominator", current->dominator()->rpo_number());
    599     }
    600 
    601     PrintIntProperty("loop_depth", current->loop_depth());
    602 
    603     const InstructionBlock* instruction_block =
    604         instructions->InstructionBlockAt(
    605             RpoNumber::FromInt(current->rpo_number()));
    606     if (instruction_block->code_start() >= 0) {
    607       int first_index = instruction_block->first_instruction_index();
    608       int last_index = instruction_block->last_instruction_index();
    609       PrintIntProperty(
    610           "first_lir_id",
    611           LifetimePosition::GapFromInstructionIndex(first_index).value());
    612       PrintIntProperty("last_lir_id",
    613                        LifetimePosition::InstructionFromInstructionIndex(
    614                            last_index).value());
    615     }
    616 
    617     {
    618       Tag states_tag(this, "states");
    619       Tag locals_tag(this, "locals");
    620       int total = 0;
    621       for (BasicBlock::const_iterator i = current->begin(); i != current->end();
    622            ++i) {
    623         if ((*i)->opcode() == IrOpcode::kPhi) total++;
    624       }
    625       PrintIntProperty("size", total);
    626       PrintStringProperty("method", "None");
    627       int index = 0;
    628       for (BasicBlock::const_iterator i = current->begin(); i != current->end();
    629            ++i) {
    630         if ((*i)->opcode() != IrOpcode::kPhi) continue;
    631         PrintIndent();
    632         os_ << index << " ";
    633         PrintNodeId(*i);
    634         os_ << " [";
    635         PrintInputs(*i);
    636         os_ << "]\n";
    637         index++;
    638       }
    639     }
    640 
    641     {
    642       Tag HIR_tag(this, "HIR");
    643       for (BasicBlock::const_iterator i = current->begin(); i != current->end();
    644            ++i) {
    645         Node* node = *i;
    646         if (node->opcode() == IrOpcode::kPhi) continue;
    647         int uses = node->UseCount();
    648         PrintIndent();
    649         os_ << "0 " << uses << " ";
    650         PrintNode(node);
    651         if (FLAG_trace_turbo_types) {
    652           os_ << " ";
    653           PrintType(node);
    654         }
    655         if (positions != nullptr) {
    656           SourcePosition position = positions->GetSourcePosition(node);
    657           if (position.IsKnown()) {
    658             os_ << " pos:";
    659             if (position.isInlined()) {
    660               os_ << "inlining(" << position.InliningId() << "),";
    661             }
    662             os_ << position.ScriptOffset();
    663           }
    664         }
    665         os_ << " <|@\n";
    666       }
    667 
    668       BasicBlock::Control control = current->control();
    669       if (control != BasicBlock::kNone) {
    670         PrintIndent();
    671         os_ << "0 0 ";
    672         if (current->control_input() != nullptr) {
    673           PrintNode(current->control_input());
    674         } else {
    675           os_ << -1 - current->rpo_number() << " Goto";
    676         }
    677         os_ << " ->";
    678         for (BasicBlock* successor : current->successors()) {
    679           os_ << " B" << successor->rpo_number();
    680         }
    681         if (FLAG_trace_turbo_types && current->control_input() != nullptr) {
    682           os_ << " ";
    683           PrintType(current->control_input());
    684         }
    685         os_ << " <|@\n";
    686       }
    687     }
    688 
    689     if (instructions != nullptr) {
    690       Tag LIR_tag(this, "LIR");
    691       for (int j = instruction_block->first_instruction_index();
    692            j <= instruction_block->last_instruction_index(); j++) {
    693         PrintIndent();
    694         PrintableInstruction printable = {RegisterConfiguration::Default(),
    695                                           instructions->InstructionAt(j)};
    696         os_ << j << " " << printable << " <|@\n";
    697       }
    698     }
    699   }
    700 }
    701 
    702 
    703 void GraphC1Visualizer::PrintLiveRanges(const char* phase,
    704                                         const RegisterAllocationData* data) {
    705   Tag tag(this, "intervals");
    706   PrintStringProperty("name", phase);
    707 
    708   for (const TopLevelLiveRange* range : data->fixed_double_live_ranges()) {
    709     PrintLiveRangeChain(range, "fixed");
    710   }
    711 
    712   for (const TopLevelLiveRange* range : data->fixed_live_ranges()) {
    713     PrintLiveRangeChain(range, "fixed");
    714   }
    715 
    716   for (const TopLevelLiveRange* range : data->live_ranges()) {
    717     PrintLiveRangeChain(range, "object");
    718   }
    719 }
    720 
    721 void GraphC1Visualizer::PrintLiveRangeChain(const TopLevelLiveRange* range,
    722                                             const char* type) {
    723   if (range == nullptr || range->IsEmpty()) return;
    724   int vreg = range->vreg();
    725   for (const LiveRange* child = range; child != nullptr;
    726        child = child->next()) {
    727     PrintLiveRange(child, type, vreg);
    728   }
    729 }
    730 
    731 void GraphC1Visualizer::PrintLiveRange(const LiveRange* range, const char* type,
    732                                        int vreg) {
    733   if (range != nullptr && !range->IsEmpty()) {
    734     PrintIndent();
    735     os_ << vreg << ":" << range->relative_id() << " " << type;
    736     if (range->HasRegisterAssigned()) {
    737       AllocatedOperand op = AllocatedOperand::cast(range->GetAssignedOperand());
    738       const auto config = RegisterConfiguration::Default();
    739       if (op.IsRegister()) {
    740         os_ << " \"" << config->GetGeneralRegisterName(op.register_code())
    741             << "\"";
    742       } else if (op.IsDoubleRegister()) {
    743         os_ << " \"" << config->GetDoubleRegisterName(op.register_code())
    744             << "\"";
    745       } else {
    746         DCHECK(op.IsFloatRegister());
    747         os_ << " \"" << config->GetFloatRegisterName(op.register_code())
    748             << "\"";
    749       }
    750     } else if (range->spilled()) {
    751       const TopLevelLiveRange* top = range->TopLevel();
    752       int index = -1;
    753       if (top->HasSpillRange()) {
    754         index = kMaxInt;  // This hasn't been set yet.
    755       } else if (top->GetSpillOperand()->IsConstant()) {
    756         os_ << " \"const(nostack):"
    757             << ConstantOperand::cast(top->GetSpillOperand())->virtual_register()
    758             << "\"";
    759       } else {
    760         index = AllocatedOperand::cast(top->GetSpillOperand())->index();
    761         if (IsFloatingPoint(top->representation())) {
    762           os_ << " \"fp_stack:" << index << "\"";
    763         } else {
    764           os_ << " \"stack:" << index << "\"";
    765         }
    766       }
    767     }
    768 
    769     os_ << " " << vreg;
    770     for (const UseInterval* interval = range->first_interval();
    771          interval != nullptr; interval = interval->next()) {
    772       os_ << " [" << interval->start().value() << ", "
    773           << interval->end().value() << "[";
    774     }
    775 
    776     UsePosition* current_pos = range->first_pos();
    777     while (current_pos != nullptr) {
    778       if (current_pos->RegisterIsBeneficial() || FLAG_trace_all_uses) {
    779         os_ << " " << current_pos->pos().value() << " M";
    780       }
    781       current_pos = current_pos->next();
    782     }
    783 
    784     os_ << " \"\"\n";
    785   }
    786 }
    787 
    788 
    789 std::ostream& operator<<(std::ostream& os, const AsC1VCompilation& ac) {
    790   AccountingAllocator allocator;
    791   Zone tmp_zone(&allocator, ZONE_NAME);
    792   GraphC1Visualizer(os, &tmp_zone).PrintCompilation(ac.info_);
    793   return os;
    794 }
    795 
    796 
    797 std::ostream& operator<<(std::ostream& os, const AsC1V& ac) {
    798   AccountingAllocator allocator;
    799   Zone tmp_zone(&allocator, ZONE_NAME);
    800   GraphC1Visualizer(os, &tmp_zone)
    801       .PrintSchedule(ac.phase_, ac.schedule_, ac.positions_, ac.instructions_);
    802   return os;
    803 }
    804 
    805 
    806 std::ostream& operator<<(std::ostream& os,
    807                          const AsC1VRegisterAllocationData& ac) {
    808   AccountingAllocator allocator;
    809   Zone tmp_zone(&allocator, ZONE_NAME);
    810   GraphC1Visualizer(os, &tmp_zone).PrintLiveRanges(ac.phase_, ac.data_);
    811   return os;
    812 }
    813 
    814 const int kUnvisited = 0;
    815 const int kOnStack = 1;
    816 const int kVisited = 2;
    817 
    818 std::ostream& operator<<(std::ostream& os, const AsRPO& ar) {
    819   AccountingAllocator allocator;
    820   Zone local_zone(&allocator, ZONE_NAME);
    821 
    822   // Do a post-order depth-first search on the RPO graph. For every node,
    823   // print:
    824   //
    825   //   - the node id
    826   //   - the operator mnemonic
    827   //   - in square brackets its parameter (if present)
    828   //   - in parentheses the list of argument ids and their mnemonics
    829   //   - the node type (if it is typed)
    830 
    831   // Post-order guarantees that all inputs of a node will be printed before
    832   // the node itself, if there are no cycles. Any cycles are broken
    833   // arbitrarily.
    834 
    835   ZoneVector<byte> state(ar.graph.NodeCount(), kUnvisited, &local_zone);
    836   ZoneStack<Node*> stack(&local_zone);
    837 
    838   stack.push(ar.graph.end());
    839   state[ar.graph.end()->id()] = kOnStack;
    840   while (!stack.empty()) {
    841     Node* n = stack.top();
    842     bool pop = true;
    843     for (Node* const i : n->inputs()) {
    844       if (state[i->id()] == kUnvisited) {
    845         state[i->id()] = kOnStack;
    846         stack.push(i);
    847         pop = false;
    848         break;
    849       }
    850     }
    851     if (pop) {
    852       state[n->id()] = kVisited;
    853       stack.pop();
    854       os << "#" << n->id() << ":" << *n->op() << "(";
    855       // Print the inputs.
    856       int j = 0;
    857       for (Node* const i : n->inputs()) {
    858         if (j++ > 0) os << ", ";
    859         os << "#" << SafeId(i) << ":" << SafeMnemonic(i);
    860       }
    861       os << ")";
    862       // Print the node type, if any.
    863       if (NodeProperties::IsTyped(n)) {
    864         os << "  [Type: " << NodeProperties::GetType(n) << "]";
    865       }
    866       os << std::endl;
    867     }
    868   }
    869   return os;
    870 }
    871 
    872 namespace {
    873 
    874 void PrintIndent(std::ostream& os, int indent) {
    875   os << "     ";
    876   for (int i = 0; i < indent; i++) {
    877     os << ". ";
    878   }
    879 }
    880 
    881 void PrintScheduledNode(std::ostream& os, int indent, Node* n) {
    882   PrintIndent(os, indent);
    883   os << "#" << n->id() << ":" << *n->op() << "(";
    884   // Print the inputs.
    885   int j = 0;
    886   for (Node* const i : n->inputs()) {
    887     if (j++ > 0) os << ", ";
    888     os << "#" << SafeId(i) << ":" << SafeMnemonic(i);
    889   }
    890   os << ")";
    891   // Print the node type, if any.
    892   if (NodeProperties::IsTyped(n)) {
    893     os << "  [Type: " << NodeProperties::GetType(n) << "]";
    894   }
    895 }
    896 
    897 void PrintScheduledGraph(std::ostream& os, const Schedule* schedule) {
    898   const BasicBlockVector* rpo = schedule->rpo_order();
    899   for (size_t i = 0; i < rpo->size(); i++) {
    900     BasicBlock* current = (*rpo)[i];
    901     int indent = current->loop_depth();
    902 
    903     os << "  + Block B" << current->rpo_number() << " (pred:";
    904     for (BasicBlock* predecessor : current->predecessors()) {
    905       os << " B" << predecessor->rpo_number();
    906     }
    907     if (current->IsLoopHeader()) {
    908       os << ", loop until B" << current->loop_end()->rpo_number();
    909     } else if (current->loop_header()) {
    910       os << ", in loop B" << current->loop_header()->rpo_number();
    911     }
    912     os << ")" << std::endl;
    913 
    914     for (BasicBlock::const_iterator i = current->begin(); i != current->end();
    915          ++i) {
    916       Node* node = *i;
    917       PrintScheduledNode(os, indent, node);
    918       os << std::endl;
    919     }
    920 
    921     if (current->SuccessorCount() > 0) {
    922       if (current->control_input() != nullptr) {
    923         PrintScheduledNode(os, indent, current->control_input());
    924       } else {
    925         PrintIndent(os, indent);
    926         os << "Goto";
    927       }
    928       os << " ->";
    929 
    930       bool isFirst = true;
    931       for (BasicBlock* successor : current->successors()) {
    932         if (isFirst) {
    933           isFirst = false;
    934         } else {
    935           os << ",";
    936         }
    937         os << " B" << successor->rpo_number();
    938       }
    939       os << std::endl;
    940     } else {
    941       DCHECK_NULL(current->control_input());
    942     }
    943   }
    944 }
    945 
    946 }  // namespace
    947 
    948 std::ostream& operator<<(std::ostream& os, const AsScheduledGraph& scheduled) {
    949   PrintScheduledGraph(os, scheduled.schedule);
    950   return os;
    951 }
    952 
    953 }  // namespace compiler
    954 }  // namespace internal
    955 }  // namespace v8
    956