1 /* 2 * Copyright (c) 2021-2023 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 panda::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 (int i = 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 AdjustFlags(id, inst); 1045 return *this; 1046 } 1047 Relocate()1048 IrConstructor &Relocate() 1049 { 1050 auto inst = CurrentInst(); 1051 ASSERT(inst->IsIntrinsic()); 1052 inst->CastToIntrinsic()->SetRelocate(); 1053 return *this; 1054 } 1055 Class(RuntimeInterface::ClassPtr klass)1056 IrConstructor &Class(RuntimeInterface::ClassPtr klass) 1057 { 1058 auto inst = CurrentInst(); 1059 switch (inst->GetOpcode()) { 1060 case Opcode::InitClass: 1061 inst->CastToInitClass()->SetClass(klass); 1062 break; 1063 case Opcode::LoadClass: 1064 inst->CastToLoadClass()->SetClass(klass); 1065 break; 1066 case Opcode::LoadAndInitClass: 1067 inst->CastToLoadAndInitClass()->SetClass(klass); 1068 break; 1069 case Opcode::UnresolvedLoadAndInitClass: 1070 inst->CastToUnresolvedLoadAndInitClass()->SetClass(klass); 1071 break; 1072 case Opcode::LoadImmediate: 1073 inst->CastToLoadImmediate()->SetObjectType(LoadImmediateInst::ObjectType::CLASS); 1074 inst->CastToLoadImmediate()->SetClass(klass); 1075 break; 1076 default: 1077 UNREACHABLE(); 1078 } 1079 return *this; 1080 } 1081 IntegerWasSeen(bool value)1082 IrConstructor &IntegerWasSeen(bool value) 1083 { 1084 auto inst = CurrentInst(); 1085 switch (inst->GetOpcode()) { 1086 case Opcode::CastAnyTypeValue: 1087 inst->CastToCastAnyTypeValue()->SetIsIntegerWasSeen(value); 1088 break; 1089 case Opcode::AnyTypeCheck: 1090 inst->CastToAnyTypeCheck()->SetIsIntegerWasSeen(value); 1091 break; 1092 default: 1093 UNREACHABLE(); 1094 } 1095 return *this; 1096 } 1097 AllowedInputType(profiling::AnyInputType type)1098 IrConstructor &AllowedInputType(profiling::AnyInputType type) 1099 { 1100 auto inst = CurrentInst(); 1101 switch (inst->GetOpcode()) { 1102 case Opcode::CastAnyTypeValue: 1103 inst->CastToCastAnyTypeValue()->SetAllowedInputType(type); 1104 break; 1105 case Opcode::AnyTypeCheck: 1106 inst->CastToAnyTypeCheck()->SetAllowedInputType(type); 1107 break; 1108 default: 1109 UNREACHABLE(); 1110 } 1111 return *this; 1112 } 1113 Loc(uint32_t dirNumber,uint32_t fileNumber,uint32_t lineNumber)1114 IrConstructor &Loc([[maybe_unused]] uint32_t dirNumber, [[maybe_unused]] uint32_t fileNumber, 1115 [[maybe_unused]] uint32_t lineNumber) 1116 { 1117 #ifdef PANDA_COMPILER_DEBUG_INFO 1118 auto debugInfo = graph_->GetAllocator()->New<InstDebugInfo>(dirNumber, fileNumber, lineNumber); 1119 CurrentInst()->SetDebugInfo(debugInfo); 1120 #endif 1121 return *this; 1122 } 1123 SetAccessType(DynObjectAccessType type)1124 IrConstructor &SetAccessType(DynObjectAccessType type) 1125 { 1126 auto *inst = CurrentInst(); 1127 switch (inst->GetOpcode()) { 1128 case Opcode::LoadObjectDynamic: 1129 inst->CastToLoadObjectDynamic()->SetAccessType(type); 1130 break; 1131 case Opcode::StoreObjectDynamic: 1132 inst->CastToStoreObjectDynamic()->SetAccessType(type); 1133 break; 1134 default: 1135 UNREACHABLE(); 1136 } 1137 return *this; 1138 } 1139 SetPtr(uintptr_t ptr)1140 IrConstructor &SetPtr(uintptr_t ptr) 1141 { 1142 auto *inst = CurrentInst(); 1143 switch (inst->GetOpcode()) { 1144 case Opcode::LoadObjFromConst: 1145 inst->CastToLoadObjFromConst()->SetObjPtr(ptr); 1146 break; 1147 case Opcode::FunctionImmediate: 1148 inst->CastToFunctionImmediate()->SetFunctionPtr(ptr); 1149 break; 1150 default: 1151 UNREACHABLE(); 1152 } 1153 return *this; 1154 } 1155 SetAccessMode(DynObjectAccessMode mode)1156 IrConstructor &SetAccessMode(DynObjectAccessMode mode) 1157 { 1158 auto *inst = CurrentInst(); 1159 switch (inst->GetOpcode()) { 1160 case Opcode::LoadObjectDynamic: 1161 inst->CastToLoadObjectDynamic()->SetAccessMode(mode); 1162 break; 1163 case Opcode::StoreObjectDynamic: 1164 inst->CastToStoreObjectDynamic()->SetAccessMode(mode); 1165 break; 1166 default: 1167 UNREACHABLE(); 1168 } 1169 return *this; 1170 } 1171 1172 template <typename T> ScopedLife()1173 std::shared_ptr<IrConstructor> ScopedLife() 1174 { 1175 #ifndef __clang_analyzer__ 1176 if constexpr (std::is_same_v<T, BasicBlock>) { 1177 return std::shared_ptr<IrConstructor>(this, [](IrConstructor *b) { b->ResetCurrentBb(); }); 1178 } else if constexpr (std::is_same_v<T, Inst>) { 1179 return std::shared_ptr<IrConstructor>(this, [](IrConstructor *b) { b->ResetCurrentInst(); }); 1180 } else if constexpr (std::is_same_v<T, Graph>) { 1181 return std::shared_ptr<IrConstructor>(this, [](IrConstructor *b) { b->Finalize(); }); 1182 } 1183 #else 1184 return nullptr; 1185 #endif 1186 } 1187 CheckInputType(Inst * inst,Inst * inputInst,size_t inputIdx)1188 void CheckInputType(Inst *inst, Inst *inputInst, size_t inputIdx) 1189 { 1190 auto type = inputInst->GetType(); 1191 auto prevType = inst->GetInputType(inputIdx); 1192 if (prevType == DataType::Type::NO_TYPE) { 1193 switch (inst->GetOpcode()) { 1194 case Opcode::Cmp: 1195 inst->CastToCmp()->SetOperandsType(type); 1196 break; 1197 case Opcode::Compare: 1198 inst->CastToCompare()->SetOperandsType(type); 1199 break; 1200 case Opcode::If: 1201 inst->CastToIf()->SetOperandsType(type); 1202 break; 1203 case Opcode::IfImm: 1204 inst->CastToIfImm()->SetOperandsType(type); 1205 break; 1206 case Opcode::Select: 1207 inst->CastToSelect()->SetOperandsType(type); 1208 break; 1209 case Opcode::SelectImm: 1210 inst->CastToSelectImm()->SetOperandsType(type); 1211 break; 1212 default: 1213 UNREACHABLE(); 1214 } 1215 } else { 1216 // Disable check for Negation (Neg+Add), we can't add deeper verification, 1217 // because the graph has not been built yet 1218 if (inputInst->GetOpcode() == Opcode::Add) { 1219 return; 1220 } 1221 CHECK_EQ(type, prevType); 1222 } 1223 } 1224 ConstructControlFlow()1225 void ConstructControlFlow() 1226 { 1227 for (auto [bbi, succs] : bbSuccsMap_) { 1228 auto bb = bbMap_.at(bbi); 1229 for (auto succ : succs) { 1230 bb->AddSucc(bbMap_.at(succ == -1 ? 1 : succ)); 1231 } 1232 if (succs.size() > 1 && bb->IsEmpty()) { 1233 bb->SetTryEnd(true); 1234 } 1235 } 1236 auto endBlock = graph_->GetEndBlock(); 1237 if (endBlock->GetPredsBlocks().empty()) { 1238 graph_->EraseBlock(endBlock); 1239 graph_->SetEndBlock(nullptr); 1240 } 1241 } 1242 ConstructDataFlow()1243 void ConstructDataFlow() 1244 { 1245 for (auto [insti, inputs] : instInputsMap_) { 1246 auto inst = instMap_.at(insti); 1247 const auto &vregs {inst->IsSaveState() ? saveStateInstVregsMap_[insti] : std::vector<int> {}}; 1248 ASSERT(!inst->IsSaveState() || inputs.size() == vregs.size()); 1249 size_t idx = 0; 1250 if (inst->IsOperandsDynamic()) { 1251 inst->ReserveInputs(inputs.size()); 1252 } 1253 for (auto inputIdx : inputs) { 1254 ASSERT_DO(instMap_.find(inputIdx) != instMap_.end(), 1255 std::cerr << "Input with Id " << inputIdx << " isn't found, inst: " << *inst << std::endl); 1256 auto inputInst = instMap_.at(inputIdx); 1257 auto op = inst->GetOpcode(); 1258 if (!inputInst->IsConst() && (op == Opcode::Cmp || op == Opcode::Compare || op == Opcode::If || 1259 op == Opcode::IfImm || op == Opcode::Select || op == Opcode::SelectImm)) { 1260 CheckInputType(inst, inputInst, idx); 1261 } 1262 if (inst->IsOperandsDynamic()) { 1263 inst->AppendInput(inputInst); 1264 if (inst->IsSaveState()) { 1265 static_cast<SaveStateInst *>(inst)->SetVirtualRegister( 1266 idx, VirtualRegister(vregs[idx], VRegType::VREG)); 1267 } 1268 } else { 1269 inst->SetInput(idx, inputInst); 1270 } 1271 ++idx; 1272 } 1273 } 1274 1275 for (auto [insti, inputs] : phiInstInputsMap_) { 1276 auto inst = instMap_.at(insti); 1277 for (auto input : inputs) { 1278 auto inputInst = instMap_.at(input.second); 1279 size_t idx = inst->GetBasicBlock()->GetPredBlockIndex(bbMap_.at(input.first)); 1280 auto i {inst->AppendInput(inputInst)}; 1281 inst->CastToPhi()->SetPhiInputBbNum(i, idx); 1282 } 1283 } 1284 } 1285 UpdateSpecialFlagsForReference(Inst * inst)1286 void UpdateSpecialFlagsForReference(Inst *inst) 1287 { 1288 if (inst->GetType() == DataType::REFERENCE) { 1289 if (inst->GetOpcode() == Opcode::StoreArray) { 1290 inst->CastToStoreArray()->SetNeedBarrier(true); 1291 } 1292 if (inst->GetOpcode() == Opcode::StoreArrayI) { 1293 inst->CastToStoreArrayI()->SetNeedBarrier(true); 1294 } 1295 if (inst->GetOpcode() == Opcode::StoreStatic) { 1296 inst->CastToStoreStatic()->SetNeedBarrier(true); 1297 } 1298 if (inst->GetOpcode() == Opcode::UnresolvedStoreStatic) { 1299 inst->CastToUnresolvedStoreStatic()->SetNeedBarrier(true); 1300 } 1301 if (inst->GetOpcode() == Opcode::StoreObject) { 1302 inst->CastToStoreObject()->SetNeedBarrier(true); 1303 } 1304 if (inst->GetOpcode() == Opcode::StoreResolvedObjectField) { 1305 inst->CastToStoreResolvedObjectField()->SetNeedBarrier(true); 1306 } 1307 if (inst->GetOpcode() == Opcode::StoreResolvedObjectFieldStatic) { 1308 inst->CastToStoreResolvedObjectFieldStatic()->SetNeedBarrier(true); 1309 } 1310 if (inst->GetOpcode() == Opcode::StoreArrayPair) { 1311 inst->CastToStoreArrayPair()->SetNeedBarrier(true); 1312 } 1313 if (inst->GetOpcode() == Opcode::StoreArrayPairI) { 1314 inst->CastToStoreArrayPairI()->SetNeedBarrier(true); 1315 } 1316 } 1317 } 1318 UpdateSpecialFlags()1319 void UpdateSpecialFlags() 1320 { 1321 int maxId = graph_->GetCurrentInstructionId(); 1322 for (auto pair : instMap_) { 1323 auto id = pair.first; 1324 auto inst = pair.second; 1325 UpdateSpecialFlagsForReference(inst); 1326 if (inst->GetOpcode() == Opcode::Try) { 1327 auto bb = inst->GetBasicBlock(); 1328 bb->SetTryBegin(true); 1329 bb->GetSuccessor(0)->SetTry(true); 1330 for (size_t idx = 1; idx < bb->GetSuccsBlocks().size(); idx++) { 1331 bb->GetSuccessor(idx)->SetCatchBegin(true); 1332 } 1333 } 1334 if (inst->GetOpcode() == Opcode::SaveStateOsr) { 1335 inst->GetBasicBlock()->SetOsrEntry(true); 1336 } 1337 if (id >= maxId) { 1338 maxId = id + 1; 1339 } 1340 } 1341 graph_->SetCurrentInstructionId(maxId); 1342 } 1343 1344 // Create SaveState instructions thet weren't explicitly constructed in the test CreateSaveStates()1345 void CreateSaveStates() 1346 { 1347 for (auto [insti, inputs] : instInputsMap_) { 1348 auto inst = instMap_.at(insti); 1349 if (!inst->IsOperandsDynamic() && inst->RequireState() && inst->GetInputsCount() > inputs.size()) { 1350 auto saveState = graph_->CreateInstSaveState(); 1351 saveState->SetId(static_cast<int>(graph_->GetCurrentInstructionId()) + 1); 1352 graph_->SetCurrentInstructionId(saveState->GetId() + 1); 1353 inst->GetBasicBlock()->InsertBefore(saveState, inst); 1354 inst->SetSaveState(saveState); 1355 } 1356 } 1357 } 1358 SetSpillFillData()1359 void SetSpillFillData() 1360 { 1361 graph_->ResetParameterInfo(); 1362 // Count number of parameters (needed for bytecode optimizer) in first cycle and set SpillFillData for each 1363 // parameter in second cycle 1364 uint32_t numArgs = 0; 1365 for (auto inst : graph_->GetStartBlock()->Insts()) { 1366 if (inst->GetOpcode() == Opcode::Parameter) { 1367 ++numArgs; 1368 } 1369 } 1370 uint32_t i = 0; 1371 for (auto inst : graph_->GetStartBlock()->Insts()) { 1372 if (inst->GetOpcode() != Opcode::Parameter) { 1373 continue; 1374 } 1375 1376 auto type = inst->GetType(); 1377 InstBuilder::SetParamSpillFill(graph_, static_cast<ParameterInst *>(inst), numArgs, i++, type); 1378 } 1379 } 1380 Finalize()1381 void Finalize() 1382 { 1383 ConstructControlFlow(); 1384 ConstructDataFlow(); 1385 UpdateSpecialFlags(); 1386 CreateSaveStates(); 1387 SetSpillFillData(); 1388 ResetCurrentBb(); 1389 ResetCurrentInst(); 1390 graph_->RunPass<LoopAnalyzer>(); 1391 PropagateRegisters(); 1392 if (enableGraphChecker_) { 1393 GraphChecker(graph_).Check(); 1394 } 1395 } 1396 GetInst(unsigned index)1397 Inst &GetInst(unsigned index) 1398 { 1399 ASSERT_DO(instMap_.find(index) != instMap_.end(), std::cerr << "Inst with Id " << index << " isn't found\n"); 1400 return *instMap_.at(index); 1401 } 1402 GetBlock(unsigned index)1403 BasicBlock &GetBlock(unsigned index) 1404 { 1405 return *bbMap_.at(index); 1406 } 1407 EnableGraphChecker(bool value)1408 void EnableGraphChecker(bool value) 1409 { 1410 enableGraphChecker_ = value; 1411 } 1412 1413 private: 1414 template <typename T> AddInput(T v)1415 void AddInput(T v) 1416 { 1417 static_assert(std::is_integral_v<T>); 1418 instInputsMap_[CurrentInstIndex()].push_back(v); 1419 } 1420 1421 template <typename T, typename... Args> AddInput(T v,Args...args)1422 void AddInput(T v, Args... args) 1423 { 1424 instInputsMap_[CurrentInstIndex()].push_back(v); 1425 AddInput(args...); 1426 } 1427 GetBbByIndex(int index)1428 BasicBlock *GetBbByIndex(int index) 1429 { 1430 return bbMap_.at(index); 1431 } 1432 CurrentBb()1433 BasicBlock *CurrentBb() 1434 { 1435 return currentBb_.second; 1436 } 1437 CurrentBbIndex()1438 int CurrentBbIndex() 1439 { 1440 return currentBb_.first; 1441 } 1442 CurrentInst()1443 Inst *CurrentInst() 1444 { 1445 return currentInst_.second; 1446 } 1447 CurrentInstIndex()1448 int CurrentInstIndex() 1449 { 1450 return currentInst_.first; 1451 } 1452 ResetCurrentBb()1453 void ResetCurrentBb() 1454 { 1455 currentBb_ = {-1, nullptr}; 1456 } 1457 ResetCurrentInst()1458 void ResetCurrentInst() 1459 { 1460 currentInst_ = {-1, nullptr}; 1461 } 1462 PropagateRegisters()1463 void PropagateRegisters() 1464 { 1465 for (auto bb : graph_->GetBlocksRPO()) { 1466 for (auto inst : bb->AllInsts()) { 1467 if (inst->GetDstReg() == INVALID_REG || inst->IsOperandsDynamic()) { 1468 continue; 1469 } 1470 for (size_t i = 0; i < inst->GetInputsCount(); i++) { 1471 inst->SetSrcReg(i, inst->GetInputs()[i].GetInst()->GetDstReg()); 1472 } 1473 } 1474 } 1475 } 1476 1477 private: 1478 Graph *graph_ {nullptr}; 1479 ArenaAllocator aa_; 1480 std::pair<int, BasicBlock *> currentBb_; 1481 std::pair<int, Inst *> currentInst_; 1482 ArenaUnorderedMap<int, BasicBlock *> bbMap_ {aa_.Adapter()}; 1483 ArenaVector<std::pair<int, std::vector<int>>> bbSuccsMap_ {aa_.Adapter()}; 1484 ArenaUnorderedMap<int, Inst *> instMap_ {aa_.Adapter()}; 1485 ArenaUnorderedMap<int, std::vector<int>> instInputsMap_ {aa_.Adapter()}; 1486 ArenaUnorderedMap<int, std::vector<int>> saveStateInstVregsMap_ {aa_.Adapter()}; 1487 ArenaUnorderedMap<int, std::vector<std::pair<int, int>>> phiInstInputsMap_ {aa_.Adapter()}; 1488 bool enableGraphChecker_ {true}; 1489 }; 1490 1491 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1492 #define GRAPH(GRAPH) if (auto __g = builder_->SetGraph(GRAPH).ScopedLife<Graph>(); true) 1493 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1494 #define BASIC_BLOCK(ID, ...) \ 1495 if (auto __b = builder_->NewBlock<ID>().Succs({__VA_ARGS__}).ScopedLife<BasicBlock>(); true) 1496 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1497 #define INST(ID, ...) builder_->NewInst(ID, __VA_ARGS__) 1498 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1499 #define CONSTANT(ID, VALUE) builder_->NewConstant(ID, VALUE) 1500 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1501 #define PARAMETER(ID, ARG_NUM) builder_->NewParameter(ID, ARG_NUM) 1502 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1503 #define NULLPTR(ID) builder_->AddNullptrInst(ID) 1504 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1505 #define INS(INDEX) builder_->GetInst(INDEX) 1506 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 1507 #define BB(INDEX) builder_->GetBlock(INDEX) 1508 } // namespace panda::compiler 1509 1510 #endif // PANDA_IR_CONSTRUCTOR_H 1511