#include "gc.h"

static	int	resvreg[nelem(reg)];

void
ginit(void)
{
	int i;

	thechar = '6';
	thestring = "amd64";
	exregoffset = REGEXT;
	exfregoffset = FREGEXT;
	listinit();
	nstring = 0;
	mnstring = 0;
	pc = 0;
	breakpc = -1;
	continpc = -1;
	cases = C;
	firstp = P;
	lastp = P;
	tfield = types[TINT];

	typeword = typechlvp;
	typeswitch = typechlv;
	typecmplx = typesu;

	/* TO DO */
	memmove(typechlpv, typechlp, sizeof(typechlpv));
	typechlpv[TVLONG] = 1;
	typechlpv[TUVLONG] = 1;

	zprog.link = P;
	zprog.as = AGOK;
	zprog.from.type = D_NONE;
	zprog.from.index = D_NONE;
	zprog.from.scale = 0;
	zprog.to = zprog.from;

	lregnode.op = OREGISTER;
	lregnode.class = CEXREG;
	lregnode.reg = REGTMP;
	lregnode.complex = 0;
	lregnode.addable = 11;
	lregnode.type = types[TLONG];

	qregnode = lregnode;
	qregnode.type = types[TVLONG];

	constnode.op = OCONST;
	constnode.class = CXXX;
	constnode.complex = 0;
	constnode.addable = 20;
	constnode.type = types[TLONG];

	vconstnode = constnode;
	vconstnode.type = types[TVLONG];

	fconstnode.op = OCONST;
	fconstnode.class = CXXX;
	fconstnode.complex = 0;
	fconstnode.addable = 20;
	fconstnode.type = types[TDOUBLE];

	nodsafe = new(ONAME, Z, Z);
	nodsafe->sym = slookup(".safe");
	nodsafe->type = types[TINT];
	nodsafe->etype = types[TINT]->etype;
	nodsafe->class = CAUTO;
	complex(nodsafe);

	nodret = new(ONAME, Z, Z);
	nodret->sym = slookup(".ret");
	nodret->type = types[TIND];
	nodret->etype = TIND;
	nodret->class = CPARAM;
	nodret = new(OIND, nodret, Z);
	complex(nodret);

	if(0)
		com64init();

	memset(reg, 0, sizeof(reg));
	for(i=0; i<nelem(reg); i++) {
		reg[i] = 1;
		if(i >= D_AX && i <= D_R15 && i != D_SP)
			reg[i] = 0;
		if(i >= D_X0 && i <= D_X7)
			reg[i] = 0;
	}
	/* keep two external registers */
	reg[REGEXT] = 1;
	reg[REGEXT-1] = 1;
	memmove(resvreg, reg, sizeof(resvreg));
}

void
gclean(void)
{
	int i;
	Sym *s;

	reg[D_SP]--;
	for(i=D_AX; i<=D_R15; i++)
		if(reg[i] && !resvreg[i])
			diag(Z, "reg %R left allocated", i);
	for(i=D_X0; i<=D_X7; i++)
		if(reg[i] && !resvreg[i])
			diag(Z, "reg %R left allocated", i);
	while(mnstring)
		outstring("", 1L);
	symstring->type->width = nstring;
	for(i=0; i<NHASH; i++)
	for(s = hash[i]; s != S; s = s->link) {
		if(s->type == T)
			continue;
		if(s->type->width == 0)
			continue;
		if(s->class != CGLOBL && s->class != CSTATIC)
			continue;
		if(s->type == types[TENUM])
			continue;
		gpseudo(AGLOBL, s, nodconst(s->type->width));
	}
	nextpc();
	p->as = AEND;
	outcode();
}

void
nextpc(void)
{

	p = alloc(sizeof(*p));
	*p = zprog;
	p->lineno = nearln;
	pc++;
	if(firstp == P) {
		firstp = p;
		lastp = p;
		return;
	}
	lastp->link = p;
	lastp = p;
}

void
gargs(Node *n, Node *tn1, Node *tn2)
{
	long regs;
	Node fnxargs[20], *fnxp;

	regs = cursafe;

	fnxp = fnxargs;
	garg1(n, tn1, tn2, 0, &fnxp);	/* compile fns to temps */

	curarg = 0;
	fnxp = fnxargs;
	garg1(n, tn1, tn2, 1, &fnxp);	/* compile normal args and temps */

	cursafe = regs;
}

int
nareg(void)
{
	int i, n;

	n = 0;
	for(i=D_AX; i<=D_R15; i++)
		if(reg[i] == 0 && !resvreg[i])
			n++;
	return n;
}

void
garg1(Node *n, Node *tn1, Node *tn2, int f, Node **fnxp)
{
	Node nod;

	if(n == Z)
		return;
	if(n->op == OLIST) {
		garg1(n->left, tn1, tn2, f, fnxp);
		garg1(n->right, tn1, tn2, f, fnxp);
		return;
	}
	if(f == 0) {
		if(n->complex >= FNX) {
			regsalloc(*fnxp, n);
			nod = znode;
			nod.op = OAS;
			nod.left = *fnxp;
			nod.right = n;
			nod.type = n->type;
			cgen(&nod, Z);
			(*fnxp)++;
		}
		return;
	}
	if(typesu[n->type->etype]) {
		regaalloc(tn2, n);
		if(n->complex >= FNX) {
			sugen(*fnxp, tn2, n->type->width);
			(*fnxp)++;
		} else
			sugen(n, tn2, n->type->width);
		return;
	}
	if(REGARG && curarg == 0 && typechlpv[n->type->etype]) {
		regaalloc1(tn1, n);
		if(n->complex >= FNX) {
			cgen(*fnxp, tn1);
			(*fnxp)++;
		} else
			cgen(n, tn1);
		return;
	}
	if(vconst(n) == 0) {
		regaalloc(tn2, n);
		gmove(n, tn2);
		return;
	}
	regalloc(tn1, n, Z);
	if(n->complex >= FNX) {
		cgen(*fnxp, tn1);
		(*fnxp)++;
	} else
		cgen(n, tn1);
	regaalloc(tn2, n);
	gmove(tn1, tn2);
	regfree(tn1);
}

