Bug Summary

File:out/../deps/v8/src/compiler/graph-visualizer.cc
Warning:line 750, column 9
Value stored to 'index' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

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