#include "limbo.h"

#define	bzero	bbzero	/* bsd name space pollution */
/* 
	(r, s) := f();	=> r, s have def on same pc
	s = g();	=> this def kills previous r def (and s def)
	solution: r has def pc, s has def pc+1 and next instruction has pc pc+2
*/

#define BLEN	(8*sizeof(ulong))
#define BSHIFT	5	/* assumes ulong 4 */
#define BMASK	(BLEN-1)

#define	SIGN(n)		(1<<(n-1))
#define	MSK(n)		(SIGN(n)|(SIGN(n)-1))
#define	MASK(a, b)	(MSK((b)-(a)+1)<<(a))

#define isnilsrc(s)	((s)->start.line == 0 && (s)->stop.line == 0 && (s)->start.pos == 0 && (s)->stop.pos == 0)

#define limbovar(d)	((d)->sym->name[0] != '.')
#define structure(t)	((t)->kind == Tadt || (t)->kind == Ttuple)

enum
{
	Bclr,
	Band,
	Bandinv,
	Bstore,
	Bandrev,
	Bnoop,
	Bxor,
	Bor,
	Bnor,
	Bequiv,
	Binv,
	Bimpby,
	Brev,
	Bimp,
	Bnand,
	Bset,
};

enum
{
	Suse = 1,
	Muse = 2,
	Duse = 4,
	Sdef = 8,
	Mdef = 16,
	Ddef = 32,
	Tuse1 = 64,	/* fixed point temporary */
	Tuse2 = 128,	/* fixed point temporary */
	Mduse = 256,	/* D used if M nil */

	None = 0,
	Unop = Suse|Ddef,
	Cunop = Muse|Ddef,
	Threop = Suse|Muse|Ddef,
	Binop = Suse|Muse|Ddef|Mduse,
	Mbinop = Suse|Mdef|Duse,	/* strange */
	Abinop=Suse|Duse|Ddef,
	Mabinop = Suse|Muse|Duse|Ddef,
	Use1 = Suse,
	Use2 = Suse|Duse,
	Use3 = Suse|Muse|Duse,
};

enum
{
	Sshift = 10,
	Mshift = 5,
	Dshift = 0,
};

#define S(x)	((x)<<Sshift)
#define M(x)	((x)<<Mshift)
#define D(x)	((x)<<Dshift)

#define SS(x)	(((x)>>Sshift)&0x1f)
#define SM(x)	(((x)>>Mshift)&0x1f)
#define SD(x)	(((x)>>Dshift)&0x1f)

enum
{
	I = 0,		/* ignore */
	B = 1,	/* byte */
	W = 4,	/* int */
	P = 4,	/* pointer */
	A = 4,	/* array */
	C = 4,	/* string */
	X = 4,	/* fixed */
	R = 4,	/* float */
	L = 8,	/* big */
	F = 8,	/* real */
	Sh = 2,	/* short */
	Pc = 4,	/* pc */
	Mp = 16,	/* memory */

	Bop2 = S(B)|D(B),
	Bop = S(B)|M(B)|D(B),
	Bopb = S(B)|M(B)|D(Pc),
	Wop2 = S(W)|D(W),
	Wop = S(W)|M(W)|D(W),
	Wopb = S(W)|M(W)|D(Pc),
	Lop2 = S(L)|D(L),
	Lop = S(L)|M(L)|D(L),
	Lopb = S(L)|M(L)|D(Pc),
	Cop2 = Wop2,
	Cop = Wop,
	Copb = Wopb,
	Fop2 = Lop2,
	Fop = Lop,
	Fopb = Lopb,
	Xop = Wop,
};

typedef struct Array Array;
typedef struct Bits Bits;
typedef struct Blist Blist;
typedef struct Block Block;
typedef struct Idlist Idlist;
typedef struct Optab Optab;

struct Array
{
	int	n;
	int	m;
	Block	**a;
};

struct Bits
{
	int	n;
	ulong	*b;
};

struct Blist
{
	Block	*block;
	Blist	*next;
};

struct Block
{
	int	dfn;
	int	flags;
	Inst	*first;
	Inst	*last;
	Block	*prev;
	Block	*next;
	Blist	*pred;
	Blist *succ;
	Bits	kill;
	Bits	gen;
	Bits	in;
	Bits	out;
};

struct Idlist
{
	int id;
	Idlist *next;
};

struct Optab
{
	short flags;
	short size;
};

Block	zblock;
Decl	*regdecls;
Idlist *frelist;
Idlist *deflist;
Idlist *uselist;

static void
addlist(Idlist **hd, int id)
{
	Idlist *il;

	if(frelist == nil)
		il = (Idlist*)malloc(sizeof(Idlist));
	else{
		il = frelist;
		frelist = frelist->next;
	}
	il->id = id;
	il->next = *hd;
	*hd = il;
}

static void
freelist(Idlist **hd)
{
	Idlist *il;

	for(il = *hd; il != nil && il->next != nil; il = il->next)
		;
	if(il != nil){
		il->next = frelist;
		frelist = *hd;
		*hd = nil;
	}
}
	