Node*
nodgconst(vlong v, Type *t)
{
	if(!typev[t->etype])
		return nodconst((long)v);
	vconstnode.vconst = v;
	return &vconstnode;
}

Node*
nodconst(long v)
{
	constnode.vconst = v;
	return &constnode;
}

Node*
nodfconst(double d)
{
	fconstnode.fconst = d;
	return &fconstnode;
}

int
isreg(Node *n, int r)
{

	if(n->op == OREGISTER)
		if(n->reg == r)
			return 1;
	return 0;
}

int
nodreg(Node *n, Node *nn, int r)
{
	int et;

	*n = qregnode;
	n->reg = r;
	if(nn != Z){
		et = nn->type->etype;
		if(!typefd[et] && nn->type->width <= SZ_LONG && 0)
			n->type = typeu[et]? types[TUINT]: types[TINT];
		else
			n->type = nn->type;
//print("nodreg %s [%s]\n", tnames[et], tnames[n->type->etype]);
		n->lineno = nn->lineno;
	}
	if(reg[r] == 0)
		return 0;
	if(nn != Z) {
		if(nn->op == OREGISTER)
		if(nn->reg == r)
			return 0;
	}
	return 1;
}

void
regret(Node *n, Node *nn)
{
	int r;

	r = REGRET;
	if(typefd[nn->type->etype])
		r = FREGRET;
	nodreg(n, nn, r);
	reg[r]++;
}

void
regalloc(Node *n, Node *tn, Node *o)
{
	int i;

	switch(tn->type->etype) {
	case TCHAR:
	case TUCHAR:
	case TSHORT:
	case TUSHORT:
	case TINT:
	case TUINT:
	case TLONG:
	case TULONG:
	case TVLONG:
	case TUVLONG:
	case TIND:
		if(o != Z && o->op == OREGISTER) {
			i = o->reg;
			if(i >= D_AX && i <= D_R15)
				goto out;
		}
		for(i=D_AX; i<=D_R15; i++){
			i ^= 7;
			if(reg[i] == 0 && !resvreg[i])
				goto out;
			i ^= 7;
		}
		diag(tn, "out of fixed registers");
		goto err;

	case TFLOAT:
	case TDOUBLE:
		if(o != Z && o->op == OREGISTER) {
			i = o->reg;
			if(i >= D_X0 && i <= D_X7)
				goto out;
		}
		for(i=D_X0; i<=D_X7; i++)
			if(reg[i] == 0 && !resvreg[i])
				goto out;
		diag(tn, "out of float registers");
		goto out;
	}
	diag(tn, "unknown type in regalloc: %T", tn->type);
err:
	i = 0;
out:
	if(i)
		reg[i]++;
	nodreg(n, tn, i);
}

void
regialloc(Node *n, Node *tn, Node *o)
{
	Node nod;

	nod = *tn;
	nod.type = types[TIND];
	regalloc(n, &nod, o);
}

void
regfree(Node *n)
{
	int i;

	i = 0;
	if(n->op != OREGISTER && n->op != OINDREG)
		goto err;
	i = n->reg;
	if(i < 0 || i >= sizeof(reg))
		goto err;
	if(reg[i] <= 0)
		goto err;
	reg[i]--;
	return;
err:
	diag(n, "error in regfree: %R", i);
}

void
regsalloc(Node *n, Node *nn)
{
	cursafe = align(cursafe, nn->type, Aaut3);
	maxargsafe = maxround(maxargsafe, cursafe+curarg);
	*n = *nodsafe;
	n->xoffset = -(stkoff + cursafe);
	n->type = nn->type;
	n->etype = nn->type->etype;
	n->lineno = nn->lineno;
}

void
regaalloc1(Node *n, Node *nn)
{
	nodreg(n, nn, REGARG);
	reg[REGARG]++;
	curarg = align(curarg, nn->type, Aarg1);
	curarg = align(curarg, nn->type, Aarg2);
	maxargsafe = maxround(maxargsafe, cursafe+curarg);
}

void
regaalloc(Node *n, Node *nn)
{
	curarg = align(curarg, nn->type, Aarg1);
	*n = *nn;
	n->op = OINDREG;
	n->reg = REGSP;
	n->xoffset = curarg;
	n->complex = 0;
	n->addable = 20;
	curarg = align(curarg, nn->type, Aarg2);
	maxargsafe = maxround(maxargsafe, cursafe+curarg);
}

void
regind(Node *n, Node *nn)
{

	if(n->op != OREGISTER) {
		diag(n, "regind not OREGISTER");
		return;
	}
	n->op = OINDREG;
	n->type = nn->type;
}

