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