node.h 20.3 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2013 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_COMPILER_NODE_H_
#define V8_COMPILER_NODE_H_

8
#include "src/common/globals.h"
9
#include "src/compiler/graph-zone-traits.h"
10 11
#include "src/compiler/opcodes.h"
#include "src/compiler/operator.h"
12
#include "src/compiler/types.h"
13
#include "src/zone/zone-containers.h"
14 15 16 17 18

namespace v8 {
namespace internal {
namespace compiler {

19
// Forward declarations.
danno's avatar
danno committed
20
class Edge;
21 22
class Graph;

23

24 25 26
// Marks are used during traversal of the graph to distinguish states of nodes.
// Each node has a mark which is a monotonically increasing integer, and a
// {NodeMarker} has a range of values that indicate states of a node.
27
using Mark = uint32_t;
28

29 30
// NodeIds are identifying numbers for nodes that can be used to index auxiliary
// out-of-line data associated with each node.
31
using NodeId = uint32_t;
32

33 34 35 36 37 38 39 40 41
// A Node is the basic primitive of graphs. Nodes are chained together by
// input/use chains but by default otherwise contain only an identifying number
// which specific applications of graphs and nodes can use to index auxiliary
// out-of-line data, especially transient data.
//
// In addition Nodes only contain a mutable Operator that may change during
// compilation, e.g. during lowering passes. Other information that needs to be
// associated with Nodes during compilation must be stored out-of-line indexed
// 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 46
                   Node* const* inputs, bool has_extensible_inputs);
  static Node* Clone(Zone* zone, NodeId id, const Node* node);
47

48
  inline bool IsDead() const;
49
  void Kill();
50

51 52
  const Operator* op() const { return op_; }

53
  constexpr IrOpcode::Value opcode() const {
54
    DCHECK_GE(IrOpcode::kLast, op_->opcode());
55 56 57
    return static_cast<IrOpcode::Value>(op_->opcode());
  }

58 59 60 61
  NodeId id() const { return IdField::decode(bit_field_); }

  int InputCount() const {
    return has_inline_inputs() ? InlineCountField::decode(bit_field_)
62
                               : outline_inputs()->count_;
63
  }
64

65
#ifdef DEBUG
66 67 68
  void Verify();
#else
  inline void Verify() {}
69
#endif
70 71

  Node* InputAt(int index) const {
72 73
    DCHECK_LE(0, index);
    DCHECK_LT(index, InputCount());
74
    return *GetInputPtrConst(index);
75
  }
76 77

  void ReplaceInput(int index, Node* new_to) {
78 79
    DCHECK_LE(0, index);
    DCHECK_LT(index, InputCount());
80
    ZoneNodePtr* input_ptr = GetInputPtr(index);
81 82 83 84 85 86 87 88 89
    Node* old_to = *input_ptr;
    if (old_to != new_to) {
      Use* use = GetUsePtr(index);
      if (old_to) old_to->RemoveUse(use);
      *input_ptr = new_to;
      if (new_to) new_to->AppendUse(use);
    }
  }

90 91
  void AppendInput(Zone* zone, Node* new_to);
  void InsertInput(Zone* zone, int index, Node* new_to);
92
  void InsertInputs(Zone* zone, int index, int count);
93 94
  // Returns the removed input.
  Node* RemoveInput(int index);
95
  void NullAllInputs();
96
  void TrimInputCount(int new_input_count);
97 98
  // Can trim, extend by appending new inputs, or do nothing.
  void EnsureInputCount(Zone* zone, int new_input_count);
99

100
  int UseCount() const;
101
  void ReplaceUses(Node* replace_to);
102

103 104
  class InputEdges;
  inline InputEdges input_edges();
105

106 107
  class Inputs;
  inline Inputs inputs() const;
danno's avatar
danno committed
108

109
  class UseEdges final {
danno's avatar
danno committed
110
   public:
111
    using value_type = Edge;
112

danno's avatar
danno committed
113
    class iterator;
114 115 116
    inline iterator begin() const;
    inline iterator end() const;

danno's avatar
danno committed
117 118 119 120 121 122 123
    bool empty() const;

