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