Skip to content
x86-assem.S 12.7 KiB
Newer Older
/* ### x86-assem.S -*- Mode: Asm; -*- */
ram's avatar
ram committed
/**
 * $Header: /Volumes/share2/src/cmucl/cvs2git/cvsroot/src/lisp/x86-assem.S,v 1.35 2010/12/22 02:12:52 rtoy Exp $
ram's avatar
ram committed
 *
 * Authors:	Paul F. Werkowski <pw@snoopy.mv.com>
 *		Douglas T. Crosher
ram's avatar
ram committed
 *
 * This code was written to support the port of CMU Common Lisp
 * to the Intel X86 ISA and the FreeBSD operating system. The
 * author has placed this code in the public domain September 1996.
 *
 */

ram's avatar
ram committed
#include "x86-validate.h"
	
#define LANGUAGE_ASSEMBLY
#include "internals.h"
ram's avatar
ram committed

/* Minimize conditionalization for different OS naming schemes */
#ifdef DARWIN	
#define GNAME(var) _##var
#define FUNCDEF(x) \
	.text			; \
	.align 2,0x90		; \
	.globl GNAME(x)		; \
GNAME(x):			;
#define ENDFUNC(x)
#elif defined(SOLARIS)
ram's avatar
ram committed
#define GNAME(var) var
cshapiro's avatar
cshapiro committed
#define FUNCDEF(x) \
	.text			; \
cshapiro's avatar
cshapiro committed
	.globl GNAME(x)		; \
	.type x,@function	; \
GNAME(x):			;
#define ENDFUNC(x) \
	.size GNAME(x),.-GNAME(x)
ram's avatar
ram committed
#else
cshapiro's avatar
cshapiro committed
#define FUNCDEF(x) \
	.text			; \
cshapiro's avatar
cshapiro committed
	.globl GNAME(x)		; \
cshapiro's avatar
cshapiro committed
GNAME(x):			;
#define ENDFUNC(x) \
	.size GNAME(x),.-GNAME(x)
ram's avatar
ram committed
#endif

#ifdef SOLARIS
#define	INT3	int $3

#else
#define INT3	int3
#endif
								
/* Get the right type of alignment.  Linux wants alignment in bytes. */
#if defined (__linux__) || defined (__FreeBSD__)
cshapiro's avatar
cshapiro committed
#define align_16byte    16
ram's avatar
ram committed
#else
#define	align_16byte	4	
#endif			

Raymond Toy's avatar
Raymond Toy committed
/*
 * Allocate |bytes| on the stack, and make sure the stack pointer is
 * aligned on a 16-byte boundary.  (Needed on Darwin, and harmless on 
 * others that don't need such alignment.)
 */		
#define STACK_PROLOGUE(bytes) \
	pushl	%ebp		; \
	mov	%esp, %ebp	; \
Raymond Toy's avatar
Raymond Toy committed
	andl	$-16, %esp	; 

/* Undo STACK_PROLOGUE */
#define STACK_EPILOGUE \
	movl	%ebp, %esp	; \
	popl	%ebp		; 
	
ram's avatar
ram committed
	.text
	.globl	GNAME(foreign_function_call_active)
ram's avatar
ram committed
/*
 * The C function will preserve ebx, esi, edi, and ebp across its
 * function call - ebx is used to save the return lisp address.
 *
 * Return values are in eax and maybe edx for quads, or st(0) for
 * floats.
 *
dtc's avatar
dtc committed
 * It should work for lisp calls C calls lisp calls C ..
ram's avatar
ram committed
 */
ram's avatar
ram committed
	movl	$1,GNAME(foreign_function_call_active)

/* Save the return lisp address in ebx */	
	popl	%ebx

	call	*%eax		# normal callout using Lisp stack

	movl	%eax,%ecx	# remember integer return value

/* Check for a return FP value */
	fxam
ram's avatar
ram committed
	andl	$0x4500,%eax
	cmpl	$0x4100,%eax
	jne	Lfp_rtn_value

/* The return value is in eax, or eax,edx? */
ram's avatar
ram committed
/* Restore the return value */
	movl	%ecx,%eax	# maybe return value

	movl	$0,GNAME(foreign_function_call_active)
/* Return */	
	jmp	*%ebx

Lfp_rtn_value:

/* 
 * The float result is in st(0).  We want it in xmm0.  For
 * consistency, save st(0) out as a double and load it into xmm0 as a
 * double.  The call-out vop will arrange to convert it to a single-float
 * if necessary.
 */
