/*
 * kaffe.def
 * Kaffe instruction definitions.
 *
 * Copyright (c) 1996 T. J. Wilkinson & Associates, London, UK.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * Written by Tim Wilkinson <tim@tjwassoc.demon.co.uk>, 1996.
 */

define_insn(NOP)
{
        /*
         * No operation.
         */
}

define_insn(ACONST_NULL)
{
	/*
	 * ... -> ..., 0
	 */
	push(1);
	move_ref_const(wstack(0), 0);
}

define_insn(ICONST_M1)
{
	/*
	 * ... -> ..., -1
	 */
	push(1);
	move_int_const(wstack(0), -1);
}

define_insn(ICONST_0)
{
	/*
	 * ... -> ..., 0
	 */
	push(1);
	move_int_const(wstack(0), 0);
}

define_insn(ICONST_1)
{
	/*
	 * ... -> ..., 1
	 */
	push(1);
	move_int_const(wstack(0), 1);
}

define_insn(ICONST_2)
{
	/*
	 * ... -> ..., 2
	 */
	push(1);
	move_int_const(wstack(0), 2);
}

define_insn(ICONST_3)
{
	/*
	 * ... -> ..., 3
	 */
	push(1);
	move_int_const(wstack(0), 3);
}

define_insn(ICONST_4)
{
	/*
	 * ... -> ..., 4
	 */
	push(1);
	move_int_const(wstack(0), 4);
}

define_insn(ICONST_5)
{
	/*
	 * ... -> ..., 5
	 */
	push(1);
	move_int_const(wstack(0), 5);
}

define_insn(LCONST_0)
{
	/*
	 * ... -> ..., 0, 0
	 */
	push(2);
	move_long_const(wstack(0), 0);
}

define_insn(LCONST_1)
{
	/*
	 * ... -> ..., 0, 1
	 */
	push(2);
	move_long_const(wstack(0), 1);
}

define_insn(FCONST_0)
{
	/*
	 * ... -> ..., 0.0
	 */
	push(1);
	move_float_const(wstack(0), 0.0);
}

define_insn(FCONST_1)
{
	/*
	 * ... -> ..., 1.0
	 */
	push(1);
	move_float_const(wstack(0), 1.0);
}

define_insn(FCONST_2)
{
	/*
	 * ... -> ..., 2.0
	 */
	push(1);
	move_float_const(wstack(0), 2.0);
}

define_insn(DCONST_0)
{
	/*
	 * ... -> ..., 0.0
	 */
	push(2);
	move_double_const(wstack(0), 0.0);
}

define_insn(DCONST_1)
{
	/*
	 * ... -> ..., 1.0
	 */
	push(2);
	move_double_const(wstack(0), 1.0);
}

define_insn(BIPUSH)
{
	/*
	 * ... -> ..., val
	 */
	check_pc (0);

	low = (int8)getpc(0);
	push(1);
	move_int_const(wstack(0), low);
}

define_insn(SIPUSH)
{
	/*
	 * ... -> ..., val
	 */
	check_pcidx (0);

	low = (int16)((getpc(0) << 8) | getpc(1));
	push(1);
	move_int_const(wstack(0), low);
}

define_insn(LDC1)
{
	/*
	 * ... -> ..., const
	 */
	check_pc (0);

	idx = (uint8)getpc(0);
	push(1);
	switch (CLASS_CONST_TAG(current_class(), idx)) {
	case CONSTANT_ResolvedString:
		move_ref_const(stack(0), (void*)CLASS_CONST_DATA(current_class(),idx));
		break;
	case CONSTANT_Float:
		move_float_const(stack(0), *(float*)&CLASS_CONST_DATA(current_class(),idx));
		break;
	case CONSTANT_Integer:
		move_int_const(stack(0), CLASS_CONST_DATA(current_class(),idx));
		break;
	default:
		ABORT();
	}
}

define_insn(LDC2)
{
	/*
	 * ... -> ..., const
	 */
	check_pcidx (0);

	idx = (uint16)((getpc(0) << 8) | getpc(1));
	push(1);
	switch (CLASS_CONST_TAG(current_class(), idx)) {
	case CONSTANT_ResolvedString:
		move_ref_const(stack(0), (void*)CLASS_CONST_DATA(current_class(),idx));
		break;
	case CONSTANT_Float:
		move_float_const(stack(0), *(jfloat*)&CLASS_CONST_DATA(current_class(),idx));
		break;
	case CONSTANT_Integer:
		move_int_const(stack(0), CLASS_CONST_DATA(current_class(),idx));
		break;
	default:
		ABORT();
	}
}

define_insn(LDC2W)
{
	/*
	 * ... -> ..., long const
	 */
	check_pcidx (0);

	idx = (uint16)((getpc(0) << 8) | getpc(1));
	tmpl = CLASS_CONST_LONG (current_class(), idx);
	push(2);
	switch (CLASS_CONST_TAG(current_class(), idx)) {
	case CONSTANT_Long:
		move_long_const(stack(0), tmpl);
		break;
	case CONSTANT_Double:
		move_double_const(stack(0), *(jdouble*)&tmpl);
		break;
	default:
		ABORT();
	}
}

define_insn(ILOAD)
{
	/*
	 * ..., -> ..., local variable
	 */
	if (wide) {
		check_pcidx (0);
		idx = (uint16)((getpc(0) << 8) | getpc(1));
	}
	else {
		check_pc (0);
		idx = (uint8)getpc(0);
	}
	wide = 0;

	check_local_int(idx);

	push(1);
	move_int(wstack(0), local(idx));
}

define_insn(LLOAD)
{
	/*
	 * ..., -> ..., long local variable
	 */
	if (wide) {
		check_pcidx (0);
		idx = (uint16)((getpc(0) << 8) | getpc(1));
	}
	else {
		check_pc (0);
		idx = (uint8)getpc(0);
	}
	wide = 0;

	check_local_long(idx+1);

	push(2);
	move_long(wstack(0), local_long(idx));
}

define_insn(FLOAD)
{
	/*
	 * ..., -> ..., local variable
	 */
	if (wide) {
		check_pcidx (0);
		idx = (uint16)((getpc(0) << 8) | getpc(1));
	}
	else {
		check_pc (0);
		idx = (uint8)getpc(0);
	}
	wide = 0;

	check_local_float(idx);

	push(1);
	move_float(wstack(0), local_float(idx));
}

define_insn(DLOAD)
{
	/*
	 * ..., -> ..., long local variable
	 */
	if (wide) {
		check_pcidx (0);
		idx = (uint16)((getpc(0) << 8) | getpc(1));
	}
	else {
		check_pc (0);
		idx = (uint8)getpc(0);
	}
	wide = 0;

	check_local_double(idx+1);

	push(2);
	move_double(wstack(0), local_double(idx));
}

define_insn(ALOAD)
{
	/*
	 * ..., -> ..., local variable
	 */
	if (wide) {
		check_pcidx (0);
		idx = (uint16)((getpc(0) << 8) | getpc(1));
	}
	else {
		check_pc (0);
		idx = (uint8)getpc(0);
	}
	wide = 0;

	check_local_ref(idx);

	push(1);
	move_ref(wstack(0), local(idx));
}

define_insn(ILOAD_0)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_int(0);

	push(1);
	move_int(wstack(0), local(0));
}

define_insn(ILOAD_1)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_int(1);

	push(1);
	move_int(wstack(0), local(1));
}

define_insn(ILOAD_2)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_int(2);

	push(1);
	move_int(wstack(0), local(2));
}

define_insn(ILOAD_3)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_int(3);

	push(1);
	move_int(wstack(0), local(3));
}

define_insn(LLOAD_0)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_long(0+1);

	push(2);
	move_long(wstack(0), local_long(0));
}

define_insn(LLOAD_1)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_long(1+1);

	push(2);
	move_long(wstack(0), local_long(1));
}

define_insn(LLOAD_2)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_long(2+1);

	push(2);
	move_long(wstack(0), local_long(2));
}

define_insn(LLOAD_3)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_long(3+1);

	push(2);
	move_long(wstack(0), local_long(3));
}

define_insn(FLOAD_0)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_float(0);

	push(1);
	move_float(wstack(0), local_float(0));
}

define_insn(FLOAD_1)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_float(1);

	push(1);
	move_float(wstack(0), local_float(1));
}

define_insn(FLOAD_2)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_float(2);

	push(1);
	move_float(wstack(0), local_float(2));
}

define_insn(FLOAD_3)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_float(3);

	push(1);
	move_float(wstack(0), local_float(3));
}

define_insn(DLOAD_0)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_double(0+1);

	push(2);
	move_double(wstack(0), local_double(0));
}

define_insn(DLOAD_1)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_double(1+1);

	push(2);
	move_double(wstack(0), local_double(1));
}

define_insn(DLOAD_2)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_double(2+1);

	push(2);
	move_double(wstack(0), local_double(2));
}

define_insn(DLOAD_3)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_double(3+1);

	push(2);
	move_double(wstack(0), local_double(3));
}

