/* src/vm/jit/intrp/codegen.c - code generator for Interpreter

   Copyright (C) 1996-2005, 2006, 2007, 2008
   CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO

   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.

*/


#include "config.h"

#include <assert.h>
#include <stdio.h>

#if defined(WITH_FFI)
# include <ffi.h>
#elif defined(WITH_FFCALL)
# include <avcall.h>
#else
# error neither WITH_FFI nor WITH_FFCALL defined
#endif

#include "vm/types.h"

#include "arch.h"

#include "vm/jit/intrp/codegen.h"
#include "vm/jit/intrp/intrp.h"

#include "mm/memory.h"

#include "native/native.h"

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

#include "vm/jit/asmpart.h"
#include "vm/jit/codegen-common.h"
#include "vm/jit/dseg.h"
#include "vm/jit/jit.h"
#include "vm/jit/parse.h"
#include "vm/jit/patcher.h"
#include "vm/jit/stack.h"
#include "vm/jit/stacktrace.h"

#include "vmcore/class.h"
#include "vmcore/options.h"


#define gen_branch(_inst) { \
  gen_##_inst(cd, 0); \
  codegen_addreference(cd, BLOCK_OF(iptr->dst.insindex)); \
}

#define index2offset(_i) (-(_i) * SIZEOF_VOID_P)

/* functions used by cacao-gen.i */

void genarg_v(codegendata *cd1, Cell v)
{
	Inst **mcodepp = (Inst **) &(cd1->mcodeptr);
	*((Cell *) *mcodepp) = v;
	(*mcodepp)++;
}

void genarg_i(codegendata *cd1, s4 i)
{
	Inst **mcodepp = (Inst **) &(cd1->mcodeptr);
	*((Cell *) *mcodepp) = i;
	(*mcodepp)++;
}

void genarg_b(codegendata *cd1, s4 i)
{
  genarg_i(cd1, i);
}

void genarg_f(codegendata *cd1, float f)
{
	s4 fi;

	vm_f2Cell(f,fi);
	genarg_i(cd1, fi);
}

void genarg_l(codegendata *cd1, s8 l)
{
	Inst **mcodepp = (Inst **) &(cd1->mcodeptr);
	vm_l2twoCell(l, ((Cell *) *mcodepp)[1], ((Cell *) *mcodepp)[0]);
	(*mcodepp) +=2;
}

void genarg_aRef(codegendata *cd1, java_objectheader *a)
{
	Inst **mcodepp = (Inst **) &(cd1->mcodeptr);
	*((java_objectheader **) *mcodepp) = a;
	(*mcodepp)++;
}

void genarg_aArray(codegendata *cd1, java_arrayheader *a)
{
	Inst **mcodepp = (Inst **) &(cd1->mcodeptr);
	*((java_arrayheader **) *mcodepp) = a;
	(*mcodepp)++;
}

void genarg_aaTarget(codegendata *cd1, Inst **a)
{
	Inst **mcodepp = (Inst **) &(cd1->mcodeptr);
	*((Inst ***) *mcodepp) = a;
	(*mcodepp)++;
}

void genarg_aClass(codegendata *cd1, classinfo *a)
{
	Inst **mcodepp = (Inst **) &(cd1->mcodeptr);
	*((classinfo **) *mcodepp) = a;
	(*mcodepp)++;
}

void genarg_acr(codegendata *cd1, constant_classref *a)
{
	Inst **mcodepp = (Inst **) &(cd1->mcodeptr);
	*((constant_classref **) *mcodepp) = a;
	(*mcodepp)++;
}

void genarg_addr(codegendata *cd1, u1 *a)
{
	Inst **mcodepp = (Inst **) &(cd1->mcodeptr);
	*((u1 **) *mcodepp) = a;
	(*mcodepp)++;
}

void genarg_af(codegendata *cd1, functionptr a)
{
	Inst **mcodepp = (Inst **) &(cd1->mcodeptr);
	*((functionptr *) *mcodepp) = a;
	(*mcodepp)++;
}

void genarg_afi(codegendata *cd1, fieldinfo *a)
{
	Inst **mcodepp = (Inst **) &(cd1->mcodeptr);
	*((fieldinfo **) *mcodepp) = a;
	(*mcodepp)++;
}

void genarg_am(codegendata *cd1, methodinfo *a)
{
	Inst **mcodepp = (Inst **) &(cd1->mcodeptr);
	*((methodinfo **) *mcodepp) = a;
	(*mcodepp)++;
}

void genarg_acell(codegendata *cd1, Cell *a)
{
	Inst **mcodepp = (Inst **) &(cd1->mcodeptr);
	*((Cell **) *mcodepp) = a;
	(*mcodepp)++;
}

void genarg_ainst(codegendata *cd1, Inst *a)
{
	Inst **mcodepp = (Inst **) &(cd1->mcodeptr);
	*((Inst **) *mcodepp) = a;
	(*mcodepp)++;
}

void genarg_auf(codegendata *cd1, unresolved_field *a)
{
	Inst **mcodepp = (Inst **) &(cd1->mcodeptr);
	*((unresolved_field **) *mcodepp) = a;
	(*mcodepp)++;
}

void genarg_aum(codegendata *cd1, unresolved_method *a)
{
	Inst **mcodepp = (Inst **) &(cd1->mcodeptr);
	*((unresolved_method **) *mcodepp) = a;
	(*mcodepp)++;
}

void genarg_avftbl(codegendata *cd1, vftbl_t *a)
{
	Inst **mcodepp = (Inst **) &(cd1->mcodeptr);
	*((vftbl_t **) *mcodepp) = a;
	(*mcodepp)++;
}


/* include the interpreter generation functions *******************************/

/* Do not use "java-gen.i", it does not work with builddir. */
#include <java-gen.i>


typedef void (*genfunctionptr) (codegendata *);

typedef struct builtin_gen builtin_gen;

struct builtin_gen {
	functionptr builtin;
	genfunctionptr gen;
};

struct builtin_gen builtin_gen_table[] = {
    {BUILTIN_new,                     gen_NEW,             }, 
    {BUILTIN_newarray,                gen_NEWARRAY,        },
    {BUILTIN_newarray_boolean,        gen_NEWARRAY_BOOLEAN,},
    {BUILTIN_newarray_byte,           gen_NEWARRAY_BYTE,   },
    {BUILTIN_newarray_char,           gen_NEWARRAY_CHAR,   },
    {BUILTIN_newarray_short,          gen_NEWARRAY_SHORT,  },    
    {BUILTIN_newarray_int,            gen_NEWARRAY_INT,    },
    {BUILTIN_newarray_long,           gen_NEWARRAY_LONG,   },
    {BUILTIN_newarray_float,          gen_NEWARRAY_FLOAT,  },    
    {BUILTIN_newarray_double,         gen_NEWARRAY_DOUBLE, },
    {BUILTIN_arrayinstanceof,         gen_ARRAYINSTANCEOF, },

#if defined(ENABLE_THREADS)
    {LOCK_monitor_enter,              gen_MONITORENTER,    },
    {LOCK_monitor_exit,               gen_MONITOREXIT,     },
#endif

#if !(SUPPORT_FLOAT && SUPPORT_LONG && SUPPORT_F2L)
    {BUILTIN_f2l,                     gen_F2L,             },
#endif

#if !(SUPPORT_DOUBLE && SUPPORT_LONG && SUPPORT_D2L)
    {BUILTIN_d2l,					  gen_D2L, 			   },
#endif

#if !(SUPPORT_FLOAT && SUPPORT_F2I)
    {BUILTIN_f2i,					  gen_F2I, 			   },
#endif

#if !(SUPPORT_DOUBLE && SUPPORT_D2I)
    {BUILTIN_d2i,					  gen_D2I, 			   },
#endif

#if !SUPPORT_DIVISION
    {BUILTIN_idiv,					  gen_IDIV,			   },
    {BUILTIN_irem,					  gen_IREM,			   },
#endif

#if !(SUPPORT_DIVISION && SUPPORT_LONG && SUPPORT_LONG_DIV)
    {BUILTIN_ldiv,					  gen_LDIV,			   },
    {BUILTIN_lrem,					  gen_LREM,			   },
#endif

    {BUILTIN_frem,					  gen_FREM,			   },
    {BUILTIN_drem,					  gen_DREM,            },
};


/* codegen *********************************************************************

   Generates machine code.

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

#define I(value)   iptr[0].sx.val.i = (value); break;

bool intrp_codegen(jitdata *jd)
{
	methodinfo         *m;
	codegendata        *cd;
	registerdata       *rd;
	s4                  i, len, s1, s2, d;
	basicblock         *bptr;
	instruction        *iptr;
	u2                  currentline;
	methodinfo         *lm;             /* local methodinfo for ICMD_INVOKE*  */
	unresolved_method  *um;
	builtintable_entry *bte;
	methoddesc         *md;
	fieldinfo          *fi;
	unresolved_field   *uf;
	s4                  fieldtype;

	/* get required compiler data */

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

	/* prevent compiler warnings */

	d = 0;
	currentline = 0;
	lm = NULL;
	bte = NULL;

	/* determine stackframe size (in units of ptrint slots) */

	cd->stackframesize = m->maxlocals;

