#include "gc.h"

int
swcmp(const void *a1, const void *a2)
{
	C1 *p1, *p2;

	p1 = (C1*)a1;
	p2 = (C1*)a2;
	if(p1->val < p2->val)
		return -1;
	return p1->val > p2->val;
}

void
doswit(int g, Node *n)
{
	Case *c;
	C1 *q, *iq;
	long def, nc, i;

	def = 0;
	nc = 0;
	for(c = cases; c->link != C; c = c->link) {
		if(c->def) {
			if(def)
				diag(n, "more than one default in switch");
			def = c->label;
			continue;
		}
		nc++;
	}

	iq = alloc(nc*sizeof(C1));
	q = iq;
	for(c = cases; c->link != C; c = c->link) {
		if(c->def)
			continue;
		q->label = c->label;
		q->val = c->val;
		q++;
	}
	qsort(iq, nc, sizeof(C1), swcmp);
	if(def == 0)
		def = breakpc;
	for(i=0; i<nc-1; i++)
		if(iq[i].val == iq[i+1].val)
			diag(n, "duplicate cases in switch %lld", iq[i].val);
	swit1(iq, nc, def, g, n);
}

#define	N1	4	/* ncase: always linear */
#define	N2	5	/* min ncase: direct */
#define	N3	4	/* range/ncase: direct */
			/* else binary */
void
swit1(C1 *q, int nc, long def, int g, Node *n)
{
	C1 *r, *s;
	int i, l, m, y;
	long v, range;
	Prog *sp1, *sp2;

	/* note that g and g+1 are not allocated */
	if(nc <= N1)
		goto linear;
	y = 23*nc/100 + 5;	/* number of cases needed to make */
	if(y < N2)		/* direct switch worthwile */
		y = N2;				/* try to do better than n**2 here */
	for(m=nc; m>=y; m--) {			/* m is number of cases */
		s = q+nc;
		r = s-m;
		for(l=nc-m; l>=0; l--) {	/* l is base of contig cases */
			s--;
			range = s->val - r->val;
			if(range > 0 && range <= N3*m)
				goto direct;
			r--;
		}
	}

	/*
	 * divide and conquer
	 */
	i = nc / 2;
	r = q+i;
	v = r->val;
	/* compare median */
	if(v >= -128 && v < 128) {
		gopcode(OAS, n->type, D_CONST, nodconst(v), g+1, n);
		gopcode(OEQ, n->type, g, n, g+1, n);
	} else
		gopcode(OEQ, n->type, g, n, D_CONST, nodconst(v));
	gbranch(OLT);
	sp1 = p;
	gbranch(OGT);
	sp2 = p;
	gbranch(OGOTO);
	patch(p, r->label);

	patch(sp1, pc);
	swit1(q, i, def, g, n);

	patch(sp2, pc);
	swit1(r+1, nc-i-1, def, g, n);
	return;

direct:
	/* compare low bound */
	v = r->val;
	if(v >= -128 && v < 128) {
		gopcode(OAS, n->type, D_CONST, nodconst(v), g+1, n);
		gopcode(OEQ, n->type, g, n, g+1, n);
	} else
		gopcode(OEQ, n->type, g, n, D_CONST, nodconst(v));
	gbranch(OLT);
	sp1 = p;

	/* compare high bound */
	v = s->val;
	if(v >= -128 && v < 128) {
		gopcode(OAS, n->type, D_CONST, nodconst(v), g+1, n);
		gopcode(OEQ, n->type, g, n, g+1, n);
	} else
		gopcode(OEQ, n->type, g, n, D_CONST, nodconst(v));
	gbranch(OGT);
	sp2 = p;

	/* switch */
	v = r->val;
	gpseudo(AMOVW, symstatic, D_R0, 0L);
	p->from.offset = nstatic - v*2;
	p->from.index = g|I_INDEX1;
	p->from.scale = 5;
	nextpc();
	p->as = ACASEW;

	/* table */
	for(i=0; i<=range; i++) {
		gbranch(OCASE);
		if(v == r->val) {
			patch(p, r->label);
			r++;
		} else
			patch(p, def);
		p->from.type = D_STATIC;
		p->from.sym = symstatic;
		p->from.offset = nstatic;
		nstatic += types[TSHORT]->width;
		v++;
	}
	gbranch(OGOTO);
	patch(p, def);
	if(r != s+1)
		print("smelly direct switch\n");

	if(l > 0) {
		patch(sp1, pc);
		swit1(q, l, def, g, n);
	} else
		patch(sp1, def);

	m += l;
	if(m < nc) {
		patch(sp2, pc);
		swit1(q+m, nc-m, def, g, n);
	} else
		patch(sp2, def);
	return;


linear:
	for(i=0; i<nc; i++) {
		v = q->val;
		if(v >= -128 && v < 128) {
			gopcode(OAS, n->type, D_CONST, nodconst(v), g+1, n);
			gopcode(OEQ, n->type, g+1, n, g, n);
		} else
			gopcode(OEQ, n->type, g, n, D_CONST, nodconst(v));
		gbranch(OEQ);
		patch(p, q->label);
		q++;
	}
	gbranch(OGOTO);
	patch(p, def);
}

