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 26namespace panda::ecmascript::kungfu { 27template<class T, class... Args> 28static T *InitializeMemory(T *mem, Args... args) 29{ 30 return new (mem) T(std::forward<Args>(args)...); 31} 32 33BytecodeInfoCollector::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 46BytecodeInfoCollector::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 59void 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 126void BytecodeInfoCollector::ProcessCurrMethod() 127{ 128 ProcessMethod(compilationEnv_->GetMethodLiteral()); 129} 130 131void 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 163void 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, 186 &opcodeSupprotFastCall, &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 217bool 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 233void 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 250void BytecodeInfoCollector::CollectMethods(const MethodLiteral *method, const std::shared_ptr<CString> recordNamePtr) 251{ 252 auto methodId = method->GetMethodId().GetOffset(); 253 CollectMethods(methodId, recordNamePtr); 254} 255 256void 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 264void 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 273void 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 283void 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::CALLRUNTIME_SUPERCALLFORWARDALLARGS_PREF_V8: 349 case EcmaOpcode::SUPERCALLSPREAD_IMM8_V8: 350 case EcmaOpcode::GETUNMAPPEDARGS: 351 case EcmaOpcode::COPYRESTARGS_IMM8: 352 case EcmaOpcode::WIDE_COPYRESTARGS_PREF_IMM16: { 353 *canFastCall = false; 354 *canTypedCall = false; 355 break; 356 } 357 default: 358 break; 359 } 360 } 361} 362} // namespace panda::ecmascript::kungfu 363