OSDN Git Service

2009-06-04 Andrew Haley <aph@redhat.com>
[pf3gnuchains/gcc-fork.git] / libffi / src / sh64 / ffi.c
1 /* -----------------------------------------------------------------------
2    ffi.c - Copyright (c) 2003, 2004, 2006, 2007 Kaz Kojima
3            Copyright (c) 2008 Anthony Green
4    
5    SuperH SHmedia 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,
19    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21    NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25    DEALINGS IN THE SOFTWARE.
26    ----------------------------------------------------------------------- */
27
28 #include <ffi.h>
29 #include <ffi_common.h>
30
31 #include <stdlib.h>
32
33 #define NGREGARG 8
34 #define NFREGARG 12
35
36 static int
37 return_type (ffi_type *arg)
38 {
39
40   if (arg->type != FFI_TYPE_STRUCT)
41     return arg->type;
42
43   /* gcc uses r2 if the result can be packed in on register.  */
44   if (arg->size <= sizeof (UINT8))
45     return FFI_TYPE_UINT8;
46   else if (arg->size <= sizeof (UINT16))
47     return FFI_TYPE_UINT16;
48   else if (arg->size <= sizeof (UINT32))
49     return FFI_TYPE_UINT32;
50   else if (arg->size <= sizeof (UINT64))
51     return FFI_TYPE_UINT64;
52
53   return FFI_TYPE_STRUCT;
54 }
55
56 /* ffi_prep_args is called by the assembly routine once stack space
57    has been allocated for the function's arguments */
58
59 void ffi_prep_args(char *stack, extended_cif *ecif)
60 {
61   register unsigned int i;
62   register unsigned int avn;
63   register void **p_argv;
64   register char *argp;
65   register ffi_type **p_arg;
66
67   argp = stack;
68
69   if (return_type (ecif->cif->rtype) == FFI_TYPE_STRUCT)
70     {
71       *(void **) argp = ecif->rvalue;
72       argp += sizeof (UINT64);
73     }
74
75   avn = ecif->cif->nargs;
76   p_argv = ecif->avalue;
77
78   for (i = 0, p_arg = ecif->cif->arg_types; i < avn; i++, p_arg++, p_argv++)
79     {
80       size_t z;
81       int align;
82
83       z = (*p_arg)->size;
84       align = (*p_arg)->alignment;
85       if (z < sizeof (UINT32))
86         {
87           switch ((*p_arg)->type)
88             {
89             case FFI_TYPE_SINT8:
90               *(SINT64 *) argp = (SINT64) *(SINT8 *)(*p_argv);
91               break;
92   
93             case FFI_TYPE_UINT8:
94               *(UINT64 *) argp = (UINT64) *(UINT8 *)(*p_argv);
95               break;
96   
97             case FFI_TYPE_SINT16:
98               *(SINT64 *) argp = (SINT64) *(SINT16 *)(*p_argv);
99               break;
100   
101             case FFI_TYPE_UINT16:
102               *(UINT64 *) argp = (UINT64) *(UINT16 *)(*p_argv);
103               break;
104   
105             case FFI_TYPE_STRUCT:
106               memcpy (argp, *p_argv, z);
107               break;
108
109             default:
110               FFI_ASSERT(0);
111             }
112           argp += sizeof (UINT64);
113         }
114       else if (z == sizeof (UINT32) && align == sizeof (UINT32))
115         {
116           switch ((*p_arg)->type)
117             {
118             case FFI_TYPE_INT:
119             case FFI_TYPE_SINT32:
120               *(SINT64 *) argp = (SINT64) *(SINT32 *) (*p_argv);
121               break;
122
123             case FFI_TYPE_FLOAT:
124             case FFI_TYPE_POINTER:
125             case FFI_TYPE_UINT32:
126             case FFI_TYPE_STRUCT:
127               *(UINT64 *) argp = (UINT64) *(UINT32 *) (*p_argv);
128               break;
129
130             default:
131               FFI_ASSERT(0);
132               break;
133             }
134           argp += sizeof (UINT64);
135         }
136       else if (z == sizeof (UINT64)
137                && align == sizeof (UINT64)
138                && ((int) *p_argv & (sizeof (UINT64) - 1)) == 0)
139         {
140           *(UINT64 *) argp = *(UINT64 *) (*p_argv);
141           argp += sizeof (UINT64);
142         }
143       else
144         {
145           int n = (z + sizeof (UINT64) - 1) / sizeof (UINT64);
146
147           memcpy (argp, *p_argv, z);
148           argp += n * sizeof (UINT64);
149         }
150     }
151
152   return;
153 }
154
155 /* Perform machine dependent cif processing */
156 ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
157 {
158   int i, j;
159   int size, type;
160   int n, m;
161   int greg;
162   int freg;
163   int fpair = -1;
164
165   greg = (return_type (cif->rtype) == FFI_TYPE_STRUCT ? 1 : 0);
166   freg = 0;
167   cif->flags2 = 0;
168
169   for (i = j = 0; i < cif->nargs; i++)
170     {
171       type = (cif->arg_types)[i]->type;
172       switch (type)
173         {
174         case FFI_TYPE_FLOAT:
175           greg++;
176           cif->bytes += sizeof (UINT64) - sizeof (float);
177           if (freg >= NFREGARG - 1)
178             continue;
179           if (fpair < 0)
180             {
181               fpair = freg;
182               freg += 2;
183             }
184           else
185             fpair = -1;
186           cif->flags2 += ((cif->arg_types)[i]->type) << (2 * j++);
187           break;
188
189         case FFI_TYPE_DOUBLE:
190           if (greg++ >= NGREGARG && (freg + 1) >= NFREGARG)
191             continue;
192           if ((freg + 1) < NFREGARG)
193             {
194               freg += 2;
195               cif->flags2 += ((cif->arg_types)[i]->type) << (2 * j++);
196             }
197           else
198             cif->flags2 += FFI_TYPE_INT << (2 * j++);
199           break;
200               
201         default:
202           size = (cif->arg_types)[i]->size;
203           if (size < sizeof (UINT64))
204             cif->bytes += sizeof (UINT64) - size;
205           n = (size + sizeof (UINT64) - 1) / sizeof (UINT64);
206           if (greg >= NGREGARG)
207             continue;
208           else if (greg + n - 1 >= NGREGARG)
209             greg = NGREGARG;
210           else
211             greg += n;
212           for (m = 0; m < n; m++)
213             cif->flags2 += FFI_TYPE_INT << (2 * j++);
214           break;
215         }
216     }
217
218   /* Set the return type flag */
219   switch (cif->rtype->type)
220     {
221     case FFI_TYPE_STRUCT:
222       cif->flags = return_type (cif->rtype);
223       break;
224
225     case FFI_TYPE_VOID:
226     case FFI_TYPE_FLOAT:
227     case FFI_TYPE_DOUBLE:
228     case FFI_TYPE_SINT64:
229     case FFI_TYPE_UINT64:
230       cif->flags = cif->rtype->type;
231       break;
232
233     default:
234       cif->flags = FFI_TYPE_INT;
235       break;
236     }
237
238   return FFI_OK;
239 }
240
241 /*@-declundef@*/
242 /*@-exportheader@*/
243 extern void ffi_call_SYSV(void (*)(char *, extended_cif *), 
244                           /*@out@*/ extended_cif *, 
245                           unsigned, unsigned, long long,
246                           /*@out@*/ unsigned *, 
247                           void (*fn)(void));
248 /*@=declundef@*/
249 /*@=exportheader@*/
250
251 void ffi_call(/*@dependent@*/ ffi_cif *cif, 
252               void (*fn)(void), 
253               /*@out@*/ void *rvalue, 
254               /*@dependent@*/ void **avalue)
255 {
256   extended_cif ecif;
257   UINT64 trvalue;
258
259   ecif.cif = cif;
260   ecif.avalue = avalue;
261   
262   /* If the return value is a struct and we don't have a return */
263   /* value address then we need to make one                     */
264
265   if (cif->rtype->type == FFI_TYPE_STRUCT
266       && return_type (cif->rtype) != FFI_TYPE_STRUCT)
267     ecif.rvalue = &trvalue;
268   else if ((rvalue == NULL) && 
269       (cif->rtype->type == FFI_TYPE_STRUCT))
270     {
271       ecif.rvalue = alloca(cif->rtype->size);
272     }
273   else
274     ecif.rvalue = rvalue;
275
276   switch (cif->abi) 
277     {
278     case FFI_SYSV:
279       ffi_call_SYSV(ffi_prep_args, &ecif, cif->bytes, cif->flags, cif->flags2,
280                     ecif.rvalue, fn);
281       break;
282     default:
283       FFI_ASSERT(0);
284       break;
285     }
286
287   if (rvalue
288       && cif->rtype->type == FFI_TYPE_STRUCT
289       && return_type (cif->rtype) != FFI_TYPE_STRUCT)
290     memcpy (rvalue, &trvalue, cif->rtype->size);
291 }
292
293 extern void ffi_closure_SYSV (void);
294 extern void __ic_invalidate (void *line);
295
296 ffi_status
297 ffi_prep_closure_loc (ffi_closure *closure,
298                       ffi_cif *cif,
299                       void (*fun)(ffi_cif*, void*, void**, void*),
300                       void *user_data,
301                       void *codeloc)
302 {
303   unsigned int *tramp;
304
305   FFI_ASSERT (cif->abi == FFI_GCC_SYSV);
306
307   tramp = (unsigned int *) &closure->tramp[0];
308   /* Since ffi_closure is an aligned object, the ffi trampoline is
309      called as an SHcompact code.  Sigh.
310      SHcompact part:
311      mova @(1,pc),r0; add #1,r0; jmp @r0; nop;
312      SHmedia part:
313      movi fnaddr >> 16,r1; shori fnaddr,r1; ptabs/l r1,tr0
314      movi cxt >> 16,r1; shori cxt,r1; blink tr0,r63  */
315 #ifdef __LITTLE_ENDIAN__
316   tramp[0] = 0x7001c701;
317   tramp[1] = 0x0009402b;
318 #else
319   tramp[0] = 0xc7017001;
320   tramp[1] = 0x402b0009;
321 #endif
322   tramp[2] = 0xcc000010 | (((UINT32) ffi_closure_SYSV) >> 16) << 10;
323   tramp[3] = 0xc8000010 | (((UINT32) ffi_closure_SYSV) & 0xffff) << 10;
324   tramp[4] = 0x6bf10600;
325   tramp[5] = 0xcc000010 | (((UINT32) codeloc) >> 16) << 10;
326   tramp[6] = 0xc8000010 | (((UINT32) codeloc) & 0xffff) << 10;
327   tramp[7] = 0x4401fff0;
328
329   closure->cif = cif;
330   closure->fun = fun;
331   closure->user_data = user_data;
332
333   /* Flush the icache.  */
334   asm volatile ("ocbwb %0,0; synco; icbi %1,0; synci" : : "r" (tramp),
335                 "r"(codeloc));
336
337   return FFI_OK;
338 }
339
340 /* Basically the trampoline invokes ffi_closure_SYSV, and on 
341  * entry, r3 holds the address of the closure.
342  * After storing the registers that could possibly contain
343  * parameters to be passed into the stack frame and setting
344  * up space for a return value, ffi_closure_SYSV invokes the 
345  * following helper function to do most of the work.
346  */
347
348 int
349 ffi_closure_helper_SYSV (ffi_closure *closure, UINT64 *rvalue, 
350                          UINT64 *pgr, UINT64 *pfr, UINT64 *pst)
351 {
352   void **avalue;
353   ffi_type **p_arg;
354   int i, avn;
355   int greg, freg;
356   ffi_cif *cif;
357   int fpair = -1;
358
359   cif = closure->cif;
360   avalue = alloca (cif->nargs * sizeof (void *));
361
362   /* Copy the caller's structure return value address so that the closure
363      returns the data directly to the caller.  */
364   if (return_type (cif->rtype) == FFI_TYPE_STRUCT)
365     {
366       rvalue = (UINT64 *) *pgr;
367       greg = 1;
368     }
369   else
370     greg = 0;
371
372   freg = 0;
373   cif = closure->cif;
374   avn = cif->nargs;
375
376   /* Grab the addresses of the arguments from the stack frame.  */
377   for (i = 0, p_arg = cif->arg_types; i < avn; i++, p_arg++)
378     {
379       size_t z;
380       void *p;
381
382       z = (*p_arg)->size;
383       if (z < sizeof (UINT32))
384         {
385           p = pgr + greg++;
386
387           switch ((*p_arg)->type)
388             {
389             case FFI_TYPE_SINT8:
390             case FFI_TYPE_UINT8:
391             case FFI_TYPE_SINT16:
392             case FFI_TYPE_UINT16:
393             case FFI_TYPE_STRUCT:
394 #ifdef __LITTLE_ENDIAN__
395               avalue[i] = p;
396 #else
397               avalue[i] = ((char *) p) + sizeof (UINT32) - z;
398 #endif
399               break;
400
401             default:
402               FFI_ASSERT(0);
403             }
404         }
405       else if (z == sizeof (UINT32))
406         {
407           if ((*p_arg)->type == FFI_TYPE_FLOAT)
408             {
409               if (freg < NFREGARG - 1)
410                 {
411                   if (fpair >= 0)
412                     {
413                       avalue[i] = (UINT32 *) pfr + fpair;
414                       fpair = -1;
415                     }
416                   else
417                     {
418 #ifdef __LITTLE_ENDIAN__
419                       fpair = freg;
420                       avalue[i] = (UINT32 *) pfr + (1 ^ freg);
421 #else
422                       fpair = 1 ^ freg;
423                       avalue[i] = (UINT32 *) pfr + freg;
424 #endif
425                       freg += 2;
426                     }
427                 }
428               else
429 #ifdef __LITTLE_ENDIAN__
430                 avalue[i] = pgr + greg;
431 #else
432                 avalue[i] = (UINT32 *) (pgr + greg) + 1;
433 #endif
434             }
435           else
436 #ifdef __LITTLE_ENDIAN__
437             avalue[i] = pgr + greg;
438 #else
439             avalue[i] = (UINT32 *) (pgr + greg) + 1;
440 #endif
441           greg++;
442         }
443       else if ((*p_arg)->type == FFI_TYPE_DOUBLE)
444         {
445           if (freg + 1 >= NFREGARG)
446             avalue[i] = pgr + greg;
447           else
448             {
449               avalue[i] = pfr + (freg >> 1);
450               freg += 2;
451             }
452           greg++;
453         }
454       else
455         {
456           int n = (z + sizeof (UINT64) - 1) / sizeof (UINT64);
457
458           avalue[i] = pgr + greg;
459           greg += n;
460         }
461     }
462
463   (closure->fun) (cif, rvalue, avalue, closure->user_data);
464
465   /* Tell ffi_closure_SYSV how to perform return type promotions.  */
466   return return_type (cif->rtype);
467 }
468