#include "limbo.h"

static Node* putinline(Node*);
static void fpcall(Src*, int, Node*, Node*);

void
optabinit(void)
{
	int i;

	for(i = 0; setisbyteinst[i] >= 0; i++)
		isbyteinst[setisbyteinst[i]] = 1;

	for(i = 0; setisused[i] >= 0; i++)
		isused[setisused[i]] = 1;

	for(i = 0; setsideeffect[i] >= 0; i++)
		sideeffect[setsideeffect[i]] = 1;

	opind[Tbyte] = 1;
	opind[Tint] = 2;
	opind[Tbig] = 3;
	opind[Treal] = 4;
	opind[Tstring] = 5;
	opind[Tfix] = 6;

	opcommute[Oeq] = Oeq;
	opcommute[Oneq] = Oneq;
	opcommute[Olt] = Ogt;
	opcommute[Ogt] = Olt;
	opcommute[Ogeq] = Oleq;
	opcommute[Oleq] = Ogeq;
	opcommute[Oadd] = Oadd;
	opcommute[Omul] = Omul;
	opcommute[Oxor] = Oxor;
	opcommute[Oor] = Oor;
	opcommute[Oand] = Oand;

	oprelinvert[Oeq] = Oneq;
	oprelinvert[Oneq] = Oeq;
	oprelinvert[Olt] = Ogeq;
	oprelinvert[Ogt] = Oleq;
	oprelinvert[Ogeq] = Olt;
	oprelinvert[Oleq] = Ogt;

	isrelop[Oeq] = 1;
	isrelop[Oneq] = 1;
	isrelop[Olt] = 1;
	isrelop[Oleq] = 1;
	isrelop[Ogt] = 1;
	isrelop[Ogeq] = 1;
	isrelop[Oandand] = 1;
	isrelop[Ooror] = 1;
	isrelop[Onot] = 1;

	precasttab[Tstring][Tbyte] = tint;
	precasttab[Tbyte][Tstring] = tint;
	precasttab[Treal][Tbyte] = tint;
	precasttab[Tbyte][Treal] = tint;
	precasttab[Tbig][Tbyte] = tint;
	precasttab[Tbyte][Tbig] = tint;
	precasttab[Tfix][Tbyte] = tint;
	precasttab[Tbyte][Tfix] = tint;
	precasttab[Tbig][Tfix] = treal;
	precasttab[Tfix][Tbig] = treal;
	precasttab[Tstring][Tfix] = treal;
	precasttab[Tfix][Tstring] = treal;

	casttab[Tint][Tint] = IMOVW;
	casttab[Tbig][Tbig] = IMOVL;
	casttab[Treal][Treal] = IMOVF;
	casttab[Tbyte][Tbyte] = IMOVB;
	casttab[Tstring][Tstring] = IMOVP;
	casttab[Tfix][Tfix] = ICVTXX;	/* never same type */

	casttab[Tint][Tbyte] = ICVTWB;
	casttab[Tint][Treal] = ICVTWF;
	casttab[Tint][Tstring] = ICVTWC;
	casttab[Tint][Tfix] = ICVTXX;
	casttab[Tbyte][Tint] = ICVTBW;
	casttab[Treal][Tint] = ICVTFW;
	casttab[Tstring][Tint] = ICVTCW;
	casttab[Tfix][Tint] = ICVTXX;

	casttab[Tint][Tbig] = ICVTWL;
	casttab[Treal][Tbig] = ICVTFL;
	casttab[Tstring][Tbig] = ICVTCL;
	casttab[Tbig][Tint] = ICVTLW;
	casttab[Tbig][Treal] = ICVTLF;
	casttab[Tbig][Tstring] = ICVTLC;

	casttab[Treal][Tstring] = ICVTFC;
	casttab[Tstring][Treal] = ICVTCF;

	casttab[Treal][Tfix] = ICVTFX;
	casttab[Tfix][Treal] = ICVTXF;

	casttab[Tstring][Tarray] = ICVTCA;
	casttab[Tarray][Tstring] = ICVTAC;

	/*
	 * placeholders; fixed in precasttab
	 */
	casttab[Tbyte][Tstring] = 0xff;
	casttab[Tstring][Tbyte] = 0xff;
	casttab[Tbyte][Treal] = 0xff;
	casttab[Treal][Tbyte] = 0xff;
	casttab[Tbyte][Tbig] = 0xff;
	casttab[Tbig][Tbyte] = 0xff;
	casttab[Tfix][Tbyte] = 0xff;
	casttab[Tbyte][Tfix] = 0xff;
	casttab[Tfix][Tbig] = 0xff;
	casttab[Tbig][Tfix] = 0xff;
	casttab[Tfix][Tstring] = 0xff;
	casttab[Tstring][Tfix] = 0xff;
}

/*
 * global variable and constant initialization checking
 */
int
vcom(Decl *ids)
{
	Decl *v;
	int ok;

	ok = 1;
	for(v = ids; v != nil; v = v->next)
		ok &= varcom(v);
	for(v = ids; v != nil; v = v->next)
		v->init = simplify(v->init);
	return ok;
}

Node*
simplify(Node *n)
{
	if(n == nil)
		return nil;
	if(debug['F'])
		print("simplify %n\n", n);
	n = efold(rewrite(n));
	if(debug['F'])
		print("simplified %n\n", n);
	return n;
}

static int
isfix(Node *n)
{
	if(n->ty->kind == Tint || n->ty->kind == Tfix){
		if(n->op == Ocast)
			return n->left->ty->kind == Tint || n->left->ty->kind == Tfix;
		return 1;
	}
	return 0;
}

/*
 * rewrite an expression to make it easiser to compile,
 * or give the correct results
 */
