x86-64: Integrate Paul Khuong's interleaved raw slot feature.
[sbcl.git] / src / runtime / x86-64-assem.S
bloba9f6227803512da0e3167f3e399b3f728d2aaa42
1 /*
2  * very-low-level utilities for runtime support
3  */
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  */
16 #define LANGUAGE_ASSEMBLY
17 #include "genesis/config.h"
18 #include "validate.h"
19 #include "sbcl.h"
20 #include "genesis/closure.h"
21 #include "genesis/funcallable-instance.h"
22 #include "genesis/fdefn.h"
23 #include "genesis/static-symbols.h"
24 #include "genesis/symbol.h"
25 #include "genesis/thread.h"
26         
27 /* Minimize conditionalization for different OS naming schemes. */
28 #if defined __linux__  || defined LISP_FEATURE_FREEBSD || defined __OpenBSD__ || defined __NetBSD__ || defined __sun || defined _WIN64 || defined __DragonFly__
29 #define GNAME(var) var
30 #else
31 #define GNAME(var) _##var
32 #endif
34 /* Get the right type of alignment. Linux, FreeBSD and OpenBSD
35  * want alignment in bytes. */
36 #if defined(__linux__) || defined(LISP_FEATURE_FREEBSD) || defined(__OpenBSD__) || defined __NetBSD__ || defined(__sun) || defined _WIN64 || defined(__DragonFly__)
37 #define align_4byte     4
38 #define align_8byte     8
39 #define align_16byte    16
40 #define align_32byte    32
41 #define align_page      32768
42 #else
43 #define align_4byte     2
44 #define align_8byte     3
45 #define align_16byte    4       
46 #define align_page      15
47 #endif                  
50  * The assembler used for win32 doesn't like .type or .size directives,
51  * so we want to conditionally kill them out. So let's wrap them in macros
52  * that are defined to be no-ops on win32. Hopefully this still works on
53  * other platforms.
54  */
55 #if !defined(LISP_FEATURE_WIN32) && !defined(LISP_FEATURE_DARWIN)
56 #define TYPE(name) .type name,@function
57 #define SIZE(name) .size name,.-name
58 #define DOLLAR(name) $(name)
59 #else
60 #define TYPE(name)
61 #define SIZE(name)
62 #endif
65  * x86/darwin (as of MacOS X 10.4.5) doesn't reliably fire signal
66  * handlers (SIGTRAP or Mach exception handlers) for 0xCC, wo we have
67  * to use ud2 instead. ud2 is an undefined opcode, #x0b0f, or
68  * 0F 0B in low-endian notation, that causes SIGILL to fire. We check
69  * for this instruction in the SIGILL handler and if we see it, we
70  * advance the EIP by two bytes to skip over ud2 instruction and
71  * call sigtrap_handler. */
72 #if defined(LISP_FEATURE_UD2_BREAKPOINTS)
73 #define TRAP ud2
74 #else
75 #define TRAP int3
76 #endif
79  * More Apple assembler hacks
80  */
82 #if defined(LISP_FEATURE_DARWIN)
83 /* global symbol x86-64 sym(%rip) hack:*/
84 #define GSYM(name) name(%rip)
85 #else
86 #define GSYM(name) $name
87 #endif
89         
90         .text
91         .globl  GNAME(all_threads)
92         
93         
95 /* From lower to higher-numbered addresses, the stack contains 
96  * return address, arg 0, arg 1, arg 2 ...
97  * rax contains the address of the function to call
98  * Lisp expects return value in rax, whic is already consistent with C
99  * XXXX correct floating point handling is unimplemented so far
100  * Based on comments cleaned from x86-assem.S, we believe that 
101  * Lisp is expecting us to preserve rsi, rdi, rsp (no idea about r8-15)
102  */
103         .text
104         .align  align_16byte,0x90
105         .globl  GNAME(call_into_c)
106         TYPE(GNAME(call_into_c))
107 GNAME(call_into_c):
108         push    %rbp            # Save old frame pointer.
109         mov     %rsp,%rbp       # Establish new frame.
111         push    %rsi            # args are going in here
112         push    %rdi
113         mov     16(%rbp),%rdi
114         mov     24(%rbp),%rsi
115         mov     32(%rbp),%rdx
116         mov     40(%rbp),%rcx
117         mov     48(%rbp),%rcx
118         mov     56(%rbp),%r8
119         mov     64(%rbp),%r9
120         call    *%rax
121         mov     %rbp,%rsp
122         pop     %rbp
123         ret
124         SIZE(GNAME(call_into_c))
127         .text   
128         .globl  GNAME(call_into_lisp_first_time)
129         TYPE(GNAME(call_into_lisp_first_time))
130                 
131 /* We don't worry too much about saving registers 
132  * here, because we never expect to return from the initial call to lisp 
133  * anyway */
134         
135         .align  align_16byte,0x90
136 GNAME(call_into_lisp_first_time):
137         push    %rbp            # Save old frame pointer.
138         mov     %rsp,%rbp       # Establish new frame.
139 #if defined(LISP_FEATURE_DARWIN)
140         movq    GSYM(GNAME(all_threads)),%rax
141 #else
142         movq    GNAME(all_threads),%rax
143 #endif
144         mov     THREAD_CONTROL_STACK_END_OFFSET(%rax) ,%rsp
145         jmp     Lstack
147         .text   
148         .globl  GNAME(call_into_lisp)
149         TYPE(GNAME(call_into_lisp))
150                 
152  * amd64 calling convention: C expects that
153  * arguments go in rdi rsi rdx rcx r8 r9
154  * return values in rax rdx
155  * callee saves rbp rbx r12-15 if it uses them
156  */
157 #ifdef LISP_FEATURE_WIN32
158 # define SUPPORT_FOMIT_FRAME_POINTER
159 #endif
160         .align  align_16byte,0x90
161 GNAME(call_into_lisp):
162 #ifdef SUPPORT_FOMIT_FRAME_POINTER
163         mov     %rbp,%rax
164 #endif
165         push    %rbp            # Save old frame pointer.
166         mov     %rsp,%rbp       # Establish new frame.
167 Lstack:
168 #ifdef SUPPORT_FOMIT_FRAME_POINTER
169         /* If called through call_into_lisp_first_time, %r15 becomes invalid
170          * here, but we will not return in that case. */
171         push    %r15
172         mov     %rax,%r15
173 #endif
174         /* FIXME x86 saves FPU state here */
175         push    %rbx    # these regs are callee-saved according to C
176         push    %r12    # so must be preserved and restored when 
177         push    %r13    # the lisp function returns
178         push    %r14    #
179         push    %r15    #
181         mov     %rsp,%rbx       # remember current stack
182         push    %rbx            # Save entry stack on (maybe) new stack.
184         push    %rdi    # args from C
185         push    %rsi    #
186         push    %rdx    #
187 #ifdef LISP_FEATURE_SB_THREAD
188 # ifdef SUPPORT_FOMIT_FRAME_POINTER
189         mov     (%rbp),%rcx
190         sub     $32,%rsp
191         call    GNAME(carry_frame_pointer)
192         add     $32,%rsp
193         mov     %rax,(%rbp)
194 # endif
195 #ifdef LISP_FEATURE_GCC_TLS
196         movq    %fs:0, %rax
197         movq    GNAME(current_thread)@TPOFF(%rax), %r12
198 #else
199 #ifdef LISP_FEATURE_DARWIN
200         mov     GSYM(GNAME(specials)),%rdi
201 #else
202         mov     specials,%rdi
203 #endif
204         call    GNAME(pthread_getspecific)
205         mov     %rax,%r12
206 #endif
207 #endif
208         pop     %rcx    # num args
209         pop     %rbx    # arg vector
210         pop     %rax    # function ptr/lexenv
212         xor     %rdx,%rdx       # clear any descriptor registers 
213         xor     %rdi,%rdi       # that we can't be sure we'll 
214         xor     %rsi,%rsi       # initialise properly.  XX do r8-r15 too?
215         cmp     $0,%rcx
216         # It's tempting to think 'cmov' for these assignments, but don't:
217         # cmov does a memory cycle whether or not it moves, succumbing to
218         # a classic buffer overrun bug if argv[] is "badly" placed.
219         je      Ldone
220         mov     0(%rbx),%rdx    # arg0
221         cmp     $1,%rcx
222         je      Ldone
223         mov     8(%rbx),%rdi    # arg1
224         cmp     $2,%rcx
225         je      Ldone
226         mov     16(%rbx),%rsi   # arg2
227 Ldone:  
228         shl     $(N_FIXNUM_TAG_BITS),%rcx       # (fixnumize num-args)
229         /* Registers rax, rcx, rdx, rdi, and rsi are now live. */
230         xor     %rbx,%rbx       # available
232         /* Alloc new frame. */
233         push    %rbp            # Dummy for return address
234         push    %rbp            # fp in save location S1
235         mov     %rsp,%rbp       # The current sp marks start of new frame.
236         sub     $8,%rsp         # Ensure 3 slots are allocated, two above.
238 Lcall:
239         call    *CLOSURE_FUN_OFFSET(%rax)
240         
241         /* If the function returned multiple values, it will return to
242            this point.  Lose them */
243         jnc     LsingleValue    
244         mov     %rbx, %rsp
245 LsingleValue:   
247 /* Restore the stack, in case there was a stack change. */
248         pop     %rsp            # c-sp
250 /* Restore C regs */
251         pop     %r15
252         pop     %r14
253         pop     %r13
254         pop     %r12
255         pop     %rbx
257 /* FIXME Restore the NPX state. */
259         mov     %rdx,%rax       # c-val
260 #ifdef SUPPORT_FOMIT_FRAME_POINTER
261         mov     %r15,%rbp       # orig rbp
262         pop     %r15            # orig r15
263         add     $8,%rsp         # no need for saved (overridden) rbp
264 #else
265         leave
266 #endif
267         ret
268         SIZE(GNAME(call_into_lisp))
270 /* support for saving and restoring the NPX state from C */
271         .text
272         .globl  GNAME(fpu_save)
273         TYPE(GNAME(fpu_save))
274         .align  align_16byte,0x90
275 GNAME(fpu_save):
276         fnsave  (%rdi)          # Save the NPX state. (resets NPX)
277         ret
278         SIZE(GNAME(fpu_save))
280         .globl  GNAME(fpu_restore)
281         TYPE(GNAME(fpu_restore))
282         .align  align_16byte,0x90
283 GNAME(fpu_restore):
284         frstor  (%rdi)          # Restore the NPX state.
285         ret
286         SIZE(GNAME(fpu_restore))
289  * the undefined-function trampoline
290  */
291         .text
292         .align  align_16byte,0x90
293         .globl  GNAME(undefined_tramp)
294         TYPE(GNAME(undefined_tramp))
295 GNAME(undefined_tramp):
296         pop     8(%rbp)         # Save return PC for backtrace.
297         TRAP
298         .byte   trap_Error
299         .byte   2
300         .byte   UNDEFINED_FUN_ERROR
301         .byte   sc_DescriptorReg # eax in the Descriptor-reg SC
302         ret
303         SIZE(GNAME(undefined_tramp))
305         .text
306         .align  align_16byte,0x90
307         .globl  GNAME(undefined_alien_function)
308         TYPE(GNAME(undefined_alien_function))
309 GNAME(undefined_alien_function):
310         pop     8(%rbp)         # Save return PC for backtrace.
311         TRAP
312         .byte   trap_Error
313         .byte   4
314         .byte   UNDEFINED_ALIEN_FUN_ERROR
315         /* Encode RBX
316            FIXME: make independt of the encoding changes. */
317         .byte   0xFE 
318         .byte   0x9F
319         .byte   0x01
320         ret
321         SIZE(GNAME(undefined_alien_function))
323 /* KLUDGE: FIND-ESCAPED-FRAME (SYS:SRC;CODE;DEBUG-INT.LISP) needs
324  * to know the name of the function immediately following the
325  * undefined-function trampoline. */
327 /* Our call-site does not take care of caller-saved xmm registers, so it
328  * falls to us spill them beforing hopping into C.
330  * We simply save all of them.
332  * (But for the sake of completeness, here is my understanding of the specs:)
333  *                     System V       Microsoft
334  * argument passing    xmm0-7         xmm0-3
335  * caller-saved        xmm8-15        xmm4-5
336  * callee-saved        -              xmm6-15
338  *  --DFL */
340 #define stkxmmsave(n) movaps %xmm##n, n*16(%rsp)
341 #define stkxmmload(n) movaps n*16(%rsp), %xmm##n
342 #define map_all_xmm(op) \
343         op(0);op(1);op(2);op(3);op(4);op(5);op(6);op(7); \
344     op(8);op(9);op(10);op(11);op(12);op(13);op(14);op(15);
346         .text
347         .align  align_16byte,0x90
348         .globl  GNAME(alloc_tramp)
349         TYPE(GNAME(alloc_tramp))
350 GNAME(alloc_tramp):
351         cld
352         push    %rbp            # Save old frame pointer.
353         mov     %rsp,%rbp       # Establish new frame.
354         and     $-32,%rsp
355         sub     $16*16,%rsp
356         map_all_xmm(stkxmmsave)
357         # Doing rax twice is to maintain 16-byte stack alignment.
358         push    %rax
359         push    %rax
360         push    %rcx
361         push    %rdx
362         push    %rsi
363         push    %rdi
364         push    %r8
365         push    %r9
366         push    %r10
367         push    %r11
368         # r12 through r15 are callee-saved
369         mov     16(%rbp),%rdi
370         call    GNAME(alloc)
371         mov     %rax,16(%rbp)
372         pop     %r11
373         pop     %r10
374         pop     %r9
375         pop     %r8
376         pop     %rdi
377         pop     %rsi
378         pop     %rdx
379         pop     %rcx
380         pop     %rax
381         pop     %rax # corresponds to "extra" push
382         map_all_xmm(stkxmmload)
383         mov     %rbp,%rsp
384         pop     %rbp
385         ret
386         SIZE(GNAME(alloc_tramp))
388                 
390  * the closure trampoline
391  */
392         .text
393         .align  align_16byte,0x90
394         .globl  GNAME(closure_tramp)
395         TYPE(GNAME(closure_tramp))
396 GNAME(closure_tramp):
397         mov     FDEFN_FUN_OFFSET(%rax),%rax
398         /* FIXME: The '*' after "jmp" in the next line is from PVE's
399          * patch posted to the CMU CL mailing list Oct 6, 1999. It looks
400          * reasonable, and it certainly seems as though if CMU CL needs it,
401          * SBCL needs it too, but I haven't actually verified that it's
402          * right. It would be good to find a way to force the flow of
403          * control through here to test it. */
404         jmp     *CLOSURE_FUN_OFFSET(%rax)
405         SIZE(GNAME(closure_tramp))
407         .text
408         .align  align_16byte,0x90
409         .globl  GNAME(funcallable_instance_tramp)
410 #if !defined(LISP_FEATURE_DARWIN)
411         TYPE(GNAME(funcallable_instance_tramp))
412 #endif
413         GNAME(funcallable_instance_tramp):
414         mov     FUNCALLABLE_INSTANCE_FUNCTION_OFFSET(%rax),%rax
415         /* KLUDGE: on this platform, whatever kind of function is in %rax
416          * now, the first word of it contains the address to jump to. */
417         jmp     *CLOSURE_FUN_OFFSET(%rax)
418 #if !defined(LISP_FEATURE_DARWIN)
419         SIZE(GNAME(funcallable_instance_tramp))
420 #endif
422  * fun-end breakpoint magic
423  */
426  * For an explanation of the magic involved in function-end
427  * breakpoints, see the implementation in ppc-assem.S.
428  */
430         .text
431         .globl  GNAME(fun_end_breakpoint_guts)
432         .align  align_16byte
433 GNAME(fun_end_breakpoint_guts):
434         /* Multiple Value return */
435         jc      multiple_value_return
436         /* Single value return: The eventual return will now use the
437            multiple values return convention but with a return values
438            count of one. */
439         mov     %rsp,%rbx       # Setup ebx - the ofp.
440         sub     $8,%rsp         # Allocate one stack slot for the return value
441         mov     $8,%rcx         # Setup ecx for one return value.
442 #if defined(LISP_FEATURE_DARWIN)
443         mov     GSYM(NIL),%rdi  # default second value
444         mov     GSYM(NIL),%rsi  # default third value
445 #else
446         mov     $NIL,%rdi       # default second value
447         mov     $NIL,%rsi       # default third value
448 #endif
449 multiple_value_return:
450         
451         .globl  GNAME(fun_end_breakpoint_trap)
452         .align  align_16byte,0x90
453 GNAME(fun_end_breakpoint_trap):
454         TRAP
455         .byte   trap_FunEndBreakpoint
456         hlt                     # We should never return here.
458         .globl  GNAME(fun_end_breakpoint_end)
459 GNAME(fun_end_breakpoint_end):
462         .globl  GNAME(do_pending_interrupt)
463         TYPE(GNAME(do_pending_interrupt))
464         .align  align_16byte,0x90
465 GNAME(do_pending_interrupt):
466         TRAP
467         .byte   trap_PendingInterrupt
468         ret
469         SIZE(GNAME(do_pending_interrupt))
471         .globl  GNAME(post_signal_tramp)
472         TYPE(GNAME(post_signal_tramp))
473         .align  align_16byte,0x90
474 GNAME(post_signal_tramp):
475         /* this is notionally the second half of a function whose first half
476          * doesn't exist.  This is where call_into_lisp returns when called 
477          * using return_to_lisp_function */
478         popq %r15
479         popq %r14
480         popq %r13
481         popq %r12
482         popq %r11
483         popq %r10
484         popq %r9
485         popq %r8
486         popq %rdi
487         popq %rsi
488         /* skip RBP and RSP */
489         popq %rbx
490         popq %rdx
491         popq %rcx
492         popq %rax
493         popfq
494         leave
495         ret
496         SIZE(GNAME(post_signal_tramp))
498 /* When LISP_FEATURE_C_STACK_IS_CONTROL_STACK, we cannot safely scrub
499  * the control stack from C, largely due to not knowing where the
500  * active stack frame ends.  On such platforms, we reimplement the
501  * core scrubbing logic in assembly, in this case here:
502  */
503         .text
504         .align  align_16byte,0x90
505         .globl GNAME(arch_scrub_control_stack)
506         TYPE(GNAME(arch_scrub_control_stack))
507 GNAME(arch_scrub_control_stack):
508         /* We are passed three parameters:
509          * A (struct thread *) in RDI,
510          * the address of the guard page in RSI, and
511          * the address of the hard guard page in RDX.
512          * We may trash RAX, RCX, and R8-R11 with impunity.
513          * [RSP] is our return address, [RSP-8] is the first
514          * stack slot to scrub. */
516         /* We start by setting up our scrub pointer in RAX, our
517          * guard page upper bound in R8, and our hard guard
518          * page upper bound in R9. */
519         lea     -8(%rsp), %rax
520 #ifdef LISP_FEATURE_DARWIN
521         mov     GSYM(GNAME(os_vm_page_size)),%r9
522 #else
523         mov     os_vm_page_size,%r9
524 #endif
525         lea     (%rsi,%r9), %r8
526         lea     (%rdx,%r9), %r9
528         /* Now we begin our main scrub loop. */
529 ascs_outer_loop:
531         /* If we're about to scrub the hard guard page, exit. */
532         cmp     %r9, %rax
533         jae     ascs_check_guard_page
534         cmp     %rax, %rdx
535         jbe     ascs_finished
537 ascs_check_guard_page:
538         /* If we're about to scrub the guard page, and the guard
539          * page is protected, exit. */
540         cmp     %r8, %rax
541         jae     ascs_clear_loop
542         cmp     %rax, %rsi
543         ja      ascs_clear_loop
544         cmpq    $(NIL), THREAD_CONTROL_STACK_GUARD_PAGE_PROTECTED_OFFSET(%rdi)
545         jne     ascs_finished
547         /* Clear memory backwards to the start of the (4KiB) page */
548 ascs_clear_loop:
549         movq    $0, (%rax)
550         test    $0xfff, %rax
551         lea     -8(%rax), %rax
552         jnz     ascs_clear_loop
554         /* If we're about to hit the hard guard page, exit. */
555         cmp     %r9, %rax
556         jae     ascs_finished
558         /* If the next (previous?) 4KiB page contains a non-zero
559          * word, continue scrubbing. */
560 ascs_check_loop:
561         testq   $-1, (%rax)
562         jnz     ascs_outer_loop
563         test    $0xfff, %rax
564         lea     -8(%rax), %rax
565         jnz     ascs_check_loop
567 ascs_finished:
568         ret
569         SIZE(GNAME(arch_scrub_control_stack))