1 /**
2 * Copyright (c) 2021-2022 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 <array>
20 #include <vector>
21 #include <iostream>
22 #include "constants.h"
23 #include "datatype.h"
24 #include "ir-dyn-base-types.h"
25 #include "marker.h"
26 #include "utils/arena_containers.h"
27 #include "utils/span.h"
28 #include "utils/bit_field.h"
29 #include "utils/bit_utils.h"
30 #include "utils/bit_vector.h"
31 #include "macros.h"
32 #include "mem/arena_allocator.h"
33 #include "opcodes.h"
34 #include "compiler_options.h"
35 #include "runtime_interface.h"
36 #include "spill_fill_data.h"
37
38 namespace panda::compiler {
39 class Inst;
40 class BasicBlock;
41 class Graph;
42 class GraphVisitor;
43 class VnObject;
44 class SaveStateItem;
45 class LocationsInfo;
46 using InstVector = ArenaVector<Inst *>;
47
48 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
49 #define INST_DEF(opcode, base, ...) class base;
50 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
51 OPCODE_LIST(INST_DEF)
52 #undef INST_DEF
53
54 /*
55 * Condition code, used in Compare, If[Imm] and Select[Imm] instructions.
56 *
57 * N.B. BranchElimination and Peephole rely on the order of these codes. Change carefully.
58 */
59 enum ConditionCode {
60 // All types.
61 CC_EQ = 0, // ==
62 CC_NE, // !=
63 // Signed integers and floating-point numbers.
64 CC_LT, // <
65 CC_LE, // <=
66 CC_GT, // >
67 CC_GE, // >=
68 // Unsigned integers.
69 CC_B, // <
70 CC_BE, // <=
71 CC_A, // >
72 CC_AE, // >=
73 // Compare result of bitwise AND with zero
74 CC_TST_EQ, // (lhs AND rhs) == 0
75 CC_TST_NE, // (lhs AND rhs) != 0
76 // First and last aliases.
77 CC_FIRST = CC_EQ,
78 CC_LAST = CC_TST_NE,
79 };
80
GetInverseConditionCode(ConditionCode code)81 inline ConditionCode GetInverseConditionCode(ConditionCode code)
82 {
83 switch (code) {
84 case ConditionCode::CC_EQ:
85 return ConditionCode::CC_NE;
86 case ConditionCode::CC_NE:
87 return ConditionCode::CC_EQ;
88
89 case ConditionCode::CC_LT:
90 return ConditionCode::CC_GE;
91 case ConditionCode::CC_LE:
92 return ConditionCode::CC_GT;
93 case ConditionCode::CC_GT:
94 return ConditionCode::CC_LE;
95 case ConditionCode::CC_GE:
96 return ConditionCode::CC_LT;
97
98 case ConditionCode::CC_B:
99 return ConditionCode::CC_AE;
100 case ConditionCode::CC_BE:
101 return ConditionCode::CC_A;
102 case ConditionCode::CC_A:
103 return ConditionCode::CC_BE;
104 case ConditionCode::CC_AE:
105 return ConditionCode::CC_B;
106
107 case ConditionCode::CC_TST_EQ:
108 return ConditionCode::CC_TST_NE;
109 case ConditionCode::CC_TST_NE:
110 return ConditionCode::CC_TST_EQ;
111
112 default:
113 UNREACHABLE();
114 }
115 }
116
InverseSignednessConditionCode(ConditionCode code)117 inline ConditionCode InverseSignednessConditionCode(ConditionCode code)
118 {
119 switch (code) {
120 case ConditionCode::CC_EQ:
121 return ConditionCode::CC_EQ;
122 case ConditionCode::CC_NE:
123 return ConditionCode::CC_NE;
124
125 case ConditionCode::CC_LT:
126 return ConditionCode::CC_B;
127 case ConditionCode::CC_LE:
128 return ConditionCode::CC_BE;
129 case ConditionCode::CC_GT:
130 return ConditionCode::CC_A;
131 case ConditionCode::CC_GE:
132 return ConditionCode::CC_AE;
133
134 case ConditionCode::CC_B:
135 return ConditionCode::CC_LT;
136 case ConditionCode::CC_BE:
137 return ConditionCode::CC_LE;
138 case ConditionCode::CC_A:
139 return ConditionCode::CC_GT;
140 case ConditionCode::CC_AE:
141 return ConditionCode::CC_GE;
142
143 case ConditionCode::CC_TST_EQ:
144 return ConditionCode::CC_TST_EQ;
145 case ConditionCode::CC_TST_NE:
146 return ConditionCode::CC_TST_NE;
147
148 default:
149 UNREACHABLE();
150 }
151 }
152
IsSignedConditionCode(ConditionCode code)153 inline bool IsSignedConditionCode(ConditionCode code)
154 {
155 switch (code) {
156 case ConditionCode::CC_LT:
157 case ConditionCode::CC_LE:
158 case ConditionCode::CC_GT:
159 case ConditionCode::CC_GE:
160 return true;
161
162 case ConditionCode::CC_EQ:
163 case ConditionCode::CC_NE:
164 case ConditionCode::CC_B:
165 case ConditionCode::CC_BE:
166 case ConditionCode::CC_A:
167 case ConditionCode::CC_AE:
168 case ConditionCode::CC_TST_EQ:
169 case ConditionCode::CC_TST_NE:
170 return false;
171
172 default:
173 UNREACHABLE();
174 }
175 }
176
SwapOperandsConditionCode(ConditionCode code)177 inline ConditionCode SwapOperandsConditionCode(ConditionCode code)
178 {
179 switch (code) {
180 case ConditionCode::CC_EQ:
181 case ConditionCode::CC_NE:
182 return code;
183
184 case ConditionCode::CC_LT:
185 return ConditionCode::CC_GT;
186 case ConditionCode::CC_LE:
187 return ConditionCode::CC_GE;
188 case ConditionCode::CC_GT:
189 return ConditionCode::CC_LT;
190 case ConditionCode::CC_GE:
191 return ConditionCode::CC_LE;
192
193 case ConditionCode::CC_B:
194 return ConditionCode::CC_A;
195 case ConditionCode::CC_BE:
196 return ConditionCode::CC_AE;
197 case ConditionCode::CC_A:
198 return ConditionCode::CC_B;
199 case ConditionCode::CC_AE:
200 return ConditionCode::CC_BE;
201
202 case ConditionCode::CC_TST_EQ:
203 case ConditionCode::CC_TST_NE:
204 return code;
205
206 default:
207 UNREACHABLE();
208 }
209 }
210
211 enum class Opcode {
212 INVALID = -1,
213 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
214 #define INST_DEF(opcode, ...) opcode,
215 OPCODE_LIST(INST_DEF)
216
217 #undef INST_DEF
218 NUM_OPCODES
219 };
220
221 /**
222 * Convert opcode to its string representation
223 */
224 constexpr std::array<const char *const, static_cast<size_t>(Opcode::NUM_OPCODES)> OPCODE_NAMES = {
225 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
226 #define INST_DEF(opcode, ...) #opcode,
227 OPCODE_LIST(INST_DEF)
228 #undef INST_DEF
229 };
230
GetOpcodeString(Opcode opc)231 constexpr const char *GetOpcodeString(Opcode opc)
232 {
233 ASSERT(static_cast<int>(opc) < static_cast<int>(Opcode::NUM_OPCODES));
234 return OPCODE_NAMES[static_cast<int>(opc)];
235 }
236
237 /**
238 * Instruction flags. See `instrutions.yaml` section `flags` for more information.
239 */
240 namespace inst_flags {
241 namespace internal {
242 enum FlagsIndex {
243 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
244 #define FLAG_DEF(flag) flag##_INDEX,
245 FLAGS_LIST(FLAG_DEF)
246 #undef FLAG_DEF
247 FLAGS_COUNT
248 };
249 } // namespace internal
250
251 enum Flags : uint32_t {
252 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
253 #define FLAG_DEF(flag) flag = (1U << internal::flag##_INDEX),
254 FLAGS_LIST(FLAG_DEF)
255 #undef FLAG_DEF
256 FLAGS_COUNT = internal::FLAGS_COUNT,
257 NONE = 0
258 };
259
GetFlagsMask(Opcode opcode)260 inline constexpr uintptr_t GetFlagsMask(Opcode opcode)
261 {
262 #define INST_DEF(OPCODE, BASE, FLAGS) FLAGS, // NOLINT(cppcoreguidelines-macro-usage)
263 // NOLINTNEXTLINE(hicpp-signed-bitwise)
264 constexpr std::array<uintptr_t, static_cast<int>(Opcode::NUM_OPCODES)> INST_FLAGS_TABLE = {OPCODE_LIST(INST_DEF)};
265 #undef INST_DEF
266 return INST_FLAGS_TABLE[static_cast<size_t>(opcode)];
267 }
268 } // namespace inst_flags
269
270 #ifndef NDEBUG
271 namespace inst_modes {
272 namespace internal {
273 enum ModeIndex {
274 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
275 #define MODE_DEF(mode) mode##_INDEX,
276 MODES_LIST(MODE_DEF)
277 #undef MODE_DEF
278 MODES_COUNT
279 };
280 } // namespace internal
281
282 enum Mode : uint8_t {
283 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
284 #define MODE_DEF(mode) mode = (1U << internal::mode##_INDEX),
285 MODES_LIST(MODE_DEF)
286 #undef MODE_DEF
287 MODES_COUNT = internal::MODES_COUNT,
288 };
289
GetModesMask(Opcode opcode)290 inline constexpr uint8_t GetModesMask(Opcode opcode)
291 {
292 // NOLINTNEXTLINE(hicpp-signed-bitwise)
293 constexpr std::array<uint8_t, static_cast<int>(Opcode::NUM_OPCODES)> INST_MODES_TABLE = {INST_MODES_LIST};
294 return INST_MODES_TABLE[static_cast<size_t>(opcode)];
295 }
296 } // namespace inst_modes
297 #endif
298
299 namespace internal {
300 inline constexpr std::array<const char *, ShiftType::INVALID_SHIFT + 1> SHIFT_TYPE_NAMES = {"LSL", "LSR", "ASR", "ROR",
301 "INVALID"};
302 } // namespace internal
303
GetShiftTypeStr(ShiftType type)304 inline const char *GetShiftTypeStr(ShiftType type)
305 {
306 ASSERT(type <= INVALID_SHIFT);
307 return internal::SHIFT_TYPE_NAMES[type];
308 }
309
310 /**
311 * Describes type of the object produced by an instruction.
312 */
313 class ObjectTypeInfo {
314 public:
315 using ClassType = RuntimeInterface::ClassPtr;
316
317 ObjectTypeInfo() = default;
ObjectTypeInfo(ClassType v)318 explicit ObjectTypeInfo(ClassType v) : class_(v) {}
319
320 // NOLINTNEXTLINE(google-explicit-constructor)
321 operator bool() const
322 {
323 return class_ != ClassType();
324 }
325
GetClass()326 ClassType GetClass() const
327 {
328 return class_;
329 }
330
IsValid()331 bool IsValid() const
332 {
333 return class_ != ClassType {};
334 }
335
336 private:
337 ClassType class_ {};
338 };
339
340 /**
341 * Class for storing panda bytecode's virtual register
342 */
343 class VirtualRegister final {
344 public:
345 using ValueType = uint16_t;
346 static constexpr unsigned BITS_FOR_VREG = (sizeof(ValueType) * BITS_PER_BYTE) - 1;
347 static constexpr ValueType INVALID = std::numeric_limits<ValueType>::max();
348
349 VirtualRegister() = default;
VirtualRegister(uint16_t v,bool is_acc)350 explicit VirtualRegister(uint16_t v, bool is_acc) : value_(v)
351 {
352 IsAccFlag::Set(is_acc, &value_);
353 }
354
uint16_t()355 explicit operator uint16_t() const
356 {
357 return value_;
358 }
359
Value()360 uint16_t Value() const
361 {
362 return ValueField::Get(value_);
363 }
364
IsAccumulator()365 bool IsAccumulator() const
366 {
367 return IsAccFlag::Get(value_);
368 }
369
370 private:
371 uint16_t value_ {INVALID};
372
373 using ValueField = BitField<unsigned, 0, BITS_FOR_VREG>;
374 using IsAccFlag = ValueField::NextFlag;
375 };
376
377 // How many bits will be used in Inst's bit fields for number of inputs.
378 constexpr size_t BITS_PER_INPUTS_NUM = 3;
379 // Maximum number of static inputs
380 constexpr size_t MAX_STATIC_INPUTS = (1U << BITS_PER_INPUTS_NUM) - 1;
381
382 /**
383 * Currently Input class is just a wrapper for the Inst class.
384 */
385 class Input final {
386 public:
387 Input() = default;
Input(Inst * inst)388 explicit Input(Inst *inst) : inst_(inst) {}
389
GetInst()390 Inst *GetInst()
391 {
392 return inst_;
393 }
GetInst()394 const Inst *GetInst() const
395 {
396 return inst_;
397 }
398
GetPadding(Arch arch,uint32_t inputs_count)399 static inline uint8_t GetPadding(Arch arch, uint32_t inputs_count)
400 {
401 return static_cast<uint8_t>(!Is64BitsArch(arch) && inputs_count % 2U == 1U);
402 }
403
404 private:
405 Inst *inst_ {nullptr};
406 };
407
408 /**
409 * User is a intrusive list node, thus it stores pointers to next and previous users.
410 * Also user has properties value to determine owner instruction and corresponding index of the input.
411 */
412 class User final {
413 public:
414 User() = default;
User(bool is_static,unsigned index,unsigned size)415 User(bool is_static, unsigned index, unsigned size)
416 : properties_(IsStaticFlag::Encode(is_static) | IndexField::Encode(index) | SizeField::Encode(size) |
417 BbNumField::Encode(BbNumField::MaxValue()))
418 {
419 ASSERT(index < 1U << (BITS_FOR_INDEX - 1U));
420 ASSERT(size < 1U << (BITS_FOR_SIZE - 1U));
421 }
422 ~User() = default;
423
424 // Copy/move semantic is disabled because we use tricky pointer arithmetic based on 'this' value
425 NO_COPY_SEMANTIC(User);
426 NO_MOVE_SEMANTIC(User);
427
428 Inst *GetInst();
GetInst()429 const Inst *GetInst() const
430 {
431 return const_cast<User *>(this)->GetInst();
432 }
433
434 Inst *GetInput();
435 const Inst *GetInput() const;
436
IsDynamic()437 bool IsDynamic() const
438 {
439 return !IsStaticFlag::Decode(properties_);
440 }
GetIndex()441 unsigned GetIndex() const
442 {
443 return IndexField::Decode(properties_);
444 }
GetSize()445 unsigned GetSize() const
446 {
447 return SizeField::Decode(properties_);
448 }
449
GetVirtualRegister()450 VirtualRegister GetVirtualRegister() const
451 {
452 ASSERT(IsDynamic());
453 return VirtualRegister(VregField::Decode(properties_), IsAccFlag::Decode(properties_));
454 }
455
SetVirtualRegister(VirtualRegister reg)456 void SetVirtualRegister(VirtualRegister reg)
457 {
458 static_assert(sizeof(reg) <= sizeof(uintptr_t), "Consider passing the register by reference");
459 ASSERT(IsDynamic());
460 VregField::Set(reg.Value(), &properties_);
461 IsAccFlag::Set(reg.IsAccumulator(), &properties_);
462 }
463
GetBbNum()464 uint32_t GetBbNum() const
465 {
466 ASSERT(IsDynamic());
467 return BbNumField::Decode(properties_);
468 }
469
SetBbNum(uint32_t bb_num)470 void SetBbNum(uint32_t bb_num)
471 {
472 ASSERT(IsDynamic());
473 BbNumField::Set(bb_num, &properties_);
474 }
475
GetNext()476 auto GetNext() const
477 {
478 return next_;
479 }
480
GetPrev()481 auto GetPrev() const
482 {
483 return prev_;
484 }
485
SetNext(User * next)486 void SetNext(User *next)
487 {
488 next_ = next;
489 }
490
SetPrev(User * prev)491 void SetPrev(User *prev)
492 {
493 prev_ = prev;
494 }
495
Remove()496 void Remove()
497 {
498 if (prev_ != nullptr) {
499 prev_->next_ = next_;
500 }
501 if (next_ != nullptr) {
502 next_->prev_ = prev_;
503 }
504 }
505
506 private:
507 static constexpr unsigned BITS_FOR_INDEX = 21;
508 static constexpr unsigned BITS_FOR_SIZE = BITS_FOR_INDEX;
509 static constexpr unsigned BITS_FOR_BB_NUM = 20;
510 using IndexField = BitField<unsigned, 0, BITS_FOR_INDEX>;
511 using SizeField = IndexField::NextField<unsigned, BITS_FOR_SIZE>;
512 using IsStaticFlag = SizeField::NextFlag;
513
514 using BbNumField = IsStaticFlag::NextField<uint32_t, BITS_FOR_BB_NUM>;
515
516 using VregField = IsStaticFlag::NextField<unsigned, VirtualRegister::BITS_FOR_VREG>;
517 using IsAccFlag = VregField::NextFlag;
518
519 uint64_t properties_ {0};
520 User *next_ {nullptr};
521 User *prev_ {nullptr};
522 };
523
524 /**
525 * List of users. Intended for range loop.
526 * @tparam T should be User or const User
527 */
528 template <typename T>
529 class UserList {
530 template <typename U>
531 struct UserIterator {
532 UserIterator() = default;
UserIteratorUserIterator533 explicit UserIterator(U *u) : user_(u) {}
534
535 UserIterator &operator++()
536 {
537 user_ = user_->GetNext();
538 return *this;
539 }
540 bool operator!=(const UserIterator &other)
541 {
542 return user_ != other.user_;
543 }
544 U &operator*()
545 {
546 return *user_;
547 }
548 U *operator->()
549 {
550 return user_;
551 }
552
553 private:
554 U *user_ {nullptr};
555 };
556
557 public:
558 using Iterator = UserIterator<T>;
559 using ConstIterator = UserIterator<const T>;
560 using PointerType = std::conditional_t<std::is_const_v<T>, T *const *, T **>;
561
UserList(PointerType head)562 explicit UserList(PointerType head) : head_(head) {}
563
564 // NOLINTNEXTLINE(readability-identifier-naming)
begin()565 Iterator begin()
566 {
567 return Iterator(*head_);
568 }
569 // NOLINTNEXTLINE(readability-identifier-naming)
end()570 Iterator end()
571 {
572 return Iterator(nullptr);
573 }
574 // NOLINTNEXTLINE(readability-identifier-naming)
begin()575 ConstIterator begin() const
576 {
577 return ConstIterator(*head_);
578 }
579 // NOLINTNEXTLINE(readability-identifier-naming)
end()580 ConstIterator end() const
581 {
582 return ConstIterator(nullptr);
583 }
Empty()584 bool Empty() const
585 {
586 return *head_ == nullptr;
587 }
Front()588 T &Front()
589 {
590 return **head_;
591 }
Front()592 const T &Front() const
593 {
594 return **head_;
595 }
596
597 private:
598 PointerType head_ {nullptr};
599 };
600
601 inline bool operator==(const User &lhs, const User &rhs)
602 {
603 return lhs.GetInst() == rhs.GetInst();
604 }
605
606 /**
607 * Operands class for instructions with fixed inputs count.
608 * Actually, this class do absolutely nothing except that we can get sizeof of it when allocating memory.
609 */
610 template <int N>
611 struct Operands {
612 static_assert(N < MAX_STATIC_INPUTS, "Invalid inputs number");
613
614 std::array<User, N> users;
615 std::array<Input, N> inputs;
616 };
617
618 /**
619 * Specialized version for instructions with variable inputs count.
620 * Users and inputs are stored outside of this class.
621 */
622 class DynamicOperands {
623 public:
DynamicOperands(ArenaAllocator * allocator)624 explicit DynamicOperands(ArenaAllocator *allocator) : allocator_(allocator) {}
625
Users()626 User *Users()
627 {
628 return users_;
629 }
630
Inputs()631 Input *Inputs()
632 {
633 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
634 return reinterpret_cast<Input *>(users_ + capacity_) + 1;
635 }
636
637 /// Append new input (and user accordingly)
638 unsigned Append(Inst *inst);
639
640 /// Remove input and user with index `index`.
641 void Remove(unsigned index);
642
643 /// Reallocate inputs/users storage to a new one with specified capacity.
644 void Reallocate(size_t new_capacity = 0);
645
646 /// Get instruction to which these operands belongs to.
GetOwnerInst()647 Inst *GetOwnerInst() const
648 {
649 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
650 return reinterpret_cast<Inst *>(const_cast<DynamicOperands *>(this) + 1);
651 }
652
GetUser(unsigned index)653 User *GetUser(unsigned index)
654 {
655 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
656 return &users_[capacity_ - index - 1];
657 }
658
GetInput(unsigned index)659 Input *GetInput(unsigned index)
660 {
661 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
662 return &Inputs()[index];
663 }
664
SetInput(unsigned index,Input input)665 void SetInput(unsigned index, Input input)
666 {
667 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
668 Inputs()[index] = input;
669 }
670
Size()671 size_t Size() const
672 {
673 return size_;
674 }
675
676 private:
677 User *users_ {nullptr};
678 size_t size_ {0};
679 size_t capacity_ {0};
680 ArenaAllocator *allocator_ {nullptr};
681 };
682
683 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
684 #define DECLARE_INST(TYPE) void Accept(GraphVisitor *v) override
685
686 /**
687 * Base class for all instructions, should not be instantiated directly
688 */
689 class InstBase {
690 NO_COPY_SEMANTIC(InstBase);
691 NO_MOVE_SEMANTIC(InstBase);
692
693 public:
694 virtual ~InstBase() = default;
695
696 public:
697 virtual void Accept(GraphVisitor *v) = 0;
698
delete(void * unused,size_t size)699 ALWAYS_INLINE void operator delete([[maybe_unused]] void *unused, [[maybe_unused]] size_t size)
700 {
701 UNREACHABLE();
702 }
new(size_t size,void * ptr)703 ALWAYS_INLINE void *operator new([[maybe_unused]] size_t size, void *ptr) noexcept
704 {
705 return ptr;
706 }
delete(void * unused1,void * unused2)707 ALWAYS_INLINE void operator delete([[maybe_unused]] void *unused1, [[maybe_unused]] void *unused2) noexcept {}
708
709 void *operator new([[maybe_unused]] size_t size) = delete;
710
711 protected:
712 InstBase() = default;
713 };
714
715 /**
716 * Base instruction class
717 */
718 class Inst : public MarkerSet, public InstBase {
719 public:
720 DECLARE_INST(Inst);
721
722 public:
723 /**
724 * Create new instruction. All instructions must be created with this method.
725 * It allocates additional space before Inst object for def-use structures.
726 *
727 * @tparam InstType - concrete type of instruction, shall be derived from Inst
728 * @tparam Args - constructor arguments types
729 * @param allocator - allocator for memory allocating
730 * @param args - constructor arguments
731 * @return - new instruction
732 */
733 template <typename InstType, typename... Args>
734 [[nodiscard]] static InstType *New(ArenaAllocator *allocator, Args &&... args);
735
736 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
737 #define INST_DEF(opcode, base, ...) inline const base *CastTo##opcode() const;
738 OPCODE_LIST(INST_DEF)
739 #undef INST_DEF
740
741 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
742 #define INST_DEF(opcode, base, ...) inline base *CastTo##opcode();
OPCODE_LIST(INST_DEF)743 OPCODE_LIST(INST_DEF)
744 #undef INST_DEF
745
746 // Methods for instruction chaining inside basic blocks.
747 Inst *GetNext()
748 {
749 return next_;
750 }
GetNext()751 const Inst *GetNext() const
752 {
753 return next_;
754 }
GetPrev()755 Inst *GetPrev()
756 {
757 return prev_;
758 }
GetPrev()759 const Inst *GetPrev() const
760 {
761 return prev_;
762 }
SetNext(Inst * next)763 void SetNext(Inst *next)
764 {
765 next_ = next;
766 }
SetPrev(Inst * prev)767 void SetPrev(Inst *prev)
768 {
769 prev_ = prev;
770 }
771
772 // Id accessors
GetId()773 auto GetId() const
774 {
775 return id_;
776 }
SetId(int id)777 void SetId(int id)
778 {
779 id_ = id;
780 }
781
GetLinearNumber()782 auto GetLinearNumber() const
783 {
784 return linear_number_;
785 }
SetLinearNumber(LinearNumber number)786 void SetLinearNumber(LinearNumber number)
787 {
788 linear_number_ = number;
789 }
790
GetCloneNumber()791 auto GetCloneNumber() const
792 {
793 return clone_number_;
794 }
SetCloneNumber(int32_t number)795 void SetCloneNumber(int32_t number)
796 {
797 clone_number_ = number;
798 }
799
800 // Opcode accessors
GetOpcode()801 Opcode GetOpcode() const
802 {
803 return opcode_;
804 }
SetOpcode(Opcode opcode)805 void SetOpcode(Opcode opcode)
806 {
807 opcode_ = opcode;
808 SetField<FieldFlags>(inst_flags::GetFlagsMask(opcode));
809 }
GetOpcodeStr()810 const char *GetOpcodeStr() const
811 {
812 return GetOpcodeString(GetOpcode());
813 }
814
815 // Bytecode PC accessors
GetPc()816 uint32_t GetPc() const
817 {
818 return pc_;
819 }
SetPc(uint32_t pc)820 void SetPc(uint32_t pc)
821 {
822 pc_ = pc;
823 }
824
825 // Type accessors
GetType()826 DataType::Type GetType() const
827 {
828 return FieldType::Get(bit_fields_);
829 }
SetType(DataType::Type type)830 void SetType(DataType::Type type)
831 {
832 FieldType::Set(type, &bit_fields_);
833 }
HasType()834 bool HasType() const
835 {
836 return GetType() != DataType::Type::NO_TYPE;
837 }
838
839 // Parent basic block accessors
GetBasicBlock()840 BasicBlock *GetBasicBlock()
841 {
842 return bb_;
843 }
GetBasicBlock()844 const BasicBlock *GetBasicBlock() const
845 {
846 return bb_;
847 }
SetBasicBlock(BasicBlock * bb)848 void SetBasicBlock(BasicBlock *bb)
849 {
850 bb_ = bb;
851 }
852
853 // Instruction properties getters
IsControlFlow()854 bool IsControlFlow() const
855 {
856 return GetFlag(inst_flags::CF);
857 }
IsVirtualCall()858 bool IsVirtualCall() const
859 {
860 return GetOpcode() == Opcode::CallVirtual || GetOpcode() == Opcode::UnresolvedCallVirtual;
861 }
IsStaticCall()862 bool IsStaticCall() const
863 {
864 return GetOpcode() == Opcode::CallStatic || GetOpcode() == Opcode::UnresolvedCallStatic;
865 }
IsInitObject()866 bool IsInitObject() const
867 {
868 return GetOpcode() == Opcode::InitObject;
869 }
IsMultiArray()870 bool IsMultiArray() const
871 {
872 return GetOpcode() == Opcode::MultiArray;
873 }
IsDynamicCall()874 bool IsDynamicCall() const
875 {
876 return GetOpcode() == Opcode::CallDynamic;
877 }
IsIndirectCall()878 bool IsIndirectCall() const
879 {
880 return GetOpcode() == Opcode::CallIndirect;
881 }
IsIntrinsic()882 bool IsIntrinsic() const
883 {
884 /* Opcode::Builtin is left for backward compatibility, the compiler
885 * itself should never generate an instruction with such an opcode */
886 return GetOpcode() == Opcode::Intrinsic || GetOpcode() == Opcode::Builtin;
887 }
888
889 /* IsBuiltin actual meaning would be "it MAY be inlined by the CG"
890 * however, since we do not make guarantees about whether it will
891 * actually be inlined nor the safety of the intrinsic itself, just
892 * checking the instruction flags to see if it is suitable for any
893 * particular optimization seems to be a better approach
894 */
IsBuiltin()895 static bool IsBuiltin()
896 {
897 return false;
898 }
899
IsCall()900 bool IsCall() const
901 {
902 return GetFlag(inst_flags::CALL);
903 }
904
IsSpillFill()905 bool IsSpillFill() const
906 {
907 return GetOpcode() == Opcode::SpillFill;
908 }
909
IsNullCheck()910 bool IsNullCheck() const
911 {
912 return GetOpcode() == Opcode::NullCheck;
913 }
914
IsNullPtr()915 bool IsNullPtr() const
916 {
917 return GetOpcode() == Opcode::NullPtr;
918 }
919
IsUnresolved()920 bool IsUnresolved() const
921 {
922 switch (GetOpcode()) {
923 case Opcode::UnresolvedCallStatic:
924 case Opcode::UnresolvedCallVirtual:
925 case Opcode::UnresolvedLoadAndInitClass:
926 case Opcode::UnresolvedLoadType:
927 case Opcode::UnresolvedLoadStatic:
928 case Opcode::UnresolvedStoreStatic:
929 case Opcode::UnresolvedLoadObject:
930 case Opcode::UnresolvedStoreObject:
931 return true;
932 default:
933 return false;
934 }
935 }
IsLoad()936 bool IsLoad() const
937 {
938 return GetFlag(inst_flags::LOAD);
939 }
IsStore()940 bool IsStore() const
941 {
942 return GetFlag(inst_flags::STORE);
943 }
944 bool IsAccRead() const;
945 bool IsAccWrite() const;
IsMemory()946 bool IsMemory() const
947 {
948 return IsLoad() || IsStore();
949 }
CanThrow()950 bool CanThrow() const
951 {
952 return GetFlag(inst_flags::CAN_THROW);
953 }
IsCheck()954 bool IsCheck() const
955 {
956 return GetFlag(inst_flags::IS_CHECK);
957 }
RequireState()958 bool RequireState() const
959 {
960 return GetFlag(inst_flags::REQUIRE_STATE);
961 }
962 // Returns true if the instruction not removable in DCE
IsNotRemovable()963 bool IsNotRemovable() const
964 {
965 return GetFlag(inst_flags::NO_DCE);
966 }
967
968 // Returns true if the instruction doesn't have destination register
NoDest()969 bool NoDest() const
970 {
971 return GetFlag(inst_flags::PSEUDO_DST) || GetFlag(inst_flags::NO_DST) || GetType() == DataType::VOID;
972 }
973
HasPseudoDestination()974 bool HasPseudoDestination() const
975 {
976 return GetFlag(inst_flags::PSEUDO_DST);
977 }
978
HasImplicitRuntimeCall()979 bool HasImplicitRuntimeCall() const
980 {
981 return GetFlag(inst_flags::IMPLICIT_RUNTIME_CALL);
982 }
983
CanDeoptimize()984 bool CanDeoptimize() const
985 {
986 return GetFlag(inst_flags::CAN_DEOPTIMIZE);
987 }
988
989 // Returns true if the instruction is low-level
IsLowLevel()990 bool IsLowLevel() const
991 {
992 return GetFlag(inst_flags::LOW_LEVEL);
993 }
994
995 // Returns true if the instruction not hoistable
IsNotHoistable()996 bool IsNotHoistable() const
997 {
998 return GetFlag(inst_flags::NO_HOIST);
999 }
1000
1001 // Returns true Cse can't be applied to the instruction
IsNotCseApplicable()1002 bool IsNotCseApplicable() const
1003 {
1004 return GetFlag(inst_flags::NO_CSE);
1005 }
1006
1007 // Returns true if the instruction is a barrier
IsBarrier()1008 virtual bool IsBarrier() const
1009 {
1010 return GetFlag(inst_flags::BARRIER);
1011 }
1012
1013 // Returns true if opcode can not be moved throught runtime calls (REFERENCE type only)
IsRefSpecial()1014 bool IsRefSpecial() const
1015 {
1016 bool result = GetFlag(inst_flags::REF_SPECIAL);
1017 ASSERT(!result || GetType() == DataType::Type::REFERENCE);
1018 return result;
1019 }
1020
1021 // Returns true if the instruction is a commutative
IsCommutative()1022 bool IsCommutative() const
1023 {
1024 return GetFlag(inst_flags::COMMUTATIVE);
1025 }
1026
1027 // Returns true if the instruction allocates a new object on the heap
IsAllocation()1028 bool IsAllocation() const
1029 {
1030 return GetFlag(inst_flags::ALLOC);
1031 }
1032
1033 // Returns true if the instruction can be used in if-conversion
IsIfConvertable()1034 bool IsIfConvertable() const
1035 {
1036 return GetFlag(inst_flags::IFCVT);
1037 }
1038
IsRuntimeCall()1039 virtual bool IsRuntimeCall() const
1040 {
1041 return GetFlag(inst_flags::RUNTIME_CALL);
1042 }
1043
1044 virtual bool IsPropagateLiveness() const;
1045
1046 // Returns true if the instruction doesn't have side effects(call runtime, throw e.t.c.)
IsSafeInst()1047 virtual bool IsSafeInst() const
1048 {
1049 return false;
1050 }
1051
1052 bool RequireRegMap() const;
1053
GetObjectTypeInfo()1054 ObjectTypeInfo GetObjectTypeInfo() const
1055 {
1056 return object_type_info_;
1057 }
1058
HasObjectTypeInfo()1059 bool HasObjectTypeInfo() const
1060 {
1061 return object_type_info_.IsValid();
1062 }
1063
SetObjectTypeInfo(ObjectTypeInfo o)1064 void SetObjectTypeInfo(ObjectTypeInfo o)
1065 {
1066 object_type_info_ = o;
1067 }
1068
GetDataFlowInput(int index)1069 Inst *GetDataFlowInput(int index) const
1070 {
1071 return GetDataFlowInput(GetInput(index).GetInst());
1072 }
1073 Inst *GetDataFlowInput(Inst *input_inst) const;
1074
1075 bool IsPrecedingInSameBlock(const Inst *other) const;
1076
1077 bool IsDominate(const Inst *other) const;
1078
1079 bool InSameBlockOrDominate(const Inst *other) const;
1080
GetSaveState()1081 const SaveStateInst *GetSaveState() const
1082 {
1083 return const_cast<Inst *>(this)->GetSaveState();
1084 }
1085
GetSaveState()1086 SaveStateInst *GetSaveState()
1087 {
1088 if (!RequireState()) {
1089 return nullptr;
1090 }
1091 if (GetInputsCount() == 0) {
1092 return nullptr;
1093 }
1094 auto ss = GetInput(GetInputsCount() - 1).GetInst();
1095 if (ss->GetOpcode() == Opcode::SaveStateDeoptimize) {
1096 return ss->CastToSaveStateDeoptimize();
1097 }
1098 if (ss->GetOpcode() != Opcode::SaveState) {
1099 return nullptr;
1100 }
1101
1102 return ss->CastToSaveState();
1103 }
1104
SetSaveState(Inst * inst)1105 void SetSaveState(Inst *inst)
1106 {
1107 ASSERT(RequireState());
1108 SetInput(GetInputsCount() - 1, inst);
1109 }
1110
1111 bool IsZeroRegInst() const;
1112
1113 /**
1114 * Return instruction clone
1115 */
1116 virtual Inst *Clone(const Graph *targetGraph) const;
1117
GetFlagsMask()1118 uintptr_t GetFlagsMask() const
1119 {
1120 return GetField<FieldFlags>();
1121 }
1122
GetFlag(inst_flags::Flags flag)1123 bool GetFlag(inst_flags::Flags flag) const
1124 {
1125 return (GetFlagsMask() & flag) != 0;
1126 }
1127
SetFlag(inst_flags::Flags flag)1128 void SetFlag(inst_flags::Flags flag)
1129 {
1130 SetField<FieldFlags>(GetFlagsMask() | flag);
1131 }
1132
ClearFlag(inst_flags::Flags flag)1133 void ClearFlag(inst_flags::Flags flag)
1134 {
1135 SetField<FieldFlags>(GetFlagsMask() & ~static_cast<uintptr_t>(flag));
1136 }
1137
1138 #ifndef NDEBUG
GetModesMask()1139 uint8_t GetModesMask() const
1140 {
1141 return inst_modes::GetModesMask(opcode_);
1142 }
1143
SupportsMode(inst_modes::Mode mode)1144 bool SupportsMode(inst_modes::Mode mode) const
1145 {
1146 return (GetModesMask() & mode) != 0;
1147 }
1148 #endif
1149
SetTerminator()1150 void SetTerminator()
1151 {
1152 SetFlag(inst_flags::Flags::TERMINATOR);
1153 }
1154
1155 void InsertBefore(Inst *inst);
1156 void InsertAfter(Inst *inst);
1157
1158 /**
1159 * Return true if instruction has dynamic operands storage.
1160 */
IsOperandsDynamic()1161 bool IsOperandsDynamic() const
1162 {
1163 return GetField<InputsCount>() == MAX_STATIC_INPUTS;
1164 }
1165
1166 /**
1167 * Add user to the instruction.
1168 * @param user - pointer to User object
1169 */
AddUser(User * user)1170 void AddUser(User *user)
1171 {
1172 ASSERT(user && user->GetInst());
1173 user->SetNext(first_user_);
1174 user->SetPrev(nullptr);
1175 if (first_user_ != nullptr) {
1176 ASSERT(first_user_->GetPrev() == nullptr);
1177 first_user_->SetPrev(user);
1178 }
1179 first_user_ = user;
1180 }
1181
1182 /**
1183 * Remove instruction from users.
1184 * @param user - pointer to User object
1185 */
RemoveUser(User * user)1186 void RemoveUser(User *user)
1187 {
1188 ASSERT(user);
1189 ASSERT(HasUsers());
1190 if (user == first_user_) {
1191 first_user_ = user->GetNext();
1192 }
1193 user->Remove();
1194 }
1195
1196 /**
1197 * Set input instruction in specified index.
1198 * Old input will be removed.
1199 * @param index - index of input to be set
1200 * @param inst - new input instruction TODO sherstennikov: currently it can be nullptr, is it correct?
1201 */
SetInput(unsigned index,Inst * inst)1202 void SetInput(unsigned index, Inst *inst)
1203 {
1204 CHECK_LT(index, GetInputsCount());
1205 auto &input = GetInputs()[index];
1206 auto user = GetUser(index);
1207 if (input.GetInst() != nullptr && input.GetInst()->HasUsers()) {
1208 input.GetInst()->RemoveUser(user);
1209 }
1210 if (inst != nullptr) {
1211 inst->AddUser(user);
1212 }
1213 input = Input(inst);
1214 }
1215
1216 /**
1217 * Replace all inputs that points to specified instruction by new one.
1218 * @param old_input - instruction that should be replaced
1219 * @param new_input - new input instruction
1220 */
ReplaceInput(Inst * old_input,Inst * new_input)1221 void ReplaceInput(Inst *old_input, Inst *new_input)
1222 {
1223 unsigned index = 0;
1224 for (auto input : GetInputs()) {
1225 if (input.GetInst() == old_input) {
1226 SetInput(index, new_input);
1227 }
1228 index++;
1229 }
1230 }
1231
1232 /**
1233 * Replace inputs that point to this instruction by given instruction.
1234 * @param inst - new input instruction
1235 */
ReplaceUsers(Inst * inst)1236 void ReplaceUsers(Inst *inst)
1237 {
1238 ASSERT(inst != this);
1239 ASSERT(inst != nullptr);
1240 for (auto it = GetUsers().begin(); it != GetUsers().end(); it = GetUsers().begin()) {
1241 it->GetInst()->SetInput(it->GetIndex(), inst);
1242 }
1243 }
1244
1245 /**
1246 * Swap the operands of the instruction.
1247 * NB! Don't swap inputs while iterating over instruction's users:
1248 * for (auto user : instruction.GetUsers()) {
1249 * // Don't do this!
1250 * user.GetInst()->SwapInputs();
1251 * }
1252 */
SwapInputs()1253 void SwapInputs()
1254 {
1255 #ifndef NDEBUG
1256 constexpr auto INPUTS_COUNT_2 = 2;
1257 #endif
1258 ASSERT(GetInputsCount() == INPUTS_COUNT_2);
1259 auto input0 = GetInput(0).GetInst();
1260 auto input1 = GetInput(1).GetInst();
1261 SetInput(0, input1);
1262 SetInput(1, input0);
1263 }
1264
1265 /**
1266 * Append input instruction.
1267 * Available only for variadic inputs instructions, such as PHI.
1268 * @param input - input instruction
1269 * @return index in inputs container where new input is placed
1270 */
AppendInput(Inst * input)1271 unsigned AppendInput(Inst *input)
1272 {
1273 ASSERT(input != nullptr);
1274 ASSERT(IsOperandsDynamic());
1275 DynamicOperands *operands = GetDynamicOperands();
1276 return operands->Append(input);
1277 }
1278
AppendInput(Input input)1279 unsigned AppendInput(Input input)
1280 {
1281 static_assert(sizeof(Input) <= sizeof(uintptr_t)); // Input become larger, so pass it by reference then
1282 return AppendInput(input.GetInst());
1283 }
1284
1285 /**
1286 * Remove input from inputs container
1287 * Available only for variadic inputs instructions, such as PHI.
1288 * @param index - index of input in inputs container
1289 */
RemoveInput(unsigned index)1290 virtual void RemoveInput(unsigned index)
1291 {
1292 ASSERT(IsOperandsDynamic());
1293 DynamicOperands *operands = GetDynamicOperands();
1294 ASSERT(index < operands->Size());
1295 operands->Remove(index);
1296 }
1297
1298 /**
1299 * Remove all inputs
1300 */
RemoveInputs()1301 void RemoveInputs()
1302 {
1303 if (UNLIKELY(IsOperandsDynamic())) {
1304 for (auto inputs_count = GetInputsCount(); inputs_count != 0; --inputs_count) {
1305 RemoveInput(inputs_count - 1);
1306 }
1307 } else {
1308 for (size_t i = 0; i < GetInputsCount(); ++i) {
1309 SetInput(i, nullptr);
1310 }
1311 }
1312 }
1313
1314 /**
1315 * Remove all users
1316 */
1317 template <bool with_inputs = false>
RemoveUsers()1318 void RemoveUsers()
1319 {
1320 auto users = GetUsers();
1321 while (!users.Empty()) {
1322 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
1323 if constexpr (with_inputs) {
1324 auto &user = users.Front();
1325 user.GetInst()->RemoveInput(user.GetIndex());
1326 // NOLINTNEXTLINE(readability-misleading-indentation)
1327 } else {
1328 RemoveUser(&users.Front());
1329 }
1330 }
1331 }
1332
1333 /**
1334 * Get input by index
1335 * @param index - index of input
1336 * @return input instruction
1337 */
GetInput(unsigned index)1338 Input GetInput(unsigned index)
1339 {
1340 ASSERT(index < GetInputsCount());
1341 return GetInputs()[index];
1342 }
1343
GetInput(unsigned index)1344 Input GetInput(unsigned index) const
1345 {
1346 ASSERT(index < GetInputsCount());
1347 return GetInputs()[index];
1348 }
1349
GetInputs()1350 Span<Input> GetInputs()
1351 {
1352 if (UNLIKELY(IsOperandsDynamic())) {
1353 DynamicOperands *operands = GetDynamicOperands();
1354 return Span<Input>(operands->Inputs(), operands->Size());
1355 }
1356
1357 auto inputs_count {GetField<InputsCount>()};
1358 return Span<Input>(
1359 reinterpret_cast<Input *>(reinterpret_cast<uintptr_t>(this) -
1360 (inputs_count + Input::GetPadding(RUNTIME_ARCH, inputs_count)) * sizeof(Input)),
1361 inputs_count);
1362 }
GetInputs()1363 Span<const Input> GetInputs() const
1364 {
1365 return Span<const Input>(const_cast<Inst *>(this)->GetInputs());
1366 }
1367
GetInputType(size_t index)1368 virtual DataType::Type GetInputType([[maybe_unused]] size_t index) const
1369 {
1370 ASSERT(index < GetInputsCount());
1371 return GetType();
1372 }
1373
GetUsers()1374 UserList<User> GetUsers()
1375 {
1376 return UserList<User>(&first_user_);
1377 }
GetUsers()1378 UserList<const User> GetUsers() const
1379 {
1380 return UserList<const User>(&first_user_);
1381 }
1382
GetInputsCount()1383 size_t GetInputsCount() const
1384 {
1385 if (UNLIKELY(IsOperandsDynamic())) {
1386 return GetDynamicOperands()->Size();
1387 }
1388 return GetInputs().Size();
1389 }
1390
HasUsers()1391 bool HasUsers() const
1392 {
1393 return first_user_ != nullptr;
1394 };
1395
HasSingleUser()1396 bool HasSingleUser() const
1397 {
1398 return first_user_ != nullptr && first_user_->GetNext() == nullptr;
1399 }
1400
1401 /// Reserve space in dataflow storage for specified inputs count
1402 void ReserveInputs(size_t capacity);
1403
SetLocation(size_t index,Location location)1404 virtual void SetLocation([[maybe_unused]] size_t index, [[maybe_unused]] Location location) {}
1405
GetLocation(size_t index)1406 virtual Location GetLocation([[maybe_unused]] size_t index) const
1407 {
1408 return Location::RequireRegister();
1409 }
1410
GetDstLocation()1411 virtual Location GetDstLocation() const
1412 {
1413 return Location::MakeRegister(GetDstReg(), GetType());
1414 }
1415
CanBeNull()1416 virtual bool CanBeNull() const
1417 {
1418 ASSERT_PRINT(GetType() == DataType::Type::REFERENCE, "CanBeNull only applies to reference types");
1419 return true;
1420 }
1421
Latency()1422 virtual uint32_t Latency() const
1423 {
1424 return options.GetCompilerSchedLatency();
1425 }
1426
1427 template <typename Accessor>
GetField()1428 typename Accessor::ValueType GetField() const
1429 {
1430 return Accessor::Get(bit_fields_);
1431 }
1432
1433 template <typename Accessor>
SetField(typename Accessor::ValueType value)1434 void SetField(typename Accessor::ValueType value)
1435 {
1436 Accessor::Set(value, &bit_fields_);
1437 }
1438
GetAllFields()1439 uint64_t GetAllFields() const
1440 {
1441 return bit_fields_;
1442 }
1443
IsPhi()1444 bool IsPhi() const
1445 {
1446 return opcode_ == Opcode::Phi;
1447 }
1448
IsCatchPhi()1449 bool IsCatchPhi() const
1450 {
1451 return opcode_ == Opcode::CatchPhi;
1452 }
1453
IsConst()1454 bool IsConst() const
1455 {
1456 return opcode_ == Opcode::Constant;
1457 }
1458
IsParameter()1459 bool IsParameter() const
1460 {
1461 return opcode_ == Opcode::Parameter;
1462 }
1463
IsBoolConst()1464 virtual bool IsBoolConst() const
1465 {
1466 return false;
1467 }
1468
IsSaveState()1469 bool IsSaveState() const
1470 {
1471 return opcode_ == Opcode::SaveState || opcode_ == Opcode::SafePoint || opcode_ == Opcode::SaveStateOsr ||
1472 opcode_ == Opcode::SaveStateDeoptimize;
1473 }
1474
IsClassInst()1475 bool IsClassInst() const
1476 {
1477 return opcode_ == Opcode::InitClass || opcode_ == Opcode::LoadClass || opcode_ == Opcode::LoadAndInitClass ||
1478 opcode_ == Opcode::UnresolvedLoadAndInitClass;
1479 }
1480
GetHashCode()1481 virtual size_t GetHashCode() const
1482 {
1483 // TODO (Aleksandr Popov) calculate hash code
1484 return 0;
1485 }
1486
SetVnObject(VnObject * vn_obj)1487 virtual void SetVnObject([[maybe_unused]] VnObject *vn_obj) {}
1488
GetDstReg()1489 Register GetDstReg() const
1490 {
1491 return dst_reg_;
1492 }
1493
SetDstReg(Register reg)1494 void SetDstReg(Register reg)
1495 {
1496 dst_reg_ = reg;
1497 }
1498
GetVN()1499 uint32_t GetVN() const
1500 {
1501 return vn_;
1502 }
1503
SetVN(uint32_t vn)1504 void SetVN(uint32_t vn)
1505 {
1506 vn_ = vn;
1507 }
1508 void Dump(std::ostream *out, bool new_line = true) const;
1509 virtual bool DumpInputs(std::ostream * /* out */) const;
1510 virtual void DumpOpcode(std::ostream * /* out */) const;
1511
SetDstReg(unsigned index,Register reg)1512 virtual void SetDstReg([[maybe_unused]] unsigned index, Register reg)
1513 {
1514 ASSERT(index == 0);
1515 SetDstReg(reg);
1516 }
1517
GetDstReg(unsigned index)1518 virtual Register GetDstReg([[maybe_unused]] unsigned index) const
1519 {
1520 ASSERT(index == 0);
1521 return GetDstReg();
1522 }
1523
GetDstCount()1524 virtual size_t GetDstCount() const
1525 {
1526 return 1;
1527 }
1528
GetSrcRegIndex()1529 virtual uint32_t GetSrcRegIndex() const
1530 {
1531 return 0;
1532 }
1533
SetSrcReg(unsigned index,Register reg)1534 virtual void SetSrcReg([[maybe_unused]] unsigned index, [[maybe_unused]] Register reg) {}
1535
GetSrcReg(unsigned index)1536 virtual Register GetSrcReg([[maybe_unused]] unsigned index) const
1537 {
1538 return INVALID_REG;
1539 }
1540
GetFirstUser()1541 User *GetFirstUser() const
1542 {
1543 return first_user_;
1544 }
1545
1546 protected:
1547 using InstBase::InstBase;
1548 static constexpr int INPUT_COUNT = 0;
1549
1550 Inst() = default;
1551
Inst(Opcode opcode)1552 explicit Inst(Opcode opcode) : Inst(opcode, DataType::Type::NO_TYPE, INVALID_PC) {}
1553
Inst(Opcode opcode,DataType::Type type,uint32_t pc)1554 explicit Inst(Opcode opcode, DataType::Type type, uint32_t pc) : pc_(pc), opcode_(opcode)
1555 {
1556 bit_fields_ = inst_flags::GetFlagsMask(opcode);
1557 SetField<FieldType>(type);
1558 }
1559
1560 protected:
1561 using FieldFlags = BitField<uint32_t, 0, MinimumBitsToStore(1U << inst_flags::FLAGS_COUNT)>;
1562 using FieldType = FieldFlags::NextField<DataType::Type, MinimumBitsToStore(DataType::LAST)>;
1563 using InputsCount = FieldType::NextField<uint32_t, BITS_PER_INPUTS_NUM>;
1564 using LastField = InputsCount;
1565
GetDynamicOperands()1566 DynamicOperands *GetDynamicOperands() const
1567 {
1568 return reinterpret_cast<DynamicOperands *>(reinterpret_cast<uintptr_t>(this) - sizeof(DynamicOperands));
1569 }
1570
1571 private:
GetUser(unsigned index)1572 User *GetUser(unsigned index)
1573 {
1574 if (UNLIKELY(IsOperandsDynamic())) {
1575 return GetDynamicOperands()->GetUser(index);
1576 }
1577 auto inputs_count {GetField<InputsCount>()};
1578 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1579 return reinterpret_cast<User *>(reinterpret_cast<Input *>(this) -
1580 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1581 (inputs_count + Input::GetPadding(RUNTIME_ARCH, inputs_count))) -
1582 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1583 index - 1;
1584 }
1585
OperandsStorageSize()1586 size_t OperandsStorageSize() const
1587 {
1588 if (UNLIKELY(IsOperandsDynamic())) {
1589 return sizeof(DynamicOperands);
1590 }
1591
1592 auto inputs_count {GetField<InputsCount>()};
1593 return inputs_count * (sizeof(Input) + sizeof(User)) +
1594 Input::GetPadding(RUNTIME_ARCH, inputs_count) * sizeof(Input);
1595 }
1596
1597 private:
1598 /// Basic block this instruction belongs to
1599 BasicBlock *bb_ {nullptr};
1600
1601 /// Next instruction within basic block
1602 Inst *next_ {nullptr};
1603
1604 /// Previous instruction within basic block
1605 Inst *prev_ {nullptr};
1606
1607 /// First user in users chain
1608 User *first_user_ {nullptr};
1609
1610 /// This value hold properties of the instruction. It accessed via BitField types(f.e. FieldType).
1611 uint64_t bit_fields_ {0};
1612
1613 /// Unique id of instruction
1614 uint32_t id_ {INVALID_ID};
1615
1616 /// Unique id of instruction
1617 uint32_t vn_ {INVALID_VN};
1618
1619 /// Bytecode pc
1620 uint32_t pc_ {INVALID_PC};
1621
1622 /// Number used in cloning
1623 uint32_t clone_number_ {0};
1624
1625 /// Instruction number getting while visiting graph
1626 LinearNumber linear_number_ {INVALID_LINEAR_NUM};
1627
1628 ObjectTypeInfo object_type_info_ {};
1629
1630 /// Opcode, see opcodes.def
1631 Opcode opcode_ {Opcode::INVALID};
1632
1633 // Destination register type - defined in FieldType
1634 Register dst_reg_ {INVALID_REG};
1635 };
1636
1637 /**
1638 * Proxy class that injects new field - type of the source operands - into property field of the instruction.
1639 * Should be used when instruction has sources of the same type and type of the instruction is not match to type of
1640 * sources. Examples: Cmp, Compare
1641 * @tparam T Base instruction class after which this mixin is injected
1642 */
1643 template <typename T>
1644 class InstWithOperandsType : public T {
1645 public:
1646 using T::T;
1647
SetOperandsType(DataType::Type type)1648 void SetOperandsType(DataType::Type type)
1649 {
1650 T::template SetField<FieldOperandsType>(type);
1651 }
GetOperandsType()1652 virtual DataType::Type GetOperandsType() const
1653 {
1654 return T::template GetField<FieldOperandsType>();
1655 }
1656
1657 protected:
1658 using FieldOperandsType =
1659 typename T::LastField::template NextField<DataType::Type, MinimumBitsToStore(DataType::LAST)>;
1660 using LastField = FieldOperandsType;
1661 };
1662
1663 /**
1664 * Mixin for NeedBarrier flag.
1665 * @tparam T Base instruction class after which this mixin is injected
1666 */
1667 template <typename T>
1668 class NeedBarrierMixin : public T {
1669 public:
1670 using T::T;
1671
SetNeedBarrier(bool v)1672 void SetNeedBarrier(bool v)
1673 {
1674 T::template SetField<NeedBarrierFlag>(v);
1675 }
GetNeedBarrier()1676 bool GetNeedBarrier() const
1677 {
1678 return T::template GetField<NeedBarrierFlag>();
1679 }
1680
1681 protected:
1682 using NeedBarrierFlag = typename T::LastField::NextFlag;
1683 using LastField = NeedBarrierFlag;
1684 };
1685
1686 /**
1687 * This mixin aims to implement type id accessors.
1688 */
1689 class TypeIdMixin {
1690 public:
1691 TypeIdMixin() = default;
1692 NO_COPY_SEMANTIC(TypeIdMixin);
1693 NO_MOVE_SEMANTIC(TypeIdMixin);
1694 virtual ~TypeIdMixin() = default;
1695
SetTypeId(uint32_t id)1696 void SetTypeId(uint32_t id)
1697 {
1698 type_id_ = id;
1699 }
1700
GetTypeId()1701 auto GetTypeId() const
1702 {
1703 return type_id_;
1704 }
1705
SetMethod(RuntimeInterface::MethodPtr method)1706 void SetMethod(RuntimeInterface::MethodPtr method)
1707 {
1708 method_ = method;
1709 }
GetMethod()1710 auto GetMethod() const
1711 {
1712 return method_;
1713 }
1714
1715 private:
1716 uint32_t type_id_ {0};
1717 // The pointer to the method in which this instruction is executed(inlined method)
1718 RuntimeInterface::MethodPtr method_ {nullptr};
1719 };
1720
1721 /**
1722 * This mixin aims to implement type of klass.
1723 */
1724 template <typename T>
1725 class ClassTypeMixin : public T {
1726 public:
1727 using T::T;
1728
SetClassType(ClassType class_type)1729 void SetClassType(ClassType class_type)
1730 {
1731 T::template SetField<ClassTypeField>(class_type);
1732 }
1733
GetClassType()1734 ClassType GetClassType() const
1735 {
1736 return T::template GetField<ClassTypeField>();
1737 }
1738
1739 protected:
1740 using ClassTypeField = typename T::LastField::template NextField<ClassType, MinimumBitsToStore(ClassType::COUNT)>;
1741 using LastField = ClassTypeField;
1742 };
1743
1744 /**
1745 * Mixin to check if null check inside CheckCast and IsInstance can be omitted.
1746 */
1747 template <typename T>
1748 class OmitNullCheckMixin : public T {
1749 public:
1750 using T::T;
1751
SetOmitNullCheck(bool omit_null_check)1752 void SetOmitNullCheck(bool omit_null_check)
1753 {
1754 T::template SetField<OmitNullCheckFlag>(omit_null_check);
1755 }
1756
GetOmitNullCheck()1757 bool GetOmitNullCheck() const
1758 {
1759 return T::template GetField<OmitNullCheckFlag>();
1760 }
1761
1762 protected:
1763 using OmitNullCheckFlag = typename T::LastField::NextFlag;
1764 using LastField = OmitNullCheckFlag;
1765 };
1766
1767 template <typename T>
1768 class ScaleMixin : public T {
1769 public:
1770 using T::T;
1771
SetScale(uint32_t scale)1772 void SetScale(uint32_t scale)
1773 {
1774 ASSERT(scale <= MAX_SCALE);
1775 T::template SetField<ScaleField>(scale);
1776 }
1777
GetScale()1778 uint32_t GetScale() const
1779 {
1780 return T::template GetField<ScaleField>();
1781 }
1782
1783 protected:
1784 using ScaleField = typename T::LastField::template NextField<uint32_t, MinimumBitsToStore(MAX_SCALE)>;
1785 using LastField = ScaleField;
1786 };
1787
1788 /**
1789 * This mixin aims to implement field accessors.
1790 */
1791 class FieldMixin {
1792 public:
1793 FieldMixin() = default;
1794 NO_COPY_SEMANTIC(FieldMixin);
1795 NO_MOVE_SEMANTIC(FieldMixin);
1796 virtual ~FieldMixin() = default;
1797
SetObjField(RuntimeInterface::FieldPtr field)1798 void SetObjField(RuntimeInterface::FieldPtr field)
1799 {
1800 field_ = field;
1801 }
GetObjField()1802 auto GetObjField() const
1803 {
1804 return field_;
1805 }
1806
1807 private:
1808 RuntimeInterface::FieldPtr field_ {nullptr};
1809 };
1810
1811 /**
1812 * This mixin aims to implement volatile accessors.
1813 */
1814 template <typename T>
1815 class VolatileMixin : public T {
1816 public:
1817 using T::T;
1818
SetVolatile(bool is_volatile)1819 void SetVolatile(bool is_volatile)
1820 {
1821 T::template SetField<IsVolatileFlag>(is_volatile);
1822 }
GetVolatile()1823 bool GetVolatile() const
1824 {
1825 return T::template GetField<IsVolatileFlag>();
1826 }
1827
1828 protected:
1829 using IsVolatileFlag = typename T::LastField::NextFlag;
1830 using LastField = IsVolatileFlag;
1831 };
1832 /**
1833 * Mixin for Inlined calls/returns.
1834 */
1835 template <typename T>
1836 class InlinedInstMixin : public T {
1837 public:
1838 using T::T;
1839
SetInlined(bool v)1840 void SetInlined(bool v)
1841 {
1842 T::template SetField<IsInlinedFlag>(v);
1843 }
IsInlined()1844 bool IsInlined() const
1845 {
1846 return T::template GetField<IsInlinedFlag>();
1847 }
1848
1849 protected:
1850 using IsInlinedFlag = typename T::LastField::NextFlag;
1851 using LastField = IsInlinedFlag;
1852 };
1853
1854 /**
1855 * Mixin for Array/String instruction
1856 */
1857 template <typename T>
1858 class ArrayInstMixin : public T {
1859 public:
1860 using T::T;
1861
SetIsArray(bool v)1862 void SetIsArray(bool v)
1863 {
1864 T::template SetField<IsStringFlag>(!v);
1865 }
1866
SetIsString(bool v)1867 void SetIsString(bool v)
1868 {
1869 T::template SetField<IsStringFlag>(v);
1870 }
1871
IsArray()1872 bool IsArray() const
1873 {
1874 return !(T::template GetField<IsStringFlag>());
1875 }
1876
IsString()1877 bool IsString() const
1878 {
1879 return T::template GetField<IsStringFlag>();
1880 }
1881
1882 protected:
1883 using IsStringFlag = typename T::LastField::NextFlag;
1884 using LastField = IsStringFlag;
1885 };
1886
1887 /**
1888 * Mixin for instructions with immediate constant value
1889 */
1890 class ImmediateMixin {
1891 public:
ImmediateMixin(uint64_t immediate)1892 explicit ImmediateMixin(uint64_t immediate) : immediate_(immediate) {}
1893
1894 NO_COPY_SEMANTIC(ImmediateMixin);
1895 NO_MOVE_SEMANTIC(ImmediateMixin);
1896 virtual ~ImmediateMixin() = default;
1897
SetImm(uint64_t immediate)1898 void SetImm(uint64_t immediate)
1899 {
1900 immediate_ = immediate;
1901 }
GetImm()1902 auto GetImm() const
1903 {
1904 return immediate_;
1905 }
1906
1907 protected:
1908 ImmediateMixin() = default;
1909
1910 private:
1911 uint64_t immediate_ {0};
1912 };
1913
1914 /**
1915 * Mixin for instructions with ConditionCode
1916 */
1917 template <typename T>
1918 class ConditionMixin : public T {
1919 public:
1920 enum class Prediction { NONE, LIKELY, UNLIKELY, SIZE = UNLIKELY };
1921
1922 using T::T;
ConditionMixin(ConditionCode cc)1923 explicit ConditionMixin(ConditionCode cc)
1924 {
1925 T::template SetField<CcFlag>(cc);
1926 }
1927 NO_COPY_SEMANTIC(ConditionMixin);
1928 NO_MOVE_SEMANTIC(ConditionMixin);
1929 ~ConditionMixin() override = default;
1930
GetCc()1931 auto GetCc() const
1932 {
1933 return T::template GetField<CcFlag>();
1934 }
SetCc(ConditionCode cc)1935 void SetCc(ConditionCode cc)
1936 {
1937 T::template SetField<CcFlag>(cc);
1938 }
InverseConditionCode()1939 void InverseConditionCode()
1940 {
1941 SetCc(GetInverseConditionCode(GetCc()));
1942 if (IsLikely()) {
1943 SetUnlikely();
1944 } else if (IsUnlikely()) {
1945 SetLikely();
1946 }
1947 }
1948
IsLikely()1949 bool IsLikely() const
1950 {
1951 return T::template GetField<PredictionFlag>() == Prediction::LIKELY;
1952 }
IsUnlikely()1953 bool IsUnlikely() const
1954 {
1955 return T::template GetField<PredictionFlag>() == Prediction::UNLIKELY;
1956 }
SetLikely()1957 void SetLikely()
1958 {
1959 T::template SetField<PredictionFlag>(Prediction::LIKELY);
1960 }
SetUnlikely()1961 void SetUnlikely()
1962 {
1963 T::template SetField<PredictionFlag>(Prediction::UNLIKELY);
1964 }
1965
1966 protected:
1967 ConditionMixin() = default;
1968
1969 using CcFlag = typename T::LastField::template NextField<ConditionCode, MinimumBitsToStore(ConditionCode::CC_LAST)>;
1970 using PredictionFlag = typename CcFlag::template NextField<Prediction, MinimumBitsToStore(Prediction::SIZE)>;
1971 using LastField = PredictionFlag;
1972 };
1973
1974 /**
1975 * Mixin for instrucion with ShiftType
1976 */
1977 class ShiftTypeMixin {
1978 public:
ShiftTypeMixin(ShiftType shift_type)1979 explicit ShiftTypeMixin(ShiftType shift_type) : shift_type_(shift_type) {}
1980 NO_COPY_SEMANTIC(ShiftTypeMixin);
1981 NO_MOVE_SEMANTIC(ShiftTypeMixin);
1982 virtual ~ShiftTypeMixin() = default;
1983
SetShiftType(ShiftType shift_type)1984 void SetShiftType(ShiftType shift_type)
1985 {
1986 shift_type_ = shift_type;
1987 }
1988
GetShiftType()1989 ShiftType GetShiftType() const
1990 {
1991 return shift_type_;
1992 }
1993
1994 protected:
1995 ShiftTypeMixin() = default;
1996
1997 private:
1998 ShiftType shift_type_ {INVALID_SHIFT};
1999 };
2000
2001 /**
2002 * Mixin for instructions with multiple return values
2003 */
2004 template <typename T, size_t N>
2005 class MultipleOutputMixin : public T {
2006 public:
2007 using T::T;
2008
GetDstReg(unsigned index)2009 Register GetDstReg(unsigned index) const override
2010 {
2011 ASSERT(index < N);
2012 if (index == 0) {
2013 return T::GetDstReg();
2014 }
2015 return dst_regs_[index - 1];
2016 }
2017
SetDstReg(unsigned index,Register reg)2018 void SetDstReg(unsigned index, Register reg) override
2019 {
2020 ASSERT(index < N);
2021 if (index == 0) {
2022 T::SetDstReg(reg);
2023 } else {
2024 dst_regs_[index - 1] = reg;
2025 }
2026 }
2027
GetDstCount()2028 size_t GetDstCount() const override
2029 {
2030 return N;
2031 }
2032
2033 private:
2034 std::array<Register, N - 1> dst_regs_;
2035 };
2036
2037 /**
2038 * Mixin for Deoptimize instructions
2039 */
2040 template <typename T>
2041 class DeoptimizeTypeMixin : public T {
2042 public:
2043 using T::T;
2044
SetDeoptimizeType(DeoptimizeType deopt_type)2045 void SetDeoptimizeType(DeoptimizeType deopt_type)
2046 {
2047 T::template SetField<DeoptimizeTypeField>(deopt_type);
2048 }
2049
GetDeoptimizeType()2050 DeoptimizeType GetDeoptimizeType() const
2051 {
2052 return T::template GetField<DeoptimizeTypeField>();
2053 }
2054
2055 protected:
2056 using DeoptimizeTypeField =
2057 typename T::LastField::template NextField<DeoptimizeType, MinimumBitsToStore(DeoptimizeType::COUNT)>;
2058 using LastField = DeoptimizeTypeField;
2059 };
2060
2061 /**
2062 * Instruction with fixed number of inputs.
2063 * Shall not be instantiated directly, only through derived classes.
2064 */
2065 template <size_t N>
2066 class FixedInputsInst : public Inst {
2067 public:
2068 using Inst::Inst;
2069
2070 static constexpr int INPUT_COUNT = N;
2071
SetSrcReg(unsigned index,Register reg)2072 void SetSrcReg(unsigned index, Register reg) override
2073 {
2074 ASSERT(index < N);
2075 src_regs_[index] = reg;
2076 }
2077
GetSrcReg(unsigned index)2078 Register GetSrcReg(unsigned index) const override
2079 {
2080 ASSERT(index < N);
2081 return src_regs_[index];
2082 }
2083
GetLocation(size_t index)2084 Location GetLocation(size_t index) const override
2085 {
2086 return Location::MakeRegister(GetSrcReg(index), GetInputType(index));
2087 }
2088
SetLocation(size_t index,Location location)2089 void SetLocation(size_t index, Location location) override
2090 {
2091 SetSrcReg(index, location.GetValue());
2092 }
2093
SetDstLocation(Location location)2094 void SetDstLocation(Location location)
2095 {
2096 SetDstReg(location.GetValue());
2097 }
2098
2099 Inst *Clone(const Graph *targetGraph) const override;
2100
2101 private:
2102 template <typename T, std::size_t... Is>
CreateArray(T value,std::index_sequence<Is...> unused)2103 constexpr auto CreateArray(T value, [[maybe_unused]] std::index_sequence<Is...> unused)
2104 {
2105 return std::array<T, sizeof...(Is)> {(static_cast<void>(Is), value)...};
2106 }
2107
2108 private:
2109 std::array<Register, N> src_regs_ = CreateArray(INVALID_REG, std::make_index_sequence<INPUT_COUNT>());
2110 };
2111
2112 /**
2113 * Instructions with fixed static inputs
2114 * We need to explicitly declare these proxy classes because some code can't work with the templated inst classes, for
2115 * example DEFINE_INST macro.
2116 */
2117 class FixedInputsInst0 : public FixedInputsInst<0> {
2118 public:
2119 DECLARE_INST(FixedInputsInst0);
2120 using FixedInputsInst::FixedInputsInst;
2121
2122 NO_COPY_SEMANTIC(FixedInputsInst0);
2123 NO_MOVE_SEMANTIC(FixedInputsInst0);
2124 ~FixedInputsInst0() override = default;
2125 };
2126
2127 class FixedInputsInst1 : public FixedInputsInst<1> {
2128 public:
2129 DECLARE_INST(FixedInputsInst1);
2130 using FixedInputsInst::FixedInputsInst;
2131
2132 NO_COPY_SEMANTIC(FixedInputsInst1);
2133 NO_MOVE_SEMANTIC(FixedInputsInst1);
2134 ~FixedInputsInst1() override = default;
2135 };
2136
2137 class FixedInputsInst2 : public FixedInputsInst<2U> {
2138 public:
2139 DECLARE_INST(FixedInputsInst2);
2140 using FixedInputsInst::FixedInputsInst;
2141
2142 NO_COPY_SEMANTIC(FixedInputsInst2);
2143 NO_MOVE_SEMANTIC(FixedInputsInst2);
2144 ~FixedInputsInst2() override = default;
2145 };
2146
2147 class FixedInputsInst3 : public FixedInputsInst<3U> {
2148 public:
2149 DECLARE_INST(FixedInputsInst3);
2150 using FixedInputsInst::FixedInputsInst;
2151 };
2152
2153 class FixedInputsInst4 : public FixedInputsInst<4U> {
2154 public:
2155 DECLARE_INST(FixedInputsInst4);
2156 using FixedInputsInst::FixedInputsInst;
2157 };
2158
2159 /**
2160 * Instruction with variable inputs count
2161 */
2162 class DynamicInputsInst : public Inst {
2163 public:
2164 DECLARE_INST(DynamicInputsInst);
2165 using Inst::Inst;
2166
2167 static constexpr int INPUT_COUNT = MAX_STATIC_INPUTS;
2168
GetLocation(size_t index)2169 Location GetLocation(size_t index) const override
2170 {
2171 if (locations_ == nullptr) {
2172 return Location::Invalid();
2173 }
2174 return locations_->GetLocation(index);
2175 }
2176
GetDstLocation()2177 Location GetDstLocation() const override
2178 {
2179 if (locations_ == nullptr) {
2180 return Location::Invalid();
2181 }
2182 return locations_->GetDstLocation();
2183 }
2184
SetLocation(size_t index,Location location)2185 void SetLocation(size_t index, Location location) override
2186 {
2187 ASSERT(locations_ != nullptr);
2188 locations_->SetLocation(index, location);
2189 }
2190
SetDstLocation(Location location)2191 void SetDstLocation(Location location)
2192 {
2193 ASSERT(locations_ != nullptr);
2194 locations_->SetDstLocation(location);
2195 }
2196
SetLocationsInfo(LocationsInfo * info)2197 void SetLocationsInfo(LocationsInfo *info)
2198 {
2199 locations_ = info;
2200 }
2201
GetSrcReg(unsigned index)2202 Register GetSrcReg(unsigned index) const override
2203 {
2204 return GetLocation(index).GetValue();
2205 }
2206
SetSrcReg(unsigned index,Register reg)2207 void SetSrcReg(unsigned index, Register reg) override
2208 {
2209 SetLocation(index, Location::MakeRegister(reg, GetInputType(index)));
2210 }
2211
2212 private:
2213 LocationsInfo *locations_ {nullptr};
2214 };
2215
2216 /**
2217 * Unary operation instruction
2218 */
2219 class UnaryOperation : public FixedInputsInst<1> {
2220 public:
2221 DECLARE_INST(UnaryOperation);
2222 using FixedInputsInst::FixedInputsInst;
UnaryOperation(Opcode opcode,DataType::Type type,uint32_t pc,Inst * input)2223 UnaryOperation(Opcode opcode, DataType::Type type, uint32_t pc, Inst *input) : FixedInputsInst(opcode, type, pc)
2224 {
2225 SetInput(0, input);
2226 }
2227
GetInputType(size_t index)2228 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2229 {
2230 ASSERT(index < GetInputsCount());
2231 if (GetOpcode() == Opcode::Cast) {
2232 return GetInput(0).GetInst()->GetType();
2233 }
2234 return GetType();
2235 }
2236
IsSafeInst()2237 bool IsSafeInst() const override
2238 {
2239 return true;
2240 }
2241
2242 void SetVnObject(VnObject *vn_obj) override;
2243
2244 Inst *Evaluate();
2245 };
2246
2247 /**
2248 * Binary operation instruction
2249 */
2250 class BinaryOperation : public FixedInputsInst<2U> {
2251 public:
2252 DECLARE_INST(BinaryOperation);
2253 using FixedInputsInst::FixedInputsInst;
2254
Latency()2255 uint32_t Latency() const override
2256 {
2257 if (GetOpcode() == Opcode::Div) {
2258 return options.GetCompilerSchedLatencyLong();
2259 }
2260 return options.GetCompilerSchedLatency();
2261 }
2262
IsSafeInst()2263 bool IsSafeInst() const override
2264 {
2265 return true;
2266 }
2267
2268 Inst *Evaluate();
2269 };
2270
2271 /**
2272 * Binary operation instruction with c immidiate
2273 */
2274 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2275 class BinaryImmOperation : public FixedInputsInst<1>, public ImmediateMixin {
2276 public:
2277 DECLARE_INST(BinaryImmOperation);
2278 using FixedInputsInst::FixedInputsInst;
2279
BinaryImmOperation(Opcode opcode,uint64_t imm)2280 explicit BinaryImmOperation(Opcode opcode, uint64_t imm) : FixedInputsInst(opcode), ImmediateMixin(imm) {}
BinaryImmOperation(Opcode opcode,DataType::Type type,uint32_t pc,uint64_t imm)2281 explicit BinaryImmOperation(Opcode opcode, DataType::Type type, uint32_t pc, uint64_t imm)
2282 : FixedInputsInst(opcode, type, pc), ImmediateMixin(imm)
2283 {
2284 }
2285
GetInputType(size_t index)2286 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2287 {
2288 ASSERT(index < GetInputsCount());
2289 return GetType();
2290 }
2291
2292 void SetVnObject(VnObject *vn_obj) override;
2293 bool DumpInputs(std::ostream * /* out */) const override;
2294
IsSafeInst()2295 bool IsSafeInst() const override
2296 {
2297 return true;
2298 }
2299
Clone(const Graph * targetGraph)2300 Inst *Clone(const Graph *targetGraph) const override
2301 {
2302 auto clone = FixedInputsInst::Clone(targetGraph);
2303 static_cast<BinaryImmOperation *>(clone)->SetImm(GetImm());
2304 return clone;
2305 }
2306 };
2307
2308 /**
2309 * Unary operation that shifts its own operand prior the application.
2310 */
2311 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2312 class UnaryShiftedRegisterOperation : public FixedInputsInst<1>, public ImmediateMixin, public ShiftTypeMixin {
2313 public:
2314 DECLARE_INST(UnaryShiftedRegisterOperation);
2315 using FixedInputsInst::FixedInputsInst;
2316
UnaryShiftedRegisterOperation(Opcode opcode,ShiftType shift_type,uint64_t imm)2317 explicit UnaryShiftedRegisterOperation(Opcode opcode, ShiftType shift_type, uint64_t imm)
2318 : FixedInputsInst(opcode), ImmediateMixin(imm), ShiftTypeMixin(shift_type)
2319 {
2320 }
UnaryShiftedRegisterOperation(Opcode opcode,DataType::Type type,uint32_t pc,ShiftType shift_type,uint64_t imm)2321 explicit UnaryShiftedRegisterOperation(Opcode opcode, DataType::Type type, uint32_t pc, ShiftType shift_type,
2322 uint64_t imm)
2323 : FixedInputsInst(opcode, type, pc), ImmediateMixin(imm), ShiftTypeMixin(shift_type)
2324 {
2325 }
2326
GetInputType(size_t index)2327 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2328 {
2329 ASSERT(index < GetInputsCount());
2330 return GetType();
2331 }
2332
2333 void SetVnObject(VnObject *vn_obj) override;
2334 bool DumpInputs(std::ostream * /* out */) const override;
2335 Inst *Clone(const Graph *targetGraph) const override;
2336 };
2337
2338 /**
2339 * Binary operation that shifts its second operand prior the application.
2340 */
2341 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2342 class BinaryShiftedRegisterOperation : public FixedInputsInst<2U>, public ImmediateMixin, public ShiftTypeMixin {
2343 public:
2344 DECLARE_INST(BinaryShiftedRegisterOperation);
2345 using FixedInputsInst::FixedInputsInst;
2346
BinaryShiftedRegisterOperation(Opcode opcode,ShiftType shift_type,uint64_t imm)2347 explicit BinaryShiftedRegisterOperation(Opcode opcode, ShiftType shift_type, uint64_t imm)
2348 : FixedInputsInst(opcode), ImmediateMixin(imm), ShiftTypeMixin(shift_type)
2349 {
2350 }
BinaryShiftedRegisterOperation(Opcode opcode,DataType::Type type,uint32_t pc,ShiftType shift_type,uint64_t imm)2351 explicit BinaryShiftedRegisterOperation(Opcode opcode, DataType::Type type, uint32_t pc, ShiftType shift_type,
2352 uint64_t imm)
2353 : FixedInputsInst(opcode, type, pc), ImmediateMixin(imm), ShiftTypeMixin(shift_type)
2354 {
2355 }
2356
GetInputType(size_t index)2357 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2358 {
2359 ASSERT(index < GetInputsCount());
2360 return GetType();
2361 }
2362
2363 void SetVnObject(VnObject *vn_obj) override;
2364 bool DumpInputs(std::ostream * /* out */) const override;
2365 Inst *Clone(const Graph *targetGraph) const override;
2366 };
2367
2368 class SpillFillInst;
2369
2370 /**
2371 * Mixin to hold location data
2372 */
2373 class LocationDataMixin {
2374 public:
SetLocationData(SpillFillData location_data)2375 void SetLocationData(SpillFillData location_data)
2376 {
2377 location_data_ = location_data;
2378 }
2379
GetLocationData()2380 auto GetLocationData() const
2381 {
2382 return location_data_;
2383 }
2384
GetLocationData()2385 auto &GetLocationData()
2386 {
2387 return location_data_;
2388 }
2389
2390 protected:
2391 LocationDataMixin() = default;
2392 NO_COPY_SEMANTIC(LocationDataMixin);
2393 NO_MOVE_SEMANTIC(LocationDataMixin);
2394 virtual ~LocationDataMixin() = default;
2395
2396 private:
2397 SpillFillData location_data_ {};
2398 };
2399
2400 /**
2401 * Mixin to hold input types of call instruction
2402 */
2403 class InputTypesMixin {
2404 public:
2405 InputTypesMixin() = default;
2406 NO_COPY_SEMANTIC(InputTypesMixin);
2407 NO_MOVE_SEMANTIC(InputTypesMixin);
2408 virtual ~InputTypesMixin() = default;
2409
AllocateInputTypes(ArenaAllocator * allocator,size_t capacity)2410 void AllocateInputTypes(ArenaAllocator *allocator, size_t capacity)
2411 {
2412 ASSERT(allocator != nullptr);
2413 ASSERT(input_types_ == nullptr);
2414 input_types_ = allocator->New<ArenaVector<DataType::Type>>(allocator->Adapter());
2415 ASSERT(input_types_ != nullptr);
2416 input_types_->reserve(capacity);
2417 ASSERT(input_types_->capacity() >= capacity);
2418 }
AddInputType(DataType::Type type)2419 void AddInputType(DataType::Type type)
2420 {
2421 ASSERT(input_types_ != nullptr);
2422 input_types_->push_back(type);
2423 }
GetInputTypes()2424 ArenaVector<DataType::Type> *GetInputTypes()
2425 {
2426 return input_types_;
2427 }
CloneTypes(ArenaAllocator * allocator,InputTypesMixin * target_inst)2428 void CloneTypes(ArenaAllocator *allocator, InputTypesMixin *target_inst) const
2429 {
2430 if (UNLIKELY(input_types_ == nullptr)) {
2431 return;
2432 }
2433 target_inst->AllocateInputTypes(allocator, input_types_->size());
2434 for (auto input_type : *input_types_) {
2435 target_inst->AddInputType(input_type);
2436 }
2437 }
2438
2439 protected:
2440 // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
2441 ArenaVector<DataType::Type> *input_types_ {nullptr};
2442 };
2443
2444 /**
2445 * Call instruction
2446 */
2447 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2448 class CallInst : public InlinedInstMixin<DynamicInputsInst>, public InputTypesMixin {
2449 static constexpr uint32_t INVALID_METHOD_ID = std::numeric_limits<uint32_t>::max();
2450
2451 public:
2452 DECLARE_INST(CallInst);
2453 using Base = InlinedInstMixin<DynamicInputsInst>;
2454 using Base::Base;
2455
CallInst(Opcode opcode,DataType::Type type,uint32_t pc,uint32_t method_id)2456 CallInst(Opcode opcode, DataType::Type type, uint32_t pc, uint32_t method_id)
2457 : Base(opcode, type, pc), method_id_(method_id)
2458 {
2459 }
2460
SetCallMethodId(uint32_t id)2461 void SetCallMethodId(uint32_t id)
2462 {
2463 method_id_ = id;
2464 }
GetCallMethodId()2465 auto GetCallMethodId() const
2466 {
2467 return method_id_;
2468 }
SetCallMethod(RuntimeInterface::MethodPtr method)2469 void SetCallMethod(RuntimeInterface::MethodPtr method)
2470 {
2471 method_ = method;
2472 }
GetCallMethod()2473 RuntimeInterface::MethodPtr GetCallMethod() const
2474 {
2475 return method_;
2476 }
2477
GetInputType(size_t index)2478 DataType::Type GetInputType(size_t index) const override
2479 {
2480 ASSERT(input_types_ != nullptr);
2481 ASSERT(index < input_types_->size());
2482 ASSERT(index < GetInputsCount());
2483 return (*input_types_)[index];
2484 }
2485
2486 void DumpOpcode(std::ostream *out) const override;
2487
SetCanNativeException(bool is_native)2488 void SetCanNativeException(bool is_native)
2489 {
2490 SetField<IsNativeExceptionFlag>(is_native);
2491 }
2492
GetCanNativeException()2493 bool GetCanNativeException() const
2494 {
2495 return GetField<IsNativeExceptionFlag>();
2496 }
2497
2498 Inst *Clone(const Graph *targetGraph) const override;
2499
IsRuntimeCall()2500 bool IsRuntimeCall() const override
2501 {
2502 return !IsInlined();
2503 }
2504
2505 protected:
2506 using IsNativeExceptionFlag = LastField::NextFlag;
2507 using LastField = IsNativeExceptionFlag;
2508
2509 private:
2510 uint32_t method_id_ {INVALID_METHOD_ID};
2511 RuntimeInterface::MethodPtr method_ {nullptr};
2512 };
2513
2514 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2515 class CallIndirectInst : public DynamicInputsInst, public InputTypesMixin {
2516 public:
2517 DECLARE_INST(CallIndirectInst);
2518 using Base = DynamicInputsInst;
2519 using Base::Base;
2520
CallIndirectInst(Opcode opcode,DataType::Type type,uint32_t pc)2521 CallIndirectInst(Opcode opcode, DataType::Type type, uint32_t pc) : Base(opcode, type, pc) {}
2522
GetInputType(size_t index)2523 DataType::Type GetInputType(size_t index) const override
2524 {
2525 ASSERT(input_types_ != nullptr);
2526 ASSERT(index < input_types_->size());
2527 ASSERT(index < GetInputsCount());
2528 return (*input_types_)[index];
2529 }
2530
2531 Inst *Clone(const Graph *target_graph) const override;
2532 };
2533
2534 /**
2535 * Length methods instruction
2536 */
2537 class LengthMethodInst : public ArrayInstMixin<FixedInputsInst1> {
2538 public:
2539 DECLARE_INST(LengthMethodInst);
2540 using Base = ArrayInstMixin<FixedInputsInst1>;
2541 using Base::Base;
2542
Base(opcode)2543 explicit LengthMethodInst(Opcode opcode, bool is_array = true) : Base(opcode)
2544 {
2545 SetIsArray(is_array);
2546 }
Base(opcode,type,pc)2547 LengthMethodInst(Opcode opcode, DataType::Type type, uint32_t pc, bool is_array = true) : Base(opcode, type, pc)
2548 {
2549 SetIsArray(is_array);
2550 }
2551
GetInputType(size_t index)2552 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2553 {
2554 ASSERT(index < GetInputsCount());
2555 return DataType::REFERENCE;
2556 }
2557
Clone(const Graph * targetGraph)2558 Inst *Clone(const Graph *targetGraph) const override
2559 {
2560 auto clone = FixedInputsInst::Clone(targetGraph);
2561 static_cast<LengthMethodInst *>(clone)->SetIsArray(IsArray());
2562 return clone;
2563 }
2564 };
2565
2566 /**
2567 * Compare instruction
2568 */
2569 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2570 class CompareInst : public InstWithOperandsType<ConditionMixin<FixedInputsInst2>> {
2571 public:
2572 DECLARE_INST(CompareInst);
2573 using BaseInst = InstWithOperandsType<ConditionMixin<FixedInputsInst2>>;
2574 using BaseInst::BaseInst;
2575
CompareInst(Opcode opcode,DataType::Type type,uint32_t pc,ConditionCode cc)2576 CompareInst(Opcode opcode, DataType::Type type, uint32_t pc, ConditionCode cc) : BaseInst(opcode, type, pc)
2577 {
2578 SetCc(cc);
2579 }
2580
GetInputType(size_t index)2581 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2582 {
2583 ASSERT(index < GetInputsCount());
2584 return GetOperandsType();
2585 }
2586 void DumpOpcode(std::ostream * /* unused */) const override;
2587
2588 void SetVnObject(VnObject *vn_obj) override;
2589
Clone(const Graph * targetGraph)2590 Inst *Clone(const Graph *targetGraph) const override
2591 {
2592 auto clone = FixedInputsInst::Clone(targetGraph);
2593 clone->CastToCompare()->SetCc(GetCc());
2594 clone->CastToCompare()->SetOperandsType(GetOperandsType());
2595 return clone;
2596 }
2597 };
2598
2599 /**
2600 * Mixin for AnyTypeMixin instructions
2601 */
2602 template <typename T>
2603 class AnyTypeMixin : public T {
2604 public:
2605 using T::T;
2606
SetAnyType(AnyBaseType any_type)2607 void SetAnyType(AnyBaseType any_type)
2608 {
2609 T::template SetField<AnyBaseTypeField>(any_type);
2610 }
2611
GetAnyType()2612 AnyBaseType GetAnyType() const
2613 {
2614 return T::template GetField<AnyBaseTypeField>();
2615 }
2616
2617 protected:
2618 using AnyBaseTypeField =
2619 typename T::LastField::template NextField<AnyBaseType, MinimumBitsToStore(AnyBaseType::COUNT)>;
2620 using LastField = AnyBaseTypeField;
2621 };
2622
2623 /**
2624 * CompareAnyTypeInst instruction
2625 */
2626 class CompareAnyTypeInst : public AnyTypeMixin<FixedInputsInst1> {
2627 public:
2628 DECLARE_INST(CompareAnyTypeInst);
2629 using BaseInst = AnyTypeMixin<FixedInputsInst1>;
2630 using BaseInst::BaseInst;
2631
CompareAnyTypeInst(Opcode opcode,uint32_t pc,AnyBaseType any_type)2632 CompareAnyTypeInst(Opcode opcode, uint32_t pc, AnyBaseType any_type) : BaseInst(opcode, DataType::Type::BOOL, pc)
2633 {
2634 SetAnyType(any_type);
2635 }
2636
GetInputType(size_t index)2637 DataType::Type GetInputType(size_t index) const override
2638 {
2639 ASSERT(index < GetInputsCount());
2640 return GetInput(index).GetInst()->GetType();
2641 }
2642
2643 void DumpOpcode(std::ostream *out) const override;
2644
Clone(const Graph * targetGraph)2645 Inst *Clone(const Graph *targetGraph) const override
2646 {
2647 auto clone = FixedInputsInst::Clone(targetGraph);
2648 clone->CastToCompareAnyType()->SetAnyType(GetAnyType());
2649 return clone;
2650 }
2651 };
2652
2653 /**
2654 * CastAnyTypeValueInst instruction
2655 */
2656 class CastAnyTypeValueInst : public AnyTypeMixin<FixedInputsInst1> {
2657 public:
2658 DECLARE_INST(CastAnyTypeValueInst);
2659 using BaseInst = AnyTypeMixin<FixedInputsInst1>;
2660 using BaseInst::BaseInst;
2661
CastAnyTypeValueInst(Opcode opcode,uint32_t pc,AnyBaseType any_type)2662 CastAnyTypeValueInst(Opcode opcode, uint32_t pc, AnyBaseType any_type)
2663 : BaseInst(opcode, AnyBaseTypeToDataType(any_type), pc)
2664 {
2665 SetAnyType(any_type);
2666 }
2667
GetInputType(size_t index)2668 DataType::Type GetInputType(size_t index) const override
2669 {
2670 ASSERT(index < GetInputsCount());
2671 return GetInput(index).GetInst()->GetType();
2672 }
2673
GetDeducedType()2674 DataType::Type GetDeducedType() const
2675 {
2676 return AnyBaseTypeToDataType(GetAnyType());
2677 }
2678
2679 void DumpOpcode(std::ostream *out) const override;
2680
Clone(const Graph * targetGraph)2681 Inst *Clone(const Graph *targetGraph) const override
2682 {
2683 auto clone = FixedInputsInst::Clone(targetGraph)->CastToCastAnyTypeValue();
2684 AnyBaseType any_type = GetAnyType();
2685 clone->SetAnyType(any_type);
2686 clone->SetType(GetType());
2687 return clone;
2688 }
2689 };
2690
2691 /**
2692 * CastValueToAnyTypeInst instruction
2693 */
2694 class CastValueToAnyTypeInst : public AnyTypeMixin<FixedInputsInst1> {
2695 public:
2696 DECLARE_INST(CastValueToAnyTypeInst);
2697 using BaseInst = AnyTypeMixin<FixedInputsInst1>;
2698 using BaseInst::BaseInst;
2699
CastValueToAnyTypeInst(Opcode opcode,uint32_t pc)2700 CastValueToAnyTypeInst(Opcode opcode, uint32_t pc) : BaseInst(opcode, DataType::ANY, pc) {}
2701
GetInputType(size_t index)2702 DataType::Type GetInputType(size_t index) const override
2703 {
2704 ASSERT(index < GetInputsCount());
2705 return GetInput(index).GetInst()->GetType();
2706 }
2707
2708 void DumpOpcode(std::ostream *out) const override;
2709
Clone(const Graph * targetGraph)2710 Inst *Clone(const Graph *targetGraph) const override
2711 {
2712 auto clone = FixedInputsInst::Clone(targetGraph)->CastToCastValueToAnyType();
2713 auto any_type = GetAnyType();
2714 clone->SetAnyType(any_type);
2715 clone->SetType(GetType());
2716 return clone;
2717 }
2718 };
2719
2720 /**
2721 * AnyTypeCheckInst instruction
2722 */
2723 class AnyTypeCheckInst : public AnyTypeMixin<FixedInputsInst2> {
2724 public:
2725 DECLARE_INST(AnyTypeCheckInst);
2726 using BaseInst = AnyTypeMixin<FixedInputsInst2>;
2727 using BaseInst::BaseInst;
2728
AnyTypeCheckInst(Opcode opcode,uint32_t pc,AnyBaseType any_type)2729 AnyTypeCheckInst(Opcode opcode, uint32_t pc, AnyBaseType any_type) : BaseInst(opcode, DataType::ANY, pc)
2730 {
2731 SetAnyType(any_type);
2732 }
2733
GetInputType(size_t index)2734 DataType::Type GetInputType(size_t index) const override
2735 {
2736 ASSERT(index < GetInputsCount());
2737 return (index == 0) ? DataType::ANY : DataType::NO_TYPE;
2738 }
2739
2740 void DumpOpcode(std::ostream *out) const override;
2741
Clone(const Graph * targetGraph)2742 Inst *Clone(const Graph *targetGraph) const override
2743 {
2744 auto clone = FixedInputsInst::Clone(targetGraph);
2745 clone->CastToAnyTypeCheck()->SetAnyType(GetAnyType());
2746 return clone;
2747 }
2748 };
2749
2750 /**
2751 * ConstantInst represent constant value.
2752 *
2753 * Available types: INT64, FLOAT32, FLOAT64, ANY. All integer types are stored as INT64 value.
2754 * Once type of constant is set, it can't be changed anymore.
2755 */
2756 class ConstantInst : public Inst {
2757 public:
2758 DECLARE_INST(ConstantInst);
2759 using Inst::Inst;
2760
2761 template <typename T>
Inst(Opcode::Constant)2762 explicit ConstantInst(Opcode /* unused */, T value, bool support_int32 = false) : Inst(Opcode::Constant)
2763 {
2764 ASSERT(GetTypeFromCType<T>() != DataType::NO_TYPE);
2765 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-branch-clone)
2766 if constexpr (GetTypeFromCType<T>() == DataType::FLOAT64) {
2767 value_ = bit_cast<uint64_t, double>(value);
2768 // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2769 } else if constexpr (GetTypeFromCType<T>() == DataType::FLOAT32) {
2770 value_ = bit_cast<uint32_t, float>(value);
2771 // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2772 } else if constexpr (GetTypeFromCType<T>() == DataType::ANY) {
2773 value_ = value.Raw();
2774 // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2775 } else if (GetTypeFromCType<T>(support_int32) == DataType::INT32) {
2776 value_ = static_cast<int32_t>(value);
2777 // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2778 } else {
2779 value_ = value;
2780 }
2781
2782 SetType(GetTypeFromCType<T>(support_int32));
2783 }
2784
IsSafeInst()2785 bool IsSafeInst() const override
2786 {
2787 return true;
2788 }
2789
GetRawValue()2790 uint64_t GetRawValue() const
2791 {
2792 return value_;
2793 }
2794
GetInt32Value()2795 uint32_t GetInt32Value() const
2796 {
2797 ASSERT(GetType() == DataType::INT32);
2798 return static_cast<uint32_t>(value_);
2799 }
2800
GetInt64Value()2801 uint64_t GetInt64Value() const
2802 {
2803 ASSERT(GetType() == DataType::INT64);
2804 return value_;
2805 }
2806
GetIntValue()2807 uint64_t GetIntValue() const
2808 {
2809 ASSERT(GetType() == DataType::INT64 || GetType() == DataType::INT32);
2810 return value_;
2811 }
2812
GetFloatValue()2813 float GetFloatValue() const
2814 {
2815 ASSERT(GetType() == DataType::FLOAT32);
2816 return bit_cast<float, uint32_t>(static_cast<uint32_t>(value_));
2817 }
2818
GetDoubleValue()2819 double GetDoubleValue() const
2820 {
2821 ASSERT(GetType() == DataType::FLOAT64);
2822 return bit_cast<double, uint64_t>(value_);
2823 }
2824
GetNextConst()2825 ConstantInst *GetNextConst()
2826 {
2827 return next_const_;
2828 }
SetNextConst(ConstantInst * next_const)2829 void SetNextConst(ConstantInst *next_const)
2830 {
2831 next_const_ = next_const;
2832 }
2833
2834 template <typename T>
2835 static constexpr DataType::Type GetTypeFromCType(bool support_int32 = false)
2836 {
2837 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-branch-clone)
2838 if constexpr (std::is_integral_v<T>) {
2839 if (support_int32 && sizeof(T) == sizeof(uint32_t)) {
2840 return DataType::INT32;
2841 }
2842 return DataType::INT64;
2843 // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2844 } else if constexpr (std::is_same_v<T, float>) {
2845 return DataType::FLOAT32;
2846 // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2847 } else if constexpr (std::is_same_v<T, double>) {
2848 return DataType::FLOAT64;
2849 // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2850 } else if constexpr (std::is_same_v<T, DataType::Any>) {
2851 return DataType::ANY;
2852 }
2853 return DataType::NO_TYPE;
2854 }
2855
2856 inline bool IsEqualConst(double value, [[maybe_unused]] bool support_int32 = false)
2857 {
2858 return IsEqualConst(DataType::FLOAT64, bit_cast<uint64_t, double>(value));
2859 }
2860 inline bool IsEqualConst(float value, [[maybe_unused]] bool support_int32 = false)
2861 {
2862 return IsEqualConst(DataType::FLOAT32, bit_cast<uint32_t, float>(value));
2863 }
2864 inline bool IsEqualConst(DataType::Any value, [[maybe_unused]] bool support_int32 = false)
2865 {
2866 return IsEqualConst(DataType::ANY, value.Raw());
2867 }
IsEqualConst(DataType::Type type,uint64_t value)2868 inline bool IsEqualConst(DataType::Type type, uint64_t value)
2869 {
2870 return GetType() == type && value_ == value;
2871 }
2872 template <typename T>
2873 inline bool IsEqualConst(T value, bool support_int32 = false)
2874 {
2875 static_assert(GetTypeFromCType<T>() == DataType::INT64);
2876 if (support_int32 && sizeof(T) == sizeof(uint32_t)) {
2877 return (GetType() == DataType::INT32 && static_cast<int32_t>(value_) == static_cast<int32_t>(value));
2878 }
2879 return (GetType() == DataType::INT64 && value_ == static_cast<uint64_t>(value));
2880 }
2881
2882 inline bool IsEqualConstAllTypes(int64_t value, bool support_int32 = false)
2883 {
2884 return IsEqualConst(value, support_int32) || IsEqualConst(static_cast<float>(value)) ||
2885 IsEqualConst(static_cast<double>(value));
2886 }
2887
IsBoolConst()2888 bool IsBoolConst() const override
2889 {
2890 ASSERT(IsConst());
2891 return GetType() == DataType::INT64 && (GetIntValue() == 0 || GetIntValue() == 1);
2892 }
2893
SetImmTableSlot(ImmTableSlot imm_slot)2894 void SetImmTableSlot(ImmTableSlot imm_slot)
2895 {
2896 imm_slot_ = imm_slot;
2897 }
2898
GetImmTableSlot()2899 auto GetImmTableSlot() const
2900 {
2901 return imm_slot_;
2902 }
2903
2904 bool DumpInputs(std::ostream * /* out */) const override;
2905
2906 Inst *Clone(const Graph *targetGraph) const override;
2907
2908 private:
2909 uint64_t value_ {0};
2910 ConstantInst *next_const_ {nullptr};
2911 ImmTableSlot imm_slot_ {INVALID_IMM_TABLE_SLOT};
2912 };
2913
2914 // Type describing the purpose of the SpillFillInst.
2915 // RegAlloc may use this information to preserve correct order of several SpillFillInst
2916 // instructions placed along each other in the graph.
2917 enum SpillFillType {
2918 UNKNOWN,
2919 INPUT_FILL,
2920 CONNECT_SPLIT_SIBLINGS,
2921 SPLIT_MOVE,
2922 };
2923
2924 class SpillFillInst : public FixedInputsInst0 {
2925 public:
2926 DECLARE_INST(SpillFillInst);
2927
SpillFillInst(ArenaAllocator * allocator,Opcode opcode)2928 explicit SpillFillInst(ArenaAllocator *allocator, Opcode opcode)
2929 : FixedInputsInst0(opcode), spill_fills_(allocator->Adapter())
2930 {
2931 }
2932
AddMove(Register src,Register dst,DataType::Type type)2933 void AddMove(Register src, Register dst, DataType::Type type)
2934 {
2935 AddSpillFill(Location::MakeRegister(src, type), Location::MakeRegister(dst, type), type);
2936 }
2937
AddSpill(Register src,StackSlot dst,DataType::Type type)2938 void AddSpill(Register src, StackSlot dst, DataType::Type type)
2939 {
2940 AddSpillFill(Location::MakeRegister(src, type), Location::MakeStackSlot(dst), type);
2941 }
2942
AddFill(StackSlot src,Register dst,DataType::Type type)2943 void AddFill(StackSlot src, Register dst, DataType::Type type)
2944 {
2945 AddSpillFill(Location::MakeStackSlot(src), Location::MakeRegister(dst, type), type);
2946 }
2947
AddMemCopy(StackSlot src,StackSlot dst,DataType::Type type)2948 void AddMemCopy(StackSlot src, StackSlot dst, DataType::Type type)
2949 {
2950 AddSpillFill(Location::MakeStackSlot(src), Location::MakeStackSlot(dst), type);
2951 }
2952
AddSpillFill(const SpillFillData & spill_fill)2953 void AddSpillFill(const SpillFillData &spill_fill)
2954 {
2955 spill_fills_.emplace_back(spill_fill);
2956 }
2957
AddSpillFill(const Location & src,const Location & dst,DataType::Type type)2958 void AddSpillFill(const Location &src, const Location &dst, DataType::Type type)
2959 {
2960 spill_fills_.emplace_back(SpillFillData {src.GetKind(), dst.GetKind(), src.GetValue(), dst.GetValue(), type});
2961 }
2962
GetSpillFills()2963 const ArenaVector<SpillFillData> &GetSpillFills() const
2964 {
2965 return spill_fills_;
2966 }
2967
GetSpillFills()2968 ArenaVector<SpillFillData> &GetSpillFills()
2969 {
2970 return spill_fills_;
2971 }
2972
GetSpillFill(size_t n)2973 const SpillFillData &GetSpillFill(size_t n) const
2974 {
2975 ASSERT(n < spill_fills_.size());
2976 return spill_fills_[n];
2977 }
2978
GetSpillFill(size_t n)2979 SpillFillData &GetSpillFill(size_t n)
2980 {
2981 ASSERT(n < spill_fills_.size());
2982 return spill_fills_[n];
2983 }
2984
RemoveSpillFill(size_t n)2985 void RemoveSpillFill(size_t n)
2986 {
2987 ASSERT(n < spill_fills_.size());
2988 spill_fills_.erase(spill_fills_.begin() + n);
2989 }
2990
2991 // Get register number, holded by n-th spill-fill
GetInputReg(size_t n)2992 Register GetInputReg(size_t n) const
2993 {
2994 ASSERT(n < spill_fills_.size());
2995 ASSERT(spill_fills_[n].SrcType() == LocationType::REGISTER);
2996 return spill_fills_[n].SrcValue();
2997 }
2998
ClearSpillFills()2999 void ClearSpillFills()
3000 {
3001 spill_fills_.clear();
3002 }
3003
GetSpillFillType()3004 SpillFillType GetSpillFillType() const
3005 {
3006 return sf_type_;
3007 }
3008
SetSpillFillType(SpillFillType type)3009 void SetSpillFillType(SpillFillType type)
3010 {
3011 sf_type_ = type;
3012 }
3013
3014 bool DumpInputs(std::ostream * /* out */) const override;
3015
3016 #ifndef NDEBUG
Clone(const Graph * targetGraph)3017 Inst *Clone(const Graph *targetGraph) const override
3018 {
3019 auto clone = FixedInputsInst::Clone(targetGraph)->CastToSpillFill();
3020 for (auto spill_fill : spill_fills_) {
3021 clone->AddSpillFill(spill_fill);
3022 }
3023 return clone;
3024 }
3025 #endif
3026
3027 private:
3028 ArenaVector<SpillFillData> spill_fills_;
3029 SpillFillType sf_type_ {UNKNOWN};
3030 };
3031
3032 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
3033 class ParameterInst : public Inst, public LocationDataMixin {
3034 public:
3035 DECLARE_INST(ParameterInst);
3036 using Inst::Inst;
3037
ParameterInst(Opcode,uint16_t arg_number)3038 explicit ParameterInst(Opcode /* unused */, uint16_t arg_number) : Inst(Opcode::Parameter), arg_number_(arg_number)
3039 {
3040 }
GetArgNumber()3041 uint16_t GetArgNumber() const
3042 {
3043 return arg_number_;
3044 }
3045
SetArgNumber(uint16_t arg_number)3046 void SetArgNumber(uint16_t arg_number)
3047 {
3048 arg_number_ = arg_number;
3049 }
3050
3051 bool DumpInputs(std::ostream * /* out */) const override;
3052
3053 Inst *Clone(const Graph *targetGraph) const override;
3054
3055 private:
3056 uint16_t arg_number_ {0};
3057 };
3058
IsZeroConstant(const Inst * inst)3059 inline bool IsZeroConstant(const Inst *inst)
3060 {
3061 return inst->IsConst() && inst->GetType() == DataType::INT64 && inst->CastToConstant()->GetIntValue() == 0;
3062 }
3063
IsZeroConstantOrNullPtr(const Inst * inst)3064 inline bool IsZeroConstantOrNullPtr(const Inst *inst)
3065 {
3066 return IsZeroConstant(inst) || inst->GetOpcode() == Opcode::NullPtr;
3067 }
3068
3069 /**
3070 * Phi instruction
3071 */
3072 class PhiInst : public AnyTypeMixin<DynamicInputsInst> {
3073 public:
3074 DECLARE_INST(PhiInst);
3075 using BaseInst = AnyTypeMixin<DynamicInputsInst>;
3076 using BaseInst::BaseInst;
3077 /// Get basic block corresponding to given input index. Returned pointer to basic block, can't be nullptr
3078 BasicBlock *GetPhiInputBb(unsigned index);
GetPhiInputBb(unsigned index)3079 const BasicBlock *GetPhiInputBb(unsigned index) const
3080 {
3081 return (const_cast<PhiInst *>(this))->GetPhiInputBb(index);
3082 }
3083
GetPhiInputBbNum(unsigned index)3084 uint32_t GetPhiInputBbNum(unsigned index) const
3085 {
3086 ASSERT(index < GetInputsCount());
3087 return GetDynamicOperands()->GetUser(index)->GetBbNum();
3088 }
3089
SetPhiInputBbNum(unsigned index,uint32_t bb_num)3090 void SetPhiInputBbNum(unsigned index, uint32_t bb_num)
3091 {
3092 ASSERT(index < GetInputsCount());
3093 GetDynamicOperands()->GetUser(index)->SetBbNum(bb_num);
3094 }
3095
Clone(const Graph * targetGraph)3096 Inst *Clone(const Graph *targetGraph) const override
3097 {
3098 auto clone = DynamicInputsInst::Clone(targetGraph);
3099 clone->CastToPhi()->SetAnyType(GetAnyType());
3100 return clone;
3101 }
3102
GetAssumedAnyType()3103 AnyBaseType GetAssumedAnyType()
3104 {
3105 return GetAnyType();
3106 }
3107
SetAssumedAnyType(AnyBaseType type)3108 void SetAssumedAnyType(AnyBaseType type)
3109 {
3110 SetAnyType(type);
3111 }
3112
3113 /// Get input instruction corresponding to the given basic block, can't be null.
3114 Inst *GetPhiInput(BasicBlock *bb);
3115 Inst *GetPhiDataflowInput(BasicBlock *bb);
3116 bool DumpInputs(std::ostream * /* out */) const override;
3117
3118 // Get index of the given block in phi inputs
3119 size_t GetPredBlockIndex(const BasicBlock *block) const;
3120
3121 protected:
3122 using FlagIsLive = LastField::NextFlag;
3123 using LastField = FlagIsLive;
3124 };
3125
3126 /**
3127 * Immediate for SavaState:
3128 * value - constant value to be stored
3129 * vreg - virtual register number
3130 */
3131 struct SaveStateImm {
3132 uint64_t value;
3133 uint16_t vreg;
3134 DataType::Type type;
3135 bool is_acc;
3136 };
3137
3138 /**
3139 * Frame state saving instruction
3140 * Aims to save pbc registers before calling something that can raise exception
3141 */
3142 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
3143 class SaveStateInst : public DynamicInputsInst {
3144 public:
3145 DECLARE_INST(SaveStateInst);
3146 using DynamicInputsInst::DynamicInputsInst;
3147
3148 bool DumpInputs(std::ostream *out) const override;
3149
SetVirtualRegister(size_t index,VirtualRegister reg)3150 void SetVirtualRegister(size_t index, VirtualRegister reg)
3151 {
3152 static_assert(sizeof(reg) <= sizeof(uintptr_t), "Consider passing the register by reference");
3153 ASSERT(index < GetInputsCount());
3154 GetDynamicOperands()->GetUser(index)->SetVirtualRegister(reg);
3155 }
3156
GetVirtualRegister(size_t index)3157 VirtualRegister GetVirtualRegister(size_t index) const
3158 {
3159 ASSERT(index < GetInputsCount());
3160 return GetDynamicOperands()->GetUser(index)->GetVirtualRegister();
3161 }
3162
Verify()3163 bool Verify() const
3164 {
3165 for (size_t i {0}; i < GetInputsCount(); ++i) {
3166 if (static_cast<uint16_t>(GetVirtualRegister(i)) == VirtualRegister::INVALID) {
3167 return false;
3168 }
3169 }
3170 return true;
3171 }
3172
RemoveNumericInputs()3173 bool RemoveNumericInputs()
3174 {
3175 size_t idx = 0;
3176 size_t inputs_count = GetInputsCount();
3177 bool removed = false;
3178 while (idx < inputs_count) {
3179 auto input_inst = GetInput(idx).GetInst();
3180 if (DataType::IsTypeNumeric(input_inst->GetType())) {
3181 RemoveInput(idx);
3182 inputs_count--;
3183 removed = true;
3184 } else {
3185 idx++;
3186 }
3187 }
3188 return removed;
3189 }
3190
GetInputType(size_t index)3191 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
3192 {
3193 ASSERT(index < GetInputsCount());
3194 return DataType::NO_TYPE;
3195 }
GetMethod()3196 auto GetMethod() const
3197 {
3198 return method_;
3199 }
SetMethod(void * method)3200 auto SetMethod(void *method)
3201 {
3202 method_ = method;
3203 }
3204
GetCallerInst()3205 auto GetCallerInst() const
3206 {
3207 return caller_inst_;
3208 }
SetCallerInst(CallInst * inst)3209 auto SetCallerInst(CallInst *inst)
3210 {
3211 caller_inst_ = inst;
3212 }
3213
3214 void AppendImmediate(uint64_t imm, uint16_t vreg, DataType::Type type, bool is_acc);
3215
GetImmediates()3216 const ArenaVector<SaveStateImm> *GetImmediates() const
3217 {
3218 return immediates_;
3219 }
3220
GetImmediate(size_t index)3221 const SaveStateImm &GetImmediate(size_t index) const
3222 {
3223 ASSERT(immediates_ != nullptr && index < immediates_->size());
3224 return (*immediates_)[index];
3225 }
3226
3227 void AllocateImmediates(ArenaAllocator *allocator, size_t size = 0);
3228
GetImmediatesCount()3229 size_t GetImmediatesCount() const
3230 {
3231 if (immediates_ == nullptr) {
3232 return 0;
3233 }
3234 return immediates_->size();
3235 }
3236
SetRootsRegMaskBit(size_t reg)3237 void SetRootsRegMaskBit(size_t reg)
3238 {
3239 ASSERT(reg < roots_regs_mask_.size());
3240 roots_regs_mask_.set(reg);
3241 }
3242
SetRootsStackMaskBit(size_t slot)3243 void SetRootsStackMaskBit(size_t slot)
3244 {
3245 if (roots_stack_mask_ != nullptr) {
3246 roots_stack_mask_->SetBit(slot);
3247 }
3248 }
3249
GetRootsStackMask()3250 ArenaBitVector *GetRootsStackMask()
3251 {
3252 return roots_stack_mask_;
3253 }
3254
GetRootsRegsMask()3255 auto &GetRootsRegsMask()
3256 {
3257 return roots_regs_mask_;
3258 }
3259
CreateRootsStackMask(ArenaAllocator * allocator)3260 void CreateRootsStackMask(ArenaAllocator *allocator)
3261 {
3262 ASSERT(roots_stack_mask_ == nullptr);
3263 roots_stack_mask_ = allocator->New<ArenaBitVector>(allocator);
3264 roots_stack_mask_->Reset();
3265 }
3266
3267 Inst *Clone(const Graph *targetGraph) const override;
3268 #ifndef NDEBUG
SetInputsWereDeleted()3269 void SetInputsWereDeleted()
3270 {
3271 SetField<FlagInputsWereDeleted>(true);
3272 }
3273
GetInputsWereDeleted()3274 bool GetInputsWereDeleted()
3275 {
3276 return GetField<FlagInputsWereDeleted>();
3277 }
3278 #endif
3279
3280 protected:
3281 #ifndef NDEBUG
3282 using FlagInputsWereDeleted = LastField::NextFlag;
3283 using LastField = FlagInputsWereDeleted;
3284 #endif
3285
3286 private:
3287 ArenaVector<SaveStateImm> *immediates_ {nullptr};
3288 void *method_ {nullptr};
3289 /// If instruction is in the inlined graph, this variable points to the inliner's call instruction.
3290 CallInst *caller_inst_ {nullptr};
3291 ArenaBitVector *roots_stack_mask_ {nullptr};
3292 std::bitset<BITS_PER_UINT32> roots_regs_mask_ {0};
3293 };
3294
3295 /**
3296 * Load value from array or string
3297 */
3298 class LoadInst : public ArrayInstMixin<NeedBarrierMixin<FixedInputsInst2>> {
3299 public:
3300 DECLARE_INST(LoadInst);
3301 using Base = ArrayInstMixin<NeedBarrierMixin<FixedInputsInst2>>;
3302 using Base::Base;
3303
Base(opcode)3304 explicit LoadInst(Opcode opcode, bool is_array = true) : Base(opcode)
3305 {
3306 SetIsArray(is_array);
3307 }
Base(opcode,type,pc)3308 LoadInst(Opcode opcode, DataType::Type type, uint32_t pc, bool is_array = true) : Base(opcode, type, pc)
3309 {
3310 SetIsArray(is_array);
3311 }
3312
GetArray()3313 Inst *GetArray()
3314 {
3315 return GetInput(0).GetInst();
3316 }
GetIndex()3317 Inst *GetIndex()
3318 {
3319 return GetInput(1).GetInst();
3320 }
3321
IsBarrier()3322 bool IsBarrier() const override
3323 {
3324 return Inst::IsBarrier() || GetNeedBarrier();
3325 }
3326
GetInputType(size_t index)3327 DataType::Type GetInputType(size_t index) const override
3328 {
3329 ASSERT(index < GetInputsCount());
3330 switch (index) {
3331 case 0:
3332 return DataType::REFERENCE;
3333 case 1:
3334 return DataType::INT32;
3335 default:
3336 return DataType::NO_TYPE;
3337 }
3338 }
3339
Latency()3340 uint32_t Latency() const override
3341 {
3342 return options.GetCompilerSchedLatencyLong();
3343 }
3344
Clone(const Graph * targetGraph)3345 Inst *Clone(const Graph *targetGraph) const override
3346 {
3347 auto clone = NeedBarrierMixin<FixedInputsInst2>::Clone(targetGraph);
3348 static_cast<LoadInst *>(clone)->SetIsArray(IsArray());
3349 return clone;
3350 }
3351 };
3352
3353 class LoadCompressedStringCharInst : public FixedInputsInst3 {
3354 public:
3355 DECLARE_INST(LoadCompressedStringCharInst);
3356 using Base = FixedInputsInst3;
3357 using Base::Base;
3358
GetArray()3359 Inst *GetArray()
3360 {
3361 return GetInput(0).GetInst();
3362 }
GetIndex()3363 Inst *GetIndex()
3364 {
3365 return GetInput(1).GetInst();
3366 }
GetLength()3367 Inst *GetLength() const
3368 {
3369 return GetInput(2U).GetInst();
3370 }
3371
GetInputType(size_t index)3372 DataType::Type GetInputType(size_t index) const override
3373 {
3374 ASSERT(index < GetInputsCount());
3375 switch (index) {
3376 case 0:
3377 return DataType::REFERENCE;
3378 case 1:
3379 return DataType::INT32;
3380 case 2U:
3381 return DataType::INT32;
3382 default:
3383 return DataType::NO_TYPE;
3384 }
3385 }
3386
Latency()3387 uint32_t Latency() const override
3388 {
3389 return options.GetCompilerSchedLatencyLong();
3390 }
3391 };
3392
3393 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
3394 class LoadCompressedStringCharInstI : public FixedInputsInst2, public ImmediateMixin {
3395 public:
3396 DECLARE_INST(LoadCompressedStringCharInstI);
3397 using Base = FixedInputsInst2;
3398 using Base::Base;
3399
GetInputType(size_t index)3400 DataType::Type GetInputType(size_t index) const override
3401 {
3402 ASSERT(index < GetInputsCount());
3403 switch (index) {
3404 case 0:
3405 return DataType::REFERENCE;
3406 case 1:
3407 return DataType::INT32;
3408 default:
3409 return DataType::NO_TYPE;
3410 }
3411 }
3412
Latency()3413 uint32_t Latency() const override
3414 {
3415 return options.GetCompilerSchedLatencyLong();
3416 }
3417 };
3418 /**
3419 * Store value into array element
3420 */
3421 class StoreInst : public NeedBarrierMixin<FixedInputsInst3> {
3422 public:
3423 DECLARE_INST(StoreInst);
3424 using Base = NeedBarrierMixin<FixedInputsInst3>;
3425 using Base::Base;
3426
IsBarrier()3427 bool IsBarrier() const override
3428 {
3429 return Inst::IsBarrier() || GetNeedBarrier();
3430 }
3431
GetArray()3432 Inst *GetArray()
3433 {
3434 return GetInput(0).GetInst();
3435 }
GetIndex()3436 Inst *GetIndex()
3437 {
3438 return GetInput(1).GetInst();
3439 }
GetStoredValue()3440 Inst *GetStoredValue()
3441 {
3442 return GetInput(2U).GetInst();
3443 }
GetInputType(size_t index)3444 DataType::Type GetInputType(size_t index) const override
3445 {
3446 ASSERT(index < GetInputsCount());
3447 switch (index) {
3448 case 0:
3449 return DataType::REFERENCE;
3450 case 1:
3451 return DataType::INT32;
3452 case 2U:
3453 return GetType();
3454 default:
3455 return DataType::NO_TYPE;
3456 }
3457 }
3458
3459 // StoreArray call barriers twice,so we need to save input register for second call
IsPropagateLiveness()3460 bool IsPropagateLiveness() const override
3461 {
3462 return GetType() == DataType::REFERENCE;
3463 }
3464 };
3465
3466 /**
3467 * Load value from array, using array index as immediate
3468 */
3469 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
3470 class LoadInstI : public VolatileMixin<ArrayInstMixin<NeedBarrierMixin<FixedInputsInst1>>>, public ImmediateMixin {
3471 public:
3472 DECLARE_INST(LoadInstI);
3473 using Base = VolatileMixin<ArrayInstMixin<NeedBarrierMixin<FixedInputsInst1>>>;
3474 using Base::Base;
3475
Base(opcode)3476 LoadInstI(Opcode opcode, uint64_t imm, bool is_array = true) : Base(opcode), ImmediateMixin(imm)
3477 {
3478 SetIsArray(is_array);
3479 }
3480 LoadInstI(Opcode opcode, DataType::Type type, uint32_t pc, uint64_t imm, bool is_array = true)
Base(opcode,type,pc)3481 : Base(opcode, type, pc), ImmediateMixin(imm)
3482 {
3483 SetIsArray(is_array);
3484 }
3485
GetArray()3486 Inst *GetArray()
3487 {
3488 return GetInput(0).GetInst();
3489 }
3490
GetInputType(size_t index)3491 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
3492 {
3493 ASSERT(index == 0);
3494 return DataType::REFERENCE;
3495 }
3496
IsBarrier()3497 bool IsBarrier() const override
3498 {
3499 return Inst::IsBarrier() || GetNeedBarrier();
3500 }
3501
3502 bool DumpInputs(std::ostream * /* out */) const override;
3503
Clone(const Graph * targetGraph)3504 Inst *Clone(const Graph *targetGraph) const override
3505 {
3506 auto clone = static_cast<LoadInstI *>(FixedInputsInst::Clone(targetGraph));
3507 clone->SetImm(GetImm());
3508 clone->SetIsArray(IsArray());
3509 clone->SetVolatile(GetVolatile());
3510 return clone;
3511 }
3512
Latency()3513 uint32_t Latency() const override
3514 {
3515 return options.GetCompilerSchedLatencyLong();
3516 }
3517 };
3518
3519 /**
3520 * Load value from pointer with offset
3521 */
3522 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
3523 class LoadMemInstI : public VolatileMixin<NeedBarrierMixin<FixedInputsInst1>>, public ImmediateMixin {
3524 public:
3525 DECLARE_INST(LoadMemInstI);
3526 using Base = VolatileMixin<NeedBarrierMixin<FixedInputsInst1>>;
3527 using Base::Base;
3528
LoadMemInstI(Opcode opcode,uint64_t imm)3529 LoadMemInstI(Opcode opcode, uint64_t imm) : Base(opcode), ImmediateMixin(imm) {}
LoadMemInstI(Opcode opcode,DataType::Type type,uint32_t pc,uint64_t imm)3530 LoadMemInstI(Opcode opcode, DataType::Type type, uint32_t pc, uint64_t imm)
3531 : Base(opcode, type, pc), ImmediateMixin(imm)
3532 {
3533 }
3534
GetPointer()3535 Inst *GetPointer()
3536 {
3537 return GetInput(0).GetInst();
3538 }
3539
GetInputType(size_t index)3540 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
3541 {
3542 ASSERT(index == 0);
3543 auto input_0_type = GetInput(0).GetInst()->GetType();
3544 ASSERT(input_0_type == DataType::POINTER || input_0_type == DataType::REFERENCE);
3545 return input_0_type;
3546 }
3547
IsBarrier()3548 bool IsBarrier() const override
3549 {
3550 return Inst::IsBarrier() || GetNeedBarrier();
3551 }
3552
3553 bool DumpInputs(std::ostream * /* out */) const override;
3554
Clone(const Graph * targetGraph)3555 Inst *Clone(const Graph *targetGraph) const override
3556 {
3557 auto clone = static_cast<LoadInstI *>(FixedInputsInst::Clone(targetGraph));
3558 clone->SetImm(GetImm());
3559 clone->SetVolatile(GetVolatile());
3560 return clone;
3561 }
3562
Latency()3563 uint32_t Latency() const override
3564 {
3565 return options.GetCompilerSchedLatencyLong();
3566 }
3567 };
3568
3569 /**
3570 * Store value into array element, using array index as immediate
3571 */
3572 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
3573 class StoreInstI : public VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>, public ImmediateMixin {
3574 public:
3575 DECLARE_INST(StoreInstI);
3576 using Base = VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>;
3577 using Base::Base;
3578
StoreInstI(Opcode opcode,uint64_t imm)3579 StoreInstI(Opcode opcode, uint64_t imm) : Base(opcode), ImmediateMixin(imm) {}
StoreInstI(Opcode opcode,DataType::Type type,uint32_t pc,uint64_t imm)3580 StoreInstI(Opcode opcode, DataType::Type type, uint32_t pc, uint64_t imm)
3581 : Base(opcode, type, pc), ImmediateMixin(imm)
3582 {
3583 }
3584
IsBarrier()3585 bool IsBarrier() const override
3586 {
3587 return Inst::IsBarrier() || GetNeedBarrier();
3588 }
3589
GetArray()3590 Inst *GetArray()
3591 {
3592 return GetInput(0).GetInst();
3593 }
GetStoredValue()3594 Inst *GetStoredValue()
3595 {
3596 return GetInput(1).GetInst();
3597 }
GetInputType(size_t index)3598 DataType::Type GetInputType(size_t index) const override
3599 {
3600 ASSERT(index < GetInputsCount());
3601 switch (index) {
3602 case 0:
3603 return DataType::REFERENCE;
3604 case 1:
3605 return GetType();
3606 default:
3607 UNREACHABLE();
3608 }
3609 }
3610
3611 bool DumpInputs(std::ostream * /* out */) const override;
3612
Clone(const Graph * targetGraph)3613 Inst *Clone(const Graph *targetGraph) const override
3614 {
3615 auto clone = static_cast<StoreInstI *>(FixedInputsInst::Clone(targetGraph));
3616 clone->SetImm(GetImm());
3617 clone->SetVolatile(GetVolatile());
3618 return clone;
3619 }
3620
3621 // StoreArrayI call barriers twice,so we need to save input register for second call
IsPropagateLiveness()3622 bool IsPropagateLiveness() const override
3623 {
3624 return GetType() == DataType::REFERENCE;
3625 }
3626 };
3627
3628 /**
3629 * Store value into pointer by offset
3630 */
3631 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
3632 class StoreMemInstI : public VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>, public ImmediateMixin {
3633 public:
3634 DECLARE_INST(StoreMemInstI);
3635 using Base = VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>;
3636 using Base::Base;
3637
StoreMemInstI(Opcode opcode,uint64_t imm)3638 StoreMemInstI(Opcode opcode, uint64_t imm) : Base(opcode), ImmediateMixin(imm) {}
StoreMemInstI(Opcode opcode,DataType::Type type,uint32_t pc,uint64_t imm)3639 StoreMemInstI(Opcode opcode, DataType::Type type, uint32_t pc, uint64_t imm)
3640 : Base(opcode, type, pc), ImmediateMixin(imm)
3641 {
3642 }
3643
IsBarrier()3644 bool IsBarrier() const override
3645 {
3646 return Inst::IsBarrier() || GetNeedBarrier();
3647 }
3648
GetPointer()3649 Inst *GetPointer()
3650 {
3651 return GetInput(0).GetInst();
3652 }
GetStoredValue()3653 Inst *GetStoredValue()
3654 {
3655 return GetInput(1).GetInst();
3656 }
GetInputType(size_t index)3657 DataType::Type GetInputType(size_t index) const override
3658 {
3659 ASSERT(index < GetInputsCount());
3660 switch (index) {
3661 case 0: {
3662 auto input_0_type = GetInput(0).GetInst()->GetType();
3663 ASSERT(input_0_type == DataType::POINTER || input_0_type == DataType::REFERENCE);
3664 return input_0_type;
3665 }
3666 case 1:
3667 return GetType();
3668 default:
3669 UNREACHABLE();
3670 }
3671 }
3672
3673 bool DumpInputs(std::ostream * /* out */) const override;
3674
Clone(const Graph * targetGraph)3675 Inst *Clone(const Graph *targetGraph) const override
3676 {
3677 auto clone = static_cast<StoreInstI *>(FixedInputsInst::Clone(targetGraph));
3678 clone->SetImm(GetImm());
3679 clone->SetVolatile(GetVolatile());
3680 return clone;
3681 }
3682 };
3683
3684 /**
3685 * Bounds check, using array index as immediate
3686 */
3687 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
3688 class BoundsCheckInstI : public ArrayInstMixin<FixedInputsInst<2U>>, public ImmediateMixin {
3689 public:
3690 DECLARE_INST(BoundsCheckInstI);
3691 using Base = ArrayInstMixin<FixedInputsInst<2U>>;
3692 using Base::Base;
3693
Base(opcode)3694 BoundsCheckInstI(Opcode opcode, uint64_t imm, bool is_array = true) : Base(opcode), ImmediateMixin(imm)
3695 {
3696 SetIsArray(is_array);
3697 }
3698
3699 BoundsCheckInstI(Opcode opcode, DataType::Type type, uint32_t pc, uint64_t imm, bool is_array = true)
Base(opcode,type,pc)3700 : Base(opcode, type, pc), ImmediateMixin(imm)
3701 {
3702 SetIsArray(is_array);
3703 }
3704
3705 bool DumpInputs(std::ostream * /* out */) const override;
3706
Clone(const Graph * targetGraph)3707 Inst *Clone(const Graph *targetGraph) const override
3708 {
3709 auto clone = FixedInputsInst::Clone(targetGraph);
3710 clone->CastToBoundsCheckI()->SetImm(GetImm());
3711 clone->CastToBoundsCheckI()->SetIsArray(IsArray());
3712 return clone;
3713 }
3714 };
3715
3716 /**
3717 * Bounds check instruction
3718 */
3719 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
3720 class BoundsCheckInst : public ArrayInstMixin<FixedInputsInst<3U>> {
3721 public:
3722 DECLARE_INST(BoundsCheckInst);
3723 using Base = ArrayInstMixin<FixedInputsInst<3U>>;
3724 using Base::Base;
3725
Base(opcode)3726 explicit BoundsCheckInst(Opcode opcode, bool is_array = true) : Base(opcode)
3727 {
3728 SetIsArray(is_array);
3729 }
3730
Base(opcode,type,pc)3731 BoundsCheckInst(Opcode opcode, DataType::Type type, uint32_t pc, bool is_array = true) : Base(opcode, type, pc)
3732 {
3733 SetIsArray(is_array);
3734 }
3735
Clone(const Graph * targetGraph)3736 Inst *Clone(const Graph *targetGraph) const override
3737 {
3738 auto clone = FixedInputsInst::Clone(targetGraph);
3739 clone->CastToBoundsCheck()->SetIsArray(IsArray());
3740 return clone;
3741 }
3742 };
3743
3744 class NullCheckInst : public FixedInputsInst2 {
3745 public:
3746 DECLARE_INST(NullCheckInst);
3747 using Base = FixedInputsInst2;
3748 using Base::Base;
3749
IsImplicit()3750 bool IsImplicit() const
3751 {
3752 return GetField<IsImplicitFlag>();
3753 }
3754
3755 void SetImplicit(bool is_implicit = true)
3756 {
3757 SetField<IsImplicitFlag>(is_implicit);
3758 }
3759
3760 private:
3761 using IsImplicitFlag = LastField::NextFlag;
3762 using LastField = IsImplicitFlag;
3763 };
3764
3765 /**
3766 * Return immediate
3767 */
3768 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
3769 class ReturnInstI : public FixedInputsInst<0>, public ImmediateMixin {
3770 public:
3771 DECLARE_INST(ReturnInstI);
3772 using FixedInputsInst::FixedInputsInst;
3773
ReturnInstI(Opcode opcode,uint64_t imm)3774 ReturnInstI(Opcode opcode, uint64_t imm) : FixedInputsInst(opcode), ImmediateMixin(imm) {}
ReturnInstI(Opcode opcode,DataType::Type type,uint32_t pc,uint64_t imm)3775 ReturnInstI(Opcode opcode, DataType::Type type, uint32_t pc, uint64_t imm)
3776 : FixedInputsInst(opcode, type, pc), ImmediateMixin(imm)
3777 {
3778 }
3779
3780 bool DumpInputs(std::ostream * /* out */) const override;
3781
Clone(const Graph * targetGraph)3782 Inst *Clone(const Graph *targetGraph) const override
3783 {
3784 auto clone = FixedInputsInst::Clone(targetGraph);
3785 clone->CastToReturnI()->SetImm(GetImm());
3786 return clone;
3787 }
3788 };
3789
3790 class ReturnInlinedInst : public FixedInputsInst<1> {
3791 public:
3792 DECLARE_INST(ReturnInlinedInst);
3793 using FixedInputsInst::FixedInputsInst;
3794
IsExtendedLiveness()3795 bool IsExtendedLiveness() const
3796 {
3797 return GetField<IsExtendedLivenessFlag>();
3798 }
3799
3800 void SetExtendedLiveness(bool is_extened_liveness = true)
3801 {
3802 SetField<IsExtendedLivenessFlag>(is_extened_liveness);
3803 }
3804
3805 private:
3806 using IsExtendedLivenessFlag = LastField::NextFlag;
3807 using LastField = IsExtendedLivenessFlag;
3808 };
3809
3810 /**
3811 * Monitor instruction
3812 */
3813 class MonitorInst : public FixedInputsInst2 {
3814 public:
3815 DECLARE_INST(MonitorInst);
3816 using Base = FixedInputsInst2;
3817 using Base::Base;
3818
IsExit()3819 bool IsExit() const
3820 {
3821 return GetField<Exit>();
3822 }
3823
IsEntry()3824 bool IsEntry() const
3825 {
3826 return !GetField<Exit>();
3827 }
3828
SetExit()3829 void SetExit()
3830 {
3831 SetField<Exit>(true);
3832 }
3833
SetEntry()3834 void SetEntry()
3835 {
3836 SetField<Exit>(false);
3837 }
3838
GetInputType(size_t index)3839 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
3840 {
3841 return DataType::REFERENCE;
3842 }
3843
3844 void DumpOpcode(std::ostream * /* unused */) const override;
3845
3846 protected:
3847 using Exit = LastField::NextFlag;
3848 using LastField = Exit;
3849 };
3850
3851 #include "intrinsics_flags.inl"
3852
3853 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
3854 class IntrinsicInst : public InlinedInstMixin<DynamicInputsInst>, public InputTypesMixin {
3855 public:
3856 DECLARE_INST(IntrinsicInst);
3857 using Base = InlinedInstMixin<DynamicInputsInst>;
3858 using Base::Base;
3859 using IntrinsicId = RuntimeInterface::IntrinsicId;
3860
IntrinsicInst(Opcode opcode,IntrinsicId intrinsic_id)3861 IntrinsicInst(Opcode opcode, IntrinsicId intrinsic_id) : Base(opcode), intrinsic_id_(intrinsic_id)
3862 {
3863 AdjustFlags(intrinsic_id, this);
3864 }
3865
IntrinsicInst(Opcode opcode,DataType::Type type,uint32_t pc,IntrinsicId intrinsic_id)3866 IntrinsicInst(Opcode opcode, DataType::Type type, uint32_t pc, IntrinsicId intrinsic_id)
3867 : Base(opcode, type, pc), intrinsic_id_(intrinsic_id)
3868 {
3869 AdjustFlags(intrinsic_id, this);
3870 }
3871
GetIntrinsicId()3872 IntrinsicId GetIntrinsicId() const
3873 {
3874 return intrinsic_id_;
3875 }
3876
SetIntrinsicId(IntrinsicId intrinsic_id)3877 void SetIntrinsicId(IntrinsicId intrinsic_id)
3878 {
3879 intrinsic_id_ = intrinsic_id;
3880 }
3881
GetInputType(size_t index)3882 DataType::Type GetInputType(size_t index) const override
3883 {
3884 ASSERT(input_types_ != nullptr);
3885 ASSERT(index < input_types_->size());
3886 ASSERT(index < GetInputsCount());
3887 return (*input_types_)[index];
3888 }
3889
GetImms()3890 const ArenaVector<uint32_t> &GetImms()
3891 {
3892 return *imms_;
3893 }
3894
GetImms()3895 const ArenaVector<uint32_t> &GetImms() const
3896 {
3897 return *imms_;
3898 }
3899
HasImms()3900 bool HasImms() const
3901 {
3902 return imms_ != nullptr;
3903 }
3904
AddImm(ArenaAllocator * allocator,uint32_t imm)3905 void AddImm(ArenaAllocator *allocator, uint32_t imm)
3906 {
3907 if (imms_ == nullptr) {
3908 imms_ = allocator->New<ArenaVector<uint32_t>>(allocator->Adapter());
3909 }
3910 imms_->push_back(imm);
3911 }
3912
3913 bool IsNativeCall() const;
3914
HasArgumentsOnStack()3915 bool HasArgumentsOnStack() const
3916 {
3917 return GetField<ArgumentsOnStack>();
3918 }
3919
SetArgumentsOnStack()3920 void SetArgumentsOnStack()
3921 {
3922 SetField<ArgumentsOnStack>(true);
3923 }
3924
3925 Inst *Clone(const Graph *targetGraph) const override;
3926
CanBeInlined()3927 bool CanBeInlined()
3928 {
3929 return IsInlined();
3930 }
3931
SetRelocate()3932 void SetRelocate()
3933 {
3934 SetField<Relocate>(true);
3935 }
3936
GetRelocate()3937 bool GetRelocate() const
3938 {
3939 return GetField<Relocate>();
3940 }
3941
3942 void DumpOpcode(std::ostream *out) const override;
3943
3944 protected:
3945 using ArgumentsOnStack = LastField::NextFlag;
3946 using Relocate = ArgumentsOnStack::NextFlag;
3947 using LastField = Relocate;
3948
3949 private:
3950 IntrinsicId intrinsic_id_ {RuntimeInterface::IntrinsicId::COUNT};
3951 ArenaVector<uint32_t> *imms_ {nullptr}; // record imms appeared in intrinsics
3952 };
3953
3954 #include <get_intrinsics_names.inl>
3955 #include <intrinsics_enum.inl>
3956 #include <can_encode_builtin.inl>
3957
3958 /**
3959 * Cast instruction
3960 */
3961 class CastInst : public InstWithOperandsType<FixedInputsInst1> {
3962 public:
3963 DECLARE_INST(CastInst);
3964 using BaseInst = InstWithOperandsType<FixedInputsInst1>;
3965 using BaseInst::BaseInst;
3966
GetInputType(size_t index)3967 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
3968 {
3969 ASSERT(index == 0);
3970 return GetOperandsType();
3971 }
3972
3973 void SetVnObject(VnObject *vn_obj) override;
3974
3975 void DumpOpcode(std::ostream * /* out */) const override;
3976
Clone(const Graph * targetGraph)3977 Inst *Clone(const Graph *targetGraph) const override
3978 {
3979 auto clone = FixedInputsInst::Clone(targetGraph);
3980 clone->CastToCast()->SetOperandsType(GetOperandsType());
3981 return clone;
3982 }
3983 };
3984
3985 /**
3986 * Cmp instruction
3987 */
3988 class CmpInst : public InstWithOperandsType<FixedInputsInst2> {
3989 public:
3990 DECLARE_INST(CmpInst);
3991 using BaseInst = InstWithOperandsType<FixedInputsInst2>;
3992 using BaseInst::BaseInst;
3993
IsFcmpg()3994 bool IsFcmpg() const
3995 {
3996 ASSERT(DataType::IsFloatType(GetOperandsType()));
3997 return GetField<Fcmpg>();
3998 }
IsFcmpl()3999 bool IsFcmpl() const
4000 {
4001 ASSERT(DataType::IsFloatType(GetOperandsType()));
4002 return !GetField<Fcmpg>();
4003 }
SetFcmpg()4004 void SetFcmpg()
4005 {
4006 ASSERT(DataType::IsFloatType(GetOperandsType()));
4007 SetField<Fcmpg>(true);
4008 }
SetFcmpg(bool v)4009 void SetFcmpg(bool v)
4010 {
4011 ASSERT(DataType::IsFloatType(GetOperandsType()));
4012 SetField<Fcmpg>(v);
4013 }
SetFcmpl()4014 void SetFcmpl()
4015 {
4016 ASSERT(DataType::IsFloatType(GetOperandsType()));
4017 SetField<Fcmpg>(false);
4018 }
SetFcmpl(bool v)4019 void SetFcmpl(bool v)
4020 {
4021 ASSERT(DataType::IsFloatType(GetOperandsType()));
4022 SetField<Fcmpg>(!v);
4023 }
4024
GetInputType(size_t index)4025 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4026 {
4027 ASSERT(index < GetInputsCount());
4028 return GetOperandsType();
4029 }
4030
4031 void SetVnObject(VnObject *vn_obj) override;
4032
4033 void DumpOpcode(std::ostream * /* out */) const override;
4034
Clone(const Graph * targetGraph)4035 Inst *Clone(const Graph *targetGraph) const override
4036 {
4037 auto clone = FixedInputsInst::Clone(targetGraph);
4038 clone->CastToCmp()->SetOperandsType(GetOperandsType());
4039 return clone;
4040 }
4041
4042 protected:
4043 using Fcmpg = LastField::NextFlag;
4044 using LastField = Fcmpg;
4045 };
4046
4047 /**
4048 * Load value from instance field
4049 */
4050 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4051 class LoadObjectInst : public VolatileMixin<NeedBarrierMixin<FixedInputsInst1>>, public TypeIdMixin, public FieldMixin {
4052 public:
4053 DECLARE_INST(LoadObjectInst);
4054 using Base = VolatileMixin<NeedBarrierMixin<FixedInputsInst1>>;
4055 using Base::Base;
4056
GetInputType(size_t index)4057 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4058 {
4059 ASSERT(index < GetInputsCount());
4060 ASSERT(GetInputsCount() == 1);
4061 return DataType::REFERENCE;
4062 }
4063
IsBarrier()4064 bool IsBarrier() const override
4065 {
4066 return Inst::IsBarrier() || GetNeedBarrier() || GetVolatile();
4067 }
4068
4069 void DumpOpcode(std::ostream *out) const override;
4070
Clone(const Graph * targetGraph)4071 Inst *Clone(const Graph *targetGraph) const override
4072 {
4073 auto clone = FixedInputsInst::Clone(targetGraph);
4074 clone->CastToLoadObject()->SetTypeId(GetTypeId());
4075 clone->CastToLoadObject()->SetMethod(GetMethod());
4076 clone->CastToLoadObject()->SetObjField(GetObjField());
4077 clone->CastToLoadObject()->SetVolatile(GetVolatile());
4078 return clone;
4079 }
4080
Latency()4081 uint32_t Latency() const override
4082 {
4083 return options.GetCompilerSchedLatencyLong();
4084 }
4085 };
4086
4087 /**
4088 * Load value from memory by offset
4089 */
4090 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4091 class LoadMemInst : public ScaleMixin<VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>> {
4092 public:
4093 DECLARE_INST(LoadMemInst);
4094 using Base = ScaleMixin<VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>>;
4095 using Base::Base;
4096
GetInputType(size_t index)4097 DataType::Type GetInputType(size_t index) const override
4098 {
4099 ASSERT(index < GetInputsCount());
4100 ASSERT(GetInputsCount() == 2U);
4101 if (index == 1U) {
4102 return DataType::UINT32;
4103 }
4104
4105 ASSERT(index == 0U);
4106 auto input_0_type = GetInput(0).GetInst()->GetType();
4107 ASSERT(input_0_type == DataType::POINTER || input_0_type == DataType::REFERENCE);
4108 return input_0_type;
4109 }
4110
IsBarrier()4111 bool IsBarrier() const override
4112 {
4113 return Inst::IsBarrier() || GetNeedBarrier() || GetVolatile();
4114 }
4115
4116 void DumpOpcode(std::ostream *out) const override;
4117 bool DumpInputs(std::ostream * /* unused */) const override;
4118
Clone(const Graph * targetGraph)4119 Inst *Clone(const Graph *targetGraph) const override
4120 {
4121 auto clone = FixedInputsInst::Clone(targetGraph);
4122 clone->CastToLoad()->SetVolatile(GetVolatile());
4123 clone->CastToLoad()->SetScale(GetScale());
4124 return clone;
4125 }
4126
Latency()4127 uint32_t Latency() const override
4128 {
4129 return options.GetCompilerSchedLatencyLong();
4130 }
4131 };
4132
4133 /**
4134 * Load value from unresolved instance field
4135 */
4136 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4137 class UnresolvedLoadObjectInst : public NeedBarrierMixin<FixedInputsInst2>, public TypeIdMixin {
4138 public:
4139 DECLARE_INST(UnresolvedLoadObjectInst);
4140 using Base = NeedBarrierMixin<FixedInputsInst2>;
4141 using Base::Base;
4142
GetInputType(size_t index)4143 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4144 {
4145 ASSERT(index < GetInputsCount());
4146 ASSERT(GetInputsCount() == 2U);
4147 if (index == 1) {
4148 // This is SaveState input
4149 return DataType::NO_TYPE;
4150 }
4151 ASSERT(index == 0);
4152 return DataType::REFERENCE;
4153 }
4154
IsBarrier()4155 bool IsBarrier() const override
4156 {
4157 return true;
4158 }
4159
4160 void DumpOpcode(std::ostream *out) const override;
4161
Clone(const Graph * targetGraph)4162 Inst *Clone(const Graph *targetGraph) const override
4163 {
4164 auto clone = FixedInputsInst::Clone(targetGraph);
4165 clone->CastToUnresolvedLoadObject()->SetTypeId(GetTypeId());
4166 clone->CastToUnresolvedLoadObject()->SetMethod(GetMethod());
4167 return clone;
4168 }
4169
Latency()4170 uint32_t Latency() const override
4171 {
4172 return options.GetCompilerSchedLatencyLong();
4173 }
4174 };
4175
4176 /**
4177 * Store value into instance field
4178 */
4179 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4180 class StoreObjectInst : public VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>,
4181 public TypeIdMixin,
4182 public FieldMixin {
4183 public:
4184 DECLARE_INST(StoreObjectInst);
4185 using Base = VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>;
4186 using Base::Base;
4187 static constexpr size_t STORED_INPUT_INDEX = 1;
4188
IsBarrier()4189 bool IsBarrier() const override
4190 {
4191 return Inst::IsBarrier() || GetNeedBarrier() || GetVolatile();
4192 }
4193
GetInputType(size_t index)4194 DataType::Type GetInputType(size_t index) const override
4195 {
4196 ASSERT(index < GetInputsCount());
4197 ASSERT(GetInputsCount() == 2U);
4198 return index == 0 ? DataType::REFERENCE : GetType();
4199 }
4200
4201 void DumpOpcode(std::ostream *out) const override;
4202
Clone(const Graph * targetGraph)4203 Inst *Clone(const Graph *targetGraph) const override
4204 {
4205 auto clone = FixedInputsInst::Clone(targetGraph);
4206 clone->CastToStoreObject()->SetTypeId(GetTypeId());
4207 clone->CastToStoreObject()->SetMethod(GetMethod());
4208 clone->CastToStoreObject()->SetObjField(GetObjField());
4209 clone->CastToStoreObject()->SetVolatile(GetVolatile());
4210 return clone;
4211 }
4212
4213 // StoreObject call barriers twice,so we need to save input register for second call
IsPropagateLiveness()4214 bool IsPropagateLiveness() const override
4215 {
4216 return GetType() == DataType::REFERENCE;
4217 }
4218 };
4219
4220 /**
4221 * Store value into unresolved instance field
4222 */
4223 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4224 class UnresolvedStoreObjectInst : public NeedBarrierMixin<FixedInputsInst3>, public TypeIdMixin {
4225 public:
4226 DECLARE_INST(UnresolvedStoreObjectInst);
4227 using Base = NeedBarrierMixin<FixedInputsInst3>;
4228 using Base::Base;
4229
IsBarrier()4230 bool IsBarrier() const override
4231 {
4232 return true;
4233 }
4234
GetInputType(size_t index)4235 DataType::Type GetInputType(size_t index) const override
4236 {
4237 ASSERT(index < GetInputsCount());
4238 ASSERT(GetInputsCount() == 3U);
4239 if (index == 2U) {
4240 // This is SaveState input
4241 return DataType::NO_TYPE;
4242 }
4243 return index == 0 ? DataType::REFERENCE : GetType();
4244 }
4245
4246 void DumpOpcode(std::ostream *out) const override;
4247
Clone(const Graph * targetGraph)4248 Inst *Clone(const Graph *targetGraph) const override
4249 {
4250 auto clone = FixedInputsInst::Clone(targetGraph);
4251 clone->CastToUnresolvedStoreObject()->SetTypeId(GetTypeId());
4252 clone->CastToUnresolvedStoreObject()->SetMethod(GetMethod());
4253 return clone;
4254 }
4255 };
4256
4257 /**
4258 * Store value in memory by offset
4259 */
4260 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4261 class StoreMemInst : public ScaleMixin<VolatileMixin<NeedBarrierMixin<FixedInputsInst3>>> {
4262 public:
4263 DECLARE_INST(StoreMemInst);
4264 using Base = ScaleMixin<VolatileMixin<NeedBarrierMixin<FixedInputsInst3>>>;
4265 using Base::Base;
4266
4267 static constexpr size_t STORED_INPUT_INDEX = 2;
4268
IsBarrier()4269 bool IsBarrier() const override
4270 {
4271 return Inst::IsBarrier() || GetNeedBarrier() || GetVolatile();
4272 }
4273
GetInputType(size_t index)4274 DataType::Type GetInputType(size_t index) const override
4275 {
4276 ASSERT(index < GetInputsCount());
4277 ASSERT(GetInputsCount() == 3U);
4278 if (index == 1U) {
4279 return DataType::UINT32;
4280 }
4281 if (index == 2U) {
4282 return GetType();
4283 }
4284
4285 ASSERT(index == 0U);
4286 auto input_0_type = GetInput(0).GetInst()->GetType();
4287 ASSERT(input_0_type == DataType::POINTER || input_0_type == DataType::REFERENCE);
4288 return input_0_type;
4289 }
4290
4291 void DumpOpcode(std::ostream *out) const override;
4292 bool DumpInputs(std::ostream * /* unused */) const override;
4293
Clone(const Graph * targetGraph)4294 Inst *Clone(const Graph *targetGraph) const override
4295 {
4296 auto clone = FixedInputsInst::Clone(targetGraph);
4297 clone->CastToStore()->SetVolatile(GetVolatile());
4298 clone->CastToStore()->SetScale(GetScale());
4299 return clone;
4300 }
4301 };
4302
4303 /**
4304 * Load static field from class.
4305 */
4306 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4307 class LoadStaticInst : public VolatileMixin<NeedBarrierMixin<FixedInputsInst1>>, public TypeIdMixin, public FieldMixin {
4308 public:
4309 DECLARE_INST(LoadStaticInst);
4310 using Base = VolatileMixin<NeedBarrierMixin<FixedInputsInst1>>;
4311 using Base::Base;
4312
IsBarrier()4313 bool IsBarrier() const override
4314 {
4315 return Inst::IsBarrier() || GetNeedBarrier() || GetVolatile();
4316 }
4317
4318 void DumpOpcode(std::ostream *out) const override;
4319
GetInputType(size_t index)4320 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4321 {
4322 ASSERT(index < GetInputsCount());
4323 ASSERT(index == 0);
4324 return DataType::REFERENCE;
4325 }
4326
Clone(const Graph * targetGraph)4327 Inst *Clone(const Graph *targetGraph) const override
4328 {
4329 auto clone = FixedInputsInst::Clone(targetGraph);
4330 clone->CastToLoadStatic()->SetTypeId(GetTypeId());
4331 clone->CastToLoadStatic()->SetMethod(GetMethod());
4332 clone->CastToLoadStatic()->SetObjField(GetObjField());
4333 clone->CastToLoadStatic()->SetVolatile(GetVolatile());
4334 return clone;
4335 }
4336
Latency()4337 uint32_t Latency() const override
4338 {
4339 return options.GetCompilerSchedLatencyLong();
4340 }
4341 };
4342
4343 /**
4344 * Load unresolved static field from class.
4345 */
4346 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4347 class UnresolvedLoadStaticInst : public NeedBarrierMixin<FixedInputsInst1>, public TypeIdMixin {
4348 public:
4349 DECLARE_INST(UnresolvedLoadStaticInst);
4350 using Base = NeedBarrierMixin<FixedInputsInst1>;
4351 using Base::Base;
4352
IsBarrier()4353 bool IsBarrier() const override
4354 {
4355 return true;
4356 }
4357
4358 void DumpOpcode(std::ostream *out) const override;
4359
GetInputType(size_t index)4360 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4361 {
4362 ASSERT(index < GetInputsCount());
4363 // This is SaveState input
4364 return DataType::NO_TYPE;
4365 }
4366
Clone(const Graph * targetGraph)4367 Inst *Clone(const Graph *targetGraph) const override
4368 {
4369 auto clone = FixedInputsInst::Clone(targetGraph);
4370 clone->CastToUnresolvedLoadStatic()->SetTypeId(GetTypeId());
4371 clone->CastToUnresolvedLoadStatic()->SetMethod(GetMethod());
4372 return clone;
4373 }
4374
Latency()4375 uint32_t Latency() const override
4376 {
4377 return options.GetCompilerSchedLatencyLong();
4378 }
4379 };
4380
4381 /**
4382 * Store value into static field.
4383 */
4384 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4385 class StoreStaticInst : public VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>,
4386 public TypeIdMixin,
4387 public FieldMixin {
4388 public:
4389 DECLARE_INST(StoreStaticInst);
4390 using Base = VolatileMixin<NeedBarrierMixin<FixedInputsInst2>>;
4391 using Base::Base;
4392 static constexpr size_t STORED_INPUT_INDEX = 1;
4393
IsBarrier()4394 bool IsBarrier() const override
4395 {
4396 return Inst::IsBarrier() || GetNeedBarrier() || GetVolatile();
4397 }
4398
4399 void DumpOpcode(std::ostream *out) const override;
4400
GetInputType(size_t index)4401 DataType::Type GetInputType(size_t index) const override
4402 {
4403 ASSERT(index < GetInputsCount());
4404 if (index == 0) {
4405 return DataType::REFERENCE;
4406 }
4407 return GetType();
4408 }
4409
Clone(const Graph * targetGraph)4410 Inst *Clone(const Graph *targetGraph) const override
4411 {
4412 auto clone = FixedInputsInst::Clone(targetGraph);
4413 clone->CastToStoreStatic()->SetTypeId(GetTypeId());
4414 clone->CastToStoreStatic()->SetMethod(GetMethod());
4415 clone->CastToStoreStatic()->SetObjField(GetObjField());
4416 clone->CastToStoreStatic()->SetVolatile(GetVolatile());
4417 return clone;
4418 }
4419
4420 // StoreStatic call barriers twice,so we need to save input register for second call
IsPropagateLiveness()4421 bool IsPropagateLiveness() const override
4422 {
4423 return GetType() == DataType::REFERENCE;
4424 }
4425 };
4426
4427 /**
4428 * Store value into unresolved static field.
4429 */
4430 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4431 class UnresolvedStoreStaticInst : public NeedBarrierMixin<FixedInputsInst2>, public TypeIdMixin {
4432 public:
4433 DECLARE_INST(UnresolvedStoreStaticInst);
4434 using Base = NeedBarrierMixin<FixedInputsInst2>;
4435 using Base::Base;
4436
IsBarrier()4437 bool IsBarrier() const override
4438 {
4439 return true;
4440 }
4441
4442 void DumpOpcode(std::ostream *out) const override;
4443
GetInputType(size_t index)4444 DataType::Type GetInputType(size_t index) const override
4445 {
4446 ASSERT(index < GetInputsCount());
4447 if (index == 1) {
4448 // This is SaveState input
4449 return DataType::NO_TYPE;
4450 }
4451 ASSERT(index == 0);
4452 return GetType();
4453 }
4454
Clone(const Graph * targetGraph)4455 Inst *Clone(const Graph *targetGraph) const override
4456 {
4457 auto clone = FixedInputsInst::Clone(targetGraph);
4458 clone->CastToUnresolvedStoreStatic()->SetTypeId(GetTypeId());
4459 clone->CastToUnresolvedStoreStatic()->SetMethod(GetMethod());
4460 return clone;
4461 }
4462 };
4463
4464 /**
4465 * Create new object
4466 */
4467 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4468 class NewObjectInst : public NeedBarrierMixin<FixedInputsInst2>, public TypeIdMixin {
4469 public:
4470 DECLARE_INST(NewObjectInst);
4471 using Base = NeedBarrierMixin<FixedInputsInst2>;
4472 using Base::Base;
4473
IsBarrier()4474 bool IsBarrier() const override
4475 {
4476 return Inst::IsBarrier() || GetNeedBarrier();
4477 }
GetInputType(size_t index)4478 DataType::Type GetInputType(size_t index) const override
4479 {
4480 ASSERT(index < GetInputsCount());
4481 if (index == 0) {
4482 return DataType::REFERENCE;
4483 }
4484 return DataType::NO_TYPE;
4485 }
4486
4487 void DumpOpcode(std::ostream *out) const override;
4488
Clone(const Graph * targetGraph)4489 Inst *Clone(const Graph *targetGraph) const override
4490 {
4491 auto clone = FixedInputsInst::Clone(targetGraph);
4492 clone->CastToNewObject()->SetTypeId(GetTypeId());
4493 clone->CastToNewObject()->SetMethod(GetMethod());
4494 return clone;
4495 }
4496 };
4497
4498 /**
4499 * Create new array
4500 */
4501 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4502 class NewArrayInst : public NeedBarrierMixin<FixedInputsInst3>, public TypeIdMixin {
4503 public:
4504 DECLARE_INST(NewArrayInst);
4505 using Base = NeedBarrierMixin<FixedInputsInst3>;
4506 using Base::Base;
4507
4508 static constexpr size_t INDEX_CLASS = 0;
4509 static constexpr size_t INDEX_SIZE = 1;
4510 static constexpr size_t INDEX_SAVE_STATE = 2;
4511
IsBarrier()4512 bool IsBarrier() const override
4513 {
4514 return Inst::IsBarrier() || GetNeedBarrier();
4515 }
4516
GetInputType(size_t index)4517 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4518 {
4519 ASSERT(index < GetInputsCount());
4520 switch (index) {
4521 case INDEX_CLASS:
4522 return GetInput(0).GetInst()->GetType();
4523 case INDEX_SIZE:
4524 return DataType::INT32;
4525 case INDEX_SAVE_STATE:
4526 // This is SaveState input
4527 return DataType::NO_TYPE;
4528 default:
4529 UNREACHABLE();
4530 }
4531 }
4532
4533 void DumpOpcode(std::ostream *out) const override;
4534
Clone(const Graph * targetGraph)4535 Inst *Clone(const Graph *targetGraph) const override
4536 {
4537 auto clone = FixedInputsInst::Clone(targetGraph);
4538 clone->CastToNewArray()->SetTypeId(GetTypeId());
4539 clone->CastToNewArray()->SetMethod(GetMethod());
4540 return clone;
4541 }
4542 };
4543
4544 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4545 class LoadConstArrayInst : public NeedBarrierMixin<FixedInputsInst1>, public TypeIdMixin {
4546 public:
4547 DECLARE_INST(LoadConstArrayInst);
4548 using Base = NeedBarrierMixin<FixedInputsInst1>;
4549 using Base::Base;
4550
IsBarrier()4551 bool IsBarrier() const override
4552 {
4553 return Inst::IsBarrier() || GetNeedBarrier();
4554 }
4555
GetInputType(size_t index)4556 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4557 {
4558 ASSERT(index < GetInputsCount());
4559 return DataType::NO_TYPE;
4560 }
4561
4562 void DumpOpcode(std::ostream *out) const override;
4563
Clone(const Graph * targetGraph)4564 Inst *Clone(const Graph *targetGraph) const override
4565 {
4566 auto clone = FixedInputsInst::Clone(targetGraph);
4567 clone->CastToLoadConstArray()->SetTypeId(GetTypeId());
4568 clone->CastToLoadConstArray()->SetMethod(GetMethod());
4569 return clone;
4570 }
4571 };
4572
4573 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4574 class FillConstArrayInst : public NeedBarrierMixin<FixedInputsInst2>, public TypeIdMixin, public ImmediateMixin {
4575 public:
4576 DECLARE_INST(FillConstArrayInst);
4577 using Base = NeedBarrierMixin<FixedInputsInst2>;
4578 using Base::Base;
4579
IsBarrier()4580 bool IsBarrier() const override
4581 {
4582 return Inst::IsBarrier() || GetNeedBarrier();
4583 }
4584
GetInputType(size_t index)4585 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4586 {
4587 ASSERT(index < GetInputsCount());
4588 return index == 0 ? DataType::REFERENCE : DataType::NO_TYPE;
4589 }
4590
4591 void DumpOpcode(std::ostream *out) const override;
4592
Clone(const Graph * targetGraph)4593 Inst *Clone(const Graph *targetGraph) const override
4594 {
4595 auto clone = FixedInputsInst::Clone(targetGraph);
4596 clone->CastToFillConstArray()->SetTypeId(GetTypeId());
4597 clone->CastToFillConstArray()->SetMethod(GetMethod());
4598 clone->CastToFillConstArray()->SetImm(GetImm());
4599 return clone;
4600 }
4601 };
4602
4603 /**
4604 * Checkcast
4605 */
4606 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4607 class CheckCastInst : public OmitNullCheckMixin<ClassTypeMixin<NeedBarrierMixin<FixedInputsInst3>>>,
4608 public TypeIdMixin {
4609 public:
4610 DECLARE_INST(CheckCastInst);
4611 using Base = OmitNullCheckMixin<ClassTypeMixin<NeedBarrierMixin<FixedInputsInst3>>>;
4612 using Base::Base;
4613
IsBarrier()4614 bool IsBarrier() const override
4615 {
4616 return Inst::IsBarrier() || GetNeedBarrier();
4617 }
4618
GetInputType(size_t index)4619 DataType::Type GetInputType(size_t index) const override
4620 {
4621 ASSERT(index < GetInputsCount());
4622 ASSERT(GetInputsCount() == 3U);
4623 if (index < 2U) {
4624 return DataType::REFERENCE;
4625 }
4626 return DataType::NO_TYPE;
4627 }
4628
4629 void DumpOpcode(std::ostream *out) const override;
4630
Clone(const Graph * targetGraph)4631 Inst *Clone(const Graph *targetGraph) const override
4632 {
4633 auto clone = FixedInputsInst::Clone(targetGraph);
4634 clone->CastToCheckCast()->SetTypeId(GetTypeId());
4635 clone->CastToCheckCast()->SetMethod(GetMethod());
4636 clone->CastToCheckCast()->SetClassType(GetClassType());
4637 clone->CastToCheckCast()->SetOmitNullCheck(GetOmitNullCheck());
4638 return clone;
4639 }
4640 };
4641
4642 /**
4643 * Is instance
4644 */
4645 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4646 class IsInstanceInst : public OmitNullCheckMixin<ClassTypeMixin<NeedBarrierMixin<FixedInputsInst3>>>,
4647 public TypeIdMixin {
4648 public:
4649 DECLARE_INST(IsInstanceInst);
4650 using Base = OmitNullCheckMixin<ClassTypeMixin<NeedBarrierMixin<FixedInputsInst3>>>;
4651 using Base::Base;
4652
IsBarrier()4653 bool IsBarrier() const override
4654 {
4655 return Inst::IsBarrier() || GetNeedBarrier();
4656 }
4657
GetInputType(size_t index)4658 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4659 {
4660 ASSERT(index < GetInputsCount());
4661 ASSERT(GetInputsCount() == 3U);
4662 if (index < 2U) {
4663 return DataType::REFERENCE;
4664 }
4665 return DataType::NO_TYPE;
4666 }
4667
4668 void DumpOpcode(std::ostream *out) const override;
4669
Clone(const Graph * targetGraph)4670 Inst *Clone(const Graph *targetGraph) const override
4671 {
4672 auto clone = FixedInputsInst::Clone(targetGraph);
4673 clone->CastToIsInstance()->SetTypeId(GetTypeId());
4674 clone->CastToIsInstance()->SetMethod(GetMethod());
4675 clone->CastToIsInstance()->SetClassType(GetClassType());
4676 clone->CastToIsInstance()->SetOmitNullCheck(GetOmitNullCheck());
4677 return clone;
4678 }
4679 };
4680
4681 /**
4682 * Load data from constant pool.
4683 */
4684 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4685 class LoadFromPool : public NeedBarrierMixin<FixedInputsInst1>, public TypeIdMixin {
4686 public:
4687 DECLARE_INST(LoadFromPool);
4688 using Base = NeedBarrierMixin<FixedInputsInst1>;
4689 using Base::Base;
4690
IsBarrier()4691 bool IsBarrier() const override
4692 {
4693 return Inst::IsBarrier() || GetNeedBarrier();
4694 }
4695
GetInputType(size_t index)4696 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4697 {
4698 ASSERT(index < GetInputsCount());
4699 return DataType::NO_TYPE;
4700 }
4701
4702 void DumpOpcode(std::ostream *out) const override;
4703
Clone(const Graph * targetGraph)4704 Inst *Clone(const Graph *targetGraph) const override
4705 {
4706 auto clone = FixedInputsInst::Clone(targetGraph);
4707 static_cast<LoadFromPool *>(clone)->SetTypeId(GetTypeId());
4708 static_cast<LoadFromPool *>(clone)->SetMethod(GetMethod());
4709 return clone;
4710 }
4711 };
4712
4713 /**
4714 * Initialization or loading of the class.
4715 */
4716 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4717 class ClassInst : public NeedBarrierMixin<FixedInputsInst1>, public TypeIdMixin {
4718 public:
4719 DECLARE_INST(ClassInst);
4720 using Base = NeedBarrierMixin<FixedInputsInst1>;
4721 using Base::Base;
4722
IsBarrier()4723 bool IsBarrier() const override
4724 {
4725 return Inst::IsBarrier() || GetNeedBarrier();
4726 }
4727
4728 void DumpOpcode(std::ostream *out) const override;
4729
Clone(const Graph * targetGraph)4730 Inst *Clone(const Graph *targetGraph) const override
4731 {
4732 auto clone = FixedInputsInst::Clone(targetGraph);
4733 static_cast<ClassInst *>(clone)->SetTypeId(GetTypeId());
4734 static_cast<ClassInst *>(clone)->SetMethod(GetMethod());
4735 static_cast<ClassInst *>(clone)->SetClass(GetClass());
4736 return clone;
4737 }
4738
GetClass()4739 RuntimeInterface::ClassPtr GetClass() const
4740 {
4741 return klass_;
4742 }
4743
SetClass(RuntimeInterface::ClassPtr klass)4744 void SetClass(RuntimeInterface::ClassPtr klass)
4745 {
4746 klass_ = klass;
4747 }
4748
4749 private:
4750 RuntimeInterface::ClassPtr klass_ {nullptr};
4751 };
4752
4753 /**
4754 * Get class pointer from the specific source.
4755 */
4756 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4757 class ClassImmediateInst : public Inst {
4758 public:
4759 DECLARE_INST(ClassImmediateInst);
4760 using Base = Inst;
4761 using Base::Base;
4762
ClassImmediateInst(Opcode opcode,DataType::Type type,uint32_t pc,RuntimeInterface::ClassPtr cls)4763 ClassImmediateInst(Opcode opcode, DataType::Type type, uint32_t pc, RuntimeInterface::ClassPtr cls)
4764 : Base(opcode, type, pc), class_(cls)
4765 {
4766 }
4767
Clone(const Graph * targetGraph)4768 Inst *Clone(const Graph *targetGraph) const override
4769 {
4770 auto clone = Inst::Clone(targetGraph);
4771 clone->CastToClassImmediate()->class_ = class_;
4772 return clone;
4773 }
4774
GetClassPtr()4775 RuntimeInterface::ClassPtr GetClassPtr() const
4776 {
4777 return class_;
4778 }
4779
4780 void DumpOpcode(std::ostream * /* unused */) const override;
4781
4782 private:
4783 RuntimeInterface::ClassPtr class_ {nullptr};
4784 };
4785
4786 /**
4787 * Select instruction
4788 */
4789 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4790 class SelectInst : public ConditionMixin<InstWithOperandsType<FixedInputsInst<4U>>> {
4791 public:
4792 DECLARE_INST(SelectInst);
4793 using Base = ConditionMixin<InstWithOperandsType<FixedInputsInst<4U>>>;
4794 using Base::Base;
4795
SelectInst(Opcode opcode,DataType::Type type,uint32_t pc,ConditionCode cc)4796 SelectInst(Opcode opcode, DataType::Type type, uint32_t pc, ConditionCode cc) : Base(opcode, type, pc)
4797 {
4798 SetCc(cc);
4799 }
4800
GetInputType(size_t index)4801 DataType::Type GetInputType(size_t index) const override
4802 {
4803 ASSERT(index < GetInputsCount());
4804 if (index < 2U) {
4805 return GetType();
4806 }
4807 return GetOperandsType();
4808 }
4809
4810 void DumpOpcode(std::ostream * /* unused */) const override;
4811 void SetVnObject(VnObject *vn_obj) override;
4812
Clone(const Graph * targetGraph)4813 Inst *Clone(const Graph *targetGraph) const override
4814 {
4815 auto clone = FixedInputsInst::Clone(targetGraph);
4816 clone->CastToSelect()->SetCc(GetCc());
4817 clone->CastToSelect()->SetOperandsType(GetOperandsType());
4818 return clone;
4819 }
4820 };
4821
4822 /**
4823 * SelectImm with comparison with immediate
4824 */
4825 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4826 class SelectImmInst : public InstWithOperandsType<ConditionMixin<FixedInputsInst3>>, public ImmediateMixin {
4827 public:
4828 DECLARE_INST(SelectImmInst);
4829 using Base = InstWithOperandsType<ConditionMixin<FixedInputsInst3>>;
4830 using Base::Base;
4831
SelectImmInst(Opcode opcode,DataType::Type type,uint32_t pc,ConditionCode cc,uint64_t imm)4832 SelectImmInst(Opcode opcode, DataType::Type type, uint32_t pc, ConditionCode cc, uint64_t imm)
4833 : Base(opcode, type, pc), ImmediateMixin(imm)
4834 {
4835 SetCc(cc);
4836 }
4837
GetInputType(size_t index)4838 DataType::Type GetInputType(size_t index) const override
4839 {
4840 ASSERT(index < GetInputsCount());
4841 if (index < 2U) {
4842 return GetType();
4843 }
4844 return GetOperandsType();
4845 }
4846
4847 void DumpOpcode(std::ostream * /* unused */) const override;
4848 bool DumpInputs(std::ostream * /* unused */) const override;
4849 void SetVnObject(VnObject *vn_obj) override;
4850
Clone(const Graph * targetGraph)4851 Inst *Clone(const Graph *targetGraph) const override
4852 {
4853 auto clone = FixedInputsInst::Clone(targetGraph);
4854 clone->CastToSelectImm()->SetCc(GetCc());
4855 clone->CastToSelectImm()->SetImm(GetImm());
4856 clone->CastToSelectImm()->SetOperandsType(GetOperandsType());
4857 return clone;
4858 }
4859 };
4860
4861 /**
4862 * Conditional jump instruction
4863 */
4864 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4865 class IfInst : public InstWithOperandsType<ConditionMixin<FixedInputsInst2>> {
4866 public:
4867 DECLARE_INST(IfInst);
4868 using Base = InstWithOperandsType<ConditionMixin<FixedInputsInst2>>;
4869 using Base::Base;
4870
IfInst(Opcode opcode,DataType::Type type,uint32_t pc,ConditionCode cc)4871 IfInst(Opcode opcode, DataType::Type type, uint32_t pc, ConditionCode cc) : Base(opcode, type, pc)
4872 {
4873 SetCc(cc);
4874 }
4875
GetInputType(size_t index)4876 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4877 {
4878 ASSERT(index < GetInputsCount());
4879 return GetOperandsType();
4880 }
4881
4882 void DumpOpcode(std::ostream * /* unused */) const override;
4883
4884 void SetVnObject(VnObject *vn_obj) override;
4885
Clone(const Graph * targetGraph)4886 Inst *Clone(const Graph *targetGraph) const override
4887 {
4888 auto clone = FixedInputsInst::Clone(targetGraph);
4889 static_cast<IfInst *>(clone)->SetCc(GetCc());
4890 static_cast<IfInst *>(clone)->SetOperandsType(GetOperandsType());
4891 static_cast<IfInst *>(clone)->SetMethod(GetMethod());
4892 return clone;
4893 }
4894
SetMethod(RuntimeInterface::MethodPtr method)4895 void SetMethod(RuntimeInterface::MethodPtr method)
4896 {
4897 method_ = method;
4898 }
4899
GetMethod()4900 RuntimeInterface::MethodPtr GetMethod() const
4901 {
4902 return method_;
4903 }
4904
4905 private:
4906 RuntimeInterface::MethodPtr method_ {nullptr};
4907 };
4908
4909 /**
4910 * IfImm instruction with immediate
4911 */
4912 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4913 class IfImmInst : public InstWithOperandsType<ConditionMixin<FixedInputsInst1>>, public ImmediateMixin {
4914 public:
4915 DECLARE_INST(IfImmInst);
4916 using Base = InstWithOperandsType<ConditionMixin<FixedInputsInst1>>;
4917 using Base::Base;
4918
IfImmInst(Opcode opcode,DataType::Type type,uint32_t pc,ConditionCode cc,uint64_t imm)4919 IfImmInst(Opcode opcode, DataType::Type type, uint32_t pc, ConditionCode cc, uint64_t imm)
4920 : Base(opcode, type, pc), ImmediateMixin(imm)
4921 {
4922 SetCc(cc);
4923 }
4924
GetInputType(size_t index)4925 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
4926 {
4927 ASSERT(index < GetInputsCount());
4928 return GetOperandsType();
4929 }
4930
4931 void DumpOpcode(std::ostream * /* unused */) const override;
4932 bool DumpInputs(std::ostream * /* unused */) const override;
4933 void SetVnObject(VnObject *vn_obj) override;
4934
Clone(const Graph * targetGraph)4935 Inst *Clone(const Graph *targetGraph) const override
4936 {
4937 auto clone = FixedInputsInst::Clone(targetGraph);
4938 clone->CastToIfImm()->SetCc(GetCc());
4939 clone->CastToIfImm()->SetImm(GetImm());
4940 clone->CastToIfImm()->SetOperandsType(GetOperandsType());
4941 clone->CastToIfImm()->SetMethod(GetMethod());
4942 return clone;
4943 }
4944
4945 BasicBlock *GetEdgeIfInputTrue();
4946 BasicBlock *GetEdgeIfInputFalse();
4947
SetMethod(RuntimeInterface::MethodPtr method)4948 void SetMethod(RuntimeInterface::MethodPtr method)
4949 {
4950 method_ = method;
4951 }
4952
GetMethod()4953 RuntimeInterface::MethodPtr GetMethod() const
4954 {
4955 return method_;
4956 }
4957
4958 private:
4959 size_t GetTrueInputEdgeIdx();
4960 RuntimeInterface::MethodPtr method_ {nullptr};
4961 };
4962
4963 /**
4964 * Load element from a pair of values, using index as immediate
4965 */
4966 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4967 class LoadPairPartInst : public FixedInputsInst1, public ImmediateMixin {
4968 public:
4969 DECLARE_INST(LoadPairPartInst);
4970 using FixedInputsInst1::FixedInputsInst1;
4971
LoadPairPartInst(Opcode opcode,uint64_t imm)4972 explicit LoadPairPartInst(Opcode opcode, uint64_t imm) : FixedInputsInst1(opcode), ImmediateMixin(imm) {}
4973
GetSrcRegIndex()4974 uint32_t GetSrcRegIndex() const override
4975 {
4976 return GetImm();
4977 }
4978
4979 bool DumpInputs(std::ostream * /* out */) const override;
4980
Clone(const Graph * targetGraph)4981 Inst *Clone(const Graph *targetGraph) const override
4982 {
4983 auto clone = FixedInputsInst::Clone(targetGraph);
4984 clone->CastToLoadPairPart()->SetImm(GetImm());
4985 return clone;
4986 }
4987
Latency()4988 uint32_t Latency() const override
4989 {
4990 return options.GetCompilerSchedLatencyLong();
4991 }
4992 };
4993
4994 /**
4995 * Load a pair of consecutive values from array
4996 */
4997 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
4998 class LoadArrayPairInst : public NeedBarrierMixin<MultipleOutputMixin<FixedInputsInst2, 2U>> {
4999 public:
5000 DECLARE_INST(LoadArrayPairInst);
5001 using Base = NeedBarrierMixin<MultipleOutputMixin<FixedInputsInst2, 2U>>;
5002 using Base::Base;
5003
GetArray()5004 Inst *GetArray()
5005 {
5006 return GetInput(0).GetInst();
5007 }
GetIndex()5008 Inst *GetIndex()
5009 {
5010 return GetInput(1).GetInst();
5011 }
5012
IsBarrier()5013 bool IsBarrier() const override
5014 {
5015 return Inst::IsBarrier() || GetNeedBarrier();
5016 }
5017
Clone(const Graph * targetGraph)5018 Inst *Clone(const Graph *targetGraph) const override
5019 {
5020 auto clone = FixedInputsInst::Clone(targetGraph)->CastToLoadArrayPair();
5021 #ifndef NDEBUG
5022 for (size_t i = 0; i < GetDstCount(); ++i) {
5023 clone->SetDstReg(i, GetDstReg(i));
5024 }
5025 #endif
5026 return clone;
5027 }
5028
GetInputType(size_t index)5029 DataType::Type GetInputType(size_t index) const override
5030 {
5031 ASSERT(index < GetInputsCount());
5032 switch (index) {
5033 case 0:
5034 return DataType::REFERENCE;
5035 case 1:
5036 return DataType::INT32;
5037 default:
5038 return DataType::NO_TYPE;
5039 }
5040 }
5041
Latency()5042 uint32_t Latency() const override
5043 {
5044 return 0;
5045 }
5046 };
5047
5048 /**
5049 * Store a pair of consecutive values to array
5050 */
5051 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5052 class StoreArrayPairInst : public NeedBarrierMixin<FixedInputsInst<4U>> {
5053 public:
5054 DECLARE_INST(StoreVectorInst);
5055 using Base = NeedBarrierMixin<FixedInputsInst<4U>>;
5056 using Base::Base;
5057
GetArray()5058 Inst *GetArray()
5059 {
5060 return GetInput(0).GetInst();
5061 }
GetIndex()5062 Inst *GetIndex()
5063 {
5064 return GetInput(1).GetInst();
5065 }
GetStoredValue(uint64_t index)5066 Inst *GetStoredValue(uint64_t index)
5067 {
5068 return GetInput(2U + index).GetInst();
5069 }
GetInputType(size_t index)5070 DataType::Type GetInputType(size_t index) const override
5071 {
5072 ASSERT(index < GetInputsCount());
5073 switch (index) {
5074 case 0:
5075 return DataType::REFERENCE;
5076 case 1:
5077 return DataType::INT32;
5078 case 2U:
5079 case 3U:
5080 return GetType();
5081 default:
5082 return DataType::NO_TYPE;
5083 }
5084 }
5085
5086 // StoreArrayPair call barriers twice,so we need to save input register for second call
IsPropagateLiveness()5087 bool IsPropagateLiveness() const override
5088 {
5089 return GetType() == DataType::REFERENCE;
5090 }
5091
IsBarrier()5092 bool IsBarrier() const override
5093 {
5094 return Inst::IsBarrier() || GetNeedBarrier();
5095 }
5096 };
5097
5098 /**
5099 * Load a pair of consecutive values from array, using array index as immediate
5100 */
5101 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5102 class LoadArrayPairInstI : public NeedBarrierMixin<MultipleOutputMixin<FixedInputsInst1, 2U>>, public ImmediateMixin {
5103 public:
5104 DECLARE_INST(LoadArrayPairInstI);
5105 using Base = NeedBarrierMixin<MultipleOutputMixin<FixedInputsInst1, 2U>>;
5106 using Base::Base;
5107
LoadArrayPairInstI(Opcode opcode,uint64_t imm)5108 explicit LoadArrayPairInstI(Opcode opcode, uint64_t imm) : Base(opcode), ImmediateMixin(imm) {}
5109
GetArray()5110 Inst *GetArray()
5111 {
5112 return GetInput(0).GetInst();
5113 }
5114
IsBarrier()5115 bool IsBarrier() const override
5116 {
5117 return Inst::IsBarrier() || GetNeedBarrier();
5118 }
5119 bool DumpInputs(std::ostream * /* out */) const override;
5120
Clone(const Graph * targetGraph)5121 Inst *Clone(const Graph *targetGraph) const override
5122 {
5123 auto clone = FixedInputsInst::Clone(targetGraph)->CastToLoadArrayPairI();
5124 clone->SetImm(GetImm());
5125 #ifndef NDEBUG
5126 for (size_t i = 0; i < GetDstCount(); ++i) {
5127 clone->SetDstReg(i, GetDstReg(i));
5128 }
5129 #endif
5130 return clone;
5131 }
5132
GetInputType(size_t index)5133 DataType::Type GetInputType(size_t index) const override
5134 {
5135 ASSERT(index < GetInputsCount());
5136 if (index == 0) {
5137 return DataType::REFERENCE;
5138 }
5139 return DataType::NO_TYPE;
5140 }
5141
Latency()5142 uint32_t Latency() const override
5143 {
5144 return 0;
5145 }
5146 };
5147
5148 /**
5149 * Store a pair of consecutive values to array, using array index as immediate
5150 */
5151 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5152 class StoreArrayPairInstI : public NeedBarrierMixin<FixedInputsInst3>, public ImmediateMixin {
5153 public:
5154 DECLARE_INST(StoreArrayPairInstI);
5155 using Base = NeedBarrierMixin<FixedInputsInst3>;
5156 using Base::Base;
5157
StoreArrayPairInstI(Opcode opcode,uint64_t imm)5158 explicit StoreArrayPairInstI(Opcode opcode, uint64_t imm) : Base(opcode), ImmediateMixin(imm) {}
5159
GetArray()5160 Inst *GetArray()
5161 {
5162 return GetInput(0).GetInst();
5163 }
GetFirstValue()5164 Inst *GetFirstValue()
5165 {
5166 return GetInput(1).GetInst();
5167 }
GetSecondValue()5168 Inst *GetSecondValue()
5169 {
5170 return GetInput(2U).GetInst();
5171 }
GetInputType(size_t index)5172 DataType::Type GetInputType(size_t index) const override
5173 {
5174 ASSERT(index < GetInputsCount());
5175 switch (index) {
5176 case 0:
5177 return DataType::REFERENCE;
5178 case 1:
5179 case 2U:
5180 return GetType();
5181 default:
5182 return DataType::NO_TYPE;
5183 }
5184 }
5185
5186 // StoreArrayPairI call barriers twice,so we need to save input register for second call
IsPropagateLiveness()5187 bool IsPropagateLiveness() const override
5188 {
5189 return GetType() == DataType::REFERENCE;
5190 }
5191
IsBarrier()5192 bool IsBarrier() const override
5193 {
5194 return Inst::IsBarrier() || GetNeedBarrier();
5195 }
5196
5197 bool DumpInputs(std::ostream * /* out */) const override;
5198
Clone(const Graph * targetGraph)5199 Inst *Clone(const Graph *targetGraph) const override
5200 {
5201 auto clone = FixedInputsInst::Clone(targetGraph);
5202 clone->CastToStoreArrayPairI()->SetImm(GetImm());
5203 return clone;
5204 }
5205 };
5206
5207 /**
5208 * CatchPhiInst instruction
5209 */
5210 class CatchPhiInst : public DynamicInputsInst {
5211 public:
5212 DECLARE_INST(CatchPhiInst);
5213 using DynamicInputsInst::DynamicInputsInst;
5214
GetThrowableInsts()5215 const ArenaVector<const Inst *> *GetThrowableInsts() const
5216 {
5217 return throw_insts_;
5218 }
5219
GetThrowableInst(size_t i)5220 const Inst *GetThrowableInst(size_t i) const
5221 {
5222 ASSERT(throw_insts_ != nullptr && i < throw_insts_->size());
5223 return throw_insts_->at(i);
5224 }
5225
5226 void AppendThrowableInst(const Inst *inst);
5227 void ReplaceThrowableInst(const Inst *old_inst, const Inst *new_inst);
5228 void RemoveInput(unsigned index) override;
5229
IsAcc()5230 bool IsAcc() const
5231 {
5232 return GetField<IsAccFlag>();
5233 }
5234
SetIsAcc()5235 void SetIsAcc()
5236 {
5237 SetField<IsAccFlag>(true);
5238 }
5239
5240 protected:
5241 using IsAccFlag = LastField::NextFlag;
5242 using LastField = IsAccFlag;
5243
5244 private:
GetThrowableInstIndex(const Inst * inst)5245 size_t GetThrowableInstIndex(const Inst *inst)
5246 {
5247 ASSERT(throw_insts_ != nullptr);
5248 auto it = std::find(throw_insts_->begin(), throw_insts_->end(), inst);
5249 ASSERT(it != throw_insts_->end());
5250 return std::distance(throw_insts_->begin(), it);
5251 }
5252
5253 private:
5254 ArenaVector<const Inst *> *throw_insts_ {nullptr};
5255 };
5256
5257 class TryInst : public FixedInputsInst0 {
5258 public:
5259 DECLARE_INST(TryInst);
5260 using FixedInputsInst0::FixedInputsInst0;
5261
5262 void AppendCatchTypeId(uint32_t id, uint32_t catch_edge_index);
5263
GetCatchTypeIds()5264 const ArenaVector<uint32_t> *GetCatchTypeIds() const
5265 {
5266 return catch_type_ids_;
5267 }
5268
GetCatchEdgeIndexes()5269 const ArenaVector<uint32_t> *GetCatchEdgeIndexes() const
5270 {
5271 return catch_edge_indexes_;
5272 }
5273
GetCatchTypeIdsCount()5274 size_t GetCatchTypeIdsCount() const
5275 {
5276 return (catch_type_ids_ == nullptr ? 0 : catch_type_ids_->size());
5277 }
5278
5279 Inst *Clone(const Graph *targetGraph) const override;
5280
SetTryEndBlock(BasicBlock * try_end_bb)5281 void SetTryEndBlock(BasicBlock *try_end_bb)
5282 {
5283 try_end_bb_ = try_end_bb;
5284 }
5285
GetTryEndBlock()5286 BasicBlock *GetTryEndBlock() const
5287 {
5288 return try_end_bb_;
5289 }
5290
5291 private:
5292 ArenaVector<uint32_t> *catch_type_ids_ {nullptr};
5293 ArenaVector<uint32_t> *catch_edge_indexes_ {nullptr};
5294 BasicBlock *try_end_bb_ {nullptr};
5295 };
5296
5297 TryInst *GetTryBeginInst(const BasicBlock *try_begin_bb);
5298
5299 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5300 class DeoptimizeInst : public DeoptimizeTypeMixin<FixedInputsInst1> {
5301 public:
5302 DECLARE_INST(DeoptimizeInst);
5303 using Base = DeoptimizeTypeMixin<FixedInputsInst1>;
5304 using Base::Base;
5305
Clone(const Graph * targetGraph)5306 Inst *Clone(const Graph *targetGraph) const override
5307 {
5308 auto clone = FixedInputsInst::Clone(targetGraph);
5309 clone->CastToDeoptimize()->SetDeoptimizeType(GetDeoptimizeType());
5310 return clone;
5311 }
5312
5313 void DumpOpcode(std::ostream *out) const override;
5314 };
5315
5316 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5317 class DeoptimizeIfInst : public DeoptimizeTypeMixin<FixedInputsInst2> {
5318 DECLARE_INST(DeoptimizeInst);
5319 using Base = DeoptimizeTypeMixin<FixedInputsInst2>;
5320
5321 public:
5322 using Base::Base;
5323
Clone(const Graph * targetGraph)5324 Inst *Clone(const Graph *targetGraph) const override
5325 {
5326 auto clone = FixedInputsInst::Clone(targetGraph);
5327 clone->CastToDeoptimizeIf()->SetDeoptimizeType(GetDeoptimizeType());
5328 return clone;
5329 }
5330
GetInputType(size_t index)5331 DataType::Type GetInputType(size_t index) const override
5332 {
5333 ASSERT(index < GetInputsCount());
5334 switch (index) {
5335 case 0:
5336 return GetInput(0).GetInst()->GetType();
5337 case 1:
5338 return DataType::NO_TYPE;
5339 default:
5340 UNREACHABLE();
5341 }
5342 }
5343
5344 void DumpOpcode(std::ostream *out) const override;
5345 };
5346
5347 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5348 class DeoptimizeCompareInst : public InstWithOperandsType<DeoptimizeTypeMixin<ConditionMixin<FixedInputsInst3>>> {
5349 public:
5350 DECLARE_INST(DeoptimizeCompareInst);
5351 using Base = InstWithOperandsType<DeoptimizeTypeMixin<ConditionMixin<FixedInputsInst3>>>;
5352 using Base::Base;
5353
DeoptimizeCompareInst(Opcode opcode,const DeoptimizeIfInst * deoptIf,const CompareInst * compare)5354 explicit DeoptimizeCompareInst(Opcode opcode, const DeoptimizeIfInst *deoptIf, const CompareInst *compare)
5355 : Base(opcode, deoptIf->GetType(), deoptIf->GetPc())
5356 {
5357 SetDeoptimizeType(deoptIf->GetDeoptimizeType());
5358 SetOperandsType(compare->GetOperandsType());
5359 SetCc(compare->GetCc());
5360 }
5361
Clone(const Graph * targetGraph)5362 Inst *Clone(const Graph *targetGraph) const override
5363 {
5364 auto clone = FixedInputsInst3::Clone(targetGraph);
5365 clone->CastToDeoptimizeCompare()->SetDeoptimizeType(GetDeoptimizeType());
5366 clone->CastToDeoptimizeCompare()->SetOperandsType(GetOperandsType());
5367 clone->CastToDeoptimizeCompare()->SetCc(GetCc());
5368 return clone;
5369 }
5370
GetInputType(size_t index)5371 DataType::Type GetInputType(size_t index) const override
5372 {
5373 ASSERT(index < GetInputsCount());
5374 switch (index) {
5375 case 0:
5376 case 1:
5377 return GetInput(index).GetInst()->GetType();
5378 case 2U:
5379 return DataType::NO_TYPE;
5380 default:
5381 UNREACHABLE();
5382 }
5383 }
5384
5385 void DumpOpcode(std::ostream *out) const override;
5386 };
5387
5388 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
5389 class DeoptimizeCompareImmInst : public InstWithOperandsType<DeoptimizeTypeMixin<ConditionMixin<FixedInputsInst2>>>,
5390 public ImmediateMixin {
5391 public:
5392 DECLARE_INST(DeoptimizeCompareImmInst);
5393 using Base = InstWithOperandsType<DeoptimizeTypeMixin<ConditionMixin<FixedInputsInst2>>>;
5394 using Base::Base;
5395
DeoptimizeCompareImmInst(Opcode opcode,const DeoptimizeIfInst * deoptIf,const CompareInst * compare,uint64_t imm)5396 explicit DeoptimizeCompareImmInst(Opcode opcode, const DeoptimizeIfInst *deoptIf, const CompareInst *compare,
5397 uint64_t imm)
5398 : Base(opcode, deoptIf->GetType(), deoptIf->GetPc()), ImmediateMixin(imm)
5399 {
5400 SetDeoptimizeType(deoptIf->GetDeoptimizeType());
5401 SetOperandsType(compare->GetOperandsType());
5402 SetCc(compare->GetCc());
5403 }
5404
Clone(const Graph * targetGraph)5405 Inst *Clone(const Graph *targetGraph) const override
5406 {
5407 auto clone = FixedInputsInst2::Clone(targetGraph);
5408 clone->CastToDeoptimizeCompareImm()->SetDeoptimizeType(GetDeoptimizeType());
5409 clone->CastToDeoptimizeCompareImm()->SetOperandsType(GetOperandsType());
5410 clone->CastToDeoptimizeCompareImm()->SetCc(GetCc());
5411 clone->CastToDeoptimizeCompareImm()->SetImm(GetImm());
5412 return clone;
5413 }
5414
GetInputType(size_t index)5415 DataType::Type GetInputType(size_t index) const override
5416 {
5417 ASSERT(index < GetInputsCount());
5418 switch (index) {
5419 case 0:
5420 return GetInput(0).GetInst()->GetType();
5421 case 1:
5422 return DataType::NO_TYPE;
5423 default:
5424 UNREACHABLE();
5425 }
5426 }
5427
5428 void DumpOpcode(std::ostream *out) const override;
5429 bool DumpInputs(std::ostream *out) const override;
5430 };
5431
5432 class ThrowInst : public FixedInputsInst2 {
5433 public:
5434 DECLARE_INST(ThrowInst);
5435 using Base = FixedInputsInst2;
5436 using Base::Base;
5437
GetInputType(size_t index)5438 DataType::Type GetInputType(size_t index) const override
5439 {
5440 ASSERT(index < GetInputsCount());
5441 if (index == 0) {
5442 return DataType::REFERENCE;
5443 }
5444 return DataType::NO_TYPE;
5445 }
5446 };
5447
5448 class BinaryOverflowInst : public IfInst {
5449 public:
5450 DECLARE_INST(BinaryOverflowInst);
5451 using Base = IfInst;
5452 using Base::Base;
5453
BinaryOverflowInst(Opcode opcode,DataType::Type type,uint32_t pc,ConditionCode cc)5454 BinaryOverflowInst(Opcode opcode, DataType::Type type, uint32_t pc, ConditionCode cc) : Base(opcode, type, pc, cc)
5455 {
5456 SetOperandsType(type);
5457 }
5458
GetInputType(size_t index)5459 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
5460 {
5461 ASSERT(index < GetInputsCount());
5462 return GetType();
5463 }
5464
GetOperandsType()5465 DataType::Type GetOperandsType() const override
5466 {
5467 return GetType();
5468 }
5469 };
5470
IsVolatileMemInst(Inst * inst)5471 inline bool IsVolatileMemInst(Inst *inst)
5472 {
5473 switch (inst->GetOpcode()) {
5474 case Opcode::LoadObject:
5475 return inst->CastToLoadObject()->GetVolatile();
5476 case Opcode::StoreObject:
5477 return inst->CastToStoreObject()->GetVolatile();
5478 case Opcode::LoadStatic:
5479 return inst->CastToLoadStatic()->GetVolatile();
5480 case Opcode::StoreStatic:
5481 return inst->CastToStoreStatic()->GetVolatile();
5482 case Opcode::UnresolvedLoadObject:
5483 case Opcode::UnresolvedStoreObject:
5484 case Opcode::UnresolvedLoadStatic:
5485 case Opcode::UnresolvedStoreStatic:
5486 return true;
5487 default:
5488 return false;
5489 }
5490 }
5491
5492 // Check if instruction is pseudo-user for mutli-output instruction
IsPseudoUserOfMultiOutput(Inst * inst)5493 inline bool IsPseudoUserOfMultiOutput(Inst *inst)
5494 {
5495 switch (inst->GetOpcode()) {
5496 case Opcode::LoadPairPart:
5497 return true;
5498 default:
5499 return false;
5500 }
5501 }
5502
5503 template <typename InstType, typename... Args>
New(ArenaAllocator * allocator,Args &&...args)5504 InstType *Inst::New(ArenaAllocator *allocator, Args &&... args)
5505 {
5506 static_assert(alignof(InstType) >= alignof(uintptr_t));
5507 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-branch-clone)
5508 if constexpr (std::is_same_v<InstType, SpillFillInst>) {
5509 auto data = reinterpret_cast<uintptr_t>(allocator->Alloc(sizeof(InstType), DEFAULT_ALIGNMENT));
5510 ASSERT(data != 0);
5511 return new (reinterpret_cast<void *>(data)) InstType(allocator, std::forward<Args>(args)...);
5512 // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
5513 } else if constexpr (InstType::INPUT_COUNT == 0) {
5514 auto data = reinterpret_cast<uintptr_t>(allocator->Alloc(sizeof(InstType), DEFAULT_ALIGNMENT));
5515 ASSERT(data != 0);
5516 return new (reinterpret_cast<void *>(data)) InstType(std::forward<Args>(args)...);
5517 // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
5518 } else if constexpr (InstType::INPUT_COUNT == MAX_STATIC_INPUTS) {
5519 constexpr size_t OPERANDS_SIZE = sizeof(DynamicOperands);
5520 static_assert((OPERANDS_SIZE % alignof(InstType)) == 0);
5521 auto data = reinterpret_cast<uintptr_t>(allocator->Alloc(OPERANDS_SIZE + sizeof(InstType), DEFAULT_ALIGNMENT));
5522 ASSERT(data != 0);
5523 auto inst = new (reinterpret_cast<void *>(data + OPERANDS_SIZE)) InstType(std::forward<Args>(args)...);
5524 [[maybe_unused]] auto operands = new (reinterpret_cast<void *>(data)) DynamicOperands(allocator);
5525 static_cast<Inst *>(inst)->SetField<InputsCount>(InstType::INPUT_COUNT);
5526 return inst;
5527 } else { // NOLINT(readability-misleading-indentation)
5528 constexpr size_t OPERANDS_SIZE = sizeof(Operands<InstType::INPUT_COUNT>);
5529 constexpr auto ALIGNMENT {GetLogAlignment(alignof(Operands<InstType::INPUT_COUNT>))};
5530 static_assert((OPERANDS_SIZE % alignof(InstType)) == 0);
5531 auto data = reinterpret_cast<uintptr_t>(allocator->Alloc(OPERANDS_SIZE + sizeof(InstType), ALIGNMENT));
5532 ASSERT(data != 0);
5533 auto inst = new (reinterpret_cast<void *>(data + OPERANDS_SIZE)) InstType(std::forward<Args>(args)...);
5534 auto operands = new (reinterpret_cast<void *>(data)) Operands<InstType::INPUT_COUNT>;
5535 static_cast<Inst *>(inst)->SetField<InputsCount>(InstType::INPUT_COUNT);
5536 unsigned idx = InstType::INPUT_COUNT - 1;
5537 for (auto &user : operands->users) {
5538 new (&user) User(true, idx--, InstType::INPUT_COUNT);
5539 }
5540 return inst;
5541 }
5542 }
5543
GetInput()5544 inline Inst *User::GetInput()
5545 {
5546 return GetInst()->GetInput(GetIndex()).GetInst();
5547 }
5548
GetInput()5549 inline const Inst *User::GetInput() const
5550 {
5551 return GetInst()->GetInput(GetIndex()).GetInst();
5552 }
5553
5554 inline std::ostream &operator<<(std::ostream &os, const Inst &inst)
5555 {
5556 inst.Dump(&os, false);
5557 return os;
5558 }
5559
5560 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
5561 #define INST_DEF(opcode, base, ...) \
5562 inline const base *Inst::CastTo##opcode() const \
5563 { \
5564 ASSERT(GetOpcode() == Opcode::opcode); \
5565 return static_cast<const base *>(this); \
5566 }
5567 OPCODE_LIST(INST_DEF)
5568 #undef INST_DEF
5569
5570 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
5571 #define INST_DEF(opcode, base, ...) \
5572 inline base *Inst::CastTo##opcode() \
5573 { \
5574 ASSERT(GetOpcode() == Opcode::opcode); \
5575 return static_cast<base *>(this); \
5576 }
5577 OPCODE_LIST(INST_DEF)
5578 #undef INST_DEF
5579 } // namespace panda::compiler
5580
5581 #endif // COMPILER_OPTIMIZER_IR_INST_H
5582