Optab opflags[] = {
	/* INOP */	None,	0,
	/* IALT */	Unop,	S(Mp)|D(W),
	/* INBALT */	Unop,	S(Mp)|D(W),
	/* IGOTO */	Use2,	S(W)|D(I),
	/* ICALL */	Use2,	S(P)|D(Pc),
	/* IFRAME */	Unop,	S(W)|D(P),
	/* ISPAWN */	Use2,	S(P)|D(Pc),
	/* IRUNT */	None,	0,
	/* ILOAD */	Threop,	S(C)|M(P)|D(P),
	/* IMCALL */	Use3,	S(P)|M(W)|D(P),
	/* IMSPAWN */	Use3,	S(P)|M(W)|D(P),
	/* IMFRAME */	Threop,	S(P)|M(W)|D(P),
	/* IRET */	None,	0,
	/* IJMP */	Duse,	D(Pc),
	/* ICASE */	Use2,	S(W)|D(I),
	/* IEXIT */	None,	0,
	/* INEW */	Unop,	S(W)|D(P),
	/* INEWA */	Threop,	S(W)|M(W)|D(P),
	/* INEWCB */	Cunop,	M(W)|D(P),
	/* INEWCW */	Cunop,	M(W)|D(P),
	/* INEWCF */	Cunop,	M(W)|D(P),
	/* INEWCP */	Cunop,	M(W)|D(P),
	/* INEWCM */	Threop,	S(W)|M(W)|D(P),
	/* INEWCMP */	Threop,	S(W)|M(W)|D(P),
	/* ISEND */	Use2,	S(Mp)|D(P),
	/* IRECV */	Unop,	S(P)|D(Mp),
	/* ICONSB */	Abinop,	S(B)|D(P),
	/* ICONSW */	Abinop,	S(W)|D(P),
	/* ICONSP */	Abinop,	S(P)|D(P),
	/* ICONSF */	Abinop,	S(F)|D(P),
	/* ICONSM */	Mabinop,	S(Mp)|M(W)|D(P),
	/* ICONSMP */	Mabinop,	S(Mp)|M(W)|D(P),
	/* IHEADB */	Unop,	S(P)|D(B),
	/* IHEADW */	Unop,	S(P)|D(W),
	/* IHEADP */	Unop,	S(P)|D(P),
	/* IHEADF */	Unop,	S(P)|D(F),
	/* IHEADM */	Threop,	S(P)|M(W)|D(Mp),
	/* IHEADMP */	Threop,	S(P)|M(W)|D(Mp),
	/* ITAIL */	Unop,	S(P)|D(P),
	/* ILEA */	Ddef,	S(Mp)|D(P),	/* S done specially cos of ALT */
	/* IINDX */	Mbinop,	S(P)|M(P)|D(W),
	/* IMOVP */	Unop,	S(P)|D(P),
	/* IMOVM */	Threop,	S(Mp)|M(W)|D(Mp),
	/* IMOVMP */	Threop,	S(Mp)|M(W)|D(Mp),
	/* IMOVB */	Unop,	Bop2,
	/* IMOVW */	Unop,	Wop2,
	/* IMOVF */	Unop,	Fop2,
	/* ICVTBW */	Unop,	S(B)|D(W),
	/* ICVTWB */	Unop,	S(W)|D(B),
	/* ICVTFW */	Unop,	S(F)|D(W),
	/* ICVTWF */	Unop,	S(W)|D(F),
	/* ICVTCA */	Unop,	S(C)|D(A),
	/* ICVTAC */	Unop,	S(A)|D(C),
	/* ICVTWC */	Unop,	S(W)|D(C),
	/* ICVTCW */	Unop,	S(C)|D(W),
	/* ICVTFC */	Unop,	S(F)|D(C),
	/* ICVTCF */	Unop,	S(C)|D(F),
	/* IADDB */	Binop,	Bop,
	/* IADDW */	Binop,	Wop,
	/* IADDF */	Binop,	Fop,
	/* ISUBB */	Binop,	Bop,
	/* ISUBW */	Binop,	Wop,
	/* ISUBF */	Binop,	Fop,
	/* IMULB */	Binop,	Bop,
	/* IMULW */	Binop,	Wop,
	/* IMULF */	Binop,	Fop,
	/* IDIVB */	Binop,	Bop,
	/* IDIVW */	Binop,	Wop,
	/* IDIVF */	Binop,	Fop,
	/* IMODW */	Binop,	Wop,
	/* IMODB */	Binop,	Bop,
	/* IANDB */	Binop,	Bop,
	/* IANDW */	Binop,	Wop,
	/* IORB */	Binop,	Bop,
	/* IORW */	Binop,	Wop,
	/* IXORB */	Binop,	Bop,
	/* IXORW */	Binop,	Wop,
	/* ISHLB */	Binop,	S(W)|M(B)|D(B),
	/* ISHLW */	Binop,	Wop,
	/* ISHRB */	Binop,	S(W)|M(B)|D(B),
	/* ISHRW */	Binop,	Wop,
	/* IINSC */	Mabinop,	S(W)|M(W)|D(C),
	/* IINDC */	Threop,	S(C)|M(W)|D(W),
	/* IADDC */	Binop,	Cop,
	/* ILENC */	Unop,	S(C)|D(W),
	/* ILENA */	Unop,	S(A)|D(W),
	/* ILENL */	Unop,	S(P)|D(W),
	/* IBEQB */	Use3,	Bopb,
	/* IBNEB */	Use3,	Bopb,
	/* IBLTB */	Use3,	Bopb,
	/* IBLEB */	Use3,	Bopb,
	/* IBGTB */	Use3,	Bopb,
	/* IBGEB */	Use3,	Bopb,
	/* IBEQW */	Use3,	Wopb,
	/* IBNEW */	Use3,	Wopb,
	/* IBLTW */	Use3,	Wopb,
	/* IBLEW */	Use3,	Wopb,
	/* IBGTW */	Use3,	Wopb,
	/* IBGEW */	Use3,	Wopb,
	/* IBEQF */	Use3,	Fopb,
	/* IBNEF */	Use3,	Fopb,
	/* IBLTF */	Use3,	Fopb,
	/* IBLEF */	Use3,	Fopb,
	/* IBGTF */	Use3,	Fopb,
	/* IBGEF */	Use3,	Fopb,
	/* IBEQC */	Use3,	Copb,
	/* IBNEC */	Use3,	Copb,
	/* IBLTC */	Use3,	Copb,
	/* IBLEC */	Use3,	Copb,
	/* IBGTC */	Use3,	Copb,
	/* IBGEC */	Use3,	Copb,
	/* ISLICEA */	Mabinop,	S(W)|M(W)|D(P),
	/* ISLICELA */	Use3,	S(P)|M(W)|D(P),
	/* ISLICEC */	Mabinop,	S(W)|M(W)|D(C),
	/* IINDW */	Mbinop,	S(P)|M(P)|D(W),
	/* IINDF */	Mbinop,	S(P)|M(P)|D(W),
	/* IINDB */	Mbinop,	S(P)|M(P)|D(W),
	/* INEGF */	Unop,	Fop2,
	/* IMOVL */	Unop,	Lop2,
	/* IADDL */	Binop,	Lop,
	/* ISUBL */	Binop,	Lop,
	/* IDIVL */	Binop,	Lop,
	/* IMODL */	Binop,	Lop,
	/* IMULL */	Binop,	Lop,
	/* IANDL */	Binop,	Lop,
	/* IORL */	Binop,	Lop,
	/* IXORL */	Binop,	Lop,
	/* ISHLL */	Binop,	S(W)|M(L)|D(L),
	/* ISHRL */	Binop,	S(W)|M(L)|D(L),
	/* IBNEL */	Use3,	Lopb,
	/* IBLTL */	Use3,	Lopb,
	/* IBLEL */	Use3,	Lopb,
	/* IBGTL */	Use3,	Lopb,
	/* IBGEL */	Use3,	Lopb,
	/* IBEQL */	Use3,	Lopb,
	/* ICVTLF */	Unop,	S(L)|D(F),
	/* ICVTFL */	Unop,	S(F)|D(L),
	/* ICVTLW */	Unop,	S(L)|D(W),
	/* ICVTWL */	Unop,	S(W)|D(L),
	/* ICVTLC */	Unop,	S(L)|D(C),
	/* ICVTCL */	Unop,	S(C)|D(L),
	/* IHEADL */	Unop,	S(P)|D(L),
	/* ICONSL */	Abinop,	S(L)|D(P),
	/* INEWCL */	Cunop,	M(W)|D(P),
	/* ICASEC */	Use2,	S(C)|D(I),
	/* IINDL */	Mbinop,	S(P)|M(P)|D(W),
	/* IMOVPC */	Unop,	S(W)|D(P),
	/* ITCMP */	Use2,	S(P)|D(P),
	/* IMNEWZ */	Threop,	S(P)|M(W)|D(P),
	/* ICVTRF */	Unop,	S(R)|D(F),
	/* ICVTFR */	Unop,	S(F)|D(R),
	/* ICVTWS */	Unop,	S(W)|D(Sh),
	/* ICVTSW */	Unop,	S(Sh)|D(W),
	/* ILSRW */	Binop,	Wop,
	/* ILSRL */	Binop,	S(W)|M(L)|D(L),
	/* IECLR */	None,	0,
	/* INEWZ */	Unop,	S(W)|D(P),
	/* INEWAZ */	Threop,	S(W)|M(W)|D(P),
	/* IRAISE */	Use1,	S(P),
	/* ICASEL */	Use2,	S(L)|D(I),
	/* IMULX */	Binop|Tuse2,	Xop,
	/* IDIVX */	Binop|Tuse2,	Xop,
	/* ICVTXX */	Threop,	Xop,
	/* IMULX0 */	Binop|Tuse1|Tuse2,	Xop,
	/* IDIVX0 */	Binop|Tuse1|Tuse2,	Xop,
	/* ICVTXX0 */	Threop|Tuse1,	Xop,
	/* IMULX1 */	Binop|Tuse1|Tuse2,	Xop,
	/* IDIVX1 */	Binop|Tuse1|Tuse2,	Xop,
	/* ICVTXX1 */	Threop|Tuse1,	Xop,
	/* ICVTFX */	Threop,	S(F)|M(F)|D(X),
	/* ICVTXF */	Threop,	S(X)|M(F)|D(F),
	/* IEXPW */	Binop,	S(W)|M(W)|D(W),
	/* IEXPL */	Binop,	S(W)|M(L)|D(L),
	/* IEXPF */	Binop,	S(W)|M(F)|D(F),
	/* ISELF */	Ddef,	D(P),
	/* IEXC */		None,		0,
	/* IEXC0 */	None,		0,
	/* INOOP */	None,		0,
};

