1 /*
2 * Copyright (c) 2022 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/type_recorder.h"
19 #include "ecmascript/interpreter/interpreter-inl.h"
20 #include "ecmascript/jspandafile/type_literal_extractor.h"
21 #include "ecmascript/module/module_path_helper.h"
22 #include "ecmascript/pgo_profiler/pgo_profiler_decoder.h"
23 #include "ecmascript/ts_types/ts_type_parser.h"
24 #include "libpandafile/code_data_accessor.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(EcmaVM * vm,JSPandaFile * jsPandaFile,PGOProfilerDecoder & pfDecoder,size_t maxAotMethodSize,bool enableCollectLiteralInfo)33 BytecodeInfoCollector::BytecodeInfoCollector(EcmaVM *vm, JSPandaFile *jsPandaFile, PGOProfilerDecoder &pfDecoder,
34 size_t maxAotMethodSize, bool enableCollectLiteralInfo)
35 : vm_(vm),
36 jsPandaFile_(jsPandaFile),
37 bytecodeInfo_(maxAotMethodSize),
38 pfDecoder_(pfDecoder),
39 enableCollectLiteralInfo_(enableCollectLiteralInfo)
40 {
41 vm_->GetJSThread()->GetCurrentEcmaContext()->GetTSManager()->SetBytecodeInfoCollector(this);
42 ProcessClasses();
43 ProcessEnvs();
44 }
45
~BytecodeInfoCollector()46 BytecodeInfoCollector::~BytecodeInfoCollector()
47 {
48 if (envManager_ != nullptr) {
49 delete envManager_;
50 envManager_ = nullptr;
51 }
52 auto tsManager = vm_->GetJSThread()->GetCurrentEcmaContext()->GetTSManager();
53 tsManager->PrintTypeInfo(jsPandaFile_);
54 tsManager->SetBytecodeInfoCollector(nullptr);
55 }
56
ProcessEnvs()57 void BytecodeInfoCollector::ProcessEnvs()
58 {
59 if (envManager_ == nullptr) {
60 envManager_ = new LexEnvManager(bytecodeInfo_);
61 }
62 }
63
ProcessClasses()64 void BytecodeInfoCollector::ProcessClasses()
65 {
66 ASSERT(jsPandaFile_ != nullptr && jsPandaFile_->GetMethodLiterals() != nullptr);
67 MethodLiteral *methods = jsPandaFile_->GetMethodLiterals();
68 const panda_file::File *pf = jsPandaFile_->GetPandaFile();
69 size_t methodIdx = 0;
70 std::map<uint32_t, std::pair<size_t, uint32_t>> processedMethod;
71 Span<const uint32_t> classIndexes = jsPandaFile_->GetClasses();
72
73 auto &recordNames = bytecodeInfo_.GetRecordNames();
74 auto &methodPcInfos = bytecodeInfo_.GetMethodPcInfos();
75 std::vector<panda_file::File::EntityId> methodIndexes;
76 std::vector<panda_file::File::EntityId> classConstructIndexes;
77 for (const uint32_t index : classIndexes) {
78 panda_file::File::EntityId classId(index);
79 if (jsPandaFile_->IsExternal(classId)) {
80 continue;
81 }
82 panda_file::ClassDataAccessor cda(*pf, classId);
83 CString desc = utf::Mutf8AsCString(cda.GetDescriptor());
84 const CString recordName = JSPandaFile::ParseEntryPoint(desc);
85 cda.EnumerateMethods([this, methods, &methodIdx, pf, &processedMethod,
86 &recordNames, &methodPcInfos, &recordName,
87 &methodIndexes, &classConstructIndexes] (panda_file::MethodDataAccessor &mda) {
88 auto methodId = mda.GetMethodId();
89 methodIndexes.emplace_back(methodId);
90 CollectFunctionTypeId(methodId);
91
92 // Generate all constpool
93 vm_->GetJSThread()->GetCurrentEcmaContext()->FindOrCreateConstPool(jsPandaFile_, methodId);
94
95 auto methodOffset = methodId.GetOffset();
96 CString name = reinterpret_cast<const char *>(jsPandaFile_->GetStringData(mda.GetNameId()).data);
97 if (JSPandaFile::IsEntryOrPatch(name)) {
98 jsPandaFile_->UpdateMainMethodIndex(methodOffset, recordName);
99 recordNames.emplace_back(recordName);
100 }
101
102 MethodLiteral *methodLiteral = methods + (methodIdx++);
103 InitializeMemory(methodLiteral, methodId);
104 methodLiteral->Initialize(jsPandaFile_);
105
106 ASSERT(jsPandaFile_->IsNewVersion());
107 panda_file::IndexAccessor indexAccessor(*pf, methodId);
108 panda_file::FunctionKind funcKind = indexAccessor.GetFunctionKind();
109 FunctionKind kind = JSPandaFile::GetFunctionKind(funcKind);
110 methodLiteral->SetFunctionKind(kind);
111
112 auto codeId = mda.GetCodeId();
113 ASSERT(codeId.has_value());
114 panda_file::CodeDataAccessor codeDataAccessor(*pf, codeId.value());
115 uint32_t codeSize = codeDataAccessor.GetCodeSize();
116 const uint8_t *insns = codeDataAccessor.GetInstructions();
117 auto it = processedMethod.find(methodOffset);
118 if (it == processedMethod.end()) {
119 std::vector<std::string> classNameVec;
120 CollectMethodPcsFromBC(codeSize, insns, methodLiteral, classNameVec,
121 recordName, methodOffset, classConstructIndexes);
122 processedMethod[methodOffset] = std::make_pair(methodPcInfos.size() - 1, methodOffset);
123 // collect className and literal offset for type infer
124 if (EnableCollectLiteralInfo()) {
125 CollectClassLiteralInfo(methodLiteral, classNameVec);
126 }
127 }
128
129 SetMethodPcInfoIndex(methodOffset, processedMethod[methodOffset]);
130 jsPandaFile_->SetMethodLiteralToMap(methodLiteral);
131 pfDecoder_.MatchAndMarkMethod(recordName, name.c_str(), methodId);
132 });
133 }
134 // class Construct need to use new target, can not fastcall
135 for (auto index : classConstructIndexes) {
136 MethodLiteral *method = jsPandaFile_->GetMethodLiteralByIndex(index.GetOffset());
137 if (method != nullptr) {
138 method->SetFunctionKind(FunctionKind::CLASS_CONSTRUCTOR);
139 method->SetIsFastCall(false);
140 bytecodeInfo_.ModifyMethodOffsetToCanFastCall(index.GetOffset(), false);
141 }
142 }
143 // Collect import(infer-needed) and export relationship among all records.
144 CollectRecordReferenceREL();
145 RearrangeInnerMethods();
146 LOG_COMPILER(INFO) << "Total number of methods in file: "
147 << jsPandaFile_->GetJSPandaFileDesc()
148 << " is: "
149 << methodIdx;
150 }
151
CollectClassLiteralInfo(const MethodLiteral * method,const std::vector<std::string> & classNameVec)152 void BytecodeInfoCollector::CollectClassLiteralInfo(const MethodLiteral *method,
153 const std::vector<std::string> &classNameVec)
154 {
155 std::vector<uint32_t> classOffsetVec;
156 IterateLiteral(method, classOffsetVec);
157
158 if (classOffsetVec.size() == classNameVec.size()) {
159 for (uint32_t i = 0; i < classOffsetVec.size(); i++) {
160 vm_->GetJSThread()->GetCurrentEcmaContext()->GetTSManager()->AddElementToClassNameMap(
161 jsPandaFile_, classOffsetVec[i], classNameVec[i]);
162 }
163 }
164 }
165
CollectFunctionTypeId(panda_file::File::EntityId fieldId)166 void BytecodeInfoCollector::CollectFunctionTypeId(panda_file::File::EntityId fieldId)
167 {
168 uint32_t offset = fieldId.GetOffset();
169 TypeAnnotationExtractor annoExtractor(jsPandaFile_, offset);
170 uint32_t typeId = annoExtractor.GetMethodTypeOffset();
171 if (typeId != 0) {
172 bytecodeInfo_.SetFunctionTypeIDAndMethodOffset(typeId, offset);
173 }
174 if (annoExtractor.IsNamespace()) {
175 MarkMethodNamespace(offset);
176 }
177 }
178
CollectInnerFuncType(const MethodLiteral * method,uint32_t innerMethodId,int32_t bcIndex)179 void BytecodeInfoCollector::CollectInnerFuncType(const MethodLiteral *method, uint32_t innerMethodId, int32_t bcIndex)
180 {
181 auto &methodList = bytecodeInfo_.GetMethodList();
182 auto methodId = method->GetMethodId().GetOffset();
183 auto methodIter = methodList.find(methodId);
184 if (methodIter == methodList.end()) {
185 return;
186 }
187 TypeAnnotationExtractor annoExtractor(jsPandaFile_, innerMethodId);
188 uint32_t innerFuncType = annoExtractor.GetMethodTypeOffset();
189 if (innerFuncType != 0) {
190 methodIter->second.AddBcToTypeId(bcIndex, innerFuncType);
191 }
192 }
193
IterateLiteral(const MethodLiteral * method,std::vector<uint32_t> & classOffsetVector)194 void BytecodeInfoCollector::IterateLiteral(const MethodLiteral *method,
195 std::vector<uint32_t> &classOffsetVector)
196 {
197 panda_file::File::EntityId fieldId = method->GetMethodId();
198 uint32_t defineMethodOffset = fieldId.GetOffset();
199 TypeAnnotationExtractor annoExtractor(jsPandaFile_, defineMethodOffset);
200 std::map<int32_t, uint32_t> offsetTypeMap;
201 annoExtractor.EnumerateInstsAndTypes(
202 [this, &offsetTypeMap, defineMethodOffset](const int32_t bcOffset, const uint32_t typeOffset) {
203 if (classDefBCIndexes_.find(bcOffset) != classDefBCIndexes_.end() ||
204 classDefBCIndexes_.find(bcOffset - 1) != classDefBCIndexes_.end()) { // for getter setter
205 bytecodeInfo_.SetClassTypeOffsetAndDefMethod(typeOffset, defineMethodOffset);
206 }
207 if (bcOffset != TypeRecorder::METHOD_ANNOTATION_THIS_TYPE_OFFSET &&
208 TSTypeParser::IsUserDefinedType(typeOffset)) {
209 offsetTypeMap.insert(std::make_pair(bcOffset, typeOffset));
210 }
211 });
212
213 for (auto item : offsetTypeMap) {
214 uint32_t typeOffset = item.second;
215 StoreClassTypeOffset(typeOffset, classOffsetVector);
216 }
217
218 classDefBCIndexes_.clear();
219 }
220
StoreClassTypeOffset(const uint32_t typeOffset,std::vector<uint32_t> & classOffsetVector)221 void BytecodeInfoCollector::StoreClassTypeOffset(const uint32_t typeOffset, std::vector<uint32_t> &classOffsetVector)
222 {
223 TypeLiteralExtractor typeLiteralExtractor(jsPandaFile_, typeOffset);
224 if (typeLiteralExtractor.GetTypeKind() != TSTypeKind::CLASS) {
225 return;
226 }
227
228 if (classOffsetVector.empty() || typeOffset != classOffsetVector.back()) {
229 classOffsetVector.emplace_back(typeOffset);
230 }
231 }
232
CollectMethodPcsFromBC(const uint32_t insSz,const uint8_t * insArr,MethodLiteral * method,std::vector<std::string> & classNameVec,const CString & recordName,uint32_t methodOffset,std::vector<panda_file::File::EntityId> & classConstructIndexes)233 void BytecodeInfoCollector::CollectMethodPcsFromBC(const uint32_t insSz, const uint8_t *insArr,
234 MethodLiteral *method, std::vector<std::string> &classNameVec, const CString &recordName,
235 uint32_t methodOffset, std::vector<panda_file::File::EntityId> &classConstructIndexes)
236 {
237 auto bcIns = BytecodeInst(insArr);
238 auto bcInsLast = bcIns.JumpTo(insSz);
239 int32_t bcIndex = 0;
240 auto &methodPcInfos = bytecodeInfo_.GetMethodPcInfos();
241 methodPcInfos.emplace_back(MethodPcInfo { {}, insSz });
242 auto &pcOffsets = methodPcInfos.back().pcOffsets;
243 const uint8_t *curPc = bcIns.GetAddress();
244 bool canFastCall = true;
245 bool noGC = true;
246
247 while (bcIns.GetAddress() != bcInsLast.GetAddress()) {
248 bool fastCallFlag = true;
249 CollectMethodInfoFromBC(bcIns, method, classNameVec, bcIndex, classConstructIndexes, &fastCallFlag);
250 if (!fastCallFlag) {
251 canFastCall = false;
252 }
253 CollectModuleInfoFromBC(bcIns, method, recordName);
254 CollectConstantPoolIndexInfoFromBC(bcIns, method, bcIndex);
255 pgoBCInfo_.Record(bcIns, bcIndex, recordName, method);
256 if (noGC && !bytecodes_.GetBytecodeMetaData(curPc).IsNoGC()) {
257 noGC = false;
258 }
259 curPc = bcIns.GetAddress();
260 auto nextInst = bcIns.GetNext();
261 bcIns = nextInst;
262 pcOffsets.emplace_back(curPc);
263 bcIndex++;
264 }
265 pcOffsets.emplace_back(bcInsLast.GetAddress());
266 bytecodeInfo_.SetMethodOffsetToFastCallInfo(methodOffset, canFastCall, noGC);
267 method->SetIsFastCall(canFastCall);
268 method->SetNoGCBit(noGC);
269 }
270
SetMethodPcInfoIndex(uint32_t methodOffset,const std::pair<size_t,uint32_t> & processedMethodInfo)271 void BytecodeInfoCollector::SetMethodPcInfoIndex(uint32_t methodOffset,
272 const std::pair<size_t, uint32_t> &processedMethodInfo)
273 {
274 auto processedMethodPcInfoIndex = processedMethodInfo.first;
275 auto processedMethodOffset = processedMethodInfo.second;
276 uint32_t numOfLexVars = 0;
277 LexicalEnvStatus status = LexicalEnvStatus::VIRTUAL_LEXENV;
278 auto &methodList = bytecodeInfo_.GetMethodList();
279 std::set<uint32_t> indexSet{};
280 // Methods with the same instructions in abc files have the same static information. Since
281 // information from bytecodes is collected only once, methods other than the processed method
282 // will obtain static information from the processed method.
283 auto processedIter = methodList.find(processedMethodOffset);
284 if (processedIter != methodList.end()) {
285 const MethodInfo &processedMethod = processedIter->second;
286 numOfLexVars = processedMethod.GetNumOfLexVars();
287 status = processedMethod.GetLexEnvStatus();
288 indexSet = processedMethod.GetImportIndexes();
289 }
290
291 auto iter = methodList.find(methodOffset);
292 if (iter != methodList.end()) {
293 MethodInfo &methodInfo = iter->second;
294 methodInfo.SetMethodPcInfoIndex(processedMethodPcInfoIndex);
295 methodInfo.SetNumOfLexVars(numOfLexVars);
296 methodInfo.SetLexEnvStatus(status);
297 // if these methods have the same bytecode, their import indexs must be the same.
298 methodInfo.CopyImportIndex(indexSet);
299 return;
300 }
301 MethodInfo info(GetMethodInfoID(), processedMethodPcInfoIndex, LexEnv::DEFAULT_ROOT,
302 MethodInfo::DEFAULT_OUTMETHOD_OFFSET, numOfLexVars, status);
303 info.CopyImportIndex(indexSet);
304 methodList.emplace(methodOffset, info);
305 }
306
CollectInnerMethods(const MethodLiteral * method,uint32_t innerMethodOffset,bool isConstructor)307 void BytecodeInfoCollector::CollectInnerMethods(const MethodLiteral *method,
308 uint32_t innerMethodOffset,
309 bool isConstructor)
310 {
311 auto methodId = method->GetMethodId().GetOffset();
312 CollectInnerMethods(methodId, innerMethodOffset, isConstructor);
313 }
314
CollectInnerMethods(uint32_t methodId,uint32_t innerMethodOffset,bool isConstructor)315 void BytecodeInfoCollector::CollectInnerMethods(uint32_t methodId, uint32_t innerMethodOffset, bool isConstructor)
316 {
317 auto &methodList = bytecodeInfo_.GetMethodList();
318 uint32_t methodInfoId = 0;
319 auto methodIter = methodList.find(methodId);
320 if (methodIter != methodList.end()) {
321 MethodInfo &methodInfo = methodIter->second;
322 methodInfoId = methodInfo.GetMethodInfoIndex();
323 methodInfo.AddInnerMethod(innerMethodOffset, isConstructor);
324 } else {
325 methodInfoId = GetMethodInfoID();
326 MethodInfo info(methodInfoId, 0, LexEnv::DEFAULT_ROOT);
327 methodList.emplace(methodId, info);
328 methodList.at(methodId).AddInnerMethod(innerMethodOffset, isConstructor);
329 }
330
331 auto innerMethodIter = methodList.find(innerMethodOffset);
332 if (innerMethodIter != methodList.end()) {
333 innerMethodIter->second.SetOutMethodId(methodInfoId);
334 innerMethodIter->second.SetOutMethodOffset(methodId);
335 return;
336 }
337 MethodInfo innerInfo(GetMethodInfoID(), 0, methodInfoId, methodId);
338 methodList.emplace(innerMethodOffset, innerInfo);
339 }
340
MarkMethodNamespace(const uint32_t methodOffset)341 void BytecodeInfoCollector::MarkMethodNamespace(const uint32_t methodOffset)
342 {
343 auto &methodList = bytecodeInfo_.GetMethodList();
344 auto iter = methodList.find(methodOffset);
345 if (iter != methodList.end()) {
346 MethodInfo &methodInfo = iter->second;
347 methodInfo.MarkMethodNamespace();
348 return;
349 }
350 MethodInfo info(GetMethodInfoID(), 0, LexEnv::DEFAULT_ROOT, MethodInfo::DEFAULT_OUTMETHOD_OFFSET,
351 0, LexicalEnvStatus::VIRTUAL_LEXENV, true);
352 methodList.emplace(methodOffset, info);
353 }
354
CollectInnerMethodsFromLiteral(const MethodLiteral * method,uint64_t index)355 void BytecodeInfoCollector::CollectInnerMethodsFromLiteral(const MethodLiteral *method, uint64_t index)
356 {
357 std::vector<uint32_t> methodOffsets;
358 LiteralDataExtractor::GetMethodOffsets(jsPandaFile_, index, methodOffsets);
359 for (auto methodOffset : methodOffsets) {
360 CollectInnerMethods(method, methodOffset);
361 }
362 }
363
NewLexEnvWithSize(const MethodLiteral * method,uint64_t numOfLexVars)364 void BytecodeInfoCollector::NewLexEnvWithSize(const MethodLiteral *method, uint64_t numOfLexVars)
365 {
366 auto &methodList = bytecodeInfo_.GetMethodList();
367 auto methodOffset = method->GetMethodId().GetOffset();
368 auto iter = methodList.find(methodOffset);
369 if (iter != methodList.end()) {
370 MethodInfo &methodInfo = iter->second;
371 methodInfo.SetNumOfLexVars(numOfLexVars);
372 methodInfo.SetLexEnvStatus(LexicalEnvStatus::REALITY_LEXENV);
373 return;
374 }
375 MethodInfo info(GetMethodInfoID(), 0, LexEnv::DEFAULT_ROOT, MethodInfo::DEFAULT_OUTMETHOD_OFFSET,
376 numOfLexVars, LexicalEnvStatus::REALITY_LEXENV);
377 methodList.emplace(methodOffset, info);
378 }
379
CollectInnerMethodsFromNewLiteral(const MethodLiteral * method,panda_file::File::EntityId literalId)380 void BytecodeInfoCollector::CollectInnerMethodsFromNewLiteral(const MethodLiteral *method,
381 panda_file::File::EntityId literalId)
382 {
383 std::vector<uint32_t> methodOffsets;
384 LiteralDataExtractor::GetMethodOffsets(jsPandaFile_, literalId, methodOffsets);
385 for (auto methodOffset : methodOffsets) {
386 CollectInnerMethods(method, methodOffset);
387 }
388 }
389
CollectMethodInfoFromBC(const BytecodeInstruction & bcIns,const MethodLiteral * method,std::vector<std::string> & classNameVec,int32_t bcIndex,std::vector<panda_file::File::EntityId> & classConstructIndexes,bool * canFastCall)390 void BytecodeInfoCollector::CollectMethodInfoFromBC(const BytecodeInstruction &bcIns,
391 const MethodLiteral *method, std::vector<std::string> &classNameVec, int32_t bcIndex,
392 std::vector<panda_file::File::EntityId> &classConstructIndexes, bool *canFastCall)
393 {
394 if (!(bcIns.HasFlag(BytecodeInstruction::Flags::STRING_ID) &&
395 BytecodeInstruction::HasId(BytecodeInstruction::GetFormat(bcIns.GetOpcode()), 0))) {
396 BytecodeInstruction::Opcode opcode = static_cast<BytecodeInstruction::Opcode>(bcIns.GetOpcode());
397 switch (opcode) {
398 uint32_t methodId;
399 case BytecodeInstruction::Opcode::DEFINEFUNC_IMM8_ID16_IMM8:
400 case BytecodeInstruction::Opcode::DEFINEFUNC_IMM16_ID16_IMM8: {
401 methodId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
402 static_cast<uint16_t>(bcIns.GetId().AsRawValue())).GetOffset();
403 CollectInnerMethods(method, methodId);
404 CollectInnerFuncType(method, methodId, bcIndex);
405 break;
406 }
407 case BytecodeInstruction::Opcode::DEFINEMETHOD_IMM8_ID16_IMM8:
408 case BytecodeInstruction::Opcode::DEFINEMETHOD_IMM16_ID16_IMM8: {
409 methodId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
410 static_cast<uint16_t>(bcIns.GetId().AsRawValue())).GetOffset();
411 CollectInnerMethods(method, methodId);
412 break;
413 }
414 case BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8:{
415 auto entityId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
416 (bcIns.GetId <BytecodeInstruction::Format::IMM8_ID16_ID16_IMM16_V8, 0>()).AsRawValue());
417 classConstructIndexes.emplace_back(entityId);
418 classNameVec.emplace_back(GetClassName(entityId));
419 classDefBCIndexes_.insert(bcIndex);
420 methodId = entityId.GetOffset();
421 CollectInnerMethods(method, methodId, true);
422 auto literalId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
423 (bcIns.GetId <BytecodeInstruction::Format::IMM8_ID16_ID16_IMM16_V8, 1>()).AsRawValue());
424 CollectInnerMethodsFromNewLiteral(method, literalId);
425 break;
426 }
427 case BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8: {
428 auto entityId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
429 (bcIns.GetId <BytecodeInstruction::Format::IMM16_ID16_ID16_IMM16_V8, 0>()).AsRawValue());
430 classConstructIndexes.emplace_back(entityId);
431 classNameVec.emplace_back(GetClassName(entityId));
432 classDefBCIndexes_.insert(bcIndex);
433 methodId = entityId.GetOffset();
434 CollectInnerMethods(method, methodId, true);
435 auto literalId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
436 (bcIns.GetId <BytecodeInstruction::Format::IMM16_ID16_ID16_IMM16_V8, 1>()).AsRawValue());
437 CollectInnerMethodsFromNewLiteral(method, literalId);
438 break;
439 }
440 case BytecodeInstruction::Opcode::CREATEARRAYWITHBUFFER_IMM8_ID16:
441 case BytecodeInstruction::Opcode::CREATEARRAYWITHBUFFER_IMM16_ID16: {
442 auto literalId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
443 static_cast<uint16_t>(bcIns.GetId().AsRawValue()));
444 CollectInnerMethodsFromNewLiteral(method, literalId);
445 break;
446 }
447 case BytecodeInstruction::Opcode::DEPRECATED_CREATEARRAYWITHBUFFER_PREF_IMM16: {
448 auto imm = bcIns.GetImm<BytecodeInstruction::Format::PREF_IMM16>();
449 CollectInnerMethodsFromLiteral(method, imm);
450 break;
451 }
452 case BytecodeInstruction::Opcode::CREATEOBJECTWITHBUFFER_IMM8_ID16:
453 case BytecodeInstruction::Opcode::CREATEOBJECTWITHBUFFER_IMM16_ID16: {
454 auto literalId = jsPandaFile_->ResolveMethodIndex(method->GetMethodId(),
455 static_cast<uint16_t>(bcIns.GetId().AsRawValue()));
456 CollectInnerMethodsFromNewLiteral(method, literalId);
457 break;
458 }
459 case BytecodeInstruction::Opcode::DEPRECATED_CREATEOBJECTWITHBUFFER_PREF_IMM16: {
460 auto imm = bcIns.GetImm<BytecodeInstruction::Format::PREF_IMM16>();
461 CollectInnerMethodsFromLiteral(method, imm);
462 break;
463 }
464 case BytecodeInstruction::Opcode::NEWLEXENV_IMM8: {
465 auto imm = bcIns.GetImm<BytecodeInstruction::Format::IMM8>();
466 NewLexEnvWithSize(method, imm);
467 break;
468 }
469 case BytecodeInstruction::Opcode::NEWLEXENVWITHNAME_IMM8_ID16: {
470 auto imm = bcIns.GetImm<BytecodeInstruction::Format::IMM8_ID16>();
471 NewLexEnvWithSize(method, imm);
472 break;
473 }
474 case BytecodeInstruction::Opcode::WIDE_NEWLEXENV_PREF_IMM16: {
475 auto imm = bcIns.GetImm<BytecodeInstruction::Format::PREF_IMM16>();
476 NewLexEnvWithSize(method, imm);
477 break;
478 }
479 case BytecodeInstruction::Opcode::WIDE_NEWLEXENVWITHNAME_PREF_IMM16_ID16: {
480 auto imm = bcIns.GetImm<BytecodeInstruction::Format::PREF_IMM16_ID16>();
481 NewLexEnvWithSize(method, imm);
482 break;
483 }
484 case EcmaOpcode::RESUMEGENERATOR:
485 case EcmaOpcode::SUSPENDGENERATOR_V8:
486 case EcmaOpcode::SUPERCALLTHISRANGE_IMM8_IMM8_V8:
487 case EcmaOpcode::WIDE_SUPERCALLTHISRANGE_PREF_IMM16_V8:
488 case EcmaOpcode::SUPERCALLARROWRANGE_IMM8_IMM8_V8:
489 case EcmaOpcode::WIDE_SUPERCALLARROWRANGE_PREF_IMM16_V8:
490 case EcmaOpcode::SUPERCALLSPREAD_IMM8_V8:
491 case EcmaOpcode::GETUNMAPPEDARGS:
492 case EcmaOpcode::COPYRESTARGS_IMM8:
493 case EcmaOpcode::WIDE_COPYRESTARGS_PREF_IMM16: {
494 *canFastCall = false;
495 return;
496 }
497 default:
498 break;
499 }
500 }
501 }
502
CollectModuleInfoFromBC(const BytecodeInstruction & bcIns,const MethodLiteral * method,const CString & recordName)503 void BytecodeInfoCollector::CollectModuleInfoFromBC(const BytecodeInstruction &bcIns,
504 const MethodLiteral *method,
505 const CString &recordName)
506 {
507 auto methodOffset = method->GetMethodId().GetOffset();
508 // For records without tsType, we don't need to collect its export info.
509 if (jsPandaFile_->HasTSTypes(recordName) && !(bcIns.HasFlag(BytecodeInstruction::Flags::STRING_ID) &&
510 BytecodeInstruction::HasId(BytecodeInstruction::GetFormat(bcIns.GetOpcode()), 0))) {
511 BytecodeInstruction::Opcode opcode = static_cast<BytecodeInstruction::Opcode>(bcIns.GetOpcode());
512 switch (opcode) {
513 case BytecodeInstruction::Opcode::STMODULEVAR_IMM8: {
514 auto imm = bcIns.GetImm<BytecodeInstruction::Format::IMM8>();
515 // The export syntax only exists in main function.
516 if (jsPandaFile_->GetMainMethodIndex(recordName) == methodOffset) {
517 CollectExportIndexs(recordName, imm);
518 }
519 break;
520 }
521 case BytecodeInstruction::Opcode::WIDE_STMODULEVAR_PREF_IMM16: {
522 auto imm = bcIns.GetImm<BytecodeInstruction::Format::PREF_IMM16>();
523 if (jsPandaFile_->GetMainMethodIndex(recordName) == methodOffset) {
524 CollectExportIndexs(recordName, imm);
525 }
526 break;
527 }
528 case BytecodeInstruction::Opcode::LDEXTERNALMODULEVAR_IMM8:{
529 auto imm = bcIns.GetImm<BytecodeInstruction::Format::IMM8>();
530 CollectImportIndexs(methodOffset, imm);
531 break;
532 }
533 case BytecodeInstruction::Opcode::WIDE_LDEXTERNALMODULEVAR_PREF_IMM16:{
534 auto imm = bcIns.GetImm<BytecodeInstruction::Format::PREF_IMM16>();
535 CollectImportIndexs(methodOffset, imm);
536 break;
537 }
538 default:
539 break;
540 }
541 }
542 }
543
CollectImportIndexs(uint32_t methodOffset,uint32_t index)544 void BytecodeInfoCollector::CollectImportIndexs(uint32_t methodOffset, uint32_t index)
545 {
546 auto &methodList = bytecodeInfo_.GetMethodList();
547 auto iter = methodList.find(methodOffset);
548 if (iter != methodList.end()) {
549 MethodInfo &methodInfo = iter->second;
550 // Collect import indexs of each method in its MethodInfo to do accurate Pgo compilation analysis.
551 methodInfo.AddImportIndex(index);
552 return;
553 }
554 MethodInfo info(GetMethodInfoID(), 0, LexEnv::DEFAULT_ROOT);
555 info.AddImportIndex(index);
556 methodList.emplace(methodOffset, info);
557 }
558
CollectExportIndexs(const CString & recordName,uint32_t index)559 void BytecodeInfoCollector::CollectExportIndexs(const CString &recordName, uint32_t index)
560 {
561 JSThread *thread = vm_->GetJSThread();
562 ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
563 CString exportLocalName = "*default*";
564 ObjectFactory *objFactory = vm_->GetFactory();
565 [[maybe_unused]] EcmaHandleScope scope(thread);
566 JSHandle<SourceTextModule> currentModule = moduleManager->HostGetImportedModule(recordName);
567 if (currentModule->GetLocalExportEntries().IsUndefined()) {
568 return;
569 }
570 // localExportEntries contain all local element info exported in this record.
571 JSHandle<TaggedArray> localExportArray(thread, currentModule->GetLocalExportEntries());
572 ASSERT(index < localExportArray->GetLength());
573 JSHandle<LocalExportEntry> currentExportEntry(thread, localExportArray->Get(index));
574 JSHandle<JSTaggedValue> exportName(thread, currentExportEntry->GetExportName());
575 JSHandle<JSTaggedValue> localName(thread, currentExportEntry->GetLocalName());
576
577 JSHandle<JSTaggedValue> exportLocalNameHandle =
578 JSHandle<JSTaggedValue>::Cast(objFactory->NewFromUtf8(exportLocalName));
579 JSHandle<JSTaggedValue> defaultName = thread->GlobalConstants()->GetHandledDefaultString();
580 /* if current exportName is "default", but localName not "*default*" like "export default class A{},
581 * localName is A, exportName is default in exportEntry". this will be recorded as "A:classType" in
582 * exportTable in typeSystem. At this situation, we will use localName to judge whether it has a actual
583 * Type record. Otherwise, we will use exportName.
584 */
585 if (JSTaggedValue::SameValue(exportName, defaultName) &&
586 !JSTaggedValue::SameValue(localName, exportLocalNameHandle)) {
587 exportName = localName;
588 }
589
590 JSHandle<EcmaString> exportNameStr(thread, EcmaString::Cast(exportName->GetTaggedObject()));
591 // In order to reduce redundant compilation, when a export element satisfies one of the following conditions,
592 // it will be added to the ExportRecordInfo of this record.
593 // 1) its name is not recorded in exportTypeTable, or
594 // 2) its type is classType or any.
595 if (!CheckExportNameAndClassType(recordName, exportNameStr)) {
596 bytecodeInfo_.AddExportIndexToRecord(recordName, index);
597 }
598 }
599
CheckExportNameAndClassType(const CString & recordName,const JSHandle<EcmaString> & exportStr)600 bool BytecodeInfoCollector::CheckExportNameAndClassType(const CString &recordName,
601 const JSHandle<EcmaString> &exportStr)
602 {
603 auto tsManager = vm_->GetJSThread()->GetCurrentEcmaContext()->GetTSManager();
604 JSHandle<TaggedArray> exportTypeTable = tsManager->GetExportTableFromLiteral(jsPandaFile_, recordName);
605 uint32_t length = exportTypeTable->GetLength();
606 for (uint32_t i = 0; i < length; i = i + 2) { // 2: skip a pair of key and value
607 EcmaString *valueString = EcmaString::Cast(exportTypeTable->Get(i).GetTaggedObject());
608 if (!EcmaStringAccessor::StringsAreEqual(*exportStr, valueString)) {
609 continue;
610 }
611 uint32_t typeId = static_cast<uint32_t>(exportTypeTable->Get(i + 1).GetInt());
612 if (TSTypeParser::IsUserDefinedType(typeId)) {
613 TypeLiteralExtractor typeExtractor(jsPandaFile_, typeId);
614 if (typeExtractor.GetTypeKind() == TSTypeKind::CLASS) {
615 return false;
616 }
617 }
618 if (typeId != 0) {
619 return true;
620 }
621 }
622 return false;
623 }
624
CollectRecordReferenceREL()625 void BytecodeInfoCollector::CollectRecordReferenceREL()
626 {
627 auto &recordNames = bytecodeInfo_.GetRecordNames();
628 for (auto &record : recordNames) {
629 JSRecordInfo info = jsPandaFile_->FindRecordInfo(record);
630 if (jsPandaFile_->HasTSTypes(info)|| jsPandaFile_->IsModule(info)) {
631 CollectRecordImportInfo(record);
632 CollectRecordExportInfo(record);
633 }
634 }
635 }
636
637 /* Each import index is corresponded to a ResolvedIndexBinding in the Environment of its module.
638 * Through ResolvedIndexBinding, we can get the export module and its export index. Only when the
639 * export index is in the non-type-record set which we have collected in CollectExportIndexs function,
640 * this export element can be infer-needed. We will collect the map as (key: import index , value: (exportRecord,
641 * exportIndex)) for using in pgo analysis and type infer.
642 */
CollectRecordImportInfo(const CString & recordName)643 void BytecodeInfoCollector::CollectRecordImportInfo(const CString &recordName)
644 {
645 auto thread = vm_->GetJSThread();
646 ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
647 [[maybe_unused]] EcmaHandleScope scope(thread);
648 JSHandle<SourceTextModule> currentModule = moduleManager->HostGetImportedModule(recordName);
649 // Collect Import Info
650 JSTaggedValue moduleEnvironment = currentModule->GetEnvironment();
651 if (moduleEnvironment.IsUndefined()) {
652 return;
653 }
654 ASSERT(moduleEnvironment.IsTaggedArray());
655 JSHandle<TaggedArray> moduleArray(thread, moduleEnvironment);
656 auto length = moduleArray->GetLength();
657 for (size_t index = 0; index < length; index++) {
658 JSTaggedValue resolvedBinding = moduleArray->Get(index);
659 // if resolvedBinding.IsHole(), means that importname is * or it belongs to empty Aot module.
660 if (resolvedBinding.IsHole()) {
661 continue;
662 }
663 ResolvedIndexBinding *binding = ResolvedIndexBinding::Cast(resolvedBinding.GetTaggedObject());
664 CString resolvedRecord = ModuleManager::GetRecordName(binding->GetModule());
665 auto bindingIndex = binding->GetIndex();
666 if (bytecodeInfo_.HasExportIndexToRecord(resolvedRecord, bindingIndex)) {
667 bytecodeInfo_.AddImportRecordInfoToRecord(recordName, resolvedRecord, index, bindingIndex);
668 }
669 }
670 }
671
672 // For type infer under retranmission (export * from "xxx"), we collect the star export records in this function.
CollectRecordExportInfo(const CString & recordName)673 void BytecodeInfoCollector::CollectRecordExportInfo(const CString &recordName)
674 {
675 auto thread = vm_->GetJSThread();
676 ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
677 [[maybe_unused]] EcmaHandleScope scope(thread);
678 JSHandle<SourceTextModule> currentModule = moduleManager->HostGetImportedModule(recordName);
679 // Collect Star Export Info
680 JSTaggedValue starEntries = currentModule->GetStarExportEntries();
681 if (starEntries.IsUndefined()) {
682 return;
683 }
684 ASSERT(starEntries.IsTaggedArray());
685 JSHandle<TaggedArray> starEntriesArray(thread, starEntries);
686 auto starLength = starEntriesArray->GetLength();
687 JSMutableHandle<StarExportEntry> starExportEntry(thread, JSTaggedValue::Undefined());
688 for (size_t index = 0; index < starLength; index++) {
689 starExportEntry.Update(starEntriesArray->Get(index));
690 JSTaggedValue moduleRequest = starExportEntry->GetModuleRequest();
691 CString moduleRequestName = ConvertToString(EcmaString::Cast(moduleRequest.GetTaggedObject()));
692 if (ModulePathHelper::IsNativeModuleRequest(moduleRequestName)) {
693 return;
694 }
695 CString baseFileName = jsPandaFile_->GetJSPandaFileDesc();
696 CString entryPoint = ModulePathHelper::ConcatFileNameWithMerge(thread, jsPandaFile_,
697 baseFileName, recordName, moduleRequestName);
698 if (jsPandaFile_->HasTypeSummaryOffset(entryPoint)) {
699 bytecodeInfo_.AddStarExportToRecord(recordName, entryPoint);
700 }
701 }
702 }
703
RearrangeInnerMethods()704 void BytecodeInfoCollector::RearrangeInnerMethods()
705 {
706 auto &methodList = bytecodeInfo_.GetMethodList();
707 for (auto &it : methodList) {
708 MethodInfo &methodInfo = it.second;
709 methodInfo.RearrangeInnerMethods();
710 }
711 }
712
CollectConstantPoolIndexInfoFromBC(const BytecodeInstruction & bcIns,const MethodLiteral * method,uint32_t bcIndex)713 void BytecodeInfoCollector::CollectConstantPoolIndexInfoFromBC(const BytecodeInstruction &bcIns,
714 const MethodLiteral *method, uint32_t bcIndex)
715 {
716 BytecodeInstruction::Opcode opcode = static_cast<BytecodeInstruction::Opcode>(bcIns.GetOpcode());
717 uint32_t methodOffset = method->GetMethodId().GetOffset();
718 switch (opcode) {
719 case BytecodeInstruction::Opcode::LDA_STR_ID16:
720 case BytecodeInstruction::Opcode::STOWNBYNAME_IMM8_ID16_V8:
721 case BytecodeInstruction::Opcode::STOWNBYNAME_IMM16_ID16_V8:
722 case BytecodeInstruction::Opcode::CREATEREGEXPWITHLITERAL_IMM8_ID16_IMM8:
723 case BytecodeInstruction::Opcode::CREATEREGEXPWITHLITERAL_IMM16_ID16_IMM8:
724 case BytecodeInstruction::Opcode::STCONSTTOGLOBALRECORD_IMM16_ID16:
725 case BytecodeInstruction::Opcode::TRYLDGLOBALBYNAME_IMM8_ID16:
726 case BytecodeInstruction::Opcode::TRYLDGLOBALBYNAME_IMM16_ID16:
727 case BytecodeInstruction::Opcode::TRYSTGLOBALBYNAME_IMM8_ID16:
728 case BytecodeInstruction::Opcode::TRYSTGLOBALBYNAME_IMM16_ID16:
729 case BytecodeInstruction::Opcode::STTOGLOBALRECORD_IMM16_ID16:
730 case BytecodeInstruction::Opcode::STOWNBYNAMEWITHNAMESET_IMM8_ID16_V8:
731 case BytecodeInstruction::Opcode::STOWNBYNAMEWITHNAMESET_IMM16_ID16_V8:
732 case BytecodeInstruction::Opcode::LDTHISBYNAME_IMM8_ID16:
733 case BytecodeInstruction::Opcode::LDTHISBYNAME_IMM16_ID16:
734 case BytecodeInstruction::Opcode::STTHISBYNAME_IMM8_ID16:
735 case BytecodeInstruction::Opcode::STTHISBYNAME_IMM16_ID16:
736 case BytecodeInstruction::Opcode::LDGLOBALVAR_IMM16_ID16:
737 case BytecodeInstruction::Opcode::LDOBJBYNAME_IMM8_ID16:
738 case BytecodeInstruction::Opcode::LDOBJBYNAME_IMM16_ID16:
739 case BytecodeInstruction::Opcode::STOBJBYNAME_IMM8_ID16_V8:
740 case BytecodeInstruction::Opcode::STOBJBYNAME_IMM16_ID16_V8:
741 case BytecodeInstruction::Opcode::LDSUPERBYNAME_IMM8_ID16:
742 case BytecodeInstruction::Opcode::LDSUPERBYNAME_IMM16_ID16:
743 case BytecodeInstruction::Opcode::STSUPERBYNAME_IMM8_ID16_V8:
744 case BytecodeInstruction::Opcode::STSUPERBYNAME_IMM16_ID16_V8:
745 case BytecodeInstruction::Opcode::STGLOBALVAR_IMM16_ID16:
746 case BytecodeInstruction::Opcode::LDBIGINT_ID16: {
747 auto index = bcIns.GetId().AsRawValue();
748 AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::STRING, index, methodOffset, bcIndex);
749 break;
750 }
751 case BytecodeInstruction::Opcode::DEFINEFUNC_IMM8_ID16_IMM8:
752 case BytecodeInstruction::Opcode::DEFINEFUNC_IMM16_ID16_IMM8:
753 case BytecodeInstruction::Opcode::DEFINEMETHOD_IMM8_ID16_IMM8:
754 case BytecodeInstruction::Opcode::DEFINEMETHOD_IMM16_ID16_IMM8: {
755 auto index = bcIns.GetId().AsRawValue();
756 AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::METHOD, index, methodOffset, bcIndex);
757 break;
758 }
759 case BytecodeInstruction::Opcode::CREATEOBJECTWITHBUFFER_IMM8_ID16:
760 case BytecodeInstruction::Opcode::CREATEOBJECTWITHBUFFER_IMM16_ID16: {
761 auto index = bcIns.GetId().AsRawValue();
762 AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::OBJECT_LITERAL, index, methodOffset, bcIndex);
763 break;
764 }
765 case BytecodeInstruction::Opcode::CREATEARRAYWITHBUFFER_IMM8_ID16:
766 case BytecodeInstruction::Opcode::CREATEARRAYWITHBUFFER_IMM16_ID16: {
767 auto index = bcIns.GetId().AsRawValue();
768 AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::ARRAY_LITERAL, index, methodOffset, bcIndex);
769 break;
770 }
771 case BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM8_ID16_ID16_IMM16_V8: {
772 auto methodIndex = (bcIns.GetId <BytecodeInstruction::Format::IMM8_ID16_ID16_IMM16_V8, 0>()).AsRawValue();
773 AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::METHOD, methodIndex, methodOffset, bcIndex);
774 auto literalIndex = (bcIns.GetId <BytecodeInstruction::Format::IMM8_ID16_ID16_IMM16_V8, 1>()).AsRawValue();
775 AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::CLASS_LITERAL, literalIndex,
776 methodOffset, bcIndex);
777 break;
778 }
779 case BytecodeInstruction::Opcode::DEFINECLASSWITHBUFFER_IMM16_ID16_ID16_IMM16_V8: {
780 auto methodIndex = (bcIns.GetId <BytecodeInstruction::Format::IMM16_ID16_ID16_IMM16_V8, 0>()).AsRawValue();
781 AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::METHOD, methodIndex, methodOffset, bcIndex);
782 auto literalIndex = (bcIns.GetId <BytecodeInstruction::Format::IMM16_ID16_ID16_IMM16_V8, 1>()).AsRawValue();
783 AddConstantPoolIndexToBCInfo(ConstantPoolInfo::ItemType::CLASS_LITERAL, literalIndex,
784 methodOffset, bcIndex);
785 break;
786 }
787 default:
788 break;
789 }
790 }
791
LexEnvManager(BCInfo & bcInfo)792 LexEnvManager::LexEnvManager(BCInfo &bcInfo)
793 : lexEnvs_(bcInfo.GetMethodList().size())
794 {
795 const auto &methodList = bcInfo.GetMethodList();
796 for (const auto &it : methodList) {
797 const MethodInfo &methodInfo = it.second;
798 lexEnvs_[methodInfo.GetMethodInfoIndex()].Inilialize(methodInfo.GetOutMethodId(),
799 methodInfo.GetNumOfLexVars(),
800 methodInfo.GetLexEnvStatus());
801 }
802 }
803
SetLexEnvElementType(uint32_t methodId,uint32_t level,uint32_t slot,const GateType & type)804 void LexEnvManager::SetLexEnvElementType(uint32_t methodId, uint32_t level, uint32_t slot, const GateType &type)
805 {
806 uint32_t offset = GetTargetLexEnv(methodId, level);
807 lexEnvs_[offset].SetLexVarType(slot, type);
808 }
809
GetLexEnvElementType(uint32_t methodId,uint32_t level,uint32_t slot) const810 GateType LexEnvManager::GetLexEnvElementType(uint32_t methodId, uint32_t level, uint32_t slot) const
811 {
812 uint32_t offset = GetTargetLexEnv(methodId, level);
813 return lexEnvs_[offset].GetLexVarType(slot);
814 }
815
GetTargetLexEnv(uint32_t methodId,uint32_t level) const816 uint32_t LexEnvManager::GetTargetLexEnv(uint32_t methodId, uint32_t level) const
817 {
818 auto offset = methodId;
819 auto status = GetLexEnvStatus(offset);
820 while (!HasDefaultRoot(offset) && ((level > 0) || (status != LexicalEnvStatus::REALITY_LEXENV))) {
821 offset = GetOutMethodId(offset);
822 if (HasDefaultRoot(offset)) {
823 break;
824 }
825 if (status == LexicalEnvStatus::REALITY_LEXENV && level != 0) {
826 --level;
827 }
828 status = GetLexEnvStatus(offset);
829 }
830 return offset;
831 }
832
AddIndexToCPItem(ItemType type,uint32_t index,uint32_t methodOffset,uint32_t bcIndex)833 void ConstantPoolInfo::AddIndexToCPItem(ItemType type, uint32_t index, uint32_t methodOffset, uint32_t bcIndex)
834 {
835 Item &item = GetCPItem(type);
836 if (item.find(index) != item.end()) {
837 return;
838 }
839 item.insert({index, ItemData {index, methodOffset, nullptr, bcIndex}});
840 }
841 } // namespace panda::ecmascript::kungfu
842