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