    explicit UseEdges(Node* node) : node_(node) {}

   private:
    Node* node_;
  };
124

125 126
  UseEdges use_edges() { return UseEdges(this); }

127
  class V8_EXPORT_PRIVATE Uses final {
128
   public:
129
    using value_type = Node*;
130 131 132 133 134

    class const_iterator;
    inline const_iterator begin() const;
    inline const_iterator end() const;

danno's avatar
danno committed
135
    bool empty() const;
136 137 138 139 140 141 142 143 144

    explicit Uses(Node* node) : node_(node) {}

   private:
    Node* node_;
  };

  Uses uses() { return Uses(this); }

145
  // Returns true if {owner} is the only user of {this} node.
146
  bool OwnedBy(Node const* owner) const;
147

148 149
  // Returns true if {owner1} and {owner2} are the only users of {this} node.
  bool OwnedBy(Node const* owner1, Node const* owner2) const;
150

151 152
  void Print() const { Print(1); }
  void Print(int depth) const;
153
  void Print(std::ostream&, int depth = 1) const;
154

155
 private:
156 157 158 159 160
  template <typename NodePtrT>
  inline static Node* NewImpl(Zone* zone, NodeId id, const Operator* op,
                              int input_count, NodePtrT const* inputs,
                              bool has_extensible_inputs);

161
  struct Use;
162 163
  using ZoneUsePtr = GraphZoneTraits::Ptr<Use>;

164 165 166
  // Out of line storage for inputs when the number of inputs overflowed the
  // capacity of the inline-allocated space.
  struct OutOfLineInputs {
167
    ZoneNodePtr node_;
168 169
    int count_;
    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 180 181

  // A link in the use chain for a node. Every input {i} to a node {n} has an
  // associated {Use} which is linked into the use chain of the {i} node.
  struct Use {
182 183
    ZoneUsePtr next;
    ZoneUsePtr prev;
184 185 186 187
    uint32_t bit_field_;

    int input_index() const { return InputIndexField::decode(bit_field_); }
    bool is_inline_use() const { return InlineField::decode(bit_field_); }
188
    ZoneNodePtr* input_ptr() {
189 190
      int index = input_index();
      Use* start = this + 1 + index;
191 192
      ZoneNodePtr* inputs =
          is_inline_use() ? reinterpret_cast<Node*>(start)->inline_inputs()
193
                          : reinterpret_cast<OutOfLineInputs*>(start)->inputs();
194 195
      return &inputs[index];
    }
196

197 198 199 200 201
    Node* from() {
      Use* start = this + 1 + input_index();
      return is_inline_use() ? reinterpret_cast<Node*>(start)
                             : reinterpret_cast<OutOfLineInputs*>(start)->node_;
    }
202

203 204
    using InlineField = base::BitField<bool, 0, 1>;
    using InputIndexField = base::BitField<unsigned, 1, 31>;
205 206
  };

207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
  //============================================================================
  //== Memory layout ===========================================================
  //============================================================================
  // Saving space for big graphs is important. We use a memory layout trick to
  // be able to map {Node} objects to {Use} objects and vice-versa in a
  // space-efficient manner.
  //
  // {Use} links are laid out in memory directly before a {Node}, followed by
  // direct pointers to input {Nodes}.
  //
  // inline case:
  // |Use #N  |Use #N-1|...|Use #1  |Use #0  |Node xxxx |I#0|I#1|...|I#N-1|I#N|
  //          ^                              ^                  ^
  //          + Use                          + Node             + Input
  //
  // Since every {Use} instance records its {input_index}, pointer arithmetic
  // can compute the {Node}.
  //
  // out-of-line case:
  //     |Node xxxx |
  //     ^       + outline ------------------+
  //     +----------------------------------------+
  //                                         |    |
  //                                         v    | node
  // |Use #N  |Use #N-1|...|Use #1  |Use #0  |OOL xxxxx |I#0|I#1|...|I#N-1|I#N|
  //          ^                                                 ^
  //          + Use                                             + Input
  //
  // Out-of-line storage of input lists is needed if appending an input to
  // a node exceeds the maximum inline capacity.

