Charles Forsythadd arm64 loader2274e1e12 years agoHistory
1304 lines · 23.1 KB · C
RawDownload
#include	"l.h" #define	BIT(n)	((uvlong)1<<(n)) static struct {	ulong	start;	ulong	size;} pool; static void		checkpool(Prog*, int);static void 	flushpool(Prog*, int);static int	ispcdisp(long); static Optab *badop;static Oprang	oprange[ALAST]; voidspan(void){	Prog *p;	Sym *setext, *s;	Optab *o;	int m, bflag, i;	long c, otxt, v; 	if(debug['v'])		Bprint(&bso, "%5.2f span\n", cputime());	Bflush(&bso); 	bflag = 0;	c = INITTEXT;	otxt = c;	for(p = firstp; p != P; p = p->link) {		if(p->as == ADWORD && (c&7) != 0)			c += 4;		p->pc = c;		o = oplook(p);		m = o->size;		if(m == 0) {			if(p->as == ATEXT) {				curtext = p;				autosize = p->to.offset + PCSZ;				if(p->from.sym != S)					p->from.sym->value = c;				/* need passes to resolve branches */				if(c-otxt >= 1L<<20)					bflag = 1;				otxt = c;				continue;			}			diag("zero-width instruction\n%P", p);			continue;		}		switch(o->flag & (LFROM|LTO)) {		case LFROM:			addpool(p, &p->from);			break;		case LTO:			addpool(p, &p->to);			break;		}		if(p->as == AB || p->as == ARET || p->as == AERET || p->as == ARETURN)	/* TO DO: other unconditional operations */			checkpool(p, 0);		c += m;		if(blitrl)			checkpool(p, 1);	} 	/*	 * if any procedure is large enough to	 * generate a large SBRA branch, then	 * generate extra passes putting branches	 * around jmps to fix. this is rare.	 */	while(bflag) {		if(debug['v'])			Bprint(&bso, "%5.2f span1\n", cputime());		bflag = 0;		c = INITTEXT;		for(p = firstp; p != P; p = p->link) {			if(p->as == ADWORD && (c&7) != 0)				c += 4;			p->pc = c;			o = oplook(p);/* very large branches			if(o->type == 6 && p->cond) {				otxt = p->cond->pc - c;				if(otxt < 0)					otxt = -otxt;				if(otxt >= (1L<<17) - 10) {					q = prg();					q->link = p->link;					p->link = q;					q->as = AB;					q->to.type = D_BRANCH;					q->cond = p->cond;					p->cond = q;					q = prg();					q->link = p->link;					p->link = q;					q->as = AB;					q->to.type = D_BRANCH;					q->cond = q->link->link;					bflag = 1;				}			} */			m = o->size;			if(m == 0) {				if(p->as == ATEXT) {					curtext = p;					autosize = p->to.offset + PCSZ;					if(p->from.sym != S)						p->from.sym->value = c;					continue;				}				diag("zero-width instruction\n%P", p);				continue;			}			c += m;		}	} 	if(debug['t']) {		/* 		 * add strings to text segment		 */		c = rnd(c, 8);		for(i=0; i<NHASH; i++)		for(s = hash[i]; s != S; s = s->link) {			if(s->type != SSTRING)				continue;			v = s->value;			while(v & 3)				v++;			s->value = c;			c += v;		}	} 	c = rnd(c, 8); 	setext = lookup("etext", 0);	if(setext != S) {		setext->value = c;		textsize = c - INITTEXT;	}	if(INITRND)		INITDAT = rnd(c, INITRND);	if(debug['v'])		Bprint(&bso, "tsize = %#llux\n", textsize);	Bflush(&bso);} /* * when the first reference to the literal pool threatens * to go out of range of a 1Mb PC-relative offset * drop the pool now, and branch round it. */static voidcheckpool(Prog *p, int skip){	if(pool.size >= 0xffff0 || !ispcdisp(p->pc+4+pool.size - pool.start+8))		flushpool(p, skip);	else if(p->link == P)		flushpool(p, 2);} static voidflushpool(Prog *p, int skip){	Prog *q; 	if(blitrl) {		if(skip){			if(debug['v'] && skip == 1)				print("note: flush literal pool at %#llux: len=%lud ref=%lux\n", p->pc+4, pool.size, pool.start);			q = prg();			q->as = AB;			q->to.type = D_BRANCH;			q->cond = p->link;			q->link = blitrl;			blitrl = q;		}		else if(p->pc+pool.size-pool.start < 1024*1024)			return;		elitrl->link = p->link;		p->link = blitrl;		blitrl = 0;	/* BUG: should refer back to values until out-of-range */		elitrl = 0;		pool.size = 0;		pool.start = 0;	}} /* * TO DO: hash */voidaddpool(Prog *p, Adr *a){	Prog *q, t;	int c, sz; 	c = aclass(a); 	t = zprg;	t.as = AWORD;	sz = 4;	if(p->as == AMOV) {		t.as = ADWORD;		sz = 8;	} 	switch(c) {	default:		t.to = *a;		break; 	case C_PSAUTO:	case C_PPAUTO:	case C_UAUTO4K:	case C_UAUTO8K:	case C_UAUTO16K:	case C_UAUTO32K:	case C_UAUTO64K:	case C_NSAUTO:	case C_NPAUTO:	case C_LAUTO:	case C_PPOREG:	case C_PSOREG:	case C_UOREG4K:	case C_UOREG8K:	case C_UOREG16K:	case C_UOREG32K:	case C_UOREG64K:	case C_NSOREG:	case C_NPOREG:	case C_LOREG:		t.to.type = D_CONST;		t.to.offset = instoffset;		sz = 4;		break;	} 	for(q = blitrl; q != P; q = q->link)	/* could hash on t.t0.offset */		if(memcmp(&q->to, &t.to, sizeof(t.to)) == 0) {			p->cond = q;			return;		} 	q = prg();	*q = t;	q->pc = pool.size; 	if(blitrl == P) {		blitrl = q;		pool.start = p->pc;	} else		elitrl->link = q;	elitrl = q;	pool.size = rnd(pool.size, sz);	pool.size += sz; 	p->cond = q;} intrelinv(int a){	switch(a) {	case ABEQ:	return ABNE;	case ABNE:	return ABEQ;	case ABCS:	return ABCC;	case ABHS:	return ABLO;	case ABCC:	return ABCS;	case ABLO:	return ABHS;	case ABMI:	return ABPL;	case ABPL:	return ABMI;	case ABVS:	return ABVC;	case ABVC:	return ABVS;	case ABHI:	return ABLS;	case ABLS:	return ABHI;	case ABGE:	return ABLT;	case ABLT:	return ABGE;	case ABGT:	return ABLE;	case ABLE:	return ABGT;	}	diag("unknown relation: %A", a);	return a;} longregoff(Adr *a){ 	instoffset = 0;	aclass(a);	return instoffset;} static intispcdisp(long v){	/* pc-relative addressing will reach? */	return v >= -0xfffff && v <= 0xfffff && (v&3) == 0;} static intisaddcon(vlong v){	/* uimm12 or uimm24? */	if(v < 0)		return 0;	if((v & 0xFFF) == 0)		v >>= 12;	return v <= 0xFFF;} static intisbitcon(uvlong v){	/*  fancy bimm32 or bimm64? */	return findmask(v) != nil || (v>>32) == 0 && findmask(v | (v<<32)) != nil;} static intmaxstr1(uvlong x){	int i; 	for(i = 0; x != 0; i++)		x &= x<<1;	return i;} static uvlongfindrotl(uvlong x, int *l){	int i; 	for(i = 0; (x&1) == 0 || (x&BIT(63)) != 0; i++)		x = (x<<1) | ((x&BIT(63))!=0);	*l = i;	return x;} static intfindmask64(Mask *m, uvlong v){	uvlong x, f, fm;	int i, lr, l0, l1, e; 	if(v == 0 || v == ~(uvlong)0)		return 0;	x = findrotl(v, &lr);	l1 = maxstr1(x);	l0 = maxstr1(~x);	e = l0+l1;	if(e == 0 || l1 == 64 || l0 == 64 || 64%e != 0)		return 0;	if(e != 64){		f = BIT(l1)-1;		fm = BIT(e)-1;		if(e > 32 && x != f)			return 0;		for(i = 0; i < 64; i += e)			if(((x>>i) & fm) != f)				return 0;	}	print("%#llux	%#llux 1:%d 0:%d r:%d\n", v, x, l1, l0, lr%e);	m->v = v;	m->s = l1;	m->e = e;	m->r = lr%e;	return 1;} /* * internal class codes for different constant classes: * they partition the constant/offset range into disjoint ranges that * are somehow treated specially by one or more load/store instructions. */static int	autoclass[] = {C_PSAUTO, C_NSAUTO, C_NPAUTO, C_PSAUTO, C_PPAUTO, C_UAUTO4K, C_UAUTO8K, C_UAUTO16K, C_UAUTO32K, C_UAUTO64K, C_LAUTO};static int	oregclass[] = {C_ZOREG, C_NSOREG, C_NPOREG, C_PSOREG, C_PPOREG, C_UOREG4K, C_UOREG8K, C_UOREG16K, C_UOREG32K, C_UOREG64K, C_LOREG};static int	sextclass[] = {C_SEXT1, C_LEXT, C_LEXT, C_SEXT1, C_SEXT1, C_SEXT1, C_SEXT2, C_SEXT4, C_SEXT8, C_SEXT16, C_LEXT}; /* * return appropriate index into tables above */static intconstclass(vlong l){	if(l == 0)		return 0;	if(l < 0){		if(l >= -256)			return 1;		if(l >= -512 && (l&7) == 0)			return 2;		return 10;	}	if(l <= 255)		return 3;	if(l <= 504 && (l&7) == 0)		return 4;	if(l <= 4095)		return 5;	if(l <= 8190 && (l&1) == 0)		return 6;	if(l <= 16380 && (l&3) == 0)		return 7;	if(l <= 32760 && (l&7) == 0)		return 8;	if(l <= 65520 && (l&0xF) == 0)		return 9;	return 10;} /* * given an offset v and a class c (see above) * return the offset value to use in the instruction, * scaled if necessary */vlongoffsetshift(vlong v, int c){	vlong vs;	int s;	static int shifts[] = {0, 1, 2, 3, 4}; 	s = 0;	if(c >= C_SEXT1 && c <= C_SEXT16)		s = shifts[c-C_SEXT1];	else if(c >= C_UAUTO4K && c <= C_UAUTO64K)		s = shifts[c-C_UAUTO4K];	else if(c >= C_UOREG4K && c <= C_UOREG64K)		s = shifts[c-C_UOREG4K];	vs = v>>s;	if(vs<<s != v)		diag("odd offset: %lld\n%P", v, curp);	return vs;} /* * if v contains a single 16-bit value aligned * on a 16-bit field, and thus suitable for movk/movn, * return the field index 0 to 3; otherwise return -1 */intmovcon(vlong v){	int s; 	for(s = 0; s < 64; s += 16)		if((v & ~((uvlong)0xFFFF<<s)) == 0)			return s/16;	return -1;} intaclass(Adr *a){	vlong v;	Sym *s;	int t; 	instoffset = 0;	switch(a->type) {	case D_NONE:		return C_NONE; 	case D_REG:		return C_REG; 	case D_VREG:		return C_VREG; 	case D_SP:		return C_RSP; 	case D_COND:		return C_COND; 	case D_SHIFT:		return C_SHIFT; 	case D_EXTREG:		return C_EXTREG; 	case D_ROFF:		return C_ROFF; 	case D_XPOST:		return C_XPOST; 	case D_XPRE:		return C_XPRE; 	case D_FREG:

Lines 1–500 of 1304.