Remove more disassembler bogosity
[sbcl.git] / src / runtime / x86-64-assem.S
blob497cf057cfe016d4da7e564a5e2161f0043018c9
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/static-symbols.h"
22 #include "genesis/thread.h"
23         
24 /* Minimize conditionalization for different OS naming schemes. */
25 #if defined __linux__  || defined LISP_FEATURE_FREEBSD || defined __OpenBSD__ || defined __NetBSD__ || defined __sun || defined _WIN64 || defined __DragonFly__
26 #define GNAME(var) var
27 #else
28 #define GNAME(var) _##var
29 #endif
31 /* Get the right type of alignment. Linux, FreeBSD and OpenBSD
32  * want alignment in bytes. */
33 #if defined(__linux__) || defined(LISP_FEATURE_FREEBSD) || defined(__OpenBSD__) || defined __NetBSD__ || defined(__sun) || defined _WIN64 || defined(__DragonFly__)
34 #define align_4byte     4
35 #define align_8byte     8
36 #define align_16byte    16
37 #define align_32byte    32
38 #define align_page      32768
39 #else
40 #define align_4byte     2
41 #define align_8byte     3
42 #define align_16byte    4       
43 #define align_page      15
44 #endif                  
47  * The assembler used for win32 doesn't like .type or .size directives,
48  * so we want to conditionally kill them out. So let's wrap them in macros
49  * that are defined to be no-ops on win32. Hopefully this still works on
50  * other platforms.
51  */
52 #if !defined(LISP_FEATURE_WIN32) && !defined(LISP_FEATURE_DARWIN)
53 #define TYPE(name) .type name,@function
54 #define SIZE(name) .size name,.-name
55 #define DOLLAR(name) $(name)
56 #else
57 #define TYPE(name)
58 #define SIZE(name)
59 #endif
62  * x86/darwin (as of MacOS X 10.4.5) doesn't reliably fire signal
63  * handlers (SIGTRAP or Mach exception handlers) for 0xCC, wo we have
64  * to use ud2 instead. ud2 is an undefined opcode, #x0b0f, or
65  * 0F 0B in low-endian notation, that causes SIGILL to fire. We check
66  * for this instruction in the SIGILL handler and if we see it, we
67  * advance the EIP by two bytes to skip over ud2 instruction and
68  * call sigtrap_handler. */
69 #if defined(LISP_FEATURE_UD2_BREAKPOINTS)
70 #define TRAP ud2
71 #else
72 #define TRAP int3
73 #endif
76  * More Apple assembler hacks
77  */
79 #if defined(LISP_FEATURE_DARWIN)
80 /* global symbol x86-64 sym(%rip) hack:*/
81 #define GSYM(name) name(%rip)
82 #else
83 #define GSYM(name) $name
84 #endif
86         
87         .text
88         .globl  GNAME(all_threads)
89         
90         .text   
91         .globl  GNAME(call_into_lisp_first_time)
92         TYPE(GNAME(call_into_lisp_first_time))
93                 
94 /* We don't worry too much about saving registers 
95  * here, because we never expect to return from the initial call to lisp 
96  * anyway */
97         
98         .align  align_16byte,0x90
99 GNAME(call_into_lisp_first_time):
100         push    %rbp            # Save old frame pointer.
101         mov     %rsp,%rbp       # Establish new frame.
102 #if defined(LISP_FEATURE_DARWIN)
103         movq    GSYM(GNAME(all_threads)),%rax
104 #else
105         movq    GNAME(all_threads),%rax
106 #endif
107         mov     THREAD_CONTROL_STACK_END_OFFSET(%rax) ,%rsp
108         jmp     Lstack
110         .text   
111         .globl  GNAME(call_into_lisp)
112         TYPE(GNAME(call_into_lisp))
113                 
115  * amd64 calling convention: C expects that
116  * arguments go in rdi rsi rdx rcx r8 r9
117  * return values in rax rdx
118  * callee saves rbp rbx r12-15 if it uses them
119  */
120 #ifdef LISP_FEATURE_WIN32
121 # define SUPPORT_FOMIT_FRAME_POINTER
122 #endif
123         .align  align_16byte,0x90
124 GNAME(call_into_lisp):
125 #ifdef SUPPORT_FOMIT_FRAME_POINTER
126         mov     %rbp,%rax
127 #endif
128         push    %rbp            # Save old frame pointer.
129         mov     %rsp,%rbp       # Establish new frame.
130 Lstack:
131 #ifdef SUPPORT_FOMIT_FRAME_POINTER
132         /* If called through call_into_lisp_first_time, %r15 becomes invalid
133          * here, but we will not return in that case. */
134         push    %r15
135         mov     %rax,%r15
136 #endif
137         /* FIXME x86 saves FPU state here */
138         push    %rbx    # these regs are callee-saved according to C
139         push    %r12    # so must be preserved and restored when 
140         push    %r13    # the lisp function returns
141         push    %r14    #
142         push    %r15    #
144         mov     %rsp,%rbx       # remember current stack
145         push    %rbx            # Save entry stack on (maybe) new stack.
147         push    %rdi    # args from C
148         push    %rsi    #
149         push    %rdx    #
150 #ifdef LISP_FEATURE_SB_THREAD
151 # ifdef SUPPORT_FOMIT_FRAME_POINTER
152         mov     (%rbp),%rcx
153         sub     $32,%rsp
154         call    GNAME(carry_frame_pointer)
155         add     $32,%rsp
156         mov     %rax,(%rbp)
157 # endif
158 #ifdef LISP_FEATURE_GCC_TLS
159         movq    %fs:0, %rax
160         movq    GNAME(current_thread)@TPOFF(%rax), %r12
161 #else
162 #ifdef LISP_FEATURE_DARWIN
163         mov     GSYM(GNAME(specials)),%rdi
164 #else
165         mov     specials,%rdi
166 #endif
167         call    GNAME(pthread_getspecific)
168         mov     %rax,%r12
169 #endif
170 #endif
171         pop     %rcx    # num args
172         pop     %rbx    # arg vector
173         pop     %rax    # function ptr/lexenv
175         xor     %rdx,%rdx       # clear any descriptor registers 
176         xor     %rdi,%rdi       # that we can't be sure we'll 
177         xor     %rsi,%rsi       # initialise properly.  XX do r8-r15 too?
178         cmp     $0,%rcx
179         # It's tempting to think 'cmov' for these assignments, but don't:
180         # cmov does a memory cycle whether or not it moves, succumbing to
181         # a classic buffer overrun bug if argv[] is "badly" placed.
182         je      Ldone
183         mov     0(%rbx),%rdx    # arg0
184         cmp     $1,%rcx
185         je      Ldone
186         mov     8(%rbx),%rdi    # arg1
187         cmp     $2,%rcx
188         je      Ldone
189         mov     16(%rbx),%rsi   # arg2
190 Ldone:  
191         shl     $(N_FIXNUM_TAG_BITS),%rcx       # (fixnumize num-args)
192         /* Registers rax, rcx, rdx, rdi, and rsi are now live. */
193         xor     %rbx,%rbx       # available
195         /* Alloc new frame. */
196         push    %rbp            # Dummy for return address
197         push    %rbp            # fp in save location S1
198         mov     %rsp,%rbp       # The current sp marks start of new frame.
199         sub     $8,%rsp         # Ensure 3 slots are allocated, two above.
201 Lcall:
202         call    *CLOSURE_FUN_OFFSET(%rax)
203         
204         /* If the function returned multiple values, it will return to
205            this point.  Lose them */
206         jnc     LsingleValue    
207         mov     %rbx, %rsp
208 LsingleValue:   
210 /* Restore the stack, in case there was a stack change. */
211         pop     %rsp            # c-sp
213 /* Restore C regs */
214         pop     %r15
215         pop     %r14
216         pop     %r13
217         pop     %r12
218         pop     %rbx
220 /* FIXME Restore the NPX state. */
222         mov     %rdx,%rax       # c-val
223 #ifdef SUPPORT_FOMIT_FRAME_POINTER
224         mov     %r15,%rbp       # orig rbp
225         pop     %r15            # orig r15
226         add     $8,%rsp         # no need for saved (overridden) rbp
227 #else
228         leave
229 #endif
230         ret
231         SIZE(GNAME(call_into_lisp))
233 /* Our call-site does not take care of caller-saved xmm registers, so it
234  * falls to us spill them beforing hopping into C.
236  * We simply save all of them.
238  * (But for the sake of completeness, here is my understanding of the specs:)
239  *                     System V       Microsoft
240  * argument passing    xmm0-7         xmm0-3
241  * caller-saved        xmm8-15        xmm4-5
242  * callee-saved        -              xmm6-15
244  *  --DFL */
246 #define stkxmmsave(n) movaps %xmm##n, n*16(%rsp)
247 #define stkxmmload(n) movaps n*16(%rsp), %xmm##n
248 #define map_all_xmm(op) \
249         op(0);op(1);op(2);op(3);op(4);op(5);op(6);op(7); \
250     op(8);op(9);op(10);op(11);op(12);op(13);op(14);op(15);
252         .text
253         .align  align_16byte,0x90
254         .globl  GNAME(alloc_tramp)
255         TYPE(GNAME(alloc_tramp))
256 GNAME(alloc_tramp):
257         cld
258         push    %rbp            # Save old frame pointer.
259         mov     %rsp,%rbp       # Establish new frame.
260         and     $-32,%rsp
261         sub     $16*16,%rsp
262         map_all_xmm(stkxmmsave)
263         # Doing rax twice is to maintain 16-byte stack alignment.
264         push    %rax
265         push    %rax
266         push    %rcx
267         push    %rdx
268         push    %rsi
269         push    %rdi
270         push    %r8
271         push    %r9
272         push    %r10
273         push    %r11
274         # r12 through r15 are callee-saved
275         mov     16(%rbp),%rdi
276         call    GNAME(alloc)
277         mov     %rax,16(%rbp)
278         pop     %r11
279         pop     %r10
280         pop     %r9
281         pop     %r8
282         pop     %rdi
283         pop     %rsi
284         pop     %rdx
285         pop     %rcx
286         pop     %rax
287         pop     %rax # corresponds to "extra" push
288         map_all_xmm(stkxmmload)
289         mov     %rbp,%rsp
290         pop     %rbp
291         ret
292         SIZE(GNAME(alloc_tramp))
295  * fun-end breakpoint magic
296  */
299  * For an explanation of the magic involved in function-end
300  * breakpoints, see the implementation in ppc-assem.S.
301  */
303         .text
304         .globl  GNAME(fun_end_breakpoint_guts)
305         .align  align_16byte
306 GNAME(fun_end_breakpoint_guts):
307         /* Multiple Value return */
308         jc      multiple_value_return
309         /* Single value return: The eventual return will now use the
310            multiple values return convention but with a return values
311            count of one. */
312         mov     %rsp,%rbx       # Setup ebx - the ofp.
313         sub     $8,%rsp         # Allocate one stack slot for the return value
314         mov     $(1 << N_FIXNUM_TAG_BITS),%rcx          # Setup ecx for one return value.
315         mov     GSYM(NIL),%rdi  # default second value
316         mov     GSYM(NIL),%rsi  # default third value
317 multiple_value_return:
318         
319         .globl  GNAME(fun_end_breakpoint_trap)
320         .align  align_16byte,0x90
321 GNAME(fun_end_breakpoint_trap):
322         TRAP
323         .byte   trap_FunEndBreakpoint
324         hlt                     # We should never return here.
326         .globl  GNAME(fun_end_breakpoint_end)
327 GNAME(fun_end_breakpoint_end):
330         .globl  GNAME(do_pending_interrupt)
331         TYPE(GNAME(do_pending_interrupt))
332         .align  align_16byte,0x90
333 GNAME(do_pending_interrupt):
334         TRAP
335         .byte   trap_PendingInterrupt
336         ret
337         SIZE(GNAME(do_pending_interrupt))
339         .globl  GNAME(post_signal_tramp)
340         TYPE(GNAME(post_signal_tramp))
341         .align  align_16byte,0x90
342 GNAME(post_signal_tramp):
343         /* this is notionally the second half of a function whose first half
344          * doesn't exist.  This is where call_into_lisp returns when called 
345          * using return_to_lisp_function */
346         popq %r15
347         popq %r14
348         popq %r13
349         popq %r12
350         popq %r11
351         popq %r10
352         popq %r9
353         popq %r8
354         popq %rdi
355         popq %rsi
356         /* skip RBP and RSP */
357         popq %rbx
358         popq %rdx
359         popq %rcx
360         popq %rax
361         popfq
362         leave
363         ret
364         SIZE(GNAME(post_signal_tramp))
366 /* When LISP_FEATURE_C_STACK_IS_CONTROL_STACK, we cannot safely scrub
367  * the control stack from C, largely due to not knowing where the
368  * active stack frame ends.  On such platforms, we reimplement the
369  * core scrubbing logic in assembly, in this case here:
370  */
371         .text
372         .align  align_16byte,0x90
373         .globl GNAME(arch_scrub_control_stack)
374         TYPE(GNAME(arch_scrub_control_stack))
375 GNAME(arch_scrub_control_stack):
376         /* We are passed three parameters:
377          * A (struct thread *) in RDI,
378          * the address of the guard page in RSI, and
379          * the address of the hard guard page in RDX.
380          * We may trash RAX, RCX, and R8-R11 with impunity.
381          * [RSP] is our return address, [RSP-8] is the first
382          * stack slot to scrub. */
384         /* We start by setting up our scrub pointer in RAX, our
385          * guard page upper bound in R8, and our hard guard
386          * page upper bound in R9. */
387         lea     -8(%rsp), %rax
388 #ifdef LISP_FEATURE_DARWIN
389         mov     GSYM(GNAME(os_vm_page_size)),%r9
390 #else
391         mov     os_vm_page_size,%r9
392 #endif
393         lea     (%rsi,%r9), %r8
394         lea     (%rdx,%r9), %r9
396         /* Now we begin our main scrub loop. */
397 ascs_outer_loop:
399         /* If we're about to scrub the hard guard page, exit. */
400         cmp     %r9, %rax
401         jae     ascs_check_guard_page
402         cmp     %rax, %rdx
403         jbe     ascs_finished
405 ascs_check_guard_page:
406         /* If we're about to scrub the guard page, and the guard
407          * page is protected, exit. */
408         cmp     %r8, %rax
409         jae     ascs_clear_loop
410         cmp     %rax, %rsi
411         ja      ascs_clear_loop
412         cmpq    $(NIL), THREAD_CONTROL_STACK_GUARD_PAGE_PROTECTED_OFFSET(%rdi)
413         jne     ascs_finished
415         /* Clear memory backwards to the start of the (4KiB) page */
416 ascs_clear_loop:
417         movq    $0, (%rax)
418         test    $0xfff, %rax
419         lea     -8(%rax), %rax
420         jnz     ascs_clear_loop
422         /* If we're about to hit the hard guard page, exit. */
423         cmp     %r9, %rax
424         jae     ascs_finished
426         /* If the next (previous?) 4KiB page contains a non-zero
427          * word, continue scrubbing. */
428 ascs_check_loop:
429         testq   $-1, (%rax)
430         jnz     ascs_outer_loop
431         test    $0xfff, %rax
432         lea     -8(%rax), %rax
433         jnz     ascs_check_loop
435 ascs_finished:
436         ret
437         SIZE(GNAME(arch_scrub_control_stack))