/* src/vm/jit/x86_64/emit.c - x86_64 code emitter functions

   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.

   $Id: emit.c 7813 2007-04-25 19:20:13Z twisti $

*/

#include "config.h"

#include <assert.h>

#include "vm/types.h"

#include "md-abi.h"

#include "vm/jit/x86_64/codegen.h"
#include "vm/jit/x86_64/emit.h"

#include "mm/memory.h"

#include "threads/lock-common.h"

#include "vm/builtin.h"
#include "vm/exceptions.h"

#include "vm/jit/abi.h"
#include "vm/jit/abi-asm.h"
#include "vm/jit/asmpart.h"
#include "vm/jit/codegen-common.h"
#include "vm/jit/emit-common.h"
#include "vm/jit/jit.h"
#include "vm/jit/replace.h"

#include "vmcore/options.h"


/* emit_load *******************************************************************

   Emits a possible load of an operand.

*******************************************************************************/

s4 emit_load(jitdata *jd, instruction *iptr, varinfo *src, s4 tempreg)
{
	codegendata  *cd;
	s4            disp;
	s4            reg;

	/* get required compiler data */

	cd = jd->cd;

	if (IS_INMEMORY(src->flags)) {
		COUNT_SPILLS;

		disp = src->vv.regoff * 8;

		switch (src->type) {
		case TYPE_INT:
			M_ILD(tempreg, REG_SP, disp);
			break;
		case TYPE_LNG:
		case TYPE_ADR:
			M_LLD(tempreg, REG_SP, disp);
			break;
		case TYPE_FLT:
			M_FLD(tempreg, REG_SP, disp);
			break;
		case TYPE_DBL:
			M_DLD(tempreg, REG_SP, disp);
			break;
		default:
			vm_abort("emit_load: unknown type %d", src->type);
		}

		reg = tempreg;
	}
	else
		reg = src->vv.regoff;

	return reg;
}


/* emit_store ******************************************************************

   This function generates the code to store the result of an
   operation back into a spilled pseudo-variable.  If the
   pseudo-variable has not been spilled in the first place, this
   function will generate nothing.
    
*******************************************************************************/

inline void emit_store(jitdata *jd, instruction *iptr, varinfo *dst, s4 d)
{
	codegendata  *cd;
	s4            disp;
#if 0
	s4            s;
	u2            opcode;
#endif

	/* get required compiler data */

	cd = jd->cd;

#if 0
	/* do we have to generate a conditional move? */

	if ((iptr != NULL) && (iptr->opc & ICMD_CONDITION_MASK)) {
		/* the passed register d is actually the source register */

		s = d;

		/* Only pass the opcode to codegen_reg_of_var to get the real
		   destination register. */

		opcode = iptr->opc & ICMD_OPCODE_MASK;

		/* get the real destination register */

		d = codegen_reg_of_var(rd, opcode, dst, REG_ITMP1);

		/* and emit the conditional move */

		emit_cmovxx(cd, iptr, s, d);
	}
#endif

	if (IS_INMEMORY(dst->flags)) {
		COUNT_SPILLS;

		disp = dst->vv.regoff * 8;

		switch (dst->type) {
		case TYPE_INT:
		case TYPE_LNG:
		case TYPE_ADR:
			M_LST(d, REG_SP, disp);
			break;
		case TYPE_FLT:
			M_FST(d, REG_SP, disp);
			break;
		case TYPE_DBL:
			M_DST(d, REG_SP, disp);
			break;
		default:
			vm_abort("emit_store: unknown type %d", dst->type);
		}
	}
}


/* emit_copy *******************************************************************

   Generates a register/memory to register/memory copy.

*******************************************************************************/

void emit_copy(jitdata *jd, instruction *iptr)
{
	codegendata *cd;
	varinfo     *src;
	varinfo     *dst;
	s4           s1, d;

	/* get required compiler data */

	cd = jd->cd;

	/* get source and destination variables */

	src = VAROP(iptr->s1);
	dst = VAROP(iptr->dst);

	if ((src->vv.regoff != dst->vv.regoff) ||
		((src->flags ^ dst->flags) & INMEMORY)) {

		if ((src->type == TYPE_RET) || (dst->type == TYPE_RET)) {
			/* emit nothing, as the value won't be used anyway */
			return;
		}

		/* If one of the variables resides in memory, we can eliminate
		   the register move from/to the temporary register with the
		   order of getting the destination register and the load. */

		if (IS_INMEMORY(src->flags)) {
			d  = codegen_reg_of_var(iptr->opc, dst, REG_IFTMP);
			s1 = emit_load(jd, iptr, src, d);
		}
		else {
			s1 = emit_load(jd, iptr, src, REG_IFTMP);
			d  = codegen_reg_of_var(iptr->opc, dst, s1);
		}

		if (s1 != d) {
			switch (src->type) {
			case TYPE_INT:
			case TYPE_LNG:
			case TYPE_ADR:
				M_MOV(s1, d);
				break;
			case TYPE_FLT:
			case TYPE_DBL:
				M_FMOV(s1, d);
				break;
			default:
				vm_abort("emit_copy: unknown type %d", src->type);
			}
		}

		emit_store(jd, iptr, dst, d);
	}
}


void emit_cmovxx(codegendata *cd, instruction *iptr, s4 s, s4 d)
{
#if 0
	switch (iptr->flags.fields.condition) {
	case ICMD_IFEQ:
		M_CMOVEQ(s, d);
		break;
	case ICMD_IFNE:
		M_CMOVNE(s, d);
		break;
	case ICMD_IFLT:
		M_CMOVLT(s, d);
		break;
	case ICMD_IFGE:
		M_CMOVGE(s, d);
		break;
	case ICMD_IFGT:
		M_CMOVGT(s, d);
		break;
	case ICMD_IFLE:
		M_CMOVLE(s, d);
		break;
	}
#endif
}


/* emit_branch *****************************************************************

   Emits the code for conditional and unconditional branchs.

*******************************************************************************/

void emit_branch(codegendata *cd, s4 disp, s4 condition, s4 reg, u4 options)
{
	s4 branchdisp;

	/* NOTE: A displacement overflow cannot happen. */

	/* check which branch to generate */

	if (condition == BRANCH_UNCONDITIONAL) {

		/* calculate the different displacements */

		branchdisp = disp - BRANCH_UNCONDITIONAL_SIZE;

		M_JMP_IMM(branchdisp);
	}
	else {
		/* calculate the different displacements */

		branchdisp = disp - BRANCH_CONDITIONAL_SIZE;

		switch (condition) {
		case BRANCH_EQ:
			M_BEQ(branchdisp);
			break;
		case BRANCH_NE:
			M_BNE(branchdisp);
			break;
		case BRANCH_LT:
			M_BLT(branchdisp);
			break;
		case BRANCH_GE:
			M_BGE(branchdisp);
			break;
		case BRANCH_GT:
			M_BGT(branchdisp);
			break;
		case BRANCH_LE:
			M_BLE(branchdisp);
			break;
		case BRANCH_ULT:
			M_BULT(branchdisp);
			break;
		case BRANCH_ULE:
			M_BULE(branchdisp);
			break;
		case BRANCH_UGE:
			M_BUGE(branchdisp);
			break;
		case BRANCH_UGT:
			M_BUGT(branchdisp);
			break;
		default:
			vm_abort("emit_branch: unknown condition %d", condition);
		}
	}
}


/* emit_arithmetic_check *******************************************************

   Emit an ArithmeticException check.

*******************************************************************************/

void emit_arithmetic_check(codegendata *cd, instruction *iptr, s4 reg)
{
	if (INSTRUCTION_MUST_CHECK(iptr)) {
		M_TEST(reg);
		M_BNE(8);
		M_ALD_MEM(reg, EXCEPTION_HARDWARE_ARITHMETIC);
	}
}


/* emit_arrayindexoutofbounds_check ********************************************

   Emit a ArrayIndexOutOfBoundsException check.

*******************************************************************************/

void emit_arrayindexoutofbounds_check(codegendata *cd, instruction *iptr, s4 s1, s4 s2)
{
	if (INSTRUCTION_MUST_CHECK(iptr)) {
        M_ILD(REG_ITMP3, s1, OFFSET(java_arrayheader, size));
        M_ICMP(REG_ITMP3, s2);
		M_BULT(8);
		M_ALD_MEM(s2, EXCEPTION_HARDWARE_ARRAYINDEXOUTOFBOUNDS);
	}
}


/* emit_classcast_check ********************************************************

   Emit a ClassCastException check.

*******************************************************************************/