Raymond Toy's avatar
Raymond Toy committed
	subl	$8, %esp	# Space for a double float
	fstpl	0(%esp)
	movsd	0(%esp), %xmm0
	addl	$8, %esp
ram's avatar
ram committed

/* Don't need to restore eax as the result is in st(0) */

	movl	$0,GNAME(foreign_function_call_active)
/* Return */	
	jmp	*%ebx
ram's avatar
ram committed

dtc's avatar
dtc committed

ram's avatar
ram committed
/* The C conventions require that ebx, esi, edi, and ebp be preserved
	across function calls. */
/* The *alien-stack* pointer is setup on the first call_into_lisp when
   the stack changes. */
ram's avatar
ram committed
	pushl	%ebp		# save old frame pointer
	movl	%esp,%ebp	# establish new frame

/* Save the NPX state */
	fwait			# Catch any pending NPX exceptions.
rtoy's avatar
rtoy committed
	/* Save the SSE2 for X87 state */
	mov	GNAME(fpu_mode), %eax
	cmp	$2, %eax	# SSE2 mode?
	jne	x87_save
	movl	%esp, %eax	# Remember the current stack pointer
	/*
	 * The SSE state is 512 bytes, but we need 16 more because we
	 * need 16-byte alignment.
	 */
	subl	$512+16,%esp
rtoy's avatar
rtoy committed
	andl	$-16, %esp	# fxsave needs 16-byte alignment
	fxsave	(%esp)		
	pushl	%eax		# Save the old stack pointer
	fninit			# Reset fpu, just in case
	jmp	npx_save_done

x87_save:
ram's avatar
ram committed
	subl	$108,%esp	# Make room for the NPX state.
	fnsave	(%esp)		# Resets NPX
rtoy's avatar
rtoy committed
		
dtc's avatar
dtc committed
	movl	(%esp),%eax	# Load NPX control word
	andl	$0xfffff3ff,%eax	# Set rounding mode to nearest
dtc's avatar
dtc committed
	orl	$0x00000300,%eax	# Set precision to 64 bits
#else
	orl	$0x00000200,%eax	# Set precision to 53 bits
#endif	
dtc's avatar
dtc committed
	pushl	%eax
ram's avatar
ram committed
	fldcw	(%esp)		# Recover modes
dtc's avatar
dtc committed
	popl	%eax
rtoy's avatar
rtoy committed
npx_save_done:			
ram's avatar
ram committed
/* Save C regs: ebx esi edi */
	pushl	%ebx
	pushl	%esi
	pushl	%edi
	
/* clear descriptor regs */
	xorl	%eax,%eax	# lexenv
	xorl	%ebx,%ebx	# available
	xorl	%ecx,%ecx	# arg count
	xorl	%edx,%edx	# first arg
	xorl	%edi,%edi	# second arg
	xorl	%esi,%esi	# third arg
ram's avatar
ram committed

/* no longer in function call */
	movl	%eax, GNAME(foreign_function_call_active)

	movl	%esp,%ebx	# remember current stack
	cmpl	$CONTROL_STACK_START,%esp
	jbe	ChangeToLispStack
Raymond Toy's avatar
Raymond Toy committed
	cmpl	GNAME(control_stack_end), %esp
	jbe	OnLispStack
ChangeToLispStack:
	/* Setup the *alien-stack* pointer */
	movl	%esp,ALIEN_STACK + SYMBOL_VALUE_OFFSET
Raymond Toy's avatar
Raymond Toy committed
	movl	GNAME(control_stack_end), %esp		# New stack
ram's avatar
ram committed
	pushl	%ebx		# save entry stack on (maybe) new stack

	/* establish lisp args */
	movl	 8(%ebp),%eax	# lexenv?
	movl	12(%ebp),%ebx	# address of arg vec
	movl	16(%ebp),%ecx	# num args
	shll	$2,%ecx		# make into fixnum
	cmpl	$0,%ecx
	je	Ldone
	movl	(%ebx),%edx	# arg0
	cmpl	$4,%ecx
	je	Ldone
	movl	4(%ebx),%edi	# arg1
	cmpl	$8,%ecx
	je	Ldone
	movl	8(%ebx),%esi	# arg2
Ldone:	
dtc's avatar
dtc committed
	/* Registers eax, ecx, edx,edi,esi now live */
ram's avatar
ram committed

dtc's avatar
dtc committed
	/* Allocate new frame */
ram's avatar
ram committed
	mov	%esp,%ebx	# current sp marks start of new frame
	push	%ebp		# fp in save location S0
	sub	$8,%esp		# Ensure 3 slots are allocated, one above.
