Dictionary to array for fills

Issues: https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I8C4I2?from=project-issue
Signed-off-by: liuzhijie <jay.lau2020.work@outlook.com>

Change-Id: Ie79be53b1ec05381ca6339edb0e94ddfc63d09ea
This commit is contained in:
liuzhijie 2023-10-30 20:16:50 +08:00
parent bba3353ad5
commit b7a21e857d
10 changed files with 390 additions and 1 deletions

View File

@ -745,6 +745,16 @@ JSTaggedValue BuiltinsArray::Fill(EcmaRuntimeCallInfo *argv)
// 1. Let O be ToObject(this value).
JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
JSHandle<JSObject> thisObjHandle = JSTaggedValue::ToObject(thread, thisHandle);
bool isDictionary = thisObjHandle->GetJSHClass()->IsDictionaryElement();
if (isDictionary && thisObjHandle->IsJSArray()) {
uint32_t length = JSArray::Cast(*thisObjHandle)->GetLength();
uint32_t size = thisObjHandle->GetNumberOfElements();
if (length - size > JSObject::MAX_GAP) {
JSObject::TryOptimizeAsFastElements(thread, thisObjHandle);
}
}
// 2. ReturnIfAbrupt(O).
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> thisObjVal(thisObjHandle);

View File

@ -30,6 +30,7 @@
#include "ecmascript/mem/c_containers.h"
#include "ecmascript/subtyping_operator.h"
#include "ecmascript/tagged_array-inl.h"
#include "ecmascript/tagged_dictionary.h"
#include "ecmascript/weak_vector.h"
namespace panda::ecmascript {
@ -220,6 +221,18 @@ void JSHClass::TransitionElementsToDictionary(const JSThread *thread, const JSHa
obj->GetJSHClass()->SetElementsKind(ElementsKind::GENERIC);
}
void JSHClass::OptimizeAsFastElements(const JSThread *thread, JSHandle<JSObject> obj)
{
if (obj->GetJSHClass()->IsDictionaryMode()) {
JSObject::OptimizeAsFastProperties(thread, obj);
} else {
OptimizeAsFastProperties(thread, obj);
}
obj->GetJSHClass()->SetIsDictionaryElement(false);
obj->GetJSHClass()->SetIsStableElements(true);
obj->GetJSHClass()->SetElementsKind(ElementsKind::HOLE_TAGGED);
}
JSHandle<JSHClass> JSHClass::SetPropertyOfObjHClass(const JSThread *thread, JSHandle<JSHClass> &jshclass,
const JSHandle<JSTaggedValue> &key,
const PropertyAttributes &attr)
@ -454,6 +467,52 @@ void JSHClass::TransitionToDictionary(const JSThread *thread, const JSHandle<JSO
}
}
void JSHClass::OptimizeAsFastProperties(const JSThread *thread, const JSHandle<JSObject> &obj,
const std::vector<int> &indexOrder, bool isDictionary)
{
// 1. new a hclass
JSHandle<JSHClass> jshclass(thread, obj->GetJSHClass());
JSHandle<JSHClass> newJsHClass = Clone(thread, jshclass, isDictionary);
UpdateRootHClass(thread, jshclass, newJsHClass);
// 2. If it is dictionary, migrate should change layout. otherwise, copy the hclass only.
JSHandle<NameDictionary> properties(thread, obj->GetProperties());
int numberOfProperties = properties->EntriesCount();
if (isDictionary) {
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<LayoutInfo> layoutInfoHandle = factory->CreateLayoutInfo(numberOfProperties);
int numberOfInlinedProps = newJsHClass->GetInlinedProperties();
for (int i = 0; i < numberOfProperties; i++) {
JSTaggedValue key = properties->GetKey(indexOrder[i]);
PropertyAttributes attributes = properties->GetAttributes(indexOrder[i]);
if (i < numberOfInlinedProps) {
attributes.SetIsInlinedProps(true);
} else {
attributes.SetIsInlinedProps(false);
}
attributes.SetOffset(i);
layoutInfoHandle->AddKey(thread, i, key, attributes);
}
{
DISALLOW_GARBAGE_COLLECTION;
newJsHClass->SetNumberOfProps(numberOfProperties);
newJsHClass->SetLayout(thread, layoutInfoHandle);
}
}
{
DISALLOW_GARBAGE_COLLECTION;
// 3. Copy
newJsHClass->SetIsDictionaryMode(false);
// 4. Add newJsHClass to ?
#if ECMASCRIPT_ENABLE_IC
JSHClass::NotifyHclassChanged(thread, JSHandle<JSHClass>(thread, obj->GetJSHClass()), newJsHClass);
#endif
obj->SynchronizedSetClass(*newJsHClass);
}
}
void JSHClass::TransitionForRepChange(const JSThread *thread, const JSHandle<JSObject> &receiver,
const JSHandle<JSTaggedValue> &key, PropertyAttributes attr)
{

View File

@ -386,6 +386,9 @@ public:
static JSHandle<JSHClass> CloneWithoutInlinedProperties(const JSThread *thread, const JSHandle<JSHClass> &jshclass);
static void TransitionElementsToDictionary(const JSThread *thread, const JSHandle<JSObject> &obj);
static void OptimizeAsFastElements(const JSThread *thread, JSHandle<JSObject> obj);
static void OptimizeAsFastProperties(const JSThread *thread, const JSHandle<JSObject> &obj,
const std::vector<int> &indexArray = {}, bool isDictionary = false);
static JSHandle<JSHClass> SetPropertyOfObjHClass(const JSThread *thread, JSHandle<JSHClass> &jshclass,
const JSHandle<JSTaggedValue> &key,
const PropertyAttributes &attr);

View File

@ -239,6 +239,82 @@ void JSObject::ElementsToDictionary(const JSThread *thread, JSHandle<JSObject> o
JSHClass::TransitionElementsToDictionary(thread, obj);
}
inline bool JSObject::ShouldOptimizeAsFastElements(const JSThread *thread, JSHandle<JSObject> obj)
{
JSHandle<NumberDictionary> elements(thread, obj->GetElements());
uint32_t size = elements->Size();
for (uint32_t hashIndex = 0; hashIndex < size; hashIndex++) {
JSTaggedValue key = elements->GetKey(hashIndex);
if (key.IsUndefined() || key.IsHole()) {
continue;
}
PropertyAttributes attr = elements->GetAttributes(hashIndex);
if (!attr.IsDefaultAttributes()) {
return false;
}
}
return true;
}
void JSObject::TryOptimizeAsFastElements(const JSThread *thread, JSHandle<JSObject> obj)
{
ASSERT(obj->GetJSHClass()->IsDictionaryElement() && obj->IsJSArray());
if (ShouldOptimizeAsFastElements(thread, obj)) {
uint32_t length = JSArray::Cast(*obj)->GetLength();
JSHandle<NumberDictionary> elements(thread, obj->GetElements());
uint32_t size = elements->Size();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<TaggedArray> array = factory->NewTaggedArray(length);
for (uint32_t hashIndex = 0; hashIndex < size; hashIndex++) {
JSTaggedValue key = elements->GetKey(hashIndex);
JSTaggedValue value = elements->GetValue(hashIndex);
if (key.IsUndefined() || key.IsHole()) {
continue;
}
array->Set(thread, key.GetNumber(), value);
}
obj->SetElements(thread, array);
JSHClass::OptimizeAsFastElements(thread, obj);
}
}
void JSObject::OptimizeAsFastProperties(const JSThread *thread, JSHandle<JSObject> obj)
{
ASSERT(obj->GetJSHClass()->IsDictionaryMode());
// 1. Get NameDictionary properties
JSHandle<NameDictionary> properties(thread, obj->GetProperties());
int numberOfProperties = properties->EntriesCount();
// Make sure we preserve enough capacity
if (numberOfProperties > static_cast<int>(PropertyAttributes::MAX_FAST_PROPS_CAPACITY)) {
return ;
}
// 2. iteration indices
std::vector<int> indexOrder = properties->GetEnumerationOrder();
ASSERT(static_cast<int>(indexOrder.size()) == numberOfProperties);
// 3. Change Hclass
int numberOfInlinedProps = obj->GetJSHClass()->GetInlinedProperties();
JSHClass::OptimizeAsFastProperties(thread, obj, indexOrder, true);
// 4. New out-properties
int numberOfOutProperties = numberOfProperties - numberOfInlinedProps;
ASSERT(numberOfOutProperties >= 0);
JSHandle<TaggedArray> array = thread->GetEcmaVM()->GetFactory()->NewTaggedArray(numberOfOutProperties);
// 5. Fill properties
for (int i = 0; i < numberOfProperties; i++) {
JSTaggedValue value = properties->GetValue(indexOrder[i]);
if (i < numberOfInlinedProps) {
obj->SetPropertyInlinedPropsWithRep(thread, i, value);
} else {
array->Set(thread, i - numberOfInlinedProps, value);
}
}
obj->SetProperties(thread, array);
}
bool JSObject::IsArrayLengthWritable(JSThread *thread, const JSHandle<JSObject> &receiver)
{
auto *hclass = receiver->GetJSHClass();

View File

@ -655,6 +655,7 @@ public:
static bool IsArrayLengthWritable(JSThread *thread, const JSHandle<JSObject> &receiver);
bool UpdatePropertyInDictionary(const JSThread *thread, JSTaggedValue key, JSTaggedValue value);
static bool ShouldTransToDict(uint32_t capacity, uint32_t index);
static bool ShouldOptimizeAsFastElements(const JSThread *thread, JSHandle<JSObject> obj);
static JSHandle<TaggedArray> GrowElementsCapacity(const JSThread *thread, const JSHandle<JSObject> &obj,
uint32_t capacity, bool highGrowth = false, bool isNew = false);
@ -670,6 +671,9 @@ public:
static JSHandle<JSTaggedValue> IterableToList(JSThread *thread, const JSHandle<JSTaggedValue> &items,
JSTaggedValue method = JSTaggedValue::Undefined());
static void TryOptimizeAsFastElements(const JSThread *thread, JSHandle<JSObject> obj);
static void OptimizeAsFastProperties(const JSThread *thread, JSHandle<JSObject> obj);
protected:
static void ElementsToDictionary(const JSThread *thread, JSHandle<JSObject> obj);

View File

@ -16,7 +16,6 @@
#ifndef ECMASCRIPT_TAGGED_DICTIONARY_H
#define ECMASCRIPT_TAGGED_DICTIONARY_H
#include "ecmascript/ecma_vm.h"
#include "ecmascript/js_tagged_value-inl.h"
#include "ecmascript/tagged_array-inl.h"
#include "ecmascript/tagged_hash_table.h"
@ -76,6 +75,11 @@ public:
{
return a.second.GetDictionaryOrder() < b.second.GetDictionaryOrder();
}
static inline bool CompIndex(const PropertyAttributes &a,
const PropertyAttributes &b)
{
return a.GetDictionaryOrder() < b.GetDictionaryOrder();
}
DECL_DUMP()
static constexpr int ENTRY_KEY_INDEX = 0;

View File

@ -18,6 +18,7 @@ group("object_test") {
"object_assign",
"object_hasOwnProperty",
"object_toString",
"object_transition",
]
deps = []

View File

@ -0,0 +1,18 @@
# Copyright (c) 2023 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//arkcompiler/ets_runtime/test/test_helper.gni")
host_aot_test_action("object_transition") {
deps = []
}

View File

@ -0,0 +1,63 @@
# Copyright (c) 2023 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
TestSample:
1
TestArrayWithElementsAndProperties:
000
000
1
2
3
4
5
2047
TestFullArrayWithElementsAndProperties:
0
0
0
0
0
0
0
undefined
undefined
1
2
3
4
5
2047
TestShouldNotOptimizeAsFastElements:
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
1
2
3
4
5
0
TestStringArrayWithElementsAndProperties:
ark
ark
b
TestSpecialCase:

View File

@ -0,0 +1,151 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function TestSample()
{
print("TestSample:");
let arr: number[] = new Array(1025).fill(0);
arr[1] = 1;
print(arr[1]);
}
function TestArrayWithElementsAndProperties()
{
print("TestArrayWithElementsAndProperties:");
let arr: number[] = new Array(2048)
arr[1] = 2;
arr[3] = 4;
arr.x1 = 1;
arr.x2 = 2;
arr.x3 = 3;
arr.x4 = 4;
arr.x5 = 5;
arr.length = 2047;
arr.fill("000");
print(arr[1]);
print(arr[3]);
print(arr.x1);
print(arr.x2);
print(arr.x3);
print(arr.x4);
print(arr.x5);
print(arr.length);
}
function TestFullArrayWithElementsAndProperties()
{
print("TestFullArrayWithElementsAndProperties:");
let arr: number[] = new Array(2048)
arr.x1 = 1;
arr.x2 = 2;
arr.x3 = 3;
arr.x4 = 4;
arr.x5 = 5;
for (let i: number = 0; i < 2048; i++) {
arr[i] = "apple"
}
arr.length = 2047;
arr.fill(0);
for (let i: number = 0; i < 5; i++) {
print(arr[i]);
}
for (let i: number = 2045; i < 2048; i++) {
print(arr[i]);
}
print(arr.apple);
print(arr.x1);
print(arr.x2);
print(arr.x3);
print(arr.x4);
print(arr.x5);
print(arr.length);
}
function TestShouldNotOptimizeAsFastElements()
{
print("TestShouldNotOptimizeAsFastElements:");
let arr: number[] = new Array(1025)
arr.x1 = 1;
arr.x2 = 2;
arr.x3 = 3;
arr.x4 = 4;
arr.x5 = 5;
for (let i: number = 0; i < 1025; i++) {
arr[i] = "apple"
}
arr.length = 0;
arr.fill(0);
for (let i: number = 0; i < 5; i++) {
print(arr[i]);
}
for (let i: number = 1020; i < 1025; i++) {
print(arr[i]);
}
print(arr.apple);
print(arr.x1);
print(arr.x2);
print(arr.x3);
print(arr.x4);
print(arr.x5);
print(arr.length);
}
function TestStringArrayWithElementsAndProperties()
{
print("TestStringArrayWithElementsAndProperties:");
let arr: string[] = new Array(1025)
arr[1] = "apple"
arr.apple = "b"
arr.fill("ark");
print(arr[0]);
print(arr[1]);
print(arr.apple);
}
function TestSpecialCase()
{
print("TestSpecialCase:");
let arr: number[] = new Array(1025);
Object.defineProperty(arr, '0', {
value: 42,
writable: false,
});
arr.fill(1);
print(arr[0]);
}
TestSample();
TestArrayWithElementsAndProperties();
TestFullArrayWithElementsAndProperties();
TestShouldNotOptimizeAsFastElements();
TestStringArrayWithElementsAndProperties();
TestSpecialCase();