1 /* 2 * Copyright (c) 2021-2024 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 21 #ifdef PANDA_COMPILER_DEBUG_INFO 22 #include "debug_info.h" 23 #endif 24 25 #include "graph.h" 26 #include "graph_checker.h" 27 #include "mark_word.h" 28 #include "optimizer/ir_builder/inst_builder.h" 29 30 namespace ark::compiler { 31 /** 32 * This class aims to simplify IR construction. 33 * 34 * Example: 35 * auto g = CreateEmptyGraph(); 36 * GRAPH(g) { 37 * BASIC_BLOCK(0, 1, 2) { 38 * INST(0, Opcode::IntConstant).Constant(12); 39 * INST(1, Opcode::IntConstant).Constant(12); 40 * INST(2, Opcode::Add).Inputs(0, 1); 41 * INST(6, Opcode::Compare).Inputs(2).CC(ConditionCode::CC_AE); 42 * INST(7, Opcode::If).Inputs(6); 43 * } 44 * BASIC_BLOCK(1, 2) { 45 * INST(3, Opcode::Not).Inputs(0); 46 * } 47 * BASIC_BLOCK(2, -1) { 48 * INST(4, Opcode::Phi).Inputs(2, 3); 49 * INST(5, Opcode::Not).Inputs(4); 50 * } 51 * } 52 * g->Dump(cerr); 53 * 54 * GRAPH(g) macro initializies Builder object by 'g' graph. Builder works with this graph only inside followed scope 55 * in braces. 56 * BASIC_BLOCK creates new basic block and add it to the current graph. All code inside followed scope will work with 57 * this basic block. 58 * First argument is ID of basic block. It must be unique for graph. 59 * All remaining arguments are IDs of successors blocks. '-1' value means that there is no successor. Block 60 * that hasn't successors is considered as end block. 61 * Block with '0' ID is considered as start block. 62 * INST creates new instruction and append it to the current basic block. 63 * First parameter is ID of instruction. It must be unique within the current graph 64 * Second parameter is an opcode. 65 * Dataflow can be constructed via 'Inputs' method, that gets IDs of the input instructions as parameters. 66 * All other properties of instruction may be set via corresponding proxy methods, defined in Builder. 67 */ 68 class IrConstructor final { 69 public: 70 static const size_t ID_ENTRY_BB = 0; 71 static const size_t ID_EXIT_BB = 1U; 72 73 static constexpr DataType::Type MARK_WORD_TYPE = DataType::GetIntTypeBySize(sizeof(MarkWord::MarkWordSize)); 74 IrConstructor()75 IrConstructor() : aa_(SpaceType::SPACE_TYPE_COMPILER) {} 76 SetGraph(Graph * graph)77 IrConstructor &SetGraph(Graph *graph) 78 { 79 graph_ = graph; 80 if (graph_->GetStartBlock() == nullptr) { 81 graph_->CreateStartBlock(); 82 } 83 if (graph_->GetEndBlock() == nullptr) { 84 graph_->CreateEndBlock(0U); 85 } 86 ASSERT(graph_->GetVectorBlocks().size() == 2U); 87 bbMap_.clear(); 88 bbMap_[ID_ENTRY_BB] = graph_->GetStartBlock(); 89 bbMap_[ID_EXIT_BB] = graph_->GetEndBlock(); 90 bbSuccsMap_.clear(); 91 instMap_.clear(); 92 instInputsMap_.clear(); 93 saveStateInstVregsMap_.clear(); 94 phiInstInputsMap_.clear(); 95 return *this; 96 } 97 98 template <size_t ID> NewBlock()99 IrConstructor &NewBlock() 100 { 101 ASSERT(ID != ID_ENTRY_BB && ID != ID_EXIT_BB); 102 ASSERT(bbMap_.count(ID) == 0); 103 ASSERT(CurrentBb() == nullptr); 104 auto bb = graph_->GetAllocator()->New<BasicBlock>(graph_); 105 bb->SetGuestPc(0U); 106 #ifdef NDEBUG 107 graph_->AddBlock(bb); 108 #else 109 graph_->AddBlock(bb, ID); 110 #endif 111 currentBb_ = {ID, bb}; 112 bbMap_[ID] = bb; 113 // add connection the first custom block with entry 114 if (bbSuccsMap_.empty()) { 115 graph_->GetStartBlock()->AddSucc(bb); 116 } 117 return *this; 118 } 119 120 template <typename... Args> NewInst(size_t id,Args &&...args)121 IrConstructor &NewInst(size_t id, Args &&...args) 122 { 123 ASSERT_DO(instMap_.find(id) == instMap_.end(), 124 std::cerr << "Instruction with same Id " << id << " already exists"); 125 auto inst = graph_->CreateInst(std::forward<Args>(args)...); 126 inst->SetId(id); 127 for (size_t i = 0; i < inst->GetInputsCount(); ++i) { 128 inst->SetSrcReg(i, GetInvalidReg()); 129 } 130 currentInst_ = {id, inst}; 131 instMap_[id] = inst; 132 auto block = CurrentBb(); 133 if (block == nullptr) { 134 block = graph_->GetStartBlock(); 135 } 136 ASSERT(block); 137 if (inst->IsPhi()) { 138 block->AppendPhi(inst); 139 } else { 140 block->AppendInst(inst); 141 } 142 #ifndef NDEBUG 143 if (inst->IsLowLevel()) { 144 // GraphChecker hack: LowLevel instructions may appear only after Lowering pass: 145 graph_->SetLowLevelInstructionsEnabled(); 146 } 147 #endif 148 if (inst->IsSaveState()) { 149 graph_->SetVRegsCount(std::max(graph_->GetVRegsCount(), sizeof...(args))); 150 } 151 return *this; 152 } 153 154 template <typename T> NewConstant(size_t id,T value)155 IrConstructor &NewConstant(size_t id, [[maybe_unused]] T value) 156 { 157 ASSERT_DO(instMap_.find(id) == instMap_.end(), 158 std::cerr << "Instruction with same Id " << id << "already exists"); 159 Inst *inst = nullptr; 160 auto toStartBb = (CurrentBbIndex() == 0) || (CurrentBbIndex() == -1); 161 if constexpr (std::is_same<T, std::nullptr_t>()) { 162 if (toStartBb) { 163 inst = graph_->GetOrCreateNullPtr(); 164 } else { 165 inst = graph_->CreateInstNullPtr(DataType::REFERENCE); 166 CurrentBb()->AppendInst(inst); 167 } 168 } else { // NOLINT(readability-misleading-indentation) 169 if (toStartBb) { 170 inst = graph_->FindOrCreateConstant(value); 171 } else { 172 inst = graph_->CreateInstConstant(value, graph_->IsBytecodeOptimizer()); 173 CurrentBb()->AppendInst(inst); 174 } 175 } 176 inst->SetId(id); 177 instMap_[id] = inst; 178 currentInst_ = {id, inst}; 179 return *this; 180 } 181 NewParameter(int id,uint16_t argNumber)182 IrConstructor &NewParameter(int id, uint16_t argNumber) 183 { 184 ASSERT_DO(instMap_.find(id) == instMap_.end(), 185 std::cerr << "Instruction with same Id " << id << "already exists"); 186 auto inst = graph_->AddNewParameter(argNumber); 187 inst->SetId(id); 188 instMap_[id] = inst; 189 currentInst_ = {id, inst}; 190 return *this; 191 } 192 AddNullptrInst(int id)193 IrConstructor &AddNullptrInst(int id) 194 { 195 ASSERT_DO(instMap_.find(id) == instMap_.end(), 196 std::cerr << "Instruction with same Id " << id << "already exists"); 197 auto inst = graph_->GetOrCreateNullPtr(); 198 inst->SetId(id); 199 instMap_[id] = inst; 200 currentInst_ = {id, inst}; 201 return *this; 202 } 203 Succs(std::vector<int> succs)204 IrConstructor &Succs(std::vector<int> succs) 205 { 206 bbSuccsMap_.emplace_back(CurrentBbIndex(), std::move(succs)); 207 return *this; 208 } 209 210 /** 211 * Define inputs for current instruction. 212 * Input is an index of input instruction. 213 */ 214 template <typename... Args> Inputs(Args...inputs)215 IrConstructor &Inputs(Args... inputs) 216 { 217 ASSERT(!CurrentInst()->IsCall() && !CurrentInst()->IsIntrinsic()); 218 instInputsMap_[CurrentInstIndex()].reserve(sizeof...(inputs)); 219 // NOLINTNEXTLINE(bugprone-suspicious-semicolon) 220 if constexpr (sizeof...(inputs) != 0) { 221 AddInput(inputs...); 222 } 223 return *this; 224 } 225 226 /** 227 * Define inputs for current call-, intrinsic-, or phi-instriction. 228 * Input is defined by std::pair. 229 * In case of phi: first is index of basic block, second is index of input instruction. 230 * In case of call and intrinsic: first is Type of input, second is index of input instruction. 231 */ Inputs(std::initializer_list<std::pair<int,int>> inputs)232 IrConstructor &Inputs(std::initializer_list<std::pair<int, int>> inputs) 233 { 234 ASSERT(CurrentInst()->IsPhi() || CurrentInst()->IsCall() || CurrentInst()->IsInitObject() || 235 CurrentInst()->IsIntrinsic()); 236 if (CurrentInst()->IsPhi()) { 237 phiInstInputsMap_[CurrentInstIndex()].reserve(inputs.size()); 238 for (const auto &input : inputs) { 239 phiInstInputsMap_[CurrentInstIndex()].push_back(input); 240 } 241 } else { 242 auto opc = CurrentInst()->GetOpcode(); 243 using Types = InputTypesMixin<DynamicInputsInst>; 244 Types *types; 245 switch (opc) { 246 case Opcode::Intrinsic: 247 types = static_cast<Types *>(CurrentInst()->CastToIntrinsic()); 248 break; 249 case Opcode::CallIndirect: 250 types = static_cast<Types *>(CurrentInst()->CastToCallIndirect()); 251 break; 252 case Opcode::Builtin: 253 types = static_cast<Types *>(CurrentInst()->CastToBuiltin()); 254 break; 255 case Opcode::InitObject: 256 types = static_cast<Types *>(CurrentInst()->CastToInitObject()); 257 break; 258 default: 259 ASSERT(CurrentInst()->IsCall()); 260 types = static_cast<Types *>(static_cast<CallInst *>(CurrentInst())); 261 break; 262 } 263 264 instInputsMap_[CurrentInstIndex()].reserve(inputs.size()); 265 types->AllocateInputTypes(graph_->GetAllocator(), inputs.size()); 266 for (const auto &input : inputs) { 267 types->AddInputType(static_cast<DataType::Type>(input.first)); 268 instInputsMap_[CurrentInstIndex()].push_back(input.second); 269 } 270 } 271 return *this; 272 } 273 274 /** 275 * Same as the default Inputs() method, but defines inputs' types with respect to call-instructions. 276 * Copies types of inputs to the instruction's input_types_. 277 */ 278 template <typename... Args> InputsAutoType(Args...inputs)279 IrConstructor &InputsAutoType(Args... inputs) 280 { 281 using Types = InputTypesMixin<DynamicInputsInst>; 282 Types *types; 283 switch (CurrentInst()->GetOpcode()) { 284 case Opcode::Intrinsic: 285 types = static_cast<Types *>(CurrentInst()->CastToIntrinsic()); 286 break; 287 case Opcode::MultiArray: 288 types = static_cast<Types *>(CurrentInst()->CastToMultiArray()); 289 break; 290 default: 291 ASSERT(CurrentInst()->IsCall()); 292 types = static_cast<Types *>(static_cast<CallInst *>(CurrentInst())); 293 break; 294 } 295 types->AllocateInputTypes(graph_->GetAllocator(), sizeof...(inputs)); 296 ((types->AddInputType(GetInst(inputs).GetType())), ...); 297 instInputsMap_[CurrentInstIndex()].reserve(sizeof...(inputs)); 298 ((instInputsMap_[CurrentInstIndex()].push_back(inputs)), ...); 299 return *this; 300 } 301 Pc(uint32_t pc)302 IrConstructor &Pc(uint32_t pc) 303 { 304 ASSERT(CurrentInst()); 305 CurrentInst()->SetPc(pc); 306 return *this; 307 } 308 309 IrConstructor &Volatile(bool volat = true) 310 { 311 auto inst = CurrentInst(); 312 switch (inst->GetOpcode()) { 313 case Opcode::StoreObject: 314 inst->CastToStoreObject()->SetVolatile(volat); 315 break; 316 case Opcode::LoadObject: 317 inst->CastToLoadObject()->SetVolatile(volat); 318 break; 319 case Opcode::StoreStatic: 320 inst->CastToStoreStatic()->SetVolatile(volat); 321 break; 322 case Opcode::LoadStatic: 323 inst->CastToLoadStatic()->SetVolatile(volat); 324 break; 325 case Opcode::Store: 326 inst->CastToStore()->SetVolatile(volat); 327 break; 328 case Opcode::Load: 329 inst->CastToLoad()->SetVolatile(volat); 330 break; 331 case Opcode::StoreI: 332 inst->CastToStoreI()->SetVolatile(volat); 333 break; 334 case Opcode::LoadI: 335 inst->CastToLoadI()->SetVolatile(volat); 336 break; 337 default: 338 UNREACHABLE(); 339 } 340 return *this; 341 } 342 Likely()343 IrConstructor &Likely() 344 { 345 auto inst = CurrentInst(); 346 switch (inst->GetOpcode()) { 347 case Opcode::If: 348 ASSERT(!inst->CastToIf()->IsUnlikely()); 349 inst->CastToIf()->SetLikely(); 350 break; 351 case Opcode::IfImm: 352 ASSERT(!inst->CastToIfImm()->IsUnlikely()); 353 inst->CastToIfImm()->SetLikely(); 354 break; 355 default: 356 UNREACHABLE(); 357 } 358 return *this; 359 } 360 Unlikely()361 IrConstructor &Unlikely() 362 { 363 auto inst = CurrentInst(); 364 switch (inst->GetOpcode()) { 365 case Opcode::If: 366 ASSERT(!inst->CastToIf()->IsLikely()); 367 inst->CastToIf()->SetUnlikely(); 368 break; 369 case Opcode::IfImm: 370 ASSERT(!inst->CastToIfImm()->IsLikely()); 371 inst->CastToIfImm()->SetUnlikely(); 372 break; 373 default: 374 UNREACHABLE(); 375 } 376 return *this; 377 } 378 IsArray(bool value)379 IrConstructor &IsArray(bool value) 380 { 381 auto inst = CurrentInst(); 382 switch (inst->GetOpcode()) { 383 case Opcode::LoadArray: 384 inst->CastToLoadArray()->SetIsArray(value); 385 break; 386 case Opcode::LenArray: 387 inst->CastToLenArray()->SetIsArray(value); 388 break; 389 default: 390 UNREACHABLE(); 391 } 392 return *this; 393 } 394 CC(ConditionCode cc)395 IrConstructor &CC(ConditionCode cc) 396 { 397 auto inst = CurrentInst(); 398 switch (inst->GetOpcode()) { 399 case Opcode::Compare: 400 inst->CastToCompare()->SetCc(cc); 401 break; 402 case Opcode::If: 403 inst->CastToIf()->SetCc(cc); 404 break; 405 case Opcode::AddOverflow: 406 inst->CastToAddOverflow()->SetCc(cc); 407 break; 408 case Opcode::SubOverflow: 409 inst->CastToSubOverflow()->SetCc(cc); 410 break; 411 case Opcode::IfImm: 412 inst->CastToIfImm()->SetCc(cc); 413 break; 414 case Opcode::Select: 415 inst->CastToSelect()->SetCc(cc); 416 break; 417 case Opcode::SelectImm: 418 inst->CastToSelectImm()->SetCc(cc); 419 break; 420 case Opcode::DeoptimizeCompare: 421 inst->CastToDeoptimizeCompare()->SetCc(cc); 422 break; 423 case Opcode::DeoptimizeCompareImm: 424 inst->CastToDeoptimizeCompareImm()->SetCc(cc); 425 break; 426 default: 427 UNREACHABLE(); 428 } 429 return *this; 430 } 431 SetFlag(compiler::inst_flags::Flags flag)432 IrConstructor &SetFlag(compiler::inst_flags::Flags flag) 433 { 434 CurrentInst()->SetFlag(flag); 435 return *this; 436 } 437 ClearFlag(compiler::inst_flags::Flags flag)438 IrConstructor &ClearFlag(compiler::inst_flags::Flags flag) 439 { 440 CurrentInst()->ClearFlag(flag); 441 return *this; 442 } 443 Inlined()444 IrConstructor &Inlined() 445 { 446 auto inst = CurrentInst(); 447 if (inst->GetOpcode() == Opcode::Intrinsic) { 448 inst->CastToIntrinsic()->SetInlined(true); 449 return *this; 450 } 451 452 ASSERT(inst->GetOpcode() == Opcode::CallStatic || inst->GetOpcode() == Opcode::CallVirtual || 453 inst->GetOpcode() == Opcode::CallDynamic); 454 static_cast<CallInst *>(inst)->SetInlined(true); 455 inst->SetFlag(inst_flags::NO_DST); 456 return *this; 457 } 458 Caller(unsigned index)459 IrConstructor &Caller(unsigned index) 460 { 461 auto inst = CurrentInst(); 462 ASSERT(inst->GetOpcode() == Opcode::SaveState); 463 auto callInst = &GetInst(index); 464 ASSERT(callInst->GetOpcode() == Opcode::CallStatic || callInst->GetOpcode() == Opcode::CallVirtual || 465 callInst->GetOpcode() == Opcode::CallDynamic); 466 inst->CastToSaveState()->SetCallerInst(static_cast<CallInst *>(callInst)); 467 return *this; 468 } 469 Scale(uint64_t scale)470 IrConstructor &Scale(uint64_t scale) 471 { 472 auto inst = CurrentInst(); 473 switch (inst->GetOpcode()) { 474 case Opcode::Load: 475 inst->CastToLoad()->SetScale(scale); 476 break; 477 case Opcode::Store: 478 inst->CastToStore()->SetScale(scale); 479 break; 480 default: 481 UNREACHABLE(); 482 } 483 return *this; 484 } 485 486 // CC-OFFNXT(huge_method, G.FUN.01) big switch-case Imm(uint64_t imm)487 IrConstructor &Imm(uint64_t imm) 488 { 489 auto inst = CurrentInst(); 490 switch (inst->GetOpcode()) { 491 case Opcode::AddI: 492 case Opcode::SubI: 493 case Opcode::MulI: 494 case Opcode::DivI: 495 case Opcode::ModI: 496 case Opcode::ShlI: 497 case Opcode::ShrI: 498 case Opcode::AShrI: 499 case Opcode::AndI: 500 case Opcode::OrI: 501 case Opcode::XorI: 502 static_cast<BinaryImmOperation *>(inst)->SetImm(imm); 503 break; 504 case Opcode::BoundsCheckI: 505 inst->CastToBoundsCheckI()->SetImm(imm); 506 break; 507 case Opcode::LoadArrayI: 508 inst->CastToLoadArrayI()->SetImm(imm); 509 break; 510 case Opcode::StoreArrayI: 511 inst->CastToStoreArrayI()->SetImm(imm); 512 break; 513 case Opcode::LoadI: 514 inst->CastToLoadI()->SetImm(imm); 515 break; 516 case Opcode::StoreI: 517 inst->CastToStoreI()->SetImm(imm); 518 break; 519 case Opcode::ReturnI: 520 inst->CastToReturnI()->SetImm(imm); 521 break; 522 case Opcode::IfImm: 523 inst->CastToIfImm()->SetImm(imm); 524 break; 525 case Opcode::SelectImm: 526 inst->CastToSelectImm()->SetImm(imm); 527 break; 528 case Opcode::LoadArrayPairI: 529 inst->CastToLoadArrayPairI()->SetImm(imm); 530 break; 531 case Opcode::StoreArrayPairI: 532 inst->CastToStoreArrayPairI()->SetImm(imm); 533 break; 534 case Opcode::LoadPairPart: 535 inst->CastToLoadPairPart()->SetImm(imm); 536 break; 537 case Opcode::DeoptimizeCompareImm: 538 inst->CastToDeoptimizeCompareImm()->SetImm(imm); 539 break; 540 case Opcode::LoadArrayPair: 541 inst->CastToLoadArrayPair()->SetImm(imm); 542 break; 543 case Opcode::StoreArrayPair: 544 inst->CastToStoreArrayPair()->SetImm(imm); 545 break; 546 default: 547 UNREACHABLE(); 548 } 549 return *this; 550 } 551 Shift(ShiftType shiftType,uint64_t imm)552 IrConstructor &Shift(ShiftType shiftType, uint64_t imm) 553 { 554 auto inst = CurrentInst(); 555 switch (inst->GetOpcode()) { 556 case Opcode::AndSR: 557 case Opcode::OrSR: 558 case Opcode::XorSR: 559 case Opcode::AndNotSR: 560 case Opcode::OrNotSR: 561 case Opcode::XorNotSR: 562 case Opcode::AddSR: 563 case Opcode::SubSR: 564 static_cast<BinaryShiftedRegisterOperation *>(inst)->SetShiftType(shiftType); 565 static_cast<BinaryShiftedRegisterOperation *>(inst)->SetImm(imm); 566 break; 567 case Opcode::NegSR: 568 static_cast<UnaryShiftedRegisterOperation *>(inst)->SetShiftType(shiftType); 569 static_cast<UnaryShiftedRegisterOperation *>(inst)->SetImm(imm); 570 break; 571 default: 572 UNREACHABLE(); 573 } 574 return *this; 575 }; 576 Exit()577 IrConstructor &Exit() 578 { 579 CurrentInst()->CastToMonitor()->SetExit(); 580 return *this; 581 } 582 Entry()583 IrConstructor &Entry() 584 { 585 CurrentInst()->CastToMonitor()->SetEntry(); 586 return *this; 587 } 588 589 IrConstructor &Fcmpg(bool fcmpg = true) 590 { 591 CurrentInst()->CastToCmp()->SetFcmpg(fcmpg); 592 return *this; 593 } 594 595 IrConstructor &Fcmpl(bool fcmpl = true) 596 { 597 CurrentInst()->CastToCmp()->SetFcmpl(fcmpl); 598 return *this; 599 } 600 u8()601 IrConstructor &u8() // NOLINT(readability-identifier-naming) 602 { 603 CurrentInst()->SetType(DataType::UINT8); 604 return *this; 605 } u16()606 IrConstructor &u16() // NOLINT(readability-identifier-naming) 607 { 608 CurrentInst()->SetType(DataType::UINT16); 609 return *this; 610 } u32()611 IrConstructor &u32() // NOLINT(readability-identifier-naming) 612 { 613 CurrentInst()->SetType(DataType::UINT32); 614 return *this; 615 } u64()616 IrConstructor &u64() // NOLINT(readability-identifier-naming) 617 { 618 CurrentInst()->SetType(DataType::UINT64); 619 return *this; 620 } s8()621 IrConstructor &s8() // NOLINT(readability-identifier-naming) 622 { 623 CurrentInst()->SetType(DataType::INT8); 624 return *this; 625 } s16()626 IrConstructor &s16() // NOLINT(readability-identifier-naming) 627 { 628 CurrentInst()->SetType(DataType::INT16); 629 return *this; 630 } s32()631 IrConstructor &s32() // NOLINT(readability-identifier-naming) 632 { 633 CurrentInst()->SetType(DataType::INT32); 634 return *this; 635 } s64()636 IrConstructor &s64() // NOLINT(readability-identifier-naming) 637 { 638 CurrentInst()->SetType(DataType::INT64); 639 return *this; 640 } i8()641 IrConstructor &i8() // NOLINT(readability-identifier-naming) 642 { 643 return s8(); 644 } i16()645 IrConstructor &i16() // NOLINT(readability-identifier-naming) 646 { 647 return s16(); 648 } i32()649 IrConstructor &i32() // NOLINT(readability-identifier-naming) 650 { 651 return s32(); 652 } i64()653 IrConstructor &i64() // NOLINT(readability-identifier-naming) 654 { 655 return s64(); 656 } b()657 IrConstructor &b() // NOLINT(readability-identifier-naming) 658 { 659 CurrentInst()->SetType(DataType::BOOL); 660 return *this; 661 } ref()662 IrConstructor &ref() // NOLINT(readability-identifier-naming) 663 { 664 CurrentInst()->SetType(DataType::REFERENCE); 665 return *this; 666 } ptr()667 IrConstructor &ptr() // NOLINT(readability-identifier-naming) 668 { 669 CurrentInst()->SetType(DataType::POINTER); 670 return *this; 671 } w()672 IrConstructor &w() // NOLINT(readability-identifier-naming) 673 { 674 return ptr(); 675 } 676 // Type representing MarkWord mw()677 IrConstructor &mw() // NOLINT(readability-identifier-naming) 678 { 679 return type(MARK_WORD_TYPE); 680 } 681 // Type representing uint type for ref ref_uint()682 IrConstructor &ref_uint() // NOLINT(readability-identifier-naming) 683 { 684 return type(DataType::GetIntTypeForReference(graph_->GetArch())); 685 } f32()686 IrConstructor &f32() // NOLINT(readability-identifier-naming) 687 { 688 CurrentInst()->SetType(DataType::FLOAT32); 689 return *this; 690 } f64()691 IrConstructor &f64() // NOLINT(readability-identifier-naming) 692 { 693 CurrentInst()->SetType(DataType::FLOAT64); 694 return *this; 695 } any()696 IrConstructor &any() // NOLINT(readability-identifier-naming) 697 { 698 CurrentInst()->SetType(DataType::ANY); 699 return *this; 700 } SetType(DataType::Type type)701 IrConstructor &SetType(DataType::Type type) // NOLINT(readability-identifier-naming) 702 { 703 CurrentInst()->SetType(type); 704 return *this; 705 } AnyType(AnyBaseType anyType)706 IrConstructor &AnyType(AnyBaseType anyType) 707 { 708 auto *atm = static_cast<AnyTypeMixin<FixedInputsInst1> *>(CurrentInst()); 709 atm->SetAnyType(anyType); 710 return *this; 711 } v0id()712 IrConstructor &v0id() // NOLINT(readability-identifier-naming) 713 { 714 CurrentInst()->SetType(DataType::VOID); 715 return *this; 716 } type(DataType::Type type)717 IrConstructor &type(DataType::Type type) // NOLINT(readability-identifier-naming) 718 { 719 CurrentInst()->SetType(type); 720 return *this; 721 } 722 Terminator()723 IrConstructor &Terminator() 724 { 725 CurrentInst()->SetFlag(inst_flags::TERMINATOR); 726 return *this; 727 } 728 AddImm(uint32_t imm)729 IrConstructor &AddImm(uint32_t imm) 730 { 731 CurrentInst()->CastToIntrinsic()->AddImm(graph_->GetAllocator(), imm); 732 return *this; 733 } 734 DstReg(uint8_t reg)735 IrConstructor &DstReg(uint8_t reg) 736 { 737 CurrentInst()->SetDstReg(reg); 738 if (DataType::IsFloatType(CurrentInst()->GetType())) { 739 graph_->SetUsedReg<DataType::FLOAT64>(reg); 740 } 741 return *this; 742 } 743 SrcReg(uint8_t id,uint8_t reg)744 IrConstructor &SrcReg(uint8_t id, uint8_t reg) 745 { 746 CurrentInst()->SetSrcReg(id, reg); 747 if (DataType::IsFloatType(CurrentInst()->GetType())) { 748 graph_->SetUsedReg<DataType::FLOAT64>(reg); 749 } 750 graph_->SetUsedReg<DataType::INT64>(reg); 751 return *this; 752 } 753 754 // CC-OFFNXT(huge_method, huge_cyclomatic_complexity, G.FUN.01) big switch-case TypeId(uint32_t typeId)755 IrConstructor &TypeId(uint32_t typeId) 756 { 757 auto inst = CurrentInst(); 758 switch (inst->GetOpcode()) { 759 case Opcode::Call: 760 inst->CastToCall()->SetCallMethodId(typeId); 761 break; 762 case Opcode::LoadString: 763 inst->CastToLoadString()->SetTypeId(typeId); 764 break; 765 case Opcode::LoadType: 766 inst->CastToLoadType()->SetTypeId(typeId); 767 break; 768 case Opcode::UnresolvedLoadType: 769 inst->CastToUnresolvedLoadType()->SetTypeId(typeId); 770 break; 771 case Opcode::LoadFromConstantPool: 772 inst->CastToLoadFromConstantPool()->SetTypeId(typeId); 773 break; 774 case Opcode::StoreStatic: 775 inst->CastToStoreStatic()->SetTypeId(typeId); 776 break; 777 case Opcode::UnresolvedStoreStatic: 778 inst->CastToUnresolvedStoreStatic()->SetTypeId(typeId); 779 break; 780 case Opcode::ResolveObjectField: 781 inst->CastToResolveObjectField()->SetTypeId(typeId); 782 break; 783 case Opcode::ResolveObjectFieldStatic: 784 inst->CastToResolveObjectFieldStatic()->SetTypeId(typeId); 785 break; 786 case Opcode::StoreResolvedObjectField: 787 inst->CastToStoreResolvedObjectField()->SetTypeId(typeId); 788 break; 789 case Opcode::StoreResolvedObjectFieldStatic: 790 inst->CastToStoreResolvedObjectFieldStatic()->SetTypeId(typeId); 791 break; 792 case Opcode::LoadStatic: 793 inst->CastToLoadStatic()->SetTypeId(typeId); 794 break; 795 case Opcode::LoadObject: 796 inst->CastToLoadObject()->SetTypeId(typeId); 797 break; 798 case Opcode::StoreObject: 799 inst->CastToStoreObject()->SetTypeId(typeId); 800 break; 801 case Opcode::NewObject: 802 inst->CastToNewObject()->SetTypeId(typeId); 803 break; 804 case Opcode::InitObject: 805 inst->CastToInitObject()->SetCallMethodId(typeId); 806 break; 807 case Opcode::NewArray: 808 inst->CastToNewArray()->SetTypeId(typeId); 809 break; 810 case Opcode::CheckCast: 811 inst->CastToCheckCast()->SetTypeId(typeId); 812 break; 813 case Opcode::IsInstance: 814 inst->CastToIsInstance()->SetTypeId(typeId); 815 break; 816 case Opcode::InitClass: 817 inst->CastToInitClass()->SetTypeId(typeId); 818 break; 819 case Opcode::LoadClass: 820 inst->CastToLoadClass()->SetTypeId(typeId); 821 break; 822 case Opcode::LoadAndInitClass: 823 inst->CastToLoadAndInitClass()->SetTypeId(typeId); 824 break; 825 case Opcode::UnresolvedLoadAndInitClass: 826 inst->CastToUnresolvedLoadAndInitClass()->SetTypeId(typeId); 827 break; 828 default: 829 UNREACHABLE(); 830 } 831 return *this; 832 } 833 ObjField(RuntimeInterface::FieldPtr field)834 IrConstructor &ObjField(RuntimeInterface::FieldPtr field) 835 { 836 auto inst = CurrentInst(); 837 switch (inst->GetOpcode()) { 838 case Opcode::StoreStatic: 839 inst->CastToStoreStatic()->SetObjField(field); 840 break; 841 case Opcode::LoadStatic: 842 inst->CastToLoadStatic()->SetObjField(field); 843 break; 844 case Opcode::LoadObject: 845 inst->CastToLoadObject()->SetObjField(field); 846 break; 847 case Opcode::StoreObject: 848 inst->CastToStoreObject()->SetObjField(field); 849 break; 850 default: 851 UNREACHABLE(); 852 } 853 return *this; 854 } 855 ObjectType(ObjectType objType)856 IrConstructor &ObjectType(ObjectType objType) 857 { 858 auto inst = CurrentInst(); 859 switch (inst->GetOpcode()) { 860 case Opcode::LoadObject: 861 inst->CastToLoadObject()->SetObjectType(objType); 862 break; 863 case Opcode::StoreObject: 864 inst->CastToStoreObject()->SetObjectType(objType); 865 break; 866 default: 867 UNREACHABLE(); 868 } 869 return *this; 870 } 871 ObjectTypeLoadImm(LoadImmediateInst::ObjectType objType)872 IrConstructor &ObjectTypeLoadImm(LoadImmediateInst::ObjectType objType) 873 { 874 auto inst = CurrentInst(); 875 ASSERT(inst->GetOpcode() == Opcode::LoadImmediate); 876 inst->CastToLoadImmediate()->SetObjectType(objType); 877 return *this; 878 } 879 SetChecks(HclassChecks checks)880 IrConstructor &SetChecks(HclassChecks checks) 881 { 882 auto inst = CurrentInst(); 883 ASSERT(inst->GetOpcode() == Opcode::HclassCheck); 884 switch (checks) { 885 case HclassChecks::ALL_CHECKS: 886 inst->CastToHclassCheck()->SetCheckFunctionIsNotClassConstructor(true); 887 inst->CastToHclassCheck()->SetCheckIsFunction(true); 888 break; 889 case HclassChecks::IS_FUNCTION: 890 inst->CastToHclassCheck()->SetCheckIsFunction(true); 891 break; 892 case HclassChecks::IS_NOT_CLASS_CONSTRUCTOR: 893 inst->CastToHclassCheck()->SetCheckFunctionIsNotClassConstructor(true); 894 break; 895 default: 896 UNREACHABLE(); 897 } 898 return *this; 899 } 900 SetNeedBarrier(bool needBarrier)901 IrConstructor &SetNeedBarrier(bool needBarrier) 902 { 903 auto inst = CurrentInst(); 904 switch (inst->GetOpcode()) { 905 case Opcode::Store: 906 inst->CastToStore()->SetNeedBarrier(needBarrier); 907 break; 908 case Opcode::StoreI: 909 inst->CastToStoreI()->SetNeedBarrier(needBarrier); 910 break; 911 case Opcode::StoreObject: 912 inst->CastToStoreObject()->SetNeedBarrier(needBarrier); 913 break; 914 case Opcode::StoreArray: 915 inst->CastToStoreArray()->SetNeedBarrier(needBarrier); 916 break; 917 case Opcode::StoreArrayI: 918 inst->CastToStoreArrayI()->SetNeedBarrier(needBarrier); 919 break; 920 case Opcode::StoreArrayPair: 921 inst->CastToStoreArrayPair()->SetNeedBarrier(needBarrier); 922 break; 923 case Opcode::StoreArrayPairI: 924 inst->CastToStoreArrayPairI()->SetNeedBarrier(needBarrier); 925 break; 926 default: 927 UNREACHABLE(); 928 } 929 return *this; 930 } 931 SrcVregs(std::vector<int> && vregs)932 IrConstructor &SrcVregs(std::vector<int> &&vregs) 933 { 934 ASSERT(CurrentInst()->IsSaveState()); 935 if (!vregs.empty()) { 936 graph_->SetVRegsCount( 937 std::max<size_t>(graph_->GetVRegsCount(), *std::max_element(vregs.begin(), vregs.end()))); 938 } 939 if (saveStateInstVregsMap_.count(CurrentInstIndex()) == 0) { 940 saveStateInstVregsMap_.emplace(CurrentInstIndex(), std::move(vregs)); 941 } 942 return *this; 943 } 944 NoVregs()945 IrConstructor &NoVregs() 946 { 947 ASSERT(CurrentInst()->IsSaveState()); 948 return *this; 949 } 950 951 // Useful for parametrized tests CleanupInputs()952 IrConstructor &CleanupInputs() 953 { 954 ASSERT(CurrentInst()->IsSaveState()); 955 auto &inputs = instInputsMap_[CurrentInstIndex()]; 956 auto &vregs = saveStateInstVregsMap_[CurrentInstIndex()]; 957 for (auto i = static_cast<int>(inputs.size()) - 1; i >= 0; i--) { 958 if (instMap_.count(inputs[i]) == 0) { 959 inputs.erase(inputs.begin() + i); 960 vregs.erase(vregs.begin() + i); 961 } 962 } 963 return *this; 964 } 965 CatchTypeIds(std::vector<uint16_t> && ids)966 IrConstructor &CatchTypeIds(std::vector<uint16_t> &&ids) 967 { 968 auto inst = CurrentInst(); 969 ASSERT(inst->GetOpcode() == Opcode::Try); 970 auto tryInst = inst->CastToTry(); 971 for (auto id : ids) { 972 tryInst->AppendCatchTypeId(id, 0); 973 } 974 return *this; 975 } 976 ThrowableInsts(std::vector<int> && ids)977 IrConstructor &ThrowableInsts(std::vector<int> &&ids) 978 { 979 auto inst = CurrentInst(); 980 ASSERT(inst->GetOpcode() == Opcode::CatchPhi); 981 auto catchPhi = inst->CastToCatchPhi(); 982 for (auto id : ids) { 983 ASSERT(instMap_.count(id) > 0); 984 catchPhi->AppendThrowableInst(instMap_.at(id)); 985 } 986 return *this; 987 } 988 DeoptimizeType(DeoptimizeType type)989 IrConstructor &DeoptimizeType(DeoptimizeType type) 990 { 991 auto inst = CurrentInst(); 992 if (inst->GetOpcode() == Opcode::Deoptimize) { 993 inst->CastToDeoptimize()->SetDeoptimizeType(type); 994 } else { 995 ASSERT(inst->GetOpcode() == Opcode::DeoptimizeIf); 996 inst->CastToDeoptimizeIf()->SetDeoptimizeType(type); 997 } 998 return *this; 999 } 1000 SrcType(DataType::Type type)1001 IrConstructor &SrcType(DataType::Type type) 1002 { 1003 auto inst = CurrentInst(); 1004 switch (inst->GetOpcode()) { 1005 case Opcode::Compare: 1006 inst->CastToCompare()->SetOperandsType(type); 1007 break; 1008 case Opcode::Cmp: 1009 inst->CastToCmp()->SetOperandsType(type); 1010 break; 1011 case Opcode::If: 1012 inst->CastToIf()->SetOperandsType(type); 1013 break; 1014 case Opcode::AddOverflow: 1015 inst->CastToAddOverflow()->SetOperandsType(type); 1016 break; 1017 case Opcode::SubOverflow: 1018 inst->CastToSubOverflow()->SetOperandsType(type); 1019 break; 1020 case Opcode::Select: 1021 inst->CastToSelect()->SetOperandsType(type); 1022 break; 1023 case Opcode::SelectImm: 1024 inst->CastToSelectImm()->SetOperandsType(type); 1025 break; 1026 case Opcode::IfImm: 1027 inst->CastToIfImm()->SetOperandsType(type); 1028 break; 1029 case Opcode::Cast: 1030 inst->CastToCast()->SetOperandsType(type); 1031 break; 1032 case Opcode::Bitcast: 1033 inst->CastToBitcast()->SetOperandsType(type); 1034 break; 1035 default: 1036 UNREACHABLE(); 1037 } 1038 return *this; 1039 } 1040 IntrinsicId(RuntimeInterface::IntrinsicId id)1041 IrConstructor &IntrinsicId(RuntimeInterface::IntrinsicId id) 1042 { 1043 auto inst = CurrentInst(); 1044 ASSERT(inst->IsIntrinsic()); 1045 inst->CastToIntrinsic()->SetIntrinsicId(id); 1046 return *this; 1047 } 1048 Relocate()1049 IrConstructor &Relocate() 1050 { 1051 auto inst = CurrentInst(); 1052 ASSERT(inst->IsIntrinsic()); 1053 inst->CastToIntrinsic()->SetRelocate(); 1054 return *this; 1055 } 1056 Class(RuntimeInterface::ClassPtr klass)1057 IrConstructor &Class(RuntimeInterface::ClassPtr klass) 1058 { 1059 auto inst = CurrentInst(); 1060 switch (inst->GetOpcode()) { 1061 case Opcode::InitClass: 1062 inst->CastToInitClass()->SetClass(klass); 1063 break; 1064 case Opcode::LoadClass: 1065 inst->CastToLoadClass()->SetClass(klass); 1066 break; 1067 case Opcode::LoadAndInitClass: 1068 inst->CastToLoadAndInitClass()->SetClass(klass); 1069 break; 1070 case Opcode::UnresolvedLoadAndInitClass: 1071 inst->CastToUnresolvedLoadAndInitClass()->SetClass(klass); 1072 break; 1073 case Opcode::LoadImmediate: 1074 inst->CastToLoadImmediate()->SetObjectType(LoadImmediateInst::ObjectType::CLASS); 1075 inst->CastToLoadImmediate()->SetClass(klass); 1076 break; 1077 default: 1078 UNREACHABLE(); 1079 } 1080 return *this; 1081 } 1082 IntegerWasSeen(bool value)1083 IrConstructor &IntegerWasSeen(bool value) 1084 { 1085 auto inst = CurrentInst(); 1086 switch (inst->GetOpcode()) { 1087 case Opcode::CastAnyTypeValue: 1088 inst->CastToCastAnyTypeValue()->SetIsIntegerWasSeen(value); 1089 break; 1090 case Opcode::AnyTypeCheck: 1091 inst->CastToAnyTypeCheck()->SetIsIntegerWasSeen(value); 1092 break; 1093 default: 1094 UNREACHABLE(); 1095 } 1096 return *this; 1097 } 1098 AllowedInputType(profiling::AnyInputType type)1099 IrConstructor &AllowedInputType(profiling::AnyInputType type) 1100 { 1101 auto inst = CurrentInst(); 1102 switch (inst->GetOpcode()) { 1103 case Opcode::CastAnyTypeValue: 1104 inst->CastToCastAnyTypeValue()->SetAllowedInputType(type); 1105 break; 1106 case Opcode::AnyTypeCheck: 1107 inst->CastToAnyTypeCheck()->SetAllowedInputType(type); 1108 break; 1109 default: 1110 UNREACHABLE(); 1111 } 1112 return *this; 1113 } 1114 Loc(uint32_t dirNumber,uint32_t fileNumber,uint32_t lineNumber)1115 IrConstructor &Loc([[maybe_unused]] uint32_t dirNumber, [[maybe_unused]] uint32_t fileNumber, 1116 [[maybe_unused]] uint32_t lineNumber) 1117 { 1118 #ifdef PANDA_COMPILER_DEBUG_INFO 1119 auto debugInfo = graph_->GetAllocator()->New<InstDebugInfo>(dirNumber, fileNumber, lineNumber); 1120 CurrentInst()->SetDebugInfo(debugInfo); 1121 #endif 1122 return *this; 1123 } 1124 SetAccessType(DynObjectAccessType type)1125 IrConstructor &SetAccessType(DynObjectAccessType type) 1126 { 1127 auto *inst = CurrentInst(); 1128 switch (inst->GetOpcode()) { 1129 case Opcode::LoadObjectDynamic: 1130 inst->CastToLoadObjectDynamic()->SetAccessType(type); 1131 break; 1132 case Opcode::StoreObjectDynamic: 1133 inst->CastToStoreObjectDynamic()->SetAccessType(type); 1134 break; 1135 default: 1136 UNREACHABLE(); 1137 } 1138 return *this; 1139 } 1140 SetPtr(uintptr_t ptr)1141 IrConstructor &SetPtr(uintptr_t ptr) 1142 { 1143 auto *inst = CurrentInst(); 1144 switch (inst->GetOpcode()) { 1145 case Opcode::LoadObjFromConst: 1146 inst->CastToLoadObjFromConst()->SetObjPtr(ptr); 1147 break; 1148 case Opcode::FunctionImmediate: 1149 inst->CastToFunctionImmediate()->SetFunctionPtr(ptr); 1150 break; 1151 default: 1152 UNREACHABLE(); 1153 } 1154 return *this; 1155 } 1156 SetAccessMode(DynObjectAccessMode mode)1157 IrConstructor &SetAccessMode(DynObjectAccessMode mode) 1158 { 1159 auto *inst = CurrentInst(); 1160 switch (inst->GetOpcode()) { 1161 case Opcode::LoadObjectDynamic: 1162 inst->CastToLoadObjectDynamic()->SetAccessMode(mode); 1163 break; 1164 case Opcode::StoreObjectDynamic: 1165 inst->CastToStoreObjectDynamic()->SetAccessMode(mode); 1166 break; 1167 default: 1168 UNREACHABLE(); 1169 } 1170 return *this; 1171 } 1172 1173 template <typename T> ScopedLife()1174 std::shared_ptr<IrConstructor> ScopedLife() 1175 { 1176 #ifndef __clang_analyzer__ 1177 if constexpr (std::is_same_v<T, BasicBlock>) { 1178 return std::shared_ptr<IrConstructor>(this, [](IrConstructor *b) { b->ResetCurrentBb(); }); 1179 } else if constexpr (std::is_same_v<T, Inst>) { 1180 return std::shared_ptr<IrConstructor>(this, [](IrConstructor *b) { b->ResetCurrentInst(); }); 1181 } else if constexpr (std::is_same_v<T, Graph>) { 1182 return std::shared_ptr<IrConstructor>(this, [](IrConstructor *b) { b->Finalize(); }); 1183 } 1184 #else 1185 return nullptr; 1186 #endif 1187 } 1188 CheckInputType(Inst * inst,Inst * inputInst,size_t inputIdx)1189 void CheckInputType(Inst *inst, Inst *inputInst, size_t inputIdx) 1190 { 1191 auto type = inputInst->GetType(); 1192 auto prevType = inst->GetInputType(inputIdx); 1193 if (prevType == DataType::Type::NO_TYPE) { 1194 switch (inst->GetOpcode()) { 1195 case Opcode::Cmp: 1196 inst->CastToCmp()->SetOperandsType(type); 1197 break; 1198 case Opcode::Compare: 1199 inst->CastToCompare()->SetOperandsType(type); 1200 break; 1201 case Opcode::If: 1202 inst->CastToIf()->SetOperandsType(type); 1203 break; 1204 case Opcode::IfImm: 1205 inst->CastToIfImm()->SetOperandsType(type); 1206 break; 1207 case Opcode::Select: 1208 inst->CastToSelect()->SetOperandsType(type); 1209 break; 1210 case Opcode::SelectImm: 1211 inst->CastToSelectImm()->SetOperandsType(type); 1212 break; 1213 default: 1214 UNREACHABLE(); 1215 } 1216 } else { 1217 // Disable check for Negation (Neg+Add), we can't add deeper verification, 1218 // because the graph has not been built yet 1219 if (inputInst->GetOpcode() == Opcode::Add) { 1220 return; 1221 } 1222 CHECK_EQ(type, prevType); 1223 } 1224 } 1225 ConstructControlFlow()1226 void ConstructControlFlow() 1227 { 1228 for (auto [bbi, succs] : bbSuccsMap_) { 1229 auto bb = bbMap_.at(bbi); 1230 for (auto succ : succs) { 1231 bb->AddSucc(bbMap_.at(succ == -1 ? 1 : succ)); 1232 } 1233 if (succs.size() > 1 && bb->IsEmpty()) { 1234 bb->SetTryEnd(true); 1235 } 1236 } 1237 auto endBlock = graph_->GetEndBlock(); 1238 if (endBlock->GetPredsBlocks().empty()) { 1239 graph_->EraseBlock(endBlock); 1240 graph_->SetEndBlock(nullptr); 1241 } 1242 } 1243 ConstructInput(Inst * inst,size_t inputIdx,const std::vector<int> & vregs,size_t idx)1244 void ConstructInput(Inst *inst, size_t inputIdx, const std::vector<int> &vregs, size_t idx) 1245 { 1246 ASSERT_DO(instMap_.find(inputIdx) != instMap_.end(), 1247 std::cerr << "Input with Id " << inputIdx << " isn't found, inst: " << *inst << std::endl); 1248 auto inputInst = instMap_.at(inputIdx); 1249 auto op = inst->GetOpcode(); 1250 if (!inputInst->IsConst() && (op == Opcode::Cmp || op == Opcode::Compare || op == Opcode::If || 1251 op == Opcode::IfImm || op == Opcode::Select || op == Opcode::SelectImm)) { 1252 CheckInputType(inst, inputInst, idx); 1253 } 1254 if (inst->IsOperandsDynamic()) { 1255 inst->AppendInput(inputInst); 1256 if (inst->IsSaveState()) { 1257 static_cast<SaveStateInst *>(inst)->SetVirtualRegister(idx, 1258 VirtualRegister(vregs[idx], VRegType::VREG)); 1259 } 1260 } else { 1261 inst->SetInput(idx, inputInst); 1262 } 1263 } 1264 ConstructDataFlow()1265 void ConstructDataFlow() 1266 { 1267 for (auto [insti, inputs] : instInputsMap_) { 1268 auto inst = instMap_.at(insti); 1269 const auto &vregs {inst->IsSaveState() ? saveStateInstVregsMap_[insti] : std::vector<int> {}}; 1270 ASSERT(!inst->IsSaveState() || inputs.size() == vregs.size()); 1271 size_t idx = 0; 1272 if (inst->IsOperandsDynamic()) { 1273 inst->ReserveInputs(inputs.size()); 1274 } 1275 for (auto inputIdx : inputs) { 1276 ConstructInput(inst, inputIdx, vregs, idx); 1277 ++idx; 1278 } 1279 } 1280 1281 for (auto [insti, inputs] : phiInstInputsMap_) { 1282 auto inst = instMap_.at(insti); 1283 for (auto input : inputs) { 1284 auto inputInst = instMap_.at(input.second); 1285 size_t idx = inst->GetBasicBlock()->GetPredBlockIndex(bbMap_.at(input.first)); 1286 auto i {inst->AppendInput(inputInst)}; 1287 inst->CastToPhi()->SetPhiInputBbNum(i, idx); 1288 } 1289 } 1290 } 1291 UpdateSpecialFlagsForReference(Inst * inst)1292 void UpdateSpecialFlagsForReference(Inst *inst) 1293 { 1294 if (inst->GetType() == DataType::REFERENCE) { 1295 if (inst->GetOpcode() == Opcode::StoreArray) { 1296 inst->CastToStoreArray()->SetNeedBarrier(true); 1297 } 1298 if (inst->GetOpcode() == Opcode::StoreArrayI) { 1299 inst->CastToStoreArrayI()->SetNeedBarrier(true); 1300 } 1301 if (inst->GetOpcode() == Opcode::StoreStatic) { 1302 inst->CastToStoreStatic()->SetNeedBarrier(true); 1303 } 1304 if (inst->GetOpcode() == Opcode::UnresolvedStoreStatic) { 1305 inst->CastToUnresolvedStoreStatic()->SetNeedBarrier(true); 1306 } 1307 if (inst->GetOpcode() == Opcode::StoreObject) { 1308 inst->CastToStoreObject()->SetNeedBarrier(true); 1309 } 1310 if (inst->GetOpcode() == Opcode::StoreResolvedObjectField) { 1311 inst->CastToStoreResolvedObjectField()->SetNeedBarrier(true); 1312 } 1313 if (inst->GetOpcode() == Opcode::StoreResolvedObjectFieldStatic) { 1314 inst->CastToStoreResolvedObjectFieldStatic()->SetNeedBarrier(true); 1315 } 1316 if (inst->GetOpcode() == Opcode::StoreArrayPair) { 1317 inst->CastToStoreArrayPair()->SetNeedBarrier(true); 1318 } 1319 if (inst->GetOpcode() == Opcode::StoreArrayPairI) { 1320 inst->CastToStoreArrayPairI()->SetNeedBarrier(true); 1321 } 1322 } 1323 } 1324 UpdateSpecialFlags()1325 void UpdateSpecialFlags() 1326 { 1327 size_t maxId = graph_->GetCurrentInstructionId(); 1328 for (auto pair : instMap_) { 1329 auto id = static_cast<size_t>(pair.first); 1330 auto inst = pair.second; 1331 UpdateSpecialFlagsForReference(inst); 1332 if (inst->GetOpcode() == Opcode::Try) { 1333 auto bb = inst->GetBasicBlock(); 1334 bb->SetTryBegin(true); 1335 bb->GetSuccessor(0)->SetTry(true); 1336 for (size_t idx = 1; idx < bb->GetSuccsBlocks().size(); idx++) { 1337 bb->GetSuccessor(idx)->SetCatchBegin(true); 1338 } 1339 } 1340 if (inst->GetOpcode() == Opcode::SaveStateOsr) { 1341 inst->GetBasicBlock()->SetOsrEntry(true); 1342 } 1343 if (id >= maxId) { 1344 maxId = id + 1; 1345 } 1346 } 1347 graph_->SetCurrentInstructionId(maxId); 1348 } 1349 1350 // Create SaveState instructions thet weren't explicitly constructed in the test CreateSaveStates()1351 void CreateSaveStates() 1352 { 1353 for (auto [insti, inputs] : instInputsMap_) { 1354 auto inst = instMap_.at(insti); 1355 if (!inst->IsOperandsDynamic() && inst->RequireState() && inst->GetInputsCount() > inputs.size()) { 1356 auto saveState = graph_->CreateInstSaveState(); 1357 saveState->SetId(static_cast<int>(graph_->GetCurrentInstructionId()) + 1); 1358 graph_->SetCurrentInstructionId(saveState->GetId() + 1); 1359 inst->GetBasicBlock()->InsertBefore(saveState, inst); 1360 inst->SetSaveState(saveState); 1361 } 1362 } 1363 } 1364 SetSpillFillData()1365 void SetSpillFillData() 1366 { 1367 graph_->ResetParameterInfo(); 1368 // Count number of parameters (needed for bytecode optimizer) in first cycle and set SpillFillData for each 1369 // parameter in second cycle 1370 uint32_t numArgs = 0; 1371 for (auto inst : graph_->GetStartBlock()->Insts()) { 1372 if (inst->GetOpcode() == Opcode::Parameter) { 1373 ++numArgs; 1374 } 1375 } 1376 uint32_t i = 0; 1377 for (auto inst : graph_->GetStartBlock()->Insts()) { 1378 if (inst->GetOpcode() != Opcode::Parameter) { 1379 continue; 1380 } 1381 1382 auto type = inst->GetType(); 1383 InstBuilder::SetParamSpillFill(graph_, static_cast<ParameterInst *>(inst), numArgs, i++, type); 1384 } 1385 } 1386 Finalize()1387 void Finalize() 1388 { 1389 ConstructControlFlow(); 1390 ConstructDataFlow(); 1391 UpdateSpecialFlags(); 1392 CreateSaveStates(); 1393 SetSpillFillData(); 1394 ResetCurrentBb(); 1395 ResetCurrentInst(); 1396 graph_->RunPass<LoopAnalyzer>(); 1397 PropagateRegisters(); 1398 if (enableGraphChecker_) { 1399 GraphChecker(graph_).Check(); 1400 } 1401 } 1402 GetInst(unsigned index)1403 Inst &GetInst(unsigned index) 1404 { 1405 ASSERT_DO(instMap_.find(index) != instMap_.end(), std::cerr << "Inst with Id " << index << " isn't found\n"); 1406 return *instMap_.at(index); 1407 } 1408 GetBlock(unsigned index)1409 BasicBlock &GetBlock(unsigned index) 1410 { 1411 return *bbMap_.at(index); 1412 } 1413 EnableGraphChecker(bool value)1414 void EnableGraphChecker(bool value) 1415 { 1416 enableGraphChecker_ = value; 1417 } 1418 1419 private: 1420 template <typename T> AddInput(T v)1421 void AddInput(T v) 1422 { 1423 static_assert(std::is_integral_v<T>); 1424 instInputsMap_[CurrentInstIndex()].push_back(v); 1425 } 1426 1427 template <typename T, typename... Args> AddInput(T v,Args...args)1428 void AddInput(T v, Args... args) 1429 { 1430 instInputsMap_[CurrentInstIndex()].push_back(v); 1431 AddInput(args...); 1432 } 1433 GetBbByIndex(int index)1434 BasicBlock *GetBbByIndex(int index) 1435 { 1436 return bbMap_.at(index); 1437 } 1438 CurrentBb()1439 BasicBlock *CurrentBb() 1440 { 1441 return currentBb_.second; 1442 } 1443 CurrentBbIndex()1444 int CurrentBbIndex() 1445 { 1446 return currentBb_.first; 1447 } 1448 CurrentInst()1449 Inst *CurrentInst() 1450 { 1451 return currentInst_.second; 1452 } 1453 CurrentInstIndex()1454 int CurrentInstIndex() 1455 { 1456 return currentInst_.first; 1457 } 1458 ResetCurrentBb()1459 void ResetCurrentBb() 1460 { 1461 currentBb_ = {-1, nullptr}; 1462 } 1463 ResetCurrentInst()1464 void ResetCurrentInst() 1465 { 1466 currentInst_ = {-1, nullptr}; 1467 } 1468 PropagateRegisters()1469 void PropagateRegisters() 1470 { 1471 for (auto bb : graph_->GetBlocksRPO()) { 1472 for (auto inst : bb->AllInsts()) { 1473 if (inst->GetDstReg() == GetInvalidReg() || inst->IsOperandsDynamic()) { 1474 continue; 1475 } 1476 for (size_t i = 0; i < inst->GetInputsCount(); i++) { 1477 inst->SetSrcReg(i, inst->GetInputs()[i].GetInst()->GetDstReg()); 1478 } 1479 } 1480 } 1481 } 1482 1483 private: 1484 Graph *graph_ {nullptr}; 1485 ArenaAllocator aa_; 1486 std::pair<int, BasicBlock *> currentBb_; 1487 std::pair<int, Inst *> currentInst_; 1488 ArenaUnorderedMap<int, BasicBlock *> bbMap_ {aa_.Adapter()}; 1489 ArenaVector<std::pair<int, std::vector<int>>> bbSuccsMap_ {aa_.Adapter()}; 1490 ArenaUnorderedMap<int, Inst *> instMap_ {aa_.Adapter()}; 1491 ArenaUnorderedMap<int, std::vector<int>> instInputsMap_ {aa_.Adapter()}; 1492 ArenaUnorderedMap<int, std::vector<int>> saveStateInstVregsMap_ {aa_.Adapter()}; 1493 ArenaUnorderedMap<int, std::vector<std::pair<int, int>>> phiInstInputsMap_ {aa_.Adapter()}; 1494 bool enableGraphChecker_ {true}; 1495 }; 1496 1497 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1498 #define GRAPH(GRAPH) if (auto __g = builder_->SetGraph(GRAPH).ScopedLife<Graph>(); true) 1499 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1500 #define BASIC_BLOCK(ID, ...) \ 1501 if (auto __b = builder_->NewBlock<ID>().Succs({__VA_ARGS__}).template ScopedLife<BasicBlock>(); true) 1502 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1503 #define INST(ID, ...) builder_->NewInst(ID, __VA_ARGS__) 1504 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1505 #define CONSTANT(ID, VALUE) builder_->NewConstant(ID, VALUE) 1506 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1507 #define PARAMETER(ID, ARG_NUM) builder_->NewParameter(ID, ARG_NUM) 1508 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1509 #define NULLPTR(ID) builder_->AddNullptrInst(ID) 1510 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1511 #define INS(INDEX) builder_->GetInst(INDEX) 1512 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1513 #define BB(INDEX) builder_->GetBlock(INDEX) 1514 } // namespace ark::compiler 1515 1516 #endif // PANDA_IR_CONSTRUCTOR_H 1517