/* jit-alpha.def
 * Alpha instruction definition.
 *
 * Copyright (c) 1996, 1997
 *	Transvirtual Technologies, Inc.  All rights reserved.
 *
 * See the file "license.terms" for information on usage and redistribution 
 * of this file. 
 */

/* Turn this on for full denormal/NaN/Inf support at the expense of speed.
   It is possible that this may be able to be turned off anyway on the
   EV5 and higher, as they require no kernel support for such things.  */

/* If so, use IMPLVER to detect current AXP Implementation Version:
   0 21064 (EV4), 21064A (EV45), 21066A/21068A (LCA45)
   1 21164 (EV5), 21164A (EV56), 21164PC (PCA56)
   2 21264 (EV6)

   Or AMASK bit 9: Support for precise arithmetic trap reporting in
   hardware.  The trap PC is the same as the instruction PC after the
   traping instruction is executed.

   See alpha_have_precise_trap().  Edouard  */

#define AXP_FULL_IEEE_FP

#include <string.h>
#include "classMethod.h"
#include "access.h"
#include "constpool.h"
#include "exception.h"
#include "thread.h"
#include "gc.h"
#include "gtypes.h"


#define REG_v0			0
#define REG_s0			9
#define REG_fp			15
#define REG_a0			16
#define REG_t9			23
#define REG_t10			24
#define REG_t11			25
#define REG_ra			26
#define REG_pv			27
#define REG_at			28
#define REG_gp			29
#define REG_sp			30
#define REG_zero		31
#define REG_f0			(32+0)
#define REG_f2			(32+2)
#define REG_fa0			(32+16)
#define REG_ft			(32+30)
#define REG_fzero		(32+31)


#ifdef KAFFE_VMDEBUG
static const char * const rnames[] = {
	"v0",
	"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
	"s0", "s1", "s2", "s3", "s4", "s5",
	"fp",
	"a0", "a1", "a2", "a3", "a4", "a5",
	"t8", "t9", "t10", "t11",
	"ra",
	"pv",
	"at",
	"gp",
	"sp",
	"zero",
	"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
	"f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
	"f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
	"f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31"
};

#define regname(n)	rnames[n]
#define fregname(n)	rnames[n]
#endif

int *alpha_slot2argoffset;
int alpha_nslot2argoffset;
alpha_jit_info_t alpha_jit_info;

#define alpha_s32_rangecheck(v)	((v) >= -0x80000000L && (v) < 0x80000000L)


/* --------------------------------------------------------------------- */
/* Instruction formats							 */

#define insn_bra(op, ra, disp)						\
	LOUT((((op) << 26) | (((ra) & 0x1F) << 21)			\
		| ((((disp) + 4) / 4) & 0x1FFFFF)))

#define insn_mem(op, ra, rb, off)					\
	LOUT((((op) << 26) | (((ra) & 0x1F) << 21)			\
		| (((rb) & 0x1F) << 16) | ((off) & 0xFFFF)))

#define insn_mfc(op, fn, ra, rb)					\
	LOUT((((op) << 26) | (((ra) & 0x1F) << 21)			\
		| (((rb) & 0x1F) << 16) | ((fn) & 0xFFFF)))

#define insn_fp(op, fn, ra, rb, rc)					\
	LOUT((((op) << 26) | (((ra) & 0x1F) << 21)			\
		| (((rb) & 0x1F) << 16) | (((fn) & 0x7FF) << 5)		\
		| ((rc) & 0x1F)))

#define insn_opr(op, fn, ra, rb, rc)					\
	LOUT((((op) << 26) | (((ra) & 0x1F) << 21)			\
		| (((rb) & 0x1F) << 16) | (((fn) & 0x7F) << 5)		\
		| ((rc) & 0x1F)))

#define insn_oprl(op, fn, ra, lit, rc)					\
	LOUT((((op) << 26) | (((ra) & 0x1F) << 21)			\
		| (((lit) & 0xFF) << 13) | 0x1000 | (((fn) & 0x7F) << 5)\
		| ((rc) & 0x1F)))

#define insn_mbr(op, fn, ra, rb, extra)					\
	LOUT((((op) << 26) | (((ra) & 0x1F) << 21)			\
		| (((rb) & 0x1F) << 16) | (((fn) & 3) << 14)		\
		| ((extra) & 0x3FFF)))


/* --------------------------------------------------------------------- */
/* Specific Instructions						 */

#ifdef KAFFE_VMDEBUG
int jit_debug;
#define debug(x)	(jit_debug ? dprintf("%x:\t", CODEPC), dprintf x : 0)
#else
#define debug(x)
#endif

