| Index: src/compiler/node.h
|
| diff --git a/src/compiler/node.h b/src/compiler/node.h
|
| index 1731f866b51597a0b1c576d5f0c2eb5f156ad6e2..1df1fb66312ec038245c0ef206dd411d91a1d048 100644
|
| --- a/src/compiler/node.h
|
| +++ b/src/compiler/node.h
|
| @@ -9,18 +9,22 @@
|
| #include <set>
|
| #include <vector>
|
|
|
| +#include "src/v8.h"
|
| +
|
| #include "src/compiler/generic-algorithm.h"
|
| -#include "src/compiler/generic-node.h"
|
| #include "src/compiler/opcodes.h"
|
| #include "src/compiler/operator.h"
|
| #include "src/types.h"
|
| #include "src/zone.h"
|
| #include "src/zone-allocator.h"
|
| +#include "src/zone-containers.h"
|
|
|
| namespace v8 {
|
| namespace internal {
|
| namespace compiler {
|
|
|
| +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.
|
| @@ -54,16 +58,19 @@ class NodeData {
|
| void set_mark(Mark mark) { mark_ = mark; }
|
| };
|
|
|
| -// A Node is the basic primitive of an IR graph. In addition to the members
|
| -// inherited from Vector, 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 Node FINAL : public GenericNode<NodeData, Node> {
|
| - public:
|
| - Node(Graph* graph, int input_count, int reserve_input_count)
|
| - : GenericNode<NodeData, Node>(graph, input_count, reserve_input_count) {}
|
| +typedef int 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 Node FINAL : public NodeData {
|
| + public:
|
| void Initialize(const Operator* op) {
|
| set_op(op);
|
| set_mark(0);
|
| @@ -74,11 +81,237 @@ class Node FINAL : public GenericNode<NodeData, Node> {
|
|
|
| void CollectProjections(ZoneVector<Node*>* projections);
|
| Node* FindProjection(size_t projection_index);
|
| +
|
| + inline NodeId id() const { return id_; }
|
| +
|
| + int InputCount() const { return input_count_; }
|
| + Node* InputAt(int index) const { return GetInputRecordPtr(index)->to; }
|
| + inline void ReplaceInput(int index, Node* new_input);
|
| + inline void AppendInput(Zone* zone, Node* new_input);
|
| + inline void InsertInput(Zone* zone, int index, Node* new_input);
|
| + inline void RemoveInput(int index);
|
| +
|
| + int UseCount() { return use_count_; }
|
| + Node* UseAt(int index) {
|
| + DCHECK(index < use_count_);
|
| + Use* current = first_use_;
|
| + while (index-- != 0) {
|
| + current = current->next;
|
| + }
|
| + return current->from;
|
| + }
|
| + inline void ReplaceUses(Node* replace_to);
|
| + template <class UnaryPredicate>
|
| + inline void ReplaceUsesIf(UnaryPredicate pred, Node* replace_to);
|
| + inline void RemoveAllInputs();
|
| +
|
| + inline void TrimInputCount(int input_count);
|
| +
|
| + class Inputs {
|
| + public:
|
| + class iterator;
|
| + iterator begin();
|
| + iterator end();
|
| +
|
| + explicit Inputs(Node* node) : node_(node) {}
|
| +
|
| + private:
|
| + Node* node_;
|
| + };
|
| +
|
| + Inputs inputs() { return Inputs(this); }
|
| +
|
| + class Uses {
|
| + public:
|
| + class iterator;
|
| + iterator begin();
|
| + iterator end();
|
| + bool empty();
|
| +
|
| + explicit Uses(Node* node) : node_(node) {}
|
| +
|
| + private:
|
| + Node* node_;
|
| + };
|
| +
|
| + Uses uses() { return Uses(this); }
|
| +
|
| + class Edge;
|
| +
|
| + bool OwnedBy(Node* owner) const;
|
| +
|
| + static Node* New(Graph* graph, int input_count, Node** inputs,
|
| + bool has_extensible_inputs);
|
| +
|
| + protected:
|
| + friend class Graph;
|
| +
|
| + class Use : public ZoneObject {
|
| + public:
|
| + Node* from;
|
| + Use* next;
|
| + Use* prev;
|
| + int input_index;
|
| + };
|
| +
|
| + class Input {
|
| + public:
|
| + Node* to;
|
| + Use* use;
|
| +
|
| + void Update(Node* new_to);
|
| + };
|
| +
|
| + void EnsureAppendableInputs(Zone* zone);
|
| +
|
| + Input* GetInputRecordPtr(int index) const {
|
| + if (has_appendable_inputs_) {
|
| + return &((*inputs_.appendable_)[index]);
|
| + } else {
|
| + return inputs_.static_ + index;
|
| + }
|
| + }
|
| +
|
| + inline void AppendUse(Use* use);
|
| + inline void RemoveUse(Use* use);
|
| +
|
| + void* operator new(size_t, void* location) { return location; }
|
| +
|
| + private:
|
| + Node(Graph* graph, int input_count, int reserve_input_count);
|
| +
|
| + typedef ZoneDeque<Input> InputDeque;
|
| +
|
| + static const int kReservedInputCountBits = 2;
|
| + static const int kMaxReservedInputs = (1 << kReservedInputCountBits) - 1;
|
| + static const int kDefaultReservedInputs = kMaxReservedInputs;
|
| +
|
| + NodeId id_;
|
| + int input_count_ : 29;
|
| + unsigned int reserve_input_count_ : kReservedInputCountBits;
|
| + bool has_appendable_inputs_ : 1;
|
| + union {
|
| + // When a node is initially allocated, it uses a static buffer to hold its
|
| + // inputs under the assumption that the number of outputs will not increase.
|
| + // When the first input is appended, the static buffer is converted into a
|
| + // deque to allow for space-efficient growing.
|
| + Input* static_;
|
| + InputDeque* appendable_;
|
| + } inputs_;
|
| + int use_count_;
|
| + Use* first_use_;
|
| + Use* last_use_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(Node);
|
| +};
|
| +
|
| +// 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 ndoe having the input.
|
| +class Node::Edge {
|
| + public:
|
| + Node* from() const { return input_->use->from; }
|
| + Node* to() const { return input_->to; }
|
| + int index() const {
|
| + int index = input_->use->input_index;
|
| + DCHECK(index < input_->use->from->input_count_);
|
| + return index;
|
| + }
|
| +
|
| + private:
|
| + friend class Node::Uses::iterator;
|
| + friend class Node::Inputs::iterator;
|
| +
|
| + explicit Edge(Node::Input* input) : input_(input) {}
|
| +
|
| + Node::Input* input_;
|
| +};
|
| +
|
| +// A forward iterator to visit the nodes which are depended upon by a node
|
| +// in the order of input.
|
| +class Node::Inputs::iterator {
|
| + public:
|
| + iterator(const Node::Inputs::iterator& other) // NOLINT
|
| + : node_(other.node_),
|
| + index_(other.index_) {}
|
| +
|
| + Node* operator*() { return GetInput()->to; }
|
| + Node::Edge edge() { return Node::Edge(GetInput()); }
|
| + bool operator==(const iterator& other) const {
|
| + return other.index_ == index_ && other.node_ == node_;
|
| + }
|
| + bool operator!=(const iterator& other) const { return !(other == *this); }
|
| + iterator& operator++() {
|
| + DCHECK(node_ != NULL);
|
| + DCHECK(index_ < node_->input_count_);
|
| + ++index_;
|
| + return *this;
|
| + }
|
| + iterator& UpdateToAndIncrement(Node* new_to) {
|
| + Node::Input* input = GetInput();
|
| + input->Update(new_to);
|
| + index_++;
|
| + return *this;
|
| + }
|
| + int index() { return index_; }
|
| +
|
| + private:
|
| + friend class Node;
|
| +
|
| + explicit iterator(Node* node, int index) : node_(node), index_(index) {}
|
| +
|
| + Input* GetInput() const { return node_->GetInputRecordPtr(index_); }
|
| +
|
| + Node* node_;
|
| + int index_;
|
| +};
|
| +
|
| +// A forward iterator to visit the uses of a node. The uses are returned in
|
| +// the order in which they were added as inputs.
|
| +class Node::Uses::iterator {
|
| + public:
|
| + iterator(const Node::Uses::iterator& other) // NOLINT
|
| + : current_(other.current_),
|
| + index_(other.index_) {}
|
| +
|
| + Node* operator*() { return current_->from; }
|
| + Node::Edge edge() { return Node::Edge(CurrentInput()); }
|
| +
|
| + bool operator==(const iterator& other) { return other.current_ == current_; }
|
| + bool operator!=(const iterator& other) { return other.current_ != current_; }
|
| + iterator& operator++() {
|
| + DCHECK(current_ != NULL);
|
| + index_++;
|
| + current_ = current_->next;
|
| + return *this;
|
| + }
|
| + iterator& UpdateToAndIncrement(Node* new_to) {
|
| + DCHECK(current_ != NULL);
|
| + index_++;
|
| + Node::Input* input = CurrentInput();
|
| + current_ = current_->next;
|
| + input->Update(new_to);
|
| + return *this;
|
| + }
|
| + int index() const { return index_; }
|
| +
|
| + private:
|
| + friend class Node::Uses;
|
| +
|
| + iterator() : current_(NULL), index_(0) {}
|
| + explicit iterator(Node* node) : current_(node->first_use_), index_(0) {}
|
| +
|
| + Input* CurrentInput() const {
|
| + return current_->from->GetInputRecordPtr(current_->input_index);
|
| + }
|
| +
|
| + Node::Use* current_;
|
| + int index_;
|
| };
|
|
|
| std::ostream& operator<<(std::ostream& os, const Node& n);
|
|
|
| -typedef GenericGraphVisit::NullNodeVisitor<NodeData, Node> NullNodeVisitor;
|
| +typedef GenericGraphVisit::NullNodeVisitor NullNodeVisitor;
|
|
|
| typedef std::set<Node*, std::less<Node*>, zone_allocator<Node*> > NodeSet;
|
| typedef NodeSet::iterator NodeSetIter;
|
| @@ -104,6 +337,179 @@ static inline const T& OpParameter(const Node* node) {
|
| return OpParameter<T>(node->op());
|
| }
|
|
|
| +inline Node::Inputs::iterator Node::Inputs::begin() {
|
| + return Node::Inputs::iterator(this->node_, 0);
|
| +}
|
| +
|
| +inline Node::Inputs::iterator Node::Inputs::end() {
|
| + return Node::Inputs::iterator(this->node_, this->node_->InputCount());
|
| +}
|
| +
|
| +inline Node::Uses::iterator Node::Uses::begin() {
|
| + return Node::Uses::iterator(this->node_);
|
| +}
|
| +
|
| +inline Node::Uses::iterator Node::Uses::end() { return Node::Uses::iterator(); }
|
| +
|
| +inline bool Node::Uses::empty() { return begin() == end(); }
|
| +
|
| +inline void Node::ReplaceUses(Node* replace_to) {
|
| + for (Use* use = first_use_; use != NULL; use = use->next) {
|
| + use->from->GetInputRecordPtr(use->input_index)->to = replace_to;
|
| + }
|
| + if (replace_to->last_use_ == NULL) {
|
| + DCHECK_EQ(NULL, replace_to->first_use_);
|
| + replace_to->first_use_ = first_use_;
|
| + replace_to->last_use_ = last_use_;
|
| + } else if (first_use_ != NULL) {
|
| + DCHECK_NE(NULL, replace_to->first_use_);
|
| + replace_to->last_use_->next = first_use_;
|
| + first_use_->prev = replace_to->last_use_;
|
| + replace_to->last_use_ = last_use_;
|
| + }
|
| + replace_to->use_count_ += use_count_;
|
| + use_count_ = 0;
|
| + first_use_ = NULL;
|
| + last_use_ = NULL;
|
| +}
|
| +
|
| +template <class UnaryPredicate>
|
| +inline void Node::ReplaceUsesIf(UnaryPredicate pred, Node* replace_to) {
|
| + for (Use* use = first_use_; use != NULL;) {
|
| + Use* next = use->next;
|
| + if (pred(use->from)) {
|
| + RemoveUse(use);
|
| + replace_to->AppendUse(use);
|
| + use->from->GetInputRecordPtr(use->input_index)->to = replace_to;
|
| + }
|
| + use = next;
|
| + }
|
| +}
|
| +
|
| +inline void Node::RemoveAllInputs() {
|
| + for (Inputs::iterator iter(inputs().begin()); iter != inputs().end();
|
| + ++iter) {
|
| + iter.GetInput()->Update(NULL);
|
| + }
|
| +}
|
| +
|
| +inline void Node::TrimInputCount(int new_input_count) {
|
| + if (new_input_count == input_count_) return; // Nothing to do.
|
| +
|
| + DCHECK(new_input_count < input_count_);
|
| +
|
| + // Update inline inputs.
|
| + for (int i = new_input_count; i < input_count_; i++) {
|
| + Node::Input* input = GetInputRecordPtr(i);
|
| + input->Update(NULL);
|
| + }
|
| + input_count_ = new_input_count;
|
| +}
|
| +
|
| +inline void Node::ReplaceInput(int index, Node* new_to) {
|
| + Input* input = GetInputRecordPtr(index);
|
| + input->Update(new_to);
|
| +}
|
| +
|
| +inline void Node::Input::Update(Node* new_to) {
|
| + Node* old_to = this->to;
|
| + if (new_to == old_to) return; // Nothing to do.
|
| + // Snip out the use from where it used to be
|
| + if (old_to != NULL) {
|
| + old_to->RemoveUse(use);
|
| + }
|
| + to = new_to;
|
| + // And put it into the new node's use list.
|
| + if (new_to != NULL) {
|
| + new_to->AppendUse(use);
|
| + } else {
|
| + use->next = NULL;
|
| + use->prev = NULL;
|
| + }
|
| +}
|
| +
|
| +inline void Node::EnsureAppendableInputs(Zone* zone) {
|
| + if (!has_appendable_inputs_) {
|
| + void* deque_buffer = zone->New(sizeof(InputDeque));
|
| + InputDeque* deque = new (deque_buffer) InputDeque(zone);
|
| + for (int i = 0; i < input_count_; ++i) {
|
| + deque->push_back(inputs_.static_[i]);
|
| + }
|
| + inputs_.appendable_ = deque;
|
| + has_appendable_inputs_ = true;
|
| + }
|
| +}
|
| +
|
| +inline void Node::AppendInput(Zone* zone, Node* to_append) {
|
| + Use* new_use = new (zone) Use;
|
| + Input new_input;
|
| + new_input.to = to_append;
|
| + new_input.use = new_use;
|
| + if (reserve_input_count_ > 0) {
|
| + DCHECK(!has_appendable_inputs_);
|
| + reserve_input_count_--;
|
| + inputs_.static_[input_count_] = new_input;
|
| + } else {
|
| + EnsureAppendableInputs(zone);
|
| + inputs_.appendable_->push_back(new_input);
|
| + }
|
| + new_use->input_index = input_count_;
|
| + new_use->from = this;
|
| + to_append->AppendUse(new_use);
|
| + input_count_++;
|
| +}
|
| +
|
| +inline void Node::InsertInput(Zone* zone, int index, Node* to_insert) {
|
| + DCHECK(index >= 0 && index < InputCount());
|
| + // TODO(turbofan): Optimize this implementation!
|
| + AppendInput(zone, InputAt(InputCount() - 1));
|
| + for (int i = InputCount() - 1; i > index; --i) {
|
| + ReplaceInput(i, InputAt(i - 1));
|
| + }
|
| + ReplaceInput(index, to_insert);
|
| +}
|
| +
|
| +inline void Node::RemoveInput(int index) {
|
| + DCHECK(index >= 0 && index < InputCount());
|
| + // TODO(turbofan): Optimize this implementation!
|
| + for (; index < InputCount() - 1; ++index) {
|
| + ReplaceInput(index, InputAt(index + 1));
|
| + }
|
| + TrimInputCount(InputCount() - 1);
|
| +}
|
| +
|
| +inline void Node::AppendUse(Use* use) {
|
| + use->next = NULL;
|
| + use->prev = last_use_;
|
| + if (last_use_ == NULL) {
|
| + first_use_ = use;
|
| + } else {
|
| + last_use_->next = use;
|
| + }
|
| + last_use_ = use;
|
| + ++use_count_;
|
| +}
|
| +
|
| +inline void Node::RemoveUse(Use* use) {
|
| + if (last_use_ == use) {
|
| + last_use_ = use->prev;
|
| + }
|
| + if (use->prev != NULL) {
|
| + use->prev->next = use->next;
|
| + } else {
|
| + first_use_ = use->next;
|
| + }
|
| + if (use->next != NULL) {
|
| + use->next->prev = use->prev;
|
| + }
|
| + --use_count_;
|
| +}
|
| +
|
| +inline bool Node::OwnedBy(Node* owner) const {
|
| + return first_use_ != NULL && first_use_->from == owner &&
|
| + first_use_->next == NULL;
|
| +}
|
| +
|
| } // namespace compiler
|
| } // namespace internal
|
| } // namespace v8
|
|
|