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#ifndef ECMASCRIPT_PGO_PROFILER_LAYOUT_H
17#define ECMASCRIPT_PGO_PROFILER_LAYOUT_H
18
19#include <cstdint>
20#include <string>
21
22#include "ecmascript/ecma_vm.h"
23#include "ecmascript/elements.h"
24#include "ecmascript/js_hclass.h"
25#include "ecmascript/js_object.h"
26#include "ecmascript/log_wrapper.h"
27#include "ecmascript/mem/c_containers.h"
28#include "ecmascript/mem/region.h"
29#include "ecmascript/pgo_profiler/pgo_context.h"
30#include "ecmascript/pgo_profiler/pgo_utils.h"
31#include "ecmascript/pgo_profiler/types/pgo_profile_type.h"
32#include "ecmascript/pgo_profiler/types/pgo_profiler_type.h"
33#include "ecmascript/property_attributes.h"
34
35namespace panda::ecmascript::pgo {
36class PGOHandler {
37public:
38    using TrackTypeField =
39        PropertyAttributes::PropertyMetaDataField::NextField<TrackType, PropertyAttributes::TRACK_TYPE_NUM>;
40    using IsSymbol = TrackTypeField::NextFlag;
41
42    PGOHandler()
43    {
44        SetTrackType(TrackType::NONE);
45        SetPropertyMeta(false);
46    }
47
48    PGOHandler(TrackType type, int meta)
49    {
50        SetTrackType(type);
51        SetPropertyMeta(meta);
52    }
53
54    PGOHandler(TrackType type, int meta, bool isSymbol)
55    {
56        SetTrackType(type);
57        SetPropertyMeta(meta);
58        SetIsSymbol(isSymbol);
59    }
60
61    uint32_t GetValue() const
62    {
63        return value_;
64    }
65
66    bool SetAttribute(const JSThread *thread, PropertyAttributes &attr) const;
67
68    void SetIsSymbol(bool isSymbol)
69    {
70        IsSymbol::Set(isSymbol, &value_);
71    }
72
73    bool GetIsSymbol() const
74    {
75        return IsSymbol::Get(value_);
76    }
77
78    void SetTrackType(TrackType type)
79    {
80        TrackTypeField::Set(type, &value_);
81    }
82
83    TrackType GetTrackType() const
84    {
85        return TrackTypeField::Get(value_);
86    }
87
88    void SetPropertyMeta(int meta)
89    {
90        PropertyAttributes::PropertyMetaDataField::Set(meta, &value_);
91    }
92
93    int GetPropertyMeta() const
94    {
95        return PropertyAttributes::PropertyMetaDataField::Get(value_);
96    }
97
98    bool IsAccessor() const
99    {
100        return PropertyAttributes::IsAccessorField::Get(value_);
101    }
102
103    bool IsWritable() const
104    {
105        return PropertyAttributes::WritableField::Get(value_);
106    }
107
108    bool IsEnumerable() const
109    {
110        return PropertyAttributes::EnumerableField::Get(value_);
111    }
112
113    bool IsConfigurable() const
114    {
115        return PropertyAttributes::ConfigurableField::Get(value_);
116    }
117
118    bool operator!=(const PGOHandler &right) const
119    {
120        return value_ != right.value_;
121    }
122
123    bool operator==(const PGOHandler &right) const
124    {
125        return value_ == right.value_;
126    }
127
128private:
129    uint32_t value_ { 0 };
130};
131
132using PropertyDesc = std::pair<CString, PGOHandler>;
133using LayoutDesc = CVector<PropertyDesc>;
134class PGOHClassTreeDesc;
135
136class HClassLayoutDesc {
137public:
138    explicit HClassLayoutDesc(ProfileType type) : type_(type) {}
139    HClassLayoutDesc(ProfileType type, const CSet<ProfileType> &childs) : type_(type), childs_(childs) {}
140    virtual ~HClassLayoutDesc() = default;
141
142    virtual void Merge(const HClassLayoutDesc *from);
143    virtual void InsertKeyAndDesc(const CString &key, const PGOHandler &handler) = 0;
144    virtual bool UpdateKeyAndDesc(const CString &key, const PGOHandler &handler) = 0;
145
146    ProfileType GetProfileType() const
147    {
148        return type_;
149    }
150
151    void AddChildHClassLayoutDesc(const ProfileType &type)
152    {
153        if (type_.GetRaw() == type.GetRaw()) {
154            return;
155        }
156        childs_.emplace(type);
157    }
158
159    bool FindChild(const ProfileType &type)
160    {
161        return childs_.find(type) != childs_.end();
162    }
163
164    inline size_t GetChildSize() const
165    {
166        return childs_.size();
167    }
168
169    template<typename Callback>
170    void IterateChilds(Callback callback) const
171    {
172        for (const auto &type : childs_) {
173            if (!callback(type)) {
174                break;
175            }
176        }
177    }
178
179protected:
180    void InsertKeyAndDesc(const PGOHandler &handler, PropertyDesc &desc);
181
182    ProfileType type_;
183    CSet<ProfileType> childs_;
184};
185
186class RootHClassLayoutDesc final : public HClassLayoutDesc {
187public:
188    explicit RootHClassLayoutDesc(ProfileType type) : HClassLayoutDesc(type) {}
189    RootHClassLayoutDesc(ProfileType type, JSType objType, uint32_t objSize)
190        : HClassLayoutDesc(type), objType_(objType), objSize_(objSize) {}
191    RootHClassLayoutDesc(const RootHClassLayoutDesc &desc)
192        : HClassLayoutDesc(desc.type_, desc.childs_), objType_(desc.objType_), objSize_(desc.objSize_),
193          layoutDesc_(desc.layoutDesc_) {}
194    RootHClassLayoutDesc& operator=(const RootHClassLayoutDesc &desc)
195    {
196        this->type_ = desc.type_;
197        this->childs_ = desc.childs_;
198        this->objType_ = desc.objType_;
199        this->objSize_ = desc.objSize_;
200        this->layoutDesc_ = desc.layoutDesc_;
201        return *this;
202    }
203
204    void Merge(const HClassLayoutDesc *from) override;
205    void InsertKeyAndDesc(const CString &key, const PGOHandler &handler) override;
206    bool UpdateKeyAndDesc(const CString &key, const PGOHandler &handler) override;
207
208    void SetObjectSize(uint32_t objSize)
209    {
210        objSize_ = objSize;
211    }
212
213    uint32_t GetObjectSize() const
214    {
215        return objSize_;
216    }
217
218    void SetObjectType(JSType objType)
219    {
220        objType_ = objType;
221    }
222
223    JSType GetObjectType() const
224    {
225        return objType_;
226    }
227
228    size_t NumOfProps() const
229    {
230        return layoutDesc_.size();
231    }
232
233    template<typename Callback>
234    void IterateProps(Callback callback) const
235    {
236        for (const auto &iter : layoutDesc_) {
237            callback(iter);
238        }
239    }
240
241private:
242    JSType objType_;
243    uint32_t objSize_ {0};
244    LayoutDesc layoutDesc_;
245};
246
247class ChildHClassLayoutDesc final : public HClassLayoutDesc {
248public:
249    explicit ChildHClassLayoutDesc(ProfileType type) : HClassLayoutDesc(type) {}
250    ChildHClassLayoutDesc(const ChildHClassLayoutDesc &desc)
251        : HClassLayoutDesc(desc.type_, desc.childs_), propertyDesc_(desc.propertyDesc_) {}
252    ChildHClassLayoutDesc& operator=(const ChildHClassLayoutDesc &desc)
253    {
254        this->type_ = desc.type_;
255        this->childs_ = desc.childs_;
256        this->propertyDesc_ = desc.propertyDesc_;
257        return *this;
258    }
259
260    void Merge(const HClassLayoutDesc *from) override;
261    void InsertKeyAndDesc(const CString &key, const PGOHandler &handler) override;
262    bool UpdateKeyAndDesc(const CString &key, const PGOHandler &handler) override;
263
264    PropertyDesc GetPropertyDesc() const
265    {
266        return propertyDesc_;
267    }
268
269private:
270    PropertyDesc propertyDesc_;
271};
272
273class PGOHClassTreeDesc {
274public:
275    PGOHClassTreeDesc() = default;
276    explicit PGOHClassTreeDesc(ProfileType type) : type_(type) {}
277
278    void Clear();
279
280    ProfileType GetProfileType() const
281    {
282        return type_;
283    }
284
285    void SetProtoPt(ProfileType protoPt)
286    {
287        protoPt_ = protoPt;
288    }
289
290    ProfileType GetProtoPt() const
291    {
292        return protoPt_;
293    }
294
295    void Merge(const PGOHClassTreeDesc &from);
296
297    bool operator<(const PGOHClassTreeDesc &right) const
298    {
299        return type_ < right.type_;
300    }
301
302    HClassLayoutDesc *GetHClassLayoutDesc(ProfileType type) const;
303    HClassLayoutDesc *GetOrInsertHClassLayoutDesc(ProfileType type, bool root = false);
304
305    bool DumpForRoot(JSTaggedType root, ProfileType rootType);
306    bool DumpForChild(JSTaggedType child, ProfileType childType);
307    bool UpdateForTransition(JSTaggedType parent, ProfileType parentType, JSTaggedType child, ProfileType childType);
308    bool UpdateLayout(JSTaggedType curHClass, ProfileType curType);
309    bool IsDumped(ProfileType curType) const;
310
311    template<typename Callback>
312    void IterateChilds(Callback callback) const
313    {
314        IterateAll([&callback] (HClassLayoutDesc *desc) {
315            if (desc->GetProfileType().IsRootType()) {
316                return;
317            }
318            callback(reinterpret_cast<ChildHClassLayoutDesc *>(desc));
319        });
320    }
321
322private:
323    template<typename Callback>
324    void IterateAll(Callback callback) const
325    {
326        for (auto iter : transitionLayout_) {
327            callback(iter.second);
328        }
329    }
330
331    ProfileType type_;
332    ProfileType protoPt_;
333    CMap<ProfileType, HClassLayoutDesc *> transitionLayout_;
334};
335
336class PGOLayoutDescInfo {
337public:
338    PGOLayoutDescInfo() = default;
339    PGOLayoutDescInfo(const CString &key, PGOHandler handler) : handler_(handler)
340    {
341        size_t len = key.size();
342        size_ = Size(len);
343        if (len > 0 && memcpy_s(&key_, len, key.c_str(), len) != EOK) {
344            LOG_ECMA(ERROR) << "SetMethodName memcpy_s failed" << key << ", len = " << len;
345            UNREACHABLE();
346        }
347        *(&key_ + len) = '\0';
348    }
349
350    static int32_t Size(size_t len)
351    {
352        return sizeof(PGOLayoutDescInfo) + AlignUp(len, GetAlignmentInBytes(ALIGN_SIZE));
353    }
354
355    int32_t Size() const
356    {
357        return size_;
358    }
359
360    const char *GetKey() const
361    {
362        return &key_;
363    }
364
365    PGOHandler GetHandler() const
366    {
367        return handler_;
368    }
369
370private:
371    int32_t size_ {0};
372    PGOHandler handler_;
373    char key_ {'\0'};
374};
375
376class HClassLayoutDescInner {
377public:
378    virtual ~HClassLayoutDescInner() = default;
379    int32_t Size() const
380    {
381        return size_;
382    }
383
384    ProfileType GetProfileType() const
385    {
386        return type_;
387    }
388
389protected:
390    int32_t size_ { 0 };
391    ProfileType type_;
392};
393
394class RootHClassLayoutDescInner : public HClassLayoutDescInner {
395public:
396    static size_t CaculateSize(const RootHClassLayoutDesc &desc)
397    {
398        size_t size = sizeof(RootHClassLayoutDescInner);
399        size += desc.GetChildSize() * sizeof(ProfileType);
400        desc.IterateProps([&size] (const PropertyDesc &propDesc) {
401            auto key = propDesc.first;
402            size += static_cast<size_t>(PGOLayoutDescInfo::Size(key.size()));
403        });
404        return size;
405    }
406
407    void Merge(const RootHClassLayoutDesc &desc)
408    {
409        size_ = static_cast<int32_t>(RootHClassLayoutDescInner::CaculateSize(desc));
410        type_ = desc.GetProfileType();
411        childCount_ = 0;
412        desc.IterateChilds([this] (const ProfileType &childType) -> bool {
413            auto newChildType = const_cast<ProfileType *>(GetChildType(childCount_++));
414            new (newChildType) ProfileType(childType);
415            return true;
416        });
417
418        auto current = const_cast<PGOLayoutDescInfo *>(GetFirstProperty());
419        desc.IterateProps([this, &current] (const PropertyDesc &propDesc) {
420            auto key = propDesc.first;
421            auto type = propDesc.second;
422            new (current) PGOLayoutDescInfo(key, type);
423            current = const_cast<PGOLayoutDescInfo *>(GetNextProperty(current));
424            propCount_++;
425        });
426    }
427
428    void Convert(PGOContext& context, RootHClassLayoutDesc *desc) const
429    {
430        auto descInfo = GetFirstProperty();
431        for (uint32_t i = 0; i < propCount_; i++) {
432            desc->InsertKeyAndDesc(descInfo->GetKey(), descInfo->GetHandler());
433            descInfo = GetNextProperty(descInfo);
434        }
435        for (uint32_t i = 0; i < childCount_; i++) {
436            auto profileType(*GetChildType(i));
437            desc->AddChildHClassLayoutDesc(profileType.Remap(context));
438        }
439    }
440
441    void SetObjectType(JSType objType)
442    {
443        objType_ = objType;
444    }
445
446    JSType GetObjectType() const
447    {
448        return objType_;
449    }
450
451    void SetObjectSize(uint32_t size)
452    {
453        objSize_ = size;
454    }
455
456    uint32_t GetObjectSize() const
457    {
458        return objSize_;
459    }
460
461private:
462    const ProfileType *GetChildType(uint32_t index) const
463    {
464        return (&childType_) + index;
465    }
466
467    const PGOLayoutDescInfo *GetFirstProperty() const
468    {
469        return reinterpret_cast<const PGOLayoutDescInfo *>(reinterpret_cast<uintptr_t>(&childType_ + childCount_));
470    }
471
472    const PGOLayoutDescInfo *GetNextProperty(const PGOLayoutDescInfo *current) const
473    {
474        return reinterpret_cast<const PGOLayoutDescInfo *>(reinterpret_cast<uintptr_t>(current) + current->Size());
475    }
476
477    JSType objType_;
478    uint32_t objSize_ {0};
479    uint32_t propCount_ {0};
480    uint32_t childCount_ {0};
481    ProfileType childType_;
482};
483
484class ChildHClassLayoutDescInner : public HClassLayoutDescInner {
485public:
486    static size_t CaculateSize(const ChildHClassLayoutDesc &desc)
487    {
488        size_t size = sizeof(ChildHClassLayoutDescInner);
489        size += desc.GetChildSize() * sizeof(ProfileType);
490        auto key = desc.GetPropertyDesc().first;
491        size += static_cast<size_t>(PGOLayoutDescInfo::Size(key.size()));
492        return size;
493    }
494
495    void Merge(const ChildHClassLayoutDesc &desc)
496    {
497        size_ = static_cast<int32_t>(ChildHClassLayoutDescInner::CaculateSize(desc));
498        type_ = desc.GetProfileType();
499        childCount_ = 0;
500        desc.IterateChilds([this] (const ProfileType &childType) -> bool {
501            auto newChildType = const_cast<ProfileType *>(GetChildType(childCount_++));
502            new (newChildType) ProfileType(childType);
503            return true;
504        });
505
506        auto current = const_cast<PGOLayoutDescInfo *>(GetProperty());
507        auto propDesc = desc.GetPropertyDesc();
508        auto key = propDesc.first;
509        auto type = propDesc.second;
510        new (current) PGOLayoutDescInfo(key, type);
511    }
512
513    void Convert(PGOContext& context, ChildHClassLayoutDesc *desc) const
514    {
515        auto descInfo = GetProperty();
516        desc->InsertKeyAndDesc(descInfo->GetKey(), descInfo->GetHandler());
517        for (uint32_t i = 0; i < childCount_; i++) {
518            auto profileType(*GetChildType(i));
519            desc->AddChildHClassLayoutDesc(profileType.Remap(context));
520        }
521    }
522
523private:
524    const ProfileType *GetChildType(uint32_t index) const
525    {
526        return (&childType_) + index;
527    }
528
529    const PGOLayoutDescInfo *GetProperty() const
530    {
531        return reinterpret_cast<const PGOLayoutDescInfo *>(&childType_ + childCount_);
532    }
533
534    uint32_t childCount_ { 0 };
535    ProfileType childType_;
536};
537
538template <typename SampleType>
539class PGOHClassTreeTemplate {
540public:
541    PGOHClassTreeTemplate(size_t size, SampleType type, SampleType protoSt)
542        : size_(size), type_(type), protoSt_(protoSt) {}
543
544    static size_t CaculateSize(const PGOHClassTreeDesc &desc)
545    {
546        auto rootLayout = desc.GetHClassLayoutDesc(desc.GetProfileType());
547        if (rootLayout == nullptr) {
548            return sizeof(PGOHClassTreeTemplate<SampleType>);
549        }
550        size_t size = sizeof(PGOHClassTreeTemplate<SampleType>) - sizeof(RootHClassLayoutDescInner);
551        size += RootHClassLayoutDescInner::CaculateSize(*reinterpret_cast<const RootHClassLayoutDesc *>(rootLayout));
552
553        desc.IterateChilds([&size](ChildHClassLayoutDesc *desc) {
554            size += ChildHClassLayoutDescInner::CaculateSize(*desc);
555        });
556        return size;
557    }
558
559    static std::string GetTypeString(const PGOHClassTreeDesc &desc)
560    {
561        std::string text;
562        text += desc.GetProfileType().GetTypeString();
563        text += DumpUtils::BLOCK_AND_ARRAY_START;
564        auto layoutDesc = desc.GetHClassLayoutDesc(desc.GetProfileType());
565        if (layoutDesc != nullptr) {
566            bool isLayoutFirst = true;
567            ASSERT(layoutDesc->GetProfileType().IsRootType());
568            auto rootLayoutDesc = reinterpret_cast<RootHClassLayoutDesc *>(layoutDesc);
569            rootLayoutDesc->IterateProps([&text, &isLayoutFirst] (const PropertyDesc &propDesc) {
570                if (!isLayoutFirst) {
571                    text += DumpUtils::TYPE_SEPARATOR + DumpUtils::SPACE;
572                } else {
573                    text += DumpUtils::ARRAY_START;
574                }
575                isLayoutFirst = false;
576                text += propDesc.first;
577                text += DumpUtils::BLOCK_START;
578                text += std::to_string(propDesc.second.GetValue());
579            });
580        }
581        text += (DumpUtils::SPACE + DumpUtils::ARRAY_END);
582        return text;
583    }
584
585    void Merge(const PGOHClassTreeDesc &desc)
586    {
587        auto root = const_cast<RootHClassLayoutDescInner *>(GetRoot());
588        auto layoutDesc = desc.GetHClassLayoutDesc(desc.GetProfileType());
589        if (layoutDesc == nullptr) {
590            return;
591        }
592        auto rootLayoutDesc = reinterpret_cast<const RootHClassLayoutDesc *>(layoutDesc);
593        root->Merge(*rootLayoutDesc);
594        root->SetObjectType(rootLayoutDesc->GetObjectType());
595        root->SetObjectSize(rootLayoutDesc->GetObjectSize());
596
597        childCount_ = 0;
598        auto last = reinterpret_cast<HClassLayoutDescInner *>(root);
599        desc.IterateChilds([this, &last](ChildHClassLayoutDesc *desc) {
600            auto current = const_cast<ChildHClassLayoutDescInner *>(GetNext(last));
601            new (current) ChildHClassLayoutDescInner();
602            current->Merge(*desc);
603            last = current;
604            childCount_++;
605        });
606    }
607
608    PGOHClassTreeDesc Convert(PGOContext& context)
609    {
610        PGOHClassTreeDesc desc(ProfileType(context, GetType().GetProfileType()));
611        desc.SetProtoPt(ProfileType(context, GetProtoSt().GetProfileType()));
612        auto root = GetRoot();
613        if (root->GetProfileType().IsNone()) {
614            return desc;
615        }
616        auto layoutDesc = desc.GetOrInsertHClassLayoutDesc(root->GetProfileType().Remap(context), true);
617        auto rootLayoutDesc = reinterpret_cast<RootHClassLayoutDesc *>(layoutDesc);
618        rootLayoutDesc->SetObjectType(root->GetObjectType());
619        rootLayoutDesc->SetObjectSize(root->GetObjectSize());
620        root->Convert(context, rootLayoutDesc);
621
622        auto last = reinterpret_cast<const HClassLayoutDescInner *>(root);
623        for (int32_t i = 0; i < childCount_; i++) {
624            auto current = GetNext(last);
625            auto childLayoutDesc = desc.GetOrInsertHClassLayoutDesc(current->GetProfileType().Remap(context), false);
626            current->Convert(context, reinterpret_cast<ChildHClassLayoutDesc *>(childLayoutDesc));
627            last = current;
628        }
629        return desc;
630    }
631
632    int32_t Size() const
633    {
634        return size_;
635    }
636
637    SampleType GetType() const
638    {
639        return type_;
640    }
641
642    SampleType GetProtoSt() const
643    {
644        return protoSt_;
645    }
646
647private:
648    const RootHClassLayoutDescInner *GetRoot() const
649    {
650        return &rootHClassLayout_;
651    }
652
653    const ChildHClassLayoutDescInner *GetNext(const HClassLayoutDescInner *current) const
654    {
655        return reinterpret_cast<const ChildHClassLayoutDescInner *>(
656            reinterpret_cast<uintptr_t>(current) + current->Size());
657    }
658
659    uintptr_t GetEnd() const
660    {
661        return reinterpret_cast<uintptr_t>(this) + Size();
662    }
663
664    int32_t size_;
665    SampleType type_;
666    SampleType protoSt_;
667    int32_t childCount_ { 0 };
668    RootHClassLayoutDescInner rootHClassLayout_;
669};
670
671using PGOHClassTreeDescInner = PGOHClassTreeTemplate<PGOSampleType>;
672using PGOHClassTreeDescInnerRef = PGOHClassTreeTemplate<PGOSampleTypeRef>;
673} // namespace panda::ecmascript::pgo
674#endif // ECMASCRIPT_PGO_PROFILER_LAYOUT_H
675