• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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