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#include "ecmascript/compiler/bytecode_info_collector.h"
17
18#include "ecmascript/jspandafile/literal_data_extractor.h"
19#include "ecmascript/module/module_path_helper.h"
20#include "ecmascript/pgo_profiler/pgo_profiler_decoder.h"
21#include "libpandafile/code_data_accessor.h"
22#include "libpandafile/class_data_accessor-inl.h"
23#include "libpandafile/index_accessor.h"
24#include "libpandafile/method_data_accessor-inl.h"
25
26namespace panda::ecmascript::kungfu {
27template<class T, class... Args>
28static T *InitializeMemory(T *mem, Args... args)
29{
30    return new (mem) T(std::forward<Args>(args)...);
31}
32
33BytecodeInfoCollector::BytecodeInfoCollector(CompilationEnv *env, JSPandaFile *jsPandaFile,
34                                             PGOProfilerDecoder &pfDecoder,
35                                             size_t maxAotMethodSize)
36    : compilationEnv_(env),
37      jsPandaFile_(jsPandaFile),
38      bytecodeInfo_(maxAotMethodSize),
39      pfDecoder_(pfDecoder),
40      snapshotCPData_(new SnapshotConstantPoolData(env->GetEcmaVM(), jsPandaFile, &pfDecoder))
41{
42    ASSERT(env->IsAotCompiler());
43    ProcessClasses();
44}
45
46BytecodeInfoCollector::BytecodeInfoCollector(CompilationEnv *env, JSPandaFile *jsPandaFile,
47                                             PGOProfilerDecoder &pfDecoder)
48    : compilationEnv_(env),
49      jsPandaFile_(jsPandaFile),
50      // refactor: jit max method size
51      bytecodeInfo_(env->GetJSOptions().GetMaxAotMethodSize()),
52      pfDecoder_(pfDecoder),
53      snapshotCPData_(nullptr) // jit no need
54{
55    ASSERT(env->IsJitCompiler());
56    ProcessCurrMethod();
57}
58
59void BytecodeInfoCollector::ProcessClasses()
60{
61    ASSERT(jsPandaFile_ != nullptr && jsPandaFile_->GetMethodLiterals() != nullptr);
62    MethodLiteral *methods = jsPandaFile_->GetMethodLiterals();
63    const panda_file::File *pf = jsPandaFile_->GetPandaFile();
64    size_t methodIdx = 0;
65    std::map<uint32_t, std::pair<size_t, uint32_t>> processedMethod;
66    Span<const uint32_t> classIndexes = jsPandaFile_->GetClasses();
67
68    auto &recordNamePtrs = bytecodeInfo_.GetRecordNamePtrs();
69    auto &methodPcInfos = bytecodeInfo_.GetMethodPcInfos();
70    for (const uint32_t index : classIndexes) {
71        panda_file::File::EntityId classId(index);
72        if (jsPandaFile_->IsExternal(classId)) {
73            continue;
74        }
75        panda_file::ClassDataAccessor cda(*pf, classId);
76        CString desc = utf::Mutf8AsCString(cda.GetDescriptor());
77        const std::shared_ptr<CString> recordNamePtr = std::make_shared<CString>(JSPandaFile::ParseEntryPoint(desc));
78        cda.EnumerateMethods([this, methods, &methodIdx, pf, &processedMethod,
79            &recordNamePtrs, &methodPcInfos, &recordNamePtr] (panda_file::MethodDataAccessor &mda) {
80            auto methodId = mda.GetMethodId();
81
82            // Generate all constpool
83            compilationEnv_->FindOrCreateConstPool(jsPandaFile_, methodId);
84
85            auto methodOffset = methodId.GetOffset();
86            CString name = reinterpret_cast<const char *>(jsPandaFile_->GetStringData(mda.GetNameId()).data);
87            if (JSPandaFile::IsEntryOrPatch(name)) {
88                jsPandaFile_->UpdateMainMethodIndex(methodOffset, *recordNamePtr);
89                recordNamePtrs.emplace_back(recordNamePtr);
90            }
91
92            MethodLiteral *methodLiteral = methods + (methodIdx++);
93            InitializeMemory(methodLiteral, methodId);
94            methodLiteral->Initialize(jsPandaFile_);
95
96            ASSERT(jsPandaFile_->IsNewVersion());
97            panda_file::IndexAccessor indexAccessor(*pf, methodId);
98            panda_file::FunctionKind funcKind = indexAccessor.GetFunctionKind();
99            bool isShared = JSPandaFile::IsSendableFunctionKind(funcKind);
100            methodLiteral->SetIsShared(isShared);
101            FunctionKind kind = JSPandaFile::GetFunctionKind(funcKind);
102            methodLiteral->SetFunctionKind(kind);
103
104            auto codeId = mda.GetCodeId();
105            ASSERT(codeId.has_value());
106            panda_file::CodeDataAccessor codeDataAccessor(*pf, codeId.value());
107            uint32_t codeSize = codeDataAccessor.GetCodeSize();
108            const uint8_t *insns = codeDataAccessor.GetInstructions();
109            auto it = processedMethod.find(methodOffset);
110            if (it == processedMethod.end()) {
111                CollectMethodPcsFromBC(codeSize, insns, methodLiteral,
112                    methodOffset, recordNamePtr);
113                ASSERT(methodPcInfos.size() > 0);
114                processedMethod[methodOffset] = std::make_pair(methodPcInfos.size() - 1, methodOffset);
115            }
116
117            SetMethodPcInfoIndex(methodOffset, processedMethod[methodOffset], recordNamePtr);
118            jsPandaFile_->SetMethodLiteralToMap(methodLiteral);
119            pfDecoder_.MatchAndMarkMethod(jsPandaFile_, *recordNamePtr, name.c_str(), methodId);
120        });
121    }
122    LOG_COMPILER(INFO) << "Total number of methods in file: " << jsPandaFile_->GetJSPandaFileDesc()
123                       << " is: " << methodIdx;
124}
125
126void BytecodeInfoCollector::ProcessCurrMethod()
127{
128    ProcessMethod(compilationEnv_->GetMethodLiteral());
129}
130
131void BytecodeInfoCollector::ProcessMethod(MethodLiteral *methodLiteral)
132{
133    if (UNLIKELY(methodLiteral == nullptr)) {
134        return;
135    }
136    panda_file::File::EntityId methodIdx = methodLiteral->GetMethodId();
137    auto methodOffset = methodIdx.GetOffset();
138    if (processedMethod_.find(methodOffset) != processedMethod_.end()) {
139        return;
140    }
141
142    auto &recordNamePtrs = bytecodeInfo_.GetRecordNamePtrs();
143    auto &methodPcInfos = bytecodeInfo_.GetMethodPcInfos();
144    const std::shared_ptr<CString> recordNamePtr =
145        std::make_shared<CString>(jsPandaFile_->GetRecordNameWithBundlePack(methodIdx));
146    recordNamePtrs.emplace_back(recordNamePtr);
147    ASSERT(jsPandaFile_->IsNewVersion());
148
149    const panda_file::File *pf = jsPandaFile_->GetPandaFile();
150    panda_file::MethodDataAccessor mda(*pf, methodIdx);
151    auto codeId = mda.GetCodeId();
152    ASSERT(codeId.has_value());
153    panda_file::CodeDataAccessor codeDataAccessor(*pf, codeId.value());
154    uint32_t codeSize = codeDataAccessor.GetCodeSize();
155    const uint8_t *insns = codeDataAccessor.GetInstructions();
156
157    CollectMethodPcsFromBC(codeSize, insns, methodLiteral, methodOffset, recordNamePtr);
158    ASSERT(methodPcInfos.size() > 0);
159    SetMethodPcInfoIndex(methodOffset, {methodPcInfos.size() - 1, methodOffset}, recordNamePtr);
160    processedMethod_.emplace(methodOffset);
161}
162
163void BytecodeInfoCollector::CollectMethodPcsFromBC(const uint32_t insSz, const uint8_t *insArr,
164                                                   MethodLiteral *method, uint32_t methodOffset,
165                                                   const std::shared_ptr<CString> recordNamePtr)
166{
167    auto bcIns = BytecodeInst(insArr);
168    auto bcInsLast = bcIns.JumpTo(insSz);
169    int32_t bcIndex = 0;
170    auto &methodPcInfos = bytecodeInfo_.GetMethodPcInfos();
171    methodPcInfos.emplace_back(MethodPcInfo { {}, insSz });
172    auto &pcOffsets = methodPcInfos.back().pcOffsets;
173    const uint8_t *curPc = bcIns.GetAddress();
174    bool canFastCall = true;
175    bool noGC = true;
176    bool debuggerStmt = false;
177    uint32_t newtargetIndex = method->GetNewTargetVregIndex();
178    bool canTypedCall = true;
179
180    while (bcIns.GetAddress() != bcInsLast.GetAddress()) {
181        curPc = bcIns.GetAddress();
182        auto metaData = bytecodes_.GetBytecodeMetaData(curPc);
183        bool opcodeSupprotFastCall = true;
184        bool opcodeSupportTypeByteCall = true;
185        CollectMethodInfoFromBC(bcIns, method, bcIndex, recordNamePtr,
186                                &opcodeSupprotFastCall, &opcodeSupportTypeByteCall);
187        bool vregSupportFastCall = !IsVRegUsed(bcIns, metaData, newtargetIndex);
188        if (!opcodeSupprotFastCall || !vregSupportFastCall) {
189            canFastCall = false;
190        }
191        if (!opcodeSupportTypeByteCall) {
192            canTypedCall = false;
193        }
194        if (snapshotCPData_ != nullptr) {
195            snapshotCPData_->Record(bcIns, bcIndex, *recordNamePtr, method);
196        }
197        pgoBCInfo_.Record(bcIns, bcIndex, *recordNamePtr, method);
198        if (noGC && !metaData.IsNoGC()) {
199            noGC = false;
200        }
201        if (!debuggerStmt && metaData.HasDebuggerStmt()) {
202            debuggerStmt = true;
203        }
204        auto nextInst = bcIns.GetNext();
205        bcIns = nextInst;
206        pcOffsets.emplace_back(curPc);
207        bcIndex++;
208    }
209    bytecodeInfo_.SetMethodOffsetToFastCallInfo(methodOffset, canFastCall, noGC);
210    method->SetIsFastCall(canFastCall);
211    method->SetNoGCBit(noGC);
212    method->SetHasDebuggerStmtBit(debuggerStmt);
213    method->SetCanTypedCall(canTypedCall);
214}
215
216// static
217bool BytecodeInfoCollector::IsVRegUsed(const BytecodeInstruction &inst, const BytecodeMetaData &metaData, uint32_t idx)
218{
219    if (idx == 0) {
220        return false;
221    }
222    uint32_t vregCount = metaData.GetVRegCount();
223    for (uint32_t i = 0; i < vregCount; i++) {
224        ASSERT(inst.HasVReg(inst.GetFormat(), i));
225        uint16_t vregIdx = inst.GetVReg(i);
226        if (vregIdx == idx) {
227            return true;
228        }
229    }
230    return false;
231}
232
233void BytecodeInfoCollector::SetMethodPcInfoIndex(uint32_t methodOffset,
234                                                 const std::pair<size_t, uint32_t> &processedMethodInfo,
235                                                 const std::shared_ptr<CString> recordNamePtr)
236{
237    auto processedMethodPcInfoIndex = processedMethodInfo.first;
238    auto &methodList = bytecodeInfo_.GetMethodList();
239
240    auto iter = methodList.find(methodOffset);
241    if (iter != methodList.end()) {
242        MethodInfo &methodInfo = iter->second;
243        methodInfo.SetMethodPcInfoIndex(processedMethodPcInfoIndex);
244        return;
245    }
246    MethodInfo info(GetNewMethodInfoID(), processedMethodPcInfoIndex, recordNamePtr);
247    methodList.emplace(methodOffset, info);
248}
249
250void BytecodeInfoCollector::CollectMethods(const MethodLiteral *method, const std::shared_ptr<CString> recordNamePtr)
251{
252    auto methodId = method->GetMethodId().GetOffset();
253    CollectMethods(methodId, recordNamePtr);
254}
255
256void BytecodeInfoCollector::CollectMethods(uint32_t methodId, const std::shared_ptr<CString> recordNamePtr)
257{
258    auto &methodList = bytecodeInfo_.GetMethodList();
259    if (methodList.find(methodId) == methodList.end()) {
260        methodList.emplace(methodId, MethodInfo(GetNewMethodInfoID(), 0, recordNamePtr));
261    }
262}
263
264void BytecodeInfoCollector::CollectInnerMethodsFromLiteral(uint64_t index, const std::shared_ptr<CString> recordNamePtr)
265{
266    std::vector<uint32_t> methodOffsets;
267    LiteralDataExtractor::GetMethodOffsets(jsPandaFile_, index, methodOffsets);
268    for (auto methodOffset : methodOffsets) {
269        CollectMethods(methodOffset, recordNamePtr);
270    }
271}
272
273void BytecodeInfoCollector::CollectInnerMethodsFromNewLiteral(panda_file::File::EntityId literalId,
274                                                              const std::shared_ptr<CString> recordNamePtr)
275{
276    std::vector<uint32_t> methodOffsets;
277    LiteralDataExtractor::GetMethodOffsets(jsPandaFile_, literalId, methodOffsets);
278    for (auto methodOffset : methodOffsets) {
279        CollectMethods(methodOffset, recordNamePtr);
280    }
281}
282
283void BytecodeInfoCollector::CollectMethodInfoFromBC(const BytecodeInstruction &bcIns, const MethodLiteral *method,
284                                                    int32_t bcIndex, const std::shared_ptr<CString> recordNamePtr,
285                                                    bool *canFastCall, bool *canTypedCall)
286{
287    if (!(bcIns.HasFlag(BytecodeInstruction::Flags::STRING_ID) &&
288        BytecodeInstruction::HasId(BytecodeInstruction::GetFormat(bcIns.GetOpcode()), 0))) {
289        CollectMethods(method, recordNamePtr);
290        BytecodeInstruction::Opcode opcode = static_cast<BytecodeInstruction::Opcode>(bcIns.GetOpcode());
291        switch (opcode) {
292            uint32_t innerMethodId;
293            case BytecodeInstruction::Opcode::DEFINEFUNC_IMM8_ID16_IMM8:
294            case BytecodeInstruction::Opcode::DEFINEFUNC_IMM16_ID16_IMM8:
295            case BytecodeInstruction::Opcode::DEFINEMETHOD_IMM8_ID16_IMM8:
296            case BytecodeInstruction::Opcode::DEFINEMETHOD_IMM16_ID16_IMM8: {
297                innerMethodId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
298                    static_cast<uint16_t>(bcIns.GetId().AsRawValue())).GetOffset();
299                CollectMethods(innerMethodId, recordNamePtr);
300                break;
301            }
302            case BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8:{
303                auto entityId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
304                    (bcIns.GetId <BytecodeInstruction::Format::IMM8_ID16_ID16_IMM16_V8, 0>()).AsRawValue());
305                classDefBCIndexes_.insert(bcIndex);
306                innerMethodId = entityId.GetOffset();
307                CollectMethods(innerMethodId, recordNamePtr);
308                auto literalId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
309                    (bcIns.GetId <BytecodeInstruction::Format::IMM8_ID16_ID16_IMM16_V8, 1>()).AsRawValue());
310                CollectInnerMethodsFromNewLiteral(literalId, recordNamePtr);
311                break;
312            }
313            case BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8: {
314                auto entityId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
315                    (bcIns.GetId <BytecodeInstruction::Format::IMM16_ID16_ID16_IMM16_V8, 0>()).AsRawValue());
316                classDefBCIndexes_.insert(bcIndex);
317                innerMethodId = entityId.GetOffset();
318                CollectMethods(innerMethodId, recordNamePtr);
319                auto literalId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
320                    (bcIns.GetId <BytecodeInstruction::Format::IMM16_ID16_ID16_IMM16_V8, 1>()).AsRawValue());
321                CollectInnerMethodsFromNewLiteral(literalId, recordNamePtr);
322                break;
323            }
324            case BytecodeInstruction::Opcode::CREATEARRAYWITHBUFFER_IMM8_ID16:
325            case BytecodeInstruction::Opcode::CREATEARRAYWITHBUFFER_IMM16_ID16:
326            case BytecodeInstruction::Opcode::CREATEOBJECTWITHBUFFER_IMM8_ID16:
327            case BytecodeInstruction::Opcode::CREATEOBJECTWITHBUFFER_IMM16_ID16: {
328                auto literalId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
329                    static_cast<uint16_t>(bcIns.GetId().AsRawValue()));
330                CollectInnerMethodsFromNewLiteral(literalId, recordNamePtr);
331                break;
332            }
333            case BytecodeInstruction::Opcode::DEPRECATED_CREATEARRAYWITHBUFFER_PREF_IMM16:
334            case BytecodeInstruction::Opcode::DEPRECATED_CREATEOBJECTWITHBUFFER_PREF_IMM16: {
335                auto imm = bcIns.GetImm<BytecodeInstruction::Format::PREF_IMM16>();
336                CollectInnerMethodsFromLiteral(imm, recordNamePtr);
337                break;
338            }
339            case EcmaOpcode::RESUMEGENERATOR:
340            case EcmaOpcode::SUSPENDGENERATOR_V8:
341            case EcmaOpcode::SUPERCALLTHISRANGE_IMM8_IMM8_V8:
342            case EcmaOpcode::WIDE_SUPERCALLTHISRANGE_PREF_IMM16_V8:
343            case EcmaOpcode::SUPERCALLARROWRANGE_IMM8_IMM8_V8:
344            case EcmaOpcode::WIDE_SUPERCALLARROWRANGE_PREF_IMM16_V8: {
345                *canFastCall = false;
346                break;
347            }
348            case EcmaOpcode::CALLRUNTIME_SUPERCALLFORWARDALLARGS_PREF_V8:
349            case EcmaOpcode::SUPERCALLSPREAD_IMM8_V8:
350            case EcmaOpcode::GETUNMAPPEDARGS:
351            case EcmaOpcode::COPYRESTARGS_IMM8:
352            case EcmaOpcode::WIDE_COPYRESTARGS_PREF_IMM16: {
353                *canFastCall = false;
354                *canTypedCall = false;
355                break;
356            }
357            default:
358                break;
359        }
360    }
361}
362}  // namespace panda::ecmascript::kungfu
363