1 /**
2 * Copyright (c) 2021-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 <abc2program/program_dump.h>
17 #include <mem/pool_manager.h>
18 #include "utils/timers.h"
19
20 #include <emitFiles.h>
21 #include <protobufSnapshotGenerator.h>
22 #include <resolveDepsRelation.h>
23 #include <util/commonUtil.h>
24 #include <util/dumper.h>
25 #include <util/moduleHelpers.h>
26
27 namespace panda::es2panda::aot {
28 using mem::MemConfig;
29 class MemManager {
30 public:
MemManager()31 explicit MemManager()
32 {
33 constexpr auto COMPILER_SIZE = 8192_MB;
34
35 MemConfig::Initialize(0, 0, COMPILER_SIZE, 0);
36 PoolManager::Initialize(PoolType::MMAP);
37 }
38
39 NO_COPY_SEMANTIC(MemManager);
40 NO_MOVE_SEMANTIC(MemManager);
41
~MemManager()42 ~MemManager()
43 {
44 PoolManager::Finalize();
45 MemConfig::Finalize();
46 }
47 };
48
GenerateBase64Output(panda::pandasm::Program * prog,const std::unique_ptr<panda::es2panda::aot::Options> & options)49 static void GenerateBase64Output(panda::pandasm::Program *prog,
50 const std::unique_ptr<panda::es2panda::aot::Options> &options)
51 {
52 auto pandaFile = panda::pandasm::AsmEmitter::Emit(*prog, nullptr, options->CompilerOptions().targetApiVersion,
53 options->CompilerOptions().targetApiSubVersion);
54 const uint8_t *buffer = pandaFile->GetBase();
55 size_t size = pandaFile->GetPtr().GetSize();
56 std::string content(reinterpret_cast<const char*>(buffer), size);
57 std::string base64Output = util::Base64Encode(content);
58 std::cout << base64Output << std::endl;
59 }
60
DumpPandaFileSizeStatistic(std::map<std::string,size_t> & stat)61 static void DumpPandaFileSizeStatistic(std::map<std::string, size_t> &stat)
62 {
63 size_t totalSize = 0;
64 std::cout << "Panda file size statistic:" << std::endl;
65 constexpr std::array<std::string_view, 2> INFO_STATS = {"instructions_number", "codesize"};
66
67 for (const auto &[name, size] : stat) {
68 if (find(INFO_STATS.begin(), INFO_STATS.end(), name) != INFO_STATS.end()) {
69 continue;
70 }
71 std::cout << name << " section: " << size << std::endl;
72 totalSize += size;
73 }
74
75 for (const auto &name : INFO_STATS) {
76 std::cout << name << ": " << stat.at(std::string(name)) << std::endl;
77 }
78
79 std::cout << "total: " << totalSize << std::endl;
80 }
81
DumpPandaFileSizePctStatistic(std::map<std::string,size_t> & stat)82 static void DumpPandaFileSizePctStatistic(std::map<std::string, size_t> &stat)
83 {
84 size_t totalSize = 0;
85 std::cout << "Panda file size statistic:" << std::endl;
86 constexpr std::array<std::string_view, 2> INFO_STATS = {"instructions_number", "codesize"};
87
88 for (const auto &[name, size] : stat) {
89 if (find(INFO_STATS.begin(), INFO_STATS.end(), name) != INFO_STATS.end()) {
90 continue;
91 }
92 totalSize += size;
93 }
94
95 const int itemWidth = 32; // The width of each item name is 32 bit. (item name is a data structure in abc file)
96 const int sectionWidth = 8; // The width of each section size is 8 bit. (section size is the size of each item)
97 const int precision = 2; // Control the output precision of the size percentage.
98 const double percentFlag = 100.0f; // Conversion of percentage output.
99 for (const auto &[name, size] : stat) {
100 if (find(INFO_STATS.begin(), INFO_STATS.end(), name) != INFO_STATS.end()) {
101 continue;
102 }
103 std::cout << std::left << std::setw(itemWidth) << name << " section: " << \
104 std::setw(sectionWidth) << size << ", percent: " << \
105 std::fixed << std::setprecision(precision) << (size * percentFlag / totalSize) << "%" << std::endl;
106 }
107
108 std::cout << "total: " << totalSize << std::endl;
109 }
110
GenerateProgramsByWorkers(const std::map<std::string,panda::es2panda::util::ProgramCache * > & programsInfo,const std::unique_ptr<panda::es2panda::aot::Options> & options,std::map<std::string,size_t> * statp)111 static bool GenerateProgramsByWorkers(const std::map<std::string, panda::es2panda::util::ProgramCache*> &programsInfo,
112 const std::unique_ptr<panda::es2panda::aot::Options> &options, std::map<std::string, size_t> *statp)
113 {
114 auto queue = new panda::es2panda::aot::EmitFileQueue(options, statp, programsInfo);
115
116 bool emitResult = true;
117 try {
118 queue->Schedule();
119 queue->Consume();
120 queue->Wait();
121 } catch (const class Error &e) {
122 emitResult = false;
123 std::cerr << e.Message() << std::endl;
124 }
125
126 delete queue;
127 queue = nullptr;
128
129 return emitResult;
130 }
131
DumpProgramInfos(const std::map<std::string,panda::es2panda::util::ProgramCache * > & programsInfo,const std::unique_ptr<panda::es2panda::aot::Options> & options)132 static void DumpProgramInfos(const std::map<std::string, panda::es2panda::util::ProgramCache*> &programsInfo,
133 const std::unique_ptr<panda::es2panda::aot::Options> &options)
134 {
135 const es2panda::CompilerOptions &compilerOptions = options->CompilerOptions();
136 if (compilerOptions.dumpAsm || compilerOptions.dumpLiteralBuffer || compilerOptions.dumpAsmProgram ||
137 compilerOptions.dumpNormalizedAsmProgram) {
138 for (const auto &progInfo : programsInfo) {
139 if (compilerOptions.dumpAsmProgram || compilerOptions.dumpNormalizedAsmProgram) {
140 panda::abc2program::PandasmProgramDumper dumper(compilerOptions.dumpNormalizedAsmProgram,
141 compilerOptions.isDebug);
142 dumper.Dump(std::cout, progInfo.second->program);
143 }
144
145 if (compilerOptions.dumpAsm) {
146 es2panda::Compiler::DumpAsm(&(progInfo.second->program));
147 }
148
149 if (compilerOptions.dumpLiteralBuffer) {
150 panda::es2panda::util::Dumper::DumpLiterals(progInfo.second->program.literalarray_table);
151 }
152
153 if (compilerOptions.dumpString) {
154 panda::es2panda::util::Dumper::DumpStrings(progInfo.second->program.strings);
155 }
156 }
157 }
158 }
159
GenerateProgram(std::map<std::string,panda::es2panda::util::ProgramCache * > & programsInfo,const std::unique_ptr<panda::es2panda::aot::Options> & options,const std::map<std::string,std::unordered_set<std::string>> & resolvedDepsRelation)160 static bool GenerateProgram(std::map<std::string, panda::es2panda::util::ProgramCache*> &programsInfo,
161 const std::unique_ptr<panda::es2panda::aot::Options> &options,
162 const std::map<std::string, std::unordered_set<std::string>> &resolvedDepsRelation)
163 {
164 DumpProgramInfos(programsInfo, options);
165
166 if (programsInfo.size() == 1) {
167 auto *prog = &(programsInfo.begin()->second->program);
168 if (options->OutputFiles().empty() && options->CompilerOutput().empty()) {
169 GenerateBase64Output(prog, options);
170 return true;
171 }
172
173 // Disable generating cached files when cross-program optimization is required, to prevent cached files from
174 // not being invalidated when their dependencies are changed
175 if (options->CompilerProtoOutput().size() > 0 && !options->CompilerOptions().requireGlobalOptimization) {
176 panda::proto::ProtobufSnapshotGenerator::GenerateSnapshot(*prog, options->CompilerProtoOutput());
177 return true;
178 }
179 }
180
181 if (options->NeedRemoveRedundantRecord()) {
182 util::Helpers::RemoveProgramsRedundantData(programsInfo, resolvedDepsRelation);
183 DumpProgramInfos(programsInfo, options);
184 }
185
186 bool dumpSize = options->SizeStat();
187 bool dumpSizePct = options->SizePctStat();
188 std::map<std::string, size_t> stat;
189 std::map<std::string, size_t> *statp = (dumpSize || dumpSizePct) ? &stat : nullptr;
190
191 if (!GenerateProgramsByWorkers(programsInfo, options, statp)) {
192 return false;
193 }
194
195 if (dumpSize) {
196 DumpPandaFileSizeStatistic(stat);
197 }
198
199 if (dumpSizePct) {
200 DumpPandaFileSizePctStatistic(stat);
201 }
202
203 return true;
204 }
205
CheckMergeModeConsistency(bool mergeAbc,std::map<std::string,panda::es2panda::util::ProgramCache * > programsInfo)206 static bool CheckMergeModeConsistency(bool mergeAbc,
207 std::map<std::string, panda::es2panda::util::ProgramCache*> programsInfo)
208 {
209 std::unordered_map<std::string, std::unordered_set<std::string>> recordNameMap;
210
211 // Names of these program records generated from abc input all follow such format: abcName|recordName
212 for (auto &[name, _] : programsInfo) {
213 if (util::RecordNotGeneratedFromBytecode(name)) {
214 continue;
215 }
216
217 auto nameVec = util::Split(name, util::CHAR_VERTICAL_LINE);
218 auto abcFileName = nameVec[0];
219 auto recordName = nameVec.back();
220
221 if (mergeAbc) {
222 if (recordName == util::GLOBAL_TYPE_NAME) {
223 std::cerr << "Current compile mode is merge-abc, all input abc files must be merged mode. "
224 << "But file '" << abcFileName << "' is not a merged abc." << std::endl;
225 return false;
226 }
227 } else {
228 if (recordNameMap.find(abcFileName) != recordNameMap.end()) {
229 recordNameMap.find(abcFileName)->second.insert(recordName);
230 } else {
231 recordNameMap.insert({abcFileName, {recordName}});
232 }
233 }
234 }
235
236 if (!mergeAbc) {
237 for (auto &[abcFileName, recordNameSet] : recordNameMap) {
238 if (!recordNameSet.count(util::GLOBAL_TYPE_NAME)) {
239 std::cerr << "Current compile mode is non merge-abc, all input abc files must be unmerged mode. "
240 << "But file '" << abcFileName << "' is a merged abc." << std::endl;
241 return false;
242 }
243 }
244 }
245
246 return true;
247 }
248
GenerateAbcFiles(std::map<std::string,panda::es2panda::util::ProgramCache * > & programsInfo,const std::unique_ptr<panda::es2panda::aot::Options> & options,size_t expectedProgsCount,const std::map<std::string,std::unordered_set<std::string>> & resolvedDepsRelation)249 static bool GenerateAbcFiles(std::map<std::string, panda::es2panda::util::ProgramCache*> &programsInfo,
250 const std::unique_ptr<panda::es2panda::aot::Options> &options, size_t expectedProgsCount,
251 const std::map<std::string, std::unordered_set<std::string>> &resolvedDepsRelation)
252 {
253 if (programsInfo.size() != expectedProgsCount) {
254 std::cerr << "The size of programs is expected to be " << expectedProgsCount
255 << ", but is " << programsInfo.size() << std::endl;
256 return false;
257 }
258
259 if (!GenerateProgram(programsInfo, options, resolvedDepsRelation)) {
260 std::cerr << "GenerateProgram Failed!" << std::endl;
261 return false;
262 }
263
264 return true;
265 }
266
ResolveDepsRelations(const std::map<std::string,panda::es2panda::util::ProgramCache * > & programsInfo,const std::unique_ptr<panda::es2panda::aot::Options> & options,std::map<std::string,std::unordered_set<std::string>> & resolvedDepsRelation)267 static bool ResolveDepsRelations(const std::map<std::string, panda::es2panda::util::ProgramCache *> &programsInfo,
268 const std::unique_ptr<panda::es2panda::aot::Options> &options,
269 std::map<std::string, std::unordered_set<std::string>> &resolvedDepsRelation)
270 {
271 panda::es2panda::aot::DepsRelationResolver depsRelationResolver(programsInfo, options, resolvedDepsRelation);
272 return depsRelationResolver.Resolve();
273 }
274
ResolveAndGenerate(std::map<std::string,panda::es2panda::util::ProgramCache * > & programsInfo,const std::unique_ptr<panda::es2panda::aot::Options> & options)275 static bool ResolveAndGenerate(std::map<std::string, panda::es2panda::util::ProgramCache*> &programsInfo,
276 const std::unique_ptr<panda::es2panda::aot::Options> &options)
277 {
278 panda::Timer::timerStart(panda::EVENT_RESOLVE_DEPS, "");
279 // A mapping of program to its records which are resolved and collected as valid dependencies.
280 std::map<std::string, std::unordered_set<std::string>> resolvedDepsRelation {};
281
282 if (options->NeedCollectDepsRelation() &&
283 !ResolveDepsRelations(programsInfo, options, resolvedDepsRelation)) {
284 return true;
285 }
286 panda::Timer::timerEnd(panda::EVENT_RESOLVE_DEPS, "");
287
288 panda::Timer::timerStart(panda::EVENT_EMIT_ABC, "");
289 if (!GenerateAbcFiles(programsInfo, options, Compiler::GetExpectedProgsCount(), resolvedDepsRelation)) {
290 return true;
291 }
292 panda::Timer::timerEnd(panda::EVENT_EMIT_ABC, "");
293
294 return false;
295 }
296
Run(int argc,const char ** argv)297 int Run(int argc, const char **argv)
298 {
299 auto options = std::make_unique<Options>();
300 if (!options->Parse(argc, argv)) {
301 std::cerr << options->ErrorMsg() << std::endl;
302 return 1;
303 }
304
305 if (options->CompilerOptions().targetBcVersion) {
306 auto bcVersionByApi = panda::panda_file::GetVersionByApi(options->CompilerOptions().targetApiVersion,
307 options->CompilerOptions().targetApiSubVersion);
308 std::cout << panda::panda_file::GetVersion(bcVersionByApi.value());
309 return 0;
310 }
311
312 panda::Timer::timerStart(panda::EVENT_TOTAL, "");
313 if (options->CompilerOptions().bcVersion || options->CompilerOptions().bcMinVersion) {
314 std::string version = options->CompilerOptions().bcVersion ?
315 panda::panda_file::GetVersion(panda::panda_file::version) :
316 panda::panda_file::GetVersion(panda::panda_file::minVersion);
317 std::cout << version;
318 return 0;
319 }
320
321 std::map<std::string, panda::es2panda::util::ProgramCache*> programsInfo;
322 panda::ArenaAllocator allocator(panda::SpaceType::SPACE_TYPE_COMPILER, nullptr, true);
323
324 Compiler::SetExpectedProgsCount(options->CompilerOptions().sourceFiles.size());
325 panda::Timer::timerStart(panda::EVENT_COMPILE, "");
326 int ret = Compiler::CompileFiles(options->CompilerOptions(), programsInfo, &allocator);
327
328 if (!CheckMergeModeConsistency(options->CompilerOptions().mergeAbc, programsInfo)) {
329 return 1;
330 }
331
332 if (options->ParseOnly()) {
333 return ret;
334 }
335
336 if (!options->NpmModuleEntryList().empty()) {
337 es2panda::util::ModuleHelpers::CompileNpmModuleEntryList(options->NpmModuleEntryList(),
338 options->CompilerOptions(), programsInfo, &allocator);
339 Compiler::SetExpectedProgsCount(Compiler::GetExpectedProgsCount() + 1);
340 }
341 panda::Timer::timerEnd(panda::EVENT_COMPILE, "");
342
343 if (ResolveAndGenerate(programsInfo, options)) {
344 return 1;
345 }
346 panda::Timer::timerEnd(panda::EVENT_TOTAL, "");
347 if (!options->PerfFile().empty()) {
348 panda::Timer::PrintTimers();
349 }
350 return 0;
351 }
352 } // namespace panda::es2panda::aot
353
main(int argc,const char ** argv)354 int main(int argc, const char **argv)
355 {
356 panda::es2panda::aot::MemManager mm;
357 return panda::es2panda::aot::Run(argc, argv);
358 }
359