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