0151229c0629e9ee4c0c559851f7f0c17cf36a52
[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_ST_RAX_RDX
160         movq    %rdx, 8(%rsi)
161         jmp     2f
162 E UNIX64_RET_ST_XMM0_RAX
163         movq    %rax, 8(%rsi)
164         jmp     3f
165 E UNIX64_RET_ST_RAX_XMM0
166         movq    %xmm0, 8(%rsi)
167         jmp     2f
168 E UNIX64_RET_ST_XMM0_XMM1
169         movq    %xmm1, 8(%rsi)
170
171         .align 8
172 3:      movq    %xmm0, (%rsi)
173         shrl    $UNIX64_SIZE_SHIFT, %ecx
174         rep movsb
175         ret
176         .align 8
177 2:      movq    %rax, (%rsi)
178         shrl    $UNIX64_SIZE_SHIFT, %ecx
179         rep movsb
180         ret
181
182 9:      call    abort@PLT
183
184         /* Many times we can avoid loading any SSE registers at all.
185            It's not worth an indirect jump to load the exact set of
186            SSE registers needed; zero or all is a good compromise.  */
187         .align 2
188         cfi_restore_state
189 .Lload_sse:
190         movdqa  0x30(%r10), %xmm0
191         movdqa  0x40(%r10), %xmm1
192         movdqa  0x50(%r10), %xmm2
193         movdqa  0x60(%r10), %xmm3
194         movdqa  0x70(%r10), %xmm4
195         movdqa  0x80(%r10), %xmm5
196         movdqa  0x90(%r10), %xmm6
197         movdqa  0xa0(%r10), %xmm7
198         jmp     .Lret_from_load_sse
199
200         cfi_endproc
201         .size    ffi_call_unix64,.-ffi_call_unix64
202
203 /* 6 general registers, 8 vector registers,
204    16 bytes of rvalue, 8 bytes of alignment.  */
205 #define ffi_closure_OFS_G       0
206 #define ffi_closure_OFS_V       (6*8)
207 #define ffi_closure_OFS_RVALUE  (ffi_closure_OFS_V + 8*16)
208 #define ffi_closure_FS          (ffi_closure_OFS_RVALUE + 16 + 8)
209
210 /* The location of rvalue within the red zone after deallocating the frame.  */
211 #define ffi_closure_RED_RVALUE  (ffi_closure_OFS_RVALUE - ffi_closure_FS)
212
213         .align  2
214         .globl  ffi_closure_unix64_sse
215         .type   ffi_closure_unix64_sse,@function
216         FFI_HIDDEN(ffi_closure_unix64_sse)
217
218 ffi_closure_unix64_sse:
219         cfi_startproc
220         subq    $ffi_closure_FS, %rsp
221         cfi_adjust_cfa_offset(ffi_closure_FS)
222
223         movdqa  %xmm0, ffi_closure_OFS_V+0x00(%rsp)
224         movdqa  %xmm1, ffi_closure_OFS_V+0x10(%rsp)
225         movdqa  %xmm2, ffi_closure_OFS_V+0x20(%rsp)
226         movdqa  %xmm3, ffi_closure_OFS_V+0x30(%rsp)
227         movdqa  %xmm4, ffi_closure_OFS_V+0x40(%rsp)
228         movdqa  %xmm5, ffi_closure_OFS_V+0x50(%rsp)
229         movdqa  %xmm6, ffi_closure_OFS_V+0x60(%rsp)
230         movdqa  %xmm7, ffi_closure_OFS_V+0x70(%rsp)
231         jmp     0f
232
233         cfi_endproc
234         .size   ffi_closure_unix64_sse,.-ffi_closure_unix64_sse
235
236         .align  2
237         .globl  ffi_closure_unix64
238         .type   ffi_closure_unix64,@function
239         FFI_HIDDEN(ffi_closure_unix64)
240
241 ffi_closure_unix64:
242         cfi_startproc
243         subq    $ffi_closure_FS, %rsp
244         cfi_adjust_cfa_offset(ffi_closure_FS)
245 0:
246         movq    %rdi, ffi_closure_OFS_G+0x00(%rsp)
247         movq    %rsi, ffi_closure_OFS_G+0x08(%rsp)
248         movq    %rdx, ffi_closure_OFS_G+0x10(%rsp)
249         movq    %rcx, ffi_closure_OFS_G+0x18(%rsp)
250         movq    %r8,  ffi_closure_OFS_G+0x20(%rsp)
251         movq    %r9,  ffi_closure_OFS_G+0x28(%rsp)
252
253 #ifdef __ILP32__
254         movl    FFI_TRAMPOLINE_SIZE(%r10), %edi         /* Load cif */
255         movl    FFI_TRAMPOLINE_SIZE+4(%r10), %esi       /* Load fun */
256         movl    FFI_TRAMPOLINE_SIZE+8(%r10), %edx       /* Load user_data */
257 #else
258         movq    FFI_TRAMPOLINE_SIZE(%r10), %rdi         /* Load cif */
259         movq    FFI_TRAMPOLINE_SIZE+8(%r10), %rsi       /* Load fun */
260         movq    FFI_TRAMPOLINE_SIZE+16(%r10), %rdx      /* Load user_data */
261 #endif
262 .Ldo_closure:
263         leaq    ffi_closure_OFS_RVALUE(%rsp), %rcx      /* Load rvalue */
264         movq    %rsp, %r8                               /* Load reg_args */
265         leaq    ffi_closure_FS+8(%rsp), %r9             /* Load argp */
266         call    ffi_closure_unix64_inner
267
268         /* Deallocate stack frame early; return value is now in redzone.  */
269         addq    $ffi_closure_FS, %rsp
270         cfi_adjust_cfa_offset(-ffi_closure_FS)
271
272         /* The first byte of the return value contains the FFI_TYPE.  */
273         cmpb    $UNIX64_RET_LAST, %al
274         movzbl  %al, %r10d
275         leaq    0f(%rip), %r11
276         ja      9f
277         leaq    (%r11, %r10, 8), %r10
278         jmp     *%r10
279
280         .align  8
281 0:
282 E UNIX64_RET_VOID
283         ret
284 E UNIX64_RET_UINT8
285         movzbl  ffi_closure_RED_RVALUE(%rsp), %eax
286         ret
287 E UNIX64_RET_UINT16
288         movzwl  ffi_closure_RED_RVALUE(%rsp), %eax
289         ret
290 E UNIX64_RET_UINT32
291         movl    ffi_closure_RED_RVALUE(%rsp), %eax
292         ret
293 E UNIX64_RET_SINT8
294         movsbl  ffi_closure_RED_RVALUE(%rsp), %eax
295         ret
296 E UNIX64_RET_SINT16
297         movswl  ffi_closure_RED_RVALUE(%rsp), %eax
298         ret
299 E UNIX64_RET_SINT32
300         movl    ffi_closure_RED_RVALUE(%rsp), %eax
301         ret
302 E UNIX64_RET_INT64
303         movq    ffi_closure_RED_RVALUE(%rsp), %rax
304         ret
305 E UNIX64_RET_XMM32
306         movd    ffi_closure_RED_RVALUE(%rsp), %xmm0
307         ret
308 E UNIX64_RET_XMM64
309         movq    ffi_closure_RED_RVALUE(%rsp), %xmm0
310         ret
311 E UNIX64_RET_X87
312         fldt    ffi_closure_RED_RVALUE(%rsp)
313         ret
314 E UNIX64_RET_ST_RAX_RDX
315         movq    ffi_closure_RED_RVALUE+8(%rsp), %rdx
316         jmp     2f
317 E UNIX64_RET_ST_XMM0_RAX
318         movq    ffi_closure_RED_RVALUE+8(%rsp), %rax
319         jmp     3f
320 E UNIX64_RET_ST_RAX_XMM0
321         movq    ffi_closure_RED_RVALUE+8(%rsp), %xmm0
322         jmp     2f
323 E UNIX64_RET_ST_XMM0_XMM1
324         movq    ffi_closure_RED_RVALUE+8(%rsp), %xmm1
325
326         .align  8
327 3:      movq    ffi_closure_RED_RVALUE(%rsp), %xmm0
328         ret
329         .align  8
330 2:      movq    ffi_closure_RED_RVALUE(%rsp), %rax
331         ret
332
333 9:      call    abort@PLT
334
335         cfi_endproc
336         .size   ffi_closure_unix64,.-ffi_closure_unix64
337
338         .align  2
339         .globl  ffi_go_closure_unix64_sse
340         .type   ffi_go_closure_unix64_sse,@function
341         FFI_HIDDEN(ffi_go_closure_unix64_sse)
342
343 ffi_go_closure_unix64_sse:
344         cfi_startproc
345         subq    $ffi_closure_FS, %rsp
346         cfi_adjust_cfa_offset(ffi_closure_FS)
347
348         movdqa  %xmm0, ffi_closure_OFS_V+0x00(%rsp)
349         movdqa  %xmm1, ffi_closure_OFS_V+0x10(%rsp)
350         movdqa  %xmm2, ffi_closure_OFS_V+0x20(%rsp)
351         movdqa  %xmm3, ffi_closure_OFS_V+0x30(%rsp)
352         movdqa  %xmm4, ffi_closure_OFS_V+0x40(%rsp)
353         movdqa  %xmm5, ffi_closure_OFS_V+0x50(%rsp)
354         movdqa  %xmm6, ffi_closure_OFS_V+0x60(%rsp)
355         movdqa  %xmm7, ffi_closure_OFS_V+0x70(%rsp)
356         jmp     0f
357
358         cfi_endproc
359         .size   ffi_go_closure_unix64_sse,.-ffi_go_closure_unix64_sse
360
361         .align  2
362         .globl  ffi_go_closure_unix64
363         .type   ffi_go_closure_unix64,@function
364         FFI_HIDDEN(ffi_go_closure_unix64)
365
366 ffi_go_closure_unix64:
367         cfi_startproc
368         subq    $ffi_closure_FS, %rsp
369         cfi_adjust_cfa_offset(ffi_closure_FS)
370 0:
371         movq    %rdi, ffi_closure_OFS_G+0x00(%rsp)
372         movq    %rsi, ffi_closure_OFS_G+0x08(%rsp)
373         movq    %rdx, ffi_closure_OFS_G+0x10(%rsp)
374         movq    %rcx, ffi_closure_OFS_G+0x18(%rsp)
375         movq    %r8,  ffi_closure_OFS_G+0x20(%rsp)
376         movq    %r9,  ffi_closure_OFS_G+0x28(%rsp)
377
378 #ifdef __ILP32__
379         movl    4(%r10), %edi           /* Load cif */
380         movl    8(%r10), %esi           /* Load fun */
381         movl    %r10d, %edx             /* Load closure (user_data) */
382 #else
383         movq    8(%r10), %rdi           /* Load cif */
384         movq    16(%r10), %rsi          /* Load fun */
385         movq    %r10, %rdx              /* Load closure (user_data) */
386 #endif
387         jmp     .Ldo_closure
388
389         cfi_endproc
390         .size   ffi_go_closure_unix64,.-ffi_go_closure_unix64
391
392 #endif /* __x86_64__ */
393 #if defined __ELF__ && defined __linux__
394         .section        .note.GNU-stack,"",@progbits
395 #endif