| OLD | NEW |
| 1 // Copyright 2013 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/compiler/graph-visualizer.h" | 5 #include "src/compiler/graph-visualizer.h" |
| 6 | 6 |
| 7 #include "src/compiler/generic-algorithm.h" | 7 #include "src/compiler/generic-algorithm.h" |
| 8 #include "src/compiler/generic-node.h" | 8 #include "src/compiler/generic-node.h" |
| 9 #include "src/compiler/generic-node-inl.h" | 9 #include "src/compiler/generic-node-inl.h" |
| 10 #include "src/compiler/graph.h" | 10 #include "src/compiler/graph.h" |
| 11 #include "src/compiler/graph-inl.h" | 11 #include "src/compiler/graph-inl.h" |
| 12 #include "src/compiler/node.h" | 12 #include "src/compiler/node.h" |
| 13 #include "src/compiler/node-properties.h" | 13 #include "src/compiler/node-properties.h" |
| 14 #include "src/compiler/node-properties-inl.h" | 14 #include "src/compiler/node-properties-inl.h" |
| 15 #include "src/compiler/opcodes.h" | 15 #include "src/compiler/opcodes.h" |
| 16 #include "src/compiler/operator.h" | 16 #include "src/compiler/operator.h" |
| 17 #include "src/ostreams.h" | 17 #include "src/ostreams.h" |
| 18 | 18 |
| 19 namespace v8 { | 19 namespace v8 { |
| 20 namespace internal { | 20 namespace internal { |
| 21 namespace compiler { | 21 namespace compiler { |
| 22 | 22 |
| 23 #define DEAD_COLOR "#999999" | 23 #define DEAD_COLOR "#999999" |
| 24 | 24 |
| 25 class Escaped { |
| 26 public: |
| 27 explicit Escaped(const OStringStream& os, const char* escaped_chars = "<>|{}") |
| 28 : str_(os.c_str()), escaped_chars_(escaped_chars) {} |
| 29 |
| 30 friend OStream& operator<<(OStream& os, const Escaped& e) { |
| 31 for (const char* s = e.str_; *s != '\0'; ++s) { |
| 32 if (e.needs_escape(*s)) os << "\\"; |
| 33 os << *s; |
| 34 } |
| 35 return os; |
| 36 } |
| 37 |
| 38 private: |
| 39 bool needs_escape(char ch) const { |
| 40 for (size_t i = 0; i < strlen(escaped_chars_); ++i) { |
| 41 if (ch == escaped_chars_[i]) return true; |
| 42 } |
| 43 return false; |
| 44 } |
| 45 |
| 46 const char* const str_; |
| 47 const char* const escaped_chars_; |
| 48 }; |
| 49 |
| 50 class JSONGraphNodeWriter : public NullNodeVisitor { |
| 51 public: |
| 52 JSONGraphNodeWriter(OStream& os, Zone* zone, const Graph* graph) // NOLINT |
| 53 : os_(os), |
| 54 graph_(graph), |
| 55 first_node_(true) {} |
| 56 |
| 57 void Print() { const_cast<Graph*>(graph_)->VisitNodeInputsFromEnd(this); } |
| 58 |
| 59 GenericGraphVisit::Control Pre(Node* node); |
| 60 |
| 61 private: |
| 62 OStream& os_; |
| 63 const Graph* const graph_; |
| 64 bool first_node_; |
| 65 |
| 66 DISALLOW_COPY_AND_ASSIGN(JSONGraphNodeWriter); |
| 67 }; |
| 68 |
| 69 |
| 70 GenericGraphVisit::Control JSONGraphNodeWriter::Pre(Node* node) { |
| 71 if (first_node_) { |
| 72 first_node_ = false; |
| 73 } else { |
| 74 os_ << ","; |
| 75 } |
| 76 OStringStream label; |
| 77 label << *node->op(); |
| 78 os_ << "{\"id\":" << node->id() << ",\"label\":\"" << Escaped(label, "\"") |
| 79 << "\""; |
| 80 IrOpcode::Value opcode = node->opcode(); |
| 81 if (opcode == IrOpcode::kPhi || opcode == IrOpcode::kEffectPhi) { |
| 82 os_ << ",\"rankInputs\":[0," << NodeProperties::FirstControlIndex(node) |
| 83 << "]"; |
| 84 os_ << ",\"rankWithInput\":[" << NodeProperties::FirstControlIndex(node) |
| 85 << "]"; |
| 86 } else if (opcode == IrOpcode::kIfTrue || opcode == IrOpcode::kIfFalse || |
| 87 opcode == IrOpcode::kLoop) { |
| 88 os_ << ",\"rankInputs\":[" << NodeProperties::FirstControlIndex(node) |
| 89 << "]"; |
| 90 } |
| 91 if (opcode == IrOpcode::kBranch) { |
| 92 os_ << ",\"rankInputs\":[0]"; |
| 93 } |
| 94 os_ << ",\"opcode\":\"" << IrOpcode::Mnemonic(node->opcode()) << "\""; |
| 95 os_ << ",\"control\":" << (NodeProperties::IsControl(node) ? "true" |
| 96 : "false"); |
| 97 os_ << "}"; |
| 98 return GenericGraphVisit::CONTINUE; |
| 99 } |
| 100 |
| 101 |
| 102 class JSONGraphEdgeWriter : public NullNodeVisitor { |
| 103 public: |
| 104 JSONGraphEdgeWriter(OStream& os, Zone* zone, const Graph* graph) // NOLINT |
| 105 : os_(os), |
| 106 graph_(graph), |
| 107 first_edge_(true) {} |
| 108 |
| 109 void Print() { const_cast<Graph*>(graph_)->VisitNodeInputsFromEnd(this); } |
| 110 |
| 111 GenericGraphVisit::Control PreEdge(Node* from, int index, Node* to); |
| 112 |
| 113 private: |
| 114 OStream& os_; |
| 115 const Graph* const graph_; |
| 116 bool first_edge_; |
| 117 |
| 118 DISALLOW_COPY_AND_ASSIGN(JSONGraphEdgeWriter); |
| 119 }; |
| 120 |
| 121 |
| 122 GenericGraphVisit::Control JSONGraphEdgeWriter::PreEdge(Node* from, int index, |
| 123 Node* to) { |
| 124 if (first_edge_) { |
| 125 first_edge_ = false; |
| 126 } else { |
| 127 os_ << ","; |
| 128 } |
| 129 const char* edge_type = NULL; |
| 130 if (index < NodeProperties::FirstValueIndex(from)) { |
| 131 edge_type = "unknown"; |
| 132 } else if (index < NodeProperties::FirstContextIndex(from)) { |
| 133 edge_type = "value"; |
| 134 } else if (index < NodeProperties::FirstFrameStateIndex(from)) { |
| 135 edge_type = "context"; |
| 136 } else if (index < NodeProperties::FirstEffectIndex(from)) { |
| 137 edge_type = "frame-state"; |
| 138 } else if (index < NodeProperties::FirstControlIndex(from)) { |
| 139 edge_type = "effect"; |
| 140 } else { |
| 141 edge_type = "control"; |
| 142 } |
| 143 os_ << "{\"source\":" << to->id() << ",\"target\":" << from->id() |
| 144 << ",\"index\":" << index << ",\"type\":\"" << edge_type << "\"}"; |
| 145 return GenericGraphVisit::CONTINUE; |
| 146 } |
| 147 |
| 148 |
| 149 OStream& operator<<(OStream& os, const AsJSON& ad) { |
| 150 Zone tmp_zone(ad.graph.zone()->isolate()); |
| 151 os << "{\"nodes\":["; |
| 152 JSONGraphNodeWriter(os, &tmp_zone, &ad.graph).Print(); |
| 153 os << "],\"edges\":["; |
| 154 JSONGraphEdgeWriter(os, &tmp_zone, &ad.graph).Print(); |
| 155 os << "]}"; |
| 156 return os; |
| 157 } |
| 158 |
| 159 |
| 25 class GraphVisualizer : public NullNodeVisitor { | 160 class GraphVisualizer : public NullNodeVisitor { |
| 26 public: | 161 public: |
| 27 GraphVisualizer(OStream& os, Zone* zone, const Graph* graph); // NOLINT | 162 GraphVisualizer(OStream& os, Zone* zone, const Graph* graph); // NOLINT |
| 28 | 163 |
| 29 void Print(); | 164 void Print(); |
| 30 | 165 |
| 31 GenericGraphVisit::Control Pre(Node* node); | 166 GenericGraphVisit::Control Pre(Node* node); |
| 32 GenericGraphVisit::Control PreEdge(Node* from, int index, Node* to); | 167 GenericGraphVisit::Control PreEdge(Node* from, int index, Node* to); |
| 33 | 168 |
| 34 private: | 169 private: |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 80 Node* to) { | 215 Node* to) { |
| 81 if (use_to_def_) return GenericGraphVisit::CONTINUE; | 216 if (use_to_def_) return GenericGraphVisit::CONTINUE; |
| 82 // When going from def to use, only consider white -> other edges, which are | 217 // 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 | 218 // the dead nodes that use live nodes. We're probably not interested in |
| 84 // dead nodes that only use other dead nodes. | 219 // dead nodes that only use other dead nodes. |
| 85 if (white_nodes_.count(from) > 0) return GenericGraphVisit::CONTINUE; | 220 if (white_nodes_.count(from) > 0) return GenericGraphVisit::CONTINUE; |
| 86 return GenericGraphVisit::SKIP; | 221 return GenericGraphVisit::SKIP; |
| 87 } | 222 } |
| 88 | 223 |
| 89 | 224 |
| 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) { | 225 static bool IsLikelyBackEdge(Node* from, int index, Node* to) { |
| 121 if (from->opcode() == IrOpcode::kPhi || | 226 if (from->opcode() == IrOpcode::kPhi || |
| 122 from->opcode() == IrOpcode::kEffectPhi) { | 227 from->opcode() == IrOpcode::kEffectPhi) { |
| 123 Node* control = NodeProperties::GetControlInput(from, 0); | 228 Node* control = NodeProperties::GetControlInput(from, 0); |
| 124 return control->opcode() != IrOpcode::kMerge && control != to && index != 0; | 229 return control->opcode() != IrOpcode::kMerge && control != to && index != 0; |
| 125 } else if (from->opcode() == IrOpcode::kLoop) { | 230 } else if (from->opcode() == IrOpcode::kLoop) { |
| 126 return index != 0; | 231 return index != 0; |
| 127 } else { | 232 } else { |
| 128 return false; | 233 return false; |
| 129 } | 234 } |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 273 | 378 |
| 274 | 379 |
| 275 OStream& operator<<(OStream& os, const AsDOT& ad) { | 380 OStream& operator<<(OStream& os, const AsDOT& ad) { |
| 276 Zone tmp_zone(ad.graph.zone()->isolate()); | 381 Zone tmp_zone(ad.graph.zone()->isolate()); |
| 277 GraphVisualizer(os, &tmp_zone, &ad.graph).Print(); | 382 GraphVisualizer(os, &tmp_zone, &ad.graph).Print(); |
| 278 return os; | 383 return os; |
| 279 } | 384 } |
| 280 } | 385 } |
| 281 } | 386 } |
| 282 } // namespace v8::internal::compiler | 387 } // namespace v8::internal::compiler |
| OLD | NEW |