/* src/vm/jit/s390/codegen.h - code generation macros for s390

   Copyright (C) 1996-2005, 2006, 2007 R. Grafl, A. Krall, C. Kruegel,
   C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
   E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
   J. Wenninger, Institut f. Computersprachen - TU Wien

   This file is part of CACAO.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2, or (at
   your option) any later version.

   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   02110-1301, USA.

*/


#ifndef _CODEGEN_H
#define _CODEGEN_H

#include "config.h"

#include <ucontext.h>

#include "vm/types.h"

#include "vm/jit/jit.h"


/* MCODECHECK(icnt) */

#define MCODECHECK(icnt) \
    do { \
        if ((cd->mcodeptr + (icnt)) > cd->mcodeend) \
            codegen_increase(cd); \
    } while (0)

/* some patcher defines *******************************************************/

#define PATCHER_CALL_SIZE    2          /* size in bytes of a patcher call    */
#define PATCHER_NOPS M_NOP3
#define PATCHER_NOPS_SKIP   2

/* branch defines ************************************************************/

#define BRANCH_NOPS \
	do { \
		if (CODEGENDATA_HAS_FLAG_LONGBRANCHES(cd)) { \
			M_NOP2; M_NOP2; /* brc */ \
			M_NOP2; M_NOP2; M_NOP2; M_NOP2; M_NOP2; M_NOP2; M_NOP2; M_NOP2; /* ild */ \
			M_NOP2; /* ar, bcr */ \
		} else { \
			M_NOP; /* brc */ \
		} \
	} while (0) 

/* stub defines **************************************************************/

#define COMPILERSTUB_CODESIZE    (SZ_AHI + SZ_L + SZ_L + SZ_BCR)

/* *** BIG TODO ***
 * Make all this inline functions !!!!!!!!!!
 */

/* macros to create code ******************************************************/

/* Conventions:
 * N_foo:   defines the instrucition foo as in `ESA/390 Principles of operations'
 * SZ_foo:  defines the size of the instruction N_foo
 * DD_foo:  defines a condition code as used by s390 GCC
 * M_foo:   defines the alpha like instruction used in cacao
 *          the instruction is defined by an equivalent N_ instruction
 * CC_foo:  defines a condition code as used
 *          the instruction is defined as an equivalent DD_ condition code
 */

/* S390 specific code */

/* Argument checks for debug mode */

/* Some instructions with register arguments treat %r0 as "value not given".
 * To prevent bugs, in debug mode we use a special value RN (reg none) with 
 * the meaning "value not given".
 * In debug mode, the instructions assert that %r0 was not given as argument.
 */

#if !defined(NDEBUG)

#	include <stdlib.h>

	/* register none */
#	define RN 16

	static inline int _OR_IMPL(const char *file, int line, int r) {
		if(!(
			((0 < r) && (r < 16)) ||
			(r == RN)
		)) {
			fprintf(stdout, "%d is not a valid register at %s:%d.\n", r, file, line);
			abort();
		}
		return ((r == RN) ? 0 : r);
	}
#	define _OR(r) _OR_IMPL(__FILE__, __LINE__, r)

#	define _SMIN(b) (-(1 << (bits - 1)))
#	define _SMAX(b) ((1 << (b - 1)) - 1)
#	define _UMIN(b) 0
#	define _UMAX(b) ((1 << b) - 1)

	static inline int _UBITS_IMPL(const char *file, int line, int i, int bits) {
		if (!((_UMIN(bits) <= i) && (i <= _UMAX(bits)))) {
			fprintf(stdout, "%d (0x%X) is not an unsigned %d bit integer at %s:%d.\n", i, i, bits, file, line);
			abort();
		}
		return i;
	}

#	define _UBITS(i, bits) _UBITS_IMPL(__FILE__, __LINE__, i, bits)

	static inline int _SBITS_IMPL(const char *file, int line, int i, int bits) {
		if(!((_SMIN(bits) <= i) && (i <= _SMAX(bits)))) {
			fprintf(stdout, "%d (0x%X) is not an signed %d bit integer at %s:%d.\n", i, i, bits, file, line);
			abort();
		}
		return i;
	}

#	define _SBITS(i, bits) _SBITS_IMPL(__FILE__, __LINE__, i, bits)

	static inline int _BITS_IMPL(const char *file, int line, int i, int bits) {
		if (!(
			((_UMIN(bits) <= i) && (i <= _UMAX(bits))) ||
			((_SMIN(bits) <= i) && (i <= _SMAX(bits)))
		)) {
			fprintf(stdout, "%d (0x%X) is not an %d bit integer at %s:%d.\n", i, i, bits, file, line);
			abort();
		}
		return i;
	}

#	define _BITS(i, bits) _BITS_IMPL(__FILE__, __LINE__, i, bits)

#else
#	define RN 0
#	define _OR(x) (x)
#	define _BITS(x, b) (x)
#	define _UBITS(x, b) (x)
#	define _SBITS(x, b) (x)
#endif

/* Register */
#define _R(x) _UBITS((x), 4)
/* Displacement */
#define _D(x) _UBITS((x), 12)
/* 4 bit Immediate */
#define _I4(x) _BITS((x), 4)
#define _UI4(x) _UBITS((x), 4)
#define _SI4(x) _SBITS((x), 4)
/* 8 bit Immediate */
#define _I8(x) _BITS((x), 8)
#define _UI8(x) _UBITS((x), 8)
#define _SI8(x) _SBITS((x), 8)
/* 12 bit Immediate */
#define _I12(x) _BITS((x), 12)
#define _UI12(x) _UBITS((x), 12)
#define _SI12(x) _SBITS((x), 12)
/* 16 bit Immediate */
#define _I16(x) _BITS((x), 16)
#define _UI16(x) _UBITS((x), 16)
#define _SI16(x) _SBITS((x), 16)
/* Opcode */
#define _OP(x) _UBITS((x), 8)
/* Second part of opcode */
#define _OP4(x) _UBITS((x), 4)
/* Extended opcode */
#define _OP16(x) _UBITS((x), 16)

