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