OSDN Git Service

cfa7b69e839540f6f2c7f628b8fe48777c1662b2
[pf3gnuchains/gcc-fork.git] / libffi / src / alpha / ffi.c
1 /* -----------------------------------------------------------------------
2    ffi.c - Copyright (c) 1998, 2001, 2007 Red Hat, Inc.
3    
4    Alpha Foreign Function Interface 
5
6    Permission is hereby granted, free of charge, to any person obtaining
7    a copy of this software and associated documentation files (the
8    ``Software''), to deal in the Software without restriction, including
9    without limitation the rights to use, copy, modify, merge, publish,
10    distribute, sublicense, and/or sell copies of the Software, and to
11    permit persons to whom the Software is furnished to do so, subject to
12    the following conditions:
13
14    The above copyright notice and this permission notice shall be included
15    in all copies or substantial portions of the Software.
16
17    THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
18    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20    IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21    OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22    ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23    OTHER DEALINGS IN THE SOFTWARE.
24    ----------------------------------------------------------------------- */
25
26 #include <ffi.h>
27 #include <ffi_common.h>
28 #include <stdlib.h>
29
30 /* Force FFI_TYPE_LONGDOUBLE to be different than FFI_TYPE_DOUBLE;
31    all further uses in this file will refer to the 128-bit type.  */
32 #if defined(__LONG_DOUBLE_128__)
33 # if FFI_TYPE_LONGDOUBLE != 4
34 #  error FFI_TYPE_LONGDOUBLE out of date
35 # endif
36 #else
37 # undef FFI_TYPE_LONGDOUBLE
38 # define FFI_TYPE_LONGDOUBLE 4
39 #endif
40
41 extern void ffi_call_osf(void *, unsigned long, unsigned, void *, void (*)())
42   FFI_HIDDEN;
43 extern void ffi_closure_osf(void) FFI_HIDDEN;
44
45
46 ffi_status
47 ffi_prep_cif_machdep(ffi_cif *cif)
48 {
49   /* Adjust cif->bytes to represent a minimum 6 words for the temporary
50      register argument loading area.  */
51   if (cif->bytes < 6*FFI_SIZEOF_ARG)
52     cif->bytes = 6*FFI_SIZEOF_ARG;
53
54   /* Set the return type flag */
55   switch (cif->rtype->type)
56     {
57     case FFI_TYPE_STRUCT:
58     case FFI_TYPE_FLOAT:
59     case FFI_TYPE_DOUBLE:
60       cif->flags = cif->rtype->type;
61       break;
62
63     case FFI_TYPE_LONGDOUBLE:
64       /* 128-bit long double is returned in memory, like a struct.  */
65       cif->flags = FFI_TYPE_STRUCT;
66       break;
67
68     default:
69       cif->flags = FFI_TYPE_INT;
70       break;
71     }
72   
73   return FFI_OK;
74 }
75
76
77 void
78 ffi_call(ffi_cif *cif, void (*fn)(), void *rvalue, void **avalue)
79 {
80   unsigned long *stack, *argp;
81   long i, avn;
82   ffi_type **arg_types;
83   
84   /* If the return value is a struct and we don't have a return
85      value address then we need to make one.  */
86   if (rvalue == NULL && cif->flags == FFI_TYPE_STRUCT)
87     rvalue = alloca(cif->rtype->size);
88
89   /* Allocate the space for the arguments, plus 4 words of temp
90      space for ffi_call_osf.  */
91   argp = stack = alloca(cif->bytes + 4*FFI_SIZEOF_ARG);
92
93   if (cif->flags == FFI_TYPE_STRUCT)
94     *(void **) argp++ = rvalue;
95
96   i = 0;
97   avn = cif->nargs;
98   arg_types = cif->arg_types;
99
100   while (i < avn)
101     {
102       size_t size = (*arg_types)->size;
103
104       switch ((*arg_types)->type)
105         {
106         case FFI_TYPE_SINT8:
107           *(SINT64 *) argp = *(SINT8 *)(* avalue);
108           break;
109                   
110         case FFI_TYPE_UINT8:
111           *(SINT64 *) argp = *(UINT8 *)(* avalue);
112           break;
113                   
114         case FFI_TYPE_SINT16:
115           *(SINT64 *) argp = *(SINT16 *)(* avalue);
116           break;
117                   
118         case FFI_TYPE_UINT16:
119           *(SINT64 *) argp = *(UINT16 *)(* avalue);
120           break;
121                   
122         case FFI_TYPE_SINT32:
123         case FFI_TYPE_UINT32:
124           /* Note that unsigned 32-bit quantities are sign extended.  */
125           *(SINT64 *) argp = *(SINT32 *)(* avalue);
126           break;
127                   
128         case FFI_TYPE_SINT64:
129         case FFI_TYPE_UINT64:
130         case FFI_TYPE_POINTER:
131           *(UINT64 *) argp = *(UINT64 *)(* avalue);
132           break;
133
134         case FFI_TYPE_FLOAT:
135           if (argp - stack < 6)
136             {
137               /* Note the conversion -- all the fp regs are loaded as
138                  doubles.  The in-register format is the same.  */
139               *(double *) argp = *(float *)(* avalue);
140             }
141           else
142             *(float *) argp = *(float *)(* avalue);
143           break;
144
145         case FFI_TYPE_DOUBLE:
146           *(double *) argp = *(double *)(* avalue);
147           break;
148
149         case FFI_TYPE_LONGDOUBLE:
150           /* 128-bit long double is passed by reference.  */
151           *(long double **) argp = (long double *)(* avalue);
152           size = sizeof (long double *);
153           break;
154
155         case FFI_TYPE_STRUCT:
156           memcpy(argp, *avalue, (*arg_types)->size);
157           break;
158
159         default:
160           FFI_ASSERT(0);
161         }
162
163       argp += ALIGN(size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
164       i++, arg_types++, avalue++;
165     }
166
167   ffi_call_osf(stack, cif->bytes, cif->flags, rvalue, fn);
168 }
169
170
171 ffi_status
172 ffi_prep_closure_loc (ffi_closure* closure,
173                       ffi_cif* cif,
174                       void (*fun)(ffi_cif*, void*, void**, void*),
175                       void *user_data,
176                       void *codeloc)
177 {
178   unsigned int *tramp;
179
180   tramp = (unsigned int *) &closure->tramp[0];
181   tramp[0] = 0x47fb0401;        /* mov $27,$1           */
182   tramp[1] = 0xa77b0010;        /* ldq $27,16($27)      */
183   tramp[2] = 0x6bfb0000;        /* jmp $31,($27),0      */
184   tramp[3] = 0x47ff041f;        /* nop                  */
185   *(void **) &tramp[4] = ffi_closure_osf;
186
187   closure->cif = cif;
188   closure->fun = fun;
189   closure->user_data = user_data;
190
191   /* Flush the Icache.
192
193      Tru64 UNIX as doesn't understand the imb mnemonic, so use call_pal
194      instead, since both Compaq as and gas can handle it.
195
196      0x86 is PAL_imb in Tru64 UNIX <alpha/pal.h>.  */
197   asm volatile ("call_pal 0x86" : : : "memory");
198
199   return FFI_OK;
200 }
201
202
203 long FFI_HIDDEN
204 ffi_closure_osf_inner(ffi_closure *closure, void *rvalue, unsigned long *argp)
205 {
206   ffi_cif *cif;
207   void **avalue;
208   ffi_type **arg_types;
209   long i, avn, argn;
210
211   cif = closure->cif;
212   avalue = alloca(cif->nargs * sizeof(void *));
213
214   argn = 0;
215
216   /* Copy the caller's structure return address to that the closure
217      returns the data directly to the caller.  */
218   if (cif->flags == FFI_TYPE_STRUCT)
219     {
220       rvalue = (void *) argp[0];
221       argn = 1;
222     }
223
224   i = 0;
225   avn = cif->nargs;
226   arg_types = cif->arg_types;
227   
228   /* Grab the addresses of the arguments from the stack frame.  */
229   while (i < avn)
230     {
231       size_t size = arg_types[i]->size;
232
233       switch (arg_types[i]->type)
234         {
235         case FFI_TYPE_SINT8:
236         case FFI_TYPE_UINT8:
237         case FFI_TYPE_SINT16:
238         case FFI_TYPE_UINT16:
239         case FFI_TYPE_SINT32:
240         case FFI_TYPE_UINT32:
241         case FFI_TYPE_SINT64:
242         case FFI_TYPE_UINT64:
243         case FFI_TYPE_POINTER:
244         case FFI_TYPE_STRUCT:
245           avalue[i] = &argp[argn];
246           break;
247
248         case FFI_TYPE_FLOAT:
249           if (argn < 6)
250             {
251               /* Floats coming from registers need conversion from double
252                  back to float format.  */
253               *(float *)&argp[argn - 6] = *(double *)&argp[argn - 6];
254               avalue[i] = &argp[argn - 6];
255             }
256           else
257             avalue[i] = &argp[argn];
258           break;
259
260         case FFI_TYPE_DOUBLE:
261           avalue[i] = &argp[argn - (argn < 6 ? 6 : 0)];
262           break;
263
264         case FFI_TYPE_LONGDOUBLE:
265           /* 128-bit long double is passed by reference.  */
266           avalue[i] = (long double *) argp[argn];
267           size = sizeof (long double *);
268           break;
269
270         default:
271           abort ();
272         }
273
274       argn += ALIGN(size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
275       i++;
276     }
277
278   /* Invoke the closure.  */
279   closure->fun (cif, rvalue, avalue, closure->user_data);
280
281   /* Tell ffi_closure_osf how to perform return type promotions.  */
282   return cif->rtype->type;
283 }