#include "lib9.h"
#include "isa.h"
#include "interp.h"
#include "raise.h"

/*
 * Copyright © 1997 C H Forsyth (forsyth@terzarima.net)
 */

#define	ROMABLE	0	/* costs something to zero patch vectors */
#define	RESCHED 1	/* check for interpreter reschedule */

#define PATCH(ptr)		   *ptr |= ((ulong)code-(ulong)ptr) & 0xfffc

#define T(r)	*((void**)(R.r))

#define	XO(o,xo)	(((o)<<26)|((xo)<<1))

/* botch: ARRR, AIRR, LRRR, etc have dest first (will fix soon) */

#define	OPARRR(o,d,a,b)	((o)|((d)<<21)|((a)<<16)|((b)<<11))
#define	ARRR(o,d,a,b)		gen((o)|((d)<<21)|((a)<<16)|((b)<<11))
#define	AIRR(o,d,a,v)		gen((o)|((d)<<21)|((a)<<16)|((v)&0xFFFF))
#define	IRR(o,v,a,d)	AIRR((o),(d),(a),(v))
#define	RRR(o,b,a,d)	ARRR((o),(d),(a),(b))
#define	LRRR(o,a,s,b)		ARRR((o),(s),(a),(b))
#define	LIRR(o,a,s,v)		AIRR((o),(s),(a),(v))
#define	Bx(li,aa)		gen((18<<26)|((li)&0x3FFFFFC)|((aa)<<1))
#define	RLW(op,a,s,sh,mb,me) ((op)|(((s)&31L)<<21)|(((a)&31L)<<16)|(((sh)&31L)<<11)|\
					(((mb)&31L)<<6)|(((me)&31L)<<1))
#define	MFSPR(s, d)	gen(XO(31,339) | ((d)<<21) | ((s)<<11))
#define	MTSPR(s, d)	gen(XO(31,467) | ((s)<<21) | ((d)<<11));

#define	SLWI(d,a,n)	gen(slw((d),(a),(n),0))
#define	LRET()	gen(Oblr)

#define	SETR0()	if(macjit){	AIRR(Oaddi, Rzero, 0, 0); }		/* set R0 to 0 */

/* assumes H can be formed from signed halfword */
#define	CMPH(r)		AIRR(Ocmpi, Rcrf0, (r), (ulong)H);
#define NOTNIL(r)	(CMPH((r)), CCALL(EQ, bounds))

enum
{
	Rzero	= 0,	/* zero by design, not definition (P9/Inferno) */

	Rsp = 1,
	Rsb = 2,
	Rarg	= 3,

	Ro1	= 8,
	Ro2	= 9,
	Ro3	= 10,
	Ri	= 11,
	Rj	= 12,

	Rmp	= 13,
	Rfp	= 14,
	Rreg	= 15,
	Rta	= 16,		/* unused */
	Rpic	= 17,		/* address for computed goto, for move to CTR or LR */

	Rcon = 26,	/* constant builder; temporary */
	/* 27, 28, 29, 30 are potentially external registers (P9/Inferno) */
	Rlink = 31,	/* holds copies of LR; linker temp */

	Rfret	= 0,
	Rf1	= 4,
	Rf2	= 6,
	Rfcvi	= 27,	/* floating conversion constant (P9/Inferno) */
	Rfzero = 28,	/* 0.0 (P9/Inferno) */
	Rfhalf = 29,	/* 0.5 (P9/Inferno) */

	Rlr = 8<<5,	/* SPR(LR) */
	Rctr = 9<<5,	/* SPR(CTR) */

	Rcrf0 = 0,		/* condition code field 0 */
	Rcrf1 = 1<<2,	/* condition code field 1 */

	Rcrbrel = 31,	/* condition code bit set to force relinquish */

	Olwz	= XO(32, 0),
	Olwzu = XO(33, 0),
	Olwzx = XO(31, 23),
	Olbz	= XO(34, 0),
	Olbzu = XO(35, 0),
	Olbzx = XO(31, 87),
	Olfd	= XO(50, 0),
	Olhz	= XO(40, 0),
	Olhzx = XO(31, 279),
	Ostw	= XO(36, 0),
	Ostwu = XO(37, 0),
	Ostwx = XO(31, 151),
	Ostb	= XO(38, 0),
	Ostbu = XO(39, 0),
	Ostbx = XO(31, 215),
	Osth	= XO(44,0),
	Osthx = XO(31, 407),
	Ostfd	= XO(54, 0),
	Ostfdu	= XO(55, 0),

	Oaddc	= XO(31,10),
	Oadde	= XO(31, 138),
	Oaddi	= XO(14, 0),	/* simm */
	Oaddic_	= XO(13, 0),
	Oaddis	= XO(15, 0),
	Ocrxor	= XO(19, 193),
	Ofadd	= XO(63, 21),
	Ofcmpo	= XO(63, 32),
	Ofctiwz	= XO(63, 15),
	Ofsub	= XO(63, 20),
	Ofmr	= XO(63, 72),
	Ofmul	= XO(63, 25),
	Ofdiv	= XO(63, 18),
	Ofneg	= XO(63, 40),
	Oori		= XO(24,0),	/* uimm */
	Ooris	= XO(25,0),	/* uimm */
	Odivw	= XO(31, 491),
	Odivwu	= XO(31, 459),
	Omulhw	= XO(31, 75),
	Omulhwu	= XO(31, 11),
	Omulli	= XO(7, 0),
	Omullw	= XO(31, 235),
	Osubf	= XO(31, 40),
	Osubfc	= XO(31,8),
	Osubfe	= XO(31,136),
	Osubfic	= XO(8, 0),
	Oadd	= XO(31, 266),
	Oand	= XO(31, 28),
	Oneg	= XO(31, 104),
	Oor		= XO(31, 444),
	Oxor		= XO(31, 316),

	Ocmpi = XO(11, 0),
	Ocmp = XO(31, 0),
	Ocmpl = XO(31, 32),
	Ocmpli = XO(10,0),

	Orlwinm = XO(21, 0),
	Oslw	= XO(31, 24),
	Osraw = XO(31,792),
	Osrawi =	XO(31,824),
	Osrw = XO(31,536),

	Cnone	= OPARRR(0,20,0,0),	/* unconditional */
	Ceq		= OPARRR(0,12,2,0),
	Cle		= OPARRR(0,4,1,0),
	Clt		= OPARRR(0,12,0,0),
	Cdnz	= OPARRR(0,16,0,0),
	Cgt		= OPARRR(0,12,1,0),
	Cne		= OPARRR(0,4,2,0),
	Cge		= OPARRR(0,4,0,0),
	Cle1		= OPARRR(0,4,5,0),	/* Cle on CR1 */
	Crelq	= OPARRR(0,12,Rcrbrel,0),	/* relinquish */
	Cnrelq	= OPARRR(0,4,Rcrbrel,0),	/* not relinquish */
	Cpredict	= OPARRR(0,1,0,0),	/* reverse prediction */
	Lk		= 1,
	Aa		= 2,

	Obeq	= OPARRR(16<<26,12,2,0),
	Obge	= OPARRR(16<<26,4,0,0),
	Obgt		= OPARRR(16<<26,12,1,0),
	Oble		= OPARRR(16<<26,4,1,0),
	Oblt		= OPARRR(16<<26,12,0,0),
	Obne	= OPARRR(16<<26,4,2,0),

	Ob		= XO(18, 0),
	Obc		= XO(16, 0),
	Obcctr	= XO(19,528),
	Obcctrl	= Obcctr | Lk,
	Obctr	= Obcctr | Cnone,
	Obctrl	= Obctr | Lk,
	Obclr	= XO(19, 16),
	Oblr		= Obclr | Cnone,
	Oblrl		= Oblr | Lk,

	Olea	= 100,		// pseudo op

	SRCOP	= (1<<0),
	DSTOP	= (1<<1),
	WRTPC	= (1<<2),	/* update R.PC */
	TCHECK	= (1<<3),	/* check R.t for continue/ret */
	NEWPC	= (1<<4),	/* goto R.PC */
	DBRAN	= (1<<5),	/* dest is branch */
	THREOP	= (1<<6),

	Lg2Rune	= sizeof(Rune)==4? 2: 1,
	ANDAND	= 1,
	OROR,
	EQAND,

	MacRET	= 0,
	MacFRP,
	MacCASE,
	MacFRAM,
	MacCOLR,
	MacMCAL,
	MacMFRA,
	MacCVTFW,
	MacRELQ,
	MacEND,
	NMACRO
};

	void	(*comvec)(void);
	int	macjit;
extern	long	das(ulong*);
static	ulong*	code;
static	ulong*	base;
static	ulong*	patch;
static	int	pass;
static	Module*	mod;
static	ulong*	tinit;
static	ulong*	litpool;
static	int	nlit;
static	ulong	macro[NMACRO];
static	void	ldbigc(long, int);
static	void	rdestroy(void);
static	void	macret(void);
static	void	macfrp(void);
static	void	maccase(void);
static	void	maccvtfw(void);
static	void	macfram(void);
static	void	maccolr(void);
static	void	macend(void);
static	void	macmcal(void);
static	void	macmfra(void);
static	void	macrelq(void);
static	void	movmem(Inst*);