void
naddr(Node *n, Adr *a)
{
	long v;

	a->type = D_NONE;
	if(n == Z)
		return;
	switch(n->op) {
	default:
	bad:
		diag(n, "bad in naddr: %O %D", n->op, a);
		break;

	case OREGISTER:
		a->type = n->reg;
		a->sym = S;
		break;


	case OIND:
		naddr(n->left, a);
		if(a->type >= D_AX && a->type <= D_R15)
			a->type += D_INDIR;
		else
		if(a->type == D_CONST)
			a->type = D_NONE+D_INDIR;
		else
		if(a->type == D_ADDR) {
			a->type = a->index;
			a->index = D_NONE;
		} else
			goto bad;
		break;

	case OINDEX:
		a->type = idx.ptr;
		if(n->left->op == OADDR || n->left->op == OCONST)
			naddr(n->left, a);
		if(a->type >= D_AX && a->type <= D_R15)
			a->type += D_INDIR;
		else
		if(a->type == D_CONST)
			a->type = D_NONE+D_INDIR;
		else
		if(a->type == D_ADDR) {
			a->type = a->index;
			a->index = D_NONE;
		} else
			goto bad;
		a->index = idx.reg;
		a->scale = n->scale;
		a->offset += n->xoffset;
		break;

	case OINDREG:
		a->type = n->reg+D_INDIR;
		a->sym = S;
		a->offset = n->xoffset;
		break;

	case ONAME:
		a->etype = n->etype;
		a->type = D_STATIC;
		a->sym = n->sym;
		a->offset = n->xoffset;
		if(n->class == CSTATIC)
			break;
		if(n->class == CEXTERN || n->class == CGLOBL) {
			a->type = D_EXTERN;
			break;
		}
		if(n->class == CAUTO) {
			a->type = D_AUTO;
			break;
		}
		if(n->class == CPARAM) {
			a->type = D_PARAM;
			break;
		}
		goto bad;

	case OCONST:
		if(typefd[n->type->etype]) {
			a->type = D_FCONST;
			a->dval = n->fconst;
			break;
		}
		a->sym = S;
		a->type = D_CONST;
		if(typev[n->type->etype] || n->type->etype == TIND)
			a->offset = n->vconst;
		else
			a->offset = convvtox(n->vconst, typeu[n->type->etype]? TULONG: TLONG);
		break;

	case OADDR:
		naddr(n->left, a);
		if(a->type >= D_INDIR) {
			a->type -= D_INDIR;
			break;
		}
		if(a->type == D_EXTERN || a->type == D_STATIC ||
		   a->type == D_AUTO || a->type == D_PARAM)
			if(a->index == D_NONE) {
				a->index = a->type;
				a->type = D_ADDR;
				break;
			}
		goto bad;

	case OADD:
		if(n->right->op == OCONST) {
			v = n->right->vconst;
			naddr(n->left, a);
		} else
		if(n->left->op == OCONST) {
			v = n->left->vconst;
			naddr(n->right, a);
		} else
			goto bad;
		a->offset += v;
		break;

	}
}

void
gcmp(int op, Node *n, vlong val)
{
	Node *cn, nod;

	cn = nodgconst(val, n->type);
	if(!immconst(cn)){
		regalloc(&nod, n, Z);
		gmove(cn, &nod);
		gopcode(op, n->type, n, &nod);
		regfree(&nod);
	}else
		gopcode(op, n->type, n, cn);
}

#define	CASE(a,b)	((a<<8)|(b<<0))