Node*
rewrite(Node *n)
{
	Long v;
	Type *t;
	Decl *d;
	Node *nn, *left, *right;

	if(n == nil)
		return nil;

	left = n->left;
	right = n->right;

	/*
	 * rewrites
	 */
	switch(n->op){
	case Oname:
		d = n->decl;
		if(d->importid != nil){
			left = mkbin(Omdot, dupn(1, &n->src, d->eimport), mkdeclname(&n->src, d->importid));
			left->ty = n->ty;
			return rewrite(left);
		}
		if((t = n->ty)->kind == Texception){
			if(t->cons)
				fatal("cons in rewrite Oname");
			n = mkbin(Oadd, n, mkconst(&n->src, 2*IBY2WD));
			n = mkunary(Oind, n);
			n->ty = t;
			n->left->ty = n->left->left->ty = tint;
			return rewrite(n);
		}
		break;
	case Odas:
		n->op = Oas;
		return rewrite(n);
	case Oneg:
		n->left = rewrite(left);
		if(n->ty == treal)
			break;
		left = n->left;
		n->right = left;
		n->left = mkconst(&n->src, 0);
		n->left->ty = n->ty;
		n->op = Osub;
		break;
	case Ocomp:
		v = 0;
		v = ~v;
		n->right = mkconst(&n->src, v);
		n->right->ty = n->ty;
		n->left = rewrite(left);
		n->op = Oxor;
		break;
	case Oinc:
	case Odec:
	case Opreinc:
	case Opredec:
		n->left = rewrite(left);
		switch(n->ty->kind){
		case Treal:
			n->right = mkrconst(&n->src, 1.0);
			break;
		case Tint:
		case Tbig:
		case Tbyte:
		case Tfix:
			n->right = mkconst(&n->src, 1);
			n->right->ty = n->ty;
			break;
		default:
			fatal("can't rewrite inc/dec %n", n);
			break;
		}
		if(n->op == Opreinc)
			n->op = Oaddas;
		else if(n->op == Opredec)
			n->op = Osubas;
		break;
	case Oslice:
		if(right->left->op == Onothing)
			right->left = mkconst(&right->left->src, 0);
		n->left = rewrite(left);
		n->right = rewrite(right);
		break;
	case Oindex:
		n->op = Oindx;
		n->left = rewrite(left);
		n->right = rewrite(right);
		n = mkunary(Oind, n);
		n->ty = n->left->ty;
		n->left->ty = tint;
		break;
	case Oload:
		n->right = mkn(Oname, nil, nil);
		n->right->src = n->left->src;
		n->right->decl = n->ty->tof->decl;
		n->right->ty = n->ty;
		n->left = rewrite(left);
		break;
	case Ocast:
		if(left->ty->kind == Texception){
			n = rewrite(left);
			break;
		}
		n->op = Ocast;
		t = precasttab[left->ty->kind][n->ty->kind];
		if(t != nil){
			n->left = mkunary(Ocast, left);
			n->left->ty = t;
			return rewrite(n);
		}
		n->left = rewrite(left);
		break;
	case Oraise:
		if(left->ty == tstring)
			{}
		else if(!left->ty->cons)
			break;
		else if(left->op != Ocall || left->left->ty->kind == Tfn){
			left = mkunary(Ocall, left);
			left->ty = left->left->ty;
		}
		n->left = rewrite(left);
		break;
	case Ocall:
		t = left->ty;
		if(t->kind == Tref)
			t = t->tof;
		if(t->kind == Tfn){
if(debug['U']) print("call %n\n", left);
			if(left->ty->kind == Tref){	/* call by function reference */
				n->left = mkunary(Oind, left);
				n->left->ty = t;
				return rewrite(n);
			}
			d = nil;
			if(left->op == Oname)
				d = left->decl;
			else if(left->op == Omdot && left->right->op == Odot)
				d = left->right->right->decl;
			else if(left->op == Omdot || left->op == Odot)
				d = left->right->decl;
			else if(left->op != Oind)
				fatal("cannot deal with call %n in rewrite", n);
			if(ispoly(d))
				addfnptrs(d, 0);
			n->left = rewrite(left);
			if(right != nil)
				n->right = rewrite(right);
			if(d != nil && d->caninline == 1)
				n = simplify(putinline(n));
			break;
		}
		switch(n->ty->kind){
		case Tref:
			n = mkunary(Oref, n);
			n->ty = n->left->ty;
			n->left->ty = n->left->ty->tof;
			n->left->left->ty = n->left->ty;
			return rewrite(n);
		case Tadt:
			n->op = Otuple;
			n->right = nil;
			if(n->ty->tags != nil){
				n->left = nn = mkunary(Oseq, mkconst(&n->src, left->right->decl->tag));
				if(right != nil){
					nn->right = right;
					nn->src.stop = right->src.stop;
				}
				n->ty = left->right->decl->ty->tof;
			}else
				n->left = right;
			return rewrite(n);
		case Tadtpick:
			n->op = Otuple;
			n->right = nil;
			n->left = nn = mkunary(Oseq, mkconst(&n->src, left->right->decl->tag));
			if(right != nil){
				nn->right = right;
				nn->src.stop = right->src.stop;
			}
			n->ty = left->right->decl->ty->tof;
			return rewrite(n);
		case Texception:
			if(!n->ty->cons)
				return n->left;
			if(left->op == Omdot){
				left->right->ty = left->ty;
				left = left->right;
			}
			n->op = Otuple;
			n->right = nil;
			n->left = nn = mkunary(Oseq, left->decl->init);
			nn->right = mkunary(Oseq, mkconst(&n->src, 0));
			nn->right->right = right;
			n->ty = mkexbasetype(n->ty);
			n = mkunary(Oref, n);
			n->ty = internaltype(mktype(&n->src.start, &n->src.stop, Tref, t, nil));
			return rewrite(n);
		default:
			fatal("can't deal with %n in rewrite/Ocall", n);
			break;
		}
		break;
	case Omdot:
		/*
		 * what about side effects from left?
		 */
		d = right->decl;
		switch(d->store){
		case Dfn:
			n->left = rewrite(left);
			if(right->op == Odot){
				n->right = dupn(1, &left->src, right->right);
				n->right->ty = d->ty;
			}
			break;
		case Dconst:
		case Dtag:
		case Dtype:
			/* handled by fold */
			return n;
		case Dglobal:
			right->op = Oconst;
			right->val = d->offset;
			right->ty = tint;

			n->left = left = mkunary(Oind, left);
			left->ty = tint;
			n->op = Oadd;
			n = mkunary(Oind, n);
			n->ty = n->left->ty;
			n->left->ty = tint;
			n->left = rewrite(n->left);
			return n;
		case Darg:
			return n;
		default:
			fatal("can't deal with %n in rewrite/Omdot", n);
			break;
		}
		break;
	case Odot:
		/*
		 * what about side effects from left?
		 */
		d = right->decl;
		switch(d->store){
		case Dfn:
			if(right->left != nil){
				n = mkbin(Omdot, dupn(1, &left->src, right->left), right);
				right->left = nil;
				n->ty = d->ty;
				return rewrite(n);
			}
			if(left->ty->kind == Tpoly){
				n = mkbin(Omdot, mkdeclname(&left->src, d->link), mkdeclname(&left->src, d->link->next));
				n->ty = d->ty;
				return rewrite(n);
			}
			n->op = Oname;
			n->decl = d;
			n->right = nil;
			n->left = nil;
			return n;
		case Dconst:
		case Dtag:
		case Dtype:
			/* handled by fold */
			return n;
		}
		if(istuple(left))
			return n;	/* handled by fold */
		right->op = Oconst;
		right->val = d->offset;
		right->ty = tint;

		if(left->ty->kind != Tref){
			n->left = mkunary(Oadr, left);
			n->left->ty = tint;
		}
		n->op = Oadd;
		n = mkunary(Oind, n);
		n->ty = n->left->ty;
		n->left->ty = tint;
		n->left = rewrite(n->left);
		return n;
	case Oadr:
		left = rewrite(left);
		n->left = left;
		if(left->op == Oind)
			return left->left;
		break;
	case Otagof:
		if(n->decl == nil){
			n->op = Oind;
			return rewrite(n);
		}
		return n;
	case Omul:
	case Odiv:
		left = n->left = rewrite(left);
		right = n->right = rewrite(right);
		if(n->ty->kind == Tfix && isfix(left) && isfix(right)){
			if(left->op == Ocast && tequal(left->ty, n->ty))
				n->left = left->left;
			if(right->op == Ocast && tequal(right->ty, n->ty))
				n->right = right->left;
		}
		break;
	case Oself:
		if(newfnptr)
			return n;
		if(selfdecl == nil){
			d = selfdecl = mkids(&n->src, enter(strdup(".self"), 5), tany, nil);
			installids(Dglobal, d);
			d->refs++;
		}
		nn = mkn(Oload, nil, nil);
		nn->src = n->src;
		nn->left = mksconst(&n->src, enterstring(strdup("$self"), 5));
		nn->ty = impdecl->ty;
		usetype(nn->ty);
		usetype(nn->ty->tof);
		nn = rewrite(nn);
		nn->op = Oself;
		return nn;
	case Ofnptr:
		if(n->flags == 0){
			/* module */
			if(left == nil)
				left = mkn(Oself, nil, nil);
			return rewrite(left);
		}
		right->flags = n->flags;
		n = right;
		d = n->decl;
		if(n->flags == FNPTR2){
			if(left != nil && left->op != Oname)
				fatal("not Oname for addiface");
			if(left == nil){
				addiface(nil, d);
				if(newfnptr)
					n->flags |= FNPTRN;
			}
			else
				addiface(left->decl, d);	/* is this necessary ? */
			n->ty = tint;
			return n;
		}
		if(n->flags == FNPTRA){
			n = mkdeclname(&n->src, d->link);
			n->ty = tany;
			return n;
		}
		if(n->flags == (FNPTRA|FNPTR2)){
			n = mkdeclname(&n->src, d->link->next);
			n->ty = tint;
			return n;
		}
		break;
	case Ochan:
		if(left == nil)
			left = n->left = mkconst(&n->src, 0);
		n->left = rewrite(left);
		break;
	default:
		n->left = rewrite(left);
		n->right = rewrite(right);
		break;
	}

	return n;
}

/*
 * label a node with sethi-ullman numbers and addressablity
 * genaddr interprets addable to generate operands,
 * so a change here mandates a change there.
 *
 * addressable:
 *	const			Rconst	$value		 may also be Roff or Rdesc or Rnoff
 *	Asmall(local)		Rreg	value(FP)
 *	Asmall(global)		Rmreg	value(MP)
 *	ind(Rareg)		Rreg	value(FP)
 *	ind(Ramreg)		Rmreg	value(MP)
 *	ind(Rreg)		Radr	*value(FP)
 *	ind(Rmreg)		Rmadr	*value(MP)
 *	ind(Raadr)		Radr	value(value(FP))
 *	ind(Ramadr)		Rmadr	value(value(MP))
 *
 * almost addressable:
 *	adr(Rreg)		Rareg
 *	adr(Rmreg)		Ramreg
 *	add(const, Rareg)	Rareg
 *	add(const, Ramreg)	Ramreg
 *	add(const, Rreg)	Raadr
 *	add(const, Rmreg)	Ramadr
 *	add(const, Raadr)	Raadr
 *	add(const, Ramadr)	Ramadr
 *	adr(Radr)		Raadr
 *	adr(Rmadr)		Ramadr
 *
 * strangely addressable:
 *	fn			Rpc
 *	mdot(module,exp)	Rmpc
 */