/*
static int
pop(int i)
{
	i = (i & 0x55555555) + ((i>>1) & 0x55555555);
	i = (i & 0x33333333) + ((i>>2) & 0x33333333);
	i = (i & 0x0F0F0F0F) + ((i>>4) & 0x0F0F0F0F);
	i = (i & 0x00FF00FF) + ((i>>8) & 0x00FF00FF);
	i = (i & 0x0000FFFF) + ((i>>16) & 0x0000FFFF);
	return i;
}
*/

static int
bitc(uint x)
{
	uint n;

	n = (x>>1)&0x77777777;
	x -= n;
	n = (n>>1)&0x77777777;
	x -= n;
	n = (n>>1)&0x77777777;
	x -= n;
	x = (x+(x>>4))&0x0f0f0f0f;
	x *= 0x01010101;
	return x>>24;
}

/*
static int
top(uint x)
{
	int i;

	for(i = -1; x; i++)
		x >>= 1;
	return i;
}
*/

static int
topb(uint x)
{
	int i;

	if(x == 0)
		return -1;
	i = 0;
	if(x&0xffff0000){
		i |= 16;
		x >>= 16;
	}
	if(x&0xff00){
		i |= 8;
		x >>= 8;
	}
	if(x&0xf0){
		i |= 4;
		x >>= 4;
	}
	if(x&0xc){
		i |= 2;
		x >>= 2;
	}
	if(x&0x2)
		i |= 1;
	return i;
}

/*
static int
lowb(uint x)
{
	int i;

	if(x == 0)
		return -1;
	for(i = BLEN; x; i--)
		x <<= 1;
	return i;
}
*/

static int
lowb(uint x)
{
	int i;

	if(x == 0)
		return -1;
	i = 0;
	if((x&0xffff) == 0){
		i |= 16;
		x >>= 16;
	}
	if((x&0xff) == 0){
		i |= 8;
		x >>= 8;
	}
	if((x&0xf) == 0){
		i |= 4;
		x >>= 4;
	}
	if((x&0x3) == 0){
		i |= 2;
		x >>= 2;
	}
	return i+1-(x&1);
}
		
static void
pbit(int x, int n)
{
	int i, m;

	m = 1;
	for(i = 0; i < BLEN; i++){
		if(x&m)
			print("%d ", i+n);
		m <<= 1;
	}
}

