// Inferno utils/8c/cgen64.c
// http://code.google.com/p/inferno-os/source/browse/utils/8c/cgen64.c
//
//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
//	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
//	Portions Copyright © 1997-1999 Vita Nuova Limited
//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
//	Portions Copyright © 2004,2006 Bruce Ellis
//	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include "gc.h"

void
zeroregm(Node *n)
{
	gins(AMOVL, nodconst(0), n);
}

/* do we need to load the address of a vlong? */
int
vaddr(Node *n, int a)
{
	switch(n->op) {
	case ONAME:
		if(a)
			return 1;
		return !(n->class == CEXTERN || n->class == CGLOBL || n->class == CSTATIC);

	case OCONST:
	case OREGISTER:
	case OINDREG:
		return 1;
	}
	return 0;
}

int32
hi64v(Node *n)
{
	if(align(0, types[TCHAR], Aarg1, nil))	/* isbigendian */
		return (int32)(n->vconst) & ~0L;
	else
		return (int32)((uvlong)n->vconst>>32) & ~0L;
}

int32
lo64v(Node *n)
{
	if(align(0, types[TCHAR], Aarg1, nil))	/* isbigendian */
		return (int32)((uvlong)n->vconst>>32) & ~0L;
	else
		return (int32)(n->vconst) & ~0L;
}

Node *
hi64(Node *n)
{
	return nodconst(hi64v(n));
}

Node *
lo64(Node *n)
{
	return nodconst(lo64v(n));
}

static Node *
anonreg(void)
{
	Node *n;

	n = new(OREGISTER, Z, Z);
	n->reg = D_NONE;
	n->type = types[TLONG];
	return n;
}

static Node *
regpair(Node *n, Node *t)
{
	Node *r;

	if(n != Z && n->op == OREGPAIR)
		return n;
	r = new(OREGPAIR, anonreg(), anonreg());
	if(n != Z)
		r->type = n->type;
	else
		r->type = t->type;
	return r;
}

static void
evacaxdx(Node *r)
{
	Node nod1, nod2;

	if(r->reg == D_AX || r->reg == D_DX) {
		reg[D_AX]++;
		reg[D_DX]++;
		/*
		 * this is just an optim that should
		 * check for spill
		 */
		r->type = types[TULONG];
		regalloc(&nod1, r, Z);
		nodreg(&nod2, Z, r->reg);
		gins(AMOVL, &nod2, &nod1);
		regfree(r);
		r->reg = nod1.reg;
		reg[D_AX]--;
		reg[D_DX]--;
	}
}

/* lazy instantiation of register pair */
static int
instpair(Node *n, Node *l)
{
	int r;

	r = 0;
	if(n->left->reg == D_NONE) {
		if(l != Z) {
			n->left->reg = l->reg;
			r = 1;
		}
		else
			regalloc(n->left, n->left, Z);
	}
	if(n->right->reg == D_NONE)
		regalloc(n->right, n->right, Z);
	return r;
}

static void
zapreg(Node *n)
{
	if(n->reg != D_NONE) {
		regfree(n);
		n->reg = D_NONE;
	}
}

static void
freepair(Node *n)
{
	regfree(n->left);
	regfree(n->right);
}

/* n is not OREGPAIR, nn is */
void
loadpair(Node *n, Node *nn)
{
	Node nod;

	instpair(nn, Z);
	if(n->op == OCONST) {
		gins(AMOVL, lo64(n), nn->left);
		n->xoffset += SZ_LONG;
		gins(AMOVL, hi64(n), nn->right);
		n->xoffset -= SZ_LONG;
		return;
	}
	if(!vaddr(n, 0)) {
		/* steal the right register for the laddr */
		nod = regnode;
		nod.reg = nn->right->reg;
		lcgen(n, &nod);
		n = &nod;
		regind(n, n);
		n->xoffset = 0;
	}
	gins(AMOVL, n, nn->left);
	n->xoffset += SZ_LONG;
	gins(AMOVL, n, nn->right);
	n->xoffset -= SZ_LONG;
}

/* n is OREGPAIR, nn is not */
static void
storepair(Node *n, Node *nn, int f)
{
	Node nod;

	if(!vaddr(nn, 0)) {
		reglcgen(&nod, nn, Z);
		nn = &nod;
	}
	gins(AMOVL, n->left, nn);
	nn->xoffset += SZ_LONG;
	gins(AMOVL, n->right, nn);
	nn->xoffset -= SZ_LONG;
	if(nn == &nod)
		regfree(&nod);
	if(f)
		freepair(n);
}

enum
{
/* 4 only, see WW */
	WNONE	= 0,
	WCONST,
	WADDR,
	WHARD,
};

static int
whatof(Node *n, int a)
{
	if(n->op == OCONST)
		return WCONST;
	return !vaddr(n, a) ? WHARD : WADDR;
}

/* can upgrade an extern to addr for AND */
static int
reduxv(Node *n)
{
	return lo64v(n) == 0 || hi64v(n) == 0;
}

int
cond(int op)
{
	switch(op) {
	case OANDAND:
	case OOROR:
	case ONOT:
		return 1;

	case OEQ:
	case ONE:
	case OLE:
	case OLT:
	case OGE:
	case OGT:
	case OHI:
	case OHS:
	case OLO:
	case OLS:
		return 1;
	}
	return 0;
}

/*
 * for a func operand call it and then return
 * the safe node
 */
static Node *
vfunc(Node *n, Node *nn)
{
	Node *t;

	if(n->op != OFUNC)
		return n;
	t = new(0, Z, Z);
	if(nn == Z || nn == nodret)
		nn = n;
	regsalloc(t, nn);
	sugen(n, t, 8);
	return t;
}

/* try to steal a reg */
static int
getreg(Node **np, Node *t, int r)
{
	Node *n, *p;

	n = *np;
	if(n->reg == r) {
		p = new(0, Z, Z);
		regalloc(p, n, Z);
		gins(AMOVL, n, p);
		*t = *n;
		*np = p;
		return 1;
	}
	return 0;
}

static Node *
snarfreg(Node *n, Node *t, int r, Node *d, Node *c)
{
	if(n == Z || n->op != OREGPAIR || (!getreg(&n->left, t, r) && !getreg(&n->right, t, r))) {
		if(nodreg(t, Z, r)) {
			regalloc(c, d, Z);
			gins(AMOVL, t, c);
			reg[r]++;
			return c;
		}
		reg[r]++;
	}
	return Z;
}

enum
{
	Vstart	= OEND,

	Vgo,
	Vamv,
	Vmv,
	Vzero,
	Vop,
	Vopx,
	Vins,
	Vins0,
	Vinsl,
	Vinsr,
	Vinsla,
	Vinsra,
	Vinsx,
	Vmul,
	Vshll,
	VT,
	VF,
	V_l_lo_f,
	V_l_hi_f,
	V_l_lo_t,
	V_l_hi_t,
	V_l_lo_u,
	V_l_hi_u,
	V_r_lo_f,
	V_r_hi_f,
	V_r_lo_t,
	V_r_hi_t,
	V_r_lo_u,
	V_r_hi_u,
	Vspazz,
	Vend,

	V_T0,
	V_T1,
	V_F0,
	V_F1,

	V_a0,
	V_a1,
	V_f0,
	V_f1,

	V_p0,
	V_p1,
	V_p2,
	V_p3,
	V_p4,

	V_s0,
	V_s1,
	V_s2,
	V_s3,
	V_s4,

	C00,
	C01,
	C31,
	C32,

	O_l_lo,
	O_l_hi,
	O_r_lo,
	O_r_hi,
	O_t_lo,
	O_t_hi,
	O_l,
	O_r,
	O_l_rp,
	O_r_rp,
	O_t_rp,
	O_r0,
	O_r1,
	O_Zop,

	O_a0,
	O_a1,

	V_C0,
	V_C1,

	V_S0,
	V_S1,

	VOPS	= 5,
	VLEN	= 5,
	VARGS	= 2,

	S00	= 0,
	Sc0,
	Sc1,
	Sc2,
	Sac3,
	Sac4,
	S10,

	SAgen	= 0,
	SAclo,
	SAc32,
	SAchi,
	SAdgen,
	SAdclo,
	SAdc32,
	SAdchi,

	B0c	= 0,
	Bca,
	Bac,

	T0i	= 0,
	Tii,

	Bop0	= 0,
	Bop1,
};

/*
 * _testv:
 * 	CMPL	lo,$0
 * 	JNE	true
 * 	CMPL	hi,$0
 * 	JNE	true
 * 	GOTO	false
 * false:
 * 	GOTO	code
 * true:
 * 	GOTO	patchme
 * code:
 */

static uchar	testi[][VLEN] =
{
	{Vop, ONE, O_l_lo, C00},
	{V_s0, Vop, ONE, O_l_hi, C00},
	{V_s1, Vgo, V_s2, Vgo, V_s3},
	{VF, V_p0, V_p1, VT, V_p2},
	{Vgo, V_p3},
	{VT, V_p0, V_p1, VF, V_p2},
	{Vend},
};