/* Instruction formats */

#define _CODE(t, code) \
	do { \
		*((t *) cd->mcodeptr) = (code); \
		cd->mcodeptr += sizeof(t); \
	} while (0)

#define _CODE2(code) _CODE(u2, code)
#define _CODE4(code) _CODE(u4, code)

#define _IF(cond, t, f) \
	do { if (cond) { t ; } else { f ; } } while (0)

#define _IFNEG(val, neg, pos) _IF((val) < 0, neg, pos)

#define N_RR(op, r1, r2) \
	_CODE2( (_OP(op) << 8) | (_R(r1) << 4) | _R(r2) )

#define SZ_RR 2

static inline uint8_t N_RR_GET_OPC(uint8_t *instrp) {
	return instrp[0];
}

static inline uint8_t N_RR_GET_REG1(uint8_t *instrp) {
	return (instrp[1] >> 4) & 0xF;
}

static inline uint8_t N_RR_GET_REG2(uint8_t *instrp) {
	return (instrp[1] & 0xF);
}

#define N_RR2(op, i) \
	_CODE2( (_OP(op) << 8) | _I8(i) )

#define N_RX(op, r1, d2, x2, b2) \
	_CODE4( (_OP(op) << 24) | (_R(r1) << 20) | (_OR(x2) << 16) | (_OR(b2) << 12) | (_D(d2) << 0) )

#define SZ_RX 4

static inline uint8_t N_RX_GET_OPC(uint8_t *instrp) {
	return instrp[0];
}

static inline uint8_t N_RX_GET_REG(uint8_t *instrp) {
	return (instrp[1] >> 4) & 0xF;	
}

static inline uint8_t N_RX_GET_INDEX(uint8_t *instrp) {
	return (instrp[1] & 0xF);	
}

static inline uint8_t N_RX_GET_BASE(uint8_t *instrp) {
	return (instrp[2] >> 4) & 0xF;
}

static inline uint16_t N_RX_GET_DISP(uint8_t *instrp) {
	return *(uint16_t *)(instrp + 2) & 0xFFF;
}

static inline void N_RX_SET_DISP(uint8_t *instrp, uint16_t disp) {
	*(uint16_t *)(instrp + 2) |= (disp & 0xFFF);
}

#define N_RI(op1, op2, r1, i2) \
	_CODE4( (_OP(op1) << 24) | (_R(r1) << 20) | (_OP4(op2) << 16) | (u2)_SI16(i2) )

static inline int16_t N_RI_GET_IMM(uint8_t *instrp) {
	return *(int16_t *)(instrp + 2);
}

static inline void N_RI_SET_IMM(uint8_t *instrp, int16_t imm) {
	*(int16_t *)(instrp + 2) = imm;
}

#define N_RI2(op1, op2, r1, i2) \
	_CODE4( (_OP(op1) << 24) | (_R(r1) << 20) | (_OP4(op2) << 16) | (u2)_UI16(i2) )

#define SZ_RI 4

#define N_SI(op, d1, b1, i2) \
	_CODE4( (_OP(op) << 24) | (_OR(i2) << 16) | (_OR(b1) << 12) | _D(d1) )

#define SZ_SI 4

#define N_SS(op, d1, l, b1, d2, b2) \
	do { \
		_CODE4( (_OP(op) << 24) | (_I8(l) << 16) | (_OR(b1) << 12) | _D(d1) ); \
		_CODE2( (_OR(b2) << 12) | _D(d2) ); \
	} while (0)

#define SZ_SS 6

#define N_SS2(op, d1, l1, b1, d2, l2, b2) \
	N_SS(op, d1, (_I4(l1) << 4) | _I4(l2), b1, d2, l2)

#define N_RS(op, r1, r3, d2, b2) \
	_CODE4( (_OP(op) << 24) | (_R(r1) << 20) | (_R(r3) << 16) | (_OR(b2) << 12) | _D(d2) )

#define SZ_RS 4

#define N_RSI(op, r1, r2, i2) \
	_CODE4( ((op) << 24) | (_R(r1) << 20) | (_R(r3) << 16) | (u2)_16(i2) )

#define SZ_RSI 4

#define N_RRE(op, r1, r2) \
	_CODE4( (_OP16(op) << 16) | (_R(r1) << 4) | _R(r2) )

#define SZ_RRE 4

#define N_S2(d2, b2) \
	_CODE4( (_OP16(op) << 16) | (_OR(b2) << 12) | _D(d2)  )

#define SZ_S2 4

#define N_E(op) \
	_CODE2( _OP16(op) )

#define SZ_E 2

#define N_RXE(op, r1, d2, x2, b2) \
	do { \
		_CODE4( ((_OP16(op) >> 8) << 24) | (_R(r1) << 20) | \
			(_R(x2) << 16) | (_R(b2) << 12) | _UI12(d2) ); \
		_CODE2( _OP16(op) & 0xFF ); \
	} while (0) 

#define S_RXE 6

#define N_RRF(op, r1, m3, r2) \
	_CODE4( (_OP16(op) << 16) | (_R(m3) << 12) | (_R(r1) << 4) | _R(r2) )