struct
{
	int	o;
	void	(*f)(void);
} macinit[] =
{
	MacFRP,		macfrp,		/* decrement and free pointer */
	MacRET,		macret,		/* return instruction */
	MacCASE,	maccase,	/* case instruction */
	MacCOLR,	maccolr,	/* increment and color pointer */
	MacFRAM,	macfram,	/* frame instruction */
	MacMCAL,	macmcal,	/* mcall bottom half */
	MacMFRA,	macmfra,	/* punt mframe because t->initialize==0 */
	MacCVTFW,	maccvtfw,
	MacRELQ,		macrelq,	/* reschedule */
	MacEND,		macend,
	0
};

static void
rdestroy(void)
{
	destroy(R.s);
}

static void
rmcall(void)
{
	Frame *f;
	Prog *p;

	if((void*)R.dt == H)
		error(exModule);

	f = (Frame*)R.FP;
	if(f == H)
		error(exModule);

	f->mr = nil;
	((void(*)(Frame*))R.dt)(f);
	R.SP = (uchar*)f;
	R.FP = f->fp;
	if(f->t == nil)
		unextend(f);
	else
		freeptrs(f, f->t);
	p = currun();
	if(p->kill != nil)
		error(p->kill);
}

static void
rmfram(void)
{
	Type *t;
	Frame *f;
	uchar *nsp;

	t = (Type*)R.s;
	if(t == H)
		error(exModule);
	nsp = R.SP + t->size;
	if(nsp >= R.TS) {
		R.s = t;
		extend();
		T(d) = R.s;
		return;
	}
	f = (Frame*)R.SP;
	R.SP = nsp;
	f->t = t;
	f->mr = nil;
	initmem(t, f);
	T(d) = f;
}

void
urk(char *s)
{
	print("compile failed: %s\n", s);	// debugging
	error(exCompile);	// production
}

static void
gen(ulong o)
{
	*code++ = o;
}

static void
br(ulong op, ulong disp)
{
	*code++ = op | (disp & 0xfffc);
}

static void
mfspr(int d, int s)
{
	MFSPR(s, d);
}

static void
mtspr(int d, int s)
{
	MTSPR(s, d);
}

static ulong
slw(int d, int s, int v, int rshift)
{
	int m0, m1;

	if(v < 0 || v > 32)
		urk("slw v");
	if(v < 0)
		v = 0;
	else if(v > 32)
		v = 32;
	if(rshift) {	/* shift right */
		m0 = v;
		m1 = 31;
		v = 32-v;
	} else {
		m0 = 0;
		m1 = 31-v;
	}
	return RLW(Orlwinm, d, s, v, m0, m1);
}

static void
jr(int reg)
{
	mtspr(Rctr, reg);	/* code would be faster if this were loaded well before branch */
	gen(Obctr);
}

static void
jrl(int reg)
{
	mtspr(Rctr, reg);
	gen(Obctrl);
}

static void
jrc(int op, int reg)
{
	mtspr(Rctr, reg);
	gen(Obcctr | op);
}

static long
brdisp(ulong *dest)
{
	ulong d, top;

	d = (ulong)dest - (ulong)code;
	if(!ROMABLE)
		return d & 0x3fffffc;
	top = d>>25;
	if(top == 0 || top == 0x7F){
		/* fits in 26-bit signed displacement */
		return d & 0x3fffffc;
	}
	return -1;
}

static void
jmp(ulong *dest)
{
	long d;

	if((d = brdisp(dest)) < 0){
		ldbigc((ulong)dest, Rpic);	/* Rpic & Rctr must be free */
		jr(Rpic);
	} else
		gen(Ob | d);
}

static void
jmpl(ulong *dest)
{
	long d;

	if((d = brdisp(dest)) < 0){
		ldbigc((ulong)dest, Rpic);	/* Rpic must be free */
		jrl(Rpic);
	} else
		gen(Ob | d | Lk);
}

static void
jmpc(int op, ulong *dest)
{
	ldbigc((ulong)dest, Rpic);
	jrc(op, Rpic);
}

static int
bigc(long c)
{
	if(c >= -0x8000 && c <= 0x7FFF)
		return 0;
	return 1;
}

static void
ldbigc(long c, int reg)
{
	AIRR(Oaddis, reg,Rzero,c>>16);
	LIRR(Oori, reg,reg,c);
}

static void
ldc(long c, int reg)
{
	if(!bigc(c))
		AIRR(Oaddi, reg, Rzero, c);
	else if((ulong)c <= 0xFFFF)
		LIRR(Oori, reg, Rzero, c);
	else if((c&0xFFFF) == 0)
		LIRR(Ooris, reg, Rzero, c>>16);
	else {
		AIRR(Oaddis, reg,Rzero,c>>16);
		LIRR(Oori, reg,reg,c);
	}
}

static void
mem(int inst, long disp, int rm, int r)
{
	if(bigc(disp)) {
		ldc(disp, Rcon);
		switch(inst){
		default: 		urk("mem op"); break;
		case Olea:		inst = Oadd; break;
		case Olwz:	inst = Olwzx; break;
		case Olbz:		inst = Olbzx; break;
		case Olhz:		inst = Olhzx; break;
		case Ostw:	inst = Ostwx; break;
		case Ostb:		inst = Ostbx; break;
		case Osth:		inst = Osthx; break;
		}
		ARRR(inst, r, Rcon, rm);
	} else {
		if(inst == Olea)
			inst = Oaddi;
		AIRR(inst, r, rm,disp);
	}
}

static void
opx(int mode, Adr *a, int op, int reg)
{
	ulong c;
	int r, rx, lea;

	lea = 0;
	if(op == Olea){
		lea = 1;
		op = Oaddi;
	}
	switch(mode) {
	case AFP:
		c = a->ind;
		if(bigc(c))
			urk("bigc op1b 1");
		AIRR(op, reg, Rfp,c);
		break;
	case AMP:
		c = a->ind;
		if(bigc(c))
			urk("bigc op1b 2");
		AIRR(op, reg, Rmp,c);
		break;
	case AIMM:
		if(lea) {
			if(a->imm != 0) {
				ldc(a->imm, reg);
				AIRR(Ostw, reg, Rreg,O(REG,st));
			} else
				AIRR(Ostw, Rzero, Rreg,O(REG,st));
			AIRR(Oaddi, reg, Rreg,O(REG,st));
		} else
			ldc(a->imm, reg);
		return;
	case AIND|AFP:
		r = Rfp;
		goto offset;
	case AIND|AMP:
		r = Rmp;
	offset:
		c = a->i.s;
		rx = Ri;
		if(lea || op == Olwz)
			rx = reg;
		AIRR(Olwz, rx, r,a->i.f);
		if(!lea || c != 0)
			AIRR(op, reg, rx,c);
		break;
	}
}

static void
opwld(Inst *i, int op, int reg)
{
	opx(USRC(i->add), &i->s, op, reg);
}

static void
opwst(Inst *i, int op, int reg)
{
	opx(UDST(i->add), &i->d, op, reg);
}

static void
op2(Inst *i, int op, int reg)
{
	int lea;

	lea = 0;
	if(op == Olea){
		op = Oaddi;
		lea = 1;
	}
	switch(i->add & ARM) {
	case AXNON:
		if(lea)
			op = Olea;
		opwst(i, op, reg);
		return;
	case AXIMM:
		if(lea)
			urk("op2/lea");
		ldc((short)i->reg, reg);
		return;
	case AXINF:
		IRR(op, i->reg,Rfp, reg);
		break;
	case AXINM:
		IRR(op, i->reg,Rmp, reg);
		break;
	}
}

static void
op12(Inst *i, int b1flag, int b2flag)
{
	int o1, o2;

	o1 = Olwz;
	if(b1flag)
		o1 = Olbz;
	o2 = Olwz;
	if(b2flag)
		o2 = Olbz;
	if((i->add & ARM) == AXIMM) {
		opwld(i, o1, Ro1);
		op2(i, o2, Ro2);
	} else {
		op2(i, o2, Ro2);
		opwld(i, o1, Ro1);
	}
}

static void
op13(Inst *i, int o1, int o2)
{
	opwld(i, o1, Ro1);
	opwst(i, o2, Ro1);
}

static ulong
branch(Inst *i)
{
	ulong rel;

	if(base == 0)
		return 0;
	rel = (ulong)(base+patch[i->d.ins - mod->prog]);
	rel -= (ulong)code;
	if(rel & 3 || (long)rel <= -(1<<16) || (long)rel >= 1<<16)
		urk("branch off");
	return rel & 0xfffc;
}

static void
schedcheck(Inst *i)
{
	ulong *cp;

	if(i != nil && i->d.ins != nil && i->d.ins > i)
		return;	/* only backwards jumps can loop: needn't check forward ones */
	cp = code;
	gen(Obc | Cnrelq | Cpredict);
	jmpl(base+macro[MacRELQ]);
	PATCH(cp);
}

static void
literal(ulong imm, int roff)
{
	nlit++;

	ldbigc((ulong)litpool, Ro1);
	IRR(Ostw, roff, Rreg, Ro1);

	if(pass == 0)
		return;

	*litpool = imm;
	litpool++;	
}

