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