Node*
sumark(Node *n)
{
	Node *left, *right;
	long v;

	if(n == nil)
		return nil;

	n->temps = 0;
	n->addable = Rcant;

	left = n->left;
	right = n->right;
	if(left != nil){
		sumark(left);
		n->temps = left->temps;
	}
	if(right != nil){
		sumark(right);
		if(right->temps == n->temps)
			n->temps++;
		else if(right->temps > n->temps)
			n->temps = right->temps;
	}

	switch(n->op){
	case Oadr:
		switch(left->addable){
		case Rreg:
			n->addable = Rareg;
			break;
		case Rmreg:
			n->addable = Ramreg;
			break;
		case Radr:
			n->addable = Raadr;
			break;
		case Rmadr:
			n->addable = Ramadr;
			break;
		}
		break;
	case Oind:
		switch(left->addable){
		case Rreg:
			n->addable = Radr;
			break;
		case Rmreg:
			n->addable = Rmadr;
			break;
		case Rareg:
			n->addable = Rreg;
			break;
		case Ramreg:
			n->addable = Rmreg;
			break;
		case Raadr:
			n->addable = Radr;
			break;
		case Ramadr:
			n->addable = Rmadr;
			break;
		}
		break;
	case Oname:
		switch(n->decl->store){
		case Darg:
		case Dlocal:
			n->addable = Rreg;
			break;
		case Dglobal:
			n->addable = Rmreg;
			if(LDT && n->decl->ty->kind == Tiface)
				n->addable = Rldt;
			break;
		case Dtype:
			/*
			 * check for inferface to load
			 */
			if(n->decl->ty->kind == Tmodule)
				n->addable = Rmreg;
			break;
		case Dfn:
			if(n->flags & FNPTR){
				if(n->flags == FNPTR2)
					n->addable = Roff;
				else if(n->flags == (FNPTR2|FNPTRN))
					n->addable = Rnoff;
			}
			else
				n->addable = Rpc;
			break;
		default:
			fatal("cannot deal with %K in Oname in %n", n->decl, n);
			break;
		}
		break;
	case Omdot:
		n->addable = Rmpc;
		break;
	case Oconst:
		switch(n->ty->kind){
		case Tint:
		case Tfix:
			v = n->val;
			if(v < 0 && ((v >> 29) & 0x7) != 7
			|| v > 0 && (v >> 29) != 0){
				n->decl = globalconst(n);
				n->addable = Rmreg;
			}else
				n->addable = Rconst;
			break;
		case Tbig:
			n->decl = globalBconst(n);
			n->addable = Rmreg;
			break;
		case Tbyte:
			n->decl = globalbconst(n);
			n->addable = Rmreg;
			break;
		case Treal:
			n->decl = globalfconst(n);
			n->addable = Rmreg;
			break;
		case Tstring:
			n->decl = globalsconst(n);
			n->addable = Rmreg;
			break;
		default:
			fatal("cannot %T const in sumark", n->ty);
			break;
		}
		break;
	case Oadd:
		if(right->addable == Rconst){
			switch(left->addable){
			case Rareg:
				n->addable = Rareg;
				break;
			case Ramreg:
				n->addable = Ramreg;
				break;
			case Rreg:
			case Raadr:
				n->addable = Raadr;
				break;
			case Rmreg:
			case Ramadr:
				n->addable = Ramadr;
				break;
			}
		}
		break;
	}
	if(n->addable < Rcant)
		n->temps = 0;
	else if(n->temps == 0)
		n->temps = 1;
	return n;
}

Node*
mktn(Type *t)
{
	Node *n;

	n = mkn(Oname, nil, nil);
	usedesc(mktdesc(t));
	n->ty = t;
	n->decl = t->decl;
	if(n->decl == nil)
		fatal("mktn t %T nil decl", t);
	n->addable = Rdesc;
	return n;
}

/* does a tuple of the form (a, b, ...) form a contiguous block
 * of memory on the stack when offsets are assigned later
 * - only when (a, b, ...) := rhs and none of the names nil
 * can we guarantee this
 */
static int
tupblk0(Node *n, Decl **dd)
{
	Decl *d;
	int nid;

	switch(n->op){
	case Otuple:
		for(n = n->left; n != nil; n = n->right)
			if(!tupblk0(n->left, dd))
				return 0;
		return 1;
	case Oname:
		if(n->decl == nildecl)
			return 0;
		d = *dd;
		if(d != nil && d->next != n->decl)
			return 0;
		nid = n->decl->nid;
		if(d == nil && nid == 1)
			return 0;
		if(d != nil && nid != 0)
			return 0;
		*dd = n->decl;
		return 1;
	}
	return 0;
}

/* could force locals to be next to each other
 * - need to shuffle locals list
 * - later
 */
static Node*
tupblk(Node *n)
{
	Decl *d;

	if(n->op != Otuple)
		return nil;
	d = nil;
	if(!tupblk0(n, &d))
		return nil;
	while(n->op == Otuple)
		n = n->left->left;
	if(n->op != Oname || n->decl->nid == 1)
		fatal("bad tupblk");
	return n;
}
	
/* for cprof */
#define esrc(src, osrc, nto) (src != nil && nto != nil ? src : osrc)

/*
 * compile an expression with an implicit assignment
 * note: you are not allowed to use to->src
 *
 * need to think carefully about the types used in moves
 * it particular, it would be nice to gen movp rather than movc sometimes.
 */