static ulong
bop(int o, ulong s, ulong d)
{
	switch(o){
	case Bclr:	return 0;
	case Band:	return s & d;
	case Bandinv:	return s & ~d;
	case Bstore:	return s;
	case Bandrev:	return ~s & d;
	case Bnoop:	return d;
	case Bxor:	return s ^ d;
	case Bor:	return s | d;
	case Bnor:	return ~(s | d);
	case Bequiv:	return ~(s ^ d);
	case Binv:	return ~d;
	case Bimpby:	return s | ~d;
	case Brev:	return ~s;
	case Bimp:	return ~s | d;
	case Bnand:	return ~(s & d);
	case Bset:	return 0xffffffff;
	}
	return 0;
}

static Bits
bnew(int n, int bits)
{
	Bits b;

	if(bits)
		b.n = (n+BLEN-1)>>BSHIFT;
	else
		b.n = n;
	b.b = allocmem(b.n*sizeof(ulong));
	memset(b.b, 0, b.n*sizeof(ulong));
	return b;
}

static void
bfree(Bits b)
{
	free(b.b);
}

static void
bset(Bits b, int n)
{
	b.b[n>>BSHIFT] |= 1<<(n&BMASK);
}

static void
bclr(Bits b, int n)
{
	b.b[n>>BSHIFT] &= ~(1<<(n&BMASK));
}

static int
bmem(Bits b, int n)
{
	return b.b[n>>BSHIFT] & (1<<(n&BMASK));
}

static void
bsets(Bits b, int m, int n)
{
	int i, c1, c2;

	c1 = m>>BSHIFT;
	c2 = n>>BSHIFT;
	m &= BMASK;
	n &= BMASK;
	if(c1 == c2){
		b.b[c1] |= MASK(m, n);
		return;
	}
	for(i = c1+1; i < c2; i++)
		b.b[i] = 0xffffffff;
	b.b[c1] |= MASK(m, BLEN-1);
	b.b[c2] |= MASK(0, n);
}

static void
bclrs(Bits b, int m, int n)
{
	int i, c1, c2;

	if(n < 0)
		n = (b.n<<BSHIFT)-1;
	c1 = m>>BSHIFT;
	c2 = n>>BSHIFT;
	m &= BMASK;
	n &= BMASK;
	if(c1 == c2){
		b.b[c1] &= ~MASK(m, n);
		return;
	}
	for(i = c1+1; i < c2; i++)
		b.b[i] = 0;
	b.b[c1] &= ~MASK(m, BLEN-1);
	b.b[c2] &= ~MASK(0, n);
}
	
/* b = a op b */
static Bits
boper(int o, Bits a, Bits b)
{
	int i, n;

	n = a.n;
	if(b.n != n)
		fatal("boper %d %d %d", o, a.n, b.n);
	for(i = 0; i < n; i++)
		b.b[i] = bop(o, a.b[i], b.b[i]);
	return b;
}

static int
beq(Bits a, Bits b)
{
	int i, n;

	n = a.n;
	for(i = 0; i < n; i++)
		if(a.b[i] != b.b[i])
			return 0;
	return 1;
}

static int
bzero(Bits b)
{
	int i, n;

	n = b.n;
	for(i = 0; i < n; i++)
		if(b.b[i] != 0)
			return 0;
	return 1;
}

static int
bitcnt(Bits b)
{
	int i, m, n;

	m = b.n;
	n = 0;
	for(i = 0; i < m; i++)
		n += bitc(b.b[i]);
	return n;
}

static int
topbit(Bits b)
{
	int i, n;

	n = b.n;
	for(i = n-1; i >= 0; i--)
		if(b.b[i] != 0)
			return (i<<BSHIFT)+topb(b.b[i]);
	return -1;
}

static int
lowbit(Bits b)
{
	int i, n;

	n = b.n;
	for(i = 0; i < n; i++)
		if(b.b[i] != 0)
			return (i<<BSHIFT)+lowb(b.b[i]);
	return -1;
}

static void
pbits(Bits b)
{
	int i, n;

	n = b.n;
	for(i = 0; i < n; i++)
		pbit(b.b[i], i<<BSHIFT);
}

static char*
decname(Decl *d)
{
	if(d->sym == nil)
		return "<nil>";
	return d->sym->name;
}

static void
warning(Inst *i, char *s, Decl *d, Decl *sd)
{
	int n;
	char *f;
	Decl *ds;

	n = 0;
	for(ds = sd; ds != nil; ds = ds->next)
		if(ds->link == d)
			n += strlen(ds->sym->name)+1;
	if(n == 0){
		warn(i->src.start, "%s: %s", d->sym->name, s);
		return;
	}
	n += strlen(d->sym->name);
	f = malloc(n+1);
	strcpy(f, d->sym->name);
	for(ds = sd; ds != nil; ds = ds->next){
		if(ds->link == d){
			strcat(f, "/");
			strcat(f, ds->sym->name);
		}
	}
	warn(i->src.start, "%s: %s", f, s);
	free(f);
}

static int
inspc(Inst *in)
{
	int n;
	Inst *i;

	n = 0;
	for(i = in; i != nil; i = i->next)
		i->pc = n++;
	return n;
}

static Inst*
pc2i(Block *b, int pc)
{
	Inst *i;

	for( ; b != nil; b = b->next){
		if(pc > b->last->pc)
			continue;
		for(i = b->first; ; i = i->next){
			if(i->pc == pc)
				return i;
			if(i == b->last)
				fatal("pc2i a");
		}
	}
	fatal("pc2i b");
	return nil;
}

static void
padr(int am, Addr *a, Inst *br)
{
	long reg;

	if(br != nil){
		print("$%ld", br->pc);
		return;
	}
	reg = a->reg;
	if(a->decl != nil && am != Adesc)
		reg += a->decl->offset;
	switch(am){
	case Anone:
		print("-");
		break;
	case Aimm:
	case Apc:
	case Adesc:
		print("$%ld", a->offset);
		break;
	case Aoff:
		print("$%ld", a->decl->iface->offset);
		break;
	case Anoff:
		print("-$%ld", a->decl->iface->offset);
		break;
	case Afp:
		print("%ld(fp)", reg);
		break;
	case Afpind:
		print("%ld(%ld(fp))", a->offset, reg);
		break;
	case Amp:
		print("%ld(mp)", reg);
		break;
	case Ampind:
		print("%ld(%ld(mp))", a->offset, reg);
		break;
	case Aldt:
		print("$%ld", reg);
		break;
	case Aerr:
	default:
		print("%ld(%ld(?%d?))", a->offset, reg, am);
		break;
	}
}

