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
11 namespace v8 {
12 namespace internal {
13 namespace compiler {
14
15 namespace {
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.
BuildParameterLocations(const MachineSignature * msig,size_t kFPParamRegisterCount,size_t kParamRegisterCount,const DoubleRegister * kFPParamRegisters,const v8::internal::Register * kParamRegisters,LocationSignature::Builder * out_locations)144 void 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.
BuildParameterLocations(const MachineSignature * msig,size_t kFPParamRegisterCount,size_t kParamRegisterCount,const DoubleRegister * kFPParamRegisters,const v8::internal::Register * kParamRegisters,LocationSignature::Builder * out_locations)181 void 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.
GetSimplifiedCDescriptor(Zone * zone,const MachineSignature * msig,CallDescriptor::Flags flags)219 CallDescriptor* 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