• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2025 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 <vector>
20 #include "constants.h"
21 #include "datatype.h"
22 
23 #ifdef PANDA_COMPILER_DEBUG_INFO
24 #include "debug_info.h"
25 #endif
26 
27 #include "ir-dyn-base-types.h"
28 #include "marker.h"
29 #include "utils/arena_containers.h"
30 #include "utils/span.h"
31 #include "utils/bit_field.h"
32 #include "utils/bit_utils.h"
33 #include "utils/bit_vector.h"
34 #include "macros.h"
35 #include "mem/arena_allocator.h"
36 #include "opcodes.h"
37 #include "compiler_options.h"
38 #include "runtime_interface.h"
39 #include "spill_fill_data.h"
40 #include "compiler/code_info/vreg_info.h"
41 namespace ark::compiler {
42 class Inst;
43 class BasicBlock;
44 class Graph;
45 class GraphVisitor;
46 class VnObject;
47 class SaveStateItem;
48 class LocationsInfo;
49 using InstVector = ArenaVector<Inst *>;
50 
51 template <size_t N>
52 class FixedInputsInst;
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 
81 ConditionCode GetInverseConditionCode(ConditionCode code);
82 ConditionCode InverseSignednessConditionCode(ConditionCode code);
83 bool IsSignedConditionCode(ConditionCode code);
84 PANDA_PUBLIC_API ConditionCode SwapOperandsConditionCode(ConditionCode code);
85 
86 template <typename T>
Compare(ConditionCode cc,T lhs,T rhs)87 bool Compare(ConditionCode cc, T lhs, T rhs)
88 {
89     using SignedT = std::make_signed_t<T>;
90     using UnsignedT = std::make_unsigned_t<T>;
91     auto lhsU = bit_cast<UnsignedT>(lhs);
92     auto rhsU = bit_cast<UnsignedT>(rhs);
93     auto lhsS = bit_cast<SignedT>(lhs);
94     auto rhsS = bit_cast<SignedT>(rhs);
95 
96     switch (cc) {
97         case ConditionCode::CC_EQ:
98             return lhsU == rhsU;
99         case ConditionCode::CC_NE:
100             return lhsU != rhsU;
101         case ConditionCode::CC_LT:
102             return lhsS < rhsS;
103         case ConditionCode::CC_LE:
104             return lhsS <= rhsS;
105         case ConditionCode::CC_GT:
106             return lhsS > rhsS;
107         case ConditionCode::CC_GE:
108             return lhsS >= rhsS;
109         case ConditionCode::CC_B:
110             return lhsU < rhsU;
111         case ConditionCode::CC_BE:
112             return lhsU <= rhsU;
113         case ConditionCode::CC_A:
114             return lhsU > rhsU;
115         case ConditionCode::CC_AE:
116             return lhsU >= rhsU;
117         case ConditionCode::CC_TST_EQ:
118             return (lhsU & rhsU) == 0;
119         case ConditionCode::CC_TST_NE:
120             return (lhsU & rhsU) != 0;
121         default:
122             UNREACHABLE();
123             return false;
124     }
125 }
126 
127 enum class Opcode {
128     INVALID = -1,
129 // NOLINTBEGIN(readability-identifier-naming)
130 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
131 #define INST_DEF(opcode, ...) opcode,
132     OPCODE_LIST(INST_DEF)
133 
134 #undef INST_DEF
135     // NOLINTEND(readability-identifier-naming)
136     NUM_OPCODES
137 };
138 
139 /// Convert opcode to its string representation
140 constexpr std::array<const char *const, static_cast<size_t>(Opcode::NUM_OPCODES)> OPCODE_NAMES = {
141 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
142 #define INST_DEF(opcode, ...) #opcode,
143     OPCODE_LIST(INST_DEF)
144 #undef INST_DEF
145 };
146 
GetOpcodeString(Opcode opc)147 constexpr const char *GetOpcodeString(Opcode opc)
148 {
149     ASSERT(static_cast<int>(opc) < static_cast<int>(Opcode::NUM_OPCODES));
150     return OPCODE_NAMES[static_cast<int>(opc)];
151 }
152 
153 /// Instruction flags. See `instrutions.yaml` section `flags` for more information.
154 namespace inst_flags {
155 namespace internal {
156 enum FlagsIndex {
157 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
158 #define FLAG_DEF(flag) flag##_INDEX,
159     FLAGS_LIST(FLAG_DEF)
160 #undef FLAG_DEF
161         FLAGS_COUNT
162 };
163 }  // namespace internal
164 
165 enum Flags : uint32_t {
166 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
167 #define FLAG_DEF(flag) flag = (1U << internal::flag##_INDEX),
168     FLAGS_LIST(FLAG_DEF)
169 #undef FLAG_DEF
170         FLAGS_COUNT = internal::FLAGS_COUNT,
171     NONE = 0
172 };
173 
GetFlagsMask(Opcode opcode)174 inline constexpr uintptr_t GetFlagsMask(Opcode opcode)
175 {
176 #define INST_DEF(OPCODE, BASE, FLAGS) (FLAGS),  // NOLINT(cppcoreguidelines-macro-usage)
177     // NOLINTNEXTLINE(hicpp-signed-bitwise)
178     constexpr std::array<uintptr_t, static_cast<int>(Opcode::NUM_OPCODES)> INST_FLAGS_TABLE = {OPCODE_LIST(INST_DEF)};
179 #undef INST_DEF
180     return INST_FLAGS_TABLE[static_cast<size_t>(opcode)];
181 }
182 }  // namespace inst_flags
183 
184 #ifndef NDEBUG
185 namespace inst_modes {
186 namespace internal {
187 enum ModeIndex {
188 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
189 #define MODE_DEF(mode) mode##_INDEX,
190     MODES_LIST(MODE_DEF)
191 #undef MODE_DEF
192         MODES_COUNT
193 };
194 }  // namespace internal
195 
196 enum Mode : uint8_t {
197 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
198 #define MODE_DEF(mode) mode = (1U << internal::mode##_INDEX),
199     MODES_LIST(MODE_DEF)
200 #undef MODE_DEF
201         MODES_COUNT = internal::MODES_COUNT,
202 };
203 
GetModesMask(Opcode opcode)204 inline constexpr uint8_t GetModesMask(Opcode opcode)
205 {
206     // NOLINTNEXTLINE(hicpp-signed-bitwise)
207     constexpr std::array<uint8_t, static_cast<int>(Opcode::NUM_OPCODES)> INST_MODES_TABLE = {INST_MODES_LIST};
208     return INST_MODES_TABLE[static_cast<size_t>(opcode)];
209 }
210 }  // namespace inst_modes
211 #endif
212 
213 namespace internal {
214 inline constexpr std::array<const char *, ShiftType::INVALID_SHIFT + 1> SHIFT_TYPE_NAMES = {"LSL", "LSR", "ASR", "ROR",
215                                                                                             "INVALID"};
216 }  // namespace internal
217 
GetShiftTypeStr(ShiftType type)218 inline const char *GetShiftTypeStr(ShiftType type)
219 {
220     ASSERT(type <= INVALID_SHIFT);
221     return internal::SHIFT_TYPE_NAMES[type];
222 }
223 
224 /// Describes type of the object produced by an instruction.
225 class ObjectTypeInfo {
226 public:
227     using ClassType = RuntimeInterface::ClassPtr;
228 
229     constexpr ObjectTypeInfo() = default;
ObjectTypeInfo(ClassType klass,bool isExact)230     ObjectTypeInfo(ClassType klass, bool isExact)
231         : class_(reinterpret_cast<uintptr_t>(klass) | static_cast<uintptr_t>(isExact))
232     {
233         ASSERT((reinterpret_cast<uintptr_t>(klass) & EXACT_MASK) == 0);
234     }
235 
236     bool operator==(const ObjectTypeInfo &other) const
237     {
238         return class_ == other.class_;
239     }
240 
241     bool operator!=(const ObjectTypeInfo &other) const
242     {
243         return class_ != other.class_;
244     }
245 
246     // NOLINTNEXTLINE(*-explicit-constructor)
247     operator bool() const
248     {
249         return IsValid();
250     }
251 
GetClass()252     ClassType GetClass() const
253     {
254         return reinterpret_cast<ClassType>(class_ & ~EXACT_MASK);
255     }
256 
IsExact()257     bool IsExact() const
258     {
259         return (class_ & EXACT_MASK) != 0;
260     }
261 
IsValid()262     bool IsValid() const
263     {
264         return class_ > 1;
265     }
266 
267     static const ObjectTypeInfo INVALID;
268     static const ObjectTypeInfo UNKNOWN;
269 
270 private:
ObjectTypeInfo(uintptr_t klass)271     explicit constexpr ObjectTypeInfo(uintptr_t klass) : class_(klass) {}
272 
273 private:
274     static constexpr uintptr_t EXACT_MASK = 1;
275     // Lowest bit in ClassPtr is always zero due to alignment, we set it to 1 if `klass` is the exact class
276     // of the object and to 0 if it is some superclass of that class
277     uintptr_t class_ = 0;
278 };
279 
280 using VRegType = VRegInfo::VRegType;
281 
282 /// Class for storing panda bytecode's virtual register
283 class VirtualRegister final {
284 public:
285     using ValueType = uint16_t;
286 
287     static constexpr unsigned BITS_FOR_VREG_TYPE = MinimumBitsToStore(VRegType::COUNT);
288     static constexpr unsigned BITS_FOR_VREG = (sizeof(ValueType) * BITS_PER_BYTE) - BITS_FOR_VREG_TYPE;
289 
290     static constexpr ValueType INVALID = std::numeric_limits<ValueType>::max() >> BITS_FOR_VREG_TYPE;
291     // This value we marked the virtual registers, that create in bridge for SS
292     static constexpr ValueType BRIDGE = INVALID - 1U;
293     static constexpr ValueType MAX_NUM_VIRT_REGS = BRIDGE - 2U;
294 
295     VirtualRegister() = default;
VirtualRegister(ValueType v,VRegType type)296     explicit VirtualRegister(ValueType v, VRegType type) : value_(v)
297     {
298         ASSERT(ValidNumVirtualReg(value_));
299         VRegTypeField::Set(type, &value_);
300     }
301 
uint16_t()302     explicit operator uint16_t() const
303     {
304         return value_;
305     }
306 
Value()307     ValueType Value() const
308     {
309         return ValueField::Get(value_);
310     }
311 
IsAccumulator()312     bool IsAccumulator() const
313     {
314         return VRegTypeField::Get(value_) == VRegType::ACC;
315     }
316 
IsEnv()317     bool IsEnv() const
318     {
319         return IsSpecialReg() && !IsAccumulator();
320     }
321 
IsSpecialReg()322     bool IsSpecialReg() const
323     {
324         return VRegTypeField::Get(value_) != VRegType::VREG;
325     }
326 
GetVRegType()327     VRegType GetVRegType() const
328     {
329         return static_cast<VRegType>(VRegTypeField::Get(value_));
330     }
331 
IsBridge()332     bool IsBridge() const
333     {
334         return ValueField::Get(value_) == BRIDGE;
335     }
336 
ValidNumVirtualReg(uint16_t num)337     static bool ValidNumVirtualReg(uint16_t num)
338     {
339         return num <= INVALID;
340     }
341 
342 private:
343     ValueType value_ {INVALID};
344 
345     using ValueField = BitField<unsigned, 0, BITS_FOR_VREG>;
346     using VRegTypeField = ValueField::NextField<unsigned, BITS_FOR_VREG_TYPE>;
347 };
348 
349 // How many bits will be used in Inst's bit fields for number of inputs.
350 constexpr size_t BITS_PER_INPUTS_NUM = 3;
351 // Maximum number of static inputs
352 constexpr size_t MAX_STATIC_INPUTS = (1U << BITS_PER_INPUTS_NUM) - 1;
353 
354 /// Currently Input class is just a wrapper for the Inst class.
355 class Input final {
356 public:
357     Input() = default;
Input(Inst * inst)358     explicit Input(Inst *inst) : inst_(inst) {}
359 
GetInst()360     Inst *GetInst()
361     {
362         return inst_;
363     }
GetInst()364     const Inst *GetInst() const
365     {
366         return inst_;
367     }
368 
GetPadding(Arch arch,uint32_t inputsCount)369     static inline uint8_t GetPadding(Arch arch, uint32_t inputsCount)
370     {
371         return static_cast<uint8_t>(!Is64BitsArch(arch) && inputsCount % 2U == 1U);
372     }
373 
374 private:
375     Inst *inst_ {nullptr};
376 };
377 
378 inline bool operator==(const Inst *lhs, const Input &rhs)
379 {
380     return lhs == rhs.GetInst();
381 }
382 
383 inline bool operator==(const Input &lhs, const Inst *rhs)
384 {
385     return lhs.GetInst() == rhs;
386 }
387 
388 inline bool operator==(const Input &lhs, const Input &rhs)
389 {
390     return lhs.GetInst() == rhs.GetInst();
391 }
392 
393 inline bool operator!=(const Inst *lhs, const Input &rhs)
394 {
395     return lhs != rhs.GetInst();
396 }
397 
398 inline bool operator!=(const Input &lhs, const Inst *rhs)
399 {
400     return lhs.GetInst() != rhs;
401 }
402 
403 inline bool operator!=(const Input &lhs, const Input &rhs)
404 {
405     return lhs.GetInst() != rhs.GetInst();
406 }
407 
408 /**
409  * User is a intrusive list node, thus it stores pointers to next and previous users.
410  * Also user has properties value to determine owner instruction and corresponding index of the input.
411  */
412 class User final {
413 public:
414     User() = default;
User(bool isStatic,unsigned index,unsigned size)415     User(bool isStatic, unsigned index, unsigned size)
416         : properties_(IsStaticFlag::Encode(isStatic) | IndexField::Encode(index) | SizeField::Encode(size) |
417                       BbNumField::Encode(BbNumField::MaxValue()))
418     {
419         ASSERT(index < 1U << (BITS_FOR_INDEX - 1U));
420         ASSERT(size < 1U << (BITS_FOR_SIZE - 1U));
421     }
422     ~User() = default;
423 
424     // Copy/move semantic is disabled because we use tricky pointer arithmetic based on 'this' value
425     NO_COPY_SEMANTIC(User);
426     NO_MOVE_SEMANTIC(User);
427 
428     PANDA_PUBLIC_API Inst *GetInst();
GetInst()429     const Inst *GetInst() const
430     {
431         return const_cast<User *>(this)->GetInst();
432     }
433 
434     Inst *GetInput();
435     const Inst *GetInput() const;
436 
IsDynamic()437     bool IsDynamic() const
438     {
439         return !IsStaticFlag::Decode(properties_);
440     }
GetIndex()441     unsigned GetIndex() const
442     {
443         return IndexField::Decode(properties_);
444     }
GetSize()445     unsigned GetSize() const
446     {
447         return SizeField::Decode(properties_);
448     }
449 
GetVirtualRegister()450     VirtualRegister GetVirtualRegister() const
451     {
452         ASSERT(IsDynamic());
453         return VirtualRegister(VRegField::Decode(properties_),
454                                static_cast<VRegType>(VRegTypeField::Decode(properties_)));
455     }
456 
SetVirtualRegister(VirtualRegister reg)457     void SetVirtualRegister(VirtualRegister reg)
458     {
459         static_assert(sizeof(reg) <= sizeof(uintptr_t), "Consider passing the register by reference");
460         ASSERT(IsDynamic());
461         VRegField::Set(reg.Value(), &properties_);
462         VRegTypeField::Set(reg.GetVRegType(), &properties_);
463     }
464 
GetBbNum()465     uint32_t GetBbNum() const
466     {
467         ASSERT(IsDynamic());
468         return BbNumField::Decode(properties_);
469     }
470 
SetBbNum(uint32_t bbNum)471     void SetBbNum(uint32_t bbNum)
472     {
473         ASSERT(IsDynamic());
474         BbNumField::Set(bbNum, &properties_);
475     }
476 
GetNext()477     auto GetNext() const
478     {
479         return next_;
480     }
481 
GetPrev()482     auto GetPrev() const
483     {
484         return prev_;
485     }
486 
SetNext(User * next)487     void SetNext(User *next)
488     {
489         next_ = next;
490     }
491 
SetPrev(User * prev)492     void SetPrev(User *prev)
493     {
494         prev_ = prev;
495     }
496 
Remove()497     void Remove()
498     {
499         if (prev_ != nullptr) {
500             prev_->next_ = next_;
501         }
502         if (next_ != nullptr) {
503             next_->prev_ = prev_;
504         }
505     }
506 
507 private:
508     static constexpr unsigned BITS_FOR_INDEX = 21;
509     static constexpr unsigned BITS_FOR_SIZE = BITS_FOR_INDEX;
510     static constexpr unsigned BITS_FOR_BB_NUM = 20;
511     using IndexField = BitField<unsigned, 0, BITS_FOR_INDEX>;
512     using SizeField = IndexField::NextField<unsigned, BITS_FOR_SIZE>;
513     using IsStaticFlag = SizeField::NextFlag;
514 
515     using BbNumField = IsStaticFlag::NextField<uint32_t, BITS_FOR_BB_NUM>;
516 
517     using VRegField = IsStaticFlag::NextField<unsigned, VirtualRegister::BITS_FOR_VREG>;
518     using VRegTypeField = VRegField::NextField<unsigned, VirtualRegister::BITS_FOR_VREG_TYPE>;
519 
520     uint64_t properties_ {0};
521     User *next_ {nullptr};
522     User *prev_ {nullptr};
523 };
524 
525 /**
526  * List of users. Intended for range loop.
527  * @tparam T should be User or const User
528  */
529 template <typename T>
530 class UserList {
531     template <typename U>
532     struct UserIterator {
533         // NOLINTBEGIN(readability-identifier-naming)
534         using iterator_category = std::forward_iterator_tag;
535         using value_type = U;
536         using difference_type = std::ptrdiff_t;
537         using pointer = value_type *;
538         using reference = value_type &;
539         // NOLINTEND(readability-identifier-naming)
540 
541         UserIterator() = default;
UserIteratorUserIterator542         explicit UserIterator(U *u) : user_(u) {}
543 
544         UserIterator &operator++()
545         {
546             user_ = user_->GetNext();
547             return *this;
548         }
549         bool operator!=(const UserIterator &other)
550         {
551             return user_ != other.user_;
552         }
553         bool operator==(const UserIterator &other)
554         {
555             return user_ == other.user_;
556         }
557         U &operator*()
558         {
559             return *user_;
560         }
561         U *operator->()
562         {
563             return user_;
564         }
565 
566     private:
567         U *user_ {nullptr};
568     };
569 
570 public:
571     using Iterator = UserIterator<T>;
572     using ConstIterator = UserIterator<const T>;
573     using PointerType = std::conditional_t<std::is_const_v<T>, T *const *, T **>;
574 
UserList(PointerType head)575     explicit UserList(PointerType head) : head_(head) {}
576 
577     // NOLINTNEXTLINE(readability-identifier-naming)
begin()578     Iterator begin()
579     {
580         return Iterator(*head_);
581     }
582     // NOLINTNEXTLINE(readability-identifier-naming)
end()583     Iterator end()
584     {
585         return Iterator(nullptr);
586     }
587     // NOLINTNEXTLINE(readability-identifier-naming)
begin()588     ConstIterator begin() const
589     {
590         return ConstIterator(*head_);
591     }
592     // NOLINTNEXTLINE(readability-identifier-naming)
end()593     ConstIterator end() const
594     {
595         return ConstIterator(nullptr);
596     }
Empty()597     bool Empty() const
598     {
599         return *head_ == nullptr;
600     }
Front()601     T &Front()
602     {
603         return **head_;
604     }
Front()605     const T &Front() const
606     {
607         return **head_;
608     }
609 
610 private:
611     PointerType head_ {nullptr};
612 };
613 
614 inline bool operator==(const User &lhs, const User &rhs)
615 {
616     return lhs.GetInst() == rhs.GetInst();
617 }
618 
619 /**
620  * Operands class for instructions with fixed inputs count.
621  * Actually, this class do absolutely nothing except that we can get sizeof of it when allocating memory.
622  */
623 template <int N>
624 struct Operands {
625     static_assert(N < MAX_STATIC_INPUTS, "Invalid inputs number");
626 
627     std::array<User, N> users;
628     std::array<Input, N> inputs;
629 };
630 
631 enum InputOrd { INP0 = 0, INP1 = 1, INP2 = 2, INP3 = 3 };
632 
633 /**
634  * Specialized version for instructions with variable inputs count.
635  * Users and inputs are stored outside of this class.
636  */
637 class DynamicOperands {
638 public:
DynamicOperands(ArenaAllocator * allocator)639     explicit DynamicOperands(ArenaAllocator *allocator) : allocator_(allocator) {}
640 
Users()641     User *Users()
642     {
643         return users_;
644     }
645 
Inputs()646     NO_UB_SANITIZE Input *Inputs()
647     {
648         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
649         return reinterpret_cast<Input *>(users_ + capacity_) + 1;
650     }
651 
652     /// Append new input (and user accordingly)
653     PANDA_PUBLIC_API unsigned Append(Inst *inst);
654 
655     /// Remove input and user with index `index`.
656     PANDA_PUBLIC_API void Remove(unsigned index);
657 
658     /// Reallocate inputs/users storage to a new one with specified capacity.
659     void Reallocate(size_t newCapacity = 0);
660 
661     /// Get instruction to which these operands belongs to.
GetOwnerInst()662     Inst *GetOwnerInst() const
663     {
664         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
665         return reinterpret_cast<Inst *>(const_cast<DynamicOperands *>(this) + 1);
666     }
667 
GetUser(unsigned index)668     User *GetUser(unsigned index)
669     {
670         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
671         return &users_[capacity_ - index - 1];
672     }
673 
GetInput(unsigned index)674     Input *GetInput(unsigned index)
675     {
676         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
677         return &Inputs()[index];
678     }
679 
SetInput(unsigned index,Input input)680     void SetInput(unsigned index, Input input)
681     {
682         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
683         Inputs()[index] = input;
684     }
685 
Size()686     size_t Size() const
687     {
688         return size_;
689     }
690 
691 private:
692     User *users_ {nullptr};
693     size_t size_ {0};
694     size_t capacity_ {0};
695     ArenaAllocator *allocator_ {nullptr};
696 };
697 
698 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
699 
700 /// Base class for all instructions, should not be instantiated directly
701 class InstBase {
702     NO_COPY_SEMANTIC(InstBase);
703     NO_MOVE_SEMANTIC(InstBase);
704 
705 public:
706     virtual ~InstBase() = default;
707 
708 public:
delete(void * unused,size_t size)709     ALWAYS_INLINE void operator delete([[maybe_unused]] void *unused, [[maybe_unused]] size_t size)
710     {
711         UNREACHABLE();
712     }
new(size_t size,void * ptr)713     ALWAYS_INLINE void *operator new([[maybe_unused]] size_t size, void *ptr) noexcept
714     {
715         return ptr;
716     }
delete(void * unused1,void * unused2)717     ALWAYS_INLINE void operator delete([[maybe_unused]] void *unused1, [[maybe_unused]] void *unused2) noexcept {}
718 
719     void *operator new([[maybe_unused]] size_t size) = delete;
720 
721 protected:
722     InstBase() = default;
723 };
724 
725 /// Base instruction class
726 class PANDA_PUBLIC_API Inst : public MarkerSet, public InstBase {
727 public:
728     // Used for SFINAE for inputs deduction during CreateInst-calls
729     template <typename... Ds>
730     using CheckBase = std::enable_if_t<std::conjunction_v<std::is_convertible<Ds, Inst *>...>>;
731 
732 public:
733     /**
734      * Create new instruction. All instructions must be created with this method.
735      * It allocates additional space before Inst object for def-use structures.
736      *
737      * @tparam InstType - concrete type of instruction, shall be derived from Inst
738      * @tparam Args - constructor arguments types
739      * @param allocator - allocator for memory allocating
740      * @param args - constructor arguments
741      * @return - new instruction
742      */
743     template <typename InstType, typename... Args>
744     [[nodiscard]] static InstType *New(ArenaAllocator *allocator, Args &&...args);
745 
INST_CAST_TO_DECL()746     INST_CAST_TO_DECL()
747 
748     // Methods for instruction chaining inside basic blocks.
749     Inst *GetNext()
750     {
751         return next_;
752     }
GetNext()753     const Inst *GetNext() const
754     {
755         return next_;
756     }
GetPrev()757     Inst *GetPrev()
758     {
759         return prev_;
760     }
GetPrev()761     const Inst *GetPrev() const
762     {
763         return prev_;
764     }
SetNext(Inst * next)765     void SetNext(Inst *next)
766     {
767         next_ = next;
768     }
SetPrev(Inst * prev)769     void SetPrev(Inst *prev)
770     {
771         prev_ = prev;
772     }
773 
774     // Id accessors
GetId()775     auto GetId() const
776     {
777         return id_;
778     }
SetId(int id)779     void SetId(int id)
780     {
781         id_ = id;
782     }
783 
GetLinearNumber()784     auto GetLinearNumber() const
785     {
786         return linearNumber_;
787     }
SetLinearNumber(LinearNumber number)788     void SetLinearNumber(LinearNumber number)
789     {
790         linearNumber_ = number;
791     }
792 
GetCloneNumber()793     auto GetCloneNumber() const
794     {
795         return cloneNumber_;
796     }
SetCloneNumber(int32_t number)797     void SetCloneNumber(int32_t number)
798     {
799         cloneNumber_ = number;
800     }
801 
802     // Opcode accessors
GetOpcode()803     Opcode GetOpcode() const
804     {
805         return opcode_;
806     }
SetOpcode(Opcode opcode)807     void SetOpcode(Opcode opcode)
808     {
809         opcode_ = opcode;
810         SetField<FieldFlags>(inst_flags::GetFlagsMask(opcode));
811     }
GetOpcodeStr()812     const char *GetOpcodeStr() const
813     {
814         return GetOpcodeString(GetOpcode());
815     }
816 
817     // Bytecode PC accessors
GetPc()818     uint32_t GetPc() const
819     {
820         return pc_;
821     }
SetPc(uint32_t pc)822     void SetPc(uint32_t pc)
823     {
824         pc_ = pc;
825     }
826 
827     // Type accessors
GetType()828     DataType::Type GetType() const
829     {
830         return FieldType::Get(bitFields_);
831     }
SetType(DataType::Type type)832     void SetType(DataType::Type type)
833     {
834         FieldType::Set(type, &bitFields_);
835     }
HasType()836     bool HasType() const
837     {
838         return GetType() != DataType::Type::NO_TYPE;
839     }
840 
GetAnyType()841     virtual AnyBaseType GetAnyType() const
842     {
843         return AnyBaseType::UNDEFINED_TYPE;
844     }
845 
846     // Parent basic block accessors
GetBasicBlock()847     BasicBlock *GetBasicBlock()
848     {
849         return bb_;
850     }
GetBasicBlock()851     const BasicBlock *GetBasicBlock() const
852     {
853         return bb_;
854     }
SetBasicBlock(BasicBlock * bb)855     void SetBasicBlock(BasicBlock *bb)
856     {
857         bb_ = bb;
858     }
859 
860     // Instruction properties getters
IsControlFlow()861     bool IsControlFlow() const
862     {
863         return GetFlag(inst_flags::CF);
864     }
IsVirtualCall()865     bool IsVirtualCall() const
866     {
867         return GetOpcode() == Opcode::CallVirtual || GetOpcode() == Opcode::CallResolvedVirtual;
868     }
IsStaticCall()869     bool IsStaticCall() const
870     {
871         return GetOpcode() == Opcode::CallStatic || GetOpcode() == Opcode::CallResolvedStatic;
872     }
IsNativeApiCall()873     bool IsNativeApiCall() const
874     {
875         return GetOpcode() == Opcode::CallNative;
876     }
IsReferenceForNativeApiCall()877     bool IsReferenceForNativeApiCall() const
878     {
879         if (GetType() != DataType::REFERENCE) {
880             return false;
881         }
882 
883         for (const auto &user : GetUsers()) {
884             if (user.GetInst()->GetOpcode() == Opcode::WrapObjectNative) {
885                 return true;
886             }
887         }
888         return false;
889     }
IsMethodResolver()890     bool IsMethodResolver() const
891     {
892         return opcode_ == Opcode::ResolveVirtual || opcode_ == Opcode::ResolveStatic ||
893                opcode_ == Opcode::ResolveByName;
894     }
IsFieldResolver()895     bool IsFieldResolver() const
896     {
897         return opcode_ == Opcode::ResolveObjectField || opcode_ == Opcode::ResolveObjectFieldStatic;
898     }
IsResolver()899     bool IsResolver() const
900     {
901         return IsFieldResolver() || IsMethodResolver();
902     }
IsInitObject()903     bool IsInitObject() const
904     {
905         return GetOpcode() == Opcode::InitObject;
906     }
IsMultiArray()907     bool IsMultiArray() const
908     {
909         return GetOpcode() == Opcode::MultiArray;
910     }
IsDynamicCall()911     bool IsDynamicCall() const
912     {
913         return GetOpcode() == Opcode::CallDynamic;
914     }
IsIndirectCall()915     bool IsIndirectCall() const
916     {
917         return GetOpcode() == Opcode::CallIndirect;
918     }
IsIntrinsic()919     bool IsIntrinsic() const
920     {
921         /* Opcode::Builtin is left for backward compatibility, the compiler
922          * itself should never generate an instruction with such an opcode */
923         return GetOpcode() == Opcode::Intrinsic || GetOpcode() == Opcode::Builtin;
924     }
925 
926     /* IsBuiltin actual meaning would be "it MAY be inlined by the CG"
927      * however, since we do not make guarantees about whether it will
928      * actually be inlined nor the safety of the intrinsic itself, just
929      * checking the instruction flags to see if it is suitable for any
930      * particular optimization seems to be a better approach
931      */
IsBuiltin()932     static bool IsBuiltin()
933     {
934         return false;
935     }
936 
IsCall()937     bool IsCall() const
938     {
939         return GetFlag(inst_flags::CALL) && !IsIntrinsic();
940     }
941 
IsCallOrIntrinsic()942     bool IsCallOrIntrinsic() const
943     {
944         return GetFlag(inst_flags::CALL);
945     }
946 
IsSpillFill()947     bool IsSpillFill() const
948     {
949         return GetOpcode() == Opcode::SpillFill;
950     }
951 
IsNullCheck()952     bool IsNullCheck() const
953     {
954         return GetOpcode() == Opcode::NullCheck;
955     }
956 
IsNullPtr()957     bool IsNullPtr() const
958     {
959         return GetOpcode() == Opcode::NullPtr;
960     }
961 
IsLoadUniqueObject()962     bool IsLoadUniqueObject() const
963     {
964         return GetOpcode() == Opcode::LoadUniqueObject;
965     }
966 
IsReturn()967     bool IsReturn() const
968     {
969         return GetOpcode() == Opcode::Return || GetOpcode() == Opcode::ReturnI || GetOpcode() == Opcode::ReturnVoid;
970     }
971 
IsUnresolved()972     bool IsUnresolved() const
973     {
974         switch (GetOpcode()) {
975             case Opcode::UnresolvedLoadAndInitClass:
976             case Opcode::UnresolvedLoadType:
977             case Opcode::UnresolvedStoreStatic:
978                 return true;
979             default:
980                 return false;
981         }
982     }
WithGluedInsts()983     bool WithGluedInsts() const
984     {
985         return GetOpcode() == Opcode::LoadArrayPair || GetOpcode() == Opcode::LoadArrayPairI ||
986                GetOpcode() == Opcode::LoadObjectPair;
987     }
IsLoad()988     bool IsLoad() const
989     {
990         return GetFlag(inst_flags::LOAD);
991     }
IsStore()992     bool IsStore() const
993     {
994         return GetFlag(inst_flags::STORE);
995     }
996     bool IsAccRead() const;
997     bool IsAccWrite() const;
IsMemory()998     bool IsMemory() const
999     {
1000         return IsLoad() || IsStore();
1001     }
CanThrow()1002     bool CanThrow() const
1003     {
1004         return GetFlag(inst_flags::CAN_THROW);
1005     }
IsCheck()1006     bool IsCheck() const
1007     {
1008         return GetFlag(inst_flags::IS_CHECK);
1009     }
RequireState()1010     bool RequireState() const
1011     {
1012         return GetFlag(inst_flags::REQUIRE_STATE);
1013     }
1014     // Returns true if the instruction not removable in DCE
IsNotRemovable()1015     bool IsNotRemovable() const
1016     {
1017         return GetFlag(inst_flags::NO_DCE);
1018     }
1019 
1020     // Returns true if the instruction doesn't have destination register
NoDest()1021     bool NoDest() const
1022     {
1023         return GetFlag(inst_flags::PSEUDO_DST) || GetFlag(inst_flags::NO_DST) || GetType() == DataType::VOID;
1024     }
1025 
HasPseudoDestination()1026     bool HasPseudoDestination() const
1027     {
1028         return GetFlag(inst_flags::PSEUDO_DST);
1029     }
1030 
HasImplicitRuntimeCall()1031     bool HasImplicitRuntimeCall() const
1032     {
1033         return GetFlag(inst_flags::IMPLICIT_RUNTIME_CALL);
1034     }
1035 
CanDeoptimize()1036     bool CanDeoptimize() const
1037     {
1038         return GetFlag(inst_flags::CAN_DEOPTIMIZE);
1039     }
1040 
RequireTmpReg()1041     bool RequireTmpReg() const
1042     {
1043         return GetFlag(inst_flags::REQUIRE_TMP);
1044     }
1045 
1046     // Returns true if the instruction is low-level
IsLowLevel()1047     bool IsLowLevel() const
1048     {
1049         return GetFlag(inst_flags::LOW_LEVEL);
1050     }
1051 
1052     // Returns true if the instruction not hoistable
IsNotHoistable()1053     bool IsNotHoistable() const
1054     {
1055         return GetFlag(inst_flags::NO_HOIST);
1056     }
1057 
1058     // Returns true Cse can't be applied to the instruction
IsNotCseApplicable()1059     bool IsNotCseApplicable() const
1060     {
1061         return GetFlag(inst_flags::NO_CSE);
1062     }
1063 
1064     // Returns true if the instruction is a barrier
IsBarrier()1065     virtual bool IsBarrier() const
1066     {
1067         return GetFlag(inst_flags::BARRIER);
1068     }
1069 
1070     // Returns true if opcode can not be moved throught runtime calls (REFERENCE type only)
IsRefSpecial()1071     bool IsRefSpecial() const
1072     {
1073         bool result = GetFlag(inst_flags::REF_SPECIAL);
1074         ASSERT(!result || IsReferenceOrAny());
1075         return result;
1076     }
1077 
1078     // Returns true if the instruction is a commutative
IsCommutative()1079     bool IsCommutative() const
1080     {
1081         return GetFlag(inst_flags::COMMUTATIVE);
1082     }
1083 
1084     // Returns true if the instruction allocates a new object on the heap
IsAllocation()1085     bool IsAllocation() const
1086     {
1087         return GetFlag(inst_flags::ALLOC);
1088     }
1089 
1090     // Returns true if the instruction can be used in if-conversion
IsIfConvertable()1091     bool IsIfConvertable() const
1092     {
1093         return GetFlag(inst_flags::IFCVT);
1094     }
1095 
IsRuntimeCall()1096     virtual bool IsRuntimeCall() const
1097     {
1098         return GetFlag(inst_flags::RUNTIME_CALL);
1099     }
1100 
NoNullPtr()1101     virtual bool NoNullPtr() const
1102     {
1103         return GetFlag(inst_flags::NO_NULLPTR);
1104     }
1105 
1106     PANDA_PUBLIC_API virtual bool IsPropagateLiveness() const;
1107 
1108     // Returns true if the instruction doesn't have side effects(call runtime, throw e.t.c.)
IsSafeInst()1109     virtual bool IsSafeInst() const
1110     {
1111         return false;
1112     }
1113 
IsBinaryInst()1114     virtual bool IsBinaryInst() const
1115     {
1116         return false;
1117     }
1118 
IsBinaryImmInst()1119     virtual bool IsBinaryImmInst() const
1120     {
1121         return false;
1122     }
1123 
1124     bool RequireRegMap() const;
1125 
GetObjectTypeInfo()1126     ObjectTypeInfo GetObjectTypeInfo() const
1127     {
1128         return objectTypeInfo_;
1129     }
1130 
HasObjectTypeInfo()1131     bool HasObjectTypeInfo() const
1132     {
1133         return objectTypeInfo_.IsValid();
1134     }
1135 
SetObjectTypeInfo(ObjectTypeInfo o)1136     void SetObjectTypeInfo(ObjectTypeInfo o)
1137     {
1138         objectTypeInfo_ = o;
1139     }
1140 
GetDataFlowInput(int index)1141     Inst *GetDataFlowInput(int index) const
1142     {
1143         return GetDataFlowInput(GetInput(index).GetInst());
1144     }
1145     static Inst *GetDataFlowInput(Inst *inputInst);
1146 
1147     bool IsPrecedingInSameBlock(const Inst *other) const;
1148 
1149     bool IsDominate(const Inst *other) const;
1150 
1151     bool InSameBlockOrDominate(const Inst *other) const;
1152 
IsAdd()1153     bool IsAdd() const
1154     {
1155         return GetOpcode() == Opcode::Add || GetOpcode() == Opcode::AddOverflowCheck;
1156     }
1157 
IsSub()1158     bool IsSub() const
1159     {
1160         return GetOpcode() == Opcode::Sub || GetOpcode() == Opcode::SubOverflowCheck;
1161     }
1162 
IsAddSub()1163     bool IsAddSub() const
1164     {
1165         return IsAdd() || IsSub();
1166     }
1167 
GetSaveState()1168     const SaveStateInst *GetSaveState() const
1169     {
1170         return const_cast<Inst *>(this)->GetSaveState();
1171     }
1172 
GetSaveState()1173     SaveStateInst *GetSaveState()
1174     {
1175         if (!RequireState()) {
1176             return nullptr;
1177         }
1178         if (GetInputsCount() == 0) {
1179             return nullptr;
1180         }
1181         auto ss = GetInput(GetInputsCount() - 1).GetInst();
1182         if (ss->GetOpcode() == Opcode::SaveStateDeoptimize) {
1183             return ss->CastToSaveStateDeoptimize();
1184         }
1185         if (ss->GetOpcode() != Opcode::SaveState) {
1186             return nullptr;
1187         }
1188 
1189         return ss->CastToSaveState();
1190     }
1191 
SetSaveState(Inst * inst)1192     void SetSaveState(Inst *inst)
1193     {
1194         ASSERT(RequireState());
1195         SetInput(GetInputsCount() - 1, inst);
1196     }
1197 
1198     PANDA_PUBLIC_API virtual uint32_t GetInliningDepth() const;
1199 
1200     bool IsZeroRegInst() const;
1201 
1202     bool IsReferenceOrAny() const;
1203     bool IsMovableObject();
1204 
1205     /// Return instruction clone
1206     PANDA_PUBLIC_API virtual Inst *Clone(const Graph *targetGraph) const;
1207 
GetFlagsMask()1208     uintptr_t GetFlagsMask() const
1209     {
1210         return GetField<FieldFlags>();
1211     }
1212 
SetFlagsMask(inst_flags::Flags flag)1213     void SetFlagsMask(inst_flags::Flags flag)
1214     {
1215         SetField<FieldFlags>(flag);
1216     }
1217 
GetFlag(inst_flags::Flags flag)1218     bool GetFlag(inst_flags::Flags flag) const
1219     {
1220         return (GetFlagsMask() & flag) != 0;
1221     }
1222 
SetFlag(inst_flags::Flags flag)1223     void SetFlag(inst_flags::Flags flag)
1224     {
1225         SetField<FieldFlags>(GetFlagsMask() | flag);
1226     }
1227 
ClearFlag(inst_flags::Flags flag)1228     void ClearFlag(inst_flags::Flags flag)
1229     {
1230         SetField<FieldFlags>(GetFlagsMask() & ~static_cast<uintptr_t>(flag));
1231     }
1232 
1233 #ifndef NDEBUG
GetModesMask()1234     uint8_t GetModesMask() const
1235     {
1236         return inst_modes::GetModesMask(opcode_);
1237     }
1238 
SupportsMode(inst_modes::Mode mode)1239     bool SupportsMode(inst_modes::Mode mode) const
1240     {
1241         return (GetModesMask() & mode) != 0;
1242     }
1243 #endif
1244 
SetTerminator()1245     void SetTerminator()
1246     {
1247         SetFlag(inst_flags::Flags::TERMINATOR);
1248     }
1249 
IsTerminator()1250     bool IsTerminator() const
1251     {
1252         return GetFlag(inst_flags::TERMINATOR);
1253     }
1254 
1255     PANDA_PUBLIC_API void InsertBefore(Inst *inst);
1256     void InsertAfter(Inst *inst);
1257 
1258     /// Return true if instruction has dynamic operands storage.
IsOperandsDynamic()1259     bool IsOperandsDynamic() const
1260     {
1261         return GetField<InputsCount>() == MAX_STATIC_INPUTS;
1262     }
1263 
1264     /**
1265      * Add user to the instruction.
1266      * @param user - pointer to User object
1267      */
AddUser(User * user)1268     void AddUser(User *user)
1269     {
1270         ASSERT(user && user->GetInst());
1271         user->SetNext(firstUser_);
1272         user->SetPrev(nullptr);
1273         if (firstUser_ != nullptr) {
1274             ASSERT(firstUser_->GetPrev() == nullptr);
1275             firstUser_->SetPrev(user);
1276         }
1277         firstUser_ = user;
1278     }
1279 
1280     /**
1281      * Remove instruction from users.
1282      * @param user - pointer to User object
1283      */
RemoveUser(User * user)1284     void RemoveUser(User *user)
1285     {
1286         ASSERT(user);
1287         ASSERT(HasUsers());
1288         if (user == firstUser_) {
1289             firstUser_ = user->GetNext();
1290         }
1291         user->Remove();
1292     }
1293 
1294     /**
1295      * Set input instruction in specified index.
1296      * Old input will be removed.
1297      * @param index - index of input to be set
1298      * @param inst - new input instruction NOTE sherstennikov: currently it can be nullptr, is it correct?
1299      */
SetInput(unsigned index,Inst * inst)1300     void SetInput(unsigned index, Inst *inst)
1301     {
1302         CHECK_LT(index, GetInputsCount());
1303         auto &input = GetInputs()[index];
1304         auto user = GetUser(index);
1305         if (input.GetInst() != nullptr && input.GetInst()->HasUsers()) {
1306             input.GetInst()->RemoveUser(user);
1307         }
1308         if (inst != nullptr) {
1309             inst->AddUser(user);
1310         }
1311         input = Input(inst);
1312     }
1313 
1314     /**
1315      * Replace all inputs that points to specified instruction by new one.
1316      * @param old_input - instruction that should be replaced
1317      * @param new_input - new input instruction
1318      */
ReplaceInput(Inst * oldInput,Inst * newInput)1319     void ReplaceInput(Inst *oldInput, Inst *newInput)
1320     {
1321         unsigned index = 0;
1322         for (auto input : GetInputs()) {
1323             if (input.GetInst() == oldInput) {
1324                 SetInput(index, newInput);
1325             }
1326             index++;
1327         }
1328     }
1329 
1330     /**
1331      * Replace inputs that point to this instruction by given instruction.
1332      * @param inst - new input instruction
1333      */
ReplaceUsers(Inst * inst)1334     void ReplaceUsers(Inst *inst)
1335     {
1336         ASSERT(inst != this);
1337         ASSERT(inst != nullptr);
1338         for (auto it = GetUsers().begin(); it != GetUsers().end(); it = GetUsers().begin()) {
1339             it->GetInst()->SetInput(it->GetIndex(), inst);
1340         }
1341     }
1342 
1343     /**
1344      * Swap first 2 operands of the instruction.
1345      * NB! Don't swap inputs while iterating over instruction's users:
1346      * for (auto user : instruction.GetUsers()) {
1347      *     // Don't do this!
1348      *     user.GetInst()->SwapInputs();
1349      * }
1350      */
SwapInputs()1351     void SwapInputs()
1352     {
1353         ASSERT(GetInputsCount() >= 2U);
1354         auto input0 = GetInput(0).GetInst();
1355         auto input1 = GetInput(1).GetInst();
1356         SetInput(0, input1);
1357         SetInput(1, input0);
1358     }
1359 
1360     /**
1361      * Append input instruction.
1362      * Available only for variadic inputs instructions, such as PHI.
1363      * @param input - input instruction
1364      * @return index in inputs container where new input is placed
1365      */
AppendInput(Inst * input)1366     unsigned AppendInput(Inst *input)
1367     {
1368         ASSERT(input != nullptr);
1369         ASSERT(IsOperandsDynamic());
1370         DynamicOperands *operands = GetDynamicOperands();
1371         return operands->Append(input);
1372     }
1373 
AppendInput(Input input)1374     unsigned AppendInput(Input input)
1375     {
1376         static_assert(sizeof(Input) <= sizeof(uintptr_t));  // Input become larger, so pass it by reference then
1377         return AppendInput(input.GetInst());
1378     }
1379 
1380     /**
1381      * Remove input from inputs container
1382      * Available only for variadic inputs instructions, such as PHI.
1383      * @param index - index of input in inputs container
1384      */
RemoveInput(unsigned index)1385     virtual void RemoveInput(unsigned index)
1386     {
1387         ASSERT(IsOperandsDynamic());
1388         DynamicOperands *operands = GetDynamicOperands();
1389         ASSERT(index < operands->Size());
1390         operands->Remove(index);
1391     }
1392 
1393     /// Remove all inputs
RemoveInputs()1394     void RemoveInputs()
1395     {
1396         if (UNLIKELY(IsOperandsDynamic())) {
1397             for (auto inputsCount = GetInputsCount(); inputsCount != 0; --inputsCount) {
1398                 RemoveInput(inputsCount - 1);
1399             }
1400         } else {
1401             for (size_t i = 0; i < GetInputsCount(); ++i) {
1402                 SetInput(i, nullptr);
1403             }
1404         }
1405     }
1406 
1407     /// Remove all users
1408     template <bool WITH_INPUTS = false>
RemoveUsers()1409     void RemoveUsers()
1410     {
1411         auto users = GetUsers();
1412         while (!users.Empty()) {
1413             // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
1414             if constexpr (WITH_INPUTS) {
1415                 auto &user = users.Front();
1416                 user.GetInst()->RemoveInput(user.GetIndex());
1417                 // NOLINTNEXTLINE(readability-misleading-indentation)
1418             } else {
1419                 RemoveUser(&users.Front());
1420             }
1421         }
1422     }
1423 
1424     /**
1425      * Get input by index
1426      * @param index - index of input
1427      * @return input instruction
1428      */
GetInput(unsigned index)1429     Input GetInput(unsigned index)
1430     {
1431         ASSERT(index < GetInputsCount());
1432         return GetInputs()[index];
1433     }
1434 
GetInput(unsigned index)1435     Input GetInput(unsigned index) const
1436     {
1437         ASSERT(index < GetInputsCount());
1438         return GetInputs()[index];
1439     }
1440 
GetInputs()1441     Span<Input> GetInputs()
1442     {
1443         if (UNLIKELY(IsOperandsDynamic())) {
1444             DynamicOperands *operands = GetDynamicOperands();
1445             return Span<Input>(operands->Inputs(), operands->Size());
1446         }
1447 
1448         auto inputsCount {GetField<InputsCount>()};
1449         return Span<Input>(
1450             reinterpret_cast<Input *>(reinterpret_cast<uintptr_t>(this) -
1451                                       (inputsCount + Input::GetPadding(RUNTIME_ARCH, inputsCount)) * sizeof(Input)),
1452             inputsCount);
1453     }
GetInputs()1454     Span<const Input> GetInputs() const
1455     {
1456         return Span<const Input>(const_cast<Inst *>(this)->GetInputs());
1457     }
1458 
GetInputType(size_t index)1459     virtual DataType::Type GetInputType([[maybe_unused]] size_t index) const
1460     {
1461         ASSERT(index < GetInputsCount());
1462         if (GetInput(index).GetInst()->IsSaveState()) {
1463             return DataType::NO_TYPE;
1464         }
1465         return GetType();
1466     }
1467 
GetUsers()1468     UserList<User> GetUsers()
1469     {
1470         return UserList<User>(&firstUser_);
1471     }
GetUsers()1472     UserList<const User> GetUsers() const
1473     {
1474         return UserList<const User>(&firstUser_);
1475     }
1476 
GetInputsCount()1477     size_t GetInputsCount() const
1478     {
1479         if (UNLIKELY(IsOperandsDynamic())) {
1480             return GetDynamicOperands()->Size();
1481         }
1482         return GetInputs().Size();
1483     }
1484 
HasUsers()1485     bool HasUsers() const
1486     {
1487         return firstUser_ != nullptr;
1488     };
1489 
HasSingleUser()1490     bool HasSingleUser() const
1491     {
1492         return firstUser_ != nullptr && firstUser_->GetNext() == nullptr;
1493     }
1494 
1495     /// Reserve space in dataflow storage for specified inputs count
1496     void ReserveInputs(size_t capacity);
1497 
SetLocation(size_t index,Location location)1498     virtual void SetLocation([[maybe_unused]] size_t index, [[maybe_unused]] Location location) {}
1499 
GetLocation(size_t index)1500     virtual Location GetLocation([[maybe_unused]] size_t index) const
1501     {
1502         return Location::RequireRegister();
1503     }
1504 
GetDstLocation()1505     virtual Location GetDstLocation() const
1506     {
1507         return Location::MakeRegister(GetDstReg(), GetType());
1508     }
1509 
GetDstLocation(unsigned index)1510     virtual Location GetDstLocation([[maybe_unused]] unsigned index) const
1511     {
1512         ASSERT(index == 0);
1513         return GetDstLocation();
1514     }
1515 
SetTmpLocation(Location location)1516     virtual void SetTmpLocation([[maybe_unused]] Location location) {}
1517 
GetTmpLocation()1518     virtual Location GetTmpLocation() const
1519     {
1520         return Location::Invalid();
1521     }
1522 
CanBeNull()1523     virtual bool CanBeNull() const
1524     {
1525         ASSERT_PRINT(GetType() == DataType::Type::REFERENCE, "CanBeNull only applies to reference types");
1526         return true;
1527     }
1528 
Latency()1529     virtual uint32_t Latency() const
1530     {
1531         return g_options.GetCompilerSchedLatency();
1532     }
1533 
1534     template <typename Accessor>
SetField(typename Accessor::ValueType value)1535     void SetField(typename Accessor::ValueType value)
1536     {
1537         Accessor::Set(value, &bitFields_);
1538     }
1539 
1540     template <typename Accessor>
GetField()1541     typename Accessor::ValueType GetField() const
1542     {
1543         return Accessor::Get(bitFields_);
1544     }
1545 
GetAllFields()1546     uint64_t GetAllFields() const
1547     {
1548         return bitFields_;
1549     }
1550 
IsPhi()1551     bool IsPhi() const
1552     {
1553         return opcode_ == Opcode::Phi;
1554     }
1555 
IsCatchPhi()1556     bool IsCatchPhi() const
1557     {
1558         return opcode_ == Opcode::CatchPhi;
1559     }
1560 
IsConst()1561     bool IsConst() const
1562     {
1563         return opcode_ == Opcode::Constant;
1564     }
1565 
IsParameter()1566     bool IsParameter() const
1567     {
1568         return opcode_ == Opcode::Parameter;
1569     }
1570 
IsBoolConst()1571     virtual bool IsBoolConst() const
1572     {
1573         return false;
1574     }
1575 
IsSaveState()1576     bool IsSaveState() const
1577     {
1578         return opcode_ == Opcode::SaveState || opcode_ == Opcode::SafePoint || opcode_ == Opcode::SaveStateOsr ||
1579                opcode_ == Opcode::SaveStateDeoptimize;
1580     }
1581 
IsClassInst()1582     bool IsClassInst() const
1583     {
1584         return opcode_ == Opcode::InitClass || opcode_ == Opcode::LoadClass || opcode_ == Opcode::LoadAndInitClass ||
1585                opcode_ == Opcode::UnresolvedLoadAndInitClass;
1586     }
1587 
GetHashCode()1588     virtual size_t GetHashCode() const
1589     {
1590         // NOTE (Aleksandr Popov) calculate hash code
1591         return 0;
1592     }
1593 
SetVnObject(VnObject * vnObj)1594     virtual void SetVnObject([[maybe_unused]] VnObject *vnObj) {}
1595 
GetDstReg()1596     Register GetDstReg() const
1597     {
1598         return dstReg_;
1599     }
1600 
SetDstReg(Register reg)1601     void SetDstReg(Register reg)
1602     {
1603         dstReg_ = reg;
1604     }
1605 
GetVN()1606     uint32_t GetVN() const
1607     {
1608         return vn_;
1609     }
1610 
SetVN(uint32_t vn)1611     void SetVN(uint32_t vn)
1612     {
1613         vn_ = vn;
1614     }
1615     void Dump(std::ostream *out, bool newLine = true) const;
1616     PANDA_PUBLIC_API virtual bool DumpInputs(std::ostream *out) const;
1617     PANDA_PUBLIC_API virtual void DumpOpcode(std::ostream *out) const;
1618     void DumpBytecode(std::ostream *out) const;
1619 
1620 #ifdef PANDA_COMPILER_DEBUG_INFO
1621     void DumpSourceLine(std::ostream *out) const;
1622 #endif  // PANDA_COMPILER_DEBUG_INFO
1623 
SetDstReg(unsigned index,Register reg)1624     virtual void SetDstReg([[maybe_unused]] unsigned index, Register reg)
1625     {
1626         ASSERT(index == 0);
1627         SetDstReg(reg);
1628     }
1629 
GetDstReg(unsigned index)1630     virtual Register GetDstReg([[maybe_unused]] unsigned index) const
1631     {
1632         ASSERT(index == 0);
1633         return GetDstReg();
1634     }
1635 
GetDstCount()1636     virtual size_t GetDstCount() const
1637     {
1638         return 1;
1639     }
1640 
GetSrcRegIndex()1641     virtual uint32_t GetSrcRegIndex() const
1642     {
1643         return 0;
1644     }
1645 
SetSrcReg(unsigned index,Register reg)1646     virtual void SetSrcReg([[maybe_unused]] unsigned index, [[maybe_unused]] Register reg) {}
1647 
GetSrcReg(unsigned index)1648     virtual Register GetSrcReg([[maybe_unused]] unsigned index) const
1649     {
1650         return GetInvalidReg();
1651     }
1652 
GetFirstUser()1653     User *GetFirstUser() const
1654     {
1655         return firstUser_;
1656     }
1657 
1658 #ifdef PANDA_COMPILER_DEBUG_INFO
GetDebugInfo()1659     InstDebugInfo *GetDebugInfo() const
1660     {
1661         return debugInfo_;
1662     }
1663 
SetDebugInfo(InstDebugInfo * info)1664     void SetDebugInfo(InstDebugInfo *info)
1665     {
1666         debugInfo_ = info;
1667     }
1668 
GetCurrentMethod()1669     RuntimeInterface::MethodPtr GetCurrentMethod() const
1670     {
1671         return currentMethod_;
1672     }
1673 
SetCurrentMethod(RuntimeInterface::MethodPtr currentMethod)1674     void SetCurrentMethod(RuntimeInterface::MethodPtr currentMethod)
1675     {
1676         currentMethod_ = currentMethod;
1677     }
1678 #endif
1679 
1680     using Initializer = std::tuple<Opcode, DataType::Type, uint32_t>;
1681 
1682 protected:
1683     using InstBase::InstBase;
1684     static constexpr int INPUT_COUNT = 0;
1685 
1686     Inst() = default;
1687 
Inst(Opcode opcode)1688     explicit Inst(Opcode opcode) : Inst(Initializer {opcode, DataType::Type::NO_TYPE, INVALID_PC}) {}
1689 
Inst(Initializer t)1690     explicit Inst(Initializer t) : pc_(std::get<uint32_t>(t)), opcode_(std::get<Opcode>(t))
1691     {
1692         bitFields_ = inst_flags::GetFlagsMask(opcode_);
1693         SetField<FieldType>(std::get<DataType::Type>(t));
1694     }
1695 
1696 protected:
1697     using FieldFlags = BitField<uint32_t, 0, MinimumBitsToStore(1U << inst_flags::FLAGS_COUNT)>;
1698     using FieldType = FieldFlags::NextField<DataType::Type, MinimumBitsToStore(DataType::LAST)>;
1699     using InputsCount = FieldType::NextField<uint32_t, BITS_PER_INPUTS_NUM>;
1700     using LastField = InputsCount;
1701 
GetDynamicOperands()1702     DynamicOperands *GetDynamicOperands() const
1703     {
1704         return reinterpret_cast<DynamicOperands *>(reinterpret_cast<uintptr_t>(this) - sizeof(DynamicOperands));
1705     }
1706 
1707 private:
1708     template <typename InstType, typename... Args>
1709     static InstType *ConstructInst(InstType *ptr, ArenaAllocator *allocator, Args &&...args);
1710 
GetUser(unsigned index)1711     User *GetUser(unsigned index)
1712     {
1713         if (UNLIKELY(IsOperandsDynamic())) {
1714             return GetDynamicOperands()->GetUser(index);
1715         }
1716         auto inputsCount {GetField<InputsCount>()};
1717         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1718         return reinterpret_cast<User *>(reinterpret_cast<Input *>(this) -
1719                                         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1720                                         (inputsCount + Input::GetPadding(RUNTIME_ARCH, inputsCount))) -
1721                // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1722                index - 1;  // CC-OFF(G.FMT.02) project code style
1723     }
1724 
OperandsStorageSize()1725     size_t OperandsStorageSize() const
1726     {
1727         if (UNLIKELY(IsOperandsDynamic())) {
1728             return sizeof(DynamicOperands);
1729         }
1730 
1731         auto inputsCount {GetField<InputsCount>()};
1732         return inputsCount * (sizeof(Input) + sizeof(User)) +
1733                Input::GetPadding(RUNTIME_ARCH, inputsCount) * sizeof(Input);
1734     }
1735 
1736 private:
1737     /// Basic block this instruction belongs to
1738     BasicBlock *bb_ {nullptr};
1739 
1740 #ifdef PANDA_COMPILER_DEBUG_INFO
1741     InstDebugInfo *debugInfo_ {nullptr};
1742     RuntimeInterface::MethodPtr currentMethod_ {nullptr};
1743 #endif
1744 
1745     /// Next instruction within basic block
1746     Inst *next_ {nullptr};
1747 
1748     /// Previous instruction within basic block
1749     Inst *prev_ {nullptr};
1750 
1751     /// First user in users chain
1752     User *firstUser_ {nullptr};
1753 
1754     /// This value hold properties of the instruction. It accessed via BitField types(f.e. FieldType).
1755     uint64_t bitFields_ {0};
1756 
1757     /// Unique id of instruction
1758     uint32_t id_ {INVALID_ID};
1759 
1760     /// Unique id of instruction
1761     uint32_t vn_ {INVALID_VN};
1762 
1763     /// Bytecode pc
1764     uint32_t pc_ {INVALID_PC};
1765 
1766     /// Number used in cloning
1767     uint32_t cloneNumber_ {INVALID_ID};
1768 
1769     /// Instruction number getting while visiting graph
1770     LinearNumber linearNumber_ {INVALID_LINEAR_NUM};
1771 
1772     ObjectTypeInfo objectTypeInfo_ {};
1773 
1774     /// Opcode, see opcodes.def
1775     Opcode opcode_ {Opcode::INVALID};
1776 
1777     // Destination register type - defined in FieldType
1778     Register dstReg_ {GetInvalidReg()};
1779 };
1780 
1781 /**
1782  * Proxy class that injects new field - type of the source operands - into property field of the instruction.
1783  * Should be used when instruction has sources of the same type and type of the instruction is not match to type of
1784  * sources. Examples: Cmp, Compare
1785  * @tparam T Base instruction class after which this mixin is injected
1786  */
1787 template <typename T>
1788 class InstWithOperandsType : public T {
1789 public:
1790     using T::T;
1791 
SetOperandsType(DataType::Type type)1792     void SetOperandsType(DataType::Type type)
1793     {
1794         T::template SetField<FieldOperandsType>(type);
1795     }
GetOperandsType()1796     virtual DataType::Type GetOperandsType() const
1797     {
1798         return T::template GetField<FieldOperandsType>();
1799     }
1800 
1801 protected:
1802     using FieldOperandsType =
1803         typename T::LastField::template NextField<DataType::Type, MinimumBitsToStore(DataType::LAST)>;
1804     using LastField = FieldOperandsType;
1805 };
1806 
1807 /**
1808  * Mixin for NeedBarrier flag.
1809  * @tparam T Base instruction class after which this mixin is injected
1810  */
1811 template <typename T>
1812 class NeedBarrierMixin : public T {
1813 public:
1814     using T::T;
1815 
SetNeedBarrier(bool v)1816     void SetNeedBarrier(bool v)
1817     {
1818         T::template SetField<NeedBarrierFlag>(v);
1819     }
GetNeedBarrier()1820     bool GetNeedBarrier() const
1821     {
1822         return T::template GetField<NeedBarrierFlag>();
1823     }
1824 
1825 protected:
1826     using NeedBarrierFlag = typename T::LastField::NextFlag;
1827     using LastField = NeedBarrierFlag;
1828 };
1829 
1830 enum class DynObjectAccessType {
1831     UNKNOWN = 0,  // corresponds by value semantic
1832     BY_NAME = 1,
1833     BY_INDEX = 2,
1834     LAST = BY_INDEX
1835 };
1836 
1837 enum class DynObjectAccessMode {
1838     UNKNOWN = 0,
1839     DICTIONARY = 1,
1840     ARRAY = 2,
1841     LAST = ARRAY,
1842 };
1843 
1844 /**
1845  * Mixin for dynamic object access properties.
1846  * @tparam T Base instruction class after which this mixin is injected
1847  */
1848 template <typename T>
1849 class DynObjectAccessMixin : public T {
1850 public:
1851     using T::T;
1852     using Type = DynObjectAccessType;
1853     using Mode = DynObjectAccessMode;
1854 
SetAccessType(Type type)1855     void SetAccessType(Type type)
1856     {
1857         T::template SetField<AccessType>(type);
1858     }
1859 
GetAccessType()1860     Type GetAccessType() const
1861     {
1862         return T::template GetField<AccessType>();
1863     }
1864 
SetAccessMode(Mode mode)1865     void SetAccessMode(Mode mode)
1866     {
1867         T::template SetField<AccessMode>(mode);
1868     }
1869 
GetAccessMode()1870     Mode GetAccessMode() const
1871     {
1872         return T::template GetField<AccessMode>();
1873     }
1874 
1875 protected:
1876     using AccessType = typename T::LastField::template NextField<Type, MinimumBitsToStore(Type::LAST)>;
1877     using AccessMode = typename AccessType::template NextField<Mode, MinimumBitsToStore(Mode::LAST)>;
1878     using LastField = AccessMode;
1879 };
1880 
1881 enum ObjectType {
1882     MEM_OBJECT = 0,
1883     MEM_STATIC,
1884     MEM_DYN_GLOBAL,
1885     MEM_DYN_INLINED,
1886     MEM_DYN_PROPS,
1887     MEM_DYN_ELEMENTS,
1888     MEM_DYN_CLASS,
1889     MEM_DYN_HCLASS,
1890     MEM_DYN_METHOD,
1891     MEM_DYN_PROTO_HOLDER,
1892     MEM_DYN_PROTO_CELL,
1893     MEM_DYN_CHANGE_FIELD,
1894     MEM_DYN_ARRAY_LENGTH,
1895     LAST = MEM_DYN_ARRAY_LENGTH
1896 };
1897 
ObjectTypeToString(ObjectType objType)1898 inline const char *ObjectTypeToString(ObjectType objType)
1899 {
1900     static constexpr auto COUNT = static_cast<uint8_t>(ObjectType::LAST) + 1;
1901     static constexpr std::array<const char *, COUNT> OBJ_TYPE_NAMES = {
1902         "Object", "Static", "GlobalVar",        "Dynamic inlined", "Properties",     "Elements", "Class",
1903         "Hclass", "Method", "Prototype holder", "Prototype cell",  "IsChangeFieald", "Length"};
1904     auto idx = static_cast<uint8_t>(objType);
1905     ASSERT(idx <= LAST);
1906     return OBJ_TYPE_NAMES[idx];
1907 }
1908 
1909 /// This mixin aims to implement type id accessors.
1910 class TypeIdMixin {
1911 public:
1912     static constexpr uint32_t INVALID_ID = std::numeric_limits<uint32_t>::max();
1913     static constexpr uint32_t MEM_PROMISE_CLASS_ID = RuntimeInterface::MEM_PROMISE_CLASS_ID;
1914     static constexpr uint32_t MEM_DYN_CLASS_ID = MEM_PROMISE_CLASS_ID - ObjectType::MEM_DYN_CLASS;
1915     static constexpr uint32_t MEM_DYN_HCLASS_ID = MEM_PROMISE_CLASS_ID - ObjectType::MEM_DYN_HCLASS;
1916     static constexpr uint32_t MEM_DYN_METHOD_ID = MEM_PROMISE_CLASS_ID - ObjectType::MEM_DYN_METHOD;
1917     static constexpr uint32_t MEM_DYN_PROTO_HOLDER_ID = MEM_PROMISE_CLASS_ID - ObjectType::MEM_DYN_PROTO_HOLDER;
1918     static constexpr uint32_t MEM_DYN_PROTO_CELL_ID = MEM_PROMISE_CLASS_ID - ObjectType::MEM_DYN_PROTO_CELL;
1919     static constexpr uint32_t MEM_DYN_CHANGE_FIELD_ID = MEM_PROMISE_CLASS_ID - ObjectType::MEM_DYN_CHANGE_FIELD;
1920     static constexpr uint32_t MEM_DYN_GLOBAL_ID = MEM_PROMISE_CLASS_ID - ObjectType::MEM_DYN_GLOBAL;
1921     static constexpr uint32_t MEM_DYN_PROPS_ID = MEM_PROMISE_CLASS_ID - ObjectType::MEM_DYN_PROPS;
1922     static constexpr uint32_t MEM_DYN_ELEMENTS_ID = MEM_PROMISE_CLASS_ID - ObjectType::MEM_DYN_ELEMENTS;
1923     static constexpr uint32_t MEM_DYN_ARRAY_LENGTH_ID = MEM_PROMISE_CLASS_ID - ObjectType::MEM_DYN_ARRAY_LENGTH;
1924 
TypeIdMixin(uint32_t typeId,RuntimeInterface::MethodPtr method)1925     TypeIdMixin(uint32_t typeId, RuntimeInterface::MethodPtr method) : typeId_(typeId), method_(method) {}
1926 
1927     TypeIdMixin() = default;
1928     DEFAULT_COPY_SEMANTIC(TypeIdMixin);
1929     DEFAULT_MOVE_SEMANTIC(TypeIdMixin);
1930     virtual ~TypeIdMixin() = default;
1931 
SetTypeId(uint32_t id)1932     void SetTypeId(uint32_t id)
1933     {
1934         typeId_ = id;
1935     }
1936 
GetTypeId()1937     auto GetTypeId() const
1938     {
1939         return typeId_;
1940     }
1941 
SetMethod(RuntimeInterface::MethodPtr method)1942     void SetMethod(RuntimeInterface::MethodPtr method)
1943     {
1944         method_ = method;
1945     }
GetMethod()1946     auto GetMethod() const
1947     {
1948         return method_;
1949     }
1950 
1951 private:
1952     uint32_t typeId_ {0};
1953     // The pointer to the method in which this instruction is executed(inlined method)
1954     RuntimeInterface::MethodPtr method_ {nullptr};
1955 };
1956 
1957 /// This mixin aims to implement two type id accessors.
1958 class TypeIdMixin2 : public TypeIdMixin {
1959     using Base = TypeIdMixin;
1960     using Base::Base;
1961 
1962 public:
TypeIdMixin2(uint32_t typeId0,uint32_t typeId1,RuntimeInterface::MethodPtr method)1963     TypeIdMixin2(uint32_t typeId0, uint32_t typeId1, RuntimeInterface::MethodPtr method)
1964         : TypeIdMixin(typeId1, method), typeId0_(typeId0)
1965     {
1966     }
1967 
1968     TypeIdMixin2() = default;
1969     NO_COPY_SEMANTIC(TypeIdMixin2);
1970     NO_MOVE_SEMANTIC(TypeIdMixin2);
1971     ~TypeIdMixin2() override = default;
1972 
SetTypeId0(uint32_t id)1973     void SetTypeId0(uint32_t id)
1974     {
1975         typeId0_ = id;
1976     }
GetTypeId0()1977     auto GetTypeId0() const
1978     {
1979         return typeId0_;
1980     }
SetTypeId1(uint32_t id)1981     void SetTypeId1(uint32_t id)
1982     {
1983         SetTypeId(id);
1984     }
GetTypeId1()1985     auto GetTypeId1() const
1986     {
1987         return GetTypeId();
1988     }
1989 
1990 private:
1991     uint32_t typeId0_ {0};
1992 };
1993 
1994 /// This mixin aims to implement type of klass.
1995 template <typename T>
1996 class ClassTypeMixin : public T {
1997 public:
1998     using T::T;
1999 
SetClassType(ClassType classType)2000     void SetClassType(ClassType classType)
2001     {
2002         T::template SetField<ClassTypeField>(classType);
2003     }
2004 
GetClassType()2005     ClassType GetClassType() const
2006     {
2007         return T::template GetField<ClassTypeField>();
2008     }
2009 
2010 protected:
2011     using ClassTypeField = typename T::LastField::template NextField<ClassType, MinimumBitsToStore(ClassType::COUNT)>;
2012     using LastField = ClassTypeField;
2013 };
2014 
2015 /// Mixin to check if null check inside CheckCast and IsInstance can be omitted.
2016 template <typename T>
2017 class OmitNullCheckMixin : public T {
2018 public:
2019     using T::T;
2020 
SetOmitNullCheck(bool omitNullCheck)2021     void SetOmitNullCheck(bool omitNullCheck)
2022     {
2023         T::template SetField<OmitNullCheckFlag>(omitNullCheck);
2024     }
2025 
GetOmitNullCheck()2026     bool GetOmitNullCheck() const
2027     {
2028         return T::template GetField<OmitNullCheckFlag>();
2029     }
2030 
2031 protected:
2032     using OmitNullCheckFlag = typename T::LastField::NextFlag;
2033     using LastField = OmitNullCheckFlag;
2034 };
2035 
2036 template <typename T>
2037 class ScaleMixin : public T {
2038 public:
2039     using T::T;
2040 
SetScale(uint32_t scale)2041     void SetScale(uint32_t scale)
2042     {
2043         T::template SetField<ScaleField>(scale);
2044     }
2045 
GetScale()2046     uint32_t GetScale() const
2047     {
2048         return T::template GetField<ScaleField>();
2049     }
2050 
2051 protected:
2052     using ScaleField = typename T::LastField::template NextField<uint32_t, MinimumBitsToStore(MAX_SCALE)>;
2053     using LastField = ScaleField;
2054 };
2055 
2056 template <typename T>
2057 class ObjectTypeMixin : public T {
2058 public:
2059     using T::T;
2060 
SetObjectType(ObjectType type)2061     void SetObjectType(ObjectType type)
2062     {
2063         T::template SetField<ObjectTypeField>(type);
2064     }
2065 
GetObjectType()2066     ObjectType GetObjectType() const
2067     {
2068         return T::template GetField<ObjectTypeField>();
2069     }
2070 
2071 protected:
2072     using ObjectTypeField = typename T::LastField::template NextField<ObjectType, MinimumBitsToStore(ObjectType::LAST)>;
2073     using LastField = ObjectTypeField;
2074 };
2075 
2076 /// This mixin aims to implement field accessors.
2077 class FieldMixin {
2078 public:
FieldMixin(RuntimeInterface::FieldPtr field)2079     explicit FieldMixin(RuntimeInterface::FieldPtr field) : field_(field) {}
2080 
2081     FieldMixin() = default;
2082     NO_COPY_SEMANTIC(FieldMixin);
2083     NO_MOVE_SEMANTIC(FieldMixin);
2084     virtual ~FieldMixin() = default;
2085 
SetObjField(RuntimeInterface::FieldPtr field)2086     void SetObjField(RuntimeInterface::FieldPtr field)
2087     {
2088         field_ = field;
2089     }
GetObjField()2090     auto GetObjField() const
2091     {
2092         return field_;
2093     }
2094 
2095 private:
2096     RuntimeInterface::FieldPtr field_ {nullptr};
2097 };
2098 
2099 /// This mixin aims to implement 2 field accessors.
2100 class FieldMixin2 {
2101 public:
FieldMixin2(RuntimeInterface::FieldPtr field0,RuntimeInterface::FieldPtr field1)2102     explicit FieldMixin2(RuntimeInterface::FieldPtr field0, RuntimeInterface::FieldPtr field1)
2103         : field0_(field0), field1_(field1)
2104     {
2105     }
2106 
2107     FieldMixin2() = default;
2108     NO_COPY_SEMANTIC(FieldMixin2);
2109     NO_MOVE_SEMANTIC(FieldMixin2);
2110     virtual ~FieldMixin2() = default;
2111 
SetObjField0(RuntimeInterface::FieldPtr field)2112     void SetObjField0(RuntimeInterface::FieldPtr field)
2113     {
2114         field0_ = field;
2115     }
GetObjField0()2116     auto GetObjField0() const
2117     {
2118         return field0_;
2119     }
SetObjField1(RuntimeInterface::FieldPtr field)2120     void SetObjField1(RuntimeInterface::FieldPtr field)
2121     {
2122         field1_ = field;
2123     }
GetObjField1()2124     auto GetObjField1() const
2125     {
2126         return field1_;
2127     }
2128 
2129 private:
2130     RuntimeInterface::FieldPtr field0_ {nullptr};
2131     RuntimeInterface::FieldPtr field1_ {nullptr};
2132 };
2133 
2134 /// This mixin aims to implement volatile accessors.
2135 template <typename T>
2136 class VolatileMixin : public T {
2137 public:
2138     using T::T;
2139 
SetVolatile(bool isVolatile)2140     void SetVolatile(bool isVolatile)
2141     {
2142         T::template SetField<IsVolatileFlag>(isVolatile);
2143     }
GetVolatile()2144     bool GetVolatile() const
2145     {
2146         return T::template GetField<IsVolatileFlag>();
2147     }
2148 
2149 protected:
2150     using IsVolatileFlag = typename T::LastField::NextFlag;
2151     using LastField = IsVolatileFlag;
2152 };
2153 /// Mixin for Inlined calls/returns.
2154 template <typename T>
2155 class InlinedInstMixin : public T {
2156 public:
2157     using T::T;
2158 
SetInlined(bool v)2159     void SetInlined(bool v)
2160     {
2161         T::template SetField<IsInlinedFlag>(v);
2162     }
IsInlined()2163     bool IsInlined() const
2164     {
2165         return T::template GetField<IsInlinedFlag>();
2166     }
2167 
2168 protected:
2169     using IsInlinedFlag = typename T::LastField::NextFlag;
2170     using LastField = IsInlinedFlag;
2171 };
2172 
2173 /// Mixin for Array/String instruction
2174 template <typename T>
2175 class ArrayInstMixin : public T {
2176 public:
2177     using T::T;
2178 
SetIsArray(bool v)2179     void SetIsArray(bool v)
2180     {
2181         T::template SetField<IsStringFlag>(!v);
2182     }
2183 
SetIsString(bool v)2184     void SetIsString(bool v)
2185     {
2186         T::template SetField<IsStringFlag>(v);
2187     }
2188 
IsArray()2189     bool IsArray() const
2190     {
2191         return !(T::template GetField<IsStringFlag>());
2192     }
2193 
IsString()2194     bool IsString() const
2195     {
2196         return T::template GetField<IsStringFlag>();
2197     }
2198 
2199 protected:
2200     using IsStringFlag = typename T::LastField::NextFlag;
2201     using LastField = IsStringFlag;
2202 };
2203 
2204 /// Mixin for instructions with immediate constant value
2205 class ImmediateMixin {
2206 public:
ImmediateMixin(uint64_t immediate)2207     explicit ImmediateMixin(uint64_t immediate) : immediate_(immediate) {}
2208 
2209     NO_COPY_SEMANTIC(ImmediateMixin);
2210     NO_MOVE_SEMANTIC(ImmediateMixin);
2211     virtual ~ImmediateMixin() = default;
2212 
SetImm(uint64_t immediate)2213     void SetImm(uint64_t immediate)
2214     {
2215         immediate_ = immediate;
2216     }
GetImm()2217     auto GetImm() const
2218     {
2219         return immediate_;
2220     }
2221 
2222 protected:
2223     ImmediateMixin() = default;
2224 
2225 private:
2226     uint64_t immediate_ {0};
2227 };
2228 
2229 /// Mixin for instructions with ConditionCode
2230 template <typename T>
2231 class ConditionMixin : public T {
2232 public:
2233     enum class Prediction { NONE = 0, LIKELY, UNLIKELY, SIZE = UNLIKELY };
2234 
2235     using T::T;
ConditionMixin(ConditionCode cc)2236     explicit ConditionMixin(ConditionCode cc)
2237     {
2238         T::template SetField<CcFlag>(cc);
2239     }
2240     NO_COPY_SEMANTIC(ConditionMixin);
2241     NO_MOVE_SEMANTIC(ConditionMixin);
2242     ~ConditionMixin() override = default;
2243 
GetCc()2244     auto GetCc() const
2245     {
2246         return T::template GetField<CcFlag>();
2247     }
SetCc(ConditionCode cc)2248     void SetCc(ConditionCode cc)
2249     {
2250         T::template SetField<CcFlag>(cc);
2251     }
InverseConditionCode()2252     void InverseConditionCode()
2253     {
2254         SetCc(GetInverseConditionCode(GetCc()));
2255         if (IsLikely()) {
2256             SetUnlikely();
2257         } else if (IsUnlikely()) {
2258             SetLikely();
2259         }
2260     }
2261 
IsLikely()2262     bool IsLikely() const
2263     {
2264         return T::template GetField<PredictionFlag>() == Prediction::LIKELY;
2265     }
IsUnlikely()2266     bool IsUnlikely() const
2267     {
2268         return T::template GetField<PredictionFlag>() == Prediction::UNLIKELY;
2269     }
SetLikely()2270     void SetLikely()
2271     {
2272         T::template SetField<PredictionFlag>(Prediction::LIKELY);
2273     }
SetUnlikely()2274     void SetUnlikely()
2275     {
2276         T::template SetField<PredictionFlag>(Prediction::UNLIKELY);
2277     }
2278 
2279 protected:
2280     ConditionMixin() = default;
2281 
2282     using CcFlag = typename T::LastField::template NextField<ConditionCode, MinimumBitsToStore(ConditionCode::CC_LAST)>;
2283     using PredictionFlag = typename CcFlag::template NextField<Prediction, MinimumBitsToStore(Prediction::SIZE)>;
2284     using LastField = PredictionFlag;
2285 };
2286 
2287 /// Mixin for instrucion with ShiftType
2288 class ShiftTypeMixin {
2289 public:
ShiftTypeMixin(ShiftType shiftType)2290     explicit ShiftTypeMixin(ShiftType shiftType) : shiftType_(shiftType) {}
2291     NO_COPY_SEMANTIC(ShiftTypeMixin);
2292     NO_MOVE_SEMANTIC(ShiftTypeMixin);
2293     virtual ~ShiftTypeMixin() = default;
2294 
SetShiftType(ShiftType shiftType)2295     void SetShiftType(ShiftType shiftType)
2296     {
2297         shiftType_ = shiftType;
2298     }
2299 
GetShiftType()2300     ShiftType GetShiftType() const
2301     {
2302         return shiftType_;
2303     }
2304 
2305 protected:
2306     ShiftTypeMixin() = default;
2307 
2308 private:
2309     ShiftType shiftType_ {INVALID_SHIFT};
2310 };
2311 
2312 /// Mixin for instructions with multiple return values
2313 template <typename T, size_t N>
2314 class MultipleOutputMixin : public T {
2315 public:
2316     using T::T;
2317 
GetDstReg(unsigned index)2318     Register GetDstReg(unsigned index) const override
2319     {
2320         ASSERT(index < N);
2321         if (index == 0) {
2322             return T::GetDstReg();
2323         }
2324         return dstRegs_[index - 1];
2325     }
2326 
GetDstLocation(unsigned index)2327     Location GetDstLocation(unsigned index) const override
2328     {
2329         ASSERT(index < N);
2330         if (index == 0) {
2331             return Location::MakeRegister(T::GetDstReg());
2332         }
2333         return Location::MakeRegister(dstRegs_[index - 1]);
2334     }
2335 
SetDstReg(unsigned index,Register reg)2336     void SetDstReg(unsigned index, Register reg) override
2337     {
2338         ASSERT(index < N);
2339         if (index == 0) {
2340             T::SetDstReg(reg);
2341         } else {
2342             dstRegs_[index - 1] = reg;
2343         }
2344     }
2345 
GetDstCount()2346     size_t GetDstCount() const override
2347     {
2348         return N;
2349     }
2350 
2351 private:
2352     std::array<Register, N - 1> dstRegs_;
2353 };
2354 
2355 /**
2356  * Instruction with fixed number of inputs.
2357  * Shall not be instantiated directly, only through derived classes.
2358  */
2359 template <size_t N>
2360 class FixedInputsInst : public Inst {
2361 public:
2362     using Inst::Inst;
2363 
2364     static constexpr int INPUT_COUNT = N;
2365 
2366     using InputsArray = std::array<Inst *, INPUT_COUNT>;
2367     struct Initializer {
2368         Inst::Initializer base;
2369         InputsArray inputs;
2370     };
2371 
FixedInputsInst(Initializer t)2372     explicit FixedInputsInst(Initializer t) : Inst(t.base)
2373     {
2374         SetField<InputsCount>(INPUT_COUNT);
2375         for (size_t i = 0; i < t.inputs.size(); i++) {
2376             SetInput(i, t.inputs[i]);
2377         }
2378     }
2379 
2380     template <typename... Inputs, typename = Inst::CheckBase<Inputs...>>
FixedInputsInst(Inst::Initializer t,Inputs...inputs)2381     explicit FixedInputsInst(Inst::Initializer t, Inputs... inputs) : FixedInputsInst(Initializer {t, {inputs...}})
2382     {
2383     }
2384 
FixedInputsInst(Opcode opcode)2385     explicit FixedInputsInst(Opcode opcode) : Inst(opcode)
2386     {
2387         SetField<InputsCount>(INPUT_COUNT);
2388     }
FixedInputsInst(Opcode opcode,DataType::Type type)2389     FixedInputsInst(Opcode opcode, DataType::Type type) : FixedInputsInst({opcode, type, INVALID_PC}) {}
2390 
SetSrcReg(unsigned index,Register reg)2391     void SetSrcReg(unsigned index, Register reg) override
2392     {
2393         ASSERT(index < N);
2394         srcRegs_[index] = reg;
2395     }
2396 
GetSrcReg(unsigned index)2397     Register GetSrcReg(unsigned index) const override
2398     {
2399         ASSERT(index < N);
2400         return srcRegs_[index];
2401     }
2402 
GetLocation(size_t index)2403     Location GetLocation(size_t index) const override
2404     {
2405         return Location::MakeRegister(GetSrcReg(index), GetInputType(index));
2406     }
2407 
SetLocation(size_t index,Location location)2408     void SetLocation(size_t index, Location location) override
2409     {
2410         SetSrcReg(index, location.GetValue());
2411     }
2412 
SetDstLocation(Location location)2413     void SetDstLocation(Location location)
2414     {
2415         SetDstReg(location.GetValue());
2416     }
2417 
SetTmpLocation(Location location)2418     void SetTmpLocation(Location location) override
2419     {
2420         tmpLocation_ = location;
2421     }
2422 
GetTmpLocation()2423     Location GetTmpLocation() const override
2424     {
2425         return tmpLocation_;
2426     }
2427 
2428     PANDA_PUBLIC_API Inst *Clone(const Graph *targetGraph) const override;
2429 
2430 private:
2431     template <typename T, std::size_t... IS>
CreateArray(T value,std::index_sequence<IS...> unused)2432     constexpr auto CreateArray(T value, [[maybe_unused]] std::index_sequence<IS...> unused)
2433     {
2434         return std::array<T, sizeof...(IS)> {(static_cast<void>(IS), value)...};
2435     }
2436 
2437 private:
2438     std::array<Register, N> srcRegs_ = CreateArray(GetInvalidReg(), std::make_index_sequence<INPUT_COUNT>());
2439     Location tmpLocation_ {};
2440 };
2441 
2442 using FixedInputsInst0 = FixedInputsInst<0>;
2443 using FixedInputsInst1 = FixedInputsInst<1>;
2444 using FixedInputsInst2 = FixedInputsInst<2U>;
2445 using FixedInputsInst3 = FixedInputsInst<3U>;
2446 using FixedInputsInst4 = FixedInputsInst<4U>;
2447 
2448 /// Instruction with variable inputs count
2449 class DynamicInputsInst : public Inst {
2450 public:
2451     using Inst::Inst;
2452 
2453     static constexpr int INPUT_COUNT = MAX_STATIC_INPUTS;
2454 
GetLocation(size_t index)2455     Location GetLocation(size_t index) const override
2456     {
2457         if (locations_ == nullptr) {
2458             return Location::Invalid();
2459         }
2460         return locations_->GetLocation(index);
2461     }
2462 
GetDstLocation()2463     Location GetDstLocation() const override
2464     {
2465         if (locations_ == nullptr) {
2466             return Location::Invalid();
2467         }
2468         return locations_->GetDstLocation();
2469     }
2470 
GetTmpLocation()2471     Location GetTmpLocation() const override
2472     {
2473         if (locations_ == nullptr) {
2474             return Location::Invalid();
2475         }
2476         return locations_->GetTmpLocation();
2477     }
2478 
SetLocation(size_t index,Location location)2479     void SetLocation(size_t index, Location location) override
2480     {
2481         ASSERT(locations_ != nullptr);
2482         locations_->SetLocation(index, location);
2483     }
2484 
SetDstLocation(Location location)2485     void SetDstLocation(Location location)
2486     {
2487         ASSERT(locations_ != nullptr);
2488         locations_->SetDstLocation(location);
2489     }
2490 
SetTmpLocation(Location location)2491     void SetTmpLocation(Location location) override
2492     {
2493         ASSERT(locations_ != nullptr);
2494         locations_->SetTmpLocation(location);
2495     }
2496 
SetLocationsInfo(LocationsInfo * info)2497     void SetLocationsInfo(LocationsInfo *info)
2498     {
2499         locations_ = info;
2500     }
2501 
GetSrcReg(unsigned index)2502     Register GetSrcReg(unsigned index) const override
2503     {
2504         return GetLocation(index).GetValue();
2505     }
2506 
SetSrcReg(unsigned index,Register reg)2507     void SetSrcReg(unsigned index, Register reg) override
2508     {
2509         SetLocation(index, Location::MakeRegister(reg, GetInputType(index)));
2510     }
2511 
2512 private:
2513     LocationsInfo *locations_ {nullptr};
2514 };
2515 
2516 /// Unary operation instruction
2517 class PANDA_PUBLIC_API UnaryOperation : public FixedInputsInst<1> {
2518 public:
2519     using FixedInputsInst::FixedInputsInst;
2520 
GetInputType(size_t index)2521     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2522     {
2523         ASSERT(index < GetInputsCount());
2524         if (GetOpcode() == Opcode::Cast) {
2525             return GetInput(0).GetInst()->GetType();
2526         }
2527         return GetType();
2528     }
2529 
IsSafeInst()2530     bool IsSafeInst() const override
2531     {
2532         return true;
2533     }
2534 
2535     PANDA_PUBLIC_API void SetVnObject(VnObject *vnObj) override;
2536 
2537     Inst *Evaluate();
2538 };
2539 
2540 /// Binary operation instruction
2541 class BinaryOperation : public FixedInputsInst<2U> {
2542 public:
2543     using FixedInputsInst::FixedInputsInst;
2544 
Latency()2545     uint32_t Latency() const override
2546     {
2547         if (GetOpcode() == Opcode::Div) {
2548             return g_options.GetCompilerSchedLatencyLong();
2549         }
2550         return g_options.GetCompilerSchedLatency();
2551     }
2552 
IsSafeInst()2553     bool IsSafeInst() const override
2554     {
2555         return true;
2556     }
2557 
IsBinaryInst()2558     bool IsBinaryInst() const override
2559     {
2560         return true;
2561     }
2562 
2563     Inst *Evaluate();
2564 
GetInputType(size_t index)2565     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2566     {
2567         ASSERT(index < GetInputsCount());
2568         auto inputType = GetInput(index).GetInst()->GetType();
2569         auto type = GetType();
2570         if (GetOpcode() == Opcode::Sub && !DataType::IsTypeSigned(type)) {
2571             ASSERT(GetCommonType(inputType) == GetCommonType(type) || inputType == DataType::POINTER ||
2572                    DataType::GetCommonType(inputType) == DataType::INT64);
2573             return inputType;
2574         }
2575         if (GetOpcode() == Opcode::Add && type == DataType::POINTER) {
2576             ASSERT(GetCommonType(inputType) == GetCommonType(type) ||
2577                    DataType::GetCommonType(inputType) == DataType::INT64);
2578             return inputType;
2579         }
2580         return GetType();
2581     }
2582 };
2583 
2584 /// Binary operation instruction with c immidiate
2585 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2586 class PANDA_PUBLIC_API BinaryImmOperation : public FixedInputsInst<1>, public ImmediateMixin {
2587 public:
2588     using FixedInputsInst::FixedInputsInst;
2589 
BinaryImmOperation(Initializer t,uint64_t imm)2590     BinaryImmOperation(Initializer t, uint64_t imm) : FixedInputsInst(std::move(t)), ImmediateMixin(imm) {}
2591 
GetInputType(size_t index)2592     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2593     {
2594         ASSERT(index < GetInputsCount());
2595         auto inputType = GetInput(index).GetInst()->GetType();
2596         auto type = GetType();
2597         if (GetOpcode() == Opcode::SubI && !DataType::IsTypeSigned(type)) {
2598             ASSERT(GetCommonType(inputType) == GetCommonType(type) || inputType == DataType::POINTER);
2599             return inputType;
2600         }
2601         if (GetOpcode() == Opcode::AddI && type == DataType::POINTER) {
2602             ASSERT(DataType::GetCommonType(inputType) == DataType::GetCommonType(GetType()) ||
2603                    (inputType == DataType::REFERENCE && GetType() == DataType::POINTER));
2604             return inputType;
2605         }
2606         return GetType();
2607     }
2608 
2609     PANDA_PUBLIC_API void SetVnObject(VnObject *vnObj) override;
2610     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
2611 
IsSafeInst()2612     bool IsSafeInst() const override
2613     {
2614         return true;
2615     }
2616 
IsBinaryImmInst()2617     bool IsBinaryImmInst() const override
2618     {
2619         return true;
2620     }
2621 
Clone(const Graph * targetGraph)2622     Inst *Clone(const Graph *targetGraph) const override
2623     {
2624         auto clone = FixedInputsInst::Clone(targetGraph);
2625         static_cast<BinaryImmOperation *>(clone)->SetImm(GetImm());
2626         return clone;
2627     }
2628 };
2629 
2630 /// Unary operation that shifts its own operand prior the application.
2631 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2632 class PANDA_PUBLIC_API UnaryShiftedRegisterOperation : public FixedInputsInst<1>,
2633                                                        public ImmediateMixin,
2634                                                        public ShiftTypeMixin {
2635 public:
2636     using FixedInputsInst::FixedInputsInst;
2637 
UnaryShiftedRegisterOperation(Initializer t,uint64_t imm,ShiftType shiftType)2638     UnaryShiftedRegisterOperation(Initializer t, uint64_t imm, ShiftType shiftType)
2639         : FixedInputsInst(std::move(t)), ImmediateMixin(imm), ShiftTypeMixin(shiftType)
2640     {
2641     }
2642 
GetInputType(size_t index)2643     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2644     {
2645         ASSERT(index < GetInputsCount());
2646         return GetType();
2647     }
2648 
2649     PANDA_PUBLIC_API void SetVnObject(VnObject *vnObj) override;
2650     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
2651     Inst *Clone(const Graph *targetGraph) const override;
2652 };
2653 
2654 /// Binary operation that shifts its second operand prior the application.
2655 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2656 class PANDA_PUBLIC_API BinaryShiftedRegisterOperation : public FixedInputsInst<2U>,
2657                                                         public ImmediateMixin,
2658                                                         public ShiftTypeMixin {
2659 public:
2660     using FixedInputsInst::FixedInputsInst;
2661 
BinaryShiftedRegisterOperation(Initializer t,uint64_t imm,ShiftType shiftType)2662     BinaryShiftedRegisterOperation(Initializer t, uint64_t imm, ShiftType shiftType)
2663         : FixedInputsInst(std::move(t)), ImmediateMixin(imm), ShiftTypeMixin(shiftType)
2664     {
2665     }
2666 
GetInputType(size_t index)2667     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2668     {
2669         ASSERT(index < GetInputsCount());
2670         return GetType();
2671     }
2672 
2673     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
2674     PANDA_PUBLIC_API void SetVnObject(VnObject *vnObj) override;
2675     PANDA_PUBLIC_API Inst *Clone(const Graph *targetGraph) const override;
2676 };
2677 
2678 class SpillFillInst;
2679 
2680 /// Mixin to hold location data
2681 class LocationDataMixin {
2682 public:
SetLocationData(SpillFillData locationData)2683     void SetLocationData(SpillFillData locationData)
2684     {
2685         locationData_ = locationData;
2686     }
2687 
GetLocationData()2688     auto GetLocationData() const
2689     {
2690         return locationData_;
2691     }
2692 
GetLocationData()2693     auto &GetLocationData()
2694     {
2695         return locationData_;
2696     }
2697 
2698 protected:
2699     LocationDataMixin() = default;
2700     NO_COPY_SEMANTIC(LocationDataMixin);
2701     NO_MOVE_SEMANTIC(LocationDataMixin);
2702     virtual ~LocationDataMixin() = default;
2703 
2704 private:
2705     SpillFillData locationData_ {};
2706 };
2707 
2708 /// Mixin to hold input types of call instruction
2709 template <typename T>
2710 class InputTypesMixin : public T {
2711 public:
2712     using Base = T;
2713     using Base::AppendInput;
2714     using Base::Base;
2715 
AllocateInputTypes(ArenaAllocator * allocator,size_t capacity)2716     void AllocateInputTypes(ArenaAllocator *allocator, size_t capacity)
2717     {
2718         ASSERT(allocator != nullptr);
2719         ASSERT(inputTypes_ == nullptr);
2720         inputTypes_ = allocator->New<ArenaVector<DataType::Type>>(allocator->Adapter());
2721         ASSERT(inputTypes_ != nullptr);
2722         inputTypes_->reserve(capacity);
2723         ASSERT(inputTypes_->capacity() >= capacity);
2724     }
AddInputType(DataType::Type type)2725     void AddInputType(DataType::Type type)
2726     {
2727         ASSERT(inputTypes_ != nullptr);
2728         inputTypes_->push_back(type);
2729     }
SetInputType(unsigned index,DataType::Type type)2730     void SetInputType(unsigned index, DataType::Type type)
2731     {
2732         ASSERT(inputTypes_ != nullptr);
2733         (*inputTypes_)[index] = type;
2734     }
GetInputTypes()2735     ArenaVector<DataType::Type> *GetInputTypes()
2736     {
2737         return inputTypes_;
2738     }
CloneTypes(ArenaAllocator * allocator,InputTypesMixin<T> * targetInst)2739     void CloneTypes(ArenaAllocator *allocator, InputTypesMixin<T> *targetInst) const
2740     {
2741         if (UNLIKELY(inputTypes_ == nullptr)) {
2742             return;
2743         }
2744         targetInst->AllocateInputTypes(allocator, inputTypes_->size());
2745         for (auto inputType : *inputTypes_) {
2746             targetInst->AddInputType(inputType);
2747         }
2748     }
AppendInputs(const std::initializer_list<std::pair<Inst *,DataType::Type>> & inputs)2749     void AppendInputs(const std::initializer_list<std::pair<Inst *, DataType::Type>> &inputs)
2750     {
2751         static_assert(std::is_base_of_v<DynamicInputsInst, T>);
2752         for (auto [input, type] : inputs) {
2753             static_cast<Inst *>(this)->AppendInput(input);
2754             AddInputType(type);
2755         }
2756     }
2757 
AppendInput(Inst * input,DataType::Type type)2758     void AppendInput(Inst *input, DataType::Type type)
2759     {
2760         static_assert(std::is_base_of_v<DynamicInputsInst, T>);
2761         static_cast<Inst *>(this)->AppendInput(input);
2762         AddInputType(type);
2763     }
2764 
SetInputs(ArenaAllocator * allocator,const std::initializer_list<std::pair<Inst *,DataType::Type>> & inputs)2765     void SetInputs(ArenaAllocator *allocator, const std::initializer_list<std::pair<Inst *, DataType::Type>> &inputs)
2766     {
2767         static_assert(std::is_base_of_v<DynamicInputsInst, T>);
2768         static_cast<Inst *>(this)->ReserveInputs(inputs.size());
2769         AllocateInputTypes(allocator, inputs.size());
2770         AppendInputs(inputs);
2771     }
2772 
2773 protected:
2774     // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
2775     ArenaVector<DataType::Type> *inputTypes_ {nullptr};
2776 };
2777 
2778 /// Mixin to hold method data
2779 class MethodDataMixin {
2780 public:
2781     static constexpr uint32_t INVALID_METHOD_ID = std::numeric_limits<uint32_t>::max();
2782 
MethodDataMixin(uint32_t id,RuntimeInterface::MethodPtr method)2783     MethodDataMixin(uint32_t id, RuntimeInterface::MethodPtr method)
2784     {
2785         methodId_ = id;
2786         method_ = method;
2787     }
2788 
SetCallMethodId(uint32_t id)2789     void SetCallMethodId(uint32_t id)
2790     {
2791         methodId_ = id;
2792     }
GetCallMethodId()2793     auto GetCallMethodId() const
2794     {
2795         return methodId_;
2796     }
SetCallMethod(RuntimeInterface::MethodPtr method)2797     void SetCallMethod(RuntimeInterface::MethodPtr method)
2798     {
2799         method_ = method;
2800     }
GetCallMethod()2801     RuntimeInterface::MethodPtr GetCallMethod() const
2802     {
2803         return method_;
2804     }
SetFunctionObject(uintptr_t func)2805     void SetFunctionObject(uintptr_t func)
2806     {
2807         function_ = func;
2808     }
GetFunctionObject()2809     auto GetFunctionObject() const
2810     {
2811         return function_;
2812     }
2813 
2814 protected:
2815     MethodDataMixin() = default;
2816     NO_COPY_SEMANTIC(MethodDataMixin);
2817     NO_MOVE_SEMANTIC(MethodDataMixin);
2818     virtual ~MethodDataMixin() = default;
2819 
2820 private:
2821     uint32_t methodId_ {INVALID_METHOD_ID};
2822     RuntimeInterface::MethodPtr method_ {nullptr};
2823     uintptr_t function_ {0};
2824 };
2825 
2826 /// ResolveStatic
2827 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2828 class PANDA_PUBLIC_API ResolveStaticInst : public FixedInputsInst1, public MethodDataMixin {
2829 public:
2830     using Base = FixedInputsInst1;
2831     using Base::Base;
2832 
ResolveStaticInst(Inst::Initializer t,uint32_t methodId,RuntimeInterface::MethodPtr method)2833     ResolveStaticInst(Inst::Initializer t, uint32_t methodId, RuntimeInterface::MethodPtr method)
2834         : Base(std::move(t)), MethodDataMixin(methodId, method)
2835     {
2836     }
2837 
GetInputType(size_t index)2838     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2839     {
2840         ASSERT(index == 0);
2841         // This is SaveState input
2842         return DataType::NO_TYPE;
2843     }
2844 
Clone(const Graph * targetGraph)2845     Inst *Clone(const Graph *targetGraph) const override
2846     {
2847         ASSERT(targetGraph != nullptr);
2848         auto instClone = FixedInputsInst1::Clone(targetGraph);
2849         auto rslvClone = static_cast<ResolveStaticInst *>(instClone);
2850         rslvClone->SetCallMethodId(GetCallMethodId());
2851         rslvClone->SetCallMethod(GetCallMethod());
2852         return instClone;
2853     }
2854 
2855     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
2856 };
2857 
2858 /// ResolveVirtual
2859 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2860 class PANDA_PUBLIC_API ResolveVirtualInst : public FixedInputsInst2, public MethodDataMixin {
2861 public:
2862     using Base = FixedInputsInst2;
2863     using Base::Base;
2864 
ResolveVirtualInst(Inst::Initializer t,uint32_t methodId,RuntimeInterface::MethodPtr method)2865     ResolveVirtualInst(Inst::Initializer t, uint32_t methodId, RuntimeInterface::MethodPtr method)
2866         : Base(std::move(t)), MethodDataMixin(methodId, method)
2867     {
2868     }
2869 
GetInputType(size_t index)2870     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2871     {
2872         ASSERT(GetInputsCount() == 2U && index < GetInputsCount());
2873         switch (index) {
2874             case 0:
2875                 return DataType::REFERENCE;
2876             case 1:
2877                 // This is SaveState input
2878                 return DataType::NO_TYPE;
2879             default:
2880                 UNREACHABLE();
2881         }
2882     }
2883 
Clone(const Graph * targetGraph)2884     Inst *Clone(const Graph *targetGraph) const override
2885     {
2886         ASSERT(targetGraph != nullptr);
2887         auto instClone = FixedInputsInst2::Clone(targetGraph);
2888         auto rslvClone = static_cast<ResolveVirtualInst *>(instClone);
2889         rslvClone->SetCallMethodId(GetCallMethodId());
2890         rslvClone->SetCallMethod(GetCallMethod());
2891         return instClone;
2892     }
2893 
2894     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
2895 };
2896 
2897 class PANDA_PUBLIC_API InitStringInst : public FixedInputsInst2 {
2898 public:
2899     using Base = FixedInputsInst2;
2900     using Base::Base;
2901 
InitStringInst(Initializer t,StringCtorType ctorType)2902     InitStringInst(Initializer t, StringCtorType ctorType) : Base(std::move(t))
2903     {
2904         SetStringCtorType(ctorType);
2905     }
2906 
IsFromString()2907     bool IsFromString() const
2908     {
2909         return GetField<StringCtorTypeField>() == StringCtorType::STRING;
2910     }
IsFromCharArray()2911     bool IsFromCharArray() const
2912     {
2913         return GetField<StringCtorTypeField>() == StringCtorType::CHAR_ARRAY;
2914     }
2915 
GetInputType(size_t index)2916     DataType::Type GetInputType(size_t index) const override
2917     {
2918         ASSERT(index < GetInputsCount());
2919         if (index == 0) {
2920             return DataType::REFERENCE;
2921         }
2922         return DataType::NO_TYPE;
2923     }
2924 
SetStringCtorType(StringCtorType ctorType)2925     void SetStringCtorType(StringCtorType ctorType)
2926     {
2927         SetField<StringCtorTypeField>(ctorType);
2928     }
2929 
GetStringCtorType()2930     StringCtorType GetStringCtorType() const
2931     {
2932         return GetField<StringCtorTypeField>();
2933     }
2934 
2935     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
2936 
Clone(const Graph * targetGraph)2937     Inst *Clone(const Graph *targetGraph) const override
2938     {
2939         auto clone = FixedInputsInst2::Clone(targetGraph);
2940         clone->CastToInitString()->SetStringCtorType(GetStringCtorType());
2941         return clone;
2942     }
2943 
2944 private:
2945     using StringCtorTypeField = Base::LastField::NextField<StringCtorType, MinimumBitsToStore(StringCtorType::COUNT)>;
2946     using LastField = StringCtorTypeField;
2947 };
2948 
2949 /// Call instruction
2950 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2951 class PANDA_PUBLIC_API CallInst : public InlinedInstMixin<InputTypesMixin<DynamicInputsInst>>, public MethodDataMixin {
2952 public:
2953     using Base = InlinedInstMixin<InputTypesMixin<DynamicInputsInst>>;
2954     using Base::Base;
2955 
2956     CallInst(Initializer t, uint32_t methodId, RuntimeInterface::MethodPtr method = nullptr)
Base(std::move (t))2957         : Base(std::move(t)), MethodDataMixin(methodId, method)
2958     {
2959     }
2960 
GetObjectIndex()2961     int GetObjectIndex() const
2962     {
2963         return GetOpcode() == Opcode::CallResolvedVirtual ? 1 : 0;
2964     }
2965 
GetObjectInst()2966     Inst *GetObjectInst()
2967     {
2968         return GetInput(GetObjectIndex()).GetInst();
2969     }
2970 
GetObjectInst()2971     const Inst *GetObjectInst() const
2972     {
2973         return GetInput(GetObjectIndex()).GetInst();
2974     }
2975 
GetInputType(size_t index)2976     DataType::Type GetInputType(size_t index) const override
2977     {
2978         ASSERT(inputTypes_ != nullptr);
2979         ASSERT(index < inputTypes_->size());
2980         ASSERT(index < GetInputsCount());
2981         return (*inputTypes_)[index];
2982     }
2983 
2984     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
2985 
SetIsNative(bool isNative)2986     void SetIsNative(bool isNative)
2987     {
2988         SetField<IsNativeFlag>(isNative);
2989     }
2990 
GetIsNative()2991     bool GetIsNative() const
2992     {
2993         return GetField<IsNativeFlag>();
2994     }
2995 
SetCanNativeException(bool isNative)2996     void SetCanNativeException(bool isNative)
2997     {
2998         SetField<IsNativeExceptionFlag>(isNative);
2999     }
3000 
GetCanNativeException()3001     bool GetCanNativeException() const
3002     {
3003         return GetField<IsNativeExceptionFlag>();
3004     }
3005 
3006     PANDA_PUBLIC_API Inst *Clone(const Graph *targetGraph) const override;
3007 
IsRuntimeCall()3008     bool IsRuntimeCall() const override
3009     {
3010         if (IsNativeApiCall()) {
3011             // checking runtime_call flag is enough
3012             return Base::IsRuntimeCall();
3013         }
3014         return !IsInlined();
3015     }
3016 
3017 protected:
3018     using IsNativeFlag = LastField::NextFlag;
3019     using IsNativeExceptionFlag = IsNativeFlag::NextFlag;
3020     using LastField = IsNativeExceptionFlag;
3021 };
3022 
3023 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
3024 class PANDA_PUBLIC_API CallIndirectInst : public InputTypesMixin<DynamicInputsInst> {
3025 public:
3026     using Base = InputTypesMixin<DynamicInputsInst>;
3027     using Base::Base;
3028 
CallIndirectInst(Initializer t)3029     explicit CallIndirectInst(Initializer t) : Base(std::move(t)) {}
3030 
GetInputType(size_t index)3031     DataType::Type GetInputType(size_t index) const override
3032     {
3033         ASSERT(inputTypes_ != nullptr);
3034         ASSERT(index < inputTypes_->size());
3035         ASSERT(index < GetInputsCount());
3036         return (*inputTypes_)[index];
3037     }
3038 
3039     PANDA_PUBLIC_API Inst *Clone(const Graph *targetGraph) const override;
3040 };
3041 
3042 /// Length methods instruction
3043 class LengthMethodInst : public ArrayInstMixin<FixedInputsInst1> {
3044 public:
3045     using Base = ArrayInstMixin<FixedInputsInst1>;
3046     using Base::Base;
3047 
Base(opcode)3048     explicit LengthMethodInst(Opcode opcode, bool isArray = true) : Base(opcode)
3049     {
3050         SetIsArray(isArray);
3051     }
Base(std::move (t))3052     explicit LengthMethodInst(Initializer t, bool isArray = true) : Base(std::move(t))
3053     {
3054         SetIsArray(isArray);
3055     }
3056 
GetInputType(size_t index)3057     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
3058     {
3059         ASSERT(index < GetInputsCount());
3060         return DataType::REFERENCE;
3061     }
3062 
Clone(const Graph * targetGraph)3063     Inst *Clone(const Graph *targetGraph) const override
3064     {
3065         auto clone = FixedInputsInst::Clone(targetGraph);
3066         ASSERT(static_cast<LengthMethodInst *>(clone)->IsArray() == IsArray());
3067         return clone;
3068     }
3069 };
3070 
3071 /// Compare instruction
3072 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
3073 class PANDA_PUBLIC_API CompareInst : public InstWithOperandsType<ConditionMixin<FixedInputsInst2>> {
3074 public:
3075     using BaseInst = InstWithOperandsType<ConditionMixin<FixedInputsInst2>>;
3076     using BaseInst::BaseInst;
3077 
CompareInst(Initializer t,ConditionCode cc)3078     CompareInst(Initializer t, ConditionCode cc) : BaseInst(std::move(t))
3079     {
3080         SetCc(cc);
3081     }
CompareInst(Initializer t,DataType::Type operType,ConditionCode cc)3082     CompareInst(Initializer t, DataType::Type operType, ConditionCode cc) : BaseInst(std::move(t))
3083     {
3084         SetOperandsType(operType);
3085         SetCc(cc);
3086     }
3087 
3088     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
3089     PANDA_PUBLIC_API void SetVnObject(VnObject *vnObj) override;
3090 
GetInputType(size_t index)3091     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
3092     {
3093         ASSERT(index < GetInputsCount());
3094         return GetOperandsType();
3095     }
3096 
Clone(const Graph * targetGraph)3097     Inst *Clone(const Graph *targetGraph) const override
3098     {
3099         auto clone = FixedInputsInst::Clone(targetGraph);
3100         ASSERT(clone->CastToCompare()->GetCc() == GetCc());
3101         ASSERT(clone->CastToCompare()->GetOperandsType() == GetOperandsType());
3102         return clone;
3103     }
3104 };
3105 
3106 /// Mixin for AnyTypeMixin instructions
3107 template <typename T>
3108 class AnyTypeMixin : public T {
3109 public:
3110     using T::T;
3111 
SetAnyType(AnyBaseType anyType)3112     void SetAnyType(AnyBaseType anyType)
3113     {
3114         T::template SetField<AnyBaseTypeField>(anyType);
3115     }
3116 
GetAnyType()3117     AnyBaseType GetAnyType() const override
3118     {
3119         return T::template GetField<AnyBaseTypeField>();
3120     }
3121 
IsIntegerWasSeen()3122     bool IsIntegerWasSeen() const
3123     {
3124         return T::template GetField<IntegerWasSeen>();
3125     }
3126 
SetIsIntegerWasSeen(bool value)3127     void SetIsIntegerWasSeen(bool value)
3128     {
3129         T::template SetField<IntegerWasSeen>(value);
3130     }
3131 
IsSpecialWasSeen()3132     bool IsSpecialWasSeen() const
3133     {
3134         return T::template GetField<SpecialWasSeen>();
3135     }
3136 
GetAllowedInputType()3137     profiling::AnyInputType GetAllowedInputType() const
3138     {
3139         return T::template GetField<AllowedInputType>();
3140     }
3141 
SetAllowedInputType(profiling::AnyInputType type)3142     void SetAllowedInputType(profiling::AnyInputType type)
3143     {
3144         T::template SetField<AllowedInputType>(type);
3145     }
3146 
IsTypeWasProfiled()3147     bool IsTypeWasProfiled() const
3148     {
3149         return T::template GetField<TypeWasProfiled>();
3150     }
3151 
SetIsTypeWasProfiled(bool value)3152     void SetIsTypeWasProfiled(bool value)
3153     {
3154         T::template SetField<TypeWasProfiled>(value);
3155     }
3156 
3157 protected:
3158     using AnyBaseTypeField =
3159         typename T::LastField::template NextField<AnyBaseType, MinimumBitsToStore(AnyBaseType::COUNT)>;
3160     using IntegerWasSeen = typename AnyBaseTypeField::NextFlag;
3161     using SpecialWasSeen = typename IntegerWasSeen::NextFlag;
3162     using AllowedInputType =
3163         typename AnyBaseTypeField::template NextField<profiling::AnyInputType,
3164                                                       MinimumBitsToStore(profiling::AnyInputType::LAST)>;
3165     static_assert(SpecialWasSeen::END_BIT == AllowedInputType::END_BIT);
3166     using TypeWasProfiled = typename AllowedInputType::NextFlag;
3167     using LastField = TypeWasProfiled;
3168 };
3169 
3170 /// CompareAnyTypeInst instruction
3171 class PANDA_PUBLIC_API CompareAnyTypeInst : public AnyTypeMixin<FixedInputsInst1> {
3172 public:
3173     using BaseInst = AnyTypeMixin<FixedInputsInst1>;
3174     using BaseInst::BaseInst;
3175 
3176     CompareAnyTypeInst(Opcode opcode, uint32_t pc, Inst *input0, AnyBaseType anyType = AnyBaseType::UNDEFINED_TYPE)
3177         : BaseInst({opcode, DataType::Type::BOOL, pc}, input0)
3178     {
3179         SetAnyType(anyType);
3180     }
3181 
GetInputType(size_t index)3182     DataType::Type GetInputType(size_t index) const override
3183     {
3184         ASSERT(index < GetInputsCount());
3185         return GetInput(index).GetInst()->GetType();
3186     }
3187 
3188     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
3189     PANDA_PUBLIC_API void SetVnObject(VnObject *vnObj) override;
3190 
Clone(const Graph * targetGraph)3191     Inst *Clone(const Graph *targetGraph) const override
3192     {
3193         auto clone = FixedInputsInst::Clone(targetGraph);
3194         ASSERT(clone->CastToCompareAnyType()->GetAnyType() == GetAnyType());
3195         return clone;
3196     }
3197 };
3198 
3199 /// GetAnyTypeName instruction
3200 class PANDA_PUBLIC_API GetAnyTypeNameInst : public AnyTypeMixin<FixedInputsInst0> {
3201 public:
3202     using BaseInst = AnyTypeMixin<FixedInputsInst0>;
3203     using BaseInst::BaseInst;
3204 
3205     GetAnyTypeNameInst(Opcode opcode, uint32_t pc, AnyBaseType anyType = AnyBaseType::UNDEFINED_TYPE)
3206         : BaseInst({opcode, DataType::Type::ANY, pc})
3207     {
3208         SetAnyType(anyType);
3209     }
3210 
3211     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
3212     PANDA_PUBLIC_API void SetVnObject(VnObject *vnObj) override;
3213 
Clone(const Graph * targetGraph)3214     Inst *Clone(const Graph *targetGraph) const override
3215     {
3216         auto clone = FixedInputsInst::Clone(targetGraph);
3217         clone->CastToGetAnyTypeName()->SetAnyType(GetAnyType());
3218         return clone;
3219     }
3220 };
3221 
3222 /// CastAnyTypeValueInst instruction
3223 class PANDA_PUBLIC_API CastAnyTypeValueInst : public AnyTypeMixin<FixedInputsInst1> {
3224 public:
3225     using BaseInst = AnyTypeMixin<FixedInputsInst1>;
3226     using BaseInst::BaseInst;
3227 
3228     CastAnyTypeValueInst(Opcode opcode, uint32_t pc, Inst *input0, AnyBaseType anyType = AnyBaseType::UNDEFINED_TYPE)
3229         : BaseInst({opcode, AnyBaseTypeToDataType(anyType), pc}, input0)
3230     {
3231         SetAnyType(anyType);
3232     }
3233 
GetInputType(size_t index)3234     DataType::Type GetInputType(size_t index) const override
3235     {
3236         ASSERT(index < GetInputsCount());
3237         return GetInput(index).GetInst()->GetType();
3238     }
3239 
GetDeducedType()3240     DataType::Type GetDeducedType() const
3241     {
3242         return AnyBaseTypeToDataType(GetAnyType());
3243     }
3244 
3245     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
3246 
Clone(const Graph * targetGraph)3247     Inst *Clone(const Graph *targetGraph) const override
3248     {
3249         auto clone = FixedInputsInst::Clone(targetGraph)->CastToCastAnyTypeValue();
3250         ASSERT(clone->GetAnyType() == GetAnyType());
3251         ASSERT(clone->GetType() == GetType());
3252         return clone;
3253     }
3254 };
3255 
3256 /// CastValueToAnyTypeInst instruction
3257 class PANDA_PUBLIC_API CastValueToAnyTypeInst : public AnyTypeMixin<FixedInputsInst1> {
3258 public:
3259     using BaseInst = AnyTypeMixin<FixedInputsInst1>;
3260     using BaseInst::BaseInst;
3261 
CastValueToAnyTypeInst(Opcode opcode,uint32_t pc,AnyBaseType anyType,Inst * input0)3262     CastValueToAnyTypeInst(Opcode opcode, uint32_t pc, AnyBaseType anyType, Inst *input0)
3263         : BaseInst({opcode, DataType::ANY, pc}, input0)
3264     {
3265         SetAnyType(anyType);
3266     }
3267 
GetInputType(size_t index)3268     DataType::Type GetInputType(size_t index) const override
3269     {
3270         ASSERT(index < GetInputsCount());
3271         return GetInput(index).GetInst()->GetType();
3272     }
3273 
3274     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
3275 
Clone(const Graph * targetGraph)3276     Inst *Clone(const Graph *targetGraph) const override
3277     {
3278         auto clone = FixedInputsInst::Clone(targetGraph)->CastToCastValueToAnyType();
3279         ASSERT(clone->GetAnyType() == GetAnyType());
3280         ASSERT(clone->GetType() == GetType());
3281         return clone;
3282     }
3283 };
3284 
3285 /// AnyTypeCheckInst instruction
3286 class PANDA_PUBLIC_API AnyTypeCheckInst : public AnyTypeMixin<FixedInputsInst2> {
3287 public:
3288     using BaseInst = AnyTypeMixin<FixedInputsInst2>;
3289     using BaseInst::BaseInst;
3290 
BaseInst(std::move (t))3291     explicit AnyTypeCheckInst(Initializer t, AnyBaseType anyType = AnyBaseType::UNDEFINED_TYPE) : BaseInst(std::move(t))
3292     {
3293         SetAnyType(anyType);
3294     }
3295 
GetInputType(size_t index)3296     DataType::Type GetInputType(size_t index) const override
3297     {
3298         ASSERT(index < GetInputsCount());
3299         return (index == 0) ? DataType::ANY : DataType::NO_TYPE;
3300     }
3301 
3302     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
3303 
Clone(const Graph * targetGraph)3304     Inst *Clone(const Graph *targetGraph) const override
3305     {
3306         auto clone = FixedInputsInst::Clone(targetGraph);
3307         ASSERT(clone->CastToAnyTypeCheck()->GetAnyType() == GetAnyType());
3308         return clone;
3309     }
3310 
3311     DeoptimizeType GetDeoptimizeType() const;
3312 };
3313 
3314 /// HclassCheck instruction
3315 enum class HclassChecks {
3316     ALL_CHECKS = 0,
3317     IS_FUNCTION,
3318     IS_NOT_CLASS_CONSTRUCTOR,
3319 };
3320 class PANDA_PUBLIC_API HclassCheckInst : public AnyTypeMixin<FixedInputsInst2> {
3321 public:
3322     using BaseInst = AnyTypeMixin<FixedInputsInst2>;
3323     using BaseInst::BaseInst;
3324 
BaseInst(std::move (t))3325     explicit HclassCheckInst(Initializer t, AnyBaseType anyType = AnyBaseType::UNDEFINED_TYPE) : BaseInst(std::move(t))
3326     {
3327         SetAnyType(anyType);
3328     }
3329 
GetInputType(size_t index)3330     DataType::Type GetInputType(size_t index) const override
3331     {
3332         ASSERT(index < GetInputsCount());
3333         return (index == 0) ? DataType::REFERENCE : DataType::NO_TYPE;
3334     }
3335 
SetCheckIsFunction(bool value)3336     void SetCheckIsFunction(bool value)
3337     {
3338         SetField<CheckTypeIsFunction>(value);
3339     }
3340 
GetCheckIsFunction()3341     bool GetCheckIsFunction() const
3342     {
3343         return GetField<CheckTypeIsFunction>();
3344     }
3345 
3346     void SetCheckFunctionIsNotClassConstructor(bool value = true)
3347     {
3348         SetField<CheckFunctionIsNotClassConstructor>(value);
3349     }
3350 
GetCheckFunctionIsNotClassConstructor()3351     bool GetCheckFunctionIsNotClassConstructor() const
3352     {
3353         return GetField<CheckFunctionIsNotClassConstructor>();
3354     }
3355 
3356     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
3357 
3358     void ExtendFlags(Inst *inst);
3359 
Clone(const Graph * targetGraph)3360     Inst *Clone(const Graph *targetGraph) const override
3361     {
3362         auto clone = static_cast<HclassCheckInst *>(FixedInputsInst2::Clone(targetGraph));
3363         clone->SetCheckFunctionIsNotClassConstructor(GetCheckFunctionIsNotClassConstructor());
3364         clone->SetCheckIsFunction(GetCheckIsFunction());
3365         return clone;
3366     }
3367 
3368 protected:
3369     using CheckTypeIsFunction = LastField::NextFlag;
3370     using CheckFunctionIsNotClassConstructor = CheckTypeIsFunction::NextFlag;
3371     using LastField = CheckFunctionIsNotClassConstructor;
3372 };
3373 
3374 /**
3375  * ConstantInst represent constant value.
3376  *
3377  * Available types: INT64, FLOAT32, FLOAT64, ANY. All integer types are stored as INT64 value.
3378  * Once type of constant is set, it can't be changed anymore.
3379  */
3380 class PANDA_PUBLIC_API ConstantInst : public FixedInputsInst<0> {
3381 public:
3382     using FixedInputsInst::FixedInputsInst;
3383 
3384     template <typename T>
FixedInputsInst(Opcode::Constant)3385     explicit ConstantInst(Opcode /* unused */, T value, bool supportInt32 = false) : FixedInputsInst(Opcode::Constant)
3386     {
3387         ASSERT(GetTypeFromCType<T>() != DataType::NO_TYPE);
3388         // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-branch-clone)
3389         if constexpr (GetTypeFromCType<T>() == DataType::FLOAT64) {
3390             value_ = bit_cast<uint64_t, double>(value);
3391             // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
3392         } else if constexpr (GetTypeFromCType<T>() == DataType::FLOAT32) {
3393             value_ = bit_cast<uint32_t, float>(value);
3394             // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
3395         } else if constexpr (GetTypeFromCType<T>() == DataType::ANY) {
3396             value_ = value.Raw();
3397             // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
3398         } else if (GetTypeFromCType<T>(supportInt32) == DataType::INT32) {
3399             value_ = static_cast<int32_t>(value);
3400             // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
3401         } else {
3402             value_ = value;
3403         }
3404 
3405         SetType(GetTypeFromCType<T>(supportInt32));
3406     }
3407 
IsSafeInst()3408     bool IsSafeInst() const override
3409     {
3410         return true;
3411     }
3412 
GetRawValue()3413     uint64_t GetRawValue() const
3414     {
3415         return value_;
3416     }
3417 
GetInt32Value()3418     uint32_t GetInt32Value() const
3419     {
3420         ASSERT(GetType() == DataType::INT32);
3421         return static_cast<uint32_t>(value_);
3422     }
3423 
GetInt64Value()3424     uint64_t GetInt64Value() const
3425     {
3426         ASSERT(GetType() == DataType::INT64);
3427         return value_;
3428     }
3429 
GetIntValue()3430     uint64_t GetIntValue() const
3431     {
3432         ASSERT(GetType() == DataType::INT64 || GetType() == DataType::INT32);
3433         return value_;
3434     }
3435 
GetFloatValue()3436     float GetFloatValue() const
3437     {
3438         ASSERT(GetType() == DataType::FLOAT32);
3439         return bit_cast<float, uint32_t>(static_cast<uint32_t>(value_));
3440     }
3441 
GetDoubleValue()3442     double GetDoubleValue() const
3443     {
3444         ASSERT(GetType() == DataType::FLOAT64);
3445         return bit_cast<double, uint64_t>(value_);
3446     }
3447 
GetNextConst()3448     ConstantInst *GetNextConst()
3449     {
3450         return nextConst_;
3451     }
SetNextConst(ConstantInst * nextConst)3452     void SetNextConst(ConstantInst *nextConst)
3453     {
3454         nextConst_ = nextConst;
3455     }
3456 
3457     template <typename T>
3458     static constexpr DataType::Type GetTypeFromCType(bool supportInt32 = false)
3459     {
3460         // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-branch-clone)
3461         if constexpr (std::is_integral_v<T>) {
3462             if (supportInt32 && sizeof(T) == sizeof(uint32_t)) {
3463                 return DataType::INT32;
3464             }
3465             return DataType::INT64;
3466             // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
3467         } else if constexpr (std::is_same_v<T, float>) {
3468             return DataType::FLOAT32;
3469             // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
3470         } else if constexpr (std::is_same_v<T, double>) {
3471             return DataType::FLOAT64;
3472             // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
3473         } else if constexpr (std::is_same_v<T, DataType::Any>) {
3474             return DataType::ANY;
3475         }
3476         return DataType::NO_TYPE;
3477     }
3478 
3479     inline bool IsEqualConst(double value, [[maybe_unused]] bool supportInt32 = false)
3480     {
3481         // Special compare for NaN
3482         if (GetType() == DataType::FLOAT64 && std::isnan(value) && std::isnan(bit_cast<double, uint64_t>(value_))) {
3483             return true;
3484         }
3485         return IsEqualConst(DataType::FLOAT64, bit_cast<uint64_t, double>(value));
3486     }
3487     inline bool IsEqualConst(float value, [[maybe_unused]] bool supportInt32 = false)
3488     {
3489         // Special compare for NaN
3490         if (GetType() == DataType::FLOAT32 && std::isnan(value) && std::isnan(bit_cast<float, uint32_t>(value_))) {
3491             return true;
3492         }
3493         return IsEqualConst(DataType::FLOAT32, bit_cast<uint32_t, float>(value));
3494     }
3495     inline bool IsEqualConst(DataType::Any value, [[maybe_unused]] bool supportInt32 = false)
3496     {
3497         return IsEqualConst(DataType::ANY, value.Raw());
3498     }
IsEqualConst(DataType::Type type,uint64_t value)3499     inline bool IsEqualConst(DataType::Type type, uint64_t value)
3500     {
3501         return GetType() == type && value_ == value;
3502     }
3503     template <typename T>
3504     inline bool IsEqualConst(T value, bool supportInt32 = false)
3505     {
3506         static_assert(GetTypeFromCType<T>() == DataType::INT64);
3507         if (supportInt32 && sizeof(T) == sizeof(uint32_t)) {
3508             return (GetType() == DataType::INT32 && static_cast<int32_t>(value_) == static_cast<int32_t>(value));
3509         }
3510         return (GetType() == DataType::INT64 && value_ == static_cast<uint64_t>(value));
3511     }
3512 
3513     inline bool IsEqualConstAllTypes(int64_t value, bool supportInt32 = false)
3514     {
3515         return IsEqualConst(value, supportInt32) || IsEqualConst(static_cast<float>(value)) ||
3516                IsEqualConst(static_cast<double>(value));
3517     }
3518 
IsBoolConst()3519     bool IsBoolConst() const override
3520     {
3521         ASSERT(IsConst());
3522         return (GetType() == DataType::INT32 || GetType() == DataType::INT64) &&
3523                (GetIntValue() == 0 || GetIntValue() == 1);
3524     }
3525 
IsNaNConst()3526     bool IsNaNConst() const
3527     {
3528         ASSERT(DataType::IsFloatType(GetType()));
3529         if (GetType() == DataType::FLOAT32) {
3530             return std::isnan(GetFloatValue());
3531         }
3532         // DataType::FLOAT64
3533         return std::isnan(GetDoubleValue());
3534     }
3535 
SetImmTableSlot(ImmTableSlot immSlot)3536     void SetImmTableSlot(ImmTableSlot immSlot)
3537     {
3538         immSlot_ = immSlot;
3539     }
3540 
GetImmTableSlot()3541     auto GetImmTableSlot() const
3542     {
3543         return immSlot_;
3544     }
3545 
GetDstLocation()3546     Location GetDstLocation() const override
3547     {
3548         if (GetImmTableSlot() != GetInvalidImmTableSlot()) {
3549             return Location::MakeConstant(GetImmTableSlot());
3550         }
3551         return Inst::GetDstLocation();
3552     }
3553 
3554     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
3555 
3556     PANDA_PUBLIC_API Inst *Clone(const Graph *targetGraph) const override;
3557 
3558 private:
3559     uint64_t value_ {0};
3560     ConstantInst *nextConst_ {nullptr};
3561     ImmTableSlot immSlot_ {GetInvalidImmTableSlot()};
3562 };
3563 
3564 // Type describing the purpose of the SpillFillInst.
3565 // RegAlloc may use this information to preserve correct order of several SpillFillInst
3566 // instructions placed along each other in the graph.
3567 enum SpillFillType {
3568     UNKNOWN,
3569     INPUT_FILL,
3570     CONNECT_SPLIT_SIBLINGS,
3571     SPLIT_MOVE,
3572 };
3573 
3574 class PANDA_PUBLIC_API SpillFillInst : public FixedInputsInst0 {
3575 public:
3576     explicit SpillFillInst(ArenaAllocator *allocator, Opcode opcode, SpillFillType type = UNKNOWN)
FixedInputsInst0(opcode)3577         : FixedInputsInst0(opcode), spillFills_(allocator->Adapter()), sfType_(type)
3578     {
3579     }
3580 
AddMove(Register src,Register dst,DataType::Type type)3581     void AddMove(Register src, Register dst, DataType::Type type)
3582     {
3583         AddSpillFill(Location::MakeRegister(src, type), Location::MakeRegister(dst, type), type);
3584     }
3585 
AddSpill(Register src,StackSlot dst,DataType::Type type)3586     void AddSpill(Register src, StackSlot dst, DataType::Type type)
3587     {
3588         AddSpillFill(Location::MakeRegister(src, type), Location::MakeStackSlot(dst), type);
3589     }
3590 
AddFill(StackSlot src,Register dst,DataType::Type type)3591     void AddFill(StackSlot src, Register dst, DataType::Type type)
3592     {
3593         AddSpillFill(Location::MakeStackSlot(src), Location::MakeRegister(dst, type), type);
3594     }
3595 
AddMemCopy(StackSlot src,StackSlot dst,DataType::Type type)3596     void AddMemCopy(StackSlot src, StackSlot dst, DataType::Type type)
3597     {
3598         AddSpillFill(Location::MakeStackSlot(src), Location::MakeStackSlot(dst), type);
3599     }
3600 
AddSpillFill(const SpillFillData & spillFill)3601     void AddSpillFill(const SpillFillData &spillFill)
3602     {
3603         spillFills_.emplace_back(spillFill);
3604     }
3605 
AddSpillFill(const Location & src,const Location & dst,DataType::Type type)3606     void AddSpillFill(const Location &src, const Location &dst, DataType::Type type)
3607     {
3608         spillFills_.emplace_back(SpillFillData {src.GetKind(), dst.GetKind(), src.GetValue(), dst.GetValue(), type});
3609     }
3610 
GetSpillFills()3611     const ArenaVector<SpillFillData> &GetSpillFills() const
3612     {
3613         return spillFills_;
3614     }
3615 
GetSpillFills()3616     ArenaVector<SpillFillData> &GetSpillFills()
3617     {
3618         return spillFills_;
3619     }
3620 
GetSpillFill(size_t n)3621     const SpillFillData &GetSpillFill(size_t n) const
3622     {
3623         ASSERT(n < spillFills_.size());
3624         return spillFills_[n];
3625     }
3626 
GetSpillFill(size_t n)3627     SpillFillData &GetSpillFill(size_t n)
3628     {
3629         ASSERT(n < spillFills_.size());
3630         return spillFills_[n];
3631     }
3632 
RemoveSpillFill(size_t n)3633     void RemoveSpillFill(size_t n)
3634     {
3635         ASSERT(n < spillFills_.size());
3636         spillFills_.erase(spillFills_.begin() + n);
3637     }
3638 
3639     // Get register number, holded by n-th spill-fill
GetInputReg(size_t n)3640     Register GetInputReg(size_t n) const
3641     {
3642         ASSERT(n < spillFills_.size());
3643         ASSERT(spillFills_[n].SrcType() == LocationType::REGISTER);
3644         return spillFills_[n].SrcValue();
3645     }
3646 
ClearSpillFills()3647     void ClearSpillFills()
3648     {
3649         spillFills_.clear();
3650     }
3651 
GetSpillFillType()3652     SpillFillType GetSpillFillType() const
3653     {
3654         return sfType_;
3655     }
3656 
SetSpillFillType(SpillFillType type)3657     void SetSpillFillType(SpillFillType type)
3658     {
3659         sfType_ = type;
3660     }
3661 
3662     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
3663 
3664 #ifndef NDEBUG
Clone(const Graph * targetGraph)3665     Inst *Clone(const Graph *targetGraph) const override
3666     {
3667         auto clone = FixedInputsInst::Clone(targetGraph)->CastToSpillFill();
3668         clone->SetSpillFillType(GetSpillFillType());
3669         for (auto spillFill : spillFills_) {
3670             clone->AddSpillFill(spillFill);
3671         }
3672         return clone;
3673     }
3674 #endif
3675 
3676 private:
3677     ArenaVector<SpillFillData> spillFills_;
3678     SpillFillType sfType_ {UNKNOWN};
3679 };
3680 
3681 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
3682 class PANDA_PUBLIC_API ParameterInst : public FixedInputsInst<0>, public LocationDataMixin {
3683 public:
3684     using FixedInputsInst::FixedInputsInst;
3685     static constexpr uint16_t DYNAMIC_NUM_ARGS = std::numeric_limits<uint16_t>::max();
3686     static constexpr uint16_t INVALID_ARG_REF_NUM = std::numeric_limits<uint16_t>::max();
3687 
3688     explicit ParameterInst(Opcode /* unused */, uint16_t argNumber, DataType::Type type = DataType::NO_TYPE)
3689         : FixedInputsInst({Opcode::Parameter, type, INVALID_PC}), argNumber_(argNumber)
3690     {
3691     }
GetArgNumber()3692     uint16_t GetArgNumber() const
3693     {
3694         return argNumber_;
3695     }
3696 
SetArgNumber(uint16_t argNumber)3697     void SetArgNumber(uint16_t argNumber)
3698     {
3699         argNumber_ = argNumber;
3700     }
GetArgRefNumber()3701     uint16_t GetArgRefNumber() const
3702     {
3703         return argRefNumber_;
3704     }
3705 
SetArgRefNumber(uint16_t argRefNumber)3706     void SetArgRefNumber(uint16_t argRefNumber)
3707     {
3708         argRefNumber_ = argRefNumber;
3709     }
3710     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
3711 
3712     PANDA_PUBLIC_API Inst *Clone(const Graph *targetGraph) const override;
3713 
3714 private:
3715     uint16_t argNumber_ {0};
3716     uint16_t argRefNumber_ {INVALID_ARG_REF_NUM};
3717 };
3718 
IsZeroConstant(const Inst * inst)3719 inline bool IsZeroConstant(const Inst *inst)
3720 {
3721     return inst->IsConst() && inst->GetType() == DataType::INT64 && inst->CastToConstant()->GetIntValue() == 0;
3722 }
3723 
IsZeroConstantOrNullPtr(const Inst * inst)3724 inline bool IsZeroConstantOrNullPtr(const Inst *inst)
3725 {
3726     return IsZeroConstant(inst) || inst->GetOpcode() == Opcode::NullPtr;
3727 }
3728 
3729 /// Phi instruction
3730 class PANDA_PUBLIC_API PhiInst : public AnyTypeMixin<DynamicInputsInst> {
3731 public:
3732     using BaseInst = AnyTypeMixin<DynamicInputsInst>;
3733     using BaseInst::BaseInst;
3734 
BaseInst(std::move (t))3735     explicit PhiInst(Initializer t, AnyBaseType anyType = AnyBaseType::UNDEFINED_TYPE) : BaseInst(std::move(t))
3736     {
3737         SetAnyType(anyType);
3738     }
3739 
3740     /// Get basic block corresponding to given input index. Returned pointer to basic block, can't be nullptr
3741     BasicBlock *GetPhiInputBb(unsigned index);
GetPhiInputBb(unsigned index)3742     const BasicBlock *GetPhiInputBb(unsigned index) const
3743     {
3744         return (const_cast<PhiInst *>(this))->GetPhiInputBb(index);
3745     }
3746 
GetPhiInputBbNum(unsigned index)3747     uint32_t GetPhiInputBbNum(unsigned index) const
3748     {
3749         ASSERT(index < GetInputsCount());
3750         return GetDynamicOperands()->GetUser(index)->GetBbNum();
3751     }
3752 
SetPhiInputBbNum(unsigned index,uint32_t bbNum)3753     void SetPhiInputBbNum(unsigned index, uint32_t bbNum)
3754     {
3755         ASSERT(index < GetInputsCount());
3756         GetDynamicOperands()->GetUser(index)->SetBbNum(bbNum);
3757     }
3758 
Clone(const Graph * targetGraph)3759     Inst *Clone(const Graph *targetGraph) const override
3760     {
3761         auto clone = DynamicInputsInst::Clone(targetGraph);
3762         ASSERT(clone->CastToPhi()->GetAnyType() == GetAnyType());
3763         return clone;
3764     }
3765 
GetAssumedAnyType()3766     AnyBaseType GetAssumedAnyType() const
3767     {
3768         return GetAnyType();
3769     }
3770 
SetAssumedAnyType(AnyBaseType type)3771     void SetAssumedAnyType(AnyBaseType type)
3772     {
3773         SetAnyType(type);
3774     }
3775 
3776     /// Get input instruction corresponding to the given basic block, can't be null.
3777     Inst *GetPhiInput(BasicBlock *bb);
3778     Inst *GetPhiDataflowInput(BasicBlock *bb);
3779     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
3780     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
3781 
3782     // Get index of the given block in phi inputs
3783     size_t GetPredBlockIndex(const BasicBlock *block) const;
3784 
3785 protected:
3786     using FlagIsLive = LastField::NextFlag;
3787     using LastField = FlagIsLive;
3788 };
3789 
3790 /**
3791  * Immediate for SaveState:
3792  * value - constant value to be stored
3793  * vreg - virtual register number
3794  */
3795 struct SaveStateImm {
3796     uint64_t value;
3797     uint16_t vreg;
3798     DataType::Type type;
3799     VRegType vregType;
3800 };
3801 
3802 /**
3803  * Frame state saving instruction
3804  * Aims to save pbc registers before calling something that can raise exception
3805  */
3806 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
3807 class PANDA_PUBLIC_API SaveStateInst : public DynamicInputsInst {
3808 public:
3809     using DynamicInputsInst::DynamicInputsInst;
3810 
SaveStateInst(Opcode opcode,uint32_t pc,void * method,CallInst * inst,uint32_t inliningDepth)3811     SaveStateInst(Opcode opcode, uint32_t pc, void *method, CallInst *inst, uint32_t inliningDepth)
3812         : DynamicInputsInst({opcode, DataType::NO_TYPE, pc}),
3813           method_(method),
3814           callerInst_(inst),
3815           inliningDepth_(inliningDepth)
3816     {
3817     }
3818 
3819     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
3820 
AppendBridge(Inst * inst)3821     void AppendBridge(Inst *inst)
3822     {
3823         ASSERT(inst != nullptr);
3824         auto newInput = AppendInput(inst);
3825         SetVirtualRegister(newInput, VirtualRegister(VirtualRegister::BRIDGE, VRegType::VREG));
3826     }
3827 
SetVirtualRegister(size_t index,VirtualRegister reg)3828     void SetVirtualRegister(size_t index, VirtualRegister reg)
3829     {
3830         static_assert(sizeof(reg) <= sizeof(uintptr_t), "Consider passing the register by reference");
3831         ASSERT(index < GetInputsCount());
3832         GetDynamicOperands()->GetUser(index)->SetVirtualRegister(reg);
3833     }
3834 
GetVirtualRegister(size_t index)3835     VirtualRegister GetVirtualRegister(size_t index) const
3836     {
3837         ASSERT(index < GetInputsCount());
3838         return GetDynamicOperands()->GetUser(index)->GetVirtualRegister();
3839     }
3840 
Verify()3841     bool Verify() const
3842     {
3843         for (size_t i {0}; i < GetInputsCount(); ++i) {
3844             if (static_cast<uint16_t>(GetVirtualRegister(i)) == VirtualRegister::INVALID) {
3845                 return false;
3846             }
3847         }
3848         return true;
3849     }
3850 
RemoveNumericInputs()3851     bool RemoveNumericInputs()
3852     {
3853         size_t idx = 0;
3854         size_t inputsCount = GetInputsCount();
3855         bool removed = false;
3856         while (idx < inputsCount) {
3857             auto inputInst = GetInput(idx).GetInst();
3858             if (DataType::IsTypeNumeric(inputInst->GetType())) {
3859                 RemoveInput(idx);
3860                 inputsCount--;
3861                 removed = true;
3862             } else {
3863                 idx++;
3864             }
3865         }
3866         return removed;
3867     }
3868 
GetInputType(size_t index)3869     DataType::Type GetInputType(size_t index) const override
3870     {
3871         ASSERT(index < GetInputsCount());
3872         return GetInput(index).GetInst()->GetType();
3873     }
GetMethod()3874     auto GetMethod() const
3875     {
3876         return method_;
3877     }
SetMethod(void * method)3878     auto SetMethod(void *method)
3879     {
3880         method_ = method;
3881     }
3882 
GetCallerInst()3883     auto GetCallerInst() const
3884     {
3885         return callerInst_;
3886     }
SetCallerInst(CallInst * inst)3887     auto SetCallerInst(CallInst *inst)
3888     {
3889         callerInst_ = inst;
3890     }
3891 
GetInliningDepth()3892     uint32_t GetInliningDepth() const override
3893     {
3894         return inliningDepth_;
3895     }
SetInliningDepth(uint32_t inliningDepth)3896     void SetInliningDepth(uint32_t inliningDepth)
3897     {
3898         inliningDepth_ = inliningDepth;
3899     }
3900 
3901     void AppendImmediate(uint64_t imm, uint16_t vreg, DataType::Type type, VRegType vregType);
3902 
GetImmediates()3903     const ArenaVector<SaveStateImm> *GetImmediates() const
3904     {
3905         return immediates_;
3906     }
3907 
GetImmediate(size_t index)3908     const SaveStateImm &GetImmediate(size_t index) const
3909     {
3910         ASSERT(immediates_ != nullptr && index < immediates_->size());
3911         return (*immediates_)[index];
3912     }
3913 
3914     void AllocateImmediates(ArenaAllocator *allocator, size_t size = 0);
3915 
GetImmediatesCount()3916     size_t GetImmediatesCount() const
3917     {
3918         if (immediates_ == nullptr) {
3919             return 0;
3920         }
3921         return immediates_->size();
3922     }
3923 
SetRootsRegMaskBit(size_t reg)3924     void SetRootsRegMaskBit(size_t reg)
3925     {
3926         ASSERT(reg < rootsRegsMask_.size());
3927         rootsRegsMask_.set(reg);
3928     }
3929 
SetRootsStackMaskBit(size_t slot)3930     void SetRootsStackMaskBit(size_t slot)
3931     {
3932         if (rootsStackMask_ != nullptr) {
3933             rootsStackMask_->SetBit(slot);
3934         }
3935     }
3936 
GetRootsStackMask()3937     ArenaBitVector *GetRootsStackMask()
3938     {
3939         return rootsStackMask_;
3940     }
SetRootsStackMask(ArenaBitVector * rootsStackMask)3941     void SetRootsStackMask(ArenaBitVector *rootsStackMask)
3942     {
3943         rootsStackMask_ = rootsStackMask;
3944     }
3945 
GetRootsRegsMask()3946     auto &GetRootsRegsMask()
3947     {
3948         return rootsRegsMask_;
3949     }
3950 
SetRootsRegsMask(uint32_t rootsRegsMask)3951     void SetRootsRegsMask(uint32_t rootsRegsMask)
3952     {
3953         rootsRegsMask_ = rootsRegsMask;
3954     }
3955 
CreateRootsStackMask(ArenaAllocator * allocator)3956     void CreateRootsStackMask(ArenaAllocator *allocator)
3957     {
3958         ASSERT(rootsStackMask_ == nullptr);
3959         rootsStackMask_ = allocator->New<ArenaBitVector>(allocator);
3960         ASSERT(rootsStackMask_ != nullptr);
3961         rootsStackMask_->Reset();
3962     }
3963 
3964     Inst *Clone(const Graph *targetGraph) const override;
3965 
SetInputsWereDeleted()3966     void SetInputsWereDeleted()
3967     {
3968         SetField<FlagInputsWereDeleted>(true);
3969     }
3970 
GetInputsWereDeleted()3971     bool GetInputsWereDeleted() const
3972     {
3973         return GetField<FlagInputsWereDeleted>();
3974     }
3975 
3976     bool GetInputsWereDeletedRec() const;
3977 
InstMayRequireRegMap(const Inst * inst)3978     static bool InstMayRequireRegMap(const Inst *inst)
3979     {
3980         // The call may be inlined later on and have Deoptimize or DeoptimizeIf
3981         // Or the block may become try after inlining
3982         return inst->RequireRegMap() || inst->IsCall() || inst->CanThrow();
3983     }
3984 
3985     template <typename PredT = bool (*)(const Inst *)>
3986     bool CanRemoveInputs(PredT &&mayRequireRegMap = SaveStateInst::InstMayRequireRegMap) const
3987     {
3988         for (auto &user : GetUsers()) {
3989             if (mayRequireRegMap(user.GetInst())) {
3990                 return false;
3991             }
3992         }
3993         return true;
3994     }
3995 
3996 #ifndef NDEBUG
GetInputsWereDeletedSafely()3997     bool GetInputsWereDeletedSafely() const
3998     {
3999         return GetField<FlagInputsWereDeletedSafely>();
4000     }
4001 
SetInputsWereDeletedSafely()4002     void SetInputsWereDeletedSafely()
4003     {
4004         SetField<FlagInputsWereDeletedSafely>(true);
4005     }
4006 #endif
4007 
4008 protected:
4009     using FlagInputsWereDeleted = LastField::NextFlag;
4010 #ifndef NDEBUG
4011     using FlagInputsWereDeletedSafely = FlagInputsWereDeleted::NextFlag;
4012     using LastField = FlagInputsWereDeletedSafely;
4013 #else
4014     using LastField = FlagInputsWereDeleted;
4015 #endif
4016 
4017 private:
4018     ArenaVector<SaveStateImm> *immediates_ {nullptr};
4019     void *method_ {nullptr};
4020     // If instruction is in the inlined graph, this variable points to the inliner's call instruction.
4021     CallInst *callerInst_ {nullptr};
4022     uint32_t inliningDepth_ {0};
4023     ArenaBitVector *rootsStackMask_ {nullptr};
4024     std::bitset<BITS_PER_UINT32> rootsRegsMask_ {0};
4025 };
4026 
4027 /// Load value from array or string
4028 class PANDA_PUBLIC_API LoadInst : public ArrayInstMixin<NeedBarrierMixin<FixedInputsInst2>> {
4029 public:
4030     using Base = ArrayInstMixin<NeedBarrierMixin<FixedInputsInst2>>;
4031     using Base::Base;
4032 
Base(opcode)4033     explicit LoadInst(Opcode opcode, bool isArray = true) : Base(opcode)
4034     {
4035         SetIsArray(isArray);
4036     }
Base(std::move (t))4037     explicit LoadInst(Initializer t, bool needBarrier = false, bool isArray = true) : Base(std::move(t))
4038     {
4039         SetNeedBarrier(needBarrier);
4040         SetIsArray(isArray);
4041     }
4042 
GetIndex()4043     Inst *GetIndex()
4044     {
4045         return GetInput(1).GetInst();
4046     }
GetArray()4047     Inst *GetArray()
4048     {
4049         return GetInput(0).GetInst();
4050     }
4051 
IsBarrier()4052     bool IsBarrier() const override
4053     {
4054         return Inst::IsBarrier() || GetNeedBarrier();
4055     }
4056 
GetInputType(size_t index)4057     DataType::Type GetInputType(size_t index) const override
4058     {
4059         ASSERT(index < GetInputsCount());
4060         switch (index) {
4061             case 0: {
4062                 auto inputType = GetInput(0).GetInst()->GetType();
4063                 ASSERT(inputType == DataType::NO_TYPE || inputType == DataType::REFERENCE ||
4064                        inputType == DataType::ANY);
4065                 return inputType;
4066             }
4067             case 1:
4068                 return DataType::INT32;
4069             default:
4070                 return DataType::NO_TYPE;
4071         }
4072     }
4073 
Clone(const Graph * targetGraph)4074     Inst *Clone(const Graph *targetGraph) const override
4075     {
4076         auto clone = NeedBarrierMixin<FixedInputsInst2>::Clone(targetGraph);
4077         ASSERT(static_cast<LoadInst *>(clone)->IsArray() == IsArray());
4078         return clone;
4079     }
4080 
Latency()4081     uint32_t Latency() const override
4082     {
4083         return g_options.GetCompilerSchedLatencyLong();
4084     }
4085 };
4086 
4087 class LoadCompressedStringCharInst : public FixedInputsInst3 {
4088 public:
4089     using Base = FixedInputsInst3;
4090     using Base::Base;
4091 
GetArray()4092     Inst *GetArray()
4093     {
4094         return GetInput(0).GetInst();
4095     }
GetIndex()4096     Inst *GetIndex()
4097     {
4098         return GetInput(1).GetInst();
4099     }
GetLength()4100     Inst *GetLength() const
4101     {
4102         return GetInput(2U).GetInst();
4103     }
4104 
GetInputType(size_t index)4105     DataType::Type GetInputType(size_t index) const override
4106     {
4107         ASSERT(index < GetInputsCount());
4108         switch (index) {
4109             case 0:
4110                 return DataType::REFERENCE;
4111             case 1:
4112                 return DataType::INT32;
4113             case 2U:
4114                 return DataType::INT32;
4115             default:
4116                 return DataType::NO_TYPE;
4117         }
4118     }
4119 
Latency()4120     uint32_t Latency() const override
4121     {
4122         return g_options.GetCompilerSchedLatencyLong();
4123     }
4124 };
4125 
4126 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4127 class LoadCompressedStringCharInstI : public FixedInputsInst2, public ImmediateMixin {
4128 public:
4129     using Base = FixedInputsInst2;
4130     using Base::Base;
4131 
LoadCompressedStringCharInstI(Initializer t,uint64_t imm)4132     LoadCompressedStringCharInstI(Initializer t, uint64_t imm) : Base(std::move(t)), ImmediateMixin(imm) {}
4133 
Latency()4134     uint32_t Latency() const override
4135     {
4136         return g_options.GetCompilerSchedLatencyLong();
4137     }
4138 
GetInputType(size_t index)4139     DataType::Type GetInputType(size_t index) const override
4140     {
4141         ASSERT(index < GetInputsCount());
4142         switch (index) {
4143             case 0:
4144                 return DataType::REFERENCE;
4145             case 1:
4146                 return DataType::INT32;
4147             default:
4148                 return DataType::NO_TYPE;
4149         }
4150     }
4151 };
4152 /// Store value into array element
4153 class StoreInst : public NeedBarrierMixin<FixedInputsInst3> {
4154 public:
4155     using Base = NeedBarrierMixin<FixedInputsInst3>;
4156     using Base::Base;
4157 
Base(std::move (t))4158     explicit StoreInst(Initializer t, bool needBarrier = false) : Base(std::move(t))
4159     {
4160         SetNeedBarrier(needBarrier);
4161     }
4162 
IsBarrier()4163     bool IsBarrier() const override
4164     {
4165         return Inst::IsBarrier() || GetNeedBarrier();
4166     }
4167 
GetArray()4168     Inst *GetArray()
4169     {
4170         return GetInput(0).GetInst();
4171     }
GetIndex()4172     Inst *GetIndex()
4173     {
4174         return GetInput(1).GetInst();
4175     }
GetStoredValue()4176     Inst *GetStoredValue()
4177     {
4178         return GetInput(2U).GetInst();
4179     }
GetInputType(size_t index)4180     DataType::Type GetInputType(size_t index) const override
4181     {
4182         ASSERT(index < GetInputsCount());
4183         switch (index) {
4184             case 0: {
4185                 auto input = GetInput(0).GetInst();
4186                 auto inputType = input->GetType();
4187                 ASSERT(inputType == DataType::ANY || inputType == DataType::REFERENCE);
4188                 return inputType;
4189             }
4190             case 1:
4191                 return DataType::INT32;
4192             case 2U:
4193                 return GetType();
4194             default:
4195                 return DataType::NO_TYPE;
4196         }
4197     }
4198 
4199     // StoreArray call barriers twice,so we need to save input register for second call
IsPropagateLiveness()4200     PANDA_PUBLIC_API bool IsPropagateLiveness() const override
4201     {
4202         return GetType() == DataType::REFERENCE;
4203     }
4204 };
4205 
4206 /// Load value from array, using array index as immediate
4207 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4208 class PANDA_PUBLIC_API LoadInstI : public VolatileMixin<ArrayInstMixin<NeedBarrierMixin<FixedInputsInst1>>>,
4209                                    public ImmediateMixin {
4210 public:
4211     using Base = VolatileMixin<ArrayInstMixin<NeedBarrierMixin<FixedInputsInst1>>>;
4212     using Base::Base;
4213 
Base(opcode)4214     LoadInstI(Opcode opcode, uint64_t imm, bool isArray = true) : Base(opcode), ImmediateMixin(imm)
4215     {
4216         SetIsArray(isArray);
4217     }
4218 
4219     LoadInstI(Initializer t, uint64_t imm, bool isVolatile = false, bool needBarrier = false, bool isArray = true)
Base(std::move (t))4220         : Base(std::move(t)), ImmediateMixin(imm)
4221     {
4222         SetIsArray(isArray);
4223         SetVolatile(isVolatile);
4224         SetNeedBarrier(needBarrier);
4225     }
4226 
GetArray()4227     Inst *GetArray()
4228     {
4229         return GetInput(0).GetInst();
4230     }
4231 
GetInputType(size_t index)4232     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4233     {
4234         ASSERT(index == 0);
4235         auto inputType = GetInput(0).GetInst()->GetType();
4236         ASSERT(inputType == DataType::ANY || inputType == DataType::REFERENCE);
4237         return inputType;
4238     }
4239 
IsBarrier()4240     bool IsBarrier() const override
4241     {
4242         return Inst::IsBarrier() || GetNeedBarrier();
4243     }
4244 
4245     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
4246 
Clone(const Graph * targetGraph)4247     Inst *Clone(const Graph *targetGraph) const override
4248     {
4249         auto clone = static_cast<LoadInstI *>(FixedInputsInst::Clone(targetGraph));
4250         clone->SetImm(GetImm());
4251         ASSERT(clone->IsArray() == IsArray());
4252         ASSERT(clone->GetVolatile() == GetVolatile());
4253         return clone;
4254     }
4255 
Latency()4256     uint32_t Latency() const override
4257     {
4258         return g_options.GetCompilerSchedLatencyLong();
4259     }
4260 };
4261 
4262 /// Load value from pointer with offset
4263 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4264 class PANDA_PUBLIC_API LoadMemInstI : public VolatileMixin<NeedBarrierMixin<FixedInputsInst1>>, public ImmediateMixin {
4265 public:
4266     using Base = VolatileMixin<NeedBarrierMixin<FixedInputsInst1>>;
4267     using Base::Base;
4268 
LoadMemInstI(Opcode opcode,uint64_t imm)4269     LoadMemInstI(Opcode opcode, uint64_t imm) : Base(opcode), ImmediateMixin(imm) {}
4270     LoadMemInstI(Initializer t, uint64_t imm, bool isVolatile = false, bool needBarrier = false)
Base(std::move (t))4271         : Base(std::move(t)), ImmediateMixin(imm)
4272     {
4273         SetVolatile(isVolatile);
4274         SetNeedBarrier(needBarrier);
4275     }
4276 
GetPointer()4277     Inst *GetPointer()
4278     {
4279         return GetInput(0).GetInst();
4280     }
4281 
GetInputType(size_t index)4282     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4283     {
4284         ASSERT(index == 0);
4285         auto input0Type = GetInput(0).GetInst()->GetType();
4286         ASSERT(input0Type == DataType::POINTER || input0Type == DataType::REFERENCE);
4287         return input0Type;
4288     }
4289 
IsBarrier()4290     bool IsBarrier() const override
4291     {
4292         return Inst::IsBarrier() || GetNeedBarrier();
4293     }
4294 
4295     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
4296 
Clone(const Graph * targetGraph)4297     Inst *Clone(const Graph *targetGraph) const override
4298     {
4299         auto clone = static_cast<LoadMemInstI *>(FixedInputsInst::Clone(targetGraph));
4300         clone->SetImm(GetImm());
4301         ASSERT(clone->GetVolatile() == GetVolatile());
4302         return clone;
4303     }
4304 
Latency()4305     uint32_t Latency() const override
4306     {
4307         return g_options.GetCompilerSchedLatencyLong();
4308     }
4309 };
4310 
4311 /// Store value into array element, using array index as immediate
4312 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4313 class PANDA_PUBLIC_API StoreInstI : public VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>, public ImmediateMixin {
4314 public:
4315     using Base = VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>;
4316     using Base::Base;
4317 
StoreInstI(Opcode opcode,uint64_t imm)4318     StoreInstI(Opcode opcode, uint64_t imm) : Base(opcode), ImmediateMixin(imm) {}
4319 
IsBarrier()4320     bool IsBarrier() const override
4321     {
4322         return Inst::IsBarrier() || GetNeedBarrier();
4323     }
4324 
GetStoredValue()4325     Inst *GetStoredValue()
4326     {
4327         return GetInput(1).GetInst();
4328     }
GetArray()4329     Inst *GetArray()
4330     {
4331         return GetInput(0).GetInst();
4332     }
GetInputType(size_t index)4333     DataType::Type GetInputType(size_t index) const override
4334     {
4335         ASSERT(index < GetInputsCount());
4336         switch (index) {
4337             case 0: {
4338                 auto inputType = GetInput(0).GetInst()->GetType();
4339                 ASSERT(inputType == DataType::ANY || inputType == DataType::REFERENCE);
4340                 return inputType;
4341             }
4342             case 1:
4343                 return GetType();
4344             default:
4345                 UNREACHABLE();
4346         }
4347     }
4348 
Clone(const Graph * targetGraph)4349     Inst *Clone(const Graph *targetGraph) const override
4350     {
4351         auto clone = static_cast<StoreInstI *>(FixedInputsInst::Clone(targetGraph));
4352         clone->SetImm(GetImm());
4353         ASSERT(clone->GetVolatile() == GetVolatile());
4354         return clone;
4355     }
4356 
4357     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
4358 
4359     // StoreArrayI call barriers twice,so we need to save input register for second call
IsPropagateLiveness()4360     bool IsPropagateLiveness() const override
4361     {
4362         return GetType() == DataType::REFERENCE;
4363     }
4364 };
4365 
4366 /// Store value into pointer by offset
4367 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4368 class PANDA_PUBLIC_API StoreMemInstI : public VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>, public ImmediateMixin {
4369 public:
4370     using Base = VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>;
4371     using Base::Base;
4372 
StoreMemInstI(Opcode opcode,uint64_t imm)4373     StoreMemInstI(Opcode opcode, uint64_t imm) : Base(opcode), ImmediateMixin(imm) {}
4374 
IsBarrier()4375     bool IsBarrier() const override
4376     {
4377         return Inst::IsBarrier() || GetNeedBarrier();
4378     }
4379 
GetPointer()4380     Inst *GetPointer()
4381     {
4382         return GetInput(0).GetInst();
4383     }
GetStoredValue()4384     Inst *GetStoredValue()
4385     {
4386         return GetInput(1).GetInst();
4387     }
GetInputType(size_t index)4388     DataType::Type GetInputType(size_t index) const override
4389     {
4390         ASSERT(index < GetInputsCount());
4391         switch (index) {
4392             case 0: {
4393                 auto input0Type = GetInput(0).GetInst()->GetType();
4394                 ASSERT(input0Type == DataType::POINTER || input0Type == DataType::REFERENCE);
4395                 return input0Type;
4396             }
4397             case 1:
4398                 return GetType();
4399             default:
4400                 UNREACHABLE();
4401         }
4402     }
4403 
4404     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
4405 
Clone(const Graph * targetGraph)4406     Inst *Clone(const Graph *targetGraph) const override
4407     {
4408         auto clone = static_cast<StoreMemInstI *>(FixedInputsInst::Clone(targetGraph));
4409         clone->SetImm(GetImm());
4410         ASSERT(clone->GetVolatile() == GetVolatile());
4411         return clone;
4412     }
4413 };
4414 
4415 /// Bounds check, using array index as immediate
4416 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4417 class PANDA_PUBLIC_API BoundsCheckInstI : public ArrayInstMixin<FixedInputsInst<2U>>, public ImmediateMixin {
4418 public:
4419     using Base = ArrayInstMixin<FixedInputsInst<2U>>;
4420     using Base::Base;
4421 
Base(opcode)4422     BoundsCheckInstI(Opcode opcode, uint64_t imm, bool isArray = true) : Base(opcode), ImmediateMixin(imm)
4423     {
4424         SetIsArray(isArray);
4425     }
4426 
Base(std::move (t))4427     BoundsCheckInstI(Initializer t, uint64_t imm, bool isArray = true) : Base(std::move(t)), ImmediateMixin(imm)
4428     {
4429         SetIsArray(isArray);
4430     }
4431 
4432     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
4433 
Clone(const Graph * targetGraph)4434     Inst *Clone(const Graph *targetGraph) const override
4435     {
4436         auto clone = FixedInputsInst::Clone(targetGraph);
4437         clone->CastToBoundsCheckI()->SetImm(GetImm());
4438         ASSERT(clone->CastToBoundsCheckI()->IsArray() == IsArray());
4439         return clone;
4440     }
4441 };
4442 
4443 /// Bounds check instruction
4444 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4445 class BoundsCheckInst : public ArrayInstMixin<FixedInputsInst<3U>> {
4446 public:
4447     using Base = ArrayInstMixin<FixedInputsInst<3U>>;
4448     using Base::Base;
4449 
Base(opcode)4450     explicit BoundsCheckInst(Opcode opcode, bool isArray = true) : Base(opcode)
4451     {
4452         SetIsArray(isArray);
4453     }
4454 
Base(std::move (t))4455     explicit BoundsCheckInst(Initializer t, bool isArray = true) : Base(std::move(t))
4456     {
4457         SetIsArray(isArray);
4458     }
4459 
Clone(const Graph * targetGraph)4460     Inst *Clone(const Graph *targetGraph) const override
4461     {
4462         auto clone = FixedInputsInst::Clone(targetGraph);
4463         ASSERT(clone->CastToBoundsCheck()->IsArray() == IsArray());
4464         return clone;
4465     }
4466 
GetInputType(size_t index)4467     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4468     {
4469         if (index == GetInputsCount() - 1) {
4470             return DataType::NO_TYPE;
4471         }
4472         return GetType();
4473     }
4474 };
4475 
4476 class NullCheckInst : public FixedInputsInst2 {
4477 public:
4478     using Base = FixedInputsInst2;
4479     using Base::Base;
4480 
IsImplicit()4481     bool IsImplicit() const
4482     {
4483         return GetField<IsImplicitFlag>();
4484     }
4485 
4486     void SetImplicit(bool isImplicit = true)
4487     {
4488         SetField<IsImplicitFlag>(isImplicit);
4489     }
4490 
GetInputType(size_t index)4491     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4492     {
4493         if (index == GetInputsCount() - 1) {
4494             return DataType::NO_TYPE;
4495         }
4496         return DataType::REFERENCE;
4497     }
4498 
4499 private:
4500     using IsImplicitFlag = LastField::NextFlag;
4501     using LastField = IsImplicitFlag;
4502 };
4503 
4504 /// Return immediate
4505 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4506 class PANDA_PUBLIC_API ReturnInstI : public FixedInputsInst<0>, public ImmediateMixin {
4507 public:
4508     using FixedInputsInst::FixedInputsInst;
4509 
ReturnInstI(Opcode opcode,uint64_t imm)4510     ReturnInstI(Opcode opcode, uint64_t imm) : FixedInputsInst(opcode), ImmediateMixin(imm) {}
ReturnInstI(Inst::Initializer t,uint64_t imm)4511     ReturnInstI(Inst::Initializer t, uint64_t imm) : FixedInputsInst(std::move(t)), ImmediateMixin(imm) {}
4512 
4513     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
4514 
Clone(const Graph * targetGraph)4515     Inst *Clone(const Graph *targetGraph) const override
4516     {
4517         auto clone = FixedInputsInst::Clone(targetGraph);
4518         clone->CastToReturnI()->SetImm(GetImm());
4519         return clone;
4520     }
4521 };
4522 
4523 class ReturnInlinedInst : public FixedInputsInst<1> {
4524 public:
4525     using FixedInputsInst::FixedInputsInst;
4526 
IsExtendedLiveness()4527     bool IsExtendedLiveness() const
4528     {
4529         return GetField<IsExtendedLivenessFlag>();
4530     }
4531 
4532     void SetExtendedLiveness(bool isExtenedLiveness = true)
4533     {
4534         SetField<IsExtendedLivenessFlag>(isExtenedLiveness);
4535     }
4536 
4537 private:
4538     using IsExtendedLivenessFlag = LastField::NextFlag;
4539     using LastField = IsExtendedLivenessFlag;
4540 };
4541 
4542 /// Monitor instruction
4543 class PANDA_PUBLIC_API MonitorInst : public FixedInputsInst2 {
4544 public:
4545     using Base = FixedInputsInst2;
4546     using Base::Base;
4547 
IsExit()4548     bool IsExit() const
4549     {
4550         return GetField<Exit>();
4551     }
4552 
IsEntry()4553     bool IsEntry() const
4554     {
4555         return !GetField<Exit>();
4556     }
4557 
SetExit()4558     void SetExit()
4559     {
4560         SetField<Exit>(true);
4561     }
4562 
SetEntry()4563     void SetEntry()
4564     {
4565         SetField<Exit>(false);
4566     }
4567 
GetInputType(size_t index)4568     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4569     {
4570         ASSERT(index < GetInputsCount());
4571         if (index == 1) {
4572             return DataType::NO_TYPE;
4573         }
4574         return DataType::REFERENCE;
4575     }
4576 
4577     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
4578 
4579 protected:
4580     using Exit = LastField::NextFlag;
4581     using LastField = Exit;
4582 };
4583 
4584 #include "inst_flags.inl"
4585 #include "intrinsics_flags.inl"
4586 
4587 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4588 class PANDA_PUBLIC_API IntrinsicInst : public InlinedInstMixin<InputTypesMixin<DynamicInputsInst>>,
4589                                        public TypeIdMixin,
4590                                        public MethodDataMixin {
4591 public:
4592     using Base = InlinedInstMixin<InputTypesMixin<DynamicInputsInst>>;
4593     using Base::Base;
4594     using IntrinsicId = RuntimeInterface::IntrinsicId;
4595 
IntrinsicInst(Opcode opcode,IntrinsicId intrinsicId)4596     IntrinsicInst(Opcode opcode, IntrinsicId intrinsicId) : Base(opcode), intrinsicId_(intrinsicId)
4597     {
4598         AdjustFlags(intrinsicId, this);
4599     }
4600 
IntrinsicInst(Initializer t,IntrinsicId intrinsicId)4601     IntrinsicInst(Initializer t, IntrinsicId intrinsicId) : Base(std::move(t)), intrinsicId_(intrinsicId)
4602     {
4603         AdjustFlags(intrinsicId, this);
4604     }
4605 
GetIntrinsicId()4606     IntrinsicId GetIntrinsicId() const
4607     {
4608         return intrinsicId_;
4609     }
4610 
SetIntrinsicId(IntrinsicId intrinsicId)4611     void SetIntrinsicId(IntrinsicId intrinsicId)
4612     {
4613         if (intrinsicId_ != RuntimeInterface::IntrinsicId::INVALID) {
4614             SetField<FieldFlags>(inst_flags::GetFlagsMask(GetOpcode()));
4615         }
4616         intrinsicId_ = intrinsicId;
4617         AdjustFlags(intrinsicId, this);
4618     }
4619 
GetInputType(size_t index)4620     DataType::Type GetInputType(size_t index) const override
4621     {
4622         ASSERT(inputTypes_ != nullptr);
4623         ASSERT(index < inputTypes_->size());
4624         ASSERT(index < GetInputsCount());
4625         return (*inputTypes_)[index];
4626     }
4627 
GetImm(size_t index)4628     uint32_t GetImm(size_t index) const
4629     {
4630         ASSERT(HasImms() && index < GetImms().size());
4631         return GetImms()[index];
4632     }
4633 
SetImm(size_t index,uint32_t imm)4634     void SetImm(size_t index, uint32_t imm)
4635     {
4636         ASSERT(HasImms() && index < GetImms().size());
4637         GetImms()[index] = imm;
4638     }
4639 
GetImms()4640     ArenaVector<uint32_t> &GetImms()
4641     {
4642         return *imms_;
4643     }
4644 
GetImms()4645     const ArenaVector<uint32_t> &GetImms() const
4646     {
4647         return *imms_;
4648     }
4649 
HasImms()4650     bool HasImms() const
4651     {
4652         return imms_ != nullptr;
4653     }
4654 
AddImm(ArenaAllocator * allocator,uint32_t imm)4655     void AddImm(ArenaAllocator *allocator, uint32_t imm)
4656     {
4657         if (imms_ == nullptr) {
4658             imms_ = allocator->New<ArenaVector<uint32_t>>(allocator->Adapter());
4659         }
4660         ASSERT(imms_ != nullptr);
4661         imms_->push_back(imm);
4662     }
4663 
4664     bool IsNativeCall() const;
4665 
HasArgumentsOnStack()4666     bool HasArgumentsOnStack() const
4667     {
4668         return GetField<ArgumentsOnStack>();
4669     }
4670 
SetArgumentsOnStack()4671     void SetArgumentsOnStack()
4672     {
4673         SetField<ArgumentsOnStack>(true);
4674     }
4675 
IsReplaceOnDeoptimize()4676     bool IsReplaceOnDeoptimize() const
4677     {
4678         return GetField<ReplaceOnDeoptimize>();
4679     }
4680 
SetReplaceOnDeoptimize()4681     void SetReplaceOnDeoptimize()
4682     {
4683         SetField<ReplaceOnDeoptimize>(true);
4684     }
4685 
HasIdInput()4686     bool HasIdInput() const
4687     {
4688         return GetField<IdInput>();
4689     }
4690 
SetHasIdInput()4691     void SetHasIdInput()
4692     {
4693         SetField<IdInput>(true);
4694     }
4695 
IsMethodFirstInput()4696     bool IsMethodFirstInput() const
4697     {
4698         return GetField<MethodFirstInput>();
4699     }
4700 
SetMethodFirstInput()4701     void SetMethodFirstInput()
4702     {
4703         SetField<MethodFirstInput>(true);
4704     }
4705 
4706     PANDA_PUBLIC_API Inst *Clone(const Graph *targetGraph) const override;
4707 
CanBeInlined()4708     bool CanBeInlined()
4709     {
4710         return IsInlined();
4711     }
4712 
SetRelocate()4713     void SetRelocate()
4714     {
4715         SetField<Relocate>(true);
4716     }
4717 
GetRelocate()4718     bool GetRelocate() const
4719     {
4720         return GetField<Relocate>();
4721     }
4722 
4723     uint32_t GetTypeId() = delete;  // only method field of TypeIdMixin is used
4724 
4725     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
4726     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
4727 
4728     void DumpImms(std::ostream *out) const;
4729 
4730 protected:
4731     using ArgumentsOnStack = LastField::NextFlag;
4732     using Relocate = ArgumentsOnStack::NextFlag;
4733     using ReplaceOnDeoptimize = Relocate::NextFlag;
4734     using IdInput = ReplaceOnDeoptimize::NextFlag;
4735     using MethodFirstInput = IdInput::NextFlag;
4736     using LastField = MethodFirstInput;
4737 
4738 private:
4739     IntrinsicId intrinsicId_ {RuntimeInterface::IntrinsicId::COUNT};
4740     ArenaVector<uint32_t> *imms_ {nullptr};  // record imms appeared in intrinsics
4741 };
4742 
4743 #include <get_intrinsics_names.inl>
4744 #include <intrinsics_enum.inl>
4745 #include <can_encode_builtin.inl>
4746 
4747 /// Cast instruction
4748 class PANDA_PUBLIC_API CastInst : public InstWithOperandsType<FixedInputsInst1> {
4749 public:
4750     using BaseInst = InstWithOperandsType<FixedInputsInst1>;
4751     using BaseInst::BaseInst;
4752 
CastInst(Initializer t,DataType::Type operType)4753     CastInst(Initializer t, DataType::Type operType) : BaseInst(std::move(t))
4754     {
4755         SetOperandsType(operType);
4756     }
4757 
GetInputType(size_t index)4758     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4759     {
4760         ASSERT(index == 0);
4761         auto type = GetOperandsType();
4762         return type != DataType::NO_TYPE ? type : GetInput(0).GetInst()->GetType();
4763     }
4764 
4765     PANDA_PUBLIC_API void SetVnObject(VnObject *vnObj) override;
4766 
4767     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
4768 
Clone(const Graph * targetGraph)4769     PANDA_PUBLIC_API Inst *Clone(const Graph *targetGraph) const override
4770     {
4771         auto clone = static_cast<CastInst *>(FixedInputsInst::Clone(targetGraph));
4772         ASSERT(clone->GetOperandsType() == GetOperandsType());
4773         return clone;
4774     }
4775 
4776     bool IsDynamicCast() const;
4777 };
4778 
4779 /// Cmp instruction
4780 class PANDA_PUBLIC_API CmpInst : public InstWithOperandsType<FixedInputsInst2> {
4781 public:
4782     using BaseInst = InstWithOperandsType<FixedInputsInst2>;
4783     using BaseInst::BaseInst;
4784 
CmpInst(Initializer t,DataType::Type operType)4785     CmpInst(Initializer t, DataType::Type operType) : BaseInst(std::move(t))
4786     {
4787         SetOperandsType(operType);
4788     }
4789 
IsFcmpg()4790     bool IsFcmpg() const
4791     {
4792         ASSERT(DataType::IsFloatType(GetOperandsType()));
4793         return GetField<Fcmpg>();
4794     }
IsFcmpl()4795     bool IsFcmpl() const
4796     {
4797         ASSERT(DataType::IsFloatType(GetOperandsType()));
4798         return !GetField<Fcmpg>();
4799     }
SetFcmpg()4800     void SetFcmpg()
4801     {
4802         ASSERT(DataType::IsFloatType(GetOperandsType()));
4803         SetField<Fcmpg>(true);
4804     }
SetFcmpg(bool v)4805     void SetFcmpg(bool v)
4806     {
4807         ASSERT(DataType::IsFloatType(GetOperandsType()));
4808         SetField<Fcmpg>(v);
4809     }
SetFcmpl()4810     void SetFcmpl()
4811     {
4812         ASSERT(DataType::IsFloatType(GetOperandsType()));
4813         SetField<Fcmpg>(false);
4814     }
SetFcmpl(bool v)4815     void SetFcmpl(bool v)
4816     {
4817         ASSERT(DataType::IsFloatType(GetOperandsType()));
4818         SetField<Fcmpg>(!v);
4819     }
4820 
GetInputType(size_t index)4821     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4822     {
4823         ASSERT(index < GetInputsCount());
4824         return GetOperandsType();
4825     }
4826 
4827     PANDA_PUBLIC_API void SetVnObject(VnObject *vnObj) override;
4828 
4829     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
4830 
Clone(const Graph * targetGraph)4831     Inst *Clone(const Graph *targetGraph) const override
4832     {
4833         auto clone = FixedInputsInst::Clone(targetGraph);
4834         ASSERT(clone->CastToCmp()->GetOperandsType() == GetOperandsType());
4835         return clone;
4836     }
4837 
4838 protected:
4839     using Fcmpg = LastField::NextFlag;
4840     using LastField = Fcmpg;
4841 };
4842 
4843 /// Load value from instance field
4844 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4845 class PANDA_PUBLIC_API LoadObjectInst : public ObjectTypeMixin<VolatileMixin<NeedBarrierMixin<FixedInputsInst1>>>,
4846                                         public TypeIdMixin,
4847                                         public FieldMixin {
4848 public:
4849     using Base = ObjectTypeMixin<VolatileMixin<NeedBarrierMixin<FixedInputsInst1>>>;
4850     using Base::Base;
4851 
4852     LoadObjectInst(Initializer t, TypeIdMixin m, RuntimeInterface::FieldPtr field, bool isVolatile = false,
4853                    bool needBarrier = false)
Base(std::move (t))4854         : Base(std::move(t)), TypeIdMixin(std::move(m)), FieldMixin(field)
4855     {
4856         SetVolatile(isVolatile);
4857         SetNeedBarrier(needBarrier);
4858     }
4859 
GetInputType(size_t index)4860     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4861     {
4862         ASSERT(index < GetInputsCount());
4863         ASSERT(GetInputsCount() == 1);
4864         auto inputType = GetInput(0).GetInst()->GetType();
4865         ASSERT(inputType == DataType::NO_TYPE || inputType == DataType::REFERENCE || inputType == DataType::ANY);
4866         return inputType;
4867     }
4868 
IsBarrier()4869     bool IsBarrier() const override
4870     {
4871         return Inst::IsBarrier() || GetNeedBarrier() || GetVolatile();
4872     }
4873 
4874     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
4875 
Clone(const Graph * targetGraph)4876     Inst *Clone(const Graph *targetGraph) const override
4877     {
4878         auto clone = FixedInputsInst::Clone(targetGraph);
4879         clone->CastToLoadObject()->SetTypeId(GetTypeId());
4880         clone->CastToLoadObject()->SetMethod(GetMethod());
4881         clone->CastToLoadObject()->SetObjField(GetObjField());
4882         ASSERT(clone->CastToLoadObject()->GetVolatile() == GetVolatile());
4883         ASSERT(clone->CastToLoadObject()->GetObjectType() == GetObjectType());
4884         return clone;
4885     }
4886 
Latency()4887     uint32_t Latency() const override
4888     {
4889         return g_options.GetCompilerSchedLatencyLong();
4890     }
4891 };
4892 
4893 /// Load value from memory by offset
4894 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4895 class PANDA_PUBLIC_API LoadMemInst : public ScaleMixin<VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>> {
4896 public:
4897     using Base = ScaleMixin<VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>>;
4898     using Base::Base;
4899 
Base(std::move (t))4900     explicit LoadMemInst(Initializer t, bool isVolatile = false, bool needBarrier = false) : Base(std::move(t))
4901     {
4902         SetVolatile(isVolatile);
4903         SetNeedBarrier(needBarrier);
4904     }
4905 
GetInputType(size_t index)4906     DataType::Type GetInputType(size_t index) const override
4907     {
4908         ASSERT(index < GetInputsCount());
4909         ASSERT(GetInputsCount() == 2U);
4910         if (index == 1U) {
4911             return DataType::IsInt32Bit(GetInput(1).GetInst()->GetType()) ? DataType::INT32 : DataType::INT64;
4912         }
4913 
4914         ASSERT(index == 0U);
4915         auto input0Type = GetInput(0).GetInst()->GetType();
4916         ASSERT((GetInput(0).GetInst()->IsConst() && input0Type == DataType::INT64) || input0Type == DataType::POINTER ||
4917                input0Type == DataType::REFERENCE);
4918         return input0Type;
4919     }
4920 
IsBarrier()4921     bool IsBarrier() const override
4922     {
4923         return Inst::IsBarrier() || GetNeedBarrier() || GetVolatile();
4924     }
4925 
4926     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
4927     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
4928 
Clone(const Graph * targetGraph)4929     Inst *Clone(const Graph *targetGraph) const override
4930     {
4931         auto clone = FixedInputsInst::Clone(targetGraph);
4932         ASSERT((clone->GetOpcode() == Opcode::Load && clone->CastToLoad()->GetVolatile() == GetVolatile()) ||
4933                (clone->GetOpcode() == Opcode::LoadNative && clone->CastToLoadNative()->GetVolatile() == GetVolatile()));
4934         ASSERT((clone->GetOpcode() == Opcode::Load && clone->CastToLoad()->GetScale() == GetScale()) ||
4935                (clone->GetOpcode() == Opcode::LoadNative && clone->CastToLoadNative()->GetVolatile() == GetVolatile()));
4936         return clone;
4937     }
4938 
Latency()4939     uint32_t Latency() const override
4940     {
4941         return g_options.GetCompilerSchedLatencyLong();
4942     }
4943 };
4944 
4945 /// Load value from dynamic object
4946 class LoadObjectDynamicInst : public DynObjectAccessMixin<FixedInputsInst3> {
4947 public:
4948     using Base = DynObjectAccessMixin<FixedInputsInst3>;
4949     using Base::Base;
4950 
IsBarrier()4951     bool IsBarrier() const override
4952     {
4953         return true;
4954     }
4955 };
4956 
4957 /// Store value to dynamic object
4958 class StoreObjectDynamicInst : public DynObjectAccessMixin<FixedInputsInst<4U>> {
4959 public:
4960     using Base = DynObjectAccessMixin<FixedInputsInst<4U>>;
4961     using Base::Base;
4962 
IsBarrier()4963     bool IsBarrier() const override
4964     {
4965         return true;
4966     }
4967 };
4968 
4969 /// Resolve instance field
4970 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4971 class PANDA_PUBLIC_API ResolveObjectFieldInst : public NeedBarrierMixin<FixedInputsInst1>, public TypeIdMixin {
4972 public:
4973     using Base = NeedBarrierMixin<FixedInputsInst1>;
4974     using Base::Base;
4975 
4976     ResolveObjectFieldInst(Initializer t, TypeIdMixin m, bool needBarrier = false)
Base(std::move (t))4977         : Base(std::move(t)), TypeIdMixin(std::move(m))
4978     {
4979         SetNeedBarrier(needBarrier);
4980     }
4981 
GetInputType(size_t index)4982     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4983     {
4984         ASSERT(GetInputsCount() == 1U);
4985         ASSERT(index == 0);
4986         // This is SaveState input
4987         return DataType::NO_TYPE;
4988     }
4989 
Clone(const Graph * targetGraph)4990     Inst *Clone(const Graph *targetGraph) const override
4991     {
4992         auto clone = FixedInputsInst::Clone(targetGraph);
4993         clone->CastToResolveObjectField()->SetTypeId(GetTypeId());
4994         clone->CastToResolveObjectField()->SetMethod(GetMethod());
4995         return clone;
4996     }
4997 
Latency()4998     uint32_t Latency() const override
4999     {
5000         return g_options.GetCompilerSchedLatencyLong();
5001     }
5002 
5003     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5004 };
5005 
5006 /// Load value from resolved instance field
5007 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5008 class PANDA_PUBLIC_API LoadResolvedObjectFieldInst : public VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>,
5009                                                      public TypeIdMixin {
5010 public:
5011     using Base = VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>;
5012     using Base::Base;
5013 
5014     LoadResolvedObjectFieldInst(Initializer t, TypeIdMixin m, bool isVolatile = false, bool needBarrier = false)
Base(std::move (t))5015         : Base(std::move(t)), TypeIdMixin(std::move(m))
5016     {
5017         SetVolatile(isVolatile);
5018         SetNeedBarrier(needBarrier);
5019     }
5020 
GetInputType(size_t index)5021     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
5022     {
5023         ASSERT(GetInputsCount() == 2U);
5024         ASSERT(index < GetInputsCount());
5025         return index == 0 ? DataType::REFERENCE : DataType::UINT32;
5026     }
5027 
IsBarrier()5028     bool IsBarrier() const override
5029     {
5030         return Inst::IsBarrier() || GetNeedBarrier() || GetVolatile();
5031     }
5032 
Clone(const Graph * targetGraph)5033     Inst *Clone(const Graph *targetGraph) const override
5034     {
5035         auto clone = FixedInputsInst::Clone(targetGraph);
5036         clone->CastToLoadResolvedObjectField()->SetTypeId(GetTypeId());
5037         clone->CastToLoadResolvedObjectField()->SetMethod(GetMethod());
5038         ASSERT(clone->CastToLoadResolvedObjectField()->GetVolatile() == GetVolatile());
5039         return clone;
5040     }
5041 
Latency()5042     uint32_t Latency() const override
5043     {
5044         return g_options.GetCompilerSchedLatencyLong();
5045     }
5046 
5047     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5048 };
5049 
5050 /// Store value into instance field
5051 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5052 class PANDA_PUBLIC_API StoreObjectInst : public ObjectTypeMixin<VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>>,
5053                                          public TypeIdMixin,
5054                                          public FieldMixin {
5055 public:
5056     using Base = ObjectTypeMixin<VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>>;
5057     using Base::Base;
5058     static constexpr size_t STORED_INPUT_INDEX = 1;
5059 
5060     StoreObjectInst(Initializer t, TypeIdMixin m, RuntimeInterface::FieldPtr field, bool isVolatile = false,
5061                     bool needBarrier = false)
Base(std::move (t))5062         : Base(std::move(t)), TypeIdMixin(std::move(m)), FieldMixin(field)
5063     {
5064         SetVolatile(isVolatile);
5065         SetNeedBarrier(needBarrier);
5066     }
5067 
GetInputType(size_t index)5068     DataType::Type GetInputType(size_t index) const override
5069     {
5070         ASSERT(index < GetInputsCount());
5071         ASSERT(GetInputsCount() == 2U);
5072         return index == 0 ? DataType::REFERENCE : GetType();
5073     }
5074 
IsBarrier()5075     bool IsBarrier() const override
5076     {
5077         return Inst::IsBarrier() || GetNeedBarrier() || GetVolatile();
5078     }
5079 
Clone(const Graph * targetGraph)5080     Inst *Clone(const Graph *targetGraph) const override
5081     {
5082         auto clone = FixedInputsInst::Clone(targetGraph);
5083         clone->CastToStoreObject()->SetTypeId(GetTypeId());
5084         clone->CastToStoreObject()->SetMethod(GetMethod());
5085         clone->CastToStoreObject()->SetObjField(GetObjField());
5086         ASSERT(clone->CastToStoreObject()->GetVolatile() == GetVolatile());
5087         ASSERT(clone->CastToStoreObject()->GetObjectType() == GetObjectType());
5088         return clone;
5089     }
5090 
5091     // StoreObject call barriers twice,so we need to save input register for second call
IsPropagateLiveness()5092     bool IsPropagateLiveness() const override
5093     {
5094         return GetType() == DataType::REFERENCE;
5095     }
5096 
5097     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5098 };
5099 
5100 /// Store value into resolved instance field
5101 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5102 class PANDA_PUBLIC_API StoreResolvedObjectFieldInst : public VolatileMixin<NeedBarrierMixin<FixedInputsInst3>>,
5103                                                       public TypeIdMixin {
5104 public:
5105     using Base = VolatileMixin<NeedBarrierMixin<FixedInputsInst3>>;
5106     using Base::Base;
5107     static constexpr size_t STORED_INPUT_INDEX = 1;
5108 
5109     StoreResolvedObjectFieldInst(Initializer t, TypeIdMixin m, bool isVolatile = false, bool needBarrier = false)
Base(std::move (t))5110         : Base(std::move(t)), TypeIdMixin(std::move(m))
5111     {
5112         SetVolatile(isVolatile);
5113         SetNeedBarrier(needBarrier);
5114     }
5115 
IsBarrier()5116     bool IsBarrier() const override
5117     {
5118         return Inst::IsBarrier() || GetVolatile() || GetNeedBarrier();
5119     }
5120 
GetInputType(size_t index)5121     DataType::Type GetInputType(size_t index) const override
5122     {
5123         ASSERT(index < GetInputsCount());
5124         ASSERT(GetInputsCount() == 3U);
5125         if (index == 0) {
5126             return DataType::REFERENCE;  // null-check
5127         }
5128         if (index == 1) {
5129             return GetType();  // stored value
5130         }
5131         return DataType::UINT32;  // field offset
5132     }
5133 
5134     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5135 
Clone(const Graph * targetGraph)5136     Inst *Clone(const Graph *targetGraph) const override
5137     {
5138         auto clone = FixedInputsInst::Clone(targetGraph);
5139         clone->CastToStoreResolvedObjectField()->SetTypeId(GetTypeId());
5140         clone->CastToStoreResolvedObjectField()->SetMethod(GetMethod());
5141         ASSERT(clone->CastToStoreResolvedObjectField()->GetVolatile() == GetVolatile());
5142         return clone;
5143     }
5144 
5145     // StoreResolvedObjectField calls barriers twice,
5146     // so we need to save input register for the second call.
IsPropagateLiveness()5147     bool IsPropagateLiveness() const override
5148     {
5149         return GetType() == DataType::REFERENCE;
5150     }
5151 };
5152 
5153 /// Store value in memory by offset
5154 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5155 class PANDA_PUBLIC_API StoreMemInst : public ScaleMixin<VolatileMixin<NeedBarrierMixin<FixedInputsInst3>>> {
5156 public:
5157     using Base = ScaleMixin<VolatileMixin<NeedBarrierMixin<FixedInputsInst3>>>;
5158     using Base::Base;
5159 
5160     static constexpr size_t STORED_INPUT_INDEX = 2;
5161 
Base(std::move (t))5162     explicit StoreMemInst(Initializer t, bool isVolatile = false, bool needBarrier = false) : Base(std::move(t))
5163     {
5164         SetVolatile(isVolatile);
5165         SetNeedBarrier(needBarrier);
5166     }
5167 
IsBarrier()5168     bool IsBarrier() const override
5169     {
5170         return Inst::IsBarrier() || GetNeedBarrier() || GetVolatile();
5171     }
5172 
GetInputType(size_t index)5173     DataType::Type GetInputType(size_t index) const override
5174     {
5175         ASSERT(index < GetInputsCount());
5176         ASSERT(GetInputsCount() == 3U);
5177         if (index == 1U) {
5178             return DataType::IsInt32Bit(GetInput(1).GetInst()->GetType()) ? DataType::INT32 : DataType::INT64;
5179         }
5180         if (index == 2U) {
5181             return GetType();
5182         }
5183 
5184         ASSERT(index == 0U);
5185         auto input0Type = GetInput(0).GetInst()->GetType();
5186         ASSERT(input0Type == DataType::POINTER || input0Type == DataType::REFERENCE);
5187         return input0Type;
5188     }
5189 
5190     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5191     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
5192 
Clone(const Graph * targetGraph)5193     Inst *Clone(const Graph *targetGraph) const override
5194     {
5195         auto clone = FixedInputsInst::Clone(targetGraph);
5196         ASSERT(
5197             (clone->GetOpcode() == Opcode::Store && clone->CastToStore()->GetVolatile() == GetVolatile()) ||
5198             (clone->GetOpcode() == Opcode::StoreNative && clone->CastToStoreNative()->GetVolatile() == GetVolatile()));
5199         ASSERT(
5200             (clone->GetOpcode() == Opcode::Store && clone->CastToStore()->GetScale() == GetScale()) ||
5201             (clone->GetOpcode() == Opcode::StoreNative && clone->CastToStoreNative()->GetVolatile() == GetVolatile()));
5202         return clone;
5203     }
5204 };
5205 
5206 /// Load static field from class.
5207 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5208 class PANDA_PUBLIC_API LoadStaticInst : public VolatileMixin<NeedBarrierMixin<FixedInputsInst1>>,
5209                                         public TypeIdMixin,
5210                                         public FieldMixin {
5211 public:
5212     using Base = VolatileMixin<NeedBarrierMixin<FixedInputsInst1>>;
5213     using Base::Base;
5214 
5215     LoadStaticInst(Initializer t, TypeIdMixin m, RuntimeInterface::FieldPtr field, bool isVolatile = false,
5216                    bool needBarrier = false)
Base(std::move (t))5217         : Base(std::move(t)), TypeIdMixin(std::move(m)), FieldMixin(field)
5218     {
5219         SetVolatile(isVolatile);
5220         SetNeedBarrier(needBarrier);
5221     }
5222 
IsBarrier()5223     bool IsBarrier() const override
5224     {
5225         return GetNeedBarrier() || GetVolatile() || Inst::IsBarrier();
5226     }
5227 
5228     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5229 
GetInputType(size_t index)5230     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
5231     {
5232         ASSERT(index < GetInputsCount());
5233         ASSERT(index == 0);
5234         return DataType::REFERENCE;
5235     }
5236 
Clone(const Graph * targetGraph)5237     Inst *Clone(const Graph *targetGraph) const override
5238     {
5239         auto clone = FixedInputsInst::Clone(targetGraph);
5240         clone->CastToLoadStatic()->SetTypeId(GetTypeId());
5241         clone->CastToLoadStatic()->SetMethod(GetMethod());
5242         clone->CastToLoadStatic()->SetObjField(GetObjField());
5243         ASSERT(clone->CastToLoadStatic()->GetVolatile() == GetVolatile());
5244         return clone;
5245     }
5246 
Latency()5247     uint32_t Latency() const override
5248     {
5249         return g_options.GetCompilerSchedLatencyLong();
5250     }
5251 };
5252 
5253 /// Resolve static instance field
5254 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5255 class PANDA_PUBLIC_API ResolveObjectFieldStaticInst : public NeedBarrierMixin<FixedInputsInst1>, public TypeIdMixin {
5256 public:
5257     using Base = NeedBarrierMixin<FixedInputsInst1>;
5258     using Base::Base;
5259 
5260     ResolveObjectFieldStaticInst(Initializer t, TypeIdMixin m, bool needBarrier = false)
Base(std::move (t))5261         : Base(std::move(t)), TypeIdMixin(std::move(m))
5262     {
5263         SetNeedBarrier(needBarrier);
5264     }
5265 
Clone(const Graph * targetGraph)5266     Inst *Clone(const Graph *targetGraph) const override
5267     {
5268         auto clone = FixedInputsInst::Clone(targetGraph);
5269         clone->CastToResolveObjectFieldStatic()->SetTypeId(GetTypeId());
5270         clone->CastToResolveObjectFieldStatic()->SetMethod(GetMethod());
5271         return clone;
5272     }
5273 
GetInputType(size_t index)5274     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
5275     {
5276         ASSERT(GetInputsCount() == 1U);
5277         ASSERT(index == 0);
5278         // This is SaveState input
5279         return DataType::NO_TYPE;
5280     }
5281 
Latency()5282     uint32_t Latency() const override
5283     {
5284         return g_options.GetCompilerSchedLatencyLong();
5285     }
5286 
5287     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5288 };
5289 
5290 /// Load value from resolved static instance field
5291 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5292 class PANDA_PUBLIC_API LoadResolvedObjectFieldStaticInst : public VolatileMixin<NeedBarrierMixin<FixedInputsInst1>>,
5293                                                            public TypeIdMixin {
5294 public:
5295     using Base = VolatileMixin<NeedBarrierMixin<FixedInputsInst1>>;
5296     using Base::Base;
5297 
5298     LoadResolvedObjectFieldStaticInst(Initializer t, TypeIdMixin m, bool isVolatile = false, bool needBarrier = false)
Base(std::move (t))5299         : Base(std::move(t)), TypeIdMixin(std::move(m))
5300     {
5301         SetVolatile(isVolatile);
5302         SetNeedBarrier(needBarrier);
5303     }
5304 
GetInputType(size_t index)5305     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
5306     {
5307         ASSERT(GetInputsCount() == 1U);
5308         ASSERT(index < GetInputsCount());
5309         return DataType::REFERENCE;
5310     }
5311 
IsBarrier()5312     bool IsBarrier() const override
5313     {
5314         return Inst::IsBarrier() || GetNeedBarrier() || GetVolatile();
5315     }
5316 
Clone(const Graph * targetGraph)5317     Inst *Clone(const Graph *targetGraph) const override
5318     {
5319         auto clone = FixedInputsInst::Clone(targetGraph);
5320         clone->CastToLoadResolvedObjectFieldStatic()->SetTypeId(GetTypeId());
5321         clone->CastToLoadResolvedObjectFieldStatic()->SetMethod(GetMethod());
5322         ASSERT(clone->CastToLoadResolvedObjectFieldStatic()->GetVolatile() == GetVolatile());
5323         return clone;
5324     }
5325 
Latency()5326     uint32_t Latency() const override
5327     {
5328         return g_options.GetCompilerSchedLatencyLong();
5329     }
5330 
5331     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5332 };
5333 
5334 /// Store value into static field.
5335 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5336 class PANDA_PUBLIC_API StoreStaticInst : public VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>,
5337                                          public TypeIdMixin,
5338                                          public FieldMixin {
5339 public:
5340     using Base = VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>;
5341     using Base::Base;
5342     static constexpr size_t STORED_INPUT_INDEX = 1;
5343 
5344     StoreStaticInst(Initializer t, TypeIdMixin m, RuntimeInterface::FieldPtr field, bool isVolatile = false,
5345                     bool needBarrier = false)
Base(std::move (t))5346         : Base(std::move(t)), TypeIdMixin(std::move(m)), FieldMixin(field)
5347     {
5348         SetNeedBarrier(needBarrier);
5349         SetVolatile(isVolatile);
5350     }
5351 
IsBarrier()5352     bool IsBarrier() const override
5353     {
5354         return Inst::IsBarrier() || GetNeedBarrier() || GetVolatile();
5355     }
5356 
5357     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5358 
GetInputType(size_t index)5359     DataType::Type GetInputType(size_t index) const override
5360     {
5361         ASSERT(index < GetInputsCount());
5362         if (index == 0) {
5363             return DataType::REFERENCE;
5364         }
5365         return GetType();
5366     }
5367 
Clone(const Graph * targetGraph)5368     Inst *Clone(const Graph *targetGraph) const override
5369     {
5370         auto clone = FixedInputsInst::Clone(targetGraph);
5371         clone->CastToStoreStatic()->SetTypeId(GetTypeId());
5372         clone->CastToStoreStatic()->SetMethod(GetMethod());
5373         clone->CastToStoreStatic()->SetObjField(GetObjField());
5374         ASSERT(clone->CastToStoreStatic()->GetVolatile() == GetVolatile());
5375         return clone;
5376     }
5377 
5378     // StoreStatic call barriers twice,so we need to save input register for second call
IsPropagateLiveness()5379     bool IsPropagateLiveness() const override
5380     {
5381         return GetType() == DataType::REFERENCE;
5382     }
5383 };
5384 
5385 /// Store value into unresolved static field.
5386 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5387 class PANDA_PUBLIC_API UnresolvedStoreStaticInst : public NeedBarrierMixin<FixedInputsInst2>, public TypeIdMixin {
5388 public:
5389     using Base = NeedBarrierMixin<FixedInputsInst2>;
5390     using Base::Base;
5391     static constexpr size_t STORED_INPUT_INDEX = 0U;
5392 
5393     UnresolvedStoreStaticInst(Initializer t, TypeIdMixin m, bool needBarrier = false)
Base(std::move (t))5394         : Base(std::move(t)), TypeIdMixin(std::move(m))
5395     {
5396         SetNeedBarrier(needBarrier);
5397     }
5398 
IsBarrier()5399     bool IsBarrier() const override
5400     {
5401         return true;
5402     }
5403 
5404     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5405 
GetInputType(size_t index)5406     DataType::Type GetInputType(size_t index) const override
5407     {
5408         ASSERT(index < GetInputsCount());
5409         if (index == 1) {
5410             // This is SaveState input
5411             return DataType::NO_TYPE;
5412         }
5413         ASSERT(index == 0);
5414         return GetType();
5415     }
5416 
Clone(const Graph * targetGraph)5417     Inst *Clone(const Graph *targetGraph) const override
5418     {
5419         auto clone = FixedInputsInst::Clone(targetGraph);
5420         clone->CastToUnresolvedStoreStatic()->SetTypeId(GetTypeId());
5421         clone->CastToUnresolvedStoreStatic()->SetMethod(GetMethod());
5422         return clone;
5423     }
5424 };
5425 
5426 /// Store value into resolved static field.
5427 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5428 class PANDA_PUBLIC_API StoreResolvedObjectFieldStaticInst : public NeedBarrierMixin<FixedInputsInst2>,
5429                                                             public TypeIdMixin {
5430 public:
5431     using Base = NeedBarrierMixin<FixedInputsInst2>;
5432     using Base::Base;
5433     static constexpr size_t STORED_INPUT_INDEX = 1U;
5434 
5435     StoreResolvedObjectFieldStaticInst(Initializer t, TypeIdMixin m, bool needBarrier = false)
Base(std::move (t))5436         : Base(std::move(t)), TypeIdMixin(std::move(m))
5437     {
5438         SetNeedBarrier(needBarrier);
5439     }
5440 
IsBarrier()5441     bool IsBarrier() const override
5442     {
5443         return true;
5444     }
5445 
GetInputType(size_t index)5446     DataType::Type GetInputType(size_t index) const override
5447     {
5448         ASSERT(GetInputsCount() == 2U);
5449         ASSERT(index < GetInputsCount());
5450         if (index == 0) {
5451             return DataType::REFERENCE;
5452         }
5453         return GetType();  // stored value
5454     }
5455 
Clone(const Graph * targetGraph)5456     Inst *Clone(const Graph *targetGraph) const override
5457     {
5458         auto clone = FixedInputsInst::Clone(targetGraph);
5459         clone->CastToStoreResolvedObjectFieldStatic()->SetTypeId(GetTypeId());
5460         clone->CastToStoreResolvedObjectFieldStatic()->SetMethod(GetMethod());
5461         return clone;
5462     }
5463 
5464     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5465 };
5466 
5467 /// Create new object
5468 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5469 class PANDA_PUBLIC_API NewObjectInst : public NeedBarrierMixin<FixedInputsInst2>, public TypeIdMixin {
5470 public:
5471     using Base = NeedBarrierMixin<FixedInputsInst2>;
5472     using Base::Base;
5473 
5474     NewObjectInst(Initializer t, TypeIdMixin m, bool needBarrier = false)
Base(std::move (t))5475         : Base(std::move(t)), TypeIdMixin(std::move(m))
5476     {
5477         SetNeedBarrier(needBarrier);
5478     }
5479 
IsBarrier()5480     bool IsBarrier() const override
5481     {
5482         return Inst::IsBarrier() || GetNeedBarrier();
5483     }
GetInputType(size_t index)5484     DataType::Type GetInputType(size_t index) const override
5485     {
5486         ASSERT(index < GetInputsCount());
5487         if (index == 0) {
5488             return DataType::REFERENCE;
5489         }
5490         return DataType::NO_TYPE;
5491     }
5492 
5493     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5494 
Clone(const Graph * targetGraph)5495     Inst *Clone(const Graph *targetGraph) const override
5496     {
5497         auto clone = FixedInputsInst::Clone(targetGraph);
5498         clone->CastToNewObject()->SetTypeId(GetTypeId());
5499         clone->CastToNewObject()->SetMethod(GetMethod());
5500         return clone;
5501     }
5502 };
5503 
5504 /// Create new array
5505 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5506 class PANDA_PUBLIC_API NewArrayInst : public NeedBarrierMixin<FixedInputsInst3>, public TypeIdMixin {
5507 public:
5508     using Base = NeedBarrierMixin<FixedInputsInst3>;
5509     using Base::Base;
5510 
5511     static constexpr size_t INDEX_CLASS = 0;
5512     static constexpr size_t INDEX_SIZE = 1;
5513     static constexpr size_t INDEX_SAVE_STATE = 2;
5514 
Base(std::move (t))5515     NewArrayInst(Initializer t, TypeIdMixin m, bool needBarrier = false) : Base(std::move(t)), TypeIdMixin(std::move(m))
5516     {
5517         SetNeedBarrier(needBarrier);
5518     }
5519 
GetInputType(size_t index)5520     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
5521     {
5522         ASSERT(index < GetInputsCount());
5523         switch (index) {
5524             case INDEX_CLASS:
5525                 return GetInput(0).GetInst()->GetType();
5526             case INDEX_SIZE:
5527                 return DataType::INT32;
5528             case INDEX_SAVE_STATE:
5529                 // This is SaveState input
5530                 return DataType::NO_TYPE;
5531             default:
5532                 UNREACHABLE();
5533         }
5534     }
5535 
IsBarrier()5536     bool IsBarrier() const override
5537     {
5538         return Inst::IsBarrier() || GetNeedBarrier();
5539     }
5540 
5541     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5542 
Clone(const Graph * targetGraph)5543     Inst *Clone(const Graph *targetGraph) const override
5544     {
5545         auto clone = FixedInputsInst::Clone(targetGraph);
5546         clone->CastToNewArray()->SetTypeId(GetTypeId());
5547         clone->CastToNewArray()->SetMethod(GetMethod());
5548         return clone;
5549     }
5550 };
5551 
5552 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5553 class PANDA_PUBLIC_API LoadConstArrayInst : public NeedBarrierMixin<FixedInputsInst1>, public TypeIdMixin {
5554 public:
5555     using Base = NeedBarrierMixin<FixedInputsInst1>;
5556     using Base::Base;
5557 
5558     LoadConstArrayInst(Initializer t, TypeIdMixin m, bool needBarrier = false)
Base(std::move (t))5559         : Base(std::move(t)), TypeIdMixin(std::move(m))
5560     {
5561         SetNeedBarrier(needBarrier);
5562     }
5563 
IsBarrier()5564     bool IsBarrier() const override
5565     {
5566         return Inst::IsBarrier() || GetNeedBarrier();
5567     }
5568 
GetInputType(size_t index)5569     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
5570     {
5571         ASSERT(index < GetInputsCount());
5572         return DataType::NO_TYPE;
5573     }
5574 
5575     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5576 
Clone(const Graph * targetGraph)5577     Inst *Clone(const Graph *targetGraph) const override
5578     {
5579         auto clone = FixedInputsInst::Clone(targetGraph);
5580         clone->CastToLoadConstArray()->SetTypeId(GetTypeId());
5581         clone->CastToLoadConstArray()->SetMethod(GetMethod());
5582         return clone;
5583     }
5584 };
5585 
5586 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5587 class PANDA_PUBLIC_API FillConstArrayInst : public NeedBarrierMixin<FixedInputsInst2>,
5588                                             public TypeIdMixin,
5589                                             public ImmediateMixin {
5590 public:
5591     using Base = NeedBarrierMixin<FixedInputsInst2>;
5592     using Base::Base;
5593 
5594     FillConstArrayInst(Initializer t, TypeIdMixin m, uint64_t imm, bool needBarrier = false)
Base(std::move (t))5595         : Base(std::move(t)), TypeIdMixin(std::move(m)), ImmediateMixin(imm)
5596     {
5597         SetNeedBarrier(needBarrier);
5598     }
5599 
IsBarrier()5600     bool IsBarrier() const override
5601     {
5602         return GetNeedBarrier() || Inst::IsBarrier();
5603     }
5604 
GetInputType(size_t index)5605     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
5606     {
5607         ASSERT(index < GetInputsCount());
5608         return index == 0 ? DataType::REFERENCE : DataType::NO_TYPE;
5609     }
5610 
5611     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5612 
Clone(const Graph * targetGraph)5613     Inst *Clone(const Graph *targetGraph) const override
5614     {
5615         auto clone = FixedInputsInst::Clone(targetGraph);
5616         clone->CastToFillConstArray()->SetTypeId(GetTypeId());
5617         clone->CastToFillConstArray()->SetMethod(GetMethod());
5618         clone->CastToFillConstArray()->SetImm(GetImm());
5619         return clone;
5620     }
5621 };
5622 
5623 /// Checkcast
5624 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5625 class PANDA_PUBLIC_API CheckCastInst : public OmitNullCheckMixin<ClassTypeMixin<NeedBarrierMixin<FixedInputsInst3>>>,
5626                                        public TypeIdMixin {
5627 public:
5628     using Base = OmitNullCheckMixin<ClassTypeMixin<NeedBarrierMixin<FixedInputsInst3>>>;
5629     using Base::Base;
5630 
5631     CheckCastInst(Initializer t, TypeIdMixin m, ClassType classType, bool needBarrier = false)
Base(std::move (t))5632         : Base(std::move(t)), TypeIdMixin(std::move(m))
5633     {
5634         SetClassType(classType);
5635         SetNeedBarrier(needBarrier);
5636     }
5637 
IsBarrier()5638     bool IsBarrier() const override
5639     {
5640         return Inst::IsBarrier() || GetNeedBarrier();
5641     }
5642 
GetInputType(size_t index)5643     DataType::Type GetInputType(size_t index) const override
5644     {
5645         ASSERT(index < GetInputsCount());
5646         ASSERT(GetInputsCount() == 3U);
5647         if (index < 2U) {
5648             return DataType::REFERENCE;
5649         }
5650         return DataType::NO_TYPE;
5651     }
5652 
5653     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5654 
Clone(const Graph * targetGraph)5655     Inst *Clone(const Graph *targetGraph) const override
5656     {
5657         auto clone = FixedInputsInst::Clone(targetGraph);
5658         clone->CastToCheckCast()->SetTypeId(GetTypeId());
5659         clone->CastToCheckCast()->SetMethod(GetMethod());
5660         ASSERT(clone->CastToCheckCast()->GetClassType() == GetClassType());
5661         ASSERT(clone->CastToCheckCast()->GetOmitNullCheck() == GetOmitNullCheck());
5662         return clone;
5663     }
5664 };
5665 
5666 /// Is instance
5667 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5668 class PANDA_PUBLIC_API IsInstanceInst : public OmitNullCheckMixin<ClassTypeMixin<NeedBarrierMixin<FixedInputsInst3>>>,
5669                                         public TypeIdMixin {
5670 public:
5671     using Base = OmitNullCheckMixin<ClassTypeMixin<NeedBarrierMixin<FixedInputsInst3>>>;
5672     using Base::Base;
5673 
5674     IsInstanceInst(Initializer t, TypeIdMixin m, ClassType classType, bool needBarrier = false)
Base(std::move (t))5675         : Base(std::move(t)), TypeIdMixin(std::move(m))
5676     {
5677         SetClassType(classType);
5678         SetNeedBarrier(needBarrier);
5679     }
5680 
IsBarrier()5681     bool IsBarrier() const override
5682     {
5683         return Inst::IsBarrier() || GetNeedBarrier();
5684     }
5685 
GetInputType(size_t index)5686     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
5687     {
5688         ASSERT(index < GetInputsCount());
5689         ASSERT(GetInputsCount() == 3U);
5690         if (index < 2U) {
5691             return DataType::REFERENCE;
5692         }
5693         return DataType::NO_TYPE;
5694     }
5695 
Clone(const Graph * targetGraph)5696     Inst *Clone(const Graph *targetGraph) const override
5697     {
5698         auto clone = FixedInputsInst::Clone(targetGraph);
5699         clone->CastToIsInstance()->SetTypeId(GetTypeId());
5700         clone->CastToIsInstance()->SetMethod(GetMethod());
5701         ASSERT(clone->CastToIsInstance()->GetClassType() == GetClassType());
5702         ASSERT(clone->CastToIsInstance()->GetOmitNullCheck() == GetOmitNullCheck());
5703         return clone;
5704     }
5705 
5706     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5707 };
5708 
5709 /// Load data from constant pool.
5710 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5711 class PANDA_PUBLIC_API LoadFromPool : public NeedBarrierMixin<FixedInputsInst1>, public TypeIdMixin {
5712 public:
5713     using Base = NeedBarrierMixin<FixedInputsInst1>;
5714     using Base::Base;
5715 
Base(std::move (t))5716     LoadFromPool(Initializer t, TypeIdMixin m, bool needBarrier = false) : Base(std::move(t)), TypeIdMixin(std::move(m))
5717     {
5718         SetNeedBarrier(needBarrier);
5719     }
5720 
GetInputType(size_t index)5721     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
5722     {
5723         ASSERT(index < GetInputsCount());
5724         return DataType::NO_TYPE;
5725     }
5726 
IsBarrier()5727     bool IsBarrier() const override
5728     {
5729         return GetNeedBarrier() || Inst::IsBarrier();
5730     }
5731 
5732     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5733 
Clone(const Graph * targetGraph)5734     Inst *Clone(const Graph *targetGraph) const override
5735     {
5736         auto clone = FixedInputsInst::Clone(targetGraph);
5737         static_cast<LoadFromPool *>(clone)->SetTypeId(GetTypeId());
5738         static_cast<LoadFromPool *>(clone)->SetMethod(GetMethod());
5739         return clone;
5740     }
5741 };
5742 
5743 /// Load data from dynamic constant pool.
5744 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5745 class PANDA_PUBLIC_API LoadFromPoolDynamic : public NeedBarrierMixin<FixedInputsInst1>, public TypeIdMixin {
5746 public:
5747     using Base = NeedBarrierMixin<FixedInputsInst1>;
5748     using Base::Base;
5749 
5750     LoadFromPoolDynamic(Initializer t, TypeIdMixin m, bool needBarrier = false)
Base(std::move (t))5751         : Base(std::move(t)), TypeIdMixin(std::move(m))
5752     {
5753         SetNeedBarrier(needBarrier);
5754     }
5755 
GetInputType(size_t index)5756     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
5757     {
5758         ASSERT(index < GetInputsCount());
5759         return DataType::ANY;
5760     }
5761 
5762     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5763 
Clone(const Graph * targetGraph)5764     Inst *Clone(const Graph *targetGraph) const override
5765     {
5766         auto clone = FixedInputsInst::Clone(targetGraph);
5767         static_cast<LoadFromPoolDynamic *>(clone)->SetTypeId(GetTypeId());
5768         static_cast<LoadFromPoolDynamic *>(clone)->SetMethod(GetMethod());
5769         return clone;
5770     }
5771 
IsString()5772     bool IsString() const
5773     {
5774         return GetField<StringFlag>();
5775     }
5776 
SetString(bool v)5777     void SetString(bool v)
5778     {
5779         SetField<StringFlag>(v);
5780     }
5781 
5782     PANDA_PUBLIC_API void SetVnObject(VnObject *vnObj) override;
5783 
5784 protected:
5785     using StringFlag = LastField::NextFlag;
5786     using LastField = StringFlag;
5787 };
5788 
5789 /// Initialization or loading of the class.
5790 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5791 class PANDA_PUBLIC_API ClassInst : public NeedBarrierMixin<FixedInputsInst1>, public TypeIdMixin {
5792 public:
5793     using Base = NeedBarrierMixin<FixedInputsInst1>;
5794     using Base::Base;
5795 
5796     ClassInst(Initializer t, TypeIdMixin m, RuntimeInterface::ClassPtr klass, bool needBarrier = false)
Base(std::move (t))5797         : Base(std::move(t)), TypeIdMixin(std::move(m)), klass_(klass)
5798     {
5799         SetNeedBarrier(needBarrier);
5800     }
5801 
IsBarrier()5802     bool IsBarrier() const override
5803     {
5804         return Inst::IsBarrier() || GetNeedBarrier();
5805     }
5806 
5807     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5808 
Clone(const Graph * targetGraph)5809     Inst *Clone(const Graph *targetGraph) const override
5810     {
5811         auto clone = FixedInputsInst::Clone(targetGraph);
5812         static_cast<ClassInst *>(clone)->SetTypeId(GetTypeId());
5813         static_cast<ClassInst *>(clone)->SetMethod(GetMethod());
5814         static_cast<ClassInst *>(clone)->SetClass(GetClass());
5815         return clone;
5816     }
5817 
GetClass()5818     RuntimeInterface::ClassPtr GetClass() const
5819     {
5820         return klass_;
5821     }
5822 
SetClass(RuntimeInterface::ClassPtr klass)5823     void SetClass(RuntimeInterface::ClassPtr klass)
5824     {
5825         klass_ = klass;
5826     }
5827 
GetInputType(size_t index)5828     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
5829     {
5830         return DataType::NO_TYPE;
5831     }
5832 
5833 private:
5834     RuntimeInterface::ClassPtr klass_ {nullptr};
5835 };
5836 
5837 /// Loading of the runtime class.
5838 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5839 class PANDA_PUBLIC_API RuntimeClassInst : public NeedBarrierMixin<FixedInputsInst0>, public TypeIdMixin {
5840 public:
5841     using Base = NeedBarrierMixin<FixedInputsInst0>;
5842     using Base::Base;
5843 
5844     RuntimeClassInst(Inst::Initializer t, TypeIdMixin m, RuntimeInterface::ClassPtr klass, bool needBarrier = false)
Base(std::move (t))5845         : Base(std::move(t)), TypeIdMixin(std::move(m)), klass_(klass)
5846     {
5847         SetNeedBarrier(needBarrier);
5848     }
5849 
IsBarrier()5850     bool IsBarrier() const override
5851     {
5852         return GetNeedBarrier() || Inst::IsBarrier();
5853     }
5854 
5855     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5856 
Clone(const Graph * targetGraph)5857     Inst *Clone(const Graph *targetGraph) const override
5858     {
5859         auto clone = FixedInputsInst::Clone(targetGraph);
5860         static_cast<RuntimeClassInst *>(clone)->SetTypeId(GetTypeId());
5861         static_cast<RuntimeClassInst *>(clone)->SetMethod(GetMethod());
5862         static_cast<RuntimeClassInst *>(clone)->SetClass(GetClass());
5863         return clone;
5864     }
5865 
GetClass()5866     RuntimeInterface::ClassPtr GetClass() const
5867     {
5868         return klass_;
5869     }
5870 
5871     PANDA_PUBLIC_API void SetVnObject(VnObject *vnObj) override;
5872 
SetClass(RuntimeInterface::ClassPtr klass)5873     void SetClass(RuntimeInterface::ClassPtr klass)
5874     {
5875         klass_ = klass;
5876     }
5877 
5878 private:
5879     RuntimeInterface::ClassPtr klass_ {nullptr};
5880 };
5881 
5882 class PANDA_PUBLIC_API WrapObjectNativeInst : public FixedInputsInst1 {
5883 public:
5884     using Base = FixedInputsInst1;
5885     using Base::Base;
5886 
WrapObjectNativeInst(Inst::Initializer t)5887     explicit WrapObjectNativeInst(Inst::Initializer t) : Base(std::move(t)) {}
5888 
GetInputType(size_t index)5889     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
5890     {
5891         ASSERT(index < GetInputsCount());
5892         return DataType::REFERENCE;
5893     }
5894 
Clone(const Graph * targetGraph)5895     Inst *Clone(const Graph *targetGraph) const override
5896     {
5897         return FixedInputsInst::Clone(targetGraph);
5898     }
5899 };
5900 
5901 /// Get global var address inst
5902 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5903 class PANDA_PUBLIC_API GlobalVarInst : public NeedBarrierMixin<FixedInputsInst2>, public TypeIdMixin {
5904 public:
5905     using Base = NeedBarrierMixin<FixedInputsInst2>;
5906     using Base::Base;
5907 
GlobalVarInst(Initializer t,uintptr_t address)5908     GlobalVarInst(Initializer t, uintptr_t address) : Base(std::move(t)), address_(address) {}
5909 
5910     GlobalVarInst(Initializer t, TypeIdMixin m, uintptr_t address, bool needBarrier = false)
Base(std::move (t))5911         : Base(std::move(t)), TypeIdMixin(std::move(m)), address_(address)
5912     {
5913         SetNeedBarrier(needBarrier);
5914     }
5915 
IsBarrier()5916     bool IsBarrier() const override
5917     {
5918         return Inst::IsBarrier() || GetNeedBarrier();
5919     }
5920 
Clone(const Graph * targetGraph)5921     Inst *Clone(const Graph *targetGraph) const override
5922     {
5923         auto clone = FixedInputsInst::Clone(targetGraph);
5924         static_cast<GlobalVarInst *>(clone)->SetTypeId(GetTypeId());
5925         static_cast<GlobalVarInst *>(clone)->SetMethod(GetMethod());
5926         return clone;
5927     }
5928 
5929     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
5930 
GetInputType(size_t index)5931     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
5932     {
5933         ASSERT(index < GetInputsCount());
5934         if (index == 0) {
5935             return DataType::ANY;
5936         }
5937         return DataType::NO_TYPE;
5938     }
5939 
GetAddress()5940     uintptr_t GetAddress() const
5941     {
5942         return address_;
5943     }
5944 
5945 private:
5946     uintptr_t address_ {0};
5947 };
5948 
5949 /// Get object pointer from the specific source.
5950 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5951 class PANDA_PUBLIC_API LoadImmediateInst : public FixedInputsInst<0> {
5952 public:
5953     using Base = FixedInputsInst;
5954     using Base::Base;
5955 
5956     enum class ObjectType {
5957         UNKNOWN,
5958         CLASS,
5959         METHOD,
5960         CONSTANT_POOL,
5961         STRING,
5962         PANDA_FILE_OFFSET,
5963         OBJECT,
5964         TLS_OFFSET,
5965         LAST
5966     };
5967 
5968     LoadImmediateInst(Inst::Initializer t, const void *obj, ObjectType objType = ObjectType::CLASS)
Base(std::move (t))5969         : Base(std::move(t)), obj_(reinterpret_cast<uint64_t>(obj))
5970     {
5971         SetObjectType(objType);
5972     }
5973 
5974     LoadImmediateInst(Inst::Initializer t, uint64_t obj, ObjectType objType = ObjectType::CLASS)
Base(std::move (t))5975         : Base(std::move(t)), obj_(obj)
5976     {
5977         SetObjectType(objType);
5978     }
5979 
Clone(const Graph * targetGraph)5980     Inst *Clone(const Graph *targetGraph) const override
5981     {
5982         auto clone = FixedInputsInst::Clone(targetGraph);
5983         clone->CastToLoadImmediate()->SetObjectType(GetObjectType());
5984         clone->CastToLoadImmediate()->obj_ = obj_;
5985         return clone;
5986     }
5987 
GetObject()5988     void *GetObject() const
5989     {
5990         return reinterpret_cast<void *>(obj_);
5991     }
5992 
GetObjectType()5993     ObjectType GetObjectType() const
5994     {
5995         return GetField<ObjectTypeField>();
5996     }
5997 
SetObjectType(ObjectType objType)5998     void SetObjectType(ObjectType objType)
5999     {
6000         SetField<ObjectTypeField>(objType);
6001     }
6002 
SetMethod(RuntimeInterface::MethodPtr obj)6003     void SetMethod(RuntimeInterface::MethodPtr obj)
6004     {
6005         ASSERT(GetObjectType() == ObjectType::METHOD);
6006         obj_ = reinterpret_cast<uint64_t>(obj);
6007     }
6008 
GetMethod()6009     RuntimeInterface::MethodPtr GetMethod() const
6010     {
6011         ASSERT(GetObjectType() == ObjectType::METHOD);
6012         return reinterpret_cast<RuntimeInterface::MethodPtr>(obj_);
6013     }
6014 
IsMethod()6015     bool IsMethod() const
6016     {
6017         return GetField<ObjectTypeField>() == ObjectType::METHOD;
6018     }
6019 
SetClass(RuntimeInterface::ClassPtr obj)6020     void SetClass(RuntimeInterface::ClassPtr obj)
6021     {
6022         ASSERT(GetObjectType() == ObjectType::CLASS);
6023         obj_ = reinterpret_cast<uint64_t>(obj);
6024     }
6025 
GetClass()6026     RuntimeInterface::ClassPtr GetClass() const
6027     {
6028         ASSERT(GetObjectType() == ObjectType::CLASS);
6029         return reinterpret_cast<RuntimeInterface::ClassPtr>(obj_);
6030     }
6031 
IsConstantPool()6032     bool IsConstantPool() const
6033     {
6034         return GetField<ObjectTypeField>() == ObjectType::CONSTANT_POOL;
6035     }
6036 
GetConstantPool()6037     uintptr_t GetConstantPool() const
6038     {
6039         ASSERT(GetObjectType() == ObjectType::CONSTANT_POOL);
6040         return static_cast<uintptr_t>(obj_);
6041     }
6042 
IsClass()6043     bool IsClass() const
6044     {
6045         return GetField<ObjectTypeField>() == ObjectType::CLASS;
6046     }
6047 
IsString()6048     bool IsString() const
6049     {
6050         return GetField<ObjectTypeField>() == ObjectType::STRING;
6051     }
6052 
GetString()6053     uintptr_t GetString() const
6054     {
6055         ASSERT(GetObjectType() == ObjectType::STRING);
6056         return static_cast<uintptr_t>(obj_);
6057     }
6058 
IsPandaFileOffset()6059     bool IsPandaFileOffset() const
6060     {
6061         return GetField<ObjectTypeField>() == ObjectType::PANDA_FILE_OFFSET;
6062     }
6063 
GetPandaFileOffset()6064     uint64_t GetPandaFileOffset() const
6065     {
6066         ASSERT(GetObjectType() == ObjectType::PANDA_FILE_OFFSET);
6067         return obj_;
6068     }
6069 
IsObject()6070     bool IsObject() const
6071     {
6072         return GetField<ObjectTypeField>() == ObjectType::OBJECT;
6073     }
6074 
IsTlsOffset()6075     bool IsTlsOffset() const
6076     {
6077         return GetField<ObjectTypeField>() == ObjectType::TLS_OFFSET;
6078     }
6079 
GetTlsOffset()6080     uint64_t GetTlsOffset() const
6081     {
6082         ASSERT(GetObjectType() == ObjectType::TLS_OFFSET);
6083         return obj_;
6084     }
6085 
6086     PANDA_PUBLIC_API void SetVnObject(VnObject *vnObj) override;
6087 
6088     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
6089 
6090 private:
6091     uint64_t obj_ {0};
6092     using ObjectTypeField = Base::LastField::NextField<ObjectType, MinimumBitsToStore(ObjectType::LAST)>;
6093     using LastField = ObjectTypeField;
6094 };
6095 
6096 /// Get function from the specific source.
6097 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
6098 class PANDA_PUBLIC_API FunctionImmediateInst : public FixedInputsInst<0> {
6099 public:
6100     using Base = FixedInputsInst;
6101     using Base::Base;
6102 
FunctionImmediateInst(Inst::Initializer t,uintptr_t ptr)6103     FunctionImmediateInst(Inst::Initializer t, uintptr_t ptr) : Base(std::move(t)), functionPtr_(ptr) {}
6104 
Clone(const Graph * targetGraph)6105     Inst *Clone(const Graph *targetGraph) const override
6106     {
6107         auto clone = FixedInputsInst::Clone(targetGraph);
6108         clone->CastToFunctionImmediate()->functionPtr_ = functionPtr_;
6109         return clone;
6110     }
6111 
GetFunctionPtr()6112     uintptr_t GetFunctionPtr() const
6113     {
6114         return functionPtr_;
6115     }
6116 
SetFunctionPtr(uintptr_t ptr)6117     void SetFunctionPtr(uintptr_t ptr)
6118     {
6119         functionPtr_ = ptr;
6120     }
6121 
6122     PANDA_PUBLIC_API void SetVnObject(VnObject *vnObj) override;
6123     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
6124 
6125 private:
6126     uintptr_t functionPtr_ {0};
6127 };
6128 
6129 /// Get object from the specific source(handle).
6130 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
6131 class PANDA_PUBLIC_API LoadObjFromConstInst : public FixedInputsInst<0> {
6132 public:
6133     using Base = FixedInputsInst;
6134     using Base::Base;
6135 
LoadObjFromConstInst(Inst::Initializer t,uintptr_t ptr)6136     LoadObjFromConstInst(Inst::Initializer t, uintptr_t ptr) : Base(std::move(t)), objectPtr_(ptr) {}
6137 
Clone(const Graph * targetGraph)6138     Inst *Clone(const Graph *targetGraph) const override
6139     {
6140         auto clone = FixedInputsInst::Clone(targetGraph);
6141         clone->CastToLoadObjFromConst()->objectPtr_ = objectPtr_;
6142         return clone;
6143     }
6144 
GetObjPtr()6145     uintptr_t GetObjPtr() const
6146     {
6147         return objectPtr_;
6148     }
6149 
SetObjPtr(uintptr_t ptr)6150     void SetObjPtr(uintptr_t ptr)
6151     {
6152         objectPtr_ = ptr;
6153     }
6154 
6155     PANDA_PUBLIC_API void SetVnObject(VnObject *vnObj) override;
6156     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
6157 
6158 private:
6159     uintptr_t objectPtr_ {0};
6160 };
6161 
6162 /// Select instruction
6163 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
6164 class PANDA_PUBLIC_API SelectInst : public ConditionMixin<InstWithOperandsType<FixedInputsInst<4U>>> {
6165 public:
6166     using Base = ConditionMixin<InstWithOperandsType<FixedInputsInst<4U>>>;
6167     using Base::Base;
6168 
SelectInst(Initializer t,DataType::Type operType,ConditionCode cc)6169     SelectInst(Initializer t, DataType::Type operType, ConditionCode cc) : Base(std::move(t))
6170     {
6171         SetOperandsType(operType);
6172         SetCc(cc);
6173         if (IsReferenceOrAny()) {
6174             SetFlag(inst_flags::REF_SPECIAL);
6175             // Select instruction cannot be generated before LICM where NO_HOIST flag is checked
6176             // Set it just for consistency
6177             SetFlag(inst_flags::NO_HOIST);
6178         }
6179     }
6180 
6181     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
6182     PANDA_PUBLIC_API void SetVnObject(VnObject *vnObj) override;
6183 
GetInputType(size_t index)6184     DataType::Type GetInputType(size_t index) const override
6185     {
6186         ASSERT(index < GetInputsCount());
6187         if (index < 2U) {
6188             return GetType();
6189         }
6190         return GetOperandsType();
6191     }
6192 
Clone(const Graph * targetGraph)6193     Inst *Clone(const Graph *targetGraph) const override
6194     {
6195         auto clone = FixedInputsInst::Clone(targetGraph);
6196         ASSERT(clone->CastToSelect()->GetCc() == GetCc());
6197         ASSERT(clone->CastToSelect()->GetOperandsType() == GetOperandsType());
6198         return clone;
6199     }
6200 };
6201 
6202 /// SelectImm with comparison with immediate
6203 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
6204 class PANDA_PUBLIC_API SelectImmInst : public InstWithOperandsType<ConditionMixin<FixedInputsInst3>>,
6205                                        public ImmediateMixin {
6206 public:
6207     using Base = InstWithOperandsType<ConditionMixin<FixedInputsInst3>>;
6208     using Base::Base;
6209 
SelectImmInst(Initializer t,uint64_t imm,DataType::Type operType,ConditionCode cc)6210     SelectImmInst(Initializer t, uint64_t imm, DataType::Type operType, ConditionCode cc)
6211         : Base(std::move(t)), ImmediateMixin(imm)
6212     {
6213         SetOperandsType(operType);
6214         SetCc(cc);
6215         if (IsReferenceOrAny()) {
6216             SetFlag(inst_flags::REF_SPECIAL);
6217             SetFlag(inst_flags::NO_HOIST);
6218         }
6219     }
6220 
GetInputType(size_t index)6221     DataType::Type GetInputType(size_t index) const override
6222     {
6223         ASSERT(index < GetInputsCount());
6224         if (index < 2U) {
6225             return GetType();
6226         }
6227         return GetOperandsType();
6228     }
6229 
6230     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
6231     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
6232 
Clone(const Graph * targetGraph)6233     Inst *Clone(const Graph *targetGraph) const override
6234     {
6235         auto clone = FixedInputsInst::Clone(targetGraph);
6236         ASSERT(clone->CastToSelectImm()->GetCc() == GetCc());
6237         ASSERT(clone->CastToSelectImm()->GetOperandsType() == GetOperandsType());
6238         clone->CastToSelectImm()->SetImm(GetImm());
6239         return clone;
6240     }
6241 };
6242 
6243 /// Conditional jump instruction
6244 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
6245 class PANDA_PUBLIC_API IfInst : public InstWithOperandsType<ConditionMixin<FixedInputsInst2>> {
6246 public:
6247     using Base = InstWithOperandsType<ConditionMixin<FixedInputsInst2>>;
6248     using Base::Base;
6249 
IfInst(Initializer t,ConditionCode cc)6250     IfInst(Initializer t, ConditionCode cc) : Base(std::move(t))
6251     {
6252         SetCc(cc);
6253     }
6254 
6255     IfInst(Initializer t, DataType::Type operType, ConditionCode cc, RuntimeInterface::MethodPtr method = nullptr)
Base(std::move (t))6256         : Base(std::move(t)), method_(method)
6257     {
6258         SetOperandsType(operType);
6259         SetCc(cc);
6260     }
6261 
GetInputType(size_t index)6262     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
6263     {
6264         ASSERT(index < GetInputsCount());
6265         return GetOperandsType();
6266     }
6267 
6268     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
6269 
6270     PANDA_PUBLIC_API void SetVnObject(VnObject *vnObj) override;
6271 
Clone(const Graph * targetGraph)6272     Inst *Clone(const Graph *targetGraph) const override
6273     {
6274         auto clone = FixedInputsInst::Clone(targetGraph);
6275         ASSERT(static_cast<IfInst *>(clone)->GetCc() == GetCc());
6276         ASSERT(static_cast<IfInst *>(clone)->GetOperandsType() == GetOperandsType());
6277         static_cast<IfInst *>(clone)->SetMethod(GetMethod());
6278         return clone;
6279     }
6280 
SetMethod(RuntimeInterface::MethodPtr method)6281     void SetMethod(RuntimeInterface::MethodPtr method)
6282     {
6283         method_ = method;
6284     }
6285 
GetMethod()6286     RuntimeInterface::MethodPtr GetMethod() const
6287     {
6288         return method_;
6289     }
6290 
6291 private:
6292     RuntimeInterface::MethodPtr method_ {nullptr};
6293 };
6294 
6295 /// IfImm instruction with immediate
6296 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
6297 class PANDA_PUBLIC_API IfImmInst : public InstWithOperandsType<ConditionMixin<FixedInputsInst1>>,
6298                                    public ImmediateMixin {
6299 public:
6300     using Base = InstWithOperandsType<ConditionMixin<FixedInputsInst1>>;
6301     using Base::Base;
6302 
IfImmInst(Initializer t,ConditionCode cc,uint64_t imm)6303     IfImmInst(Initializer t, ConditionCode cc, uint64_t imm) : Base(std::move(t)), ImmediateMixin(imm)
6304     {
6305         SetCc(cc);
6306     }
6307 
6308     IfImmInst(Initializer t, uint64_t imm, DataType::Type operType, ConditionCode cc,
6309               RuntimeInterface::MethodPtr method = nullptr)
Base(std::move (t))6310         : Base(std::move(t)), ImmediateMixin(imm), method_(method)
6311     {
6312         SetOperandsType(operType);
6313         SetCc(cc);
6314     }
6315 
6316     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
6317     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
6318     PANDA_PUBLIC_API void SetVnObject(VnObject *vnObj) override;
6319 
GetInputType(size_t index)6320     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
6321     {
6322         ASSERT(index < GetInputsCount());
6323         return GetOperandsType();
6324     }
6325 
Clone(const Graph * targetGraph)6326     Inst *Clone(const Graph *targetGraph) const override
6327     {
6328         auto clone = FixedInputsInst::Clone(targetGraph);
6329         ASSERT(clone->CastToIfImm()->GetCc() == GetCc());
6330         ASSERT(clone->CastToIfImm()->GetOperandsType() == GetOperandsType());
6331         clone->CastToIfImm()->SetMethod(GetMethod());
6332         clone->CastToIfImm()->SetImm(GetImm());
6333         return clone;
6334     }
6335 
6336     BasicBlock *GetEdgeIfInputTrue();
6337     BasicBlock *GetEdgeIfInputFalse();
6338 
SetMethod(RuntimeInterface::MethodPtr method)6339     void SetMethod(RuntimeInterface::MethodPtr method)
6340     {
6341         method_ = method;
6342     }
6343 
GetMethod()6344     RuntimeInterface::MethodPtr GetMethod() const
6345     {
6346         return method_;
6347     }
6348 
6349 private:
6350     size_t GetTrueInputEdgeIdx();
6351     RuntimeInterface::MethodPtr method_ {nullptr};
6352 };
6353 
6354 /// Load element from a pair of values, using index as immediate
6355 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
6356 class PANDA_PUBLIC_API LoadPairPartInst : public FixedInputsInst1, public ImmediateMixin {
6357 public:
6358     using FixedInputsInst1::FixedInputsInst1;
6359 
LoadPairPartInst(Opcode opcode,uint64_t imm)6360     LoadPairPartInst(Opcode opcode, uint64_t imm) : FixedInputsInst1(opcode), ImmediateMixin(imm) {}
6361 
LoadPairPartInst(Initializer t,uint64_t imm)6362     LoadPairPartInst(Initializer t, uint64_t imm) : FixedInputsInst1(std::move(t)), ImmediateMixin(imm) {}
6363 
GetSrcRegIndex()6364     uint32_t GetSrcRegIndex() const override
6365     {
6366         return GetImm();
6367     }
6368 
6369     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
6370 
Clone(const Graph * targetGraph)6371     Inst *Clone(const Graph *targetGraph) const override
6372     {
6373         auto clone = FixedInputsInst::Clone(targetGraph);
6374         clone->CastToLoadPairPart()->SetImm(GetImm());
6375         return clone;
6376     }
6377 
Latency()6378     uint32_t Latency() const override
6379     {
6380         return g_options.GetCompilerSchedLatencyLong();
6381     }
6382 };
6383 
6384 /// Load a pair of consecutive values from array
6385 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
6386 class PANDA_PUBLIC_API LoadArrayPairInst : public NeedBarrierMixin<MultipleOutputMixin<FixedInputsInst2, 2U>>,
6387                                            public ImmediateMixin {
6388 public:
6389     using Base = NeedBarrierMixin<MultipleOutputMixin<FixedInputsInst2, 2U>>;
6390     using Base::Base;
6391 
Base(std::move (t))6392     explicit LoadArrayPairInst(Initializer t, bool needBarrier = false) : Base(std::move(t))
6393     {
6394         SetNeedBarrier(needBarrier);
6395     }
6396 
GetArray()6397     Inst *GetArray()
6398     {
6399         return GetInput(0).GetInst();
6400     }
GetIndex()6401     Inst *GetIndex()
6402     {
6403         return GetInput(1).GetInst();
6404     }
6405 
IsBarrier()6406     bool IsBarrier() const override
6407     {
6408         return Inst::IsBarrier() || GetNeedBarrier();
6409     }
6410 
Clone(const Graph * targetGraph)6411     Inst *Clone(const Graph *targetGraph) const override
6412     {
6413         auto clone = FixedInputsInst::Clone(targetGraph)->CastToLoadArrayPair();
6414         static_cast<LoadArrayPairInst *>(clone)->SetImm(GetImm());
6415 #ifndef NDEBUG
6416         for (size_t i = 0; i < GetDstCount(); ++i) {
6417             clone->SetDstReg(i, GetDstReg(i));
6418         }
6419 #endif
6420         return clone;
6421     }
6422 
GetInputType(size_t index)6423     DataType::Type GetInputType(size_t index) const override
6424     {
6425         ASSERT(index < GetInputsCount());
6426         switch (index) {
6427             case 0:
6428                 return DataType::REFERENCE;
6429             case 1:
6430                 return DataType::INT32;
6431             default:
6432                 return DataType::NO_TYPE;
6433         }
6434     }
6435 
Latency()6436     uint32_t Latency() const override
6437     {
6438         return 0;
6439     }
6440 
6441     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
6442 };
6443 
6444 /// Load a pair of consecutive values from object
6445 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
6446 class PANDA_PUBLIC_API LoadObjectPairInst
6447     : public ObjectTypeMixin<VolatileMixin<NeedBarrierMixin<MultipleOutputMixin<FixedInputsInst1, 2U>>>>,
6448       public TypeIdMixin2,
6449       public FieldMixin2 {
6450 public:
6451     using Base = ObjectTypeMixin<VolatileMixin<NeedBarrierMixin<MultipleOutputMixin<FixedInputsInst1, 2U>>>>;
6452     using Base::Base;
6453 
LoadObjectPairInst(Initializer t)6454     explicit LoadObjectPairInst(Initializer t) : Base(std::move(t))
6455     {
6456         SetVolatile(false);
6457         SetNeedBarrier(false);
6458     }
6459 
GetInputType(size_t index)6460     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
6461     {
6462         ASSERT(index < GetInputsCount());
6463         ASSERT(GetInputsCount() == 1);
6464         auto inputType = GetInput(0).GetInst()->GetType();
6465         ASSERT(inputType == DataType::REFERENCE || inputType == DataType::ANY);
6466         return inputType;
6467     }
6468 
IsBarrier()6469     bool IsBarrier() const override
6470     {
6471         return Inst::IsBarrier() || GetNeedBarrier() || GetVolatile();
6472     }
6473 
Clone(const Graph * targetGraph)6474     Inst *Clone(const Graph *targetGraph) const override
6475     {
6476         auto clone = FixedInputsInst::Clone(targetGraph);
6477         clone->CastToLoadObjectPair()->SetTypeId0(GetTypeId0());
6478         clone->CastToLoadObjectPair()->SetTypeId1(GetTypeId1());
6479         clone->CastToLoadObjectPair()->SetMethod(GetMethod());
6480         clone->CastToLoadObjectPair()->SetObjField0(GetObjField0());
6481         clone->CastToLoadObjectPair()->SetObjField1(GetObjField1());
6482         ASSERT(clone->CastToLoadObjectPair()->GetVolatile() == GetVolatile());
6483         ASSERT(clone->CastToLoadObjectPair()->GetObjectType() == GetObjectType());
6484         return clone;
6485     }
6486 
Latency()6487     uint32_t Latency() const override
6488     {
6489         return g_options.GetCompilerSchedLatencyLong();
6490     }
6491 
6492     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
6493 };
6494 
6495 /// Store a pair of consecutive values to array
6496 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
6497 class PANDA_PUBLIC_API StoreArrayPairInst : public NeedBarrierMixin<FixedInputsInst<4U>>, public ImmediateMixin {
6498 public:
6499     using Base = NeedBarrierMixin<FixedInputsInst<4U>>;
6500     using Base::Base;
6501 
Base(std::move (t))6502     explicit StoreArrayPairInst(Initializer t, bool needBarrier = false) : Base(std::move(t))
6503     {
6504         SetNeedBarrier(needBarrier);
6505     }
6506 
GetIndex()6507     Inst *GetIndex()
6508     {
6509         return GetInput(1).GetInst();
6510     }
GetArray()6511     Inst *GetArray()
6512     {
6513         return GetInput(0).GetInst();
6514     }
GetStoredValue(uint64_t index)6515     Inst *GetStoredValue(uint64_t index)
6516     {
6517         return GetInput(2U + index).GetInst();
6518     }
GetInputType(size_t index)6519     DataType::Type GetInputType(size_t index) const override
6520     {
6521         ASSERT(index < GetInputsCount());
6522         switch (index) {
6523             case 0:
6524                 return DataType::REFERENCE;
6525             case 1:
6526                 return DataType::INT32;
6527             case 2U:
6528             case 3U:
6529                 return GetType();
6530             default:
6531                 return DataType::NO_TYPE;
6532         }
6533     }
6534 
6535     // StoreArrayPair call barriers twice,so we need to save input register for second call
IsPropagateLiveness()6536     bool IsPropagateLiveness() const override
6537     {
6538         return GetType() == DataType::REFERENCE;
6539     }
6540 
IsBarrier()6541     bool IsBarrier() const override
6542     {
6543         return Inst::IsBarrier() || GetNeedBarrier();
6544     }
6545 
Clone(const Graph * targetGraph)6546     Inst *Clone(const Graph *targetGraph) const override
6547     {
6548         auto clone = FixedInputsInst::Clone(targetGraph)->CastToStoreArrayPair();
6549         static_cast<StoreArrayPairInst *>(clone)->SetImm(GetImm());
6550         return clone;
6551     }
6552 
6553     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
6554 };
6555 
6556 /// Store a pair of consecutive values to object
6557 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
6558 class PANDA_PUBLIC_API StoreObjectPairInst : public ObjectTypeMixin<VolatileMixin<NeedBarrierMixin<FixedInputsInst3>>>,
6559                                              public TypeIdMixin2,
6560                                              public FieldMixin2 {
6561 public:
6562     using Base = ObjectTypeMixin<VolatileMixin<NeedBarrierMixin<FixedInputsInst3>>>;
6563     using Base::Base;
6564     static constexpr size_t STORED_INPUT_INDEX = 2;
6565 
StoreObjectPairInst(Initializer t)6566     explicit StoreObjectPairInst(Initializer t) : Base(std::move(t))
6567     {
6568         SetVolatile(false);
6569         SetNeedBarrier(false);
6570     }
6571 
IsBarrier()6572     bool IsBarrier() const override
6573     {
6574         return Inst::IsBarrier() || GetNeedBarrier() || GetVolatile();
6575     }
6576 
GetInputType(size_t index)6577     DataType::Type GetInputType(size_t index) const override
6578     {
6579         ASSERT(index < GetInputsCount());
6580         ASSERT(GetInputsCount() == 3U);
6581         return index == 0 ? DataType::REFERENCE : GetType();
6582     }
6583 
Clone(const Graph * targetGraph)6584     Inst *Clone(const Graph *targetGraph) const override
6585     {
6586         auto clone = FixedInputsInst::Clone(targetGraph);
6587         clone->CastToStoreObjectPair()->SetTypeId0(GetTypeId0());
6588         clone->CastToStoreObjectPair()->SetTypeId1(GetTypeId1());
6589         clone->CastToStoreObjectPair()->SetMethod(GetMethod());
6590         clone->CastToStoreObjectPair()->SetObjField0(GetObjField0());
6591         clone->CastToStoreObjectPair()->SetObjField1(GetObjField1());
6592         ASSERT(clone->CastToStoreObjectPair()->GetVolatile() == GetVolatile());
6593         ASSERT(clone->CastToStoreObjectPair()->GetObjectType() == GetObjectType());
6594         return clone;
6595     }
6596 
6597     // StoreObject call barriers twice,so we need to save input register for second call
IsPropagateLiveness()6598     bool IsPropagateLiveness() const override
6599     {
6600         return GetType() == DataType::REFERENCE;
6601     }
6602 
6603     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
6604 };
6605 
6606 /// Load a pair of consecutive values from array, using array index as immediate
6607 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
6608 class PANDA_PUBLIC_API LoadArrayPairInstI : public NeedBarrierMixin<MultipleOutputMixin<FixedInputsInst1, 2U>>,
6609                                             public ImmediateMixin {
6610 public:
6611     using Base = NeedBarrierMixin<MultipleOutputMixin<FixedInputsInst1, 2U>>;
6612     using Base::Base;
6613 
LoadArrayPairInstI(Opcode opcode,uint64_t imm)6614     explicit LoadArrayPairInstI(Opcode opcode, uint64_t imm) : Base(opcode), ImmediateMixin(imm) {}
6615 
Base(std::move (t))6616     LoadArrayPairInstI(Initializer t, uint64_t imm, bool needBarrier = false) : Base(std::move(t)), ImmediateMixin(imm)
6617     {
6618         SetNeedBarrier(needBarrier);
6619     }
6620 
GetArray()6621     Inst *GetArray()
6622     {
6623         return GetInput(0).GetInst();
6624     }
6625 
IsBarrier()6626     bool IsBarrier() const override
6627     {
6628         return Inst::IsBarrier() || GetNeedBarrier();
6629     }
6630     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
6631 
GetInputType(size_t index)6632     DataType::Type GetInputType(size_t index) const override
6633     {
6634         ASSERT(index < GetInputsCount());
6635         if (index == 0) {
6636             return DataType::REFERENCE;
6637         }
6638         return DataType::NO_TYPE;
6639     }
6640 
Clone(const Graph * targetGraph)6641     Inst *Clone(const Graph *targetGraph) const override
6642     {
6643         auto clone = FixedInputsInst::Clone(targetGraph)->CastToLoadArrayPairI();
6644         clone->SetImm(GetImm());
6645 #ifndef NDEBUG
6646         for (size_t i = 0; i < GetDstCount(); ++i) {
6647             clone->SetDstReg(i, GetDstReg(i));
6648         }
6649 #endif
6650         return clone;
6651     }
6652 
Latency()6653     uint32_t Latency() const override
6654     {
6655         return 0;
6656     }
6657 };
6658 
6659 /// Store a pair of consecutive values to array, using array index as immediate
6660 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
6661 class PANDA_PUBLIC_API StoreArrayPairInstI : public NeedBarrierMixin<FixedInputsInst3>, public ImmediateMixin {
6662 public:
6663     using Base = NeedBarrierMixin<FixedInputsInst3>;
6664     using Base::Base;
6665 
StoreArrayPairInstI(Opcode opcode,uint64_t imm)6666     explicit StoreArrayPairInstI(Opcode opcode, uint64_t imm) : Base(opcode), ImmediateMixin(imm) {}
6667 
Base(std::move (t))6668     StoreArrayPairInstI(Initializer t, uint64_t imm, bool needBarrier = false) : Base(std::move(t)), ImmediateMixin(imm)
6669     {
6670         SetNeedBarrier(needBarrier);
6671     }
6672 
GetInputType(size_t index)6673     DataType::Type GetInputType(size_t index) const override
6674     {
6675         ASSERT(index < GetInputsCount());
6676         switch (index) {
6677             case 0:
6678                 return DataType::REFERENCE;
6679             case 1:
6680             case 2U:
6681                 return GetType();
6682             default:
6683                 return DataType::NO_TYPE;
6684         }
6685     }
GetArray()6686     Inst *GetArray()
6687     {
6688         return GetInput(0).GetInst();
6689     }
GetFirstValue()6690     Inst *GetFirstValue()
6691     {
6692         return GetInput(1).GetInst();
6693     }
GetSecondValue()6694     Inst *GetSecondValue()
6695     {
6696         return GetInput(2U).GetInst();
6697     }
6698 
6699     // StoreArrayPairI call barriers twice,so we need to save input register for second call
IsPropagateLiveness()6700     bool IsPropagateLiveness() const override
6701     {
6702         return GetType() == DataType::REFERENCE;
6703     }
6704 
IsBarrier()6705     bool IsBarrier() const override
6706     {
6707         return Inst::IsBarrier() || GetNeedBarrier();
6708     }
6709 
6710     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
6711 
Clone(const Graph * targetGraph)6712     Inst *Clone(const Graph *targetGraph) const override
6713     {
6714         auto clone = FixedInputsInst::Clone(targetGraph);
6715         clone->CastToStoreArrayPairI()->SetImm(GetImm());
6716         return clone;
6717     }
6718 };
6719 
6720 /// CatchPhiInst instruction
6721 class PANDA_PUBLIC_API CatchPhiInst : public DynamicInputsInst {
6722 public:
6723     using DynamicInputsInst::DynamicInputsInst;
6724 
CatchPhiInst(Initializer t)6725     explicit CatchPhiInst(Initializer t) : DynamicInputsInst(std::move(t)) {}
6726 
GetThrowableInsts()6727     const ArenaVector<const Inst *> *GetThrowableInsts() const
6728     {
6729         return throwInsts_;
6730     }
6731 
GetThrowableInst(size_t i)6732     const Inst *GetThrowableInst(size_t i) const
6733     {
6734         ASSERT(throwInsts_ != nullptr && i < throwInsts_->size());
6735         return throwInsts_->at(i);
6736     }
6737 
6738     void AppendThrowableInst(const Inst *inst);
6739     void ReplaceThrowableInst(const Inst *oldInst, const Inst *newInst);
6740     void RemoveInput(unsigned index) override;
6741 
IsAcc()6742     bool IsAcc() const
6743     {
6744         return GetField<IsAccFlag>();
6745     }
6746 
SetIsAcc()6747     void SetIsAcc()
6748     {
6749         SetField<IsAccFlag>(true);
6750     }
6751 
6752 protected:
6753     using IsAccFlag = LastField::NextFlag;
6754     using LastField = IsAccFlag;
6755 
6756 private:
GetThrowableInstIndex(const Inst * inst)6757     size_t GetThrowableInstIndex(const Inst *inst)
6758     {
6759         ASSERT(throwInsts_ != nullptr);
6760         auto it = std::find(throwInsts_->begin(), throwInsts_->end(), inst);
6761         ASSERT(it != throwInsts_->end());
6762         return std::distance(throwInsts_->begin(), it);
6763     }
6764 
6765 private:
6766     ArenaVector<const Inst *> *throwInsts_ {nullptr};
6767 };
6768 
6769 class PANDA_PUBLIC_API TryInst : public FixedInputsInst0 {
6770 public:
6771     using FixedInputsInst0::FixedInputsInst0;
6772 
6773     explicit TryInst(Opcode opcode, BasicBlock *endBb = nullptr)
6774         : FixedInputsInst0({opcode, DataType::NO_TYPE, INVALID_PC}), tryEndBb_(endBb)
6775     {
6776     }
6777 
6778     void AppendCatchTypeId(uint32_t id, uint32_t catchEdgeIndex);
6779 
GetCatchTypeIds()6780     const ArenaVector<uint32_t> *GetCatchTypeIds() const
6781     {
6782         return catchTypeIds_;
6783     }
6784 
GetCatchEdgeIndexes()6785     const ArenaVector<uint32_t> *GetCatchEdgeIndexes() const
6786     {
6787         return catchEdgeIndexes_;
6788     }
6789 
GetCatchTypeIdsCount()6790     size_t GetCatchTypeIdsCount() const
6791     {
6792         return (catchTypeIds_ == nullptr ? 0 : catchTypeIds_->size());
6793     }
6794 
6795     Inst *Clone(const Graph *targetGraph) const override;
6796 
SetTryEndBlock(BasicBlock * tryEndBb)6797     void SetTryEndBlock(BasicBlock *tryEndBb)
6798     {
6799         tryEndBb_ = tryEndBb;
6800     }
6801 
GetTryEndBlock()6802     BasicBlock *GetTryEndBlock() const
6803     {
6804         return tryEndBb_;
6805     }
6806 
6807 private:
6808     ArenaVector<uint32_t> *catchTypeIds_ {nullptr};
6809     ArenaVector<uint32_t> *catchEdgeIndexes_ {nullptr};
6810     BasicBlock *tryEndBb_ {nullptr};
6811 };
6812 
6813 PANDA_PUBLIC_API TryInst *GetTryBeginInst(const BasicBlock *tryBeginBb);
6814 
6815 /// Mixin for Deoptimize instructions
6816 template <typename T>
6817 class DeoptimizeTypeMixin : public T {
6818 public:
6819     using T::T;
6820 
SetDeoptimizeType(DeoptimizeType deoptType)6821     void SetDeoptimizeType(DeoptimizeType deoptType)
6822     {
6823         T::template SetField<DeoptimizeTypeField>(deoptType);
6824     }
6825 
SetDeoptimizeType(Inst * inst)6826     void SetDeoptimizeType(Inst *inst)
6827     {
6828         switch (inst->GetOpcode()) {
6829             case Opcode::NullCheck:
6830                 SetDeoptimizeType(DeoptimizeType::NULL_CHECK);
6831                 break;
6832             case Opcode::BoundsCheck:
6833                 SetDeoptimizeType(DeoptimizeType::BOUNDS_CHECK);
6834                 break;
6835             case Opcode::ZeroCheck:
6836                 SetDeoptimizeType(DeoptimizeType::ZERO_CHECK);
6837                 break;
6838             case Opcode::NegativeCheck:
6839                 SetDeoptimizeType(DeoptimizeType::NEGATIVE_CHECK);
6840                 break;
6841             case Opcode::NotPositiveCheck:
6842                 SetDeoptimizeType(DeoptimizeType::NEGATIVE_CHECK);
6843                 break;
6844             case Opcode::CheckCast:
6845                 SetDeoptimizeType(DeoptimizeType::CHECK_CAST);
6846                 break;
6847             case Opcode::AnyTypeCheck: {
6848                 SetDeoptimizeType(inst->CastToAnyTypeCheck()->GetDeoptimizeType());
6849                 break;
6850             }
6851             case Opcode::AddOverflowCheck:
6852             case Opcode::SubOverflowCheck:
6853             case Opcode::NegOverflowAndZeroCheck:
6854                 SetDeoptimizeType(DeoptimizeType::OVERFLOW_TYPE);
6855                 break;
6856             case Opcode::DeoptimizeIf:
6857                 SetDeoptimizeType(static_cast<DeoptimizeTypeMixin *>(inst)->GetDeoptimizeType());
6858                 break;
6859             case Opcode::Intrinsic:
6860                 SetDeoptimizeType(DeoptimizeType::NOT_PROFILED);
6861                 break;
6862             default:
6863                 UNREACHABLE();
6864                 break;
6865         }
6866     }
6867 
GetDeoptimizeType()6868     DeoptimizeType GetDeoptimizeType() const
6869     {
6870         return T::template GetField<DeoptimizeTypeField>();
6871     }
6872 
6873 protected:
6874     using DeoptimizeTypeField =
6875         typename T::LastField::template NextField<DeoptimizeType, MinimumBitsToStore(DeoptimizeType::COUNT)>;
6876     using LastField = DeoptimizeTypeField;
6877 };
6878 
6879 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
6880 class PANDA_PUBLIC_API DeoptimizeInst : public DeoptimizeTypeMixin<FixedInputsInst1> {
6881 public:
6882     using Base = DeoptimizeTypeMixin<FixedInputsInst1>;
6883     using Base::Base;
6884 
Base(std::move (t))6885     explicit DeoptimizeInst(Initializer t, DeoptimizeType deoptType = DeoptimizeType::NOT_PROFILED) : Base(std::move(t))
6886     {
6887         SetDeoptimizeType(deoptType);
6888     }
6889 
Clone(const Graph * targetGraph)6890     Inst *Clone(const Graph *targetGraph) const override
6891     {
6892         auto clone = FixedInputsInst::Clone(targetGraph);
6893         ASSERT(clone->CastToDeoptimize()->GetDeoptimizeType() == GetDeoptimizeType());
6894         return clone;
6895     }
6896 
6897     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
6898 };
6899 
6900 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
6901 class PANDA_PUBLIC_API DeoptimizeIfInst : public DeoptimizeTypeMixin<FixedInputsInst2> {
6902     using Base = DeoptimizeTypeMixin<FixedInputsInst2>;
6903 
6904 public:
6905     using Base::Base;
6906 
DeoptimizeIfInst(Opcode opcode,uint32_t pc,Inst * cond,Inst * ss,DeoptimizeType deoptType)6907     DeoptimizeIfInst(Opcode opcode, uint32_t pc, Inst *cond, Inst *ss, DeoptimizeType deoptType)
6908         : Base({opcode, DataType::NO_TYPE, pc}, cond, ss)
6909     {
6910         SetDeoptimizeType(deoptType);
6911     }
6912 
Clone(const Graph * targetGraph)6913     Inst *Clone(const Graph *targetGraph) const override
6914     {
6915         auto clone = FixedInputsInst::Clone(targetGraph);
6916         ASSERT(clone->CastToDeoptimizeIf()->GetDeoptimizeType() == GetDeoptimizeType());
6917         return clone;
6918     }
6919 
GetInputType(size_t index)6920     DataType::Type GetInputType(size_t index) const override
6921     {
6922         ASSERT(index < GetInputsCount());
6923         switch (index) {
6924             case 0:
6925                 return GetInput(0).GetInst()->GetType();
6926             case 1:
6927                 return DataType::NO_TYPE;
6928             default:
6929                 UNREACHABLE();
6930         }
6931     }
6932 
6933     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
6934 };
6935 
6936 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
6937 class PANDA_PUBLIC_API DeoptimizeCompareInst
6938     : public InstWithOperandsType<DeoptimizeTypeMixin<ConditionMixin<FixedInputsInst3>>> {
6939 public:
6940     using Base = InstWithOperandsType<DeoptimizeTypeMixin<ConditionMixin<FixedInputsInst3>>>;
6941     using Base::Base;
6942 
DeoptimizeCompareInst(Opcode opcode,const DeoptimizeIfInst * deoptIf,const CompareInst * compare)6943     explicit DeoptimizeCompareInst(Opcode opcode, const DeoptimizeIfInst *deoptIf, const CompareInst *compare)
6944         : Base({opcode, deoptIf->GetType(), deoptIf->GetPc()})
6945     {
6946         SetDeoptimizeType(deoptIf->GetDeoptimizeType());
6947         SetOperandsType(compare->GetOperandsType());
6948         SetCc(compare->GetCc());
6949     }
6950 
Clone(const Graph * targetGraph)6951     Inst *Clone(const Graph *targetGraph) const override
6952     {
6953         auto clone = FixedInputsInst3::Clone(targetGraph);
6954         ASSERT(clone->CastToDeoptimizeCompare()->GetDeoptimizeType() == GetDeoptimizeType());
6955         ASSERT(clone->CastToDeoptimizeCompare()->GetOperandsType() == GetOperandsType());
6956         ASSERT(clone->CastToDeoptimizeCompare()->GetCc() == GetCc());
6957         return clone;
6958     }
6959 
GetInputType(size_t index)6960     DataType::Type GetInputType(size_t index) const override
6961     {
6962         ASSERT(index < GetInputsCount());
6963         switch (index) {
6964             case 0:
6965             case 1:
6966                 return GetInput(index).GetInst()->GetType();
6967             case 2U:
6968                 return DataType::NO_TYPE;
6969             default:
6970                 UNREACHABLE();
6971         }
6972     }
6973 
6974     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
6975 };
6976 
6977 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
6978 class PANDA_PUBLIC_API DeoptimizeCompareImmInst
6979     : public InstWithOperandsType<DeoptimizeTypeMixin<ConditionMixin<FixedInputsInst2>>>,
6980       public ImmediateMixin {
6981 public:
6982     using Base = InstWithOperandsType<DeoptimizeTypeMixin<ConditionMixin<FixedInputsInst2>>>;
6983     using Base::Base;
6984 
DeoptimizeCompareImmInst(Opcode opcode,const DeoptimizeIfInst * deoptIf,const CompareInst * compare,uint64_t imm)6985     explicit DeoptimizeCompareImmInst(Opcode opcode, const DeoptimizeIfInst *deoptIf, const CompareInst *compare,
6986                                       uint64_t imm)
6987         : Base({opcode, deoptIf->GetType(), deoptIf->GetPc()}), ImmediateMixin(imm)
6988     {
6989         SetDeoptimizeType(deoptIf->GetDeoptimizeType());
6990         SetOperandsType(compare->GetOperandsType());
6991         SetCc(compare->GetCc());
6992     }
6993 
GetInputType(size_t index)6994     DataType::Type GetInputType(size_t index) const override
6995     {
6996         ASSERT(index < GetInputsCount());
6997         switch (index) {
6998             case 0:
6999                 return GetInput(0).GetInst()->GetType();
7000             case 1:
7001                 return DataType::NO_TYPE;
7002             default:
7003                 UNREACHABLE();
7004         }
7005     }
7006 
Clone(const Graph * targetGraph)7007     Inst *Clone(const Graph *targetGraph) const override
7008     {
7009         auto clone = FixedInputsInst2::Clone(targetGraph);
7010         ASSERT(clone->CastToDeoptimizeCompareImm()->GetDeoptimizeType() == GetDeoptimizeType());
7011         ASSERT(clone->CastToDeoptimizeCompareImm()->GetOperandsType() == GetOperandsType());
7012         ASSERT(clone->CastToDeoptimizeCompareImm()->GetCc() == GetCc());
7013         clone->CastToDeoptimizeCompareImm()->SetImm(GetImm());
7014         return clone;
7015     }
7016 
7017     PANDA_PUBLIC_API void DumpOpcode(std::ostream *out) const override;
7018     PANDA_PUBLIC_API bool DumpInputs(std::ostream *out) const override;
7019 };
7020 
7021 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
7022 class ThrowInst : public FixedInputsInst2, public MethodDataMixin {
7023 public:
7024     using Base = FixedInputsInst2;
7025     using Base::Base;
7026 
GetInputType(size_t index)7027     DataType::Type GetInputType(size_t index) const override
7028     {
7029         ASSERT(index < GetInputsCount());
7030         if (index == 0) {
7031             return DataType::REFERENCE;
7032         }
7033         return DataType::NO_TYPE;
7034     }
7035 
IsInlined()7036     bool IsInlined() const
7037     {
7038         auto ss = GetSaveState();
7039         ASSERT(ss != nullptr);
7040         return ss->GetCallerInst() != nullptr;
7041     }
7042 
Clone(const Graph * targetGraph)7043     Inst *Clone(const Graph *targetGraph) const override
7044     {
7045         ASSERT(targetGraph != nullptr);
7046         auto instClone = FixedInputsInst2::Clone(targetGraph);
7047         auto throwClone = static_cast<ThrowInst *>(instClone);
7048         throwClone->SetCallMethodId(GetCallMethodId());
7049         throwClone->SetCallMethod(GetCallMethod());
7050         return instClone;
7051     }
7052 };
7053 
7054 class BinaryOverflowInst : public IfInst {
7055 public:
7056     using Base = IfInst;
7057     using Base::Base;
7058 
BinaryOverflowInst(Initializer t,ConditionCode cc)7059     BinaryOverflowInst(Initializer t, ConditionCode cc) : Base(t, cc)
7060     {
7061         SetOperandsType(GetType());
7062     }
7063 
GetInputType(size_t index)7064     DataType::Type GetInputType([[maybe_unused]] size_t index) const override
7065     {
7066         ASSERT(index < GetInputsCount());
7067         return GetType();
7068     }
7069 
GetOperandsType()7070     DataType::Type GetOperandsType() const override
7071     {
7072         return GetType();
7073     }
7074 };
7075 
7076 bool IsVolatileMemInst(const Inst *inst);
7077 
7078 // Check if instruction is pseudo-user for mutli-output instruction
IsPseudoUserOfMultiOutput(Inst * inst)7079 inline bool IsPseudoUserOfMultiOutput(Inst *inst)
7080 {
7081     ASSERT(inst != nullptr);
7082     switch (inst->GetOpcode()) {
7083         case Opcode::LoadPairPart:
7084             return true;
7085         default:
7086             return false;
7087     }
7088 }
7089 
7090 template <typename T>
7091 struct ConstructWrapper : public T {
7092     template <typename... Args>
ConstructWrapperConstructWrapper7093     explicit ConstructWrapper([[maybe_unused]] ArenaAllocator *a, Args &&...args)
7094         : ConstructWrapper(std::forward<Args>(args)...)
7095     {
7096     }
7097 
7098     // Try wrap Inst::Initializer:
7099     template <typename PC, typename... Args>
ConstructWrapperConstructWrapper7100     ConstructWrapper(Opcode opcode, DataType::Type type, PC pc, Args &&...args)
7101         : ConstructWrapper(Inst::Initializer {opcode, type, pc}, std::forward<Args>(args)...)
7102     {
7103     }
7104 
7105     // Try wrap FixedInputs:
7106     using Initializer = typename T::Initializer;
7107 
7108     template <typename T0, typename... Args, typename = Inst::CheckBase<T0>>
ConstructWrapperConstructWrapper7109     ConstructWrapper(Inst::Initializer t, T0 input0, Args &&...args)
7110         : ConstructWrapper(Initializer {t, std::array<Inst *, 1U> {input0}}, std::forward<Args>(args)...)
7111     {
7112     }
7113 
7114     template <typename T0, typename T1, typename... Args, typename = Inst::CheckBase<T0, T1>>
ConstructWrapperConstructWrapper7115     ConstructWrapper(Inst::Initializer t, T0 input0, T1 input1, Args &&...args)
7116         : ConstructWrapper(Initializer {t, std::array<Inst *, 2U> {input0, input1}}, std::forward<Args>(args)...)
7117     {
7118     }
7119 
7120     template <typename T0, typename T1, typename T2, typename... Args, typename = Inst::CheckBase<T0, T1, T2>>
ConstructWrapperConstructWrapper7121     ConstructWrapper(Inst::Initializer t, T0 input0, T1 input1, T2 input2, Args &&...args)
7122         : ConstructWrapper(Initializer {t, std::array<Inst *, 3U> {input0, input1, input2}},
7123                            std::forward<Args>(args)...)
7124     {
7125     }
7126 
7127     template <size_t N, typename... Args>
ConstructWrapperConstructWrapper7128     ConstructWrapper(Inst::Initializer t, std::array<Inst *, N> &&inputs, Args &&...args)
7129         : ConstructWrapper(Initializer {t, std::move(inputs)}, std::forward<Args>(args)...)
7130     {
7131     }
7132 
7133     // Final forward:
7134     template <typename... Args>
ConstructWrapperConstructWrapper7135     explicit ConstructWrapper(Args &&...args) : T(std::forward<Args>(args)...)
7136     {
7137     }
7138 };
7139 
7140 template <>
7141 struct ConstructWrapper<SpillFillInst> : SpillFillInst {
7142     ConstructWrapper(ArenaAllocator *allocator, Opcode opcode, SpillFillType type = UNKNOWN)
7143         : SpillFillInst(allocator, opcode, type)
7144     {
7145     }
7146 };
7147 
7148 template <typename InstType, typename... Args>
7149 InstType *Inst::ConstructInst(InstType *ptr, ArenaAllocator *allocator, Args &&...args)
7150 {
7151     // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
7152     if constexpr (InstType::INPUT_COUNT == MAX_STATIC_INPUTS) {
7153         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
7154         auto opsPtr = reinterpret_cast<char *>(ptr) - sizeof(DynamicOperands);
7155         [[maybe_unused]] auto ops = new (opsPtr) DynamicOperands(allocator);
7156         // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
7157     } else if constexpr (InstType::INPUT_COUNT != 0) {
7158         constexpr size_t OPERANDS_SIZE = sizeof(Operands<InstType::INPUT_COUNT>);
7159         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
7160         auto operands = new (reinterpret_cast<char *>(ptr) - OPERANDS_SIZE) Operands<InstType::INPUT_COUNT>;
7161         unsigned idx = InstType::INPUT_COUNT - 1;
7162         for (auto &user : operands->users) {
7163             new (&user) User(true, idx--, InstType::INPUT_COUNT);
7164         }
7165     }
7166     auto inst = new (ptr) ConstructWrapper<InstType>(allocator, std::forward<Args>(args)...);
7167     inst->template SetField<Inst::InputsCount>(InstType::INPUT_COUNT);
7168     return inst;
7169 }
7170 
7171 template <typename InstType, typename... Args>
7172 InstType *Inst::New(ArenaAllocator *allocator, Args &&...args)
7173 {
7174     static_assert(alignof(InstType) >= alignof(uintptr_t));
7175 
7176     size_t allocSize = sizeof(InstType);
7177     auto alignment = DEFAULT_ALIGNMENT;
7178     size_t operandsSize = 0;
7179     // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-branch-clone)
7180     if constexpr (InstType::INPUT_COUNT == MAX_STATIC_INPUTS) {
7181         constexpr size_t OPERANDS_SIZE = sizeof(DynamicOperands);
7182         static_assert((OPERANDS_SIZE % alignof(InstType)) == 0);
7183         operandsSize = OPERANDS_SIZE;
7184         // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-branch-clone)
7185     } else if constexpr (InstType::INPUT_COUNT != 0) {
7186         constexpr size_t OPERANDS_SIZE = sizeof(Operands<InstType::INPUT_COUNT>);
7187         static_assert((OPERANDS_SIZE % alignof(InstType)) == 0);
7188         operandsSize = OPERANDS_SIZE;
7189         alignment = GetLogAlignment(alignof(Operands<InstType::INPUT_COUNT>));
7190     }
7191 
7192     auto ptr = reinterpret_cast<uintptr_t>(allocator->Alloc(allocSize + operandsSize, alignment));
7193     ASSERT(ptr != 0);
7194     return ConstructInst(reinterpret_cast<InstType *>(ptr + operandsSize), allocator, std::forward<Args>(args)...);
7195 }
7196 
7197 INST_CAST_TO_DEF()
7198 
7199 inline Inst *User::GetInput()
7200 {
7201     return GetInst()->GetInput(GetIndex()).GetInst();
7202 }
7203 
7204 inline const Inst *User::GetInput() const
7205 {
7206     return GetInst()->GetInput(GetIndex()).GetInst();
7207 }
7208 
7209 inline std::ostream &operator<<(std::ostream &os, const Inst &inst)
7210 {
7211     inst.Dump(&os, false);
7212     return os;
7213 }
7214 
7215 }  // namespace ark::compiler
7216 
7217 #endif  // COMPILER_OPTIMIZER_IR_INST_H
7218