// 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_
|