static void
bounds(void)
{
	/* mem(Ostw, O(REG,FP), Rreg, Rfp); */
	error(exBounds);
}

static void
punt(Inst *i, int m, void (*fn)(void))
{
	ulong pc;

	if(m & SRCOP) {
		if(UXSRC(i->add) == SRC(AIMM))
			literal(i->s.imm, O(REG, s));
		else {
			opwld(i, Olea, Ro1);
			mem(Ostw, O(REG, s), Rreg, Ro1);
		}
	}
	if(m & DSTOP) {
		opwst(i, Olea, Ro3);
		IRR(Ostw, O(REG,d),Rreg, Ro3);
	}
	if(m & WRTPC) {
		pc = patch[i-mod->prog+1];
		ldbigc((ulong)(base+pc), Ro1);
		IRR(Ostw, O(REG,PC),Rreg, Ro1);
	}
	if(m & DBRAN) {
		pc = patch[i->d.ins-mod->prog];
		literal((ulong)(base+pc), O(REG, d));
	}

	switch(i->add&ARM) {
	case AXNON:
		if(m & THREOP) {
			IRR(Olwz, O(REG,d),Rreg, Ro2);
			IRR(Ostw, O(REG,m),Rreg, Ro2);
		}
		break;
	case AXIMM:
		literal((short)i->reg, O(REG,m));
		break;
	case AXINF:
		mem(Olea, i->reg, Rfp, Ro2);
		mem(Ostw, O(REG, m), Rreg, Ro2);
		break;
	case AXINM:
		mem(Olea, i->reg, Rmp, Ro2);
		mem(Ostw, O(REG, m), Rreg, Ro2);
		break;
	}
	IRR(Ostw, O(REG,FP),Rreg, Rfp);

	jmpl((ulong*)fn);

	ldc((ulong)&R, Rreg);
	SETR0();
	if(m & TCHECK) {
		IRR(Olwz, O(REG,t),Rreg, Ro1);
		IRR(Olwz, O(REG,xpc),Rreg, Ro2);
		IRR(Ocmpi, 0, Ro1, Rcrf0);
		mtspr(Rctr, Ro2);
		gen(Obcctr | Cne);
	}
	IRR(Olwz, O(REG,FP),Rreg, Rfp);
	IRR(Olwz, O(REG,MP),Rreg, Rmp);

	if(m & NEWPC) {
		IRR(Olwz, O(REG,PC),Rreg, Ro1);
		jr(Ro1);
	}
}
				
static void
comgoto(Inst *i)
{
	WORD *t, *e;

	opwld(i, Olwz, Ro2);
	opwst(i, Olea, Ro3);
	SLWI(Ro2, Ro2, 2);
	ARRR(Olwzx, Ro1, Ro3,Ro2);
	jr(Ro1);

	if(pass == 0)
		return;

	t = (WORD*)(mod->origmp+i->d.ind);
	e = t + t[-1];
	t[-1] = 0;
	while(t < e) {
		t[0] = (ulong)(base + patch[t[0]]);
		t++;
	}
}

static void
comcase(Inst *i, int w)
{
	int l;
	WORD *t, *e;

	if(w != 0) {
		opwld(i, Olwz, Ro1);		// v
		opwst(i, Olea, Ro3);		// table
		jmp(base+macro[MacCASE]);
	}

	t = (WORD*)(mod->origmp+i->d.ind+4);
	l = t[-1];
	
	/* have to take care not to relocate the same table twice - 
	 * the limbo compiler can duplicate a case instruction
	 * during its folding phase
	 */

	if(pass == 0) {
		if(l >= 0)
			t[-1] = -l-1;	/* Mark it not done */
		return;
	}
	if(l >= 0)			/* Check pass 2 done */
		return;
	t[-1] = -l-1;			/* Set real count */
	e = t + t[-1]*3;
	while(t < e) {
		t[2] = (ulong)(base + patch[t[2]]);
		t += 3;
	}
	t[0] = (ulong)(base + patch[t[0]]);
}

static void
comcasel(Inst *i)
{
	int l;
	WORD *t, *e;

	t = (WORD*)(mod->origmp+i->d.ind+8);
	l = t[-2];
	if(pass == 0) {
		if(l >= 0)
			t[-2] = -l-1;	/* Mark it not done */
		return;
	}
	if(l >= 0)			/* Check pass 2 done */
		return;
	t[-2] = -l-1;			/* Set real count */
	e = t + t[-2]*6;
	while(t < e) {
		t[4] = (ulong)base + patch[t[4]];
		t += 6;
	}
	t[0] = (ulong)base + patch[t[0]];
}

static void
commframe(Inst *i)
{
	ulong *cp1, *cp2;

	opwld(i, Olwz, Ri);	// must use Ri for MacFRAM
	CMPH(Ri);
	cp1 = code;
	br(Obeq, 0);

	if((i->add&ARM) == AXIMM) {
		mem(Olwz, OA(Modlink, links)+i->reg*sizeof(Modl)+O(Modl, frame), Ri, Ri);
	} else {
		op2(i, Olwz, Ro2);
		SLWI(Ro2, Ro2, 3);	// assumes sizeof(Modl) == 8
		ARRR(Oadd, Ri, Ro2, Ro2);
		mem(Olwz, OA(Modlink, links)+O(Modl, frame), Ri, Ri);
	}

	AIRR(Olwz, Ro2, Ri,O(Type,initialize));
	AIRR(Ocmpi, Rcrf0, Ro2, 0);
	cp2 = code;
	br(Obne, 0);

	opwst(i, Olea, Rj);

	PATCH(cp1);
	ldbigc((ulong)(base+patch[i-mod->prog+1]), Rpic);
	mtspr(Rlr, Rpic);
	jmp(base+macro[MacMFRA]);

	PATCH(cp2);
	jmpl(base+macro[MacFRAM]);
	opwst(i, Ostw, Ro1);
}

static void
commcall(Inst *i)
{
	opwld(i, Olwz, Ro1);				// f in Ro1
	AIRR(Olwz, Ro3, Rreg,O(REG,M));
	AIRR(Ostw, Rfp, Ro1,O(Frame,fp));			// f->fp = R.FP
	AIRR(Ostw, Ro3, Ro1,O(Frame,mr));			// f->mr = R.M
	opwst(i, Olwz, Ri);
	if((i->add&ARM) == AXIMM) {
		mem(Olwz, OA(Modlink, links)+i->reg*sizeof(Modl)+O(Modl, u.pc), Ri, Rj);	// ml->entry in Rj
	} else {
		op2(i, Olwz, Rj);
		SLWI(Rj, Rj, 3);	// assumes sizeof(Modl) == 8
		ARRR(Oadd, Ri, Rj, Rj);
		mem(Olwz, OA(Modlink, links)+O(Modl, u.pc), Rj, Rj);
	}
	jmpl(base+macro[MacMCAL]);
}

static int
swapbraop(int b)
{
	switch(b) {
	case Obge:
		return Oble;
	case Oble:
		return Obge;
	case Obgt:
		return Oblt;
	case Oblt:
		return Obgt;
	}
	return b;
}

static void
cbra(Inst *i, int op)
{
	if(RESCHED)
		schedcheck(i);
	if(UXSRC(i->add) == SRC(AIMM) && !bigc(i->s.imm)) {
		op2(i, Olwz, Ro1);
		AIRR(Ocmpi, Rcrf0, Ro1, i->s.imm);
		op = swapbraop(op);
	} else if((i->add & ARM) == AXIMM) {
		opwld(i, Olwz, Ro1);
		AIRR(Ocmpi, Rcrf0, Ro1, i->reg);
	} else {
		op12(i, 0, 0);
		ARRR(Ocmp, Rcrf0, Ro1, Ro2);
	}
	br(op, branch(i));
}

static void
cbrab(Inst *i, int op)
{
	if(RESCHED)
		schedcheck(i);
	if(UXSRC(i->add) == SRC(AIMM)) {
		op2(i, Olbz, Ro1);
		AIRR(Ocmpi, Rcrf0, Ro1, i->s.imm&0xFF);
		op = swapbraop(op);
	} else if((i->add & ARM) == AXIMM) {
		opwld(i, Olbz, Ro1);
		AIRR(Ocmpi, Rcrf0, Ro1, i->reg&0xFF);	// mask i->reg?
	} else {
		op12(i, 1, 1);
		ARRR(Ocmp, Rcrf0, Ro1, Ro2);
	}
	br(op, branch(i));
}

static void
cbraf(Inst *i, int op)
{
	if(RESCHED)
		schedcheck(i);
	opwld(i, Olfd, Rf1);
	op2(i, Olfd, Rf2);
	ARRR(Ofcmpo, Rcrf0, Rf1, Rf2);
	br(op, branch(i));
}