#define S_RRF 4

#define N_IMM_MIN -32768
#define N_IMM_MAX 32767
#define N_VALID_IMM(x) ((N_IMM_MIN <= (x)) && ((x) <= N_IMM_MAX))
#define ASSERT_VALID_IMM(x) assert(N_VALID_IMM(x))

#define N_DISP_MIN 0
#define N_DISP_MAX 0xFFF
#define N_VALID_DISP(x) ((N_DISP_MIN <= (x)) && ((x) <= N_DISP_MAX))
#define ASSERT_VALID_DISP(x) assert(N_VALID_DISP(x))

#define N_PV_OFFSET (-0xFFC)
#define N_DSEG_DISP(x) ((x) - N_PV_OFFSET)
#define N_VALID_DSEG_DISP(x) N_VALID_DISP(N_DSEG_DISP(x))

#define N_BRANCH_MIN (-32768 * 2)
#define N_BRANCH_MAX (32767 * 2)
#define N_VALID_BRANCH(x) ((N_BRANCH_MIN <= (x)) && ((x) <= N_BRANCH_MAX))
#define ASSERT_VALID_BRANCH(x) assert(N_VALID_BRANCH(x))

#define N_IS_EVEN_ODD(x) \
	(((GET_HIGH_REG(x) % 2) == 0) && (GET_LOW_REG(x) == (GET_HIGH_REG(x) + 1)))

/* Condition codes */

#define DD_O 1
#define DD_H 2
#define DD_NLE 3
#define DD_L 4
#define DD_NHE 5
#define DD_LH 6
#define DD_NE 7
#define DD_E 8
#define DD_NLH 9
#define DD_HE 10
#define DD_NL 11
#define DD_LE 12
#define DD_NH 13
#define DD_NO 14
#define DD_ANY 15

#define DD_0 8
#define DD_1 4
#define DD_2 2
#define DD_3 1

/* Misc */

/* Trap instruction.
 * If most significant bits of first opcode byte are 00, then
 * format is RR (1 byte opcode) or E (2 bytes opcode). 
 * There seems to be no opcode 0x02 or 0x02**, so we'll define
 * our trap instruction as:
 * +--------+--------+
 * |  0x02  |  data  |
 * +--------+--------+
 * 0                 15
 */
#define N_ILL(data) _CODE2(0x0200 | _UBITS(data, 8))
#	define OPC_ILL 0x02
#	define SZ_ILL 2

static inline uint8_t N_ILL_GET_REG(uint8_t *instrp) {
	return (instrp[1] >> 4) & 0xF;
}

static inline uint8_t N_ILL_GET_TYPE(uint8_t *instrp) {
	return (instrp[1] & 0xF);
}

#define N_LONG(l) _CODE4(l)
#define SZ_LONG 4

/* Chapter 7. General instructions */

#define N_AR(r1, r2) N_RR(0x1A, r1, r2)
#define N_A(r1, d2, x2, b2) N_RX(0x5A, r1, d2, x2, b2)
#define N_AH(r1, d2, x2, b2) N_RX(0x4A, r1, d2, x2, b2)
#define N_AHI(r1, i2) N_RI(0xA7, 0xA, r1, i2)
#	define SZ_AHI SZ_RI
#define N_ALR(r1, r2) N_RR(0x1E, r1, r2)
#define N_AL(r1, d2, x2, b2) N_RX(0x5E, r1, d2, x2, b2)
#define N_NR(r1, r2) N_RR(0x14, r1, r2)
#	define SZ_NR SZ_RR
#define N_N(r1, d2, x2, b2) N_RX(0x54, r1, d2, x2, b2)
#define N_NI(d1, b1, i2) N_SI(0x94, d1, b1, i2)
#define N_NC(d1, l, b1, d2, b2) N_SS(0xD4, (l - 1), b1, d1, b2, d2)
#define N_BALR(r1, r2) N_RR(0x05, r1, _OR(r2))
#define N_BAL(r1, d2, x2, b2) N_RX(0x45, r1, d2, x2, b2)
#define N_BASR(r1, r2) N_RR(0x0D, r1, _OR(r2))
#	define SZ_BASR SZ_RR
#define N_BAS(r1, d2, x2, b2) N_RX(0x4D, r1, d2, x2, b2)
#define N_BASSM(r1, r2) N_RR(0x0C, r1, _OR(r2))
#define N_BSM(r1, r2) N_RR(0x0B, r1, _OR(r2))
#define N_BCR(m1, r2) N_RR(0x07, m1, _OR(r2))
#	define SZ_BCR SZ_RR
#	define N_BR(r2) N_BCR(DD_ANY, r2)
#define N_BC(m1, d2, x2, b2) N_RX(0x47, m1, d2, x2, b2)
#	define SZ_BC SZ_RS
#define N_BCTR(r1, r2) N_RR(0x06, r1, _OR(r2))
#define N_BCT(r1, d2, x2, b2) N_RX(0x46, r1, d2, x2, b2)
#define N_BHX(r1, r2, d2, b2) N_RS(0xB6, r1, r3, d2, b2)
#define N_BXLE(r1, r3, d2, b2) N_RS(0xB7, r1, r3, d2, b2)
#define N_BRAS(r1, i2) N_RI(0xA7, 0x5, r1, (i2) / 2)
#	define SZ_BRAS SZ_RI
#define N_BRC(m1, i2) N_RI(0xA7, 0x4, m1, (i2) / 2)
#	define N_J(i2) N_BRC(DD_ANY, i2)
#	define SZ_BRC SZ_RI
#	define SZ_J SZ_RI
#	define N_BRC_BACK_PATCH(brc_pos) \
		do { \
			*(u4 *)(brc_pos) |= (u4)(cd->mcodeptr - (brc_pos)) / 2; \
		} while (0)
