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 #include "ecmascript/compiler/compilation_driver.h"
17 #include "ecmascript/compiler/file_generators.h"
18 #include "ecmascript/jspandafile/method_literal.h"
19 #include "ecmascript/ts_types/ts_manager.h"
20
21 namespace panda::ecmascript::kungfu {
CompilationDriver(PGOProfilerDecoder & profilerDecoder,BytecodeInfoCollector * collector,const std::string & optionSelectMethods,const std::string & optionSkipMethods,AOTFileGenerator * fileGenerator,const std::string & fileName,const std::string & triple,LOptions * lOptions,CompilerLog * log,bool outputAsm,size_t maxMethodsInModule)22 CompilationDriver::CompilationDriver(PGOProfilerDecoder &profilerDecoder,
23 BytecodeInfoCollector *collector,
24 const std::string &optionSelectMethods,
25 const std::string &optionSkipMethods,
26 AOTFileGenerator *fileGenerator,
27 const std::string &fileName,
28 const std::string &triple,
29 LOptions *lOptions,
30 CompilerLog *log,
31 bool outputAsm,
32 size_t maxMethodsInModule)
33 : vm_(collector->GetVM()),
34 jsPandaFile_(collector->GetJSPandaFile()),
35 pfDecoder_(profilerDecoder),
36 collector_(collector),
37 bytecodeInfo_(collector->GetBytecodeInfo()),
38 fileGenerator_(fileGenerator),
39 fileName_(fileName),
40 triple_(triple),
41 lOptions_(lOptions),
42 log_(log),
43 outputAsm_(outputAsm),
44 maxMethodsInModule_(maxMethodsInModule)
45 {
46 vm_->GetJSThread()->GetCurrentEcmaContext()->GetTSManager()->SetCompilationDriver(this);
47
48 if (!optionSelectMethods.empty() && !optionSkipMethods.empty()) {
49 LOG_COMPILER(FATAL) <<
50 "--compiler-select-methods and --compiler-skip-methods should not be set at the same time";
51 }
52
53 if (!optionSelectMethods.empty()) {
54 ParseOption(optionSelectMethods, optionSelectMethods_);
55 }
56
57 if (!optionSkipMethods.empty()) {
58 ParseOption(optionSkipMethods, optionSkipMethods_);
59 }
60 }
61
~CompilationDriver()62 CompilationDriver::~CompilationDriver()
63 {
64 vm_->GetJSThread()->GetCurrentEcmaContext()->GetTSManager()->SetCompilationDriver(nullptr);
65 }
66
GetModule()67 Module *CompilationDriver::GetModule()
68 {
69 return IsCurModuleFull() ? fileGenerator_->AddModule(fileName_, triple_, *lOptions_, outputAsm_)
70 : fileGenerator_->GetLatestModule();
71 }
72
IncCompiledMethod()73 void CompilationDriver::IncCompiledMethod()
74 {
75 compiledMethodCnt_++;
76 }
77
IsCurModuleFull() const78 bool CompilationDriver::IsCurModuleFull() const
79 {
80 return (compiledMethodCnt_ % maxMethodsInModule_ == 0);
81 }
82
CompileModuleThenDestroyIfNeeded()83 void CompilationDriver::CompileModuleThenDestroyIfNeeded()
84 {
85 if (IsCurModuleFull()) {
86 fileGenerator_->CompileLatestModuleThenDestroy();
87 }
88 }
89
CompileLastModuleThenDestroyIfNeeded()90 void CompilationDriver::CompileLastModuleThenDestroyIfNeeded()
91 {
92 if (!IsCurModuleFull()) {
93 fileGenerator_->CompileLatestModuleThenDestroy();
94 }
95 }
96
TopologicalSortForRecords()97 void CompilationDriver::TopologicalSortForRecords()
98 {
99 const auto &importRecordsInfos = bytecodeInfo_.GetImportRecordsInfos();
100 std::queue<CString> recordList;
101 std::unordered_map<CString, uint32_t> recordInDegree;
102 std::unordered_map<CString, std::vector<CString>> exportRecords;
103 std::vector<CString> &tpOrder = bytecodeInfo_.GetRecordNames();
104 for (auto &record : tpOrder) {
105 auto iter = importRecordsInfos.find(record);
106 if (iter == importRecordsInfos.end()) {
107 recordInDegree.emplace(record, 0);
108 recordList.emplace(record);
109 } else {
110 recordInDegree.emplace(record, iter->second.GetImportRecordSize());
111 }
112 }
113 tpOrder.clear();
114
115 for (auto iter = importRecordsInfos.begin(); iter != importRecordsInfos.end(); iter++) {
116 const auto &importRecords = iter->second.GetImportRecords();
117 for (const auto &import : importRecords) {
118 if (exportRecords.find(import) != exportRecords.end()) {
119 exportRecords[import].emplace_back(iter->first);
120 } else {
121 exportRecords.emplace(import, std::vector<CString>{iter->first});
122 }
123 }
124 }
125
126 while (!recordList.empty()) {
127 auto curRecord = recordList.front();
128 tpOrder.emplace_back(curRecord);
129 recordList.pop();
130 auto iter = exportRecords.find(curRecord);
131 if (iter != exportRecords.end()) {
132 for (const auto &ele : iter->second) {
133 if (recordInDegree[ele] > 0 && --recordInDegree[ele] == 0) {
134 recordList.emplace(ele);
135 }
136 }
137 }
138 }
139
140 if (UNLIKELY(tpOrder.size() != recordInDegree.size())) {
141 LOG_COMPILER(INFO) << "There are circular references in records";
142 for (auto &it : recordInDegree) {
143 if (it.second != 0) {
144 tpOrder.emplace_back(it.first);
145 }
146 }
147 ASSERT(tpOrder.size() == recordInDegree.size());
148 }
149 auto &mainMethods = bytecodeInfo_.GetMainMethodIndexes();
150 auto sortId = 0;
151 for (auto &it : tpOrder) {
152 mainMethods.emplace_back(jsPandaFile_->GetMainMethodIndex(it));
153 sortedRecords_.emplace(it, sortId++);
154 }
155 ASSERT(tpOrder.size() == mainMethods.size());
156 }
157
FetchPGOMismatchResult()158 void CompilationDriver::FetchPGOMismatchResult()
159 {
160 ASSERT(log_ != nullptr);
161 uint32_t totalMethodCount = 0;
162 uint32_t mismatchMethodCount = 0;
163 std::set<std::pair<std::string, CString>> mismatchMethodSet {};
164 pfDecoder_.GetMismatchResult(jsPandaFile_, totalMethodCount, mismatchMethodCount, mismatchMethodSet);
165 log_->SetPGOMismatchResult(totalMethodCount, mismatchMethodCount, mismatchMethodSet);
166 }
167
UpdatePGO()168 void CompilationDriver::UpdatePGO()
169 {
170 std::unordered_set<EntityId> newMethodIds;
171 auto dfs = [this, &newMethodIds] (const CString &recordName,
172 const std::unordered_set<EntityId> &oldIds) -> std::unordered_set<EntityId> & {
173 newMethodIds.clear();
174 if (!jsPandaFile_->HasTSTypes(recordName)) {
175 return newMethodIds;
176 }
177 uint32_t mainMethodOffset = jsPandaFile_->GetMainMethodIndex(recordName);
178 SearchForCompilation(recordName, oldIds, newMethodIds, mainMethodOffset, false);
179 return newMethodIds;
180 };
181 pfDecoder_.Update(jsPandaFile_, dfs);
182 FetchPGOMismatchResult();
183 }
184
InitializeCompileQueue()185 void CompilationDriver::InitializeCompileQueue()
186 {
187 TopologicalSortForRecords();
188 auto &mainMethodIndexes = bytecodeInfo_.GetMainMethodIndexes();
189 for (auto mainMethodIndex : mainMethodIndexes) {
190 compileQueue_.push_back(mainMethodIndex);
191 }
192 }
193
FilterMethod(const CString & recordName,const MethodLiteral * methodLiteral,const MethodPcInfo & methodPCInfo,const std::string & methodName) const194 bool CompilationDriver::FilterMethod(const CString &recordName, const MethodLiteral *methodLiteral,
195 const MethodPcInfo &methodPCInfo, const std::string &methodName) const
196 {
197 if (methodPCInfo.methodsSize > bytecodeInfo_.GetMaxMethodSize() ||
198 !pfDecoder_.Match(jsPandaFile_, recordName, methodLiteral->GetMethodId())) {
199 return true;
200 }
201
202 if (!optionSelectMethods_.empty()) {
203 return !FilterOption(optionSelectMethods_, ConvertToStdString(recordName), methodName);
204 } else if (!optionSkipMethods_.empty()) {
205 return FilterOption(optionSkipMethods_, ConvertToStdString(recordName), methodName);
206 }
207
208 return false;
209 }
210
SplitString(const std::string & str,const char ch) const211 std::vector<std::string> CompilationDriver::SplitString(const std::string &str, const char ch) const
212 {
213 std::vector<std::string> vec {};
214 std::istringstream sstr(str.c_str());
215 std::string split;
216 while (getline(sstr, split, ch)) {
217 vec.emplace_back(split);
218 }
219 return vec;
220 }
221
ParseOption(const std::string & option,std::map<std::string,std::vector<std::string>> & optionMap) const222 void CompilationDriver::ParseOption(const std::string &option,
223 std::map<std::string, std::vector<std::string>> &optionMap) const
224 {
225 const char colon = ':';
226 const char comma = ',';
227 std::string str = option;
228 size_t posColon = 0;
229 size_t posComma = 0;
230 do {
231 posColon = str.find_last_of(colon);
232 std::string methodNameList = str.substr(posColon + 1, str.size());
233 std::vector<std::string> methodNameVec = SplitString(methodNameList, comma);
234 str = str.substr(0, posColon);
235 posComma = str.find_last_of(comma);
236 std::string recordName = str.substr(posComma + 1, str.size());
237 str = str.substr(0, posComma);
238 optionMap[recordName] = methodNameVec;
239 } while (posComma != std::string::npos);
240 }
241
FilterOption(const std::map<std::string,std::vector<std::string>> & optionMap,const std::string & recordName,const std::string & methodName) const242 bool CompilationDriver::FilterOption(const std::map<std::string, std::vector<std::string>> &optionMap,
243 const std::string &recordName, const std::string &methodName) const
244 {
245 if (optionMap.empty()) {
246 return false;
247 }
248
249 auto it = optionMap.find(recordName);
250 if (it == optionMap.end()) {
251 return false;
252 }
253
254 std::vector<std::string> vec = it->second;
255 return find(vec.begin(), vec.end(), methodName) != vec.end();
256 }
257
SetCurrentConstantPool(uint32_t methodOffset) const258 void CompilationDriver::SetCurrentConstantPool(uint32_t methodOffset) const
259 {
260 PGOTypeManager *ptManager = vm_->GetJSThread()->GetCurrentEcmaContext()->GetPTManager();
261 ptManager->SetCurConstantPool(jsPandaFile_, methodOffset);
262 }
263
StoreConstantPoolInfo() const264 void CompilationDriver::StoreConstantPoolInfo() const
265 {
266 auto &methodList = bytecodeInfo_.GetMethodList();
267 for (auto &x : methodList) {
268 if (!bytecodeInfo_.FindMethodOffsetToRecordName(x.first)) {
269 bytecodeInfo_.AddSkippedMethod(x.first);
270 }
271 }
272 PGOTypeManager *ptManager = vm_->GetJSThread()->GetCurrentEcmaContext()->GetPTManager();
273 ptManager->GetAOTSnapshot().StoreConstantPoolInfo(collector_);
274 }
275
RunCg()276 bool JitCompilationDriver::RunCg()
277 {
278 IncCompiledMethod();
279 CompileModuleThenDestroyIfNeeded();
280 return true;
281 }
282
GetModule()283 Module *JitCompilationDriver::GetModule()
284 {
285 return IsCurModuleFull() ? fileGenerator_->AddModule(fileName_, triple_, *lOptions_, outputAsm_, true) :
286 fileGenerator_->GetLatestModule();
287 }
288 } // namespace panda::ecmascript::kungfu
289