• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 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 #include "graph.h"
21 #include "graph_checker.h"
22 #include "mark_word.h"
23 #include "optimizer/ir_builder/inst_builder.h"
24 
25 namespace panda::compiler {
26 /**
27  * This class aims to simplify IR construction.
28  *
29  * Example:
30  *  auto g = CreateEmptyGraph();
31  *  GRAPH(g) {
32  *      BASIC_BLOCK(0, 1, 2) {
33  *          INST(0, Opcode::IntConstant).Constant(12);
34  *          INST(1, Opcode::IntConstant).Constant(12);
35  *          INST(2, Opcode::Add).Inputs(0, 1);
36  *          INST(6, Opcode::Compare).Inputs(2).CC(ConditionCode::CC_AE);
37  *          INST(7, Opcode::If).Inputs(6);
38  *      }
39  *      BASIC_BLOCK(1, 2) {
40  *          INST(3, Opcode::Not).Inputs(0);
41  *      }
42  *      BASIC_BLOCK(2, -1) {
43  *          INST(4, Opcode::Phi).Inputs(2, 3);
44  *          INST(5, Opcode::Not).Inputs(4);
45  *      }
46  *  }
47  *  g->Dump(cerr);
48  *
49  * GRAPH(g) macro initializies Builder object by 'g' graph. Builder works with this graph only inside followed scope
50  *          in braces.
51  * BASIC_BLOCK creates new basic block and add it to the current graph. All code inside followed scope will work with
52  *          this basic block.
53  *          First argument is ID of basic block. It must be unique for graph.
54  *          All remaining arguments are IDs of successors blocks. '-1' value means that there is no successor. Block
55  *          that hasn't successors is considered as end block.
56  *          Block with '0' ID is considered as start block.
57  * INST creates new instruction and append it to the current basic block.
58  *          First parameter is ID of instruction. It must be unique within the current graph
59  *          Second parameter is an opcode.
60  *          Dataflow can be constructed via 'Inputs' method, that gets IDs of the input instructions as parameters.
61  *          All other properties of instruction may be set via corresponding proxy methods, defined in Builder.
62  */
63 class IrConstructor final {
64 public:
65     static const size_t ID_ENTRY_BB = 0;
66     static const size_t ID_EXIT_BB = 1U;
67 
68     static constexpr DataType::Type MARK_WORD_TYPE = DataType::GetIntTypeBySize(sizeof(MarkWord::markWordSize));
69 
IrConstructor()70     IrConstructor() : aa_(SpaceType::SPACE_TYPE_COMPILER) {}
71 
SetGraph(Graph * graph)72     IrConstructor &SetGraph(Graph *graph)
73     {
74         graph_ = graph;
75         if (graph_->GetStartBlock() == nullptr) {
76             graph_->CreateStartBlock();
77         }
78         if (graph_->GetEndBlock() == nullptr) {
79             graph_->CreateEndBlock(0U);
80         }
81         ASSERT(graph_->GetVectorBlocks().size() == 2U);
82         bb_map_.clear();
83         bb_map_[ID_ENTRY_BB] = graph_->GetStartBlock();
84         bb_map_[ID_EXIT_BB] = graph_->GetEndBlock();
85         bb_succs_map_.clear();
86         inst_map_.clear();
87         inst_inputs_map_.clear();
88         save_state_inst_vregs_map_.clear();
89         phi_inst_inputs_map_.clear();
90         return *this;
91     }
92 
93     template <size_t id>
NewBlock()94     IrConstructor &NewBlock()
95     {
96         ASSERT(id != ID_ENTRY_BB && id != ID_EXIT_BB);
97         ASSERT(bb_map_.count(id) == 0);
98         ASSERT(CurrentBb() == nullptr);
99         auto bb = graph_->GetAllocator()->New<BasicBlock>(graph_);
100         bb->SetGuestPc(0U);
101 #ifdef NDEBUG
102         graph_->AddBlock(bb);
103 #else
104         graph_->AddBlock(bb, id);
105 #endif
106         current_bb_ = {id, bb};
107         bb_map_[id] = bb;
108         // add connection the first custom block with entry
109         if (bb_succs_map_.empty()) {
110             graph_->GetStartBlock()->AddSucc(bb);
111         }
112         return *this;
113     }
114 
115     template <typename... Args>
NewInst(size_t id,Args &&...args)116     IrConstructor &NewInst(size_t id, Args &&... args)
117     {
118         ASSERT_DO(inst_map_.find(id) == inst_map_.end(),
119                   std::cerr << "Instruction with same Id " << id << "already exists");
120         auto inst = graph_->CreateInst(std::forward<Args>(args)...);
121         inst->SetId(id);
122         for (size_t i = 0; i < inst->GetInputsCount(); ++i) {
123             inst->SetSrcReg(i, INVALID_REG);
124         }
125         current_inst_ = {id, inst};
126         inst_map_[id] = inst;
127         auto block = CurrentBb();
128         if (block == nullptr) {
129             block = graph_->GetStartBlock();
130         }
131         ASSERT(block);
132         if (inst->IsPhi()) {
133             block->AppendPhi(inst);
134         } else {
135             block->AppendInst(inst);
136         }
137 #ifndef NDEBUG
138         if (inst->IsLowLevel()) {
139             // GraphChecker hack: LowLevel instructions may appear only after Lowering pass:
140             graph_->SetLowLevelInstructionsEnabled();
141         }
142 #endif
143         if (inst->IsSaveState()) {
144             graph_->SetVRegsCount(std::max(graph_->GetVRegsCount(), sizeof...(args)));
145         }
146         return *this;
147     }
148 
149     template <typename T>
NewConstant(size_t id,T value)150     IrConstructor &NewConstant(size_t id, [[maybe_unused]] T value)
151     {
152         ASSERT_DO(inst_map_.find(id) == inst_map_.end(),
153                   std::cerr << "Instruction with same Id " << id << "already exists");
154         Inst *inst = nullptr;
155         auto to_start_bb = (CurrentBbIndex() == 0) || (CurrentBbIndex() == -1);
156         if constexpr (std::is_same<T, std::nullptr_t>()) {
157             if (to_start_bb) {
158                 inst = graph_->GetOrCreateNullPtr();
159             } else {
160                 inst = graph_->CreateInstNullPtr();
161                 inst->SetType(DataType::REFERENCE);
162                 CurrentBb()->AppendInst(inst);
163             }
164         } else {
165             if (to_start_bb) {
166                 inst = graph_->FindOrCreateConstant(value);
167             } else {
168                 inst = graph_->CreateInstConstant(value, graph_->IsBytecodeOptimizer());
169                 CurrentBb()->AppendInst(inst);
170             }
171         }
172         inst->SetId(id);
173         inst_map_[id] = inst;
174         current_inst_ = {id, inst};
175         return *this;
176     }
177 
NewParameter(int id,uint16_t arg_number)178     IrConstructor &NewParameter(int id, uint16_t arg_number)
179     {
180         ASSERT_DO(inst_map_.find(id) == inst_map_.end(),
181                   std::cerr << "Instruction with same Id " << id << "already exists");
182         auto inst = graph_->AddNewParameter(arg_number);
183         inst->SetId(id);
184         inst_map_[id] = inst;
185         current_inst_ = {id, inst};
186         return *this;
187     }
188 
Succs(std::vector<int> succs)189     IrConstructor &Succs(std::vector<int> succs)
190     {
191         bb_succs_map_.emplace_back(CurrentBbIndex(), std::move(succs));
192         return *this;
193     }
194 
195     /// Define inputs for current instruction.
196     /// Input is an index of input instruction.
197     template <typename... Args>
Inputs(Args...inputs)198     IrConstructor &Inputs(Args... inputs)
199     {
200         ASSERT(!CurrentInst()->IsCall() && !CurrentInst()->IsIntrinsic());
201         inst_inputs_map_[CurrentInstIndex()].reserve(sizeof...(inputs));
202         if constexpr (sizeof...(inputs) != 0) {
203             AddInput(inputs...);
204         }
205         return *this;
206     }
207 
208     /// Define inputs for current call-, intrinsic-, or phi-instriction.
209     /// Input is defined by std::pair.
210     /// In case of phi: first is index of basic block, second is index of input instruction.
211     /// In case of call and intrinsic: first is Type of input, second is index of input instruction.
Inputs(std::initializer_list<std::pair<int,int>> inputs)212     IrConstructor &Inputs(std::initializer_list<std::pair<int, int>> inputs)
213     {
214         ASSERT(CurrentInst()->IsPhi() || CurrentInst()->IsCall() || CurrentInst()->IsInitObject() ||
215                CurrentInst()->IsIntrinsic());
216         if (CurrentInst()->IsPhi()) {
217             phi_inst_inputs_map_[CurrentInstIndex()].reserve(inputs.size());
218             for (const auto &input : inputs) {
219                 phi_inst_inputs_map_[CurrentInstIndex()].push_back(input);
220             }
221         } else {
222             auto opc = CurrentInst()->GetOpcode();
223             InputTypesMixin *types;
224             switch (opc) {
225                 case Opcode::Intrinsic:
226                     types = static_cast<InputTypesMixin *>(CurrentInst()->CastToIntrinsic());
227                     break;
228                 case Opcode::CallIndirect:
229                     types = static_cast<InputTypesMixin *>(CurrentInst()->CastToCallIndirect());
230                     break;
231                 case Opcode::Builtin:
232                     types = static_cast<InputTypesMixin *>(CurrentInst()->CastToBuiltin());
233                     break;
234                 case Opcode::InitObject:
235                     types = static_cast<InputTypesMixin *>(CurrentInst()->CastToInitObject());
236                     break;
237                 default:
238                     ASSERT(CurrentInst()->IsCall());
239                     types = static_cast<InputTypesMixin *>(static_cast<CallInst *>(CurrentInst()));
240                     break;
241             }
242 
243             inst_inputs_map_[CurrentInstIndex()].reserve(inputs.size());
244             types->AllocateInputTypes(graph_->GetAllocator(), inputs.size());
245             for (const auto &input : inputs) {
246                 types->AddInputType(static_cast<DataType::Type>(input.first));
247                 inst_inputs_map_[CurrentInstIndex()].push_back(input.second);
248             }
249         }
250         return *this;
251     }
252 
253     /// Same as the default Inputs() method, but defines inputs' types with respect to call-instructions.
254     /// Copies types of inputs to the instruction's input_types_.
255     template <typename... Args>
InputsAutoType(Args...inputs)256     IrConstructor &InputsAutoType(Args... inputs)
257     {
258         ASSERT(CurrentInst()->IsCall());
259         auto *call_inst = static_cast<CallInst *>(CurrentInst());
260         call_inst->AllocateInputTypes(graph_->GetAllocator(), sizeof...(inputs));
261         ((call_inst->AddInputType(GetInst(inputs).GetType())), ...);
262         inst_inputs_map_[CurrentInstIndex()].reserve(sizeof...(inputs));
263         ((inst_inputs_map_[CurrentInstIndex()].push_back(inputs)), ...);
264         return *this;
265     }
266 
Pc(uint32_t pc)267     IrConstructor &Pc(uint32_t pc)
268     {
269         ASSERT(CurrentInst());
270         CurrentInst()->SetPc(pc);
271         return *this;
272     }
273 
274     IrConstructor &Volatile(bool volat = true)
275     {
276         auto inst = CurrentInst();
277         switch (inst->GetOpcode()) {
278             case Opcode::StoreObject:
279                 inst->CastToStoreObject()->SetVolatile(volat);
280                 break;
281             case Opcode::LoadObject:
282                 inst->CastToLoadObject()->SetVolatile(volat);
283                 break;
284             case Opcode::StoreStatic:
285                 inst->CastToStoreStatic()->SetVolatile(volat);
286                 break;
287             case Opcode::LoadStatic:
288                 inst->CastToLoadStatic()->SetVolatile(volat);
289                 break;
290             case Opcode::Store:
291                 inst->CastToStore()->SetVolatile(volat);
292                 break;
293             case Opcode::Load:
294                 inst->CastToLoad()->SetVolatile(volat);
295                 break;
296             case Opcode::StoreI:
297                 inst->CastToStoreI()->SetVolatile(volat);
298                 break;
299             case Opcode::LoadI:
300                 inst->CastToLoadI()->SetVolatile(volat);
301                 break;
302             default:
303                 UNREACHABLE();
304         }
305         return *this;
306     }
307 
IsArray(bool value)308     IrConstructor &IsArray(bool value)
309     {
310         auto inst = CurrentInst();
311         switch (inst->GetOpcode()) {
312             case Opcode::LoadArray:
313                 inst->CastToLoadArray()->SetIsArray(value);
314                 break;
315             case Opcode::LenArray:
316                 inst->CastToLenArray()->SetIsArray(value);
317                 break;
318             default:
319                 UNREACHABLE();
320         }
321         return *this;
322     }
323 
CC(ConditionCode cc)324     IrConstructor &CC(ConditionCode cc)
325     {
326         auto inst = CurrentInst();
327         switch (inst->GetOpcode()) {
328             case Opcode::Compare:
329                 inst->CastToCompare()->SetCc(cc);
330                 break;
331             case Opcode::If:
332                 inst->CastToIf()->SetCc(cc);
333                 break;
334             case Opcode::AddOverflow:
335                 inst->CastToAddOverflow()->SetCc(cc);
336                 break;
337             case Opcode::SubOverflow:
338                 inst->CastToSubOverflow()->SetCc(cc);
339                 break;
340             case Opcode::IfImm:
341                 inst->CastToIfImm()->SetCc(cc);
342                 break;
343             case Opcode::Select:
344                 inst->CastToSelect()->SetCc(cc);
345                 break;
346             case Opcode::SelectImm:
347                 inst->CastToSelectImm()->SetCc(cc);
348                 break;
349             case Opcode::DeoptimizeCompare:
350                 inst->CastToDeoptimizeCompare()->SetCc(cc);
351                 break;
352             case Opcode::DeoptimizeCompareImm:
353                 inst->CastToDeoptimizeCompareImm()->SetCc(cc);
354                 break;
355             default:
356                 UNREACHABLE();
357         }
358         return *this;
359     }
360 
SetFlag(compiler::inst_flags::Flags flag)361     IrConstructor &SetFlag(compiler::inst_flags::Flags flag)
362     {
363         CurrentInst()->SetFlag(flag);
364         return *this;
365     }
366 
ClearFlag(compiler::inst_flags::Flags flag)367     IrConstructor &ClearFlag(compiler::inst_flags::Flags flag)
368     {
369         CurrentInst()->ClearFlag(flag);
370         return *this;
371     }
372 
Inlined()373     IrConstructor &Inlined()
374     {
375         auto inst = CurrentInst();
376         if (inst->GetOpcode() == Opcode::Intrinsic) {
377             inst->CastToIntrinsic()->SetInlined(true);
378             return *this;
379         }
380         ASSERT(inst->GetOpcode() == Opcode::CallStatic || inst->GetOpcode() == Opcode::CallVirtual);
381         static_cast<CallInst *>(inst)->SetInlined(true);
382         inst->SetFlag(inst_flags::NO_DST);
383         return *this;
384     }
385 
Scale(uint64_t scale)386     IrConstructor &Scale(uint64_t scale)
387     {
388         auto inst = CurrentInst();
389         switch (inst->GetOpcode()) {
390             case Opcode::Load:
391                 inst->CastToLoad()->SetScale(scale);
392                 break;
393             case Opcode::Store:
394                 inst->CastToStore()->SetScale(scale);
395                 break;
396             default:
397                 UNREACHABLE();
398         }
399         return *this;
400     }
401 
Imm(uint64_t imm)402     IrConstructor &Imm(uint64_t imm)
403     {
404         auto inst = CurrentInst();
405         switch (inst->GetOpcode()) {
406             case Opcode::AddI:
407             case Opcode::SubI:
408             case Opcode::MulI:
409             case Opcode::DivI:
410             case Opcode::ModI:
411             case Opcode::ShlI:
412             case Opcode::ShrI:
413             case Opcode::AShrI:
414             case Opcode::AndI:
415             case Opcode::OrI:
416             case Opcode::XorI:
417                 static_cast<BinaryImmOperation *>(inst)->SetImm(imm);
418                 break;
419             case Opcode::BoundsCheckI:
420                 inst->CastToBoundsCheckI()->SetImm(imm);
421                 break;
422             case Opcode::LoadArrayI:
423                 inst->CastToLoadArrayI()->SetImm(imm);
424                 break;
425             case Opcode::StoreArrayI:
426                 inst->CastToStoreArrayI()->SetImm(imm);
427                 break;
428             case Opcode::LoadI:
429                 inst->CastToLoadI()->SetImm(imm);
430                 break;
431             case Opcode::StoreI:
432                 inst->CastToStoreI()->SetImm(imm);
433                 break;
434             case Opcode::ReturnI:
435                 inst->CastToReturnI()->SetImm(imm);
436                 break;
437             case Opcode::IfImm:
438                 inst->CastToIfImm()->SetImm(imm);
439                 break;
440             case Opcode::SelectImm:
441                 inst->CastToSelectImm()->SetImm(imm);
442                 break;
443             case Opcode::LoadArrayPairI:
444                 inst->CastToLoadArrayPairI()->SetImm(imm);
445                 break;
446             case Opcode::StoreArrayPairI:
447                 inst->CastToStoreArrayPairI()->SetImm(imm);
448                 break;
449             case Opcode::LoadPairPart:
450                 inst->CastToLoadPairPart()->SetImm(imm);
451                 break;
452             case Opcode::DeoptimizeCompareImm:
453                 inst->CastToDeoptimizeCompareImm()->SetImm(imm);
454                 break;
455             default:
456                 UNREACHABLE();
457         }
458         return *this;
459     }
460 
Shift(ShiftType shift_type,uint64_t imm)461     IrConstructor &Shift(ShiftType shift_type, uint64_t imm)
462     {
463         auto inst = CurrentInst();
464         switch (inst->GetOpcode()) {
465             case Opcode::AndSR:
466             case Opcode::OrSR:
467             case Opcode::XorSR:
468             case Opcode::AndNotSR:
469             case Opcode::OrNotSR:
470             case Opcode::XorNotSR:
471             case Opcode::AddSR:
472             case Opcode::SubSR:
473                 static_cast<BinaryShiftedRegisterOperation *>(inst)->SetShiftType(shift_type);
474                 static_cast<BinaryShiftedRegisterOperation *>(inst)->SetImm(imm);
475                 break;
476             case Opcode::NegSR:
477                 static_cast<UnaryShiftedRegisterOperation *>(inst)->SetShiftType(shift_type);
478                 static_cast<UnaryShiftedRegisterOperation *>(inst)->SetImm(imm);
479                 break;
480             default:
481                 UNREACHABLE();
482         }
483         return *this;
484     };
485 
Exit()486     IrConstructor &Exit()
487     {
488         CurrentInst()->CastToMonitor()->SetExit();
489         return *this;
490     }
491 
Entry()492     IrConstructor &Entry()
493     {
494         CurrentInst()->CastToMonitor()->SetEntry();
495         return *this;
496     }
497 
Fcmpg(bool fcmpg)498     IrConstructor &Fcmpg(bool fcmpg)
499     {
500         CurrentInst()->CastToCmp()->SetFcmpg(fcmpg);
501         return *this;
502     }
503 
u8()504     IrConstructor &u8()  // NOLINT(readability-identifier-naming)
505     {
506         CurrentInst()->SetType(DataType::UINT8);
507         return *this;
508     }
u16()509     IrConstructor &u16()  // NOLINT(readability-identifier-naming)
510     {
511         CurrentInst()->SetType(DataType::UINT16);
512         return *this;
513     }
u32()514     IrConstructor &u32()  // NOLINT(readability-identifier-naming)
515     {
516         CurrentInst()->SetType(DataType::UINT32);
517         return *this;
518     }
u64()519     IrConstructor &u64()  // NOLINT(readability-identifier-naming)
520     {
521         CurrentInst()->SetType(DataType::UINT64);
522         return *this;
523     }
s8()524     IrConstructor &s8()  // NOLINT(readability-identifier-naming)
525     {
526         CurrentInst()->SetType(DataType::INT8);
527         return *this;
528     }
s16()529     IrConstructor &s16()  // NOLINT(readability-identifier-naming)
530     {
531         CurrentInst()->SetType(DataType::INT16);
532         return *this;
533     }
s32()534     IrConstructor &s32()  // NOLINT(readability-identifier-naming)
535     {
536         CurrentInst()->SetType(DataType::INT32);
537         return *this;
538     }
s64()539     IrConstructor &s64()  // NOLINT(readability-identifier-naming)
540     {
541         CurrentInst()->SetType(DataType::INT64);
542         return *this;
543     }
i8()544     IrConstructor &i8()  // NOLINT(readability-identifier-naming)
545     {
546         return s8();
547     }
i16()548     IrConstructor &i16()  // NOLINT(readability-identifier-naming)
549     {
550         return s16();
551     }
i32()552     IrConstructor &i32()  // NOLINT(readability-identifier-naming)
553     {
554         return s32();
555     }
i64()556     IrConstructor &i64()  // NOLINT(readability-identifier-naming)
557     {
558         return s64();
559     }
b()560     IrConstructor &b()  // NOLINT(readability-identifier-naming)
561     {
562         CurrentInst()->SetType(DataType::BOOL);
563         return *this;
564     }
ref()565     IrConstructor &ref()  // NOLINT(readability-identifier-naming)
566     {
567         CurrentInst()->SetType(DataType::REFERENCE);
568         return *this;
569     }
ptr()570     IrConstructor &ptr()  // NOLINT(readability-identifier-naming)
571     {
572         CurrentInst()->SetType(DataType::POINTER);
573         return *this;
574     }
w()575     IrConstructor &w()  // NOLINT(readability-identifier-naming)
576     {
577         return ptr();
578     }
579     // Type representing MarkWord
mw()580     IrConstructor &mw()  // NOLINT(readability-identifier-naming)
581     {
582         return type(MARK_WORD_TYPE);
583     }
f32()584     IrConstructor &f32()  // NOLINT(readability-identifier-naming)
585     {
586         CurrentInst()->SetType(DataType::FLOAT32);
587         return *this;
588     }
f64()589     IrConstructor &f64()  // NOLINT(readability-identifier-naming)
590     {
591         CurrentInst()->SetType(DataType::FLOAT64);
592         return *this;
593     }
any()594     IrConstructor &any()  // NOLINT(readability-identifier-naming)
595     {
596         CurrentInst()->SetType(DataType::ANY);
597         return *this;
598     }
SetType(DataType::Type type)599     IrConstructor &SetType(DataType::Type type)  // NOLINT(readability-identifier-naming)
600     {
601         CurrentInst()->SetType(type);
602         return *this;
603     }
AnyType(AnyBaseType any_type)604     IrConstructor &AnyType(AnyBaseType any_type)
605     {
606         auto *atm = static_cast<AnyTypeMixin<FixedInputsInst1> *>(CurrentInst());
607         atm->SetAnyType(any_type);
608         return *this;
609     }
v0id()610     IrConstructor &v0id()  // NOLINT(readability-identifier-naming)
611     {
612         CurrentInst()->SetType(DataType::VOID);
613         return *this;
614     }
type(DataType::Type type)615     IrConstructor &type(DataType::Type type)  // NOLINT(readability-identifier-naming)
616     {
617         CurrentInst()->SetType(type);
618         return *this;
619     }
620 
Terminator()621     IrConstructor &Terminator()
622     {
623         CurrentInst()->SetFlag(inst_flags::TERMINATOR);
624         return *this;
625     }
626 
AddImm(uint32_t imm)627     IrConstructor &AddImm(uint32_t imm)
628     {
629         CurrentInst()->CastToIntrinsic()->AddImm(graph_->GetAllocator(), imm);
630         return *this;
631     }
632 
DstReg(uint8_t reg)633     IrConstructor &DstReg(uint8_t reg)
634     {
635         CurrentInst()->SetDstReg(reg);
636         if (DataType::IsFloatType(CurrentInst()->GetType())) {
637             graph_->SetUsedReg<DataType::FLOAT64>(reg);
638         }
639         return *this;
640     }
641 
SrcReg(uint8_t id,uint8_t reg)642     IrConstructor &SrcReg(uint8_t id, uint8_t reg)
643     {
644         CurrentInst()->SetSrcReg(id, reg);
645         if (DataType::IsFloatType(CurrentInst()->GetType())) {
646             graph_->SetUsedReg<DataType::FLOAT64>(reg);
647         }
648         graph_->SetUsedReg<DataType::INT64>(reg);
649         return *this;
650     }
651 
TypeId(uint32_t type_id)652     IrConstructor &TypeId(uint32_t type_id)
653     {
654         auto inst = CurrentInst();
655         switch (inst->GetOpcode()) {
656             case Opcode::Call:
657                 inst->CastToCall()->SetCallMethodId(type_id);
658                 break;
659             case Opcode::LoadString:
660                 inst->CastToLoadString()->SetTypeId(type_id);
661                 break;
662             case Opcode::LoadType:
663                 inst->CastToLoadType()->SetTypeId(type_id);
664                 break;
665             case Opcode::UnresolvedLoadType:
666                 inst->CastToUnresolvedLoadType()->SetTypeId(type_id);
667                 break;
668             case Opcode::StoreStatic:
669                 inst->CastToStoreStatic()->SetTypeId(type_id);
670                 break;
671             case Opcode::UnresolvedStoreStatic:
672                 inst->CastToUnresolvedStoreStatic()->SetTypeId(type_id);
673                 break;
674             case Opcode::LoadStatic:
675                 inst->CastToLoadStatic()->SetTypeId(type_id);
676                 break;
677             case Opcode::UnresolvedLoadStatic:
678                 inst->CastToUnresolvedLoadStatic()->SetTypeId(type_id);
679                 break;
680             case Opcode::LoadObject:
681                 inst->CastToLoadObject()->SetTypeId(type_id);
682                 break;
683             case Opcode::UnresolvedLoadObject:
684                 inst->CastToUnresolvedLoadObject()->SetTypeId(type_id);
685                 break;
686             case Opcode::StoreObject:
687                 inst->CastToStoreObject()->SetTypeId(type_id);
688                 break;
689             case Opcode::UnresolvedStoreObject:
690                 inst->CastToUnresolvedStoreObject()->SetTypeId(type_id);
691                 break;
692             case Opcode::NewObject:
693                 inst->CastToNewObject()->SetTypeId(type_id);
694                 break;
695             case Opcode::InitObject:
696                 inst->CastToInitObject()->SetCallMethodId(type_id);
697                 break;
698             case Opcode::NewArray:
699                 inst->CastToNewArray()->SetTypeId(type_id);
700                 break;
701             case Opcode::CheckCast:
702                 inst->CastToCheckCast()->SetTypeId(type_id);
703                 break;
704             case Opcode::IsInstance:
705                 inst->CastToIsInstance()->SetTypeId(type_id);
706                 break;
707             case Opcode::InitClass:
708                 inst->CastToInitClass()->SetTypeId(type_id);
709                 break;
710             case Opcode::LoadClass:
711                 inst->CastToLoadClass()->SetTypeId(type_id);
712                 break;
713             case Opcode::LoadAndInitClass:
714                 inst->CastToLoadAndInitClass()->SetTypeId(type_id);
715                 break;
716             case Opcode::UnresolvedLoadAndInitClass:
717                 inst->CastToUnresolvedLoadAndInitClass()->SetTypeId(type_id);
718                 break;
719             default:
720                 UNREACHABLE();
721         }
722         return *this;
723     }
724 
ObjField(RuntimeInterface::FieldPtr field)725     IrConstructor &ObjField(RuntimeInterface::FieldPtr field)
726     {
727         auto inst = CurrentInst();
728         switch (inst->GetOpcode()) {
729             case Opcode::StoreStatic:
730                 inst->CastToStoreStatic()->SetObjField(field);
731                 break;
732             case Opcode::LoadStatic:
733                 inst->CastToLoadStatic()->SetObjField(field);
734                 break;
735             case Opcode::LoadObject:
736                 inst->CastToLoadObject()->SetObjField(field);
737                 break;
738             case Opcode::StoreObject:
739                 inst->CastToStoreObject()->SetObjField(field);
740                 break;
741             default:
742                 UNREACHABLE();
743         }
744         return *this;
745     }
746 
SetNeedBarrier(bool need_barrier)747     IrConstructor &SetNeedBarrier(bool need_barrier)
748     {
749         auto inst = CurrentInst();
750         switch (inst->GetOpcode()) {
751             case Opcode::Store:
752                 inst->CastToStore()->SetNeedBarrier(need_barrier);
753                 break;
754             case Opcode::StoreI:
755                 inst->CastToStoreI()->SetNeedBarrier(need_barrier);
756                 break;
757             case Opcode::StoreObject:
758                 inst->CastToStoreObject()->SetNeedBarrier(need_barrier);
759                 break;
760             case Opcode::StoreArray:
761                 inst->CastToStoreArray()->SetNeedBarrier(need_barrier);
762                 break;
763             case Opcode::StoreArrayI:
764                 inst->CastToStoreArrayI()->SetNeedBarrier(need_barrier);
765                 break;
766             case Opcode::StoreArrayPair:
767                 inst->CastToStoreArrayPair()->SetNeedBarrier(need_barrier);
768                 break;
769             case Opcode::StoreArrayPairI:
770                 inst->CastToStoreArrayPairI()->SetNeedBarrier(need_barrier);
771                 break;
772             default:
773                 UNREACHABLE();
774         }
775         return *this;
776     }
777 
SrcVregs(std::vector<int> && vregs)778     IrConstructor &SrcVregs(std::vector<int> &&vregs)
779     {
780         ASSERT(CurrentInst()->IsSaveState());
781         if (!vregs.empty()) {
782             graph_->SetVRegsCount(
783                 std::max<size_t>(graph_->GetVRegsCount(), *std::max_element(vregs.begin(), vregs.end())));
784         }
785         if (save_state_inst_vregs_map_.count(CurrentInstIndex()) == 0) {
786             save_state_inst_vregs_map_.emplace(CurrentInstIndex(), std::move(vregs));
787         }
788         return *this;
789     }
790 
NoVregs()791     IrConstructor &NoVregs()
792     {
793         ASSERT(CurrentInst()->IsSaveState());
794         return *this;
795     }
796 
CatchTypeIds(std::vector<uint16_t> && ids)797     IrConstructor &CatchTypeIds(std::vector<uint16_t> &&ids)
798     {
799         auto inst = CurrentInst();
800         ASSERT(inst->GetOpcode() == Opcode::Try);
801         auto try_inst = inst->CastToTry();
802         for (auto id : ids) {
803             try_inst->AppendCatchTypeId(id, 0);
804         }
805         return *this;
806     }
807 
ThrowableInsts(std::vector<int> && ids)808     IrConstructor &ThrowableInsts(std::vector<int> &&ids)
809     {
810         auto inst = CurrentInst();
811         ASSERT(inst->GetOpcode() == Opcode::CatchPhi);
812         auto catch_phi = inst->CastToCatchPhi();
813         for (auto id : ids) {
814             ASSERT(inst_map_.count(id) > 0);
815             catch_phi->AppendThrowableInst(inst_map_.at(id));
816         }
817         return *this;
818     }
819 
DeoptimizeType(DeoptimizeType type)820     IrConstructor &DeoptimizeType(DeoptimizeType type)
821     {
822         auto inst = CurrentInst();
823         if (inst->GetOpcode() == Opcode::Deoptimize) {
824             inst->CastToDeoptimize()->SetDeoptimizeType(type);
825         } else {
826             ASSERT(inst->GetOpcode() == Opcode::DeoptimizeIf);
827             inst->CastToDeoptimizeIf()->SetDeoptimizeType(type);
828         }
829         return *this;
830     }
831 
SrcType(DataType::Type type)832     IrConstructor &SrcType(DataType::Type type)
833     {
834         auto inst = CurrentInst();
835         switch (inst->GetOpcode()) {
836             case Opcode::Cmp:
837                 inst->CastToCmp()->SetOperandsType(type);
838                 break;
839             case Opcode::Compare:
840                 inst->CastToCompare()->SetOperandsType(type);
841                 break;
842             case Opcode::If:
843                 inst->CastToIf()->SetOperandsType(type);
844                 break;
845             case Opcode::AddOverflow:
846                 inst->CastToAddOverflow()->SetOperandsType(type);
847                 break;
848             case Opcode::SubOverflow:
849                 inst->CastToSubOverflow()->SetOperandsType(type);
850                 break;
851             case Opcode::IfImm:
852                 inst->CastToIfImm()->SetOperandsType(type);
853                 break;
854             case Opcode::Select:
855                 inst->CastToSelect()->SetOperandsType(type);
856                 break;
857             case Opcode::SelectImm:
858                 inst->CastToSelectImm()->SetOperandsType(type);
859                 break;
860             case Opcode::Cast:
861                 inst->CastToCast()->SetOperandsType(type);
862                 break;
863             default:
864                 UNREACHABLE();
865         }
866         return *this;
867     }
868 
IntrinsicId(RuntimeInterface::IntrinsicId id)869     IrConstructor &IntrinsicId(RuntimeInterface::IntrinsicId id)
870     {
871         auto inst = CurrentInst();
872         ASSERT(inst->IsIntrinsic());
873         inst->CastToIntrinsic()->SetIntrinsicId(id);
874         AdjustFlags(id, inst);
875         return *this;
876     }
877 
Relocate()878     IrConstructor &Relocate()
879     {
880         auto inst = CurrentInst();
881         ASSERT(inst->IsIntrinsic());
882         inst->CastToIntrinsic()->SetRelocate();
883         return *this;
884     }
885 
Class(RuntimeInterface::ClassPtr klass)886     IrConstructor &Class(RuntimeInterface::ClassPtr klass)
887     {
888         auto inst = CurrentInst();
889         switch (inst->GetOpcode()) {
890             case Opcode::InitClass:
891                 inst->CastToInitClass()->SetClass(klass);
892                 break;
893             case Opcode::LoadClass:
894                 inst->CastToLoadClass()->SetClass(klass);
895                 break;
896             case Opcode::LoadAndInitClass:
897                 inst->CastToLoadAndInitClass()->SetClass(klass);
898                 break;
899             case Opcode::UnresolvedLoadAndInitClass:
900                 inst->CastToUnresolvedLoadAndInitClass()->SetClass(klass);
901                 break;
902             default:
903                 UNREACHABLE();
904         }
905         return *this;
906     }
907 
908     template <typename T>
ScopedLife()909     std::shared_ptr<IrConstructor> ScopedLife()
910     {
911 #ifndef __clang_analyzer__
912         if constexpr (std::is_same_v<T, BasicBlock>) {
913             return std::shared_ptr<IrConstructor>(this, [](IrConstructor *b) { b->ResetCurrentBb(); });
914         } else if constexpr (std::is_same_v<T, Inst>) {
915             return std::shared_ptr<IrConstructor>(this, [](IrConstructor *b) { b->ResetCurrentInst(); });
916         } else if constexpr (std::is_same_v<T, Graph>) {
917             return std::shared_ptr<IrConstructor>(this, [](IrConstructor *b) { b->Finalize(); });
918         }
919 #else
920         return nullptr;
921 #endif
922     }
923 
CheckInputType(Inst * inst,Inst * input_inst,size_t input_idx)924     void CheckInputType(Inst *inst, Inst *input_inst, size_t input_idx)
925     {
926         auto type = input_inst->GetType();
927         auto prev_type = inst->GetInputType(input_idx);
928         if (prev_type == DataType::Type::NO_TYPE) {
929             switch (inst->GetOpcode()) {
930                 case Opcode::Cmp:
931                     inst->CastToCmp()->SetOperandsType(type);
932                     break;
933                 case Opcode::Compare:
934                     inst->CastToCompare()->SetOperandsType(type);
935                     break;
936                 case Opcode::If:
937                     inst->CastToIf()->SetOperandsType(type);
938                     break;
939                 case Opcode::IfImm:
940                     inst->CastToIfImm()->SetOperandsType(type);
941                     break;
942                 case Opcode::Select:
943                     inst->CastToSelect()->SetOperandsType(type);
944                     break;
945                 case Opcode::SelectImm:
946                     inst->CastToSelectImm()->SetOperandsType(type);
947                     break;
948                 default:
949                     UNREACHABLE();
950             }
951         } else {
952             CHECK_EQ(type, prev_type);
953         }
954     }
955 
ConstructControlFlow()956     void ConstructControlFlow()
957     {
958         for (auto [bbi, succs] : bb_succs_map_) {
959             auto bb = bb_map_.at(bbi);
960             for (auto succ : succs) {
961                 bb->AddSucc(bb_map_.at(succ == -1 ? 1 : succ));
962             }
963             if (succs.size() > 1 && bb->IsEmpty()) {
964                 bb->SetTryEnd(true);
965             }
966         }
967         auto end_block = graph_->GetEndBlock();
968         if (end_block->GetPredsBlocks().empty()) {
969             graph_->EraseBlock(end_block);
970             graph_->SetEndBlock(nullptr);
971         }
972     }
973 
ConstructDataFlow()974     void ConstructDataFlow()
975     {
976         for (auto [insti, inputs] : inst_inputs_map_) {
977             auto inst = inst_map_.at(insti);
978             const auto &vregs {inst->IsSaveState() ? save_state_inst_vregs_map_[insti] : std::vector<int> {}};
979             ASSERT(!inst->IsSaveState() || inputs.size() == vregs.size());
980             size_t idx = 0;
981             if (inst->IsOperandsDynamic()) {
982                 inst->ReserveInputs(inputs.size());
983             }
984             for (auto input_idx : inputs) {
985                 ASSERT_DO(inst_map_.find(input_idx) != inst_map_.end(),
986                           std::cerr << "Input with Id " << input_idx << " isn't found, inst: " << *inst << std::endl);
987                 auto input_inst = inst_map_.at(input_idx);
988                 auto op = inst->GetOpcode();
989                 if (!input_inst->IsConst() &&
990                     (op == Opcode::Cmp || op == Opcode::Compare || op == Opcode::If || op == Opcode::IfImm ||
991                      op == Opcode::Select || op == Opcode::SelectImm)) {
992                     CheckInputType(inst, input_inst, idx);
993                 }
994                 if (inst->IsOperandsDynamic()) {
995                     inst->AppendInput(input_inst);
996                     if (inst->IsSaveState()) {
997                         static_cast<SaveStateInst *>(inst)->SetVirtualRegister(idx, VirtualRegister(vregs[idx], false));
998                     }
999                 } else {
1000                     inst->SetInput(idx, input_inst);
1001                 }
1002                 ++idx;
1003             }
1004         }
1005 
1006         for (auto [insti, inputs] : phi_inst_inputs_map_) {
1007             auto inst = inst_map_.at(insti);
1008             for (auto input : inputs) {
1009                 auto input_inst = inst_map_.at(input.second);
1010                 size_t idx = inst->GetBasicBlock()->GetPredBlockIndex(bb_map_.at(input.first));
1011                 auto i {inst->AppendInput(input_inst)};
1012                 inst->CastToPhi()->SetPhiInputBbNum(i, idx);
1013             }
1014         }
1015     }
1016 
UpdateSpecialFlags()1017     void UpdateSpecialFlags()
1018     {
1019         int max_id = graph_->GetCurrentInstructionId();
1020         for (auto pair : inst_map_) {
1021             auto id = pair.first;
1022             auto inst = pair.second;
1023             if (inst->GetType() == DataType::REFERENCE) {
1024                 if (inst->GetOpcode() == Opcode::StoreArray) {
1025                     inst->CastToStoreArray()->SetNeedBarrier(true);
1026                 }
1027                 if (inst->GetOpcode() == Opcode::StoreArrayI) {
1028                     inst->CastToStoreArrayI()->SetNeedBarrier(true);
1029                 }
1030                 if (inst->GetOpcode() == Opcode::StoreStatic) {
1031                     inst->CastToStoreStatic()->SetNeedBarrier(true);
1032                 }
1033                 if (inst->GetOpcode() == Opcode::UnresolvedStoreStatic) {
1034                     inst->CastToUnresolvedStoreStatic()->SetNeedBarrier(true);
1035                 }
1036                 if (inst->GetOpcode() == Opcode::StoreObject) {
1037                     inst->CastToStoreObject()->SetNeedBarrier(true);
1038                 }
1039                 if (inst->GetOpcode() == Opcode::UnresolvedStoreObject) {
1040                     inst->CastToUnresolvedStoreObject()->SetNeedBarrier(true);
1041                 }
1042                 if (inst->GetOpcode() == Opcode::StoreArrayPair) {
1043                     inst->CastToStoreArrayPair()->SetNeedBarrier(true);
1044                 }
1045                 if (inst->GetOpcode() == Opcode::StoreArrayPairI) {
1046                     inst->CastToStoreArrayPairI()->SetNeedBarrier(true);
1047                 }
1048             }
1049             if (inst->GetOpcode() == Opcode::Try) {
1050                 auto bb = inst->GetBasicBlock();
1051                 bb->SetTryBegin(true);
1052                 bb->GetSuccessor(0)->SetTry(true);
1053                 for (size_t idx = 1; idx < bb->GetSuccsBlocks().size(); idx++) {
1054                     bb->GetSuccessor(idx)->SetCatchBegin(true);
1055                 }
1056             }
1057             if (inst->GetOpcode() == Opcode::SaveStateOsr) {
1058                 inst->GetBasicBlock()->SetOsrEntry(true);
1059             }
1060             if (id >= max_id) {
1061                 max_id = id + 1;
1062             }
1063         }
1064         graph_->SetCurrentInstructionId(max_id);
1065     }
1066 
1067     // Create SaveState instructions thet weren't explicitly constructed in the test
CreateSaveStates()1068     void CreateSaveStates()
1069     {
1070         for (auto [insti, inputs] : inst_inputs_map_) {
1071             auto inst = inst_map_.at(insti);
1072             if (!inst->IsOperandsDynamic() && inst->RequireState() && inst->GetInputsCount() > inputs.size()) {
1073                 auto save_state = graph_->CreateInstSaveState();
1074                 save_state->SetId(static_cast<int>(graph_->GetCurrentInstructionId()) + 1);
1075                 graph_->SetCurrentInstructionId(save_state->GetId() + 1);
1076                 inst->GetBasicBlock()->InsertBefore(save_state, inst);
1077                 inst->SetSaveState(save_state);
1078             }
1079         }
1080     }
1081 
SetSpillFillData()1082     void SetSpillFillData()
1083     {
1084         graph_->ResetParameterInfo();
1085         // Count number of parameters (needed for bytecode optimizer) in first cycle and set SpillFillData for each
1086         // parameter in second cycle
1087         uint32_t num_args = 0;
1088         for (auto inst : graph_->GetStartBlock()->Insts()) {
1089             if (inst->GetOpcode() == Opcode::Parameter) {
1090                 ++num_args;
1091             }
1092         }
1093         uint32_t i = 0;
1094         for (auto inst : graph_->GetStartBlock()->Insts()) {
1095             if (inst->GetOpcode() != Opcode::Parameter) {
1096                 continue;
1097             }
1098             ++i;
1099 
1100             auto type = inst->GetType();
1101             InstBuilder::SetParamSpillFill(graph_, static_cast<ParameterInst *>(inst), num_args, i - 1, type);
1102         }
1103     }
1104 
Finalize()1105     void Finalize()
1106     {
1107         ConstructControlFlow();
1108         ConstructDataFlow();
1109         UpdateSpecialFlags();
1110         CreateSaveStates();
1111         SetSpillFillData();
1112         ResetCurrentBb();
1113         ResetCurrentInst();
1114         graph_->RunPass<LoopAnalyzer>();
1115         PropagateRegisters();
1116         if (enable_graph_checker_) {
1117             GraphChecker(graph_).Check();
1118         }
1119     }
1120 
GetInst(unsigned index)1121     Inst &GetInst(unsigned index)
1122     {
1123         return *inst_map_.at(index);
1124     }
1125 
GetBlock(unsigned index)1126     BasicBlock &GetBlock(unsigned index)
1127     {
1128         return *bb_map_.at(index);
1129     }
1130 
EnableGraphChecker(bool value)1131     void EnableGraphChecker(bool value)
1132     {
1133         enable_graph_checker_ = value;
1134     }
1135 
1136 private:
AddInput(int v)1137     void AddInput(int v)
1138     {
1139         inst_inputs_map_[CurrentInstIndex()].push_back(v);
1140     }
1141 
1142     template <typename T, typename... Args>
AddInput(T v,Args...args)1143     void AddInput(T v, Args... args)
1144     {
1145         inst_inputs_map_[CurrentInstIndex()].push_back(v);
1146         AddInput(args...);
1147     }
1148 
GetBbByIndex(int index)1149     BasicBlock *GetBbByIndex(int index)
1150     {
1151         return bb_map_.at(index);
1152     }
1153 
CurrentBb()1154     BasicBlock *CurrentBb()
1155     {
1156         return current_bb_.second;
1157     }
1158 
CurrentBbIndex()1159     int CurrentBbIndex()
1160     {
1161         return current_bb_.first;
1162     }
1163 
CurrentInst()1164     Inst *CurrentInst()
1165     {
1166         return current_inst_.second;
1167     }
1168 
CurrentInstIndex()1169     int CurrentInstIndex()
1170     {
1171         return current_inst_.first;
1172     }
1173 
ResetCurrentBb()1174     void ResetCurrentBb()
1175     {
1176         current_bb_ = {-1, nullptr};
1177     }
1178 
ResetCurrentInst()1179     void ResetCurrentInst()
1180     {
1181         current_inst_ = {-1, nullptr};
1182     }
1183 
PropagateRegisters()1184     void PropagateRegisters()
1185     {
1186         for (auto bb : graph_->GetBlocksRPO()) {
1187             for (auto inst : bb->AllInsts()) {
1188                 if (inst->GetDstReg() != INVALID_REG && !inst->IsOperandsDynamic()) {
1189                     for (size_t i = 0; i < inst->GetInputsCount(); i++) {
1190                         inst->SetSrcReg(i, inst->GetInputs()[i].GetInst()->GetDstReg());
1191                     }
1192                 }
1193             }
1194         }
1195     }
1196 
1197 private:
1198     Graph *graph_ {nullptr};
1199     ArenaAllocator aa_;
1200     std::pair<int, BasicBlock *> current_bb_;
1201     std::pair<int, Inst *> current_inst_;
1202     ArenaUnorderedMap<int, BasicBlock *> bb_map_ {aa_.Adapter()};
1203     ArenaVector<std::pair<int, std::vector<int>>> bb_succs_map_ {aa_.Adapter()};
1204     ArenaUnorderedMap<int, Inst *> inst_map_ {aa_.Adapter()};
1205     ArenaUnorderedMap<int, std::vector<int>> inst_inputs_map_ {aa_.Adapter()};
1206     ArenaUnorderedMap<int, std::vector<int>> save_state_inst_vregs_map_ {aa_.Adapter()};
1207     ArenaUnorderedMap<int, std::vector<std::pair<int, int>>> phi_inst_inputs_map_ {aa_.Adapter()};
1208     bool enable_graph_checker_ {true};
1209 };
1210 
1211 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
1212 #define GRAPH(GRAPH) if (auto __g = builder_->SetGraph(GRAPH).ScopedLife<Graph>(); true)
1213 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
1214 #define BASIC_BLOCK(ID, ...) \
1215     if (auto __b = builder_->NewBlock<ID>().Succs({__VA_ARGS__}).ScopedLife<BasicBlock>(); true)
1216 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
1217 #define INST(ID, ...) builder_->NewInst(ID, __VA_ARGS__)
1218 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
1219 #define CONSTANT(ID, VALUE) builder_->NewConstant(ID, VALUE)
1220 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
1221 #define PARAMETER(ID, ARG_NUM) builder_->NewParameter(ID, ARG_NUM)
1222 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
1223 #define INS(INDEX) builder_->GetInst(INDEX)
1224 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
1225 #define BB(INDEX) builder_->GetBlock(INDEX)
1226 }  // namespace panda::compiler
1227 
1228 #endif  // PANDA_IR_CONSTRUCTOR_H
1229