• 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 #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