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