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