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