Index: src/compiler/node.cc |
diff --git a/src/compiler/node.cc b/src/compiler/node.cc |
index e1802b8d10b8a01540565319f5752873d7c161db..f38fb954416ceeaca6a8b475dde4fa754fd3877c 100644 |
--- a/src/compiler/node.cc |
+++ b/src/compiler/node.cc |
@@ -4,37 +4,105 @@ |
#include "src/compiler/node.h" |
-#include <algorithm> |
- |
namespace v8 { |
namespace internal { |
namespace compiler { |
+Node::OutOfLineInputs* Node::OutOfLineInputs::New(Zone* zone, int capacity) { |
+ size_t size = |
+ sizeof(OutOfLineInputs) + capacity * (sizeof(Node*) + sizeof(Use)); |
+ intptr_t raw_buffer = reinterpret_cast<intptr_t>(zone->New(size)); |
+ Node::OutOfLineInputs* outline = |
+ reinterpret_cast<OutOfLineInputs*>(raw_buffer + capacity * sizeof(Use)); |
+ outline->capacity_ = capacity; |
+ outline->count_ = 0; |
+ return outline; |
+} |
+ |
+ |
+void Node::OutOfLineInputs::ExtractFrom(Use* old_use_ptr, Node** old_input_ptr, |
+ int count) { |
+ // Extract the inputs from the old use and input pointers and copy them |
+ // to this out-of-line-storage. |
+ Use* new_use_ptr = reinterpret_cast<Use*>(this) - 1; |
+ Node** new_input_ptr = inputs_; |
+ for (int current = 0; current < count; current++) { |
+ new_use_ptr->bit_field_ = |
+ Use::InputIndexField::encode(current) | Use::InlineField::encode(false); |
+ DCHECK_EQ(old_input_ptr, old_use_ptr->input_ptr()); |
+ DCHECK_EQ(new_input_ptr, new_use_ptr->input_ptr()); |
+ Node* old_to = *old_input_ptr; |
+ if (old_to) { |
+ *old_input_ptr = nullptr; |
+ old_to->RemoveUse(old_use_ptr); |
+ *new_input_ptr = old_to; |
+ old_to->AppendUse(new_use_ptr); |
+ } else { |
+ *new_input_ptr = nullptr; |
+ } |
+ old_input_ptr++; |
+ new_input_ptr++; |
+ old_use_ptr--; |
+ new_use_ptr--; |
+ } |
+ this->count_ = count; |
+} |
+ |
+ |
Node* Node::New(Zone* zone, NodeId id, const Operator* op, int input_count, |
Node** inputs, bool has_extensible_inputs) { |
- size_t node_size = sizeof(Node) - sizeof(Input); |
- int reserve_input_count = has_extensible_inputs ? kDefaultReservedInputs : 0; |
- size_t inputs_size = std::max<size_t>( |
- (input_count + reserve_input_count) * sizeof(Input), sizeof(InputDeque*)); |
- size_t uses_size = input_count * sizeof(Use); |
- int size = static_cast<int>(node_size + inputs_size + uses_size); |
- void* buffer = zone->New(size); |
- Node* result = new (buffer) Node(id, op, input_count, reserve_input_count); |
- Input* input = result->inputs_.static_; |
- Use* use = |
- reinterpret_cast<Use*>(reinterpret_cast<char*>(input) + inputs_size); |
+ Node** input_ptr; |
+ Use* use_ptr; |
+ Node* node; |
+ bool is_inline; |
+ |
+ if (input_count > kMaxInlineCapacity) { |
+ // Allocate out-of-line inputs. |
+ int capacity = |
+ has_extensible_inputs ? input_count + kMaxInlineCapacity : input_count; |
+ OutOfLineInputs* outline = OutOfLineInputs::New(zone, capacity); |
+ |
+ // Allocate node. |
+ void* node_buffer = zone->New(sizeof(Node)); |
+ node = new (node_buffer) Node(id, op, kOutlineMarker, 0); |
+ node->inputs_.outline_ = outline; |
+ |
+ outline->node_ = node; |
+ outline->count_ = input_count; |
+ |
+ input_ptr = outline->inputs_; |
+ use_ptr = reinterpret_cast<Use*>(outline); |
+ is_inline = false; |
+ } else { |
+ // Allocate node with inline inputs. |
+ int capacity = input_count; |
+ if (has_extensible_inputs) { |
+ const int max = kMaxInlineCapacity; |
+ capacity = std::min(input_count + 3, max); |
+ } |
+ |
+ size_t size = sizeof(Node) + capacity * (sizeof(Node*) + sizeof(Use)); |
+ intptr_t raw_buffer = reinterpret_cast<intptr_t>(zone->New(size)); |
+ void* node_buffer = |
+ reinterpret_cast<void*>(raw_buffer + capacity * sizeof(Use)); |
+ |
+ node = new (node_buffer) Node(id, op, input_count, capacity); |
+ input_ptr = node->inputs_.inline_; |
+ use_ptr = reinterpret_cast<Use*>(node); |
+ is_inline = true; |
+ } |
+ // Initialize the input pointers and the uses. |
for (int current = 0; current < input_count; ++current) { |
Node* to = *inputs++; |
- input->to = to; |
- input->use = use; |
- use->input_index = current; |
- use->from = result; |
+ input_ptr[current] = to; |
+ Use* use = use_ptr - 1 - current; |
+ use->bit_field_ = Use::InputIndexField::encode(current) | |
+ Use::InlineField::encode(is_inline); |
to->AppendUse(use); |
- ++use; |
- ++input; |
} |
- return result; |
+ node->Verify(); |
+ return node; |
} |
@@ -48,22 +116,47 @@ void Node::Kill() { |
void Node::AppendInput(Zone* zone, Node* new_to) { |
DCHECK_NOT_NULL(zone); |
DCHECK_NOT_NULL(new_to); |
- Use* new_use = new (zone) Use; |
- Input new_input; |
- new_input.to = new_to; |
- 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; |
+ |
+ int inline_count = InlineCountField::decode(bit_field_); |
+ int inline_capacity = InlineCapacityField::decode(bit_field_); |
+ if (inline_count < inline_capacity) { |
+ // Append inline input. |
+ bit_field_ = InlineCountField::update(bit_field_, inline_count + 1); |
+ *GetInputPtr(inline_count) = new_to; |
+ Use* use = GetUsePtr(inline_count); |
+ use->bit_field_ = Use::InputIndexField::encode(inline_count) | |
+ Use::InlineField::encode(true); |
+ new_to->AppendUse(use); |
} else { |
- EnsureAppendableInputs(zone); |
- inputs_.appendable_->push_back(new_input); |
+ // Append out-of-line input. |
+ int input_count = InputCount(); |
+ OutOfLineInputs* outline = nullptr; |
+ if (inline_count != kOutlineMarker) { |
+ // switch to out of line inputs. |
+ outline = OutOfLineInputs::New(zone, input_count * 2 + 3); |
+ outline->node_ = this; |
+ outline->ExtractFrom(GetUsePtr(0), GetInputPtr(0), input_count); |
+ bit_field_ = InlineCountField::update(bit_field_, kOutlineMarker); |
+ inputs_.outline_ = outline; |
+ } else { |
+ // use current out of line inputs. |
+ outline = inputs_.outline_; |
+ if (input_count >= outline->capacity_) { |
+ // out of space in out-of-line inputs. |
+ outline = OutOfLineInputs::New(zone, input_count * 2 + 3); |
+ outline->node_ = this; |
+ outline->ExtractFrom(GetUsePtr(0), GetInputPtr(0), input_count); |
+ inputs_.outline_ = outline; |
+ } |
+ } |
+ outline->count_++; |
+ *GetInputPtr(input_count) = new_to; |
+ Use* use = GetUsePtr(input_count); |
+ use->bit_field_ = Use::InputIndexField::encode(input_count) | |
+ Use::InlineField::encode(false); |
+ new_to->AppendUse(use); |
} |
- new_use->input_index = input_count(); |
- new_use->from = this; |
- new_to->AppendUse(new_use); |
- set_input_count(input_count() + 1); |
+ Verify(); |
} |
@@ -76,6 +169,7 @@ void Node::InsertInput(Zone* zone, int index, Node* new_to) { |
ReplaceInput(i, InputAt(i - 1)); |
} |
ReplaceInput(index, new_to); |
+ Verify(); |
} |
@@ -86,28 +180,38 @@ void Node::RemoveInput(int index) { |
ReplaceInput(index, InputAt(index + 1)); |
} |
TrimInputCount(InputCount() - 1); |
+ Verify(); |
} |
-void Node::NullAllInputs() { |
- for (Edge edge : input_edges()) edge.UpdateTo(nullptr); |
+void Node::ClearInputs(int start, int count) { |
+ Node** input_ptr = GetInputPtr(start); |
+ Use* use_ptr = GetUsePtr(start); |
+ while (count-- > 0) { |
+ DCHECK_EQ(input_ptr, use_ptr->input_ptr()); |
+ Node* input = *input_ptr; |
+ *input_ptr = nullptr; |
+ if (input) input->RemoveUse(use_ptr); |
+ input_ptr++; |
+ use_ptr--; |
+ } |
+ Verify(); |
} |
+void Node::NullAllInputs() { ClearInputs(0, InputCount()); } |
+ |
+ |
void Node::TrimInputCount(int new_input_count) { |
- DCHECK_LE(new_input_count, input_count()); |
- if (new_input_count == input_count()) return; // Nothing to do. |
- for (int index = new_input_count; index < input_count(); ++index) { |
- ReplaceInput(index, nullptr); |
- } |
- if (has_appendable_inputs()) { |
- inputs_.appendable_->resize(new_input_count); |
+ int current_count = InputCount(); |
+ DCHECK_LE(new_input_count, current_count); |
+ if (new_input_count == current_count) return; // Nothing to do. |
+ ClearInputs(new_input_count, current_count - new_input_count); |
+ if (has_inline_inputs()) { |
+ bit_field_ = InlineCountField::update(bit_field_, new_input_count); |
} else { |
- set_reserved_input_count(std::min<int>( |
- ReservedInputCountField::kMax, |
- reserved_input_count() + (input_count() - new_input_count))); |
+ inputs_.outline_->count_ = new_input_count; |
} |
- set_input_count(new_input_count); |
} |
@@ -127,7 +231,7 @@ void Node::ReplaceUses(Node* that) { |
// Update the pointers to {this} to point to {that}. |
Use* last_use = nullptr; |
for (Use* use = this->first_use_; use; use = use->next) { |
- use->from->GetInputRecordPtr(use->input_index)->to = that; |
+ *use->input_ptr() = that; |
last_use = use; |
} |
if (last_use) { |
@@ -143,9 +247,10 @@ void Node::ReplaceUses(Node* that) { |
bool Node::OwnedBy(Node const* owner1, Node const* owner2) const { |
unsigned mask = 0; |
for (Use* use = first_use_; use; use = use->next) { |
- if (use->from == owner1) { |
+ Node* from = use->from(); |
+ if (from == owner1) { |
mask |= 1; |
- } else if (use->from == owner2) { |
+ } else if (from == owner2) { |
mask |= 2; |
} else { |
return false; |
@@ -155,50 +260,21 @@ bool Node::OwnedBy(Node const* owner1, Node const* owner2) const { |
} |
-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) { |
- old_to->RemoveUse(use); |
- } |
- to = new_to; |
- // And put it into the new node's use list. |
- if (new_to) { |
- new_to->AppendUse(use); |
- } else { |
- use->next = nullptr; |
- use->prev = nullptr; |
- } |
-} |
- |
- |
-Node::Node(NodeId id, const Operator* op, int input_count, |
- int reserved_input_count) |
+Node::Node(NodeId id, const Operator* op, int inline_count, int inline_capacity) |
: op_(op), |
mark_(0), |
- id_(id), |
- bit_field_(InputCountField::encode(input_count) | |
- ReservedInputCountField::encode(reserved_input_count) | |
- HasAppendableInputsField::encode(false)), |
- first_use_(nullptr) {} |
- |
- |
-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); |
- } |
+ bit_field_(IdField::encode(id) | InlineCountField::encode(inline_count) | |
+ InlineCapacityField::encode(inline_capacity)), |
+ first_use_(nullptr) { |
+ // Inputs must either be out of line or within the inline capacity. |
+ DCHECK(inline_capacity <= kMaxInlineCapacity); |
+ DCHECK(inline_count == kOutlineMarker || inline_count <= inline_capacity); |
} |
-void Node::AppendUse(Use* const use) { |
+void Node::AppendUse(Use* use) { |
DCHECK(first_use_ == nullptr || first_use_->prev == nullptr); |
+ DCHECK_EQ(this, *use->input_ptr()); |
use->next = first_use_; |
use->prev = nullptr; |
if (first_use_) first_use_->prev = use; |
@@ -206,7 +282,7 @@ void Node::AppendUse(Use* const use) { |
} |
-void Node::RemoveUse(Use* const use) { |
+void Node::RemoveUse(Use* use) { |
DCHECK(first_use_ == nullptr || first_use_->prev == nullptr); |
if (use->prev) { |
DCHECK_NE(first_use_, use); |
@@ -221,6 +297,44 @@ void Node::RemoveUse(Use* const use) { |
} |
+#if DEBUG |
+void Node::Verify() { |
+ // Check basic sanity of input data structures. |
+ fflush(stdout); |
+ int count = this->InputCount(); |
+ // Avoid quadratic explosion for mega nodes; only verify if the input |
+ // count is less than 200 or is a round number of 100s. |
+ if (count > 200 && count % 100) return; |
+ |
+ for (int i = 0; i < count; i++) { |
+ CHECK_EQ(i, this->GetUsePtr(i)->input_index()); |
+ CHECK_EQ(this->GetInputPtr(i), this->GetUsePtr(i)->input_ptr()); |
+ CHECK_EQ(count, this->InputCount()); |
+ } |
+ { // Direct input iteration. |
+ int index = 0; |
+ for (Node* input : this->inputs()) { |
+ CHECK_EQ(this->InputAt(index), input); |
+ index++; |
+ } |
+ CHECK_EQ(count, index); |
+ CHECK_EQ(this->InputCount(), index); |
+ } |
+ { // Input edge iteration. |
+ int index = 0; |
+ for (Edge edge : this->input_edges()) { |
+ CHECK_EQ(edge.from(), this); |
+ CHECK_EQ(index, edge.index()); |
+ CHECK_EQ(this->InputAt(index), edge.to()); |
+ index++; |
+ } |
+ CHECK_EQ(count, index); |
+ CHECK_EQ(this->InputCount(), index); |
+ } |
+} |
+#endif |
+ |
+ |
std::ostream& operator<<(std::ostream& os, const Node& n) { |
os << n.id() << ": " << *n.op(); |
if (n.InputCount() > 0) { |