• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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