define_insn(ALOAD_0)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_ref(0);

	push(1);
	move_ref(wstack(0), local(0));
}

define_insn(ALOAD_1)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_ref(1);

	push(1);
	move_ref(wstack(0), local(1));
}

define_insn(ALOAD_2)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_ref(2);

	push(1);
	move_ref(wstack(0), local(2));
}

define_insn(ALOAD_3)
{
	/*
	 * ..., -> ..., local variable
	 */
	check_local_ref(3);

	push(1);
	move_ref(wstack(0), local(3));
}

define_insn(IALOAD)
{
	/*
	 * ..., array ref, index -> ..., value
	 */
	check_stack_int(0);
	check_stack_intarray(1);

	CHECK_NULL (IALOAD, rstack(1), 34);
	slot_alloctmp(tmp);

	/* Check we are within the array bounds */
	load_offset_int(tmp, rstack(1), object_array_length);

	cbranch_int_ult(rstack(0), tmp, reference_label(IALOAD, 10));
	softcall_badarrayindex();
	set_label(IALOAD, 10);

	lshl_int_const(rstack(0), rstack(0), SHIFT_jint);
	add_ref(rstack(0), rstack(1), rstack(0));
	load_offset_int(wstack(1), rstack(0), object_array_offset);

	pop(1);
}

define_insn(LALOAD)
{
	/*
	 * ..., array ref, index -> ..., long value
	 */
	check_stack_int(0);
	check_stack_longarray(1);

	CHECK_NULL (LALOAD, rstack(1), 34);
	slot_alloctmp(tmp);

	/* Check we are within the array bounds */
	load_offset_int(tmp, rstack(1), object_array_length);

	cbranch_int_ult(rstack(0), tmp, reference_label(LALOAD, 11));
	softcall_badarrayindex();
	set_label(LALOAD, 11);

	lshl_int_const(tmp, rstack(0), SHIFT_jlong);
	if (object_array_offset != 0) {
		add_int_const(tmp, tmp, object_array_offset);
	}
	add_ref(tmp, rstack(1), tmp);
	load_long(wstack(0), tmp);
}

define_insn(FALOAD)
{
	/*
	 * ..., array ref, index -> ..., float value
	 */
	check_stack_int(0);
	check_stack_floatarray(1);

	CHECK_NULL (FALOAD, rstack(1), 34);
	slot_alloctmp(tmp);

	/* Check we are within the array bounds */
	load_offset_int(tmp, rstack(1), object_array_length);

	cbranch_int_ult(rstack(0), tmp, reference_label(FALOAD, 12));
	softcall_badarrayindex();
	set_label(FALOAD, 12);

	lshl_int_const(tmp, rstack(0), SHIFT_jfloat);
	if (object_array_offset != 0) {
		add_int_const(tmp, tmp, object_array_offset);
	}
	add_ref(tmp, rstack(1), tmp);
	load_float(wstack(1), tmp);

	pop(1);
}

define_insn(DALOAD)
{
	/*
	 * ..., array ref, index -> ..., double value
	 */
	check_stack_int(0);
	check_stack_doublearray(1);

	CHECK_NULL (DALOAD, rstack(1), 34);
	slot_alloctmp(tmp);

	/* Check we are within the array bounds */
	load_offset_int(tmp, rstack(1), object_array_length);

	cbranch_int_ult(rstack(0), tmp, reference_label(DALOAD, 13));
	softcall_badarrayindex();
	set_label(DALOAD, 13);

	lshl_int_const(tmp, rstack(0), SHIFT_jdouble);
	if (object_array_offset != 0) {
		add_int_const(tmp, tmp, object_array_offset);
	}
	add_ref(tmp, rstack(1), tmp);
	load_double(wstack(0), tmp);
}

define_insn(AALOAD)
{
	/*
	 * ..., array ref, index -> ..., ref value
	 */
	check_stack_int(0);
	check_stack_refarray(1);

	CHECK_NULL(AALOAD, rstack(1), 34);
	slot_alloctmp(tmp);

	/* Check we are within the array bounds */
	load_offset_int(tmp, rstack(1), object_array_length);

	cbranch_int_ult(rstack(0), tmp, reference_label(AALOAD, 14));
	softcall_badarrayindex();
	set_label(AALOAD, 14);

	lshl_int_const(rstack(0), rstack(0), SHIFT_jref);
	add_ref(rstack(0), rstack(1), rstack(0));
	load_offset_ref(wstack(1), rstack(0), object_array_offset);

	pop(1);
}

define_insn(BALOAD)
{
	/*
	 * ..., array ref, index -> ..., byte value
	 */
	check_stack_int(0);
	check_stack_bytearray(1);

	CHECK_NULL(BALOAD, rstack(1), 34);
	slot_alloctmp(tmp);

	/* Check we are within the array bounds */
	load_offset_int(tmp, rstack(1), object_array_length);

	cbranch_int_ult(rstack(0), tmp, reference_label(BALOAD, 15));
	softcall_badarrayindex();
	set_label(BALOAD, 15);

	if (SHIFT_jbyte > 0) {
		lshl_int_const(tmp, rstack(0), SHIFT_jbyte);
	}
	else {
		move_int(tmp, rstack(0));
	}
	if (object_array_offset != 0) {
		add_int_const(tmp, tmp, object_array_offset);
	}
	add_ref(tmp, rstack(1), tmp);
	load_byte(wstack(1), tmp);

	pop(1);
}

define_insn(CALOAD)
{
	/*
	 * ..., array ref, index -> ..., char value
	 */
	check_stack_int(0);
	check_stack_chararray(1);

	CHECK_NULL(CALOAD, rstack(1), 34);
	slot_alloctmp(tmp);

	/* Check we are within the array bounds */
	load_offset_int(tmp, rstack(1), object_array_length);

	cbranch_int_ult(rstack(0), tmp, reference_label(CALOAD, 16));
	softcall_badarrayindex();
	set_label(CALOAD, 16);

	lshl_int_const(tmp, rstack(0), SHIFT_jchar);
	if (object_array_offset != 0) {
		add_int_const(tmp, tmp, object_array_offset);
	}
	add_ref(tmp, rstack(1), tmp);
	load_char(wstack(1), tmp);

	pop(1);
}

define_insn(SALOAD)
{
	/*
	 * ..., array ref, index -> ..., short value
	 */
	check_stack_int(0);
	check_stack_shortarray(1);

	CHECK_NULL(SALOAD, rstack(1), 34);
	slot_alloctmp(tmp);

	/* Check we are within the array bounds */
	load_offset_int(tmp, rstack(1), object_array_length);

	cbranch_int_ult(rstack(0), tmp, reference_label(SALOAD, 17));
	softcall_badarrayindex();
	set_label(SALOAD, 17);

	lshl_int_const(tmp, rstack(0), SHIFT_jshort);
	if (object_array_offset != 0) {
		add_int_const(tmp, tmp, object_array_offset);
	}
	add_ref(tmp, rstack(1), tmp);
	load_short(wstack(1), tmp);

	pop(1);
}

define_insn(ISTORE)
{
	/*
	 * ..., var -> ...
	 */
	if (wide) {
		check_pcidx (0);
		idx = (uint16)((getpc(0) << 8) | getpc(1));
	}
	else {
		check_pc (0);
		idx = (uint8)getpc(0);
	}
	wide = 0;

	check_stack_int(0);

	move_int(local(idx), rstack(0));
	pop(1);
}

define_insn(LSTORE)
{
	/*
	 * ..., long var -> ...
	 */
	if (wide) {
		check_pcidx (0);
		idx = (uint16)((getpc(0) << 8) | getpc(1));
	}
	else {
		check_pc (0);
		idx = (uint8)getpc(0);
	}
	wide = 0;

	check_stack_long(0);

	move_long(local_long(idx), rstack(0));
	pop(2);
}

define_insn(FSTORE)
{
	/*
	 * ..., var -> ...
	 */
	if (wide) {
		check_pcidx (0);
		idx = (uint16)((getpc(0) << 8) | getpc(1));
	}
	else {
		check_pc (0);
		idx = (uint8)getpc(0);
	}
	wide = 0;

	check_stack_float(0);

	move_float(local_float(idx), rstack(0));
	pop(1);
}

define_insn(DSTORE)
{
	/*
	 * ..., var -> ...
	 */
	if (wide) {
		check_pcidx (0);
		idx = (uint16)((getpc(0) << 8) | getpc(1));
	}
	else {
		check_pc (0);
		idx = (uint8)getpc(0);
	}
	wide = 0;

	check_stack_double(0);

	move_double(local_double(idx), rstack(0));
	pop(2);
}

define_insn(ASTORE)
{
	/*
	 * ..., var -> ...
	 */
	if (wide) {
		check_pcidx (0);
		idx = (uint16)((getpc(0) << 8) | getpc(1));
	}
	else {
		check_pc (0);
		idx = (uint8)getpc(0);
	}
	wide = 0;

	check_stack_ref(0);

	move_ref(local(idx), rstack(0));
	pop(1);
}

