source: trunk/gcc/libffi/src/ia64/ffi.c

Last change on this file was 2, checked in by bird, 22 years ago

Initial revision

  • Property cvs2svn:cvs-rev set to 1.1
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 18.2 KB
Line 
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. */
36typedef 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
42struct 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
55static 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 */
76static 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
130static bool
131ffi_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 */
250ffi_status
251ffi_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
365extern int ffi_call_unix(bool (*)(struct ia64_args *, extended_cif *, int),
366 extended_cif *, unsigned,
367 unsigned, unsigned *, void (*)());
368
369void
370ffi_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
456static void
457ffi_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. */
470void ffi_closure_UNIX();
471
472#ifndef __GNUC__
473# error This requires gcc
474#endif
475void
476ffi_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
542static void
543ffi_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. */
644typedef struct ia64_fd_struct {
645 void *code_pointer;
646 void *gp;
647} ia64_fd;
648
649ffi_status
650ffi_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
Note: See TracBrowser for help on using the repository browser.