  Node(NodeId id, const Operator* op, int inline_count, int inline_capacity);
239 240
  Node(const Node&) = delete;
  Node& operator=(const Node&) = delete;
241

242 243
  inline Address inputs_location() const;

244 245
  ZoneNodePtr* inline_inputs() const {
    return reinterpret_cast<ZoneNodePtr*>(inputs_location());
246 247
  }
  OutOfLineInputs* outline_inputs() const {
248
    return *reinterpret_cast<ZoneOutOfLineInputsPtr*>(inputs_location());
249 250
  }
  void set_outline_inputs(OutOfLineInputs* outline) {
251
    *reinterpret_cast<ZoneOutOfLineInputsPtr*>(inputs_location()) = outline;
252 253
  }

254
  ZoneNodePtr const* GetInputPtrConst(int input_index) const {
255 256
    return has_inline_inputs() ? &(inline_inputs()[input_index])
                               : &(outline_inputs()->inputs()[input_index]);
257
  }
258
  ZoneNodePtr* GetInputPtr(int input_index) {
259 260
    return has_inline_inputs() ? &(inline_inputs()[input_index])
                               : &(outline_inputs()->inputs()[input_index]);
261 262 263
  }
  Use* GetUsePtr(int input_index) {
    Use* ptr = has_inline_inputs() ? reinterpret_cast<Use*>(this)
264
                                   : reinterpret_cast<Use*>(outline_inputs());
265
    return &ptr[-1 - input_index];
266 267
  }

268 269
  void AppendUse(Use* use);
  void RemoveUse(Use* use);
270 271 272

  void* operator new(size_t, void* location) { return location; }

273 274 275
  // Only NodeProperties should manipulate the op.
  void set_op(const Operator* op) { op_ = op; }

276
  // Only NodeProperties should manipulate the type.
277 278
  Type type() const { return type_; }
  void set_type(Type type) { type_ = type; }
279 280

  // Only NodeMarkers should manipulate the marks on nodes.
281
  Mark mark() const { return mark_; }
282 283
  void set_mark(Mark mark) { mark_ = mark; }

284 285
  inline bool has_inline_inputs() const {
    return InlineCountField::decode(bit_field_) != kOutlineMarker;
286 287
  }

288
  void ClearInputs(int start, int count);
289

290 291 292
  using IdField = base::BitField<NodeId, 0, 24>;
  using InlineCountField = base::BitField<unsigned, 24, 4>;
  using InlineCapacityField = base::BitField<unsigned, 28, 4>;
293 294
  static const int kOutlineMarker = InlineCountField::kMax;
  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 303 304
  friend class Edge;
  friend class NodeMarkerBase;
  friend class NodeProperties;
305 306
};

307 308 309 310
Address Node::inputs_location() const {
  return reinterpret_cast<Address>(this) + sizeof(Node);
}

311 312 313
ZoneNodePtr* Node::OutOfLineInputs::inputs() {
  return reinterpret_cast<ZoneNodePtr*>(reinterpret_cast<Address>(this) +
                                        sizeof(Node::OutOfLineInputs));
314
}
315

316 317
std::ostream& operator<<(std::ostream& os, const Node& n);

318 319 320 321 322 323 324
// Base class for node wrappers.
class NodeWrapper {
 public:
  explicit constexpr NodeWrapper(Node* node) : node_(node) {}
  operator Node*() const { return node_; }
  Node* operator->() const { return node_; }

325 326 327 328 329 330 331
 protected:
  Node* node() const { return node_; }
  void set_node(Node* node) {
    DCHECK_NOT_NULL(node);
    node_ = node;
  }

332 333 334
 private:
  Node* node_;
};
335

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
// Wrapper classes for special node/edge types (effect, control, frame states).

class Effect : public NodeWrapper {
 public:
  explicit constexpr Effect(Node* node) : NodeWrapper(node) {
    // TODO(jgruber): Remove the End special case.
    SLOW_DCHECK(node == nullptr || node->op()->opcode() == IrOpcode::kEnd ||
                node->op()->EffectOutputCount() > 0);
  }

