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