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