#define EXTERN

#include "cc.h"

/*
 *	locals, parameters, globals etc of the same name should work ok without having
 *	to duplicate Syms because the details are on the containing Nodes
 */

#define SZ_CHAR	1
#define SZ_SHORT	2
#define SZ_INT	4
#define SZ_LONG	4
#define SZ_FLOAT	4
#define SZ_IND	4
#define SZ_VLONG	8
#define SZ_DOUBLE	8

char buf[128], mbuf[128];
static Sym *sysop, *bioop, *libcop;
static int again;

#define	STAR	0x80
#define	RET		0x80

#define	LARR	(-1729)

static void swalk(void);
static int isdec(Node*);
static int isconst(Node*, vlong);
static int cktype(Node*, Node*, int, int);
static void addnode(int, Node*);
static int argpos(Node*, Node*);
static void setdec(Sym*, Type*);
static Type* tcp(Type*);
static int isadt(Type*);
static void aargs(Node*);
static int iteq(Type*, Type*);
static Node* arg(Node*, int);
static void etgen2(Sym*);
static Node* ckneg(Node*);
static Sym* suename(Type*);
static int isnil(Node*);
static void sliceasgn(Node*);
static Node* lastn(Node*);
static char* hasm(void);
static void prn(Node*, int);
static int isfn(Type*);

schar	ewidth[NTYPE] =
{
	-1,		/* [TXXX] */
	SZ_CHAR,	/* [TCHAR] */
	SZ_CHAR,	/* [TUCHAR] */
	SZ_SHORT,	/* [TSHORT] */
	SZ_SHORT,	/* [TUSHORT] */
	SZ_INT,		/* [TINT] */
	SZ_INT,		/* [TUINT] */
	SZ_LONG,	/* [TLONG] */
	SZ_LONG,	/* [TULONG] */
	SZ_VLONG,	/* [TVLONG] */
	SZ_VLONG,	/* [TUVLONG] */
	SZ_FLOAT,	/* [TFLOAT] */
	SZ_DOUBLE,	/* [TDOUBLE] */
	SZ_IND,		/* [TIND] */
	0,		/* [TFUNC] */
	-1,		/* [TARRAY] */
	0,		/* [TVOID] */
	-1,		/* [TSTRUCT] */
	-1,		/* [TUNION] */
	SZ_INT,		/* [TENUM] */
};

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

enum{
	TCFD = 1,
	TCFC = 2,
	TCPC = 4,
	TCAR = 8,
	TCIN = 16,
	TCGEN = TCFD|TCFC|TCPC|TCAR,
	TCALL = TCFD|TCFC|TCPC|TCAR|TCIN,
};

enum{
	SGLOB,
	SPARM,
	SAUTO,
};

typedef struct Scope Scope;

struct Scope{
	Node *n;
	int	k;
	Scope *nxt;
};

static void
prtyp(Type *t, char *s, int nl)
{
	print("%s: ", s);
	if(t == T){
		print("nil");
		if(nl)
			print("\n");
		return;
	}
	while(t != T){
		print("%d(%d)[%x] ", t->etype, t->mark, (int)t);
		if(isadt(t))
			break;
		t = t->link;
	}
	if(nl)
		print("\n");
}

static Node*
func(Node *n)
{
	while(n != Z && n->op != OFUNC)
		n = n->left;
	return n;
}

static void
setmain(Node *n)
{
	inmain |= n->left->op == ONAME && strcmp(n->left->sym->name, "main") == 0;
}

static Node*
protoname(Node *n)
{
	do
		n = n->left;
	while(n != Z && n->op != ONAME && n->op != ODOTDOT);
	return n;
}

static Type*
prototype(Node *n, Type *t)
{
	for( ; n != Z ; n = n->left){
		switch(n->op){
		case OARRAY:
			t = typ(TARRAY, t);
			t->width = 0;
			break;
		case OIND:
			t = typ(TIND, t);
			break;
		case OFUNC:
			t = typ(TFUNC, t);
			t->down = fnproto(n);
			break;
		}
	}
	return t;
}

static Scope *scopes, *freescopes;

static void
pushdcl(Node *n, int c)
{
	Sym *s;

	if(passes){
		s = n->sym;
		push1(s);
		if(c != CAUTO || s->class != CSTATIC)
			s->class = c;
		s->type = n->type;
	}
}

static void
pushparams(Node *n)
{
	if(n == Z)
		return;
	if(passes){
		if(n->op == OLIST){
			pushparams(n->left);
			pushparams(n->right);
		}
		else if(n->op == OPROTO){
			n = protoname(n);
			if(n != Z && n->op == ONAME)
				pushdcl(n, CPARAM);
		}
		else if(n->op == ONAME){
			addnode(OPROTO, n);
			pushdcl(n, CPARAM);
		}
		else if(n->op != ODOTDOT)
			diag(Z, "bad op in pushparams");
	}
}

static void
pushscope(Node *n, int k)
{
	Scope *s;

	if(freescopes != nil){
		s = freescopes;
		freescopes = freescopes->nxt;
	}
	else
		s = (Scope*)malloc(sizeof(Scope));
	s->n = n;
	s->k = k;
	s->nxt = scopes;
	scopes = s;
	if(passes && (k == SPARM || k == SAUTO))
		markdcl();
	if(k == SPARM)
		pushparams(n->right);
}

static void
popscope(void)
{
	int k;
	Scope *s;

	s = scopes;
	k = s->k;
	scopes = scopes->nxt;
	s->nxt = freescopes;
	freescopes = s;
	if(passes && (k == SPARM || k == SAUTO))
		revertdcl();
}

static Node*
curfn(void)
{
	Scope *s;

	for(s = scopes; s != nil; s = s->nxt)
		if(s->k == SPARM)
			return s->n;
	return Z;
}

static void
marktype(Type *t, int tc)
{
	t->mark = tc;
}

static int
marked(Type *t)
{
	return t == T ? 0 :  t->mark;
}

static Sym*
decsym(Node *n)
{
	if(n == Z)
		return S;
	if(n->op == OFUNC){
		if(n->left->op == ONAME)
			return n->left->sym;
		return S;
	}
	if(n->op == ODAS)
		return n->left->sym;
	return n->sym;
}

static void
trep(Type *t1, Type *t)
{
	int l;
	Sym *s;
	Type *t2;

	if(t1 != T){
		l = t1->lineno;
		s = t1->sym;
		t2 = t1->down;
		*t1 = *t;
		t1->down = t2;
		t1->sym = s;
		t1->lineno = l;
	}
}

static void
tind(Node *n)
{
	if(n == Z)
		return;
	n = protoname(n);
	if(n != Z && n->type != T){
		n->type = tcp(n->type->link);
		marktype(n->type, TCIN);
	}
}

static void
tcon(Node *n, Type *t)
{
	Type *tt;

	if(n->garb)
		return;
	n->garb = 1;
	again = 1;
	switch(n->op){
		case OCONST:
			if(t->mark == TCFD && !isnil(n))
				addnode(OFILDES, n);
			n->type = t;
			break;
		case OCAST:
			tcon(n->left, t);
			*n = *n->left;
			n->type = t;
			break;
		case ONAME:
			n->sym->type = t;
			n->type = t;
			setdec(n->sym, t);
			break;
		case ODOT:
		case ODOTIND:
			trep(n->type, t);
			n->type = t;
			break;
		case OARRIND:
			tt = n->left->type;
			if(tt != T)
				tt->link = t;
			n->type = t;
			break;
		case OFUNC:
			n->left->type->link = t;
			if(n->left->op == ONAME)
				n->left->sym->type->link = t;
			n->type = t;
			break;
	}
}

static Node*
retval(Node *n)
{
	int i;
	Type *t;
	Node *a, *l, *cf;

	cf = curfn();
	t = cf->left->type->link;
	if(t->mark&(TCPC|TCFC) && (n == Z || !(n->type->mark&(TCPC|TCFC)))){
		if(n == Z)
			n = new1(ORETURN, Z, Z);
		l = n->left;
		for(i = 0; ; i++){
			a = arg(cf->right, i);
			if(a == Z)
				break;
			a = protoname(a);
			if(a == Z || a->op != ONAME)
				break;
			if(a->type->mark == TCIN){
				if(l == Z)
					l = ncopy(a);
				else
					l = new1(OTUPLE, l, ncopy(a));
			}
		}
		n->left = l;
		n->type = l->type = t;
	}
	return n;
}

static void
sube(Node *n)
{
	Node *l, *r, *nn;
	Type *tt;
	static Node *gn;
	int p;

	if(n == Z)
		return;
	l = n->left;
	r = n->right;
	switch(n->op){
		default:
			sube(l);
			sube(r);
			break;
		case OIND:
			if(l == Z)
				return;
			tt = l->type;
			sube(l);
			if(cktype(l, n, TCIN, 0) && iteq(tt, l->type))
				*n = *n->left;
			break;
		case OARRIND:
			tt = l->type;
			sube(l);
			sube(r);
			if(!isconst(r, 0))
				break;
			if(cktype(l, n, TCIN, 0) && iteq(tt, l->type))
				*n = *n->left;
			break;
		case ONAME:
			if(cktype(n, n, TCALL, 0))
				setdec(n->sym, n->type);
			break;
		case OCAST:
			sube(l);
			if(cktype(l, n, TCALL, 0))
				n->type = l->type;
			break;
		case OPROTO:
			sube(l);
			sube(r);
			nn = protoname(n);
			if(nn != Z && cktype(nn, n, TCALL, 0)){
				n->type = nn->type;
				p = argpos(n, gn->right);
				for(tt = gn->left->type->down; tt != T && p >= 0; tt = tt->down){
					if(p == 0){
						trep(tt, nn->type);
						break;
					}
					--p;
				}
			}
			break;
		case OFUNC:
			if(n->kind == KEXP)
				aargs(n);
			if(n->left->op == ONAME)
				gn = n;
			sube(l);
			sube(r);
			if(l != Z && cktype(n, n, TCGEN, 0))
				l->type->link = n->type;
			break;
		case OAS:
			sube(l);
			sube(r);
			if(r->op == ORETV){
				n->left = new1(OTUPLE, l, r->right);
				n->right = r->left;
				n->left->type = n->type;
				break;
			}
			if(cktype(r, n, TCGEN, 0)){
				tcon(l, r->type);
				n->type = r->type;
			}
			if(cktype(l, n, TCGEN, 1)){
				tcon(r, l->type);
				n->type = l->type;
			}
			break;
		case OLT:
		case OGE:
			sube(l);
			sube(r);
			if(cktype(l, n, TCFD, 0) && isconst(r, 0)){
				n->op = n->op == OLT ? OEQ : ONE;
				r->op = ONIL;
				r->type = l->type;
			}
			break;
		case OGT:
		case OLE:
			sube(l);
			sube(r);
			if(cktype(r, n, TCFD, 0) && isconst(l, 0)){
				n->op = n->op == OGT ? OEQ : ONE;
				l->op = ONIL;
				l->type = r->type;
			}
			break;
	}
}

static void
subs(Node *n, int blk, int aut)
{
	Node *l, *r;

	if(n == Z)
		return;
	if(blk)
		pushscope(n, SAUTO);
	nearln = n->lineno;
	l = n->left;
	r = n->right;
	switch(n->op){
		default:
			sube(n);
			break;
		case ONAME:
			if(aut && n->kind != KEXP)
				pushdcl(n, CAUTO);
			if(cktype(n, n, TCALL, 0))
				setdec(n->sym, n->type);
			break;
		case ODAS:
			if(aut)
				pushdcl(l, CAUTO);
			subs(l, 0, aut);
			if(cktype(l, n, TCALL, 0))
				tcon(r, l->type);
			break;
		case OSBREAK:
		case ONUL:
		case OLABEL:
		case OGOTO:
		case OCONTINUE:
		case OBREAK:
		case OSET:
		case OUSED:
			break;
		case OBLK:
			subs(l, 1, aut);
			break;
		case OCASE:
			subs(r, 1, aut);
			break;
		case OLIST:
			subs(l, 0, aut);
			subs(r, 0, aut);
			break;
		case ORETURN:
			sube(l);
			if(l != Z && cktype(l, n, TCGEN, 0)){
				n->type = l->type;
				tcon(curfn(), l->type);
			}
			retval(n);
			break;
		case OSWITCH:
		case OWHILE:
		case ODWHILE:
			sube(l);
			subs(r, 1, aut);
			break;
		case OIF:
			sube(l);
			subs(r->left, 1, aut);
			subs(r->right, 1, aut);
			break;
		case OFOR:
			sube(l->left);
			sube(l->right->left);
			sube(l->right->right);
			subs(r, 1, aut);
			break;
	}
	if(blk)
		popscope();
}

static Node*
finddec0(Sym *s, Node *n)
{
	Node *nn;

	if(n == Z)
		return ZZ;
	switch(n->op){
		case OLIST:
			nn = finddec0(s, n->left);
			if(nn != Z)
				return nn;
			return finddec0(s, n->right);
		case OFUNC:
			if(n->op != KEXP){
				if(s == decsym(n))
					return n;
				return finddec0(s, n->right);
			}
			else
				return ZZ;
		case OPROTO:
		case OIND:
		case OARRAY:
			return finddec0(s, n->left);
		case ODOTDOT:
			return ZZ;
		case ONOOP:
		case OPUSH:
		case OPOP:
		case OCODE:
		case ODECE:
		case ODECT:
			return finddec0(s, n->right);
		case ODECV:
		case ODECF:
			if(s == decsym(n->left) && !isfn(n->left->type))
				return n->left;
			return finddec0(s, n->right);
	}
	if(isdec(n)){
		if(s == decsym(n) && !isfn(n->type))
			return n;
		return Z;
	}
	return ZZ;
}

static Node*
finddec(Sym *s, int g)
{
	Node *n;
	Scope *sc;

	for(sc = scopes; sc != nil; sc = sc->nxt){
		if(!g || sc->k == SGLOB){
			n = finddec0(s, sc->n);
			if(n != Z && n != ZZ)
				return n;
		}
	}
	return Z;	
}

static void
setdec(Sym *s, Type *t)
{
	Node *n;

	if((n = finddec(s, 0)) != Z){
		n->type = t;
		if(n->op == ODAS){
			n = n->left;
			n->type = t;
		}
		n->sym->type = t;
	}
}
		
typedef struct Syml Syml;

struct Syml{
	Sym *sym;
	Syml *nxt;
};

