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 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 CHECK_LE(0, index);
73 CHECK_LT(index, InputCount());
74 return *GetInputPtrConst(index);
75 }
76
ReplaceInput(int index,Node * new_to)77 void ReplaceInput(int index, Node* new_to) {
78 CHECK_LE(0, index);
79 CHECK_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 class FrameState : public NodeWrapper {
371 public:
FrameState(Node * node)372 explicit constexpr FrameState(Node* node) : NodeWrapper(node) {
373 // TODO(jgruber): Disallow kStart (needed for PromiseConstructorBasic unit
374 // test, among others).
375 SLOW_DCHECK(node->opcode() == IrOpcode::kFrameState ||
376 node->opcode() == IrOpcode::kStart);
377 }
378
379 // Duplicating here from frame-states.h for ease of access and to keep
380 // header include-balls small. Equality of the two constants is
381 // static-asserted elsewhere.
382 static constexpr int kFrameStateOuterStateInput = 5;
383
outer_frame_state()384 FrameState outer_frame_state() const {
385 return FrameState{node()->InputAt(kFrameStateOuterStateInput)};
386 }
387 };
388
389 // Typedefs to shorten commonly used Node containers.
390 using NodeDeque = ZoneDeque<Node*>;
391 using NodeSet = ZoneSet<Node*>;
392 using NodeVector = ZoneVector<Node*>;
393 using NodeVectorVector = ZoneVector<NodeVector>;
394
395 class Node::InputEdges final {
396 public:
397 using value_type = Edge;
398
399 class iterator;
400 inline iterator begin() const;
401 inline iterator end() const;
402
empty()403 bool empty() const { return count_ == 0; }
count()404 int count() const { return count_; }
405
406 inline value_type operator[](int index) const;
407
InputEdges(ZoneNodePtr * input_root,Use * use_root,int count)408 InputEdges(ZoneNodePtr* input_root, Use* use_root, int count)
409 : input_root_(input_root), use_root_(use_root), count_(count) {}
410
411 private:
412 ZoneNodePtr* input_root_;
413 Use* use_root_;
414 int count_;
415 };
416
417 class V8_EXPORT_PRIVATE Node::Inputs final {
418 public:
419 using value_type = Node*;
420
421 class const_iterator;
422 inline const_iterator begin() const;
423 inline const_iterator end() const;
424
empty()425 bool empty() const { return count_ == 0; }
count()426 int count() const { return count_; }
427
428 inline value_type operator[](int index) const;
429
Inputs(ZoneNodePtr const * input_root,int count)430 explicit Inputs(ZoneNodePtr const* input_root, int count)
431 : input_root_(input_root), count_(count) {}
432
433 private:
434 ZoneNodePtr const* input_root_;
435 int count_;
436 };
437
438 // An encapsulation for information associated with a single use of node as a
439 // input from another node, allowing access to both the defining node and
440 // the node having the input.
441 class Edge final {
442 public:
from()443 Node* from() const { return use_->from(); }
to()444 Node* to() const { return *input_ptr_; }
index()445 int index() const {
446 int const index = use_->input_index();
447 DCHECK_LT(index, use_->from()->InputCount());
448 return index;
449 }
450
451 bool operator==(const Edge& other) { return input_ptr_ == other.input_ptr_; }
452 bool operator!=(const Edge& other) { return !(*this == other); }
453
UpdateTo(Node * new_to)454 void UpdateTo(Node* new_to) {
455 Node* old_to = *input_ptr_;
456 if (old_to != new_to) {
457 if (old_to) old_to->RemoveUse(use_);
458 *input_ptr_ = new_to;
459 if (new_to) new_to->AppendUse(use_);
460 }
461 }
462
463 private:
464 friend class Node::UseEdges::iterator;
465 friend class Node::InputEdges;
466 friend class Node::InputEdges::iterator;
467
Edge(Node::Use * use,ZoneNodePtr * input_ptr)468 Edge(Node::Use* use, ZoneNodePtr* input_ptr)
469 : use_(use), input_ptr_(input_ptr) {
470 DCHECK_NOT_NULL(use);
471 DCHECK_NOT_NULL(input_ptr);
472 DCHECK_EQ(input_ptr, use->input_ptr());
473 }
474
475 Node::Use* use_;
476 ZoneNodePtr* input_ptr_;
477 };
478
IsDead()479 bool Node::IsDead() const {
480 Node::Inputs inputs = this->inputs();
481 return inputs.count() > 0 && inputs[0] == nullptr;
482 }
483
input_edges()484 Node::InputEdges Node::input_edges() {
485 int inline_count = InlineCountField::decode(bit_field_);
486 if (inline_count != kOutlineMarker) {
487 return InputEdges(inline_inputs(), reinterpret_cast<Use*>(this) - 1,
488 inline_count);
489 } else {
490 return InputEdges(outline_inputs()->inputs(),
491 reinterpret_cast<Use*>(outline_inputs()) - 1,
492 outline_inputs()->count_);
493 }
494 }
495
inputs()496 Node::Inputs Node::inputs() const {
497 int inline_count = InlineCountField::decode(bit_field_);
498 if (inline_count != kOutlineMarker) {
499 return Inputs(inline_inputs(), inline_count);
500 } else {
501 return Inputs(outline_inputs()->inputs(), outline_inputs()->count_);
502 }
503 }
504
505 // A forward iterator to visit the edges for the input dependencies of a node.
506 class Node::InputEdges::iterator final {
507 public:
508 using iterator_category = std::forward_iterator_tag;
509 using difference_type = std::ptrdiff_t;
510 using value_type = Edge;
511 using pointer = Edge*;
512 using reference = Edge&;
513
iterator()514 iterator() : use_(nullptr), input_ptr_(nullptr) {}
515 iterator(const iterator& other) = default;
516
517 Edge operator*() const { return Edge(use_, input_ptr_); }
518 bool operator==(const iterator& other) const {
519 return input_ptr_ == other.input_ptr_;
520 }
521 bool operator!=(const iterator& other) const { return !(*this == other); }
522 iterator& operator++() {
523 input_ptr_++;
524 use_--;
525 return *this;
526 }
527 iterator operator++(int);
528 iterator& operator+=(difference_type offset) {
529 input_ptr_ += offset;
530 use_ -= offset;
531 return *this;
532 }
533 iterator operator+(difference_type offset) const {
534 return iterator(use_ - offset, input_ptr_ + offset);
535 }
536 difference_type operator-(const iterator& other) const {
537 return input_ptr_ - other.input_ptr_;
538 }
539
540 private:
541 friend class Node;
542
iterator(Use * use,ZoneNodePtr * input_ptr)543 explicit iterator(Use* use, ZoneNodePtr* input_ptr)
544 : use_(use), input_ptr_(input_ptr) {}
545
546 Use* use_;
547 ZoneNodePtr* input_ptr_;
548 };
549
550
begin()551 Node::InputEdges::iterator Node::InputEdges::begin() const {
552 return Node::InputEdges::iterator(use_root_, input_root_);
553 }
554
555
end()556 Node::InputEdges::iterator Node::InputEdges::end() const {
557 return Node::InputEdges::iterator(use_root_ - count_, input_root_ + count_);
558 }
559
560 Edge Node::InputEdges::operator[](int index) const {
561 return Edge(use_root_ + index, input_root_ + index);
562 }
563
564 // A forward iterator to visit the inputs of a node.
565 class Node::Inputs::const_iterator final {
566 public:
567 using iterator_category = std::forward_iterator_tag;
568 using difference_type = std::ptrdiff_t;
569 using value_type = Node*;
570 using pointer = const value_type*;
571 using reference = value_type&;
572
573 const_iterator(const const_iterator& other) = default;
574
575 Node* operator*() const { return *input_ptr_; }
576 bool operator==(const const_iterator& other) const {
577 return input_ptr_ == other.input_ptr_;
578 }
579 bool operator!=(const const_iterator& other) const {
580 return !(*this == other);
581 }
582 const_iterator& operator++() {
583 ++input_ptr_;
584 return *this;
585 }
586 const_iterator operator++(int);
587 const_iterator& operator+=(difference_type offset) {
588 input_ptr_ += offset;
589 return *this;
590 }
591 const_iterator operator+(difference_type offset) const {
592 return const_iterator(input_ptr_ + offset);
593 }
594 difference_type operator-(const const_iterator& other) const {
595 return input_ptr_ - other.input_ptr_;
596 }
597
598 private:
599 friend class Node::Inputs;
600
const_iterator(ZoneNodePtr const * input_ptr)601 explicit const_iterator(ZoneNodePtr const* input_ptr)
602 : input_ptr_(input_ptr) {}
603
604 ZoneNodePtr const* input_ptr_;
605 };
606
607
begin()608 Node::Inputs::const_iterator Node::Inputs::begin() const {
609 return const_iterator(input_root_);
610 }
611
612
end()613 Node::Inputs::const_iterator Node::Inputs::end() const {
614 return const_iterator(input_root_ + count_);
615 }
616
617 Node* Node::Inputs::operator[](int index) const { return input_root_[index]; }
618
619 // A forward iterator to visit the uses edges of a node.
620 class Node::UseEdges::iterator final {
621 public:
622 iterator(const iterator& other) = default;
623
624 Edge operator*() const { return Edge(current_, current_->input_ptr()); }
625 bool operator==(const iterator& other) const {
626 return current_ == other.current_;
627 }
628 bool operator!=(const iterator& other) const { return !(*this == other); }
629 iterator& operator++() {
630 DCHECK_NOT_NULL(current_);
631 current_ = next_;
632 next_ = current_ ? static_cast<Node::Use*>(current_->next) : nullptr;
633 return *this;
634 }
635 iterator operator++(int);
636
637 private:
638 friend class Node::UseEdges;
639
iterator()640 iterator() : current_(nullptr), next_(nullptr) {}
iterator(Node * node)641 explicit iterator(Node* node)
642 : current_(node->first_use_),
643 next_(current_ ? static_cast<Node::Use*>(current_->next) : nullptr) {}
644
645 Node::Use* current_;
646 Node::Use* next_;
647 };
648
649
begin()650 Node::UseEdges::iterator Node::UseEdges::begin() const {
651 return Node::UseEdges::iterator(this->node_);
652 }
653
654
end()655 Node::UseEdges::iterator Node::UseEdges::end() const {
656 return Node::UseEdges::iterator();
657 }
658
659
660 // A forward iterator to visit the uses of a node.
661 class Node::Uses::const_iterator final {
662 public:
663 using iterator_category = std::forward_iterator_tag;
664 using difference_type = int;
665 using value_type = Node*;
666 using pointer = Node**;
667 using reference = Node*&;
668
669 Node* operator*() const { return current_->from(); }
670 bool operator==(const const_iterator& other) const {
671 return other.current_ == current_;
672 }
673 bool operator!=(const const_iterator& other) const {
674 return other.current_ != current_;
675 }
676 const_iterator& operator++() {
677 DCHECK_NOT_NULL(current_);
678 // Checking no use gets mutated while iterating through them, a potential
679 // very tricky cause of bug.
680 current_ = current_->next;
681 #ifdef DEBUG
682 DCHECK_EQ(current_, next_);
683 next_ = current_ ? current_->next : nullptr;
684 #endif
685 return *this;
686 }
687 const_iterator operator++(int);
688
689 private:
690 friend class Node::Uses;
691
const_iterator()692 const_iterator() : current_(nullptr) {}
const_iterator(Node * node)693 explicit const_iterator(Node* node)
694 : current_(node->first_use_)
695 #ifdef DEBUG
696 ,
697 next_(current_ ? current_->next : nullptr)
698 #endif
699 {
700 }
701
702 Node::Use* current_;
703 #ifdef DEBUG
704 Node::Use* next_;
705 #endif
706 };
707
708
begin()709 Node::Uses::const_iterator Node::Uses::begin() const {
710 return const_iterator(this->node_);
711 }
712
713
end()714 Node::Uses::const_iterator Node::Uses::end() const { return const_iterator(); }
715
716 } // namespace compiler
717 } // namespace internal
718 } // namespace v8
719
720 #endif // V8_COMPILER_NODE_H_
721