* Copyright (c) 2023-2024 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.
*/
#include "ecmascript/pgo_profiler/pgo_profiler_layout.h"
#include "ecmascript/js_thread.h"
namespace panda::ecmascript::pgo {
bool PGOHandler::SetAttribute(const JSThread *thread, PropertyAttributes &attr) const
{
bool ret = false;
if (thread->GetEcmaVM()->GetJSOptions().IsEnableOptTrackField()) {
TrackType trackType = GetTrackType();
attr.SetTrackType(trackType);
switch (trackType) {
case TrackType::DOUBLE:
case TrackType::NUMBER:
attr.SetRepresentation(Representation::DOUBLE);
ret = true;
break;
case TrackType::INT:
attr.SetRepresentation(Representation::INT);
ret = true;
break;
case TrackType::TAGGED:
default:
attr.SetRepresentation(Representation::TAGGED);
break;
}
} else {
attr.SetRepresentation(Representation::TAGGED);
}
attr.SetWritable(IsWritable());
attr.SetEnumerable(IsEnumerable());
attr.SetConfigurable(IsConfigurable());
attr.SetIsAccessor(IsAccessor());
return ret;
}
void PGOHClassTreeDesc::Clear()
{
IterateAll([] (HClassLayoutDesc *desc) {
delete desc;
});
transitionLayout_.clear();
}
void PGOHClassTreeDesc::Merge(const PGOHClassTreeDesc &from)
{
ASSERT(from.GetProfileType() == GetProfileType());
from.IterateAll([this] (HClassLayoutDesc *fromDesc) {
auto curLayoutDesc = GetHClassLayoutDesc(fromDesc->GetProfileType());
if (curLayoutDesc == nullptr) {
if (fromDesc->GetProfileType().IsRootType()) {
RootHClassLayoutDesc *rootFromTreeDesc = reinterpret_cast<RootHClassLayoutDesc *>(fromDesc);
curLayoutDesc = new RootHClassLayoutDesc(*rootFromTreeDesc);
} else {
ChildHClassLayoutDesc *childFromTreeDesc = reinterpret_cast<ChildHClassLayoutDesc *>(fromDesc);
curLayoutDesc = new ChildHClassLayoutDesc(*childFromTreeDesc);
}
transitionLayout_.emplace(fromDesc->GetProfileType(), curLayoutDesc);
} else {
curLayoutDesc->Merge(fromDesc);
}
});
}
HClassLayoutDesc *PGOHClassTreeDesc::GetHClassLayoutDesc(ProfileType type) const
{
auto iter = transitionLayout_.find(type);
if (iter != transitionLayout_.end()) {
return iter->second;
}
return nullptr;
}
HClassLayoutDesc *PGOHClassTreeDesc::GetOrInsertHClassLayoutDesc(ProfileType type, bool root)
{
auto iter = transitionLayout_.find(type);
if (iter != transitionLayout_.end()) {
return iter->second;
} else {
HClassLayoutDesc *layout;
if (root) {
layout = new RootHClassLayoutDesc(type);
} else {
layout = new ChildHClassLayoutDesc(type);
}
transitionLayout_.emplace(type, layout);
return layout;
}
}
bool PGOHClassTreeDesc::DumpForRoot(const JSThread *thread, JSTaggedType root, ProfileType rootType)
{
ASSERT(rootType.IsRootType());
HClassLayoutDesc *rootLayout;
auto iter = transitionLayout_.find(rootType);
auto rootHClass = JSHClass::Cast(JSTaggedValue(root).GetTaggedObject());
if (iter != transitionLayout_.end()) {
rootLayout = iter->second;
return JSHClass::UpdateRootLayoutDescByPGO(thread, rootHClass, rootLayout);
} else {
rootLayout = new RootHClassLayoutDesc(rootType, rootHClass->GetObjectType(),
rootHClass->GetObjectSizeExcludeInlinedProps());
transitionLayout_.emplace(rootType, rootLayout);
}
return JSHClass::DumpRootHClassByPGO(thread, rootHClass, rootLayout);
}
bool PGOHClassTreeDesc::DumpForChild(const JSThread *thread, JSTaggedType child, ProfileType childType)
{
ASSERT(!childType.IsRootType());
auto childHClass = JSHClass::Cast(JSTaggedValue(child).GetTaggedObject());
HClassLayoutDesc *childLayout;
auto iter = transitionLayout_.find(childType);
if (iter != transitionLayout_.end()) {
childLayout = iter->second;
return JSHClass::UpdateChildLayoutDescByPGO(thread, childHClass, childLayout);
} else {
childLayout = new ChildHClassLayoutDesc(childType);
transitionLayout_.emplace(childType, childLayout);
return JSHClass::DumpChildHClassByPGO(thread, childHClass, childLayout);
}
}
bool PGOHClassTreeDesc::UpdateLayout(const JSThread *thread, JSTaggedType curHClass, ProfileType curType)
{
if (curType.IsRootType()) {
return DumpForRoot(thread, curHClass, curType);
} else {
return DumpForChild(thread, curHClass, curType);
}
}
bool PGOHClassTreeDesc::IsDumped(ProfileType curType) const
{
return transitionLayout_.find(curType) != transitionLayout_.end();
}
bool PGOHClassTreeDesc::UpdateForTransition(const JSThread *thread,
JSTaggedType parent, ProfileType parentType, JSTaggedType child, ProfileType childType)
{
if (parentType.IsRootType()) {
if (!DumpForRoot(thread, parent, parentType)) {
return false;
}
}
if (transitionLayout_.find(parentType) == transitionLayout_.end()) {
return false;
}
bool ret = DumpForChild(thread, child, childType);
auto parentLayoutDesc = transitionLayout_.find(parentType)->second;
auto childLayoutDesc = transitionLayout_.find(childType)->second;
parentLayoutDesc->AddChildHClassLayoutDesc(childLayoutDesc->GetProfileType());
return ret;
}
void HClassLayoutDesc::Merge(const HClassLayoutDesc *from)
{
from->IterateChilds([this] (const ProfileType &type) -> bool {
AddChildHClassLayoutDesc(type);
return true;
});
}
void HClassLayoutDesc::InsertKeyAndDesc(const PGOHandler &handler, PropertyDesc &desc)
{
PGOHandler oldHandler = desc.second;
if (oldHandler == handler) {
return;
}
auto oldTrackType = oldHandler.GetTrackType();
auto newTrackType = handler.GetTrackType();
if (oldTrackType == newTrackType) {
desc.second.SetPropertyMeta(handler.GetPropertyMeta());
return;
}
switch (oldTrackType) {
case TrackType::TAGGED:
desc.second.SetPropertyMeta(handler.GetPropertyMeta());
break;
case TrackType::NONE:
case TrackType::INT:
case TrackType::DOUBLE:
if (newTrackType != TrackType::TAGGED) {
newTrackType = static_cast<TrackType>(static_cast<uint8_t>(newTrackType) |
static_cast<uint8_t>(oldTrackType));
}
desc.second = PGOHandler(newTrackType, handler.GetPropertyMeta());
break;
default:
break;
}
}
void RootHClassLayoutDesc::Merge(const HClassLayoutDesc *from)
{
ASSERT(from->GetProfileType() == GetProfileType());
ASSERT(from->GetProfileType().IsRootType());
auto fromDesc = reinterpret_cast<const RootHClassLayoutDesc *>(from);
fromDesc->IterateProps([this] (const PropertyDesc &desc) {
InsertKeyAndDesc(desc.first, desc.second);
});
HClassLayoutDesc::Merge(from);
}
void RootHClassLayoutDesc::InsertKeyAndDesc(const CString &key, const PGOHandler &handler)
{
if (!UpdateKeyAndDesc(key, handler)) {
layoutDesc_.emplace_back(key, handler);
}
}
bool RootHClassLayoutDesc::UpdateKeyAndDesc(const CString &key, const PGOHandler &handler)
{
for (auto &iter : layoutDesc_) {
if (iter.first == key) {
HClassLayoutDesc::InsertKeyAndDesc(handler, iter);
return true;
}
}
return false;
}
void ChildHClassLayoutDesc::Merge(const HClassLayoutDesc *from)
{
ASSERT(from->GetProfileType() == GetProfileType());
ASSERT(!from->GetProfileType().IsRootType());
auto fromDesc = reinterpret_cast<const ChildHClassLayoutDesc *>(from);
auto fromPropDesc = fromDesc->GetPropertyDesc();
InsertKeyAndDesc(fromPropDesc.first, fromPropDesc.second);
HClassLayoutDesc::Merge(from);
}
void ChildHClassLayoutDesc::InsertKeyAndDesc(const CString &key, const PGOHandler &handler)
{
if (!UpdateKeyAndDesc(key, handler)) {
propertyDesc_ = PropertyDesc(key, handler);
}
}
bool ChildHClassLayoutDesc::UpdateKeyAndDesc(const CString &key, const PGOHandler &handler)
{
if (propertyDesc_.first == key) {
HClassLayoutDesc::InsertKeyAndDesc(handler, propertyDesc_);
return true;
}
return false;
}
}