• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/compiler-source-position-table.h"
14 #include "src/compiler/graph.h"
15 #include "src/compiler/node-origin-table.h"
16 #include "src/compiler/node-properties.h"
17 #include "src/compiler/node.h"
18 #include "src/compiler/opcodes.h"
19 #include "src/compiler/operator-properties.h"
20 #include "src/compiler/operator.h"
21 #include "src/compiler/register-allocator.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 
get_cached_trace_turbo_filename(OptimizedCompilationInfo * info)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 
TurboJsonFile(OptimizedCompilationInfo * info,std::ios_base::openmode mode)43 TurboJsonFile::TurboJsonFile(OptimizedCompilationInfo* info,
44                              std::ios_base::openmode mode)
45     : std::ofstream(get_cached_trace_turbo_filename(info), mode) {}
46 
~TurboJsonFile()47 TurboJsonFile::~TurboJsonFile() { flush(); }
48 
operator <<(std::ostream & out,const SourcePositionAsJSON & asJSON)49 std::ostream& operator<<(std::ostream& out,
50                          const SourcePositionAsJSON& asJSON) {
51   asJSON.sp.PrintJson(out);
52   return out;
53 }
54 
operator <<(std::ostream & out,const NodeOriginAsJSON & asJSON)55 std::ostream& operator<<(std::ostream& out, const NodeOriginAsJSON& asJSON) {
56   asJSON.no.PrintJson(out);
57   return out;
58 }
59 
JsonPrintFunctionSource(std::ostream & os,int source_id,std::unique_ptr<char[]> function_name,Handle<Script> script,Isolate * isolate,Handle<SharedFunctionInfo> shared,bool with_key)60 void JsonPrintFunctionSource(std::ostream& os, int source_id,
61                              std::unique_ptr<char[]> function_name,
62                              Handle<Script> script, Isolate* isolate,
63                              Handle<SharedFunctionInfo> shared, bool with_key) {
64   if (with_key) os << "\"" << source_id << "\" : ";
65 
66   os << "{ ";
67   os << "\"sourceId\": " << source_id;
68   os << ", \"functionName\": \"" << function_name.get() << "\" ";
69 
70   int start = 0;
71   int end = 0;
72   if (!script.is_null() && !script->IsUndefined(isolate) && !shared.is_null()) {
73     Object* source_name = script->name();
74     os << ", \"sourceName\": \"";
75     if (source_name->IsString()) {
76       os << String::cast(source_name)->ToCString().get();
77     }
78     os << "\"";
79     {
80       DisallowHeapAllocation no_allocation;
81       start = shared->StartPosition();
82       end = shared->EndPosition();
83       os << ", \"sourceText\": \"";
84       int len = shared->EndPosition() - start;
85       String::SubStringRange source(String::cast(script->source()), start, len);
86       for (const auto& c : source) {
87         os << AsEscapedUC16ForJSON(c);
88       }
89       os << "\"";
90     }
91   } else {
92     os << ", \"sourceName\": \"\"";
93     os << ", \"sourceText\": \"\"";
94   }
95   os << ", \"startPosition\": " << start;
96   os << ", \"endPosition\": " << end;
97   os << "}";
98 }
99 
GetIdFor(Handle<SharedFunctionInfo> shared)100 int SourceIdAssigner::GetIdFor(Handle<SharedFunctionInfo> shared) {
101   for (unsigned i = 0; i < printed_.size(); i++) {
102     if (printed_.at(i).is_identical_to(shared)) {
103       source_ids_.push_back(i);
104       return i;
105     }
106   }
107   const int source_id = static_cast<int>(printed_.size());
108   printed_.push_back(shared);
109   source_ids_.push_back(source_id);
110   return source_id;
111 }
112 
113 namespace {
114 
JsonPrintInlinedFunctionInfo(std::ostream & os,int source_id,int inlining_id,const OptimizedCompilationInfo::InlinedFunctionHolder & h)115 void JsonPrintInlinedFunctionInfo(
116     std::ostream& os, int source_id, int inlining_id,
117     const OptimizedCompilationInfo::InlinedFunctionHolder& h) {
118   os << "\"" << inlining_id << "\" : ";
119   os << "{ \"inliningId\" : " << inlining_id;
120   os << ", \"sourceId\" : " << source_id;
121   const SourcePosition position = h.position.position;
122   if (position.IsKnown()) {
123     os << ", \"inliningPosition\" : " << AsJSON(position);
124   }
125   os << "}";
126 }
127 
128 }  // namespace
129 
JsonPrintAllSourceWithPositions(std::ostream & os,OptimizedCompilationInfo * info,Isolate * isolate)130 void JsonPrintAllSourceWithPositions(std::ostream& os,
131                                      OptimizedCompilationInfo* info,
132                                      Isolate* isolate) {
133   AllowDeferredHandleDereference allow_deference_for_print_code;
134   os << "\"sources\" : {";
135   Handle<Script> script =
136       (info->shared_info().is_null() || !info->shared_info()->script())
137           ? Handle<Script>()
138           : handle(Script::cast(info->shared_info()->script()), isolate);
139   JsonPrintFunctionSource(os, -1,
140                           info->shared_info().is_null()
141                               ? std::unique_ptr<char[]>(new char[1]{0})
142                               : info->shared_info()->DebugName()->ToCString(),
143                           script, isolate, info->shared_info(), true);
144   const auto& inlined = info->inlined_functions();
145   SourceIdAssigner id_assigner(info->inlined_functions().size());
146   for (unsigned id = 0; id < inlined.size(); id++) {
147     os << ", ";
148     Handle<SharedFunctionInfo> shared = inlined[id].shared_info;
149     const int source_id = id_assigner.GetIdFor(shared);
150     JsonPrintFunctionSource(os, source_id, shared->DebugName()->ToCString(),
151                             handle(Script::cast(shared->script()), isolate),
152                             isolate, shared, true);
153   }
154   os << "}, ";
155   os << "\"inlinings\" : {";
156   bool need_comma = false;
157   for (unsigned id = 0; id < inlined.size(); id++) {
158     if (need_comma) os << ", ";
159     const int source_id = id_assigner.GetIdAt(id);
160     JsonPrintInlinedFunctionInfo(os, source_id, id, inlined[id]);
161     need_comma = true;
162   }
163   os << "}";
164 }
165 
GetVisualizerLogFileName(OptimizedCompilationInfo * info,const char * optional_base_dir,const char * phase,const char * suffix)166 std::unique_ptr<char[]> GetVisualizerLogFileName(OptimizedCompilationInfo* info,
167                                                  const char* optional_base_dir,
168                                                  const char* phase,
169                                                  const char* suffix) {
170   EmbeddedVector<char, 256> filename(0);
171   std::unique_ptr<char[]> debug_name = info->GetDebugName();
172   int optimization_id = info->IsOptimizing() ? info->optimization_id() : 0;
173   if (strlen(debug_name.get()) > 0) {
174     SNPrintF(filename, "turbo-%s-%i", debug_name.get(), optimization_id);
175   } else if (info->has_shared_info()) {
176     SNPrintF(filename, "turbo-%p-%i",
177              reinterpret_cast<void*>(info->shared_info()->address()),
178              optimization_id);
179   } else {
180     SNPrintF(filename, "turbo-none-%i", optimization_id);
181   }
182   EmbeddedVector<char, 256> source_file(0);
183   bool source_available = false;
184   if (FLAG_trace_file_names && info->has_shared_info() &&
185       info->shared_info()->script()->IsScript()) {
186     Object* source_name = Script::cast(info->shared_info()->script())->name();
187     if (source_name->IsString()) {
188       String* str = String::cast(source_name);
189       if (str->length() > 0) {
190         SNPrintF(source_file, "%s", str->ToCString().get());
191         std::replace(source_file.start(),
192                      source_file.start() + source_file.length(), '/', '_');
193         source_available = true;
194       }
195     }
196   }
197   std::replace(filename.start(), filename.start() + filename.length(), ' ',
198                '_');
199 
200   EmbeddedVector<char, 256> base_dir;
201   if (optional_base_dir != nullptr) {
202     SNPrintF(base_dir, "%s%c", optional_base_dir,
203              base::OS::DirectorySeparator());
204   } else {
205     base_dir[0] = '\0';
206   }
207 
208   EmbeddedVector<char, 256> full_filename;
209   if (phase == nullptr && !source_available) {
210     SNPrintF(full_filename, "%s%s.%s", base_dir.start(), filename.start(),
211              suffix);
212   } else if (phase != nullptr && !source_available) {
213     SNPrintF(full_filename, "%s%s-%s.%s", base_dir.start(), filename.start(),
214              phase, suffix);
215   } else if (phase == nullptr && source_available) {
216     SNPrintF(full_filename, "%s%s_%s.%s", base_dir.start(), filename.start(),
217              source_file.start(), suffix);
218   } else {
219     SNPrintF(full_filename, "%s%s_%s-%s.%s", base_dir.start(), filename.start(),
220              source_file.start(), phase, suffix);
221   }
222 
223   char* buffer = new char[full_filename.length() + 1];
224   memcpy(buffer, full_filename.start(), full_filename.length());
225   buffer[full_filename.length()] = '\0';
226   return std::unique_ptr<char[]>(buffer);
227 }
228 
229 
SafeId(Node * node)230 static int SafeId(Node* node) { return node == nullptr ? -1 : node->id(); }
SafeMnemonic(Node * node)231 static const char* SafeMnemonic(Node* node) {
232   return node == nullptr ? "null" : node->op()->mnemonic();
233 }
234 
235 class JSONEscaped {
236  public:
JSONEscaped(const std::ostringstream & os)237   explicit JSONEscaped(const std::ostringstream& os) : str_(os.str()) {}
238 
operator <<(std::ostream & os,const JSONEscaped & e)239   friend std::ostream& operator<<(std::ostream& os, const JSONEscaped& e) {
240     for (char c : e.str_) PipeCharacter(os, c);
241     return os;
242   }
243 
244  private:
PipeCharacter(std::ostream & os,char c)245   static std::ostream& PipeCharacter(std::ostream& os, char c) {
246     if (c == '"') return os << "\\\"";
247     if (c == '\\') return os << "\\\\";
248     if (c == '\b') return os << "\\b";
249     if (c == '\f') return os << "\\f";
250     if (c == '\n') return os << "\\n";
251     if (c == '\r') return os << "\\r";
252     if (c == '\t') return os << "\\t";
253     return os << c;
254   }
255 
256   const std::string str_;
257 };
258 
259 class JSONGraphNodeWriter {
260  public:
JSONGraphNodeWriter(std::ostream & os,Zone * zone,const Graph * graph,const SourcePositionTable * positions,const NodeOriginTable * origins)261   JSONGraphNodeWriter(std::ostream& os, Zone* zone, const Graph* graph,
262                       const SourcePositionTable* positions,
263                       const NodeOriginTable* origins)
264       : os_(os),
265         all_(zone, graph, false),
266         live_(zone, graph, true),
267         positions_(positions),
268         origins_(origins),
269         first_node_(true) {}
270 
Print()271   void Print() {
272     for (Node* const node : all_.reachable) PrintNode(node);
273     os_ << "\n";
274   }
275 
PrintNode(Node * node)276   void PrintNode(Node* node) {
277     if (first_node_) {
278       first_node_ = false;
279     } else {
280       os_ << ",\n";
281     }
282     std::ostringstream label, title, properties;
283     node->op()->PrintTo(label, Operator::PrintVerbosity::kSilent);
284     node->op()->PrintTo(title, Operator::PrintVerbosity::kVerbose);
285     node->op()->PrintPropsTo(properties);
286     os_ << "{\"id\":" << SafeId(node) << ",\"label\":\"" << JSONEscaped(label)
287         << "\""
288         << ",\"title\":\"" << JSONEscaped(title) << "\""
289         << ",\"live\": " << (live_.IsLive(node) ? "true" : "false")
290         << ",\"properties\":\"" << JSONEscaped(properties) << "\"";
291     IrOpcode::Value opcode = node->opcode();
292     if (IrOpcode::IsPhiOpcode(opcode)) {
293       os_ << ",\"rankInputs\":[0," << NodeProperties::FirstControlIndex(node)
294           << "]";
295       os_ << ",\"rankWithInput\":[" << NodeProperties::FirstControlIndex(node)
296           << "]";
297     } else if (opcode == IrOpcode::kIfTrue || opcode == IrOpcode::kIfFalse ||
298                opcode == IrOpcode::kLoop) {
299       os_ << ",\"rankInputs\":[" << NodeProperties::FirstControlIndex(node)
300           << "]";
301     }
302     if (opcode == IrOpcode::kBranch) {
303       os_ << ",\"rankInputs\":[0]";
304     }
305     SourcePosition position = positions_->GetSourcePosition(node);
306     if (position.IsKnown()) {
307       os_ << ", \"sourcePosition\" : " << AsJSON(position);
308     }
309     if (origins_) {
310       NodeOrigin origin = origins_->GetNodeOrigin(node);
311       if (origin.IsKnown()) {
312         os_ << ", \"origin\" : " << AsJSON(origin);
313       }
314     }
315     os_ << ",\"opcode\":\"" << IrOpcode::Mnemonic(node->opcode()) << "\"";
316     os_ << ",\"control\":" << (NodeProperties::IsControl(node) ? "true"
317                                                                : "false");
318     os_ << ",\"opinfo\":\"" << node->op()->ValueInputCount() << " v "
319         << node->op()->EffectInputCount() << " eff "
320         << node->op()->ControlInputCount() << " ctrl in, "
321         << node->op()->ValueOutputCount() << " v "
322         << node->op()->EffectOutputCount() << " eff "
323         << node->op()->ControlOutputCount() << " ctrl out\"";
324     if (NodeProperties::IsTyped(node)) {
325       Type type = NodeProperties::GetType(node);
326       std::ostringstream type_out;
327       type.PrintTo(type_out);
328       os_ << ",\"type\":\"" << JSONEscaped(type_out) << "\"";
329     }
330     os_ << "}";
331   }
332 
333  private:
334   std::ostream& os_;
335   AllNodes all_;
336   AllNodes live_;
337   const SourcePositionTable* positions_;
338   const NodeOriginTable* origins_;
339   bool first_node_;
340 
341   DISALLOW_COPY_AND_ASSIGN(JSONGraphNodeWriter);
342 };
343 
344 
345 class JSONGraphEdgeWriter {
346  public:
JSONGraphEdgeWriter(std::ostream & os,Zone * zone,const Graph * graph)347   JSONGraphEdgeWriter(std::ostream& os, Zone* zone, const Graph* graph)
348       : os_(os), all_(zone, graph, false), first_edge_(true) {}
349 
Print()350   void Print() {
351     for (Node* const node : all_.reachable) PrintEdges(node);
352     os_ << "\n";
353   }
354 
PrintEdges(Node * node)355   void PrintEdges(Node* node) {
356     for (int i = 0; i < node->InputCount(); i++) {
357       Node* input = node->InputAt(i);
358       if (input == nullptr) continue;
359       PrintEdge(node, i, input);
360     }
361   }
362 
PrintEdge(Node * from,int index,Node * to)363   void PrintEdge(Node* from, int index, Node* to) {
364     if (first_edge_) {
365       first_edge_ = false;
366     } else {
367       os_ << ",\n";
368     }
369     const char* edge_type = nullptr;
370     if (index < NodeProperties::FirstValueIndex(from)) {
371       edge_type = "unknown";
372     } else if (index < NodeProperties::FirstContextIndex(from)) {
373       edge_type = "value";
374     } else if (index < NodeProperties::FirstFrameStateIndex(from)) {
375       edge_type = "context";
376     } else if (index < NodeProperties::FirstEffectIndex(from)) {
377       edge_type = "frame-state";
378     } else if (index < NodeProperties::FirstControlIndex(from)) {
379       edge_type = "effect";
380     } else {
381       edge_type = "control";
382     }
383     os_ << "{\"source\":" << SafeId(to) << ",\"target\":" << SafeId(from)
384         << ",\"index\":" << index << ",\"type\":\"" << edge_type << "\"}";
385   }
386 
387  private:
388   std::ostream& os_;
389   AllNodes all_;
390   bool first_edge_;
391 
392   DISALLOW_COPY_AND_ASSIGN(JSONGraphEdgeWriter);
393 };
394 
operator <<(std::ostream & os,const GraphAsJSON & ad)395 std::ostream& operator<<(std::ostream& os, const GraphAsJSON& ad) {
396   AccountingAllocator allocator;
397   Zone tmp_zone(&allocator, ZONE_NAME);
398   os << "{\n\"nodes\":[";
399   JSONGraphNodeWriter(os, &tmp_zone, &ad.graph, ad.positions, ad.origins)
400       .Print();
401   os << "],\n\"edges\":[";
402   JSONGraphEdgeWriter(os, &tmp_zone, &ad.graph).Print();
403   os << "]}";
404   return os;
405 }
406 
407 
408 class GraphC1Visualizer {
409  public:
410   GraphC1Visualizer(std::ostream& os, Zone* zone);  // NOLINT
411 
412   void PrintCompilation(const OptimizedCompilationInfo* info);
413   void PrintSchedule(const char* phase, const Schedule* schedule,
414                      const SourcePositionTable* positions,
415                      const InstructionSequence* instructions);
416   void PrintLiveRanges(const char* phase, const RegisterAllocationData* data);
zone() const417   Zone* zone() const { return zone_; }
418 
419  private:
420   void PrintIndent();
421   void PrintStringProperty(const char* name, const char* value);
422   void PrintLongProperty(const char* name, int64_t value);
423   void PrintIntProperty(const char* name, int value);
424   void PrintBlockProperty(const char* name, int rpo_number);
425   void PrintNodeId(Node* n);
426   void PrintNode(Node* n);
427   void PrintInputs(Node* n);
428   template <typename InputIterator>
429   void PrintInputs(InputIterator* i, int count, const char* prefix);
430   void PrintType(Node* node);
431 
432   void PrintLiveRange(const LiveRange* range, const char* type, int vreg);
433   void PrintLiveRangeChain(const TopLevelLiveRange* range, const char* type);
434 
435   class Tag final BASE_EMBEDDED {
436    public:
Tag(GraphC1Visualizer * visualizer,const char * name)437     Tag(GraphC1Visualizer* visualizer, const char* name) {
438       name_ = name;
439       visualizer_ = visualizer;
440       visualizer->PrintIndent();
441       visualizer_->os_ << "begin_" << name << "\n";
442       visualizer->indent_++;
443     }
444 
~Tag()445     ~Tag() {
446       visualizer_->indent_--;
447       visualizer_->PrintIndent();
448       visualizer_->os_ << "end_" << name_ << "\n";
449       DCHECK_LE(0, visualizer_->indent_);
450     }
451 
452    private:
453     GraphC1Visualizer* visualizer_;
454     const char* name_;
455   };
456 
457   std::ostream& os_;
458   int indent_;
459   Zone* zone_;
460 
461   DISALLOW_COPY_AND_ASSIGN(GraphC1Visualizer);
462 };
463 
464 
PrintIndent()465 void GraphC1Visualizer::PrintIndent() {
466   for (int i = 0; i < indent_; i++) {
467     os_ << "  ";
468   }
469 }
470 
471 
GraphC1Visualizer(std::ostream & os,Zone * zone)472 GraphC1Visualizer::GraphC1Visualizer(std::ostream& os, Zone* zone)
473     : os_(os), indent_(0), zone_(zone) {}
474 
475 
PrintStringProperty(const char * name,const char * value)476 void GraphC1Visualizer::PrintStringProperty(const char* name,
477                                             const char* value) {
478   PrintIndent();
479   os_ << name << " \"" << value << "\"\n";
480 }
481 
482 
PrintLongProperty(const char * name,int64_t value)483 void GraphC1Visualizer::PrintLongProperty(const char* name, int64_t value) {
484   PrintIndent();
485   os_ << name << " " << static_cast<int>(value / 1000) << "\n";
486 }
487 
488 
PrintBlockProperty(const char * name,int rpo_number)489 void GraphC1Visualizer::PrintBlockProperty(const char* name, int rpo_number) {
490   PrintIndent();
491   os_ << name << " \"B" << rpo_number << "\"\n";
492 }
493 
494 
PrintIntProperty(const char * name,int value)495 void GraphC1Visualizer::PrintIntProperty(const char* name, int value) {
496   PrintIndent();
497   os_ << name << " " << value << "\n";
498 }
499 
PrintCompilation(const OptimizedCompilationInfo * info)500 void GraphC1Visualizer::PrintCompilation(const OptimizedCompilationInfo* info) {
501   Tag tag(this, "compilation");
502   std::unique_ptr<char[]> name = info->GetDebugName();
503   if (info->IsOptimizing()) {
504     PrintStringProperty("name", name.get());
505     PrintIndent();
506     os_ << "method \"" << name.get() << ":" << info->optimization_id()
507         << "\"\n";
508   } else {
509     PrintStringProperty("name", name.get());
510     PrintStringProperty("method", "stub");
511   }
512   PrintLongProperty(
513       "date",
514       static_cast<int64_t>(V8::GetCurrentPlatform()->CurrentClockTimeMillis()));
515 }
516 
517 
PrintNodeId(Node * n)518 void GraphC1Visualizer::PrintNodeId(Node* n) { os_ << "n" << SafeId(n); }
519 
520 
PrintNode(Node * n)521 void GraphC1Visualizer::PrintNode(Node* n) {
522   PrintNodeId(n);
523   os_ << " " << *n->op() << " ";
524   PrintInputs(n);
525 }
526 
527 
528 template <typename InputIterator>
PrintInputs(InputIterator * i,int count,const char * prefix)529 void GraphC1Visualizer::PrintInputs(InputIterator* i, int count,
530                                     const char* prefix) {
531   if (count > 0) {
532     os_ << prefix;
533   }
534   while (count > 0) {
535     os_ << " ";
536     PrintNodeId(**i);
537     ++(*i);
538     count--;
539   }
540 }
541 
542 
PrintInputs(Node * node)543 void GraphC1Visualizer::PrintInputs(Node* node) {
544   auto i = node->inputs().begin();
545   PrintInputs(&i, node->op()->ValueInputCount(), " ");
546   PrintInputs(&i, OperatorProperties::GetContextInputCount(node->op()),
547               " Ctx:");
548   PrintInputs(&i, OperatorProperties::GetFrameStateInputCount(node->op()),
549               " FS:");
550   PrintInputs(&i, node->op()->EffectInputCount(), " Eff:");
551   PrintInputs(&i, node->op()->ControlInputCount(), " Ctrl:");
552 }
553 
554 
PrintType(Node * node)555 void GraphC1Visualizer::PrintType(Node* node) {
556   if (NodeProperties::IsTyped(node)) {
557     Type type = NodeProperties::GetType(node);
558     os_ << " type:" << type;
559   }
560 }
561 
562 
PrintSchedule(const char * phase,const Schedule * schedule,const SourcePositionTable * positions,const InstructionSequence * instructions)563 void GraphC1Visualizer::PrintSchedule(const char* phase,
564                                       const Schedule* schedule,
565                                       const SourcePositionTable* positions,
566                                       const InstructionSequence* instructions) {
567   Tag tag(this, "cfg");
568   PrintStringProperty("name", phase);
569   const BasicBlockVector* rpo = schedule->rpo_order();
570   for (size_t i = 0; i < rpo->size(); i++) {
571     BasicBlock* current = (*rpo)[i];
572     Tag block_tag(this, "block");
573     PrintBlockProperty("name", current->rpo_number());
574     PrintIntProperty("from_bci", -1);
575     PrintIntProperty("to_bci", -1);
576 
577     PrintIndent();
578     os_ << "predecessors";
579     for (BasicBlock* predecessor : current->predecessors()) {
580       os_ << " \"B" << predecessor->rpo_number() << "\"";
581     }
582     os_ << "\n";
583 
584     PrintIndent();
585     os_ << "successors";
586     for (BasicBlock* successor : current->successors()) {
587       os_ << " \"B" << successor->rpo_number() << "\"";
588     }
589     os_ << "\n";
590 
591     PrintIndent();
592     os_ << "xhandlers\n";
593 
594     PrintIndent();
595     os_ << "flags\n";
596 
597     if (current->dominator() != nullptr) {
598       PrintBlockProperty("dominator", current->dominator()->rpo_number());
599     }
600 
601     PrintIntProperty("loop_depth", current->loop_depth());
602 
603     const InstructionBlock* instruction_block =
604         instructions->InstructionBlockAt(
605             RpoNumber::FromInt(current->rpo_number()));
606     if (instruction_block->code_start() >= 0) {
607       int first_index = instruction_block->first_instruction_index();
608       int last_index = instruction_block->last_instruction_index();
609       PrintIntProperty(
610           "first_lir_id",
611           LifetimePosition::GapFromInstructionIndex(first_index).value());
612       PrintIntProperty("last_lir_id",
613                        LifetimePosition::InstructionFromInstructionIndex(
614                            last_index).value());
615     }
616 
617     {
618       Tag states_tag(this, "states");
619       Tag locals_tag(this, "locals");
620       int total = 0;
621       for (BasicBlock::const_iterator i = current->begin(); i != current->end();
622            ++i) {
623         if ((*i)->opcode() == IrOpcode::kPhi) total++;
624       }
625       PrintIntProperty("size", total);
626       PrintStringProperty("method", "None");
627       int index = 0;
628       for (BasicBlock::const_iterator i = current->begin(); i != current->end();
629            ++i) {
630         if ((*i)->opcode() != IrOpcode::kPhi) continue;
631         PrintIndent();
632         os_ << index << " ";
633         PrintNodeId(*i);
634         os_ << " [";
635         PrintInputs(*i);
636         os_ << "]\n";
637         index++;
638       }
639     }
640 
641     {
642       Tag HIR_tag(this, "HIR");
643       for (BasicBlock::const_iterator i = current->begin(); i != current->end();
644            ++i) {
645         Node* node = *i;
646         if (node->opcode() == IrOpcode::kPhi) continue;
647         int uses = node->UseCount();
648         PrintIndent();
649         os_ << "0 " << uses << " ";
650         PrintNode(node);
651         if (FLAG_trace_turbo_types) {
652           os_ << " ";
653           PrintType(node);
654         }
655         if (positions != nullptr) {
656           SourcePosition position = positions->GetSourcePosition(node);
657           if (position.IsKnown()) {
658             os_ << " pos:";
659             if (position.isInlined()) {
660               os_ << "inlining(" << position.InliningId() << "),";
661             }
662             os_ << position.ScriptOffset();
663           }
664         }
665         os_ << " <|@\n";
666       }
667 
668       BasicBlock::Control control = current->control();
669       if (control != BasicBlock::kNone) {
670         PrintIndent();
671         os_ << "0 0 ";
672         if (current->control_input() != nullptr) {
673           PrintNode(current->control_input());
674         } else {
675           os_ << -1 - current->rpo_number() << " Goto";
676         }
677         os_ << " ->";
678         for (BasicBlock* successor : current->successors()) {
679           os_ << " B" << successor->rpo_number();
680         }
681         if (FLAG_trace_turbo_types && current->control_input() != nullptr) {
682           os_ << " ";
683           PrintType(current->control_input());
684         }
685         os_ << " <|@\n";
686       }
687     }
688 
689     if (instructions != nullptr) {
690       Tag LIR_tag(this, "LIR");
691       for (int j = instruction_block->first_instruction_index();
692            j <= instruction_block->last_instruction_index(); j++) {
693         PrintIndent();
694         PrintableInstruction printable = {RegisterConfiguration::Default(),
695                                           instructions->InstructionAt(j)};
696         os_ << j << " " << printable << " <|@\n";
697       }
698     }
699   }
700 }
701 
702 
PrintLiveRanges(const char * phase,const RegisterAllocationData * data)703 void GraphC1Visualizer::PrintLiveRanges(const char* phase,
704                                         const RegisterAllocationData* data) {
705   Tag tag(this, "intervals");
706   PrintStringProperty("name", phase);
707 
708   for (const TopLevelLiveRange* range : data->fixed_double_live_ranges()) {
709     PrintLiveRangeChain(range, "fixed");
710   }
711 
712   for (const TopLevelLiveRange* range : data->fixed_live_ranges()) {
713     PrintLiveRangeChain(range, "fixed");
714   }
715 
716   for (const TopLevelLiveRange* range : data->live_ranges()) {
717     PrintLiveRangeChain(range, "object");
718   }
719 }
720 
PrintLiveRangeChain(const TopLevelLiveRange * range,const char * type)721 void GraphC1Visualizer::PrintLiveRangeChain(const TopLevelLiveRange* range,
722                                             const char* type) {
723   if (range == nullptr || range->IsEmpty()) return;
724   int vreg = range->vreg();
725   for (const LiveRange* child = range; child != nullptr;
726        child = child->next()) {
727     PrintLiveRange(child, type, vreg);
728   }
729 }
730 
PrintLiveRange(const LiveRange * range,const char * type,int vreg)731 void GraphC1Visualizer::PrintLiveRange(const LiveRange* range, const char* type,
732                                        int vreg) {
733   if (range != nullptr && !range->IsEmpty()) {
734     PrintIndent();
735     os_ << vreg << ":" << range->relative_id() << " " << type;
736     if (range->HasRegisterAssigned()) {
737       AllocatedOperand op = AllocatedOperand::cast(range->GetAssignedOperand());
738       const auto config = RegisterConfiguration::Default();
739       if (op.IsRegister()) {
740         os_ << " \"" << config->GetGeneralRegisterName(op.register_code())
741             << "\"";
742       } else if (op.IsDoubleRegister()) {
743         os_ << " \"" << config->GetDoubleRegisterName(op.register_code())
744             << "\"";
745       } else {
746         DCHECK(op.IsFloatRegister());
747         os_ << " \"" << config->GetFloatRegisterName(op.register_code())
748             << "\"";
749       }
750     } else if (range->spilled()) {
751       const TopLevelLiveRange* top = range->TopLevel();
752       int index = -1;
753       if (top->HasSpillRange()) {
754         index = kMaxInt;  // This hasn't been set yet.
755       } else if (top->GetSpillOperand()->IsConstant()) {
756         os_ << " \"const(nostack):"
757             << ConstantOperand::cast(top->GetSpillOperand())->virtual_register()
758             << "\"";
759       } else {
760         index = AllocatedOperand::cast(top->GetSpillOperand())->index();
761         if (IsFloatingPoint(top->representation())) {
762           os_ << " \"fp_stack:" << index << "\"";
763         } else {
764           os_ << " \"stack:" << index << "\"";
765         }
766       }
767     }
768 
769     os_ << " " << vreg;
770     for (const UseInterval* interval = range->first_interval();
771          interval != nullptr; interval = interval->next()) {
772       os_ << " [" << interval->start().value() << ", "
773           << interval->end().value() << "[";
774     }
775 
776     UsePosition* current_pos = range->first_pos();
777     while (current_pos != nullptr) {
778       if (current_pos->RegisterIsBeneficial() || FLAG_trace_all_uses) {
779         os_ << " " << current_pos->pos().value() << " M";
780       }
781       current_pos = current_pos->next();
782     }
783 
784     os_ << " \"\"\n";
785   }
786 }
787 
788 
operator <<(std::ostream & os,const AsC1VCompilation & ac)789 std::ostream& operator<<(std::ostream& os, const AsC1VCompilation& ac) {
790   AccountingAllocator allocator;
791   Zone tmp_zone(&allocator, ZONE_NAME);
792   GraphC1Visualizer(os, &tmp_zone).PrintCompilation(ac.info_);
793   return os;
794 }
795 
796 
operator <<(std::ostream & os,const AsC1V & ac)797 std::ostream& operator<<(std::ostream& os, const AsC1V& ac) {
798   AccountingAllocator allocator;
799   Zone tmp_zone(&allocator, ZONE_NAME);
800   GraphC1Visualizer(os, &tmp_zone)
801       .PrintSchedule(ac.phase_, ac.schedule_, ac.positions_, ac.instructions_);
802   return os;
803 }
804 
805 
operator <<(std::ostream & os,const AsC1VRegisterAllocationData & ac)806 std::ostream& operator<<(std::ostream& os,
807                          const AsC1VRegisterAllocationData& ac) {
808   AccountingAllocator allocator;
809   Zone tmp_zone(&allocator, ZONE_NAME);
810   GraphC1Visualizer(os, &tmp_zone).PrintLiveRanges(ac.phase_, ac.data_);
811   return os;
812 }
813 
814 const int kUnvisited = 0;
815 const int kOnStack = 1;
816 const int kVisited = 2;
817 
operator <<(std::ostream & os,const AsRPO & ar)818 std::ostream& operator<<(std::ostream& os, const AsRPO& ar) {
819   AccountingAllocator allocator;
820   Zone local_zone(&allocator, ZONE_NAME);
821 
822   // Do a post-order depth-first search on the RPO graph. For every node,
823   // print:
824   //
825   //   - the node id
826   //   - the operator mnemonic
827   //   - in square brackets its parameter (if present)
828   //   - in parentheses the list of argument ids and their mnemonics
829   //   - the node type (if it is typed)
830 
831   // Post-order guarantees that all inputs of a node will be printed before
832   // the node itself, if there are no cycles. Any cycles are broken
833   // arbitrarily.
834 
835   ZoneVector<byte> state(ar.graph.NodeCount(), kUnvisited, &local_zone);
836   ZoneStack<Node*> stack(&local_zone);
837 
838   stack.push(ar.graph.end());
839   state[ar.graph.end()->id()] = kOnStack;
840   while (!stack.empty()) {
841     Node* n = stack.top();
842     bool pop = true;
843     for (Node* const i : n->inputs()) {
844       if (state[i->id()] == kUnvisited) {
845         state[i->id()] = kOnStack;
846         stack.push(i);
847         pop = false;
848         break;
849       }
850     }
851     if (pop) {
852       state[n->id()] = kVisited;
853       stack.pop();
854       os << "#" << n->id() << ":" << *n->op() << "(";
855       // Print the inputs.
856       int j = 0;
857       for (Node* const i : n->inputs()) {
858         if (j++ > 0) os << ", ";
859         os << "#" << SafeId(i) << ":" << SafeMnemonic(i);
860       }
861       os << ")";
862       // Print the node type, if any.
863       if (NodeProperties::IsTyped(n)) {
864         os << "  [Type: " << NodeProperties::GetType(n) << "]";
865       }
866       os << std::endl;
867     }
868   }
869   return os;
870 }
871 
872 namespace {
873 
PrintIndent(std::ostream & os,int indent)874 void PrintIndent(std::ostream& os, int indent) {
875   os << "     ";
876   for (int i = 0; i < indent; i++) {
877     os << ". ";
878   }
879 }
880 
PrintScheduledNode(std::ostream & os,int indent,Node * n)881 void PrintScheduledNode(std::ostream& os, int indent, Node* n) {
882   PrintIndent(os, indent);
883   os << "#" << n->id() << ":" << *n->op() << "(";
884   // Print the inputs.
885   int j = 0;
886   for (Node* const i : n->inputs()) {
887     if (j++ > 0) os << ", ";
888     os << "#" << SafeId(i) << ":" << SafeMnemonic(i);
889   }
890   os << ")";
891   // Print the node type, if any.
892   if (NodeProperties::IsTyped(n)) {
893     os << "  [Type: " << NodeProperties::GetType(n) << "]";
894   }
895 }
896 
PrintScheduledGraph(std::ostream & os,const Schedule * schedule)897 void PrintScheduledGraph(std::ostream& os, const Schedule* schedule) {
898   const BasicBlockVector* rpo = schedule->rpo_order();
899   for (size_t i = 0; i < rpo->size(); i++) {
900     BasicBlock* current = (*rpo)[i];
901     int indent = current->loop_depth();
902 
903     os << "  + Block B" << current->rpo_number() << " (pred:";
904     for (BasicBlock* predecessor : current->predecessors()) {
905       os << " B" << predecessor->rpo_number();
906     }
907     if (current->IsLoopHeader()) {
908       os << ", loop until B" << current->loop_end()->rpo_number();
909     } else if (current->loop_header()) {
910       os << ", in loop B" << current->loop_header()->rpo_number();
911     }
912     os << ")" << std::endl;
913 
914     for (BasicBlock::const_iterator i = current->begin(); i != current->end();
915          ++i) {
916       Node* node = *i;
917       PrintScheduledNode(os, indent, node);
918       os << std::endl;
919     }
920 
921     if (current->SuccessorCount() > 0) {
922       if (current->control_input() != nullptr) {
923         PrintScheduledNode(os, indent, current->control_input());
924       } else {
925         PrintIndent(os, indent);
926         os << "Goto";
927       }
928       os << " ->";
929 
930       bool isFirst = true;
931       for (BasicBlock* successor : current->successors()) {
932         if (isFirst) {
933           isFirst = false;
934         } else {
935           os << ",";
936         }
937         os << " B" << successor->rpo_number();
938       }
939       os << std::endl;
940     } else {
941       DCHECK_NULL(current->control_input());
942     }
943   }
944 }
945 
946 }  // namespace
947 
operator <<(std::ostream & os,const AsScheduledGraph & scheduled)948 std::ostream& operator<<(std::ostream& os, const AsScheduledGraph& scheduled) {
949   PrintScheduledGraph(os, scheduled.schedule);
950   return os;
951 }
952 
953 }  // namespace compiler
954 }  // namespace internal
955 }  // namespace v8
956