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