static void
cbral(Inst *i, int cms, int cls, int mode)
{
	ulong *cp;

	if(RESCHED)
		schedcheck(i);
	cp = nil;
	opwld(i, Olea, Ri);
	op2(i, Olea, Rj);
	IRR(Olwz, 0,Ri, Ro1);
	IRR(Olwz, 0,Rj, Ro2);
	ARRR(Ocmp, Rcrf0, Ro1, Ro2);
	switch(mode) {
	case ANDAND:
		cp = code;
		br(cms, 0);
		break;
	case OROR:
		br(cms, branch(i));
		break;
	case EQAND:
		br(cms, branch(i));
		cp = code;
		br(Obne, 0);
		break;
	}
	IRR(Olwz, 4,Ri, Ro1);
	IRR(Olwz, 4,Rj, Ro2);
	ARRR(Ocmpl, Rcrf0, Ro1, Ro2);
	br(cls, branch(i));
	if(cp)
		PATCH(cp);
}

static void
shrl(Inst *i)
{
//	int c;

//	if(USRC(i->add) != AIMM) {
		punt(i, SRCOP|DSTOP|THREOP, optab[i->op]);
		return;
//	}
/*
	c = i->s.imm;
	op2(i, Olea, Ro3);
	IRR(Olwz, 0,Ro3, Ro1);
	if(c >= 32) {
		if((i->add&ARM) != AXNON)
			opwst(i, Olea, Ro3);
		SRR(Osra, 31, Ro1, Ro2);
		IRR(Ostw, 0,Ro3, Ro2);
		if(c >= 64) {
			IRR(Ostw, 4,Ro3, Ro2);
			return;
		}
		if(c > 32)
			SRR(Osra, c-32, Ro1, Ro1);
		IRR(Ostw, 4,Ro3, Ro1);
		return;
	}
	IRR(Olwz, 4,Ro3, Ro2);
	if((i->add&ARM) != AXNON)
		opwst(i, Olea, Ro3);
	if(c != 0) {
		SRR(Osll, 32-c, Ro1, Ri);
		SRR(Osra, c, Ro1, Ro1);
		SRR(Osrl, c, Ro2, Ro2);
		RRR(Oor, Ri, Ro2, Ro2);
	}
	IRR(Ostw, 4,Ro3, Ro2);
	IRR(Ostw, 0,Ro3, Ro1);
*/
}

static void
shll(Inst *i)
{
//	int c;

//	if(USRC(i->add) != AIMM) {
		punt(i, SRCOP|DSTOP|THREOP, optab[i->op]);
		return;
//	}
/*
	c = i->s.imm;
	if(c >= 64) {
		opwst(i, Olea, Ro3);
		IRR(Ostw, 0,Ro3, Rzero);
		IRR(Ostw, 4,Ro3, Rzero);
		return;
	}
	op2(i, Olea, Ro3);
	if(c >= 32) {
		IRR(Olwz, 4,Ro3, Ro1);
		if((i->add&ARM) != AXNON)
			opwst(i, Olea, Ro3);
		IRR(Ostw, 4,Ro3, Rzero);
		if(c > 32)
			SRR(Osll, c-32, Ro1, Ro1);
		IRR(Ostw, 0,Ro3, Ro1);
		return;
	}
	IRR(Olwz, 4,Ro3, Ro2);
	IRR(Olwz, 0,Ro3, Ro1);
	if((i->add&ARM) != AXNON)
		opwst(i, Olea, Ro3);
	if(c != 0) {
		SRR(Osrl, 32-c, Ro2, Ri);
		SRR(Osll, c, Ro2, Ro2);
		SRR(Osll, c, Ro1, Ro1);
		RRR(Oor, Ri, Ro1, Ro1);
	}
	IRR(Ostw, 4,Ro3, Ro2);
	IRR(Ostw, 0,Ro3, Ro1);
*/
}

static void
compdbg(void)
{
	print("%s:%lud@%.8lux\n", R.M->m->name, *(ulong*)R.m, *(ulong*)R.s);
}

