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_TARGET_AARCH32_TARGET_H_
17 #define COMPILER_OPTIMIZER_CODEGEN_TARGET_AARCH32_TARGET_H_
18
19 #include "operands.h"
20 #include "encode.h"
21 #include "callconv.h"
22 #include "target_info.h"
23
24 #ifdef USE_VIXL_ARM32
25 #include "aarch32/constants-aarch32.h"
26 #include "aarch32/instructions-aarch32.h"
27 #include "aarch32/macro-assembler-aarch32.h"
28 #else
29 #error "Wrong build type, please add VIXL in build"
30 #endif // USE_VIXL_ARM32
31
32 namespace panda::compiler::aarch32 {
33 constexpr uint32_t AVAILABLE_DOUBLE_WORD_REGISTERS = 4;
34
35 static inline constexpr const uint8_t UNDEF_REG = std::numeric_limits<uint8_t>::max();
36
37 const size_t AARCH32_COUNT_REG = 3;
38 const size_t AARCH32_COUNT_VREG = 2;
39
40 const size_t MAX_SCALAR_PARAM_ID = 3; // r0-r3
41 const size_t MAX_VECTOR_SINGLE_PARAM_ID = 15; // s0-s15
42 const size_t MAX_VECTOR_DOUBLE_PARAM_ID = 7; // d0-d7
43
44 // Temporary registers used (r12 already used by vixl)
45 // r11 is used as FP register for frames
46 const std::array<unsigned, AARCH32_COUNT_REG> AARCH32_TMP_REG = {
47 vixl::aarch32::r8.GetCode(), vixl::aarch32::r9.GetCode(), vixl::aarch32::r12.GetCode()};
48
49 // Temporary vector registers used
50 const std::array<unsigned, AARCH32_COUNT_VREG> AARCH32_TMP_VREG = {vixl::aarch32::s14.GetCode(),
51 vixl::aarch32::s15.GetCode()};
52
IsConditionSigned(Condition cc)53 static inline bool IsConditionSigned(Condition cc)
54 {
55 switch (cc) {
56 case Condition::LT:
57 case Condition::LE:
58 case Condition::GT:
59 case Condition::GE:
60 return true;
61
62 default:
63 return false;
64 }
65 }
66
67 /**
68 * Converters
69 */
Convert(const Condition CC)70 static inline vixl::aarch32::Condition Convert(const Condition CC)
71 {
72 switch (CC) {
73 case Condition::EQ:
74 return vixl::aarch32::eq;
75 case Condition::NE:
76 return vixl::aarch32::ne;
77 case Condition::LT:
78 return vixl::aarch32::lt;
79 case Condition::GT:
80 return vixl::aarch32::gt;
81 case Condition::LE:
82 return vixl::aarch32::le;
83 case Condition::GE:
84 return vixl::aarch32::ge;
85 case Condition::LO:
86 return vixl::aarch32::lo;
87 case Condition::LS:
88 return vixl::aarch32::ls;
89 case Condition::HI:
90 return vixl::aarch32::hi;
91 case Condition::HS:
92 return vixl::aarch32::hs;
93 // TODO(igorban) : Remove them
94 case Condition::MI:
95 return vixl::aarch32::mi;
96 case Condition::PL:
97 return vixl::aarch32::pl;
98 case Condition::VS:
99 return vixl::aarch32::vs;
100 case Condition::VC:
101 return vixl::aarch32::vc;
102 case Condition::AL:
103 return vixl::aarch32::al;
104 default:
105 UNREACHABLE();
106 return vixl::aarch32::eq;
107 }
108 }
109
110 /**
111 * Converters
112 */
ConvertTest(const Condition CC)113 static inline vixl::aarch32::Condition ConvertTest(const Condition CC)
114 {
115 ASSERT(CC == Condition::TST_EQ || CC == Condition::TST_NE);
116 return CC == Condition::TST_EQ ? vixl::aarch32::eq : vixl::aarch32::ne;
117 }
118
VixlReg(Reg reg)119 static inline vixl::aarch32::Register VixlReg(Reg reg)
120 {
121 ASSERT(reg.IsValid());
122 if (reg.IsScalar()) {
123 auto vixl_reg = vixl::aarch32::Register(reg.GetId());
124 ASSERT(vixl_reg.IsValid());
125 return vixl_reg;
126 }
127 // Unsupported register type
128 UNREACHABLE();
129 return vixl::aarch32::Register();
130 }
131
132 // Upper half-part for register
VixlRegU(Reg reg)133 static inline vixl::aarch32::Register VixlRegU(Reg reg)
134 {
135 ASSERT(reg.IsValid());
136 if (reg.IsScalar()) {
137 auto vixl_reg = vixl::aarch32::Register(reg.GetId() + 1);
138 ASSERT(reg.GetId() <= AVAILABLE_DOUBLE_WORD_REGISTERS * 2U);
139 ASSERT(vixl_reg.IsValid());
140 return vixl_reg;
141 }
142 // Unsupported register type
143 UNREACHABLE();
144 return vixl::aarch32::Register();
145 }
146
VixlVReg(Reg reg)147 static inline vixl::aarch32::VRegister VixlVReg(Reg reg)
148 {
149 ASSERT(reg.IsValid());
150 ASSERT(reg.IsFloat());
151 if (reg.GetSize() == WORD_SIZE) {
152 // Aarch32 Vreg map double regs for 2 single-word registers
153 auto vixl_vreg = vixl::aarch32::SRegister(reg.GetId());
154 ASSERT(vixl_vreg.IsValid());
155 return vixl_vreg;
156 }
157 ASSERT(reg.GetSize() == DOUBLE_WORD_SIZE);
158 ASSERT(reg.GetId() % 2U == 0);
159 auto vixl_vreg = vixl::aarch32::DRegister(reg.GetId() / 2U);
160 ASSERT(vixl_vreg.IsValid());
161 return vixl_vreg;
162 }
163
VixlImm(const int32_t IMM)164 static inline vixl::aarch32::Operand VixlImm(const int32_t IMM)
165 {
166 return vixl::aarch32::Operand(IMM);
167 }
168
VixlNeonImm(const float IMM)169 static inline vixl::aarch32::NeonImmediate VixlNeonImm(const float IMM)
170 {
171 return vixl::aarch32::NeonImmediate(IMM);
172 }
173
VixlNeonImm(const double IMM)174 static inline vixl::aarch32::NeonImmediate VixlNeonImm(const double IMM)
175 {
176 return vixl::aarch32::NeonImmediate(IMM);
177 }
178
179 static inline vixl::aarch32::DataType Convert(const TypeInfo INFO, const bool IS_SIGNED = false)
180 {
181 if (!IS_SIGNED) {
182 if (INFO.IsFloat()) {
183 if (INFO.GetSize() == WORD_SIZE) {
184 return vixl::aarch32::DataType(vixl::aarch32::DataTypeValue::F32);
185 }
186 ASSERT(INFO.GetSize() == DOUBLE_WORD_SIZE);
187 return vixl::aarch32::DataType(vixl::aarch32::DataTypeValue::F64);
188 }
189 switch (INFO.GetSize()) {
190 case BYTE_SIZE:
191 return vixl::aarch32::DataType(vixl::aarch32::DataTypeValue::I8);
192 case HALF_SIZE:
193 return vixl::aarch32::DataType(vixl::aarch32::DataTypeValue::I16);
194 case WORD_SIZE:
195 return vixl::aarch32::DataType(vixl::aarch32::DataTypeValue::I32);
196 case DOUBLE_WORD_SIZE:
197 return vixl::aarch32::DataType(vixl::aarch32::DataTypeValue::I64);
198 default:
199 break;
200 }
201 }
202 ASSERT(!INFO.IsFloat());
203
204 switch (INFO.GetSize()) {
205 case BYTE_SIZE:
206 return vixl::aarch32::DataType(vixl::aarch32::DataTypeValue::S8);
207 case HALF_SIZE:
208 return vixl::aarch32::DataType(vixl::aarch32::DataTypeValue::S16);
209 case WORD_SIZE:
210 return vixl::aarch32::DataType(vixl::aarch32::DataTypeValue::S32);
211 case DOUBLE_WORD_SIZE:
212 return vixl::aarch32::DataType(vixl::aarch32::DataTypeValue::S64);
213 default:
214 break;
215 }
216 return vixl::aarch32::DataType(vixl::aarch32::DataTypeValue::kDataTypeValueInvalid);
217 }
218
VixlImm(Imm imm)219 static inline vixl::aarch32::Operand VixlImm(Imm imm)
220 {
221 ASSERT(imm.IsValid());
222 // Unsupported 64-bit values - force cast
223 if (imm.GetType() == INT64_TYPE) {
224 auto data = static_cast<int32_t>(imm.GetValue<int64_t>());
225 return vixl::aarch32::Operand(data);
226 }
227 if (imm.GetType() == INT32_TYPE) {
228 return vixl::aarch32::Operand(imm.GetValue<int32_t>());
229 }
230 if (imm.GetType() == INT16_TYPE) {
231 return vixl::aarch32::Operand(imm.GetValue<int16_t>());
232 }
233 if (imm.GetType() == INT8_TYPE) {
234 return vixl::aarch32::Operand(imm.GetValue<int8_t>());
235 }
236 if (imm.GetType() == FLOAT32_TYPE) {
237 auto data = bit_cast<int32_t>(imm.GetValue<float>());
238 return vixl::aarch32::Operand(data);
239 }
240 if (imm.GetType() == FLOAT64_TYPE) {
241 auto data = static_cast<int32_t>(bit_cast<int64_t>(imm.GetValue<double>()));
242 return vixl::aarch32::Operand(data);
243 }
244 // Invalid converted register
245 UNREACHABLE();
246 return vixl::aarch32::Operand(imm.GetValue<int8_t>());
247 }
248
249 // Upper half for immediate
VixlImmU(Imm imm)250 static inline vixl::aarch32::Operand VixlImmU(Imm imm)
251 {
252 ASSERT(imm.IsValid());
253 // Unsupported 64-bit values - force cast
254 if (imm.GetType() == INT64_TYPE) {
255 // NOLINTNEXTLINE(hicpp-signed-bitwise)
256 auto data = static_cast<int32_t>(imm.GetValue<int64_t>() >> WORD_SIZE);
257 return vixl::aarch32::Operand(data);
258 }
259 if (imm.GetType() == FLOAT64_TYPE) {
260 auto val = bit_cast<int64_t>(imm.GetValue<double>());
261 // NOLINTNEXTLINE(hicpp-signed-bitwise)
262 auto data = static_cast<int32_t>(val >> WORD_SIZE);
263 return vixl::aarch32::Operand(data);
264 }
265
266 return vixl::aarch32::Operand(0x0);
267 }
268
VixlDImm(Imm imm)269 static inline vixl::aarch32::DOperand VixlDImm(Imm imm)
270 {
271 ASSERT(imm.IsValid());
272 if (imm.GetType() == INT64_TYPE) {
273 auto data = imm.GetValue<int64_t>();
274 return vixl::aarch32::DOperand(data);
275 }
276 if (imm.GetType() == INT32_TYPE) {
277 return vixl::aarch32::DOperand(imm.GetValue<int32_t>());
278 }
279 if (imm.GetType() == INT16_TYPE) {
280 return vixl::aarch32::DOperand(imm.GetValue<int16_t>());
281 }
282 if (imm.GetType() == INT8_TYPE) {
283 return vixl::aarch32::DOperand(imm.GetValue<int8_t>());
284 }
285 // Invalid converted register
286 UNREACHABLE();
287 return vixl::aarch32::DOperand(imm.GetValue<int8_t>());
288 }
289
290 class Aarch32RegisterDescription final : public RegistersDescription {
291 // r4-r10 - "0000011111110000"
292 const RegMask CALLEE_SAVED = RegMask(GetCalleeRegsMask(Arch::AARCH32, false));
293 // s16-s31 - "11111111111111110000000000000000"
294 const VRegMask CALLEE_SAVEDV = VRegMask(GetCalleeRegsMask(Arch::AARCH32, true));
295 // r0-r3 - "0000000000001111"
296 const RegMask CALLER_SAVED = RegMask(GetCallerRegsMask(Arch::AARCH32, false));
297 // s0-s15 - "00000000000000001111111111111111"
298 const VRegMask CALLER_SAVEDV = VRegMask(GetCallerRegsMask(Arch::AARCH32, true));
299
300 public:
301 explicit Aarch32RegisterDescription(ArenaAllocator *allocator);
302 NO_MOVE_SEMANTIC(Aarch32RegisterDescription);
303 NO_COPY_SEMANTIC(Aarch32RegisterDescription);
304 ~Aarch32RegisterDescription() override = default;
305
306 ArenaVector<Reg> GetCalleeSaved() override;
307 void SetCalleeSaved(const ArenaVector<Reg> ®s) override;
308
309 // Set used regs - change GetCallee
310 void SetUsedRegs(const ArenaVector<Reg> ®s) override;
311
GetCallerSavedRegMask()312 RegMask GetCallerSavedRegMask() const override
313 {
314 return caller_saved_;
315 }
316
GetCallerSavedVRegMask()317 VRegMask GetCallerSavedVRegMask() const override
318 {
319 return caller_savedv_;
320 }
321
IsCalleeRegister(Reg reg)322 bool IsCalleeRegister(Reg reg) override
323 {
324 bool is_fp = reg.IsFloat();
325 return reg.GetId() >= GetFirstCalleeReg(Arch::AARCH32, is_fp) &&
326 reg.GetId() <= GetLastCalleeReg(Arch::AARCH32, is_fp);
327 }
328
GetZeroReg()329 Reg GetZeroReg() const override
330 {
331 return INVALID_REGISTER;
332 }
333
IsZeroReg(Reg reg)334 bool IsZeroReg([[maybe_unused]] Reg reg) const override
335 {
336 return false;
337 }
338
GetTempReg()339 Reg::RegIDType GetTempReg() override
340 {
341 return INVALID_REG_ID;
342 }
343
GetTempVReg()344 Reg::RegIDType GetTempVReg() override
345 {
346 return INVALID_REG_ID;
347 }
348
349 // Reg Mask
350 // r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15
351 // -dwr0,-dwr1,-dwr2,-dwr3,-dwr4,---dwr8,---dwr6,---dwr7 <- double word
352 // r0,r1,r2,r3,r4,r5,r6,r7,r8,r9, fp+tmp, sp+ip, lr+pc
353 // |----------------------------| <- available for regalloc
354 // r0, r2, r4, r6 r8 - market to be available
GetDefaultRegMask()355 RegMask GetDefaultRegMask() const override
356 {
357 // Set all to 1
358 RegMask reg_mask;
359 reg_mask.set();
360 for (size_t i = 0; i < AVAILABLE_DOUBLE_WORD_REGISTERS; ++i) {
361 reg_mask.reset(i * 2U);
362 }
363 reg_mask.set(GetThreadReg(Arch::AARCH32));
364 return reg_mask;
365 }
366
GetVRegMask()367 VRegMask GetVRegMask() override
368 {
369 VRegMask vreg_mask;
370 for (auto vreg_code : AARCH32_TMP_VREG) {
371 vreg_mask.set(vreg_code);
372 }
373 // Only d0-d15 available for alloc
374 // They mapped on s0-s31 same, like scalar:
375 for (size_t i = 0; i < vreg_mask.size() / 2U; ++i) {
376 vreg_mask.set(i * 2U + 1);
377 }
378 return vreg_mask;
379 }
380
SupportMapping(uint32_t type)381 bool SupportMapping(uint32_t type) override
382 {
383 // Current implementation does not support vreg-vreg mapping
384 if ((type & (RegMapping::VECTOR_VECTOR | RegMapping::FLOAT_FLOAT)) != 0U) {
385 return false;
386 }
387 // Scalar and float registers lay in different registers
388 if ((type & (RegMapping::SCALAR_VECTOR | RegMapping::SCALAR_FLOAT)) != 0U) {
389 return false;
390 }
391 // Supported mapping for upper half register-parts:
392 // (type & RegMapping::SCALAR_SCALAR != 0)
393 return true;
394 };
395
396 bool IsValid() const override;
397
398 bool IsRegUsed(ArenaVector<Reg> vec_reg, Reg reg) override;
399
400 // TODO(igorban): implement as virtual
401 static bool IsTmp(Reg reg);
402
403 public:
404 // Special implementation-specific getters
GetCalleeSavedR()405 RegMask GetCalleeSavedR()
406 {
407 return callee_saved_;
408 }
GetCalleeSavedV()409 VRegMask GetCalleeSavedV()
410 {
411 return callee_savedv_;
412 }
GetCallerSavedR()413 RegMask GetCallerSavedR()
414 {
415 return caller_saved_;
416 }
GetCallerSavedV()417 VRegMask GetCallerSavedV()
418 {
419 return caller_savedv_;
420 }
421
GetAligmentReg(bool is_callee)422 uint8_t GetAligmentReg(bool is_callee)
423 {
424 auto allignment_reg = is_callee ? allignment_reg_callee_ : allignment_reg_caller_;
425 ASSERT(allignment_reg != UNDEF_REG);
426 return allignment_reg;
427 }
428
429 private:
430 // Full list of arm64 General-purpose registers (with vector registers)
431 ArenaVector<Reg> aarch32_reg_list_;
432 //
433 ArenaVector<Reg> used_regs_;
434 Reg tmp_reg1_;
435 Reg tmp_reg2_;
436
437 RegMask callee_saved_ {CALLEE_SAVED};
438 RegMask caller_saved_ {CALLER_SAVED};
439
440 VRegMask callee_savedv_ {CALLEE_SAVEDV};
441 VRegMask caller_savedv_ {CALLER_SAVEDV};
442
443 uint8_t allignment_reg_callee_ {UNDEF_REG};
444 uint8_t allignment_reg_caller_ {UNDEF_REG};
445 }; // Aarch32RegisterDescription
446
447 class Aarch32Encoder;
448
449 class Aarch32LabelHolder final : public LabelHolder {
450 public:
451 using LabelType = vixl::aarch32::Label;
Aarch32LabelHolder(Encoder * enc)452 explicit Aarch32LabelHolder(Encoder *enc) : LabelHolder(enc), labels_(enc->GetAllocator()->Adapter()) {};
453
CreateLabel()454 LabelId CreateLabel() override
455 {
456 ++id_;
457 auto allocator = GetEncoder()->GetAllocator();
458 auto *label = allocator->New<LabelType>(allocator);
459 labels_.push_back(label);
460 ASSERT(labels_.size() == id_);
461 return id_ - 1;
462 };
463
CreateLabels(LabelId size)464 void CreateLabels(LabelId size) override
465 {
466 for (LabelId i = 0; i <= size; ++i) {
467 CreateLabel();
468 }
469 };
470
471 void BindLabel(LabelId id) override;
472
GetLabel(LabelId id)473 LabelType *GetLabel(LabelId id)
474 {
475 ASSERT(labels_.size() > id);
476 return labels_[id];
477 }
478
Size()479 LabelId Size() override
480 {
481 return labels_.size();
482 };
483
484 NO_MOVE_SEMANTIC(Aarch32LabelHolder);
485 NO_COPY_SEMANTIC(Aarch32LabelHolder);
486 ~Aarch32LabelHolder() override = default;
487
488 private:
489 ArenaVector<LabelType *> labels_;
490 LabelId id_ {0};
491 friend Aarch32Encoder;
492 }; // Aarch32LabelHolder
493
494 class Aarch32Encoder final : public Encoder {
495 public:
496 explicit Aarch32Encoder(ArenaAllocator *allocator);
497
GetLabels()498 LabelHolder *GetLabels() const override
499 {
500 return labels_;
501 };
502
503 ~Aarch32Encoder() override;
504
505 NO_COPY_SEMANTIC(Aarch32Encoder);
506 NO_MOVE_SEMANTIC(Aarch32Encoder);
507
IsValid()508 bool IsValid() const override
509 {
510 return true;
511 }
512
GetTarget()513 static constexpr auto GetTarget()
514 {
515 return panda::compiler::Target(Arch::AARCH32);
516 }
517
SetMaxAllocatedBytes(size_t size)518 void SetMaxAllocatedBytes(size_t size) override
519 {
520 GetMasm()->GetBuffer()->SetMmapMaxBytes(size);
521 }
522
523 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
524 #define UnaryOperation(opc) void Encode##opc(Reg dst, Reg src0) override;
525 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
526 #define BinaryOperationRegRegReg(opc) void Encode##opc(Reg dst, Reg src0, Reg src1) override;
527 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
528 #define BinaryOperationRegRegImm(opc) void Encode##opc(Reg dst, Reg src0, Imm src1) override;
529 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
530 #define BinaryOperation(opc) BinaryOperationRegRegReg(opc) BinaryOperationRegRegImm(opc)
531 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
532 #define INST_DEF(OPCODE, TYPE) TYPE(OPCODE)
533
534 ENCODE_MATH_LIST(INST_DEF)
535
536 #undef UnaryOperation
537 #undef BinaryOperation
538 #undef INST_DEF
539
540 void EncodeNop() override;
541
542 // Additional special instructions
543 void EncodeCastToBool(Reg dst, Reg src) override;
544 void EncodeCast(Reg dst, bool dst_signed, Reg src, bool src_signed) override;
545 void EncodeMin(Reg dst, bool dst_signed, Reg src0, Reg src1) override;
546 void EncodeDiv(Reg dst, bool dst_signed, Reg src0, Reg src1) override;
547 void EncodeMod(Reg dst, bool dst_signed, Reg src0, Reg src1) override;
548 void EncodeMax(Reg dst, bool dst_signed, Reg src0, Reg src1) override;
549
550 void EncodeLdr(Reg dst, bool dst_signed, MemRef mem) override;
551 void EncodeLdr(Reg dst, bool dst_signed, const vixl::aarch32::MemOperand &vixl_mem);
552 void EncodeLdrAcquire(Reg dst, bool dst_signed, MemRef mem) override;
553
554 void EncodeMemoryBarrier(MemoryOrder::Order order) override;
555
556 void EncodeMov(Reg dst, Imm src) override;
557 void EncodeStr(Reg src, const vixl::aarch32::MemOperand &vixl_mem);
558 void EncodeStr(Reg src, MemRef mem) override;
559 void EncodeStrRelease(Reg src, MemRef mem) override;
560 void EncodeStp(Reg src0, Reg src1, MemRef mem) override;
561
562 /* builtins-related encoders */
563 void EncodeIsInf(Reg dst, Reg src) override;
564 void EncodeBitCount(Reg dst, Reg src) override;
565 void EncodeCountLeadingZeroBits(Reg dst, Reg src) override;
566 void EncodeCeil([[maybe_unused]] Reg dst, [[maybe_unused]] Reg src) override;
567 void EncodeFloor([[maybe_unused]] Reg dst, [[maybe_unused]] Reg src) override;
568 void EncodeRint([[maybe_unused]] Reg dst, [[maybe_unused]] Reg src) override;
569 void EncodeRound([[maybe_unused]] Reg dst, [[maybe_unused]] Reg src) override;
570 void EncodeReverseBytes(Reg dst, Reg src) override;
571 void EncodeReverseBits(Reg dst, Reg src) override;
572 void EncodeFpToBits(Reg dst, Reg src) override;
573 void EncodeMoveBitsRaw(Reg dst, Reg src) override;
574
575 void EncodeLdrExclusive(Reg dst, Reg addr, bool acquire) override;
576 void EncodeStrExclusive(Reg dst, Reg src, Reg addr, bool release) override;
577
578 // zerod high part: [reg.size, 64)
579 void EncodeStrz(Reg src, MemRef mem) override;
580 void EncodeSti(Imm src, MemRef mem) override;
581 // size must be 8, 16,32 or 64
582 void EncodeMemCopy(MemRef mem_from, MemRef mem_to, size_t size) override;
583 // size must be 8, 16,32 or 64
584 // zerod high part: [reg.size, 64)
585 void EncodeMemCopyz(MemRef mem_from, MemRef mem_to, size_t size) override;
586
587 void EncodeCmp(Reg dst, Reg src0, Reg src1, Condition cc) override;
588
589 void EncodeCompare(Reg dst, Reg src0, Reg src1, Condition cc) override;
590 void EncodeCompareTest(Reg dst, Reg src0, Reg src1, Condition cc) override;
591
592 void EncodeSelect(Reg dst, Reg src0, Reg src1, Reg src2, Reg src3, Condition cc) override;
593 void EncodeSelect(Reg dst, Reg src0, Reg src1, Reg src2, Imm imm, Condition cc) override;
594 void EncodeSelectTest(Reg dst, Reg src0, Reg src1, Reg src2, Reg src3, Condition cc) override;
595 void EncodeSelectTest(Reg dst, Reg src0, Reg src1, Reg src2, Imm imm, Condition cc) override;
596
597 bool CanEncodeImmAddSubCmp(int64_t imm, uint32_t size, bool signed_compare) override;
598 bool CanEncodeImmLogical(uint64_t imm, uint32_t size) override;
599
GetCursorOffset()600 size_t GetCursorOffset() const override
601 {
602 return GetMasm()->GetBuffer()->GetCursorOffset();
603 }
604
SetCursorOffset(size_t offset)605 void SetCursorOffset(size_t offset) override
606 {
607 GetMasm()->GetBuffer()->Rewind(offset);
608 }
609
AcquireScratchRegister(TypeInfo type)610 Reg AcquireScratchRegister(TypeInfo type) override
611 {
612 ASSERT(GetMasm()->GetCurrentScratchRegisterScope() == nullptr);
613 if (type.IsFloat()) {
614 if (type == FLOAT32_TYPE) {
615 auto reg = GetMasm()->GetScratchVRegisterList()->GetFirstAvailableSRegister();
616 ASSERT(reg.IsValid());
617 GetMasm()->GetScratchVRegisterList()->Remove(reg);
618 return Reg(reg.GetCode(), type);
619 }
620 // Get 2 float registers instead one double - to save agreement about hi-regs in VixlVReg
621 auto reg1 = GetMasm()->GetScratchVRegisterList()->GetFirstAvailableSRegister();
622 auto reg2 = GetMasm()->GetScratchVRegisterList()->GetFirstAvailableSRegister();
623 ASSERT(reg1.IsValid());
624 ASSERT(reg2.IsValid());
625 GetMasm()->GetScratchVRegisterList()->Remove(reg1);
626 GetMasm()->GetScratchVRegisterList()->Remove(reg2);
627 return Reg(reg1.GetCode(), type);
628 }
629 auto reg = GetMasm()->GetScratchRegisterList()->GetFirstAvailableRegister();
630 ASSERT(reg.IsValid());
631 GetMasm()->GetScratchRegisterList()->Remove(reg);
632 return Reg(reg.GetCode(), type);
633 }
634
AcquireScratchRegister(Reg reg)635 void AcquireScratchRegister(Reg reg) override
636 {
637 if (reg == GetTarget().GetLinkReg()) {
638 ASSERT_PRINT(!lr_acquired_, "Trying to acquire LR, which hasn't been released before");
639 lr_acquired_ = true;
640 } else if (reg.IsFloat()) {
641 ASSERT(GetMasm()->GetScratchVRegisterList()->IncludesAliasOf(vixl::aarch32::SRegister(reg.GetId())));
642 GetMasm()->GetScratchVRegisterList()->Remove(vixl::aarch32::SRegister(reg.GetId()));
643 } else {
644 ASSERT(GetMasm()->GetScratchRegisterList()->Includes(vixl::aarch32::Register(reg.GetId())));
645 GetMasm()->GetScratchRegisterList()->Remove(vixl::aarch32::Register(reg.GetId()));
646 }
647 }
648
ReleaseScratchRegister(Reg reg)649 void ReleaseScratchRegister(Reg reg) override
650 {
651 if (reg == GetTarget().GetLinkReg()) {
652 ASSERT_PRINT(lr_acquired_, "Trying to release LR, which hasn't been acquired before");
653 lr_acquired_ = false;
654 } else if (reg.IsFloat()) {
655 GetMasm()->GetScratchVRegisterList()->Combine(vixl::aarch32::SRegister(reg.GetId()));
656 } else {
657 GetMasm()->GetScratchRegisterList()->Combine(vixl::aarch32::Register(reg.GetId()));
658 }
659 }
660
IsScratchRegisterReleased(Reg reg)661 bool IsScratchRegisterReleased(Reg reg) override
662 {
663 if (reg == GetTarget().GetLinkReg()) {
664 return !lr_acquired_;
665 }
666 if (reg.IsFloat()) {
667 return GetMasm()->GetScratchVRegisterList()->IncludesAliasOf(vixl::aarch32::SRegister(reg.GetId()));
668 }
669 return GetMasm()->GetScratchRegisterList()->Includes(vixl::aarch32::Register(reg.GetId()));
670 }
671
GetScratchRegistersMask()672 RegMask GetScratchRegistersMask() const override
673 {
674 return RegMask(GetMasm()->GetScratchRegisterList()->GetList());
675 }
676
GetScratchFpRegistersMask()677 RegMask GetScratchFpRegistersMask() const override
678 {
679 return RegMask(GetMasm()->GetScratchVRegisterList()->GetList());
680 }
681
GetAvailableScratchRegisters()682 RegMask GetAvailableScratchRegisters() const override
683 {
684 return RegMask(GetMasm()->GetScratchRegisterList()->GetList());
685 }
686
GetAvailableScratchFpRegisters()687 VRegMask GetAvailableScratchFpRegisters() const override
688 {
689 return VRegMask(GetMasm()->GetScratchVRegisterList()->GetList());
690 }
691
SetRegister(RegMask * mask,VRegMask * vmask,Reg reg,bool val)692 void SetRegister(RegMask *mask, VRegMask *vmask, Reg reg, bool val) const override
693 {
694 if (!reg.IsValid()) {
695 return;
696 }
697 if (reg.IsScalar()) {
698 ASSERT(mask != nullptr);
699 mask->set(reg.GetId(), val);
700 if (reg.GetSize() > WORD_SIZE) {
701 mask->set(reg.GetId() + 1, val);
702 }
703 } else {
704 ASSERT(vmask != nullptr);
705 ASSERT(reg.IsFloat());
706 vmask->set(reg.GetId(), val);
707 if (reg.GetSize() > WORD_SIZE) {
708 vmask->set(reg.GetId() + 1, val);
709 }
710 }
711 }
712
GetRefType()713 TypeInfo GetRefType() override
714 {
715 return INT32_TYPE;
716 };
717
718 size_t DisasmInstr(std::ostream &stream, size_t pc, ssize_t code_offset) const override;
719
BufferData()720 void *BufferData() const override
721 {
722 return GetMasm()->GetBuffer()->GetStartAddress<void *>();
723 };
724
BufferSize()725 size_t BufferSize() const override
726 {
727 return GetMasm()->GetBuffer()->GetSizeInBytes();
728 };
729
730 bool InitMasm() override;
731
732 void Finalize() override;
733
734 void MakeCall(compiler::RelocationInfo *relocation) override;
735 void MakeCall(const void *entry_point) override;
736 void MakeCall(MemRef entry_point) override;
737 void MakeCall(Reg reg) override;
738
739 void MakeCallAot(intptr_t offset) override;
740 void MakeCallByOffset(intptr_t offset) override;
741 void MakeLoadAotTable(intptr_t offset, Reg reg) override;
742 void MakeLoadAotTableAddr(intptr_t offset, Reg addr, Reg val) override;
743
744 // Encode unconditional branch
745 void EncodeJump(LabelHolder::LabelId id) override;
746
747 // Encode jump with compare to zero
748 void EncodeJump(LabelHolder::LabelId id, Reg src, Condition cc) override;
749
750 // Compare reg and immediate and branch
751 void EncodeJump(LabelHolder::LabelId id, Reg src, Imm imm, Condition cc) override;
752
753 // Compare two regs and branch
754 void EncodeJump(LabelHolder::LabelId id, Reg src0, Reg src1, Condition cc) override;
755
756 // Compare reg and immediate and branch
757 void EncodeJumpTest(LabelHolder::LabelId id, Reg src, Imm imm, Condition cc) override;
758
759 // Compare two regs and branch
760 void EncodeJumpTest(LabelHolder::LabelId id, Reg src0, Reg src1, Condition cc) override;
761
762 // Encode jump by register value
763 void EncodeJump(Reg dst) override;
764
765 void EncodeJump(RelocationInfo *relocation) override;
766
767 void EncodeBitTestAndBranch(LabelHolder::LabelId id, compiler::Reg reg, uint32_t bit_pos, bool bit_value) override;
768
769 void EncodeAbort() override;
770
771 void EncodeReturn() override;
772
773 void EncodeStackOverflowCheck(ssize_t offset) override;
774
SaveRegisters(RegMask registers,ssize_t slot,size_t start_reg,bool is_fp)775 void SaveRegisters(RegMask registers, ssize_t slot, size_t start_reg, bool is_fp) override
776 {
777 LoadStoreRegisters<true>(registers, slot, start_reg, is_fp);
778 }
LoadRegisters(RegMask registers,ssize_t slot,size_t start_reg,bool is_fp)779 void LoadRegisters(RegMask registers, ssize_t slot, size_t start_reg, bool is_fp) override
780 {
781 LoadStoreRegisters<false>(registers, slot, start_reg, is_fp);
782 }
783
SaveRegisters(RegMask registers,bool is_fp,ssize_t slot,Reg base,RegMask mask)784 void SaveRegisters(RegMask registers, bool is_fp, ssize_t slot, Reg base, RegMask mask) override
785 {
786 LoadStoreRegisters<true>(registers, is_fp, slot, base, mask);
787 }
LoadRegisters(RegMask registers,bool is_fp,ssize_t slot,Reg base,RegMask mask)788 void LoadRegisters(RegMask registers, bool is_fp, ssize_t slot, Reg base, RegMask mask) override
789 {
790 LoadStoreRegisters<false>(registers, is_fp, slot, base, mask);
791 }
792
793 void PushRegisters(RegMask registers, bool is_fp, bool align) override;
794 void PopRegisters(RegMask registers, bool is_fp, bool align) override;
795
ConvertMem(MemRef mem)796 static inline vixl::aarch32::MemOperand ConvertMem(MemRef mem)
797 {
798 bool has_index = mem.HasIndex();
799 bool has_shift = mem.HasScale();
800 bool has_offset = mem.HasDisp();
801 auto base_reg = VixlReg(mem.GetBase());
802 if (has_index) {
803 // MemRef with index and offser isn't supported
804 ASSERT(!has_offset);
805 auto index_reg = mem.GetIndex();
806 if (has_shift) {
807 auto shift = mem.GetScale();
808 return vixl::aarch32::MemOperand(base_reg, VixlReg(index_reg), vixl::aarch32::LSL, shift);
809 }
810 return vixl::aarch32::MemOperand(base_reg, VixlReg(index_reg));
811 }
812 if (has_offset) {
813 auto offset = mem.GetDisp();
814 return vixl::aarch32::MemOperand(base_reg, offset);
815 }
816 return vixl::aarch32::MemOperand(base_reg);
817 }
818
819 /**
820 * The function construct additional instruction for encode memory instructions and returns MemOperand for ldr/str
821 * LDR/STR with immediate offset(for A32)
822 * | mem type | offset size |
823 * | ---------- | ----------- |
824 * |word or byte|-4095 to 4095|
825 * | others | -255 to 255 |
826 *
827 * LDR/STR with register offset(for A32)
828 * | mem type | shift |
829 * | ---------- | ----------- |
830 * |word or byte| 0 to 31 |
831 * | others | -- |
832 *
833 * VLDR and VSTR has base and offset. The offset must be a multiple of 4, and lie in the range -1020 to +1020.
834 */
835 static bool IsNeedToPrepareMemLdS(MemRef mem, const TypeInfo &mem_type, bool is_signed);
836 vixl::aarch32::MemOperand PrepareMemLdS(MemRef mem, const TypeInfo &mem_type, vixl::aarch32::Register tmp,
837 bool is_signed, bool copy_sp = false);
838
839 void MakeLibCall(Reg dst, Reg src0, Reg src1, void *entry_point, bool second_value = false);
840
841 void MakeLibCall(Reg dst, Reg src, void *entry_point);
842
GetMasm()843 vixl::aarch32::MacroAssembler *GetMasm() const
844 {
845 ASSERT(masm_ != nullptr);
846 return masm_;
847 }
848
GetLabelAddress(LabelHolder::LabelId label)849 size_t GetLabelAddress(LabelHolder::LabelId label) override
850 {
851 auto plabel = labels_->GetLabel(label);
852 ASSERT(plabel->IsBound());
853 return GetMasm()->GetBuffer()->GetOffsetAddress<size_t>(plabel->GetLocation());
854 }
855
LabelHasLinks(LabelHolder::LabelId label)856 bool LabelHasLinks(LabelHolder::LabelId label) override
857 {
858 auto plabel = labels_->GetLabel(label);
859 return plabel->IsReferenced();
860 }
861
862 private:
863 template <bool is_store>
864 void LoadStoreRegisters(RegMask registers, ssize_t slot, size_t start_reg, bool is_fp);
865
866 template <bool is_store>
867 void LoadStoreRegisters(RegMask registers, bool is_fp, int32_t slot, Reg base, RegMask mask);
868
869 private:
870 vixl::aarch32::MemOperand PrepareMemLdSForFloat(MemRef mem, vixl::aarch32::Register tmp);
871 void EncodeCastFloatToFloat(Reg dst, Reg src);
872 void EncodeCastFloatToInt64(Reg dst, Reg src);
873 void EncodeCastDoubleToInt64(Reg dst, Reg src);
874 void EncodeCastScalarToFloat(Reg dst, Reg src, bool src_signed);
875 void EncodeCastFloatToScalar(Reg dst, bool dst_signed, Reg src);
876 void EncodeCastFloatToScalarWithSmallDst(Reg dst, bool dst_signed, Reg src);
877
878 void EncoderCastExtendFromInt32(Reg dst, bool dst_signed);
879 void EncodeCastScalar(Reg dst, bool dst_signed, Reg src, bool src_signed);
880 void EncodeCastScalarFromSignedScalar(Reg dst, Reg src);
881 void EncodeCastScalarFromUnsignedScalar(Reg dst, Reg src);
882 template <bool is_max>
883 void EncodeMinMaxFp(Reg dst, Reg src0, Reg src1);
884 void EncodeVorr(Reg dst, Reg src0, Reg src1);
885 void EncodeVand(Reg dst, Reg src0, Reg src1);
886 void MakeLibCallWithFloatResult(Reg dst, Reg src0, Reg src1, void *entry_point, bool second_value);
887 void MakeLibCallWithDoubleResult(Reg dst, Reg src0, Reg src1, void *entry_point, bool second_value);
888 void MakeLibCallWithInt64Result(Reg dst, Reg src0, Reg src1, void *entry_point, bool second_value);
889 void CompareHelper(Reg src0, Reg src1, Condition *cc);
890 void TestHelper(Reg src0, Reg src1, Condition cc);
891 bool CompareImmHelper(Reg src, Imm imm, Condition *cc);
892 void TestImmHelper(Reg src, Imm imm, Condition cc);
893 bool CompareNegImmHelper(Reg src, int64_t value, const Condition *cc);
894 bool ComparePosImmHelper(Reg src, int64_t value, Condition *cc);
895 void CompareZeroHelper(Reg src, Condition *cc);
896 void EncodeFloatSti(Imm src, MemRef mem);
897 static inline constexpr int32_t MEM_BIG_OFFSET = 4095;
898 static inline constexpr int32_t MEM_SMALL_OFFSET = 255;
899 static inline constexpr int32_t VMEM_OFFSET = 1020;
900 Aarch32LabelHolder *labels_ {nullptr};
901 vixl::aarch32::MacroAssembler *masm_ {nullptr};
902 bool lr_acquired_ {false};
903 }; // Aarch32Encoder
904
905 class Aarch32ParameterInfo final : public ParameterInfo {
906 public:
907 std::variant<Reg, uint8_t> GetNativeParam(const TypeInfo &type) override;
908 Location GetNextLocation(DataType::Type type) override;
909 };
910
911 class Aarch32CallingConvention : public CallingConvention {
912 public:
913 Aarch32CallingConvention(ArenaAllocator *allocator, Encoder *enc, RegistersDescription *descr, CallConvMode mode);
914
GetTarget()915 static constexpr auto GetTarget()
916 {
917 return panda::compiler::Target(Arch::AARCH32);
918 }
919
IsValid()920 bool IsValid() const override
921 {
922 return true;
923 }
924
925 void GeneratePrologue(const FrameInfo &frame_info) override;
926 void GenerateEpilogue(const FrameInfo &frame_info, std::function<void()> post_job) override;
GenerateNativePrologue(const FrameInfo & frame_info)927 void GenerateNativePrologue(const FrameInfo &frame_info) override
928 {
929 GeneratePrologue(frame_info);
930 }
GenerateNativeEpilogue(const FrameInfo & frame_info,std::function<void ()> post_job)931 void GenerateNativeEpilogue(const FrameInfo &frame_info, std::function<void()> post_job) override
932 {
933 GenerateEpilogue(frame_info, post_job);
934 }
935
936 void *GetCodeEntry() override;
937 uint32_t GetCodeSize() override;
938
GetMasm()939 vixl::aarch32::MacroAssembler *GetMasm()
940 {
941 return (static_cast<Aarch32Encoder *>(GetEncoder()))->GetMasm();
942 }
943
944 // Calculating information about parameters and save regs_offset registers for special needs
945 ParameterInfo *GetParameterInfo(uint8_t regs_offset) override;
946
947 NO_MOVE_SEMANTIC(Aarch32CallingConvention);
948 NO_COPY_SEMANTIC(Aarch32CallingConvention);
949 ~Aarch32CallingConvention() override = default;
950
951 private:
952 uint8_t PushPopVRegs(VRegMask vregs, bool is_push);
953 uint8_t PushRegs(RegMask regs, VRegMask vregs, bool is_callee);
954 uint8_t PopRegs(RegMask regs, VRegMask vregs, bool is_callee);
955 }; // Aarch32CallingConvention
956 } // namespace panda::compiler::aarch32
957
958 #endif // COMPILER_OPTIMIZER_CODEGEN_TARGET_AARCH32_TARGET_H_
959