Commit 6aea9de2 authored by jkummerow's avatar jkummerow Committed by Commit bot

[stubs] Implement binary-search descriptor lookup in CSA

And use it to avoid runtime calls when there are too many properties
for linear searching.
The threshold of 32 was chosen by experimentation.

Review-Url: https://codereview.chromium.org/2680973002
Cr-Commit-Position: refs/heads/master@{#43078}
parent 4f4da1f8
......@@ -4721,6 +4721,7 @@ void CodeStubAssembler::DescriptorLookupLinear(Node* unique_name,
Label* if_found,
Variable* var_name_index,
Label* if_not_found) {
Comment("DescriptorLookupLinear");
Node* first_inclusive = IntPtrConstant(DescriptorArray::ToKeyIndex(0));
Node* factor = IntPtrConstant(DescriptorArray::kEntrySize);
Node* last_exclusive = IntPtrAdd(first_inclusive, IntPtrMul(nof, factor));
......@@ -4738,6 +4739,136 @@ void CodeStubAssembler::DescriptorLookupLinear(Node* unique_name,
Goto(if_not_found);
}
Node* CodeStubAssembler::DescriptorArrayNumberOfEntries(Node* descriptors) {
return LoadAndUntagToWord32FixedArrayElement(
descriptors, IntPtrConstant(DescriptorArray::kDescriptorLengthIndex));
}
namespace {
Node* DescriptorNumberToIndex(CodeStubAssembler* a, Node* descriptor_number) {
Node* descriptor_size = a->Int32Constant(DescriptorArray::kEntrySize);
Node* index = a->Int32Mul(descriptor_number, descriptor_size);
return a->ChangeInt32ToIntPtr(index);
}
} // namespace
Node* CodeStubAssembler::DescriptorArrayToKeyIndex(Node* descriptor_number) {
return IntPtrAdd(IntPtrConstant(DescriptorArray::ToKeyIndex(0)),
DescriptorNumberToIndex(this, descriptor_number));
}
Node* CodeStubAssembler::DescriptorArrayGetSortedKeyIndex(
Node* descriptors, Node* descriptor_number) {
const int details_offset = DescriptorArray::ToDetailsIndex(0) * kPointerSize;
Node* details = LoadAndUntagToWord32FixedArrayElement(
descriptors, DescriptorNumberToIndex(this, descriptor_number),
details_offset);
return DecodeWord32<PropertyDetails::DescriptorPointer>(details);
}
Node* CodeStubAssembler::DescriptorArrayGetKey(Node* descriptors,
Node* descriptor_number) {
const int key_offset = DescriptorArray::ToKeyIndex(0) * kPointerSize;
return LoadFixedArrayElement(descriptors,
DescriptorNumberToIndex(this, descriptor_number),
key_offset);
}
void CodeStubAssembler::DescriptorLookupBinary(Node* unique_name,
Node* descriptors, Node* nof,
Label* if_found,
Variable* var_name_index,
Label* if_not_found) {
Comment("DescriptorLookupBinary");
Variable var_low(this, MachineRepresentation::kWord32, Int32Constant(0));
Node* limit =
Int32Sub(DescriptorArrayNumberOfEntries(descriptors), Int32Constant(1));
Variable var_high(this, MachineRepresentation::kWord32, limit);
Node* hash = LoadNameHashField(unique_name);
CSA_ASSERT(this, Word32NotEqual(hash, Int32Constant(0)));
// Assume non-empty array.
CSA_ASSERT(this, Uint32LessThanOrEqual(var_low.value(), var_high.value()));
Variable* loop_vars[] = {&var_high, &var_low};
Label binary_loop(this, 2, loop_vars);
Goto(&binary_loop);
Bind(&binary_loop);
{
// mid = low + (high - low) / 2 (to avoid overflow in "(low + high) / 2").
Node* mid =
Int32Add(var_low.value(),
Word32Shr(Int32Sub(var_high.value(), var_low.value()), 1));
// mid_name = descriptors->GetSortedKey(mid).
Node* sorted_key_index = DescriptorArrayGetSortedKeyIndex(descriptors, mid);
Node* mid_name = DescriptorArrayGetKey(descriptors, sorted_key_index);
Node* mid_hash = LoadNameHashField(mid_name);
Label mid_greater(this), mid_less(this), merge(this);
Branch(Uint32GreaterThanOrEqual(mid_hash, hash), &mid_greater, &mid_less);
Bind(&mid_greater);
{
var_high.Bind(mid);
Goto(&merge);
}
Bind(&mid_less);
{
var_low.Bind(Int32Add(mid, Int32Constant(1)));
Goto(&merge);
}
Bind(&merge);
GotoIf(Word32NotEqual(var_low.value(), var_high.value()), &binary_loop);
}
Label scan_loop(this, &var_low);
Goto(&scan_loop);
Bind(&scan_loop);
{
GotoIf(Int32GreaterThan(var_low.value(), limit), if_not_found);
Node* sort_index =
DescriptorArrayGetSortedKeyIndex(descriptors, var_low.value());
Node* current_name = DescriptorArrayGetKey(descriptors, sort_index);
Node* current_hash = LoadNameHashField(current_name);
GotoIf(Word32NotEqual(current_hash, hash), if_not_found);
Label next(this);
GotoIf(WordNotEqual(current_name, unique_name), &next);
GotoIf(Int32GreaterThanOrEqual(sort_index, nof), if_not_found);
var_name_index->Bind(DescriptorArrayToKeyIndex(sort_index));
Goto(if_found);
Bind(&next);
var_low.Bind(Int32Add(var_low.value(), Int32Constant(1)));
Goto(&scan_loop);
}
}
void CodeStubAssembler::DescriptorLookup(Node* unique_name, Node* descriptors,
Node* bitfield3, Label* if_found,
Variable* var_name_index,
Label* if_not_found) {
Comment("DescriptorArrayLookup");
Node* nof = DecodeWord32<Map::NumberOfOwnDescriptorsBits>(bitfield3);
GotoIf(Word32Equal(nof, Int32Constant(0)), if_not_found);
Label linear_search(this), binary_search(this);
const int kMaxElementsForLinearSearch = 32;
Branch(Int32LessThanOrEqual(nof, Int32Constant(kMaxElementsForLinearSearch)),
&linear_search, &binary_search);
Bind(&linear_search);
{
DescriptorLookupLinear(unique_name, descriptors, ChangeInt32ToIntPtr(nof),
if_found, var_name_index, if_not_found);
}
Bind(&binary_search);
{
DescriptorLookupBinary(unique_name, descriptors, nof, if_found,
var_name_index, if_not_found);
}
}
void CodeStubAssembler::TryLookupProperty(
Node* object, Node* map, Node* instance_type, Node* unique_name,
Label* if_found_fast, Label* if_found_dict, Label* if_found_global,
......@@ -4763,19 +4894,10 @@ void CodeStubAssembler::TryLookupProperty(
&if_isfastmap);
Bind(&if_isfastmap);
{
Comment("DescriptorArrayLookup");
Node* nof =
DecodeWordFromWord32<Map::NumberOfOwnDescriptorsBits>(bit_field3);
// Bail out to the runtime for large numbers of own descriptors. The stub
// only does linear search, which becomes too expensive in that case.
{
static const int32_t kMaxLinear = 210;
GotoIf(UintPtrGreaterThan(nof, IntPtrConstant(kMaxLinear)), if_bailout);
}
Node* descriptors = LoadMapDescriptors(map);
var_meta_storage->Bind(descriptors);
DescriptorLookupLinear(unique_name, descriptors, nof, if_found_fast,
DescriptorLookup(unique_name, descriptors, bit_field3, if_found_fast,
var_name_index, if_not_found);
}
Bind(&if_isslowmap);
......
......@@ -752,7 +752,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* ToNumber(Node* context, Node* input);
// Converts |input| to one of 2^32 integer values in the range 0 through
// 2^321, inclusive.
// 2^32-1, inclusive.
// ES#sec-touint32
compiler::Node* ToUint32(compiler::Node* context, compiler::Node* input);
......@@ -1242,9 +1242,15 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
inline void Print(Node* tagged_value) { return Print(nullptr, tagged_value); }
protected:
void DescriptorLookup(Node* unique_name, Node* descriptors, Node* bitfield3,
Label* if_found, Variable* var_name_index,
Label* if_not_found);
void DescriptorLookupLinear(Node* unique_name, Node* descriptors, Node* nof,
Label* if_found, Variable* var_name_index,
Label* if_not_found);
void DescriptorLookupBinary(Node* unique_name, Node* descriptors, Node* nof,
Label* if_found, Variable* var_name_index,
Label* if_not_found);
Node* CallGetterIfAccessor(Node* value, Node* details, Node* context,
Node* receiver, Label* if_bailout);
......@@ -1283,6 +1289,19 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* AllocateConsString(Heap::RootListIndex map_root_index, Node* length,
Node* first, Node* second, AllocationFlags flags);
// Implements DescriptorArray::number_of_entries.
// Returns an untagged int32.
Node* DescriptorArrayNumberOfEntries(Node* descriptors);
// Implements DescriptorArray::ToKeyIndex.
// Returns an untagged IntPtr.
Node* DescriptorArrayToKeyIndex(Node* descriptor_number);
// Implements DescriptorArray::GetSortedKeyIndex.
// Returns an untagged int32.
Node* DescriptorArrayGetSortedKeyIndex(Node* descriptors,
Node* descriptor_number);
// Implements DescriptorArray::GetKey.
Node* DescriptorArrayGetKey(Node* descriptors, Node* descriptor_number);
static const int kElementLoopUnrollThreshold = 8;
};
......
......@@ -1174,20 +1174,12 @@ void AccessorAssembler::GenericPropertyLoad(Node* receiver, Node* receiver_map,
// Try looking up the property on the receiver; if unsuccessful, look
// for a handler in the stub cache.
Comment("DescriptorArray lookup");
// Skip linear search if there are too many descriptors.
// TODO(jkummerow): Consider implementing binary search.
// See also TryLookupProperty() which has the same limitation.
const int32_t kMaxLinear = 210;
Label stub_cache(this);
Node* bitfield3 = LoadMapBitField3(receiver_map);
Node* nof = DecodeWordFromWord32<Map::NumberOfOwnDescriptorsBits>(bitfield3);
GotoIf(UintPtrLessThan(IntPtrConstant(kMaxLinear), nof), &stub_cache);
Node* descriptors = LoadMapDescriptors(receiver_map);
Label if_descriptor_found(this), stub_cache(this);
Variable var_name_index(this, MachineType::PointerRepresentation());
Label if_descriptor_found(this);
DescriptorLookupLinear(key, descriptors, nof, &if_descriptor_found,
DescriptorLookup(key, descriptors, bitfield3, &if_descriptor_found,
&var_name_index, &stub_cache);
Bind(&if_descriptor_found);
......
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