  // Support the common `Node* x = effect = ...` pattern.
  Node* operator=(Node* value) {
    DCHECK_GT(value->op()->EffectOutputCount(), 0);
    set_node(value);
    return value;
  }
};

class Control : public NodeWrapper {
 public:
  explicit constexpr Control(Node* node) : NodeWrapper(node) {
    // TODO(jgruber): Remove the End special case.
    SLOW_DCHECK(node == nullptr || node->opcode() == IrOpcode::kEnd ||
                node->op()->ControlOutputCount() > 0);
  }

  // Support the common `Node* x = control = ...` pattern.
  Node* operator=(Node* value) {
    DCHECK_GT(value->op()->ControlOutputCount(), 0);
    set_node(value);
    return value;
  }
};

370
// Typedefs to shorten commonly used Node containers.
371 372 373 374
using NodeDeque = ZoneDeque<Node*>;
using NodeSet = ZoneSet<Node*>;
using NodeVector = ZoneVector<Node*>;
using NodeVectorVector = ZoneVector<NodeVector>;
375

376 377
class Node::InputEdges final {
 public:
378
  using value_type = Edge;
379 380 381 382 383 384 385 386 387 388

  class iterator;
  inline iterator begin() const;
  inline iterator end() const;

  bool empty() const { return count_ == 0; }
  int count() const { return count_; }

  inline value_type operator[](int index) const;

389
  InputEdges(ZoneNodePtr* input_root, Use* use_root, int count)
390 391 392
      : input_root_(input_root), use_root_(use_root), count_(count) {}

 private:
393
  ZoneNodePtr* input_root_;
394 395 396 397 398 399
  Use* use_root_;
  int count_;
};

class V8_EXPORT_PRIVATE Node::Inputs final {
 public:
400
  using value_type = Node*;
401 402 403 404 405 406 407 408 409 410

  class const_iterator;
  inline const_iterator begin() const;
  inline const_iterator end() const;

  bool empty() const { return count_ == 0; }
  int count() const { return count_; }

  inline value_type operator[](int index) const;

411
  explicit Inputs(ZoneNodePtr const* input_root, int count)
412 413 414
      : input_root_(input_root), count_(count) {}