#if defined(ENABLE_THREADS)
	if (checksync && code_is_synchronized(code))
		cd->stackframesize += 1;
#endif

	/* create method header */

	(void) dseg_add_unique_address(cd, jd->code);
	(void) dseg_add_unique_s4(cd, cd->stackframesize * SIZEOF_VOID_P);

	code->synchronizedoffset = rd->memuse * 8;

	/* REMOVEME: We still need it for exception handling in assembler. */

	if (code_is_leafmethod(code))
		(void) dseg_add_unique_s4(cd, 1);
	else
		(void) dseg_add_unique_s4(cd, 0);

	(void) dseg_add_unique_s4(cd, 0);
	(void) dseg_add_unique_s4(cd, 0);

	dseg_addlinenumbertablesize(cd);

#if 0	
	/* initialize mcode variables */
	
	cd->mcodeptr = cd->mcodebase;
	cd->mcodeend = (s4 *) (cd->mcodebase + cd->mcodesize);
#endif

	gen_BBSTART;

#if defined(ENABLE_THREADS)
	if (checksync && code_is_synchronized(code)) {
		if (m->flags & ACC_STATIC) {
			gen_ACONST(cd, (java_objectheader *) m->clazz);
		}
		else {
			gen_ALOAD(cd, 0);
			gen_DUP(cd);
			gen_ASTORE(cd, index2offset(m->maxlocals));
		}
		
		gen_MONITORENTER(cd);
	}			
