From f9868eaa53ff036b4dd8b0001e65040eeaf130a3 Mon Sep 17 00:00:00 2001 From: Shiyu Zhang Date: Mon, 25 Jun 2018 09:19:41 +0800 Subject: [PATCH] [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 Reviewed-by: Benedikt Meurer Commit-Queue: Shiyu Zhang Cr-Commit-Position: refs/heads/master@{#53992} --- src/bootstrapper.cc | 2 +- src/builtins/builtins-definitions.h | 2 +- src/builtins/builtins-object-gen.cc | 102 ++++++++++++++++++++++++++++ src/builtins/builtins-object.cc | 5 -- src/debug/debug-evaluate.cc | 2 + src/runtime/runtime-object.cc | 50 ++++++++++++++ src/runtime/runtime.h | 2 + 7 files changed, 158 insertions(+), 7 deletions(-) diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index b50e38471d..1c04208d42 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1452,7 +1452,7 @@ void Genesis::InitializeGlobal(Handle 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, diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h index fbae52460b..6254d659d3 100644 --- a/src/builtins/builtins-definitions.h +++ b/src/builtins/builtins-definitions.h @@ -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) \ diff --git a/src/builtins/builtins-object-gen.cc b/src/builtins/builtins-object-gen.cc index 269ae0ed1f..fb89694c31 100644 --- a/src/builtins/builtins-object-gen.cc +++ b/src/builtins/builtins-object-gen.cc @@ -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(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(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 object = TNode::UncheckedCast(Parameter(Descriptor::kObject)); diff --git a/src/builtins/builtins-object.cc b/src/builtins/builtins-object.cc index 2df04d6bca..83447860e3 100644 --- a/src/builtins/builtins-object.cc +++ b/src/builtins/builtins-object.cc @@ -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); diff --git a/src/debug/debug-evaluate.cc b/src/debug/debug-evaluate.cc index c146d3ca4f..22e4f38e5e 100644 --- a/src/debug/debug-evaluate.cc +++ b/src/debug/debug-evaluate.cc @@ -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) \ diff --git a/src/runtime/runtime-object.cc b/src/runtime/runtime-object.cc index f2c5042477..34fbe9ebe3 100644 --- a/src/runtime/runtime-object.cc +++ b/src/runtime/runtime-object.cc @@ -244,6 +244,56 @@ RUNTIME_FUNCTION(Runtime_ObjectKeys) { return *keys; } +// ES #sec-object.getOwnPropertyNames +RUNTIME_FUNCTION(Runtime_ObjectGetOwnPropertyNames) { + HandleScope scope(isolate); + Handle object = args.at(0); + + // Convert the {object} to a proper {receiver}. + Handle receiver; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, receiver, + Object::ToObject(isolate, object)); + + // Collect the own keys for the {receiver}. + Handle 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 = args.at(0); + + // Convert the {object} to a proper {receiver}. + Handle receiver; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, receiver, + Object::ToObject(isolate, object)); + + Handle map(receiver->map(), isolate); + + int nod = map->NumberOfOwnDescriptors(); + Handle 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); diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 4f5f6dc31d..bfcb9d3127 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -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) \