void
casf(void)
{
	Case *c;

	c = alloc(sizeof(*c));
	c->link = cases;
	cases = c;
}


int
bitload(Node *b, int n1, int n2, int n3, Node *nn)
{
	int sh, g, gs;
	long v;
	Node *l;
	Type *t;

	/*
	 * n1 gets adjusted/masked value
	 * n2 gets address of cell
	 * n3 gets contents of cell
	 */
	gs = 0;
	t = tfield;

	l = b->left;
	g = regalloc(t, n3);
	if(n2 != D_NONE) {
		lcgen(l, n2, Z);
		n2 |= I_INDIR;
		gmove(t, t, n2, l, g, l);
		gmove(t, t, g, l, n1, l);
	} else
		cgen(l, g, nn);
	if(b->type->shift == 0 && typeu[b->type->etype]) {
		v = ~0 + (1L << b->type->nbits);
		gopcode(OAND, t, D_CONST, nodconst(v), g, l);
	} else {
		sh = 32 - b->type->shift - b->type->nbits;
		if(sh > 0)
			if(sh >= 8) {
				gs = regalloc(t, D_NONE);
				gmove(t, t, D_CONST, nodconst(sh), gs, l);
				gopcode(OASHL, t, gs, l, g, l);
				if(b->type->shift)
					regfree(gs);
			} else
				gopcode(OASHL, t, D_CONST, nodconst(sh), g, l);
		sh += b->type->shift;
		if(sh > 0) {
			if(sh >= 8) {
				if(b->type->shift) {
					gs = regalloc(t, D_NONE);
					gmove(t, t, D_CONST, nodconst(sh), gs, l);
				}
				if(typeu[b->type->etype])
					gopcode(OLSHR, t, gs, l, g, l);
				else
					gopcode(OASHR, t, gs, l, g, l);
				regfree(gs);
			} else {
				if(typeu[b->type->etype])
					gopcode(OLSHR, t, D_CONST, nodconst(sh), g, l);
				else
					gopcode(OASHR, t, D_CONST, nodconst(sh), g, l);
			}
		}
	}
	return g;
}

void
bitstore(Node *b, int n1, int n2, int n3, int result, Node *nn)
{
	long v;
	Node *l;
	Type *t;
	int sh, g, gs;

	/*
	 * n1 has adjusted/masked value
	 * n2 has address of cell
	 * n3 has contents of cell
	 */
	t = tfield;

	l = b->left;
	g = regalloc(t, D_NONE);
	v = ~0 + (1L << b->type->nbits);
	gopcode(OAND, t, D_CONST, nodconst(v), n1, l);
	gmove(t, t, n1, l, g, l);
	if(result != D_NONE)
		gmove(t, nn->type, n1, l, result, nn);
	sh = b->type->shift;
	if(sh > 0) {
		if(sh >= 8) {
			gs = regalloc(t, D_NONE);
			gmove(t, t, D_CONST, nodconst(sh), gs, l);
			gopcode(OASHL, t, gs, l, g, l);
			regfree(gs);
		} else
			gopcode(OASHL, t, D_CONST, nodconst(sh), g, l);
	}
	v <<= sh;
	gopcode(OAND, t, D_CONST, nodconst(~v), n3, l);
	gopcode(OOR, t, n3, l, g, l);
	gmove(t, t, g, l, n2|I_INDIR, l);

	regfree(g);
	regfree(n1);
	regfree(n2);
	regfree(n3);
}

