// Copyright 2013 the V8 project authors. All rights reserved.
|
// Use of this source code is governed by a BSD-style license that can be
|
// found in the LICENSE file.
|
|
#include "src/compiler/node.h"
|
|
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* const* inputs, bool has_extensible_inputs) {
|
Node** input_ptr;
|
Use* use_ptr;
|
Node* node;
|
bool is_inline;
|
|
#if DEBUG
|
// Verify that none of the inputs are {nullptr}.
|
for (int i = 0; i < input_count; i++) {
|
if (inputs[i] == nullptr) {
|
FATAL("Node::New() Error: #%d:%s[%d] is nullptr", static_cast<int>(id),
|
op->mnemonic(), i);
|
}
|
}
|
#endif
|
|
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_ptr[current] = to;
|
Use* use = use_ptr - 1 - current;
|
use->bit_field_ = Use::InputIndexField::encode(current) |
|
Use::InlineField::encode(is_inline);
|
to->AppendUse(use);
|
}
|
node->Verify();
|
return node;
|
}
|
|
|
Node* Node::Clone(Zone* zone, NodeId id, const Node* node) {
|
int const input_count = node->InputCount();
|
Node* const* const inputs = node->has_inline_inputs()
|
? node->inputs_.inline_
|
: node->inputs_.outline_->inputs_;
|
Node* const clone = New(zone, id, node->op(), input_count, inputs, false);
|
clone->set_type(node->type());
|
return clone;
|
}
|
|
|
void Node::Kill() {
|
DCHECK_NOT_NULL(op());
|
NullAllInputs();
|
DCHECK(uses().empty());
|
}
|
|
|
void Node::AppendInput(Zone* zone, Node* new_to) {
|
DCHECK_NOT_NULL(zone);
|
DCHECK_NOT_NULL(new_to);
|
|
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 {
|
// 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);
|
}
|
Verify();
|
}
|
|
|
void Node::InsertInput(Zone* zone, int index, Node* new_to) {
|
DCHECK_NOT_NULL(zone);
|
DCHECK_LE(0, index);
|
DCHECK_LT(index, InputCount());
|
AppendInput(zone, InputAt(InputCount() - 1));
|
for (int i = InputCount() - 1; i > index; --i) {
|
ReplaceInput(i, InputAt(i - 1));
|
}
|
ReplaceInput(index, new_to);
|
Verify();
|
}
|
|
void Node::InsertInputs(Zone* zone, int index, int count) {
|
DCHECK_NOT_NULL(zone);
|
DCHECK_LE(0, index);
|
DCHECK_LT(0, count);
|
DCHECK_LT(index, InputCount());
|
for (int i = 0; i < count; i++) {
|
AppendInput(zone, InputAt(Max(InputCount() - count, 0)));
|
}
|
for (int i = InputCount() - count - 1; i >= Max(index, count); --i) {
|
ReplaceInput(i, InputAt(i - count));
|
}
|
for (int i = 0; i < count; i++) {
|
ReplaceInput(index + i, nullptr);
|
}
|
Verify();
|
}
|
|
void Node::RemoveInput(int index) {
|
DCHECK_LE(0, index);
|
DCHECK_LT(index, InputCount());
|
for (; index < InputCount() - 1; ++index) {
|
ReplaceInput(index, InputAt(index + 1));
|
}
|
TrimInputCount(InputCount() - 1);
|
Verify();
|
}
|
|
|
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) {
|
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 {
|
inputs_.outline_->count_ = new_input_count;
|
}
|
}
|
|
|
int Node::UseCount() const {
|
int use_count = 0;
|
for (const Use* use = first_use_; use; use = use->next) {
|
++use_count;
|
}
|
return use_count;
|
}
|
|
|
void Node::ReplaceUses(Node* that) {
|
DCHECK(this->first_use_ == nullptr || this->first_use_->prev == nullptr);
|
DCHECK(that->first_use_ == nullptr || that->first_use_->prev == nullptr);
|
|
// Update the pointers to {this} to point to {that}.
|
Use* last_use = nullptr;
|
for (Use* use = this->first_use_; use; use = use->next) {
|
*use->input_ptr() = that;
|
last_use = use;
|
}
|
if (last_use) {
|
// Concat the use list of {this} and {that}.
|
last_use->next = that->first_use_;
|
if (that->first_use_) that->first_use_->prev = last_use;
|
that->first_use_ = this->first_use_;
|
}
|
first_use_ = nullptr;
|
}
|
|
|
bool Node::OwnedBy(Node const* owner1, Node const* owner2) const {
|
unsigned mask = 0;
|
for (Use* use = first_use_; use; use = use->next) {
|
Node* from = use->from();
|
if (from == owner1) {
|
mask |= 1;
|
} else if (from == owner2) {
|
mask |= 2;
|
} else {
|
return false;
|
}
|
}
|
return mask == 3;
|
}
|
|
void Node::Print() const {
|
StdoutStream os;
|
os << *this << std::endl;
|
for (Node* input : this->inputs()) {
|
os << " " << *input << std::endl;
|
}
|
}
|
|
std::ostream& operator<<(std::ostream& os, const Node& n) {
|
os << n.id() << ": " << *n.op();
|
if (n.InputCount() > 0) {
|
os << "(";
|
for (int i = 0; i < n.InputCount(); ++i) {
|
if (i != 0) os << ", ";
|
if (n.InputAt(i)) {
|
os << n.InputAt(i)->id();
|
} else {
|
os << "null";
|
}
|
}
|
os << ")";
|
}
|
return os;
|
}
|
|
Node::Node(NodeId id, const Operator* op, int inline_count, int inline_capacity)
|
: op_(op),
|
mark_(0),
|
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_GE(kMaxInlineCapacity, inline_capacity);
|
DCHECK(inline_count == kOutlineMarker || inline_count <= inline_capacity);
|
}
|
|
|
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;
|
first_use_ = use;
|
}
|
|
|
void Node::RemoveUse(Use* use) {
|
DCHECK(first_use_ == nullptr || first_use_->prev == nullptr);
|
if (use->prev) {
|
DCHECK_NE(first_use_, use);
|
use->prev->next = use->next;
|
} else {
|
DCHECK_EQ(first_use_, use);
|
first_use_ = use->next;
|
}
|
if (use->next) {
|
use->next->prev = use->prev;
|
}
|
}
|
|
|
#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++) {
|
DCHECK_EQ(i, this->GetUsePtr(i)->input_index());
|
DCHECK_EQ(this->GetInputPtr(i), this->GetUsePtr(i)->input_ptr());
|
DCHECK_EQ(count, this->InputCount());
|
}
|
{ // Direct input iteration.
|
int index = 0;
|
for (Node* input : this->inputs()) {
|
DCHECK_EQ(this->InputAt(index), input);
|
index++;
|
}
|
DCHECK_EQ(count, index);
|
DCHECK_EQ(this->InputCount(), index);
|
}
|
{ // Input edge iteration.
|
int index = 0;
|
for (Edge edge : this->input_edges()) {
|
DCHECK_EQ(edge.from(), this);
|
DCHECK_EQ(index, edge.index());
|
DCHECK_EQ(this->InputAt(index), edge.to());
|
index++;
|
}
|
DCHECK_EQ(count, index);
|
DCHECK_EQ(this->InputCount(), index);
|
}
|
}
|
#endif
|
|
Node::InputEdges::iterator Node::InputEdges::iterator::operator++(int n) {
|
iterator result(*this);
|
++(*this);
|
return result;
|
}
|
|
|
Node::Inputs::const_iterator Node::Inputs::const_iterator::operator++(int n) {
|
const_iterator result(*this);
|
++(*this);
|
return result;
|
}
|
|
|
Node::UseEdges::iterator Node::UseEdges::iterator::operator++(int n) {
|
iterator result(*this);
|
++(*this);
|
return result;
|
}
|
|
|
bool Node::UseEdges::empty() const { return begin() == end(); }
|
|
|
Node::Uses::const_iterator Node::Uses::const_iterator::operator++(int n) {
|
const_iterator result(*this);
|
++(*this);
|
return result;
|
}
|
|
|
bool Node::Uses::empty() const { return begin() == end(); }
|
|
} // namespace compiler
|
} // namespace internal
|
} // namespace v8
|