Commit 9a05c175 authored by Z Duong Nguyen-Huu's avatar Z Duong Nguyen-Huu Committed by Commit Bot

Further optimize object.assign fast path for symbol properties

This is a follow-up CL from https://chromium-review.googlesource.com/c/v8/v8/+/1432597
Indices of first and last symbol properties are recorded and used on a second iteration of DescriptorArrayForEach() to potentially reduce the iteration range

Bug: v8:6705
Change-Id: Iac73909d138214d1128e935eff686f2f058e17f7
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1516021
Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60344}
parent 455b79ef
...@@ -8901,22 +8901,6 @@ void CodeStubAssembler::LookupBinary(TNode<Name> unique_name, ...@@ -8901,22 +8901,6 @@ void CodeStubAssembler::LookupBinary(TNode<Name> unique_name,
} }
} }
void CodeStubAssembler::DescriptorArrayForEach(
VariableList& variable_list, TNode<Uint32T> start_descriptor,
TNode<Uint32T> end_descriptor, const ForEachDescriptorBodyFunction& body) {
TNode<IntPtrT> start_index = ToKeyIndex<DescriptorArray>(start_descriptor);
TNode<IntPtrT> end_index = ToKeyIndex<DescriptorArray>(end_descriptor);
BuildFastLoop(variable_list, start_index, end_index,
[=](Node* index) {
TNode<IntPtrT> descriptor_key_index =
TNode<IntPtrT>::UncheckedCast(index);
body(descriptor_key_index);
},
DescriptorArray::kEntrySize, INTPTR_PARAMETERS,
IndexAdvanceMode::kPost);
}
void CodeStubAssembler::ForEachEnumerableOwnProperty( void CodeStubAssembler::ForEachEnumerableOwnProperty(
TNode<Context> context, TNode<Map> map, TNode<JSObject> object, TNode<Context> context, TNode<Map> map, TNode<JSObject> object,
ForEachEnumerationMode mode, const ForEachKeyValueFunction& body, ForEachEnumerationMode mode, const ForEachKeyValueFunction& body,
...@@ -8933,18 +8917,29 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty( ...@@ -8933,18 +8917,29 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty(
TVARIABLE(BoolT, var_has_symbol, Int32FalseConstant()); TVARIABLE(BoolT, var_has_symbol, Int32FalseConstant());
// false - iterate only string properties, true - iterate only symbol // false - iterate only string properties, true - iterate only symbol
// properties // properties
TVARIABLE(BoolT, var_name_filter, Int32FalseConstant()); TVARIABLE(BoolT, var_is_symbol_processing_loop, Int32FalseConstant());
VariableList list({&var_stable, &var_has_symbol, &var_name_filter}, zone()); TVARIABLE(IntPtrT, var_start_key_index,
Label descriptor_array_loop(this, ToKeyIndex<DescriptorArray>(Unsigned(Int32Constant(0))));
{&var_stable, &var_has_symbol, &var_name_filter}); // Note: var_end_key_index is exclusive for the loop
TVARIABLE(IntPtrT, var_end_key_index,
ToKeyIndex<DescriptorArray>(nof_descriptors));
VariableList list(
{&var_stable, &var_has_symbol, &var_is_symbol_processing_loop,
&var_start_key_index, &var_end_key_index},
zone());
Label descriptor_array_loop(
this, {&var_stable, &var_has_symbol, &var_is_symbol_processing_loop,
&var_start_key_index, &var_end_key_index});
Goto(&descriptor_array_loop); Goto(&descriptor_array_loop);
BIND(&descriptor_array_loop); BIND(&descriptor_array_loop);
DescriptorArrayForEach( BuildFastLoop(
list, Unsigned(Int32Constant(0)), nof_descriptors, list, var_start_key_index.value(), var_end_key_index.value(),
[=, &var_stable, &var_has_symbol, [=, &var_stable, &var_has_symbol, &var_is_symbol_processing_loop,
&var_name_filter](TNode<IntPtrT> descriptor_key_index) { &var_start_key_index, &var_end_key_index](Node* index) {
TNode<IntPtrT> descriptor_key_index =
TNode<IntPtrT>::UncheckedCast(index);
TNode<Name> next_key = TNode<Name> next_key =
LoadKeyByKeyIndex(descriptors, descriptor_key_index); LoadKeyByKeyIndex(descriptors, descriptor_key_index);
...@@ -8953,21 +8948,35 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty( ...@@ -8953,21 +8948,35 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty(
if (mode == kEnumerationOrder) { if (mode == kEnumerationOrder) {
// |next_key| is either a string or a symbol // |next_key| is either a string or a symbol
// Skip strings or symbols depending on var_name_filter value. // Skip strings or symbols depending on
// |var_is_symbol_processing_loop|.
Label if_string(this), if_symbol(this), if_name_ok(this); Label if_string(this), if_symbol(this), if_name_ok(this);
Branch(IsSymbol(next_key), &if_symbol, &if_string); Branch(IsSymbol(next_key), &if_symbol, &if_string);
BIND(&if_symbol); BIND(&if_symbol);
{ {
var_has_symbol = Int32TrueConstant(); // Process symbol property when |var_is_symbol_processing_loop| is
// Process symbol property when |var_name_filer| is true. // true.
Branch(var_name_filter.value(), &if_name_ok, &next_iteration); GotoIf(var_is_symbol_processing_loop.value(), &if_name_ok);
// First iteration need to calculate smaller range for processing
// symbols
Label if_first_symbol(this);
// var_end_key_index is still inclusive at this point.
var_end_key_index = descriptor_key_index;
Branch(var_has_symbol.value(), &next_iteration, &if_first_symbol);
BIND(&if_first_symbol);
{
var_start_key_index = descriptor_key_index;
var_has_symbol = Int32TrueConstant();
Goto(&next_iteration);
}
} }
BIND(&if_string); BIND(&if_string);
{ {
CSA_ASSERT(this, IsString(next_key)); CSA_ASSERT(this, IsString(next_key));
// Process string property when |var_name_filer| is false. // Process string property when |var_is_symbol_processing_loop| is
Branch(var_name_filter.value(), &next_iteration, &if_name_ok); // false.
Branch(var_is_symbol_processing_loop.value(), &next_iteration,
&if_name_ok);
} }
BIND(&if_name_ok); BIND(&if_name_ok);
} }
...@@ -9064,14 +9073,19 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty( ...@@ -9064,14 +9073,19 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty(
} }
} }
BIND(&next_iteration); BIND(&next_iteration);
}); },
DescriptorArray::kEntrySize, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
if (mode == kEnumerationOrder) { if (mode == kEnumerationOrder) {
Label done(this); Label done(this);
GotoIf(var_name_filter.value(), &done); GotoIf(var_is_symbol_processing_loop.value(), &done);
GotoIfNot(var_has_symbol.value(), &done); GotoIfNot(var_has_symbol.value(), &done);
// All string properties are processed, now process symbol properties. // All string properties are processed, now process symbol properties.
var_name_filter = Int32TrueConstant(); var_is_symbol_processing_loop = Int32TrueConstant();
// Add DescriptorArray::kEntrySize to make the var_end_key_index exclusive
// as BuildFastLoop() expects.
Increment(&var_end_key_index, DescriptorArray::kEntrySize,
INTPTR_PARAMETERS);
Goto(&descriptor_array_loop); Goto(&descriptor_array_loop);
BIND(&done); BIND(&done);
......
...@@ -3323,11 +3323,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ...@@ -3323,11 +3323,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
typedef std::function<void(TNode<IntPtrT> descriptor_key_index)> typedef std::function<void(TNode<IntPtrT> descriptor_key_index)>
ForEachDescriptorBodyFunction; ForEachDescriptorBodyFunction;
void DescriptorArrayForEach(VariableList& variable_list,
TNode<Uint32T> start_descriptor,
TNode<Uint32T> end_descriptor,
const ForEachDescriptorBodyFunction& body);
// Descriptor array accessors based on key_index, which is equal to // Descriptor array accessors based on key_index, which is equal to
// DescriptorArray::ToKeyIndex(descriptor). // DescriptorArray::ToKeyIndex(descriptor).
TNode<Name> LoadKeyByKeyIndex(TNode<DescriptorArray> container, TNode<Name> LoadKeyByKeyIndex(TNode<DescriptorArray> container,
......
...@@ -171,3 +171,58 @@ assertSame(Object.assign(o, {}), o); ...@@ -171,3 +171,58 @@ assertSame(Object.assign(o, {}), o);
var source = {get k1() { throw "fail" }}; var source = {get k1() { throw "fail" }};
assertThrows(()=>Object.assign(target, source)); assertThrows(()=>Object.assign(target, source));
})(); })();
(function strings_and_symbol_order1() {
// first order
var log = [];
var sym1 = Symbol("x"), sym2 = Symbol("y");
var source = {
get [sym1](){ log.push("get sym1"); },
get a() { log.push("get a"); },
get b() { log.push("get b"); },
get c() { log.push("get c"); },
get [sym2](){ log.push("get sym2"); },
};
Object.assign({}, source);
assertEquals(log, ["get a", "get b", "get c", "get sym1", "get sym2"]);
})();
(function strings_and_symbol_order2() {
// first order
var log = [];
var sym1 = Symbol("x"), sym2 = Symbol("y");
var source = {
get [sym1](){ log.push("get sym1"); },
get a() { log.push("get a"); },
get [sym2](){ log.push("get sym2"); },
get b() { log.push("get b"); },
get c() { log.push("get c"); },
};
Object.assign({}, source);
assertEquals(log, ["get a", "get b", "get c", "get sym1", "get sym2"]);
})();
(function strings_and_symbol_order3() {
// first order
var log = [];
var sym1 = Symbol("x"), sym2 = Symbol("y");
var source = {
get a() { log.push("get a"); },
get [sym1](){ log.push("get sym1"); },
get b() { log.push("get b"); },
get [sym2](){ log.push("get sym2"); },
get c() { log.push("get c"); },
};
Object.assign({}, source);
assertEquals(log, ["get a", "get b", "get c", "get sym1", "get sym2"]);
})();
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