typedef struct Symq Symq;

struct Symq{
	Syml *f;
	Syml *r;
};

typedef struct Modl Modl;

struct Modl{
	char *mod;
	int	ld;
	Modl *nxt;
};

static void
prn(Node *n, int i)
{
	int j;

	for(j = 0; j < i; j++)
		print("\t");
	if(n == Z){
		print("Z\n");
		return;
	}
	print("%s", onames[n->op]);
	if(n->blk)
		print("	block");
	if(n->type == T)
		print("	T");
	else
		print("	%s", tnames[n->type->etype]);
	if(n->op == OCONST)
		print("	%d", (int)n->vconst);
	else if(n->op == OSTRING)
		print("	%s", n->cstring);
	else if(n->op == ONAME)
		print("	%s", n->sym->name);
	print("\n");
	if(n->op != OLIST)
		i++;
	prn(n->left, i);
	prn(n->right, i);
}

static int
isbigv(vlong v)
{
	return v > 0xffffffff;
}

static int
islbigv(vlong v)
{
	return v > 0x7fffffff || v < -0x7fffffff;
}

static int
isuintv(vlong v)
{
	return !isbigv(v) && (v&0x80000000) != 0;
}

static int
isadt(Type *t)
{
	return t != T && (t->etype == TSTRUCT || t->etype == TUNION);
}

static int
isreal(Type *t)
{
	return t != T && (t->etype == TDOUBLE || t->etype == TFLOAT);
}

static int
isbyte(Type *t)
{
	return t != T && (t->etype == TCHAR || t->etype == TUCHAR);
}

static int
isshort(Type *t)
{
	return t != T && (t->etype == TSHORT || t->etype == TUSHORT);
}

static int
isint(Type *t)
{
	return t != T && (t->etype == TINT || t->etype == TUINT);
}

static int
islong(Type *t)
{
	return t != T && (t->etype == TLONG || t->etype == TULONG);
}

static int
isbig(Type *t)
{
	return t != T && (t->etype == TVLONG || t->etype == TUVLONG);
}

static int
isinteger(Type *t)
{
	return isbyte(t) || isshort(t) || isint(t) || islong(t) || isbig(t);
}

static int
isptr(Type *t)
{
	return t != T && (t->etype == TIND || t->etype == TARRAY || t->etype == TFUNC);
}

static int
isscalar(Type *t)
{
	return t != T && !isadt(t) && t->etype != TTUPLE;
}

static int
isvoid(Type *t)
{
	return t == T || t->etype == TVOID;
}

static int
isnum(Type *t)
{
	return t != T && isscalar(t) && !isptr(t) && !isvoid(t);
}

static int
isarray(Type *t)
{
	return t != T && (t->etype == TARRAY || (t->etype == TIND && !isadt(t->link)));
}

static int
isstr(Type *t)
{
	return t != T && (t->etype == TSTRING || isarray(t) && isbyte(t->link));
}

static int
isfn(Type *t)
{
	return t != T && t->etype == TFUNC;
}

static int
iscastable(Type *t, Type *tt)
{
	return t != T && (!isptr(t) || isarray(t) && isbyte(t->link) && isstr(tt));
}

static int
isname(Node *n)
{
	return n->op == ONAME;
}

static int
isstring(Node *n)
{
	return n->op == OSTRING || n->op == OLSTRING || n->op == ONAME && n->sym->tenum != T && n->sym->tenum->etype == TIND;
}

static int
isnil(Node *n)
{
	if(!isptr(n->type))
		return 0;
	while(n->op == OCAST)
		n = n->left;
	return n->op == OCONST && n->vconst == 0 || n->op == ONIL;
}

static int
isconst(Node *n, vlong v)
{
	while(n->op == OCAST)
		n = n->left;
	return n->op == OCONST && n->vconst == v;
}

static Node*
cknil(Node *n)
{
	if(isconst(n, 0))
		n->op = ONIL;
	return n;
}

static int
cktype(Node *n, Node *t, int mask, int lev)
{
	int g, m, m0;

	g = t->garb > lev;
	m = marked(n->type) & mask;
	if(n->op == ONAME){
		m0 = marked(n->sym->type) & mask;
		if(m && !m0){
			n->sym->type = n->type;
			if(!g)
				again = 1;
		}
		if(!m && m0){
			n->type = n->sym->type;
			if(!g)
				again = 1;
		}
		m |= m0;
	}
	if(m && t->garb < 2)
		t->garb++;
	return m && !g ? m : 0;
}

int
isconsym(Sym *s)
{
	switch(s->class){
		case CXXX:
		case CTYPEDEF:
			return 1;
		case CEXTERN:
		case	CGLOBL:
		case CSTATIC:
		case CLOCAL:
			return s->type != T && s->type->etype == TENUM;
	}
	return -1;
}

static void genstart(void);

static char*
mprolog[] =
{
	"%%: module",
	"{",
	"\tPATH: con \"%%%.dis\";",
	"",
	nil
};

static char*
mepilog[] =
{
	"};",
	nil
};

static char*
bprolog[] =
{
	"implement %%;",
	"",
	"include \"draw.m\";",
	"",
	"%%: module",
	"{",
	"	init: fn(nil: ref Draw->Context, argl: list of string);",
	"};",
	"",
	nil
};

static char*
bmprolog[] =
{
	"implement %%;",
	"",
	"include \"draw.m\";",
	"",
	nil
};

static char*
bepilog[] =
{
	nil
};

static void
pgen0(char **txt)
{
	int sub;
	char *b, *s, *t, **p;

	p = txt;
	for(;;){
		s = *p++;
		if(s == nil)
			break;
		sub = 0;
		for(t = s; *t != 0; t++){
			if(*t == '%' && *(t+1) == '%'){
				sub = 1;
				break;
			}
		}
		if(sub){
			strcpy(buf, s);
			b = buf;
			for(t = s; *t != 0; t++){
				if(*t == '%' && *(t+1) == '%'){
					if(*(t+2) == '%'){
						outmod(mbuf, 0);
						t++;
					}
					else
						outmod(mbuf, 1);
					strcpy(b, mbuf);
					b += strlen(mbuf);
					t++;
				}
				else
					*b++ = *t;
			}
			*b = 0;
			prline(buf);
		}
		else			
			prline(s);
	}
}

static char*
hasm()
{
	outmod(mbuf, 0);
	strcat(mbuf, ".m");
	if(exists(mbuf))
		return mbuf;
	else if(domod){
		outmod(buf, 0);
		strcat(buf, ".h");
		if(exists(buf))
			return mbuf;
	}
	return nil;
}

void
pgen(int b)
{
	char **p;

	if(!dolog())
		return;
	if(b)
		p = hasm() ? bmprolog : bprolog;
	else
		p = mprolog;
	pgen0(p);
	if(b && passes)
		genstart();
	if(!b)
		incind();
}

void
epgen(int b)
{
	char **p;

	/* output(0x7fffffff, 1); */	/* INFINITY */
	if(!dolog())
		return;
	if(b){
		if(!passes)
			genstart();
		p = bepilog;
	}
	else
		p = mepilog;
	if(!b)
		decind();
	pgen0(p);
}

static int lastsec = 0;

#define ASSOC		1
#define RASSOC	2
#define POSTOP	4

#define LEFT	1
#define RIGHT	2
#define PRE	4
#define POST	8

static int space[] = { 0, 0, 2, 0, 4, 5, 0, 0, 0, 9, 10, 0, 0, 0, 0, 0, 0, 0, 0 };

static struct{
	char *name;
	int	prec;
	int	kind;
} ops[] = {
	"",		0,	0,	/* ONOOP */
	"",		16,	0,	/* OXXX, */
	"+",		12,	ASSOC,	/* OADD, */
	"&",		14,	RASSOC,	/* OADDR, */
	"&",		8,	ASSOC,	/* OAND, */
	"&&",	5,	ASSOC,	/* OANDAND, */
	"",		16,	0,	/* OARRAY, */
	"=",		2,	RASSOC,	/* OAS, */
	"=",		2,	RASSOC,	/* OASI, */
	"+=",		2,	RASSOC,	/* OASADD, */
	"&=",		2,	RASSOC,	/* OASAND, */
	"<<=",	2,	RASSOC,	/* OASASHL, */
	">>=",	2,	RASSOC,	/* OASASHR, */
	"/=",		2,	RASSOC,	/* OASDIV, */
	"<<",		11,	0,	/* OASHL, */
	">>",		11,	0,	/* OASHR, */
	"/=",		2,	RASSOC,	/* OASLDIV, */
	"%=",		2,	RASSOC,	/* OASLMOD, */
	"*=",		2,	RASSOC,	/* OASLMUL, */
	">>=",	2,	RASSOC,	/* OASLSHR, */
	"%=",		2,	RASSOC,	/* OASMOD, */
	"*=",		2,	RASSOC,	/* OASMUL, */
	"|=",		2,	RASSOC,	/* OASOR, */
	"-=",		2,	RASSOC,	/* OASSUB, */
	"^=",		2,	RASSOC,	/* OASXOR, */
	"",		-1,	0,	/* OBIT, */
	"",		-1,	0,	/* OBREAK, */
	"",		-1,	0,	/* OCASE, */
	"",		14,	RASSOC,	/* OCAST, */
	"",		1,	ASSOC,	/* OCOMMA, */
	"",		3,	RASSOC,	/* OCOND, */
	"",		16,	0,	/* OCONST, */
	"",		-1,	0,	/* OCONTINUE, */
	"/",		13,	0,	/* ODIV, */
	".",		15,	0,	/* ODOT, */
	"...",		16,	0,	/* ODOTDOT, */
	"",		-1,	0,	/* ODWHILE, */
	"",		-1,	0,	/* OENUM, */
	"==",		9,	0,	/* OEQ, */
	"",		-1,	0,	/* OFOR, */
	"",		15,	0,	/* OFUNC, */
	">=",		10,	0,	/* OGE, */
	"",		-1,	0,	/* OGOTO, */
	">",		10,	0,	/* OGT, */
	">",		10,	0,	/* OHI, */
	">=",		10,	0,	/* OHS, */
	"",		-1,	0,	/* OIF, */
	"*",		14,	RASSOC,	/* OIND, */
	"",		-1,	0,	/* OINDREG, */
	"",		16,	0,	/* OINIT, */
	"",		-1,	0,	/* OLABEL, */
	"/",		13,	0,	/* OLDIV, */
	"<=",		10,	0,	/* OLE, */
	"",		16,	0,	/* OLIST, */
	"%",		13,	0,	/* OLMOD, */
	"*",		13,	ASSOC,	/* OLMUL, */
	"<",		10,	0,	/* OLO, */
	"<=",		10,	0,	/* OLS, */
	">>",		11,	0,	/* OLSHR, */
	"<",		10,	0,	/* OLT, */
	"%",		13,	0,	/* OMOD, */
	"*",		13,	ASSOC,	/* OMUL, */
	"",		16,	0,	/* ONAME, */
	"!=",		9,	0,	/* ONE, */
	"!",		14,	RASSOC,	/* ONOT, */
	"|",		6,	ASSOC,	/* OOR, */
	"||",		4,	ASSOC,	/* OOROR, */
	"--",		14,	RASSOC|POSTOP,	/* OPOSTDEC, */
	"++",		14,	RASSOC|POSTOP,	/* OPOSTINC, */
	"--",		14,	RASSOC,	/* OPREDEC, */
	"++",		14,	RASSOC,	/* OPREINC, */
	"",		16,	0,	/* OPROTO, */
	"",		-1,	0,	/* OREGISTER, */
	"",		0,	0,	/* ORETURN, */
	"SET",	-1,	0,	/* OSET, */
	"signof",	14,	RASSOC,	/* OSIGN, */
	"sizeof",	14,	RASSOC,	/* OSIZE, */
	"",		16,	0,	/* OSTRING, */
	"",		16,	0,	/* OLSTRING, */
	"",		16,	0,	/* OSTRUCT, */
	"-",		12,	0,	/* OSUB, */
	"",		-1,	0,	/* OSWITCH, */
	"",		16,	0,	/* OUNION, */
	"USED",	-1,	0,	/* OUSED, */
	"",		-1,	0,	/* OWHILE, */
	"^",		7,	ASSOC,	/* OXOR, */
	"-",		14,	RASSOC,	/* ONEG, */
	"~",		14,	RASSOC,	/* OCOM, */
	"",		16,	0,	/* OELEM, */
	"",		-1,	0,	/* OTST, */
	"",		-1,	0,	/* OINDEX, */
	"",		-1,	0,	/* OFAS, */
	"",		-1,	0,	/* OBLK */
	"+",		14,	RASSOC,	/* OPOS */
	"",		-1,	0,	/* ONUL */
	".",		15,	0,	/* ODOTIND */
	"",		15,	0,	/* OARRIND */
	"",		-1,	0,	/* ODAS */
	":=",		2,	RASSOC,	/* OASD */
	"",		16,	0,	/* OIOTA */
	"",		14,	RASSOC,	/* OLEN */
	"",		17,	0,	/* OBRACKET */
	"",		14,	RASSOC,	/* OREF */
	"",		14,	RASSOC,	/* OARRAYOF */
	"",		15,	0,	/* OSLICE */
	"&",		14,	RASSOC,	/* OSADDR, */
	"",		16,	0,	/* ONIL */
	"",		16,	0,	/* OS2AB */
	"",		16,	0,	/* OAB2S */
	"",		16,	0,	/* OFILDES */
	".",		15,	0,	/* OFD */
	"",		16,	0,	/* OTUPLE */
	".",		15,	0,	/* OT0 */
	"",		15,	0,	/* ORETV */
	"+",		12,	ASSOC,	/* OCAT */
	"",		-1,	0,	/* OSBREAK, */
	".",		15,	0,	/* OLDOT */
	"->",		15,	0,	/* OMDOT */
	nil,		-1,	0,	/* OCODE */
	nil,		-1,	0,	/* ODECE */
	nil,		-1,	0,	/* ODECT */
	nil,		-1,	0,	/* ODECV */
	nil,		-1,	0,	/* ODECF */
	nil,		-1,	0,	/* OPUSH */
	nil,		-1,	0,	/* OPOP */
	"",		-1,	0,	/* OEND */
};

#define COMPLEX	32

#define NOBR	2
#define NOIN	4
#define YESBR	8
#define NONL	16
#define NOENL	32