void emit_classcast_check(codegendata *cd, instruction *iptr, s4 condition, s4 reg, s4 s1)
{
	if (INSTRUCTION_MUST_CHECK(iptr)) {
		switch (condition) {
		case BRANCH_LE:
			M_BGT(8);
			break;
		case BRANCH_EQ:
			M_BNE(8);
			break;
		case BRANCH_UGT:
			M_BULE(8);
			break;
		default:
			vm_abort("emit_classcast_check: unknown condition %d", condition);
		}
		M_ALD_MEM(s1, EXCEPTION_HARDWARE_CLASSCAST);
	}
}


/* emit_nullpointer_check ******************************************************

   Emit a NullPointerException check.

*******************************************************************************/

void emit_nullpointer_check(codegendata *cd, instruction *iptr, s4 reg)
{
	if (INSTRUCTION_MUST_CHECK(iptr)) {
		M_TEST(reg);
		M_BNE(8);
		M_ALD_MEM(reg, EXCEPTION_HARDWARE_NULLPOINTER);
	}
}


/* emit_exception_check ********************************************************

   Emit an Exception check.

*******************************************************************************/

void emit_exception_check(codegendata *cd, instruction *iptr)
{
	if (INSTRUCTION_MUST_CHECK(iptr)) {
		M_TEST(REG_RESULT);
		M_BNE(8);
		M_ALD_MEM(REG_RESULT, EXCEPTION_HARDWARE_EXCEPTION);
	}
}


/* emit_patcher_stubs **********************************************************

   Generates the code for the patcher stubs.

*******************************************************************************/

void emit_patcher_stubs(jitdata *jd)
{
	codegendata *cd;
	patchref    *pref;
	u8           mcode;
	u1          *savedmcodeptr;
	u1          *tmpmcodeptr;
	s4           targetdisp;
	s4           disp;

	/* get required compiler data */

	cd = jd->cd;

	/* generate code patching stub call code */

	targetdisp = 0;

	for (pref = cd->patchrefs; pref != NULL; pref = pref->next) {
		/* check size of code segment */

		MCODECHECK(512);

		/* Get machine code which is patched back in later. A
		   `call rel32' is 5 bytes long (but read 8 bytes). */

		savedmcodeptr = cd->mcodebase + pref->branchpos;
		mcode = *((u8 *) savedmcodeptr);

		/* patch in `call rel32' to call the following code */

		tmpmcodeptr  = cd->mcodeptr;    /* save current mcodeptr              */
		cd->mcodeptr = savedmcodeptr;   /* set mcodeptr to patch position     */

		M_CALL_IMM(tmpmcodeptr - (savedmcodeptr + PATCHER_CALL_SIZE));

		cd->mcodeptr = tmpmcodeptr;     /* restore the current mcodeptr       */

		/* move pointer to java_objectheader onto stack */

#if defined(ENABLE_THREADS)
		/* create a virtual java_objectheader */

		(void) dseg_add_unique_address(cd, NULL);                  /* flcword */
		(void) dseg_add_unique_address(cd, lock_get_initial_lock_word());
		disp = dseg_add_unique_address(cd, NULL);                  /* vftbl   */

		emit_lea_membase_reg(cd, RIP, -((cd->mcodeptr + 7) - cd->mcodebase) + disp, REG_ITMP3);
		M_PUSH(REG_ITMP3);
#else
		M_PUSH_IMM(0);
#endif

		/* move machine code bytes and classinfo pointer into registers */

		M_MOV_IMM(mcode, REG_ITMP3);
		M_PUSH(REG_ITMP3);

		M_MOV_IMM(pref->ref, REG_ITMP3);
		M_PUSH(REG_ITMP3);

		M_MOV_IMM(pref->disp, REG_ITMP3);
		M_PUSH(REG_ITMP3);

		M_MOV_IMM(pref->patcher, REG_ITMP3);
		M_PUSH(REG_ITMP3);

		if (targetdisp == 0) {
			targetdisp = cd->mcodeptr - cd->mcodebase;

			M_MOV_IMM(asm_patcher_wrapper, REG_ITMP3);
			M_JMP(REG_ITMP3);
		}
		else {
			M_JMP_IMM((cd->mcodebase + targetdisp) -
					  (cd->mcodeptr + PATCHER_CALL_SIZE));
		}
	}
}


/* emit_replacement_stubs ******************************************************

   Generates the code for the replacement stubs.

*******************************************************************************/

#if defined(ENABLE_REPLACEMENT)
void emit_replacement_stubs(jitdata *jd)
{
	codegendata *cd;
	codeinfo    *code;
	rplpoint    *rplp;
	s4           i;
#if !defined(NDEBUG)
	u1          *savedmcodeptr;
#endif

	/* get required compiler data */

	cd   = jd->cd;
	code = jd->code;

	rplp = code->rplpoints;

	/* store beginning of replacement stubs */

	code->replacementstubs = (u1*) (cd->mcodeptr - cd->mcodebase);

	for (i = 0; i < code->rplpointcount; ++i, ++rplp) {
		/* do not generate stubs for non-trappable points */

		if (rplp->flags & RPLPOINT_FLAG_NOTRAP)
			continue;

		/* check code segment size */

		MCODECHECK(512);

		/* note start of stub code */

#if !defined(NDEBUG)
		savedmcodeptr = cd->mcodeptr;
#endif

		/* push address of `rplpoint` struct */
			
		M_MOV_IMM(rplp, REG_ITMP3);
		M_PUSH(REG_ITMP3);

		/* jump to replacement function */

		M_MOV_IMM(asm_replacement_out, REG_ITMP3);
		M_PUSH(REG_ITMP3);
		M_RET;

		assert((cd->mcodeptr - savedmcodeptr) == REPLACEMENT_STUB_SIZE);
	}
}
#endif /* defined(ENABLE_REPLACEMENT) */


/* emit_verbosecall_enter ******************************************************

   Generates the code for the call trace.

*******************************************************************************/

#if !defined(NDEBUG)
void emit_verbosecall_enter(jitdata *jd)
{
	methodinfo   *m;
	codegendata  *cd;
	registerdata *rd;
	methoddesc   *md;
	s4            i, j, k;

	/* get required compiler data */

	m  = jd->m;
	cd = jd->cd;
	rd = jd->rd;

	md = m->parseddesc;

	/* mark trace code */

	M_NOP;

	/* additional +1 is for 16-byte stack alignment */

	M_LSUB_IMM((ARG_CNT + TMP_CNT + 1 + 1) * 8, REG_SP);

	/* save argument registers */

	for (i = 0; i < INT_ARG_CNT; i++)
		M_LST(abi_registers_integer_argument[i], REG_SP, (1 + i) * 8);

	for (i = 0; i < FLT_ARG_CNT; i++)
		M_DST(abi_registers_float_argument[i], REG_SP, (1 + INT_ARG_CNT + i) * 8);

	/* save temporary registers for leaf methods */

	if (jd->isleafmethod) {
		for (i = 0; i < INT_TMP_CNT; i++)
			M_LST(rd->tmpintregs[i], REG_SP, (1 + ARG_CNT + i) * 8);

		for (i = 0; i < FLT_TMP_CNT; i++)
			M_DST(rd->tmpfltregs[i], REG_SP, (1 + ARG_CNT + INT_TMP_CNT + i) * 8);
	}

	/* show integer hex code for float arguments */

	for (i = 0, j = 0; i < md->paramcount && i < INT_ARG_CNT; i++) {
		/* If the paramtype is a float, we have to right shift all
		   following integer registers. */
	
		if (IS_FLT_DBL_TYPE(md->paramtypes[i].type)) {
			for (k = INT_ARG_CNT - 2; k >= i; k--)
				M_MOV(abi_registers_integer_argument[k],
					  abi_registers_integer_argument[k + 1]);

			emit_movd_freg_reg(cd, abi_registers_float_argument[j],
							   abi_registers_integer_argument[i]);
			j++;
		}
	}

	M_MOV_IMM(m, REG_ITMP2);
	M_AST(REG_ITMP2, REG_SP, 0 * 8);
	M_MOV_IMM(builtin_verbosecall_enter, REG_ITMP1);
	M_CALL(REG_ITMP1);

	/* restore argument registers */

	for (i = 0; i < INT_ARG_CNT; i++)
		M_LLD(abi_registers_integer_argument[i], REG_SP, (1 + i) * 8);

	for (i = 0; i < FLT_ARG_CNT; i++)
		M_DLD(abi_registers_float_argument[i], REG_SP, (1 + INT_ARG_CNT + i) * 8);

	/* restore temporary registers for leaf methods */

	if (jd->isleafmethod) {
		for (i = 0; i < INT_TMP_CNT; i++)
			M_LLD(rd->tmpintregs[i], REG_SP, (1 + ARG_CNT + i) * 8);

		for (i = 0; i < FLT_TMP_CNT; i++)
			M_DLD(rd->tmpfltregs[i], REG_SP, (1 + ARG_CNT + INT_TMP_CNT + i) * 8);
	}

	M_LADD_IMM((ARG_CNT + TMP_CNT + 1 + 1) * 8, REG_SP);

	/* mark trace code */

	M_NOP;
}
#endif /* !defined(NDEBUG) */


