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 Low-level calling convention
17 */
18 #include "target/amd64/target.h"
19
20 namespace panda::compiler::amd64 {
21
Amd64CallingConvention(ArenaAllocator * allocator,Encoder * enc,RegistersDescription * descr,CallConvMode mode)22 Amd64CallingConvention::Amd64CallingConvention(ArenaAllocator *allocator, Encoder *enc, RegistersDescription *descr,
23 CallConvMode mode)
24 : CallingConvention(allocator, enc, descr, mode)
25 {
26 }
27
GetParameterInfo(uint8_t regs_offset)28 ParameterInfo *Amd64CallingConvention::GetParameterInfo(uint8_t regs_offset)
29 {
30 auto param_info = GetAllocator()->New<amd64::Amd64ParameterInfo>();
31 // reserve first parameter to method pointer
32 for (int i = 0; i < regs_offset; ++i) {
33 param_info->GetNativeParam(INT64_TYPE);
34 }
35 return param_info;
36 }
37
GetCodeEntry()38 void *Amd64CallingConvention::GetCodeEntry()
39 {
40 auto code = static_cast<Amd64Encoder *>(GetEncoder())->GetMasm()->code();
41 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
42 return reinterpret_cast<void *>(code->baseAddress());
43 }
44
GetCodeSize()45 uint32_t Amd64CallingConvention::GetCodeSize()
46 {
47 return static_cast<Amd64Encoder *>(GetEncoder())->GetMasm()->code()->codeSize();
48 }
49
PushRegs(RegList regs,RegList vregs)50 size_t Amd64CallingConvention::PushRegs(RegList regs, RegList vregs)
51 {
52 size_t regs_count {0};
53 size_t vregs_count {0};
54
55 for (uint32_t i = 0; i < MAX_NUM_REGS; ++i) {
56 uint32_t ii {MAX_NUM_REGS - i - 1};
57 if (vregs.Has(ii)) {
58 ++vregs_count;
59 GetMasm()->sub(asmjit::x86::rsp, asmjit::imm(DOUBLE_WORD_SIZE_BYTE));
60 GetMasm()->movsd(asmjit::x86::ptr(asmjit::x86::rsp), asmjit::x86::xmm(ii));
61 }
62 }
63
64 for (uint32_t i = 0; i < MAX_NUM_REGS; ++i) {
65 uint32_t ii {MAX_NUM_REGS - i - 1};
66 if (regs.Has(ii)) {
67 ++regs_count;
68 GetMasm()->push(asmjit::x86::gpq(ConvertRegNumber(ii)));
69 }
70 }
71
72 return vregs_count + regs_count;
73 }
74
PopRegs(RegList regs,RegList vregs)75 size_t Amd64CallingConvention::PopRegs(RegList regs, RegList vregs)
76 {
77 size_t regs_count {0};
78 size_t vregs_count {0};
79
80 for (uint32_t i = 0; i < MAX_NUM_REGS; ++i) {
81 if (regs.Has(i)) {
82 ++regs_count;
83 GetMasm()->pop(asmjit::x86::gpq(ConvertRegNumber(i)));
84 }
85 }
86
87 for (uint32_t i = 0; i < MAX_NUM_REGS; ++i) {
88 if (vregs.Has(i)) {
89 ++vregs_count;
90 GetMasm()->movsd(asmjit::x86::xmm(i), asmjit::x86::ptr(asmjit::x86::rsp));
91 GetMasm()->add(asmjit::x86::rsp, asmjit::imm(DOUBLE_WORD_SIZE_BYTE));
92 }
93 }
94
95 return vregs_count + regs_count;
96 }
97
GetNativeParam(const TypeInfo & type)98 std::variant<Reg, uint8_t> Amd64ParameterInfo::GetNativeParam(const TypeInfo &type)
99 {
100 if (type.IsFloat()) {
101 if (current_vector_number_ > MAX_VECTOR_PARAM_ID) {
102 return current_stack_offset_++;
103 }
104 return Reg(current_vector_number_++, type);
105 }
106 if (current_scalar_number_ > MAX_SCALAR_PARAM_ID) {
107 return current_stack_offset_++;
108 }
109
110 return Target(Arch::X86_64).GetParamReg(current_scalar_number_++, type);
111 }
112
GetNextLocation(DataType::Type type)113 Location Amd64ParameterInfo::GetNextLocation(DataType::Type type)
114 {
115 if (DataType::IsFloatType(type)) {
116 if (current_vector_number_ > MAX_VECTOR_PARAM_ID) {
117 return Location::MakeStackArgument(current_stack_offset_++);
118 }
119 return Location::MakeFpRegister(current_vector_number_++);
120 }
121 if (current_scalar_number_ > MAX_SCALAR_PARAM_ID) {
122 return Location::MakeStackArgument(current_stack_offset_++);
123 }
124 Target target(Arch::X86_64);
125 return Location::MakeRegister(target.GetParamRegId(current_scalar_number_++));
126 }
127
GeneratePrologue(const FrameInfo & frame_info)128 void Amd64CallingConvention::GeneratePrologue([[maybe_unused]] const FrameInfo &frame_info)
129 {
130 auto encoder = GetEncoder();
131 const CFrameLayout &fl = encoder->GetFrameLayout();
132 auto fp_reg = GetTarget().GetFrameReg();
133 auto sp_reg = GetTarget().GetStackReg();
134
135 // we do not push return address, because in amd64 call instruction already pushed it
136 GetMasm()->push(asmjit::x86::rbp); // frame pointer
137 SET_CFI_OFFSET(push_fplr, encoder->GetCursorOffset());
138
139 encoder->EncodeMov(fp_reg, sp_reg);
140 SET_CFI_OFFSET(set_fp, encoder->GetCursorOffset());
141 encoder->EncodeSub(sp_reg, sp_reg, Imm(2U * DOUBLE_WORD_SIZE_BYTE));
142
143 encoder->EncodeStr(GetTarget().GetParamReg(0), MemRef(sp_reg, DOUBLE_WORD_SIZE_BYTE));
144
145 // Reset OSR flag and set HasFloatRegsFlag
146 auto flags {static_cast<uint64_t>(frame_info.GetHasFloatRegs()) << CFrameLayout::HasFloatRegsFlag::START_BIT};
147 encoder->EncodeSti(Imm(flags), MemRef(sp_reg));
148 // Allocate space for locals
149 encoder->EncodeSub(sp_reg, sp_reg, Imm(DOUBLE_WORD_SIZE_BYTE * (CFrameSlots::Start() - CFrameData::Start())));
150 static_assert((CFrameLayout::GetLocalsCount() & 1U) == 0);
151
152 RegList callee_regs {GetCalleeRegsMask(Arch::X86_64, false).GetValue()};
153 RegList callee_vregs {GetCalleeRegsMask(Arch::X86_64, true).GetValue()};
154 SET_CFI_CALLEE_REGS(RegMask(static_cast<size_t>(callee_regs)));
155 SET_CFI_CALLEE_VREGS(VRegMask(static_cast<size_t>(callee_vregs)));
156 PushRegs(callee_regs, callee_vregs);
157 SET_CFI_OFFSET(push_callees, encoder->GetCursorOffset());
158
159 encoder->EncodeSub(
160 sp_reg, sp_reg,
161 Imm((fl.GetSpillsCount() + fl.GetCallerRegistersCount(false) + fl.GetCallerRegistersCount(true)) *
162 DOUBLE_WORD_SIZE_BYTE));
163 }
164
GenerateEpilogue(const FrameInfo & frame_info,std::function<void ()> post_job)165 void Amd64CallingConvention::GenerateEpilogue([[maybe_unused]] const FrameInfo &frame_info,
166 std::function<void()> post_job)
167 {
168 auto encoder = GetEncoder();
169 const CFrameLayout &fl = encoder->GetFrameLayout();
170 auto sp_reg = GetTarget().GetStackReg();
171
172 if (post_job) {
173 post_job();
174 }
175
176 encoder->EncodeAdd(
177 sp_reg, sp_reg,
178 Imm((fl.GetSpillsCount() + fl.GetCallerRegistersCount(false) + fl.GetCallerRegistersCount(true)) *
179 DOUBLE_WORD_SIZE_BYTE));
180
181 PopRegs(RegList(GetCalleeRegsMask(Arch::X86_64, false).GetValue()),
182 RegList(GetCalleeRegsMask(Arch::X86_64, true).GetValue()));
183 SET_CFI_OFFSET(pop_callees, encoder->GetCursorOffset());
184
185 // X86_64 doesn't support OSR mode
186 ASSERT(!IsOsrMode());
187 // Support restoring of LR and FP registers once OSR is supported in x86_64
188 static_assert(!ArchTraits<Arch::X86_64>::SUPPORT_OSR);
189 constexpr auto SHIFT = DOUBLE_WORD_SIZE_BYTE * (2 + CFrameSlots::Start() - CFrameData::Start());
190 encoder->EncodeAdd(sp_reg, sp_reg, Imm(SHIFT));
191
192 GetMasm()->pop(asmjit::x86::rbp); // frame pointer
193 SET_CFI_OFFSET(pop_fplr, encoder->GetCursorOffset());
194 GetMasm()->ret();
195 }
196 } // namespace panda::compiler::amd64
197