• 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 COMPILER_OPTIMIZER_IR_INST_H
17 #define COMPILER_OPTIMIZER_IR_INST_H
18 
19 #include <array>
20 #include <vector>
21 #include <iostream>
22 #include "constants.h"
23 #include "datatype.h"
24 #include "ir-dyn-base-types.h"
25 #include "marker.h"
26 #include "utils/arena_containers.h"
27 #include "utils/span.h"
28 #include "utils/bit_field.h"
29 #include "utils/bit_utils.h"
30 #include "utils/bit_vector.h"
31 #include "macros.h"
32 #include "mem/arena_allocator.h"
33 #include "opcodes.h"
34 #include "compiler_options.h"
35 #include "runtime_interface.h"
36 #include "spill_fill_data.h"
37 
38 namespace panda::compiler {
39 class Inst;
40 class BasicBlock;
41 class Graph;
42 class GraphVisitor;
43 class VnObject;
44 class SaveStateItem;
45 class LocationsInfo;
46 using InstVector = ArenaVector<Inst *>;
47 
48 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
49 #define INST_DEF(opcode, base, ...) class base;
50 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
51 OPCODE_LIST(INST_DEF)
52 #undef INST_DEF
53 
54 /*
55  * Condition code, used in Compare, If[Imm] and Select[Imm] instructions.
56  *
57  * N.B. BranchElimination and Peephole rely on the order of these codes. Change carefully.
58  */
59 enum ConditionCode {
60     // All types.
61     CC_EQ = 0,  // ==
62     CC_NE,      // !=
63     // Signed integers and floating-point numbers.
64     CC_LT,  // <
65     CC_LE,  // <=
66     CC_GT,  // >
67     CC_GE,  // >=
68     // Unsigned integers.
69     CC_B,   // <
70     CC_BE,  // <=
71     CC_A,   // >
72     CC_AE,  // >=
73     // Compare result of bitwise AND with zero
74     CC_TST_EQ,  // (lhs AND rhs) == 0
75     CC_TST_NE,  // (lhs AND rhs) != 0
76     // First and last aliases.
77     CC_FIRST = CC_EQ,
78     CC_LAST = CC_TST_NE,
79 };
80 
GetInverseConditionCode(ConditionCode code)81 inline ConditionCode GetInverseConditionCode(ConditionCode code)
82 {
83     switch (code) {
84         case ConditionCode::CC_EQ:
85             return ConditionCode::CC_NE;
86         case ConditionCode::CC_NE:
87             return ConditionCode::CC_EQ;
88         default:
89             UNREACHABLE();
90     }
91 }
92 
InverseSignednessConditionCode(ConditionCode code)93 inline ConditionCode InverseSignednessConditionCode(ConditionCode code)
94 {
95     switch (code) {
96         case ConditionCode::CC_EQ:
97             return ConditionCode::CC_EQ;
98         case ConditionCode::CC_NE:
99             return ConditionCode::CC_NE;
100 
101         case ConditionCode::CC_LT:
102             return ConditionCode::CC_B;
103         case ConditionCode::CC_LE:
104             return ConditionCode::CC_BE;
105         case ConditionCode::CC_GT:
106             return ConditionCode::CC_A;
107         case ConditionCode::CC_GE:
108             return ConditionCode::CC_AE;
109 
110         case ConditionCode::CC_B:
111             return ConditionCode::CC_LT;
112         case ConditionCode::CC_BE:
113             return ConditionCode::CC_LE;
114         case ConditionCode::CC_A:
115             return ConditionCode::CC_GT;
116         case ConditionCode::CC_AE:
117             return ConditionCode::CC_GE;
118 
119         case ConditionCode::CC_TST_EQ:
120             return ConditionCode::CC_TST_EQ;
121         case ConditionCode::CC_TST_NE:
122             return ConditionCode::CC_TST_NE;
123 
124         default:
125             UNREACHABLE();
126     }
127 }
128 
IsSignedConditionCode(ConditionCode code)129 inline bool IsSignedConditionCode(ConditionCode code)
130 {
131     switch (code) {
132         case ConditionCode::CC_LT:
133         case ConditionCode::CC_LE:
134         case ConditionCode::CC_GT:
135         case ConditionCode::CC_GE:
136             return true;
137 
138         case ConditionCode::CC_EQ:
139         case ConditionCode::CC_NE:
140         case ConditionCode::CC_B:
141         case ConditionCode::CC_BE:
142         case ConditionCode::CC_A:
143         case ConditionCode::CC_AE:
144         case ConditionCode::CC_TST_EQ:
145         case ConditionCode::CC_TST_NE:
146             return false;
147 
148         default:
149             UNREACHABLE();
150     }
151 }
152 
SwapOperandsConditionCode(ConditionCode code)153 inline ConditionCode SwapOperandsConditionCode(ConditionCode code)
154 {
155     switch (code) {
156         case ConditionCode::CC_EQ:
157         case ConditionCode::CC_NE:
158             return code;
159         default:
160             UNREACHABLE();
161     }
162 }
163 
164 enum class Opcode {
165     INVALID = -1,
166 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
167 #define INST_DEF(opcode, ...) opcode,
168     OPCODE_LIST(INST_DEF)
169 
170 #undef INST_DEF
171         NUM_OPCODES
172 };
173 
174 /**
175  * Convert opcode to its string representation
176  */
177 constexpr std::array<const char *const, static_cast<size_t>(Opcode::NUM_OPCODES)> OPCODE_NAMES = {
178 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
179 #define INST_DEF(opcode, ...) #opcode,
180     OPCODE_LIST(INST_DEF)
181 #undef INST_DEF
182 };
183 
GetOpcodeString(Opcode opc)184 constexpr const char *GetOpcodeString(Opcode opc)
185 {
186     ASSERT(static_cast<int>(opc) < static_cast<int>(Opcode::NUM_OPCODES));
187     return OPCODE_NAMES[static_cast<int>(opc)];
188 }
189 
190 /**
191  * Instruction flags. See `instrutions.yaml` section `flags` for more information.
192  */
193 namespace inst_flags {
194 namespace internal {
195 enum FlagsIndex {
196 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
197 #define FLAG_DEF(flag) flag##_INDEX,
198     FLAGS_LIST(FLAG_DEF)
199 #undef FLAG_DEF
200         FLAGS_COUNT
201 };
202 }  // namespace internal
203 
204 enum Flags : uint32_t {
205 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
206 #define FLAG_DEF(flag) flag = (1U << internal::flag##_INDEX),
207     FLAGS_LIST(FLAG_DEF)
208 #undef FLAG_DEF
209         FLAGS_COUNT = internal::FLAGS_COUNT,
210     NONE = 0
211 };
212 
GetFlagsMask(Opcode opcode)213 inline constexpr uintptr_t GetFlagsMask(Opcode opcode)
214 {
215 #define INST_DEF(OPCODE, BASE, FLAGS) FLAGS,  // NOLINT(cppcoreguidelines-macro-usage)
216     // NOLINTNEXTLINE(hicpp-signed-bitwise)
217     constexpr std::array<uintptr_t, static_cast<int>(Opcode::NUM_OPCODES)> INST_FLAGS_TABLE = {OPCODE_LIST(INST_DEF)};
218 #undef INST_DEF
219     return INST_FLAGS_TABLE[static_cast<size_t>(opcode)];
220 }
221 }  // namespace inst_flags
222 
223 #ifndef NDEBUG
224 namespace inst_modes {
225 namespace internal {
226 enum ModeIndex {
227 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
228 #define MODE_DEF(mode) mode##_INDEX,
229     MODES_LIST(MODE_DEF)
230 #undef MODE_DEF
231         MODES_COUNT
232 };
233 }  // namespace internal
234 
235 enum Mode : uint8_t {
236 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
237 #define MODE_DEF(mode) mode = (1U << internal::mode##_INDEX),
238     MODES_LIST(MODE_DEF)
239 #undef MODE_DEF
240         MODES_COUNT = internal::MODES_COUNT,
241 };
242 
GetModesMask(Opcode opcode)243 inline constexpr uint8_t GetModesMask(Opcode opcode)
244 {
245     // NOLINTNEXTLINE(hicpp-signed-bitwise)
246     constexpr std::array<uint8_t, static_cast<int>(Opcode::NUM_OPCODES)> INST_MODES_TABLE = {INST_MODES_LIST};
247     return INST_MODES_TABLE[static_cast<size_t>(opcode)];
248 }
249 }  // namespace inst_modes
250 #endif
251 
252 namespace internal {
253 inline constexpr std::array<const char *, ShiftType::INVALID_SHIFT + 1> SHIFT_TYPE_NAMES = {"LSL", "LSR", "ASR", "ROR",
254                                                                                             "INVALID"};
255 }  // namespace internal
256 
GetShiftTypeStr(ShiftType type)257 inline const char *GetShiftTypeStr(ShiftType type)
258 {
259     ASSERT(type <= INVALID_SHIFT);
260     return internal::SHIFT_TYPE_NAMES[type];
261 }
262 
263 /**
264  * Describes type of the object produced by an instruction.
265  */
266 class ObjectTypeInfo {
267 public:
268     using ClassType = RuntimeInterface::ClassPtr;
269 
270     ObjectTypeInfo() = default;
ObjectTypeInfo(ClassType v)271     explicit ObjectTypeInfo(ClassType v) : class_(v) {}
272 
273     // NOLINTNEXTLINE(google-explicit-constructor)
274     operator bool() const
275     {
276         return class_ != ClassType();
277     }
278 
GetClass()279     ClassType GetClass() const
280     {
281         return class_;
282     }
283 
IsValid()284     bool IsValid() const
285     {
286         return class_ != ClassType {};
287     }
288 
289 private:
290     ClassType class_ {};
291 };
292 
293 /**
294  * Class for storing panda bytecode's virtual register
295  */
296 class VirtualRegister final {
297 public:
298     using ValueType = uint16_t;
299     static constexpr unsigned BITS_FOR_VREG = (sizeof(ValueType) * BITS_PER_BYTE) - 1;
300     static constexpr ValueType INVALID = std::numeric_limits<ValueType>::max();
301 
302     VirtualRegister() = default;
VirtualRegister(uint16_t v,bool is_acc)303     explicit VirtualRegister(uint16_t v, bool is_acc) : value_(v)
304     {
305         IsAccFlag::Set(is_acc, &value_);
306     }
307 
uint16_t()308     explicit operator uint16_t() const
309     {
310         return value_;
311     }
312 
Value()313     uint16_t Value() const
314     {
315         return ValueField::Get(value_);
316     }
317 
IsAccumulator()318     bool IsAccumulator() const
319     {
320         return IsAccFlag::Get(value_);
321     }
322 
323 private:
324     uint16_t value_ {INVALID};
325 
326     using ValueField = BitField<unsigned, 0, BITS_FOR_VREG>;
327     using IsAccFlag = ValueField::NextFlag;
328 };
329 
330 // How many bits will be used in Inst's bit fields for number of inputs.
331 constexpr size_t BITS_PER_INPUTS_NUM = 3;
332 // Maximum number of static inputs
333 constexpr size_t MAX_STATIC_INPUTS = (1U << BITS_PER_INPUTS_NUM) - 1;
334 
335 /**
336  * Currently Input class is just a wrapper for the Inst class.
337  */
338 class Input final {
339 public:
340     Input() = default;
Input(Inst * inst)341     explicit Input(Inst *inst) : inst_(inst) {}
342 
GetInst()343     Inst *GetInst()
344     {
345         return inst_;
346     }
GetInst()347     const Inst *GetInst() const
348     {
349         return inst_;
350     }
351 
GetPadding(Arch arch,uint32_t inputs_count)352     static inline uint8_t GetPadding(Arch arch, uint32_t inputs_count)
353     {
354         return static_cast<uint8_t>(!Is64BitsArch(arch) && inputs_count % 2U == 1U);
355     }
356 
357 private:
358     Inst *inst_ {nullptr};
359 };
360 
361 /**
362  * User is a intrusive list node, thus it stores pointers to next and previous users.
363  * Also user has properties value to determine owner instruction and corresponding index of the input.
364  */
365 class User final {
366 public:
367     User() = default;
User(bool is_static,unsigned index,unsigned size)368     User(bool is_static, unsigned index, unsigned size)
369         : properties_(IsStaticFlag::Encode(is_static) | IndexField::Encode(index) | SizeField::Encode(size) |
370                       BbNumField::Encode(BbNumField::MaxValue()))
371     {
372         ASSERT(index < 1U << (BITS_FOR_INDEX - 1U));
373         ASSERT(size < 1U << (BITS_FOR_SIZE - 1U));
374     }
375     ~User() = default;
376 
377     // Copy/move semantic is disabled because we use tricky pointer arithmetic based on 'this' value
378     NO_COPY_SEMANTIC(User);
379     NO_MOVE_SEMANTIC(User);
380 
381     Inst *GetInst();
GetInst()382     const Inst *GetInst() const
383     {
384         return const_cast<User *>(this)->GetInst();
385     }
386 
387     Inst *GetInput();
388     const Inst *GetInput() const;
389 
IsDynamic()390     bool IsDynamic() const
391     {
392         return !IsStaticFlag::Decode(properties_);
393     }
GetIndex()394     unsigned GetIndex() const
395     {
396         return IndexField::Decode(properties_);
397     }
GetSize()398     unsigned GetSize() const
399     {
400         return SizeField::Decode(properties_);
401     }
402 
GetVirtualRegister()403     VirtualRegister GetVirtualRegister() const
404     {
405         ASSERT(IsDynamic());
406         return VirtualRegister(VregField::Decode(properties_), IsAccFlag::Decode(properties_));
407     }
408 
SetVirtualRegister(VirtualRegister reg)409     void SetVirtualRegister(VirtualRegister reg)
410     {
411         static_assert(sizeof(reg) <= sizeof(uintptr_t), "Consider passing the register by reference");
412         ASSERT(IsDynamic());
413         VregField::Set(reg.Value(), &properties_);
414         IsAccFlag::Set(reg.IsAccumulator(), &properties_);
415     }
416 
GetBbNum()417     uint32_t GetBbNum() const
418     {
419         ASSERT(IsDynamic());
420         return BbNumField::Decode(properties_);
421     }
422 
SetBbNum(uint32_t bb_num)423     void SetBbNum(uint32_t bb_num)
424     {
425         ASSERT(IsDynamic());
426         BbNumField::Set(bb_num, &properties_);
427     }
428 
GetNext()429     auto GetNext() const
430     {
431         return next_;
432     }
433 
GetPrev()434     auto GetPrev() const
435     {
436         return prev_;
437     }
438 
SetNext(User * next)439     void SetNext(User *next)
440     {
441         next_ = next;
442     }
443 
SetPrev(User * prev)444     void SetPrev(User *prev)
445     {
446         prev_ = prev;
447     }
448 
Remove()449     void Remove()
450     {
451         if (prev_ != nullptr) {
452             prev_->next_ = next_;
453         }
454         if (next_ != nullptr) {
455             next_->prev_ = prev_;
456         }
457     }
458 
459 private:
460     static constexpr unsigned BITS_FOR_INDEX = 21;
461     static constexpr unsigned BITS_FOR_SIZE = BITS_FOR_INDEX;
462     static constexpr unsigned BITS_FOR_BB_NUM = 20;
463     using IndexField = BitField<unsigned, 0, BITS_FOR_INDEX>;
464     using SizeField = IndexField::NextField<unsigned, BITS_FOR_SIZE>;
465     using IsStaticFlag = SizeField::NextFlag;
466 
467     using BbNumField = IsStaticFlag::NextField<uint32_t, BITS_FOR_BB_NUM>;
468 
469     using VregField = IsStaticFlag::NextField<unsigned, VirtualRegister::BITS_FOR_VREG>;
470     using IsAccFlag = VregField::NextFlag;
471 
472     uint64_t properties_ {0};
473     User *next_ {nullptr};
474     User *prev_ {nullptr};
475 };
476 
477 /**
478  * List of users. Intended for range loop.
479  * @tparam T should be User or const User
480  */
481 template <typename T>
482 class UserList {
483     template <typename U>
484     struct UserIterator {
485         UserIterator() = default;
UserIteratorUserIterator486         explicit UserIterator(U *u) : user_(u) {}
487 
488         UserIterator &operator++()
489         {
490             user_ = user_->GetNext();
491             return *this;
492         }
493         bool operator!=(const UserIterator &other)
494         {
495             return user_ != other.user_;
496         }
497         U &operator*()
498         {
499             return *user_;
500         }
501         U *operator->()
502         {
503             return user_;
504         }
505 
506     private:
507         U *user_ {nullptr};
508     };
509 
510 public:
511     using Iterator = UserIterator<T>;
512     using ConstIterator = UserIterator<const T>;
513     using PointerType = std::conditional_t<std::is_const_v<T>, T *const *, T **>;
514 
UserList(PointerType head)515     explicit UserList(PointerType head) : head_(head) {}
516 
517     // NOLINTNEXTLINE(readability-identifier-naming)
begin()518     Iterator begin()
519     {
520         return Iterator(*head_);
521     }
522     // NOLINTNEXTLINE(readability-identifier-naming)
end()523     Iterator end()
524     {
525         return Iterator(nullptr);
526     }
527     // NOLINTNEXTLINE(readability-identifier-naming)
begin()528     ConstIterator begin() const
529     {
530         return ConstIterator(*head_);
531     }
532     // NOLINTNEXTLINE(readability-identifier-naming)
end()533     ConstIterator end() const
534     {
535         return ConstIterator(nullptr);
536     }
Empty()537     bool Empty() const
538     {
539         return *head_ == nullptr;
540     }
Front()541     T &Front()
542     {
543         return **head_;
544     }
Front()545     const T &Front() const
546     {
547         return **head_;
548     }
549 
550 private:
551     PointerType head_ {nullptr};
552 };
553 
554 inline bool operator==(const User &lhs, const User &rhs)
555 {
556     return lhs.GetInst() == rhs.GetInst();
557 }
558 
559 /**
560  * Operands class for instructions with fixed inputs count.
561  * Actually, this class do absolutely nothing except that we can get sizeof of it when allocating memory.
562  */
563 template <int N>
564 struct Operands {
565     static_assert(N < MAX_STATIC_INPUTS, "Invalid inputs number");
566 
567     std::array<User, N> users;
568     std::array<Input, N> inputs;
569 };
570 
571 /**
572  * Specialized version for instructions with variable inputs count.
573  * Users and inputs are stored outside of this class.
574  */
575 class DynamicOperands {
576 public:
DynamicOperands(ArenaAllocator * allocator)577     explicit DynamicOperands(ArenaAllocator *allocator) : allocator_(allocator) {}
578 
Users()579     User *Users()
580     {
581         return users_;
582     }
583 
Inputs()584     Input *Inputs()
585     {
586         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
587         return reinterpret_cast<Input *>(users_ + capacity_) + 1;
588     }
589 
590     /// Append new input (and user accordingly)
591     unsigned Append(Inst *inst);
592 
593     /// Remove input and user with index `index`.
594     void Remove(unsigned index);
595 
596     /// Reallocate inputs/users storage to a new one with specified capacity.
597     void Reallocate(size_t new_capacity = 0);
598 
599     /// Get instruction to which these operands belongs to.
GetOwnerInst()600     Inst *GetOwnerInst() const
601     {
602         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
603         return reinterpret_cast<Inst *>(const_cast<DynamicOperands *>(this) + 1);
604     }
605 
GetUser(unsigned index)606     User *GetUser(unsigned index)
607     {
608         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
609         return &users_[capacity_ - index - 1];
610     }
611 
GetInput(unsigned index)612     Input *GetInput(unsigned index)
613     {
614         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
615         return &Inputs()[index];
616     }
617 
SetInput(unsigned index,Input input)618     void SetInput(unsigned index, Input input)
619     {
620         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
621         Inputs()[index] = input;
622     }
623 
Size()624     size_t Size() const
625     {
626         return size_;
627     }
628 
629 private:
630     User *users_ {nullptr};
631     size_t size_ {0};
632     size_t capacity_ {0};
633     ArenaAllocator *allocator_ {nullptr};
634 };
635 
636 /**
637  * Base class for all instructions, should not be instantiated directly
638  */
639 class InstBase {
640     NO_COPY_SEMANTIC(InstBase);
641     NO_MOVE_SEMANTIC(InstBase);
642 
643 public:
644     virtual ~InstBase() = default;
645 
646 public:
delete(void * unused,size_t size)647     ALWAYS_INLINE void operator delete([[maybe_unused]] void *unused, [[maybe_unused]] size_t size)
648     {
649         UNREACHABLE();
650     }
new(size_t size,void * ptr)651     ALWAYS_INLINE void *operator new([[maybe_unused]] size_t size, void *ptr) noexcept
652     {
653         return ptr;
654     }
delete(void * unused1,void * unused2)655     ALWAYS_INLINE void operator delete([[maybe_unused]] void *unused1, [[maybe_unused]] void *unused2) noexcept {}
656 
657     void *operator new([[maybe_unused]] size_t size) = delete;
658 
659 protected:
660     InstBase() = default;
661 };
662 
663 /**
664  * Base instruction class
665  */
666 class Inst : public MarkerSet, public InstBase {
667 public:
668     /**
669      * Create new instruction. All instructions must be created with this method.
670      * It allocates additional space before Inst object for def-use structures.
671      *
672      * @tparam InstType - concrete type of instruction, shall be derived from Inst
673      * @tparam Args - constructor arguments types
674      * @param allocator - allocator for memory allocating
675      * @param args - constructor arguments
676      * @return - new instruction
677      */
678     template <typename InstType, typename... Args>
679     [[nodiscard]] static InstType *New(ArenaAllocator *allocator, Args &&... args);
680 
681     // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
682 #define INST_DEF(opcode, base, ...) inline const base *CastTo##opcode() const;
683     OPCODE_LIST(INST_DEF)
684 #undef INST_DEF
685 
686     // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
687 #define INST_DEF(opcode, base, ...) inline base *CastTo##opcode();
OPCODE_LIST(INST_DEF)688     OPCODE_LIST(INST_DEF)
689 #undef INST_DEF
690 
691     // Methods for instruction chaining inside basic blocks.
692     Inst *GetNext()
693     {
694         return next_;
695     }
GetNext()696     const Inst *GetNext() const
697     {
698         return next_;
699     }
GetPrev()700     Inst *GetPrev()
701     {
702         return prev_;
703     }
GetPrev()704     const Inst *GetPrev() const
705     {
706         return prev_;
707     }
SetNext(Inst * next)708     void SetNext(Inst *next)
709     {
710         next_ = next;
711     }
SetPrev(Inst * prev)712     void SetPrev(Inst *prev)
713     {
714         prev_ = prev;
715     }
716 
717     // Id accessors
GetId()718     auto GetId() const
719     {
720         return id_;
721     }
SetId(int id)722     void SetId(int id)
723     {
724         id_ = id;
725     }
726 
GetLinearNumber()727     auto GetLinearNumber() const
728     {
729         return linear_number_;
730     }
SetLinearNumber(LinearNumber number)731     void SetLinearNumber(LinearNumber number)
732     {
733         linear_number_ = number;
734     }
735 
GetCloneNumber()736     auto GetCloneNumber() const
737     {
738         return clone_number_;
739     }
SetCloneNumber(int32_t number)740     void SetCloneNumber(int32_t number)
741     {
742         clone_number_ = number;
743     }
744 
745     // Opcode accessors
GetOpcode()746     Opcode GetOpcode() const
747     {
748         return opcode_;
749     }
SetOpcode(Opcode opcode)750     void SetOpcode(Opcode opcode)
751     {
752         opcode_ = opcode;
753         SetField<FieldFlags>(inst_flags::GetFlagsMask(opcode));
754     }
GetOpcodeStr()755     const char *GetOpcodeStr() const
756     {
757         return GetOpcodeString(GetOpcode());
758     }
759 
760     // Bytecode PC accessors
GetPc()761     uint32_t GetPc() const
762     {
763         return pc_;
764     }
SetPc(uint32_t pc)765     void SetPc(uint32_t pc)
766     {
767         pc_ = pc;
768     }
769 
770     // Type accessors
GetType()771     DataType::Type GetType() const
772     {
773         return FieldType::Get(bit_fields_);
774     }
SetType(DataType::Type type)775     void SetType(DataType::Type type)
776     {
777         FieldType::Set(type, &bit_fields_);
778     }
HasType()779     bool HasType() const
780     {
781         return GetType() != DataType::Type::NO_TYPE;
782     }
783 
784     // Parent basic block accessors
GetBasicBlock()785     BasicBlock *GetBasicBlock()
786     {
787         return bb_;
788     }
GetBasicBlock()789     const BasicBlock *GetBasicBlock() const
790     {
791         return bb_;
792     }
SetBasicBlock(BasicBlock * bb)793     void SetBasicBlock(BasicBlock *bb)
794     {
795         bb_ = bb;
796     }
797 
798     // Instruction properties getters
IsControlFlow()799     bool IsControlFlow() const
800     {
801         return GetFlag(inst_flags::CF);
802     }
803 
IsIntrinsic()804     bool IsIntrinsic() const
805     {
806         return GetOpcode() == Opcode::Intrinsic;
807     }
808 
IsCall()809     bool IsCall() const
810     {
811         return GetFlag(inst_flags::CALL);
812     }
813 
IsSpillFill()814     bool IsSpillFill() const
815     {
816         return GetOpcode() == Opcode::SpillFill;
817     }
818 
819     bool IsAccRead() const;
820     bool IsAccWrite() const;
CanThrow()821     bool CanThrow() const
822     {
823         return GetFlag(inst_flags::CAN_THROW);
824     }
RequireState()825     bool RequireState() const
826     {
827         return GetFlag(inst_flags::REQUIRE_STATE);
828     }
829     // Returns true if the instruction not removable in DCE
IsNotRemovable()830     bool IsNotRemovable() const
831     {
832         return GetFlag(inst_flags::NO_DCE);
833     }
834 
835     // Returns true if the instruction doesn't have destination register
NoDest()836     bool NoDest() const
837     {
838         return GetFlag(inst_flags::PSEUDO_DST) || GetFlag(inst_flags::NO_DST) || GetType() == DataType::VOID;
839     }
840 
HasPseudoDestination()841     bool HasPseudoDestination() const
842     {
843         return GetFlag(inst_flags::PSEUDO_DST);
844     }
845 
HasImplicitRuntimeCall()846     bool HasImplicitRuntimeCall() const
847     {
848         return GetFlag(inst_flags::IMPLICIT_RUNTIME_CALL);
849     }
850 
CanDeoptimize()851     bool CanDeoptimize() const
852     {
853         return GetFlag(inst_flags::CAN_DEOPTIMIZE);
854     }
855 
856     // Returns true if the instruction is low-level
IsLowLevel()857     bool IsLowLevel() const
858     {
859         return GetFlag(inst_flags::LOW_LEVEL);
860     }
861 
862     // Returns true if the instruction not hoistable
IsNotHoistable()863     bool IsNotHoistable() const
864     {
865         return GetFlag(inst_flags::NO_HOIST);
866     }
867 
868     // Returns true Cse can't be applied to the instruction
IsNotCseApplicable()869     bool IsNotCseApplicable() const
870     {
871         return GetFlag(inst_flags::NO_CSE);
872     }
873 
874     // Returns true if opcode can not be moved throught runtime calls (REFERENCE type only)
IsRefSpecial()875     bool IsRefSpecial() const
876     {
877         bool result = GetFlag(inst_flags::REF_SPECIAL);
878         ASSERT(!result || GetType() == DataType::Type::REFERENCE);
879         return result;
880     }
881 
882     // Returns true if the instruction is a commutative
IsCommutative()883     bool IsCommutative() const
884     {
885         return GetFlag(inst_flags::COMMUTATIVE);
886     }
887 
888     // Returns true if the instruction can be used in if-conversion
IsIfConvertable()889     bool IsIfConvertable() const
890     {
891         return GetFlag(inst_flags::IFCVT);
892     }
893 
894     virtual bool IsPropagateLiveness() const;
895 
896     bool RequireRegMap() const;
897 
GetObjectTypeInfo()898     ObjectTypeInfo GetObjectTypeInfo() const
899     {
900         return object_type_info_;
901     }
902 
HasObjectTypeInfo()903     bool HasObjectTypeInfo() const
904     {
905         return object_type_info_.IsValid();
906     }
907 
GetDataFlowInput(int index)908     Inst *GetDataFlowInput(int index) const
909     {
910         return GetDataFlowInput(GetInput(index).GetInst());
911     }
912     Inst *GetDataFlowInput(Inst *input_inst) const;
913 
914     bool IsPrecedingInSameBlock(const Inst *other) const;
915 
916     bool IsDominate(const Inst *other) const;
917 
918     bool InSameBlockOrDominate(const Inst *other) const;
919 
GetSaveState()920     const SaveStateInst *GetSaveState() const
921     {
922         return const_cast<Inst *>(this)->GetSaveState();
923     }
924 
GetSaveState()925     SaveStateInst *GetSaveState()
926     {
927         if (!RequireState()) {
928             return nullptr;
929         }
930         if (GetInputsCount() == 0) {
931             return nullptr;
932         }
933         auto ss = GetInput(GetInputsCount() - 1).GetInst();
934         if (ss->GetOpcode() != Opcode::SaveState) {
935             return nullptr;
936         }
937         return ss->CastToSaveState();
938     }
939 
SetSaveState(Inst * inst)940     void SetSaveState(Inst *inst)
941     {
942         ASSERT(RequireState());
943         SetInput(GetInputsCount() - 1, inst);
944     }
945 
946     bool IsZeroRegInst() const;
947 
948     /**
949      * Return instruction clone
950      */
951     virtual Inst *Clone(const Graph *targetGraph) const;
952 
GetFlagsMask()953     uintptr_t GetFlagsMask() const
954     {
955         return GetField<FieldFlags>();
956     }
957 
GetFlag(inst_flags::Flags flag)958     bool GetFlag(inst_flags::Flags flag) const
959     {
960         return (GetFlagsMask() & flag) != 0;
961     }
962 
SetFlag(inst_flags::Flags flag)963     void SetFlag(inst_flags::Flags flag)
964     {
965         SetField<FieldFlags>(GetFlagsMask() | flag);
966     }
967 
ClearFlag(inst_flags::Flags flag)968     void ClearFlag(inst_flags::Flags flag)
969     {
970         SetField<FieldFlags>(GetFlagsMask() & ~static_cast<uintptr_t>(flag));
971     }
972 
973 #ifndef NDEBUG
GetModesMask()974     uint8_t GetModesMask() const
975     {
976         return inst_modes::GetModesMask(opcode_);
977     }
978 
SupportsMode(inst_modes::Mode mode)979     bool SupportsMode(inst_modes::Mode mode) const
980     {
981         return (GetModesMask() & mode) != 0;
982     }
983 #endif
984 
SetTerminator()985     void SetTerminator()
986     {
987         SetFlag(inst_flags::Flags::TERMINATOR);
988     }
989 
990     void InsertBefore(Inst *inst);
991     void InsertAfter(Inst *inst);
992 
993     /**
994      * Return true if instruction has dynamic operands storage.
995      */
IsOperandsDynamic()996     bool IsOperandsDynamic() const
997     {
998         return GetField<InputsCount>() == MAX_STATIC_INPUTS;
999     }
1000 
1001     /**
1002      * Add user to the instruction.
1003      * @param user - pointer to User object
1004      */
AddUser(User * user)1005     void AddUser(User *user)
1006     {
1007         ASSERT(user && user->GetInst());
1008         user->SetNext(first_user_);
1009         user->SetPrev(nullptr);
1010         if (first_user_ != nullptr) {
1011             ASSERT(first_user_->GetPrev() == nullptr);
1012             first_user_->SetPrev(user);
1013         }
1014         first_user_ = user;
1015     }
1016 
1017     /**
1018      * Remove instruction from users.
1019      * @param user - pointer to User object
1020      */
RemoveUser(User * user)1021     void RemoveUser(User *user)
1022     {
1023         ASSERT(user);
1024         ASSERT(HasUsers());
1025         if (user == first_user_) {
1026             first_user_ = user->GetNext();
1027         }
1028         user->Remove();
1029     }
1030 
1031     /**
1032      * Set input instruction in specified index.
1033      * Old input will be removed.
1034      * @param index - index of input to be set
1035      * @param inst - new input instruction TODO sherstennikov: currently it can be nullptr, is it correct?
1036      */
SetInput(unsigned index,Inst * inst)1037     void SetInput(unsigned index, Inst *inst)
1038     {
1039         CHECK_LT(index, GetInputsCount());
1040         auto &input = GetInputs()[index];
1041         auto user = GetUser(index);
1042         if (input.GetInst() != nullptr && input.GetInst()->HasUsers()) {
1043             input.GetInst()->RemoveUser(user);
1044         }
1045         if (inst != nullptr) {
1046             inst->AddUser(user);
1047         }
1048         input = Input(inst);
1049     }
1050 
1051     /**
1052      * Replace all inputs that points to specified instruction by new one.
1053      * @param old_input - instruction that should be replaced
1054      * @param new_input - new input instruction
1055      */
ReplaceInput(Inst * old_input,Inst * new_input)1056     void ReplaceInput(Inst *old_input, Inst *new_input)
1057     {
1058         unsigned index = 0;
1059         for (auto input : GetInputs()) {
1060             if (input.GetInst() == old_input) {
1061                 SetInput(index, new_input);
1062             }
1063             index++;
1064         }
1065     }
1066 
1067     /**
1068      * Replace inputs that point to this instruction by given instruction.
1069      * @param inst - new input instruction
1070      */
ReplaceUsers(Inst * inst)1071     void ReplaceUsers(Inst *inst)
1072     {
1073         ASSERT(inst != this);
1074         ASSERT(inst != nullptr);
1075         for (auto it = GetUsers().begin(); it != GetUsers().end(); it = GetUsers().begin()) {
1076             it->GetInst()->SetInput(it->GetIndex(), inst);
1077         }
1078     }
1079 
1080     /**
1081      * Append input instruction.
1082      * Available only for variadic inputs instructions, such as PHI.
1083      * @param input - input instruction
1084      * @return index in inputs container where new input is placed
1085      */
AppendInput(Inst * input)1086     unsigned AppendInput(Inst *input)
1087     {
1088         ASSERT(input != nullptr);
1089         ASSERT(IsOperandsDynamic());
1090         DynamicOperands *operands = GetDynamicOperands();
1091         return operands->Append(input);
1092     }
1093 
AppendInput(Input input)1094     unsigned AppendInput(Input input)
1095     {
1096         static_assert(sizeof(Input) <= sizeof(uintptr_t));  // Input become larger, so pass it by reference then
1097         return AppendInput(input.GetInst());
1098     }
1099 
1100     /**
1101      * Remove input from inputs container
1102      * Available only for variadic inputs instructions, such as PHI.
1103      * @param index - index of input in inputs container
1104      */
RemoveInput(unsigned index)1105     virtual void RemoveInput(unsigned index)
1106     {
1107         ASSERT(IsOperandsDynamic());
1108         DynamicOperands *operands = GetDynamicOperands();
1109         ASSERT(index < operands->Size());
1110         operands->Remove(index);
1111     }
1112 
1113     /**
1114      * Remove all inputs
1115      */
RemoveInputs()1116     void RemoveInputs()
1117     {
1118         if (UNLIKELY(IsOperandsDynamic())) {
1119             for (auto inputs_count = GetInputsCount(); inputs_count != 0; --inputs_count) {
1120                 RemoveInput(inputs_count - 1);
1121             }
1122         } else {
1123             for (size_t i = 0; i < GetInputsCount(); ++i) {
1124                 SetInput(i, nullptr);
1125             }
1126         }
1127     }
1128 
1129     /**
1130      * Remove all users
1131      */
1132     template <bool with_inputs = false>
RemoveUsers()1133     void RemoveUsers()
1134     {
1135         auto users = GetUsers();
1136         while (!users.Empty()) {
1137             // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
1138             if constexpr (with_inputs) {
1139                 auto &user = users.Front();
1140                 user.GetInst()->RemoveInput(user.GetIndex());
1141                 // NOLINTNEXTLINE(readability-misleading-indentation)
1142             } else {
1143                 RemoveUser(&users.Front());
1144             }
1145         }
1146     }
1147 
1148     /**
1149      * Get input by index
1150      * @param index - index of input
1151      * @return input instruction
1152      */
GetInput(unsigned index)1153     Input GetInput(unsigned index)
1154     {
1155         ASSERT(index < GetInputsCount());
1156         return GetInputs()[index];
1157     }
1158 
GetInput(unsigned index)1159     Input GetInput(unsigned index) const
1160     {
1161         ASSERT(index < GetInputsCount());
1162         return GetInputs()[index];
1163     }
1164 
GetInputs()1165     Span<Input> GetInputs()
1166     {
1167         if (UNLIKELY(IsOperandsDynamic())) {
1168             DynamicOperands *operands = GetDynamicOperands();
1169             return Span<Input>(operands->Inputs(), operands->Size());
1170         }
1171 
1172         auto inputs_count {GetField<InputsCount>()};
1173         return Span<Input>(
1174             reinterpret_cast<Input *>(reinterpret_cast<uintptr_t>(this) -
1175                                       (inputs_count + Input::GetPadding(RUNTIME_ARCH, inputs_count)) * sizeof(Input)),
1176             inputs_count);
1177     }
GetInputs()1178     Span<const Input> GetInputs() const
1179     {
1180         return Span<const Input>(const_cast<Inst *>(this)->GetInputs());
1181     }
1182 
GetInputType(size_t index)1183     virtual DataType::Type GetInputType([[maybe_unused]] size_t index) const
1184     {
1185         ASSERT(index < GetInputsCount());
1186         return GetType();
1187     }
1188 
GetUsers()1189     UserList<User> GetUsers()
1190     {
1191         return UserList<User>(&first_user_);
1192     }
GetUsers()1193     UserList<const User> GetUsers() const
1194     {
1195         return UserList<const User>(&first_user_);
1196     }
1197 
GetInputsCount()1198     size_t GetInputsCount() const
1199     {
1200         if (UNLIKELY(IsOperandsDynamic())) {
1201             return GetDynamicOperands()->Size();
1202         }
1203         return GetInputs().Size();
1204     }
1205 
HasUsers()1206     bool HasUsers() const
1207     {
1208         return first_user_ != nullptr;
1209     };
1210 
HasSingleUser()1211     bool HasSingleUser() const
1212     {
1213         return first_user_ != nullptr && first_user_->GetNext() == nullptr;
1214     }
1215 
1216     /// Reserve space in dataflow storage for specified inputs count
1217     void ReserveInputs(size_t capacity);
1218 
SetLocation(size_t index,Location location)1219     virtual void SetLocation([[maybe_unused]] size_t index, [[maybe_unused]] Location location) {}
1220 
GetLocation(size_t index)1221     virtual Location GetLocation([[maybe_unused]] size_t index) const
1222     {
1223         return Location::RequireRegister();
1224     }
1225 
GetDstLocation()1226     virtual Location GetDstLocation() const
1227     {
1228         return Location::MakeRegister(GetDstReg(), GetType());
1229     }
1230 
1231     template <typename Accessor>
GetField()1232     typename Accessor::ValueType GetField() const
1233     {
1234         return Accessor::Get(bit_fields_);
1235     }
1236 
1237     template <typename Accessor>
SetField(typename Accessor::ValueType value)1238     void SetField(typename Accessor::ValueType value)
1239     {
1240         Accessor::Set(value, &bit_fields_);
1241     }
1242 
GetAllFields()1243     uint64_t GetAllFields() const
1244     {
1245         return bit_fields_;
1246     }
1247 
IsPhi()1248     bool IsPhi() const
1249     {
1250         return opcode_ == Opcode::Phi;
1251     }
1252 
IsCatchPhi()1253     bool IsCatchPhi() const
1254     {
1255         return opcode_ == Opcode::CatchPhi;
1256     }
1257 
IsConst()1258     bool IsConst() const
1259     {
1260         return opcode_ == Opcode::Constant;
1261     }
1262 
IsParameter()1263     bool IsParameter() const
1264     {
1265         return opcode_ == Opcode::Parameter;
1266     }
1267 
IsBoolConst()1268     virtual bool IsBoolConst() const
1269     {
1270         return false;
1271     }
1272 
IsSaveState()1273     bool IsSaveState() const
1274     {
1275         return opcode_ == Opcode::SaveState;
1276     }
1277 
IsTry()1278     bool IsTry() const
1279     {
1280         return opcode_ == Opcode::Try;
1281     }
1282 
SetVnObject(VnObject * vn_obj)1283     virtual void SetVnObject([[maybe_unused]] VnObject *vn_obj) {}
1284 
GetDstReg()1285     Register GetDstReg() const
1286     {
1287         return dst_reg_;
1288     }
1289 
SetDstReg(Register reg)1290     void SetDstReg(Register reg)
1291     {
1292         dst_reg_ = reg;
1293     }
1294 
GetVN()1295     uint32_t GetVN() const
1296     {
1297         return vn_;
1298     }
1299 
SetVN(uint32_t vn)1300     void SetVN(uint32_t vn)
1301     {
1302         vn_ = vn;
1303     }
1304     void Dump(std::ostream *out, bool new_line = true) const;
1305     virtual bool DumpInputs(std::ostream *out) const;
1306     virtual void DumpOpcode(std::ostream *out) const;
1307 
SetDstReg(unsigned index,Register reg)1308     virtual void SetDstReg([[maybe_unused]] unsigned index, Register reg)
1309     {
1310         ASSERT(index == 0);
1311         SetDstReg(reg);
1312     }
1313 
GetDstReg(unsigned index)1314     virtual Register GetDstReg([[maybe_unused]] unsigned index) const
1315     {
1316         ASSERT(index == 0);
1317         return GetDstReg();
1318     }
1319 
GetDstCount()1320     virtual size_t GetDstCount() const
1321     {
1322         return 1;
1323     }
1324 
GetSrcRegIndex()1325     virtual uint32_t GetSrcRegIndex() const
1326     {
1327         return 0;
1328     }
1329 
SetSrcReg(unsigned index,Register reg)1330     virtual void SetSrcReg([[maybe_unused]] unsigned index, [[maybe_unused]] Register reg) {}
1331 
GetSrcReg(unsigned index)1332     virtual Register GetSrcReg([[maybe_unused]] unsigned index) const
1333     {
1334         return INVALID_REG;
1335     }
1336 
GetFirstUser()1337     User *GetFirstUser() const
1338     {
1339         return first_user_;
1340     }
1341 
1342 protected:
1343     using InstBase::InstBase;
1344     static constexpr int INPUT_COUNT = 0;
1345 
1346     Inst() = default;
1347 
Inst(Opcode opcode)1348     explicit Inst(Opcode opcode) : Inst(opcode, DataType::Type::NO_TYPE, INVALID_PC) {}
1349 
Inst(Opcode opcode,DataType::Type type,uint32_t pc)1350     explicit Inst(Opcode opcode, DataType::Type type, uint32_t pc) : pc_(pc), opcode_(opcode)
1351     {
1352         bit_fields_ = inst_flags::GetFlagsMask(opcode);
1353         SetField<FieldType>(type);
1354     }
1355 
1356 protected:
1357     using FieldFlags = BitField<uint32_t, 0, MinimumBitsToStore(1U << inst_flags::FLAGS_COUNT)>;
1358     using FieldType = FieldFlags::NextField<DataType::Type, MinimumBitsToStore(DataType::LAST)>;
1359     using InputsCount = FieldType::NextField<uint32_t, BITS_PER_INPUTS_NUM>;
1360     using LastField = InputsCount;
1361 
GetDynamicOperands()1362     DynamicOperands *GetDynamicOperands() const
1363     {
1364         return reinterpret_cast<DynamicOperands *>(reinterpret_cast<uintptr_t>(this) - sizeof(DynamicOperands));
1365     }
1366 
1367 private:
GetUser(unsigned index)1368     User *GetUser(unsigned index)
1369     {
1370         if (UNLIKELY(IsOperandsDynamic())) {
1371             return GetDynamicOperands()->GetUser(index);
1372         }
1373         auto inputs_count {GetField<InputsCount>()};
1374         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1375         return reinterpret_cast<User *>(reinterpret_cast<Input *>(this) -
1376                                         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1377                                         (inputs_count + Input::GetPadding(RUNTIME_ARCH, inputs_count))) -
1378                // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1379                index - 1;
1380     }
1381 
OperandsStorageSize()1382     size_t OperandsStorageSize() const
1383     {
1384         if (UNLIKELY(IsOperandsDynamic())) {
1385             return sizeof(DynamicOperands);
1386         }
1387 
1388         auto inputs_count {GetField<InputsCount>()};
1389         return inputs_count * (sizeof(Input) + sizeof(User)) +
1390                Input::GetPadding(RUNTIME_ARCH, inputs_count) * sizeof(Input);
1391     }
1392 
1393 private:
1394     /// Basic block this instruction belongs to
1395     BasicBlock *bb_ {nullptr};
1396 
1397     /// Next instruction within basic block
1398     Inst *next_ {nullptr};
1399 
1400     /// Previous instruction within basic block
1401     Inst *prev_ {nullptr};
1402 
1403     /// First user in users chain
1404     User *first_user_ {nullptr};
1405 
1406     /// This value hold properties of the instruction. It accessed via BitField types(f.e. FieldType).
1407     uint64_t bit_fields_ {0};
1408 
1409     /// Unique id of instruction
1410     uint32_t id_ {INVALID_ID};
1411 
1412     /// Unique id of instruction
1413     uint32_t vn_ {INVALID_VN};
1414 
1415     /// Bytecode pc
1416     uint32_t pc_ {INVALID_PC};
1417 
1418     /// Number used in cloning
1419     uint32_t clone_number_ {0};
1420 
1421     /// Instruction number getting while visiting graph
1422     LinearNumber linear_number_ {INVALID_LINEAR_NUM};
1423 
1424     ObjectTypeInfo object_type_info_ {};
1425 
1426     /// Opcode, see opcodes.def
1427     Opcode opcode_ {Opcode::INVALID};
1428 
1429     // Destination register type - defined in FieldType
1430     Register dst_reg_ {INVALID_REG};
1431 };
1432 
1433 /**
1434  * Proxy class that injects new field - type of the source operands - into property field of the instruction.
1435  * Should be used when instruction has sources of the same type and type of the instruction is not match to type of
1436  * sources. Examples: Cmp, Compare
1437  * @tparam T Base instruction class after which this mixin is injected
1438  */
1439 template <typename T>
1440 class InstWithOperandsType : public T {
1441 public:
1442     using T::T;
1443 
SetOperandsType(DataType::Type type)1444     void SetOperandsType(DataType::Type type)
1445     {
1446         T::template SetField<FieldOperandsType>(type);
1447     }
GetOperandsType()1448     virtual DataType::Type GetOperandsType() const
1449     {
1450         return T::template GetField<FieldOperandsType>();
1451     }
1452 
1453 protected:
1454     using FieldOperandsType =
1455         typename T::LastField::template NextField<DataType::Type, MinimumBitsToStore(DataType::LAST)>;
1456     using LastField = FieldOperandsType;
1457 };
1458 
1459 /**
1460  * Mixin for NeedBarrier flag.
1461  * @tparam T Base instruction class after which this mixin is injected
1462  */
1463 template <typename T>
1464 class NeedBarrierMixin : public T {
1465 public:
1466     using T::T;
1467 
SetNeedBarrier(bool v)1468     void SetNeedBarrier(bool v)
1469     {
1470         T::template SetField<NeedBarrierFlag>(v);
1471     }
GetNeedBarrier()1472     bool GetNeedBarrier() const
1473     {
1474         return T::template GetField<NeedBarrierFlag>();
1475     }
1476 
1477 protected:
1478     using NeedBarrierFlag = typename T::LastField::NextFlag;
1479     using LastField = NeedBarrierFlag;
1480 };
1481 
1482 /**
1483  * This mixin aims to implement type id accessors.
1484  */
1485 class TypeIdMixin {
1486 public:
1487     TypeIdMixin() = default;
1488     NO_COPY_SEMANTIC(TypeIdMixin);
1489     NO_MOVE_SEMANTIC(TypeIdMixin);
1490     virtual ~TypeIdMixin() = default;
1491 
SetTypeId(uint32_t id)1492     void SetTypeId(uint32_t id)
1493     {
1494         type_id_ = id;
1495     }
1496 
GetTypeId()1497     auto GetTypeId() const
1498     {
1499         return type_id_;
1500     }
1501 
SetMethod(RuntimeInterface::MethodPtr method)1502     void SetMethod(RuntimeInterface::MethodPtr method)
1503     {
1504         method_ = method;
1505     }
GetMethod()1506     auto GetMethod() const
1507     {
1508         return method_;
1509     }
1510 
1511 private:
1512     uint32_t type_id_ {0};
1513     // The pointer to the method in which this instruction is executed(inlined method)
1514     RuntimeInterface::MethodPtr method_ {nullptr};
1515 };
1516 
1517 /**
1518  * Mixin for Inlined calls/returns.
1519  */
1520 template <typename T>
1521 class InlinedInstMixin : public T {
1522 public:
1523     using T::T;
1524 
SetInlined(bool v)1525     void SetInlined(bool v)
1526     {
1527         T::template SetField<IsInlinedFlag>(v);
1528     }
IsInlined()1529     bool IsInlined() const
1530     {
1531         return T::template GetField<IsInlinedFlag>();
1532     }
1533 
1534 protected:
1535     using IsInlinedFlag = typename T::LastField::NextFlag;
1536     using LastField = IsInlinedFlag;
1537 };
1538 
1539 /**
1540  * Mixin for instructions with immediate constant value
1541  */
1542 class ImmediateMixin {
1543 public:
ImmediateMixin(uint64_t immediate)1544     explicit ImmediateMixin(uint64_t immediate) : immediate_(immediate) {}
1545 
1546     NO_COPY_SEMANTIC(ImmediateMixin);
1547     NO_MOVE_SEMANTIC(ImmediateMixin);
1548     virtual ~ImmediateMixin() = default;
1549 
SetImm(uint64_t immediate)1550     void SetImm(uint64_t immediate)
1551     {
1552         immediate_ = immediate;
1553     }
GetImm()1554     auto GetImm() const
1555     {
1556         return immediate_;
1557     }
1558 
1559 protected:
1560     ImmediateMixin() = default;
1561 
1562 private:
1563     uint64_t immediate_ {0};
1564 };
1565 
1566 /**
1567  * Mixin for instructions with ConditionCode
1568  */
1569 template <typename T>
1570 class ConditionMixin : public T {
1571 public:
1572     enum class Prediction { NONE, LIKELY, UNLIKELY, SIZE = UNLIKELY };
1573 
1574     using T::T;
ConditionMixin(ConditionCode cc)1575     explicit ConditionMixin(ConditionCode cc)
1576     {
1577         T::template SetField<CcFlag>(cc);
1578     }
1579     NO_COPY_SEMANTIC(ConditionMixin);
1580     NO_MOVE_SEMANTIC(ConditionMixin);
1581     ~ConditionMixin() override = default;
1582 
GetCc()1583     auto GetCc() const
1584     {
1585         return T::template GetField<CcFlag>();
1586     }
SetCc(ConditionCode cc)1587     void SetCc(ConditionCode cc)
1588     {
1589         T::template SetField<CcFlag>(cc);
1590     }
InverseConditionCode()1591     void InverseConditionCode()
1592     {
1593         SetCc(GetInverseConditionCode(GetCc()));
1594         if (IsLikely()) {
1595             SetUnlikely();
1596         } else if (IsUnlikely()) {
1597             SetLikely();
1598         }
1599     }
1600 
IsLikely()1601     bool IsLikely() const
1602     {
1603         return T::template GetField<PredictionFlag>() == Prediction::LIKELY;
1604     }
IsUnlikely()1605     bool IsUnlikely() const
1606     {
1607         return T::template GetField<PredictionFlag>() == Prediction::UNLIKELY;
1608     }
SetLikely()1609     void SetLikely()
1610     {
1611         T::template SetField<PredictionFlag>(Prediction::LIKELY);
1612     }
SetUnlikely()1613     void SetUnlikely()
1614     {
1615         T::template SetField<PredictionFlag>(Prediction::UNLIKELY);
1616     }
1617 
1618 protected:
1619     ConditionMixin() = default;
1620 
1621     using CcFlag = typename T::LastField::template NextField<ConditionCode, MinimumBitsToStore(ConditionCode::CC_LAST)>;
1622     using PredictionFlag = typename CcFlag::template NextField<Prediction, MinimumBitsToStore(Prediction::SIZE)>;
1623     using LastField = PredictionFlag;
1624 };
1625 
1626 /**
1627  * Instruction with fixed number of inputs.
1628  * Shall not be instantiated directly, only through derived classes.
1629  */
1630 template <size_t N>
1631 class FixedInputsInst : public Inst {
1632 public:
1633     using Inst::Inst;
1634 
1635     static constexpr int INPUT_COUNT = N;
1636 
SetSrcReg(unsigned index,Register reg)1637     void SetSrcReg(unsigned index, Register reg) override
1638     {
1639         ASSERT(index < N);
1640         src_regs_[index] = reg;
1641     }
1642 
GetSrcReg(unsigned index)1643     Register GetSrcReg(unsigned index) const override
1644     {
1645         ASSERT(index < N);
1646         return src_regs_[index];
1647     }
1648 
GetLocation(size_t index)1649     Location GetLocation(size_t index) const override
1650     {
1651         return Location::MakeRegister(GetSrcReg(index), GetInputType(index));
1652     }
1653 
SetLocation(size_t index,Location location)1654     void SetLocation(size_t index, Location location) override
1655     {
1656         SetSrcReg(index, location.GetValue());
1657     }
1658 
SetDstLocation(Location location)1659     void SetDstLocation(Location location)
1660     {
1661         SetDstReg(location.GetValue());
1662     }
1663 
1664     Inst *Clone(const Graph *targetGraph) const override;
1665 
1666 private:
1667     template <typename T, std::size_t... Is>
CreateArray(T value,std::index_sequence<Is...> unused)1668     constexpr auto CreateArray(T value, [[maybe_unused]] std::index_sequence<Is...> unused)
1669     {
1670         return std::array<T, sizeof...(Is)> {(static_cast<void>(Is), value)...};
1671     }
1672 
1673     std::array<Register, N> src_regs_ = CreateArray(INVALID_REG, std::make_index_sequence<INPUT_COUNT>());
1674 };
1675 
1676 template <size_t N>
Clone(const Graph * targetGraph)1677 Inst *FixedInputsInst<N>::Clone(const Graph *targetGraph) const
1678 {
1679     auto clone = static_cast<FixedInputsInst *>(Inst::Clone(targetGraph));
1680 #ifndef NDEBUG
1681     for (size_t i = 0; i < INPUT_COUNT; ++i) {
1682         clone->SetSrcReg(i, GetSrcReg(i));
1683     }
1684 #endif
1685     return clone;
1686 }
1687 
1688 /**
1689  * Instructions with fixed static inputs
1690  * We need to explicitly declare these proxy classes because some code can't work with the templated inst classes, for
1691  * example DEFINE_INST macro.
1692  */
1693 class FixedInputsInst0 : public FixedInputsInst<0> {
1694 public:
1695     using FixedInputsInst::FixedInputsInst;
1696 
1697     NO_COPY_SEMANTIC(FixedInputsInst0);
1698     NO_MOVE_SEMANTIC(FixedInputsInst0);
1699     ~FixedInputsInst0() override = default;
1700 };
1701 
1702 class FixedInputsInst1 : public FixedInputsInst<1> {
1703 public:
1704     using FixedInputsInst::FixedInputsInst;
1705 
1706     NO_COPY_SEMANTIC(FixedInputsInst1);
1707     NO_MOVE_SEMANTIC(FixedInputsInst1);
1708     ~FixedInputsInst1() override = default;
1709 };
1710 
1711 class FixedInputsInst2 : public FixedInputsInst<2U> {
1712 public:
1713     using FixedInputsInst::FixedInputsInst;
1714 
1715     NO_COPY_SEMANTIC(FixedInputsInst2);
1716     NO_MOVE_SEMANTIC(FixedInputsInst2);
1717     ~FixedInputsInst2() override = default;
1718 };
1719 
1720 /**
1721  * Instruction with variable inputs count
1722  */
1723 class DynamicInputsInst : public Inst {
1724 public:
1725     using Inst::Inst;
1726 
1727     static constexpr int INPUT_COUNT = MAX_STATIC_INPUTS;
1728 
GetLocation(size_t index)1729     Location GetLocation(size_t index) const override
1730     {
1731         if (locations_ == nullptr) {
1732             return Location::Invalid();
1733         }
1734         return locations_->GetLocation(index);
1735     }
1736 
GetDstLocation()1737     Location GetDstLocation() const override
1738     {
1739         if (locations_ == nullptr) {
1740             return Location::Invalid();
1741         }
1742         return locations_->GetDstLocation();
1743     }
1744 
SetLocation(size_t index,Location location)1745     void SetLocation(size_t index, Location location) override
1746     {
1747         ASSERT(locations_ != nullptr);
1748         locations_->SetLocation(index, location);
1749     }
1750 
SetDstLocation(Location location)1751     void SetDstLocation(Location location)
1752     {
1753         ASSERT(locations_ != nullptr);
1754         locations_->SetDstLocation(location);
1755     }
1756 
SetLocationsInfo(LocationsInfo * info)1757     void SetLocationsInfo(LocationsInfo *info)
1758     {
1759         locations_ = info;
1760     }
1761 
GetSrcReg(unsigned index)1762     Register GetSrcReg(unsigned index) const override
1763     {
1764         return GetLocation(index).GetValue();
1765     }
1766 
SetSrcReg(unsigned index,Register reg)1767     void SetSrcReg(unsigned index, Register reg) override
1768     {
1769         SetLocation(index, Location::MakeRegister(reg, GetInputType(index)));
1770     }
1771 
1772 private:
1773     LocationsInfo *locations_ {nullptr};
1774 };
1775 
1776 class SpillFillInst;
1777 
1778 /**
1779  * Mixin to hold location data
1780  */
1781 class LocationDataMixin {
1782 public:
SetLocationData(SpillFillData location_data)1783     void SetLocationData(SpillFillData location_data)
1784     {
1785         location_data_ = location_data;
1786     }
1787 
GetLocationData()1788     auto GetLocationData() const
1789     {
1790         return location_data_;
1791     }
1792 
GetLocationData()1793     auto &GetLocationData()
1794     {
1795         return location_data_;
1796     }
1797 
1798 protected:
1799     LocationDataMixin() = default;
1800     NO_COPY_SEMANTIC(LocationDataMixin);
1801     NO_MOVE_SEMANTIC(LocationDataMixin);
1802     virtual ~LocationDataMixin() = default;
1803 
1804 private:
1805     SpillFillData location_data_ {};
1806 };
1807 
1808 /**
1809  * Mixin to hold input types of call instruction
1810  */
1811 class InputTypesMixin {
1812 public:
1813     InputTypesMixin() = default;
1814     NO_COPY_SEMANTIC(InputTypesMixin);
1815     NO_MOVE_SEMANTIC(InputTypesMixin);
1816     virtual ~InputTypesMixin() = default;
1817 
AllocateInputTypes(ArenaAllocator * allocator,size_t capacity)1818     void AllocateInputTypes(ArenaAllocator *allocator, size_t capacity)
1819     {
1820         ASSERT(allocator != nullptr);
1821         ASSERT(input_types_ == nullptr);
1822         input_types_ = allocator->New<ArenaVector<DataType::Type>>(allocator->Adapter());
1823         ASSERT(input_types_ != nullptr);
1824         input_types_->reserve(capacity);
1825         ASSERT(input_types_->capacity() >= capacity);
1826     }
AddInputType(DataType::Type type)1827     void AddInputType(DataType::Type type)
1828     {
1829         ASSERT(input_types_ != nullptr);
1830         input_types_->push_back(type);
1831     }
GetInputTypes()1832     ArenaVector<DataType::Type> *GetInputTypes()
1833     {
1834         return input_types_;
1835     }
CloneTypes(ArenaAllocator * allocator,InputTypesMixin * target_inst)1836     void CloneTypes(ArenaAllocator *allocator, InputTypesMixin *target_inst) const
1837     {
1838         if (UNLIKELY(input_types_ == nullptr)) {
1839             return;
1840         }
1841         target_inst->AllocateInputTypes(allocator, input_types_->size());
1842         for (auto input_type : *input_types_) {
1843             target_inst->AddInputType(input_type);
1844         }
1845     }
1846 
1847 protected:
1848     // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
1849     ArenaVector<DataType::Type> *input_types_ {nullptr};
1850 };
1851 
1852 /**
1853  * Compare instruction
1854  */
1855 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
1856 class CompareInst : public InstWithOperandsType<ConditionMixin<FixedInputsInst2>> {
1857 public:
1858     using BaseInst = InstWithOperandsType<ConditionMixin<FixedInputsInst2>>;
1859     using BaseInst::BaseInst;
1860 
CompareInst(Opcode opcode,DataType::Type type,uint32_t pc,ConditionCode cc)1861     CompareInst(Opcode opcode, DataType::Type type, uint32_t pc, ConditionCode cc) : BaseInst(opcode, type, pc)
1862     {
1863         SetCc(cc);
1864     }
1865 
GetInputType(size_t index)1866     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
1867     {
1868         ASSERT(index < GetInputsCount());
1869         return GetOperandsType();
1870     }
1871     void DumpOpcode(std::ostream *out) const override;
1872 
1873     void SetVnObject(VnObject *vn_obj) override;
1874 
1875     Inst *Clone(const Graph *targetGraph) const override;
1876 };
1877 
1878 /**
1879  * Mixin for AnyTypeMixin instructions
1880  */
1881 template <typename T>
1882 class AnyTypeMixin : public T {
1883 public:
1884     using T::T;
1885 
SetAnyType(AnyBaseType any_type)1886     void SetAnyType(AnyBaseType any_type)
1887     {
1888         T::template SetField<AnyBaseTypeField>(any_type);
1889     }
1890 
GetAnyType()1891     AnyBaseType GetAnyType() const
1892     {
1893         return T::template GetField<AnyBaseTypeField>();
1894     }
1895 
1896 protected:
1897     using AnyBaseTypeField =
1898         typename T::LastField::template NextField<AnyBaseType, MinimumBitsToStore(AnyBaseType::COUNT)>;
1899     using LastField = AnyBaseTypeField;
1900 };
1901 
1902 /**
1903  * CompareAnyTypeInst instruction
1904  */
1905 class CompareAnyTypeInst : public AnyTypeMixin<FixedInputsInst1> {
1906 public:
1907     using BaseInst = AnyTypeMixin<FixedInputsInst1>;
1908     using BaseInst::BaseInst;
1909 
CompareAnyTypeInst(Opcode opcode,uint32_t pc,AnyBaseType any_type)1910     CompareAnyTypeInst(Opcode opcode, uint32_t pc, AnyBaseType any_type) : BaseInst(opcode, DataType::Type::BOOL, pc)
1911     {
1912         SetAnyType(any_type);
1913     }
1914 
GetInputType(size_t index)1915     DataType::Type GetInputType(size_t index) const override
1916     {
1917         ASSERT(index < GetInputsCount());
1918         return GetInput(index).GetInst()->GetType();
1919     }
1920 
1921     void DumpOpcode(std::ostream *out) const override;
1922 
Clone(const Graph * targetGraph)1923     Inst *Clone(const Graph *targetGraph) const override
1924     {
1925         auto clone = FixedInputsInst::Clone(targetGraph);
1926         clone->CastToCompareAnyType()->SetAnyType(GetAnyType());
1927         return clone;
1928     }
1929 };
1930 
1931 /**
1932  * CastAnyTypeValueInst instruction
1933  */
1934 class CastAnyTypeValueInst : public AnyTypeMixin<FixedInputsInst1> {
1935 public:
1936     using BaseInst = AnyTypeMixin<FixedInputsInst1>;
1937     using BaseInst::BaseInst;
1938 
CastAnyTypeValueInst(Opcode opcode,uint32_t pc,AnyBaseType any_type)1939     CastAnyTypeValueInst(Opcode opcode, uint32_t pc, AnyBaseType any_type)
1940         : BaseInst(opcode, AnyBaseTypeToDataType(any_type), pc)
1941     {
1942         SetAnyType(any_type);
1943     }
1944 
GetInputType(size_t index)1945     DataType::Type GetInputType(size_t index) const override
1946     {
1947         ASSERT(index < GetInputsCount());
1948         return GetInput(index).GetInst()->GetType();
1949     }
1950 
GetDeducedType()1951     DataType::Type GetDeducedType() const
1952     {
1953         return AnyBaseTypeToDataType(GetAnyType());
1954     }
1955 
1956     void DumpOpcode(std::ostream *out) const override;
1957 
Clone(const Graph * targetGraph)1958     Inst *Clone(const Graph *targetGraph) const override
1959     {
1960         auto clone = FixedInputsInst::Clone(targetGraph)->CastToCastAnyTypeValue();
1961         AnyBaseType any_type = GetAnyType();
1962         clone->SetAnyType(any_type);
1963         clone->SetType(GetType());
1964         return clone;
1965     }
1966 };
1967 
1968 /**
1969  * CastValueToAnyTypeInst instruction
1970  */
1971 class CastValueToAnyTypeInst : public AnyTypeMixin<FixedInputsInst1> {
1972 public:
1973     using BaseInst = AnyTypeMixin<FixedInputsInst1>;
1974     using BaseInst::BaseInst;
1975 
CastValueToAnyTypeInst(Opcode opcode,uint32_t pc)1976     CastValueToAnyTypeInst(Opcode opcode, uint32_t pc) : BaseInst(opcode, DataType::ANY, pc) {}
1977 
GetInputType(size_t index)1978     DataType::Type GetInputType(size_t index) const override
1979     {
1980         ASSERT(index < GetInputsCount());
1981         return GetInput(index).GetInst()->GetType();
1982     }
1983 
1984     void DumpOpcode(std::ostream *out) const override;
1985 
Clone(const Graph * targetGraph)1986     Inst *Clone(const Graph *targetGraph) const override
1987     {
1988         auto clone = FixedInputsInst::Clone(targetGraph)->CastToCastValueToAnyType();
1989         auto any_type = GetAnyType();
1990         clone->SetAnyType(any_type);
1991         clone->SetType(GetType());
1992         return clone;
1993     }
1994 };
1995 
1996 /**
1997  * ConstantInst represent constant value.
1998  *
1999  * Available types: INT64, FLOAT32, FLOAT64, ANY. All integer types are stored as INT64 value.
2000  * Once type of constant is set, it can't be changed anymore.
2001  */
2002 class ConstantInst : public Inst {
2003 public:
2004     using Inst::Inst;
2005 
2006     template <typename T>
Inst(Opcode::Constant)2007     explicit ConstantInst(Opcode /* unused */, T value, bool support_int32 = false) : Inst(Opcode::Constant)
2008     {
2009         ASSERT(GetTypeFromCType<T>() != DataType::NO_TYPE);
2010         // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-branch-clone)
2011         if constexpr (GetTypeFromCType<T>() == DataType::FLOAT64) {
2012             value_ = bit_cast<uint64_t, double>(value);
2013             // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2014         } else if constexpr (GetTypeFromCType<T>() == DataType::FLOAT32) {
2015             value_ = bit_cast<uint32_t, float>(value);
2016             // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2017         } else if constexpr (GetTypeFromCType<T>() == DataType::ANY) {
2018             value_ = value.Raw();
2019             // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2020         } else if (GetTypeFromCType<T>(support_int32) == DataType::INT32) {
2021             value_ = static_cast<int32_t>(value);
2022             // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2023         } else {
2024             value_ = value;
2025         }
2026 
2027         SetType(GetTypeFromCType<T>(support_int32));
2028     }
2029 
GetRawValue()2030     uint64_t GetRawValue() const
2031     {
2032         return value_;
2033     }
2034 
GetInt32Value()2035     uint32_t GetInt32Value() const
2036     {
2037         ASSERT(GetType() == DataType::INT32);
2038         return static_cast<uint32_t>(value_);
2039     }
2040 
GetInt64Value()2041     uint64_t GetInt64Value() const
2042     {
2043         ASSERT(GetType() == DataType::INT64);
2044         return value_;
2045     }
2046 
GetIntValue()2047     uint64_t GetIntValue() const
2048     {
2049         ASSERT(GetType() == DataType::INT64 || GetType() == DataType::INT32);
2050         return value_;
2051     }
2052 
GetFloatValue()2053     float GetFloatValue() const
2054     {
2055         ASSERT(GetType() == DataType::FLOAT32);
2056         return bit_cast<float, uint32_t>(static_cast<uint32_t>(value_));
2057     }
2058 
GetDoubleValue()2059     double GetDoubleValue() const
2060     {
2061         ASSERT(GetType() == DataType::FLOAT64);
2062         return bit_cast<double, uint64_t>(value_);
2063     }
2064 
GetNextConst()2065     ConstantInst *GetNextConst()
2066     {
2067         return next_const_;
2068     }
SetNextConst(ConstantInst * next_const)2069     void SetNextConst(ConstantInst *next_const)
2070     {
2071         next_const_ = next_const;
2072     }
2073 
2074     template <typename T>
2075     static constexpr DataType::Type GetTypeFromCType(bool support_int32 = false)
2076     {
2077         // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-branch-clone)
2078         if constexpr (std::is_integral_v<T>) {
2079             if (support_int32 && sizeof(T) == sizeof(uint32_t)) {
2080                 return DataType::INT32;
2081             }
2082             return DataType::INT64;
2083             // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2084         } else if constexpr (std::is_same_v<T, float>) {
2085             return DataType::FLOAT32;
2086             // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2087         } else if constexpr (std::is_same_v<T, double>) {
2088             return DataType::FLOAT64;
2089             // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2090         } else if constexpr (std::is_same_v<T, DataType::Any>) {
2091             return DataType::ANY;
2092         }
2093         return DataType::NO_TYPE;
2094     }
2095 
2096     inline bool IsEqualConst(double value, [[maybe_unused]] bool support_int32 = false)
2097     {
2098         return IsEqualConst(DataType::FLOAT64, bit_cast<uint64_t, double>(value));
2099     }
2100     inline bool IsEqualConst(float value, [[maybe_unused]] bool support_int32 = false)
2101     {
2102         return IsEqualConst(DataType::FLOAT32, bit_cast<uint32_t, float>(value));
2103     }
2104     inline bool IsEqualConst(DataType::Any value, [[maybe_unused]] bool support_int32 = false)
2105     {
2106         return IsEqualConst(DataType::ANY, value.Raw());
2107     }
IsEqualConst(DataType::Type type,uint64_t value)2108     inline bool IsEqualConst(DataType::Type type, uint64_t value)
2109     {
2110         return GetType() == type && value_ == value;
2111     }
2112     template <typename T>
2113     inline bool IsEqualConst(T value, bool support_int32 = false)
2114     {
2115         static_assert(GetTypeFromCType<T>() == DataType::INT64);
2116         if (support_int32 && sizeof(T) == sizeof(uint32_t)) {
2117             return (GetType() == DataType::INT32 && static_cast<int32_t>(value_) == static_cast<int32_t>(value));
2118         }
2119         return (GetType() == DataType::INT64 && value_ == static_cast<uint64_t>(value));
2120     }
2121 
2122     inline bool IsEqualConstAllTypes(int64_t value, bool support_int32 = false)
2123     {
2124         return IsEqualConst(value, support_int32) || IsEqualConst(static_cast<float>(value)) ||
2125                IsEqualConst(static_cast<double>(value));
2126     }
2127 
IsBoolConst()2128     bool IsBoolConst() const override
2129     {
2130         ASSERT(IsConst());
2131         return GetType() == DataType::INT64 && (GetIntValue() == 0 || GetIntValue() == 1);
2132     }
2133 
SetImmTableSlot(ImmTableSlot imm_slot)2134     void SetImmTableSlot(ImmTableSlot imm_slot)
2135     {
2136         imm_slot_ = imm_slot;
2137     }
2138 
GetImmTableSlot()2139     auto GetImmTableSlot() const
2140     {
2141         return imm_slot_;
2142     }
2143 
2144     bool DumpInputs(std::ostream *out) const override;
2145 
2146     Inst *Clone(const Graph *targetGraph) const override;
2147 
2148 private:
2149     uint64_t value_ {0};
2150     ConstantInst *next_const_ {nullptr};
2151     ImmTableSlot imm_slot_ {INVALID_IMM_TABLE_SLOT};
2152 };
2153 
2154 // Type describing the purpose of the SpillFillInst.
2155 // RegAlloc may use this information to preserve correct order of several SpillFillInst
2156 // instructions placed along each other in the graph.
2157 enum SpillFillType {
2158     UNKNOWN,
2159     INPUT_FILL,
2160     CONNECT_SPLIT_SIBLINGS,
2161     SPLIT_MOVE,
2162 };
2163 
2164 class SpillFillInst : public FixedInputsInst0 {
2165 public:
SpillFillInst(ArenaAllocator * allocator,Opcode opcode)2166     explicit SpillFillInst(ArenaAllocator *allocator, Opcode opcode)
2167         : FixedInputsInst0(opcode), spill_fills_(allocator->Adapter())
2168     {
2169     }
2170 
AddMove(Register src,Register dst,DataType::Type type)2171     void AddMove(Register src, Register dst, DataType::Type type)
2172     {
2173         AddSpillFill(Location::MakeRegister(src, type), Location::MakeRegister(dst, type), type);
2174     }
2175 
AddSpill(Register src,StackSlot dst,DataType::Type type)2176     void AddSpill(Register src, StackSlot dst, DataType::Type type)
2177     {
2178         AddSpillFill(Location::MakeRegister(src, type), Location::MakeStackSlot(dst), type);
2179     }
2180 
AddFill(StackSlot src,Register dst,DataType::Type type)2181     void AddFill(StackSlot src, Register dst, DataType::Type type)
2182     {
2183         AddSpillFill(Location::MakeStackSlot(src), Location::MakeRegister(dst, type), type);
2184     }
2185 
AddMemCopy(StackSlot src,StackSlot dst,DataType::Type type)2186     void AddMemCopy(StackSlot src, StackSlot dst, DataType::Type type)
2187     {
2188         AddSpillFill(Location::MakeStackSlot(src), Location::MakeStackSlot(dst), type);
2189     }
2190 
AddSpillFill(const SpillFillData & spill_fill)2191     void AddSpillFill(const SpillFillData &spill_fill)
2192     {
2193         spill_fills_.emplace_back(spill_fill);
2194     }
2195 
AddSpillFill(const Location & src,const Location & dst,DataType::Type type)2196     void AddSpillFill(const Location &src, const Location &dst, DataType::Type type)
2197     {
2198         spill_fills_.emplace_back(SpillFillData {src.GetKind(), dst.GetKind(), src.GetValue(), dst.GetValue(), type});
2199     }
2200 
GetSpillFills()2201     const ArenaVector<SpillFillData> &GetSpillFills() const
2202     {
2203         return spill_fills_;
2204     }
2205 
GetSpillFills()2206     ArenaVector<SpillFillData> &GetSpillFills()
2207     {
2208         return spill_fills_;
2209     }
2210 
GetSpillFill(size_t n)2211     const SpillFillData &GetSpillFill(size_t n) const
2212     {
2213         ASSERT(n < spill_fills_.size());
2214         return spill_fills_[n];
2215     }
2216 
GetSpillFill(size_t n)2217     SpillFillData &GetSpillFill(size_t n)
2218     {
2219         ASSERT(n < spill_fills_.size());
2220         return spill_fills_[n];
2221     }
2222 
RemoveSpillFill(size_t n)2223     void RemoveSpillFill(size_t n)
2224     {
2225         ASSERT(n < spill_fills_.size());
2226         spill_fills_.erase(spill_fills_.begin() + n);
2227     }
2228 
2229     // Get register number, holded by n-th spill-fill
GetInputReg(size_t n)2230     Register GetInputReg(size_t n) const
2231     {
2232         ASSERT(n < spill_fills_.size());
2233         ASSERT(spill_fills_[n].SrcType() == LocationType::REGISTER);
2234         return spill_fills_[n].SrcValue();
2235     }
2236 
ClearSpillFills()2237     void ClearSpillFills()
2238     {
2239         spill_fills_.clear();
2240     }
2241 
GetSpillFillType()2242     SpillFillType GetSpillFillType() const
2243     {
2244         return sf_type_;
2245     }
2246 
SetSpillFillType(SpillFillType type)2247     void SetSpillFillType(SpillFillType type)
2248     {
2249         sf_type_ = type;
2250     }
2251 
2252     bool DumpInputs(std::ostream *out) const override;
2253 
2254     Inst *Clone(const Graph *targetGraph) const override;
2255 
2256 private:
2257     ArenaVector<SpillFillData> spill_fills_;
2258     SpillFillType sf_type_ {UNKNOWN};
2259 };
2260 
2261 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2262 class ParameterInst : public Inst, public LocationDataMixin {
2263 public:
2264     using Inst::Inst;
2265 
ParameterInst(Opcode,uint16_t arg_number)2266     explicit ParameterInst(Opcode /* unused */, uint16_t arg_number) : Inst(Opcode::Parameter), arg_number_(arg_number)
2267     {
2268     }
GetArgNumber()2269     uint16_t GetArgNumber() const
2270     {
2271         return arg_number_;
2272     }
2273 
SetArgNumber(uint16_t arg_number)2274     void SetArgNumber(uint16_t arg_number)
2275     {
2276         arg_number_ = arg_number;
2277     }
2278 
2279     bool DumpInputs(std::ostream *out) const override;
2280 
2281     Inst *Clone(const Graph *targetGraph) const override;
2282 
2283 private:
2284     uint16_t arg_number_ {0};
2285 };
2286 
IsZeroConstant(const Inst * inst)2287 inline bool IsZeroConstant(const Inst *inst)
2288 {
2289     return inst->IsConst() && inst->GetType() == DataType::INT64 && inst->CastToConstant()->GetIntValue() == 0;
2290 }
2291 
IsZeroConstantOrNullPtr(const Inst * inst)2292 inline bool IsZeroConstantOrNullPtr(const Inst *inst)
2293 {
2294     return IsZeroConstant(inst);
2295 }
2296 
2297 /**
2298  * Phi instruction
2299  */
2300 class PhiInst : public AnyTypeMixin<DynamicInputsInst> {
2301 public:
2302     using BaseInst = AnyTypeMixin<DynamicInputsInst>;
2303     using BaseInst::BaseInst;
2304     /// Get basic block corresponding to given input index. Returned pointer to basic block, can't be nullptr
2305     BasicBlock *GetPhiInputBb(unsigned index);
GetPhiInputBb(unsigned index)2306     const BasicBlock *GetPhiInputBb(unsigned index) const
2307     {
2308         return (const_cast<PhiInst *>(this))->GetPhiInputBb(index);
2309     }
2310 
GetPhiInputBbNum(unsigned index)2311     uint32_t GetPhiInputBbNum(unsigned index) const
2312     {
2313         ASSERT(index < GetInputsCount());
2314         return GetDynamicOperands()->GetUser(index)->GetBbNum();
2315     }
2316 
SetPhiInputBbNum(unsigned index,uint32_t bb_num)2317     void SetPhiInputBbNum(unsigned index, uint32_t bb_num)
2318     {
2319         ASSERT(index < GetInputsCount());
2320         GetDynamicOperands()->GetUser(index)->SetBbNum(bb_num);
2321     }
2322 
Clone(const Graph * targetGraph)2323     Inst *Clone(const Graph *targetGraph) const override
2324     {
2325         auto clone = DynamicInputsInst::Clone(targetGraph);
2326         clone->CastToPhi()->SetAnyType(GetAnyType());
2327         return clone;
2328     }
2329 
GetAssumedAnyType()2330     AnyBaseType GetAssumedAnyType()
2331     {
2332         return GetAnyType();
2333     }
2334 
2335     /// Get input instruction corresponding to the given basic block, can't be null.
2336     Inst *GetPhiInput(BasicBlock *bb);
2337     Inst *GetPhiDataflowInput(BasicBlock *bb);
2338     bool DumpInputs(std::ostream *out) const override;
2339 
2340     // Get index of the given block in phi inputs
2341     size_t GetPredBlockIndex(const BasicBlock *block) const;
2342 
2343 protected:
2344     using FlagIsLive = LastField::NextFlag;
2345     using LastField = FlagIsLive;
2346 };
2347 
2348 /**
2349  * Immediate for SavaState:
2350  * value - constant value to be stored
2351  * vreg - virtual register number
2352  */
2353 struct SaveStateImm {
2354     uint64_t value;
2355     uint16_t vreg;
2356     DataType::Type type;
2357     bool is_acc;
2358 };
2359 
2360 /**
2361  * Frame state saving instruction
2362  * Aims to save pbc registers before calling something that can raise exception
2363  */
2364 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2365 class SaveStateInst : public DynamicInputsInst {
2366 public:
2367     using DynamicInputsInst::DynamicInputsInst;
2368 
2369     bool DumpInputs(std::ostream *out) const override;
2370 
SetVirtualRegister(size_t index,VirtualRegister reg)2371     void SetVirtualRegister(size_t index, VirtualRegister reg)
2372     {
2373         static_assert(sizeof(reg) <= sizeof(uintptr_t), "Consider passing the register by reference");
2374         ASSERT(index < GetInputsCount());
2375         GetDynamicOperands()->GetUser(index)->SetVirtualRegister(reg);
2376     }
2377 
GetVirtualRegister(size_t index)2378     VirtualRegister GetVirtualRegister(size_t index) const
2379     {
2380         ASSERT(index < GetInputsCount());
2381         return GetDynamicOperands()->GetUser(index)->GetVirtualRegister();
2382     }
2383 
Verify()2384     bool Verify() const
2385     {
2386         for (size_t i {0}; i < GetInputsCount(); ++i) {
2387             if (static_cast<uint16_t>(GetVirtualRegister(i)) == VirtualRegister::INVALID) {
2388                 return false;
2389             }
2390         }
2391         return true;
2392     }
2393 
RemoveNumericInputs()2394     bool RemoveNumericInputs()
2395     {
2396         size_t idx = 0;
2397         size_t inputs_count = GetInputsCount();
2398         bool removed = false;
2399         while (idx < inputs_count) {
2400             auto input_inst = GetInput(idx).GetInst();
2401             if (DataType::IsTypeNumeric(input_inst->GetType())) {
2402                 RemoveInput(idx);
2403                 inputs_count--;
2404                 removed = true;
2405             } else {
2406                 idx++;
2407             }
2408         }
2409         return removed;
2410     }
2411 
GetInputType(size_t index)2412     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2413     {
2414         ASSERT(index < GetInputsCount());
2415         return DataType::NO_TYPE;
2416     }
GetMethod()2417     auto GetMethod() const
2418     {
2419         return method_;
2420     }
SetMethod(void * method)2421     auto SetMethod(void *method)
2422     {
2423         method_ = method;
2424     }
2425 
2426     void AppendImmediate(uint64_t imm, uint16_t vreg, DataType::Type type, bool is_acc);
2427 
GetImmediates()2428     const ArenaVector<SaveStateImm> *GetImmediates() const
2429     {
2430         return immediates_;
2431     }
2432 
GetImmediate(size_t index)2433     const SaveStateImm &GetImmediate(size_t index) const
2434     {
2435         ASSERT(immediates_ != nullptr && index < immediates_->size());
2436         return (*immediates_)[index];
2437     }
2438 
2439     void AllocateImmediates(ArenaAllocator *allocator, size_t size = 0);
2440 
GetImmediatesCount()2441     size_t GetImmediatesCount() const
2442     {
2443         if (immediates_ == nullptr) {
2444             return 0;
2445         }
2446         return immediates_->size();
2447     }
2448 
SetRootsRegMaskBit(size_t reg)2449     void SetRootsRegMaskBit(size_t reg)
2450     {
2451         ASSERT(reg < roots_regs_mask_.size());
2452         roots_regs_mask_.set(reg);
2453     }
2454 
SetRootsStackMaskBit(size_t slot)2455     void SetRootsStackMaskBit(size_t slot)
2456     {
2457         if (roots_stack_mask_ != nullptr) {
2458             roots_stack_mask_->SetBit(slot);
2459         }
2460     }
2461 
GetRootsStackMask()2462     ArenaBitVector *GetRootsStackMask()
2463     {
2464         return roots_stack_mask_;
2465     }
2466 
GetRootsRegsMask()2467     auto &GetRootsRegsMask()
2468     {
2469         return roots_regs_mask_;
2470     }
2471 
CreateRootsStackMask(ArenaAllocator * allocator)2472     void CreateRootsStackMask(ArenaAllocator *allocator)
2473     {
2474         ASSERT(roots_stack_mask_ == nullptr);
2475         roots_stack_mask_ = allocator->New<ArenaBitVector>(allocator);
2476         roots_stack_mask_->Reset();
2477     }
2478 
2479     Inst *Clone(const Graph *targetGraph) const override;
2480 #ifndef NDEBUG
SetInputsWereDeleted()2481     void SetInputsWereDeleted()
2482     {
2483         SetField<FlagInputsWereDeleted>(true);
2484     }
2485 
GetInputsWereDeleted()2486     bool GetInputsWereDeleted()
2487     {
2488         return GetField<FlagInputsWereDeleted>();
2489     }
2490 #endif
2491 
2492 protected:
2493 #ifndef NDEBUG
2494     using FlagInputsWereDeleted = LastField::NextFlag;
2495     using LastField = FlagInputsWereDeleted;
2496 #endif
2497 
2498 private:
2499     ArenaVector<SaveStateImm> *immediates_ {nullptr};
2500     void *method_ {nullptr};
2501     /// If instruction is in the inlined graph, this variable points to the inliner's call instruction.
2502     ArenaBitVector *roots_stack_mask_ {nullptr};
2503     std::bitset<BITS_PER_UINT32> roots_regs_mask_ {0};
2504 };
2505 
2506 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2507 class IntrinsicInst : public InlinedInstMixin<DynamicInputsInst>, public InputTypesMixin {
2508 public:
2509     using Base = InlinedInstMixin<DynamicInputsInst>;
2510     using Base::Base;
2511     using IntrinsicId = RuntimeInterface::IntrinsicId;
2512 
IntrinsicInst(Opcode opcode,IntrinsicId intrinsic_id)2513     IntrinsicInst(Opcode opcode, IntrinsicId intrinsic_id) : Base(opcode), intrinsic_id_(intrinsic_id) {}
2514 
IntrinsicInst(Opcode opcode,DataType::Type type,uint32_t pc,IntrinsicId intrinsic_id)2515     IntrinsicInst(Opcode opcode, DataType::Type type, uint32_t pc, IntrinsicId intrinsic_id)
2516         : Base(opcode, type, pc), intrinsic_id_(intrinsic_id) {}
2517 
GetIntrinsicId()2518     IntrinsicId GetIntrinsicId() const
2519     {
2520         return intrinsic_id_;
2521     }
2522 
SetIntrinsicId(IntrinsicId intrinsic_id)2523     void SetIntrinsicId(IntrinsicId intrinsic_id)
2524     {
2525         intrinsic_id_ = intrinsic_id;
2526     }
2527 
GetInputType(size_t index)2528     DataType::Type GetInputType(size_t index) const override
2529     {
2530         ASSERT(input_types_ != nullptr);
2531         ASSERT(index < input_types_->size());
2532         ASSERT(index < GetInputsCount());
2533         return (*input_types_)[index];
2534     }
2535 
GetImms()2536     const ArenaVector<uint32_t> &GetImms()
2537     {
2538         return *imms_;
2539     }
2540 
GetImms()2541     const ArenaVector<uint32_t> &GetImms() const
2542     {
2543         return *imms_;
2544     }
2545 
HasImms()2546     bool HasImms() const
2547     {
2548         return imms_ != nullptr;
2549     }
2550 
AddImm(ArenaAllocator * allocator,uint32_t imm)2551     void AddImm(ArenaAllocator *allocator, uint32_t imm)
2552     {
2553         if (imms_ == nullptr) {
2554             imms_ = allocator->New<ArenaVector<uint32_t>>(allocator->Adapter());
2555         }
2556         imms_->push_back(imm);
2557     }
2558 
2559     bool IsNativeCall() const;
2560 
HasArgumentsOnStack()2561     bool HasArgumentsOnStack() const
2562     {
2563         return GetField<ArgumentsOnStack>();
2564     }
2565 
SetArgumentsOnStack()2566     void SetArgumentsOnStack()
2567     {
2568         SetField<ArgumentsOnStack>(true);
2569     }
2570 
2571     Inst *Clone(const Graph *targetGraph) const override;
2572 
CanBeInlined()2573     bool CanBeInlined()
2574     {
2575         return IsInlined();
2576     }
2577 
SetRelocate()2578     void SetRelocate()
2579     {
2580         SetField<Relocate>(true);
2581     }
2582 
GetRelocate()2583     bool GetRelocate() const
2584     {
2585         return GetField<Relocate>();
2586     }
2587 
2588     void DumpOpcode(std::ostream *out) const override;
2589 
2590 protected:
2591     using ArgumentsOnStack = LastField::NextFlag;
2592     using Relocate = ArgumentsOnStack::NextFlag;
2593     using LastField = Relocate;
2594 
2595 private:
2596     std::string GetIntrinsicOpcodeName() const;
2597 
2598     IntrinsicId intrinsic_id_ {RuntimeInterface::IntrinsicId::COUNT};
2599     ArenaVector<uint32_t> *imms_ {nullptr};  // record imms appeared in intrinsics
2600 };
2601 
2602 #include <ecma_intrinsics_enum.inl>
2603 
2604 /**
2605  * Cmp instruction
2606  */
2607 class CmpInst : public InstWithOperandsType<FixedInputsInst2> {
2608 public:
2609     using BaseInst = InstWithOperandsType<FixedInputsInst2>;
2610     using BaseInst::BaseInst;
2611 
IsFcmpg()2612     bool IsFcmpg() const
2613     {
2614         ASSERT(DataType::IsFloatType(GetOperandsType()));
2615         return GetField<Fcmpg>();
2616     }
IsFcmpl()2617     bool IsFcmpl() const
2618     {
2619         ASSERT(DataType::IsFloatType(GetOperandsType()));
2620         return !GetField<Fcmpg>();
2621     }
SetFcmpg()2622     void SetFcmpg()
2623     {
2624         ASSERT(DataType::IsFloatType(GetOperandsType()));
2625         SetField<Fcmpg>(true);
2626     }
SetFcmpg(bool v)2627     void SetFcmpg(bool v)
2628     {
2629         ASSERT(DataType::IsFloatType(GetOperandsType()));
2630         SetField<Fcmpg>(v);
2631     }
SetFcmpl()2632     void SetFcmpl()
2633     {
2634         ASSERT(DataType::IsFloatType(GetOperandsType()));
2635         SetField<Fcmpg>(false);
2636     }
SetFcmpl(bool v)2637     void SetFcmpl(bool v)
2638     {
2639         ASSERT(DataType::IsFloatType(GetOperandsType()));
2640         SetField<Fcmpg>(!v);
2641     }
2642 
GetInputType(size_t index)2643     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2644     {
2645         ASSERT(index < GetInputsCount());
2646         return GetOperandsType();
2647     }
2648 
2649     void SetVnObject(VnObject *vn_obj) override;
2650 
2651     void DumpOpcode(std::ostream *out) const override;
2652 
2653     Inst *Clone(const Graph *targetGraph) const override;
2654 
2655 protected:
2656     using Fcmpg = LastField::NextFlag;
2657     using LastField = Fcmpg;
2658 };
2659 
2660 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2661 class LoadFromPool : public NeedBarrierMixin<FixedInputsInst1>, public TypeIdMixin {
2662 public:
2663     using Base = NeedBarrierMixin<FixedInputsInst1>;
2664     using Base::Base;
2665 
GetInputType(size_t index)2666     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2667     {
2668         ASSERT(index < GetInputsCount());
2669         return DataType::NO_TYPE;
2670     }
2671 
2672     void DumpOpcode(std::ostream *out) const override;
2673 
Clone(const Graph * targetGraph)2674     Inst *Clone(const Graph *targetGraph) const override
2675     {
2676         auto clone = FixedInputsInst::Clone(targetGraph);
2677         static_cast<LoadFromPool *>(clone)->SetTypeId(GetTypeId());
2678         static_cast<LoadFromPool *>(clone)->SetMethod(GetMethod());
2679         return clone;
2680     }
2681 };
2682 
2683 /**
2684  * Conditional jump instruction
2685  */
2686 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2687 class IfInst : public InstWithOperandsType<ConditionMixin<FixedInputsInst2>> {
2688 public:
2689     using Base = InstWithOperandsType<ConditionMixin<FixedInputsInst2>>;
2690     using Base::Base;
2691 
IfInst(Opcode opcode,DataType::Type type,uint32_t pc,ConditionCode cc)2692     IfInst(Opcode opcode, DataType::Type type, uint32_t pc, ConditionCode cc) : Base(opcode, type, pc)
2693     {
2694         SetCc(cc);
2695     }
2696 
GetInputType(size_t index)2697     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2698     {
2699         ASSERT(index < GetInputsCount());
2700         return GetOperandsType();
2701     }
2702 
2703     void DumpOpcode(std::ostream *out) const override;
2704 
2705     void SetVnObject(VnObject *vn_obj) override;
2706 
2707     Inst *Clone(const Graph *targetGraph) const override;
2708 
SetMethod(RuntimeInterface::MethodPtr method)2709     void SetMethod(RuntimeInterface::MethodPtr method)
2710     {
2711         method_ = method;
2712     }
2713 
GetMethod()2714     RuntimeInterface::MethodPtr GetMethod() const
2715     {
2716         return method_;
2717     }
2718 
2719 private:
2720     RuntimeInterface::MethodPtr method_ {nullptr};
2721 };
2722 
2723 /**
2724  * IfImm instruction with immediate
2725  */
2726 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2727 class IfImmInst : public InstWithOperandsType<ConditionMixin<FixedInputsInst1>>, public ImmediateMixin {
2728 public:
2729     using Base = InstWithOperandsType<ConditionMixin<FixedInputsInst1>>;
2730     using Base::Base;
2731 
IfImmInst(Opcode opcode,DataType::Type type,uint32_t pc,ConditionCode cc,uint64_t imm)2732     IfImmInst(Opcode opcode, DataType::Type type, uint32_t pc, ConditionCode cc, uint64_t imm)
2733         : Base(opcode, type, pc), ImmediateMixin(imm)
2734     {
2735         SetCc(cc);
2736     }
2737 
GetInputType(size_t index)2738     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2739     {
2740         ASSERT(index < GetInputsCount());
2741         return GetOperandsType();
2742     }
2743 
2744     void DumpOpcode(std::ostream *out) const override;
2745     bool DumpInputs(std::ostream *out) const override;
2746     void SetVnObject(VnObject *vn_obj) override;
2747 
Clone(const Graph * targetGraph)2748     Inst *Clone(const Graph *targetGraph) const override
2749     {
2750         auto clone = FixedInputsInst::Clone(targetGraph);
2751         clone->CastToIfImm()->SetCc(GetCc());
2752         clone->CastToIfImm()->SetImm(GetImm());
2753         clone->CastToIfImm()->SetOperandsType(GetOperandsType());
2754         clone->CastToIfImm()->SetMethod(GetMethod());
2755         return clone;
2756     }
2757 
2758     BasicBlock *GetEdgeIfInputTrue();
2759     BasicBlock *GetEdgeIfInputFalse();
2760 
SetMethod(RuntimeInterface::MethodPtr method)2761     void SetMethod(RuntimeInterface::MethodPtr method)
2762     {
2763         method_ = method;
2764     }
2765 
GetMethod()2766     RuntimeInterface::MethodPtr GetMethod() const
2767     {
2768         return method_;
2769     }
2770 
2771 private:
2772     size_t GetTrueInputEdgeIdx();
2773     RuntimeInterface::MethodPtr method_ {nullptr};
2774 };
2775 
2776 /**
2777  * CatchPhiInst instruction
2778  */
2779 class CatchPhiInst : public DynamicInputsInst {
2780 public:
2781     using DynamicInputsInst::DynamicInputsInst;
2782 
GetThrowableInsts()2783     const ArenaVector<const Inst *> *GetThrowableInsts() const
2784     {
2785         return throw_insts_;
2786     }
2787 
GetThrowableInst(size_t i)2788     const Inst *GetThrowableInst(size_t i) const
2789     {
2790         ASSERT(throw_insts_ != nullptr && i < throw_insts_->size());
2791         return throw_insts_->at(i);
2792     }
2793 
2794     void AppendThrowableInst(const Inst *inst);
2795     void ReplaceThrowableInst(const Inst *old_inst, const Inst *new_inst);
2796     void RemoveInput(unsigned index) override;
2797 
IsAcc()2798     bool IsAcc() const
2799     {
2800         return GetField<IsAccFlag>();
2801     }
2802 
SetIsAcc()2803     void SetIsAcc()
2804     {
2805         SetField<IsAccFlag>(true);
2806     }
2807 
2808 protected:
2809     using IsAccFlag = LastField::NextFlag;
2810     using LastField = IsAccFlag;
2811 
2812 private:
GetThrowableInstIndex(const Inst * inst)2813     size_t GetThrowableInstIndex(const Inst *inst)
2814     {
2815         ASSERT(throw_insts_ != nullptr);
2816         auto it = std::find(throw_insts_->begin(), throw_insts_->end(), inst);
2817         ASSERT(it != throw_insts_->end());
2818         return std::distance(throw_insts_->begin(), it);
2819     }
2820 
2821 private:
2822     ArenaVector<const Inst *> *throw_insts_ {nullptr};
2823 };
2824 
2825 class TryInst : public FixedInputsInst0 {
2826 public:
2827     using FixedInputsInst0::FixedInputsInst0;
2828 
2829     void AppendCatchTypeId(uint32_t id, uint32_t catch_edge_index);
2830 
GetCatchTypeIds()2831     const ArenaVector<uint32_t> *GetCatchTypeIds() const
2832     {
2833         return catch_type_ids_;
2834     }
2835 
GetCatchEdgeIndexes()2836     const ArenaVector<uint32_t> *GetCatchEdgeIndexes() const
2837     {
2838         return catch_edge_indexes_;
2839     }
2840 
GetCatchTypeIdsCount()2841     size_t GetCatchTypeIdsCount() const
2842     {
2843         return (catch_type_ids_ == nullptr ? 0 : catch_type_ids_->size());
2844     }
2845 
2846     Inst *Clone(const Graph *targetGraph) const override;
2847 
SetTryEndBlock(BasicBlock * try_end_bb)2848     void SetTryEndBlock(BasicBlock *try_end_bb)
2849     {
2850         try_end_bb_ = try_end_bb;
2851     }
2852 
GetTryEndBlock()2853     BasicBlock *GetTryEndBlock() const
2854     {
2855         return try_end_bb_;
2856     }
2857 
2858 private:
2859     ArenaVector<uint32_t> *catch_type_ids_ {nullptr};
2860     ArenaVector<uint32_t> *catch_edge_indexes_ {nullptr};
2861     BasicBlock *try_end_bb_ {nullptr};
2862 };
2863 
2864 TryInst *GetTryBeginInst(const BasicBlock *try_begin_bb);
2865 
2866 template <typename InstType, typename... Args>
New(ArenaAllocator * allocator,Args &&...args)2867 InstType *Inst::New(ArenaAllocator *allocator, Args &&... args)
2868 {
2869     static_assert(alignof(InstType) >= alignof(uintptr_t));
2870     // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-branch-clone)
2871     if constexpr (std::is_same_v<InstType, SpillFillInst>) {
2872         auto data = reinterpret_cast<uintptr_t>(allocator->Alloc(sizeof(InstType), DEFAULT_ALIGNMENT));
2873         ASSERT(data != 0);
2874         return new (reinterpret_cast<void *>(data)) InstType(allocator, std::forward<Args>(args)...);
2875         // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2876     } else if constexpr (InstType::INPUT_COUNT == 0) {
2877         auto data = reinterpret_cast<uintptr_t>(allocator->Alloc(sizeof(InstType), DEFAULT_ALIGNMENT));
2878         ASSERT(data != 0);
2879         return new (reinterpret_cast<void *>(data)) InstType(std::forward<Args>(args)...);
2880         // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2881     } else if constexpr (InstType::INPUT_COUNT == MAX_STATIC_INPUTS) {
2882         constexpr size_t OPERANDS_SIZE = sizeof(DynamicOperands);
2883         static_assert((OPERANDS_SIZE % alignof(InstType)) == 0);
2884         auto data = reinterpret_cast<uintptr_t>(allocator->Alloc(OPERANDS_SIZE + sizeof(InstType), DEFAULT_ALIGNMENT));
2885         ASSERT(data != 0);
2886         auto inst = new (reinterpret_cast<void *>(data + OPERANDS_SIZE)) InstType(std::forward<Args>(args)...);
2887         [[maybe_unused]] auto operands = new (reinterpret_cast<void *>(data)) DynamicOperands(allocator);
2888         static_cast<Inst *>(inst)->SetField<InputsCount>(InstType::INPUT_COUNT);
2889         return inst;
2890     } else {  // NOLINT(readability-misleading-indentation)
2891         constexpr size_t OPERANDS_SIZE = sizeof(Operands<InstType::INPUT_COUNT>);
2892         constexpr auto ALIGNMENT {GetLogAlignment(alignof(Operands<InstType::INPUT_COUNT>))};
2893         static_assert((OPERANDS_SIZE % alignof(InstType)) == 0);
2894         auto data = reinterpret_cast<uintptr_t>(allocator->Alloc(OPERANDS_SIZE + sizeof(InstType), ALIGNMENT));
2895         ASSERT(data != 0);
2896         auto inst = new (reinterpret_cast<void *>(data + OPERANDS_SIZE)) InstType(std::forward<Args>(args)...);
2897         auto operands = new (reinterpret_cast<void *>(data)) Operands<InstType::INPUT_COUNT>;
2898         static_cast<Inst *>(inst)->SetField<InputsCount>(InstType::INPUT_COUNT);
2899         unsigned idx = InstType::INPUT_COUNT - 1;
2900         for (auto &user : operands->users) {
2901             new (&user) User(true, idx--, InstType::INPUT_COUNT);
2902         }
2903         return inst;
2904     }
2905 }
2906 
GetInput()2907 inline Inst *User::GetInput()
2908 {
2909     return GetInst()->GetInput(GetIndex()).GetInst();
2910 }
2911 
GetInput()2912 inline const Inst *User::GetInput() const
2913 {
2914     return GetInst()->GetInput(GetIndex()).GetInst();
2915 }
2916 
2917 inline std::ostream &operator<<(std::ostream &os, const Inst &inst)
2918 {
2919     inst.Dump(&os, false);
2920     return os;
2921 }
2922 
2923 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
2924 #define INST_DEF(opcode, base, ...)                 \
2925     inline const base *Inst::CastTo##opcode() const \
2926     {                                               \
2927         ASSERT(GetOpcode() == Opcode::opcode);      \
2928         return static_cast<const base *>(this);     \
2929     }
2930 OPCODE_LIST(INST_DEF)
2931 #undef INST_DEF
2932 
2933 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
2934 #define INST_DEF(opcode, base, ...)            \
2935     inline base *Inst::CastTo##opcode()        \
2936     {                                          \
2937         ASSERT(GetOpcode() == Opcode::opcode); \
2938         return static_cast<base *>(this);      \
2939     }
2940 OPCODE_LIST(INST_DEF)
2941 #undef INST_DEF
2942 }  // namespace panda::compiler
2943 
2944 #endif  // COMPILER_OPTIMIZER_IR_INST_H
2945