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 default:
89 UNREACHABLE();
90 }
91 }
92
InverseSignednessConditionCode(ConditionCode code)93 inline ConditionCode InverseSignednessConditionCode(ConditionCode code)
94 {
95 switch (code) {
96 case ConditionCode::CC_EQ:
97 return ConditionCode::CC_EQ;
98 case ConditionCode::CC_NE:
99 return ConditionCode::CC_NE;
100
101 case ConditionCode::CC_LT:
102 return ConditionCode::CC_B;
103 case ConditionCode::CC_LE:
104 return ConditionCode::CC_BE;
105 case ConditionCode::CC_GT:
106 return ConditionCode::CC_A;
107 case ConditionCode::CC_GE:
108 return ConditionCode::CC_AE;
109
110 case ConditionCode::CC_B:
111 return ConditionCode::CC_LT;
112 case ConditionCode::CC_BE:
113 return ConditionCode::CC_LE;
114 case ConditionCode::CC_A:
115 return ConditionCode::CC_GT;
116 case ConditionCode::CC_AE:
117 return ConditionCode::CC_GE;
118
119 case ConditionCode::CC_TST_EQ:
120 return ConditionCode::CC_TST_EQ;
121 case ConditionCode::CC_TST_NE:
122 return ConditionCode::CC_TST_NE;
123
124 default:
125 UNREACHABLE();
126 }
127 }
128
IsSignedConditionCode(ConditionCode code)129 inline bool IsSignedConditionCode(ConditionCode code)
130 {
131 switch (code) {
132 case ConditionCode::CC_LT:
133 case ConditionCode::CC_LE:
134 case ConditionCode::CC_GT:
135 case ConditionCode::CC_GE:
136 return true;
137
138 case ConditionCode::CC_EQ:
139 case ConditionCode::CC_NE:
140 case ConditionCode::CC_B:
141 case ConditionCode::CC_BE:
142 case ConditionCode::CC_A:
143 case ConditionCode::CC_AE:
144 case ConditionCode::CC_TST_EQ:
145 case ConditionCode::CC_TST_NE:
146 return false;
147
148 default:
149 UNREACHABLE();
150 }
151 }
152
SwapOperandsConditionCode(ConditionCode code)153 inline ConditionCode SwapOperandsConditionCode(ConditionCode code)
154 {
155 switch (code) {
156 case ConditionCode::CC_EQ:
157 case ConditionCode::CC_NE:
158 return code;
159 default:
160 UNREACHABLE();
161 }
162 }
163
164 enum class Opcode {
165 INVALID = -1,
166 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
167 #define INST_DEF(opcode, ...) opcode,
168 OPCODE_LIST(INST_DEF)
169
170 #undef INST_DEF
171 NUM_OPCODES
172 };
173
174 /**
175 * Convert opcode to its string representation
176 */
177 constexpr std::array<const char *const, static_cast<size_t>(Opcode::NUM_OPCODES)> OPCODE_NAMES = {
178 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
179 #define INST_DEF(opcode, ...) #opcode,
180 OPCODE_LIST(INST_DEF)
181 #undef INST_DEF
182 };
183
GetOpcodeString(Opcode opc)184 constexpr const char *GetOpcodeString(Opcode opc)
185 {
186 ASSERT(static_cast<int>(opc) < static_cast<int>(Opcode::NUM_OPCODES));
187 return OPCODE_NAMES[static_cast<int>(opc)];
188 }
189
190 /**
191 * Instruction flags. See `instrutions.yaml` section `flags` for more information.
192 */
193 namespace inst_flags {
194 namespace internal {
195 enum FlagsIndex {
196 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
197 #define FLAG_DEF(flag) flag##_INDEX,
198 FLAGS_LIST(FLAG_DEF)
199 #undef FLAG_DEF
200 FLAGS_COUNT
201 };
202 } // namespace internal
203
204 enum Flags : uint32_t {
205 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
206 #define FLAG_DEF(flag) flag = (1U << internal::flag##_INDEX),
207 FLAGS_LIST(FLAG_DEF)
208 #undef FLAG_DEF
209 FLAGS_COUNT = internal::FLAGS_COUNT,
210 NONE = 0
211 };
212
GetFlagsMask(Opcode opcode)213 inline constexpr uintptr_t GetFlagsMask(Opcode opcode)
214 {
215 #define INST_DEF(OPCODE, BASE, FLAGS) FLAGS, // NOLINT(cppcoreguidelines-macro-usage)
216 // NOLINTNEXTLINE(hicpp-signed-bitwise)
217 constexpr std::array<uintptr_t, static_cast<int>(Opcode::NUM_OPCODES)> INST_FLAGS_TABLE = {OPCODE_LIST(INST_DEF)};
218 #undef INST_DEF
219 return INST_FLAGS_TABLE[static_cast<size_t>(opcode)];
220 }
221 } // namespace inst_flags
222
223 #ifndef NDEBUG
224 namespace inst_modes {
225 namespace internal {
226 enum ModeIndex {
227 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
228 #define MODE_DEF(mode) mode##_INDEX,
229 MODES_LIST(MODE_DEF)
230 #undef MODE_DEF
231 MODES_COUNT
232 };
233 } // namespace internal
234
235 enum Mode : uint8_t {
236 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
237 #define MODE_DEF(mode) mode = (1U << internal::mode##_INDEX),
238 MODES_LIST(MODE_DEF)
239 #undef MODE_DEF
240 MODES_COUNT = internal::MODES_COUNT,
241 };
242
GetModesMask(Opcode opcode)243 inline constexpr uint8_t GetModesMask(Opcode opcode)
244 {
245 // NOLINTNEXTLINE(hicpp-signed-bitwise)
246 constexpr std::array<uint8_t, static_cast<int>(Opcode::NUM_OPCODES)> INST_MODES_TABLE = {INST_MODES_LIST};
247 return INST_MODES_TABLE[static_cast<size_t>(opcode)];
248 }
249 } // namespace inst_modes
250 #endif
251
252 namespace internal {
253 inline constexpr std::array<const char *, ShiftType::INVALID_SHIFT + 1> SHIFT_TYPE_NAMES = {"LSL", "LSR", "ASR", "ROR",
254 "INVALID"};
255 } // namespace internal
256
GetShiftTypeStr(ShiftType type)257 inline const char *GetShiftTypeStr(ShiftType type)
258 {
259 ASSERT(type <= INVALID_SHIFT);
260 return internal::SHIFT_TYPE_NAMES[type];
261 }
262
263 /**
264 * Describes type of the object produced by an instruction.
265 */
266 class ObjectTypeInfo {
267 public:
268 using ClassType = RuntimeInterface::ClassPtr;
269
270 ObjectTypeInfo() = default;
ObjectTypeInfo(ClassType v)271 explicit ObjectTypeInfo(ClassType v) : class_(v) {}
272
273 // NOLINTNEXTLINE(google-explicit-constructor)
274 operator bool() const
275 {
276 return class_ != ClassType();
277 }
278
GetClass()279 ClassType GetClass() const
280 {
281 return class_;
282 }
283
IsValid()284 bool IsValid() const
285 {
286 return class_ != ClassType {};
287 }
288
289 private:
290 ClassType class_ {};
291 };
292
293 /**
294 * Class for storing panda bytecode's virtual register
295 */
296 class VirtualRegister final {
297 public:
298 using ValueType = uint16_t;
299 static constexpr unsigned BITS_FOR_VREG = (sizeof(ValueType) * BITS_PER_BYTE) - 1;
300 static constexpr ValueType INVALID = std::numeric_limits<ValueType>::max();
301
302 VirtualRegister() = default;
VirtualRegister(uint16_t v,bool is_acc)303 explicit VirtualRegister(uint16_t v, bool is_acc) : value_(v)
304 {
305 IsAccFlag::Set(is_acc, &value_);
306 }
307
uint16_t()308 explicit operator uint16_t() const
309 {
310 return value_;
311 }
312
Value()313 uint16_t Value() const
314 {
315 return ValueField::Get(value_);
316 }
317
IsAccumulator()318 bool IsAccumulator() const
319 {
320 return IsAccFlag::Get(value_);
321 }
322
323 private:
324 uint16_t value_ {INVALID};
325
326 using ValueField = BitField<unsigned, 0, BITS_FOR_VREG>;
327 using IsAccFlag = ValueField::NextFlag;
328 };
329
330 // How many bits will be used in Inst's bit fields for number of inputs.
331 constexpr size_t BITS_PER_INPUTS_NUM = 3;
332 // Maximum number of static inputs
333 constexpr size_t MAX_STATIC_INPUTS = (1U << BITS_PER_INPUTS_NUM) - 1;
334
335 /**
336 * Currently Input class is just a wrapper for the Inst class.
337 */
338 class Input final {
339 public:
340 Input() = default;
Input(Inst * inst)341 explicit Input(Inst *inst) : inst_(inst) {}
342
GetInst()343 Inst *GetInst()
344 {
345 return inst_;
346 }
GetInst()347 const Inst *GetInst() const
348 {
349 return inst_;
350 }
351
GetPadding(Arch arch,uint32_t inputs_count)352 static inline uint8_t GetPadding(Arch arch, uint32_t inputs_count)
353 {
354 return static_cast<uint8_t>(!Is64BitsArch(arch) && inputs_count % 2U == 1U);
355 }
356
357 private:
358 Inst *inst_ {nullptr};
359 };
360
361 /**
362 * User is a intrusive list node, thus it stores pointers to next and previous users.
363 * Also user has properties value to determine owner instruction and corresponding index of the input.
364 */
365 class User final {
366 public:
367 User() = default;
User(bool is_static,unsigned index,unsigned size)368 User(bool is_static, unsigned index, unsigned size)
369 : properties_(IsStaticFlag::Encode(is_static) | IndexField::Encode(index) | SizeField::Encode(size) |
370 BbNumField::Encode(BbNumField::MaxValue()))
371 {
372 ASSERT(index < 1U << (BITS_FOR_INDEX - 1U));
373 ASSERT(size < 1U << (BITS_FOR_SIZE - 1U));
374 }
375 ~User() = default;
376
377 // Copy/move semantic is disabled because we use tricky pointer arithmetic based on 'this' value
378 NO_COPY_SEMANTIC(User);
379 NO_MOVE_SEMANTIC(User);
380
381 Inst *GetInst();
GetInst()382 const Inst *GetInst() const
383 {
384 return const_cast<User *>(this)->GetInst();
385 }
386
387 Inst *GetInput();
388 const Inst *GetInput() const;
389
IsDynamic()390 bool IsDynamic() const
391 {
392 return !IsStaticFlag::Decode(properties_);
393 }
GetIndex()394 unsigned GetIndex() const
395 {
396 return IndexField::Decode(properties_);
397 }
GetSize()398 unsigned GetSize() const
399 {
400 return SizeField::Decode(properties_);
401 }
402
GetVirtualRegister()403 VirtualRegister GetVirtualRegister() const
404 {
405 ASSERT(IsDynamic());
406 return VirtualRegister(VregField::Decode(properties_), IsAccFlag::Decode(properties_));
407 }
408
SetVirtualRegister(VirtualRegister reg)409 void SetVirtualRegister(VirtualRegister reg)
410 {
411 static_assert(sizeof(reg) <= sizeof(uintptr_t), "Consider passing the register by reference");
412 ASSERT(IsDynamic());
413 VregField::Set(reg.Value(), &properties_);
414 IsAccFlag::Set(reg.IsAccumulator(), &properties_);
415 }
416
GetBbNum()417 uint32_t GetBbNum() const
418 {
419 ASSERT(IsDynamic());
420 return BbNumField::Decode(properties_);
421 }
422
SetBbNum(uint32_t bb_num)423 void SetBbNum(uint32_t bb_num)
424 {
425 ASSERT(IsDynamic());
426 BbNumField::Set(bb_num, &properties_);
427 }
428
GetNext()429 auto GetNext() const
430 {
431 return next_;
432 }
433
GetPrev()434 auto GetPrev() const
435 {
436 return prev_;
437 }
438
SetNext(User * next)439 void SetNext(User *next)
440 {
441 next_ = next;
442 }
443
SetPrev(User * prev)444 void SetPrev(User *prev)
445 {
446 prev_ = prev;
447 }
448
Remove()449 void Remove()
450 {
451 if (prev_ != nullptr) {
452 prev_->next_ = next_;
453 }
454 if (next_ != nullptr) {
455 next_->prev_ = prev_;
456 }
457 }
458
459 private:
460 static constexpr unsigned BITS_FOR_INDEX = 21;
461 static constexpr unsigned BITS_FOR_SIZE = BITS_FOR_INDEX;
462 static constexpr unsigned BITS_FOR_BB_NUM = 20;
463 using IndexField = BitField<unsigned, 0, BITS_FOR_INDEX>;
464 using SizeField = IndexField::NextField<unsigned, BITS_FOR_SIZE>;
465 using IsStaticFlag = SizeField::NextFlag;
466
467 using BbNumField = IsStaticFlag::NextField<uint32_t, BITS_FOR_BB_NUM>;
468
469 using VregField = IsStaticFlag::NextField<unsigned, VirtualRegister::BITS_FOR_VREG>;
470 using IsAccFlag = VregField::NextFlag;
471
472 uint64_t properties_ {0};
473 User *next_ {nullptr};
474 User *prev_ {nullptr};
475 };
476
477 /**
478 * List of users. Intended for range loop.
479 * @tparam T should be User or const User
480 */
481 template <typename T>
482 class UserList {
483 template <typename U>
484 struct UserIterator {
485 UserIterator() = default;
UserIteratorUserIterator486 explicit UserIterator(U *u) : user_(u) {}
487
488 UserIterator &operator++()
489 {
490 user_ = user_->GetNext();
491 return *this;
492 }
493 bool operator!=(const UserIterator &other)
494 {
495 return user_ != other.user_;
496 }
497 U &operator*()
498 {
499 return *user_;
500 }
501 U *operator->()
502 {
503 return user_;
504 }
505
506 private:
507 U *user_ {nullptr};
508 };
509
510 public:
511 using Iterator = UserIterator<T>;
512 using ConstIterator = UserIterator<const T>;
513 using PointerType = std::conditional_t<std::is_const_v<T>, T *const *, T **>;
514
UserList(PointerType head)515 explicit UserList(PointerType head) : head_(head) {}
516
517 // NOLINTNEXTLINE(readability-identifier-naming)
begin()518 Iterator begin()
519 {
520 return Iterator(*head_);
521 }
522 // NOLINTNEXTLINE(readability-identifier-naming)
end()523 Iterator end()
524 {
525 return Iterator(nullptr);
526 }
527 // NOLINTNEXTLINE(readability-identifier-naming)
begin()528 ConstIterator begin() const
529 {
530 return ConstIterator(*head_);
531 }
532 // NOLINTNEXTLINE(readability-identifier-naming)
end()533 ConstIterator end() const
534 {
535 return ConstIterator(nullptr);
536 }
Empty()537 bool Empty() const
538 {
539 return *head_ == nullptr;
540 }
Front()541 T &Front()
542 {
543 return **head_;
544 }
Front()545 const T &Front() const
546 {
547 return **head_;
548 }
549
550 private:
551 PointerType head_ {nullptr};
552 };
553
554 inline bool operator==(const User &lhs, const User &rhs)
555 {
556 return lhs.GetInst() == rhs.GetInst();
557 }
558
559 /**
560 * Operands class for instructions with fixed inputs count.
561 * Actually, this class do absolutely nothing except that we can get sizeof of it when allocating memory.
562 */
563 template <int N>
564 struct Operands {
565 static_assert(N < MAX_STATIC_INPUTS, "Invalid inputs number");
566
567 std::array<User, N> users;
568 std::array<Input, N> inputs;
569 };
570
571 /**
572 * Specialized version for instructions with variable inputs count.
573 * Users and inputs are stored outside of this class.
574 */
575 class DynamicOperands {
576 public:
DynamicOperands(ArenaAllocator * allocator)577 explicit DynamicOperands(ArenaAllocator *allocator) : allocator_(allocator) {}
578
Users()579 User *Users()
580 {
581 return users_;
582 }
583
Inputs()584 Input *Inputs()
585 {
586 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
587 return reinterpret_cast<Input *>(users_ + capacity_) + 1;
588 }
589
590 /// Append new input (and user accordingly)
591 unsigned Append(Inst *inst);
592
593 /// Remove input and user with index `index`.
594 void Remove(unsigned index);
595
596 /// Reallocate inputs/users storage to a new one with specified capacity.
597 void Reallocate(size_t new_capacity = 0);
598
599 /// Get instruction to which these operands belongs to.
GetOwnerInst()600 Inst *GetOwnerInst() const
601 {
602 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
603 return reinterpret_cast<Inst *>(const_cast<DynamicOperands *>(this) + 1);
604 }
605
GetUser(unsigned index)606 User *GetUser(unsigned index)
607 {
608 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
609 return &users_[capacity_ - index - 1];
610 }
611
GetInput(unsigned index)612 Input *GetInput(unsigned index)
613 {
614 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
615 return &Inputs()[index];
616 }
617
SetInput(unsigned index,Input input)618 void SetInput(unsigned index, Input input)
619 {
620 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
621 Inputs()[index] = input;
622 }
623
Size()624 size_t Size() const
625 {
626 return size_;
627 }
628
629 private:
630 User *users_ {nullptr};
631 size_t size_ {0};
632 size_t capacity_ {0};
633 ArenaAllocator *allocator_ {nullptr};
634 };
635
636 /**
637 * Base class for all instructions, should not be instantiated directly
638 */
639 class InstBase {
640 NO_COPY_SEMANTIC(InstBase);
641 NO_MOVE_SEMANTIC(InstBase);
642
643 public:
644 virtual ~InstBase() = default;
645
646 public:
delete(void * unused,size_t size)647 ALWAYS_INLINE void operator delete([[maybe_unused]] void *unused, [[maybe_unused]] size_t size)
648 {
649 UNREACHABLE();
650 }
new(size_t size,void * ptr)651 ALWAYS_INLINE void *operator new([[maybe_unused]] size_t size, void *ptr) noexcept
652 {
653 return ptr;
654 }
delete(void * unused1,void * unused2)655 ALWAYS_INLINE void operator delete([[maybe_unused]] void *unused1, [[maybe_unused]] void *unused2) noexcept {}
656
657 void *operator new([[maybe_unused]] size_t size) = delete;
658
659 protected:
660 InstBase() = default;
661 };
662
663 /**
664 * Base instruction class
665 */
666 class Inst : public MarkerSet, public InstBase {
667 public:
668 /**
669 * Create new instruction. All instructions must be created with this method.
670 * It allocates additional space before Inst object for def-use structures.
671 *
672 * @tparam InstType - concrete type of instruction, shall be derived from Inst
673 * @tparam Args - constructor arguments types
674 * @param allocator - allocator for memory allocating
675 * @param args - constructor arguments
676 * @return - new instruction
677 */
678 template <typename InstType, typename... Args>
679 [[nodiscard]] static InstType *New(ArenaAllocator *allocator, Args &&... args);
680
681 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
682 #define INST_DEF(opcode, base, ...) inline const base *CastTo##opcode() const;
683 OPCODE_LIST(INST_DEF)
684 #undef INST_DEF
685
686 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
687 #define INST_DEF(opcode, base, ...) inline base *CastTo##opcode();
OPCODE_LIST(INST_DEF)688 OPCODE_LIST(INST_DEF)
689 #undef INST_DEF
690
691 // Methods for instruction chaining inside basic blocks.
692 Inst *GetNext()
693 {
694 return next_;
695 }
GetNext()696 const Inst *GetNext() const
697 {
698 return next_;
699 }
GetPrev()700 Inst *GetPrev()
701 {
702 return prev_;
703 }
GetPrev()704 const Inst *GetPrev() const
705 {
706 return prev_;
707 }
SetNext(Inst * next)708 void SetNext(Inst *next)
709 {
710 next_ = next;
711 }
SetPrev(Inst * prev)712 void SetPrev(Inst *prev)
713 {
714 prev_ = prev;
715 }
716
717 // Id accessors
GetId()718 auto GetId() const
719 {
720 return id_;
721 }
SetId(int id)722 void SetId(int id)
723 {
724 id_ = id;
725 }
726
GetLinearNumber()727 auto GetLinearNumber() const
728 {
729 return linear_number_;
730 }
SetLinearNumber(LinearNumber number)731 void SetLinearNumber(LinearNumber number)
732 {
733 linear_number_ = number;
734 }
735
GetCloneNumber()736 auto GetCloneNumber() const
737 {
738 return clone_number_;
739 }
SetCloneNumber(int32_t number)740 void SetCloneNumber(int32_t number)
741 {
742 clone_number_ = number;
743 }
744
745 // Opcode accessors
GetOpcode()746 Opcode GetOpcode() const
747 {
748 return opcode_;
749 }
SetOpcode(Opcode opcode)750 void SetOpcode(Opcode opcode)
751 {
752 opcode_ = opcode;
753 SetField<FieldFlags>(inst_flags::GetFlagsMask(opcode));
754 }
GetOpcodeStr()755 const char *GetOpcodeStr() const
756 {
757 return GetOpcodeString(GetOpcode());
758 }
759
760 // Bytecode PC accessors
GetPc()761 uint32_t GetPc() const
762 {
763 return pc_;
764 }
SetPc(uint32_t pc)765 void SetPc(uint32_t pc)
766 {
767 pc_ = pc;
768 }
769
770 // Type accessors
GetType()771 DataType::Type GetType() const
772 {
773 return FieldType::Get(bit_fields_);
774 }
SetType(DataType::Type type)775 void SetType(DataType::Type type)
776 {
777 FieldType::Set(type, &bit_fields_);
778 }
HasType()779 bool HasType() const
780 {
781 return GetType() != DataType::Type::NO_TYPE;
782 }
783
784 // Parent basic block accessors
GetBasicBlock()785 BasicBlock *GetBasicBlock()
786 {
787 return bb_;
788 }
GetBasicBlock()789 const BasicBlock *GetBasicBlock() const
790 {
791 return bb_;
792 }
SetBasicBlock(BasicBlock * bb)793 void SetBasicBlock(BasicBlock *bb)
794 {
795 bb_ = bb;
796 }
797
798 // Instruction properties getters
IsControlFlow()799 bool IsControlFlow() const
800 {
801 return GetFlag(inst_flags::CF);
802 }
803
IsIntrinsic()804 bool IsIntrinsic() const
805 {
806 return GetOpcode() == Opcode::Intrinsic;
807 }
808
IsCall()809 bool IsCall() const
810 {
811 return GetFlag(inst_flags::CALL);
812 }
813
IsSpillFill()814 bool IsSpillFill() const
815 {
816 return GetOpcode() == Opcode::SpillFill;
817 }
818
819 bool IsAccRead() const;
820 bool IsAccWrite() const;
CanThrow()821 bool CanThrow() const
822 {
823 return GetFlag(inst_flags::CAN_THROW);
824 }
RequireState()825 bool RequireState() const
826 {
827 return GetFlag(inst_flags::REQUIRE_STATE);
828 }
829 // Returns true if the instruction not removable in DCE
IsNotRemovable()830 bool IsNotRemovable() const
831 {
832 return GetFlag(inst_flags::NO_DCE);
833 }
834
835 // Returns true if the instruction doesn't have destination register
NoDest()836 bool NoDest() const
837 {
838 return GetFlag(inst_flags::PSEUDO_DST) || GetFlag(inst_flags::NO_DST) || GetType() == DataType::VOID;
839 }
840
HasPseudoDestination()841 bool HasPseudoDestination() const
842 {
843 return GetFlag(inst_flags::PSEUDO_DST);
844 }
845
HasImplicitRuntimeCall()846 bool HasImplicitRuntimeCall() const
847 {
848 return GetFlag(inst_flags::IMPLICIT_RUNTIME_CALL);
849 }
850
CanDeoptimize()851 bool CanDeoptimize() const
852 {
853 return GetFlag(inst_flags::CAN_DEOPTIMIZE);
854 }
855
856 // Returns true if the instruction is low-level
IsLowLevel()857 bool IsLowLevel() const
858 {
859 return GetFlag(inst_flags::LOW_LEVEL);
860 }
861
862 // Returns true if the instruction not hoistable
IsNotHoistable()863 bool IsNotHoistable() const
864 {
865 return GetFlag(inst_flags::NO_HOIST);
866 }
867
868 // Returns true Cse can't be applied to the instruction
IsNotCseApplicable()869 bool IsNotCseApplicable() const
870 {
871 return GetFlag(inst_flags::NO_CSE);
872 }
873
874 // Returns true if opcode can not be moved throught runtime calls (REFERENCE type only)
IsRefSpecial()875 bool IsRefSpecial() const
876 {
877 bool result = GetFlag(inst_flags::REF_SPECIAL);
878 ASSERT(!result || GetType() == DataType::Type::REFERENCE);
879 return result;
880 }
881
882 // Returns true if the instruction is a commutative
IsCommutative()883 bool IsCommutative() const
884 {
885 return GetFlag(inst_flags::COMMUTATIVE);
886 }
887
888 // Returns true if the instruction can be used in if-conversion
IsIfConvertable()889 bool IsIfConvertable() const
890 {
891 return GetFlag(inst_flags::IFCVT);
892 }
893
894 virtual bool IsPropagateLiveness() const;
895
896 bool RequireRegMap() const;
897
GetObjectTypeInfo()898 ObjectTypeInfo GetObjectTypeInfo() const
899 {
900 return object_type_info_;
901 }
902
HasObjectTypeInfo()903 bool HasObjectTypeInfo() const
904 {
905 return object_type_info_.IsValid();
906 }
907
GetDataFlowInput(int index)908 Inst *GetDataFlowInput(int index) const
909 {
910 return GetDataFlowInput(GetInput(index).GetInst());
911 }
912 Inst *GetDataFlowInput(Inst *input_inst) const;
913
914 bool IsPrecedingInSameBlock(const Inst *other) const;
915
916 bool IsDominate(const Inst *other) const;
917
918 bool InSameBlockOrDominate(const Inst *other) const;
919
GetSaveState()920 const SaveStateInst *GetSaveState() const
921 {
922 return const_cast<Inst *>(this)->GetSaveState();
923 }
924
GetSaveState()925 SaveStateInst *GetSaveState()
926 {
927 if (!RequireState()) {
928 return nullptr;
929 }
930 if (GetInputsCount() == 0) {
931 return nullptr;
932 }
933 auto ss = GetInput(GetInputsCount() - 1).GetInst();
934 if (ss->GetOpcode() != Opcode::SaveState) {
935 return nullptr;
936 }
937 return ss->CastToSaveState();
938 }
939
SetSaveState(Inst * inst)940 void SetSaveState(Inst *inst)
941 {
942 ASSERT(RequireState());
943 SetInput(GetInputsCount() - 1, inst);
944 }
945
946 bool IsZeroRegInst() const;
947
948 /**
949 * Return instruction clone
950 */
951 virtual Inst *Clone(const Graph *targetGraph) const;
952
GetFlagsMask()953 uintptr_t GetFlagsMask() const
954 {
955 return GetField<FieldFlags>();
956 }
957
GetFlag(inst_flags::Flags flag)958 bool GetFlag(inst_flags::Flags flag) const
959 {
960 return (GetFlagsMask() & flag) != 0;
961 }
962
SetFlag(inst_flags::Flags flag)963 void SetFlag(inst_flags::Flags flag)
964 {
965 SetField<FieldFlags>(GetFlagsMask() | flag);
966 }
967
ClearFlag(inst_flags::Flags flag)968 void ClearFlag(inst_flags::Flags flag)
969 {
970 SetField<FieldFlags>(GetFlagsMask() & ~static_cast<uintptr_t>(flag));
971 }
972
973 #ifndef NDEBUG
GetModesMask()974 uint8_t GetModesMask() const
975 {
976 return inst_modes::GetModesMask(opcode_);
977 }
978
SupportsMode(inst_modes::Mode mode)979 bool SupportsMode(inst_modes::Mode mode) const
980 {
981 return (GetModesMask() & mode) != 0;
982 }
983 #endif
984
SetTerminator()985 void SetTerminator()
986 {
987 SetFlag(inst_flags::Flags::TERMINATOR);
988 }
989
990 void InsertBefore(Inst *inst);
991 void InsertAfter(Inst *inst);
992
993 /**
994 * Return true if instruction has dynamic operands storage.
995 */
IsOperandsDynamic()996 bool IsOperandsDynamic() const
997 {
998 return GetField<InputsCount>() == MAX_STATIC_INPUTS;
999 }
1000
1001 /**
1002 * Add user to the instruction.
1003 * @param user - pointer to User object
1004 */
AddUser(User * user)1005 void AddUser(User *user)
1006 {
1007 ASSERT(user && user->GetInst());
1008 user->SetNext(first_user_);
1009 user->SetPrev(nullptr);
1010 if (first_user_ != nullptr) {
1011 ASSERT(first_user_->GetPrev() == nullptr);
1012 first_user_->SetPrev(user);
1013 }
1014 first_user_ = user;
1015 }
1016
1017 /**
1018 * Remove instruction from users.
1019 * @param user - pointer to User object
1020 */
RemoveUser(User * user)1021 void RemoveUser(User *user)
1022 {
1023 ASSERT(user);
1024 ASSERT(HasUsers());
1025 if (user == first_user_) {
1026 first_user_ = user->GetNext();
1027 }
1028 user->Remove();
1029 }
1030
1031 /**
1032 * Set input instruction in specified index.
1033 * Old input will be removed.
1034 * @param index - index of input to be set
1035 * @param inst - new input instruction TODO sherstennikov: currently it can be nullptr, is it correct?
1036 */
SetInput(unsigned index,Inst * inst)1037 void SetInput(unsigned index, Inst *inst)
1038 {
1039 CHECK_LT(index, GetInputsCount());
1040 auto &input = GetInputs()[index];
1041 auto user = GetUser(index);
1042 if (input.GetInst() != nullptr && input.GetInst()->HasUsers()) {
1043 input.GetInst()->RemoveUser(user);
1044 }
1045 if (inst != nullptr) {
1046 inst->AddUser(user);
1047 }
1048 input = Input(inst);
1049 }
1050
1051 /**
1052 * Replace all inputs that points to specified instruction by new one.
1053 * @param old_input - instruction that should be replaced
1054 * @param new_input - new input instruction
1055 */
ReplaceInput(Inst * old_input,Inst * new_input)1056 void ReplaceInput(Inst *old_input, Inst *new_input)
1057 {
1058 unsigned index = 0;
1059 for (auto input : GetInputs()) {
1060 if (input.GetInst() == old_input) {
1061 SetInput(index, new_input);
1062 }
1063 index++;
1064 }
1065 }
1066
1067 /**
1068 * Replace inputs that point to this instruction by given instruction.
1069 * @param inst - new input instruction
1070 */
ReplaceUsers(Inst * inst)1071 void ReplaceUsers(Inst *inst)
1072 {
1073 ASSERT(inst != this);
1074 ASSERT(inst != nullptr);
1075 for (auto it = GetUsers().begin(); it != GetUsers().end(); it = GetUsers().begin()) {
1076 it->GetInst()->SetInput(it->GetIndex(), inst);
1077 }
1078 }
1079
1080 /**
1081 * Append input instruction.
1082 * Available only for variadic inputs instructions, such as PHI.
1083 * @param input - input instruction
1084 * @return index in inputs container where new input is placed
1085 */
AppendInput(Inst * input)1086 unsigned AppendInput(Inst *input)
1087 {
1088 ASSERT(input != nullptr);
1089 ASSERT(IsOperandsDynamic());
1090 DynamicOperands *operands = GetDynamicOperands();
1091 return operands->Append(input);
1092 }
1093
AppendInput(Input input)1094 unsigned AppendInput(Input input)
1095 {
1096 static_assert(sizeof(Input) <= sizeof(uintptr_t)); // Input become larger, so pass it by reference then
1097 return AppendInput(input.GetInst());
1098 }
1099
1100 /**
1101 * Remove input from inputs container
1102 * Available only for variadic inputs instructions, such as PHI.
1103 * @param index - index of input in inputs container
1104 */
RemoveInput(unsigned index)1105 virtual void RemoveInput(unsigned index)
1106 {
1107 ASSERT(IsOperandsDynamic());
1108 DynamicOperands *operands = GetDynamicOperands();
1109 ASSERT(index < operands->Size());
1110 operands->Remove(index);
1111 }
1112
1113 /**
1114 * Remove all inputs
1115 */
RemoveInputs()1116 void RemoveInputs()
1117 {
1118 if (UNLIKELY(IsOperandsDynamic())) {
1119 for (auto inputs_count = GetInputsCount(); inputs_count != 0; --inputs_count) {
1120 RemoveInput(inputs_count - 1);
1121 }
1122 } else {
1123 for (size_t i = 0; i < GetInputsCount(); ++i) {
1124 SetInput(i, nullptr);
1125 }
1126 }
1127 }
1128
1129 /**
1130 * Remove all users
1131 */
1132 template <bool with_inputs = false>
RemoveUsers()1133 void RemoveUsers()
1134 {
1135 auto users = GetUsers();
1136 while (!users.Empty()) {
1137 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
1138 if constexpr (with_inputs) {
1139 auto &user = users.Front();
1140 user.GetInst()->RemoveInput(user.GetIndex());
1141 // NOLINTNEXTLINE(readability-misleading-indentation)
1142 } else {
1143 RemoveUser(&users.Front());
1144 }
1145 }
1146 }
1147
1148 /**
1149 * Get input by index
1150 * @param index - index of input
1151 * @return input instruction
1152 */
GetInput(unsigned index)1153 Input GetInput(unsigned index)
1154 {
1155 ASSERT(index < GetInputsCount());
1156 return GetInputs()[index];
1157 }
1158
GetInput(unsigned index)1159 Input GetInput(unsigned index) const
1160 {
1161 ASSERT(index < GetInputsCount());
1162 return GetInputs()[index];
1163 }
1164
GetInputs()1165 Span<Input> GetInputs()
1166 {
1167 if (UNLIKELY(IsOperandsDynamic())) {
1168 DynamicOperands *operands = GetDynamicOperands();
1169 return Span<Input>(operands->Inputs(), operands->Size());
1170 }
1171
1172 auto inputs_count {GetField<InputsCount>()};
1173 return Span<Input>(
1174 reinterpret_cast<Input *>(reinterpret_cast<uintptr_t>(this) -
1175 (inputs_count + Input::GetPadding(RUNTIME_ARCH, inputs_count)) * sizeof(Input)),
1176 inputs_count);
1177 }
GetInputs()1178 Span<const Input> GetInputs() const
1179 {
1180 return Span<const Input>(const_cast<Inst *>(this)->GetInputs());
1181 }
1182
GetInputType(size_t index)1183 virtual DataType::Type GetInputType([[maybe_unused]] size_t index) const
1184 {
1185 ASSERT(index < GetInputsCount());
1186 return GetType();
1187 }
1188
GetUsers()1189 UserList<User> GetUsers()
1190 {
1191 return UserList<User>(&first_user_);
1192 }
GetUsers()1193 UserList<const User> GetUsers() const
1194 {
1195 return UserList<const User>(&first_user_);
1196 }
1197
GetInputsCount()1198 size_t GetInputsCount() const
1199 {
1200 if (UNLIKELY(IsOperandsDynamic())) {
1201 return GetDynamicOperands()->Size();
1202 }
1203 return GetInputs().Size();
1204 }
1205
HasUsers()1206 bool HasUsers() const
1207 {
1208 return first_user_ != nullptr;
1209 };
1210
HasSingleUser()1211 bool HasSingleUser() const
1212 {
1213 return first_user_ != nullptr && first_user_->GetNext() == nullptr;
1214 }
1215
1216 /// Reserve space in dataflow storage for specified inputs count
1217 void ReserveInputs(size_t capacity);
1218
SetLocation(size_t index,Location location)1219 virtual void SetLocation([[maybe_unused]] size_t index, [[maybe_unused]] Location location) {}
1220
GetLocation(size_t index)1221 virtual Location GetLocation([[maybe_unused]] size_t index) const
1222 {
1223 return Location::RequireRegister();
1224 }
1225
GetDstLocation()1226 virtual Location GetDstLocation() const
1227 {
1228 return Location::MakeRegister(GetDstReg(), GetType());
1229 }
1230
1231 template <typename Accessor>
GetField()1232 typename Accessor::ValueType GetField() const
1233 {
1234 return Accessor::Get(bit_fields_);
1235 }
1236
1237 template <typename Accessor>
SetField(typename Accessor::ValueType value)1238 void SetField(typename Accessor::ValueType value)
1239 {
1240 Accessor::Set(value, &bit_fields_);
1241 }
1242
GetAllFields()1243 uint64_t GetAllFields() const
1244 {
1245 return bit_fields_;
1246 }
1247
IsPhi()1248 bool IsPhi() const
1249 {
1250 return opcode_ == Opcode::Phi;
1251 }
1252
IsCatchPhi()1253 bool IsCatchPhi() const
1254 {
1255 return opcode_ == Opcode::CatchPhi;
1256 }
1257
IsConst()1258 bool IsConst() const
1259 {
1260 return opcode_ == Opcode::Constant;
1261 }
1262
IsParameter()1263 bool IsParameter() const
1264 {
1265 return opcode_ == Opcode::Parameter;
1266 }
1267
IsBoolConst()1268 virtual bool IsBoolConst() const
1269 {
1270 return false;
1271 }
1272
IsSaveState()1273 bool IsSaveState() const
1274 {
1275 return opcode_ == Opcode::SaveState;
1276 }
1277
IsTry()1278 bool IsTry() const
1279 {
1280 return opcode_ == Opcode::Try;
1281 }
1282
SetVnObject(VnObject * vn_obj)1283 virtual void SetVnObject([[maybe_unused]] VnObject *vn_obj) {}
1284
GetDstReg()1285 Register GetDstReg() const
1286 {
1287 return dst_reg_;
1288 }
1289
SetDstReg(Register reg)1290 void SetDstReg(Register reg)
1291 {
1292 dst_reg_ = reg;
1293 }
1294
GetVN()1295 uint32_t GetVN() const
1296 {
1297 return vn_;
1298 }
1299
SetVN(uint32_t vn)1300 void SetVN(uint32_t vn)
1301 {
1302 vn_ = vn;
1303 }
1304 void Dump(std::ostream *out, bool new_line = true) const;
1305 virtual bool DumpInputs(std::ostream *out) const;
1306 virtual void DumpOpcode(std::ostream *out) const;
1307
SetDstReg(unsigned index,Register reg)1308 virtual void SetDstReg([[maybe_unused]] unsigned index, Register reg)
1309 {
1310 ASSERT(index == 0);
1311 SetDstReg(reg);
1312 }
1313
GetDstReg(unsigned index)1314 virtual Register GetDstReg([[maybe_unused]] unsigned index) const
1315 {
1316 ASSERT(index == 0);
1317 return GetDstReg();
1318 }
1319
GetDstCount()1320 virtual size_t GetDstCount() const
1321 {
1322 return 1;
1323 }
1324
GetSrcRegIndex()1325 virtual uint32_t GetSrcRegIndex() const
1326 {
1327 return 0;
1328 }
1329
SetSrcReg(unsigned index,Register reg)1330 virtual void SetSrcReg([[maybe_unused]] unsigned index, [[maybe_unused]] Register reg) {}
1331
GetSrcReg(unsigned index)1332 virtual Register GetSrcReg([[maybe_unused]] unsigned index) const
1333 {
1334 return INVALID_REG;
1335 }
1336
GetFirstUser()1337 User *GetFirstUser() const
1338 {
1339 return first_user_;
1340 }
1341
1342 protected:
1343 using InstBase::InstBase;
1344 static constexpr int INPUT_COUNT = 0;
1345
1346 Inst() = default;
1347
Inst(Opcode opcode)1348 explicit Inst(Opcode opcode) : Inst(opcode, DataType::Type::NO_TYPE, INVALID_PC) {}
1349
Inst(Opcode opcode,DataType::Type type,uint32_t pc)1350 explicit Inst(Opcode opcode, DataType::Type type, uint32_t pc) : pc_(pc), opcode_(opcode)
1351 {
1352 bit_fields_ = inst_flags::GetFlagsMask(opcode);
1353 SetField<FieldType>(type);
1354 }
1355
1356 protected:
1357 using FieldFlags = BitField<uint32_t, 0, MinimumBitsToStore(1U << inst_flags::FLAGS_COUNT)>;
1358 using FieldType = FieldFlags::NextField<DataType::Type, MinimumBitsToStore(DataType::LAST)>;
1359 using InputsCount = FieldType::NextField<uint32_t, BITS_PER_INPUTS_NUM>;
1360 using LastField = InputsCount;
1361
GetDynamicOperands()1362 DynamicOperands *GetDynamicOperands() const
1363 {
1364 return reinterpret_cast<DynamicOperands *>(reinterpret_cast<uintptr_t>(this) - sizeof(DynamicOperands));
1365 }
1366
1367 private:
GetUser(unsigned index)1368 User *GetUser(unsigned index)
1369 {
1370 if (UNLIKELY(IsOperandsDynamic())) {
1371 return GetDynamicOperands()->GetUser(index);
1372 }
1373 auto inputs_count {GetField<InputsCount>()};
1374 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1375 return reinterpret_cast<User *>(reinterpret_cast<Input *>(this) -
1376 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1377 (inputs_count + Input::GetPadding(RUNTIME_ARCH, inputs_count))) -
1378 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1379 index - 1;
1380 }
1381
OperandsStorageSize()1382 size_t OperandsStorageSize() const
1383 {
1384 if (UNLIKELY(IsOperandsDynamic())) {
1385 return sizeof(DynamicOperands);
1386 }
1387
1388 auto inputs_count {GetField<InputsCount>()};
1389 return inputs_count * (sizeof(Input) + sizeof(User)) +
1390 Input::GetPadding(RUNTIME_ARCH, inputs_count) * sizeof(Input);
1391 }
1392
1393 private:
1394 /// Basic block this instruction belongs to
1395 BasicBlock *bb_ {nullptr};
1396
1397 /// Next instruction within basic block
1398 Inst *next_ {nullptr};
1399
1400 /// Previous instruction within basic block
1401 Inst *prev_ {nullptr};
1402
1403 /// First user in users chain
1404 User *first_user_ {nullptr};
1405
1406 /// This value hold properties of the instruction. It accessed via BitField types(f.e. FieldType).
1407 uint64_t bit_fields_ {0};
1408
1409 /// Unique id of instruction
1410 uint32_t id_ {INVALID_ID};
1411
1412 /// Unique id of instruction
1413 uint32_t vn_ {INVALID_VN};
1414
1415 /// Bytecode pc
1416 uint32_t pc_ {INVALID_PC};
1417
1418 /// Number used in cloning
1419 uint32_t clone_number_ {0};
1420
1421 /// Instruction number getting while visiting graph
1422 LinearNumber linear_number_ {INVALID_LINEAR_NUM};
1423
1424 ObjectTypeInfo object_type_info_ {};
1425
1426 /// Opcode, see opcodes.def
1427 Opcode opcode_ {Opcode::INVALID};
1428
1429 // Destination register type - defined in FieldType
1430 Register dst_reg_ {INVALID_REG};
1431 };
1432
1433 /**
1434 * Proxy class that injects new field - type of the source operands - into property field of the instruction.
1435 * Should be used when instruction has sources of the same type and type of the instruction is not match to type of
1436 * sources. Examples: Cmp, Compare
1437 * @tparam T Base instruction class after which this mixin is injected
1438 */
1439 template <typename T>
1440 class InstWithOperandsType : public T {
1441 public:
1442 using T::T;
1443
SetOperandsType(DataType::Type type)1444 void SetOperandsType(DataType::Type type)
1445 {
1446 T::template SetField<FieldOperandsType>(type);
1447 }
GetOperandsType()1448 virtual DataType::Type GetOperandsType() const
1449 {
1450 return T::template GetField<FieldOperandsType>();
1451 }
1452
1453 protected:
1454 using FieldOperandsType =
1455 typename T::LastField::template NextField<DataType::Type, MinimumBitsToStore(DataType::LAST)>;
1456 using LastField = FieldOperandsType;
1457 };
1458
1459 /**
1460 * Mixin for NeedBarrier flag.
1461 * @tparam T Base instruction class after which this mixin is injected
1462 */
1463 template <typename T>
1464 class NeedBarrierMixin : public T {
1465 public:
1466 using T::T;
1467
SetNeedBarrier(bool v)1468 void SetNeedBarrier(bool v)
1469 {
1470 T::template SetField<NeedBarrierFlag>(v);
1471 }
GetNeedBarrier()1472 bool GetNeedBarrier() const
1473 {
1474 return T::template GetField<NeedBarrierFlag>();
1475 }
1476
1477 protected:
1478 using NeedBarrierFlag = typename T::LastField::NextFlag;
1479 using LastField = NeedBarrierFlag;
1480 };
1481
1482 /**
1483 * This mixin aims to implement type id accessors.
1484 */
1485 class TypeIdMixin {
1486 public:
1487 TypeIdMixin() = default;
1488 NO_COPY_SEMANTIC(TypeIdMixin);
1489 NO_MOVE_SEMANTIC(TypeIdMixin);
1490 virtual ~TypeIdMixin() = default;
1491
SetTypeId(uint32_t id)1492 void SetTypeId(uint32_t id)
1493 {
1494 type_id_ = id;
1495 }
1496
GetTypeId()1497 auto GetTypeId() const
1498 {
1499 return type_id_;
1500 }
1501
SetMethod(RuntimeInterface::MethodPtr method)1502 void SetMethod(RuntimeInterface::MethodPtr method)
1503 {
1504 method_ = method;
1505 }
GetMethod()1506 auto GetMethod() const
1507 {
1508 return method_;
1509 }
1510
1511 private:
1512 uint32_t type_id_ {0};
1513 // The pointer to the method in which this instruction is executed(inlined method)
1514 RuntimeInterface::MethodPtr method_ {nullptr};
1515 };
1516
1517 /**
1518 * Mixin for Inlined calls/returns.
1519 */
1520 template <typename T>
1521 class InlinedInstMixin : public T {
1522 public:
1523 using T::T;
1524
SetInlined(bool v)1525 void SetInlined(bool v)
1526 {
1527 T::template SetField<IsInlinedFlag>(v);
1528 }
IsInlined()1529 bool IsInlined() const
1530 {
1531 return T::template GetField<IsInlinedFlag>();
1532 }
1533
1534 protected:
1535 using IsInlinedFlag = typename T::LastField::NextFlag;
1536 using LastField = IsInlinedFlag;
1537 };
1538
1539 /**
1540 * Mixin for instructions with immediate constant value
1541 */
1542 class ImmediateMixin {
1543 public:
ImmediateMixin(uint64_t immediate)1544 explicit ImmediateMixin(uint64_t immediate) : immediate_(immediate) {}
1545
1546 NO_COPY_SEMANTIC(ImmediateMixin);
1547 NO_MOVE_SEMANTIC(ImmediateMixin);
1548 virtual ~ImmediateMixin() = default;
1549
SetImm(uint64_t immediate)1550 void SetImm(uint64_t immediate)
1551 {
1552 immediate_ = immediate;
1553 }
GetImm()1554 auto GetImm() const
1555 {
1556 return immediate_;
1557 }
1558
1559 protected:
1560 ImmediateMixin() = default;
1561
1562 private:
1563 uint64_t immediate_ {0};
1564 };
1565
1566 /**
1567 * Mixin for instructions with ConditionCode
1568 */
1569 template <typename T>
1570 class ConditionMixin : public T {
1571 public:
1572 enum class Prediction { NONE, LIKELY, UNLIKELY, SIZE = UNLIKELY };
1573
1574 using T::T;
ConditionMixin(ConditionCode cc)1575 explicit ConditionMixin(ConditionCode cc)
1576 {
1577 T::template SetField<CcFlag>(cc);
1578 }
1579 NO_COPY_SEMANTIC(ConditionMixin);
1580 NO_MOVE_SEMANTIC(ConditionMixin);
1581 ~ConditionMixin() override = default;
1582
GetCc()1583 auto GetCc() const
1584 {
1585 return T::template GetField<CcFlag>();
1586 }
SetCc(ConditionCode cc)1587 void SetCc(ConditionCode cc)
1588 {
1589 T::template SetField<CcFlag>(cc);
1590 }
InverseConditionCode()1591 void InverseConditionCode()
1592 {
1593 SetCc(GetInverseConditionCode(GetCc()));
1594 if (IsLikely()) {
1595 SetUnlikely();
1596 } else if (IsUnlikely()) {
1597 SetLikely();
1598 }
1599 }
1600
IsLikely()1601 bool IsLikely() const
1602 {
1603 return T::template GetField<PredictionFlag>() == Prediction::LIKELY;
1604 }
IsUnlikely()1605 bool IsUnlikely() const
1606 {
1607 return T::template GetField<PredictionFlag>() == Prediction::UNLIKELY;
1608 }
SetLikely()1609 void SetLikely()
1610 {
1611 T::template SetField<PredictionFlag>(Prediction::LIKELY);
1612 }
SetUnlikely()1613 void SetUnlikely()
1614 {
1615 T::template SetField<PredictionFlag>(Prediction::UNLIKELY);
1616 }
1617
1618 protected:
1619 ConditionMixin() = default;
1620
1621 using CcFlag = typename T::LastField::template NextField<ConditionCode, MinimumBitsToStore(ConditionCode::CC_LAST)>;
1622 using PredictionFlag = typename CcFlag::template NextField<Prediction, MinimumBitsToStore(Prediction::SIZE)>;
1623 using LastField = PredictionFlag;
1624 };
1625
1626 /**
1627 * Instruction with fixed number of inputs.
1628 * Shall not be instantiated directly, only through derived classes.
1629 */
1630 template <size_t N>
1631 class FixedInputsInst : public Inst {
1632 public:
1633 using Inst::Inst;
1634
1635 static constexpr int INPUT_COUNT = N;
1636
SetSrcReg(unsigned index,Register reg)1637 void SetSrcReg(unsigned index, Register reg) override
1638 {
1639 ASSERT(index < N);
1640 src_regs_[index] = reg;
1641 }
1642
GetSrcReg(unsigned index)1643 Register GetSrcReg(unsigned index) const override
1644 {
1645 ASSERT(index < N);
1646 return src_regs_[index];
1647 }
1648
GetLocation(size_t index)1649 Location GetLocation(size_t index) const override
1650 {
1651 return Location::MakeRegister(GetSrcReg(index), GetInputType(index));
1652 }
1653
SetLocation(size_t index,Location location)1654 void SetLocation(size_t index, Location location) override
1655 {
1656 SetSrcReg(index, location.GetValue());
1657 }
1658
SetDstLocation(Location location)1659 void SetDstLocation(Location location)
1660 {
1661 SetDstReg(location.GetValue());
1662 }
1663
1664 Inst *Clone(const Graph *targetGraph) const override;
1665
1666 private:
1667 template <typename T, std::size_t... Is>
CreateArray(T value,std::index_sequence<Is...> unused)1668 constexpr auto CreateArray(T value, [[maybe_unused]] std::index_sequence<Is...> unused)
1669 {
1670 return std::array<T, sizeof...(Is)> {(static_cast<void>(Is), value)...};
1671 }
1672
1673 std::array<Register, N> src_regs_ = CreateArray(INVALID_REG, std::make_index_sequence<INPUT_COUNT>());
1674 };
1675
1676 template <size_t N>
Clone(const Graph * targetGraph)1677 Inst *FixedInputsInst<N>::Clone(const Graph *targetGraph) const
1678 {
1679 auto clone = static_cast<FixedInputsInst *>(Inst::Clone(targetGraph));
1680 #ifndef NDEBUG
1681 for (size_t i = 0; i < INPUT_COUNT; ++i) {
1682 clone->SetSrcReg(i, GetSrcReg(i));
1683 }
1684 #endif
1685 return clone;
1686 }
1687
1688 /**
1689 * Instructions with fixed static inputs
1690 * We need to explicitly declare these proxy classes because some code can't work with the templated inst classes, for
1691 * example DEFINE_INST macro.
1692 */
1693 class FixedInputsInst0 : public FixedInputsInst<0> {
1694 public:
1695 using FixedInputsInst::FixedInputsInst;
1696
1697 NO_COPY_SEMANTIC(FixedInputsInst0);
1698 NO_MOVE_SEMANTIC(FixedInputsInst0);
1699 ~FixedInputsInst0() override = default;
1700 };
1701
1702 class FixedInputsInst1 : public FixedInputsInst<1> {
1703 public:
1704 using FixedInputsInst::FixedInputsInst;
1705
1706 NO_COPY_SEMANTIC(FixedInputsInst1);
1707 NO_MOVE_SEMANTIC(FixedInputsInst1);
1708 ~FixedInputsInst1() override = default;
1709 };
1710
1711 class FixedInputsInst2 : public FixedInputsInst<2U> {
1712 public:
1713 using FixedInputsInst::FixedInputsInst;
1714
1715 NO_COPY_SEMANTIC(FixedInputsInst2);
1716 NO_MOVE_SEMANTIC(FixedInputsInst2);
1717 ~FixedInputsInst2() override = default;
1718 };
1719
1720 /**
1721 * Instruction with variable inputs count
1722 */
1723 class DynamicInputsInst : public Inst {
1724 public:
1725 using Inst::Inst;
1726
1727 static constexpr int INPUT_COUNT = MAX_STATIC_INPUTS;
1728
GetLocation(size_t index)1729 Location GetLocation(size_t index) const override
1730 {
1731 if (locations_ == nullptr) {
1732 return Location::Invalid();
1733 }
1734 return locations_->GetLocation(index);
1735 }
1736
GetDstLocation()1737 Location GetDstLocation() const override
1738 {
1739 if (locations_ == nullptr) {
1740 return Location::Invalid();
1741 }
1742 return locations_->GetDstLocation();
1743 }
1744
SetLocation(size_t index,Location location)1745 void SetLocation(size_t index, Location location) override
1746 {
1747 ASSERT(locations_ != nullptr);
1748 locations_->SetLocation(index, location);
1749 }
1750
SetDstLocation(Location location)1751 void SetDstLocation(Location location)
1752 {
1753 ASSERT(locations_ != nullptr);
1754 locations_->SetDstLocation(location);
1755 }
1756
SetLocationsInfo(LocationsInfo * info)1757 void SetLocationsInfo(LocationsInfo *info)
1758 {
1759 locations_ = info;
1760 }
1761
GetSrcReg(unsigned index)1762 Register GetSrcReg(unsigned index) const override
1763 {
1764 return GetLocation(index).GetValue();
1765 }
1766
SetSrcReg(unsigned index,Register reg)1767 void SetSrcReg(unsigned index, Register reg) override
1768 {
1769 SetLocation(index, Location::MakeRegister(reg, GetInputType(index)));
1770 }
1771
1772 private:
1773 LocationsInfo *locations_ {nullptr};
1774 };
1775
1776 class SpillFillInst;
1777
1778 /**
1779 * Mixin to hold location data
1780 */
1781 class LocationDataMixin {
1782 public:
SetLocationData(SpillFillData location_data)1783 void SetLocationData(SpillFillData location_data)
1784 {
1785 location_data_ = location_data;
1786 }
1787
GetLocationData()1788 auto GetLocationData() const
1789 {
1790 return location_data_;
1791 }
1792
GetLocationData()1793 auto &GetLocationData()
1794 {
1795 return location_data_;
1796 }
1797
1798 protected:
1799 LocationDataMixin() = default;
1800 NO_COPY_SEMANTIC(LocationDataMixin);
1801 NO_MOVE_SEMANTIC(LocationDataMixin);
1802 virtual ~LocationDataMixin() = default;
1803
1804 private:
1805 SpillFillData location_data_ {};
1806 };
1807
1808 /**
1809 * Mixin to hold input types of call instruction
1810 */
1811 class InputTypesMixin {
1812 public:
1813 InputTypesMixin() = default;
1814 NO_COPY_SEMANTIC(InputTypesMixin);
1815 NO_MOVE_SEMANTIC(InputTypesMixin);
1816 virtual ~InputTypesMixin() = default;
1817
AllocateInputTypes(ArenaAllocator * allocator,size_t capacity)1818 void AllocateInputTypes(ArenaAllocator *allocator, size_t capacity)
1819 {
1820 ASSERT(allocator != nullptr);
1821 ASSERT(input_types_ == nullptr);
1822 input_types_ = allocator->New<ArenaVector<DataType::Type>>(allocator->Adapter());
1823 ASSERT(input_types_ != nullptr);
1824 input_types_->reserve(capacity);
1825 ASSERT(input_types_->capacity() >= capacity);
1826 }
AddInputType(DataType::Type type)1827 void AddInputType(DataType::Type type)
1828 {
1829 ASSERT(input_types_ != nullptr);
1830 input_types_->push_back(type);
1831 }
GetInputTypes()1832 ArenaVector<DataType::Type> *GetInputTypes()
1833 {
1834 return input_types_;
1835 }
CloneTypes(ArenaAllocator * allocator,InputTypesMixin * target_inst)1836 void CloneTypes(ArenaAllocator *allocator, InputTypesMixin *target_inst) const
1837 {
1838 if (UNLIKELY(input_types_ == nullptr)) {
1839 return;
1840 }
1841 target_inst->AllocateInputTypes(allocator, input_types_->size());
1842 for (auto input_type : *input_types_) {
1843 target_inst->AddInputType(input_type);
1844 }
1845 }
1846
1847 protected:
1848 // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
1849 ArenaVector<DataType::Type> *input_types_ {nullptr};
1850 };
1851
1852 /**
1853 * Compare instruction
1854 */
1855 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
1856 class CompareInst : public InstWithOperandsType<ConditionMixin<FixedInputsInst2>> {
1857 public:
1858 using BaseInst = InstWithOperandsType<ConditionMixin<FixedInputsInst2>>;
1859 using BaseInst::BaseInst;
1860
CompareInst(Opcode opcode,DataType::Type type,uint32_t pc,ConditionCode cc)1861 CompareInst(Opcode opcode, DataType::Type type, uint32_t pc, ConditionCode cc) : BaseInst(opcode, type, pc)
1862 {
1863 SetCc(cc);
1864 }
1865
GetInputType(size_t index)1866 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
1867 {
1868 ASSERT(index < GetInputsCount());
1869 return GetOperandsType();
1870 }
1871 void DumpOpcode(std::ostream *out) const override;
1872
1873 void SetVnObject(VnObject *vn_obj) override;
1874
1875 Inst *Clone(const Graph *targetGraph) const override;
1876 };
1877
1878 /**
1879 * Mixin for AnyTypeMixin instructions
1880 */
1881 template <typename T>
1882 class AnyTypeMixin : public T {
1883 public:
1884 using T::T;
1885
SetAnyType(AnyBaseType any_type)1886 void SetAnyType(AnyBaseType any_type)
1887 {
1888 T::template SetField<AnyBaseTypeField>(any_type);
1889 }
1890
GetAnyType()1891 AnyBaseType GetAnyType() const
1892 {
1893 return T::template GetField<AnyBaseTypeField>();
1894 }
1895
1896 protected:
1897 using AnyBaseTypeField =
1898 typename T::LastField::template NextField<AnyBaseType, MinimumBitsToStore(AnyBaseType::COUNT)>;
1899 using LastField = AnyBaseTypeField;
1900 };
1901
1902 /**
1903 * CompareAnyTypeInst instruction
1904 */
1905 class CompareAnyTypeInst : public AnyTypeMixin<FixedInputsInst1> {
1906 public:
1907 using BaseInst = AnyTypeMixin<FixedInputsInst1>;
1908 using BaseInst::BaseInst;
1909
CompareAnyTypeInst(Opcode opcode,uint32_t pc,AnyBaseType any_type)1910 CompareAnyTypeInst(Opcode opcode, uint32_t pc, AnyBaseType any_type) : BaseInst(opcode, DataType::Type::BOOL, pc)
1911 {
1912 SetAnyType(any_type);
1913 }
1914
GetInputType(size_t index)1915 DataType::Type GetInputType(size_t index) const override
1916 {
1917 ASSERT(index < GetInputsCount());
1918 return GetInput(index).GetInst()->GetType();
1919 }
1920
1921 void DumpOpcode(std::ostream *out) const override;
1922
Clone(const Graph * targetGraph)1923 Inst *Clone(const Graph *targetGraph) const override
1924 {
1925 auto clone = FixedInputsInst::Clone(targetGraph);
1926 clone->CastToCompareAnyType()->SetAnyType(GetAnyType());
1927 return clone;
1928 }
1929 };
1930
1931 /**
1932 * CastAnyTypeValueInst instruction
1933 */
1934 class CastAnyTypeValueInst : public AnyTypeMixin<FixedInputsInst1> {
1935 public:
1936 using BaseInst = AnyTypeMixin<FixedInputsInst1>;
1937 using BaseInst::BaseInst;
1938
CastAnyTypeValueInst(Opcode opcode,uint32_t pc,AnyBaseType any_type)1939 CastAnyTypeValueInst(Opcode opcode, uint32_t pc, AnyBaseType any_type)
1940 : BaseInst(opcode, AnyBaseTypeToDataType(any_type), pc)
1941 {
1942 SetAnyType(any_type);
1943 }
1944
GetInputType(size_t index)1945 DataType::Type GetInputType(size_t index) const override
1946 {
1947 ASSERT(index < GetInputsCount());
1948 return GetInput(index).GetInst()->GetType();
1949 }
1950
GetDeducedType()1951 DataType::Type GetDeducedType() const
1952 {
1953 return AnyBaseTypeToDataType(GetAnyType());
1954 }
1955
1956 void DumpOpcode(std::ostream *out) const override;
1957
Clone(const Graph * targetGraph)1958 Inst *Clone(const Graph *targetGraph) const override
1959 {
1960 auto clone = FixedInputsInst::Clone(targetGraph)->CastToCastAnyTypeValue();
1961 AnyBaseType any_type = GetAnyType();
1962 clone->SetAnyType(any_type);
1963 clone->SetType(GetType());
1964 return clone;
1965 }
1966 };
1967
1968 /**
1969 * CastValueToAnyTypeInst instruction
1970 */
1971 class CastValueToAnyTypeInst : public AnyTypeMixin<FixedInputsInst1> {
1972 public:
1973 using BaseInst = AnyTypeMixin<FixedInputsInst1>;
1974 using BaseInst::BaseInst;
1975
CastValueToAnyTypeInst(Opcode opcode,uint32_t pc)1976 CastValueToAnyTypeInst(Opcode opcode, uint32_t pc) : BaseInst(opcode, DataType::ANY, pc) {}
1977
GetInputType(size_t index)1978 DataType::Type GetInputType(size_t index) const override
1979 {
1980 ASSERT(index < GetInputsCount());
1981 return GetInput(index).GetInst()->GetType();
1982 }
1983
1984 void DumpOpcode(std::ostream *out) const override;
1985
Clone(const Graph * targetGraph)1986 Inst *Clone(const Graph *targetGraph) const override
1987 {
1988 auto clone = FixedInputsInst::Clone(targetGraph)->CastToCastValueToAnyType();
1989 auto any_type = GetAnyType();
1990 clone->SetAnyType(any_type);
1991 clone->SetType(GetType());
1992 return clone;
1993 }
1994 };
1995
1996 /**
1997 * ConstantInst represent constant value.
1998 *
1999 * Available types: INT64, FLOAT32, FLOAT64, ANY. All integer types are stored as INT64 value.
2000 * Once type of constant is set, it can't be changed anymore.
2001 */
2002 class ConstantInst : public Inst {
2003 public:
2004 using Inst::Inst;
2005
2006 template <typename T>
Inst(Opcode::Constant)2007 explicit ConstantInst(Opcode /* unused */, T value, bool support_int32 = false) : Inst(Opcode::Constant)
2008 {
2009 ASSERT(GetTypeFromCType<T>() != DataType::NO_TYPE);
2010 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-branch-clone)
2011 if constexpr (GetTypeFromCType<T>() == DataType::FLOAT64) {
2012 value_ = bit_cast<uint64_t, double>(value);
2013 // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2014 } else if constexpr (GetTypeFromCType<T>() == DataType::FLOAT32) {
2015 value_ = bit_cast<uint32_t, float>(value);
2016 // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2017 } else if constexpr (GetTypeFromCType<T>() == DataType::ANY) {
2018 value_ = value.Raw();
2019 // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2020 } else if (GetTypeFromCType<T>(support_int32) == DataType::INT32) {
2021 value_ = static_cast<int32_t>(value);
2022 // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2023 } else {
2024 value_ = value;
2025 }
2026
2027 SetType(GetTypeFromCType<T>(support_int32));
2028 }
2029
GetRawValue()2030 uint64_t GetRawValue() const
2031 {
2032 return value_;
2033 }
2034
GetInt32Value()2035 uint32_t GetInt32Value() const
2036 {
2037 ASSERT(GetType() == DataType::INT32);
2038 return static_cast<uint32_t>(value_);
2039 }
2040
GetInt64Value()2041 uint64_t GetInt64Value() const
2042 {
2043 ASSERT(GetType() == DataType::INT64);
2044 return value_;
2045 }
2046
GetIntValue()2047 uint64_t GetIntValue() const
2048 {
2049 ASSERT(GetType() == DataType::INT64 || GetType() == DataType::INT32);
2050 return value_;
2051 }
2052
GetFloatValue()2053 float GetFloatValue() const
2054 {
2055 ASSERT(GetType() == DataType::FLOAT32);
2056 return bit_cast<float, uint32_t>(static_cast<uint32_t>(value_));
2057 }
2058
GetDoubleValue()2059 double GetDoubleValue() const
2060 {
2061 ASSERT(GetType() == DataType::FLOAT64);
2062 return bit_cast<double, uint64_t>(value_);
2063 }
2064
GetNextConst()2065 ConstantInst *GetNextConst()
2066 {
2067 return next_const_;
2068 }
SetNextConst(ConstantInst * next_const)2069 void SetNextConst(ConstantInst *next_const)
2070 {
2071 next_const_ = next_const;
2072 }
2073
2074 template <typename T>
2075 static constexpr DataType::Type GetTypeFromCType(bool support_int32 = false)
2076 {
2077 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-branch-clone)
2078 if constexpr (std::is_integral_v<T>) {
2079 if (support_int32 && sizeof(T) == sizeof(uint32_t)) {
2080 return DataType::INT32;
2081 }
2082 return DataType::INT64;
2083 // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2084 } else if constexpr (std::is_same_v<T, float>) {
2085 return DataType::FLOAT32;
2086 // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2087 } else if constexpr (std::is_same_v<T, double>) {
2088 return DataType::FLOAT64;
2089 // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2090 } else if constexpr (std::is_same_v<T, DataType::Any>) {
2091 return DataType::ANY;
2092 }
2093 return DataType::NO_TYPE;
2094 }
2095
2096 inline bool IsEqualConst(double value, [[maybe_unused]] bool support_int32 = false)
2097 {
2098 return IsEqualConst(DataType::FLOAT64, bit_cast<uint64_t, double>(value));
2099 }
2100 inline bool IsEqualConst(float value, [[maybe_unused]] bool support_int32 = false)
2101 {
2102 return IsEqualConst(DataType::FLOAT32, bit_cast<uint32_t, float>(value));
2103 }
2104 inline bool IsEqualConst(DataType::Any value, [[maybe_unused]] bool support_int32 = false)
2105 {
2106 return IsEqualConst(DataType::ANY, value.Raw());
2107 }
IsEqualConst(DataType::Type type,uint64_t value)2108 inline bool IsEqualConst(DataType::Type type, uint64_t value)
2109 {
2110 return GetType() == type && value_ == value;
2111 }
2112 template <typename T>
2113 inline bool IsEqualConst(T value, bool support_int32 = false)
2114 {
2115 static_assert(GetTypeFromCType<T>() == DataType::INT64);
2116 if (support_int32 && sizeof(T) == sizeof(uint32_t)) {
2117 return (GetType() == DataType::INT32 && static_cast<int32_t>(value_) == static_cast<int32_t>(value));
2118 }
2119 return (GetType() == DataType::INT64 && value_ == static_cast<uint64_t>(value));
2120 }
2121
2122 inline bool IsEqualConstAllTypes(int64_t value, bool support_int32 = false)
2123 {
2124 return IsEqualConst(value, support_int32) || IsEqualConst(static_cast<float>(value)) ||
2125 IsEqualConst(static_cast<double>(value));
2126 }
2127
IsBoolConst()2128 bool IsBoolConst() const override
2129 {
2130 ASSERT(IsConst());
2131 return GetType() == DataType::INT64 && (GetIntValue() == 0 || GetIntValue() == 1);
2132 }
2133
SetImmTableSlot(ImmTableSlot imm_slot)2134 void SetImmTableSlot(ImmTableSlot imm_slot)
2135 {
2136 imm_slot_ = imm_slot;
2137 }
2138
GetImmTableSlot()2139 auto GetImmTableSlot() const
2140 {
2141 return imm_slot_;
2142 }
2143
2144 bool DumpInputs(std::ostream *out) const override;
2145
2146 Inst *Clone(const Graph *targetGraph) const override;
2147
2148 private:
2149 uint64_t value_ {0};
2150 ConstantInst *next_const_ {nullptr};
2151 ImmTableSlot imm_slot_ {INVALID_IMM_TABLE_SLOT};
2152 };
2153
2154 // Type describing the purpose of the SpillFillInst.
2155 // RegAlloc may use this information to preserve correct order of several SpillFillInst
2156 // instructions placed along each other in the graph.
2157 enum SpillFillType {
2158 UNKNOWN,
2159 INPUT_FILL,
2160 CONNECT_SPLIT_SIBLINGS,
2161 SPLIT_MOVE,
2162 };
2163
2164 class SpillFillInst : public FixedInputsInst0 {
2165 public:
SpillFillInst(ArenaAllocator * allocator,Opcode opcode)2166 explicit SpillFillInst(ArenaAllocator *allocator, Opcode opcode)
2167 : FixedInputsInst0(opcode), spill_fills_(allocator->Adapter())
2168 {
2169 }
2170
AddMove(Register src,Register dst,DataType::Type type)2171 void AddMove(Register src, Register dst, DataType::Type type)
2172 {
2173 AddSpillFill(Location::MakeRegister(src, type), Location::MakeRegister(dst, type), type);
2174 }
2175
AddSpill(Register src,StackSlot dst,DataType::Type type)2176 void AddSpill(Register src, StackSlot dst, DataType::Type type)
2177 {
2178 AddSpillFill(Location::MakeRegister(src, type), Location::MakeStackSlot(dst), type);
2179 }
2180
AddFill(StackSlot src,Register dst,DataType::Type type)2181 void AddFill(StackSlot src, Register dst, DataType::Type type)
2182 {
2183 AddSpillFill(Location::MakeStackSlot(src), Location::MakeRegister(dst, type), type);
2184 }
2185
AddMemCopy(StackSlot src,StackSlot dst,DataType::Type type)2186 void AddMemCopy(StackSlot src, StackSlot dst, DataType::Type type)
2187 {
2188 AddSpillFill(Location::MakeStackSlot(src), Location::MakeStackSlot(dst), type);
2189 }
2190
AddSpillFill(const SpillFillData & spill_fill)2191 void AddSpillFill(const SpillFillData &spill_fill)
2192 {
2193 spill_fills_.emplace_back(spill_fill);
2194 }
2195
AddSpillFill(const Location & src,const Location & dst,DataType::Type type)2196 void AddSpillFill(const Location &src, const Location &dst, DataType::Type type)
2197 {
2198 spill_fills_.emplace_back(SpillFillData {src.GetKind(), dst.GetKind(), src.GetValue(), dst.GetValue(), type});
2199 }
2200
GetSpillFills()2201 const ArenaVector<SpillFillData> &GetSpillFills() const
2202 {
2203 return spill_fills_;
2204 }
2205
GetSpillFills()2206 ArenaVector<SpillFillData> &GetSpillFills()
2207 {
2208 return spill_fills_;
2209 }
2210
GetSpillFill(size_t n)2211 const SpillFillData &GetSpillFill(size_t n) const
2212 {
2213 ASSERT(n < spill_fills_.size());
2214 return spill_fills_[n];
2215 }
2216
GetSpillFill(size_t n)2217 SpillFillData &GetSpillFill(size_t n)
2218 {
2219 ASSERT(n < spill_fills_.size());
2220 return spill_fills_[n];
2221 }
2222
RemoveSpillFill(size_t n)2223 void RemoveSpillFill(size_t n)
2224 {
2225 ASSERT(n < spill_fills_.size());
2226 spill_fills_.erase(spill_fills_.begin() + n);
2227 }
2228
2229 // Get register number, holded by n-th spill-fill
GetInputReg(size_t n)2230 Register GetInputReg(size_t n) const
2231 {
2232 ASSERT(n < spill_fills_.size());
2233 ASSERT(spill_fills_[n].SrcType() == LocationType::REGISTER);
2234 return spill_fills_[n].SrcValue();
2235 }
2236
ClearSpillFills()2237 void ClearSpillFills()
2238 {
2239 spill_fills_.clear();
2240 }
2241
GetSpillFillType()2242 SpillFillType GetSpillFillType() const
2243 {
2244 return sf_type_;
2245 }
2246
SetSpillFillType(SpillFillType type)2247 void SetSpillFillType(SpillFillType type)
2248 {
2249 sf_type_ = type;
2250 }
2251
2252 bool DumpInputs(std::ostream *out) const override;
2253
2254 Inst *Clone(const Graph *targetGraph) const override;
2255
2256 private:
2257 ArenaVector<SpillFillData> spill_fills_;
2258 SpillFillType sf_type_ {UNKNOWN};
2259 };
2260
2261 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2262 class ParameterInst : public Inst, public LocationDataMixin {
2263 public:
2264 using Inst::Inst;
2265
ParameterInst(Opcode,uint16_t arg_number)2266 explicit ParameterInst(Opcode /* unused */, uint16_t arg_number) : Inst(Opcode::Parameter), arg_number_(arg_number)
2267 {
2268 }
GetArgNumber()2269 uint16_t GetArgNumber() const
2270 {
2271 return arg_number_;
2272 }
2273
SetArgNumber(uint16_t arg_number)2274 void SetArgNumber(uint16_t arg_number)
2275 {
2276 arg_number_ = arg_number;
2277 }
2278
2279 bool DumpInputs(std::ostream *out) const override;
2280
2281 Inst *Clone(const Graph *targetGraph) const override;
2282
2283 private:
2284 uint16_t arg_number_ {0};
2285 };
2286
IsZeroConstant(const Inst * inst)2287 inline bool IsZeroConstant(const Inst *inst)
2288 {
2289 return inst->IsConst() && inst->GetType() == DataType::INT64 && inst->CastToConstant()->GetIntValue() == 0;
2290 }
2291
IsZeroConstantOrNullPtr(const Inst * inst)2292 inline bool IsZeroConstantOrNullPtr(const Inst *inst)
2293 {
2294 return IsZeroConstant(inst);
2295 }
2296
2297 /**
2298 * Phi instruction
2299 */
2300 class PhiInst : public AnyTypeMixin<DynamicInputsInst> {
2301 public:
2302 using BaseInst = AnyTypeMixin<DynamicInputsInst>;
2303 using BaseInst::BaseInst;
2304 /// Get basic block corresponding to given input index. Returned pointer to basic block, can't be nullptr
2305 BasicBlock *GetPhiInputBb(unsigned index);
GetPhiInputBb(unsigned index)2306 const BasicBlock *GetPhiInputBb(unsigned index) const
2307 {
2308 return (const_cast<PhiInst *>(this))->GetPhiInputBb(index);
2309 }
2310
GetPhiInputBbNum(unsigned index)2311 uint32_t GetPhiInputBbNum(unsigned index) const
2312 {
2313 ASSERT(index < GetInputsCount());
2314 return GetDynamicOperands()->GetUser(index)->GetBbNum();
2315 }
2316
SetPhiInputBbNum(unsigned index,uint32_t bb_num)2317 void SetPhiInputBbNum(unsigned index, uint32_t bb_num)
2318 {
2319 ASSERT(index < GetInputsCount());
2320 GetDynamicOperands()->GetUser(index)->SetBbNum(bb_num);
2321 }
2322
Clone(const Graph * targetGraph)2323 Inst *Clone(const Graph *targetGraph) const override
2324 {
2325 auto clone = DynamicInputsInst::Clone(targetGraph);
2326 clone->CastToPhi()->SetAnyType(GetAnyType());
2327 return clone;
2328 }
2329
GetAssumedAnyType()2330 AnyBaseType GetAssumedAnyType()
2331 {
2332 return GetAnyType();
2333 }
2334
2335 /// Get input instruction corresponding to the given basic block, can't be null.
2336 Inst *GetPhiInput(BasicBlock *bb);
2337 Inst *GetPhiDataflowInput(BasicBlock *bb);
2338 bool DumpInputs(std::ostream *out) const override;
2339
2340 // Get index of the given block in phi inputs
2341 size_t GetPredBlockIndex(const BasicBlock *block) const;
2342
2343 protected:
2344 using FlagIsLive = LastField::NextFlag;
2345 using LastField = FlagIsLive;
2346 };
2347
2348 /**
2349 * Immediate for SavaState:
2350 * value - constant value to be stored
2351 * vreg - virtual register number
2352 */
2353 struct SaveStateImm {
2354 uint64_t value;
2355 uint16_t vreg;
2356 DataType::Type type;
2357 bool is_acc;
2358 };
2359
2360 /**
2361 * Frame state saving instruction
2362 * Aims to save pbc registers before calling something that can raise exception
2363 */
2364 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2365 class SaveStateInst : public DynamicInputsInst {
2366 public:
2367 using DynamicInputsInst::DynamicInputsInst;
2368
2369 bool DumpInputs(std::ostream *out) const override;
2370
SetVirtualRegister(size_t index,VirtualRegister reg)2371 void SetVirtualRegister(size_t index, VirtualRegister reg)
2372 {
2373 static_assert(sizeof(reg) <= sizeof(uintptr_t), "Consider passing the register by reference");
2374 ASSERT(index < GetInputsCount());
2375 GetDynamicOperands()->GetUser(index)->SetVirtualRegister(reg);
2376 }
2377
GetVirtualRegister(size_t index)2378 VirtualRegister GetVirtualRegister(size_t index) const
2379 {
2380 ASSERT(index < GetInputsCount());
2381 return GetDynamicOperands()->GetUser(index)->GetVirtualRegister();
2382 }
2383
Verify()2384 bool Verify() const
2385 {
2386 for (size_t i {0}; i < GetInputsCount(); ++i) {
2387 if (static_cast<uint16_t>(GetVirtualRegister(i)) == VirtualRegister::INVALID) {
2388 return false;
2389 }
2390 }
2391 return true;
2392 }
2393
RemoveNumericInputs()2394 bool RemoveNumericInputs()
2395 {
2396 size_t idx = 0;
2397 size_t inputs_count = GetInputsCount();
2398 bool removed = false;
2399 while (idx < inputs_count) {
2400 auto input_inst = GetInput(idx).GetInst();
2401 if (DataType::IsTypeNumeric(input_inst->GetType())) {
2402 RemoveInput(idx);
2403 inputs_count--;
2404 removed = true;
2405 } else {
2406 idx++;
2407 }
2408 }
2409 return removed;
2410 }
2411
GetInputType(size_t index)2412 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2413 {
2414 ASSERT(index < GetInputsCount());
2415 return DataType::NO_TYPE;
2416 }
GetMethod()2417 auto GetMethod() const
2418 {
2419 return method_;
2420 }
SetMethod(void * method)2421 auto SetMethod(void *method)
2422 {
2423 method_ = method;
2424 }
2425
2426 void AppendImmediate(uint64_t imm, uint16_t vreg, DataType::Type type, bool is_acc);
2427
GetImmediates()2428 const ArenaVector<SaveStateImm> *GetImmediates() const
2429 {
2430 return immediates_;
2431 }
2432
GetImmediate(size_t index)2433 const SaveStateImm &GetImmediate(size_t index) const
2434 {
2435 ASSERT(immediates_ != nullptr && index < immediates_->size());
2436 return (*immediates_)[index];
2437 }
2438
2439 void AllocateImmediates(ArenaAllocator *allocator, size_t size = 0);
2440
GetImmediatesCount()2441 size_t GetImmediatesCount() const
2442 {
2443 if (immediates_ == nullptr) {
2444 return 0;
2445 }
2446 return immediates_->size();
2447 }
2448
SetRootsRegMaskBit(size_t reg)2449 void SetRootsRegMaskBit(size_t reg)
2450 {
2451 ASSERT(reg < roots_regs_mask_.size());
2452 roots_regs_mask_.set(reg);
2453 }
2454
SetRootsStackMaskBit(size_t slot)2455 void SetRootsStackMaskBit(size_t slot)
2456 {
2457 if (roots_stack_mask_ != nullptr) {
2458 roots_stack_mask_->SetBit(slot);
2459 }
2460 }
2461
GetRootsStackMask()2462 ArenaBitVector *GetRootsStackMask()
2463 {
2464 return roots_stack_mask_;
2465 }
2466
GetRootsRegsMask()2467 auto &GetRootsRegsMask()
2468 {
2469 return roots_regs_mask_;
2470 }
2471
CreateRootsStackMask(ArenaAllocator * allocator)2472 void CreateRootsStackMask(ArenaAllocator *allocator)
2473 {
2474 ASSERT(roots_stack_mask_ == nullptr);
2475 roots_stack_mask_ = allocator->New<ArenaBitVector>(allocator);
2476 roots_stack_mask_->Reset();
2477 }
2478
2479 Inst *Clone(const Graph *targetGraph) const override;
2480 #ifndef NDEBUG
SetInputsWereDeleted()2481 void SetInputsWereDeleted()
2482 {
2483 SetField<FlagInputsWereDeleted>(true);
2484 }
2485
GetInputsWereDeleted()2486 bool GetInputsWereDeleted()
2487 {
2488 return GetField<FlagInputsWereDeleted>();
2489 }
2490 #endif
2491
2492 protected:
2493 #ifndef NDEBUG
2494 using FlagInputsWereDeleted = LastField::NextFlag;
2495 using LastField = FlagInputsWereDeleted;
2496 #endif
2497
2498 private:
2499 ArenaVector<SaveStateImm> *immediates_ {nullptr};
2500 void *method_ {nullptr};
2501 /// If instruction is in the inlined graph, this variable points to the inliner's call instruction.
2502 ArenaBitVector *roots_stack_mask_ {nullptr};
2503 std::bitset<BITS_PER_UINT32> roots_regs_mask_ {0};
2504 };
2505
2506 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2507 class IntrinsicInst : public InlinedInstMixin<DynamicInputsInst>, public InputTypesMixin {
2508 public:
2509 using Base = InlinedInstMixin<DynamicInputsInst>;
2510 using Base::Base;
2511 using IntrinsicId = RuntimeInterface::IntrinsicId;
2512
IntrinsicInst(Opcode opcode,IntrinsicId intrinsic_id)2513 IntrinsicInst(Opcode opcode, IntrinsicId intrinsic_id) : Base(opcode), intrinsic_id_(intrinsic_id) {}
2514
IntrinsicInst(Opcode opcode,DataType::Type type,uint32_t pc,IntrinsicId intrinsic_id)2515 IntrinsicInst(Opcode opcode, DataType::Type type, uint32_t pc, IntrinsicId intrinsic_id)
2516 : Base(opcode, type, pc), intrinsic_id_(intrinsic_id) {}
2517
GetIntrinsicId()2518 IntrinsicId GetIntrinsicId() const
2519 {
2520 return intrinsic_id_;
2521 }
2522
SetIntrinsicId(IntrinsicId intrinsic_id)2523 void SetIntrinsicId(IntrinsicId intrinsic_id)
2524 {
2525 intrinsic_id_ = intrinsic_id;
2526 }
2527
GetInputType(size_t index)2528 DataType::Type GetInputType(size_t index) const override
2529 {
2530 ASSERT(input_types_ != nullptr);
2531 ASSERT(index < input_types_->size());
2532 ASSERT(index < GetInputsCount());
2533 return (*input_types_)[index];
2534 }
2535
GetImms()2536 const ArenaVector<uint32_t> &GetImms()
2537 {
2538 return *imms_;
2539 }
2540
GetImms()2541 const ArenaVector<uint32_t> &GetImms() const
2542 {
2543 return *imms_;
2544 }
2545
HasImms()2546 bool HasImms() const
2547 {
2548 return imms_ != nullptr;
2549 }
2550
AddImm(ArenaAllocator * allocator,uint32_t imm)2551 void AddImm(ArenaAllocator *allocator, uint32_t imm)
2552 {
2553 if (imms_ == nullptr) {
2554 imms_ = allocator->New<ArenaVector<uint32_t>>(allocator->Adapter());
2555 }
2556 imms_->push_back(imm);
2557 }
2558
2559 bool IsNativeCall() const;
2560
HasArgumentsOnStack()2561 bool HasArgumentsOnStack() const
2562 {
2563 return GetField<ArgumentsOnStack>();
2564 }
2565
SetArgumentsOnStack()2566 void SetArgumentsOnStack()
2567 {
2568 SetField<ArgumentsOnStack>(true);
2569 }
2570
2571 Inst *Clone(const Graph *targetGraph) const override;
2572
CanBeInlined()2573 bool CanBeInlined()
2574 {
2575 return IsInlined();
2576 }
2577
SetRelocate()2578 void SetRelocate()
2579 {
2580 SetField<Relocate>(true);
2581 }
2582
GetRelocate()2583 bool GetRelocate() const
2584 {
2585 return GetField<Relocate>();
2586 }
2587
2588 void DumpOpcode(std::ostream *out) const override;
2589
2590 protected:
2591 using ArgumentsOnStack = LastField::NextFlag;
2592 using Relocate = ArgumentsOnStack::NextFlag;
2593 using LastField = Relocate;
2594
2595 private:
2596 std::string GetIntrinsicOpcodeName() const;
2597
2598 IntrinsicId intrinsic_id_ {RuntimeInterface::IntrinsicId::COUNT};
2599 ArenaVector<uint32_t> *imms_ {nullptr}; // record imms appeared in intrinsics
2600 };
2601
2602 #include <ecma_intrinsics_enum.inl>
2603
2604 /**
2605 * Cmp instruction
2606 */
2607 class CmpInst : public InstWithOperandsType<FixedInputsInst2> {
2608 public:
2609 using BaseInst = InstWithOperandsType<FixedInputsInst2>;
2610 using BaseInst::BaseInst;
2611
IsFcmpg()2612 bool IsFcmpg() const
2613 {
2614 ASSERT(DataType::IsFloatType(GetOperandsType()));
2615 return GetField<Fcmpg>();
2616 }
IsFcmpl()2617 bool IsFcmpl() const
2618 {
2619 ASSERT(DataType::IsFloatType(GetOperandsType()));
2620 return !GetField<Fcmpg>();
2621 }
SetFcmpg()2622 void SetFcmpg()
2623 {
2624 ASSERT(DataType::IsFloatType(GetOperandsType()));
2625 SetField<Fcmpg>(true);
2626 }
SetFcmpg(bool v)2627 void SetFcmpg(bool v)
2628 {
2629 ASSERT(DataType::IsFloatType(GetOperandsType()));
2630 SetField<Fcmpg>(v);
2631 }
SetFcmpl()2632 void SetFcmpl()
2633 {
2634 ASSERT(DataType::IsFloatType(GetOperandsType()));
2635 SetField<Fcmpg>(false);
2636 }
SetFcmpl(bool v)2637 void SetFcmpl(bool v)
2638 {
2639 ASSERT(DataType::IsFloatType(GetOperandsType()));
2640 SetField<Fcmpg>(!v);
2641 }
2642
GetInputType(size_t index)2643 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2644 {
2645 ASSERT(index < GetInputsCount());
2646 return GetOperandsType();
2647 }
2648
2649 void SetVnObject(VnObject *vn_obj) override;
2650
2651 void DumpOpcode(std::ostream *out) const override;
2652
2653 Inst *Clone(const Graph *targetGraph) const override;
2654
2655 protected:
2656 using Fcmpg = LastField::NextFlag;
2657 using LastField = Fcmpg;
2658 };
2659
2660 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2661 class LoadFromPool : public NeedBarrierMixin<FixedInputsInst1>, public TypeIdMixin {
2662 public:
2663 using Base = NeedBarrierMixin<FixedInputsInst1>;
2664 using Base::Base;
2665
GetInputType(size_t index)2666 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2667 {
2668 ASSERT(index < GetInputsCount());
2669 return DataType::NO_TYPE;
2670 }
2671
2672 void DumpOpcode(std::ostream *out) const override;
2673
Clone(const Graph * targetGraph)2674 Inst *Clone(const Graph *targetGraph) const override
2675 {
2676 auto clone = FixedInputsInst::Clone(targetGraph);
2677 static_cast<LoadFromPool *>(clone)->SetTypeId(GetTypeId());
2678 static_cast<LoadFromPool *>(clone)->SetMethod(GetMethod());
2679 return clone;
2680 }
2681 };
2682
2683 /**
2684 * Conditional jump instruction
2685 */
2686 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2687 class IfInst : public InstWithOperandsType<ConditionMixin<FixedInputsInst2>> {
2688 public:
2689 using Base = InstWithOperandsType<ConditionMixin<FixedInputsInst2>>;
2690 using Base::Base;
2691
IfInst(Opcode opcode,DataType::Type type,uint32_t pc,ConditionCode cc)2692 IfInst(Opcode opcode, DataType::Type type, uint32_t pc, ConditionCode cc) : Base(opcode, type, pc)
2693 {
2694 SetCc(cc);
2695 }
2696
GetInputType(size_t index)2697 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2698 {
2699 ASSERT(index < GetInputsCount());
2700 return GetOperandsType();
2701 }
2702
2703 void DumpOpcode(std::ostream *out) const override;
2704
2705 void SetVnObject(VnObject *vn_obj) override;
2706
2707 Inst *Clone(const Graph *targetGraph) const override;
2708
SetMethod(RuntimeInterface::MethodPtr method)2709 void SetMethod(RuntimeInterface::MethodPtr method)
2710 {
2711 method_ = method;
2712 }
2713
GetMethod()2714 RuntimeInterface::MethodPtr GetMethod() const
2715 {
2716 return method_;
2717 }
2718
2719 private:
2720 RuntimeInterface::MethodPtr method_ {nullptr};
2721 };
2722
2723 /**
2724 * IfImm instruction with immediate
2725 */
2726 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
2727 class IfImmInst : public InstWithOperandsType<ConditionMixin<FixedInputsInst1>>, public ImmediateMixin {
2728 public:
2729 using Base = InstWithOperandsType<ConditionMixin<FixedInputsInst1>>;
2730 using Base::Base;
2731
IfImmInst(Opcode opcode,DataType::Type type,uint32_t pc,ConditionCode cc,uint64_t imm)2732 IfImmInst(Opcode opcode, DataType::Type type, uint32_t pc, ConditionCode cc, uint64_t imm)
2733 : Base(opcode, type, pc), ImmediateMixin(imm)
2734 {
2735 SetCc(cc);
2736 }
2737
GetInputType(size_t index)2738 DataType::Type GetInputType([[maybe_unused]] size_t index) const override
2739 {
2740 ASSERT(index < GetInputsCount());
2741 return GetOperandsType();
2742 }
2743
2744 void DumpOpcode(std::ostream *out) const override;
2745 bool DumpInputs(std::ostream *out) const override;
2746 void SetVnObject(VnObject *vn_obj) override;
2747
Clone(const Graph * targetGraph)2748 Inst *Clone(const Graph *targetGraph) const override
2749 {
2750 auto clone = FixedInputsInst::Clone(targetGraph);
2751 clone->CastToIfImm()->SetCc(GetCc());
2752 clone->CastToIfImm()->SetImm(GetImm());
2753 clone->CastToIfImm()->SetOperandsType(GetOperandsType());
2754 clone->CastToIfImm()->SetMethod(GetMethod());
2755 return clone;
2756 }
2757
2758 BasicBlock *GetEdgeIfInputTrue();
2759 BasicBlock *GetEdgeIfInputFalse();
2760
SetMethod(RuntimeInterface::MethodPtr method)2761 void SetMethod(RuntimeInterface::MethodPtr method)
2762 {
2763 method_ = method;
2764 }
2765
GetMethod()2766 RuntimeInterface::MethodPtr GetMethod() const
2767 {
2768 return method_;
2769 }
2770
2771 private:
2772 size_t GetTrueInputEdgeIdx();
2773 RuntimeInterface::MethodPtr method_ {nullptr};
2774 };
2775
2776 /**
2777 * CatchPhiInst instruction
2778 */
2779 class CatchPhiInst : public DynamicInputsInst {
2780 public:
2781 using DynamicInputsInst::DynamicInputsInst;
2782
GetThrowableInsts()2783 const ArenaVector<const Inst *> *GetThrowableInsts() const
2784 {
2785 return throw_insts_;
2786 }
2787
GetThrowableInst(size_t i)2788 const Inst *GetThrowableInst(size_t i) const
2789 {
2790 ASSERT(throw_insts_ != nullptr && i < throw_insts_->size());
2791 return throw_insts_->at(i);
2792 }
2793
2794 void AppendThrowableInst(const Inst *inst);
2795 void ReplaceThrowableInst(const Inst *old_inst, const Inst *new_inst);
2796 void RemoveInput(unsigned index) override;
2797
IsAcc()2798 bool IsAcc() const
2799 {
2800 return GetField<IsAccFlag>();
2801 }
2802
SetIsAcc()2803 void SetIsAcc()
2804 {
2805 SetField<IsAccFlag>(true);
2806 }
2807
2808 protected:
2809 using IsAccFlag = LastField::NextFlag;
2810 using LastField = IsAccFlag;
2811
2812 private:
GetThrowableInstIndex(const Inst * inst)2813 size_t GetThrowableInstIndex(const Inst *inst)
2814 {
2815 ASSERT(throw_insts_ != nullptr);
2816 auto it = std::find(throw_insts_->begin(), throw_insts_->end(), inst);
2817 ASSERT(it != throw_insts_->end());
2818 return std::distance(throw_insts_->begin(), it);
2819 }
2820
2821 private:
2822 ArenaVector<const Inst *> *throw_insts_ {nullptr};
2823 };
2824
2825 class TryInst : public FixedInputsInst0 {
2826 public:
2827 using FixedInputsInst0::FixedInputsInst0;
2828
2829 void AppendCatchTypeId(uint32_t id, uint32_t catch_edge_index);
2830
GetCatchTypeIds()2831 const ArenaVector<uint32_t> *GetCatchTypeIds() const
2832 {
2833 return catch_type_ids_;
2834 }
2835
GetCatchEdgeIndexes()2836 const ArenaVector<uint32_t> *GetCatchEdgeIndexes() const
2837 {
2838 return catch_edge_indexes_;
2839 }
2840
GetCatchTypeIdsCount()2841 size_t GetCatchTypeIdsCount() const
2842 {
2843 return (catch_type_ids_ == nullptr ? 0 : catch_type_ids_->size());
2844 }
2845
2846 Inst *Clone(const Graph *targetGraph) const override;
2847
SetTryEndBlock(BasicBlock * try_end_bb)2848 void SetTryEndBlock(BasicBlock *try_end_bb)
2849 {
2850 try_end_bb_ = try_end_bb;
2851 }
2852
GetTryEndBlock()2853 BasicBlock *GetTryEndBlock() const
2854 {
2855 return try_end_bb_;
2856 }
2857
2858 private:
2859 ArenaVector<uint32_t> *catch_type_ids_ {nullptr};
2860 ArenaVector<uint32_t> *catch_edge_indexes_ {nullptr};
2861 BasicBlock *try_end_bb_ {nullptr};
2862 };
2863
2864 TryInst *GetTryBeginInst(const BasicBlock *try_begin_bb);
2865
2866 template <typename InstType, typename... Args>
New(ArenaAllocator * allocator,Args &&...args)2867 InstType *Inst::New(ArenaAllocator *allocator, Args &&... args)
2868 {
2869 static_assert(alignof(InstType) >= alignof(uintptr_t));
2870 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-branch-clone)
2871 if constexpr (std::is_same_v<InstType, SpillFillInst>) {
2872 auto data = reinterpret_cast<uintptr_t>(allocator->Alloc(sizeof(InstType), DEFAULT_ALIGNMENT));
2873 ASSERT(data != 0);
2874 return new (reinterpret_cast<void *>(data)) InstType(allocator, std::forward<Args>(args)...);
2875 // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2876 } else if constexpr (InstType::INPUT_COUNT == 0) {
2877 auto data = reinterpret_cast<uintptr_t>(allocator->Alloc(sizeof(InstType), DEFAULT_ALIGNMENT));
2878 ASSERT(data != 0);
2879 return new (reinterpret_cast<void *>(data)) InstType(std::forward<Args>(args)...);
2880 // NOLINTNEXTLINE(readability-braces-around-statements, readability-misleading-indentation)
2881 } else if constexpr (InstType::INPUT_COUNT == MAX_STATIC_INPUTS) {
2882 constexpr size_t OPERANDS_SIZE = sizeof(DynamicOperands);
2883 static_assert((OPERANDS_SIZE % alignof(InstType)) == 0);
2884 auto data = reinterpret_cast<uintptr_t>(allocator->Alloc(OPERANDS_SIZE + sizeof(InstType), DEFAULT_ALIGNMENT));
2885 ASSERT(data != 0);
2886 auto inst = new (reinterpret_cast<void *>(data + OPERANDS_SIZE)) InstType(std::forward<Args>(args)...);
2887 [[maybe_unused]] auto operands = new (reinterpret_cast<void *>(data)) DynamicOperands(allocator);
2888 static_cast<Inst *>(inst)->SetField<InputsCount>(InstType::INPUT_COUNT);
2889 return inst;
2890 } else { // NOLINT(readability-misleading-indentation)
2891 constexpr size_t OPERANDS_SIZE = sizeof(Operands<InstType::INPUT_COUNT>);
2892 constexpr auto ALIGNMENT {GetLogAlignment(alignof(Operands<InstType::INPUT_COUNT>))};
2893 static_assert((OPERANDS_SIZE % alignof(InstType)) == 0);
2894 auto data = reinterpret_cast<uintptr_t>(allocator->Alloc(OPERANDS_SIZE + sizeof(InstType), ALIGNMENT));
2895 ASSERT(data != 0);
2896 auto inst = new (reinterpret_cast<void *>(data + OPERANDS_SIZE)) InstType(std::forward<Args>(args)...);
2897 auto operands = new (reinterpret_cast<void *>(data)) Operands<InstType::INPUT_COUNT>;
2898 static_cast<Inst *>(inst)->SetField<InputsCount>(InstType::INPUT_COUNT);
2899 unsigned idx = InstType::INPUT_COUNT - 1;
2900 for (auto &user : operands->users) {
2901 new (&user) User(true, idx--, InstType::INPUT_COUNT);
2902 }
2903 return inst;
2904 }
2905 }
2906
GetInput()2907 inline Inst *User::GetInput()
2908 {
2909 return GetInst()->GetInput(GetIndex()).GetInst();
2910 }
2911
GetInput()2912 inline const Inst *User::GetInput() const
2913 {
2914 return GetInst()->GetInput(GetIndex()).GetInst();
2915 }
2916
2917 inline std::ostream &operator<<(std::ostream &os, const Inst &inst)
2918 {
2919 inst.Dump(&os, false);
2920 return os;
2921 }
2922
2923 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
2924 #define INST_DEF(opcode, base, ...) \
2925 inline const base *Inst::CastTo##opcode() const \
2926 { \
2927 ASSERT(GetOpcode() == Opcode::opcode); \
2928 return static_cast<const base *>(this); \
2929 }
2930 OPCODE_LIST(INST_DEF)
2931 #undef INST_DEF
2932
2933 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
2934 #define INST_DEF(opcode, base, ...) \
2935 inline base *Inst::CastTo##opcode() \
2936 { \
2937 ASSERT(GetOpcode() == Opcode::opcode); \
2938 return static_cast<base *>(this); \
2939 }
2940 OPCODE_LIST(INST_DEF)
2941 #undef INST_DEF
2942 } // namespace panda::compiler
2943
2944 #endif // COMPILER_OPTIMIZER_IR_INST_H
2945