#endif

	if (opt_verbosecall)
		gen_TRACECALL(cd, m);

	gen_BBEND;

	/* walk through all basic blocks */

	for (bptr = jd->basicblocks; bptr != NULL; bptr = bptr->next) {

		bptr->mpc = (s4) (cd->mcodeptr - cd->mcodebase);

		if (bptr->flags != BBDELETED) {

		/* walk through all instructions */
		
		len = bptr->icount;

		gen_BBSTART;

		for (iptr = bptr->iinstr; len > 0; len--, iptr++) {
			if (iptr->line != currentline) {
				dseg_addlinenumber(cd, iptr->line);
				currentline = iptr->line;
			}

		MCODECHECK(64);       /* an instruction usually needs < 64 words      */

switch_again:
		switch (iptr->opc) {

		case ICMD_INLINE_START:
		case ICMD_INLINE_END:
			break;

		case ICMD_NOP:        /* ...  ==> ...                                 */
			break;

		case ICMD_CHECKNULL:  /* ..., objectref  ==> ..., objectref           */

			gen_CHECKNULL(cd);
			break;

		/* constant operations ************************************************/

		case ICMD_ICONST:     /* ...  ==> ..., constant                       */
		                      /* op1 = 0, val.i = constant                    */

			/* optimize ICONST (2^x) .. IREM --> IREMPOW2 (const) */

			if (len >= 2 && iptr[1].opc == ICMD_IREM) {
				switch (iptr[0].sx.val.i) {
	case 0x00000001: case 0x00000002: case 0x00000004: case 0x00000008:
	case 0x00000010: case 0x00000020: case 0x00000040: case 0x00000080:
	case 0x00000100: case 0x00000200: case 0x00000400: case 0x00000800:
	case 0x00001000: case 0x00002000: case 0x00004000: case 0x00008000:
	case 0x00010000: case 0x00020000: case 0x00040000: case 0x00080000:
	case 0x00100000: case 0x00200000: case 0x00400000: case 0x00800000:
	case 0x01000000: case 0x02000000: case 0x04000000: case 0x08000000:
	case 0x10000000: case 0x20000000: case 0x40000000: case 0x80000000:
					iptr[0].opc = ICMD_IREMPOW2;
					iptr[0].sx.val.i--;
					iptr[1].opc = ICMD_NOP;
					goto switch_again;
				}
			}

			/* optimize ICONST (2^x) .. IDIV --> IDIVPOW2 (const) */

			if (len >= 2 && iptr[1].opc == ICMD_IDIV) {
				switch (iptr[0].sx.val.i) {
	                       case 0x00000002: I( 1) case 0x00000004: I( 2) case 0x00000008: I( 3)
	case 0x00000010: I( 4) case 0x00000020: I( 5) case 0x00000040: I( 6) case 0x00000080: I( 7)
	case 0x00000100: I( 8) case 0x00000200: I( 9) case 0x00000400: I(10) case 0x00000800: I(11)
	case 0x00001000: I(12) case 0x00002000: I(13) case 0x00004000: I(14) case 0x00008000: I(15)
	case 0x00010000: I(16) case 0x00020000: I(17) case 0x00040000: I(18) case 0x00080000: I(19)
	case 0x00100000: I(20) case 0x00200000: I(21) case 0x00400000: I(22) case 0x00800000: I(23)
	case 0x01000000: I(24) case 0x02000000: I(25) case 0x04000000: I(26) case 0x08000000: I(27)
	case 0x10000000: I(28) case 0x20000000: I(29) case 0x40000000: I(30) case 0x80000000: I(31)
	default: goto dont_opt_IDIVPOW2;
				}
				iptr[0].opc = ICMD_IDIVPOW2;
				iptr[1].opc = ICMD_NOP;
				goto switch_again;
			}
dont_opt_IDIVPOW2:

			/* optimize ICONST .. IF_ICMPxx --> IFxx (const) */

			if (len >= 2) {
				switch (iptr[1].opc) {
					case ICMD_IF_ICMPEQ: iptr[0].opc = ICMD_IFEQ; break;
					case ICMD_IF_ICMPNE: iptr[0].opc = ICMD_IFNE; break;
					case ICMD_IF_ICMPLT: iptr[0].opc = ICMD_IFLT; break;
					case ICMD_IF_ICMPLE: iptr[0].opc = ICMD_IFLE; break;
					case ICMD_IF_ICMPGT: iptr[0].opc = ICMD_IFGT; break;
					case ICMD_IF_ICMPGE: iptr[0].opc = ICMD_IFGE; break;
					default:        goto dont_opt_IFxx;
				}
				iptr[0].dst.insindex = iptr[1].dst.insindex;
				iptr[1].opc = ICMD_NOP;
				goto switch_again;
			}
dont_opt_IFxx:

			gen_ICONST(cd, iptr->sx.val.i);
			break;

		case ICMD_LCONST:     /* ...  ==> ..., constant                       */
		                      /* op1 = 0, val.l = constant                    */

			/* optimize LCONST (2^x) .. LREM --> LREMPOW2 (const) */

			if (len >= 2 && iptr[1].opc == ICMD_LREM) {
				switch (iptr[0].sx.val.l) {
	case 0x00000001: case 0x00000002: case 0x00000004: case 0x00000008:
	case 0x00000010: case 0x00000020: case 0x00000040: case 0x00000080:
	case 0x00000100: case 0x00000200: case 0x00000400: case 0x00000800:
	case 0x00001000: case 0x00002000: case 0x00004000: case 0x00008000:
	case 0x00010000: case 0x00020000: case 0x00040000: case 0x00080000:
	case 0x00100000: case 0x00200000: case 0x00400000: case 0x00800000:
	case 0x01000000: case 0x02000000: case 0x04000000: case 0x08000000:
	case 0x10000000: case 0x20000000: case 0x40000000: case 0x80000000:
					iptr[0].opc = ICMD_LREMPOW2;
					iptr[0].sx.val.l--;
					iptr[1].opc = ICMD_NOP;
					goto switch_again;
				}
			}

			/* optimize LCONST (2^x) .. LDIV --> LDIVPOW2 (const) */

			if (len >= 2 && iptr[1].opc == ICMD_LDIV) {
				switch (iptr[0].sx.val.l) {
	                       case 0x00000002: I( 1) case 0x00000004: I( 2) case 0x00000008: I( 3)
	case 0x00000010: I( 4) case 0x00000020: I( 5) case 0x00000040: I( 6) case 0x00000080: I( 7)
	case 0x00000100: I( 8) case 0x00000200: I( 9) case 0x00000400: I(10) case 0x00000800: I(11)
	case 0x00001000: I(12) case 0x00002000: I(13) case 0x00004000: I(14) case 0x00008000: I(15)
	case 0x00010000: I(16) case 0x00020000: I(17) case 0x00040000: I(18) case 0x00080000: I(19)
	case 0x00100000: I(20) case 0x00200000: I(21) case 0x00400000: I(22) case 0x00800000: I(23)
	case 0x01000000: I(24) case 0x02000000: I(25) case 0x04000000: I(26) case 0x08000000: I(27)
	case 0x10000000: I(28) case 0x20000000: I(29) case 0x40000000: I(30) case 0x80000000: I(31)
	default: goto dont_opt_LDIVPOW2;
				}
				iptr[0].opc = ICMD_LDIVPOW2;
				iptr[1].opc = ICMD_NOP;
				goto switch_again;
			}
dont_opt_LDIVPOW2:

			/* optimize LCONST .. LCMP .. IFxx (0) --> IF_Lxx */

			if (len >= 3 && iptr[1].opc == ICMD_LCMP && iptr[2].sx.val.i == 0) {
				switch (iptr[2].opc) {
					case ICMD_IFEQ: iptr[0].opc = ICMD_IF_LEQ; break;
					case ICMD_IFNE: iptr[0].opc = ICMD_IF_LNE; break;
					case ICMD_IFLT: iptr[0].opc = ICMD_IF_LLT; break;
					case ICMD_IFLE: iptr[0].opc = ICMD_IF_LLE; break;
					case ICMD_IFGT: iptr[0].opc = ICMD_IF_LGT; break;
					case ICMD_IFGE: iptr[0].opc = ICMD_IF_LGE; break;
					default:        goto dont_opt_IF_Lxx;
				}
				iptr[0].dst.insindex = iptr[2].dst.insindex;
				iptr[1].opc = ICMD_NOP;
				iptr[2].opc = ICMD_NOP;
				goto switch_again;
			}
dont_opt_IF_Lxx:

			gen_LCONST(cd, iptr->sx.val.l);
			break;

		case ICMD_FCONST:     /* ...  ==> ..., constant                       */
		                      /* op1 = 0, val.f = constant                    */
			{
				ptrint fi;

				vm_f2Cell(iptr->sx.val.f, fi);
				gen_ICONST(cd, fi);
			}
			break;
			
		case ICMD_DCONST:     /* ...  ==> ..., constant                       */
		                      /* op1 = 0, val.d = constant                    */

			gen_LCONST(cd, *(s8 *)&(iptr->sx.val.d));
			break;

		case ICMD_ACONST:     /* ...  ==> ..., constant                       */
		                      /* op1 = 0, val.a = constant                    */

			if (INSTRUCTION_IS_UNRESOLVED(iptr))
				gen_PATCHER_ACONST(cd, NULL, iptr->sx.val.c.ref);
			else
				gen_ACONST(cd, iptr->sx.val.anyptr);
			break;


		/* load/store operations **********************************************/

		case ICMD_ILOAD:      /* ...  ==> ..., content of local variable      */
		                      /* op1 = local variable                         */

			gen_ILOAD(cd, index2offset(iptr->s1.varindex));
			break;

		case ICMD_LLOAD:      /* ...  ==> ..., content of local variable      */
		                      /* op1 = local variable                         */

			gen_LLOAD(cd, index2offset(iptr->s1.varindex));
			break;

		case ICMD_ALOAD:      /* ...  ==> ..., content of local variable      */
		                      /* op1 = local variable                         */

			gen_ALOAD(cd, index2offset(iptr->s1.varindex));
			break;

		case ICMD_FLOAD:      /* ...  ==> ..., content of local variable      */
		                      /* op1 = local variable                         */

			gen_ILOAD(cd, index2offset(iptr->s1.varindex));
			break;

		case ICMD_DLOAD:      /* ...  ==> ..., content of local variable      */
		                      /* op1 = local variable                         */

			gen_LLOAD(cd, index2offset(iptr->s1.varindex));
			break;


		case ICMD_ISTORE:     /* ..., value  ==> ...                          */
		                      /* op1 = local variable                         */

			gen_ISTORE(cd, index2offset(iptr->dst.varindex));
			break;

		case ICMD_LSTORE:     /* ..., value  ==> ...                          */
		                      /* op1 = local variable                         */

			gen_LSTORE(cd, index2offset(iptr->dst.varindex));
			break;

		case ICMD_ASTORE:     /* ..., value  ==> ...                          */
		                      /* op1 = local variable                         */

			gen_ASTORE(cd, index2offset(iptr->dst.varindex));
			break;


		case ICMD_FSTORE:     /* ..., value  ==> ...                          */
		                      /* op1 = local variable                         */

			gen_ISTORE(cd, index2offset(iptr->dst.varindex));
			break;

		case ICMD_DSTORE:     /* ..., value  ==> ...                          */
		                      /* op1 = local variable                         */

			gen_LSTORE(cd, index2offset(iptr->dst.varindex));
			break;


		/* pop/dup/swap operations ********************************************/

		case ICMD_POP:        /* ..., value  ==> ...                          */

			gen_POP(cd);
			break;

		case ICMD_POP2:       /* ..., value, value  ==> ...                   */

			gen_POP2(cd);
			break;

		case ICMD_DUP:        /* ..., a ==> ..., a, a                         */

			gen_DUP(cd);
			break;

		case ICMD_DUP_X1:     /* ..., a, b ==> ..., b, a, b                   */

			gen_DUP_X1(cd);
			break;

		case ICMD_DUP_X2:     /* ..., a, b, c ==> ..., c, a, b, c             */

			gen_DUP_X2(cd);
			break;

		case ICMD_DUP2:       /* ..., a, b ==> ..., a, b, a, b                */

			gen_DUP2(cd);
			break;

		case ICMD_DUP2_X1:    /* ..., a, b, c ==> ..., b, c, a, b, c          */

			gen_DUP2_X1(cd);
			break;

		case ICMD_DUP2_X2:    /* ..., a, b, c, d ==> ..., c, d, a, b, c, d    */

			gen_DUP2_X2(cd);
			break;

		case ICMD_SWAP:       /* ..., a, b ==> ..., b, a                      */

			gen_SWAP(cd);
			break;


		/* integer operations *************************************************/

		case ICMD_INEG:       /* ..., value  ==> ..., - value                 */

			gen_INEG(cd);
			break;

		case ICMD_LNEG:       /* ..., value  ==> ..., - value                 */

			gen_LNEG(cd);
			break;

		case ICMD_I2L:        /* ..., value  ==> ..., value                   */

			gen_I2L(cd);
			break;

		case ICMD_L2I:        /* ..., value  ==> ..., value                   */

			gen_L2I(cd);
			break;

		case ICMD_INT2BYTE:   /* ..., value  ==> ..., value                   */

			gen_INT2BYTE(cd);
			break;

		case ICMD_INT2CHAR:   /* ..., value  ==> ..., value                   */

			gen_INT2CHAR(cd);
			break;

		case ICMD_INT2SHORT:  /* ..., value  ==> ..., value                   */

			gen_INT2SHORT(cd);
			break;


		case ICMD_IADD:       /* ..., val1, val2  ==> ..., val1 + val2        */

			gen_IADD(cd);
			break;

		case ICMD_LADD:       /* ..., val1, val2  ==> ..., val1 + val2        */

			gen_LADD(cd);
			break;

		case ICMD_ISUB:       /* ..., val1, val2  ==> ..., val1 - val2        */

			gen_ISUB(cd);
			break;

		case ICMD_LSUB:       /* ..., val1, val2  ==> ..., val1 - val2        */

			gen_LSUB(cd);
			break;

		case ICMD_IMUL:       /* ..., val1, val2  ==> ..., val1 * val2        */

			gen_IMUL(cd);
			break;

		case ICMD_LMUL:       /* ..., val1, val2  ==> ..., val1 * val2        */

			gen_LMUL(cd);
			break;

		case ICMD_IDIV:       /* ..., val1, val2  ==> ..., val1 / val2        */

			gen_IDIV(cd);
			break;

		case ICMD_IREM:       /* ..., val1, val2  ==> ..., val1 % val2        */

			gen_IREM(cd);
			break;

		case ICMD_LDIV:       /* ..., val1, val2  ==> ..., val1 / val2        */

			gen_LDIV(cd);
			break;

		case ICMD_LREM:       /* ..., val1, val2  ==> ..., val1 % val2        */

			gen_LREM(cd);
			break;

		case ICMD_IDIVPOW2:   /* ..., value  ==> ..., value << constant       */
		                      /* val.i = constant                             */
		                      
			gen_IDIVPOW2(cd, iptr->sx.val.i);
			break;

		case ICMD_IREMPOW2:   /* ..., value  ==> ..., value % constant        */
		                      /* val.i = constant                             */

			gen_IREMPOW2(cd, iptr->sx.val.i);
			break;

		case ICMD_LDIVPOW2:   /* ..., value  ==> ..., value << constant       */
		                      /* val.i = constant                             */
		                      
			gen_LDIVPOW2(cd, iptr->sx.val.i);
			break;

		case ICMD_LREMPOW2:   /* ..., value  ==> ..., value % constant        */
		                      /* val.l = constant                             */

			gen_LREMPOW2(cd, iptr->sx.val.i);
			break;

		case ICMD_ISHL:       /* ..., val1, val2  ==> ..., val1 << val2       */

			gen_ISHL(cd);
			break;

		case ICMD_ISHR:       /* ..., val1, val2  ==> ..., val1 >> val2       */

			gen_ISHR(cd);
			break;

		case ICMD_IUSHR:      /* ..., val1, val2  ==> ..., val1 >>> val2      */

			gen_IUSHR(cd);
			break;

		case ICMD_LSHL:       /* ..., val1, val2  ==> ..., val1 << val2       */

			gen_LSHL(cd);
			break;

		case ICMD_LSHR:       /* ..., val1, val2  ==> ..., val1 >> val2       */

			gen_LSHR(cd);
			break;

		case ICMD_LUSHR:      /* ..., val1, val2  ==> ..., val1 >>> val2      */

			gen_LUSHR(cd);
			break;

		case ICMD_IAND:       /* ..., val1, val2  ==> ..., val1 & val2        */

			gen_IAND(cd);
			break;

		case ICMD_LAND:       /* ..., val1, val2  ==> ..., val1 & val2        */

			gen_LAND(cd);
			break;

		case ICMD_IOR:        /* ..., val1, val2  ==> ..., val1 | val2        */

			gen_IOR(cd);
			break;

		case ICMD_LOR:        /* ..., val1, val2  ==> ..., val1 | val2        */

			gen_LOR(cd);
			break;

		case ICMD_IXOR:       /* ..., val1, val2  ==> ..., val1 ^ val2        */

			gen_IXOR(cd);
			break;

		case ICMD_LXOR:       /* ..., val1, val2  ==> ..., val1 ^ val2        */

			gen_LXOR(cd);
			break;


		case ICMD_LCMP:       /* ..., val1, val2  ==> ..., val1 cmp val2      */

			/* optimize LCMP .. IFxx (0) --> IF_LCMPxx */

			if (len >= 2 && iptr[1].sx.val.i == 0) {
				switch (iptr[1].opc) {
					case ICMD_IFEQ: iptr[0].opc = ICMD_IF_LCMPEQ; break;
					case ICMD_IFNE: iptr[0].opc = ICMD_IF_LCMPNE; break;
					case ICMD_IFLT: iptr[0].opc = ICMD_IF_LCMPLT; break;
					case ICMD_IFLE: iptr[0].opc = ICMD_IF_LCMPLE; break;
					case ICMD_IFGT: iptr[0].opc = ICMD_IF_LCMPGT; break;
					case ICMD_IFGE: iptr[0].opc = ICMD_IF_LCMPGE; break;
					default:        goto dont_opt_IF_LCMPxx;
				}
				iptr[0].dst.insindex = iptr[1].dst.insindex;
				iptr[1].opc = ICMD_NOP;
				goto switch_again;
			}
dont_opt_IF_LCMPxx:

			gen_LCMP(cd);
			break;


		case ICMD_IINC:       /* ..., value  ==> ..., value + constant        */
		                      /* op1 = variable, val.i = constant             */

			gen_IINC(cd, index2offset(iptr->s1.varindex), iptr->sx.val.i);
			break;


		/* floating operations ************************************************/

		case ICMD_FNEG:       /* ..., value  ==> ..., - value                 */

			gen_FNEG(cd);
			break;

		case ICMD_DNEG:       /* ..., value  ==> ..., - value                 */

			gen_DNEG(cd);
			break;

		case ICMD_FADD:       /* ..., val1, val2  ==> ..., val1 + val2        */

			gen_FADD(cd);
			break;

		case ICMD_DADD:       /* ..., val1, val2  ==> ..., val1 + val2        */

			gen_DADD(cd);
			break;

		case ICMD_FSUB:       /* ..., val1, val2  ==> ..., val1 - val2        */

			gen_FSUB(cd);
			break;

		case ICMD_DSUB:       /* ..., val1, val2  ==> ..., val1 - val2        */

			gen_DSUB(cd);
			break;

		case ICMD_FMUL:       /* ..., val1, val2  ==> ..., val1 * val2        */

			gen_FMUL(cd);
			break;

		case ICMD_DMUL:       /* ..., val1, val2  ==> ..., val1 *** val2      */

			gen_DMUL(cd);
			break;

		case ICMD_FDIV:       /* ..., val1, val2  ==> ..., val1 / val2        */

			gen_FDIV(cd);
			break;

		case ICMD_DDIV:       /* ..., val1, val2  ==> ..., val1 / val2        */

			gen_DDIV(cd);
			break;
		
		case ICMD_FREM:       /* ..., val1, val2  ==> ..., val1 % val2        */

			gen_FREM(cd);
			break;

		case ICMD_DREM:       /* ..., val1, val2  ==> ..., val1 % val2        */

			gen_DREM(cd);
			break;
		
		case ICMD_I2F:       /* ..., value  ==> ..., (float) value            */

			gen_I2F(cd);
			break;

		case ICMD_L2F:       /* ..., value  ==> ..., (float) value            */

			gen_L2F(cd);
			break;

		case ICMD_I2D:       /* ..., value  ==> ..., (double) value           */

			gen_I2D(cd);
			break;
			
		case ICMD_L2D:       /* ..., value  ==> ..., (double) value           */

			gen_L2D(cd);
			break;
			
		case ICMD_F2I:       /* ..., value  ==> ..., (int) value              */

			gen_F2I(cd);
			break;
		
		case ICMD_D2I:       /* ..., value  ==> ..., (int) value              */

			gen_D2I(cd);
			break;
		
		case ICMD_F2L:       /* ..., value  ==> ..., (long) value             */

			gen_F2L(cd);
			break;

		case ICMD_D2L:       /* ..., value  ==> ..., (long) value             */

			gen_D2L(cd);
			break;

		case ICMD_F2D:       /* ..., value  ==> ..., (double) value           */

			gen_F2D(cd);
			break;
					
		case ICMD_D2F:       /* ..., value  ==> ..., (float) value            */

			gen_D2F(cd);
			break;
		
		case ICMD_FCMPL:      /* ..., val1, val2  ==> ..., val1 fcmpl val2    */

			gen_FCMPL(cd);
			break;
			
		case ICMD_DCMPL:      /* ..., val1, val2  ==> ..., val1 fcmpl val2    */

			gen_DCMPL(cd);
			break;
			
		case ICMD_FCMPG:      /* ..., val1, val2  ==> ..., val1 fcmpg val2    */

			gen_FCMPG(cd);
			break;

		case ICMD_DCMPG:      /* ..., val1, val2  ==> ..., val1 fcmpg val2    */

			gen_DCMPG(cd);
			break;


		/* memory operations **************************************************/

		case ICMD_ARRAYLENGTH: /* ..., arrayref  ==> ..., length              */

			gen_ARRAYLENGTH(cd);
			break;

		case ICMD_BALOAD:     /* ..., arrayref, index  ==> ..., value         */

			gen_BALOAD(cd);
			break;

		case ICMD_CALOAD:     /* ..., arrayref, index  ==> ..., value         */

			gen_CALOAD(cd);
			break;			

		case ICMD_SALOAD:     /* ..., arrayref, index  ==> ..., value         */

			gen_SALOAD(cd);
			break;

		case ICMD_IALOAD:     /* ..., arrayref, index  ==> ..., value         */

			gen_IALOAD(cd);
			break;

		case ICMD_FALOAD:     /* ..., arrayref, index  ==> ..., value         */

			gen_FALOAD(cd);
			break;

		case ICMD_LALOAD:     /* ..., arrayref, index  ==> ..., value         */
		case ICMD_DALOAD:     /* ..., arrayref, index  ==> ..., value         */

			gen_LALOAD(cd);
			break;

		case ICMD_AALOAD:     /* ..., arrayref, index  ==> ..., value         */

			gen_AALOAD(cd);
			break;


		case ICMD_BASTORE:    /* ..., arrayref, index, value  ==> ...         */

			gen_BASTORE(cd);
			break;

		case ICMD_CASTORE:    /* ..., arrayref, index, value  ==> ...         */
		case ICMD_SASTORE:    /* ..., arrayref, index, value  ==> ...         */

			gen_CASTORE(cd);
			break;

		case ICMD_IASTORE:    /* ..., arrayref, index, value  ==> ...         */

			gen_IASTORE(cd);
			break;

		case ICMD_FASTORE:    /* ..., arrayref, index, value  ==> ...         */

			gen_FASTORE(cd);
			break;

		case ICMD_LASTORE:    /* ..., arrayref, index, value  ==> ...         */
		case ICMD_DASTORE:    /* ..., arrayref, index, value  ==> ...         */

			gen_LASTORE(cd);
			break;

		case ICMD_AASTORE:    /* ..., arrayref, index, value  ==> ...         */

			gen_AASTORE(cd);
			break;


		case ICMD_GETSTATIC:  /* ...  ==> ..., value                          */
		                      /* op1 = type, val.a = field address            */

			if (INSTRUCTION_IS_UNRESOLVED(iptr)) {
				fi        = NULL;
				uf        = iptr->sx.s23.s3.uf;
				fieldtype = uf->fieldref->parseddesc.fd->type;
			}
			else {
				fi        = iptr->sx.s23.s3.fmiref->p.field;
				uf        = NULL;
				fieldtype = fi->type;
			}

			switch (fieldtype) {
			case TYPE_INT:
				if (fi == NULL)
					gen_PATCHER_GETSTATIC_INT(cd, 0, uf);
				else if (!CLASS_IS_OR_ALMOST_INITIALIZED(fi->clazz))
					gen_PATCHER_GETSTATIC_CLINIT_INT(cd, 0, fi);
				else
					gen_GETSTATIC_INT(cd, (u1 *) &(fi->value.i), fi);
				break;
			case TYPE_FLT:
				if (fi == NULL)
					gen_PATCHER_GETSTATIC_FLOAT(cd, 0, uf);
				else if (!CLASS_IS_OR_ALMOST_INITIALIZED(fi->clazz))
					gen_PATCHER_GETSTATIC_CLINIT_FLOAT(cd, 0, fi);
				else
					gen_GETSTATIC_FLOAT(cd, (u1 *) &(fi->value.i), fi);
				break;
			case TYPE_LNG:
			case TYPE_DBL:
				if (fi == NULL)
					gen_PATCHER_GETSTATIC_LONG(cd, 0, uf);
				else if (!CLASS_IS_OR_ALMOST_INITIALIZED(fi->clazz))
					gen_PATCHER_GETSTATIC_CLINIT_LONG(cd, 0, fi);
				else
					gen_GETSTATIC_LONG(cd, (u1 *) &(fi->value.l), fi);
				break;
			case TYPE_ADR:
				if (fi == NULL)
					gen_PATCHER_GETSTATIC_CELL(cd, 0, uf);
				else if (!CLASS_IS_OR_ALMOST_INITIALIZED(fi->clazz))
					gen_PATCHER_GETSTATIC_CLINIT_CELL(cd, 0, fi);
				else
					gen_GETSTATIC_CELL(cd, (u1 *) &(fi->value.a), fi);
				break;
			}
			break;

		case ICMD_PUTSTATIC:  /* ..., value  ==> ...                          */
		                      /* op1 = type, val.a = field address            */

			if (INSTRUCTION_IS_UNRESOLVED(iptr)) {
				fi        = NULL;
				uf        = iptr->sx.s23.s3.uf;
				fieldtype = uf->fieldref->parseddesc.fd->type;
			}
			else {
				fi        = iptr->sx.s23.s3.fmiref->p.field;
				uf        = NULL;
				fieldtype = fi->type;
			}

			switch (fieldtype) {
			case TYPE_INT:
				if (fi == NULL)
					gen_PATCHER_PUTSTATIC_INT(cd, 0, uf);
				else if (!CLASS_IS_OR_ALMOST_INITIALIZED(fi->clazz))
					gen_PATCHER_PUTSTATIC_CLINIT_INT(cd, 0, fi);
				else
					gen_PUTSTATIC_INT(cd, (u1 *) &(fi->value.i), fi);
				break;
			case TYPE_FLT:
				if (fi == NULL)
					gen_PATCHER_PUTSTATIC_FLOAT(cd, 0, uf);
				else if (!CLASS_IS_OR_ALMOST_INITIALIZED(fi->clazz))
					gen_PATCHER_PUTSTATIC_CLINIT_FLOAT(cd, 0, fi);
				else
					gen_PUTSTATIC_FLOAT(cd, (u1 *) &(fi->value.i), fi);
				break;
			case TYPE_LNG:
			case TYPE_DBL:
				if (fi == NULL)
					gen_PATCHER_PUTSTATIC_LONG(cd, 0, uf);
				else if (!CLASS_IS_OR_ALMOST_INITIALIZED(fi->clazz))
					gen_PATCHER_PUTSTATIC_CLINIT_LONG(cd, 0, fi);
				else
					gen_PUTSTATIC_LONG(cd, (u1 *) &(fi->value.l), fi);
				break;
			case TYPE_ADR:
				if (fi == NULL)
					gen_PATCHER_PUTSTATIC_CELL(cd, 0, uf);
				else if (!CLASS_IS_OR_ALMOST_INITIALIZED(fi->clazz))
					gen_PATCHER_PUTSTATIC_CLINIT_CELL(cd, 0, fi);
				else
					gen_PUTSTATIC_CELL(cd, (u1 *) &(fi->value.a), fi);
				break;
			}
			break;


		case ICMD_GETFIELD:   /* ...  ==> ..., value                          */
		                      /* op1 = type, val.a = field address            */

			if (INSTRUCTION_IS_UNRESOLVED(iptr)) {
				fi        = NULL;
				uf        = iptr->sx.s23.s3.uf;
				fieldtype = uf->fieldref->parseddesc.fd->type;
			}
			else {
				fi        = iptr->sx.s23.s3.fmiref->p.field;
				uf        = NULL;
				fieldtype = fi->type;
			}

			switch (fieldtype) {
			case TYPE_INT:
				if (fi == NULL)
					gen_PATCHER_GETFIELD_INT(cd, 0, uf);
				else
					gen_GETFIELD_INT(cd, fi->offset, fi);
				break;
			case TYPE_FLT:
				if (fi == NULL)
					gen_PATCHER_GETFIELD_FLOAT(cd, 0, uf);
				else
					gen_GETFIELD_FLOAT(cd, fi->offset, fi);
				break;
			case TYPE_LNG:
			case TYPE_DBL:
				if (fi == NULL)
					gen_PATCHER_GETFIELD_LONG(cd, 0, uf);
				else
					gen_GETFIELD_LONG(cd, fi->offset, fi);
				break;
			case TYPE_ADR:
				if (fi == NULL)
					gen_PATCHER_GETFIELD_CELL(cd, 0, uf);
				else
					gen_GETFIELD_CELL(cd, fi->offset, fi);
				break;
			}
			break;

		case ICMD_PUTFIELD:   /* ..., objectref, value  ==> ...               */
		                      /* op1 = type, val.a = field address            */

			if (INSTRUCTION_IS_UNRESOLVED(iptr)) {
				fi        = NULL;
				uf        = iptr->sx.s23.s3.uf;
				fieldtype = uf->fieldref->parseddesc.fd->type;
			}
			else {
				fi        = iptr->sx.s23.s3.fmiref->p.field;
				uf        = NULL;
				fieldtype = fi->type;
			}

			switch (fieldtype) {
			case TYPE_INT:
				if (fi == NULL)
					gen_PATCHER_PUTFIELD_INT(cd, 0, uf);
				else
					gen_PUTFIELD_INT(cd, fi->offset, fi);
				break;
			case TYPE_FLT:
				if (fi == NULL)
					gen_PATCHER_PUTFIELD_FLOAT(cd, 0, uf);
				else
					gen_PUTFIELD_FLOAT(cd, fi->offset, fi);
				break;
			case TYPE_LNG:
			case TYPE_DBL:
				if (fi == NULL)
					gen_PATCHER_PUTFIELD_LONG(cd, 0, uf);
				else
					gen_PUTFIELD_LONG(cd, fi->offset, fi);
				break;
			case TYPE_ADR:
				if (fi == NULL)
					gen_PATCHER_PUTFIELD_CELL(cd, 0, uf);
				else
					gen_PUTFIELD_CELL(cd, fi->offset, fi);
				break;
			}
			break;


		/* branch operations **************************************************/

		case ICMD_ATHROW:       /* ..., objectref ==> ... (, objectref)       */

			gen_ATHROW(cd);
			break;

		case ICMD_GOTO:         /* ... ==> ...                                */
		                        /* op1 = target JavaVM pc                     */
			gen_branch(GOTO);
			break;

		case ICMD_JSR:          /* ... ==> ...                                */
		                        /* op1 = target JavaVM pc                     */

			gen_JSR(cd, NULL);
			codegen_addreference(cd, BLOCK_OF(iptr->sx.s23.s3.jsrtarget.insindex));
			break;

		case ICMD_RET:          /* ... ==> ...                                */
		                        /* op1 = local variable                       */

			gen_RET(cd, index2offset(iptr->s1.varindex));
			break;

		case ICMD_IFNULL:       /* ..., value ==> ...                         */
		                        /* op1 = target JavaVM pc                     */

			gen_branch(IFNULL);
			break;

		case ICMD_IFNONNULL:    /* ..., value ==> ...                         */
		                        /* op1 = target JavaVM pc                     */

			gen_branch(IFNONNULL);
			break;

		case ICMD_IFEQ:         /* ..., value ==> ...                         */
		                        /* op1 = target JavaVM pc, val.i = constant   */

			if (iptr->sx.val.i == 0) {
				gen_branch(IFEQ);
			} else {
				gen_ICONST(cd, iptr->sx.val.i);
				gen_branch(IF_ICMPEQ);
			}
			break;

		case ICMD_IFLT:         /* ..., value ==> ...                         */
		                        /* op1 = target JavaVM pc, val.i = constant   */

			if (iptr->sx.val.i == 0) {
				gen_branch(IFLT);
			} else {
				gen_ICONST(cd, iptr->sx.val.i);
				gen_branch(IF_ICMPLT);
			}
			break;

		case ICMD_IFLE:         /* ..., value ==> ...                         */
		                        /* op1 = target JavaVM pc, val.i = constant   */

			if (iptr->sx.val.i == 0) {
				gen_branch(IFLE);
			} else {
				gen_ICONST(cd, iptr->sx.val.i);
				gen_branch(IF_ICMPLE);
			}
			break;

		case ICMD_IFNE:         /* ..., value ==> ...                         */
		                        /* op1 = target JavaVM pc, val.i = constant   */

			if (iptr->sx.val.i == 0) {
				gen_branch(IFNE);
			} else {
				gen_ICONST(cd, iptr->sx.val.i);
				gen_branch(IF_ICMPNE);
			}
			break;

		case ICMD_IFGT:         /* ..., value ==> ...                         */
		                        /* op1 = target JavaVM pc, val.i = constant   */

			if (iptr->sx.val.i == 0) {
				gen_branch(IFGT);
			} else {
				gen_ICONST(cd, iptr->sx.val.i);
				gen_branch(IF_ICMPGT);
			}
			break;

		case ICMD_IFGE:         /* ..., value ==> ...                         */
		                        /* op1 = target JavaVM pc, val.i = constant   */

			if (iptr->sx.val.i == 0) {
				gen_branch(IFGE);
			} else {
				gen_ICONST(cd, iptr->sx.val.i);
				gen_branch(IF_ICMPGE);
			}
			break;


		case ICMD_IF_LEQ:       /* ..., value ==> ...                         */
		                        /* op1 = target JavaVM pc, val.l = constant   */

			gen_LCONST(cd, iptr->sx.val.l);
			gen_branch(IF_LCMPEQ);
			break;

		case ICMD_IF_LLT:       /* ..., value ==> ...                         */
		                        /* op1 = target JavaVM pc, val.l = constant   */

			gen_LCONST(cd, iptr->sx.val.l);
			gen_branch(IF_LCMPLT);
			break;

		case ICMD_IF_LLE:       /* ..., value ==> ...                         */
		                        /* op1 = target JavaVM pc, val.l = constant   */

			gen_LCONST(cd, iptr->sx.val.l);
			gen_branch(IF_LCMPLE);
			break;

		case ICMD_IF_LNE:       /* ..., value ==> ...                         */
		                        /* op1 = target JavaVM pc, val.l = constant   */

			gen_LCONST(cd, iptr->sx.val.l);
			gen_branch(IF_LCMPNE);
			break;

		case ICMD_IF_LGT:       /* ..., value ==> ...                         */
		                        /* op1 = target JavaVM pc, val.l = constant   */

			gen_LCONST(cd, iptr->sx.val.l);
			gen_branch(IF_LCMPGT);
			break;

		case ICMD_IF_LGE:       /* ..., value ==> ...                         */
		                        /* op1 = target JavaVM pc, val.l = constant   */

			gen_LCONST(cd, iptr->sx.val.l);
			gen_branch(IF_LCMPGE);
			break;

		case ICMD_IF_LCMPEQ:    /* ..., value, value ==> ...                  */
		                        /* op1 = target JavaVM pc                     */

			gen_branch(IF_LCMPEQ);
			break;

		case ICMD_IF_LCMPNE:    /* ..., value, value ==> ...                  */
		                        /* op1 = target JavaVM pc                     */

			gen_branch(IF_LCMPNE);
			break;

		case ICMD_IF_LCMPLT:    /* ..., value, value ==> ...                  */
		                        /* op1 = target JavaVM pc                     */

			gen_branch(IF_LCMPLT);
			break;

		case ICMD_IF_LCMPGT:    /* ..., value, value ==> ...                  */
		                        /* op1 = target JavaVM pc                     */

			gen_branch(IF_LCMPGT);
			break;

		case ICMD_IF_LCMPLE:    /* ..., value, value ==> ...                  */
		                        /* op1 = target JavaVM pc                     */

			gen_branch(IF_LCMPLE);
			break;

		case ICMD_IF_LCMPGE:    /* ..., value, value ==> ...                  */
		                        /* op1 = target JavaVM pc                     */

			gen_branch(IF_LCMPGE);
			break;


		case ICMD_IF_ICMPEQ:    /* ..., value, value ==> ...                  */
		                        /* op1 = target JavaVM pc                     */

			gen_branch(IF_ICMPEQ);
			break;

		case ICMD_IF_ACMPEQ:    /* ..., value, value ==> ...                  */
		                        /* op1 = target JavaVM pc                     */

			gen_branch(IF_ACMPEQ);
			break;

		case ICMD_IF_ICMPNE:    /* ..., value, value ==> ...                  */
		                        /* op1 = target JavaVM pc                     */

			gen_branch(IF_ICMPNE);
			break;

		case ICMD_IF_ACMPNE:    /* ..., value, value ==> ...                  */
		                        /* op1 = target JavaVM pc                     */

			gen_branch(IF_ACMPNE);
			break;

		case ICMD_IF_ICMPLT:    /* ..., value, value ==> ...                  */
		                        /* op1 = target JavaVM pc                     */

			gen_branch(IF_ICMPLT);
			break;

		case ICMD_IF_ICMPGT:    /* ..., value, value ==> ...                  */
		                        /* op1 = target JavaVM pc                     */

			gen_branch(IF_ICMPGT);
			break;

		case ICMD_IF_ICMPLE:    /* ..., value, value ==> ...                  */
		                        /* op1 = target JavaVM pc                     */

			gen_branch(IF_ICMPLE);
			break;

		case ICMD_IF_ICMPGE:    /* ..., value, value ==> ...                  */
		                        /* op1 = target JavaVM pc                     */

			gen_branch(IF_ICMPGE);
			break;


		case ICMD_ARETURN:      /* ..., retvalue ==> ...                      */
		case ICMD_IRETURN:      /* ..., retvalue ==> ...                      */
		case ICMD_FRETURN:      /* ..., retvalue ==> ...                      */

#if defined(ENABLE_THREADS)
			if (checksync && code_is_synchronized(code)) {
				if (m->flags & ACC_STATIC) {
					gen_ACONST(cd, (java_objectheader *) m->clazz);
				} else {
					gen_ALOAD(cd, index2offset(m->maxlocals));
				}
				gen_MONITOREXIT(cd);
			}
#endif
			if (opt_verbosecall)
				gen_TRACERETURN(cd, m);

			gen_IRETURN(cd, index2offset(cd->stackframesize));
			break;

		case ICMD_LRETURN:      /* ..., retvalue ==> ...                      */
		case ICMD_DRETURN:      /* ..., retvalue ==> ...                      */

#if defined(ENABLE_THREADS)
			if (checksync && code_is_synchronized(code)) {
				if (m->flags & ACC_STATIC) {
					gen_ACONST(cd, (java_objectheader *) m->clazz);
				} else {
					gen_ALOAD(cd, index2offset(m->maxlocals));
				}
				gen_MONITOREXIT(cd);
			}
#endif
			if (opt_verbosecall)
				gen_TRACELRETURN(cd, m);

			gen_LRETURN(cd, index2offset(cd->stackframesize));
			break;

		case ICMD_RETURN:       /* ...  ==> ...                               */

#if defined(ENABLE_THREADS)
			if (checksync && code_is_synchronized(code)) {
				if (m->flags & ACC_STATIC) {
					gen_ACONST(cd, (java_objectheader *) m->clazz);
				} else {
					gen_ALOAD(cd, index2offset(m->maxlocals));
				}
				gen_MONITOREXIT(cd);
			}
#endif
			if (opt_verbosecall)
				gen_TRACERETURN(cd, m);

			gen_RETURN(cd, index2offset(cd->stackframesize));
			break;


		case ICMD_TABLESWITCH:  /* ..., index ==> ...                         */
			{
			s4 i, l;
			branch_target_t *table;

			table = iptr->dst.table;

			l = iptr->sx.s23.s2.tablelow;
			i = iptr->sx.s23.s3.tablehigh;
			
			i = i - l + 1;

			/* arguments: low, range, datasegment address, table
		       offset in datasegment, default target */

			gen_TABLESWITCH(cd, l, i, NULL, 0, NULL);

			/* actually -3 cells offset */

			cd->mcodeptr = (u1 *) cd->mcodeptr - 2 * sizeof(Inst);
			dseg_adddata(cd);
			cd->mcodeptr = (u1 *) cd->mcodeptr + 2 * sizeof(Inst);

			codegen_addreference(cd, BLOCK_OF(table[0].insindex));

			/* build jump table top down and use address of lowest entry */

			table += i;

			while (--i >= 0) {
				dseg_add_target(cd, BLOCK_OF(table->insindex)); 
				--table;
			}
			}

			/* length of dataseg after last dseg_add_target is used by load */
			((ptrint *)(cd->mcodeptr))[-2] = (ptrint) -(cd->dseglen);
			break;


		case ICMD_LOOKUPSWITCH: /* ..., key ==> ...                           */
			{
			s4 i;
			lookup_target_t *lookup;

			lookup = iptr->dst.lookup;

			i = iptr->sx.s23.s2.lookupcount;
			
			/* arguments: count, datasegment address, table offset in         */
			/* datasegment, default target                                    */
			gen_LOOKUPSWITCH(cd, i, NULL, 0, NULL);

			/* actually -3 cells offset */

			cd->mcodeptr = (u1 *) cd->mcodeptr - 2 * sizeof(Inst);
			dseg_adddata(cd);
			cd->mcodeptr = (u1 *) cd->mcodeptr + 2 * sizeof(Inst);

			/* build jump table top down and use address of lowest entry */

			while (--i >= 0) {
				dseg_add_target(cd, BLOCK_OF(lookup->target.insindex)); 
				dseg_add_unique_address(cd, lookup->value);
				lookup++;
			}

			codegen_addreference(cd, BLOCK_OF(iptr->sx.s23.s3.lookupdefault.insindex));
			}

			/* length of dataseg after last dseg_add_target is used by load */
			((ptrint *)(cd->mcodeptr))[-2] = (ptrint) -(cd->dseglen);
			break;


		case ICMD_BUILTIN:      /* ..., arg1, arg2, arg3 ==> ...              */
		                        /* op1 = arg count val.a = builtintable entry */
			bte = iptr->sx.s23.s3.bte;

			for (i = 0; i < sizeof(builtin_gen_table)/sizeof(builtin_gen); i++) {
				builtin_gen *bg = &builtin_gen_table[i];
				if (bg->builtin == bte->fp) {
					(bg->gen)(cd);
					goto gen_builtin_end;
				}
			}

			vm_abort(0);

		gen_builtin_end:
			break;

		case ICMD_INVOKESTATIC: /* ..., [arg1, [arg2 ...]] ==> ...            */
		                        /* op1 = arg count, val.a = method pointer    */

			if (INSTRUCTION_IS_UNRESOLVED(iptr)) {
				um = iptr->sx.s23.s3.um;
				md = um->methodref->parseddesc.md;
				gen_PATCHER_INVOKESTATIC(cd, 0, md->paramslots, um);
			}
			else {
				lm = iptr->sx.s23.s3.fmiref->p.method;
				md = lm->parseddesc;
				gen_INVOKESTATIC(cd, (Inst **) lm->stubroutine, md->paramslots, lm);
			}
			break;

		case ICMD_INVOKESPECIAL:/* ..., objectref, [arg1, [arg2 ...]] ==> ... */

			if (INSTRUCTION_IS_UNRESOLVED(iptr)) {
				um = iptr->sx.s23.s3.um;
				md = um->methodref->parseddesc.md;
				gen_PATCHER_INVOKESPECIAL(cd, 0, md->paramslots, um);
			}
			else {
				lm = iptr->sx.s23.s3.fmiref->p.method;
				md = lm->parseddesc;
				gen_INVOKESPECIAL(cd, (Inst **) lm->stubroutine, md->paramslots, lm);
			}
			break;

		case ICMD_INVOKEVIRTUAL:/* op1 = arg count, val.a = method pointer    */

			if (INSTRUCTION_IS_UNRESOLVED(iptr)) {
				um = iptr->sx.s23.s3.um;
				md = um->methodref->parseddesc.md;
				gen_PATCHER_INVOKEVIRTUAL(cd, 0, md->paramslots, um);
			}
			else {
				lm = iptr->sx.s23.s3.fmiref->p.method;
				md = lm->parseddesc;

				s1 = OFFSET(vftbl_t, table[0]) +
					sizeof(methodptr) * lm->vftblindex;

				gen_INVOKEVIRTUAL(cd, s1, md->paramslots, lm);
			}
			break;

		case ICMD_INVOKEINTERFACE:/* op1 = arg count, val.a = method pointer  */

			if (INSTRUCTION_IS_UNRESOLVED(iptr)) {
				um = iptr->sx.s23.s3.um;
				md = um->methodref->parseddesc.md;
				gen_PATCHER_INVOKEINTERFACE(cd, 0, 0, md->paramslots, um);
			}
			else {
				lm = iptr->sx.s23.s3.fmiref->p.method;
				md = lm->parseddesc;

				s1 = OFFSET(vftbl_t, interfacetable[0]) -
					sizeof(methodptr*) * lm->clazz->index;

				s2 = sizeof(methodptr) * (lm - lm->clazz->methods);

				gen_INVOKEINTERFACE(cd, s1, s2, md->paramslots, lm);
			}
			break;


		case ICMD_CHECKCAST:  /* ..., objectref ==> ..., objectref            */
		                      /* op1:   0 == array, 1 == class                */
		                      /* val.a: (classinfo *) superclass              */

			if (!(iptr->flags.bits & INS_FLAG_ARRAY)) {
				if (INSTRUCTION_IS_UNRESOLVED(iptr))
					gen_PATCHER_CHECKCAST(cd, NULL, iptr->sx.s23.s3.c.ref);
				else
					gen_CHECKCAST(cd, iptr->sx.s23.s3.c.cls, NULL);
			}
			else {
				if (INSTRUCTION_IS_UNRESOLVED(iptr))
					gen_PATCHER_ARRAYCHECKCAST(cd, NULL, iptr->sx.s23.s3.c.ref);
				else
					gen_ARRAYCHECKCAST(cd, iptr->sx.s23.s3.c.cls, NULL);
			}
			break;

		case ICMD_INSTANCEOF: /* ..., objectref ==> ..., intresult            */
		                      /* op1:   0 == array, 1 == class                */
		                      /* val.a: (classinfo *) superclass              */

			if (INSTRUCTION_IS_UNRESOLVED(iptr))
				gen_PATCHER_INSTANCEOF(cd, NULL, iptr->sx.s23.s3.c.ref);
			else
				gen_INSTANCEOF(cd, iptr->sx.s23.s3.c.cls, iptr->sx.s23.s3.c.ref);
			break;

		case ICMD_MULTIANEWARRAY:/* ..., cnt1, [cnt2, ...] ==> ..., arrayref  */
		                      /* op1 = dimension, val.a = class               */

			if (INSTRUCTION_IS_UNRESOLVED(iptr))
				gen_PATCHER_MULTIANEWARRAY(cd, NULL, iptr->s1.argcount, iptr->sx.s23.s3.c.ref);
			else
				gen_MULTIANEWARRAY(cd, iptr->sx.s23.s3.c.cls, iptr->s1.argcount, NULL);
			break;

		default:
			exceptions_throw_internalerror("Unknown ICMD %d during code generation",
										   iptr->opc);
			return false;
	} /* switch */
		
	} /* for instruction */

	gen_BBEND;

	} /* if (bptr->flags != BBDELETED) */
	} /* for basic block */

	dseg_createlinenumbertable(cd);

	codegen_finish(jd);