#define N_BRCT(r1, i2) N_RI(0xA7, 0x6, r1, (i2) / 2)
#define N_BRXH(r1, r3, i2) N_RSI(0x84, r1, r3, (i2) / 2)
#define N_BRXLE(r1, r3, i2) N_RSI(0x85, r1, r2, (i2) / 2)
#define N_CKSM(r1, r2) N_RRE(0xB241, r1, r2)
#define N_CR(r1, r2) N_RR(0x19, r1, r2)
#	define SZ_CR SZ_RR
#define N_C(r1, d2, x2, b2) N_RX(0x59, r1, d2, x2, b2)
#define N_CFC(d2, b2) N_S2(0xB21A, d2, b2)
#define N_CS(r1, r3, d2, b2) N_RS(0xBA, r1, r3, d2, b2)
#define N_CDS(r1, r3, d2, b2) N_RS(0xBB, r1, r3, d2, b2)
#define N_CH(r1, d2, x2, b2) N_CH(0x49, r1, d2, x2, b2)
#define N_CHI(r1, i2) N_RI(0xA7, 0xE, r1, i2)
#define N_CLR(r1, r2) N_RR(0x15, r1, r2)
#define N_CL(r1, d2, x2, b2) N_RX(0x55, r1, d2, x2, b2)
#	define OPC_CL 0x55
#define N_CLI(d1, b1, i2) N_SI(0x95, d1, b1, i2)
#define N_CLC(d1, l, b1, d2, b2) N_SS(0xD5, d1, (l - 1), b1, d2, b2)
#define N_CLM(r1, m3, d2, b2) N_RS(0xBD, r1, m3, d2, b2)
#define N_CLCL(r1, r2) N_RR(0x0F, r1, r2)
#define N_CLCLE(r1, r3, d2, b2) N_RS(0xA9, r1, r3, d2, b2)
#define N_CLST(r1, r2) N_RRE(0xB25D, r1, r2)
#define N_CUSE(r1, r2) N_RRE(0xB257, r1, r2)
#define N_CVB(r1, d2, x2, b2) N_RX(0x4F, r1, r2, x2, b2)
#define N_CVD(r1, d2, x2, b2) N_RX(0x4E, r1, d2, x2, b2)
#define N_CUUTF(r1, r2) N_RRE(0xB2A6, r1, r2)
#define N_CUTFU(r1, r2) N_RRE(0xB2A7, r1, r2)
#define N_CPYA(r1, r2) N_RRE(0xB240, r1, r2)
#define N_DR(r1, r2) N_RR(0x1D, r1, r2)
#	define OPC_DR 0x1D
#define N_D(r1, d2, x2, b2) N_RX(0x5D, r1, d2, x2, b2)
#define N_XR(r1, r2) N_RR(0x17, r1, r2)
#define N_X(r1, d2, x2, b2) N_RX(0x57, r1, d2, x2, b2)
#define N_XI(d1, b1, i2) N_SI(0x97, d1, b1, i2)
#define N_XC(d1, l, b1, d2, b2) N_SS(0xD7, d1, (l - 1), b1, d2, b2)
#define N_EX(r1, d2, x2, b2) N_RX(0x44, r1, d2, x2, b2)
#define N_EAR(r1, r2) N_RRE(0xB24F, r1, r2)
#define N_IC(r1, d2, x2, b2) N_RX(0x43, r1, d2, x2, b2)
#define N_ICM(r1, m3, d2, b2) N_RS(0xBF, r1, m3, d2, b2)
#define N_IPM(r1) N_RRE(0xB222, r1, 0)
#define N_LR(r1, r2) N_RR(0x18, r1, r2)
#define N_L(r1, d2, x2, b2) N_RX(0x58, r1, d2, x2, b2)
#	define SZ_L SZ_RX
#	define OPC_L 0x58
#define N_LAM(r1, r3, d2, b2) N_RS(0x9A, r1, r3, d2, b2)
#define N_LA(r1, d2, x2, b2) N_RX(0x41, r1, d2, x2, b2)
#define N_LAE(r1, d2, x2, b2) N_RX(0x51, r1, d2, x2, b2)
#define N_LTR(r1, r2) N_RR(0x12, r1, r2)
#define N_LCR(r1, r2) N_RR(0x13, r1, r2)
#	define SZ_LCR SZ_RR
#define N_LH(r1, d2, x2, b2) N_RX(0x48, r1, d2, x2, b2)
#define N_LHI(r1, i2) N_RI(0xA7, 0x8, r1, i2)
#	define SZ_LHI SZ_RI
#define N_LM(r1, r3, d2, b2) N_RS(0x98, r1, r3, d2, b2)
#define N_LNR(r1, r2) N_RR(0x11, r1, r2)
#define N_LPR(r1, r2) N_RR(0x10, r1, r2)
#define N_MC(d1, b1, i2) N_SI(0xAF, d1, b1, i2)
#define N_MVI(d1, b1, i2) N_SI(0x92, d1, b1, i2)
#define N_MVC(d1, l, b1, d2, b2) N_SS(0xD2, d1, (l - 1), b1, d2, b2)
#define N_MVCIN(d1, l, b1, d2, b2) N_SS(0xEB, d1, (l - 1), b1, d2, b2)
#define N_MVCL(r1, r2) N_RR(0x0E, r1, r2)
#define N_MVCLE(r1, r3, d2, b2)  N_RS(0xAB, r1, r3, d2, b2)
#define N_MVN(d1, l, b1, d2, b2) N_SS(0xD1, d1, (l - 1), b1, d2, b2)
#define N_MVPG(r1, r2) N_RRE(0xB254, r1, r2)
#define N_MVST(r1, r2) N_RRE(0xB255, r1, r2)
#define N_MVO(d1, l1, b1, d2, l2, b2) N_SS2(0xF1, d1, (l1 - 1), b1, d2, (l2 - 1), b2)
#define N_MVZ(d1, l, b1, d2, b2) N_SS(0xD3, d1, (l - 1), b1, d2, b2)
#define N_MR(r1, r2) N_RR(0x1C, r1, r2)
#define N_M(r1, d2, x2, b2) N_RX(0x5C, r1, d2, x2, b2)
#define N_MH(r1, d2, x2, b2) N_RX(0x4C, r1, d2, x2, b2)
#define N_MHI(r1, i2) N_RI(0xA7, 0xC, r1, i2)
#define N_MSR(r1, r2) N_RRE(0xB252, r1, r2)
#define N_MS(r1, d2, x2, b2) N_RX(0x71, r1, d2, x2, b2)
#define N_OR(r1, r2) N_RR(0x16, r1, r2)
#define N_O(r1, d2, x2, b2) N_RX(0x56, r1, d2, x2, b2)
#define N_OI(d1, b1, i2) N_SI(0x96, d1, b1, i2)
#define N_OC(d1, l, b1, d2, b2) N_SS(0xD6, d1, (l - 1), b1, d2, b2)
#define N_PACK(d1, l1, b1, d2, l2, b2) N_SS2(0xF2, d1, (l1 - 1), b1, d2, (l2 - 1), b2)
#define N_PLO(r1, d2, b2, r3, d4, b4) N_SS2(0xEE, d2, r1, b2, d4, r3, b4)
#define N_SRST(r1, r2) N_RRE(0xB25E, r1, r2)
#define N_SAR(r1, r2) N_RRE(0xB24E, r1, r2)
#define N_SPM(r1) N_RR(0x04, r1, 0x00)
#define N_SLDA(r1, d2, b2) N_RS(0x8F, r1, 0x00, d2, b2)
#define N_SLDL(r1, d2, b2) N_RS(0x8D, r1, 0x00, d2, b2)
#define N_SLA(r1, d2, b2) N_RS(0x8B, r1, 0x00, d2, b2)
#define N_SLL(r1, d2, b2) N_RS(0x89, r1, 0x00, d2, b2)
#define N_SRDA(r1, d2, b2) N_RS(0x8E, r1, 0x00, d2, b2)
#define N_SRDL(r1, d2, b2) N_RS(0x8C, r1, 0x00, d2, b2)
#define N_SRA(r1, d2, b2) N_RS(0x8A, r1, 0x00, d2, b2)
#define N_SRL(r1, d2, b2) N_RS(0x88, r1, 0x00, d2, b2)
#define N_ST(r1, d2, x2, b2) N_RX(0x50, r1, d2, x2, b2)
#	define OPC_ST 0x50
#define N_STAM(r1, r3, d2, b2) N_RS(0x9B, r1, r3, d2, b2)
#define N_STC(r1, d2, x2, b2) N_RX(0x42, r1, d2, x2, b2)
#define N_STCM(r1, m3, d2, b2) N_RS(0xBE, r1, m3, d2, b2)
#define N_STCK(d2, b2) N_S2(0xB205, d2, b2)
#define N_STCKE(d2, b2) N_S2(0xB278, d2, b2)
#define N_STH(r1, d2, x2, b2) N_RX(0x40, r1, d2, x2, b2)
#define N_STM(r1, r3, d2, b2) N_RS(0x90, r1, r3, d2, b2)
#define N_SR(r1, r2) N_RR(0x1B, r1, r2)
#define N_S(r1, d2, x2, b2) N_RX(0x5B, r1, d2, x2, b2)
#define N_SH(r1, d2, x2, b2) N_RX(0x4B, r1, d2, x2, b2)
#define N_SLR(r1, r2) N_RR(0x1F, r1, r2)
#define N_SL(r1, d2, x2, b2) N_RX(0x5F, r1, d2, x2, b2)
#define N_SVC(i) N_RR2(0x0A, i)
#define N_TS(d2, b2) N_S2(0x93, d2, b2)
#define N_TM(d1, b1, i2) N_SI(0x91, d1, b1, i2)
#define N_TMH(r1, i2) N_RI2(0xA7, 0x00, r1, i2)
#define N_TML(r1, i2) N_RI2(0xA7, 0x01, r1, i2)
#define N_TR(d1, l, b1, d2, b2) N_SS(0xDC, d1, (l - 1), b1, d2, b2)
#define N_TRT(d1, l, b1, d2, b2) N_SS(0xDD, d1, (l - 1), b1, d2, b2)
#define N_TRE(r1, r2) N_RRE(0xB2A5, r1, r2)
#define N_UNPK(d1, l1, b1, d2, l2, b2) N_SS2(0xF3, d1, (l1 - 1), b1, d2, (l2 - 2), b2)
#define N_UPT() N_E(0x0102)