long
outstring(char *s, long n)
{
	long r;

	if(suppress)
		return nstring;
	r = nstring;
	while(n) {
		string[mnstring] = *s++;
		mnstring++;
		nstring++;
		if(mnstring >= NSNAME) {
			gpseudo(ADATA, symstring, D_SCONST, 0L);
			memmove(p->to.sval, string, NSNAME);
			p->from.offset = nstring - NSNAME;
			p->from.displace = NSNAME;
			mnstring = 0;
		}
		n--;
	}
	return r;
}

long
outlstring(Rune *s, long n)
{
	char buf[sizeof(Rune)];
	int c, i;
	long r;

	while(nstring % sizeof buf)
		outstring("", 1);
	r = nstring;
	while(n > 0) {
		c = *s++;
		if(align(0, types[TCHAR], Aarg1)) {
			for(i = sizeof buf; i > 0; c >>= 8)
				buf[--i] = c;
		} else {
			for(i = 0; i < sizeof buf; c >>= 8)
				buf[i++] = c;
		}
		outstring(buf, sizeof buf);
		n -= sizeof buf;
	}
	return r;
}

int
doinc(Node *n, int f)
{
	Node *l;
	int a;

loop:
	if(n == Z)
		return 0;
	l = n->left;
	switch(n->op) {

	case OPOSTINC:
	case OPOSTDEC:
		if(f & POST) {
			a = n->addable;
			if(a >= INDEXED) {
				if(f & TEST)
					return 1;
				n->addable = 0;
				cgen(n, D_NONE, n);
				n->addable = a;
			}
		}
		break;

	case OAS:
	case OASLMUL:
	case OASLDIV:
	case OASLMOD:
	case OASMUL:
	case OASDIV:
	case OASMOD:
	case OASXOR:
	case OASOR:
	case OASADD:
	case OASSUB:
	case OASLSHR:
	case OASASHR:
	case OASASHL:
	case OASAND:

	case OPREINC:
	case OPREDEC:
		if(f & PRE) {
			a = n->addable;
			if(a >= INDEXED) {
				if(f & TEST)
					return 1;
				n->addable = 0;
				doinc(n, PRE);
				cgen(n, D_NONE, n);
				n->addable = a;
				return 0;
			}
		}
		break;

	case OFUNC:
		if(f & PRE)
			break;
		return 0;

	case ONAME:
	case OREGISTER:
	case OSTRING:
	case OCONST:

	case OANDAND:
	case OOROR:
		return 0;

	case OCOND:
		return 0;

	case OCOMMA:
		n = n->right;
		if(f & PRE)
			n = l;
		goto loop;
	}
	if(l != Z)
		if(doinc(l, f))
			return 1;
	n = n->right;
	goto loop;
}

void
setsp(void)
{

	nextpc();
	p->as = AADJSP;
	p->from.type = D_CONST;
	p->from.offset = 0;
}

void
adjsp(long o)
{

	if(o != 0) {
		nextpc();
		p->as = AADJSP;
		p->from.type = D_CONST;
		p->from.offset = o;
		argoff += o;
	}
}

int
simplv(Node *n)
{

	if(n->addable <= INDEXED)
		return 0;
	while(n->op == OIND)
		n = n->left;
	if(n->op == ONAME)
		return 1;
	return 0;
}

int
eval(Node *n, int g)
{

	if(n->addable >= INDEXED)
		return D_TREE;
	g = regalloc(n->type, g);
	cgen(n, g, n);
	return g;
}

void	outhist(Biobuf*);
void	zname(Biobuf*, Sym*, int);
void	zaddr(Biobuf*, Adr*, int);
void	zwrite(Biobuf*, Prog*, int, int);