ram's avatar
ram committed
	mov	%ebx,%ebp	# switch to new frame

dtc's avatar
dtc committed
	/* Indirect the closure */
	call	*CLOSURE_FUNCTION_OFFSET(%eax)
ram's avatar
ram committed
	
	/* Multi-value return - blow off any extra values */
	mov	%ebx, %esp
	/* Single value return */	

/* Restore the stack, in case there was a stack change. */
ram's avatar
ram committed
	popl	%esp		# c-sp

dtc's avatar
dtc committed
/* Restore C regs: ebx esi edi */
ram's avatar
ram committed
	popl	%edi
	popl	%esi
	popl	%ebx

/* Restore the NPX state */
rtoy's avatar
rtoy committed
	/* Restore SSE2 state? */
	mov	GNAME(fpu_mode), %eax
	cmp	$2, %eax	# SSE2 mode?
	jne	x87_restore
	popl	%eax		# Get the old stack pointer
	fxrstor	(%esp)		# Restore the SSE state
	movl	%eax, %esp	# Now really restore the old stack pointer
	jmp	npx_restore_done
x87_restore:
ram's avatar
ram committed
	frstor  (%esp)
	addl	$108, %esp
rtoy's avatar
rtoy committed
npx_restore_done:	
			
ram's avatar
ram committed
	popl	%ebp		# c-sp
	movl	%edx,%eax	# c-val
	ret
/* Support for saving and restoring the NPX state from C. */
	movl	4(%esp),%eax
	fnsave	(%eax)		# Save the NPX state - Resets NPX
	ret
	movl	4(%esp),%eax
	frstor	(%eax)		# Restore the NPX state.
	ret
rtoy's avatar
rtoy committed

FUNCDEF(sse_save)
	movl	4(%esp),%eax
	addl	$16, %eax	# Make sure eax is on a 16-byte boundary
	and	$-16, %eax
	fxsave	(%eax)
	ret
ENDFUNC(sse_save)
		
FUNCDEF(sse_restore)
	movl	4(%esp),%eax
	addl	$16, %eax	# Make sure eax is on a 16-byte boundary
	and	$-16, %eax
	fxrstor	(%eax)
	ret
ENDFUNC(sse_restore)
dtc's avatar
dtc committed

 * These are now implemented as Lisp assembly routines.  We leave
 * these here for the time being until we're sure the assembly
 * routines are working as expected.
 */

 /*
 * The undefined-function trampoline.
ram's avatar
ram committed
 */
        /* Number of argument bytes */
ram's avatar
ram committed
        .byte   2
	.byte	UNDEFINED_SYMBOL_ERROR
        /* SC_OFFSET(sc_DescriptorReg,reg_EAX) */
        .byte   SC_OFFSET(sc_DescriptorReg,0)
ram's avatar
ram committed
	ret
ram's avatar
ram committed

ram's avatar
ram committed
	movl	FDEFN_FUNCTION_OFFSET(%eax),%eax
	jmp	*CLOSURE_FUNCTION_OFFSET(%eax)
ram's avatar
ram committed

/*
 * Function-end breakpoint magic.
 */
FUNCDEF(function_end_breakpoint_guts)
	/* Multiple Value return
	   This MUST be a two-byte instruction.  If it isn't tracing is 
	   majorly broken. */
	jmp	1f
	/* 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
	.globl GNAME(function_end_breakpoint_trap)
GNAME(function_end_breakpoint_trap):
	.byte 	trap_FunctionEndBreakpoint
	hlt			# Should never return here.
ram's avatar
ram committed

	.globl GNAME(function_end_breakpoint_end)
GNAME(function_end_breakpoint_end):
ram's avatar
ram committed

FUNCDEF(do_pending_interrupt)
ram's avatar
ram committed
	.byte 	trap_PendingInterrupt
	ret
ENDFUNC(do_pending_interrupt)
	
#ifdef trap_DynamicSpaceOverflowError
FUNCDEF(do_dynamic_space_overflow_error)
	.byte 	trap_DynamicSpaceOverflowError
	ret
ENDFUNC(do_dynamic_space_overflow_error)
#endif				
	
#ifdef trap_DynamicSpaceOverflowWarning
FUNCDEF(do_dynamic_space_overflow_warning)
	.byte 	trap_DynamicSpaceOverflowWarning
	ret
ENDFUNC(do_dynamic_space_overflow_warning)
ram's avatar
ram committed
/* 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.
 */
