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