 private:
415
  ZoneNodePtr const* input_root_;
416 417
  int count_;
};
418

419 420
// An encapsulation for information associated with a single use of node as a
// input from another node, allowing access to both the defining node and
danno's avatar
danno committed
421
// the node having the input.
422
class Edge final {
423
 public:
424 425
  Node* from() const { return use_->from(); }
  Node* to() const { return *input_ptr_; }
426
  int index() const {
427 428
    int const index = use_->input_index();
    DCHECK_LT(index, use_->from()->InputCount());
429 430 431
    return index;
  }

432
  bool operator==(const Edge& other) { return input_ptr_ == other.input_ptr_; }
danno's avatar
danno committed
433 434
  bool operator!=(const Edge& other) { return !(*this == other); }

435 436 437 438 439 440 441 442
  void UpdateTo(Node* new_to) {
    Node* old_to = *input_ptr_;
    if (old_to != new_to) {
      if (old_to) old_to->RemoveUse(use_);
      *input_ptr_ = new_to;
      if (new_to) new_to->AppendUse(use_);
    }
  }
danno's avatar
danno committed
443

444
 private:
danno's avatar
danno committed
445
  friend class Node::UseEdges::iterator;
446
  friend class Node::InputEdges;
danno's avatar
danno committed
447
  friend class Node::InputEdges::iterator;
448

449 450
  Edge(Node::Use* use, ZoneNodePtr* input_ptr)
      : use_(use), input_ptr_(input_ptr) {
451 452 453 454
    DCHECK_NOT_NULL(use);
    DCHECK_NOT_NULL(input_ptr);
    DCHECK_EQ(input_ptr, use->input_ptr());
  }
455

456
  Node::Use* use_;
457
  ZoneNodePtr* input_ptr_;
458 459
};

460 461 462 463 464 465 466 467
bool Node::IsDead() const {
  Node::Inputs inputs = this->inputs();
  return inputs.count() > 0 && inputs[0] == nullptr;
}

Node::InputEdges Node::input_edges() {
  int inline_count = InlineCountField::decode(bit_field_);
  if (inline_count != kOutlineMarker) {
468
    return InputEdges(inline_inputs(), reinterpret_cast<Use*>(this) - 1,
469 470
                      inline_count);
  } else {
471 472 473
    return InputEdges(outline_inputs()->inputs(),
                      reinterpret_cast<Use*>(outline_inputs()) - 1,
                      outline_inputs()->count_);
474 475 476 477 478 479
  }
}

Node::Inputs Node::inputs() const {
  int inline_count = InlineCountField::decode(bit_field_);
  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 {
danno's avatar
danno committed
488
 public:
489 490 491 492 493
  using iterator_category = std::forward_iterator_tag;
  using difference_type = std::ptrdiff_t;
  using value_type = Edge;
  using pointer = Edge*;
  using reference = Edge&;
494

495
  iterator() : use_(nullptr), input_ptr_(nullptr) {}
496
  iterator(const iterator& other) = default;
danno's avatar
danno committed
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); }
danno's avatar
danno committed
503
  iterator& operator++() {
504 505
    input_ptr_++;
    use_--;
danno's avatar
danno committed
506 507
    return *this;
  }
508
  iterator operator++(int);
509 510 511 512 513 514 515 516 517
  iterator& operator+=(difference_type offset) {
    input_ptr_ += offset;
    use_ -= offset;
    return *this;
  }
  iterator operator+(difference_type offset) const {
    return iterator(use_ - offset, input_ptr_ + offset);
  }
  difference_type operator-(const iterator& other) const {
518
    return input_ptr_ - other.input_ptr_;
519
  }
danno's avatar
danno committed
520 521 522 523

 private:
  friend class Node;

524
  explicit iterator(Use* use, ZoneNodePtr* input_ptr)
525 526
      : use_(use), input_ptr_(input_ptr) {}

527
  Use* use_;
528
  ZoneNodePtr* input_ptr_;
danno's avatar
danno committed
529 530 531
};


532
Node::InputEdges::iterator Node::InputEdges::begin() const {
533
  return Node::InputEdges::iterator(use_root_, input_root_);
534 535 536 537
}


Node::InputEdges::iterator Node::InputEdges::end() const {
538
  return Node::InputEdges::iterator(use_root_ - count_, input_root_ + count_);
539 540
}

541 542 543
Edge Node::InputEdges::operator[](int index) const {
  return Edge(use_root_ + index, input_root_ + index);
}
544

danno's avatar
danno committed
545
// A forward iterator to visit the inputs of a node.
546
class Node::Inputs::const_iterator final {
547
 public:
548 549 550 551 552
  using iterator_category = std::forward_iterator_tag;
  using difference_type = std::ptrdiff_t;
  using value_type = Node*;
  using pointer = const value_type*;
  using reference = value_type&;
danno's avatar
danno committed
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 561 562 563
  }
  bool operator!=(const const_iterator& other) const {
    return !(*this == other);
  }
  const_iterator& operator++() {
564
    ++input_ptr_;
565 566
    return *this;
  }
567
  const_iterator operator++(int);
568
  const_iterator& operator+=(difference_type offset) {
569
    input_ptr_ += offset;
570 571 572
    return *this;
  }
  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
  }
