Fix warning under cmucl host
[sbcl.git] / src / runtime / x86-64-assem.S
blobb1ecb6bd96215d5b48357db94d58e108f0c6e86a
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         .text   
94         .globl  GNAME(call_into_lisp_first_time)
95         TYPE(GNAME(call_into_lisp_first_time))
96                 
97 /* We don't worry too much about saving registers 
98  * here, because we never expect to return from the initial call to lisp 
99  * anyway */
100         
101         .align  align_16byte,0x90
102 GNAME(call_into_lisp_first_time):
103         push    %rbp            # Save old frame pointer.
104         mov     %rsp,%rbp       # Establish new frame.
105 #if defined(LISP_FEATURE_DARWIN)
106         movq    GSYM(GNAME(all_threads)),%rax
107 #else
108         movq    GNAME(all_threads),%rax
109 #endif
110         mov     THREAD_CONTROL_STACK_END_OFFSET(%rax) ,%rsp
111         jmp     Lstack
113         .text   
114         .globl  GNAME(call_into_lisp)
115         TYPE(GNAME(call_into_lisp))
116                 
118  * amd64 calling convention: C expects that
119  * arguments go in rdi rsi rdx rcx r8 r9
120  * return values in rax rdx
121  * callee saves rbp rbx r12-15 if it uses them
122  */
123 #ifdef LISP_FEATURE_WIN32
124 # define SUPPORT_FOMIT_FRAME_POINTER
125 #endif
126         .align  align_16byte,0x90
127 GNAME(call_into_lisp):
128 #ifdef SUPPORT_FOMIT_FRAME_POINTER
129         mov     %rbp,%rax
130 #endif
131         push    %rbp            # Save old frame pointer.
132         mov     %rsp,%rbp       # Establish new frame.
133 Lstack:
134 #ifdef SUPPORT_FOMIT_FRAME_POINTER
135         /* If called through call_into_lisp_first_time, %r15 becomes invalid
136          * here, but we will not return in that case. */
137         push    %r15
138         mov     %rax,%r15
139 #endif
140         /* FIXME x86 saves FPU state here */
141         push    %rbx    # these regs are callee-saved according to C
142         push    %r12    # so must be preserved and restored when 
143         push    %r13    # the lisp function returns
144         push    %r14    #
145         push    %r15    #
147         mov     %rsp,%rbx       # remember current stack
148         push    %rbx            # Save entry stack on (maybe) new stack.
150         push    %rdi    # args from C
151         push    %rsi    #
152         push    %rdx    #
153 #ifdef LISP_FEATURE_SB_THREAD
154 # ifdef SUPPORT_FOMIT_FRAME_POINTER
155         mov     (%rbp),%rcx
156         sub     $32,%rsp
157         call    GNAME(carry_frame_pointer)
158         add     $32,%rsp
159         mov     %rax,(%rbp)
160 # endif
161 #ifdef LISP_FEATURE_GCC_TLS
162         movq    %fs:0, %rax
163         movq    GNAME(current_thread)@TPOFF(%rax), %r12
164 #else
165 #ifdef LISP_FEATURE_DARWIN
166         mov     GSYM(GNAME(specials)),%rdi
167 #else
168         mov     specials,%rdi
169 #endif
170         call    GNAME(pthread_getspecific)
171         mov     %rax,%r12
172 #endif
173 #endif
174         pop     %rcx    # num args
175         pop     %rbx    # arg vector
176         pop     %rax    # function ptr/lexenv
178         xor     %rdx,%rdx       # clear any descriptor registers 
179         xor     %rdi,%rdi       # that we can't be sure we'll 
180         xor     %rsi,%rsi       # initialise properly.  XX do r8-r15 too?
181         cmp     $0,%rcx
182         # It's tempting to think 'cmov' for these assignments, but don't:
183         # cmov does a memory cycle whether or not it moves, succumbing to
184         # a classic buffer overrun bug if argv[] is "badly" placed.
185         je      Ldone
186         mov     0(%rbx),%rdx    # arg0
187         cmp     $1,%rcx
188         je      Ldone
189         mov     8(%rbx),%rdi    # arg1
190         cmp     $2,%rcx
191         je      Ldone
192         mov     16(%rbx),%rsi   # arg2
193 Ldone:  
194         shl     $(N_FIXNUM_TAG_BITS),%rcx       # (fixnumize num-args)
195         /* Registers rax, rcx, rdx, rdi, and rsi are now live. */
196         xor     %rbx,%rbx       # available
198         /* Alloc new frame. */
199         push    %rbp            # Dummy for return address
200         push    %rbp            # fp in save location S1
201         mov     %rsp,%rbp       # The current sp marks start of new frame.
202         sub     $8,%rsp         # Ensure 3 slots are allocated, two above.
204 Lcall:
205         call    *CLOSURE_FUN_OFFSET(%rax)
206         
207         /* If the function returned multiple values, it will return to
208            this point.  Lose them */
209         jnc     LsingleValue    
210         mov     %rbx, %rsp
211 LsingleValue:   
213 /* Restore the stack, in case there was a stack change. */
214         pop     %rsp            # c-sp
216 /* Restore C regs */
217         pop     %r15
218         pop     %r14
219         pop     %r13
220         pop     %r12
221         pop     %rbx
223 /* FIXME Restore the NPX state. */
225         mov     %rdx,%rax       # c-val
226 #ifdef SUPPORT_FOMIT_FRAME_POINTER
227         mov     %r15,%rbp       # orig rbp
228         pop     %r15            # orig r15
229         add     $8,%rsp         # no need for saved (overridden) rbp
230 #else
231         leave
232 #endif
233         ret
234         SIZE(GNAME(call_into_lisp))
236 /* support for saving and restoring the NPX state from C */
237         .text
238         .globl  GNAME(fpu_save)
239         TYPE(GNAME(fpu_save))
240         .align  align_16byte,0x90
241 GNAME(fpu_save):
242         fnsave  (%rdi)          # Save the NPX state. (resets NPX)
243         ret
244         SIZE(GNAME(fpu_save))
246         .globl  GNAME(fpu_restore)
247         TYPE(GNAME(fpu_restore))
248         .align  align_16byte,0x90
249 GNAME(fpu_restore):
250         frstor  (%rdi)          # Restore the NPX state.
251         ret
252         SIZE(GNAME(fpu_restore))
255  * the undefined-function trampoline
256  */
257         .text
258         .align  align_16byte,0x90
259         .globl  GNAME(undefined_tramp)
260         TYPE(GNAME(undefined_tramp))
261 GNAME(undefined_tramp):
262         pop     8(%rbp)         # Save return PC for backtrace.
263         TRAP
264         .byte   trap_Error
265         .byte   2
266         .byte   UNDEFINED_FUN_ERROR
267         .byte   sc_DescriptorReg # eax in the Descriptor-reg SC
268         ret
269         SIZE(GNAME(undefined_tramp))
271         .text
272         .align  align_16byte,0x90
273         .globl  GNAME(undefined_alien_function)
274         TYPE(GNAME(undefined_alien_function))
275 GNAME(undefined_alien_function):
276         pop     8(%rbp)         # Save return PC for backtrace.
277         TRAP
278         .byte   trap_Error
279         .byte   4
280         .byte   UNDEFINED_ALIEN_FUN_ERROR
281         /* Encode RBX
282            FIXME: make independt of the encoding changes. */
283         .byte   0xFE 
284         .byte   0x9F
285         .byte   0x01
286         ret
287         SIZE(GNAME(undefined_alien_function))
289 /* KLUDGE: FIND-ESCAPED-FRAME (SYS:SRC;CODE;DEBUG-INT.LISP) needs
290  * to know the name of the function immediately following the
291  * undefined-function trampoline. */
293 /* Our call-site does not take care of caller-saved xmm registers, so it
294  * falls to us spill them beforing hopping into C.
296  * We simply save all of them.
298  * (But for the sake of completeness, here is my understanding of the specs:)
299  *                     System V       Microsoft
300  * argument passing    xmm0-7         xmm0-3
301  * caller-saved        xmm8-15        xmm4-5
302  * callee-saved        -              xmm6-15
304  *  --DFL */
306 #define stkxmmsave(n) movaps %xmm##n, n*16(%rsp)
307 #define stkxmmload(n) movaps n*16(%rsp), %xmm##n
308 #define map_all_xmm(op) \
309         op(0);op(1);op(2);op(3);op(4);op(5);op(6);op(7); \
310     op(8);op(9);op(10);op(11);op(12);op(13);op(14);op(15);
312         .text
313         .align  align_16byte,0x90
314         .globl  GNAME(alloc_tramp)
315         TYPE(GNAME(alloc_tramp))
316 GNAME(alloc_tramp):
317         cld
318         push    %rbp            # Save old frame pointer.
319         mov     %rsp,%rbp       # Establish new frame.
320         and     $-32,%rsp
321         sub     $16*16,%rsp
322         map_all_xmm(stkxmmsave)
323         # Doing rax twice is to maintain 16-byte stack alignment.
324         push    %rax
325         push    %rax
326         push    %rcx
327         push    %rdx
328         push    %rsi
329         push    %rdi
330         push    %r8
331         push    %r9
332         push    %r10
333         push    %r11
334         # r12 through r15 are callee-saved
335         mov     16(%rbp),%rdi
336         call    GNAME(alloc)
337         mov     %rax,16(%rbp)
338         pop     %r11
339         pop     %r10
340         pop     %r9
341         pop     %r8
342         pop     %rdi
343         pop     %rsi
344         pop     %rdx
345         pop     %rcx
346         pop     %rax
347         pop     %rax # corresponds to "extra" push
348         map_all_xmm(stkxmmload)
349         mov     %rbp,%rsp
350         pop     %rbp
351         ret
352         SIZE(GNAME(alloc_tramp))
354                 
356  * the closure trampoline - entered when a global function is a closure
357  * and the function is called "by name" (normally, as when it is the
358  * head of a form) via an FDEFN. Register %RAX holds the fdefn address,
359  * but the simple-fun which underlies the closure expects %RAX to be the
360  * closure itself. So we grab the closure out of the fdefn pointed to,
361  * then jump to the simple-fun that the closure points to.
362  */
363         .text
364         .align  align_16byte,0x90
365         .globl  GNAME(closure_tramp)
366         TYPE(GNAME(closure_tramp))
367 GNAME(closure_tramp):
368         mov     FDEFN_FUN_OFFSET(%rax),%rax
369         jmp     *CLOSURE_FUN_OFFSET(%rax)
370         SIZE(GNAME(closure_tramp))
372         .text
373         .align  align_16byte,0x90
374         .globl  GNAME(funcallable_instance_tramp)
375 #if !defined(LISP_FEATURE_DARWIN)
376         TYPE(GNAME(funcallable_instance_tramp))
377 #endif
378         GNAME(funcallable_instance_tramp):
379         mov     FUNCALLABLE_INSTANCE_FUNCTION_OFFSET(%rax),%rax
380         /* KLUDGE: on this platform, whatever kind of function is in %rax
381          * now, the first word of it contains the address to jump to. */
382         jmp     *CLOSURE_FUN_OFFSET(%rax)
383 #if !defined(LISP_FEATURE_DARWIN)
384         SIZE(GNAME(funcallable_instance_tramp))
385 #endif
387  * fun-end breakpoint magic
388  */
391  * For an explanation of the magic involved in function-end
392  * breakpoints, see the implementation in ppc-assem.S.
393  */
395         .text
396         .globl  GNAME(fun_end_breakpoint_guts)
397         .align  align_16byte
398 GNAME(fun_end_breakpoint_guts):
399         /* Multiple Value return */
400         jc      multiple_value_return
401         /* Single value return: The eventual return will now use the
402            multiple values return convention but with a return values
403            count of one. */
404         mov     %rsp,%rbx       # Setup ebx - the ofp.
405         sub     $8,%rsp         # Allocate one stack slot for the return value
406         mov     $(1 << N_FIXNUM_TAG_BITS),%rcx          # Setup ecx for one return value.
407         mov     GSYM(NIL),%rdi  # default second value
408         mov     GSYM(NIL),%rsi  # default third value
409 multiple_value_return:
410         
411         .globl  GNAME(fun_end_breakpoint_trap)
412         .align  align_16byte,0x90
413 GNAME(fun_end_breakpoint_trap):
414         TRAP
415         .byte   trap_FunEndBreakpoint
416         hlt                     # We should never return here.
418         .globl  GNAME(fun_end_breakpoint_end)
419 GNAME(fun_end_breakpoint_end):
422         .globl  GNAME(do_pending_interrupt)
423         TYPE(GNAME(do_pending_interrupt))
424         .align  align_16byte,0x90
425 GNAME(do_pending_interrupt):
426         TRAP
427         .byte   trap_PendingInterrupt
428         ret
429         SIZE(GNAME(do_pending_interrupt))
431         .globl  GNAME(post_signal_tramp)
432         TYPE(GNAME(post_signal_tramp))
433         .align  align_16byte,0x90
434 GNAME(post_signal_tramp):
435         /* this is notionally the second half of a function whose first half
436          * doesn't exist.  This is where call_into_lisp returns when called 
437          * using return_to_lisp_function */
438         popq %r15
439         popq %r14
440         popq %r13
441         popq %r12
442         popq %r11
443         popq %r10
444         popq %r9
445         popq %r8
446         popq %rdi
447         popq %rsi
448         /* skip RBP and RSP */
449         popq %rbx
450         popq %rdx
451         popq %rcx
452         popq %rax
453         popfq
454         leave
455         ret
456         SIZE(GNAME(post_signal_tramp))
458 /* When LISP_FEATURE_C_STACK_IS_CONTROL_STACK, we cannot safely scrub
459  * the control stack from C, largely due to not knowing where the
460  * active stack frame ends.  On such platforms, we reimplement the
461  * core scrubbing logic in assembly, in this case here:
462  */
463         .text
464         .align  align_16byte,0x90
465         .globl GNAME(arch_scrub_control_stack)
466         TYPE(GNAME(arch_scrub_control_stack))
467 GNAME(arch_scrub_control_stack):
468         /* We are passed three parameters:
469          * A (struct thread *) in RDI,
470          * the address of the guard page in RSI, and
471          * the address of the hard guard page in RDX.
472          * We may trash RAX, RCX, and R8-R11 with impunity.
473          * [RSP] is our return address, [RSP-8] is the first
474          * stack slot to scrub. */
476         /* We start by setting up our scrub pointer in RAX, our
477          * guard page upper bound in R8, and our hard guard
478          * page upper bound in R9. */
479         lea     -8(%rsp), %rax
480 #ifdef LISP_FEATURE_DARWIN
481         mov     GSYM(GNAME(os_vm_page_size)),%r9
482 #else
483         mov     os_vm_page_size,%r9
484 #endif
485         lea     (%rsi,%r9), %r8
486         lea     (%rdx,%r9), %r9
488         /* Now we begin our main scrub loop. */
489 ascs_outer_loop:
491         /* If we're about to scrub the hard guard page, exit. */
492         cmp     %r9, %rax
493         jae     ascs_check_guard_page
494         cmp     %rax, %rdx
495         jbe     ascs_finished
497 ascs_check_guard_page:
498         /* If we're about to scrub the guard page, and the guard
499          * page is protected, exit. */
500         cmp     %r8, %rax
501         jae     ascs_clear_loop
502         cmp     %rax, %rsi
503         ja      ascs_clear_loop
504         cmpq    $(NIL), THREAD_CONTROL_STACK_GUARD_PAGE_PROTECTED_OFFSET(%rdi)
505         jne     ascs_finished
507         /* Clear memory backwards to the start of the (4KiB) page */
508 ascs_clear_loop:
509         movq    $0, (%rax)
510         test    $0xfff, %rax
511         lea     -8(%rax), %rax
512         jnz     ascs_clear_loop
514         /* If we're about to hit the hard guard page, exit. */
515         cmp     %r9, %rax
516         jae     ascs_finished
518         /* If the next (previous?) 4KiB page contains a non-zero
519          * word, continue scrubbing. */
520 ascs_check_loop:
521         testq   $-1, (%rax)
522         jnz     ascs_outer_loop
523         test    $0xfff, %rax
524         lea     -8(%rax), %rax
525         jnz     ascs_check_loop
527 ascs_finished:
528         ret
529         SIZE(GNAME(arch_scrub_control_stack))