void
gmove(Node *f, Node *t)
{
	int ft, tt, t64, a;
	Node nod, nod1, nod2, nod3;
	Prog *p1, *p2;

	ft = f->type->etype;
	tt = t->type->etype;
	t64 = tt == TVLONG || tt == TUVLONG || tt == TIND;
	if(debug['M'])
		print("gop: %O %O[%s],%O[%s]\n", OAS,
			f->op, tnames[ft], t->op, tnames[tt]);
	if(typefd[ft] && f->op == OCONST) {
		/* TO DO: pick up special constants, possibly preloaded */
		if(f->fconst == 0.0){
			regalloc(&nod, t, t);
			gins(AXORPD, &nod, &nod);
			gmove(&nod, t);
			regfree(&nod);
			return;
		}
	}
/*
 * load
 */
	if(f->op == ONAME || f->op == OINDREG ||
	   f->op == OIND || f->op == OINDEX)
	switch(ft) {
	case TCHAR:
		a = AMOVBLSX;
		if(t64)
			a = AMOVBQSX;
		goto ld;
	case TUCHAR:
		a = AMOVBLZX;
		if(t64)
			a = AMOVBQZX;
		goto ld;
	case TSHORT:
		a = AMOVWLSX;
		if(t64)
			a = AMOVWQSX;
		goto ld;
	case TUSHORT:
		a = AMOVWLZX;
		if(t64)
			a = AMOVWQZX;
		goto ld;
	case TINT:
	case TLONG:
		if(typefd[tt]) {
			regalloc(&nod, t, t);
			if(tt == TDOUBLE)
				a = ACVTSL2SD;
			else
				a = ACVTSL2SS;
			gins(a, f, &nod);
			gmove(&nod, t);
			regfree(&nod);
			return;
		}
		a = AMOVL;
		if(t64)
			a = AMOVLQSX;
		goto ld;
	case TUINT:
	case TULONG:
		a = AMOVL;
		if(t64)
			a = AMOVLQZX;	/* could probably use plain MOVL */
		goto ld;
	case TVLONG:
		if(typefd[tt]) {
			regalloc(&nod, t, t);
			if(tt == TDOUBLE)
				a = ACVTSQ2SD;
			else
				a = ACVTSQ2SS;
			gins(a, f, &nod);
			gmove(&nod, t);
			regfree(&nod);
			return;
		}
	case TUVLONG:
	case TIND:
		a = AMOVQ;
	ld:
		regalloc(&nod, f, t);
		gins(a, f, &nod);
		gmove(&nod, t);
		regfree(&nod);
		return;

	case TFLOAT:
		a = AMOVSS;
		goto ld;
	case TDOUBLE:
		a = AMOVSD;
		goto ld;
	}

/*
 * store
 */
	if(t->op == ONAME || t->op == OINDREG ||
	   t->op == OIND || t->op == OINDEX)
	switch(tt) {
	case TCHAR:
	case TUCHAR:
		a = AMOVB;	goto st;
	case TSHORT:
	case TUSHORT:
		a = AMOVW;	goto st;
	case TINT:
	case TUINT:
	case TLONG:
	case TULONG:
		a = AMOVL;	goto st;
	case TVLONG:
	case TUVLONG:
	case TIND:
		a = AMOVQ;	goto st;

	st:
		if(f->op == OCONST) {
			gins(a, f, t);
			return;
		}
	fst:
		regalloc(&nod, t, f);
		gmove(f, &nod);
		gins(a, &nod, t);
		regfree(&nod);
		return;

	case TFLOAT:
		a = AMOVSS;
		goto fst;
	case TDOUBLE:
		a = AMOVSD;
		goto fst;
	}

/*
 * convert
 */
	switch(CASE(ft,tt)) {
	default:
/*
 * integer to integer
 ********
		a = AGOK;	break;

	case CASE(	TCHAR,	TCHAR):
	case CASE(	TUCHAR,	TCHAR):
	case CASE(	TSHORT,	TCHAR):
	case CASE(	TUSHORT,TCHAR):
	case CASE(	TINT,	TCHAR):
	case CASE(	TUINT,	TCHAR):
	case CASE(	TLONG,	TCHAR):
	case CASE(	TULONG,	TCHAR):

	case CASE(	TCHAR,	TUCHAR):
	case CASE(	TUCHAR,	TUCHAR):
	case CASE(	TSHORT,	TUCHAR):
	case CASE(	TUSHORT,TUCHAR):
	case CASE(	TINT,	TUCHAR):
	case CASE(	TUINT,	TUCHAR):
	case CASE(	TLONG,	TUCHAR):
	case CASE(	TULONG,	TUCHAR):

	case CASE(	TSHORT,	TSHORT):
	case CASE(	TUSHORT,TSHORT):
	case CASE(	TINT,	TSHORT):
	case CASE(	TUINT,	TSHORT):
	case CASE(	TLONG,	TSHORT):
	case CASE(	TULONG,	TSHORT):

	case CASE(	TSHORT,	TUSHORT):
	case CASE(	TUSHORT,TUSHORT):
	case CASE(	TINT,	TUSHORT):
	case CASE(	TUINT,	TUSHORT):
	case CASE(	TLONG,	TUSHORT):
	case CASE(	TULONG,	TUSHORT):

	case CASE(	TINT,	TINT):
	case CASE(	TUINT,	TINT):
	case CASE(	TLONG,	TINT):
	case CASE(	TULONG,	TINT)::

	case CASE(	TINT,	TUINT):
	case CASE(	TUINT,	TUINT):
	case CASE(	TLONG,	TUINT):
	case CASE(	TULONG,	TUINT):
 *****/
		a = AMOVL;
		break;

	case CASE(	TINT,	TIND):
	case CASE(	TINT,	TVLONG):
	case CASE(	TINT,	TUVLONG):
	case CASE(	TLONG,	TIND):
	case CASE(	TLONG,	TVLONG):
	case CASE(	TLONG,	TUVLONG):
		a = AMOVLQSX;
		if(f->op == OCONST) {
			f->vconst &= (uvlong)0xffffffffU;
			if(f->vconst & 0x80000000)
				f->vconst |= (vlong)0xffffffff << 32;
			a = AMOVQ;
		}
		break;

	case CASE(	TUINT,	TIND):
	case CASE(	TUINT,	TVLONG):
	case CASE(	TUINT,	TUVLONG):
	case CASE(	TULONG,	TVLONG):
	case CASE(	TULONG,	TUVLONG):
	case CASE(	TULONG,	TIND):
		a = AMOVLQZX;
		if(f->op == OCONST) {
			f->vconst &= (uvlong)0xffffffffU;
			a = AMOVQ;
		}
		break;

	case CASE(	TIND,	TCHAR):
	case CASE(	TIND,	TUCHAR):
	case CASE(	TIND,	TSHORT):
	case CASE(	TIND,	TUSHORT):
	case CASE(	TIND,	TINT):
	case CASE(	TIND,	TUINT):
	case CASE(	TIND,	TLONG):
	case CASE(	TIND,	TULONG):
	case CASE(	TVLONG,	TCHAR):
	case CASE(	TVLONG,	TUCHAR):
	case CASE(	TVLONG,	TSHORT):
	case CASE(	TVLONG,	TUSHORT):
	case CASE(	TVLONG,	TINT):
	case CASE(	TVLONG,	TUINT):
	case CASE(	TVLONG,	TLONG):
	case CASE(	TVLONG,	TULONG):
	case CASE(	TUVLONG,	TCHAR):
	case CASE(	TUVLONG,	TUCHAR):
	case CASE(	TUVLONG,	TSHORT):
	case CASE(	TUVLONG,	TUSHORT):
	case CASE(	TUVLONG,	TINT):
	case CASE(	TUVLONG,	TUINT):
	case CASE(	TUVLONG,	TLONG):
	case CASE(	TUVLONG,	TULONG):
		a = AMOVQL;
		if(f->op == OCONST) {
			f->vconst &= 0xffffffffU;
			a = AMOVL;
		}
		break;

	case CASE(	TIND,	TIND):
	case CASE(	TIND,	TVLONG):
	case CASE(	TIND,	TUVLONG):
	case CASE(	TVLONG,	TIND):
	case CASE(	TVLONG,	TVLONG):
	case CASE(	TVLONG,	TUVLONG):
	case CASE(	TUVLONG,	TIND):
	case CASE(	TUVLONG,	TVLONG):
	case CASE(	TUVLONG,	TUVLONG):
		a = AMOVQ;
		break;

	case CASE(	TSHORT,	TINT):
	case CASE(	TSHORT,	TUINT):
	case CASE(	TSHORT,	TLONG):
	case CASE(	TSHORT,	TULONG):
		a = AMOVWLSX;
		if(f->op == OCONST) {
			f->vconst &= 0xffff;
			if(f->vconst & 0x8000)
				f->vconst |= 0xffff0000;
			a = AMOVL;
		}
		break;

	case CASE(	TSHORT,	TVLONG):
	case CASE(	TSHORT,	TUVLONG):
	case CASE(	TSHORT,	TIND):
		a = AMOVWQSX;
		if(f->op == OCONST) {
			f->vconst &= 0xffff;
			if(f->vconst & 0x8000){
				f->vconst |= 0xffff0000;
				f->vconst |= (vlong)~0 << 32;
			}
			a = AMOVL;
		}
		break;

	case CASE(	TUSHORT,TINT):
	case CASE(	TUSHORT,TUINT):
	case CASE(	TUSHORT,TLONG):
	case CASE(	TUSHORT,TULONG):
		a = AMOVWLZX;
		if(f->op == OCONST) {
			f->vconst &= 0xffff;
			a = AMOVL;
		}
		break;

	case CASE(	TUSHORT,TVLONG):
	case CASE(	TUSHORT,TUVLONG):
	case CASE(	TUSHORT,TIND):
		a = AMOVWQZX;
		if(f->op == OCONST) {
			f->vconst &= 0xffff;
			a = AMOVL;	/* MOVL also zero-extends to 64 bits */
		}
		break;

	case CASE(	TCHAR,	TSHORT):
	case CASE(	TCHAR,	TUSHORT):
	case CASE(	TCHAR,	TINT):
	case CASE(	TCHAR,	TUINT):
	case CASE(	TCHAR,	TLONG):
	case CASE(	TCHAR,	TULONG):
		a = AMOVBLSX;
		if(f->op == OCONST) {
			f->vconst &= 0xff;
			if(f->vconst & 0x80)
				f->vconst |= 0xffffff00;
			a = AMOVL;
		}
		break;

	case CASE(	TCHAR,	TVLONG):
	case CASE(	TCHAR,	TUVLONG):
	case CASE(	TCHAR,	TIND):
		a = AMOVBQSX;
		if(f->op == OCONST) {
			f->vconst &= 0xff;
			if(f->vconst & 0x80){
				f->vconst |= 0xffffff00;
				f->vconst |= (vlong)~0 << 32;
			}
			a = AMOVQ;
		}
		break;

	case CASE(	TUCHAR,	TSHORT):
	case CASE(	TUCHAR,	TUSHORT):
	case CASE(	TUCHAR,	TINT):
	case CASE(	TUCHAR,	TUINT):
	case CASE(	TUCHAR,	TLONG):
	case CASE(	TUCHAR,	TULONG):
		a = AMOVBLZX;
		if(f->op == OCONST) {
			f->vconst &= 0xff;
			a = AMOVL;
		}
		break;

	case CASE(	TUCHAR,	TVLONG):
	case CASE(	TUCHAR,	TUVLONG):
	case CASE(	TUCHAR,	TIND):
		a = AMOVBQZX;
		if(f->op == OCONST) {
			f->vconst &= 0xff;
			a = AMOVL;	/* zero-extends to 64-bits */
		}
		break;

/*
 * float to fix
 */
	case CASE(	TFLOAT,	TCHAR):
	case CASE(	TFLOAT,	TUCHAR):
	case CASE(	TFLOAT,	TSHORT):
	case CASE(	TFLOAT,	TUSHORT):
	case CASE(	TFLOAT,	TINT):
	case CASE(	TFLOAT,	TUINT):
	case CASE(	TFLOAT,	TLONG):
	case CASE(	TFLOAT,	TULONG):
	case CASE(	TFLOAT,	TVLONG):
	case CASE(	TFLOAT,	TUVLONG):
	case CASE(	TFLOAT,	TIND):

	case CASE(	TDOUBLE,TCHAR):
	case CASE(	TDOUBLE,TUCHAR):
	case CASE(	TDOUBLE,TSHORT):
	case CASE(	TDOUBLE,TUSHORT):
	case CASE(	TDOUBLE,TINT):
	case CASE(	TDOUBLE,TUINT):
	case CASE(	TDOUBLE,TLONG):
	case CASE(	TDOUBLE,TULONG):
	case CASE(	TDOUBLE,TVLONG):
	case CASE(	TDOUBLE,TUVLONG):
	case CASE(	TDOUBLE,TIND):
		regalloc(&nod, t, Z);
		if(ewidth[tt] == SZ_VLONG || typeu[tt] && ewidth[tt] == SZ_INT){
			if(ft == TFLOAT)
				a = ACVTTSS2SQ;
			else
				a = ACVTTSD2SQ;
		}else{
			if(ft == TFLOAT)
				a = ACVTTSS2SL;
			else
				a = ACVTTSD2SL;
		}
		gins(a, f, &nod);
		gmove(&nod, t);
		regfree(&nod);
		return;

/*
 * ulong to float
 */
	case CASE(	TUVLONG,	TDOUBLE):
	case CASE(	TUVLONG,	TFLOAT):
		a = ACVTSQ2SS;
		if(tt == TDOUBLE)
			a = ACVTSQ2SD;
		regalloc(&nod, f, f);
		gmove(f, &nod);
		regalloc(&nod1, t, t);
		gins(ACMPQ, &nod, nodconst(0));
		gins(AJLT, Z, Z);
		p1 = p;
		gins(a, &nod, &nod1);
		gins(AJMP, Z, Z);
		p2 = p;
		patch(p1, pc);
		regalloc(&nod2, f, Z);
		regalloc(&nod3, f, Z);
		gmove(&nod, &nod2);
		gins(ASHRQ, nodconst(1), &nod2);
		gmove(&nod, &nod3);
		gins(AANDL, nodconst(1), &nod3);
		gins(AORQ, &nod3, &nod2);
		gins(a, &nod2, &nod1);
		gins(tt == TDOUBLE? AADDSD: AADDSS, &nod1, &nod1);
		regfree(&nod2);
		regfree(&nod3);
		patch(p2, pc);
		regfree(&nod);
		regfree(&nod1);
		return;

	case CASE(	TULONG,	TDOUBLE):
	case CASE(	TUINT,	TDOUBLE):
	case CASE(	TULONG,	TFLOAT):
	case CASE(	TUINT,	TFLOAT):
		a = ACVTSQ2SS;
		if(tt == TDOUBLE)
			a = ACVTSQ2SD;
		regalloc(&nod, f, f);
		gins(AMOVLQZX, f, &nod);
		regalloc(&nod1, t, t);
		gins(a, &nod, &nod1);
		gmove(&nod1, t);
		regfree(&nod);
		regfree(&nod1);
		return;

/*
 * fix to float
 */
	case CASE(	TUCHAR,	TFLOAT):
	case CASE(	TUSHORT,TFLOAT):
	case CASE(	TINT,	TFLOAT):
	case CASE(	TLONG,	TFLOAT):
	case CASE(	TVLONG,	TFLOAT):
	case CASE(	TIND,	TFLOAT):


	case CASE(	TUCHAR,	TDOUBLE):
	case CASE(	TUSHORT,TDOUBLE):
	case CASE(	TINT,	TDOUBLE):
	case CASE(	TLONG,	TDOUBLE):
	case CASE(	TVLONG,	TDOUBLE):
	case CASE(	TIND,	TDOUBLE):
		regalloc(&nod, t, t);
		if(ewidth[ft] == SZ_VLONG){
			if(tt == TFLOAT)
				a = ACVTSQ2SS;
			else
				a = ACVTSQ2SD;
		}else{
			if(tt == TFLOAT)
				a = ACVTSL2SS;
			else
				a = ACVTSL2SD;
		}
		gins(a, f, &nod);
		gmove(&nod, t);
		regfree(&nod);
		return;

	case CASE(	TCHAR,	TFLOAT):
	case CASE(	TCHAR,	TDOUBLE):
	case CASE(	TSHORT,	TFLOAT):
	case CASE(	TSHORT,	TDOUBLE):
		regalloc(&nod, f, f);
		gins(ft == TCHAR? AMOVBLSX: AMOVWLSX, f, &nod);
		regalloc(&nod1, t, t);
		gins(tt == TDOUBLE? ACVTSL2SD: ACVTSL2SS, &nod, &nod1);
		gmove(&nod1, t);
		regfree(&nod);
		regfree(&nod1);
		return;


/*
 * float to float
 */
	case CASE(	TFLOAT,	TFLOAT):
		a = AMOVSS;
		break;
	case CASE(	TDOUBLE,TFLOAT):
		a = ACVTSD2SS;
		break;
	case CASE(	TFLOAT,	TDOUBLE):
		a = ACVTSS2SD;
		break;
	case CASE(	TDOUBLE,TDOUBLE):
		a = AMOVSD;
		break;
	}
	if(a == AMOVQ || a == AMOVSD || a == AMOVSS || a == AMOVL && ewidth[ft] == ewidth[tt])	/* TO DO: check AMOVL */
	if(samaddr(f, t))
		return;
	gins(a, f, t);
}

