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