v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
maglev-graph-printer.cc
Go to the documentation of this file.
1// Copyright 2022 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#ifdef V8_ENABLE_MAGLEV_GRAPH_PRINTER
6
8
9#include <initializer_list>
10#include <iomanip>
11#include <ostream>
12#include <type_traits>
13#include <vector>
14
15#include "src/base/logging.h"
26#include "src/utils/utils.h"
27
28namespace v8 {
29namespace internal {
30namespace maglev {
31
32namespace {
33
34int IntWidth(int val) {
35 if (val == -1) return 2;
36 return std::ceil(std::log10(val + 1));
37}
38
39int MaxIdWidth(MaglevGraphLabeller* graph_labeller, NodeIdT max_node_id,
40 int padding_adjustement = 0) {
41 int max_width = IntWidth(graph_labeller->max_node_id());
42 if (max_node_id != kInvalidNodeId) {
43 max_width += IntWidth(max_node_id) + 1;
44 }
45 return max_width + 2 + padding_adjustement;
46}
47
48void PrintPaddedId(std::ostream& os, MaglevGraphLabeller* graph_labeller,
49 NodeIdT max_node_id, NodeBase* node,
50 std::string padding = " ", int padding_adjustement = 0) {
51 int id = graph_labeller->NodeId(node);
52 int id_width = IntWidth(id);
53 int other_id_width = node->has_id() ? 1 + IntWidth(node->id()) : 0;
54 int max_width = MaxIdWidth(graph_labeller, max_node_id, padding_adjustement);
55 int padding_width = std::max(0, max_width - id_width - other_id_width);
56
57 for (int i = 0; i < padding_width; ++i) {
58 os << padding;
59 }
60 if (v8_flags.log_colour) os << "\033[0m";
61 if (node->has_id()) {
62 os << node->id() << "/";
63 }
64 os << graph_labeller->NodeId(node) << ": ";
65}
66
67void PrintPadding(std::ostream& os, int size) {
68 os << std::setfill(' ') << std::setw(size) << "";
69}
70
71void PrintPadding(std::ostream& os, MaglevGraphLabeller* graph_labeller,
72 NodeIdT max_node_id, int padding_adjustement) {
73 PrintPadding(os,
74 MaxIdWidth(graph_labeller, max_node_id, padding_adjustement));
75}
76
77enum ConnectionLocation {
78 kTop = 1 << 0,
79 kLeft = 1 << 1,
80 kRight = 1 << 2,
81 kBottom = 1 << 3
82};
83
84struct Connection {
85 void Connect(ConnectionLocation loc) { connected |= loc; }
86
87 void AddHorizontal() {
88 Connect(kLeft);
89 Connect(kRight);
90 }
91
92 void AddVertical() {
93 Connect(kTop);
94 Connect(kBottom);
95 }
96
97 const char* ToString() const {
98 switch (connected) {
99 case 0:
100 return " ";
101 case kTop:
102 return "╵";
103 case kLeft:
104 return "╴";
105 case kRight:
106 return "╶";
107 case kBottom:
108 return "╷";
109 case kTop | kLeft:
110 return "╯";
111 case kTop | kRight:
112 return "╰";
113 case kBottom | kLeft:
114 return "╮";
115 case kBottom | kRight:
116 return "╭";
117 case kTop | kBottom:
118 return "│";
119 case kLeft | kRight:
120 return "─";
121 case kTop | kBottom | kLeft:
122 return "┤";
123 case kTop | kBottom | kRight:
124 return "├";
125 case kLeft | kRight | kTop:
126 return "┴";
127 case kLeft | kRight | kBottom:
128 return "┬";
129 case kTop | kLeft | kRight | kBottom:
130 return "┼";
131 }
132 UNREACHABLE();
133 }
134
135 uint8_t connected = 0;
136};
137
138std::ostream& operator<<(std::ostream& os, const Connection& c) {
139 return os << c.ToString();
140}
141
142// Print the vertical parts of connection arrows, optionally connecting arrows
143// that were only first created on this line (passed in "arrows_starting_here")
144// and should therefore connect rightwards instead of upwards.
145void PrintVerticalArrows(std::ostream& os,
146 const std::vector<BasicBlock*>& targets,
147 std::set<size_t> arrows_starting_here = {},
148 std::set<BasicBlock*> targets_starting_here = {},
149 bool is_loop = false) {
150 bool saw_start = false;
151 int line_color = -1;
152 int current_color = -1;
153 for (size_t i = 0; i < targets.size(); ++i) {
154 int desired_color = line_color;
155 Connection c;
156 if (saw_start) {
157 c.AddHorizontal();
158 }
159 if (arrows_starting_here.find(i) != arrows_starting_here.end() ||
160 targets_starting_here.find(targets[i]) != targets_starting_here.end()) {
161 desired_color = (i % 6) + 1;
162 line_color = desired_color;
163 c.Connect(kRight);
164 c.Connect(is_loop ? kTop : kBottom);
165 saw_start = true;
166 }
167
168 // Only add the vertical connection if there was no other connection.
169 if (c.connected == 0 && targets[i] != nullptr) {
170 desired_color = (i % 6) + 1;
171 c.AddVertical();
172 }
173 if (v8_flags.log_colour && desired_color != current_color &&
174 desired_color != -1) {
175 os << "\033[0;3" << desired_color << "m";
176 current_color = desired_color;
177 }
178 os << c;
179 }
180 // If there are no arrows starting here, clear the color. Otherwise,
181 // PrintPaddedId will clear it.
182 if (v8_flags.log_colour && arrows_starting_here.empty() &&
183 targets_starting_here.empty()) {
184 os << "\033[0m";
185 }
186}
187
188// Add a target to the target list in the first non-null position from the end.
189// This might have to extend the target list if there is no free spot.
190size_t AddTarget(std::vector<BasicBlock*>& targets, BasicBlock* target) {
191 if (targets.size() == 0 || targets.back() != nullptr) {
192 targets.push_back(target);
193 return targets.size() - 1;
194 }
195
196 size_t i = targets.size();
197 while (i > 0) {
198 if (targets[i - 1] != nullptr) break;
199 i--;
200 }
201 targets[i] = target;
202 return i;
203}
204
205// If the target is not a fallthrough, add i to the target list in the first
206// non-null position from the end. This might have to extend the target list if
207// there is no free spot. Returns true if it was added, false if it was a
208// fallthrough.
209bool AddTargetIfNotNext(std::vector<BasicBlock*>& targets, BasicBlock* target,
210 BasicBlock* next_block,
211 std::set<size_t>* arrows_starting_here = nullptr) {
212 if (next_block == target) return false;
213 size_t index = AddTarget(targets, target);
214 if (arrows_starting_here != nullptr) arrows_starting_here->insert(index);
215 return true;
216}
217
218class MaglevPrintingVisitorOstream : public std::ostream,
219 private std::streambuf {
220 public:
221 MaglevPrintingVisitorOstream(std::ostream& os,
222 std::vector<BasicBlock*>* targets)
223 : std::ostream(this), os_(os), targets_(targets), padding_size_(0) {}
224 ~MaglevPrintingVisitorOstream() override = default;
225
226 static MaglevPrintingVisitorOstream* cast(
227 const std::unique_ptr<std::ostream>& os) {
228 return static_cast<MaglevPrintingVisitorOstream*>(os.get());
229 }
230
231 void set_padding(int padding_size) { padding_size_ = padding_size; }
232
233 protected:
234 int overflow(int c) override;
235
236 private:
237 std::ostream& os_;
238 std::vector<BasicBlock*>* targets_;
239 int padding_size_;
240 bool previous_was_new_line_ = true;
241};
242
243int MaglevPrintingVisitorOstream::overflow(int c) {
244 if (c == EOF) return c;
245
246 if (previous_was_new_line_) {
247 PrintVerticalArrows(os_, *targets_);
248 PrintPadding(os_, padding_size_);
249 }
250 os_.rdbuf()->sputc(c);
251 previous_was_new_line_ = (c == '\n');
252 return c;
253}
254
255} // namespace
256
258 MaglevGraphLabeller* graph_labeller, std::ostream& os)
259 : graph_labeller_(graph_labeller),
260 os_(os),
261 os_for_additional_info_(
262 new MaglevPrintingVisitorOstream(os_, &targets_)) {}
263
264void MaglevPrintingVisitor::PreProcessGraph(Graph* graph) {
265 os_ << "Graph\n\n";
266
267 for (BasicBlock* block : *graph) {
268 if (block->control_node()->Is<JumpLoop>()) {
269 loop_headers_.insert(block->control_node()->Cast<JumpLoop>()->target());
270 }
271 if (max_node_id_ == kInvalidNodeId) {
272 if (block->control_node()->has_id()) {
273 max_node_id_ = block->control_node()->id();
274 }
275 } else {
276 max_node_id_ = std::max(max_node_id_, block->control_node()->id());
277 }
278 }
279
280 // Precalculate the maximum number of targets.
281 for (BlockConstIterator block_it = graph->begin(); block_it != graph->end();
282 ++block_it) {
283 BasicBlock* block = *block_it;
284 std::replace(targets_.begin(), targets_.end(), block,
285 static_cast<BasicBlock*>(nullptr));
286
287 if (loop_headers_.find(block) != loop_headers_.end()) {
288 AddTarget(targets_, block);
289 }
290 ControlNode* node = block->control_node();
291 if (node->Is<JumpLoop>()) {
292 BasicBlock* target = node->Cast<JumpLoop>()->target();
293 std::replace(targets_.begin(), targets_.end(), target,
294 static_cast<BasicBlock*>(nullptr));
295 } else if (node->Is<UnconditionalControlNode>()) {
296 AddTargetIfNotNext(targets_,
297 node->Cast<UnconditionalControlNode>()->target(),
298 *(block_it + 1));
299 } else if (node->Is<BranchControlNode>()) {
300 AddTargetIfNotNext(targets_, node->Cast<BranchControlNode>()->if_true(),
301 *(block_it + 1));
302 AddTargetIfNotNext(targets_, node->Cast<BranchControlNode>()->if_false(),
303 *(block_it + 1));
304 } else if (node->Is<Switch>()) {
305 for (int i = 0; i < node->Cast<Switch>()->size(); i++) {
306 const BasicBlockRef& target = node->Cast<Switch>()->targets()[i];
307 AddTargetIfNotNext(targets_, target.block_ptr(), *(block_it + 1));
308 }
309 if (node->Cast<Switch>()->has_fallthrough()) {
310 BasicBlock* fallthrough_target = node->Cast<Switch>()->fallthrough();
311 AddTargetIfNotNext(targets_, fallthrough_target, *(block_it + 1));
312 }
313 }
314 }
315 DCHECK(std::all_of(targets_.begin(), targets_.end(),
316 [](BasicBlock* block) { return block == nullptr; }));
317}
318
319BlockProcessResult MaglevPrintingVisitor::PreProcessBasicBlock(
320 BasicBlock* block) {
321 size_t loop_position = static_cast<size_t>(-1);
322 if (loop_headers_.erase(block) > 0) {
323 loop_position = AddTarget(targets_, block);
324 }
325 {
326 bool saw_start = false;
327 int current_color = -1;
328 int line_color = -1;
329 for (size_t i = 0; i < targets_.size(); ++i) {
330 int desired_color = line_color;
331 Connection c;
332 if (saw_start) {
333 c.AddHorizontal();
334 }
335 // If this is one of the arrows pointing to this block, terminate the
336 // line by connecting it rightwards.
337 if (targets_[i] == block) {
338 // Update the color of the line.
339 desired_color = (i % 6) + 1;
340 line_color = desired_color;
341 c.Connect(kRight);
342 // If this is the loop header, go down instead of up and don't clear
343 // the target.
344 if (i == loop_position) {
345 c.Connect(kBottom);
346 } else {
347 c.Connect(kTop);
348 targets_[i] = nullptr;
349 }
350 saw_start = true;
351 } else if (c.connected == 0 && targets_[i] != nullptr) {
352 // If this is another arrow, connect it, but only if that doesn't
353 // clobber any existing drawing. Set the current color, but don't update
354 // the overall color.
355 desired_color = (i % 6) + 1;
356 c.AddVertical();
357 }
358 if (v8_flags.log_colour && current_color != desired_color &&
359 desired_color != -1) {
360 os_ << "\033[0;3" << desired_color << "m";
361 current_color = desired_color;
362 }
363 os_ << c;
364 }
365 os_ << (saw_start ? "►" : " ");
366 if (v8_flags.log_colour) os_ << "\033[0m";
367 }
368
369 os_ << "Block b" << block->id();
370 if (block->has_state() && block->state()->is_resumable_loop()) {
371 os_ << " (resumable)";
372 }
373 if (block->is_exception_handler_block()) {
374 os_ << " (exception handler)";
375 }
376 if (block->is_loop() && block->has_state()) {
377 if (block->state()->is_loop_with_peeled_iteration()) {
378 os_ << " peeled";
379 }
380 if (const LoopEffects* loop_effects = block->state()->loop_effects()) {
381 os_ << " (effects:";
382 if (loop_effects->unstable_aspects_cleared) {
383 if (loop_effects->unstable_aspects_cleared) {
384 os_ << " ua";
385 }
386 if (loop_effects->context_slot_written.size()) {
387 os_ << " c" << loop_effects->context_slot_written.size();
388 }
389 if (loop_effects->objects_written.size()) {
390 os_ << " o" << loop_effects->objects_written.size();
391 }
392 if (loop_effects->keys_cleared.size()) {
393 os_ << " k" << loop_effects->keys_cleared.size();
394 }
395 }
396 os_ << ")";
397 }
398 }
399 os_ << "\n";
400
401 MaglevPrintingVisitorOstream::cast(os_for_additional_info_)->set_padding(1);
402 return BlockProcessResult::kContinue;
403}
404
405namespace {
406
407void PrintInputLocationAndAdvance(std::ostream& os, ValueNode* node,
408 InputLocation*& input_location) {
409 if (InlinedAllocation* allocation = node->TryCast<InlinedAllocation>()) {
410 if (allocation->HasBeenAnalysed() && allocation->HasBeenElided()) {
411 os << "(elided)";
412 return;
413 }
414 }
415 if (input_location) {
416 os << input_location->operand();
417 input_location++;
418 }
419}
420
421void PrintSingleDeoptFrame(
422 std::ostream& os, MaglevGraphLabeller* graph_labeller,
423 const DeoptFrame& frame, InputLocation*& current_input_location,
424 LazyDeoptInfo* lazy_deopt_info_if_top_frame = nullptr) {
425 switch (frame.type()) {
426 case DeoptFrame::FrameType::kInterpretedFrame: {
427 os << "@" << frame.as_interpreted().bytecode_position();
428 if (!v8_flags.print_maglev_deopt_verbose) {
429 int count = 0;
430 frame.as_interpreted().frame_state()->ForEachValue(
431 frame.as_interpreted().unit(),
432 [&](ValueNode* node, interpreter::Register reg) { count++; });
433 os << " (" << count << " live vars)";
434 return;
435 }
436 os << " : {";
437 os << "<closure>:"
438 << PrintNodeLabel(graph_labeller, frame.as_interpreted().closure())
439 << ":";
440 PrintInputLocationAndAdvance(os, frame.as_interpreted().closure(),
441 current_input_location);
442 frame.as_interpreted().frame_state()->ForEachValue(
443 frame.as_interpreted().unit(),
444 [&](ValueNode* node, interpreter::Register reg) {
445 os << ", " << reg.ToString() << ":";
446 if (lazy_deopt_info_if_top_frame &&
447 lazy_deopt_info_if_top_frame->IsResultRegister(reg)) {
448 os << "<result>";
449 } else {
450 os << PrintNodeLabel(graph_labeller, node) << ":";
451 PrintInputLocationAndAdvance(os, node, current_input_location);
452 }
453 });
454 os << "}";
455 break;
456 }
457 case DeoptFrame::FrameType::kConstructInvokeStubFrame: {
458 os << "@ConstructInvokeStub";
459 if (!v8_flags.print_maglev_deopt_verbose) return;
460 os << " : {";
461 os << "<this>:"
462 << PrintNodeLabel(graph_labeller, frame.as_construct_stub().receiver())
463 << ":";
464 PrintInputLocationAndAdvance(os, frame.as_construct_stub().receiver(),
465 current_input_location);
466 os << ", <context>:"
467 << PrintNodeLabel(graph_labeller, frame.as_construct_stub().context())
468 << ":";
469 PrintInputLocationAndAdvance(os, frame.as_construct_stub().context(),
470 current_input_location);
471 os << "}";
472 break;
473 }
474 case DeoptFrame::FrameType::kInlinedArgumentsFrame: {
475 os << "@" << frame.as_inlined_arguments().bytecode_position();
476 if (!v8_flags.print_maglev_deopt_verbose) return;
477 os << " : {";
478 auto arguments = frame.as_inlined_arguments().arguments();
479 DCHECK_GT(arguments.size(), 0);
480 os << "<this>:" << PrintNodeLabel(graph_labeller, arguments[0]) << ":";
481 PrintInputLocationAndAdvance(os, arguments[0], current_input_location);
482 if (arguments.size() > 1) {
483 os << ", ";
484 }
485 for (size_t i = 1; i < arguments.size(); i++) {
486 os << "a" << (i - 1) << ":"
487 << PrintNodeLabel(graph_labeller, arguments[i]) << ":";
488 PrintInputLocationAndAdvance(os, arguments[i], current_input_location);
489 os << ", ";
490 }
491 os << "}";
492 break;
493 }
494 case DeoptFrame::FrameType::kBuiltinContinuationFrame: {
495 os << "@" << Builtins::name(frame.as_builtin_continuation().builtin_id());
496 if (!v8_flags.print_maglev_deopt_verbose) return;
497 os << " : {";
498 int arg_index = 0;
499 for (ValueNode* node : frame.as_builtin_continuation().parameters()) {
500 os << "a" << arg_index << ":" << PrintNodeLabel(graph_labeller, node)
501 << ":";
502 PrintInputLocationAndAdvance(os, node, current_input_location);
503 arg_index++;
504 os << ", ";
505 }
506 os << "<context>:"
507 << PrintNodeLabel(graph_labeller,
508 frame.as_builtin_continuation().context())
509 << ":";
510 PrintInputLocationAndAdvance(os,
511 frame.as_builtin_continuation().context(),
512 current_input_location);
513 os << "}";
514 break;
515 }
516 }
517}
518
519void PrintVirtualObjects(std::ostream& os, std::vector<BasicBlock*> targets,
520 const DeoptFrame& frame,
521 MaglevGraphLabeller* graph_labeller, int max_node_id) {
522 if (!v8_flags.trace_deopt_verbose) return;
523 PrintVerticalArrows(os, targets);
524 PrintPadding(os, graph_labeller, max_node_id, 0);
525 os << " │ VOs : { ";
526 const VirtualObjectList& virtual_objects = frame.GetVirtualObjects();
527 for (auto vo : virtual_objects) {
528 os << PrintNodeLabel(graph_labeller, vo) << "; ";
529 }
530 os << "}\n";
531}
532
533void PrintDeoptInfoInputLocation(std::ostream& os,
534 std::vector<BasicBlock*> targets,
535 DeoptInfo* deopt_info,
536 MaglevGraphLabeller* graph_labeller,
537 int max_node_id) {
538#ifdef DEBUG
539 if (!v8_flags.print_maglev_deopt_verbose) return;
540 PrintVerticalArrows(os, targets);
541 PrintPadding(os, graph_labeller, max_node_id, 0);
542 if (deopt_info->has_input_locations()) {
543 os << " input locations: " << deopt_info->input_locations() << " ("
544 << deopt_info->input_location_count() << " slots)";
545 }
546 os << "\n";
547#endif // DEBUG
548}
549
550void RecursivePrintEagerDeopt(std::ostream& os,
551 std::vector<BasicBlock*> targets,
552 const DeoptFrame& frame,
553 MaglevGraphLabeller* graph_labeller,
554 int max_node_id,
555 InputLocation*& current_input_location) {
556 if (frame.parent()) {
557 RecursivePrintEagerDeopt(os, targets, *frame.parent(), graph_labeller,
558 max_node_id, current_input_location);
559 }
560
561 PrintVerticalArrows(os, targets);
562 PrintPadding(os, graph_labeller, max_node_id, 0);
563 if (!frame.parent()) {
564 os << " ↱ eager ";
565 } else {
566 os << " │ ";
567 }
568 PrintSingleDeoptFrame(os, graph_labeller, frame, current_input_location);
569 os << "\n";
570 PrintVirtualObjects(os, targets, frame, graph_labeller, max_node_id);
571}
572
573void PrintEagerDeopt(std::ostream& os, std::vector<BasicBlock*> targets,
574 NodeBase* node, MaglevGraphLabeller* graph_labeller,
575 int max_node_id) {
576 EagerDeoptInfo* deopt_info = node->eager_deopt_info();
577 InputLocation* current_input_location = nullptr;
578 if (deopt_info->has_input_locations()) {
579 current_input_location = deopt_info->input_locations();
580 }
581 PrintDeoptInfoInputLocation(os, targets, deopt_info, graph_labeller,
582 max_node_id);
583 RecursivePrintEagerDeopt(os, targets, deopt_info->top_frame(), graph_labeller,
584 max_node_id, current_input_location);
585}
586
587void MaybePrintEagerDeopt(std::ostream& os, std::vector<BasicBlock*> targets,
588 NodeBase* node, MaglevGraphLabeller* graph_labeller,
589 int max_node_id) {
590 if (node->properties().can_eager_deopt()) {
591 PrintEagerDeopt(os, targets, node, graph_labeller, max_node_id);
592 }
593}
594
595void RecursivePrintLazyDeopt(std::ostream& os, std::vector<BasicBlock*> targets,
596 const DeoptFrame& frame,
597 MaglevGraphLabeller* graph_labeller,
598 int max_node_id,
599 InputLocation*& current_input_location) {
600 if (frame.parent()) {
601 RecursivePrintLazyDeopt(os, targets, *frame.parent(), graph_labeller,
602 max_node_id, current_input_location);
603 }
604
605 PrintVerticalArrows(os, targets);
606 PrintPadding(os, graph_labeller, max_node_id, 0);
607 os << " │ ";
608 PrintSingleDeoptFrame(os, graph_labeller, frame, current_input_location);
609 os << "\n";
610 PrintVirtualObjects(os, targets, frame, graph_labeller, max_node_id);
611}
612
613template <typename NodeT>
614void PrintLazyDeopt(std::ostream& os, std::vector<BasicBlock*> targets,
615 NodeT* node, MaglevGraphLabeller* graph_labeller,
616 int max_node_id) {
617 LazyDeoptInfo* deopt_info = node->lazy_deopt_info();
618 InputLocation* current_input_location = nullptr;
619 if (deopt_info->has_input_locations()) {
620 current_input_location = deopt_info->input_locations();
621 }
622
623 PrintDeoptInfoInputLocation(os, targets, deopt_info, graph_labeller,
624 max_node_id);
625
626 const DeoptFrame& top_frame = deopt_info->top_frame();
627 if (top_frame.parent()) {
628 RecursivePrintLazyDeopt(os, targets, *top_frame.parent(), graph_labeller,
629 max_node_id, current_input_location);
630 }
631
632 PrintVerticalArrows(os, targets);
633 PrintPadding(os, graph_labeller, max_node_id, 0);
634
635 os << " ↳ lazy ";
636 PrintSingleDeoptFrame(os, graph_labeller, top_frame, current_input_location,
637 deopt_info);
638 os << "\n";
639 PrintVirtualObjects(os, targets, top_frame, graph_labeller, max_node_id);
640}
641
642template <typename NodeT>
643void PrintExceptionHandlerPoint(std::ostream& os,
644 std::vector<BasicBlock*> targets, NodeT* node,
645 MaglevGraphLabeller* graph_labeller,
646 int max_node_id) {
647 // If no handler info, then we cannot throw.
648 ExceptionHandlerInfo* info = node->exception_handler_info();
649 if (!info->HasExceptionHandler() || info->ShouldLazyDeopt()) return;
650
651 BasicBlock* block = info->catch_block();
652 DCHECK(block->is_exception_handler_block());
653
654 if (!block->has_phi()) {
655 return;
656 }
657 Phi* first_phi = block->phis()->first();
658 CHECK_NOT_NULL(first_phi);
659 int handler_offset = first_phi->merge_state()->merge_offset();
660
661 // The exception handler liveness should be a subset of lazy_deopt_info one.
662 auto* liveness = block->state()->frame_state().liveness();
663 LazyDeoptInfo* deopt_info = node->lazy_deopt_info();
664
665 const InterpretedDeoptFrame* lazy_frame;
666 switch (deopt_info->top_frame().type()) {
667 case DeoptFrame::FrameType::kInterpretedFrame:
668 lazy_frame = &deopt_info->top_frame().as_interpreted();
669 break;
670 case DeoptFrame::FrameType::kInlinedArgumentsFrame:
671 UNREACHABLE();
672 case DeoptFrame::FrameType::kConstructInvokeStubFrame:
673 case DeoptFrame::FrameType::kBuiltinContinuationFrame:
674 lazy_frame = &deopt_info->top_frame().parent()->as_interpreted();
675 break;
676 }
677
678 PrintVerticalArrows(os, targets);
679 PrintPadding(os, graph_labeller, max_node_id, 0);
680
681 os << " ↳ throw @" << handler_offset << " : {";
682 bool first = true;
683 lazy_frame->as_interpreted().frame_state()->ForEachValue(
684 lazy_frame->as_interpreted().unit(),
685 [&](ValueNode* node, interpreter::Register reg) {
686 if (!reg.is_parameter() && !liveness->RegisterIsLive(reg.index())) {
687 // Skip, since not live at the handler offset.
688 return;
689 }
690 if (first) {
691 first = false;
692 } else {
693 os << ", ";
694 }
695 os << reg.ToString() << ":" << PrintNodeLabel(graph_labeller, node);
696 });
697 os << "}\n";
698}
699
700void MaybePrintLazyDeoptOrExceptionHandler(std::ostream& os,
701 std::vector<BasicBlock*> targets,
702 NodeBase* node,
703 MaglevGraphLabeller* graph_labeller,
704 int max_node_id) {
705 switch (node->opcode()) {
706#define CASE(Name) \
707 case Opcode::k##Name: \
708 if constexpr (Name::kProperties.can_lazy_deopt()) { \
709 PrintLazyDeopt<Name>(os, targets, node->Cast<Name>(), graph_labeller, \
710 max_node_id); \
711 } \
712 if constexpr (Name::kProperties.can_throw()) { \
713 PrintExceptionHandlerPoint<Name>(os, targets, node->Cast<Name>(), \
714 graph_labeller, max_node_id); \
715 } \
716 break;
718#undef CASE
719 }
720}
721
722void MaybePrintProvenance(std::ostream& os, std::vector<BasicBlock*> targets,
723 MaglevGraphLabeller::Provenance provenance,
724 MaglevGraphLabeller::Provenance existing_provenance) {
726
727 // Print function every time the compilation unit changes.
728 bool needs_function_print = provenance.unit != existing_provenance.unit;
729 Tagged<Script> script;
730 Script::PositionInfo position_info;
731 bool has_position_info = false;
732
733 // Print position inside function every time either the position or the
734 // compilation unit changes.
735 if (provenance.position.IsKnown() &&
736 (provenance.position != existing_provenance.position ||
737 provenance.unit != existing_provenance.unit)) {
738 script = Cast<Script>(
739 provenance.unit->shared_function_info().object()->script());
740 has_position_info = script->GetPositionInfo(
741 provenance.position.ScriptOffset(), &position_info,
742 Script::OffsetFlag::kWithOffset);
743 needs_function_print = true;
744 }
745
746 // Do the actual function + position print.
747 if (needs_function_print) {
748 if (script.is_null()) {
749 script = Cast<Script>(
750 provenance.unit->shared_function_info().object()->script());
751 }
752 PrintVerticalArrows(os, targets);
753 if (v8_flags.log_colour) {
754 os << "\033[1;34m";
755 }
756 os << *provenance.unit->shared_function_info().object() << " ("
757 << script->GetNameOrSourceURL();
758 if (has_position_info) {
759 os << ":" << position_info.line << ":" << position_info.column;
760 } else if (provenance.position.IsKnown()) {
761 os << "@" << provenance.position.ScriptOffset();
762 }
763 os << ")\n";
764 if (v8_flags.log_colour) {
765 os << "\033[m";
766 }
767 }
768
769 // Print current bytecode every time the offset or current compilation unit
770 // (i.e. bytecode array) changes.
771 if (!provenance.bytecode_offset.IsNone() &&
772 (provenance.bytecode_offset != existing_provenance.bytecode_offset ||
773 provenance.unit != existing_provenance.unit)) {
774 PrintVerticalArrows(os, targets);
775
776 interpreter::BytecodeArrayIterator iterator(
777 provenance.unit->bytecode().object(),
778 provenance.bytecode_offset.ToInt(), no_gc);
779 if (v8_flags.log_colour) {
780 os << "\033[0;34m";
781 }
782 os << std::setw(4) << iterator.current_offset() << " : ";
783 interpreter::BytecodeDecoder::Decode(os, iterator.current_address(), false);
784 os << "\n";
785 if (v8_flags.log_colour) {
786 os << "\033[m";
787 }
788 }
789}
790
791} // namespace
792
793ProcessResult MaglevPrintingVisitor::Process(Phi* phi,
794 const ProcessingState& state) {
795 PrintVerticalArrows(os_, targets_);
796 PrintPaddedId(os_, graph_labeller_, max_node_id_, phi);
797 os_ << "φ";
798 switch (phi->value_representation()) {
799 case ValueRepresentation::kTagged:
800 os_ << "ᵀ";
801 break;
802 case ValueRepresentation::kInt32:
803 os_ << "ᴵ";
804 break;
805 case ValueRepresentation::kUint32:
806 os_ << "ᵁ";
807 break;
808 case ValueRepresentation::kFloat64:
809 os_ << "ᶠ";
810 break;
811 case ValueRepresentation::kHoleyFloat64:
812 os_ << "ʰᶠ";
813 break;
814 case ValueRepresentation::kIntPtr:
815 UNREACHABLE();
816 }
817 if (phi->uses_require_31_bit_value()) {
818 os_ << "ⁱ";
819 }
820 if (phi->input_count() == 0) {
821 os_ << "ₑ " << (phi->owner().is_valid() ? phi->owner().ToString() : "VO");
822 } else {
823 os_ << " " << (phi->owner().is_valid() ? phi->owner().ToString() : "VO")
824 << " (";
825 // Manually walk Phi inputs to print just the node labels, without
826 // input locations (which are shown in the predecessor block's gap
827 // moves).
828 for (int i = 0; i < phi->input_count(); ++i) {
829 if (i > 0) os_ << ", ";
830 os_ << PrintNodeLabel(graph_labeller_, phi->input(i).node());
831 }
832 os_ << ")";
833 }
834 if (phi->is_tagged() && !phi->result().operand().IsUnallocated()) {
835 if (phi->decompresses_tagged_result()) {
836 os_ << " (decompressed)";
837 } else {
838 os_ << " (compressed)";
839 }
840 }
841 os_ << " → " << phi->result().operand();
842 if (phi->result().operand().IsAllocated() && phi->is_spilled() &&
843 phi->spill_slot() != phi->result().operand()) {
844 os_ << " (spilled: " << phi->spill_slot() << ")";
845 }
846 if (phi->has_valid_live_range()) {
847 os_ << ", live range: [" << phi->live_range().start << "-"
848 << phi->live_range().end << "]";
849 }
850 if (!phi->has_id()) {
851 os_ << ", " << phi->use_count() << " uses";
852 }
853 os_ << "\n";
854
855 MaglevPrintingVisitorOstream::cast(os_for_additional_info_)
856 ->set_padding(MaxIdWidth(graph_labeller_, max_node_id_, 2));
857 return ProcessResult::kContinue;
858}
859
860ProcessResult MaglevPrintingVisitor::Process(Node* node,
861 const ProcessingState& state) {
862 MaglevGraphLabeller::Provenance provenance =
863 graph_labeller_->GetNodeProvenance(node);
864 if (provenance.unit != nullptr) {
865 MaybePrintProvenance(os_, targets_, provenance, existing_provenance_);
866 existing_provenance_ = provenance;
867 }
868
869 MaybePrintEagerDeopt(os_, targets_, node, graph_labeller_, max_node_id_);
870
871 PrintVerticalArrows(os_, targets_);
872 PrintPaddedId(os_, graph_labeller_, max_node_id_, node);
873 if (node->properties().is_call()) {
874 os_ << "🐢 ";
875 }
876 os_ << PrintNode(graph_labeller_, node) << "\n";
877
878 MaglevPrintingVisitorOstream::cast(os_for_additional_info_)
879 ->set_padding(MaxIdWidth(graph_labeller_, max_node_id_, 2));
880
881 MaybePrintLazyDeoptOrExceptionHandler(os_, targets_, node, graph_labeller_,
882 max_node_id_);
883 return ProcessResult::kContinue;
884}
885
886ProcessResult MaglevPrintingVisitor::Process(ControlNode* control_node,
887 const ProcessingState& state) {
888 MaglevGraphLabeller::Provenance provenance =
889 graph_labeller_->GetNodeProvenance(control_node);
890 if (provenance.unit != nullptr) {
891 MaybePrintProvenance(os_, targets_, provenance, existing_provenance_);
892 existing_provenance_ = provenance;
893 }
894
895 MaybePrintEagerDeopt(os_, targets_, control_node, graph_labeller_,
896 max_node_id_);
897
898 bool has_fallthrough = false;
899
900 if (control_node->Is<JumpLoop>()) {
901 BasicBlock* target = control_node->Cast<JumpLoop>()->target();
902
903 PrintVerticalArrows(os_, targets_, {}, {target}, true);
904 os_ << "◄─";
905 PrintPaddedId(os_, graph_labeller_, max_node_id_, control_node, "─", -2);
906 std::replace(targets_.begin(), targets_.end(), target,
907 static_cast<BasicBlock*>(nullptr));
908
909 } else if (control_node->Is<UnconditionalControlNode>()) {
910 BasicBlock* target =
911 control_node->Cast<UnconditionalControlNode>()->target();
912
913 std::set<size_t> arrows_starting_here;
914 has_fallthrough |= !AddTargetIfNotNext(targets_, target, state.next_block(),
915 &arrows_starting_here);
916 PrintVerticalArrows(os_, targets_, arrows_starting_here);
917 PrintPaddedId(os_, graph_labeller_, max_node_id_, control_node,
918 has_fallthrough ? " " : "─");
919
920 } else if (control_node->Is<BranchControlNode>()) {
921 BasicBlock* true_target =
922 control_node->Cast<BranchControlNode>()->if_true();
923 BasicBlock* false_target =
924 control_node->Cast<BranchControlNode>()->if_false();
925
926 std::set<size_t> arrows_starting_here;
927 has_fallthrough |= !AddTargetIfNotNext(
928 targets_, false_target, state.next_block(), &arrows_starting_here);
929 has_fallthrough |= !AddTargetIfNotNext(
930 targets_, true_target, state.next_block(), &arrows_starting_here);
931 PrintVerticalArrows(os_, targets_, arrows_starting_here);
932 PrintPaddedId(os_, graph_labeller_, max_node_id_, control_node, "─");
933 } else if (control_node->Is<Switch>()) {
934 std::set<size_t> arrows_starting_here;
935 for (int i = 0; i < control_node->Cast<Switch>()->size(); i++) {
936 const BasicBlockRef& target = control_node->Cast<Switch>()->targets()[i];
937 has_fallthrough |=
938 !AddTargetIfNotNext(targets_, target.block_ptr(), state.next_block(),
939 &arrows_starting_here);
940 }
941
942 if (control_node->Cast<Switch>()->has_fallthrough()) {
943 BasicBlock* fallthrough_target =
944 control_node->Cast<Switch>()->fallthrough();
945 has_fallthrough |=
946 !AddTargetIfNotNext(targets_, fallthrough_target, state.next_block(),
947 &arrows_starting_here);
948 }
949
950 PrintVerticalArrows(os_, targets_, arrows_starting_here);
951 PrintPaddedId(os_, graph_labeller_, max_node_id_, control_node, "─");
952
953 } else {
954 PrintVerticalArrows(os_, targets_);
955 PrintPaddedId(os_, graph_labeller_, max_node_id_, control_node);
956 }
957
958 os_ << PrintNode(graph_labeller_, control_node) << "\n";
959
960 bool printed_phis = false;
961 if (control_node->Is<UnconditionalControlNode>()) {
962 BasicBlock* target =
963 control_node->Cast<UnconditionalControlNode>()->target();
964 if (target->has_phi()) {
965 printed_phis = true;
966 PrintVerticalArrows(os_, targets_);
967 PrintPadding(os_, graph_labeller_, max_node_id_, -1);
968 os_ << (has_fallthrough ? "│" : " ");
969 os_ << " with gap moves:\n";
970 int pid = state.block()->predecessor_id();
971 for (Phi* phi : *target->phis()) {
972 PrintVerticalArrows(os_, targets_);
973 PrintPadding(os_, graph_labeller_, max_node_id_, -1);
974 os_ << (has_fallthrough ? "│" : " ");
975 os_ << " - ";
976 graph_labeller_->PrintInput(os_, phi->input(pid));
977 os_ << " → " << graph_labeller_->NodeId(phi) << ": φ";
978 switch (phi->value_representation()) {
979 case ValueRepresentation::kTagged:
980 os_ << "ᵀ";
981 break;
982 case ValueRepresentation::kInt32:
983 os_ << "ᴵ";
984 break;
985 case ValueRepresentation::kUint32:
986 os_ << "ᵁ";
987 break;
988 case ValueRepresentation::kFloat64:
989 os_ << "ᶠ";
990 break;
991 case ValueRepresentation::kHoleyFloat64:
992 os_ << "ʰᶠ";
993 break;
994 case ValueRepresentation::kIntPtr:
995 UNREACHABLE();
996 }
997 if (phi->uses_require_31_bit_value()) {
998 os_ << "ⁱ";
999 }
1000 os_ << " " << (phi->owner().is_valid() ? phi->owner().ToString() : "VO")
1001 << " " << phi->result().operand() << "\n";
1002 }
1003#ifdef V8_ENABLE_MAGLEV
1004 if (target->state()->register_state().is_initialized()) {
1005 PrintVerticalArrows(os_, targets_);
1006 PrintPadding(os_, graph_labeller_, max_node_id_, -1);
1007 os_ << (has_fallthrough ? "│" : " ");
1008 os_ << " with register merges:\n";
1009 auto print_register_merges = [&](auto reg, RegisterState& state) {
1010 ValueNode* node;
1011 RegisterMerge* merge;
1012 if (LoadMergeState(state, &node, &merge)) {
1013 compiler::InstructionOperand source = merge->operand(pid);
1014 PrintVerticalArrows(os_, targets_);
1015 PrintPadding(os_, graph_labeller_, max_node_id_, -1);
1016 os_ << (has_fallthrough ? "│" : " ");
1017 os_ << " - " << source << " → " << reg << "\n";
1018 }
1019 };
1020 target->state()->register_state().ForEachGeneralRegister(
1021 print_register_merges);
1022 target->state()->register_state().ForEachDoubleRegister(
1023 print_register_merges);
1024 }
1025#endif
1026 }
1027 }
1028
1029 PrintVerticalArrows(os_, targets_);
1030 if (has_fallthrough) {
1031 PrintPadding(os_, graph_labeller_, max_node_id_, -1);
1032 if (printed_phis) {
1033 os_ << "▼";
1034 } else {
1035 os_ << "↓";
1036 }
1037 }
1038 os_ << "\n";
1039
1040 // TODO(leszeks): Allow MaglevPrintingVisitorOstream to print the arrowhead
1041 // so that it overlaps the fallthrough arrow.
1042 MaglevPrintingVisitorOstream::cast(os_for_additional_info_)
1043 ->set_padding(MaxIdWidth(graph_labeller_, max_node_id_, 2));
1044
1045 return ProcessResult::kContinue;
1046}
1047
1048void PrintGraph(std::ostream& os, MaglevCompilationInfo* compilation_info,
1049 Graph* const graph) {
1050 GraphProcessor<MaglevPrintingVisitor, /*visit_identity_nodes*/ true> printer(
1051 compilation_info->graph_labeller(), os);
1052 printer.ProcessGraph(graph);
1053}
1054
1055void PrintNode::Print(std::ostream& os) const {
1056 node_->Print(os, graph_labeller_, skip_targets_);
1057}
1058
1059void PrintNodeLabel::Print(std::ostream& os) const {
1060 graph_labeller_->PrintNodeLabel(os, node_);
1061}
1062
1063// For GDB: Print any basic block with `print bb->Print()`.
1064void BasicBlock::Print() const {
1065 std::cout << "Block";
1066 if (is_loop()) {
1067 if (state()->is_loop_with_peeled_iteration()) {
1068 std::cout << " (peeled loop)";
1069 } else if (has_state() && state()->is_resumable_loop()) {
1070 std::cout << " (resumable loop)";
1071 } else {
1072 std::cout << " (loop header)";
1073 }
1074 } else if (is_exception_handler_block()) {
1075 std::cout << " (exception handler)";
1076 }
1077 std::cout << "\n";
1078 for (auto node : nodes_) {
1079 node->Print();
1080 }
1081 if (control_node_) {
1082 control_node_->Print();
1083 } else {
1084 std::cout << " (missing control node)\n";
1085 }
1086}
1087
1088} // namespace maglev
1089} // namespace internal
1090} // namespace v8
1091
1092#endif // V8_ENABLE_MAGLEV_GRAPH_PRINTER
#define CASE(Name,...)
MaglevPrintingVisitor(MaglevGraphLabeller *graph_labeller, std::ostream &os)
constexpr const char * ToString(DataViewOp op)
ZoneLinkedList< BFEntry > nodes_
TNode< Object > target
Node * node
BalanceOverflow overflow
LiftoffRegister reg
LiftoffAssembler::CacheState state
#define NODE_BASE_LIST(V)
Definition maglev-ir.h:426
STL namespace.
static constexpr NodeIdT kInvalidNodeId
Definition maglev-ir.h:899
void PrintGraph(std::ostream &os, MaglevCompilationInfo *compilation_info, Graph *const graph)
std::ostream & operator<<(std::ostream &os, const PrintNode &printer)
base::PointerWithPayload< void, RegisterStateFlags, 2 > RegisterState
bool LoadMergeState(RegisterState state, RegisterMerge **merge)
PerThreadAssertScopeDebugOnly< false, SAFEPOINTS_ASSERT, HEAP_ALLOCATION_ASSERT > DisallowGarbageCollection
V8_EXPORT_PRIVATE FlagValues v8_flags
uint32_t cast
Node * node_
#define UNREACHABLE()
Definition logging.h:67
#define CHECK_NOT_NULL(val)
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_GT(v1, v2)
Definition logging.h:487
std::ostream * os_