changecom(`/*', `*/')dnl
/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 2004-2010. 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%
 */
/*
 * $Id$
 */
`#ifndef HIPE_AMD64_ASM_H
#define HIPE_AMD64_ASM_H'

dnl
dnl Tunables.
dnl
define(LEAF_WORDS,24)dnl number of stack words for leaf functions
define(NR_ARG_REGS,4)dnl admissible values are 0 to 6, inclusive
define(HP_IN_REGISTER,1)dnl 1 to reserve a global register for HP
define(FCALLS_IN_REGISTER,0)dnl 1 to reserve global register for FCALLS
define(HEAP_LIMIT_IN_REGISTER,0)dnl global for HL
define(SIMULATE_NSP,0)dnl change to 1 to simulate call/ret insns

`#define AMD64_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		%rbp'

`#define AMD64_HP_IN_REGISTER	'HP_IN_REGISTER
`#if AMD64_HP_IN_REGISTER
#define AMD64_HEAP_POINTER 15'
define(HP,%r15)dnl Only change this together with above
`#define SAVE_HP	movq 'HP`, P_HP(P)
#define RESTORE_HP	movq P_HP(P), 'HP`
#else
#define SAVE_HP		/*empty*/
#define RESTORE_HP	/*empty*/
#endif'

`#define AMD64_FCALLS_IN_REGISTER 'FCALLS_IN_REGISTER
`#if AMD64_FCALLS_IN_REGISTER
#define AMD64_FCALLS_REGISTER 11'
define(FCALLS,%r11)dnl This goes together with line above
`#define SAVE_FCALLS	movq 'FCALLS`, P_FCALLS(P)
#define RESTORE_FCALLS	movq P_FCALLS(P), 'FCALLS`
#else
#define SAVE_FCALLS	/*empty*/
#define RESTORE_FCALLS	/*empty*/
#endif'

`#define AMD64_HEAP_LIMIT_IN_REGISTER 'HEAP_LIMIT_IN_REGISTER
`#if AMD64_HEAP_LIMIT_IN_REGISTER
#define AMD64_HEAP_LIMIT_REGISTER 12'
define(HEAP_LIMIT,%r12)dnl Change this together with line above
`#define RESTORE_HEAP_LIMIT	movq P_HP_LIMIT(P), 'HEAP_LIMIT`
#else
#define RESTORE_HEAP_LIMIT	/*empty*/
#endif'

define(NSP,%rsp)dnl
`#define NSP		'NSP
`#define SAVE_CSP	movq	%rsp, P_CSP(P)
#define RESTORE_CSP	movq	P_CSP(P), %rsp'

`#define AMD64_SIMULATE_NSP	'SIMULATE_NSP

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

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

`#define SAVE_CACHED_STATE	\
	SAVE_HP;		\
	SAVE_FCALLS'

`#define RESTORE_CACHED_STATE	\
	RESTORE_HP;		\
	RESTORE_HEAP_LIMIT;	\
	RESTORE_FCALLS'

`#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 AMD64_NR_ARG_REGS	'NR_ARG_REGS
`#define NR_ARG_REGS		'NR_ARG_REGS

define(defarg,`define(ARG$1,`$2')dnl
#`define ARG'$1	$2'
)dnl

ifelse(eval(NR_ARG_REGS >= 1),0,,
`defarg(0,`%rsi')')dnl
ifelse(eval(NR_ARG_REGS >= 2),0,,
`defarg(1,`%rdx')')dnl
ifelse(eval(NR_ARG_REGS >= 3),0,,
`defarg(2,`%rcx')')dnl
ifelse(eval(NR_ARG_REGS >= 4),0,,
`defarg(3,`%r8')')dnl
ifelse(eval(NR_ARG_REGS >= 5),0,,
`defarg(4,`%r9')')dnl
ifelse(eval(NR_ARG_REGS >= 6),0,,
`defarg(5,`%rdi')')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		%rbx'

dnl XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
dnl X								X
dnl X			hipe_amd64_glue.S support		X
dnl X								X
dnl XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

dnl
dnl LOAD_ARG_REGS
dnl (identical to x86 version except for movq)
dnl
define(LAR_1,`movq 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 (identical to x86 version except for movq)
dnl
define(SAR_1,`movq 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)	subq $8,NSP; leaq 1f(%rip),%rax; movq %rax,(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)	movq (NSP),TEMP_RV; addq $8+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	movq (NSP),TEMP_RV; addq $8,NSP; jmp *TEMP_RV'')dnl

dnl XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
dnl X								X
dnl X			hipe_amd64_bifs.m4 support		X
dnl X								X
dnl XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

dnl
dnl NBIF_ARG(DST,ARITY,ARGNO)
dnl Access a formal parameter.
dnl It will be a memory load via NSP when ARGNO >= NR_ARG_REGS.
dnl It will be a register move when 0 <= ARGNO < NR_ARG_REGS; if
dnl the source and destination are the same, the move is suppressed.
dnl
dnl This must be called before SWITCH_ERLANG_TO_C{,QUICK}.
dnl This must not be called if the C BIF's arity > 6.
dnl
define(NBIF_MOVE_REG,`ifelse($1,$2,`# movq	$2, $1',`movq	$2, $1')')dnl
define(NBIF_REG_ARG,`NBIF_MOVE_REG($1,ARG$2)')dnl
define(NBIF_STK_LOAD,`movq	$2(NSP), $1')dnl
define(NBIF_STK_ARG,`NBIF_STK_LOAD($1,eval(8*($2-$3)))')dnl
define(NBIF_ARG,`ifelse(eval($3 >= NR_ARG_REGS),0,`NBIF_REG_ARG($1,$3)',`NBIF_STK_ARG($1,$2,$3)')')dnl
`/* #define NBIF_ARG_1_0	'NBIF_ARG(%rsi,1,0)` */'
`/* #define NBIF_ARG_2_0	'NBIF_ARG(%rsi,2,0)` */'
`/* #define NBIF_ARG_2_1	'NBIF_ARG(%rdx,2,1)` */'
`/* #define NBIF_ARG_3_0	'NBIF_ARG(%rsi,3,0)` */'
`/* #define NBIF_ARG_3_1	'NBIF_ARG(%rdx,3,1)` */'
`/* #define NBIF_ARG_3_2	'NBIF_ARG(%rcx,3,2)` */'
`/* #define NBIF_ARG_5_0	'NBIF_ARG(%rsi,5,0)` */'
`/* #define NBIF_ARG_5_1	'NBIF_ARG(%rdx,5,1)` */'
`/* #define NBIF_ARG_5_2	'NBIF_ARG(%rcx,5,2)` */'
`/* #define NBIF_ARG_5_3	'NBIF_ARG(%r8,5,3)` */'
`/* #define NBIF_ARG_5_4	'NBIF_ARG(%r9,5,4)` */'

dnl XXX: For >6 arity C BIFs, we need:
dnl	NBIF_COPY_NSP(ARITY)
dnl	SWITCH_ERLANG_TO_C
dnl	NBIF_GE6_ARG_MOVE(DSTREG,ARITY,ARGNO)
dnl	pushq NBIF_GE6_ARG_OPND(ARITY,ARGNO) <-- uses NSP copied above

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(8*($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)` */'

`#endif /* HIPE_AMD64_ASM_H */'