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