define_insn(ISTORE_0)
{
	/*
	 * ..., val -> ...
	 */
	check_stack_int(0);

	move_int(local(0), rstack(0));
	pop(1);
}

define_insn(ISTORE_1)
{
	/*
	 * ..., val -> ...
	 */
	check_stack_int(0);

	move_int(local(1), rstack(0));
	pop(1);
}

define_insn(ISTORE_2)
{
	/*
	 * ..., val -> ...
	 */
	check_stack_int(0);

	move_int(local(2), rstack(0));
	pop(1);
}

define_insn(ISTORE_3)
{
	/*
	 * ..., val -> ...
	 */
	check_stack_int(0);

	move_int(local(3), rstack(0));
	pop(1);
}

define_insn(LSTORE_0)
{
	/*
	 * ..., long val -> ...
	 */
	check_stack_long(0);

	move_long(local_long(0), rstack(0));
	pop(2);
}

define_insn(LSTORE_1)
{
	/*
	 * ..., long val -> ...
	 */
	check_stack_long(0);

	move_long(local_long(1), rstack(0));
	pop(2);
}

define_insn(LSTORE_2)
{
	/*
	 * ..., long val -> ...
	 */
	check_stack_long(0);

	move_long(local_long(2), rstack(0));
	pop(2);
}

define_insn(LSTORE_3)
{
	/*
	 * ..., long val -> ...
	 */
	check_stack_long(0);

	move_long(local_long(3), rstack(0));
	pop(2);
}

define_insn(FSTORE_0)
{
	/*
	 * ..., val -> ...
	 */
	check_stack_float(0);

	move_float(local_float(0), rstack(0));
	pop(1);
}

define_insn(FSTORE_1)
{
	/*
	 * ..., val -> ...
	 */
	check_stack_float(0);

	move_float(local_float(1), rstack(0));
	pop(1);
}

define_insn(FSTORE_2)
{
	/*
	 * ..., val -> ...
	 */
	check_stack_float(0);

	move_float(local_float(2), rstack(0));
	pop(1);
}

define_insn(FSTORE_3)
{
	/*
	 * ..., val -> ...
	 */
	check_stack_float(0);

	move_float(local_float(3), rstack(0));
	pop(1);
}

define_insn(DSTORE_0)
{
	/*
	 * ..., long val -> ...
	 */
	check_stack_double(0);

	move_double(local_double(0), rstack(0));
	pop(2);
}

define_insn(DSTORE_1)
{
	/*
	 * ..., long val -> ...
	 */
	check_stack_double(0);

	move_double(local_double(1), rstack(0));
	pop(2);
}

define_insn(DSTORE_2)
{
	/*
	 * ..., long val -> ...
	 */
	check_stack_double(0);

	move_double(local_double(2), rstack(0));
	pop(2);
}

define_insn(DSTORE_3)
{
	/*
	 * ..., long val -> ...
	 */
	check_stack_double(0);

	move_double(local_double(3), rstack(0));
	pop(2);
}

define_insn(ASTORE_0)
{
	/*
	 * ..., val -> ...
	 */
	check_stack_ref(0);

	move_ref(local(0), rstack(0));
	pop(1);
}

define_insn(ASTORE_1)
{
	/*
	 * ..., val -> ...
	 */
	check_stack_ref(0);

	move_ref(local(1), rstack(0));
	pop(1);
}

define_insn(ASTORE_2)
{
	/*
	 * ..., val -> ...
	 */
	check_stack_ref(0);

	move_ref(local(2), rstack(0));
	pop(1);
}

define_insn(ASTORE_3)
{
	/*
	 * ..., val -> ...
	 */
	check_stack_ref(0);

	move_ref(local(3), rstack(0));
	pop(1);
}

define_insn(IASTORE)
{
	/*
	 * ..., array ref, index, val -> ...
	 */
	check_stack_int(0);
	check_stack_int(1);
	check_stack_intarray(2);

	CHECK_NULL(IASTORE, rstack(2), 34);
	slot_alloctmp(tmp);

	/* Check we are within the array bounds */
	load_offset_int(tmp, rstack(2), object_array_length);

	cbranch_int_ult(rstack(1), tmp, reference_label(IASTORE, 18));
	softcall_badarrayindex();
	set_label(IASTORE, 18);

	lshl_int_const(rstack(1), rstack(1), SHIFT_jint);
	add_ref(rstack(1), rstack(2), rstack(1));
	store_offset_int(rstack(1), object_array_offset, rstack(0));

	pop(3);
}

define_insn(LASTORE)
{
	/*
	 * ..., array ref, index, long val -> ...
	 */
	check_stack_int(0);
	check_stack_int(1);
	check_stack_longarray(2);

	CHECK_NULL(LASTORE, rstack(3), 34);
	slot_alloctmp(tmp);

	/* Check we are within the array bounds */
	load_offset_int(tmp, rstack(3), object_array_length);

	cbranch_int_ult(rstack(2), tmp, reference_label(LASTORE, 19));
	softcall_badarrayindex();
	set_label(LASTORE, 19);

	lshl_int_const(tmp, rstack(2), SHIFT_jlong);
	if (object_array_offset != 0) {
		add_int_const(tmp, tmp, object_array_offset);
	}
	add_ref(tmp, rstack(3), tmp);
	store_long(tmp, rstack(0));

	pop(4);
}

define_insn(FASTORE)
{
	/*
	 * ..., array ref, index, float val -> ...
	 */
	check_stack_int(0);
	check_stack_int(1);
	check_stack_floatarray(2);

	CHECK_NULL(FASTORE, rstack(2), 34);
	slot_alloctmp(tmp);

	/* Check we are within the array bounds */
	load_offset_int(tmp, rstack(2), object_array_length);

	cbranch_int_ult(rstack(1), tmp, reference_label(FASTORE, 20));
	softcall_badarrayindex();
	set_label(FASTORE, 20);

	lshl_int_const(tmp, rstack(1), SHIFT_jfloat);
	if (object_array_offset != 0) {
		add_int_const(tmp, tmp, object_array_offset);
	}
	add_ref(tmp, rstack(2), tmp);
	store_float(tmp, rstack(0));

	pop(3);
}

define_insn(DASTORE)
{
	/*
	 * ..., array ref, index, double val -> ...
	 */
	check_stack_int(0);
	check_stack_int(1);
	check_stack_doublearray(2);

	CHECK_NULL(DASTORE, rstack(3), 34);
	slot_alloctmp(tmp);

	/* Check we are within the array bounds */
	load_offset_int(tmp, rstack(3), object_array_length);

	cbranch_int_ult(rstack(2), tmp, reference_label(DASTORE, 21));
	softcall_badarrayindex();
	set_label(DASTORE, 21);

	lshl_int_const(tmp, rstack(2), SHIFT_jdouble);
	if (object_array_offset != 0) {
		add_int_const(tmp, tmp, object_array_offset);
	}
	add_ref(tmp, rstack(3), tmp);
	store_double(tmp, rstack(0));

	pop(4);
}

define_insn(AASTORE)
{
	/*
	 * ..., array ref, index, val -> ...
	 */
	check_stack_int(0);
	check_stack_int(1);
	check_stack_refarray(2);

	CHECK_NULL(AASTORE, rstack(2), 34);
	slot_alloctmp(tmp);

	/* Check we are within the array bounds */
	load_offset_int(tmp, rstack(2), object_array_length);

	cbranch_int_ult(rstack(1), tmp, reference_label(AASTORE, 22));
	softcall_badarrayindex();
	set_label(AASTORE, 22);

	softcall_checkarraystore(rstack(2), rstack(0));

	SOFT_ADDREFERENCE(rstack(2), rstack(0));

	lshl_int_const(rstack(1), rstack(1), SHIFT_jref);
	add_ref(rstack(1), rstack(2), rstack(1));
	store_offset_ref(rstack(1), object_array_offset, rstack(0));

	pop(3);
}

define_insn(BASTORE)
{
	/*
	 * ..., array ref, index, byte value  -> ...
	 */
	check_stack_int(0);
	check_stack_int(1);
	check_stack_bytearray(2);

	CHECK_NULL(BASTORE, rstack(2), 34);
	slot_alloctmp(tmp);

	/* Check we are within the array bounds */
	load_offset_int(tmp, rstack(2), object_array_length);

	cbranch_int_ult(rstack(1), tmp, reference_label(BASTORE, 23));
	softcall_badarrayindex();
	set_label(BASTORE, 23);

	if (SHIFT_jbyte > 0) {
		lshl_int_const(tmp, rstack(1), SHIFT_jbyte);
	}
	else {
		move_int(tmp, rstack(1));
	}
	if (object_array_offset != 0) {
		add_int_const(tmp, tmp, object_array_offset);
	}
	add_ref(tmp, rstack(2), tmp);
	store_byte(tmp, rstack(0));

	pop(3);
}

