[builtins] implement fast path of Object.getOwnPropertyNames using CSA.
Migrate the Object.getOwnPropertyNames to the CodeStubAssembler and use the enum cache backing store when 1) the enum cache is avaible 2) the {object} has no elements 3) all own properties are enumerable This makes a speedup of 10x when using Object.getOwnPropertyNames with fast-path. It improves Speedometer2.0 Inferno case by ~9% on ATOM platform. Change-Id: I05e1df0e7d9d53d97664c322248cedb106a7b1d0 Reviewed-on: https://chromium-review.googlesource.com/1004434 Reviewed-by: Yang Guo <yangguo@chromium.org> Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Commit-Queue: Shiyu Zhang <shiyu.zhang@intel.com> Cr-Commit-Position: refs/heads/master@{#53992}
This commit is contained in:
parent
e1f86718e4
commit
f9868eaa53
@ -1452,7 +1452,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
factory->getOwnPropertyDescriptors_string(),
|
||||
Builtins::kObjectGetOwnPropertyDescriptors, 1, false);
|
||||
SimpleInstallFunction(isolate_, object_function, "getOwnPropertyNames",
|
||||
Builtins::kObjectGetOwnPropertyNames, 1, false);
|
||||
Builtins::kObjectGetOwnPropertyNames, 1, true);
|
||||
SimpleInstallFunction(isolate_, object_function, "getOwnPropertySymbols",
|
||||
Builtins::kObjectGetOwnPropertySymbols, 1, false);
|
||||
SimpleInstallFunction(isolate_, object_function, "is", Builtins::kObjectIs,
|
||||
|
@ -795,7 +795,7 @@ namespace internal {
|
||||
TFJ(ObjectGetOwnPropertyDescriptor, \
|
||||
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
||||
CPP(ObjectGetOwnPropertyDescriptors) \
|
||||
CPP(ObjectGetOwnPropertyNames) \
|
||||
TFJ(ObjectGetOwnPropertyNames, 1, kReceiver, kObject) \
|
||||
CPP(ObjectGetOwnPropertySymbols) \
|
||||
CPP(ObjectGetPrototypeOf) \
|
||||
CPP(ObjectSetPrototypeOf) \
|
||||
|
@ -779,6 +779,108 @@ TF_BUILTIN(ObjectKeys, ObjectBuiltinsAssembler) {
|
||||
}
|
||||
}
|
||||
|
||||
// ES #sec-object.getOwnPropertyNames
|
||||
TF_BUILTIN(ObjectGetOwnPropertyNames, ObjectBuiltinsAssembler) {
|
||||
Node* object = Parameter(Descriptor::kObject);
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
|
||||
VARIABLE(var_length, MachineRepresentation::kTagged);
|
||||
VARIABLE(var_elements, MachineRepresentation::kTagged);
|
||||
Label if_empty(this, Label::kDeferred), if_empty_elements(this),
|
||||
if_fast(this), try_fast(this, Label::kDeferred),
|
||||
if_slow(this, Label::kDeferred), if_join(this);
|
||||
|
||||
// Check if the {object} has a usable enum cache.
|
||||
GotoIf(TaggedIsSmi(object), &if_slow);
|
||||
Node* object_map = LoadMap(object);
|
||||
Node* object_bit_field3 = LoadMapBitField3(object_map);
|
||||
Node* object_enum_length =
|
||||
DecodeWordFromWord32<Map::EnumLengthBits>(object_bit_field3);
|
||||
GotoIf(
|
||||
WordEqual(object_enum_length, IntPtrConstant(kInvalidEnumCacheSentinel)),
|
||||
&try_fast);
|
||||
|
||||
// Ensure that the {object} doesn't have any elements.
|
||||
CSA_ASSERT(this, IsJSObjectMap(object_map));
|
||||
Node* object_elements = LoadElements(object);
|
||||
GotoIf(IsEmptyFixedArray(object_elements), &if_empty_elements);
|
||||
Branch(IsEmptySlowElementDictionary(object_elements), &if_empty_elements,
|
||||
&if_slow);
|
||||
|
||||
// Check whether all own properties are enumerable.
|
||||
BIND(&if_empty_elements);
|
||||
Node* number_descriptors =
|
||||
DecodeWordFromWord32<Map::NumberOfOwnDescriptorsBits>(object_bit_field3);
|
||||
GotoIfNot(WordEqual(object_enum_length, number_descriptors), &if_slow);
|
||||
|
||||
// Check whether there are enumerable properties.
|
||||
Branch(WordEqual(object_enum_length, IntPtrConstant(0)), &if_empty, &if_fast);
|
||||
|
||||
BIND(&if_fast);
|
||||
{
|
||||
// The {object} has a usable enum cache and all own properties are
|
||||
// enumerable, use that.
|
||||
Node* object_descriptors = LoadMapDescriptors(object_map);
|
||||
Node* object_enum_cache =
|
||||
LoadObjectField(object_descriptors, DescriptorArray::kEnumCacheOffset);
|
||||
Node* object_enum_keys =
|
||||
LoadObjectField(object_enum_cache, EnumCache::kKeysOffset);
|
||||
|
||||
// Allocate a JSArray and copy the elements from the {object_enum_keys}.
|
||||
Node* array = nullptr;
|
||||
Node* elements = nullptr;
|
||||
Node* native_context = LoadNativeContext(context);
|
||||
Node* array_map = LoadJSArrayElementsMap(PACKED_ELEMENTS, native_context);
|
||||
Node* array_length = SmiTag(object_enum_length);
|
||||
std::tie(array, elements) = AllocateUninitializedJSArrayWithElements(
|
||||
PACKED_ELEMENTS, array_map, array_length, nullptr, object_enum_length,
|
||||
INTPTR_PARAMETERS);
|
||||
CopyFixedArrayElements(PACKED_ELEMENTS, object_enum_keys, elements,
|
||||
object_enum_length, SKIP_WRITE_BARRIER);
|
||||
Return(array);
|
||||
}
|
||||
|
||||
BIND(&try_fast);
|
||||
{
|
||||
// Let the runtime compute the elements and try initializing enum cache.
|
||||
Node* elements = CallRuntime(Runtime::kObjectGetOwnPropertyNamesTryFast,
|
||||
context, object);
|
||||
var_length.Bind(LoadObjectField(elements, FixedArray::kLengthOffset));
|
||||
var_elements.Bind(elements);
|
||||
Goto(&if_join);
|
||||
}
|
||||
|
||||
BIND(&if_empty);
|
||||
{
|
||||
// The {object} doesn't have any enumerable keys.
|
||||
var_length.Bind(SmiConstant(0));
|
||||
var_elements.Bind(EmptyFixedArrayConstant());
|
||||
Goto(&if_join);
|
||||
}
|
||||
|
||||
BIND(&if_slow);
|
||||
{
|
||||
// Let the runtime compute the elements.
|
||||
Node* elements =
|
||||
CallRuntime(Runtime::kObjectGetOwnPropertyNames, context, object);
|
||||
var_length.Bind(LoadObjectField(elements, FixedArray::kLengthOffset));
|
||||
var_elements.Bind(elements);
|
||||
Goto(&if_join);
|
||||
}
|
||||
|
||||
BIND(&if_join);
|
||||
{
|
||||
// Wrap the elements into a proper JSArray and return that.
|
||||
Node* native_context = LoadNativeContext(context);
|
||||
Node* array_map = LoadJSArrayElementsMap(PACKED_ELEMENTS, native_context);
|
||||
Node* array = AllocateUninitializedJSArrayWithoutElements(
|
||||
array_map, var_length.value(), nullptr);
|
||||
StoreObjectFieldNoWriteBarrier(array, JSArray::kElementsOffset,
|
||||
var_elements.value());
|
||||
Return(array);
|
||||
}
|
||||
}
|
||||
|
||||
TF_BUILTIN(ObjectValues, ObjectEntriesValuesBuiltinsAssembler) {
|
||||
TNode<JSObject> object =
|
||||
TNode<JSObject>::UncheckedCast(Parameter(Descriptor::kObject));
|
||||
|
@ -326,11 +326,6 @@ Object* GetOwnPropertyKeys(Isolate* isolate, BuiltinArguments args,
|
||||
|
||||
} // namespace
|
||||
|
||||
// ES6 section 19.1.2.7 Object.getOwnPropertyNames ( O )
|
||||
BUILTIN(ObjectGetOwnPropertyNames) {
|
||||
return GetOwnPropertyKeys(isolate, args, SKIP_SYMBOLS);
|
||||
}
|
||||
|
||||
// ES6 section 19.1.2.8 Object.getOwnPropertySymbols ( O )
|
||||
BUILTIN(ObjectGetOwnPropertySymbols) {
|
||||
return GetOwnPropertyKeys(isolate, args, SKIP_STRINGS);
|
||||
|
@ -331,6 +331,8 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
|
||||
V(ObjectHasOwnProperty) \
|
||||
V(ObjectValues) \
|
||||
V(ObjectValuesSkipFastPath) \
|
||||
V(ObjectGetOwnPropertyNames) \
|
||||
V(ObjectGetOwnPropertyNamesTryFast) \
|
||||
V(RegExpInitializeAndCompile) \
|
||||
V(StackGuard) \
|
||||
V(StringAdd) \
|
||||
|
@ -244,6 +244,56 @@ RUNTIME_FUNCTION(Runtime_ObjectKeys) {
|
||||
return *keys;
|
||||
}
|
||||
|
||||
// ES #sec-object.getOwnPropertyNames
|
||||
RUNTIME_FUNCTION(Runtime_ObjectGetOwnPropertyNames) {
|
||||
HandleScope scope(isolate);
|
||||
Handle<Object> object = args.at(0);
|
||||
|
||||
// Convert the {object} to a proper {receiver}.
|
||||
Handle<JSReceiver> receiver;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, receiver,
|
||||
Object::ToObject(isolate, object));
|
||||
|
||||
// Collect the own keys for the {receiver}.
|
||||
Handle<FixedArray> keys;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, keys,
|
||||
KeyAccumulator::GetKeys(receiver, KeyCollectionMode::kOwnOnly,
|
||||
SKIP_SYMBOLS,
|
||||
GetKeysConversion::kConvertToString));
|
||||
return *keys;
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ObjectGetOwnPropertyNamesTryFast) {
|
||||
HandleScope scope(isolate);
|
||||
Handle<Object> object = args.at(0);
|
||||
|
||||
// Convert the {object} to a proper {receiver}.
|
||||
Handle<JSReceiver> receiver;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, receiver,
|
||||
Object::ToObject(isolate, object));
|
||||
|
||||
Handle<Map> map(receiver->map(), isolate);
|
||||
|
||||
int nod = map->NumberOfOwnDescriptors();
|
||||
Handle<FixedArray> keys;
|
||||
if (nod != 0 && map->NumberOfEnumerableProperties() == nod) {
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, keys,
|
||||
KeyAccumulator::GetKeys(receiver, KeyCollectionMode::kOwnOnly,
|
||||
ENUMERABLE_STRINGS,
|
||||
GetKeysConversion::kConvertToString));
|
||||
} else {
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, keys,
|
||||
KeyAccumulator::GetKeys(receiver, KeyCollectionMode::kOwnOnly,
|
||||
SKIP_SYMBOLS,
|
||||
GetKeysConversion::kConvertToString));
|
||||
}
|
||||
|
||||
return *keys;
|
||||
}
|
||||
|
||||
// ES6 19.1.3.2
|
||||
RUNTIME_FUNCTION(Runtime_ObjectHasOwnProperty) {
|
||||
HandleScope scope(isolate);
|
||||
|
@ -357,6 +357,8 @@ namespace internal {
|
||||
F(ObjectEntriesSkipFastPath, 1, 1) \
|
||||
F(ObjectHasOwnProperty, 2, 1) \
|
||||
F(ObjectKeys, 1, 1) \
|
||||
F(ObjectGetOwnPropertyNames, 1, 1) \
|
||||
F(ObjectGetOwnPropertyNamesTryFast, 1, 1) \
|
||||
F(ObjectValues, 1, 1) \
|
||||
F(ObjectValuesSkipFastPath, 1, 1) \
|
||||
F(OptimizeObjectForAddingMultipleProperties, 2, 1) \
|
||||
|
Loading…
Reference in New Issue
Block a user