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