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