#ifdef VM_PROFILING
	vm_block_insert(jd->code->mcode + jd->code->mcodelength);
#endif

	/* branch resolving (walk through all basic blocks) */

	for (bptr = jd->basicblocks; bptr != NULL; bptr = bptr->next) {
		branchref *brefs;

		for (brefs = bptr->branchrefs; brefs != NULL; brefs = brefs->next) {
			gen_resolveanybranch(((u1*) jd->code->entrypoint) + brefs->branchpos,
			                     ((u1 *)jd->code->entrypoint) + bptr->mpc);
		}
	}

	/* everything's ok */

	return true;
}


/* createcompilerstub **********************************************************

   Creates a stub routine which calls the compiler.

   A stub consists of:

   +-------------+
   | codeinfo *  |
   +-------------+ <-- stub
   | codeptr     |
   +-------------+
   | framesize   |  (in ptrint units, does not include return address)
   +-------------+
   | TRANSLATE   |
   +-------------+
   | methodinfo  |
   +-------------+

   codeptr points either to TRANSLATE or to the translated threaded code

   all methods are called indirectly through methodptr

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

#define COMPILERSTUB_DATASIZE    2
#define COMPILERSTUB_CODESIZE    4

#define COMPILERSTUB_SIZE        COMPILERSTUB_DATASIZE + COMPILERSTUB_CODESIZE


u1 *intrp_createcompilerstub(methodinfo *m)
{
	Inst        *s;
	Inst        *d;
	codegendata *cd;
	s4           stackframesize;
	int32_t      dumpmarker;

	s = CNEW(Inst, COMPILERSTUB_SIZE);

	/* set data pointer and code pointer */

	d = s;
	s = s + COMPILERSTUB_DATASIZE;

	/* The codeinfo pointer is actually a pointer to the
	   methodinfo. This fakes a codeinfo structure. */

	d[0] = (Inst *) m;
	d[1] = (Inst *) &d[0];                                    /* fake code->m */

	/* mark start of dump memory area */

	DMARKER;
	
	cd = DNEW(codegendata);
    cd->mcodeptr = (u1 *) s;
	cd->lastinstwithoutdispatch = ~0;
	cd->superstarts = NULL;

	genarg_ainst(cd, s + 2);

	if (m->flags & ACC_NATIVE) {
		stackframesize = m->parseddesc->paramslots;
	} 
	else {
		stackframesize = m->maxlocals;

#if defined(ENABLE_THREADS)
		if (checksync && code_is_synchronized(code))
			stackframesize += 1;
#endif
	}

	genarg_i(cd, stackframesize);

	gen_BBSTART;
	gen_TRANSLATE(cd, m);
	gen_BBEND;
	
