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