enum{
	LNONE,
	LSTRLEN,
	LSTRCMP,
	LSTRCPY,
	LSTRCAT,
	LSTRNCMP,
	LSTRNCPY,
	LSTRNCAT,
	LSTRDUP,
	LMEMMOVE,
	LMALLOC,
	LFREE,
	LEXIT,
	LCLOSE,
	LATOI,
	LATOL,
	LATOF,
	LPRINT,
	LFPRINT,
	LSPRINT,
	LSELF,
};

static int tmp;

static void egen(Node*, int, int);
static Node* buildcases(Node*);
static void tdgen(Node *, int);
static Node* cfind(Node*);
static Node* cgen(Node*, Node*);
static void cgen0(Node*, Node*);
static int lteq(Type*, Type*);
static Type* ntype(Node*);
static int rewe(Node*, Type*, int);
static void rewlc(Node*, int, Type*);
static Node* con(vlong);
static void	clrbrk(Node*);
static int hasbrk(Node*);
static int isgen(char*);
static int simple(Node*);
static void pfmt(char*);
static void lpfmt(ushort*);
static int lline(Node*);
static void args(Node*);
static void addmodn(Sym*);
static void scomplex(Node*);
static void mset(Node*);

static Node *lastd;

static int
rev(int op)
{
	switch(op){
		case OLT:	return OGT;
		case OLE:	return OGE;
		case OGT:	return OLT;
		case OGE:	return OLE;
	}
	return op;
}

void
newsec(int l)
{
	if(l != 1 && lastd != Z){
		tdgen(lastd, 1);
		lastd = Z;
	}
	if(l != 2)
		etgen2(nil);
	if(lastsec && l != lastsec)
		newline();
	lastsec = l;
}

static Node*
defval(Type *t)
{
	Node *n;

	if(t == T)
		t = types[TINT];
	n = con(0);
	n->type = types[TINT];
	n->kind = KDEC;
	switch(t->etype){
		case TFLOAT:
		case TDOUBLE:
			n->type = types[TDOUBLE];
			n->fconst = 0.0;
			n->cstring = "0.0";
			return n;
		default:
			break;
		case TIND:
		case TFUNC:
		case TARRAY:
			n->type = typ1(TIND, types[TVOID]);
			return n;
		case TVOID:
		case TSTRUCT:
		case TUNION:
			free(n);
			return Z;
	}
	if(!lteq(n->type, t)){
		n = new1(OCAST, n, Z);
		n->type = t;
	}
	return n;
}

static int
teq(Type *t1, Type *t2)
{
	if(t1 == t2)
		return 1;
	return sametype(t1, t2);
/*
	if(t1->etype != t2->etype)
		return 0;
	switch(t1->etype){
		case TARRAY:
			if(t1->width != t2->width)
				return 0;
			break;
		case TFUNC:
			if(!teq(t1->down, t2->down))
				return 0;
			break;
		case TSTRUCT:
		case TUNION:
			return t1->link == t2->link;
		case TENUM:
			return 1;
	}
	return teq(t1->link, t2->link);
*/
}

static int
tequiv(Type *t1, Type *t2)
{
	if(!teq(t1, t2))
		return 0;
	if(t1->etype == TSTRUCT || t1->etype == TUNION)
		return suename(t1) == suename(t2);
	return 1;
}

static int
iteq(Type *t1, Type *t2)
{
	if(t1 == T || t2 == T)
		return 0;
	return t1->etype == TIND && (teq(t1->link, t2) || (t1->link->etype == TVOID && isnum(t2)));
}

static Type *
ltype(Type *t)
{
	switch(t->etype){
		case TUCHAR:
			return types[TCHAR];
		case TSHORT:
		case TUSHORT:
		case TUINT:
		case TLONG:
		case TULONG:
		case TENUM:
			return types[TINT];
		case TUVLONG:
			return types[TVLONG];
		case TFLOAT:
			return types[TDOUBLE];
		default:
			return t;
	}
}

static int
lteq(Type *t1, Type *t2)
{
	if(t1 == T || t2 == T)
		return 0;
	if(t1 == t2)
		return 1;
	if(t1->etype == TIND && t2->etype == TIND)
		return lteq(t1->link, t2->link);
	return sametype(ltype(t1), ltype(t2));
}

static Type*
tcp(Type *t)
{
	Type *nt;

	if(t == T)
		return T;
	nt = typ1(TXXX, T);
	*nt = *t;
	return nt;
}

static Type*
tuple(Type *t1, Type *t2)
{
	Type *t, **at, *l;

	if(t1 == T || t1->etype == TVOID)
		return tcp(t2);
	if(t2 == T || t2->etype == TVOID)
		return tcp(t1);
	if(t2->etype == TTUPLE)
		diag(Z, "bad tuple type");
	t = typ1(TTUPLE, T);
	at = &t->link;
	if(t1->etype == TTUPLE){
		for(l = t1->link; l != T; l = l->down){
			*at = tcp(l);
			at = &(*at)->down;
		}
	}
	else{
		*at = tcp(t1);
		at = &(*at)->down;
	}
	*at = tcp(t2);
	return t;
}

static Sym*
sue(Type *t)
{
	int h;
	Sym *s;

	if(t != T)
		for(h=0; h<nelem(hash); h++)
			for(s = hash[h]; s != S; s = s->link)
				if(s->suetag && s->suetag->link == t)
					return s;
	return S;
}

static void
pranon(int i)
{
	prid("anon_");
	prnum(i+1, KDEC, T);
}

static int
dotpath(Sym *s, Type *t, int pr)
{
	int i;
	Type *t1;

	if(t == T)
		return 0;
	for(t1 = t->link; t1 != T; t1 = t1->down){
		if(t1->sym == s){
			if(pr){
				prdelim(".");
				prsym(s, 0);
			}
			return 1;
		}
	}
	i = 0;
	for(t1 = t->link; t1 != T; t1 = t1->down){
		if(t1->sym == S){
			i++;
			if(typesu[t1->etype] && sametype(s->type, t1)){
				if(pr){
					prdelim(".");
					pranon(i-1);
				}
				return 1;
			}
		}
	}
	i = 0;
	for(t1 = t->link; t1 != T; t1 = t1->down){
		if(t1->sym == S){
			i++;
			if(typesu[t1->etype] && dotpath(s, t1, 0)){
				if(pr){
					prdelim(".");
					pranon(i-1);
					dotpath(s, t1, 1);
				}
				return 1;
			}
		}
	}
	return 0;
}

static Sym*
suename(Type *t)
{
	Sym *s;

	s = sue(t->link);
	if(s != S)
		return s;
	else if(t->tag != S)
		return t->tag;
	else if(t->sym != S)
		return t->sym;
	return S;
}

static int
cycle(Type *t, Type *base)
{
	int r;
	Type *l;

	if(t->vis){
		/* sametype() does structural comparison so have to check names */
		if(t == base || tequiv(t, base))
			return 1;
		return 0;
	}
	r = 0;
	t->vis = 1;
	switch(t->etype){
		case TIND:
		case TARRAY:
			r = cycle(t->link, base);
			break;
		case TSTRUCT:
		case TUNION:
		case TTUPLE:
			for(l = t->link; l != T; l = l->down)
				r |= cycle(l, base);
			break;
	}
	t->vis = 0;
	return r;
}

static void
addnode(int op, Node *n)
{
	Node *nn;

	nn = new1(OXXX, Z, Z);
	*nn = *n;
	n->op = op;
	n->left = nn;
	n->right = Z;
	n->type = nn->type;
}

static void
cast(Node *n, Type *t)
{
	addnode(OCAST, n);
	n->type = t;
}

static void
intcast(Node *n)
{
	if(isptr(n->type)){
		addnode(ONE, n);
		n->right = con(0);
		n->right->type = n->left->type;
		n->type = types[TINT];
	}
	else
		cast(n, types[TINT]);
}

static void
strcast(Node *n)
{
	cast(n, stringtype);
}

static void
bptr(Node *n)
{
	if(n == Z)
		return;
	switch(n->op){
		default:
			if(!lteq(n->type, types[TINT]))
				intcast(n);
			break;
		case ONOT:
			if(!lteq(n->left->type, types[TINT])){
				intcast(n->left);
				if(n->left->op == ONE){
					n->left->op = OEQ;
					*n = *n->left;
				}
			}
			break;
		case OANDAND:
		case OOROR:
			bptr(n->left);
			bptr(n->right);
			break;
		case OCOND:
			bptr(n->right->left);
			bptr(n->right->right);
			break;
	}
}

static void
bcomplex(Node *n)
{
	if(n == Z)
		return;
	if(!passes)
		complex(n);
	bptr(n);
}

static void
ecomplex(Node *n)
{
	if(!passes)
		complex(n);
	rewe(n, T, 0);
}

static void
becomplex(Node *n)
{
	bcomplex(n);
	rewe(n, T, 0);
}

static void
tgen(Type *t, int dec, int arinit)
{
	Type *l;

	if(t == T)
		return;
	switch(t->etype){
		case TXXX:
			prid("int");
			break;
		case TCHAR: 
		case TUCHAR:
			prid("byte");
			break;
		case TSHORT:
		case TUSHORT:
		case TINT:
		case TUINT:
		case TLONG:
		case TULONG:
		case TENUM:
			prid("int");
			break;
		case TVLONG:
		case TUVLONG:
			prid("big");
			break;
		case TFLOAT:
		case TDOUBLE:
			prid("real");
			break;
		case TIND:
			if(strings == 2 && t->link && t->link->etype == TCHAR){
				prid("string");
				break;
			}
			if(isadt(t->link) || t->link->etype == TFUNC)
				prid("ref ");
			else
				prid("array of ");
			if(t->link && t->link->etype == TVOID){
				prid("byte");
				prcom("was void*", Z);
			}
			else
				tgen(t->link, 1, 0);
			break;
		case TFUNC:
			if(0){
				prid("int");
				prcom("was function", Z);
				break;
			}
			prid("fn");
			prdelim("(");
			for(l = t->down; l != T; l = l->down){
				if(l->etype == TVOID && l->down == T)
					break;
				if(l->etype == TDOT){
					prcom("was ...", Z);
					break;
				}
				if(l->sym != S)
					prsym(l->sym, 0);
				else
					prid("nil");
				prdelim(": ");
				tgen(l, 1, 0);
				if(l->down != T && l->down->etype != TDOT)
					prdelim(", ");
			}
			/* tgen(t->down, dec, 0, 0); */
			prdelim(")");
			if(!isvoid(t->link)){
				prdelim(": ");
				tgen(t->link, dec, 0);
			}
			break;
		case TARRAY:
			prid("array");
			if(t->width == LARR)
				t->width = LARR;
			else if(dec){
				if(t->nwidth != Z)
					prcom("array index was ", t->nwidth);
				else if(t->width != 0){
					sprint(buf, "array index was %ld", t->width/t->link->width);
					prcom(buf, Z);
				}
			}
			else{
				prdelim("[");
				if(t->nwidth != Z)
					egen(t->nwidth, ONOOP, PRE);
				else if(t->width != 0)
					prnum(t->width/t->link->width, KDEC, T);
				prdelim("]");
			}
			prdelim(" of ");
			if(!arinit)
				tgen(t->link, 1, 0);
			break;
		case TVOID:
			/* prid("void"); */
			prid("byte");
			prcom("was void", Z);
			break;
		case TSTRUCT:
		case TUNION:
			if(t->link != T && t->link->etype == TFD){
				prid("Sys->FD");
				usemod(sysop, 0);
			}
			else
				prsym(suename(t), 1);
			break;
		case TTUPLE:
			prdelim("(");
			for(l = t->link; l != T; l = l->down){
				tgen(l, dec, 0);
				if(l->down != T)
					prdelim(", ");
			}
			prdelim(")");
			break;
		case TDOT:
			prdelim("...");
			break;
		case TSTRING:
			prid("string");
			break;
		case TFD:
			prid("fd");
			break;
		default:
			diag(Z, "unknown type");
			break;
	}
}

static Type*
typn(Type *t, int i)
{
	Type *l;

	for(l = t->down; l != T && --i >= 0; l = l->down)
		;
	return l;
}

void
ttgen2(Type *t)
{
	Type *l;
	Sym *s;
	int anon = 0;

	switch(t->etype){
		case TSTRUCT:
		case TUNION:
			newsec(0);
			output(t->lineno, 1);
			s = suename(t);
			if(isgen(s->name))
				addmodn(s);
			setmod(s);
			prsym(s, 0);
			prdelim(": ");
			prid("adt");
			prdelim("{");
			if(t->etype == TUNION)
				prcom("was union", Z);
			newline();
			incind();
			t->vis = 1;
			for(l = t->link; l != T; l = l->down){
				output(l->lineno, 1);
				if(l->nbits)
					prcom("was bit field", Z);
				if(l->sym != S)
					prsym(l->sym, 0);
				else
					pranon(anon++);
				prdelim(": ");
				if(cycle(l, t))
					prid("cyclic ");
				tgen(l, 1, 0);
				prdelim(";");
				newline();
			}
			t->vis = 0;
			decind();
			prdelim("};");
			newline();
			newline();
			break;
		default:
			break;
	}
}

static int
canjoin(Node *n, Node *nn)
{
	return teq(n->type, nn->type) && isname(n) && isname(nn) && n->type->etype != TARRAY;
}

void
vtgen2(Node *n)
{
	int  t, c, comma = 0;
	Node *nn;
	Sym *s;

	nn = n;
	if(n->op == ODAS)
		nn = n->left;
	if(nn->type == T || nn->sym == S)
		return;
	t = nn->type->etype;
	c = nn->sym->class;
	if(0 && c == CTYPEDEF){
		/* egen(nn, ONOOP, PRE); */
		/* tdgen(n, 1, 0); */
		if(isadt(n->type)){
			s = suename(n->type);
			if(isgen(s->name)){
				s->lname = nn->sym->name;
				ttgen2(n->type);
			}
		}
	}
	if(c != CGLOBL && c != CSTATIC && c != CLOCAL && c != CEXREG)
		return;
	newsec(1);
	if(lastd != Z){
		if(t != TFUNC && canjoin(lastd, n))
			comma = 1;
		else
			tdgen(lastd, 1);
	}
	output(nn->lineno, 1);
	if(t == TFUNC){
		if(ism()){
			setmod(nn->sym);
			egen(nn, ONOOP, PRE);
			tdgen(n, 1);
		}
		lastd = Z;
		return;
	}
	if(comma)
		prdelim(", ");
	if(nn->op != ONAME)
		diag(nn, "internal: not name in vtgen");
	setmod(nn->sym);
	prsym(nn->sym, 0);
	/* egen(nn, ONOOP, PRE); */
	/* tdgen(n, 1, 0); */
	lastd = n;
	if(n->op == ODAS)
		rewe(n->right, T, 1);
}