Node*
ecom(Src *src, Node *nto, Node *n)
{
	Node *left, *right, *tn;
	Node tl, tr, tto, ttn;
	Type *t, *tt;
	Inst *p, *pp;
	int op;

	if(debug['e']){
		print("ecom: %n\n", n);
		if(nto != nil)
			print("ecom to: %n\n", nto);
	}

	if(n->addable < Rcant){
		/*
		 * think carefully about the type used here
		 */
		if(nto != nil)
			genmove(src, Mas, n->ty, n, nto);
		return nto;
	}

	tl.decl = nil;
	tr.decl = nil;
	tto.decl = nil;
	ttn.decl = nil;

	left = n->left;
	right = n->right;
	op = n->op;
	switch(op){
	default:
	case Oadr:
		fatal("can't %n in ecom", n);
		return nto;
	case Oif:
		p = bcom(left, 1, nil);
		ecom(&right->left->src, nto, right->left);
		if(right->right != nil){
			pp = p;
			p = genrawop(&right->left->src, IJMP, nil, nil, nil);
			patch(pp, nextinst());
			ecom(&right->right->src, nto, right->right);
		}
		patch(p, nextinst());
		break;
	case Ocomma:
		tn = left->left;
		ecom(&left->src, nil, left);
		ecom(&right->src, nto, right);
		tfree(tn);
		break;
	case Oname:
		if(n->addable == Rpc){
			if(nto != nil)
				genmove(src, Mas, n->ty, n, nto);
			return nto;
		}
		fatal("can't %n in ecom", n);
		break;
	case Onothing:
		break;
	case Oused:
		if(nto != nil)
			fatal("superfluous used %n to %n", left, nto);
		talloc(&tto, left->ty, nil);
		ecom(&left->src, &tto, left);
		tfree(&tto);
		break;
	case Oas:
		if(right->ty == tany)
			right->ty = n->ty;
		if(left->op == Oname && left->decl->ty == tany){
			if(nto == nil)
				nto = talloc(&tto, right->ty, nil);
			left = nto;
			nto = nil;
		}
		if(left->op == Oinds){
			indsascom(src, nto, n);
			tfree(&tto);
			break;
		}
		if(left->op == Oslice){
			slicelcom(src, nto, n);
			tfree(&tto);
			break;
		}

		if(left->op == Otuple){
			if(!tupsaliased(right, left)){
				if((tn = tupblk(left)) != nil){
					tn->ty = n->ty;
					ecom(&n->right->src, tn, right);
					if(nto != nil)
						genmove(src, Mas, n->ty, tn, nto);
					tfree(&tto);
					break;
				}
				if((tn = tupblk(right)) != nil){
					tn->ty = n->ty;
					tuplcom(tn, left);
					if(nto != nil)
						genmove(src, Mas, n->ty, tn, nto);
					tfree(&tto);
					break;
				}
				if(nto == nil && right->op == Otuple && left->ty->kind != Tadtpick){
					tuplrcom(right, left);
					tfree(&tto);
					break;
				}
			}
			if(right->addable >= Ralways
			|| right->op != Oname
			|| tupaliased(right, left)){
				talloc(&tr, n->ty, nil);
				ecom(&n->right->src, &tr, right);
				right = &tr;
			}
			tuplcom(right, n->left);
			if(nto != nil)
				genmove(src, Mas, n->ty, right, nto);
			tfree(&tr);
			tfree(&tto);
			break;
		}

		/*
		 * check for left/right aliasing and build right into temporary
		 */
		if(right->op == Otuple){
			if(!tupsaliased(left, right) && (tn = tupblk(right)) != nil){
				tn->ty = n->ty;
				right = tn;
			}
			else if(left->op != Oname || tupaliased(left, right))
				right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
		}

		/*
		 * think carefully about types here
		 */
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nto);
		ecom(&n->src, left, right);
		if(nto != nil)
			genmove(src, Mas, nto->ty, left, nto);
		tfree(&tl);
		tfree(&tr);
		tfree(&tto);
		break;
	case Ochan:
		if(left && left->addable >= Rcant)
			left = eacom(left, &tl, nto);
		genchan(src, left, n->ty->tof, nto);
		tfree(&tl);
		break;
	case Oinds:
		if(right->addable < Ralways){
			if(left->addable >= Rcant)
				left = eacom(left, &tl, nil);
		}else if(left->temps <= right->temps){
			right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
			if(left->addable >= Rcant)
				left = eacom(left, &tl, nil);
		}else{
			left = eacom(left, &tl, nil);
			right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
		}
		genop(&n->src, op, left, right, nto);
		tfree(&tl);
		tfree(&tr);
		break;
	case Osnd:
		if(right->addable < Rcant){
			if(left->addable >= Rcant)
				left = eacom(left, &tl, nto);
		}else if(left->temps < right->temps){
			right = eacom(right, &tr, nto);
			if(left->addable >= Rcant)
				left = eacom(left, &tl, nil);
		}else{
			left = eacom(left, &tl, nto);
			right = eacom(right, &tr, nil);
		}
		p = genrawop(&n->src, ISEND, right, nil, left);
		p->m.offset = n->ty->size;	/* for optimizer */
		if(nto != nil)
			genmove(src, Mas, right->ty, right, nto);
		tfree(&tl);
		tfree(&tr);
		break;
	case Orcv:
		if(nto == nil){
			ecom(&n->src, talloc(&tto, n->ty, nil), n);
			tfree(&tto);
			return nil;
		}
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nto);
		if(left->ty->kind == Tchan){
			p = genrawop(src, IRECV, left, nil, nto);
			p->m.offset = n->ty->size;	/* for optimizer */
		}else{
			recvacom(src, nto, n);
		}
		tfree(&tl);
		break;
	case Ocons:
		/*
		 * another temp which can go with analysis
		 */
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nil);
		if(!sameaddr(right, nto)){
			ecom(&right->src, talloc(&tto, n->ty, nto), right);
			genmove(src, Mcons, left->ty, left, &tto);
			if(!sameaddr(&tto, nto))
				genmove(src, Mas, nto->ty, &tto, nto);
		}else
			genmove(src, Mcons, left->ty, left, nto);
		tfree(&tl);
		tfree(&tto);
		break;
	case Ohd:
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nto);
		genmove(src, Mhd, nto->ty, left, nto);
		tfree(&tl);
		break;
	case Otl:
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nto);
		genmove(src, Mtl, left->ty, left, nto);
		tfree(&tl);
		break;
	case Otuple:
		if((tn = tupblk(n)) != nil){
			tn->ty = n->ty;
			genmove(src, Mas, n->ty, tn, nto);
			break;
		}
		tupcom(nto, n);
		break;
	case Oadd:
	case Osub:
	case Omul:
	case Odiv:
	case Omod:
	case Oand:
	case Oor:
	case Oxor:
	case Olsh:
	case Orsh:
	case Oexp:
		/*
		 * check for 2 operand forms
		 */
		if(sameaddr(nto, left)){
			if(right->addable >= Rcant)
				right = eacom(right, &tr, nto);
			genop(src, op, right, nil, nto);
			tfree(&tr);
			break;
		}

		if(opcommute[op] && sameaddr(nto, right) && n->ty != tstring){
			if(left->addable >= Rcant)
				left = eacom(left, &tl, nto);
			genop(src, opcommute[op], left, nil, nto);
			tfree(&tl);
			break;
		}

		if(right->addable < left->addable
		&& opcommute[op]
		&& n->ty != tstring){
			op = opcommute[op];
			left = right;
			right = n->left;
		}
		if(left->addable < Ralways){
			if(right->addable >= Rcant)
				right = eacom(right, &tr, nto);
		}else if(right->temps <= left->temps){
			left = ecom(&left->src, talloc(&tl, left->ty, nto), left);
			if(right->addable >= Rcant)
				right = eacom(right, &tr, nil);
		}else{
			right = eacom(right, &tr, nto);
			left = ecom(&left->src, talloc(&tl, left->ty, nil), left);
		}

		/*
		 * check for 2 operand forms
		 */
		if(sameaddr(nto, left))
			genop(src, op, right, nil, nto);
		else if(opcommute[op] && sameaddr(nto, right) && n->ty != tstring)
			genop(src, opcommute[op], left, nil, nto);
		else
			genop(src, op, right, left, nto);
		tfree(&tl);
		tfree(&tr);
		break;
	case Oaddas:
	case Osubas:
	case Omulas:
	case Odivas:
	case Omodas:
	case Oexpas:
	case Oandas:
	case Ooras:
	case Oxoras:
	case Olshas:
	case Orshas:
		if(left->op == Oinds){
			indsascom(src, nto, n);
			break;
		}
		if(right->addable < Rcant){
			if(left->addable >= Rcant)
				left = eacom(left, &tl, nto);
		}else if(left->temps < right->temps){
			right = eacom(right, &tr, nto);
			if(left->addable >= Rcant)
				left = eacom(left, &tl, nil);
		}else{
			left = eacom(left, &tl, nto);
			right = eacom(right, &tr, nil);
		}
		genop(&n->src, op, right, nil, left);
		if(nto != nil)
			genmove(src, Mas, left->ty, left, nto);
		tfree(&tl);
		tfree(&tr);
		break;
	case Olen:
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nto);
		op = -1;
		t = left->ty;
		if(t == tstring)
			op = ILENC;
		else if(t->kind == Tarray)
			op = ILENA;
		else if(t->kind == Tlist)
			op = ILENL;
		else
			fatal("can't len %n", n);
		genrawop(src, op, left, nil, nto);
		tfree(&tl);
		break;
	case Oneg:
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nto);
		genop(&n->src, op, left, nil, nto);
		tfree(&tl);
		break;
	case Oinc:
	case Odec:
		if(left->op == Oinds){
			indsascom(src, nto, n);
			break;
		}
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nil);
		if(nto != nil)
			genmove(src, Mas, left->ty, left, nto);
		if(right->addable >= Rcant)
			fatal("inc/dec amount not addressable: %n", n);
		genop(&n->src, op, right, nil, left);
		tfree(&tl);
		break;
	case Ospawn:
		if(left->left->op == Oind)
			fpcall(&n->src, op, left, nto);
		else
			callcom(&n->src, op, left, nto);
		break;
	case Oraise:
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nil);
		genrawop(&n->src, IRAISE, left, nil, nil);
		tfree(&tl);
		break;
	case Ocall:
		if(left->op == Oind)
			fpcall(esrc(src, &n->src, nto), op, n, nto);
		else
			callcom(esrc(src, &n->src, nto), op, n, nto);
		break;
	case Oref:
		t = left->ty;
		if(left->op == Oname && left->decl->store == Dfn || left->op == Omdot && left->right->op == Oname && left->right->decl->store == Dfn){	/* create a function reference */
			Decl *d;
			Node *mod, *ind;

			d = left->decl;
			if(left->op == Omdot){
				d = left->right->decl;
				mod = left->left;
			}
			else if(d->eimport != nil)
				mod = d->eimport;
			else{
				mod = rewrite(mkn(Oself, nil, nil));
				addiface(nil, d);
			}
			sumark(mod);
			talloc(&tto, n->ty, nto);
			genrawop(src, INEW, mktn(usetype(tfnptr)), nil, &tto);
			tr.src = *src;
			tr.op = Oind;
			tr.left = &tto;
			tr.right = nil;
			tr.ty = tany;
			sumark(&tr);
			ecom(src, &tr, mod);
			ind = mkunary(Oind, mkbin(Oadd, dupn(0, src, &tto), mkconst(src, IBY2WD)));
			ind->ty = ind->left->ty = ind->left->right->ty = tint;
			tr.op = Oas;
			tr.left = ind;
			tr.right = mkdeclname(src, d);
			tr.ty = tr.right->ty = tint;
			sumark(&tr);
			tr.right->addable = mod->op == Oself && newfnptr ? Rnoff : Roff;
			ecom(src, nil, &tr);
			if(!sameaddr(&tto, nto))
				genmove(src, Mas, n->ty, &tto, nto);
			tfree(&tto);
			break;
		}
		if(left->op == Oname && left->decl->store == Dtype){
			genrawop(src, INEW, mktn(t), nil, nto);
			break;
		}
		if(t->kind == Tadt && t->tags != nil){
			pickdupcom(src, nto, left);
			break;
		}

		tt = t;
		if(left->op == Oconst && left->decl->store == Dtag)
			t = left->decl->ty->tof;
		/*
		 * could eliminate temp if to does not occur
		 * in tuple initializer
		 */
		talloc(&tto, n->ty, nto);
		genrawop(src, INEW, mktn(t), nil, &tto);
		tr.op = Oind;
		tr.left = &tto;
		tr.right = nil;
		tr.ty = tt;
		sumark(&tr);
		ecom(src, &tr, left);
		if(!sameaddr(&tto, nto))
			genmove(src, Mas, n->ty, &tto, nto);
		tfree(&tto);
		break;
	case Oload:
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nto);
		talloc(&tr, tint, nil);
		if(LDT)
			genrawop(src, ILOAD, left, right, nto);
		else{
			genrawop(src, ILEA, right, nil, &tr);
			genrawop(src, ILOAD, left, &tr, nto);
		}
		tfree(&tl);
		tfree(&tr);
		break;
	case Ocast:
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nto);
		t = left->ty;
		if(t->kind == Tfix || n->ty->kind == Tfix){
			op = casttab[t->kind][n->ty->kind];
			if(op == ICVTXX)
				genfixcastop(src, op, left, nto);
			else{
				tn = sumark(mkrconst(src, scale2(t, n->ty)));
				genrawop(src, op, left, tn, nto);
			}
		}
		else
			genrawop(src, casttab[t->kind][n->ty->kind], left, nil, nto);
		tfree(&tl);
		break;
	case Oarray:
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nto);
		genrawop(esrc(src, &left->src, nto), arrayz ? INEWAZ : INEWA, left, mktn(n->ty->tof), nto);
		if(right != nil)
			arraycom(nto, right);
		tfree(&tl);
		break;
	case Oslice:
		tn = right->right;
		right = right->left;

		/*
		 * make the left node of the slice directly addressable
		 * therefore, if it's len is taken (via tn),
		 * left's tree won't be rewritten
		 */
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nil);

		if(tn->op == Onothing){
			tn = mkn(Olen, left, nil);
			tn->src = *src;
			tn->ty = tint;
			sumark(tn);
		}
		if(tn->addable < Ralways){
			if(right->addable >= Rcant)
				right = eacom(right, &tr, nil);
		}else if(right->temps <= tn->temps){
			tn = ecom(&tn->src, talloc(&ttn, tn->ty, nil), tn);
			if(right->addable >= Rcant)
				right = eacom(right, &tr, nil);
		}else{
			right = eacom(right, &tr, nil);
			tn = ecom(&tn->src, talloc(&ttn, tn->ty, nil), tn);
		}
		op = ISLICEA;
		if(nto->ty == tstring)
			op = ISLICEC;

		/*
		 * overwrite the destination last,
		 * since it might be used in computing the slice bounds
		 */
		if(!sameaddr(left, nto))
			ecom(&left->src, nto, left);

		genrawop(src, op, right, tn, nto);
		tfree(&tl);
		tfree(&tr);
		tfree(&ttn);
		break;
	case Oindx:
		if(right->addable < Rcant){
			if(left->addable >= Rcant)
				left = eacom(left, &tl, nto);
		}else if(left->temps < right->temps){
			right = eacom(right, &tr, nto);
			if(left->addable >= Rcant)
				left = eacom(left, &tl, nil);
		}else{
			left = eacom(left, &tl, nto);
			right = eacom(right, &tr, nil);
		}
		if(nto->addable >= Ralways)
			nto = ecom(src, talloc(&tto, nto->ty, nil), nto);
		op = IINDX;
		switch(left->ty->tof->size){
		case IBY2LG:
			op = IINDL;
			if(left->ty->tof == treal)
				op = IINDF;
			break;
		case IBY2WD:
			op = IINDW;
			break;
		case 1:
			op = IINDB;
			break;
		}
		genrawop(src, op, left, nto, right);
		// array[] of {....} [index] frees array too early (before index value used)
		// function(...) [index] frees array too early (before index value used)
		if(tl.decl != nil)
			tfreelater(&tl);
		else
			tfree(&tl);
		tfree(&tr);
		tfree(&tto);
		break;
	case Oind:
		n = eacom(n, &tl, nto);
		genmove(src, Mas, n->ty, n, nto);
		tfree(&tl);
		break;
	case Onot:
	case Oandand:
	case Ooror:
	case Oeq:
	case Oneq:
	case Olt:
	case Oleq:
	case Ogt:
	case Ogeq:
		p = bcom(n, 1, nil);
		genmove(src, Mas, tint, sumark(mkconst(src, 1)), nto);
		pp = genrawop(src, IJMP, nil, nil, nil);
		patch(p, nextinst());
		genmove(src, Mas, tint, sumark(mkconst(src, 0)), nto);
		patch(pp, nextinst());
		break;
	case Oself:
		if(newfnptr){
			if(nto != nil)
				genrawop(src, ISELF, nil, nil, nto);
			break;
		}
		tn = sumark(mkdeclname(src, selfdecl));
		p = genbra(src, Oneq, tn, sumark(mkdeclname(src, nildecl)));
		n->op = Oload;
		ecom(src, tn, n);
		patch(p, nextinst());
		genmove(src, Mas, n->ty, tn, nto);
		break;
	}
	return nto;
}

