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