define_insn(CASTORE)
{
	/*
	 * ..., array ref, index, char value  -> ...
	 */
	check_stack_int(0);
	check_stack_int(1);
	check_stack_chararray(2);

	CHECK_NULL(CASTORE, rstack(2), 34);
	slot_alloctmp(tmp);

	/* Check we are within the array bounds */
	load_offset_int(tmp, rstack(2), object_array_length);

	cbranch_int_ult(rstack(1), tmp, reference_label(CASTORE, 24));
	softcall_badarrayindex();
	set_label(CASTORE, 24);

	lshl_int_const(tmp, rstack(1), SHIFT_jchar);
	if (object_array_offset != 0) {
		add_int_const(tmp, tmp, object_array_offset);
	}
	add_ref(tmp, rstack(2), tmp);
	store_char(tmp, rstack(0));

	pop(3);
}

define_insn(SASTORE)
{
	/*
	 * ..., array ref, index, short value  -> ...
	 */
	check_stack_int(0);
	check_stack_int(1);
	check_stack_shortarray(2);

	CHECK_NULL(SASTORE, rstack(2), 34);
	slot_alloctmp(tmp);

	/* Check we are within the array bounds */
	load_offset_int(tmp, rstack(2), object_array_length);

	cbranch_int_ult(rstack(1), tmp, reference_label(SASTORE, 25));
	softcall_badarrayindex();
	set_label(SASTORE, 25);

	lshl_int_const(tmp, rstack(1), SHIFT_jshort);
	if (object_array_offset != 0) {
		add_int_const(tmp, tmp, object_array_offset);
	}
	add_ref(tmp, rstack(2), tmp);
	store_short(tmp, rstack(0));

	pop(3);
}

define_insn(POP)
{
	pop(1);
}

define_insn(POP2)
{
	pop(2);
}

define_insn(DUP)
{
	push(1);
	move_any(wstack(0), rstack(1));
}

define_insn(DUP_X1)
{
	push(1);
	move_any(stack(0), stack(1));
	move_any(stack(1), stack(2));
	move_any(stack(2), stack(0));
}

define_insn(DUP_X2)
{
	push(1);
	move_any(stack(0), stack(1));
	move_any(stack(1), stack(2));
	move_any(stack(2), stack(3));
	move_any(stack(3), stack(0));
}

define_insn(DUP2)
{
	push(2);
	move_any(stack(0), stack(2));
	move_any(stack(1), stack(3));
}

define_insn(DUP2_X1)
{
	push(2);
	move_any(stack(0), stack(2));
	move_any(stack(1), stack(3));
	move_any(stack(2), stack(4));
	move_any(stack(3), stack(0));
	move_any(stack(4), stack(1));
}

define_insn(DUP2_X2)
{
	push(2);
	move_any(stack(0), stack(2));
	move_any(stack(1), stack(3));
	move_any(stack(2), stack(4));
	move_any(stack(3), stack(5));
	move_any(stack(4), stack(0));
	move_any(stack(5), stack(1));
}

define_insn(SWAP)
{
        /*
         * ..., val1, val2 -> ..., val2, val1
         */
	swap_any(stack(0), stack(1));
}

