1 /*
2 * Copyright (c) 2021-2023 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 regsOffset)28 ParameterInfo *Amd64CallingConvention::GetParameterInfo(uint8_t regsOffset)
29 {
30 auto paramInfo = GetAllocator()->New<amd64::Amd64ParameterInfo>();
31 // reserve first parameter to method pointer
32 for (int i = 0; i < regsOffset; ++i) {
33 paramInfo->GetNativeParam(INT64_TYPE);
34 }
35 return paramInfo;
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 regsCount {0};
53 size_t vregsCount {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 ++vregsCount;
59 GetMasm()->sub(asmjit::x86::rsp, asmjit::imm(DOUBLE_WORD_SIZE_BYTES));
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 ++regsCount;
68 GetMasm()->push(asmjit::x86::gpq(ConvertRegNumber(ii)));
69 }
70 }
71
72 return vregsCount + regsCount;
73 }
74
PopRegs(RegList regs,RegList vregs)75 size_t Amd64CallingConvention::PopRegs(RegList regs, RegList vregs)
76 {
77 size_t regsCount {0};
78 size_t vregsCount {0};
79
80 for (uint32_t i = 0; i < MAX_NUM_REGS; ++i) {
81 if (regs.Has(i)) {
82 ++regsCount;
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 ++vregsCount;
90 GetMasm()->movsd(asmjit::x86::xmm(i), asmjit::x86::ptr(asmjit::x86::rsp));
91 GetMasm()->add(asmjit::x86::rsp, asmjit::imm(DOUBLE_WORD_SIZE_BYTES));
92 }
93 }
94
95 return vregsCount + regsCount;
96 }
97
GetNativeParam(const TypeInfo & type)98 std::variant<Reg, uint8_t> Amd64ParameterInfo::GetNativeParam(const TypeInfo &type)
99 {
100 if (type.IsFloat()) {
101 if (currentVectorNumber_ > MAX_VECTOR_PARAM_ID) {
102 return currentStackOffset_++;
103 }
104 return Reg(currentVectorNumber_++, type);
105 }
106 if (currentScalarNumber_ > MAX_SCALAR_PARAM_ID) {
107 return currentStackOffset_++;
108 }
109
110 return Target(Arch::X86_64).GetParamReg(currentScalarNumber_++, type);
111 }
112
GetNextLocation(DataType::Type type)113 Location Amd64ParameterInfo::GetNextLocation(DataType::Type type)
114 {
115 if (DataType::IsFloatType(type)) {
116 if (currentVectorNumber_ > MAX_VECTOR_PARAM_ID) {
117 return Location::MakeStackArgument(currentStackOffset_++);
118 }
119 return Location::MakeFpRegister(currentVectorNumber_++);
120 }
121 if (currentScalarNumber_ > MAX_SCALAR_PARAM_ID) {
122 return Location::MakeStackArgument(currentStackOffset_++);
123 }
124 Target target(Arch::X86_64);
125 return Location::MakeRegister(target.GetParamRegId(currentScalarNumber_++));
126 }
127
GeneratePrologue(const FrameInfo & frameInfo)128 void Amd64CallingConvention::GeneratePrologue([[maybe_unused]] const FrameInfo &frameInfo)
129 {
130 auto encoder = GetEncoder();
131 const CFrameLayout &fl = encoder->GetFrameLayout();
132 auto fpReg = GetTarget().GetFrameReg();
133 auto spReg = 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(pushFplr, encoder->GetCursorOffset());
138
139 encoder->EncodeMov(fpReg, spReg);
140 SET_CFI_OFFSET(setFp, encoder->GetCursorOffset());
141
142 if (IsDynCallMode() && GetDynInfo().IsCheckRequired()) {
143 static_assert(CallConvDynInfo::REG_NUM_ARGS == 1);
144 static_assert(CallConvDynInfo::REG_COUNT == CallConvDynInfo::REG_NUM_ARGS + 1);
145
146 constexpr auto NUM_ACTUAL_REG = GetTarget().GetParamReg(CallConvDynInfo::REG_NUM_ARGS);
147 constexpr auto NUM_EXPECTED_REG = GetTarget().GetParamReg(CallConvDynInfo::REG_COUNT);
148 auto numExpected = GetDynInfo().GetNumExpectedArgs();
149
150 auto expandDone = encoder->CreateLabel();
151 encoder->EncodeJump(expandDone, NUM_ACTUAL_REG, Imm(numExpected), Condition::GE);
152 encoder->EncodeMov(NUM_EXPECTED_REG, Imm(numExpected));
153
154 MemRef expandEntrypoint(Reg(GetThreadReg(Arch::X86_64), GetTarget().GetPtrRegType()),
155 GetDynInfo().GetExpandEntrypointTlsOffset());
156 GetEncoder()->MakeCall(expandEntrypoint);
157 encoder->BindLabel(expandDone);
158 }
159
160 encoder->EncodeSub(spReg, spReg, Imm(2U * DOUBLE_WORD_SIZE_BYTES));
161 encoder->EncodeStr(GetTarget().GetParamReg(0), MemRef(spReg, DOUBLE_WORD_SIZE_BYTES));
162
163 // Reset OSR flag and set HasFloatRegsFlag
164 auto flags {static_cast<uint64_t>(frameInfo.GetHasFloatRegs()) << CFrameLayout::HasFloatRegsFlag::START_BIT};
165 encoder->EncodeSti(flags, sizeof(flags), MemRef(spReg));
166 // Allocate space for locals
167 encoder->EncodeSub(spReg, spReg, Imm(DOUBLE_WORD_SIZE_BYTES * (CFrameSlots::Start() - CFrameData::Start())));
168 static_assert((CFrameLayout::GetLocalsCount() & 1U) == 0);
169
170 RegList calleeRegs {GetCalleeRegsMask(Arch::X86_64, false).GetValue()};
171 RegList calleeVregs {GetCalleeRegsMask(Arch::X86_64, true).GetValue()};
172 SET_CFI_CALLEE_REGS(RegMask(static_cast<size_t>(calleeRegs)));
173 SET_CFI_CALLEE_VREGS(VRegMask(static_cast<size_t>(calleeVregs)));
174 PushRegs(calleeRegs, calleeVregs);
175 SET_CFI_OFFSET(pushCallees, encoder->GetCursorOffset());
176
177 encoder->EncodeSub(
178 spReg, spReg,
179 Imm((fl.GetSpillsCount() + fl.GetCallerRegistersCount(false) + fl.GetCallerRegistersCount(true)) *
180 DOUBLE_WORD_SIZE_BYTES));
181 }
182
GenerateEpilogue(const FrameInfo & frameInfo,std::function<void ()> postJob)183 void Amd64CallingConvention::GenerateEpilogue([[maybe_unused]] const FrameInfo &frameInfo,
184 std::function<void()> postJob)
185 {
186 auto encoder = GetEncoder();
187 const CFrameLayout &fl = encoder->GetFrameLayout();
188 auto spReg = GetTarget().GetStackReg();
189
190 if (postJob) {
191 postJob();
192 }
193
194 encoder->EncodeAdd(
195 spReg, spReg,
196 Imm((fl.GetSpillsCount() + fl.GetCallerRegistersCount(false) + fl.GetCallerRegistersCount(true)) *
197 DOUBLE_WORD_SIZE_BYTES));
198
199 PopRegs(RegList(GetCalleeRegsMask(Arch::X86_64, false).GetValue()),
200 RegList(GetCalleeRegsMask(Arch::X86_64, true).GetValue()));
201 SET_CFI_OFFSET(popCallees, encoder->GetCursorOffset());
202
203 // X86_64 doesn't support OSR mode
204 ASSERT(!IsOsrMode());
205 // Support restoring of LR and FP registers once OSR is supported in x86_64
206 static_assert(!ArchTraits<Arch::X86_64>::SUPPORT_OSR);
207 constexpr auto SHIFT = DOUBLE_WORD_SIZE_BYTES * (2 + CFrameSlots::Start() - CFrameData::Start());
208 encoder->EncodeAdd(spReg, spReg, Imm(SHIFT));
209
210 GetMasm()->pop(asmjit::x86::rbp); // frame pointer
211 SET_CFI_OFFSET(popFplr, encoder->GetCursorOffset());
212 GetMasm()->ret();
213 }
214 } // namespace panda::compiler::amd64
215