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