/* Chapter 9. Floating point instructions */

#define N_LER(r1, r2) N_RR(0x38, r1, r2)
#define N_LDR(r1, r2) N_RR(0x28, r1, r2)
#define N_LXR(r1, r2) N_RRE(0xB365, r1, r2)
#define N_LE(r1, d2, x2, b2) N_RX(0x78, r1, d2, x2, b2)
#define N_LD(r1, d2, x2, b2) N_RX(0x68, r1, d2, x2, b2)
#define N_LZER(r1) N_RRE(0xB374, r1, 0x0)
#define N_LZDR(r1) N_RRE(0xB375, r1, 0x0)
#define N_LZXR(r1) N_RRE(0xB376, r1, 0x0)
#define N_STE(r1, d2, x2, b2) N_RX(0x70, r1, d2, x2, b2)
#define N_STD(r1, d2, x2, b2) N_RX(0x60, r1, d2, x2, b2)

/* chapter 19. Binary floating point instructions */

#define N_AEBR(r1, r2) N_RRE(0xB30A, r1, r2)
#define N_ADBR(r1, r2) N_RRE(0xB31A, r1, r2)
#define N_AXBR(r1, r2) N_RRE(0xB34A, r1, r2)
#define N_AEB(r1, d2, x2, b2) N_RXE(0xED0A, r1, d2, x2, b2)
#define N_ADB(r1, d2, x2, b2) N_RXE(0xED1A, r1, d2, x2, b2)