/*
 * compile exp n to yield an addressable expression
 * use reg to build a temporary; if t is a temp, it is usable
 * if dangle leaves the address dangling, generate into a temporary
 *	this should only happen with arrays
 *
 * note that 0adr's are strange as they are only used
 * for calculating the addresses of fields within adt's.
 * therefore an Oind is the parent or grandparent of the Oadr,
 * and we pick off all of the cases where Oadr's argument is not
 * addressable by looking from the Oind.
 */
Node*
eacom(Node *n, Node *reg, Node *t)
{
	Node *left, *tn;

	if(n->op == Ocomma){
		tn = n->left->left;
		ecom(&n->left->src, nil, n->left);
		n = eacom(n->right, reg, t);
		tfree(tn);
		return n;
	}

	if(debug['e'] || debug['E'])
		print("eacom: %n\n", n);

	left = n->left;
	if(n->op != Oind){
		ecom(&n->src, talloc(reg, n->ty, t), n);
		reg->src = n->src;
		return reg;
	}
		
	if(left->op == Oadd && left->right->op == Oconst){
		if(left->left->op == Oadr){
			left->left->left = eacom(left->left->left, reg, t);
			sumark(n);
			if(n->addable >= Rcant)
				fatal("eacom can't make node addressable: %n", n);
			return n;
		}
		talloc(reg, left->left->ty, t);
		ecom(&left->left->src, reg, left->left);
		left->left->decl = reg->decl;
		left->left->addable = Rreg;
		left->left = reg;
		left->addable = Raadr;
		n->addable = Radr;
	}else if(left->op == Oadr){
		talloc(reg, left->left->ty, t);
		ecom(&left->left->src, reg, left->left);

		/*
		 * sleaze: treat the temp as the type of the field, not the enclosing structure
		 */
		reg->ty = n->ty;
		reg->src = n->src;
		return reg;
	}else{
		talloc(reg, left->ty, t);
		ecom(&left->src, reg, left);
		n->left = reg;
		n->addable = Radr;
	}
	return n;
}

/*
 * compile an assignment to an array slice
 */
Node*
slicelcom(Src *src, Node *nto, Node *n)
{
	Node *left, *right, *v;
	Node tl, tr, tv, tu;

	tl.decl = nil;
	tr.decl = nil;
	tv.decl = nil;
	tu.decl = nil;

	left = n->left->left;
	right = n->left->right->left;
	v = n->right;
	if(right->addable < Ralways){
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nto);
	}else if(left->temps <= right->temps){
		right = ecom(&right->src, talloc(&tr, right->ty, nto), right);
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nil);
	}else{
		left = eacom(left, &tl, nil);		/* dangle on right and v */
		right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
	}

	switch(n->op){
	case Oas:
		if(v->addable >= Rcant)
			v = eacom(v, &tv, nil);
		break;
	}

	genrawop(&n->src, ISLICELA, v, right, left);
	if(nto != nil)
		genmove(src, Mas, n->ty, left, nto);
	tfree(&tl);
	tfree(&tv);
	tfree(&tr);
	tfree(&tu);
	return nto;
}

/*
 * compile an assignment to a string location
 */