static void minseq(Syml*);

static Node*
con(vlong v)
{
	int neg = 0;
	Node *n;

	if(v < 0){
		neg = 1;
		v = -v;
	}
	n = new1(OCONST, Z, Z);
	n->vconst = v;
	n->kind = KDEC;
	n->type = types[TINT];
	if(neg)
		n = new1(ONEG, n, Z);
	return n;
}

/*
static Node*
fcon(double v)
{
	int neg = 0;
	Node *n;

	if(v < 0){
		neg = 1;
		v = -v;
	}
	n = new1(OCONST, Z, Z);
	n->fconst = v;
	n->kind = KDEC;
	n->type = types[TDOUBLE];
	if(neg)
		n = new1(ONEG, n, Z);
	return n;
}
*/

static Node*
add(vlong v, Node *n)
{
	if(v == 0)
		return n;
	return new1(OADD, con(v), n);
}

static Node*
addn(Node *n1, Node *n2)
{
	if(n1 == Z || n2 == Z)
		return Z;
	if(isconst(n1, 0))
		return n2;
	if(isconst(n2, 0))
		return n1;
	return new1(OADD, n1, n2);
}

static Node*
mul(vlong v, Node *n)
{
	if(v == 0)
		return con(0);
	else if(v == 1)
		return n;
	else if(v == -1)
		return new1(ONEG, n, Z);
	return new1(OMUL, con(v), n);
}

static Node*
mydiv(Node *n, vlong w)
{
	Node *nn;

	if(w == 0)
		return Z;
	if(w == 1)
		return n;
	else if(w == -1)
		return new1(ONEG, n, Z);
	switch(n->op){
		case OCONST:
			if(n->vconst % w == 0){
				n->vconst /= w;
				if(n->left != Z && mydiv(n->left, w) == Z){
					n->vconst *= w;
					break;
				}
				return n;
			}
			break;
		case OCAST:
			return mydiv(n->left, w);
		case OMUL:
			nn = mydiv(n->right, w);
			if(nn != Z){
				if(isconst(nn, 1))
					*n = *n->left;
				return n;
			}
			nn = mydiv(n->left, w);
			if(nn != Z){
				if(isconst(nn, 1))
					*n = *n->right;
				return n;
			}
			break;
		default:
			break;
	}
	return Z;			
}

static Node*
iota(void)
{
	return new1(OIOTA, Z, Z);
}

static Node*
symcon(Sym *s)
{
	Node *n;

	if(s->nconst != Z)
		return s->nconst;
	n = con(s->vconst);
	n->kind = s->kind;
	return n;
}

#define ARITH	1
#define GEOM	2

static Syml*
newsyml(Sym *s, Syml **frees)
{
	Syml *sl, *f;

	if((f = *frees) != nil){
		sl = f;
		*frees = f->nxt;
	}
	else
		sl = (Syml*)malloc(sizeof(Syml));
	sl->sym = s;
	sl->nxt = nil;
	return sl;
}

static Syml*
etseq(Syml *syml)
{
	int e, pio, io, comma;
	vlong d, dd, v0, v1, v, t, tt;
	Node *expr;
	Sym *s;
	Syml *sl, *lsl;

	lsl = nil;
	pio = io = ARITH|GEOM;
	e = 0;
	dd = 0;
	for(sl = syml; sl != nil; sl = sl->nxt){
		s = sl->sym;
		if(isreal(s->tenum) || s->tenum->etype == TIND)
			break;
		if(e == 0)
			v0 = s->vconst;
		if(e == 1){
			v1 = s->vconst;
			d = v1-v0;
		}
		if(e > 0 && (v <= 0 || s->vconst != 2*v))
			io &= ~GEOM;
		if(0 && e > 1 && s->vconst-v != d)
			io &= ~ARITH;
		if(e > 1){
			t = s->vconst-v;
			tt = t-d;
			if(e > 2 && tt != dd)
				io &= ~ARITH;
			else{
				d = t;
				dd = tt;
			}
		}
		if(io == 0)
			break;
		v = s->vconst;
		lsl = sl;
		pio = io;
		e++;
	}
	if(e < 2)
		pio = 0;
	if(pio&GEOM){
		if(e < 3)
			pio = 0;
	}
	else if(pio&ARITH){
		int n;

		if(d == 0 && dd == 0)
			n = 2;
		else if(dd == 0)
			n = 3;
		else
			n = 4;
		if(e < n || (dd&1) != 0)
			pio = 0;
	}
	if(lsl == nil || pio == 0)
		lsl = syml;
	comma = 0;
	for(sl = syml; sl != nil; sl = sl->nxt){
		s = sl->sym;
		nearln = s->lineno;
		output(s->lineno, 1);
		if(pio){
			if(comma)
				prdelim(", ");
			setmod(s);
			prsym(s, 0);
			comma = 1;
		}
		else{
			setmod(s);
			prsym(s, 0);
			prdelim(": ");
			prid("con ");
			if(isbyte(s->tenum) || isbig(s->tenum) && !islbigv(s->vconst) || !isbig(s->tenum) && isuintv(s->vconst)){
				tgen(s->tenum, 1, 0);
				prdelim(" ");
			}
			if(s->nconst != Z)
				egen(s->nconst, ONOOP, PRE);
			else if(s->kind == KCHR)
				prchar(s->vconst);
			else if(isreal(s->tenum))
				prreal(s->fconst, s->cstring, s->kind);
			else
				prnum(s->vconst, s->kind, s->tenum);
			prdelim(";");
			newline();
		}
		if(sl == lsl)
			break;
	}
	if(pio){
		s = syml->sym;
		prdelim(": ");
		prid("con ");
		if(isbyte(s->tenum) || isbig(s->tenum)){
			tgen(s->tenum, 1, 0);
			prdelim(" ");
		}
		if(pio&GEOM){
			if(v0 == 0 || v0 == 1 || v0 == -1)
				expr = mul(v0, new1(OASHL, con(1), iota()));
			else
				expr = new1(OMUL, symcon(s), new1(OASHL, con(1), iota()));
		}
		else if(d == 0 && dd == 0)
			expr = symcon(s);
		else if(dd == 0)
			expr = add(v0, mul(d, iota()));
		else
			expr = add(v0, new1(OADD, mul(v1-dd/2-v0, iota()), mul(dd/2, new1(OMUL, iota(), iota()))));
		complex(expr);
		expr = ckneg(expr);
		egen(expr, ONOOP, PRE);
		prdelim(";");
		newline();
	}
	return lsl->nxt;
}

static void
adde(Syml *sl, Symq *q)
{
	if(q->f == nil)
		q->f = sl;
	else
		q->r->nxt = sl;
	q->r = sl;
}

static void
freeq(Symq *q, Syml **frees)
{
	if(q->f){
		q->r->nxt = *frees;
		*frees = q->f;
		q->f = q->r = nil;
	}
}

static void
etgen2(Sym *s)
{
	Syml *sl;
	static Syml *frees;
	static Symq symq, symq1;

	if(s != nil){
		newsec(2);
		sl = newsyml(s, &frees);
		adde(sl, &symq);
		if(isinteger(s->tenum) && isbigv(s->vconst) && !isbig(s->tenum))
			s->tenum = types[TVLONG];
		return;
	}
	/* end of enums */
	if(symq.f && symq.f == symq.r){	/* try to merge with other singletons */
		adde(symq.f, &symq1);
		symq.f = symq.r = nil;
		return;
	}
	if(symq1.f){
		for(sl = symq1.f; sl != nil; sl = etseq(sl))
			;
		freeq(&symq1, &frees);
	}
	if(symq.f){
		for(sl = symq.f; sl != nil; sl = etseq(sl))
			;
		freeq(&symq, &frees);
	}
}

static void
lgen(Node *n, int br, int first)
{
	if(br)
		prdelim("(");
	if(n == Z){
		if(br)
			prdelim(")");
		return;
	}
	if(n->op == OLIST || n->op == OTUPLE){
		lgen(n->left, 0, first);
		lgen(n->right, 0, 0);
	}
	else if(n->op != ODOTDOT){
		if(!first)
			prdelim(", ");
		egen(n, ONOOP, PRE);
	}
	else
		prcom("was ...", Z);
	if(br)
		prdelim(")");
}

static void
preced(int op1, int op2, int s, int c)
{
	int p1, p2, k1, k2, br;
	char buf[2];

	br = 0;
	p1 = ops[op1].prec;
	p2 = ops[op2].prec;
	if(p1 < 0 || p2 < 0)
		diag(Z, "-ve precedence");
	if(p1 > p2)
		br = 1;
	else if(p1 == p2){
		k1 = ops[op1].kind;
		k2 = ops[op2].kind;
		if(op1 == op2){
			if(k1&RASSOC)
				br = s == LEFT;
			else
				br = s == RIGHT && !(k1&ASSOC);
		}
		else{
			if(k1&RASSOC)
				br = s == LEFT;
			else
				br = s == RIGHT && op1 != OADD;

			if(k1&POSTOP && !(k2&POSTOP))
				br = 1;

			/* funny case */
			if(op2 == OMDOT && s == LEFT && (op1 == ODOT || op1 == ODOTIND))
				br = 1;
		}
	}
	if(br){
		buf[0] = c;
		buf[1] = '\0';
		prdelim(buf);
	}
}

static void
egen(Node *n, int op0, int side)
{
	int op, p;
	Type *t;
	Node *nn;

	if(n == Z){
		if(op0 == OBRACKET)
			prdelim("()");
		return;
	}
	if(n->op == OCONST && n->left != Z){	/* actual node in source */
		n->left->type = n->type;
		n = n->left;
	}
	if((n->op == OSTRING || n->op == OLSTRING) && n->left != Z)	/* actual node in source */
		n = n->left;
	if(n->op == OCAST && (lteq(n->type, n->left->type) || isnil(n) || !iscastable(n->type, n->left->type))){
		if(isnil(n))
			prid("nil");
		else
			egen(n->left, op0, side);
		return;
	}
	if(n->op == ONAME && arrow(n->sym))
		n->op = OMDOT;
	if(n->op != OLIST)
		output(n->lineno, 0);
	op = n->op;
	preced(op0, op, side, '(');
	switch(op){
		case OLIST:
		case OTUPLE:
			lgen(n, 1, 1);
			break;
		case OIOTA:
			prid("iota");
			break;
		case OMDOT:
		case ONAME:
		case OXXX:
			prsym(n->sym, 1);
			break;
		case OCONST:
			if(n->kind == KCHR)
				prchar(n->vconst);
			else if(isreal(n->type))
				prreal(n->fconst, n->cstring, n->kind);
			else if(isnil(n))
				prid("nil");
			else
				prnum(n->vconst, n->kind, n->type);
			if(n->right != Z)
				prcom("was ", n->right);
			break;
		case OSTRING:
			prstr(n->cstring);
			break;
		case OLSTRING:
			prlstr(n->rstring);
			break;
		case OCOND:
			egen(n->left, op, POST);
			prdelim(" ? ");
			egen(n->right->left, op, PRE|POST);
			prdelim(" : ");
			egen(n->right->right, op, PRE);
			prcom("?", Z);
			break;
		case OCOMMA:
			if(op0 != OCOMMA)
				prdelim("(");
			egen(n->left, op, LEFT);
			prdelim(", ");
			egen(n->right, op, RIGHT);
			if(op0 != OCOMMA)
				prdelim(")");
			break;
		case OLDOT:
			egen(n->left, OMOD, LEFT);	/* any precedence 13 operator */
			prdelim(".");
			egen(n->right, op, RIGHT);
			break;
		default:
			p = ops[op].prec;
			egen(n->left, op, LEFT);
			if(space[p])
				prdelim(" ");
			prdelim(ops[op].name);
			if(space[p])
				prdelim(" ");
			egen(n->right, op, RIGHT);
			break;
		case OIND: case OADDR: case OSADDR:
		case OPOS: case ONEG:
		case ONOT: case OCOM:
		case OPREINC: case OPREDEC:
			if(op == OADDR){
				n->op = OSADDR;
				if(!isfn(n->left->type))
					prcom("was ", n);
			}
			else
				prdelim(ops[op].name);
			egen(n->left, op, PRE);
			break;
		case OPOSTINC: case OPOSTDEC:
			egen(n->left, op, POST);
			prdelim(ops[op].name);
			break;
		case ODOT:
			egen(n->left, op, LEFT);
			dotpath(n->sym, n->left->type, 1);
			/* prdelim(ops[op].name); */
			/* prsym(n->sym, 0); */
			break;
		case ODOTIND:
			egen(n->left, op, LEFT);
			if(isadt(n->left->type))
				dotpath(n->sym, n->left->type, 1);	/* type may be demoted arg */
			else
				dotpath(n->sym, n->left->type->link, 1);
			/* prdelim(ops[op].name); */
			/* prsym(n->sym, 0); */
			break;
		case OARRIND:
			egen(n->left, op, LEFT);
			prdelim("[");
			egen(n->right, ONOOP, RIGHT);
			prdelim("]");
			if(n->right->op == OCONST && n->right->vconst < 0)
				prcom("negative array index", Z);
			break;
		case OLEN:
			prid("len ");
			egen(n->right, op, PRE);
			break;
		case OREF:
			prid("ref ");
			tgen(n->type->link, 0, 0);
			break;
		case OARRAYOF:
			prid("array");
			prdelim("[");
			egen(n->left, ONOOP, LEFT);
			prdelim("]");
			prid(" of ");
			tgen(n->type->link, 0, 0);
			break;
		case OSLICE:
			egen(n->left, op, LEFT);
			prdelim("[");
			egen(n->right->left, ONOOP, RIGHT);
			prdelim(": ");
			egen(n->right->right, ONOOP, RIGHT);
			prdelim("]");
			break;
		case OFUNC:
			if(n->kind == KEXP)
				egen(n->left, op, LEFT);
			else
				prsym(n->left->sym, 0);
			lgen(n->right, 1, 1);
			if(n->kind != KEXP && !isvoid(n->left->type->link)){
				prdelim(": ");
				tgen(n->left->type->link, 0, 0);
			}
			break;
		case	ONIL:
			prid("nil");
			break;
		case OCAST:
			if(isnil(n))
				prid("nil");
			else if(iscastable(n->type, n->left->type)){
				tgen(n->type, 0, 0);
				prdelim(" ");
				egen(n->left, op, RIGHT);
			}
			else
				egen(n->left, op0, RIGHT);
			break;
		case OARRAY:
			tgen(n->type, 0, 0);
			egen(n->left, op, LEFT);
			prdelim("[");
			egen(n->right, ONOOP, RIGHT);
			prdelim("]");
			break;
		case OSTRUCT:
		case OUNION:
			tgen(n->type, 0, 0);
			lgen(n->left, 1, 1);
			break;
		case OELEM:
			prdelim(".");
			/* tgen(n->type, 0, 0, 0); */
			prsym(n->sym, 0);
			break;
		case OSIZE:
		case OSIGN:
			prid(ops[op].name);
			if(n->left != Z)
				egen(n->left, OBRACKET, RIGHT);
			else{
				prdelim(" ");
				prid(tnames[n->type->etype]);
				if(typesu[n->type->etype] && n->type->tag){
					prdelim(" ");
					prid(n->type->tag->name);
				}
			}
			break;
		case OPROTO:
			nn = n;
			t = n->type;
			n = protoname(n);
			if(n != Z)
				t = n->type;
			else
				t = prototype(nn->left, t);
			if(!isvoid(t) || n != Z){
				if(n == Z)
					prid("nil");
				else if(n->op == ODOTDOT){
					prcom("was ...", Z);
					break;
				}
				else
					prsym(n->sym, 0);
				/* egen(n, ONOOP, PRE); */
				prdelim(": ");
				tgen(t, 1, 0);
			}
			break;
		case ODOTDOT:
			prid("...");
			break;
		case OINIT:
			egen(n->left, ONOOP, PRE);
			break;
		case OS2AB:
			prid("libc0->s2ab");
			prdelim("(");
			egen(n->left, ONOOP, PRE);
			prdelim(")");
			usemod(libcop, 1);
			break;
		case OAB2S:
			prid("libc0->ab2s");
			prdelim("(");
			egen(n->left, ONOOP, PRE);
			prdelim(")");
			usemod(libcop, 1);
			break;
		case OFILDES:
			prid("sys->fildes");
			prdelim("(");
			egen(n->left, ONOOP, PRE);
			prdelim(")");
			usemod(sysop, 1);
			break;
		case OFD:
			egen(n->left, op, LEFT);
			prdelim(ops[op].name);
			prid("fd");
			break;
		case OT0:
			egen(n->left, op, LEFT);
			prdelim(ops[op].name);
			prid("t0");
			break;
		case ORETV:
			n->op = OAS;
			nn = n->left;
			p = isvoid(n->type) || n->type->etype != TTUPLE || n->type->mark == TCPC;
			if(p)
				n->left = n->right;
			else
				n->left = new1(OTUPLE, new1(ONIL, Z, Z), n->right);
			n->right = nn;
			n->left->type = n->type;
			if(!p && op0 != ONOOP)
				addnode(OT0, n);
			egen(n, op0, side);
			break;
		case OCAT:
			egen(n->left, op, LEFT);
			prdelim(" + ");
			egen(n->right, op, RIGHT);
			break;
	}
	preced(op0, op, side, ')');
}

