OSDN Git Service

* src/alpha/ffi.c (ffi_prep_closure): Avoid gas-only mnemonic.
[pf3gnuchains/gcc-fork.git] / libffi / src / alpha / ffi.c
1 /* -----------------------------------------------------------------------
2    ffi.c - Copyright (c) 1998, 2001 Cygnus Solutions
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*SIZEOF_ARG)
41     cif->bytes = 6*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*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, SIZEOF_ARG) / 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 (ffi_closure* closure,
150                   ffi_cif* cif,
151                   void (*fun)(ffi_cif*, void*, void**, void*),
152                   void *user_data)
153 {
154   unsigned int *tramp;
155
156   FFI_ASSERT (cif->abi == FFI_OSF);
157
158   tramp = (unsigned int *) &closure->tramp[0];
159   tramp[0] = 0x47fb0401;        /* mov $27,$1           */
160   tramp[1] = 0xa77b0010;        /* ldq $27,16($27)      */
161   tramp[2] = 0x6bfb0000;        /* jmp $31,($27),0      */
162   tramp[3] = 0x47ff041f;        /* nop                  */
163   *(void **) &tramp[4] = ffi_closure_osf;
164
165   closure->cif = cif;
166   closure->fun = fun;
167   closure->user_data = user_data;
168
169   /* Flush the Icache.
170
171      Tru64 UNIX as doesn't understand the imb mnemonic, so use call_pal
172      instead, since both Compaq as and gas can handle it.
173
174      0x86 is PAL_imb in Tru64 UNIX <alpha/pal.h>.  */
175   asm volatile ("call_pal 0x86" : : : "memory");
176
177   return FFI_OK;
178 }
179
180 int
181 ffi_closure_osf_inner(ffi_closure *closure, void *rvalue, unsigned long *argp)
182 {
183   ffi_cif *cif;
184   void **avalue;
185   ffi_type **arg_types;
186   long i, avn, argn;
187
188   cif = closure->cif;
189   avalue = alloca(cif->nargs * sizeof(void *));
190
191   argn = 0;
192
193   /* Copy the caller's structure return address to that the closure
194      returns the data directly to the caller.  */
195   if (cif->flags == FFI_TYPE_STRUCT)
196     {
197       rvalue = (void *) argp[0];
198       argn = 1;
199     }
200
201   i = 0;
202   avn = cif->nargs;
203   arg_types = cif->arg_types;
204   
205   /* Grab the addresses of the arguments from the stack frame.  */
206   while (i < avn)
207     {
208       switch (arg_types[i]->type)
209         {
210         case FFI_TYPE_SINT8:
211         case FFI_TYPE_UINT8:
212         case FFI_TYPE_SINT16:
213         case FFI_TYPE_UINT16:
214         case FFI_TYPE_SINT32:
215         case FFI_TYPE_UINT32:
216         case FFI_TYPE_SINT64:
217         case FFI_TYPE_UINT64:
218         case FFI_TYPE_POINTER:
219         case FFI_TYPE_STRUCT:
220           avalue[i] = &argp[argn];
221           break;
222
223         case FFI_TYPE_FLOAT:
224           if (argn < 6)
225             {
226               /* Floats coming from registers need conversion from double
227                  back to float format.  */
228               *(float *)&argp[argn - 6] = *(double *)&argp[argn - 6];
229               avalue[i] = &argp[argn - 6];
230             }
231           else
232             avalue[i] = &argp[argn];
233           break;
234
235         case FFI_TYPE_DOUBLE:
236           avalue[i] = &argp[argn - (argn < 6 ? 6 : 0)];
237           break;
238
239         default:
240           FFI_ASSERT(0);
241         }
242
243       argn += ALIGN(arg_types[i]->size, SIZEOF_ARG) / SIZEOF_ARG;
244       i++;
245     }
246
247   /* Invoke the closure.  */
248   (closure->fun) (cif, rvalue, avalue, closure->user_data);
249
250   /* Tell ffi_closure_osf how to perform return type promotions.  */
251   return cif->rtype->type;
252 }