define_insn(IADD)
{
        /*
         * ..., val1, val2 -> ..., val1+val2
         */
	check_stack_int(0);
	check_stack_int(1);

	add_int(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(LADD)
{
        /*
         * ..., long val1, long val2 -> ..., long val1+val2
         */
	check_stack_long(0);
	check_stack_long(2);

	add_long(wstack(2), rstack(2), rstack(0));
	pop(2);
}

define_insn(FADD)
{
        /*
         * ..., val1, val2 -> ..., val1+val2
         */
	check_stack_float(0);
	check_stack_float(1);

	add_float(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(DADD)
{
        /*
         * ..., double val1, long val2 -> ..., double val1+val2
         */
	check_stack_double(0);
	check_stack_double(2);

	add_double(wstack(2), rstack(2), rstack(0));
	pop(2);
}

define_insn(ISUB)
{
        /*
         * ..., val1, val2 -> ..., val1-val2
         */
	check_stack_int(0);
	check_stack_int(1);

	sub_int(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(LSUB)
{
        /*
         * ..., long val1, long val2 -> ..., long val1-val2
         */
	check_stack_long(0);
	check_stack_long(2);

	sub_long(wstack(2), rstack(2), rstack(0));
	pop(2);
}

define_insn(FSUB)
{
        /*
         * ..., val1, val2 -> ..., val1-val2
         */
	check_stack_float(0);
	check_stack_float(1);

	sub_float(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(DSUB)
{
        /*
         * ..., double val1, long val2 -> ..., double val1-val2
         */
	check_stack_double(0);
	check_stack_double(2);

	sub_double(wstack(2), rstack(2), rstack(0));
	pop(2);
}

define_insn(IMUL)
{
        /*
         * ..., val1, val2 -> ..., val1*val2
         */
	check_stack_int(0);
	check_stack_int(1);

	mul_int(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(LMUL)
{
        /*
         * ..., long val1, long val2 -> ..., long val1*val2
         */
	check_stack_long(0);
	check_stack_long(2);

	mul_long(wstack(2), rstack(2), rstack(0));
	pop(2);
}

define_insn(FMUL)
{
        /*
         * ..., val1, val2 -> ..., val1*val2
         */
	check_stack_float(0);
	check_stack_float(1);

	mul_float(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(DMUL)
{
        /*
         * ..., double val1, long val2 -> ..., double val1*val2
         */
	check_stack_double(0);
	check_stack_double(2);

	mul_double(wstack(2), rstack(2), rstack(0));
	pop(2);
}

define_insn(IDIV)
{
        /*
         * ..., val1, val2 -> ..., val1/val2
         */
	check_stack_int(0);
	check_stack_int(1);

	div_int(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(LDIV)
{
        /*
         * ..., long val1, long val2 -> ..., long val1/val2
         */
	check_stack_long(0);
	check_stack_long(2);

	div_long(wstack(2), rstack(2), rstack(0));
	pop(2);
}

define_insn(FDIV)
{
        /*
         * ..., val1, val2 -> ..., val1/val2
         */
	check_stack_float(0);
	check_stack_float(1);

	div_float(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(DDIV)
{
        /*
         * ..., val1, val2 -> ..., val1/val2
         */
	check_stack_double(0);
	check_stack_double(2);

	div_double(wstack(2), rstack(2), rstack(0));
	pop(2);
}

define_insn(IREM)
{
        /*
         * ..., val1, val2 -> ..., val1%val2
         */
	check_stack_int(0);
	check_stack_int(1);

	rem_int(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(LREM)
{
        /*
         * ..., long val1, long val2 -> ..., long val1%val2
         */
	check_stack_long(0);
	check_stack_long(2);

	rem_long(wstack(2), rstack(2), rstack(0));
	pop(2);
}

define_insn(FREM)
{
        /*
         * ..., val1, val2 -> ..., val1%val2
         */
	check_stack_float(0);
	check_stack_float(1);

	rem_float(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(DREM)
{
        /*
         * ..., val1, val2 -> ..., val1%val2
         */
	check_stack_double(0);
	check_stack_double(2);

	rem_double(wstack(2), rstack(2), rstack(0));
	pop(2);
}

define_insn(INEG)
{
	check_stack_int(0);

	neg_int(wstack(0), rstack(0));
}

define_insn(LNEG)
{
	check_stack_long(0);

	neg_long(wstack(0), rstack(0));
}

define_insn(FNEG)
{
	check_stack_float(0);

	neg_float(wstack(0), rstack(0));
}

define_insn(DNEG)
{
	check_stack_double(0);

	neg_double(wstack(0), rstack(0));
}

define_insn(ISHL)
{
	/*
	 * ..., val1, val2 -> ... val1 << val2
	 */
	check_stack_int(0);
	check_stack_int(1);

	lshl_int(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(LSHL)
{
	/*
	 * ..., long val1, val2 -> ... long val1 << val2
	 */
	check_stack_int(0);
	check_stack_long(1);

	lshl_long(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(ISHR)
{
	/*
	 * ..., val1, val2 -> ... val1 >> val2
	 */
	check_stack_int(0);
	check_stack_int(1);

	ashr_int(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(LSHR)
{
	/*
	 * ..., long val1, val2 -> ... long val1 >> val2
	 */
	check_stack_int(0);
	check_stack_long(1);

	ashr_long(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(IUSHR)
{
	/*
	 * ..., val1, val2 -> ... val1 >> val2
	 */
	check_stack_int(0);
	check_stack_int(1);

	lshr_int(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(LUSHR)
{
	/*
	 * ..., long val1, val2 -> ... long val1 >> val2
	 */
	check_stack_int(0);
	check_stack_long(1);

	lshr_long(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(IAND)
{
        /*
         * ..., val1, val2 -> ..., val1 & val2
         */
	check_stack_int(0);
	check_stack_int(1);

	and_int(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(LAND)
{
        /*
         * ..., long val1, long val2 -> ..., long val1 & val2
         */
	check_stack_long(0);
	check_stack_long(2);

	and_long(wstack(2), rstack(2), rstack(0));
	pop(2);
}

define_insn(IOR)
{
        /*
         * ..., val1, val2 -> ..., val1 | val2
         */
	check_stack_int(0);
	check_stack_int(1);

	or_int(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(LOR)
{
        /*
         * ..., long val1, long val2 -> ..., long val1 | val2
         */
	check_stack_long(0);
	check_stack_long(2);

	or_long(wstack(2), rstack(2), rstack(0));
	pop(2);
}

define_insn(IXOR)
{
        /*
         * ..., val1, val2 -> ..., val1 ^ val2
         */
	check_stack_int(0);
	check_stack_int(1);

	xor_int(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(LXOR)
{
        /*
         * ..., long val1, long val2 -> ..., long val1 ^ val2
         */
	check_stack_long(0);
	check_stack_long(2);

	xor_long(wstack(2), rstack(2), rstack(0));
	pop(2);
}

define_insn(IINC)
{
	if (wide) {
		check_pcidx (0);
		check_pcidx (2);
		idx = (uint16)((getpc(0) << 8) | getpc(1));
		low = (int16)((getpc(2) << 8) | getpc(3));
	}
	else {
		check_pc (0);
		check_pc (1);
		idx = (uint8)getpc(0);
		low = (int8)getpc(1);

	}
	wide = 0;

	check_local_int(idx);

	add_int_const(local(idx), local(idx), low);
}

define_insn(I2L)
{
	check_stack_int(0);

	push(1);
	cvt_int_long(wstack(0), rstack(1));
}

define_insn(I2F)
{
	check_stack_int(0);

	cvt_int_float(wstack(0), rstack(0));
}

define_insn(I2D)
{
	check_stack_int(0);

	push(1);
	cvt_int_double(wstack(0), rstack(1));
}

define_insn(L2I)
{
	check_stack_long(0);

	cvt_long_int(wstack(1), rstack(0));
	pop(1);
}

define_insn(L2F)
{
	check_stack_long(0);

	cvt_long_float(wstack(1), rstack(0));
	pop(1);
}

define_insn(L2D)
{
	check_stack_long(0);

	cvt_long_double(wstack(0), rstack(0));
}

define_insn(F2I)
{
	check_stack_float(0);

	cvt_float_int(wstack(0), rstack(0));
}

define_insn(F2L)
{
	check_stack_float(0);

	push(1);
	cvt_float_long(wstack(0), rstack(1));
}

define_insn(F2D)
{
	check_stack_float(0);

	push(1);
	cvt_float_double(wstack(0), rstack(1));
}

define_insn(D2I)
{
	check_stack_double(0);

	cvt_double_int(wstack(1), rstack(0));
	pop(1);
}

define_insn(D2L)
{
	check_stack_double(0);

	cvt_double_long(wstack(0), rstack(0));
}

define_insn(D2F)
{
	check_stack_double(0);

	cvt_double_float(wstack(1), rstack(0));
	pop(1);
}

define_insn(INT2BYTE)
{
	check_stack_int(0);

	cvt_int_byte(wstack(0), rstack(0));
}

define_insn(INT2CHAR)
{
	check_stack_int(0);

	cvt_int_char(wstack(0), rstack(0));
}

define_insn(INT2SHORT)
{
	check_stack_int(0);

	cvt_int_short(wstack(0), rstack(0));
}

define_insn(LCMP)
{
	/*
	 * ..., long val1, long val2 -> ..., result
	 */
	check_stack_long(0);
	check_stack_long(2);

	lcmp(wstack(3), rstack(0), rstack(2));
	pop(3);
}

define_insn(FCMPL)
{
	/*
	 * ..., float val1, float val2 -> ..., result
	 */
	check_stack_float(0);
	check_stack_float(1);

	cmpl_float(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(FCMPG)
{
	/*
	 * ..., float val1, float val2 -> ..., result
	 */
	check_stack_float(0);
	check_stack_float(1);

	cmpg_float(wstack(1), rstack(1), rstack(0));
	pop(1);
}

define_insn(DCMPL)
{
	/*
	 * ..., double val1, double val2 -> ..., result
	 */
	check_stack_double(0);
	check_stack_double(2);

	cmpl_double(wstack(3), rstack(2), rstack(0));
	pop(3);
}

define_insn(DCMPG)
{
	/*
	 * ..., double val1, double val2 -> ..., result
	 */
	check_stack_double(0);
	check_stack_double(2);

	cmpg_double(wstack(3), rstack(2), rstack(0));
	pop(3);
}

define_insn(IFEQ)
{
	check_pcidx (0);
	check_stack_int(0);

	idx = (int16)((getpc(0) << 8) | getpc(1));

	slot_nowriteback(rstack(0));
	end_basic_block();

	cbranch_int_const_eq(rstack(0), 0, reference_code_label(pc+idx));

	pop(1);
}

define_insn(IFNE)
{
	check_pcidx (0);
	check_stack_int(0);

	idx = (int16)((getpc(0) << 8) | getpc(1));

	slot_nowriteback(rstack(0));
	end_basic_block();

	cbranch_int_const_ne(rstack(0), 0, reference_code_label(pc+idx));

	pop(1);
}

define_insn(IFLT)
{
	check_pcidx (0);
	check_stack_int(0);

	idx = (int16)((getpc(0) << 8) | getpc(1));

	slot_nowriteback(rstack(0));
	end_basic_block();

	cbranch_int_const_lt(rstack(0), 0, reference_code_label(pc+idx));

	pop(1);
}

define_insn(IFGE)
{
	check_pcidx (0);
	check_stack_int(0);

	idx = (int16)((getpc(0) << 8) | getpc(1));

	slot_nowriteback(rstack(0));
	end_basic_block();

	cbranch_int_const_ge(rstack(0), 0, reference_code_label(pc+idx));

	pop(1);
}

define_insn(IFGT)
{
	check_pcidx (0);
	check_stack_int(0);

	idx = (int16)((getpc(0) << 8) | getpc(1));

	slot_nowriteback(rstack(0));
	end_basic_block();

	cbranch_int_const_gt(rstack(0), 0, reference_code_label(pc+idx));

	pop(1);
}

define_insn(IFLE)
{
	check_pcidx (0);
	check_stack_int(0);

	idx = (int16)((getpc(0) << 8) | getpc(1));

	slot_nowriteback(rstack(0));
	end_basic_block();

	cbranch_int_const_le(rstack(0), 0, reference_code_label(pc+idx));

	pop(1);
}

define_insn(IF_ICMPEQ)
{
	check_pcidx (0);
	check_stack_int(0);
	check_stack_int(1);

	idx = (int16)((getpc(0) << 8) | getpc(1));

	slot_nowriteback(rstack(0));
	slot_nowriteback(rstack(1));
	end_basic_block();

	cbranch_int_eq(rstack(1), rstack(0), reference_code_label(pc+idx));

	pop(2);
}

define_insn(IF_ICMPNE)
{
	check_pcidx (0);
	check_stack_int(0);
	check_stack_int(1);

	idx = (int16)((getpc(0) << 8) | getpc(1));

	slot_nowriteback(rstack(0));
	slot_nowriteback(rstack(1));
	end_basic_block();

	cbranch_int_ne(rstack(1), rstack(0), reference_code_label(pc+idx));

	pop(2);
}

define_insn(IF_ICMPLT)
{
	check_pcidx (0);
	check_stack_int(0);
	check_stack_int(1);

	idx = (int16)((getpc(0) << 8) | getpc(1));

	slot_nowriteback(rstack(0));
	slot_nowriteback(rstack(1));
	end_basic_block();

	cbranch_int_lt(rstack(1), rstack(0), reference_code_label(pc+idx));

	pop(2);
}

define_insn(IF_ICMPGE)
{
	check_pcidx (0);
	check_stack_int(0);
	check_stack_int(1);

	idx = (int16)((getpc(0) << 8) | getpc(1));

	slot_nowriteback(rstack(0));
	slot_nowriteback(rstack(1));
	end_basic_block();

	cbranch_int_ge(rstack(1), rstack(0), reference_code_label(pc+idx));

	pop(2);
}

define_insn(IF_ICMPGT)
{
	check_pcidx (0);
	check_stack_int(0);
	check_stack_int(1);

	idx = (int16)((getpc(0) << 8) | getpc(1));

	slot_nowriteback(rstack(0));
	slot_nowriteback(rstack(1));
	end_basic_block();

	cbranch_int_gt(rstack(1), rstack(0), reference_code_label(pc+idx));

	pop(2);
}

define_insn(IF_ICMPLE)
{
	check_pcidx (0);
	check_stack_int(0);
	check_stack_int(1);

	idx = (int16)((getpc(0) << 8) | getpc(1));

	slot_nowriteback(rstack(0));
	slot_nowriteback(rstack(1));
	end_basic_block();

	cbranch_int_le(rstack(1), rstack(0), reference_code_label(pc+idx));

	pop(2);
}

define_insn(IF_ACMPEQ)
{
	check_pcidx (0);
	check_stack_ref(0);
	check_stack_ref(1);

	idx = (int16)((getpc(0) << 8) | getpc(1));

	slot_nowriteback(rstack(0));
	slot_nowriteback(rstack(1));
	end_basic_block();

	cbranch_int_eq(rstack(1), rstack(0), reference_code_label(pc+idx));

	pop(2);
}

define_insn(IF_ACMPNE)
{
	check_pcidx (0);
	check_stack_ref(0);
	check_stack_ref(1);

	idx = (int16)((getpc(0) << 8) | getpc(1));

	slot_nowriteback(rstack(0));
	slot_nowriteback(rstack(1));
	end_basic_block();

	cbranch_ref_ne(rstack(1), rstack(0), reference_code_label(pc+idx));

	pop(2);
}

define_insn(GOTO)
{
	check_pcidx (0);

	idx = (int16)((getpc(0) << 8) | getpc(1));

	end_basic_block();
	branch_a(reference_code_label(pc+idx));
}

define_insn(JSR)
{
	/*
	 * ... -> ..., ret-addr
	 */
	check_pcidx (0);

	idx = (int16)((getpc(0) << 8) | getpc(1));

	push(1);
	move_label_const(wstack(0), reference_code_label(npc));
	end_basic_block();
	branch_a(reference_code_label(pc+idx));
}

define_insn(RET)
{
	check_pc (0);

	idx = (uint8)getpc(0);

	check_local_ref(idx);

	end_basic_block();
	branch_indirect(stored_code_label(local(idx)));
}

define_insn(TABLESWITCH)
{
	/*
	 * ..., index -> ...
	 */
	check_stack_int(0);

	slot_alloctmp(tmp2);

	npc = (pc + 1 + 3) & -4;
	low = (int32)((getcode(npc+4) << 24) | (getcode(npc+5) << 16) |
				(getcode(npc+6) << 8) | getcode(npc+7));
	high = (int32)((getcode(npc+8) << 24) | (getcode(npc+9) << 16) |
				(getcode(npc+10) << 8) | getcode(npc+11));
	npc = npc + 12;

	end_basic_block();
	cbranch_int_const_lt(stack(0), low, reference_label(TABLESWITCH, 8));
	if (low != 0) {
		start_basic_block();
		sub_int_const(stack(0), stack(0), low);
		end_basic_block();
	}
	cbranch_int_const_le(stack(0), high-low, reference_label(TABLESWITCH, 7));

	start_basic_block();
	set_label(TABLESWITCH, 8);
	move_int_const(stack(0), -3); /* Position at default entry */
	end_basic_block();

	start_basic_block();
	set_label(TABLESWITCH, 7);
	lshl_int_const(stack(0), stack(0), switchtable_shift);
	move_label_const(tmp2, reference_table_label(9));
	add_ref(stack(0), tmp2, stack(0));
	load_code_ref(stack(0), stack(0));
	end_basic_block();
	branch_indirect(table_code_label(stack(0)));
	pop(1);

#if defined(TRANSLATOR)
	{
		build_code_ref(&getcode(npc-12), pc);	/* Default entry */
		build_code_ref(&getcode(npc-12), pc);	/* Dummy */
		build_code_ref(&getcode(npc-12), pc);	/* Dummy */
		set_label(TABLESWITCH, 9);
		for (idx = 0; idx < high-low+1; idx++) {
			build_code_ref(&getcode(npc + (idx << switchtable_shift)), pc);
		}
	}
#endif
	adjustpc((4 - (pc % 4)) + 12 + (high - low + 1) * 4);
}

define_insn(LOOKUPSWITCH)
{
	/*
	 * ..., key -> ...
	 */
	check_stack_int(0);

	npc = (pc + 1 + 3) & -4;
	idx = (int32)((getcode(npc+4) << 24) | (getcode(npc+5) << 16) |
				(getcode(npc+6) << 8) | getcode(npc+7));

	slot_alloctmp(mtable);
	slot_alloctmp(tmp);
	slot_alloctmp(tmp2);

	move_label_const(tmp2, reference_table_label(7));
	move_ref(tmp, tmp2);
	add_ref_const(tmp, tmp, idx * switchpair_size);

	end_basic_block();
	set_label(LOOKUPSWITCH, 5);
	start_basic_block();
	load_key(mtable, tmp);
	end_basic_block();
	cbranch_int_eq(mtable, rstack(0), reference_label(LOOKUPSWITCH, 6));

	start_basic_block();
	add_ref_const(tmp, tmp, -switchpair_size);
	end_basic_block();
	cbranch_ref_ne(tmp, tmp2, reference_label(LOOKUPSWITCH, 5));

	start_basic_block();
	add_ref_const(tmp, tmp, -switchpair_addr);
	end_basic_block();

	set_label(LOOKUPSWITCH, 6);
	start_basic_block();
	add_ref_const(tmp, tmp, switchpair_addr);
	load_code_ref(tmp, tmp);
	end_basic_block();
	branch_indirect(table_code_label(tmp));
	pop(1);

#if defined(TRANSLATOR)
	{
		set_label(LOOKUPSWITCH, 7);
		build_code_ref(&getcode(npc), pc);
		build_key(&getcode(npc)); /* Dummy key */
		for (low = 1; low <= idx; low++) {
			build_key(&getcode(npc + (low * switchpair_size)));
			build_code_ref(&getcode(npc + (low * switchpair_size) + switchpair_addr), pc);
		}
	}
#endif
	adjustpc((4 - (pc % 4)) + (idx + 1) * 8);
}

define_insn(IRETURN)
{
	check_stack_int(0);

	monitor_exit();
	returnarg_int(rstack(0));
	end_function();
	ret();
}

define_insn(LRETURN)
{
	check_stack_long(0);

	monitor_exit();
	returnarg_long(rstack(0));
	end_function();
	ret();
}

define_insn(FRETURN)
{
	check_stack_float(0);

	monitor_exit();
	returnarg_float(rstack(0));
	end_function();
	ret();
}

define_insn(DRETURN)
{
	check_stack_double(0);

	monitor_exit();
	returnarg_double(rstack(0));
	end_function();
	ret();
}

define_insn(ARETURN)
{
	check_stack_ref(0);

	monitor_exit();
	returnarg_ref(rstack(0));
	end_function();
	ret();
}

define_insn(RETURN)
{
	monitor_exit();
	end_function();
	ret();
}

/*
 * This macro initialises the static data associated with a class.
 */
#define	INIT_STATIC_CLASS(c)				\
	if (c != 0 && c->state != CSTATE_OK) {		\
		softcall_initialise_class(c);		\
	}

define_insn(GETSTATIC)
{
	/*
	 * ... -> ..., value
	 */
	check_pcidx (0);

	idx = (uint16)((getpc(0) << 8) | getpc(1));

	get_static_field_info(idx);
	INIT_STATIC_CLASS(field_class());
	slot_alloctmp(tmp);
	move_ref_const(tmp, FIELD_ADDRESS(field));

	/* Use the proper load to increase the chances of the translator
	   getting data in the right type of register.  */

	if (FIELD_ISREF(field)) {
		push(1);
		load_ref(wstack(0), tmp);
	}
	else switch (CLASS_PRIM_SIG(FIELD_TYPE(field))) {
	case 'I':
		push(1);
		load_int(wstack(0), tmp);
		break;
	case 'S':
		push(1);
		load_short(wstack(0), tmp);
		break;
	case 'B':
	case 'Z':
		push(1);
		load_byte(wstack(0), tmp);
		break;
	case 'C':
		push(1);
		load_char(wstack(0), tmp);
		break;
	case 'F':
		push(1);
		load_float(wstack(0), tmp);
		break;
	case 'J':
		push(2);
		load_long(wstack(0), tmp);
		break;
	case 'D':
		push(2);
		load_double(wstack(0), tmp);
		break;
	default:
		ABORT();
	}
}

define_insn(PUTSTATIC)
{
	/*
	 * ..., value  -> ...
	 */
	check_pcidx (0);

	idx = (uint16)((getpc(0) << 8) | getpc(1));
	slot_alloctmp(tmp);

	get_static_field_info(idx);
	INIT_STATIC_CLASS(field_class());
	move_ref_const(tmp, FIELD_ADDRESS(field));

	if (FIELD_ISREF(field)) {
		SOFT_ADDREFERENCE_STATIC(field_statics(), rstack(0));
		store_ref(tmp, rstack(0));
		pop(1);
	}
	else switch (CLASS_PRIM_SIG(FIELD_TYPE(field))) {
	case 'I':
		store_int(tmp, rstack(0));
		pop(1);
		break;
	case 'S':
		store_short(tmp, rstack(0));
		pop(1);
		break;
	case 'B':
	case 'Z':
		store_byte(tmp, rstack(0));
		pop(1);
		break;
	case 'C':
		store_char(tmp, rstack(0));
		pop(1);
		break;
	case 'F':
		store_float(tmp, rstack(0));
		pop(1);
		break;
	case 'J':
		store_long(tmp, rstack(0));
		pop(2);
		break;
	case 'D':
		store_double(tmp, rstack(0));
		pop(2);
		break;
	default:
		ABORT();
	}
}

define_insn(GETFIELD)
{
	/*
	 * ..., obj-ref -> ..., value
	 */
	check_pcidx (0);
	check_stack_ref(0);

	CHECK_NULL(GETFIELD, rstack(0), 34);

	idx = (uint16)((getpc(0) << 8) | getpc(1));

	get_field_info(idx);

	/* Use the proper load to increase the chances of the translator
	   getting data in the right type of register.  */

	if (FIELD_ISREF(field)) {
		load_offset_ref(wstack(0), rstack(0), FIELD_OFFSET(field));
	}
	else switch (CLASS_PRIM_SIG(FIELD_TYPE(field))) {
	case 'I':
		load_offset_int(wstack(0), rstack(0), FIELD_OFFSET(field));
		break;
	case 'S':
		add_ref_const(stack(0), stack(0), FIELD_OFFSET(field));
		load_short(stack(0), stack(0));
		break;
	case 'B':
	case 'Z':
		add_ref_const(stack(0), stack(0), FIELD_OFFSET(field));
		load_byte(stack(0), stack(0));
		break;
	case 'C':
		add_ref_const(stack(0), stack(0), FIELD_OFFSET(field));
		load_char(stack(0), stack(0));
		break;
	case 'F':
		add_ref_const(stack(0), stack(0), FIELD_OFFSET(field));
		load_float(stack(0), stack(0));
		break;
	case 'J':
		push(1);
		add_ref_const(stack(0), stack(1), FIELD_OFFSET(field));
		load_long(stack(0), stack(0));
		break;
	case 'D':
		push(1);
		add_ref_const(stack(0), stack(1), FIELD_OFFSET(field));
		load_double(stack(0), stack(0));
		break;

	default:
		ABORT();
	}
}

define_insn(PUTFIELD)
{
	/*
	 * ..., obj-ref, value  -> ...
	 */
	check_pcidx (0);

	idx = (uint16)((getpc(0) << 8) | getpc(1));

	get_field_info(idx);

	/* Use the proper store to increase the chances of the translator
	   having the data in the right type of register already.  */

	if (FIELD_ISREF(field)) {
		check_stack_ref(1);
		CHECK_NULL(PUTFIELD, rstack(1), 35);
		SOFT_ADDREFERENCE(rstack(1), rstack(0));
		store_offset_ref(wstack(1), FIELD_OFFSET(field), rstack(0));
		pop(2);
	}
	else switch (CLASS_PRIM_SIG(FIELD_TYPE(field))) {
	case 'I':
		check_stack_ref(1);
		CHECK_NULL(PUTFIELD, rstack(1), 36);
		store_offset_int(wstack(1), FIELD_OFFSET(field), rstack(0));
		pop(2);
		break;
	case 'S':
		check_stack_ref(1);
		CHECK_NULL(PUTFIELD, stack(1), 37);
		add_ref_const(stack(1), stack(1), FIELD_OFFSET(field));
		store_short(stack(1), stack(0));
		pop(2);
		break;
	case 'B':
	case 'Z':
		check_stack_ref(1);
		CHECK_NULL(PUTFIELD, stack(1), 37);
		add_ref_const(stack(1), stack(1), FIELD_OFFSET(field));
		store_byte(stack(1), stack(0));
		pop(2);
		break;
	case 'C':
		check_stack_ref(1);
		CHECK_NULL(PUTFIELD, stack(1), 37);
		add_ref_const(stack(1), stack(1), FIELD_OFFSET(field));
		store_char(stack(1), stack(0));
		pop(2);
		break;
	case 'F':
		check_stack_ref(1);
		CHECK_NULL(PUTFIELD, stack(1), 37);
		add_ref_const(stack(1), stack(1), FIELD_OFFSET(field));
		store_float(stack(1), stack(0));
		pop(2);
		break;
	case 'J':
		check_stack_ref(2);
		CHECK_NULL(PUTFIELD, stack(2), 38);
		add_ref_const(stack(2), stack(2), FIELD_OFFSET(field));
		store_long(stack(2), stack(0));
		pop(3);
		break;
	case 'D':
		check_stack_ref(2);
		CHECK_NULL(PUTFIELD, stack(2), 39);
		add_ref_const(stack(2), stack(2), FIELD_OFFSET(field));
		store_double(stack(2), stack(0));
		pop(3);
		break;
	default:
		ABORT();
	}
}

#if defined(INTERPRETER)
#define METHOD_PUSHARGS(IDX) /* do nothing */
#else
#define	METHOD_PUSHARGS(IDX)						\
do {									\
	/* Yeah, this is used several places, and we wind up with	\
	   multiple static variables, but so what?  */			\
	struct pusharg_info {						\
		struct pusharg_info *next, *prev;			\
		char type;						\
		int sp_idx, arg_idx;					\
	};								\
	static struct pusharg_info *firstArg, *lastArg;			\
	struct pusharg_info *a = firstArg;				\
	char *ptr = method_sig()->data + 1;				\
	int arg_idx = (IDX); 						\
	assert(ptr[-1] == '(');						\
	for (low = idx-1; *ptr != ')'; --low, ++ptr, a = a->next) {	\
		if (a == NULL) {					\
			a = gc_malloc_fixed(sizeof(*a));		\
			if ((a->prev = lastArg) != NULL)		\
				lastArg->next = a;			\
			else						\
				firstArg = a;				\
			lastArg = a;					\
			a->next = NULL;					\
		}							\
		a->sp_idx = low;	/* the common case  */		\
		a->arg_idx = arg_idx++;					\
		switch (*ptr) {						\
		case '[':						\
			while (*++ptr == '[') continue;			\
			if (*ptr == 'L') {				\
		case 'L':						\
				ptr = strchr(ptr, ';');			\
			}						\
			a->type = 'r';					\
			break;						\
		case 'I':						\
		case 'Z':						\
		case 'S':						\
		case 'B':						\
		case 'C':						\
			a->type = 'i';					\
			break;						\
		case 'F':						\
			a->type = 'f';					\
			break;						\
		case 'J':						\
			a->type = 'l';					\
			low--;						\
			a->sp_idx = low;				\
			arg_idx += pusharg_long_idx_inc - 1;		\
			break;						\
		case 'D':						\
			a->type = 'd';					\
			low--;						\
			a->sp_idx = low;				\
			arg_idx += pusharg_long_idx_inc - 1;		\
			break;						\
		case 'V':						\
		default:						\
			ABORT();					\
		}							\
	}								\
	low = ptr[1];	/* remember the return value */			\
	a = (a ? a->prev : lastArg);					\
	while (a) {							\
		switch (a->type) {					\
		case 'r':						\
			pusharg_ref(rstack(a->sp_idx), a->arg_idx);	\
			break;						\
		case 'i':						\
			pusharg_int(rstack(a->sp_idx), a->arg_idx);	\
			break;						\
		case 'l':						\
			pusharg_long(rstack(a->sp_idx), a->arg_idx);	\
			break;						\
		case 'f':						\
			pusharg_float(rstack(a->sp_idx), a->arg_idx);	\
			break;						\
		case 'd':						\
			pusharg_double(rstack(a->sp_idx), a->arg_idx);	\
			break;						\
		}							\
		a = a->prev;						\
	}								\
} while (0)
#endif

#define	METHOD_RETURN_VALUE()					\
do {								\
	/* Store the return type (if necessary) */		\
	switch (low) {						\
	case 'V':						\
		pop(idx);					\
		break;						\
	case 'L':						\
	case '[':						\
		pop(idx - 1);					\
		return_ref(wstack(0));				\
		break;						\
	case 'I':						\
	case 'Z':						\
	case 'S':						\
	case 'B':						\
	case 'C':						\
		pop(idx - 1);					\
		return_int(wstack(0));				\
		break;						\
	case 'F':						\
		pop(idx - 1);					\
		return_float(wstack(0));			\
		break;						\
	case 'J':						\
		pop(idx - 2);					\
		return_long(wstack(0));				\
		break;						\
	case 'D':						\
		pop(idx - 2);					\
		return_double(wstack(0));			\
		break;						\
	default:						\
		ABORT();					\
	}							\
} while (0)

define_insn(INVOKEVIRTUAL)
{
	/*
	 * ..., obj, ..args.., -> ...
	 */
	check_pcidx (0);

	idx = (uint16)((getpc(0) << 8) | getpc(1));

	slot_alloctmp(tmp);
	slot_alloctmp(mtable);

	get_method_info(idx);
	idx = method_nargs();

	CHECK_NULL(INVOKEVIRTUAL, rstack(idx), 34);

	check_stack_ref(idx);

	/* Find dispatch table in object */
	load_offset_ref(mtable, rstack(idx), method_dtable_offset);

	/* Check method table for cached entry */
	load_offset_ref(tmp, mtable,
			DTABLE_METHODOFFSET + method_idx() * DTABLE_METHODSIZE);
#if !(defined(TRANSLATOR) && defined(HAVE_TRAMPOLINE))
	softcall_get_method_code (tmp, tmp);
#endif

	/* Push arguments */
	METHOD_PUSHARGS(1);
	/* Push object */
	pusharg_ref(rstack(idx), 0);

	slot_nowriteback(tmp);
	end_basic_block();

	/* Call it */
	low = method_returntype();
	call(tmp);

	/* Pop args */
	idx++;
	popargs();

	start_basic_block();
	METHOD_RETURN_VALUE();
}

define_insn(INVOKESPECIAL)
{
	/*
	 * ..., obj, ..args.., -> ...
	 */
	check_pcidx (0);

	idx = (uint16)((getpc(0) << 8) | getpc(1));

	slot_alloctmp(mtable);

	get_method_info(idx);
	idx = method_nargs();

	CHECK_NULL(INVOKESPECIAL, rstack(idx), 34);

	check_stack_ref(idx);

#if !(defined(TRANSLATOR) && defined(HAVE_TRAMPOLINE))
	slot_alloctmp(tmp);
	softcall_get_method_code_const (tmp, method_method ());
#endif

	/* Push arguments */
	METHOD_PUSHARGS(1);
	/* Push object */
	pusharg_ref(rstack(idx), 0);

#if !(defined(TRANSLATOR) && defined(HAVE_TRAMPOLINE))
	slot_nowriteback(tmp);
#endif
	end_basic_block();

	/* Call it */
	low = method_returntype();

#if defined(TRANSLATOR) && defined(HAVE_TRAMPOLINE)
	call_indirect_const(&METHOD_NATIVECODE(method_method()));
#else
	call(tmp);
#endif

	/* Pop args */
	idx++;
	popargs();

	start_basic_block();
	METHOD_RETURN_VALUE();
}

define_insn(INVOKESTATIC)
{
	/*
	 * ..., ..args.., -> ...
	 */
	check_pcidx (0);

	idx = (uint16)((getpc(0) << 8) | getpc(1));

	slot_alloctmp(mtable);

	get_method_info(idx);
	idx = method_nargs();

#if !(defined(TRANSLATOR) && defined(HAVE_TRAMPOLINE))
	slot_alloctmp(tmp);
	softcall_get_method_code_const (tmp, method_method ());
#endif

	/* Push arguments */
	METHOD_PUSHARGS(0);

#if !(defined(TRANSLATOR) && defined(HAVE_TRAMPOLINE))
	slot_nowriteback(tmp);
#endif
	end_basic_block();

	/* Call it */
	low = method_returntype();

#if defined(TRANSLATOR) && defined(HAVE_TRAMPOLINE)
	call_indirect_const(&METHOD_NATIVECODE(method_method()));
#else
	call(tmp);
#endif

	/* Pop args */
	popargs();

	start_basic_block();
	METHOD_RETURN_VALUE();
}

define_insn(INVOKEINTERFACE)
{
	/*
	 * ..., obj, ..args.., -> ...
	 */
	check_pcidx (0);
	check_pc (2);

	idx = (uint16)((getpc(0) << 8) | getpc(1));

	slot_alloctmp(tmp);

	get_method_info(idx);
	idx = (uint8)getpc(2) - 1;

	CHECK_NULL(INVOKEINTERFACE, rstack(idx), 34);

	check_stack_ref(idx);

	softcall_lookupmethod(tmp, method_method(), rstack(idx));
#if !(defined(TRANSLATOR) && defined(HAVE_TRAMPOLINE))
	softcall_get_method_code (tmp, tmp);
#endif

	/* Push arguments */
	METHOD_PUSHARGS(1);
	/* Push object */
	pusharg_ref(rstack(idx), 0);

	slot_nowriteback(tmp);
	end_basic_block();

	/* Call it */
	low = method_returntype();
	call(tmp);

	/* Pop args */
	idx++;
	popargs();

	start_basic_block();
	METHOD_RETURN_VALUE();
}

define_insn(NEW)
{
	/*
	 * ... ->  ..., object ref
	 */
	check_pcidx (0);

	idx = (uint16)((getpc(0) << 8) | getpc(1));

	get_class_info(idx);
	push(1);
	softcall_new(wstack(0), class_object());
}

define_insn(NEWARRAY)
{
	/*
	 * ... size ->  ..., object ref
	 */
	check_pc (0);
	check_stack_int(0);

	low = (uint8)getpc(0);
	softcall_newarray(wstack(0), rstack(0), low);
}

define_insn(ANEWARRAY)
{
	/*
	 * ... size ->  ..., object ref
	 */
	check_pcidx (0);
	check_stack_int(0);

	idx = (uint16)((getpc(0) << 8) | getpc(1));

	get_class_info(idx);
	softcall_anewarray(wstack(0), rstack(0), class_object());
}

define_insn(ARRAYLENGTH)
{
	/*
	 * ..., obj -> ..., length
	 */
	CHECK_NULL(ARRAYLENGTH, rstack(0), 34);

	check_stack_array(0);

	load_offset_int(wstack(0), rstack(0), object_array_length);
}

define_insn(ATHROW)
{
	/*
	 * ..., obj -> undefined
	 */
	CHECK_NULL(ATHROW, rstack(0), 34);

	check_stack_ref(0);

	softcall_athrow(rstack(0));
}

define_insn(CHECKCAST)
{
	/*
	 * ..., obj -> ..., obj
	 */
	check_pcidx (0);
	check_stack_ref(0);

	idx = (uint16)((getpc(0) << 8) | getpc(1));

	get_class_info(idx);
	softcall_checkcast(stack(0), class_object());
}

define_insn(INSTANCEOF)
{
	/*
	 * ..., obj -> ..., result
	 */
	check_pcidx (0);
	check_stack_ref(0);

	idx = (uint16)((getpc(0) << 8) | getpc(1));

	get_class_info(idx);
	softcall_instanceof(wstack(0), rstack(0), class_object());
}

define_insn(MONITORENTER)
{
	/*
	 * ... obj -> ...
	 */
	CHECK_NULL(MONITORENTER, rstack(0), 34);

	check_stack_ref(0);

	softcall_monitorenter(rstack(0));
	pop(1);
}

define_insn(MONITOREXIT)
{
	/*
	 * ... obj -> ...
	 */
	CHECK_NULL(MONITOREXIT, rstack(0), 34);

	check_stack_ref(0);

	softcall_monitorexit(rstack(0));
	pop(1);
}

define_insn(WIDE)
{
	wide = 1;
}

define_insn(MULTIANEWARRAY)
{
	/*
	 * ... size1, size2, ... sizen ->  ..., object ref
	 */
	check_pcidx (0);
	check_pc (2);

	idx = (uint16)((getpc(0) << 8) | getpc(1));
	low = (uint8)getpc(2);

	for (high = 0; high < low; high++) {
		check_stack_int(high);
	}

	get_class_info(idx);
	softcall_multianewarray(wstack(low-1), low, rstack(0), class_object());

	pop(low-1);
}

define_insn(IFNULL)
{
	check_pcidx (0);
	check_stack_ref(0);

	idx = (int16)((getpc(0) << 8) | getpc(1));

	slot_nowriteback(rstack(0));
	end_basic_block();

	cbranch_ref_const_eq(rstack(0), 0, reference_code_label(pc+idx));

	pop(1);
}

define_insn(IFNONNULL)
{
	check_pcidx (0);
	check_stack_ref(0);

	idx = (int16)((getpc(0) << 8) | getpc(1));

	slot_nowriteback(rstack(0));
	end_basic_block();

	cbranch_ref_const_ne(rstack(0), 0, reference_code_label(pc+idx));

	pop(1);
}

define_insn(GOTO_W)
{
	check_pcwidx (0);

	idx = (int32)((getpc(0) << 24) | (getpc(1) << 16) |
				(getpc(2) << 8) | getpc(3));

	end_basic_block();
	branch_a(reference_code_label(pc+idx));
}

define_insn(JSR_W)
{
	/*
	 * ... -> ..., ret-addr
	 */
	check_pcwidx (0);

	idx = (int32)((getpc(0) << 24) | (getpc(1) << 16) |
				(getpc(2) << 8) | getpc(3));

	push(1);
	move_label_const(wstack(0), reference_code_label(npc));
	end_basic_block();
	branch_a(reference_code_label(pc+idx));
}

define_insn(BREAKPOINT)
{
	breakpoint();
}

define_insn(CHECKCAST_FAST)
{
	/*
	 * ..., obj -> ..., obj
	 */
	idx = (uint16)((getpc(0) << 8) | getpc(1));
	soft_checkcast(CLASS_CLASS(idx, CLASS_CONSTANTS(meth->class)), stack(0)->v.taddr);

}

define_insn(INSTANCEOF_FAST)
{
	/*
	 * ..., obj -> ..., int
	 */
	idx = (uint16)((getpc(0) << 8) | getpc(1));
	stack(0)->v.tint = soft_instanceof(CLASS_CLASS(idx, CLASS_CONSTANTS(meth->class)), stack(0)->v.taddr);
}
