ElementsKind Support JSArray as Proto

Issue: https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/IAE3RU

Signed-off-by: yaoyuan <yuanyao14@huawei.com>
Change-Id: I4501de04972faa211d6f5822c951da8592b909d0
This commit is contained in:
yaoyuan 2024-07-20 16:21:36 +08:00
parent be50acd63d
commit 72a3e62801
26 changed files with 223 additions and 102 deletions

View File

@ -484,8 +484,8 @@ JSTaggedValue BuiltinsArkTools::GetElementsKind(EcmaRuntimeCallInfo *info)
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> obj = GetCallArg(info, 0);
JSHandle<JSObject> receiver(thread, obj.GetTaggedValue());
ElementsKind kind = receiver->GetClass()->GetElementsKind();
JSHClass *hclass = obj->GetTaggedObject()->GetClass();
ElementsKind kind = hclass->GetElementsKind();
return JSTaggedValue(static_cast<uint32_t>(kind));
}

View File

@ -33,7 +33,7 @@ JSHandle<GlobalEnv> AOTCompilationEnv::GetGlobalEnv() const
return vm_->GetGlobalEnv();
}
const CMap<ElementsKind, ConstantIndex> &AOTCompilationEnv::GetArrayHClassIndexMap() const
const CMap<ElementsKind, std::pair<ConstantIndex, ConstantIndex>> &AOTCompilationEnv::GetArrayHClassIndexMap() const
{
return thread_->GetArrayHClassIndexMap();
}

View File

@ -31,7 +31,7 @@ public:
JSRuntimeOptions &GetJSOptions() override;
// thread
const CMap<ElementsKind, ConstantIndex> &GetArrayHClassIndexMap() const override;
const CMap<ElementsKind, std::pair<ConstantIndex, ConstantIndex>> &GetArrayHClassIndexMap() const override;
const BuiltinHClassEntries &GetBuiltinHClassEntries() const override;
JSHClass *GetBuiltinPrototypeHClass(BuiltinTypeId type) const override;

View File

@ -60,7 +60,7 @@ public:
virtual std::shared_ptr<pgo::PGOProfiler> GetPGOProfiler() const;
// thread
virtual const CMap<ElementsKind, ConstantIndex> &GetArrayHClassIndexMap() const = 0;
virtual const CMap<ElementsKind, std::pair<ConstantIndex, ConstantIndex>> &GetArrayHClassIndexMap() const = 0;
virtual const BuiltinHClassEntries &GetBuiltinHClassEntries() const = 0;
virtual JSHClass *GetBuiltinPrototypeHClass(BuiltinTypeId type) const = 0;

View File

@ -40,7 +40,7 @@ JSRuntimeOptions &JitCompilationEnv::GetJSOptions()
return hostThread_->GetEcmaVM()->GetJSOptions();
}
const CMap<ElementsKind, ConstantIndex> &JitCompilationEnv::GetArrayHClassIndexMap() const
const CMap<ElementsKind, std::pair<ConstantIndex, ConstantIndex>> &JitCompilationEnv::GetArrayHClassIndexMap() const
{
return hostThread_->GetArrayHClassIndexMap();
}

View File

@ -30,7 +30,7 @@ public:
}
JSRuntimeOptions &GetJSOptions() override;
// thread
const CMap<ElementsKind, ConstantIndex> &GetArrayHClassIndexMap() const override;
const CMap<ElementsKind, std::pair<ConstantIndex, ConstantIndex>> &GetArrayHClassIndexMap() const override;
const BuiltinHClassEntries &GetBuiltinHClassEntries() const override;
JSHClass *GetBuiltinPrototypeHClass(BuiltinTypeId type) const override;
void SetTsManagerCompilationEnv();

View File

