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.bit() | edi.bit() | ebx.bit()
23
24 #elif V8_TARGET_ARCH_X64
25 // ===========================================================================
26 // == x64 ====================================================================
27 // ===========================================================================
28
29 #ifdef V8_TARGET_OS_WIN
30 // == x64 windows ============================================================
31 #define STACK_SHADOW_WORDS 4
32 #define PARAM_REGISTERS rcx, rdx, r8, r9
33 #define FP_PARAM_REGISTERS xmm0, xmm1, xmm2, xmm3
34 #define CALLEE_SAVE_REGISTERS \
35 rbx.bit() | rdi.bit() | rsi.bit() | r12.bit() | r13.bit() | r14.bit() | \
36 r15.bit()
37 #define CALLEE_SAVE_FP_REGISTERS \
38 (1 << xmm6.code()) | (1 << xmm7.code()) | (1 << xmm8.code()) | \
39 (1 << xmm9.code()) | (1 << xmm10.code()) | (1 << xmm11.code()) | \
40 (1 << xmm12.code()) | (1 << xmm13.code()) | (1 << xmm14.code()) | \
41 (1 << xmm15.code())
42 #else // V8_TARGET_OS_WIN
43 // == x64 other ==============================================================
44 #define PARAM_REGISTERS rdi, rsi, rdx, rcx, r8, r9
45 #define FP_PARAM_REGISTERS xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7
46 #define CALLEE_SAVE_REGISTERS \
47 rbx.bit() | r12.bit() | r13.bit() | r14.bit() | r15.bit()
48 #endif // V8_TARGET_OS_WIN
49
50 #elif V8_TARGET_ARCH_ARM
51 // ===========================================================================
52 // == arm ====================================================================
53 // ===========================================================================
54 #define PARAM_REGISTERS r0, r1, r2, r3
55 #define CALLEE_SAVE_REGISTERS \
56 r4.bit() | r5.bit() | r6.bit() | r7.bit() | r8.bit() | r9.bit() | r10.bit()
57 #define CALLEE_SAVE_FP_REGISTERS \
58 (1 << d8.code()) | (1 << d9.code()) | (1 << d10.code()) | \
59 (1 << d11.code()) | (1 << d12.code()) | (1 << d13.code()) | \
60 (1 << d14.code()) | (1 << d15.code())
61
62
63 #elif V8_TARGET_ARCH_ARM64
64 // ===========================================================================
65 // == arm64 ====================================================================
66 // ===========================================================================
67 #define PARAM_REGISTERS x0, x1, x2, x3, x4, x5, x6, x7
68 #define CALLEE_SAVE_REGISTERS \
69 (1 << x19.code()) | (1 << x20.code()) | (1 << x21.code()) | \
70 (1 << x22.code()) | (1 << x23.code()) | (1 << x24.code()) | \
71 (1 << x25.code()) | (1 << x26.code()) | (1 << x27.code()) | \
72 (1 << x28.code())
73
74 #define CALLEE_SAVE_FP_REGISTERS \
75 (1 << d8.code()) | (1 << d9.code()) | (1 << d10.code()) | \
76 (1 << d11.code()) | (1 << d12.code()) | (1 << d13.code()) | \
77 (1 << d14.code()) | (1 << d15.code())
78
79 #elif V8_TARGET_ARCH_MIPS
80 // ===========================================================================
81 // == mips ===================================================================
82 // ===========================================================================
83 #define STACK_SHADOW_WORDS 4
84 #define PARAM_REGISTERS a0, a1, a2, a3
85 #define CALLEE_SAVE_REGISTERS \
86 s0.bit() | s1.bit() | s2.bit() | s3.bit() | s4.bit() | s5.bit() | s6.bit() | \
87 s7.bit()
88 #define CALLEE_SAVE_FP_REGISTERS \
89 f20.bit() | f22.bit() | f24.bit() | f26.bit() | f28.bit() | f30.bit()
90
91 #elif V8_TARGET_ARCH_MIPS64
92 // ===========================================================================
93 // == mips64 =================================================================
94 // ===========================================================================
95 #define PARAM_REGISTERS a0, a1, a2, a3, a4, a5, a6, a7
96 #define CALLEE_SAVE_REGISTERS \
97 s0.bit() | s1.bit() | s2.bit() | s3.bit() | s4.bit() | s5.bit() | s6.bit() | \
98 s7.bit()
99 #define CALLEE_SAVE_FP_REGISTERS \
100 f20.bit() | f22.bit() | f24.bit() | f26.bit() | f28.bit() | f30.bit()
101
102 #elif V8_TARGET_ARCH_PPC64
103 // ===========================================================================
104 // == ppc & ppc64 ============================================================
105 // ===========================================================================
106 #ifdef V8_TARGET_LITTLE_ENDIAN // ppc64le linux
107 #define STACK_SHADOW_WORDS 12
108 #else // AIX
109 #define STACK_SHADOW_WORDS 14
110 #endif
111 #define PARAM_REGISTERS r3, r4, r5, r6, r7, r8, r9, r10
112 #define CALLEE_SAVE_REGISTERS \
113 r14.bit() | r15.bit() | r16.bit() | r17.bit() | r18.bit() | r19.bit() | \
114 r20.bit() | r21.bit() | r22.bit() | r23.bit() | r24.bit() | r25.bit() | \
115 r26.bit() | r27.bit() | r28.bit() | r29.bit() | r30.bit()
116 #define CALLEE_SAVE_FP_REGISTERS \
117 d14.bit() | d15.bit() | d16.bit() | d17.bit() | d18.bit() | d19.bit() | \
118 d20.bit() | d21.bit() | d22.bit() | d23.bit() | d24.bit() | d25.bit() | \
119 d26.bit() | d27.bit() | d28.bit() | d29.bit() | d30.bit() | d31.bit()
120
121 #elif V8_TARGET_ARCH_S390X
122 // ===========================================================================
123 // == s390x ==================================================================
124 // ===========================================================================
125 #define STACK_SHADOW_WORDS 20
126 #define PARAM_REGISTERS r2, r3, r4, r5, r6
127 #define CALLEE_SAVE_REGISTERS \
128 r6.bit() | r7.bit() | r8.bit() | r9.bit() | r10.bit() | ip.bit() | r13.bit()
129 #define CALLEE_SAVE_FP_REGISTERS \
130 d8.bit() | d9.bit() | d10.bit() | d11.bit() | d12.bit() | d13.bit() | \
131 d14.bit() | d15.bit()
132
133 #else
134 // ===========================================================================
135 // == unknown ================================================================
136 // ===========================================================================
137 #define UNSUPPORTED_C_LINKAGE 1
138 #endif
139 } // namespace
140
141 #if defined(V8_TARGET_OS_WIN) && defined(V8_TARGET_ARCH_X64)
142 // As defined in
143 // https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=vs-2019#parameter-passing,
144 // Windows calling convention doesn't differentiate between GP and FP params
145 // when counting how many of them should be placed in registers. That's why
146 // 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)147 void BuildParameterLocations(const MachineSignature* msig,
148 size_t kFPParamRegisterCount,
149 size_t kParamRegisterCount,
150 const DoubleRegister* kFPParamRegisters,
151 const v8::internal::Register* kParamRegisters,
152 LocationSignature::Builder* out_locations) {
153 #ifdef STACK_SHADOW_WORDS
154 int stack_offset = STACK_SHADOW_WORDS;
155 #else
156 int stack_offset = 0;
157 #endif
158 CHECK_EQ(kFPParamRegisterCount, kParamRegisterCount);
159
160 for (size_t i = 0; i < msig->parameter_count(); i++) {
161 MachineType type = msig->GetParam(i);
162 bool spill = (i >= kParamRegisterCount);
163 if (spill) {
164 out_locations->AddParam(
165 LinkageLocation::ForCallerFrameSlot(-1 - stack_offset, type));
166 stack_offset++;
167 } else {
168 if (IsFloatingPoint(type.representation())) {
169 out_locations->AddParam(
170 LinkageLocation::ForRegister(kFPParamRegisters[i].code(), type));
171 } else {
172 out_locations->AddParam(
173 LinkageLocation::ForRegister(kParamRegisters[i].code(), type));
174 }
175 }
176 }
177 }
178 #else // defined(V8_TARGET_OS_WIN) && defined(V8_TARGET_ARCH_X64)
179 // As defined in https://www.agner.org/optimize/calling_conventions.pdf,
180 // Section 7, Linux and Mac place parameters in consecutive registers,
181 // differentiating between GP and FP params. That's why we maintain two
182 // separate counters here. This also applies to Arm systems following
183 // 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)184 void BuildParameterLocations(const MachineSignature* msig,
185 size_t kFPParamRegisterCount,
186 size_t kParamRegisterCount,
187 const DoubleRegister* kFPParamRegisters,
188 const v8::internal::Register* kParamRegisters,
189 LocationSignature::Builder* out_locations) {
190 #ifdef STACK_SHADOW_WORDS
191 int stack_offset = STACK_SHADOW_WORDS;
192 #else
193 int stack_offset = 0;
194 #endif
195 size_t num_params = 0;
196 size_t num_fp_params = 0;
197 for (size_t i = 0; i < msig->parameter_count(); i++) {
198 MachineType type = msig->GetParam(i);
199 bool spill = IsFloatingPoint(type.representation())
200 ? (num_fp_params >= kFPParamRegisterCount)
201 : (num_params >= kParamRegisterCount);
202 if (spill) {
203 out_locations->AddParam(
204 LinkageLocation::ForCallerFrameSlot(-1 - stack_offset, type));
205 stack_offset++;
206 } else {
207 if (IsFloatingPoint(type.representation())) {
208 out_locations->AddParam(LinkageLocation::ForRegister(
209 kFPParamRegisters[num_fp_params].code(), type));
210 ++num_fp_params;
211 } else {
212 out_locations->AddParam(LinkageLocation::ForRegister(
213 kParamRegisters[num_params].code(), type));
214 ++num_params;
215 }
216 }
217 }
218 }
219 #endif // defined(V8_TARGET_OS_WIN) && defined(V8_TARGET_ARCH_X64)
220
221 // General code uses the above configuration data.
GetSimplifiedCDescriptor(Zone * zone,const MachineSignature * msig,CallDescriptor::Flags flags)222 CallDescriptor* Linkage::GetSimplifiedCDescriptor(Zone* zone,
223 const MachineSignature* msig,
224 CallDescriptor::Flags flags) {
225 #ifdef UNSUPPORTED_C_LINKAGE
226 // This method should not be called on unknown architectures.
227 FATAL("requested C call descriptor on unsupported architecture");
228 return nullptr;
229 #endif
230
231 DCHECK_LE(msig->parameter_count(), static_cast<size_t>(kMaxCParameters));
232
233 LocationSignature::Builder locations(zone, msig->return_count(),
234 msig->parameter_count());
235
236 #ifndef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
237 // Check the types of the signature.
238 for (size_t i = 0; i < msig->parameter_count(); i++) {
239 MachineRepresentation rep = msig->GetParam(i).representation();
240 CHECK_NE(MachineRepresentation::kFloat32, rep);
241 CHECK_NE(MachineRepresentation::kFloat64, rep);
242 }
243 #endif
244
245 // Add return location(s). We don't support FP returns for now.
246 for (size_t i = 0; i < locations.return_count_; i++) {
247 MachineType type = msig->GetReturn(i);
248 CHECK(!IsFloatingPoint(type.representation()));
249 }
250
251 CHECK_GE(2, locations.return_count_);
252 if (locations.return_count_ > 0) {
253 locations.AddReturn(LinkageLocation::ForRegister(kReturnRegister0.code(),
254 msig->GetReturn(0)));
255 }
256 if (locations.return_count_ > 1) {
257 locations.AddReturn(LinkageLocation::ForRegister(kReturnRegister1.code(),
258 msig->GetReturn(1)));
259 }
260
261 #ifdef PARAM_REGISTERS
262 const v8::internal::Register kParamRegisters[] = {PARAM_REGISTERS};
263 const int kParamRegisterCount = static_cast<int>(arraysize(kParamRegisters));
264 #else
265 const v8::internal::Register* kParamRegisters = nullptr;
266 const int kParamRegisterCount = 0;
267 #endif
268
269 #ifdef FP_PARAM_REGISTERS
270 const DoubleRegister kFPParamRegisters[] = {FP_PARAM_REGISTERS};
271 const size_t kFPParamRegisterCount = arraysize(kFPParamRegisters);
272 #else
273 const DoubleRegister* kFPParamRegisters = nullptr;
274 const size_t kFPParamRegisterCount = 0;
275 #endif
276
277 // Add register and/or stack parameter(s).
278 BuildParameterLocations(msig, kFPParamRegisterCount, kParamRegisterCount,
279 kFPParamRegisters, kParamRegisters, &locations);
280
281 #ifdef CALLEE_SAVE_REGISTERS
282 const RegList kCalleeSaveRegisters = CALLEE_SAVE_REGISTERS;
283 #else
284 const RegList kCalleeSaveRegisters = 0;
285 #endif
286
287 #ifdef CALLEE_SAVE_FP_REGISTERS
288 const RegList kCalleeSaveFPRegisters = CALLEE_SAVE_FP_REGISTERS;
289 #else
290 const RegList kCalleeSaveFPRegisters = 0;
291 #endif
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