static void
comp(Inst *i)
{
	int o, q, b;
	ulong *cp, *cp1;
	char buf[64];

	if(0) {
		Inst xx;
		xx.add = AXIMM|SRC(AIMM);
		xx.s.imm = (ulong)code;
		xx.reg = i-mod->prog;
		punt(&xx, SRCOP, compdbg);
	}

	switch(i->op) {
	default:
		snprint(buf, sizeof buf, "%s compile, no '%D'", mod->name, i);
		error(buf);
		break;
	case IMCALL:
		if((i->add&ARM) == AXIMM)
			commcall(i);
		else
			punt(i, SRCOP|DSTOP|THREOP|WRTPC|NEWPC, optab[i->op]);
		break;
	case ISEND:
	case IRECV:
	case IALT:
		punt(i, SRCOP|DSTOP|TCHECK|WRTPC, optab[i->op]);
		break;
	case ISPAWN:
		punt(i, SRCOP|DBRAN, optab[i->op]);
		break;
	case IBNEC:
	case IBEQC:
	case IBLTC:
	case IBLEC:
	case IBGTC:
	case IBGEC:
		punt(i, SRCOP|DBRAN|NEWPC|WRTPC, optab[i->op]);
		break;
	case ICASEC:
		comcase(i, 0);
		punt(i, SRCOP|DSTOP|NEWPC, optab[i->op]);
		break;
	case ICASEL:
		comcasel(i);
		punt(i, SRCOP|DSTOP|NEWPC, optab[i->op]);
		break;
	case IADDC:
	case IMULL:
	case IDIVL:
	case IMODL:
	case IMNEWZ:
	case ILSRW:
	case ILSRL:
		punt(i, SRCOP|DSTOP|THREOP, optab[i->op]);
		break;
	case IMFRAME:
		if((i->add&ARM) == AXIMM)
			commframe(i);
		else
			punt(i, SRCOP|DSTOP|THREOP, optab[i->op]);
		break;
	case ILOAD:
	case INEWA:
	case INEWAZ:
	case INEW:
	case INEWZ:
	case ISLICEA:
	case ISLICELA:
	case ICONSB:
	case ICONSW:
	case ICONSL:
	case ICONSF:
	case ICONSM:
	case ICONSMP:
	case ICONSP:
	case IMOVMP:
	case IHEADMP:
	case IINSC:
	case ICVTAC:
	case ICVTCW:
	case ICVTWC:
	case ICVTCL:
	case ICVTLC:
	case ICVTFC:
	case ICVTCF:
	case ICVTFL:
	case ICVTLF:
	case ICVTRF:
	case ICVTFR:
	case ICVTWS:
	case ICVTSW:
	case IMSPAWN:
	case ICVTCA:
	case ISLICEC:
	case INBALT:
		punt(i, SRCOP|DSTOP, optab[i->op]);
		break;
	case INEWCM:
	case INEWCMP:
		punt(i, SRCOP|DSTOP|THREOP, optab[i->op]);
		break;
	case INEWCB:
	case INEWCW:
	case INEWCF:
	case INEWCP:
	case INEWCL:
		punt(i, DSTOP|THREOP, optab[i->op]);
		break;
	case IEXIT:
		punt(i, 0, optab[i->op]);
		break;
	case ICVTWB:
		op13(i, Olwz, Ostb);
		break;
	case ICVTBW:
		op13(i, Olbz, Ostw);
		break;
	case IMOVB:
		if(USRC(i->add) == AIMM && i->s.imm == 0) {
			opwst(i, Ostb, Rzero);
			break;
		}
		op13(i, Olbz, Ostb);
		break;
	case IMOVW:
		if(USRC(i->add) == AIMM && i->s.imm == 0) {
			opwst(i, Ostw, Rzero);
			break;
		}
		op13(i, Olwz, Ostw);
		break;
	case ICVTLW:
		opwld(i, Olea, Ro1);
		AIRR(Olwz, Ro2, Ro1,4);
		opwst(i, Ostw, Ro2);
		break;
	case ICVTWL:
		opwld(i, Olwz, Ro1);
		opwst(i, Olea, Ro2);
		LRRR(Osrawi, Ro3, Ro1, 31);
		AIRR(Ostw, Ro1, Ro2,4);
		AIRR(Ostw, Ro3, Ro2,0);
		break;
	case IHEADM:
		opwld(i, Olwz, Ro1);
		AIRR(Oaddi, Ro1, Ro1,OA(List,data));
		movmem(i);
		break;
	case IMOVM:
		opwld(i, Olea, Ro1);
		movmem(i);
		break;
	case IRET:
		jmp(base+macro[MacRET]);
		break;
	case IFRAME:
		if(UXSRC(i->add) != SRC(AIMM)) {
			punt(i, SRCOP|DSTOP, optab[i->op]);
			break;
		}
		tinit[i->s.imm] = 1;
		ldc((ulong)mod->type[i->s.imm], Ri);
		jmpl(base+macro[MacFRAM]);
		opwst(i, Ostw, Ro1);
		break;
	case ILEA:
		op13(i, Olea, Ostw);
		break;
	case IHEADW:
		opwld(i, Olwz, Ro1);
		AIRR(Olwz, Ro1, Ro1,OA(List,data));
		opwst(i, Ostw, Ro1);
		break;
	case IHEADF:
		opwld(i, Olwz, Ro1);
		AIRR(Olfd, Rf1, Ro1,OA(List,data));
		opwst(i, Ostfd, Rf1);
		break;
	case IHEADB:
		opwld(i, Olwz, Ro1);
		AIRR(Olbz, Ro1, Ro1,OA(List,data));
		opwst(i, Ostb, Ro1);
		break;
	case ITAIL:
		opwld(i, Olwz, Ro1);
		AIRR(Olwz, Ro1, Ro1,O(List,tail));
		goto movp;
	case IMOVP:
		opwld(i, Olwz, Ro1);
		goto movp;
	case IHEADP:
		opwld(i, Olwz, Ro1);
		AIRR(Olwz, Ro1, Ro1,OA(List,data));
	movp:
		CMPH(Ro1);
		cp = code;
		br(Obeq, 0);
		jmpl(base+macro[MacCOLR]);
		PATCH(cp);
		opwst(i, Olea, Ro3);
		AIRR(Olwz, Ri, Ro3,0);
		AIRR(Ostw, Ro1, Ro3,0);
		jmpl(base+macro[MacFRP]);
		break;
	case ILENA:
		opwld(i, Olwz, Ri);
		ldc(0, Ro1);
		CMPH(Ri);
		cp = code;
		br(Obeq, 0);
		AIRR(Olwz, Ro1, Ri,O(Array,len));
		PATCH(cp);
		opwst(i, Ostw, Ro1);
		break;
	case ILENC:
		opwld(i, Olwz, Ri);
		ldc(0, Ro1);
		CMPH(Ri);
		cp = code;
		br(Obeq, 0);
		AIRR(Olwz, Ro1, Ri,O(String,len));
		AIRR(Ocmpi, Rcrf0, Ro1, 0);
		br(Obge, 2*4);	// BGE 2(PC); skip
		ARRR(Oneg, Ro1, Ro1, 0);
		PATCH(cp);
		opwst(i, Ostw, Ro1);
		break;
	case ILENL:
		opwld(i, Olwz, Ro1);
		ldc(0, Ro3);
		CMPH(Ro1);
		cp = code;
		br(Obeq, 0);

		cp1 = code;
		AIRR(Olwz, Ro1, Ro1,O(List,tail));
		AIRR(Oaddi, Ro3, Ro3, 1);
		CMPH(Ro1);
		br(Obne, ((ulong)cp1-(ulong)code));

		PATCH(cp);
		opwst(i, Ostw, Ro3);
		break;
	case IMOVL:
		opwld(i, Olea, Ro1);
		AIRR(Olwz, Ro2, Ro1,0);
		AIRR(Olwz, Ro3, Ro1,4);
		opwst(i, Olea, Ro1);
		AIRR(Ostw, Ro2, Ro1,0);
		AIRR(Ostw, Ro3, Ro1,4);
		break;
	case IMOVF:
		opwld(i, Olfd, Rf1);
		opwst(i, Ostfd, Rf1);
		break;
	case ICVTFW:
		if(!macjit){
			opwld(i, Olfd, Rf1);
			jmpl(base+macro[MacCVTFW]);
			opwst(i, Ostw, Ro1);
			break;
		}
	case ICVTWF:
		punt(i, SRCOP|DSTOP, optab[i->op]);
		break;
	case INEGF:
		opwld(i, Olfd, Rf1);
		ARRR(Ofneg, Rf2, 0, Rf1);
		opwst(i, Ostfd, Rf2);
		break;
	case IXORL:
	case IORL:
	case IANDL:
	case IADDL:
	case ISUBL:
		opwld(i, Olea, Ro1);
		op2(i, Olea, Ro3);

		AIRR(Olwz, Rj, Ro1,4);	/* ls */
		AIRR(Olwz, Ro2, Ro3,4);
		AIRR(Olwz, Ri, Ro1,0);	/* ms */
		AIRR(Olwz, Ro1, Ro3,0);

		switch(i->op) {
		case IXORL:
			o = Oxor;
			goto l1;
		case IORL:
			o = Oor;
			goto l1;
		case IANDL:
			o = Oand;
		l1:
			LRRR(o, Ro1, Ri, Ro1);
			LRRR(o, Ro2, Rj, Ro2);
			break;
		case IADDL:
			RRR(Oaddc, Rj,Ro2, Ro2);
			RRR(Oadde, Ri,Ro1, Ro1);
			break;
		case ISUBL:
			RRR(Osubfc, Ro2,Rj, Ro2);
			RRR(Osubfe, Ro1,Ri, Ro1);
			break;
		}
		if((i->add&ARM) != AXNON)
			opwst(i, Olea, Ro3);
		IRR(Ostw, 0,Ro3, Ro1);
		IRR(Ostw, 4,Ro3, Ro2);
		break;
	case ISHLL:
		shll(i);
		break;
	case ISHRL:
		shrl(i);
		break;
	case IADDF:	o = Ofadd; goto f1;
	case ISUBF:	o = Ofsub; goto f1;
	case IMULF:	o = Ofmul; goto f1;
	case IDIVF:	o = Ofdiv; goto f1;
	f1:
		opwld(i, Olfd, Rf1);
		op2(i, Olfd, Rf2);
		if(o == Ofmul)
			gen(o | (Rf2<<21) | (Rf2<<16) | (Rf1<<6));	/* odd one out: op D,A,-,C */
		else
			ARRR(o, Rf2, Rf2, Rf1);
		opwst(i, Ostfd, Rf2);
		break;

	case IBEQF:
		cbraf(i, Obeq);
		break;
	case IBGEF:
		cbraf(i, Obge);
	case IBGTF:
		cbraf(i, Obgt);
		break;
	case IBLEF:
		cbraf(i, Oble);
		break;
	case IBLTF:
		cbraf(i, Oblt);
		break;
	case IBNEF:
		cbraf(i, Obne);
		break;

	case IBLTB:
		cbrab(i, Oblt);
		break;
	case IBLEB:
		cbrab(i, Oble);
		break;
	case IBGTB:
		cbrab(i, Obgt);
		break;
	case IBGEB:
		cbrab(i, Obge);
		break;
	case IBEQB:
		cbrab(i, Obeq);
		break;
	case IBNEB:
		cbrab(i, Obne);
		break;

	case IBLTW:
		cbra(i, Oblt);
		break;
	case IBLEW:
		cbra(i, Oble);
		break;
	case IBGTW:
		cbra(i, Obgt);
		break;
	case IBGEW:
		cbra(i, Obge);
		break;
	case IBEQW:
		cbra(i, Obeq);
		break;
	case IBNEW:
		cbra(i, Obne);
		break;

	case IBEQL:
		cbral(i, Obne, Obeq, ANDAND);
		break;
	case IBNEL:
		cbral(i, Obne, Obne, OROR);
		break;
	case IBLTL:
		cbral(i, Oblt, Oblt, EQAND);
		break;
	case IBLEL:
		cbral(i, Oblt, Oble, EQAND);
		break;
	case IBGTL:
		cbral(i, Obgt, Obgt, EQAND);
		break;
	case IBGEL:
		cbral(i, Obgt, Obge, EQAND);
		break;

	case ISUBB:
	case IADDB:
	case IANDB:
	case IORB:
	case IXORB:
	case IMODB:
	case IDIVB:
	case IMULB:
		b = 1;
		op12(i, b, b);
		goto s2;
	case ISHLB:
	case ISHRB:
		b = 1;
		op12(i, 0, b);
		goto s2;
	case ISUBW:
	case IADDW:
	case IANDW:
	case IORW:
	case IXORW:
	case ISHLW:
	case ISHRW:
	case IMODW:
	case IDIVW:
	case IMULW:
		b = 0;
		op12(i, b, b);
	s2:
		q = 0;
		switch(i->op) {
		case ISUBB:
		case ISUBW:	o = Osubf; q = Osubfic;
			// TO DO: if immediate operand, should use opcode q
			USED(q);
			ARRR(o, Ro3, Ro1, Ro2);
			break;
		case IADDB:
		case IADDW:	o = Oadd; q = Oaddi; goto c1;
		case IMULB:
		case IMULW:	o = Omullw; q = Omulli; goto c1;
		case IDIVB:
		case IDIVW:	o = Odivw; goto c1;
		c1:
			// TO DO: if immediate operand, should use opcode q
			USED(q);
			ARRR(o, Ro3, Ro2, Ro1);
			break;
		case IANDB:
		case IANDW:	o = Oand; goto c2;
		case IORB:
		case IORW:	o = Oor; goto c2;
		case IXORB:
		case IXORW:	o = Oxor; goto c2;
		case ISHLB:
		case ISHLW:	o = Oslw; goto c2;
		case ISHRB:
		case ISHRW:	o = Osraw; goto c2;
		c2:
			LRRR(o, Ro3,Ro2,Ro1);
			break;
		case IMODB:
		case IMODW:
			ARRR(Odivw, Ro3, Ro2, Ro1);
			ARRR(Omullw, Ro3, Ro3, Ro1);
			ARRR(Osubf, Ro3, Ro3, Ro2);
			break;
		}
		opwst(i, b? Ostb: Ostw, Ro3);
		break;
	case ICALL:
		opwld(i, Olwz, Ro1);	/* f = T(s) */
		ldbigc((ulong)(base+patch[i-mod->prog+1]), Ro2);	/* R.pc */
		AIRR(Ostw, Rfp, Ro1,O(Frame,fp));	/* f->fp = R.fp */
		AIRR(Ostw, Ro2, Ro1,O(Frame,lr));	/* f->lr = R.pc */
		AIRR(Oaddi, Rfp, Ro1, 0);	/* R.fp = (uchar*)f */
		jmp(base+patch[i->d.ins - mod->prog]);
		break;
	case IJMP:
		if(RESCHED)
			schedcheck(i);
		jmp(base+patch[i->d.ins - mod->prog]);
		break;
	case IGOTO:
		comgoto(i);
		break;
	case IINDC:
		opwld(i, Olwz, Ro1);			// Ro1 = string
		if((i->add&ARM) != AXIMM)
			op2(i, Olwz, Ro2);			// Ro2 = i
		AIRR(Olwz, Ri, Ro1,O(String,len));	// len<0 => index Runes, otherwise bytes
		AIRR(Oaddi, Ro1, Ro1,O(String,data));
		AIRR(Ocmpi, Rcrf0, Ri, 0);
		if(bflag){
			br(Obge, 2*4);
			ARRR(Oneg, Ri, Ri, 0);
			if((i->add&ARM) != AXIMM)
				ARRR(Ocmpl, Rcrf1, Ri, Ro2);		/* CMPU len, i */
			else
				AIRR(Ocmpli, Rcrf1, Ri, i->reg);	/* CMPU len, i */
			jmpc(Cle1, (ulong*)bounds);
		}
		cp = code;
		br(Obge, 0);
		if((i->add&ARM) != AXIMM){
			SLWI(Ro2, Ro2, Lg2Rune);
			if(sizeof(Rune) == 4)
				ARRR(Olwz, Ro3, Ro1, Ro2);
			else
				ARRR(Olhzx, Ro3, Ro1, Ro2);
		} else
			mem(Olwz, (short)i->reg<<Lg2Rune, Ro1, Ro3);	/* BUG: TO DO: 16-bit signed displacement */
		gen(Ob | (2*4));	// skip
		PATCH(cp);
		if((i->add&ARM) != AXIMM)
			ARRR(Olbzx, Ro3, Ro1, Ro2);
		else
			AIRR(Olbz, Ro3, Ro1,i->reg);
		opwst(i, Ostw, Ro3);
		break;
	case IINDX:
	case IINDB:
	case IINDF:
	case IINDW:
	case IINDL:
		opwld(i, Olwz, Ro1);			/* Ro1 = a */
		opwst(i, Olwz, Ro3);			/* Ro3 = i */
		if(bflag){
			AIRR(Olwz, Ro2, Ro1, O(Array, len));		/* Ro2 = a->len */
			ARRR(Ocmpl, Rcrf0, Ro3, Ro2);			/* CMPU i, len */
			jmpc(Cge, (ulong*)bounds);
		}
		// TO DO: check a != H
		AIRR(Olwz, Ro2, Ro1,O(Array,data));	/* Ro2 = a->data */
		switch(i->op) {
		case IINDX:
			AIRR(Olwz, Ri, Ro1,O(Array,t));			// Ri = a->t
			AIRR(Olwz, Ro1, Ri,O(Type,size));		// Ro1 = a->t->size
			ARRR(Omullw, Ro3, Ro3, Ro1);			// Ro3 = i*size
			break;
		case IINDL:
		case IINDF:
			SLWI(Ro3, Ro3, 3);		/* Ro3 = i*8 */
			break;
		case IINDW:
			SLWI(Ro3, Ro3, 2);		/* Ro3 = i*4 */
			break;
		case IINDB:
			/* no further work */
			break;
		}
		ARRR(Oadd, Ro2, Ro2, Ro3);		/* Ro2 = i*size + data */
		op2(i, Ostw, Ro2);
		break;
	case ICASE:
		comcase(i, 1);
		break;
	case IRAISE:
		punt(i, SRCOP|WRTPC|NEWPC, optab[i->op]);
		break;
	case IMULX:
	case IDIVX:
	case ICVTXX:
	case IMULX0:
	case IDIVX0:
	case ICVTXX0:
	case IMULX1:
	case IDIVX1:
	case ICVTXX1:
	case ICVTFX:
	case ICVTXF:
	case IEXPW:
	case IEXPL:
	case IEXPF:
		punt(i, SRCOP|DSTOP|THREOP, optab[i->op]);
		break;
	case ISELF:
		punt(i, DSTOP, optab[i->op]);
		break;
	}
}

