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