static int
regused(Node *n, int r)
{
	if(n == nil)
		return 0;
	if(isreg(n, r))
		return 1;
	return regused(n->left, r) || regused(n->right, r);
}

void
doindex(Node *n, Node *o)
{
	Node nod, nod1;
	long v;

if(debug['Y'])
prtree(n, "index");

if(n->left->complex >= FNX)
print("botch in doindex\n");

	if(n->right->op == OREGISTER)
		o = n->right;
	else if(o == Z || o->op != OREGISTER || regused(n, o->reg))
		o = Z;
	regalloc(&nod, &qregnode, o);
	v = constnode.vconst;
	cgen(n->right, &nod);
	idx.ptr = D_NONE;
	if(n->left->op == OCONST)
		idx.ptr = D_CONST;
	else if(n->left->op == OREGISTER)
		idx.ptr = n->left->reg;
	else if(n->left->op != OADDR) {
		reg[D_BP]++;	// cant be used as a base
		reg[D_R13]++;
		regalloc(&nod1, &qregnode, Z);
		cgen(n->left, &nod1);
		idx.ptr = nod1.reg;
		regfree(&nod1);
		reg[D_BP]--;
		reg[D_R13]--;
	}
	idx.reg = nod.reg;
	regfree(&nod);
	constnode.vconst = v;
}

