• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "libpandafile/class_data_accessor.h"
17 #include "paoc_options.h"
18 #include "aot/compiled_method.h"
19 #include "aot_builder/aot_builder.h"
20 #include "paoc_clusters.h"
21 #include "compiler.h"
22 #include "compiler_options.h"
23 #include "compiler_events_gen.h"
24 #include "compiler_logger.h"
25 #include "events/events.h"
26 #include "include/runtime.h"
27 #include "mem/gc/gc_types.h"
28 #include "optimizer_run.h"
29 #include "optimizer/ir_builder/ir_builder.h"
30 #include "os/filesystem.h"
31 #include "generated/base_options.h"
32 
33 #include "paoc.h"
34 
35 using namespace panda::compiler;  // NOLINT(google-build-using-namespace)
36 
37 namespace panda::paoc {
38 
39 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
40 #define LOG_PAOC(level) LOG(level, COMPILER) << "PAOC: "
41 
42 /**
43  * A class that encloses paoc's initialization:
44  */
45 class Paoc::PaocInitializer {
46 public:
PaocInitializer(Paoc * paoc)47     explicit PaocInitializer(Paoc *paoc) : paoc_(paoc) {}
48     // NOLINTNEXTLINE(modernize-avoid-c-arrays)
Init(const panda::Span<const char * > & args)49     int Init(const panda::Span<const char *> &args)
50     {
51         ASSERT(args.Data() != nullptr);
52         panda::PandArgParser pa_parser;
53 
54         paoc_->runtime_options_ = std::make_unique<decltype(paoc_->runtime_options_)::element_type>(args[0]);
55         paoc_->runtime_options_->AddOptions(&pa_parser);
56         paoc_->paoc_options_ = std::make_unique<decltype(paoc_->paoc_options_)::element_type>(args[0]);
57         paoc_->paoc_options_->AddOptions(&pa_parser);
58         panda::compiler::options.AddOptions(&pa_parser);
59 
60         base_options::Options base_options("");
61         base_options.AddOptions((&pa_parser));
62 
63         if (!pa_parser.Parse(args.Size(), args.Data())) {
64             std::cerr << "Error: " << pa_parser.GetErrorString() << "\n";
65             return -1;
66         }
67         Logger::Initialize(base_options);
68         if (paoc_->paoc_options_->GetPaocPandaFiles().empty()) {
69             paoc_->PrintUsage(pa_parser);
70             return 1;
71         }
72         if (InitRuntime() != 0) {
73             return -1;
74         }
75         if (InitCompiler() != 0) {
76             return -1;
77         }
78         if (paoc_->paoc_options_->WasSetPaocClusters() && !InitPaocClusters(&pa_parser)) {
79             return -1;
80         }
81         return 0;
82     }
83 
84 private:
InitRuntime()85     int InitRuntime()
86     {
87         auto runtime_options_err = paoc_->runtime_options_->Validate();
88         if (runtime_options_err) {
89             std::cerr << "Error: " << runtime_options_err.value().GetMessage() << std::endl;
90             return 1;
91         }
92 #ifndef PANDA_ASAN_ON
93         if (!paoc_->runtime_options_->WasSetInternalMemorySizeLimit()) {
94             // 2Gb - for emitting code
95             constexpr size_t CODE_SIZE_LIMIT = 0x7f000000;
96             paoc_->runtime_options_->SetInternalMemorySizeLimit(CODE_SIZE_LIMIT);
97         }
98 #endif
99         paoc_->runtime_options_->SetArkAot(true);
100         if (!panda::Runtime::Create(*paoc_->runtime_options_)) {
101             std::cerr << "Failed to create runtime!\n";
102             return -1;
103         }
104         return 0;
105     }
106 
InitCompiler()107     int InitCompiler()
108     {
109         CompilerLogger::Init(panda::compiler::options.GetCompilerLog());
110 
111         if (panda::compiler::options.IsCompilerEnableEvents()) {
112             EventWriter::Init(panda::compiler::options.GetCompilerEventsPath());
113         }
114         ValidateCompilerOptions();
115 
116         if (paoc_->paoc_options_->WasSetPaocMethodsFromFile()) {
117             InitPaocMethodsFromFile();
118         }
119         paoc_->skip_info_.is_first_compiled = !paoc_->paoc_options_->WasSetPaocSkipUntil();
120         paoc_->skip_info_.is_last_compiled = false;
121 
122         if (!InitAotBuilder()) {
123             return 1;
124         }
125         if (paoc_->paoc_options_->WasSetPaocDumpStatsCsv()) {
126             paoc_->statistics_dump_ = std::ofstream(paoc_->paoc_options_->GetPaocDumpStatsCsv(), std::ofstream::trunc);
127         }
128         InitPaocMode();
129         paoc_->code_allocator_ = new ArenaAllocator(panda::SpaceType::SPACE_TYPE_INTERNAL);
130         paoc_->loader_ = panda::Runtime::GetCurrent()->GetClassLinker();
131 
132         return 0;
133     }
134 
ValidateCompilerOptions()135     void ValidateCompilerOptions()
136     {
137         auto compiler_options_err = panda::compiler::options.Validate();
138         if (compiler_options_err) {
139             LOG_PAOC(FATAL) << compiler_options_err.value().GetMessage();
140         }
141         auto paoc_options_err = paoc_->paoc_options_->Validate();
142         if (paoc_options_err) {
143             LOG_PAOC(FATAL) << paoc_options_err.value().GetMessage();
144         }
145         if (paoc_->paoc_options_->WasSetPaocOutput() && paoc_->paoc_options_->WasSetPaocBootOutput()) {
146             LOG_PAOC(FATAL) << "combination of --paoc-output and --paoc-boot-output is not compatible\n";
147         }
148         if (paoc_->paoc_options_->WasSetPaocBootPandaLocations()) {
149             if (paoc_->paoc_options_->WasSetPaocBootLocation()) {
150                 LOG_PAOC(FATAL) << "We can't use --paoc-boot-panda-locations with --paoc-boot-location\n";
151             }
152             auto locations = paoc_->paoc_options_->GetPaocBootPandaLocations();
153             size_t i = 0;
154             // In fact boot files are preloaded by Runtime
155             for (auto bpf : Runtime::GetCurrent()->GetClassLinker()->GetBootPandaFiles()) {
156                 if (i >= locations.size()) {
157                     EVENT_PAOC("Numbers of files in --paoc-boot-panda-locations and --boot-panda-files are different");
158                     LOG_PAOC(FATAL)
159                         << "number of locations in --paoc-boot-panda-locations less then files in --boot-panda-files\n";
160                 }
161                 auto filename = locations[i];
162                 auto bpf_name = bpf->GetFilename();
163                 if (!CompareBootFiles(bpf_name, filename)) {
164                     EVENT_PAOC("different files in --paoc-boot-panda-locations and --boot-panda-files");
165                     LOG_PAOC(ERROR) << "The file from --paoc-boot-panda-locations: " << filename;
166                     LOG_PAOC(ERROR) << "isn't eqaul the file from --boot-panda-files: " << bpf->GetFilename();
167                     LOG_PAOC(FATAL) << "Files posotions " << i;
168                 }
169                 paoc_->location_mapping_[bpf_name] = filename;
170                 paoc_->preloaded_files_[os::GetAbsolutePath(bpf_name)] = bpf;
171                 ++i;
172             }
173             if (i != locations.size()) {
174                 EVENT_PAOC("Numbers of files in --paoc-boot-panda-locations and --boot-panda-files are different");
175                 LOG_PAOC(FATAL)
176                     << "number of locations in --paoc-boot-panda-locations more then files in --boot-panda-files\n";
177             }
178         }
179         // In fact boot files are preloaded by Runtime
180         for (auto bpf : Runtime::GetCurrent()->GetClassLinker()->GetBootPandaFiles()) {
181             if (!paoc_->paoc_options_->GetPaocBootLocation().empty()) {
182                 std::string filename = GetFileLocation(*bpf, paoc_->paoc_options_->GetPaocBootLocation());
183                 paoc_->location_mapping_[bpf->GetFilename()] = filename;
184             }
185             paoc_->preloaded_files_[os::GetAbsolutePath(bpf->GetFilename())] = bpf;
186         }
187     }
188 
InitPaocMethodsFromFile()189     void InitPaocMethodsFromFile()
190     {
191         std::ifstream mfile(paoc_->paoc_options_->GetPaocMethodsFromFile());
192         std::string line;
193         if (mfile) {
194             while (getline(mfile, line)) {
195                 paoc_->methods_list_.insert(line);
196             }
197         }
198         LOG_PAOC(INFO) << "Method list size: " << paoc_->methods_list_.size();
199     }
200 
InitPaocMode()201     void InitPaocMode()
202     {
203         const auto &mode_str = paoc_->paoc_options_->GetPaocMode();
204         if (mode_str == "aot") {
205             paoc_->mode_ = PaocMode::AOT;
206         } else if (mode_str == "jit") {
207             paoc_->mode_ = PaocMode::JIT;
208         } else if (mode_str == "osr") {
209             paoc_->mode_ = PaocMode::OSR;
210         } else {
211             UNREACHABLE();
212         }
213     }
214 
InitAotBuilder()215     bool InitAotBuilder()
216     {
217         // Initialize Arch:
218         Arch arch;
219         bool cross_compilation;
220         if (paoc_->paoc_options_->WasSetPaocArch()) {
221             auto paoc_arch = GetArchFromString(paoc_->paoc_options_->GetPaocArch());
222             cross_compilation = paoc_arch != RUNTIME_ARCH;
223             arch = paoc_arch;
224         } else {
225             cross_compilation = false;
226             arch = RUNTIME_ARCH;
227         }
228         panda::compiler::options.AdjustCpuFeatures(cross_compilation);
229 
230         if (arch == Arch::NONE) {
231             LOG_PAOC(ERROR) << "Invalid arch option:" << paoc_->paoc_options_->GetPaocArch();
232             return false;
233         }
234         if (!BackendSupport(arch)) {
235             LOG_PAOC(ERROR) << "Backend is not supported: " << paoc_->paoc_options_->GetPaocArch();
236             return false;
237         }
238         paoc_->aot_builder_.SetArch(arch);
239 
240         // Initialize GC:
241         auto runtime_lang = paoc_->runtime_options_->GetRuntimeType();
242         // Fix it in issue 8164:
243         auto gc_type = panda::mem::GCTypeFromString(paoc_->runtime_options_->GetGcType(runtime_lang));
244         ASSERT(gc_type != panda::mem::GCType::INVALID_GC);
245 
246         paoc_->aot_builder_.SetGcType(static_cast<uint32_t>(gc_type));
247 
248 #ifndef NDEBUG
249         paoc_->aot_builder_.SetGenerateSymbols(true);
250 #else
251         paoc_->aot_builder_.SetGenerateSymbols(paoc_->paoc_options_->IsPaocGenerateSymbols());
252 #endif
253 #ifdef PANDA_COMPILER_CFI
254         paoc_->aot_builder_.SetEmitDebugInfo(panda::compiler::options.IsCompilerEmitDebugInfo());
255 #endif
256         return true;
257     }
258 
InitPaocClusters(panda::PandArgParser * pa_parser)259     bool InitPaocClusters(panda::PandArgParser *pa_parser)
260     {
261         std::ifstream fstream(paoc_->paoc_options_->GetPaocClusters());
262         if (!fstream) {
263             LOG_PAOC(ERROR) << "Can't open `paoc-clusters` file";
264             return false;
265         }
266         JsonObject obj(fstream.rdbuf());
267         fstream.close();
268         paoc_->clusters_info_.Init(obj, pa_parser);
269         return true;
270     }
271 
272 private:
273     Paoc *paoc_ {nullptr};
274 };
275 
Run(const panda::Span<const char * > & args)276 int Paoc::Run(const panda::Span<const char *> &args)
277 {
278     if (PaocInitializer(this).Init(args) != 0) {
279         return -1;
280     }
281     auto allocator = Runtime::GetCurrent()->GetInternalAllocator();
282     runtime_ = Runtime::GetCurrent()->GetPandaVM()->GetCompilerRuntimeInterface();
283     aot_builder_.SetRuntime(runtime_);
284     slow_path_data_ = allocator->New<compiler::SharedSlowPathData>();
285     if (!LoadPandaFiles()) {
286         LOG_PAOC(FATAL) << "Can not load panda files";
287     }
288     bool error_occurred = !CompileFiles();
289     bool attempted_to_compile = (compilation_index_ != 0);
290     error_occurred |= !attempted_to_compile;
291     if (mode_ == PaocMode::AOT) {
292         if (aot_builder_.GetCurrentCodeAddress() != 0 || !aot_builder_.GetClassHashTableSize()->empty()) {
293             RunAotMode(args);
294         } else {
295             LOG_PAOC(ERROR) << "There are no compiled methods";
296             Clear(allocator);
297             return -1;
298         }
299     }
300     if (!attempted_to_compile) {
301         LOG_PAOC(WARNING) << "There are no compiled methods";
302     }
303     Clear(allocator);
304     return ShouldIgnoreFailures() ? 0 : (error_occurred ? 1 : 0);
305 }
306 
RunAotMode(const panda::Span<const char * > & args)307 void Paoc::RunAotMode(const panda::Span<const char *> &args)
308 {
309     std::string cmdline;
310     for (auto arg : args) {
311         cmdline += arg;
312         cmdline += " ";
313     }
314     std::string class_ctx;
315     loader_->EnumeratePandaFiles([this, &class_ctx](const panda_file::File &pf) {
316         if (location_mapping_.find(pf.GetFilename()) == location_mapping_.end()) {
317             class_ctx += os::GetAbsolutePath(pf.GetFilename());
318         } else {
319             class_ctx += location_mapping_[pf.GetFilename()];
320         }
321         class_ctx += '*';
322         class_ctx += std::to_string(pf.GetHeader()->checksum);
323         class_ctx += ':';
324         return true;
325     });
326     class_ctx.pop_back();
327     aot_builder_.SetClassContext(class_ctx);
328     LOG(DEBUG, COMPILER) << "PAOC: ClassContext '" << class_ctx << '\'';
329     auto output_file = paoc_options_->GetPaocOutput();
330     if (paoc_options_->WasSetPaocBootOutput()) {
331         aot_builder_.SetBootAot(true);
332         output_file = paoc_options_->GetPaocBootOutput();
333     }
334     aot_builder_.SetWithCha(paoc_options_->IsPaocUseCha());
335     aot_builder_.Write(cmdline, output_file);
336     LOG_PAOC(DEBUG) << "Successfully compiled to " << output_file;
337 }
338 
Clear(panda::mem::InternalAllocatorPtr allocator)339 void Paoc::Clear(panda::mem::InternalAllocatorPtr allocator)
340 {
341     delete code_allocator_;
342     code_allocator_ = nullptr;
343     allocator->Delete(slow_path_data_);
344     methods_list_.clear();
345     panda::Runtime::Destroy();
346 }
347 
StartAotFile(const panda_file::File & pfile_ref)348 void Paoc::StartAotFile(const panda_file::File &pfile_ref)
349 {
350     ASSERT(mode_ == PaocMode::AOT);
351     std::string filename;
352     if (paoc_options_->GetPaocLocation().empty()) {
353         filename = os::GetAbsolutePath(pfile_ref.GetFilename());
354     } else {
355         filename = GetFileLocation(pfile_ref, paoc_options_->GetPaocLocation());
356         location_mapping_[pfile_ref.GetFilename()] = filename;
357     }
358     ASSERT(!filename.empty());
359     aot_builder_.StartFile(filename, pfile_ref.GetHeader()->checksum);
360 }
361 
362 /**
363  * Iterate over `--paoc-panda-files`.
364  * @return `false` on error.
365  */
CompileFiles()366 bool Paoc::CompileFiles()
367 {
368     auto pfiles = paoc_options_->GetPaocPandaFiles();
369     bool error_occurred = false;
370     auto *vm = panda::Runtime::GetCurrent()->GetPandaVM();
371     for (auto &file_name : pfiles) {
372         // Load panda file
373         const panda_file::File *pfile;
374 
375         auto abs_path = os::GetAbsolutePath(file_name);
376         if (preloaded_files_.find(abs_path) != preloaded_files_.end()) {
377             pfile = preloaded_files_[abs_path];
378         } else {
379             auto file = vm->OpenPandaFile(file_name);
380             if (!file) {
381                 error_occurred = true;
382                 if (!ShouldIgnoreFailures()) {
383                     LOG_PAOC(FATAL) << "Can not open file: " << file_name;
384                 }
385                 LOG_PAOC(WARNING) << "Can not open file: " << file_name;
386                 continue;
387             }
388             pfile = file.get();
389             loader_->AddPandaFile(std::move(file));
390             LOG_PAOC(DEBUG) << "Added panda file: " << file_name;
391         }
392         auto &pfile_ref = *pfile;
393 
394         if (mode_ == PaocMode::AOT) {
395             StartAotFile(pfile_ref);
396         }
397 
398         if (!CompilePandaFile(pfile_ref)) {
399             error_occurred = true;
400         }
401 
402         if (mode_ == PaocMode::AOT) {
403             aot_builder_.EndFile();
404         }
405         if (error_occurred && !ShouldIgnoreFailures()) {
406             return false;
407         }
408     }
409     return !error_occurred;
410 }
411 
412 /**
413  * Compile every method from loaded panda files that matches `--compiler-regex`,
414  * `--paoc-skip-until`, `--paoc-compile-until` and `--paoc-methods-from-file` options:
415  * @return `false` on error.
416  */
CompilePandaFile(const panda_file::File & pfile_ref)417 bool Paoc::CompilePandaFile(const panda_file::File &pfile_ref)
418 {
419     auto classes = pfile_ref.GetClasses();
420     bool error_occurred = false;
421     for (auto &class_id : classes) {
422         panda_file::File::EntityId id(class_id);
423         panda::Class *klass = ResolveClass(pfile_ref, id);
424         std::string class_name = ClassHelper::GetName(pfile_ref.GetStringData(id).data);
425 
426         if (!PossibleToCompile(pfile_ref, klass, id)) {
427             if (paoc_options_->IsPaocVerbose()) {
428                 LOG_PAOC(DEBUG) << "Ignoring a class `" << class_name << "` (id = " << id << ", file `"
429                                 << pfile_ref.GetFilename() << "`)";
430             }
431             continue;
432         }
433 
434         ASSERT(klass != nullptr);
435         if (paoc_options_->IsPaocVerbose()) {
436             LOG_PAOC(DEBUG) << "Compiling class `" << class_name << "` (id = " << id << ", file `"
437                             << pfile_ref.GetFilename() << "`)";
438         }
439 
440         // Check that all of the methods are compiled correctly:
441         ASSERT(klass->GetPandaFile() != nullptr);
442         if (!Compile(klass, *klass->GetPandaFile())) {
443             error_occurred = true;
444             std::string err_msg =
445                 "Class `" + class_name + "` from " + pfile_ref.GetFilename() + " compiled with errors";
446             PrintError(err_msg);
447             if (!ShouldIgnoreFailures()) {
448                 break;
449             }
450         }
451     }
452 
453     BuildClassHashTable(pfile_ref);
454 
455     return !error_occurred;
456 }
457 
ResolveClass(const panda_file::File & pfile_ref,panda_file::File::EntityId class_id)458 panda::Class *Paoc::ResolveClass(const panda_file::File &pfile_ref, panda_file::File::EntityId class_id)
459 {
460     ErrorHandler handler;
461     ScopedMutatorLock lock;
462     if (pfile_ref.IsExternal(class_id)) {
463         return nullptr;
464     }
465     panda_file::ClassDataAccessor cda(pfile_ref, class_id);
466     LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(&cda);
467     auto klass = loader_->GetExtension(ctx)->GetClass(pfile_ref, class_id, nullptr, &handler);
468     return klass;
469 }
470 
471 /**
472  * Check if it is possible to compile a class.
473  * @return true if the class isn't external, abstract, interface and isn't an array class.
474  */
PossibleToCompile(const panda_file::File & pfile_ref,const panda::Class * klass,panda_file::File::EntityId class_id)475 bool Paoc::PossibleToCompile(const panda_file::File &pfile_ref, const panda::Class *klass,
476                              panda_file::File::EntityId class_id)
477 {
478     std::string class_name = ClassHelper::GetName(pfile_ref.GetStringData(class_id).data);
479 
480     // Skip external classes
481     if (pfile_ref.IsExternal(class_id)) {
482         if (paoc_options_->IsPaocVerbose()) {
483             LOG_PAOC(DEBUG) << "Can't compile external class `" << class_name << "`";
484         }
485         return false;
486     }
487     if (klass == nullptr) {
488         if (paoc_options_->IsPaocVerbose()) {
489             LOG_PAOC(DEBUG) << "Class is nullptr: `" << class_name << "`";
490             LOG_PAOC(ERROR) << "ClassLinker can't load a class `" << class_name << "`";
491         }
492         return false;
493     }
494     // Skip object array class
495     if (klass->IsArrayClass()) {
496         if (paoc_options_->IsPaocVerbose()) {
497             LOG_PAOC(DEBUG) << "Can't compile array class `" << class_name << "`";
498         }
499         return false;
500     }
501 
502     if (klass->IsAbstract()) {
503         if (paoc_options_->IsPaocVerbose()) {
504             LOG_PAOC(DEBUG) << "Will compile abstract class `" << class_name << "`";
505         }
506     }
507 
508     return true;
509 }
510 
511 /**
512  * Compile the class.
513  * @return `false` on error.
514  */
Compile(Class * klass,const panda_file::File & pfile_ref)515 bool Paoc::Compile(Class *klass, const panda_file::File &pfile_ref)
516 {
517     ASSERT(klass != nullptr);
518 
519     if (mode_ == PaocMode::AOT) {
520         aot_builder_.StartClass(*klass);
521     }
522     bool error_occurred = false;
523     auto methods = klass->GetMethods();
524     panda_file::ClassDataAccessor cda(pfile_ref, klass->GetFileId());
525     size_t method_index = 0;
526     size_t smethod_idx = klass->GetNumVirtualMethods();
527     size_t vmethod_idx = 0;
528     cda.EnumerateMethods([this, &smethod_idx, &vmethod_idx, &methods, &method_index, &pfile_ref,
529                           &error_occurred](panda_file::MethodDataAccessor &method_data_accessor) {
530         if (error_occurred && !ShouldIgnoreFailures()) {
531             return;
532         }
533         // TODO(pishin, msherstennikov): revisit
534         // Method (or the whole class?) may already have a definition in another file,
535         // in this case it should not be added into AOT file.
536         Method &method = method_data_accessor.IsStatic() ? methods[smethod_idx++] : methods[vmethod_idx++];
537         auto method_name = runtime_->GetMethodFullName(&method, false);
538         if (method.GetPandaFile()->GetFilename() == pfile_ref.GetFilename() && !Skip(&method) &&
539             IsMethodInList(method_name) && options.MatchesRegex(method_name) && !Compile(&method, method_index)) {
540             error_occurred = true;
541         }
542         method_index++;
543     });
544 
545     if (mode_ == PaocMode::AOT) {
546         aot_builder_.EndClass();
547     }
548 
549     return !error_occurred;
550 }
551 
552 // Wrapper for CompileJit(), CompileOsr() and CompileAot() arguments:
553 struct Paoc::CompilingContext {
554 public:
555     NO_COPY_SEMANTIC(CompilingContext);
556     NO_MOVE_SEMANTIC(CompilingContext);
CompilingContextpanda::paoc::Paoc::CompilingContext557     CompilingContext(Method *method_ptr, size_t method_index, std::ofstream *statistics_dump)
558         : method(method_ptr),
559           allocator(panda::SpaceType::SPACE_TYPE_COMPILER, PandaVM::GetCurrent()->GetMemStats()),
560           graph_local_allocator(panda::SpaceType::SPACE_TYPE_COMPILER, PandaVM::GetCurrent()->GetMemStats(), true),
561           index(method_index),
562           stats(statistics_dump)
563     {
564     }
~CompilingContextpanda::paoc::Paoc::CompilingContext565     ~CompilingContext()
566     {
567         if (graph != nullptr) {
568             graph->~Graph();
569         }
570         ASSERT(stats != nullptr);
571         if (*stats) {
572             DumpStatistics();
573         }
574     }
DumpStatisticspanda::paoc::Paoc::CompilingContext575     void DumpStatistics() const
576     {
577         ASSERT(stats);
578         char sep = ',';
579         *stats << method->GetFullName() << sep;
580         *stats << "paoc-summary" << sep;
581         *stats << allocator.GetAllocatedSize() << sep;
582         *stats << graph_local_allocator.GetAllocatedSize() << '\n';
583     }
584 
585 public:
586     Method *method {nullptr};                     // NOLINT(misc-non-private-member-variables-in-classes)
587     panda::ArenaAllocator allocator;              // NOLINT(misc-non-private-member-variables-in-classes)
588     panda::ArenaAllocator graph_local_allocator;  // NOLINT(misc-non-private-member-variables-in-classes)
589     Graph *graph {nullptr};                       // NOLINT(misc-non-private-member-variables-in-classes)
590     size_t index;                                 // NOLINT(misc-non-private-member-variables-in-classes)
591     std::ofstream *stats {nullptr};               // NOLINT(misc-non-private-member-variables-in-classes)
592 };
593 
Compile(Method * method,size_t method_index)594 bool Paoc::Compile(Method *method, size_t method_index)
595 {
596     if (method == nullptr) {
597         LOG_PAOC(WARNING) << "Method is nullptr";
598         return false;
599     }
600 #ifndef NDEBUG
601     const auto *pfile = method->GetPandaFile();
602     auto method_id = method->GetFileId();
603     ASSERT(pfile != nullptr);
604     ASSERT(!pfile->IsExternal(method_id));
605 #endif  // NDEBUG
606 
607     if (method->IsAbstract() || method->IsNative() || method->IsIntrinsic()) {
608         return true;
609     }
610     auto method_name = runtime_->GetMethodFullName(method, false);
611 
612     ++compilation_index_;
613     LOG_PAOC(INFO) << "[" << compilation_index_ << "] Compile method (id=" << method->GetFileId()
614                    << "): " << method_name;
615 
616     CompilingContext ctx(method, method_index, &statistics_dump_);
617 
618     PaocClusters::ScopedApplySpecialOptions to(method_name, &clusters_info_);
619     switch (mode_) {
620         case PaocMode::AOT:
621             if (!CompileAot(&ctx)) {
622                 EVENT_COMPILATION(method_name, false, ctx.method->GetCodeSize(), 0, 0, 0,
623                                   events::CompilationStatus::FAILED);
624                 return false;
625             }
626             return true;
627         case PaocMode::JIT:
628             return CompileJit(&ctx);
629         case PaocMode::OSR:
630             return CompileOsr(&ctx);
631         default:
632             UNREACHABLE();
633     }
634 }
635 
636 /**
637  * Compiles a method in JIT mode (i.e. no code generated).
638  * @return `false` on error.
639  */
CompileJit(CompilingContext * ctx)640 bool Paoc::CompileJit(CompilingContext *ctx)
641 {
642     ASSERT(ctx != nullptr);
643     ASSERT(mode_ == PaocMode::JIT);
644     auto name = runtime_->GetMethodFullName(ctx->method, false);
645     auto arch {Arch::NONE};
646     bool is_dynamic = panda::panda_file::IsDynamicLanguage(ctx->method->GetClass()->GetSourceLang());
647     if (!compiler::CompileInGraph(runtime_, ctx->method, false, &ctx->allocator, &ctx->graph_local_allocator,
648                                   is_dynamic, &arch, name, &ctx->graph)) {
649         std::string err_msg = "Failed to JIT-compile method: " + name;
650         PrintError(err_msg);
651         return false;
652     }
653     EVENT_COMPILATION(name, false, ctx->method->GetCodeSize(), 0, 0, 0, events::CompilationStatus::COMPILED);
654     return true;
655 }
656 
657 /**
658  * Compiles a method in OSR mode.
659  * @return `false` on error.
660  */
CompileOsr(CompilingContext * ctx)661 bool Paoc::CompileOsr(CompilingContext *ctx)
662 {
663     ASSERT(ctx != nullptr);
664     ASSERT(mode_ == PaocMode::OSR);
665     std::string name;
666     auto arch {Arch::NONE};
667     bool is_dynamic = panda::panda_file::IsDynamicLanguage(ctx->method->GetClass()->GetSourceLang());
668     if (!compiler::CompileInGraph(runtime_, ctx->method, true, &ctx->allocator, &ctx->graph_local_allocator, is_dynamic,
669                                   &arch, name, &ctx->graph)) {
670         std::string err_msg = "Failed to OSR-compile method: " + runtime_->GetMethodFullName(ctx->method, false);
671         PrintError(err_msg);
672         return false;
673     }
674     EVENT_COMPILATION(name, true, ctx->method->GetCodeSize(), 0, 0, 0, events::CompilationStatus::COMPILED);
675     return true;
676 }
677 
678 /**
679  * Compiles a method in AOT mode.
680  * @return `false` on error.
681  */
CompileAot(CompilingContext * ctx)682 bool Paoc::CompileAot(CompilingContext *ctx)
683 {
684     ASSERT(ctx != nullptr);
685     ASSERT(mode_ == PaocMode::AOT);
686     std::string class_name = ClassHelper::GetName(ctx->method->GetClassName().data);
687 
688     bool is_dynamic = panda::panda_file::IsDynamicLanguage(ctx->method->GetClass()->GetSourceLang());
689 
690     ctx->graph = ctx->allocator.New<Graph>(&ctx->allocator, &ctx->graph_local_allocator, aot_builder_.GetArch(),
691                                            ctx->method, runtime_, false, nullptr, is_dynamic);
692     if (ctx->graph == nullptr) {
693         PrintError("Graph creation failed!");
694         return false;
695     }
696 
697     auto aot_data = ctx->graph->GetAllocator()->New<AotData>(
698         ctx->method->GetPandaFile(), ctx->graph, aot_builder_.GetCurrentCodeAddress(),
699         aot_builder_.GetIntfInlineCacheIndex(), aot_builder_.GetGotPlt(), aot_builder_.GetGotVirtIndexes(),
700         aot_builder_.GetGotClass(), aot_builder_.GetGotString(), aot_builder_.GetGotIntfInlineCache(), slow_path_data_);
701     aot_data->SetUseCha(paoc_options_->IsPaocUseCha());
702     ctx->graph->SetAotData(aot_data);
703 
704     if (!ctx->graph->RunPass<IrBuilder>()) {
705         PrintError("IrBuilder failed!");
706         return false;
707     }
708     if (!RunOptimizations(ctx->graph)) {
709         PrintError("RunOptimizations failed!");
710         return false;
711     }
712 
713     CompiledMethod compiled_method(ctx->graph->GetArch(), ctx->method);
714     compiled_method.SetCode(ctx->graph->GetData().ToConst());
715     compiled_method.SetCodeInfo(ctx->graph->GetCodeInfoData());
716 #ifdef PANDA_COMPILER_CFI
717     compiled_method.SetCfiInfo(ctx->graph->GetCallingConvention()->GetCfiInfo());
718 #endif
719     if (ctx->graph->GetData().empty() || ctx->graph->GetCodeInfoData().empty()) {
720         LOG(INFO, COMPILER) << "Emit code failed";
721         return false;
722     }
723     aot_builder_.AddMethod(compiled_method, ctx->index);
724 
725     LOG(INFO, COMPILER) << "Successfully compiled method: " << runtime_->GetMethodFullName(ctx->method, false);
726     ASSERT(ctx->graph != nullptr);
727 
728     EVENT_COMPILATION(runtime_->GetMethodFullName(ctx->method, false), false, ctx->method->GetCodeSize(),
729                       aot_builder_.GetCurrentCodeAddress() - compiled_method.GetCode().size(),
730                       compiled_method.GetCode().size(), compiled_method.GetCodeInfo().size(),
731                       events::CompilationStatus::COMPILED);
732 
733     return true;
734 }
735 
PrintError(const std::string & error)736 void Paoc::PrintError(const std::string &error)
737 {
738     if (ShouldIgnoreFailures()) {
739         LOG_PAOC(WARNING) << error;
740     } else {
741         LOG_PAOC(ERROR) << error;
742     }
743 }
744 
ShouldIgnoreFailures()745 bool Paoc::ShouldIgnoreFailures()
746 {
747     return compiler::options.IsCompilerIgnoreFailures();
748 }
749 
PrintUsage(const panda::PandArgParser & pa_parser)750 void Paoc::PrintUsage(const panda::PandArgParser &pa_parser)
751 {
752     std::cerr << "Usage: ark_aot [OPTIONS] --paoc-panda-files <list>\n";
753     std::cerr << "    --paoc-panda-files          list of input panda files, it is a mandatory option\n";
754     std::cerr << "    --paoc-mode                 specify compilation mode (AOT, JIT or OSR) \n";
755     std::cerr << "    --paoc-output               path to output file, default is out.an\n";
756     std::cerr << "    --paoc-arch                 target architecture, default is native,"
757                  " other possible values see below\n";
758     std::cerr << "    --paoc-location             location path of the input panda file, that will be written into"
759                  " the AOT file\n";
760     std::cerr << "    --paoc-skip-until           first method to compile, skip all previous\n";
761     std::cerr << "    --paoc-compile-until        last method to compile, skip all following\n";
762     std::cerr << "    --paoc-methods-from-file    path to file which contains methods to compile\n";
763 #ifndef NDEBUG
764     std::cerr << "    --paoc-generate-symbols     generate symbols for compiled methods, disabled by default\n";
765 #endif
766     std::cerr << "    --compiler-ignore-failures  ignore failures in methods/classes/files compilation\n";
767     std::cerr << " You can also use other Ark compiler options\n";
768 
769     std::cerr << pa_parser.GetHelpString() << std::endl;
770 }
771 
IsMethodInList(const std::string & method_full_name)772 bool Paoc::IsMethodInList(const std::string &method_full_name)
773 {
774     return !paoc_options_->WasSetPaocMethodsFromFile() || (methods_list_.find(method_full_name) != methods_list_.end());
775 }
776 
777 /*
778  * Check if needed to skip method, considering  'paoc-skip-until' and 'paoc-compile-until' options
779  */
Skip(Method * method)780 bool Paoc::Skip(Method *method)
781 {
782     if (method == nullptr) {
783         return true;
784     }
785 
786     auto method_name = runtime_->GetMethodFullName(method, false);
787     if (!skip_info_.is_first_compiled) {
788         if (method_name == paoc_options_->GetPaocSkipUntil()) {
789             skip_info_.is_first_compiled = true;
790         } else {
791             return true;
792         }
793     }
794     if (skip_info_.is_last_compiled) {
795         return true;
796     }
797     if (paoc_options_->WasSetPaocCompileUntil() && method_name == paoc_options_->GetPaocCompileUntil()) {
798         skip_info_.is_last_compiled = true;
799     }
800     return false;
801 }
802 
GetFileLocation(const panda_file::File & pfile_ref,std::string location)803 std::string Paoc::GetFileLocation(const panda_file::File &pfile_ref, std::string location)
804 {
805     auto &filename = pfile_ref.GetFilename();
806     if (auto pos = filename.rfind('/'); pos != std::string::npos) {
807         if (location.back() == '/') {
808             pos++;
809         }
810         location += filename.substr(pos);
811     } else {
812         location += '/' + filename;
813     }
814     return location;
815 }
816 
CompareBootFiles(std::string filename,std::string paoc_location)817 bool Paoc::CompareBootFiles(std::string filename, std::string paoc_location)
818 {
819     if (auto pos = filename.rfind('/'); pos != std::string::npos) {
820         filename = filename.substr(++pos);
821     }
822     if (auto pos = paoc_location.rfind('/'); pos != std::string::npos) {
823         paoc_location = paoc_location.substr(++pos);
824     }
825 
826     return paoc_location == filename;
827 }
828 
LoadPandaFiles()829 bool Paoc::LoadPandaFiles()
830 {
831     bool error_occurred = false;
832     auto *vm = panda::Runtime::GetCurrent()->GetPandaVM();
833     auto pfiles = runtime_options_->GetPandaFiles();
834     for (auto &file_name : pfiles) {
835         auto pfile = vm->OpenPandaFile(file_name);
836         if (!pfile) {
837             error_occurred = true;
838             if (!ShouldIgnoreFailures()) {
839                 LOG_PAOC(FATAL) << "Can not open file: " << file_name;
840             }
841             LOG_PAOC(WARNING) << "Can not open file: " << file_name;
842             continue;
843         }
844 
845         if (!paoc_options_->GetPaocLocation().empty()) {
846             std::string filename = GetFileLocation(*pfile, paoc_options_->GetPaocLocation());
847             location_mapping_[pfile->GetFilename()] = filename;
848         }
849 
850         preloaded_files_[os::GetAbsolutePath(file_name)] = pfile.get();
851         loader_->AddPandaFile(std::move(pfile));
852     }
853     return !error_occurred;
854 }
855 
BuildClassHashTable(const panda_file::File & pfile_ref)856 void Paoc::BuildClassHashTable(const panda_file::File &pfile_ref)
857 {
858     if (!pfile_ref.GetClasses().empty()) {
859         aot_builder_.AddClassHashTable(pfile_ref);
860     }
861 }
862 
863 #undef LOG_PAOC
864 
865 }  // namespace panda::paoc
866 
main(int argc,const char * argv[])867 int main(int argc, const char *argv[])
868 {
869     panda::paoc::Paoc paoc;
870     panda::Span<const char *> args(argv, argc);
871     return paoc.Run(args);
872 }
873