static int
isexpr(Node *n, Type *t)
{
	if(n == Z)
		return 0;
	if(n->op == OLIST || n->op == OINIT || n->op == OSTRUCT)
		return 0;
	if(teq(t, n->type))
		return 1;
	return 0;
}

static Node *
nxtval(Node *n, Node **nn)
{
	if(n == Z){
		*nn = Z;
		return Z;
	}
	if(n->op == OLIST){
		*nn = n->right;
		return n->left;
	}
	*nn = Z;
	return n;
}

static Node*
eagen(Node *n, Type *t, int ar, int *nz, int depth)
{
	int i, w, nw, down;
	Type *t1;
	Node *nn, *tn;

	if(n != Z){
		if(n->type == T && t == T){
			egen(n, ONOOP, PRE);
			if(ar){
				prdelim(",");
				newline();
			}
			return Z;
		}
		if(ar && n->op == OLIST && n->left->op == OARRAY){
			egen(n->left->left, ONOOP, PRE);
			prdelim(" => ");
			n = n->right;
		}
		if(n->op == OLIST && n->left->op == OELEM){
			prcom("cannot do ", n->left);
			n = n->right;
		}
		if(n->op == OUSED || n->op == ODOTDOT)
			n = n->left;
		if(t == T)
			t = n->type;
	}
	switch(t->etype){
		case TSTRUCT:
		case TUNION:
			if(isexpr(n, t))
				goto Default;
			down = 0;
			tn = nxtval(n, &nn);
			if(tn != Z && (tn->op == OINIT || tn->op == OSTRUCT)){
				down = 1;
				n = tn->left;
			}
			if(depth > 0){
				tgen(t, 0, 0);
				prdelim(" ");
			}
			prdelim("(");
			for(t1 = t->link; t1 != T; t1 = t1->down){
				if(n == Z)
					n = defval(t1);
				n = eagen(n, t1, 0, nil, depth+1);
				if(t1->down != T){
					prdelim(",");
					if(ar)
						prdelim("\t");
					else
						prdelim(" ");
				}
			}
			prdelim(")");
			if(down)
				n = nn;
			break;
		case TARRAY:
			if(isexpr(n, t))
				goto Default;
			if(depth > 0){
				tgen(t, 0, 1);
				prdelim(" ");
			}
			prdelim("{");
			newline();
			incind();
			w = t->width/t->link->width;
			nw = 0;
			for(i = 0; i < w; i++){
				down = 0;
				tn = nxtval(n, &nn);
				if(tn != Z && (tn->op == OINIT || tn->op == OSTRUCT)){
					down = 1;
					n = tn->left;
				}
				n = eagen(n, t->link, 1, &nw, depth+1);
				if(down)
					n = nn;
			}
			if(nw > 0){
				if(nw > 1)
					prdelim("* => ");
				egen(defval(t->link), ONOOP, PRE);
				newline();
			}
			decind();
			prdelim("}");
			break;
		default:
Default:
			if(n == Z){
				if(ar)
					(*nz)++;
				else
					egen(defval(t), ONOOP, PRE);
				return Z;
			}
			n = nxtval(n, &nn);
			if(ar && isnil(n) && iscastable(t, types[TINT])){
				tgen(t, 0, 0);
				prdelim(" ");
			}
			egen(n, ONOOP, PRE);
			n = nn;
			break;
	}
	if(ar){
		prdelim(",");
		newline();
	}
	return n;
}

/* better is
 *	array of byte "abcde\0"
 * but limbo compiler does not accept this as a constant expression
 */
static void
stob(Node *n)
{
	int m;
	char *s = nil, buf[UTFmax];
	ushort *u = nil;

	while(n->op == ONAME)
		n = n->sym->nconst;
	if(n->op == OSTRING)
		s = n->cstring;
	else
		u = n->rstring;
	prdelim("{ ");
	if(s){
		while(*s){
			prid("byte ");
			prchar(*s++);
			prdelim(", ");
		}
	}
	else{
		while(*u){
			m = runetochar(buf, u++);
			s = buf;
			while(--m >= 0){
				prid("byte ");
				prchar(*s++);
				prdelim(", ");
			}
		}
	}
	prid("byte ");
	prchar('\0');
	prdelim(" }");
}

static Type *arrayofchar;

static void
sdgen(Node *n, int glob)
{
	int sop = 0;

	prdelim(" := ");
	if(glob && n->right->op == OS2AB && isstring(n->right->left)){
		if(arrayofchar == T){
			arrayofchar = typ1(TARRAY, types[TCHAR]);
			arrayofchar->width = 0;
		}
		n->type = n->right->type = arrayofchar;
		sop = 1;
	}
	else
		n->type = n->right->type = T;
	tgen(n->type, 0, 1);
	if(sop)
		stob(n->right->left);
	else
		eagen(n->right, n->type, 0, nil, 0);
	prdelim(";");
	newline();
}

static void
tdgen(Node *n, int glob)
{
	int ar, arinit;

	if(ism()){
		prdelim(": ");
		tgen(n->type, 1, 0);
		if(n->op == ODAS)
			prcom("initial value was ", n->right);
		prdelim(";");
		newline();
		return;
	}
	if(n->op == ODAS && (isstring(n->right) || n->right->op == OS2AB)){
		sdgen(n, glob);
		return;
	}
	ar = n->type->etype == TARRAY && n->type->width != LARR;
	arinit = ar && n->op == ODAS;
	if(ar)
		prdelim(" := ");
	else
		prdelim(": ");
	tgen(n->type, 0, arinit);
	if(n->op == ODAS){
		if(!arinit)
			prdelim(" = ");
		eagen(n->right, n->type, 0, nil, 0);
	}
	prdelim(";");
	newline();
}

static int
isdec(Node *n)
{
	return isname(n) && n->kind != KEXP || n->op == ODAS;
}

static void
sgen(Node *n, int blk, Node **ln)
{
	int comma = 0;
	Node *nn;

	if(n == Z)
		return;
	if(blk){
		pushscope(n, SAUTO);
		if(n->op == OLIST && !(blk&NOBR) || (blk&YESBR)){
			prdelim("{");
			newline();
		}
		else if(!(blk&NONL))
			newline();
		if(!(blk&NOIN))
			incind();
	}
	if((nn = *ln) != Z && isdec(nn)){
		if(isdec(n)){
			if(canjoin(nn, n))
				comma = 1;
			else
				tdgen(nn, 0);
		}
		else if(n->op != OLIST){
			tdgen(nn, 0);
			newline();
		}
	}
	if(n->op != OLIST){
		*ln = n;
		output(n->lineno, 1);
	}
	switch(n->op){
		default:
			egen(n, ONOOP, PRE);
			prdelim(";");
			newline();
			break;
		case ODAS:
			pushdcl(n->left, CAUTO);
			egen(n->left, ONOOP, PRE);
			break;
		case ONAME:
			if(n->kind == KEXP){
				egen(n, ONOOP, PRE);
				prdelim(";");
				newline();
			}
			else{
				pushdcl(n, CAUTO);
				if(comma)
					prdelim(", ");
				if(n->op != ONAME)
					diag(n, "internal: not name in sgen");
				prsym(n->sym, 0);
				/* egen(n, ONOOP, PRE); */
/*
				prdelim(": ");
				tgen(n->type, 0, 0, 0);
				prdelim(";");
				newline();
*/
			}
			break;
		case OSBREAK:
			break;
		case ONUL:
			prdelim(";");
			newline();
			break;
		case OBLK:
			sgen(n->left, 1|YESBR, ln);
			break;
		case OLIST:
			sgen(n->left, 0, ln);
			sgen(n->right, 0, ln);
			break;
		case ORETURN:
			prkeywd("return");
			if(n->left != Z)
				prdelim(" ");
			egen(n->left, ONOOP, PRE);
			prdelim(";");
			newline();
			break;
		case OLABEL:
			prcom("was label ", n->left);
			/* i = zeroind(); */
			/* egen(n->left, ONOOP, PRE); */
			/* prdelim(":"); */
			newline();
			/* restoreind(i); */
			break;
		case OGOTO:
			prcom("was goto ", n->left);
			/* prkeywd("goto "); */
			/* egen(n->left, ONOOP, PRE); */
			prdelim(";");
			newline();
			break;
		case OCASE:
			for(nn = n->left; nn != Z; nn = nn->right){
				if(nn != n->left)
					prkeywd(" or ");
				if(nn->left != Z)
					egen(nn->left, ONOOP, PRE);
				else
					prkeywd("*");
			}
			prdelim(" =>");
			clrbrk(n->right);
			sgen(n->right, 1|NOBR, ln);
			if(n->kind != KLAST && !hasbrk(n->right)){
				prcom("fall through", Z);
				newline();
			}
			break;
		case OSWITCH:
			prkeywd("case");
			egen(n->left, OBRACKET, PRE);
			sgen(n->right, 1|NOIN|YESBR, ln);
			break;
		case OWHILE:
			prkeywd("while");
			egen(n->left, OBRACKET, PRE);
			sgen(n->right, 1, ln);
			break;
		case ODWHILE:
			prkeywd("do");
			sgen(n->right, 1|NOENL, ln);
			prkeywd("while");
			egen(n->left, OBRACKET, PRE);
			prdelim(";");
			newline();
			break;
		case OFOR:
			prkeywd("for");
			prdelim("(");
			egen(n->left->right->left, ONOOP, PRE);
			prdelim(";");
			if(n->left->left != Z)
				prdelim(" ");
			egen(n->left->left, ONOOP, PRE);
			prdelim(";");
			if(n->left->right->right != Z)
				prdelim(" ");
			egen(n->left->right->right, ONOOP, PRE);
			prdelim(")");
			sgen(n->right, 1, ln);
			break;
		case OCONTINUE:
			prkeywd("continue");
			prdelim(";");
			newline();
			break;
		case OBREAK:
			prkeywd("break");
			prdelim(";");
			newline();
			break;
		case OIF:
			prkeywd("if");
			egen(n->left, OBRACKET, PRE);
			if(n->right->left->op == OIF && n->right->left->right->right == Z && n->right->right != Z)		/* avoid dangling else */
				sgen(n->right->left, 1|YESBR, ln);
			else
				sgen(n->right->left, 1, ln);
			if(n->right->right != Z){
				prdelim("else");
				if(n->right->right->op == OIF){	/* merge else and if */
					prdelim(" ");
					sgen(n->right->right, 1|NONL|NOIN, ln);
				}
				else
					sgen(n->right->right, 1, ln);
			}
			break;
		case OSET:
		case OUSED:
			prkeywd(ops[n->op].name);
			lgen(n->left, 1, 1);
			prdelim(";");
			newline();
			break;
	}
	if(blk){
		if(!(blk&NOIN))
			decind();
		if(n->op == OLIST&& !(blk&NOBR) || (blk&YESBR)){
			prdelim("}");
			if(!(blk&NOENL))
				newline();
		}
		popscope();
	}
}

static void rew(Node*, int);