void
gins(int a, Node *f, Node *t)
{

	if(f != Z && f->op == OINDEX)
		doindex(f, a == AMOVL || a == ALEAL
			|| a == AMOVQ || a == ALEAQ
			|| a == AMOVBLSX || a == AMOVBLZX
			|| a == AMOVBQSX || a == AMOVBQZX
			|| a == AMOVWLSX || a == AMOVWLZX
			|| a == AMOVWQSX || a == AMOVWQZX ? t : Z);
	if(t != Z && t->op == OINDEX)
		doindex(t, Z);
	nextpc();
	p->as = a;
	if(f != Z)
		naddr(f, &p->from);
	if(t != Z)
		naddr(t, &p->to);
	if(debug['g'])
		print("%P\n", p);
}

void
gopcode(int o, Type *ty, Node *f, Node *t)
{
	int a, et;

	et = TLONG;
	if(ty != T)
		et = ty->etype;
	if(debug['M']) {
		if(f != Z && f->type != T)
			print("gop: %O %O[%s],", o, f->op, tnames[et]);
		else
			print("gop: %O Z,", o);
		if(t != Z && t->type != T)
			print("%O[%s]\n", t->op, tnames[t->type->etype]);
		else
			print("Z\n");
	}
	a = AGOK;
	switch(o) {
	case OCOM:
		a = ANOTL;
		if(et == TCHAR || et == TUCHAR)
			a = ANOTB;
		if(et == TSHORT || et == TUSHORT)
			a = ANOTW;
		if(et == TVLONG || et == TUVLONG || et == TIND)
			a = ANOTQ;
		break;

	case ONEG:
		a = ANEGL;
		if(et == TCHAR || et == TUCHAR)
			a = ANEGB;
		if(et == TSHORT || et == TUSHORT)
			a = ANEGW;
		if(et == TVLONG || et == TUVLONG || et == TIND)
			a = ANEGQ;
		break;

	case OADDR:
		a = ALEAQ;
		break;

	case OASADD:
	case OADD:
		a = AADDL;
		if(et == TCHAR || et == TUCHAR)
			a = AADDB;
		if(et == TSHORT || et == TUSHORT)
			a = AADDW;
		if(et == TVLONG || et == TUVLONG || et == TIND)
			a = AADDQ;
		if(et == TFLOAT)
			a = AADDSS;
		if(et == TDOUBLE)
			a = AADDSD;
		break;

	case OASSUB:
	case OSUB:
		a = ASUBL;
		if(et == TCHAR || et == TUCHAR)
			a = ASUBB;
		if(et == TSHORT || et == TUSHORT)
			a = ASUBW;
		if(et == TVLONG || et == TUVLONG || et == TIND)
			a = ASUBQ;
		if(et == TFLOAT)
			a = ASUBSS;
		if(et == TDOUBLE)
			a = ASUBSD;
		break;

	case OASOR:
	case OOR:
		a = AORL;
		if(et == TCHAR || et == TUCHAR)
			a = AORB;
		if(et == TSHORT || et == TUSHORT)
			a = AORW;
		if(et == TVLONG || et == TUVLONG || et == TIND)
			a = AORQ;
		break;

	case OASAND:
	case OAND:
		a = AANDL;
		if(et == TCHAR || et == TUCHAR)
			a = AANDB;
		if(et == TSHORT || et == TUSHORT)
			a = AANDW;
		if(et == TVLONG || et == TUVLONG || et == TIND)
			a = AANDQ;
		break;

	case OASXOR:
	case OXOR:
		a = AXORL;
		if(et == TCHAR || et == TUCHAR)
			a = AXORB;
		if(et == TSHORT || et == TUSHORT)
			a = AXORW;
		if(et == TVLONG || et == TUVLONG || et == TIND)
			a = AXORQ;
		break;

	case OASLSHR:
	case OLSHR:
		a = ASHRL;
		if(et == TCHAR || et == TUCHAR)
			a = ASHRB;
		if(et == TSHORT || et == TUSHORT)
			a = ASHRW;
		if(et == TVLONG || et == TUVLONG || et == TIND)
			a = ASHRQ;
		break;

	case OASASHR:
	case OASHR:
		a = ASARL;
		if(et == TCHAR || et == TUCHAR)
			a = ASARB;
		if(et == TSHORT || et == TUSHORT)
			a = ASARW;
		if(et == TVLONG || et == TUVLONG || et == TIND)
			a = ASARQ;
		break;

	case OASASHL:
	case OASHL:
		a = ASALL;
		if(et == TCHAR || et == TUCHAR)
			a = ASALB;
		if(et == TSHORT || et == TUSHORT)
			a = ASALW;
		if(et == TVLONG || et == TUVLONG || et == TIND)
			a = ASALQ;
		break;

	case OROL:
		a = AROLL;
		if(et == TCHAR || et == TUCHAR)
			a = AROLB;
		if(et == TSHORT || et == TUSHORT)
			a = AROLW;
		if(et == TVLONG || et == TUVLONG || et == TIND)
			a = AROLQ;
		break;

	case OFUNC:
		a = ACALL;
		break;

	case OASMUL:
	case OMUL:
		if(f->op == OREGISTER && t != Z && isreg(t, D_AX) && reg[D_DX] == 0)
			t = Z;
		a = AIMULL;
		if(et == TVLONG || et == TUVLONG || et == TIND)
			a = AIMULQ;
		if(et == TFLOAT)
			a = AMULSS;
		if(et == TDOUBLE)
			a = AMULSD;
		break;

	case OASMOD:
	case OMOD:
	case OASDIV:
	case ODIV:
		a = AIDIVL;
		if(et == TVLONG || et == TUVLONG || et == TIND)
			a = AIDIVQ;
		if(et == TFLOAT)
			a = ADIVSS;
		if(et == TDOUBLE)
			a = ADIVSD;
		break;

	case OASLMUL:
	case OLMUL:
		a = AMULL;
		if(et == TVLONG || et == TUVLONG || et == TIND)
			a = AMULQ;
		break;

	case OASLMOD:
	case OLMOD:
	case OASLDIV:
	case OLDIV:
		a = ADIVL;
		if(et == TVLONG || et == TUVLONG || et == TIND)
			a = ADIVQ;
		break;

	case OEQ:
	case ONE:
	case OLT:
	case OLE:
	case OGE:
	case OGT:
	case OLO:
	case OLS:
	case OHS:
	case OHI:
		a = ACMPL;
		if(et == TCHAR || et == TUCHAR)
			a = ACMPB;
		if(et == TSHORT || et == TUSHORT)
			a = ACMPW;
		if(et == TVLONG || et == TUVLONG || et == TIND)
			a = ACMPQ;
		if(et == TFLOAT)
			a = AUCOMISS;
		if(et == TDOUBLE)
			a = AUCOMISD;
		gins(a, f, t);
		switch(o) {
		case OEQ:	a = AJEQ; break;
		case ONE:	a = AJNE; break;
		case OLT:	a = AJLT; break;
		case OLE:	a = AJLE; break;
		case OGE:	a = AJGE; break;
		case OGT:	a = AJGT; break;
		case OLO:	a = AJCS; break;
		case OLS:	a = AJLS; break;
		case OHS:	a = AJCC; break;
		case OHI:	a = AJHI; break;
		}
		gins(a, Z, Z);
		return;
	}
	if(a == AGOK)
		diag(Z, "bad in gopcode %O", o);
	gins(a, f, t);
}

