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 
28 namespace panda::ecmascript::kungfu {
29 using 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
33 struct MethodPcInfo {
34     std::vector<const uint8_t*> pcOffsets {};
35     uint32_t methodsSize {0};
36 };
37 
38 class MethodInfo {
39 public:
MethodInfo(uint32_t methodInfoIndex, uint32_t methodPcInfoIndex, std::shared_ptr<CString> recordNamePtr)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 
GetMethodPcInfoIndex() const48     inline uint32_t GetMethodPcInfoIndex() const
49     {
50         return methodPcInfoIndex_;
51     }
52 
SetMethodPcInfoIndex(uint32_t methodPcInfoIndex)53     inline void SetMethodPcInfoIndex(uint32_t methodPcInfoIndex)
54     {
55         methodPcInfoIndex_ = methodPcInfoIndex;
56     }
57 
GetMethodInfoIndex() const58     inline uint32_t GetMethodInfoIndex() const
59     {
60         return methodInfoIndex_;
61     }
62 
SetMethodInfoIndex(uint32_t methodInfoIndex)63     inline void SetMethodInfoIndex(uint32_t methodInfoIndex)
64     {
65         methodInfoIndex_ = methodInfoIndex;
66     }
67 
SetRecordNamePtr(const std::shared_ptr<CString> recordNamePtr)68     inline void SetRecordNamePtr(const std::shared_ptr<CString> recordNamePtr)
69     {
70         recordNamePtr_ = recordNamePtr;
71     }
72 
GetRecordNamePtr() const73     inline const std::shared_ptr<CString> GetRecordNamePtr() const
74     {
75         return recordNamePtr_;
76     }
77 
GetRecordName() const78     inline const CString &GetRecordName() const
79     {
80         return *recordNamePtr_;
81     }
82 
IsPGO() const83     bool IsPGO() const
84     {
85         return CompileStateBit::PGOBit::Decode(compileState_.value_);
86     }
87 
SetIsPGO(bool pgoMark)88     void SetIsPGO(bool pgoMark)
89     {
90         CompileStateBit::PGOBit::Set<uint8_t>(pgoMark, &compileState_.value_);
91     }
92 
IsCompiled() const93     bool IsCompiled() const
94     {
95         return CompileStateBit::CompiledBit::Decode(compileState_.value_);
96     }
97 
SetIsCompiled(bool isCompiled)98     void SetIsCompiled(bool isCompiled)
99     {
100         CompileStateBit::CompiledBit::Set<uint8_t>(isCompiled, &compileState_.value_);
101     }
102 
103 private:
104     class CompileStateBit {
105     public:
CompileStateBit(uint8_t value)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 
128 struct FastCallInfo {
129     bool canFastCall_ {false};
130     bool isNoGC_ {false};
131 };
132 
133 class BCInfo {
134 public:
BCInfo(size_t maxAotMethodSize)135     explicit BCInfo(size_t maxAotMethodSize)
136         : maxMethodSize_(maxAotMethodSize)
137     {
138     }
139 
GetMainMethodIndexes()140     std::vector<uint32_t>& GetMainMethodIndexes()
141     {
142         return mainMethodIndexes_;
143     }
144 
GetRecordNamePtrs()145     std::vector<std::shared_ptr<CString>>& GetRecordNamePtrs()
146     {
147         return recordNamePtrs_;
148     }
149 
GetRecordNameWithIndex(uint32_t index) const150     const CString &GetRecordNameWithIndex(uint32_t index) const
151     {
152         return *recordNamePtrs_[index];
153     }
154 
GetMethodPcInfos()155     std::vector<MethodPcInfo>& GetMethodPcInfos()
156     {
157         return methodPcInfos_;
158     }
159 
GetMethodList()160     std::unordered_map<uint32_t, MethodInfo>& GetMethodList()
161     {
162         return methodList_;
163     }
164 
GetMaxMethodSize() const165     size_t GetMaxMethodSize() const
166     {
167         return maxMethodSize_;
168     }
169 
IsSkippedMethod(uint32_t methodOffset) const170     bool IsSkippedMethod(uint32_t methodOffset) const
171     {
172         if (skippedMethods_.find(methodOffset) == skippedMethods_.end()) {
173             return false;
174         }
175         return true;
176     }
177 
GetSkippedMethodSet() const178     const std::set<uint32_t>& GetSkippedMethodSet() const
179     {
180         return skippedMethods_;
181     }
182 
AddSkippedMethod(uint32_t methodOffset)183     void AddSkippedMethod(uint32_t methodOffset)
184     {
185         skippedMethods_.insert(methodOffset);
186     }
187 
EraseSkippedMethod(uint32_t methodOffset)188     void EraseSkippedMethod(uint32_t methodOffset)
189     {
190         if (skippedMethods_.find(methodOffset) != skippedMethods_.end()) {
191             skippedMethods_.erase(methodOffset);
192         }
193     }
194 
FindMethodOffsetToRecordName(uint32_t methodOffset)195     bool FindMethodOffsetToRecordName(uint32_t methodOffset)
196     {
197         return methodOffsetToRecordName_.find(methodOffset) != methodOffsetToRecordName_.end();
198     }
199 
AddMethodOffsetToRecordName(uint32_t methodOffset, CString recordName)200     void AddMethodOffsetToRecordName(uint32_t methodOffset, CString recordName)
201     {
202         methodOffsetToRecordName_.emplace(methodOffset, recordName);
203     }
204 
GetSkippedMethodSize() const205     size_t GetSkippedMethodSize() const
206     {
207         return skippedMethods_.size();
208     }
209 
GetDefineMethod(const uint32_t classLiteralOffset) const210     uint32_t GetDefineMethod(const uint32_t classLiteralOffset) const
211     {
212         return classTypeLOffsetToDefMethod_.at(classLiteralOffset);
213     }
214 
HasClassDefMethod(const uint32_t classLiteralOffset) const215     bool HasClassDefMethod(const uint32_t classLiteralOffset) const
216     {
217         return classTypeLOffsetToDefMethod_.find(classLiteralOffset) != classTypeLOffsetToDefMethod_.end();
218     }
219 
SetClassTypeOffsetAndDefMethod(uint32_t classLiteralOffset, uint32_t methodOffset)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 
IterateFunctionTypeIDAndMethodOffset(uint32_t functionTypeId)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 
SetFunctionTypeIDAndMethodOffset(uint32_t functionTypeId, uint32_t methodOffset)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 
IterateMethodOffsetToFastCallInfo(uint32_t methodOffset, bool *isValid)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 
SetMethodOffsetToFastCallInfo(uint32_t methodOffset, bool canFastCall, bool noGC)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 
ModifyMethodOffsetToCanFastCall(uint32_t methodOffset, bool canFastCall)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     }
273 private:
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 
286 class BytecodeInfoCollector {
287 public:
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 
GetByteCodes()298     Bytecodes* GetByteCodes()
299     {
300         return &bytecodes_;
301     }
302 
GetBytecodeInfo()303     BCInfo& GetBytecodeInfo()
304     {
305         return bytecodeInfo_;
306     }
307 
GetBytecodeInfoPtr()308     BCInfo* GetBytecodeInfoPtr()
309     {
310         return &bytecodeInfo_;
311     }
312 
GetPGOBCInfo() const313     const PGOBCInfo* GetPGOBCInfo() const
314     {
315         return &pgoBCInfo_;
316     }
317 
StoreDataToGlobalData(SnapshotGlobalData &snapshotData)318     void StoreDataToGlobalData(SnapshotGlobalData &snapshotData)
319     {
320         snapshotCPData_->StoreDataToGlobalData(snapshotData, GetSkippedMethodSet());
321     }
322 
GetSkippedMethodSet() const323     const std::set<uint32_t>& GetSkippedMethodSet() const
324     {
325         return bytecodeInfo_.GetSkippedMethodSet();
326     }
327 
IsSkippedMethod(uint32_t methodOffset) const328     bool IsSkippedMethod(uint32_t methodOffset) const
329     {
330         return bytecodeInfo_.IsSkippedMethod(methodOffset);
331     }
332 
FilterMethod(const MethodLiteral *methodLiteral, const MethodPcInfo &methodPCInfo) const333     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 
GetJSPandaFile() const344     const JSPandaFile *GetJSPandaFile() const
345     {
346         return jsPandaFile_;
347     }
348 
GetCompilationEnv() const349     CompilationEnv *GetCompilationEnv() const
350     {
351         return compilationEnv_;
352     }
353 
354     template <class Callback>
IterateAllMethods(const Callback &cb)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);
365 private:
GetNewMethodInfoID()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