blob: 2295b7b5e3e9dfba6a161ca8b9c6b768bf26ee16 [file] [log] [blame]
// 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 <deque>
#include <set>
#include <vector>
#include "src/v8.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 {
// 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 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:
void Initialize(const Operator* op) {
set_op(op);
set_mark(0);
}
bool IsDead() const { return InputCount() > 0 && InputAt(0) == NULL; }
void Kill();
void CollectProjections(ZoneVector<Node*>* projections);
Node* FindProjection(size_t projection_index);
const Operator* op() const { return op_; }
void set_op(const Operator* op) { op_ = op; }
IrOpcode::Value opcode() const {
DCHECK(op_->opcode() <= IrOpcode::kLast);
return static_cast<IrOpcode::Value>(op_->opcode());
}
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() const;
Node* UseAt(int index) const;
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 InputEdges {
public:
class iterator;
iterator begin() const;
iterator end() const;
bool empty() const;
explicit InputEdges(Node* node) : node_(node) {}
private:
Node* node_;
};
class Inputs {
public:
class iterator;
iterator begin() const;
iterator end() const;
bool empty() const;
explicit Inputs(Node* node) : node_(node) {}
private:
Node* node_;
};
Inputs inputs() { return Inputs(this); }
InputEdges input_edges() { return InputEdges(this); }
class UseEdges {
public:
class iterator;
iterator begin() const;
iterator end() const;
bool empty() const;
explicit UseEdges(Node* node) : node_(node) {}
private:
Node* node_;
};
class Uses {
public:
class iterator;
iterator begin() const;
iterator end() const;
bool empty() const;
explicit Uses(Node* node) : node_(node) {}
private:
Node* node_;
};
Uses uses() { return Uses(this); }
UseEdges use_edges() { return UseEdges(this); }
bool OwnedBy(Node* owner) const;
static Node* New(Graph* graph, int input_count, Node** inputs,
bool has_extensible_inputs);
protected:
friend class Graph;
friend class Edge;
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:
inline Node(NodeId id, int input_count, int reserve_input_count);
typedef ZoneDeque<Input> InputDeque;
friend class NodeProperties;
template <typename State>
friend class NodeMarker;
// Only NodeProperties should manipulate the bounds.
Bounds bounds() { return bounds_; }
void set_bounds(Bounds b) { bounds_ = b; }
// Only NodeMarkers should manipulate the marks on nodes.
Mark mark() { return mark_; }
void set_mark(Mark mark) { mark_ = mark; }
int input_count() const { return InputCountField::decode(bit_field_); }
void set_input_count(int input_count) {
DCHECK_LE(0, input_count);
bit_field_ = InputCountField::update(bit_field_, input_count);
}
int reserved_input_count() const {
return ReservedInputCountField::decode(bit_field_);
}
void set_reserved_input_count(int reserved_input_count) {
DCHECK_LE(0, reserved_input_count);
bit_field_ =
ReservedInputCountField::update(bit_field_, reserved_input_count);
}
bool has_appendable_inputs() const {
return HasAppendableInputsField::decode(bit_field_);
}
void set_has_appendable_inputs(bool has_appendable_inputs) {
bit_field_ =
HasAppendableInputsField::update(bit_field_, has_appendable_inputs);
}
typedef BitField<unsigned, 0, 29> InputCountField;
typedef BitField<unsigned, 29, 2> ReservedInputCountField;
typedef BitField<unsigned, 31, 1> HasAppendableInputsField;
static const int kDefaultReservedInputs = ReservedInputCountField::kMax;
const Operator* op_;
Bounds bounds_;
Mark mark_;
NodeId id_;
unsigned bit_field_;
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_;
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 node having the input.
class 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;
}
bool operator==(const Edge& other) { return input_ == other.input_; }
bool operator!=(const Edge& other) { return !(*this == other); }
void UpdateTo(Node* new_to) { input_->Update(new_to); }
private:
friend class Node::Uses::iterator;
friend class Node::Inputs::iterator;
friend class Node::UseEdges::iterator;
friend class Node::InputEdges::iterator;
explicit Edge(Node::Input* input) : input_(input) {}
Node::Input* input_;
};
// A forward iterator to visit the edges for the input dependencies of a node..
class Node::InputEdges::iterator {
public:
typedef std::forward_iterator_tag iterator_category;
typedef int difference_type;
typedef Edge value_type;
typedef Edge* pointer;
typedef Edge& reference;
iterator(const Node::InputEdges::iterator& other) // NOLINT
: input_(other.input_) {}
iterator() : input_(NULL) {}
Edge operator*() const { return Edge(input_); }
bool operator==(const iterator& other) const { return Equals(other); }
bool operator!=(const iterator& other) const { return !Equals(other); }
iterator& operator++() {
DCHECK(input_ != NULL);
Edge edge(input_);
Node* from = edge.from();
SetInput(from, input_->use->input_index + 1);
return *this;
}
iterator operator++(int) {
iterator result(*this);
++(*this);
return result;
}
private:
friend class Node;
explicit iterator(Node* from, int index = 0) : input_(NULL) {
SetInput(from, index);
}
bool Equals(const iterator& other) const { return other.input_ == input_; }
void SetInput(Node* from, int index) {
DCHECK(index >= 0 && index <= from->InputCount());
if (index < from->InputCount()) {
input_ = from->GetInputRecordPtr(index);
} else {
input_ = NULL;
}
}
Input* input_;
};
// A forward iterator to visit the inputs of a node.
class Node::Inputs::iterator {
public:
typedef std::forward_iterator_tag iterator_category;
typedef int difference_type;
typedef Node* value_type;
typedef Node** pointer;
typedef Node*& reference;
iterator(const Node::Inputs::iterator& other) // NOLINT
: iter_(other.iter_) {}
Node* operator*() const { return (*iter_).to(); }
bool operator==(const iterator& other) const { return Equals(other); }
bool operator!=(const iterator& other) const { return !Equals(other); }
iterator& operator++() {
++iter_;
return *this;
}
iterator operator++(int) {
iterator result(*this);
++(*this);
return result;
}
private:
friend class Node::Inputs;
explicit iterator(Node* node, int index) : iter_(node, index) {}
bool Equals(const iterator& other) const { return other.iter_ == iter_; }
Node::InputEdges::iterator iter_;
};
// A forward iterator to visit the uses edges of a node. The edges are returned
// in
// the order in which they were added as inputs.
class Node::UseEdges::iterator {
public:
iterator(const Node::UseEdges::iterator& other) // NOLINT
: current_(other.current_),
next_(other.next_) {}
Edge operator*() const { return Edge(CurrentInput()); }
bool operator==(const iterator& other) { return Equals(other); }
bool operator!=(const iterator& other) { return !Equals(other); }
iterator& operator++() {
DCHECK(current_ != NULL);
current_ = next_;
next_ = (current_ == NULL) ? NULL : current_->next;
return *this;
}
iterator operator++(int) {
iterator result(*this);
++(*this);
return result;
}
private:
friend class Node::UseEdges;
iterator() : current_(NULL), next_(NULL) {}
explicit iterator(Node* node)
: current_(node->first_use_),
next_(current_ == NULL ? NULL : current_->next) {}
bool Equals(const iterator& other) const {
return other.current_ == current_;
}
Input* CurrentInput() const {
return current_->from->GetInputRecordPtr(current_->input_index);
}
Node::Use* current_;
Node::Use* next_;
};
// 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_) {}
Node* operator*() { return current_->from; }
bool operator==(const iterator& other) { return other.current_ == current_; }
bool operator!=(const iterator& other) { return other.current_ != current_; }
iterator& operator++() {
DCHECK(current_ != NULL);
current_ = current_->next;
return *this;
}
private:
friend class Node::Uses;
iterator() : current_(NULL) {}
explicit iterator(Node* node) : current_(node->first_use_) {}
Input* CurrentInput() const {
return current_->from->GetInputRecordPtr(current_->input_index);
}
Node::Use* current_;
};
std::ostream& operator<<(std::ostream& os, const Node& n);
typedef std::set<Node*, std::less<Node*>, zone_allocator<Node*> > NodeSet;
typedef NodeSet::iterator NodeSetIter;
typedef NodeSet::reverse_iterator NodeSetRIter;
typedef ZoneDeque<Node*> NodeDeque;
typedef ZoneVector<Node*> NodeVector;
typedef NodeVector::iterator NodeVectorIter;
typedef NodeVector::const_iterator NodeVectorConstIter;
typedef NodeVector::reverse_iterator NodeVectorRIter;
typedef ZoneVector<NodeVector> NodeVectorVector;
typedef NodeVectorVector::iterator NodeVectorVectorIter;
typedef NodeVectorVector::reverse_iterator NodeVectorVectorRIter;
typedef Node::Uses::iterator UseIter;
typedef Node::Inputs::iterator InputIter;
// Helper to extract parameters from Operator1<*> nodes.
template <typename T>
static inline const T& OpParameter(const Node* node) {
return OpParameter<T>(node->op());
}
inline Node::InputEdges::iterator Node::InputEdges::begin() const {
return Node::InputEdges::iterator(this->node_, 0);
}
inline Node::InputEdges::iterator Node::InputEdges::end() const {
return Node::InputEdges::iterator(this->node_, this->node_->InputCount());
}
inline Node::Inputs::iterator Node::Inputs::begin() const {
return Node::Inputs::iterator(this->node_, 0);
}
inline Node::Inputs::iterator Node::Inputs::end() const {
return Node::Inputs::iterator(this->node_, this->node_->InputCount());
}
inline Node::UseEdges::iterator Node::UseEdges::begin() const {
return Node::UseEdges::iterator(this->node_);
}
inline Node::UseEdges::iterator Node::UseEdges::end() const {
return Node::UseEdges::iterator();
}
inline Node::Uses::iterator Node::Uses::begin() const {
return Node::Uses::iterator(this->node_);
}
inline Node::Uses::iterator Node::Uses::end() const {
return Node::Uses::iterator();
}
inline bool Node::InputEdges::empty() const { return begin() == end(); }
inline bool Node::Uses::empty() const { return begin() == end(); }
inline bool Node::UseEdges::empty() const { return begin() == end(); }
inline bool Node::Inputs::empty() const { 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_;
}
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 (Edge edge : input_edges()) {
edge.UpdateTo(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);
}
set_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;
set_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 (reserved_input_count() > 0) {
DCHECK(!has_appendable_inputs());
set_reserved_input_count(reserved_input_count() - 1);
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);
set_input_count(input_count() + 1);
}
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;
}
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;
}
}
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
#endif // V8_COMPILER_NODE_H_