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