• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "src/maglev/maglev-graph-printer.h"
6 
7 #include <initializer_list>
8 #include <iomanip>
9 #include <ostream>
10 #include <type_traits>
11 #include <vector>
12 
13 #include "src/maglev/maglev-basic-block.h"
14 #include "src/maglev/maglev-graph-labeller.h"
15 #include "src/maglev/maglev-graph-processor.h"
16 #include "src/maglev/maglev-graph.h"
17 #include "src/maglev/maglev-ir.h"
18 
19 namespace v8 {
20 namespace internal {
21 namespace maglev {
22 
23 namespace {
24 
PrintPaddedId(std::ostream & os,MaglevGraphLabeller * graph_labeller,NodeBase * node,std::string padding=" ",int padding_adjustement=0)25 void PrintPaddedId(std::ostream& os, MaglevGraphLabeller* graph_labeller,
26                    NodeBase* node, std::string padding = " ",
27                    int padding_adjustement = 0) {
28   int id = graph_labeller->NodeId(node);
29   int id_width = std::ceil(std::log10(id + 1));
30   int max_width = graph_labeller->max_node_id_width() + 2 + padding_adjustement;
31   int padding_width = std::max(0, max_width - id_width);
32 
33   for (int i = 0; i < padding_width; ++i) {
34     os << padding;
35   }
36   os << graph_labeller->NodeId(node) << ": ";
37 }
38 
PrintPadding(std::ostream & os,int size)39 void PrintPadding(std::ostream& os, int size) {
40   os << std::setfill(' ') << std::setw(size) << "";
41 }
42 
PrintPadding(std::ostream & os,MaglevGraphLabeller * graph_labeller,int padding_adjustement=0)43 void PrintPadding(std::ostream& os, MaglevGraphLabeller* graph_labeller,
44                   int padding_adjustement = 0) {
45   PrintPadding(os,
46                graph_labeller->max_node_id_width() + 2 + padding_adjustement);
47 }
48 
49 enum ConnectionLocation {
50   kTop = 1 << 0,
51   kLeft = 1 << 1,
52   kRight = 1 << 2,
53   kBottom = 1 << 3
54 };
55 
56 struct Connection {
Connectv8::internal::maglev::__anonfd84d97e0111::Connection57   void Connect(ConnectionLocation loc) { connected |= loc; }
58 
AddHorizontalv8::internal::maglev::__anonfd84d97e0111::Connection59   void AddHorizontal() {
60     Connect(kLeft);
61     Connect(kRight);
62   }
63 
AddVerticalv8::internal::maglev::__anonfd84d97e0111::Connection64   void AddVertical() {
65     Connect(kTop);
66     Connect(kBottom);
67   }
68 
ToStringv8::internal::maglev::__anonfd84d97e0111::Connection69   const char* ToString() const {
70     switch (connected) {
71       case 0:
72         return " ";
73       case kTop:
74         return "╵";
75       case kLeft:
76         return "╴";
77       case kRight:
78         return "╶";
79       case kBottom:
80         return "╷";
81       case kTop | kLeft:
82         return "╯";
83       case kTop | kRight:
84         return "╰";
85       case kBottom | kLeft:
86         return "╮";
87       case kBottom | kRight:
88         return "╭";
89       case kTop | kBottom:
90         return "│";
91       case kLeft | kRight:
92         return "─";
93       case kTop | kBottom | kLeft:
94         return "┤";
95       case kTop | kBottom | kRight:
96         return "├";
97       case kLeft | kRight | kTop:
98         return "┴";
99       case kLeft | kRight | kBottom:
100         return "┬";
101       case kTop | kLeft | kRight | kBottom:
102         return "┼";
103     }
104     UNREACHABLE();
105   }
106 
107   uint8_t connected = 0;
108 };
109 
operator <<(std::ostream & os,const Connection & c)110 std::ostream& operator<<(std::ostream& os, const Connection& c) {
111   return os << c.ToString();
112 }
113 
114 // Print the vertical parts of connection arrows, optionally connecting arrows
115 // that were only first created on this line (passed in "arrows_starting_here")
116 // and should therefore connect rightwards instead of upwards.
PrintVerticalArrows(std::ostream & os,const std::vector<BasicBlock * > & targets,const std::set<size_t> & arrows_starting_here={},const std::set<BasicBlock * > & targets_starting_here={},bool is_loop=false)117 void PrintVerticalArrows(
118     std::ostream& os, const std::vector<BasicBlock*>& targets,
119     const std::set<size_t>& arrows_starting_here = {},
120     const std::set<BasicBlock*>& targets_starting_here = {},
121     bool is_loop = false) {
122   bool saw_start = false;
123   for (size_t i = 0; i < targets.size(); ++i) {
124     Connection c;
125     if (saw_start) {
126       c.AddHorizontal();
127     }
128     if (arrows_starting_here.find(i) != arrows_starting_here.end() ||
129         targets_starting_here.find(targets[i]) != targets_starting_here.end()) {
130       c.Connect(kRight);
131       c.Connect(is_loop ? kTop : kBottom);
132       saw_start = true;
133     }
134 
135     // Only add the vertical connection if there was no other connection.
136     if (c.connected == 0 && targets[i] != nullptr) {
137       c.AddVertical();
138     }
139     os << c;
140   }
141 }
142 
143 // Add a target to the target list in the first non-null position from the end.
144 // This might have to extend the target list if there is no free spot.
AddTarget(std::vector<BasicBlock * > & targets,BasicBlock * target)145 size_t AddTarget(std::vector<BasicBlock*>& targets, BasicBlock* target) {
146   if (targets.size() == 0 || targets.back() != nullptr) {
147     targets.push_back(target);
148     return targets.size() - 1;
149   }
150 
151   size_t i = targets.size();
152   while (i > 0) {
153     if (targets[i - 1] != nullptr) break;
154     i--;
155   }
156   targets[i] = target;
157   return i;
158 }
159 
160 // If the target is not a fallthrough, add i to the target list in the first
161 // non-null position from the end. This might have to extend the target list if
162 // there is no free spot. Returns true if it was added, false if it was a
163 // fallthrough.
AddTargetIfNotNext(std::vector<BasicBlock * > & targets,BasicBlock * target,BasicBlock * next_block,std::set<size_t> * arrows_starting_here=nullptr)164 bool AddTargetIfNotNext(std::vector<BasicBlock*>& targets, BasicBlock* target,
165                         BasicBlock* next_block,
166                         std::set<size_t>* arrows_starting_here = nullptr) {
167   if (next_block == target) return false;
168   size_t index = AddTarget(targets, target);
169   if (arrows_starting_here != nullptr) arrows_starting_here->insert(index);
170   return true;
171 }
172 
173 class MaglevPrintingVisitorOstream : public std::ostream,
174                                      private std::streambuf {
175  public:
MaglevPrintingVisitorOstream(std::ostream & os,std::vector<BasicBlock * > * targets)176   MaglevPrintingVisitorOstream(std::ostream& os,
177                                std::vector<BasicBlock*>* targets)
178       : std::ostream(this), os_(os), targets_(targets), padding_size_(0) {}
179   ~MaglevPrintingVisitorOstream() override = default;
180 
cast(const std::unique_ptr<std::ostream> & os)181   static MaglevPrintingVisitorOstream* cast(
182       const std::unique_ptr<std::ostream>& os) {
183     return static_cast<MaglevPrintingVisitorOstream*>(os.get());
184   }
185 
set_padding(int padding_size)186   void set_padding(int padding_size) { padding_size_ = padding_size; }
187 
188  protected:
189   int overflow(int c) override;
190 
191  private:
192   std::ostream& os_;
193   std::vector<BasicBlock*>* targets_;
194   int padding_size_;
195   bool previous_was_new_line_ = true;
196 };
197 
overflow(int c)198 int MaglevPrintingVisitorOstream::overflow(int c) {
199   if (c == EOF) return c;
200 
201   if (previous_was_new_line_) {
202     PrintVerticalArrows(os_, *targets_);
203     PrintPadding(os_, padding_size_);
204   }
205   os_.rdbuf()->sputc(c);
206   previous_was_new_line_ = (c == '\n');
207   return c;
208 }
209 
210 }  // namespace
211 
MaglevPrintingVisitor(std::ostream & os)212 MaglevPrintingVisitor::MaglevPrintingVisitor(std::ostream& os)
213     : os_(os),
214       os_for_additional_info_(
215           new MaglevPrintingVisitorOstream(os_, &targets_)) {}
216 
PreProcessGraph(MaglevCompilationUnit * compilation_unit,Graph * graph)217 void MaglevPrintingVisitor::PreProcessGraph(
218     MaglevCompilationUnit* compilation_unit, Graph* graph) {
219   os_ << "Graph (param count: " << compilation_unit->parameter_count()
220       << ", frame size: " << compilation_unit->register_count() << ")\n\n";
221 
222   for (BasicBlock* block : *graph) {
223     if (block->control_node()->Is<JumpLoop>()) {
224       loop_headers_.insert(block->control_node()->Cast<JumpLoop>()->target());
225     }
226   }
227 
228   // Precalculate the maximum number of targets.
229   for (BlockConstIterator block_it = graph->begin(); block_it != graph->end();
230        ++block_it) {
231     BasicBlock* block = *block_it;
232     std::replace(targets_.begin(), targets_.end(), block,
233                  static_cast<BasicBlock*>(nullptr));
234 
235     if (loop_headers_.find(block) != loop_headers_.end()) {
236       AddTarget(targets_, block);
237     }
238     ControlNode* node = block->control_node();
239     if (node->Is<JumpLoop>()) {
240       BasicBlock* target = node->Cast<JumpLoop>()->target();
241       std::replace(targets_.begin(), targets_.end(), target,
242                    static_cast<BasicBlock*>(nullptr));
243     } else if (node->Is<UnconditionalControlNode>()) {
244       AddTargetIfNotNext(targets_,
245                          node->Cast<UnconditionalControlNode>()->target(),
246                          *(block_it + 1));
247     } else if (node->Is<ConditionalControlNode>()) {
248       AddTargetIfNotNext(targets_,
249                          node->Cast<ConditionalControlNode>()->if_true(),
250                          *(block_it + 1));
251       AddTargetIfNotNext(targets_,
252                          node->Cast<ConditionalControlNode>()->if_false(),
253                          *(block_it + 1));
254     }
255   }
256   DCHECK(std::all_of(targets_.begin(), targets_.end(),
257                      [](BasicBlock* block) { return block == nullptr; }));
258 }
259 
PreProcessBasicBlock(MaglevCompilationUnit * compilation_unit,BasicBlock * block)260 void MaglevPrintingVisitor::PreProcessBasicBlock(
261     MaglevCompilationUnit* compilation_unit, BasicBlock* block) {
262   MaglevGraphLabeller* graph_labeller = compilation_unit->graph_labeller();
263 
264   size_t loop_position = static_cast<size_t>(-1);
265   if (loop_headers_.erase(block) > 0) {
266     loop_position = AddTarget(targets_, block);
267   }
268   {
269     bool saw_start = false;
270     for (size_t i = 0; i < targets_.size(); ++i) {
271       Connection c;
272       if (saw_start) {
273         c.AddHorizontal();
274       }
275       // If this is one of the arrows pointing to this block, terminate the
276       // line by connecting it rightwards.
277       if (targets_[i] == block) {
278         c.Connect(kRight);
279         // If this is the loop header, go down instead of up and don't clear
280         // the target.
281         if (i == loop_position) {
282           c.Connect(kBottom);
283         } else {
284           c.Connect(kTop);
285           targets_[i] = nullptr;
286         }
287         saw_start = true;
288       } else if (c.connected == 0 && targets_[i] != nullptr) {
289         // If this is another arrow, connect it, but only if that doesn't
290         // clobber any existing drawing.
291         c.AddVertical();
292       }
293       os_ << c;
294     }
295     os_ << (saw_start ? "►" : " ");
296   }
297 
298   int block_id = graph_labeller->BlockId(block);
299   os_ << "Block b" << block_id << "\n";
300 
301   MaglevPrintingVisitorOstream::cast(os_for_additional_info_)->set_padding(1);
302 }
303 
304 namespace {
305 
306 template <typename NodeT>
PrintEagerDeopt(std::ostream & os,std::vector<BasicBlock * > targets,NodeT * node,const ProcessingState & state)307 void PrintEagerDeopt(std::ostream& os, std::vector<BasicBlock*> targets,
308                      NodeT* node, const ProcessingState& state) {
309   MaglevGraphLabeller* graph_labeller = state.graph_labeller();
310 
311   PrintVerticalArrows(os, targets);
312   PrintPadding(os, graph_labeller, 0);
313 
314   EagerDeoptInfo* deopt_info = node->eager_deopt_info();
315   os << "  ↱ eager @" << deopt_info->state.bytecode_position << " : {";
316   bool first = true;
317   int index = 0;
318   deopt_info->state.register_frame->ForEachValue(
319       *state.compilation_unit(),
320       [&](ValueNode* node, interpreter::Register reg) {
321         if (first) {
322           first = false;
323         } else {
324           os << ", ";
325         }
326         os << reg.ToString() << ":" << PrintNodeLabel(graph_labeller, node)
327            << ":" << deopt_info->input_locations[index].operand();
328         index++;
329       });
330   os << "}\n";
331 }
MaybePrintEagerDeopt(std::ostream & os,std::vector<BasicBlock * > targets,NodeBase * node,const ProcessingState & state)332 void MaybePrintEagerDeopt(std::ostream& os, std::vector<BasicBlock*> targets,
333                           NodeBase* node, const ProcessingState& state) {
334   switch (node->opcode()) {
335 #define CASE(Name)                                                   \
336   case Opcode::k##Name:                                              \
337     if constexpr (Name::kProperties.can_eager_deopt()) {             \
338       PrintEagerDeopt<Name>(os, targets, node->Cast<Name>(), state); \
339     }                                                                \
340     break;
341     NODE_BASE_LIST(CASE)
342 #undef CASE
343   }
344 }
345 
346 template <typename NodeT>
PrintLazyDeopt(std::ostream & os,std::vector<BasicBlock * > targets,NodeT * node,const ProcessingState & state)347 void PrintLazyDeopt(std::ostream& os, std::vector<BasicBlock*> targets,
348                     NodeT* node, const ProcessingState& state) {
349   MaglevGraphLabeller* graph_labeller = state.graph_labeller();
350 
351   PrintVerticalArrows(os, targets);
352   PrintPadding(os, graph_labeller, 0);
353 
354   LazyDeoptInfo* deopt_info = node->lazy_deopt_info();
355   os << "  ↳ lazy @" << deopt_info->state.bytecode_position << " : {";
356   bool first = true;
357   int index = 0;
358   deopt_info->state.register_frame->ForEachValue(
359       *state.compilation_unit(),
360       [&](ValueNode* node, interpreter::Register reg) {
361         if (first) {
362           first = false;
363         } else {
364           os << ", ";
365         }
366         os << reg.ToString() << ":";
367         if (reg == deopt_info->result_location) {
368           os << "<result>";
369         } else {
370           os << PrintNodeLabel(graph_labeller, node) << ":"
371              << deopt_info->input_locations[index].operand();
372         }
373         index++;
374       });
375   os << "}\n";
376 }
MaybePrintLazyDeopt(std::ostream & os,std::vector<BasicBlock * > targets,NodeBase * node,const ProcessingState & state)377 void MaybePrintLazyDeopt(std::ostream& os, std::vector<BasicBlock*> targets,
378                          NodeBase* node, const ProcessingState& state) {
379   switch (node->opcode()) {
380 #define CASE(Name)                                                  \
381   case Opcode::k##Name:                                             \
382     if constexpr (Name::kProperties.can_lazy_deopt()) {             \
383       PrintLazyDeopt<Name>(os, targets, node->Cast<Name>(), state); \
384     }                                                               \
385     break;
386     NODE_BASE_LIST(CASE)
387 #undef CASE
388   }
389 }
390 
391 }  // namespace
392 
Process(Phi * phi,const ProcessingState & state)393 void MaglevPrintingVisitor::Process(Phi* phi, const ProcessingState& state) {
394   MaglevGraphLabeller* graph_labeller = state.graph_labeller();
395 
396   PrintVerticalArrows(os_, targets_);
397   PrintPaddedId(os_, graph_labeller, phi);
398   os_ << "Phi (";
399   // Manually walk Phi inputs to print just the node labels, without
400   // input locations (which are shown in the predecessor block's gap
401   // moves).
402   for (int i = 0; i < phi->input_count(); ++i) {
403     if (i > 0) os_ << ", ";
404     if (state.block()->predecessor_at(i) == nullptr) {
405       os_ << "<dead>";
406     } else {
407       os_ << PrintNodeLabel(graph_labeller, phi->input(i).node());
408     }
409   }
410   os_ << ") → " << phi->result().operand() << "\n";
411 
412   MaglevPrintingVisitorOstream::cast(os_for_additional_info_)
413       ->set_padding(graph_labeller->max_node_id_width() + 4);
414 }
415 
Process(Node * node,const ProcessingState & state)416 void MaglevPrintingVisitor::Process(Node* node, const ProcessingState& state) {
417   MaglevGraphLabeller* graph_labeller = state.graph_labeller();
418 
419   MaybePrintEagerDeopt(os_, targets_, node, state);
420 
421   PrintVerticalArrows(os_, targets_);
422   PrintPaddedId(os_, graph_labeller, node);
423   os_ << PrintNode(graph_labeller, node) << "\n";
424 
425   MaglevPrintingVisitorOstream::cast(os_for_additional_info_)
426       ->set_padding(graph_labeller->max_node_id_width() + 4);
427 
428   MaybePrintLazyDeopt(os_, targets_, node, state);
429 }
430 
Process(ControlNode * control_node,const ProcessingState & state)431 void MaglevPrintingVisitor::Process(ControlNode* control_node,
432                                     const ProcessingState& state) {
433   MaglevGraphLabeller* graph_labeller = state.graph_labeller();
434 
435   MaybePrintEagerDeopt(os_, targets_, control_node, state);
436 
437   bool has_fallthrough = false;
438 
439   if (control_node->Is<JumpLoop>()) {
440     BasicBlock* target = control_node->Cast<JumpLoop>()->target();
441 
442     PrintVerticalArrows(os_, targets_, {}, {target}, true);
443     os_ << "◄─";
444     PrintPaddedId(os_, graph_labeller, control_node, "─", -2);
445     std::replace(targets_.begin(), targets_.end(), target,
446                  static_cast<BasicBlock*>(nullptr));
447 
448   } else if (control_node->Is<UnconditionalControlNode>()) {
449     BasicBlock* target =
450         control_node->Cast<UnconditionalControlNode>()->target();
451 
452     std::set<size_t> arrows_starting_here;
453     has_fallthrough |= !AddTargetIfNotNext(targets_, target, state.next_block(),
454                                            &arrows_starting_here);
455     PrintVerticalArrows(os_, targets_, arrows_starting_here);
456     PrintPaddedId(os_, graph_labeller, control_node,
457                   has_fallthrough ? " " : "─");
458 
459   } else if (control_node->Is<ConditionalControlNode>()) {
460     BasicBlock* true_target =
461         control_node->Cast<ConditionalControlNode>()->if_true();
462     BasicBlock* false_target =
463         control_node->Cast<ConditionalControlNode>()->if_false();
464 
465     std::set<size_t> arrows_starting_here;
466     has_fallthrough |= !AddTargetIfNotNext(
467         targets_, false_target, state.next_block(), &arrows_starting_here);
468     has_fallthrough |= !AddTargetIfNotNext(
469         targets_, true_target, state.next_block(), &arrows_starting_here);
470     PrintVerticalArrows(os_, targets_, arrows_starting_here);
471     PrintPaddedId(os_, graph_labeller, control_node, "─");
472 
473   } else {
474     PrintVerticalArrows(os_, targets_);
475     PrintPaddedId(os_, graph_labeller, control_node);
476   }
477 
478   os_ << PrintNode(graph_labeller, control_node) << "\n";
479 
480   bool printed_phis = false;
481   if (control_node->Is<UnconditionalControlNode>()) {
482     BasicBlock* target =
483         control_node->Cast<UnconditionalControlNode>()->target();
484     if (target->has_phi()) {
485       printed_phis = true;
486       PrintVerticalArrows(os_, targets_);
487       PrintPadding(os_, graph_labeller, -1);
488       os_ << (has_fallthrough ? "│" : " ");
489       os_ << "  with gap moves:\n";
490       int pid = state.block()->predecessor_id();
491       for (Phi* phi : *target->phis()) {
492         PrintVerticalArrows(os_, targets_);
493         PrintPadding(os_, graph_labeller, -1);
494         os_ << (has_fallthrough ? "│" : " ");
495         os_ << "    - ";
496         graph_labeller->PrintInput(os_, phi->input(pid));
497         os_ << " → " << graph_labeller->NodeId(phi) << ": Phi "
498             << phi->result().operand() << "\n";
499       }
500     }
501   }
502 
503   PrintVerticalArrows(os_, targets_);
504   if (has_fallthrough) {
505     PrintPadding(os_, graph_labeller, -1);
506     if (printed_phis) {
507       os_ << "▼";
508     } else {
509       os_ << "↓";
510     }
511   }
512   os_ << "\n";
513 
514   // TODO(leszeks): Allow MaglevPrintingVisitorOstream to print the arrowhead
515   // so that it overlaps the fallthrough arrow.
516   MaglevPrintingVisitorOstream::cast(os_for_additional_info_)
517       ->set_padding(graph_labeller->max_node_id_width() + 4);
518 }
519 
PrintGraph(std::ostream & os,MaglevCompilationUnit * compilation_unit,Graph * const graph)520 void PrintGraph(std::ostream& os, MaglevCompilationUnit* compilation_unit,
521                 Graph* const graph) {
522   GraphProcessor<MaglevPrintingVisitor> printer(compilation_unit, os);
523   printer.ProcessGraph(graph);
524 }
525 
Print(std::ostream & os) const526 void PrintNode::Print(std::ostream& os) const {
527   node_->Print(os, graph_labeller_);
528 }
529 
operator <<(std::ostream & os,const PrintNode & printer)530 std::ostream& operator<<(std::ostream& os, const PrintNode& printer) {
531   printer.Print(os);
532   return os;
533 }
534 
Print(std::ostream & os) const535 void PrintNodeLabel::Print(std::ostream& os) const {
536   graph_labeller_->PrintNodeLabel(os, node_);
537 }
538 
operator <<(std::ostream & os,const PrintNodeLabel & printer)539 std::ostream& operator<<(std::ostream& os, const PrintNodeLabel& printer) {
540   printer.Print(os);
541   return os;
542 }
543 
544 }  // namespace maglev
545 }  // namespace internal
546 }  // namespace v8
547