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