#define N_CEBR(r1, r2) N_RRE(0xB309, r1, r2)
#define N_CDBR(r1, r2) N_RRE(0xB319, r1, r2)
#define N_CXBR(r1, r2) N_RRE(0xB349, r1, r2)
#define N_CEB(r1, d2, x2, b2) N_RXE(0xED09, r1, d2, x2, b2)
#define N_CDB(r1, d2, x2, b2) N_RXE(0xED19, r1, d2, x2, b2)

#define N_CEFBR(r1, r2) N_RRE(0xB394, r1, r2)
#define N_CDFBR(r1, r2) N_RRE(0xB395, r1, r2)
#define N_CXFBR(r1, r2) N_RRE(0xB396, r1, r2)

#define N_CFEBR(r1, m3, r2) N_RRF(0xB398, r1, m3, r2)
#define N_CFDBR(r1, m3, r2) N_RRF(0xB399, r1, m3, r2)
#define N_CFXBR(r1, m3, r2) N_RRF(0xB39A, r1, m3, r2)

#define N_DEBR(r1, r2) N_RRE(0xB30D, r1, r2)
#define N_DDBR(r1, r2) N_RRE(0xB31D, r1, r2)
#define N_DXBR(r1, r2) N_RRE(0xB34D, r1, r2)
#define N_DEB(r1, d2, x2, b2) N_RXE(0xED0D, r1, d2, x2, b2)
#define N_DDB(r1, d2, x2, b2) N_RXE(0xED1D, r1, d2, x2, b2)

#define N_LCEBR(r1, r2) N_RRE(0xB303, r1, r2)
#define N_LCDBR(r1, r2) N_RRE(0xB313, r1, r2)
#define N_LCXBR(r1, r2) N_RRE(0xB343, r1, r2)

#define N_LDEBR(r1, r2) N_RRE(0xB304, r1, r2)
#	define SZ_LDEBR SZ_RRE
#define N_LXDBR(r1, r2) N_RRE(0xB305, r1, r2)
#define N_LXEBR(r1, r2) N_RRE(0xB306, r1, r2)

#define N_LEDBR(r1, r2) N_RRE(0xB344, r1, r2)
#define N_LDXBR(r1, r2) N_RRE(0xB345, r1, r2)
#define N_LEXBR(r1, r2) N_RRE(0xB346, r1, r2)

#define N_LTEBR(r1, r2) N_RRE(0xB302, r1, r2)
#define N_LTDBR(r1, r2) N_RRE(0xB312, r1, r2)
#define N_LTXBR(r1, r2) N_RRE(0xB342, r1, r2)

#define N_MEEBR(r1, r2) N_RRE(0xB317, r1, r2)
#define N_MDBR(r1, r2) N_RRE(0xB31C, r1, r2)
#define N_MXBR(r1, r2) N_RRE(0xB34C, r1, r2)
#define N_MDEBR(r1, r2) N_RRE(0xB30C, r1, r2)
#define N_MXDBR(r1, r2) N_RRE(0xB307, r1, r2)

#define N_SEBR(r1, r2) N_RRE(0xB30B, r1, r2)
#define N_SDBR(r1, r2) N_RRE(0xB31B, r1, r2)
#define N_SXBR(r1, r2) N_RRE(0xB34B, r1, r2)
#define N_SEB(r1, d2, x2, b2) N_RXE(0xED0B, r1, d2, x2, b2)
#define N_SDB(r1, d2, x2, b2) N_RXE(0xED1B, r1, d2, x2, b2)

/* Alpha like instructions */

#define M_CALL(r2) N_BASR(R14, r2)
#define M_ILL(data) N_ILL(data)
#define M_ILL2(data1, data2) N_ILL((_UBITS(data1, 4) << 4) | _UBITS(data2, 4))
#define M_LONG(l) N_LONG(l)

#define M_ILD(r, b, d) \
	do { \
		if (N_VALID_DISP(d)) { \
			N_L(r, d, RN, b); \
		} else if ((r == R0) && N_VALID_IMM(d)) { \
			N_LR(R0, R1); \
			N_LHI(R1, d); \
			N_L(R1, 0, R1, b); \
			N_XR(R1, R0); \
			N_XR(R0, R1); \
			N_XR(R1, R0); \
		} else if ((r != R0) && N_VALID_IMM(d)) { \
			N_LHI(r, d); N_L(r, 0, r, b); \
		} else { \
			N_BRAS(r, SZ_BRAS + SZ_LONG); \
			N_LONG(d); \
			N_L(r, 0, RN, r); \
			N_L(r, 0, r, b); \
		} \
	} while (0)

