re PR fortran/77414 (ICE in create_function_arglist, at fortran/trans-decl.c:2410)
[official-gcc.git] / libffi / src / x86 / ffiw64.c
blob8a33a6c478568f99b6f109c2e21ba5424678982a
1 /* -----------------------------------------------------------------------
2 ffiw64.c - Copyright (c) 2014 Red Hat, Inc.
4 x86 win64 Foreign Function Interface
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:
14 The above copyright notice and this permission notice shall be included
15 in all copies or substantial portions of the Software.
17 THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 DEALINGS IN THE SOFTWARE.
25 ----------------------------------------------------------------------- */
27 #include <ffi.h>
28 #include <ffi_common.h>
29 #include <stdlib.h>
30 #include <stdint.h>
32 #ifdef X86_WIN64
34 struct win64_call_frame
36 UINT64 rbp; /* 0 */
37 UINT64 retaddr; /* 8 */
38 UINT64 fn; /* 16 */
39 UINT64 flags; /* 24 */
40 UINT64 rvalue; /* 32 */
43 extern void ffi_call_win64 (void *stack, struct win64_call_frame *,
44 void *closure) FFI_HIDDEN;
46 ffi_status
47 ffi_prep_cif_machdep (ffi_cif *cif)
49 int flags, n;
51 if (cif->abi != FFI_WIN64)
52 return FFI_BAD_ABI;
54 flags = cif->rtype->type;
55 switch (flags)
57 default:
58 break;
59 case FFI_TYPE_LONGDOUBLE:
60 flags = FFI_TYPE_STRUCT;
61 break;
62 case FFI_TYPE_COMPLEX:
63 flags = FFI_TYPE_STRUCT;
64 /* FALLTHRU */
65 case FFI_TYPE_STRUCT:
66 switch (cif->rtype->size)
68 case 8:
69 flags = FFI_TYPE_UINT64;
70 break;
71 case 4:
72 flags = FFI_TYPE_SMALL_STRUCT_4B;
73 break;
74 case 2:
75 flags = FFI_TYPE_SMALL_STRUCT_2B;
76 break;
77 case 1:
78 flags = FFI_TYPE_SMALL_STRUCT_1B;
79 break;
81 break;
83 cif->flags = flags;
85 /* Each argument either fits in a register, an 8 byte slot, or is
86 passed by reference with the pointer in the 8 byte slot. */
87 n = cif->nargs;
88 n += (flags == FFI_TYPE_STRUCT);
89 if (n < 4)
90 n = 4;
91 cif->bytes = n * 8;
93 return FFI_OK;
96 static void
97 ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue,
98 void **avalue, void *closure)
100 int i, j, n, flags;
101 UINT64 *stack;
102 size_t rsize;
103 struct win64_call_frame *frame;
105 FFI_ASSERT(cif->abi == FFI_WIN64);
107 flags = cif->flags;
108 rsize = 0;
110 /* If we have no return value for a structure, we need to create one.
111 Otherwise we can ignore the return type entirely. */
112 if (rvalue == NULL)
114 if (flags == FFI_TYPE_STRUCT)
115 rsize = cif->rtype->size;
116 else
117 flags = FFI_TYPE_VOID;
120 stack = alloca(cif->bytes + sizeof(struct win64_call_frame) + rsize);
121 frame = (struct win64_call_frame *)((char *)stack + cif->bytes);
122 if (rsize)
123 rvalue = frame + 1;
125 frame->fn = (uintptr_t)fn;
126 frame->flags = flags;
127 frame->rvalue = (uintptr_t)rvalue;
129 j = 0;
130 if (flags == FFI_TYPE_STRUCT)
132 stack[0] = (uintptr_t)rvalue;
133 j = 1;
136 for (i = 0, n = cif->nargs; i < n; ++i, ++j)
138 switch (cif->arg_types[i]->size)
140 case 8:
141 stack[j] = *(UINT64 *)avalue[i];
142 break;
143 case 4:
144 stack[j] = *(UINT32 *)avalue[i];
145 break;
146 case 2:
147 stack[j] = *(UINT16 *)avalue[i];
148 break;
149 case 1:
150 stack[j] = *(UINT8 *)avalue[i];
151 break;
152 default:
153 stack[j] = (uintptr_t)avalue[i];
154 break;
158 ffi_call_win64 (stack, frame, closure);
161 void
162 ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
164 ffi_call_int (cif, fn, rvalue, avalue, NULL);
167 void
168 ffi_call_go (ffi_cif *cif, void (*fn)(void), void *rvalue,
169 void **avalue, void *closure)
171 ffi_call_int (cif, fn, rvalue, avalue, closure);
175 extern void ffi_closure_win64(void) FFI_HIDDEN;
176 extern void ffi_go_closure_win64(void) FFI_HIDDEN;
178 ffi_status
179 ffi_prep_closure_loc (ffi_closure* closure,
180 ffi_cif* cif,
181 void (*fun)(ffi_cif*, void*, void**, void*),
182 void *user_data,
183 void *codeloc)
185 static const unsigned char trampoline[16] = {
186 /* leaq -0x7(%rip),%r10 # 0x0 */
187 0x4c, 0x8d, 0x15, 0xf9, 0xff, 0xff, 0xff,
188 /* jmpq *0x3(%rip) # 0x10 */
189 0xff, 0x25, 0x03, 0x00, 0x00, 0x00,
190 /* nopl (%rax) */
191 0x0f, 0x1f, 0x00
193 unsigned char *tramp = closure->tramp;
195 if (cif->abi != FFI_WIN64)
196 return FFI_BAD_ABI;
198 memcpy (tramp, trampoline, sizeof(trampoline));
199 *(UINT64 *)(tramp + 16) = (uintptr_t)ffi_closure_win64;
201 closure->cif = cif;
202 closure->fun = fun;
203 closure->user_data = user_data;
205 return FFI_OK;
208 ffi_status
209 ffi_prep_go_closure (ffi_go_closure* closure, ffi_cif* cif,
210 void (*fun)(ffi_cif*, void*, void**, void*))
212 if (cif->abi != FFI_WIN64)
213 return FFI_BAD_ABI;
215 closure->tramp = ffi_go_closure_win64;
216 closure->cif = cif;
217 closure->fun = fun;
219 return FFI_OK;
222 struct win64_closure_frame
224 UINT64 rvalue[2];
225 UINT64 fargs[4];
226 UINT64 retaddr;
227 UINT64 args[];
230 int FFI_HIDDEN
231 ffi_closure_win64_inner(ffi_cif *cif,
232 void (*fun)(ffi_cif*, void*, void**, void*),
233 void *user_data,
234 struct win64_closure_frame *frame)
236 void **avalue;
237 void *rvalue;
238 int i, n, nreg, flags;
240 avalue = alloca(cif->nargs * sizeof(void *));
241 rvalue = frame->rvalue;
242 nreg = 0;
244 /* When returning a structure, the address is in the first argument.
245 We must also be prepared to return the same address in eax, so
246 install that address in the frame and pretend we return a pointer. */
247 flags = cif->flags;
248 if (flags == FFI_TYPE_STRUCT)
250 rvalue = (void *)(uintptr_t)frame->args[0];
251 frame->rvalue[0] = frame->args[0];
252 nreg = 1;
255 for (i = 0, n = cif->nargs; i < n; ++i, ++nreg)
257 size_t size = cif->arg_types[i]->size;
258 size_t type = cif->arg_types[i]->type;
259 void *a;
261 if (type == FFI_TYPE_DOUBLE || type == FFI_TYPE_FLOAT)
263 if (nreg < 4)
264 a = &frame->fargs[nreg];
265 else
266 a = &frame->args[nreg];
268 else if (size == 1 || size == 2 || size == 4 || size == 8)
269 a = &frame->args[nreg];
270 else
271 a = (void *)(uintptr_t)frame->args[nreg];
273 avalue[i] = a;
276 /* Invoke the closure. */
277 fun (cif, rvalue, avalue, user_data);
278 return flags;
281 #endif /* X86_WIN64 */