enum {
	PREFLEN = 20,	/* max instruction words in comvec */
};

static void
preamble(void)
{
	ulong *s;

	if(comvec != nil)
		return;
	s = code = malloc(PREFLEN*sizeof(*code));
	if(s == nil)
		error(exNomem);
	ldc((ulong)&R, Rreg);
	SETR0();
	mfspr(Rlink, Rlr);
	AIRR(Ostw, Rlink, Rreg,O(REG,xpc));
	AIRR(Olwz, Ri, Rreg,O(REG,PC));
	mtspr(Rctr, Ri);
	AIRR(Olwz, Rfp, Rreg,O(REG,FP));
	AIRR(Olwz, Rmp, Rreg,O(REG,MP));
	gen(Obctr);
	if(code >= (ulong*)(s + PREFLEN))
		urk("preamble");
	comvec = (void*)s;
	segflush(s, PREFLEN*sizeof(*s));
	if(cflag > 3) {
		print("comvec\n");
		while(s < code)
			s += das(s);
	}
}

static void
macfrp(void)
{
	CMPH(Ri);
	gen(Obclr | Ceq);	// arg == $H? => return

	AIRR(Olwz, Ro2, Ri,O(Heap,ref)-sizeof(Heap));
	AIRR(Oaddic_, Rj, Ro2, -1);		// ref(arg)-- and test
	AIRR(Ostw, Rj, Ri,O(Heap,ref)-sizeof(Heap));
	gen(Obclr | Cne);		// ref(arg) nonzero? => return

	AIRR(Ostw, Ro2, Ri,O(Heap,ref)-sizeof(Heap));	// restore ref count of 1 for destroy
	mfspr(Rlink, Rlr);
	AIRR(Ostw, Rlink, Rreg,O(REG,st));
	AIRR(Ostw, Rfp, Rreg,O(REG,FP));
	AIRR(Ostw, Ri, Rreg,O(REG,s));

	jmpl((ulong*)rdestroy);				// CALL	destroy

	ldc((ulong)&R, Rreg);
	SETR0();
	AIRR(Olwz, Rlink, Rreg,O(REG,st));
	mtspr(Rlr, Rlink);
	AIRR(Olwz, Rfp, Rreg,O(REG,FP));
	AIRR(Olwz, Rmp, Rreg,O(REG,MP));
	LRET();
}

static void
macret(void)
{
	ulong *cp1, *cp2, *cp3, *cp4, *cp5, *linterp;
	Inst i;

	AIRR(Olwz, Ro1, Rfp,O(Frame,t));
	AIRR(Ocmpi, Rcrf0, Ro1, 0);
	cp1 = code;
	br(Obeq, 0);		// t(Rfp) == 0

	AIRR(Olwz, Rpic, Ro1,O(Type,destroy));
	AIRR(Ocmpi, Rcrf0, Rpic, 0);
	cp2 = code;
	br(Obeq, 0);		// destroy(t(fp)) == 0

	AIRR(Olwz, Ro2, Rfp,O(Frame,fp));
	AIRR(Ocmpi, Rcrf0, Ro2, 0);
	cp3 = code;
	br(Obeq, 0);		// fp(Rfp) == 0

	AIRR(Olwz, Ro3, Rfp,O(Frame,mr));
	AIRR(Ocmpi, Rcrf0, Ro3, 0);
	cp4 = code;
	br(Obeq, 0);		// mr(Rfp) == 0

	AIRR(Olwz, Ro2, Rreg,O(REG,M));
	AIRR(Olwz, Ro3, Ro2,O(Heap,ref)-sizeof(Heap));
	AIRR(Oaddic_, Ro3, Ro3, -1);	// --ref(arg), set cc
	cp5 = code;
	br(Obeq, 0);	// --ref(arg) == 0?
	AIRR(Ostw, Ro3, Ro2,O(Heap,ref)-sizeof(Heap));

	AIRR(Olwz, Ro1, Rfp,O(Frame,mr));
	AIRR(Ostw, Ro1, Rreg,O(REG,M));
	AIRR(Olwz, Rmp, Ro1,O(Modlink,MP));
	AIRR(Ostw, Rmp, Rreg,O(REG,MP));
	AIRR(Olwz, Ro3, Ro1,O(Modlink,compiled));	// R.M->compiled?
	AIRR(Ocmpi, Rcrf0, Ro3, 0);
	linterp = code;
	br(Obeq, 0);

	PATCH(cp4);
	jrl(Rpic);			// call destroy(t(fp))
	AIRR(Ostw, Rfp, Rreg,O(REG,SP));
	AIRR(Olwz, Ro1, Rfp,O(Frame,lr));
	AIRR(Olwz, Rfp, Rfp,O(Frame,fp));
	AIRR(Ostw, Rfp, Rreg,O(REG,FP));	// R.FP = Rfp
	jr(Ro1);				// goto lr(Rfp)

	PATCH(linterp);
	jrl(Rpic);			// call destroy(t(fp))
	AIRR(Ostw, Rfp, Rreg,O(REG,SP));
	AIRR(Olwz, Ro1, Rfp,O(Frame,lr));
	AIRR(Olwz, Rfp, Rfp,O(Frame,fp));
	AIRR(Ostw, Ro1, Rreg,O(REG,PC));	// R.PC = fp->lr
	AIRR(Ostw, Rfp, Rreg,O(REG,FP));	// R.FP = Rfp
	AIRR(Olwz, Rpic, Rreg,O(REG,xpc));
	mtspr(Rlr, Rpic);
	gen(Oblr);		// return to xec uncompiled code

	PATCH(cp1);
	PATCH(cp2);
	PATCH(cp3);
	PATCH(cp5);
	i.add = AXNON;
	punt(&i, TCHECK|NEWPC, optab[IRET]);
}

