1 | /* -----------------------------------------------------------------------
|
---|
2 | ffi.c - Copyright (c) 1998 Cygnus Solutions
|
---|
3 | Copyright (c) 2000 Hewlett Packard Company
|
---|
4 |
|
---|
5 | IA64 Foreign Function Interface
|
---|
6 |
|
---|
7 | Permission is hereby granted, free of charge, to any person obtaining
|
---|
8 | a copy of this software and associated documentation files (the
|
---|
9 | ``Software''), to deal in the Software without restriction, including
|
---|
10 | without limitation the rights to use, copy, modify, merge, publish,
|
---|
11 | distribute, sublicense, and/or sell copies of the Software, and to
|
---|
12 | permit persons to whom the Software is furnished to do so, subject to
|
---|
13 | the following conditions:
|
---|
14 |
|
---|
15 | The above copyright notice and this permission notice shall be included
|
---|
16 | in all copies or substantial portions of the Software.
|
---|
17 |
|
---|
18 | THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
---|
19 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
---|
20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
---|
21 | IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
---|
22 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
---|
23 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
---|
24 | OTHER DEALINGS IN THE SOFTWARE.
|
---|
25 | ----------------------------------------------------------------------- */
|
---|
26 |
|
---|
27 | #include <ffi.h>
|
---|
28 | #include <ffi_common.h>
|
---|
29 |
|
---|
30 | #include <stdlib.h>
|
---|
31 |
|
---|
32 | #include "ia64_flags.h"
|
---|
33 |
|
---|
34 | /* Memory image of fp register contents. Should eventually be an fp */
|
---|
35 | /* type long enough to hold an entire register. For now we use double. */
|
---|
36 | typedef double float80;
|
---|
37 |
|
---|
38 | /* The stack layout at call to ffi_prep_args. Other_args will remain */
|
---|
39 | /* on the stack for the actual call. Everything else we be transferred */
|
---|
40 | /* to registers and popped by the assembly code. */
|
---|
41 |
|
---|
42 | struct ia64_args {
|
---|
43 | long scratch[2]; /* Two scratch words at top of stack. */
|
---|
44 | /* Allows sp to be passed as arg pointer. */
|
---|
45 | void * r8_contents; /* Value to be passed in r8 */
|
---|
46 | long spare; /* Not used. */
|
---|
47 | float80 fp_regs[8]; /* Contents of 8 floating point argument */
|
---|
48 | /* registers. */
|
---|
49 | long out_regs[8]; /* Contents of the 8 out registers used */
|
---|
50 | /* for integer parameters. */
|
---|
51 | long other_args[0]; /* Arguments passed on stack, variable size */
|
---|
52 | /* Treated as continuation of out_regs. */
|
---|
53 | };
|
---|
54 |
|
---|
55 | static size_t float_type_size(unsigned short tp)
|
---|
56 | {
|
---|
57 | switch(tp) {
|
---|
58 | case FFI_TYPE_FLOAT:
|
---|
59 | return sizeof(float);
|
---|
60 | case FFI_TYPE_DOUBLE:
|
---|
61 | return sizeof(double);
|
---|
62 | #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
|
---|
63 | case FFI_TYPE_LONGDOUBLE:
|
---|
64 | return sizeof(long double);
|
---|
65 | #endif
|
---|
66 | default:
|
---|
67 | FFI_ASSERT(0);
|
---|
68 | }
|
---|
69 | }
|
---|
70 |
|
---|
71 | /*
|
---|
72 | * Is type a struct containing at most n floats, doubles, or extended
|
---|
73 | * doubles, all of the same fp type?
|
---|
74 | * If so, set *element_type to the fp type.
|
---|
75 | */
|
---|
76 | static bool is_homogeneous_fp_aggregate(ffi_type * type, int n,
|
---|
77 | unsigned short * element_type)
|
---|
78 | {
|
---|
79 | ffi_type **ptr;
|
---|
80 | unsigned short element, struct_element;
|
---|
81 |
|
---|
82 | int type_set = 0;
|
---|
83 |
|
---|
84 | FFI_ASSERT(type != NULL);
|
---|
85 |
|
---|
86 | FFI_ASSERT(type->elements != NULL);
|
---|
87 |
|
---|
88 | ptr = &(type->elements[0]);
|
---|
89 |
|
---|
90 | while ((*ptr) != NULL)
|
---|
91 | {
|
---|
92 | switch((*ptr) -> type) {
|
---|
93 | case FFI_TYPE_FLOAT:
|
---|
94 | if (type_set && element != FFI_TYPE_FLOAT) return 0;
|
---|
95 | if (--n < 0) return FALSE;
|
---|
96 | type_set = 1;
|
---|
97 | element = FFI_TYPE_FLOAT;
|
---|
98 | break;
|
---|
99 | case FFI_TYPE_DOUBLE:
|
---|
100 | if (type_set && element != FFI_TYPE_DOUBLE) return 0;
|
---|
101 | if (--n < 0) return FALSE;
|
---|
102 | type_set = 1;
|
---|
103 | element = FFI_TYPE_DOUBLE;
|
---|
104 | break;
|
---|
105 | case FFI_TYPE_STRUCT:
|
---|
106 | if (!is_homogeneous_fp_aggregate(type, n, &struct_element))
|
---|
107 | return FALSE;
|
---|
108 | if (type_set && struct_element != element) return FALSE;
|
---|
109 | n -= (type -> size)/float_type_size(element);
|
---|
110 | element = struct_element;
|
---|
111 | if (n < 0) return FALSE;
|
---|
112 | break;
|
---|
113 | /* case FFI_TYPE_LONGDOUBLE:
|
---|
114 | Not yet implemented. */
|
---|
115 | default:
|
---|
116 | return FALSE;
|
---|
117 | }
|
---|
118 | ptr++;
|
---|
119 | }
|
---|
120 | *element_type = element;
|
---|
121 | return TRUE;
|
---|
122 |
|
---|
123 | }
|
---|
124 |
|
---|
125 | /* ffi_prep_args is called by the assembly routine once stack space
|
---|
126 | has been allocated for the function's arguments. It fills in
|
---|
127 | the arguments in the structure referenced by stack. Returns nonzero
|
---|
128 | if fp registers are used for arguments. */
|
---|
129 |
|
---|
130 | static bool
|
---|
131 | ffi_prep_args(struct ia64_args *stack, extended_cif *ecif, int bytes)
|
---|
132 | {
|
---|
133 | register long i, avn;
|
---|
134 | register void **p_argv;
|
---|
135 | register long *argp = stack -> out_regs;
|
---|
136 | register float80 *fp_argp = stack -> fp_regs;
|
---|
137 | register ffi_type **p_arg;
|
---|
138 |
|
---|
139 | /* For big return structs, r8 needs to contain the target address. */
|
---|
140 | /* Since r8 is otherwise dead, we set it unconditionally. */
|
---|
141 | stack -> r8_contents = ecif -> rvalue;
|
---|
142 | i = 0;
|
---|
143 | avn = ecif->cif->nargs;
|
---|
144 | p_arg = ecif->cif->arg_types;
|
---|
145 | p_argv = ecif->avalue;
|
---|
146 | while (i < avn)
|
---|
147 | {
|
---|
148 | size_t z; /* z is in units of arg slots or words, not bytes. */
|
---|
149 |
|
---|
150 | switch ((*p_arg)->type)
|
---|
151 | {
|
---|
152 | case FFI_TYPE_SINT8:
|
---|
153 | z = 1;
|
---|
154 | *(SINT64 *) argp = *(SINT8 *)(* p_argv);
|
---|
155 | break;
|
---|
156 |
|
---|
157 | case FFI_TYPE_UINT8:
|
---|
158 | z = 1;
|
---|
159 | *(UINT64 *) argp = *(UINT8 *)(* p_argv);
|
---|
160 | break;
|
---|
161 |
|
---|
162 | case FFI_TYPE_SINT16:
|
---|
163 | z = 1;
|
---|
164 | *(SINT64 *) argp = *(SINT16 *)(* p_argv);
|
---|
165 | break;
|
---|
166 |
|
---|
167 | case FFI_TYPE_UINT16:
|
---|
168 | z = 1;
|
---|
169 | *(UINT64 *) argp = *(UINT16 *)(* p_argv);
|
---|
170 | break;
|
---|
171 |
|
---|
172 | case FFI_TYPE_SINT32:
|
---|
173 | z = 1;
|
---|
174 | *(SINT64 *) argp = *(SINT32 *)(* p_argv);
|
---|
175 | break;
|
---|
176 |
|
---|
177 | case FFI_TYPE_UINT32:
|
---|
178 | z = 1;
|
---|
179 | *(UINT64 *) argp = *(UINT32 *)(* p_argv);
|
---|
180 | break;
|
---|
181 |
|
---|
182 | case FFI_TYPE_SINT64:
|
---|
183 | case FFI_TYPE_UINT64:
|
---|
184 | case FFI_TYPE_POINTER:
|
---|
185 | z = 1;
|
---|
186 | *(UINT64 *) argp = *(UINT64 *)(* p_argv);
|
---|
187 | break;
|
---|
188 |
|
---|
189 | case FFI_TYPE_FLOAT:
|
---|
190 | z = 1;
|
---|
191 | if (fp_argp - stack->fp_regs < 8)
|
---|
192 | {
|
---|
193 | /* Note the conversion -- all the fp regs are loaded as
|
---|
194 | doubles. */
|
---|
195 | *fp_argp++ = *(float *)(* p_argv);
|
---|
196 | }
|
---|
197 | /* Also put it into the integer registers or memory: */
|
---|
198 | *(UINT64 *) argp = *(UINT32 *)(* p_argv);
|
---|
199 | break;
|
---|
200 |
|
---|
201 | case FFI_TYPE_DOUBLE:
|
---|
202 | z = 1;
|
---|
203 | if (fp_argp - stack->fp_regs < 8)
|
---|
204 | *fp_argp++ = *(double *)(* p_argv);
|
---|
205 | /* Also put it into the integer registers or memory: */
|
---|
206 | *(double *) argp = *(double *)(* p_argv);
|
---|
207 | break;
|
---|
208 |
|
---|
209 | case FFI_TYPE_STRUCT:
|
---|
210 | {
|
---|
211 | size_t sz = (*p_arg)->size;
|
---|
212 | unsigned short element_type;
|
---|
213 | z = ((*p_arg)->size + SIZEOF_ARG - 1)/SIZEOF_ARG;
|
---|
214 | if (is_homogeneous_fp_aggregate(*p_arg, 8, &element_type)) {
|
---|
215 | int i;
|
---|
216 | int nelements = sz/float_type_size(element_type);
|
---|
217 | for (i = 0; i < nelements; ++i) {
|
---|
218 | switch (element_type) {
|
---|
219 | case FFI_TYPE_FLOAT:
|
---|
220 | if (fp_argp - stack->fp_regs < 8)
|
---|
221 | *fp_argp++ = ((float *)(* p_argv))[i];
|
---|
222 | break;
|
---|
223 | case FFI_TYPE_DOUBLE:
|
---|
224 | if (fp_argp - stack->fp_regs < 8)
|
---|
225 | *fp_argp++ = ((double *)(* p_argv))[i];
|
---|
226 | break;
|
---|
227 | default:
|
---|
228 | /* Extended precision not yet implemented. */
|
---|
229 | abort();
|
---|
230 | }
|
---|
231 | }
|
---|
232 | }
|
---|
233 | /* And pass it in integer registers as a struct, with */
|
---|
234 | /* its actual field sizes packed into registers. */
|
---|
235 | memcpy(argp, *p_argv, (*p_arg)->size);
|
---|
236 | }
|
---|
237 | break;
|
---|
238 |
|
---|
239 | default:
|
---|
240 | FFI_ASSERT(0);
|
---|
241 | }
|
---|
242 |
|
---|
243 | argp += z;
|
---|
244 | i++, p_arg++, p_argv++;
|
---|
245 | }
|
---|
246 | return (fp_argp != stack -> fp_regs);
|
---|
247 | }
|
---|
248 |
|
---|
249 | /* Perform machine dependent cif processing */
|
---|
250 | ffi_status
|
---|
251 | ffi_prep_cif_machdep(ffi_cif *cif)
|
---|
252 | {
|
---|
253 | long i, avn;
|
---|
254 | bool is_simple = TRUE;
|
---|
255 | long simple_flag = FFI_SIMPLE_V;
|
---|
256 | /* Adjust cif->bytes to include space for the 2 scratch words,
|
---|
257 | r8 register contents, spare word,
|
---|
258 | the 8 fp register contents, and all 8 integer register contents.
|
---|
259 | This will be removed before the call, though 2 scratch words must
|
---|
260 | remain. */
|
---|
261 |
|
---|
262 | cif->bytes += 4*sizeof(long) + 8 *sizeof(float80);
|
---|
263 | if (cif->bytes < sizeof(struct ia64_args))
|
---|
264 | cif->bytes = sizeof(struct ia64_args);
|
---|
265 |
|
---|
266 | /* The stack must be double word aligned, so round bytes up
|
---|
267 | appropriately. */
|
---|
268 |
|
---|
269 | cif->bytes = ALIGN(cif->bytes, 2*sizeof(void*));
|
---|
270 |
|
---|
271 | avn = cif->nargs;
|
---|
272 | if (avn <= 2) {
|
---|
273 | for (i = 0; i < avn; ++i) {
|
---|
274 | switch(cif -> arg_types[i] -> type) {
|
---|
275 | case FFI_TYPE_SINT32:
|
---|
276 | simple_flag = FFI_ADD_INT_ARG(simple_flag);
|
---|
277 | break;
|
---|
278 | case FFI_TYPE_SINT64:
|
---|
279 | case FFI_TYPE_UINT64:
|
---|
280 | case FFI_TYPE_POINTER:
|
---|
281 | simple_flag = FFI_ADD_LONG_ARG(simple_flag);
|
---|
282 | break;
|
---|
283 | default:
|
---|
284 | is_simple = FALSE;
|
---|
285 | }
|
---|
286 | }
|
---|
287 | } else {
|
---|
288 | is_simple = FALSE;
|
---|
289 | }
|
---|
290 |
|
---|
291 | /* Set the return type flag */
|
---|
292 | switch (cif->rtype->type)
|
---|
293 | {
|
---|
294 | case FFI_TYPE_VOID:
|
---|
295 | cif->flags = FFI_TYPE_VOID;
|
---|
296 | break;
|
---|
297 |
|
---|
298 | case FFI_TYPE_STRUCT:
|
---|
299 | {
|
---|
300 | size_t sz = cif -> rtype -> size;
|
---|
301 | unsigned short element_type;
|
---|
302 |
|
---|
303 | is_simple = FALSE;
|
---|
304 | if (is_homogeneous_fp_aggregate(cif -> rtype, 8, &element_type)) {
|
---|
305 | int nelements = sz/float_type_size(element_type);
|
---|
306 | if (nelements <= 1) {
|
---|
307 | if (0 == nelements) {
|
---|
308 | cif -> flags = FFI_TYPE_VOID;
|
---|
309 | } else {
|
---|
310 | cif -> flags = element_type;
|
---|
311 | }
|
---|
312 | } else {
|
---|
313 | switch(element_type) {
|
---|
314 | case FFI_TYPE_FLOAT:
|
---|
315 | cif -> flags = FFI_IS_FLOAT_FP_AGGREGATE | nelements;
|
---|
316 | break;
|
---|
317 | case FFI_TYPE_DOUBLE:
|
---|
318 | cif -> flags = FFI_IS_DOUBLE_FP_AGGREGATE | nelements;
|
---|
319 | break;
|
---|
320 | default:
|
---|
321 | /* long double NYI */
|
---|
322 | abort();
|
---|
323 | }
|
---|
324 | }
|
---|
325 | break;
|
---|
326 | }
|
---|
327 | if (sz <= 32) {
|
---|
328 | if (sz <= 8) {
|
---|
329 | cif->flags = FFI_TYPE_INT;
|
---|
330 | } else if (sz <= 16) {
|
---|
331 | cif->flags = FFI_IS_SMALL_STRUCT2;
|
---|
332 | } else if (sz <= 24) {
|
---|
333 | cif->flags = FFI_IS_SMALL_STRUCT3;
|
---|
334 | } else {
|
---|
335 | cif->flags = FFI_IS_SMALL_STRUCT4;
|
---|
336 | }
|
---|
337 | } else {
|
---|
338 | cif->flags = FFI_TYPE_STRUCT;
|
---|
339 | }
|
---|
340 | }
|
---|
341 | break;
|
---|
342 |
|
---|
343 | case FFI_TYPE_FLOAT:
|
---|
344 | is_simple = FALSE;
|
---|
345 | cif->flags = FFI_TYPE_FLOAT;
|
---|
346 | break;
|
---|
347 |
|
---|
348 | case FFI_TYPE_DOUBLE:
|
---|
349 | is_simple = FALSE;
|
---|
350 | cif->flags = FFI_TYPE_DOUBLE;
|
---|
351 | break;
|
---|
352 |
|
---|
353 | default:
|
---|
354 | cif->flags = FFI_TYPE_INT;
|
---|
355 | /* This seems to depend on little endian mode, and the fact that */
|
---|
356 | /* the return pointer always points to at least 8 bytes. But */
|
---|
357 | /* that also seems to be true for other platforms. */
|
---|
358 | break;
|
---|
359 | }
|
---|
360 |
|
---|
361 | if (is_simple) cif -> flags |= simple_flag;
|
---|
362 | return FFI_OK;
|
---|
363 | }
|
---|
364 |
|
---|
365 | extern int ffi_call_unix(bool (*)(struct ia64_args *, extended_cif *, int),
|
---|
366 | extended_cif *, unsigned,
|
---|
367 | unsigned, unsigned *, void (*)());
|
---|
368 |
|
---|
369 | void
|
---|
370 | ffi_call(ffi_cif *cif, void (*fn)(), void *rvalue, void **avalue)
|
---|
371 | {
|
---|
372 | extended_cif ecif;
|
---|
373 | long simple = cif -> flags & FFI_SIMPLE;
|
---|
374 |
|
---|
375 | /* Should this also check for Unix ABI? */
|
---|
376 | /* This is almost, but not quite, machine independent. Note that */
|
---|
377 | /* we can get away with not caring about length of the result because */
|
---|
378 | /* we assume we are little endian, and the result buffer is large */
|
---|
379 | /* enough. */
|
---|
380 | /* This needs work for HP/UX. */
|
---|
381 | if (simple) {
|
---|
382 | long (*lfn)() = (long (*)())fn;
|
---|
383 | long result;
|
---|
384 | switch(simple) {
|
---|
385 | case FFI_SIMPLE_V:
|
---|
386 | result = lfn();
|
---|
387 | break;
|
---|
388 | case FFI_SIMPLE_I:
|
---|
389 | result = lfn(*(int *)avalue[0]);
|
---|
390 | break;
|
---|
391 | case FFI_SIMPLE_L:
|
---|
392 | result = lfn(*(long *)avalue[0]);
|
---|
393 | break;
|
---|
394 | case FFI_SIMPLE_II:
|
---|
395 | result = lfn(*(int *)avalue[0], *(int *)avalue[1]);
|
---|
396 | break;
|
---|
397 | case FFI_SIMPLE_IL:
|
---|
398 | result = lfn(*(int *)avalue[0], *(long *)avalue[1]);
|
---|
399 | break;
|
---|
400 | case FFI_SIMPLE_LI:
|
---|
401 | result = lfn(*(long *)avalue[0], *(int *)avalue[1]);
|
---|
402 | break;
|
---|
403 | case FFI_SIMPLE_LL:
|
---|
404 | result = lfn(*(long *)avalue[0], *(long *)avalue[1]);
|
---|
405 | break;
|
---|
406 | }
|
---|
407 | if ((cif->flags & ~FFI_SIMPLE) != FFI_TYPE_VOID && 0 != rvalue) {
|
---|
408 | * (long *)rvalue = result;
|
---|
409 | }
|
---|
410 | return;
|
---|
411 | }
|
---|
412 | ecif.cif = cif;
|
---|
413 | ecif.avalue = avalue;
|
---|
414 |
|
---|
415 | /* If the return value is a struct and we don't have a return
|
---|
416 | value address then we need to make one. */
|
---|
417 |
|
---|
418 | if (rvalue == NULL && cif->rtype->type == FFI_TYPE_STRUCT)
|
---|
419 | ecif.rvalue = alloca(cif->rtype->size);
|
---|
420 | else
|
---|
421 | ecif.rvalue = rvalue;
|
---|
422 |
|
---|
423 | switch (cif->abi)
|
---|
424 | {
|
---|
425 | case FFI_UNIX:
|
---|
426 | ffi_call_unix(ffi_prep_args, &ecif, cif->bytes,
|
---|
427 | cif->flags, rvalue, fn);
|
---|
428 | break;
|
---|
429 |
|
---|
430 | default:
|
---|
431 | FFI_ASSERT(0);
|
---|
432 | break;
|
---|
433 | }
|
---|
434 | }
|
---|
435 |
|
---|
436 | /*
|
---|
437 | * Closures represent a pair consisting of a function pointer, and
|
---|
438 | * some user data. A closure is invoked by reinterpreting the closure
|
---|
439 | * as a function pointer, and branching to it. Thus we can make an
|
---|
440 | * interpreted function callable as a C function: We turn the interpreter
|
---|
441 | * itself, together with a pointer specifying the interpreted procedure,
|
---|
442 | * into a closure.
|
---|
443 | * On X86, the first few words of the closure structure actually contain code,
|
---|
444 | * which will do the right thing. On most other architectures, this
|
---|
445 | * would raise some Icache/Dcache coherence issues (which can be solved, but
|
---|
446 | * often not cheaply).
|
---|
447 | * For IA64, function pointer are already pairs consisting of a code
|
---|
448 | * pointer, and a gp pointer. The latter is needed to access global variables.
|
---|
449 | * Here we set up such a pair as the first two words of the closure (in
|
---|
450 | * the "trampoline" area), but we replace the gp pointer with a pointer
|
---|
451 | * to the closure itself. We also add the real gp pointer to the
|
---|
452 | * closure. This allows the function entry code to both retrieve the
|
---|
453 | * user data, and to restire the correct gp pointer.
|
---|
454 | */
|
---|
455 |
|
---|
456 | static void
|
---|
457 | ffi_prep_incoming_args_UNIX(struct ia64_args *args, void **rvalue,
|
---|
458 | void **avalue, ffi_cif *cif);
|
---|
459 |
|
---|
460 | /* This function is entered with the doctored gp (r1) value.
|
---|
461 | * This code is extremely gcc specific. There is some argument that
|
---|
462 | * it should really be written in assembly code, since it depends on
|
---|
463 | * gcc properties that might change over time.
|
---|
464 | */
|
---|
465 |
|
---|
466 | /* ffi_closure_UNIX is an assembly routine, which copies the register */
|
---|
467 | /* state into a struct ia64_args, and then invokes */
|
---|
468 | /* ffi_closure_UNIX_inner. It also recovers the closure pointer */
|
---|
469 | /* from its fake gp pointer. */
|
---|
470 | void ffi_closure_UNIX();
|
---|
471 |
|
---|
472 | #ifndef __GNUC__
|
---|
473 | # error This requires gcc
|
---|
474 | #endif
|
---|
475 | void
|
---|
476 | ffi_closure_UNIX_inner (ffi_closure *closure, struct ia64_args * args)
|
---|
477 | /* Hopefully declaring this as a varargs function will force all args */
|
---|
478 | /* to memory. */
|
---|
479 | {
|
---|
480 | // this is our return value storage
|
---|
481 | long double res;
|
---|
482 |
|
---|
483 | // our various things...
|
---|
484 | ffi_cif *cif;
|
---|
485 | unsigned short rtype;
|
---|
486 | void *resp;
|
---|
487 | void **arg_area;
|
---|
488 |
|
---|
489 | resp = (void*)&res;
|
---|
490 | cif = closure->cif;
|
---|
491 | arg_area = (void**) alloca (cif->nargs * sizeof (void*));
|
---|
492 |
|
---|
493 | /* this call will initialize ARG_AREA, such that each
|
---|
494 | * element in that array points to the corresponding
|
---|
495 | * value on the stack; and if the function returns
|
---|
496 | * a structure, it will re-set RESP to point to the
|
---|
497 | * structure return address. */
|
---|
498 |
|
---|
499 | ffi_prep_incoming_args_UNIX(args, (void**)&resp, arg_area, cif);
|
---|
500 |
|
---|
501 | (closure->fun) (cif, resp, arg_area, closure->user_data);
|
---|
502 |
|
---|
503 | rtype = cif->flags;
|
---|
504 |
|
---|
505 | /* now, do a generic return based on the value of rtype */
|
---|
506 | if (rtype == FFI_TYPE_INT)
|
---|
507 | {
|
---|
508 | asm volatile ("ld8 r8=[%0]" : : "r" (resp) : "r8");
|
---|
509 | }
|
---|
510 | else if (rtype == FFI_TYPE_FLOAT)
|
---|
511 | {
|
---|
512 | asm volatile ("ldfs f8=[%0]" : : "r" (resp) : "f8");
|
---|
513 | }
|
---|
514 | else if (rtype == FFI_TYPE_DOUBLE)
|
---|
515 | {
|
---|
516 | asm volatile ("ldfd f8=[%0]" : : "r" (resp) : "f8");
|
---|
517 | }
|
---|
518 | else if (rtype == FFI_IS_SMALL_STRUCT2)
|
---|
519 | {
|
---|
520 | asm volatile ("ld8 r8=[%0]; ld8 r9=[%1]"
|
---|
521 | : : "r" (resp), "r" (resp+8) : "r8","r9");
|
---|
522 | }
|
---|
523 | else if (rtype == FFI_IS_SMALL_STRUCT3)
|
---|
524 | {
|
---|
525 | asm volatile ("ld8 r8=[%0]; ld8 r9=[%1]; ld8 r10=[%2]"
|
---|
526 | : : "r" (resp), "r" (resp+8), "r" (resp+16)
|
---|
527 | : "r8","r9","r10");
|
---|
528 | }
|
---|
529 | else if (rtype == FFI_IS_SMALL_STRUCT4)
|
---|
530 | {
|
---|
531 | asm volatile ("ld8 r8=[%0]; ld8 r9=[%1]; ld8 r10=[%2]; ld8 r11=[%3]"
|
---|
532 | : : "r" (resp), "r" (resp+8), "r" (resp+16), "r" (resp+24)
|
---|
533 | : "r8","r9","r10","r11");
|
---|
534 | }
|
---|
535 | else if (rtype != FFI_TYPE_VOID && rtype != FFI_TYPE_STRUCT)
|
---|
536 | {
|
---|
537 | /* Can only happen for homogeneous FP aggregates? */
|
---|
538 | abort();
|
---|
539 | }
|
---|
540 | }
|
---|
541 |
|
---|
542 | static void
|
---|
543 | ffi_prep_incoming_args_UNIX(struct ia64_args *args, void **rvalue,
|
---|
544 | void **avalue, ffi_cif *cif)
|
---|
545 | {
|
---|
546 | register unsigned int i;
|
---|
547 | register unsigned int avn;
|
---|
548 | register void **p_argv;
|
---|
549 | register unsigned long *argp = args -> out_regs;
|
---|
550 | unsigned fp_reg_num = 0;
|
---|
551 | register ffi_type **p_arg;
|
---|
552 |
|
---|
553 | avn = cif->nargs;
|
---|
554 | p_argv = avalue;
|
---|
555 |
|
---|
556 | for (i = cif->nargs, p_arg = cif->arg_types; i != 0; i--, p_arg++)
|
---|
557 | {
|
---|
558 | size_t z; /* In units of words or argument slots. */
|
---|
559 |
|
---|
560 | switch ((*p_arg)->type)
|
---|
561 | {
|
---|
562 | case FFI_TYPE_SINT8:
|
---|
563 | case FFI_TYPE_UINT8:
|
---|
564 | case FFI_TYPE_SINT16:
|
---|
565 | case FFI_TYPE_UINT16:
|
---|
566 | case FFI_TYPE_SINT32:
|
---|
567 | case FFI_TYPE_UINT32:
|
---|
568 | case FFI_TYPE_SINT64:
|
---|
569 | case FFI_TYPE_UINT64:
|
---|
570 | case FFI_TYPE_POINTER:
|
---|
571 | z = 1;
|
---|
572 | *p_argv = (void *)argp;
|
---|
573 | break;
|
---|
574 |
|
---|
575 | case FFI_TYPE_FLOAT:
|
---|
576 | z = 1;
|
---|
577 | /* Convert argument back to float in place from the saved value */
|
---|
578 | if (fp_reg_num < 8) {
|
---|
579 | *(float *)argp = args -> fp_regs[fp_reg_num++];
|
---|
580 | } else {
|
---|
581 | *(float *)argp = *(double *)argp;
|
---|
582 | }
|
---|
583 | *p_argv = (void *)argp;
|
---|
584 | break;
|
---|
585 |
|
---|
586 | case FFI_TYPE_DOUBLE:
|
---|
587 | z = 1;
|
---|
588 | if (fp_reg_num < 8) {
|
---|
589 | *p_argv = args -> fp_regs + fp_reg_num++;
|
---|
590 | } else {
|
---|
591 | *p_argv = (void *)argp;
|
---|
592 | }
|
---|
593 | break;
|
---|
594 |
|
---|
595 | case FFI_TYPE_STRUCT:
|
---|
596 | {
|
---|
597 | size_t sz = (*p_arg)->size;
|
---|
598 | unsigned short element_type;
|
---|
599 | z = ((*p_arg)->size + SIZEOF_ARG - 1)/SIZEOF_ARG;
|
---|
600 | if (is_homogeneous_fp_aggregate(*p_arg, 8, &element_type)) {
|
---|
601 | int nelements = sz/float_type_size(element_type);
|
---|
602 | if (nelements + fp_reg_num >= 8) {
|
---|
603 | /* hard case NYI. */
|
---|
604 | abort();
|
---|
605 | }
|
---|
606 | if (element_type == FFI_TYPE_DOUBLE) {
|
---|
607 | *p_argv = args -> fp_regs + fp_reg_num;
|
---|
608 | fp_reg_num += nelements;
|
---|
609 | break;
|
---|
610 | }
|
---|
611 | if (element_type == FFI_TYPE_FLOAT) {
|
---|
612 | int j;
|
---|
613 | for (j = 0; j < nelements; ++ j) {
|
---|
614 | ((float *)argp)[j] = args -> fp_regs[fp_reg_num + j];
|
---|
615 | }
|
---|
616 | *p_argv = (void *)argp;
|
---|
617 | fp_reg_num += nelements;
|
---|
618 | break;
|
---|
619 | }
|
---|
620 | abort(); /* Other fp types NYI */
|
---|
621 | }
|
---|
622 | }
|
---|
623 | break;
|
---|
624 |
|
---|
625 | default:
|
---|
626 | FFI_ASSERT(0);
|
---|
627 | }
|
---|
628 |
|
---|
629 | argp += z;
|
---|
630 | p_argv++;
|
---|
631 |
|
---|
632 | }
|
---|
633 |
|
---|
634 | return;
|
---|
635 | }
|
---|
636 |
|
---|
637 |
|
---|
638 | /* Fill in a closure to refer to the specified fun and user_data. */
|
---|
639 | /* cif specifies the argument and result types for fun. */
|
---|
640 | /* the cif must already be prep'ed */
|
---|
641 |
|
---|
642 | /* The layout of a function descriptor. A C function pointer really */
|
---|
643 | /* points to one of these. */
|
---|
644 | typedef struct ia64_fd_struct {
|
---|
645 | void *code_pointer;
|
---|
646 | void *gp;
|
---|
647 | } ia64_fd;
|
---|
648 |
|
---|
649 | ffi_status
|
---|
650 | ffi_prep_closure (ffi_closure* closure,
|
---|
651 | ffi_cif* cif,
|
---|
652 | void (*fun)(ffi_cif*,void*,void**,void*),
|
---|
653 | void *user_data)
|
---|
654 | {
|
---|
655 | struct ffi_ia64_trampoline_struct *tramp =
|
---|
656 | (struct ffi_ia64_trampoline_struct *) (closure -> tramp);
|
---|
657 | ia64_fd *fd = (ia64_fd *)(void *)ffi_closure_UNIX;
|
---|
658 |
|
---|
659 | FFI_ASSERT (cif->abi == FFI_UNIX);
|
---|
660 |
|
---|
661 | tramp -> code_pointer = fd -> code_pointer;
|
---|
662 | tramp -> real_gp = fd -> gp;
|
---|
663 | tramp -> fake_gp = closure;
|
---|
664 | closure->cif = cif;
|
---|
665 | closure->user_data = user_data;
|
---|
666 | closure->fun = fun;
|
---|
667 |
|
---|
668 | return FFI_OK;
|
---|
669 | }
|
---|
670 |
|
---|
671 |
|
---|