static void
pins(Inst *i)
{
	/* print("%L		%ld	", i->src.start, i->pc); */
	print("		%ld	", i->pc);
	if(i->op < MAXDIS)
		print("%s", instname[i->op]);
	else
		print("noop");
	print("	");
	padr(i->sm, &i->s, nil);
	print(", ");
	padr(i->mm, &i->m, nil);
	print(", ");
	padr(i->dm, &i->d, i->branch);
	print("\n");
}

static void
blfree(Blist *bl)
{
	Blist *nbl;

	for( ; bl != nil; bl = nbl){
		nbl = bl->next;
		free(bl);
	}
}

static void
freebits(Bits *bs, int nv)
{
	int i;

	for(i = 0; i < nv; i++)
		bfree(bs[i]);
	free(bs);
}

static void
freeblks(Block *b)
{
	Block *nb;

	for( ; b != nil; b = nb){
		blfree(b->pred);
		blfree(b->succ);
		bfree(b->kill);
		bfree(b->gen);
		bfree(b->in);
		bfree(b->out);
		nb = b->next;
		free(b);
	}
}

static int
len(Decl *d)
{
	int n;

	n = 0;
	for( ; d != nil; d = d->next)
		n++;
	return n;
}

static Bits*
allocbits(int nv, int npc)
{
	int i;
	Bits *defs;

	defs = (Bits*)allocmem(nv*sizeof(Bits));
	for(i = 0; i < nv; i++)
		defs[i] = bnew(npc, 1);
	return defs;
}

static int
bitcount(Bits *bs, int nv)
{
	int i, n;

	n = 0;
	for(i = 0; i < nv; i++)
		n += bitcnt(bs[i]);
	return n;
}

static Block*
mkblock(Inst *i)
{
	Block *b;

	b = allocmem(sizeof(Block));
	*b = zblock;
	b->first = b->last = i;
	return b;
}

static Blist*
mkblist(Block *b, Blist *nbl)
{
	Blist *bl;

	bl = allocmem(sizeof(Blist));
	bl->block = b;
	bl->next = nbl;
	return bl;
}

static void
leader(Inst *i, Array *ab)
{
	int m, n;
	Block *b, **a;

	if(i != nil && i->pc == 0){
		if((n = ab->n) == (m = ab->m)){
			a = ab->a;
			ab->a = allocmem(2*m*sizeof(Block*));
			memcpy(ab->a, a, m*sizeof(Block*));
			ab->m = 2*m;
			free(a);
		}
		b = mkblock(i);
		b->dfn = n;
		ab->a[n] = b;
		i->pc = ab->n = n+1;
	}
}

static Block*
findb(Inst *i, Array *ab)
{
	if(i == nil)
		return nil;
	if(i->pc <= 0)
		fatal("pc <= 0 in findb");
	return ab->a[i->pc-1];
}

static int
memb(Block *b, Blist *bl)
{
	for( ; bl != nil; bl = bl->next)
		if(bl->block == b)
			return 1;
	return 0;
}

static int
canfallthrough(Inst *i)
{
	if(i == nil)
		return 0;
	switch(i->op){
	case IGOTO:
	case ICASE:
	case ICASEL:
	case ICASEC:
	case IRET:
	case IEXIT:
	case IRAISE:
	case IJMP:
		return 0;
	case INOOP:
		return i->branch != nil;
	}
	return 1;
}

static void
predsucc(Block *b1, Block *b2)
{
	if(b1 == nil || b2 == nil)
		return;
	if(!memb(b1, b2->pred))
		b2->pred = mkblist(b1, b2->pred);
	if(!memb(b2, b1->succ))
		b1->succ = mkblist(b2, b1->succ);
}
	
static Block*
mkblocks(Inst *in, int *nb)
{
	Inst *i;
	Block *b, *firstb, *lastb;
	Label *lab;
	Array *ab;
	int j, n;

	ab = allocmem(sizeof(Array));
	ab->n = 0;
	ab->m = 16;
	ab->a = allocmem(ab->m*sizeof(Block*));
	leader(in, ab);
	for(i = in; i != nil; i = i->next){
		switch(i->op){
		case IGOTO:
		case ICASE:
		case ICASEL:
		case ICASEC:
		case INOOP:
			if(i->op == INOOP && i->branch != nil){
				leader(i->branch, ab);
				leader(i->next, ab);
				break;
			}
			leader(i->d.decl->ty->cse->iwild, ab);
			lab = i->d.decl->ty->cse->labs;
			n = i->d.decl->ty->cse->nlab;
			for(j = 0; j < n; j++)
				leader(lab[j].inst, ab);
			leader(i->next, ab);
			break;
		case IRET:
		case IEXIT:
		case IRAISE:
			leader(i->next, ab);
			break;
		case IJMP:
			leader(i->branch, ab);
			leader(i->next, ab);
			break;
		default:
			if(i->branch != nil){
				leader(i->branch, ab);
				leader(i->next, ab);
			}
			break;
		}
	}
	firstb = lastb = mkblock(nil);
	for(i = in; i != nil; i = i->next){
		if(i->pc != 0){
			b = findb(i, ab);
			b->prev = lastb;
			lastb->next = b;
			if(canfallthrough(lastb->last))
				predsucc(lastb, b);
			lastb = b;
		}
		else
			lastb->last = i;
		switch(i->op){
		case IGOTO:
		case ICASE:
		case ICASEL:
		case ICASEC:
		case INOOP:
			if(i->op == INOOP && i->branch != nil){
				b = findb(i->next, ab);
				predsucc(lastb, b);
				b = findb(i->branch, ab);
				predsucc(lastb, b);
				break;
			}
			b = findb(i->d.decl->ty->cse->iwild, ab);
			predsucc(lastb, b);
			lab = i->d.decl->ty->cse->labs;
			n = i->d.decl->ty->cse->nlab;
			for(j = 0; j < n; j++){
				b = findb(lab[j].inst, ab);
				predsucc(lastb, b);
			}
			break;
		case IRET:
		case IEXIT:
		case IRAISE:
			break;
		case IJMP:
			b = findb(i->branch, ab);
			predsucc(lastb, b);
			break;
		default:
			if(i->branch != nil){
				b = findb(i->next, ab);
				predsucc(lastb, b);
				b = findb(i->branch, ab);
				predsucc(lastb, b);
			}
			break;
		}
	}
	*nb = ab->n;
	free(ab->a);
	free(ab);
	b = firstb->next;
	b->prev = nil;
	return b;
}

