• 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 #ifndef V8_COMPILER_NODE_H_
6 #define V8_COMPILER_NODE_H_
7 
8 #include "src/common/globals.h"
9 #include "src/compiler/graph-zone-traits.h"
10 #include "src/compiler/opcodes.h"
11 #include "src/compiler/operator.h"
12 #include "src/compiler/types.h"
13 #include "src/zone/zone-containers.h"
14 
15 namespace v8 {
16 namespace internal {
17 namespace compiler {
18 
19 // Forward declarations.
20 class Edge;
21 class Graph;
22 
23 
24 // Marks are used during traversal of the graph to distinguish states of nodes.
25 // Each node has a mark which is a monotonically increasing integer, and a
26 // {NodeMarker} has a range of values that indicate states of a node.
27 using Mark = uint32_t;
28 
29 // NodeIds are identifying numbers for nodes that can be used to index auxiliary
30 // out-of-line data associated with each node.
31 using NodeId = uint32_t;
32 
33 // A Node is the basic primitive of graphs. Nodes are chained together by
34 // input/use chains but by default otherwise contain only an identifying number
35 // which specific applications of graphs and nodes can use to index auxiliary
36 // out-of-line data, especially transient data.
37 //
38 // In addition Nodes only contain a mutable Operator that may change during
39 // compilation, e.g. during lowering passes. Other information that needs to be
40 // associated with Nodes during compilation must be stored out-of-line indexed
41 // by the Node's id.
42 class V8_EXPORT_PRIVATE Node final {
43  public:
44   static Node* New(Zone* zone, NodeId id, const Operator* op, int input_count,
45                    Node* const* inputs, bool has_extensible_inputs);
46   static Node* Clone(Zone* zone, NodeId id, const Node* node);
47 
48   inline bool IsDead() const;
49   void Kill();
50 
op()51   const Operator* op() const { return op_; }
52 
opcode()53   constexpr IrOpcode::Value opcode() const {
54     DCHECK_GE(IrOpcode::kLast, op_->opcode());
55     return static_cast<IrOpcode::Value>(op_->opcode());
56   }
57 
id()58   NodeId id() const { return IdField::decode(bit_field_); }
59 
InputCount()60   int InputCount() const {
61     return has_inline_inputs() ? InlineCountField::decode(bit_field_)
62                                : outline_inputs()->count_;
63   }
64 
65 #ifdef DEBUG
66   void Verify();
67 #else
Verify()68   inline void Verify() {}
69 #endif
70 
InputAt(int index)71   Node* InputAt(int index) const {
72     DCHECK_LE(0, index);
73     DCHECK_LT(index, InputCount());
74     return *GetInputPtrConst(index);
75   }
76 
ReplaceInput(int index,Node * new_to)77   void ReplaceInput(int index, Node* new_to) {
78     DCHECK_LE(0, index);
79     DCHECK_LT(index, InputCount());
80     ZoneNodePtr* input_ptr = GetInputPtr(index);
81     Node* old_to = *input_ptr;
82     if (old_to != new_to) {
83       Use* use = GetUsePtr(index);
84       if (old_to) old_to->RemoveUse(use);
85       *input_ptr = new_to;
86       if (new_to) new_to->AppendUse(use);
87     }
88   }
89 
90   void AppendInput(Zone* zone, Node* new_to);
91   void InsertInput(Zone* zone, int index, Node* new_to);
92   void InsertInputs(Zone* zone, int index, int count);
93   // Returns the removed input.
94   Node* RemoveInput(int index);
95   void NullAllInputs();
96   void TrimInputCount(int new_input_count);
97   // Can trim, extend by appending new inputs, or do nothing.
98   void EnsureInputCount(Zone* zone, int new_input_count);
99 
100   int UseCount() const;
101   void ReplaceUses(Node* replace_to);
102 
103   class InputEdges;
104   inline InputEdges input_edges();
105 
106   class Inputs;
107   inline Inputs inputs() const;
108 
109   class UseEdges final {
110    public:
111     using value_type = Edge;
112 
113     class iterator;
114     inline iterator begin() const;
115     inline iterator end() const;
116 
117     bool empty() const;
118 
UseEdges(Node * node)119     explicit UseEdges(Node* node) : node_(node) {}
120 
121    private:
122     Node* node_;
123   };
124 
use_edges()125   UseEdges use_edges() { return UseEdges(this); }
126 
127   class V8_EXPORT_PRIVATE Uses final {
128    public:
129     using value_type = Node*;
130 
131     class const_iterator;
132     inline const_iterator begin() const;
133     inline const_iterator end() const;
134 
135     bool empty() const;
136 
Uses(Node * node)137     explicit Uses(Node* node) : node_(node) {}
138 
139    private:
140     Node* node_;
141   };
142 
uses()143   Uses uses() { return Uses(this); }
144 
145   // Returns true if {owner} is the only user of {this} node.
146   bool OwnedBy(Node const* owner) const;
147 
148   // Returns true if {owner1} and {owner2} are the only users of {this} node.
149   bool OwnedBy(Node const* owner1, Node const* owner2) const;
150 
Print()151   void Print() const { Print(1); }
152   void Print(int depth) const;
153   void Print(std::ostream&, int depth = 1) const;
154 
155  private:
156   template <typename NodePtrT>
157   inline static Node* NewImpl(Zone* zone, NodeId id, const Operator* op,
158                               int input_count, NodePtrT const* inputs,
159                               bool has_extensible_inputs);
160 
161   struct Use;
162   using ZoneUsePtr = GraphZoneTraits::Ptr<Use>;
163 
164   // Out of line storage for inputs when the number of inputs overflowed the
165   // capacity of the inline-allocated space.
166   struct OutOfLineInputs {
167     ZoneNodePtr node_;
168     int count_;
169     int capacity_;
170 
171     // Inputs are allocated right behind the OutOfLineInputs instance.
172     inline ZoneNodePtr* inputs();
173 
174     static OutOfLineInputs* New(Zone* zone, int capacity);
175     void ExtractFrom(Use* use_ptr, ZoneNodePtr* input_ptr, int count);
176   };
177   using ZoneOutOfLineInputsPtr = GraphZoneTraits::Ptr<OutOfLineInputs>;
178 
179   // A link in the use chain for a node. Every input {i} to a node {n} has an
180   // associated {Use} which is linked into the use chain of the {i} node.
181   struct Use {
182     ZoneUsePtr next;
183     ZoneUsePtr prev;
184     uint32_t bit_field_;
185 
input_indexUse186     int input_index() const { return InputIndexField::decode(bit_field_); }
is_inline_useUse187     bool is_inline_use() const { return InlineField::decode(bit_field_); }
input_ptrUse188     ZoneNodePtr* input_ptr() {
189       int index = input_index();
190       Use* start = this + 1 + index;
191       ZoneNodePtr* inputs =
192           is_inline_use() ? reinterpret_cast<Node*>(start)->inline_inputs()
193                           : reinterpret_cast<OutOfLineInputs*>(start)->inputs();
194       return &inputs[index];
195     }
196 
fromUse197     Node* from() {
198       Use* start = this + 1 + input_index();
199       return is_inline_use() ? reinterpret_cast<Node*>(start)
200                              : reinterpret_cast<OutOfLineInputs*>(start)->node_;
201     }
202 
203     using InlineField = base::BitField<bool, 0, 1>;
204     using InputIndexField = base::BitField<unsigned, 1, 31>;
205   };
206 
207   //============================================================================
208   //== Memory layout ===========================================================
209   //============================================================================
210   // Saving space for big graphs is important. We use a memory layout trick to
211   // be able to map {Node} objects to {Use} objects and vice-versa in a
212   // space-efficient manner.
213   //
214   // {Use} links are laid out in memory directly before a {Node}, followed by
215   // direct pointers to input {Nodes}.
216   //
217   // inline case:
218   // |Use #N  |Use #N-1|...|Use #1  |Use #0  |Node xxxx |I#0|I#1|...|I#N-1|I#N|
219   //          ^                              ^                  ^
220   //          + Use                          + Node             + Input
221   //
222   // Since every {Use} instance records its {input_index}, pointer arithmetic
223   // can compute the {Node}.
224   //
225   // out-of-line case:
226   //     |Node xxxx |
227   //     ^       + outline ------------------+
228   //     +----------------------------------------+
229   //                                         |    |
230   //                                         v    | node
231   // |Use #N  |Use #N-1|...|Use #1  |Use #0  |OOL xxxxx |I#0|I#1|...|I#N-1|I#N|
232   //          ^                                                 ^
233   //          + Use                                             + Input
234   //
235   // Out-of-line storage of input lists is needed if appending an input to
236   // a node exceeds the maximum inline capacity.
237 
238   Node(NodeId id, const Operator* op, int inline_count, int inline_capacity);
239   Node(const Node&) = delete;
240   Node& operator=(const Node&) = delete;
241 
242   inline Address inputs_location() const;
243 
inline_inputs()244   ZoneNodePtr* inline_inputs() const {
245     return reinterpret_cast<ZoneNodePtr*>(inputs_location());
246   }
outline_inputs()247   OutOfLineInputs* outline_inputs() const {
248     return *reinterpret_cast<ZoneOutOfLineInputsPtr*>(inputs_location());
249   }
set_outline_inputs(OutOfLineInputs * outline)250   void set_outline_inputs(OutOfLineInputs* outline) {
251     *reinterpret_cast<ZoneOutOfLineInputsPtr*>(inputs_location()) = outline;
252   }
253 
GetInputPtrConst(int input_index)254   ZoneNodePtr const* GetInputPtrConst(int input_index) const {
255     return has_inline_inputs() ? &(inline_inputs()[input_index])
256                                : &(outline_inputs()->inputs()[input_index]);
257   }
GetInputPtr(int input_index)258   ZoneNodePtr* GetInputPtr(int input_index) {
259     return has_inline_inputs() ? &(inline_inputs()[input_index])
260                                : &(outline_inputs()->inputs()[input_index]);
261   }
GetUsePtr(int input_index)262   Use* GetUsePtr(int input_index) {
263     Use* ptr = has_inline_inputs() ? reinterpret_cast<Use*>(this)
264                                    : reinterpret_cast<Use*>(outline_inputs());
265     return &ptr[-1 - input_index];
266   }
267 
268   void AppendUse(Use* use);
269   void RemoveUse(Use* use);
270 
new(size_t,void * location)271   void* operator new(size_t, void* location) { return location; }
272 
273   // Only NodeProperties should manipulate the op.
set_op(const Operator * op)274   void set_op(const Operator* op) { op_ = op; }
275 
276   // Only NodeProperties should manipulate the type.
type()277   Type type() const { return type_; }
set_type(Type type)278   void set_type(Type type) { type_ = type; }
279 
280   // Only NodeMarkers should manipulate the marks on nodes.
mark()281   Mark mark() const { return mark_; }
set_mark(Mark mark)282   void set_mark(Mark mark) { mark_ = mark; }
283 
has_inline_inputs()284   inline bool has_inline_inputs() const {
285     return InlineCountField::decode(bit_field_) != kOutlineMarker;
286   }
287 
288   void ClearInputs(int start, int count);
289 
290   using IdField = base::BitField<NodeId, 0, 24>;
291   using InlineCountField = base::BitField<unsigned, 24, 4>;
292   using InlineCapacityField = base::BitField<unsigned, 28, 4>;
293   static const int kOutlineMarker = InlineCountField::kMax;
294   static const int kMaxInlineCapacity = InlineCapacityField::kMax - 1;
295 
296   const Operator* op_;
297   Type type_;
298   Mark mark_;
299   uint32_t bit_field_;
300   ZoneUsePtr first_use_;
301 
302   friend class Edge;
303   friend class NodeMarkerBase;
304   friend class NodeProperties;
305 };
306 
inputs_location()307 Address Node::inputs_location() const {
308   return reinterpret_cast<Address>(this) + sizeof(Node);
309 }
310 
inputs()311 ZoneNodePtr* Node::OutOfLineInputs::inputs() {
312   return reinterpret_cast<ZoneNodePtr*>(reinterpret_cast<Address>(this) +
313                                         sizeof(Node::OutOfLineInputs));
314 }
315 
316 std::ostream& operator<<(std::ostream& os, const Node& n);
317 
318 // Base class for node wrappers.
319 class NodeWrapper {
320  public:
NodeWrapper(Node * node)321   explicit constexpr NodeWrapper(Node* node) : node_(node) {}
322   operator Node*() const { return node_; }
323   Node* operator->() const { return node_; }
324 
325  protected:
node()326   Node* node() const { return node_; }
set_node(Node * node)327   void set_node(Node* node) {
328     DCHECK_NOT_NULL(node);
329     node_ = node;
330   }
331 
332  private:
333   Node* node_;
334 };
335 
336 // Wrapper classes for special node/edge types (effect, control, frame states).
337 
338 class Effect : public NodeWrapper {
339  public:
Effect(Node * node)340   explicit constexpr Effect(Node* node) : NodeWrapper(node) {
341     // TODO(jgruber): Remove the End special case.
342     SLOW_DCHECK(node == nullptr || node->op()->opcode() == IrOpcode::kEnd ||
343                 node->op()->EffectOutputCount() > 0);
344   }
345 
346   // Support the common `Node* x = effect = ...` pattern.
347   Node* operator=(Node* value) {
348     DCHECK_GT(value->op()->EffectOutputCount(), 0);
349     set_node(value);
350     return value;
351   }
352 };
353 
354 class Control : public NodeWrapper {
355  public:
Control(Node * node)356   explicit constexpr Control(Node* node) : NodeWrapper(node) {
357     // TODO(jgruber): Remove the End special case.
358     SLOW_DCHECK(node == nullptr || node->opcode() == IrOpcode::kEnd ||
359                 node->op()->ControlOutputCount() > 0);
360   }
361 
362   // Support the common `Node* x = control = ...` pattern.
363   Node* operator=(Node* value) {
364     DCHECK_GT(value->op()->ControlOutputCount(), 0);
365     set_node(value);
366     return value;
367   }
368 };
369 
370 // Typedefs to shorten commonly used Node containers.
371 using NodeDeque = ZoneDeque<Node*>;
372 using NodeSet = ZoneSet<Node*>;
373 using NodeVector = ZoneVector<Node*>;
374 using NodeVectorVector = ZoneVector<NodeVector>;
375 
376 class Node::InputEdges final {
377  public:
378   using value_type = Edge;
379 
380   class iterator;
381   inline iterator begin() const;
382   inline iterator end() const;
383 
empty()384   bool empty() const { return count_ == 0; }
count()385   int count() const { return count_; }
386 
387   inline value_type operator[](int index) const;
388 
InputEdges(ZoneNodePtr * input_root,Use * use_root,int count)389   InputEdges(ZoneNodePtr* input_root, Use* use_root, int count)
390       : input_root_(input_root), use_root_(use_root), count_(count) {}
391 
392  private:
393   ZoneNodePtr* input_root_;
394   Use* use_root_;
395   int count_;
396 };
397 
398 class V8_EXPORT_PRIVATE Node::Inputs final {
399  public:
400   using value_type = Node*;
401 
402   class const_iterator;
403   inline const_iterator begin() const;
404   inline const_iterator end() const;
405 
empty()406   bool empty() const { return count_ == 0; }
count()407   int count() const { return count_; }
408 
409   inline value_type operator[](int index) const;
410 
Inputs(ZoneNodePtr const * input_root,int count)411   explicit Inputs(ZoneNodePtr const* input_root, int count)
412       : input_root_(input_root), count_(count) {}
413 
414  private:
415   ZoneNodePtr const* input_root_;
416   int count_;
417 };
418 
419 // An encapsulation for information associated with a single use of node as a
420 // input from another node, allowing access to both the defining node and
421 // the node having the input.
422 class Edge final {
423  public:
from()424   Node* from() const { return use_->from(); }
to()425   Node* to() const { return *input_ptr_; }
index()426   int index() const {
427     int const index = use_->input_index();
428     DCHECK_LT(index, use_->from()->InputCount());
429     return index;
430   }
431 
432   bool operator==(const Edge& other) { return input_ptr_ == other.input_ptr_; }
433   bool operator!=(const Edge& other) { return !(*this == other); }
434 
UpdateTo(Node * new_to)435   void UpdateTo(Node* new_to) {
436     Node* old_to = *input_ptr_;
437     if (old_to != new_to) {
438       if (old_to) old_to->RemoveUse(use_);
439       *input_ptr_ = new_to;
440       if (new_to) new_to->AppendUse(use_);
441     }
442   }
443 
444  private:
445   friend class Node::UseEdges::iterator;
446   friend class Node::InputEdges;
447   friend class Node::InputEdges::iterator;
448 
Edge(Node::Use * use,ZoneNodePtr * input_ptr)449   Edge(Node::Use* use, ZoneNodePtr* input_ptr)
450       : use_(use), input_ptr_(input_ptr) {
451     DCHECK_NOT_NULL(use);
452     DCHECK_NOT_NULL(input_ptr);
453     DCHECK_EQ(input_ptr, use->input_ptr());
454   }
455 
456   Node::Use* use_;
457   ZoneNodePtr* input_ptr_;
458 };
459 
IsDead()460 bool Node::IsDead() const {
461   Node::Inputs inputs = this->inputs();
462   return inputs.count() > 0 && inputs[0] == nullptr;
463 }
464 
input_edges()465 Node::InputEdges Node::input_edges() {
466   int inline_count = InlineCountField::decode(bit_field_);
467   if (inline_count != kOutlineMarker) {
468     return InputEdges(inline_inputs(), reinterpret_cast<Use*>(this) - 1,
469                       inline_count);
470   } else {
471     return InputEdges(outline_inputs()->inputs(),
472                       reinterpret_cast<Use*>(outline_inputs()) - 1,
473                       outline_inputs()->count_);
474   }
475 }
476 
inputs()477 Node::Inputs Node::inputs() const {
478   int inline_count = InlineCountField::decode(bit_field_);
479   if (inline_count != kOutlineMarker) {
480     return Inputs(inline_inputs(), inline_count);
481   } else {
482     return Inputs(outline_inputs()->inputs(), outline_inputs()->count_);
483   }
484 }
485 
486 // A forward iterator to visit the edges for the input dependencies of a node.
487 class Node::InputEdges::iterator final {
488  public:
489   using iterator_category = std::forward_iterator_tag;
490   using difference_type = std::ptrdiff_t;
491   using value_type = Edge;
492   using pointer = Edge*;
493   using reference = Edge&;
494 
iterator()495   iterator() : use_(nullptr), input_ptr_(nullptr) {}
496   iterator(const iterator& other) = default;
497 
498   Edge operator*() const { return Edge(use_, input_ptr_); }
499   bool operator==(const iterator& other) const {
500     return input_ptr_ == other.input_ptr_;
501   }
502   bool operator!=(const iterator& other) const { return !(*this == other); }
503   iterator& operator++() {
504     input_ptr_++;
505     use_--;
506     return *this;
507   }
508   iterator operator++(int);
509   iterator& operator+=(difference_type offset) {
510     input_ptr_ += offset;
511     use_ -= offset;
512     return *this;
513   }
514   iterator operator+(difference_type offset) const {
515     return iterator(use_ - offset, input_ptr_ + offset);
516   }
517   difference_type operator-(const iterator& other) const {
518     return input_ptr_ - other.input_ptr_;
519   }
520 
521  private:
522   friend class Node;
523 
iterator(Use * use,ZoneNodePtr * input_ptr)524   explicit iterator(Use* use, ZoneNodePtr* input_ptr)
525       : use_(use), input_ptr_(input_ptr) {}
526 
527   Use* use_;
528   ZoneNodePtr* input_ptr_;
529 };
530 
531 
begin()532 Node::InputEdges::iterator Node::InputEdges::begin() const {
533   return Node::InputEdges::iterator(use_root_, input_root_);
534 }
535 
536 
end()537 Node::InputEdges::iterator Node::InputEdges::end() const {
538   return Node::InputEdges::iterator(use_root_ - count_, input_root_ + count_);
539 }
540 
541 Edge Node::InputEdges::operator[](int index) const {
542   return Edge(use_root_ + index, input_root_ + index);
543 }
544 
545 // A forward iterator to visit the inputs of a node.
546 class Node::Inputs::const_iterator final {
547  public:
548   using iterator_category = std::forward_iterator_tag;
549   using difference_type = std::ptrdiff_t;
550   using value_type = Node*;
551   using pointer = const value_type*;
552   using reference = value_type&;
553 
554   const_iterator(const const_iterator& other) = default;
555 
556   Node* operator*() const { return *input_ptr_; }
557   bool operator==(const const_iterator& other) const {
558     return input_ptr_ == other.input_ptr_;
559   }
560   bool operator!=(const const_iterator& other) const {
561     return !(*this == other);
562   }
563   const_iterator& operator++() {
564     ++input_ptr_;
565     return *this;
566   }
567   const_iterator operator++(int);
568   const_iterator& operator+=(difference_type offset) {
569     input_ptr_ += offset;
570     return *this;
571   }
572   const_iterator operator+(difference_type offset) const {
573     return const_iterator(input_ptr_ + offset);
574   }
575   difference_type operator-(const const_iterator& other) const {
576     return input_ptr_ - other.input_ptr_;
577   }
578 
579  private:
580   friend class Node::Inputs;
581 
const_iterator(ZoneNodePtr const * input_ptr)582   explicit const_iterator(ZoneNodePtr const* input_ptr)
583       : input_ptr_(input_ptr) {}
584 
585   ZoneNodePtr const* input_ptr_;
586 };
587 
588 
begin()589 Node::Inputs::const_iterator Node::Inputs::begin() const {
590   return const_iterator(input_root_);
591 }
592 
593 
end()594 Node::Inputs::const_iterator Node::Inputs::end() const {
595   return const_iterator(input_root_ + count_);
596 }
597 
598 Node* Node::Inputs::operator[](int index) const { return input_root_[index]; }
599 
600 // A forward iterator to visit the uses edges of a node.
601 class Node::UseEdges::iterator final {
602  public:
603   iterator(const iterator& other) = default;
604 
605   Edge operator*() const { return Edge(current_, current_->input_ptr()); }
606   bool operator==(const iterator& other) const {
607     return current_ == other.current_;
608   }
609   bool operator!=(const iterator& other) const { return !(*this == other); }
610   iterator& operator++() {
611     DCHECK_NOT_NULL(current_);
612     current_ = next_;
613     next_ = current_ ? static_cast<Node::Use*>(current_->next) : nullptr;
614     return *this;
615   }
616   iterator operator++(int);
617 
618  private:
619   friend class Node::UseEdges;
620 
iterator()621   iterator() : current_(nullptr), next_(nullptr) {}
iterator(Node * node)622   explicit iterator(Node* node)
623       : current_(node->first_use_),
624         next_(current_ ? static_cast<Node::Use*>(current_->next) : nullptr) {}
625 
626   Node::Use* current_;
627   Node::Use* next_;
628 };
629 
630 
begin()631 Node::UseEdges::iterator Node::UseEdges::begin() const {
632   return Node::UseEdges::iterator(this->node_);
633 }
634 
635 
end()636 Node::UseEdges::iterator Node::UseEdges::end() const {
637   return Node::UseEdges::iterator();
638 }
639 
640 
641 // A forward iterator to visit the uses of a node.
642 class Node::Uses::const_iterator final {
643  public:
644   using iterator_category = std::forward_iterator_tag;
645   using difference_type = int;
646   using value_type = Node*;
647   using pointer = Node**;
648   using reference = Node*&;
649 
650   Node* operator*() const { return current_->from(); }
651   bool operator==(const const_iterator& other) const {
652     return other.current_ == current_;
653   }
654   bool operator!=(const const_iterator& other) const {
655     return other.current_ != current_;
656   }
657   const_iterator& operator++() {
658     DCHECK_NOT_NULL(current_);
659     // Checking no use gets mutated while iterating through them, a potential
660     // very tricky cause of bug.
661     current_ = current_->next;
662 #ifdef DEBUG
663     DCHECK_EQ(current_, next_);
664     next_ = current_ ? current_->next : nullptr;
665 #endif
666     return *this;
667   }
668   const_iterator operator++(int);
669 
670  private:
671   friend class Node::Uses;
672 
const_iterator()673   const_iterator() : current_(nullptr) {}
const_iterator(Node * node)674   explicit const_iterator(Node* node)
675       : current_(node->first_use_)
676 #ifdef DEBUG
677         ,
678         next_(current_ ? current_->next : nullptr)
679 #endif
680   {
681   }
682 
683   Node::Use* current_;
684 #ifdef DEBUG
685   Node::Use* next_;
686 #endif
687 };
688 
689 
begin()690 Node::Uses::const_iterator Node::Uses::begin() const {
691   return const_iterator(this->node_);
692 }
693 
694 
end()695 Node::Uses::const_iterator Node::Uses::end() const { return const_iterator(); }
696 
697 }  // namespace compiler
698 }  // namespace internal
699 }  // namespace v8
700 
701 #endif  // V8_COMPILER_NODE_H_
702