/* * Copyright (c) 2022-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. */ #ifndef ECMASCRIPT_COMPILER_BYTECODE_INFO_COLLECTOR_H #define ECMASCRIPT_COMPILER_BYTECODE_INFO_COLLECTOR_H #include "ecmascript/compiler/aot_snapshot/snapshot_constantpool_data.h" #include "ecmascript/compiler/bytecodes.h" #include "ecmascript/compiler/pgo_bc_info.h" #include "ecmascript/jspandafile/js_pandafile.h" #include "ecmascript/jspandafile/method_literal.h" #include "ecmascript/pgo_profiler/pgo_profiler_decoder.h" #include "ecmascript/compiler/compilation_env.h" #include "libpandafile/bytecode_instruction-inl.h" namespace panda::ecmascript::kungfu { using PGOProfilerDecoder = pgo::PGOProfilerDecoder; // each method in the abc file corresponds to one MethodInfo and // methods with the same instructions share one common MethodPcInfo struct MethodPcInfo { std::vector pcOffsets {}; uint32_t methodsSize {0}; }; class MethodInfo { public: MethodInfo(uint32_t methodInfoIndex, uint32_t methodPcInfoIndex, std::shared_ptr recordNamePtr) : methodInfoIndex_(methodInfoIndex), methodPcInfoIndex_(methodPcInfoIndex), recordNamePtr_(recordNamePtr) {} ~MethodInfo() = default; static constexpr uint32_t DEFAULT_OUTMETHOD_OFFSET = 0; static constexpr uint32_t DEFAULT_ROOT = std::numeric_limits::max(); inline uint32_t GetMethodPcInfoIndex() const { return methodPcInfoIndex_; } inline void SetMethodPcInfoIndex(uint32_t methodPcInfoIndex) { methodPcInfoIndex_ = methodPcInfoIndex; } inline uint32_t GetMethodInfoIndex() const { return methodInfoIndex_; } inline void SetMethodInfoIndex(uint32_t methodInfoIndex) { methodInfoIndex_ = methodInfoIndex; } inline void SetRecordNamePtr(const std::shared_ptr recordNamePtr) { recordNamePtr_ = recordNamePtr; } inline const std::shared_ptr GetRecordNamePtr() const { return recordNamePtr_; } inline const CString &GetRecordName() const { return *recordNamePtr_; } bool IsPGO() const { return CompileStateBit::PGOBit::Decode(compileState_.value_); } void SetIsPGO(bool pgoMark) { CompileStateBit::PGOBit::Set(pgoMark, &compileState_.value_); } bool IsCompiled() const { return CompileStateBit::CompiledBit::Decode(compileState_.value_); } void SetIsCompiled(bool isCompiled) { CompileStateBit::CompiledBit::Set(isCompiled, &compileState_.value_); } private: class CompileStateBit { public: explicit CompileStateBit(uint8_t value) : value_(value) {} CompileStateBit() = default; ~CompileStateBit() = default; DEFAULT_COPY_SEMANTIC(CompileStateBit); DEFAULT_MOVE_SEMANTIC(CompileStateBit); static constexpr size_t BOOL_FLAG_BIT_LENGTH = 1; using PGOBit = panda::BitField; using CompiledBit = PGOBit::NextField; private: uint8_t value_ {0}; friend class MethodInfo; }; // used to record the index of the current MethodInfo to speed up the lookup of lexEnv uint32_t methodInfoIndex_ { 0 }; // used to obtain MethodPcInfo from the vector methodPcInfos of struct BCInfo uint32_t methodPcInfoIndex_ { 0 }; std::shared_ptr recordNamePtr_ {nullptr}; CompileStateBit compileState_ { 0 }; }; struct FastCallInfo { bool canFastCall_ {false}; bool isNoGC_ {false}; }; class BCInfo { public: explicit BCInfo(size_t maxAotMethodSize) : maxMethodSize_(maxAotMethodSize) { } std::vector& GetMainMethodIndexes() { return mainMethodIndexes_; } std::vector>& GetRecordNamePtrs() { return recordNamePtrs_; } const CString &GetRecordNameWithIndex(uint32_t index) const { return *recordNamePtrs_[index]; } std::vector& GetMethodPcInfos() { return methodPcInfos_; } std::unordered_map& GetMethodList() { return methodList_; } size_t GetMaxMethodSize() const { return maxMethodSize_; } bool IsSkippedMethod(uint32_t methodOffset) const { if (skippedMethods_.find(methodOffset) == skippedMethods_.end()) { return false; } return true; } const std::set& GetSkippedMethodSet() const { return skippedMethods_; } void AddSkippedMethod(uint32_t methodOffset) { skippedMethods_.insert(methodOffset); } void EraseSkippedMethod(uint32_t methodOffset) { if (skippedMethods_.find(methodOffset) != skippedMethods_.end()) { skippedMethods_.erase(methodOffset); } } bool FindMethodOffsetToRecordName(uint32_t methodOffset) { return methodOffsetToRecordName_.find(methodOffset) != methodOffsetToRecordName_.end(); } void AddMethodOffsetToRecordName(uint32_t methodOffset, CString recordName) { methodOffsetToRecordName_.emplace(methodOffset, recordName); } size_t GetSkippedMethodSize() const { return skippedMethods_.size(); } uint32_t GetDefineMethod(const uint32_t classLiteralOffset) const { return classTypeLOffsetToDefMethod_.at(classLiteralOffset); } bool HasClassDefMethod(const uint32_t classLiteralOffset) const { return classTypeLOffsetToDefMethod_.find(classLiteralOffset) != classTypeLOffsetToDefMethod_.end(); } void SetClassTypeOffsetAndDefMethod(uint32_t classLiteralOffset, uint32_t methodOffset) { if (classTypeLOffsetToDefMethod_.find(classLiteralOffset) == classTypeLOffsetToDefMethod_.end()) { classTypeLOffsetToDefMethod_.emplace(classLiteralOffset, methodOffset); } } uint32_t IterateFunctionTypeIDAndMethodOffset(uint32_t functionTypeId) { auto iter = functionTypeIdToMethodOffset_.find(functionTypeId); if (iter != functionTypeIdToMethodOffset_.end()) { return iter->second; } return 0; } void SetFunctionTypeIDAndMethodOffset(uint32_t functionTypeId, uint32_t methodOffset) { if (functionTypeIdToMethodOffset_.find(functionTypeId) == functionTypeIdToMethodOffset_.end()) { functionTypeIdToMethodOffset_.emplace(functionTypeId, methodOffset); } } FastCallInfo IterateMethodOffsetToFastCallInfo(uint32_t methodOffset, bool *isValid) { auto iter = methodOffsetToFastCallInfos_.find(methodOffset); if (iter != methodOffsetToFastCallInfos_.end()) { *isValid = true; return iter->second; } *isValid = false; return FastCallInfo(); } void SetMethodOffsetToFastCallInfo(uint32_t methodOffset, bool canFastCall, bool noGC) { if (methodOffsetToFastCallInfos_.find(methodOffset) == methodOffsetToFastCallInfos_.end()) { methodOffsetToFastCallInfos_.emplace(methodOffset, FastCallInfo { canFastCall, noGC }); } } void ModifyMethodOffsetToCanFastCall(uint32_t methodOffset, bool canFastCall) { auto iter = methodOffsetToFastCallInfos_.find(methodOffset); bool isNoGC = false; if (iter != methodOffsetToFastCallInfos_.end()) { isNoGC = iter->second.isNoGC_; } methodOffsetToFastCallInfos_.erase(methodOffset); if (methodOffsetToFastCallInfos_.find(methodOffset) == methodOffsetToFastCallInfos_.end()) { methodOffsetToFastCallInfos_.emplace(methodOffset, FastCallInfo { canFastCall, isNoGC }); } } private: std::vector mainMethodIndexes_ {}; std::vector> recordNamePtrs_ {}; std::vector methodPcInfos_ {}; std::unordered_map methodList_ {}; std::unordered_map methodOffsetToRecordName_ {}; std::set skippedMethods_ {}; size_t maxMethodSize_; std::unordered_map classTypeLOffsetToDefMethod_ {}; std::unordered_map functionTypeIdToMethodOffset_ {}; std::unordered_map methodOffsetToFastCallInfos_ {}; }; class BytecodeInfoCollector { public: BytecodeInfoCollector(CompilationEnv *env, JSPandaFile *jsPandaFile, PGOProfilerDecoder &pfDecoder, size_t maxAotMethodSize); BytecodeInfoCollector(CompilationEnv *env, JSPandaFile *jsPandaFile, PGOProfilerDecoder &pfDecoder); ~BytecodeInfoCollector() = default; NO_COPY_SEMANTIC(BytecodeInfoCollector); NO_MOVE_SEMANTIC(BytecodeInfoCollector); Bytecodes* GetByteCodes() { return &bytecodes_; } BCInfo& GetBytecodeInfo() { return bytecodeInfo_; } BCInfo* GetBytecodeInfoPtr() { return &bytecodeInfo_; } const PGOBCInfo* GetPGOBCInfo() const { return &pgoBCInfo_; } void StoreDataToGlobalData(SnapshotGlobalData &snapshotData) { snapshotCPData_->StoreDataToGlobalData(snapshotData, GetSkippedMethodSet()); } const std::set& GetSkippedMethodSet() const { return bytecodeInfo_.GetSkippedMethodSet(); } bool IsSkippedMethod(uint32_t methodOffset) const { return bytecodeInfo_.IsSkippedMethod(methodOffset); } bool FilterMethod(const MethodLiteral *methodLiteral, const MethodPcInfo &methodPCInfo) const { auto recordName = MethodLiteral::GetRecordName(jsPandaFile_, methodLiteral->GetMethodId()); bool methodSizeIsIllegal = methodPCInfo.methodsSize > bytecodeInfo_.GetMaxMethodSize(); bool methodFilteredByPGO = !pfDecoder_.Match(jsPandaFile_, recordName, methodLiteral->GetMethodId()); if (methodSizeIsIllegal || methodFilteredByPGO) { return true; } return false; } const JSPandaFile *GetJSPandaFile() const { return jsPandaFile_; } CompilationEnv *GetCompilationEnv() const { return compilationEnv_; } template void IterateAllMethods(const Callback &cb) { auto &methodList = bytecodeInfo_.GetMethodList(); for (const auto &method : methodList) { uint32_t methodOffset = method.first; cb(methodOffset); } } void ProcessMethod(MethodLiteral *methodLiteral); static void ProcessMethodForJIT(MethodLiteral *method, const JSPandaFile *jp); private: inline size_t GetNewMethodInfoID() { return methodInfoCounts_++; } void ProcessClasses(); void ProcessCurrMethod(); static bool OpcodeSupprotFastCall(BytecodeInstruction::Opcode opcode); static bool OpcodeSupportTypeByteCall(BytecodeInstruction::Opcode opcode); void CollectMethodPcsFromBC(const uint32_t insSz, const uint8_t *insArr, MethodLiteral *method, uint32_t methodOffset, const std::shared_ptr recordNamePtr); static bool IsVRegUsed(const BytecodeInstruction &inst, const BytecodeMetaData &metaData, uint32_t idx); void SetMethodPcInfoIndex(uint32_t methodOffset, const std::pair &processedMethodInfo, const std::shared_ptr recordNamePtr); void CollectMethods(const MethodLiteral *method, const std::shared_ptr recordNamePtr); void CollectMethods(uint32_t methodId, const std::shared_ptr recordNamePtr); void CollectInnerMethodsFromLiteral(uint64_t index, const std::shared_ptr recordNamePtr); void CollectInnerMethodsFromNewLiteral(panda_file::File::EntityId literalId, const std::shared_ptr recordNamePtr); void CollectMethodInfoFromBC(const BytecodeInstruction &bcIns, const MethodLiteral *method, int32_t bcIndex, const std::shared_ptr recordNamePtr); void IterateLiteral(const MethodLiteral *method, std::vector &classOffsetVector); void StoreClassTypeOffset(const uint32_t typeOffset, std::vector &classOffsetVector); void CollectClassLiteralInfo(const MethodLiteral *method, const std::vector &classNameVec); CompilationEnv *compilationEnv_ {nullptr}; JSPandaFile *jsPandaFile_ {nullptr}; BCInfo bytecodeInfo_; PGOProfilerDecoder &pfDecoder_; PGOBCInfo pgoBCInfo_ {}; std::unique_ptr snapshotCPData_; size_t methodInfoCounts_ {0}; std::set classDefBCIndexes_ {}; Bytecodes bytecodes_; std::set processedMethod_; }; } // namespace panda::ecmascript::kungfu #endif // ECMASCRIPT_COMPILER_BYTECODE_INFO_COLLECTOR_H