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