void
outcode(void)
{
	struct { Sym *sym; short type; } h[NSYM];
	Prog *p;
	Sym *s;
	int f, sf, st, t, sym;
	Biobuf b;

	if(debug['S']) {
		for(p = firstp; p != P; p = p->link)
			if(p->as != ADATA && p->as != AGLOBL)
				pc--;
		for(p = firstp; p != P; p = p->link) {
			print("%P\n", p);
			if(p->as != ADATA && p->as != AGLOBL)
				pc++;
		}
	}
	f = open(outfile, OWRITE);
	if(f < 0) {
		diag(Z, "cant open %s", outfile);
		errorexit();
	}
	Binit(&b, f, OWRITE);
	Bseek(&b, 0L, 2);
	outhist(&b);
	for(sym=0; sym<NSYM; sym++) {
		h[sym].sym = S;
		h[sym].type = 0;
	}
	sym = 1;
	for(p = firstp; p != P; p = p->link) {
	jackpot:
		sf = 0;
		s = p->from.sym;
		while(s != S) {
			sf = s->sym;
			if(sf < 0 || sf >= NSYM)
				sf = 0;
			t = p->from.type & D_MASK;
			if(h[sf].type == t)
			if(h[sf].sym == s)
				break;
			s->sym = sym;
			zname(&b, s, t);
			h[sym].sym = s;
			h[sym].type = t;
			sf = sym;
			sym++;
			if(sym >= NSYM)
				sym = 1;
			break;
		}
		st = 0;
		s = p->to.sym;
		while(s != S) {
			st = s->sym;
			if(st < 0 || st >= NSYM)
				st = 0;
			t = p->to.type & D_MASK;
			if(h[st].type == t)
			if(h[st].sym == s)
				break;
			s->sym = sym;
			zname(&b, s, t);
			h[sym].sym = s;
			h[sym].type = t;
			st = sym;
			sym++;
			if(sym >= NSYM)
				sym = 1;
			if(st == sf)
				goto jackpot;
			break;
		}
		zwrite(&b, p, sf, st);
	}
	Bflush(&b);
	close(f);
	firstp = P;
	lastp = P;
}

void
zwrite(Biobuf *b, Prog *p, int sf, int st)
{
	long l;

	l = p->as;
	Bputc(b, l);
	Bputc(b, l>>8);
	l = p->lineno;
	Bputc(b, l);
	Bputc(b, l>>8);
	Bputc(b, l>>16);
	Bputc(b, l>>24);
	zaddr(b, &p->from, sf);
	zaddr(b, &p->to, st);
}

void
zname(Biobuf *b, Sym *s, int t)
{
	char *n;
	ulong sig;

	if(debug['T'] && t == D_EXTERN && s->sig != SIGDONE && s->type != types[TENUM] && s != symrathole){
		sig = sign(s);
		Bputc(b, ASIGNAME);
		Bputc(b, ASIGNAME>>8);
		Bputc(b, sig);
		Bputc(b, sig>>8);
		Bputc(b, sig>>16);
		Bputc(b, sig>>24);
		s->sig = SIGDONE;
	}
	else{
		Bputc(b, ANAME);	/* as */
		Bputc(b, ANAME>>8);	/* as */
	}
	Bputc(b, t);		/* type */
	Bputc(b, s->sym);		/* sym */
	n = s->name;
	while(*n) {
		Bputc(b, *n);
		n++;
	}
	Bputc(b, 0);
}