/* emit_verbosecall_exit *******************************************************

   Generates the code for the call trace.

*******************************************************************************/

#if !defined(NDEBUG)
void emit_verbosecall_exit(jitdata *jd)
{
	methodinfo   *m;
	codegendata  *cd;
	registerdata *rd;

	/* get required compiler data */

	m  = jd->m;
	cd = jd->cd;
	rd = jd->rd;

	/* mark trace code */

	M_NOP;

	M_ASUB_IMM(2 * 8, REG_SP);

	M_LST(REG_RESULT, REG_SP, 0 * 8);
	M_DST(REG_FRESULT, REG_SP, 1 * 8);

	M_INTMOVE(REG_RESULT, REG_A0);
	M_FLTMOVE(REG_FRESULT, REG_FA0);
	M_FLTMOVE(REG_FRESULT, REG_FA1);
	M_MOV_IMM(m, REG_A1);

	M_MOV_IMM(builtin_verbosecall_exit, REG_ITMP1);
	M_CALL(REG_ITMP1);

	M_LLD(REG_RESULT, REG_SP, 0 * 8);
	M_DLD(REG_FRESULT, REG_SP, 1 * 8);

	M_AADD_IMM(2 * 8, REG_SP);

	/* mark trace code */

	M_NOP;
}
#endif /* !defined(NDEBUG) */


/* code generation functions **************************************************/

static void emit_membase(codegendata *cd, s4 basereg, s4 disp, s4 dreg)
{
	if ((basereg == REG_SP) || (basereg == R12)) {
		if (disp == 0) {
			emit_address_byte(0, dreg, REG_SP);
			emit_address_byte(0, REG_SP, REG_SP);

		} else if (IS_IMM8(disp)) {
			emit_address_byte(1, dreg, REG_SP);
			emit_address_byte(0, REG_SP, REG_SP);
			emit_imm8(disp);

		} else {
			emit_address_byte(2, dreg, REG_SP);
			emit_address_byte(0, REG_SP, REG_SP);
			emit_imm32(disp);
		}

	} else if ((disp) == 0 && (basereg) != RBP && (basereg) != R13) {
		emit_address_byte(0,(dreg),(basereg));

	} else if ((basereg) == RIP) {
		emit_address_byte(0, dreg, RBP);
		emit_imm32(disp);

	} else {
		if (IS_IMM8(disp)) {
			emit_address_byte(1, dreg, basereg);
			emit_imm8(disp);

		} else {
			emit_address_byte(2, dreg, basereg);
			emit_imm32(disp);
		}
	}
}


static void emit_membase32(codegendata *cd, s4 basereg, s4 disp, s4 dreg)
{
	if ((basereg == REG_SP) || (basereg == R12)) {
		emit_address_byte(2, dreg, REG_SP);
		emit_address_byte(0, REG_SP, REG_SP);
		emit_imm32(disp);
	}
	else {
		emit_address_byte(2, dreg, basereg);
		emit_imm32(disp);
	}
}


static void emit_memindex(codegendata *cd, s4 reg, s4 disp, s4 basereg, s4 indexreg, s4 scale)
{
	if (basereg == -1) {
		emit_address_byte(0, reg, 4);
		emit_address_byte(scale, indexreg, 5);
		emit_imm32(disp);
	}
	else if ((disp == 0) && (basereg != RBP) && (basereg != R13)) {
		emit_address_byte(0, reg, 4);
		emit_address_byte(scale, indexreg, basereg);
	}
	else if (IS_IMM8(disp)) {
		emit_address_byte(1, reg, 4);
		emit_address_byte(scale, indexreg, basereg);
		emit_imm8(disp);
	}
	else {
		emit_address_byte(2, reg, 4);
		emit_address_byte(scale, indexreg, basereg);
		emit_imm32(disp);
	}
}


void emit_ishift(jitdata *jd, s4 shift_op, instruction *iptr)
{
	s4 s1, s2, d, d_old;
	varinfo *v_s1,*v_s2,*v_dst;
	codegendata *cd;

	/* get required compiler data */

	cd = jd->cd;

	v_s1  = VAROP(iptr->s1);
	v_s2  = VAROP(iptr->sx.s23.s2);
	v_dst = VAROP(iptr->dst);

	s1 = v_s1->vv.regoff;
	s2 = v_s2->vv.regoff;
	d  = v_dst->vv.regoff;

	M_INTMOVE(RCX, REG_ITMP1);                                    /* save RCX */

	if (IS_INMEMORY(v_dst->flags)) {
		if (IS_INMEMORY(v_s2->flags) && IS_INMEMORY(v_s1->flags)) {
			if (s1 == d) {
				M_ILD(RCX, REG_SP, s2 * 8);
				emit_shiftl_membase(cd, shift_op, REG_SP, d * 8);

			} else {
				M_ILD(RCX, REG_SP, s2 * 8);
				M_ILD(REG_ITMP2, REG_SP, s1 * 8);
				emit_shiftl_reg(cd, shift_op, REG_ITMP2);
				M_IST(REG_ITMP2, REG_SP, d * 8);
			}

		} else if (IS_INMEMORY(v_s2->flags) && !IS_INMEMORY(v_s1->flags)) {
			/* s1 may be equal to RCX */
			if (s1 == RCX) {
				if (s2 == d) {
					M_ILD(REG_ITMP1, REG_SP, s2 * 8);
					M_IST(s1, REG_SP, d * 8);
					M_INTMOVE(REG_ITMP1, RCX);

				} else {
					M_IST(s1, REG_SP, d * 8);
					M_ILD(RCX, REG_SP, s2 * 8);
				}

			} else {
				M_ILD(RCX, REG_SP, s2 * 8);
				M_IST(s1, REG_SP, d * 8);
			}

			emit_shiftl_membase(cd, shift_op, REG_SP, d * 8);

		} else if (!IS_INMEMORY(v_s2->flags) && IS_INMEMORY(v_s1->flags)) {
			if (s1 == d) {
				M_INTMOVE(s2, RCX);
				emit_shiftl_membase(cd, shift_op, REG_SP, d * 8);

			} else {
				M_INTMOVE(s2, RCX);
				M_ILD(REG_ITMP2, REG_SP, s1 * 8);
				emit_shiftl_reg(cd, shift_op, REG_ITMP2);
				M_IST(REG_ITMP2, REG_SP, d * 8);
			}

		} else {
			/* s1 may be equal to RCX */
			M_IST(s1, REG_SP, d * 8);
			M_INTMOVE(s2, RCX);
			emit_shiftl_membase(cd, shift_op, REG_SP, d * 8);
		}

		M_INTMOVE(REG_ITMP1, RCX);                             /* restore RCX */

	} else {
		d_old = d;
		if (d == RCX) {
			d = REG_ITMP3;
		}
					
		if (IS_INMEMORY(v_s2->flags) && IS_INMEMORY(v_s1->flags)) {
			M_ILD(RCX, REG_SP, s2 * 8);
			M_ILD(d, REG_SP, s1 * 8);
			emit_shiftl_reg(cd, shift_op, d);

		} else if (IS_INMEMORY(v_s2->flags) && !IS_INMEMORY(v_s1->flags)) {
			/* s1 may be equal to RCX */
			M_INTMOVE(s1, d);
			M_ILD(RCX, REG_SP, s2 * 8);
			emit_shiftl_reg(cd, shift_op, d);

		} else if (!IS_INMEMORY(v_s2->flags) && IS_INMEMORY(v_s1->flags)) {
			M_INTMOVE(s2, RCX);
			M_ILD(d, REG_SP, s1 * 8);
			emit_shiftl_reg(cd, shift_op, d);

		} else {
			/* s1 may be equal to RCX */
			if (s1 == RCX) {
				if (s2 == d) {
					/* d cannot be used to backup s1 since this would
					   overwrite s2. */
					M_INTMOVE(s1, REG_ITMP3);
					M_INTMOVE(s2, RCX);
					M_INTMOVE(REG_ITMP3, d);

				} else {
					M_INTMOVE(s1, d);
					M_INTMOVE(s2, RCX);
				}

			} else {
				/* d may be equal to s2 */
				M_INTMOVE(s2, RCX);
				M_INTMOVE(s1, d);
			}
			emit_shiftl_reg(cd, shift_op, d);
		}

		if (d_old == RCX)
			M_INTMOVE(REG_ITMP3, RCX);
		else
			M_INTMOVE(REG_ITMP1, RCX);                         /* restore RCX */
	}
}


