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