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