| // 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_ | |
| #include "src/compiler/opcodes.h" | |
| #include "src/compiler/operator.h" | |
| #include "src/compiler/types.h" | |
| #include "src/globals.h" | |
| #include "src/zone/zone-containers.h" | |
| namespace v8 { | |
| namespace internal { | |
| namespace compiler { | |
| // Forward declarations. | |
| class Edge; | |
| class Graph; | |
| // 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. | |
| typedef uint32_t Mark; | |
| // NodeIds are identifying numbers for nodes that can be used to index auxiliary | |
| // out-of-line data associated with each node. | |
| typedef uint32_t NodeId; | |
| // 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. | |
| class V8_EXPORT_PRIVATE Node final { | |
| public: | |
| static Node* New(Zone* zone, NodeId id, const Operator* op, int input_count, | |
| Node* const* inputs, bool has_extensible_inputs); | |
| static Node* Clone(Zone* zone, NodeId id, const Node* node); | |
| inline bool IsDead() const; | |
| void Kill(); | |
| const Operator* op() const { return op_; } | |
| IrOpcode::Value opcode() const { | |
| DCHECK_GE(IrOpcode::kLast, op_->opcode()); | |
| return static_cast<IrOpcode::Value>(op_->opcode()); | |
| } | |
| NodeId id() const { return IdField::decode(bit_field_); } | |
| int InputCount() const { | |
| return has_inline_inputs() ? InlineCountField::decode(bit_field_) | |
| : inputs_.outline_->count_; | |
| } | |
| #ifdef DEBUG | |
| void Verify(); | |
| #define BOUNDS_CHECK(index) \ | |
| do { \ | |
| if (index < 0 || index >= InputCount()) { \ | |
| FATAL("Node #%d:%s->InputAt(%d) out of bounds", id(), op()->mnemonic(), \ | |
| index); \ | |
| } \ | |
| } while (false) | |
| #else | |
| // No bounds checks or verification in release mode. | |
| inline void Verify() {} | |
| #define BOUNDS_CHECK(index) \ | |
| do { \ | |
| } while (false) | |
| #endif | |
| Node* InputAt(int index) const { | |
| BOUNDS_CHECK(index); | |
| return *GetInputPtrConst(index); | |
| } | |
| void ReplaceInput(int index, Node* new_to) { | |
| BOUNDS_CHECK(index); | |
| Node** input_ptr = GetInputPtr(index); | |
| 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); | |
| } | |
| } | |
| #undef BOUNDS_CHECK | |
| void AppendInput(Zone* zone, Node* new_to); | |
| void InsertInput(Zone* zone, int index, Node* new_to); | |
| void InsertInputs(Zone* zone, int index, int count); | |
| void RemoveInput(int index); | |
| void NullAllInputs(); | |
| void TrimInputCount(int new_input_count); | |
| int UseCount() const; | |
| void ReplaceUses(Node* replace_to); | |
| class InputEdges; | |
| inline InputEdges input_edges(); | |
| class Inputs; | |
| inline Inputs inputs() const; | |
| class UseEdges final { | |
| public: | |
| typedef Edge value_type; | |
| class iterator; | |
| inline iterator begin() const; | |
| inline iterator end() const; | |
| bool empty() const; | |
| explicit UseEdges(Node* node) : node_(node) {} | |
| private: | |
| Node* node_; | |
| }; | |
| UseEdges use_edges() { return UseEdges(this); } | |
| class V8_EXPORT_PRIVATE Uses final { | |
| public: | |
| typedef Node* value_type; | |
| class const_iterator; | |
| inline const_iterator begin() const; | |
| inline const_iterator end() const; | |
| bool empty() const; | |
| explicit Uses(Node* node) : node_(node) {} | |
| private: | |
| Node* node_; | |
| }; | |
| Uses uses() { return Uses(this); } | |
| // Returns true if {owner} is the user of {this} node. | |
| bool OwnedBy(Node* owner) const { | |
| return first_use_ && first_use_->from() == owner && !first_use_->next; | |
| } | |
| // Returns true if {owner1} and {owner2} are the only users of {this} node. | |
| bool OwnedBy(Node const* owner1, Node const* owner2) const; | |
| void Print() const; | |
| private: | |
| struct Use; | |
| // Out of line storage for inputs when the number of inputs overflowed the | |
| // capacity of the inline-allocated space. | |
| struct OutOfLineInputs { | |
| Node* node_; | |
| int count_; | |
| int capacity_; | |
| Node* inputs_[1]; | |
| static OutOfLineInputs* New(Zone* zone, int capacity); | |
| void ExtractFrom(Use* use_ptr, Node** input_ptr, int count); | |
| }; | |
| // 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 { | |
| Use* next; | |
| Use* prev; | |
| uint32_t bit_field_; | |
| int input_index() const { return InputIndexField::decode(bit_field_); } | |
| bool is_inline_use() const { return InlineField::decode(bit_field_); } | |
| Node** input_ptr() { | |
| int index = input_index(); | |
| Use* start = this + 1 + index; | |
| Node** inputs = is_inline_use() | |
| ? reinterpret_cast<Node*>(start)->inputs_.inline_ | |
| : reinterpret_cast<OutOfLineInputs*>(start)->inputs_; | |
| return &inputs[index]; | |
| } | |
| Node* from() { | |
| Use* start = this + 1 + input_index(); | |
| return is_inline_use() ? reinterpret_cast<Node*>(start) | |
| : reinterpret_cast<OutOfLineInputs*>(start)->node_; | |
| } | |
| typedef BitField<bool, 0, 1> InlineField; | |
| typedef BitField<unsigned, 1, 17> InputIndexField; | |
| // Leaving some space in the bitset in case we ever decide to record | |
| // the output index. | |
| }; | |
| //============================================================================ | |
| //== 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); | |
| Node* const* GetInputPtrConst(int input_index) const { | |
| return has_inline_inputs() ? &(inputs_.inline_[input_index]) | |
| : &inputs_.outline_->inputs_[input_index]; | |
| } | |
| Node** GetInputPtr(int input_index) { | |
| return has_inline_inputs() ? &(inputs_.inline_[input_index]) | |
| : &inputs_.outline_->inputs_[input_index]; | |
| } | |
| Use* GetUsePtr(int input_index) { | |
| Use* ptr = has_inline_inputs() ? reinterpret_cast<Use*>(this) | |
| : reinterpret_cast<Use*>(inputs_.outline_); | |
| return &ptr[-1 - input_index]; | |
| } | |
| void AppendUse(Use* use); | |
| void RemoveUse(Use* use); | |
| void* operator new(size_t, void* location) { return location; } | |
| // Only NodeProperties should manipulate the op. | |
| void set_op(const Operator* op) { op_ = op; } | |
| // Only NodeProperties should manipulate the type. | |
| Type type() const { return type_; } | |
| void set_type(Type type) { type_ = type; } | |
| // Only NodeMarkers should manipulate the marks on nodes. | |
| Mark mark() const { return mark_; } | |
| void set_mark(Mark mark) { mark_ = mark; } | |
| inline bool has_inline_inputs() const { | |
| return InlineCountField::decode(bit_field_) != kOutlineMarker; | |
| } | |
| void ClearInputs(int start, int count); | |
| typedef BitField<NodeId, 0, 24> IdField; | |
| typedef BitField<unsigned, 24, 4> InlineCountField; | |
| typedef BitField<unsigned, 28, 4> InlineCapacityField; | |
| static const int kOutlineMarker = InlineCountField::kMax; | |
| static const int kMaxInlineCount = InlineCountField::kMax - 1; | |
| static const int kMaxInlineCapacity = InlineCapacityField::kMax - 1; | |
| const Operator* op_; | |
| Type type_; | |
| Mark mark_; | |
| uint32_t bit_field_; | |
| Use* first_use_; | |
| union { | |
| // Inline storage for inputs or out-of-line storage. | |
| Node* inline_[1]; | |
| OutOfLineInputs* outline_; | |
| } inputs_; | |
| friend class Edge; | |
| friend class NodeMarkerBase; | |
| friend class NodeProperties; | |
| DISALLOW_COPY_AND_ASSIGN(Node); | |
| }; | |
| std::ostream& operator<<(std::ostream& os, const Node& n); | |
| // Typedefs to shorten commonly used Node containers. | |
| typedef ZoneDeque<Node*> NodeDeque; | |
| typedef ZoneSet<Node*> NodeSet; | |
| typedef ZoneVector<Node*> NodeVector; | |
| typedef ZoneVector<NodeVector> NodeVectorVector; | |
| class Node::InputEdges final { | |
| public: | |
| typedef Edge value_type; | |
| 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; | |
| InputEdges(Node** input_root, Use* use_root, int count) | |
| : input_root_(input_root), use_root_(use_root), count_(count) {} | |
| private: | |
| Node** input_root_; | |
| Use* use_root_; | |
| int count_; | |
| }; | |
| class V8_EXPORT_PRIVATE Node::Inputs final { | |
| public: | |
| typedef Node* value_type; | |
| 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; | |
| explicit Inputs(Node* const* input_root, int count) | |
| : input_root_(input_root), count_(count) {} | |
| private: | |
| Node* const* input_root_; | |
| int count_; | |
| }; | |
| // 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 | |
| // the node having the input. | |
| class Edge final { | |
| public: | |
| Node* from() const { return use_->from(); } | |
| Node* to() const { return *input_ptr_; } | |
| int index() const { | |
| int const index = use_->input_index(); | |
| DCHECK_LT(index, use_->from()->InputCount()); | |
| return index; | |
| } | |
| bool operator==(const Edge& other) { return input_ptr_ == other.input_ptr_; } | |
| bool operator!=(const Edge& other) { return !(*this == other); } | |
| 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_); | |
| } | |
| } | |
| private: | |
| friend class Node::UseEdges::iterator; | |
| friend class Node::InputEdges; | |
| friend class Node::InputEdges::iterator; | |
| Edge(Node::Use* use, Node** input_ptr) : use_(use), input_ptr_(input_ptr) { | |
| DCHECK_NOT_NULL(use); | |
| DCHECK_NOT_NULL(input_ptr); | |
| DCHECK_EQ(input_ptr, use->input_ptr()); | |
| } | |
| Node::Use* use_; | |
| Node** input_ptr_; | |
| }; | |
| 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) { | |
| return InputEdges(inputs_.inline_, reinterpret_cast<Use*>(this) - 1, | |
| inline_count); | |
| } else { | |
| return InputEdges(inputs_.outline_->inputs_, | |
| reinterpret_cast<Use*>(inputs_.outline_) - 1, | |
| inputs_.outline_->count_); | |
| } | |
| } | |
| Node::Inputs Node::inputs() const { | |
| int inline_count = InlineCountField::decode(bit_field_); | |
| if (inline_count != kOutlineMarker) { | |
| return Inputs(inputs_.inline_, inline_count); | |
| } else { | |
| return Inputs(inputs_.outline_->inputs_, inputs_.outline_->count_); | |
| } | |
| } | |
| // A forward iterator to visit the edges for the input dependencies of a node. | |
| class Node::InputEdges::iterator final { | |
| public: | |
| typedef std::forward_iterator_tag iterator_category; | |
| typedef std::ptrdiff_t difference_type; | |
| typedef Edge value_type; | |
| typedef Edge* pointer; | |
| typedef Edge& reference; | |
| iterator() : use_(nullptr), input_ptr_(nullptr) {} | |
| iterator(const iterator& other) | |
| : use_(other.use_), input_ptr_(other.input_ptr_) {} | |
| Edge operator*() const { return Edge(use_, input_ptr_); } | |
| bool operator==(const iterator& other) const { | |
| return input_ptr_ == other.input_ptr_; | |
| } | |
| bool operator!=(const iterator& other) const { return !(*this == other); } | |
| iterator& operator++() { | |
| input_ptr_++; | |
| use_--; | |
| return *this; | |
| } | |
| iterator operator++(int); | |
| 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 { | |
| return input_ptr_ - other.input_ptr_; | |
| } | |
| private: | |
| friend class Node; | |
| explicit iterator(Use* use, Node** input_ptr) | |
| : use_(use), input_ptr_(input_ptr) {} | |
| Use* use_; | |
| Node** input_ptr_; | |
| }; | |
| Node::InputEdges::iterator Node::InputEdges::begin() const { | |
| return Node::InputEdges::iterator(use_root_, input_root_); | |
| } | |
| Node::InputEdges::iterator Node::InputEdges::end() const { | |
| return Node::InputEdges::iterator(use_root_ - count_, input_root_ + count_); | |
| } | |
| Edge Node::InputEdges::operator[](int index) const { | |
| return Edge(use_root_ + index, input_root_ + index); | |
| } | |
| // A forward iterator to visit the inputs of a node. | |
| class Node::Inputs::const_iterator final { | |
| public: | |
| typedef std::forward_iterator_tag iterator_category; | |
| typedef std::ptrdiff_t difference_type; | |
| typedef Node* value_type; | |
| typedef const value_type* pointer; | |
| typedef value_type& reference; | |
| const_iterator(const const_iterator& other) : input_ptr_(other.input_ptr_) {} | |
| Node* operator*() const { return *input_ptr_; } | |
| bool operator==(const const_iterator& other) const { | |
| return input_ptr_ == other.input_ptr_; | |
| } | |
| bool operator!=(const const_iterator& other) const { | |
| return !(*this == other); | |
| } | |
| const_iterator& operator++() { | |
| ++input_ptr_; | |
| return *this; | |
| } | |
| const_iterator operator++(int); | |
| const_iterator& operator+=(difference_type offset) { | |
| input_ptr_ += offset; | |
| return *this; | |
| } | |
| const_iterator operator+(difference_type offset) const { | |
| return const_iterator(input_ptr_ + offset); | |
| } | |
| difference_type operator-(const const_iterator& other) const { | |
| return input_ptr_ - other.input_ptr_; | |
| } | |
| private: | |
| friend class Node::Inputs; | |
| explicit const_iterator(Node* const* input_ptr) : input_ptr_(input_ptr) {} | |
| Node* const* input_ptr_; | |
| }; | |
| Node::Inputs::const_iterator Node::Inputs::begin() const { | |
| return const_iterator(input_root_); | |
| } | |
| Node::Inputs::const_iterator Node::Inputs::end() const { | |
| return const_iterator(input_root_ + count_); | |
| } | |
| Node* Node::Inputs::operator[](int index) const { return input_root_[index]; } | |
| // A forward iterator to visit the uses edges of a node. | |
| class Node::UseEdges::iterator final { | |
| public: | |
| iterator(const iterator& other) | |
| : current_(other.current_), next_(other.next_) {} | |
| Edge operator*() const { return Edge(current_, current_->input_ptr()); } | |
| bool operator==(const iterator& other) const { | |
| return current_ == other.current_; | |
| } | |
| bool operator!=(const iterator& other) const { return !(*this == other); } | |
| iterator& operator++() { | |
| DCHECK_NOT_NULL(current_); | |
| current_ = next_; | |
| next_ = current_ ? current_->next : nullptr; | |
| return *this; | |
| } | |
| iterator operator++(int); | |
| private: | |
| friend class Node::UseEdges; | |
| iterator() : current_(nullptr), next_(nullptr) {} | |
| explicit iterator(Node* node) | |
| : current_(node->first_use_), | |
| next_(current_ ? current_->next : nullptr) {} | |
| Node::Use* current_; | |
| Node::Use* next_; | |
| }; | |
| Node::UseEdges::iterator Node::UseEdges::begin() const { | |
| return Node::UseEdges::iterator(this->node_); | |
| } | |
| Node::UseEdges::iterator Node::UseEdges::end() const { | |
| return Node::UseEdges::iterator(); | |
| } | |
| // A forward iterator to visit the uses of a node. | |
| class Node::Uses::const_iterator final { | |
| public: | |
| typedef std::forward_iterator_tag iterator_category; | |
| typedef int difference_type; | |
| typedef Node* value_type; | |
| typedef Node** pointer; | |
| typedef Node*& reference; | |
| const_iterator(const const_iterator& other) | |
| : current_(other.current_) | |
| #ifdef DEBUG | |
| , | |
| next_(other.next_) | |
| #endif | |
| { | |
| } | |
| Node* operator*() const { return current_->from(); } | |
| bool operator==(const const_iterator& other) const { | |
| return other.current_ == current_; | |
| } | |
| bool operator!=(const const_iterator& other) const { | |
| return other.current_ != current_; | |
| } | |
| const_iterator& operator++() { | |
| DCHECK_NOT_NULL(current_); | |
| // Checking no use gets mutated while iterating through them, a potential | |
| // very tricky cause of bug. | |
| current_ = current_->next; | |
| #ifdef DEBUG | |
| DCHECK_EQ(current_, next_); | |
| next_ = current_ ? current_->next : nullptr; | |
| #endif | |
| return *this; | |
| } | |
| const_iterator operator++(int); | |
| private: | |
| friend class Node::Uses; | |
| const_iterator() : current_(nullptr) {} | |
| explicit const_iterator(Node* node) | |
| : current_(node->first_use_) | |
| #ifdef DEBUG | |
| , | |
| next_(current_ ? current_->next : nullptr) | |
| #endif | |
| { | |
| } | |
| Node::Use* current_; | |
| #ifdef DEBUG | |
| Node::Use* next_; | |
| #endif | |
| }; | |
| Node::Uses::const_iterator Node::Uses::begin() const { | |
| return const_iterator(this->node_); | |
| } | |
| Node::Uses::const_iterator Node::Uses::end() const { return const_iterator(); } | |
| } // namespace compiler | |
| } // namespace internal | |
| } // namespace v8 | |
| #endif // V8_COMPILER_NODE_H_ |