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 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 43 TurboJsonFile::TurboJsonFile(OptimizedCompilationInfo* info, 44 std::ios_base::openmode mode) 45 : std::ofstream(get_cached_trace_turbo_filename(info), mode) {} 46 47 TurboJsonFile::~TurboJsonFile() { flush(); } 48 49 std::ostream& operator<<(std::ostream& out, 50 const SourcePositionAsJSON& asJSON) { 51 asJSON.sp.PrintJson(out); 52 return out; 53 } 54 55 std::ostream& operator<<(std::ostream& out, const NodeOriginAsJSON& asJSON) { 56 asJSON.no.PrintJson(out); 57 return out; 58 } 59 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 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 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 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 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 230 static int SafeId(Node* node) { return node == nullptr ? -1 : node->id(); } 231 static const char* SafeMnemonic(Node* node) { 232 return node == nullptr ? "null" : node->op()->mnemonic(); 233 } 234 235 class JSONEscaped { 236 public: 237 explicit JSONEscaped(const std::ostringstream& os) : str_(os.str()) {} 238 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: 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: 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 271 void Print() { 272 for (Node* const node : all_.reachable) PrintNode(node); 273 os_ << "\n"; 274 } 275 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: 347 JSONGraphEdgeWriter(std::ostream& os, Zone* zone, const Graph* graph) 348 : os_(os), all_(zone, graph, false), first_edge_(true) {} 349 350 void Print() { 351 for (Node* const node : all_.reachable) PrintEdges(node); 352 os_ << "\n"; 353 } 354 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 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 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); 417 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: 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 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 465 void GraphC1Visualizer::PrintIndent() { 466 for (int i = 0; i < indent_; i++) { 467 os_ << " "; 468 } 469 } 470 471 472 GraphC1Visualizer::GraphC1Visualizer(std::ostream& os, Zone* zone) 473 : os_(os), indent_(0), zone_(zone) {} 474 475 476 void GraphC1Visualizer::PrintStringProperty(const char* name, 477 const char* value) { 478 PrintIndent(); 479 os_ << name << " \"" << value << "\"\n"; 480 } 481 482 483 void GraphC1Visualizer::PrintLongProperty(const char* name, int64_t value) { 484 PrintIndent(); 485 os_ << name << " " << static_cast<int>(value / 1000) << "\n"; 486 } 487 488 489 void GraphC1Visualizer::PrintBlockProperty(const char* name, int rpo_number) { 490 PrintIndent(); 491 os_ << name << " \"B" << rpo_number << "\"\n"; 492 } 493 494 495 void GraphC1Visualizer::PrintIntProperty(const char* name, int value) { 496 PrintIndent(); 497 os_ << name << " " << value << "\n"; 498 } 499 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 518 void GraphC1Visualizer::PrintNodeId(Node* n) { os_ << "n" << SafeId(n); } 519 520 521 void GraphC1Visualizer::PrintNode(Node* n) { 522 PrintNodeId(n); 523 os_ << " " << *n->op() << " "; 524 PrintInputs(n); 525 } 526 527 528 template <typename InputIterator> 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 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 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 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 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 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 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 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 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 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 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 874 void PrintIndent(std::ostream& os, int indent) { 875 os << " "; 876 for (int i = 0; i < indent; i++) { 877 os << ". "; 878 } 879 } 880 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 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 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