@ -206,7 +206,8 @@ GateRef NTypeHCRLowering::NewJSArrayLiteral(GateRef glue, GateRef gate, GateRef
ElementsKind kind = acc_.GetArrayMetaDataAccessor(gate).GetElementsKind();
GateRef hclass = Circuit::NullGate();
if (!Elements::IsGeneric(kind)) {
auto hclassIndex = compilationEnv_->GetArrayHClassIndexMap().at(kind);
// At define point, we use initial array class without IsPrototype set.
auto hclassIndex = compilationEnv_->GetArrayHClassIndexMap().at(kind).first;
hclass = builder_.GetGlobalConstantValue(hclassIndex);
} else {
GateRef globalEnv = builder_.GetGlobalEnv();

View File

@ -109,7 +109,9 @@ bool ArrayParser::RecordTypeInfo(const PGODefineOpType &defType, const PGOTypeLo
ptManager_->RecordLocationToElementsKind(loc, kind);
auto traceId = rootType.GetId();
auto hclassIdx = ptManager_->GetJSThread()->GetArrayHClassIndexMap().at(kind);
// For PGO, we do not care whether an array isPrototype or not.
// This type is used at define point, we can use initial array hclass without IsPrototype bit set.
auto hclassIdx = ptManager_->GetJSThread()->GetArrayHClassIndexMap().at(kind).first;
ptManager_->RecordConstantIndex(traceId, static_cast<uint32_t>(hclassIdx));
return true;
}

View File

@ -1567,7 +1567,7 @@ void SlowPathLowering::LowerFastStrictEqual(GateRef gate)
void SlowPathLowering::LowerCreateEmptyArray(GateRef gate)
{
GateRef result = builder_.CallStub(glue_, gate, CommonStubCSigns::CreateEmptyArray, { glue_ });
GateRef newRes = LowerUpdateArrayHClass(gate, result);
GateRef newRes = LowerUpdateArrayHClassAtDefine(gate, result);
ReplaceHirWithValue(gate, newRes, true);
}
@ -1583,15 +1583,15 @@ void SlowPathLowering::LowerCreateArrayWithBuffer(GateRef gate)
GateRef index = builder_.TruncInt64ToInt32(acc_.GetValueIn(gate, 0));
GateRef result = builder_.CallStub(glue_, gate, CommonStubCSigns::CreateArrayWithBuffer, { glue_, index, jsFunc });
// when elementsKind switch on, we should not update arrayHClass here.
GateRef newRes = LowerUpdateArrayHClass(gate, result);
GateRef newRes = LowerUpdateArrayHClassAtDefine(gate, result);
ReplaceHirWithValue(gate, newRes, true);
}
GateRef SlowPathLowering::LowerUpdateArrayHClass(GateRef gate, GateRef array)
GateRef SlowPathLowering::LowerUpdateArrayHClassAtDefine(GateRef gate, GateRef array)
{
ElementsKind kind = acc_.TryGetElementsKind(gate);
if (!Elements::IsGeneric(kind)) {
size_t hclassIndex = static_cast<size_t>(compilationEnv_->GetArrayHClassIndexMap().at(kind));
size_t hclassIndex = static_cast<size_t>(compilationEnv_->GetArrayHClassIndexMap().at(kind).first);
GateRef gConstAddr = builder_.Load(VariableType::JS_POINTER(), glue_,
builder_.IntPtr(JSThread::GlueData::GetGlobalConstOffset(false)));
GateRef constantIndex = builder_.IntPtr(JSTaggedValue::TaggedTypeSize() * hclassIndex);

View File

@ -303,7 +303,7 @@ private:
void LowerSetGeneratorState(GateRef gate);
GateRef GetValueFromTaggedArray(GateRef arrayGate, GateRef indexOffset);
GateRef GetTaggedArrayFromValueIn(Environment *env, GateRef gate, size_t length);
GateRef LowerUpdateArrayHClass(GateRef gate, GateRef array);
GateRef LowerUpdateArrayHClassAtDefine(GateRef gate, GateRef array);
void AddProfiling(GateRef gate, bool skipGenerator = true);
GateRef FastStrictEqual(GateRef left, GateRef right);
void LowerWideLdPatchVar(GateRef gate);

View File

@ -599,9 +599,12 @@ void TypedHCRLowering::BuiltinInstanceHClassCheck(Environment *env, GateRef gate
auto arrayHClassIndexMap = compilationEnv_->GetArrayHClassIndexMap();
auto iter = arrayHClassIndexMap.find(kind);
ASSERT(iter != arrayHClassIndexMap.end());
GateRef initialIhcAddress = builder_.GetGlobalConstantValue(iter->second);
GateRef initialIhcAddress = builder_.GetGlobalConstantValue(iter->second.first);
GateRef initialIhcWithProtoAddress = builder_.GetGlobalConstantValue(iter->second.second);
GateRef receiverHClass = builder_.LoadHClassByConstOffset(receiver);
ihcMatches = builder_.Equal(receiverHClass, initialIhcAddress);
GateRef tryIhcMatches = builder_.Equal(receiverHClass, initialIhcAddress);
GateRef tryIhcWithProtoMatches = builder_.Equal(receiverHClass, initialIhcWithProtoAddress);
ihcMatches = builder_.BoolOr(tryIhcMatches, tryIhcWithProtoMatches);
} else {
GateRef receiverHClass = builder_.LoadHClassByConstOffset(receiver);
GateRef elementsKind = builder_.GetElementsKindByHClass(receiverHClass);

View File

@ -20,21 +20,14 @@
#include "ecmascript/tagged_array-inl.h"
namespace panda::ecmascript {
CMap<ElementsKind, ConstantIndex> Elements::InitializeHClassMap()
CMap<ElementsKind, std::pair<ConstantIndex, ConstantIndex>> Elements::InitializeHClassMap()
{
CMap<ElementsKind, ConstantIndex> result;
result.emplace(ElementsKind::NONE, ConstantIndex::ELEMENT_NONE_HCLASS_INDEX);
result.emplace(ElementsKind::HOLE, ConstantIndex::ELEMENT_HOLE_HCLASS_INDEX);
result.emplace(ElementsKind::INT, ConstantIndex::ELEMENT_INT_HCLASS_INDEX);
result.emplace(ElementsKind::NUMBER, ConstantIndex::ELEMENT_NUMBER_HCLASS_INDEX);
result.emplace(ElementsKind::STRING, ConstantIndex::ELEMENT_STRING_HCLASS_INDEX);
result.emplace(ElementsKind::OBJECT, ConstantIndex::ELEMENT_OBJECT_HCLASS_INDEX);
result.emplace(ElementsKind::TAGGED, ConstantIndex::ELEMENT_TAGGED_HCLASS_INDEX);
result.emplace(ElementsKind::HOLE_INT, ConstantIndex::ELEMENT_HOLE_INT_HCLASS_INDEX);
result.emplace(ElementsKind::HOLE_NUMBER, ConstantIndex::ELEMENT_HOLE_NUMBER_HCLASS_INDEX);
result.emplace(ElementsKind::HOLE_STRING, ConstantIndex::ELEMENT_HOLE_STRING_HCLASS_INDEX);
result.emplace(ElementsKind::HOLE_OBJECT, ConstantIndex::ELEMENT_HOLE_OBJECT_HCLASS_INDEX);
result.emplace(ElementsKind::HOLE_TAGGED, ConstantIndex::ELEMENT_HOLE_TAGGED_HCLASS_INDEX);
CMap<ElementsKind, std::pair<ConstantIndex, ConstantIndex>> result;
#define INIT_ARRAY_HCLASS_INDEX_MAPS(name) \
result.emplace(ElementsKind::name, std::make_pair(ConstantIndex::ELEMENT_##name##_HCLASS_INDEX, \
ConstantIndex::ELEMENT_##name##_PROTO_HCLASS_INDEX));
ELEMENTS_KIND_INIT_HCLASS_LIST(INIT_ARRAY_HCLASS_INDEX_MAPS)
#undef INIT_ARRAY_HCLASS_INDEX_MAPS
return result;
}

View File

@ -21,6 +21,21 @@
#include "ecmascript/mem/c_containers.h"
namespace panda::ecmascript {
#define ELEMENTS_KIND_INIT_HCLASS_LIST(V) \
V(NONE) \
V(HOLE) \
V(INT) \
V(NUMBER) \
V(STRING) \
V(OBJECT) \
V(TAGGED) \
V(HOLE_INT) \
V(HOLE_NUMBER) \
V(HOLE_STRING) \
V(HOLE_OBJECT) \
V(HOLE_TAGGED)
enum class ElementsKind : uint8_t {
NONE = 0x00UL,
HOLE = 0x01UL,
@ -40,7 +55,7 @@ enum class ElementsKind : uint8_t {
class PUBLIC_API Elements {
public:
static CMap<ElementsKind, ConstantIndex> InitializeHClassMap();
static CMap<ElementsKind, std::pair<ConstantIndex, ConstantIndex>> InitializeHClassMap();
static std::string GetString(ElementsKind kind);
static bool IsInt(ElementsKind kind);

View File

@ -419,12 +419,14 @@ void GlobalEnvConstants::InitElementKindHClass(const JSThread *thread, JSHandle<
{
auto map = thread->GetArrayHClassIndexMap();
for (auto iter : map) {
JSHandle<JSHClass> hclass = originHClass;
JSHandle<JSHClass> hclassWithProto = JSHClass::CloneWithElementsKind(thread, originHClass, iter.first, true);
if (iter.first != ElementsKind::GENERIC) {
hclass = JSHClass::Clone(thread, originHClass);
hclass->SetElementsKind(iter.first);
JSHandle<JSHClass> hclass = JSHClass::CloneWithElementsKind(thread, originHClass, iter.first, false);
SetConstant(iter.second.first, hclass);
} else {
SetConstant(iter.second.first, originHClass);
}
SetConstant(iter.second, hclass);
SetConstant(iter.second.second, hclassWithProto);
}
}
} // namespace panda::ecmascript

View File

@ -134,6 +134,18 @@ class ObjectFactory;
V(JSTaggedValue, IteratorResultClass, ITERATOR_RESULT_CLASS, ecma_roots_class) \
V(JSTaggedValue, ClassPrototypeClass, CLASS_PROTOTYPE_HCLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, ClassConstructorClass, CLASS_CONSTRUCTOR_HCLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, ElementNoneProtoClass, ELEMENT_NONE_PROTO_HCLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, ElementHoleProtoClass, ELEMENT_HOLE_PROTO_HCLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, ElementIntProtoClass, ELEMENT_INT_PROTO_HCLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, ElementNumberProtoClass, ELEMENT_NUMBER_PROTO_HCLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, ElementStringProtoClass, ELEMENT_STRING_PROTO_HCLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, ElementObjectProtoClass, ELEMENT_OBJECT_PROTO_HCLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, ElementTaggedProtoClass, ELEMENT_TAGGED_PROTO_HCLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, ElementHoleIntProtoClass, ELEMENT_HOLE_INT_PROTO_HCLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, ElementHoleNumberProtoClass, ELEMENT_HOLE_NUMBER_PROTO_HCLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, ElementHoleStringProtoClass, ELEMENT_HOLE_STRING_PROTO_HCLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, ElementHoleObjectProtoClass, ELEMENT_HOLE_OBJECT_PROTO_HCLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, ElementHoleTaggedProtoClass, ELEMENT_HOLE_TAGGED_PROTO_HCLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, ElementNoneClass, ELEMENT_NONE_HCLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, ElementHoleClass, ELEMENT_HOLE_HCLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, ElementIntClass, ELEMENT_INT_HCLASS_INDEX, ecma_roots_class) \

View File

@ -985,7 +985,8 @@ bool JITProfiler::AddBuiltinsInfoByNameInInstance(ApEntityId abcId, int32_t bcOf
}
JSHClass *exceptRecvHClass = nullptr;
if (builtinsId == BuiltinTypeId::ARRAY) {
exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind());
bool receiverIsPrototype = receiver->IsPrototype();
exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind(), receiverIsPrototype);
} else if (builtinsId == BuiltinTypeId::STRING) {
exceptRecvHClass = receiver;
} else {
@ -1021,7 +1022,8 @@ bool JITProfiler::AddBuiltinsInfoByNameInProt(ApEntityId abcId, int32_t bcOffset
auto thread = vm_->GetJSThread();
JSHClass *exceptRecvHClass = nullptr;
if (builtinsId == BuiltinTypeId::ARRAY) {
exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind());
bool receiverIsPrototype = receiver->IsPrototype();
exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind(), receiverIsPrototype);
} else if (builtinsId == BuiltinTypeId::STRING) {
exceptRecvHClass = receiver;
} else {

View File

@ -30,6 +30,22 @@ inline JSHClass *JSHClass::Cast(const TaggedObject *object)
return static_cast<JSHClass *>(const_cast<TaggedObject *>(object));
}
bool JSHClass::ProtoIsFastJSArray(const JSThread *thread, const JSHandle<JSTaggedValue> proto,
const JSHandle<JSHClass> hclass)
{
// Since we currently only support ElementsKind for JSArray initial hclass,
// if an object's hclass has a non-generic ElementsKind, it must be one of the JSArray initial hclass.
// if an object's hclass has a Generic ElementsKind, it might be the JSArray initial generic elementskind hclass,
// which therefore needs further hclass comparison.
if (proto->IsJSArray()) {
JSTaggedValue genericArrayHClass = thread->GlobalConstants()->GetElementHoleTaggedClass();
if (!Elements::IsGeneric(hclass->GetElementsKind()) || hclass.GetTaggedValue() == genericArrayHClass) {
return true;
}
}
return false;
}
void JSHClass::AddTransitions(const JSThread *thread, const JSHandle<JSHClass> &parent, const JSHandle<JSHClass> &child,
const JSHandle<JSTaggedValue> &key, PropertyAttributes attributes)
{

View File

@ -267,6 +267,15 @@ JSHandle<JSHClass> JSHClass::Clone(const JSThread *thread, const JSHandle<JSHCla
return newJsHClass;
}
JSHandle<JSHClass> JSHClass::CloneWithElementsKind(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
const ElementsKind kind, bool isPrototype)
{
JSHandle<JSHClass> newHClass = Clone(thread, jshclass);
newHClass->SetIsPrototype(isPrototype);
newHClass->SetElementsKind(kind);
return newHClass;
}
// use for transition to dictionary
JSHandle<JSHClass> JSHClass::CloneWithoutInlinedProperties(const JSThread *thread, const JSHandle<JSHClass> &jshclass)
{
@ -530,7 +539,16 @@ void JSHClass::OptimizePrototypeForIC(const JSThread *thread, const JSHandle<JST
// o1 becomes a prototype object of object o2 and an on-proto IC loading x from o2 will rely on the
// stability of the prototype-chain o2 -> o1. If directly marking the o1.hclass1 as a prototype hclass,
// the previous IC of adding property x won't trigger IC-miss and fails to notify the IC on o2.
JSHandle<JSHClass> newProtoClass = JSHClass::Clone(thread, hclass);
// At here, When a JSArray with initial hclass is set as a proto,
// we substitute its hclass with preserved proto hclass.
JSHandle<JSHClass> newProtoClass;
if (ProtoIsFastJSArray(thread, proto, hclass)) {
newProtoClass = JSHandle<JSHClass>(thread, thread->GetArrayInstanceHClass(hclass->GetElementsKind(),
true));
} else {
newProtoClass = JSHClass::Clone(thread, hclass);
}
JSTaggedValue layout = newProtoClass->GetLayout();
// If the type of object is JSObject, the layout info value is initialized to the default value,
// if the value is not JSObject, the layout info value is initialized to null.
@ -551,9 +569,6 @@ void JSHClass::OptimizePrototypeForIC(const JSThread *thread, const JSHandle<JST
if (!isChangeProto) {
thread->GetEcmaVM()->GetPGOProfiler()->UpdateRootProfileTypeSafe(*hclass, *newProtoClass);
}
if (proto->IsJSObject()) {
TryRestoreElementsKind(thread, newProtoClass, JSHandle<JSObject>::Cast(proto));
}
} else {
// There is no sharing in AOT hclass. Therefore, it is not necessary or possible to clone here.
hclass->SetIsPrototype(true);
@ -659,17 +674,21 @@ void JSHClass::TransitionForRepChange(const JSThread *thread, const JSHandle<JSO
// 4. Maybe Transition And Maintain subtypeing check
}
JSHClass* JSHClass::GetInitialArrayHClassWithElementsKind(const JSThread *thread, const ElementsKind kind)
bool JSHClass::IsInitialArrayHClassWithElementsKind(const JSThread *thread, const JSHClass *targetHClass,
const ElementsKind targetKind)
{
const auto &arrayHClassIndexMap = thread->GetArrayHClassIndexMap();
auto newKindIter = arrayHClassIndexMap.find(kind);
auto newKindIter = arrayHClassIndexMap.find(targetKind);
if (newKindIter != arrayHClassIndexMap.end()) {
auto index = static_cast<size_t>(newKindIter->second);
auto hclassVal = thread->GlobalConstants()->GetGlobalConstantObject(index);
auto indexPair = newKindIter->second;
auto hclassVal = thread->GlobalConstants()->GetGlobalConstantObject(static_cast<size_t>(indexPair.first));
auto hclassWithProtoVal = thread->GlobalConstants()->
GetGlobalConstantObject(static_cast<size_t>(indexPair.second));
JSHClass *hclass = JSHClass::Cast(hclassVal.GetTaggedObject());
return hclass;
JSHClass *hclassWithProto = JSHClass::Cast(hclassWithProtoVal.GetTaggedObject());
return (targetHClass == hclass || targetHClass == hclassWithProto);
}
return nullptr;
return false;
}
bool JSHClass::TransitToElementsKindUncheck(const JSThread *thread, const JSHandle<JSObject> &obj,
@ -677,16 +696,21 @@ bool JSHClass::TransitToElementsKindUncheck(const JSThread *thread, const JSHand
{
ElementsKind current = obj->GetJSHClass()->GetElementsKind();
// currently we only support initial array hclass
if (obj->GetClass() == GetInitialArrayHClassWithElementsKind(thread, current)) {
JSHClass *objHclass = obj->GetClass();
if (IsInitialArrayHClassWithElementsKind(thread, objHclass, current)) {
const auto &arrayHClassIndexMap = thread->GetArrayHClassIndexMap();
auto newKindIter = arrayHClassIndexMap.find(newKind);
bool objHclassIsPrototype = objHclass->IsPrototype();
if (newKindIter != arrayHClassIndexMap.end()) {
auto index = static_cast<size_t>(newKindIter->second);
auto indexPair = newKindIter->second;
auto index = objHclassIsPrototype ? static_cast<size_t>(indexPair.second) :
static_cast<size_t>(indexPair.first);
auto hclassVal = thread->GlobalConstants()->GetGlobalConstantObject(index);
JSHClass *hclass = JSHClass::Cast(hclassVal.GetTaggedObject());
obj->SynchronizedSetClass(thread, hclass);
return true;
}
LOG_ECMA(FATAL) << "Unknown newKind: " << static_cast<int32_t>(newKind);
}
return false;
}
@ -703,16 +727,9 @@ void JSHClass::TransitToElementsKind(const JSThread *thread, const JSHandle<JSAr
if (newKind == current) {
return;
}
// Currently, we only support fast array elementsKind
ASSERT(array->GetClass() == GetInitialArrayHClassWithElementsKind(thread, current));
const auto &arrayHClassIndexMap = thread->GetArrayHClassIndexMap();
auto newKindIter = arrayHClassIndexMap.find(newKind);
if (newKindIter != arrayHClassIndexMap.end()) {
auto index = static_cast<size_t>(newKindIter->second);
auto hclassVal = thread->GlobalConstants()->GetGlobalConstantObject(index);
JSHClass *hclass = JSHClass::Cast(hclassVal.GetTaggedObject());
array->SynchronizedSetClass(thread, hclass);
}
ASSERT(IsInitialArrayHClassWithElementsKind(thread, array->GetJSHClass(), current));
TransitToElementsKindUncheck(thread, JSHandle<JSObject>(array), newKind);
}
bool JSHClass::TransitToElementsKind(const JSThread *thread, const JSHandle<JSObject> &object,
@ -732,27 +749,21 @@ bool JSHClass::TransitToElementsKind(const JSThread *thread, const JSHandle<JSOb
return false;
}
// Currently, we only support fast array elementsKind
ASSERT(object->GetClass() == GetInitialArrayHClassWithElementsKind(thread, current));
const auto &arrayHClassIndexMap = thread->GetArrayHClassIndexMap();
auto newKindIter = arrayHClassIndexMap.find(newKind);
if (newKindIter != arrayHClassIndexMap.end()) {
auto index = static_cast<size_t>(newKindIter->second);
auto hclassVal = thread->GlobalConstants()->GetGlobalConstantObject(index);
JSHClass *hclass = JSHClass::Cast(hclassVal.GetTaggedObject());
object->SynchronizedSetClass(thread, hclass);
ASSERT(IsInitialArrayHClassWithElementsKind(thread, object->GetJSHClass(), current));
if (!TransitToElementsKindUncheck(thread, object, newKind)) {
return false;
}
if (!thread->GetEcmaVM()->IsEnableElementsKind()) {
// Update TrackInfo
if (!thread->IsPGOProfilerEnable()) {
return true;
}
auto trackInfoVal = JSHandle<JSArray>(object)->GetTrackInfo();
thread->GetEcmaVM()->GetPGOProfiler()->UpdateTrackElementsKind(trackInfoVal, newKind);
if (!thread->GetEcmaVM()->IsEnableElementsKind()) {
// Update TrackInfo
if (!thread->IsPGOProfilerEnable()) {
return true;
}
auto trackInfoVal = JSHandle<JSArray>(object)->GetTrackInfo();
thread->GetEcmaVM()->GetPGOProfiler()->UpdateTrackElementsKind(trackInfoVal, newKind);
return true;
}
return false;
return true;
}
TransitionResult JSHClass::ConvertOrTransitionWithRep(const JSThread *thread,

View File

@ -427,6 +427,8 @@ public:
static JSHandle<JSHClass> Clone(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
bool withoutInlinedProperties = false, uint32_t incInlinedProperties = 0);
static JSHandle<JSHClass> CloneWithoutInlinedProperties(const JSThread *thread, const JSHandle<JSHClass> &jshclass);
static JSHandle<JSHClass> CloneWithElementsKind(const JSThread *thread, const JSHandle<JSHClass> &jshclass,
const ElementsKind kind, bool isPrototype);
static void TransitionElementsToDictionary(const JSThread *thread, const JSHandle<JSObject> &obj);
static void OptimizeAsFastElements(const JSThread *thread, JSHandle<JSObject> obj);
@ -463,7 +465,8 @@ public:
const JSHandle<JSTaggedValue> &key, PropertyAttributes attr);
static void TransitionForElementsKindChange(const JSThread *thread, const JSHandle<JSObject> &receiver,
const ElementsKind newKind);
static JSHClass* GetInitialArrayHClassWithElementsKind(const JSThread *thread, const ElementsKind kind);
static bool IsInitialArrayHClassWithElementsKind(const JSThread *thread, const JSHClass *targetHClass,
const ElementsKind targetKind);
static bool PUBLIC_API TransitToElementsKindUncheck(const JSThread *thread, const JSHandle<JSObject> &obj,
ElementsKind newKind);
static void PUBLIC_API TransitToElementsKind(const JSThread *thread, const JSHandle<JSArray> &array,
@ -2040,6 +2043,10 @@ public:
static JSHandle<JSHClass> CreateSPrototypeHClass(JSThread *thread, const std::vector<PropertyDescriptor> &descs);
private:
static inline bool ProtoIsFastJSArray(const JSThread *thread, const JSHandle<JSTaggedValue> proto,
const JSHandle<JSHClass> hclass);
static void CreateSInlinedLayout(JSThread *thread,
const std::vector<PropertyDescriptor> &descs,
const JSHandle<JSHClass> &hclass,

View File

@ -619,11 +619,11 @@ JSHClass *JSThread::GetBuiltinExtraHClass(BuiltinTypeId type) const
return glueData_.builtinHClassEntries_.entries[index].extraHClass;
}
JSHClass *JSThread::GetArrayInstanceHClass(ElementsKind kind) const
JSHClass *JSThread::GetArrayInstanceHClass(ElementsKind kind, bool isPrototype) const
{
auto iter = GetArrayHClassIndexMap().find(kind);
ASSERT(iter != GetArrayHClassIndexMap().end());
auto index = static_cast<size_t>(iter->second);
auto index = isPrototype ? static_cast<size_t>(iter->second.second) : static_cast<size_t>(iter->second.first);
auto exceptArrayHClass = GlobalConstants()->GetGlobalConstantObject(index);
auto exceptRecvHClass = JSHClass::Cast(exceptArrayHClass.GetTaggedObject());
ASSERT(exceptRecvHClass->IsJSArray());

View File

@ -316,7 +316,7 @@ public:
return &glueData_.builtinEntries_;
}
const CMap<ElementsKind, ConstantIndex> &GetArrayHClassIndexMap() const
const CMap<ElementsKind, std::pair<ConstantIndex, ConstantIndex>> &GetArrayHClassIndexMap() const
{
return arrayHClassIndexMap_;
}
@ -346,7 +346,7 @@ public:
JSHClass *GetBuiltinInstanceHClass(BuiltinTypeId type) const;
JSHClass *GetBuiltinExtraHClass(BuiltinTypeId type) const;
JSHClass *GetArrayInstanceHClass(ElementsKind kind) const;
JSHClass *GetArrayInstanceHClass(ElementsKind kind, bool isPrototype) const;
PUBLIC_API JSHClass *GetBuiltinPrototypeHClass(BuiltinTypeId type) const;
PUBLIC_API JSHClass *GetBuiltinPrototypeOfPrototypeHClass(BuiltinTypeId type) const;
@ -1522,7 +1522,7 @@ private:
glueData_.currentContext_ = context;
}
void SetArrayHClassIndexMap(const CMap<ElementsKind, ConstantIndex> &map)
void SetArrayHClassIndexMap(const CMap<ElementsKind, std::pair<ConstantIndex, ConstantIndex>> &map)
{
arrayHClassIndexMap_ = map;
}
@ -1607,7 +1607,8 @@ private:
bool isMainThread_ {false};
bool fullMarkRequest_ {false};
CMap<ElementsKind, ConstantIndex> arrayHClassIndexMap_;
// { ElementsKind, (hclass, hclassWithProto) }
CMap<ElementsKind, std::pair<ConstantIndex, ConstantIndex>> arrayHClassIndexMap_;
CMap<JSHClass *, GlobalIndex> ctorHclassEntries_;
CVector<EcmaContext *> contexts_;

View File

@ -271,7 +271,9 @@ void PGOProfiler::UpdateTrackElementsKind(JSTaggedValue trackInfoVal, ElementsKi
trackInfo->SetElementsKind(mixKind);
auto thread = vm_->GetJSThread();
auto globalConst = thread->GlobalConstants();
auto constantId = thread->GetArrayHClassIndexMap().at(mixKind);
// Since trackinfo is only used at define point,
// we update cachedHClass with initial array hclass which does not have IsPrototype set.
auto constantId = thread->GetArrayHClassIndexMap().at(mixKind).first;
auto hclass = globalConst->GetGlobalConstantObject(static_cast<size_t>(constantId));
trackInfo->SetCachedHClass(vm_->GetJSThread(), hclass);
UpdateTrackInfo(JSTaggedValue(trackInfo));
@ -1684,7 +1686,8 @@ bool PGOProfiler::AddBuiltinsInfoByNameInInstance(ApEntityId abcId, const CStrin
}
JSHClass *exceptRecvHClass = nullptr;
if (builtinsId == BuiltinTypeId::ARRAY) {
exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind());
bool receiverIsPrototype = receiver->IsPrototype();
exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind(), receiverIsPrototype);
} else if (builtinsId == BuiltinTypeId::STRING) {
exceptRecvHClass = receiver;
} else {
@ -1720,7 +1723,8 @@ bool PGOProfiler::AddBuiltinsInfoByNameInProt(ApEntityId abcId, const CString &r
auto thread = vm_->GetJSThread();
JSHClass *exceptRecvHClass = nullptr;
if (builtinsId == BuiltinTypeId::ARRAY) {
exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind());
bool receiverIsPrototype = receiver->IsPrototype();
exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind(), receiverIsPrototype);
} else if (builtinsId == BuiltinTypeId::STRING) {
exceptRecvHClass = receiver;
} else {

View File

@ -637,22 +637,20 @@ DEF_RUNTIME_STUBS(UpdateHClassForElementsKind)
JSTaggedType elementsKind = GetTArg(argv, argc, 1); // 1: means the first parameter
ASSERT(receiver->IsJSArray());
ElementsKind kind = Elements::FixElementsKind(static_cast<ElementsKind>(elementsKind));
auto arrayIndexMap = thread->GetArrayHClassIndexMap();
if (arrayIndexMap.find(kind) != arrayIndexMap.end()) {
auto index = thread->GetArrayHClassIndexMap().at(kind);
auto globalConst = thread->GlobalConstants();
auto targetHClassValue = globalConst->GetGlobalConstantObject(static_cast<size_t>(index));
auto hclass = JSHClass::Cast(targetHClassValue.GetTaggedObject());
auto array = JSHandle<JSArray>(receiver);
array->SynchronizedSetClass(thread, hclass);
if (!thread->GetEcmaVM()->IsEnableElementsKind()) {
// Update TrackInfo
if (!thread->IsPGOProfilerEnable()) {
return JSTaggedValue::Hole().GetRawData();
}
auto trackInfoVal = array->GetTrackInfo();
thread->GetEcmaVM()->GetPGOProfiler()->UpdateTrackElementsKind(trackInfoVal, kind);
auto array = JSHandle<JSArray>(receiver);
ASSERT(JSHClass::IsInitialArrayHClassWithElementsKind(thread, receiver->GetTaggedObject()->GetClass(),
receiver->GetTaggedObject()->GetClass()->GetElementsKind()));
if (!JSHClass::TransitToElementsKindUncheck(thread, JSHandle<JSObject>(array), kind)) {
return JSTaggedValue::Hole().GetRawData();
}
if (!thread->GetEcmaVM()->IsEnableElementsKind()) {
// Update TrackInfo
if (!thread->IsPGOProfilerEnable()) {
return JSTaggedValue::Hole().GetRawData();
}
auto trackInfoVal = JSHandle<JSArray>(receiver)->GetTrackInfo();
thread->GetEcmaVM()->GetPGOProfiler()->UpdateTrackElementsKind(trackInfoVal, kind);
}
return JSTaggedValue::Hole().GetRawData();
}

View File

@ -88,3 +88,31 @@ function testArrayUsedAsProto() {
}
testArrayUsedAsProto();
function testProto1() {
let a = [1, 2, 3];
let obj = {};
obj.__proto__ = a;
print(a[0]);
print(ArkTools.getElementsKind(a));
}
print(ArkTools.isAOTCompiled(testProto1));
testProto1();
function testProto2() {
let obj = {};
for (let i = 0; i < 4; i++) {
let a = [1, 2, 3];
if (i == 2) {
obj.__proto__ = a;
a[1] = 1.5
}
print(a[0] + 1);
print(ArkTools.getElementsKind(a));
}
}
print(ArkTools.isAOTCompiled(testProto2));
testProto2();

View File

@ -19,4 +19,17 @@ undefined
2
0
success
testArrayUsedAsProto success
testArrayUsedAsProto success
true
1
2
true
2
6
2
6
2
6
2
6

View File

@ -19,4 +19,17 @@ undefined
2
0
success
testArrayUsedAsProto success
testArrayUsedAsProto success
false
1
2
false
2
2
2
2
2
6
2
6