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