static int
back(Block *b1, Block *b2)
{
	return b1->dfn >= b2->dfn;
}

static void
pblocks(Block *b, int nb)
{
	Inst *i;
	Blist *bl;

	print("--------------------%d blocks--------------------\n", nb);
	print("------------------------------------------------\n");
	for( ; b != nil; b = b->next){
		print("dfn=%d\n", b->dfn);
		print("    pred	");
		for(bl = b->pred; bl != nil; bl = bl->next)
			print("%d%s ", bl->block->dfn, back(bl->block, b) ? "*" : "");
		print("\n");
		print("    succ	");
		for(bl = b->succ; bl != nil; bl = bl->next)
			print("%d%s ", bl->block->dfn, back(b, bl->block) ? "*" : "");
		print("\n");
		for(i = b->first; i != nil; i = i->next){
			// print("	%I\n", i);
			pins(i);
			if(i == b->last)
				break;
		}
	}
	print("------------------------------------------------\n");
}

static void
ckblocks(Inst *in, Block *b, int nb)
{
	int n;
	Block *lastb;

	if(b->first != in)
		fatal("A - %d", b->dfn);
	n = 0;
	lastb = nil;
	for( ; b != nil; b = b->next){
		n++;
		if(b->prev != lastb)
			fatal("a - %d\n", b->dfn);
		if(b->prev != nil && b->prev->next != b)
			fatal("b - %d\n", b->dfn);
		if(b->next != nil && b->next->prev != b)
			fatal("c - %d\n", b->dfn);

		if(b->prev != nil && b->prev->last->next != b->first)
			fatal("B - %d\n", b->dfn);
		if(b->next != nil && b->last->next != b->next->first)
			fatal("C - %d\n", b->dfn);
		if(b->next == nil && b->last->next != nil)
			fatal("D - %d\n", b->dfn);

		if(b->last->branch != nil && b->succ->block->first != b->last->branch)
			fatal("0 - %d\n", b->dfn);

		lastb = b;
	}
	if(n != nb)
		fatal("N - %d %d\n", n, nb);
}

static void
dfs0(Block *b, int *n)
{
	Block *s;
	Blist *bl;

	b->flags = 1;
	for(bl = b->succ; bl != nil; bl = bl->next){
		s = bl->block;
		if(s->flags == 0)
			dfs0(s, n);
	}
	b->dfn = --(*n);
}

static int
dfs(Block *b, int nb)
{
	int n, u;
	Block *b0;

	b0 = b;
	n = nb;
	dfs0(b0, &n);
	u = 0;
	for(b = b0; b != nil; b = b->next){
		if(b->flags == 0){	/* unreachable: see foldbranch */
			fatal("found unreachable code");
			u++;
			b->prev->next = b->next;
			if(b->next){
				b->next->prev = b->prev;
				b->prev->last->next = b->next->first;
			}
			else
				b->prev->last->next = nil;
		}
		b->flags = 0;
	}
	if(u){
		for(b = b0; b != nil; b = b->next)
			b->dfn -= u;
	}
	return nb-u;
}

static void
loop0(Block *b)
{
	Block *p;
	Blist *bl;

	b->flags = 1;
	for(bl = b->pred; bl != nil; bl = bl->next){
		p = bl->block;
		if(p->flags == 0)
			loop0(p);
	}
}

/* b1->b2 a back edge */
static void
loop(Block *b, Block *b1, Block *b2)
{
	if(0 && debug['o'])
		print("back edge %d->%d\n", b1->dfn, b2->dfn);
	b2->flags = 1;
	if(b1->flags == 0)
		loop0(b1);
	if(0 && debug['o'])
		print("	loop	");
	for( ; b != nil; b = b->next){
		if(b->flags && 0 && debug['o'])
			print("%d ", b->dfn);
		b->flags = 0;
	}
	if(0 && debug['o'])
		print("\n");
}

static void
loops(Block *b)
{
	Block *b0;
	Blist *bl;

	b0 = b;
	for( ; b != nil; b = b->next){
		for(bl = b->succ; bl != nil; bl = bl->next){
			if(back(b, bl->block))
				loop(b0, b, bl->block);
		}
	}
}

static int
imm(int m, Addr *a)
{
	if(m == Aimm)
		return a->offset;
	fatal("bad immediate value");
	return -1;
}

static int
desc(int m, Addr *a)
{
	if(m == Adesc)
		return a->decl->desc->size;
	fatal("bad descriptor value");
	return -1;
}

static int
fpoff(int m, Addr *a)
{
	int off;
	Decl *d;

	if(m == Afp || m == Afpind){
		off = a->reg;
		if((d = a->decl) != nil)
			off += d->offset;
		return off;
	}
	return -1;
}

static int
size(Inst *i)
{
	switch(i->op){
	case ISEND:
	case IRECV:
	case IALT:
	case INBALT:
	case ILEA:
		return i->m.offset;
	case IMOVM:
	case IHEADM:
	case ICONSM:
		return imm(i->mm, &i->m);
	case IMOVMP:
	case IHEADMP:
	case ICONSMP:
		return desc(i->mm, &i->m);
		break;
	}
	fatal("bad op in size");
	return -1;
}

static Decl*
mkdec(int o)
{
	Decl *d;

	d = mkdecl(&nosrc, Dlocal, tint);
	d->offset = o;
	return d;
}

static void
mkdecls(void)
{
	regdecls = mkdec(REGRET*IBY2WD);
	regdecls->next = mkdec(STemp);
	regdecls->next->next = mkdec(DTemp);
}

