x86_64: Add support for complex types
[libffi.git] / src / x86 / unix64.S
1 /* -----------------------------------------------------------------------
2    unix64.S - Copyright (c) 2013  The Written Word, Inc.
3             - Copyright (c) 2008  Red Hat, Inc
4             - Copyright (c) 2002  Bo Thorsen <bo@suse.de>
5
6    x86-64 Foreign Function Interface 
7
8    Permission is hereby granted, free of charge, to any person obtaining
9    a copy of this software and associated documentation files (the
10    ``Software''), to deal in the Software without restriction, including
11    without limitation the rights to use, copy, modify, merge, publish,
12    distribute, sublicense, and/or sell copies of the Software, and to
13    permit persons to whom the Software is furnished to do so, subject to
14    the following conditions:
15
16    The above copyright notice and this permission notice shall be included
17    in all copies or substantial portions of the Software.
18
19    THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
20    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22    NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26    DEALINGS IN THE SOFTWARE.
27    ----------------------------------------------------------------------- */
28
29 #ifdef __x86_64__
30 #define LIBFFI_ASM      
31 #include <fficonfig.h>
32 #include <ffi.h>
33 #include <ffi_cfi.h>
34 #include "internal64.h"
35
36         .text
37
38 .macro E index
39         .align  8
40         .org    0b + \index * 8, 0x90
41 .endm
42
43 /* ffi_call_unix64 (void *args, unsigned long bytes, unsigned flags,
44                     void *raddr, void (*fnaddr)(void));
45
46    Bit o trickiness here -- ARGS+BYTES is the base of the stack frame
47    for this function.  This has been allocated by ffi_call.  We also
48    deallocate some of the stack that has been alloca'd.  */
49
50         .align  8
51         .globl  ffi_call_unix64
52         .type   ffi_call_unix64,@function
53         FFI_HIDDEN(ffi_call_unix64)
54
55 ffi_call_unix64:
56         cfi_startproc
57         movq    (%rsp), %r10            /* Load return address.  */
58         leaq    (%rdi, %rsi), %rax      /* Find local stack base.  */
59         movq    %rdx, (%rax)            /* Save flags.  */
60         movq    %rcx, 8(%rax)           /* Save raddr.  */
61         movq    %rbp, 16(%rax)          /* Save old frame pointer.  */
62         movq    %r10, 24(%rax)          /* Relocate return address.  */
63         movq    %rax, %rbp              /* Finalize local stack frame.  */
64
65         /* New stack frame based off rbp.  This is a itty bit of unwind
66            trickery in that the CFA *has* changed.  There is no easy way
67            to describe it correctly on entry to the function.  Fortunately,
68            it doesn't matter too much since at all points we can correctly
69            unwind back to ffi_call.  Note that the location to which we
70            moved the return address is (the new) CFA-8, so from the
71            perspective of the unwind info, it hasn't moved.  */
72         cfi_def_cfa(%rbp, 32)
73         cfi_rel_offset(%rbp, 16)
74
75         movq    %rdi, %r10              /* Save a copy of the register area. */
76         movq    %r8, %r11               /* Save a copy of the target fn.  */
77         movl    %r9d, %eax              /* Set number of SSE registers.  */
78
79         /* Load up all argument registers.  */
80         movq    (%r10), %rdi
81         movq    0x08(%r10), %rsi
82         movq    0x10(%r10), %rdx
83         movq    0x18(%r10), %rcx
84         movq    0x20(%r10), %r8
85         movq    0x28(%r10), %r9
86         movl    0xb0(%r10), %eax
87         testl   %eax, %eax
88         jnz     .Lload_sse
89 .Lret_from_load_sse:
90
91         /* Deallocate the reg arg area, except for r10, then load via pop.  */
92         leaq    0xb8(%r10), %rsp
93         popq    %r10
94
95         /* Call the user function.  */
96         call    *%r11
97
98         /* Deallocate stack arg area; local stack frame in redzone.  */
99         leaq    24(%rbp), %rsp
100
101         movq    0(%rbp), %rcx           /* Reload flags.  */
102         movq    8(%rbp), %rdi           /* Reload raddr.  */
103         movq    16(%rbp), %rbp          /* Reload old frame pointer.  */
104         cfi_remember_state
105         cfi_def_cfa(%rsp, 8)
106         cfi_restore(%rbp)
107
108         /* The first byte of the flags contains the FFI_TYPE.  */
109         cmpb    $UNIX64_RET_LAST, %cl
110         movzbl  %cl, %r10d
111         leaq    0f(%rip), %r11
112         ja      9f
113         leaq    (%r11, %r10, 8), %r10
114
115         /* Prep for the structure cases: scratch area in redzone.  */
116         leaq    -20(%rsp), %rsi
117         jmp     *%r10
118
119         .align  8
120 0:
121 E UNIX64_RET_VOID
122         ret
123 E UNIX64_RET_UINT8
124         movzbl  %al, %eax
125         movq    %rax, (%rdi)
126         ret
127 E UNIX64_RET_UINT16
128         movzwl  %ax, %eax
129         movq    %rax, (%rdi)
130         ret
131 E UNIX64_RET_UINT32
132         movl    %eax, %eax
133         movq    %rax, (%rdi)
134         ret
135 E UNIX64_RET_SINT8
136         movsbq  %al, %rax
137         movq    %rax, (%rdi)
138         ret
139 E UNIX64_RET_SINT16
140         movswq  %ax, %rax
141         movq    %rax, (%rdi)
142         ret
143 E UNIX64_RET_SINT32
144         cltq
145         movq    %rax, (%rdi)
146         ret
147 E UNIX64_RET_INT64
148         movq    %rax, (%rdi)
149         ret
150 E UNIX64_RET_XMM32
151         movd    %xmm0, (%rdi)
152         ret
153 E UNIX64_RET_XMM64
154         movq    %xmm0, (%rdi)
155         ret
156 E UNIX64_RET_X87
157         fstpt   (%rdi)
158         ret
159 E UNIX64_RET_X87_2
160         fstpt   (%rdi)
161         fstpt   16(%rdi)
162         ret
163 E UNIX64_RET_ST_XMM0_RAX
164         movq    %rax, 8(%rsi)
165         jmp     3f
166 E UNIX64_RET_ST_RAX_XMM0
167         movq    %xmm0, 8(%rsi)
168         jmp     2f
169 E UNIX64_RET_ST_XMM0_XMM1
170         movq    %xmm1, 8(%rsi)
171         jmp     3f
172 E UNIX64_RET_ST_RAX_RDX
173         movq    %rdx, 8(%rsi)
174 2:      movq    %rax, (%rsi)
175         shrl    $UNIX64_SIZE_SHIFT, %ecx
176         rep movsb
177         ret
178         .align 8
179 3:      movq    %xmm0, (%rsi)
180         shrl    $UNIX64_SIZE_SHIFT, %ecx
181         rep movsb
182         ret
183
184 9:      call    abort@PLT
185
186         /* Many times we can avoid loading any SSE registers at all.
187            It's not worth an indirect jump to load the exact set of
188            SSE registers needed; zero or all is a good compromise.  */
189         .align 2
190         cfi_restore_state
191 .Lload_sse:
192         movdqa  0x30(%r10), %xmm0
193         movdqa  0x40(%r10), %xmm1
194         movdqa  0x50(%r10), %xmm2
195         movdqa  0x60(%r10), %xmm3
196         movdqa  0x70(%r10), %xmm4
197         movdqa  0x80(%r10), %xmm5
198         movdqa  0x90(%r10), %xmm6
199         movdqa  0xa0(%r10), %xmm7
200         jmp     .Lret_from_load_sse
201
202         cfi_endproc
203         .size    ffi_call_unix64,.-ffi_call_unix64
204
205 /* 6 general registers, 8 vector registers,
206    32 bytes of rvalue, 8 bytes of alignment.  */
207 #define ffi_closure_OFS_G       0
208 #define ffi_closure_OFS_V       (6*8)
209 #define ffi_closure_OFS_RVALUE  (ffi_closure_OFS_V + 8*16)
210 #define ffi_closure_FS          (ffi_closure_OFS_RVALUE + 32 + 8)
211
212 /* The location of rvalue within the red zone after deallocating the frame.  */
213 #define ffi_closure_RED_RVALUE  (ffi_closure_OFS_RVALUE - ffi_closure_FS)
214
215         .align  2
216         .globl  ffi_closure_unix64_sse
217         .type   ffi_closure_unix64_sse,@function
218         FFI_HIDDEN(ffi_closure_unix64_sse)
219
220 ffi_closure_unix64_sse:
221         cfi_startproc
222         subq    $ffi_closure_FS, %rsp
223         cfi_adjust_cfa_offset(ffi_closure_FS)
224
225         movdqa  %xmm0, ffi_closure_OFS_V+0x00(%rsp)
226         movdqa  %xmm1, ffi_closure_OFS_V+0x10(%rsp)
227         movdqa  %xmm2, ffi_closure_OFS_V+0x20(%rsp)
228         movdqa  %xmm3, ffi_closure_OFS_V+0x30(%rsp)
229         movdqa  %xmm4, ffi_closure_OFS_V+0x40(%rsp)
230         movdqa  %xmm5, ffi_closure_OFS_V+0x50(%rsp)
231         movdqa  %xmm6, ffi_closure_OFS_V+0x60(%rsp)
232         movdqa  %xmm7, ffi_closure_OFS_V+0x70(%rsp)
233         jmp     0f
234
235         cfi_endproc
236         .size   ffi_closure_unix64_sse,.-ffi_closure_unix64_sse
237
238         .align  2
239         .globl  ffi_closure_unix64
240         .type   ffi_closure_unix64,@function
241         FFI_HIDDEN(ffi_closure_unix64)
242
243 ffi_closure_unix64:
244         cfi_startproc
245         subq    $ffi_closure_FS, %rsp
246         cfi_adjust_cfa_offset(ffi_closure_FS)
247 0:
248         movq    %rdi, ffi_closure_OFS_G+0x00(%rsp)
249         movq    %rsi, ffi_closure_OFS_G+0x08(%rsp)
250         movq    %rdx, ffi_closure_OFS_G+0x10(%rsp)
251         movq    %rcx, ffi_closure_OFS_G+0x18(%rsp)
252         movq    %r8,  ffi_closure_OFS_G+0x20(%rsp)
253         movq    %r9,  ffi_closure_OFS_G+0x28(%rsp)
254
255 #ifdef __ILP32__
256         movl    FFI_TRAMPOLINE_SIZE(%r10), %edi         /* Load cif */
257         movl    FFI_TRAMPOLINE_SIZE+4(%r10), %esi       /* Load fun */
258         movl    FFI_TRAMPOLINE_SIZE+8(%r10), %edx       /* Load user_data */
259 #else
260         movq    FFI_TRAMPOLINE_SIZE(%r10), %rdi         /* Load cif */
261         movq    FFI_TRAMPOLINE_SIZE+8(%r10), %rsi       /* Load fun */
262         movq    FFI_TRAMPOLINE_SIZE+16(%r10), %rdx      /* Load user_data */
263 #endif
264 .Ldo_closure:
265         leaq    ffi_closure_OFS_RVALUE(%rsp), %rcx      /* Load rvalue */
266         movq    %rsp, %r8                               /* Load reg_args */
267         leaq    ffi_closure_FS+8(%rsp), %r9             /* Load argp */
268         call    ffi_closure_unix64_inner
269
270         /* Deallocate stack frame early; return value is now in redzone.  */
271         addq    $ffi_closure_FS, %rsp
272         cfi_adjust_cfa_offset(-ffi_closure_FS)
273
274         /* The first byte of the return value contains the FFI_TYPE.  */
275         cmpb    $UNIX64_RET_LAST, %al
276         movzbl  %al, %r10d
277         leaq    0f(%rip), %r11
278         ja      9f
279         leaq    (%r11, %r10, 8), %r10
280         leaq    ffi_closure_RED_RVALUE(%rsp), %rsi
281         jmp     *%r10
282
283         .align  8
284 0:
285 E UNIX64_RET_VOID
286         ret
287 E UNIX64_RET_UINT8
288         movzbl  (%rsi), %eax
289         ret
290 E UNIX64_RET_UINT16
291         movzwl  (%rsi), %eax
292         ret
293 E UNIX64_RET_UINT32
294         movl    (%rsi), %eax
295         ret
296 E UNIX64_RET_SINT8
297         movsbl  (%rsi), %eax
298         ret
299 E UNIX64_RET_SINT16
300         movswl  (%rsi), %eax
301         ret
302 E UNIX64_RET_SINT32
303         movl    (%rsi), %eax
304         ret
305 E UNIX64_RET_INT64
306         movq    (%rsi), %rax
307         ret
308 E UNIX64_RET_XMM32
309         movd    (%rsi), %xmm0
310         ret
311 E UNIX64_RET_XMM64
312         movq    (%rsi), %xmm0
313         ret
314 E UNIX64_RET_X87
315         fldt    (%rsi)
316         ret
317 E UNIX64_RET_X87_2
318         fldt    16(%rsi)
319         fldt    (%rsi)
320         ret
321 E UNIX64_RET_ST_XMM0_RAX
322         movq    8(%rsi), %rax
323         jmp     3f
324 E UNIX64_RET_ST_RAX_XMM0
325         movq    8(%rsi), %xmm0
326         jmp     2f
327 E UNIX64_RET_ST_XMM0_XMM1
328         movq    8(%rsi), %xmm1
329         jmp     3f
330 E UNIX64_RET_ST_RAX_RDX
331         movq    8(%rsi), %rdx
332 2:      movq    (%rsi), %rax
333         ret
334         .align  8
335 3:      movq    (%rsi), %xmm0
336         ret
337
338 9:      call    abort@PLT
339
340         cfi_endproc
341         .size   ffi_closure_unix64,.-ffi_closure_unix64
342
343         .align  2
344         .globl  ffi_go_closure_unix64_sse
345         .type   ffi_go_closure_unix64_sse,@function
346         FFI_HIDDEN(ffi_go_closure_unix64_sse)
347
348 ffi_go_closure_unix64_sse:
349         cfi_startproc
350         subq    $ffi_closure_FS, %rsp
351         cfi_adjust_cfa_offset(ffi_closure_FS)
352
353         movdqa  %xmm0, ffi_closure_OFS_V+0x00(%rsp)
354         movdqa  %xmm1, ffi_closure_OFS_V+0x10(%rsp)
355         movdqa  %xmm2, ffi_closure_OFS_V+0x20(%rsp)
356         movdqa  %xmm3, ffi_closure_OFS_V+0x30(%rsp)
357         movdqa  %xmm4, ffi_closure_OFS_V+0x40(%rsp)
358         movdqa  %xmm5, ffi_closure_OFS_V+0x50(%rsp)
359         movdqa  %xmm6, ffi_closure_OFS_V+0x60(%rsp)
360         movdqa  %xmm7, ffi_closure_OFS_V+0x70(%rsp)
361         jmp     0f
362
363         cfi_endproc
364         .size   ffi_go_closure_unix64_sse,.-ffi_go_closure_unix64_sse
365
366         .align  2
367         .globl  ffi_go_closure_unix64
368         .type   ffi_go_closure_unix64,@function
369         FFI_HIDDEN(ffi_go_closure_unix64)
370
371 ffi_go_closure_unix64:
372         cfi_startproc
373         subq    $ffi_closure_FS, %rsp
374         cfi_adjust_cfa_offset(ffi_closure_FS)
375 0:
376         movq    %rdi, ffi_closure_OFS_G+0x00(%rsp)
377         movq    %rsi, ffi_closure_OFS_G+0x08(%rsp)
378         movq    %rdx, ffi_closure_OFS_G+0x10(%rsp)
379         movq    %rcx, ffi_closure_OFS_G+0x18(%rsp)
380         movq    %r8,  ffi_closure_OFS_G+0x20(%rsp)
381         movq    %r9,  ffi_closure_OFS_G+0x28(%rsp)
382
383 #ifdef __ILP32__
384         movl    4(%r10), %edi           /* Load cif */
385         movl    8(%r10), %esi           /* Load fun */
386         movl    %r10d, %edx             /* Load closure (user_data) */
387 #else
388         movq    8(%r10), %rdi           /* Load cif */
389         movq    16(%r10), %rsi          /* Load fun */
390         movq    %r10, %rdx              /* Load closure (user_data) */
391 #endif
392         jmp     .Ldo_closure
393
394         cfi_endproc
395         .size   ffi_go_closure_unix64,.-ffi_go_closure_unix64
396
397 #endif /* __x86_64__ */
398 #if defined __ELF__ && defined __linux__
399         .section        .note.GNU-stack,"",@progbits
400 #endif