Index: src/compiler/node.h |
diff --git a/src/compiler/node.h b/src/compiler/node.h |
index 0c8f1205a70430fe35bf34d7db00aaf9f85e3f02..16b2aeeb1cc2b12e982ace299453f63645c5e8f7 100644 |
--- a/src/compiler/node.h |
+++ b/src/compiler/node.h |
@@ -55,19 +55,49 @@ class Node final { |
return static_cast<IrOpcode::Value>(op_->opcode()); |
} |
- NodeId id() const { return id_; } |
+ NodeId id() const { return IdField::decode(bit_field_); } |
+ |
+ int InputCount() const { |
+ return has_inline_inputs() ? InlineCountField::decode(bit_field_) |
+ : inputs_.outline_->count_; |
+ } |
- int InputCount() const { return input_count(); } |
- Node* InputAt(int index) const { |
#if DEBUG |
- if (index < 0 || index >= InputCount()) { |
- V8_Fatal(__FILE__, __LINE__, "Node #%d:%s->InputAt(%d) out of bounds", |
- id(), op()->mnemonic(), index); |
- } |
+ void Verify(); |
+#define BOUNDS_CHECK(index) \ |
+ do { \ |
+ if (index < 0 || index >= InputCount()) { \ |
+ V8_Fatal(__FILE__, __LINE__, "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 |
- return GetInputRecordPtr(index)->to; |
+ |
+ Node* InputAt(int index) const { |
+ BOUNDS_CHECK(index); |
+ return *GetInputPtrConst(index); |
} |
- inline void ReplaceInput(int index, Node* new_to); |
+ |
+ 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 RemoveInput(int index); |
@@ -151,49 +181,108 @@ class Node final { |
// 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; |
+ 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; |
private: |
- struct Use final : public ZoneObject { |
- Node* from; |
+ 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; |
- int input_index; |
- }; |
+ uint32_t bit_field_; |
+ |
+ int input_index() const { return InputIndexField::decode(bit_field_); } |
+ int output_index() const { return OutputIndexField::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]; |
+ } |
- class Input final { |
- public: |
- Node* to; |
- Use* use; |
+ Node* from() { |
+ Use* start = this + 1 + input_index(); |
+ return is_inline_use() ? reinterpret_cast<Node*>(start) |
+ : reinterpret_cast<OutOfLineInputs*>(start)->node_; |
+ } |
- void Update(Node* new_to); |
+ typedef BitField<bool, 0, 1> InlineField; |
+ typedef BitField<unsigned, 1, 17> InputIndexField; |
+ typedef BitField<unsigned, 17, 14> OutputIndexField; |
}; |
- inline Node(NodeId id, const Operator* op, int input_count, |
- int reserve_input_count); |
- |
- inline void EnsureAppendableInputs(Zone* zone); |
- |
- Input* GetInputRecordPtr(int index) { |
- return has_appendable_inputs() ? &((*inputs_.appendable_)[index]) |
- : &inputs_.static_[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]; |
} |
- const Input* GetInputRecordPtr(int index) const { |
- return has_appendable_inputs() ? &((*inputs_.appendable_)[index]) |
- : &inputs_.static_[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]; |
} |
- inline void AppendUse(Use* const use); |
- inline void RemoveUse(Use* const use); |
+ void AppendUse(Use* use); |
+ void RemoveUse(Use* use); |
void* operator new(size_t, void* location) { return location; } |
- typedef ZoneDeque<Input> InputDeque; |
- |
// Only NodeProperties should manipulate the bounds. |
Bounds bounds() { return bounds_; } |
void set_bounds(Bounds b) { bounds_ = b; } |
@@ -202,47 +291,28 @@ class Node final { |
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); |
+ inline bool has_inline_inputs() const { |
+ return InlineCountField::decode(bit_field_) != kOutlineMarker; |
} |
- 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); |
- } |
+ void ClearInputs(int start, int count); |
- typedef BitField<unsigned, 0, 29> InputCountField; |
- typedef BitField<unsigned, 29, 2> ReservedInputCountField; |
- typedef BitField<unsigned, 31, 1> HasAppendableInputsField; |
- static const int kDefaultReservedInputs = ReservedInputCountField::kMax; |
+ 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_; |
Bounds bounds_; |
Mark mark_; |
- NodeId const id_; |
- unsigned bit_field_; |
+ uint32_t bit_field_; |
Use* first_use_; |
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_[1]; |
- InputDeque* appendable_; |
+ // Inline storage for inputs or out-of-line storage. |
+ Node* inline_[1]; |
+ OutOfLineInputs* outline_; |
} inputs_; |
friend class Edge; |
@@ -275,26 +345,38 @@ static inline const T& OpParameter(const Node* node) { |
// the node having the input. |
class Edge final { |
public: |
- Node* from() const { return input_->use->from; } |
- Node* to() const { return input_->to; } |
+ Node* from() const { return use_->from(); } |
+ Node* to() const { return *input_ptr_; } |
int index() const { |
- int const index = input_->use->input_index; |
- DCHECK_LT(index, input_->use->from->input_count()); |
+ int const index = use_->input_index(); |
+ DCHECK_LT(index, use_->from()->InputCount()); |
return index; |
} |
- bool operator==(const Edge& other) { return input_ == other.input_; } |
+ bool operator==(const Edge& other) { return input_ptr_ == other.input_ptr_; } |
bool operator!=(const Edge& other) { return !(*this == other); } |
- void UpdateTo(Node* new_to) { input_->Update(new_to); } |
+ 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::iterator; |
- explicit Edge(Node::Input* input) : input_(input) { DCHECK_NOT_NULL(input); } |
+ 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::Input* input_; |
+ Node::Use* use_; |
+ Node** input_ptr_; |
}; |
@@ -307,16 +389,18 @@ class Node::InputEdges::iterator final { |
typedef Edge* pointer; |
typedef Edge& reference; |
- iterator() : input_(nullptr) {} |
- iterator(const iterator& other) : input_(other.input_) {} |
+ iterator() : use_(nullptr), input_ptr_(nullptr) {} |
+ iterator(const iterator& other) |
+ : use_(other.use_), input_ptr_(other.input_ptr_) {} |
- Edge operator*() const { return Edge(input_); } |
+ Edge operator*() const { return Edge(use_, input_ptr_); } |
bool operator==(const iterator& other) const { |
- return input_ == other.input_; |
+ return input_ptr_ == other.input_ptr_; |
} |
bool operator!=(const iterator& other) const { return !(*this == other); } |
iterator& operator++() { |
- SetInput(Edge(input_).from(), input_->use->input_index + 1); |
+ input_ptr_++; |
+ use_--; |
return *this; |
} |
iterator operator++(int); |
@@ -324,20 +408,11 @@ class Node::InputEdges::iterator final { |
private: |
friend class Node; |
- explicit iterator(Node* from, int index = 0) : input_(nullptr) { |
- SetInput(from, index); |
- } |
- |
- void SetInput(Node* from, int index) { |
- DCHECK(index >= 0 && index <= from->InputCount()); |
- if (index < from->InputCount()) { |
- input_ = from->GetInputRecordPtr(index); |
- } else { |
- input_ = nullptr; |
- } |
- } |
+ explicit iterator(Node* from, int index = 0) |
+ : use_(from->GetUsePtr(index)), input_ptr_(from->GetInputPtr(index)) {} |
- Input* input_; |
+ Use* use_; |
+ Node** input_ptr_; |
}; |
@@ -400,10 +475,7 @@ class Node::UseEdges::iterator final { |
iterator(const iterator& other) |
: current_(other.current_), next_(other.next_) {} |
- Edge operator*() const { |
- return Edge(current_->from->GetInputRecordPtr(current_->input_index)); |
- } |
- |
+ Edge operator*() const { return Edge(current_, current_->input_ptr()); } |
bool operator==(const iterator& other) const { |
return current_ == other.current_; |
} |
@@ -450,7 +522,7 @@ class Node::Uses::const_iterator final { |
const_iterator(const const_iterator& other) : current_(other.current_) {} |
- Node* operator*() const { return current_->from; } |
+ Node* operator*() const { return current_->from(); } |
bool operator==(const const_iterator& other) const { |
return other.current_ == current_; |
} |
@@ -481,11 +553,6 @@ Node::Uses::const_iterator Node::Uses::begin() const { |
Node::Uses::const_iterator Node::Uses::end() const { return const_iterator(); } |
- |
-void Node::ReplaceInput(int index, Node* new_to) { |
- GetInputRecordPtr(index)->Update(new_to); |
-} |
- |
} // namespace compiler |
} // namespace internal |
} // namespace v8 |