1// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/codegen/assembler-inl.h"
6#include "src/codegen/macro-assembler.h"
7#include "src/compiler/globals.h"
8#include "src/compiler/linkage.h"
9#include "src/zone/zone.h"
10
11namespace v8 {
12namespace internal {
13namespace compiler {
14
15namespace {
16
17// Platform-specific configuration for C calling convention.
18#if V8_TARGET_ARCH_IA32
19// ===========================================================================
20// == ia32 ===================================================================
21// ===========================================================================
22#define CALLEE_SAVE_REGISTERS esi, edi, ebx
23#define CALLEE_SAVE_FP_REGISTERS
24
25#elif V8_TARGET_ARCH_X64
26// ===========================================================================
27// == x64 ====================================================================
28// ===========================================================================
29
30#ifdef V8_TARGET_OS_WIN
31// == x64 windows ============================================================
32#define STACK_SHADOW_WORDS 4
33#define PARAM_REGISTERS rcx, rdx, r8, r9
34#define FP_PARAM_REGISTERS xmm0, xmm1, xmm2, xmm3
35#define FP_RETURN_REGISTER xmm0
36#define CALLEE_SAVE_REGISTERS rbx, rdi, rsi, r12, r13, r14, r15
37#define CALLEE_SAVE_FP_REGISTERS \
38  xmm6, xmm7, xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15
39
40#else  // V8_TARGET_OS_WIN
41// == x64 other ==============================================================
42#define PARAM_REGISTERS rdi, rsi, rdx, rcx, r8, r9
43#define FP_PARAM_REGISTERS xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7
44#define FP_RETURN_REGISTER xmm0
45#define CALLEE_SAVE_REGISTERS rbx, r12, r13, r14, r15
46#define CALLEE_SAVE_FP_REGISTERS
47#endif  // V8_TARGET_OS_WIN
48
49#elif V8_TARGET_ARCH_ARM
50// ===========================================================================
51// == arm ====================================================================
52// ===========================================================================
53#define PARAM_REGISTERS r0, r1, r2, r3
54#define CALLEE_SAVE_REGISTERS r4, r5, r6, r7, r8, r9, r10
55#define CALLEE_SAVE_FP_REGISTERS d8, d9, d10, d11, d12, d13, d14, d15
56
57#elif V8_TARGET_ARCH_ARM64
58// ===========================================================================
59// == arm64 ====================================================================
60// ===========================================================================
61#define PARAM_REGISTERS x0, x1, x2, x3, x4, x5, x6, x7
62#define FP_PARAM_REGISTERS d0, d1, d2, d3, d4, d5, d6, d7
63#define FP_RETURN_REGISTER d0
64#define CALLEE_SAVE_REGISTERS x19, x20, x21, x22, x23, x24, x25, x26, x27, x28
65
66#define CALLEE_SAVE_FP_REGISTERS d8, d9, d10, d11, d12, d13, d14, d15
67
68#elif V8_TARGET_ARCH_MIPS
69// ===========================================================================
70// == mips ===================================================================
71// ===========================================================================
72#define STACK_SHADOW_WORDS 4
73#define PARAM_REGISTERS a0, a1, a2, a3
74#define CALLEE_SAVE_REGISTERS s0, s1, s2, s3, s4, s5, s6, s7
75#define CALLEE_SAVE_FP_REGISTERS f20, f22, f24, f26, f28, f30
76
77#elif V8_TARGET_ARCH_MIPS64
78// ===========================================================================
79// == mips64 =================================================================
80// ===========================================================================
81#define PARAM_REGISTERS a0, a1, a2, a3, a4, a5, a6, a7
82#define CALLEE_SAVE_REGISTERS s0, s1, s2, s3, s4, s5, s6, s7
83#define CALLEE_SAVE_FP_REGISTERS f20, f22, f24, f26, f28, f30
84
85#elif V8_TARGET_ARCH_LOONG64
86// ===========================================================================
87// == loong64 ================================================================
88// ===========================================================================
89#define PARAM_REGISTERS a0, a1, a2, a3, a4, a5, a6, a7
90#define CALLEE_SAVE_REGISTERS s0, s1, s2, s3, s4, s5, s6, s7, s8, fp
91#define CALLEE_SAVE_FP_REGISTERS f24, f25, f26, f27, f28, f29, f30, f31
92
93#elif V8_TARGET_ARCH_PPC64
94// ===========================================================================
95// == ppc & ppc64 ============================================================
96// ===========================================================================
97#ifdef V8_TARGET_LITTLE_ENDIAN  // ppc64le linux
98#define STACK_SHADOW_WORDS 12
99#else  // AIX
100#define STACK_SHADOW_WORDS 14
101#endif
102#define PARAM_REGISTERS r3, r4, r5, r6, r7, r8, r9, r10
103#define CALLEE_SAVE_REGISTERS                                                \
104  r14, r15, r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, r26, r27, r28, \
105      r29, r30
106
107#define CALLEE_SAVE_FP_REGISTERS                                             \
108  d14, d15, d16, d17, d18, d19, d20, d21, d22, d23, d24, d25, d26, d27, d28, \
109      d29, d30, d31
110
111#elif V8_TARGET_ARCH_S390X
112// ===========================================================================
113// == s390x ==================================================================
114// ===========================================================================
115#define STACK_SHADOW_WORDS 20
116#define PARAM_REGISTERS r2, r3, r4, r5, r6
117#define CALLEE_SAVE_REGISTERS r6, r7, r8, r9, r10, ip, r13
118#define CALLEE_SAVE_FP_REGISTERS d8, d9, d10, d11, d12, d13, d14, d15
119
120#elif V8_TARGET_ARCH_RISCV64
121// ===========================================================================
122// == riscv64 =================================================================
123// ===========================================================================
124#define PARAM_REGISTERS a0, a1, a2, a3, a4, a5, a6, a7
125// fp is not part of CALLEE_SAVE_REGISTERS (similar to how MIPS64 or PPC defines
126// it)
127#define CALLEE_SAVE_REGISTERS s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11
128#define CALLEE_SAVE_FP_REGISTERS \
129  fs0, fs1, fs2, fs3, fs4, fs5, fs6, fs7, fs8, fs9, fs10, fs11
130#else
131// ===========================================================================
132// == unknown ================================================================
133// ===========================================================================
134#define UNSUPPORTED_C_LINKAGE 1
135#endif
136}  // namespace
137
138#if defined(V8_TARGET_OS_WIN) && defined(V8_TARGET_ARCH_X64)
139// As defined in
140// https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=vs-2019#parameter-passing,
141// Windows calling convention doesn't differentiate between GP and FP params
142// when counting how many of them should be placed in registers. That's why
143// we use the same counter {i} for both types here.
144void BuildParameterLocations(const MachineSignature* msig,
145                             size_t kFPParamRegisterCount,
146                             size_t kParamRegisterCount,
147                             const DoubleRegister* kFPParamRegisters,
148                             const v8::internal::Register* kParamRegisters,
149                             LocationSignature::Builder* out_locations) {
150#ifdef STACK_SHADOW_WORDS
151  int stack_offset = STACK_SHADOW_WORDS;
152#else
153  int stack_offset = 0;
154#endif
155  CHECK_EQ(kFPParamRegisterCount, kParamRegisterCount);
156
157  for (size_t i = 0; i < msig->parameter_count(); i++) {
158    MachineType type = msig->GetParam(i);
159    bool spill = (i >= kParamRegisterCount);
160    if (spill) {
161      out_locations->AddParam(
162          LinkageLocation::ForCallerFrameSlot(-1 - stack_offset, type));
163      stack_offset++;
164    } else {
165      if (IsFloatingPoint(type.representation())) {
166        out_locations->AddParam(
167            LinkageLocation::ForRegister(kFPParamRegisters[i].code(), type));
168      } else {
169        out_locations->AddParam(
170            LinkageLocation::ForRegister(kParamRegisters[i].code(), type));
171      }
172    }
173  }
174}
175#else  // defined(V8_TARGET_OS_WIN) && defined(V8_TARGET_ARCH_X64)
176// As defined in https://www.agner.org/optimize/calling_conventions.pdf,
177// Section 7, Linux and Mac place parameters in consecutive registers,
178// differentiating between GP and FP params. That's why we maintain two
179// separate counters here. This also applies to Arm systems following
180// the AAPCS and Windows on Arm.
181void BuildParameterLocations(const MachineSignature* msig,
182                             size_t kFPParamRegisterCount,
183                             size_t kParamRegisterCount,
184                             const DoubleRegister* kFPParamRegisters,
185                             const v8::internal::Register* kParamRegisters,
186                             LocationSignature::Builder* out_locations) {
187#ifdef STACK_SHADOW_WORDS
188  int stack_offset = STACK_SHADOW_WORDS;
189#else
190  int stack_offset = 0;
191#endif
192  size_t num_params = 0;
193  size_t num_fp_params = 0;
194  for (size_t i = 0; i < msig->parameter_count(); i++) {
195    MachineType type = msig->GetParam(i);
196    bool spill = IsFloatingPoint(type.representation())
197                     ? (num_fp_params >= kFPParamRegisterCount)
198                     : (num_params >= kParamRegisterCount);
199    if (spill) {
200      out_locations->AddParam(
201          LinkageLocation::ForCallerFrameSlot(-1 - stack_offset, type));
202      stack_offset++;
203    } else {
204      if (IsFloatingPoint(type.representation())) {
205        out_locations->AddParam(LinkageLocation::ForRegister(
206            kFPParamRegisters[num_fp_params].code(), type));
207        ++num_fp_params;
208      } else {
209        out_locations->AddParam(LinkageLocation::ForRegister(
210            kParamRegisters[num_params].code(), type));
211        ++num_params;
212      }
213    }
214  }
215}
216#endif  // defined(V8_TARGET_OS_WIN) && defined(V8_TARGET_ARCH_X64)
217
218// General code uses the above configuration data.
219CallDescriptor* Linkage::GetSimplifiedCDescriptor(Zone* zone,
220                                                  const MachineSignature* msig,
221                                                  CallDescriptor::Flags flags) {
222#ifdef UNSUPPORTED_C_LINKAGE
223  // This method should not be called on unknown architectures.
224  FATAL("requested C call descriptor on unsupported architecture");
225  return nullptr;
226#endif
227
228  DCHECK_LE(msig->parameter_count(), static_cast<size_t>(kMaxCParameters));
229
230  LocationSignature::Builder locations(zone, msig->return_count(),
231                                       msig->parameter_count());
232
233#ifndef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
234  // Check the types of the signature.
235  for (size_t i = 0; i < msig->parameter_count(); i++) {
236    MachineType type = msig->GetParam(i);
237    CHECK(!IsFloatingPoint(type.representation()));
238  }
239
240  // Check the return types.
241  for (size_t i = 0; i < locations.return_count_; i++) {
242    MachineType type = msig->GetReturn(i);
243    CHECK(!IsFloatingPoint(type.representation()));
244  }
245#endif
246
247  CHECK_GE(2, locations.return_count_);
248  if (locations.return_count_ > 0) {
249#ifdef FP_RETURN_REGISTER
250    const v8::internal::DoubleRegister kFPReturnRegister = FP_RETURN_REGISTER;
251    auto reg = IsFloatingPoint(msig->GetReturn(0).representation())
252                   ? kFPReturnRegister.code()
253                   : kReturnRegister0.code();
254#else
255    auto reg = kReturnRegister0.code();
256#endif
257    // TODO(chromium:1052746): Use the correctly sized register here (e.g. "al"
258    // if the return type is kBit), so we don't have to use a hacky bitwise AND
259    // elsewhere.
260    locations.AddReturn(LinkageLocation::ForRegister(reg, msig->GetReturn(0)));
261  }
262
263  if (locations.return_count_ > 1) {
264    DCHECK(!IsFloatingPoint(msig->GetReturn(0).representation()));
265
266    locations.AddReturn(LinkageLocation::ForRegister(kReturnRegister1.code(),
267                                                     msig->GetReturn(1)));
268  }
269
270#ifdef PARAM_REGISTERS
271  const v8::internal::Register kParamRegisters[] = {PARAM_REGISTERS};
272  const int kParamRegisterCount = static_cast<int>(arraysize(kParamRegisters));
273#else
274  const v8::internal::Register* kParamRegisters = nullptr;
275  const int kParamRegisterCount = 0;
276#endif
277
278#ifdef FP_PARAM_REGISTERS
279  const DoubleRegister kFPParamRegisters[] = {FP_PARAM_REGISTERS};
280  const size_t kFPParamRegisterCount = arraysize(kFPParamRegisters);
281#else
282  const DoubleRegister* kFPParamRegisters = nullptr;
283  const size_t kFPParamRegisterCount = 0;
284#endif
285
286  // Add register and/or stack parameter(s).
287  BuildParameterLocations(msig, kFPParamRegisterCount, kParamRegisterCount,
288                          kFPParamRegisters, kParamRegisters, &locations);
289
290  const RegList kCalleeSaveRegisters = {CALLEE_SAVE_REGISTERS};
291  const DoubleRegList kCalleeSaveFPRegisters = {CALLEE_SAVE_FP_REGISTERS};
292
293  // The target for C calls is always an address (i.e. machine pointer).
294  MachineType target_type = MachineType::Pointer();
295  LinkageLocation target_loc = LinkageLocation::ForAnyRegister(target_type);
296  flags |= CallDescriptor::kNoAllocate;
297
298  return zone->New<CallDescriptor>(  // --
299      CallDescriptor::kCallAddress,  // kind
300      target_type,                   // target MachineType
301      target_loc,                    // target location
302      locations.Build(),             // location_sig
303      0,                             // stack_parameter_count
304      Operator::kNoThrow,            // properties
305      kCalleeSaveRegisters,          // callee-saved registers
306      kCalleeSaveFPRegisters,        // callee-saved fp regs
307      flags, "c-call");
308}
309
310}  // namespace compiler
311}  // namespace internal
312}  // namespace v8
313