/* shift left general case */
static uchar	shll00[][VLEN] =
{
	{Vop, OGE, O_r, C32},
	{V_s0, Vinsl, ASHLL, O_r, O_l_rp},
	{Vins, ASHLL, O_r, O_l_lo, Vgo},
	{V_p0, V_s0},
	{Vins, ASHLL, O_r, O_l_lo},
	{Vins, AMOVL, O_l_lo, O_l_hi},
	{Vzero, O_l_lo, V_p0, Vend},
};

/* shift left rp, const < 32 */
static uchar	shllc0[][VLEN] =
{
	{Vinsl, ASHLL, O_r, O_l_rp},
	{Vshll, O_r, O_l_lo, Vend},
};

/* shift left rp, const == 32 */
static uchar	shllc1[][VLEN] =
{
	{Vins, AMOVL, O_l_lo, O_l_hi},
	{Vzero, O_l_lo, Vend},
};

/* shift left rp, const > 32 */
static uchar	shllc2[][VLEN] =
{
	{Vshll, O_r, O_l_lo},
	{Vins, AMOVL, O_l_lo, O_l_hi},
	{Vzero, O_l_lo, Vend},
};

/* shift left addr, const == 32 */
static uchar	shllac3[][VLEN] =
{
	{Vins, AMOVL, O_l_lo, O_t_hi},
	{Vzero, O_t_lo, Vend},
};

/* shift left addr, const > 32 */
static uchar	shllac4[][VLEN] =
{
	{Vins, AMOVL, O_l_lo, O_t_hi},
	{Vshll, O_r, O_t_hi},
	{Vzero, O_t_lo, Vend},
};

/* shift left of constant */
static uchar	shll10[][VLEN] =
{
	{Vop, OGE, O_r, C32},
	{V_s0, Vins, AMOVL, O_l_lo, O_t_lo},
	{Vins, AMOVL, O_l_hi, O_t_hi},
	{Vinsl, ASHLL, O_r, O_t_rp},
	{Vins, ASHLL, O_r, O_t_lo, Vgo},
	{V_p0, V_s0},
	{Vins, AMOVL, O_l_lo, O_t_hi},
	{V_l_lo_t, Vins, ASHLL, O_r, O_t_hi},
	{Vzero, O_t_lo, V_p0, Vend},
};

static uchar	(*shlltab[])[VLEN] =
{
	shll00,
	shllc0,
	shllc1,
	shllc2,
	shllac3,
	shllac4,
	shll10,
};

/* shift right general case */
static uchar	shrl00[][VLEN] =
{
	{Vop, OGE, O_r, C32},
	{V_s0, Vinsr, ASHRL, O_r, O_l_rp},
	{Vins, O_a0, O_r, O_l_hi, Vgo},
	{V_p0, V_s0},
	{Vins, O_a0, O_r, O_l_hi},
	{Vins, AMOVL, O_l_hi, O_l_lo},
	{V_T1, Vzero, O_l_hi},
	{V_F1, Vins, ASARL, C31, O_l_hi},
	{V_p0, Vend},
};

/* shift right rp, const < 32 */
static uchar	shrlc0[][VLEN] =
{
	{Vinsr, ASHRL, O_r, O_l_rp},
	{Vins, O_a0, O_r, O_l_hi, Vend},
};

/* shift right rp, const == 32 */
static uchar	shrlc1[][VLEN] =
{
	{Vins, AMOVL, O_l_hi, O_l_lo},
	{V_T1, Vzero, O_l_hi},
	{V_F1, Vins, ASARL, C31, O_l_hi},
	{Vend},
};

/* shift right rp, const > 32 */
static uchar	shrlc2[][VLEN] =
{
	{Vins, O_a0, O_r, O_l_hi},
	{Vins, AMOVL, O_l_hi, O_l_lo},
	{V_T1, Vzero, O_l_hi},
	{V_F1, Vins, ASARL, C31, O_l_hi},
	{Vend},
};

/* shift right addr, const == 32 */
static uchar	shrlac3[][VLEN] =
{
	{Vins, AMOVL, O_l_hi, O_t_lo},
	{V_T1, Vzero, O_t_hi},
	{V_F1, Vins, AMOVL, O_t_lo, O_t_hi},
	{V_F1, Vins, ASARL, C31, O_t_hi},
	{Vend},
};

/* shift right addr, const > 32 */
static uchar	shrlac4[][VLEN] =
{
	{Vins, AMOVL, O_l_hi, O_t_lo},
	{Vins, O_a0, O_r, O_t_lo},
	{V_T1, Vzero, O_t_hi},
	{V_F1, Vins, AMOVL, O_t_lo, O_t_hi},
	{V_F1, Vins, ASARL, C31, O_t_hi},
	{Vend},
};

/* shift right of constant */
static uchar	shrl10[][VLEN] =
{
	{Vop, OGE, O_r, C32},
	{V_s0, Vins, AMOVL, O_l_lo, O_t_lo},
	{Vins, AMOVL, O_l_hi, O_t_hi},
	{Vinsr, ASHRL, O_r, O_t_rp},
	{Vins, O_a0, O_r, O_t_hi, Vgo},
	{V_p0, V_s0},
	{Vins, AMOVL, O_l_hi, O_t_lo},
	{V_l_hi_t, Vins, O_a0, O_r, O_t_lo},
	{V_l_hi_u, V_S1},
	{V_T1, Vzero, O_t_hi, V_p0},
	{V_F1, Vins, AMOVL, O_t_lo, O_t_hi},
	{V_F1, Vins, ASARL, C31, O_t_hi},
	{Vend},
};

static uchar	(*shrltab[])[VLEN] =
{
	shrl00,
	shrlc0,
	shrlc1,
	shrlc2,
	shrlac3,
	shrlac4,
	shrl10,
};

/* shift asop left general case */
static uchar	asshllgen[][VLEN] =
{
	{V_a0, V_a1},
	{Vop, OGE, O_r, C32},
	{V_s0, Vins, AMOVL, O_l_lo, O_r0},
	{Vins, AMOVL, O_l_hi, O_r1},
	{Vinsla, ASHLL, O_r, O_r0},
	{Vins, ASHLL, O_r, O_r0},
	{Vins, AMOVL, O_r1, O_l_hi},
	{Vins, AMOVL, O_r0, O_l_lo, Vgo},
	{V_p0, V_s0},
	{Vins, AMOVL, O_l_lo, O_r0},
	{Vzero, O_l_lo},
	{Vins, ASHLL, O_r, O_r0},
	{Vins, AMOVL, O_r0, O_l_hi, V_p0},
	{V_f0, V_f1, Vend},
};

/* shift asop left, const < 32 */
static uchar	asshllclo[][VLEN] =
{
	{V_a0, V_a1},
	{Vins, AMOVL, O_l_lo, O_r0},
	{Vins, AMOVL, O_l_hi, O_r1},
	{Vinsla, ASHLL, O_r, O_r0},
	{Vshll, O_r, O_r0},
	{Vins, AMOVL, O_r1, O_l_hi},
	{Vins, AMOVL, O_r0, O_l_lo},
	{V_f0, V_f1, Vend},
};

/* shift asop left, const == 32 */
static uchar	asshllc32[][VLEN] =
{
	{V_a0},
	{Vins, AMOVL, O_l_lo, O_r0},
	{Vzero, O_l_lo},
	{Vins, AMOVL, O_r0, O_l_hi},
	{V_f0, Vend},
};

/* shift asop left, const > 32 */
static uchar	asshllchi[][VLEN] =
{
	{V_a0},
	{Vins, AMOVL, O_l_lo, O_r0},
	{Vzero, O_l_lo},
	{Vshll, O_r, O_r0},
	{Vins, AMOVL, O_r0, O_l_hi},
	{V_f0, Vend},
};

/* shift asop dest left general case */
static uchar	asdshllgen[][VLEN] =
{
	{Vop, OGE, O_r, C32},
	{V_s0, Vins, AMOVL, O_l_lo, O_t_lo},
	{Vins, AMOVL, O_l_hi, O_t_hi},
	{Vinsl, ASHLL, O_r, O_t_rp},
	{Vins, ASHLL, O_r, O_t_lo},
	{Vins, AMOVL, O_t_hi, O_l_hi},
	{Vins, AMOVL, O_t_lo, O_l_lo, Vgo},
	{V_p0, V_s0},
	{Vins, AMOVL, O_l_lo, O_t_hi},
	{Vzero, O_l_lo},
	{Vins, ASHLL, O_r, O_t_hi},
	{Vzero, O_t_lo},
	{Vins, AMOVL, O_t_hi, O_l_hi, V_p0},
	{Vend},
};

/* shift asop dest left, const < 32 */
static uchar	asdshllclo[][VLEN] =
{
	{Vins, AMOVL, O_l_lo, O_t_lo},
	{Vins, AMOVL, O_l_hi, O_t_hi},
	{Vinsl, ASHLL, O_r, O_t_rp},
	{Vshll, O_r, O_t_lo},
	{Vins, AMOVL, O_t_hi, O_l_hi},
	{Vins, AMOVL, O_t_lo, O_l_lo},
	{Vend},
};

