• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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     ~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(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(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         cb(bytecodeInfo_.GetRecordName(index), methodName, methodLiteral, methodOffset,
90             methodPcInfo, methodInfo, module);
91         if (methodInfo.IsTypeInferAbort()) {
92             return;
93         }
94         IncCompiledMethod();
95         CompileModuleThenDestroyIfNeeded();
96     }
97 
98     template <class Callback>
Run(const Callback & cb)99     void Run(const Callback &cb)
100     {
101         UpdatePGO();
102         InitializeCompileQueue();
103         uint32_t index = 0;
104         auto &methodList = bytecodeInfo_.GetMethodList();
105         const auto &methodPcInfos = bytecodeInfo_.GetMethodPcInfos();
106         while (!compileQueue_.empty()) {
107             std::queue<uint32_t> methodCompiledOrder;
108             methodCompiledOrder.push(compileQueue_.front());
109             compileQueue_.pop_front();
110             while (!methodCompiledOrder.empty()) {
111                 auto compilingMethod = methodCompiledOrder.front();
112                 methodCompiledOrder.pop();
113                 bytecodeInfo_.AddMethodOffsetToRecordName(compilingMethod, bytecodeInfo_.GetRecordName(index));
114                 auto &methodInfo = methodList.at(compilingMethod);
115                 auto &methodPcInfo = methodPcInfos[methodInfo.GetMethodPcInfoIndex()];
116                 auto methodLiteral = jsPandaFile_->FindMethodLiteral(compilingMethod);
117                 const std::string methodName(MethodLiteral::GetMethodName(jsPandaFile_, methodLiteral->GetMethodId()));
118                 if (FilterMethod(bytecodeInfo_.GetRecordName(index), methodLiteral, methodPcInfo, methodName)) {
119                     bytecodeInfo_.AddSkippedMethod(compilingMethod);
120                 } else {
121                     if (!methodInfo.IsCompiled()) {
122                         methodInfo.SetIsCompiled(true);
123                         CompileMethod(cb, index, methodName, methodLiteral, compilingMethod, methodPcInfo, methodInfo);
124                     } else if (NeedSecondaryCompile(methodInfo)) {
125                         // if a method used to be not full compiled but now it's a deopt resolved method
126                         // it should be full compiled again
127                         CompileMethod(cb, index, methodName, methodLiteral, compilingMethod, methodPcInfo, methodInfo);
128                     }
129                 }
130                 auto &innerMethods = methodInfo.GetInnerMethods();
131                 for (auto it : innerMethods) {
132                     methodCompiledOrder.push(it);
133                 }
134             }
135             index++;
136         }
137         CompileLastModuleThenDestroyIfNeeded();
138     }
139 
140     void FetchPGOMismatchResult();
141 
AddResolvedMethod(const CString & recordName,uint32_t classLiteralOffset)142     void AddResolvedMethod(const CString &recordName, uint32_t classLiteralOffset)
143     {
144         if (!IsPGOLoaded() || !bytecodeInfo_.HasClassDefMethod(classLiteralOffset)) {
145             return;
146         }
147         uint32_t resolvedMethod = bytecodeInfo_.GetDefineMethod(classLiteralOffset);
148         auto &methodList = bytecodeInfo_.GetMethodList();
149         auto &methodInfo = methodList.at(resolvedMethod);
150         methodInfo.SetResolvedMethod(true);
151         panda_file::File::EntityId resolvedMethodId(resolvedMethod);
152         UpdateCompileQueue(recordName, resolvedMethodId);
153     }
154 private:
155     // add maxMethodsInModule_ functions in a module and when a module is
156     // full(maxMethodsInModule_ functions have been put into) or the module is the last module,
157     // compile it and the destroy it.
158     Module *GetModule();
159 
160     void IncCompiledMethod();
161 
162     bool IsCurModuleFull() const;
163 
164     void CompileModuleThenDestroyIfNeeded();
165 
166     void CompileLastModuleThenDestroyIfNeeded();
167 
168     void TopologicalSortForRecords();
169 
170     void UpdatePGO();
171 
172     void InitializeCompileQueue();
173 
NeedSecondaryCompile(const MethodInfo & methodInfo)174     bool NeedSecondaryCompile(const MethodInfo &methodInfo) const
175     {
176         return methodInfo.IsTypeInferAbort() && methodInfo.IsResolvedMethod();
177     }
178 
AddDependList(const CString & recordName,uint32_t methodOffset,std::deque<CString> & dependList)179     void AddDependList(const CString &recordName, uint32_t methodOffset,
180                        std::deque<CString> &dependList)
181     {
182         auto &methodList = bytecodeInfo_.GetMethodList();
183         const auto &importRecordInfos = bytecodeInfo_.GetImportRecordsInfos();
184         auto iter = importRecordInfos.find(recordName);
185         // if the resolved method don't have import records need-inferred, just return.
186         if (iter == importRecordInfos.end()) {
187             return;
188         }
189         auto &methodInfo = methodList.at(methodOffset);
190         // Get the import indexs collected in methodInfo.
191         auto &importIndexs = methodInfo.GetImportIndexes();
192         // idInRecord is a map like "importIndex: (exportRecord: exportIndex)".
193         const auto &idInRecord = iter->second.GetImportIdToExportRecord();
194         for (auto index : importIndexs) {
195             auto it = idInRecord.find(index);
196             if (it != idInRecord.end()) {
197                 dependList.emplace_back(it->second.first);
198             }
199         }
200     }
201 
VerifyAndMarkCurMethod(uint32_t methodOffset,std::unordered_set<EntityId> & newMethodSet)202     bool VerifyAndMarkCurMethod(uint32_t methodOffset, std::unordered_set<EntityId> &newMethodSet)
203     {
204         // if current method is at the boundary state, we should stop the define chain search.
205         if (methodOffset == MethodInfo::DEFAULT_OUTMETHOD_OFFSET) {
206             return false;
207         }
208         // if pgo profile is not matched with current abc file, methodOffset will be different.
209         auto &methodList = bytecodeInfo_.GetMethodList();
210         auto iter = methodList.find(methodOffset);
211         if (iter == methodList.end()) {
212             LOG_COMPILER(ERROR) << "The correct aot profile has not been used";
213             return false;
214         }
215         auto &methodInfo = iter->second;
216         // if current method has already been marked as PGO, stop searching upper layer of the define chain.
217         if (methodInfo.IsPGO() && !methodInfo.IsTypeInferAbort()) {
218             return false;
219         }
220         // we need to collect these new-marked PGO methods to update PGO profile
221         panda_file::File::EntityId methodId(methodOffset);
222         newMethodSet.insert(std::move(methodId));
223         methodInfo.SetIsPGO(true);
224         return true;
225     }
226 
UpdateResolveDepends(std::vector<CString> & dependNames,bool needUpdate)227     void UpdateResolveDepends(std::vector<CString> &dependNames, bool needUpdate)
228     {
229         if (needUpdate && !dependNames.empty()) {
230             // depend methods should keep the topological sorting rule
231             std::sort(dependNames.begin(), dependNames.end(), [this](auto first, auto second) {
232                 return sortedRecords_.at(first) < sortedRecords_.at(second);
233             });
234             auto resolvedMethod = compileQueue_.back();
235             compileQueue_.pop_back();
236             // it should be like "depended main method1, depended main method2, resolved method" in compileQueue.
237             for (const auto &ele : dependNames) {
238                 bytecodeInfo_.AddRecordName(ele);
239                 auto eleOffset = jsPandaFile_->GetMainMethodIndex(ele);
240                 // these methods will be added to compile queue
241                 bytecodeInfo_.EraseSkippedMethod(eleOffset);
242                 compileQueue_.push_back(eleOffset);
243             }
244             compileQueue_.push_back(resolvedMethod);
245         }
246     }
247 
SearchForCompilation(const CString & recordName,const std::unordered_set<EntityId> & methodSet,std::unordered_set<EntityId> & newMethodSet,uint32_t mainMethodOffset,bool needUpdateCompile)248     void SearchForCompilation(const CString &recordName, const std::unordered_set<EntityId> &methodSet,
249                               std::unordered_set<EntityId> &newMethodSet,
250                               uint32_t mainMethodOffset, bool needUpdateCompile)
251     {
252         auto &methodList = bytecodeInfo_.GetMethodList();
253         std::unordered_set<EntityId> mainMethodSet;
254         auto getMainMethodSet = [this, &mainMethodSet](const CString &importRecord,
255             [[maybe_unused]] const std::unordered_set<EntityId> &oldIds) -> std::unordered_set<EntityId>& {
256             mainMethodSet.clear();
257             auto mainMethodOffset = jsPandaFile_->GetMainMethodIndex(importRecord);
258             panda_file::File::EntityId mainMethodId(mainMethodOffset);
259             mainMethodSet.insert(mainMethodId);
260             return mainMethodSet;
261         };
262 
263         std::vector<CString> importNames{};
264         std::function<void(EntityId)> importBfs =
265             [this, &methodList, &recordName, &importNames, &getMainMethodSet]
266             (EntityId methodId) -> void {
267             uint32_t methodOffset = methodId.GetOffset();
268             std::deque<CString> importList{};
269             AddDependList(recordName, methodOffset, importList);
270             while (!importList.empty()) {
271                 auto importRecord = importList.front();
272                 importList.pop_front();
273                 // export syntax only exists in main method, so just judge and collect the main method.
274                 auto mainMethodOffset = jsPandaFile_->GetMainMethodIndex(importRecord);
275                 auto &mainMethodInfo = methodList.at(mainMethodOffset);
276                 // mark the main method in other record as PGO.
277                 if (mainMethodInfo.IsPGO()) {
278                     continue;
279                 }
280                 importNames.emplace_back(importRecord);
281                 mainMethodInfo.SetIsPGO(true);
282                 pfDecoder_.Update(importRecord, getMainMethodSet);
283                 AddDependList(importRecord, mainMethodOffset, importList);
284             }
285         };
286 
287         std::function<void(EntityId, bool)> dfs =
288             [this, &newMethodSet, &mainMethodOffset, &dfs, &methodList, &importBfs]
289             (EntityId methodId, bool needUpdateCompile) -> void {
290             uint32_t methodOffset = methodId.GetOffset();
291             // verify whether we should stop the search for pgo methods or resolve methods.
292             if (!VerifyAndMarkCurMethod(methodOffset, newMethodSet)) {
293                 return;
294             }
295             auto &methodInfo = methodList.at(methodOffset);
296             auto outMethodOffset = methodInfo.GetOutMethodOffset();
297             // for pgo method, collect its import depends method
298             importBfs(methodId);
299             if (needUpdateCompile) {
300                 // in deopt, we need to push the first un-marked method which is
301                 // in upper layer of the deopt method's define chain (or maybe the deopt method itself)
302                 // into the sharedCompiledQueue to trigger the compilation of the deopt method.
303                 if (methodOffset != mainMethodOffset) {
304                     // few methods which have the same bytecodes as other method can't find its outter method
305                     if (outMethodOffset == MethodInfo::DEFAULT_OUTMETHOD_OFFSET) {
306                         compileQueue_.push_back(methodOffset);
307                         return;
308                     }
309                     // currentMethod whose outtermethod has been marked as pgo need to push into queue
310                     auto outMethodInfo = methodList.at(outMethodOffset);
311                     if (outMethodInfo.IsPGO()) {
312                         compileQueue_.push_back(methodOffset);
313                         return;
314                     }
315                 } else {
316                     // if current searched method is an un-marked main method, just push it to compile queue
317                     compileQueue_.push_back(methodOffset);
318                 }
319             }
320             if (methodOffset == mainMethodOffset) {
321                 return;
322             }
323             EntityId outMethod(outMethodOffset);
324             dfs(outMethod, needUpdateCompile);
325         };
326 
327         // search as link list, a one-way define chain
328         for (auto pgoMethod = methodSet.begin(); pgoMethod != methodSet.end(); pgoMethod++) {
329             dfs(*pgoMethod, needUpdateCompile);
330         }
331         // update compile queue only for resolve method when it depends on other record
332         UpdateResolveDepends(importNames, needUpdateCompile);
333     }
334 
335     bool FilterMethod(const CString &recordName, const MethodLiteral *methodLiteral,
336                       const MethodPcInfo &methodPCInfo, const std::string &methodName) const;
337 
338     std::vector<std::string> SplitString(const std::string &str, const char ch) const;
339 
340     void ParseOption(const std::string &option, std::map<std::string, std::vector<std::string>> &optionMap) const;
341 
342     bool FilterOption(const std::map<std::string, std::vector<std::string>> &optionMap, const std::string &recordName,
343                       const std::string &methodName) const;
344 
345     EcmaVM *vm_ {nullptr};
346     const JSPandaFile *jsPandaFile_ {nullptr};
347     PGOProfilerDecoder &pfDecoder_;
348     BCInfo &bytecodeInfo_;
349     std::deque<uint32_t> compileQueue_ {};
350     std::map<CString, uint32_t> sortedRecords_ {};
351     std::map<std::string, std::vector<std::string>> optionSelectMethods_ {};
352     std::map<std::string, std::vector<std::string>> optionSkipMethods_ {};
353     uint32_t compiledMethodCnt_ {0};
354     AOTFileGenerator *fileGenerator_ {nullptr};
355     std::string fileName_ {};
356     std::string triple_ {};
357     LOptions *lOptions_ {nullptr};
358     CompilerLog *log_ {nullptr};
359     bool outputAsm_ {false};
360     size_t maxMethodsInModule_ {0};
361 };
362 } // namespace panda::ecmascript::kungfu
363 #endif  // ECMASCRIPT_COMPILER_COMPILATION_DRIVER_H
364