ram's avatar
ram committed
	pushl	%ebp
	movl	%esp,%ebp
	movl	8(%ebp), %edx	# dst
	movl	12(%ebp),%eax	# src
	movl	16(%ebp),%ecx	# bytes
	pushl	%ebx
	pushl	%esi
	pushl	%edi
	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
/*
 * alloc_overflow_x87 and alloc_overflow_sse2 must not be called from
 * C because it doesn't follow C conventions.
 *
 * On entry:
 * %eax = bytes to allocate
 * On exit:
 * %eax = address
 */
FUNCDEF(alloc_overflow_x87)
	STACK_PROLOGUE(12)
	movl	%ecx, 8(%esp)	# Save ecx and edx registers
	movl	%edx, 4(%esp)
	movl	%eax, (%esp)	# Put size on stack for first arg to alloc()
	call	GNAME(alloc)
	movl	4(%esp), %edx	# Restore edx and ecx registers.  eax has the return value.
	movl	8(%esp), %ecx
	STACK_EPILOGUE
	ret
ENDFUNC(alloc_overflow_x87)	

FUNCDEF(alloc_overflow_sse2)
	STACK_PROLOGUE(20)
	movl	%ecx, 8(%esp)	# Save ecx and edx registers
	movl	%edx, 4(%esp)
	stmxcsr 12(%esp)	# Save MXCSR
	/* Clear the exceptions that might have occurred */
	movl	12(%esp), %edx
	and	$-64, %edx	# Clear the exceptions
	movl	%edx, 16(%esp)
	ldmxcsr 16(%esp)	# Get new mxcsr value
	movl	%eax, (%esp)	# Put size on stack for first arg to alloc()
	movl	4(%esp), %edx	# Restore edx and ecx registers.  eax has the return value.
	movl	8(%esp), %ecx
	ldmxcsr	12(%esp)
	STACK_EPILOGUE
	ret
ENDFUNC(alloc_overflow_sse2)	
		
moore's avatar
 
moore committed
/* 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...
 */	
FUNCDEF(resolve_linkage_tramp)
moore's avatar
 
moore committed
	pushl	%ebp		# save old frame pointer
	movl	%esp,%ebp	# establish new frame
cshapiro's avatar
cshapiro committed
	subl	$28, %esp
	andl	$-16, %esp
	movl	%eax, 24(%esp)
	movl	%ecx, 20(%esp)
	movl	%edx, 16(%esp)
	movl	%ebx, 12(%esp)
	movl	%esi, 8(%esp)	
	movl	%edi, 4(%esp)
moore's avatar
 
moore committed
	/* calling location (plus offset) was on top of stack */
	movl	4(%ebp), %eax
cshapiro's avatar
cshapiro committed
	movl	%eax, (%esp)	# push for C function
moore's avatar
 
moore committed
	call	GNAME(lazy_resolve_linkage)
	/* real address of target is in %eax.  Replace return address on stack
	 * with it.  That way we can get out of here without trashing any 
	 *registers!
	 */
	movl	%eax,4(%ebp)
cshapiro's avatar
cshapiro committed
	movl	4(%esp), %edi
	movl	8(%esp), %esi
	movl	12(%esp), %ebx
	movl	16(%esp), %edx
	movl	20(%esp), %ecx
	movl	24(%esp), %eax
	leave
moore's avatar
 
moore committed
	ret			# jump to the real target
ENDFUNC(resolve_linkage_tramp)
moore's avatar
 
moore committed

/*
 * The C-callable undefined-foreign-symbol trapping function.
moore's avatar
 
moore committed
 */
FUNCDEF(undefined_foreign_symbol_trap)
moore's avatar
 
moore committed
	/* C Calling Convention, move one arg to EAX */
        pushl %ebp
        movl %esp,%ebp
        movl 8(%ebp),%eax

	/* Now trap to Lisp */
moore's avatar
 
moore committed
	.byte	trap_Error
        /* Number of argument bytes */
moore's avatar
 
moore committed
        .byte   2
	.byte	UNDEFINED_FOREIGN_SYMBOL_ERROR
        /* SC_OFFSET(sc_DescriptorReg,reg_EAX) */
        .byte   SC_OFFSET(sc_DescriptorReg,0)
moore's avatar
 
moore committed

	/* C Calling Convention */
	/* Doesn't matter here, but could if we'd use trap_Cerror */
	leave
	ret
ENDFUNC(undefined_foreign_symbol_trap)

#endif /* LINKAGE_TABLE */