1/*
2 * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecmascript/pgo_profiler/pgo_profiler_layout.h"
17#include "ecmascript/js_thread.h"
18
19namespace panda::ecmascript::pgo {
20
21bool PGOHandler::SetAttribute(const JSThread *thread, PropertyAttributes &attr) const
22{
23    bool ret = false;
24    if (thread->GetEcmaVM()->GetJSOptions().IsEnableOptTrackField()) {
25        TrackType trackType = GetTrackType();
26        attr.SetTrackType(trackType);
27        switch (trackType) {
28            case TrackType::DOUBLE:
29            case TrackType::NUMBER:
30                attr.SetRepresentation(Representation::DOUBLE);
31                ret = true;
32                break;
33            case TrackType::INT:
34                attr.SetRepresentation(Representation::INT);
35                ret = true;
36                break;
37            case TrackType::TAGGED:
38            default:
39                attr.SetRepresentation(Representation::TAGGED);
40                break;
41        }
42    } else {
43        attr.SetRepresentation(Representation::TAGGED);
44    }
45    attr.SetWritable(IsWritable());
46    attr.SetEnumerable(IsEnumerable());
47    attr.SetConfigurable(IsConfigurable());
48    attr.SetIsAccessor(IsAccessor());
49    return ret;
50}
51
52void PGOHClassTreeDesc::Clear()
53{
54    IterateAll([] (HClassLayoutDesc *desc) {
55        delete desc;
56    });
57    transitionLayout_.clear();
58}
59
60void PGOHClassTreeDesc::Merge(const PGOHClassTreeDesc &from)
61{
62    ASSERT(from.GetProfileType() == GetProfileType());
63    from.IterateAll([this] (HClassLayoutDesc *fromDesc) {
64        auto curLayoutDesc = GetHClassLayoutDesc(fromDesc->GetProfileType());
65        if (curLayoutDesc == nullptr) {
66            if (fromDesc->GetProfileType().IsRootType()) {
67                RootHClassLayoutDesc *rootFromTreeDesc = reinterpret_cast<RootHClassLayoutDesc *>(fromDesc);
68                curLayoutDesc = new RootHClassLayoutDesc(*rootFromTreeDesc);
69            } else {
70                ChildHClassLayoutDesc *childFromTreeDesc = reinterpret_cast<ChildHClassLayoutDesc *>(fromDesc);
71                curLayoutDesc = new ChildHClassLayoutDesc(*childFromTreeDesc);
72            }
73            transitionLayout_.emplace(fromDesc->GetProfileType(), curLayoutDesc);
74        } else {
75            curLayoutDesc->Merge(fromDesc);
76        }
77    });
78}
79
80HClassLayoutDesc *PGOHClassTreeDesc::GetHClassLayoutDesc(ProfileType type) const
81{
82    auto iter = transitionLayout_.find(type);
83    if (iter != transitionLayout_.end()) {
84        return iter->second;
85    }
86    return nullptr;
87}
88
89HClassLayoutDesc *PGOHClassTreeDesc::GetOrInsertHClassLayoutDesc(ProfileType type, bool root)
90{
91    auto iter = transitionLayout_.find(type);
92    if (iter != transitionLayout_.end()) {
93        return iter->second;
94    } else {
95        HClassLayoutDesc *layout;
96        if (root) {
97            layout = new RootHClassLayoutDesc(type);
98        } else {
99            layout = new ChildHClassLayoutDesc(type);
100        }
101        transitionLayout_.emplace(type, layout);
102        return layout;
103    }
104}
105
106bool PGOHClassTreeDesc::DumpForRoot(JSTaggedType root, ProfileType rootType)
107{
108    ASSERT(rootType.IsRootType());
109    HClassLayoutDesc *rootLayout;
110    auto iter = transitionLayout_.find(rootType);
111    auto rootHClass = JSHClass::Cast(JSTaggedValue(root).GetTaggedObject());
112    if (iter != transitionLayout_.end()) {
113        rootLayout = iter->second;
114        return JSHClass::UpdateRootLayoutDescByPGO(rootHClass, rootLayout);
115    } else {
116        rootLayout = new RootHClassLayoutDesc(rootType, rootHClass->GetObjectType(),
117                                              rootHClass->GetObjectSizeExcludeInlinedProps());
118        transitionLayout_.emplace(rootType, rootLayout);
119    }
120
121    return JSHClass::DumpRootHClassByPGO(rootHClass, rootLayout);
122}
123
124bool PGOHClassTreeDesc::DumpForChild(JSTaggedType child, ProfileType childType)
125{
126    ASSERT(!childType.IsRootType());
127    auto childHClass = JSHClass::Cast(JSTaggedValue(child).GetTaggedObject());
128
129    HClassLayoutDesc *childLayout;
130    auto iter = transitionLayout_.find(childType);
131    if (iter != transitionLayout_.end()) {
132        childLayout = iter->second;
133        return JSHClass::UpdateChildLayoutDescByPGO(childHClass, childLayout);
134    } else {
135        childLayout = new ChildHClassLayoutDesc(childType);
136        transitionLayout_.emplace(childType, childLayout);
137        return JSHClass::DumpChildHClassByPGO(childHClass, childLayout);
138    }
139}
140
141bool PGOHClassTreeDesc::UpdateLayout(JSTaggedType curHClass, ProfileType curType)
142{
143    if (curType.IsRootType()) {
144        return DumpForRoot(curHClass, curType);
145    } else {
146        return DumpForChild(curHClass, curType);
147    }
148}
149
150bool PGOHClassTreeDesc::IsDumped(ProfileType curType) const
151{
152    return transitionLayout_.find(curType) != transitionLayout_.end();
153}
154
155bool PGOHClassTreeDesc::UpdateForTransition(
156    JSTaggedType parent, ProfileType parentType, JSTaggedType child, ProfileType childType)
157{
158    if (parentType.IsRootType()) {
159        if (!DumpForRoot(parent, parentType)) {
160            return false;
161        }
162    }
163    if (transitionLayout_.find(parentType) == transitionLayout_.end()) {
164        return false;
165    }
166
167    bool ret = DumpForChild(child, childType);
168    auto parentLayoutDesc = transitionLayout_.find(parentType)->second;
169    auto childLayoutDesc = transitionLayout_.find(childType)->second;
170    parentLayoutDesc->AddChildHClassLayoutDesc(childLayoutDesc->GetProfileType());
171    return ret;
172}
173
174void HClassLayoutDesc::Merge(const HClassLayoutDesc *from)
175{
176    from->IterateChilds([this] (const ProfileType &type) -> bool {
177        AddChildHClassLayoutDesc(type);
178        return true;
179    });
180}
181
182void HClassLayoutDesc::InsertKeyAndDesc(const PGOHandler &handler, PropertyDesc &desc)
183{
184    PGOHandler oldHandler = desc.second;
185    if (oldHandler == handler) {
186        return;
187    }
188    auto oldTrackType = oldHandler.GetTrackType();
189    auto newTrackType = handler.GetTrackType();
190    if (oldTrackType == newTrackType) {
191        desc.second.SetPropertyMeta(handler.GetPropertyMeta());
192        return;
193    }
194
195    switch (oldTrackType) {
196        case TrackType::TAGGED:
197            desc.second.SetPropertyMeta(handler.GetPropertyMeta());
198            break;
199        case TrackType::NONE:
200        case TrackType::INT:
201        case TrackType::DOUBLE:
202            if (newTrackType != TrackType::TAGGED) {
203                newTrackType = static_cast<TrackType>(static_cast<uint8_t>(newTrackType) |
204                    static_cast<uint8_t>(oldTrackType));
205            }
206            desc.second = PGOHandler(newTrackType, handler.GetPropertyMeta());
207            break;
208        default:
209            break;
210    }
211}
212
213void RootHClassLayoutDesc::Merge(const HClassLayoutDesc *from)
214{
215    ASSERT(from->GetProfileType() == GetProfileType());
216    ASSERT(from->GetProfileType().IsRootType());
217    auto fromDesc = reinterpret_cast<const RootHClassLayoutDesc *>(from);
218    fromDesc->IterateProps([this] (const PropertyDesc &desc) {
219        InsertKeyAndDesc(desc.first, desc.second);
220    });
221    HClassLayoutDesc::Merge(from);
222}
223
224void RootHClassLayoutDesc::InsertKeyAndDesc(const CString &key, const PGOHandler &handler)
225{
226    if (!UpdateKeyAndDesc(key, handler)) {
227        layoutDesc_.emplace_back(key, handler);
228    }
229}
230
231bool RootHClassLayoutDesc::UpdateKeyAndDesc(const CString &key, const PGOHandler &handler)
232{
233    for (auto &iter : layoutDesc_) {
234        if (iter.first == key) {
235            HClassLayoutDesc::InsertKeyAndDesc(handler, iter);
236            return true;
237        }
238    }
239    return false;
240}
241
242void ChildHClassLayoutDesc::Merge(const HClassLayoutDesc *from)
243{
244    ASSERT(from->GetProfileType() == GetProfileType());
245    ASSERT(!from->GetProfileType().IsRootType());
246    auto fromDesc = reinterpret_cast<const ChildHClassLayoutDesc *>(from);
247    auto fromPropDesc = fromDesc->GetPropertyDesc();
248    InsertKeyAndDesc(fromPropDesc.first, fromPropDesc.second);
249    HClassLayoutDesc::Merge(from);
250}
251
252void ChildHClassLayoutDesc::InsertKeyAndDesc(const CString &key, const PGOHandler &handler)
253{
254    if (!UpdateKeyAndDesc(key, handler)) {
255        propertyDesc_ = PropertyDesc(key, handler);
256    }
257}
258
259bool ChildHClassLayoutDesc::UpdateKeyAndDesc(const CString &key, const PGOHandler &handler)
260{
261    if (propertyDesc_.first == key) {
262        HClassLayoutDesc::InsertKeyAndDesc(handler, propertyDesc_);
263        return true;
264    }
265    return false;
266}
267} // namespace panda::ecmascript::pgo
268