Ruby: ext/fiddle/libffi-3.2.1/src/ia64/ffi.c Source File

Ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c52d4d35cc6a173c89eda98ceffa2dcf)

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