/* shift asop dest left, const == 32 */
static uchar	asdshllc32[][VLEN] =
{
	{Vins, AMOVL, O_l_lo, O_t_hi},
	{Vzero, O_t_lo},
	{Vins, AMOVL, O_t_hi, O_l_hi},
	{Vins, AMOVL, O_t_lo, O_l_lo},
	{Vend},
};

/* shift asop dest, const > 32 */
static uchar	asdshllchi[][VLEN] =
{
	{Vins, AMOVL, O_l_lo, O_t_hi},
	{Vzero, O_t_lo},
	{Vshll, O_r, O_t_hi},
	{Vins, AMOVL, O_t_lo, O_l_lo},
	{Vins, AMOVL, O_t_hi, O_l_hi},
	{Vend},
};

static uchar	(*asshlltab[])[VLEN] =
{
	asshllgen,
	asshllclo,
	asshllc32,
	asshllchi,
	asdshllgen,
	asdshllclo,
	asdshllc32,
	asdshllchi,
};

/* shift asop right general case */
static uchar	asshrlgen[][VLEN] =
{
	{V_a0, V_a1},
	{Vop, OGE, O_r, C32},
	{V_s0, Vins, AMOVL, O_l_lo, O_r0},
	{Vins, AMOVL, O_l_hi, O_r1},
	{Vinsra, ASHRL, O_r, O_r0},
	{Vinsx, Bop0, O_r, O_r1},
	{Vins, AMOVL, O_r0, O_l_lo},
	{Vins, AMOVL, O_r1, O_l_hi, Vgo},
	{V_p0, V_s0},
	{Vins, AMOVL, O_l_hi, O_r0},
	{Vinsx, Bop0, O_r, O_r0},
	{V_T1, Vzero, O_l_hi},
	{Vins, AMOVL, O_r0, O_l_lo},
	{V_F1, Vins, ASARL, C31, O_r0},
	{V_F1, Vins, AMOVL, O_r0, O_l_hi},
	{V_p0, V_f0, V_f1, Vend},
};

/* shift asop right, const < 32 */
static uchar	asshrlclo[][VLEN] =
{
	{V_a0, V_a1},
	{Vins, AMOVL, O_l_lo, O_r0},
	{Vins, AMOVL, O_l_hi, O_r1},
	{Vinsra, ASHRL, O_r, O_r0},
	{Vinsx, Bop0, O_r, O_r1},
	{Vins, AMOVL, O_r0, O_l_lo},
	{Vins, AMOVL, O_r1, O_l_hi},
	{V_f0, V_f1, Vend},
};

/* shift asop right, const == 32 */
static uchar	asshrlc32[][VLEN] =
{
	{V_a0},
	{Vins, AMOVL, O_l_hi, O_r0},
	{V_T1, Vzero, O_l_hi},
	{Vins, AMOVL, O_r0, O_l_lo},
	{V_F1, Vins, ASARL, C31, O_r0},
	{V_F1, Vins, AMOVL, O_r0, O_l_hi},
	{V_f0, Vend},
};

/* shift asop right, const > 32 */
static uchar	asshrlchi[][VLEN] =
{
	{V_a0},
	{Vins, AMOVL, O_l_hi, O_r0},
	{V_T1, Vzero, O_l_hi},
	{Vinsx, Bop0, O_r, O_r0},
	{Vins, AMOVL, O_r0, O_l_lo},
	{V_F1, Vins, ASARL, C31, O_r0},
	{V_F1, Vins, AMOVL, O_r0, O_l_hi},
	{V_f0, Vend},
};

/* shift asop dest right general case */
static uchar	asdshrlgen[][VLEN] =
{
	{Vop, OGE, O_r, C32},
	{V_s0, Vins, AMOVL, O_l_lo, O_t_lo},
	{Vins, AMOVL, O_l_hi, O_t_hi},
	{Vinsr, ASHRL, O_r, O_t_rp},
	{Vinsx, Bop0, O_r, O_t_hi},
	{Vins, AMOVL, O_t_lo, O_l_lo},
	{Vins, AMOVL, O_t_hi, O_l_hi, Vgo},
	{V_p0, V_s0},
	{Vins, AMOVL, O_l_hi, O_t_lo},
	{V_T1, Vzero, O_t_hi},
	{Vinsx, Bop0, O_r, O_t_lo},
	{V_F1, Vins, AMOVL, O_t_lo, O_t_hi},
	{V_F1, Vins, ASARL, C31, O_t_hi},
	{Vins, AMOVL, O_t_hi, O_l_hi, V_p0},
	{Vend},
};

/* shift asop dest right, const < 32 */
static uchar	asdshrlclo[][VLEN] =
{
	{Vins, AMOVL, O_l_lo, O_t_lo},
	{Vins, AMOVL, O_l_hi, O_t_hi},
	{Vinsr, ASHRL, O_r, O_t_rp},
	{Vinsx, Bop0, O_r, O_t_hi},
	{Vins, AMOVL, O_t_lo, O_l_lo},
	{Vins, AMOVL, O_t_hi, O_l_hi},
	{Vend},
};

/* shift asop dest right, const == 32 */
static uchar	asdshrlc32[][VLEN] =
{
	{Vins, AMOVL, O_l_hi, O_t_lo},
	{V_T1, Vzero, O_t_hi},
	{V_F1, Vins, AMOVL, O_t_lo, O_t_hi},
	{V_F1, Vins, ASARL, C31, O_t_hi},
	{Vins, AMOVL, O_t_lo, O_l_lo},
	{Vins, AMOVL, O_t_hi, O_l_hi},
	{Vend},
};

/* shift asop dest, const > 32 */
static uchar	asdshrlchi[][VLEN] =
{
	{Vins, AMOVL, O_l_hi, O_t_lo},
	{V_T1, Vzero, O_t_hi},
	{Vinsx, Bop0, O_r, O_t_lo},
	{V_T1, Vins, AMOVL, O_t_hi, O_l_hi},
	{V_T1, Vins, AMOVL, O_t_lo, O_l_lo},
	{V_F1, Vins, AMOVL, O_t_lo, O_t_hi},
	{V_F1, Vins, ASARL, C31, O_t_hi},
	{V_F1, Vins, AMOVL, O_t_lo, O_l_lo},
	{V_F1, Vins, AMOVL, O_t_hi, O_l_hi},
	{Vend},
};

static uchar	(*asshrltab[])[VLEN] =
{
	asshrlgen,
	asshrlclo,
	asshrlc32,
	asshrlchi,
	asdshrlgen,
	asdshrlclo,
	asdshrlc32,
	asdshrlchi,
};

static uchar	shrlargs[]	= { ASHRL, 1 };
static uchar	sarlargs[]	= { ASARL, 0 };

/* ++ -- */
static uchar	incdec[][VLEN] =
{
	{Vinsx, Bop0, C01, O_l_lo},
	{Vinsx, Bop1, C00, O_l_hi, Vend},
};

/* ++ -- *p */
static uchar	incdecpre[][VLEN] =
{
	{Vins, AMOVL, O_l_lo, O_t_lo},
	{Vins, AMOVL, O_l_hi, O_t_hi},
	{Vinsx, Bop0, C01, O_t_lo},
	{Vinsx, Bop1, C00, O_t_hi},
	{Vins, AMOVL, O_t_lo, O_l_lo},
	{Vins, AMOVL, O_t_hi, O_l_hi, Vend},
};

/* *p ++ -- */
static uchar	incdecpost[][VLEN] =
{
	{Vins, AMOVL, O_l_lo, O_t_lo},
	{Vins, AMOVL, O_l_hi, O_t_hi},
	{Vinsx, Bop0, C01, O_l_lo},
	{Vinsx, Bop1, C00, O_l_hi, Vend},
};

/* binop rp, rp */
static uchar	binop00[][VLEN] =
{
	{Vinsx, Bop0, O_r_lo, O_l_lo},
	{Vinsx, Bop1, O_r_hi, O_l_hi, Vend},
	{Vend},
};

/* binop rp, addr */
static uchar	binoptmp[][VLEN] =
{
	{V_a0, Vins, AMOVL, O_r_lo, O_r0},
	{Vinsx, Bop0, O_r0, O_l_lo},
	{Vins, AMOVL, O_r_hi, O_r0},
	{Vinsx, Bop1, O_r0, O_l_hi},
	{V_f0, Vend},
};

/* binop t = *a op *b */
static uchar	binop11[][VLEN] =
{
	{Vins, AMOVL, O_l_lo, O_t_lo},
	{Vinsx, Bop0, O_r_lo, O_t_lo},
	{Vins, AMOVL, O_l_hi, O_t_hi},
	{Vinsx, Bop1, O_r_hi, O_t_hi, Vend},
};

/* binop t = rp +- c */
static uchar	add0c[][VLEN] =
{
	{V_r_lo_t, Vinsx, Bop0, O_r_lo, O_l_lo},
	{V_r_lo_f, Vamv, Bop0, Bop1},
	{Vinsx, Bop1, O_r_hi, O_l_hi},
	{Vend},
};

