1 /* -----------------------------------------------------------------------
2 ffi.c - Copyright (c) 2011 Timothy Wall
3 Copyright (c) 2011 Plausible Labs Cooperative, Inc.
4 Copyright (c) 2011 Anthony Green
5 Copyright (c) 2011 Free Software Foundation
6 Copyright (c) 1998, 2008, 2011 Red Hat, Inc.
7
8 ARM Foreign Function Interface
9
10 Permission is hereby granted, free of charge, to any person obtaining
11 a copy of this software and associated documentation files (the
12 ``Software''), to deal in the Software without restriction, including
13 without limitation the rights to use, copy, modify, merge, publish,
14 distribute, sublicense, and/or sell copies of the Software, and to
15 permit persons to whom the Software is furnished to do so, subject to
16 the following conditions:
17
18 The above copyright notice and this permission notice shall be included
19 in all copies or substantial portions of the Software.
20
21 THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
22 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 DEALINGS IN THE SOFTWARE.
29 ----------------------------------------------------------------------- */
30
31 #include <ffi.h>
32 #include <ffi_common.h>
33
34 #include <stdlib.h>
35
36 /* Forward declares. */
37 static int vfp_type_p (ffi_type *);
38 static void layout_vfp_args (ffi_cif *);
39
40 /* ffi_prep_args is called by the assembly routine once stack space
41 has been allocated for the function's arguments
42
43 The vfp_space parameter is the load area for VFP regs, the return
44 value is cif->vfp_used (word bitset of VFP regs used for passing
45 arguments). These are only used for the VFP hard-float ABI.
46 */
ffi_prep_args(char * stack,extended_cif * ecif,float * vfp_space)47 int ffi_prep_args(char *stack, extended_cif *ecif, float *vfp_space)
48 {
49 register unsigned int i, vi = 0;
50 register void **p_argv;
51 register char *argp;
52 register ffi_type **p_arg;
53
54 argp = stack;
55
56 if ( ecif->cif->flags == FFI_TYPE_STRUCT ) {
57 *(void **) argp = ecif->rvalue;
58 argp += 4;
59 }
60
61 p_argv = ecif->avalue;
62
63 for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types;
64 (i != 0);
65 i--, p_arg++)
66 {
67 size_t z;
68 size_t alignment;
69
70 /* Allocated in VFP registers. */
71 if (ecif->cif->abi == FFI_VFP
72 && vi < ecif->cif->vfp_nargs && vfp_type_p (*p_arg))
73 {
74 float* vfp_slot = vfp_space + ecif->cif->vfp_args[vi++];
75 if ((*p_arg)->type == FFI_TYPE_FLOAT)
76 *((float*)vfp_slot) = *((float*)*p_argv);
77 else if ((*p_arg)->type == FFI_TYPE_DOUBLE)
78 *((double*)vfp_slot) = *((double*)*p_argv);
79 else
80 memcpy(vfp_slot, *p_argv, (*p_arg)->size);
81 p_argv++;
82 continue;
83 }
84
85 /* Align if necessary */
86 alignment = (*p_arg)->alignment;
87 #ifdef _WIN32_WCE
88 if (alignment > 4)
89 alignment = 4;
90 #endif
91 if ((alignment - 1) & (unsigned) argp) {
92 argp = (char *) ALIGN(argp, alignment);
93 }
94
95 if ((*p_arg)->type == FFI_TYPE_STRUCT)
96 argp = (char *) ALIGN(argp, 4);
97
98 z = (*p_arg)->size;
99 if (z < sizeof(int))
100 {
101 z = sizeof(int);
102 switch ((*p_arg)->type)
103 {
104 case FFI_TYPE_SINT8:
105 *(signed int *) argp = (signed int)*(SINT8 *)(* p_argv);
106 break;
107
108 case FFI_TYPE_UINT8:
109 *(unsigned int *) argp = (unsigned int)*(UINT8 *)(* p_argv);
110 break;
111
112 case FFI_TYPE_SINT16:
113 *(signed int *) argp = (signed int)*(SINT16 *)(* p_argv);
114 break;
115
116 case FFI_TYPE_UINT16:
117 *(unsigned int *) argp = (unsigned int)*(UINT16 *)(* p_argv);
118 break;
119
120 case FFI_TYPE_STRUCT:
121 memcpy(argp, *p_argv, (*p_arg)->size);
122 break;
123
124 default:
125 FFI_ASSERT(0);
126 }
127 }
128 else if (z == sizeof(int))
129 {
130 *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv);
131 }
132 else
133 {
134 memcpy(argp, *p_argv, z);
135 }
136 p_argv++;
137 argp += z;
138 }
139
140 /* Indicate the VFP registers used. */
141 return ecif->cif->vfp_used;
142 }
143
144 /* Perform machine dependent cif processing */
ffi_prep_cif_machdep(ffi_cif * cif)145 ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
146 {
147 int type_code;
148 /* Round the stack up to a multiple of 8 bytes. This isn't needed
149 everywhere, but it is on some platforms, and it doesn't harm anything
150 when it isn't needed. */
151 cif->bytes = (cif->bytes + 7) & ~7;
152
153 /* Set the return type flag */
154 switch (cif->rtype->type)
155 {
156 case FFI_TYPE_VOID:
157 case FFI_TYPE_FLOAT:
158 case FFI_TYPE_DOUBLE:
159 cif->flags = (unsigned) cif->rtype->type;
160 break;
161
162 case FFI_TYPE_SINT64:
163 case FFI_TYPE_UINT64:
164 cif->flags = (unsigned) FFI_TYPE_SINT64;
165 break;
166
167 case FFI_TYPE_STRUCT:
168 if (cif->abi == FFI_VFP
169 && (type_code = vfp_type_p (cif->rtype)) != 0)
170 {
171 /* A Composite Type passed in VFP registers, either
172 FFI_TYPE_STRUCT_VFP_FLOAT or FFI_TYPE_STRUCT_VFP_DOUBLE. */
173 cif->flags = (unsigned) type_code;
174 }
175 else if (cif->rtype->size <= 4)
176 /* A Composite Type not larger than 4 bytes is returned in r0. */
177 cif->flags = (unsigned)FFI_TYPE_INT;
178 else
179 /* A Composite Type larger than 4 bytes, or whose size cannot
180 be determined statically ... is stored in memory at an
181 address passed [in r0]. */
182 cif->flags = (unsigned)FFI_TYPE_STRUCT;
183 break;
184
185 default:
186 cif->flags = FFI_TYPE_INT;
187 break;
188 }
189
190 /* Map out the register placements of VFP register args.
191 The VFP hard-float calling conventions are slightly more sophisticated than
192 the base calling conventions, so we do it here instead of in ffi_prep_args(). */
193 if (cif->abi == FFI_VFP)
194 layout_vfp_args (cif);
195
196 return FFI_OK;
197 }
198
199 /* Perform machine dependent cif processing for variadic calls */
ffi_prep_cif_machdep_var(ffi_cif * cif,unsigned int nfixedargs,unsigned int ntotalargs)200 ffi_status ffi_prep_cif_machdep_var(ffi_cif *cif,
201 unsigned int nfixedargs,
202 unsigned int ntotalargs)
203 {
204 /* VFP variadic calls actually use the SYSV ABI */
205 if (cif->abi == FFI_VFP)
206 cif->abi = FFI_SYSV;
207
208 return ffi_prep_cif_machdep(cif);
209 }
210
211 /* Prototypes for assembly functions, in sysv.S */
212 extern void ffi_call_SYSV (void (*fn)(void), extended_cif *, unsigned, unsigned, unsigned *);
213 extern void ffi_call_VFP (void (*fn)(void), extended_cif *, unsigned, unsigned, unsigned *);
214
ffi_call(ffi_cif * cif,void (* fn)(void),void * rvalue,void ** avalue)215 void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
216 {
217 extended_cif ecif;
218
219 int small_struct = (cif->flags == FFI_TYPE_INT
220 && cif->rtype->type == FFI_TYPE_STRUCT);
221 int vfp_struct = (cif->flags == FFI_TYPE_STRUCT_VFP_FLOAT
222 || cif->flags == FFI_TYPE_STRUCT_VFP_DOUBLE);
223
224 ecif.cif = cif;
225 ecif.avalue = avalue;
226
227 unsigned int temp;
228
229 /* If the return value is a struct and we don't have a return */
230 /* value address then we need to make one */
231
232 if ((rvalue == NULL) &&
233 (cif->flags == FFI_TYPE_STRUCT))
234 {
235 ecif.rvalue = alloca(cif->rtype->size);
236 }
237 else if (small_struct)
238 ecif.rvalue = &temp;
239 else if (vfp_struct)
240 {
241 /* Largest case is double x 4. */
242 ecif.rvalue = alloca(32);
243 }
244 else
245 ecif.rvalue = rvalue;
246
247 switch (cif->abi)
248 {
249 case FFI_SYSV:
250 ffi_call_SYSV (fn, &ecif, cif->bytes, cif->flags, ecif.rvalue);
251 break;
252
253 case FFI_VFP:
254 #ifdef __ARM_EABI__
255 ffi_call_VFP (fn, &ecif, cif->bytes, cif->flags, ecif.rvalue);
256 break;
257 #endif
258
259 default:
260 FFI_ASSERT(0);
261 break;
262 }
263 if (small_struct)
264 memcpy (rvalue, &temp, cif->rtype->size);
265 else if (vfp_struct)
266 memcpy (rvalue, ecif.rvalue, cif->rtype->size);
267 }
268
269 /** private members **/
270
271 static void ffi_prep_incoming_args_SYSV (char *stack, void **ret,
272 void** args, ffi_cif* cif, float *vfp_stack);
273
274 void ffi_closure_SYSV (ffi_closure *);
275
276 void ffi_closure_VFP (ffi_closure *);
277
278 /* This function is jumped to by the trampoline */
279
280 unsigned int
ffi_closure_SYSV_inner(closure,respp,args,vfp_args)281 ffi_closure_SYSV_inner (closure, respp, args, vfp_args)
282 ffi_closure *closure;
283 void **respp;
284 void *args;
285 void *vfp_args;
286 {
287 // our various things...
288 ffi_cif *cif;
289 void **arg_area;
290
291 cif = closure->cif;
292 arg_area = (void**) alloca (cif->nargs * sizeof (void*));
293
294 /* this call will initialize ARG_AREA, such that each
295 * element in that array points to the corresponding
296 * value on the stack; and if the function returns
297 * a structure, it will re-set RESP to point to the
298 * structure return address. */
299
300 ffi_prep_incoming_args_SYSV(args, respp, arg_area, cif, vfp_args);
301
302 (closure->fun) (cif, *respp, arg_area, closure->user_data);
303
304 return cif->flags;
305 }
306
307 /*@-exportheader@*/
308 static void
ffi_prep_incoming_args_SYSV(char * stack,void ** rvalue,void ** avalue,ffi_cif * cif,float * vfp_stack)309 ffi_prep_incoming_args_SYSV(char *stack, void **rvalue,
310 void **avalue, ffi_cif *cif,
311 /* Used only under VFP hard-float ABI. */
312 float *vfp_stack)
313 /*@=exportheader@*/
314 {
315 register unsigned int i, vi = 0;
316 register void **p_argv;
317 register char *argp;
318 register ffi_type **p_arg;
319
320 argp = stack;
321
322 if ( cif->flags == FFI_TYPE_STRUCT ) {
323 *rvalue = *(void **) argp;
324 argp += 4;
325 }
326
327 p_argv = avalue;
328
329 for (i = cif->nargs, p_arg = cif->arg_types; (i != 0); i--, p_arg++)
330 {
331 size_t z;
332 size_t alignment;
333
334 if (cif->abi == FFI_VFP
335 && vi < cif->vfp_nargs && vfp_type_p (*p_arg))
336 {
337 *p_argv++ = (void*)(vfp_stack + cif->vfp_args[vi++]);
338 continue;
339 }
340
341 alignment = (*p_arg)->alignment;
342 if (alignment < 4)
343 alignment = 4;
344 #ifdef _WIN32_WCE
345 else
346 if (alignment > 4)
347 alignment = 4;
348 #endif
349 /* Align if necessary */
350 if ((alignment - 1) & (unsigned) argp) {
351 argp = (char *) ALIGN(argp, alignment);
352 }
353
354 z = (*p_arg)->size;
355
356 /* because we're little endian, this is what it turns into. */
357
358 *p_argv = (void*) argp;
359
360 p_argv++;
361 argp += z;
362 }
363
364 return;
365 }
366
367 /* How to make a trampoline. */
368
369 extern unsigned int ffi_arm_trampoline[3];
370
371 #if FFI_EXEC_TRAMPOLINE_TABLE
372
373 #include <mach/mach.h>
374 #include <pthread.h>
375 #include <stdio.h>
376 #include <stdlib.h>
377
378 extern void *ffi_closure_trampoline_table_page;
379
380 typedef struct ffi_trampoline_table ffi_trampoline_table;
381 typedef struct ffi_trampoline_table_entry ffi_trampoline_table_entry;
382
383 struct ffi_trampoline_table {
384 /* contigious writable and executable pages */
385 vm_address_t config_page;
386 vm_address_t trampoline_page;
387
388 /* free list tracking */
389 uint16_t free_count;
390 ffi_trampoline_table_entry *free_list;
391 ffi_trampoline_table_entry *free_list_pool;
392
393 ffi_trampoline_table *prev;
394 ffi_trampoline_table *next;
395 };
396
397 struct ffi_trampoline_table_entry {
398 void *(*trampoline)();
399 ffi_trampoline_table_entry *next;
400 };
401
402 /* Override the standard architecture trampoline size */
403 // XXX TODO - Fix
404 #undef FFI_TRAMPOLINE_SIZE
405 #define FFI_TRAMPOLINE_SIZE 12
406
407 /* The trampoline configuration is placed at 4080 bytes prior to the trampoline's entry point */
408 #define FFI_TRAMPOLINE_CODELOC_CONFIG(codeloc) ((void **) (((uint8_t *) codeloc) - 4080));
409
410 /* The first 16 bytes of the config page are unused, as they are unaddressable from the trampoline page. */
411 #define FFI_TRAMPOLINE_CONFIG_PAGE_OFFSET 16
412
413 /* Total number of trampolines that fit in one trampoline table */
414 #define FFI_TRAMPOLINE_COUNT ((PAGE_SIZE - FFI_TRAMPOLINE_CONFIG_PAGE_OFFSET) / FFI_TRAMPOLINE_SIZE)
415
416 static pthread_mutex_t ffi_trampoline_lock = PTHREAD_MUTEX_INITIALIZER;
417 static ffi_trampoline_table *ffi_trampoline_tables = NULL;
418
419 static ffi_trampoline_table *
ffi_trampoline_table_alloc()420 ffi_trampoline_table_alloc ()
421 {
422 ffi_trampoline_table *table = NULL;
423
424 /* Loop until we can allocate two contigious pages */
425 while (table == NULL) {
426 vm_address_t config_page = 0x0;
427 kern_return_t kt;
428
429 /* Try to allocate two pages */
430 kt = vm_allocate (mach_task_self (), &config_page, PAGE_SIZE*2, VM_FLAGS_ANYWHERE);
431 if (kt != KERN_SUCCESS) {
432 fprintf(stderr, "vm_allocate() failure: %d at %s:%d\n", kt, __FILE__, __LINE__);
433 break;
434 }
435
436 /* Now drop the second half of the allocation to make room for the trampoline table */
437 vm_address_t trampoline_page = config_page+PAGE_SIZE;
438 kt = vm_deallocate (mach_task_self (), trampoline_page, PAGE_SIZE);
439 if (kt != KERN_SUCCESS) {
440 fprintf(stderr, "vm_deallocate() failure: %d at %s:%d\n", kt, __FILE__, __LINE__);
441 break;
442 }
443
444 /* Remap the trampoline table to directly follow the config page */
445 vm_prot_t cur_prot;
446 vm_prot_t max_prot;
447
448 kt = vm_remap (mach_task_self (), &trampoline_page, PAGE_SIZE, 0x0, FALSE, mach_task_self (), (vm_address_t) &ffi_closure_trampoline_table_page, FALSE, &cur_prot, &max_prot, VM_INHERIT_SHARE);
449
450 /* If we lost access to the destination trampoline page, drop our config allocation mapping and retry */
451 if (kt != KERN_SUCCESS) {
452 /* Log unexpected failures */
453 if (kt != KERN_NO_SPACE) {
454 fprintf(stderr, "vm_remap() failure: %d at %s:%d\n", kt, __FILE__, __LINE__);
455 }
456
457 vm_deallocate (mach_task_self (), config_page, PAGE_SIZE);
458 continue;
459 }
460
461 /* We have valid trampoline and config pages */
462 table = calloc (1, sizeof(ffi_trampoline_table));
463 table->free_count = FFI_TRAMPOLINE_COUNT;
464 table->config_page = config_page;
465 table->trampoline_page = trampoline_page;
466
467 /* Create and initialize the free list */
468 table->free_list_pool = calloc(FFI_TRAMPOLINE_COUNT, sizeof(ffi_trampoline_table_entry));
469
470 uint16_t i;
471 for (i = 0; i < table->free_count; i++) {
472 ffi_trampoline_table_entry *entry = &table->free_list_pool[i];
473 entry->trampoline = (void *) (table->trampoline_page + (i * FFI_TRAMPOLINE_SIZE));
474
475 if (i < table->free_count - 1)
476 entry->next = &table->free_list_pool[i+1];
477 }
478
479 table->free_list = table->free_list_pool;
480 }
481
482 return table;
483 }
484
485 void *
ffi_closure_alloc(size_t size,void ** code)486 ffi_closure_alloc (size_t size, void **code)
487 {
488 /* Create the closure */
489 ffi_closure *closure = malloc(size);
490 if (closure == NULL)
491 return NULL;
492
493 pthread_mutex_lock(&ffi_trampoline_lock);
494
495 /* Check for an active trampoline table with available entries. */
496 ffi_trampoline_table *table = ffi_trampoline_tables;
497 if (table == NULL || table->free_list == NULL) {
498 table = ffi_trampoline_table_alloc ();
499 if (table == NULL) {
500 free(closure);
501 return NULL;
502 }
503
504 /* Insert the new table at the top of the list */
505 table->next = ffi_trampoline_tables;
506 if (table->next != NULL)
507 table->next->prev = table;
508
509 ffi_trampoline_tables = table;
510 }
511
512 /* Claim the free entry */
513 ffi_trampoline_table_entry *entry = ffi_trampoline_tables->free_list;
514 ffi_trampoline_tables->free_list = entry->next;
515 ffi_trampoline_tables->free_count--;
516 entry->next = NULL;
517
518 pthread_mutex_unlock(&ffi_trampoline_lock);
519
520 /* Initialize the return values */
521 *code = entry->trampoline;
522 closure->trampoline_table = table;
523 closure->trampoline_table_entry = entry;
524
525 return closure;
526 }
527
528 void
ffi_closure_free(void * ptr)529 ffi_closure_free (void *ptr)
530 {
531 ffi_closure *closure = ptr;
532
533 pthread_mutex_lock(&ffi_trampoline_lock);
534
535 /* Fetch the table and entry references */
536 ffi_trampoline_table *table = closure->trampoline_table;
537 ffi_trampoline_table_entry *entry = closure->trampoline_table_entry;
538
539 /* Return the entry to the free list */
540 entry->next = table->free_list;
541 table->free_list = entry;
542 table->free_count++;
543
544 /* If all trampolines within this table are free, and at least one other table exists, deallocate
545 * the table */
546 if (table->free_count == FFI_TRAMPOLINE_COUNT && ffi_trampoline_tables != table) {
547 /* Remove from the list */
548 if (table->prev != NULL)
549 table->prev->next = table->next;
550
551 if (table->next != NULL)
552 table->next->prev = table->prev;
553
554 /* Deallocate pages */
555 kern_return_t kt;
556 kt = vm_deallocate (mach_task_self (), table->config_page, PAGE_SIZE);
557 if (kt != KERN_SUCCESS)
558 fprintf(stderr, "vm_deallocate() failure: %d at %s:%d\n", kt, __FILE__, __LINE__);
559
560 kt = vm_deallocate (mach_task_self (), table->trampoline_page, PAGE_SIZE);
561 if (kt != KERN_SUCCESS)
562 fprintf(stderr, "vm_deallocate() failure: %d at %s:%d\n", kt, __FILE__, __LINE__);
563
564 /* Deallocate free list */
565 free (table->free_list_pool);
566 free (table);
567 } else if (ffi_trampoline_tables != table) {
568 /* Otherwise, bump this table to the top of the list */
569 table->prev = NULL;
570 table->next = ffi_trampoline_tables;
571 if (ffi_trampoline_tables != NULL)
572 ffi_trampoline_tables->prev = table;
573
574 ffi_trampoline_tables = table;
575 }
576
577 pthread_mutex_unlock (&ffi_trampoline_lock);
578
579 /* Free the closure */
580 free (closure);
581 }
582
583 #else
584
585 #define FFI_INIT_TRAMPOLINE(TRAMP,FUN,CTX) \
586 ({ unsigned char *__tramp = (unsigned char*)(TRAMP); \
587 unsigned int __fun = (unsigned int)(FUN); \
588 unsigned int __ctx = (unsigned int)(CTX); \
589 unsigned char *insns = (unsigned char *)(CTX); \
590 memcpy (__tramp, ffi_arm_trampoline, sizeof ffi_arm_trampoline); \
591 *(unsigned int*) &__tramp[12] = __ctx; \
592 *(unsigned int*) &__tramp[16] = __fun; \
593 __clear_cache((&__tramp[0]), (&__tramp[19])); /* Clear data mapping. */ \
594 __clear_cache(insns, insns + 3 * sizeof (unsigned int)); \
595 /* Clear instruction \
596 mapping. */ \
597 })
598
599 #endif
600
601 /* the cif must already be prep'ed */
602
603 ffi_status
ffi_prep_closure_loc(ffi_closure * closure,ffi_cif * cif,void (* fun)(ffi_cif *,void *,void **,void *),void * user_data,void * codeloc)604 ffi_prep_closure_loc (ffi_closure* closure,
605 ffi_cif* cif,
606 void (*fun)(ffi_cif*,void*,void**,void*),
607 void *user_data,
608 void *codeloc)
609 {
610 void (*closure_func)(ffi_closure*) = NULL;
611
612 if (cif->abi == FFI_SYSV)
613 closure_func = &ffi_closure_SYSV;
614 #ifdef __ARM_EABI__
615 else if (cif->abi == FFI_VFP)
616 closure_func = &ffi_closure_VFP;
617 #endif
618 else
619 return FFI_BAD_ABI;
620
621 #if FFI_EXEC_TRAMPOLINE_TABLE
622 void **config = FFI_TRAMPOLINE_CODELOC_CONFIG(codeloc);
623 config[0] = closure;
624 config[1] = closure_func;
625 #else
626 FFI_INIT_TRAMPOLINE (&closure->tramp[0], \
627 closure_func, \
628 codeloc);
629 #endif
630
631 closure->cif = cif;
632 closure->user_data = user_data;
633 closure->fun = fun;
634
635 return FFI_OK;
636 }
637
638 /* Below are routines for VFP hard-float support. */
639
rec_vfp_type_p(ffi_type * t,int * elt,int * elnum)640 static int rec_vfp_type_p (ffi_type *t, int *elt, int *elnum)
641 {
642 switch (t->type)
643 {
644 case FFI_TYPE_FLOAT:
645 case FFI_TYPE_DOUBLE:
646 *elt = (int) t->type;
647 *elnum = 1;
648 return 1;
649
650 case FFI_TYPE_STRUCT_VFP_FLOAT:
651 *elt = FFI_TYPE_FLOAT;
652 *elnum = t->size / sizeof (float);
653 return 1;
654
655 case FFI_TYPE_STRUCT_VFP_DOUBLE:
656 *elt = FFI_TYPE_DOUBLE;
657 *elnum = t->size / sizeof (double);
658 return 1;
659
660 case FFI_TYPE_STRUCT:;
661 {
662 int base_elt = 0, total_elnum = 0;
663 ffi_type **el = t->elements;
664 while (*el)
665 {
666 int el_elt = 0, el_elnum = 0;
667 if (! rec_vfp_type_p (*el, &el_elt, &el_elnum)
668 || (base_elt && base_elt != el_elt)
669 || total_elnum + el_elnum > 4)
670 return 0;
671 base_elt = el_elt;
672 total_elnum += el_elnum;
673 el++;
674 }
675 *elnum = total_elnum;
676 *elt = base_elt;
677 return 1;
678 }
679 default: ;
680 }
681 return 0;
682 }
683
vfp_type_p(ffi_type * t)684 static int vfp_type_p (ffi_type *t)
685 {
686 int elt, elnum;
687 if (rec_vfp_type_p (t, &elt, &elnum))
688 {
689 if (t->type == FFI_TYPE_STRUCT)
690 {
691 if (elnum == 1)
692 t->type = elt;
693 else
694 t->type = (elt == FFI_TYPE_FLOAT
695 ? FFI_TYPE_STRUCT_VFP_FLOAT
696 : FFI_TYPE_STRUCT_VFP_DOUBLE);
697 }
698 return (int) t->type;
699 }
700 return 0;
701 }
702
place_vfp_arg(ffi_cif * cif,ffi_type * t)703 static void place_vfp_arg (ffi_cif *cif, ffi_type *t)
704 {
705 int reg = cif->vfp_reg_free;
706 int nregs = t->size / sizeof (float);
707 int align = ((t->type == FFI_TYPE_STRUCT_VFP_FLOAT
708 || t->type == FFI_TYPE_FLOAT) ? 1 : 2);
709 /* Align register number. */
710 if ((reg & 1) && align == 2)
711 reg++;
712 while (reg + nregs <= 16)
713 {
714 int s, new_used = 0;
715 for (s = reg; s < reg + nregs; s++)
716 {
717 new_used |= (1 << s);
718 if (cif->vfp_used & (1 << s))
719 {
720 reg += align;
721 goto next_reg;
722 }
723 }
724 /* Found regs to allocate. */
725 cif->vfp_used |= new_used;
726 cif->vfp_args[cif->vfp_nargs++] = reg;
727
728 /* Update vfp_reg_free. */
729 if (cif->vfp_used & (1 << cif->vfp_reg_free))
730 {
731 reg += nregs;
732 while (cif->vfp_used & (1 << reg))
733 reg += 1;
734 cif->vfp_reg_free = reg;
735 }
736 return;
737 next_reg: ;
738 }
739 }
740
layout_vfp_args(ffi_cif * cif)741 static void layout_vfp_args (ffi_cif *cif)
742 {
743 int i;
744 /* Init VFP fields */
745 cif->vfp_used = 0;
746 cif->vfp_nargs = 0;
747 cif->vfp_reg_free = 0;
748 memset (cif->vfp_args, -1, 16); /* Init to -1. */
749
750 for (i = 0; i < cif->nargs; i++)
751 {
752 ffi_type *t = cif->arg_types[i];
753 if (vfp_type_p (t))
754 place_vfp_arg (cif, t);
755 }
756 }
757