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