static void
rewc0(Node *n, Node *r)
{
	Node *nn;

	if((nn = cfind(n)) != Z){
		cgen0(nn, n);
		if(r->op == ORETURN){
			n->right->left = new1(ORETURN, n->right->left, Z);
			n->right->right = new1(ORETURN, n->right->right, Z);
			n->right->left->type = n->right->left->left->type;
			n->right->right->type = n->right->right->left->type;
			*r = *n;
		}
	}
}

static void
rewc1(Node *n)
{
	Node *c, *nc;

	if(n == Z || n->op != OCOND || side(n) || !simple(n))
		return;
	c = n->left;
	nc = new1(ONOT, ncopy(c), Z);
	n->op = OOROR;
	n->left = new1(OANDAND, c, n->right->left);
	n->right = new1(OANDAND, nc, n->right->right);
}

static void
rewc(Node *n, Node *r)
{
	Node *nn, *rr, *i;

	if((nn = cfind(n)) != Z){
		i = cgen(nn, n);
		rr = new1(OXXX, Z, Z);
		if(n == r && nn == n)
			*rr = *nn;
		else
			*rr = *r;
		r->op = OLIST;
		r->left = i;
		r->right = rr;
	}
}

static int
rewe(Node *n, Type *t, int lev)
{
	int op, k, k1, k2;
	int v;
	Node *nn;

	if(n == Z)
		return -1;
	switch(n->op){
		case OCONST:
			break;
		case ONAME:
			if(strings || !isstring(n))
				break;
		case OSTRING:
		case OLSTRING:
			if(!strings)
				addnode(OS2AB, n);
			break;
		case OCOND:
			bptr(n->left);
			rewe(n->left, T, 1);
			rewe(n->right, T, 1);
			break;
		case OIND:
			if(isfn(n->type)){
				*n = *n->left;
				rewe(n, T, 1);
				break;
			}
			if(!isadt(n->type)){
				n->op = OARRIND;
				n->right = con(0);
				rewe(n, T, 1);
				break;
			}
			rewe(n->left, T, 1);
			break;
		case OADDR:
			if(n->left->op == OARRIND){
				n->right = n->left;
				n->left = n->right->left;
				n->right->left = n->right->right;
				n->right->right = Z;
				n->right->op = OLIST;
				n->op = OSLICE;
				rewe(n, T, 1);
				break;
			}
			rewe(n->left, T, 1);
			break;
		case OSLICE:
			rewe(n->left, T, 1);
			rewe(n->right, T, 1);
			if(n->left->op == OSLICE){
				n->right->left = addn(n->left->right->left, n->right->left);
				n->right->right = addn(n->left->right->left, n->right->right);
				n->left = n->left->left;
				rewe(n, T, 1);
				break;
			}
			break;
		case OCOMMA:
			rewe(n->left, T, 1);
			rewe(n->right, T, 1);
			if(n->left->op == OAS && n->right->op == OAS){
				n->op = OAS;
				n->left->op = n->right->op = OLIST;
				nn = n->left->right;
				n->left->right = n->right->left;
				n->right->left = nn;
				rewe(n, T, 1);
				break;
			}
			break;
		case OFUNC:
			if(n->left->op == ONAME){
				if((k = n->left->sym->kind) != LNONE){
					rewlc(n, k, t);
					rewe(n->left, T, 1);
					rewe(n->right, T, 1);
					args(n);
					return k;
				}
			}
			else
				rewe(n->left, T, 1);
			rewe(n->right, T, 1);
			args(n);
			break;
		case OCAST:
			rewe(n->left, n->type, 1);
			break;
		case OAS:
		case OASI:
		case OASD:
			rewe(n->left, T, 1);
			rewe(n->right, n->type, 1);
			break;
		case ONOT:
		case OANDAND:
		case OOROR:
			bptr(n);
			rewe(n->left, T, 1);
			rewe(n->right, T, 1);
			break;
		case OPREINC:
		case OPOSTINC:
		case OASADD:
			if(n->op != OPOSTINC || lev == 0){
				sliceasgn(n);
				if(n->op == OAS){
					rewe(n, T, 1);
					break;
				}
			}
			rewe(n->left, T, 1);
			rewe(n->right, T, 1);
			break;
		case OEQ:
		case ONE:
		case OLT:
		case OLE:
		case OGT:
		case OGE:
			k1 = rewe(n->left, T, 1);
			k2 = rewe(n->right, T, 1);
			if(k1 == LSTRCMP && n->right->op == OCONST){
				op = -1;
				v = n->right->vconst;
				switch(v){
					case -1:
						if(n->op == OEQ)
							op = OLT;
						else if(n->op == ONE)
							op = OGE;
						break;
					case 0:
						op = n->op;
						break;
					case 1:
						if(n->op == OEQ)
							op = OGT;
						else if(n->op == ONE)
							op = OLE;
						break;
				}
				if(op != -1){
					*n = *n->left;
					n->op = op;
				}
			}
			if(k2 == LSTRCMP && n->left->op == OCONST){
				op = -1;
				v = n->left->vconst;
				switch(v){
					case -1:
						if(n->op == OEQ)
							op = OLT;
						else if(n->op == ONE)
							op = OGE;
						break;
					case 0:
						op = rev(n->op);
						break;
					case 1:
						if(n->op == OEQ)
							op = OGT;
						else if(n->op == ONE)
							op = OLE;
						break;
				}
				if(op != -1){
					*n = *n->right;
					n->op = op;
				}
			}
			break;
		default:
			rewe(n->left, T, 1);
			rewe(n->right, T, 1);
			break;
	}
	return -1;	
}

/*
static void
rewf(Node *n)
{
	if(n == Z)
		return;
	switch(n->op){
		case OFUNC:
			if(n->left->op == ONAME)
				fdargs(n);
			break;
		default:
			rewf(n->left);
			rewf(n->right);
			break;
	}
}
*/

static void
rew(Node *n, int blk)
{
	int i;
	Node *a, *nn;

	if(n == Z)
		return;
	if(blk)
		pushscope(n, SAUTO);
	nearln = n->lineno;
	if(n->blk){
		n->blk = 0;
		addnode(OBLK, n);
	}
	switch(n->op){
		default:
			if(simple(n))
				rewc0(n, n);
			else
				rewc(n, n);
			if(n->op == OLIST || n->op == OIF){
				rew(n, 0);
				break;
			}
			ecomplex(n);
			break;
		case ODAS:
			pushdcl(n->left, CAUTO);
			rewe(n->right, T, 1);
			break;
		case OSBREAK:
		case ONUL:
			break;
		case ONAME:
			if(n->kind == KEXP)
				ecomplex(n);
			else
				pushdcl(n, CAUTO);
			break;
		case OBLK:
			rew(n->left, 1);
			break;
		case OLIST:
			rew(n->left, 0);
			rew(n->right, 0);
			break;
		case ORETURN:
			if(simple(n->left))
				rewc0(n->left, n);
			else	
				rewc(n->left, n);
			if(n->op != ORETURN){
				rew(n, 0);
				break;
			}
			ecomplex(n);
			break;
		case OLABEL:
		case OGOTO:
			break;
		case OCASE:
			for(nn = n->left; nn != Z; nn = nn->right)
				if(nn->left != Z)
					ecomplex(nn->left);
			rew(n->right, 1);
			break;
		case OSWITCH:
			rewc(n->left, n);
			if(n->op == OLIST){
				rew(n, 0);
				break;
			}
			ecomplex(n->left);
			if(!lteq(n->left->type, types[TINT]))
				intcast(n->left);
			n->right = buildcases(n->right);
			rew(n->right, 1);
			break;
		case OWHILE:
		case ODWHILE:
			rewc1(n->left);
			becomplex(n->left);
			rew(n->right, 1);
			break;
		case OFOR:
			rewc1(n->left->left);
			rewc(n->left->right->left, n);
			if(n->op == OLIST){
				rew(n, 0);
				break;
			}
			becomplex(n->left->left);
			ecomplex(n->left->right->left);
			ecomplex(n->left->right->right);
			rew(n->right, 1);
			break;
		case OCONTINUE:
			break;
		case OBREAK:
			break;
		case OIF:
			rewc1(n->left);
			rewc(n->left, n);
			if(n->op == OLIST){
				rew(n, 0);
				break;
			}
			becomplex(n->left);
			rew(n->right->left, 1);
			rew(n->right->right, 1);
			break;
		case OSET:
			if(n->left == Z){
				n->op = ONUL;
				n->left = n->right = Z;
				break;
			}
			if(n->left->op != OLIST){
				n->op = OAS;
				n->right = defval(n->left->type);
				rew(n, 0);
				break;
			}
			i = 0;
			nn = Z;
			for(;;){
				a = arg(n->left, i);
				if(a == Z)
					break;
				a = new1(OAS, a, defval(a->type));
				if(i == 0)
					nn = a;
				else
					nn = new1(OLIST, nn, a);
				i++;
			}
			*n = *nn;
			rew(n, 0);
			break;
		case OUSED:
			if(n->left == Z){
				n->op = ONUL;
				n->left = n->right = Z;
				break;
			}
			i = 0;
			nn = Z;
			for(;;){
				a = arg(n->left, i);
				if(a == Z)
					break;
				if(i == 0)
					nn = a;
				else
					nn = new1(OOROR, nn, a);
				i++;
			}
			n->op = OIF;
			n->left = nn;
			n->right = new1(OLIST, Z, Z);
			n->right->left = new1(ONUL, Z, Z);
			rew(n, 0);
			break;
	}
	if(blk)
		popscope();
}

void
codgen2(Node *n, Node *nn, int lastlno, int rw)
{
	Node *ln = Z;

	newsec(0);
	output(nn->lineno, 1);
	tmp = 0;
	/* t = types[TVOID]; */
	nn = func(nn);
	pushscope(nn, SPARM);
	if(rw)
		rew(n, 1);
	egen(nn, ONOOP, PRE);
	newline();
	prdelim("{");
	newline();
	incind();
	/* rewf(n); */
	pushscope(n, SAUTO);
	sgen(n, 0, &ln);
	if(ln != Z && isdec(ln))
		tdgen(ln, 0);
	popscope();
	popscope();
	if(n != Z)
		output(lline(n), 1);
	output(lastlno, 1);
	decind();
	prdelim("}");
	newline();
	newline();
	setmain(nn);
}

void
rewall(Node *n, Node *nn, int lastlno)
{
	USED(lastlno);
	tmp = 0;
	nn = func(nn);
	pushscope(nn, SPARM);
	rew(n, 1);
	popscope();
	setmain(nn);
}

void
suball(Node *n, Node *nn)
{
	Node *rn;

	nn = func(nn);
	pushscope(nn, SPARM);
	subs(nn, 0, 0);
	subs(n, 1, 1);
	nn = lastn(n);
	if(nn != Z && nn->op != ORETURN){
		rn = retval(Z);
		if(rn != Z){
			addnode(OLIST, nn);
			nn->right = rn;
		}
	}
	popscope();
}

void
ginit(void)
{
	thechar = 'o';
	thestring = "386";
	tfield = types[TLONG];
}

long
align(long i, Type *t, int op)
{
	long o;
	Type *v;
	int w;

	o = i;
	w = 1;
	switch(op) {
	default:
		diag(Z, "unknown align opcode %d", op);
		break;

	case Asu2:	/* padding at end of a struct */
		w = SZ_LONG;
		break;

	case Ael1:	/* initial allign of struct element */
		for(v=t; v->etype==TARRAY; v=v->link)
			;
		w = ewidth[v->etype];
		if(w <= 0 || w >= SZ_LONG)
			w = SZ_LONG;
		break;

	case Ael2:	/* width of a struct element */
		o += t->width;
		break;

	case Aarg0:	/* initial passbyptr argument in arg list */
		if(typesuv[t->etype]) {
			o = align(o, types[TIND], Aarg1);
			o = align(o, types[TIND], Aarg2);
		}
		break;

	case Aarg1:	/* initial allign of parameter */
		w = ewidth[t->etype];
		if(w <= 0 || w >= SZ_LONG) {
			w = SZ_LONG;
			break;
		}
		w = 1;		/* little endian no adjustment */
		break;

	case Aarg2:	/* width of a parameter */
		o += t->width;
		w = SZ_LONG;
		break;

	case Aaut3:	/* total allign of automatic */
		o = align(o, t, Ael2);
		o = align(o, t, Ael1);
		w = SZ_LONG;	/* because of a pun in cc/dcl.c:contig() */
		break;
	}
	o = round(o, w);
	if(0)
		print("align %s %ld %T = %ld\n", bnames[op], i, t, o);
	return o;
}

long
maxround(long max, long v)
{
	v = round(v, SZ_LONG);
	if(v > max)
		return v;
	return max;
}

static int
nlen(Node *n)
{
	if(n == Z)
		return 0;
	if(n->op == OLIST)
		return nlen(n->left)+nlen(n->right);
	return 1;
}

static void
flatten(Node *n, Node **a, int *i)
{
	if(n == Z)
		return;
	if(n->op == OLIST){
		flatten(n->left, a, i);
		flatten(n->right, a, i);
		free(n);
		return;
	}
	a[(*i)++] = n;
}

static Node*
addcase(Node *n, Node **e, Node **s, int k)
{
	Node *nn;

	if(*e != Z){
		nn = new1(OCASE, *e, *s);
		nn->right->blk = 0;
		nn->kind = k;
	}
	else
		nn = *s;
	*e = *s = Z;
	if(n == Z)
		return nn;
	return new1(OLIST, n, nn);
}

/* collect case code together */		
static Node*
buildcases(Node *n)
{
	int i, m, m0, c;
	Node *e, *s, *nn, **a, **ep;

	m = nlen(n);
	a = (Node **)malloc(m*sizeof(Node*));
	m0 = 0;
	flatten(n, a, &m0);
	if(m != m0)
		diag(Z, "internal: bad buildcases()");
	c = 1;
	e = s = nn = Z;
	ep = &e;
	for(i = 0; i < m; i++){
		n = a[i];
		if(n->op == OCASE){
			if(!c){
				nn = addcase(nn, &e, &s, KNIL);
				ep = &e;
			}
			*ep = new1(OLIST, n->left, Z);
			if(n->left == Z)
				(*ep)->lineno = n->lineno;
			ep = &(*ep)->right;
			c = 1;
		}
		else{
			if(s == Z)
				s = n;
			else
				s = new1(OLIST, s, n);
			c = 0;
		}
	}
	nn = addcase(nn, &e, &s, KLAST);
	free(a);
	return nn;
}