#ifdef VM_PROFILING
	vm_block_insert(cd->mcodeptr);
#endif

#if defined(ENABLE_STATISTICS)
	if (opt_stat)
		count_cstub_len += COMPILERSTUB_SIZE;
#endif

	/* release dump area */

	DRELEASE;
	
	return (u1 *) s;
}


#if defined(WITH_FFI)
static ffi_type *cacaotype2ffitype(s4 cacaotype)
{
	switch (cacaotype) {
	case TYPE_INT:
#if SIZEOF_VOID_P == 8
		return &ffi_type_sint64;
#else
		return &ffi_type_sint32;
#endif
	case TYPE_LNG:
		return &ffi_type_sint64;
	case TYPE_FLT:
		return &ffi_type_float;
	case TYPE_DBL:
		return &ffi_type_double;
	case TYPE_ADR:
		return &ffi_type_pointer;
	case TYPE_VOID:
		return &ffi_type_void;
	default:
		assert(false);
	}
}
#endif


/* native stub:
+---------+
|NATIVECALL|
+---------+
|methodinf|
+---------+
|function |
+---------+
|cif      |
+---------+
*/

#if defined(WITH_FFI)
static ffi_cif *createnativecif(methodinfo *m, methoddesc *nmd)
{
	methoddesc  *md = m->parseddesc; 
	ffi_cif     *pcif = NEW(ffi_cif);
	ffi_type   **types = MNEW(ffi_type *, nmd->paramcount);
	ffi_type   **ptypes = types;
	s4           i;

	/* pass env pointer */

	*ptypes++ = &ffi_type_pointer;

	/* for static methods, pass class pointer */

	if (m->flags & ACC_STATIC)
		*ptypes++ = &ffi_type_pointer;

	/* pass parameter to native function */

	for (i = 0; i < md->paramcount; i++)
		*ptypes++ = cacaotype2ffitype(md->paramtypes[i].type);

	assert(ptypes - types == nmd->paramcount);

    if (ffi_prep_cif(pcif, FFI_DEFAULT_ABI, nmd->paramcount, cacaotype2ffitype(md->returntype.type), types) != FFI_OK)
		assert(0);

	return pcif;
}
#endif


