1/*
2 * Copyright (c) 2022-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_COMPILER_BYTECODE_INFO_COLLECTOR_H
17#define ECMASCRIPT_COMPILER_BYTECODE_INFO_COLLECTOR_H
18
19#include "ecmascript/compiler/aot_snapshot/snapshot_constantpool_data.h"
20#include "ecmascript/compiler/bytecodes.h"
21#include "ecmascript/compiler/pgo_bc_info.h"
22#include "ecmascript/jspandafile/js_pandafile.h"
23#include "ecmascript/jspandafile/method_literal.h"
24#include "ecmascript/pgo_profiler/pgo_profiler_decoder.h"
25#include "ecmascript/compiler/compilation_env.h"
26#include "libpandafile/bytecode_instruction-inl.h"
27
28namespace panda::ecmascript::kungfu {
29using PGOProfilerDecoder = pgo::PGOProfilerDecoder;
30
31// each method in the abc file corresponds to one MethodInfo and
32// methods with the same instructions share one common MethodPcInfo
33struct MethodPcInfo {
34    std::vector<const uint8_t*> pcOffsets {};
35    uint32_t methodsSize {0};
36};
37
38class MethodInfo {
39public:
40    MethodInfo(uint32_t methodInfoIndex, uint32_t methodPcInfoIndex, std::shared_ptr<CString> recordNamePtr)
41        : methodInfoIndex_(methodInfoIndex), methodPcInfoIndex_(methodPcInfoIndex), recordNamePtr_(recordNamePtr) {}
42
43    ~MethodInfo() = default;
44
45    static constexpr uint32_t DEFAULT_OUTMETHOD_OFFSET = 0;
46    static constexpr uint32_t DEFAULT_ROOT = std::numeric_limits<uint32_t>::max();
47
48    inline uint32_t GetMethodPcInfoIndex() const
49    {
50        return methodPcInfoIndex_;
51    }
52
53    inline void SetMethodPcInfoIndex(uint32_t methodPcInfoIndex)
54    {
55        methodPcInfoIndex_ = methodPcInfoIndex;
56    }
57
58    inline uint32_t GetMethodInfoIndex() const
59    {
60        return methodInfoIndex_;
61    }
62
63    inline void SetMethodInfoIndex(uint32_t methodInfoIndex)
64    {
65        methodInfoIndex_ = methodInfoIndex;
66    }
67
68    inline void SetRecordNamePtr(const std::shared_ptr<CString> recordNamePtr)
69    {
70        recordNamePtr_ = recordNamePtr;
71    }
72
73    inline const std::shared_ptr<CString> GetRecordNamePtr() const
74    {
75        return recordNamePtr_;
76    }
77
78    inline const CString &GetRecordName() const
79    {
80        return *recordNamePtr_;
81    }
82
83    bool IsPGO() const
84    {
85        return CompileStateBit::PGOBit::Decode(compileState_.value_);
86    }
87
88    void SetIsPGO(bool pgoMark)
89    {
90        CompileStateBit::PGOBit::Set<uint8_t>(pgoMark, &compileState_.value_);
91    }
92
93    bool IsCompiled() const
94    {
95        return CompileStateBit::CompiledBit::Decode(compileState_.value_);
96    }
97
98    void SetIsCompiled(bool isCompiled)
99    {
100        CompileStateBit::CompiledBit::Set<uint8_t>(isCompiled, &compileState_.value_);
101    }
102
103private:
104    class CompileStateBit {
105    public:
106        explicit CompileStateBit(uint8_t value) : value_(value) {}
107        CompileStateBit() = default;
108        ~CompileStateBit() = default;
109        DEFAULT_COPY_SEMANTIC(CompileStateBit);
110        DEFAULT_MOVE_SEMANTIC(CompileStateBit);
111
112        static constexpr size_t BOOL_FLAG_BIT_LENGTH = 1;
113        using PGOBit = panda::BitField<bool, 0, BOOL_FLAG_BIT_LENGTH>;
114        using CompiledBit = PGOBit::NextField<bool, BOOL_FLAG_BIT_LENGTH>;
115
116    private:
117        uint8_t value_ {0};
118        friend class MethodInfo;
119    };
120    // used to record the index of the current MethodInfo to speed up the lookup of lexEnv
121    uint32_t methodInfoIndex_ { 0 };
122    // used to obtain MethodPcInfo from the vector methodPcInfos of struct BCInfo
123    uint32_t methodPcInfoIndex_ { 0 };
124    std::shared_ptr<CString> recordNamePtr_ {nullptr};
125    CompileStateBit compileState_ { 0 };
126};
127
128struct FastCallInfo {
129    bool canFastCall_ {false};
130    bool isNoGC_ {false};
131};
132
133class BCInfo {
134public:
135    explicit BCInfo(size_t maxAotMethodSize)
136        : maxMethodSize_(maxAotMethodSize)
137    {
138    }
139
140    std::vector<uint32_t>& GetMainMethodIndexes()
141    {
142        return mainMethodIndexes_;
143    }
144
145    std::vector<std::shared_ptr<CString>>& GetRecordNamePtrs()
146    {
147        return recordNamePtrs_;
148    }
149
150    const CString &GetRecordNameWithIndex(uint32_t index) const
151    {
152        return *recordNamePtrs_[index];
153    }
154
155    std::vector<MethodPcInfo>& GetMethodPcInfos()
156    {
157        return methodPcInfos_;
158    }
159
160    std::unordered_map<uint32_t, MethodInfo>& GetMethodList()
161    {
162        return methodList_;
163    }
164
165    size_t GetMaxMethodSize() const
166    {
167        return maxMethodSize_;
168    }
169
170    bool IsSkippedMethod(uint32_t methodOffset) const
171    {
172        if (skippedMethods_.find(methodOffset) == skippedMethods_.end()) {
173            return false;
174        }
175        return true;
176    }
177
178    const std::set<uint32_t>& GetSkippedMethodSet() const
179    {
180        return skippedMethods_;
181    }
182
183    void AddSkippedMethod(uint32_t methodOffset)
184    {
185        skippedMethods_.insert(methodOffset);
186    }
187
188    void EraseSkippedMethod(uint32_t methodOffset)
189    {
190        if (skippedMethods_.find(methodOffset) != skippedMethods_.end()) {
191            skippedMethods_.erase(methodOffset);
192        }
193    }
194
195    bool FindMethodOffsetToRecordName(uint32_t methodOffset)
196    {
197        return methodOffsetToRecordName_.find(methodOffset) != methodOffsetToRecordName_.end();
198    }
199
200    void AddMethodOffsetToRecordName(uint32_t methodOffset, CString recordName)
201    {
202        methodOffsetToRecordName_.emplace(methodOffset, recordName);
203    }
204
205    size_t GetSkippedMethodSize() const
206    {
207        return skippedMethods_.size();
208    }
209
210    uint32_t GetDefineMethod(const uint32_t classLiteralOffset) const
211    {
212        return classTypeLOffsetToDefMethod_.at(classLiteralOffset);
213    }
214
215    bool HasClassDefMethod(const uint32_t classLiteralOffset) const
216    {
217        return classTypeLOffsetToDefMethod_.find(classLiteralOffset) != classTypeLOffsetToDefMethod_.end();
218    }
219
220    void SetClassTypeOffsetAndDefMethod(uint32_t classLiteralOffset, uint32_t methodOffset)
221    {
222        if (classTypeLOffsetToDefMethod_.find(classLiteralOffset) == classTypeLOffsetToDefMethod_.end()) {
223            classTypeLOffsetToDefMethod_.emplace(classLiteralOffset, methodOffset);
224        }
225    }
226
227    uint32_t IterateFunctionTypeIDAndMethodOffset(uint32_t functionTypeId)
228    {
229        auto iter = functionTypeIdToMethodOffset_.find(functionTypeId);
230        if (iter != functionTypeIdToMethodOffset_.end()) {
231            return iter->second;
232        }
233        return 0;
234    }
235
236    void SetFunctionTypeIDAndMethodOffset(uint32_t functionTypeId, uint32_t methodOffset)
237    {
238        if (functionTypeIdToMethodOffset_.find(functionTypeId) == functionTypeIdToMethodOffset_.end()) {
239            functionTypeIdToMethodOffset_.emplace(functionTypeId, methodOffset);
240        }
241    }
242
243    FastCallInfo IterateMethodOffsetToFastCallInfo(uint32_t methodOffset, bool *isValid)
244    {
245        auto iter = methodOffsetToFastCallInfos_.find(methodOffset);
246        if (iter != methodOffsetToFastCallInfos_.end()) {
247            *isValid = true;
248            return iter->second;
249        }
250        *isValid = false;
251        return FastCallInfo();
252    }
253
254    void SetMethodOffsetToFastCallInfo(uint32_t methodOffset, bool canFastCall, bool noGC)
255    {
256        if (methodOffsetToFastCallInfos_.find(methodOffset) == methodOffsetToFastCallInfos_.end()) {
257            methodOffsetToFastCallInfos_.emplace(methodOffset, FastCallInfo { canFastCall, noGC });
258        }
259    }
260
261    void ModifyMethodOffsetToCanFastCall(uint32_t methodOffset, bool canFastCall)
262    {
263        auto iter = methodOffsetToFastCallInfos_.find(methodOffset);
264        bool isNoGC = false;
265        if (iter != methodOffsetToFastCallInfos_.end()) {
266            isNoGC = iter->second.isNoGC_;
267        }
268        methodOffsetToFastCallInfos_.erase(methodOffset);
269        if (methodOffsetToFastCallInfos_.find(methodOffset) == methodOffsetToFastCallInfos_.end()) {
270            methodOffsetToFastCallInfos_.emplace(methodOffset, FastCallInfo { canFastCall, isNoGC });
271        }
272    }
273private:
274    std::vector<uint32_t> mainMethodIndexes_ {};
275    std::vector<std::shared_ptr<CString>> recordNamePtrs_ {};
276    std::vector<MethodPcInfo> methodPcInfos_ {};
277    std::unordered_map<uint32_t, MethodInfo> methodList_ {};
278    std::unordered_map<uint32_t, CString> methodOffsetToRecordName_ {};
279    std::set<uint32_t> skippedMethods_ {};
280    size_t maxMethodSize_;
281    std::unordered_map<uint32_t, uint32_t> classTypeLOffsetToDefMethod_ {};
282    std::unordered_map<uint32_t, uint32_t> functionTypeIdToMethodOffset_ {};
283    std::unordered_map<uint32_t, FastCallInfo> methodOffsetToFastCallInfos_ {};
284};
285
286class BytecodeInfoCollector {
287public:
288    BytecodeInfoCollector(CompilationEnv *env, JSPandaFile *jsPandaFile, PGOProfilerDecoder &pfDecoder,
289                          size_t maxAotMethodSize);
290
291    BytecodeInfoCollector(CompilationEnv *env, JSPandaFile *jsPandaFile,
292                          PGOProfilerDecoder &pfDecoder);
293
294    ~BytecodeInfoCollector() = default;
295    NO_COPY_SEMANTIC(BytecodeInfoCollector);
296    NO_MOVE_SEMANTIC(BytecodeInfoCollector);
297
298    Bytecodes* GetByteCodes()
299    {
300        return &bytecodes_;
301    }
302
303    BCInfo& GetBytecodeInfo()
304    {
305        return bytecodeInfo_;
306    }
307
308    BCInfo* GetBytecodeInfoPtr()
309    {
310        return &bytecodeInfo_;
311    }
312
313    const PGOBCInfo* GetPGOBCInfo() const
314    {
315        return &pgoBCInfo_;
316    }
317
318    void StoreDataToGlobalData(SnapshotGlobalData &snapshotData)
319    {
320        snapshotCPData_->StoreDataToGlobalData(snapshotData, GetSkippedMethodSet());
321    }
322
323    const std::set<uint32_t>& GetSkippedMethodSet() const
324    {
325        return bytecodeInfo_.GetSkippedMethodSet();
326    }
327
328    bool IsSkippedMethod(uint32_t methodOffset) const
329    {
330        return bytecodeInfo_.IsSkippedMethod(methodOffset);
331    }
332
333    bool FilterMethod(const MethodLiteral *methodLiteral, const MethodPcInfo &methodPCInfo) const
334    {
335        auto recordName = MethodLiteral::GetRecordName(jsPandaFile_, methodLiteral->GetMethodId());
336        bool methodSizeIsIllegal = methodPCInfo.methodsSize > bytecodeInfo_.GetMaxMethodSize();
337        bool methodFilteredByPGO = !pfDecoder_.Match(jsPandaFile_, recordName, methodLiteral->GetMethodId());
338        if (methodSizeIsIllegal || methodFilteredByPGO) {
339            return true;
340        }
341        return false;
342    }
343
344    const JSPandaFile *GetJSPandaFile() const
345    {
346        return jsPandaFile_;
347    }
348
349    CompilationEnv *GetCompilationEnv() const
350    {
351        return compilationEnv_;
352    }
353
354    template <class Callback>
355    void IterateAllMethods(const Callback &cb)
356    {
357        auto &methodList = bytecodeInfo_.GetMethodList();
358        for (const auto &method : methodList) {
359            uint32_t methodOffset = method.first;
360            cb(methodOffset);
361        }
362    }
363
364    void ProcessMethod(MethodLiteral *methodLiteral);
365private:
366    inline size_t GetNewMethodInfoID()
367    {
368        return methodInfoCounts_++;
369    }
370
371    void ProcessClasses();
372    void ProcessCurrMethod();
373    void CollectMethodPcsFromBC(const uint32_t insSz, const uint8_t *insArr, MethodLiteral *method,
374                                uint32_t methodOffset, const std::shared_ptr<CString> recordNamePtr);
375    static bool IsVRegUsed(const BytecodeInstruction &inst, const BytecodeMetaData &metaData, uint32_t idx);
376    void SetMethodPcInfoIndex(uint32_t methodOffset, const std::pair<size_t, uint32_t> &processedMethodInfo,
377                              const std::shared_ptr<CString> recordNamePtr);
378    void CollectMethods(const MethodLiteral *method, const std::shared_ptr<CString> recordNamePtr);
379    void CollectMethods(uint32_t methodId, const std::shared_ptr<CString> recordNamePtr);
380    void CollectInnerMethodsFromLiteral(uint64_t index, const std::shared_ptr<CString> recordNamePtr);
381    void CollectInnerMethodsFromNewLiteral(panda_file::File::EntityId literalId,
382                                           const std::shared_ptr<CString> recordNamePtr);
383    void CollectMethodInfoFromBC(const BytecodeInstruction &bcIns, const MethodLiteral *method, int32_t bcIndex,
384                                 const std::shared_ptr<CString> recordNamePtr, bool *canFastCall,
385                                 bool *canTypedCall);
386    void IterateLiteral(const MethodLiteral *method, std::vector<uint32_t> &classOffsetVector);
387    void StoreClassTypeOffset(const uint32_t typeOffset, std::vector<uint32_t> &classOffsetVector);
388    void CollectClassLiteralInfo(const MethodLiteral *method, const std::vector<std::string> &classNameVec);
389
390    CompilationEnv *compilationEnv_ {nullptr};
391    JSPandaFile *jsPandaFile_ {nullptr};
392    BCInfo bytecodeInfo_;
393    PGOProfilerDecoder &pfDecoder_;
394    PGOBCInfo pgoBCInfo_ {};
395    std::unique_ptr<SnapshotConstantPoolData> snapshotCPData_;
396    size_t methodInfoCounts_ {0};
397    std::set<int32_t> classDefBCIndexes_ {};
398    Bytecodes bytecodes_;
399    std::set<uint32_t> processedMethod_;
400};
401}  // namespace panda::ecmascript::kungfu
402#endif  // ECMASCRIPT_COMPILER_BYTECODE_INFO_COLLECTOR_H
403