### x86-assem.S -*- Mode: Asm; -*- /** * $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/amd64-assem.S,v 1.1 2004/05/18 21:58:06 cwang Exp $ * * Authors: Paul F. Werkowski <pw@snoopy.mv.com> * Douglas T. Crosher * Cheuksan Edward Wang * * This code was written to support the port of CMU Common Lisp * to the AMD64 ISA and the Linux operating system. The * author has placed this code in the public domain September 1996. * */ #include "x86-validate.h" #define LANGUAGE_ASSEMBLY #include "internals.h" #include "lispregs.h" #if defined __FreeBSD__ #include <osreldate.h> #endif /* Minimize conditionalization for different OS naming schemes */ #if defined (__linux__) || (__FreeBSD_version >= 300000) || defined (__NetBSD__) #define GNAME(var) var #else #define GNAME(var) _##var #endif /* Get the right type of alignment. Linux wants alignment in bytes. */ #if defined (__linux__) || (__FreeBSD_version >= 300000) #define align_4byte 4 #define align_8byte 8 #define align_16byte 16 #else #define align_4byte 2 #define align_8byte 3 #define align_16byte 4 #endif .text .global GNAME(foreign_function_call_active) /* * The C function will preserve rbx, r12-15, and rbp across its * function call - rbx is used to save the return lisp address. * * Return values are in rax, or st(0) for * floats. * * It should work for lisp calls C calls lisp calls C .. */ .text .align align_16byte,0x90 .global GNAME(call_into_c) .type GNAME(call_into_c),@function GNAME(call_into_c): movl $1,GNAME(foreign_function_call_active) /* Save the return lisp address in rbx */ popq %rbx /* Setup the NPX for C */ fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) call *%rax # normal callout using Lisp stack movq %rax,%rcx # remember integer return value /* Check for a return FP value */ fxam fnstsw %eax andl $0x4500,%eax cmpl $0x4100,%eax jne Lfp_rtn_value /* The return value is in eax, or eax,edx? */ /* Setup the NPX stack for lisp */ fldz # insure no regs are empty fldz fldz fldz fldz fldz fldz fldz /* Restore the return value */ movq %rcx,%rax # maybe return value movl $0,GNAME(foreign_function_call_active) /* Return */ jmp *%rbx Lfp_rtn_value: /* The return result is in st(0) */ /* Setup the NPX stack for lisp, placing the result in st(0) */ fldz # insure no regs are empty fldz fldz fldz fldz fldz fldz fxch %st(7) # move the result back to st(0) /* Don't need to restore eax as the result is in st(0) */ movl $0,GNAME(foreign_function_call_active) /* Return */ jmp *%ebx .size GNAME(call_into_c), . - GNAME(call_into_c) .text .global GNAME(call_into_lisp) .type GNAME(call_into_lisp),@function /* The C conventions require that rbx, r12-r15, and rbp be preserved across function calls. */ /* The *alien-stack* pointer is setup on the first call_into_lisp when the stack changes. */ .align align_16byte,0x90 GNAME(call_into_lisp): pushq %rbp # save old frame pointer movq %rsp,%rbp # establish new frame /* Save the NPX state */ fwait # Catch any pending NPX exceptions. subq $108,%rsp # Make room for the NPX state. fnsave (%rsp) # Resets NPX movl (%rsp),%eax # Load NPX control word andl $0xfffff3ff,%eax # Set rounding mode to nearest #ifdef type_LongFloat orl $0x00000300,%eax # Set precision to 64 bits #else orl $0x00000200,%eax # Set precision to 53 bits #endif pushq %rax fldcw (%rsp) # Recover modes popq %rax fldz # insure no FP regs are empty fldz fldz fldz fldz fldz fldz fldz /* Save C regs: rbx r12-15 */ pushq %rbx pushq %r12 pushq %r13 pushq %r14 pushq %r15 /* clear descriptor regs */ xorq %rax,%rax # lexenv xorq %rbx,%rbx # available xorq %rcx,%rcx # arg count /* no longer in function call */ movl $0, GNAME(foreign_function_call_active) movq %rsp,%rbx # remember current stack cmpq $CONTROL_STACK_START,%rsp jbe ChangeToLispStack cmpq $CONTROL_STACK_END,%rsp jbe OnLispStack ChangeToLispStack: /* Setup the *alien-stack* pointer */ movq %rsp,ALIEN_STACK + SYMBOL_VALUE_OFFSET movq $CONTROL_STACK_END,%rsp # New stack OnLispStack: pushq %rbx # save entry stack on (maybe) new stack /* establish lisp args */ movq %rdi,%rax # lexenv? (C arg 1) xorq %rdi,%rdi # clear second arg (lisp) movq %rsi,%rbx # address of arg vec (C arg 2) xorq %rsi,%rsi # clear third arg (lisp) movq %rdx,%rcx # num args (C arg 3) xorq %rdx,%rdx # clear first arg (lisp) shlq $2,%rcx # make into fixnum cmpq $0,%rcx je Ldone movq (%rbx),%rdx # arg0 cmpq $4,%rcx je Ldone movq 8(%rbx),%rdi # arg1 cmpq $8,%rcx je Ldone movq 16(%rbx),%rsi # arg2 Ldone: /* Registers rax (lexenv), rcx (num args), rdx (arg 0),rdi (arg 1),rsi (arg 2) now live */ /* Allocate new frame */ mov %rsp,%rbx # current sp marks start of new frame push %rbp # fp in save location S0 sub $16,%rsp # Ensure 3 slots are allocated, one above. mov %rbx,%rbp # switch to new frame /* Indirect the closure */ call *CLOSURE_FUNCTION_OFFSET(%rax) /* Multi-value return - blow off any extra values */ mov %rbx, %rsp /* Single value return */ /* Restore the stack, in case there was a stack change. */ popq %rsp # c-sp /* Restore C regs: rbx esi edi */ popq %r15 popq %r14 popq %r13 popq %r12 popq %rbx /* Restore the NPX state */ frstor (%rsp) addq $108, %rsp popq %rbp # c-fp movq %rdx,%rax # c-val ret .size GNAME(call_into_lisp), . - GNAME(call_into_lisp) /* Support for saving and restoring the NPX state from C. */ .text .global GNAME(fpu_save) .type GNAME(fpu_save),@function .align 2,0x90 GNAME(fpu_save): #if TOO_LAME_TO_PORT_IT_YET movl 4(%esp),%eax fnsave (%eax) # Save the NPX state - Resets NPX #endif ret .size GNAME(fpu_save),.-GNAME(fpu_save) .global GNAME(fpu_restore) .type GNAME(fpu_restore),@function .align 2,0x90 GNAME(fpu_restore): #if TOO_LAME_TO_PORT_IT_YET movl 4(%esp),%eax frstor (%eax) # Restore the NPX state. #endif ret .size GNAME(fpu_restore),.-GNAME(fpu_restore) /* * The undefined-function trampoline. */ .text .align align_4byte,0x90 .global GNAME(undefined_tramp) .type GNAME(undefined_tramp),@function GNAME(undefined_tramp): int3 .byte trap_Error /* Number of argument bytes */ .byte 2 .byte UNDEFINED_SYMBOL_ERROR /* SC_OFFSET(sc_DescriptorReg,reg_RAX) */ .byte SC_OFFSET(sc_DescriptorReg,0) ret .size GNAME(undefined_tramp), .-GNAME(undefined_tramp) /* * The closure trampoline. */ .text .align align_4byte,0x90 .global GNAME(closure_tramp) .type GNAME(closure_tramp),@function GNAME(closure_tramp): movq FDEFN_FUNCTION_OFFSET(%rax),%rax jmp *CLOSURE_FUNCTION_OFFSET(%rax) .size GNAME(closure_tramp), .-GNAME(closure_tramp) /* * Function-end breakpoint magic. */ .text .global GNAME(function_end_breakpoint_guts) .align align_4byte GNAME(function_end_breakpoint_guts): /* Multiple Value return */ jmp multiple_value_return /* Single value return: The eventual return will now use the multiple values return convention but with a return values count of one. */ movl %esp,%ebx # Setup ebx - the ofp. subl $4,%esp # Allocate one stack slot for the return value movl $4,%ecx # Setup ecx for one return value. movl $NIL,%edi # Default second value movl $NIL,%esi # Default third value multiple_value_return: .global GNAME(function_end_breakpoint_trap) GNAME(function_end_breakpoint_trap): int3 .byte trap_FunctionEndBreakpoint hlt # Should never return here. .global GNAME(function_end_breakpoint_end) GNAME(function_end_breakpoint_end): .global GNAME(do_pending_interrupt) .type GNAME(do_pending_interrupt),@function .align align_4byte,0x90 GNAME(do_pending_interrupt): int3 .byte trap_PendingInterrupt ret .size GNAME(do_pending_interrupt),.-GNAME(do_pending_interrupt) #ifdef WANT_CGC /* A copy function optimized for the Pentium and works ok on * 486 as well. This assumes (does not check) that the input * byte count is a multiple of 8-bytes (one lisp object). * This code takes advantage of pairing in the Pentium as well * as the 128-bit cache line. */ .global GNAME(fastcopy16) .type GNAME(fastcopy16),@function .align align_4byte,0x90 GNAME(fastcopy16): #if TOO_LAME_TO_PORT_IT_YET pushl %ebp #endif movl %esp,%ebp movl 8(%ebp), %edx # dst movl 12(%ebp),%eax # src movl 16(%ebp),%ecx # bytes #if TOO_LAME_TO_PORT_IT_YET pushl %ebx pushl %esi pushl %edi #endif movl %edx,%edi movl %eax,%esi sarl $3,%ecx # number 8-byte units testl $1,%ecx # odd? jz Lquad movl (%esi),%eax movl 4(%esi),%ebx movl %eax,(%edi) movl %ebx,4(%edi) leal 8(%esi),%esi leal 8(%edi),%edi Lquad: sarl $1,%ecx # count 16-byte units jz Lend movl %ecx,%ebp # use ebp for loop counter .align align_16byte,0x90 Ltop: movl (%edi),%eax #prefetch! MAJOR Pentium win. movl (%esi),%eax movl 4(%esi),%ebx movl 8(%esi),%ecx movl 12(%esi),%edx movl %eax, (%edi) movl %ebx, 4(%edi) movl %ecx, 8(%edi) movl %edx,12(%edi) leal 16(%esi),%esi leal 16(%edi),%edi decl %ebp jnz Ltop # non-prefixed jump saves cycles Lend: popl %edi popl %esi popl %ebx popl %ebp ret .size GNAME(fastcopy16),.-GNAME(fastcopy16) #endif #ifdef GENCGC /* Fast bzero using the FPU. The first argument is the start address which needs to be aligned on an 8 byte boundary, the second argument is the number of bytes which needs to be a multiple of 8 bytes and not zero. */ .text .globl GNAME(i586_bzero) .type GNAME(i586_bzero),@function .align align_4byte,0x90 GNAME(i586_bzero): #if TOO_LAME_TO_PORT_IT_YET movl 4(%esp),%edx # Load the start address movl 8(%esp),%eax # Load the number of bytes #endif fldz #if TOO_LAME_TO_PORT_IT_YET l1: fstl 0(%edx) #else l1: nop #endif addl $8,%edx subl $8,%eax jnz l1 fstp %st(0) ret .size GNAME(i586_bzero),.-GNAME(i586_bzero) #endif /* Allocate bytes and return the start of the allocated space in the specified destination register. In the general case the size will be in the destination register. All registers must be preserved except the destination. The C conventions will preserve rbx, r12-r15, and rbp. So only rax, rdi, rsi, rdx, rcx, and r8-r11 need special care here. */ .globl GNAME(alloc_to_rax) .type GNAME(alloc_to_rax),@function .align align_4byte,0x90 GNAME(alloc_to_rax): pushq %rcx # Save rcx, rdx, rdi, rsi, r8-r11 as C could destroy them. pushq %rdx pushq %rdi pushq %rsi pushq %r8 pushq %r9 pushq %r10 pushq %r11 movq %rax, %rdi # Pass arg 1 call GNAME(alloc) # return value is in %rax popq %r11 popq %r10 popq %r9 popq %r8 popq %rsi popq %rdi popq %rdx # Restore rcx and rdx. popq %rcx ret .size GNAME(alloc_to_rax),.-GNAME(alloc_to_rax) .globl GNAME(alloc_to_rcx) .type GNAME(alloc_to_rcx),@function .align align_4byte,0x90 GNAME(alloc_to_rcx): pushq %rax # Save rax, rdi, rsi, rdx, and r8-r11 as C could destroy them. pushq %rdi pushq %rsi pushq %rdx pushq %r8 pushq %r9 pushq %r10 pushq %r11 movq %rcx, %rdi # Pass the size call GNAME(alloc) movq %rax, %rcx # setup the destination. popq %r11 popq %r10 popq %r9 popq %r8 popq %rdx # Restore popq %rsi popq %rdi popq %rax ret .size GNAME(alloc_to_rcx),.-GNAME(alloc_to_rcx) .globl GNAME(alloc_to_rdx) .type GNAME(alloc_to_rdx),@function .align align_4byte,0x90 GNAME(alloc_to_rdx): pushq %rax # Save rax, rcx, rdi, rsi, r8-r11 as C could destroy them. pushq %rcx pushq %rdi pushq %rsi pushq %r8 pushq %r9 pushq %r10 pushq %r11 mov %rdx, %rdi # move the size to arg 1 call GNAME(alloc) movq %rax, %rdx # setup the destination. popq %r11 popq %r10 popq %r9 popq %r8 popq %rsi popq %rdi # Restore rdi, rax, and rcx. popq %rcx popq %rax ret .size GNAME(alloc_to_rdx),.-GNAME(alloc_to_rdx) .globl GNAME(alloc_to_rbx) .type GNAME(alloc_to_rbx),@function .align align_4byte,0x90 GNAME(alloc_to_rbx): pushq %rax # Save rax, rcx, rdx, rdi, rsi, r8-r11 as C could destroy them. pushq %rcx pushq %rdx pushq %rdi pushq %rsi pushq %r8 pushq %r9 pushq %r10 pushq %r11 movq %rbx, %rdi # Pass the size call GNAME(alloc) movq %rax, %rbx # setup the destination. popq %r11 popq %r10 popq %r9 popq %r8 popq %rsi popq %rdi popq %rdx # Restore rax, rcx, rdx, rdi, rsi, r8-r11. popq %rcx popq %rax ret .size GNAME(alloc_to_rbx),.-GNAME(alloc_to_rbx) .globl GNAME(alloc_to_rsi) .type GNAME(alloc_to_rsi),@function .align align_4byte,0x90 GNAME(alloc_to_rsi): pushq %rax # Save rax, rcx, rdx, rdi, r8-r11 as C could destroy them. pushq %rcx pushq %rdx pushq %rdi pushq %r8 pushq %r9 pushq %r10 pushq %r11 mov %rsi, %rdi # move the size to arg 1 call GNAME(alloc) movq %rax,%rsi # setup the destination. popq %r11 popq %r10 popq %r9 popq %r8 popq %rdi # Restore rdi, rax, rcx and rdx. popq %rdx popq %rcx popq %rax ret .size GNAME(alloc_to_rsi),.-GNAME(alloc_to_rsi) .globl GNAME(alloc_to_rdi) .type GNAME(alloc_to_rdi),@function .align align_4byte,0x90 GNAME(alloc_to_rdi): pushq %rax # Save rax, rcx, rdx, rsi, r8-r11 as C could destroy them. pushq %rcx pushq %rdx pushq %rsi pushq %r8 pushq %r9 pushq %r10 pushq %r11 call GNAME(alloc) movq %rax, %rdi # setup the destination. popq %r11 popq %r10 popq %r9 popq %r8 popq %rsi popq %rdx # Restore rax, rcx and rdx. popq %rcx popq %rax ret .size GNAME(alloc_to_rdi),.-GNAME(alloc_to_rdi) #ifdef GENCGC /* Called from lisp when an inline allocation overflows. Every register except the result needs to be preserved. We depend on C to preserve rbx, r12-r15, and rbp. But where necessary must save rax, rcx, rdx, rdi, rsi, r8-r11. */ /* This routine handles an overflow with rax=crfp+size. So the size=rax-crfp. */ .align align_4byte .globl GNAME(alloc_overflow_rax) .type GNAME(alloc_overflow_rax),@function GNAME(alloc_overflow_rax): pushq %rcx # Save rcx pushq %rdx # Save rdx pushq %rdi pushq %rsi pushq %r8 pushq %r9 pushq %r10 pushq %r11 /* Calculate the size for the allocation. */ subq CURRENT_REGION_FREE_POINTER + SYMBOL_VALUE_OFFSET,%rax movq %rax, %rdi # Pass arg 1 call GNAME(alloc) popq %r11 popq %r10 popq %r9 popq %r8 popq %rsi popq %rdi popq %rdx # Restore rdx. popq %rcx # Restore rcx. addq $13,(%rsp) # Adjust the return address to skip the next inst. ret .size GNAME(alloc_overflow_rax),.-GNAME(alloc_overflow_rax) /* This routine handles an overflow with rcx=crfp+size. So the size=rcx-crfp. */ .align align_4byte .globl GNAME(alloc_overflow_rcx) .type GNAME(alloc_overflow_rcx),@function GNAME(alloc_overflow_rcx): pushq %rax # Save rax pushq %rdx # Save rdx pushq %rdi pushq %rsi pushq %r8 pushq %r9 pushq %r10 pushq %r11 /* Calculate the size for the allocation. */ subq CURRENT_REGION_FREE_POINTER + SYMBOL_VALUE_OFFSET,%rcx movq %rcx, %rdi # Pass arg1 call GNAME(alloc) movq %rax, %rcx # setup the destination. popq %r11 popq %r10 popq %r9 popq %r8 popq %rsi popq %rdi popq %rdx # Restore rdx. popq %rax # Restore rax. addq $13,(%rsp) # Adjust the return address to skip the next 2 inst. ret .size GNAME(alloc_overflow_rcx),.-GNAME(alloc_overflow_rcx) /* This routine handles an overflow with rdx=crfp+size. So the size=rdx-crfp. */ .align align_4byte .globl GNAME(alloc_overflow_rdx) .type GNAME(alloc_overflow_rdx),@function GNAME(alloc_overflow_rdx): pushq %rax # Save rax pushq %rcx # Save rcx pushq %rdi pushq %rsi pushq %r8 pushq %r9 pushq %r10 pushq %r11 /* Calculate the size for the allocation. */ subq CURRENT_REGION_FREE_POINTER + SYMBOL_VALUE_OFFSET,%rdx movq %rdx, %rdi # Move the size call GNAME(alloc) movq %rax,%rdx # setup the destination. popq %r11 popq %r10 popq %r9 popq %r8 popq %rsi popq %rdi popq %rcx # Restore rcx. popq %rax # Restore rax. addq $13,(%rsp) # Adjust the return address to skip the next 2 inst. # The next 2 instructions sets the CRFP. ret .size GNAME(alloc_overflow_rdx),.-GNAME(alloc_overflow_rdx) /* This routine handles an overflow with rbx=crfp+size. So the size=rbx-crfp. */ .align align_4byte .globl GNAME(alloc_overflow_rbx) .type GNAME(alloc_overflow_rbx),@function GNAME(alloc_overflow_rbx): pushq %rax # Save rax pushq %rcx # Save rcx pushq %rdx # Save rdx pushq %rdi pushq %rsi pushq %r8 pushq %r9 pushq %r10 pushq %r11 /* Calculate the size for the allocation. */ subq CURRENT_REGION_FREE_POINTER + SYMBOL_VALUE_OFFSET,%rbx movq %rbx, %rdi # Pass arg 1 call GNAME(alloc) movq %rax,%rbx # setup the destination. popq %r11 popq %r10 popq %r9 popq %r8 popq %rsi popq %rdi popq %rdx # Restore rdx. popq %rcx # Restore rcx. popq %rax # Restore rax. addq $13,(%rsp) # Adjust the return address to skip the next 2 inst. ret .size GNAME(alloc_overflow_rbx),.-GNAME(alloc_overflow_rbx) /* This routine handles an overflow with rsi=crfp+size. So the size=rsi-crfp. */ .align align_4byte .globl GNAME(alloc_overflow_rsi) .type GNAME(alloc_overflow_rsi),@function GNAME(alloc_overflow_rsi): pushq %rax # Save rax pushq %rcx # Save rcx pushq %rdx # Save rdx pushq %rdi pushq %r8 pushq %r9 pushq %r10 pushq %r11 /* Calculate the size for the allocation. */ subq CURRENT_REGION_FREE_POINTER + SYMBOL_VALUE_OFFSET,%rsi movq %rsi, %rdi # Pass arg 1 call GNAME(alloc) movq %rax, %rsi # setup the destination. popq %r11 popq %r10 popq %r9 popq %r8 popq %rdi popq %rdx # Restore rdx. popq %rcx # Restore rcx. popq %rax # Restore rax. addq $13,(%rsp) # Adjust the return address to skip the next 2 inst. ret .size GNAME(alloc_overflow_rsi),.-GNAME(alloc_overflow_rsi) /* This routine handles an overflow with rdi=crfp+size. So the size=rdi-crfp. */ .align align_4byte .globl GNAME(alloc_overflow_rdi) .type GNAME(alloc_overflow_rdi),@function GNAME(alloc_overflow_rdi): pushq %rax # Save rax pushq %rcx # Save rcx pushq %rdx # Save rdx pushq %rsi pushq %r8 pushq %r9 pushq %r10 pushq %r11 /* Calculate the size for the allocation. */ subq CURRENT_REGION_FREE_POINTER + SYMBOL_VALUE_OFFSET,%rdi call GNAME(alloc) movq %rax,%rdi # setup the destination. popq %r11 popq %r10 popq %r9 popq %r8 popq %rsi popq %rdx # Restore rdx. popq %rcx # Restore rcx. popq %rax # Restore rax. addl $13,(%rsp) # Adjust the return address to skip the next inst. ret .size GNAME(alloc_overflow_rdi),.-GNAME(alloc_overflow_rdi) #endif #ifdef LINKAGE_TABLE /* Call into C code to resolve a linkage entry. The initial code in the * linkage entry has done a call to here; pass that return entry along as a * parameter. * * We could be called from raw Lisp code or from a foreign call site, so we * have to save all the registers... */ .align align_4byte .globl GNAME(resolve_linkage_tramp) .type GNAME(resolve_linkage_tramp),@function GNAME(resolve_linkage_tramp): pushq %rbp # save old frame pointer movq %rsp,%rbp # establish new frame pushq %rax pushq %rbx pushq %rcx pushq %rdx pushq %rdi pushq %rsi /* maybe save r8-15 too */ /* calling location (plus offset) was on top of stack */ movq 8(%rbp), %rdi # arg 1 for C function call GNAME(lazy_resolve_linkage) /* real address of target is in %rax. Replace return address on stack * with it. That way we can get out of here without trashing any * registers! */ movq %rax,8(%rbp) /* maybe pop r8-15 too */ popq %rsi popq %rdi popq %rdx popq %rcx popq %rbx popq %rax popq %rbp ret # jump to the real target .size GNAME(resolve_linkage_tramp),.-GNAME(resolve_linkage_tramp) /* * The C-callable undefined-foreign-symbol trapping function. */ .text .align align_4byte,0x90 .global GNAME(undefined_foreign_symbol_trap) .type GNAME(undefined_foreign_symbol_trap),@function GNAME(undefined_foreign_symbol_trap): /* C Calling Convention, move one arg to EAX */ #if TOO_LAME_TO_PORT_IT_YET pushl %ebp #endif movl %esp,%ebp #if TOO_LAME_TO_PORT_IT_YET movl 8(%ebp),%eax #endif /* Now trap to Lisp */ int3 .byte trap_Error /* Number of argument bytes */ .byte 2 .byte UNDEFINED_FOREIGN_SYMBOL_ERROR /* SC_OFFSET(sc_DescriptorReg,reg_RAX) */ .byte SC_OFFSET(sc_DescriptorReg,0) /* C Calling Convention */ /* Doesn't matter here, but could if we'd use trap_Cerror */ leave ret .size GNAME(undefined_foreign_symbol_trap), .-GNAME(undefined_foreign_symbol_trap) .end #endif /* LINKAGE_TABLE */