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 #ifndef BYTECODE_OPTIMIZER_TESTS_COMMON_H 17 #define BYTECODE_OPTIMIZER_TESTS_COMMON_H 18 19 #include <gtest/gtest.h> 20 #include <string> 21 #include <string_view> 22 23 #include "assembler/assembly-emitter.h" 24 #include "assembler/assembly-parser.h" 25 #include "assembler/assembly-program.h" 26 #include "assembler/extensions/extensions.h" 27 #include "canonicalization.h" 28 #include "class_data_accessor-inl.h" 29 #include "codegen.h" 30 #include "compiler/compiler_options.h" 31 #include "compiler/optimizer/analysis/rpo.h" 32 #include "compiler/optimizer/ir/datatype.h" 33 #include "compiler/optimizer/ir/inst.h" 34 #include "compiler/optimizer/ir/ir_constructor.h" 35 #include "compiler/optimizer/ir_builder/ir_builder.h" 36 #include "compiler/optimizer/optimizations/cleanup.h" 37 #include "compiler/optimizer/optimizations/lowering.h" 38 #include "compiler/optimizer/optimizations/regalloc/reg_alloc_linear_scan.h" 39 #include "file_items.h" 40 #include "ir_interface.h" 41 #include "libpandabase/utils/logger.h" 42 #include "mem/arena_allocator.h" 43 #include "mem/pool_manager.h" 44 #include "method_data_accessor-inl.h" 45 #include "optimize_bytecode.h" 46 #include "reg_encoder.h" 47 #include "runtime_adapter.h" 48 49 namespace panda::bytecodeopt { 50 51 using compiler::BasicBlock; 52 using compiler::Graph; 53 using compiler::Input; 54 using compiler::Inst; 55 using compiler::Opcode; 56 57 struct RuntimeInterfaceMock : public compiler::RuntimeInterface { 58 size_t argument_count {0}; 59 bool is_constructor {true}; 60 RuntimeInterfaceMockRuntimeInterfaceMock61 explicit RuntimeInterfaceMock(size_t arg_count) : RuntimeInterfaceMock(arg_count, true) {} 62 RuntimeInterfaceMockRuntimeInterfaceMock63 RuntimeInterfaceMock(size_t arg_count, bool is_ctor) : argument_count(arg_count), is_constructor(is_ctor) {} 64 GetMethodTotalArgumentsCountRuntimeInterfaceMock65 size_t GetMethodTotalArgumentsCount([[maybe_unused]] MethodPtr method) const override 66 { 67 return argument_count; 68 } 69 IsConstructorRuntimeInterfaceMock70 bool IsConstructor([[maybe_unused]] MethodPtr method, [[maybe_unused]] uint32_t class_id) override 71 { 72 return is_constructor; 73 } 74 }; 75 76 class IrInterfaceTest : public BytecodeOptIrInterface { 77 public: 78 explicit IrInterfaceTest(pandasm::Program *prog = nullptr, 79 const pandasm::AsmEmitter::PandaFileToPandaAsmMaps *maps = nullptr) BytecodeOptIrInterface(maps,prog)80 : BytecodeOptIrInterface(maps, prog) 81 { 82 } 83 GetFieldIdByOffset(uint32_t offset)84 std::string GetFieldIdByOffset([[maybe_unused]] uint32_t offset) const override 85 { 86 return ""; 87 } 88 GetTypeIdByOffset(uint32_t offset)89 std::string GetTypeIdByOffset([[maybe_unused]] uint32_t offset) const override 90 { 91 return IsMapsSet() ? BytecodeOptIrInterface::GetTypeIdByOffset(offset) : ""; 92 } 93 GetMethodIdByOffset(uint32_t offset)94 std::string GetMethodIdByOffset([[maybe_unused]] uint32_t offset) const override 95 { 96 return ""; 97 } 98 GetStringIdByOffset(uint32_t offset)99 std::string GetStringIdByOffset([[maybe_unused]] uint32_t offset) const override 100 { 101 return ""; 102 } 103 }; 104 105 namespace test { 106 107 extern std::string glob_argv0; 108 109 } // namespace test 110 111 class CommonTest : public ::testing::Test { 112 public: CommonTest()113 CommonTest() 114 { 115 compiler::options.SetCompilerUseSafepoint(false); 116 compiler::options.SetCompilerSupportInitObjectInst(true); 117 118 mem::MemConfig::Initialize(128_MB, 64_MB, 64_MB, 32_MB); 119 PoolManager::Initialize(); 120 allocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_INTERNAL); 121 local_allocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_INTERNAL); 122 builder_ = new compiler::IrConstructor(); 123 124 Logger::InitializeStdLogging(Logger::Level::ERROR, 125 panda::Logger::ComponentMask().set(Logger::Component::BYTECODE_OPTIMIZER)); 126 } ~CommonTest()127 virtual ~CommonTest() 128 { 129 delete allocator_; 130 delete local_allocator_; 131 delete builder_; 132 PoolManager::Finalize(); 133 mem::MemConfig::Finalize(); 134 135 Logger::Destroy(); 136 } GetAllocator()137 ArenaAllocator *GetAllocator() 138 { 139 return allocator_; 140 } GetLocalAllocator()141 ArenaAllocator *GetLocalAllocator() 142 { 143 return local_allocator_; 144 } 145 146 compiler::Graph *CreateEmptyGraph(bool isDynamic = false) 147 { 148 auto *graph = 149 GetAllocator()->New<compiler::Graph>(GetAllocator(), GetLocalAllocator(), Arch::NONE, isDynamic, true); 150 return graph; 151 } 152 GetGraph()153 compiler::Graph *GetGraph() 154 { 155 return graph_; 156 } 157 SetGraph(compiler::Graph * graph)158 void SetGraph(compiler::Graph *graph) 159 { 160 graph_ = graph; 161 } 162 FuncHasInst(pandasm::Function * func,pandasm::Opcode opcode)163 bool FuncHasInst(pandasm::Function *func, pandasm::Opcode opcode) 164 { 165 for (const auto &inst : func->ins) { 166 if (inst.opcode == opcode) { 167 return true; 168 } 169 } 170 return false; 171 } 172 173 protected: 174 compiler::IrConstructor *builder_; 175 176 private: 177 ArenaAllocator *allocator_; 178 ArenaAllocator *local_allocator_; 179 compiler::Graph *graph_ {nullptr}; 180 }; 181 182 class AsmTest : public CommonTest { 183 public: 184 bool ParseToGraph(const std::string &source, const std::string &func_name, const char *file_name = "test.pb") 185 { 186 panda::pandasm::Parser parser; 187 auto res = parser.Parse(source, file_name); 188 if (parser.ShowError().err != pandasm::Error::ErrorType::ERR_NONE) { 189 std::cerr << "Parse failed: " << parser.ShowError().message << std::endl 190 << parser.ShowError().whole_line << std::endl; 191 ADD_FAILURE(); 192 return false; 193 } 194 auto &prog = res.Value(); 195 return ParseToGraph(&prog, func_name); 196 } 197 ParseToGraph(pandasm::Program * prog,const std::string & func_name)198 bool ParseToGraph(pandasm::Program *prog, const std::string &func_name) 199 { 200 pfile_ = pandasm::AsmEmitter::Emit(*prog, &maps_); 201 ir_interface_ = std::make_unique<bytecodeopt::BytecodeOptIrInterface>(&maps_, prog); 202 203 if (pfile_ == nullptr) { 204 ADD_FAILURE(); 205 return false; 206 } 207 208 auto ptr_file = pfile_.get(); 209 if (ptr_file == nullptr) { 210 ADD_FAILURE(); 211 return false; 212 } 213 214 compiler::Graph *temp_graph = nullptr; 215 216 for (uint32_t id : ptr_file->GetClasses()) { 217 panda_file::File::EntityId record_id {id}; 218 219 if (ptr_file->IsExternal(record_id)) { 220 continue; 221 } 222 223 panda_file::ClassDataAccessor cda {*ptr_file, record_id}; 224 cda.EnumerateMethods([&temp_graph, ptr_file, func_name, this](panda_file::MethodDataAccessor &mda) { 225 auto name_id = mda.GetNameId(); 226 auto str = ptr_file->GetStringData(name_id).data; 227 bool is_equal = (std::string(func_name) == std::string(reinterpret_cast<const char *>(str))); 228 auto method_ptr = 229 reinterpret_cast<compiler::RuntimeInterface::MethodPtr>(mda.GetMethodId().GetOffset()); 230 231 if (!mda.IsExternal() && !mda.IsAbstract() && !mda.IsNative() && is_equal) { 232 auto adapter = allocator_.New<BytecodeOptimizerRuntimeAdapter>(mda.GetPandaFile()); 233 temp_graph = allocator_.New<compiler::Graph>(&allocator_, &local_allocator_, Arch::NONE, method_ptr, 234 adapter, false, nullptr, false, true); 235 ASSERT_NE(temp_graph, nullptr); 236 ASSERT_TRUE(temp_graph->RunPass<compiler::IrBuilder>()); 237 } 238 }); 239 } 240 241 if (temp_graph != nullptr) { 242 SetGraph(temp_graph); 243 return true; 244 } 245 return false; 246 } 247 GetIrInterface()248 bytecodeopt::BytecodeOptIrInterface *GetIrInterface() 249 { 250 return ir_interface_.get(); 251 } 252 GetMaps()253 pandasm::AsmEmitter::PandaFileToPandaAsmMaps *GetMaps() 254 { 255 return &maps_; 256 } 257 GetFile()258 auto GetFile() 259 { 260 return pfile_.get(); 261 } 262 263 private: 264 std::unique_ptr<BytecodeOptIrInterface> ir_interface_; 265 pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps_; 266 ArenaAllocator allocator_ {SpaceType::SPACE_TYPE_COMPILER}; 267 ArenaAllocator local_allocator_ {SpaceType::SPACE_TYPE_COMPILER}; 268 std::unique_ptr<const panda_file::File> pfile_ {nullptr}; 269 }; 270 271 class GraphComparator { 272 public: Compare(Graph * graph1,Graph * graph2)273 bool Compare(Graph *graph1, Graph *graph2) 274 { 275 graph1->InvalidateAnalysis<compiler::Rpo>(); 276 graph2->InvalidateAnalysis<compiler::Rpo>(); 277 if (graph1->GetBlocksRPO().size() != graph2->GetBlocksRPO().size()) { 278 std::cerr << "Different number of blocks: " << graph1->GetBlocksRPO().size() << " and " 279 << graph2->GetBlocksRPO().size() << std::endl; 280 return false; 281 } 282 return std::equal(graph1->GetBlocksRPO().begin(), graph1->GetBlocksRPO().end(), graph2->GetBlocksRPO().begin(), 283 graph2->GetBlocksRPO().end(), [this](auto bb1, auto bb2) { return Compare(bb1, bb2); }); 284 } 285 Compare(BasicBlock * block1,BasicBlock * block2)286 bool Compare(BasicBlock *block1, BasicBlock *block2) 287 { 288 if (block1->GetPredsBlocks().size() != block2->GetPredsBlocks().size()) { 289 std::cerr << "Different number of preds blocks\n"; 290 block1->Dump(&std::cout); 291 block2->Dump(&std::cout); 292 return false; 293 } 294 if (block1->GetSuccsBlocks().size() != block2->GetSuccsBlocks().size()) { 295 std::cerr << "Different number of succs blocks\n"; 296 block1->Dump(&std::cout); 297 block2->Dump(&std::cout); 298 return false; 299 } 300 return std::equal(block1->AllInsts().begin(), block1->AllInsts().end(), block2->AllInsts().begin(), 301 block2->AllInsts().end(), [this](auto inst1, auto inst2) { 302 assert(inst2 != nullptr); 303 bool t = Compare(inst1, inst2); 304 if (!t) { 305 std::cerr << "Different instructions:\n"; 306 inst1->Dump(&std::cout); 307 inst2->Dump(&std::cout); 308 } 309 return t; 310 }); 311 } 312 Compare(Inst * inst1,Inst * inst2)313 bool Compare(Inst *inst1, Inst *inst2) 314 { 315 if (auto it = inst_compare_map_.insert({inst1, inst2}); !it.second) { 316 if (inst2 == it.first->second) { 317 return true; 318 } 319 inst_compare_map_.erase(inst1); 320 return false; 321 } 322 323 if (inst1->GetOpcode() != inst2->GetOpcode() || inst1->GetType() != inst2->GetType() || 324 inst1->GetInputsCount() != inst2->GetInputsCount()) { 325 inst_compare_map_.erase(inst1); 326 return false; 327 } 328 329 if (inst1->GetOpcode() == Opcode::Intrinsic || inst1->GetOpcode() == Opcode::Builtin) { 330 if (inst1->CastToIntrinsic()->GetIntrinsicId() != inst2->CastToIntrinsic()->GetIntrinsicId()) { 331 inst_compare_map_.erase(inst1); 332 return false; 333 } 334 } 335 336 if (inst1->GetOpcode() != Opcode::Phi) { 337 if (!std::equal( 338 inst1->GetInputs().begin(), inst1->GetInputs().end(), inst2->GetInputs().begin(), 339 [this](Input input1, Input input2) { return Compare(input1.GetInst(), input2.GetInst()); })) { 340 inst_compare_map_.erase(inst1); 341 return false; 342 } 343 } else { 344 for (auto input1 : inst1->GetInputs()) { 345 auto it = 346 std::find_if(inst2->GetInputs().begin(), inst2->GetInputs().end(), 347 [this, &input1](Input input2) { return Compare(input1.GetInst(), input2.GetInst()); }); 348 if (it == inst2->GetInputs().end()) { 349 inst_compare_map_.erase(inst1); 350 return false; 351 } 352 } 353 } 354 355 #define CAST(Opc) CastTo##Opc() 356 357 #define CHECK(Opc, Getter) \ 358 if (inst1->GetOpcode() == Opcode::Opc && inst1->CAST(Opc)->Getter() != inst2->CAST(Opc)->Getter()) { \ 359 inst_compare_map_.erase(inst1); \ 360 return false; \ 361 } 362 363 CHECK(Constant, GetRawValue) 364 365 CHECK(Cast, GetOperandsType) 366 CHECK(Cmp, GetOperandsType) 367 368 CHECK(Compare, GetCc) 369 CHECK(Compare, GetOperandsType) 370 371 CHECK(If, GetCc) 372 CHECK(If, GetOperandsType) 373 374 CHECK(IfImm, GetCc) 375 CHECK(IfImm, GetImm) 376 CHECK(IfImm, GetOperandsType) 377 378 CHECK(LoadArrayI, GetImm) 379 CHECK(LoadArrayPairI, GetImm) 380 CHECK(LoadPairPart, GetImm) 381 CHECK(StoreArrayI, GetImm) 382 CHECK(StoreArrayPairI, GetImm) 383 CHECK(BoundsCheckI, GetImm) 384 CHECK(ReturnI, GetImm) 385 CHECK(AddI, GetImm) 386 CHECK(SubI, GetImm) 387 CHECK(ShlI, GetImm) 388 CHECK(ShrI, GetImm) 389 CHECK(AShrI, GetImm) 390 CHECK(AndI, GetImm) 391 CHECK(OrI, GetImm) 392 CHECK(XorI, GetImm) 393 394 CHECK(LoadStatic, GetVolatile) 395 CHECK(StoreStatic, GetVolatile) 396 CHECK(LoadObject, GetVolatile) 397 CHECK(StoreObject, GetVolatile) 398 #undef CHECK 399 #undef CAST 400 401 if (inst1->GetOpcode() == Opcode::Cmp && IsFloatType(inst1->GetInput(0).GetInst()->GetType())) { 402 auto cmp1 = static_cast<compiler::CmpInst *>(inst1); 403 auto cmp2 = static_cast<compiler::CmpInst *>(inst2); 404 if (cmp1->IsFcmpg() != cmp2->IsFcmpg()) { 405 inst_compare_map_.erase(inst1); 406 return false; 407 } 408 } 409 for (uint32_t i = 0; i < inst2->GetInputsCount(); i++) { 410 if (inst1->GetInputType(i) != inst2->GetInputType(i)) { 411 inst_compare_map_.erase(inst1); 412 return false; 413 } 414 } 415 return true; 416 } 417 418 private: 419 std::unordered_map<Inst *, Inst *> inst_compare_map_; 420 }; 421 422 class IrBuilderTest : public AsmTest { 423 public: CheckSimple(std::string inst_name,compiler::DataType::Type data_type,std::string inst_type)424 void CheckSimple(std::string inst_name, compiler::DataType::Type data_type, std::string inst_type) 425 { 426 ASSERT(inst_name == "mov" || inst_name == "lda" || inst_name == "sta"); 427 std::string curr_type; 428 if (data_type == compiler::DataType::Type::REFERENCE) { 429 curr_type = "i64[]"; 430 } else { 431 curr_type = ToString(data_type); 432 } 433 434 std::string source = ".function " + curr_type + " main("; 435 source += curr_type + " a0){\n"; 436 if (inst_name == "mov") { 437 source += "mov" + inst_type + " v0, a0\n"; 438 source += "lda" + inst_type + " v0\n"; 439 } else if (inst_name == "lda") { 440 source += "lda" + inst_type + " a0\n"; 441 } else if (inst_name == "sta") { 442 source += "lda" + inst_type + " a0\n"; 443 source += "sta" + inst_type + " v0\n"; 444 source += "lda" + inst_type + " v0\n"; 445 } else { 446 UNREACHABLE(); 447 } 448 source += "return" + inst_type + "\n"; 449 source += "}"; 450 451 ASSERT_TRUE(ParseToGraph(source, "main")); 452 453 auto graph = CreateEmptyGraph(); 454 GRAPH(graph) 455 { 456 PARAMETER(0, 0); 457 INS(0).SetType(data_type); 458 459 BASIC_BLOCK(2, -1) 460 { 461 INST(1, Opcode::Return).Inputs(0); 462 INS(1).SetType(data_type); 463 } 464 } 465 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph)); 466 } 467 CheckSimpleWithImm(std::string inst_name,compiler::DataType::Type data_type,std::string inst_type)468 void CheckSimpleWithImm(std::string inst_name, compiler::DataType::Type data_type, std::string inst_type) 469 { 470 ASSERT(inst_name == "mov" || inst_name == "fmov" || inst_name == "lda" || inst_name == "flda"); 471 std::string curr_type = ToString(data_type); 472 473 std::string source = ".function " + curr_type + " main(){\n"; 474 if (inst_name == "mov") { 475 source += "movi" + inst_type + " v0, 0\n"; 476 source += "lda" + inst_type + " v0\n"; 477 } else if (inst_name == "fmov") { 478 source += "fmovi" + inst_type + " v0, 0.\n"; 479 source += "lda" + inst_type + " v0\n"; 480 } else if (inst_name == "lda") { 481 source += "ldai" + inst_type + " 0\n"; 482 } else if (inst_name == "flda") { 483 source += "fldai" + inst_type + " 0.\n"; 484 } else { 485 UNREACHABLE(); 486 } 487 source += "return" + inst_type + "\n"; 488 source += "}"; 489 490 ASSERT_TRUE(ParseToGraph(source, "main")); 491 492 auto graph = CreateEmptyGraph(); 493 494 GRAPH(graph) 495 { 496 CONSTANT(0, 0); 497 INS(0).SetType(data_type); 498 499 BASIC_BLOCK(2, -1) 500 { 501 INST(1, Opcode::Return).Inputs(0); 502 INS(1).SetType(data_type); 503 } 504 } 505 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph)); 506 } 507 CheckCmp(std::string inst_name,compiler::DataType::Type data_type,std::string inst_type)508 void CheckCmp(std::string inst_name, compiler::DataType::Type data_type, std::string inst_type) 509 { 510 ASSERT(inst_name == "ucmp" || inst_name == "fcmpl" || inst_name == "fcmpg"); 511 std::string curr_type; 512 if (data_type == compiler::DataType::Type::REFERENCE) { 513 curr_type = "i64[]"; 514 } else { 515 curr_type = ToString(data_type); 516 } 517 std::string source = ".function i32 main("; 518 source += curr_type + " a0, "; 519 source += curr_type + " a1){\n"; 520 source += "lda" + inst_type + " a0\n"; 521 source += inst_name + inst_type + " a1\n"; 522 source += "return\n"; 523 source += "}"; 524 525 ASSERT_TRUE(ParseToGraph(source, "main")); 526 527 auto graph = CreateEmptyGraph(); 528 GRAPH(graph) 529 { 530 PARAMETER(0, 0); 531 INS(0).SetType(data_type); 532 PARAMETER(1, 1); 533 INS(1).SetType(data_type); 534 535 BASIC_BLOCK(2, -1) 536 { 537 INST(2, Opcode::Cmp).s32().Inputs(0, 1); 538 INST(3, Opcode::Return).s32().Inputs(2); 539 } 540 } 541 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph)); 542 } 543 CheckFloatCmp(std::string inst_name,compiler::DataType::Type data_type,std::string inst_type,bool fcmpg)544 void CheckFloatCmp(std::string inst_name, compiler::DataType::Type data_type, std::string inst_type, bool fcmpg) 545 { 546 ASSERT(inst_name == "fcmpl" || inst_name == "fcmpg"); 547 std::string curr_type = ToString(data_type); 548 549 std::string source = ".function i32 main("; 550 source += curr_type + " a0, "; 551 source += curr_type + " a1){\n"; 552 source += "lda" + inst_type + " a0\n"; 553 source += inst_name + inst_type + " a1\n"; 554 source += "return\n"; 555 source += "}"; 556 557 ASSERT_TRUE(ParseToGraph(source, "main")); 558 559 auto graph = CreateEmptyGraph(); 560 GRAPH(graph) 561 { 562 PARAMETER(0, 0); 563 INS(0).SetType(data_type); 564 PARAMETER(1, 1); 565 INS(1).SetType(data_type); 566 567 BASIC_BLOCK(2, -1) 568 { 569 INST(2, Opcode::Cmp).s32().SrcType(data_type).Fcmpg(fcmpg).Inputs(0, 1); 570 INST(3, Opcode::Return).s32().Inputs(2); 571 } 572 } 573 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph)); 574 } 575 576 template <bool is_obj> CheckCondJumpWithZero(compiler::ConditionCode CC)577 void CheckCondJumpWithZero(compiler::ConditionCode CC) 578 { 579 std::string cmd; 580 switch (CC) { 581 case compiler::ConditionCode::CC_EQ: 582 cmd = "jeqz"; 583 break; 584 case compiler::ConditionCode::CC_NE: 585 cmd = "jnez"; 586 break; 587 case compiler::ConditionCode::CC_LT: 588 cmd = "jltz"; 589 break; 590 case compiler::ConditionCode::CC_GT: 591 cmd = "jgtz"; 592 break; 593 case compiler::ConditionCode::CC_LE: 594 cmd = "jlez"; 595 break; 596 case compiler::ConditionCode::CC_GE: 597 cmd = "jgez"; 598 break; 599 default: 600 UNREACHABLE(); 601 } 602 603 std::string inst_postfix = ""; 604 std::string param_type = "i32"; 605 auto type = compiler::DataType::INT32; 606 if constexpr (is_obj) { 607 inst_postfix = ".obj"; 608 param_type = "i64[]"; 609 type = compiler::DataType::REFERENCE; 610 } 611 612 std::string source = ".function void main("; 613 source += param_type + " a0) {\n"; 614 source += "lda" + inst_postfix + " a0\n"; 615 source += cmd + inst_postfix + " label\n"; 616 source += "label: "; 617 source += "return.void\n}"; 618 619 ASSERT_TRUE(ParseToGraph(source, "main")); 620 621 auto graph = CreateEmptyGraph(); 622 GRAPH(graph) 623 { 624 PARAMETER(0, 0); 625 INS(0).SetType(type); 626 CONSTANT(2, 0).s64(); 627 628 BASIC_BLOCK(2, 3, 4) 629 { 630 INST(1, Opcode::Compare).b().CC(CC).Inputs(0, 2); 631 INST(3, Opcode::IfImm) 632 .SrcType(compiler::DataType::BOOL) 633 .CC(compiler::ConditionCode::CC_NE) 634 .Inputs(1) 635 .Imm(0); 636 } 637 BASIC_BLOCK(3, 4) {} 638 BASIC_BLOCK(4, -1) 639 { 640 INST(4, Opcode::ReturnVoid).v0id(); 641 } 642 } 643 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph)); 644 } 645 646 template <bool is_obj> CheckCondJump(compiler::ConditionCode CC)647 void CheckCondJump(compiler::ConditionCode CC) 648 { 649 std::string cmd; 650 switch (CC) { 651 case compiler::ConditionCode::CC_EQ: 652 cmd = "jeq"; 653 break; 654 case compiler::ConditionCode::CC_NE: 655 cmd = "jne"; 656 break; 657 case compiler::ConditionCode::CC_LT: 658 cmd = "jlt"; 659 break; 660 case compiler::ConditionCode::CC_GT: 661 cmd = "jgt"; 662 break; 663 case compiler::ConditionCode::CC_LE: 664 cmd = "jle"; 665 break; 666 case compiler::ConditionCode::CC_GE: 667 cmd = "jge"; 668 break; 669 default: 670 UNREACHABLE(); 671 } 672 std::string inst_postfix = ""; 673 std::string param_type = "i32"; 674 auto type = compiler::DataType::INT32; 675 if constexpr (is_obj) { 676 inst_postfix = ".obj"; 677 param_type = "i64[]"; 678 type = compiler::DataType::REFERENCE; 679 } 680 681 std::string source = ".function void main("; 682 source += param_type + " a0, " + param_type + " a1) {\n"; 683 source += "lda" + inst_postfix + " a0\n"; 684 source += cmd + inst_postfix + " a1, label\n"; 685 source += "label: "; 686 source += "return.void\n}"; 687 688 ASSERT_TRUE(ParseToGraph(source, "main")); 689 690 auto graph = CreateEmptyGraph(); 691 GRAPH(graph) 692 { 693 PARAMETER(0, 0); 694 INS(0).SetType(type); 695 PARAMETER(1, 1); 696 INS(1).SetType(type); 697 698 BASIC_BLOCK(2, 3, 4) 699 { 700 INST(2, Opcode::Compare).b().CC(CC).Inputs(0, 1); 701 INST(3, Opcode::IfImm) 702 .SrcType(compiler::DataType::BOOL) 703 .CC(compiler::ConditionCode::CC_NE) 704 .Imm(0) 705 .Inputs(2); 706 } 707 BASIC_BLOCK(3, 4) {} 708 BASIC_BLOCK(4, -1) 709 { 710 INST(4, Opcode::ReturnVoid).v0id(); 711 } 712 } 713 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph)); 714 } 715 CheckOtherPasses(panda::pandasm::Program * prog,std::string fun_name)716 void CheckOtherPasses(panda::pandasm::Program *prog, std::string fun_name) 717 { 718 GetGraph()->RunPass<compiler::Cleanup>(); 719 GetGraph()->RunPass<Canonicalization>(); 720 #ifndef NDEBUG 721 GetGraph()->SetLowLevelInstructionsEnabled(); 722 #endif 723 GetGraph()->RunPass<compiler::Cleanup>(); 724 GetGraph()->RunPass<compiler::Lowering>(); 725 GetGraph()->RunPass<compiler::Cleanup>(); 726 EXPECT_TRUE(GetGraph()->RunPass<compiler::RegAllocLinearScan>(compiler::EmptyRegMask())); 727 GetGraph()->RunPass<compiler::Cleanup>(); 728 EXPECT_TRUE(GetGraph()->RunPass<RegEncoder>()); 729 ASSERT_TRUE(prog->function_table.find(fun_name) != prog->function_table.end()); 730 auto &function = prog->function_table.at(fun_name); 731 GetGraph()->RunPass<compiler::Cleanup>(); 732 EXPECT_TRUE(GetGraph()->RunPass<BytecodeGen>(&function, GetIrInterface())); 733 auto pf = pandasm::AsmEmitter::Emit(*prog); 734 ASSERT_NE(pf, nullptr); 735 } 736 CheckConstArrayFilling(panda::pandasm::Program * prog,std::string class_name,std::string func_name)737 void CheckConstArrayFilling(panda::pandasm::Program *prog, [[maybe_unused]] std::string class_name, 738 std::string func_name) 739 { 740 if (prog->literalarray_table.size() == 1) { 741 EXPECT_TRUE(prog->literalarray_table["0"].literals_[0].tag_ == panda_file::LiteralTag::TAGVALUE); 742 EXPECT_TRUE(prog->literalarray_table["0"].literals_[1].tag_ == panda_file::LiteralTag::INTEGER); 743 EXPECT_TRUE(prog->literalarray_table["0"].literals_[2].tag_ == panda_file::LiteralTag::ARRAY_I32); 744 return; 745 } 746 EXPECT_TRUE(prog->literalarray_table.size() == 8); 747 for (const auto &elem : prog->literalarray_table) { 748 EXPECT_TRUE(elem.second.literals_.size() == 5); 749 EXPECT_TRUE(elem.second.literals_[0].tag_ == panda_file::LiteralTag::TAGVALUE); 750 EXPECT_TRUE(elem.second.literals_[1].tag_ == panda_file::LiteralTag::INTEGER); 751 } 752 EXPECT_TRUE(prog->literalarray_table["7"].literals_[2].tag_ == panda_file::LiteralTag::ARRAY_U1); 753 EXPECT_TRUE(prog->literalarray_table["6"].literals_[2].tag_ == panda_file::LiteralTag::ARRAY_I8); 754 EXPECT_TRUE(prog->literalarray_table["5"].literals_[2].tag_ == panda_file::LiteralTag::ARRAY_I16); 755 EXPECT_TRUE(prog->literalarray_table["4"].literals_[2].tag_ == panda_file::LiteralTag::ARRAY_I32); 756 EXPECT_TRUE(prog->literalarray_table["3"].literals_[2].tag_ == panda_file::LiteralTag::ARRAY_I64); 757 EXPECT_TRUE(prog->literalarray_table["2"].literals_[2].tag_ == panda_file::LiteralTag::ARRAY_F32); 758 EXPECT_TRUE(prog->literalarray_table["1"].literals_[2].tag_ == panda_file::LiteralTag::ARRAY_F64); 759 EXPECT_TRUE(prog->literalarray_table["0"].literals_[2].tag_ == panda_file::LiteralTag::ARRAY_STRING); 760 761 EXPECT_TRUE(GetGraph()->RunPass<RegEncoder>()); 762 ASSERT_TRUE(prog->function_table.find(func_name) != prog->function_table.end()); 763 auto &function = prog->function_table.at(func_name); 764 EXPECT_TRUE(GetGraph()->RunPass<BytecodeGen>(&function, GetIrInterface())); 765 ASSERT(pandasm::AsmEmitter::Emit(class_name + ".panda", *prog, nullptr, nullptr, false)); 766 } 767 768 enum CheckConstArrayTypes { ACCESS, SKIP_MULTIDIM_ARRAYS }; 769 CheckConstArray(panda::pandasm::Program * prog,const char * class_name,std::string func_name,CheckConstArrayTypes type)770 void CheckConstArray(panda::pandasm::Program *prog, const char *class_name, std::string func_name, 771 CheckConstArrayTypes type) 772 { 773 options.SetConstArrayResolver(true); 774 775 panda::pandasm::AsmEmitter::Emit(std::string(class_name) + ".panda", *prog, nullptr, nullptr, false); 776 auto temp_name = func_name.substr(func_name.find(".") + 1); 777 EXPECT_TRUE(ParseToGraph(prog, temp_name.substr(0, temp_name.find(":")))); 778 EXPECT_TRUE(RunOptimizations(GetGraph(), GetIrInterface())); 779 780 compiler::Inst *const_array_def_inst {nullptr}; 781 for (auto bb : GetGraph()->GetBlocksRPO()) { 782 for (auto inst : bb->AllInsts()) { 783 switch (type) { 784 case CheckConstArrayTypes::ACCESS: { 785 if (inst->GetOpcode() == Opcode::LoadConstArray) { 786 const_array_def_inst = inst; 787 continue; 788 } 789 if (inst->GetOpcode() == Opcode::LoadArray) { 790 EXPECT_TRUE(const_array_def_inst != nullptr); 791 EXPECT_TRUE(inst->CastToLoadArray()->GetArray() == const_array_def_inst); 792 } 793 continue; 794 } 795 case CheckConstArrayTypes::SKIP_MULTIDIM_ARRAYS: { 796 EXPECT_TRUE(inst->GetOpcode() != Opcode::LoadConstArray); 797 continue; 798 } 799 default: 800 UNREACHABLE(); 801 } 802 } 803 } 804 805 EXPECT_TRUE(GetGraph()->RunPass<RegEncoder>()); 806 ASSERT_TRUE(prog->function_table.find(func_name) != prog->function_table.end()); 807 auto &function = prog->function_table.at(func_name); 808 EXPECT_TRUE(GetGraph()->RunPass<BytecodeGen>(&function, GetIrInterface())); 809 ASSERT(pandasm::AsmEmitter::Emit("LiteralArrayIntAccess.panda", *prog, nullptr, nullptr, false)); 810 } 811 }; 812 813 } // namespace panda::bytecodeopt 814 815 #endif // BYTECODE_OPTIMIZER_TESTS_COMMON_H 816