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