int
samaddr(Node *f, Node *t)
{
	return f->op == OREGISTER && t->op == OREGISTER && f->reg == t->reg;
}

void
gbranch(int o)
{
	int a;

	a = AGOK;
	switch(o) {
	case ORETURN:
		a = ARET;
		break;
	case OGOTO:
		a = AJMP;
		break;
	}
	nextpc();
	if(a == AGOK) {
		diag(Z, "bad in gbranch %O",  o);
		nextpc();
	}
	p->as = a;
}

void
patch(Prog *op, long pc)
{

	op->to.offset = pc;
	op->to.type = D_BRANCH;
}

void
gpseudo(int a, Sym *s, Node *n)
{

	nextpc();
	p->as = a;
	p->from.type = D_EXTERN;
	p->from.sym = s;
	p->from.scale = (profileflg ? 0 : NOPROF);
	if(s->class == CSTATIC)
		p->from.type = D_STATIC;
	naddr(n, &p->to);
	if(a == ADATA || a == AGLOBL)
		pc--;
}

int
sconst(Node *n)
{
	long v;

	if(n->op == OCONST && !typefd[n->type->etype]) {
		v = n->vconst;
		if(v >= -32766L && v < 32766L)
			return 1;
	}
	return 0;
}

long
exreg(Type *t)
{
	long o;

	if(typechlpv[t->etype]) {
		if(exregoffset <= REGEXT-4)
			return 0;
		o = exregoffset;
		exregoffset--;
		return o;
	}
	return 0;
}

