2 * very-low-level utilities for runtime support
6 * This software is part of the SBCL system. See the README file for
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.
16 #define LANGUAGE_ASSEMBLY
17 #include "genesis/config.h"
20 #include "genesis/closure.h"
21 #include "genesis/static-symbols.h"
22 #include "genesis/thread.h"
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
28 #define GNAME(var) _##var
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__)
36 #define align_16byte 16
37 #define align_32byte 32
38 #define align_page 32768
42 #define align_16byte 4
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
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)
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)
76 * More Apple assembler hacks
79 #if defined(LISP_FEATURE_DARWIN)
80 /* global symbol x86-64 sym(%rip) hack:*/
81 #define GSYM(name) name(%rip)
83 #define GSYM(name) $name
88 .globl GNAME(all_threads)
91 .globl GNAME(call_into_lisp_first_time)
92 TYPE(GNAME(call_into_lisp_first_time))
94 /* We don't worry too much about saving registers
95 * here, because we never expect to return from the initial call to lisp
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
105 movq GNAME(all_threads),%rax
107 mov THREAD_CONTROL_STACK_END_OFFSET(%rax) ,%rsp
111 .globl GNAME(call_into_lisp)
112 TYPE(GNAME(call_into_lisp))
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
120 #ifdef LISP_FEATURE_WIN32
121 # define SUPPORT_FOMIT_FRAME_POINTER
123 .align align_16byte,0x90
124 GNAME(call_into_lisp):
125 #ifdef SUPPORT_FOMIT_FRAME_POINTER
128 push %rbp # Save old frame pointer.
129 mov %rsp,%rbp # Establish new frame.
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. */
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
144 mov %rsp,%rbx # remember current stack
145 push %rbx # Save entry stack on (maybe) new stack.
147 push %rdi # args from C
150 #ifdef LISP_FEATURE_SB_THREAD
151 # ifdef SUPPORT_FOMIT_FRAME_POINTER
154 call GNAME(carry_frame_pointer)
158 #ifdef LISP_FEATURE_GCC_TLS
160 movq GNAME(current_thread)@TPOFF(%rax), %r12
162 #ifdef LISP_FEATURE_DARWIN
163 mov GSYM(GNAME(specials)),%rdi
167 call GNAME(pthread_getspecific)
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?
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.
183 mov 0(%rbx),%rdx # arg0
186 mov 8(%rbx),%rdi # arg1
189 mov 16(%rbx),%rsi # arg2
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.
202 call *CLOSURE_FUN_OFFSET(%rax)
204 /* If the function returned multiple values, it will return to
205 this point. Lose them */
210 /* Restore the stack, in case there was a stack change. */
220 /* FIXME Restore the NPX state. */
222 mov %rdx,%rax # c-val
223 #ifdef SUPPORT_FOMIT_FRAME_POINTER
224 mov %r15,%rbp # orig rbp
226 add $8,%rsp # no need for saved (overridden) rbp
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:)
240 * argument passing xmm0-7 xmm0-3
241 * caller-saved xmm8-15 xmm4-5
242 * callee-saved - xmm6-15
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);
253 .align align_16byte,0x90
254 .globl GNAME(alloc_tramp)
255 TYPE(GNAME(alloc_tramp))
258 push %rbp # Save old frame pointer.
259 mov %rsp,%rbp # Establish new frame.
262 map_all_xmm(stkxmmsave)
263 # Doing rax twice is to maintain 16-byte stack alignment.
274 # r12 through r15 are callee-saved
287 pop %rax # corresponds to "extra" push
288 map_all_xmm(stkxmmload)
292 SIZE(GNAME(alloc_tramp))
295 * fun-end breakpoint magic
299 * For an explanation of the magic involved in function-end
300 * breakpoints, see the implementation in ppc-assem.S.
304 .globl GNAME(fun_end_breakpoint_guts)
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
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:
319 .globl GNAME(fun_end_breakpoint_trap)
320 .align align_16byte,0x90
321 GNAME(fun_end_breakpoint_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):
335 .byte trap_PendingInterrupt
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 */
356 /* skip RBP and RSP */
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:
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. */
388 #ifdef LISP_FEATURE_DARWIN
389 mov GSYM(GNAME(os_vm_page_size)),%r9
391 mov os_vm_page_size,%r9
396 /* Now we begin our main scrub loop. */
399 /* If we're about to scrub the hard guard page, exit. */
401 jae ascs_check_guard_page
405 ascs_check_guard_page:
406 /* If we're about to scrub the guard page, and the guard
407 * page is protected, exit. */
412 cmpq $(NIL), THREAD_CONTROL_STACK_GUARD_PAGE_PROTECTED_OFFSET(%rdi)
415 /* Clear memory backwards to the start of the (4KiB) page */
422 /* If we're about to hit the hard guard page, exit. */
426 /* If the next (previous?) 4KiB page contains a non-zero
427 * word, continue scrubbing. */
437 SIZE(GNAME(arch_scrub_control_stack))