1485 lines · 39.3 KB · C
RawDownload
#include "l.h" #define	OPVCC(o,xo,oe,rc) (((o)<<26)|((xo)<<1)|((oe)<<10)|((rc)&1))#define	OPCC(o,xo,rc) OPVCC((o),(xo),0,(rc))#define	OP(o,xo) OPVCC((o),(xo),0,0) /* the order is dest, a/s, b/imm for both arithmetic and logical operations */#define	AOP_RRR(op,d,a,b) ((op)|(((d)&31L)<<21)|(((a)&31L)<<16)|(((b)&31L)<<11))#define	AOP_IRR(op,d,a,simm) ((op)|(((d)&31L)<<21)|(((a)&31L)<<16)|((simm)&0xFFFF))#define	LOP_RRR(op,a,s,b) ((op)|(((s)&31L)<<21)|(((a)&31L)<<16)|(((b)&31L)<<11))#define	LOP_IRR(op,a,s,uimm) ((op)|(((s)&31L)<<21)|(((a)&31L)<<16)|((uimm)&0xFFFF))#define	OP_BR(op,li,aa) ((op)|((li)&0x03FFFFFC)|((aa)<<1))#define	OP_BC(op,bo,bi,bd,aa) ((op)|(((bo)&0x1F)<<21)|(((bi)&0x1F)<<16)|((bd)&0xFFFC)|((aa)<<1))#define	OP_BCR(op,bo,bi) ((op)|(((bo)&0x1F)<<21)|(((bi)&0x1F)<<16))#define	OP_RLW(op,a,s,sh,mb,me) ((op)|(((s)&31L)<<21)|(((a)&31L)<<16)|(((sh)&31L)<<11)|\					(((mb)&31L)<<6)|(((me)&31L)<<1)) #define	OP_ADD	OPVCC(31,266,0,0)#define	OP_ADDI	OPVCC(14,0,0,0)#define	OP_ADDIS OPVCC(15,0,0,0)#define	OP_ANDI	OPVCC(28,0,0,0)#define	OP_EXTSB	OPVCC(31,954,0,0)#define	OP_EXTSH OPVCC(31,922,0,0)#define	OP_EXTSW OPVCC(31,986,0,0)#define	OP_MCRF	OPVCC(19,0,0,0)#define	OP_MCRFS OPVCC(63,64,0,0)#define	OP_MCRXR OPVCC(31,512,0,0)#define	OP_MFCR	OPVCC(31,19,0,0)#define	OP_MFFS	OPVCC(63,583,0,0)#define	OP_MFMSR OPVCC(31,83,0,0)#define	OP_MFSPR OPVCC(31,339,0,0)#define	OP_MFSR	OPVCC(31,595,0,0)#define	OP_MFSRIN	OPVCC(31,659,0,0)#define	OP_MTCRF OPVCC(31,144,0,0)#define	OP_MTFSF OPVCC(63,711,0,0)#define	OP_MTFSFI OPVCC(63,134,0,0)#define	OP_MTMSR OPVCC(31,146,0,0)#define	OP_MTMSRD OPVCC(31,178,0,0)#define	OP_MTSPR OPVCC(31,467,0,0)#define	OP_MTSR	OPVCC(31,210,0,0)#define	OP_MTSRIN	OPVCC(31,242,0,0)#define	OP_MULLW OPVCC(31,235,0,0)#define	OP_MULLD OPVCC(31,233,0,0)#define	OP_OR	OPVCC(31,444,0,0)#define	OP_ORI	OPVCC(24,0,0,0)#define	OP_ORIS	OPVCC(25,0,0,0)#define	OP_RLWINM	OPVCC(21,0,0,0)#define	OP_SUBF	OPVCC(31,40,0,0)#define	OP_RLDIC	OPVCC(30,4,0,0)#define	OP_RLDICR	OPVCC(30,2,0,0)#define	OP_RLDICL	OPVCC(30,0,0,0) #define	oclass(v)	((v).class-1) long	oprrr(int), opirr(int), opload(int), opstore(int), oploadx(int), opstorex(int); /* * 32-bit masks */intgetmask(uchar *m, ulong v){	int i; 	m[0] = m[1] = 0;	if(v != ~0L && v & (1<<31) && v & 1){	/* MB > ME */		if(getmask(m, ~v)){			i = m[0]; m[0] = m[1]+1; m[1] = i-1;			return 1;		}		return 0;	}	for(i=0; i<32; i++)		if(v & (1<<(31-i))){			m[0] = i;			do {				m[1] = i;			} while(++i<32 && (v & (1<<(31-i))) != 0);			for(; i<32; i++)				if(v & (1<<(31-i)))					return 0;			return 1;		}	return 0;} voidmaskgen(Prog *p, uchar *m, ulong v){	if(!getmask(m, v))		diag("cannot generate mask #%lux\n%P", v, p);} /* * 64-bit masks (rldic etc) */intgetmask64(uchar *m, uvlong v){	int i; 	m[0] = m[1] = 0;	for(i=0; i<64; i++)		if(v & ((uvlong)1<<(63-i))){			m[0] = i;			do {				m[1] = i;			} while(++i<64 && (v & ((uvlong)1<<(63-i))) != 0);			for(; i<64; i++)				if(v & ((uvlong)1<<(63-i)))					return 0;			return 1;		}	return 0;} voidmaskgen64(Prog *p, uchar *m, uvlong v){	if(!getmask64(m, v))		diag("cannot generate mask #%llux\n%P", v, p);} static voidreloc(Adr *a, long pc, int sext){	if(a->name == D_EXTERN || a->name == D_STATIC)		dynreloc(a->sym, pc, 1, 1, sext);} static ulongloadu32(int r, vlong d){	long v; 	v = d>>16;	if(isuint32(d))		return LOP_IRR(OP_ORIS, r, REGZERO, v);	return AOP_IRR(OP_ADDIS, r, REGZERO, v);}	intasmout(Prog *p, Optab *o, int aflag){	long o1, o2, o3, o4, o5, v, t;	vlong d;	Prog *ct;	int r, a;	uchar mask[2]; 	o1 = 0;	o2 = 0;	o3 = 0;	o4 = 0;	o5 = 0;	switch(o->type) {	default:		if(aflag)			return 0;		diag("unknown type %d", o->type);		if(!debug['a'])			prasm(p);		break; 	case 0:		/* pseudo ops */		if(aflag) {			if(p->link) {				if(p->as == ATEXT) {					ct = curtext;					o2 = autosize;					curtext = p;					autosize = p->to.offset + 8;					o1 = asmout(p->link, oplook(p->link), aflag);					curtext = ct;					autosize = o2;				} else					o1 = asmout(p->link, oplook(p->link), aflag);			}			return o1;		}		break; 	case 1:		/* mov r1,r2 ==> OR Rs,Rs,Ra */		if(p->to.reg == REGZERO && p->from.type == D_CONST) {			v = regoff(&p->from);			if(r0iszero && v != 0) {				nerrors--;				diag("literal operation on R0\n%P", p);			}			o1 = LOP_IRR(OP_ADDI, REGZERO, REGZERO, v);			break;		}		o1 = LOP_RRR(OP_OR, p->to.reg, p->from.reg, p->from.reg);		break; 	case 2:		/* int/cr/fp op Rb,[Ra],Rd */		r = p->reg;		if(r == NREG)			r = p->to.reg;		o1 = AOP_RRR(oprrr(p->as), p->to.reg, r, p->from.reg);		break; 	case 3:		/* mov $soreg/addcon/ucon, r ==> addis/addi $i,reg',r */		d = vregoff(&p->from);		v = d;		r = p->from.reg;		if(r == NREG)			r = o->param;		if(r0iszero && p->to.reg == 0 && (r != 0 || v != 0))			diag("literal operation on R0\n%P", p);		a = OP_ADDI;		if(o->a1 == C_UCON) {			v >>= 16;			if(r == REGZERO && isuint32(d)){				o1 = LOP_IRR(OP_ORIS, p->to.reg, REGZERO, v);				break;			}			a = OP_ADDIS;		}		o1 = AOP_IRR(a, p->to.reg, r, v);		break; 	case 4:		/* add/mul $scon,[r1],r2 */		v = regoff(&p->from);		r = p->reg;		if(r == NREG)			r = p->to.reg;		if(r0iszero && p->to.reg == 0)			diag("literal operation on R0\n%P", p);		o1 = AOP_IRR(opirr(p->as), p->to.reg, r, v);		break; 	case 5:		/* syscall */		if(aflag)			return 0;		o1 = oprrr(p->as);		break; 	case 6:		/* logical op Rb,[Rs,]Ra; no literal */		r = p->reg;		if(r == NREG)			r = p->to.reg;		o1 = LOP_RRR(oprrr(p->as), p->to.reg, r, p->from.reg);		break; 	case 7:		/* mov r, soreg ==> stw o(r) */		r = p->to.reg;		if(r == NREG)			r = o->param;		v = regoff(&p->to);		if(p->to.type == D_OREG && p->reg != NREG) {			if(v)				diag("illegal indexed instruction\n%P", p);			o1 = AOP_RRR(opstorex(p->as), p->from.reg, p->reg, r);		} else			o1 = AOP_IRR(opstore(p->as), p->from.reg, r, v);		break; 	case 8:		/* mov soreg, r ==> lbz/lhz/lwz o(r) */		r = p->from.reg;		if(r == NREG)			r = o->param;		v = regoff(&p->from);		if(p->from.type == D_OREG && p->reg != NREG) {			if(v)				diag("illegal indexed instruction\n%P", p);			o1 = AOP_RRR(oploadx(p->as), p->to.reg, p->reg, r);		} else			o1 = AOP_IRR(opload(p->as), p->to.reg, r, v);		break; 	case 9:		/* movb soreg, r ==> lbz o(r),r2; extsb r2,r2 */		r = p->from.reg;		if(r == NREG)			r = o->param;		v = regoff(&p->from);		if(p->from.type == D_OREG && p->reg != NREG) {			if(v)				diag("illegal indexed instruction\n%P", p);			o1 = AOP_RRR(oploadx(p->as), p->to.reg, p->reg, r);		} else			o1 = AOP_IRR(opload(p->as), p->to.reg, r, v);		o2 = LOP_RRR(OP_EXTSB, p->to.reg, p->to.reg, 0);		break; 	case 10:		/* sub Ra,[Rb],Rd => subf Rd,Ra,Rb */		r = p->reg;		if(r == NREG)			r = p->to.reg;		o1 = AOP_RRR(oprrr(p->as), p->to.reg, p->from.reg, r);		break; 	case 11:	/* br/bl lbra */		if(aflag)			return 0;		v = 0;		if(p->cond == UP){			if(p->to.sym->type != SUNDEF)				diag("bad branch sym type");			v = (ulong)p->to.sym->value >> (Roffset-2);			dynreloc(p->to.sym, p->pc, 0, 0, 0);		}		else if(p->cond)			v = p->cond->pc - p->pc;		if(v & 03) {			diag("odd branch target address\n%P", p);			v &= ~03;		}		if(v < -(1L<<25) || v >= (1L<<24))			diag("branch too far\n%P", p);		o1 = OP_BR(opirr(p->as), v, 0);		break; 	case 12:	/* movb r,r (extsb); movw r,r (extsw) */		if(p->to.reg == REGZERO && p->from.type == D_CONST) {			v = regoff(&p->from);			if(r0iszero && v != 0) {				nerrors--;				diag("literal operation on R0\n%P", p);			}			o1 = LOP_IRR(OP_ADDI, REGZERO, REGZERO, v);			break;		}		if(p->as == AMOVW)			o1 = LOP_RRR(OP_EXTSW, p->to.reg, p->from.reg, 0);		else			o1 = LOP_RRR(OP_EXTSB, p->to.reg, p->from.reg, 0);		break; 	case 13:	/* mov[bhw]z r,r; uses rlwinm not andi. to avoid changing CC */		if(p->as == AMOVBZ)			o1 = OP_RLW(OP_RLWINM, p->to.reg, p->from.reg, 0, 24, 31);		else if(p->as == AMOVH)			o1 = LOP_RRR(OP_EXTSH, p->to.reg, p->from.reg, 0);		else if(p->as == AMOVHZ)			o1 = OP_RLW(OP_RLWINM, p->to.reg, p->from.reg, 0, 16, 31);		else if(p->as == AMOVWZ)			o1 = OP_RLW(OP_RLDIC, p->to.reg, p->from.reg, 0, 0, 0) | (1<<5);	/* MB=32 */		else			diag("internal: bad mov[bhw]z\n%P", p);		break; 	case 14:	/* rldc[lr] Rb,Rs,$mask,Ra -- left, right give different masks */		r = p->reg;		if(r == NREG)			r = p->to.reg;		d = vregoff(&p->from3);		maskgen64(p, mask, d);		switch(p->as){		case ARLDCL: case ARLDCLCC:			a = mask[0];	/* MB */			if(mask[1] != 63)				diag("invalid mask for rotate: %llux (end != bit 63)\n%P", d, p);			break;		case ARLDCR: case ARLDCRCC:			a = mask[1];	/* ME */			if(mask[0] != 0)				diag("invalid mask for rotate: %llux (start != 0)\n%P", d, p);			break;		default:			diag("unexpected op in rldc case\n%P", p);			a = 0;		}		o1 = LOP_RRR(oprrr(p->as), p->to.reg, r, p->from.reg);		o1 |= (a&31L)<<6;		if(a & 0x20)			o1 |= 1<<5;	/* mb[5] is top bit */		break; 	case 17:	/* bc bo,bi,lbra (same for now) */	case 16:	/* bc bo,bi,sbra */		if(aflag)			return 0;		a = 0;		if(p->from.type == D_CONST)			a = regoff(&p->from);		r = p->reg;		if(r == NREG)			r = 0;		v = 0;		if(p->cond)			v = p->cond->pc - p->pc;		if(v & 03) {			diag("odd branch target address\n%P", p);			v &= ~03;		}		if(v < -(1L<<16) || v >= (1L<<15))			diag("branch too far\n%P", p);		o1 = OP_BC(opirr(p->as), a, r, v, 0);		break; 	case 15:	/* br/bl (r) => mov r,lr; br/bl (lr) */		if(aflag)			return 0;		if(p->as == ABC || p->as == ABCL)			v = regoff(&p->to)&31L;		else			v = 20;	/* unconditional */		r = p->reg;		if(r == NREG)			r = 0;		o1 = AOP_RRR(OP_MTSPR, p->to.reg, 0, 0) | ((D_LR&0x1f)<<16) | (((D_LR>>5)&0x1f)<<11);		o2 = OPVCC(19, 16, 0, 0);		if(p->as == ABL || p->as == ABCL)			o2 |= 1;		o2 = OP_BCR(o2, v, r);		break; 	case 18:	/* br/bl (lr/ctr); bc/bcl bo,bi,(lr/ctr) */		if(aflag)			return 0;		if(p->as == ABC || p->as == ABCL)			v = regoff(&p->from)&31L;		else			v = 20;	/* unconditional */		r = p->reg;		if(r == NREG)			r = 0;		switch(oclass(p->to)) {		case C_CTR:			o1 = OPVCC(19, 528, 0, 0);			break;		case C_LR:			o1 = OPVCC(19, 16, 0, 0);			break;		default:			diag("bad optab entry (18): %d\n%P", p->to.class, p);			v = 0;		}		if(p->as == ABL || p->as == ABCL)			o1 |= 1;		o1 = OP_BCR(o1, v, r);		break; 	case 19:	/* mov $lcon,r ==> cau+or */		d = vregoff(&p->from);		o1 = loadu32(p->to.reg, d);		o2 = LOP_IRR(OP_ORI, p->to.reg, p->to.reg, (long)d);		if(dlm)			reloc(&p->from, p->pc, 0);		break; 	case 20:	/* add $ucon,,r */		v = regoff(&p->from);		r = p->reg;		if(r == NREG)			r = p->to.reg;		if(p->as == AADD && (!r0iszero && p->reg == 0 || r0iszero && p->to.reg == 0))			diag("literal operation on R0\n%P", p);		o1 = AOP_IRR(opirr(p->as+AEND), p->to.reg, r, v>>16);		break; 	case 22:	/* add $lcon,r1,r2 ==> cau+or+add */	/* could do add/sub more efficiently */		if(p->to.reg == REGTMP || p->reg == REGTMP)			diag("cant synthesize large constant\n%P", p);		d = vregoff(&p->from);		o1 = loadu32(REGTMP, d);		o2 = LOP_IRR(OP_ORI, REGTMP, REGTMP, (long)d);		r = p->reg;		if(r == NREG)			r = p->to.reg;		o3 = AOP_RRR(oprrr(p->as), p->to.reg, REGTMP, r);		if(dlm)			reloc(&p->from, p->pc, 0);		break; 	case 23:	/* and $lcon,r1,r2 ==> cau+or+and */	/* masks could be done using rlnm etc. */		if(p->to.reg == REGTMP || p->reg == REGTMP)			diag("cant synthesize large constant\n%P", p);		d = vregoff(&p->from);		o1 = loadu32(REGTMP, d);		o2 = LOP_IRR(OP_ORI, REGTMP, REGTMP, (long)d);		r = p->reg;		if(r == NREG)			r = p->to.reg;		o3 = LOP_RRR(oprrr(p->as), p->to.reg, REGTMP, r);		if(dlm)			reloc(&p->from, p->pc, 0);		break;/*24*/ 	case 25:	/* sld[.] $sh,rS,rA -> rldicr[.] $sh,rS,mask(0,63-sh),rA; srd[.] -> rldicl */		v = regoff(&p->from);		if(v < 0)			v = 0;		else if(v > 63)			v = 63;		r = p->reg;		if(r == NREG)			r = p->to.reg;		switch(p->as){		case ASLD: case ASLDCC:			a = 63-v;			o1 = OP_RLDICR;			break;		case ASRD: case ASRDCC:			a = v;			v = 64-v;			o1 = OP_RLDICL;			break;

Lines 1–500 of 1485.