static void
maccase(void)
{
	ulong *cp1, *cp2, *cp3, *loop;

/*
 * Ro1 = value (input arg), t
 * Ro2 = count, n
 * Ro3 = table pointer (input arg)
 * Ri  = n/2, n2
 * Rj  = pivot element t+n/2*3, l
 */

	IRR(Olwz, 0,Ro3, Ro2);		// count
	IRR(Oaddi, 0,Ro3, Rlink);	// initial table pointer

	loop = code;			// loop:
	AIRR(Ocmpi, Rcrf0, Ro2, 0);
	cp1 = code;
	br(Oble, 0);	// n <= 0? goto out
	LRRR(Osrawi, Ri, Ro2, 1);		// n2 = n>>1
	SLWI(Rj, Ri, 1);
	ARRR(Oadd, Rj, Rj, Ri);
	SLWI(Rj, Rj, 2);
	ARRR(Oadd, Rj, Rj, Ro3);	// l = t + n2*3;
	AIRR(Olwz, Rpic, Rj,4);
	ARRR(Ocmp, Rcrf0, Ro1, Rpic);
	cp2 = code;
	br(Oblt, 0);		// v < l[1]? goto low

	IRR(Olwz, 8,Rj, Rpic);
	ARRR(Ocmp, Rcrf0, Ro1, Rpic);
	cp3 = code;
	br(Obge, 0);		// v >= l[2]? goto high

	IRR(Olwz, 12,Rj, Ro3);		// found
	jr(Ro3);

	PATCH(cp2);	// low:
	IRR(Oaddi, 0, Ri, Ro2);	// n = n2
	jmp(loop);

	PATCH(cp3);	// high:
	IRR(Oaddi, 12, Rj, Ro3);	// t = l+3;
	IRR(Oaddi, 1, Ri, Rpic);
	RRR(Osubf, Ro2, Rpic, Ro2);	// n -= n2 + 1
	jmp(loop);

	PATCH(cp1);	// out:
	IRR(Olwz, 0,Rlink, Ro2);		// initial n
	SLWI(Ro3, Ro2, 1);
	RRR(Oadd, Ro3, Ro2, Ro2);
	SLWI(Ro2, Ro2, 2);
	RRR(Oadd, Ro2, Rlink, Rlink);
	IRR(Olwz, 4,Rlink, Ro3);		// (initial t)[n*3+1]
	jr(Ro3);
}

static	void
macmcal(void)
{
	ulong *cp;

	AIRR(Olwz, Ro2, Ri,O(Modlink,prog));
	mfspr(Rlink, Rlr);
	AIRR(Ostw, Rlink, Ro1,O(Frame,lr));	// f->lr = return
	AIRR(Ocmpi, Rcrf0, Ro2, 0);
	AIRR(Oaddi, Rfp, Ro1, 0);		// R.FP = f
	cp = code;
	br(Obne, 0);		// CMPL ml->m->prog != 0

	AIRR(Ostw, Rlink, Rreg,O(REG,st));
	AIRR(Ostw, Ro1, Rreg,O(REG,FP));
	AIRR(Ostw, Rj, Rreg,O(REG,dt));
	jmpl((ulong*)rmcall);			// CALL	rmcall
	ldc((ulong)&R, Rreg);
	SETR0();
	AIRR(Olwz, Rlink, Rreg,O(REG,st));
	mtspr(Rlr, Rlink);
	AIRR(Olwz, Rfp, Rreg,O(REG,FP));
	AIRR(Olwz, Rmp, Rreg,O(REG,MP));
	gen(Oblr);	// RET

	PATCH(cp);
	AIRR(Olwz, Ro2, Ri,O(Heap,ref)-sizeof(Heap));
	AIRR(Ostw, Ri, Rreg,O(REG,M));
	AIRR(Oaddi, Ro2, Ro2, 1);
	AIRR(Olwz, Rmp, Ri,O(Modlink,MP));
	AIRR(Ostw, Ro2, Ri,O(Heap,ref)-sizeof(Heap));
	AIRR(Ostw, Rmp, Rreg,O(REG,MP));
	AIRR(Olwz, Ro2, Ri,O(Modlink,compiled));
	AIRR(Ocmpi, Rcrf0, Ro2, 0);
	mtspr(Rctr, Rj);
	gen(Obcctr | Cne);	// return to compiled code

	AIRR(Ostw, Rfp, Rreg,O(REG,FP));	// R.FP = Rfp
	AIRR(Ostw, Rj, Rreg,O(REG,PC));	// R.PC = Rj
	AIRR(Olwz, Rpic, Rreg,O(REG,xpc));
	mtspr(Rlr, Rpic);
	gen(Oblr);		// return to xec uncompiled code
}

static	void
macmfra(void)
{
	mfspr(Rlink, Rlr);
	AIRR(Ostw, Rlink, Rreg,O(REG,st));
	AIRR(Ostw, Rfp, Rreg,O(REG,FP));
	AIRR(Ostw, Ri, Rreg,O(REG,s));
	AIRR(Ostw, Rj, Rreg,O(REG,d));
	jmpl((ulong*)rmfram);
	ldc((ulong)&R, Rreg);
	SETR0();
	AIRR(Olwz, Rlink, Rreg,O(REG,st));
	mtspr(Rlr, Rlink);
	AIRR(Olwz, Rfp, Rreg,O(REG,FP));
	AIRR(Olwz, Rmp, Rreg,O(REG,MP));
	gen(Oblr);
}

static void
macfram(void)
{
	ulong *cp;

	/*
	 * Ri has t
	 */
	AIRR(Olwz, Ro2, Ri,O(Type,size));		// MOVW t->size, Ro3
	AIRR(Olwz, Ro1, Rreg,O(REG,SP));		// MOVW	R.SP, Ro1  (=(Frame*)R.SP)
	AIRR(Olwz, Ro3, Rreg,O(REG,TS));		// MOVW	R.TS, tmp
	ARRR(Oadd, Ro2, Ro2, Ro1);		// ADD Ro1, t->size, nsp
	ARRR(Ocmpl, Rcrf0, Ro2, Ro3);		// CMPU nsp,tmp (nsp >= R.TS?)
	cp = code;
	br(Obge, 0);			// BGE expand

	AIRR(Olwz, Rj, Ri,O(Type,initialize));
	mtspr(Rctr, Rj);
	AIRR(Ostw, Ro2, Rreg,O(REG,SP));		// R.SP = nsp
	AIRR(Ostw, Rzero, Ro1,O(Frame,mr));	// Ro1->mr = nil
	AIRR(Ostw, Ri, Ro1,O(Frame,t));		// Ro1->t = t
	gen(Obctr);				// become t->init(Ro1), returning Ro1

	PATCH(cp);					// expand:
	AIRR(Ostw, Ri, Rreg,O(REG,s));		// MOVL	t, R.s
	mfspr(Rlink, Rlr);
	AIRR(Ostw, Rlink, Rreg,O(REG,st));	// MOVL	Rlink, R.st
	AIRR(Ostw, Rfp, Rreg,O(REG,FP));		// MOVL	RFP, R.FP
	jmpl((ulong*)extend);		// CALL	extend
	ldc((ulong)&R, Rreg);
	SETR0();
	AIRR(Olwz, Rlink, Rreg,O(REG,st));	// reload registers
	mtspr(Rlr, Rlink);
	AIRR(Olwz, Rfp, Rreg,O(REG,FP));
	AIRR(Olwz, Rmp, Rreg,O(REG,MP));
	AIRR(Olwz, Ro1, Rreg,O(REG,s));		// return R.s set by extend
	LRET();	// RET
}

static void
movloop(int ldu, int stu, int adj)
{
	ulong *cp;

	AIRR(Oaddi, Ro1, Ro1, -adj);	// adjust for update ld/st
	AIRR(Oaddi, Ro3, Ro3, -adj);
	mtspr(Rctr, Ro2);

	cp = code;			// l0:
	AIRR(ldu, Ri, Ro1,adj);
	AIRR(stu, Ri, Ro3,adj);
	br(Obc | Cdnz, ((ulong)cp-(ulong)code));	// DBNZ l0
}