u1 *intrp_createnativestub(functionptr f, jitdata *jd, methoddesc *nmd)
{
	methodinfo   *m;
	codeinfo     *code;
	codegendata  *cd;
	registerdata *rd;
#if defined(WITH_FFI)
	ffi_cif *cif;
#else
	u1      *cif;
#endif
	s4            stackframesize;

	/* get required compiler data */

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

	/* determine stackframe size (in units of ptrint) */

	stackframesize = nmd->paramslots;

	/* create method header */

	(void) dseg_add_unique_address(cd, code);              /* CodeinfoPointer */
	(void) dseg_add_unique_s4(cd, stackframesize * SIZEOF_VOID_P); /*FrameSize*/
	(void) dseg_add_unique_s4(cd, 0);                       /* IsSync         */
	(void) dseg_add_unique_s4(cd, 0);                       /* IsLeaf         */
	(void) dseg_add_unique_s4(cd, 0);                       /* IntSave        */
	(void) dseg_add_unique_s4(cd, 0);                       /* FltSave        */
	dseg_addlinenumbertablesize(cd);
	(void) dseg_add_unique_s4(cd, 0);                       /* ExTableSize    */

#if defined(WITH_FFI)
	/* prepare ffi cif structure */

	cif = createnativecif(m, nmd);
#else
	cif = NULL;
#endif

	gen_BBSTART;

	if (opt_verbosecall)
		gen_TRACECALL(cd, m);

	if (f == NULL) {
		gen_PATCHER_NATIVECALL(cd, m, f, (u1 *)cif);
	} else {
		if (opt_verbosecall)
			gen_TRACENATIVECALL(cd, m, f, (u1 *)cif);
		else
			gen_NATIVECALL(cd, m, f, (u1 *)cif);
	}

	gen_BBEND;

	codegen_finish(jd);

#ifdef VM_PROFILING
	vm_block_insert(jd->code->mcode + jd->code->mcodelength);
#endif

	return jd->code->entrypoint;
}