void
zaddr(Biobuf *b, Adr *a, int s)
{
	long l;
	int i, t;
	char *n;
	Ieee e;

	t = 0;
	if(a->field)
		t |= T_FIELD;
	if(a->index != D_NONE)
		t |= T_INDEX;
	if(s)
		t |= T_SYM;

	switch(a->type) {
	default:
		if(a->offset)
			t |= T_OFFSET;
		if(a->displace)
			t |= T_INDEX;
		if(a->type & ~0xff)
			t |= T_TYPE;
		break;
	case D_FCONST:
		t |= T_FCONST;
		break;
	case D_SCONST:
		t |= T_SCONST;
		break;
	}
	Bputc(b, t);

	if(t & T_FIELD) {	/* implies field */
		i = a->field;
		Bputc(b, i);
		Bputc(b, i>>8);
	}
	if(t & T_INDEX) {	/* implies index, scale, displace */
		i = a->index;
		Bputc(b, i);
		Bputc(b, i>>8);
		Bputc(b, a->scale);
		l = a->displace;
		Bputc(b, l);
		Bputc(b, l>>8);
		Bputc(b, l>>16);
		Bputc(b, l>>24);
	}
	if(t & T_OFFSET) {	/* implies offset */
		l = a->offset;
		Bputc(b, l);
		Bputc(b, l>>8);
		Bputc(b, l>>16);
		Bputc(b, l>>24);
	}
	if(t & T_SYM)		/* implies sym */
		Bputc(b, s);
	if(t & T_FCONST) {
		ieeedtod(&e, a->dval);
		l = e.l;
		Bputc(b, l);
		Bputc(b, l>>8);
		Bputc(b, l>>16);
		Bputc(b, l>>24);
		l = e.h;
		Bputc(b, l);
		Bputc(b, l>>8);
		Bputc(b, l>>16);
		Bputc(b, l>>24);
		return;
	}
	if(t & T_SCONST) {
		n = a->sval;
		for(i=0; i<NSNAME; i++) {
			Bputc(b, *n);
			n++;
		}
		return;
	}
	i = a->type;
	Bputc(b, i);
	if(t & T_TYPE)
		Bputc(b, i>>8);
}



void
outhist(Biobuf *b)
{
	Hist *h;
	char *p, *q, *op, c;
	Prog pg;
	int n;

	pg = zprog;
	pg.as = AHISTORY;
	c = pathchar();
	for(h = hist; h != H; h = h->link) {
		p = h->name;
		op = 0;
		/* on windows skip drive specifier in pathname */
		if(systemtype(Windows) && p && p[1] == ':'){
			p += 2;
			c = *p;
		}
		if(p && p[0] != c && h->offset == 0 && pathname){
			/* on windows skip drive specifier in pathname */
			if(systemtype(Windows) && pathname[1] == ':') {
				op = p;
				p = pathname+2;
				c = *p;
			} else if(pathname[0] == c){
				op = p;
				p = pathname;
			}
		}
		while(p) {
			q = utfrune(p, c);
			if(q) {
				n = q-p;
				if(n == 0){
					n = 1;	/* leading "/" */
					*p = '/';	/* don't emit "\" on windows */
				}
				q++;
			} else {
				n = strlen(p);
				q = 0;
			}
			if(n) {
				Bputc(b, ANAME);
				Bputc(b, ANAME>>8);
				Bputc(b, D_FILE);
				Bputc(b, 1);
				Bputc(b, '<');
				Bwrite(b, p, n);
				Bputc(b, 0);
			}
			p = q;
			if(p == 0 && op) {
				p = op;
				op = 0;
			}
		}
		pg.lineno = h->line;
		pg.to.type = zprog.to.type;
		pg.to.offset = h->offset;
		if(h->offset)
			pg.to.type = D_CONST;

		zwrite(b, &pg, 0, 0);
	}
}

void
ieeedtod(Ieee *ieee, double native)
{
	double fr, ho, f;
	int exp;

	if(native < 0) {
		ieeedtod(ieee, -native);
		ieee->h |= 0x80000000L;
		return;
	}
	if(native == 0) {
		ieee->l = 0;
		ieee->h = 0;
		return;
	}
	fr = frexp(native, &exp);
	f = 2097152L;		/* shouldnt use fp constants here */
	fr = modf(fr*f, &ho);
	ieee->h = ho;
	ieee->h &= 0xfffffL;
	ieee->h |= (exp+1022L) << 20;
	f = 65536L;
	fr = modf(fr*f, &ho);
	ieee->l = ho;
	ieee->l <<= 16;
	ieee->l |= (long)(fr*f);
}

int
nodalloc(Type *t, int g, Node *n)
{

	n->type = t;
	n->op = OREGISTER;
	n->addable = 12;
	n->complex = 0;
	g = regaddr(g);
	n->reg = g | I_INDIR;
	n->xoffset = 0;
	return g;
}

