5 #include "src/compiler/graph-visualizer.h" 11 #include "src/code-stubs.h" 12 #include "src/compiler/all-nodes.h" 13 #include "src/compiler/backend/register-allocator.h" 14 #include "src/compiler/compiler-source-position-table.h" 15 #include "src/compiler/graph.h" 16 #include "src/compiler/node-origin-table.h" 17 #include "src/compiler/node-properties.h" 18 #include "src/compiler/node.h" 19 #include "src/compiler/opcodes.h" 20 #include "src/compiler/operator-properties.h" 21 #include "src/compiler/operator.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" 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"));
40 return info->trace_turbo_filename();
43 TurboJsonFile::TurboJsonFile(OptimizedCompilationInfo* info,
44 std::ios_base::openmode mode)
45 :
std::ofstream(get_cached_trace_turbo_filename(info), mode) {}
47 TurboJsonFile::~TurboJsonFile() { flush(); }
49 TurboCfgFile::TurboCfgFile(Isolate* isolate)
50 :
std::ofstream(Isolate::GetTurboCfgFileName(isolate).c_str(),
51 std::ios_base::app) {}
53 TurboCfgFile::~TurboCfgFile() { flush(); }
55 std::ostream& operator<<(std::ostream& out,
56 const SourcePositionAsJSON& asJSON) {
57 asJSON.sp.PrintJson(out);
61 std::ostream& operator<<(std::ostream& out,
const NodeOriginAsJSON& asJSON) {
62 asJSON.no.PrintJson(out);
66 void JsonPrintFunctionSource(std::ostream& os,
int source_id,
67 std::unique_ptr<
char[]> function_name,
68 Handle<Script> script, Isolate* isolate,
69 Handle<SharedFunctionInfo> shared,
bool with_key) {
70 if (with_key) os <<
"\"" << source_id <<
"\" : ";
73 os <<
"\"sourceId\": " << source_id;
74 os <<
", \"functionName\": \"" << function_name.get() <<
"\" ";
78 if (!script.is_null() && !script->IsUndefined(isolate) && !shared.is_null()) {
79 Object* source_name = script->name();
80 os <<
", \"sourceName\": \"";
81 if (source_name->IsString()) {
82 os << String::cast(source_name)->ToCString().get();
86 DisallowHeapAllocation no_allocation;
87 start = shared->StartPosition();
88 end = shared->EndPosition();
89 os <<
", \"sourceText\": \"";
90 int len = shared->EndPosition() - start;
91 SubStringRange source(String::cast(script->source()), start, len);
92 for (
const auto& c : source) {
93 os << AsEscapedUC16ForJSON(c);
98 os <<
", \"sourceName\": \"\"";
99 os <<
", \"sourceText\": \"\"";
101 os <<
", \"startPosition\": " << start;
102 os <<
", \"endPosition\": " << end;
106 int SourceIdAssigner::GetIdFor(Handle<SharedFunctionInfo> shared) {
107 for (
unsigned i = 0;
i < printed_.size();
i++) {
108 if (printed_.at(
i).is_identical_to(shared)) {
109 source_ids_.push_back(
i);
113 const int source_id =
static_cast<int>(printed_.size());
114 printed_.push_back(shared);
115 source_ids_.push_back(source_id);
121 void JsonPrintInlinedFunctionInfo(
122 std::ostream& os,
int source_id,
int inlining_id,
123 const OptimizedCompilationInfo::InlinedFunctionHolder& h) {
124 os <<
"\"" << inlining_id <<
"\" : ";
125 os <<
"{ \"inliningId\" : " << inlining_id;
126 os <<
", \"sourceId\" : " << source_id;
127 const SourcePosition position = h.position.position;
128 if (position.IsKnown()) {
129 os <<
", \"inliningPosition\" : " << AsJSON(position);
136 void JsonPrintAllSourceWithPositions(std::ostream& os,
137 OptimizedCompilationInfo* info,
139 AllowDeferredHandleDereference allow_deference_for_print_code;
140 os <<
"\"sources\" : {";
141 Handle<Script> script =
142 (info->shared_info().is_null() || !info->shared_info()->script())
144 : handle(Script::cast(info->shared_info()->script()), isolate);
145 JsonPrintFunctionSource(os, -1,
146 info->shared_info().is_null()
147 ? std::unique_ptr<char[]>(
new char[1]{0})
148 : info->shared_info()->DebugName()->ToCString(),
149 script, isolate, info->shared_info(),
true);
150 const auto& inlined = info->inlined_functions();
151 SourceIdAssigner id_assigner(info->inlined_functions().size());
152 for (
unsigned id = 0;
id < inlined.size();
id++) {
154 Handle<SharedFunctionInfo> shared = inlined[id].shared_info;
155 const int source_id = id_assigner.GetIdFor(shared);
156 JsonPrintFunctionSource(os, source_id, shared->DebugName()->ToCString(),
157 handle(Script::cast(shared->script()), isolate),
158 isolate, shared,
true);
161 os <<
"\"inlinings\" : {";
162 bool need_comma =
false;
163 for (
unsigned id = 0;
id < inlined.size();
id++) {
164 if (need_comma) os <<
", ";
165 const int source_id = id_assigner.GetIdAt(
id);
166 JsonPrintInlinedFunctionInfo(os, source_id,
id, inlined[
id]);
172 std::unique_ptr<char[]> GetVisualizerLogFileName(OptimizedCompilationInfo* info,
173 const char* optional_base_dir,
175 const char* suffix) {
176 EmbeddedVector<char, 256> filename(0);
177 std::unique_ptr<char[]> debug_name = info->GetDebugName();
178 int optimization_id = info->IsOptimizing() ? info->optimization_id() : 0;
179 if (strlen(debug_name.get()) > 0) {
180 SNPrintF(filename,
"turbo-%s-%i", debug_name.get(), optimization_id);
181 }
else if (info->has_shared_info()) {
182 SNPrintF(filename,
"turbo-%p-%i",
183 reinterpret_cast<void*>(info->shared_info()->address()),
186 SNPrintF(filename,
"turbo-none-%i", optimization_id);
188 EmbeddedVector<char, 256> source_file(0);
189 bool source_available =
false;
190 if (FLAG_trace_file_names && info->has_shared_info() &&
191 info->shared_info()->script()->IsScript()) {
192 Object* source_name = Script::cast(info->shared_info()->script())->name();
193 if (source_name->IsString()) {
194 String str = String::cast(source_name);
195 if (str->length() > 0) {
196 SNPrintF(source_file,
"%s", str->ToCString().get());
197 std::replace(source_file.start(),
198 source_file.start() + source_file.length(),
'/',
'_');
199 source_available =
true;
203 std::replace(filename.start(), filename.start() + filename.length(),
' ',
206 EmbeddedVector<char, 256> base_dir;
207 if (optional_base_dir !=
nullptr) {
208 SNPrintF(base_dir,
"%s%c", optional_base_dir,
209 base::OS::DirectorySeparator());
214 EmbeddedVector<char, 256> full_filename;
215 if (phase ==
nullptr && !source_available) {
216 SNPrintF(full_filename,
"%s%s.%s", base_dir.start(), filename.start(),
218 }
else if (phase !=
nullptr && !source_available) {
219 SNPrintF(full_filename,
"%s%s-%s.%s", base_dir.start(), filename.start(),
221 }
else if (phase ==
nullptr && source_available) {
222 SNPrintF(full_filename,
"%s%s_%s.%s", base_dir.start(), filename.start(),
223 source_file.start(), suffix);
225 SNPrintF(full_filename,
"%s%s_%s-%s.%s", base_dir.start(), filename.start(),
226 source_file.start(), phase, suffix);
229 char* buffer =
new char[full_filename.length() + 1];
230 memcpy(buffer, full_filename.start(), full_filename.length());
231 buffer[full_filename.length()] =
'\0';
232 return std::unique_ptr<char[]>(buffer);
236 static int SafeId(Node* node) {
return node ==
nullptr ? -1 : node->id(); }
237 static const char* SafeMnemonic(Node* node) {
238 return node ==
nullptr ?
"null" : node->op()->mnemonic();
243 explicit JSONEscaped(
const std::ostringstream& os) : str_(os.str()) {}
245 friend std::ostream& operator<<(std::ostream& os,
const JSONEscaped& e) {
246 for (
char c : e.str_) PipeCharacter(os, c);
251 static std::ostream& PipeCharacter(std::ostream& os,
char c) {
252 if (c ==
'"')
return os <<
"\\\"";
253 if (c ==
'\\')
return os <<
"\\\\";
254 if (c ==
'\b')
return os <<
"\\b";
255 if (c ==
'\f')
return os <<
"\\f";
256 if (c ==
'\n')
return os <<
"\\n";
257 if (c ==
'\r')
return os <<
"\\r";
258 if (c ==
'\t')
return os <<
"\\t";
262 const std::string str_;
271 all_(zone, graph,
false),
272 live_(zone, graph,
true),
273 positions_(positions),
278 for (
Node*
const node : all_.reachable) PrintNode(node);
282 void PrintNode(
Node* node) {
288 std::ostringstream label, title, properties;
289 node->op()->PrintTo(label, Operator::PrintVerbosity::kSilent);
290 node->op()->PrintTo(title, Operator::PrintVerbosity::kVerbose);
291 node->op()->PrintPropsTo(properties);
292 os_ <<
"{\"id\":" << SafeId(node) <<
",\"label\":\"" <<
JSONEscaped(label)
295 <<
",\"live\": " << (live_.IsLive(node) ?
"true" :
"false")
296 <<
",\"properties\":\"" <<
JSONEscaped(properties) <<
"\"";
297 IrOpcode::Value opcode = node->opcode();
298 if (IrOpcode::IsPhiOpcode(opcode)) {
299 os_ <<
",\"rankInputs\":[0," << NodeProperties::FirstControlIndex(node)
301 os_ <<
",\"rankWithInput\":[" << NodeProperties::FirstControlIndex(node)
303 }
else if (opcode == IrOpcode::kIfTrue || opcode == IrOpcode::kIfFalse ||
304 opcode == IrOpcode::kLoop) {
305 os_ <<
",\"rankInputs\":[" << NodeProperties::FirstControlIndex(node)
308 if (opcode == IrOpcode::kBranch) {
309 os_ <<
",\"rankInputs\":[0]";
311 if (positions_ !=
nullptr) {
313 if (position.IsKnown()) {
314 os_ <<
", \"sourcePosition\" : " << AsJSON(position);
318 NodeOrigin origin = origins_->GetNodeOrigin(node);
319 if (origin.IsKnown()) {
320 os_ <<
", \"origin\" : " << AsJSON(origin);
323 os_ <<
",\"opcode\":\"" << IrOpcode::Mnemonic(node->opcode()) <<
"\"";
324 os_ <<
",\"control\":" << (NodeProperties::IsControl(node) ?
"true" 326 os_ <<
",\"opinfo\":\"" << node->op()->ValueInputCount() <<
" v " 327 << node->op()->EffectInputCount() <<
" eff " 328 << node->op()->ControlInputCount() <<
" ctrl in, " 329 << node->op()->ValueOutputCount() <<
" v " 330 << node->op()->EffectOutputCount() <<
" eff " 331 << node->op()->ControlOutputCount() <<
" ctrl out\"";
332 if (NodeProperties::IsTyped(node)) {
333 Type type = NodeProperties::GetType(node);
334 std::ostringstream type_out;
335 type.PrintTo(type_out);
336 os_ <<
",\"type\":\"" <<
JSONEscaped(type_out) <<
"\"";
356 : os_(os), all_(zone, graph,
false), first_edge_(
true) {}
359 for (
Node*
const node : all_.reachable) PrintEdges(node);
363 void PrintEdges(
Node* node) {
364 for (
int i = 0;
i < node->InputCount();
i++) {
365 Node* input = node->InputAt(
i);
366 if (input ==
nullptr)
continue;
367 PrintEdge(node,
i, input);
371 void PrintEdge(
Node* from,
int index,
Node* to) {
377 const char* edge_type =
nullptr;
378 if (index < NodeProperties::FirstValueIndex(from)) {
379 edge_type =
"unknown";
380 }
else if (index < NodeProperties::FirstContextIndex(from)) {
382 }
else if (index < NodeProperties::FirstFrameStateIndex(from)) {
383 edge_type =
"context";
384 }
else if (index < NodeProperties::FirstEffectIndex(from)) {
385 edge_type =
"frame-state";
386 }
else if (index < NodeProperties::FirstControlIndex(from)) {
387 edge_type =
"effect";
389 edge_type =
"control";
391 os_ <<
"{\"source\":" << SafeId(to) <<
",\"target\":" << SafeId(from)
392 <<
",\"index\":" << index <<
",\"type\":\"" << edge_type <<
"\"}";
403 std::ostream& operator<<(std::ostream& os,
const GraphAsJSON& ad) {
405 Zone tmp_zone(&allocator, ZONE_NAME);
406 os <<
"{\n\"nodes\":[";
409 os <<
"],\n\"edges\":[";
421 void PrintSchedule(
const char* phase,
const Schedule* schedule,
425 Zone* zone()
const {
return zone_; }
429 void PrintStringProperty(
const char* name,
const char* value);
430 void PrintLongProperty(
const char* name,
int64_t value);
431 void PrintIntProperty(
const char* name,
int value);
432 void PrintBlockProperty(
const char* name,
int rpo_number);
433 void PrintNodeId(
Node* n);
434 void PrintNode(
Node* n);
435 void PrintInputs(
Node* n);
436 template <
typename InputIterator>
437 void PrintInputs(InputIterator*
i,
int count,
const char* prefix);
438 void PrintType(
Node* node);
440 void PrintLiveRange(
const LiveRange* range,
const char*
type,
int vreg);
447 visualizer_ = visualizer;
448 visualizer->PrintIndent();
449 visualizer_->os_ <<
"begin_" << name <<
"\n";
450 visualizer->indent_++;
454 visualizer_->indent_--;
455 visualizer_->PrintIndent();
456 visualizer_->os_ <<
"end_" << name_ <<
"\n";
457 DCHECK_LE(0, visualizer_->indent_);
473 void GraphC1Visualizer::PrintIndent() {
474 for (
int i = 0;
i < indent_;
i++) {
480 GraphC1Visualizer::GraphC1Visualizer(std::ostream& os, Zone* zone)
481 : os_(os), indent_(0), zone_(zone) {}
484 void GraphC1Visualizer::PrintStringProperty(
const char* name,
487 os_ << name <<
" \"" << value <<
"\"\n";
491 void GraphC1Visualizer::PrintLongProperty(
const char* name,
int64_t value) {
493 os_ << name <<
" " <<
static_cast<int>(value / 1000) <<
"\n";
497 void GraphC1Visualizer::PrintBlockProperty(
const char* name,
int rpo_number) {
499 os_ << name <<
" \"B" << rpo_number <<
"\"\n";
503 void GraphC1Visualizer::PrintIntProperty(
const char* name,
int value) {
505 os_ << name <<
" " << value <<
"\n";
508 void GraphC1Visualizer::PrintCompilation(
const OptimizedCompilationInfo* info) {
509 Tag tag(
this,
"compilation");
510 std::unique_ptr<char[]> name = info->GetDebugName();
511 if (info->IsOptimizing()) {
512 PrintStringProperty(
"name", name.get());
514 os_ <<
"method \"" << name.get() <<
":" << info->optimization_id()
517 PrintStringProperty(
"name", name.get());
518 PrintStringProperty(
"method",
"stub");
522 static_cast<int64_t>(V8::GetCurrentPlatform()->CurrentClockTimeMillis()));
526 void GraphC1Visualizer::PrintNodeId(Node* n) { os_ <<
"n" << SafeId(n); }
529 void GraphC1Visualizer::PrintNode(Node* n) {
531 os_ <<
" " << *n->op() <<
" ";
536 template <
typename InputIterator>
537 void GraphC1Visualizer::PrintInputs(InputIterator*
i,
int count,
538 const char* prefix) {
551 void GraphC1Visualizer::PrintInputs(Node* node) {
552 auto i = node->inputs().begin();
553 PrintInputs(&
i, node->op()->ValueInputCount(),
" ");
554 PrintInputs(&
i, OperatorProperties::GetContextInputCount(node->op()),
556 PrintInputs(&
i, OperatorProperties::GetFrameStateInputCount(node->op()),
558 PrintInputs(&
i, node->op()->EffectInputCount(),
" Eff:");
559 PrintInputs(&
i, node->op()->ControlInputCount(),
" Ctrl:");
563 void GraphC1Visualizer::PrintType(Node* node) {
564 if (NodeProperties::IsTyped(node)) {
565 Type type = NodeProperties::GetType(node);
566 os_ <<
" type:" << type;
571 void GraphC1Visualizer::PrintSchedule(
const char* phase,
572 const Schedule* schedule,
573 const SourcePositionTable* positions,
574 const InstructionSequence* instructions) {
575 Tag tag(
this,
"cfg");
576 PrintStringProperty(
"name", phase);
577 const BasicBlockVector* rpo = schedule->rpo_order();
578 for (
size_t i = 0;
i < rpo->size();
i++) {
579 BasicBlock* current = (*rpo)[
i];
580 Tag block_tag(
this,
"block");
581 PrintBlockProperty(
"name", current->rpo_number());
582 PrintIntProperty(
"from_bci", -1);
583 PrintIntProperty(
"to_bci", -1);
586 os_ <<
"predecessors";
587 for (BasicBlock* predecessor : current->predecessors()) {
588 os_ <<
" \"B" << predecessor->rpo_number() <<
"\"";
594 for (BasicBlock* successor : current->successors()) {
595 os_ <<
" \"B" << successor->rpo_number() <<
"\"";
600 os_ <<
"xhandlers\n";
605 if (current->dominator() !=
nullptr) {
606 PrintBlockProperty(
"dominator", current->dominator()->rpo_number());
609 PrintIntProperty(
"loop_depth", current->loop_depth());
611 const InstructionBlock* instruction_block =
612 instructions->InstructionBlockAt(
613 RpoNumber::FromInt(current->rpo_number()));
614 if (instruction_block->code_start() >= 0) {
615 int first_index = instruction_block->first_instruction_index();
616 int last_index = instruction_block->last_instruction_index();
619 LifetimePosition::GapFromInstructionIndex(first_index).value());
620 PrintIntProperty(
"last_lir_id",
621 LifetimePosition::InstructionFromInstructionIndex(
622 last_index).value());
626 Tag states_tag(
this,
"states");
627 Tag locals_tag(
this,
"locals");
629 for (BasicBlock::const_iterator
i = current->begin();
i != current->end();
631 if ((*i)->opcode() == IrOpcode::kPhi) total++;
633 PrintIntProperty(
"size", total);
634 PrintStringProperty(
"method",
"None");
636 for (BasicBlock::const_iterator
i = current->begin();
i != current->end();
638 if ((*i)->opcode() != IrOpcode::kPhi)
continue;
650 Tag HIR_tag(
this,
"HIR");
651 for (BasicBlock::const_iterator
i = current->begin();
i != current->end();
654 if (node->opcode() == IrOpcode::kPhi)
continue;
655 int uses = node->UseCount();
657 os_ <<
"0 " << uses <<
" ";
659 if (FLAG_trace_turbo_types) {
663 if (positions !=
nullptr) {
664 SourcePosition position = positions->GetSourcePosition(node);
665 if (position.IsKnown()) {
667 if (position.isInlined()) {
668 os_ <<
"inlining(" << position.InliningId() <<
"),";
670 os_ << position.ScriptOffset();
676 BasicBlock::Control control = current->control();
677 if (control != BasicBlock::kNone) {
680 if (current->control_input() !=
nullptr) {
681 PrintNode(current->control_input());
683 os_ << -1 - current->rpo_number() <<
" Goto";
686 for (BasicBlock* successor : current->successors()) {
687 os_ <<
" B" << successor->rpo_number();
689 if (FLAG_trace_turbo_types && current->control_input() !=
nullptr) {
691 PrintType(current->control_input());
697 if (instructions !=
nullptr) {
698 Tag LIR_tag(
this,
"LIR");
699 for (
int j = instruction_block->first_instruction_index();
700 j <= instruction_block->last_instruction_index(); j++) {
702 os_ << j <<
" " << *instructions->InstructionAt(j) <<
" <|@\n";
709 void GraphC1Visualizer::PrintLiveRanges(
const char* phase,
710 const RegisterAllocationData* data) {
711 Tag tag(
this,
"intervals");
712 PrintStringProperty(
"name", phase);
714 for (
const TopLevelLiveRange* range : data->fixed_double_live_ranges()) {
715 PrintLiveRangeChain(range,
"fixed");
718 for (
const TopLevelLiveRange* range : data->fixed_live_ranges()) {
719 PrintLiveRangeChain(range,
"fixed");
722 for (
const TopLevelLiveRange* range : data->live_ranges()) {
723 PrintLiveRangeChain(range,
"object");
727 void GraphC1Visualizer::PrintLiveRangeChain(
const TopLevelLiveRange* range,
729 if (range ==
nullptr || range->IsEmpty())
return;
730 int vreg = range->vreg();
731 for (
const LiveRange* child = range; child !=
nullptr;
732 child = child->next()) {
733 PrintLiveRange(child, type, vreg);
737 void GraphC1Visualizer::PrintLiveRange(
const LiveRange* range,
const char* type,
739 if (range !=
nullptr && !range->IsEmpty()) {
741 os_ << vreg <<
":" << range->relative_id() <<
" " << type;
742 if (range->HasRegisterAssigned()) {
743 AllocatedOperand op = AllocatedOperand::cast(range->GetAssignedOperand());
744 if (op.IsRegister()) {
745 os_ <<
" \"" << Register::from_code(op.register_code()) <<
"\"";
746 }
else if (op.IsDoubleRegister()) {
747 os_ <<
" \"" << DoubleRegister::from_code(op.register_code()) <<
"\"";
749 DCHECK(op.IsFloatRegister());
750 os_ <<
" \"" << FloatRegister::from_code(op.register_code()) <<
"\"";
752 }
else if (range->spilled()) {
753 const TopLevelLiveRange* top = range->TopLevel();
755 if (top->HasSpillRange()) {
757 }
else if (top->GetSpillOperand()->IsConstant()) {
758 os_ <<
" \"const(nostack):" 759 << ConstantOperand::cast(top->GetSpillOperand())->virtual_register()
762 index = AllocatedOperand::cast(top->GetSpillOperand())->index();
763 if (IsFloatingPoint(top->representation())) {
764 os_ <<
" \"fp_stack:" << index <<
"\"";
766 os_ <<
" \"stack:" << index <<
"\"";
773 const TopLevelLiveRange* parent = range->TopLevel();
774 if (parent->IsSplinter()) parent = parent->splintered_from();
775 os_ <<
" " << parent->vreg() <<
":" << parent->relative_id();
780 for (
const UseInterval* interval = range->first_interval();
781 interval !=
nullptr; interval = interval->next()) {
782 os_ <<
" [" << interval->start().value() <<
", " 783 << interval->end().value() <<
"[";
786 UsePosition* current_pos = range->first_pos();
787 while (current_pos !=
nullptr) {
788 if (current_pos->RegisterIsBeneficial() || FLAG_trace_all_uses) {
789 os_ <<
" " << current_pos->pos().value() <<
" M";
791 current_pos = current_pos->next();
799 std::ostream& operator<<(std::ostream& os,
const AsC1VCompilation& ac) {
800 AccountingAllocator allocator;
801 Zone tmp_zone(&allocator, ZONE_NAME);
802 GraphC1Visualizer(os, &tmp_zone).PrintCompilation(ac.info_);
807 std::ostream& operator<<(std::ostream& os,
const AsC1V& ac) {
808 AccountingAllocator allocator;
809 Zone tmp_zone(&allocator, ZONE_NAME);
810 GraphC1Visualizer(os, &tmp_zone)
811 .PrintSchedule(ac.phase_, ac.schedule_, ac.positions_, ac.instructions_);
816 std::ostream& operator<<(std::ostream& os,
817 const AsC1VRegisterAllocationData& ac) {
818 AccountingAllocator allocator;
819 Zone tmp_zone(&allocator, ZONE_NAME);
820 GraphC1Visualizer(os, &tmp_zone).PrintLiveRanges(ac.phase_, ac.data_);
824 const int kUnvisited = 0;
825 const int kOnStack = 1;
826 const int kVisited = 2;
828 std::ostream& operator<<(std::ostream& os,
const AsRPO& ar) {
829 AccountingAllocator allocator;
830 Zone local_zone(&allocator, ZONE_NAME);
845 ZoneVector<byte> state(ar.graph.NodeCount(), kUnvisited, &local_zone);
846 ZoneStack<Node*> stack(&local_zone);
848 stack.push(ar.graph.end());
849 state[ar.graph.end()->id()] = kOnStack;
850 while (!stack.empty()) {
851 Node* n = stack.top();
853 for (Node*
const i : n->inputs()) {
854 if (state[
i->id()] == kUnvisited) {
855 state[
i->id()] = kOnStack;
862 state[n->id()] = kVisited;
864 os <<
"#" << n->id() <<
":" << *n->op() <<
"(";
867 for (Node*
const i : n->inputs()) {
868 if (j++ > 0) os <<
", ";
869 os <<
"#" << SafeId(
i) <<
":" << SafeMnemonic(
i);
873 if (NodeProperties::IsTyped(n)) {
874 os <<
" [Type: " << NodeProperties::GetType(n) <<
"]";
884 void PrintIndent(std::ostream& os,
int indent) {
886 for (
int i = 0;
i < indent;
i++) {
891 void PrintScheduledNode(std::ostream& os,
int indent, Node* n) {
892 PrintIndent(os, indent);
893 os <<
"#" << n->id() <<
":" << *n->op() <<
"(";
896 for (Node*
const i : n->inputs()) {
897 if (j++ > 0) os <<
", ";
898 os <<
"#" << SafeId(
i) <<
":" << SafeMnemonic(
i);
902 if (NodeProperties::IsTyped(n)) {
903 os <<
" [Type: " << NodeProperties::GetType(n) <<
"]";
907 void PrintScheduledGraph(std::ostream& os,
const Schedule* schedule) {
908 const BasicBlockVector* rpo = schedule->rpo_order();
909 for (
size_t i = 0;
i < rpo->size();
i++) {
910 BasicBlock* current = (*rpo)[
i];
911 int indent = current->loop_depth();
913 os <<
" + Block B" << current->rpo_number() <<
" (pred:";
914 for (BasicBlock* predecessor : current->predecessors()) {
915 os <<
" B" << predecessor->rpo_number();
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();
922 os <<
")" << std::endl;
924 for (BasicBlock::const_iterator
i = current->begin();
i != current->end();
927 PrintScheduledNode(os, indent, node);
931 if (current->SuccessorCount() > 0) {
932 if (current->control_input() !=
nullptr) {
933 PrintScheduledNode(os, indent, current->control_input());
935 PrintIndent(os, indent);
941 for (BasicBlock* successor : current->successors()) {
947 os <<
" B" << successor->rpo_number();
951 DCHECK_NULL(current->control_input());
958 std::ostream& operator<<(std::ostream& os,
const AsScheduledGraph& scheduled) {
959 PrintScheduledGraph(os, scheduled.schedule);
963 std::ostream& operator<<(std::ostream& os,
const InstructionOperandAsJSON& o) {
964 const InstructionOperand* op = o.op_;
965 const InstructionSequence* code = o.code_;
967 switch (op->kind()) {
968 case InstructionOperand::UNALLOCATED: {
969 const UnallocatedOperand* unalloc = UnallocatedOperand::cast(op);
970 os <<
"\"type\": \"unallocated\", ";
971 os <<
"\"text\": \"v" << unalloc->virtual_register() <<
"\"";
972 if (unalloc->basic_policy() == UnallocatedOperand::FIXED_SLOT) {
973 os <<
",\"tooltip\": \"FIXED_SLOT: " << unalloc->fixed_slot_index()
977 switch (unalloc->extended_policy()) {
978 case UnallocatedOperand::NONE:
980 case UnallocatedOperand::FIXED_REGISTER: {
981 os <<
",\"tooltip\": \"FIXED_REGISTER: " 982 << Register::from_code(unalloc->fixed_register_index()) <<
"\"";
985 case UnallocatedOperand::FIXED_FP_REGISTER: {
986 os <<
",\"tooltip\": \"FIXED_FP_REGISTER: " 987 << DoubleRegister::from_code(unalloc->fixed_register_index())
991 case UnallocatedOperand::MUST_HAVE_REGISTER: {
992 os <<
",\"tooltip\": \"MUST_HAVE_REGISTER\"";
995 case UnallocatedOperand::MUST_HAVE_SLOT: {
996 os <<
",\"tooltip\": \"MUST_HAVE_SLOT\"";
999 case UnallocatedOperand::SAME_AS_FIRST_INPUT: {
1000 os <<
",\"tooltip\": \"SAME_AS_FIRST_INPUT\"";
1003 case UnallocatedOperand::REGISTER_OR_SLOT: {
1004 os <<
",\"tooltip\": \"REGISTER_OR_SLOT\"";
1007 case UnallocatedOperand::REGISTER_OR_SLOT_OR_CONSTANT: {
1008 os <<
",\"tooltip\": \"REGISTER_OR_SLOT_OR_CONSTANT\"";
1014 case InstructionOperand::CONSTANT: {
1015 int vreg = ConstantOperand::cast(op)->virtual_register();
1016 os <<
"\"type\": \"constant\", ";
1017 os <<
"\"text\": \"v" << vreg <<
"\",";
1018 os <<
"\"tooltip\": \"";
1019 std::stringstream tooltip;
1020 tooltip << code->GetConstant(vreg);
1021 for (
const auto& c : tooltip.str()) {
1022 os << AsEscapedUC16ForJSON(c);
1027 case InstructionOperand::IMMEDIATE: {
1028 os <<
"\"type\": \"immediate\", ";
1029 const ImmediateOperand* imm = ImmediateOperand::cast(op);
1030 switch (imm->type()) {
1031 case ImmediateOperand::INLINE: {
1032 os <<
"\"text\": \"#" << imm->inline_value() <<
"\"";
1035 case ImmediateOperand::INDEXED: {
1036 int index = imm->indexed_value();
1037 os <<
"\"text\": \"imm:" << index <<
"\",";
1038 os <<
"\"tooltip\": \"";
1039 std::stringstream tooltip;
1040 tooltip << code->GetImmediate(imm);
1041 for (
const auto& c : tooltip.str()) {
1042 os << AsEscapedUC16ForJSON(c);
1050 case InstructionOperand::EXPLICIT:
1051 case InstructionOperand::ALLOCATED: {
1052 const LocationOperand* allocated = LocationOperand::cast(op);
1054 if (allocated->IsExplicit()) {
1055 os <<
"\"explicit\", ";
1057 os <<
"\"allocated\", ";
1059 os <<
"\"text\": \"";
1060 if (op->IsStackSlot()) {
1061 os <<
"stack:" << allocated->index();
1062 }
else if (op->IsFPStackSlot()) {
1063 os <<
"fp_stack:" << allocated->index();
1064 }
else if (op->IsRegister()) {
1065 if (allocated->register_code() < Register::kNumRegisters) {
1066 os << Register::from_code(allocated->register_code());
1068 os << Assembler::GetSpecialRegisterName(allocated->register_code());
1070 }
else if (op->IsDoubleRegister()) {
1071 os << DoubleRegister::from_code(allocated->register_code());
1072 }
else if (op->IsFloatRegister()) {
1073 os << FloatRegister::from_code(allocated->register_code());
1075 DCHECK(op->IsSimd128Register());
1076 os << Simd128Register::from_code(allocated->register_code());
1079 os <<
"\"tooltip\": \"" 1080 << MachineReprToString(allocated->representation()) <<
"\"";
1083 case InstructionOperand::INVALID:
1090 std::ostream& operator<<(std::ostream& os,
const InstructionAsJSON& i_json) {
1091 const Instruction* instr = i_json.instr_;
1094 os <<
"\"id\": " << i_json.index_ <<
",";
1095 os <<
"\"opcode\": \"" << ArchOpcodeField::decode(instr->opcode()) <<
"\",";
1096 os <<
"\"flags\": \"";
1097 FlagsMode fm = FlagsModeField::decode(instr->opcode());
1098 AddressingMode am = AddressingModeField::decode(instr->opcode());
1099 if (am != kMode_None) {
1100 os <<
" : " << AddressingModeField::decode(instr->opcode());
1102 if (fm != kFlags_none) {
1103 os <<
" && " << fm <<
" if " 1104 << FlagsConditionField::decode(instr->opcode());
1108 os <<
"\"gaps\": [";
1109 for (
int i = Instruction::FIRST_GAP_POSITION;
1110 i <= Instruction::LAST_GAP_POSITION;
i++) {
1111 if (
i != Instruction::FIRST_GAP_POSITION) os <<
",";
1113 const ParallelMove* pm = instr->parallel_moves()[
i];
1114 if (pm ==
nullptr) {
1119 for (MoveOperands* move : *pm) {
1120 if (move->IsEliminated())
continue;
1121 if (!first) os <<
",";
1123 os <<
"[" << InstructionOperandAsJSON{&move->destination(), i_json.code_}
1124 <<
"," << InstructionOperandAsJSON{&move->source(), i_json.code_}
1131 os <<
"\"outputs\": [";
1132 bool need_comma =
false;
1133 for (
size_t i = 0;
i < instr->OutputCount();
i++) {
1134 if (need_comma) os <<
",";
1136 os << InstructionOperandAsJSON{instr->OutputAt(
i), i_json.code_};
1140 os <<
"\"inputs\": [";
1142 for (
size_t i = 0;
i < instr->InputCount();
i++) {
1143 if (need_comma) os <<
",";
1145 os << InstructionOperandAsJSON{instr->InputAt(
i), i_json.code_};
1149 os <<
"\"temps\": [";
1151 for (
size_t i = 0;
i < instr->TempCount();
i++) {
1152 if (need_comma) os <<
",";
1154 os << InstructionOperandAsJSON{instr->TempAt(
i), i_json.code_};
1162 std::ostream& operator<<(std::ostream& os,
const InstructionBlockAsJSON& b) {
1163 const InstructionBlock* block = b.block_;
1164 const InstructionSequence* code = b.code_;
1166 os <<
"\"id\": " << block->rpo_number() <<
",";
1167 os <<
"\"deferred\": " << block->IsDeferred() <<
",";
1168 os <<
"\"loop_header\": " << block->IsLoopHeader() <<
",";
1169 if (block->IsLoopHeader()) {
1170 os <<
"\"loop_end\": " << block->loop_end() <<
",";
1172 os <<
"\"predecessors\": [";
1173 bool need_comma =
false;
1174 for (RpoNumber pred : block->predecessors()) {
1175 if (need_comma) os <<
",";
1180 os <<
"\"successors\": [";
1182 for (RpoNumber succ : block->successors()) {
1183 if (need_comma) os <<
",";
1188 os <<
"\"phis\": [";
1189 bool needs_comma =
false;
1190 InstructionOperandAsJSON json_op = {
nullptr, code};
1191 for (
const PhiInstruction* phi : block->phis()) {
1192 if (needs_comma) os <<
",";
1194 json_op.op_ = &phi->output();
1195 os <<
"{\"output\" : " << json_op <<
",";
1196 os <<
"\"operands\": [";
1197 bool op_needs_comma =
false;
1198 for (
int input : phi->operands()) {
1199 if (op_needs_comma) os <<
",";
1200 op_needs_comma =
true;
1201 os <<
"\"v" << input <<
"\"";
1207 os <<
"\"instructions\": [";
1208 InstructionAsJSON json_instr = {-1,
nullptr, code};
1210 for (
int j = block->first_instruction_index();
1211 j <= block->last_instruction_index(); j++) {
1212 if (need_comma) os <<
",";
1214 json_instr.index_ = j;
1215 json_instr.instr_ = code->InstructionAt(j);
1224 std::ostream& operator<<(std::ostream& os,
const InstructionSequenceAsJSON& s) {
1225 const InstructionSequence* code = s.sequence_;
1227 os <<
"\"blocks\": [";
1229 bool need_comma =
false;
1230 for (
int i = 0;
i < code->InstructionBlockCount();
i++) {
1231 if (need_comma) os <<
",";
1233 os << InstructionBlockAsJSON{
1234 code->InstructionBlockAt(RpoNumber::FromInt(
i)), code};