/* call jni function */
Cell *nativecall(functionptr f, methodinfo *m, Cell *sp, Inst *ra, Cell *fp, u1 *addrcif)
{
#if defined(WITH_FFCALL)
	av_alist alist;
	methoddesc *md;
	Cell *p;
	Cell *endsp;
	s4 i;

	struct {
		stackframeinfo sfi;
		localref_table lrt;
	} s;

	md = m->parseddesc;

	switch (md->returntype.type) {
	case TYPE_INT:
		endsp = sp - 1 + md->paramslots;
		av_start_long(alist, f, endsp);
		break;
	case TYPE_LNG:
		endsp = sp - 2 + md->paramslots;
		av_start_longlong(alist, f, endsp);
		break;
	case TYPE_FLT:
		endsp = sp - 1 + md->paramslots;
		av_start_float(alist, f, endsp);
		break;
	case TYPE_DBL:
		endsp = sp - 2 + md->paramslots;
		av_start_double(alist, f, endsp);
		break;
	case TYPE_ADR:
		endsp = sp - 1 + md->paramslots;
		av_start_ptr(alist, f, void *, endsp);
		break;
	case TYPE_VOID:
		endsp = sp + md->paramslots;
		av_start_void(alist, f);
		break;
	default:
		assert(false);
	}

	av_ptr(alist, _Jv_JNIEnv *, _Jv_env);

	if (m->flags & ACC_STATIC)
		av_ptr(alist, classinfo *, m->clazz);

	for (i = 0, p = sp + md->paramslots; i < md->paramcount; i++) {
		switch (md->paramtypes[i].type) {
		case TYPE_INT:
			p -= 1;
			av_long(alist, *p);
			break;
		case TYPE_LNG:
			p -= 2;
			av_longlong(alist, *(s8 *)p);
			break;
		case TYPE_FLT:
			p -= 1;
			av_float(alist, *((float *) p));
			break;
		case TYPE_DBL:
			p -= 2;
			av_double(alist, *(double *) p);
			break;
		case TYPE_ADR:
			p -= 1;
			av_ptr(alist, void *, *(void **) p);
			break;
		default:
			assert(false);
		}
	}

	global_sp = sp;

	/* create stackframe info structure */

	codegen_start_native_call(((u1 *) &s) + sizeof(s), m->code->entrypoint,
							  (u1 *) fp, (u1 *) ra);

	av_call(alist);

	*exceptionptr = codegen_finish_native_call(((u1 *) &s) + sizeof(s));

	CLEAR_global_sp;

	return endsp;
#elif defined(WITH_FFI)
	methoddesc  *md = m->parseddesc; 
	ffi_cif     *pcif;
	void        *values[md->paramcount + 2];
	void       **pvalues = values;
	Cell        *p;
	Cell        *endsp;
	s4           i;
	_Jv_JNIEnv  *penv;

	struct {
		stackframeinfo sfi;
		localref_table lrt;
	} s;

	pcif = (ffi_cif *) addrcif;

	/* pass env pointer */

	penv = (_Jv_JNIEnv *) _Jv_env;
	*pvalues++ = &penv;

	/* for static methods, pass class pointer */

	if (m->flags & ACC_STATIC)
		*pvalues++ = &m->clazz;

	/* pass parameter to native function */

	for (i = 0, p = sp + md->paramslots; i < md->paramcount; i++) {
		if (IS_2_WORD_TYPE(md->paramtypes[i].type))
			p -= 2;
		else
			p--;

		*pvalues++ = p;
	}

	/* calculate position of return value */

	if (md->returntype.type == TYPE_VOID)
		endsp = sp + md->paramslots;
	else
		endsp = sp - (IS_2_WORD_TYPE(md->returntype.type) ? 2 : 1) + md->paramslots;

	global_sp = sp;

	/* create stackframe info structure */

	codegen_start_native_call(((u1 *) &s) + sizeof(s), m->code->entrypoint,
							  (u1 *) fp, (u1 *) ra);

	ffi_call(pcif, FFI_FN(f), endsp, values);

	*exceptionptr = codegen_finish_native_call(((u1 *) &s) + sizeof(s));

	CLEAR_global_sp;

	return endsp;
#endif
}


