Commit 65b66634 authored by titzer's avatar titzer Committed by Commit bot

[turbofan] Rework Node guts to save space.

This reduces the storage per-Node storage from 7 words to 6 and per-edge
storage from 6 words to 4.
On average this is about 10%-15% space savings over the whole graph.

Remove the use of std::deque as the out-of-line storage for inputs.
Reduce size of Use links and use pointer arithmetic to find Node
from Use.

R=mstarzinger@chromium.org,jarin@chromium.org
BUG=

Review URL: https://codereview.chromium.org/1150923003

Cr-Commit-Position: refs/heads/master@{#28583}
parent dd5a93cd
...@@ -4,37 +4,105 @@ ...@@ -4,37 +4,105 @@
#include "src/compiler/node.h" #include "src/compiler/node.h"
#include <algorithm>
namespace v8 { namespace v8 {
namespace internal { namespace internal {
namespace compiler { 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* Node::New(Zone* zone, NodeId id, const Operator* op, int input_count,
Node** inputs, bool has_extensible_inputs) { Node** inputs, bool has_extensible_inputs) {
size_t node_size = sizeof(Node) - sizeof(Input); Node** input_ptr;
int reserve_input_count = has_extensible_inputs ? kDefaultReservedInputs : 0; Use* use_ptr;
size_t inputs_size = std::max<size_t>( Node* node;
(input_count + reserve_input_count) * sizeof(Input), sizeof(InputDeque*)); bool is_inline;
size_t uses_size = input_count * sizeof(Use);
int size = static_cast<int>(node_size + inputs_size + uses_size); if (input_count > kMaxInlineCapacity) {
void* buffer = zone->New(size); // Allocate out-of-line inputs.
Node* result = new (buffer) Node(id, op, input_count, reserve_input_count); int capacity =
Input* input = result->inputs_.static_; has_extensible_inputs ? input_count + kMaxInlineCapacity : input_count;
Use* use = OutOfLineInputs* outline = OutOfLineInputs::New(zone, capacity);
reinterpret_cast<Use*>(reinterpret_cast<char*>(input) + inputs_size);
// 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) { for (int current = 0; current < input_count; ++current) {
Node* to = *inputs++; Node* to = *inputs++;
input->to = to; input_ptr[current] = to;
input->use = use; Use* use = use_ptr - 1 - current;
use->input_index = current; use->bit_field_ = Use::InputIndexField::encode(current) |
use->from = result; Use::InlineField::encode(is_inline);
to->AppendUse(use); to->AppendUse(use);
++use;
++input;
} }
return result; node->Verify();
return node;
} }
...@@ -48,22 +116,47 @@ void Node::Kill() { ...@@ -48,22 +116,47 @@ void Node::Kill() {
void Node::AppendInput(Zone* zone, Node* new_to) { void Node::AppendInput(Zone* zone, Node* new_to) {
DCHECK_NOT_NULL(zone); DCHECK_NOT_NULL(zone);
DCHECK_NOT_NULL(new_to); DCHECK_NOT_NULL(new_to);
Use* new_use = new (zone) Use;
Input new_input; int inline_count = InlineCountField::decode(bit_field_);
new_input.to = new_to; int inline_capacity = InlineCapacityField::decode(bit_field_);
new_input.use = new_use; if (inline_count < inline_capacity) {
if (reserved_input_count() > 0) { // Append inline input.
DCHECK(!has_appendable_inputs()); bit_field_ = InlineCountField::update(bit_field_, inline_count + 1);
set_reserved_input_count(reserved_input_count() - 1); *GetInputPtr(inline_count) = new_to;
inputs_.static_[input_count()] = new_input; Use* use = GetUsePtr(inline_count);
use->bit_field_ = Use::InputIndexField::encode(inline_count) |
Use::InlineField::encode(true);
new_to->AppendUse(use);
} else {
// 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 { } else {
EnsureAppendableInputs(zone); // use current out of line inputs.
inputs_.appendable_->push_back(new_input); 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;
}
} }
new_use->input_index = input_count(); outline->count_++;
new_use->from = this; *GetInputPtr(input_count) = new_to;
new_to->AppendUse(new_use); Use* use = GetUsePtr(input_count);
set_input_count(input_count() + 1); use->bit_field_ = Use::InputIndexField::encode(input_count) |
Use::InlineField::encode(false);
new_to->AppendUse(use);
}
Verify();
} }
...@@ -76,6 +169,7 @@ void Node::InsertInput(Zone* zone, int index, Node* new_to) { ...@@ -76,6 +169,7 @@ void Node::InsertInput(Zone* zone, int index, Node* new_to) {
ReplaceInput(i, InputAt(i - 1)); ReplaceInput(i, InputAt(i - 1));
} }
ReplaceInput(index, new_to); ReplaceInput(index, new_to);
Verify();
} }
...@@ -86,28 +180,38 @@ void Node::RemoveInput(int index) { ...@@ -86,28 +180,38 @@ void Node::RemoveInput(int index) {
ReplaceInput(index, InputAt(index + 1)); ReplaceInput(index, InputAt(index + 1));
} }
TrimInputCount(InputCount() - 1); TrimInputCount(InputCount() - 1);
Verify();
} }
void Node::NullAllInputs() { void Node::ClearInputs(int start, int count) {
for (Edge edge : input_edges()) edge.UpdateTo(nullptr); 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) { void Node::TrimInputCount(int new_input_count) {
DCHECK_LE(new_input_count, input_count()); int current_count = InputCount();
if (new_input_count == input_count()) return; // Nothing to do. DCHECK_LE(new_input_count, current_count);
for (int index = new_input_count; index < input_count(); ++index) { if (new_input_count == current_count) return; // Nothing to do.
ReplaceInput(index, nullptr); ClearInputs(new_input_count, current_count - new_input_count);
} if (has_inline_inputs()) {
if (has_appendable_inputs()) { bit_field_ = InlineCountField::update(bit_field_, new_input_count);
inputs_.appendable_->resize(new_input_count);
} else { } else {
set_reserved_input_count(std::min<int>( inputs_.outline_->count_ = new_input_count;
ReservedInputCountField::kMax,
reserved_input_count() + (input_count() - new_input_count)));
} }
set_input_count(new_input_count);
} }
...@@ -127,7 +231,7 @@ void Node::ReplaceUses(Node* that) { ...@@ -127,7 +231,7 @@ void Node::ReplaceUses(Node* that) {
// Update the pointers to {this} to point to {that}. // Update the pointers to {this} to point to {that}.
Use* last_use = nullptr; Use* last_use = nullptr;
for (Use* use = this->first_use_; use; use = use->next) { 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; last_use = use;
} }
if (last_use) { if (last_use) {
...@@ -143,9 +247,10 @@ void Node::ReplaceUses(Node* that) { ...@@ -143,9 +247,10 @@ void Node::ReplaceUses(Node* that) {
bool Node::OwnedBy(Node const* owner1, Node const* owner2) const { bool Node::OwnedBy(Node const* owner1, Node const* owner2) const {
unsigned mask = 0; unsigned mask = 0;
for (Use* use = first_use_; use; use = use->next) { for (Use* use = first_use_; use; use = use->next) {
if (use->from == owner1) { Node* from = use->from();
if (from == owner1) {
mask |= 1; mask |= 1;
} else if (use->from == owner2) { } else if (from == owner2) {
mask |= 2; mask |= 2;
} else { } else {
return false; return false;
...@@ -155,50 +260,21 @@ bool Node::OwnedBy(Node const* owner1, Node const* owner2) const { ...@@ -155,50 +260,21 @@ bool Node::OwnedBy(Node const* owner1, Node const* owner2) const {
} }
void Node::Input::Update(Node* new_to) { Node::Node(NodeId id, const Operator* op, int inline_count, int inline_capacity)
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)
: op_(op), : op_(op),
mark_(0), mark_(0),
id_(id), bit_field_(IdField::encode(id) | InlineCountField::encode(inline_count) |
bit_field_(InputCountField::encode(input_count) | InlineCapacityField::encode(inline_capacity)),
ReservedInputCountField::encode(reserved_input_count) | first_use_(nullptr) {
HasAppendableInputsField::encode(false)), // Inputs must either be out of line or within the inline capacity.
first_use_(nullptr) {} DCHECK(inline_capacity <= kMaxInlineCapacity);
DCHECK(inline_count == kOutlineMarker || inline_count <= inline_capacity);
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);
}
} }
void Node::AppendUse(Use* const use) { void Node::AppendUse(Use* use) {
DCHECK(first_use_ == nullptr || first_use_->prev == nullptr); DCHECK(first_use_ == nullptr || first_use_->prev == nullptr);
DCHECK_EQ(this, *use->input_ptr());
use->next = first_use_; use->next = first_use_;
use->prev = nullptr; use->prev = nullptr;
if (first_use_) first_use_->prev = use; if (first_use_) first_use_->prev = use;
...@@ -206,7 +282,7 @@ void Node::AppendUse(Use* const 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); DCHECK(first_use_ == nullptr || first_use_->prev == nullptr);
if (use->prev) { if (use->prev) {
DCHECK_NE(first_use_, use); DCHECK_NE(first_use_, use);
...@@ -221,6 +297,44 @@ void Node::RemoveUse(Use* const 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) { std::ostream& operator<<(std::ostream& os, const Node& n) {
os << n.id() << ": " << *n.op(); os << n.id() << ": " << *n.op();
if (n.InputCount() > 0) { if (n.InputCount() > 0) {
......
...@@ -55,19 +55,49 @@ class Node final { ...@@ -55,19 +55,49 @@ class Node final {
return static_cast<IrOpcode::Value>(op_->opcode()); return static_cast<IrOpcode::Value>(op_->opcode());
} }
NodeId id() const { return id_; } NodeId id() const { return IdField::decode(bit_field_); }
int InputCount() const { return input_count(); } int InputCount() const {
Node* InputAt(int index) const { return has_inline_inputs() ? InlineCountField::decode(bit_field_)
#if DEBUG : inputs_.outline_->count_;
if (index < 0 || index >= InputCount()) {
V8_Fatal(__FILE__, __LINE__, "Node #%d:%s->InputAt(%d) out of bounds",
id(), op()->mnemonic(), index);
} }
#if DEBUG
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 #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 AppendInput(Zone* zone, Node* new_to);
void InsertInput(Zone* zone, int index, Node* new_to); void InsertInput(Zone* zone, int index, Node* new_to);
void RemoveInput(int index); void RemoveInput(int index);
...@@ -151,49 +181,108 @@ class Node final { ...@@ -151,49 +181,108 @@ class Node final {
// Returns true if {owner} is the user of {this} node. // Returns true if {owner} is the user of {this} node.
bool OwnedBy(Node* owner) const { 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. // Returns true if {owner1} and {owner2} are the only users of {this} node.
bool OwnedBy(Node const* owner1, Node const* owner2) const; bool OwnedBy(Node const* owner1, Node const* owner2) const;
private: private:
struct Use final : public ZoneObject { struct Use;
Node* from; // 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* next;
Use* prev; 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 { Node* from() {
public: Use* start = this + 1 + input_index();
Node* to; return is_inline_use() ? reinterpret_cast<Node*>(start)
Use* use; : 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); //== Memory layout ===========================================================
//============================================================================
inline void EnsureAppendableInputs(Zone* zone); // 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
Input* GetInputRecordPtr(int index) { // space-efficient manner.
return has_appendable_inputs() ? &((*inputs_.appendable_)[index]) //
: &inputs_.static_[index]; // {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 { Node** GetInputPtr(int input_index) {
return has_appendable_inputs() ? &((*inputs_.appendable_)[index]) return has_inline_inputs() ? &(inputs_.inline_[input_index])
: &inputs_.static_[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); void AppendUse(Use* use);
inline void RemoveUse(Use* const use); void RemoveUse(Use* use);
void* operator new(size_t, void* location) { return location; } void* operator new(size_t, void* location) { return location; }
typedef ZoneDeque<Input> InputDeque;
// Only NodeProperties should manipulate the bounds. // Only NodeProperties should manipulate the bounds.
Bounds bounds() { return bounds_; } Bounds bounds() { return bounds_; }
void set_bounds(Bounds b) { bounds_ = b; } void set_bounds(Bounds b) { bounds_ = b; }
...@@ -202,47 +291,28 @@ class Node final { ...@@ -202,47 +291,28 @@ class Node final {
Mark mark() { return mark_; } Mark mark() { return mark_; }
void set_mark(Mark mark) { mark_ = mark; } void set_mark(Mark mark) { mark_ = mark; }
int input_count() const { return InputCountField::decode(bit_field_); } inline bool has_inline_inputs() const {
void set_input_count(int input_count) { return InlineCountField::decode(bit_field_) != kOutlineMarker;
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 { void ClearInputs(int start, int count);
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<NodeId, 0, 24> IdField;
typedef BitField<unsigned, 29, 2> ReservedInputCountField; typedef BitField<unsigned, 24, 4> InlineCountField;
typedef BitField<unsigned, 31, 1> HasAppendableInputsField; typedef BitField<unsigned, 28, 4> InlineCapacityField;
static const int kDefaultReservedInputs = ReservedInputCountField::kMax; static const int kOutlineMarker = InlineCountField::kMax;
static const int kMaxInlineCount = InlineCountField::kMax - 1;
static const int kMaxInlineCapacity = InlineCapacityField::kMax - 1;
const Operator* op_; const Operator* op_;
Bounds bounds_; Bounds bounds_;
Mark mark_; Mark mark_;
NodeId const id_; uint32_t bit_field_;
unsigned bit_field_;
Use* first_use_; Use* first_use_;
union { union {
// When a node is initially allocated, it uses a static buffer to hold its // Inline storage for inputs or out-of-line storage.
// inputs under the assumption that the number of outputs will not increase. Node* inline_[1];
// When the first input is appended, the static buffer is converted into a OutOfLineInputs* outline_;
// deque to allow for space-efficient growing.
Input static_[1];
InputDeque* appendable_;
} inputs_; } inputs_;
friend class Edge; friend class Edge;
...@@ -275,26 +345,38 @@ static inline const T& OpParameter(const Node* node) { ...@@ -275,26 +345,38 @@ static inline const T& OpParameter(const Node* node) {
// the node having the input. // the node having the input.
class Edge final { class Edge final {
public: public:
Node* from() const { return input_->use->from; } Node* from() const { return use_->from(); }
Node* to() const { return input_->to; } Node* to() const { return *input_ptr_; }
int index() const { int index() const {
int const index = input_->use->input_index; int const index = use_->input_index();
DCHECK_LT(index, input_->use->from->input_count()); DCHECK_LT(index, use_->from()->InputCount());
return index; 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); } 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: private:
friend class Node::UseEdges::iterator; friend class Node::UseEdges::iterator;
friend class Node::InputEdges::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 { ...@@ -307,16 +389,18 @@ class Node::InputEdges::iterator final {
typedef Edge* pointer; typedef Edge* pointer;
typedef Edge& reference; typedef Edge& reference;
iterator() : input_(nullptr) {} iterator() : use_(nullptr), input_ptr_(nullptr) {}
iterator(const iterator& other) : input_(other.input_) {} 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 { 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); } bool operator!=(const iterator& other) const { return !(*this == other); }
iterator& operator++() { iterator& operator++() {
SetInput(Edge(input_).from(), input_->use->input_index + 1); input_ptr_++;
use_--;
return *this; return *this;
} }
iterator operator++(int); iterator operator++(int);
...@@ -324,20 +408,11 @@ class Node::InputEdges::iterator final { ...@@ -324,20 +408,11 @@ class Node::InputEdges::iterator final {
private: private:
friend class Node; friend class Node;
explicit iterator(Node* from, int index = 0) : input_(nullptr) { explicit iterator(Node* from, int index = 0)
SetInput(from, index); : use_(from->GetUsePtr(index)), input_ptr_(from->GetInputPtr(index)) {}
}
void SetInput(Node* from, int index) {
DCHECK(index >= 0 && index <= from->InputCount());
if (index < from->InputCount()) {
input_ = from->GetInputRecordPtr(index);
} else {
input_ = nullptr;
}
}
Input* input_; Use* use_;
Node** input_ptr_;
}; };
...@@ -400,10 +475,7 @@ class Node::UseEdges::iterator final { ...@@ -400,10 +475,7 @@ class Node::UseEdges::iterator final {
iterator(const iterator& other) iterator(const iterator& other)
: current_(other.current_), next_(other.next_) {} : current_(other.current_), next_(other.next_) {}
Edge operator*() const { Edge operator*() const { return Edge(current_, current_->input_ptr()); }
return Edge(current_->from->GetInputRecordPtr(current_->input_index));
}
bool operator==(const iterator& other) const { bool operator==(const iterator& other) const {
return current_ == other.current_; return current_ == other.current_;
} }
...@@ -450,7 +522,7 @@ class Node::Uses::const_iterator final { ...@@ -450,7 +522,7 @@ class Node::Uses::const_iterator final {
const_iterator(const const_iterator& other) : current_(other.current_) {} 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 { bool operator==(const const_iterator& other) const {
return other.current_ == current_; return other.current_ == current_;
} }
...@@ -481,11 +553,6 @@ Node::Uses::const_iterator Node::Uses::begin() const { ...@@ -481,11 +553,6 @@ Node::Uses::const_iterator Node::Uses::begin() const {
Node::Uses::const_iterator Node::Uses::end() const { return const_iterator(); } 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 compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -864,8 +864,8 @@ TEST(CMergeReduce_dead_rm1b) { ...@@ -864,8 +864,8 @@ TEST(CMergeReduce_dead_rm1b) {
Node* t = R.graph.NewNode(R.common.IfTrue(), R.start); Node* t = R.graph.NewNode(R.common.IfTrue(), R.start);
Node* f = R.graph.NewNode(R.common.IfTrue(), R.start); Node* f = R.graph.NewNode(R.common.IfTrue(), R.start);
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
Node* merge = R.graph.NewNode(R.common.Merge(3), R.dead, R.dead, R.dead);
for (int j = i + 1; j < 3; j++) { for (int j = i + 1; j < 3; j++) {
Node* merge = R.graph.NewNode(R.common.Merge(3), R.dead, R.dead, R.dead);
merge->ReplaceInput(i, t); merge->ReplaceInput(i, t);
merge->ReplaceInput(j, f); merge->ReplaceInput(j, f);
R.ReduceMerge(merge, merge); R.ReduceMerge(merge, merge);
......
...@@ -233,6 +233,29 @@ TEST_F(NodeTest, TrimThenAppend) { ...@@ -233,6 +233,29 @@ TEST_F(NodeTest, TrimThenAppend) {
EXPECT_THAT(node->inputs(), ElementsAre(n1, n3, n4, n5, n8, n9)); EXPECT_THAT(node->inputs(), ElementsAre(n1, n3, n4, n5, n8, n9));
} }
TEST_F(NodeTest, BigNodes) {
static const int kMaxSize = 512;
Node* inputs[kMaxSize];
Node* n0 = Node::New(zone(), 0, &kOp0, 0, nullptr, false);
Node* n1 = Node::New(zone(), 1, &kOp1, 1, &n0, false);
for (int i = 0; i < kMaxSize; i++) {
inputs[i] = i & 1 ? n0 : n1;
}
for (int size = 13; size <= kMaxSize; size += 9) {
Node* node = Node::New(zone(), 12345, &kOp0, size, inputs, false);
EXPECT_EQ(size, node->InputCount());
for (int i = 0; i < size; i++) {
EXPECT_EQ(inputs[i], node->InputAt(i));
}
}
}
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment