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