#define M_ILD_DSEG(r, d) M_ILD(r, REG_PV, N_DSEG_DISP(d))

#define M_ALD(r, b, d) M_ILD(r, b, d)
#define M_ALD_DSEG(r, d) M_ALD(r, REG_PV, N_DSEG_DISP(d))

#define M_LDA(r, b, d) \
	do { \
		if (N_VALID_DISP(d)) { \
			N_LA(r, d, RN, b); \
		} else if (N_VALID_IMM(d)) { \
			N_LHI(r, d); \
			N_LA(r, 0, r, b); \
		} else { \
			N_BRAS(r, SZ_BRAS + SZ_LONG); \
			N_LONG(d); \
			N_L(r, 0, RN, r); \
			N_LA(r, 0, r, b); \
		} \
	} while (0)
#define M_LDA_DSEG(r, d) M_LDA(r, REG_PV, N_DSEG_DISP(d))

#define M_FLD(r, b, d) N_LE(r, d, RN, b)
#define M_FLDN(r, b, d, t) _IFNEG( \
	d, \
	N_LHI(t, d); N_LE(r, 0, t, b), \
	N_LE(r, d, RN, b) \
)
#define M_FLD_DSEG(r, d, t) M_FLDN(r, REG_PV, N_DSEG_DISP(d), t)

#define M_DLD(r, b, d) N_LD(r, d, RN, b)
#define M_DLDN(r, b, d, t) _IFNEG( \
	d, \
	N_LHI(t, d); N_LD(r, 0, t, b), \
	N_LD(r, d, RN, b) \
)
#define M_DLD_DSEG(r, d, t) M_DLDN(r, REG_PV, N_DSEG_DISP(d), t)

#define M_LLD(r, b, d) _IFNEG( \
	d, \
	N_LHI(GET_LOW_REG(r), d); \
		N_L(GET_HIGH_REG(r), 0, GET_LOW_REG(r), b); \
		N_L(GET_LOW_REG(r), 4, GET_LOW_REG(r), b), \
	N_L(GET_HIGH_REG(r), (d) + 0, RN, b); N_L(GET_LOW_REG(r), (d) + 4, RN, b) \
)
#define M_LLD_DSEG(r, d) M_LLD(r, REG_PV, N_DSEG_DISP(d)

/* MOV(a, b) -> mov from A to B */

#define M_MOV(a, b) N_LR(b, a)
#define M_FMOV(a, b) N_LDR(b, a)
#define M_DST(r, b, d) _IFNEG(d, assert(0), N_STD(r, d, RN, b))
#define M_FST(r, b, d) _IFNEG(d, assert(0), N_STE(r, d, RN, b))
#define M_IST(r, b, d) _IFNEG( \
	d, \
	assert(0), \
	N_ST(r, d, RN, b) \
)
#define M_AST(r, b, d) M_IST(r, b, d)
#define M_LST(r, b, d) _IFNEG( \
	d, \
	assert(0), \
	N_ST(GET_HIGH_REG(r), (d) + 0, RN, b); N_ST(GET_LOW_REG(r), (d) + 4, RN, b) \
)
#define M_TEST(r) N_LTR(r, r)
#define M_BEQ(off) N_BRC(DD_E, off)
#define M_BNE(off) N_BRC(DD_NE, off)
#define M_BLE(off) N_BRC(DD_LE, off)
#define M_BGT(off) N_BRC(DD_H, off)
#define M_BLT(off) N_BRC(DD_L, off)
#define M_BGE(off) N_BRC(DD_HE, off)
#define M_BO(off) N_BRC(DD_O, off)

#define M_CMP(r1, r2) N_CR(r1, r2)
#define M_CMPU(r1, r2) N_CLR(r1, r2)
#define M_CLR(r) N_LHI(r, 0)
#define M_AADD_IMM(val, reg) N_AHI(reg, val)
#define M_IADD_IMM(val, reg) N_AHI(reg, val)
#define M_ISUB_IMM(val, reg) N_AHI(reg, -(val))
#define M_ASUB_IMM(val, reg) N_AHI(reg, -(val))
#define M_RET N_BCR(DD_ANY, R14)
#define M_BSR(ret_reg, disp) N_BRAS(ret_reg, disp)
#define M_BR(disp) N_BRC(DD_ANY, disp)
#define M_JMP(rs, rd) _IF(rs == RN, N_BCR(DD_ANY, rd), N_BASR(rs, rd))
#define M_NOP N_BC(0, 0, RN, RN)
#define M_NOP2 N_BCR(0, RN)
#define M_NOP3 N_BCR(0, 1)
#define M_JSR(reg_ret, reg_addr) N_BASR(reg_ret, reg_addr)
#define M_ICMP(a, b) N_CR(a, b)
#define M_ICMPU(a, b) N_CLR(a, b)
#define M_ICMP_IMM(a, b) N_CHI(a, b)
#define M_CVTIF(src, dst) N_CEFBR(dst, src)
#define M_CVTID(src, dst) N_CDFBR(dst, src)
#define M_FMUL(a, dest) N_MEEBR(dest, a)
#define M_FSUB(a, dest) N_SEBR(dest, a)
#define M_FADD(a, dest) N_AEBR(dest, a)
#define M_FDIV(a, dest) N_DEBR(dest, a)
#define M_DMUL(a, dest) N_MDBR(dest, a)
#define M_DSUB(a, dest) N_SDBR(dest, a)
#define M_DADD(a, dest) N_ADBR(dest, a)
#define M_DDIV(a, dest) N_DDBR(dest, a)
#define M_CVTFI(src, dst) N_CFEBR(dst, 5, src)
#define M_CVTDI(src, dst) N_CFDBR(dst, 5, src)
#define M_IADD(a, dest) N_AR(dest, a)
#define M_AADD(a, dest) N_AR(dest, a)
#define M_ISUB(a, dest) N_SR(dest, a)
#define M_ASUB(a, dest) N_SR(dest, a)
#define M_IAND(a, dest) N_NR(dest, a)
#define M_IOR(a, dest) N_OR(dest, a)
#define M_IXOR(a, dest) N_XR(dest, a)
#define M_CVTFD(src,dst) N_LDEBR(dst, src)
#define M_CVTDF(src,dst) N_LEDBR(dst, src)

