changecom(`/*', `*/')dnl
/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 2002-2011. All Rights Reserved.
 *
 * The contents of this file are subject to the Erlang Public License,
 * Version 1.1, (the "License"); you may not use this file except in
 * compliance with the License. You should have received a copy of the
 * Erlang Public License along with this software. If not, it can be
 * retrieved online at http://www.erlang.org/.
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * %CopyrightEnd%
 */


`#ifndef HIPE_X86_ASM_H
#define HIPE_X86_ASM_H'

/*
 * Tunables.
 */
define(LEAF_WORDS,24)dnl number of stack words for leaf functions
define(NR_ARG_REGS,3)dnl admissible values are 0 to 5, inclusive
define(HP_IN_ESI,1)dnl change to 0 to not reserve a global register for HP
define(SIMULATE_NSP,0)dnl change to 1 to simulate call/ret insns

`#define X86_LEAF_WORDS	'LEAF_WORDS
`#define LEAF_WORDS	'LEAF_WORDS

/*
 * Workarounds for Darwin.
 */
ifelse(OPSYS,darwin,``
/* Darwin */
#define TEXT		.text
#define JOIN(X,Y)	X##Y
#define CSYM(NAME)	JOIN(_,NAME)
#define ASYM(NAME)	CSYM(NAME)
#define GLOBAL(NAME)	.globl NAME
#define SET_SIZE(NAME)	/*empty*/
#define TYPE_FUNCTION(NAME)	/*empty*/
'',``
/* Not Darwin */
#define TEXT		.section ".text"
#define CSYM(NAME)	NAME
#define ASYM(NAME)	NAME
#define GLOBAL(NAME)	.global NAME
#define SET_SIZE(NAME)	.size NAME,.-NAME
#define TYPE_FUNCTION(NAME)	.type NAME,@function
'')dnl

/*
 * Reserved registers.
 */
`#define P	%ebp'

`#define X86_HP_IN_ESI	'HP_IN_ESI
`#if X86_HP_IN_ESI
#define SAVE_HP		movl %esi, P_HP(P)
#define RESTORE_HP	movl P_HP(P), %esi
#else
#define SAVE_HP		/*empty*/
#define RESTORE_HP	/*empty*/
#endif'

`#define NSP		%esp
#define SAVE_CSP	movl %esp, P_CSP(P)
#define RESTORE_CSP	movl P_CSP(P), %esp'

`#define X86_SIMULATE_NSP	'SIMULATE_NSP

/*
 * Context switching macros.
 */
`#define SWITCH_C_TO_ERLANG_QUICK	\
	SAVE_CSP; \
	movl P_NSP(P), NSP'

`#define SWITCH_ERLANG_TO_C_QUICK	\
	movl NSP, P_NSP(P); \
	RESTORE_CSP'

`#define SAVE_CACHED_STATE	\
	SAVE_HP'

`#define RESTORE_CACHED_STATE	\
	RESTORE_HP'

`#define SWITCH_C_TO_ERLANG	\
	RESTORE_CACHED_STATE;	\
	SWITCH_C_TO_ERLANG_QUICK'

`#define SWITCH_ERLANG_TO_C	\
	SAVE_CACHED_STATE;	\
	SWITCH_ERLANG_TO_C_QUICK'

/*
 * Argument (parameter) registers.
 */
`#define X86_NR_ARG_REGS	'NR_ARG_REGS
`#define NR_ARG_REGS	'NR_ARG_REGS

ifelse(eval(NR_ARG_REGS >= 1),0,,
``#define ARG0	%eax
'')dnl
ifelse(eval(NR_ARG_REGS >= 2),0,,
``#define ARG1	%edx
'')dnl
ifelse(eval(NR_ARG_REGS >= 3),0,,
``#define ARG2	%ecx
'')dnl
ifelse(eval(NR_ARG_REGS >= 4),0,,
``#define ARG3	%ebx
'')dnl
ifelse(eval(NR_ARG_REGS >= 5),0,,
``#define ARG4	%edi
'')dnl

/*
 * TEMP_RV:
 *	Used in nbif_stack_trap_ra to preserve the return value.
 *	Must be a C callee-save register.
 *	Must be otherwise unused in the return path.
 */
`#define TEMP_RV	%ebx'

/*
 * TEMP_NSP:
 *	Used in BIF wrappers to permit copying stacked parameter from
 *	the native stack to the C stack.
 *	Set up by NBIF_COPY_NSP(arity) and used by NBIF_ARG(arity,argno).
 *	TEMP_NSP may alias the last BIF argument register.
 *	NBIF_COPY_NSP and NBIF_ARG currently fail if ARITY > NR_ARG_REGS!
 */
`#define TEMP_NSP	%edi'

dnl XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
dnl X								X
dnl X			hipe_x86_glue.S support			X
dnl X								X
dnl XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

dnl
dnl LOAD_ARG_REGS
dnl
define(LAR_1,`movl P_ARG$1(P), ARG$1 ; ')dnl
define(LAR_N,`ifelse(eval($1 >= 0),0,,`LAR_N(eval($1-1))LAR_1($1)')')dnl
define(LOAD_ARG_REGS,`LAR_N(eval(NR_ARG_REGS-1))')dnl
`#define LOAD_ARG_REGS	'LOAD_ARG_REGS

dnl
dnl STORE_ARG_REGS
dnl
define(SAR_1,`movl ARG$1, P_ARG$1(P) ; ')dnl
define(SAR_N,`ifelse(eval($1 >= 0),0,,`SAR_N(eval($1-1))SAR_1($1)')')dnl
define(STORE_ARG_REGS,`SAR_N(eval(NR_ARG_REGS-1))')dnl
`#define STORE_ARG_REGS	'STORE_ARG_REGS

dnl
dnl NSP_CALL(FUN)
dnl Emit a CALL FUN instruction, or simulate it.
dnl FUN must not be an NSP-based memory operand.
dnl
ifelse(eval(SIMULATE_NSP),0,
``#define NSP_CALL(FUN)	call FUN'',
``#define NSP_CALL(FUN)	subl $4,NSP; movl $1f,(NSP); jmp FUN; 1:'')dnl

dnl
dnl NSP_RETN(NPOP)
dnl Emit a RET $NPOP instruction, or simulate it.
dnl NPOP should be non-zero.
dnl
ifelse(eval(SIMULATE_NSP),0,
``#define NSP_RETN(NPOP)	ret $NPOP'',
``#define NSP_RETN(NPOP)	movl (NSP),TEMP_RV; addl $4+NPOP,NSP; jmp *TEMP_RV'')dnl

dnl
dnl NSP_RET0
dnl Emit a RET instruction, or simulate it.
dnl
ifelse(eval(SIMULATE_NSP),0,
``#define NSP_RET0	ret'',
``#define NSP_RET0	movl (NSP),TEMP_RV; addl $4,NSP; jmp *TEMP_RV'')dnl

dnl XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
dnl X								X
dnl X			hipe_x86_bifs.m4 support		X
dnl X								X
dnl XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

dnl
dnl NBIF_COPY_NSP(ARITY)
dnl if ARITY > NR_ARG_REGS then TEMP_NSP := %esp.
dnl Allows the stacked formals to be referenced via TEMP_NSP after the stack switch.
dnl
define(NBIF_COPY_NSP,`ifelse(eval($1 > NR_ARG_REGS),0,,`movl	%esp, TEMP_NSP')')dnl
`/* #define NBIF_COPY_NSP_0	'NBIF_COPY_NSP(0)` */'
`/* #define NBIF_COPY_NSP_1	'NBIF_COPY_NSP(1)` */'
`/* #define NBIF_COPY_NSP_2	'NBIF_COPY_NSP(2)` */'
`/* #define NBIF_COPY_NSP_3	'NBIF_COPY_NSP(3)` */'
`/* #define NBIF_COPY_NSP_5	'NBIF_COPY_NSP(5)` */'

dnl
dnl BASE_OFFSET(N)
dnl Generates a base-register offset operand for the value N.
dnl When N is zero the offset becomes the empty string, as this
dnl may allow the assembler to choose a more compat encoding.
dnl
define(BASE_OFFSET,`ifelse(eval($1),0,`',`$1')')dnl

dnl
dnl NBIF_ARG_OPND(ARITY,ARGNO)
dnl Generates an operand for this formal parameter.
dnl It will be a register operand when 0 <= ARGNO < NR_ARG_REGS.
dnl It will be a memory operand via TEMP_NSP when ARGNO >= NR_ARG_REGS.
dnl
define(NBIF_ARG_OPND,`ifelse(eval($2 >= NR_ARG_REGS),0,`ARG'$2,BASE_OFFSET(eval(($1-NR_ARG_REGS)*4-($2-NR_ARG_REGS)*4))`(TEMP_NSP)')')dnl
`/* #define NBIF_ARG_OPND_1_0	'NBIF_ARG_OPND(1,0)` */'
`/* #define NBIF_ARG_OPND_2_0	'NBIF_ARG_OPND(2,0)` */'
`/* #define NBIF_ARG_OPND_2_1	'NBIF_ARG_OPND(2,1)` */'
`/* #define NBIF_ARG_OPND_3_0	'NBIF_ARG_OPND(3,0)` */'
`/* #define NBIF_ARG_OPND_3_1	'NBIF_ARG_OPND(3,1)` */'
`/* #define NBIF_ARG_OPND_3_2	'NBIF_ARG_OPND(3,2)` */'
`/* #define NBIF_ARG_OPND_5_0	'NBIF_ARG_OPND(5,0)` */'
`/* #define NBIF_ARG_OPND_5_1	'NBIF_ARG_OPND(5,1)` */'
`/* #define NBIF_ARG_OPND_5_2	'NBIF_ARG_OPND(5,2)` */'
`/* #define NBIF_ARG_OPND_5_3	'NBIF_ARG_OPND(5,3)` */'
`/* #define NBIF_ARG_OPND_5_4	'NBIF_ARG_OPND(5,4)` */'

dnl
dnl NBIF_ARG_REG(CARGNO,REG)
dnl Generates code to move REG to C argument number CARGNO.
dnl
define(NBIF_ARG_REG,`movl $2,BASE_OFFSET(eval(4*$1))(%esp)')dnl
`/* #define NBIF_ARG_REG_0_P	'NBIF_ARG_REG(0,P)` */'

dnl
dnl NBIF_ARG(CARGNO,ARITY,ARGNO)
dnl Generates code to move Erlang parameter number ARGNO
dnl in a BIF of arity ARITY to C parameter number CARGNO.
dnl
dnl This must be called after NBIF_COPY_NSP(ARITY).
dnl
dnl NBIF_ARG(_,_,ARGNO2) must be called after NBIF_ARG(_,_,ARGNO1)
dnl if ARGNO2 > ARGNO1. (ARG0 may be reused as a temporary register
dnl for Erlang parameters passed on the stack.)
dnl
define(NBIF_ARG_MEM,`movl NBIF_ARG_OPND($2,$3),%eax; NBIF_ARG_REG($1,%eax)')dnl
define(NBIF_ARG,`ifelse(eval($3 >= NR_ARG_REGS),0,`NBIF_ARG_REG($1,`ARG'$3)',`NBIF_ARG_MEM($1,$2,$3)')')dnl

dnl
dnl NBIF_RET(ARITY)
dnl Generates a return from a native BIF, taking care to pop
dnl any stacked formal parameters.
dnl
define(RET_POP,`ifelse(eval($1 > NR_ARG_REGS),0,0,eval(4*($1 - NR_ARG_REGS)))')dnl
define(NBIF_RET_N,`ifelse(eval($1),0,`NSP_RET0',`NSP_RETN($1)')')dnl
define(NBIF_RET,`NBIF_RET_N(eval(RET_POP($1)))')dnl
`/* #define NBIF_RET_0	'NBIF_RET(0)` */'
`/* #define NBIF_RET_1	'NBIF_RET(1)` */'
`/* #define NBIF_RET_2	'NBIF_RET(2)` */'
`/* #define NBIF_RET_3	'NBIF_RET(3)` */'
`/* #define NBIF_RET_5	'NBIF_RET(5)` */'

dnl
dnl STORE_CALLER_SAVE
dnl LOAD_CALLER_SAVE
dnl Used to save and restore C caller-save argument registers around
dnl calls to hipe_inc_nstack. The first 3 arguments registers are C 
dnl caller-save, remaining ones are C callee-save.
dnl
define(NBIF_MIN,`ifelse(eval($1 > $2),0,$1,$2)')dnl
define(NR_CALLER_SAVE,NBIF_MIN(NR_ARG_REGS,3))dnl
define(STORE_CALLER_SAVE,`SAR_N(eval(NR_CALLER_SAVE-1))')dnl
define(LOAD_CALLER_SAVE,`LAR_N(eval(NR_CALLER_SAVE-1))')dnl
`#define STORE_CALLER_SAVE	'STORE_CALLER_SAVE
`#define LOAD_CALLER_SAVE	'LOAD_CALLER_SAVE

`#endif /* HIPE_X86_ASM_H */'