Commit 500d73b9 authored by Dan Elphick's avatar Dan Elphick Committed by Commit Bot

[compiler] Optimize AddInputsToFrameStateDescriptor

Optimizes InstructionSelector::AddInputsToFrameStateDescriptor by
taking advantage of SparseInputMask data structure to more quickly
handle empty inputs and insert all the OptimizedOut entries in one go.
The number of empty inputs is now determined using CountTrailingZeros
rather than iterating over them one at a time.

Gives a 9% improvement to SelectInstructions runtime call stat for
Octane in turboprop.

Bug: v8:10051
Change-Id: Ib13d6f9644b4c89ba0546a19fe0ed623d69fec99
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2037443
Commit-Queue: Dan Elphick <delphick@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66591}
parent 2d927fc3
...@@ -574,11 +574,7 @@ size_t InstructionSelector::AddOperandToStateValueDescriptor( ...@@ -574,11 +574,7 @@ size_t InstructionSelector::AddOperandToStateValueDescriptor(
StateValueList* values, InstructionOperandVector* inputs, StateValueList* values, InstructionOperandVector* inputs,
OperandGenerator* g, StateObjectDeduplicator* deduplicator, Node* input, OperandGenerator* g, StateObjectDeduplicator* deduplicator, Node* input,
MachineType type, FrameStateInputKind kind, Zone* zone) { MachineType type, FrameStateInputKind kind, Zone* zone) {
if (input == nullptr) { DCHECK_NOT_NULL(input);
values->PushOptimizedOut();
return 0;
}
switch (input->opcode()) { switch (input->opcode()) {
case IrOpcode::kArgumentsElementsState: { case IrOpcode::kArgumentsElementsState: {
values->PushArgumentsElements(ArgumentsStateTypeOf(input->op())); values->PushArgumentsElements(ArgumentsStateTypeOf(input->op()));
...@@ -636,6 +632,26 @@ size_t InstructionSelector::AddOperandToStateValueDescriptor( ...@@ -636,6 +632,26 @@ size_t InstructionSelector::AddOperandToStateValueDescriptor(
} }
} }
size_t InstructionSelector::AddInputsToFrameStateDescriptor(
StateValueList* values, InstructionOperandVector* inputs,
OperandGenerator* g, StateObjectDeduplicator* deduplicator, Node* node,
FrameStateInputKind kind, Zone* zone) {
size_t entries = 0;
StateValuesAccess::iterator it = StateValuesAccess(node).begin();
// Take advantage of sparse nature of StateValuesAccess to skip over multiple
// empty nodes at once pushing repeated OptimizedOuts all in one go.
while (!it.done()) {
values->PushOptimizedOut(it.AdvanceTillNotEmpty());
if (it.done()) break;
StateValuesAccess::TypedNode input_node = *it;
entries += AddOperandToStateValueDescriptor(values, inputs, g, deduplicator,
input_node.node,
input_node.type, kind, zone);
++it;
}
return entries;
}
// Returns the number of instruction operands added to inputs. // Returns the number of instruction operands added to inputs.
size_t InstructionSelector::AddInputsToFrameStateDescriptor( size_t InstructionSelector::AddInputsToFrameStateDescriptor(
FrameStateDescriptor* descriptor, Node* state, OperandGenerator* g, FrameStateDescriptor* descriptor, Node* state, OperandGenerator* g,
...@@ -669,30 +685,25 @@ size_t InstructionSelector::AddInputsToFrameStateDescriptor( ...@@ -669,30 +685,25 @@ size_t InstructionSelector::AddInputsToFrameStateDescriptor(
DCHECK_EQ(values_descriptor->size(), 0u); DCHECK_EQ(values_descriptor->size(), 0u);
values_descriptor->ReserveSize(descriptor->GetSize()); values_descriptor->ReserveSize(descriptor->GetSize());
DCHECK_NOT_NULL(function);
entries += AddOperandToStateValueDescriptor( entries += AddOperandToStateValueDescriptor(
values_descriptor, inputs, g, deduplicator, function, values_descriptor, inputs, g, deduplicator, function,
MachineType::AnyTagged(), FrameStateInputKind::kStackSlot, zone); MachineType::AnyTagged(), FrameStateInputKind::kStackSlot, zone);
for (StateValuesAccess::TypedNode input_node :
StateValuesAccess(parameters)) { entries += AddInputsToFrameStateDescriptor(
entries += AddOperandToStateValueDescriptor(values_descriptor, inputs, g, values_descriptor, inputs, g, deduplicator, parameters, kind, zone);
deduplicator, input_node.node,
input_node.type, kind, zone);
}
if (descriptor->HasContext()) { if (descriptor->HasContext()) {
DCHECK_NOT_NULL(context);
entries += AddOperandToStateValueDescriptor( entries += AddOperandToStateValueDescriptor(
values_descriptor, inputs, g, deduplicator, context, values_descriptor, inputs, g, deduplicator, context,
MachineType::AnyTagged(), FrameStateInputKind::kStackSlot, zone); MachineType::AnyTagged(), FrameStateInputKind::kStackSlot, zone);
} }
for (StateValuesAccess::TypedNode input_node : StateValuesAccess(locals)) {
entries += AddOperandToStateValueDescriptor(values_descriptor, inputs, g, entries += AddInputsToFrameStateDescriptor(values_descriptor, inputs, g,
deduplicator, input_node.node, deduplicator, locals, kind, zone);
input_node.type, kind, zone); entries += AddInputsToFrameStateDescriptor(values_descriptor, inputs, g,
} deduplicator, stack, kind, zone);
for (StateValuesAccess::TypedNode input_node : StateValuesAccess(stack)) {
entries += AddOperandToStateValueDescriptor(values_descriptor, inputs, g,
deduplicator, input_node.node,
input_node.type, kind, zone);
}
DCHECK_EQ(initial_size + entries, inputs->size()); DCHECK_EQ(initial_size + entries, inputs->size());
return entries; return entries;
} }
......
...@@ -576,6 +576,12 @@ class V8_EXPORT_PRIVATE InstructionSelector final { ...@@ -576,6 +576,12 @@ class V8_EXPORT_PRIVATE InstructionSelector final {
StateObjectDeduplicator* deduplicator, StateObjectDeduplicator* deduplicator,
InstructionOperandVector* inputs, InstructionOperandVector* inputs,
FrameStateInputKind kind, Zone* zone); FrameStateInputKind kind, Zone* zone);
size_t AddInputsToFrameStateDescriptor(StateValueList* values,
InstructionOperandVector* inputs,
OperandGenerator* g,
StateObjectDeduplicator* deduplicator,
Node* node, FrameStateInputKind kind,
Zone* zone);
size_t AddOperandToStateValueDescriptor(StateValueList* values, size_t AddOperandToStateValueDescriptor(StateValueList* values,
InstructionOperandVector* inputs, InstructionOperandVector* inputs,
OperandGenerator* g, OperandGenerator* g,
......
...@@ -1212,8 +1212,8 @@ class StateValueList { ...@@ -1212,8 +1212,8 @@ class StateValueList {
void PushPlain(MachineType type) { void PushPlain(MachineType type) {
fields_.push_back(StateValueDescriptor::Plain(type)); fields_.push_back(StateValueDescriptor::Plain(type));
} }
void PushOptimizedOut() { void PushOptimizedOut(size_t num = 1) {
fields_.push_back(StateValueDescriptor::OptimizedOut()); fields_.insert(fields_.end(), num, StateValueDescriptor::OptimizedOut());
} }
iterator begin() { return iterator(fields_.begin(), nested_.begin()); } iterator begin() { return iterator(fields_.begin(), nested_.begin()); }
......
...@@ -282,6 +282,15 @@ void SparseInputMask::InputIterator::Advance() { ...@@ -282,6 +282,15 @@ void SparseInputMask::InputIterator::Advance() {
bit_mask_ >>= 1; bit_mask_ >>= 1;
} }
size_t SparseInputMask::InputIterator::AdvanceToNextRealOrEnd() {
DCHECK_NE(bit_mask_, SparseInputMask::kDenseBitMask);
size_t count = base::bits::CountTrailingZeros(bit_mask_);
bit_mask_ >>= count;
DCHECK(IsReal() || IsEnd());
return count;
}
Node* SparseInputMask::InputIterator::GetReal() const { Node* SparseInputMask::InputIterator::GetReal() const {
DCHECK(IsReal()); DCHECK(IsReal());
return parent_->InputAt(real_index_); return parent_->InputAt(real_index_);
......
...@@ -273,6 +273,11 @@ class SparseInputMask final { ...@@ -273,6 +273,11 @@ class SparseInputMask final {
// current sparse input is real. // current sparse input is real.
Node* GetReal() const; Node* GetReal() const;
// Advance to the next real value or the end. Only valid if the iterator is
// not dense. Returns the number of empty values that were skipped. This can
// return 0 and in that case, it does not advance.
size_t AdvanceToNextRealOrEnd();
// Get the current sparse input, returning either a real input node if // Get the current sparse input, returning either a real input node if
// the current sparse input is real, or the given {empty_value} if the // the current sparse input is real, or the given {empty_value} if the
// current sparse input is empty. // current sparse input is empty.
......
...@@ -1421,9 +1421,9 @@ Node* JSCreateLowering::AllocateArguments(Node* effect, Node* control, ...@@ -1421,9 +1421,9 @@ Node* JSCreateLowering::AllocateArguments(Node* effect, Node* control,
a.AllocateArray(argument_count, a.AllocateArray(argument_count,
MapRef(broker(), factory()->fixed_array_map())); MapRef(broker(), factory()->fixed_array_map()));
for (int i = 0; i < argument_count; ++i, ++parameters_it) { for (int i = 0; i < argument_count; ++i, ++parameters_it) {
DCHECK_NOT_NULL((*parameters_it).node); DCHECK_NOT_NULL(parameters_it.node());
a.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i), a.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i),
(*parameters_it).node); parameters_it.node());
} }
return a.Finish(); return a.Finish();
} }
...@@ -1452,9 +1452,9 @@ Node* JSCreateLowering::AllocateRestArguments(Node* effect, Node* control, ...@@ -1452,9 +1452,9 @@ Node* JSCreateLowering::AllocateRestArguments(Node* effect, Node* control,
AllocationBuilder a(jsgraph(), effect, control); AllocationBuilder a(jsgraph(), effect, control);
a.AllocateArray(num_elements, MapRef(broker(), factory()->fixed_array_map())); a.AllocateArray(num_elements, MapRef(broker(), factory()->fixed_array_map()));
for (int i = 0; i < num_elements; ++i, ++parameters_it) { for (int i = 0; i < num_elements; ++i, ++parameters_it) {
DCHECK_NOT_NULL((*parameters_it).node); DCHECK_NOT_NULL(parameters_it.node());
a.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i), a.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i),
(*parameters_it).node); parameters_it.node());
} }
return a.Finish(); return a.Finish();
} }
...@@ -1496,9 +1496,9 @@ Node* JSCreateLowering::AllocateAliasedArguments( ...@@ -1496,9 +1496,9 @@ Node* JSCreateLowering::AllocateAliasedArguments(
jsgraph()->TheHoleConstant()); jsgraph()->TheHoleConstant());
} }
for (int i = mapped_count; i < argument_count; ++i, ++parameters_it) { for (int i = mapped_count; i < argument_count; ++i, ++parameters_it) {
DCHECK_NOT_NULL((*parameters_it).node); DCHECK_NOT_NULL(parameters_it.node());
aa.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i), aa.Store(AccessBuilder::ForFixedArrayElement(), jsgraph()->Constant(i),
(*parameters_it).node); parameters_it.node());
} }
Node* arguments = aa.Finish(); Node* arguments = aa.Finish();
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "src/compiler/state-values-utils.h" #include "src/compiler/state-values-utils.h"
#include "src/compiler/common-operator.h"
#include "src/utils/bit-vector.h" #include "src/utils/bit-vector.h"
namespace v8 { namespace v8 {
...@@ -240,9 +241,9 @@ void CheckTreeContainsValues(Node* tree, Node** values, size_t count, ...@@ -240,9 +241,9 @@ void CheckTreeContainsValues(Node* tree, Node** values, size_t count,
auto itend = access.end(); auto itend = access.end();
for (i = 0; it != itend; ++it, ++i) { for (i = 0; it != itend; ++it, ++i) {
if (liveness == nullptr || liveness->Contains(liveness_offset + i)) { if (liveness == nullptr || liveness->Contains(liveness_offset + i)) {
DCHECK_EQ((*it).node, values[i]); DCHECK_EQ(it.node(), values[i]);
} else { } else {
DCHECK_NULL((*it).node); DCHECK_NULL(it.node());
} }
} }
DCHECK_EQ(static_cast<size_t>(i), count); DCHECK_EQ(static_cast<size_t>(i), count);
...@@ -329,13 +330,20 @@ void StateValuesAccess::iterator::Pop() { ...@@ -329,13 +330,20 @@ void StateValuesAccess::iterator::Pop() {
current_depth_--; current_depth_--;
} }
bool StateValuesAccess::iterator::done() const { return current_depth_ < 0; }
void StateValuesAccess::iterator::Advance() { void StateValuesAccess::iterator::Advance() {
Top()->Advance(); Top()->Advance();
EnsureValid(); EnsureValid();
} }
size_t StateValuesAccess::iterator::AdvanceTillNotEmpty() {
size_t count = 0;
while (!done() && Top()->IsEmpty()) {
count += Top()->AdvanceToNextRealOrEnd();
EnsureValid();
}
return count;
}
void StateValuesAccess::iterator::EnsureValid() { void StateValuesAccess::iterator::EnsureValid() {
while (true) { while (true) {
SparseInputMask::InputIterator* top = Top(); SparseInputMask::InputIterator* top = Top();
...@@ -376,21 +384,18 @@ Node* StateValuesAccess::iterator::node() { return Top()->Get(nullptr); } ...@@ -376,21 +384,18 @@ Node* StateValuesAccess::iterator::node() { return Top()->Get(nullptr); }
MachineType StateValuesAccess::iterator::type() { MachineType StateValuesAccess::iterator::type() {
Node* parent = Top()->parent(); Node* parent = Top()->parent();
DCHECK(!Top()->IsEmpty());
if (parent->opcode() == IrOpcode::kStateValues) { if (parent->opcode() == IrOpcode::kStateValues) {
return MachineType::AnyTagged(); return MachineType::AnyTagged();
} else { } else {
DCHECK_EQ(IrOpcode::kTypedStateValues, parent->opcode()); DCHECK_EQ(IrOpcode::kTypedStateValues, parent->opcode());
if (Top()->IsEmpty()) { ZoneVector<MachineType> const* types = MachineTypesOf(parent->op());
return MachineType::None(); return (*types)[Top()->real_index()];
} else {
ZoneVector<MachineType> const* types = MachineTypesOf(parent->op());
return (*types)[Top()->real_index()];
}
} }
} }
bool StateValuesAccess::iterator::operator!=(iterator const& other) { bool StateValuesAccess::iterator::operator!=(iterator const& other) const {
// We only allow comparison with end(). // We only allow comparison with end().
CHECK(other.done()); CHECK(other.done());
return !done(); return !done();
...@@ -406,8 +411,7 @@ StateValuesAccess::TypedNode StateValuesAccess::iterator::operator*() { ...@@ -406,8 +411,7 @@ StateValuesAccess::TypedNode StateValuesAccess::iterator::operator*() {
return TypedNode(node(), type()); return TypedNode(node(), type());
} }
size_t StateValuesAccess::size() const {
size_t StateValuesAccess::size() {
size_t count = 0; size_t count = 0;
SparseInputMask mask = SparseInputMaskOf(node_->op()); SparseInputMask mask = SparseInputMaskOf(node_->op());
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define V8_COMPILER_STATE_VALUES_UTILS_H_ #define V8_COMPILER_STATE_VALUES_UTILS_H_
#include <array> #include <array>
#include "src/common/globals.h" #include "src/common/globals.h"
#include "src/compiler/common-operator.h" #include "src/compiler/common-operator.h"
#include "src/compiler/js-graph.h" #include "src/compiler/js-graph.h"
...@@ -91,20 +92,23 @@ class V8_EXPORT_PRIVATE StateValuesAccess { ...@@ -91,20 +92,23 @@ class V8_EXPORT_PRIVATE StateValuesAccess {
class V8_EXPORT_PRIVATE iterator { class V8_EXPORT_PRIVATE iterator {
public: public:
// Bare minimum of operators needed for range iteration. bool operator!=(iterator const& other) const;
bool operator!=(iterator const& other);
iterator& operator++(); iterator& operator++();
TypedNode operator*(); TypedNode operator*();
Node* node();
bool done() const { return current_depth_ < 0; }
// Returns the number of empty nodes that were skipped over.
size_t AdvanceTillNotEmpty();
private: private:
friend class StateValuesAccess; friend class StateValuesAccess;
iterator() : current_depth_(-1) {} iterator() : current_depth_(-1) {}
explicit iterator(Node* node); explicit iterator(Node* node);
Node* node();
MachineType type(); MachineType type();
bool done() const;
void Advance(); void Advance();
void EnsureValid(); void EnsureValid();
...@@ -119,9 +123,9 @@ class V8_EXPORT_PRIVATE StateValuesAccess { ...@@ -119,9 +123,9 @@ class V8_EXPORT_PRIVATE StateValuesAccess {
explicit StateValuesAccess(Node* node) : node_(node) {} explicit StateValuesAccess(Node* node) : node_(node) {}
size_t size(); size_t size() const;
iterator begin() { return iterator(node_); } iterator begin() const { return iterator(node_); }
iterator end() { return iterator(); } iterator end() const { return iterator(); }
private: private:
Node* node_; Node* node_;
......
...@@ -153,11 +153,13 @@ TEST_F(StateValuesIteratorTest, TreeFromVectorWithLiveness) { ...@@ -153,11 +153,13 @@ TEST_F(StateValuesIteratorTest, TreeFromVectorWithLiveness) {
// Check the tree contents with vector. // Check the tree contents with vector.
int i = 0; int i = 0;
for (StateValuesAccess::TypedNode node : StateValuesAccess(values_node)) { for (StateValuesAccess::iterator it =
StateValuesAccess(values_node).begin();
!it.done(); ++it) {
if (liveness.Contains(i)) { if (liveness.Contains(i)) {
EXPECT_THAT(node.node, IsInt32Constant(i)); EXPECT_THAT(it.node(), IsInt32Constant(i));
} else { } else {
EXPECT_EQ(node.node, nullptr); EXPECT_EQ(it.node(), nullptr);
} }
i++; i++;
} }
......
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