danno's avatar
danno committed
578 579 580 581

 private:
  friend class Node::Inputs;

582 583
  explicit const_iterator(ZoneNodePtr const* input_ptr)
      : input_ptr_(input_ptr) {}
584

585
  ZoneNodePtr const* input_ptr_;
danno's avatar
danno committed
586 587
};

588 589

Node::Inputs::const_iterator Node::Inputs::begin() const {
590
  return const_iterator(input_root_);
591 592 593 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 {
danno's avatar
danno committed
602
 public:
603
  iterator(const iterator& other) = default;
danno's avatar
danno committed
604

605
  Edge operator*() const { return Edge(current_, current_->input_ptr()); }
606 607 608 609
  bool operator==(const iterator& other) const {
    return current_ == other.current_;
  }
  bool operator!=(const iterator& other) const { return !(*this == other); }
danno's avatar
danno committed
610
  iterator& operator++() {
611
    DCHECK_NOT_NULL(current_);
danno's avatar
danno committed
612
    current_ = next_;
613
    next_ = current_ ? static_cast<Node::Use*>(current_->next) : nullptr;
614 615
    return *this;
  }
616
  iterator operator++(int);
617 618

 private:
danno's avatar
danno committed
619 620
  friend class Node::UseEdges;

621
  iterator() : current_(nullptr), next_(nullptr) {}
danno's avatar
danno committed
622 623
  explicit iterator(Node* node)
      : current_(node->first_use_),
624
        next_(current_ ? static_cast<Node::Use*>(current_->next) : nullptr) {}
625

danno's avatar
danno committed
626 627
  Node::Use* current_;
  Node::Use* next_;
628 629
};

630

631
Node::UseEdges::iterator Node::UseEdges::begin() const {
danno's avatar
danno committed
632 633 634 635
  return Node::UseEdges::iterator(this->node_);
}


636 637
Node::UseEdges::iterator Node::UseEdges::end() const {
  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 645 646 647 648
  using iterator_category = std::forward_iterator_tag;
  using difference_type = int;
  using value_type = Node*;
  using pointer = Node**;
  using reference = Node*&;
649

650
  Node* operator*() const { return current_->from(); }
651 652
  bool operator==(const const_iterator& other) const {
    return other.current_ == current_;
653
  }
654 655
  bool operator!=(const const_iterator& other) const {
    return other.current_ != current_;
656
  }
657 658
  const_iterator& operator++() {
    DCHECK_NOT_NULL(current_);
659 660
    // Checking no use gets mutated while iterating through them, a potential
    // very tricky cause of bug.
661
    current_ = current_->next;
662 663 664 665
#ifdef DEBUG
    DCHECK_EQ(current_, next_);
    next_ = current_ ? current_->next : nullptr;
#endif
666
    return *this;
667
  }
668
  const_iterator operator++(int);
669

670 671
 private:
  friend class Node::Uses;
672

673
  const_iterator() : current_(nullptr) {}
674 675 676 677 678 679 680 681
  explicit const_iterator(Node* node)
      : current_(node->first_use_)
#ifdef DEBUG
        ,
        next_(current_ ? current_->next : nullptr)
#endif
  {
  }
682

683
  Node::Use* current_;
684 685 686
#ifdef DEBUG
  Node::Use* next_;
#endif
687
};
688 689


690 691
Node::Uses::const_iterator Node::Uses::begin() const {
  return const_iterator(this->node_);
692 693 694
}


695
Node::Uses::const_iterator Node::Uses::end() const { return const_iterator(); }
696

697 698 699
}  // namespace compiler
}  // namespace internal
}  // namespace v8
700 701

#endif  // V8_COMPILER_NODE_H_