void emit_lshift(jitdata *jd, s4 shift_op, instruction *iptr)
{
	s4 s1, s2, d, d_old;
	varinfo *v_s1,*v_s2,*v_dst;
	codegendata *cd;

	/* get required compiler data */

	cd = jd->cd;

	v_s1  = VAROP(iptr->s1);
	v_s2  = VAROP(iptr->sx.s23.s2);
	v_dst = VAROP(iptr->dst);

	s1 = v_s1->vv.regoff;
	s2 = v_s2->vv.regoff;
	d  = v_dst->vv.regoff;
	
	M_INTMOVE(RCX, REG_ITMP1);                                    /* save RCX */

	if (IS_INMEMORY(v_dst->flags)) {
		if (IS_INMEMORY(v_s2->flags) && IS_INMEMORY(v_s1->flags)) {
			if (s1 == d) {
				M_ILD(RCX, REG_SP, s2 * 8);
				emit_shift_membase(cd, shift_op, REG_SP, d * 8);

			} else {
				M_ILD(RCX, REG_SP, s2 * 8);
				M_LLD(REG_ITMP2, REG_SP, s1 * 8);
				emit_shift_reg(cd, shift_op, REG_ITMP2);
				M_LST(REG_ITMP2, REG_SP, d * 8);
			}

		} else if (IS_INMEMORY(v_s2->flags) && !IS_INMEMORY(v_s1->flags)) {
			/* s1 may be equal to RCX */
			if (s1 == RCX) {
				if (s2 == d) {
					M_ILD(REG_ITMP1, REG_SP, s2 * 8);
					M_LST(s1, REG_SP, d * 8);
					M_INTMOVE(REG_ITMP1, RCX);

				} else {
					M_LST(s1, REG_SP, d * 8);
					M_ILD(RCX, REG_SP, s2 * 8);
				}

			} else {
				M_ILD(RCX, REG_SP, s2 * 8);
				M_LST(s1, REG_SP, d * 8);
			}

			emit_shift_membase(cd, shift_op, REG_SP, d * 8);

		} else if (!IS_INMEMORY(v_s2->flags) && IS_INMEMORY(v_s1->flags)) {
			if (s1 == d) {
				M_INTMOVE(s2, RCX);
				emit_shift_membase(cd, shift_op, REG_SP, d * 8);

			} else {
				M_INTMOVE(s2, RCX);
				M_LLD(REG_ITMP2, REG_SP, s1 * 8);
				emit_shift_reg(cd, shift_op, REG_ITMP2);
				M_LST(REG_ITMP2, REG_SP, d * 8);
			}

		} else {
			/* s1 may be equal to RCX */
			M_LST(s1, REG_SP, d * 8);
			M_INTMOVE(s2, RCX);
			emit_shift_membase(cd, shift_op, REG_SP, d * 8);
		}

		M_INTMOVE(REG_ITMP1, RCX);                             /* restore RCX */

	} else {
		d_old = d;
		if (d == RCX) {
			d = REG_ITMP3;
		}

		if (IS_INMEMORY(v_s2->flags) && IS_INMEMORY(v_s1->flags)) {
			M_ILD(RCX, REG_SP, s2 * 8);
			M_LLD(d, REG_SP, s1 * 8);
			emit_shift_reg(cd, shift_op, d);

		} else if (IS_INMEMORY(v_s2->flags) && !IS_INMEMORY(v_s1->flags)) {
			/* s1 may be equal to RCX */
			M_INTMOVE(s1, d);
			M_ILD(RCX, REG_SP, s2 * 8);
			emit_shift_reg(cd, shift_op, d);

		} else if (!IS_INMEMORY(v_s2->flags) && IS_INMEMORY(v_s1->flags)) {
			M_INTMOVE(s2, RCX);
			M_LLD(d, REG_SP, s1 * 8);
			emit_shift_reg(cd, shift_op, d);

		} else {
			/* s1 may be equal to RCX */
			if (s1 == RCX) {
				if (s2 == d) {
					/* d cannot be used to backup s1 since this would
					   overwrite s2. */
					M_INTMOVE(s1, REG_ITMP3);
					M_INTMOVE(s2, RCX);
					M_INTMOVE(REG_ITMP3, d);

				} else {
					M_INTMOVE(s1, d);
					M_INTMOVE(s2, RCX);
				}

			} else {
				/* d may be equal to s2 */
				M_INTMOVE(s2, RCX);
				M_INTMOVE(s1, d);
			}
			emit_shift_reg(cd, shift_op, d);
		}

		if (d_old == RCX)
			M_INTMOVE(REG_ITMP3, RCX);
		else
			M_INTMOVE(REG_ITMP1, RCX);                         /* restore RCX */
	}
}


/* low-level code emitter functions *******************************************/

void emit_mov_reg_reg(codegendata *cd, s8 reg, s8 dreg)
{
	emit_rex(1,(reg),0,(dreg));
	*(cd->mcodeptr++) = 0x89;
	emit_reg((reg),(dreg));
}


void emit_mov_imm_reg(codegendata *cd, s8 imm, s8 reg)
{
	emit_rex(1,0,0,(reg));
	*(cd->mcodeptr++) = 0xb8 + ((reg) & 0x07);
	emit_imm64((imm));
}


void emit_movl_reg_reg(codegendata *cd, s8 reg, s8 dreg)
{
	emit_rex(0,(reg),0,(dreg));
	*(cd->mcodeptr++) = 0x89;
	emit_reg((reg),(dreg));
}


void emit_movl_imm_reg(codegendata *cd, s8 imm, s8 reg) {
	emit_rex(0,0,0,(reg));
	*(cd->mcodeptr++) = 0xb8 + ((reg) & 0x07);
	emit_imm32((imm));
}


void emit_mov_membase_reg(codegendata *cd, s8 basereg, s8 disp, s8 reg) {
	emit_rex(1,(reg),0,(basereg));
	*(cd->mcodeptr++) = 0x8b;
	emit_membase(cd, (basereg),(disp),(reg));
}


/*
 * this one is for INVOKEVIRTUAL/INVOKEINTERFACE to have a
 * constant membase immediate length of 32bit
 */
void emit_mov_membase32_reg(codegendata *cd, s8 basereg, s8 disp, s8 reg) {
	emit_rex(1,(reg),0,(basereg));
	*(cd->mcodeptr++) = 0x8b;
	emit_membase32(cd, (basereg),(disp),(reg));
}


void emit_movl_membase_reg(codegendata *cd, s8 basereg, s8 disp, s8 reg)
{
	emit_rex(0,(reg),0,(basereg));
	*(cd->mcodeptr++) = 0x8b;
	emit_membase(cd, (basereg),(disp),(reg));
}


/* ATTENTION: Always emit a REX byte, because the instruction size can
   be smaller when all register indexes are smaller than 7. */
void emit_movl_membase32_reg(codegendata *cd, s8 basereg, s8 disp, s8 reg)
{
	emit_byte_rex((reg),0,(basereg));
	*(cd->mcodeptr++) = 0x8b;
	emit_membase32(cd, (basereg),(disp),(reg));
}


void emit_mov_reg_membase(codegendata *cd, s8 reg, s8 basereg, s8 disp) {
	emit_rex(1,(reg),0,(basereg));
	*(cd->mcodeptr++) = 0x89;
	emit_membase(cd, (basereg),(disp),(reg));
}


void emit_mov_reg_membase32(codegendata *cd, s8 reg, s8 basereg, s8 disp) {
	emit_rex(1,(reg),0,(basereg));
	*(cd->mcodeptr++) = 0x89;
	emit_membase32(cd, (basereg),(disp),(reg));
}


void emit_movl_reg_membase(codegendata *cd, s8 reg, s8 basereg, s8 disp) {
	emit_rex(0,(reg),0,(basereg));
	*(cd->mcodeptr++) = 0x89;
	emit_membase(cd, (basereg),(disp),(reg));
}


/* Always emit a REX byte, because the instruction size can be smaller when   */
/* all register indexes are smaller than 7.                                   */
void emit_movl_reg_membase32(codegendata *cd, s8 reg, s8 basereg, s8 disp) {
	emit_byte_rex((reg),0,(basereg));
	*(cd->mcodeptr++) = 0x89;
	emit_membase32(cd, (basereg),(disp),(reg));
}


void emit_mov_memindex_reg(codegendata *cd, s8 disp, s8 basereg, s8 indexreg, s8 scale, s8 reg) {
	emit_rex(1,(reg),(indexreg),(basereg));
	*(cd->mcodeptr++) = 0x8b;
	emit_memindex(cd, (reg),(disp),(basereg),(indexreg),(scale));
}


