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