schar	ewidth[NTYPE] =
{
	-1,		/*[TXXX]*/	
	SZ_CHAR,	/*[TCHAR]*/	
	SZ_CHAR,	/*[TUCHAR]*/
	SZ_SHORT,	/*[TSHORT]*/
	SZ_SHORT,	/*[TUSHORT]*/
	SZ_INT,		/*[TINT]*/
	SZ_INT,		/*[TUINT]*/
	SZ_LONG,	/*[TLONG]*/
	SZ_LONG,	/*[TULONG]*/
	SZ_VLONG,	/*[TVLONG]*/
	SZ_VLONG,	/*[TUVLONG]*/
	SZ_FLOAT,	/*[TFLOAT]*/
	SZ_DOUBLE,	/*[TDOUBLE]*/
	SZ_IND,		/*[TIND]*/
	0,		/*[TFUNC]*/
	-1,		/*[TARRAY]*/
	0,		/*[TVOID]*/
	-1,		/*[TSTRUCT]*/
	-1,		/*[TUNION]*/
	SZ_INT,		/*[TENUM]*/
};
long	ncast[NTYPE] =
{
	0,				/*[TXXX]*/
	BCHAR|BUCHAR,			/*[TCHAR]*/
	BCHAR|BUCHAR,			/*[TUCHAR]*/	
	BSHORT|BUSHORT,			/*[TSHORT]*/
	BSHORT|BUSHORT,			/*[TUSHORT]*/
	BINT|BUINT|BLONG|BULONG,	/*[TINT]*/		
	BINT|BUINT|BLONG|BULONG,	/*[TUINT]*/
	BINT|BUINT|BLONG|BULONG,	/*[TLONG]*/
	BINT|BUINT|BLONG|BULONG,	/*[TULONG]*/
	BVLONG|BUVLONG|BIND,			/*[TVLONG]*/
	BVLONG|BUVLONG|BIND,			/*[TUVLONG]*/
	BFLOAT,				/*[TFLOAT]*/
	BDOUBLE,			/*[TDOUBLE]*/
	BVLONG|BUVLONG|BIND,		/*[TIND]*/
	0,				/*[TFUNC]*/
	0,				/*[TARRAY]*/
	0,				/*[TVOID]*/
	BSTRUCT,			/*[TSTRUCT]*/
	BUNION,				/*[TUNION]*/
	0,				/*[TENUM]*/
};
