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 31using panda::panda_file::File; 32 33namespace panda::compiler { 34class AotTest : public AsmTest { 35public: 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 44 std::string GetPaocDirectory() const 45 { 46 auto pos = paoc_path_.rfind('/'); 47 return paoc_path_.substr(0, pos); 48 } 49 50 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 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_; 78protected: 79 Arch target_arch = Arch::AARCH64; 80}; 81 82#ifdef PANDA_COMPILER_TARGET_AARCH64 83TEST_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 119TEST_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 167TEST_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 271TEST_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 344TEST_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 407TEST_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 459TEST_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 511TEST_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 617TEST_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 747TEST_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