* 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 "bytecode_instruction-inl.h"
namespace panda::ecmascript::kungfu {
using PGOProfilerDecoder = pgo::PGOProfilerDecoder;
struct MethodPcInfo {
std::vector<const uint8_t*> pcOffsets {};
uint32_t methodsSize {0};
};
class MethodInfo {
public:
MethodInfo(uint32_t methodInfoIndex, uint32_t methodPcInfoIndex, std::shared_ptr<CString> 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<uint32_t>::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<CString> recordNamePtr)
{
recordNamePtr_ = recordNamePtr;
}
inline const std::shared_ptr<CString> 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<uint8_t>(pgoMark, &compileState_.value_);
}
bool IsCompiled() const
{
return CompileStateBit::CompiledBit::Decode(compileState_.value_);
}
void SetIsCompiled(bool isCompiled)
{
CompileStateBit::CompiledBit::Set<uint8_t>(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<bool, 0, BOOL_FLAG_BIT_LENGTH>;
using CompiledBit = PGOBit::NextField<bool, BOOL_FLAG_BIT_LENGTH>;
private:
uint8_t value_ {0};
friend class MethodInfo;
};
uint32_t methodInfoIndex_ { 0 };
uint32_t methodPcInfoIndex_ { 0 };
std::shared_ptr<CString> 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<uint32_t>& GetMainMethodIndexes()
{
return mainMethodIndexes_;
}
std::vector<std::shared_ptr<CString>>& GetRecordNamePtrs()
{
return recordNamePtrs_;
}
const CString &GetRecordNameWithIndex(uint32_t index) const
{
return *recordNamePtrs_[index];
}
std::vector<MethodPcInfo>& GetMethodPcInfos()
{
return methodPcInfos_;
}
std::unordered_map<uint32_t, MethodInfo>& 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<uint32_t>& 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<uint32_t> mainMethodIndexes_ {};
std::vector<std::shared_ptr<CString>> recordNamePtrs_ {};
std::vector<MethodPcInfo> methodPcInfos_ {};
std::unordered_map<uint32_t, MethodInfo> methodList_ {};
std::unordered_map<uint32_t, CString> methodOffsetToRecordName_ {};
std::set<uint32_t> skippedMethods_ {};
size_t maxMethodSize_;
std::unordered_map<uint32_t, uint32_t> classTypeLOffsetToDefMethod_ {};
std::unordered_map<uint32_t, uint32_t> functionTypeIdToMethodOffset_ {};
std::unordered_map<uint32_t, FastCallInfo> 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<uint32_t>& 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 <class Callback>
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<CString> recordNamePtr);
static bool IsVRegUsed(const BytecodeInstruction &inst, const BytecodeMetaData &metaData, uint32_t idx);
void SetMethodPcInfoIndex(uint32_t methodOffset, const std::pair<size_t, uint32_t> &processedMethodInfo,
const std::shared_ptr<CString> recordNamePtr);
void CollectMethods(const MethodLiteral *method, const std::shared_ptr<CString> recordNamePtr);
void CollectMethods(uint32_t methodId, const std::shared_ptr<CString> recordNamePtr);
void CollectInnerMethodsFromLiteral(uint64_t index, const std::shared_ptr<CString> recordNamePtr);
void CollectInnerMethodsFromNewLiteral(panda_file::File::EntityId literalId,
const std::shared_ptr<CString> recordNamePtr);
void CollectMethodInfoFromBC(const BytecodeInstruction &bcIns, const MethodLiteral *method, int32_t bcIndex,
const std::shared_ptr<CString> recordNamePtr);
void IterateLiteral(const MethodLiteral *method, std::vector<uint32_t> &classOffsetVector);
void StoreClassTypeOffset(const uint32_t typeOffset, std::vector<uint32_t> &classOffsetVector);
void CollectClassLiteralInfo(const MethodLiteral *method, const std::vector<std::string> &classNameVec);
CompilationEnv *compilationEnv_ {nullptr};
JSPandaFile *jsPandaFile_ {nullptr};
BCInfo bytecodeInfo_;
PGOProfilerDecoder &pfDecoder_;
PGOBCInfo pgoBCInfo_ {};
std::unique_ptr<SnapshotConstantPoolData> snapshotCPData_;
size_t methodInfoCounts_ {0};
std::set<int32_t> classDefBCIndexes_ {};
Bytecodes bytecodes_;
std::set<uint32_t> processedMethod_;
};
}
#endif