• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <sstream>
8 #include <string>
9 
10 #include "src/code-stubs.h"
11 #include "src/compiler/all-nodes.h"
12 #include "src/compiler/graph.h"
13 #include "src/compiler/node.h"
14 #include "src/compiler/node-properties.h"
15 #include "src/compiler/opcodes.h"
16 #include "src/compiler/operator.h"
17 #include "src/compiler/operator-properties.h"
18 #include "src/compiler/register-allocator.h"
19 #include "src/compiler/schedule.h"
20 #include "src/compiler/scheduler.h"
21 #include "src/interpreter/bytecodes.h"
22 #include "src/ostreams.h"
23 
24 namespace v8 {
25 namespace internal {
26 namespace compiler {
27 
28 
OpenVisualizerLogFile(CompilationInfo * info,const char * phase,const char * suffix,const char * mode)29 FILE* OpenVisualizerLogFile(CompilationInfo* info, const char* phase,
30                             const char* suffix, const char* mode) {
31   EmbeddedVector<char, 256> filename(0);
32   base::SmartArrayPointer<char> debug_name = info->GetDebugName();
33   if (strlen(debug_name.get()) > 0) {
34     SNPrintF(filename, "turbo-%s", debug_name.get());
35   } else if (info->has_shared_info()) {
36     SNPrintF(filename, "turbo-%p", static_cast<void*>(info));
37   } else {
38     SNPrintF(filename, "turbo-none-%s", phase);
39   }
40   std::replace(filename.start(), filename.start() + filename.length(), ' ',
41                '_');
42 
43   EmbeddedVector<char, 256> full_filename;
44   if (phase == nullptr) {
45     SNPrintF(full_filename, "%s.%s", filename.start(), suffix);
46   } else {
47     SNPrintF(full_filename, "%s-%s.%s", filename.start(), phase, suffix);
48   }
49   return base::OS::FOpen(full_filename.start(), mode);
50 }
51 
52 
SafeId(Node * node)53 static int SafeId(Node* node) { return node == nullptr ? -1 : node->id(); }
SafeMnemonic(Node * node)54 static const char* SafeMnemonic(Node* node) {
55   return node == nullptr ? "null" : node->op()->mnemonic();
56 }
57 
58 #define DEAD_COLOR "#999999"
59 
60 class Escaped {
61  public:
Escaped(const std::ostringstream & os,const char * escaped_chars="<>|{}")62   explicit Escaped(const std::ostringstream& os,
63                    const char* escaped_chars = "<>|{}")
64       : str_(os.str()), escaped_chars_(escaped_chars) {}
65 
operator <<(std::ostream & os,const Escaped & e)66   friend std::ostream& operator<<(std::ostream& os, const Escaped& e) {
67     for (std::string::const_iterator i = e.str_.begin(); i != e.str_.end();
68          ++i) {
69       if (e.needs_escape(*i)) os << "\\";
70       os << *i;
71     }
72     return os;
73   }
74 
75  private:
needs_escape(char ch) const76   bool needs_escape(char ch) const {
77     for (size_t i = 0; i < strlen(escaped_chars_); ++i) {
78       if (ch == escaped_chars_[i]) return true;
79     }
80     return false;
81   }
82 
83   const std::string str_;
84   const char* const escaped_chars_;
85 };
86 
87 class JSONGraphNodeWriter {
88  public:
JSONGraphNodeWriter(std::ostream & os,Zone * zone,const Graph * graph,const SourcePositionTable * positions)89   JSONGraphNodeWriter(std::ostream& os, Zone* zone, const Graph* graph,
90                       const SourcePositionTable* positions)
91       : os_(os), all_(zone, graph), positions_(positions), first_node_(true) {}
92 
Print()93   void Print() {
94     for (Node* const node : all_.live) PrintNode(node);
95     os_ << "\n";
96   }
97 
PrintNode(Node * node)98   void PrintNode(Node* node) {
99     if (first_node_) {
100       first_node_ = false;
101     } else {
102       os_ << ",\n";
103     }
104     std::ostringstream label;
105     label << *node->op();
106     os_ << "{\"id\":" << SafeId(node) << ",\"label\":\"" << Escaped(label, "\"")
107         << "\"";
108     IrOpcode::Value opcode = node->opcode();
109     if (IrOpcode::IsPhiOpcode(opcode)) {
110       os_ << ",\"rankInputs\":[0," << NodeProperties::FirstControlIndex(node)
111           << "]";
112       os_ << ",\"rankWithInput\":[" << NodeProperties::FirstControlIndex(node)
113           << "]";
114     } else if (opcode == IrOpcode::kIfTrue || opcode == IrOpcode::kIfFalse ||
115                opcode == IrOpcode::kLoop) {
116       os_ << ",\"rankInputs\":[" << NodeProperties::FirstControlIndex(node)
117           << "]";
118     }
119     if (opcode == IrOpcode::kBranch) {
120       os_ << ",\"rankInputs\":[0]";
121     }
122     SourcePosition position = positions_->GetSourcePosition(node);
123     if (position.IsKnown()) {
124       os_ << ",\"pos\":" << position.raw();
125     }
126     os_ << ",\"opcode\":\"" << IrOpcode::Mnemonic(node->opcode()) << "\"";
127     os_ << ",\"control\":" << (NodeProperties::IsControl(node) ? "true"
128                                                                : "false");
129     if (NodeProperties::IsTyped(node)) {
130       Type* type = NodeProperties::GetType(node);
131       std::ostringstream type_out;
132       type->PrintTo(type_out);
133       os_ << ",\"type\":\"" << Escaped(type_out, "\"") << "\"";
134     }
135     os_ << "}";
136   }
137 
138  private:
139   std::ostream& os_;
140   AllNodes all_;
141   const SourcePositionTable* positions_;
142   bool first_node_;
143 
144   DISALLOW_COPY_AND_ASSIGN(JSONGraphNodeWriter);
145 };
146 
147 
148 class JSONGraphEdgeWriter {
149  public:
JSONGraphEdgeWriter(std::ostream & os,Zone * zone,const Graph * graph)150   JSONGraphEdgeWriter(std::ostream& os, Zone* zone, const Graph* graph)
151       : os_(os), all_(zone, graph), first_edge_(true) {}
152 
Print()153   void Print() {
154     for (Node* const node : all_.live) PrintEdges(node);
155     os_ << "\n";
156   }
157 
PrintEdges(Node * node)158   void PrintEdges(Node* node) {
159     for (int i = 0; i < node->InputCount(); i++) {
160       Node* input = node->InputAt(i);
161       if (input == nullptr) continue;
162       PrintEdge(node, i, input);
163     }
164   }
165 
PrintEdge(Node * from,int index,Node * to)166   void PrintEdge(Node* from, int index, Node* to) {
167     if (first_edge_) {
168       first_edge_ = false;
169     } else {
170       os_ << ",\n";
171     }
172     const char* edge_type = nullptr;
173     if (index < NodeProperties::FirstValueIndex(from)) {
174       edge_type = "unknown";
175     } else if (index < NodeProperties::FirstContextIndex(from)) {
176       edge_type = "value";
177     } else if (index < NodeProperties::FirstFrameStateIndex(from)) {
178       edge_type = "context";
179     } else if (index < NodeProperties::FirstEffectIndex(from)) {
180       edge_type = "frame-state";
181     } else if (index < NodeProperties::FirstControlIndex(from)) {
182       edge_type = "effect";
183     } else {
184       edge_type = "control";
185     }
186     os_ << "{\"source\":" << SafeId(to) << ",\"target\":" << SafeId(from)
187         << ",\"index\":" << index << ",\"type\":\"" << edge_type << "\"}";
188   }
189 
190  private:
191   std::ostream& os_;
192   AllNodes all_;
193   bool first_edge_;
194 
195   DISALLOW_COPY_AND_ASSIGN(JSONGraphEdgeWriter);
196 };
197 
198 
operator <<(std::ostream & os,const AsJSON & ad)199 std::ostream& operator<<(std::ostream& os, const AsJSON& ad) {
200   Zone tmp_zone;
201   os << "{\n\"nodes\":[";
202   JSONGraphNodeWriter(os, &tmp_zone, &ad.graph, ad.positions).Print();
203   os << "],\n\"edges\":[";
204   JSONGraphEdgeWriter(os, &tmp_zone, &ad.graph).Print();
205   os << "]}";
206   return os;
207 }
208 
209 
210 class GraphC1Visualizer {
211  public:
212   GraphC1Visualizer(std::ostream& os, Zone* zone);  // NOLINT
213 
214   void PrintCompilation(const CompilationInfo* info);
215   void PrintSchedule(const char* phase, const Schedule* schedule,
216                      const SourcePositionTable* positions,
217                      const InstructionSequence* instructions);
218   void PrintLiveRanges(const char* phase, const RegisterAllocationData* data);
zone() const219   Zone* zone() const { return zone_; }
220 
221  private:
222   void PrintIndent();
223   void PrintStringProperty(const char* name, const char* value);
224   void PrintLongProperty(const char* name, int64_t value);
225   void PrintIntProperty(const char* name, int value);
226   void PrintBlockProperty(const char* name, int rpo_number);
227   void PrintNodeId(Node* n);
228   void PrintNode(Node* n);
229   void PrintInputs(Node* n);
230   template <typename InputIterator>
231   void PrintInputs(InputIterator* i, int count, const char* prefix);
232   void PrintType(Node* node);
233 
234   void PrintLiveRange(LiveRange* range, const char* type, int vreg);
235   void PrintLiveRangeChain(TopLevelLiveRange* range, const char* type);
236 
237   class Tag final BASE_EMBEDDED {
238    public:
Tag(GraphC1Visualizer * visualizer,const char * name)239     Tag(GraphC1Visualizer* visualizer, const char* name) {
240       name_ = name;
241       visualizer_ = visualizer;
242       visualizer->PrintIndent();
243       visualizer_->os_ << "begin_" << name << "\n";
244       visualizer->indent_++;
245     }
246 
~Tag()247     ~Tag() {
248       visualizer_->indent_--;
249       visualizer_->PrintIndent();
250       visualizer_->os_ << "end_" << name_ << "\n";
251       DCHECK(visualizer_->indent_ >= 0);
252     }
253 
254    private:
255     GraphC1Visualizer* visualizer_;
256     const char* name_;
257   };
258 
259   std::ostream& os_;
260   int indent_;
261   Zone* zone_;
262 
263   DISALLOW_COPY_AND_ASSIGN(GraphC1Visualizer);
264 };
265 
266 
PrintIndent()267 void GraphC1Visualizer::PrintIndent() {
268   for (int i = 0; i < indent_; i++) {
269     os_ << "  ";
270   }
271 }
272 
273 
GraphC1Visualizer(std::ostream & os,Zone * zone)274 GraphC1Visualizer::GraphC1Visualizer(std::ostream& os, Zone* zone)
275     : os_(os), indent_(0), zone_(zone) {}
276 
277 
PrintStringProperty(const char * name,const char * value)278 void GraphC1Visualizer::PrintStringProperty(const char* name,
279                                             const char* value) {
280   PrintIndent();
281   os_ << name << " \"" << value << "\"\n";
282 }
283 
284 
PrintLongProperty(const char * name,int64_t value)285 void GraphC1Visualizer::PrintLongProperty(const char* name, int64_t value) {
286   PrintIndent();
287   os_ << name << " " << static_cast<int>(value / 1000) << "\n";
288 }
289 
290 
PrintBlockProperty(const char * name,int rpo_number)291 void GraphC1Visualizer::PrintBlockProperty(const char* name, int rpo_number) {
292   PrintIndent();
293   os_ << name << " \"B" << rpo_number << "\"\n";
294 }
295 
296 
PrintIntProperty(const char * name,int value)297 void GraphC1Visualizer::PrintIntProperty(const char* name, int value) {
298   PrintIndent();
299   os_ << name << " " << value << "\n";
300 }
301 
302 
PrintCompilation(const CompilationInfo * info)303 void GraphC1Visualizer::PrintCompilation(const CompilationInfo* info) {
304   Tag tag(this, "compilation");
305   base::SmartArrayPointer<char> name = info->GetDebugName();
306   if (info->IsOptimizing()) {
307     PrintStringProperty("name", name.get());
308     PrintIndent();
309     os_ << "method \"" << name.get() << ":" << info->optimization_id()
310         << "\"\n";
311   } else {
312     PrintStringProperty("name", name.get());
313     PrintStringProperty("method", "stub");
314   }
315   PrintLongProperty("date",
316                     static_cast<int64_t>(base::OS::TimeCurrentMillis()));
317 }
318 
319 
PrintNodeId(Node * n)320 void GraphC1Visualizer::PrintNodeId(Node* n) { os_ << "n" << SafeId(n); }
321 
322 
PrintNode(Node * n)323 void GraphC1Visualizer::PrintNode(Node* n) {
324   PrintNodeId(n);
325   os_ << " " << *n->op() << " ";
326   PrintInputs(n);
327 }
328 
329 
330 template <typename InputIterator>
PrintInputs(InputIterator * i,int count,const char * prefix)331 void GraphC1Visualizer::PrintInputs(InputIterator* i, int count,
332                                     const char* prefix) {
333   if (count > 0) {
334     os_ << prefix;
335   }
336   while (count > 0) {
337     os_ << " ";
338     PrintNodeId(**i);
339     ++(*i);
340     count--;
341   }
342 }
343 
344 
PrintInputs(Node * node)345 void GraphC1Visualizer::PrintInputs(Node* node) {
346   auto i = node->inputs().begin();
347   PrintInputs(&i, node->op()->ValueInputCount(), " ");
348   PrintInputs(&i, OperatorProperties::GetContextInputCount(node->op()),
349               " Ctx:");
350   PrintInputs(&i, OperatorProperties::GetFrameStateInputCount(node->op()),
351               " FS:");
352   PrintInputs(&i, node->op()->EffectInputCount(), " Eff:");
353   PrintInputs(&i, node->op()->ControlInputCount(), " Ctrl:");
354 }
355 
356 
PrintType(Node * node)357 void GraphC1Visualizer::PrintType(Node* node) {
358   if (NodeProperties::IsTyped(node)) {
359     Type* type = NodeProperties::GetType(node);
360     os_ << " type:";
361     type->PrintTo(os_);
362   }
363 }
364 
365 
PrintSchedule(const char * phase,const Schedule * schedule,const SourcePositionTable * positions,const InstructionSequence * instructions)366 void GraphC1Visualizer::PrintSchedule(const char* phase,
367                                       const Schedule* schedule,
368                                       const SourcePositionTable* positions,
369                                       const InstructionSequence* instructions) {
370   Tag tag(this, "cfg");
371   PrintStringProperty("name", phase);
372   const BasicBlockVector* rpo = schedule->rpo_order();
373   for (size_t i = 0; i < rpo->size(); i++) {
374     BasicBlock* current = (*rpo)[i];
375     Tag block_tag(this, "block");
376     PrintBlockProperty("name", current->rpo_number());
377     PrintIntProperty("from_bci", -1);
378     PrintIntProperty("to_bci", -1);
379 
380     PrintIndent();
381     os_ << "predecessors";
382     for (BasicBlock* predecessor : current->predecessors()) {
383       os_ << " \"B" << predecessor->rpo_number() << "\"";
384     }
385     os_ << "\n";
386 
387     PrintIndent();
388     os_ << "successors";
389     for (BasicBlock* successor : current->successors()) {
390       os_ << " \"B" << successor->rpo_number() << "\"";
391     }
392     os_ << "\n";
393 
394     PrintIndent();
395     os_ << "xhandlers\n";
396 
397     PrintIndent();
398     os_ << "flags\n";
399 
400     if (current->dominator() != nullptr) {
401       PrintBlockProperty("dominator", current->dominator()->rpo_number());
402     }
403 
404     PrintIntProperty("loop_depth", current->loop_depth());
405 
406     const InstructionBlock* instruction_block =
407         instructions->InstructionBlockAt(
408             RpoNumber::FromInt(current->rpo_number()));
409     if (instruction_block->code_start() >= 0) {
410       int first_index = instruction_block->first_instruction_index();
411       int last_index = instruction_block->last_instruction_index();
412       PrintIntProperty(
413           "first_lir_id",
414           LifetimePosition::GapFromInstructionIndex(first_index).value());
415       PrintIntProperty("last_lir_id",
416                        LifetimePosition::InstructionFromInstructionIndex(
417                            last_index).value());
418     }
419 
420     {
421       Tag states_tag(this, "states");
422       Tag locals_tag(this, "locals");
423       int total = 0;
424       for (BasicBlock::const_iterator i = current->begin(); i != current->end();
425            ++i) {
426         if ((*i)->opcode() == IrOpcode::kPhi) total++;
427       }
428       PrintIntProperty("size", total);
429       PrintStringProperty("method", "None");
430       int index = 0;
431       for (BasicBlock::const_iterator i = current->begin(); i != current->end();
432            ++i) {
433         if ((*i)->opcode() != IrOpcode::kPhi) continue;
434         PrintIndent();
435         os_ << index << " ";
436         PrintNodeId(*i);
437         os_ << " [";
438         PrintInputs(*i);
439         os_ << "]\n";
440         index++;
441       }
442     }
443 
444     {
445       Tag HIR_tag(this, "HIR");
446       for (BasicBlock::const_iterator i = current->begin(); i != current->end();
447            ++i) {
448         Node* node = *i;
449         if (node->opcode() == IrOpcode::kPhi) continue;
450         int uses = node->UseCount();
451         PrintIndent();
452         os_ << "0 " << uses << " ";
453         PrintNode(node);
454         if (FLAG_trace_turbo_types) {
455           os_ << " ";
456           PrintType(node);
457         }
458         if (positions != nullptr) {
459           SourcePosition position = positions->GetSourcePosition(node);
460           if (position.IsKnown()) {
461             os_ << " pos:" << position.raw();
462           }
463         }
464         os_ << " <|@\n";
465       }
466 
467       BasicBlock::Control control = current->control();
468       if (control != BasicBlock::kNone) {
469         PrintIndent();
470         os_ << "0 0 ";
471         if (current->control_input() != nullptr) {
472           PrintNode(current->control_input());
473         } else {
474           os_ << -1 - current->rpo_number() << " Goto";
475         }
476         os_ << " ->";
477         for (BasicBlock* successor : current->successors()) {
478           os_ << " B" << successor->rpo_number();
479         }
480         if (FLAG_trace_turbo_types && current->control_input() != nullptr) {
481           os_ << " ";
482           PrintType(current->control_input());
483         }
484         os_ << " <|@\n";
485       }
486     }
487 
488     if (instructions != nullptr) {
489       Tag LIR_tag(this, "LIR");
490       for (int j = instruction_block->first_instruction_index();
491            j <= instruction_block->last_instruction_index(); j++) {
492         PrintIndent();
493         PrintableInstruction printable = {
494             RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN),
495             instructions->InstructionAt(j)};
496         os_ << j << " " << printable << " <|@\n";
497       }
498     }
499   }
500 }
501 
502 
PrintLiveRanges(const char * phase,const RegisterAllocationData * data)503 void GraphC1Visualizer::PrintLiveRanges(const char* phase,
504                                         const RegisterAllocationData* data) {
505   Tag tag(this, "intervals");
506   PrintStringProperty("name", phase);
507 
508   for (auto range : data->fixed_double_live_ranges()) {
509     PrintLiveRangeChain(range, "fixed");
510   }
511 
512   for (auto range : data->fixed_live_ranges()) {
513     PrintLiveRangeChain(range, "fixed");
514   }
515 
516   for (auto range : data->live_ranges()) {
517     PrintLiveRangeChain(range, "object");
518   }
519 }
520 
521 
PrintLiveRangeChain(TopLevelLiveRange * range,const char * type)522 void GraphC1Visualizer::PrintLiveRangeChain(TopLevelLiveRange* range,
523                                             const char* type) {
524   if (range == nullptr || range->IsEmpty()) return;
525   int vreg = range->vreg();
526   for (LiveRange* child = range; child != nullptr; child = child->next()) {
527     PrintLiveRange(child, type, vreg);
528   }
529 }
530 
531 
PrintLiveRange(LiveRange * range,const char * type,int vreg)532 void GraphC1Visualizer::PrintLiveRange(LiveRange* range, const char* type,
533                                        int vreg) {
534   if (range != nullptr && !range->IsEmpty()) {
535     PrintIndent();
536     os_ << vreg << ":" << range->relative_id() << " " << type;
537     if (range->HasRegisterAssigned()) {
538       AllocatedOperand op = AllocatedOperand::cast(range->GetAssignedOperand());
539       if (op.IsDoubleRegister()) {
540         DoubleRegister assigned_reg = op.GetDoubleRegister();
541         os_ << " \"" << assigned_reg.ToString() << "\"";
542       } else {
543         DCHECK(op.IsRegister());
544         Register assigned_reg = op.GetRegister();
545         os_ << " \"" << assigned_reg.ToString() << "\"";
546       }
547     } else if (range->spilled()) {
548       auto top = range->TopLevel();
549       int index = -1;
550       if (top->HasSpillRange()) {
551         index = kMaxInt;  // This hasn't been set yet.
552       } else if (top->GetSpillOperand()->IsConstant()) {
553         os_ << " \"const(nostack):"
554             << ConstantOperand::cast(top->GetSpillOperand())->virtual_register()
555             << "\"";
556       } else {
557         index = AllocatedOperand::cast(top->GetSpillOperand())->index();
558         if (top->kind() == DOUBLE_REGISTERS) {
559           os_ << " \"double_stack:" << index << "\"";
560         } else if (top->kind() == GENERAL_REGISTERS) {
561           os_ << " \"stack:" << index << "\"";
562         }
563       }
564     }
565 
566     os_ << " " << vreg;
567     for (auto interval = range->first_interval(); interval != nullptr;
568          interval = interval->next()) {
569       os_ << " [" << interval->start().value() << ", "
570           << interval->end().value() << "[";
571     }
572 
573     UsePosition* current_pos = range->first_pos();
574     while (current_pos != nullptr) {
575       if (current_pos->RegisterIsBeneficial() || FLAG_trace_all_uses) {
576         os_ << " " << current_pos->pos().value() << " M";
577       }
578       current_pos = current_pos->next();
579     }
580 
581     os_ << " \"\"\n";
582   }
583 }
584 
585 
operator <<(std::ostream & os,const AsC1VCompilation & ac)586 std::ostream& operator<<(std::ostream& os, const AsC1VCompilation& ac) {
587   Zone tmp_zone;
588   GraphC1Visualizer(os, &tmp_zone).PrintCompilation(ac.info_);
589   return os;
590 }
591 
592 
operator <<(std::ostream & os,const AsC1V & ac)593 std::ostream& operator<<(std::ostream& os, const AsC1V& ac) {
594   Zone tmp_zone;
595   GraphC1Visualizer(os, &tmp_zone)
596       .PrintSchedule(ac.phase_, ac.schedule_, ac.positions_, ac.instructions_);
597   return os;
598 }
599 
600 
operator <<(std::ostream & os,const AsC1VRegisterAllocationData & ac)601 std::ostream& operator<<(std::ostream& os,
602                          const AsC1VRegisterAllocationData& ac) {
603   Zone tmp_zone;
604   GraphC1Visualizer(os, &tmp_zone).PrintLiveRanges(ac.phase_, ac.data_);
605   return os;
606 }
607 
608 const int kUnvisited = 0;
609 const int kOnStack = 1;
610 const int kVisited = 2;
611 
operator <<(std::ostream & os,const AsRPO & ar)612 std::ostream& operator<<(std::ostream& os, const AsRPO& ar) {
613   Zone local_zone;
614   ZoneVector<byte> state(ar.graph.NodeCount(), kUnvisited, &local_zone);
615   ZoneStack<Node*> stack(&local_zone);
616 
617   stack.push(ar.graph.end());
618   state[ar.graph.end()->id()] = kOnStack;
619   while (!stack.empty()) {
620     Node* n = stack.top();
621     bool pop = true;
622     for (Node* const i : n->inputs()) {
623       if (state[i->id()] == kUnvisited) {
624         state[i->id()] = kOnStack;
625         stack.push(i);
626         pop = false;
627         break;
628       }
629     }
630     if (pop) {
631       state[n->id()] = kVisited;
632       stack.pop();
633       os << "#" << n->id() << ":" << *n->op() << "(";
634       int j = 0;
635       for (Node* const i : n->inputs()) {
636         if (j++ > 0) os << ", ";
637         os << "#" << SafeId(i) << ":" << SafeMnemonic(i);
638       }
639       os << ")" << std::endl;
640     }
641   }
642   return os;
643 }
644 }  // namespace compiler
645 }  // namespace internal
646 }  // namespace v8
647