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