void emit_movl_memindex_reg(codegendata *cd, s8 disp, s8 basereg, s8 indexreg, s8 scale, s8 reg) {
	emit_rex(0,(reg),(indexreg),(basereg));
	*(cd->mcodeptr++) = 0x8b;
	emit_memindex(cd, (reg),(disp),(basereg),(indexreg),(scale));
}


void emit_mov_reg_memindex(codegendata *cd, s8 reg, s8 disp, s8 basereg, s8 indexreg, s8 scale) {
	emit_rex(1,(reg),(indexreg),(basereg));
	*(cd->mcodeptr++) = 0x89;
	emit_memindex(cd, (reg),(disp),(basereg),(indexreg),(scale));
}


void emit_movl_reg_memindex(codegendata *cd, s8 reg, s8 disp, s8 basereg, s8 indexreg, s8 scale) {
	emit_rex(0,(reg),(indexreg),(basereg));
	*(cd->mcodeptr++) = 0x89;
	emit_memindex(cd, (reg),(disp),(basereg),(indexreg),(scale));
}


void emit_movw_reg_memindex(codegendata *cd, s8 reg, s8 disp, s8 basereg, s8 indexreg, s8 scale) {
	*(cd->mcodeptr++) = 0x66;
	emit_rex(0,(reg),(indexreg),(basereg));
	*(cd->mcodeptr++) = 0x89;
	emit_memindex(cd, (reg),(disp),(basereg),(indexreg),(scale));
}


void emit_movb_reg_memindex(codegendata *cd, s8 reg, s8 disp, s8 basereg, s8 indexreg, s8 scale) {
	emit_byte_rex((reg),(indexreg),(basereg));
	*(cd->mcodeptr++) = 0x88;
	emit_memindex(cd, (reg),(disp),(basereg),(indexreg),(scale));
}


void emit_mov_imm_membase(codegendata *cd, s8 imm, s8 basereg, s8 disp) {
	emit_rex(1,0,0,(basereg));
	*(cd->mcodeptr++) = 0xc7;
	emit_membase(cd, (basereg),(disp),0);
	emit_imm32((imm));
}


void emit_mov_imm_membase32(codegendata *cd, s8 imm, s8 basereg, s8 disp) {
	emit_rex(1,0,0,(basereg));
	*(cd->mcodeptr++) = 0xc7;
	emit_membase32(cd, (basereg),(disp),0);
	emit_imm32((imm));
}


void emit_movl_imm_membase(codegendata *cd, s8 imm, s8 basereg, s8 disp) {
	emit_rex(0,0,0,(basereg));
	*(cd->mcodeptr++) = 0xc7;
	emit_membase(cd, (basereg),(disp),0);
	emit_imm32((imm));
}


/* Always emit a REX byte, because the instruction size can be smaller when   */
/* all register indexes are smaller than 7.                                   */
void emit_movl_imm_membase32(codegendata *cd, s8 imm, s8 basereg, s8 disp) {
	emit_byte_rex(0,0,(basereg));
	*(cd->mcodeptr++) = 0xc7;
	emit_membase32(cd, (basereg),(disp),0);
	emit_imm32((imm));
}


