code: purgatorio

ref: 82b046f36f8084a22bbb5d71edd0edd9179561eb
dir: /appl/cmd/limbo/ecom.b/

View raw version
maxstack:	int;				# max size of a stack frame called

precasttab := array[Tend] of array of ref Type;

optabinit()
{
	ct := array[Tend] of ref Type;
	for(i := 0; i < Tend; i++)
		precasttab[i] = ct;
	precasttab[Tstring] = array[Tend] of { Tbyte => tint, Tfix => treal, };
	precasttab[Tbig] = array[Tend] of { Tbyte => tint, Tfix => treal, };
	precasttab[Treal] = array[Tend] of { Tbyte => tint, };
	precasttab[Tfix] = array[Tend] of { Tbyte => tint, Tstring => treal, Tbig => treal, };
	precasttab[Tbyte] = array[Tend] of { Tstring => tint, Tbig => tint, Treal => tint, Tfix => tint, };

	casttab = array[Tend] of { * => array[Tend] of {* => 0}};

	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] = 16rff;
	casttab[Tstring][Tbyte] = 16rff;
	casttab[Tbyte][Treal] = 16rff;
	casttab[Treal][Tbyte] = 16rff;
	casttab[Tbyte][Tbig] = 16rff;
	casttab[Tbig][Tbyte] = 16rff;
	casttab[Tfix][Tbyte] = 16rff;
	casttab[Tbyte][Tfix] = 16rff;
	casttab[Tfix][Tbig] = 16rff;
	casttab[Tbig][Tfix] = 16rff;
	casttab[Tfix][Tstring] = 16rff;
	casttab[Tstring][Tfix] = 16rff;
}

#
# global variable and constant initialization checking
#
vcom(ids: ref Decl): int
{
	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;
}

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

