• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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