* Copyright (c) 2021-2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <program_dump.h>
#include <mem/pool_manager.h>
#include "utils/perfMetrics.h"
#include "utils/timers.h"
#include <emitFiles.h>
#include <protobufSnapshotGenerator.h>
#include <resolveDepsRelation.h>
#include <util/commonUtil.h>
#include <util/dumper.h>
#include <util/moduleHelpers.h>
#ifdef OHOS_PLATFORM
#include <malloc.h>
#endif
#if defined(PANDA_TARGET_OHOS) || defined(PANDA_TARGET_LINUX)
#include <sys/resource.h>
#endif
namespace panda::es2panda::aot {
using mem::MemConfig;
class MemManager {
public:
explicit MemManager()
{
constexpr auto COMPILER_SIZE = 8192_MB;
MemConfig::Initialize(0, 0, COMPILER_SIZE, 0);
PoolManager::Initialize(PoolType::MMAP);
}
NO_COPY_SEMANTIC(MemManager);
NO_MOVE_SEMANTIC(MemManager);
~MemManager()
{
PoolManager::Finalize();
MemConfig::Finalize();
}
};
static bool GenerateBase64Output(panda::pandasm::Program *prog,
const std::unique_ptr<panda::es2panda::aot::Options> &options)
{
auto pandaFile = panda::pandasm::AsmEmitter::Emit(*prog, nullptr, options->CompilerOptions().targetApiVersion,
options->CompilerOptions().targetApiSubVersion);
if (pandaFile == nullptr) {
std::cerr << "Error: Failed to emit program" << std::endl;
return false;
}
const uint8_t *buffer = pandaFile->GetBase();
size_t size = pandaFile->GetPtr().GetSize();
std::string content(reinterpret_cast<const char*>(buffer), size);
std::string base64Output = util::Base64Encode(content);
std::cout << base64Output << std::endl;
return true;
}
static void DumpPandaFileSizeStatistic(std::map<std::string, size_t> &stat)
{
size_t totalSize = 0;
std::cout << "Panda file size statistic:" << std::endl;
constexpr std::array<std::string_view, 2> INFO_STATS = {"instructions_number", "codesize"};
for (const auto &[name, size] : stat) {
if (find(INFO_STATS.begin(), INFO_STATS.end(), name) != INFO_STATS.end()) {
continue;
}
std::cout << name << " section: " << size << std::endl;
totalSize += size;
}
for (const auto &name : INFO_STATS) {
std::cout << name << ": " << stat.at(std::string(name)) << std::endl;
}
std::cout << "total: " << totalSize << std::endl;
}
static void DumpPandaFileSizePctStatistic(std::map<std::string, size_t> &stat)
{
size_t totalSize = 0;
std::cout << "Panda file size statistic:" << std::endl;
constexpr std::array<std::string_view, 2> INFO_STATS = {"instructions_number", "codesize"};
for (const auto &[name, size] : stat) {
if (find(INFO_STATS.begin(), INFO_STATS.end(), name) != INFO_STATS.end()) {
continue;
}
totalSize += size;
}
const int itemWidth = 32;
const int sectionWidth = 8;
const int precision = 2;
const double percentFlag = 100.0f;
for (const auto &[name, size] : stat) {
if (find(INFO_STATS.begin(), INFO_STATS.end(), name) != INFO_STATS.end()) {
continue;
}
std::cout << std::left << std::setw(itemWidth) << name << " section: " << \
std::setw(sectionWidth) << size << ", percent: " << \
std::fixed << std::setprecision(precision) << (size * percentFlag / totalSize) << "%" << std::endl;
}
std::cout << "total: " << totalSize << std::endl;
}
static bool 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)
{
auto queue = new panda::es2panda::aot::EmitFileQueue(options, statp, programsInfo);
bool emitResult = true;
try {
queue->Schedule();
queue->Consume();
queue->Wait();
} catch (const class Error &e) {
emitResult = false;
std::cerr << e.Message() << std::endl;
}
delete queue;
queue = nullptr;
if (options->CompilerOptions().mergeAbc) {
try {
EmitMergedAbcJob emitMergedAbcJob(options, programsInfo, statp);
emitMergedAbcJob.Run();
} catch (const class Error &e) {
emitResult = false;
std::cerr << "Error: Failed to emit merged abc: " << e.Message() << std::endl;
}
}
return emitResult;
}
static void DumpProgramInfos(const std::map<std::string, panda::es2panda::util::ProgramCache*> &programsInfo,
const std::unique_ptr<panda::es2panda::aot::Options> &options)
{
const es2panda::CompilerOptions &compilerOptions = options->CompilerOptions();
if (compilerOptions.dumpAsm || compilerOptions.dumpLiteralBuffer || compilerOptions.dumpAsmProgram ||
compilerOptions.dumpNormalizedAsmProgram) {
for (const auto &progInfo : programsInfo) {
if (compilerOptions.dumpAsmProgram || compilerOptions.dumpNormalizedAsmProgram) {
panda::abc2program::PandasmProgramDumper dumper(compilerOptions.dumpNormalizedAsmProgram,
compilerOptions.isDebug);
dumper.Dump(std::cout, progInfo.second->program);
}
if (compilerOptions.dumpAsm) {
es2panda::Compiler::DumpAsm(&(progInfo.second->program));
}
if (compilerOptions.dumpLiteralBuffer) {
panda::es2panda::util::Dumper::DumpLiterals(progInfo.second->program.literalarray_table);
}
if (compilerOptions.dumpString) {
panda::es2panda::util::Dumper::DumpStrings(progInfo.second->program.strings);
}
}
}
}
static bool 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)
{
DumpProgramInfos(programsInfo, options);
if (programsInfo.size() == 1) {
auto *prog = &(programsInfo.begin()->second->program);
if (options->OutputFiles().empty() && options->CompilerOutput().empty()) {
return GenerateBase64Output(prog, options);
}
if (options->CompilerProtoOutput().size() > 0 && !options->CompilerOptions().requireGlobalOptimization) {
panda::proto::ProtobufSnapshotGenerator::GenerateSnapshot(*prog, options->CompilerProtoOutput());
return true;
}
}
if (options->NeedRemoveRedundantRecord()) {
util::Helpers::RemoveProgramsRedundantData(programsInfo, resolvedDepsRelation);
DumpProgramInfos(programsInfo, options);
}
bool dumpSize = options->SizeStat();
bool dumpSizePct = options->SizePctStat();
std::map<std::string, size_t> stat;
std::map<std::string, size_t> *statp = (dumpSize || dumpSizePct) ? &stat : nullptr;
if (!GenerateProgramsByWorkers(programsInfo, options, statp)) {
return false;
}
if (dumpSize) {
DumpPandaFileSizeStatistic(stat);
}
if (dumpSizePct) {
DumpPandaFileSizePctStatistic(stat);
}
return true;
}
static bool CheckMergeModeConsistency(bool mergeAbc,
std::map<std::string, panda::es2panda::util::ProgramCache*> programsInfo)
{
std::unordered_map<std::string, std::unordered_set<std::string>> recordNameMap;
for (auto &[name, _] : programsInfo) {
if (util::RecordNotGeneratedFromBytecode(name)) {
continue;
}
auto nameVec = util::Split(name, util::CHAR_VERTICAL_LINE);
auto abcFileName = nameVec[0];
auto recordName = nameVec.back();
if (mergeAbc) {
if (recordName == util::GLOBAL_TYPE_NAME) {
std::cerr << "Current compile mode is merge-abc, all input abc files must be merged mode. "
<< "But file '" << abcFileName << "' is not a merged abc." << std::endl;
return false;
}
} else {
if (recordNameMap.find(abcFileName) != recordNameMap.end()) {
recordNameMap.find(abcFileName)->second.insert(recordName);
} else {
recordNameMap.insert({abcFileName, {recordName}});
}
}
}
if (!mergeAbc) {
for (auto &[abcFileName, recordNameSet] : recordNameMap) {
if (!recordNameSet.count(util::GLOBAL_TYPE_NAME)) {
std::cerr << "Current compile mode is non merge-abc, all input abc files must be unmerged mode. "
<< "But file '" << abcFileName << "' is a merged abc." << std::endl;
return false;
}
}
}
return true;
}
static bool 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)
{
if (programsInfo.size() != expectedProgsCount) {
std::cerr << "The size of programs is expected to be " << expectedProgsCount
<< ", but is " << programsInfo.size() << std::endl;
return false;
}
if (!GenerateProgram(programsInfo, options, resolvedDepsRelation)) {
std::cerr << "GenerateProgram Failed!" << std::endl;
return false;
}
return true;
}
static bool 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)
{
panda::es2panda::aot::DepsRelationResolver depsRelationResolver(programsInfo, options, resolvedDepsRelation);
return depsRelationResolver.Resolve();
}
static bool ResolveAndGenerate(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 {};
{
ES2ABC_PERF_SCOPE("@EVENT_RESOLVE_DEPS");
panda::Timer::timerStart(panda::EVENT_RESOLVE_DEPS, "");
if (options->NeedCollectDepsRelation() && !ResolveDepsRelations(programsInfo, options, resolvedDepsRelation)) {
return false;
}
panda::Timer::timerEnd(panda::EVENT_RESOLVE_DEPS, "");
}
{
ES2ABC_PERF_SCOPE("@EVENT_EMIT_ABC");
panda::Timer::timerStart(panda::EVENT_EMIT_ABC, "");
if (!GenerateAbcFiles(programsInfo, options, Compiler::GetExpectedProgsCount(), resolvedDepsRelation)) {
return false;
}
panda::Timer::timerEnd(panda::EVENT_EMIT_ABC, "");
}
return true;
}
static bool HandleBcVersion(const std::unique_ptr<panda::es2panda::aot::Options> &options)
{
if (options->CompilerOptions().targetBcVersion) {
auto bcVersionByApi = panda::panda_file::GetVersionByApi(options->CompilerOptions().targetApiVersion,
options->CompilerOptions().targetApiSubVersion);
std::cout << panda::panda_file::GetVersion(bcVersionByApi.value());
return true;
}
if (options->CompilerOptions().bcVersion || options->CompilerOptions().bcMinVersion) {
std::string version = options->CompilerOptions().bcVersion
? panda::panda_file::GetVersion(panda::panda_file::version)
: panda::panda_file::GetVersion(panda::panda_file::minVersion);
std::cout << version;
return true;
}
return false;
}
static void DumpPerfInfo(const std::unique_ptr<panda::es2panda::aot::Options> &options)
{
if (!options->PerfFile().empty()) {
panda::Timer::PrintTimers();
}
if (options->CompilerOptions().dumpPerfMetrics) {
panda::DumpPerfMetrics();
}
}
int Run(int argc, const char **argv)
{
auto options = std::make_unique<Options>();
{
ES2ABC_PERF_SCOPE("@TOTAL");
if (!options->Parse(argc, argv)) {
std::cerr << options->ErrorMsg() << std::endl;
return 1;
}
panda::Timer::timerStart(panda::EVENT_TOTAL, "");
if (HandleBcVersion(options)) {
return 0;
}
std::map<std::string, panda::es2panda::util::ProgramCache *> programsInfo;
panda::ArenaAllocator allocator(panda::SpaceType::SPACE_TYPE_COMPILER, nullptr, true);
Compiler::SetExpectedProgsCount(options->CompilerOptions().sourceFiles.size());
{
ES2ABC_PERF_SCOPE("@EVENT_COMPILE");
panda::Timer::timerStart(panda::EVENT_COMPILE, "");
int ret = Compiler::CompileFiles(options->CompilerOptions(), programsInfo, &allocator);
if (!CheckMergeModeConsistency(options->CompilerOptions().mergeAbc, programsInfo)) {
return 1;
}
if (options->ParseOnly()) {
return ret;
}
if (!options->NpmModuleEntryList().empty()) {
es2panda::util::ModuleHelpers::CompileNpmModuleEntryList(
options->NpmModuleEntryList(), options->CompilerOptions(), programsInfo, &allocator);
Compiler::SetExpectedProgsCount(Compiler::GetExpectedProgsCount() + 1);
}
panda::Timer::timerEnd(panda::EVENT_COMPILE, "");
}
if (!ResolveAndGenerate(programsInfo, options)) {
return 1;
}
panda::Timer::timerEnd(panda::EVENT_TOTAL, "");
}
DumpPerfInfo(options);
return 0;
}
}
#if defined(PANDA_TARGET_OHOS) || defined(PANDA_TARGET_LINUX)
void IncreaseStackLimit(size_t stackLimit)
{
struct rlimit rlim;
if (getrlimit(RLIMIT_STACK, &rlim) != 0) {
std::cerr << "Getrlimit failed: " << strerror(errno) << std::endl;
return;
}
if (rlim.rlim_cur >= stackLimit) {
return;
}
rlim.rlim_cur = stackLimit;
if (stackLimit > rlim.rlim_max) {
rlim.rlim_max = stackLimit;
}
if (setrlimit(RLIMIT_STACK, &rlim) != 0) {
std::cerr << "Warnning: Setrlimit failed, " << strerror(errno) << ". Please run command line 'ulimit - s 8256'"
<< " in terminal can eliminate this issue." << std::endl;
}
}
#endif
int main(int argc, const char **argv)
{
#if defined(PANDA_TARGET_OHOS) || defined(PANDA_TARGET_LINUX)
IncreaseStackLimit(panda::es2panda::util::STACK_SIZE_LINUX);
#endif
#ifdef OHOS_PLATFORM
mallopt(M_OHOS_CONFIG, M_TCACHE_PERFORMANCE_MODE);
mallopt(M_OHOS_CONFIG, M_ENABLE_OPT_TCACHE);
mallopt(M_SET_THREAD_CACHE, M_THREAD_CACHE_ENABLE);
mallopt(M_DELAYED_FREE, M_DELAYED_FREE_ENABLE);
#endif
panda::es2panda::aot::MemManager mm;
return panda::es2panda::aot::Run(argc, argv);
}