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 @@
#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) {
......
......@@ -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
......
......@@ -864,8 +864,8 @@ TEST(CMergeReduce_dead_rm1b) {
Node* t = 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++) {
Node* merge = R.graph.NewNode(R.common.Merge(3), R.dead, R.dead, R.dead);
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(j, f);
R.ReduceMerge(merge, merge);
......
......@@ -233,6 +233,29 @@ TEST_F(NodeTest, TrimThenAppend) {
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 internal
} // 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