V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
graph-visualizer.cc
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/backend/register-allocator.h"
14 #include "src/compiler/compiler-source-position-table.h"
15 #include "src/compiler/graph.h"
16 #include "src/compiler/node-origin-table.h"
17 #include "src/compiler/node-properties.h"
18 #include "src/compiler/node.h"
19 #include "src/compiler/opcodes.h"
20 #include "src/compiler/operator-properties.h"
21 #include "src/compiler/operator.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 TurboCfgFile::TurboCfgFile(Isolate* isolate)
50  : std::ofstream(Isolate::GetTurboCfgFileName(isolate).c_str(),
51  std::ios_base::app) {}
52 
53 TurboCfgFile::~TurboCfgFile() { flush(); }
54 
55 std::ostream& operator<<(std::ostream& out,
56  const SourcePositionAsJSON& asJSON) {
57  asJSON.sp.PrintJson(out);
58  return out;
59 }
60 
61 std::ostream& operator<<(std::ostream& out, const NodeOriginAsJSON& asJSON) {
62  asJSON.no.PrintJson(out);
63  return out;
64 }
65 
66 void JsonPrintFunctionSource(std::ostream& os, int source_id,
67  std::unique_ptr<char[]> function_name,
68  Handle<Script> script, Isolate* isolate,
69  Handle<SharedFunctionInfo> shared, bool with_key) {
70  if (with_key) os << "\"" << source_id << "\" : ";
71 
72  os << "{ ";
73  os << "\"sourceId\": " << source_id;
74  os << ", \"functionName\": \"" << function_name.get() << "\" ";
75 
76  int start = 0;
77  int end = 0;
78  if (!script.is_null() && !script->IsUndefined(isolate) && !shared.is_null()) {
79  Object* source_name = script->name();
80  os << ", \"sourceName\": \"";
81  if (source_name->IsString()) {
82  os << String::cast(source_name)->ToCString().get();
83  }
84  os << "\"";
85  {
86  DisallowHeapAllocation no_allocation;
87  start = shared->StartPosition();
88  end = shared->EndPosition();
89  os << ", \"sourceText\": \"";
90  int len = shared->EndPosition() - start;
91  SubStringRange source(String::cast(script->source()), start, len);
92  for (const auto& c : source) {
93  os << AsEscapedUC16ForJSON(c);
94  }
95  os << "\"";
96  }
97  } else {
98  os << ", \"sourceName\": \"\"";
99  os << ", \"sourceText\": \"\"";
100  }
101  os << ", \"startPosition\": " << start;
102  os << ", \"endPosition\": " << end;
103  os << "}";
104 }
105 
106 int SourceIdAssigner::GetIdFor(Handle<SharedFunctionInfo> shared) {
107  for (unsigned i = 0; i < printed_.size(); i++) {
108  if (printed_.at(i).is_identical_to(shared)) {
109  source_ids_.push_back(i);
110  return i;
111  }
112  }
113  const int source_id = static_cast<int>(printed_.size());
114  printed_.push_back(shared);
115  source_ids_.push_back(source_id);
116  return source_id;
117 }
118 
119 namespace {
120 
121 void JsonPrintInlinedFunctionInfo(
122  std::ostream& os, int source_id, int inlining_id,
123  const OptimizedCompilationInfo::InlinedFunctionHolder& h) {
124  os << "\"" << inlining_id << "\" : ";
125  os << "{ \"inliningId\" : " << inlining_id;
126  os << ", \"sourceId\" : " << source_id;
127  const SourcePosition position = h.position.position;
128  if (position.IsKnown()) {
129  os << ", \"inliningPosition\" : " << AsJSON(position);
130  }
131  os << "}";
132 }
133 
134 } // namespace
135 
136 void JsonPrintAllSourceWithPositions(std::ostream& os,
137  OptimizedCompilationInfo* info,
138  Isolate* isolate) {
139  AllowDeferredHandleDereference allow_deference_for_print_code;
140  os << "\"sources\" : {";
141  Handle<Script> script =
142  (info->shared_info().is_null() || !info->shared_info()->script())
143  ? Handle<Script>()
144  : handle(Script::cast(info->shared_info()->script()), isolate);
145  JsonPrintFunctionSource(os, -1,
146  info->shared_info().is_null()
147  ? std::unique_ptr<char[]>(new char[1]{0})
148  : info->shared_info()->DebugName()->ToCString(),
149  script, isolate, info->shared_info(), true);
150  const auto& inlined = info->inlined_functions();
151  SourceIdAssigner id_assigner(info->inlined_functions().size());
152  for (unsigned id = 0; id < inlined.size(); id++) {
153  os << ", ";
154  Handle<SharedFunctionInfo> shared = inlined[id].shared_info;
155  const int source_id = id_assigner.GetIdFor(shared);
156  JsonPrintFunctionSource(os, source_id, shared->DebugName()->ToCString(),
157  handle(Script::cast(shared->script()), isolate),
158  isolate, shared, true);
159  }
160  os << "}, ";
161  os << "\"inlinings\" : {";
162  bool need_comma = false;
163  for (unsigned id = 0; id < inlined.size(); id++) {
164  if (need_comma) os << ", ";
165  const int source_id = id_assigner.GetIdAt(id);
166  JsonPrintInlinedFunctionInfo(os, source_id, id, inlined[id]);
167  need_comma = true;
168  }
169  os << "}";
170 }
171 
172 std::unique_ptr<char[]> GetVisualizerLogFileName(OptimizedCompilationInfo* info,
173  const char* optional_base_dir,
174  const char* phase,
175  const char* suffix) {
176  EmbeddedVector<char, 256> filename(0);
177  std::unique_ptr<char[]> debug_name = info->GetDebugName();
178  int optimization_id = info->IsOptimizing() ? info->optimization_id() : 0;
179  if (strlen(debug_name.get()) > 0) {
180  SNPrintF(filename, "turbo-%s-%i", debug_name.get(), optimization_id);
181  } else if (info->has_shared_info()) {
182  SNPrintF(filename, "turbo-%p-%i",
183  reinterpret_cast<void*>(info->shared_info()->address()),
184  optimization_id);
185  } else {
186  SNPrintF(filename, "turbo-none-%i", optimization_id);
187  }
188  EmbeddedVector<char, 256> source_file(0);
189  bool source_available = false;
190  if (FLAG_trace_file_names && info->has_shared_info() &&
191  info->shared_info()->script()->IsScript()) {
192  Object* source_name = Script::cast(info->shared_info()->script())->name();
193  if (source_name->IsString()) {
194  String str = String::cast(source_name);
195  if (str->length() > 0) {
196  SNPrintF(source_file, "%s", str->ToCString().get());
197  std::replace(source_file.start(),
198  source_file.start() + source_file.length(), '/', '_');
199  source_available = true;
200  }
201  }
202  }
203  std::replace(filename.start(), filename.start() + filename.length(), ' ',
204  '_');
205 
206  EmbeddedVector<char, 256> base_dir;
207  if (optional_base_dir != nullptr) {
208  SNPrintF(base_dir, "%s%c", optional_base_dir,
209  base::OS::DirectorySeparator());
210  } else {
211  base_dir[0] = '\0';
212  }
213 
214  EmbeddedVector<char, 256> full_filename;
215  if (phase == nullptr && !source_available) {
216  SNPrintF(full_filename, "%s%s.%s", base_dir.start(), filename.start(),
217  suffix);
218  } else if (phase != nullptr && !source_available) {
219  SNPrintF(full_filename, "%s%s-%s.%s", base_dir.start(), filename.start(),
220  phase, suffix);
221  } else if (phase == nullptr && source_available) {
222  SNPrintF(full_filename, "%s%s_%s.%s", base_dir.start(), filename.start(),
223  source_file.start(), suffix);
224  } else {
225  SNPrintF(full_filename, "%s%s_%s-%s.%s", base_dir.start(), filename.start(),
226  source_file.start(), phase, suffix);
227  }
228 
229  char* buffer = new char[full_filename.length() + 1];
230  memcpy(buffer, full_filename.start(), full_filename.length());
231  buffer[full_filename.length()] = '\0';
232  return std::unique_ptr<char[]>(buffer);
233 }
234 
235 
236 static int SafeId(Node* node) { return node == nullptr ? -1 : node->id(); }
237 static const char* SafeMnemonic(Node* node) {
238  return node == nullptr ? "null" : node->op()->mnemonic();
239 }
240 
241 class JSONEscaped {
242  public:
243  explicit JSONEscaped(const std::ostringstream& os) : str_(os.str()) {}
244 
245  friend std::ostream& operator<<(std::ostream& os, const JSONEscaped& e) {
246  for (char c : e.str_) PipeCharacter(os, c);
247  return os;
248  }
249 
250  private:
251  static std::ostream& PipeCharacter(std::ostream& os, char c) {
252  if (c == '"') return os << "\\\"";
253  if (c == '\\') return os << "\\\\";
254  if (c == '\b') return os << "\\b";
255  if (c == '\f') return os << "\\f";
256  if (c == '\n') return os << "\\n";
257  if (c == '\r') return os << "\\r";
258  if (c == '\t') return os << "\\t";
259  return os << c;
260  }
261 
262  const std::string str_;
263 };
264 
266  public:
267  JSONGraphNodeWriter(std::ostream& os, Zone* zone, const Graph* graph,
268  const SourcePositionTable* positions,
269  const NodeOriginTable* origins)
270  : os_(os),
271  all_(zone, graph, false),
272  live_(zone, graph, true),
273  positions_(positions),
274  origins_(origins),
275  first_node_(true) {}
276 
277  void Print() {
278  for (Node* const node : all_.reachable) PrintNode(node);
279  os_ << "\n";
280  }
281 
282  void PrintNode(Node* node) {
283  if (first_node_) {
284  first_node_ = false;
285  } else {
286  os_ << ",\n";
287  }
288  std::ostringstream label, title, properties;
289  node->op()->PrintTo(label, Operator::PrintVerbosity::kSilent);
290  node->op()->PrintTo(title, Operator::PrintVerbosity::kVerbose);
291  node->op()->PrintPropsTo(properties);
292  os_ << "{\"id\":" << SafeId(node) << ",\"label\":\"" << JSONEscaped(label)
293  << "\""
294  << ",\"title\":\"" << JSONEscaped(title) << "\""
295  << ",\"live\": " << (live_.IsLive(node) ? "true" : "false")
296  << ",\"properties\":\"" << JSONEscaped(properties) << "\"";
297  IrOpcode::Value opcode = node->opcode();
298  if (IrOpcode::IsPhiOpcode(opcode)) {
299  os_ << ",\"rankInputs\":[0," << NodeProperties::FirstControlIndex(node)
300  << "]";
301  os_ << ",\"rankWithInput\":[" << NodeProperties::FirstControlIndex(node)
302  << "]";
303  } else if (opcode == IrOpcode::kIfTrue || opcode == IrOpcode::kIfFalse ||
304  opcode == IrOpcode::kLoop) {
305  os_ << ",\"rankInputs\":[" << NodeProperties::FirstControlIndex(node)
306  << "]";
307  }
308  if (opcode == IrOpcode::kBranch) {
309  os_ << ",\"rankInputs\":[0]";
310  }
311  if (positions_ != nullptr) {
312  SourcePosition position = positions_->GetSourcePosition(node);
313  if (position.IsKnown()) {
314  os_ << ", \"sourcePosition\" : " << AsJSON(position);
315  }
316  }
317  if (origins_) {
318  NodeOrigin origin = origins_->GetNodeOrigin(node);
319  if (origin.IsKnown()) {
320  os_ << ", \"origin\" : " << AsJSON(origin);
321  }
322  }
323  os_ << ",\"opcode\":\"" << IrOpcode::Mnemonic(node->opcode()) << "\"";
324  os_ << ",\"control\":" << (NodeProperties::IsControl(node) ? "true"
325  : "false");
326  os_ << ",\"opinfo\":\"" << node->op()->ValueInputCount() << " v "
327  << node->op()->EffectInputCount() << " eff "
328  << node->op()->ControlInputCount() << " ctrl in, "
329  << node->op()->ValueOutputCount() << " v "
330  << node->op()->EffectOutputCount() << " eff "
331  << node->op()->ControlOutputCount() << " ctrl out\"";
332  if (NodeProperties::IsTyped(node)) {
333  Type type = NodeProperties::GetType(node);
334  std::ostringstream type_out;
335  type.PrintTo(type_out);
336  os_ << ",\"type\":\"" << JSONEscaped(type_out) << "\"";
337  }
338  os_ << "}";
339  }
340 
341  private:
342  std::ostream& os_;
343  AllNodes all_;
344  AllNodes live_;
345  const SourcePositionTable* positions_;
346  const NodeOriginTable* origins_;
347  bool first_node_;
348 
349  DISALLOW_COPY_AND_ASSIGN(JSONGraphNodeWriter);
350 };
351 
352 
354  public:
355  JSONGraphEdgeWriter(std::ostream& os, Zone* zone, const Graph* graph)
356  : os_(os), all_(zone, graph, false), first_edge_(true) {}
357 
358  void Print() {
359  for (Node* const node : all_.reachable) PrintEdges(node);
360  os_ << "\n";
361  }
362 
363  void PrintEdges(Node* node) {
364  for (int i = 0; i < node->InputCount(); i++) {
365  Node* input = node->InputAt(i);
366  if (input == nullptr) continue;
367  PrintEdge(node, i, input);
368  }
369  }
370 
371  void PrintEdge(Node* from, int index, Node* to) {
372  if (first_edge_) {
373  first_edge_ = false;
374  } else {
375  os_ << ",\n";
376  }
377  const char* edge_type = nullptr;
378  if (index < NodeProperties::FirstValueIndex(from)) {
379  edge_type = "unknown";
380  } else if (index < NodeProperties::FirstContextIndex(from)) {
381  edge_type = "value";
382  } else if (index < NodeProperties::FirstFrameStateIndex(from)) {
383  edge_type = "context";
384  } else if (index < NodeProperties::FirstEffectIndex(from)) {
385  edge_type = "frame-state";
386  } else if (index < NodeProperties::FirstControlIndex(from)) {
387  edge_type = "effect";
388  } else {
389  edge_type = "control";
390  }
391  os_ << "{\"source\":" << SafeId(to) << ",\"target\":" << SafeId(from)
392  << ",\"index\":" << index << ",\"type\":\"" << edge_type << "\"}";
393  }
394 
395  private:
396  std::ostream& os_;
397  AllNodes all_;
398  bool first_edge_;
399 
400  DISALLOW_COPY_AND_ASSIGN(JSONGraphEdgeWriter);
401 };
402 
403 std::ostream& operator<<(std::ostream& os, const GraphAsJSON& ad) {
404  AccountingAllocator allocator;
405  Zone tmp_zone(&allocator, ZONE_NAME);
406  os << "{\n\"nodes\":[";
407  JSONGraphNodeWriter(os, &tmp_zone, &ad.graph, ad.positions, ad.origins)
408  .Print();
409  os << "],\n\"edges\":[";
410  JSONGraphEdgeWriter(os, &tmp_zone, &ad.graph).Print();
411  os << "]}";
412  return os;
413 }
414 
415 
417  public:
418  GraphC1Visualizer(std::ostream& os, Zone* zone); // NOLINT
419 
420  void PrintCompilation(const OptimizedCompilationInfo* info);
421  void PrintSchedule(const char* phase, const Schedule* schedule,
422  const SourcePositionTable* positions,
423  const InstructionSequence* instructions);
424  void PrintLiveRanges(const char* phase, const RegisterAllocationData* data);
425  Zone* zone() const { return zone_; }
426 
427  private:
428  void PrintIndent();
429  void PrintStringProperty(const char* name, const char* value);
430  void PrintLongProperty(const char* name, int64_t value);
431  void PrintIntProperty(const char* name, int value);
432  void PrintBlockProperty(const char* name, int rpo_number);
433  void PrintNodeId(Node* n);
434  void PrintNode(Node* n);
435  void PrintInputs(Node* n);
436  template <typename InputIterator>
437  void PrintInputs(InputIterator* i, int count, const char* prefix);
438  void PrintType(Node* node);
439 
440  void PrintLiveRange(const LiveRange* range, const char* type, int vreg);
441  void PrintLiveRangeChain(const TopLevelLiveRange* range, const char* type);
442 
443  class Tag final {
444  public:
445  Tag(GraphC1Visualizer* visualizer, const char* name) {
446  name_ = name;
447  visualizer_ = visualizer;
448  visualizer->PrintIndent();
449  visualizer_->os_ << "begin_" << name << "\n";
450  visualizer->indent_++;
451  }
452 
453  ~Tag() {
454  visualizer_->indent_--;
455  visualizer_->PrintIndent();
456  visualizer_->os_ << "end_" << name_ << "\n";
457  DCHECK_LE(0, visualizer_->indent_);
458  }
459 
460  private:
461  GraphC1Visualizer* visualizer_;
462  const char* name_;
463  };
464 
465  std::ostream& os_;
466  int indent_;
467  Zone* zone_;
468 
469  DISALLOW_COPY_AND_ASSIGN(GraphC1Visualizer);
470 };
471 
472 
473 void GraphC1Visualizer::PrintIndent() {
474  for (int i = 0; i < indent_; i++) {
475  os_ << " ";
476  }
477 }
478 
479 
480 GraphC1Visualizer::GraphC1Visualizer(std::ostream& os, Zone* zone)
481  : os_(os), indent_(0), zone_(zone) {}
482 
483 
484 void GraphC1Visualizer::PrintStringProperty(const char* name,
485  const char* value) {
486  PrintIndent();
487  os_ << name << " \"" << value << "\"\n";
488 }
489 
490 
491 void GraphC1Visualizer::PrintLongProperty(const char* name, int64_t value) {
492  PrintIndent();
493  os_ << name << " " << static_cast<int>(value / 1000) << "\n";
494 }
495 
496 
497 void GraphC1Visualizer::PrintBlockProperty(const char* name, int rpo_number) {
498  PrintIndent();
499  os_ << name << " \"B" << rpo_number << "\"\n";
500 }
501 
502 
503 void GraphC1Visualizer::PrintIntProperty(const char* name, int value) {
504  PrintIndent();
505  os_ << name << " " << value << "\n";
506 }
507 
508 void GraphC1Visualizer::PrintCompilation(const OptimizedCompilationInfo* info) {
509  Tag tag(this, "compilation");
510  std::unique_ptr<char[]> name = info->GetDebugName();
511  if (info->IsOptimizing()) {
512  PrintStringProperty("name", name.get());
513  PrintIndent();
514  os_ << "method \"" << name.get() << ":" << info->optimization_id()
515  << "\"\n";
516  } else {
517  PrintStringProperty("name", name.get());
518  PrintStringProperty("method", "stub");
519  }
520  PrintLongProperty(
521  "date",
522  static_cast<int64_t>(V8::GetCurrentPlatform()->CurrentClockTimeMillis()));
523 }
524 
525 
526 void GraphC1Visualizer::PrintNodeId(Node* n) { os_ << "n" << SafeId(n); }
527 
528 
529 void GraphC1Visualizer::PrintNode(Node* n) {
530  PrintNodeId(n);
531  os_ << " " << *n->op() << " ";
532  PrintInputs(n);
533 }
534 
535 
536 template <typename InputIterator>
537 void GraphC1Visualizer::PrintInputs(InputIterator* i, int count,
538  const char* prefix) {
539  if (count > 0) {
540  os_ << prefix;
541  }
542  while (count > 0) {
543  os_ << " ";
544  PrintNodeId(**i);
545  ++(*i);
546  count--;
547  }
548 }
549 
550 
551 void GraphC1Visualizer::PrintInputs(Node* node) {
552  auto i = node->inputs().begin();
553  PrintInputs(&i, node->op()->ValueInputCount(), " ");
554  PrintInputs(&i, OperatorProperties::GetContextInputCount(node->op()),
555  " Ctx:");
556  PrintInputs(&i, OperatorProperties::GetFrameStateInputCount(node->op()),
557  " FS:");
558  PrintInputs(&i, node->op()->EffectInputCount(), " Eff:");
559  PrintInputs(&i, node->op()->ControlInputCount(), " Ctrl:");
560 }
561 
562 
563 void GraphC1Visualizer::PrintType(Node* node) {
564  if (NodeProperties::IsTyped(node)) {
565  Type type = NodeProperties::GetType(node);
566  os_ << " type:" << type;
567  }
568 }
569 
570 
571 void GraphC1Visualizer::PrintSchedule(const char* phase,
572  const Schedule* schedule,
573  const SourcePositionTable* positions,
574  const InstructionSequence* instructions) {
575  Tag tag(this, "cfg");
576  PrintStringProperty("name", phase);
577  const BasicBlockVector* rpo = schedule->rpo_order();
578  for (size_t i = 0; i < rpo->size(); i++) {
579  BasicBlock* current = (*rpo)[i];
580  Tag block_tag(this, "block");
581  PrintBlockProperty("name", current->rpo_number());
582  PrintIntProperty("from_bci", -1);
583  PrintIntProperty("to_bci", -1);
584 
585  PrintIndent();
586  os_ << "predecessors";
587  for (BasicBlock* predecessor : current->predecessors()) {
588  os_ << " \"B" << predecessor->rpo_number() << "\"";
589  }
590  os_ << "\n";
591 
592  PrintIndent();
593  os_ << "successors";
594  for (BasicBlock* successor : current->successors()) {
595  os_ << " \"B" << successor->rpo_number() << "\"";
596  }
597  os_ << "\n";
598 
599  PrintIndent();
600  os_ << "xhandlers\n";
601 
602  PrintIndent();
603  os_ << "flags\n";
604 
605  if (current->dominator() != nullptr) {
606  PrintBlockProperty("dominator", current->dominator()->rpo_number());
607  }
608 
609  PrintIntProperty("loop_depth", current->loop_depth());
610 
611  const InstructionBlock* instruction_block =
612  instructions->InstructionBlockAt(
613  RpoNumber::FromInt(current->rpo_number()));
614  if (instruction_block->code_start() >= 0) {
615  int first_index = instruction_block->first_instruction_index();
616  int last_index = instruction_block->last_instruction_index();
617  PrintIntProperty(
618  "first_lir_id",
619  LifetimePosition::GapFromInstructionIndex(first_index).value());
620  PrintIntProperty("last_lir_id",
621  LifetimePosition::InstructionFromInstructionIndex(
622  last_index).value());
623  }
624 
625  {
626  Tag states_tag(this, "states");
627  Tag locals_tag(this, "locals");
628  int total = 0;
629  for (BasicBlock::const_iterator i = current->begin(); i != current->end();
630  ++i) {
631  if ((*i)->opcode() == IrOpcode::kPhi) total++;
632  }
633  PrintIntProperty("size", total);
634  PrintStringProperty("method", "None");
635  int index = 0;
636  for (BasicBlock::const_iterator i = current->begin(); i != current->end();
637  ++i) {
638  if ((*i)->opcode() != IrOpcode::kPhi) continue;
639  PrintIndent();
640  os_ << index << " ";
641  PrintNodeId(*i);
642  os_ << " [";
643  PrintInputs(*i);
644  os_ << "]\n";
645  index++;
646  }
647  }
648 
649  {
650  Tag HIR_tag(this, "HIR");
651  for (BasicBlock::const_iterator i = current->begin(); i != current->end();
652  ++i) {
653  Node* node = *i;
654  if (node->opcode() == IrOpcode::kPhi) continue;
655  int uses = node->UseCount();
656  PrintIndent();
657  os_ << "0 " << uses << " ";
658  PrintNode(node);
659  if (FLAG_trace_turbo_types) {
660  os_ << " ";
661  PrintType(node);
662  }
663  if (positions != nullptr) {
664  SourcePosition position = positions->GetSourcePosition(node);
665  if (position.IsKnown()) {
666  os_ << " pos:";
667  if (position.isInlined()) {
668  os_ << "inlining(" << position.InliningId() << "),";
669  }
670  os_ << position.ScriptOffset();
671  }
672  }
673  os_ << " <|@\n";
674  }
675 
676  BasicBlock::Control control = current->control();
677  if (control != BasicBlock::kNone) {
678  PrintIndent();
679  os_ << "0 0 ";
680  if (current->control_input() != nullptr) {
681  PrintNode(current->control_input());
682  } else {
683  os_ << -1 - current->rpo_number() << " Goto";
684  }
685  os_ << " ->";
686  for (BasicBlock* successor : current->successors()) {
687  os_ << " B" << successor->rpo_number();
688  }
689  if (FLAG_trace_turbo_types && current->control_input() != nullptr) {
690  os_ << " ";
691  PrintType(current->control_input());
692  }
693  os_ << " <|@\n";
694  }
695  }
696 
697  if (instructions != nullptr) {
698  Tag LIR_tag(this, "LIR");
699  for (int j = instruction_block->first_instruction_index();
700  j <= instruction_block->last_instruction_index(); j++) {
701  PrintIndent();
702  os_ << j << " " << *instructions->InstructionAt(j) << " <|@\n";
703  }
704  }
705  }
706 }
707 
708 
709 void GraphC1Visualizer::PrintLiveRanges(const char* phase,
710  const RegisterAllocationData* data) {
711  Tag tag(this, "intervals");
712  PrintStringProperty("name", phase);
713 
714  for (const TopLevelLiveRange* range : data->fixed_double_live_ranges()) {
715  PrintLiveRangeChain(range, "fixed");
716  }
717 
718  for (const TopLevelLiveRange* range : data->fixed_live_ranges()) {
719  PrintLiveRangeChain(range, "fixed");
720  }
721 
722  for (const TopLevelLiveRange* range : data->live_ranges()) {
723  PrintLiveRangeChain(range, "object");
724  }
725 }
726 
727 void GraphC1Visualizer::PrintLiveRangeChain(const TopLevelLiveRange* range,
728  const char* type) {
729  if (range == nullptr || range->IsEmpty()) return;
730  int vreg = range->vreg();
731  for (const LiveRange* child = range; child != nullptr;
732  child = child->next()) {
733  PrintLiveRange(child, type, vreg);
734  }
735 }
736 
737 void GraphC1Visualizer::PrintLiveRange(const LiveRange* range, const char* type,
738  int vreg) {
739  if (range != nullptr && !range->IsEmpty()) {
740  PrintIndent();
741  os_ << vreg << ":" << range->relative_id() << " " << type;
742  if (range->HasRegisterAssigned()) {
743  AllocatedOperand op = AllocatedOperand::cast(range->GetAssignedOperand());
744  if (op.IsRegister()) {
745  os_ << " \"" << Register::from_code(op.register_code()) << "\"";
746  } else if (op.IsDoubleRegister()) {
747  os_ << " \"" << DoubleRegister::from_code(op.register_code()) << "\"";
748  } else {
749  DCHECK(op.IsFloatRegister());
750  os_ << " \"" << FloatRegister::from_code(op.register_code()) << "\"";
751  }
752  } else if (range->spilled()) {
753  const TopLevelLiveRange* top = range->TopLevel();
754  int index = -1;
755  if (top->HasSpillRange()) {
756  index = kMaxInt; // This hasn't been set yet.
757  } else if (top->GetSpillOperand()->IsConstant()) {
758  os_ << " \"const(nostack):"
759  << ConstantOperand::cast(top->GetSpillOperand())->virtual_register()
760  << "\"";
761  } else {
762  index = AllocatedOperand::cast(top->GetSpillOperand())->index();
763  if (IsFloatingPoint(top->representation())) {
764  os_ << " \"fp_stack:" << index << "\"";
765  } else {
766  os_ << " \"stack:" << index << "\"";
767  }
768  }
769  }
770 
771  // The toplevel range might be a splinter. Pre-resolve those here so that
772  // they have a proper parent.
773  const TopLevelLiveRange* parent = range->TopLevel();
774  if (parent->IsSplinter()) parent = parent->splintered_from();
775  os_ << " " << parent->vreg() << ":" << parent->relative_id();
776 
777  // TODO(herhut) Find something useful to print for the hint field
778  os_ << " unknown";
779 
780  for (const UseInterval* interval = range->first_interval();
781  interval != nullptr; interval = interval->next()) {
782  os_ << " [" << interval->start().value() << ", "
783  << interval->end().value() << "[";
784  }
785 
786  UsePosition* current_pos = range->first_pos();
787  while (current_pos != nullptr) {
788  if (current_pos->RegisterIsBeneficial() || FLAG_trace_all_uses) {
789  os_ << " " << current_pos->pos().value() << " M";
790  }
791  current_pos = current_pos->next();
792  }
793 
794  os_ << " \"\"\n";
795  }
796 }
797 
798 
799 std::ostream& operator<<(std::ostream& os, const AsC1VCompilation& ac) {
800  AccountingAllocator allocator;
801  Zone tmp_zone(&allocator, ZONE_NAME);
802  GraphC1Visualizer(os, &tmp_zone).PrintCompilation(ac.info_);
803  return os;
804 }
805 
806 
807 std::ostream& operator<<(std::ostream& os, const AsC1V& ac) {
808  AccountingAllocator allocator;
809  Zone tmp_zone(&allocator, ZONE_NAME);
810  GraphC1Visualizer(os, &tmp_zone)
811  .PrintSchedule(ac.phase_, ac.schedule_, ac.positions_, ac.instructions_);
812  return os;
813 }
814 
815 
816 std::ostream& operator<<(std::ostream& os,
817  const AsC1VRegisterAllocationData& ac) {
818  AccountingAllocator allocator;
819  Zone tmp_zone(&allocator, ZONE_NAME);
820  GraphC1Visualizer(os, &tmp_zone).PrintLiveRanges(ac.phase_, ac.data_);
821  return os;
822 }
823 
824 const int kUnvisited = 0;
825 const int kOnStack = 1;
826 const int kVisited = 2;
827 
828 std::ostream& operator<<(std::ostream& os, const AsRPO& ar) {
829  AccountingAllocator allocator;
830  Zone local_zone(&allocator, ZONE_NAME);
831 
832  // Do a post-order depth-first search on the RPO graph. For every node,
833  // print:
834  //
835  // - the node id
836  // - the operator mnemonic
837  // - in square brackets its parameter (if present)
838  // - in parentheses the list of argument ids and their mnemonics
839  // - the node type (if it is typed)
840 
841  // Post-order guarantees that all inputs of a node will be printed before
842  // the node itself, if there are no cycles. Any cycles are broken
843  // arbitrarily.
844 
845  ZoneVector<byte> state(ar.graph.NodeCount(), kUnvisited, &local_zone);
846  ZoneStack<Node*> stack(&local_zone);
847 
848  stack.push(ar.graph.end());
849  state[ar.graph.end()->id()] = kOnStack;
850  while (!stack.empty()) {
851  Node* n = stack.top();
852  bool pop = true;
853  for (Node* const i : n->inputs()) {
854  if (state[i->id()] == kUnvisited) {
855  state[i->id()] = kOnStack;
856  stack.push(i);
857  pop = false;
858  break;
859  }
860  }
861  if (pop) {
862  state[n->id()] = kVisited;
863  stack.pop();
864  os << "#" << n->id() << ":" << *n->op() << "(";
865  // Print the inputs.
866  int j = 0;
867  for (Node* const i : n->inputs()) {
868  if (j++ > 0) os << ", ";
869  os << "#" << SafeId(i) << ":" << SafeMnemonic(i);
870  }
871  os << ")";
872  // Print the node type, if any.
873  if (NodeProperties::IsTyped(n)) {
874  os << " [Type: " << NodeProperties::GetType(n) << "]";
875  }
876  os << std::endl;
877  }
878  }
879  return os;
880 }
881 
882 namespace {
883 
884 void PrintIndent(std::ostream& os, int indent) {
885  os << " ";
886  for (int i = 0; i < indent; i++) {
887  os << ". ";
888  }
889 }
890 
891 void PrintScheduledNode(std::ostream& os, int indent, Node* n) {
892  PrintIndent(os, indent);
893  os << "#" << n->id() << ":" << *n->op() << "(";
894  // Print the inputs.
895  int j = 0;
896  for (Node* const i : n->inputs()) {
897  if (j++ > 0) os << ", ";
898  os << "#" << SafeId(i) << ":" << SafeMnemonic(i);
899  }
900  os << ")";
901  // Print the node type, if any.
902  if (NodeProperties::IsTyped(n)) {
903  os << " [Type: " << NodeProperties::GetType(n) << "]";
904  }
905 }
906 
907 void PrintScheduledGraph(std::ostream& os, const Schedule* schedule) {
908  const BasicBlockVector* rpo = schedule->rpo_order();
909  for (size_t i = 0; i < rpo->size(); i++) {
910  BasicBlock* current = (*rpo)[i];
911  int indent = current->loop_depth();
912 
913  os << " + Block B" << current->rpo_number() << " (pred:";
914  for (BasicBlock* predecessor : current->predecessors()) {
915  os << " B" << predecessor->rpo_number();
916  }
917  if (current->IsLoopHeader()) {
918  os << ", loop until B" << current->loop_end()->rpo_number();
919  } else if (current->loop_header()) {
920  os << ", in loop B" << current->loop_header()->rpo_number();
921  }
922  os << ")" << std::endl;
923 
924  for (BasicBlock::const_iterator i = current->begin(); i != current->end();
925  ++i) {
926  Node* node = *i;
927  PrintScheduledNode(os, indent, node);
928  os << std::endl;
929  }
930 
931  if (current->SuccessorCount() > 0) {
932  if (current->control_input() != nullptr) {
933  PrintScheduledNode(os, indent, current->control_input());
934  } else {
935  PrintIndent(os, indent);
936  os << "Goto";
937  }
938  os << " ->";
939 
940  bool isFirst = true;
941  for (BasicBlock* successor : current->successors()) {
942  if (isFirst) {
943  isFirst = false;
944  } else {
945  os << ",";
946  }
947  os << " B" << successor->rpo_number();
948  }
949  os << std::endl;
950  } else {
951  DCHECK_NULL(current->control_input());
952  }
953  }
954 }
955 
956 } // namespace
957 
958 std::ostream& operator<<(std::ostream& os, const AsScheduledGraph& scheduled) {
959  PrintScheduledGraph(os, scheduled.schedule);
960  return os;
961 }
962 
963 std::ostream& operator<<(std::ostream& os, const InstructionOperandAsJSON& o) {
964  const InstructionOperand* op = o.op_;
965  const InstructionSequence* code = o.code_;
966  os << "{";
967  switch (op->kind()) {
968  case InstructionOperand::UNALLOCATED: {
969  const UnallocatedOperand* unalloc = UnallocatedOperand::cast(op);
970  os << "\"type\": \"unallocated\", ";
971  os << "\"text\": \"v" << unalloc->virtual_register() << "\"";
972  if (unalloc->basic_policy() == UnallocatedOperand::FIXED_SLOT) {
973  os << ",\"tooltip\": \"FIXED_SLOT: " << unalloc->fixed_slot_index()
974  << "\"";
975  break;
976  }
977  switch (unalloc->extended_policy()) {
978  case UnallocatedOperand::NONE:
979  break;
980  case UnallocatedOperand::FIXED_REGISTER: {
981  os << ",\"tooltip\": \"FIXED_REGISTER: "
982  << Register::from_code(unalloc->fixed_register_index()) << "\"";
983  break;
984  }
985  case UnallocatedOperand::FIXED_FP_REGISTER: {
986  os << ",\"tooltip\": \"FIXED_FP_REGISTER: "
987  << DoubleRegister::from_code(unalloc->fixed_register_index())
988  << "\"";
989  break;
990  }
991  case UnallocatedOperand::MUST_HAVE_REGISTER: {
992  os << ",\"tooltip\": \"MUST_HAVE_REGISTER\"";
993  break;
994  }
995  case UnallocatedOperand::MUST_HAVE_SLOT: {
996  os << ",\"tooltip\": \"MUST_HAVE_SLOT\"";
997  break;
998  }
999  case UnallocatedOperand::SAME_AS_FIRST_INPUT: {
1000  os << ",\"tooltip\": \"SAME_AS_FIRST_INPUT\"";
1001  break;
1002  }
1003  case UnallocatedOperand::REGISTER_OR_SLOT: {
1004  os << ",\"tooltip\": \"REGISTER_OR_SLOT\"";
1005  break;
1006  }
1007  case UnallocatedOperand::REGISTER_OR_SLOT_OR_CONSTANT: {
1008  os << ",\"tooltip\": \"REGISTER_OR_SLOT_OR_CONSTANT\"";
1009  break;
1010  }
1011  }
1012  break;
1013  }
1014  case InstructionOperand::CONSTANT: {
1015  int vreg = ConstantOperand::cast(op)->virtual_register();
1016  os << "\"type\": \"constant\", ";
1017  os << "\"text\": \"v" << vreg << "\",";
1018  os << "\"tooltip\": \"";
1019  std::stringstream tooltip;
1020  tooltip << code->GetConstant(vreg);
1021  for (const auto& c : tooltip.str()) {
1022  os << AsEscapedUC16ForJSON(c);
1023  }
1024  os << "\"";
1025  break;
1026  }
1027  case InstructionOperand::IMMEDIATE: {
1028  os << "\"type\": \"immediate\", ";
1029  const ImmediateOperand* imm = ImmediateOperand::cast(op);
1030  switch (imm->type()) {
1031  case ImmediateOperand::INLINE: {
1032  os << "\"text\": \"#" << imm->inline_value() << "\"";
1033  break;
1034  }
1035  case ImmediateOperand::INDEXED: {
1036  int index = imm->indexed_value();
1037  os << "\"text\": \"imm:" << index << "\",";
1038  os << "\"tooltip\": \"";
1039  std::stringstream tooltip;
1040  tooltip << code->GetImmediate(imm);
1041  for (const auto& c : tooltip.str()) {
1042  os << AsEscapedUC16ForJSON(c);
1043  }
1044  os << "\"";
1045  break;
1046  }
1047  }
1048  break;
1049  }
1050  case InstructionOperand::EXPLICIT:
1051  case InstructionOperand::ALLOCATED: {
1052  const LocationOperand* allocated = LocationOperand::cast(op);
1053  os << "\"type\": ";
1054  if (allocated->IsExplicit()) {
1055  os << "\"explicit\", ";
1056  } else {
1057  os << "\"allocated\", ";
1058  }
1059  os << "\"text\": \"";
1060  if (op->IsStackSlot()) {
1061  os << "stack:" << allocated->index();
1062  } else if (op->IsFPStackSlot()) {
1063  os << "fp_stack:" << allocated->index();
1064  } else if (op->IsRegister()) {
1065  if (allocated->register_code() < Register::kNumRegisters) {
1066  os << Register::from_code(allocated->register_code());
1067  } else {
1068  os << Assembler::GetSpecialRegisterName(allocated->register_code());
1069  }
1070  } else if (op->IsDoubleRegister()) {
1071  os << DoubleRegister::from_code(allocated->register_code());
1072  } else if (op->IsFloatRegister()) {
1073  os << FloatRegister::from_code(allocated->register_code());
1074  } else {
1075  DCHECK(op->IsSimd128Register());
1076  os << Simd128Register::from_code(allocated->register_code());
1077  }
1078  os << "\",";
1079  os << "\"tooltip\": \""
1080  << MachineReprToString(allocated->representation()) << "\"";
1081  break;
1082  }
1083  case InstructionOperand::INVALID:
1084  UNREACHABLE();
1085  }
1086  os << "}";
1087  return os;
1088 }
1089 
1090 std::ostream& operator<<(std::ostream& os, const InstructionAsJSON& i_json) {
1091  const Instruction* instr = i_json.instr_;
1092 
1093  os << "{";
1094  os << "\"id\": " << i_json.index_ << ",";
1095  os << "\"opcode\": \"" << ArchOpcodeField::decode(instr->opcode()) << "\",";
1096  os << "\"flags\": \"";
1097  FlagsMode fm = FlagsModeField::decode(instr->opcode());
1098  AddressingMode am = AddressingModeField::decode(instr->opcode());
1099  if (am != kMode_None) {
1100  os << " : " << AddressingModeField::decode(instr->opcode());
1101  }
1102  if (fm != kFlags_none) {
1103  os << " && " << fm << " if "
1104  << FlagsConditionField::decode(instr->opcode());
1105  }
1106  os << "\",";
1107 
1108  os << "\"gaps\": [";
1109  for (int i = Instruction::FIRST_GAP_POSITION;
1110  i <= Instruction::LAST_GAP_POSITION; i++) {
1111  if (i != Instruction::FIRST_GAP_POSITION) os << ",";
1112  os << "[";
1113  const ParallelMove* pm = instr->parallel_moves()[i];
1114  if (pm == nullptr) {
1115  os << "]";
1116  continue;
1117  }
1118  bool first = true;
1119  for (MoveOperands* move : *pm) {
1120  if (move->IsEliminated()) continue;
1121  if (!first) os << ",";
1122  first = false;
1123  os << "[" << InstructionOperandAsJSON{&move->destination(), i_json.code_}
1124  << "," << InstructionOperandAsJSON{&move->source(), i_json.code_}
1125  << "]";
1126  }
1127  os << "]";
1128  }
1129  os << "],";
1130 
1131  os << "\"outputs\": [";
1132  bool need_comma = false;
1133  for (size_t i = 0; i < instr->OutputCount(); i++) {
1134  if (need_comma) os << ",";
1135  need_comma = true;
1136  os << InstructionOperandAsJSON{instr->OutputAt(i), i_json.code_};
1137  }
1138  os << "],";
1139 
1140  os << "\"inputs\": [";
1141  need_comma = false;
1142  for (size_t i = 0; i < instr->InputCount(); i++) {
1143  if (need_comma) os << ",";
1144  need_comma = true;
1145  os << InstructionOperandAsJSON{instr->InputAt(i), i_json.code_};
1146  }
1147  os << "],";
1148 
1149  os << "\"temps\": [";
1150  need_comma = false;
1151  for (size_t i = 0; i < instr->TempCount(); i++) {
1152  if (need_comma) os << ",";
1153  need_comma = true;
1154  os << InstructionOperandAsJSON{instr->TempAt(i), i_json.code_};
1155  }
1156  os << "]";
1157  os << "}";
1158 
1159  return os;
1160 }
1161 
1162 std::ostream& operator<<(std::ostream& os, const InstructionBlockAsJSON& b) {
1163  const InstructionBlock* block = b.block_;
1164  const InstructionSequence* code = b.code_;
1165  os << "{";
1166  os << "\"id\": " << block->rpo_number() << ",";
1167  os << "\"deferred\": " << block->IsDeferred() << ",";
1168  os << "\"loop_header\": " << block->IsLoopHeader() << ",";
1169  if (block->IsLoopHeader()) {
1170  os << "\"loop_end\": " << block->loop_end() << ",";
1171  }
1172  os << "\"predecessors\": [";
1173  bool need_comma = false;
1174  for (RpoNumber pred : block->predecessors()) {
1175  if (need_comma) os << ",";
1176  need_comma = true;
1177  os << pred.ToInt();
1178  }
1179  os << "],";
1180  os << "\"successors\": [";
1181  need_comma = false;
1182  for (RpoNumber succ : block->successors()) {
1183  if (need_comma) os << ",";
1184  need_comma = true;
1185  os << succ.ToInt();
1186  }
1187  os << "],";
1188  os << "\"phis\": [";
1189  bool needs_comma = false;
1190  InstructionOperandAsJSON json_op = {nullptr, code};
1191  for (const PhiInstruction* phi : block->phis()) {
1192  if (needs_comma) os << ",";
1193  needs_comma = true;
1194  json_op.op_ = &phi->output();
1195  os << "{\"output\" : " << json_op << ",";
1196  os << "\"operands\": [";
1197  bool op_needs_comma = false;
1198  for (int input : phi->operands()) {
1199  if (op_needs_comma) os << ",";
1200  op_needs_comma = true;
1201  os << "\"v" << input << "\"";
1202  }
1203  os << "]}";
1204  }
1205  os << "],";
1206 
1207  os << "\"instructions\": [";
1208  InstructionAsJSON json_instr = {-1, nullptr, code};
1209  need_comma = false;
1210  for (int j = block->first_instruction_index();
1211  j <= block->last_instruction_index(); j++) {
1212  if (need_comma) os << ",";
1213  need_comma = true;
1214  json_instr.index_ = j;
1215  json_instr.instr_ = code->InstructionAt(j);
1216  os << json_instr;
1217  }
1218  os << "]";
1219  os << "}";
1220 
1221  return os;
1222 }
1223 
1224 std::ostream& operator<<(std::ostream& os, const InstructionSequenceAsJSON& s) {
1225  const InstructionSequence* code = s.sequence_;
1226 
1227  os << "\"blocks\": [";
1228 
1229  bool need_comma = false;
1230  for (int i = 0; i < code->InstructionBlockCount(); i++) {
1231  if (need_comma) os << ",";
1232  need_comma = true;
1233  os << InstructionBlockAsJSON{
1234  code->InstructionBlockAt(RpoNumber::FromInt(i)), code};
1235  }
1236  os << "]";
1237 
1238  return os;
1239 }
1240 
1241 } // namespace compiler
1242 } // namespace internal
1243 } // namespace v8
STL namespace.
Definition: libplatform.h:13