Node*
indsascom(Src *src, Node *nto, Node *n)
{
	Node *left, *right, *u, *v;
	Node tl, tr, tv, tu;

	tl.decl = nil;
	tr.decl = nil;
	tv.decl = nil;
	tu.decl = nil;

	left = n->left->left;
	right = n->left->right;
	v = n->right;
	if(right->addable < Ralways){
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nto);
	}else if(left->temps <= right->temps){
		right = ecom(&right->src, talloc(&tr, right->ty, nto), right);
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nil);
	}else{
		left = eacom(left, &tl, nil);		/* dangle on right and v */
		right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
	}

	switch(n->op){
	case Oas:
		if(v->addable >= Rcant)
			v = eacom(v, &tv, nil);
		break;
	case Oinc:
	case Odec:
		if(v->addable >= Rcant)
			fatal("inc/dec amount not addable");
		u = talloc(&tu, tint, nil);
		genop(&n->left->src, Oinds, left, right, u);
		if(nto != nil)
			genmove(src, Mas, n->ty, u, nto);
		nto = nil;
		genop(&n->src, n->op, v, nil, u);
		v = u;
		break;
	case Oaddas:
	case Osubas:
	case Omulas:
	case Odivas:
	case Omodas:
	case Oexpas:
	case Oandas:
	case Ooras:
	case Oxoras:
	case Olshas:
	case Orshas:
		if(v->addable >= Rcant)
			v = eacom(v, &tv, nil);
		u = talloc(&tu, tint, nil);
		genop(&n->left->src, Oinds, left, right, u);
		genop(&n->src, n->op, v, nil, u);
		v = u;
		break;
	}

	genrawop(&n->src, IINSC, v, right, left);
	tfree(&tl);
	tfree(&tv);
	tfree(&tr);
	tfree(&tu);
	if(nto != nil)
		genmove(src, Mas, n->ty, v, nto);
	return nto;
}

void
callcom(Src *src, int op, Node *n, Node *ret)
{
	Node frame, tadd, toff, pass, *a, *mod, *ind, *nfn, *args, tmod, tind, *tn;
	Inst *in,*p;
	Decl *d, *callee;
	long off;
	int iop;

	args = n->right;
	nfn = n->left;
	switch(nfn->op){
		case Odot:
			callee = nfn->right->decl;
			nfn->addable = Rpc;
			break;
		case Omdot:
			callee = nfn->right->decl;
			break;
		case Oname:
			callee = nfn->decl;
			break;
		default:
			callee = nil;
			fatal("bad call op in callcom");
	}
	if(nfn->addable != Rpc && nfn->addable != Rmpc)
		fatal("can't gen call addresses");
	if(nfn->ty->tof != tnone && ret == nil){
		ecom(src, talloc(&tmod, nfn->ty->tof, nil), n);
		tfree(&tmod);
		return;
	}
	if(ispoly(callee))
		addfnptrs(callee, 0);
	if(nfn->ty->varargs){
		nfn->decl = dupdecl(nfn->right->decl);
		nfn->decl->desc = gendesc(nfn->right->decl, idoffsets(nfn->ty->ids, MaxTemp, MaxAlign), nfn->ty->ids);
	}

	talloc(&frame, tint, nil);

	mod = nfn->left;
	ind = nfn->right;
	tmod.decl = tind.decl = nil;
	if(nfn->addable == Rmpc){
		if(mod->addable >= Rcant)
			mod = eacom(mod, &tmod, nil);		/* dangle always */
		if(ind->op != Oname && ind->addable >= Ralways){
			talloc(&tind, ind->ty, nil);
			ecom(&ind->src, &tind, ind);
			ind = &tind;
		}
		else if(ind->decl != nil && ind->decl->store != Darg)
			ind->addable = Roff;
	}

	/*
	 * stop nested uncalled frames
	 * otherwise exception handling very complicated
	 */
	for(a = args; a != nil; a = a->right){
		if(hascall(a->left)){
			tn = mkn(0, nil, nil);
			talloc(tn, a->left->ty, nil);
			ecom(&a->left->src, tn, a->left);
			a->left = tn;
			tn->flags |= TEMP;
		}
	}

	/*
	 * allocate the frame
	 */
	if(nfn->addable == Rmpc && !nfn->ty->varargs){
		genrawop(src, IMFRAME, mod, ind, &frame);
	}else if(nfn->op == Odot){
		genrawop(src, IFRAME, nfn->left, nil, &frame);
	}else{
		in = genrawop(src, IFRAME, nil, nil, &frame);
		in->sm = Adesc;
		in->s.decl = nfn->decl;
	}

	/*
	 * build a fake node for the argument area
	 */
	toff = znode;
	tadd = znode;
	pass = znode;
	toff.op = Oconst;
	toff.addable = Rconst;
	toff.ty = tint;
	tadd.op = Oadd;
	tadd.addable = Raadr;
	tadd.left = &frame;
	tadd.right = &toff;
	tadd.ty = tint;
	pass.op = Oind;
	pass.addable = Radr;
	pass.left = &tadd;

	/*
	 * compile all the args
	 */
	d = nfn->ty->ids;
	off = 0;
	for(a = args; a != nil; a = a->right){
		off = d->offset;
		toff.val = off;
		if(d->ty->kind == Tpoly)
			pass.ty = a->left->ty;
		else
			pass.ty = d->ty;
		ecom(&a->left->src, &pass, a->left);
		d = d->next;
		if(a->left->flags & TEMP)
			tfree(a->left);
	}
	if(off > maxstack)
		maxstack = off;

	/*
	 * pass return value
	 */
	if(ret != nil){
		toff.val = REGRET*IBY2WD;
		pass.ty = nfn->ty->tof;
		p = genrawop(src, ILEA, ret, nil, &pass);
		p->m.offset = ret->ty->size;	/* for optimizer */
	}

	/*
	 * call it
	 */
	if(nfn->addable == Rmpc){
		iop = IMCALL;
		if(op == Ospawn)
			iop = IMSPAWN;
		genrawop(src, iop, &frame, ind, mod);
		tfree(&tmod);
		tfree(&tind);
	}else if(nfn->op == Odot){
		iop = ICALL;
		if(op == Ospawn)
			iop = ISPAWN;
		genrawop(src, iop, &frame, nil, nfn->right);
	}else{
		iop = ICALL;
		if(op == Ospawn)
			iop = ISPAWN;
		in = genrawop(src, iop, &frame, nil, nil);
		in->d.decl = nfn->decl;
		in->dm = Apc;
	}
	tfree(&frame);
}

/*
 * initialization code for arrays
 * a must be addressable (< Rcant)
 */
void
arraycom(Node *a, Node *elems)
{
	Node tindex, fake, tmp, ri, *e, *n, *q, *body, *wild;
	Inst *top, *out;
	/* Case *c; */

	if(debug['A'])
		print("arraycom: %n %n\n", a, elems);

	/* c = elems->ty->cse; */
	/* don't use c->wild in case we've been inlined */
	wild = nil;
	for(e = elems; e != nil; e = e->right)
		for(q = e->left->left; q != nil; q = q->right)
			if(q->left->op == Owild)
				wild = e->left;
	if(wild != nil)
		arraydefault(a, wild->right);

	tindex = znode;
	fake = znode;
	talloc(&tmp, tint, nil);
	tindex.op = Oindx;
	tindex.addable = Rcant;
	tindex.left = a;
	tindex.right = nil;
	tindex.ty = tint;
	fake.op = Oind;
	fake.addable = Radr;
	fake.left = &tmp;
	fake.ty = a->ty->tof;

	for(e = elems; e != nil; e = e->right){
		/*
		 * just duplicate the initializer for Oor
		 */
		for(q = e->left->left; q != nil; q = q->right){
			if(q->left->op == Owild)
				continue;
	
			body = e->left->right;
			if(q->right != nil)
				body = dupn(0, &nosrc, body);
			top = nil;
			out = nil;
			ri.decl = nil;
			if(q->left->op == Orange){
				/*
				 * for(i := q.left.left; i <= q.left.right; i++)
				 */
				talloc(&ri, tint, nil);
				ri.src = q->left->src;
				ecom(&q->left->src, &ri, q->left->left);
	
				/* i <= q.left.right; */
				n = mkn(Oleq, &ri, q->left->right);
				n->src = q->left->src;
				n->ty = tint;
				top = nextinst();
				out = bcom(n, 1, nil);
	
				tindex.right = &ri;
			}else{
				tindex.right = q->left;
			}
	
			tindex.addable = Rcant;
			tindex.src = q->left->src;
			ecom(&tindex.src, &tmp, &tindex);
	
			ecom(&body->src, &fake, body);
	
			if(q->left->op == Orange){
				/* i++ */
				n = mkbin(Oinc, &ri, sumark(mkconst(&ri.src, 1)));
				n->ty = tint;
				n->addable = Rcant;
				ecom(&n->src, nil, n);
	
				/* jump to test */
				patch(genrawop(&q->left->src, IJMP, nil, nil, nil), top);
				patch(out, nextinst());
				tfree(&ri);
			}
		}
	}
	tfree(&tmp);
}