#define op_addl(ra, rb, rc)						\
	debug(("addl\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x10, 0x00, (ra), (rb), (rc))

#define op_addl_i(ra, ib, rc)						\
	debug(("addl\t%s,%d,%s\n",regname(ra),(unsigned char)(ib),regname(rc))); \
	insn_oprl(0x10, 0x00, (ra), (ib), (rc))

#define op_addq(ra, rb, rc)						\
	debug(("addq\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x10, 0x20, (ra), (rb), (rc))

#define op_addq_i(ra, ib, rc)						\
	debug(("addq\t%s,%d,%s\n",regname(ra),(unsigned char)(ib),regname(rc))); \
	insn_oprl(0x10, 0x20, (ra), (ib), (rc))

#define op_adds(ra, rb, rc)						\
	debug(("adds\t%s,%s,%s\n",fregname(ra),fregname(rb),fregname(rc))); \
	insn_fp(0x16, 0x080, (ra), (rb), (rc))

#define op_adds_su(ra, rb, rc)						\
	debug(("adds/su\t%s,%s,%s\n",fregname(ra),fregname(rb),fregname(rc)));\
	insn_fp(0x16, 0x580, (ra), (rb), (rc))

#define op_addt(ra, rb, rc)						\
	debug(("addt\t%s,%s,%s\n",fregname(ra),fregname(rb),fregname(rc))); \
	insn_fp(0x16, 0x0A0, (ra), (rb), (rc))

#define op_addt_su(ra, rb, rc)						\
	debug(("addt/su\t%s,%s,%s\n",fregname(ra),fregname(rb),fregname(rc)));\
	insn_fp(0x16, 0x5A0, (ra), (rb), (rc))

#define op_and(ra, rb, rc)						\
	debug(("and\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x11, 0x00, (ra), (rb), (rc))

#define op_and_i(ra, ib, rc)						\
	debug(("and\t%s,%d,%s\n",regname(ra),(unsigned char)(ib),regname(rc))); \
	insn_oprl(0x11, 0x00, (ra), (ib), (rc))

#define op_andnot_i(ra, ib, rc)						\
	debug(("andnot\t%s,%d,%s\n",regname(ra),(unsigned char)(ib),regname(rc))); \
	insn_oprl(0x11, 0x08, (ra), (ib), (rc))

#define op_beq(ra, disp)						\
	debug(("beq\t%s,%+d\n",regname(ra),(disp)));			\
	insn_bra(0x39, (ra), (disp))

#define op_bge(ra, disp)						\
	debug(("bge\t%s,%+d\n",regname(ra),(disp)));			\
	insn_bra(0x3E, (ra), (disp))

#define op_bgt(ra, disp)						\
	debug(("bgt\t%s,%+d\n",regname(ra),(disp)));			\
	insn_bra(0x3F, (ra), (disp))

#define op_ble(ra, disp)						\
	debug(("ble\t%s,%+d\n",regname(ra),(disp)));			\
	insn_bra(0x3B, (ra), (disp))

#define op_blt(ra, disp)						\
	debug(("blt\t%s,%+d\n",regname(ra),(disp)));			\
	insn_bra(0x3A, (ra), (disp))

#define op_bne(ra, disp)						\
	debug(("bne\t%s,%+d\n",regname(ra),(disp)));			\
	insn_bra(0x3D, (ra), (disp))

#define op_br(ra, disp)							\
	debug(("br\t%s,%+d\n",regname(ra),(disp)));			\
	insn_bra(0x30, (ra), (disp))

#define op_cmpeq(ra, rb, rc)						\
	debug(("cmpeq\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x10, 0x2D, (ra), (rb), (rc))

#define op_cmpeq_i(ra, ib, rc)						\
	debug(("cmpeq\t%s,%d,%s\n",regname(ra),(unsigned char)(ib),regname(rc))); \
	insn_oprl(0x10, 0x2D, (ra), (ib), (rc))

#define op_cmple(ra, rb, rc)						\
	debug(("cmple\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x10, 0x6D, (ra), (rb), (rc))

#define op_cmple_i(ra, ib, rc)						\
	debug(("cmple\t%s,%d,%s\n",regname(ra),(unsigned char)(ib),regname(rc))); \
	insn_oprl(0x10, 0x6D, (ra), (ib), (rc))

#define op_cmplt(ra, rb, rc)						\
	debug(("cmplt\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x10, 0x4D, (ra), (rb), (rc))

#define op_cmplt_i(ra, ib, rc)						\
	debug(("cmplt\t%s,%d,%s\n",regname(ra),(unsigned char)(ib),regname(rc))); \
	insn_oprl(0x10, 0x4D, (ra), (ib), (rc))

#define op_cmpult(ra, rb, rc)						\
	debug(("cmpult\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x10, 0x1D, (ra), (rb), (rc))

#define op_cmpult_i(ra, ib, rc)						\
	debug(("cmpult\t%s,%d,%s\n",regname(ra),(unsigned char)(ib),regname(rc))); \
	insn_oprl(0x10, 0x1D, (ra), (ib), (rc))

#define op_cpysn(ra, rb, rc)						\
	debug(("cpysn\t%s,%s,%s\n",fregname(ra),fregname(rb),fregname(rc))); \
	insn_fp(0x17, 0x021, (ra), (rb), (rc))

#define op_cvtlq(rb, rc)						\
	debug(("cvtlq\t%s,%s\n",fregname(rb),fregname(rc)));		\
	insn_fp(0x17, 0x010, REG_zero, (rb), (rc))

#define op_cvtql(rb, rc)						\
	debug(("cvtql\t%s,%s\n",fregname(rb),fregname(rc)));		\
	insn_fp(0x17, 0x030, REG_zero, (rb), (rc))

#define op_cvtql_sv(rb, rc)						\
	debug(("cvtql/sv\t%s,%s\n",fregname(rb),fregname(rc)));		\
	insn_fp(0x17, 0x530, REG_zero, (rb), (rc))

#define op_cvtqs(rb, rc)						\
	debug(("cvtqs\t%s,%s\n",fregname(rb),fregname(rc)));		\
	insn_fp(0x16, 0x0BC, REG_zero, (rb), (rc))

#define op_cvtqt(rb, rc)						\
	debug(("cvtqt\t%s,%s\n",fregname(rb),fregname(rc)));		\
	insn_fp(0x16, 0x0BE, REG_zero, (rb), (rc))

#define op_cvtst(rb, rc)						\
	debug(("cvtst\t%s,%s\n",fregname(rb),fregname(rc)));		\
	insn_fp(0x16, 0x2AC, REG_zero, (rb), (rc))

#define op_cvtst_s(rb, rc)						\
	debug(("cvtst/s\t%s,%s\n",fregname(rb),fregname(rc)));		\
	insn_fp(0x16, 0x6AC, REG_zero, (rb), (rc))

#define op_cvttq_c(rb, rc)						\
	debug(("cvttq/c\t%s,%s\n",fregname(rb),fregname(rc)));		\
	insn_fp(0x16, 0x02F, REG_zero, (rb), (rc))

#define op_cvttq_svc(rb, rc)						\
	debug(("cvttq/svc\t%s,%s\n",fregname(rb),fregname(rc)));	\
	insn_fp(0x16, 0x52F, REG_zero, (rb), (rc))

#define op_cvtts(rb, rc)						\
	debug(("cvtts\t%s,%s\n",fregname(rb),fregname(rc)));		\
	insn_fp(0x16, 0x0AC, REG_zero, (rb), (rc))

#define op_cvtts_su(rb, rc)						\
	debug(("cvtts/su\t%s,%s\n",fregname(rb),fregname(rc)));		\
	insn_fp(0x16, 0x5AC, REG_zero, (rb), (rc))

#define op_divs(ra, rb, rc)						\
	debug(("divs\t%s,%s,%s\n",fregname(ra),fregname(rb),fregname(rc))); \
	insn_fp(0x16, 0x083, (ra), (rb), (rc))

#define op_divs_su(ra, rb, rc)						\
	debug(("divs/su\t%s,%s,%s\n",fregname(ra),fregname(rb),fregname(rc)));\
	insn_fp(0x16, 0x583, (ra), (rb), (rc))

#define op_divt(ra, rb, rc)						\
	debug(("divt\t%s,%s,%s\n",fregname(ra),fregname(rb),fregname(rc))); \
	insn_fp(0x16, 0x0A3, (ra), (rb), (rc))

#define op_divt_su(ra, rb, rc)						\
	debug(("divt/su\t%s,%s,%s\n",fregname(ra),fregname(rb),fregname(rc)));\
	insn_fp(0x16, 0x5A3, (ra), (rb), (rc))

#define op_extbl(ra, rb, rc)						\
	debug(("extbl\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x12, 0x06, (ra), (rb), (rc))

#define op_extwl(ra, rb, rc)						\
	debug(("extwl\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x12, 0x16, (ra), (rb), (rc))

#define op_extqh(ra, rb, rc)						\
	debug(("extqh\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x12, 0x7A, (ra), (rb), (rc))

#define op_fmov(ra, rb)							\
	debug(("fmov\t%s,%s\n",fregname(ra),fregname(rb)));		\
	insn_fp(0x17, 0x020, (ra), (ra), (rb))

#define op_insbl(ra, rb, rc)						\
	debug(("insbl\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x12, 0x0B, (ra), (rb), (rc))

#define op_inswl(ra, rb, rc)						\
	debug(("inswl\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x12, 0x1B, (ra), (rb), (rc))

#define op_jmp(ra, rb, hint)						\
	debug(("jmp\t%s,(%s),%+d\n",regname(ra),regname(rb),(hint)));	\
	insn_mbr(0x1A, 0, (ra), (rb), (hint))

#define op_jsr(ra, rb, hint)						\
	debug(("jsr\t%s,(%s),%+d\n",regname(ra),regname(rb),(hint)));	\
	insn_mbr(0x1A, 1, (ra), (rb), (hint))

#define op_lda(ra, rb, off)						\
	debug(("lda\t%s,%hd(%s)\n",regname(ra),(short)(off),regname(rb))); \
	insn_mem(0x08, (ra), (rb), (off))

#define op_ldah(ra, rb, off)						\
	debug(("ldah\t%s,%hd(%s)\n",regname(ra),(short)(off),regname(rb))); \
	insn_mem(0x09, (ra), (rb), (off))

#define op_ldl(ra, rb, off)						\
	debug(("ldl\t%s,%hd(%s)\n",regname(ra),(off),regname(rb)));	\
	insn_mem(0x28, (ra), (rb), (off))

#define op_ldq(ra, rb, off)						\
	debug(("ldq\t%s,%hd(%s)\n",regname(ra),(off),regname(rb)));	\
	insn_mem(0x29, (ra), (rb), (off))

#define op_ldq_u(ra, rb, off)						\
	debug(("ldq_u\t%s,%hd(%s)\n",regname(ra),(off),regname(rb)));	\
	insn_mem(0x0B, (ra), (rb), (off))

#define op_lds(ra, rb, off)						\
	debug(("lds\t%s,%hd(%s)\n",fregname(ra),(off),regname(rb)));	\
	insn_mem(0x22, (ra), (rb), (off))

#define op_ldt(ra, rb, off)						\
	debug(("ldt\t%s,%hd(%s)\n",fregname(ra),(off),regname(rb)));	\
	insn_mem(0x23, (ra), (rb), (off))

#define op_mov(ra, rb)							\
	debug(("mov\t%s,%s\n",regname(ra),regname(rb)));		\
	insn_opr(0x11, 0x20, (ra), (ra), (rb))

#define op_mskbl(ra, rb, rc)						\
	debug(("mskbl\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x12, 0x02, (ra), (rb), (rc))

#define op_mskwl(ra, rb, rc)						\
	debug(("mskwl\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x12, 0x12, (ra), (rb), (rc))

#define op_mull(ra, rb, rc)						\
	debug(("mull\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x13, 0x00, (ra), (rb), (rc))

#define op_mull_i(ra, ib, rc)						\
	debug(("mull\t%s,%d,%s\n",regname(ra),(ib),regname(rc)));	\
	insn_oprl(0x13, 0x00, (ra), (ib), (rc))

#define op_mulq(ra, rb, rc)						\
	debug(("mulq\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x13, 0x20, (ra), (rb), (rc))

#define op_mulq_i(ra, ib, rc)						\
	debug(("mulq\t%s,%d,%s\n",regname(ra),(ib),regname(rc)));	\
	insn_opr(0x13, 0x20, (ra), (ib), (rc))

#define op_muls(ra, rb, rc)						\
	debug(("muls\t%s,%s,%s\n",fregname(ra),fregname(rb),fregname(rc))); \
	insn_fp(0x16, 0x082, (ra), (rb), (rc))

#define op_muls_su(ra, rb, rc)						\
	debug(("muls/su\t%s,%s,%s\n",fregname(ra),fregname(rb),fregname(rc)));\
	insn_fp(0x16, 0x582, (ra), (rb), (rc))

#define op_mult(ra, rb, rc)						\
	debug(("mult\t%s,%s,%s\n",fregname(ra),fregname(rb),fregname(rc))); \
	insn_fp(0x16, 0x0A2, (ra), (rb), (rc))

#define op_mult_su(ra, rb, rc)						\
	debug(("mult/su\t%s,%s,%s\n",fregname(ra),fregname(rb),fregname(rc)));\
	insn_fp(0x16, 0x5A2, (ra), (rb), (rc))

#define op_or(ra, rb, rc)						\
	debug(("or\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc)));	\
	insn_opr(0x11, 0x20, (ra), (rb), (rc))

#define op_ret(ra, rb, code)						\
	debug(("ret\t%s,(%s),%d\n",regname(ra),regname(rb),(code)));	\
	insn_mbr(0x1A, 2, (ra), (rb), (code))

#define op_s4addl(ra, rb, rc)						\
	debug(("s4addl\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x10, 0x02, (ra), (rb), (rc))

#define op_s4addq(ra, rb, rc)						\
	debug(("s4addq\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x10, 0x22, (ra), (rb), (rc))

#define op_s4subl(ra, rb, rc)						\
	debug(("s4subl\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x10, 0x0B, (ra), (rb), (rc))

#define op_s4subq(ra, rb, rc)						\
	debug(("s4subq\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x10, 0x2B, (ra), (rb), (rc))

#define op_s8addl(ra, rb, rc)						\
	debug(("s8addl\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x10, 0x12, (ra), (rb), (rc))

#define op_s8addq(ra, rb, rc)						\
	debug(("s8addq\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x10, 0x32, (ra), (rb), (rc))

#define op_s8subl(ra, rb, rc)						\
	debug(("s8subl\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x10, 0x1B, (ra), (rb), (rc))

#define op_s8subq(ra, rb, rc)						\
	debug(("s8subq\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x10, 0x3B, (ra), (rb), (rc))

#define op_sll(ra, rb, rc)						\
	debug(("sll\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x12, 0x39, (ra), (rb), (rc))

#define op_sll_i(ra, ib, rc)						\
	debug(("sll\t%s,%d,%s\n",regname(ra),(ib),regname(rc)));	\
	insn_oprl(0x12, 0x39, (ra), (ib), (rc))

#define op_sra(ra, rb, rc)						\
	debug(("sra\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x12, 0x3C, (ra), (rb), (rc))

#define op_sra_i(ra, ib, rc)						\
	debug(("sra\t%s,%d,%s\n",regname(ra),(ib),regname(rc)));	\
	insn_oprl(0x12, 0x3C, (ra), (ib), (rc))

#define op_srl(ra, rb, rc)						\
	debug(("srl\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc)));	\
	insn_opr(0x12, 0x34, (ra), (rb), (rc))

#define op_srl_i(ra, ib, rc)						\
	debug(("srl\t%s,%d,%s\n",regname(ra),(ib),regname(rc)));	\
	insn_oprl(0x12, 0x34, (ra), (ib), (rc))

#define op_stl(ra, rb, off)						\
	debug(("stl\t%s,%hd(%s)\n",regname(ra),(off),regname(rb)));	\
	insn_mem(0x2C, (ra), (rb), (off))

#define op_stq(ra, rb, off)						\
	debug(("stq\t%s,%hd(%s)\n",regname(ra),(off),regname(rb)));	\
	insn_mem(0x2D, (ra), (rb), (off))

#define op_stq_u(ra, rb, off)						\
	debug(("stq_u\t%s,%hd(%s)\n",regname(ra),(off),regname(rb)));	\
	insn_mem(0x0F, (ra), (rb), (off))

#define op_sts(ra, rb, off)						\
	debug(("sts\t%s,%hd(%s)\n",fregname(ra),(off),regname(rb)));	\
	insn_mem(0x26, (ra), (rb), (off))

#define op_stt(ra, rb, off)						\
	debug(("stt\t%s,%hd(%s)\n",fregname(ra),(off),regname(rb)));	\
	insn_mem(0x27, (ra), (rb), (off))

#define op_subl(ra, rb, rc)						\
	debug(("subl\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc)));\
	insn_opr(0x10, 0x09, (ra), (rb), (rc))

#define op_subl_i(ra, ib, rc)						\
	debug(("subl\t%s,%d,%s\n",regname(ra),(ib),regname(rc)));	\
	insn_oprl(0x10, 0x09, (ra), (ib), (rc))

#define op_subq(ra, rb, rc)						\
	debug(("subq\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc)));\
	insn_opr(0x10, 0x29, (ra), (rb), (rc))

#define op_subq_i(ra, ib, rc)						\
	debug(("subq\t%s,%d,%s\n",regname(ra),(ib),regname(rc)));	\
	insn_oprl(0x10, 0x29, (ra), (ib), (rc))

#define op_subs(ra, rb, rc)						\
	debug(("subs\t%s,%s,%s\n",fregname(ra),fregname(rb),fregname(rc))); \
	insn_fp(0x16, 0x081, (ra), (rb), (rc))

#define op_subs_su(ra, rb, rc)						\
	debug(("subs/su\t%s,%s,%s\n",fregname(ra),fregname(rb),fregname(rc)));\
	insn_fp(0x16, 0x581, (ra), (rb), (rc))

#define op_subt(ra, rb, rc)						\
	debug(("subt\t%s,%s,%s\n",fregname(ra),fregname(rb),fregname(rc))); \
	insn_fp(0x16, 0x0A1, (ra), (rb), (rc))

#define op_subt_su(ra, rb, rc)						\
	debug(("subt/su\t%s,%s,%s\n",fregname(ra),fregname(rb),fregname(rc)));\
	insn_fp(0x16, 0x5A1, (ra), (rb), (rc))

#define op_trapb()							\
	debug(("trapb\n"));						\
	insn_mfc(0x18, 0x0000, 0, 0)

#define op_unop()							\
	debug(("unop\n"));						\
	insn_mem(0x0B, REG_zero, REG_zero, 0)

#define op_xor(ra, rb, rc)						\
	debug(("xor\t%s,%s,%s\n",regname(ra),regname(rb),regname(rc))); \
	insn_opr(0x11, 0x40, (ra), (rb), (rc))

#define op_zapnot_i(ra, ib, rc)						\
	debug(("zapnot\t%s,%#x,%s\n",regname(ra),(unsigned char)(ib),regname(rc))); \
	insn_oprl(0x12, 0x31, (ra), (ib), (rc))


/* The Byte-Word instruction extension present as of the EV56.  */

#define op_ldbu(ra, rb, off)						\
	debug(("ldbu\t%s,%hd(%s)\n",regname(ra),(off),regname(rb)));	\
	insn_mem(0x0A, (ra), (rb), (off))

#define op_ldwu(ra, rb, off)						\
	debug(("ldwu\t%s,%hd(%s)\n",regname(ra),(off),regname(rb)));	\
	insn_mem(0x0C, (ra), (rb), (off))

#define op_sextb(rb, rc)						\
	debug(("sextb\t%s,%s\n",regname(rb),regname(rc)));		\
	insn_opr(0x1C, 0x00, REG_zero, (rb), (rc))

#define op_sextw(rb, rc)						\
	debug(("sextw\t%s,%s\n",regname(rb),regname(rc)));		\
	insn_opr(0x1C, 0x01, REG_zero, (rb), (rc))

#define op_stb(ra, rb, off)						\
	debug(("stb\t%s,%hd(%s)\n",regname(ra),(off),regname(rb)));	\
	insn_mem(0x0E, (ra), (rb), (off))

#define op_stw(ra, rb, off)						\
	debug(("stw\t%s,%hd(%s)\n",regname(ra),(off),regname(rb)));	\
	insn_mem(0x0D, (ra), (rb), (off))


static inline long
alpha_have_bwx(void)
{
#ifdef HAVE_ALPHA_ASM_AMASK
	long r;
	__asm__("amask 1,%0" : "=r"(r));
#else
	/* For those whose assemblers are so far behind not to
	   have the amask instruction.  */
	register long r __asm__("$0");
	__asm__(".long %1"
		: "=r"(r)
		: "i"((0x11 << 26) | (REG_zero << 21) | (1 << 13)
		      | 0x1000 | (0x61 << 5) | REG_v0));
#endif
	return !r;
}

static inline long
alpha_have_precise_trap(void)
{
#ifdef HAVE_ALPHA_ASM_AMASK
	long r;
	__asm__("amask %1,%0" : "=r"(r) : "r"(1<<9));
#else
	/* For those whose assemblers are so far behind not to
	   have the amask instruction.  */
	register long r __asm__("$0") = (1 << 9);
	__asm__(".long %1"
		: "=r"(r)
		: "i"((0x11 << 26) | (REG_zero << 21) | (REG_v0 << 16)
		      | (0x61 << 5) | REG_v0));
#endif
	return !r;
}


/* --------------------------------------------------------------------- */
/* Recognize constants that go to either and or zapnot.  */

static inline long
alpha_zapnot_const(long x)
{
	/* Create a mask of bits in which bit n == 1 iff byte n == 0xFF.  */
	__asm__("cmpbge %1,%2,%0" : "=r"(x) : "r"(x), "r"(-1));
	return x;
}

int
alpha_andconst_rangecheck(long v)
{
	if (v >= 0 && v <= 0xFF) {
		/* we can just use "and" */
		return 1;
	}
	else if (~v >= 0 && ~v <= 0xFF) {
		/* we can use "andnot" */
		return 1;
	}
	else if ((alpha_zapnot_const(v) ^ alpha_zapnot_const(~v)) == 0xFF) {
		/* we can use "zapnot" */
		return 1;
	}
	return 0;
}

/* --------------------------------------------------------------------- */

define_insn(unimplemented, unimplemented)
{
	KAFFEVM_ABORT();
}

define_insn(nop, nop)
{
	/* Presumably we're nop'ing for alignment not delay.  */
	op_unop();
}

static void
alpha_ldgp(int reg)
{
	long hi, lo;

	hi = -CODEPC;
	lo = (short)hi;
	hi = (hi - lo) >> 16;

	if (hi) {
		op_ldah(REG_gp, reg, hi);
		op_lda(REG_gp, REG_gp, lo);
	}
	else {
		op_lda(REG_gp, reg, lo);
	}
}

/* --------------------------------------------------------------------- */

define_insn(prologue, prologue_xxC)
{
	int r, N;
	int l;
	int sl;
	int aspill;
	label* L;
	Method* meth;
	int rsa_size;

	L = const_label(1);
	meth = const_method(2);

#ifdef KAFFE_VMDEBUG
	if (jit_debug) {
		dprintf("\n%s.%s %s\n", meth->class->name->data,
			meth->name->data, METHOD_SIGD(meth));
	}
#endif
	alpha_jit_info.ieee = 0;
	
	/* Initial stack frame looks like:
	 *
	 *      |  Argument above 6             |
	 * SP-> |-------------------------------|
	 *
	 * Calling Standard for Alpha Systems
	 * 3.1.2.1 Stack Frame Format
	 *
	 *  Figure 3-1: Fixed Size Stack Frame Format
	 *              (PDSC_FLAGS_BASE_REG_IS_FP is 0)
	 *  octaword-aligned
	 *      |  Argument above 6             |
	 *      |-------------------------------| <- PDSC_RPD_FRAME_SIZE from SP
	 *      |  Argument home area           |
	 *      |- - - - - - - - - - - - - - - -|
	 *      |  Fixed temporary locations    |
	 *      |-------------------------------|
	 *      |  Register save area           |
	 *      |-------------------------------| <- PDSC_RPD_RSA_OFFSET from SP
	 *      |  Fixed temporary locations    |
	 *      |-------------------------------| <- SP
	 *
	 *  Figure 3-2: Variable Size Stack Frame Format
	 *              (PDSC_FLAGS_BASE_REG_IS_FP is 1)
	 *  octaword-aligned
	 *      |  Argument above 6             |
	 *      |-------------------------------| <- PDSC_RPD_FRAME_SIZE from FP
	 *      |  Argument home area           |
	 *      |- - - - - - - - - - - - - - - -|
	 *      |  Fixed temporary locations    |
	 *      |-------------------------------|
	 *      |  Register save area           |
	 *      |-------------------------------| <- PDSC_RPD_RSA_OFFSET from FP
	 *      |  Fixed temporary locations    |
	 *      |-------------------------------| <- FP
	 *      |  Stack temporary area         |
	 *      |-------------------------------| <- SP
	 *
	 * * Fixed temporary locations are optional sections.  These locations
	 *   might include, for exemple, register spill areas, ..., or fixed
	 *   temporary locations.
	 *
	 * * The argument home area is an optionl section, it is a region of
	 *   memory used by the called procedure for the purpose of assembling
	 *   in contignous memory the arguments passed in registers adjacent to
	 *   the arguments passed in memory, so that all argument can be
	 *   addressed as a contignous array.
	 *
	 * * The register save area is the set of consecutive quadwords where
	 *   the current procedure saves and restores registers.  The registers
	 *   save area begins at the location pointed to by the frame base
	 *   register (as indicated by PDSC_FLAGS_BASE_REG_IS_FP) added to the
	 *   value of the content of PDSC_RPD_RSA_OFFSET.  The result must be
	 *   a quadword-aligned address.
	 *
	 *   All registers saved in the variable portion of the register save
	 *   area must have the corresponding bit set to 1 in the appropriate
	 *   procedure descriptor register save mask.  The algorithm for
	 *   packing saved register in the quadword-aligned register save area
	 *   is as follows:
	 *     . The return address is save at the lowest address of the
	 *       register save area, offset 0.
	 *     . All saved integer registers, as indicated by the corresponding
	 *       bit in PDSC_RPD_IMASK being set to 1, are store, in register
	 *       order, in consecutive quadwords beginning at offset 8 of the
	 *       register save area.
	 *     . All saved floating-point registers, as indicated by the
	 *       corresponding bit in PDSC_RPD_FMASK being set to 1, are stored,
	 *       in register-number order, in consecutive quadwords following
	 *       the saved integer registers.
	 *
	 *   A standard-conforming procedure that utilizes a register save area
	 *   must save the return address register at offset 0 in the register
	 *   save area.  There is no corresponding bit in the register save
	 *   mask for this register slot.
	 *
	 *
	 * Applied for JIT Stack Frame:
	 * We use a fixed-stack frame (PDSC_FLAGS_BASE_REG_IS_FP is 0) with
	 * FP used as a virtual frame pointer to improve FRAMEOBJECT() macro.
	 *
	 *      |  Argument above 6             |
	 * FP ->|-------------------------------| <- PDSC_RPD_FRAME_SIZE from SP
	 *      |  Spill for up to 6 args       |
	 *      |-------------------------------|
	 *      |  Locals, temps                |
	 *      |-------------------------------|
	 *      |  Register save area           |
	 *      |-------------------------------| <- PDSC_RPD_RSA_OFFSET from SP
	 *      |  outgoing parameters past 6   |
	 *      |-------------------------------| <- SP
	 *
	 *
	 * Until we have some method of determining in the prologue which
	 * Rnosaveoncall registers are used by a method, we will save registers
	 * s0 to s5 and f2 to f9.  As we change FP, we must save it also.
	 *
	 * imask = (1 << 9) + ... + (1 << 14) + (1 << 15)
	 * fmask = (1 << 2) + ... + (1 << 9)
	 *
	 * with the return address register, we save 16 registers in RSA.
	 * ra, s0 ... s5, fp, f2 ... f9
	 *
	 */

	/* Calling Standard for Alpha Systems
	 * 3.2.6.1    Entry Code Sequence
	 * Because the value of the PC defines the currently executing
	 * procedure, all properties of the environment specified by a
	 * procedure's descriptor must be valid before the first instruction
	 * after the procedure prologue (as defined by PDSC_RPD_ENTRY_LENGTH)
	 * is executed.  In addition, none of the properties specified in the
	 * calling procedure's descriptor may be invalidated before the called
	 * procedure becomes current. Thus, until the procedure becomes current,
	 * all entry code must adhere to the following rules: 
	 * . All registers specified by this standard as saved across a
	 *   standard-conforming call must contain their original (at entry)
	 *   contents.
	 * . The register designated by PDSC_RPD_ENTRY_RA ($26 in a standard
	 *   call) must contain its original (at entry) contents. This
	 *   requirement also applies to nonstandard procedures to allow for
	 *   proper unwinding.
	 * . No standard calls can be made.
	 *
	 * These actions involve the following steps, performed in the
	 * specified order:
	 *
	 * 1. Compute and load the procedure's GP value using the
	 *    passed-in-procedure (code address) value in $27.
	 *
	 * 2. If stack space is allocated (PDSC_RPD_FRAME_SIZE is not 0), set
	 *    register SP to SP - PDSC_RPD_FRAME_SIZE.
	 *    After any necessary calculations and stack limit checks, this
	 *    step must be completed in exactly one instruction that modifies
	 *    SP.  This instruction must be the one specified by
	 *    PDSC_RPD_SP_SET.
	 *
	 * 3. For a stack frame procedure (PDSC_FLAGS_REGISTER_FRAME is 0), do
	 *    both of these steps.  (There is no requirement as to which step
	 *    occurs first.) 
	 *
	 *    Store the registers specified by PDSC_RPD_IMASK and
	 *    PDSC_RDP_FMASK in the register save area based on
	 *    PDSC_RPD_RSA_OFFSET.
	 *
	 *    Store the return address in the register save area.
	 *
	 * 4. For a register frame procedure (PDSC_FLAGS_REGISTER_FRAME is 1),
	 *    copy the return address to the register specified by
	 *    PDSC_RPD_SAVE_RA if the value is not already there.
	 *
	 * 5. Execute trapb, if required. (See Section 5.1.12 for details.)
	 *
	 * 6. For a variable-size stack frame procedure
	 *    (PDSC_FLAGS_BASE_REG_IS_FP is 1), copy the SP value to register
	 *    FP.  This step must be completed in exactly one instruction that
	 *    modifies the FP and that instruction must be the last instruction
	 *    in the prologue.
	 */

	/* Calling Standard for Alpha Systems
	 * 3.2.6.1    Entry Code Sequence
	 * 1. Compute and load the procedure's GP value using the
	 *    passed-in-procedure (code address) value in $27.  */

	op_lda(REG_gp, REG_pv, 0);
	// op_mov(REG_pv, REG_gp);

	/* Calling Standard for Alpha Systems
	 * 3.2.6.1    Entry Code Sequence
	 * 2. If stack space is allocated (PDSC_RPD_FRAME_SIZE is not 0), set
	 *    register SP to SP - PDSC_RPD_FRAME_SIZE.
	 *    After any necessary calculations and stack limit checks, this
	 *    step must be completed in exactly one instruction that modifies
	 *    SP.  This instruction must be the one specified by
	 *    PDSC_RPD_SP_SET.  */

	alpha_jit_info.sp_set = CODEPC / 4;
	L->type = Lnegframe|Labsolute|Lgeneral;
	L->at = (uintp)CODEPC;
	op_lda(REG_sp, REG_sp, 0);

	/* Calling Standard for Alpha Systems
	 * 3.2.6.1    Entry Code Sequence
	 * 3. For a stack frame procedure (PDSC_FLAGS_REGISTER_FRAME is 0), do
	 *    both of these steps.  (There is no requirement as to which step
	 *    occurs first.) 
	 *
	 *    Store the registers specified by PDSC_RPD_IMASK and
	 *    PDSC_RDP_FMASK in the register save area based on
	 *    PDSC_RPD_RSA_OFFSET.
	 *
	 *    Store the return address in the register save area.  */

	/* Build Register Save mask (without ra) */
	alpha_jit_info.imask = 0xFE00;		/* s0 to s5 and fp */
	alpha_jit_info.fmask = 0x3FC;		/* f2 to f9 */

	/* compute rsa_size for ra, s0-s5, fp, f2-f9 */
	rsa_size = 1 + (5 - 0 + 1) + 1 + (9 - 2 + 1);

	alpha_jit_info.rsa_size = rsa_size;

	/* return address */
	L = KaffeJIT_newLabel();
	L->type = Lrsa|Labsolute|Lgeneral;
	L->at = (uintp)CODEPC;
	op_stq(REG_ra, REG_sp, 0);
	
	N = 1;

	/* registers s0 - s5 and fp (also know as s6) */
	for (r = 0; r < 7; r++, N++) {
		L = KaffeJIT_newLabel();
		L->type = Lrsa|Labsolute|Lgeneral;
		L->at = (uintp)CODEPC;
		op_stq(REG_s0+r, REG_sp, SLOTSIZE * N);
	}

	/* registers f2 - f9 */
	for (r = 0; r < (9 - 2 + 1); r++, N++) {
		L = KaffeJIT_newLabel();
		L->type = Lrsa|Labsolute|Lgeneral;
		L->at = (uintp)CODEPC;
		op_stt(REG_f2+r, REG_sp, SLOTSIZE * N);
	}
	    
	/* this is the end of the prologue */
	alpha_jit_info.entry_length = CODEPC / 4;


	/* setup our virtual frame pointer, it's not part of the prologue.  */
	L = KaffeJIT_newLabel();
	L->type = Lframe|Labsolute|Lgeneral;
	L->at = (uintp)CODEPC;
	op_lda(REG_fp, REG_sp, 0);

	/* Work out which register is what and where.  */
	l = maxLocal + maxStack + maxTemp;
	if (alpha_nslot2argoffset < l) {
		alpha_nslot2argoffset = l;
		alpha_slot2argoffset = KREALLOC(alpha_slot2argoffset,
						l * sizeof(alpha_slot2argoffset[0]));
	}

	if (isStatic) {
		r = 0;
	}
	else {
		alpha_slot2argoffset[0] = -SLOTSIZE;
		r = 1;
	}
	sl = r;

	/* register passed arguments */
	for (l = 0; l < METHOD_NARGS(meth) && r < 6; l++, r++) {
		char type = *METHOD_ARG_TYPE(meth, l);
		
		alpha_slot2argoffset[sl++] = -SLOTSIZE * (r+1);
		if (type == 'J' || type == 'D') {
			/* long and double use two slots */
			alpha_slot2argoffset[sl++] = -SLOTSIZE * (r+1);
		}
	}
	aspill = r + 1;
	
	/* memory passed arguments */
	for (; l < METHOD_NARGS(meth); l++, r++) {
		char type = *METHOD_ARG_TYPE(meth, l);
		
		alpha_slot2argoffset[sl++] = SLOTSIZE * (r-6);
		if (type == 'J' || type == 'D') {
			/* long and double use two slots */
			alpha_slot2argoffset[sl++] = SLOTSIZE * (r-6);
		}
	}

#ifdef KAFFE_VMDEBUG
	if (jit_debug) {
	    dprintf ("nr arg slots = %d\n", sl);
	}
#endif
	
	l = maxLocal + maxStack + maxTemp;
	/* locals and temps */
	while (sl < l) {
		alpha_slot2argoffset[sl++] = -SLOTSIZE * (aspill++);
	}

#ifdef KAFFE_VMDEBUG
	if (jit_debug) {
		for (sl = 0; sl < l; sl++) {
			dprintf ("slot(%d) = %d\n", sl, alpha_slot2argoffset[sl]);
		}
	}
#endif

	/* Now assign the registers to slots */
        if (isStatic) {
		r = 0;
	}
	else {
		forceRegister(&localinfo[0], REG_a0, Rref);
		r = 1;
	}
	sl = r;

	for (l = 0; l < METHOD_NARGS(meth) && r < 6; l++, r++, sl++) {
		switch (*METHOD_ARG_TYPE(meth, l)) {
		case 'L':
		case '[':
			forceRegister(&localinfo[sl], REG_a0+r, Rref);
			break;
		case 'B':
		case 'C':
		case 'I':
		case 'S':
		case 'Z':
			forceRegister(&localinfo[sl], REG_a0+r, Rint);
			break;
		case 'F':
			forceRegister(&localinfo[sl], REG_fa0+r, Rfloat);
			break;
		case 'J':
			forceRegister(&localinfo[sl], REG_a0+r, Rlong);
			sl++;
			break;
		case 'D':
			forceRegister(&localinfo[sl], REG_fa0+r, Rdouble);
			sl++;
			break;
		default:
			KAFFEVM_ABORT();
		}
	}
}

define_insn(epilogue, epilogue_xxx)
{
	label *l;
	int r, N;

	KaffeJIT_setEpilogueLabel ((uintp)CODEPC);

	/* Calling Standard for Alpha Systems
	 * 3.2.6.2.2   Exit Code Sequence Steps
	 * When a procedure returns, the exit code must restore the caller's
	 * context, synchronize any pending hardware exceptions, and make the
	 * calling procedure current by returning control to it.  The following
	 * list contains the exit code sequence steps.  The program performs
	 * step 1, followed by steps 2 through 5 in any order, followed by
	 * steps 6 through 8 in exact order. 
	 *
	 * 1. If the GP register has been modified or a call has been made,
	 *    restore the GP register to the GOT segment pointer of the current
	 *    procedure.
	 *
	 * 2. For a variable-size stack frame procedure that does not return a
	 *    value on the top of stack (PDSC_FLAGS_BASE_REG_IS_FP is 1), copy
	 *    FP to SP.
	 *
	 * 3. For a stack frame procedure (PDSC_FLAGS_REGISTER_FRAME is 0),
	 *    reload any saved registers from the register save area as
	 *    specified by PDSC_RPD_RSA_OFFSET.  Note that, for a variable-size
	 *    stack frame procedure (PDSC_FLAGS_BASE_REG_IS_FP is 1), FP is not
	 *    reloaded in this step.  For a fixed-size stack frame procedure
	 *    (PDSC_FLAGS_BASE_REG_IS_FP is 0), $15 is reloaded if it was saved
	 *    on entry.
	 *
	 * 4. Reload the register that held the return address on entry with
	 *    the saved return address, if necessary.
	 *
	 *    For a stack frame procedure (PDSC_FLAGS_REGISTER_FRAME is 0),
	 *    load the register designated by PDSC_RPD_ENTRY_RA ($26 in a
	 *    standard call) with the return address from the register save
	 *    area as specified by PDSC_RPD_RSA_OFFSET.
	 *
	 *    For a register frame procedure (PDSC_FLAGS_REGISTER_FRAME is 1),
	 *    copy the return address from the register specified by
	 *    PDSC_RPD_SAVE_RA to the register designated by PDSC_RPD_ENTRY_RA.
	 *
	 * 5. Execute trapb, if required. (See Section 5.1.12 for details.)
	 *
	 * 6. For a variable-size stack frame procedure
	 *    (PDSC_FLAGS_REGISTER_FRAME is 0 and PDSC_FLAGS_BASE_REG_IS_FP is
	 *    1), reload $15 (FP) as would be done for any other saved register.
	 *
	 *    After any necessary calculations, this step must be completed by
	 *    exactly one instruction, as described in Section 3.2.6.1.
	 *
	 * 7. If a function value is not being returned on the stack, restore
	 *    SP to the value it had at procedure entry by adding the value in
	 *    PDSC_RDP_FRAME_SIZE to SP.  In some cases, the returning procedure
	 *    leaves SP pointing to a lower stack address than it had on entry
	 *    to the procedure, as specified in Section 4.1.7.
	 *
	 *    After any necessary calculations, this step must be completed by
	 *    exactly one instruction, as described in Section 3.2.6.1.
	 *
	 * 8. Execute the ret $31,($n),0001 instruction, as described in
	 *    Section 3.2.6.2.1, to return control to the calling procedure.
	 *    In almost all cases the $n used will be $26 (the return address
	 *    register) because its value must be restored before the call
	 *    returns.
	 *
	 *    Note that the called routine does not adjust the stack to remove
	 *    any arguments passed in memory.  This responsibility falls to the
	 *    calling routine, which can choose to defer removal of arguments
	 *    because of optimizations or other considerations.
	 */


	/* Calling Standard for Alpha Systems
	 * 3.2.6.2.2   Exit Code Sequence Steps
	 * 1. If the GP register has been modified or a call has been made,
	 *    restore the GP register to the GOT segment pointer of the current
	 *    procedure.
	 *
	 * NOT NEED, GP is already restored after each subroutine call.
	 * see alpha_division(), call_ref(), call() and call_soft().  */


	/* Calling Standard for Alpha Systems
	 * 3.2.6.2.2   Exit Code Sequence Steps
	 * 3. For a stack frame procedure (PDSC_FLAGS_REGISTER_FRAME is 0),
	 *    reload any saved registers from the register save area as
	 *    specified by PDSC_RPD_RSA_OFFSET.  Note that, for a variable-size
	 *    stack frame procedure (PDSC_FLAGS_BASE_REG_IS_FP is 1), FP is not
	 *    reloaded in this step.  For a fixed-size stack frame procedure
	 *    (PDSC_FLAGS_BASE_REG_IS_FP is 0), $15 is reloaded if it was saved
	 *    on entry.
	 *
	 * 4. Reload the register that held the return address on entry with
	 *    the saved return address, if necessary.
	 *
	 *    For a stack frame procedure (PDSC_FLAGS_REGISTER_FRAME is 0),
	 *    load the register designated by PDSC_RPD_ENTRY_RA ($26 in a
	 *    standard call) with the return address from the register save
	 *    area as specified by PDSC_RPD_RSA_OFFSET.  */

	/* return address */
	l = KaffeJIT_newLabel();
	l->type = Lrsa|Labsolute|Lgeneral;
	l->at = (uintp)CODEPC;
	op_ldq(REG_ra, REG_sp, 0);

	N = 1;

	/* registers s0 - s5 and fp (also know as s6) */
	for (r = 0; r < 7; r++, N++) {
		l = KaffeJIT_newLabel();
		l->type = Lrsa|Labsolute|Lgeneral;
		l->at = (uintp)CODEPC;
		op_ldq(REG_s0+r, REG_sp, SLOTSIZE * N);
	}

	/* registers f2 - f9 */
	for (r = 0; r < (9 - 2 + 1); r++, N++) {
		l = KaffeJIT_newLabel();
		l->type = Lrsa|Labsolute|Lgeneral;
		l->at = (uintp)CODEPC;
		op_ldt(REG_f2+r, REG_sp, SLOTSIZE * N);
	}

	/* Calling Standard for Alpha Systems
	 * 3.2.6.2.2   Exit Code Sequence Steps
	 * 7. If a function value is not being returned on the stack, restore
	 *    SP to the value it had at procedure entry by adding the value in
	 *    PDSC_RDP_FRAME_SIZE to SP.  In some cases, the returning procedure
	 *    leaves SP pointing to a lower stack address than it had on entry
	 *    to the procedure, as specified in Section 4.1.7.  */

	l = KaffeJIT_newLabel();
	l->type = Lframe|Labsolute|Lgeneral;
	l->at = (uintp)CODEPC;
	op_lda(REG_sp, REG_sp, 0);

	/* Calling Standard for Alpha Systems
	 * 3.2.6.2.2   Exit Code Sequence Steps
	 * 8. Execute the ret $31,($n),0001 instruction, as described in
	 *    Section 3.2.6.2.1, to return control to the calling procedure.
	 *    In almost all cases the $n used will be $26 (the return address
	 *    register) because its value must be restored before the call
	 *    returns.  */

	op_ret(REG_zero, REG_ra, 1);
}

define_insn(eprologue, eprologue_xxx)
{
	label *l;

	/* Reload GP, CALL_KAFFE_EXCEPTION() does jmp ($27) to go here
	 * and FP set.  */
	alpha_ldgp(REG_pv);

	l = KaffeJIT_newLabel();
	l->type = Lnegframe|Labsolute|Lgeneral;
	l->at = (uintp)CODEPC;
	op_lda(REG_sp, REG_fp, 0);
}

/* --------------------------------------------------------------------- */

define_insn(spill_int, spilli_RCx)
{
	int r = sreg_int(0);
	int o = const_int(1);

	assert(o >= -0x8000 && o < 0x8000);

	/* Always spill integers in wide mode, so that we don't end up with
	   garbage when slots are manipulated in "any" mode, e.g. with dup.  */

	op_stq(r, REG_fp, o);
}

define_insn(spill_ref, spillr_RCx)
{
	int r = sreg_ref(0);
	int o = const_int(1);

	assert(o >= -0x8000 && o < 0x8000);
	op_stq(r, REG_fp, o);
}

define_insn(spill_long, spilll_RCx)
{
	int r = sreg_long(0);
	int o = const_int(1);

	assert(o >= -0x8000 && o < 0x8000);
	op_stq(r, REG_fp, o);
}

define_insn(spill_float, spillf_RCx)
{
	int r = sreg_float(0);
	int o = const_int(1);

	assert(o >= -0x8000 && o < 0x8000);
	op_sts(r, REG_fp, o);
}

define_insn(spill_double, spilld_RCx)
{
	int r = sreg_double(0);
	int o = const_int(1);

	assert(o >= -0x8000 && o < 0x8000);
	op_stt(r, REG_fp, o);
}

define_insn(reload_int, reloadi_RCx)
{
	int r = lreg_int(0);
	int o = const_int(1);

	assert(o >= -0x8000 && o < 0x8000);
	op_ldl(r, REG_fp, o);
}

define_insn(reload_ref, reloadr_RCx)
{
	int r = lreg_ref(0);
	int o = const_int(1);

	assert(o >= -0x8000 && o < 0x8000);
	op_ldq(r, REG_fp, o);
}

define_insn(reload_long, reloadl_RCx)
{
	int r = lreg_long(0);
	int o = const_int(1);

	assert(o >= -0x8000 && o < 0x8000);
	op_ldq(r, REG_fp, o);
}

define_insn(reload_float, reloadf_RCx)
{
	int r = lreg_float(0);
	int o = const_int(1);

	assert(o >= -0x8000 && o < 0x8000);
	op_lds(r, REG_fp, o);
}

define_insn(reload_double, reloadd_RCx)
{
	int r = lreg_double(0);
	int o = const_int(1);

	assert(o >= -0x8000 && o < 0x8000);
	op_ldt(r, REG_fp, o);
}

/* --------------------------------------------------------------------- */

static void
alpha_move_const_32(int w, int val)
{
	long hi, lo, extra, tmp;
	int b = REG_zero;

	lo = (short)val;
	tmp = val - lo;
	hi = (short)(tmp >> 16);

	if (tmp - (hi << 16)) {
		extra = 0x4000;
		tmp -= 0x40000000;
		hi = (short)(tmp >> 16);
	}
	else {
		extra = 0;
	}

	if (extra) {
		op_ldah(w, b, extra); 
		b = w;
	}
	if (hi) {
		op_ldah(w, b, hi); 
		b = w;
	}
	op_lda(w, b, lo);
}

#define alpha_move_const_pool(w, l, t, d, op)				\
	do {								\
		constpool *_c = KaffeJIT_newConstant((t), (d));			\
		label *_l = ((l) ? (l) : KaffeJIT_newLabel());			\
		int _w = (w);						\
									\
		_l->from = 0;						\
		_l->to = (uintp)_c;					\
		_l->at = CODEPC;					\
									\
		/* We shouldn't need _this_much gp space per function.	\
	   	   Hell, we don't allow that much per C object file.	\
		_l->type = Lconstant|Lrelative|Llong16x16;		\
		op_ldah(_w, REG_gp, 0); */				\
									\
		_l->type = Lconstant|Lrelative|Llong16|Lrangecheck;	\
		op(_w, REG_gp, 0);					\
	} while (0)

define_insn(move_int_const, movei_RxC)
{
	alpha_move_const_32(wreg_int(0), const_int(2));
}

/* We manipulate the constpool ourselves here so that (1) we don't waste
   too many registers, and (2) so that we can overlap the long offset
   addition with the actual load.  */

define_insn(move_ref_const, mover_RxC)
{
	long r = const_long(2);
	int w = wreg_ref(0);

	if (alpha_s32_rangecheck(r)) {
		alpha_move_const_32(w, r);
	}
	else {
		alpha_move_const_pool(w, NULL, CPref, r, op_ldq);
	}
}

define_insn(move_long_const, movel_RxC)
{
	long r = const_long(2);
	int w = wreg_long(0);

	if (alpha_s32_rangecheck(r)) {
		alpha_move_const_32(w, r);
	}
	else {
		alpha_move_const_pool(w, NULL, CPlong, r, op_ldq);
	}
}

define_insn(move_label_const, move_RxL)
{
	label* l = const_label(2);
	int w = wreg_ref(0);

	l->type |= Lrelative | Llong16x16 | Lrangecheck;
	l->at = CODEPC;
	l->from = 0;

	op_ldah(w, REG_gp, 0);
	op_lda(w, w, 0);
}

define_insn(move_float_const, movef_RxC)
{
	jvalue d;
	int w;
	
	d.d = const_double(2);
	w = wreg_float(0);

	if (d.d == 0.0) {
		if (((d.j >> 63) & 1) != 0) {
			op_cpysn(REG_fzero, REG_fzero, w);
		}
		else {
			op_fmov(REG_fzero, w);
		}
	}
	else {
		alpha_move_const_pool(w, NULL, CPfloat, d.d, op_lds);
	}
}

define_insn(move_double_const, moved_RxC)
{
	jvalue d;
	int w;

	d.d = const_double(2);
	w = wreg_double(0);

	if (d.d == 0.0) {
		if (((d.j >> 63) & 1) != 0) {
			op_cpysn(REG_fzero, REG_fzero, w);
		}
		else {
			op_fmov(REG_fzero, w);
		}
	}
	else {
		alpha_move_const_pool(w, NULL, CPdouble, d.d, op_ldt);
	}
}

define_insn(move_int, movei_RxR)
{
	/* In general, we win by always forcing the source slot
	   into a register so it's there for later.  */
	int r = rreg_int(2);
	int w = wreg_int(0);

	if (r != w) {
		op_mov(r, w);
	}
}

define_insn(move_ref, mover_RxR)
{
	/* In general, we win by always forcing the source slot
	   into a register so it's there for later.  */
	int r = rreg_ref(2);
	int w = wreg_ref(0);

	if (r != w) {
		op_mov(r, w);
	}
}

define_insn(move_long, movel_RxR)
{
	/* In general, we win by always forcing the source slot
	   into a register so it's there for later.  */
	int r = rreg_long(2);
	int w = wreg_long(0);

	if (r != w) {
		op_mov(r, w);
	}
}

define_insn(move_float, movef_RxR)
{
	/* In general, we win by always forcing the source slot
	   into a register so it's there for later.  */
	int r = rreg_float(2);
	int w = wreg_float(0);

	if (r != w) {
		op_fmov(r, w);
	}
}

define_insn(move_double, moved_RxR)
{
	/* In general, we win by always forcing the source slot
	   into a register so it's there for later.  */
	int r = rreg_double(2);
	int w = wreg_double(0);

	if (r != w) {
		op_fmov(r, w);
	}
}

/* --------------------------------------------------------------------- */

define_insn(add_int_const, addi_RRC)
{
	int o = const_int(2);
	int r = rreg_int(1);
	int w = wreg_int(0);

	if (o >= 0 && o <= 0xFF) {
		op_addl_i(r, o, w);
	}
	else if (o >= -0xFF && o <= 0) {
		op_subl_i(r, -o, w);
	}
	else {
		KAFFEVM_ABORT();
	}
}

define_insn(add_ref_const, addr_RRC)
{
	int o = const_int(2);
	int r = rreg_ref(1);
	int w = wreg_ref(0);

	assert(o >= -0x8000 && o < 0x8000);
	op_lda(w, r, o);
}

define_insn(add_long_const, addl_RRC)
{
	int o = const_int(2);
	int r = rreg_long(1);
	int w = wreg_long(0);

	assert(o >= -0x8000 && o < 0x8000);
	op_lda(w, r, o);
}

define_insn(add_int, addi_RRR)
{
	int r2 = rreg_int(2);
	int r1 = rreg_int(1);
	int w = wreg_int(0);

	op_addl(r1, r2, w);
}

define_insn(add_ref, addr_RRR)
{
	int r2 = rreg_int(2);
	int r1 = rreg_ref(1);
	int w = wreg_ref(0);

	op_addq(r1, r2, w);
}

define_insn(add_long, addl_RRR)
{
	int r2 = rreg_long(2);
	int r1 = rreg_long(1);
	int w = wreg_long(0);

	op_addq(r1, r2, w);
}

define_insn(add_float, addf_RRR)
{
	int r2 = rreg_float(2);
	int r1 = rreg_float(1);
	int w = wreg_float(0);
	int t = ((r1 == w) || (r2 == w)) ? REG_ft : w;

	alpha_jit_info.ieee = 1;
#ifndef AXP_FULL_IEEE_FP
	op_adds(r1, r2, t);
#else
	op_adds_su(r1, r2, t);
	op_trapb();
#endif
	if (t == REG_ft) {
		op_fmov(t, w);
	}
}

define_insn(add_double, addd_RRR)
{
	int r2 = rreg_double(2);
	int r1 = rreg_double(1);
	int w = wreg_double(0);
	int t = ((r1 == w) || (r2 == w)) ? REG_ft : w;

	alpha_jit_info.ieee = 1;
#ifndef AXP_FULL_IEEE_FP
	op_addt(r1, r2, t);
#else
	op_addt_su(r1, r2, t);
	op_trapb();
#endif
	if (t == REG_ft) {
		op_fmov(t, w);
	}
}

define_insn(sub_int_const, subi_RRC)
{
	int o = const_int(2);
	int r = rreg_int(1);
	int w = wreg_int(0);

	if (o >= 0 && o <= 0xFF) {
		op_subl_i(r, o, w);
	}
	else if (o >= -0xFF && o <= 0) {
		op_addl_i(r, -o, w);
	}
	else {
		KAFFEVM_ABORT();
	}
}

define_insn(sub_long_const, subl_RRC)
{
	int o = const_int(2);
	int r = rreg_long(1);
	int w = wreg_long(0);

	assert(o > -0x8000 && o <= 0x8000);
	op_lda(w, r, -o);
}

define_insn(sub_int, subi_RRR)
{
	int r2 = rreg_int(2);
	int r1 = rreg_int(1);
	int w = wreg_int(0);

	op_subl(r1, r2, w);
}

define_insn(sub_long, subl_RRR)
{
	int r2 = rreg_long(2);
	int r1 = rreg_long(1);
	int w = wreg_long(0);

	op_subq(r1, r2, w);
}

define_insn(sub_float, subf_RRR)
{
	int r2 = rreg_float(2);
	int r1 = rreg_float(1);
	int w = wreg_float(0);
	int t = ((r1 == w) || (r2 == w)) ? REG_ft : w;

	alpha_jit_info.ieee = 1;
#ifndef AXP_FULL_IEEE_FP
	op_subs(r1, r2, t);
#else
	op_subs_su(r1, r2, t);
	op_trapb();
#endif
	if (t == REG_ft) {
		op_fmov(t, w);
	}
}

define_insn(sub_double, subd_RRR)
{
	int r2 = rreg_double(2);
	int r1 = rreg_double(1);
	int w = wreg_double(0);
	int t = ((r1 == w) || (r2 == w)) ? REG_ft : w;

	alpha_jit_info.ieee = 1;
#ifndef AXP_FULL_IEEE_FP
	op_subt(r1, r2, t);
#else
	op_subt_su(r1, r2, t);
	op_trapb();
#endif
	if (t == REG_ft) {
		op_fmov(t, w);
	}
}

/* Implement these because the compiler don't know about $zero and will
   attempt to load a constant zero.  */

define_insn(neg_int, negi_RxR)
{
	int r = rreg_int(2);
	int w = wreg_int(0);

	op_subl(REG_zero, r, w);
}

define_insn(neg_long, negl_RxR)
{
	int r = rreg_long(2);
	int w = wreg_long(0);

	op_subq(REG_zero, r, w);
}

define_insn(neg_float, negf_RxR)
{
	int r = rreg_float(2);
	int w = wreg_float(0);

	op_cpysn(r, r, w);
}

define_insn(neg_double, negd_RxR)
{
	int r = rreg_double(2);
	int w = wreg_double(0);

	op_cpysn(r, r, w);
}

define_insn(mul_int_const, muli_RRC)
{
	int o = const_int(2);
	int r = rreg_int(1);
	int w = wreg_int(0);

	assert(o >= 0 && o <= 0xFF);
	switch (o) {
	case 0:
		op_mov(REG_zero, w);
		break;
	case 1:
		op_mov(r, w);
		break;
	case 2:
		op_addl(r, r, w);
		break;
	case 3:
		op_s4subl(r, r, w);
		break;
	case 4:
		op_s4addl(r, REG_zero, w);
		break;
	case 5:
		op_s4addl(r, r, w);
		break;
	case 7:
		op_s8subl(r, r, w);
		break;
	case 8:
		op_s8addl(r, REG_zero, w);
		break;
	case 9:
		op_s8addl(r, r, w);
		break;
	default:
		op_mull_i(r, o, w);
		break;
	}
}

define_insn(mul_long_const, mull_RRC)
{
	int o = const_int(2);
	int r = rreg_long(1);
	int w = wreg_long(0);

	assert(o >= 0 && o <= 0xFF);
	switch (o) {
	case 0:
		op_mov(REG_zero, w);
		break;
	case 1:
		op_mov(r, w);
		break;
	case 2:
		op_addq(r, r, w);
		break;
	case 3:
		op_s4subq(r, r, w);
		break;
	case 4:
		op_s4addq(r, REG_zero, w);
		break;
	case 5:
		op_s4addq(r, r, w);
		break;
	case 7:
		op_s8subq(r, r, w);
		break;
	case 8:
		op_s8addq(r, REG_zero, w);
		break;
	case 9:
		op_s8addq(r, r, w);
		break;
	default:
		op_mulq_i(r, o, w);
		break;
	}
}

define_insn(mul_int, muli_RRR)
{
	int r2 = rreg_int(2);
	int r1 = rreg_int(1);
	int w = wreg_int(0);

	op_mull(r1, r2, w);
}

define_insn(mul_long, mull_RRR)
{
	int r2 = rreg_long(2);
	int r1 = rreg_long(1);
	int w = wreg_long(0);

	op_mulq(r1, r2, w);
}

define_insn(mul_float, mulf_RRR)
{
	int r2 = rreg_float(2);
	int r1 = rreg_float(1);
	int w = wreg_float(0);
	int t = ((r1 == w) || (r2 == w)) ? REG_ft : w;

	alpha_jit_info.ieee = 1;
#ifndef AXP_FULL_IEEE_FP
	op_muls(r1, r2, t);
#else
	op_muls_su(r1, r2, t);
	op_trapb();
#endif
	if (t == REG_ft) {
		op_fmov(t, w);
	}
}

define_insn(mul_double, muld_RRR)
{
	int r2 = rreg_double(2);
	int r1 = rreg_double(1);
	int w = wreg_double(0);
	int t = ((r1 == w) || (r2 == w)) ? REG_ft : w;

	alpha_jit_info.ieee = 1;
#ifndef AXP_FULL_IEEE_FP
	op_mult(r1, r2, t);
#else
	op_mult_su(r1, r2, t);
	op_trapb();
#endif
	if (t == REG_ft) {
		op_fmov(t, w);
	}
}

/* While we don't have a native integer division instruction, we do have
   library functions with extra friendly calling conventions.  */

static void
alpha_division(sequence *s, int type, void *fn)
{
	extern void alpha_do_call_div(void);

	int r1 = NOREG, r2 = NOREG;

	/* Move the arguments into place.  */

	if (slotInRegister(1, type)) {
		r1 = fastSlotRegister(seq_slot(s, 1), type, rread);
	}
	if (slotInRegister(2, type)) {
		r2 = fastSlotRegister(seq_slot(s, 2), type, rread);
	}

	if (r2 == REG_t10 && r1 == REG_t11) {
		/* Urg.  They are just in the wrong place.  Exchange them
		   and diddle the compiler's structures.  */
		register_invalidate(REG_t10);
		register_invalidate(REG_t11);
		forceRegister(seq_slot(s, 1), REG_t10, type);
		forceRegister(seq_slot(s, 2), REG_t11, type);

		op_mov(REG_t10, REG_at);
		op_mov(REG_t11, REG_t10);
		op_mov(REG_at, REG_t11);
	}
	else {
		if (r2 == REG_t10) {
			forceRegister(seq_slot(s, 2), REG_t11, type);
			op_mov(REG_t10, REG_t11);
		}
		if (r1 == NOREG) {
			r1 = slowSlotOffset(seq_slot(s, 1), type, rread);
			assert(r1 >= -0x8000 && r1 < 0x8000);
			forceRegister(seq_slot(s, 1), REG_t10, type);
			if (type == Rint) {
				op_ldl(REG_t10, REG_fp, r1);
			}
			else {
				op_ldq(REG_t10, REG_fp, r1);
			}
		}
		else if (r1 != REG_t10) {
			forceRegister(seq_slot(s, 1), REG_t10, type);
			op_mov(r1, REG_t10);
		}
		if (r2 == NOREG) {
			r2 = slowSlotOffset(seq_slot(s, 2), type, rread);
			assert(r2 >= -0x8000 && r2 < 0x8000);
			forceRegister(seq_slot(s, 2), REG_t11, type);
			if (type == Rint) {
				op_ldl(REG_t11, REG_fp, r2);
			}
			else {
				op_ldq(REG_t11, REG_fp, r2);
			}
		}
		else if (r2 != REG_t10 && r2 != REG_t11) {
			forceRegister(seq_slot(s, 2), REG_t11, type);
			op_mov(r2, REG_t11);
		}
	}

	clobberRegister(REG_t9);
	clobberRegister(REG_pv);

	alpha_move_const_pool(REG_pv, NULL, CPref, fn, op_ldq);
	op_jsr(REG_t9, REG_pv, 0);

	alpha_ldgp(REG_t9);

	forceRegister(seq_dst(s), REG_pv, type);
}

define_insn(div_int, divi_RRR)
{
	extern void __divl(void);
	alpha_division(s, Rint, &__divl);
}

define_insn(div_long, divl_RRR)
{
	extern void __divq(void);
	alpha_division(s, Rlong, &__divq);
}

define_insn(div_float, divf_RRR)
{
	int r2 = rreg_float(2);
	int r1 = rreg_float(1);
	int w = wreg_float(0);
	int t = ((r1 == w) || (r2 == w)) ? REG_ft : w;

	alpha_jit_info.ieee = 1;
#ifndef AXP_FULL_IEEE_FP
	op_divs(r1, r2, t);
#else
	op_divs_su(r1, r2, t);
	op_trapb();
#endif
	if (t == REG_ft) {
		op_fmov(t, w);
	}
}

define_insn(div_double, divd_RRR)
{
	int r2 = rreg_double(2);
	int r1 = rreg_double(1);
	int w = wreg_double(0);
	int t = ((r1 == w) || (r2 == w)) ? REG_ft : w;

	alpha_jit_info.ieee = 1;
#ifndef AXP_FULL_IEEE_FP
	op_divt(r1, r2, t);
#else
	op_divt_su(r1, r2, t);
	op_trapb();
#endif
	if (t == REG_ft) {
		op_fmov(t, w);
	}
}

define_insn(rem_int, remi_RRR)
{
	extern void __reml(void);
	alpha_division(s, Rint, &__reml);
}

define_insn(rem_long, reml_RRR)
{
	extern void __remq(void);
	alpha_division(s, Rlong, &__remq);
}

/* --------------------------------------------------------------------- */

static inline void
alpha_and_const(int r, long o, int w)
{
	if (o >= 0 && o <= 0xFF) {
		op_and_i(r, o, w);
	}
	else if (~o >= 0 && ~o <= 0xFF) {
		op_andnot_i(r, ~o, w);
	}
	else {
		/* Our rangecheck asserts that this is the only other
		   option here.  */
		o = alpha_zapnot_const(o);
		op_zapnot_i(r, o, w);
	}
}

define_insn(and_int_const, andi_RRC)
{
	alpha_and_const(rreg_int(1), const_int(2), wreg_int(0));
}

define_insn(and_long_const, andl_RRC)
{
	alpha_and_const(rreg_long(1), const_long(2), wreg_long(0));
}

define_insn(and_int, andi_RRR)
{
	int r2 = rreg_int(2);
	int r1 = rreg_int(1);
	int w = wreg_int(0);

	op_and(r1, r2, w);
}

define_insn(and_long, andl_RRR)
{
	int r2 = rreg_long(2);
	int r1 = rreg_long(1);
	int w = wreg_long(0);

	op_and(r1, r2, w);
}

define_insn(or_int, ori_RRR)
{
	int r2 = rreg_int(2);
	int r1 = rreg_int(1);
	int w = wreg_int(0);

	op_or(r1, r2, w);
}

define_insn(or_long, orl_RRR)
{
	int r2 = rreg_long(2);
	int r1 = rreg_long(1);
	int w = wreg_long(0);

	op_or(r1, r2, w);
}

define_insn(xor_int, xori_RRR)
{
	int r2 = rreg_int(2);
	int r1 = rreg_int(1);
	int w = wreg_int(0);

	op_xor(r1, r2, w);
}

define_insn(xor_long, xorl_RRR)
{
	int r2 = rreg_long(2);
	int r1 = rreg_long(1);
	int w = wreg_long(0);

	op_xor(r1, r2, w);
}

define_insn(ashr_int_const, ashri_RRC)
{
	int o = const_int(2);
	int r = rreg_int(1);
	int w = wreg_int(0);

	op_sra_i(r, o, w);
}

define_insn(ashr_long_const, ashrl_RRC)
{
	int o = const_int(2);
	int r = rreg_long(1);
	int w = wreg_long(0);

	op_sra_i(r, o, w);
}

define_insn(ashr_int, ashri_RRR)
{
	int r2 = rreg_int(2);
	int r1 = rreg_int(1);
	int w = wreg_int(0);

	op_sra(r1, r2, w);
}

define_insn(ashr_long, ashrl_RRR)
{
	int r2 = rreg_long(2);
	int r1 = rreg_long(1);
	int w = wreg_long(0);

	op_sra(r1, r2, w);
}

define_insn(lshr_int_const, lshri_RRC)
{
	int o = const_int(2);
	int r = rreg_int(1);
	int w = wreg_int(0);

	op_zapnot_i(r, 15, w);
	op_srl_i(w, o, w);
}

define_insn(lshr_long_const, lshrl_RRC)
{
	int o = const_int(2);
	int r = rreg_long(1);
	int w = wreg_long(0);

	op_srl_i(r, o, w);
}

define_insn(lshr_int, lshri_RRR)
{
	int r2 = rreg_int(2);
	int r1 = rreg_int(1);
	int w = wreg_int(0);

	op_zapnot_i(r1, 15, w);
	op_srl(w, r2, w);
}

define_insn(lshr_long, lshrl_RRR)
{
	int r2 = rreg_long(2);
	int r1 = rreg_long(1);
	int w = wreg_long(0);

	op_srl(r1, r2, w);
}

define_insn(lshl_int_const, lshli_RRC)
{
	int o = const_int(2);
	int r = rreg_int(1);
	int w = wreg_int(0);

	/* Attempt to use an addition instruction because (1) they slot
	   better on the EV5, and (2) they have better latency on the EV4.  */
	switch (o) {
	case 0:
		if (r != w) {
			op_mov(r, w);
		}
		break;
	case 1:
		op_addl(r, r, w);
		break;
	case 2:
		op_s4addl(r, REG_zero, w);
		break;
	case 3:
		op_s8addl(r, REG_zero, w);
		break;
	default:
		op_sll_i(r, o, w);
		op_addl(w, REG_zero, w);	/* care for proper overflow. */
		break;
	}
}

define_insn(lshl_long_const, lshll_RRC)
{
	int o = const_int(2);
	int r = rreg_long(1);
	int w = wreg_long(0);

	/* Attempt to use an addition instruction because (1) they slot
	   better on the EV5, and (2) they have better latency on the EV4.  */
	switch (o) {
	case 0:
		if (r != w) {
			op_mov(r, w);
		}
		break;
	case 1:
		op_addq(r, r, w);
		break;
	case 2:
		op_s4addq(r, REG_zero, w);
		break;
	case 3:
		op_s8addq(r, REG_zero, w);
		break;
	default:
		op_sll_i(r, o, w);
		break;
	}
}

define_insn(lshl_int, lshli_RRR)
{
	int r2 = rreg_int(2);
	int r1 = rreg_int(1);
	int w = wreg_int(0);

	op_sll(r1, r2, w);
	op_addl(w, REG_zero, w);	/* care for proper overflow. */
}

define_insn(lshl_long, lshll_RRR)
{
	int r2 = rreg_long(2);
	int r1 = rreg_long(1);
	int w = wreg_long(0);

	op_sll(r1, r2, w);
}

/* --------------------------------------------------------------------- */

define_insn(load_int, loadi_RxR)
{
	int r = rreg_ref(2);
	int w = wreg_int(0);

	op_ldl(w, r, 0);
}

define_insn(load_offset_int, loadi_RRC)
{
	int o = const_int(2);
	int r = rreg_ref(1);
	int w = wreg_int(0);

	assert(o >= -0x8000 && o < 0x8000);
	op_ldl(w, r, o);
}

define_insn(load_ref, loadr_RxR)
{
	int r = rreg_ref(2);
	int w = wreg_ref(0);

	op_ldq(w, r, 0);
}

define_insn(load_offset_ref, loadr_RRC)
{
	int o = const_int(2);
	int r = rreg_ref(1);
	int w = wreg_ref(0);

	assert(o >= -0x8000 && o < 0x8000);
	op_ldq(w, r, o);
}

define_insn(load_long, loadl_RxR)
{
	int r = rreg_ref(2);
	int w = wreg_long(0);

	op_ldq(w, r, 0);
}

define_insn(load_offset_long, loadl_RRC)
{
	int o = const_int(2);
	int r = rreg_ref(1);
	int w = wreg_long(0);

	assert(o >= -0x8000 && o < 0x8000);
	op_ldq(w, r, o);
}

define_insn(load_float, loadf_RxR)
{
	int r = rreg_ref(2);
	int w = wreg_float(0);

	op_lds(w, r, 0);
}

define_insn(load_double, loadd_RxR)
{
	int r = rreg_ref(2);
	int w = wreg_double(0);

	op_ldt(w, r, 0);
}

define_insn(load_byte, loadb_RxR)
{
	int r = rreg_ref(2);
	int w = wreg_int(0);

	if (alpha_have_bwx()) {
		op_ldbu(w, r, 0);
		op_sextb(w, w);
	}
	else {
#if 0
		op_ldq_u(w, r, 0);
		op_addq_i(r, 1, REG_at);
		op_extqh(w, REG_at, w);
		op_sra_i(w, 56, w);
#endif
		op_mov(r, REG_at);
                op_ldq_u(w, r, 0);
                op_extbl(w, REG_at, w);
                op_sll_i(w, 56, w);
                op_sra_i(w, 56, w);    
	}
}

define_insn(load_char, loadc_RxR)
{
	int r = rreg_ref(2);
	int w = wreg_int(0);

	if (alpha_have_bwx()) {
		op_ldwu(w, r, 0);
	}
	else {
		/* Note that an aligned pointer is assumed here.  */
		op_ldq_u(w, r, 0);
		op_extwl(w, r, w);
	}
}

define_insn(load_short, loads_RxR)
{
	int r = rreg_ref(2);
	int w = wreg_int(0);

	if (alpha_have_bwx()) {
		op_ldwu(w, r, 0);
		op_sextw(w, w);
	}
	else {
		/* Note that an aligned pointer is assumed here.  */
		op_ldq_u(w, r, 0);
		op_addq_i(r, 2, REG_at);
		op_extqh(w, REG_at, w);
		op_sra_i(w, 48, w);
	}
}

define_insn(store_int, storei_xRR)
{
	int r = rreg_int(2);
	int w = rreg_ref(1);

	op_stl(r, w, 0);
}

define_insn(store_offset_int, storei_xRRC)
{
	int o = const_int(2);
	int w = rreg_ref(1);
	int r = rreg_int(0);

	assert(o >= -0x8000 && o < 0x8000);
	op_stl(r, w, o);
}

define_insn(store_ref, storer_xRR)
{
	int r = rreg_ref(2);
	int w = rreg_ref(1);

	op_stq(r, w, 0);
}

define_insn(store_offset_ref, storer_xRRC)
{
	int o = const_int(2);
	int w = rreg_ref(1);
	int r = rreg_ref(0);

	assert(o >= -0x8000 && o < 0x8000);
	op_stq(r, w, o);
}

define_insn(store_long, storel_xRR)
{
	int w = rreg_ref(1);
	int r = rreg_long(2);

	op_stq(r, w, 0);
}

define_insn(store_offset_long, storel_xRRC)
{
	int o = const_int(2);
	int w = rreg_ref(1);
	int r = rreg_long(0);

	assert(o >= -0x8000 && o < 0x8000);
	op_stq(r, w, o);
}

define_insn(store_float, storef_xRR)
{
	int r = rreg_float(2);
	int w = rreg_ref(1);

	op_sts(r, w, 0);
}

define_insn(store_double, stored_xRR)
{
	int r = rreg_double(2);
	int w = rreg_ref(1);

	op_stt(r, w, 0);
}

define_insn(store_byte, storeb_xRR)
{
	int r = rreg_int(2);
	int w = rreg_ref(1);

	if (alpha_have_bwx()) {
		op_stb(r, w, 0);
	}
	else {
		op_ldq_u(REG_at, w, 0);
		clobberRegister(r);
		op_insbl(r, w, r);
		op_mskbl(REG_at, w, REG_at);
		op_or(r, REG_at, r);
		op_stq_u(r, w, 0);
	}
}

define_insn(store_short, stores_xRR)
{
	int r = rreg_int(2);
	int w = rreg_ref(1);

	if (alpha_have_bwx()) {
		op_stw(r, w, 0);
	}
	else {
		/* Note that an aligned pointer is assumed here.  */
		op_ldq_u(REG_at, w, 0);
		clobberRegister(r);
		op_inswl(r, w, r);
		op_mskwl(REG_at, w, REG_at);
		op_or(r, REG_at, r);
		op_stq_u(r, w, 0);
	}
}

/* --------------------------------------------------------------------- */

define_insn(cvt_int_byte, cvtib_RxR)
{
	int r = rreg_int(2);
	int w = wreg_int(0);

	if (alpha_have_bwx()) {
		op_sextb(r, w);
	}
	else {
		op_sll_i(r, 56, w);
		op_sra_i(w, 56, w);
	}
}

define_insn(cvt_int_short, cvtis_RxR)
{
	int r = rreg_int(2);
	int w = wreg_int(0);

	if (alpha_have_bwx()) {
		op_sextw(r, w);
	}
	else {
		op_sll_i(r, 48, w);
		op_sra_i(w, 48, w);
	}
}

define_insn(cvt_int_long, cvtil_RxR)
{
	int r = rreg_int(2);
	int w = wreg_long(0);

	op_addl(r, REG_zero, w);
}

define_insn(cvt_long_int, cvtli_RxR)
{
	int r = rreg_long(2);
	int w = wreg_int(0);

	op_addl(r, REG_zero, w);
}

define_insn(cvt_int_float, cvtif_RxR)
{
	int r = rreg_float(2);
	int w = wreg_float(0);
	
	alpha_jit_info.ieee = 1;
	op_cvtlq(r, REG_ft);
	op_cvtqs(REG_ft, w);
	op_trapb();
}

define_insn(cvt_long_float, cvtlf_RxR)
{
	int r = rreg_double(2);
	int w = wreg_float(0);
	int t = (r == w) ? REG_ft : w;
	
	alpha_jit_info.ieee = 1;
	op_cvtqs(r, t);
	op_trapb();
	if (t == REG_ft) {
		op_fmov(t, w);
	}
}

define_insn(cvt_int_double, cvtid_RxR)
{
	int r = rreg_float(2);
	int w = wreg_double(0);

	alpha_jit_info.ieee = 1;
	op_cvtlq(r, REG_ft);
	op_cvtqt(REG_ft, w);
	op_trapb();
}

define_insn(cvt_long_double, cvtld_RxR)
{
	int r = rreg_double(2);
	int w = wreg_double(0);
	int t = (r == w) ? REG_ft : w;

	alpha_jit_info.ieee = 1;
	op_cvtqt(r, t);
	op_trapb();
	if (t == REG_ft) {
		op_fmov(t, w);
	}
}

#if 0
/* These functions does not work correctly, use soft_cvtXX */

define_insn(cvt_float_int, cvtfi_RxR)
{
	int r = rreg_float(2);
	int w = wreg_float(0);

	alpha_jit_info.ieee = 1;
	if (r == w) {
		/* XXX allocate three temp registers ? */
		op_cvtst_s(r, REG_ft);
		op_trapb();
		op_cvttq_svc(REG_ft, w);
		op_trapb();
		op_cvtql_sv(w, REG_ft);
		op_trapb();
		op_fmov(REG_ft, w);
	}
	else {
		op_cvtst_s(r, w);
		op_cvttq_svc(w, REG_ft);
		/* XXX allocate another temp register ? */
		op_trapb();
		op_cvtql_sv(REG_ft, w);
		op_trapb();
	}
}

define_insn(cvt_float_long, cvtfl_RxR)
{
	int r = rreg_float(2);
	int w = wreg_double(0);

	alpha_jit_info.ieee = 1;
	op_cvtst_s(r, REG_ft);
	if (r == w) {
		/* XXX allocate another temp register ? */
		op_trapb();
	}
	op_cvttq_svc(REG_ft, w);
	op_trapb();
}

define_insn(cvt_double_int, cvtdi_RxR)
{
	int r = rreg_double(2);
	int w = wreg_float(0);

	alpha_jit_info.ieee = 1;
	op_cvttq_svc(r, REG_ft);
	if (r == w) {
		/* XXX allocate another temp register ? */
		op_trapb();
	}
	op_cvtql_sv(REG_ft, w);
	op_trapb();
}

define_insn(cvt_double_long, cvtdl_RxR)
{
	int r = rreg_double(2);
	int w = wreg_double(0);
	int t = (r == w) ? REG_ft : w;

	alpha_jit_info.ieee = 1;
	op_cvttq_svc(r, t);
	op_trapb();
	if (t == REG_ft) {
		op_fmov(t, w);
	}
}
#endif

define_insn(cvt_float_double, cvtfd_RxR)
{
	int r = rreg_float(2);
	int w = wreg_double(0);
	int t = (r == w) ? REG_ft : w;

	alpha_jit_info.ieee = 1;
	op_cvtst_s(r, t);
	op_trapb();
	if (t == REG_ft) {
		op_fmov(t, w);
	}
}

define_insn(cvt_double_float, cvtdf_RxR)
{
	int r = rreg_double(2);
	int w = wreg_float(0);
	int t = (r == w) ? REG_ft : w;

	alpha_jit_info.ieee = 1;
#ifndef AXP_FULL_IEEE_FP
	op_cvtts(r, t);
#else
	op_cvtts_su(r, t);
	op_trapb();
#endif
	if (t == REG_ft) {
		op_fmov(t, w);
	}
}

/* --------------------------------------------------------------------- */

define_insn(lcmp, lcmp_RRR)
{
	int r2 = rreg_long(2);
	int r1 = rreg_long(1);
	int w = wreg_int(0);

	op_cmplt(r1, r2, w);
	op_cmplt(r2, r1, REG_at);
	op_subl(w, REG_at, w);
}

/* --------------------------------------------------------------------- */

define_insn(build_key, set_word_xxC)
{
	jint val = const_int(2);

	debug((".long %08x\n", val));
	LOUT(val);
}

define_insn(build_code_ref, set_wordpc_xxC)
{
	label* l = const_label(2);

	l->type |= Lrelative | Llong | Lrangecheck;
	l->at = CODEPC;
	l->from = 0;

	debug((".gprel ?\n"));
	LOUT(0);
}

define_insn(load_code_ref, loadpc_RxR)
{
	int r = rreg_long(2);
	int w = wreg_long(0);

	op_ldl(w, r, 0);
	op_addq(w, REG_gp, w);
}

/* --------------------------------------------------------------------- */

define_insn(set_label, set_label_xxC)
{
	label* l = const_label(2);

	l->to = CODEPC;
}

define_insn(branch, branch_xCC)
{
	label* l = const_label(1);
	int bt = const_int(2);

	l->type |= Llong21 | Lrelative | Lrangecheck;
	l->at = CODEPC;

	switch (bt) {
	case ba:
		op_br(REG_zero, 0);
		break;
	default:
		KAFFEVM_ABORT();
	}

	l->from = CODEPC;
}

static inline void
alpha_cbranch(int r1, int r2, label *l, int bt)
{
	int ne;

	/* Do the compare and branch with cmp{eq,le,lt} and b{ne,eq} because
	   that slots better on the ev5 than a sub and b*.  */

	switch (bt) {
	case beq:
		op_cmpeq(r1, r2, REG_at);
		ne = 1;
		break;
	case bne:
		op_cmpeq(r1, r2, REG_at);
		ne = 0;
		break;
	case blt:
		op_cmplt(r1, r2, REG_at);
		ne = 1;
		break;
	case ble:
		op_cmple(r1, r2, REG_at);
		ne = 1;
		break;
	case bgt:
		op_cmple(r1, r2, REG_at);
		ne = 0;
		break;
	case bge:
		op_cmplt(r1, r2, REG_at);
		ne = 0;
		break;
	case bult:
		op_cmpult(r1, r2, REG_at);
		ne = 1;
		break;
	default:
		KAFFEVM_ABORT();
	}

	l->type |= Llong21 | Lrelative | Lrangecheck;
	l->at = CODEPC;

	if (ne) {
		op_bne(REG_at, 0);
	}
	else {
		op_beq(REG_at, 0);
	}

	l->from = CODEPC;
}

define_insn(cbranch_int, cbranchi_xRRLC)
{
	alpha_cbranch(rreg_int(1), rreg_int(2), const_label(3), const_int(4));
}

define_insn(cbranch_ref, cbranchr_xRRLC)
{
	alpha_cbranch(rreg_ref(1), rreg_ref(2), const_label(3), const_int(4));
}

static void
alpha_cbranch_const(int r, long o, label *l, int bt, bool is_int)
{
	int t;

	if (o == 0) {
		t = r;
	}
	else if (o > 0 && o <= 0xFF) {
		/* Do the compare and branch with cmp{eq,le,lt} and b{ne,eq}
		   because that slots better on the ev5 than a sub and b*.  */

		switch (bt) {
		case beq:
			op_cmpeq_i(r, o, REG_at);
			bt = bne;
			break;
		case bne:
			op_cmpeq_i(r, o, REG_at);
			break;
		case blt:
			op_cmplt_i(r, o, REG_at);
			bt = bne;
			break;
		case ble:
			op_cmple_i(r, o, REG_at);
			bt = bne;
			break;
		case bgt:
			op_cmple_i(r, o, REG_at);
			bt = beq;
			break;
		case bge:
			op_cmplt_i(r, o, REG_at);
			bt = beq;
			break;
		case bult:
			op_cmpult_i(r, o, REG_at);
			bt = bne;
			break;
		default:
			KAFFEVM_ABORT();
		}
		t = REG_at;
	}
	else if (o < 0 && o >= -0xFF) {
		if (is_int) {
			op_addl_i(r, -o, REG_at);
		}
		else {
			op_addq_i(r, -o, REG_at);
		}
		t = REG_at;
	}
	else {
		/* The rangechecks should have been set up to prevent this.  */
		assert(!is_int && o >= -0x8000 && o < 0x8000);
		op_lda(REG_at, r, -o);
		t = REG_at;
	}

	l->type |= Llong21 | Lrelative | Lrangecheck;
	l->at = CODEPC;

	switch (bt) {
	case beq:
		op_beq(t, 0);
		break;
	case bne:
		op_bne(t, 0);
		break;
	case blt:
		op_blt(t, 0);
		break;
	case ble:
		op_ble(t, 0);
		break;
	case bgt:
		op_bgt(t, 0);
		break;
	case bge:
		op_bge(t, 0);
		break;
	default:
		KAFFEVM_ABORT();
	}

	l->from = CODEPC;
}

define_insn(cbranch_int_const, cbranchi_xRCLC)
{
	alpha_cbranch_const(rreg_int(1), const_int(2), const_label(3),
			    const_int(4), true);
}

define_insn(cbranch_ref_const, cbranchr_xRCLC)
{
	alpha_cbranch_const(rreg_ref(1), const_long(2), const_label(3),
			    const_int(4), false);
}

define_insn(branch_indirect, branch_indirect_xRC)
{
	int r = rreg_long(1);
	assert(const_int(2) == ba);

	op_jmp(REG_zero, r, 0);
}

define_insn(call_ref, call_xCC)
{
	/* Since we can't tell where we are finally going to be located,
	   we can't tell if we are going to be in range for a bsr.
	   Nevertheless, we implement this insn such that we don't wind
	   up muddying too many registers, since we have to have the
	   value in $pv due to calling convention constraints.  */

        label* l = const_label(1);
        assert(const_int(2) == ba);

#if 0
	/* For normal function calls, these should have been flushed
	   already.  For specials like soft_badarrayindex that aren't
	   considered in the the normal basic block divisions, this
	   is in fact wrong, because this flush isn't happening before
	   the test, and so we'll later think that the up-to-date
	   value for the slot is on the stack when it isn't.  */
	clobberRegister(REG_pv);
	clobberRegister(REG_ra);
#endif

	alpha_move_const_pool(REG_pv, l, CPref, l->to, op_ldq);

	op_jsr(REG_ra, REG_pv, 0);

	alpha_ldgp(REG_ra);
}

define_insn(call, call_xRC)
{
	int r;

	assert(const_int(2) == ba);

	/* Like call_xCC, clobbers do not belong here, even though we
	   kill $ra and $pv.  */

	if (slotInRegister(1, Rref)) {
		r = rreg_ref(1);
		if (r != REG_pv) {
			op_mov(r, REG_pv);
		}
	}
	else {
		r = rslot_ref(1);
		assert(r >= -0x8000 && r < 0x8000);
		op_ldq(REG_pv, REG_fp, r);
		r = REG_pv;
	}

	op_jsr(REG_ra, r, 0);
	alpha_ldgp(REG_ra);
}

static void
alpha_pusharg_integer(sequence *s, int type)
{
	int a = const_int(2);
	int w, r;

	if (a < 6) {
		w = REG_a0 + a;
		if (slotInRegister(1, type)) {
			r = fastSlotRegister(seq_slot(s, 1), type, rread);
			if (r != w) {
				clobberRegister(w);
				op_mov(r, w);
			}
		}
		else {
			r = slowSlotOffset(seq_slot(s, 1), type, rread);
			assert(r >= -0x8000 && r < 0x8000);
			clobberRegister(w);
			if (type == Rint) {
				op_ldl(w, REG_fp, r);
			}
			else {
				op_ldq(w, REG_fp, r);
			}
		}
	}
	else {
		w = SLOT2PUSHOFFSET(a);
		if (slotInRegister(1, type)) {
			r = fastSlotRegister(seq_slot(s, 1), type, rread);
		}
		else {
			r = slowSlotOffset(seq_slot(s, 1), type, rread);
			assert(r >= -0x8000 && r < 0x8000);
			if (type == Rint) {
				op_ldl(REG_at, REG_fp, r);
			}
			else {
				op_ldq(REG_at, REG_fp, r);
			}
			r = REG_at;
		}
		assert(w >= -0x8000 && w < 0x8000);
		op_stq(r, REG_sp, w);
	}
}

define_insn(pusharg_int, pushi_xRC)
{
	alpha_pusharg_integer(s, Rint);
}

define_insn(push_ref, pushr_xRC)
{
	alpha_pusharg_integer(s, Rref);
}

define_insn(push_long, pushl_xRC)
{
	alpha_pusharg_integer(s, Rlong);
}

static void
alpha_pusharg_integer_const(long c, int a)
{
	int w;
	if (a < 6) {
		w = REG_a0 + a;
		clobberRegister(w);
		if (alpha_s32_rangecheck(c)) {
			alpha_move_const_32(w, c);
		}
		else {
			alpha_move_const_pool(w, NULL, CPlong, c, op_ldq);
		}
	}
	else {
		w = SLOT2PUSHOFFSET(a);
		if (c == 0) {
			op_stq(REG_zero, REG_sp, w);
		}
		else if (alpha_s32_rangecheck(c)) {
			alpha_move_const_32(REG_at, c);
			op_stq(REG_at, REG_sp, w);
		}
		else {
			alpha_move_const_pool(REG_at, NULL, CPlong, c, op_ldq);
			op_stq(REG_at, REG_sp, w);
		}
	}
}

define_insn(pusharg_int_const, pushi_xCC)
{
	alpha_pusharg_integer_const(const_int(1), const_int(2));
}

define_insn(pusharg_ref_const, pushr_xCC)
{
	alpha_pusharg_integer_const(const_long(1), const_int(2));
}

define_insn(pusharg_long_const, pushl_xCC)
{
	alpha_pusharg_integer_const(const_long(1), const_int(2));
}

define_insn(push_float, pushf_xRC)
{
	int a = const_int(2);
	int r, w;

	if (a < 6) {
		w = REG_fa0 + a;
		if (slotInRegister(1, Rfloat)) {
			r = fastSlotRegister(seq_slot(s, 1), Rfloat, rread);
			if (r != w) {
				clobberRegister(w);
				op_fmov(r, w);
			}
		}
		else {
			r = rslot_float(1);
			assert(r >= -0x8000 && r < 0x8000);
			clobberRegister(w);
			op_lds(w, REG_fp, r);
		}
	}
	else {
		w = SLOT2PUSHOFFSET(a);
		r = rreg_float(1);
		assert(w >= -0x8000 && w < 0x8000);
		op_sts(r, REG_sp, w);
	}
}

define_insn(push_double, pushd_xRC)
{
	int a = const_int(2);
	int r, w;

	if (a < 6) {
		w = REG_fa0 + a;
		if (slotInRegister(1, Rdouble)) {
			r = fastSlotRegister(seq_slot(s, 1), Rdouble, rread);
			if (r != w) {
				clobberRegister(w);
				op_fmov(r, w);
			}
		}
		else {
			r = rslot_double(1);
			assert(r >= -0x8000 && r < 0x8000);
			clobberRegister(w);
			op_ldt(w, REG_fp, r);
		}
	}
	else {
		w = SLOT2PUSHOFFSET(a);
		r = rreg_double(1);
		assert(w >= -0x8000 && w < 0x8000);
		op_stt(r, REG_sp, w);
	}
}

define_insn(popargs, popargs_xxC)
{
}

define_insn(return_int, returni_Rxx)
{
	forceRegister(seq_dst(s), REG_v0, Rint);
}

define_insn(return_ref, returnr_Rxx)
{
	forceRegister(seq_dst(s), REG_v0, Rref);
}

define_insn(return_long, returnl_Rxx)
{
	forceRegister(seq_dst(s), REG_v0, Rlong);
}

define_insn(return_float, returnf_Rxx)
{
	forceRegister(seq_dst(s), REG_f0, Rfloat);
}

define_insn(return_double, returnd_Rxx)
{
	forceRegister(seq_dst(s), REG_f0, Rdouble);
}

define_insn(returnarg_int, returnargi_xxR)
{
	int r;

	if (slotInRegister(2, Rint)) {
		r = rreg_int(2);
		if (r != REG_v0) {
			op_mov(r, REG_v0);
		}
	}
	else {
		r = rslot_int(2);
		assert(r >= -0x8000 && r < 0x8000);
		op_ldl(REG_v0, REG_fp, r);
	}
}

define_insn(returnarg_ref, returnargr_xxR)
{
	int r;

	if (slotInRegister(2, Rref)) {
		r = rreg_ref(2);
		if (r != REG_v0) {
			op_mov(r, REG_v0);
		}
	}
	else {
		r = rslot_ref(2);
		assert(r >= -0x8000 && r < 0x8000);
		op_ldq(REG_v0, REG_fp, r);
	}
}

define_insn(returnarg_long, returnargl_xxR)
{
	int r;

	if (slotInRegister(2, Rlong)) {
		r = rreg_long(2);
		if (r != REG_v0) {
			op_mov(r, REG_v0);
		}
	}
	else {
		r = rslot_long(2);
		assert(r >= -0x8000 && r < 0x8000);
		op_ldq(REG_v0, REG_fp, r);
	}
}

define_insn(returnarg_float, returnargf_xxR)
{
	int r;

	if (slotInRegister(2, Rfloat)) {
		r = rreg_float(2);
		if (r != REG_f0) {
			op_fmov(r, REG_f0);
		}
	}
	else {
		r = rslot_float(2);
		assert(r >= -0x8000 && r < 0x8000);
		op_lds(REG_f0, REG_fp, r);
	}
}

define_insn(returnarg_double, returnargd_xxR)
{
	int r;

	if (slotInRegister(2, Rdouble)) {
		r = rreg_double(2);
		if (r != REG_f0) {
			op_fmov(r, REG_f0);
		}
	}
	else {
		r = rslot_double(2);
		assert(r >= -0x8000 && r < 0x8000);
		op_ldt(REG_f0, REG_fp, r);
	}
}

/* --------------------------------------------------------------------- */

define_insn(call_soft, call_soft_xCC)
{
        label* l = const_label(1);
        assert(const_int(2) == ba);

	alpha_move_const_pool(REG_pv, l, CPref, l->to, op_ldq);
	op_jsr(REG_ra, REG_pv, 0);

	alpha_ldgp(REG_ra);
}
