• 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 <elf.h>
17 #include "unit_test.h"
18 #include "aot/aot_manager.h"
19 #include "aot/aot_builder/aot_builder.h"
20 #include "aot/compiled_method.h"
21 #include "compiler/code_info/code_info_builder.h"
22 #include "os/exec.h"
23 #include "assembly-parser.h"
24 #include "utils/string_helpers.h"
25 #include "events/events.h"
26 #include "mem/gc/gc_types.h"
27 #include "runtime/include/file_manager.h"
28 
29 #include <regex>
30 
31 using panda::panda_file::File;
32 
33 namespace panda::compiler {
34 class AotTest : public AsmTest {
35 public:
AotTest()36     AotTest()
37     {
38         std::string exe_path = GetExecPath();
39         auto pos = exe_path.rfind('/');
40         paoc_path_ = exe_path.substr(0, pos) + "/../bin/ark_aot";
41         aotdump_path_ = exe_path.substr(0, pos) + "/../bin/ark_aotdump";
42     }
43 
GetPaocDirectory() const44     std::string GetPaocDirectory() const
45     {
46         auto pos = paoc_path_.rfind('/');
47         return paoc_path_.substr(0, pos);
48     }
49 
GetArchAsArgString() const50     const char *GetArchAsArgString() const
51     {
52         switch (target_arch) {
53             case Arch::AARCH32:
54                 return "arm";
55             case Arch::AARCH64:
56                 return "arm64";
57             case Arch::X86:
58                 return "x86";
59             case Arch::X86_64:
60                 return "x86_64";
61             default:
62                 UNREACHABLE();
63         }
64     }
65 
RunAotdump(const std::string & aot_filename)66     void RunAotdump(const std::string &aot_filename)
67     {
68         TmpFile tmpfile("aotdump.tmp");
69 
70         auto res = os::exec::Exec(aotdump_path_.c_str(), "--show-code=disasm", "--output-file", tmpfile.GetFileName(),
71                                   aot_filename.c_str());
72         ASSERT_TRUE(res) << "aotdump failed with error: " << res.Error().ToString();
73         ASSERT_EQ(res.Value(), 0) << "aotdump return error code: " << res.Value();
74     }
75 
76     std::string paoc_path_;
77     std::string aotdump_path_;
78 protected:
79     Arch target_arch = Arch::AARCH64;
80 };
81 
82 #ifdef PANDA_COMPILER_TARGET_AARCH64
TEST_F(AotTest,PaocBootPandaFiles)83 TEST_F(AotTest, PaocBootPandaFiles)
84 {
85     // Test basic functionality only in host mode.
86     if (RUNTIME_ARCH != Arch::X86_64) {
87         return;
88     }
89     TmpFile panda_fname("test.pf");
90     TmpFile aot_fname("./test.an");
91     static const std::string location = "/data/local/tmp";
92     static const std::string panda_file_path = location + "/" + panda_fname.GetFileName();
93 
94     auto source = R"(
95         .function void dummy() {
96             return.void
97         }
98     )";
99 
100     {
101         pandasm::Parser parser;
102         auto res = parser.Parse(source);
103         ASSERT_TRUE(res);
104         ASSERT_TRUE(pandasm::AsmEmitter::Emit(panda_fname.GetFileName(), res.Value()));
105     }
106 
107     // Correct path to arkstdlib.abc
108     {
109         auto pandastdlib_path = GetPaocDirectory() + "/../pandastdlib/arkstdlib.abc";
110         auto res = os::exec::Exec(paoc_path_.c_str(), "--paoc-panda-files", panda_fname.GetFileName(), "--paoc-output",
111                                   aot_fname.GetFileName(), "--paoc-location", location.c_str(), "--paoc-arch",
112                                   GetArchAsArgString(), "--boot-panda-files", pandastdlib_path.c_str());
113         ASSERT_TRUE(res) << "paoc failed with error: " << res.Error().ToString();
114         ASSERT_EQ(res.Value(), 0) << "Aot compiler failed with code " << res.Value();
115         RunAotdump(aot_fname.GetFileName());
116     }
117 }
118 
TEST_F(AotTest,PaocLocation)119 TEST_F(AotTest, PaocLocation)
120 {
121     // Test basic functionality only in host mode.
122     if (RUNTIME_ARCH != Arch::X86_64) {
123         return;
124     }
125     TmpFile panda_fname("test.pf");
126     TmpFile aot_fname("./test.an");
127     static const std::string location = "/data/local/tmp";
128     static const std::string panda_file_path = location + "/" + panda_fname.GetFileName();
129 
130     auto source = R"(
131         .function u32 add(u64 a0, u64 a1) {
132             add a0, a1
133             return
134         }
135     )";
136 
137     {
138         pandasm::Parser parser;
139         auto res = parser.Parse(source);
140         ASSERT_TRUE(res);
141         ASSERT_TRUE(pandasm::AsmEmitter::Emit(panda_fname.GetFileName(), res.Value()));
142     }
143 
144     {
145         auto pandastdlib_path = GetPaocDirectory() + "/../pandastdlib/arkstdlib.abc";
146         auto res = os::exec::Exec(paoc_path_.c_str(), "--paoc-panda-files", panda_fname.GetFileName(), "--paoc-output",
147                                   aot_fname.GetFileName(), "--paoc-location", location.c_str(), "--paoc-arch=x86_64",
148                                   "--gc-type=epsilon", "--paoc-use-cha=false");
149         ASSERT_TRUE(res) << "paoc failed with error: " << res.Error().ToString();
150         ASSERT_EQ(res.Value(), 0) << "Aot compiler failed with code " << res.Value();
151     }
152 
153     AotManager aot_manager;
154     {
155         auto res =
156             aot_manager.AddFile(aot_fname.GetFileName(), nullptr, static_cast<uint32_t>(mem::GCType::EPSILON_GC));
157         ASSERT_TRUE(res) << res.Error();
158     }
159 
160     auto aot_file = aot_manager.GetFile(aot_fname.GetFileName());
161     ASSERT_TRUE(aot_file);
162     ASSERT_EQ(aot_file->GetFilesCount(), 1);
163     ASSERT_TRUE(aot_file->FindPandaFile(panda_file_path));
164 }
165 #endif  // PANDA_COMPILER_TARGET_AARCH64
166 
TEST_F(AotTest,BuildAndLoad)167 TEST_F(AotTest, BuildAndLoad)
168 {
169     if (RUNTIME_ARCH == Arch::AARCH32) {
170         // TODO(msherstennikov): for some reason dlopen cannot open aot file in qemu-arm
171         return;
172     }
173     uint32_t tid = os::thread::GetCurrentThreadId();
174     std::string tmpfile = helpers::string::Format("/tmp/tmpfile_%04x.pn", tid);
175     static constexpr const char *tmpfile_pf = "test.pf";
176     static constexpr const char *cmdline = "cmdline";
177     static constexpr uint32_t method1_id = 42;
178     static constexpr uint32_t method2_id = 43;
179     const std::string class_name("Foo");
180     std::string method_name(class_name + "::method");
181     std::array<uint8_t, 4> x86_add = {
182         0x8d, 0x04, 0x37,  // lea    eax,[rdi+rdi*1]
183         0xc3               // ret
184     };
185 
186     AotBuilder aot_builder;
187     aot_builder.SetArch(RUNTIME_ARCH);
188     aot_builder.SetGcType(2);
189     RuntimeInterfaceMock iruntime;
190     aot_builder.SetRuntime(&iruntime);
191 
192     aot_builder.StartFile(tmpfile_pf, 0x12345678);
193 
194     auto thread = MTManagedThread::GetCurrent();
195     if (thread != nullptr) {
196         thread->ManagedCodeBegin();
197     }
198     auto runtime = Runtime::GetCurrent();
199     auto etx = runtime->GetClassLinker()->GetExtension(runtime->GetLanguageContext(runtime->GetRuntimeType()));
200     auto klass = etx->CreateClass(reinterpret_cast<const uint8_t *>(class_name.data()), 0, 0,
201                                   AlignUp(sizeof(Class), OBJECT_POINTER_SIZE));
202     if (thread != nullptr) {
203         thread->ManagedCodeEnd();
204     }
205 
206     klass->SetFileId(panda_file::File::EntityId(13));
207     aot_builder.StartClass(*klass);
208 
209     Method method1(klass, nullptr, File::EntityId(method1_id), File::EntityId(), 0, 1, nullptr);
210     {
211         CodeInfoBuilder code_builder(RUNTIME_ARCH, GetAllocator());
212         ArenaVector<uint8_t> data(GetAllocator()->Adapter());
213         code_builder.Encode(&data);
214         CompiledMethod compiled_method1(RUNTIME_ARCH, &method1);
215         compiled_method1.SetCode(Span(reinterpret_cast<const uint8_t *>(method_name.data()), method_name.size() + 1));
216         compiled_method1.SetCodeInfo(Span(data).ToConst());
217         aot_builder.AddMethod(compiled_method1, 0);
218     }
219 
220     Method method2(klass, nullptr, File::EntityId(method2_id), File::EntityId(), 0, 1, nullptr);
221     {
222         CodeInfoBuilder code_builder(RUNTIME_ARCH, GetAllocator());
223         ArenaVector<uint8_t> data(GetAllocator()->Adapter());
224         code_builder.Encode(&data);
225         CompiledMethod compiled_method2(RUNTIME_ARCH, &method2);
226         compiled_method2.SetCode(Span(reinterpret_cast<const uint8_t *>(x86_add.data()), x86_add.size()));
227         compiled_method2.SetCodeInfo(Span(data).ToConst());
228         aot_builder.AddMethod(compiled_method2, 1);
229     }
230 
231     aot_builder.EndClass();
232     uint32_t hash = GetHash32String(reinterpret_cast<const uint8_t *>(class_name.data()));
233     aot_builder.InsertEntityPairHeader(hash, 13);
234     aot_builder.InsertClassHashTableSize(1);
235     aot_builder.EndFile();
236 
237     aot_builder.Write(cmdline, tmpfile.c_str());
238 
239     AotManager aot_manager;
240     auto res = aot_manager.AddFile(tmpfile.c_str(), nullptr, static_cast<uint32_t>(mem::GCType::STW_GC));
241     ASSERT_TRUE(res) << res.Error();
242 
243     auto aot_file = aot_manager.GetFile(tmpfile.c_str());
244     ASSERT_TRUE(aot_file);
245     ASSERT_TRUE(strcmp(cmdline, aot_file->GetCommandLine()) == 0U);
246     ASSERT_TRUE(strcmp(tmpfile.c_str(), aot_file->GetFileName()) == 0U);
247     ASSERT_EQ(aot_file->GetFilesCount(), 1U);
248 
249     auto pfile = aot_manager.FindPandaFile(tmpfile_pf);
250     ASSERT_NE(pfile, nullptr);
251     auto cls = pfile->GetClass(13);
252     ASSERT_TRUE(cls.IsValid());
253 
254     {
255         auto code = cls.FindMethodCodeEntry(0);
256         ASSERT_FALSE(code == nullptr);
257         ASSERT_EQ(method_name, reinterpret_cast<const char *>(code));
258     }
259 
260     {
261         auto code = cls.FindMethodCodeEntry(1);
262         ASSERT_FALSE(code == nullptr);
263         ASSERT_EQ(std::memcmp(x86_add.data(), code, x86_add.size()), 0);
264 #ifdef PANDA_TARGET_AMD64
265         auto func_add = (int (*)(int, int))code;
266         ASSERT_EQ(func_add(2, 3), 5);
267 #endif
268     }
269 }
270 
TEST_F(AotTest,PaocSpecifyMethods)271 TEST_F(AotTest, PaocSpecifyMethods)
272 {
273 #ifndef PANDA_EVENTS_ENABLED
274     GTEST_SKIP();
275 #endif
276 
277     // Test basic functionality only in host mode.
278     if (RUNTIME_ARCH != Arch::X86_64) {
279         return;
280     }
281     TmpFile panda_fname("test.pf");
282     TmpFile paoc_output_name("events-out.csv");
283 
284     static const std::string location = "/data/local/tmp";
285     static const std::string panda_file_path = location + "/" + panda_fname.GetFileName();
286 
287     auto source = R"(
288         .record A {}
289         .record B {}
290 
291         .function i32 A.f1() {
292             ldai 10
293             return
294         }
295 
296         .function i32 B.f1() {
297             ldai 20
298             return
299         }
300 
301         .function i32 A.f2() {
302             ldai 10
303             return
304         }
305 
306         .function i32 B.f2() {
307             ldai 20
308             return
309         }
310 
311         .function i32 main() {
312             ldai 0
313             return
314         }
315     )";
316 
317     {
318         pandasm::Parser parser;
319         auto res = parser.Parse(source);
320         ASSERT_TRUE(res);
321         ASSERT_TRUE(pandasm::AsmEmitter::Emit(panda_fname.GetFileName(), res.Value()));
322     }
323 
324     {
325         // paoc will try compiling all the methods from the panda-file that matches `--compiler-regex`
326         auto res =
327             os::exec::Exec(paoc_path_.c_str(), "--paoc-panda-files", panda_fname.GetFileName(),
328                            "--compiler-regex", "B::f1",
329                            "--paoc-mode=jit", "--events-output=csv",
330                            "--events-file", paoc_output_name.GetFileName());
331         ASSERT_TRUE(res) << "paoc failed with error: " << res.Error().ToString();
332         ASSERT_EQ(res.Value(), 0);
333 
334         std::ifstream infile(paoc_output_name.GetFileName());
335         std::regex rgx("Compilation,B::f1.*,COMPILED");
336         for (std::string line; std::getline(infile, line);) {
337             if (line.rfind("Compilation", 0) == 0) {
338                 ASSERT_TRUE(std::regex_match(line, rgx));
339             }
340         }
341     }
342 }
343 
TEST_F(AotTest,PaocMultipleFiles)344 TEST_F(AotTest, PaocMultipleFiles)
345 {
346     if (RUNTIME_ARCH != Arch::X86_64) {
347         GTEST_SKIP();
348     }
349 
350     TmpFile aot_fname("./test.an");
351     TmpFile panda_fname1("test1.pf");
352     TmpFile panda_fname2("test2.pf");
353 
354     {
355         auto source = R"(
356             .function f64 main() {
357                 fldai.64 3.1415926
358                 return.64
359             }
360         )";
361 
362         pandasm::Parser parser;
363         auto res = parser.Parse(source);
364         ASSERT_TRUE(res);
365         ASSERT_TRUE(pandasm::AsmEmitter::Emit(panda_fname1.GetFileName(), res.Value()));
366     }
367 
368     {
369         auto source = R"(
370             .record MyMath {
371             }
372 
373             .function f64 MyMath.getPi() <static> {
374                 fldai.64 3.1415926
375                 return.64
376             }
377         )";
378 
379         pandasm::Parser parser;
380         auto res = parser.Parse(source);
381         ASSERT_TRUE(res);
382         ASSERT_TRUE(pandasm::AsmEmitter::Emit(panda_fname2.GetFileName(), res.Value()));
383     }
384 
385     {
386         std::stringstream panda_files;
387         panda_files << panda_fname1.GetFileName() << ',' << panda_fname2.GetFileName();
388         auto res = os::exec::Exec(paoc_path_.c_str(), "--paoc-panda-files", panda_files.str().c_str(), "--paoc-output",
389                                   aot_fname.GetFileName(), "--gc-type=epsilon", "--paoc-use-cha=false");
390         ASSERT_TRUE(res) << "paoc failed with error: " << res.Error().ToString();
391         ASSERT_EQ(res.Value(), 0);
392     }
393 
394     {
395         AotManager aot_manager;
396         auto res =
397             aot_manager.AddFile(aot_fname.GetFileName(), nullptr, static_cast<uint32_t>(mem::GCType::EPSILON_GC));
398         ASSERT_TRUE(res) << res.Error();
399 
400         auto aot_file = aot_manager.GetFile(aot_fname.GetFileName());
401         ASSERT_TRUE(aot_file);
402         ASSERT_EQ(aot_file->GetFilesCount(), 2U);
403     }
404     RunAotdump(aot_fname.GetFileName());
405 }
406 
TEST_F(AotTest,PaocGcType)407 TEST_F(AotTest, PaocGcType)
408 {
409     if (RUNTIME_ARCH != Arch::X86_64) {
410         GTEST_SKIP();
411     }
412 
413     TmpFile aot_fname("./test.pn");
414     TmpFile panda_fname("test.pf");
415 
416     {
417         auto source = R"(
418             .function f64 main() {
419                 fldai.64 3.1415926
420                 return.64
421             }
422         )";
423 
424         pandasm::Parser parser;
425         auto res = parser.Parse(source);
426         ASSERT_TRUE(res);
427         ASSERT_TRUE(pandasm::AsmEmitter::Emit(panda_fname.GetFileName(), res.Value()));
428     }
429 
430     {
431         auto res = os::exec::Exec(paoc_path_.c_str(), "--paoc-panda-files", panda_fname.GetFileName(), "--paoc-output",
432                                   aot_fname.GetFileName(), "--gc-type=epsilon", "--paoc-use-cha=false");
433         ASSERT_TRUE(res) << "paoc failed with error: " << res.Error().ToString();
434         ASSERT_EQ(res.Value(), 0);
435     }
436 
437     {
438         // Wrong gc-type
439         AotManager aot_manager;
440         auto res = aot_manager.AddFile(aot_fname.GetFileName(), nullptr, static_cast<uint32_t>(mem::GCType::STW_GC));
441         ASSERT_FALSE(res) << res.Error();
442         std::string expected_string = "Wrong AotHeader gc-type: epsilon vs stw";
443         ASSERT_NE(res.Error().find(expected_string), std::string::npos);
444     }
445 
446     {
447         AotManager aot_manager;
448         auto res =
449             aot_manager.AddFile(aot_fname.GetFileName(), nullptr, static_cast<uint32_t>(mem::GCType::EPSILON_GC));
450         ASSERT_TRUE(res) << res.Error();
451 
452         auto aot_file = aot_manager.GetFile(aot_fname.GetFileName());
453         ASSERT_TRUE(aot_file);
454         ASSERT_EQ(aot_file->GetFilesCount(), 1U);
455     }
456     RunAotdump(aot_fname.GetFileName());
457 }
458 
TEST_F(AotTest,FileManagerLoadAbc)459 TEST_F(AotTest, FileManagerLoadAbc)
460 {
461     if (RUNTIME_ARCH != Arch::X86_64) {
462         GTEST_SKIP();
463     }
464 
465     TmpFile aot_fname("./test.an");
466     TmpFile panda_fname("./test.pf");
467 
468     {
469         auto source = R"(
470             .function f64 main() {
471                 fldai.64 3.1415926
472                 return.64
473             }
474         )";
475 
476         pandasm::Parser parser;
477         auto res = parser.Parse(source);
478         ASSERT_TRUE(res);
479         ASSERT_TRUE(pandasm::AsmEmitter::Emit(panda_fname.GetFileName(), res.Value()));
480     }
481 
482     {
483         auto runtime = Runtime::GetCurrent();
484         auto gc_type_id = static_cast<uint32_t>(
485             Runtime::GetGCType(runtime->GetOptions(), plugins::RuntimeTypeToLang(runtime->GetRuntimeType())));
486         auto gc_type_name = "--gc-type=epsilon";
487         if (gc_type_id == 2) {
488             gc_type_name = "--gc-type=stw";
489         } else if (gc_type_id == 4) {
490             gc_type_name = "--gc-type=gen-gc";
491         } else {
492             ASSERT_EQ(gc_type_id, 1) << "Invalid GC type\n";
493         }
494         auto res = os::exec::Exec(paoc_path_.c_str(), "--paoc-panda-files", panda_fname.GetFileName(), "--paoc-output",
495                                   aot_fname.GetFileName(), gc_type_name, "--paoc-use-cha=false");
496         ASSERT_TRUE(res) << "paoc failed with error: " << res.Error().ToString();
497         ASSERT_EQ(res.Value(), 0);
498     }
499 
500     {
501         auto res = FileManager::LoadAbcFile(panda_fname.GetFileName(), panda_file::File::READ_ONLY);
502         ASSERT_TRUE(res);
503         auto aot_manager = Runtime::GetCurrent()->GetClassLinker()->GetAotManager();
504         auto aot_file = aot_manager->GetFile(aot_fname.GetFileName());
505         ASSERT_TRUE(aot_file);
506         ASSERT_EQ(aot_file->GetFilesCount(), 1);
507     }
508     RunAotdump(aot_fname.GetFileName());
509 }
510 
TEST_F(AotTest,FileManagerLoadAn)511 TEST_F(AotTest, FileManagerLoadAn)
512 {
513     if (RUNTIME_ARCH == Arch::AARCH32) {
514         // TODO(msherstennikov): for some reason dlopen cannot open aot file in qemu-arm
515         return;
516     }
517     uint32_t tid = os::thread::GetCurrentThreadId();
518     std::string tmpfile = helpers::string::Format("test.an", tid);
519     static constexpr const char *tmpfile_pf = "test.pf";
520     static constexpr const char *cmdline = "cmdline";
521     static constexpr uint32_t method1_id = 42;
522     static constexpr uint32_t method2_id = 43;
523     const std::string class_name("Foo");
524     std::string method_name(class_name + "::method");
525     std::array<uint8_t, 4> x86_add = {
526         0x8d, 0x04, 0x37,  // lea    eax,[rdi+rdi*1]
527         0xc3               // ret
528     };
529 
530     AotBuilder aot_builder;
531     aot_builder.SetArch(RUNTIME_ARCH);
532     RuntimeInterfaceMock iruntime;
533     aot_builder.SetRuntime(&iruntime);
534     auto runtime = Runtime::GetCurrent();
535     auto gc_type = Runtime::GetGCType(runtime->GetOptions(), plugins::RuntimeTypeToLang(runtime->GetRuntimeType()));
536     aot_builder.SetGcType(static_cast<uint32_t>(gc_type));
537 
538     aot_builder.StartFile(tmpfile_pf, 0x12345678);
539 
540     auto thread = MTManagedThread::GetCurrent();
541     if (thread != nullptr) {
542         thread->ManagedCodeBegin();
543     }
544     auto etx = runtime->GetClassLinker()->GetExtension(runtime->GetLanguageContext(runtime->GetRuntimeType()));
545     auto klass = etx->CreateClass(reinterpret_cast<const uint8_t *>(class_name.data()), 0, 0,
546                                   AlignUp(sizeof(Class), OBJECT_POINTER_SIZE));
547     if (thread != nullptr) {
548         thread->ManagedCodeEnd();
549     }
550 
551     klass->SetFileId(panda_file::File::EntityId(13));
552     aot_builder.StartClass(*klass);
553 
554     Method method1(klass, nullptr, File::EntityId(method1_id), File::EntityId(), 0, 1, nullptr);
555     {
556         CodeInfoBuilder code_builder(RUNTIME_ARCH, GetAllocator());
557         ArenaVector<uint8_t> data(GetAllocator()->Adapter());
558         code_builder.Encode(&data);
559         CompiledMethod compiled_method1(RUNTIME_ARCH, &method1);
560         compiled_method1.SetCode(Span(reinterpret_cast<const uint8_t *>(method_name.data()), method_name.size() + 1));
561         compiled_method1.SetCodeInfo(Span(data).ToConst());
562         aot_builder.AddMethod(compiled_method1, 0);
563     }
564 
565     Method method2(klass, nullptr, File::EntityId(method2_id), File::EntityId(), 0, 1, nullptr);
566     {
567         CodeInfoBuilder code_builder(RUNTIME_ARCH, GetAllocator());
568         ArenaVector<uint8_t> data(GetAllocator()->Adapter());
569         code_builder.Encode(&data);
570         CompiledMethod compiled_method2(RUNTIME_ARCH, &method2);
571         compiled_method2.SetCode(Span(reinterpret_cast<const uint8_t *>(x86_add.data()), x86_add.size()));
572         compiled_method2.SetCodeInfo(Span(data).ToConst());
573         aot_builder.AddMethod(compiled_method2, 1);
574     }
575 
576     aot_builder.EndClass();
577     uint32_t hash = GetHash32String(reinterpret_cast<const uint8_t *>(class_name.data()));
578     aot_builder.InsertEntityPairHeader(hash, 13);
579     aot_builder.InsertClassHashTableSize(1);
580     aot_builder.EndFile();
581 
582     aot_builder.Write(cmdline, tmpfile.c_str());
583     {
584         auto res = FileManager::LoadAnFile(tmpfile.c_str());
585         ASSERT_TRUE(res) << "Fail to load an file";
586     }
587 
588     auto aot_manager = Runtime::GetCurrent()->GetClassLinker()->GetAotManager();
589     auto aot_file = aot_manager->GetFile(tmpfile.c_str());
590     ASSERT_TRUE(aot_file);
591     ASSERT_TRUE(strcmp(cmdline, aot_file->GetCommandLine()) == 0U);
592     ASSERT_TRUE(strcmp(tmpfile.c_str(), aot_file->GetFileName()) == 0U);
593     ASSERT_EQ(aot_file->GetFilesCount(), 1U);
594 
595     auto pfile = aot_manager->FindPandaFile(tmpfile_pf);
596     ASSERT_NE(pfile, nullptr);
597     auto cls = pfile->GetClass(13);
598     ASSERT_TRUE(cls.IsValid());
599 
600     {
601         auto code = cls.FindMethodCodeEntry(0);
602         ASSERT_FALSE(code == nullptr);
603         ASSERT_EQ(method_name, reinterpret_cast<const char *>(code));
604     }
605 
606     {
607         auto code = cls.FindMethodCodeEntry(1);
608         ASSERT_FALSE(code == nullptr);
609         ASSERT_EQ(std::memcmp(x86_add.data(), code, x86_add.size()), 0);
610 #ifdef PANDA_TARGET_AMD64
611         auto func_add = (int (*)(int, int))code;
612         ASSERT_EQ(func_add(2, 3), 5);
613 #endif
614     }
615 }
616 
TEST_F(AotTest,PaocClusters)617 TEST_F(AotTest, PaocClusters)
618 {
619     // Test basic functionality only in host mode.
620     if (RUNTIME_ARCH != Arch::X86_64) {
621         return;
622     }
623 
624     TmpFile paoc_clusters("clusters.json");
625     std::ofstream(paoc_clusters.GetFileName()) <<
626         R"(
627     {
628         "clusters_map" :
629         {
630             "A::count" : ["unroll_enable"],
631             "B::count2" : ["unroll_disable"],
632             "_GLOBAL::main" : ["inline_disable", 1]
633         },
634 
635         "compiler_options" :
636         {
637             "unroll_disable" :
638             {
639                 "compiler-loop-unroll" : "false"
640             },
641 
642             "unroll_enable" :
643             {
644                 "compiler-loop-unroll" : "true",
645                 "compiler-loop-unroll-factor" : 42,
646                 "compiler-loop-unroll-inst-limit" : 850
647             },
648 
649             "inline_disable" :
650             {
651                 "compiler-inlining" : "false"
652             }
653         }
654     }
655     )";
656 
657     TmpFile panda_fname("test.pf");
658     auto source = R"(
659         .record A {}
660         .record B {}
661 
662         .function i32 A.count() <static> {
663             movi v1, 5
664             ldai 0
665         main_loop:
666             jeq v1, main_ret
667             addi 1
668             jmp main_loop
669         main_ret:
670             return
671         }
672 
673         .function i32 B.count() <static> {
674             movi v1, 5
675             ldai 0
676         main_loop:
677             jeq v1, main_ret
678             addi 1
679             jmp main_loop
680         main_ret:
681             return
682         }
683 
684         .function i32 B.count2() <static> {
685             movi v1, 5
686             ldai 0
687         main_loop:
688             jeq v1, main_ret
689             addi 1
690             jmp main_loop
691         main_ret:
692             return
693         }
694 
695         .function i32 main() {
696             call.short A.count
697             sta v0
698             call.short B.count
699             add2 v0
700             call.short B.count2
701             add2 v0
702             return
703         }
704     )";
705 
706     {
707         pandasm::Parser parser;
708         auto res = parser.Parse(source);
709         ASSERT_TRUE(res);
710         ASSERT_TRUE(pandasm::AsmEmitter::Emit(panda_fname.GetFileName(), res.Value()));
711     }
712 
713     {
714         TmpFile compiler_events("events.csv");
715         auto res =
716             os::exec::Exec(paoc_path_.c_str(), "--paoc-panda-files", panda_fname.GetFileName(), "--paoc-clusters",
717                            paoc_clusters.GetFileName(), "--compiler-loop-unroll-factor=7",
718                            "--compiler-enable-events=true", "--compiler-events-path", compiler_events.GetFileName());
719         ASSERT_TRUE(res) << "paoc failed with error: " << res.Error().ToString();
720         ASSERT_EQ(res.Value(), 0);
721 
722         bool first_found = false;
723         bool second_found = false;
724         std::ifstream events_file(compiler_events.GetFileName());
725 
726         std::regex rgx_unroll_applied_cluster("A::count,loop-unroll,.*,unroll_factor:42,.*");
727         std::regex rgx_unroll_restored_default("B::count,loop-unroll,.*,unroll_factor:7,.*");
728 
729         for (std::string line; std::getline(events_file, line);) {
730             if (line.rfind("loop-unroll") != std::string::npos) {
731                 if (!first_found) {
732                     // Check that the cluster is applied:
733                     ASSERT_TRUE(std::regex_match(line, rgx_unroll_applied_cluster));
734                     first_found = true;
735                     continue;
736                 }
737                 ASSERT_FALSE(second_found);
738                 // Check that the option is restored:
739                 ASSERT_TRUE(std::regex_match(line, rgx_unroll_restored_default));
740                 second_found = true;
741             }
742         }
743         ASSERT_TRUE(first_found && second_found);
744     }
745 }
746 
TEST_F(AotTest,PandaFiles)747 TEST_F(AotTest, PandaFiles)
748 {
749 #ifndef PANDA_EVENTS_ENABLED
750     GTEST_SKIP();
751 #endif
752 
753     if (RUNTIME_ARCH != Arch::X86_64) {
754         GTEST_SKIP();
755     }
756 
757     TmpFile aot_fname("./test.an");
758     TmpFile panda_fname1("test1.pf");
759     TmpFile panda_fname2("test2.pf");
760     TmpFile paoc_output_name("events-out.csv");
761 
762     {
763         auto source = R"(
764             .record Z {}
765             .function i32 Z.zoo() <static> {
766                 ldai 45
767                 return
768             }
769         )";
770 
771         pandasm::Parser parser;
772         auto res = parser.Parse(source);
773         ASSERT_TRUE(res);
774         ASSERT_TRUE(pandasm::AsmEmitter::Emit(panda_fname1.GetFileName(), res.Value()));
775     }
776 
777     {
778         auto source = R"(
779             .record Z <external>
780             .function i32 Z.zoo() <external, static>
781             .record X {}
782             .function i32 X.main() {
783                 call.short Z.zoo
784                 return
785             }
786         )";
787 
788         pandasm::Parser parser;
789         auto res = parser.Parse(source);
790         ASSERT_TRUE(res);
791         ASSERT_TRUE(pandasm::AsmEmitter::Emit(panda_fname2.GetFileName(), res.Value()));
792     }
793 
794     {
795         std::stringstream panda_files;
796         panda_files << panda_fname1.GetFileName() << ',' << panda_fname2.GetFileName();
797         auto res = os::exec::Exec(paoc_path_.c_str(), "--paoc-panda-files", panda_fname2.GetFileName(), "--panda-files",
798                                   panda_fname1.GetFileName(), "--events-output=csv", "--events-file",
799                                   paoc_output_name.GetFileName());
800         ASSERT_TRUE(res) << "paoc failed with error: " << res.Error().ToString();
801         ASSERT_EQ(res.Value(), 0);
802 
803         std::ifstream infile(paoc_output_name.GetFileName());
804         // Inlining attempt proofs that Z::zoo was available to inline
805         std::regex rgx("Inline,.*Z::zoo.*");
806         bool inline_attempt = false;
807         for (std::string line; std::getline(infile, line);) {
808             inline_attempt |= std::regex_match(line, rgx);
809         }
810         ASSERT_TRUE(inline_attempt);
811     }
812 }
813 
814 }  // namespace panda::compiler
815