/*
 * default initialization code for arrays.
 * compiles to
 *	n = len a;
 *	while(n){
 *		n--;
 *		a[n] = elem;
 *	}
 */
void
arraydefault(Node *a, Node *elem)
{
	Inst *out, *top;
	Node n, e, *t;

	if(debug['A'])
		print("arraydefault: %n %n\n", a, elem);

	t = mkn(Olen, a, nil);
	t->src = elem->src;
	t->ty = tint;
	t->addable = Rcant;
	talloc(&n, tint, nil);
	n.src = elem->src;
	ecom(&t->src, &n, t);

	top = nextinst();
	out = bcom(&n, 1, nil);

	t = mkbin(Odec, &n, sumark(mkconst(&elem->src, 1)));
	t->ty = tint;
	t->addable = Rcant;
	ecom(&t->src, nil, t);

	e.decl = nil;
	if(elem->addable >= Rcant)
		elem = eacom(elem, &e, nil);

	t = mkn(Oindx, a, &n);
	t->src = elem->src;
	t = mkbin(Oas, mkunary(Oind, t), elem);
	t->ty = elem->ty;
	t->left->ty = elem->ty;
	t->left->left->ty = tint;
	sumark(t);
	ecom(&t->src, nil, t);

	patch(genrawop(&t->src, IJMP, nil, nil, nil), top);

	tfree(&n);
	tfree(&e);
	patch(out, nextinst());
}

void
tupcom(Node *nto, Node *n)
{
	Node tadr, tadd, toff, fake, *e;
	Decl *d;

	if(debug['Y'])
		print("tupcom %n\nto %n\n", n, nto);

	/*
	 * build a fake node for the tuple
	 */
	toff = znode;
	tadd = znode;
	fake = znode;
	tadr = znode;
	toff.op = Oconst;
	toff.ty = tint;
	tadr.op = Oadr;
	tadr.left = nto;
	tadr.ty = tint;
	tadd.op = Oadd;
	tadd.left = &tadr;
	tadd.right = &toff;
	tadd.ty = tint;
	fake.op = Oind;
	fake.left = &tadd;
	sumark(&fake);
	if(fake.addable >= Rcant)
		fatal("tupcom: bad value exp %n", &fake);

	/*
	 * compile all the exps
	 */
	d = n->ty->ids;
	for(e = n->left; e != nil; e = e->right){
		toff.val = d->offset;
		fake.ty = d->ty;
		ecom(&e->left->src, &fake, e->left);
		d = d->next;
	}
}

void
tuplcom(Node *n, Node *nto)
{
	Node tadr, tadd, toff, fake, tas, *e, *as;
	Decl *d;

	if(debug['Y'])
		print("tuplcom %n\nto %n\n", n, nto);

	/*
	 * build a fake node for the tuple
	 */
	toff = znode;
	tadd = znode;
	fake = znode;
	tadr = znode;
	toff.op = Oconst;
	toff.ty = tint;
	tadr.op = Oadr;
	tadr.left = n;
	tadr.ty = tint;
	tadd.op = Oadd;
	tadd.left = &tadr;
	tadd.right = &toff;
	tadd.ty = tint;
	fake.op = Oind;
	fake.left = &tadd;
	sumark(&fake);
	if(fake.addable >= Rcant)
		fatal("tuplcom: bad value exp for %n", &fake);

	/*
	 * compile all the exps
	 */
	d = nto->ty->ids;
	if(nto->ty->kind == Tadtpick)
		d = nto->ty->tof->ids->next;
	for(e = nto->left; e != nil; e = e->right){
		as = e->left;
		if(as->op != Oname || as->decl != nildecl){
			toff.val = d->offset;
			fake.ty = d->ty;
			fake.src = as->src;
			if(as->addable < Rcant)
				genmove(&as->src, Mas, d->ty, &fake, as);
			else{
				tas.op = Oas;
				tas.ty = d->ty;
				tas.src = as->src;
				tas.left = as;
				tas.right = &fake;
				tas.addable = Rcant;
				ecom(&tas.src, nil, &tas);
			}
		}
		d = d->next;
	}
}

void
tuplrcom(Node *n, Node *nto)
{
	Node *s, *d, tas;
	Decl *de;

	de = nto->ty->ids;
	for(s = n->left, d = nto->left; s != nil && d != nil; s = s->right, d = d->right){
		if(d->left->op != Oname || d->left->decl != nildecl){
			tas.op = Oas;
			tas.ty = de->ty;
			tas.src = s->left->src;
			tas.left = d->left;
			tas.right = s->left;
			sumark(&tas);
			ecom(&tas.src, nil, &tas);
		}
		de = de->next;
	}
	if(s != nil || d != nil)
		fatal("tuplrcom");
}

/*
 * boolean compiler
 * fall through when condition == true
 */
Inst*
bcom(Node *n, int true, Inst *b)
{
	Inst *bb;
	Node tl, tr, *t, *left, *right, *tn;
	int op;

	if(n->op == Ocomma){
		tn = n->left->left;
		ecom(&n->left->src, nil, n->left);
		bb = bcom(n->right, true, b);
		tfree(tn);
		return bb;
	}

	if(debug['b'])
		print("bcom %n %d\n", n, true);

	left = n->left;
	right = n->right;
	op = n->op;
	
	switch(op){
	case Onothing:
		return b;
	case Onot:
		return bcom(n->left, !true, b);
	case Oandand:
		if(!true)
			return oror(n, true, b);
		return andand(n, true, b);
	case Ooror:
		if(!true)
			return andand(n, true, b);
		return oror(n, true, b);
	case Ogt:
	case Ogeq:
	case Oneq:
	case Oeq:
	case Olt:
	case Oleq:
		break;
	default:
		if(n->ty->kind == Tint){
			right = mkconst(&n->src, 0);
			right->addable = Rconst;
			left = n;
			op = Oneq;
			break;
		}
		fatal("can't bcom %n", n);
		return b;
	}

	if(true)
		op = oprelinvert[op];

	if(left->addable < right->addable){
		t = left;
		left = right;
		right = t;
		op = opcommute[op];
	}

	tl.decl = nil;
	tr.decl = nil;
	if(right->addable < Ralways){
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nil);
	}else if(left->temps <= right->temps){
		right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
		if(left->addable >= Rcant)
			left = eacom(left, &tl, nil);
	}else{
		left = eacom(left, &tl, nil);
		right = ecom(&right->src, talloc(&tr, right->ty, nil), right);
	}
	bb = genbra(&n->src, op, left, right);
	bb->branch = b;
	tfree(&tl);
	tfree(&tr);
	return bb;
}

Inst*
andand(Node *n, int true, Inst *b)
{
	if(debug['b'])
		print("andand %n\n", n);
	b = bcom(n->left, true, b);
	b = bcom(n->right, true, b);
	return b;
}

Inst*
oror(Node *n, int true, Inst *b)
{
	Inst *bb;

	if(debug['b'])
		print("oror %n\n", n);
	bb = bcom(n->left, !true, nil);
	b = bcom(n->right, true, b);
	patch(bb, nextinst());
	return b;
}

/*
 * generate code for a recva expression
 * this is just a hacked up small alt
 */
void
recvacom(Src *src, Node *nto, Node *n)
{
	Label *labs;
	Case *c;
	Node which, tab, off, add, adr, slot, *left;
	Type *talt;
	Inst *p;

	left = n->left;

	labs = allocmem(1 * sizeof *labs);
	labs[0].isptr = left->addable >= Rcant;
	c = allocmem(sizeof *c);
	c->nlab = 1;
	c->labs = labs;
	talt = mktalt(c);

	talloc(&which, tint, nil);
	talloc(&tab, talt, nil);

	/*
	 * build the node for the address of each channel,
	 * the values to send, and the storage fro values received
	 */
	off = znode;
	off.op = Oconst;
	off.ty = tint;
	off.addable = Rconst;
	adr = znode;
	adr.op = Oadr;
	adr.left = &tab;
	adr.ty = tint;
	add = znode;
	add.op = Oadd;
	add.left = &adr;
	add.right = &off;
	add.ty = tint;
	slot = znode;
	slot.op = Oind;
	slot.left = &add;
	sumark(&slot);

	/*
	 * gen the channel
	 * this sleaze is lying to the garbage collector
	 */
	off.val = 2*IBY2WD;
	if(left->addable < Rcant)
		genmove(src, Mas, tint, left, &slot);
	else{
		slot.ty = left->ty;
		ecom(src, &slot, left);
		slot.ty = nil;
	}

	/*
	 * gen the value
	 */
	off.val += IBY2WD;
	p = genrawop(&left->src, ILEA, nto, nil, &slot);
	p->m.offset = nto->ty->size;	/* for optimizer */

	/*
	 * number of senders and receivers
	 */
	off.val = 0;
	genmove(src, Mas, tint, sumark(mkconst(src, 0)), &slot);
	off.val += IBY2WD;
	genmove(src, Mas, tint, sumark(mkconst(src, 1)), &slot);
	off.val += IBY2WD;

	p = genrawop(src, IALT, &tab, nil, &which);
	p->m.offset = talt->size;	/* for optimizer */
	tfree(&which);
	tfree(&tab);
}

