]> git.ipfire.org Git - thirdparty/gcc.git/blame - libffi/src/ia64/ffi.c
ffi.h.in (ffi_closure_alloc, [...]): New.
[thirdparty/gcc.git] / libffi / src / ia64 / ffi.c
CommitLineData
dc5de370 1/* -----------------------------------------------------------------------
18fa3240 2 ffi.c - Copyright (c) 1998, 2007 Red Hat, Inc.
dc5de370
HB
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>
1450eb7a 31#include <stdbool.h>
81a69b13 32#include <float.h>
dc5de370
HB
33
34#include "ia64_flags.h"
35
81a69b13
RH
36/* A 64-bit pointer value. In LP64 mode, this is effectively a plain
37 pointer. In ILP32 mode, it's a pointer that's been extended to
38 64 bits by "addp4". */
39typedef void *PTR64 __attribute__((mode(DI)));
40
41/* Memory image of fp register contents. This is the implementation
42 specific format used by ldf.fill/stf.spill. All we care about is
43 that it wants a 16 byte aligned slot. */
44typedef struct
45{
46 UINT64 x[2] __attribute__((aligned(16)));
47} fpreg;
48
49
50/* The stack layout given to ffi_call_unix and ffi_closure_unix_inner. */
51
52struct ia64_args
53{
54 fpreg fp_regs[8]; /* Contents of 8 fp arg registers. */
55 UINT64 gp_regs[8]; /* Contents of 8 gp arg registers. */
56 UINT64 other_args[]; /* Arguments passed on stack, variable size. */
dc5de370
HB
57};
58
81a69b13
RH
59
60/* Adjust ADDR, a pointer to an 8 byte slot, to point to the low LEN bytes. */
61
62static inline void *
63endian_adjust (void *addr, size_t len)
dc5de370 64{
81a69b13
RH
65#ifdef __BIG_ENDIAN__
66 return addr + (8 - len);
67#else
68 return addr;
dc5de370 69#endif
dc5de370
HB
70}
71
86066f9b
JW
72/* Store VALUE to ADDR in the current cpu implementation's fp spill format.
73 This is a macro instead of a function, so that it works for all 3 floating
74 point types without type conversions. Type conversion to long double breaks
75 the denorm support. */
81a69b13 76
86066f9b 77#define stf_spill(addr, value) \
81a69b13 78 asm ("stf.spill %0 = %1%P0" : "=m" (*addr) : "f"(value));
81a69b13
RH
79
80/* Load a value from ADDR, which is in the current cpu implementation's
86066f9b 81 fp spill format. As above, this must also be a macro. */
dc5de370 82
86066f9b
JW
83#define ldf_fill(result, addr) \
84 asm ("ldf.fill %0 = %1%P1" : "=f"(result) : "m"(*addr));
dc5de370 85
81a69b13
RH
86/* Return the size of the C type associated with with TYPE. Which will
87 be one of the FFI_IA64_TYPE_HFA_* values. */
dc5de370 88
81a69b13
RH
89static size_t
90hfa_type_size (int type)
91{
92 switch (type)
93 {
94 case FFI_IA64_TYPE_HFA_FLOAT:
95 return sizeof(float);
96 case FFI_IA64_TYPE_HFA_DOUBLE:
97 return sizeof(double);
98 case FFI_IA64_TYPE_HFA_LDOUBLE:
99 return sizeof(__float80);
100 default:
101 abort ();
102 }
103}
dc5de370 104
81a69b13
RH
105/* Load from ADDR a value indicated by TYPE. Which will be one of
106 the FFI_IA64_TYPE_HFA_* values. */
dc5de370 107
86066f9b
JW
108static void
109hfa_type_load (fpreg *fpaddr, int type, void *addr)
81a69b13
RH
110{
111 switch (type)
dc5de370 112 {
81a69b13 113 case FFI_IA64_TYPE_HFA_FLOAT:
86066f9b
JW
114 stf_spill (fpaddr, *(float *) addr);
115 return;
81a69b13 116 case FFI_IA64_TYPE_HFA_DOUBLE:
86066f9b
JW
117 stf_spill (fpaddr, *(double *) addr);
118 return;
81a69b13 119 case FFI_IA64_TYPE_HFA_LDOUBLE:
86066f9b
JW
120 stf_spill (fpaddr, *(__float80 *) addr);
121 return;
81a69b13
RH
122 default:
123 abort ();
dc5de370 124 }
81a69b13 125}
dc5de370 126
81a69b13
RH
127/* Load VALUE into ADDR as indicated by TYPE. Which will be one of
128 the FFI_IA64_TYPE_HFA_* values. */
dc5de370 129
81a69b13 130static void
86066f9b 131hfa_type_store (int type, void *addr, fpreg *fpaddr)
dc5de370 132{
81a69b13 133 switch (type)
dc5de370 134 {
81a69b13 135 case FFI_IA64_TYPE_HFA_FLOAT:
86066f9b
JW
136 {
137 float result;
138 ldf_fill (result, fpaddr);
139 *(float *) addr = result;
140 break;
141 }
81a69b13 142 case FFI_IA64_TYPE_HFA_DOUBLE:
86066f9b
JW
143 {
144 double result;
145 ldf_fill (result, fpaddr);
146 *(double *) addr = result;
147 break;
148 }
81a69b13 149 case FFI_IA64_TYPE_HFA_LDOUBLE:
86066f9b
JW
150 {
151 __float80 result;
152 ldf_fill (result, fpaddr);
153 *(__float80 *) addr = result;
154 break;
155 }
81a69b13
RH
156 default:
157 abort ();
158 }
159}
dc5de370 160
81a69b13
RH
161/* Is TYPE a struct containing floats, doubles, or extended doubles,
162 all of the same fp type? If so, return the element type. Return
163 FFI_TYPE_VOID if not. */
dc5de370 164
81a69b13
RH
165static int
166hfa_element_type (ffi_type *type, int nested)
167{
168 int element = FFI_TYPE_VOID;
dc5de370 169
81a69b13
RH
170 switch (type->type)
171 {
172 case FFI_TYPE_FLOAT:
173 /* We want to return VOID for raw floating-point types, but the
174 synthetic HFA type if we're nested within an aggregate. */
175 if (nested)
176 element = FFI_IA64_TYPE_HFA_FLOAT;
177 break;
dc5de370 178
81a69b13
RH
179 case FFI_TYPE_DOUBLE:
180 /* Similarly. */
181 if (nested)
182 element = FFI_IA64_TYPE_HFA_DOUBLE;
183 break;
dc5de370 184
81a69b13
RH
185 case FFI_TYPE_LONGDOUBLE:
186 /* Similarly, except that that HFA is true for double extended,
187 but not quad precision. Both have sizeof == 16, so tell the
188 difference based on the precision. */
189 if (LDBL_MANT_DIG == 64 && nested)
190 element = FFI_IA64_TYPE_HFA_LDOUBLE;
191 break;
192
193 case FFI_TYPE_STRUCT:
194 {
195 ffi_type **ptr = &type->elements[0];
196
197 for (ptr = &type->elements[0]; *ptr ; ptr++)
dc5de370 198 {
81a69b13
RH
199 int sub_element = hfa_element_type (*ptr, 1);
200 if (sub_element == FFI_TYPE_VOID)
201 return FFI_TYPE_VOID;
202
203 if (element == FFI_TYPE_VOID)
204 element = sub_element;
205 else if (element != sub_element)
206 return FFI_TYPE_VOID;
dc5de370 207 }
81a69b13
RH
208 }
209 break;
dc5de370 210
81a69b13
RH
211 default:
212 return FFI_TYPE_VOID;
dc5de370 213 }
81a69b13
RH
214
215 return element;
dc5de370
HB
216}
217
81a69b13
RH
218
219/* Perform machine dependent cif processing. */
220
dc5de370
HB
221ffi_status
222ffi_prep_cif_machdep(ffi_cif *cif)
223{
81a69b13
RH
224 int flags;
225
226 /* Adjust cif->bytes to include space for the bits of the ia64_args frame
227 that preceeds the integer register portion. The estimate that the
228 generic bits did for the argument space required is good enough for the
229 integer component. */
230 cif->bytes += offsetof(struct ia64_args, gp_regs[0]);
dc5de370
HB
231 if (cif->bytes < sizeof(struct ia64_args))
232 cif->bytes = sizeof(struct ia64_args);
233
81a69b13
RH
234 /* Set the return type flag. */
235 flags = cif->rtype->type;
dc5de370
HB
236 switch (cif->rtype->type)
237 {
81a69b13
RH
238 case FFI_TYPE_LONGDOUBLE:
239 /* Leave FFI_TYPE_LONGDOUBLE as meaning double extended precision,
240 and encode quad precision as a two-word integer structure. */
241 if (LDBL_MANT_DIG != 64)
242 flags = FFI_IA64_TYPE_SMALL_STRUCT | (16 << 8);
dc5de370
HB
243 break;
244
245 case FFI_TYPE_STRUCT:
246 {
81a69b13
RH
247 size_t size = cif->rtype->size;
248 int hfa_type = hfa_element_type (cif->rtype, 0);
249
250 if (hfa_type != FFI_TYPE_VOID)
251 {
252 size_t nelts = size / hfa_type_size (hfa_type);
253 if (nelts <= 8)
254 flags = hfa_type | (size << 8);
dc5de370 255 }
81a69b13
RH
256 else
257 {
258 if (size <= 32)
259 flags = FFI_IA64_TYPE_SMALL_STRUCT | (size << 8);
dc5de370 260 }
dc5de370
HB
261 }
262 break;
263
dc5de370 264 default:
dc5de370
HB
265 break;
266 }
81a69b13
RH
267 cif->flags = flags;
268
dc5de370
HB
269 return FFI_OK;
270}
271
81a69b13 272extern int ffi_call_unix (struct ia64_args *, PTR64, void (*)(), UINT64);
dc5de370
HB
273
274void
275ffi_call(ffi_cif *cif, void (*fn)(), void *rvalue, void **avalue)
276{
81a69b13
RH
277 struct ia64_args *stack;
278 long i, avn, gpcount, fpcount;
279 ffi_type **p_arg;
dc5de370 280
81a69b13 281 FFI_ASSERT (cif->abi == FFI_UNIX);
dc5de370 282
81a69b13
RH
283 /* If we have no spot for a return value, make one. */
284 if (rvalue == NULL && cif->rtype->type != FFI_TYPE_VOID)
285 rvalue = alloca (cif->rtype->size);
286
287 /* Allocate the stack frame. */
288 stack = alloca (cif->bytes);
dc5de370 289
81a69b13 290 gpcount = fpcount = 0;
dc5de370 291 avn = cif->nargs;
81a69b13 292 for (i = 0, p_arg = cif->arg_types; i < avn; i++, p_arg++)
dc5de370 293 {
dc5de370
HB
294 switch ((*p_arg)->type)
295 {
296 case FFI_TYPE_SINT8:
81a69b13
RH
297 stack->gp_regs[gpcount++] = *(SINT8 *)avalue[i];
298 break;
dc5de370 299 case FFI_TYPE_UINT8:
81a69b13
RH
300 stack->gp_regs[gpcount++] = *(UINT8 *)avalue[i];
301 break;
dc5de370 302 case FFI_TYPE_SINT16:
81a69b13
RH
303 stack->gp_regs[gpcount++] = *(SINT16 *)avalue[i];
304 break;
dc5de370 305 case FFI_TYPE_UINT16:
81a69b13
RH
306 stack->gp_regs[gpcount++] = *(UINT16 *)avalue[i];
307 break;
dc5de370 308 case FFI_TYPE_SINT32:
81a69b13
RH
309 stack->gp_regs[gpcount++] = *(SINT32 *)avalue[i];
310 break;
dc5de370 311 case FFI_TYPE_UINT32:
81a69b13
RH
312 stack->gp_regs[gpcount++] = *(UINT32 *)avalue[i];
313 break;
dc5de370
HB
314 case FFI_TYPE_SINT64:
315 case FFI_TYPE_UINT64:
81a69b13
RH
316 stack->gp_regs[gpcount++] = *(UINT64 *)avalue[i];
317 break;
318
dc5de370 319 case FFI_TYPE_POINTER:
81a69b13 320 stack->gp_regs[gpcount++] = (UINT64)(PTR64) *(void **)avalue[i];
dc5de370 321 break;
81a69b13 322
dc5de370 323 case FFI_TYPE_FLOAT:
81a69b13
RH
324 if (gpcount < 8 && fpcount < 8)
325 stf_spill (&stack->fp_regs[fpcount++], *(float *)avalue[i]);
326 stack->gp_regs[gpcount++] = *(UINT32 *)avalue[i];
dc5de370
HB
327 break;
328
329 case FFI_TYPE_DOUBLE:
81a69b13
RH
330 if (gpcount < 8 && fpcount < 8)
331 stf_spill (&stack->fp_regs[fpcount++], *(double *)avalue[i]);
332 stack->gp_regs[gpcount++] = *(UINT64 *)avalue[i];
333 break;
334
335 case FFI_TYPE_LONGDOUBLE:
336 if (gpcount & 1)
337 gpcount++;
338 if (LDBL_MANT_DIG == 64 && gpcount < 8 && fpcount < 8)
339 stf_spill (&stack->fp_regs[fpcount++], *(__float80 *)avalue[i]);
340 memcpy (&stack->gp_regs[gpcount], avalue[i], 16);
341 gpcount += 2;
dc5de370
HB
342 break;
343
344 case FFI_TYPE_STRUCT:
345 {
81a69b13
RH
346 size_t size = (*p_arg)->size;
347 size_t align = (*p_arg)->alignment;
348 int hfa_type = hfa_element_type (*p_arg, 0);
349
350 FFI_ASSERT (align <= 16);
351 if (align == 16 && (gpcount & 1))
352 gpcount++;
353
354 if (hfa_type != FFI_TYPE_VOID)
355 {
356 size_t hfa_size = hfa_type_size (hfa_type);
357 size_t offset = 0;
358 size_t gp_offset = gpcount * 8;
359
360 while (fpcount < 8
361 && offset < size
362 && gp_offset < 8 * 8)
363 {
86066f9b
JW
364 hfa_type_load (&stack->fp_regs[fpcount], hfa_type,
365 avalue[i] + offset);
81a69b13
RH
366 offset += hfa_size;
367 gp_offset += hfa_size;
368 fpcount += 1;
dc5de370 369 }
dc5de370 370 }
81a69b13
RH
371
372 memcpy (&stack->gp_regs[gpcount], avalue[i], size);
373 gpcount += (size + 7) / 8;
dc5de370
HB
374 }
375 break;
376
377 default:
81a69b13 378 abort ();
dc5de370 379 }
dc5de370 380 }
81a69b13
RH
381
382 ffi_call_unix (stack, rvalue, fn, cif->flags);
dc5de370
HB
383}
384
81a69b13
RH
385/* Closures represent a pair consisting of a function pointer, and
386 some user data. A closure is invoked by reinterpreting the closure
387 as a function pointer, and branching to it. Thus we can make an
388 interpreted function callable as a C function: We turn the
389 interpreter itself, together with a pointer specifying the
390 interpreted procedure, into a closure.
dc5de370 391
81a69b13
RH
392 For IA64, function pointer are already pairs consisting of a code
393 pointer, and a gp pointer. The latter is needed to access global
394 variables. Here we set up such a pair as the first two words of
395 the closure (in the "trampoline" area), but we replace the gp
396 pointer with a pointer to the closure itself. We also add the real
397 gp pointer to the closure. This allows the function entry code to
398 both retrieve the user data, and to restire the correct gp pointer. */
dc5de370 399
81a69b13 400extern void ffi_closure_unix ();
dc5de370
HB
401
402ffi_status
18fa3240
AO
403ffi_prep_closure_loc (ffi_closure* closure,
404 ffi_cif* cif,
405 void (*fun)(ffi_cif*,void*,void**,void*),
406 void *user_data,
407 void *codeloc)
dc5de370 408{
81a69b13
RH
409 /* The layout of a function descriptor. A C function pointer really
410 points to one of these. */
411 struct ia64_fd
412 {
413 UINT64 code_pointer;
414 UINT64 gp;
415 };
416
417 struct ffi_ia64_trampoline_struct
418 {
419 UINT64 code_pointer; /* Pointer to ffi_closure_unix. */
420 UINT64 fake_gp; /* Pointer to closure, installed as gp. */
421 UINT64 real_gp; /* Real gp value. */
422 };
423
424 struct ffi_ia64_trampoline_struct *tramp;
425 struct ia64_fd *fd;
dc5de370
HB
426
427 FFI_ASSERT (cif->abi == FFI_UNIX);
428
81a69b13
RH
429 tramp = (struct ffi_ia64_trampoline_struct *)closure->tramp;
430 fd = (struct ia64_fd *)(void *)ffi_closure_unix;
431
432 tramp->code_pointer = fd->code_pointer;
433 tramp->real_gp = fd->gp;
18fa3240 434 tramp->fake_gp = (UINT64)(PTR64)codeloc;
81a69b13 435 closure->cif = cif;
dc5de370 436 closure->user_data = user_data;
81a69b13 437 closure->fun = fun;
dc5de370
HB
438
439 return FFI_OK;
440}
441
442
81a69b13
RH
443UINT64
444ffi_closure_unix_inner (ffi_closure *closure, struct ia64_args *stack,
445 void *rvalue, void *r8)
446{
447 ffi_cif *cif;
448 void **avalue;
449 ffi_type **p_arg;
450 long i, avn, gpcount, fpcount;
451
452 cif = closure->cif;
453 avn = cif->nargs;
454 avalue = alloca (avn * sizeof (void *));
455
456 /* If the structure return value is passed in memory get that location
457 from r8 so as to pass the value directly back to the caller. */
458 if (cif->flags == FFI_TYPE_STRUCT)
459 rvalue = r8;
460
461 gpcount = fpcount = 0;
462 for (i = 0, p_arg = cif->arg_types; i < avn; i++, p_arg++)
463 {
464 switch ((*p_arg)->type)
465 {
466 case FFI_TYPE_SINT8:
467 case FFI_TYPE_UINT8:
468 avalue[i] = endian_adjust(&stack->gp_regs[gpcount++], 1);
469 break;
470 case FFI_TYPE_SINT16:
471 case FFI_TYPE_UINT16:
472 avalue[i] = endian_adjust(&stack->gp_regs[gpcount++], 2);
473 break;
474 case FFI_TYPE_SINT32:
475 case FFI_TYPE_UINT32:
476 avalue[i] = endian_adjust(&stack->gp_regs[gpcount++], 4);
477 break;
478 case FFI_TYPE_SINT64:
479 case FFI_TYPE_UINT64:
480 avalue[i] = &stack->gp_regs[gpcount++];
481 break;
482 case FFI_TYPE_POINTER:
483 avalue[i] = endian_adjust(&stack->gp_regs[gpcount++], sizeof(void*));
484 break;
485
486 case FFI_TYPE_FLOAT:
487 if (gpcount < 8 && fpcount < 8)
488 {
86066f9b
JW
489 fpreg *addr = &stack->fp_regs[fpcount++];
490 float result;
81a69b13 491 avalue[i] = addr;
86066f9b
JW
492 ldf_fill (result, addr);
493 *(float *)addr = result;
81a69b13
RH
494 }
495 else
496 avalue[i] = endian_adjust(&stack->gp_regs[gpcount], 4);
497 gpcount++;
498 break;
499
500 case FFI_TYPE_DOUBLE:
501 if (gpcount < 8 && fpcount < 8)
502 {
86066f9b
JW
503 fpreg *addr = &stack->fp_regs[fpcount++];
504 double result;
81a69b13 505 avalue[i] = addr;
86066f9b
JW
506 ldf_fill (result, addr);
507 *(double *)addr = result;
81a69b13
RH
508 }
509 else
510 avalue[i] = &stack->gp_regs[gpcount];
511 gpcount++;
512 break;
513
514 case FFI_TYPE_LONGDOUBLE:
515 if (gpcount & 1)
516 gpcount++;
517 if (LDBL_MANT_DIG == 64 && gpcount < 8 && fpcount < 8)
518 {
86066f9b
JW
519 fpreg *addr = &stack->fp_regs[fpcount++];
520 __float80 result;
81a69b13 521 avalue[i] = addr;
86066f9b
JW
522 ldf_fill (result, addr);
523 *(__float80 *)addr = result;
81a69b13
RH
524 }
525 else
526 avalue[i] = &stack->gp_regs[gpcount];
527 gpcount += 2;
528 break;
529
530 case FFI_TYPE_STRUCT:
531 {
532 size_t size = (*p_arg)->size;
533 size_t align = (*p_arg)->alignment;
534 int hfa_type = hfa_element_type (*p_arg, 0);
535
536 FFI_ASSERT (align <= 16);
537 if (align == 16 && (gpcount & 1))
538 gpcount++;
539
540 if (hfa_type != FFI_TYPE_VOID)
541 {
542 size_t hfa_size = hfa_type_size (hfa_type);
543 size_t offset = 0;
544 size_t gp_offset = gpcount * 8;
545 void *addr = alloca (size);
546
547 avalue[i] = addr;
548
549 while (fpcount < 8
550 && offset < size
551 && gp_offset < 8 * 8)
552 {
86066f9b
JW
553 hfa_type_store (hfa_type, addr + offset,
554 &stack->fp_regs[fpcount]);
81a69b13
RH
555 offset += hfa_size;
556 gp_offset += hfa_size;
557 fpcount += 1;
558 }
559
560 if (offset < size)
561 memcpy (addr + offset, (char *)stack->gp_regs + gp_offset,
562 size - offset);
563 }
564 else
565 avalue[i] = &stack->gp_regs[gpcount];
566
567 gpcount += (size + 7) / 8;
568 }
569 break;
570
571 default:
572 abort ();
573 }
574 }
575
576 closure->fun (cif, rvalue, avalue, closure->user_data);
577
578 return cif->flags;
579}