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); [[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> obj = GetCallArg(info, 0); JSHandle<JSTaggedValue> obj = GetCallArg(info, 0);
JSHandle<JSObject> receiver(thread, obj.GetTaggedValue()); JSHClass *hclass = obj->GetTaggedObject()->GetClass();
ElementsKind kind = receiver->GetClass()->GetElementsKind(); ElementsKind kind = hclass->GetElementsKind();
return JSTaggedValue(static_cast<uint32_t>(kind)); return JSTaggedValue(static_cast<uint32_t>(kind));
} }

View File

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

View File

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

View File

@ -60,7 +60,7 @@ public:
virtual std::shared_ptr<pgo::PGOProfiler> GetPGOProfiler() const; virtual std::shared_ptr<pgo::PGOProfiler> GetPGOProfiler() const;
// thread // 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 const BuiltinHClassEntries &GetBuiltinHClassEntries() const = 0;
virtual JSHClass *GetBuiltinPrototypeHClass(BuiltinTypeId type) const = 0; virtual JSHClass *GetBuiltinPrototypeHClass(BuiltinTypeId type) const = 0;

View File

@ -40,7 +40,7 @@ JSRuntimeOptions &JitCompilationEnv::GetJSOptions()
return hostThread_->GetEcmaVM()->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(); return hostThread_->GetArrayHClassIndexMap();
} }

View File

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

View File

@ -206,7 +206,8 @@ GateRef NTypeHCRLowering::NewJSArrayLiteral(GateRef glue, GateRef gate, GateRef
ElementsKind kind = acc_.GetArrayMetaDataAccessor(gate).GetElementsKind(); ElementsKind kind = acc_.GetArrayMetaDataAccessor(gate).GetElementsKind();
GateRef hclass = Circuit::NullGate(); GateRef hclass = Circuit::NullGate();
if (!Elements::IsGeneric(kind)) { 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); hclass = builder_.GetGlobalConstantValue(hclassIndex);
} else { } else {
GateRef globalEnv = builder_.GetGlobalEnv(); GateRef globalEnv = builder_.GetGlobalEnv();

View File

@ -109,7 +109,9 @@ bool ArrayParser::RecordTypeInfo(const PGODefineOpType &defType, const PGOTypeLo
ptManager_->RecordLocationToElementsKind(loc, kind); ptManager_->RecordLocationToElementsKind(loc, kind);
auto traceId = rootType.GetId(); 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)); ptManager_->RecordConstantIndex(traceId, static_cast<uint32_t>(hclassIdx));
return true; return true;
} }

View File

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

View File

@ -303,7 +303,7 @@ private:
void LowerSetGeneratorState(GateRef gate); void LowerSetGeneratorState(GateRef gate);
GateRef GetValueFromTaggedArray(GateRef arrayGate, GateRef indexOffset); GateRef GetValueFromTaggedArray(GateRef arrayGate, GateRef indexOffset);
GateRef GetTaggedArrayFromValueIn(Environment *env, GateRef gate, size_t length); 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); void AddProfiling(GateRef gate, bool skipGenerator = true);
GateRef FastStrictEqual(GateRef left, GateRef right); GateRef FastStrictEqual(GateRef left, GateRef right);
void LowerWideLdPatchVar(GateRef gate); void LowerWideLdPatchVar(GateRef gate);

View File