void emit_movsbq_reg_reg(codegendata *cd, s8 reg, s8 dreg)
{
	emit_rex(1,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0xbe;
	/* XXX: why do reg and dreg have to be exchanged */
	emit_reg((dreg),(reg));
}


void emit_movswq_reg_reg(codegendata *cd, s8 reg, s8 dreg)
{
	emit_rex(1,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0xbf;
	/* XXX: why do reg and dreg have to be exchanged */
	emit_reg((dreg),(reg));
}


void emit_movslq_reg_reg(codegendata *cd, s8 reg, s8 dreg)
{
	emit_rex(1,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x63;
	/* XXX: why do reg and dreg have to be exchanged */
	emit_reg((dreg),(reg));
}


void emit_movzwq_reg_reg(codegendata *cd, s8 reg, s8 dreg)
{
	emit_rex(1,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0xb7;
	/* XXX: why do reg and dreg have to be exchanged */
	emit_reg((dreg),(reg));
}


void emit_movswq_memindex_reg(codegendata *cd, s8 disp, s8 basereg, s8 indexreg, s8 scale, s8 reg) {
	emit_rex(1,(reg),(indexreg),(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0xbf;
	emit_memindex(cd, (reg),(disp),(basereg),(indexreg),(scale));
}


void emit_movsbq_memindex_reg(codegendata *cd, s8 disp, s8 basereg, s8 indexreg, s8 scale, s8 reg) {
	emit_rex(1,(reg),(indexreg),(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0xbe;
	emit_memindex(cd, (reg),(disp),(basereg),(indexreg),(scale));
}


void emit_movzwq_memindex_reg(codegendata *cd, s8 disp, s8 basereg, s8 indexreg, s8 scale, s8 reg) {
	emit_rex(1,(reg),(indexreg),(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0xb7;
	emit_memindex(cd, (reg),(disp),(basereg),(indexreg),(scale));
}


void emit_mov_imm_memindex(codegendata *cd, s4 imm, s4 disp, s4 basereg, s4 indexreg, s4 scale)
{
	emit_rex(1,0,(indexreg),(basereg));
	*(cd->mcodeptr++) = 0xc7;
	emit_memindex(cd, 0,(disp),(basereg),(indexreg),(scale));
	emit_imm32((imm));
}


void emit_movl_imm_memindex(codegendata *cd, s4 imm, s4 disp, s4 basereg, s4 indexreg, s4 scale)
{
	emit_rex(0,0,(indexreg),(basereg));
	*(cd->mcodeptr++) = 0xc7;
	emit_memindex(cd, 0,(disp),(basereg),(indexreg),(scale));
	emit_imm32((imm));
}


void emit_movw_imm_memindex(codegendata *cd, s4 imm, s4 disp, s4 basereg, s4 indexreg, s4 scale)
{
	*(cd->mcodeptr++) = 0x66;
	emit_rex(0,0,(indexreg),(basereg));
	*(cd->mcodeptr++) = 0xc7;
	emit_memindex(cd, 0,(disp),(basereg),(indexreg),(scale));
	emit_imm16((imm));
}


void emit_movb_imm_memindex(codegendata *cd, s4 imm, s4 disp, s4 basereg, s4 indexreg, s4 scale)
{
	emit_rex(0,0,(indexreg),(basereg));
	*(cd->mcodeptr++) = 0xc6;
	emit_memindex(cd, 0,(disp),(basereg),(indexreg),(scale));
	emit_imm8((imm));
}


void emit_mov_mem_reg(codegendata *cd, s4 disp, s4 dreg)
{
	emit_rex(1, dreg, 0, 0);
	*(cd->mcodeptr++) = 0x8b;
	emit_address_byte(0, dreg, 4);
	emit_mem(4, disp);
}


/*
 * alu operations
 */
void emit_alu_reg_reg(codegendata *cd, s8 opc, s8 reg, s8 dreg)
{
	emit_rex(1,(reg),0,(dreg));
	*(cd->mcodeptr++) = (((opc)) << 3) + 1;
	emit_reg((reg),(dreg));
}


void emit_alul_reg_reg(codegendata *cd, s8 opc, s8 reg, s8 dreg)
{
	emit_rex(0,(reg),0,(dreg));
	*(cd->mcodeptr++) = (((opc)) << 3) + 1;
	emit_reg((reg),(dreg));
}


void emit_alu_reg_membase(codegendata *cd, s8 opc, s8 reg, s8 basereg, s8 disp)
{
	emit_rex(1,(reg),0,(basereg));
	*(cd->mcodeptr++) = (((opc)) << 3) + 1;
	emit_membase(cd, (basereg),(disp),(reg));
}


void emit_alul_reg_membase(codegendata *cd, s8 opc, s8 reg, s8 basereg, s8 disp)
{
	emit_rex(0,(reg),0,(basereg));
	*(cd->mcodeptr++) = (((opc)) << 3) + 1;
	emit_membase(cd, (basereg),(disp),(reg));
}


void emit_alu_membase_reg(codegendata *cd, s8 opc, s8 basereg, s8 disp, s8 reg)
{
	emit_rex(1,(reg),0,(basereg));
	*(cd->mcodeptr++) = (((opc)) << 3) + 3;
	emit_membase(cd, (basereg),(disp),(reg));
}


void emit_alul_membase_reg(codegendata *cd, s8 opc, s8 basereg, s8 disp, s8 reg)
{
	emit_rex(0,(reg),0,(basereg));
	*(cd->mcodeptr++) = (((opc)) << 3) + 3;
	emit_membase(cd, (basereg),(disp),(reg));
}


void emit_alu_imm_reg(codegendata *cd, s8 opc, s8 imm, s8 dreg) {
	if (IS_IMM8(imm)) {
		emit_rex(1,0,0,(dreg));
		*(cd->mcodeptr++) = 0x83;
		emit_reg((opc),(dreg));
		emit_imm8((imm));
	} else {
		emit_rex(1,0,0,(dreg));
		*(cd->mcodeptr++) = 0x81;
		emit_reg((opc),(dreg));
		emit_imm32((imm));
	}
}


void emit_alu_imm32_reg(codegendata *cd, s4 opc, s4 imm, s4 dreg)
{
	emit_rex(1,0,0,(dreg));
	*(cd->mcodeptr++) = 0x81;
	emit_reg((opc),(dreg));
	emit_imm32((imm));
}


void emit_alul_imm32_reg(codegendata *cd, s4 opc, s4 imm, s4 dreg)
{
	emit_rex(0,0,0,(dreg));
	*(cd->mcodeptr++) = 0x81;
	emit_reg((opc),(dreg));
	emit_imm32((imm));
}


void emit_alul_imm_reg(codegendata *cd, s8 opc, s8 imm, s8 dreg) {
	if (IS_IMM8(imm)) {
		emit_rex(0,0,0,(dreg));
		*(cd->mcodeptr++) = 0x83;
		emit_reg((opc),(dreg));
		emit_imm8((imm));
	} else {
		emit_rex(0,0,0,(dreg));
		*(cd->mcodeptr++) = 0x81;
		emit_reg((opc),(dreg));
		emit_imm32((imm));
	}
}


void emit_alu_imm_membase(codegendata *cd, s8 opc, s8 imm, s8 basereg, s8 disp) {
	if (IS_IMM8(imm)) {
		emit_rex(1,(basereg),0,0);
		*(cd->mcodeptr++) = 0x83;
		emit_membase(cd, (basereg),(disp),(opc));
		emit_imm8((imm));
	} else {
		emit_rex(1,(basereg),0,0);
		*(cd->mcodeptr++) = 0x81;
		emit_membase(cd, (basereg),(disp),(opc));
		emit_imm32((imm));
	}
}


void emit_alul_imm_membase(codegendata *cd, s8 opc, s8 imm, s8 basereg, s8 disp) {
	if (IS_IMM8(imm)) {
		emit_rex(0,(basereg),0,0);
		*(cd->mcodeptr++) = 0x83;
		emit_membase(cd, (basereg),(disp),(opc));
		emit_imm8((imm));
	} else {
		emit_rex(0,(basereg),0,0);
		*(cd->mcodeptr++) = 0x81;
		emit_membase(cd, (basereg),(disp),(opc));
		emit_imm32((imm));
	}
}


void emit_test_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	emit_rex(1,(reg),0,(dreg));
	*(cd->mcodeptr++) = 0x85;
	emit_reg((reg),(dreg));
}


void emit_testl_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	emit_rex(0,(reg),0,(dreg));
	*(cd->mcodeptr++) = 0x85;
	emit_reg((reg),(dreg));
}


void emit_test_imm_reg(codegendata *cd, s8 imm, s8 reg) {
	*(cd->mcodeptr++) = 0xf7;
	emit_reg(0,(reg));
	emit_imm32((imm));
}


void emit_testw_imm_reg(codegendata *cd, s8 imm, s8 reg) {
	*(cd->mcodeptr++) = 0x66;
	*(cd->mcodeptr++) = 0xf7;
	emit_reg(0,(reg));
	emit_imm16((imm));
}


void emit_testb_imm_reg(codegendata *cd, s8 imm, s8 reg) {
	*(cd->mcodeptr++) = 0xf6;
	emit_reg(0,(reg));
	emit_imm8((imm));
}


void emit_lea_membase_reg(codegendata *cd, s8 basereg, s8 disp, s8 reg) {
	emit_rex(1,(reg),0,(basereg));
	*(cd->mcodeptr++) = 0x8d;
	emit_membase(cd, (basereg),(disp),(reg));
}


void emit_leal_membase_reg(codegendata *cd, s8 basereg, s8 disp, s8 reg) {
	emit_rex(0,(reg),0,(basereg));
	*(cd->mcodeptr++) = 0x8d;
	emit_membase(cd, (basereg),(disp),(reg));
}



void emit_incl_membase(codegendata *cd, s8 basereg, s8 disp)
{
	emit_rex(0,0,0,(basereg));
	*(cd->mcodeptr++) = 0xff;
	emit_membase(cd, (basereg),(disp),0);
}



void emit_cltd(codegendata *cd) {
    *(cd->mcodeptr++) = 0x99;
}


void emit_cqto(codegendata *cd) {
	emit_rex(1,0,0,0);
	*(cd->mcodeptr++) = 0x99;
}



void emit_imul_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	emit_rex(1,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0xaf;
	emit_reg((dreg),(reg));
}


void emit_imull_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0xaf;
	emit_reg((dreg),(reg));
}


void emit_imul_membase_reg(codegendata *cd, s8 basereg, s8 disp, s8 dreg) {
	emit_rex(1,(dreg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0xaf;
	emit_membase(cd, (basereg),(disp),(dreg));
}


void emit_imull_membase_reg(codegendata *cd, s8 basereg, s8 disp, s8 dreg) {
	emit_rex(0,(dreg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0xaf;
	emit_membase(cd, (basereg),(disp),(dreg));
}


void emit_imul_imm_reg(codegendata *cd, s8 imm, s8 dreg) {
	if (IS_IMM8((imm))) {
		emit_rex(1,0,0,(dreg));
		*(cd->mcodeptr++) = 0x6b;
		emit_reg(0,(dreg));
		emit_imm8((imm));
	} else {
		emit_rex(1,0,0,(dreg));
		*(cd->mcodeptr++) = 0x69;
		emit_reg(0,(dreg));
		emit_imm32((imm));
	}
}


void emit_imul_imm_reg_reg(codegendata *cd, s8 imm, s8 reg, s8 dreg) {
	if (IS_IMM8((imm))) {
		emit_rex(1,(dreg),0,(reg));
		*(cd->mcodeptr++) = 0x6b;
		emit_reg((dreg),(reg));
		emit_imm8((imm));
	} else {
		emit_rex(1,(dreg),0,(reg));
		*(cd->mcodeptr++) = 0x69;
		emit_reg((dreg),(reg));
		emit_imm32((imm));
	}
}


void emit_imull_imm_reg_reg(codegendata *cd, s8 imm, s8 reg, s8 dreg) {
	if (IS_IMM8((imm))) {
		emit_rex(0,(dreg),0,(reg));
		*(cd->mcodeptr++) = 0x6b;
		emit_reg((dreg),(reg));
		emit_imm8((imm));
	} else {
		emit_rex(0,(dreg),0,(reg));
		*(cd->mcodeptr++) = 0x69;
		emit_reg((dreg),(reg));
		emit_imm32((imm));
	}
}


void emit_imul_imm_membase_reg(codegendata *cd, s8 imm, s8 basereg, s8 disp, s8 dreg) {
	if (IS_IMM8((imm))) {
		emit_rex(1,(dreg),0,(basereg));
		*(cd->mcodeptr++) = 0x6b;
		emit_membase(cd, (basereg),(disp),(dreg));
		emit_imm8((imm));
	} else {
		emit_rex(1,(dreg),0,(basereg));
		*(cd->mcodeptr++) = 0x69;
		emit_membase(cd, (basereg),(disp),(dreg));
		emit_imm32((imm));
	}
}


void emit_imull_imm_membase_reg(codegendata *cd, s8 imm, s8 basereg, s8 disp, s8 dreg) {
	if (IS_IMM8((imm))) {
		emit_rex(0,(dreg),0,(basereg));
		*(cd->mcodeptr++) = 0x6b;
		emit_membase(cd, (basereg),(disp),(dreg));
		emit_imm8((imm));
	} else {
		emit_rex(0,(dreg),0,(basereg));
		*(cd->mcodeptr++) = 0x69;
		emit_membase(cd, (basereg),(disp),(dreg));
		emit_imm32((imm));
	}
}


void emit_idiv_reg(codegendata *cd, s8 reg) {
	emit_rex(1,0,0,(reg));
	*(cd->mcodeptr++) = 0xf7;
	emit_reg(7,(reg));
}


void emit_idivl_reg(codegendata *cd, s8 reg) {
	emit_rex(0,0,0,(reg));
	*(cd->mcodeptr++) = 0xf7;
	emit_reg(7,(reg));
}



void emit_ret(codegendata *cd) {
    *(cd->mcodeptr++) = 0xc3;
}



/*
 * shift ops
 */
void emit_shift_reg(codegendata *cd, s8 opc, s8 reg) {
	emit_rex(1,0,0,(reg));
	*(cd->mcodeptr++) = 0xd3;
	emit_reg((opc),(reg));
}


void emit_shiftl_reg(codegendata *cd, s8 opc, s8 reg) {
	emit_rex(0,0,0,(reg));
	*(cd->mcodeptr++) = 0xd3;
	emit_reg((opc),(reg));
}


void emit_shift_membase(codegendata *cd, s8 opc, s8 basereg, s8 disp) {
	emit_rex(1,0,0,(basereg));
	*(cd->mcodeptr++) = 0xd3;
	emit_membase(cd, (basereg),(disp),(opc));
}


void emit_shiftl_membase(codegendata *cd, s8 opc, s8 basereg, s8 disp) {
	emit_rex(0,0,0,(basereg));
	*(cd->mcodeptr++) = 0xd3;
	emit_membase(cd, (basereg),(disp),(opc));
}


void emit_shift_imm_reg(codegendata *cd, s8 opc, s8 imm, s8 dreg) {
	if ((imm) == 1) {
		emit_rex(1,0,0,(dreg));
		*(cd->mcodeptr++) = 0xd1;
		emit_reg((opc),(dreg));
	} else {
		emit_rex(1,0,0,(dreg));
		*(cd->mcodeptr++) = 0xc1;
		emit_reg((opc),(dreg));
		emit_imm8((imm));
	}
}


void emit_shiftl_imm_reg(codegendata *cd, s8 opc, s8 imm, s8 dreg) {
	if ((imm) == 1) {
		emit_rex(0,0,0,(dreg));
		*(cd->mcodeptr++) = 0xd1;
		emit_reg((opc),(dreg));
	} else {
		emit_rex(0,0,0,(dreg));
		*(cd->mcodeptr++) = 0xc1;
		emit_reg((opc),(dreg));
		emit_imm8((imm));
	}
}


void emit_shift_imm_membase(codegendata *cd, s8 opc, s8 imm, s8 basereg, s8 disp) {
	if ((imm) == 1) {
		emit_rex(1,0,0,(basereg));
		*(cd->mcodeptr++) = 0xd1;
		emit_membase(cd, (basereg),(disp),(opc));
	} else {
		emit_rex(1,0,0,(basereg));
		*(cd->mcodeptr++) = 0xc1;
		emit_membase(cd, (basereg),(disp),(opc));
		emit_imm8((imm));
	}
}


void emit_shiftl_imm_membase(codegendata *cd, s8 opc, s8 imm, s8 basereg, s8 disp) {
	if ((imm) == 1) {
		emit_rex(0,0,0,(basereg));
		*(cd->mcodeptr++) = 0xd1;
		emit_membase(cd, (basereg),(disp),(opc));
	} else {
		emit_rex(0,0,0,(basereg));
		*(cd->mcodeptr++) = 0xc1;
		emit_membase(cd, (basereg),(disp),(opc));
		emit_imm8((imm));
	}
}



/*
 * jump operations
 */
void emit_jmp_imm(codegendata *cd, s8 imm) {
	*(cd->mcodeptr++) = 0xe9;
	emit_imm32((imm));
}


void emit_jmp_reg(codegendata *cd, s8 reg) {
	emit_rex(0,0,0,(reg));
	*(cd->mcodeptr++) = 0xff;
	emit_reg(4,(reg));
}


void emit_jcc(codegendata *cd, s8 opc, s8 imm) {
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = (0x80 + (opc));
	emit_imm32((imm));
}



/*
 * conditional set and move operations
 */

/* we need the rex byte to get all low bytes */
void emit_setcc_reg(codegendata *cd, s4 opc, s4 reg)
{
	*(cd->mcodeptr++) = (0x40 | (((reg) >> 3) & 0x01));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = (0x90 + (opc));
	emit_reg(0,(reg));
}


/* we need the rex byte to get all low bytes */
void emit_setcc_membase(codegendata *cd, s4 opc, s4 basereg, s4 disp)
{
	*(cd->mcodeptr++) = (0x40 | (((basereg) >> 3) & 0x01));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = (0x90 + (opc));
	emit_membase(cd, (basereg),(disp),0);
}


void emit_cmovcc_reg_reg(codegendata *cd, s4 opc, s4 reg, s4 dreg)
{
	emit_rex(1,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = (0x40 + (opc));
	emit_reg((dreg),(reg));
}


void emit_cmovccl_reg_reg(codegendata *cd, s4 opc, s4 reg, s4 dreg)
{
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = (0x40 + (opc));
	emit_reg((dreg),(reg));
}


void emit_neg_reg(codegendata *cd, s8 reg)
{
	emit_rex(1,0,0,(reg));
	*(cd->mcodeptr++) = 0xf7;
	emit_reg(3,(reg));
}


void emit_negl_reg(codegendata *cd, s8 reg)
{
	emit_rex(0,0,0,(reg));
	*(cd->mcodeptr++) = 0xf7;
	emit_reg(3,(reg));
}


void emit_push_reg(codegendata *cd, s8 reg) {
	emit_rex(0,0,0,(reg));
	*(cd->mcodeptr++) = 0x50 + (0x07 & (reg));
}


void emit_push_imm(codegendata *cd, s8 imm) {
	*(cd->mcodeptr++) = 0x68;
	emit_imm32((imm));
}


void emit_pop_reg(codegendata *cd, s8 reg) {
	emit_rex(0,0,0,(reg));
	*(cd->mcodeptr++) = 0x58 + (0x07 & (reg));
}


void emit_xchg_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	emit_rex(1,(reg),0,(dreg));
	*(cd->mcodeptr++) = 0x87;
	emit_reg((reg),(dreg));
}


void emit_nop(codegendata *cd) {
    *(cd->mcodeptr++) = 0x90;
}



/*
 * call instructions
 */
void emit_call_reg(codegendata *cd, s8 reg)
{
	emit_rex(0,0,0,(reg));
	*(cd->mcodeptr++) = 0xff;
	emit_reg(2,(reg));
}


void emit_call_imm(codegendata *cd, s8 imm)
{
	*(cd->mcodeptr++) = 0xe8;
	emit_imm32((imm));
}


void emit_call_mem(codegendata *cd, ptrint mem)
{
	*(cd->mcodeptr++) = 0xff;
	emit_mem(2,(mem));
}



/*
 * floating point instructions (SSE2)
 */
void emit_addsd_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf2;
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x58;
	emit_reg((dreg),(reg));
}


void emit_addss_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf3;
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x58;
	emit_reg((dreg),(reg));
}


void emit_cvtsi2ssq_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf3;
	emit_rex(1,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x2a;
	emit_reg((dreg),(reg));
}


void emit_cvtsi2ss_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf3;
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x2a;
	emit_reg((dreg),(reg));
}


void emit_cvtsi2sdq_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf2;
	emit_rex(1,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x2a;
	emit_reg((dreg),(reg));
}


void emit_cvtsi2sd_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf2;
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x2a;
	emit_reg((dreg),(reg));
}


void emit_cvtss2sd_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf3;
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x5a;
	emit_reg((dreg),(reg));
}


void emit_cvtsd2ss_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf2;
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x5a;
	emit_reg((dreg),(reg));
}


void emit_cvttss2siq_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf3;
	emit_rex(1,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x2c;
	emit_reg((dreg),(reg));
}


void emit_cvttss2si_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf3;
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x2c;
	emit_reg((dreg),(reg));
}


void emit_cvttsd2siq_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf2;
	emit_rex(1,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x2c;
	emit_reg((dreg),(reg));
}


void emit_cvttsd2si_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf2;
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x2c;
	emit_reg((dreg),(reg));
}


void emit_divss_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf3;
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x5e;
	emit_reg((dreg),(reg));
}


void emit_divsd_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf2;
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x5e;
	emit_reg((dreg),(reg));
}


void emit_movd_reg_freg(codegendata *cd, s8 reg, s8 freg) {
	*(cd->mcodeptr++) = 0x66;
	emit_rex(1,(freg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x6e;
	emit_reg((freg),(reg));
}


void emit_movd_freg_reg(codegendata *cd, s8 freg, s8 reg) {
	*(cd->mcodeptr++) = 0x66;
	emit_rex(1,(freg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x7e;
	emit_reg((freg),(reg));
}


void emit_movd_reg_membase(codegendata *cd, s8 reg, s8 basereg, s8 disp) {
	*(cd->mcodeptr++) = 0x66;
	emit_rex(0,(reg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x7e;
	emit_membase(cd, (basereg),(disp),(reg));
}


void emit_movd_reg_memindex(codegendata *cd, s8 reg, s8 disp, s8 basereg, s8 indexreg, s8 scale) {
	*(cd->mcodeptr++) = 0x66;
	emit_rex(0,(reg),(indexreg),(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x7e;
	emit_memindex(cd, (reg),(disp),(basereg),(indexreg),(scale));
}


void emit_movd_membase_reg(codegendata *cd, s8 basereg, s8 disp, s8 dreg) {
	*(cd->mcodeptr++) = 0x66;
	emit_rex(1,(dreg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x6e;
	emit_membase(cd, (basereg),(disp),(dreg));
}


void emit_movdl_membase_reg(codegendata *cd, s8 basereg, s8 disp, s8 dreg) {
	*(cd->mcodeptr++) = 0x66;
	emit_rex(0,(dreg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x6e;
	emit_membase(cd, (basereg),(disp),(dreg));
}


void emit_movd_memindex_reg(codegendata *cd, s8 disp, s8 basereg, s8 indexreg, s8 scale, s8 dreg) {
	*(cd->mcodeptr++) = 0x66;
	emit_rex(0,(dreg),(indexreg),(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x6e;
	emit_memindex(cd, (dreg),(disp),(basereg),(indexreg),(scale));
}


void emit_movq_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf3;
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x7e;
	emit_reg((dreg),(reg));
}


void emit_movq_reg_membase(codegendata *cd, s8 reg, s8 basereg, s8 disp) {
	*(cd->mcodeptr++) = 0x66;
	emit_rex(0,(reg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0xd6;
	emit_membase(cd, (basereg),(disp),(reg));
}


void emit_movq_membase_reg(codegendata *cd, s8 basereg, s8 disp, s8 dreg) {
	*(cd->mcodeptr++) = 0xf3;
	emit_rex(0,(dreg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x7e;
	emit_membase(cd, (basereg),(disp),(dreg));
}


void emit_movss_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf3;
	emit_rex(0,(reg),0,(dreg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x10;
	emit_reg((reg),(dreg));
}


void emit_movsd_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf2;
	emit_rex(0,(reg),0,(dreg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x10;
	emit_reg((reg),(dreg));
}


void emit_movss_reg_membase(codegendata *cd, s8 reg, s8 basereg, s8 disp) {
	*(cd->mcodeptr++) = 0xf3;
	emit_rex(0,(reg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x11;
	emit_membase(cd, (basereg),(disp),(reg));
}


/* Always emit a REX byte, because the instruction size can be smaller when   */
/* all register indexes are smaller than 7.                                   */
void emit_movss_reg_membase32(codegendata *cd, s8 reg, s8 basereg, s8 disp) {
	*(cd->mcodeptr++) = 0xf3;
	emit_byte_rex((reg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x11;
	emit_membase32(cd, (basereg),(disp),(reg));
}


void emit_movsd_reg_membase(codegendata *cd, s8 reg, s8 basereg, s8 disp) {
	*(cd->mcodeptr++) = 0xf2;
	emit_rex(0,(reg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x11;
	emit_membase(cd, (basereg),(disp),(reg));
}


/* Always emit a REX byte, because the instruction size can be smaller when   */
/* all register indexes are smaller than 7.                                   */
void emit_movsd_reg_membase32(codegendata *cd, s8 reg, s8 basereg, s8 disp) {
	*(cd->mcodeptr++) = 0xf2;
	emit_byte_rex((reg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x11;
	emit_membase32(cd, (basereg),(disp),(reg));
}


void emit_movss_membase_reg(codegendata *cd, s8 basereg, s8 disp, s8 dreg) {
	*(cd->mcodeptr++) = 0xf3;
	emit_rex(0,(dreg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x10;
	emit_membase(cd, (basereg),(disp),(dreg));
}


/* Always emit a REX byte, because the instruction size can be smaller when   */
/* all register indexes are smaller than 7.                                   */
void emit_movss_membase32_reg(codegendata *cd, s8 basereg, s8 disp, s8 dreg) {
	*(cd->mcodeptr++) = 0xf3;
	emit_byte_rex((dreg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x10;
	emit_membase32(cd, (basereg),(disp),(dreg));
}


void emit_movlps_membase_reg(codegendata *cd, s8 basereg, s8 disp, s8 dreg)
{
	emit_rex(0,(dreg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x12;
	emit_membase(cd, (basereg),(disp),(dreg));
}


void emit_movlps_reg_membase(codegendata *cd, s8 reg, s8 basereg, s8 disp)
{
	emit_rex(0,(reg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x13;
	emit_membase(cd, (basereg),(disp),(reg));
}


void emit_movsd_membase_reg(codegendata *cd, s8 basereg, s8 disp, s8 dreg) {
	*(cd->mcodeptr++) = 0xf2;
	emit_rex(0,(dreg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x10;
	emit_membase(cd, (basereg),(disp),(dreg));
}


/* Always emit a REX byte, because the instruction size can be smaller when   */
/* all register indexes are smaller than 7.                                   */
void emit_movsd_membase32_reg(codegendata *cd, s8 basereg, s8 disp, s8 dreg) {
	*(cd->mcodeptr++) = 0xf2;
	emit_byte_rex((dreg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x10;
	emit_membase32(cd, (basereg),(disp),(dreg));
}


void emit_movlpd_membase_reg(codegendata *cd, s8 basereg, s8 disp, s8 dreg)
{
	*(cd->mcodeptr++) = 0x66;
	emit_rex(0,(dreg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x12;
	emit_membase(cd, (basereg),(disp),(dreg));
}


void emit_movlpd_reg_membase(codegendata *cd, s8 reg, s8 basereg, s8 disp)
{
	*(cd->mcodeptr++) = 0x66;
	emit_rex(0,(reg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x13;
	emit_membase(cd, (basereg),(disp),(reg));
}


void emit_movss_reg_memindex(codegendata *cd, s8 reg, s8 disp, s8 basereg, s8 indexreg, s8 scale) {
	*(cd->mcodeptr++) = 0xf3;
	emit_rex(0,(reg),(indexreg),(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x11;
	emit_memindex(cd, (reg),(disp),(basereg),(indexreg),(scale));
}


void emit_movsd_reg_memindex(codegendata *cd, s8 reg, s8 disp, s8 basereg, s8 indexreg, s8 scale) {
	*(cd->mcodeptr++) = 0xf2;
	emit_rex(0,(reg),(indexreg),(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x11;
	emit_memindex(cd, (reg),(disp),(basereg),(indexreg),(scale));
}


void emit_movss_memindex_reg(codegendata *cd, s8 disp, s8 basereg, s8 indexreg, s8 scale, s8 dreg) {
	*(cd->mcodeptr++) = 0xf3;
	emit_rex(0,(dreg),(indexreg),(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x10;
	emit_memindex(cd, (dreg),(disp),(basereg),(indexreg),(scale));
}


void emit_movsd_memindex_reg(codegendata *cd, s8 disp, s8 basereg, s8 indexreg, s8 scale, s8 dreg) {
	*(cd->mcodeptr++) = 0xf2;
	emit_rex(0,(dreg),(indexreg),(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x10;
	emit_memindex(cd, (dreg),(disp),(basereg),(indexreg),(scale));
}


void emit_mulss_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf3;
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x59;
	emit_reg((dreg),(reg));
}


void emit_mulsd_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf2;
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x59;
	emit_reg((dreg),(reg));
}


void emit_subss_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf3;
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x5c;
	emit_reg((dreg),(reg));
}


void emit_subsd_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0xf2;
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x5c;
	emit_reg((dreg),(reg));
}


void emit_ucomiss_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x2e;
	emit_reg((dreg),(reg));
}


void emit_ucomisd_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0x66;
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x2e;
	emit_reg((dreg),(reg));
}


void emit_xorps_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x57;
	emit_reg((dreg),(reg));
}


void emit_xorps_membase_reg(codegendata *cd, s8 basereg, s8 disp, s8 dreg) {
	emit_rex(0,(dreg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x57;
	emit_membase(cd, (basereg),(disp),(dreg));
}


void emit_xorpd_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
	*(cd->mcodeptr++) = 0x66;
	emit_rex(0,(dreg),0,(reg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x57;
	emit_reg((dreg),(reg));
}


void emit_xorpd_membase_reg(codegendata *cd, s8 basereg, s8 disp, s8 dreg) {
	*(cd->mcodeptr++) = 0x66;
	emit_rex(0,(dreg),0,(basereg));
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x57;
	emit_membase(cd, (basereg),(disp),(dreg));
}


/* system instructions ********************************************************/

void emit_rdtsc(codegendata *cd)
{
	*(cd->mcodeptr++) = 0x0f;
	*(cd->mcodeptr++) = 0x31;
}


/*
 * 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:
 */