/*
 * generate code to duplicate an adt with pick fields
 * this is just a hacked up small pick
 * n is Oind(exp)
 */
void
pickdupcom(Src *src, Node *nto, Node *n)
{
	Node *start, *stop, *node, *orig, *dest, tmp, clab;
	Case *c;
	Inst *j, *jmps, *wild;
	Label *labs;
	Decl *d, *tg, *stg;
	Type *t;
	int i, nlab;
	char buf[32];

	if(n->op != Oind)
		fatal("pickdupcom not Oind: %n" ,n);

	t = n->ty;
	nlab = t->decl->tag;

	/*
	 * generate global which has case labels
	 */
	seprint(buf, buf+sizeof(buf), ".c%d", nlabel++);
	d = mkids(src, enter(buf, 0), mktype(&src->start, &src->stop, Tcase, nil, nil), nil);
	d->init = mkdeclname(src, d);

	clab.addable = Rmreg;
	clab.left = nil;
	clab.right = nil;
	clab.op = Oname;
	clab.ty = d->ty;
	clab.decl = d;

	/*
	 * generate a temp to hold the real value
	 * then generate a case on the tag
	 */
	orig = n->left;
	talloc(&tmp, orig->ty, nil);
	ecom(src, &tmp, orig);
	orig = mkunary(Oind, &tmp);
	orig->ty = tint;
	sumark(orig);

	dest = mkunary(Oind, nto);
	dest->ty = nto->ty->tof;
	sumark(dest);

	genrawop(src, ICASE, orig, nil, &clab);

	labs = allocmem(nlab * sizeof *labs);

	i = 0;
	jmps = nil;
	for(tg = t->tags; tg != nil; tg = tg->next){
		stg = tg;
		for(; tg->next != nil; tg = tg->next)
			if(stg->ty != tg->next->ty)
				break;
		start = sumark(simplify(mkdeclname(src, stg)));
		stop = start;
		node = start;
		if(stg != tg){
			stop = sumark(simplify(mkdeclname(src, tg)));
			node = mkbin(Orange, start, stop);
		}

		labs[i].start = start;
		labs[i].stop = stop;
		labs[i].node = node;
		labs[i++].inst = nextinst();

		genrawop(src, INEW, mktn(tg->ty->tof), nil, nto);
		genmove(src, Mas, tg->ty->tof, orig, dest);

		j = genrawop(src, IJMP, nil, nil, nil);
		j->branch = jmps;
		jmps = j;
	}

	/*
	 * this should really be a runtime error
	 */
	wild = genrawop(src, IJMP, nil, nil, nil);
	patch(wild, wild);

	patch(jmps, nextinst());
	tfree(&tmp);

	if(i > nlab)
		fatal("overflowed label tab for pickdupcom");

	c = allocmem(sizeof *c);
	c->nlab = i;
	c->nsnd = 0;
	c->labs = labs;
	c->iwild = wild;

	d->ty->cse = c;
	usetype(d->ty);
	installids(Dglobal, d);
}

/*
 * see if name n occurs anywhere in e
 */
int
tupaliased(Node *n, Node *e)
{
	for(;;){
		if(e == nil)
			return 0;
		if(e->op == Oname && e->decl == n->decl)
			return 1;
		if(tupaliased(n, e->left))
			return 1;
		e = e->right;
	}
}

/*
 * see if any name in n occurs anywere in e
 */
int
tupsaliased(Node *n, Node *e)
{
	for(;;){
		if(n == nil)
			return 0;
		if(n->op == Oname && tupaliased(n, e))
			return 1;
		if(tupsaliased(n->left, e))
			return 1;
		n = n->right;
	}
}

/*
 * put unaddressable constants in the global data area
 */
Decl*
globalconst(Node *n)
{
	Decl *d;
	Sym *s;
	char buf[32];

	seprint(buf, buf+sizeof(buf), ".i.%.8lux", (long)n->val);
	s = enter(buf, 0);
	d = s->decl;
	if(d == nil){
		d = mkids(&n->src, s, tint, nil);
		installids(Dglobal, d);
		d->init = n;
		d->refs++;
	}
	return d;
}

Decl*
globalBconst(Node *n)
{
	Decl *d;
	Sym *s;
	char buf[32];

	seprint(buf, buf+sizeof(buf), ".B.%.8lux.%8lux", (long)(n->val>>32), (long)n->val);

	s = enter(buf, 0);
	d = s->decl;
	if(d == nil){
		d = mkids(&n->src, s, tbig, nil);
		installids(Dglobal, d);
		d->init = n;
		d->refs++;
	}
	return d;
}

Decl*
globalbconst(Node *n)
{
	Decl *d;
	Sym *s;
	char buf[32];

	seprint(buf, buf+sizeof(buf), ".b.%.2lux", (long)n->val & 0xff);
	s = enter(buf, 0);
	d = s->decl;
	if(d == nil){
		d = mkids(&n->src, s, tbyte, nil);
		installids(Dglobal, d);
		d->init = n;
		d->refs++;
	}
	return d;
}

Decl*
globalfconst(Node *n)
{
	Decl *d;
	Sym *s;
	char buf[32];
	ulong dv[2];

	dtocanon(n->rval, dv);
	seprint(buf, buf+sizeof(buf), ".f.%.8lux.%8lux", dv[0], dv[1]);
	s = enter(buf, 0);
	d = s->decl;
	if(d == nil){
		d = mkids(&n->src, s, treal, nil);
		installids(Dglobal, d);
		d->init = n;
		d->refs++;
	}
	return d;
}

Decl*
globalsconst(Node *n)
{
	Decl *d;
	Sym *s;

	s = n->decl->sym;
	d = s->decl;
	if(d == nil){
		d = mkids(&n->src, s, tstring, nil);
		installids(Dglobal, d);
		d->init = n;
	}
	d->refs++;
	return d;
}

static Node*
subst(Decl *d, Node *e, Node *n)
{
	if(n == nil)
		return nil;
	if(n->op == Oname){
		if(d == n->decl){
			n = dupn(0, nil, e);
			n->ty = d->ty;
		}
		return n;
	}
	n->left = subst(d, e, n->left);
	n->right = subst(d, e, n->right);
	return n;
}

static Node*
putinline(Node *n)
{
	Node *e, *tn;
	Type *t;
	Decl *d;

if(debug['z']) print("inline1: %n\n", n);
	if(n->left->op == Oname)
		d = n->left->decl;
	else
		d = n->left->right->decl;
	e = d->init;
	t = e->ty;
	e = dupn(1, &n->src, e->right->left->left);
	for(d = t->ids, n = n->right; d != nil && n != nil; d = d->next, n = n->right){
		if(hasside(n->left, 0) && occurs(d, e) != 1){
			tn = talloc(mkn(0, nil, nil), d->ty, nil);
			e = mkbin(Ocomma, mkbin(Oas, tn, n->left), subst(d, tn, e));
			e->ty = e->right->ty;
			e->left->ty = d->ty;
		}
		else
			e = subst(d, n->left, e);
	}
	if(d != nil || n != nil)
		fatal("bad arg match in putinline()");
if(debug['z']) print("inline2: %n\n", e);
	return e;
}

static void
fpcall(Src *src, int op, Node *n, Node *ret)
{
	Node tp, *e, *mod, *ind;

	tp.decl = nil;
	e = n->left->left;
	if(e->addable >= Rcant)
		e = eacom(e, &tp, nil);
	mod = mkunary(Oind, e);
	ind = mkunary(Oind, mkbin(Oadd, dupn(0, src, e), mkconst(src, IBY2WD)));
	n->left = mkbin(Omdot, mod, ind);
	n->left->ty = e->ty->tof;
	mod->ty = ind->ty = ind->left->ty = ind->left->right->ty = tint;
	sumark(n);
	callcom(src, op, n, ret);
	tfree(&tp);
}