u1 *createcalljavafunction(methodinfo *m)
{
	methodinfo         *tmpm;
	u1                 *entrypoint;
	jitdata            *jd;
	codegendata        *cd;
	registerdata       *rd;
	methoddesc         *md;
	int32_t             dumpmarker;

	/* mark dump memory */

	DMARKER;

	/* allocate memory */

	jd = DNEW(jitdata);

	tmpm = DNEW(methodinfo);
	cd = DNEW(codegendata);
	rd = DNEW(registerdata);

	jd->m = tmpm;
	jd->flags = 0;
	jd->cd = cd;
	jd->rd = rd;

	/* Allocate codeinfo memory from the heap as we need to keep them. */

	jd->code = code_codeinfo_new(tmpm); /* XXX check allocation */

	/* setup code generation stuff */

	MSET(tmpm, 0, u1, sizeof(methodinfo));

	codegen_setup(jd);

	md = m->parseddesc;

	/* create method header */

	(void) dseg_add_unique_address(cd, NULL);              /* CodeinfoPointer */
	(void) dseg_add_unique_s4(cd, md->paramslots * SIZEOF_VOID_P);/* FrameSize*/
	(void) dseg_add_unique_s4(cd, 0);                       /* IsSync         */
	(void) dseg_add_unique_s4(cd, 0);                       /* IsLeaf         */
	(void) dseg_add_unique_s4(cd, 0);                       /* IntSave        */
	(void) dseg_add_unique_s4(cd, 0);                       /* FltSave        */
	dseg_addlinenumbertablesize(cd);
	(void) dseg_add_unique_s4(cd, 0);                       /* ExTableSize    */


	/* generate code */
	
	gen_BBSTART;
	gen_INVOKESTATIC(cd, (Inst **)m->stubroutine, md->paramslots, 0);
	gen_END(cd);

	gen_BBEND;

	codegen_finish(jd);

#ifdef VM_PROFILING
	vm_block_insert(jd->code->mcode + jd->code->mcodelength);
#endif
	entrypoint = jd->code->entrypoint;

	/* release memory */

	DRELEASE;

	return entrypoint;
}


/*
 * 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:
 * vim:noexpandtab:sw=4:ts=4:
 */