static Sym *
tmpgen(Type *t)
{
	Sym *s;

	sprint(buf, "tmp_%d", ++tmp);
	s = slookup(buf);
	s->type = t;
	s->class = CAUTO;
	if(t->etype == TENUM)
		s->type = types[TINT];
	return s;
}

static Node*
cfind(Node *n)
{
	Node *nn;

	if(n == Z)
		return Z;
	if(n->op == OCOND)
		return n;
	nn = cfind(n->left);
	if(nn != Z)
		return nn;
	return cfind(n->right);
}

Node*
ncopy(Node *n)
{
	Node *nn;

	if(n == Z)
		return Z;
	nn = new1(n->op, Z, Z);
	*nn = *n;
	nn->left = ncopy(n->left);
	nn->right = ncopy(n->right);
	return nn;
}

static int
complexity(Node *n, int *cond)
{
	int c;

	if(n == Z)
		return 0;
	c = complexity(n->left, cond)+1+complexity(n->right, cond);
	if(n->op == OCOND)
		(*cond)++;
	return c;
}

static int
simple(Node *n)
{
	int c;

	c = 0;
	return complexity(n, &c) < COMPLEX && c <= 1;
}

static Type*
intype(Node *n)
{
	Type *t;

	t = ntype(n);
	if(t == T)
		return T;
	return t->link;
}

static Type*
ntype(Node *n)
{
	Type *t;

	if(n == Z)
		return T;
	t = n->type;
	if(t != T){
		if(t->etype == TENUM)
			return n->sym->tenum;
		return t;
	}
	switch(n->op){
		case OEQ:
		case ONE:
		case OLT:
		case OGE:
		case OGT:
		case OLE:
		case ONOT:
		case OANDAND:
		case OOROR:
		case OIOTA:
			return types[TINT];
		case OCOMMA:
			return ntype(n->right);
		case OCOND:
			return maxtype(ntype(n->right->left), ntype(n->right->right));
		case OFUNC:
			return intype(n->left);
		case ODOT:
			tcomd(n, ntype(n->left));
			t = n->type;
			n->type = T;
			return t;
		case ODOTIND:
			tcomd(n, intype(n->left));
			t = n->type;
			n->type = T;
			return t;
		case OARRIND:
			return intype(n->left);
		case OADDR:
			return typ1(TIND, ntype(n->left));
		case OIND:
			return intype(n->left);
		case OSTRUCT:
			return T;
	}
	return maxtype(ntype(n->left), ntype(n->right));
}

static Type*
gettype(Node *n1, Node *n2)
{
	Type *t;

	t = maxtype(n1->type, n2->type);
	if(t != T)
		return t;
	return maxtype(ntype(n1), ntype(n2));
}

static void
cgen0(Node *n, Node *e)
{
	Node *c, *nn, *ed, *ee;

	if(n == e){
		n->op = OIF;
		return;
	}
	c = n->left;
	ed = new1(OXXX, Z, Z);
	*ed = *e;
	ee = ncopy(e);
	nn = cfind(ee);
	*n = *n->right->left;
	*nn = *nn->right->right;
	e->op = OIF;
	e->left = c;
	e->right = new1(OLIST, ed, ee);
}

static Node*
cgen(Node *n, Node *e)
{
	Type *t;
	Node *tn, *i;

	USED(e);
	tn = new1(ONAME, Z, Z);
	t = gettype(n->right->left, n->right->right);
	tn->sym = tmpgen(t);
	tn->type = tn->sym->type;
/*
	if(n == e){
		n->op = OIF;
		n->right->left = new1(OASD, tn, n->right->left);
		n->right->right = new1(OAS, tn, n->right->right);
		return n;
	}
*/
	i = new1(OIF, n->left, new1(OLIST, new1(OASD, tn, n->right->left), new1(OAS, tn, n->right->right)));
	*n = *tn;
	return i;
}

static struct{
	char *name;
	int	args;
	int	fd;
	char *lname;
} sysops[] = {
	"create",	1,	RET,	nil,
	"dirstat",	1,	0,	"stat",
	"dirfstat",	0,	1,	"fstat",
	"dirwstat",	1,	0,	"wstat",
	"dirfwstat",	0,	1,	"fwstat",
	"dirread",	0,	1,	nil,
	"dup",	0,	0,	nil,
	"fprint",	2|STAR,	1,	nil,
	"fprintf",	2|STAR,	1,	"fprint",
	"open",	1,	RET,	nil,
	"print",	1|STAR,	0,	nil,
	"printf",	1|STAR,	0,	"print",
	"read",	0,	1,	nil,
	"remove",	1,	0,	nil,
	"seek",	0,	1,	nil,
	"sleep",	0,	0,	nil,
	"sprint",	1|STAR,	0,	nil,
	"sprintf",	1|STAR,	0,	"sprint",
	"write",	0,	1,	nil,
	0
};

/* dummy entry for module */
#define BIOTMP	"__bio__"

static struct{
	char	*name;
	char	*lname;
} bioops[] = {
	"Bflush",	"flush",
	"Bgetc",	"getc",
	"Bprint",	"puts",
	"Bputc",	"putc",
	"Bread",	"read",
	"Bseek",	"seek",
	"Bungetc",	"ungetc",
	"Bwrite",	"write",
	BIOTMP,	nil,
	0
};

char *libcops[] = {
	"isalnum",
	"isalpha",
	"isascii",
	"iscntrl",
	"isdigit",
	"isgraph",
	"islower",
	"isprint",
	"ispunct",
	"isspace",
	"isupper",
	"isxdigit",
	"strchr",
	"strrchr",
	"toascii",
	"tolower",
	"toupper",
	"abs",
	"min",
	"max",
	0,
};

static struct{
	char *name;
	int	type;
	int	string;
} xops[] = {
	"strlen",	LSTRLEN,	1,
	"strcmp",	LSTRCMP,	1,
	"strcpy",	LSTRCPY,	1,
	"strcat",	LSTRCAT,	1,
	"strncmp",	LSTRNCMP,	1,
	"strncpy",	LSTRNCPY,	1,
	"strncat",	LSTRNCAT,	1,
	"strdup",	LSTRDUP,	1,
	"memcpy",	LMEMMOVE,	0,
	"memmove",	LMEMMOVE,	0,
	"malloc",	LMALLOC,	0,
	"free",	LFREE,	0,
	"exit",	LEXIT,	0,
	"exits",	LEXIT,	0,
	"close",	LCLOSE,	0,
	"atoi",	LATOI,	0,
	"atol",	LATOI,	0,
	"atoll",	LATOL,	0,
	"atof",	LATOF,	0,
	"atod",	LATOF,	0,
	"print",	LPRINT,	0,
	"printf",	LPRINT,	0,
	"fprint",	LFPRINT,	0,
	"fprintf",	LFPRINT,	0,
	"sprint",	LSPRINT,	0,
	"sprintf",	LSPRINT,	0,
	0
};

char *mathsops[] = {
	"sin",
	"cos",
	"tan",
	"sinh",
	"cosh",
	"tanh",
	"asin",
	"acos",
	"atan",
	"asinh",
	"acosh",
	"atanh",
	"atan2",
	"sqrt",
	"cbrt",
	"pow",
	"pow10",
	"exp",
	"log",
	"log10",
	0
};

Node *glob, *globe;

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

	glob = globe = new1(ONOOP, Z, Z);
	for(i = 0; sysops[i].name; i++){
		s = slookup(sysops[i].name);
		s->class = CEXTERN;
		s->args = sysops[i].args;
		s->fd = sysops[i].fd;
		s->mod = "sys";
		s->lname = sysops[i].lname;
		s->limbo = 1;
		sysop = s;
	}
	for(i = 0; bioops[i].name; i++){
		s = slookup(bioops[i].name);
		s->class = CEXTERN;
		if(strcmp(bioops[i].name, BIOTMP) == 0){
			s->mod = "bufio";
			bioop = s;
		}
		s->lname = bioops[i].lname;
		s->kind = LSELF;
		s->limbo = 1;
	}
	for(i = 0; mathsops[i]; i++){
		s = slookup(mathsops[i]);
		s->class = CEXTERN;
		s->mod = "math";
		s->limbo = 1;
	}
	for(i = 0; libcops[i]; i++){
		s = slookup(libcops[i]);
		s->class = CEXTERN;
		s->mod = strings ? "libc" : "libc0";
		s->limbo = 1;
		libcop = s;
	}
	for(i = 0; xops[i].name; i++){
		s = slookup(xops[i].name);
		s->class = CEXTERN;
		if(strings || !xops[i].string)
			s->kind = xops[i].type;
		else
			s->mod = "libc0";
		if(s->kind == LEXIT)
			s->lname = "exit";
		s->limbo = 1;
	}
	usemod(sysop, 1);
	if(!strings)
		usemod(libcop, 1);
}

void
clbegin(void)
{
	pushscope(glob, SGLOB);
}

void
clend(void)
{
	if(passes)
		swalk();
	popscope();
}

static Modl *mods;

void
usemod(Sym *s, int ld)
{
	Modl *ml;

	for(ml = mods; ml != nil; ml = ml->nxt)
		if(strcmp(ml->mod, s->mod) == 0){
			ml->ld |= ld;
			return;
		}
	ml = (Modl *)malloc(sizeof(Modl));
	ml->mod = s->mod;
	ml->ld = ld;
	ml->nxt = mods;
	mods = ml;
}

static void
ginc(Modl *ml)
{
	int c;
	char *s;

	if(ml == nil)
		return;
	if(ml->nxt != nil)
		ginc(ml->nxt);
	s = ml->mod;
	c = toupper(s[0]);
	sprint(buf, "include \"%s.m\";", s);
	prline(buf);
	if(ml->ld){
		sprint(buf, "	%s: %c%s;", s, c, s+1);
		prline(buf);
	}
}

static void
gload(Modl *ml)
{
	int c;
	char *s;

	if(ml == nil)
		return;
	if(ml->nxt != nil)
		gload(ml->nxt);
	if(ml->ld){
		s = ml->mod;
		c = toupper(s[0]);
		sprint(buf, "	%s = load %c%s %c%s->PATH;", s, c, s+1, c, s+1);
		prline(buf);
	}
}

static void
callmain(void)
{
	if(inmain){
		if(strings)
			prline("	main(len argl, argl);");
		else
			prline("	main(len argl, libc0->ls2aab(argl));");
	}
}

static void
genstart(void)
{
	char *s;

	if(!strings && inmain)
		usemod(libcop, 1);
	ginc(mods);
	s = hasm();
	if(s){
		sprint(buf, "include \"%s\";", s);
		prline(buf);
	}
	prline("");
	prline("init(nil: ref Draw->Context, argl: list of string)");
	prline("{");
	gload(mods);
	callmain();
	prline("}");
	prline("");
}

static int
argpos0(Node *nn, Node *n, int *p)
{
	int pp;

	if(n == Z)
		return -1;
	if(n->op == OLIST){
		pp = argpos0(nn, n->left, p);
		if(pp >= 0)
			return pp;
		return argpos0(nn, n->right, p);
	}
	if(n == nn)
		return *p;
	(*p)++;
	return -1;
}

static int
argpos(Node *nn, Node *n)
{
	int p = 0;

	p = argpos0(nn, n, &p);
	if(p < 0)
		diag(Z, "-ve argpos");
	return p;
}

static Node*
arg0(Node *n, int a, int *i)
{
	Node *nn;

	if(n == Z)
		return Z;
	if(n->op == OLIST){
		nn = arg0(n->left, a, i);
		if(nn != Z)
			return nn;
		return arg0(n->right, a, i);
	}
	if(a == (*i)++)
		return n;
	return Z;
}

static Node*
arg(Node *n, int a)
{
	int i = 0;

	return arg0(n, a, &i);
}

static Node*
list(Node *l, Node *r)
{
	if(r == Z)
		return l;
	if(l == Z)
		return r;
	return new1(OLIST, l, r);
}

static Node*
droparg(Node *n, int a, int *i)
{
	if(n == Z)
		return Z;
	if(n->op == OLIST)
		return list(droparg(n->left, a, i), droparg(n->right, a, i));
	if(a == (*i)++)
		return Z;
	return n;
}

static void
sargs(Node *n)
{
	int s, f, i, j;
	Node *a;

	if(strings || (f = n->left->sym->args) == 0)
		return;
	s = 0;
	for(i = 1, j = 0; i < STAR || s; i *= 2, j++){
		if(f&i || s){
			a = arg(n->right, j);
			if(a == Z)
				break;
			if(s && !isstr(a->type))
				continue;
			if(f&STAR)
				s++;
			if(a->op == OS2AB){
				*a = *a->left;
				continue;
			}
			addnode(OAB2S, a);
		}
	}
}

static void
fdargs(Node *n)
{
	int f, i, j;
	Node *a;

	if((f = n->left->sym->fd) == 0)
		return;
	marktype(pfdtype, TCFD);
	if(f&RET)
		tcon(n, pfdtype);
	for(i = 1, j = 0; i < RET; i *= 2, j++){
		if(f&i){
			a = arg(n->right, j);
			if(a == Z)
				break;
			tcon(a, pfdtype);
		}
	}
}

static void
aargs(Node *n)
{
	int i;
	Node *a, *nn, *fn;
	Type *t, *t0, *ft, *at, *st;

	if(!doaddr)
		return;
	if(n->op != OFUNC || n->left->op != ONAME)
		return;
	/* ft = n->left->type; */
	ft = n->left->sym->type;
	t = t0 = ft->link;
	nn = Z;
	for(i = 0; ; i++){
		a = arg(n->right, i);
		if(a == Z)
			break;
		at = typn(ft, i);
		if(at != T && at->etype != TDOT && (a->op == OADDR || iteq(a->type, at) || iteq(at, a->type))){
			if(iteq(at, a->type))
				st = at->link;
			else
				st = a->type->link;
			if(doalladdr || isscalar(st)){
				if(a->op == OADDR)
					*a = *a->left;
				else if(iteq(a->type, at))
					a->type = at;
				if(t->mark == 0){
					t = tuple(t, a->type);
					trep(at, at->link);
					fn = finddec(n->left->sym, 1);
					if(fn != Z && fn->op == OFUNC)
						tind(arg(fn->right, i));
				}
				if(nn == Z)
					nn = cknil(ncopy(a));
				else{
					nn = new1(OTUPLE, nn, cknil(ncopy(a)));
					nn->type = t;
				}
			}
		}
	}
	if(nn != Z){
		if(isvoid(t0) || t->mark == TCPC)
			marktype(t, TCPC);
		else
			marktype(t, TCFC);
		tcon(n, t);
		addnode(ORETV, n);
		n->right = nn;
	}
}