isfix(n: ref Node): int
{
	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
#
rewrite(n: ref Node): ref Node
{
	v: big;
	t: ref Type;
	d: ref Decl;
	nn: ref Node;

	if(n == nil)
		return nil;

	left := n.left;
	right := n.right;

	#
	# rewrites
	#
	case n.op{
	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(int t.cons)
				fatal("cons in rewrite Oname");
			n = mkbin(Oadd, n, mkconst(n.src, big(2*IBY2WD)));
			n = mkunary(Oind, n);
			n.ty = t;
			n.left.ty = n.left.left.ty = tint;
			return rewrite(n);
		}
	Odas =>
		n.op = Oas;
		return rewrite(n);
	Oneg =>
		n.left = rewrite(left);
		if(n.ty == treal)
			break;
		left = n.left;
		n.right = left;
		n.left = mkconst(n.src, big 0);
		n.left.ty = n.ty;
		n.op = Osub;
	Ocomp =>
		v = big 0;
		v = ~v;
		n.right = mkconst(n.src, v);
		n.right.ty = n.ty;
		n.left = rewrite(left);
		n.op = Oxor;
	Oinc or
	Odec or
	Opreinc or
	Opredec =>
		n.left = rewrite(left);
		case n.ty.kind{
		Treal =>
			n.right = mkrconst(n.src, 1.0);
		Tint or
		Tbig or
		Tbyte or
		Tfix =>
			n.right = mkconst(n.src, big 1);
			n.right.ty = n.ty;
		* =>
			fatal("can't rewrite inc/dec "+nodeconv(n));
		}
		if(n.op == Opreinc)
			n.op = Oaddas;
		else if(n.op == Opredec)
			n.op = Osubas;
	Oslice =>
		if(right.left.op == Onothing)
			right.left = mkconst(right.left.src, big 0);
		n.left = rewrite(left);
		n.right = rewrite(right);
	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;
	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);
	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);
	Oraise =>
		if(left.ty == tstring)
			;
		else if(left.ty.cons == byte 0)
			break;
		else if(left.op != Ocall || left.left.ty.kind == Tfn){
			left = mkunary(Ocall, left);
			left.ty = left.left.ty;
		}
		n.left = rewrite(left);
	Ocall =>
		t = left.ty;
		if(t.kind == Tref)
			t = t.tof;
		if(t.kind == Tfn){
			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 " + nodeconv(n) + " in rewrite");
			if(ispoly(d))
				addfnptrs(d, 0);
			n.left = rewrite(left);
			if(right != nil)
				n.right = rewrite(right);
			if(d != nil && int d.inline == 1)
				n = simplify(inline(n));
			break;
		}
		case n.ty.kind{
		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);
		Tadt =>
			n.op = Otuple;
			n.right = nil;
			if(n.ty.tags != nil){
				n.left = nn = mkunary(Oseq, mkconst(n.src, big 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);
		Tadtpick =>
			n.op = Otuple;
			n.right = nil;
			n.left = nn = mkunary(Oseq, mkconst(n.src, big 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);
		Texception =>
			if(n.ty.cons == byte 0)
				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, big 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);
		* =>
			fatal("can't deal with "+nodeconv(n)+" in rewrite/Ocall");
		}
	Omdot =>
		#
		# what about side effects from left?
		#
		d = right.decl;
		case d.store{
		Dfn =>
			n.left = rewrite(left);
			if(right.op == Odot){
				n.right = dupn(1, left.src, right.right);
				n.right.ty = d.ty;
			}
		Dconst or
		Dtag or
		Dtype =>
			# handled by fold
			return n;
		Dglobal =>
			right.op = Oconst;
			right.c = ref Const(big d.offset, 0.);
			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;
		Darg =>
			return n;
		* =>
			fatal("can't deal with "+nodeconv(n)+" in rewrite/Omdot");
		}
	Odot =>
		#
		# what about side effects from left?
		#
		d = right.decl;
		case d.store{
		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;
		Dconst or
		Dtag or
		Dtype =>
			# handled by fold
			return n;
		}
		if(istuple(left))
			return n;	# handled by fold
		right.op = Oconst;
		right.c = ref Const(big d.offset, 0.);
		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;
	Oadr =>
		left = rewrite(left);
		n.left = left;
		if(left.op == Oind)
			return left.left;
	Otagof =>
		if(n.decl == nil){
			n.op = Oind;
			return rewrite(n);
		}
		return n;
	Omul or
	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;
		}
	Oself =>
		if(newfnptr)
			return n;
		if(selfdecl == nil){
			d = selfdecl = mkids(n.src, enter(".self", 5), tany, nil);
			installids(Dglobal, d);
			d.refs++;
		}
		nn = mkn(Oload, nil, nil);
		nn.src = n.src;
		nn.left = mksconst(n.src, enterstring("$self"));
		nn.ty = impdecl.ty;
		usetype(nn.ty);
		usetype(nn.ty.tof);
		nn = rewrite(nn);
		nn.op = Oself;
		return nn;
	Ofnptr =>
		if(n.flags == byte 0){
			# module
			if(left == nil)
				left = mkn(Oself, nil, nil);
			return rewrite(left);
		}
		right.flags = n.flags;
		n = right;
		d = n.decl;
		if(int n.flags == FNPTR2){
			if(left != nil && left.op != Oname)
				fatal("not Oname for addiface");
			if(left == nil){
				addiface(nil, d);
				if(newfnptr)
					n.flags |= byte FNPTRN;
			}
			else
				addiface(left.decl, d);
			n.ty = tint;
			return n;
		}
		if(int n.flags == FNPTRA){
			n = mkdeclname(n.src, d.link);
			n.ty = tany;
			return n;
		}
		if(int n.flags == (FNPTRA|FNPTR2)){
			n = mkdeclname(n.src, d.link.next);
			n.ty = tint;
			return n;
		}
	Ochan =>
		if(left == nil)
			left = n.left = mkconst(n.src, big 0);
		n.left = rewrite(left);
	* =>
		n.left = rewrite(left);
		n.right = rewrite(right);
	}

	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
#
sumark(n: ref Node): ref Node
{
	if(n == nil)
		return nil;

	n.temps = byte 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;
	}

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

