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 #ifndef ECMASCRIPT_COMPILER_COMPILATION_DRIVER_H 17 #define ECMASCRIPT_COMPILER_COMPILATION_DRIVER_H 18 19 #include "ecmascript/compiler/bytecode_info_collector.h" 20 #include "ecmascript/js_function.h" 21 22 namespace panda::ecmascript::kungfu { 23 class AOTFileGenerator; 24 class CompilerLog; 25 struct LOptions; 26 class Module; 27 class CompilationDriver { 28 public: 29 CompilationDriver(PGOProfilerDecoder &profilerDecoder, 30 BytecodeInfoCollector* collector, 31 const std::string &compilemMethodsOption, 32 const std::string &compileSkipMethodsOption, 33 AOTFileGenerator *fileGenerator, 34 const std::string &fileName, 35 const std::string &triple, 36 LOptions *lOptions, 37 CompilerLog *log, 38 bool outputAsm, 39 size_t maxMethodsInModule); 40 virtual ~CompilationDriver(); 41 42 NO_COPY_SEMANTIC(CompilationDriver); 43 NO_MOVE_SEMANTIC(CompilationDriver); 44 IsPGOLoaded()45 bool IsPGOLoaded() const 46 { 47 return pfDecoder_.IsLoaded(); 48 } 49 UpdateCompileQueue(const CString & recordName,EntityId resolvedMethod)50 void UpdateCompileQueue(const CString &recordName, EntityId resolvedMethod) 51 { 52 const auto &methodList = bytecodeInfo_.GetMethodList(); 53 auto &resolvedMethodInfo = methodList.at(resolvedMethod.GetOffset()); 54 if (pfDecoder_.Match(jsPandaFile_, recordName, resolvedMethod) && !resolvedMethodInfo.IsTypeInferAbort()) { 55 return; 56 } 57 // update profile and update compile queue 58 std::unordered_set<EntityId> fullResolvedMethodSet; 59 auto dfs = [this, &fullResolvedMethodSet, resolvedMethod] (const CString &recordName, 60 [[maybe_unused]] const std::unordered_set<EntityId> &oldIds) -> std::unordered_set<EntityId> & { 61 fullResolvedMethodSet.clear(); 62 std::unordered_set<EntityId> currentResolvedMethodSet {resolvedMethod}; 63 uint32_t mainMethodOffset = jsPandaFile_->GetMainMethodIndex(recordName); 64 SearchForCompilation(recordName, currentResolvedMethodSet, 65 fullResolvedMethodSet, mainMethodOffset, true); 66 return fullResolvedMethodSet; 67 }; 68 69 pfDecoder_.Update(jsPandaFile_, recordName, dfs); 70 71 if (fullResolvedMethodSet.size() > 0) { 72 bytecodeInfo_.AddRecordName(recordName); 73 } 74 for (auto &newMethod : fullResolvedMethodSet) { 75 bytecodeInfo_.EraseSkippedMethod(newMethod.GetOffset()); 76 } 77 } 78 79 template <class Callback> CompileMethod(const Callback & cb,uint32_t index,const std::string & methodName,MethodLiteral * methodLiteral,uint32_t methodOffset,const MethodPcInfo & methodPcInfo,MethodInfo & methodInfo)80 void CompileMethod(const Callback &cb, 81 uint32_t index, 82 const std::string &methodName, 83 MethodLiteral *methodLiteral, 84 uint32_t methodOffset, 85 const MethodPcInfo &methodPcInfo, 86 MethodInfo &methodInfo) 87 { 88 Module *module = GetModule(); 89 SetCurrentConstantPool(methodOffset); 90 cb(bytecodeInfo_.GetRecordName(index), methodName, methodLiteral, methodOffset, 91 methodPcInfo, methodInfo, module); 92 if (methodInfo.IsTypeInferAbort()) { 93 return; 94 } 95 IncCompiledMethod(); 96 CompileModuleThenDestroyIfNeeded(); 97 } 98 99 template <class Callback> Run(const Callback & cb)100 void Run(const Callback &cb) 101 { 102 UpdatePGO(); 103 InitializeCompileQueue(); 104 uint32_t index = 0; 105 auto &methodList = bytecodeInfo_.GetMethodList(); 106 const auto &methodPcInfos = bytecodeInfo_.GetMethodPcInfos(); 107 while (!compileQueue_.empty()) { 108 std::queue<uint32_t> methodCompiledOrder; 109 methodCompiledOrder.push(compileQueue_.front()); 110 compileQueue_.pop_front(); 111 while (!methodCompiledOrder.empty()) { 112 auto compilingMethod = methodCompiledOrder.front(); 113 methodCompiledOrder.pop(); 114 bytecodeInfo_.AddMethodOffsetToRecordName(compilingMethod, bytecodeInfo_.GetRecordName(index)); 115 auto &methodInfo = methodList.at(compilingMethod); 116 auto &methodPcInfo = methodPcInfos[methodInfo.GetMethodPcInfoIndex()]; 117 auto methodLiteral = jsPandaFile_->FindMethodLiteral(compilingMethod); 118 const std::string methodName(MethodLiteral::GetMethodName(jsPandaFile_, methodLiteral->GetMethodId())); 119 if (FilterMethod(bytecodeInfo_.GetRecordName(index), methodLiteral, methodPcInfo, methodName)) { 120 bytecodeInfo_.AddSkippedMethod(compilingMethod); 121 } else { 122 if (!methodInfo.IsCompiled()) { 123 methodInfo.SetIsCompiled(true); 124 CompileMethod(cb, index, methodName, methodLiteral, compilingMethod, methodPcInfo, methodInfo); 125 } else if (NeedSecondaryCompile(methodInfo)) { 126 // if a method used to be not full compiled but now it's a deopt resolved method 127 // it should be full compiled again 128 CompileMethod(cb, index, methodName, methodLiteral, compilingMethod, methodPcInfo, methodInfo); 129 } 130 } 131 auto &innerMethods = methodInfo.GetInnerMethods(); 132 for (auto it : innerMethods) { 133 methodCompiledOrder.push(it); 134 } 135 } 136 index++; 137 } 138 CompileLastModuleThenDestroyIfNeeded(); 139 StoreConstantPoolInfo(); 140 } 141 142 void FetchPGOMismatchResult(); 143 AddResolvedMethod(const CString & recordName,uint32_t classLiteralOffset)144 void AddResolvedMethod(const CString &recordName, uint32_t classLiteralOffset) 145 { 146 if (!IsPGOLoaded() || !bytecodeInfo_.HasClassDefMethod(classLiteralOffset)) { 147 return; 148 } 149 uint32_t resolvedMethod = bytecodeInfo_.GetDefineMethod(classLiteralOffset); 150 auto &methodList = bytecodeInfo_.GetMethodList(); 151 auto &methodInfo = methodList.at(resolvedMethod); 152 methodInfo.SetResolvedMethod(true); 153 panda_file::File::EntityId resolvedMethodId(resolvedMethod); 154 UpdateCompileQueue(recordName, resolvedMethodId); 155 } 156 protected: 157 // add maxMethodsInModule_ functions in a module and when a module is 158 // full(maxMethodsInModule_ functions have been put into) or the module is the last module, 159 // compile it and the destroy it. 160 Module *GetModule(); 161 162 void IncCompiledMethod(); 163 164 bool IsCurModuleFull() const; 165 166 void CompileModuleThenDestroyIfNeeded(); 167 168 void CompileLastModuleThenDestroyIfNeeded(); 169 170 void TopologicalSortForRecords(); 171 172 void UpdatePGO(); 173 174 void InitializeCompileQueue(); 175 NeedSecondaryCompile(const MethodInfo & methodInfo)176 bool NeedSecondaryCompile(const MethodInfo &methodInfo) const 177 { 178 return methodInfo.IsTypeInferAbort() && methodInfo.IsResolvedMethod(); 179 } 180 AddDependList(const CString & recordName,uint32_t methodOffset,std::deque<CString> & dependList)181 void AddDependList(const CString &recordName, uint32_t methodOffset, 182 std::deque<CString> &dependList) 183 { 184 auto &methodList = bytecodeInfo_.GetMethodList(); 185 const auto &importRecordInfos = bytecodeInfo_.GetImportRecordsInfos(); 186 auto iter = importRecordInfos.find(recordName); 187 // if the resolved method don't have import records need-inferred, just return. 188 if (iter == importRecordInfos.end()) { 189 return; 190 } 191 auto &methodInfo = methodList.at(methodOffset); 192 // Get the import indexs collected in methodInfo. 193 auto &importIndexs = methodInfo.GetImportIndexes(); 194 // idInRecord is a map like "importIndex: (exportRecord: exportIndex)". 195 const auto &idInRecord = iter->second.GetImportIdToExportRecord(); 196 for (auto index : importIndexs) { 197 auto it = idInRecord.find(index); 198 if (it != idInRecord.end()) { 199 dependList.emplace_back(it->second.first); 200 } 201 } 202 } 203 VerifyAndMarkCurMethod(uint32_t methodOffset,std::unordered_set<EntityId> & newMethodSet)204 bool VerifyAndMarkCurMethod(uint32_t methodOffset, std::unordered_set<EntityId> &newMethodSet) 205 { 206 // if current method is at the boundary state, we should stop the define chain search. 207 if (methodOffset == MethodInfo::DEFAULT_OUTMETHOD_OFFSET) { 208 return false; 209 } 210 // if pgo profile is not matched with current abc file, methodOffset will be different. 211 auto &methodList = bytecodeInfo_.GetMethodList(); 212 auto iter = methodList.find(methodOffset); 213 if (iter == methodList.end()) { 214 LOG_COMPILER(ERROR) << "The correct aot profile has not been used"; 215 return false; 216 } 217 auto &methodInfo = iter->second; 218 // if current method has already been marked as PGO, stop searching upper layer of the define chain. 219 if (methodInfo.IsPGO() && !methodInfo.IsTypeInferAbort()) { 220 return false; 221 } 222 // we need to collect these new-marked PGO methods to update PGO profile 223 panda_file::File::EntityId methodId(methodOffset); 224 newMethodSet.insert(std::move(methodId)); 225 methodInfo.SetIsPGO(true); 226 return true; 227 } 228 UpdateResolveDepends(std::vector<CString> & dependNames,bool needUpdate)229 void UpdateResolveDepends(std::vector<CString> &dependNames, bool needUpdate) 230 { 231 if (needUpdate && !dependNames.empty()) { 232 // depend methods should keep the topological sorting rule 233 std::sort(dependNames.begin(), dependNames.end(), [this](auto first, auto second) { 234 return sortedRecords_.at(first) < sortedRecords_.at(second); 235 }); 236 auto resolvedMethod = compileQueue_.back(); 237 compileQueue_.pop_back(); 238 // it should be like "depended main method1, depended main method2, resolved method" in compileQueue. 239 for (const auto &ele : dependNames) { 240 bytecodeInfo_.AddRecordName(ele); 241 auto eleOffset = jsPandaFile_->GetMainMethodIndex(ele); 242 // these methods will be added to compile queue 243 bytecodeInfo_.EraseSkippedMethod(eleOffset); 244 compileQueue_.push_back(eleOffset); 245 } 246 compileQueue_.push_back(resolvedMethod); 247 } 248 } 249 SearchForCompilation(const CString & recordName,const std::unordered_set<EntityId> & methodSet,std::unordered_set<EntityId> & newMethodSet,uint32_t mainMethodOffset,bool needUpdateCompile)250 void SearchForCompilation(const CString &recordName, const std::unordered_set<EntityId> &methodSet, 251 std::unordered_set<EntityId> &newMethodSet, 252 uint32_t mainMethodOffset, bool needUpdateCompile) 253 { 254 auto &methodList = bytecodeInfo_.GetMethodList(); 255 std::unordered_set<EntityId> mainMethodSet; 256 auto getMainMethodSet = [this, &mainMethodSet](const CString &importRecord, 257 [[maybe_unused]] const std::unordered_set<EntityId> &oldIds) -> std::unordered_set<EntityId>& { 258 mainMethodSet.clear(); 259 auto mainMethodOffset = jsPandaFile_->GetMainMethodIndex(importRecord); 260 panda_file::File::EntityId mainMethodId(mainMethodOffset); 261 mainMethodSet.insert(mainMethodId); 262 return mainMethodSet; 263 }; 264 265 std::vector<CString> importNames{}; 266 std::function<void(EntityId)> importBfs = 267 [this, &methodList, &recordName, &importNames, &getMainMethodSet] 268 (EntityId methodId) -> void { 269 uint32_t methodOffset = methodId.GetOffset(); 270 std::deque<CString> importList{}; 271 AddDependList(recordName, methodOffset, importList); 272 while (!importList.empty()) { 273 auto importRecord = importList.front(); 274 importList.pop_front(); 275 // export syntax only exists in main method, so just judge and collect the main method. 276 auto mainMethodOffset = jsPandaFile_->GetMainMethodIndex(importRecord); 277 auto &mainMethodInfo = methodList.at(mainMethodOffset); 278 // mark the main method in other record as PGO. 279 if (mainMethodInfo.IsPGO()) { 280 continue; 281 } 282 importNames.emplace_back(importRecord); 283 mainMethodInfo.SetIsPGO(true); 284 pfDecoder_.Update(jsPandaFile_, importRecord, getMainMethodSet); 285 AddDependList(importRecord, mainMethodOffset, importList); 286 } 287 }; 288 289 std::function<void(EntityId, bool)> dfs = 290 [this, &newMethodSet, &mainMethodOffset, &dfs, &methodList, &importBfs] 291 (EntityId methodId, bool needUpdateCompile) -> void { 292 uint32_t methodOffset = methodId.GetOffset(); 293 // verify whether we should stop the search for pgo methods or resolve methods. 294 if (!VerifyAndMarkCurMethod(methodOffset, newMethodSet)) { 295 return; 296 } 297 auto &methodInfo = methodList.at(methodOffset); 298 auto outMethodOffset = methodInfo.GetOutMethodOffset(); 299 // for pgo method, collect its import depends method 300 importBfs(methodId); 301 if (needUpdateCompile) { 302 // in deopt, we need to push the first un-marked method which is 303 // in upper layer of the deopt method's define chain (or maybe the deopt method itself) 304 // into the sharedCompiledQueue to trigger the compilation of the deopt method. 305 if (methodOffset != mainMethodOffset) { 306 // few methods which have the same bytecodes as other method can't find its outter method 307 if (outMethodOffset == MethodInfo::DEFAULT_OUTMETHOD_OFFSET) { 308 compileQueue_.push_back(methodOffset); 309 return; 310 } 311 // currentMethod whose outtermethod has been marked as pgo need to push into queue 312 auto outMethodInfo = methodList.at(outMethodOffset); 313 if (outMethodInfo.IsPGO()) { 314 compileQueue_.push_back(methodOffset); 315 return; 316 } 317 } else { 318 // if current searched method is an un-marked main method, just push it to compile queue 319 compileQueue_.push_back(methodOffset); 320 } 321 } 322 if (methodOffset == mainMethodOffset) { 323 return; 324 } 325 EntityId outMethod(outMethodOffset); 326 dfs(outMethod, needUpdateCompile); 327 }; 328 329 // search as link list, a one-way define chain 330 for (auto pgoMethod = methodSet.begin(); pgoMethod != methodSet.end(); pgoMethod++) { 331 dfs(*pgoMethod, needUpdateCompile); 332 } 333 // update compile queue only for resolve method when it depends on other record 334 UpdateResolveDepends(importNames, needUpdateCompile); 335 } 336 337 bool FilterMethod(const CString &recordName, const MethodLiteral *methodLiteral, 338 const MethodPcInfo &methodPCInfo, const std::string &methodName) const; 339 340 std::vector<std::string> SplitString(const std::string &str, const char ch) const; 341 342 void ParseOption(const std::string &option, std::map<std::string, std::vector<std::string>> &optionMap) const; 343 344 bool FilterOption(const std::map<std::string, std::vector<std::string>> &optionMap, const std::string &recordName, 345 const std::string &methodName) const; 346 347 void SetCurrentConstantPool(uint32_t methodOffset) const; 348 349 void StoreConstantPoolInfo() const; 350 351 EcmaVM *vm_ {nullptr}; 352 const JSPandaFile *jsPandaFile_ {nullptr}; 353 PGOProfilerDecoder &pfDecoder_; 354 BytecodeInfoCollector* collector_; 355 BCInfo &bytecodeInfo_; 356 std::deque<uint32_t> compileQueue_ {}; 357 std::map<CString, uint32_t> sortedRecords_ {}; 358 std::map<std::string, std::vector<std::string>> optionSelectMethods_ {}; 359 std::map<std::string, std::vector<std::string>> optionSkipMethods_ {}; 360 uint32_t compiledMethodCnt_ {0}; 361 AOTFileGenerator *fileGenerator_ {nullptr}; 362 std::string fileName_ {}; 363 std::string triple_ {}; 364 LOptions *lOptions_ {nullptr}; 365 CompilerLog *log_ {nullptr}; 366 bool outputAsm_ {false}; 367 size_t maxMethodsInModule_ {0}; 368 }; 369 370 class JitCompilationDriver : public CompilationDriver { 371 public: JitCompilationDriver(PGOProfilerDecoder & profilerDecoder,BytecodeInfoCollector * collector,const std::string & compilemMethodsOption,const std::string & compileSkipMethodsOption,AOTFileGenerator * fileGenerator,const std::string & fileName,const std::string & triple,LOptions * lOptions,CompilerLog * log,bool outputAsm,size_t maxMethodsInModule)372 JitCompilationDriver(PGOProfilerDecoder &profilerDecoder, 373 BytecodeInfoCollector* collector, 374 const std::string &compilemMethodsOption, 375 const std::string &compileSkipMethodsOption, 376 AOTFileGenerator *fileGenerator, 377 const std::string &fileName, 378 const std::string &triple, 379 LOptions *lOptions, 380 CompilerLog *log, 381 bool outputAsm, 382 size_t maxMethodsInModule) : CompilationDriver(profilerDecoder, collector, 383 compilemMethodsOption, compileSkipMethodsOption, 384 fileGenerator, fileName, triple, lOptions, log, 385 outputAsm, maxMethodsInModule) { }; 386 ~JitCompilationDriver() = default; 387 bool RunCg(); 388 Module *GetModule(); 389 390 template <class Callback> CompileMethod(JSHandle<JSFunction> & jsFunction,const Callback & cb)391 void CompileMethod(JSHandle<JSFunction> &jsFunction, const Callback &cb) 392 { 393 for (auto mi : bytecodeInfo_.GetMethodList()) { 394 bytecodeInfo_.AddSkippedMethod(mi.first); 395 } 396 const JSPandaFile *jsPandaFile = Method::Cast(jsFunction->GetMethod().GetTaggedObject())->GetJSPandaFile(); 397 Method *method = Method::Cast(jsFunction->GetMethod().GetTaggedObject()); 398 MethodLiteral *methodLiteral = method->GetMethodLiteral(); 399 const std::string methodName(MethodLiteral::GetMethodName(jsPandaFile, methodLiteral->GetMethodId())); 400 401 auto &methodList = bytecodeInfo_.GetMethodList(); 402 const auto &methodPcInfos = bytecodeInfo_.GetMethodPcInfos(); 403 auto &methodInfo = methodList.at(methodLiteral->GetMethodId().GetOffset()); 404 405 auto &methodPcInfo = methodPcInfos[methodInfo.GetMethodPcInfoIndex()]; 406 auto methodOffset = methodLiteral->GetMethodId().GetOffset(); 407 bytecodeInfo_.EraseSkippedMethod(methodOffset); 408 409 Module *module = GetModule(); 410 cb(bytecodeInfo_.GetRecordName(0), methodName, methodLiteral, methodOffset, 411 methodPcInfo, methodInfo, module); 412 } 413 }; 414 } // namespace panda::ecmascript::kungfu 415 #endif // ECMASCRIPT_COMPILER_COMPILATION_DRIVER_H 416