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