Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(91)

Unified Diff: src/compiler/node.h

Issue 1150923003: [turbofan] Rework Node guts to save space. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Avoid quadratic verification explosion. Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | src/compiler/node.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « no previous file | src/compiler/node.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698