int
mulcon(Node *n, Node *c, int result, Node *nn)
{
	long v;

	if(typefd[n->type->etype])
		return 0;
	v = c->vconst;
	if(mulcon1(n, v, result, nn))
		return 1;
	return 0;
}

int
shlcon(Node *n, Node *c, int result, Node *nn)
{
	long v;

	v = 1L << c->vconst;
	return mulcon1(n, v, result, nn);
}

int
mulcon1(Node *n, long v, int result, Node *nn)
{
	int g, g1, a1, a2, neg;
	int o;
	char code[10], *p;

	if(result == D_NONE)
		return 0;
	neg = 0;
	if(v < 0) {
		v = -v;
		neg++;
	}
	a1 = 0;
	a2 = multabsize;
	for(;;) {
		if(a1 >= a2)
			return 0;
		g1 = (a2 + a1)/2;
		if(v < multab[g1].val) {
			a2 = g1;
			continue;
		}
		if(v > multab[g1].val) {
			a1 = g1+1;
			continue;
		}
		break;
	}
	strcpy(code, "0");
	strncat(code, multab[g1].code, sizeof(multab[0].code));
	p = code;
	if(p[1] == 'i')
		p += 2;
	g = regalloc(n->type, result);
	cgen(n, g, n);
	if(neg)
		gopcode(ONEG, n->type, D_NONE, n, g, n);
	g1 = regalloc(n->type, D_NONE);
loop:
	switch(*p) {
	case 0:
		regfree(g1);
		gmove(n->type, nn->type, g, n, result, nn);
		regfree(g);
		return 1;
	case '0':
		o = OAS;
		*p -= '0';
		goto com;
	case '1':
	case '2':
		o = OSUB;
		*p -= '1';
		goto com;
	case '3':
	case '4':
	case '5':
	case '6':
		o = OADD;
		*p -= '3';
	com:
		a1 = g;
		if(*p == 1 || *p == 3)
			a1 = g1;
		a2 = g;
		if(*p == 0 || *p == 3)
			a2 = g1;
		gopcode(o, n->type, a1, n, a2, n);
		p++;
		break;
	default:
		a1 = *p++ - 'a' + 1;
		a2 = g;
		if(a1 > 8) {
			a2 = g1;
			a1 -= 8;
		}
		gopcode(OASHL, n->type, D_CONST, nodconst(a1), a2, n);
		break;
	}
	goto loop;
}

void
nullwarn(Node *l, Node *r)
{
	warn(Z, "result of operation not used");
	if(l != Z)
		cgen(l, D_NONE, Z);
	if(r != Z)
		cgen(r, D_NONE, Z);
}

void
sextern(Sym *s, Node *a, long o, long w)
{
	long e, lw;

	for(e=0; e<w; e+=NSNAME) {
		lw = NSNAME;
		if(w-e < lw)
			lw = w-e;
		gpseudo(ADATA, s, D_SCONST, 0L);
		p->from.offset += o+e;
		p->from.displace = lw;
		memmove(p->to.sval, a->cstring+e, lw);
	}
}

void
gextern(Sym *s, Node *a, long o, long w)
{
	if(a->op == OCONST && typev[a->type->etype]) {
		gpseudo(ADATA, s, D_CONST, (long)(a->vconst>>32));
		p->from.offset += o;
		p->from.displace = 4;
		gpseudo(ADATA, s, D_CONST, (long)(a->vconst));
		p->from.offset += o + 4;
		p->from.displace = 4;
		return;
	}
	gpseudotree(ADATA, s, a);
	p->from.offset += o;
	p->from.displace = w;
}

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;
		if(packflg)
			w = packflg;
		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_SHORT)
			w = SZ_SHORT;
		if(packflg)
			w = packflg;
		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;
		}
		o += SZ_LONG - w;	/* big endian adjustment */
		w = 1;
		break;

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

	case Aaut3:	/* total allign of automatic */
		o = align(o, t, Ael1);
		o = align(o, t, Ael2);
		break;
	}
	o = round(o, w);
	if(debug['A'])
		print("align %s %ld %T = %ld\n", bnames[op], i, t, o);
	return o;
}

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