• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef COMPILER_OPTIMIZER_CODEGEN_OPERANDS_H
17 #define COMPILER_OPTIMIZER_CODEGEN_OPERANDS_H
18 
19 /*
20 Arch-feature definitions
21 */
22 #include <bitset>
23 #include <cstdint>
24 #include <type_traits>
25 
26 #include "utils/arch.h"
27 #include "utils/arena_containers.h"
28 #include "utils/bit_field.h"
29 #include "utils/bit_utils.h"
30 #include "utils/regmask.h"
31 #include "compiler/optimizer/ir/constants.h"
32 #include "compiler/optimizer/ir/datatype.h"
33 #include "utils/type_helpers.h"
34 
35 #ifdef __clang_analyzer__
36 #ifdef PANDA_TARGET_ARM32
37 // NOLINTNEXTLINE(clang-diagnostic-macro-redefined)
38 #define __arm__
39 #endif
40 #endif
41 
42 namespace panda::compiler {
43 constexpr uint8_t BYTE_SIZE = 8;
44 constexpr uint8_t HALF_SIZE = 16;
45 constexpr uint8_t WORD_SIZE = 32;
46 constexpr uint8_t DOUBLE_WORD_SIZE = 64;
47 constexpr uint8_t HALF_WORD_SIZE_BYTES = 2;
48 constexpr uint8_t WORD_SIZE_BYTES = 4;
49 constexpr uint8_t DOUBLE_WORD_SIZE_BYTES = 8;
50 constexpr uint8_t QUAD_WORD_SIZE_BYTES = 16;
51 /// Maximum possible registers count (for scalar and for vector):
52 constexpr uint8_t MAX_NUM_REGS = 32;
53 constexpr uint8_t MAX_NUM_VREGS = 32;
54 
55 constexpr uint64_t NAN_DOUBLE = uint64_t(0x7ff8000000000000);
56 constexpr uint32_t NAN_FLOAT = uint32_t(0x7fc00000);
57 constexpr uint32_t NAN_FLOAT_BITS = NAN_FLOAT >> 16U;
58 
59 // Constants for cast from float to int64:
60 // The number of the bit from which exponential part starts in float
61 constexpr uint8_t START_EXP_FLOAT = 23;
62 // Size exponential part in float
63 constexpr uint8_t SIZE_EXP_FLOAT = 8;
64 // The maximum exponential part of float that can be loaded in int64
65 constexpr uint32_t POSSIBLE_EXP_FLOAT = 0xbe;
66 // Mask say that float number is NaN by IEEE 754
67 constexpr uint32_t UP_BITS_NAN_FLOAT = 0xff;
68 
69 // Constants for cast from double to int64:
70 // The number of the bit from which exponential part starts in double
71 constexpr uint8_t START_EXP_DOUBLE = 20;
72 // Size exponential part in double
73 constexpr uint8_t SIZE_EXP_DOUBLE = 11;
74 // The maximum exponential part of double that can be loaded in int64
75 constexpr uint32_t POSSIBLE_EXP_DOUBLE = 0x43e;
76 // Mask say that double number is NaN by IEEE 754
77 constexpr uint32_t UP_BITS_NAN_DOUBLE = 0x7ff;
78 
79 constexpr uint32_t SHIFT_BITS_DOUBLE = 12;
80 constexpr uint32_t SHIFT_BITS_FLOAT = 9;
81 
82 // Return true, if architecture can be encoded.
83 #ifdef PANDA_WITH_CODEGEN
84 bool BackendSupport(Arch arch);
85 #else
BackendSupport(Arch arch)86 inline bool BackendSupport([[maybe_unused]] Arch arch)
87 {
88     return false;
89 }
90 #endif
91 
92 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
93 #define ENCODE_MATH_LIST(DEF)   \
94     DEF(Mov, UNARY_OPERATION)   \
95     DEF(Neg, UNARY_OPERATION)   \
96     DEF(Abs, UNARY_OPERATION)   \
97     DEF(Not, UNARY_OPERATION)   \
98     DEF(Add, BINARY_OPERATION)  \
99     DEF(Sub, BINARY_OPERATION)  \
100     DEF(Mul, BINARY_OPERATION)  \
101     DEF(Shl, BINARY_OPERATION)  \
102     DEF(Shr, BINARY_OPERATION)  \
103     DEF(AShr, BINARY_OPERATION) \
104     DEF(And, BINARY_OPERATION)  \
105     DEF(Or, BINARY_OPERATION)   \
106     DEF(Xor, BINARY_OPERATION)  \
107     DEF(Sqrt, UNARY_OPERATION)
108 
109 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
110 #define ENCODE_INST_WITH_SHIFTED_OPERAND(DEF)      \
111     DEF(And, BINARY_SHIFTED_REGISTER_OPERATION)    \
112     DEF(Or, BINARY_SHIFTED_REGISTER_OPERATION)     \
113     DEF(Xor, BINARY_SHIFTED_REGISTER_OPERATION)    \
114     DEF(OrNot, BINARY_SHIFTED_REGISTER_OPERATION)  \
115     DEF(AndNot, BINARY_SHIFTED_REGISTER_OPERATION) \
116     DEF(XorNot, BINARY_SHIFTED_REGISTER_OPERATION) \
117     DEF(Add, BINARY_SHIFTED_REGISTER_OPERATION)    \
118     DEF(Sub, BINARY_SHIFTED_REGISTER_OPERATION)
119 
120 //  Arch-independent access types
121 
122 /**
123  * Template class for identify types compile-time (nortti - can't use typeid).
124  * Used in register class. Immediate class support conversion to it.
125  */
126 class TypeInfo final {
127 public:
128     enum TypeId : uint8_t { INT8 = 0, INT16 = 1, INT32 = 2, INT64 = 3, FLOAT32 = 4, FLOAT64 = 5, INVALID = 6 };
129 
130     /// Template constructor - use template parameter for create object.
131     template <class T>
TypeInfo(T)132     constexpr explicit TypeInfo(T /* unused */)
133     {
134 #ifndef __clang_analyzer__
135         static_assert(std::is_arithmetic_v<T>);
136         if constexpr (std::is_same<T, uint8_t>()) {
137             typeId_ = INT8;
138         } else if constexpr (std::is_same<T, int8_t>()) {
139             typeId_ = INT8;
140         } else if constexpr (std::is_same<T, uint16_t>()) {
141             typeId_ = INT16;
142         } else if constexpr (std::is_same<T, int16_t>()) {
143             typeId_ = INT16;
144         } else if constexpr (std::is_same<T, uint32_t>()) {
145             typeId_ = INT32;
146         } else if constexpr (std::is_same<T, int32_t>()) {
147             typeId_ = INT32;
148         } else if constexpr (std::is_same<T, uint64_t>()) {
149             typeId_ = INT64;
150         } else if constexpr (std::is_same<T, int64_t>()) {
151             typeId_ = INT64;
152         } else if constexpr (std::is_same<T, float>()) {
153             typeId_ = FLOAT32;
154         } else if constexpr (std::is_same<T, double>()) {
155             typeId_ = FLOAT64;
156         } else {
157             typeId_ = INVALID;
158         }
159 #endif
160     }
161 
TypeInfo(TypeId type)162     constexpr explicit TypeInfo(TypeId type) : typeId_(type) {}
163 
164     DEFAULT_MOVE_SEMANTIC(TypeInfo);
165     DEFAULT_COPY_SEMANTIC(TypeInfo);
166     ~TypeInfo() = default;
167 
168     /// Constructor for create invalid TypeInfo
169     constexpr TypeInfo() = default;
170 
171     /// Validation check
IsValid()172     constexpr bool IsValid() const
173     {
174         return typeId_ != INVALID;
175     }
176 
177     /// Type expected size
GetSize()178     constexpr size_t GetSize() const
179     {
180         ASSERT(IsValid());
181         switch (typeId_) {
182             case INT8:
183                 return BYTE_SIZE;
184             case INT16:
185                 return HALF_SIZE;
186             case INT32:
187             case FLOAT32:
188                 return WORD_SIZE;
189             case INT64:
190             case FLOAT64:
191                 return DOUBLE_WORD_SIZE;
192             default:
193                 return 0;
194         }
195         return 0;
196     }
197 
IsFloat()198     constexpr bool IsFloat() const
199     {
200         ASSERT(IsValid());
201         return typeId_ == FLOAT32 || typeId_ == FLOAT64;
202     }
203 
IsScalar()204     constexpr bool IsScalar() const
205     {
206         // VOID - is scalar type here
207         return !IsFloat();
208     }
209 
210     constexpr bool operator==(const TypeInfo &other) const
211     {
212         return (typeId_ == other.typeId_);
213     }
214 
215     constexpr bool operator!=(const TypeInfo &other) const
216     {
217         return !operator==(other);
218     }
219 
FromDataType(DataType::Type type,Arch arch)220     static TypeInfo FromDataType(DataType::Type type, Arch arch)
221     {
222         switch (type) {
223             case DataType::BOOL:
224             case DataType::UINT8:
225             case DataType::INT8: {
226                 return TypeInfo(INT8);
227             }
228             case DataType::UINT16:
229             case DataType::INT16: {
230                 return TypeInfo(INT16);
231             }
232             case DataType::UINT32:
233             case DataType::INT32: {
234                 return TypeInfo(INT32);
235             }
236             case DataType::UINT64:
237             case DataType::INT64:
238             case DataType::ANY: {
239                 return TypeInfo(INT64);
240             }
241             case DataType::FLOAT32: {
242                 return TypeInfo(FLOAT32);
243             }
244             case DataType::FLOAT64: {
245                 return TypeInfo(FLOAT64);
246             }
247             case DataType::REFERENCE: {
248                 return FromDataType(DataType::GetIntTypeForReference(arch), arch);
249             }
250             case DataType::POINTER: {
251                 return Is64BitsArch(arch) ? TypeInfo(INT64) : TypeInfo(INT32);
252             }
253             default:
254                 UNREACHABLE();
255         }
256     }
257 
ToDataType()258     DataType::Type ToDataType() const
259     {
260         switch (typeId_) {
261             case INT8:
262                 return DataType::INT8;
263             case INT16:
264                 return DataType::INT16;
265             case INT32:
266                 return DataType::INT32;
267             case INT64:
268                 return DataType::INT64;
269             case FLOAT32:
270                 return DataType::FLOAT32;
271             case FLOAT64:
272                 return DataType::FLOAT64;
273             default:
274                 UNREACHABLE();
275         }
276     }
277 
278     static constexpr TypeInfo GetScalarTypeBySize(size_t size);
279 
Dump()280     void Dump()
281     {
282         std::cerr << "TypeInfo:";
283         switch (typeId_) {
284             case INT8:
285                 std::cerr << "INT8";
286                 break;
287             case INT16:
288                 std::cerr << "INT16";
289                 break;
290             case INT32:
291                 std::cerr << "INT32";
292                 break;
293             case FLOAT32:
294                 std::cerr << "FLOAT32";
295                 break;
296             case INT64:
297                 std::cerr << "INT64";
298                 break;
299             case FLOAT64:
300                 std::cerr << "FLOAT64";
301                 break;
302             default:
303                 std::cerr << "INVALID";
304                 break;
305         }
306         std::cerr << ", size = " << GetSize();
307     }
308 
309 private:
310     TypeId typeId_ {INVALID};
311 };
312 
313 constexpr TypeInfo INT8_TYPE {TypeInfo::INT8};
314 constexpr TypeInfo INT16_TYPE {TypeInfo::INT16};
315 constexpr TypeInfo INT32_TYPE {TypeInfo::INT32};
316 constexpr TypeInfo INT64_TYPE {TypeInfo::INT64};
317 constexpr TypeInfo FLOAT32_TYPE {TypeInfo::FLOAT32};
318 constexpr TypeInfo FLOAT64_TYPE {TypeInfo::FLOAT64};
319 constexpr TypeInfo INVALID_TYPE;
320 
GetScalarTypeBySize(size_t sizeBits)321 constexpr TypeInfo TypeInfo::GetScalarTypeBySize(size_t sizeBits)
322 {
323     auto type = INT64_TYPE;
324     if (sizeBits == BYTE_SIZE) {
325         type = INT8_TYPE;
326     } else if (sizeBits == HALF_SIZE) {
327         type = INT16_TYPE;
328     } else if (sizeBits == WORD_SIZE) {
329         type = INT32_TYPE;
330     }
331     return type;
332 }
333 
334 // Mapping model for registers:
335 // reg-reg - support getters for small parts of registers
336 // reg-other - mapping between types of registers
337 enum RegMapping : uint32_t {
338     SCALAR_SCALAR = 1UL << 0UL,
339     SCALAR_VECTOR = 1UL << 1UL,
340     SCALAR_FLOAT = 1UL << 2UL,
341     VECTOR_VECTOR = 1UL << 3UL,
342     VECTOR_FLOAT = 1UL << 4UL,
343     FLOAT_FLOAT = 1UL << 5UL
344 };
345 
346 constexpr uint8_t INVALID_REG_ID = std::numeric_limits<uint8_t>::max();
347 constexpr uint8_t ACC_REG_ID = INVALID_REG_ID - 1U;
348 
349 class Reg final {
350 public:
351     using RegIDType = uint8_t;
352     using RegSizeType = size_t;
353 
354     constexpr Reg() = default;
355     DEFAULT_MOVE_SEMANTIC(Reg);
356     DEFAULT_COPY_SEMANTIC(Reg);
357     ~Reg() = default;
358 
359     // Default register constructor
Reg(RegIDType id,TypeInfo type)360     constexpr Reg(RegIDType id, TypeInfo type) : id_(id), type_(type) {}
361 
GetId()362     constexpr RegIDType GetId() const
363     {
364         return id_;
365     }
366 
GetMask()367     constexpr size_t GetMask() const
368     {
369         CHECK_LT(id_, 32U);
370         return (1U << id_);
371     }
372 
GetType()373     constexpr TypeInfo GetType() const
374     {
375         return type_;
376     }
377 
GetSize()378     RegSizeType GetSize() const
379     {
380         return GetType().GetSize();
381     }
382 
IsScalar()383     bool IsScalar() const
384     {
385         return GetType().IsScalar();
386     }
387 
IsFloat()388     bool IsFloat() const
389     {
390         return GetType().IsFloat();
391     }
392 
IsValid()393     constexpr bool IsValid() const
394     {
395         return type_ != INVALID_TYPE && id_ != INVALID_REG_ID;
396     }
397 
As(TypeInfo type)398     Reg As(TypeInfo type) const
399     {
400         return Reg(GetId(), type);
401     }
402 
403     constexpr bool operator==(Reg other) const
404     {
405         return (GetId() == other.GetId()) && (GetType() == other.GetType());
406     }
407 
408     constexpr bool operator!=(Reg other) const
409     {
410         return !operator==(other);
411     }
412 
Dump()413     void Dump()
414     {
415         std::cerr << " Reg: id = " << static_cast<int64_t>(id_) << ", ";
416         type_.Dump();
417         std::cerr << "\n";
418     }
419 
420 private:
421     RegIDType id_ {INVALID_REG_ID};
422     TypeInfo type_ {INVALID_TYPE};
423 };  // Reg
424 
425 constexpr Reg INVALID_REGISTER = Reg();
426 
427 static_assert(!INVALID_REGISTER.IsValid());
428 static_assert(sizeof(Reg) <= sizeof(uintptr_t));
429 
430 /**
431  * Immediate class may hold only int or float values (maybe vectors in future).
432  * It knows nothing about pointers and bools (bools maybe be in future).
433  */
434 class Imm final {
435     static constexpr size_t UNDEFINED_SIZE = 0;
436     static constexpr size_t INT64_SIZE = 64;
437     static constexpr size_t FLOAT32_SIZE = 32;
438     static constexpr size_t FLOAT64_SIZE = 64;
439 
440 public:
441     constexpr Imm() = default;
442 
443     template <typename T>
Imm(T value)444     constexpr explicit Imm(T value) : value_(static_cast<int64_t>(value))
445     {
446         using Type = std::decay_t<T>;
447         static_assert(std::is_integral_v<Type> || std::is_enum_v<Type>);
448     }
449 
450     // Partial template specialization
Imm(int64_t value)451     constexpr explicit Imm(int64_t value) : value_(value) {};
452 #ifndef NDEBUG
Imm(double value)453     constexpr explicit Imm(double value) : value_(value) {};
Imm(float value)454     constexpr explicit Imm(float value) : value_(value) {};
455 #else
Imm(double value)456     explicit Imm(double value) : value_(bit_cast<uint64_t>(value)) {};
Imm(float value)457     explicit Imm(float value) : value_(bit_cast<uint32_t>(value)) {};
458 #endif  // !NDEBUG
459 
460     DEFAULT_MOVE_SEMANTIC(Imm);
461     DEFAULT_COPY_SEMANTIC(Imm);
462     ~Imm() = default;
463 
464 #ifdef NDEBUG
GetAsInt()465     constexpr int64_t GetAsInt() const
466     {
467         return value_;
468     }
469 
GetAsFloat()470     float GetAsFloat() const
471     {
472         return bit_cast<float>(static_cast<int32_t>(value_));
473     }
474 
GetAsDouble()475     double GetAsDouble() const
476     {
477         return bit_cast<double>(value_);
478     }
479 
GetRawValue()480     constexpr int64_t GetRawValue() const
481     {
482         return value_;
483     }
484 
485 #else
GetAsInt()486     constexpr int64_t GetAsInt() const
487     {
488         ASSERT(std::holds_alternative<int64_t>(value_));
489         return std::get<int64_t>(value_);
490     }
491 
GetAsFloat()492     float GetAsFloat() const
493     {
494         ASSERT(std::holds_alternative<float>(value_));
495         return std::get<float>(value_);
496     }
497 
GetAsDouble()498     double GetAsDouble() const
499     {
500         ASSERT(std::holds_alternative<double>(value_));
501         return std::get<double>(value_);
502     }
503 
GetRawValue()504     constexpr int64_t GetRawValue() const
505     {
506         if (value_.index() == 0) {
507             UNREACHABLE();
508         } else if (value_.index() == 1) {
509             return std::get<int64_t>(value_);
510         } else if (value_.index() == 2U) {
511             return static_cast<int64_t>(bit_cast<int32_t>(std::get<float>(value_)));
512         } else if (value_.index() == 3U) {
513             return bit_cast<int64_t>(std::get<double>(value_));
514         }
515         UNREACHABLE();
516     }
517 
518     enum VariantID {
519         V_INVALID = 0,  // Pointer used for invalidate variants
520         V_INT64 = 1,
521         V_FLOAT32 = 2,
522         V_FLOAT64 = 3
523     };
524 
525     template <class T>
CheckVariantID()526     constexpr bool CheckVariantID() const
527     {
528 #ifndef __clang_analyzer__
529         // Immediate could be only signed (int/float)
530         // look at value_-type.
531         static_assert(std::is_signed_v<T>);
532         if constexpr (std::is_same<T, int64_t>()) {
533             return value_.index() == V_INT64;
534         }
535         if constexpr (std::is_same<T, float>()) {
536             return value_.index() == V_FLOAT32;
537         }
538         if constexpr (std::is_same<T, double>()) {
539             return value_.index() == V_FLOAT64;
540         }
541         return false;
542 #else
543         return true;
544 #endif  // !__clang_analyzer__
545     }
546 
IsValid()547     constexpr bool IsValid() const
548     {
549         return !std::holds_alternative<void *>(value_);
550     }
551 
GetType()552     TypeInfo GetType() const
553     {
554         switch (value_.index()) {
555             case V_INT64:
556                 return INT64_TYPE;
557             case V_FLOAT32:
558                 return FLOAT32_TYPE;
559             case V_FLOAT64:
560                 return FLOAT64_TYPE;
561             default:
562                 UNREACHABLE();
563                 return INVALID_TYPE;
564         }
565     }
566 
GetSize()567     constexpr size_t GetSize() const
568     {
569         switch (value_.index()) {
570             case V_INT64:
571                 return INT64_SIZE;
572             case V_FLOAT32:
573                 return FLOAT32_SIZE;
574             case V_FLOAT64:
575                 return FLOAT64_SIZE;
576             default:
577                 UNREACHABLE();
578                 return UNDEFINED_SIZE;
579         }
580     }
581 #endif  // NDEBUG
582 
583     bool operator==(Imm other) const
584     {
585         return value_ == other.value_;
586     }
587 
588     bool operator!=(Imm other) const
589     {
590         return !(operator==(other));
591     }
592 
593 private:
594 #ifndef NDEBUG
595     std::variant<void *, int64_t, float, double> value_ {nullptr};
596 #else
597     int64_t value_ {0};
598 #endif  // NDEBUG
599 };      // Imm
600 
601 class TypedImm final {
602 public:
603     template <typename T>
TypedImm(T imm)604     constexpr explicit TypedImm(T imm) : type_(imm), imm_(imm)
605     {
606     }
607 
GetType()608     TypeInfo GetType() const
609     {
610         return type_;
611     }
612 
GetImm()613     Imm GetImm() const
614     {
615         return imm_;
616     }
617 
618 private:
619     TypeInfo type_ {INVALID_TYPE};
620     Imm imm_ {0};
621 };
622 
623 // Why memory ref - because you may create one link for one encode-session
624 // And when you see this one - you can easy understand, what type of memory
625 //   you use. But if you load/store dirrectly address - you need to decode it
626 //   each time, when you read code
627 // model -> base + index<<scale + disp
628 class MemRef final {
629 public:
630     MemRef() = default;
631 
MemRef(Reg base)632     explicit MemRef(Reg base) : MemRef(base, 0) {}
MemRef(Reg base,ssize_t disp)633     MemRef(Reg base, ssize_t disp) : MemRef(base, INVALID_REGISTER, 0, disp) {}
MemRef(Reg base,Reg index,uint16_t scale)634     MemRef(Reg base, Reg index, uint16_t scale) : MemRef(base, index, scale, 0) {}
MemRef(Reg base,Reg index,uint16_t scale,ssize_t disp)635     MemRef(Reg base, Reg index, uint16_t scale, ssize_t disp) : disp_(disp), scale_(scale), base_(base), index_(index)
636     {
637         CHECK_LE(disp, std::numeric_limits<decltype(disp_)>::max());
638         CHECK_LE(scale, std::numeric_limits<decltype(scale_)>::max());
639     }
640     DEFAULT_MOVE_SEMANTIC(MemRef);
641     DEFAULT_COPY_SEMANTIC(MemRef);
642     ~MemRef() = default;
643 
GetBase()644     Reg GetBase() const
645     {
646         return base_;
647     }
GetIndex()648     Reg GetIndex() const
649     {
650         return index_;
651     }
GetScale()652     auto GetScale() const
653     {
654         return scale_;
655     }
GetDisp()656     auto GetDisp() const
657     {
658         return disp_;
659     }
660 
HasBase()661     bool HasBase() const
662     {
663         return base_.IsValid();
664     }
HasIndex()665     bool HasIndex() const
666     {
667         return index_.IsValid();
668     }
HasScale()669     bool HasScale() const
670     {
671         return HasIndex() && scale_ != 0;
672     }
HasDisp()673     bool HasDisp() const
674     {
675         return disp_ != 0;
676     }
677     // Ref must contain at least one of field
IsValid()678     bool IsValid() const
679     {
680         return HasBase() || HasIndex() || HasScale() || HasDisp();
681     }
682 
683     // return true if mem doesn't has index and scalar
IsOffsetMem()684     bool IsOffsetMem() const
685     {
686         return !HasIndex() && !HasScale();
687     }
688 
689     bool operator==(MemRef other) const
690     {
691         return (base_ == other.base_) && (index_ == other.index_) && (scale_ == other.scale_) && (disp_ == other.disp_);
692     }
693     bool operator!=(MemRef other) const
694     {
695         return !(operator==(other));
696     }
697 
698 private:
699     ssize_t disp_ {0};
700     uint16_t scale_ {0};
701     Reg base_ {INVALID_REGISTER};
702     Reg index_ {INVALID_REGISTER};
703 };  // MemRef
704 
ResoveParameterSequence(ArenaVector<std::pair<uint8_t,uint8_t>> * movedRegisters,uint8_t tmp,ArenaAllocator * allocator)705 inline ArenaVector<std::pair<uint8_t, uint8_t>> ResoveParameterSequence(
706     ArenaVector<std::pair<uint8_t, uint8_t>> *movedRegisters, uint8_t tmp, ArenaAllocator *allocator)
707 {
708     constexpr uint8_t INVALID_FIRST = -1;
709     constexpr uint8_t INVALID_SECOND = -2;
710 
711     movedRegisters->emplace_back(std::pair<uint8_t, uint8_t>(INVALID_FIRST, INVALID_SECOND));
712     /*
713         Example:
714         1. mov x0 <- x3
715         2. mov x1 <- x0
716         3. mov x2 <- x3
717         4. mov x3 <- x2
718         Agreement - dst-s can't hold same registers multiple times (double move to one register)
719                   - src for movs can hold same register multiply times
720 
721         Algorithm:
722             1. Find handing edges (x1 - just in dst)
723                 emit "2. mov x1 <- x0"
724                 goto 1.
725                 emit "1. mov x0 <- x3"
726             2. Assert all registers used just one time (loop from registers sequence)
727                All multiply-definitions must be resolved on previous step
728                 emit ".. mov xtmp <- x2" (strore xtmp == x3)
729                 emit "3. mov x2 <- x3"
730                 emit "4. mov x3 <- xtmp" (ASSERT(4->GetReg == x3) - there is no other possible situations here)
731     */
732     // Calculate weigth
733     ArenaVector<std::pair<uint8_t, uint8_t>> result(allocator->Adapter());
734     // --moved_registers->end() - for remove marker-element
735     for (auto pair = movedRegisters->begin(); pair != --movedRegisters->end();) {
736         auto conflict = std::find_if(movedRegisters->begin(), movedRegisters->end(), [pair](auto inPair) {
737             return (inPair.second == pair->first && (inPair != *pair));
738         });
739         if (conflict == movedRegisters->end()) {
740             // emit immediate - there are no another possible combinations
741             result.emplace_back(*pair);
742             movedRegisters->erase(pair);
743             pair = movedRegisters->begin();
744         } else {
745             ++pair;
746         }
747     }
748     // Here just loops
749     for (;;) {
750         /* Need support single mov x1 <- x1:
751            ASSERT(moved_registers->size() != 1);
752         */
753 
754         auto currPair = movedRegisters->begin();
755         if (currPair->first == INVALID_FIRST && currPair->second == INVALID_SECOND) {
756             movedRegisters->erase(currPair);
757             break;
758             // Finish algorithm - only marker in vector
759         }
760         auto savedReg = currPair->first;
761         result.emplace_back(std::pair<uint8_t, uint8_t>(tmp, currPair->first));
762         result.emplace_back(*currPair);  // we already save dst_register
763 
764         // Remove current instruction
765         auto currReg = currPair->second;
766         movedRegisters->erase(currPair);
767 
768         while (currPair != movedRegisters->end()) {
769             currPair = std::find_if(movedRegisters->begin(), movedRegisters->end(),
770                                     [currReg](auto inPair) { return inPair.first == currReg; });
771             ASSERT(currPair != movedRegisters->end());
772             if (currPair->second == savedReg) {
773                 result.emplace_back(std::pair<uint8_t, uint8_t>(currPair->first, tmp));
774                 movedRegisters->erase(currPair);
775                 break;
776             };
777             result.emplace_back(*currPair);
778             currReg = currPair->second;
779             movedRegisters->erase(currPair);
780         }
781     }
782     return result;
783 }
784 
785 // Condition also used for tell comparison registers type
786 enum Condition {
787     EQ,  // equal to 0
788     NE,  // not equal to 0
789     // signed
790     LT,  // less
791     LE,  // less than or equal
792     GT,  // greater
793     GE,  // greater than or equal
794     // unsigned - checked from registers
795     LO,  // less
796     LS,  // less than or equal
797     HI,  // greater
798     HS,  // greater than or equal
799     // Special arch-dependecy NOTE (igorban) Fix them
800     MI,  // N set            Negative
801     PL,  // N clear          Positive or zero
802     VS,  // V set            Overflow.
803     VC,  // V clear          No overflow.
804     AL,  //                  Always.
805     NV,  // Behaves as always/al.
806 
807     TST_EQ,
808     TST_NE,
809 
810     INVALID_COND
811 };
812 
IsTestCc(Condition cond)813 inline bool IsTestCc(Condition cond)
814 {
815     return cond == TST_EQ || cond == TST_NE;
816 }
817 
818 class Shift final {
819 public:
Shift(Reg base,ShiftType type,uint32_t scale)820     explicit Shift(Reg base, ShiftType type, uint32_t scale) : scale_(scale), base_(base), type_(type) {}
Shift(Reg base,uint32_t scale)821     explicit Shift(Reg base, uint32_t scale) : Shift(base, ShiftType::LSL, scale) {}
822 
823     DEFAULT_MOVE_SEMANTIC(Shift);
824     DEFAULT_COPY_SEMANTIC(Shift);
825     ~Shift() = default;
826 
GetBase()827     Reg GetBase() const
828     {
829         return base_;
830     }
831 
GetType()832     ShiftType GetType() const
833     {
834         return type_;
835     }
836 
GetScale()837     uint32_t GetScale() const
838     {
839         return scale_;
840     }
841 
842 private:
843     uint32_t scale_ {0};
844     Reg base_;
845     ShiftType type_ {INVALID_SHIFT};
846 };
847 
848 }  // namespace panda::compiler
849 #endif  // COMPILER_OPTIMIZER_CODEGEN_REGISTERS_H
850