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