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_AMD64_TARGET_H_
17 #define COMPILER_OPTIMIZER_CODEGEN_TARGET_AMD64_TARGET_H_
18
19 #include "compiler/optimizer/code_generator/callconv.h"
20
21 #include "asmjit/x86.h"
22 #include "target_info.h"
23
24 namespace panda::compiler::amd64 {
25 const size_t MAX_SCALAR_PARAM_ID = 5; // %rdi, %rsi, %rdx, %rcx, %r8, %r9
26 const size_t MAX_VECTOR_PARAM_ID = 7; // %xmm0-%xmm7
27
IsConditionSigned(Condition cc)28 static inline bool IsConditionSigned(Condition cc)
29 {
30 switch (cc) {
31 case Condition::LT:
32 case Condition::LE:
33 case Condition::GT:
34 case Condition::GE:
35 return true;
36
37 default:
38 return false;
39 }
40 }
41
42 /**
43 * Converters
44 */
45 static inline asmjit::x86::Condition::Code ArchCc(Condition cc, bool is_float = false)
46 {
47 switch (cc) {
48 case Condition::EQ:
49 return asmjit::x86::Condition::Code::kEqual;
50 case Condition::NE:
51 return asmjit::x86::Condition::Code::kNotEqual;
52 case Condition::LT:
53 return is_float ? asmjit::x86::Condition::Code::kUnsignedLT : asmjit::x86::Condition::Code::kSignedLT;
54 case Condition::GT:
55 return is_float ? asmjit::x86::Condition::Code::kUnsignedGT : asmjit::x86::Condition::Code::kSignedGT;
56 case Condition::LE:
57 return is_float ? asmjit::x86::Condition::Code::kUnsignedLE : asmjit::x86::Condition::Code::kSignedLE;
58 case Condition::GE:
59 return is_float ? asmjit::x86::Condition::Code::kUnsignedGE : asmjit::x86::Condition::Code::kSignedGE;
60 case Condition::LO:
61 return asmjit::x86::Condition::Code::kUnsignedLT;
62 case Condition::LS:
63 return asmjit::x86::Condition::Code::kUnsignedLE;
64 case Condition::HI:
65 return asmjit::x86::Condition::Code::kUnsignedGT;
66 case Condition::HS:
67 return asmjit::x86::Condition::Code::kUnsignedGE;
68 // TODO(igorban) : Remove them
69 case Condition::MI:
70 return asmjit::x86::Condition::Code::kNegative;
71 case Condition::PL:
72 return asmjit::x86::Condition::Code::kPositive;
73 case Condition::VS:
74 return asmjit::x86::Condition::Code::kOverflow;
75 case Condition::VC:
76 return asmjit::x86::Condition::Code::kNotOverflow;
77 case Condition::AL:
78 case Condition::NV:
79 default:
80 UNREACHABLE();
81 return asmjit::x86::Condition::Code::kEqual;
82 }
83 }
84
ArchCcTest(Condition cc)85 static inline asmjit::x86::Condition::Code ArchCcTest(Condition cc)
86 {
87 ASSERT(cc == Condition::TST_EQ || cc == Condition::TST_NE);
88 return cc == Condition::TST_EQ ? asmjit::x86::Condition::Code::kEqual : asmjit::x86::Condition::Code::kNotEqual;
89 }
90
CcMatchesNan(Condition cc)91 static inline bool CcMatchesNan(Condition cc)
92 {
93 switch (cc) {
94 case Condition::NE:
95 case Condition::LT:
96 case Condition::LE:
97 case Condition::HI:
98 case Condition::HS:
99 return true;
100
101 default:
102 return false;
103 }
104 }
105
106 class AsmJitErrorHandler : public asmjit::ErrorHandler {
107 public:
AsmJitErrorHandler(Encoder * encoder)108 explicit AsmJitErrorHandler(Encoder *encoder) : encoder_(encoder)
109 {
110 ASSERT(encoder != nullptr);
111 }
112
handleError(asmjit::Error err,const char * message,asmjit::BaseEmitter * origin)113 void handleError([[maybe_unused]] asmjit::Error err, [[maybe_unused]] const char *message,
114 [[maybe_unused]] asmjit::BaseEmitter *origin) override
115 {
116 encoder_->SetFalseResult();
117 }
118
119 NO_MOVE_SEMANTIC(AsmJitErrorHandler);
120 NO_COPY_SEMANTIC(AsmJitErrorHandler);
121 ~AsmJitErrorHandler() override = default;
122
123 private:
124 Encoder *encoder_ {nullptr};
125 };
126
127 class RegList {
128 public:
RegList(size_t mask)129 explicit RegList(size_t mask) : mask_(mask)
130 {
131 for (size_t i = 0; i < sizeof(size_t) * BITS_PER_BYTE; ++i) {
132 if (Has(i)) {
133 ++count_;
134 }
135 }
136 }
137
138 DEFAULT_MOVE_SEMANTIC(RegList);
139 DEFAULT_COPY_SEMANTIC(RegList);
140 ~RegList() = default;
141
size_t()142 explicit operator size_t() const
143 {
144 return mask_;
145 }
146
IsEmpty()147 bool IsEmpty() const
148 {
149 return count_ == size_t(0);
150 }
151
GetCount()152 size_t GetCount() const
153 {
154 return count_;
155 }
156
Has(size_t i)157 bool Has(size_t i) const
158 {
159 return (mask_ & (size_t(1) << i)) != 0;
160 }
161
Add(size_t i)162 void Add(size_t i)
163 {
164 if (Has(i)) {
165 return;
166 }
167 mask_ |= size_t(1) << i;
168 ++count_;
169 }
170
Remove(size_t i)171 void Remove(size_t i)
172 {
173 if (!Has(i)) {
174 return;
175 }
176 mask_ &= ~(size_t(1) << i);
177 --count_;
178 }
179
Pop()180 size_t Pop()
181 {
182 ASSERT(!IsEmpty());
183 size_t i = __builtin_ctzll(mask_);
184 Remove(i);
185 return i;
186 }
187
GetMask()188 size_t GetMask() const
189 {
190 return mask_;
191 }
192
193 private:
194 size_t mask_ {0};
195 size_t count_ {0};
196 };
197
198 /**
199 * Converters
200 */
201 static inline asmjit::x86::Gp ArchReg(Reg reg, uint8_t size = 0)
202 {
203 ASSERT(reg.IsValid());
204 if (reg.IsScalar()) {
205 size_t reg_size = size == 0 ? reg.GetSize() : size;
206 auto arch_id = ConvertRegNumber(reg.GetId());
207
208 asmjit::x86::Gp arch_reg;
209 switch (reg_size) {
210 case DOUBLE_WORD_SIZE:
211 arch_reg = asmjit::x86::Gp(asmjit::x86::Gpq::kSignature, arch_id);
212 break;
213 case WORD_SIZE:
214 arch_reg = asmjit::x86::Gp(asmjit::x86::Gpd::kSignature, arch_id);
215 break;
216 case HALF_SIZE:
217 arch_reg = asmjit::x86::Gp(asmjit::x86::Gpw::kSignature, arch_id);
218 break;
219 case BYTE_SIZE:
220 arch_reg = asmjit::x86::Gp(asmjit::x86::GpbLo::kSignature, arch_id);
221 break;
222
223 default:
224 UNREACHABLE();
225 }
226
227 ASSERT(arch_reg.isValid());
228 return arch_reg;
229 }
230 if (reg.GetId() == ConvertRegNumber(asmjit::x86::rsp.id())) {
231 return asmjit::x86::rsp;
232 }
233
234 // Invalid register type
235 UNREACHABLE();
236 return asmjit::x86::rax;
237 }
238
ArchVReg(Reg reg)239 static inline asmjit::x86::Xmm ArchVReg(Reg reg)
240 {
241 ASSERT(reg.IsValid() && reg.IsFloat());
242 auto arch_vreg = asmjit::x86::xmm(reg.GetId());
243 return arch_vreg;
244 }
245
ArchImm(Imm imm)246 static inline asmjit::Imm ArchImm(Imm imm)
247 {
248 ASSERT(imm.IsValid());
249 if (imm.GetType() == INT64_TYPE) {
250 return asmjit::imm(imm.GetValue<int64_t>());
251 }
252 if (imm.GetType() == INT32_TYPE) {
253 return asmjit::imm(imm.GetValue<int32_t>());
254 }
255 if (imm.GetType() == INT16_TYPE) {
256 return asmjit::imm(imm.GetValue<int16_t>());
257 }
258 if (imm.GetType() == INT8_TYPE) {
259 return asmjit::imm(imm.GetValue<int8_t>());
260 }
261 // Invalid converted register
262 UNREACHABLE();
263 return asmjit::imm(0);
264 }
265
ImmToSignedInt(Imm imm)266 static inline int64_t ImmToSignedInt(Imm imm)
267 {
268 ASSERT(imm.IsValid());
269 if (imm.GetType() == INT64_TYPE) {
270 return imm.GetValue<int64_t>();
271 }
272 if (imm.GetType() == INT32_TYPE) {
273 return imm.GetValue<int32_t>();
274 }
275 if (imm.GetType() == INT16_TYPE) {
276 return imm.GetValue<int16_t>();
277 }
278 if (imm.GetType() == INT8_TYPE) {
279 return imm.GetValue<int8_t>();
280 }
281 // Invalid converted register
282 UNREACHABLE();
283 return 0;
284 }
285
ImmToUnsignedInt(Imm imm)286 static inline uint64_t ImmToUnsignedInt(Imm imm)
287 {
288 ASSERT(imm.IsValid());
289 if (imm.GetType() == INT64_TYPE) {
290 return uint64_t(imm.GetValue<int64_t>());
291 }
292 if (imm.GetType() == INT32_TYPE) {
293 return uint32_t(imm.GetValue<int32_t>());
294 }
295 if (imm.GetType() == INT16_TYPE) {
296 return uint16_t(imm.GetValue<int16_t>());
297 }
298 if (imm.GetType() == INT8_TYPE) {
299 return uint8_t(imm.GetValue<int8_t>());
300 }
301 // Invalid converted register
302 UNREACHABLE();
303 return 0;
304 }
305
ImmFitsSize(int64_t imm,uint8_t size)306 static inline bool ImmFitsSize(int64_t imm, uint8_t size)
307 {
308 if (size == DOUBLE_WORD_SIZE) {
309 size = WORD_SIZE;
310 }
311
312 // NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult)
313 int64_t max = (uint64_t(1) << (size - 1U)) - 1U; // SUPPRESS_CSA(core.UndefinedBinaryOperatorResult)
314 int64_t min = ~uint64_t(max);
315 ASSERT(min < 0);
316 ASSERT(max > 0);
317
318 return imm >= min && imm <= max;
319 }
320
321 class ArchMem {
322 public:
ArchMem(MemRef mem)323 explicit ArchMem(MemRef mem)
324 {
325 bool base = mem.HasBase();
326 bool regoffset = mem.HasIndex();
327 bool shift = mem.HasScale();
328 bool offset = mem.HasDisp();
329
330 if (base && !regoffset && !shift) {
331 // Default memory - base + offset
332 mem_ = asmjit::x86::ptr(ArchReg(mem.GetBase()), mem.GetDisp());
333 } else if (base && regoffset && !offset) {
334 auto base_size = mem.GetBase().GetSize();
335 auto index_size = mem.GetIndex().GetSize();
336
337 ASSERT(base_size >= index_size);
338 ASSERT(index_size >= WORD_SIZE);
339
340 if (base_size > index_size) {
341 need_extend_index_ = true;
342 }
343
344 if (mem.GetScale() == 0) {
345 mem_ = asmjit::x86::ptr(ArchReg(mem.GetBase()), ArchReg(mem.GetIndex(), base_size));
346 } else {
347 auto scale = mem.GetScale();
348
349 if (scale <= 3U) {
350 mem_ = asmjit::x86::ptr(ArchReg(mem.GetBase()), ArchReg(mem.GetIndex(), base_size), scale);
351 } else {
352 mem_ = asmjit::x86::ptr(ArchReg(mem.GetBase()), ArchReg(mem.GetIndex(), base_size));
353 big_shift_ = scale;
354 }
355 }
356 } else {
357 // Wrong memRef
358 UNREACHABLE();
359 }
360 }
361
362 DEFAULT_MOVE_SEMANTIC(ArchMem);
363 DEFAULT_COPY_SEMANTIC(ArchMem);
364 ~ArchMem() = default;
365
Prepare(asmjit::x86::Assembler * masm)366 asmjit::x86::Mem Prepare(asmjit::x86::Assembler *masm)
367 {
368 if (is_prepared_) {
369 return mem_;
370 }
371
372 if (big_shift_ != 0) {
373 ASSERT(!mem_.hasOffset() && mem_.hasIndex() && big_shift_ > 3U);
374 masm->shl(mem_.indexReg().as<asmjit::x86::Gp>(), asmjit::imm(big_shift_));
375 }
376
377 if (need_extend_index_) {
378 ASSERT(mem_.hasIndex());
379 auto q_index = mem_.indexReg().as<asmjit::x86::Gp>();
380 auto d_index {q_index};
381 d_index.setSignature(asmjit::x86::Gpd::kSignature);
382 masm->movsxd(q_index, d_index);
383 }
384
385 is_prepared_ = true;
386 return mem_;
387 }
388
389 private:
390 int64_t big_shift_ {0};
391 asmjit::x86::Mem mem_;
392 bool need_extend_index_ {false};
393 bool is_prepared_ {false};
394 };
395
396 /*
397 * Scalar registers mapping:
398 * +-----------+---------------------+
399 * | AMD64 Reg | Panda Reg |
400 * +-----------+---------------------+
401 * | rax | r0 |
402 * | rcx | r1 |
403 * | rdx | r2 |
404 * | rbx | r3 (renamed to r11) |
405 * | rsp | r4 (renamed to r10) |
406 * | rbp | r5 (renamed to r9) |
407 * | rsi | r6 |
408 * | rdi | r7 |
409 * | r8 | r8 |
410 * | r9 | r9 (renamed to r5) |
411 * | r10 | r10 (renamed to r4) |
412 * | r11 | r11 (renamed to r3) |
413 * | r12 | r12 |
414 * | r13 | r13 |
415 * | r14 | r14 |
416 * | r15 | r15 |
417 * | <no reg> | r16-r31 |
418 * +-----------+---------------------+
419 *
420 * Vector registers mapping:
421 * xmm[i] <-> vreg[i], 0 <= i <= 15
422 */
423 class Amd64RegisterDescription final : public RegistersDescription {
424 public:
425 explicit Amd64RegisterDescription(ArenaAllocator *allocator);
426 NO_MOVE_SEMANTIC(Amd64RegisterDescription);
427 NO_COPY_SEMANTIC(Amd64RegisterDescription);
428 ~Amd64RegisterDescription() override = default;
429
430 ArenaVector<Reg> GetCalleeSaved() override;
431 void SetCalleeSaved(const ArenaVector<Reg> ®s) override;
432 // Set used regs - change GetCallee
433 void SetUsedRegs(const ArenaVector<Reg> ®s) override;
434
GetCallerSavedRegMask()435 RegMask GetCallerSavedRegMask() const override
436 {
437 return RegMask(caller_saved_.GetMask());
438 }
439
GetCallerSavedVRegMask()440 VRegMask GetCallerSavedVRegMask() const override
441 {
442 return VRegMask(caller_savedv_.GetMask());
443 }
444
IsCalleeRegister(Reg reg)445 bool IsCalleeRegister(Reg reg) override
446 {
447 bool is_fp = reg.IsFloat();
448 return reg.GetId() >= GetFirstCalleeReg(Arch::X86_64, is_fp) &&
449 reg.GetId() <= GetLastCalleeReg(Arch::X86_64, is_fp);
450 }
451
GetZeroReg()452 Reg GetZeroReg() const override
453 {
454 return INVALID_REGISTER; // there is no one
455 }
456
IsZeroReg(Reg reg)457 bool IsZeroReg([[maybe_unused]] Reg reg) const override
458 {
459 return false;
460 }
461
GetTempReg()462 Reg::RegIDType GetTempReg() override
463 {
464 return compiler::arch_info::x86_64::TEMP_REGS.GetMaxRegister();
465 }
466
GetTempVReg()467 Reg::RegIDType GetTempVReg() override
468 {
469 return compiler::arch_info::x86_64::TEMP_FP_REGS.GetMaxRegister();
470 }
471
GetDefaultRegMask()472 RegMask GetDefaultRegMask() const override
473 {
474 static constexpr size_t HIGH_MASK {0xFFFF0000};
475
476 RegMask reg_mask(HIGH_MASK);
477 reg_mask |= compiler::arch_info::x86_64::TEMP_REGS;
478 reg_mask.set(ConvertRegNumber(asmjit::x86::rbp.id()));
479 reg_mask.set(ConvertRegNumber(asmjit::x86::rsp.id()));
480 reg_mask.set(GetThreadReg(Arch::X86_64));
481 return reg_mask;
482 }
483
GetVRegMask()484 VRegMask GetVRegMask() override
485 {
486 static constexpr size_t HIGH_MASK {0xFFFF0000};
487
488 VRegMask vreg_mask(HIGH_MASK);
489 vreg_mask |= compiler::arch_info::x86_64::TEMP_FP_REGS;
490 return vreg_mask;
491 }
492
493 // Check register mapping
SupportMapping(uint32_t type)494 bool SupportMapping(uint32_t type) override
495 {
496 // Current implementation does not support reg-reg mapping
497 if ((type & (RegMapping::VECTOR_VECTOR | RegMapping::FLOAT_FLOAT)) != 0U) {
498 return false;
499 }
500 // Scalar and float registers lay in different registers
501 if ((type & (RegMapping::SCALAR_VECTOR | RegMapping::SCALAR_FLOAT)) != 0U) {
502 return false;
503 }
504 return true;
505 };
506
IsValid()507 bool IsValid() const override
508 {
509 return true;
510 }
511
512 bool IsRegUsed(ArenaVector<Reg> vec_reg, Reg reg) override;
513
514 public:
515 // Special implementation-specific getters
GetCalleeSavedR()516 size_t GetCalleeSavedR()
517 {
518 return static_cast<size_t>(callee_saved_);
519 }
GetCalleeSavedV()520 size_t GetCalleeSavedV()
521 {
522 return static_cast<size_t>(callee_savedv_);
523 }
GetCallerSavedR()524 size_t GetCallerSavedR()
525 {
526 return static_cast<size_t>(caller_saved_);
527 }
GetCallerSavedV()528 size_t GetCallerSavedV()
529 {
530 return static_cast<size_t>(caller_savedv_);
531 }
532
AcquireScratchRegister(TypeInfo type)533 Reg AcquireScratchRegister(TypeInfo type)
534 {
535 if (type.IsFloat()) {
536 return Reg(scratchv_.Pop(), type);
537 }
538 return Reg(scratch_.Pop(), type);
539 }
540
AcquireScratchRegister(Reg reg)541 void AcquireScratchRegister(Reg reg)
542 {
543 if (reg.GetType().IsFloat()) {
544 ASSERT(scratchv_.Has(reg.GetId()));
545 scratchv_.Remove(reg.GetId());
546 } else {
547 ASSERT(scratch_.Has(reg.GetId()));
548 scratch_.Remove(reg.GetId());
549 }
550 }
551
ReleaseScratchRegister(Reg reg)552 void ReleaseScratchRegister(Reg reg)
553 {
554 if (reg.IsFloat()) {
555 scratchv_.Add(reg.GetId());
556 } else {
557 scratch_.Add(reg.GetId());
558 }
559 }
560
IsScratchRegisterReleased(Reg reg)561 bool IsScratchRegisterReleased(Reg reg)
562 {
563 if (reg.GetType().IsFloat()) {
564 return scratchv_.Has(reg.GetId());
565 }
566 return scratch_.Has(reg.GetId());
567 }
568
GetScratchRegisters()569 RegList GetScratchRegisters() const
570 {
571 return scratch_;
572 }
573
GetScratchFPRegisters()574 RegList GetScratchFPRegisters() const
575 {
576 return scratchv_;
577 }
578
GetScratchRegistersCount()579 size_t GetScratchRegistersCount() const
580 {
581 return scratch_.GetCount();
582 }
583
GetScratchFPRegistersCount()584 size_t GetScratchFPRegistersCount() const
585 {
586 return scratchv_.GetCount();
587 }
588
GetScratchRegistersMask()589 RegMask GetScratchRegistersMask() const
590 {
591 return RegMask(scratch_.GetMask());
592 }
593
GetScratchFpRegistersMask()594 RegMask GetScratchFpRegistersMask() const
595 {
596 return RegMask(scratchv_.GetMask());
597 }
598
599 private:
600 ArenaVector<Reg> used_regs_;
601
602 RegList callee_saved_ {GetCalleeRegsMask(Arch::X86_64, false).GetValue()};
603 RegList caller_saved_ {GetCallerRegsMask(Arch::X86_64, false).GetValue()};
604
605 RegList callee_savedv_ {GetCalleeRegsMask(Arch::X86_64, true).GetValue()};
606 RegList caller_savedv_ {GetCallerRegsMask(Arch::X86_64, true).GetValue()};
607
608 RegList scratch_ {compiler::arch_info::x86_64::TEMP_REGS.to_ulong()};
609 RegList scratchv_ {compiler::arch_info::x86_64::TEMP_FP_REGS.to_ulong()};
610 }; // Amd64RegisterDescription
611
612 class Amd64Encoder;
613
614 class Amd64LabelHolder final : public LabelHolder {
615 public:
616 using LabelType = asmjit::Label;
617
Amd64LabelHolder(Encoder * enc)618 explicit Amd64LabelHolder(Encoder *enc) : LabelHolder(enc), labels_(enc->GetAllocator()->Adapter()) {};
619 NO_MOVE_SEMANTIC(Amd64LabelHolder);
620 NO_COPY_SEMANTIC(Amd64LabelHolder);
621 ~Amd64LabelHolder() override = default;
622
623 LabelId CreateLabel() override;
624
CreateLabels(LabelId max)625 void CreateLabels(LabelId max) override
626 {
627 for (LabelId i = 0; i < max; ++i) {
628 CreateLabel();
629 }
630 };
631
632 void BindLabel(LabelId id) override;
633
GetLabel(LabelId id)634 LabelType *GetLabel(LabelId id)
635 {
636 ASSERT(labels_.size() > id);
637 return labels_[id];
638 }
639
Size()640 LabelId Size() override
641 {
642 return labels_.size();
643 };
644
645 private:
646 ArenaVector<LabelType *> labels_;
647 LabelId id_ {0};
648 friend Amd64Encoder;
649 }; // Amd64LabelHolder
650
651 class Amd64Encoder final : public Encoder {
652 public:
653 using Encoder::Encoder;
654 explicit Amd64Encoder(ArenaAllocator *allocator);
655
GetLabels()656 LabelHolder *GetLabels() const override
657 {
658 ASSERT(labels_ != nullptr);
659 return labels_;
660 };
661
662 ~Amd64Encoder() override;
663
664 NO_COPY_SEMANTIC(Amd64Encoder);
665 NO_MOVE_SEMANTIC(Amd64Encoder);
666
IsValid()667 bool IsValid() const override
668 {
669 return true;
670 }
671
GetTarget()672 static constexpr auto GetTarget()
673 {
674 return panda::compiler::Target(Arch::X86_64);
675 }
676
677 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
678 #define UnaryOperation(opc) void Encode##opc(Reg dst, Reg src0) override;
679 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
680 #define BinaryOperation(opc) \
681 void Encode##opc(Reg dst, Reg src0, Reg src1) override; \
682 void Encode##opc(Reg dst, Reg src0, Imm src1) override;
683 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
684 #define INST_DEF(OPCODE, TYPE) TYPE(OPCODE)
685
686 ENCODE_MATH_LIST(INST_DEF)
687
688 #undef UnaryOperation
689 #undef BinaryOperation
690 #undef INST_DEF
691
692 void EncodeNop() override;
693
694 // Additional special instructions
695 void EncodeAdd(Reg dst, Reg src0, Shift src1) override;
696
697 void EncodeCastToBool(Reg dst, Reg src) override;
698 void EncodeCast(Reg dst, bool dst_signed, Reg src, bool src_signed) override;
699 void EncodeMin(Reg dst, bool dst_signed, Reg src0, Reg src1) override;
700 void EncodeDiv(Reg dst, bool dst_signed, Reg src0, Reg src1) override;
701 void EncodeMod(Reg dst, bool dst_signed, Reg src0, Reg src1) override;
702 void EncodeMax(Reg dst, bool dst_signed, Reg src0, Reg src1) override;
703
704 void EncodeAddOverflow(compiler::LabelHolder::LabelId id, Reg dst, Reg src0, Reg src1, Condition cc) override;
705 void EncodeSubOverflow(compiler::LabelHolder::LabelId id, Reg dst, Reg src0, Reg src1, Condition cc) override;
706
707 void EncodeLdr(Reg dst, bool dst_signed, MemRef mem) override;
708 void EncodeLdrAcquire(Reg dst, bool dst_signed, MemRef mem) override;
709
710 void EncodeMov(Reg dst, Imm src) override;
711 void EncodeStr(Reg src, MemRef mem) override;
712 void EncodeStrRelease(Reg src, MemRef mem) override;
713 // zerod high part: [reg.size, 64)
714 void EncodeStrz(Reg src, MemRef mem) override;
715 void EncodeSti(Imm src, MemRef mem) override;
716 // size must be 8, 16,32 or 64
717 void EncodeMemCopy(MemRef mem_from, MemRef mem_to, size_t size) override;
718 // size must be 8, 16,32 or 64
719 // zerod high part: [reg.size, 64)
720 void EncodeMemCopyz(MemRef mem_from, MemRef mem_to, size_t size) override;
721
722 void EncodeCmp(Reg dst, Reg src0, Reg src1, Condition cc) override;
723
724 void EncodeCompare(Reg dst, Reg src0, Reg src1, Condition cc) override;
725 void EncodeCompareTest(Reg dst, Reg src0, Reg src1, Condition cc) override;
726
727 void EncodeSelect(Reg dst, Reg src0, Reg src1, Reg src2, Reg src3, Condition cc) override;
728 void EncodeSelect(Reg dst, Reg src0, Reg src1, Reg src2, Imm imm, Condition cc) override;
729 void EncodeSelectTest(Reg dst, Reg src0, Reg src1, Reg src2, Reg src3, Condition cc) override;
730 void EncodeSelectTest(Reg dst, Reg src0, Reg src1, Reg src2, Imm imm, Condition cc) override;
731
732 void EncodeLdp(Reg dst0, Reg dst1, bool dst_signed, MemRef mem) override;
733
734 void EncodeStp(Reg src0, Reg src1, MemRef mem) override;
735
736 /* builtins-related encoders */
737 void EncodeIsInf(Reg dst, Reg src) override;
738 void EncodeBitCount(Reg dst, Reg src) override;
739 void EncodeCountLeadingZeroBits(Reg dst, Reg src) override;
740 void EncodeCountTrailingZeroBits(Reg dst, Reg src) override;
741 void EncodeCeil([[maybe_unused]] Reg dst, [[maybe_unused]] Reg src) override;
742 void EncodeFloor([[maybe_unused]] Reg dst, [[maybe_unused]] Reg src) override;
743 void EncodeRint([[maybe_unused]] Reg dst, [[maybe_unused]] Reg src) override;
744 void EncodeRound([[maybe_unused]] Reg dst, [[maybe_unused]] Reg src) override;
745 void EncodeReverseBytes(Reg dst, Reg src) override;
746 void EncodeReverseBits(Reg dst, Reg src) override;
747 void EncodeFpToBits(Reg dst, Reg src) override;
748 void EncodeMoveBitsRaw(Reg dst, Reg src) override;
749
750 bool CanEncodeImmAddSubCmp(int64_t imm, uint32_t size, bool signed_compare) override;
751 bool CanEncodeImmLogical(uint64_t imm, uint32_t size) override;
752 bool CanEncodeScale(uint64_t imm, uint32_t size) override;
753 bool CanEncodeBitCount() override;
754
EncodeCompareAndSwap(Reg dst,Reg obj,Reg offset,Reg val,Reg newval)755 void EncodeCompareAndSwap(Reg dst, Reg obj, Reg offset, Reg val, Reg newval) override
756 {
757 EncodeCompareAndSwap(dst, obj, &offset, val, newval);
758 }
759
EncodeCompareAndSwap(Reg dst,Reg addr,Reg val,Reg newval)760 void EncodeCompareAndSwap(Reg dst, Reg addr, Reg val, Reg newval) override
761 {
762 EncodeCompareAndSwap(dst, addr, nullptr, val, newval);
763 }
764
765 void EncodeUnsafeGetAndSet(Reg dst, Reg obj, Reg offset, Reg val) override;
766 void EncodeUnsafeGetAndAdd(Reg dst, Reg obj, Reg offset, Reg val, Reg tmp) override;
767 void EncodeMemoryBarrier(MemoryOrder::Order order) override;
768
769 void EncodeStackOverflowCheck(ssize_t offset) override;
770
GetCursorOffset()771 size_t GetCursorOffset() const override
772 {
773 return GetMasm()->offset();
774 }
775
SetCursorOffset(size_t offset)776 void SetCursorOffset(size_t offset) override
777 {
778 GetMasm()->setOffset(offset);
779 }
780
AcquireScratchRegister(TypeInfo type)781 Reg AcquireScratchRegister(TypeInfo type) override
782 {
783 return (static_cast<Amd64RegisterDescription *>(GetRegfile()))->AcquireScratchRegister(type);
784 }
785
AcquireScratchRegister(Reg reg)786 void AcquireScratchRegister(Reg reg) override
787 {
788 (static_cast<Amd64RegisterDescription *>(GetRegfile()))->AcquireScratchRegister(reg);
789 }
790
ReleaseScratchRegister(Reg reg)791 void ReleaseScratchRegister(Reg reg) override
792 {
793 (static_cast<Amd64RegisterDescription *>(GetRegfile()))->ReleaseScratchRegister(reg);
794 }
795
IsScratchRegisterReleased(Reg reg)796 bool IsScratchRegisterReleased(Reg reg) override
797 {
798 return (static_cast<Amd64RegisterDescription *>(GetRegfile()))->IsScratchRegisterReleased(reg);
799 }
800
GetScratchRegistersMask()801 RegMask GetScratchRegistersMask() const override
802 {
803 return (static_cast<const Amd64RegisterDescription *>(GetRegfile()))->GetScratchRegistersMask();
804 }
805
GetScratchFpRegistersMask()806 RegMask GetScratchFpRegistersMask() const override
807 {
808 return (static_cast<const Amd64RegisterDescription *>(GetRegfile()))->GetScratchFpRegistersMask();
809 }
810
GetAvailableScratchRegisters()811 RegMask GetAvailableScratchRegisters() const override
812 {
813 auto regfile = static_cast<const Amd64RegisterDescription *>(GetRegfile());
814 return RegMask(regfile->GetScratchRegisters().GetMask());
815 }
816
GetAvailableScratchFpRegisters()817 VRegMask GetAvailableScratchFpRegisters() const override
818 {
819 auto regfile = static_cast<const Amd64RegisterDescription *>(GetRegfile());
820 return VRegMask(regfile->GetScratchFPRegisters().GetMask());
821 }
822
GetRefType()823 TypeInfo GetRefType() override
824 {
825 return INT64_TYPE;
826 };
827
828 size_t DisasmInstr(std::ostream &stream, size_t pc, ssize_t code_offset) const override;
829
BufferData()830 void *BufferData() const override
831 {
832 return GetMasm()->bufferData();
833 };
834
BufferSize()835 size_t BufferSize() const override
836 {
837 return GetMasm()->offset();
838 };
839
840 bool InitMasm() override;
841
842 void Finalize() override;
843
844 void MakeCall(compiler::RelocationInfo *relocation) override;
845 void MakeCall(LabelHolder::LabelId id) override;
846 void MakeCall(const void *entry_point) override;
847 void MakeCall(Reg reg) override;
848 void MakeCall(MemRef entry_point) override;
849
850 void MakeCallAot(intptr_t offset) override;
851 void MakeCallByOffset(intptr_t offset) override;
852 void MakeLoadAotTable(intptr_t offset, Reg reg) override;
853 void MakeLoadAotTableAddr(intptr_t offset, Reg addr, Reg val) override;
854 bool CanMakeCallByOffset(intptr_t offset) override;
855
856 // Encode unconditional branch
857 void EncodeJump(LabelHolder::LabelId id) override;
858
859 // Encode jump with compare to zero
860 void EncodeJump(LabelHolder::LabelId id, Reg src, Condition cc) override;
861
862 // Compare reg and immediate and branch
863 void EncodeJump(LabelHolder::LabelId id, Reg src, Imm imm, Condition cc) override;
864
865 // Compare two regs and branch
866 void EncodeJump(LabelHolder::LabelId id, Reg src0, Reg src1, Condition cc) override;
867
868 // Compare reg and immediate and branch
869 void EncodeJumpTest(LabelHolder::LabelId id, Reg src, Imm imm, Condition cc) override;
870
871 // Compare two regs and branch
872 void EncodeJumpTest(LabelHolder::LabelId id, Reg src0, Reg src1, Condition cc) override;
873
874 // Encode jump by register value
875 void EncodeJump(Reg dst) override;
876
877 void EncodeJump(RelocationInfo *relocation) override;
878
879 void EncodeBitTestAndBranch(LabelHolder::LabelId id, compiler::Reg reg, uint32_t bit_pos, bool bit_value) override;
880
881 void EncodeAbort() override;
882
883 void EncodeReturn() override;
884
885 void MakeLibCall(Reg dst, Reg src0, Reg src1, void *entry_point);
886
SaveRegisters(RegMask registers,ssize_t slot,size_t start_reg,bool is_fp)887 void SaveRegisters(RegMask registers, ssize_t slot, size_t start_reg, bool is_fp) override
888 {
889 LoadStoreRegisters<true>(registers, slot, start_reg, is_fp);
890 }
LoadRegisters(RegMask registers,ssize_t slot,size_t start_reg,bool is_fp)891 void LoadRegisters(RegMask registers, ssize_t slot, size_t start_reg, bool is_fp) override
892 {
893 LoadStoreRegisters<false>(registers, slot, start_reg, is_fp);
894 }
SaveRegisters(RegMask registers,bool is_fp,ssize_t slot,Reg base,RegMask mask)895 void SaveRegisters(RegMask registers, bool is_fp, ssize_t slot, Reg base, RegMask mask) override
896 {
897 LoadStoreRegisters<true>(registers, is_fp, slot, base, mask);
898 }
LoadRegisters(RegMask registers,bool is_fp,ssize_t slot,Reg base,RegMask mask)899 void LoadRegisters(RegMask registers, bool is_fp, ssize_t slot, Reg base, RegMask mask) override
900 {
901 LoadStoreRegisters<false>(registers, is_fp, slot, base, mask);
902 }
903
904 void PushRegisters(RegMask registers, bool is_fp, bool align) override;
905 void PopRegisters(RegMask registers, bool is_fp, bool align) override;
906
907 template <typename Func>
908 void EncodeRelativePcMov(Reg reg, intptr_t offset, Func encode_instruction);
909
GetMasm()910 asmjit::x86::Assembler *GetMasm() const
911 {
912 ASSERT(masm_ != nullptr);
913 return masm_;
914 }
915
GetLabelAddress(LabelHolder::LabelId label)916 size_t GetLabelAddress(LabelHolder::LabelId label) override
917 {
918 auto code = GetMasm()->code();
919 ASSERT(code->isLabelBound(label));
920 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
921 return code->baseAddress() + code->labelOffset(label);
922 }
923
LabelHasLinks(LabelHolder::LabelId label)924 bool LabelHasLinks(LabelHolder::LabelId label) override
925 {
926 auto code = GetMasm()->code();
927 auto entry = code->labelEntry(label);
928 return entry->links() != nullptr;
929 }
930
931 private:
932 template <bool is_store>
933 void LoadStoreRegisters(RegMask registers, ssize_t slot, size_t start_reg, bool is_fp);
934
935 template <bool is_store>
936 void LoadStoreRegisters(RegMask registers, bool is_fp, int32_t slot, Reg base, RegMask mask);
937
938 inline Reg MakeShift(Shift shift);
939
940 template <typename T>
941 void EncodeReverseBitsImpl(Reg dst0, Reg src0);
942
943 void EncodeCastFloatToScalar(Reg dst, bool dst_signed, Reg src);
944 inline void EncodeCastFloatSignCheckRange(Reg dst, Reg src, const asmjit::Label &end);
945 inline void EncodeCastFloatUnsignCheckRange(Reg dst, Reg src, const asmjit::Label &end);
946 void EncodeCastFloatCheckNan(Reg dst, Reg src, const asmjit::Label &end);
947 void EncodeCastFloatCheckRange(Reg dst, Reg src, const asmjit::Label &end, int64_t min_value, uint64_t max_value);
948 void EncodeCastFloat32ToUint64(Reg dst, Reg src);
949 void EncodeCastFloat64ToUint64(Reg dst, Reg src);
950
951 void EncodeCastScalarToFloat(Reg dst, Reg src, bool src_signed);
952 void EncodeCastScalarToFloatUnsignDouble(Reg dst, Reg src);
953 void EncodeCastScalar(Reg dst, bool dst_signed, Reg src, bool src_signed);
954
955 void EncodeDivFloat(Reg dst, Reg src0, Reg src1);
956 void EncodeModFloat(Reg dst, Reg src0, Reg src1);
957 template <bool is_max>
958 void EncodeMinMaxFp(Reg dst, Reg src0, Reg src1);
959
960 template <typename T, size_t n>
961 void CopyArrayToXmm(Reg xmm, const std::array<T, n> &arr);
962
963 template <typename T>
964 void CopyImmToXmm(Reg xmm, T imm);
965
966 void EncodeCompareAndSwap(Reg dst, Reg obj, const Reg *offset, Reg val, Reg newval);
967
968 private:
969 Amd64LabelHolder *labels_ {nullptr};
970 asmjit::ErrorHandler *error_handler_ {nullptr};
971 asmjit::CodeHolder *code_holder_ {nullptr};
972 asmjit::x86::Assembler *masm_ {nullptr};
973 }; // Amd64Encoder
974
975 class Amd64ParameterInfo : public ParameterInfo {
976 public:
977 std::variant<Reg, uint8_t> GetNativeParam(const TypeInfo &type) override;
978 Location GetNextLocation(DataType::Type type) override;
979 };
980
981 class Amd64CallingConvention : public CallingConvention {
982 public:
983 Amd64CallingConvention(ArenaAllocator *allocator, Encoder *enc, RegistersDescription *descr, CallConvMode mode);
984 NO_MOVE_SEMANTIC(Amd64CallingConvention);
985 NO_COPY_SEMANTIC(Amd64CallingConvention);
986 ~Amd64CallingConvention() override = default;
987
GetTarget()988 static constexpr auto GetTarget()
989 {
990 return panda::compiler::Target(Arch::X86_64);
991 }
992
IsValid()993 bool IsValid() const override
994 {
995 return true;
996 }
997
998 void GeneratePrologue(const FrameInfo &frame_info) override;
999 void GenerateEpilogue(const FrameInfo &frame_info, std::function<void()> post_job) override;
GenerateNativePrologue(const FrameInfo & frame_info)1000 void GenerateNativePrologue(const FrameInfo &frame_info) override
1001 {
1002 GeneratePrologue(frame_info);
1003 }
GenerateNativeEpilogue(const FrameInfo & frame_info,std::function<void ()> post_job)1004 void GenerateNativeEpilogue(const FrameInfo &frame_info, std::function<void()> post_job) override
1005 {
1006 GenerateEpilogue(frame_info, post_job);
1007 }
1008
1009 void *GetCodeEntry() override;
1010 uint32_t GetCodeSize() override;
1011
1012 // Pushes regs and returns number of regs(from boths vectors)
1013 size_t PushRegs(RegList regs, RegList vregs);
1014 // Pops regs and returns number of regs(from boths vectors)
1015 size_t PopRegs(RegList regs, RegList vregs);
1016
1017 // Calculating information about parameters and save regs_offset registers for special needs
1018 ParameterInfo *GetParameterInfo(uint8_t regs_offset) override;
1019
GetMasm()1020 asmjit::x86::Assembler *GetMasm()
1021 {
1022 return (static_cast<Amd64Encoder *>(GetEncoder()))->GetMasm();
1023 }
1024
1025 private:
1026 }; // Amd64CallingConvention
1027 } // namespace panda::compiler::amd64
1028
1029 #endif // COMPILER_OPTIMIZER_CODEGEN_TARGET_AMD64_TARGET_H_
1030