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 "unit_test.h" 17#include "optimizer/ir_builder/inst_builder-inl.h" 18 19namespace panda::compiler { 20inline bool operator==(const Input &lhs, const Inst *rhs) 21{ 22 return lhs.GetInst() == rhs; 23} 24 25class InstTest : public GraphTest { 26}; 27 28TEST_F(InstTest, Dataflow) 29{ 30 /** 31 * '=' is a definition 32 * 33 * [2] 34 * | 35 * /---------------\ 36 * | | 37 * [3]= [4]= 38 * | | 39 * | /---------\ 40 * [5] | | 41 * | | [6] (need for removing #6) 42 * | | | 43 * | | [7]= 44 * | | | 45 * \---------[8]--------/ 46 * PHI(1,2,4) 47 * 48 */ 49 GRAPH(GetGraph()) 50 { 51 CONSTANT(0, 12); 52 CONSTANT(1, 13); 53 54 BASIC_BLOCK(2, 3, 4) 55 { 56 INST(2, Opcode::Add).u64().Inputs(0, 1); 57 INST(8, Opcode::Compare).b().SrcType(DataType::Type::INT64).Inputs(0, 1); 58 INST(9, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(8); 59 } 60 BASIC_BLOCK(3, 5) 61 { 62 INST(3, Opcode::Not).u64().Inputs(0); 63 } 64 BASIC_BLOCK(4, 8, 6) 65 { 66 INST(4, Opcode::Not).u64().Inputs(1); 67 INST(11, Opcode::Compare).b().SrcType(DataType::Type::INT64).Inputs(0, 1); 68 INST(12, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(11); 69 } 70 BASIC_BLOCK(5, 8) 71 { 72 INST(7, Opcode::Sub).u64().Inputs(3, 2); 73 } 74 BASIC_BLOCK(6, 7) {} 75 BASIC_BLOCK(7, 8) 76 { 77 INST(5, Opcode::Not).u64().Inputs(4); 78 } 79 BASIC_BLOCK(8, -1) 80 { 81 INST(6, Opcode::Phi).u64().Inputs({{5, 3}, {4, 4}, {7, 5}}); 82 INST(16, Opcode::ReturnVoid); 83 } 84 } 85 86 // Check constructed dataflow 87 ASSERT_TRUE(CheckUsers(INS(0), {2, 3, 8, 11})); 88 ASSERT_TRUE(CheckUsers(INS(1), {2, 4, 8, 11})); 89 ASSERT_TRUE(CheckUsers(INS(2), {7})); 90 ASSERT_TRUE(CheckUsers(INS(3), {6, 7})); 91 ASSERT_TRUE(CheckUsers(INS(4), {5, 6})); 92 ASSERT_TRUE(CheckUsers(INS(5), {6})); 93 ASSERT_TRUE(CheckInputs(INS(2), {0, 1})); 94 ASSERT_TRUE(CheckInputs(INS(3), {0})); 95 ASSERT_TRUE(CheckInputs(INS(7), {3, 2})); 96 ASSERT_TRUE(CheckInputs(INS(4), {1})); 97 ASSERT_TRUE(CheckInputs(INS(5), {4})); 98 ASSERT_TRUE(CheckInputs(INS(6), {3, 4, 5})); 99 ASSERT_EQ(static_cast<PhiInst &>(INS(6)).GetPhiInput(&BB(5)), &INS(3)); 100 ASSERT_EQ(static_cast<PhiInst &>(INS(6)).GetPhiInput(&BB(4)), &INS(4)); 101 ASSERT_EQ(static_cast<PhiInst &>(INS(6)).GetPhiInput(&BB(7)), &INS(5)); 102 103 { // Test iterating over users of constant instruction 104 const Inst *inst = &INS(2); 105 for (auto &user : inst->GetUsers()) { 106 ASSERT_EQ(inst, user.GetInput()); 107 } 108 } 109 110 { // Test iterating over users of non-constant instruction 111 Inst *inst = &INS(2); 112 for (auto &user : inst->GetUsers()) { 113 user.GetInst()->SetId(user.GetInst()->GetId()); 114 } 115 } 116 117 // 1. Remove instruction #3, replace its users by its input 118 INS(3).ReplaceUsers(INS(3).GetInput(0).GetInst()); 119 INS(3).GetBasicBlock()->RemoveInst(&INS(3)); 120 ASSERT_TRUE(INS(6).GetInput(0).GetInst() == &INS(0)); 121 ASSERT_TRUE(INS(3).GetInput(0).GetInst() == nullptr); 122 ASSERT_TRUE(CheckUsers(INS(0), {2, 6, 7, 8, 11})); 123 ASSERT_EQ(static_cast<PhiInst &>(INS(6)).GetPhiInput(&BB(5)), &INS(0)); 124 GraphChecker(GetGraph()).Check(); 125 126 // TODO(A.Popov): refactor RemovePredsBlocks 127 // 2. Remove basic block #4, phi should be fixed properly 128 // INS(5).RemoveInputs() 129 // INS(5).GetBasicBlock()->EraseInst(&INS(5)) 130 // GetGraph()->DisconnectBlock(&BB(7)) 131 // ASSERT_TRUE(INS(6).GetInputsCount() == 2) 132 // static_cast<PhiInst&>(INS(6)).GetPhiInput(&BB(5)), &INS(0)) 133 // static_cast<PhiInst&>(INS(6)).GetPhiInput(&BB(4)), &INS(4)) 134 GraphChecker(GetGraph()).Check(); 135 136 // 3. Append additional inputs into PHI, thereby force it to reallocate inputs storage, dataflow is not valid from 137 // this moment 138 for (int i = 0; i < 4; ++i) { 139 INS(6).AppendInput(&INS(0)); 140 } 141} 142 143TEST_F(InstTest, Arithmetics) 144{ 145 GRAPH(GetGraph()) 146 { 147 CONSTANT(0, 12); 148 CONSTANT(1, 17.23); 149 150 BASIC_BLOCK(2, -1) 151 { 152 INST(2, Opcode::Cast).u64().SrcType(DataType::FLOAT64).Inputs(1); 153 INST(3, Opcode::Add).u64().Inputs(0, 2); 154 INST(4, Opcode::ReturnVoid); 155 } 156 } 157} 158 159TEST_F(InstTest, Memory) 160{ 161 GRAPH(GetGraph()) 162 { 163 PARAMETER(0, 0).ref(); // array 164 PARAMETER(1, 1).u64(); // index 165 BASIC_BLOCK(2, -1) 166 { 167 INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1}); 168 INST(3, Opcode::NullCheck).ref().Inputs(0, 2); 169 INST(4, Opcode::LenArray).s32().Inputs(3); 170 INST(5, Opcode::BoundsCheck).s32().Inputs(4, 1, 2); 171 INST(6, Opcode::LoadArray).u64().Inputs(3, 5); 172 INST(7, Opcode::Add).u64().Inputs(6, 6); 173 INST(8, Opcode::StoreArray).u64().Inputs(3, 5, 7); 174 INST(9, Opcode::ReturnVoid); 175 } 176 } 177} 178 179TEST_F(InstTest, Const) 180{ 181 int32_t int32_const[3] = {-5, 0, 5}; 182 int64_t int64_const[3] = {-5, 0, 5}; 183 GRAPH(GetGraph()) 184 { 185 BASIC_BLOCK(2, -1) 186 { 187 INST(0, Opcode::ReturnVoid); 188 } 189 } 190 auto start = GetGraph()->GetStartBlock(); 191 for (auto i = 0; i < 3; i++) { 192 int32_t val = int32_const[i]; 193 auto const1 = GetGraph()->FindOrCreateConstant(val); 194 ASSERT_EQ(const1->GetType(), DataType::INT64); 195 ASSERT_EQ(const1->GetBasicBlock(), start); 196 uint64_t val1 = int64_const[i]; 197 auto const2 = GetGraph()->FindOrCreateConstant(val1); 198 ASSERT_EQ(const2->GetType(), DataType::INT64); 199 ASSERT_EQ(const1, const2); 200 ASSERT_EQ(const1->GetIntValue(), val1); 201 } 202 GraphChecker(GetGraph()).Check(); 203 float float_const[3] = {-5.5f, 0.1f, 5.2f}; 204 for (auto i = 0; i < 3; i++) { 205 float val = float_const[i]; 206 auto const1 = GetGraph()->FindOrCreateConstant(val); 207 ASSERT_EQ(const1->GetType(), DataType::FLOAT32); 208 ASSERT_EQ(const1->GetBasicBlock(), start); 209 auto const2 = GetGraph()->FindOrCreateConstant(val); 210 ASSERT_EQ(const1, const2); 211 ASSERT_EQ(const1->GetFloatValue(), val); 212 } 213 GraphChecker(GetGraph()).Check(); 214 double double_const[3] = {-5.5, 0.1, 5.2}; 215 for (auto i = 0; i < 3; i++) { 216 double val = double_const[i]; 217 auto const1 = GetGraph()->FindOrCreateConstant(val); 218 ASSERT_EQ(const1->GetType(), DataType::FLOAT64); 219 ASSERT_EQ(const1->GetBasicBlock(), start); 220 auto const2 = GetGraph()->FindOrCreateConstant(val); 221 ASSERT_EQ(const1, const2); 222 ASSERT_EQ(const1->GetDoubleValue(), val); 223 } 224 int i = 0; 225 for (auto current_const = GetGraph()->GetFirstConstInst(); current_const != nullptr; 226 current_const = current_const->GetNextConst()) { 227 i++; 228 } 229 ASSERT_EQ(i, 9); 230} 231 232TEST_F(InstTest, Const32) 233{ 234 int32_t int32_const[3] = {-5, 0, 5}; 235 int64_t int64_const[3] = {-5, 0, 5}; 236 auto graph = CreateEmptyBytecodeGraph(); 237 238 GRAPH(graph) 239 { 240 BASIC_BLOCK(2, -1) 241 { 242 INST(0, Opcode::ReturnVoid); 243 } 244 } 245 auto start = graph->GetStartBlock(); 246 for (auto i = 0; i < 3; i++) { 247 // add first int32 constant 248 int32_t val = int32_const[i]; 249 auto const1 = graph->FindOrCreateConstant(val); 250 ASSERT_EQ(const1->GetType(), DataType::INT32); 251 ASSERT_EQ(const1->GetBasicBlock(), start); 252 uint64_t val1 = int64_const[i]; 253 // add int64 constant, graph creates new constant 254 auto const2 = graph->FindOrCreateConstant(val1); 255 ASSERT_EQ(const2->GetType(), DataType::INT64); 256 ASSERT_NE(const1, const2); 257 ASSERT_EQ(const2->GetBasicBlock(), start); 258 ASSERT_EQ(const1->GetIntValue(), val1); 259 // add second int32 constant, graph doesn't create new constant 260 int32_t val2 = int32_const[i]; 261 auto const3 = graph->FindOrCreateConstant(val2); 262 ASSERT_EQ(const3, const1); 263 ASSERT_EQ(const1->GetInt32Value(), val2); 264 } 265 GraphChecker(graph).Check(); 266} 267 268TEST_F(InstTest, ReturnVoid) 269{ 270 GRAPH(GetGraph()) 271 { 272 BASIC_BLOCK(2, -1) 273 { 274 INST(0, Opcode::ReturnVoid); 275 } 276 } 277} 278 279TEST_F(InstTest, ReturnFloat) 280{ 281 GRAPH(GetGraph()) 282 { 283 CONSTANT(0, 1.1f); 284 BASIC_BLOCK(2, -1) 285 { 286 INST(1, Opcode::Return).f32().Inputs(0); 287 } 288 } 289} 290 291TEST_F(InstTest, ReturnDouble) 292{ 293 GRAPH(GetGraph()) 294 { 295 CONSTANT(0, 1.1); 296 BASIC_BLOCK(2, -1) 297 { 298 INST(1, Opcode::Return).f64().Inputs(0); 299 } 300 } 301} 302 303TEST_F(InstTest, ReturnLong) 304{ 305 uint64_t i = 1; 306 GRAPH(GetGraph()) 307 { 308 CONSTANT(0, i); 309 BASIC_BLOCK(2, -1) 310 { 311 INST(1, Opcode::Return).u64().Inputs(0); 312 } 313 } 314} 315 316TEST_F(InstTest, ReturnInt) 317{ 318 int32_t i = 1; 319 GRAPH(GetGraph()) 320 { 321 CONSTANT(0, i); 322 BASIC_BLOCK(2, -1) 323 { 324 INST(1, Opcode::Return).u32().Inputs(0); 325 } 326 } 327} 328 329TEST_F(InstTest, ArrayChecks) 330{ 331 GRAPH(GetGraph()) 332 { 333 PARAMETER(0, 0).ref(); // array 334 PARAMETER(1, 1).u64(); // index 335 BASIC_BLOCK(2, -1) 336 { 337 INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1}); 338 INST(3, Opcode::NullCheck).ref().Inputs(0, 2); 339 INST(4, Opcode::LenArray).s32().Inputs(3); 340 INST(5, Opcode::BoundsCheck).s32().Inputs(4, 1, 2); 341 INST(6, Opcode::LoadArray).u64().Inputs(3, 5); 342 INST(7, Opcode::Add).u64().Inputs(6, 6); 343 INST(8, Opcode::StoreArray).u64().Inputs(3, 5, 7); 344 INST(9, Opcode::ReturnVoid); 345 } 346 } 347} 348 349TEST_F(InstTest, ZeroCheck) 350{ 351 GRAPH(GetGraph()) 352 { 353 PARAMETER(0, 0).u64(); 354 PARAMETER(1, 1).u64(); 355 BASIC_BLOCK(2, -1) 356 { 357 INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1}); 358 INST(3, Opcode::ZeroCheck).u64().Inputs(0, 2); 359 INST(4, Opcode::Div).u64().Inputs(1, 3); 360 INST(5, Opcode::Mod).u64().Inputs(1, 3); 361 INST(6, Opcode::ReturnVoid); 362 } 363 } 364} 365 366TEST_F(InstTest, Parametr) 367{ 368 GRAPH(GetGraph()) 369 { 370 PARAMETER(0, 0).u64(); 371 PARAMETER(1, 1).u32(); 372 PARAMETER(2, 4).u16(); 373 PARAMETER(3, 5).u8(); 374 PARAMETER(4, 8).s64(); 375 PARAMETER(5, 10).s32(); 376 PARAMETER(6, 11).s16(); 377 PARAMETER(7, 24).s8(); 378 PARAMETER(8, 27).b(); 379 PARAMETER(9, 28).f64(); 380 PARAMETER(10, 29).f32(); 381 PARAMETER(11, 40).ref(); 382 BASIC_BLOCK(2, -1) 383 { 384 INST(12, Opcode::Add).u32().Inputs(1, 5); 385 INST(13, Opcode::ReturnVoid); 386 } 387 } 388} 389 390TEST_F(InstTest, LenArray) 391{ 392 GRAPH(GetGraph()) 393 { 394 PARAMETER(0, 40).ref(); 395 BASIC_BLOCK(2, -1) 396 { 397 INST(1, Opcode::LenArray).s32().Inputs(0); 398 INST(2, Opcode::ReturnVoid); 399 } 400 } 401} 402 403TEST_F(InstTest, Call) 404{ 405 GRAPH(GetGraph()) 406 { 407 PARAMETER(0, 1).ref(); 408 PARAMETER(1, 2).u32(); 409 PARAMETER(2, 4).u16(); 410 PARAMETER(3, 5).u8(); 411 PARAMETER(4, 8).s64(); 412 BASIC_BLOCK(2, -1) 413 { 414 using namespace DataType; 415 INST(8, Opcode::SaveState).NoVregs(); 416 INST(5, Opcode::CallVirtual).s32().InputsAutoType(0, 2, 4, 8); 417 INST(6, Opcode::CallStatic).b().InputsAutoType(1, 3, 4, 5, 8); 418 INST(7, Opcode::ReturnVoid); 419 } 420 } 421} 422 423TEST_F(InstTest, BinaryImmOperation) 424{ 425 GRAPH(GetGraph()) 426 { 427 PARAMETER(0, 1).u64(); 428 PARAMETER(1, 4).s32(); 429 BASIC_BLOCK(2, -1) 430 { 431 INST(2, Opcode::AddI).s32().Imm(10ULL).Inputs(1); 432 INST(3, Opcode::SubI).s32().Imm(15ULL).Inputs(2); 433 INST(4, Opcode::AndI).u64().Imm(15ULL).Inputs(0); 434 INST(5, Opcode::OrI).u64().Imm(1ULL).Inputs(4); 435 INST(6, Opcode::XorI).u64().Imm(10ULL).Inputs(5); 436 INST(7, Opcode::ShlI).u64().Imm(5ULL).Inputs(6); 437 INST(8, Opcode::ShrI).u64().Imm(5ULL).Inputs(7); 438 INST(9, Opcode::AShrI).s32().Imm(4ULL).Inputs(3); 439 INST(10, Opcode::ReturnVoid); 440 } 441 } 442} 443 444TEST_F(InstTest, Fcmp) 445{ 446 GRAPH(GetGraph()) 447 { 448 PARAMETER(0, 0).f32(); 449 PARAMETER(1, 1).f32(); 450 PARAMETER(2, 2).f64(); 451 PARAMETER(3, 3).f64(); 452 BASIC_BLOCK(2, -1) 453 { 454 INST(4, Opcode::Cmp).s32().Inputs(0, 1); 455 INST(5, Opcode::Cmp).s32().Inputs(2, 3); 456 INST(6, Opcode::ReturnVoid); 457 } 458 } 459 GraphChecker(GetGraph()).Check(); 460 auto inst4 = static_cast<CmpInst *>(&INS(4)); 461 auto inst5 = static_cast<CmpInst *>(&INS(5)); 462 inst4->SetFcmpl(); 463 ASSERT_EQ(inst4->IsFcmpg(), false); 464 ASSERT_EQ(inst4->IsFcmpl(), true); 465 inst4->SetFcmpl(true); 466 ASSERT_EQ(inst4->IsFcmpg(), false); 467 ASSERT_EQ(inst4->IsFcmpl(), true); 468 inst4->SetFcmpl(false); 469 ASSERT_EQ(inst4->IsFcmpg(), true); 470 ASSERT_EQ(inst4->IsFcmpl(), false); 471 inst5->SetFcmpg(); 472 ASSERT_EQ(inst5->IsFcmpg(), true); 473 ASSERT_EQ(inst5->IsFcmpl(), false); 474 inst5->SetFcmpg(true); 475 ASSERT_EQ(inst5->IsFcmpg(), true); 476 ASSERT_EQ(inst5->IsFcmpl(), false); 477 inst5->SetFcmpg(false); 478 ASSERT_EQ(inst5->IsFcmpg(), false); 479 ASSERT_EQ(inst5->IsFcmpl(), true); 480} 481 482TEST_F(InstTest, SpillFill) 483{ 484 Register R0 = 0; 485 Register R1 = 1; 486 StackSlot slot0 = 0; 487 StackSlot slot1 = 1; 488 489 auto spill_fill_inst = GetGraph()->CreateInstSpillFill(); 490 spill_fill_inst->AddFill(slot0, R0, DataType::UINT64); 491 spill_fill_inst->AddMove(R0, R1, DataType::UINT64); 492 spill_fill_inst->AddSpill(R1, slot1, DataType::UINT64); 493 494 ASSERT_EQ(spill_fill_inst->GetSpillFills().size(), 3U); 495} 496 497TEST_F(InstTest, RemovePhiInput) 498{ 499 GRAPH(GetGraph()) 500 { 501 CONSTANT(0, 0); 502 CONSTANT(1, 1); 503 CONSTANT(2, 2); 504 BASIC_BLOCK(2, 3, 5) 505 { 506 INST(5, Opcode::Compare).b().SrcType(DataType::Type::INT64).Inputs(0, 1); 507 INST(6, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(5); 508 } 509 BASIC_BLOCK(3, 5) {} 510 BASIC_BLOCK(5, -1) 511 { 512 INST(3, Opcode::Phi).u64().Inputs({{2, 0}, {3, 1}}); 513 INST(4, Opcode::ReturnVoid); 514 } 515 } 516 auto init_inputs = INS(3).GetInputs(); 517 auto init_preds = BB(5).GetPredsBlocks(); 518 519 auto pred_bb_idx = INS(3).CastToPhi()->GetPredBlockIndex(&BB(3)); 520 BB(5).RemovePred(&BB(3)); 521 INS(3).RemoveInput(pred_bb_idx); 522 523 auto curr_inputs = INS(3).GetInputs(); 524 auto curr_preds = BB(5).GetPredsBlocks(); 525 for (size_t idx = 0; idx < curr_inputs.size(); idx++) { 526 if (idx != pred_bb_idx) { 527 ASSERT_EQ(init_inputs[idx].GetInst(), curr_inputs[idx].GetInst()); 528 ASSERT_EQ(init_preds[idx], curr_preds[idx]); 529 } else { 530 ASSERT_EQ(init_inputs.rbegin()->GetInst(), curr_inputs[idx].GetInst()); 531 ASSERT_EQ(init_preds.back(), curr_preds[idx]); 532 } 533 } 534} 535 536/** 537 * Test creating instruction with huge dynamic inputs amount 538 */ 539TEST_F(InstTest, HugeDynamicOperandsAmount) 540{ 541 auto graph = CreateGraphStartEndBlocks(); 542 const size_t COUNT = 1000; 543 auto save_state = graph->CreateInstSaveState(); 544 545 for (size_t i = 0; i < COUNT; i++) { 546 save_state->AppendInput(graph->FindOrCreateConstant(i)); 547 save_state->SetVirtualRegister(i, VirtualRegister(i, false)); 548 } 549 550 for (size_t i = 0; i < COUNT; i++) { 551 auto user = graph->FindOrCreateConstant(i)->GetUsers().begin()->GetInst(); 552 ASSERT_EQ(user, save_state); 553 } 554} 555 556TEST_F(InstTest, FloatConstants) 557{ 558 auto graph = CreateGraphStartEndBlocks(); 559 graph->GetStartBlock()->AddSucc(graph->GetEndBlock()); 560 auto positiv_zero_float = graph->FindOrCreateConstant(0.0f); 561 auto negativ_zero_float = graph->FindOrCreateConstant(-0.0f); 562 auto positiv_zero_double = graph->FindOrCreateConstant(0.0); 563 auto negativ_zero_double = graph->FindOrCreateConstant(-0.0); 564 565 ASSERT_NE(positiv_zero_float, negativ_zero_float); 566 ASSERT_NE(positiv_zero_double, negativ_zero_double); 567} 568 569TEST_F(InstTest, Flags) 570{ 571 auto initial_mask = inst_flags::GetFlagsMask(Opcode::LoadObject); 572 auto inst = GetGraph()->CreateInstLoadObject(); 573 ASSERT_EQ(initial_mask, inst->GetFlagsMask()); 574 ASSERT_EQ(inst->GetFlagsMask(), initial_mask); 575 ASSERT_TRUE(inst->IsLoad()); 576 inst->SetFlag(inst_flags::ALLOC); 577 ASSERT_EQ(inst->GetFlagsMask(), initial_mask | inst_flags::ALLOC); 578 inst->ClearFlag(inst_flags::LOAD); 579 ASSERT_FALSE(inst->IsLoad()); 580 ASSERT_EQ(inst->GetFlagsMask(), (initial_mask | inst_flags::ALLOC) & ~inst_flags::LOAD); 581} 582 583TEST_F(InstTest, IntrinsicFlags) 584{ 585 ArenaAllocator allocator {SpaceType::SPACE_TYPE_COMPILER}; 586#include "intrinsic_flags_test.inl" 587} 588 589} // namespace panda::compiler 590