• 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/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 
26 namespace panda::ecmascript::kungfu {
27 template<class T, class... Args>
InitializeMemory(T * mem,Args...args)28 static T *InitializeMemory(T *mem, Args... args)
29 {
30     return new (mem) T(std::forward<Args>(args)...);
31 }
32 
BytecodeInfoCollector(CompilationEnv * env,JSPandaFile * jsPandaFile,PGOProfilerDecoder & pfDecoder,size_t maxAotMethodSize)33 BytecodeInfoCollector::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 
BytecodeInfoCollector(CompilationEnv * env,JSPandaFile * jsPandaFile,PGOProfilerDecoder & pfDecoder)46 BytecodeInfoCollector::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 
ProcessClasses()59 void 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 
ProcessCurrMethod()126 void BytecodeInfoCollector::ProcessCurrMethod()
127 {
128     ProcessMethod(compilationEnv_->GetMethodLiteral());
129 }
130 
ProcessMethod(MethodLiteral * methodLiteral)131 void 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 
CollectMethodPcsFromBC(const uint32_t insSz,const uint8_t * insArr,MethodLiteral * method,uint32_t methodOffset,const std::shared_ptr<CString> recordNamePtr)163 void 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, &opcodeSupprotFastCall,
186             &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
IsVRegUsed(const BytecodeInstruction & inst,const BytecodeMetaData & metaData,uint32_t idx)217 bool 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 
SetMethodPcInfoIndex(uint32_t methodOffset,const std::pair<size_t,uint32_t> & processedMethodInfo,const std::shared_ptr<CString> recordNamePtr)233 void 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 
CollectMethods(const MethodLiteral * method,const std::shared_ptr<CString> recordNamePtr)250 void BytecodeInfoCollector::CollectMethods(const MethodLiteral *method, const std::shared_ptr<CString> recordNamePtr)
251 {
252     auto methodId = method->GetMethodId().GetOffset();
253     CollectMethods(methodId, recordNamePtr);
254 }
255 
CollectMethods(uint32_t methodId,const std::shared_ptr<CString> recordNamePtr)256 void 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 
CollectInnerMethodsFromLiteral(uint64_t index,const std::shared_ptr<CString> recordNamePtr)264 void 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 
CollectInnerMethodsFromNewLiteral(panda_file::File::EntityId literalId,const std::shared_ptr<CString> recordNamePtr)273 void 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 
CollectMethodInfoFromBC(const BytecodeInstruction & bcIns,const MethodLiteral * method,int32_t bcIndex,const std::shared_ptr<CString> recordNamePtr,bool * canFastCall,bool * canTypedCall)283 void 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::SUPERCALLSPREAD_IMM8_V8:
349             case EcmaOpcode::GETUNMAPPEDARGS:
350             case EcmaOpcode::COPYRESTARGS_IMM8:
351             case EcmaOpcode::WIDE_COPYRESTARGS_PREF_IMM16: {
352                 *canFastCall = false;
353                 *canTypedCall = false;
354                 break;
355             }
356             default:
357                 break;
358         }
359     }
360 }
361 }  // namespace panda::ecmascript::kungfu
362