static void
args(Node *n)
{
	if(n->op != OFUNC || n->left->op != ONAME)
		return;
	sargs(n);
	if(passes){
		fdargs(n);
		aargs(n);
	}
}

static Node*
indir(Node *n)
{
	if(n->op == OADDR)
		return n->left;
	return new1(OIND, n, Z);
}

static void
rewlc(Node *n, int k, Type *t)
{
	int i;
	Type *tt;
	Node *a0, *a1, *a2, *nn;

	if(t == T)
		t = n->type;
	a0 = arg(n->right, 0);
	a1 = arg(n->right, 1);
	switch(k){
		case LSTRLEN:
			n->op = OLEN;
			break;
		case LSTRCMP:
			n->op = ONE;
			n->left = a0;
			n->right = a1;
			break;
		case LSTRCPY:
			n->op = OAS;
			n->left = a0;
			n->right = a1;
			n->type = n->left->type;
			break;
		case LSTRCAT:
			n->op = OASADD;
			n->left = a0;
			n->right = a1;
			n->type = n->left->type;
			break;
		case LSTRDUP:
			*n = *a0;
			break;
		case LMEMMOVE:
			if(!teq(a0->type, a1->type))
				break;
			if(a0->type->etype == TIND){
				tt = a0->type->link;
				a2 = arg(n->right, 2);
				if(isadt(tt) && isconst(a2, tt->width)){
					n->op = OAS;
					n->left = indir(a0);
					n->right = indir(a1);
					n->type = n->left->type = n->right->type = tt;
					break;
				}
				if(mydiv(a2, tt->width) != Z){
					n->op = OAS;
					n->left = new1(OSLICE, a0, new1(OLIST, con(0), Z));
					n->right = new1(OSLICE, a1, new1(OLIST, con(0), a2));
					n->type = n->left->type = n->right->type = a0->type;
				}
			}
			break;
		case LMALLOC:
			if(t->etype == TIND){
				tt = t->link;
				if(isadt(tt) && isconst(a0, tt->width)){
					n->op = OREF;
					n->left = Z;
					n->right = Z;
					n->type = t;
					break;
				}
				if(mydiv(a0, tt->width) != Z){
					n->op = OARRAYOF;
					n->left = a0;
					n->right = Z;
					n->type = t;
					if(isadt(tt)){
						n->type = typ1(TARRAY, tt);
						n->type->width = LARR;	/* limbo array without bounds */
						marktype(n->type, TCAR);
					}
				}
			}
			break;
		case LFREE:
			n->op = OAS;
			n->left = a0;
			n->right = con(0);
			n->type = n->left->type;
			n->right->type = n->type;
			break;
		case LEXIT:
			i = n->kind;
			*n = *n->left;
			n->kind = i;
			break;
		case LCLOSE:
			n->op = OAS;
			n->left = a0;
			n->right = con(0);
			n->left->type = typ1(TIND, n->left->type);
			n->type = n->left->type;
			n->right->type = n->type;
			break;
		case LATOI:
			if(!strings)
				strcast(a0);
			n->op = OCAST;
			n->left = a0;
			n->right = Z;
			n->type = types[TINT];
			break;
		case LATOL:
			if(!strings)
				strcast(a0);
			n->op = OCAST;
			n->left = a0;
			n->right = Z;
			n->type = types[TVLONG];
			break;
		case LATOF:
			if(!strings)
				strcast(a0);
			n->op = OCAST;
			n->left = a0;
			n->right = Z;
			n->type = types[TDOUBLE];
			break;
		case LPRINT:
			if(a0->op == OSTRING)
				pfmt(a0->cstring);
			else if(a0->op == OLSTRING)
				lpfmt(a0->rstring);
			break;
		case LFPRINT:
			if(a1->op == OSTRING)
				pfmt(a1->cstring);
			else if(a1->op == OLSTRING)
				lpfmt(a1->rstring);
			break;
		case LSPRINT:
			if(n->right->kind != KDROP){
				if(a1->op == OSTRING)
					pfmt(a1->cstring);
				else if(a1->op == OLSTRING)
					lpfmt(a1->rstring);
				nn = new1(OXXX, Z, Z);
				*nn = *n;
				i = 0;
				nn->right = droparg(nn->right, 0, &i);
				nn->right->kind = KDROP;
				n->op = OAS;
				n->left = a0;
				n->right = nn;
				n->type = nn->type;
			}
			break;
		case LSELF:
			if(n->right != Z && n->right->kind != KDROP){
				i = 0;
				n->right = droparg(n->right, 0, &i);
				if(n->right != Z)
					n->right->kind = KDROP;
				addnode(OLDOT, n->left);
				n->left->right = n->left->left;
				n->left->left = a0;
				usemod(bioop, 1);
			}
			break;
	}
}

void
expgen(Node *n)
{
	egen(n, ONOOP, PRE);
}

static void
clrbrk(Node *n)
{
	if(n == Z)
		return;
	switch(n->op){
		case OLIST:
			clrbrk(n->right);
			break;
		case OBREAK:
			n->op = OSBREAK;
			n->left = n->right = Z;
			break;
	}
}

static int
hasbrk(Node *n)
{
	if(n == Z)
		return 0;
	switch(n->op){
		case OLIST:
		case OWHILE:
		case ODWHILE:
		case OFOR:
			return hasbrk(n->right);
		case OIF:
			if(n->right->right == Z)
				return 0;
			return hasbrk(n->right->left) && hasbrk(n->right->right);
		case ORETURN:
		case OGOTO:
		case OCONTINUE:
		case OBREAK:
		case OSBREAK:
			return 1;
		default:
			return 0;
	}
}

static int
isgen(char *s)
{
	char *s1, *s2;

	s1 = strchr(s, '_');
	s2 = strrchr(s, '_');
	if(s1 == nil || s2-s1 != 4)
		return 0;
	return s1[1] == 'a' && s1[2] == 'd' && s1[3] == 't';
}

static void
addmodn(Sym *s)
{
	char buf[128], *ns;

	if(s->name[0] == '_'){
		outmod(buf, -1);
		ns = malloc(strlen(buf)+strlen(s->name)+1);
		strcpy(ns, buf);
		strcat(ns, s->name);
		s->name = ns;
	}
}
		
static void
pfmt(char *s)
{
	char *t = s;

	while(*s != '\0'){
		if(*s == '%'){
			*t++ = *s++;
			if(*s == 'l'){
				s++;
				if(*s == 'l')
					*t++ = 'b';
				else
					*t++ = *s;
				s++;
			}
			else if(*s == 'p'){
				*t++ = 'x';
				s++;
			}
			else
				*t++ = *s++;
		}
		else
			*t++ = *s++;
	}
	*t = '\0';
}

static void
lpfmt(ushort *s)
{
	 ushort*t = s;

	while(*s != '\0'){
		if(*s == '%'){
			*t++ = *s++;
			if(*s == 'l'){
				s++;
				if(*s == 'l')
					*t++ = 'b';
				else
					*t++ = *s;
				s++;
			}
			else if(*s == 'p'){
				*t++ = 'x';
				s++;
			}
			else
				*t++ = *s++;
		}
		else
			*t++ = *s++;
	}
	*t = '\0';
}

int
line(Node *n)
{
	if(n == Z)
		return 0;
	if(n->op == OLIST)
		return line(n->left);
	return n->lineno;
}

static int
lline(Node *n)
{
	if(n == Z)
		return 0;
	if(n->op == OLIST)
		return lline(n->right);
	return n->lineno+1;
}

static Node*
lastn(Node *n)
{
	while(n != Z && n->op == OLIST)
		n = n->right;
	return n;
}

static Node*
newnode(int op, Node *l)
{
	Node *n;

	n = new1(op, l, Z);
	globe->right = n;
	globe = n;
	return n;
}

void
codgen1(Node *n, Node *nn, int lastlno)
{
	Node *nnn;

	scomplex(n);
	nnn = newnode(OCODE, new1(OLIST, n, nn));
	nnn->lineno = lastlno;
	mset(n);
	mset(nn);
	nn = func(nn);
	newnode(ODECF, nn);
	setmain(nn);
}

void
vtgen1(Node *n)
{
	int c;
	Node *nn = n;

	if(n->op == ODAS)
		nn = n->left;
	if(nn->type == T || nn->sym == S)
		return;
	c = nn->sym->class;
	if(c == CGLOBL || c == CSTATIC || c == CLOCAL || c == CEXREG){
		newnode(ODECV, n);
		if(nn->type->etype != TFUNC || ism())
			setmod(nn->sym);
	}
	mset(n);
}

void
etgen1(Sym *s)
{
	Node *n;

	n = newnode(ODECE, Z);
	n->sym = s;
	if(s != S)
		setmod(s);
}

void
ttgen1(Type *t)
{
	Node *n;

	n = newnode(ODECT, Z);
	n->type = t;
	if(isadt(t))
		setmod(suename(t));
}

void
outpush1(char *s)
{
	Node *n;
	char *t;

	n = newnode(OPUSH, Z);
	if(s == nil)
		t = nil;
	else{
		t = malloc(strlen(s)+1);
		strcpy(t, s);
	}
	n->cstring = t;
	outpush0(s, n);
}

void
outpop1(int lno)
{
	Node *n;

	n = newnode(OPOP, Z);
	n->lineno = lno;
	outpop0(lno);
}

void
codgen(Node *n, Node *nn, int lastlno)
{
	if(passes)
		codgen1(n, nn, lastlno);
	else
		codgen2(n, nn, lastlno, 1);
}

void
vtgen(Node *n)
{
	if(passes)
		vtgen1(n);
	else
		vtgen2(n);
}

void
etgen(Sym *s)
{
	if(passes)
		etgen1(s);
	else
		etgen2(s);
}

void
ttgen(Type *t)
{
	if(passes)
		ttgen1(t);
	else
		ttgen2(t);
}

void
outpush(char *s)
{
	if(passes)
		outpush1(s);
	else
		outpush2(s, Z);
}

void
outpop(int lno)
{
	if(passes)
		outpop1(lno);
	else
		outpop2(lno);
}

static void
swalk(void)
{
	Node *n, *l;

	for(n = glob; n != Z; n = n->right){
		l = n->left;
		switch(n->op){
			case OCODE:
				rewall(l->left, l->right, n->lineno);
				break;
			default:
				break;
		}
	}
	while(again){
		again = 0;
		for(n = glob; n != Z; n = n->right){
			l = n->left;
			switch(n->op){
				case OCODE:
					suball(l->left, l->right);
					break;
				case ODECV:
					subs(l, 0, 0);
					break;
				case ODECE:
				case ODECT:
				case ODECF:
					break;
				default:
					break;
			}
		}
	}
	for(n = glob; n != Z; n = n->right){
		l = n->left;
		switch(n->op){
			case ONOOP:
				break;
			case OPUSH:
				outpush2(n->cstring, n);
				break;
			case OPOP:
				outpop2(n->lineno);
				break;
			case OCODE:
				codgen2(l->left, l->right, n->lineno, 0);
				break;
			case ODECV:
				vtgen2(l);
				break;
			case ODECE:
				etgen2(n->sym);
				break;
			case ODECT:
				ttgen2(n->type);
				break;
			case ODECF:
				break;
		}
	}
}

static void
scomplex(Node *n)
{
	if(n == Z)
		return;
	switch(n->op){
		default:
			complex(n);
			break;
		case ODAS:
		case OSBREAK:
		case ONUL:
		case OLABEL:
		case OGOTO:
		case OCONTINUE:
		case OBREAK:
			break;
		case ONAME:
			if(n->kind == KEXP)
				complex(n);
			break;
		case OBLK:
		case OSET:
		case OUSED:
			scomplex(n->left);
			break;
		case OLIST:
			scomplex(n->left);
			scomplex(n->right);
			break;
		case ORETURN:
			complex(n);
			break;
		case OCASE:
			complex(n->left);
			break;
		case OSWITCH:
		case OWHILE:
		case ODWHILE:
			complex(n->left);
			scomplex(n->right);
			break;
		case OFOR:
			complex(n->left->left);
			complex(n->left->right->left);
			complex(n->left->right->right);
			scomplex(n->right);
			break;
		case OIF:
			complex(n->left);
			scomplex(n->right->left);
			scomplex(n->right->right);
			break;
	}
}

static void
mtset(Type *t)
{
	if(t == T)
		return;
	switch(t->etype){
		case TIND:
		case TARRAY:
			mtset(t->link);
			break;
		case TSTRUCT:
		case TUNION:
			prsym0(suename(t));
			/*
			for(l = t->link; l != T; l = l->down)
				mtset(l);
			*/
			break;
	}
}

static void
mset(Node *n)
{
	if(n == Z)
		return;
	n->garb = 0;
	if(n->op == ONAME)
		prsym0(n->sym);
	mtset(n->type);
	mset(n->left);
	mset(n->right);
}

static int
sign(Node *n)
{
	int s;

	if(n == Z)
		return 1;
	switch(n->op){
		case OCONST:
			sign(n->left);
			if(n->vconst < 0){
				n->vconst = -n->vconst;
				return -1;
			}
			break;
		case OPOS:
			s = sign(n->left);
			*n = *n->left;
			return s;
		case ONEG:
			s = sign(n->left);
			*n = *n->left;
			return -s;
		case OADD:
			if(sign(n->right) < 0)
				n->op = OSUB;
			break;
		case OSUB:
			if(sign(n->right) < 0)
				n->op = OADD;
			break;
		case OMUL:
		case ODIV:
			return sign(n->left)*sign(n->right);
		default:
			break;
	}
	return 1;
}

static Node*
ckneg(Node *n)
{
	if(sign(n) < 0)
		return new1(ONEG, n, Z);
	return n;
}

static void
sliceasgn(Node *n)
{
	Type *t;
	Node *nn;

	if(side(n->left) || (n->right != Z && side(n->right)))
		return;
	t = n->type;
	if(isarray(t) && (!strings || t->link->etype != TCHAR)){
		if(n->op == OASADD)
			nn = n->right;
		else
			nn = con(1);
		n->op = OAS;
		n->right = new1(OSLICE, ncopy(n->left), new1(OLIST, nn, Z));
	}
}