/* binop t = rp & c */
static uchar	and0c[][VLEN] =
{
	{V_r_lo_t, Vinsx, Bop0, O_r_lo, O_l_lo},
	{V_r_lo_f, Vins, AMOVL, C00, O_l_lo},
	{V_r_hi_t, Vinsx, Bop1, O_r_hi, O_l_hi},
	{V_r_hi_f, Vins, AMOVL, C00, O_l_hi},
	{Vend},
};

/* binop t = rp | c */
static uchar	or0c[][VLEN] =
{
	{V_r_lo_t, Vinsx, Bop0, O_r_lo, O_l_lo},
	{V_r_hi_t, Vinsx, Bop1, O_r_hi, O_l_hi},
	{Vend},
};

/* binop t = c - rp */
static uchar	sub10[][VLEN] =
{
	{V_a0, Vins, AMOVL, O_l_lo, O_r0},
	{Vinsx, Bop0, O_r_lo, O_r0},
	{Vins, AMOVL, O_l_hi, O_r_lo},
	{Vinsx, Bop1, O_r_hi, O_r_lo},
	{Vspazz, V_f0, Vend},
};

/* binop t = c + *b */
static uchar	addca[][VLEN] =
{
	{Vins, AMOVL, O_r_lo, O_t_lo},
	{V_l_lo_t, Vinsx, Bop0, O_l_lo, O_t_lo},
	{V_l_lo_f, Vamv, Bop0, Bop1},
	{Vins, AMOVL, O_r_hi, O_t_hi},
	{Vinsx, Bop1, O_l_hi, O_t_hi},
	{Vend},
};

/* binop t = c & *b */
static uchar	andca[][VLEN] =
{
	{V_l_lo_t, Vins, AMOVL, O_r_lo, O_t_lo},
	{V_l_lo_t, Vinsx, Bop0, O_l_lo, O_t_lo},
	{V_l_lo_f, Vzero, O_t_lo},
	{V_l_hi_t, Vins, AMOVL, O_r_hi, O_t_hi},
	{V_l_hi_t, Vinsx, Bop1, O_l_hi, O_t_hi},
	{V_l_hi_f, Vzero, O_t_hi},
	{Vend},
};

/* binop t = c | *b */
static uchar	orca[][VLEN] =
{
	{Vins, AMOVL, O_r_lo, O_t_lo},
	{V_l_lo_t, Vinsx, Bop0, O_l_lo, O_t_lo},
	{Vins, AMOVL, O_r_hi, O_t_hi},
	{V_l_hi_t, Vinsx, Bop1, O_l_hi, O_t_hi},
	{Vend},
};

/* binop t = c - *b */
static uchar	subca[][VLEN] =
{
	{Vins, AMOVL, O_l_lo, O_t_lo},
	{Vins, AMOVL, O_l_hi, O_t_hi},
	{Vinsx, Bop0, O_r_lo, O_t_lo},
	{Vinsx, Bop1, O_r_hi, O_t_hi},
	{Vend},
};

/* binop t = *a +- c */
static uchar	addac[][VLEN] =
{
	{Vins, AMOVL, O_l_lo, O_t_lo},
	{V_r_lo_t, Vinsx, Bop0, O_r_lo, O_t_lo},
	{V_r_lo_f, Vamv, Bop0, Bop1},
	{Vins, AMOVL, O_l_hi, O_t_hi},
	{Vinsx, Bop1, O_r_hi, O_t_hi},
	{Vend},
};

/* binop t = *a | c */
static uchar	orac[][VLEN] =
{
	{Vins, AMOVL, O_l_lo, O_t_lo},
	{V_r_lo_t, Vinsx, Bop0, O_r_lo, O_t_lo},
	{Vins, AMOVL, O_l_hi, O_t_hi},
	{V_r_hi_t, Vinsx, Bop1, O_r_hi, O_t_hi},
	{Vend},
};

/* binop t = *a & c */
static uchar	andac[][VLEN] =
{
	{V_r_lo_t, Vins, AMOVL, O_l_lo, O_t_lo},
	{V_r_lo_t, Vinsx, Bop0, O_r_lo, O_t_lo},
	{V_r_lo_f, Vzero, O_t_lo},
	{V_r_hi_t, Vins, AMOVL, O_l_hi, O_t_hi},
	{V_r_hi_t, Vinsx, Bop0, O_r_hi, O_t_hi},
	{V_r_hi_f, Vzero, O_t_hi},
	{Vend},
};

static uchar	ADDargs[]	= { AADDL, AADCL };
static uchar	ANDargs[]	= { AANDL, AANDL };
static uchar	ORargs[]	= { AORL, AORL };
static uchar	SUBargs[]	= { ASUBL, ASBBL };
static uchar	XORargs[]	= { AXORL, AXORL };

static uchar	(*ADDtab[])[VLEN] =
{
	add0c, addca, addac,
};

static uchar	(*ANDtab[])[VLEN] =
{
	and0c, andca, andac,
};

static uchar	(*ORtab[])[VLEN] =
{
	or0c, orca, orac,
};

static uchar	(*SUBtab[])[VLEN] =
{
	add0c, subca, addac,
};

/* mul of const32 */
static uchar	mulc32[][VLEN] =
{
	{V_a0, Vop, ONE, O_l_hi, C00},
	{V_s0, Vins, AMOVL, O_r_lo, O_r0},
	{Vins, AMULL, O_r0, O_Zop},
	{Vgo, V_p0, V_s0},
	{Vins, AMOVL, O_l_hi, O_r0},
	{Vmul, O_r_lo, O_r0},
	{Vins, AMOVL, O_r_lo, O_l_hi},
	{Vins, AMULL, O_l_hi, O_Zop},
	{Vins, AADDL, O_r0, O_l_hi},
	{V_f0, V_p0, Vend},
};

/* mul of const64 */
static uchar	mulc64[][VLEN] =
{
	{V_a0, Vins, AMOVL, O_r_hi, O_r0},
	{Vop, OOR, O_l_hi, O_r0},
	{Vop, ONE, O_r0, C00},
	{V_s0, Vins, AMOVL, O_r_lo, O_r0},
	{Vins, AMULL, O_r0, O_Zop},
	{Vgo, V_p0, V_s0},
	{Vmul, O_r_lo, O_l_hi},
	{Vins, AMOVL, O_l_lo, O_r0},
	{Vmul, O_r_hi, O_r0},
	{Vins, AADDL, O_l_hi, O_r0},
	{Vins, AMOVL, O_r_lo, O_l_hi},
	{Vins, AMULL, O_l_hi, O_Zop},
	{Vins, AADDL, O_r0, O_l_hi},
	{V_f0, V_p0, Vend},
};

/* mul general */
static uchar	mull[][VLEN] =
{
	{V_a0, Vins, AMOVL, O_r_hi, O_r0},
	{Vop, OOR, O_l_hi, O_r0},
	{Vop, ONE, O_r0, C00},
	{V_s0, Vins, AMOVL, O_r_lo, O_r0},
	{Vins, AMULL, O_r0, O_Zop},
	{Vgo, V_p0, V_s0},
	{Vins, AIMULL, O_r_lo, O_l_hi},
	{Vins, AMOVL, O_l_lo, O_r0},
	{Vins, AIMULL, O_r_hi, O_r0},
	{Vins, AADDL, O_l_hi, O_r0},
	{Vins, AMOVL, O_r_lo, O_l_hi},
	{Vins, AMULL, O_l_hi, O_Zop},
	{Vins, AADDL, O_r0, O_l_hi},
	{V_f0, V_p0, Vend},
};

/* cast rp l to rp t */
static uchar	castrp[][VLEN] =
{
	{Vmv, O_l, O_t_lo},
	{VT, Vins, AMOVL, O_t_lo, O_t_hi},
	{VT, Vins, ASARL, C31, O_t_hi},
	{VF, Vzero, O_t_hi},
	{Vend},
};

/* cast rp l to addr t */
static uchar	castrpa[][VLEN] =
{
	{VT, V_a0, Vmv, O_l, O_r0},
	{VT, Vins, AMOVL, O_r0, O_t_lo},
	{VT, Vins, ASARL, C31, O_r0},
	{VT, Vins, AMOVL, O_r0, O_t_hi},
	{VT, V_f0},
	{VF, Vmv, O_l, O_t_lo},
	{VF, Vzero, O_t_hi},
	{Vend},
};

static uchar	netab0i[][VLEN] =
{
	{Vop, ONE, O_l_lo, O_r_lo},
	{V_s0, Vop, ONE, O_l_hi, O_r_hi},
	{V_s1, Vgo, V_s2, Vgo, V_s3},
	{VF, V_p0, V_p1, VT, V_p2},
	{Vgo, V_p3},
	{VT, V_p0, V_p1, VF, V_p2},
	{Vend},
};

