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, INVALID_REG); 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 Imm(uint64_t imm)486 IrConstructor &Imm(uint64_t imm) 487 { 488 auto inst = CurrentInst(); 489 switch (inst->GetOpcode()) { 490 case Opcode::AddI: 491 case Opcode::SubI: 492 case Opcode::MulI: 493 case Opcode::DivI: 494 case Opcode::ModI: 495 case Opcode::ShlI: 496 case Opcode::ShrI: 497 case Opcode::AShrI: 498 case Opcode::AndI: 499 case Opcode::OrI: 500 case Opcode::XorI: 501 static_cast<BinaryImmOperation *>(inst)->SetImm(imm); 502 break; 503 case Opcode::BoundsCheckI: 504 inst->CastToBoundsCheckI()->SetImm(imm); 505 break; 506 case Opcode::LoadArrayI: 507 inst->CastToLoadArrayI()->SetImm(imm); 508 break; 509 case Opcode::StoreArrayI: 510 inst->CastToStoreArrayI()->SetImm(imm); 511 break; 512 case Opcode::LoadI: 513 inst->CastToLoadI()->SetImm(imm); 514 break; 515 case Opcode::StoreI: 516 inst->CastToStoreI()->SetImm(imm); 517 break; 518 case Opcode::ReturnI: 519 inst->CastToReturnI()->SetImm(imm); 520 break; 521 case Opcode::IfImm: 522 inst->CastToIfImm()->SetImm(imm); 523 break; 524 case Opcode::SelectImm: 525 inst->CastToSelectImm()->SetImm(imm); 526 break; 527 case Opcode::LoadArrayPairI: 528 inst->CastToLoadArrayPairI()->SetImm(imm); 529 break; 530 case Opcode::StoreArrayPairI: 531 inst->CastToStoreArrayPairI()->SetImm(imm); 532 break; 533 case Opcode::LoadPairPart: 534 inst->CastToLoadPairPart()->SetImm(imm); 535 break; 536 case Opcode::DeoptimizeCompareImm: 537 inst->CastToDeoptimizeCompareImm()->SetImm(imm); 538 break; 539 case Opcode::LoadArrayPair: 540 inst->CastToLoadArrayPair()->SetImm(imm); 541 break; 542 case Opcode::StoreArrayPair: 543 inst->CastToStoreArrayPair()->SetImm(imm); 544 break; 545 default: 546 UNREACHABLE(); 547 } 548 return *this; 549 } 550 Shift(ShiftType shiftType,uint64_t imm)551 IrConstructor &Shift(ShiftType shiftType, uint64_t imm) 552 { 553 auto inst = CurrentInst(); 554 switch (inst->GetOpcode()) { 555 case Opcode::AndSR: 556 case Opcode::OrSR: 557 case Opcode::XorSR: 558 case Opcode::AndNotSR: 559 case Opcode::OrNotSR: 560 case Opcode::XorNotSR: 561 case Opcode::AddSR: 562 case Opcode::SubSR: 563 static_cast<BinaryShiftedRegisterOperation *>(inst)->SetShiftType(shiftType); 564 static_cast<BinaryShiftedRegisterOperation *>(inst)->SetImm(imm); 565 break; 566 case Opcode::NegSR: 567 static_cast<UnaryShiftedRegisterOperation *>(inst)->SetShiftType(shiftType); 568 static_cast<UnaryShiftedRegisterOperation *>(inst)->SetImm(imm); 569 break; 570 default: 571 UNREACHABLE(); 572 } 573 return *this; 574 }; 575 Exit()576 IrConstructor &Exit() 577 { 578 CurrentInst()->CastToMonitor()->SetExit(); 579 return *this; 580 } 581 Entry()582 IrConstructor &Entry() 583 { 584 CurrentInst()->CastToMonitor()->SetEntry(); 585 return *this; 586 } 587 588 IrConstructor &Fcmpg(bool fcmpg = true) 589 { 590 CurrentInst()->CastToCmp()->SetFcmpg(fcmpg); 591 return *this; 592 } 593 594 IrConstructor &Fcmpl(bool fcmpl = true) 595 { 596 CurrentInst()->CastToCmp()->SetFcmpl(fcmpl); 597 return *this; 598 } 599 u8()600 IrConstructor &u8() // NOLINT(readability-identifier-naming) 601 { 602 CurrentInst()->SetType(DataType::UINT8); 603 return *this; 604 } u16()605 IrConstructor &u16() // NOLINT(readability-identifier-naming) 606 { 607 CurrentInst()->SetType(DataType::UINT16); 608 return *this; 609 } u32()610 IrConstructor &u32() // NOLINT(readability-identifier-naming) 611 { 612 CurrentInst()->SetType(DataType::UINT32); 613 return *this; 614 } u64()615 IrConstructor &u64() // NOLINT(readability-identifier-naming) 616 { 617 CurrentInst()->SetType(DataType::UINT64); 618 return *this; 619 } s8()620 IrConstructor &s8() // NOLINT(readability-identifier-naming) 621 { 622 CurrentInst()->SetType(DataType::INT8); 623 return *this; 624 } s16()625 IrConstructor &s16() // NOLINT(readability-identifier-naming) 626 { 627 CurrentInst()->SetType(DataType::INT16); 628 return *this; 629 } s32()630 IrConstructor &s32() // NOLINT(readability-identifier-naming) 631 { 632 CurrentInst()->SetType(DataType::INT32); 633 return *this; 634 } s64()635 IrConstructor &s64() // NOLINT(readability-identifier-naming) 636 { 637 CurrentInst()->SetType(DataType::INT64); 638 return *this; 639 } i8()640 IrConstructor &i8() // NOLINT(readability-identifier-naming) 641 { 642 return s8(); 643 } i16()644 IrConstructor &i16() // NOLINT(readability-identifier-naming) 645 { 646 return s16(); 647 } i32()648 IrConstructor &i32() // NOLINT(readability-identifier-naming) 649 { 650 return s32(); 651 } i64()652 IrConstructor &i64() // NOLINT(readability-identifier-naming) 653 { 654 return s64(); 655 } b()656 IrConstructor &b() // NOLINT(readability-identifier-naming) 657 { 658 CurrentInst()->SetType(DataType::BOOL); 659 return *this; 660 } ref()661 IrConstructor &ref() // NOLINT(readability-identifier-naming) 662 { 663 CurrentInst()->SetType(DataType::REFERENCE); 664 return *this; 665 } ptr()666 IrConstructor &ptr() // NOLINT(readability-identifier-naming) 667 { 668 CurrentInst()->SetType(DataType::POINTER); 669 return *this; 670 } w()671 IrConstructor &w() // NOLINT(readability-identifier-naming) 672 { 673 return ptr(); 674 } 675 // Type representing MarkWord mw()676 IrConstructor &mw() // NOLINT(readability-identifier-naming) 677 { 678 return type(MARK_WORD_TYPE); 679 } 680 // Type representing uint type for ref ref_uint()681 IrConstructor &ref_uint() // NOLINT(readability-identifier-naming) 682 { 683 return type(DataType::GetIntTypeForReference(graph_->GetArch())); 684 } f32()685 IrConstructor &f32() // NOLINT(readability-identifier-naming) 686 { 687 CurrentInst()->SetType(DataType::FLOAT32); 688 return *this; 689 } f64()690 IrConstructor &f64() // NOLINT(readability-identifier-naming) 691 { 692 CurrentInst()->SetType(DataType::FLOAT64); 693 return *this; 694 } any()695 IrConstructor &any() // NOLINT(readability-identifier-naming) 696 { 697 CurrentInst()->SetType(DataType::ANY); 698 return *this; 699 } SetType(DataType::Type type)700 IrConstructor &SetType(DataType::Type type) // NOLINT(readability-identifier-naming) 701 { 702 CurrentInst()->SetType(type); 703 return *this; 704 } AnyType(AnyBaseType anyType)705 IrConstructor &AnyType(AnyBaseType anyType) 706 { 707 auto *atm = static_cast<AnyTypeMixin<FixedInputsInst1> *>(CurrentInst()); 708 atm->SetAnyType(anyType); 709 return *this; 710 } v0id()711 IrConstructor &v0id() // NOLINT(readability-identifier-naming) 712 { 713 CurrentInst()->SetType(DataType::VOID); 714 return *this; 715 } type(DataType::Type type)716 IrConstructor &type(DataType::Type type) // NOLINT(readability-identifier-naming) 717 { 718 CurrentInst()->SetType(type); 719 return *this; 720 } 721 Terminator()722 IrConstructor &Terminator() 723 { 724 CurrentInst()->SetFlag(inst_flags::TERMINATOR); 725 return *this; 726 } 727 AddImm(uint32_t imm)728 IrConstructor &AddImm(uint32_t imm) 729 { 730 CurrentInst()->CastToIntrinsic()->AddImm(graph_->GetAllocator(), imm); 731 return *this; 732 } 733 DstReg(uint8_t reg)734 IrConstructor &DstReg(uint8_t reg) 735 { 736 CurrentInst()->SetDstReg(reg); 737 if (DataType::IsFloatType(CurrentInst()->GetType())) { 738 graph_->SetUsedReg<DataType::FLOAT64>(reg); 739 } 740 return *this; 741 } 742 SrcReg(uint8_t id,uint8_t reg)743 IrConstructor &SrcReg(uint8_t id, uint8_t reg) 744 { 745 CurrentInst()->SetSrcReg(id, reg); 746 if (DataType::IsFloatType(CurrentInst()->GetType())) { 747 graph_->SetUsedReg<DataType::FLOAT64>(reg); 748 } 749 graph_->SetUsedReg<DataType::INT64>(reg); 750 return *this; 751 } 752 TypeId(uint32_t typeId)753 IrConstructor &TypeId(uint32_t typeId) 754 { 755 auto inst = CurrentInst(); 756 switch (inst->GetOpcode()) { 757 case Opcode::Call: 758 inst->CastToCall()->SetCallMethodId(typeId); 759 break; 760 case Opcode::LoadString: 761 inst->CastToLoadString()->SetTypeId(typeId); 762 break; 763 case Opcode::LoadType: 764 inst->CastToLoadType()->SetTypeId(typeId); 765 break; 766 case Opcode::UnresolvedLoadType: 767 inst->CastToUnresolvedLoadType()->SetTypeId(typeId); 768 break; 769 case Opcode::LoadFromConstantPool: 770 inst->CastToLoadFromConstantPool()->SetTypeId(typeId); 771 break; 772 case Opcode::StoreStatic: 773 inst->CastToStoreStatic()->SetTypeId(typeId); 774 break; 775 case Opcode::UnresolvedStoreStatic: 776 inst->CastToUnresolvedStoreStatic()->SetTypeId(typeId); 777 break; 778 case Opcode::ResolveObjectField: 779 inst->CastToResolveObjectField()->SetTypeId(typeId); 780 break; 781 case Opcode::ResolveObjectFieldStatic: 782 inst->CastToResolveObjectFieldStatic()->SetTypeId(typeId); 783 break; 784 case Opcode::StoreResolvedObjectField: 785 inst->CastToStoreResolvedObjectField()->SetTypeId(typeId); 786 break; 787 case Opcode::StoreResolvedObjectFieldStatic: 788 inst->CastToStoreResolvedObjectFieldStatic()->SetTypeId(typeId); 789 break; 790 case Opcode::LoadStatic: 791 inst->CastToLoadStatic()->SetTypeId(typeId); 792 break; 793 case Opcode::LoadObject: 794 inst->CastToLoadObject()->SetTypeId(typeId); 795 break; 796 case Opcode::StoreObject: 797 inst->CastToStoreObject()->SetTypeId(typeId); 798 break; 799 case Opcode::NewObject: 800 inst->CastToNewObject()->SetTypeId(typeId); 801 break; 802 case Opcode::InitObject: 803 inst->CastToInitObject()->SetCallMethodId(typeId); 804 break; 805 case Opcode::NewArray: 806 inst->CastToNewArray()->SetTypeId(typeId); 807 break; 808 case Opcode::CheckCast: 809 inst->CastToCheckCast()->SetTypeId(typeId); 810 break; 811 case Opcode::IsInstance: 812 inst->CastToIsInstance()->SetTypeId(typeId); 813 break; 814 case Opcode::InitClass: 815 inst->CastToInitClass()->SetTypeId(typeId); 816 break; 817 case Opcode::LoadClass: 818 inst->CastToLoadClass()->SetTypeId(typeId); 819 break; 820 case Opcode::LoadAndInitClass: 821 inst->CastToLoadAndInitClass()->SetTypeId(typeId); 822 break; 823 case Opcode::UnresolvedLoadAndInitClass: 824 inst->CastToUnresolvedLoadAndInitClass()->SetTypeId(typeId); 825 break; 826 default: 827 UNREACHABLE(); 828 } 829 return *this; 830 } 831 ObjField(RuntimeInterface::FieldPtr field)832 IrConstructor &ObjField(RuntimeInterface::FieldPtr field) 833 { 834 auto inst = CurrentInst(); 835 switch (inst->GetOpcode()) { 836 case Opcode::StoreStatic: 837 inst->CastToStoreStatic()->SetObjField(field); 838 break; 839 case Opcode::LoadStatic: 840 inst->CastToLoadStatic()->SetObjField(field); 841 break; 842 case Opcode::LoadObject: 843 inst->CastToLoadObject()->SetObjField(field); 844 break; 845 case Opcode::StoreObject: 846 inst->CastToStoreObject()->SetObjField(field); 847 break; 848 default: 849 UNREACHABLE(); 850 } 851 return *this; 852 } 853 ObjectType(ObjectType objType)854 IrConstructor &ObjectType(ObjectType objType) 855 { 856 auto inst = CurrentInst(); 857 switch (inst->GetOpcode()) { 858 case Opcode::LoadObject: 859 inst->CastToLoadObject()->SetObjectType(objType); 860 break; 861 case Opcode::StoreObject: 862 inst->CastToStoreObject()->SetObjectType(objType); 863 break; 864 default: 865 UNREACHABLE(); 866 } 867 return *this; 868 } 869 ObjectTypeLoadImm(LoadImmediateInst::ObjectType objType)870 IrConstructor &ObjectTypeLoadImm(LoadImmediateInst::ObjectType objType) 871 { 872 auto inst = CurrentInst(); 873 ASSERT(inst->GetOpcode() == Opcode::LoadImmediate); 874 inst->CastToLoadImmediate()->SetObjectType(objType); 875 return *this; 876 } 877 SetChecks(HclassChecks checks)878 IrConstructor &SetChecks(HclassChecks checks) 879 { 880 auto inst = CurrentInst(); 881 ASSERT(inst->GetOpcode() == Opcode::HclassCheck); 882 switch (checks) { 883 case HclassChecks::ALL_CHECKS: 884 inst->CastToHclassCheck()->SetCheckFunctionIsNotClassConstructor(true); 885 inst->CastToHclassCheck()->SetCheckIsFunction(true); 886 break; 887 case HclassChecks::IS_FUNCTION: 888 inst->CastToHclassCheck()->SetCheckIsFunction(true); 889 break; 890 case HclassChecks::IS_NOT_CLASS_CONSTRUCTOR: 891 inst->CastToHclassCheck()->SetCheckFunctionIsNotClassConstructor(true); 892 break; 893 default: 894 UNREACHABLE(); 895 } 896 return *this; 897 } 898 SetNeedBarrier(bool needBarrier)899 IrConstructor &SetNeedBarrier(bool needBarrier) 900 { 901 auto inst = CurrentInst(); 902 switch (inst->GetOpcode()) { 903 case Opcode::Store: 904 inst->CastToStore()->SetNeedBarrier(needBarrier); 905 break; 906 case Opcode::StoreI: 907 inst->CastToStoreI()->SetNeedBarrier(needBarrier); 908 break; 909 case Opcode::StoreObject: 910 inst->CastToStoreObject()->SetNeedBarrier(needBarrier); 911 break; 912 case Opcode::StoreArray: 913 inst->CastToStoreArray()->SetNeedBarrier(needBarrier); 914 break; 915 case Opcode::StoreArrayI: 916 inst->CastToStoreArrayI()->SetNeedBarrier(needBarrier); 917 break; 918 case Opcode::StoreArrayPair: 919 inst->CastToStoreArrayPair()->SetNeedBarrier(needBarrier); 920 break; 921 case Opcode::StoreArrayPairI: 922 inst->CastToStoreArrayPairI()->SetNeedBarrier(needBarrier); 923 break; 924 default: 925 UNREACHABLE(); 926 } 927 return *this; 928 } 929 SrcVregs(std::vector<int> && vregs)930 IrConstructor &SrcVregs(std::vector<int> &&vregs) 931 { 932 ASSERT(CurrentInst()->IsSaveState()); 933 if (!vregs.empty()) { 934 graph_->SetVRegsCount( 935 std::max<size_t>(graph_->GetVRegsCount(), *std::max_element(vregs.begin(), vregs.end()))); 936 } 937 if (saveStateInstVregsMap_.count(CurrentInstIndex()) == 0) { 938 saveStateInstVregsMap_.emplace(CurrentInstIndex(), std::move(vregs)); 939 } 940 return *this; 941 } 942 NoVregs()943 IrConstructor &NoVregs() 944 { 945 ASSERT(CurrentInst()->IsSaveState()); 946 return *this; 947 } 948 949 // Useful for parametrized tests CleanupInputs()950 IrConstructor &CleanupInputs() 951 { 952 ASSERT(CurrentInst()->IsSaveState()); 953 auto &inputs = instInputsMap_[CurrentInstIndex()]; 954 auto &vregs = saveStateInstVregsMap_[CurrentInstIndex()]; 955 for (auto i = static_cast<int>(inputs.size()) - 1; i >= 0; i--) { 956 if (instMap_.count(inputs[i]) == 0) { 957 inputs.erase(inputs.begin() + i); 958 vregs.erase(vregs.begin() + i); 959 } 960 } 961 return *this; 962 } 963 CatchTypeIds(std::vector<uint16_t> && ids)964 IrConstructor &CatchTypeIds(std::vector<uint16_t> &&ids) 965 { 966 auto inst = CurrentInst(); 967 ASSERT(inst->GetOpcode() == Opcode::Try); 968 auto tryInst = inst->CastToTry(); 969 for (auto id : ids) { 970 tryInst->AppendCatchTypeId(id, 0); 971 } 972 return *this; 973 } 974 ThrowableInsts(std::vector<int> && ids)975 IrConstructor &ThrowableInsts(std::vector<int> &&ids) 976 { 977 auto inst = CurrentInst(); 978 ASSERT(inst->GetOpcode() == Opcode::CatchPhi); 979 auto catchPhi = inst->CastToCatchPhi(); 980 for (auto id : ids) { 981 ASSERT(instMap_.count(id) > 0); 982 catchPhi->AppendThrowableInst(instMap_.at(id)); 983 } 984 return *this; 985 } 986 DeoptimizeType(DeoptimizeType type)987 IrConstructor &DeoptimizeType(DeoptimizeType type) 988 { 989 auto inst = CurrentInst(); 990 if (inst->GetOpcode() == Opcode::Deoptimize) { 991 inst->CastToDeoptimize()->SetDeoptimizeType(type); 992 } else { 993 ASSERT(inst->GetOpcode() == Opcode::DeoptimizeIf); 994 inst->CastToDeoptimizeIf()->SetDeoptimizeType(type); 995 } 996 return *this; 997 } 998 SrcType(DataType::Type type)999 IrConstructor &SrcType(DataType::Type type) 1000 { 1001 auto inst = CurrentInst(); 1002 switch (inst->GetOpcode()) { 1003 case Opcode::Cmp: 1004 inst->CastToCmp()->SetOperandsType(type); 1005 break; 1006 case Opcode::Compare: 1007 inst->CastToCompare()->SetOperandsType(type); 1008 break; 1009 case Opcode::If: 1010 inst->CastToIf()->SetOperandsType(type); 1011 break; 1012 case Opcode::AddOverflow: 1013 inst->CastToAddOverflow()->SetOperandsType(type); 1014 break; 1015 case Opcode::SubOverflow: 1016 inst->CastToSubOverflow()->SetOperandsType(type); 1017 break; 1018 case Opcode::IfImm: 1019 inst->CastToIfImm()->SetOperandsType(type); 1020 break; 1021 case Opcode::Select: 1022 inst->CastToSelect()->SetOperandsType(type); 1023 break; 1024 case Opcode::SelectImm: 1025 inst->CastToSelectImm()->SetOperandsType(type); 1026 break; 1027 case Opcode::Cast: 1028 inst->CastToCast()->SetOperandsType(type); 1029 break; 1030 case Opcode::Bitcast: 1031 inst->CastToBitcast()->SetOperandsType(type); 1032 break; 1033 default: 1034 UNREACHABLE(); 1035 } 1036 return *this; 1037 } 1038 IntrinsicId(RuntimeInterface::IntrinsicId id)1039 IrConstructor &IntrinsicId(RuntimeInterface::IntrinsicId id) 1040 { 1041 auto inst = CurrentInst(); 1042 ASSERT(inst->IsIntrinsic()); 1043 inst->CastToIntrinsic()->SetIntrinsicId(id); 1044 return *this; 1045 } 1046 Relocate()1047 IrConstructor &Relocate() 1048 { 1049 auto inst = CurrentInst(); 1050 ASSERT(inst->IsIntrinsic()); 1051 inst->CastToIntrinsic()->SetRelocate(); 1052 return *this; 1053 } 1054 Class(RuntimeInterface::ClassPtr klass)1055 IrConstructor &Class(RuntimeInterface::ClassPtr klass) 1056 { 1057 auto inst = CurrentInst(); 1058 switch (inst->GetOpcode()) { 1059 case Opcode::InitClass: 1060 inst->CastToInitClass()->SetClass(klass); 1061 break; 1062 case Opcode::LoadClass: 1063 inst->CastToLoadClass()->SetClass(klass); 1064 break; 1065 case Opcode::LoadAndInitClass: 1066 inst->CastToLoadAndInitClass()->SetClass(klass); 1067 break; 1068 case Opcode::UnresolvedLoadAndInitClass: 1069 inst->CastToUnresolvedLoadAndInitClass()->SetClass(klass); 1070 break; 1071 case Opcode::LoadImmediate: 1072 inst->CastToLoadImmediate()->SetObjectType(LoadImmediateInst::ObjectType::CLASS); 1073 inst->CastToLoadImmediate()->SetClass(klass); 1074 break; 1075 default: 1076 UNREACHABLE(); 1077 } 1078 return *this; 1079 } 1080 IntegerWasSeen(bool value)1081 IrConstructor &IntegerWasSeen(bool value) 1082 { 1083 auto inst = CurrentInst(); 1084 switch (inst->GetOpcode()) { 1085 case Opcode::CastAnyTypeValue: 1086 inst->CastToCastAnyTypeValue()->SetIsIntegerWasSeen(value); 1087 break; 1088 case Opcode::AnyTypeCheck: 1089 inst->CastToAnyTypeCheck()->SetIsIntegerWasSeen(value); 1090 break; 1091 default: 1092 UNREACHABLE(); 1093 } 1094 return *this; 1095 } 1096 AllowedInputType(profiling::AnyInputType type)1097 IrConstructor &AllowedInputType(profiling::AnyInputType type) 1098 { 1099 auto inst = CurrentInst(); 1100 switch (inst->GetOpcode()) { 1101 case Opcode::CastAnyTypeValue: 1102 inst->CastToCastAnyTypeValue()->SetAllowedInputType(type); 1103 break; 1104 case Opcode::AnyTypeCheck: 1105 inst->CastToAnyTypeCheck()->SetAllowedInputType(type); 1106 break; 1107 default: 1108 UNREACHABLE(); 1109 } 1110 return *this; 1111 } 1112 Loc(uint32_t dirNumber,uint32_t fileNumber,uint32_t lineNumber)1113 IrConstructor &Loc([[maybe_unused]] uint32_t dirNumber, [[maybe_unused]] uint32_t fileNumber, 1114 [[maybe_unused]] uint32_t lineNumber) 1115 { 1116 #ifdef PANDA_COMPILER_DEBUG_INFO 1117 auto debugInfo = graph_->GetAllocator()->New<InstDebugInfo>(dirNumber, fileNumber, lineNumber); 1118 CurrentInst()->SetDebugInfo(debugInfo); 1119 #endif 1120 return *this; 1121 } 1122 SetAccessType(DynObjectAccessType type)1123 IrConstructor &SetAccessType(DynObjectAccessType type) 1124 { 1125 auto *inst = CurrentInst(); 1126 switch (inst->GetOpcode()) { 1127 case Opcode::LoadObjectDynamic: 1128 inst->CastToLoadObjectDynamic()->SetAccessType(type); 1129 break; 1130 case Opcode::StoreObjectDynamic: 1131 inst->CastToStoreObjectDynamic()->SetAccessType(type); 1132 break; 1133 default: 1134 UNREACHABLE(); 1135 } 1136 return *this; 1137 } 1138 SetPtr(uintptr_t ptr)1139 IrConstructor &SetPtr(uintptr_t ptr) 1140 { 1141 auto *inst = CurrentInst(); 1142 switch (inst->GetOpcode()) { 1143 case Opcode::LoadObjFromConst: 1144 inst->CastToLoadObjFromConst()->SetObjPtr(ptr); 1145 break; 1146 case Opcode::FunctionImmediate: 1147 inst->CastToFunctionImmediate()->SetFunctionPtr(ptr); 1148 break; 1149 default: 1150 UNREACHABLE(); 1151 } 1152 return *this; 1153 } 1154 SetAccessMode(DynObjectAccessMode mode)1155 IrConstructor &SetAccessMode(DynObjectAccessMode mode) 1156 { 1157 auto *inst = CurrentInst(); 1158 switch (inst->GetOpcode()) { 1159 case Opcode::LoadObjectDynamic: 1160 inst->CastToLoadObjectDynamic()->SetAccessMode(mode); 1161 break; 1162 case Opcode::StoreObjectDynamic: 1163 inst->CastToStoreObjectDynamic()->SetAccessMode(mode); 1164 break; 1165 default: 1166 UNREACHABLE(); 1167 } 1168 return *this; 1169 } 1170 1171 template <typename T> ScopedLife()1172 std::shared_ptr<IrConstructor> ScopedLife() 1173 { 1174 #ifndef __clang_analyzer__ 1175 if constexpr (std::is_same_v<T, BasicBlock>) { 1176 return std::shared_ptr<IrConstructor>(this, [](IrConstructor *b) { b->ResetCurrentBb(); }); 1177 } else if constexpr (std::is_same_v<T, Inst>) { 1178 return std::shared_ptr<IrConstructor>(this, [](IrConstructor *b) { b->ResetCurrentInst(); }); 1179 } else if constexpr (std::is_same_v<T, Graph>) { 1180 return std::shared_ptr<IrConstructor>(this, [](IrConstructor *b) { b->Finalize(); }); 1181 } 1182 #else 1183 return nullptr; 1184 #endif 1185 } 1186 CheckInputType(Inst * inst,Inst * inputInst,size_t inputIdx)1187 void CheckInputType(Inst *inst, Inst *inputInst, size_t inputIdx) 1188 { 1189 auto type = inputInst->GetType(); 1190 auto prevType = inst->GetInputType(inputIdx); 1191 if (prevType == DataType::Type::NO_TYPE) { 1192 switch (inst->GetOpcode()) { 1193 case Opcode::Cmp: 1194 inst->CastToCmp()->SetOperandsType(type); 1195 break; 1196 case Opcode::Compare: 1197 inst->CastToCompare()->SetOperandsType(type); 1198 break; 1199 case Opcode::If: 1200 inst->CastToIf()->SetOperandsType(type); 1201 break; 1202 case Opcode::IfImm: 1203 inst->CastToIfImm()->SetOperandsType(type); 1204 break; 1205 case Opcode::Select: 1206 inst->CastToSelect()->SetOperandsType(type); 1207 break; 1208 case Opcode::SelectImm: 1209 inst->CastToSelectImm()->SetOperandsType(type); 1210 break; 1211 default: 1212 UNREACHABLE(); 1213 } 1214 } else { 1215 // Disable check for Negation (Neg+Add), we can't add deeper verification, 1216 // because the graph has not been built yet 1217 if (inputInst->GetOpcode() == Opcode::Add) { 1218 return; 1219 } 1220 CHECK_EQ(type, prevType); 1221 } 1222 } 1223 ConstructControlFlow()1224 void ConstructControlFlow() 1225 { 1226 for (auto [bbi, succs] : bbSuccsMap_) { 1227 auto bb = bbMap_.at(bbi); 1228 for (auto succ : succs) { 1229 bb->AddSucc(bbMap_.at(succ == -1 ? 1 : succ)); 1230 } 1231 if (succs.size() > 1 && bb->IsEmpty()) { 1232 bb->SetTryEnd(true); 1233 } 1234 } 1235 auto endBlock = graph_->GetEndBlock(); 1236 if (endBlock->GetPredsBlocks().empty()) { 1237 graph_->EraseBlock(endBlock); 1238 graph_->SetEndBlock(nullptr); 1239 } 1240 } 1241 ConstructInput(Inst * inst,size_t inputIdx,const std::vector<int> & vregs,size_t idx)1242 void ConstructInput(Inst *inst, size_t inputIdx, const std::vector<int> &vregs, size_t idx) 1243 { 1244 ASSERT_DO(instMap_.find(inputIdx) != instMap_.end(), 1245 std::cerr << "Input with Id " << inputIdx << " isn't found, inst: " << *inst << std::endl); 1246 auto inputInst = instMap_.at(inputIdx); 1247 auto op = inst->GetOpcode(); 1248 if (!inputInst->IsConst() && (op == Opcode::Cmp || op == Opcode::Compare || op == Opcode::If || 1249 op == Opcode::IfImm || op == Opcode::Select || op == Opcode::SelectImm)) { 1250 CheckInputType(inst, inputInst, idx); 1251 } 1252 if (inst->IsOperandsDynamic()) { 1253 inst->AppendInput(inputInst); 1254 if (inst->IsSaveState()) { 1255 static_cast<SaveStateInst *>(inst)->SetVirtualRegister(idx, 1256 VirtualRegister(vregs[idx], VRegType::VREG)); 1257 } 1258 } else { 1259 inst->SetInput(idx, inputInst); 1260 } 1261 } 1262 ConstructDataFlow()1263 void ConstructDataFlow() 1264 { 1265 for (auto [insti, inputs] : instInputsMap_) { 1266 auto inst = instMap_.at(insti); 1267 const auto &vregs {inst->IsSaveState() ? saveStateInstVregsMap_[insti] : std::vector<int> {}}; 1268 ASSERT(!inst->IsSaveState() || inputs.size() == vregs.size()); 1269 size_t idx = 0; 1270 if (inst->IsOperandsDynamic()) { 1271 inst->ReserveInputs(inputs.size()); 1272 } 1273 for (auto inputIdx : inputs) { 1274 ConstructInput(inst, inputIdx, vregs, idx); 1275 ++idx; 1276 } 1277 } 1278 1279 for (auto [insti, inputs] : phiInstInputsMap_) { 1280 auto inst = instMap_.at(insti); 1281 for (auto input : inputs) { 1282 auto inputInst = instMap_.at(input.second); 1283 size_t idx = inst->GetBasicBlock()->GetPredBlockIndex(bbMap_.at(input.first)); 1284 auto i {inst->AppendInput(inputInst)}; 1285 inst->CastToPhi()->SetPhiInputBbNum(i, idx); 1286 } 1287 } 1288 } 1289 UpdateSpecialFlagsForReference(Inst * inst)1290 void UpdateSpecialFlagsForReference(Inst *inst) 1291 { 1292 if (inst->GetType() == DataType::REFERENCE) { 1293 if (inst->GetOpcode() == Opcode::StoreArray) { 1294 inst->CastToStoreArray()->SetNeedBarrier(true); 1295 } 1296 if (inst->GetOpcode() == Opcode::StoreArrayI) { 1297 inst->CastToStoreArrayI()->SetNeedBarrier(true); 1298 } 1299 if (inst->GetOpcode() == Opcode::StoreStatic) { 1300 inst->CastToStoreStatic()->SetNeedBarrier(true); 1301 } 1302 if (inst->GetOpcode() == Opcode::UnresolvedStoreStatic) { 1303 inst->CastToUnresolvedStoreStatic()->SetNeedBarrier(true); 1304 } 1305 if (inst->GetOpcode() == Opcode::StoreObject) { 1306 inst->CastToStoreObject()->SetNeedBarrier(true); 1307 } 1308 if (inst->GetOpcode() == Opcode::StoreResolvedObjectField) { 1309 inst->CastToStoreResolvedObjectField()->SetNeedBarrier(true); 1310 } 1311 if (inst->GetOpcode() == Opcode::StoreResolvedObjectFieldStatic) { 1312 inst->CastToStoreResolvedObjectFieldStatic()->SetNeedBarrier(true); 1313 } 1314 if (inst->GetOpcode() == Opcode::StoreArrayPair) { 1315 inst->CastToStoreArrayPair()->SetNeedBarrier(true); 1316 } 1317 if (inst->GetOpcode() == Opcode::StoreArrayPairI) { 1318 inst->CastToStoreArrayPairI()->SetNeedBarrier(true); 1319 } 1320 } 1321 } 1322 UpdateSpecialFlags()1323 void UpdateSpecialFlags() 1324 { 1325 size_t maxId = graph_->GetCurrentInstructionId(); 1326 for (auto pair : instMap_) { 1327 auto id = static_cast<size_t>(pair.first); 1328 auto inst = pair.second; 1329 UpdateSpecialFlagsForReference(inst); 1330 if (inst->GetOpcode() == Opcode::Try) { 1331 auto bb = inst->GetBasicBlock(); 1332 bb->SetTryBegin(true); 1333 bb->GetSuccessor(0)->SetTry(true); 1334 for (size_t idx = 1; idx < bb->GetSuccsBlocks().size(); idx++) { 1335 bb->GetSuccessor(idx)->SetCatchBegin(true); 1336 } 1337 } 1338 if (inst->GetOpcode() == Opcode::SaveStateOsr) { 1339 inst->GetBasicBlock()->SetOsrEntry(true); 1340 } 1341 if (id >= maxId) { 1342 maxId = id + 1; 1343 } 1344 } 1345 graph_->SetCurrentInstructionId(maxId); 1346 } 1347 1348 // Create SaveState instructions thet weren't explicitly constructed in the test CreateSaveStates()1349 void CreateSaveStates() 1350 { 1351 for (auto [insti, inputs] : instInputsMap_) { 1352 auto inst = instMap_.at(insti); 1353 if (!inst->IsOperandsDynamic() && inst->RequireState() && inst->GetInputsCount() > inputs.size()) { 1354 auto saveState = graph_->CreateInstSaveState(); 1355 saveState->SetId(static_cast<int>(graph_->GetCurrentInstructionId()) + 1); 1356 graph_->SetCurrentInstructionId(saveState->GetId() + 1); 1357 inst->GetBasicBlock()->InsertBefore(saveState, inst); 1358 inst->SetSaveState(saveState); 1359 } 1360 } 1361 } 1362 SetSpillFillData()1363 void SetSpillFillData() 1364 { 1365 graph_->ResetParameterInfo(); 1366 // Count number of parameters (needed for bytecode optimizer) in first cycle and set SpillFillData for each 1367 // parameter in second cycle 1368 uint32_t numArgs = 0; 1369 for (auto inst : graph_->GetStartBlock()->Insts()) { 1370 if (inst->GetOpcode() == Opcode::Parameter) { 1371 ++numArgs; 1372 } 1373 } 1374 uint32_t i = 0; 1375 for (auto inst : graph_->GetStartBlock()->Insts()) { 1376 if (inst->GetOpcode() != Opcode::Parameter) { 1377 continue; 1378 } 1379 1380 auto type = inst->GetType(); 1381 InstBuilder::SetParamSpillFill(graph_, static_cast<ParameterInst *>(inst), numArgs, i++, type); 1382 } 1383 } 1384 Finalize()1385 void Finalize() 1386 { 1387 ConstructControlFlow(); 1388 ConstructDataFlow(); 1389 UpdateSpecialFlags(); 1390 CreateSaveStates(); 1391 SetSpillFillData(); 1392 ResetCurrentBb(); 1393 ResetCurrentInst(); 1394 graph_->RunPass<LoopAnalyzer>(); 1395 PropagateRegisters(); 1396 if (enableGraphChecker_) { 1397 GraphChecker(graph_).Check(); 1398 } 1399 } 1400 GetInst(unsigned index)1401 Inst &GetInst(unsigned index) 1402 { 1403 ASSERT_DO(instMap_.find(index) != instMap_.end(), std::cerr << "Inst with Id " << index << " isn't found\n"); 1404 return *instMap_.at(index); 1405 } 1406 GetBlock(unsigned index)1407 BasicBlock &GetBlock(unsigned index) 1408 { 1409 return *bbMap_.at(index); 1410 } 1411 EnableGraphChecker(bool value)1412 void EnableGraphChecker(bool value) 1413 { 1414 enableGraphChecker_ = value; 1415 } 1416 1417 private: 1418 template <typename T> AddInput(T v)1419 void AddInput(T v) 1420 { 1421 static_assert(std::is_integral_v<T>); 1422 instInputsMap_[CurrentInstIndex()].push_back(v); 1423 } 1424 1425 template <typename T, typename... Args> AddInput(T v,Args...args)1426 void AddInput(T v, Args... args) 1427 { 1428 instInputsMap_[CurrentInstIndex()].push_back(v); 1429 AddInput(args...); 1430 } 1431 GetBbByIndex(int index)1432 BasicBlock *GetBbByIndex(int index) 1433 { 1434 return bbMap_.at(index); 1435 } 1436 CurrentBb()1437 BasicBlock *CurrentBb() 1438 { 1439 return currentBb_.second; 1440 } 1441 CurrentBbIndex()1442 int CurrentBbIndex() 1443 { 1444 return currentBb_.first; 1445 } 1446 CurrentInst()1447 Inst *CurrentInst() 1448 { 1449 return currentInst_.second; 1450 } 1451 CurrentInstIndex()1452 int CurrentInstIndex() 1453 { 1454 return currentInst_.first; 1455 } 1456 ResetCurrentBb()1457 void ResetCurrentBb() 1458 { 1459 currentBb_ = {-1, nullptr}; 1460 } 1461 ResetCurrentInst()1462 void ResetCurrentInst() 1463 { 1464 currentInst_ = {-1, nullptr}; 1465 } 1466 PropagateRegisters()1467 void PropagateRegisters() 1468 { 1469 for (auto bb : graph_->GetBlocksRPO()) { 1470 for (auto inst : bb->AllInsts()) { 1471 if (inst->GetDstReg() == INVALID_REG || inst->IsOperandsDynamic()) { 1472 continue; 1473 } 1474 for (size_t i = 0; i < inst->GetInputsCount(); i++) { 1475 inst->SetSrcReg(i, inst->GetInputs()[i].GetInst()->GetDstReg()); 1476 } 1477 } 1478 } 1479 } 1480 1481 private: 1482 Graph *graph_ {nullptr}; 1483 ArenaAllocator aa_; 1484 std::pair<int, BasicBlock *> currentBb_; 1485 std::pair<int, Inst *> currentInst_; 1486 ArenaUnorderedMap<int, BasicBlock *> bbMap_ {aa_.Adapter()}; 1487 ArenaVector<std::pair<int, std::vector<int>>> bbSuccsMap_ {aa_.Adapter()}; 1488 ArenaUnorderedMap<int, Inst *> instMap_ {aa_.Adapter()}; 1489 ArenaUnorderedMap<int, std::vector<int>> instInputsMap_ {aa_.Adapter()}; 1490 ArenaUnorderedMap<int, std::vector<int>> saveStateInstVregsMap_ {aa_.Adapter()}; 1491 ArenaUnorderedMap<int, std::vector<std::pair<int, int>>> phiInstInputsMap_ {aa_.Adapter()}; 1492 bool enableGraphChecker_ {true}; 1493 }; 1494 1495 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1496 #define GRAPH(GRAPH) if (auto __g = builder_->SetGraph(GRAPH).ScopedLife<Graph>(); true) 1497 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1498 #define BASIC_BLOCK(ID, ...) \ 1499 if (auto __b = builder_->NewBlock<ID>().Succs({__VA_ARGS__}).template ScopedLife<BasicBlock>(); true) 1500 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1501 #define INST(ID, ...) builder_->NewInst(ID, __VA_ARGS__) 1502 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1503 #define CONSTANT(ID, VALUE) builder_->NewConstant(ID, VALUE) 1504 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1505 #define PARAMETER(ID, ARG_NUM) builder_->NewParameter(ID, ARG_NUM) 1506 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1507 #define NULLPTR(ID) builder_->AddNullptrInst(ID) 1508 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1509 #define INS(INDEX) builder_->GetInst(INDEX) 1510 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1511 #define BB(INDEX) builder_->GetBlock(INDEX) 1512 } // namespace ark::compiler 1513 1514 #endif // PANDA_IR_CONSTRUCTOR_H 1515