mktn(t: ref Type): ref Node
{
	n := mkn(Oname, nil, nil);
	usedesc(mktdesc(t));
	n.ty = t;
	if(t.decl == nil)
		fatal("mktn nil decl t "+typeconv(t));
	n.decl = t.decl;
	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
#
tupblk0(n: ref Node, d: ref Decl): (int, ref Decl)
{
	ok, nid: int;

	case(n.op){
	Otuple =>
		for(n = n.left; n != nil; n = n.right){
			(ok, d) = tupblk0(n.left, d);
			if(!ok)
				return (0, nil);
		}
		return (1, d);
	Oname =>
		if(n.decl == nildecl)
			return (0, nil);
		if(d != nil && d.next != n.decl)
			return (0, nil);
		nid = int n.decl.nid;
		if(d == nil && nid == 1)
			return (0, nil);
		if(d != nil && nid != 0)
			return (0, nil);
		return (1, n.decl);
	}
	return (0, nil);
}

# could force locals to be next to each other
# - need to shuffle locals list
# - later
#
tupblk(n: ref Node): ref Node
{
	ok: int;
	d: ref Decl;

	if(n.op != Otuple)
		return nil;
	d = nil;
	(ok, d) = tupblk0(n, d);
	if(!ok)
		return nil;
	while(n.op == Otuple)
		n = n.left.left;
	if(n.op != Oname || n.decl.nid == byte 1)
		fatal("bad tupblk");
	return n;
}
	
# for cprof
esrc(src: Src, osrc: Src, nto: ref Node): Src
{
	if(nto != nil && src.start != 0 && src.stop != 0)
		return src;
	return osrc;
}

#
# compile an expression with an implicit assignment
# note: you are not allowed to use nto.src
#
# need to think carefully about the types used in moves
#
ecom(src: Src, nto, n: ref Node): ref Node
{
	tleft, tright, tto, ttn: ref Node;
	t: ref Type;
	p: ref Inst;

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

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

	left := n.left;
	right := n.right;
	op := n.op;
	case op{
	* =>
		fatal("can't ecom "+nodeconv(n));
		return nto;
	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());
	Ocomma =>
		ttn = left.left;
		ecom(left.src, nil, left);
		ecom(right.src, nto, right);
		tfree(ttn);
	Oname =>
		if(n.addable == Rpc){
			if(nto != nil)
				genmove(src, Mas, n.ty, n, nto);
			return nto;
		}
		fatal("can't ecom "+nodeconv(n));
	Onothing =>
		break;
	Oused =>
		if(nto != nil)
			fatal("superflous used "+nodeconv(left)+" nto "+nodeconv(nto));
		tto = talloc(left.ty, nil);
		ecom(left.src, tto, left);
		tfree(tto);
	Oas =>
		if(right.ty == tany)
			right.ty = n.ty;
		if(left.op == Oname && left.decl.ty == tany){
			if(nto == nil)
				nto = tto = talloc(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)){
				tright = talloc(n.ty, nil);
				ecom(n.right.src, tright, right);
				right = tright;
			}
			tuplcom(right, n.left);
			if(nto != nil)
				genmove(src, Mas, n.ty, right, nto);
			tfree(tright);
			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, tright = talloc(right.ty, nil), right);
		}

		#
		# think carefully about types here
		#
		if(left.addable >= Rcant)
			(left, tleft) = eacom(left, nto);
		ecom(n.src, left, right);
		if(nto != nil)
			genmove(src, Mas, nto.ty, left, nto);
		tfree(tleft);
		tfree(tright);
		tfree(tto);
	Ochan =>
		if(left != nil && left.addable >= Rcant)
			(left, tleft) = eacom(left, nto);
		genchan(src, left, n.ty.tof, nto);
		tfree(tleft);
	Oinds =>
		if(right.addable < Ralways){
			if(left.addable >= Rcant)
				(left, tleft) = eacom(left, nil);
		}else if(left.temps <= right.temps){
			right = ecom(right.src, tright = talloc(right.ty, nil), right);
			if(left.addable >= Rcant)
				(left, tleft) = eacom(left, nil);
		}else{
			(left, tleft) = eacom(left, nil);
			right = ecom(right.src, tright = talloc(right.ty, nil), right);
		}
		genop(n.src, op, left, right, nto);
		tfree(tleft);
		tfree(tright);
	Osnd =>
		if(right.addable < Rcant){
			if(left.addable >= Rcant)
				(left, tleft) = eacom(left, nto);
		}else if(left.temps < right.temps){
			(right, tright) = eacom(right, nto);
			if(left.addable >= Rcant)
				(left, tleft) = eacom(left, nil);
		}else{
			(left, tleft) = eacom(left, nto);
			(right, tright) = eacom(right, 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(tleft);
		tfree(tright);
	Orcv =>
		if(nto == nil){
			ecom(n.src, tto = talloc(n.ty, nil), n);
			tfree(tto);
			return nil;
		}
		if(left.addable >= Rcant)
			(left, tleft) = eacom(left, 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(tleft);
	Ocons =>
		#
		# another temp which can go with analysis
		#
		if(left.addable >= Rcant)
			(left, tleft) = eacom(left, nil);
		if(!sameaddr(right, nto)){
			ecom(right.src, tto = talloc(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(tleft);
		tfree(tto);
	Ohd =>
		if(left.addable >= Rcant)
			(left, tleft) = eacom(left, nto);
		genmove(src, Mhd, nto.ty, left, nto);
		tfree(tleft);
	Otl =>
		if(left.addable >= Rcant)
			(left, tleft) = eacom(left, nto);
		genmove(src, Mtl, left.ty, left, nto);
		tfree(tleft);
	Otuple =>
		if((tn := tupblk(n)) != nil){
			tn.ty = n.ty;
			genmove(src, Mas, n.ty, tn, nto);
			break;
		}
		tupcom(nto, n);
	Oadd or
	Osub or
	Omul or
	Odiv or
	Omod or
	Oand or
	Oor or
	Oxor or
	Olsh or
	Orsh or
	Oexp =>
		#
		# check for 2 operand forms
		#
		if(sameaddr(nto, left)){
			if(right.addable >= Rcant)
				(right, tright) = eacom(right, nto);
			genop(src, op, right, nil, nto);
			tfree(tright);
			break;
		}

		if(opcommute[op] && sameaddr(nto, right) && n.ty != tstring){
			if(left.addable >= Rcant)
				(left, tleft) = eacom(left, nto);
			genop(src, opcommute[op], left, nil, nto);
			tfree(tleft);
			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, tright) = eacom(right, nto);
		}else if(right.temps <= left.temps){
			left = ecom(left.src, tleft = talloc(left.ty, nto), left);
			if(right.addable >= Rcant)
				(right, tright) = eacom(right, nil);
		}else{
			(right, tright) = eacom(right, nto);
			left = ecom(left.src, tleft = talloc(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(tleft);
		tfree(tright);
	Oaddas or
	Osubas or
	Omulas or
	Odivas or
	Omodas or
	Oexpas or
	Oandas or
	Ooras or
	Oxoras or
	Olshas or
	Orshas =>
		if(left.op == Oinds){
			indsascom(src, nto, n);
			break;
		}
		if(right.addable < Rcant){
			if(left.addable >= Rcant)
				(left, tleft) = eacom(left, nto);
		}else if(left.temps < right.temps){
			(right, tright) = eacom(right, nto);
			if(left.addable >= Rcant)
				(left, tleft) = eacom(left, nil);
		}else{
			(left, tleft) = eacom(left, nto);
			(right, tright) = eacom(right, nil);
		}
		genop(n.src, op, right, nil, left);
		if(nto != nil)
			genmove(src, Mas, left.ty, left, nto);
		tfree(tleft);
		tfree(tright);
	Olen =>
		if(left.addable >= Rcant)
			(left, tleft) = eacom(left, 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 "+nodeconv(n));
		genrawop(src, op, left, nil, nto);
		tfree(tleft);
	Oneg =>
		if(left.addable >= Rcant)
			(left, tleft) = eacom(left, nto);
		genop(n.src, op, left, nil, nto);
		tfree(tleft);
	Oinc or
	Odec =>
		if(left.op == Oinds){
			indsascom(src, nto, n);
			break;
		}
		if(left.addable >= Rcant)
			(left, tleft) = eacom(left, nil);
		if(nto != nil)
			genmove(src, Mas, left.ty, left, nto);
		if(right.addable >= Rcant)
			fatal("inc/dec amount not addressable: "+nodeconv(n));
		genop(n.src, op, right, nil, left);
		tfree(tleft);
	Ospawn =>
		if(left.left.op == Oind)
			fpcall(n.src, op, left, nto);
		else
			callcom(n.src, op, left, nto);
	Oraise =>
		if(left.addable >= Rcant)
			(left, tleft) = eacom(left, nil);
		genrawop(n.src, IRAISE, left, nil, nil);
		tfree(tleft);
	Ocall =>
		if(left.op == Oind)
			fpcall(esrc(src, n.src, nto), op, n, nto);
		else
			callcom(esrc(src, n.src, nto), op, n, nto);
	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
			mod, ind: ref Node;

			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);
			tto = talloc(n.ty, nto);
			genrawop(src, INEW, mktn(usetype(tfnptr)), nil, tto);
			tright = ref znode;
			tright.src = src;
			tright.op = Oind;
			tright.left = tto;
			tright.right = nil;
			tright.ty = tany;
			sumark(tright);
			ecom(src, tright, mod);
			ind = mkunary(Oind, mkbin(Oadd, dupn(0, src, tto), mkconst(src, big IBY2WD)));
			ind.ty = ind.left.ty = ind.left.right.ty = tint;
			tright.op = Oas;
			tright.left = ind;
			tright.right = mkdeclname(src, d);
			tright.ty = tright.right.ty = tint;
			sumark(tright);
			if(mod.op == Oself && newfnptr)
				tright.right.addable = Rnoff;
			else
				tright.right.addable = Roff;
			ecom(src, nil, tright);
			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 nto does not occur
		# in tuple initializer
		#
		tto = talloc(n.ty, nto);
		genrawop(src, INEW, mktn(t), nil, tto);
		tright = ref znode;
		tright.op = Oind;
		tright.left = tto;
		tright.right = nil;
		tright.ty = tt;
		sumark(tright);
		ecom(src, tright, left);
		if(!sameaddr(tto, nto))
			genmove(src, Mas, n.ty, tto, nto);
		tfree(tto);
	Oload =>
		if(left.addable >= Rcant)
			(left, tleft) = eacom(left, nto);
		tright = talloc(tint, nil);
		if(LDT)
			genrawop(src, ILOAD, left, right, nto);
		else{
			genrawop(src, ILEA, right, nil, tright);
			genrawop(src, ILOAD, left, tright, nto);
		}
		tfree(tleft);
		tfree(tright);
	Ocast =>
		if(left.addable >= Rcant)
			(left, tleft) = eacom(left, 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{
				ttn = sumark(mkrconst(src, scale2(t, n.ty)));
				genrawop(src, op, left, ttn, nto);
			}
		}
		else
			genrawop(src, casttab[t.kind][n.ty.kind], left, nil, nto);
		tfree(tleft);
	Oarray =>
		if(left.addable >= Rcant)
			(left, tleft) = eacom(left, nto);
		if(arrayz)
			genrawop(esrc(src, left.src, nto), INEWAZ, left, mktn(n.ty.tof), nto);
		else
			genrawop(esrc(src, left.src, nto), INEWA, left, mktn(n.ty.tof), nto);
		if(right != nil)
			arraycom(nto, right);
		tfree(tleft);
	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, tleft) = eacom(left, 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, tright) = eacom(right, nil);
		}else if(right.temps <= tn.temps){
			tn = ecom(tn.src, ttn = talloc(tn.ty, nil), tn);
			if(right.addable >= Rcant)
				(right, tright) = eacom(right, nil);
		}else{
			(right, tright) = eacom(right, nil);
			tn = ecom(tn.src, ttn = talloc(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(tleft);
		tfree(tright);
		tfree(ttn);
	Oindx =>
		if(right.addable < Rcant){
			if(left.addable >= Rcant)
				(left, tleft) = eacom(left, nto);
		}else if(left.temps < right.temps){
			(right, tright) = eacom(right, nto);
			if(left.addable >= Rcant)
				(left, tleft) = eacom(left, nil);
		}else{
			(left, tleft) = eacom(left, nto);
			(right, tright) = eacom(right, nil);
		}
		if(nto.addable >= Ralways)
			nto = ecom(src, tto = talloc(nto.ty, nil), nto);
		op = IINDX;
		case left.ty.tof.size{
		IBY2LG =>
			op = IINDL;
			if(left.ty.tof == treal)
				op = IINDF;
		IBY2WD =>
			op = IINDW;
		1 =>
			op = IINDB;
		}
		genrawop(src, op, left, nto, right);
		if(tleft != nil && tleft.decl != nil)
			tfreelater(tleft);
		else
			tfree(tleft);
		tfree(tright);
		tfree(tto);
	Oind =>
		(n, tleft) = eacom(n, nto);
		genmove(src, Mas, n.ty, n, nto);
		tfree(tleft);
	Onot or
	Oandand or
	Ooror or
	Oeq or
	Oneq or
	Olt or
	Oleq or
	Ogt or
	Ogeq =>
		p = bcom(n, 1, nil);
		genmove(src, Mas, tint, sumark(mkconst(src, big 1)), nto);
		pp := genrawop(src, IJMP, nil, nil, nil);
		patch(p, nextinst());
		genmove(src, Mas, tint, sumark(mkconst(src, big 0)), nto);
		patch(pp, nextinst());
	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);
	}
	return nto;
}

#
# compile exp n to yield an addressable expression
# use reg to build a temporary; if t is a temp, it is usable
#
# 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.
#
eacom(n, t: ref Node): (ref Node, ref Node)
{
	reg: ref Node;

	if(n.op == Ocomma){
		tn := n.left.left;
		ecom(n.left.src, nil, n.left);
		nn := eacom(n.right, t);
		tfree(tn);
		return nn;
	}

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

	left := n.left;
	if(n.op != Oind){
		ecom(n.src, reg = talloc(n.ty, t), n);
		reg.src = n.src;
		return (reg, reg);
	}

	if(left.op == Oadd && left.right.op == Oconst){
		if(left.left.op == Oadr){
			(left.left.left, reg) = eacom(left.left.left, t);
			sumark(n);
			if(n.addable >= Rcant)
				fatal("eacom can't make node addressable: "+nodeconv(n));
			return (n, reg);
		}
		reg = talloc(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){
		reg = talloc(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, reg);
	}else{
		reg = talloc(left.ty, t);
		ecom(left.src, reg, left);
		n.left = reg;
		n.addable = Radr;
	}
	return (n, reg);
}

#
# compile an assignment to an array slice
#
slicelcom(src: Src, nto, n: ref Node): ref Node
{
	tleft, tright, tv: ref Node;

	left := n.left.left;
	right := n.left.right.left;
	v := n.right;
	if(right.addable < Ralways){
		if(left.addable >= Rcant)
			(left, tleft) = eacom(left, nto);
	}else if(left.temps <= right.temps){
		right = ecom(right.src, tright = talloc(right.ty, nto), right);
		if(left.addable >= Rcant)
			(left, tleft) = eacom(left, nil);
	}else{
		(left, tleft) = eacom(left, nil);		# dangle on right and v
		right = ecom(right.src, tright = talloc(right.ty, nil), right);
	}

	case n.op{
	Oas =>
		if(v.addable >= Rcant)
			(v, tv) = eacom(v, nil);
	}

	genrawop(n.src, ISLICELA, v, right, left);
	if(nto != nil)
		genmove(src, Mas, n.ty, left, nto);
	tfree(tleft);
	tfree(tv);
	tfree(tright);
	return nto;
}

#
# compile an assignment to a string location
#
indsascom(src: Src, nto, n: ref Node): ref Node
{
	tleft, tright, tv, tu, u: ref Node;

	left := n.left.left;
	right := n.left.right;
	v := n.right;
	if(right.addable < Ralways){
		if(left.addable >= Rcant)
			(left, tleft) = eacom(left, nto);
	}else if(left.temps <= right.temps){
		right = ecom(right.src, tright = talloc(right.ty, nto), right);
		if(left.addable >= Rcant)
			(left, tleft) = eacom(left, nil);
	}else{
		(left, tleft) = eacom(left, nil);		# dangle on right and v
		right = ecom(right.src, tright = talloc(right.ty, nil), right);
	}

	case n.op{
	Oas =>
		if(v.addable >= Rcant)
			(v, tv) = eacom(v, nil);
	Oinc or
	Odec =>
		if(v.addable >= Rcant)
			fatal("inc/dec amount not addable");
		u = tu = talloc(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;
	Oaddas or
	Osubas or
	Omulas or
	Odivas or
	Omodas or
	Oexpas or
	Oandas or
	Ooras or
	Oxoras or
	Olshas or
	Orshas =>
		if(v.addable >= Rcant)
			(v, tv) = eacom(v, nil);
		u = tu = talloc(tint, nil);
		genop(n.left.src, Oinds, left, right, u);
		genop(n.src, n.op, v, nil, u);
		v = u;
	}

	genrawop(n.src, IINSC, v, right, left);
	tfree(tleft);
	tfree(tv);
	tfree(tright);
	tfree(tu);
	if(nto != nil)
		genmove(src, Mas, n.ty, v, nto);
	return nto;
}

callcom(src: Src, op: int, n, ret: ref Node)
{
	tmod, tind: ref Node;
	callee: ref Decl;

	args := n.right;
	nfn := n.left;
	case(nfn.op){
		Odot =>
			callee = nfn.right.decl;
			nfn.addable = Rpc;
		Omdot =>
			callee = nfn.right.decl;
		Oname =>
			callee = nfn.decl;
		* =>
			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, tmod = talloc(nfn.ty.tof, nil), n);
		tfree(tmod);
		return;
	}
	if(ispoly(callee))
		addfnptrs(callee, 0);
	if(nfn.ty.varargs != byte 0){
		d := dupdecl(nfn.right.decl);
		nfn.decl = d;
		d.desc = gendesc(d, idoffsets(nfn.ty.ids, MaxTemp, MaxAlign), nfn.ty.ids);
	}

	frame := talloc(tint, nil);

	mod := nfn.left;
	ind := nfn.right;
	if(nfn.addable == Rmpc){
		if(mod.addable >= Rcant)
			(mod, tmod) = eacom(mod, nil);		# dangle always
		if(ind.op != Oname && ind.addable >= Ralways){
			tind = talloc(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 := talloc(a.left.ty, nil);
			ecom(a.left.src, tn, a.left);
			a.left = tn;
			tn.flags |= byte TEMP;
		}
	}

	#
	# allocate the frame
	#
	if(nfn.addable == Rmpc && nfn.ty.varargs == byte 0){
		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 := ref znode;
	tadd := ref znode;
	pass := ref znode;
	toff.op = Oconst;
	toff.c = ref Const(big 0, 0.0);	# jrf - added initialization
	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.c.val = big 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(int a.left.flags & TEMP)
			tfree(a.left);
	}
	if(off > maxstack)
		maxstack = off;

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

	#
	# call it
	#
	iop: int;
	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)
#
arraycom(a, elems: ref Node)
{
	top, out: ref Inst;
	ri, n, wild: ref Node;

	if(debug['A'])
		print("arraycom: %s %s\n", nodeconv(a), nodeconv(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 := ref znode;
	fake := ref znode;
	tmp := talloc(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 = nil;
			if(q.left.op == Orange){
				#
				# for(i := q.left.left; i <= q.left.right; i++)
				#
				ri = talloc(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, big 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;
#	}
#
arraydefault(a, elem: ref Node)
{
	e: ref Node;

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

	t := mkn(Olen, a, nil);
	t.src = elem.src;
	t.ty = tint;
	t.addable = Rcant;
	n := talloc(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, big 1)));
	t.ty = tint;
	t.addable = Rcant;
	ecom(t.src, nil, t);

	if(elem.addable >= Rcant)
		(elem, e) = eacom(elem, 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());
}

tupcom(nto, n: ref Node)
{
	if(debug['Y'])
		print("tupcom %s\nto %s\n", nodeconv(n), nodeconv(nto));

	#
	# build a fake node for the tuple
	#
	toff := ref znode;
	tadd := ref znode;
	fake := ref znode;
	tadr := ref znode;
	toff.op = Oconst;
	toff.c = ref Const(big 0, 0.0);	# no val => may get fatal error below (jrf)
	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 "+nodeconv(fake));

	#
	# compile all the exps
	#
	d := n.ty.ids;
	for(e := n.left; e != nil; e = e.right){
		toff.c.val = big d.offset;
		fake.ty = d.ty;
		ecom(e.left.src, fake, e.left);
		d = d.next;
	}
}

tuplcom(n, nto: ref Node)
{
	if(debug['Y'])
		print("tuplcom %s\nto %s\n", nodeconv(n), nodeconv(nto));

	#
	# build a fake node for the tuple
	#
	toff := ref znode;
	tadd := ref znode;
	fake := ref znode;
	tadr := ref znode;
	toff.op = Oconst;
	toff.c = ref Const(big 0, 0.0);	# no val => may get fatal error below (jrf)
	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 "+nodeconv(fake));

	#
	# compile all the exps
	#
	tas := ref znode;
	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.c.val = big 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(as.src, nil, tas);
			}
		}
		d = d.next;
	}
}

tuplrcom(n: ref Node, nto: ref Node)
{
	s, d, tas: ref Node;
	de: ref Decl;

	tas = ref znode;
	de = nto.ty.ids;
	for((s, d) = (n.left, nto.left); s != nil && d != nil; (s, d) = (s.right, 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
#
bcom(n: ref Node, iftrue: int, b: ref Inst): ref Inst
{
	tleft, tright: ref Node;

	if(n.op == Ocomma){
		tn := n.left.left;
		ecom(n.left.src, nil, n.left);
		b = bcom(n.right, iftrue, b);
		tfree(tn);
		return b;
	}

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

	left := n.left;
	right := n.right;
	op := n.op;
	case op{
	Onothing =>
		return b;
	Onot =>
		return bcom(n.left, !iftrue, b);
	Oandand =>
		if(!iftrue)
			return oror(n, iftrue, b);
		return andand(n, iftrue, b);
	Ooror =>
		if(!iftrue)
			return andand(n, iftrue, b);
		return oror(n, iftrue, b);
	Ogt or
	Ogeq or
	Oneq or
	Oeq or
	Olt or
	Oleq =>
		break;
	* =>
		if(n.ty.kind == Tint){
			right = mkconst(n.src, big 0);
			right.addable = Rconst;
			left = n;
			op = Oneq;
			break;
		}
		fatal("can't bcom "+nodeconv(n));
		return b;
	}

	if(iftrue)
		op = oprelinvert[op];

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

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

andand(n: ref Node, iftrue: int, b: ref Inst): ref Inst
{
	if(debug['b'])
		print("andand %s\n", nodeconv(n));
	b = bcom(n.left, iftrue, b);
	b = bcom(n.right, iftrue, b);
	return b;
}

oror(n: ref Node, iftrue: int, b: ref Inst): ref Inst
{
	if(debug['b'])
		print("oror %s\n", nodeconv(n));
	bb := bcom(n.left, !iftrue, nil);
	b = bcom(n.right, iftrue, b);
	patch(bb, nextinst());
	return b;
}

#
# generate code for a recva expression
# this is just a hacked up small alt
#
recvacom(src: Src, nto, n: ref Node)
{
	p: ref Inst;

	left := n.left;

	labs := array[1] of Label;
	labs[0].isptr = left.addable >= Rcant;
	c := ref Case;
	c.nlab = 1;
	c.nsnd = 0;
	c.offset = 0;
	c.labs = labs;
	talt := mktalt(c);

	which := talloc(tint, nil);
	tab := talloc(talt, nil);

	#
	# build the node for the address of each channel,
	# the values to send, and the storage for values received
	#
	off := ref znode;
	adr := ref znode;
	add := ref znode;
	slot := ref znode;
	off.op = Oconst;
	off.c = ref Const(big 0, 0.0);		# jrf - added initialization
	off.ty = tint;
	off.addable = Rconst;
	adr.op = Oadr;
	adr.left = tab;
	adr.ty = tint;
	add.op = Oadd;
	add.left = adr;
	add.right = off;
	add.ty = tint;
	slot.op = Oind;
	slot.left = add;
	sumark(slot);

	#
	# gen the channel
	# this sleaze is lying to the garbage collector
	#
	off.c.val = big(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.c.val += big IBY2WD;
	p = genrawop(left.src, ILEA, nto, nil, slot);
	p.m.offset = nto.ty.size;	# for optimizer

	#
	# number of senders and receivers
	#
	off.c.val = big 0;
	genmove(src, Mas, tint, sumark(mkconst(src, big 0)), slot);
	off.c.val += big IBY2WD;
	genmove(src, Mas, tint, sumark(mkconst(src, big 1)), slot);
	off.c.val += big 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)
#
pickdupcom(src: Src, nto, n: ref Node)
{
	jmps: ref Inst;

	if(n.op != Oind)
		fatal("pickdupcom not Oind: " + nodeconv(n));

	t := n.ty;
	nlab := t.decl.tag;

	#
	# generate global which has case labels
	#
	d := mkids(src, enter(".c"+string nlabel++, 0), mktype(src.start, src.stop, Tcase, nil, nil), nil);
	d.init = mkdeclname(src, d);

	clab := ref znode;
	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;
	tmp := talloc(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 := array[nlab] of Label;

	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 := ref Case;
	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
#
tupaliased(n, e: ref Node): int
{
	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;
	}
	return 0;
}

#
# see if any name in n occurs anywere in e
#
tupsaliased(n, e: ref Node): int
{
	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;
	}
	return 0;
}

#
# put unaddressable constants in the global data area
#
globalconst(n: ref Node): ref Decl
{
	s := enter(".i." + hex(int n.c.val, 8), 0);
	d := s.decl;
	if(d == nil){
		d = mkids(n.src, s, tint, nil);
		installids(Dglobal, d);
		d.init = n;
		d.refs++;
	}
	return d;
}

globalBconst(n: ref Node): ref Decl
{
	s := enter(".B." + bhex(n.c.val, 16), 0);
	d := s.decl;
	if(d == nil){
		d = mkids(n.src, s, tbig, nil);
		installids(Dglobal, d);
		d.init = n;
		d.refs++;
	}
	return d;
}

globalbconst(n: ref Node): ref Decl
{
	s := enter(".b." + hex(int n.c.val & 16rff, 2), 0);
	d := s.decl;
	if(d == nil){
		d = mkids(n.src, s, tbyte, nil);
		installids(Dglobal, d);
		d.init = n;
		d.refs++;
	}
	return d;
}

globalfconst(n: ref Node): ref Decl
{
	ba := array[8] of byte;
	export_real(ba, array[] of {n.c.rval});
	fs := ".f.";
	for(i := 0; i < 8; i++)
		fs += hex(int ba[i], 2);
	if(fs != ".f." + bhex(math->realbits64(n.c.rval), 16))
		fatal("bad globalfconst number");
	s := enter(fs, 0);
	d := s.decl;
	if(d == nil){
		d = mkids(n.src, s, treal, nil);
		installids(Dglobal, d);
		d.init = n;
		d.refs++;
	}
	return d;
}

globalsconst(n: ref Node): ref Decl
{
	s := n.decl.sym;
	n.decl = nil;
	d := s.decl;
	if(d == nil){
		d = mkids(n.src, s, tstring, nil);
		installids(Dglobal, d);
		d.init = n;
	}
	d.refs++;
	n.decl = d;
	return d;
}

#
# make a global of type t
# used to make initialized data
#
globalztup(t: ref Type): ref Decl
{
	z := ".z." + string t.size + ".";
	desc := t.decl.desc;
	for(i := 0; i < desc.nmap; i++)
		z += hex(int desc.map[i], 2);
	s := enter(z, 0);
	d := s.decl;
	if(d == nil){
		d = mkids(t.src, s, t, nil);
		installids(Dglobal, d);
		d.init = nil;
	}
	d.refs++;
	return d;
}

subst(d: ref Decl, e: ref Node, n: ref Node): ref Node
{
	if(n == nil)
		return nil;
	if(n.op == Oname){
		if(d == n.decl){
			n = dupn(0, nosrc, e);
			n.ty = d.ty;
		}
		return n;
	}
	n.left = subst(d, e, n.left);
	n.right = subst(d, e, n.right);
	return n;
}

inline(n: ref Node): ref Node
{
	e, tn: ref Node;
	t: ref Type;
	d: ref Decl;

if(debug['z']) sys->print("inline1: %s\n", nodeconv(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);
	n = n.right;
	for(d = t.ids; d != nil && n != nil; d = d.next){
		if(hasside(n.left, 0) && occurs(d, e) != 1){
			tn = talloc(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);
		n = n.right;
	}
	if(d != nil || n != nil)
		fatal("bad arg match in inline()");
if(debug['z']) sys->print("inline2: %s\n", nodeconv(e));
	return e;
}

fpcall(src: Src, op: int, n: ref Node, ret: ref Node)
{
	tp, e, mod, ind: ref Node;

	e = n.left.left;
	if(e.addable >= Rcant)
		(e, tp) = eacom(e, nil);
	mod = mkunary(Oind, e);
	ind = mkunary(Oind, mkbin(Oadd, dupn(0, src, e), mkconst(src, big 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);
}