static Decl*
sharedecls(Decl *d)
{
	Decl *ld;

	ld = d;
	for(d = d->next ; d != nil; d = d->next){
		if(d->offset <= ld->offset)
			break;
		ld = d;
	}
	return d;
}

static int
finddec(int o, int s, Decl *vars, int *nv, Inst *i)
{
	int m, n;
	Decl *d;

	n = 0;
	for(d = vars; d != nil; d = d->next){
		if(o >= d->offset && o < d->offset+d->ty->size){
			m = 1;
			while(o+s > d->offset+d->ty->size){
				m++;
				d = d->next;
			}
			*nv = m;
			return n;
		}
		n++;
	}
	// print("%d %d missing\n", o, s);
	pins(i);
	fatal("missing decl");
	return -1;
}

static void
setud(Bits *b, int id, int n, int pc)
{
	if(id < 0)
		return;
	while(--n >= 0)
		bset(b[id++], pc);
}

static void
ud(Inst *i, Decl *vars, Bits *uses, Bits *defs)
{
	ushort f;
	int id, j, nv, pc, sz, s, m, d, ss, sm, sd;
	Optab *t;
	Idlist *l;

	pc = i->pc;
	ss = 0;
	t = &opflags[i->op];
	f = t->flags;
	sz = t->size;
	s = fpoff(i->sm, &i->s);
	m = fpoff(i->mm, &i->m);
	d = fpoff(i->dm, &i->d);
	if(f&Mduse && i->mm == Anone)
		f |= Duse;
	if(s >= 0){
		if(i->sm == Afp){
			ss = SS(sz);
			if(ss == Mp)
				ss = size(i);
		}
		else
			ss = IBY2WD;
		id = finddec(s, ss, vars, &nv, i);
		if(f&Suse)
			setud(uses, id, nv, pc);
		if(f&Sdef){
			if(i->sm == Afp)
				setud(defs, id, nv, pc);
			else
				setud(uses, id, nv, pc);
		}
	}
	if(m >= 0){
		if(i->mm == Afp){
			sm = SM(sz);
			if(sm == Mp)
				sm = size(i);
		}
		else
			sm = IBY2WD;
		id = finddec(m, sm, vars, &nv, i);
		if(f&Muse)
			setud(uses, id, nv, pc);
		if(f&Mdef){
			if(i->mm == Afp)
				setud(defs, id, nv, pc);
			else
				setud(uses, id, nv, pc);
		}
	}
	if(d >= 0){
		if(i->dm == Afp){
			sd = SD(sz);
			if(sd == Mp)
				sd = size(i);
		}
		else
			sd = IBY2WD;
		id = finddec(d, sd, vars, &nv, i);
		if(f&Duse)
			setud(uses, id, nv, pc);
		if(f&Ddef){
			if(i->dm == Afp)
				setud(defs, id, nv, pc);
			else
				setud(uses, id, nv, pc);
		}
	}
	if(f&Tuse1){
		id = finddec(STemp, IBY2WD, vars, &nv, i);
		setud(uses, id, nv, pc);
	}
	if(f&Tuse2){
		id = finddec(DTemp, IBY2WD, vars, &nv, i);
		setud(uses, id, nv, pc);
	}
	if(i->op == ILEA){
		if(s >= 0){
			id = finddec(s, ss, vars, &nv, i);
			if(i->sm == Afp && i->m.reg == 0)
				setud(defs, id, nv, pc);
			else
				setud(uses, id, nv, pc);
		}
	}
	if(0)
	switch(i->op){
	case ILEA:
		if(s >= 0){
			id = finddec(s, ss, vars, &nv, i);
			if(id < 0)
				break;
			for(j = 0; j < nv; j++){
				if(i->sm == Afp && i->m.reg == 0)
					addlist(&deflist, id++);
				else
					addlist(&uselist, id++);
			}
		}
		break;
	case IALT:
	case INBALT:
	case ICALL:
	case IMCALL:
		for(l = deflist; l != nil; l = l->next){
			id = l->id;
			bset(defs[id], pc);
		}
		for(l = uselist; l != nil; l = l->next){
			id = l->id;
			bset(uses[id], pc);
		}
		freelist(&deflist);
		freelist(&uselist);
		break;
	}
}

static void
usedef(Inst *in, Decl *vars, Bits *uses, Bits *defs)
{
	Inst *i;

	for(i = in; i != nil; i = i->next)
		ud(i, vars, uses, defs);
}

static void
pusedef(Bits *ud, int nv, Decl *d, char *s)
{
	int i;

	print("%s\n", s);
	for(i = 0; i < nv; i++){
		if(!bzero(ud[i])){
			print("\t%s(%ld):	", decname(d), d->offset);
			pbits(ud[i]);
			print("\n");
		}
		d = d->next;
	}
}

static void
dummydefs(Bits *defs, int nv, int npc)
{
	int i;

	for(i = 0; i < nv; i++)
		bset(defs[i], npc++);
}

static void
dogenkill(Block *b, Bits *defs, int nv)
{
	int i, n, t;
	Bits v;

	n = defs[0].n;
	v = bnew(n, 0);
	for( ; b != nil; b = b->next){
		b->gen = bnew(n, 0);
		b->kill = bnew(n, 0);
		b->in = bnew(n, 0);
		b->out = bnew(n, 0);
		for(i = 0; i < nv; i++){
			boper(Bclr, v, v);
			bsets(v, b->first->pc, b->last->pc);
			boper(Band, defs[i], v);
			t = topbit(v);
			if(t >= 0)
				bset(b->gen, t);
			else
				continue;
			boper(Bclr, v, v);
			bsets(v, b->first->pc, b->last->pc);
			boper(Binv, v, v);
			boper(Band, defs[i], v);
			boper(Bor, v, b->kill);
		}
	}
	bfree(v);
}