static uchar	netabii[][VLEN] =
{
	{V_a0, Vins, AMOVL, O_l_lo, O_r0},
	{Vop, ONE, O_r0, O_r_lo},
	{V_s0, Vins, AMOVL, O_l_hi, O_r0},
	{Vop, ONE, O_r0, O_r_hi},
	{V_s1, Vgo, V_s2, Vgo, V_s3},
	{VF, V_p0, V_p1, VT, V_p2},
	{Vgo, V_p3},
	{VT, V_p0, V_p1, VF, V_p2},
	{V_f0, Vend},
};

static uchar	cmptab0i[][VLEN] =
{
	{Vopx, Bop0, O_l_hi, O_r_hi},
	{V_s0, Vins0, AJNE},
	{V_s1, Vopx, Bop1, O_l_lo, O_r_lo},
	{V_s2, Vgo, V_s3, Vgo, V_s4},
	{VT, V_p1, V_p3},
	{VF, V_p0, V_p2},
	{Vgo, V_p4},
	{VT, V_p0, V_p2},
	{VF, V_p1, V_p3},
	{Vend},
};

static uchar	cmptabii[][VLEN] =
{
	{V_a0, Vins, AMOVL, O_l_hi, O_r0},
	{Vopx, Bop0, O_r0, O_r_hi},
	{V_s0, Vins0, AJNE},
	{V_s1, Vins, AMOVL, O_l_lo, O_r0},
	{Vopx, Bop1, O_r0, O_r_lo},
	{V_s2, Vgo, V_s3, Vgo, V_s4},
	{VT, V_p1, V_p3},
	{VF, V_p0, V_p2},
	{Vgo, V_p4},
	{VT, V_p0, V_p2},
	{VF, V_p1, V_p3},
	{V_f0, Vend},
};

static uchar	(*NEtab[])[VLEN] =
{
	netab0i, netabii,
};

static uchar	(*cmptab[])[VLEN] =
{
	cmptab0i, cmptabii,
};

static uchar	GEargs[]	= { OGT, OHS };
static uchar	GTargs[]	= { OGT, OHI };
static uchar	HIargs[]	= { OHI, OHI };
static uchar	HSargs[]	= { OHI, OHS };

/* Big Generator */
static void
biggen(Node *l, Node *r, Node *t, int true, uchar code[][VLEN], uchar *a)
{
	int i, j, g, oc, op, lo, ro, to, xo, *xp;
	Type *lt;
	Prog *pr[VOPS];
	Node *ot, *tl, *tr, tmps[2];
	uchar *c, (*cp)[VLEN], args[VARGS];

	if(a != nil)
		memmove(args, a, VARGS);
//print("biggen %d %d %d\n", args[0], args[1], args[2]);
//if(l) prtree(l, "l");
//if(r) prtree(r, "r");
//if(t) prtree(t, "t");
	lo = ro = to = 0;
	cp = code;

	for (;;) {
		c = *cp++;
		g = 1;
		i = 0;
//print("code %d %d %d %d %d\n", c[0], c[1], c[2], c[3], c[4]);
		for(;;) {
			switch(op = c[i]) {
			case Vgo:
				if(g)
					gbranch(OGOTO);
				i++;
				break;

			case Vamv:
				i += 3;
				if(i > VLEN) {
					diag(l, "bad Vop");
					return;
				}
				if(g)
					args[c[i - 1]] = args[c[i - 2]];
				break;

			case Vzero:
				i += 2;
				if(i > VLEN) {
					diag(l, "bad Vop");
					return;
				}
				j = i - 1;
				goto op;

			case Vspazz:	// nasty hack to save a reg in SUB
//print("spazz\n");
				if(g) {
//print("hi %R lo %R t %R\n", r->right->reg, r->left->reg, tmps[0].reg);
					ot = r->right;
					r->right = r->left;
					tl = new(0, Z, Z);
					*tl = tmps[0];
					r->left = tl;
					tmps[0] = *ot;
//print("hi %R lo %R t %R\n", r->right->reg, r->left->reg, tmps[0].reg);
				}
				i++;
				break;

			case Vmv:
			case Vmul:
			case Vshll:
				i += 3;
				if(i > VLEN) {
					diag(l, "bad Vop");
					return;
				}
				j = i - 2;
				goto op;

			case Vins0:
				i += 2;
				if(i > VLEN) {
					diag(l, "bad Vop");
					return;
				}
				gins(c[i - 1], Z, Z);
				break;

			case Vop:
			case Vopx:
			case Vins:
			case Vinsl:
			case Vinsr:
			case Vinsla:
			case Vinsra:
			case Vinsx:
				i += 4;
				if(i > VLEN) {
					diag(l, "bad Vop");
					return;
				}
				j = i - 2;
				goto op;

			op:
				if(!g)
					break;
				tl = Z;
				tr = Z;
				for(; j < i; j++) {
					switch(c[j]) {
					case C00:
						ot = nodconst(0);
						break;
					case C01:
						ot = nodconst(1);
						break;
					case C31:
						ot = nodconst(31);
						break;
					case C32:
						ot = nodconst(32);
						break;

					case O_l:
					case O_l_lo:
						ot = l; xp = &lo; xo = 0;
						goto op0;
					case O_l_hi:
						ot = l; xp = &lo; xo = SZ_LONG;
						goto op0;
					case O_r:
					case O_r_lo:
						ot = r; xp = &ro; xo = 0;
						goto op0;
					case O_r_hi:
						ot = r; xp = &ro; xo = SZ_LONG;
						goto op0;
					case O_t_lo:
						ot = t; xp = &to; xo = 0;
						goto op0;
					case O_t_hi:
						ot = t; xp = &to; xo = SZ_LONG;
						goto op0;
					case O_l_rp:
						ot = l;
						break;
					case O_r_rp:
						ot = r;
						break;
					case O_t_rp:
						ot = t;
						break;
					case O_r0:
					case O_r1:
						ot = &tmps[c[j] - O_r0];
						break;
					case O_Zop:
						ot = Z;
						break;

					op0:
						switch(ot->op) {
						case OCONST:
							if(xo)
								ot = hi64(ot);
							else
								ot = lo64(ot);
							break;
						case OREGPAIR:
							if(xo)
								ot = ot->right;
							else
								ot = ot->left;
							break;
						case OREGISTER:
							break;
						default:
							if(xo != *xp) {
								ot->xoffset += xo - *xp;
								*xp = xo;
							}
						}
						break;
					
					default:
						diag(l, "bad V_lop");
						return;
					}
					if(tl == nil)
						tl = ot;
					else
						tr = ot;
				}
				if(op == Vzero) {
					zeroregm(tl);
					break;
				}
				oc = c[i - 3];
				if(op == Vinsx || op == Vopx) {
//print("%d -> %d\n", oc, args[oc]);
					oc = args[oc];
				}
				else {
					switch(oc) {
					case O_a0:
					case O_a1:
						oc = args[oc - O_a0];
						break;
					}
				}
				switch(op) {
				case Vmul:
					mulgen(tr->type, tl, tr);
					break;
				case Vmv:
					gmove(tl, tr);
					break;
				case Vshll:
					shiftit(tr->type, tl, tr);
					break;
				case Vop:
				case Vopx:
					gopcode(oc, types[TULONG], tl, tr);
					break;
				case Vins:
				case Vinsx:
					gins(oc, tl, tr);
					break;
				case Vinsl:
					gins(oc, tl, tr->right);
					p->from.index = tr->left->reg;
					break;
				case Vinsr:
					gins(oc, tl, tr->left);
					p->from.index = tr->right->reg;
					break;
				case Vinsla:
					gins(oc, tl, tr + 1);
					p->from.index = tr->reg;
					break;
				case Vinsra:
					gins(oc, tl, tr);
					p->from.index = (tr + 1)->reg;
					break;
				}
				break;

			case VT:
				g = true;
				i++;
				break;
			case VF:
				g = !true;
				i++;
				break;

			case V_T0: case V_T1:
				g = args[op - V_T0];
				i++;
				break;

			case V_F0: case V_F1:
				g = !args[op - V_F0];
				i++;
				break;

			case V_C0: case V_C1:
				if(g)
					args[op - V_C0] = 0;
				i++;
				break;

			case V_S0: case V_S1:
				if(g)
					args[op - V_S0] = 1;
				i++;
				break;

			case V_l_lo_f:
				g = lo64v(l) == 0;
				i++;
				break;
			case V_l_hi_f:
				g = hi64v(l) == 0;
				i++;
				break;
			case V_l_lo_t:
				g = lo64v(l) != 0;
				i++;
				break;
			case V_l_hi_t:
				g = hi64v(l) != 0;
				i++;
				break;
			case V_l_lo_u:
				g = lo64v(l) >= 0;
				i++;
				break;
			case V_l_hi_u:
				g = hi64v(l) >= 0;
				i++;
				break;
			case V_r_lo_f:
				g = lo64v(r) == 0;
				i++;
				break;
			case V_r_hi_f:
				g = hi64v(r) == 0;
				i++;
				break;
			case V_r_lo_t:
				g = lo64v(r) != 0;
				i++;
				break;
			case V_r_hi_t:
				g = hi64v(r) != 0;
				i++;
				break;
			case V_r_lo_u:
				g = lo64v(r) >= 0;
				i++;
				break;
			case V_r_hi_u:
				g = hi64v(r) >= 0;
				i++;
				break;

			case Vend:
				goto out;

			case V_a0: case V_a1:
				if(g) {
					lt = l->type;
					l->type = types[TULONG];
					regalloc(&tmps[op - V_a0], l, Z);
					l->type = lt;
				}
				i++;
				break;

			case V_f0: case V_f1:
				if(g)
					regfree(&tmps[op - V_f0]);
				i++;
				break;

			case V_p0: case V_p1: case V_p2: case V_p3: case V_p4:
				if(g)
					patch(pr[op - V_p0], pc);
				i++;
				break;

			case V_s0: case V_s1: case V_s2: case V_s3: case V_s4:
				if(g)
					pr[op - V_s0] = p;
				i++;
				break;

			default:
				diag(l, "bad biggen: %d", op);
				return;
			}
			if(i == VLEN || c[i] == 0)
				break;
		}
	}
out:
	if(lo)
		l->xoffset -= lo;
	if(ro)
		r->xoffset -= ro;
	if(to)
		t->xoffset -= to;
}