#define M_SLL_IMM(imm, reg) N_SLL(reg, imm, RN) 
#define M_SLA_IMM(imm, reg) N_SLA(reg, imm, RN) 

#define M_SLDL_IMM(imm, reg) N_SLDL(reg, imm, RN) 
#define M_SLDA_IMM(imm, reg) N_SLDA(reg, imm, RN) 

#define M_SRL_IMM(imm, reg) N_SRL(reg, imm, RN)
#define M_SRA_IMM(imm, reg) N_SRA(reg, imm, RN)

#define M_SRDL_IMM(imm, reg) N_SRDL(reg, imm, RN)
#define M_SRDA_IMM(imm, reg) N_SRDA(reg, imm, RN)

#define M_SLL(op, dst) N_SLL(dst, 0, op)
#define M_SLA(op, dst) N_SLA(dst, 0, op)

#define M_SLDL(op, dst) N_SLDL(dst, 0, op)
#define M_SLDA(op, dst) N_SLDA(dst, 0, op)

#define M_SRL(op, dst) N_SRL(dst, 0, op)
#define M_SRA(op, dst) N_SRA(dst, 0, op)

#define M_SRDL(op, dst) N_SRDL(dst, 0, op)
#define M_SRDA(op, dst) N_SRDA(dst, 0, op)

#define M_IMUL_IMM(val, reg) N_MHI(reg, val)
#define M_IMUL(a, dest) N_MSR(dest, a)

#define M_INEG(a, dest) N_LCR(dest, a)

#define M_FCMP(a, b) N_CEBR(a, b)
#define M_DCMP(a, b) N_CDBR(a, b)

#define M_FMOVN(r, dst) N_LCEBR(dst, r)
#define M_DMOVN(r, dst) N_LCDBR(dst, r)

#define ICONST(reg, i) \
	do { \
		if (N_VALID_IMM(i)) { \
			N_LHI(reg, i); \
		} else { \
			disp = dseg_add_s4(cd, (i)); \
			M_ILD_DSEG(reg, disp); \
		} \
	} while (0) 

#define LCONST(reg,c) \
	do { \
	    ICONST(GET_HIGH_REG((reg)), (s4) ((s8) (c) >> 32));	\
	    ICONST(GET_LOW_REG((reg)), (s4) ((s8) (c))); \
	} while (0)

/* M_INTMOVE:
    generates an integer-move from register a to b.
    if a and b are the same int-register, no code will be generated.
*/ 

#define M_INTMOVE(reg,dreg) \
    do { \
        if ((reg) != (dreg)) { \
            M_MOV(reg, dreg); \
        } \
    } while (0)

#define M_LNGMOVE(a, b) \
    do { \
        if (GET_HIGH_REG(a) == GET_LOW_REG(b)) { \
            assert((GET_LOW_REG(a) != GET_HIGH_REG(b))); \
            M_INTMOVE(GET_HIGH_REG(a), GET_HIGH_REG(b)); \
            M_INTMOVE(GET_LOW_REG(a), GET_LOW_REG(b)); \
        } else { \
            M_INTMOVE(GET_LOW_REG(a), GET_LOW_REG(b)); \
            M_INTMOVE(GET_HIGH_REG(a), GET_HIGH_REG(b)); \
        } \
    } while (0)

/* M_FLTMOVE:
    generates a floating-point-move from register a to b.
    if a and b are the same float-register, no code will be generated
*/ 

#define M_FLTMOVE(reg,dreg) \
    do { \
        if ((reg) != (dreg)) { \
            M_FMOV(reg, dreg); \
        } \
    } while (0)

#define M_ISUB_IMM32(imm, tmpreg, reg) \
	do { \
		if (N_VALID_IMM(imm)) { \
			M_ISUB_IMM(imm, reg); \
		} else { \
			ICONST(tmpreg, imm); \
			M_ISUB(tmpreg, reg); \
		} \
	} while (0)

#define M_ASUB_IMM32(imm, tmpreg, reg) M_ISUB_IMM32(imm, tmpreg, reg)

#define PROFILE_CYCLE_START 

#define PROFILE_CYCLE_STOP 

#endif /* _CODEGEN_H */

/*
 * These are local overrides for various environment variables in Emacs.
 * Please do not remove this and leave it at the end of the file, where
 * Emacs will automagically detect them.
 * ---------------------------------------------------------------------
 * Local variables:
 * mode: c
 * indent-tabs-mode: t
 * c-basic-offset: 4
 * tab-width: 4
 * End:
 */
