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 "src/compiler/generic-algorithm.h"
      8 #include "src/compiler/generic-node.h"
      9 #include "src/compiler/generic-node-inl.h"
     10 #include "src/compiler/graph.h"
     11 #include "src/compiler/graph-inl.h"
     12 #include "src/compiler/node.h"
     13 #include "src/compiler/node-properties.h"
     14 #include "src/compiler/node-properties-inl.h"
     15 #include "src/compiler/opcodes.h"
     16 #include "src/compiler/operator.h"
     17 #include "src/ostreams.h"
     18 
     19 namespace v8 {
     20 namespace internal {
     21 namespace compiler {
     22 
     23 #define DEAD_COLOR "#999999"
     24 
     25 class GraphVisualizer : public NullNodeVisitor {
     26  public:
     27   GraphVisualizer(OStream& os, Zone* zone, const Graph* graph);  // NOLINT
     28 
     29   void Print();
     30 
     31   GenericGraphVisit::Control Pre(Node* node);
     32   GenericGraphVisit::Control PreEdge(Node* from, int index, Node* to);
     33 
     34  private:
     35   void AnnotateNode(Node* node);
     36   void PrintEdge(Node::Edge edge);
     37 
     38   Zone* zone_;
     39   NodeSet all_nodes_;
     40   NodeSet white_nodes_;
     41   bool use_to_def_;
     42   OStream& os_;
     43   const Graph* const graph_;
     44 
     45   DISALLOW_COPY_AND_ASSIGN(GraphVisualizer);
     46 };
     47 
     48 
     49 static Node* GetControlCluster(Node* node) {
     50   if (OperatorProperties::IsBasicBlockBegin(node->op())) {
     51     return node;
     52   } else if (OperatorProperties::GetControlInputCount(node->op()) == 1) {
     53     Node* control = NodeProperties::GetControlInput(node, 0);
     54     return OperatorProperties::IsBasicBlockBegin(control->op()) ? control
     55                                                                 : NULL;
     56   } else {
     57     return NULL;
     58   }
     59 }
     60 
     61 
     62 GenericGraphVisit::Control GraphVisualizer::Pre(Node* node) {
     63   if (all_nodes_.count(node) == 0) {
     64     Node* control_cluster = GetControlCluster(node);
     65     if (control_cluster != NULL) {
     66       os_ << "  subgraph cluster_BasicBlock" << control_cluster->id() << " {\n";
     67     }
     68     os_ << "  ID" << node->id() << " [\n";
     69     AnnotateNode(node);
     70     os_ << "  ]\n";
     71     if (control_cluster != NULL) os_ << "  }\n";
     72     all_nodes_.insert(node);
     73     if (use_to_def_) white_nodes_.insert(node);
     74   }
     75   return GenericGraphVisit::CONTINUE;
     76 }
     77 
     78 
     79 GenericGraphVisit::Control GraphVisualizer::PreEdge(Node* from, int index,
     80                                                     Node* to) {
     81   if (use_to_def_) return GenericGraphVisit::CONTINUE;
     82   // When going from def to use, only consider white -> other edges, which are
     83   // the dead nodes that use live nodes. We're probably not interested in
     84   // dead nodes that only use other dead nodes.
     85   if (white_nodes_.count(from) > 0) return GenericGraphVisit::CONTINUE;
     86   return GenericGraphVisit::SKIP;
     87 }
     88 
     89 
     90 class Escaped {
     91  public:
     92   explicit Escaped(const OStringStream& os) : str_(os.c_str()) {}
     93 
     94   friend OStream& operator<<(OStream& os, const Escaped& e) {
     95     for (const char* s = e.str_; *s != '\0'; ++s) {
     96       if (needs_escape(*s)) os << "\\";
     97       os << *s;
     98     }
     99     return os;
    100   }
    101 
    102  private:
    103   static bool needs_escape(char ch) {
    104     switch (ch) {
    105       case '>':
    106       case '<':
    107       case '|':
    108       case '}':
    109       case '{':
    110         return true;
    111       default:
    112         return false;
    113     }
    114   }
    115 
    116   const char* const str_;
    117 };
    118 
    119 
    120 static bool IsLikelyBackEdge(Node* from, int index, Node* to) {
    121   if (from->opcode() == IrOpcode::kPhi ||
    122       from->opcode() == IrOpcode::kEffectPhi) {
    123     Node* control = NodeProperties::GetControlInput(from, 0);
    124     return control->opcode() != IrOpcode::kMerge && control != to && index != 0;
    125   } else if (from->opcode() == IrOpcode::kLoop) {
    126     return index != 0;
    127   } else {
    128     return false;
    129   }
    130 }
    131 
    132 
    133 void GraphVisualizer::AnnotateNode(Node* node) {
    134   if (!use_to_def_) {
    135     os_ << "    style=\"filled\"\n"
    136         << "    fillcolor=\"" DEAD_COLOR "\"\n";
    137   }
    138 
    139   os_ << "    shape=\"record\"\n";
    140   switch (node->opcode()) {
    141     case IrOpcode::kEnd:
    142     case IrOpcode::kDead:
    143     case IrOpcode::kStart:
    144       os_ << "    style=\"diagonals\"\n";
    145       break;
    146     case IrOpcode::kMerge:
    147     case IrOpcode::kIfTrue:
    148     case IrOpcode::kIfFalse:
    149     case IrOpcode::kLoop:
    150       os_ << "    style=\"rounded\"\n";
    151       break;
    152     default:
    153       break;
    154   }
    155 
    156   OStringStream label;
    157   label << *node->op();
    158   os_ << "    label=\"{{#" << node->id() << ":" << Escaped(label);
    159 
    160   InputIter i = node->inputs().begin();
    161   for (int j = OperatorProperties::GetValueInputCount(node->op()); j > 0;
    162        ++i, j--) {
    163     os_ << "|<I" << i.index() << ">#" << (*i)->id();
    164   }
    165   for (int j = OperatorProperties::GetContextInputCount(node->op()); j > 0;
    166        ++i, j--) {
    167     os_ << "|<I" << i.index() << ">X #" << (*i)->id();
    168   }
    169   for (int j = OperatorProperties::GetFrameStateInputCount(node->op()); j > 0;
    170        ++i, j--) {
    171     os_ << "|<I" << i.index() << ">F #" << (*i)->id();
    172   }
    173   for (int j = OperatorProperties::GetEffectInputCount(node->op()); j > 0;
    174        ++i, j--) {
    175     os_ << "|<I" << i.index() << ">E #" << (*i)->id();
    176   }
    177 
    178   if (!use_to_def_ || OperatorProperties::IsBasicBlockBegin(node->op()) ||
    179       GetControlCluster(node) == NULL) {
    180     for (int j = OperatorProperties::GetControlInputCount(node->op()); j > 0;
    181          ++i, j--) {
    182       os_ << "|<I" << i.index() << ">C #" << (*i)->id();
    183     }
    184   }
    185   os_ << "}";
    186 
    187   if (FLAG_trace_turbo_types && !NodeProperties::IsControl(node)) {
    188     Bounds bounds = NodeProperties::GetBounds(node);
    189     OStringStream upper;
    190     bounds.upper->PrintTo(upper);
    191     OStringStream lower;
    192     bounds.lower->PrintTo(lower);
    193     os_ << "|" << Escaped(upper) << "|" << Escaped(lower);
    194   }
    195   os_ << "}\"\n";
    196 }
    197 
    198 
    199 void GraphVisualizer::PrintEdge(Node::Edge edge) {
    200   Node* from = edge.from();
    201   int index = edge.index();
    202   Node* to = edge.to();
    203   bool unconstrained = IsLikelyBackEdge(from, index, to);
    204   os_ << "  ID" << from->id();
    205   if (all_nodes_.count(to) == 0) {
    206     os_ << ":I" << index << ":n -> DEAD_INPUT";
    207   } else if (OperatorProperties::IsBasicBlockBegin(from->op()) ||
    208              GetControlCluster(from) == NULL ||
    209              (OperatorProperties::GetControlInputCount(from->op()) > 0 &&
    210               NodeProperties::GetControlInput(from) != to)) {
    211     os_ << ":I" << index << ":n -> ID" << to->id() << ":s"
    212         << "[" << (unconstrained ? "constraint=false, " : "")
    213         << (NodeProperties::IsControlEdge(edge) ? "style=bold, " : "")
    214         << (NodeProperties::IsEffectEdge(edge) ? "style=dotted, " : "")
    215         << (NodeProperties::IsContextEdge(edge) ? "style=dashed, " : "") << "]";
    216   } else {
    217     os_ << " -> ID" << to->id() << ":s [color=transparent, "
    218         << (unconstrained ? "constraint=false, " : "")
    219         << (NodeProperties::IsControlEdge(edge) ? "style=dashed, " : "") << "]";
    220   }
    221   os_ << "\n";
    222 }
    223 
    224 
    225 void GraphVisualizer::Print() {
    226   os_ << "digraph D {\n"
    227       << "  node [fontsize=8,height=0.25]\n"
    228       << "  rankdir=\"BT\"\n"
    229       << "  ranksep=\"1.2 equally\"\n"
    230       << "  overlap=\"false\"\n"
    231       << "  splines=\"true\"\n"
    232       << "  concentrate=\"true\"\n"
    233       << "  \n";
    234 
    235   // Make sure all nodes have been output before writing out the edges.
    236   use_to_def_ = true;
    237   // TODO(svenpanne) Remove the need for the const_casts.
    238   const_cast<Graph*>(graph_)->VisitNodeInputsFromEnd(this);
    239   white_nodes_.insert(const_cast<Graph*>(graph_)->start());
    240 
    241   // Visit all uses of white nodes.
    242   use_to_def_ = false;
    243   GenericGraphVisit::Visit<GraphVisualizer, NodeUseIterationTraits<Node> >(
    244       const_cast<Graph*>(graph_), zone_, white_nodes_.begin(),
    245       white_nodes_.end(), this);
    246 
    247   os_ << "  DEAD_INPUT [\n"
    248       << "    style=\"filled\" \n"
    249       << "    fillcolor=\"" DEAD_COLOR "\"\n"
    250       << "  ]\n"
    251       << "\n";
    252 
    253   // With all the nodes written, add the edges.
    254   for (NodeSetIter i = all_nodes_.begin(); i != all_nodes_.end(); ++i) {
    255     Node::Inputs inputs = (*i)->inputs();
    256     for (Node::Inputs::iterator iter(inputs.begin()); iter != inputs.end();
    257          ++iter) {
    258       PrintEdge(iter.edge());
    259     }
    260   }
    261   os_ << "}\n";
    262 }
    263 
    264 
    265 GraphVisualizer::GraphVisualizer(OStream& os, Zone* zone,
    266                                  const Graph* graph)  // NOLINT
    267     : zone_(zone),
    268       all_nodes_(NodeSet::key_compare(), NodeSet::allocator_type(zone)),
    269       white_nodes_(NodeSet::key_compare(), NodeSet::allocator_type(zone)),
    270       use_to_def_(true),
    271       os_(os),
    272       graph_(graph) {}
    273 
    274 
    275 OStream& operator<<(OStream& os, const AsDOT& ad) {
    276   Zone tmp_zone(ad.graph.zone()->isolate());
    277   GraphVisualizer(os, &tmp_zone, &ad.graph).Print();
    278   return os;
    279 }
    280 }
    281 }
    282 }  // namespace v8::internal::compiler
    283