OSDN Git Service

d139423f90e56ca7352e35900aebfaca3732d106
[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
29 #include <stdlib.h>
30
31 extern void ffi_call_osf(void *, unsigned long, unsigned, void *, void (*)());
32 extern void ffi_closure_osf(void);
33
34
35 ffi_status
36 ffi_prep_cif_machdep(ffi_cif *cif)
37 {
38   /* Adjust cif->bytes to represent a minimum 6 words for the temporary
39      register argument loading area.  */
40   if (cif->bytes < 6*FFI_SIZEOF_ARG)
41     cif->bytes = 6*FFI_SIZEOF_ARG;
42
43   /* Set the return type flag */
44   switch (cif->rtype->type)
45     {
46     case FFI_TYPE_STRUCT:
47     case FFI_TYPE_FLOAT:
48     case FFI_TYPE_DOUBLE:
49       cif->flags = cif->rtype->type;
50       break;
51
52     default:
53       cif->flags = FFI_TYPE_INT;
54       break;
55     }
56   
57   return FFI_OK;
58 }
59
60 void
61 ffi_call(ffi_cif *cif, void (*fn)(), void *rvalue, void **avalue)
62 {
63   unsigned long *stack, *argp;
64   long i, avn;
65   ffi_type **arg_types;
66   
67   FFI_ASSERT (cif->abi == FFI_OSF);
68
69   /* If the return value is a struct and we don't have a return
70      value address then we need to make one.  */
71   if (rvalue == NULL && cif->flags == FFI_TYPE_STRUCT)
72     rvalue = alloca(cif->rtype->size);
73
74   /* Allocate the space for the arguments, plus 4 words of temp
75      space for ffi_call_osf.  */
76   argp = stack = alloca(cif->bytes + 4*FFI_SIZEOF_ARG);
77
78   if (cif->flags == FFI_TYPE_STRUCT)
79     *(void **) argp++ = rvalue;
80
81   i = 0;
82   avn = cif->nargs;
83   arg_types = cif->arg_types;
84
85   while (i < avn)
86     {
87       switch ((*arg_types)->type)
88         {
89         case FFI_TYPE_SINT8:
90           *(SINT64 *) argp = *(SINT8 *)(* avalue);
91           break;
92                   
93         case FFI_TYPE_UINT8:
94           *(SINT64 *) argp = *(UINT8 *)(* avalue);
95           break;
96                   
97         case FFI_TYPE_SINT16:
98           *(SINT64 *) argp = *(SINT16 *)(* avalue);
99           break;
100                   
101         case FFI_TYPE_UINT16:
102           *(SINT64 *) argp = *(UINT16 *)(* avalue);
103           break;
104                   
105         case FFI_TYPE_SINT32:
106         case FFI_TYPE_UINT32:
107           /* Note that unsigned 32-bit quantities are sign extended.  */
108           *(SINT64 *) argp = *(SINT32 *)(* avalue);
109           break;
110                   
111         case FFI_TYPE_SINT64:
112         case FFI_TYPE_UINT64:
113         case FFI_TYPE_POINTER:
114           *(UINT64 *) argp = *(UINT64 *)(* avalue);
115           break;
116
117         case FFI_TYPE_FLOAT:
118           if (argp - stack < 6)
119             {
120               /* Note the conversion -- all the fp regs are loaded as
121                  doubles.  The in-register format is the same.  */
122               *(double *) argp = *(float *)(* avalue);
123             }
124           else
125             *(float *) argp = *(float *)(* avalue);
126           break;
127
128         case FFI_TYPE_DOUBLE:
129           *(double *) argp = *(double *)(* avalue);
130           break;
131
132         case FFI_TYPE_STRUCT:
133           memcpy(argp, *avalue, (*arg_types)->size);
134           break;
135
136         default:
137           FFI_ASSERT(0);
138         }
139
140       argp += ALIGN((*arg_types)->size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
141       i++, arg_types++, avalue++;
142     }
143
144   ffi_call_osf(stack, cif->bytes, cif->flags, rvalue, fn);
145 }
146
147
148 ffi_status
149 ffi_prep_closure_loc (ffi_closure* closure,
150                       ffi_cif* cif,
151                       void (*fun)(ffi_cif*, void*, void**, void*),
152                       void *user_data,
153                       void *codeloc)
154 {
155   unsigned int *tramp;
156
157   FFI_ASSERT (cif->abi == FFI_OSF);
158
159   tramp = (unsigned int *) &closure->tramp[0];
160   tramp[0] = 0x47fb0401;        /* mov $27,$1           */
161   tramp[1] = 0xa77b0010;        /* ldq $27,16($27)      */
162   tramp[2] = 0x6bfb0000;        /* jmp $31,($27),0      */
163   tramp[3] = 0x47ff041f;        /* nop                  */
164   *(void **) &tramp[4] = ffi_closure_osf;
165
166   closure->cif = cif;
167   closure->fun = fun;
168   closure->user_data = user_data;
169
170   /* Flush the Icache.
171
172      Tru64 UNIX as doesn't understand the imb mnemonic, so use call_pal
173      instead, since both Compaq as and gas can handle it.
174
175      0x86 is PAL_imb in Tru64 UNIX <alpha/pal.h>.  */
176   asm volatile ("call_pal 0x86" : : : "memory");
177
178   return FFI_OK;
179 }
180
181 int
182 ffi_closure_osf_inner(ffi_closure *closure, void *rvalue, unsigned long *argp)
183 {
184   ffi_cif *cif;
185   void **avalue;
186   ffi_type **arg_types;
187   long i, avn, argn;
188
189   cif = closure->cif;
190   avalue = alloca(cif->nargs * sizeof(void *));
191
192   argn = 0;
193
194   /* Copy the caller's structure return address to that the closure
195      returns the data directly to the caller.  */
196   if (cif->flags == FFI_TYPE_STRUCT)
197     {
198       rvalue = (void *) argp[0];
199       argn = 1;
200     }
201
202   i = 0;
203   avn = cif->nargs;
204   arg_types = cif->arg_types;
205   
206   /* Grab the addresses of the arguments from the stack frame.  */
207   while (i < avn)
208     {
209       switch (arg_types[i]->type)
210         {
211         case FFI_TYPE_SINT8:
212         case FFI_TYPE_UINT8:
213         case FFI_TYPE_SINT16:
214         case FFI_TYPE_UINT16:
215         case FFI_TYPE_SINT32:
216         case FFI_TYPE_UINT32:
217         case FFI_TYPE_SINT64:
218         case FFI_TYPE_UINT64:
219         case FFI_TYPE_POINTER:
220         case FFI_TYPE_STRUCT:
221           avalue[i] = &argp[argn];
222           break;
223
224         case FFI_TYPE_FLOAT:
225           if (argn < 6)
226             {
227               /* Floats coming from registers need conversion from double
228                  back to float format.  */
229               *(float *)&argp[argn - 6] = *(double *)&argp[argn - 6];
230               avalue[i] = &argp[argn - 6];
231             }
232           else
233             avalue[i] = &argp[argn];
234           break;
235
236         case FFI_TYPE_DOUBLE:
237           avalue[i] = &argp[argn - (argn < 6 ? 6 : 0)];
238           break;
239
240         default:
241           FFI_ASSERT(0);
242         }
243
244       argn += ALIGN(arg_types[i]->size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
245       i++;
246     }
247
248   /* Invoke the closure.  */
249   (closure->fun) (cif, rvalue, avalue, closure->user_data);
250
251   /* Tell ffi_closure_osf how to perform return type promotions.  */
252   return cif->rtype->type;
253 }