File: | out/../deps/v8/src/compiler/graph-visualizer.cc |
Warning: | line 750, column 9 Value stored to 'index' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | |
33 | namespace v8 { |
34 | namespace internal { |
35 | namespace compiler { |
36 | |
37 | const 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 | |
45 | TurboJsonFile::TurboJsonFile(OptimizedCompilationInfo* info, |
46 | std::ios_base::openmode mode) |
47 | : std::ofstream(get_cached_trace_turbo_filename(info), mode) {} |
48 | |
49 | TurboJsonFile::~TurboJsonFile() { flush(); } |
50 | |
51 | TurboCfgFile::TurboCfgFile(Isolate* isolate) |
52 | : std::ofstream(Isolate::GetTurboCfgFileName(isolate).c_str(), |
53 | std::ios_base::app) {} |
54 | |
55 | TurboCfgFile::~TurboCfgFile() { flush(); } |
56 | |
57 | std::ostream& operator<<(std::ostream& out, |
58 | const SourcePositionAsJSON& asJSON) { |
59 | asJSON.sp.PrintJson(out); |
60 | return out; |
61 | } |
62 | |
63 | std::ostream& operator<<(std::ostream& out, const NodeOriginAsJSON& asJSON) { |
64 | asJSON.no.PrintJson(out); |
65 | return out; |
66 | } |
67 | |
68 | class 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 | |
92 | void 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 | |
134 | int 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 | |
147 | namespace { |
148 | |
149 | void 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 | |
164 | void 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 | |
200 | std::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 | |
266 | static int SafeId(Node* node) { return node == nullptr ? -1 : node->id(); } |
267 | static const char* SafeMnemonic(Node* node) { |
268 | return node == nullptr ? "null" : node->op()->mnemonic(); |
269 | } |
270 | |
271 | JSONGraphWriter::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 | |
282 | void JSONGraphWriter::PrintPhase(const char* phase_name) { |
283 | os_ << "{\"name\":\"" << phase_name << "\",\"type\":\"graph\",\"data\":"; |
284 | Print(); |
285 | os_ << "},\n"; |
286 | } |
287 | |
288 | void 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 | |
306 | void 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 | |
364 | void 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 | |
372 | void 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 | |
396 | base::Optional<Type> JSONGraphWriter::GetType(Node* node) { |
397 | if (!NodeProperties::IsTyped(node)) return base::nullopt; |
398 | return NodeProperties::GetType(node); |
399 | } |
400 | |
401 | std::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 | |
408 | class 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 | |
466 | void GraphC1Visualizer::PrintIndent() { |
467 | for (int i = 0; i < indent_; i++) { |
468 | os_ << " "; |
469 | } |
470 | } |
471 | |
472 | |
473 | GraphC1Visualizer::GraphC1Visualizer(std::ostream& os, Zone* zone) |
474 | : os_(os), indent_(0), zone_(zone) {} |
475 | |
476 | |
477 | void GraphC1Visualizer::PrintStringProperty(const char* name, |
478 | const char* value) { |
479 | PrintIndent(); |
480 | os_ << name << " \"" << value << "\"\n"; |
481 | } |
482 | |
483 | |
484 | void GraphC1Visualizer::PrintLongProperty(const char* name, int64_t value) { |
485 | PrintIndent(); |
486 | os_ << name << " " << static_cast<int>(value / 1000) << "\n"; |
487 | } |
488 | |
489 | |
490 | void GraphC1Visualizer::PrintBlockProperty(const char* name, int rpo_number) { |
491 | PrintIndent(); |
492 | os_ << name << " \"B" << rpo_number << "\"\n"; |
493 | } |
494 | |
495 | |
496 | void GraphC1Visualizer::PrintIntProperty(const char* name, int value) { |
497 | PrintIndent(); |
498 | os_ << name << " " << value << "\n"; |
499 | } |
500 | |
501 | void 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 | |
519 | void GraphC1Visualizer::PrintNodeId(Node* n) { os_ << "n" << SafeId(n); } |
520 | |
521 | |
522 | void GraphC1Visualizer::PrintNode(Node* n) { |
523 | PrintNodeId(n); |
524 | os_ << " " << *n->op() << " "; |
525 | PrintInputs(n); |
526 | } |
527 | |
528 | |
529 | template <typename InputIterator> |
530 | void 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 | |
544 | void 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 | |
556 | void GraphC1Visualizer::PrintType(Node* node) { |
557 | if (NodeProperties::IsTyped(node)) { |
558 | Type type = NodeProperties::GetType(node); |
559 | os_ << " type:" << type; |
560 | } |
561 | } |
562 | |
563 | |
564 | void 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 | |
701 | void 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 | |
719 | void 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 | |
729 | void 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 | |
794 | std::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 | |
802 | std::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 | |
811 | std::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 | |
824 | const int kUnvisited = 0; |
825 | const int kOnStack = 1; |
826 | const int kVisited = 2; |
827 | |
828 | std::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 | |
882 | namespace { |
883 | |
884 | void PrintIndent(std::ostream& os, int indent) { |
885 | os << " "; |
886 | for (int i = 0; i < indent; i++) { |
887 | os << ". "; |
888 | } |
889 | } |
890 | |
891 | void 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 | |
907 | void PrintScheduledGraph(std::ostream& os, const Schedule* schedule) { |
908 | const BasicBlockVector* rpo = schedule->rpo_order(); |
909 | for (size_t i = 0; i < rpo->size(); i++) { |
910 | BasicBlock* current = (*rpo)[i]; |
911 | int indent = current->loop_depth(); |
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 | |
958 | std::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 | |
1014 | std::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 | |
1041 | void 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 | |
1059 | std::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 | |
1080 | std::ostream& operator<<(std::ostream& os, const AsScheduledGraph& scheduled) { |
1081 | PrintScheduledGraph(os, scheduled.schedule); |
1082 | return os; |
1083 | } |
1084 | |
1085 | std::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 | |
1213 | std::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 | |
1288 | std::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 | |
1351 | std::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 |