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 PANDA_IR_CONSTRUCTOR_H 17 #define PANDA_IR_CONSTRUCTOR_H 18 19 #include <memory> 20 #include "graph.h" 21 #include "graph_checker.h" 22 #include "mark_word.h" 23 #include "optimizer/ir_builder/inst_builder.h" 24 25 namespace panda::compiler { 26 /** 27 * This class aims to simplify IR construction. 28 * 29 * Example: 30 * auto g = CreateEmptyGraph(); 31 * GRAPH(g) { 32 * BASIC_BLOCK(0, 1, 2) { 33 * INST(0, Opcode::IntConstant).Constant(12); 34 * INST(1, Opcode::IntConstant).Constant(12); 35 * INST(2, Opcode::Add).Inputs(0, 1); 36 * INST(6, Opcode::Compare).Inputs(2).CC(ConditionCode::CC_AE); 37 * INST(7, Opcode::If).Inputs(6); 38 * } 39 * BASIC_BLOCK(1, 2) { 40 * INST(3, Opcode::Not).Inputs(0); 41 * } 42 * BASIC_BLOCK(2, -1) { 43 * INST(4, Opcode::Phi).Inputs(2, 3); 44 * INST(5, Opcode::Not).Inputs(4); 45 * } 46 * } 47 * g->Dump(cerr); 48 * 49 * GRAPH(g) macro initializies Builder object by 'g' graph. Builder works with this graph only inside followed scope 50 * in braces. 51 * BASIC_BLOCK creates new basic block and add it to the current graph. All code inside followed scope will work with 52 * this basic block. 53 * First argument is ID of basic block. It must be unique for graph. 54 * All remaining arguments are IDs of successors blocks. '-1' value means that there is no successor. Block 55 * that hasn't successors is considered as end block. 56 * Block with '0' ID is considered as start block. 57 * INST creates new instruction and append it to the current basic block. 58 * First parameter is ID of instruction. It must be unique within the current graph 59 * Second parameter is an opcode. 60 * Dataflow can be constructed via 'Inputs' method, that gets IDs of the input instructions as parameters. 61 * All other properties of instruction may be set via corresponding proxy methods, defined in Builder. 62 */ 63 class IrConstructor final { 64 public: 65 static const size_t ID_ENTRY_BB = 0; 66 static const size_t ID_EXIT_BB = 1U; 67 68 static constexpr DataType::Type MARK_WORD_TYPE = DataType::GetIntTypeBySize(sizeof(MarkWord::markWordSize)); 69 IrConstructor()70 IrConstructor() : aa_(SpaceType::SPACE_TYPE_COMPILER) {} 71 SetGraph(Graph * graph)72 IrConstructor &SetGraph(Graph *graph) 73 { 74 graph_ = graph; 75 if (graph_->GetStartBlock() == nullptr) { 76 graph_->CreateStartBlock(); 77 } 78 if (graph_->GetEndBlock() == nullptr) { 79 graph_->CreateEndBlock(0U); 80 } 81 ASSERT(graph_->GetVectorBlocks().size() == 2U); 82 bb_map_.clear(); 83 bb_map_[ID_ENTRY_BB] = graph_->GetStartBlock(); 84 bb_map_[ID_EXIT_BB] = graph_->GetEndBlock(); 85 bb_succs_map_.clear(); 86 inst_map_.clear(); 87 inst_inputs_map_.clear(); 88 save_state_inst_vregs_map_.clear(); 89 phi_inst_inputs_map_.clear(); 90 return *this; 91 } 92 93 template <size_t id> NewBlock()94 IrConstructor &NewBlock() 95 { 96 ASSERT(id != ID_ENTRY_BB && id != ID_EXIT_BB); 97 ASSERT(bb_map_.count(id) == 0); 98 ASSERT(CurrentBb() == nullptr); 99 auto bb = graph_->GetAllocator()->New<BasicBlock>(graph_); 100 bb->SetGuestPc(0U); 101 #ifdef NDEBUG 102 graph_->AddBlock(bb); 103 #else 104 graph_->AddBlock(bb, id); 105 #endif 106 current_bb_ = {id, bb}; 107 bb_map_[id] = bb; 108 // add connection the first custom block with entry 109 if (bb_succs_map_.empty()) { 110 graph_->GetStartBlock()->AddSucc(bb); 111 } 112 return *this; 113 } 114 115 template <typename... Args> NewInst(size_t id,Args &&...args)116 IrConstructor &NewInst(size_t id, Args &&... args) 117 { 118 ASSERT_DO(inst_map_.find(id) == inst_map_.end(), 119 std::cerr << "Instruction with same Id " << id << "already exists"); 120 auto inst = graph_->CreateInst(std::forward<Args>(args)...); 121 inst->SetId(id); 122 for (size_t i = 0; i < inst->GetInputsCount(); ++i) { 123 inst->SetSrcReg(i, INVALID_REG); 124 } 125 current_inst_ = {id, inst}; 126 inst_map_[id] = inst; 127 auto block = CurrentBb(); 128 if (block == nullptr) { 129 block = graph_->GetStartBlock(); 130 } 131 ASSERT(block); 132 if (inst->IsPhi()) { 133 block->AppendPhi(inst); 134 } else { 135 block->AppendInst(inst); 136 } 137 #ifndef NDEBUG 138 if (inst->IsLowLevel()) { 139 // GraphChecker hack: LowLevel instructions may appear only after Lowering pass: 140 graph_->SetLowLevelInstructionsEnabled(); 141 } 142 #endif 143 if (inst->IsSaveState()) { 144 graph_->SetVRegsCount(std::max(graph_->GetVRegsCount(), sizeof...(args))); 145 } 146 return *this; 147 } 148 149 template <typename T> NewConstant(size_t id,T value)150 IrConstructor &NewConstant(size_t id, [[maybe_unused]] T value) 151 { 152 ASSERT_DO(inst_map_.find(id) == inst_map_.end(), 153 std::cerr << "Instruction with same Id " << id << "already exists"); 154 Inst *inst = nullptr; 155 auto to_start_bb = (CurrentBbIndex() == 0) || (CurrentBbIndex() == -1); 156 if constexpr (std::is_same<T, std::nullptr_t>()) { 157 if (to_start_bb) { 158 inst = graph_->GetOrCreateNullPtr(); 159 } else { 160 inst = graph_->CreateInstNullPtr(); 161 inst->SetType(DataType::REFERENCE); 162 CurrentBb()->AppendInst(inst); 163 } 164 } else { 165 if (to_start_bb) { 166 inst = graph_->FindOrCreateConstant(value); 167 } else { 168 inst = graph_->CreateInstConstant(value, graph_->IsBytecodeOptimizer()); 169 CurrentBb()->AppendInst(inst); 170 } 171 } 172 inst->SetId(id); 173 inst_map_[id] = inst; 174 current_inst_ = {id, inst}; 175 return *this; 176 } 177 NewParameter(int id,uint16_t arg_number)178 IrConstructor &NewParameter(int id, uint16_t arg_number) 179 { 180 ASSERT_DO(inst_map_.find(id) == inst_map_.end(), 181 std::cerr << "Instruction with same Id " << id << "already exists"); 182 auto inst = graph_->AddNewParameter(arg_number); 183 inst->SetId(id); 184 inst_map_[id] = inst; 185 current_inst_ = {id, inst}; 186 return *this; 187 } 188 Succs(std::vector<int> succs)189 IrConstructor &Succs(std::vector<int> succs) 190 { 191 bb_succs_map_.emplace_back(CurrentBbIndex(), std::move(succs)); 192 return *this; 193 } 194 195 /// Define inputs for current instruction. 196 /// Input is an index of input instruction. 197 template <typename... Args> Inputs(Args...inputs)198 IrConstructor &Inputs(Args... inputs) 199 { 200 ASSERT(!CurrentInst()->IsCall() && !CurrentInst()->IsIntrinsic()); 201 inst_inputs_map_[CurrentInstIndex()].reserve(sizeof...(inputs)); 202 if constexpr (sizeof...(inputs) != 0) { 203 AddInput(inputs...); 204 } 205 return *this; 206 } 207 208 /// Define inputs for current call-, intrinsic-, or phi-instriction. 209 /// Input is defined by std::pair. 210 /// In case of phi: first is index of basic block, second is index of input instruction. 211 /// In case of call and intrinsic: first is Type of input, second is index of input instruction. Inputs(std::initializer_list<std::pair<int,int>> inputs)212 IrConstructor &Inputs(std::initializer_list<std::pair<int, int>> inputs) 213 { 214 ASSERT(CurrentInst()->IsPhi() || CurrentInst()->IsCall() || CurrentInst()->IsInitObject() || 215 CurrentInst()->IsIntrinsic()); 216 if (CurrentInst()->IsPhi()) { 217 phi_inst_inputs_map_[CurrentInstIndex()].reserve(inputs.size()); 218 for (const auto &input : inputs) { 219 phi_inst_inputs_map_[CurrentInstIndex()].push_back(input); 220 } 221 } else { 222 auto opc = CurrentInst()->GetOpcode(); 223 InputTypesMixin *types; 224 switch (opc) { 225 case Opcode::Intrinsic: 226 types = static_cast<InputTypesMixin *>(CurrentInst()->CastToIntrinsic()); 227 break; 228 case Opcode::CallIndirect: 229 types = static_cast<InputTypesMixin *>(CurrentInst()->CastToCallIndirect()); 230 break; 231 case Opcode::Builtin: 232 types = static_cast<InputTypesMixin *>(CurrentInst()->CastToBuiltin()); 233 break; 234 case Opcode::InitObject: 235 types = static_cast<InputTypesMixin *>(CurrentInst()->CastToInitObject()); 236 break; 237 default: 238 ASSERT(CurrentInst()->IsCall()); 239 types = static_cast<InputTypesMixin *>(static_cast<CallInst *>(CurrentInst())); 240 break; 241 } 242 243 inst_inputs_map_[CurrentInstIndex()].reserve(inputs.size()); 244 types->AllocateInputTypes(graph_->GetAllocator(), inputs.size()); 245 for (const auto &input : inputs) { 246 types->AddInputType(static_cast<DataType::Type>(input.first)); 247 inst_inputs_map_[CurrentInstIndex()].push_back(input.second); 248 } 249 } 250 return *this; 251 } 252 253 /// Same as the default Inputs() method, but defines inputs' types with respect to call-instructions. 254 /// Copies types of inputs to the instruction's input_types_. 255 template <typename... Args> InputsAutoType(Args...inputs)256 IrConstructor &InputsAutoType(Args... inputs) 257 { 258 ASSERT(CurrentInst()->IsCall()); 259 auto *call_inst = static_cast<CallInst *>(CurrentInst()); 260 call_inst->AllocateInputTypes(graph_->GetAllocator(), sizeof...(inputs)); 261 ((call_inst->AddInputType(GetInst(inputs).GetType())), ...); 262 inst_inputs_map_[CurrentInstIndex()].reserve(sizeof...(inputs)); 263 ((inst_inputs_map_[CurrentInstIndex()].push_back(inputs)), ...); 264 return *this; 265 } 266 Pc(uint32_t pc)267 IrConstructor &Pc(uint32_t pc) 268 { 269 ASSERT(CurrentInst()); 270 CurrentInst()->SetPc(pc); 271 return *this; 272 } 273 274 IrConstructor &Volatile(bool volat = true) 275 { 276 auto inst = CurrentInst(); 277 switch (inst->GetOpcode()) { 278 case Opcode::StoreObject: 279 inst->CastToStoreObject()->SetVolatile(volat); 280 break; 281 case Opcode::LoadObject: 282 inst->CastToLoadObject()->SetVolatile(volat); 283 break; 284 case Opcode::StoreStatic: 285 inst->CastToStoreStatic()->SetVolatile(volat); 286 break; 287 case Opcode::LoadStatic: 288 inst->CastToLoadStatic()->SetVolatile(volat); 289 break; 290 case Opcode::Store: 291 inst->CastToStore()->SetVolatile(volat); 292 break; 293 case Opcode::Load: 294 inst->CastToLoad()->SetVolatile(volat); 295 break; 296 case Opcode::StoreI: 297 inst->CastToStoreI()->SetVolatile(volat); 298 break; 299 case Opcode::LoadI: 300 inst->CastToLoadI()->SetVolatile(volat); 301 break; 302 default: 303 UNREACHABLE(); 304 } 305 return *this; 306 } 307 IsArray(bool value)308 IrConstructor &IsArray(bool value) 309 { 310 auto inst = CurrentInst(); 311 switch (inst->GetOpcode()) { 312 case Opcode::LoadArray: 313 inst->CastToLoadArray()->SetIsArray(value); 314 break; 315 case Opcode::LenArray: 316 inst->CastToLenArray()->SetIsArray(value); 317 break; 318 default: 319 UNREACHABLE(); 320 } 321 return *this; 322 } 323 CC(ConditionCode cc)324 IrConstructor &CC(ConditionCode cc) 325 { 326 auto inst = CurrentInst(); 327 switch (inst->GetOpcode()) { 328 case Opcode::Compare: 329 inst->CastToCompare()->SetCc(cc); 330 break; 331 case Opcode::If: 332 inst->CastToIf()->SetCc(cc); 333 break; 334 case Opcode::AddOverflow: 335 inst->CastToAddOverflow()->SetCc(cc); 336 break; 337 case Opcode::SubOverflow: 338 inst->CastToSubOverflow()->SetCc(cc); 339 break; 340 case Opcode::IfImm: 341 inst->CastToIfImm()->SetCc(cc); 342 break; 343 case Opcode::Select: 344 inst->CastToSelect()->SetCc(cc); 345 break; 346 case Opcode::SelectImm: 347 inst->CastToSelectImm()->SetCc(cc); 348 break; 349 case Opcode::DeoptimizeCompare: 350 inst->CastToDeoptimizeCompare()->SetCc(cc); 351 break; 352 case Opcode::DeoptimizeCompareImm: 353 inst->CastToDeoptimizeCompareImm()->SetCc(cc); 354 break; 355 default: 356 UNREACHABLE(); 357 } 358 return *this; 359 } 360 SetFlag(compiler::inst_flags::Flags flag)361 IrConstructor &SetFlag(compiler::inst_flags::Flags flag) 362 { 363 CurrentInst()->SetFlag(flag); 364 return *this; 365 } 366 ClearFlag(compiler::inst_flags::Flags flag)367 IrConstructor &ClearFlag(compiler::inst_flags::Flags flag) 368 { 369 CurrentInst()->ClearFlag(flag); 370 return *this; 371 } 372 Inlined()373 IrConstructor &Inlined() 374 { 375 auto inst = CurrentInst(); 376 if (inst->GetOpcode() == Opcode::Intrinsic) { 377 inst->CastToIntrinsic()->SetInlined(true); 378 return *this; 379 } 380 ASSERT(inst->GetOpcode() == Opcode::CallStatic || inst->GetOpcode() == Opcode::CallVirtual); 381 static_cast<CallInst *>(inst)->SetInlined(true); 382 inst->SetFlag(inst_flags::NO_DST); 383 return *this; 384 } 385 Scale(uint64_t scale)386 IrConstructor &Scale(uint64_t scale) 387 { 388 auto inst = CurrentInst(); 389 switch (inst->GetOpcode()) { 390 case Opcode::Load: 391 inst->CastToLoad()->SetScale(scale); 392 break; 393 case Opcode::Store: 394 inst->CastToStore()->SetScale(scale); 395 break; 396 default: 397 UNREACHABLE(); 398 } 399 return *this; 400 } 401 Imm(uint64_t imm)402 IrConstructor &Imm(uint64_t imm) 403 { 404 auto inst = CurrentInst(); 405 switch (inst->GetOpcode()) { 406 case Opcode::AddI: 407 case Opcode::SubI: 408 case Opcode::MulI: 409 case Opcode::DivI: 410 case Opcode::ModI: 411 case Opcode::ShlI: 412 case Opcode::ShrI: 413 case Opcode::AShrI: 414 case Opcode::AndI: 415 case Opcode::OrI: 416 case Opcode::XorI: 417 static_cast<BinaryImmOperation *>(inst)->SetImm(imm); 418 break; 419 case Opcode::BoundsCheckI: 420 inst->CastToBoundsCheckI()->SetImm(imm); 421 break; 422 case Opcode::LoadArrayI: 423 inst->CastToLoadArrayI()->SetImm(imm); 424 break; 425 case Opcode::StoreArrayI: 426 inst->CastToStoreArrayI()->SetImm(imm); 427 break; 428 case Opcode::LoadI: 429 inst->CastToLoadI()->SetImm(imm); 430 break; 431 case Opcode::StoreI: 432 inst->CastToStoreI()->SetImm(imm); 433 break; 434 case Opcode::ReturnI: 435 inst->CastToReturnI()->SetImm(imm); 436 break; 437 case Opcode::IfImm: 438 inst->CastToIfImm()->SetImm(imm); 439 break; 440 case Opcode::SelectImm: 441 inst->CastToSelectImm()->SetImm(imm); 442 break; 443 case Opcode::LoadArrayPairI: 444 inst->CastToLoadArrayPairI()->SetImm(imm); 445 break; 446 case Opcode::StoreArrayPairI: 447 inst->CastToStoreArrayPairI()->SetImm(imm); 448 break; 449 case Opcode::LoadPairPart: 450 inst->CastToLoadPairPart()->SetImm(imm); 451 break; 452 case Opcode::DeoptimizeCompareImm: 453 inst->CastToDeoptimizeCompareImm()->SetImm(imm); 454 break; 455 default: 456 UNREACHABLE(); 457 } 458 return *this; 459 } 460 Shift(ShiftType shift_type,uint64_t imm)461 IrConstructor &Shift(ShiftType shift_type, uint64_t imm) 462 { 463 auto inst = CurrentInst(); 464 switch (inst->GetOpcode()) { 465 case Opcode::AndSR: 466 case Opcode::OrSR: 467 case Opcode::XorSR: 468 case Opcode::AndNotSR: 469 case Opcode::OrNotSR: 470 case Opcode::XorNotSR: 471 case Opcode::AddSR: 472 case Opcode::SubSR: 473 static_cast<BinaryShiftedRegisterOperation *>(inst)->SetShiftType(shift_type); 474 static_cast<BinaryShiftedRegisterOperation *>(inst)->SetImm(imm); 475 break; 476 case Opcode::NegSR: 477 static_cast<UnaryShiftedRegisterOperation *>(inst)->SetShiftType(shift_type); 478 static_cast<UnaryShiftedRegisterOperation *>(inst)->SetImm(imm); 479 break; 480 default: 481 UNREACHABLE(); 482 } 483 return *this; 484 }; 485 Exit()486 IrConstructor &Exit() 487 { 488 CurrentInst()->CastToMonitor()->SetExit(); 489 return *this; 490 } 491 Entry()492 IrConstructor &Entry() 493 { 494 CurrentInst()->CastToMonitor()->SetEntry(); 495 return *this; 496 } 497 Fcmpg(bool fcmpg)498 IrConstructor &Fcmpg(bool fcmpg) 499 { 500 CurrentInst()->CastToCmp()->SetFcmpg(fcmpg); 501 return *this; 502 } 503 u8()504 IrConstructor &u8() // NOLINT(readability-identifier-naming) 505 { 506 CurrentInst()->SetType(DataType::UINT8); 507 return *this; 508 } u16()509 IrConstructor &u16() // NOLINT(readability-identifier-naming) 510 { 511 CurrentInst()->SetType(DataType::UINT16); 512 return *this; 513 } u32()514 IrConstructor &u32() // NOLINT(readability-identifier-naming) 515 { 516 CurrentInst()->SetType(DataType::UINT32); 517 return *this; 518 } u64()519 IrConstructor &u64() // NOLINT(readability-identifier-naming) 520 { 521 CurrentInst()->SetType(DataType::UINT64); 522 return *this; 523 } s8()524 IrConstructor &s8() // NOLINT(readability-identifier-naming) 525 { 526 CurrentInst()->SetType(DataType::INT8); 527 return *this; 528 } s16()529 IrConstructor &s16() // NOLINT(readability-identifier-naming) 530 { 531 CurrentInst()->SetType(DataType::INT16); 532 return *this; 533 } s32()534 IrConstructor &s32() // NOLINT(readability-identifier-naming) 535 { 536 CurrentInst()->SetType(DataType::INT32); 537 return *this; 538 } s64()539 IrConstructor &s64() // NOLINT(readability-identifier-naming) 540 { 541 CurrentInst()->SetType(DataType::INT64); 542 return *this; 543 } i8()544 IrConstructor &i8() // NOLINT(readability-identifier-naming) 545 { 546 return s8(); 547 } i16()548 IrConstructor &i16() // NOLINT(readability-identifier-naming) 549 { 550 return s16(); 551 } i32()552 IrConstructor &i32() // NOLINT(readability-identifier-naming) 553 { 554 return s32(); 555 } i64()556 IrConstructor &i64() // NOLINT(readability-identifier-naming) 557 { 558 return s64(); 559 } b()560 IrConstructor &b() // NOLINT(readability-identifier-naming) 561 { 562 CurrentInst()->SetType(DataType::BOOL); 563 return *this; 564 } ref()565 IrConstructor &ref() // NOLINT(readability-identifier-naming) 566 { 567 CurrentInst()->SetType(DataType::REFERENCE); 568 return *this; 569 } ptr()570 IrConstructor &ptr() // NOLINT(readability-identifier-naming) 571 { 572 CurrentInst()->SetType(DataType::POINTER); 573 return *this; 574 } w()575 IrConstructor &w() // NOLINT(readability-identifier-naming) 576 { 577 return ptr(); 578 } 579 // Type representing MarkWord mw()580 IrConstructor &mw() // NOLINT(readability-identifier-naming) 581 { 582 return type(MARK_WORD_TYPE); 583 } f32()584 IrConstructor &f32() // NOLINT(readability-identifier-naming) 585 { 586 CurrentInst()->SetType(DataType::FLOAT32); 587 return *this; 588 } f64()589 IrConstructor &f64() // NOLINT(readability-identifier-naming) 590 { 591 CurrentInst()->SetType(DataType::FLOAT64); 592 return *this; 593 } any()594 IrConstructor &any() // NOLINT(readability-identifier-naming) 595 { 596 CurrentInst()->SetType(DataType::ANY); 597 return *this; 598 } SetType(DataType::Type type)599 IrConstructor &SetType(DataType::Type type) // NOLINT(readability-identifier-naming) 600 { 601 CurrentInst()->SetType(type); 602 return *this; 603 } AnyType(AnyBaseType any_type)604 IrConstructor &AnyType(AnyBaseType any_type) 605 { 606 auto *atm = static_cast<AnyTypeMixin<FixedInputsInst1> *>(CurrentInst()); 607 atm->SetAnyType(any_type); 608 return *this; 609 } v0id()610 IrConstructor &v0id() // NOLINT(readability-identifier-naming) 611 { 612 CurrentInst()->SetType(DataType::VOID); 613 return *this; 614 } type(DataType::Type type)615 IrConstructor &type(DataType::Type type) // NOLINT(readability-identifier-naming) 616 { 617 CurrentInst()->SetType(type); 618 return *this; 619 } 620 Terminator()621 IrConstructor &Terminator() 622 { 623 CurrentInst()->SetFlag(inst_flags::TERMINATOR); 624 return *this; 625 } 626 AddImm(uint32_t imm)627 IrConstructor &AddImm(uint32_t imm) 628 { 629 CurrentInst()->CastToIntrinsic()->AddImm(graph_->GetAllocator(), imm); 630 return *this; 631 } 632 DstReg(uint8_t reg)633 IrConstructor &DstReg(uint8_t reg) 634 { 635 CurrentInst()->SetDstReg(reg); 636 if (DataType::IsFloatType(CurrentInst()->GetType())) { 637 graph_->SetUsedReg<DataType::FLOAT64>(reg); 638 } 639 return *this; 640 } 641 SrcReg(uint8_t id,uint8_t reg)642 IrConstructor &SrcReg(uint8_t id, uint8_t reg) 643 { 644 CurrentInst()->SetSrcReg(id, reg); 645 if (DataType::IsFloatType(CurrentInst()->GetType())) { 646 graph_->SetUsedReg<DataType::FLOAT64>(reg); 647 } 648 graph_->SetUsedReg<DataType::INT64>(reg); 649 return *this; 650 } 651 TypeId(uint32_t type_id)652 IrConstructor &TypeId(uint32_t type_id) 653 { 654 auto inst = CurrentInst(); 655 switch (inst->GetOpcode()) { 656 case Opcode::Call: 657 inst->CastToCall()->SetCallMethodId(type_id); 658 break; 659 case Opcode::LoadString: 660 inst->CastToLoadString()->SetTypeId(type_id); 661 break; 662 case Opcode::LoadType: 663 inst->CastToLoadType()->SetTypeId(type_id); 664 break; 665 case Opcode::UnresolvedLoadType: 666 inst->CastToUnresolvedLoadType()->SetTypeId(type_id); 667 break; 668 case Opcode::StoreStatic: 669 inst->CastToStoreStatic()->SetTypeId(type_id); 670 break; 671 case Opcode::UnresolvedStoreStatic: 672 inst->CastToUnresolvedStoreStatic()->SetTypeId(type_id); 673 break; 674 case Opcode::LoadStatic: 675 inst->CastToLoadStatic()->SetTypeId(type_id); 676 break; 677 case Opcode::UnresolvedLoadStatic: 678 inst->CastToUnresolvedLoadStatic()->SetTypeId(type_id); 679 break; 680 case Opcode::LoadObject: 681 inst->CastToLoadObject()->SetTypeId(type_id); 682 break; 683 case Opcode::UnresolvedLoadObject: 684 inst->CastToUnresolvedLoadObject()->SetTypeId(type_id); 685 break; 686 case Opcode::StoreObject: 687 inst->CastToStoreObject()->SetTypeId(type_id); 688 break; 689 case Opcode::UnresolvedStoreObject: 690 inst->CastToUnresolvedStoreObject()->SetTypeId(type_id); 691 break; 692 case Opcode::NewObject: 693 inst->CastToNewObject()->SetTypeId(type_id); 694 break; 695 case Opcode::InitObject: 696 inst->CastToInitObject()->SetCallMethodId(type_id); 697 break; 698 case Opcode::NewArray: 699 inst->CastToNewArray()->SetTypeId(type_id); 700 break; 701 case Opcode::CheckCast: 702 inst->CastToCheckCast()->SetTypeId(type_id); 703 break; 704 case Opcode::IsInstance: 705 inst->CastToIsInstance()->SetTypeId(type_id); 706 break; 707 case Opcode::InitClass: 708 inst->CastToInitClass()->SetTypeId(type_id); 709 break; 710 case Opcode::LoadClass: 711 inst->CastToLoadClass()->SetTypeId(type_id); 712 break; 713 case Opcode::LoadAndInitClass: 714 inst->CastToLoadAndInitClass()->SetTypeId(type_id); 715 break; 716 case Opcode::UnresolvedLoadAndInitClass: 717 inst->CastToUnresolvedLoadAndInitClass()->SetTypeId(type_id); 718 break; 719 default: 720 UNREACHABLE(); 721 } 722 return *this; 723 } 724 ObjField(RuntimeInterface::FieldPtr field)725 IrConstructor &ObjField(RuntimeInterface::FieldPtr field) 726 { 727 auto inst = CurrentInst(); 728 switch (inst->GetOpcode()) { 729 case Opcode::StoreStatic: 730 inst->CastToStoreStatic()->SetObjField(field); 731 break; 732 case Opcode::LoadStatic: 733 inst->CastToLoadStatic()->SetObjField(field); 734 break; 735 case Opcode::LoadObject: 736 inst->CastToLoadObject()->SetObjField(field); 737 break; 738 case Opcode::StoreObject: 739 inst->CastToStoreObject()->SetObjField(field); 740 break; 741 default: 742 UNREACHABLE(); 743 } 744 return *this; 745 } 746 SetNeedBarrier(bool need_barrier)747 IrConstructor &SetNeedBarrier(bool need_barrier) 748 { 749 auto inst = CurrentInst(); 750 switch (inst->GetOpcode()) { 751 case Opcode::Store: 752 inst->CastToStore()->SetNeedBarrier(need_barrier); 753 break; 754 case Opcode::StoreI: 755 inst->CastToStoreI()->SetNeedBarrier(need_barrier); 756 break; 757 case Opcode::StoreObject: 758 inst->CastToStoreObject()->SetNeedBarrier(need_barrier); 759 break; 760 case Opcode::StoreArray: 761 inst->CastToStoreArray()->SetNeedBarrier(need_barrier); 762 break; 763 case Opcode::StoreArrayI: 764 inst->CastToStoreArrayI()->SetNeedBarrier(need_barrier); 765 break; 766 case Opcode::StoreArrayPair: 767 inst->CastToStoreArrayPair()->SetNeedBarrier(need_barrier); 768 break; 769 case Opcode::StoreArrayPairI: 770 inst->CastToStoreArrayPairI()->SetNeedBarrier(need_barrier); 771 break; 772 default: 773 UNREACHABLE(); 774 } 775 return *this; 776 } 777 SrcVregs(std::vector<int> && vregs)778 IrConstructor &SrcVregs(std::vector<int> &&vregs) 779 { 780 ASSERT(CurrentInst()->IsSaveState()); 781 if (!vregs.empty()) { 782 graph_->SetVRegsCount( 783 std::max<size_t>(graph_->GetVRegsCount(), *std::max_element(vregs.begin(), vregs.end()))); 784 } 785 if (save_state_inst_vregs_map_.count(CurrentInstIndex()) == 0) { 786 save_state_inst_vregs_map_.emplace(CurrentInstIndex(), std::move(vregs)); 787 } 788 return *this; 789 } 790 NoVregs()791 IrConstructor &NoVregs() 792 { 793 ASSERT(CurrentInst()->IsSaveState()); 794 return *this; 795 } 796 CatchTypeIds(std::vector<uint16_t> && ids)797 IrConstructor &CatchTypeIds(std::vector<uint16_t> &&ids) 798 { 799 auto inst = CurrentInst(); 800 ASSERT(inst->GetOpcode() == Opcode::Try); 801 auto try_inst = inst->CastToTry(); 802 for (auto id : ids) { 803 try_inst->AppendCatchTypeId(id, 0); 804 } 805 return *this; 806 } 807 ThrowableInsts(std::vector<int> && ids)808 IrConstructor &ThrowableInsts(std::vector<int> &&ids) 809 { 810 auto inst = CurrentInst(); 811 ASSERT(inst->GetOpcode() == Opcode::CatchPhi); 812 auto catch_phi = inst->CastToCatchPhi(); 813 for (auto id : ids) { 814 ASSERT(inst_map_.count(id) > 0); 815 catch_phi->AppendThrowableInst(inst_map_.at(id)); 816 } 817 return *this; 818 } 819 DeoptimizeType(DeoptimizeType type)820 IrConstructor &DeoptimizeType(DeoptimizeType type) 821 { 822 auto inst = CurrentInst(); 823 if (inst->GetOpcode() == Opcode::Deoptimize) { 824 inst->CastToDeoptimize()->SetDeoptimizeType(type); 825 } else { 826 ASSERT(inst->GetOpcode() == Opcode::DeoptimizeIf); 827 inst->CastToDeoptimizeIf()->SetDeoptimizeType(type); 828 } 829 return *this; 830 } 831 SrcType(DataType::Type type)832 IrConstructor &SrcType(DataType::Type type) 833 { 834 auto inst = CurrentInst(); 835 switch (inst->GetOpcode()) { 836 case Opcode::Cmp: 837 inst->CastToCmp()->SetOperandsType(type); 838 break; 839 case Opcode::Compare: 840 inst->CastToCompare()->SetOperandsType(type); 841 break; 842 case Opcode::If: 843 inst->CastToIf()->SetOperandsType(type); 844 break; 845 case Opcode::AddOverflow: 846 inst->CastToAddOverflow()->SetOperandsType(type); 847 break; 848 case Opcode::SubOverflow: 849 inst->CastToSubOverflow()->SetOperandsType(type); 850 break; 851 case Opcode::IfImm: 852 inst->CastToIfImm()->SetOperandsType(type); 853 break; 854 case Opcode::Select: 855 inst->CastToSelect()->SetOperandsType(type); 856 break; 857 case Opcode::SelectImm: 858 inst->CastToSelectImm()->SetOperandsType(type); 859 break; 860 case Opcode::Cast: 861 inst->CastToCast()->SetOperandsType(type); 862 break; 863 default: 864 UNREACHABLE(); 865 } 866 return *this; 867 } 868 IntrinsicId(RuntimeInterface::IntrinsicId id)869 IrConstructor &IntrinsicId(RuntimeInterface::IntrinsicId id) 870 { 871 auto inst = CurrentInst(); 872 ASSERT(inst->IsIntrinsic()); 873 inst->CastToIntrinsic()->SetIntrinsicId(id); 874 AdjustFlags(id, inst); 875 return *this; 876 } 877 Relocate()878 IrConstructor &Relocate() 879 { 880 auto inst = CurrentInst(); 881 ASSERT(inst->IsIntrinsic()); 882 inst->CastToIntrinsic()->SetRelocate(); 883 return *this; 884 } 885 Class(RuntimeInterface::ClassPtr klass)886 IrConstructor &Class(RuntimeInterface::ClassPtr klass) 887 { 888 auto inst = CurrentInst(); 889 switch (inst->GetOpcode()) { 890 case Opcode::InitClass: 891 inst->CastToInitClass()->SetClass(klass); 892 break; 893 case Opcode::LoadClass: 894 inst->CastToLoadClass()->SetClass(klass); 895 break; 896 case Opcode::LoadAndInitClass: 897 inst->CastToLoadAndInitClass()->SetClass(klass); 898 break; 899 case Opcode::UnresolvedLoadAndInitClass: 900 inst->CastToUnresolvedLoadAndInitClass()->SetClass(klass); 901 break; 902 default: 903 UNREACHABLE(); 904 } 905 return *this; 906 } 907 908 template <typename T> ScopedLife()909 std::shared_ptr<IrConstructor> ScopedLife() 910 { 911 #ifndef __clang_analyzer__ 912 if constexpr (std::is_same_v<T, BasicBlock>) { 913 return std::shared_ptr<IrConstructor>(this, [](IrConstructor *b) { b->ResetCurrentBb(); }); 914 } else if constexpr (std::is_same_v<T, Inst>) { 915 return std::shared_ptr<IrConstructor>(this, [](IrConstructor *b) { b->ResetCurrentInst(); }); 916 } else if constexpr (std::is_same_v<T, Graph>) { 917 return std::shared_ptr<IrConstructor>(this, [](IrConstructor *b) { b->Finalize(); }); 918 } 919 #else 920 return nullptr; 921 #endif 922 } 923 CheckInputType(Inst * inst,Inst * input_inst,size_t input_idx)924 void CheckInputType(Inst *inst, Inst *input_inst, size_t input_idx) 925 { 926 auto type = input_inst->GetType(); 927 auto prev_type = inst->GetInputType(input_idx); 928 if (prev_type == DataType::Type::NO_TYPE) { 929 switch (inst->GetOpcode()) { 930 case Opcode::Cmp: 931 inst->CastToCmp()->SetOperandsType(type); 932 break; 933 case Opcode::Compare: 934 inst->CastToCompare()->SetOperandsType(type); 935 break; 936 case Opcode::If: 937 inst->CastToIf()->SetOperandsType(type); 938 break; 939 case Opcode::IfImm: 940 inst->CastToIfImm()->SetOperandsType(type); 941 break; 942 case Opcode::Select: 943 inst->CastToSelect()->SetOperandsType(type); 944 break; 945 case Opcode::SelectImm: 946 inst->CastToSelectImm()->SetOperandsType(type); 947 break; 948 default: 949 UNREACHABLE(); 950 } 951 } else { 952 CHECK_EQ(type, prev_type); 953 } 954 } 955 ConstructControlFlow()956 void ConstructControlFlow() 957 { 958 for (auto [bbi, succs] : bb_succs_map_) { 959 auto bb = bb_map_.at(bbi); 960 for (auto succ : succs) { 961 bb->AddSucc(bb_map_.at(succ == -1 ? 1 : succ)); 962 } 963 if (succs.size() > 1 && bb->IsEmpty()) { 964 bb->SetTryEnd(true); 965 } 966 } 967 auto end_block = graph_->GetEndBlock(); 968 if (end_block->GetPredsBlocks().empty()) { 969 graph_->EraseBlock(end_block); 970 graph_->SetEndBlock(nullptr); 971 } 972 } 973 ConstructDataFlow()974 void ConstructDataFlow() 975 { 976 for (auto [insti, inputs] : inst_inputs_map_) { 977 auto inst = inst_map_.at(insti); 978 const auto &vregs {inst->IsSaveState() ? save_state_inst_vregs_map_[insti] : std::vector<int> {}}; 979 ASSERT(!inst->IsSaveState() || inputs.size() == vregs.size()); 980 size_t idx = 0; 981 if (inst->IsOperandsDynamic()) { 982 inst->ReserveInputs(inputs.size()); 983 } 984 for (auto input_idx : inputs) { 985 ASSERT_DO(inst_map_.find(input_idx) != inst_map_.end(), 986 std::cerr << "Input with Id " << input_idx << " isn't found, inst: " << *inst << std::endl); 987 auto input_inst = inst_map_.at(input_idx); 988 auto op = inst->GetOpcode(); 989 if (!input_inst->IsConst() && 990 (op == Opcode::Cmp || op == Opcode::Compare || op == Opcode::If || op == Opcode::IfImm || 991 op == Opcode::Select || op == Opcode::SelectImm)) { 992 CheckInputType(inst, input_inst, idx); 993 } 994 if (inst->IsOperandsDynamic()) { 995 inst->AppendInput(input_inst); 996 if (inst->IsSaveState()) { 997 static_cast<SaveStateInst *>(inst)->SetVirtualRegister(idx, VirtualRegister(vregs[idx], false)); 998 } 999 } else { 1000 inst->SetInput(idx, input_inst); 1001 } 1002 ++idx; 1003 } 1004 } 1005 1006 for (auto [insti, inputs] : phi_inst_inputs_map_) { 1007 auto inst = inst_map_.at(insti); 1008 for (auto input : inputs) { 1009 auto input_inst = inst_map_.at(input.second); 1010 size_t idx = inst->GetBasicBlock()->GetPredBlockIndex(bb_map_.at(input.first)); 1011 auto i {inst->AppendInput(input_inst)}; 1012 inst->CastToPhi()->SetPhiInputBbNum(i, idx); 1013 } 1014 } 1015 } 1016 UpdateSpecialFlags()1017 void UpdateSpecialFlags() 1018 { 1019 int max_id = graph_->GetCurrentInstructionId(); 1020 for (auto pair : inst_map_) { 1021 auto id = pair.first; 1022 auto inst = pair.second; 1023 if (inst->GetType() == DataType::REFERENCE) { 1024 if (inst->GetOpcode() == Opcode::StoreArray) { 1025 inst->CastToStoreArray()->SetNeedBarrier(true); 1026 } 1027 if (inst->GetOpcode() == Opcode::StoreArrayI) { 1028 inst->CastToStoreArrayI()->SetNeedBarrier(true); 1029 } 1030 if (inst->GetOpcode() == Opcode::StoreStatic) { 1031 inst->CastToStoreStatic()->SetNeedBarrier(true); 1032 } 1033 if (inst->GetOpcode() == Opcode::UnresolvedStoreStatic) { 1034 inst->CastToUnresolvedStoreStatic()->SetNeedBarrier(true); 1035 } 1036 if (inst->GetOpcode() == Opcode::StoreObject) { 1037 inst->CastToStoreObject()->SetNeedBarrier(true); 1038 } 1039 if (inst->GetOpcode() == Opcode::UnresolvedStoreObject) { 1040 inst->CastToUnresolvedStoreObject()->SetNeedBarrier(true); 1041 } 1042 if (inst->GetOpcode() == Opcode::StoreArrayPair) { 1043 inst->CastToStoreArrayPair()->SetNeedBarrier(true); 1044 } 1045 if (inst->GetOpcode() == Opcode::StoreArrayPairI) { 1046 inst->CastToStoreArrayPairI()->SetNeedBarrier(true); 1047 } 1048 } 1049 if (inst->GetOpcode() == Opcode::Try) { 1050 auto bb = inst->GetBasicBlock(); 1051 bb->SetTryBegin(true); 1052 bb->GetSuccessor(0)->SetTry(true); 1053 for (size_t idx = 1; idx < bb->GetSuccsBlocks().size(); idx++) { 1054 bb->GetSuccessor(idx)->SetCatchBegin(true); 1055 } 1056 } 1057 if (inst->GetOpcode() == Opcode::SaveStateOsr) { 1058 inst->GetBasicBlock()->SetOsrEntry(true); 1059 } 1060 if (id >= max_id) { 1061 max_id = id + 1; 1062 } 1063 } 1064 graph_->SetCurrentInstructionId(max_id); 1065 } 1066 1067 // Create SaveState instructions thet weren't explicitly constructed in the test CreateSaveStates()1068 void CreateSaveStates() 1069 { 1070 for (auto [insti, inputs] : inst_inputs_map_) { 1071 auto inst = inst_map_.at(insti); 1072 if (!inst->IsOperandsDynamic() && inst->RequireState() && inst->GetInputsCount() > inputs.size()) { 1073 auto save_state = graph_->CreateInstSaveState(); 1074 save_state->SetId(static_cast<int>(graph_->GetCurrentInstructionId()) + 1); 1075 graph_->SetCurrentInstructionId(save_state->GetId() + 1); 1076 inst->GetBasicBlock()->InsertBefore(save_state, inst); 1077 inst->SetSaveState(save_state); 1078 } 1079 } 1080 } 1081 SetSpillFillData()1082 void SetSpillFillData() 1083 { 1084 graph_->ResetParameterInfo(); 1085 // Count number of parameters (needed for bytecode optimizer) in first cycle and set SpillFillData for each 1086 // parameter in second cycle 1087 uint32_t num_args = 0; 1088 for (auto inst : graph_->GetStartBlock()->Insts()) { 1089 if (inst->GetOpcode() == Opcode::Parameter) { 1090 ++num_args; 1091 } 1092 } 1093 uint32_t i = 0; 1094 for (auto inst : graph_->GetStartBlock()->Insts()) { 1095 if (inst->GetOpcode() != Opcode::Parameter) { 1096 continue; 1097 } 1098 ++i; 1099 1100 auto type = inst->GetType(); 1101 InstBuilder::SetParamSpillFill(graph_, static_cast<ParameterInst *>(inst), num_args, i - 1, type); 1102 } 1103 } 1104 Finalize()1105 void Finalize() 1106 { 1107 ConstructControlFlow(); 1108 ConstructDataFlow(); 1109 UpdateSpecialFlags(); 1110 CreateSaveStates(); 1111 SetSpillFillData(); 1112 ResetCurrentBb(); 1113 ResetCurrentInst(); 1114 graph_->RunPass<LoopAnalyzer>(); 1115 PropagateRegisters(); 1116 if (enable_graph_checker_) { 1117 GraphChecker(graph_).Check(); 1118 } 1119 } 1120 GetInst(unsigned index)1121 Inst &GetInst(unsigned index) 1122 { 1123 return *inst_map_.at(index); 1124 } 1125 GetBlock(unsigned index)1126 BasicBlock &GetBlock(unsigned index) 1127 { 1128 return *bb_map_.at(index); 1129 } 1130 EnableGraphChecker(bool value)1131 void EnableGraphChecker(bool value) 1132 { 1133 enable_graph_checker_ = value; 1134 } 1135 1136 private: AddInput(int v)1137 void AddInput(int v) 1138 { 1139 inst_inputs_map_[CurrentInstIndex()].push_back(v); 1140 } 1141 1142 template <typename T, typename... Args> AddInput(T v,Args...args)1143 void AddInput(T v, Args... args) 1144 { 1145 inst_inputs_map_[CurrentInstIndex()].push_back(v); 1146 AddInput(args...); 1147 } 1148 GetBbByIndex(int index)1149 BasicBlock *GetBbByIndex(int index) 1150 { 1151 return bb_map_.at(index); 1152 } 1153 CurrentBb()1154 BasicBlock *CurrentBb() 1155 { 1156 return current_bb_.second; 1157 } 1158 CurrentBbIndex()1159 int CurrentBbIndex() 1160 { 1161 return current_bb_.first; 1162 } 1163 CurrentInst()1164 Inst *CurrentInst() 1165 { 1166 return current_inst_.second; 1167 } 1168 CurrentInstIndex()1169 int CurrentInstIndex() 1170 { 1171 return current_inst_.first; 1172 } 1173 ResetCurrentBb()1174 void ResetCurrentBb() 1175 { 1176 current_bb_ = {-1, nullptr}; 1177 } 1178 ResetCurrentInst()1179 void ResetCurrentInst() 1180 { 1181 current_inst_ = {-1, nullptr}; 1182 } 1183 PropagateRegisters()1184 void PropagateRegisters() 1185 { 1186 for (auto bb : graph_->GetBlocksRPO()) { 1187 for (auto inst : bb->AllInsts()) { 1188 if (inst->GetDstReg() != INVALID_REG && !inst->IsOperandsDynamic()) { 1189 for (size_t i = 0; i < inst->GetInputsCount(); i++) { 1190 inst->SetSrcReg(i, inst->GetInputs()[i].GetInst()->GetDstReg()); 1191 } 1192 } 1193 } 1194 } 1195 } 1196 1197 private: 1198 Graph *graph_ {nullptr}; 1199 ArenaAllocator aa_; 1200 std::pair<int, BasicBlock *> current_bb_; 1201 std::pair<int, Inst *> current_inst_; 1202 ArenaUnorderedMap<int, BasicBlock *> bb_map_ {aa_.Adapter()}; 1203 ArenaVector<std::pair<int, std::vector<int>>> bb_succs_map_ {aa_.Adapter()}; 1204 ArenaUnorderedMap<int, Inst *> inst_map_ {aa_.Adapter()}; 1205 ArenaUnorderedMap<int, std::vector<int>> inst_inputs_map_ {aa_.Adapter()}; 1206 ArenaUnorderedMap<int, std::vector<int>> save_state_inst_vregs_map_ {aa_.Adapter()}; 1207 ArenaUnorderedMap<int, std::vector<std::pair<int, int>>> phi_inst_inputs_map_ {aa_.Adapter()}; 1208 bool enable_graph_checker_ {true}; 1209 }; 1210 1211 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1212 #define GRAPH(GRAPH) if (auto __g = builder_->SetGraph(GRAPH).ScopedLife<Graph>(); true) 1213 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1214 #define BASIC_BLOCK(ID, ...) \ 1215 if (auto __b = builder_->NewBlock<ID>().Succs({__VA_ARGS__}).ScopedLife<BasicBlock>(); true) 1216 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1217 #define INST(ID, ...) builder_->NewInst(ID, __VA_ARGS__) 1218 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1219 #define CONSTANT(ID, VALUE) builder_->NewConstant(ID, VALUE) 1220 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1221 #define PARAMETER(ID, ARG_NUM) builder_->NewParameter(ID, ARG_NUM) 1222 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1223 #define INS(INDEX) builder_->GetInst(INDEX) 1224 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1225 #define BB(INDEX) builder_->GetBlock(INDEX) 1226 } // namespace panda::compiler 1227 1228 #endif // PANDA_IR_CONSTRUCTOR_H 1229