static void
udflow(Block *b, int nv, int npc)
{
	int iter;
	Block *b0, *p;
	Blist *bl;
	Bits newin;

	b0 = b;
	for(b = b0; b != nil; b = b->next)
		boper(Bstore, b->gen, b->out);
	newin = bnew(b0->in.n, 0);
	iter = 1;
	while(iter){
		iter = 0;
		for(b = b0; b != nil; b = b->next){
			boper(Bclr, newin, newin);
			for(bl = b->pred; bl != nil; bl = bl->next){
				p = bl->block;
				boper(Bor, p->out, newin);
			}
			if(b == b0)
				bsets(newin, npc, npc+nv-1);
			if(!beq(b->in, newin))
				iter = 1;
			boper(Bstore, newin, b->in);
			boper(Bstore, b->in, b->out);
			boper(Bandrev, b->kill, b->out);
			boper(Bor, b->gen, b->out);
		}
	}
	bfree(newin);
}

static void
pflows(Block *b)
{
	for( ; b != nil; b = b->next){
		print("block %d\n", b->dfn);
		print("	gen:	"); pbits(b->gen); print("\n");
		print("	kill:	"); pbits(b->kill); print("\n");
		print("	in:	"); pbits(b->in); print("\n");
		print("	out:	"); pbits(b->out); print("\n");
	}
}

static int
set(Decl *d)
{
	if(d->store == Darg)
		return 1;
	if(d->sym == nil)	/* || d->sym->name[0] == '.') */
		return 1;
	if(tattr[d->ty->kind].isptr || d->ty->kind == Texception)
		return 1;
	return 0;
}

static int
used(Decl *d)
{
	if(d->sym == nil )	/* || d->sym->name[0] == '.') */
		return 1;
	return 0;
}

static void
udchain(Block *b, Decl *ds, int nv, int npc, Bits *defs, Bits *uses, Decl *sd)
{
	int i, n, p, q;
	Bits d, u, dd, ud;
	Block *b0;
	Inst *in;

	b0 = b;
	n = defs[0].n;
	u = bnew(n, 0);
	d = bnew(n, 0);
	dd = bnew(n, 0);
	ud = bnew(n, 0);
	for(i = 0; i < nv; i++){
		boper(Bstore, defs[i], ud);
		bclr(ud, npc+i);
		for(b = b0 ; b != nil; b = b->next){
			boper(Bclr, u, u);
			bsets(u, b->first->pc, b->last->pc);
			boper(Band, uses[i], u);
			boper(Bclr, d, d);
			bsets(d, b->first->pc, b->last->pc);
			boper(Band, defs[i], d);
			for(;;){
				p = topbit(u);
				if(p < 0)
					break;
				bclr(u, p);
				bclrs(d, p, -1);
				q = topbit(d);
				if(q >= 0){
					bclr(ud, q);
					if(debug['o'])
						print("udc b=%d v=%d(%s/%ld) u=%d d=%d\n", b->dfn, i, decname(ds), ds->offset, p, q);
				}
				else{
					boper(Bstore, defs[i], dd);
					boper(Band, b->in, dd);
					boper(Bandrev, dd, ud);
					if(!bzero(dd)){
						if(debug['o']){
							print("udc b=%d v=%d(%s/%ld) u=%d d=", b->dfn, i, decname(ds), ds->offset, p);
							pbits(dd);
							print("\n");
						}
						if(bmem(dd, npc+i) && !set(ds))
							warning(pc2i(b0, p), "used and not set", ds, sd);
					}
					else
						fatal("no defs in udchain");
				}
			}
		}
		for(;;){
			p = topbit(ud);
			if(p < 0)
				break;
			bclr(ud, p);
			if(!used(ds)){
				in = pc2i(b0, p);
				if(isnilsrc(&in->src))	/* nilling code */
					in->op = INOOP;	/* elim p from bitmaps ? */
				else if(limbovar(ds) && !structure(ds->ty))
					warning(in, "set and not used", ds, sd);
			}
		}
		ds = ds->next;
	}
	bfree(u);
	bfree(d);
	bfree(dd);
	bfree(ud);
}

static void
ckflags(void)
{
	int i, j, k, n;
	Optab *o;

	n = nelem(opflags);
	o = opflags;
	for(i = 0; i < n; i++){
		j = (o->flags&(Suse|Sdef)) != 0;
		k = SS(o->size) != 0;
		if(j != k){
			if(!(j == 0 && k == 1 && i == ILEA))
				fatal("S %ld %s\n", o-opflags, instname[i]);
		}
		j = (o->flags&(Muse|Mdef)) != 0;
		k = SM(o->size) != 0;
		if(j != k)
			fatal("M %ld %s\n", o-opflags, instname[i]);
		j = (o->flags&(Duse|Ddef)) != 0;
		k = SD(o->size) != 0;
		if(j != k){
			if(!(j == 1 && k == 0 && (i == IGOTO || i == ICASE || i == ICASEC || i == ICASEL)))
				fatal("D %ld %s\n", o-opflags, instname[i]);
		}
		o++;
	}
}

void
optim(Inst *in, Decl *d)
{
	int nb, npc, nv, nd, nu;
	Block *b;
	Bits *uses, *defs;
	Decl *sd;

	ckflags();
	if(debug['o'])
		print("************************************************\nfunction %s\n************************************************\n", d->sym->name);
	if(in == nil || errors > 0)
		return;
	d = d->ty->ids;
	if(regdecls == nil)
		mkdecls();
	regdecls->next->next->next = d;
	d = regdecls;
	sd = sharedecls(d);
	if(debug['o'])
		printdecls(d);
	b = mkblocks(in, &nb);
	ckblocks(in, b, nb);
	npc = inspc(in);
	nb = dfs(b, nb);
	if(debug['o'])
		pblocks(b, nb);
	loops(b);
	nv = len(d);
	uses = allocbits(nv, npc+nv);
	defs = allocbits(nv, npc+nv);
	dummydefs(defs, nv, npc);
	usedef(in, d, uses, defs);
	if(debug['o']){
		pusedef(uses, nv, d, "uses");
		pusedef(defs, nv, d, "defs");
	}
	nu = bitcount(uses, nv);
	nd = bitcount(defs, nv);
	dogenkill(b, defs, nv);
	udflow(b, nv, npc);
	if(debug['o'])
		pflows(b);
	udchain(b, d, nv, npc, defs, uses, sd);
	freeblks(b);
	freebits(uses, nv);
	freebits(defs, nv);
	if(debug['o'])
		print("nb=%d npc=%d nv=%d nd=%d nu=%d\n", nb, npc, nv, nd, nu);
}

