annotate libffi/src/x86/ffi64.c @ 158:494b0b89df80 default tip

...
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 25 May 2020 18:13:55 +0900
parents 04ced10e8804
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
111
kono
parents:
diff changeset
1 /* -----------------------------------------------------------------------
kono
parents:
diff changeset
2 ffi64.c - Copyright (c) 2013 The Written Word, Inc.
kono
parents:
diff changeset
3 Copyright (c) 2011 Anthony Green
kono
parents:
diff changeset
4 Copyright (c) 2008, 2010 Red Hat, Inc.
kono
parents:
diff changeset
5 Copyright (c) 2002, 2007 Bo Thorsen <bo@suse.de>
kono
parents:
diff changeset
6
kono
parents:
diff changeset
7 x86-64 Foreign Function Interface
kono
parents:
diff changeset
8
kono
parents:
diff changeset
9 Permission is hereby granted, free of charge, to any person obtaining
kono
parents:
diff changeset
10 a copy of this software and associated documentation files (the
kono
parents:
diff changeset
11 ``Software''), to deal in the Software without restriction, including
kono
parents:
diff changeset
12 without limitation the rights to use, copy, modify, merge, publish,
kono
parents:
diff changeset
13 distribute, sublicense, and/or sell copies of the Software, and to
kono
parents:
diff changeset
14 permit persons to whom the Software is furnished to do so, subject to
kono
parents:
diff changeset
15 the following conditions:
kono
parents:
diff changeset
16
kono
parents:
diff changeset
17 The above copyright notice and this permission notice shall be included
kono
parents:
diff changeset
18 in all copies or substantial portions of the Software.
kono
parents:
diff changeset
19
kono
parents:
diff changeset
20 THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
kono
parents:
diff changeset
21 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
kono
parents:
diff changeset
22 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
kono
parents:
diff changeset
23 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
kono
parents:
diff changeset
24 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
kono
parents:
diff changeset
25 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
kono
parents:
diff changeset
26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
kono
parents:
diff changeset
27 DEALINGS IN THE SOFTWARE.
kono
parents:
diff changeset
28 ----------------------------------------------------------------------- */
kono
parents:
diff changeset
29
kono
parents:
diff changeset
30 #include <ffi.h>
kono
parents:
diff changeset
31 #include <ffi_common.h>
kono
parents:
diff changeset
32
kono
parents:
diff changeset
33 #include <stdlib.h>
kono
parents:
diff changeset
34 #include <stdarg.h>
kono
parents:
diff changeset
35 #include <stdint.h>
kono
parents:
diff changeset
36 #include "internal64.h"
kono
parents:
diff changeset
37
kono
parents:
diff changeset
38 #ifdef __x86_64__
kono
parents:
diff changeset
39
kono
parents:
diff changeset
40 #define MAX_GPR_REGS 6
kono
parents:
diff changeset
41 #define MAX_SSE_REGS 8
kono
parents:
diff changeset
42
kono
parents:
diff changeset
43 #if defined(__INTEL_COMPILER)
kono
parents:
diff changeset
44 #include "xmmintrin.h"
kono
parents:
diff changeset
45 #define UINT128 __m128
kono
parents:
diff changeset
46 #else
kono
parents:
diff changeset
47 #if defined(__SUNPRO_C)
kono
parents:
diff changeset
48 #include <sunmedia_types.h>
kono
parents:
diff changeset
49 #define UINT128 __m128i
kono
parents:
diff changeset
50 #else
kono
parents:
diff changeset
51 #define UINT128 __int128_t
kono
parents:
diff changeset
52 #endif
kono
parents:
diff changeset
53 #endif
kono
parents:
diff changeset
54
kono
parents:
diff changeset
55 union big_int_union
kono
parents:
diff changeset
56 {
kono
parents:
diff changeset
57 UINT32 i32;
kono
parents:
diff changeset
58 UINT64 i64;
kono
parents:
diff changeset
59 UINT128 i128;
kono
parents:
diff changeset
60 };
kono
parents:
diff changeset
61
kono
parents:
diff changeset
62 struct register_args
kono
parents:
diff changeset
63 {
kono
parents:
diff changeset
64 /* Registers for argument passing. */
kono
parents:
diff changeset
65 UINT64 gpr[MAX_GPR_REGS];
kono
parents:
diff changeset
66 union big_int_union sse[MAX_SSE_REGS];
kono
parents:
diff changeset
67 UINT64 rax; /* ssecount */
kono
parents:
diff changeset
68 UINT64 r10; /* static chain */
kono
parents:
diff changeset
69 };
kono
parents:
diff changeset
70
kono
parents:
diff changeset
71 extern void ffi_call_unix64 (void *args, unsigned long bytes, unsigned flags,
kono
parents:
diff changeset
72 void *raddr, void (*fnaddr)(void)) FFI_HIDDEN;
kono
parents:
diff changeset
73
kono
parents:
diff changeset
74 /* All reference to register classes here is identical to the code in
kono
parents:
diff changeset
75 gcc/config/i386/i386.c. Do *not* change one without the other. */
kono
parents:
diff changeset
76
kono
parents:
diff changeset
77 /* Register class used for passing given 64bit part of the argument.
kono
parents:
diff changeset
78 These represent classes as documented by the PS ABI, with the
kono
parents:
diff changeset
79 exception of SSESF, SSEDF classes, that are basically SSE class,
kono
parents:
diff changeset
80 just gcc will use SF or DFmode move instead of DImode to avoid
kono
parents:
diff changeset
81 reformatting penalties.
kono
parents:
diff changeset
82
kono
parents:
diff changeset
83 Similary we play games with INTEGERSI_CLASS to use cheaper SImode moves
kono
parents:
diff changeset
84 whenever possible (upper half does contain padding). */
kono
parents:
diff changeset
85 enum x86_64_reg_class
kono
parents:
diff changeset
86 {
kono
parents:
diff changeset
87 X86_64_NO_CLASS,
kono
parents:
diff changeset
88 X86_64_INTEGER_CLASS,
kono
parents:
diff changeset
89 X86_64_INTEGERSI_CLASS,
kono
parents:
diff changeset
90 X86_64_SSE_CLASS,
kono
parents:
diff changeset
91 X86_64_SSESF_CLASS,
kono
parents:
diff changeset
92 X86_64_SSEDF_CLASS,
kono
parents:
diff changeset
93 X86_64_SSEUP_CLASS,
kono
parents:
diff changeset
94 X86_64_X87_CLASS,
kono
parents:
diff changeset
95 X86_64_X87UP_CLASS,
kono
parents:
diff changeset
96 X86_64_COMPLEX_X87_CLASS,
kono
parents:
diff changeset
97 X86_64_MEMORY_CLASS
kono
parents:
diff changeset
98 };
kono
parents:
diff changeset
99
kono
parents:
diff changeset
100 #define MAX_CLASSES 4
kono
parents:
diff changeset
101
kono
parents:
diff changeset
102 #define SSE_CLASS_P(X) ((X) >= X86_64_SSE_CLASS && X <= X86_64_SSEUP_CLASS)
kono
parents:
diff changeset
103
kono
parents:
diff changeset
104 /* x86-64 register passing implementation. See x86-64 ABI for details. Goal
kono
parents:
diff changeset
105 of this code is to classify each 8bytes of incoming argument by the register
kono
parents:
diff changeset
106 class and assign registers accordingly. */
kono
parents:
diff changeset
107
kono
parents:
diff changeset
108 /* Return the union class of CLASS1 and CLASS2.
kono
parents:
diff changeset
109 See the x86-64 PS ABI for details. */
kono
parents:
diff changeset
110
kono
parents:
diff changeset
111 static enum x86_64_reg_class
kono
parents:
diff changeset
112 merge_classes (enum x86_64_reg_class class1, enum x86_64_reg_class class2)
kono
parents:
diff changeset
113 {
kono
parents:
diff changeset
114 /* Rule #1: If both classes are equal, this is the resulting class. */
kono
parents:
diff changeset
115 if (class1 == class2)
kono
parents:
diff changeset
116 return class1;
kono
parents:
diff changeset
117
kono
parents:
diff changeset
118 /* Rule #2: If one of the classes is NO_CLASS, the resulting class is
kono
parents:
diff changeset
119 the other class. */
kono
parents:
diff changeset
120 if (class1 == X86_64_NO_CLASS)
kono
parents:
diff changeset
121 return class2;
kono
parents:
diff changeset
122 if (class2 == X86_64_NO_CLASS)
kono
parents:
diff changeset
123 return class1;
kono
parents:
diff changeset
124
kono
parents:
diff changeset
125 /* Rule #3: If one of the classes is MEMORY, the result is MEMORY. */
kono
parents:
diff changeset
126 if (class1 == X86_64_MEMORY_CLASS || class2 == X86_64_MEMORY_CLASS)
kono
parents:
diff changeset
127 return X86_64_MEMORY_CLASS;
kono
parents:
diff changeset
128
kono
parents:
diff changeset
129 /* Rule #4: If one of the classes is INTEGER, the result is INTEGER. */
kono
parents:
diff changeset
130 if ((class1 == X86_64_INTEGERSI_CLASS && class2 == X86_64_SSESF_CLASS)
kono
parents:
diff changeset
131 || (class2 == X86_64_INTEGERSI_CLASS && class1 == X86_64_SSESF_CLASS))
kono
parents:
diff changeset
132 return X86_64_INTEGERSI_CLASS;
kono
parents:
diff changeset
133 if (class1 == X86_64_INTEGER_CLASS || class1 == X86_64_INTEGERSI_CLASS
kono
parents:
diff changeset
134 || class2 == X86_64_INTEGER_CLASS || class2 == X86_64_INTEGERSI_CLASS)
kono
parents:
diff changeset
135 return X86_64_INTEGER_CLASS;
kono
parents:
diff changeset
136
kono
parents:
diff changeset
137 /* Rule #5: If one of the classes is X87, X87UP, or COMPLEX_X87 class,
kono
parents:
diff changeset
138 MEMORY is used. */
kono
parents:
diff changeset
139 if (class1 == X86_64_X87_CLASS
kono
parents:
diff changeset
140 || class1 == X86_64_X87UP_CLASS
kono
parents:
diff changeset
141 || class1 == X86_64_COMPLEX_X87_CLASS
kono
parents:
diff changeset
142 || class2 == X86_64_X87_CLASS
kono
parents:
diff changeset
143 || class2 == X86_64_X87UP_CLASS
kono
parents:
diff changeset
144 || class2 == X86_64_COMPLEX_X87_CLASS)
kono
parents:
diff changeset
145 return X86_64_MEMORY_CLASS;
kono
parents:
diff changeset
146
kono
parents:
diff changeset
147 /* Rule #6: Otherwise class SSE is used. */
kono
parents:
diff changeset
148 return X86_64_SSE_CLASS;
kono
parents:
diff changeset
149 }
kono
parents:
diff changeset
150
kono
parents:
diff changeset
151 /* Classify the argument of type TYPE and mode MODE.
kono
parents:
diff changeset
152 CLASSES will be filled by the register class used to pass each word
kono
parents:
diff changeset
153 of the operand. The number of words is returned. In case the parameter
kono
parents:
diff changeset
154 should be passed in memory, 0 is returned. As a special case for zero
kono
parents:
diff changeset
155 sized containers, classes[0] will be NO_CLASS and 1 is returned.
kono
parents:
diff changeset
156
kono
parents:
diff changeset
157 See the x86-64 PS ABI for details.
kono
parents:
diff changeset
158 */
kono
parents:
diff changeset
159 static size_t
kono
parents:
diff changeset
160 classify_argument (ffi_type *type, enum x86_64_reg_class classes[],
kono
parents:
diff changeset
161 size_t byte_offset)
kono
parents:
diff changeset
162 {
kono
parents:
diff changeset
163 switch (type->type)
kono
parents:
diff changeset
164 {
kono
parents:
diff changeset
165 case FFI_TYPE_UINT8:
kono
parents:
diff changeset
166 case FFI_TYPE_SINT8:
kono
parents:
diff changeset
167 case FFI_TYPE_UINT16:
kono
parents:
diff changeset
168 case FFI_TYPE_SINT16:
kono
parents:
diff changeset
169 case FFI_TYPE_UINT32:
kono
parents:
diff changeset
170 case FFI_TYPE_SINT32:
kono
parents:
diff changeset
171 case FFI_TYPE_UINT64:
kono
parents:
diff changeset
172 case FFI_TYPE_SINT64:
kono
parents:
diff changeset
173 case FFI_TYPE_POINTER:
kono
parents:
diff changeset
174 do_integer:
kono
parents:
diff changeset
175 {
kono
parents:
diff changeset
176 size_t size = byte_offset + type->size;
kono
parents:
diff changeset
177
kono
parents:
diff changeset
178 if (size <= 4)
kono
parents:
diff changeset
179 {
kono
parents:
diff changeset
180 classes[0] = X86_64_INTEGERSI_CLASS;
kono
parents:
diff changeset
181 return 1;
kono
parents:
diff changeset
182 }
kono
parents:
diff changeset
183 else if (size <= 8)
kono
parents:
diff changeset
184 {
kono
parents:
diff changeset
185 classes[0] = X86_64_INTEGER_CLASS;
kono
parents:
diff changeset
186 return 1;
kono
parents:
diff changeset
187 }
kono
parents:
diff changeset
188 else if (size <= 12)
kono
parents:
diff changeset
189 {
kono
parents:
diff changeset
190 classes[0] = X86_64_INTEGER_CLASS;
kono
parents:
diff changeset
191 classes[1] = X86_64_INTEGERSI_CLASS;
kono
parents:
diff changeset
192 return 2;
kono
parents:
diff changeset
193 }
kono
parents:
diff changeset
194 else if (size <= 16)
kono
parents:
diff changeset
195 {
kono
parents:
diff changeset
196 classes[0] = classes[1] = X86_64_INTEGER_CLASS;
kono
parents:
diff changeset
197 return 2;
kono
parents:
diff changeset
198 }
kono
parents:
diff changeset
199 else
kono
parents:
diff changeset
200 FFI_ASSERT (0);
kono
parents:
diff changeset
201 }
kono
parents:
diff changeset
202 case FFI_TYPE_FLOAT:
kono
parents:
diff changeset
203 if (!(byte_offset % 8))
kono
parents:
diff changeset
204 classes[0] = X86_64_SSESF_CLASS;
kono
parents:
diff changeset
205 else
kono
parents:
diff changeset
206 classes[0] = X86_64_SSE_CLASS;
kono
parents:
diff changeset
207 return 1;
kono
parents:
diff changeset
208 case FFI_TYPE_DOUBLE:
kono
parents:
diff changeset
209 classes[0] = X86_64_SSEDF_CLASS;
kono
parents:
diff changeset
210 return 1;
kono
parents:
diff changeset
211 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
kono
parents:
diff changeset
212 case FFI_TYPE_LONGDOUBLE:
kono
parents:
diff changeset
213 classes[0] = X86_64_X87_CLASS;
kono
parents:
diff changeset
214 classes[1] = X86_64_X87UP_CLASS;
kono
parents:
diff changeset
215 return 2;
kono
parents:
diff changeset
216 #endif
kono
parents:
diff changeset
217 case FFI_TYPE_STRUCT:
kono
parents:
diff changeset
218 {
kono
parents:
diff changeset
219 const size_t UNITS_PER_WORD = 8;
kono
parents:
diff changeset
220 size_t words = (type->size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
kono
parents:
diff changeset
221 ffi_type **ptr;
kono
parents:
diff changeset
222 int i;
kono
parents:
diff changeset
223 enum x86_64_reg_class subclasses[MAX_CLASSES];
kono
parents:
diff changeset
224
kono
parents:
diff changeset
225 /* If the struct is larger than 32 bytes, pass it on the stack. */
kono
parents:
diff changeset
226 if (type->size > 32)
kono
parents:
diff changeset
227 return 0;
kono
parents:
diff changeset
228
kono
parents:
diff changeset
229 for (i = 0; i < words; i++)
kono
parents:
diff changeset
230 classes[i] = X86_64_NO_CLASS;
kono
parents:
diff changeset
231
kono
parents:
diff changeset
232 /* Zero sized arrays or structures are NO_CLASS. We return 0 to
kono
parents:
diff changeset
233 signalize memory class, so handle it as special case. */
kono
parents:
diff changeset
234 if (!words)
kono
parents:
diff changeset
235 {
kono
parents:
diff changeset
236 case FFI_TYPE_VOID:
kono
parents:
diff changeset
237 classes[0] = X86_64_NO_CLASS;
kono
parents:
diff changeset
238 return 1;
kono
parents:
diff changeset
239 }
kono
parents:
diff changeset
240
kono
parents:
diff changeset
241 /* Merge the fields of structure. */
kono
parents:
diff changeset
242 for (ptr = type->elements; *ptr != NULL; ptr++)
kono
parents:
diff changeset
243 {
kono
parents:
diff changeset
244 size_t num;
kono
parents:
diff changeset
245
kono
parents:
diff changeset
246 byte_offset = ALIGN (byte_offset, (*ptr)->alignment);
kono
parents:
diff changeset
247
kono
parents:
diff changeset
248 num = classify_argument (*ptr, subclasses, byte_offset % 8);
kono
parents:
diff changeset
249 if (num == 0)
kono
parents:
diff changeset
250 return 0;
kono
parents:
diff changeset
251 for (i = 0; i < num; i++)
kono
parents:
diff changeset
252 {
kono
parents:
diff changeset
253 size_t pos = byte_offset / 8;
kono
parents:
diff changeset
254 classes[i + pos] =
kono
parents:
diff changeset
255 merge_classes (subclasses[i], classes[i + pos]);
kono
parents:
diff changeset
256 }
kono
parents:
diff changeset
257
kono
parents:
diff changeset
258 byte_offset += (*ptr)->size;
kono
parents:
diff changeset
259 }
kono
parents:
diff changeset
260
kono
parents:
diff changeset
261 if (words > 2)
kono
parents:
diff changeset
262 {
kono
parents:
diff changeset
263 /* When size > 16 bytes, if the first one isn't
kono
parents:
diff changeset
264 X86_64_SSE_CLASS or any other ones aren't
kono
parents:
diff changeset
265 X86_64_SSEUP_CLASS, everything should be passed in
kono
parents:
diff changeset
266 memory. */
kono
parents:
diff changeset
267 if (classes[0] != X86_64_SSE_CLASS)
kono
parents:
diff changeset
268 return 0;
kono
parents:
diff changeset
269
kono
parents:
diff changeset
270 for (i = 1; i < words; i++)
kono
parents:
diff changeset
271 if (classes[i] != X86_64_SSEUP_CLASS)
kono
parents:
diff changeset
272 return 0;
kono
parents:
diff changeset
273 }
kono
parents:
diff changeset
274
kono
parents:
diff changeset
275 /* Final merger cleanup. */
kono
parents:
diff changeset
276 for (i = 0; i < words; i++)
kono
parents:
diff changeset
277 {
kono
parents:
diff changeset
278 /* If one class is MEMORY, everything should be passed in
kono
parents:
diff changeset
279 memory. */
kono
parents:
diff changeset
280 if (classes[i] == X86_64_MEMORY_CLASS)
kono
parents:
diff changeset
281 return 0;
kono
parents:
diff changeset
282
kono
parents:
diff changeset
283 /* The X86_64_SSEUP_CLASS should be always preceded by
kono
parents:
diff changeset
284 X86_64_SSE_CLASS or X86_64_SSEUP_CLASS. */
kono
parents:
diff changeset
285 if (classes[i] == X86_64_SSEUP_CLASS
kono
parents:
diff changeset
286 && classes[i - 1] != X86_64_SSE_CLASS
kono
parents:
diff changeset
287 && classes[i - 1] != X86_64_SSEUP_CLASS)
kono
parents:
diff changeset
288 {
kono
parents:
diff changeset
289 /* The first one should never be X86_64_SSEUP_CLASS. */
kono
parents:
diff changeset
290 FFI_ASSERT (i != 0);
kono
parents:
diff changeset
291 classes[i] = X86_64_SSE_CLASS;
kono
parents:
diff changeset
292 }
kono
parents:
diff changeset
293
kono
parents:
diff changeset
294 /* If X86_64_X87UP_CLASS isn't preceded by X86_64_X87_CLASS,
kono
parents:
diff changeset
295 everything should be passed in memory. */
kono
parents:
diff changeset
296 if (classes[i] == X86_64_X87UP_CLASS
kono
parents:
diff changeset
297 && (classes[i - 1] != X86_64_X87_CLASS))
kono
parents:
diff changeset
298 {
kono
parents:
diff changeset
299 /* The first one should never be X86_64_X87UP_CLASS. */
kono
parents:
diff changeset
300 FFI_ASSERT (i != 0);
kono
parents:
diff changeset
301 return 0;
kono
parents:
diff changeset
302 }
kono
parents:
diff changeset
303 }
kono
parents:
diff changeset
304 return words;
kono
parents:
diff changeset
305 }
kono
parents:
diff changeset
306 case FFI_TYPE_COMPLEX:
kono
parents:
diff changeset
307 {
kono
parents:
diff changeset
308 ffi_type *inner = type->elements[0];
kono
parents:
diff changeset
309 switch (inner->type)
kono
parents:
diff changeset
310 {
kono
parents:
diff changeset
311 case FFI_TYPE_INT:
kono
parents:
diff changeset
312 case FFI_TYPE_UINT8:
kono
parents:
diff changeset
313 case FFI_TYPE_SINT8:
kono
parents:
diff changeset
314 case FFI_TYPE_UINT16:
kono
parents:
diff changeset
315 case FFI_TYPE_SINT16:
kono
parents:
diff changeset
316 case FFI_TYPE_UINT32:
kono
parents:
diff changeset
317 case FFI_TYPE_SINT32:
kono
parents:
diff changeset
318 case FFI_TYPE_UINT64:
kono
parents:
diff changeset
319 case FFI_TYPE_SINT64:
kono
parents:
diff changeset
320 goto do_integer;
kono
parents:
diff changeset
321
kono
parents:
diff changeset
322 case FFI_TYPE_FLOAT:
kono
parents:
diff changeset
323 classes[0] = X86_64_SSE_CLASS;
kono
parents:
diff changeset
324 if (byte_offset % 8)
kono
parents:
diff changeset
325 {
kono
parents:
diff changeset
326 classes[1] = X86_64_SSESF_CLASS;
kono
parents:
diff changeset
327 return 2;
kono
parents:
diff changeset
328 }
kono
parents:
diff changeset
329 return 1;
kono
parents:
diff changeset
330 case FFI_TYPE_DOUBLE:
kono
parents:
diff changeset
331 classes[0] = classes[1] = X86_64_SSEDF_CLASS;
kono
parents:
diff changeset
332 return 2;
kono
parents:
diff changeset
333 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
kono
parents:
diff changeset
334 case FFI_TYPE_LONGDOUBLE:
kono
parents:
diff changeset
335 classes[0] = X86_64_COMPLEX_X87_CLASS;
kono
parents:
diff changeset
336 return 1;
kono
parents:
diff changeset
337 #endif
kono
parents:
diff changeset
338 }
kono
parents:
diff changeset
339 }
kono
parents:
diff changeset
340 }
kono
parents:
diff changeset
341 abort();
kono
parents:
diff changeset
342 }
kono
parents:
diff changeset
343
kono
parents:
diff changeset
344 /* Examine the argument and return set number of register required in each
kono
parents:
diff changeset
345 class. Return zero iff parameter should be passed in memory, otherwise
kono
parents:
diff changeset
346 the number of registers. */
kono
parents:
diff changeset
347
kono
parents:
diff changeset
348 static size_t
kono
parents:
diff changeset
349 examine_argument (ffi_type *type, enum x86_64_reg_class classes[MAX_CLASSES],
kono
parents:
diff changeset
350 _Bool in_return, int *pngpr, int *pnsse)
kono
parents:
diff changeset
351 {
kono
parents:
diff changeset
352 size_t n;
kono
parents:
diff changeset
353 int i, ngpr, nsse;
kono
parents:
diff changeset
354
kono
parents:
diff changeset
355 n = classify_argument (type, classes, 0);
kono
parents:
diff changeset
356 if (n == 0)
kono
parents:
diff changeset
357 return 0;
kono
parents:
diff changeset
358
kono
parents:
diff changeset
359 ngpr = nsse = 0;
kono
parents:
diff changeset
360 for (i = 0; i < n; ++i)
kono
parents:
diff changeset
361 switch (classes[i])
kono
parents:
diff changeset
362 {
kono
parents:
diff changeset
363 case X86_64_INTEGER_CLASS:
kono
parents:
diff changeset
364 case X86_64_INTEGERSI_CLASS:
kono
parents:
diff changeset
365 ngpr++;
kono
parents:
diff changeset
366 break;
kono
parents:
diff changeset
367 case X86_64_SSE_CLASS:
kono
parents:
diff changeset
368 case X86_64_SSESF_CLASS:
kono
parents:
diff changeset
369 case X86_64_SSEDF_CLASS:
kono
parents:
diff changeset
370 nsse++;
kono
parents:
diff changeset
371 break;
kono
parents:
diff changeset
372 case X86_64_NO_CLASS:
kono
parents:
diff changeset
373 case X86_64_SSEUP_CLASS:
kono
parents:
diff changeset
374 break;
kono
parents:
diff changeset
375 case X86_64_X87_CLASS:
kono
parents:
diff changeset
376 case X86_64_X87UP_CLASS:
kono
parents:
diff changeset
377 case X86_64_COMPLEX_X87_CLASS:
kono
parents:
diff changeset
378 return in_return != 0;
kono
parents:
diff changeset
379 default:
kono
parents:
diff changeset
380 abort ();
kono
parents:
diff changeset
381 }
kono
parents:
diff changeset
382
kono
parents:
diff changeset
383 *pngpr = ngpr;
kono
parents:
diff changeset
384 *pnsse = nsse;
kono
parents:
diff changeset
385
kono
parents:
diff changeset
386 return n;
kono
parents:
diff changeset
387 }
kono
parents:
diff changeset
388
kono
parents:
diff changeset
389 /* Perform machine dependent cif processing. */
kono
parents:
diff changeset
390
kono
parents:
diff changeset
391 ffi_status
kono
parents:
diff changeset
392 ffi_prep_cif_machdep (ffi_cif *cif)
kono
parents:
diff changeset
393 {
kono
parents:
diff changeset
394 int gprcount, ssecount, i, avn, ngpr, nsse, flags;
kono
parents:
diff changeset
395 enum x86_64_reg_class classes[MAX_CLASSES];
kono
parents:
diff changeset
396 size_t bytes, n, rtype_size;
kono
parents:
diff changeset
397 ffi_type *rtype;
kono
parents:
diff changeset
398
kono
parents:
diff changeset
399 if (cif->abi != FFI_UNIX64)
kono
parents:
diff changeset
400 return FFI_BAD_ABI;
kono
parents:
diff changeset
401
kono
parents:
diff changeset
402 gprcount = ssecount = 0;
kono
parents:
diff changeset
403
kono
parents:
diff changeset
404 rtype = cif->rtype;
kono
parents:
diff changeset
405 rtype_size = rtype->size;
kono
parents:
diff changeset
406 switch (rtype->type)
kono
parents:
diff changeset
407 {
kono
parents:
diff changeset
408 case FFI_TYPE_VOID:
kono
parents:
diff changeset
409 flags = UNIX64_RET_VOID;
kono
parents:
diff changeset
410 break;
kono
parents:
diff changeset
411 case FFI_TYPE_UINT8:
kono
parents:
diff changeset
412 flags = UNIX64_RET_UINT8;
kono
parents:
diff changeset
413 break;
kono
parents:
diff changeset
414 case FFI_TYPE_SINT8:
kono
parents:
diff changeset
415 flags = UNIX64_RET_SINT8;
kono
parents:
diff changeset
416 break;
kono
parents:
diff changeset
417 case FFI_TYPE_UINT16:
kono
parents:
diff changeset
418 flags = UNIX64_RET_UINT16;
kono
parents:
diff changeset
419 break;
kono
parents:
diff changeset
420 case FFI_TYPE_SINT16:
kono
parents:
diff changeset
421 flags = UNIX64_RET_SINT16;
kono
parents:
diff changeset
422 break;
kono
parents:
diff changeset
423 case FFI_TYPE_UINT32:
kono
parents:
diff changeset
424 flags = UNIX64_RET_UINT32;
kono
parents:
diff changeset
425 break;
kono
parents:
diff changeset
426 case FFI_TYPE_INT:
kono
parents:
diff changeset
427 case FFI_TYPE_SINT32:
kono
parents:
diff changeset
428 flags = UNIX64_RET_SINT32;
kono
parents:
diff changeset
429 break;
kono
parents:
diff changeset
430 case FFI_TYPE_UINT64:
kono
parents:
diff changeset
431 case FFI_TYPE_SINT64:
kono
parents:
diff changeset
432 flags = UNIX64_RET_INT64;
kono
parents:
diff changeset
433 break;
kono
parents:
diff changeset
434 case FFI_TYPE_POINTER:
kono
parents:
diff changeset
435 flags = (sizeof(void *) == 4 ? UNIX64_RET_UINT32 : UNIX64_RET_INT64);
kono
parents:
diff changeset
436 break;
kono
parents:
diff changeset
437 case FFI_TYPE_FLOAT:
kono
parents:
diff changeset
438 flags = UNIX64_RET_XMM32;
kono
parents:
diff changeset
439 break;
kono
parents:
diff changeset
440 case FFI_TYPE_DOUBLE:
kono
parents:
diff changeset
441 flags = UNIX64_RET_XMM64;
kono
parents:
diff changeset
442 break;
kono
parents:
diff changeset
443 case FFI_TYPE_LONGDOUBLE:
kono
parents:
diff changeset
444 flags = UNIX64_RET_X87;
kono
parents:
diff changeset
445 break;
kono
parents:
diff changeset
446 case FFI_TYPE_STRUCT:
kono
parents:
diff changeset
447 n = examine_argument (cif->rtype, classes, 1, &ngpr, &nsse);
kono
parents:
diff changeset
448 if (n == 0)
kono
parents:
diff changeset
449 {
kono
parents:
diff changeset
450 /* The return value is passed in memory. A pointer to that
kono
parents:
diff changeset
451 memory is the first argument. Allocate a register for it. */
kono
parents:
diff changeset
452 gprcount++;
kono
parents:
diff changeset
453 /* We don't have to do anything in asm for the return. */
kono
parents:
diff changeset
454 flags = UNIX64_RET_VOID | UNIX64_FLAG_RET_IN_MEM;
kono
parents:
diff changeset
455 }
kono
parents:
diff changeset
456 else
kono
parents:
diff changeset
457 {
kono
parents:
diff changeset
458 _Bool sse0 = SSE_CLASS_P (classes[0]);
kono
parents:
diff changeset
459
kono
parents:
diff changeset
460 if (rtype_size == 4 && sse0)
kono
parents:
diff changeset
461 flags = UNIX64_RET_XMM32;
kono
parents:
diff changeset
462 else if (rtype_size == 8)
kono
parents:
diff changeset
463 flags = sse0 ? UNIX64_RET_XMM64 : UNIX64_RET_INT64;
kono
parents:
diff changeset
464 else
kono
parents:
diff changeset
465 {
kono
parents:
diff changeset
466 _Bool sse1 = n == 2 && SSE_CLASS_P (classes[1]);
kono
parents:
diff changeset
467 if (sse0 && sse1)
kono
parents:
diff changeset
468 flags = UNIX64_RET_ST_XMM0_XMM1;
kono
parents:
diff changeset
469 else if (sse0)
kono
parents:
diff changeset
470 flags = UNIX64_RET_ST_XMM0_RAX;
kono
parents:
diff changeset
471 else if (sse1)
kono
parents:
diff changeset
472 flags = UNIX64_RET_ST_RAX_XMM0;
kono
parents:
diff changeset
473 else
kono
parents:
diff changeset
474 flags = UNIX64_RET_ST_RAX_RDX;
kono
parents:
diff changeset
475 flags |= rtype_size << UNIX64_SIZE_SHIFT;
kono
parents:
diff changeset
476 }
kono
parents:
diff changeset
477 }
kono
parents:
diff changeset
478 break;
kono
parents:
diff changeset
479 case FFI_TYPE_COMPLEX:
kono
parents:
diff changeset
480 switch (rtype->elements[0]->type)
kono
parents:
diff changeset
481 {
kono
parents:
diff changeset
482 case FFI_TYPE_UINT8:
kono
parents:
diff changeset
483 case FFI_TYPE_SINT8:
kono
parents:
diff changeset
484 case FFI_TYPE_UINT16:
kono
parents:
diff changeset
485 case FFI_TYPE_SINT16:
kono
parents:
diff changeset
486 case FFI_TYPE_INT:
kono
parents:
diff changeset
487 case FFI_TYPE_UINT32:
kono
parents:
diff changeset
488 case FFI_TYPE_SINT32:
kono
parents:
diff changeset
489 case FFI_TYPE_UINT64:
kono
parents:
diff changeset
490 case FFI_TYPE_SINT64:
kono
parents:
diff changeset
491 flags = UNIX64_RET_ST_RAX_RDX | (rtype_size << UNIX64_SIZE_SHIFT);
kono
parents:
diff changeset
492 break;
kono
parents:
diff changeset
493 case FFI_TYPE_FLOAT:
kono
parents:
diff changeset
494 flags = UNIX64_RET_XMM64;
kono
parents:
diff changeset
495 break;
kono
parents:
diff changeset
496 case FFI_TYPE_DOUBLE:
kono
parents:
diff changeset
497 flags = UNIX64_RET_ST_XMM0_XMM1 | (16 << UNIX64_SIZE_SHIFT);
kono
parents:
diff changeset
498 break;
kono
parents:
diff changeset
499 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
kono
parents:
diff changeset
500 case FFI_TYPE_LONGDOUBLE:
kono
parents:
diff changeset
501 flags = UNIX64_RET_X87_2;
kono
parents:
diff changeset
502 break;
kono
parents:
diff changeset
503 #endif
kono
parents:
diff changeset
504 default:
kono
parents:
diff changeset
505 return FFI_BAD_TYPEDEF;
kono
parents:
diff changeset
506 }
kono
parents:
diff changeset
507 break;
kono
parents:
diff changeset
508 default:
kono
parents:
diff changeset
509 return FFI_BAD_TYPEDEF;
kono
parents:
diff changeset
510 }
kono
parents:
diff changeset
511
kono
parents:
diff changeset
512 /* Go over all arguments and determine the way they should be passed.
kono
parents:
diff changeset
513 If it's in a register and there is space for it, let that be so. If
kono
parents:
diff changeset
514 not, add it's size to the stack byte count. */
kono
parents:
diff changeset
515 for (bytes = 0, i = 0, avn = cif->nargs; i < avn; i++)
kono
parents:
diff changeset
516 {
kono
parents:
diff changeset
517 if (examine_argument (cif->arg_types[i], classes, 0, &ngpr, &nsse) == 0
kono
parents:
diff changeset
518 || gprcount + ngpr > MAX_GPR_REGS
kono
parents:
diff changeset
519 || ssecount + nsse > MAX_SSE_REGS)
kono
parents:
diff changeset
520 {
kono
parents:
diff changeset
521 long align = cif->arg_types[i]->alignment;
kono
parents:
diff changeset
522
kono
parents:
diff changeset
523 if (align < 8)
kono
parents:
diff changeset
524 align = 8;
kono
parents:
diff changeset
525
kono
parents:
diff changeset
526 bytes = ALIGN (bytes, align);
kono
parents:
diff changeset
527 bytes += cif->arg_types[i]->size;
kono
parents:
diff changeset
528 }
kono
parents:
diff changeset
529 else
kono
parents:
diff changeset
530 {
kono
parents:
diff changeset
531 gprcount += ngpr;
kono
parents:
diff changeset
532 ssecount += nsse;
kono
parents:
diff changeset
533 }
kono
parents:
diff changeset
534 }
kono
parents:
diff changeset
535 if (ssecount)
kono
parents:
diff changeset
536 flags |= UNIX64_FLAG_XMM_ARGS;
kono
parents:
diff changeset
537
kono
parents:
diff changeset
538 cif->flags = flags;
kono
parents:
diff changeset
539 cif->bytes = ALIGN (bytes, 8);
kono
parents:
diff changeset
540
kono
parents:
diff changeset
541 return FFI_OK;
kono
parents:
diff changeset
542 }
kono
parents:
diff changeset
543
kono
parents:
diff changeset
544 static void
kono
parents:
diff changeset
545 ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue,
kono
parents:
diff changeset
546 void **avalue, void *closure)
kono
parents:
diff changeset
547 {
kono
parents:
diff changeset
548 enum x86_64_reg_class classes[MAX_CLASSES];
kono
parents:
diff changeset
549 char *stack, *argp;
kono
parents:
diff changeset
550 ffi_type **arg_types;
kono
parents:
diff changeset
551 int gprcount, ssecount, ngpr, nsse, i, avn, flags;
kono
parents:
diff changeset
552 struct register_args *reg_args;
kono
parents:
diff changeset
553
kono
parents:
diff changeset
554 /* Can't call 32-bit mode from 64-bit mode. */
kono
parents:
diff changeset
555 FFI_ASSERT (cif->abi == FFI_UNIX64);
kono
parents:
diff changeset
556
kono
parents:
diff changeset
557 /* If the return value is a struct and we don't have a return value
kono
parents:
diff changeset
558 address then we need to make one. Otherwise we can ignore it. */
kono
parents:
diff changeset
559 flags = cif->flags;
kono
parents:
diff changeset
560 if (rvalue == NULL)
kono
parents:
diff changeset
561 {
kono
parents:
diff changeset
562 if (flags & UNIX64_FLAG_RET_IN_MEM)
kono
parents:
diff changeset
563 rvalue = alloca (cif->rtype->size);
kono
parents:
diff changeset
564 else
kono
parents:
diff changeset
565 flags = UNIX64_RET_VOID;
kono
parents:
diff changeset
566 }
kono
parents:
diff changeset
567
kono
parents:
diff changeset
568 /* Allocate the space for the arguments, plus 4 words of temp space. */
kono
parents:
diff changeset
569 stack = alloca (sizeof (struct register_args) + cif->bytes + 4*8);
kono
parents:
diff changeset
570 reg_args = (struct register_args *) stack;
kono
parents:
diff changeset
571 argp = stack + sizeof (struct register_args);
kono
parents:
diff changeset
572
kono
parents:
diff changeset
573 reg_args->r10 = (uintptr_t) closure;
kono
parents:
diff changeset
574
kono
parents:
diff changeset
575 gprcount = ssecount = 0;
kono
parents:
diff changeset
576
kono
parents:
diff changeset
577 /* If the return value is passed in memory, add the pointer as the
kono
parents:
diff changeset
578 first integer argument. */
kono
parents:
diff changeset
579 if (flags & UNIX64_FLAG_RET_IN_MEM)
kono
parents:
diff changeset
580 reg_args->gpr[gprcount++] = (unsigned long) rvalue;
kono
parents:
diff changeset
581
kono
parents:
diff changeset
582 avn = cif->nargs;
kono
parents:
diff changeset
583 arg_types = cif->arg_types;
kono
parents:
diff changeset
584
kono
parents:
diff changeset
585 for (i = 0; i < avn; ++i)
kono
parents:
diff changeset
586 {
kono
parents:
diff changeset
587 size_t n, size = arg_types[i]->size;
kono
parents:
diff changeset
588
kono
parents:
diff changeset
589 n = examine_argument (arg_types[i], classes, 0, &ngpr, &nsse);
kono
parents:
diff changeset
590 if (n == 0
kono
parents:
diff changeset
591 || gprcount + ngpr > MAX_GPR_REGS
kono
parents:
diff changeset
592 || ssecount + nsse > MAX_SSE_REGS)
kono
parents:
diff changeset
593 {
kono
parents:
diff changeset
594 long align = arg_types[i]->alignment;
kono
parents:
diff changeset
595
kono
parents:
diff changeset
596 /* Stack arguments are *always* at least 8 byte aligned. */
kono
parents:
diff changeset
597 if (align < 8)
kono
parents:
diff changeset
598 align = 8;
kono
parents:
diff changeset
599
kono
parents:
diff changeset
600 /* Pass this argument in memory. */
kono
parents:
diff changeset
601 argp = (void *) ALIGN (argp, align);
kono
parents:
diff changeset
602 memcpy (argp, avalue[i], size);
kono
parents:
diff changeset
603 argp += size;
kono
parents:
diff changeset
604 }
kono
parents:
diff changeset
605 else
kono
parents:
diff changeset
606 {
kono
parents:
diff changeset
607 /* The argument is passed entirely in registers. */
kono
parents:
diff changeset
608 char *a = (char *) avalue[i];
kono
parents:
diff changeset
609 int j;
kono
parents:
diff changeset
610
kono
parents:
diff changeset
611 for (j = 0; j < n; j++, a += 8, size -= 8)
kono
parents:
diff changeset
612 {
kono
parents:
diff changeset
613 switch (classes[j])
kono
parents:
diff changeset
614 {
kono
parents:
diff changeset
615 case X86_64_NO_CLASS:
kono
parents:
diff changeset
616 case X86_64_SSEUP_CLASS:
kono
parents:
diff changeset
617 break;
kono
parents:
diff changeset
618 case X86_64_INTEGER_CLASS:
kono
parents:
diff changeset
619 case X86_64_INTEGERSI_CLASS:
kono
parents:
diff changeset
620 /* Sign-extend integer arguments passed in general
kono
parents:
diff changeset
621 purpose registers, to cope with the fact that
kono
parents:
diff changeset
622 LLVM incorrectly assumes that this will be done
kono
parents:
diff changeset
623 (the x86-64 PS ABI does not specify this). */
kono
parents:
diff changeset
624 switch (arg_types[i]->type)
kono
parents:
diff changeset
625 {
kono
parents:
diff changeset
626 case FFI_TYPE_SINT8:
kono
parents:
diff changeset
627 reg_args->gpr[gprcount] = (SINT64) *((SINT8 *) a);
kono
parents:
diff changeset
628 break;
kono
parents:
diff changeset
629 case FFI_TYPE_SINT16:
kono
parents:
diff changeset
630 reg_args->gpr[gprcount] = (SINT64) *((SINT16 *) a);
kono
parents:
diff changeset
631 break;
kono
parents:
diff changeset
632 case FFI_TYPE_SINT32:
kono
parents:
diff changeset
633 reg_args->gpr[gprcount] = (SINT64) *((SINT32 *) a);
kono
parents:
diff changeset
634 break;
kono
parents:
diff changeset
635 default:
kono
parents:
diff changeset
636 reg_args->gpr[gprcount] = 0;
kono
parents:
diff changeset
637 memcpy (&reg_args->gpr[gprcount], a, size);
kono
parents:
diff changeset
638 }
kono
parents:
diff changeset
639 gprcount++;
kono
parents:
diff changeset
640 break;
kono
parents:
diff changeset
641 case X86_64_SSE_CLASS:
kono
parents:
diff changeset
642 case X86_64_SSEDF_CLASS:
kono
parents:
diff changeset
643 reg_args->sse[ssecount++].i64 = *(UINT64 *) a;
kono
parents:
diff changeset
644 break;
kono
parents:
diff changeset
645 case X86_64_SSESF_CLASS:
kono
parents:
diff changeset
646 reg_args->sse[ssecount++].i32 = *(UINT32 *) a;
kono
parents:
diff changeset
647 break;
kono
parents:
diff changeset
648 default:
kono
parents:
diff changeset
649 abort();
kono
parents:
diff changeset
650 }
kono
parents:
diff changeset
651 }
kono
parents:
diff changeset
652 }
kono
parents:
diff changeset
653 }
kono
parents:
diff changeset
654 reg_args->rax = ssecount;
kono
parents:
diff changeset
655
kono
parents:
diff changeset
656 ffi_call_unix64 (stack, cif->bytes + sizeof (struct register_args),
kono
parents:
diff changeset
657 flags, rvalue, fn);
kono
parents:
diff changeset
658 }
kono
parents:
diff changeset
659
kono
parents:
diff changeset
660 void
kono
parents:
diff changeset
661 ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
kono
parents:
diff changeset
662 {
kono
parents:
diff changeset
663 ffi_call_int (cif, fn, rvalue, avalue, NULL);
kono
parents:
diff changeset
664 }
kono
parents:
diff changeset
665
kono
parents:
diff changeset
666 void
kono
parents:
diff changeset
667 ffi_call_go (ffi_cif *cif, void (*fn)(void), void *rvalue,
kono
parents:
diff changeset
668 void **avalue, void *closure)
kono
parents:
diff changeset
669 {
kono
parents:
diff changeset
670 ffi_call_int (cif, fn, rvalue, avalue, closure);
kono
parents:
diff changeset
671 }
kono
parents:
diff changeset
672
kono
parents:
diff changeset
673 extern void ffi_closure_unix64(void) FFI_HIDDEN;
kono
parents:
diff changeset
674 extern void ffi_closure_unix64_sse(void) FFI_HIDDEN;
kono
parents:
diff changeset
675
kono
parents:
diff changeset
676 ffi_status
kono
parents:
diff changeset
677 ffi_prep_closure_loc (ffi_closure* closure,
kono
parents:
diff changeset
678 ffi_cif* cif,
kono
parents:
diff changeset
679 void (*fun)(ffi_cif*, void*, void**, void*),
kono
parents:
diff changeset
680 void *user_data,
kono
parents:
diff changeset
681 void *codeloc)
kono
parents:
diff changeset
682 {
kono
parents:
diff changeset
683 static const unsigned char trampoline[16] = {
kono
parents:
diff changeset
684 /* leaq -0x7(%rip),%r10 # 0x0 */
kono
parents:
diff changeset
685 0x4c, 0x8d, 0x15, 0xf9, 0xff, 0xff, 0xff,
kono
parents:
diff changeset
686 /* jmpq *0x3(%rip) # 0x10 */
kono
parents:
diff changeset
687 0xff, 0x25, 0x03, 0x00, 0x00, 0x00,
kono
parents:
diff changeset
688 /* nopl (%rax) */
kono
parents:
diff changeset
689 0x0f, 0x1f, 0x00
kono
parents:
diff changeset
690 };
kono
parents:
diff changeset
691 void (*dest)(void);
kono
parents:
diff changeset
692 char *tramp = closure->tramp;
kono
parents:
diff changeset
693
kono
parents:
diff changeset
694 if (cif->abi != FFI_UNIX64)
kono
parents:
diff changeset
695 return FFI_BAD_ABI;
kono
parents:
diff changeset
696
kono
parents:
diff changeset
697 if (cif->flags & UNIX64_FLAG_XMM_ARGS)
kono
parents:
diff changeset
698 dest = ffi_closure_unix64_sse;
kono
parents:
diff changeset
699 else
kono
parents:
diff changeset
700 dest = ffi_closure_unix64;
kono
parents:
diff changeset
701
kono
parents:
diff changeset
702 memcpy (tramp, trampoline, sizeof(trampoline));
kono
parents:
diff changeset
703 *(UINT64 *)(tramp + 16) = (uintptr_t)dest;
kono
parents:
diff changeset
704
kono
parents:
diff changeset
705 closure->cif = cif;
kono
parents:
diff changeset
706 closure->fun = fun;
kono
parents:
diff changeset
707 closure->user_data = user_data;
kono
parents:
diff changeset
708
kono
parents:
diff changeset
709 return FFI_OK;
kono
parents:
diff changeset
710 }
kono
parents:
diff changeset
711
kono
parents:
diff changeset
712 int FFI_HIDDEN
kono
parents:
diff changeset
713 ffi_closure_unix64_inner(ffi_cif *cif,
kono
parents:
diff changeset
714 void (*fun)(ffi_cif*, void*, void**, void*),
kono
parents:
diff changeset
715 void *user_data,
kono
parents:
diff changeset
716 void *rvalue,
kono
parents:
diff changeset
717 struct register_args *reg_args,
kono
parents:
diff changeset
718 char *argp)
kono
parents:
diff changeset
719 {
kono
parents:
diff changeset
720 void **avalue;
kono
parents:
diff changeset
721 ffi_type **arg_types;
kono
parents:
diff changeset
722 long i, avn;
kono
parents:
diff changeset
723 int gprcount, ssecount, ngpr, nsse;
kono
parents:
diff changeset
724 int flags;
kono
parents:
diff changeset
725
kono
parents:
diff changeset
726 avn = cif->nargs;
kono
parents:
diff changeset
727 flags = cif->flags;
kono
parents:
diff changeset
728 avalue = alloca(avn * sizeof(void *));
kono
parents:
diff changeset
729 gprcount = ssecount = 0;
kono
parents:
diff changeset
730
kono
parents:
diff changeset
731 if (flags & UNIX64_FLAG_RET_IN_MEM)
kono
parents:
diff changeset
732 {
kono
parents:
diff changeset
733 /* On return, %rax will contain the address that was passed
kono
parents:
diff changeset
734 by the caller in %rdi. */
kono
parents:
diff changeset
735 void *r = (void *)(uintptr_t)reg_args->gpr[gprcount++];
kono
parents:
diff changeset
736 *(void **)rvalue = r;
kono
parents:
diff changeset
737 rvalue = r;
kono
parents:
diff changeset
738 flags = (sizeof(void *) == 4 ? UNIX64_RET_UINT32 : UNIX64_RET_INT64);
kono
parents:
diff changeset
739 }
kono
parents:
diff changeset
740
kono
parents:
diff changeset
741 arg_types = cif->arg_types;
kono
parents:
diff changeset
742 for (i = 0; i < avn; ++i)
kono
parents:
diff changeset
743 {
kono
parents:
diff changeset
744 enum x86_64_reg_class classes[MAX_CLASSES];
kono
parents:
diff changeset
745 size_t n;
kono
parents:
diff changeset
746
kono
parents:
diff changeset
747 n = examine_argument (arg_types[i], classes, 0, &ngpr, &nsse);
kono
parents:
diff changeset
748 if (n == 0
kono
parents:
diff changeset
749 || gprcount + ngpr > MAX_GPR_REGS
kono
parents:
diff changeset
750 || ssecount + nsse > MAX_SSE_REGS)
kono
parents:
diff changeset
751 {
kono
parents:
diff changeset
752 long align = arg_types[i]->alignment;
kono
parents:
diff changeset
753
kono
parents:
diff changeset
754 /* Stack arguments are *always* at least 8 byte aligned. */
kono
parents:
diff changeset
755 if (align < 8)
kono
parents:
diff changeset
756 align = 8;
kono
parents:
diff changeset
757
kono
parents:
diff changeset
758 /* Pass this argument in memory. */
kono
parents:
diff changeset
759 argp = (void *) ALIGN (argp, align);
kono
parents:
diff changeset
760 avalue[i] = argp;
kono
parents:
diff changeset
761 argp += arg_types[i]->size;
kono
parents:
diff changeset
762 }
kono
parents:
diff changeset
763 /* If the argument is in a single register, or two consecutive
kono
parents:
diff changeset
764 integer registers, then we can use that address directly. */
kono
parents:
diff changeset
765 else if (n == 1
kono
parents:
diff changeset
766 || (n == 2 && !(SSE_CLASS_P (classes[0])
kono
parents:
diff changeset
767 || SSE_CLASS_P (classes[1]))))
kono
parents:
diff changeset
768 {
kono
parents:
diff changeset
769 /* The argument is in a single register. */
kono
parents:
diff changeset
770 if (SSE_CLASS_P (classes[0]))
kono
parents:
diff changeset
771 {
kono
parents:
diff changeset
772 avalue[i] = &reg_args->sse[ssecount];
kono
parents:
diff changeset
773 ssecount += n;
kono
parents:
diff changeset
774 }
kono
parents:
diff changeset
775 else
kono
parents:
diff changeset
776 {
kono
parents:
diff changeset
777 avalue[i] = &reg_args->gpr[gprcount];
kono
parents:
diff changeset
778 gprcount += n;
kono
parents:
diff changeset
779 }
kono
parents:
diff changeset
780 }
kono
parents:
diff changeset
781 /* Otherwise, allocate space to make them consecutive. */
kono
parents:
diff changeset
782 else
kono
parents:
diff changeset
783 {
kono
parents:
diff changeset
784 char *a = alloca (16);
kono
parents:
diff changeset
785 int j;
kono
parents:
diff changeset
786
kono
parents:
diff changeset
787 avalue[i] = a;
kono
parents:
diff changeset
788 for (j = 0; j < n; j++, a += 8)
kono
parents:
diff changeset
789 {
kono
parents:
diff changeset
790 if (SSE_CLASS_P (classes[j]))
kono
parents:
diff changeset
791 memcpy (a, &reg_args->sse[ssecount++], 8);
kono
parents:
diff changeset
792 else
kono
parents:
diff changeset
793 memcpy (a, &reg_args->gpr[gprcount++], 8);
kono
parents:
diff changeset
794 }
kono
parents:
diff changeset
795 }
kono
parents:
diff changeset
796 }
kono
parents:
diff changeset
797
kono
parents:
diff changeset
798 /* Invoke the closure. */
kono
parents:
diff changeset
799 fun (cif, rvalue, avalue, user_data);
kono
parents:
diff changeset
800
kono
parents:
diff changeset
801 /* Tell assembly how to perform return type promotions. */
kono
parents:
diff changeset
802 return flags;
kono
parents:
diff changeset
803 }
kono
parents:
diff changeset
804
kono
parents:
diff changeset
805 extern void ffi_go_closure_unix64(void) FFI_HIDDEN;
kono
parents:
diff changeset
806 extern void ffi_go_closure_unix64_sse(void) FFI_HIDDEN;
kono
parents:
diff changeset
807
kono
parents:
diff changeset
808 ffi_status
kono
parents:
diff changeset
809 ffi_prep_go_closure (ffi_go_closure* closure, ffi_cif* cif,
kono
parents:
diff changeset
810 void (*fun)(ffi_cif*, void*, void**, void*))
kono
parents:
diff changeset
811 {
kono
parents:
diff changeset
812 if (cif->abi != FFI_UNIX64)
kono
parents:
diff changeset
813 return FFI_BAD_ABI;
kono
parents:
diff changeset
814
kono
parents:
diff changeset
815 closure->tramp = (cif->flags & UNIX64_FLAG_XMM_ARGS
kono
parents:
diff changeset
816 ? ffi_go_closure_unix64_sse
kono
parents:
diff changeset
817 : ffi_go_closure_unix64);
kono
parents:
diff changeset
818 closure->cif = cif;
kono
parents:
diff changeset
819 closure->fun = fun;
kono
parents:
diff changeset
820
kono
parents:
diff changeset
821 return FFI_OK;
kono
parents:
diff changeset
822 }
kono
parents:
diff changeset
823
kono
parents:
diff changeset
824 #endif /* __x86_64__ */