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