6ea7d2153562b86e8bbcb7e397a75642ff438dca
[sbcl.git] / src / runtime / x86-64-assem.S
1 /*
2  * very-low-level utilities for runtime support
3  */
4
5 /*
6  * This software is part of the SBCL system. See the README file for
7  * more information.
8  *
9  * This software is derived from the CMU CL system, which was
10  * written at Carnegie Mellon University and released into the
11  * public domain. The software is in the public domain and is
12  * provided with absolutely no warranty. See the COPYING and CREDITS
13  * files for more information.
14  */
15 \f
16 #define LANGUAGE_ASSEMBLY
17 #include "validate.h"
18 #include "sbcl.h"
19 #include "genesis/closure.h"
20 #include "genesis/funcallable-instance.h"
21 #include "genesis/fdefn.h"
22 #include "genesis/static-symbols.h"
23 #include "genesis/symbol.h"
24 #include "genesis/thread.h"
25         
26 /* Minimize conditionalization for different OS naming schemes. */
27 #if defined __linux__  || defined __FreeBSD__ /* (but *not* OpenBSD) */
28 #define GNAME(var) var
29 #else
30 #define GNAME(var) _##var
31 #endif
32
33 /* Get the right type of alignment. Linux and FreeBSD (but not OpenBSD)
34  * want alignment in bytes. */
35 #if defined(__linux__) || defined(__FreeBSD__)
36 #define align_4byte     4
37 #define align_8byte     8
38 #define align_16byte    16
39 #define align_32byte    32
40 #else
41 #define align_4byte     2
42 #define align_8byte     3
43 #define align_16byte    4       
44 #endif                  
45
46         .text
47         .global GNAME(foreign_function_call_active)
48         .global GNAME(all_threads)
49         
50 \f
51 /* From lower to higher-numbered addresses, the stack contains 
52  * return address, arg 0, arg 1, arg 2 ...
53  * rax contains the address of the function to call
54  * Lisp expects return value in rax, whic is already consistent with C
55  * XXXX correct floating point handling is unimplemented so far
56  * Based on comments cleaned from x86-assem.S, we believe that 
57  * Lisp is expecting us to preserve rsi, rdi, rsp (no idea about r8-15)
58  */
59         .text
60         .align  align_16byte,0x90
61         .global GNAME(call_into_c)
62         .type   GNAME(call_into_c),@function
63 GNAME(call_into_c):
64         /* ABI requires that the direction flag be clear on function
65          * entry and exit. */
66         cld
67         
68         push    %rbp            # Save old frame pointer.
69         mov     %rsp,%rbp       # Establish new frame.
70
71         push    %rsi            # args are going in here
72         push    %rdi
73         mov     16(%rbp),%rdi
74         mov     24(%rbp),%rsi
75         mov     32(%rbp),%rdx
76         mov     40(%rbp),%rcx
77         mov     48(%rbp),%rcx
78         mov     56(%rbp),%r8
79         mov     64(%rbp),%r9
80         call    *%rax
81         mov     %rbp,%rsp
82         pop     %rbp
83         ret
84         .size   GNAME(call_into_c), . - GNAME(call_into_c)
85
86 \f
87         .text   
88         .global GNAME(call_into_lisp_first_time)
89         .type  GNAME(call_into_lisp_first_time),@function
90                 
91 /* The *ALIEN-STACK* pointer is set up on the first call_into_lisp when
92  * the stack changes.  We don't worry too much about saving registers 
93  * here, because we never expect to return from the initial call to lisp 
94  * anyway */
95         
96         .align  align_16byte,0x90
97 GNAME(call_into_lisp_first_time):
98         push    %rbp            # Save old frame pointer.
99         mov     %rsp,%rbp       # Establish new frame.
100         mov    %rsp,ALIEN_STACK + SYMBOL_VALUE_OFFSET
101         mov    GNAME(all_threads),%rax
102         mov    THREAD_CONTROL_STACK_START_OFFSET(%rax) ,%rsp
103         /* don't think too hard about what happens if we get interrupted
104         * here */
105         add     $THREAD_CONTROL_STACK_SIZE-16,%rsp
106         jmp     Lstack
107 \f
108         .text   
109         .global GNAME(call_into_lisp)
110         .type  GNAME(call_into_lisp),@function
111                 
112 /*
113  * amd64 calling convention: C expects that
114  * arguments go in rdi rsi rdx rcx r8 r9
115  * return values in rax rdx
116  * callee saves rbp rbx r12-15 if it uses them
117  */
118         
119         .align  align_16byte,0x90
120 GNAME(call_into_lisp):
121         push    %rbp            # Save old frame pointer.
122         mov     %rsp,%rbp       # Establish new frame.
123 Lstack:
124         /* FIXME x86 saves FPU state here */
125         push    %rbx    # these regs are callee-saved according to C
126         push    %r12    # so must be preserved and restored when 
127         push    %r13    # the lisp function returns
128         push    %r14    #
129         push    %r15    #
130
131         mov     %rsp,%rbx       # remember current stack
132         push    %rbx            # Save entry stack on (maybe) new stack.
133
134         push    %rdi    # args from C
135         push    %rsi    #
136         push    %rdx    #
137 #ifdef LISP_FEATURE_SB_THREAD
138         mov     specials,%rdi
139         call    pthread_getspecific
140         mov     %rax,%r12
141 #endif
142         pop     %rcx    # num args
143         pop     %rbx    # arg vector
144         pop     %rax    # function ptr/lexenv
145
146         xor     %rdx,%rdx       # clear any descriptor registers 
147         xor     %rdi,%rdi       # that we can't be sure we'll 
148         xor     %rsi,%rsi       # initialise properly.  XX do r8-r15 too?
149         shl     $3,%rcx         # (fixnumize num-args)
150         cmp     $0,%rcx
151         je      Ldone
152         mov     0(%rbx),%rdx    # arg0
153         cmp     $8,%rcx
154         je      Ldone
155         mov     8(%rbx),%rdi    # arg1
156         cmp     $16,%rcx
157         je      Ldone
158         mov     16(%rbx),%rsi   # arg2
159 Ldone:  
160         /* Registers rax, rcx, rdx, rdi, and rsi are now live. */
161         xor     %rbx,%rbx       # available
162
163         /* Alloc new frame. */
164         mov     %rsp,%rbx       # The current sp marks start of new frame.
165         push    %rbp            # fp in save location S0
166         sub     $16,%rsp        # Ensure 3 slots are allocated, one above.
167         mov     %rbx,%rbp       # Switch to new frame.
168
169 Lcall:
170         call    *CLOSURE_FUN_OFFSET(%rax)
171         
172         /* If the function returned multiple values, it will return to
173            this point.  Lose them */
174         jnc     LsingleValue    
175         mov     %rbx, %rsp
176 LsingleValue:   
177
178 /* Restore the stack, in case there was a stack change. */
179         pop     %rsp            # c-sp
180
181 /* Restore C regs */
182         pop     %r15
183         pop     %r14
184         pop     %r13
185         pop     %r12
186         pop     %rbx
187
188         /* ABI requires that the direction flag be clear on function
189          * entry and exit. */
190         cld
191         
192 /* FIXME Restore the NPX state. */
193
194         /* return value is already in rax where lisp expects it */
195         leave
196         ret
197         .size   GNAME(call_into_lisp), . - GNAME(call_into_lisp)
198 \f
199 /* support for saving and restoring the NPX state from C */
200         .text
201         .global GNAME(fpu_save)
202         .type   GNAME(fpu_save),@function
203         .align  2,0x90
204 GNAME(fpu_save):
205         mov     4(%rsp),%rax
206         fnsave  (%rax)          # Save the NPX state. (resets NPX)
207         ret
208         .size   GNAME(fpu_save),.-GNAME(fpu_save)
209
210         .global GNAME(fpu_restore)
211         .type   GNAME(fpu_restore),@function
212         .align  2,0x90
213 GNAME(fpu_restore):
214         mov     4(%rsp),%rax
215         frstor  (%rax)          # Restore the NPX state.
216         ret
217         .size   GNAME(fpu_restore),.-GNAME(fpu_restore)
218 \f
219 /*
220  * the undefined-function trampoline
221  */
222         .text
223         .align  align_8byte,0x90
224         .global GNAME(undefined_tramp)
225         .type   GNAME(undefined_tramp),@function
226 GNAME(undefined_tramp):
227         int3
228         .byte   trap_Error
229         .byte   2
230         .byte   UNDEFINED_FUN_ERROR
231         .byte   sc_DescriptorReg # eax in the Descriptor-reg SC
232         ret
233         .size   GNAME(undefined_tramp), .-GNAME(undefined_tramp)
234
235
236         .text
237         .align  align_8byte,0x90
238         .global GNAME(alloc_tramp)
239         .type   GNAME(alloc_tramp),@function
240 GNAME(alloc_tramp):
241         push    %rbp            # Save old frame pointer.
242         mov     %rsp,%rbp       # Establish new frame.
243         push    %rax
244         push    %rcx
245         push    %rdx
246         push    %rsi
247         push    %rdi
248         push    %r8
249         push    %r9
250         push    %r10
251         push    %r11
252         mov     16(%rbp),%rdi   
253         call    alloc
254         mov     %rax,16(%rbp)
255         pop     %r11
256         pop     %r10
257         pop     %r9
258         pop     %r8
259         pop     %rdi
260         pop     %rsi
261         pop     %rdx
262         pop     %rcx
263         pop     %rax
264         pop     %rbp
265         ret
266         .size   GNAME(alloc_tramp),.-GNAME(alloc_tramp)
267
268                 
269 /*
270  * the closure trampoline
271  */
272         .text
273         .align  align_8byte,0x90
274         .global GNAME(closure_tramp)
275         .type   GNAME(closure_tramp),@function
276 GNAME(closure_tramp):
277         mov     FDEFN_FUN_OFFSET(%rax),%rax
278         /* FIXME: The '*' after "jmp" in the next line is from PVE's
279          * patch posted to the CMU CL mailing list Oct 6, 1999. It looks
280          * reasonable, and it certainly seems as though if CMU CL needs it,
281          * SBCL needs it too, but I haven't actually verified that it's
282          * right. It would be good to find a way to force the flow of
283          * control through here to test it. */
284         jmp     *CLOSURE_FUN_OFFSET(%rax)
285         .size   GNAME(closure_tramp), .-GNAME(closure_tramp)
286
287         .text
288         .align  align_8byte,0x90
289         .global GNAME(funcallable_instance_tramp)
290         .type   GNAME(funcallable_instance_tramp),@function
291 GNAME(funcallable_instance_tramp):
292         mov     FUNCALLABLE_INSTANCE_FUNCTION_OFFSET(%rax),%rax
293         /* KLUDGE: on this platform, whatever kind of function is in %rax
294          * now, the first word of it contains the address to jump to. */
295         jmp     *CLOSURE_FUN_OFFSET(%rax)
296         .size   GNAME(funcallable_instance_tramp), .-GNAME(funcallable_instance_tramp)
297         
298 /*
299  * fun-end breakpoint magic
300  */
301         .text
302         .global GNAME(fun_end_breakpoint_guts)
303         .align  align_8byte
304 GNAME(fun_end_breakpoint_guts):
305         /* Multiple Value return */
306         jc      multiple_value_return
307         /* Single value return: The eventual return will now use the
308            multiple values return convention but with a return values
309            count of one. */
310         mov     %rsp,%rbx       # Setup ebx - the ofp.
311         sub     $8,%rsp         # Allocate one stack slot for the return value
312         mov     $8,%rcx         # Setup ecx for one return value.
313         mov     $NIL,%rdi       # default second value
314         mov     $NIL,%rsi       # default third value
315                 
316 multiple_value_return:
317         
318         .global GNAME(fun_end_breakpoint_trap)
319 GNAME(fun_end_breakpoint_trap):
320         int3
321         .byte   trap_FunEndBreakpoint
322         hlt                     # We should never return here.
323
324         .global GNAME(fun_end_breakpoint_end)
325 GNAME(fun_end_breakpoint_end):
326
327 \f
328         .global GNAME(do_pending_interrupt)
329         .type   GNAME(do_pending_interrupt),@function
330         .align  align_8byte,0x90
331 GNAME(do_pending_interrupt):
332         int3
333         .byte   trap_PendingInterrupt
334         ret
335         .size   GNAME(do_pending_interrupt),.-GNAME(do_pending_interrupt)
336 \f
337         .globl  GNAME(post_signal_tramp)
338         .type   GNAME(post_signal_tramp),@function
339         .align  align_8byte,0x90
340 GNAME(post_signal_tramp):
341         /* this is notionally the second half of a function whose first half
342          * doesn't exist.  This is where call_into_lisp returns when called 
343          * using return_to_lisp_function */
344         popq %r15
345         popq %r14
346         popq %r13
347         popq %r12
348         popq %r11
349         popq %r10
350         popq %r9
351         popq %r8
352         popq %rdi
353         popq %rsi
354         /* skip RBP and RSP */
355         popq %rbx
356         popq %rdx
357         popq %rcx
358         popq %rax
359         popfq
360         leave
361         ret
362         .size GNAME(post_signal_tramp),.-GNAME(post_signal_tramp)
363 \f
364         .text
365         .align  align_8byte,0x90
366         .global GNAME(fast_bzero)
367         .type   GNAME(fast_bzero),@function
368         
369 GNAME(fast_bzero):
370         /* A fast routine for zero-filling blocks of memory that are
371          * guaranteed to start and end at a 4096-byte aligned address.
372          */
373         shr $6, %rsi              /* Amount of 64-byte blocks to copy */
374         jz Lend                   /* If none, stop */
375         mov %rsi, %rcx            /* Save start address */
376         movups %xmm7, -16(%rsp)   /* Save XMM register */
377         xorps  %xmm7, %xmm7       /* Zero the XMM register */
378         jmp Lloop
379         .align 16                 
380 Lloop:
381
382         /* Copy the 16 zeroes from xmm7 to memory, 4 times. MOVNTDQ is the
383          * non-caching double-quadword moving variant, i.e. the memory areas
384          * we're touching are not fetched into the L1 cache, since we're just
385          * going to overwrite the memory soon anyway.
386          */
387         movntdq %xmm7, 0(%rdi)
388         movntdq %xmm7, 16(%rdi)
389         movntdq %xmm7, 32(%rdi)
390         movntdq %xmm7, 48(%rdi)
391
392         add $64, %rdi  /* Advance pointer */
393         dec %rsi       /* Decrement 64-byte block count */
394         jnz Lloop
395         mfence         /* Ensure that the writes are globally visible, since
396                         * MOVNTDQ is weakly ordered */
397         movups -16(%rsp), %xmm7 /* Restore the XMM register */
398         prefetcht0 0(%rcx)      /* Prefetch the start of the block into cache,
399                                  * since it's likely to be used immediately. */
400 Lend:        
401         ret
402         .size   GNAME(fast_bzero), .-GNAME(fast_bzero)
403
404         .end