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