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