int
cgen64(Node *n, Node *nn)
{
	Type *dt;
	uchar *args, (*cp)[VLEN], (**optab)[VLEN];
	int li, ri, lri, dr, si, m, op, sh, cmp, true;
	Node *c, *d, *l, *r, *t, *s, nod1, nod2, nod3, nod4, nod5;

	if(debug['g']) {
		prtree(nn, "cgen64 lhs");
		prtree(n, "cgen64");
		print("AX = %d\n", reg[D_AX]);
	}

	if(nn != Z && nn->complex >= FNX) {
		// Evaluate nn address to register
		// before we use registers for n.
		// Otherwise the call during computation of nn
		// will smash the registers.  See
		// http://golang.org/issue/3501.

		// If both n and nn want calls, refuse to compile.
		if(n != Z && n->complex >= FNX)
			diag(n, "cgen64 miscompile");

		reglcgen(&nod1, nn, Z);
		m = cgen64(n, &nod1);
		regfree(&nod1);
		
		if(m == 0) {
			// Now what?  We computed &nn, which involved a
			// function call, and didn't use it.  The caller will recompute nn,
			// calling the function a second time.
			// We can figure out what to do later, if this actually happens.
			diag(n, "cgen64 miscompile");
		}

		return m;
	}

	cmp = 0;
	sh = 0;

	switch(n->op) {
	case ONEG:
		d = regpair(nn, n);
		sugen(n->left, d, 8);
		gins(ANOTL, Z, d->right);
		gins(ANEGL, Z, d->left);
		gins(ASBBL, nodconst(-1), d->right);
		break;

	case OCOM:
		if(!vaddr(n->left, 0) || !vaddr(nn, 0))
			d = regpair(nn, n);
		else
			return 0;
		sugen(n->left, d, 8);
		gins(ANOTL, Z, d->left);
		gins(ANOTL, Z, d->right);
		break;

	case OADD:
		optab = ADDtab;
		args = ADDargs;
		goto twoop;
	case OAND:
		optab = ANDtab;
		args = ANDargs;
		goto twoop;
	case OOR:
		optab = ORtab;
		args = ORargs;
		goto twoop;
	case OSUB:
		optab = SUBtab;
		args = SUBargs;
		goto twoop;
	case OXOR:
		optab = ORtab;
		args = XORargs;
		goto twoop;
	case OASHL:
		sh = 1;
		args = nil;
		optab = shlltab;
		goto twoop;
	case OLSHR:
		sh = 1;
		args = shrlargs;
		optab = shrltab;
		goto twoop;
	case OASHR:
		sh = 1;
		args = sarlargs;
		optab = shrltab;
		goto twoop;
	case OEQ:
		cmp = 1;
		args = nil;
		optab = nil;
		goto twoop;
	case ONE:
		cmp = 1;
		args = nil;
		optab = nil;
		goto twoop;
	case OLE:
		cmp = 1;
		args = nil;
		optab = nil;
		goto twoop;
	case OLT:
		cmp = 1;
		args = nil;
		optab = nil;
		goto twoop;
	case OGE:
		cmp = 1;
		args = nil;
		optab = nil;
		goto twoop;
	case OGT:
		cmp = 1;
		args = nil;
		optab = nil;
		goto twoop;
	case OHI:
		cmp = 1;
		args = nil;
		optab = nil;
		goto twoop;
	case OHS:
		cmp = 1;
		args = nil;
		optab = nil;
		goto twoop;
	case OLO:
		cmp = 1;
		args = nil;
		optab = nil;
		goto twoop;
	case OLS:
		cmp = 1;
		args = nil;
		optab = nil;
		goto twoop;

twoop:
		dr = nn != Z && nn->op == OREGPAIR;
		l = vfunc(n->left, nn);
		if(sh)
			r = n->right;
		else
			r = vfunc(n->right, nn);

		li = l->op == ONAME || l->op == OINDREG || l->op == OCONST;
		ri = r->op == ONAME || r->op == OINDREG || r->op == OCONST;

#define	IMM(l, r)	((l) | ((r) << 1))

		lri = IMM(li, ri);

		/* find out what is so easy about some operands */
		if(li)
			li = whatof(l, sh | cmp);
		if(ri)
			ri = whatof(r, cmp);

		if(sh)
			goto shift;

		if(cmp)
			goto cmp;

		/* evaluate hard subexps, stealing nn if possible. */
		switch(lri) {
		case IMM(0, 0):
		bin00:
			if(l->complex > r->complex) {
				if(dr)
					t = nn;
				else
					t = regpair(Z, n);
				sugen(l, t, 8);
				l = t;
				t = regpair(Z, n);
				sugen(r, t, 8);
				r = t;
			}
			else {
				t = regpair(Z, n);
				sugen(r, t, 8);
				r = t;
				if(dr)
					t = nn;
				else
					t = regpair(Z, n);
				sugen(l, t, 8);
				l = t;
			}
			break;
		case IMM(0, 1):
			if(dr)
				t = nn;
			else
				t = regpair(Z, n);
			sugen(l, t, 8);
			l = t;
			break;
		case IMM(1, 0):
			if(n->op == OSUB && l->op == OCONST && hi64v(l) == 0) {
				lri = IMM(0, 0);
				goto bin00;
			}
			if(dr)
				t = nn;
			else
				t = regpair(Z, n);
			sugen(r, t, 8);
			r = t;
			break;
		case IMM(1, 1):
			break;
		}

#define	WW(l, r)	((l) | ((r) << 2))
		d = Z;
		dt = nn->type;
		nn->type = types[TLONG];

		switch(lri) {
		case IMM(0, 0):
			biggen(l, r, Z, 0, binop00, args);
			break;
		case IMM(0, 1):
			switch(ri) {
			case WNONE:
				diag(r, "bad whatof\n");
				break;
			case WCONST:
				biggen(l, r, Z, 0, optab[B0c], args);
				break;
			case WHARD:
				reglcgen(&nod2, r, Z);
				r = &nod2;
				/* fall thru */
			case WADDR:
				biggen(l, r, Z, 0, binoptmp, args);
				if(ri == WHARD)
					regfree(r);
				break;
			}
			break;
		case IMM(1, 0):
			if(n->op == OSUB) {
				switch(li) {
				case WNONE:
					diag(l, "bad whatof\n");
					break;
				case WHARD:
					reglcgen(&nod2, l, Z);
					l = &nod2;
					/* fall thru */
				case WADDR:
				case WCONST:
					biggen(l, r, Z, 0, sub10, args);
					break;
				}
				if(li == WHARD)
					regfree(l);
			}
			else {
				switch(li) {
				case WNONE:
					diag(l, "bad whatof\n");
					break;
				case WCONST:
					biggen(r, l, Z, 0, optab[B0c], args);
					break;
				case WHARD:
					reglcgen(&nod2, l, Z);
					l = &nod2;
					/* fall thru */
				case WADDR:
					biggen(r, l, Z, 0, binoptmp, args);
					if(li == WHARD)
						regfree(l);
					break;
				}
			}
			break;
		case IMM(1, 1):
			switch(WW(li, ri)) {
			case WW(WCONST, WHARD):
				if(r->op == ONAME && n->op == OAND && reduxv(l))
					ri = WADDR;
				break;
			case WW(WHARD, WCONST):
				if(l->op == ONAME && n->op == OAND && reduxv(r))
					li = WADDR;
				break;
			}
			if(li == WHARD) {
				reglcgen(&nod3, l, Z);
				l = &nod3;
			}
			if(ri == WHARD) {
				reglcgen(&nod2, r, Z);
				r = &nod2;
			}
			d = regpair(nn, n);
			instpair(d, Z);
			switch(WW(li, ri)) {
			case WW(WCONST, WADDR):
			case WW(WCONST, WHARD):
				biggen(l, r, d, 0, optab[Bca], args);
				break;

			case WW(WADDR, WCONST):
			case WW(WHARD, WCONST):
				biggen(l, r, d, 0, optab[Bac], args);
				break;

			case WW(WADDR, WADDR):
			case WW(WADDR, WHARD):
			case WW(WHARD, WADDR):
			case WW(WHARD, WHARD):
				biggen(l, r, d, 0, binop11, args);
				break;

			default:
				diag(r, "bad whatof pair %d %d\n", li, ri);
				break;
			}
			if(li == WHARD)
				regfree(l);
			if(ri == WHARD)
				regfree(r);
			break;
		}

		nn->type = dt;

		if(d != Z)
			goto finished;

		switch(lri) {
		case IMM(0, 0):
			freepair(r);
			/* fall thru */;
		case IMM(0, 1):
			if(!dr)
				storepair(l, nn, 1);
			break;
		case IMM(1, 0):
			if(!dr)
				storepair(r, nn, 1);
			break;
		case IMM(1, 1):
			break;
		}
		return 1;

	shift:
		c = Z;

		/* evaluate hard subexps, stealing nn if possible. */
		/* must also secure CX.  not as many optims as binop. */
		switch(lri) {
		case IMM(0, 0):
		imm00:
			if(l->complex + 1 > r->complex) {
				if(dr)
					t = nn;
				else
					t = regpair(Z, l);
				sugen(l, t, 8);
				l = t;
				t = &nod1;
				c = snarfreg(l, t, D_CX, r, &nod2);
				cgen(r, t);
				r = t;
			}
			else {
				t = &nod1;
				c = snarfreg(nn, t, D_CX, r, &nod2);
				cgen(r, t);
				r = t;
				if(dr)
					t = nn;
				else
					t = regpair(Z, l);
				sugen(l, t, 8);
				l = t;
			}
			break;
		case IMM(0, 1):
		imm01:
			if(ri != WCONST) {
				lri = IMM(0, 0);
				goto imm00;
			}
			if(dr)
				t = nn;
			else
				t = regpair(Z, n);
			sugen(l, t, 8);
			l = t;
			break;
		case IMM(1, 0):
		imm10:
			if(li != WCONST) {
				lri = IMM(0, 0);
				goto imm00;
			}
			t = &nod1;
			c = snarfreg(nn, t, D_CX, r, &nod2);
			cgen(r, t);
			r = t;
			break;
		case IMM(1, 1):
			if(ri != WCONST) {
				lri = IMM(1, 0);
				goto imm10;
			}
			if(li == WHARD) {
				lri = IMM(0, 1);
				goto imm01;
			}
			break;
		}

		d = Z;

		switch(lri) {
		case IMM(0, 0):
			biggen(l, r, Z, 0, optab[S00], args);
			break;
		case IMM(0, 1):
			switch(ri) {
			case WNONE:
			case WADDR:
			case WHARD:
				diag(r, "bad whatof\n");
				break;
			case WCONST:
				m = r->vconst & 63;
				s = nodconst(m);
				if(m < 32)
					cp = optab[Sc0];
				else if(m == 32)
					cp = optab[Sc1];
				else
					cp = optab[Sc2];
				biggen(l, s, Z, 0, cp, args);
				break;
			}
			break;
		case IMM(1, 0):
			/* left is const */
			d = regpair(nn, n);
			instpair(d, Z);
			biggen(l, r, d, 0, optab[S10], args);
			regfree(r);
			break;
		case IMM(1, 1):
			d = regpair(nn, n);
			instpair(d, Z);
			switch(WW(li, ri)) {
			case WW(WADDR, WCONST):
				m = r->vconst & 63;
				s = nodconst(m);
				if(m < 32) {
					loadpair(l, d);
					l = d;
					cp = optab[Sc0];
				}
				else if(m == 32)
					cp = optab[Sac3];
				else
					cp = optab[Sac4];
				biggen(l, s, d, 0, cp, args);
				break;

			default:
				diag(r, "bad whatof pair %d %d\n", li, ri);
				break;
			}
			break;
		}

		if(c != Z) {
			gins(AMOVL, c, r);
			regfree(c);
		}

		if(d != Z)
			goto finished;

		switch(lri) {
		case IMM(0, 0):
			regfree(r);
			/* fall thru */
		case IMM(0, 1):
			if(!dr)
				storepair(l, nn, 1);
			break;
		case IMM(1, 0):
			regfree(r);
			break;
		case IMM(1, 1):
			break;
		}
		return 1;

	cmp:
		op = n->op;
		/* evaluate hard subexps */
		switch(lri) {
		case IMM(0, 0):
			if(l->complex > r->complex) {
				t = regpair(Z, l);
				sugen(l, t, 8);
				l = t;
				t = regpair(Z, r);
				sugen(r, t, 8);
				r = t;
			}
			else {
				t = regpair(Z, r);
				sugen(r, t, 8);
				r = t;
				t = regpair(Z, l);
				sugen(l, t, 8);
				l = t;
			}
			break;
		case IMM(1, 0):
			t = r;
			r = l;
			l = t;
			ri = li;
			op = invrel[relindex(op)];
			/* fall thru */
		case IMM(0, 1):
			t = regpair(Z, l);
			sugen(l, t, 8);
			l = t;
			break;
		case IMM(1, 1):
			break;
		}

		true = 1;
		optab = cmptab;
		switch(op) {
		case OEQ:
			optab = NEtab;
			true = 0;
			break;
		case ONE:
			optab = NEtab;
			break;
		case OLE:
			args = GTargs;
			true = 0;
			break;
		case OGT:
			args = GTargs;
			break;
		case OLS:
			args = HIargs;
			true = 0;
			break;
		case OHI:
			args = HIargs;
			break;
		case OLT:
			args = GEargs;
			true = 0;
			break;
		case OGE:
			args = GEargs;
			break;
		case OLO:
			args = HSargs;
			true = 0;
			break;
		case OHS:
			args = HSargs;
			break;
		default:
			diag(n, "bad cmp\n");
			SET(optab);
		}

		switch(lri) {
		case IMM(0, 0):
			biggen(l, r, Z, true, optab[T0i], args);
			break;
		case IMM(0, 1):
		case IMM(1, 0):
			switch(ri) {
			case WNONE:
				diag(l, "bad whatof\n");
				break;
			case WCONST:
				biggen(l, r, Z, true, optab[T0i], args);
				break;
			case WHARD:
				reglcgen(&nod2, r, Z);
				r = &nod2;
				/* fall thru */
			case WADDR:
				biggen(l, r, Z, true, optab[T0i], args);
				if(ri == WHARD)
					regfree(r);
				break;
			}
			break;
		case IMM(1, 1):
			if(li == WHARD) {
				reglcgen(&nod3, l, Z);
				l = &nod3;
			}
			if(ri == WHARD) {
				reglcgen(&nod2, r, Z);
				r = &nod2;
			}
			biggen(l, r, Z, true, optab[Tii], args);
			if(li == WHARD)
				regfree(l);
			if(ri == WHARD)
				regfree(r);
			break;
		}

		switch(lri) {
		case IMM(0, 0):
			freepair(r);
			/* fall thru */;
		case IMM(0, 1):
		case IMM(1, 0):
			freepair(l);
			break;
		case IMM(1, 1):
			break;
		}
		return 1;

	case OASMUL:
	case OASLMUL:
		m = 0;
		goto mulop;

	case OMUL:
	case OLMUL:
		m = 1;
		goto mulop;

	mulop:
		dr = nn != Z && nn->op == OREGPAIR;
		l = vfunc(n->left, nn);
		r = vfunc(n->right, nn);
		if(r->op != OCONST) {
			if(l->complex > r->complex) {
				if(m) {
					t = l;
					l = r;
					r = t;
				}
				else if(!vaddr(l, 1)) {
					reglcgen(&nod5, l, Z);
					l = &nod5;
					evacaxdx(l);
				}
			}
			t = regpair(Z, n);
			sugen(r, t, 8);
			r = t;
			evacaxdx(r->left);
			evacaxdx(r->right);
			if(l->complex <= r->complex && !m && !vaddr(l, 1)) {
				reglcgen(&nod5, l, Z);
				l = &nod5;
				evacaxdx(l);
			}
		}
		if(dr)
			t = nn;
		else
			t = regpair(Z, n);
		c = Z;
		d = Z;
		if(!nodreg(&nod1, t->left, D_AX)) {
			if(t->left->reg != D_AX){
				t->left->reg = D_AX;
				reg[D_AX]++;
			}else if(reg[D_AX] == 0)
				fatal(Z, "vlong mul AX botch");
		}
		if(!nodreg(&nod2, t->right, D_DX)) {
			if(t->right->reg != D_DX){
				t->right->reg = D_DX;
				reg[D_DX]++;
			}else if(reg[D_DX] == 0)
				fatal(Z, "vlong mul DX botch");
		}
		if(m)
			sugen(l, t, 8);
		else
			loadpair(l, t);
		if(t->left->reg != D_AX) {
			c = &nod3;
			regsalloc(c, t->left);
			gmove(&nod1, c);
			gmove(t->left, &nod1);
			zapreg(t->left);
		}
		if(t->right->reg != D_DX) {
			d = &nod4;
			regsalloc(d, t->right);
			gmove(&nod2, d);
			gmove(t->right, &nod2);
			zapreg(t->right);
		}
		if(c != Z || d != Z) {
			s = regpair(Z, n);
			s->left = &nod1;
			s->right = &nod2;
		}
		else
			s = t;
		if(r->op == OCONST) {
			if(hi64v(r) == 0)
				biggen(s, r, Z, 0, mulc32, nil);
			else
				biggen(s, r, Z, 0, mulc64, nil);
		}
		else
			biggen(s, r, Z, 0, mull, nil);
		instpair(t, Z);
		if(c != Z) {
			gmove(&nod1, t->left);
			gmove(&nod3, &nod1);
		}
		if(d != Z) {
			gmove(&nod2, t->right);
			gmove(&nod4, &nod2);
		}
		if(r->op == OREGPAIR)
			freepair(r);
		if(!m)
			storepair(t, l, 0);
		if(l == &nod5)
			regfree(l);
		if(!dr) {
			if(nn != Z)
				storepair(t, nn, 1);
			else
				freepair(t);
		}
		return 1;

	case OASADD:
		args = ADDargs;
		goto vasop;
	case OASAND:
		args = ANDargs;
		goto vasop;
	case OASOR:
		args = ORargs;
		goto vasop;
	case OASSUB:
		args = SUBargs;
		goto vasop;
	case OASXOR:
		args = XORargs;
		goto vasop;

	vasop:
		l = n->left;
		r = n->right;
		dr = nn != Z && nn->op == OREGPAIR;
		m = 0;
		if(l->complex > r->complex) {
			if(!vaddr(l, 1)) {
				reglcgen(&nod1, l, Z);
				l = &nod1;
			}
			if(!vaddr(r, 1) || nn != Z || r->op == OCONST) {
				if(dr)
					t = nn;
				else
					t = regpair(Z, r);
				sugen(r, t, 8);
				r = t;
				m = 1;
			}
		}
		else {
			if(!vaddr(r, 1) || nn != Z || r->op == OCONST) {
				if(dr)
					t = nn;
				else
					t = regpair(Z, r);
				sugen(r, t, 8);
				r = t;
				m = 1;
			}
			if(!vaddr(l, 1)) {
				reglcgen(&nod1, l, Z);
				l = &nod1;
			}
		}
		if(nn != Z) {
			if(n->op == OASSUB)
				biggen(l, r, Z, 0, sub10, args);
			else
				biggen(r, l, Z, 0, binoptmp, args);
			storepair(r, l, 0);
		}
		else {
			if(m)
				biggen(l, r, Z, 0, binop00, args);
			else
				biggen(l, r, Z, 0, binoptmp, args);
		}
		if(l == &nod1)
			regfree(&nod1);
		if(m) {
			if(nn == Z)
				freepair(r);
			else if(!dr)
				storepair(r, nn, 1);
		}
		return 1;

	case OASASHL:
		args = nil;
		optab = asshlltab;
		goto assh;
	case OASLSHR:
		args = shrlargs;
		optab = asshrltab;
		goto assh;
	case OASASHR:
		args = sarlargs;
		optab = asshrltab;
		goto assh;

	assh:
		c = Z;
		l = n->left;
		r = n->right;
		if(r->op == OCONST) {
			m = r->vconst & 63;
			if(m < 32)
				m = SAclo;
			else if(m == 32)
				m = SAc32;
			else
				m = SAchi;
		}
		else
			m = SAgen;
		if(l->complex > r->complex) {
			if(!vaddr(l, 0)) {
				reglcgen(&nod1, l, Z);
				l = &nod1;
			}
			if(m == SAgen) {
				t = &nod2;
				if(l->reg == D_CX) {
					regalloc(t, r, Z);
					gmove(l, t);
					l->reg = t->reg;
					t->reg = D_CX;
				}
				else
					c = snarfreg(nn, t, D_CX, r, &nod3);
				cgen(r, t);
				r = t;
			}
		}
		else {
			if(m == SAgen) {
				t = &nod2;
				c = snarfreg(nn, t, D_CX, r, &nod3);
				cgen(r, t);
				r = t;
			}
			if(!vaddr(l, 0)) {
				reglcgen(&nod1, l, Z);
				l = &nod1;
			}
		}

		if(nn != Z) {
			m += SAdgen - SAgen;
			d = regpair(nn, n);
			instpair(d, Z);
			biggen(l, r, d, 0, optab[m], args);
			if(l == &nod1) {
				regfree(&nod1);
				l = Z;
			}
			if(r == &nod2 && c == Z) {
				regfree(&nod2);
				r = Z;
			}
			if(d != nn)
				storepair(d, nn, 1);
		}
		else
			biggen(l, r, Z, 0, optab[m], args);

		if(c != Z) {
			gins(AMOVL, c, r);
			regfree(c);
		}
		if(l == &nod1)
			regfree(&nod1);
		if(r == &nod2)
			regfree(&nod2);
		return 1;

	case OPOSTINC:
		args = ADDargs;
		cp = incdecpost;
		goto vinc;
	case OPOSTDEC:
		args = SUBargs;
		cp = incdecpost;
		goto vinc;
	case OPREINC:
		args = ADDargs;
		cp = incdecpre;
		goto vinc;
	case OPREDEC:
		args = SUBargs;
		cp = incdecpre;
		goto vinc;

	vinc:
		l = n->left;
		if(!vaddr(l, 1)) {
			reglcgen(&nod1, l, Z);
			l = &nod1;
		}
		
		if(nn != Z) {
			d = regpair(nn, n);
			instpair(d, Z);
			biggen(l, Z, d, 0, cp, args);
			if(l == &nod1) {
				regfree(&nod1);
				l = Z;
			}
			if(d != nn)
				storepair(d, nn, 1);
		}
		else
			biggen(l, Z, Z, 0, incdec, args);

		if(l == &nod1)
			regfree(&nod1);
		return 1;

	case OCAST:
		l = n->left;
		if(typev[l->type->etype]) {
			if(!vaddr(l, 1)) {
				if(l->complex + 1 > nn->complex) {
					d = regpair(Z, l);
					sugen(l, d, 8);
					if(!vaddr(nn, 1)) {
						reglcgen(&nod1, nn, Z);
						r = &nod1;
					}
					else
						r = nn;
				}
				else {
					if(!vaddr(nn, 1)) {
						reglcgen(&nod1, nn, Z);
						r = &nod1;
					}
					else
						r = nn;
					d = regpair(Z, l);
					sugen(l, d, 8);
				}
//				d->left->type = r->type;
				d->left->type = types[TLONG];
				gmove(d->left, r);
				freepair(d);
			}
			else {
				if(nn->op != OREGISTER && !vaddr(nn, 1)) {
					reglcgen(&nod1, nn, Z);
					r = &nod1;
				}
				else
					r = nn;
//				l->type = r->type;
				l->type = types[TLONG];
				gmove(l, r);
			}
			if(r != nn)
				regfree(r);
		}
		else {
			if(typeu[l->type->etype] || cond(l->op))
				si = TUNSIGNED;
			else
				si = TSIGNED;
			regalloc(&nod1, l, Z);
			cgen(l, &nod1);
			if(nn->op == OREGPAIR) {
				m = instpair(nn, &nod1);
				biggen(&nod1, Z, nn, si == TSIGNED, castrp, nil);
			}
			else {
				m = 0;
				if(!vaddr(nn, si != TSIGNED)) {
					dt = nn->type;
					nn->type = types[TLONG];
					reglcgen(&nod2, nn, Z);
					nn->type = dt;
					nn = &nod2;
				}
				dt = nn->type;
				nn->type = types[TLONG];
				biggen(&nod1, Z, nn, si == TSIGNED, castrpa, nil);
				nn->type = dt;
				if(nn == &nod2)
					regfree(&nod2);
			}
			if(!m)
				regfree(&nod1);
		}
		return 1;

	default:
		if(n->op == OREGPAIR) {
			storepair(n, nn, 1);
			return 1;
		}
		if(nn->op == OREGPAIR) {
			loadpair(n, nn);
			return 1;
		}
		return 0;
	}
finished:
	if(d != nn)
		storepair(d, nn, 1);
	return 1;
}

void
testv(Node *n, int true)
{
	Type *t;
	Node *nn, nod;

	switch(n->op) {
	case OINDREG:
	case ONAME:
		biggen(n, Z, Z, true, testi, nil);
		break;

	default:
		n = vfunc(n, n);
		if(n->addable >= INDEXED) {
			t = n->type;
			n->type = types[TLONG];
			reglcgen(&nod, n, Z);
			n->type = t;
			n = &nod;
			biggen(n, Z, Z, true, testi, nil);
			if(n == &nod)
				regfree(n);
		}
		else {
			nn = regpair(Z, n);
			sugen(n, nn, 8);
			biggen(nn, Z, Z, true, testi, nil);
			freepair(nn);
		}
	}
}