@ -599,9 +599,12 @@ void TypedHCRLowering::BuiltinInstanceHClassCheck(Environment *env, GateRef gate
auto arrayHClassIndexMap = compilationEnv_->GetArrayHClassIndexMap(); auto arrayHClassIndexMap = compilationEnv_->GetArrayHClassIndexMap();
auto iter = arrayHClassIndexMap.find(kind); auto iter = arrayHClassIndexMap.find(kind);
ASSERT(iter != arrayHClassIndexMap.end()); 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); 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 { } else {
GateRef receiverHClass = builder_.LoadHClassByConstOffset(receiver); GateRef receiverHClass = builder_.LoadHClassByConstOffset(receiver);
GateRef elementsKind = builder_.GetElementsKindByHClass(receiverHClass); GateRef elementsKind = builder_.GetElementsKindByHClass(receiverHClass);

View File

@ -20,21 +20,14 @@
#include "ecmascript/tagged_array-inl.h" #include "ecmascript/tagged_array-inl.h"
namespace panda::ecmascript { namespace panda::ecmascript {
CMap<ElementsKind, ConstantIndex> Elements::InitializeHClassMap() CMap<ElementsKind, std::pair<ConstantIndex, ConstantIndex>> Elements::InitializeHClassMap()
{ {
CMap<ElementsKind, ConstantIndex> result; CMap<ElementsKind, std::pair<ConstantIndex, ConstantIndex>> result;
result.emplace(ElementsKind::NONE, ConstantIndex::ELEMENT_NONE_HCLASS_INDEX); #define INIT_ARRAY_HCLASS_INDEX_MAPS(name) \
result.emplace(ElementsKind::HOLE, ConstantIndex::ELEMENT_HOLE_HCLASS_INDEX); result.emplace(ElementsKind::name, std::make_pair(ConstantIndex::ELEMENT_##name##_HCLASS_INDEX, \
result.emplace(ElementsKind::INT, ConstantIndex::ELEMENT_INT_HCLASS_INDEX); ConstantIndex::ELEMENT_##name##_PROTO_HCLASS_INDEX));
result.emplace(ElementsKind::NUMBER, ConstantIndex::ELEMENT_NUMBER_HCLASS_INDEX); ELEMENTS_KIND_INIT_HCLASS_LIST(INIT_ARRAY_HCLASS_INDEX_MAPS)
result.emplace(ElementsKind::STRING, ConstantIndex::ELEMENT_STRING_HCLASS_INDEX); #undef INIT_ARRAY_HCLASS_INDEX_MAPS
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);
return result; return result;
} }

View File

@ -21,6 +21,21 @@
#include "ecmascript/mem/c_containers.h" #include "ecmascript/mem/c_containers.h"
namespace panda::ecmascript { 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 { enum class ElementsKind : uint8_t {
NONE = 0x00UL, NONE = 0x00UL,
HOLE = 0x01UL, HOLE = 0x01UL,
@ -40,7 +55,7 @@ enum class ElementsKind : uint8_t {
class PUBLIC_API Elements { class PUBLIC_API Elements {
public: public:
static CMap<ElementsKind, ConstantIndex> InitializeHClassMap(); static CMap<ElementsKind, std::pair<ConstantIndex, ConstantIndex>> InitializeHClassMap();
static std::string GetString(ElementsKind kind); static std::string GetString(ElementsKind kind);
static bool IsInt(ElementsKind kind); static bool IsInt(ElementsKind kind);

View File

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

View File

@ -134,6 +134,18 @@ class ObjectFactory;
V(JSTaggedValue, IteratorResultClass, ITERATOR_RESULT_CLASS, ecma_roots_class) \ V(JSTaggedValue, IteratorResultClass, ITERATOR_RESULT_CLASS, ecma_roots_class) \
V(JSTaggedValue, ClassPrototypeClass, CLASS_PROTOTYPE_HCLASS_INDEX, 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, 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, ElementNoneClass, ELEMENT_NONE_HCLASS_INDEX, ecma_roots_class) \
V(JSTaggedValue, ElementHoleClass, ELEMENT_HOLE_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) \ 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; JSHClass *exceptRecvHClass = nullptr;
if (builtinsId == BuiltinTypeId::ARRAY) { if (builtinsId == BuiltinTypeId::ARRAY) {
exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind()); bool receiverIsPrototype = receiver->IsPrototype();
exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind(), receiverIsPrototype);
} else if (builtinsId == BuiltinTypeId::STRING) { } else if (builtinsId == BuiltinTypeId::STRING) {
exceptRecvHClass = receiver; exceptRecvHClass = receiver;
} else { } else {
@ -1021,7 +1022,8 @@ bool JITProfiler::AddBuiltinsInfoByNameInProt(ApEntityId abcId, int32_t bcOffset
auto thread = vm_->GetJSThread(); auto thread = vm_->GetJSThread();
JSHClass *exceptRecvHClass = nullptr; JSHClass *exceptRecvHClass = nullptr;
if (builtinsId == BuiltinTypeId::ARRAY) { if (builtinsId == BuiltinTypeId::ARRAY) {
exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind()); bool receiverIsPrototype = receiver->IsPrototype();
exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind(), receiverIsPrototype);
} else if (builtinsId == BuiltinTypeId::STRING) { } else if (builtinsId == BuiltinTypeId::STRING) {
exceptRecvHClass = receiver; exceptRecvHClass = receiver;
} else { } else {

View File

@ -30,6 +30,22 @@ inline JSHClass *JSHClass::Cast(const TaggedObject *object)
return static_cast<JSHClass *>(const_cast<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, void JSHClass::AddTransitions(const JSThread *thread, const JSHandle<JSHClass> &parent, const JSHandle<JSHClass> &child,
const JSHandle<JSTaggedValue> &key, PropertyAttributes attributes) const JSHandle<JSTaggedValue> &key, PropertyAttributes attributes)
{ {

View File

@ -267,6 +267,15 @@ JSHandle<JSHClass> JSHClass::Clone(const JSThread *thread, const JSHandle<JSHCla
return newJsHClass; 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 // use for transition to dictionary
JSHandle<JSHClass> JSHClass::CloneWithoutInlinedProperties(const JSThread *thread, const JSHandle<JSHClass> &jshclass) 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 // 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, // 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. // 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(); JSTaggedValue layout = newProtoClass->GetLayout();
// If the type of object is JSObject, the layout info value is initialized to the default value, // 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. // 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) { if (!isChangeProto) {
thread->GetEcmaVM()->GetPGOProfiler()->UpdateRootProfileTypeSafe(*hclass, *newProtoClass); thread->GetEcmaVM()->GetPGOProfiler()->UpdateRootProfileTypeSafe(*hclass, *newProtoClass);
} }
if (proto->IsJSObject()) {
TryRestoreElementsKind(thread, newProtoClass, JSHandle<JSObject>::Cast(proto));
}
} else { } else {
// There is no sharing in AOT hclass. Therefore, it is not necessary or possible to clone here. // There is no sharing in AOT hclass. Therefore, it is not necessary or possible to clone here.
hclass->SetIsPrototype(true); hclass->SetIsPrototype(true);
@ -659,17 +674,21 @@ void JSHClass::TransitionForRepChange(const JSThread *thread, const JSHandle<JSO
// 4. Maybe Transition And Maintain subtypeing check // 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(); const auto &arrayHClassIndexMap = thread->GetArrayHClassIndexMap();
auto newKindIter = arrayHClassIndexMap.find(kind); auto newKindIter = arrayHClassIndexMap.find(targetKind);
if (newKindIter != arrayHClassIndexMap.end()) { if (newKindIter != arrayHClassIndexMap.end()) {
auto index = static_cast<size_t>(newKindIter->second); auto indexPair = newKindIter->second;
auto hclassVal = thread->GlobalConstants()->GetGlobalConstantObject(index); 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()); 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, 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(); ElementsKind current = obj->GetJSHClass()->GetElementsKind();
// currently we only support initial array hclass // 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(); const auto &arrayHClassIndexMap = thread->GetArrayHClassIndexMap();
auto newKindIter = arrayHClassIndexMap.find(newKind); auto newKindIter = arrayHClassIndexMap.find(newKind);
bool objHclassIsPrototype = objHclass->IsPrototype();
if (newKindIter != arrayHClassIndexMap.end()) { 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); auto hclassVal = thread->GlobalConstants()->GetGlobalConstantObject(index);
JSHClass *hclass = JSHClass::Cast(hclassVal.GetTaggedObject()); JSHClass *hclass = JSHClass::Cast(hclassVal.GetTaggedObject());
obj->SynchronizedSetClass(thread, hclass); obj->SynchronizedSetClass(thread, hclass);
return true; return true;
} }
LOG_ECMA(FATAL) << "Unknown newKind: " << static_cast<int32_t>(newKind);
} }
return false; return false;
} }
@ -703,16 +727,9 @@ void JSHClass::TransitToElementsKind(const JSThread *thread, const JSHandle<JSAr
if (newKind == current) { if (newKind == current) {
return; return;
} }
// Currently, we only support fast array elementsKind
ASSERT(array->GetClass() == GetInitialArrayHClassWithElementsKind(thread, current)); ASSERT(IsInitialArrayHClassWithElementsKind(thread, array->GetJSHClass(), current));
const auto &arrayHClassIndexMap = thread->GetArrayHClassIndexMap(); TransitToElementsKindUncheck(thread, JSHandle<JSObject>(array), newKind);
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);
}
} }
bool JSHClass::TransitToElementsKind(const JSThread *thread, const JSHandle<JSObject> &object, 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; return false;
} }
// Currently, we only support fast array elementsKind // Currently, we only support fast array elementsKind
ASSERT(object->GetClass() == GetInitialArrayHClassWithElementsKind(thread, current)); ASSERT(IsInitialArrayHClassWithElementsKind(thread, object->GetJSHClass(), current));
const auto &arrayHClassIndexMap = thread->GetArrayHClassIndexMap(); if (!TransitToElementsKindUncheck(thread, object, newKind)) {
auto newKindIter = arrayHClassIndexMap.find(newKind); return false;
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);
if (!thread->GetEcmaVM()->IsEnableElementsKind()) { if (!thread->GetEcmaVM()->IsEnableElementsKind()) {
// Update TrackInfo // Update TrackInfo
if (!thread->IsPGOProfilerEnable()) { if (!thread->IsPGOProfilerEnable()) {
return true;
}
auto trackInfoVal = JSHandle<JSArray>(object)->GetTrackInfo();
thread->GetEcmaVM()->GetPGOProfiler()->UpdateTrackElementsKind(trackInfoVal, newKind);
return true; return true;
} }
auto trackInfoVal = JSHandle<JSArray>(object)->GetTrackInfo();
thread->GetEcmaVM()->GetPGOProfiler()->UpdateTrackElementsKind(trackInfoVal, newKind);
return true; return true;
} }
return false; return true;
} }
TransitionResult JSHClass::ConvertOrTransitionWithRep(const JSThread *thread, TransitionResult JSHClass::ConvertOrTransitionWithRep(const JSThread *thread,

View File

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

View File

@ -619,11 +619,11 @@ JSHClass *JSThread::GetBuiltinExtraHClass(BuiltinTypeId type) const
return glueData_.builtinHClassEntries_.entries[index].extraHClass; 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); auto iter = GetArrayHClassIndexMap().find(kind);
ASSERT(iter != GetArrayHClassIndexMap().end()); 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 exceptArrayHClass = GlobalConstants()->GetGlobalConstantObject(index);
auto exceptRecvHClass = JSHClass::Cast(exceptArrayHClass.GetTaggedObject()); auto exceptRecvHClass = JSHClass::Cast(exceptArrayHClass.GetTaggedObject());
ASSERT(exceptRecvHClass->IsJSArray()); ASSERT(exceptRecvHClass->IsJSArray());

View File

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

View File

@ -271,7 +271,9 @@ void PGOProfiler::UpdateTrackElementsKind(JSTaggedValue trackInfoVal, ElementsKi
trackInfo->SetElementsKind(mixKind); trackInfo->SetElementsKind(mixKind);
auto thread = vm_->GetJSThread(); auto thread = vm_->GetJSThread();
auto globalConst = thread->GlobalConstants(); 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)); auto hclass = globalConst->GetGlobalConstantObject(static_cast<size_t>(constantId));
trackInfo->SetCachedHClass(vm_->GetJSThread(), hclass); trackInfo->SetCachedHClass(vm_->GetJSThread(), hclass);
UpdateTrackInfo(JSTaggedValue(trackInfo)); UpdateTrackInfo(JSTaggedValue(trackInfo));
@ -1684,7 +1686,8 @@ bool PGOProfiler::AddBuiltinsInfoByNameInInstance(ApEntityId abcId, const CStrin
} }
JSHClass *exceptRecvHClass = nullptr; JSHClass *exceptRecvHClass = nullptr;
if (builtinsId == BuiltinTypeId::ARRAY) { if (builtinsId == BuiltinTypeId::ARRAY) {
exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind()); bool receiverIsPrototype = receiver->IsPrototype();
exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind(), receiverIsPrototype);
} else if (builtinsId == BuiltinTypeId::STRING) { } else if (builtinsId == BuiltinTypeId::STRING) {
exceptRecvHClass = receiver; exceptRecvHClass = receiver;
} else { } else {
@ -1720,7 +1723,8 @@ bool PGOProfiler::AddBuiltinsInfoByNameInProt(ApEntityId abcId, const CString &r
auto thread = vm_->GetJSThread(); auto thread = vm_->GetJSThread();
JSHClass *exceptRecvHClass = nullptr; JSHClass *exceptRecvHClass = nullptr;
if (builtinsId == BuiltinTypeId::ARRAY) { if (builtinsId == BuiltinTypeId::ARRAY) {
exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind()); bool receiverIsPrototype = receiver->IsPrototype();
exceptRecvHClass = thread->GetArrayInstanceHClass(receiver->GetElementsKind(), receiverIsPrototype);
} else if (builtinsId == BuiltinTypeId::STRING) { } else if (builtinsId == BuiltinTypeId::STRING) {
exceptRecvHClass = receiver; exceptRecvHClass = receiver;
} else { } else {

View File

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

View File

@ -88,3 +88,31 @@ function testArrayUsedAsProto() {
} }
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 2
0 0
success 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 2
0 0
success success
testArrayUsedAsProto success testArrayUsedAsProto success
false
1
2
false
2
2
2
2
2
6
2
6