Commit 68f1a37f authored by leszeks's avatar leszeks Committed by Commit bot

[turbofan] Sparse representation for state values

Add a more efficient encoding for state values that have a large number of
optimized-out inputs.

Review-Url: https://codereview.chromium.org/2509623002
Cr-Commit-Position: refs/heads/master@{#42088}
parent 8bb2501e
...@@ -670,7 +670,7 @@ void AstGraphBuilder::Environment::UpdateStateValues(Node** state_values, ...@@ -670,7 +670,7 @@ void AstGraphBuilder::Environment::UpdateStateValues(Node** state_values,
} }
} }
if (should_update) { if (should_update) {
const Operator* op = common()->StateValues(count); const Operator* op = common()->StateValues(count, SparseInputMask::Dense());
(*state_values) = graph()->NewNode(op, count, env_values); (*state_values) = graph()->NewNode(op, count, env_values);
} }
} }
......
...@@ -81,8 +81,8 @@ class BytecodeGraphBuilder::Environment : public ZoneObject { ...@@ -81,8 +81,8 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
bool StateValuesRequireUpdate(Node** state_values, Node** values, int count); bool StateValuesRequireUpdate(Node** state_values, Node** values, int count);
void UpdateStateValues(Node** state_values, Node** values, int count); void UpdateStateValues(Node** state_values, Node** values, int count);
void UpdateStateValuesWithCache(Node** state_values, Node** values, void UpdateStateValuesWithCache(Node** state_values, Node** values, int count,
int count); const BitVector* liveness);
int RegisterToValuesIndex(interpreter::Register the_register) const; int RegisterToValuesIndex(interpreter::Register the_register) const;
...@@ -107,10 +107,6 @@ class BytecodeGraphBuilder::Environment : public ZoneObject { ...@@ -107,10 +107,6 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
Node* accumulator_state_values_; Node* accumulator_state_values_;
int register_base_; int register_base_;
int accumulator_base_; int accumulator_base_;
// A working area for writing maybe-dead values to when updating the state
// values for registers.
NodeVector state_value_working_area_;
}; };
...@@ -131,8 +127,7 @@ BytecodeGraphBuilder::Environment::Environment(BytecodeGraphBuilder* builder, ...@@ -131,8 +127,7 @@ BytecodeGraphBuilder::Environment::Environment(BytecodeGraphBuilder* builder,
values_(builder->local_zone()), values_(builder->local_zone()),
parameters_state_values_(nullptr), parameters_state_values_(nullptr),
registers_state_values_(nullptr), registers_state_values_(nullptr),
accumulator_state_values_(nullptr), accumulator_state_values_(nullptr) {
state_value_working_area_(builder->local_zone()) {
// The layout of values_ is: // The layout of values_ is:
// //
// [receiver] [parameters] [registers] [accumulator] // [receiver] [parameters] [registers] [accumulator]
...@@ -157,8 +152,6 @@ BytecodeGraphBuilder::Environment::Environment(BytecodeGraphBuilder* builder, ...@@ -157,8 +152,6 @@ BytecodeGraphBuilder::Environment::Environment(BytecodeGraphBuilder* builder,
// Accumulator // Accumulator
accumulator_base_ = static_cast<int>(values()->size()); accumulator_base_ = static_cast<int>(values()->size());
values()->push_back(undefined_constant); values()->push_back(undefined_constant);
state_value_working_area_.resize(register_count_);
} }
BytecodeGraphBuilder::Environment::Environment( BytecodeGraphBuilder::Environment::Environment(
...@@ -174,9 +167,7 @@ BytecodeGraphBuilder::Environment::Environment( ...@@ -174,9 +167,7 @@ BytecodeGraphBuilder::Environment::Environment(
registers_state_values_(nullptr), registers_state_values_(nullptr),
accumulator_state_values_(nullptr), accumulator_state_values_(nullptr),
register_base_(other->register_base_), register_base_(other->register_base_),
accumulator_base_(other->accumulator_base_), accumulator_base_(other->accumulator_base_) {
// Environments can share their working area.
state_value_working_area_(other->state_value_working_area_) {
values_ = other->values_; values_ = other->values_;
} }
...@@ -412,15 +403,15 @@ void BytecodeGraphBuilder::Environment::UpdateStateValues(Node** state_values, ...@@ -412,15 +403,15 @@ void BytecodeGraphBuilder::Environment::UpdateStateValues(Node** state_values,
Node** values, Node** values,
int count) { int count) {
if (StateValuesRequireUpdate(state_values, values, count)) { if (StateValuesRequireUpdate(state_values, values, count)) {
const Operator* op = common()->StateValues(count); const Operator* op = common()->StateValues(count, SparseInputMask::Dense());
(*state_values) = graph()->NewNode(op, count, values); (*state_values) = graph()->NewNode(op, count, values);
} }
} }
void BytecodeGraphBuilder::Environment::UpdateStateValuesWithCache( void BytecodeGraphBuilder::Environment::UpdateStateValuesWithCache(
Node** state_values, Node** values, int count) { Node** state_values, Node** values, int count, const BitVector* liveness) {
*state_values = builder_->state_values_cache_.GetNodeForValues( *state_values = builder_->state_values_cache_.GetNodeForValues(
values, static_cast<size_t>(count)); values, static_cast<size_t>(count), liveness);
} }
Node* BytecodeGraphBuilder::Environment::Checkpoint( Node* BytecodeGraphBuilder::Environment::Checkpoint(
...@@ -429,31 +420,17 @@ Node* BytecodeGraphBuilder::Environment::Checkpoint( ...@@ -429,31 +420,17 @@ Node* BytecodeGraphBuilder::Environment::Checkpoint(
UpdateStateValues(&parameters_state_values_, &values()->at(0), UpdateStateValues(&parameters_state_values_, &values()->at(0),
parameter_count()); parameter_count());
if (liveness) { // TODO(leszeks): We should pass a view of the liveness bitvector here, with
Node* optimized_out = builder()->jsgraph()->OptimizedOutConstant(); // offset and count, rather than passing the entire bitvector and assuming
// that register liveness starts at offset 0.
for (int i = 0; i < register_count(); ++i) {
state_value_working_area_[i] = liveness->RegisterIsLive(i)
? values()->at(register_base() + i)
: optimized_out;
}
Node* accumulator_value = liveness->AccumulatorIsLive()
? values()->at(accumulator_base())
: optimized_out;
UpdateStateValuesWithCache(&registers_state_values_, UpdateStateValuesWithCache(&registers_state_values_,
state_value_working_area_.data(), &values()->at(register_base()), register_count(),
register_count()); liveness ? &liveness->bit_vector() : nullptr);
Node* accumulator_value = liveness == nullptr || liveness->AccumulatorIsLive()
? values()->at(accumulator_base())
: builder()->jsgraph()->OptimizedOutConstant();
UpdateStateValues(&accumulator_state_values_, &accumulator_value, 1); UpdateStateValues(&accumulator_state_values_, &accumulator_value, 1);
} else {
UpdateStateValuesWithCache(&registers_state_values_,
&values()->at(register_base()),
register_count());
UpdateStateValues(&accumulator_state_values_,
&values()->at(accumulator_base()), 1);
}
const Operator* op = common()->FrameState( const Operator* op = common()->FrameState(
bailout_id, combine, builder()->frame_state_function_info()); bailout_id, combine, builder()->frame_state_function_info());
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "src/assembler.h" #include "src/assembler.h"
#include "src/base/lazy-instance.h" #include "src/base/lazy-instance.h"
#include "src/compiler/linkage.h" #include "src/compiler/linkage.h"
#include "src/compiler/node.h"
#include "src/compiler/opcodes.h" #include "src/compiler/opcodes.h"
#include "src/compiler/operator.h" #include "src/compiler/operator.h"
#include "src/handles-inl.h" #include "src/handles-inl.h"
...@@ -171,6 +172,106 @@ std::ostream& operator<<(std::ostream& os, ...@@ -171,6 +172,106 @@ std::ostream& operator<<(std::ostream& os,
return os << p.value() << "|" << p.rmode() << "|" << p.type(); return os << p.value() << "|" << p.rmode() << "|" << p.type();
} }
SparseInputMask::InputIterator::InputIterator(
SparseInputMask::BitMaskType bit_mask, Node* parent)
: bit_mask_(bit_mask), parent_(parent), real_index_(0) {
#if DEBUG
if (bit_mask_ != SparseInputMask::kDenseBitMask) {
DCHECK_EQ(base::bits::CountPopulation(bit_mask_) -
base::bits::CountPopulation(kEndMarker),
parent->InputCount());
}
#endif
}
void SparseInputMask::InputIterator::Advance() {
DCHECK(!IsEnd());
if (IsReal()) {
++real_index_;
}
bit_mask_ >>= 1;
}
Node* SparseInputMask::InputIterator::GetReal() const {
DCHECK(IsReal());
return parent_->InputAt(real_index_);
}
bool SparseInputMask::InputIterator::IsReal() const {
return bit_mask_ == SparseInputMask::kDenseBitMask ||
(bit_mask_ & kEntryMask);
}
bool SparseInputMask::InputIterator::IsEnd() const {
return (bit_mask_ == kEndMarker) ||
(bit_mask_ == SparseInputMask::kDenseBitMask &&
real_index_ >= parent_->InputCount());
}
int SparseInputMask::CountReal() const {
DCHECK(!IsDense());
return base::bits::CountPopulation(bit_mask_) -
base::bits::CountPopulation(kEndMarker);
}
SparseInputMask::InputIterator SparseInputMask::IterateOverInputs(Node* node) {
DCHECK(IsDense() || CountReal() == node->InputCount());
return InputIterator(bit_mask_, node);
}
bool operator==(SparseInputMask const& lhs, SparseInputMask const& rhs) {
return lhs.mask() == rhs.mask();
}
bool operator!=(SparseInputMask const& lhs, SparseInputMask const& rhs) {
return !(lhs == rhs);
}
size_t hash_value(SparseInputMask const& p) {
return base::hash_value(p.mask());
}
std::ostream& operator<<(std::ostream& os, SparseInputMask const& p) {
if (p.IsDense()) {
return os << "dense";
} else {
SparseInputMask::BitMaskType mask = p.mask();
DCHECK_NE(mask, SparseInputMask::kDenseBitMask);
os << "sparse:";
while (mask != SparseInputMask::kEndMarker) {
if (mask & SparseInputMask::kEntryMask) {
os << "^";
} else {
os << ".";
}
mask >>= 1;
}
return os;
}
}
bool operator==(TypedStateValueInfo const& lhs,
TypedStateValueInfo const& rhs) {
return lhs.machine_types() == rhs.machine_types() &&
lhs.sparse_input_mask() == rhs.sparse_input_mask();
}
bool operator!=(TypedStateValueInfo const& lhs,
TypedStateValueInfo const& rhs) {
return !(lhs == rhs);
}
size_t hash_value(TypedStateValueInfo const& p) {
return base::hash_combine(p.machine_types(), p.sparse_input_mask());
}
std::ostream& operator<<(std::ostream& os, TypedStateValueInfo const& p) {
return os << p.machine_types() << "|" << p.sparse_input_mask();
}
size_t hash_value(RegionObservability observability) { size_t hash_value(RegionObservability observability) {
return static_cast<size_t>(observability); return static_cast<size_t>(observability);
} }
...@@ -235,9 +336,23 @@ OsrGuardType OsrGuardTypeOf(Operator const* op) { ...@@ -235,9 +336,23 @@ OsrGuardType OsrGuardTypeOf(Operator const* op) {
return OpParameter<OsrGuardType>(op); return OpParameter<OsrGuardType>(op);
} }
SparseInputMask SparseInputMaskOf(Operator const* op) {
DCHECK(op->opcode() == IrOpcode::kStateValues ||
op->opcode() == IrOpcode::kTypedStateValues);
if (op->opcode() == IrOpcode::kTypedStateValues) {
return OpParameter<TypedStateValueInfo>(op).sparse_input_mask();
}
return OpParameter<SparseInputMask>(op);
}
ZoneVector<MachineType> const* MachineTypesOf(Operator const* op) { ZoneVector<MachineType> const* MachineTypesOf(Operator const* op) {
DCHECK(op->opcode() == IrOpcode::kTypedObjectState || DCHECK(op->opcode() == IrOpcode::kTypedObjectState ||
op->opcode() == IrOpcode::kTypedStateValues); op->opcode() == IrOpcode::kTypedStateValues);
if (op->opcode() == IrOpcode::kTypedStateValues) {
return OpParameter<TypedStateValueInfo>(op).machine_types();
}
return OpParameter<const ZoneVector<MachineType>*>(op); return OpParameter<const ZoneVector<MachineType>*>(op);
} }
...@@ -635,13 +750,14 @@ struct CommonOperatorGlobalCache final { ...@@ -635,13 +750,14 @@ struct CommonOperatorGlobalCache final {
#undef CACHED_PROJECTION #undef CACHED_PROJECTION
template <int kInputCount> template <int kInputCount>
struct StateValuesOperator final : public Operator { struct StateValuesOperator final : public Operator1<SparseInputMask> {
StateValuesOperator() StateValuesOperator()
: Operator( // -- : Operator1<SparseInputMask>( // --
IrOpcode::kStateValues, // opcode IrOpcode::kStateValues, // opcode
Operator::kPure, // flags Operator::kPure, // flags
"StateValues", // name "StateValues", // name
kInputCount, 0, 0, 1, 0, 0) {} // counts kInputCount, 0, 0, 1, 0, 0, // counts
SparseInputMask::Dense()) {} // parameter
}; };
#define CACHED_STATE_VALUES(input_count) \ #define CACHED_STATE_VALUES(input_count) \
StateValuesOperator<input_count> kStateValues##input_count##Operator; StateValuesOperator<input_count> kStateValues##input_count##Operator;
...@@ -1084,7 +1200,9 @@ const Operator* CommonOperatorBuilder::BeginRegion( ...@@ -1084,7 +1200,9 @@ const Operator* CommonOperatorBuilder::BeginRegion(
return nullptr; return nullptr;
} }
const Operator* CommonOperatorBuilder::StateValues(int arguments) { const Operator* CommonOperatorBuilder::StateValues(int arguments,
SparseInputMask bitmask) {
if (bitmask.IsDense()) {
switch (arguments) { switch (arguments) {
#define CACHED_STATE_VALUES(arguments) \ #define CACHED_STATE_VALUES(arguments) \
case arguments: \ case arguments: \
...@@ -1094,20 +1212,32 @@ const Operator* CommonOperatorBuilder::StateValues(int arguments) { ...@@ -1094,20 +1212,32 @@ const Operator* CommonOperatorBuilder::StateValues(int arguments) {
default: default:
break; break;
} }
}
#if DEBUG
DCHECK(bitmask.IsDense() || bitmask.CountReal() == arguments);
#endif
// Uncached. // Uncached.
return new (zone()) Operator( // -- return new (zone()) Operator1<SparseInputMask>( // --
IrOpcode::kStateValues, Operator::kPure, // opcode IrOpcode::kStateValues, Operator::kPure, // opcode
"StateValues", // name "StateValues", // name
arguments, 0, 0, 1, 0, 0); // counts arguments, 0, 0, 1, 0, 0, // counts
bitmask); // parameter
} }
const Operator* CommonOperatorBuilder::TypedStateValues( const Operator* CommonOperatorBuilder::TypedStateValues(
const ZoneVector<MachineType>* types) { const ZoneVector<MachineType>* types, SparseInputMask bitmask) {
return new (zone()) Operator1<const ZoneVector<MachineType>*>( // -- #if DEBUG
DCHECK(bitmask.IsDense() ||
bitmask.CountReal() == static_cast<int>(types->size()));
#endif
return new (zone()) Operator1<TypedStateValueInfo>( // --
IrOpcode::kTypedStateValues, Operator::kPure, // opcode IrOpcode::kTypedStateValues, Operator::kPure, // opcode
"TypedStateValues", // name "TypedStateValues", // name
static_cast<int>(types->size()), 0, 0, 1, 0, 0, // counts static_cast<int>(types->size()), 0, 0, 1, 0, 0, // counts
types); // parameter TypedStateValueInfo(types, bitmask)); // parameters
} }
const Operator* CommonOperatorBuilder::ObjectState(int pointer_slots) { const Operator* CommonOperatorBuilder::ObjectState(int pointer_slots) {
......
...@@ -22,6 +22,7 @@ class CallDescriptor; ...@@ -22,6 +22,7 @@ class CallDescriptor;
struct CommonOperatorGlobalCache; struct CommonOperatorGlobalCache;
class Operator; class Operator;
class Type; class Type;
class Node;
// Prediction hint for branches. // Prediction hint for branches.
enum class BranchHint : uint8_t { kNone, kTrue, kFalse }; enum class BranchHint : uint8_t { kNone, kTrue, kFalse };
...@@ -158,6 +159,123 @@ std::ostream& operator<<(std::ostream&, RelocatablePtrConstantInfo const&); ...@@ -158,6 +159,123 @@ std::ostream& operator<<(std::ostream&, RelocatablePtrConstantInfo const&);
size_t hash_value(RelocatablePtrConstantInfo const& p); size_t hash_value(RelocatablePtrConstantInfo const& p);
// Used to define a sparse set of inputs. This can be used to efficiently encode
// nodes that can have a lot of inputs, but where many inputs can have the same
// value.
class SparseInputMask final {
public:
typedef uint32_t BitMaskType;
// The mask representing a dense input set.
static const BitMaskType kDenseBitMask = 0x0;
// The bits representing the end of a sparse input set.
static const BitMaskType kEndMarker = 0x1;
// The mask for accessing a sparse input entry in the bitmask.
static const BitMaskType kEntryMask = 0x1;
// The number of bits in the mask, minus one for the end marker.
static const int kMaxSparseInputs = (sizeof(BitMaskType) * kBitsPerByte - 1);
// An iterator over a node's sparse inputs.
class InputIterator final {
public:
InputIterator() {}
InputIterator(BitMaskType bit_mask, Node* parent);
Node* parent() const { return parent_; }
int real_index() const { return real_index_; }
// Advance the iterator to the next sparse input. Only valid if the iterator
// has not reached the end.
void Advance();
// Get the current sparse input's real node value. Only valid if the
// current sparse input is real.
Node* GetReal() const;
// 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
// current sparse input is empty.
Node* Get(Node* empty_value) const {
return IsReal() ? GetReal() : empty_value;
}
// True if the current sparse input is a real input node.
bool IsReal() const;
// True if the current sparse input is an empty value.
bool IsEmpty() const { return !IsReal(); }
// True if the iterator has reached the end of the sparse inputs.
bool IsEnd() const;
private:
BitMaskType bit_mask_;
Node* parent_;
int real_index_;
};
explicit SparseInputMask(BitMaskType bit_mask) : bit_mask_(bit_mask) {}
// Provides a SparseInputMask representing a dense input set.
static SparseInputMask Dense() { return SparseInputMask(kDenseBitMask); }
BitMaskType mask() const { return bit_mask_; }
bool IsDense() const { return bit_mask_ == SparseInputMask::kDenseBitMask; }
// Counts how many real values are in the sparse array. Only valid for
// non-dense masks.
int CountReal() const;
// Returns an iterator over the sparse inputs of {node}.
InputIterator IterateOverInputs(Node* node);
private:
//
// The sparse input mask has a bitmask specifying if the node's inputs are
// represented sparsely. If the bitmask value is 0, then the inputs are dense;
// otherwise, they should be interpreted as follows:
//
// * The bitmask represents which values are real, with 1 for real values
// and 0 for empty values.
// * The inputs to the node are the real values, in the order of the 1s from
// least- to most-significant.
// * The top bit of the bitmask is a guard indicating the end of the values,
// whether real or empty (and is not representative of a real input
// itself). This is used so that we don't have to additionally store a
// value count.
//
// So, for N 1s in the bitmask, there are N - 1 inputs into the node.
BitMaskType bit_mask_;
};
bool operator==(SparseInputMask const& lhs, SparseInputMask const& rhs);
bool operator!=(SparseInputMask const& lhs, SparseInputMask const& rhs);
class TypedStateValueInfo final {
public:
TypedStateValueInfo(ZoneVector<MachineType> const* machine_types,
SparseInputMask sparse_input_mask)
: machine_types_(machine_types), sparse_input_mask_(sparse_input_mask) {}
ZoneVector<MachineType> const* machine_types() const {
return machine_types_;
}
SparseInputMask sparse_input_mask() const { return sparse_input_mask_; }
private:
ZoneVector<MachineType> const* machine_types_;
SparseInputMask sparse_input_mask_;
};
bool operator==(TypedStateValueInfo const& lhs, TypedStateValueInfo const& rhs);
bool operator!=(TypedStateValueInfo const& lhs, TypedStateValueInfo const& rhs);
std::ostream& operator<<(std::ostream&, TypedStateValueInfo const&);
size_t hash_value(TypedStateValueInfo const& p);
// Used to mark a region (as identified by BeginRegion/FinishRegion) as either // Used to mark a region (as identified by BeginRegion/FinishRegion) as either
// JavaScript-observable or not (i.e. allocations are not JavaScript observable // JavaScript-observable or not (i.e. allocations are not JavaScript observable
// themselves, but transitioning stores are). // themselves, but transitioning stores are).
...@@ -181,6 +299,8 @@ size_t hash_value(OsrGuardType type); ...@@ -181,6 +299,8 @@ size_t hash_value(OsrGuardType type);
std::ostream& operator<<(std::ostream&, OsrGuardType); std::ostream& operator<<(std::ostream&, OsrGuardType);
OsrGuardType OsrGuardTypeOf(Operator const*); OsrGuardType OsrGuardTypeOf(Operator const*);
SparseInputMask SparseInputMaskOf(Operator const*);
ZoneVector<MachineType> const* MachineTypesOf(Operator const*) ZoneVector<MachineType> const* MachineTypesOf(Operator const*)
WARN_UNUSED_RESULT; WARN_UNUSED_RESULT;
...@@ -245,8 +365,9 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final ...@@ -245,8 +365,9 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final
const Operator* Checkpoint(); const Operator* Checkpoint();
const Operator* BeginRegion(RegionObservability); const Operator* BeginRegion(RegionObservability);
const Operator* FinishRegion(); const Operator* FinishRegion();
const Operator* StateValues(int arguments); const Operator* StateValues(int arguments, SparseInputMask bitmask);
const Operator* TypedStateValues(const ZoneVector<MachineType>* types); const Operator* TypedStateValues(const ZoneVector<MachineType>* types,
SparseInputMask bitmask);
const Operator* ObjectState(int pointer_slots); const Operator* ObjectState(int pointer_slots);
const Operator* TypedObjectState(const ZoneVector<MachineType>* types); const Operator* TypedObjectState(const ZoneVector<MachineType>* types);
const Operator* FrameState(BailoutId bailout_id, const Operator* FrameState(BailoutId bailout_id,
......
...@@ -501,6 +501,11 @@ size_t InstructionSelector::AddOperandToStateValueDescriptor( ...@@ -501,6 +501,11 @@ 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) {
values->PushOptimizedOut();
return 0;
}
switch (input->opcode()) { switch (input->opcode()) {
case IrOpcode::kObjectState: { case IrOpcode::kObjectState: {
UNREACHABLE(); UNREACHABLE();
......
...@@ -1030,6 +1030,7 @@ Node* JSCreateLowering::AllocateArguments(Node* effect, Node* control, ...@@ -1030,6 +1030,7 @@ Node* JSCreateLowering::AllocateArguments(Node* effect, Node* control,
AllocationBuilder a(jsgraph(), effect, control); AllocationBuilder a(jsgraph(), effect, control);
a.AllocateArray(argument_count, factory()->fixed_array_map()); a.AllocateArray(argument_count, 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);
a.Store(AccessBuilder::ForFixedArraySlot(i), (*parameters_it).node); a.Store(AccessBuilder::ForFixedArraySlot(i), (*parameters_it).node);
} }
return a.Finish(); return a.Finish();
...@@ -1059,6 +1060,7 @@ Node* JSCreateLowering::AllocateRestArguments(Node* effect, Node* control, ...@@ -1059,6 +1060,7 @@ Node* JSCreateLowering::AllocateRestArguments(Node* effect, Node* control,
AllocationBuilder a(jsgraph(), effect, control); AllocationBuilder a(jsgraph(), effect, control);
a.AllocateArray(num_elements, factory()->fixed_array_map()); a.AllocateArray(num_elements, 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);
a.Store(AccessBuilder::ForFixedArraySlot(i), (*parameters_it).node); a.Store(AccessBuilder::ForFixedArraySlot(i), (*parameters_it).node);
} }
return a.Finish(); return a.Finish();
...@@ -1088,18 +1090,19 @@ Node* JSCreateLowering::AllocateAliasedArguments( ...@@ -1088,18 +1090,19 @@ Node* JSCreateLowering::AllocateAliasedArguments(
// Prepare an iterator over argument values recorded in the frame state. // Prepare an iterator over argument values recorded in the frame state.
Node* const parameters = frame_state->InputAt(kFrameStateParametersInput); Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
StateValuesAccess parameters_access(parameters); StateValuesAccess parameters_access(parameters);
auto paratemers_it = ++parameters_access.begin(); auto parameters_it = ++parameters_access.begin();
// The unmapped argument values recorded in the frame state are stored yet // The unmapped argument values recorded in the frame state are stored yet
// another indirection away and then linked into the parameter map below, // another indirection away and then linked into the parameter map below,
// whereas mapped argument values are replaced with a hole instead. // whereas mapped argument values are replaced with a hole instead.
AllocationBuilder aa(jsgraph(), effect, control); AllocationBuilder aa(jsgraph(), effect, control);
aa.AllocateArray(argument_count, factory()->fixed_array_map()); aa.AllocateArray(argument_count, factory()->fixed_array_map());
for (int i = 0; i < mapped_count; ++i, ++paratemers_it) { for (int i = 0; i < mapped_count; ++i, ++parameters_it) {
aa.Store(AccessBuilder::ForFixedArraySlot(i), jsgraph()->TheHoleConstant()); aa.Store(AccessBuilder::ForFixedArraySlot(i), jsgraph()->TheHoleConstant());
} }
for (int i = mapped_count; i < argument_count; ++i, ++paratemers_it) { for (int i = mapped_count; i < argument_count; ++i, ++parameters_it) {
aa.Store(AccessBuilder::ForFixedArraySlot(i), (*paratemers_it).node); DCHECK_NOT_NULL((*parameters_it).node);
aa.Store(AccessBuilder::ForFixedArraySlot(i), (*parameters_it).node);
} }
Node* arguments = aa.Finish(); Node* arguments = aa.Finish();
......
...@@ -279,7 +279,8 @@ Node* JSGraph::ExternalConstant(Runtime::FunctionId function_id) { ...@@ -279,7 +279,8 @@ Node* JSGraph::ExternalConstant(Runtime::FunctionId function_id) {
} }
Node* JSGraph::EmptyStateValues() { Node* JSGraph::EmptyStateValues() {
return CACHED(kEmptyStateValues, graph()->NewNode(common()->StateValues(0))); return CACHED(kEmptyStateValues, graph()->NewNode(common()->StateValues(
0, SparseInputMask::Dense())));
} }
Node* JSGraph::Dead() { Node* JSGraph::Dead() {
......
...@@ -231,14 +231,14 @@ Node* JSInliner::CreateArtificialFrameState(Node* node, Node* outer_frame_state, ...@@ -231,14 +231,14 @@ Node* JSInliner::CreateArtificialFrameState(Node* node, Node* outer_frame_state,
const Operator* op = common()->FrameState( const Operator* op = common()->FrameState(
BailoutId(-1), OutputFrameStateCombine::Ignore(), state_info); BailoutId(-1), OutputFrameStateCombine::Ignore(), state_info);
const Operator* op0 = common()->StateValues(0); const Operator* op0 = common()->StateValues(0, SparseInputMask::Dense());
Node* node0 = graph()->NewNode(op0); Node* node0 = graph()->NewNode(op0);
NodeVector params(local_zone_); NodeVector params(local_zone_);
for (int parameter = 0; parameter < parameter_count + 1; ++parameter) { for (int parameter = 0; parameter < parameter_count + 1; ++parameter) {
params.push_back(node->InputAt(1 + parameter)); params.push_back(node->InputAt(1 + parameter));
} }
const Operator* op_param = const Operator* op_param = common()->StateValues(
common()->StateValues(static_cast<int>(params.size())); static_cast<int>(params.size()), SparseInputMask::Dense());
Node* params_node = graph()->NewNode( Node* params_node = graph()->NewNode(
op_param, static_cast<int>(params.size()), &params.front()); op_param, static_cast<int>(params.size()), &params.front());
return graph()->NewNode(op, params_node, node0, node0, return graph()->NewNode(op, params_node, node0, node0,
...@@ -269,7 +269,7 @@ Node* JSInliner::CreateTailCallerFrameState(Node* node, Node* frame_state) { ...@@ -269,7 +269,7 @@ Node* JSInliner::CreateTailCallerFrameState(Node* node, Node* frame_state) {
const Operator* op = common()->FrameState( const Operator* op = common()->FrameState(
BailoutId(-1), OutputFrameStateCombine::Ignore(), state_info); BailoutId(-1), OutputFrameStateCombine::Ignore(), state_info);
const Operator* op0 = common()->StateValues(0); const Operator* op0 = common()->StateValues(0, SparseInputMask::Dense());
Node* node0 = graph()->NewNode(op0); Node* node0 = graph()->NewNode(op0);
return graph()->NewNode(op, node0, node0, node0, return graph()->NewNode(op, node0, node0, node0,
jsgraph()->UndefinedConstant(), function, jsgraph()->UndefinedConstant(), function,
......
...@@ -1001,7 +1001,8 @@ JSNativeContextSpecialization::BuildPropertyAccess( ...@@ -1001,7 +1001,8 @@ JSNativeContextSpecialization::BuildPropertyAccess(
common()->FrameState(BailoutId::None(), common()->FrameState(BailoutId::None(),
OutputFrameStateCombine::Ignore(), OutputFrameStateCombine::Ignore(),
frame_info0), frame_info0),
graph()->NewNode(common()->StateValues(1), receiver), graph()->NewNode(common()->StateValues(1, SparseInputMask::Dense()),
receiver),
jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(),
context, target, frame_state); context, target, frame_state);
...@@ -1037,7 +1038,8 @@ JSNativeContextSpecialization::BuildPropertyAccess( ...@@ -1037,7 +1038,8 @@ JSNativeContextSpecialization::BuildPropertyAccess(
common()->FrameState(BailoutId::None(), common()->FrameState(BailoutId::None(),
OutputFrameStateCombine::Ignore(), OutputFrameStateCombine::Ignore(),
frame_info0), frame_info0),
graph()->NewNode(common()->StateValues(2), receiver, value), graph()->NewNode(common()->StateValues(2, SparseInputMask::Dense()),
receiver, value),
jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(),
context, target, frame_state); context, target, frame_state);
......
...@@ -1010,8 +1010,9 @@ class RepresentationSelector { ...@@ -1010,8 +1010,9 @@ class RepresentationSelector {
machine_type.semantic() == MachineSemantic::kUint32); machine_type.semantic() == MachineSemantic::kUint32);
(*types)[i] = machine_type; (*types)[i] = machine_type;
} }
NodeProperties::ChangeOp(node, SparseInputMask mask = SparseInputMaskOf(node->op());
jsgraph_->common()->TypedStateValues(types)); NodeProperties::ChangeOp(
node, jsgraph_->common()->TypedStateValues(types, mask));
} }
SetOutput(node, MachineRepresentation::kTagged); SetOutput(node, MachineRepresentation::kTagged);
} }
......
This diff is collapsed.
...@@ -5,12 +5,16 @@ ...@@ -5,12 +5,16 @@
#ifndef V8_COMPILER_STATE_VALUES_UTILS_H_ #ifndef V8_COMPILER_STATE_VALUES_UTILS_H_
#define V8_COMPILER_STATE_VALUES_UTILS_H_ #define V8_COMPILER_STATE_VALUES_UTILS_H_
#include <array>
#include "src/compiler/common-operator.h"
#include "src/compiler/js-graph.h" #include "src/compiler/js-graph.h"
#include "src/globals.h" #include "src/globals.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
class BitVector;
namespace compiler { namespace compiler {
class Graph; class Graph;
...@@ -19,10 +23,12 @@ class V8_EXPORT_PRIVATE StateValuesCache { ...@@ -19,10 +23,12 @@ class V8_EXPORT_PRIVATE StateValuesCache {
public: public:
explicit StateValuesCache(JSGraph* js_graph); explicit StateValuesCache(JSGraph* js_graph);
Node* GetNodeForValues(Node** values, size_t count); Node* GetNodeForValues(Node** values, size_t count,
const BitVector* liveness = nullptr);
private: private:
static const size_t kMaxInputCount = 8; static const size_t kMaxInputCount = 8;
typedef std::array<Node*, kMaxInputCount> WorkingBuffer;
struct NodeKey { struct NodeKey {
Node* node; Node* node;
...@@ -33,22 +39,34 @@ class V8_EXPORT_PRIVATE StateValuesCache { ...@@ -33,22 +39,34 @@ class V8_EXPORT_PRIVATE StateValuesCache {
struct StateValuesKey : public NodeKey { struct StateValuesKey : public NodeKey {
// ValueArray - array of nodes ({node} has to be nullptr). // ValueArray - array of nodes ({node} has to be nullptr).
size_t count; size_t count;
SparseInputMask mask;
Node** values; Node** values;
StateValuesKey(size_t count, Node** values) StateValuesKey(size_t count, SparseInputMask mask, Node** values)
: NodeKey(nullptr), count(count), values(values) {} : NodeKey(nullptr), count(count), mask(mask), values(values) {}
}; };
class ValueArrayIterator;
static bool AreKeysEqual(void* key1, void* key2); static bool AreKeysEqual(void* key1, void* key2);
static bool IsKeysEqualToNode(StateValuesKey* key, Node* node); static bool IsKeysEqualToNode(StateValuesKey* key, Node* node);
static bool AreValueKeysEqual(StateValuesKey* key1, StateValuesKey* key2); static bool AreValueKeysEqual(StateValuesKey* key1, StateValuesKey* key2);
Node* BuildTree(ValueArrayIterator* it, size_t max_height); // Fills {node_buffer}, starting from {node_count}, with {values}, starting
NodeVector* GetWorkingSpace(size_t level); // at {values_idx}, sparsely encoding according to {liveness}. {node_count} is
// updated with the new number of inputs in {node_buffer}, and a bitmask of
// the sparse encoding is returned.
SparseInputMask::BitMaskType FillBufferWithValues(WorkingBuffer* node_buffer,
size_t* node_count,
size_t* values_idx,
Node** values, size_t count,
const BitVector* liveness);
Node* BuildTree(size_t* values_idx, Node** values, size_t count,
const BitVector* liveness, size_t level);
WorkingBuffer* GetWorkingSpace(size_t level);
Node* GetEmptyStateValues(); Node* GetEmptyStateValues();
Node* GetValuesNodeFromCache(Node** nodes, size_t count); Node* GetValuesNodeFromCache(Node** nodes, size_t count,
SparseInputMask mask);
Graph* graph() { return js_graph_->graph(); } Graph* graph() { return js_graph_->graph(); }
CommonOperatorBuilder* common() { return js_graph_->common(); } CommonOperatorBuilder* common() { return js_graph_->common(); }
...@@ -57,7 +75,7 @@ class V8_EXPORT_PRIVATE StateValuesCache { ...@@ -57,7 +75,7 @@ class V8_EXPORT_PRIVATE StateValuesCache {
JSGraph* js_graph_; JSGraph* js_graph_;
CustomMatcherZoneHashMap hash_map_; CustomMatcherZoneHashMap hash_map_;
ZoneVector<NodeVector*> working_space_; // One working space per level. ZoneVector<WorkingBuffer> working_space_; // One working space per level.
Node* empty_state_values_; Node* empty_state_values_;
}; };
...@@ -86,21 +104,14 @@ class V8_EXPORT_PRIVATE StateValuesAccess { ...@@ -86,21 +104,14 @@ class V8_EXPORT_PRIVATE StateValuesAccess {
MachineType type(); MachineType type();
bool done(); bool done();
void Advance(); void Advance();
void EnsureValid();
struct StatePos { SparseInputMask::InputIterator* Top();
Node* node;
int index;
explicit StatePos(Node* node) : node(node), index(0) {}
StatePos() {}
};
StatePos* Top();
void Push(Node* node); void Push(Node* node);
void Pop(); void Pop();
static const int kMaxInlineDepth = 8; static const int kMaxInlineDepth = 8;
StatePos stack_[kMaxInlineDepth]; SparseInputMask::InputIterator stack_[kMaxInlineDepth];
int current_depth_; int current_depth_;
}; };
......
...@@ -77,9 +77,12 @@ class JSTypedLoweringTester : public HandleAndZoneScope { ...@@ -77,9 +77,12 @@ class JSTypedLoweringTester : public HandleAndZoneScope {
} }
Node* EmptyFrameState(Node* context) { Node* EmptyFrameState(Node* context) {
Node* parameters = graph.NewNode(common.StateValues(0)); Node* parameters =
Node* locals = graph.NewNode(common.StateValues(0)); graph.NewNode(common.StateValues(0, SparseInputMask::Dense()));
Node* stack = graph.NewNode(common.StateValues(0)); Node* locals =
graph.NewNode(common.StateValues(0, SparseInputMask::Dense()));
Node* stack =
graph.NewNode(common.StateValues(0, SparseInputMask::Dense()));
Node* state_node = graph.NewNode( Node* state_node = graph.NewNode(
common.FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(), common.FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(),
......
...@@ -437,9 +437,12 @@ TEST_F(EscapeAnalysisTest, DeoptReplacement) { ...@@ -437,9 +437,12 @@ TEST_F(EscapeAnalysisTest, DeoptReplacement) {
Node* effect1 = Store(FieldAccessAtIndex(0), allocation, object1, finish); Node* effect1 = Store(FieldAccessAtIndex(0), allocation, object1, finish);
Branch(); Branch();
Node* ifFalse = IfFalse(); Node* ifFalse = IfFalse();
Node* state_values1 = graph()->NewNode(common()->StateValues(1), finish); Node* state_values1 = graph()->NewNode(
Node* state_values2 = graph()->NewNode(common()->StateValues(0)); common()->StateValues(1, SparseInputMask::Dense()), finish);
Node* state_values3 = graph()->NewNode(common()->StateValues(0)); Node* state_values2 =
graph()->NewNode(common()->StateValues(0, SparseInputMask::Dense()));
Node* state_values3 =
graph()->NewNode(common()->StateValues(0, SparseInputMask::Dense()));
Node* frame_state = graph()->NewNode( Node* frame_state = graph()->NewNode(
common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(), common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(),
nullptr), nullptr),
...@@ -477,9 +480,12 @@ TEST_F(EscapeAnalysisTest, DISABLED_DeoptReplacementIdentity) { ...@@ -477,9 +480,12 @@ TEST_F(EscapeAnalysisTest, DISABLED_DeoptReplacementIdentity) {
Node* effect1 = Store(FieldAccessAtIndex(0), allocation, object1, finish); Node* effect1 = Store(FieldAccessAtIndex(0), allocation, object1, finish);
Branch(); Branch();
Node* ifFalse = IfFalse(); Node* ifFalse = IfFalse();
Node* state_values1 = graph()->NewNode(common()->StateValues(1), finish); Node* state_values1 = graph()->NewNode(
Node* state_values2 = graph()->NewNode(common()->StateValues(1), finish); common()->StateValues(1, SparseInputMask::Dense()), finish);
Node* state_values3 = graph()->NewNode(common()->StateValues(0)); Node* state_values2 = graph()->NewNode(
common()->StateValues(1, SparseInputMask::Dense()), finish);
Node* state_values3 =
graph()->NewNode(common()->StateValues(0, SparseInputMask::Dense()));
Node* frame_state = graph()->NewNode( Node* frame_state = graph()->NewNode(
common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(), common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(),
nullptr), nullptr),
......
...@@ -80,7 +80,8 @@ Node* GraphTest::UndefinedConstant() { ...@@ -80,7 +80,8 @@ Node* GraphTest::UndefinedConstant() {
Node* GraphTest::EmptyFrameState() { Node* GraphTest::EmptyFrameState() {
Node* state_values = graph()->NewNode(common()->StateValues(0)); Node* state_values =
graph()->NewNode(common()->StateValues(0, SparseInputMask::Dense()));
return graph()->NewNode( return graph()->NewNode(
common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(), common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(),
nullptr), nullptr),
......
...@@ -364,10 +364,13 @@ TARGET_TEST_F(InstructionSelectorTest, CallJSFunctionWithDeopt) { ...@@ -364,10 +364,13 @@ TARGET_TEST_F(InstructionSelectorTest, CallJSFunctionWithDeopt) {
zone(), false, 1, CallDescriptor::kNeedsFrameState); zone(), false, 1, CallDescriptor::kNeedsFrameState);
// Build frame state for the state before the call. // Build frame state for the state before the call.
Node* parameters = Node* parameters = m.AddNode(
m.AddNode(m.common()->TypedStateValues(&int32_type), m.Int32Constant(1)); m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
Node* locals = m.AddNode(m.common()->TypedStateValues(&empty_types)); m.Int32Constant(1));
Node* stack = m.AddNode(m.common()->TypedStateValues(&empty_types)); Node* locals = m.AddNode(
m.common()->TypedStateValues(&empty_types, SparseInputMask::Dense()));
Node* stack = m.AddNode(
m.common()->TypedStateValues(&empty_types, SparseInputMask::Dense()));
Node* context_sentinel = m.Int32Constant(0); Node* context_sentinel = m.Int32Constant(0);
Node* state_node = m.AddNode( Node* state_node = m.AddNode(
m.common()->FrameState(bailout_id, OutputFrameStateCombine::Push(), m.common()->FrameState(bailout_id, OutputFrameStateCombine::Push(),
...@@ -419,11 +422,14 @@ TARGET_TEST_F(InstructionSelectorTest, CallStubWithDeopt) { ...@@ -419,11 +422,14 @@ TARGET_TEST_F(InstructionSelectorTest, CallStubWithDeopt) {
CallDescriptor::kNeedsFrameState, Operator::kNoProperties); CallDescriptor::kNeedsFrameState, Operator::kNoProperties);
// Build frame state for the state before the call. // Build frame state for the state before the call.
Node* parameters = Node* parameters = m.AddNode(
m.AddNode(m.common()->TypedStateValues(&int32_type), m.Int32Constant(43)); m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
Node* locals = m.AddNode(m.common()->TypedStateValues(&float64_type), m.Int32Constant(43));
Node* locals = m.AddNode(
m.common()->TypedStateValues(&float64_type, SparseInputMask::Dense()),
m.Float64Constant(0.5)); m.Float64Constant(0.5));
Node* stack = m.AddNode(m.common()->TypedStateValues(&tagged_type), Node* stack = m.AddNode(
m.common()->TypedStateValues(&tagged_type, SparseInputMask::Dense()),
m.UndefinedConstant()); m.UndefinedConstant());
Node* context_sentinel = m.Int32Constant(0); Node* context_sentinel = m.Int32Constant(0);
Node* state_node = m.AddNode( Node* state_node = m.AddNode(
...@@ -512,23 +518,29 @@ TARGET_TEST_F(InstructionSelectorTest, CallStubWithDeoptRecursiveFrameState) { ...@@ -512,23 +518,29 @@ TARGET_TEST_F(InstructionSelectorTest, CallStubWithDeoptRecursiveFrameState) {
CallDescriptor::kNeedsFrameState, Operator::kNoProperties); CallDescriptor::kNeedsFrameState, Operator::kNoProperties);
// Build frame state for the state before the call. // Build frame state for the state before the call.
Node* parameters = Node* parameters = m.AddNode(
m.AddNode(m.common()->TypedStateValues(&int32_type), m.Int32Constant(63)); m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
Node* locals = m.Int32Constant(63));
m.AddNode(m.common()->TypedStateValues(&int32_type), m.Int32Constant(64)); Node* locals = m.AddNode(
Node* stack = m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
m.AddNode(m.common()->TypedStateValues(&int32_type), m.Int32Constant(65)); m.Int32Constant(64));
Node* stack = m.AddNode(
m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
m.Int32Constant(65));
Node* frame_state_parent = m.AddNode( Node* frame_state_parent = m.AddNode(
m.common()->FrameState(bailout_id_parent, m.common()->FrameState(bailout_id_parent,
OutputFrameStateCombine::Ignore(), OutputFrameStateCombine::Ignore(),
m.GetFrameStateFunctionInfo(1, 1)), m.GetFrameStateFunctionInfo(1, 1)),
parameters, locals, stack, context, function_node, m.UndefinedConstant()); parameters, locals, stack, context, function_node, m.UndefinedConstant());
Node* parameters2 = Node* parameters2 = m.AddNode(
m.AddNode(m.common()->TypedStateValues(&int32_type), m.Int32Constant(43)); m.common()->TypedStateValues(&int32_type, SparseInputMask::Dense()),
Node* locals2 = m.AddNode(m.common()->TypedStateValues(&float64_type), m.Int32Constant(43));
Node* locals2 = m.AddNode(
m.common()->TypedStateValues(&float64_type, SparseInputMask::Dense()),
m.Float64Constant(0.25)); m.Float64Constant(0.25));
Node* stack2 = m.AddNode(m.common()->TypedStateValues(&int32x2_type), Node* stack2 = m.AddNode(
m.common()->TypedStateValues(&int32x2_type, SparseInputMask::Dense()),
m.Int32Constant(44), m.Int32Constant(45)); m.Int32Constant(44), m.Int32Constant(45));
Node* state_node = m.AddNode( Node* state_node = m.AddNode(
m.common()->FrameState(bailout_id_before, OutputFrameStateCombine::Push(), m.common()->FrameState(bailout_id_before, OutputFrameStateCombine::Push(),
......
...@@ -46,7 +46,8 @@ class JSCreateLoweringTest : public TypedGraphTest { ...@@ -46,7 +46,8 @@ class JSCreateLoweringTest : public TypedGraphTest {
} }
Node* FrameState(Handle<SharedFunctionInfo> shared, Node* outer_frame_state) { Node* FrameState(Handle<SharedFunctionInfo> shared, Node* outer_frame_state) {
Node* state_values = graph()->NewNode(common()->StateValues(0)); Node* state_values =
graph()->NewNode(common()->StateValues(0, SparseInputMask::Dense()));
return graph()->NewNode( return graph()->NewNode(
common()->FrameState( common()->FrameState(
BailoutId::None(), OutputFrameStateCombine::Ignore(), BailoutId::None(), OutputFrameStateCombine::Ignore(),
......
...@@ -28,7 +28,8 @@ class LivenessAnalysisTest : public GraphTest { ...@@ -28,7 +28,8 @@ class LivenessAnalysisTest : public GraphTest {
jsgraph_(isolate(), graph(), common(), &javascript_, nullptr, jsgraph_(isolate(), graph(), common(), &javascript_, nullptr,
&machine_), &machine_),
analyzer_(locals_count, false, zone()), analyzer_(locals_count, false, zone()),
empty_values_(graph()->NewNode(common()->StateValues(0), 0, nullptr)), empty_values_(graph()->NewNode(
common()->StateValues(0, SparseInputMask::Dense()), 0, nullptr)),
next_checkpoint_id_(0), next_checkpoint_id_(0),
current_block_(nullptr) {} current_block_(nullptr) {}
...@@ -48,7 +49,8 @@ class LivenessAnalysisTest : public GraphTest { ...@@ -48,7 +49,8 @@ class LivenessAnalysisTest : public GraphTest {
int ast_num = next_checkpoint_id_++; int ast_num = next_checkpoint_id_++;
int first_const = intconst_from_bailout_id(ast_num, locals_count_); int first_const = intconst_from_bailout_id(ast_num, locals_count_);
const Operator* locals_op = common()->StateValues(locals_count_); const Operator* locals_op =
common()->StateValues(locals_count_, SparseInputMask::Dense());
ZoneVector<Node*> local_inputs(locals_count_, nullptr, zone()); ZoneVector<Node*> local_inputs(locals_count_, nullptr, zone());
for (int i = 0; i < locals_count_; i++) { for (int i = 0; i < locals_count_; i++) {
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "src/compiler/state-values-utils.h" #include "src/compiler/state-values-utils.h"
#include "src/bit-vector.h"
#include "test/unittests/compiler/graph-unittest.h" #include "test/unittests/compiler/graph-unittest.h"
#include "test/unittests/compiler/node-test-utils.h" #include "test/unittests/compiler/node-test-utils.h"
#include "test/unittests/test-utils.h" #include "test/unittests/test-utils.h"
...@@ -18,7 +19,8 @@ class StateValuesIteratorTest : public GraphTest { ...@@ -18,7 +19,8 @@ class StateValuesIteratorTest : public GraphTest {
Node* StateValuesFromVector(NodeVector* nodes) { Node* StateValuesFromVector(NodeVector* nodes) {
int count = static_cast<int>(nodes->size()); int count = static_cast<int>(nodes->size());
return graph()->NewNode(common()->StateValues(count), count, return graph()->NewNode(
common()->StateValues(count, SparseInputMask::Dense()), count,
count == 0 ? nullptr : &(nodes->front())); count == 0 ? nullptr : &(nodes->front()));
} }
}; };
...@@ -107,7 +109,8 @@ TEST_F(StateValuesIteratorTest, TreeFromVector) { ...@@ -107,7 +109,8 @@ TEST_F(StateValuesIteratorTest, TreeFromVector) {
// Build the tree. // Build the tree.
StateValuesCache builder(&jsgraph); StateValuesCache builder(&jsgraph);
Node* values_node = builder.GetNodeForValues( Node* values_node = builder.GetNodeForValues(
inputs.size() == 0 ? nullptr : &(inputs.front()), inputs.size()); inputs.size() == 0 ? nullptr : &(inputs.front()), inputs.size(),
nullptr);
// Check the tree contents with vector. // Check the tree contents with vector.
int i = 0; int i = 0;
...@@ -119,6 +122,46 @@ TEST_F(StateValuesIteratorTest, TreeFromVector) { ...@@ -119,6 +122,46 @@ TEST_F(StateValuesIteratorTest, TreeFromVector) {
} }
} }
TEST_F(StateValuesIteratorTest, TreeFromVectorWithLiveness) {
int sizes[] = {0, 1, 2, 100, 5000, 30000};
TRACED_FOREACH(int, count, sizes) {
JSOperatorBuilder javascript(zone());
MachineOperatorBuilder machine(zone());
JSGraph jsgraph(isolate(), graph(), common(), &javascript, nullptr,
&machine);
// Generate the input vector.
NodeVector inputs(zone());
for (int i = 0; i < count; i++) {
inputs.push_back(Int32Constant(i));
}
// Generate the input liveness.
BitVector liveness(count, zone());
for (int i = 0; i < count; i++) {
if (i % 3 == 0) {
liveness.Add(i);
}
}
// Build the tree.
StateValuesCache builder(&jsgraph);
Node* values_node = builder.GetNodeForValues(
inputs.size() == 0 ? nullptr : &(inputs.front()), inputs.size(),
&liveness);
// Check the tree contents with vector.
int i = 0;
for (StateValuesAccess::TypedNode node : StateValuesAccess(values_node)) {
if (liveness.Contains(i)) {
EXPECT_THAT(node.node, IsInt32Constant(i));
} else {
EXPECT_EQ(node.node, nullptr);
}
i++;
}
EXPECT_EQ(inputs.size(), static_cast<size_t>(i));
}
}
TEST_F(StateValuesIteratorTest, BuildTreeIdentical) { TEST_F(StateValuesIteratorTest, BuildTreeIdentical) {
int sizes[] = {0, 1, 2, 100, 5000, 30000}; int sizes[] = {0, 1, 2, 100, 5000, 30000};
...@@ -137,9 +180,46 @@ TEST_F(StateValuesIteratorTest, BuildTreeIdentical) { ...@@ -137,9 +180,46 @@ TEST_F(StateValuesIteratorTest, BuildTreeIdentical) {
// Build two trees from the same data. // Build two trees from the same data.
StateValuesCache builder(&jsgraph); StateValuesCache builder(&jsgraph);
Node* node1 = builder.GetNodeForValues( Node* node1 = builder.GetNodeForValues(
inputs.size() == 0 ? nullptr : &(inputs.front()), inputs.size()); inputs.size() == 0 ? nullptr : &(inputs.front()), inputs.size(),
nullptr);
Node* node2 = builder.GetNodeForValues(
inputs.size() == 0 ? nullptr : &(inputs.front()), inputs.size(),
nullptr);
// The trees should be equal since the data was the same.
EXPECT_EQ(node1, node2);
}
}
TEST_F(StateValuesIteratorTest, BuildTreeWithLivenessIdentical) {
int sizes[] = {0, 1, 2, 100, 5000, 30000};
TRACED_FOREACH(int, count, sizes) {
JSOperatorBuilder javascript(zone());
MachineOperatorBuilder machine(zone());
JSGraph jsgraph(isolate(), graph(), common(), &javascript, nullptr,
&machine);
// Generate the input vector.
NodeVector inputs(zone());
for (int i = 0; i < count; i++) {
inputs.push_back(Int32Constant(i));
}
// Generate the input liveness.
BitVector liveness(count, zone());
for (int i = 0; i < count; i++) {
if (i % 3 == 0) {
liveness.Add(i);
}
}
// Build two trees from the same data.
StateValuesCache builder(&jsgraph);
Node* node1 = builder.GetNodeForValues(
inputs.size() == 0 ? nullptr : &(inputs.front()), inputs.size(),
&liveness);
Node* node2 = builder.GetNodeForValues( Node* node2 = builder.GetNodeForValues(
inputs.size() == 0 ? nullptr : &(inputs.front()), inputs.size()); inputs.size() == 0 ? nullptr : &(inputs.front()), inputs.size(),
&liveness);
// The trees should be equal since the data was the same. // The trees should be equal since the data was the same.
EXPECT_EQ(node1, node2); EXPECT_EQ(node1, node2);
......
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