static void
movmem(Inst *i)
{
	ulong *cp;

	// source address already in Ro1
	if((i->add&ARM) != AXIMM){
		op2(i, Olwz, Ro2);
		AIRR(Ocmpi, Rcrf0, Ro2, 0);
		cp = code;
		br(Oble, 0);
		opwst(i, Olea, Ro3);
		movloop(Olbzu, Ostbu, 1);
		PATCH(cp);
		return;
	}
	switch(i->reg){
	case 4:
		AIRR(Olwz, Ro2, Ro1,0);
		opwst(i, Ostw, Ro2);
		break;
	case 8:
		AIRR(Olwz, Ro2, Ro1,0);
		opwst(i, Olea, Ro3);
		AIRR(Olwz, Ro1, Ro1,4);
		AIRR(Ostw, Ro2, Ro3,0);
		AIRR(Ostw, Ro1, Ro3,4);
		break;
	default:
		// could use lwsi/stwsi loop...
		opwst(i, Olea, Ro3);
		if((i->reg&3) == 0) {
			ldc(i->reg>>2, Ro2);
			movloop(Olwzu, Ostwu, 4);
		} else {
			ldc(i->reg, Ro2);
			movloop(Olbzu, Ostbu, 1);
		}
		break;
	}
}

static void
maccolr(void)
{
	ldbigc((ulong)&mutator, Ri);
	AIRR(Olwz, Ri, Ri,0);
	AIRR(Olwz, Ro3, Ro1,O(Heap,color)-sizeof(Heap));	// h->color

	AIRR(Olwz, Ro2, Ro1,O(Heap,ref)-sizeof(Heap));	// h->ref

	ARRR(Ocmp, Rcrf0, Ri, Ro3);
	AIRR(Oaddi, Ro2, Ro2, 1);	// h->ref++
	AIRR(Ostw, Ro2, Ro1,O(Heap,ref)-sizeof(Heap));
	gen(Obclr | Ceq);	// return if h->color == mutator

	ldc(propagator, Ro3);
	AIRR(Ostw, Ro3, Ro1,O(Heap,color)-sizeof(Heap));	// h->color = propagator
	ldc((ulong)&nprop, Ro3);
	AIRR(Ostw, Ro1, Ro3,0);	// nprop = !0
	LRET();
}

static void
maccvtfw(void)
{
	ulong *cp;

	ARRR(Ofcmpo, Rcrf0, Rf1, Rfzero);
	ARRR(Ofneg, Rf2, 0, Rfhalf);
	cp = code;
	br(Oblt, 0);
	ARRR(Ofmr, Rf2, 0, Rfhalf);
	PATCH(cp);
	ARRR(Ofadd, Rf1, Rf1, Rf2);	//x<0? x-.5: x+.5
	ARRR(Ofctiwz, Rf2, 0, Rf1);
	/* avoid using Ostfdu for now, since software emulation will run on same stack */
	if(0){
		AIRR(Ostfdu, Rf2, Rsp,-8);		// MOVDU Rf2, -8(R1)    (store in temp)
	}else{
		AIRR(Oaddi, Rsp, Rsp, -8);		// SUB $8, R1
		AIRR(Ostfd, Rf2, Rsp,0);		// MOVD Rf2, 0(R1)    (store in temp)
	}
	AIRR(Olwz, Ro1, Rsp,4);		// MOVW 4(R1), Ro1
	AIRR(Oaddi, Rsp, Rsp, 8);		// ADD $8, R1
	LRET();
}

static void
macrelq(void)
{
	ARRR(Ocrxor, Rcrbrel, Rcrbrel, Rcrbrel);	/* clear the relinquish condition */
	mfspr(Rlink, Rlr);
	IRR(Ostw, O(REG,FP),Rreg, Rfp);
	IRR(Ostw, O(REG,PC),Rreg, Rlink);
	IRR(Olwz, O(REG,xpc),Rreg, Ro2);
	jr(Ro2);
}

static void
macend(void)
{
}

void
comd(Type *t)
{
	int i, j, m, c;

	mfspr(Rlink, Rlr);
	AIRR(Ostw, Rlink, Rreg,O(REG,dt));
	for(i = 0; i < t->np; i++) {
		c = t->map[i];
		j = i<<5;
		for(m = 0x80; m != 0; m >>= 1) {
			if(c & m) {
				mem(Olwz, j, Rfp, Ri);
				jmpl(base+macro[MacFRP]);
			}
			j += sizeof(WORD*);
		}
	}
	AIRR(Olwz, Rlink, Rreg,O(REG,dt));
	mtspr(Rlr, Rlink);
	gen(Oblr);
}

void
comi(Type *t)
{
	int i, j, m, c;

	ldc((ulong)H, Ri);
	for(i = 0; i < t->np; i++) {
		c = t->map[i];
		j = i<<5;
		for(m = 0x80; m != 0; m >>= 1) {
			if(c & m)
				mem(Ostw, j, Ro1, Ri);
			j += sizeof(WORD*);
		}
	}
	LRET();
}

void
typecom(Type *t)
{
	int n;
	ulong *tmp, *start;

	if(t == nil || t->initialize != 0)
		return;

	tmp = mallocz(4096*sizeof(ulong), 0);
	if(tmp == nil)
		error(exNomem);

	code = tmp;
	comi(t);
	n = code - tmp;
	code = tmp;
	comd(t);
	n += code - tmp;
	free(tmp);

	n *= sizeof(*code);
	code = mallocz(n, 0);
	if(code == nil)
		return;

	start = code;
	t->initialize = code;
	comi(t);
	t->destroy = code;
	comd(t);

	segflush(start, n);

	if(cflag > 3)
		print("typ= %.8lux %4d i %.8lux d %.8lux asm=%d\n",
			(ulong)t, t->size, (ulong)t->initialize, (ulong)t->destroy, n);
}

static void
patchex(Module *m, ulong *p)
{
	Handler *h;
	Except *e;

	if((h = m->htab) == nil)
		return;
	for( ; h->etab != nil; h++){
		h->pc1 = p[h->pc1];
		h->pc2 = p[h->pc2];
		for(e = h->etab; e->s != nil; e++)
			e->pc = p[e->pc];
		if(e->pc != -1)
			e->pc = p[e->pc];
	}
}

int
compile(Module *m, int size, Modlink *ml)
{
	Link *l;
	Modl *e;
	int i, n;
	ulong *s, *tmp;

	base = nil;
	patch = mallocz(size*sizeof(*patch), ROMABLE);
	tinit = malloc(m->ntype*sizeof(*tinit));
	tmp = malloc(4096*sizeof(ulong));
	if(tinit == nil || patch == nil || tmp == nil)
		goto bad;

	preamble();

	mod = m;
	n = 0;
	pass = 0;
	nlit = 0;

	for(i = 0; i < size; i++) {
		code = tmp;
		comp(&m->prog[i]);
		if(code >= &tmp[4096]) {
			print("%3d %D\n", i, &m->prog[i]);
			urk("tmp ovflo");
		}
		patch[i] = n;
		n += code - tmp;
	}

	for(i=0; macinit[i].f; i++) {
		code = tmp;
		(*macinit[i].f)();
		macro[macinit[i].o] = n;
		n += code - tmp;
	}

	base = mallocz((n+nlit)*sizeof(*base), 0);
	if(base == nil)
		goto bad;

	if(cflag > 3)
		print("dis=%5d %5d ppc=%5d asm=%.8lux lit=%d: %s\n",
			size, size*sizeof(Inst), n, (ulong)base, nlit, m->name);

	pass++;
	nlit = 0;
	litpool = base+n;
	code = base;
	n = 0;

	for(i = 0; i < size; i++) {
		s = code;
		comp(&m->prog[i]);
		if(patch[i] != n) {
			print("%3d %D\n", i, &m->prog[i]);
			urk("phase error");
		}
		n += code - s;
		if(cflag > 3) {
			print("%3d %D\n", i, &m->prog[i]);
			while(s < code)
				s += das(s);
		}/**/
	}

	for(i=0; macinit[i].f; i++) {
		if(macro[macinit[i].o] != n) {
			print("macinit %d\n", macinit[i].o);
			urk("phase error");
		}
		s = code;
		(*macinit[i].f)();
		n += code - s;
		if(cflag > 3) {
			print("macinit %d\n", macinit[i].o);
			while(s < code)
				s += das(s);
		}/**/
	}

	for(l = m->ext; l->name; l++) {
		l->u.pc = (Inst*)(base+patch[l->u.pc-m->prog]);
		typecom(l->frame);
	}
	if(ml != nil) {
		e = &ml->links[0];
		for(i = 0; i < ml->nlinks; i++) {
			e->u.pc = (Inst*)(base+patch[e->u.pc-m->prog]);
			typecom(e->frame);
			e++;
		}
	}
	for(i = 0; i < m->ntype; i++) {
		if(tinit[i] != 0)
			typecom(m->type[i]);
	}
	patchex(m, patch);
	m->entry = (Inst*)(base+patch[mod->entry-mod->prog]);
	free(patch);
	free(tinit);
	free(tmp);
	free(m->prog);
	m->prog = (Inst*)base;
	m->compiled = 1;
	segflush(base, n*sizeof(*base));
	return 1;
bad:
	free(patch);
	free(tinit);
	free(base);
	free(tmp);
	return 0;
}
