[turbofan] Allow indexed access to node inputs/input_edges

Node::InputCount() and ::InputAt() have to check for inline/out-of-line
inputs every time they are called. The compiler doesn't seem to be very
good at caching the result of this check, meaning that it (and all its
jumps) would happen for every node access.

Previously we would get around this sometimes, by using Node::inputs(),
which returned a Node::Inputs iterable over node inputs. However,
sometimes node access is more convenient using an index, or we also
want to access the count. This patch adds an index accessor and 'count'
method to Node::Inputs, and replaces several uses of InputCount and
InputAt with this accessor.

Review-Url: https://codereview.chromium.org/2617123002
Cr-Commit-Position: refs/heads/master@{#42179}
This commit is contained in:
leszeks 2017-01-10 05:55:03 -08:00 committed by Commit bot
parent 754736d26c
commit 6873f14b60
7 changed files with 164 additions and 126 deletions

View File

@ -145,20 +145,27 @@ Reduction BranchElimination::ReduceLoop(Node* node) {
Reduction BranchElimination::ReduceMerge(Node* node) {
// Shortcut for the case when we do not know anything about some
// input.
for (Node* input : node->inputs()) {
Node::Inputs inputs = node->inputs();
for (Node* input : inputs) {
if (node_conditions_.Get(input) == nullptr) {
return UpdateConditions(node, nullptr);
}
}
const ControlPathConditions* first = node_conditions_.Get(node->InputAt(0));
auto input_it = inputs.begin();
DCHECK_GT(inputs.count(), 0);
const ControlPathConditions* first = node_conditions_.Get(*input_it);
++input_it;
// Make a copy of the first input's conditions and merge with the conditions
// from other inputs.
ControlPathConditions* conditions =
new (zone_->New(sizeof(ControlPathConditions)))
ControlPathConditions(*first);
for (int i = 1; i < node->InputCount(); i++) {
conditions->Merge(*(node_conditions_.Get(node->InputAt(i))));
auto input_end = inputs.end();
for (; input_it != input_end; ++input_it) {
conditions->Merge(*(node_conditions_.Get(*input_it)));
}
return UpdateConditions(node, conditions);

View File

@ -348,9 +348,10 @@ bool BytecodeGraphBuilder::Environment::StateValuesRequireUpdate(
if (*state_values == nullptr) {
return true;
}
DCHECK_EQ((*state_values)->InputCount(), count);
Node::Inputs inputs = (*state_values)->inputs();
DCHECK_EQ(inputs.count(), count);
for (int i = 0; i < count; i++) {
if ((*state_values)->InputAt(i) != values[i]) {
if (inputs[i] != values[i]) {
return true;
}
}

View File

@ -195,15 +195,16 @@ Reduction CommonOperatorReducer::ReduceMerge(Node* node) {
Reduction CommonOperatorReducer::ReduceEffectPhi(Node* node) {
DCHECK_EQ(IrOpcode::kEffectPhi, node->opcode());
int const input_count = node->InputCount() - 1;
DCHECK_LE(1, input_count);
Node* const merge = node->InputAt(input_count);
Node::Inputs inputs = node->inputs();
int const effect_input_count = inputs.count() - 1;
DCHECK_LE(1, effect_input_count);
Node* const merge = inputs[effect_input_count];
DCHECK(IrOpcode::IsMergeOpcode(merge->opcode()));
DCHECK_EQ(input_count, merge->InputCount());
Node* const effect = node->InputAt(0);
DCHECK_EQ(effect_input_count, merge->InputCount());
Node* const effect = inputs[0];
DCHECK_NE(node, effect);
for (int i = 1; i < input_count; ++i) {
Node* const input = node->InputAt(i);
for (int i = 1; i < effect_input_count; ++i) {
Node* const input = inputs[i];
if (input == node) {
// Ignore redundant inputs.
DCHECK_EQ(IrOpcode::kLoop, merge->opcode());
@ -219,16 +220,18 @@ Reduction CommonOperatorReducer::ReduceEffectPhi(Node* node) {
Reduction CommonOperatorReducer::ReducePhi(Node* node) {
DCHECK_EQ(IrOpcode::kPhi, node->opcode());
int const input_count = node->InputCount() - 1;
DCHECK_LE(1, input_count);
Node* const merge = node->InputAt(input_count);
Node::Inputs inputs = node->inputs();
int const value_input_count = inputs.count() - 1;
DCHECK_LE(1, value_input_count);
Node* const merge = inputs[value_input_count];
DCHECK(IrOpcode::IsMergeOpcode(merge->opcode()));
DCHECK_EQ(input_count, merge->InputCount());
if (input_count == 2) {
Node* vtrue = node->InputAt(0);
Node* vfalse = node->InputAt(1);
Node* if_true = merge->InputAt(0);
Node* if_false = merge->InputAt(1);
DCHECK_EQ(value_input_count, merge->InputCount());
if (value_input_count == 2) {
Node* vtrue = inputs[0];
Node* vfalse = inputs[1];
Node::Inputs merge_inputs = merge->inputs();
Node* if_true = merge_inputs[0];
Node* if_false = merge_inputs[1];
if (if_true->opcode() != IrOpcode::kIfTrue) {
std::swap(if_true, if_false);
std::swap(vtrue, vfalse);
@ -265,10 +268,10 @@ Reduction CommonOperatorReducer::ReducePhi(Node* node) {
}
}
}
Node* const value = node->InputAt(0);
Node* const value = inputs[0];
DCHECK_NE(node, value);
for (int i = 1; i < input_count; ++i) {
Node* const input = node->InputAt(i);
for (int i = 1; i < value_input_count; ++i) {
Node* const input = inputs[i];
if (input == node) {
// Ignore redundant inputs.
DCHECK_EQ(IrOpcode::kLoop, merge->opcode());
@ -300,20 +303,22 @@ Reduction CommonOperatorReducer::ReduceReturn(Node* node) {
effect->opcode() == IrOpcode::kEffectPhi &&
NodeProperties::GetControlInput(effect) == control &&
control->opcode() == IrOpcode::kMerge) {
int const control_input_count = control->InputCount();
DCHECK_NE(0, control_input_count);
DCHECK_EQ(control_input_count, value->InputCount() - 1);
DCHECK_EQ(control_input_count, effect->InputCount() - 1);
Node::Inputs control_inputs = control->inputs();
Node::Inputs value_inputs = value->inputs();
Node::Inputs effect_inputs = effect->inputs();
DCHECK_NE(0, control_inputs.count());
DCHECK_EQ(control_inputs.count(), value_inputs.count() - 1);
DCHECK_EQ(control_inputs.count(), effect_inputs.count() - 1);
DCHECK_EQ(IrOpcode::kEnd, graph()->end()->opcode());
DCHECK_NE(0, graph()->end()->InputCount());
for (int i = 0; i < control_input_count; ++i) {
for (int i = 0; i < control_inputs.count(); ++i) {
// Create a new {Return} and connect it to {end}. We don't need to mark
// {end} as revisit, because we mark {node} as {Dead} below, which was
// previously connected to {end}, so we know for sure that at some point
// the reducer logic will visit {end} again.
Node* ret = graph()->NewNode(common()->Return(), node->InputAt(0),
value->InputAt(i), effect->InputAt(i),
control->InputAt(i));
value_inputs[i], effect_inputs[i],
control_inputs[i]);
NodeProperties::MergeControlToEnd(graph(), common(), ret);
}
// Mark the merge {control} and return {node} as {dead}.

View File

@ -41,11 +41,11 @@ Reduction DeadCodeElimination::Reduce(Node* node) {
Reduction DeadCodeElimination::ReduceEnd(Node* node) {
DCHECK_EQ(IrOpcode::kEnd, node->opcode());
int const input_count = node->InputCount();
DCHECK_LE(1, input_count);
Node::Inputs inputs = node->inputs();
DCHECK_LE(1, inputs.count());
int live_input_count = 0;
for (int i = 0; i < input_count; ++i) {
Node* const input = node->InputAt(i);
for (int i = 0; i < inputs.count(); ++i) {
Node* const input = inputs[i];
// Skip dead inputs.
if (input->opcode() == IrOpcode::kDead) continue;
// Compact live inputs.
@ -54,20 +54,20 @@ Reduction DeadCodeElimination::ReduceEnd(Node* node) {
}
if (live_input_count == 0) {
return Replace(dead());
} else if (live_input_count < input_count) {
} else if (live_input_count < inputs.count()) {
node->TrimInputCount(live_input_count);
NodeProperties::ChangeOp(node, common()->End(live_input_count));
return Changed(node);
}
DCHECK_EQ(input_count, live_input_count);
DCHECK_EQ(inputs.count(), live_input_count);
return NoChange();
}
Reduction DeadCodeElimination::ReduceLoopOrMerge(Node* node) {
DCHECK(IrOpcode::IsMergeOpcode(node->opcode()));
int const input_count = node->InputCount();
DCHECK_LE(1, input_count);
Node::Inputs inputs = node->inputs();
DCHECK_LE(1, inputs.count());
// Count the number of live inputs to {node} and compact them on the fly, also
// compacting the inputs of the associated {Phi} and {EffectPhi} uses at the
// same time. We consider {Loop}s dead even if only the first control input
@ -75,8 +75,8 @@ Reduction DeadCodeElimination::ReduceLoopOrMerge(Node* node) {
int live_input_count = 0;
if (node->opcode() != IrOpcode::kLoop ||
node->InputAt(0)->opcode() != IrOpcode::kDead) {
for (int i = 0; i < input_count; ++i) {
Node* const input = node->InputAt(i);
for (int i = 0; i < inputs.count(); ++i) {
Node* const input = inputs[i];
// Skip dead inputs.
if (input->opcode() == IrOpcode::kDead) continue;
// Compact live inputs.
@ -84,7 +84,7 @@ Reduction DeadCodeElimination::ReduceLoopOrMerge(Node* node) {
node->ReplaceInput(live_input_count, input);
for (Node* const use : node->uses()) {
if (NodeProperties::IsPhi(use)) {
DCHECK_EQ(input_count + 1, use->InputCount());
DCHECK_EQ(inputs.count() + 1, use->InputCount());
use->ReplaceInput(live_input_count, use->InputAt(i));
}
}
@ -110,9 +110,9 @@ Reduction DeadCodeElimination::ReduceLoopOrMerge(Node* node) {
return Replace(node->InputAt(0));
}
DCHECK_LE(2, live_input_count);
DCHECK_LE(live_input_count, input_count);
DCHECK_LE(live_input_count, inputs.count());
// Trim input count for the {Merge} or {Loop} node.
if (live_input_count < input_count) {
if (live_input_count < inputs.count()) {
// Trim input counts for all phi uses and revisit them.
for (Node* const use : node->uses()) {
if (NodeProperties::IsPhi(use)) {

View File

@ -115,25 +115,21 @@ void GraphReducer::ReduceTop() {
if (node->IsDead()) return Pop(); // Node was killed while on stack.
// Recurse on an input if necessary.
int start = entry.input_index < node->InputCount() ? entry.input_index : 0;
Node::Inputs node_inputs = node->inputs();
auto node_inputs_begin = node_inputs.begin();
auto node_inputs_end = node_inputs.end();
DCHECK(node_inputs_end == node_inputs_begin + node->InputCount());
for (auto it = node_inputs_begin + start; it != node_inputs_end; ++it) {
Node* input = *it;
// Recurse on an input if necessary.
int start = entry.input_index < node_inputs.count() ? entry.input_index : 0;
for (int i = start; i < node_inputs.count(); ++i) {
Node* input = node_inputs[i];
if (input != node && Recurse(input)) {
entry.input_index = (it - node_inputs_begin) + 1;
entry.input_index = i + 1;
return;
}
}
for (auto it = node_inputs_begin; it != node_inputs_begin + start; ++it) {
Node* input = *it;
for (int i = 0; i < start; ++i) {
Node* input = node_inputs[i];
if (input != node && Recurse(input)) {
entry.input_index = (it - node_inputs_begin) + 1;
entry.input_index = i + 1;
return;
}
}
@ -152,12 +148,10 @@ void GraphReducer::ReduceTop() {
if (replacement == node) {
// In-place update of {node}, may need to recurse on an input.
Node::Inputs node_inputs = node->inputs();
auto node_inputs_begin = node_inputs.begin();
auto node_inputs_end = node_inputs.end();
for (auto it = node_inputs_begin; it != node_inputs_end; ++it) {
Node* input = *it;
for (int i = 0; i < node_inputs.count(); ++i) {
Node* input = node_inputs[i];
if (input != node && Recurse(input)) {
entry.input_index = (it - node_inputs_begin) + 1;
entry.input_index = i + 1;
return;
}
}

View File

@ -404,9 +404,6 @@ Node::InputEdges::iterator Node::InputEdges::iterator::operator++(int n) {
}
bool Node::InputEdges::empty() const { return begin() == end(); }
Node::Inputs::const_iterator Node::Inputs::const_iterator::operator++(int n) {
const_iterator result(*this);
++(*this);
@ -414,9 +411,6 @@ Node::Inputs::const_iterator Node::Inputs::const_iterator::operator++(int n) {
}
bool Node::Inputs::empty() const { return begin() == end(); }
Node::UseEdges::iterator Node::UseEdges::iterator::operator++(int n) {
iterator result(*this);
++(*this);

View File

@ -46,7 +46,7 @@ class V8_EXPORT_PRIVATE Node final {
Node* const* inputs, bool has_extensible_inputs);
static Node* Clone(Zone* zone, NodeId id, const Node* node);
bool IsDead() const { return InputCount() > 0 && !InputAt(0); }
inline bool IsDead() const;
void Kill();
const Operator* op() const { return op_; }
@ -109,41 +109,11 @@ class V8_EXPORT_PRIVATE Node final {
int UseCount() const;
void ReplaceUses(Node* replace_to);
class InputEdges final {
public:
typedef Edge value_type;
class InputEdges;
inline InputEdges input_edges();
class iterator;
inline iterator begin() const;
inline iterator end() const;
bool empty() const;
explicit InputEdges(Node* node) : node_(node) {}
private:
Node* node_;
};
InputEdges input_edges() { return InputEdges(this); }
class V8_EXPORT_PRIVATE Inputs final {
public:
typedef Node* value_type;
class const_iterator;
inline const_iterator begin() const;
inline const_iterator end() const;
bool empty() const;
explicit Inputs(Node* node) : node_(node) {}
private:
Node* node_;
};
Inputs inputs() { return Inputs(this); }
class Inputs;
inline Inputs inputs() const;
class UseEdges final {
public:
@ -345,6 +315,48 @@ static inline const T& OpParameter(const Node* node) {
return OpParameter<T>(node->op());
}
class Node::InputEdges final {
public:
typedef Edge value_type;
class iterator;
inline iterator begin() const;
inline iterator end() const;
bool empty() const { return count_ == 0; }
int count() const { return count_; }
inline value_type operator[](int index) const;
InputEdges(Node** input_root, Use* use_root, int count)
: input_root_(input_root), use_root_(use_root), count_(count) {}
private:
Node** input_root_;
Use* use_root_;
int count_;
};
class V8_EXPORT_PRIVATE Node::Inputs final {
public:
typedef Node* value_type;
class const_iterator;
inline const_iterator begin() const;
inline const_iterator end() const;
bool empty() const { return count_ == 0; }
int count() const { return count_; }
inline value_type operator[](int index) const;
explicit Inputs(Node* const* input_root, int count)
: input_root_(input_root), count_(count) {}
private:
Node* const* input_root_;
int count_;
};
// An encapsulation for information associated with a single use of node as a
// input from another node, allowing access to both the defining node and
@ -373,6 +385,7 @@ class Edge final {
private:
friend class Node::UseEdges::iterator;
friend class Node::InputEdges;
friend class Node::InputEdges::iterator;
Edge(Node::Use* use, Node** input_ptr) : use_(use), input_ptr_(input_ptr) {
@ -385,12 +398,37 @@ class Edge final {
Node** input_ptr_;
};
bool Node::IsDead() const {
Node::Inputs inputs = this->inputs();
return inputs.count() > 0 && inputs[0] == nullptr;
}
Node::InputEdges Node::input_edges() {
int inline_count = InlineCountField::decode(bit_field_);
if (inline_count != kOutlineMarker) {
return InputEdges(inputs_.inline_, reinterpret_cast<Use*>(this) - 1,
inline_count);
} else {
return InputEdges(inputs_.outline_->inputs_,
reinterpret_cast<Use*>(inputs_.outline_) - 1,
inputs_.outline_->count_);
}
}
Node::Inputs Node::inputs() const {
int inline_count = InlineCountField::decode(bit_field_);
if (inline_count != kOutlineMarker) {
return Inputs(inputs_.inline_, inline_count);
} else {
return Inputs(inputs_.outline_->inputs_, inputs_.outline_->count_);
}
}
// A forward iterator to visit the edges for the input dependencies of a node.
class Node::InputEdges::iterator final {
public:
typedef std::forward_iterator_tag iterator_category;
typedef int difference_type;
typedef std::ptrdiff_t difference_type;
typedef Edge value_type;
typedef Edge* pointer;
typedef Edge& reference;
@ -419,15 +457,12 @@ class Node::InputEdges::iterator final {
return iterator(use_ - offset, input_ptr_ + offset);
}
difference_type operator-(const iterator& other) const {
return static_cast<difference_type>(input_ptr_ - other.input_ptr_);
return input_ptr_ - other.input_ptr_;
}
private:
friend class Node;
explicit iterator(Node* from, int index = 0)
: use_(from->GetUsePtr(index)), input_ptr_(from->GetInputPtr(index)) {}
explicit iterator(Use* use, Node** input_ptr)
: use_(use), input_ptr_(input_ptr) {}
@ -437,69 +472,71 @@ class Node::InputEdges::iterator final {
Node::InputEdges::iterator Node::InputEdges::begin() const {
return Node::InputEdges::iterator(this->node_, 0);
return Node::InputEdges::iterator(use_root_, input_root_);
}
Node::InputEdges::iterator Node::InputEdges::end() const {
return Node::InputEdges::iterator(this->node_, this->node_->InputCount());
return Node::InputEdges::iterator(use_root_ - count_, input_root_ + count_);
}
Edge Node::InputEdges::operator[](int index) const {
return Edge(use_root_ + index, input_root_ + index);
}
// A forward iterator to visit the inputs of a node.
class Node::Inputs::const_iterator final {
public:
typedef std::forward_iterator_tag iterator_category;
typedef int difference_type;
typedef std::ptrdiff_t difference_type;
typedef Node* value_type;
typedef Node** pointer;
typedef Node*& reference;
typedef const value_type* pointer;
typedef value_type& reference;
const_iterator(const const_iterator& other) : iter_(other.iter_) {}
const_iterator(const const_iterator& other) : input_ptr_(other.input_ptr_) {}
Node* operator*() const { return (*iter_).to(); }
Node* operator*() const { return *input_ptr_; }
bool operator==(const const_iterator& other) const {
return iter_ == other.iter_;
return input_ptr_ == other.input_ptr_;
}
bool operator!=(const const_iterator& other) const {
return !(*this == other);
}
const_iterator& operator++() {
++iter_;
++input_ptr_;
return *this;
}
const_iterator operator++(int);
const_iterator& operator+=(difference_type offset) {
iter_ += offset;
input_ptr_ += offset;
return *this;
}
const_iterator operator+(difference_type offset) const {
return const_iterator(iter_ + offset);
return const_iterator(input_ptr_ + offset);
}
difference_type operator-(const const_iterator& other) const {
return iter_ - other.iter_;
return input_ptr_ - other.input_ptr_;
}
private:
friend class Node::Inputs;
const_iterator(Node* node, int index) : iter_(node, index) {}
explicit const_iterator(Node* const* input_ptr) : input_ptr_(input_ptr) {}
explicit const_iterator(Node::InputEdges::iterator iter) : iter_(iter) {}
Node::InputEdges::iterator iter_;
Node* const* input_ptr_;
};
Node::Inputs::const_iterator Node::Inputs::begin() const {
return const_iterator(this->node_, 0);
return const_iterator(input_root_);
}
Node::Inputs::const_iterator Node::Inputs::end() const {
return const_iterator(this->node_, this->node_->InputCount());
return const_iterator(input_root_ + count_);
}
Node* Node::Inputs::operator[](int index) const { return input_root_[index]; }
// A forward iterator to visit the uses edges of a node.
class Node::UseEdges::iterator final {