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
36namespace panda::es2panda::aot {
37using mem::MemConfig;
38class MemManager {
39public:
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
51    ~MemManager()
52    {
53        PoolManager::Finalize();
54        MemConfig::Finalize();
55    }
56};
57
58static 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
70static 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
91static 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
120static 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
141static 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
169static 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
215static 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
258static 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
276static 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
284static 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
306int 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
363int main(int argc, const char **argv)
364{
365    panda::es2panda::aot::MemManager mm;
366    return panda::es2panda::aot::Run(argc, argv);
367}
368