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 21 namespace panda::ecmascript::kungfu { 22 class CompilationDriver { 23 public: CompilationDriver(JSPandaFile * jsPandaFile,PGOProfilerLoader & profilerLoader,BCInfo & bytecodeInfo)24 explicit CompilationDriver(JSPandaFile *jsPandaFile, PGOProfilerLoader &profilerLoader, 25 BCInfo &bytecodeInfo) 26 : jsPandaFile_(jsPandaFile), pfLoader_(profilerLoader), bytecodeInfo_(bytecodeInfo) 27 { 28 } 29 30 ~CompilationDriver() = default; 31 NO_COPY_SEMANTIC(CompilationDriver); 32 NO_MOVE_SEMANTIC(CompilationDriver); 33 IsPGOLoaded()34 bool IsPGOLoaded() const 35 { 36 return pfLoader_.IsLoaded(); 37 } 38 UpdateCompileQueue(const CString & recordName,EntityId resolvedMethod)39 void UpdateCompileQueue(const CString &recordName, EntityId resolvedMethod) 40 { 41 if (pfLoader_.Match(recordName, resolvedMethod)) { 42 return; 43 } 44 // update profile and update compile queue 45 std::unordered_set<EntityId> fullResolvedMethodSet; 46 auto dfs = [this, &fullResolvedMethodSet, resolvedMethod] (const CString &recordName, 47 [[maybe_unused]] const std::unordered_set<EntityId> &oldIds) -> std::unordered_set<EntityId> &{ 48 fullResolvedMethodSet.clear(); 49 std::unordered_set<EntityId> currentResolvedMethodSet {resolvedMethod}; 50 uint32_t mainMethodOffset = jsPandaFile_->GetMainMethodIndex(recordName); 51 SearchForCompilation(currentResolvedMethodSet, fullResolvedMethodSet, mainMethodOffset, true); 52 return fullResolvedMethodSet; 53 }; 54 55 pfLoader_.Update(recordName, dfs); 56 57 if (fullResolvedMethodSet.size() > 0) { 58 bytecodeInfo_.AddRecordName(recordName); 59 } 60 for (auto &newMethod : fullResolvedMethodSet) { 61 bytecodeInfo_.EraseSkippedMethod(newMethod.GetOffset()); 62 } 63 } 64 65 template <class Callback> Run(const Callback & cb)66 void Run(const Callback &cb) 67 { 68 UpdatePGO(); 69 InitializeCompileQueue(); 70 uint32_t index = 0; 71 auto &methodList = bytecodeInfo_.GetMethodList(); 72 const auto &methodPcInfos = bytecodeInfo_.GetMethodPcInfos(); 73 while (!compileQueue_.empty()) { 74 std::queue<uint32_t> methodCompiledOrder; 75 methodCompiledOrder.push(compileQueue_.front()); 76 compileQueue_.pop(); 77 while (!methodCompiledOrder.empty()) { 78 auto compilingMethod = methodCompiledOrder.front(); 79 methodCompiledOrder.pop(); 80 bytecodeInfo_.AddMethodOffsetToRecordName(compilingMethod, bytecodeInfo_.GetRecordName(index)); 81 auto &methodInfo = methodList.at(compilingMethod); 82 auto &methodPcInfo = methodPcInfos[methodInfo.GetMethodPcInfoIndex()]; 83 auto methodLiteral = jsPandaFile_->FindMethodLiteral(compilingMethod); 84 const std::string methodName(MethodLiteral::GetMethodName(jsPandaFile_, methodLiteral->GetMethodId())); 85 if (FilterMethod(bytecodeInfo_.GetRecordName(index), methodLiteral, methodPcInfo)) { 86 bytecodeInfo_.AddSkippedMethod(compilingMethod); 87 } else { 88 if (!methodInfo.IsCompiled()) { 89 methodInfo.SetIsCompiled(true); 90 cb(bytecodeInfo_.GetRecordName(index), methodName, methodLiteral, compilingMethod, 91 methodPcInfo, methodInfo.GetMethodInfoIndex()); 92 } 93 } 94 auto &innerMethods = methodInfo.GetInnerMethods(); 95 for (auto it : innerMethods) { 96 methodCompiledOrder.push(it); 97 } 98 } 99 index++; 100 } 101 } 102 AddResolvedMethod(const CString & recordName,uint32_t classLiteralOffset)103 void AddResolvedMethod(const CString &recordName, uint32_t classLiteralOffset) 104 { 105 if (!IsPGOLoaded() || !bytecodeInfo_.HasClassDefMethod(classLiteralOffset)) { 106 return; 107 } 108 uint32_t resolvedMethod = bytecodeInfo_.GetDefineMethod(classLiteralOffset); 109 panda_file::File::EntityId resolvedMethodId(resolvedMethod); 110 UpdateCompileQueue(recordName, resolvedMethodId); 111 } 112 private: 113 void UpdatePGO(); 114 115 void InitializeCompileQueue(); 116 SearchForCompilation(const std::unordered_set<EntityId> & methodSet,std::unordered_set<EntityId> & newMethodSet,uint32_t mainMethodOffset,bool needUpdateCompile)117 void SearchForCompilation(const std::unordered_set<EntityId> &methodSet, std::unordered_set<EntityId> &newMethodSet, 118 uint32_t mainMethodOffset, bool needUpdateCompile) 119 { 120 auto &methodList = bytecodeInfo_.GetMethodList(); 121 std::function<void(EntityId, bool)> dfs = [this, &newMethodSet, &mainMethodOffset, &dfs, &methodList] 122 (EntityId methodId, bool needUpdateCompile) -> void { 123 uint32_t methodOffset = methodId.GetOffset(); 124 if (methodOffset == MethodInfo::DEFAULT_OUTMETHOD_OFFSET) { 125 return; 126 } 127 // if pgo profile is not matched with current abc file, methodOffset will be different 128 if (methodList.find(methodOffset) == methodList.end()) { 129 LOG_COMPILER(ERROR) << "The correct aot profile has not been used"; 130 return; 131 } 132 auto &methodInfo = methodList.at(methodOffset); 133 auto outMethodOffset = methodInfo.GetOutMethodOffset(); 134 // if current method has already been marked as PGO, stop searching upper layer of the define chain 135 if (methodInfo.IsPGO()) { 136 return; 137 } 138 // we need to collect these new-marked PGO methods to update PGO profile 139 newMethodSet.insert(methodId); 140 methodInfo.SetIsPGO(true); 141 if (needUpdateCompile) { 142 // in deopt, we need to push the first un-marked method which is 143 // in upper layer of the deopt method's define chain (or maybe the deopt method itself) 144 // into the sharedCompiledQueue to trigger the compilation of the deopt method. 145 if (methodOffset != mainMethodOffset) { 146 // few methods which have the same bytecodes as other method can't find its outter method 147 if (outMethodOffset == MethodInfo::DEFAULT_OUTMETHOD_OFFSET) { 148 compileQueue_.push(methodOffset); 149 return; 150 } 151 // currentMethod whose outtermethod has been marked as pgo need to push into queue 152 auto outMethodInfo = methodList.at(outMethodOffset); 153 if (outMethodInfo.IsPGO()) { 154 compileQueue_.push(methodOffset); 155 } 156 } else { 157 // if current searched method is an un-marked main method, just push it to compile queue 158 compileQueue_.push(methodOffset); 159 } 160 } 161 if (methodOffset == mainMethodOffset) { 162 return; 163 } 164 EntityId outMethod(outMethodOffset); 165 dfs(outMethod, needUpdateCompile); 166 }; 167 168 // search as link list, a one-way define chain 169 for (auto pgoMethod = methodSet.begin(); pgoMethod != methodSet.end(); pgoMethod++) { 170 dfs(*pgoMethod, needUpdateCompile); 171 } 172 } 173 174 bool FilterMethod(const CString &recordName, const MethodLiteral *methodLiteral, 175 const MethodPcInfo &methodPCInfo) const; 176 177 JSPandaFile *jsPandaFile_ {nullptr}; 178 PGOProfilerLoader &pfLoader_; 179 BCInfo &bytecodeInfo_; 180 std::queue<uint32_t> compileQueue_ {}; 181 }; 182 } // namespace panda::ecmascript::kungfu 183 #endif // ECMASCRIPT_COMPILER_COMPILATION_DRIVER_H 184