Sigrid Solveig Haflínudóttirmake it build first94efa043 years agoHistory
741 lines · 15.5 KB · C
RawDownload
#include "all.h" /* For x86_64, do the following: * * - check that constants are used only in *   places allowed * - ensure immediates always fit in 32b * - expose machine register contraints *   on instructions like division. * - implement fast locals (the streak of *   constant allocX in the first basic block) * - recognize complex addressing modes * * Invariant: the use counts that are used *            in sel() must be sound.  This *            is not so trivial, maybe the *            dce should be moved out... */ typedef struct ANum ANum; struct ANum {	char n, l, r;	Ins *i;}; static int amatch(Addr *, Ref, int, ANum *, Fn *); static intnoimm(Ref r, Fn *fn){	int64_t val; 	if (rtype(r) != RCon)		return 0;	switch (fn->con[r.val].type) {	case CAddr:		/* we only support the 'small'		 * code model of the ABI, this		 * means that we can always		 * address data with 32bits		 */		return 0;	case CBits:		val = fn->con[r.val].bits.i;		return (val < INT32_MIN || val > INT32_MAX);	default:		die("invalid constant");	}	return -1;} static intrslot(Ref r, Fn *fn){	if (rtype(r) != RTmp)		return -1;	return fn->tmp[r.val].slot;} static voidfixarg(Ref *r, int k, Ins *i, Fn *fn){	char buf[32];	Addr a, *m;	Con cc, *c;	Ref r0, r1, r2, r3;	int s, n, op; 	r0 = *r;	r1 = r0;	s = rslot(r0, fn);	op = i ? i->op : Ocopy;	if (KBASE(k) == 1 && rtype(r0) == RCon) {		/* load floating points from memory		 * slots, they can't be used as		 * immediates		 */		r1 = MEM(fn->nmem);		vgrow(&fn->mem, ++fn->nmem);		memset(&a, 0, sizeof a);		a.offset.type = CAddr;		n = stashbits(&fn->con[r0.val].bits, KWIDE(k) ? 8 : 4);		sprintf(buf, "\"%sfp%d\"", T.asloc, n);		a.offset.sym.id = intern(buf);		fn->mem[fn->nmem-1] = a;	}	else if (op != Ocopy && k == Kl && noimm(r0, fn)) {		/* load constants that do not fit in		 * a 32bit signed integer into a		 * long temporary		 */		r1 = newtmp("isel", Kl, fn);		emit(Ocopy, Kl, r1, r0, R);	}	else if (s != -1) {		/* load fast locals' addresses into		 * temporaries right before the		 * instruction		 */		r1 = newtmp("isel", Kl, fn);		emit(Oaddr, Kl, r1, SLOT(s), R);	}	else if (!(isstore(op) && r == &i->arg[1])	&& !isload(op) && op != Ocall && rtype(r0) == RCon	&& fn->con[r0.val].type == CAddr) {		/* apple as does not support 32-bit		 * absolute addressing, use a rip-		 * relative leaq instead		 */		r1 = newtmp("isel", Kl, fn);		emit(Oaddr, Kl, r1, r0, R);	}	else if (rtype(r0) == RMem) {		/* eliminate memory operands of		 * the form $foo(%rip, ...)		 */		m = &fn->mem[r0.val];		if (req(m->base, R))		if (m->offset.type == CAddr) {			r0 = newtmp("isel", Kl, fn);			emit(Oaddr, Kl, r0, newcon(&m->offset, fn), R);			m->offset.type = CUndef;			m->base = r0;		}	} else if (T.apple && rtype(r0) == RCon	&& (c = &fn->con[r0.val])->type == CAddr	&& c->sym.type == SThr) {		r1 = newtmp("isel", Kl, fn);		if (c->bits.i) {			r2 = newtmp("isel", Kl, fn);			memset(&cc, 0, sizeof(cc));			cc.type = CBits;			cc.bits.i = c->bits.i;			r3 = newcon(&cc, fn);			emit(Oadd, Kl, r1, r2, r3);		} else			r2 = r1;		emit(Ocopy, Kl, r2, TMP(RAX), R);		r2 = newtmp("isel", Kl, fn);		r3 = newtmp("isel", Kl, fn);		emit(Ocall, 0, R, r3, CALL(17));		emit(Ocopy, Kl, TMP(RDI), r2, R);		emit(Oload, Kl, r3, r2, R);		cc = *c;		cc.bits.i = 0;		r3 = newcon(&cc, fn);		emit(Oload, Kl, r2, r3, R);	}	*r = r1;} static voidseladdr(Ref *r, ANum *an, Fn *fn){	Addr a;	Ref r0; 	r0 = *r;	if (rtype(r0) == RTmp) {		memset(&a, 0, sizeof a);		if (!amatch(&a, r0, an[r0.val].n, an, fn))			return;		if (!req(a.base, R))		if (a.offset.type == CAddr) {			/* apple as does not support			 * $foo(%r0, %r1, M); try to			 * rewrite it or bail out if			 * impossible			 */			if (!req(a.index, R) || rtype(a.base) != RTmp)				return;			else {				a.index = a.base;				a.scale = 1;				a.base = R;			}		}		chuse(r0, -1, fn);		vgrow(&fn->mem, ++fn->nmem);		fn->mem[fn->nmem-1] = a;		chuse(a.base, +1, fn);		chuse(a.index, +1, fn);		*r = MEM(fn->nmem-1);	}} static intcmpswap(Ref arg[2], int op){	switch (op) {	case NCmpI+Cflt:	case NCmpI+Cfle:		return 1;	case NCmpI+Cfgt:	case NCmpI+Cfge:		return 0;	}	return rtype(arg[0]) == RCon;} static voidselcmp(Ref arg[2], int k, int swap, Fn *fn){	Ref r;	Ins *icmp; 	if (swap) {		r = arg[1];		arg[1] = arg[0];		arg[0] = r;	}	emit(Oxcmp, k, R, arg[1], arg[0]);	icmp = curi;	if (rtype(arg[0]) == RCon) {		assert(k != Kw);		icmp->arg[1] = newtmp("isel", k, fn);		emit(Ocopy, k, icmp->arg[1], arg[0], R);		fixarg(&curi->arg[0], k, curi, fn);	}	fixarg(&icmp->arg[0], k, icmp, fn);	fixarg(&icmp->arg[1], k, icmp, fn);} static voidsel(Ins i, ANum *an, Fn *fn){	Ref r0, r1, tmp[7];	int x, j, k, kc, sh, swap;	Ins *i0, *i1; 	if (rtype(i.to) == RTmp)	if (!isreg(i.to) && !isreg(i.arg[0]) && !isreg(i.arg[1]))	if (fn->tmp[i.to.val].nuse == 0) {		chuse(i.arg[0], -1, fn);		chuse(i.arg[1], -1, fn);		return;	}	i0 = curi;	k = i.cls;	switch (i.op) {	case Odiv:	case Orem:	case Oudiv:	case Ourem:		if (KBASE(k) == 1)			goto Emit;		if (i.op == Odiv || i.op == Oudiv)			r0 = TMP(RAX), r1 = TMP(RDX);		else			r0 = TMP(RDX), r1 = TMP(RAX);		emit(Ocopy, k, i.to, r0, R);		emit(Ocopy, k, R, r1, R);		if (rtype(i.arg[1]) == RCon) {			/* immediates not allowed for			 * divisions in x86			 */			r0 = newtmp("isel", k, fn);		} else			r0 = i.arg[1];		if (fn->tmp[r0.val].slot != -1)			err("unlikely argument %%%s in %s",				fn->tmp[r0.val].name, optab[i.op].name);		if (i.op == Odiv || i.op == Orem) {			emit(Oxidiv, k, R, r0, R);			emit(Osign, k, TMP(RDX), TMP(RAX), R);		} else {			emit(Oxdiv, k, R, r0, R);			emit(Ocopy, k, TMP(RDX), CON_Z, R);		}		emit(Ocopy, k, TMP(RAX), i.arg[0], R);		fixarg(&curi->arg[0], k, curi, fn);		if (rtype(i.arg[1]) == RCon)			emit(Ocopy, k, r0, i.arg[1], R);		break;	case Osar:	case Oshr:	case Oshl:		r0 = i.arg[1];		if (rtype(r0) == RCon)			goto Emit;		if (fn->tmp[r0.val].slot != -1)			err("unlikely argument %%%s in %s",				fn->tmp[r0.val].name, optab[i.op].name);		i.arg[1] = TMP(RCX);		emit(Ocopy, Kw, R, TMP(RCX), R);		emiti(i);		i1 = curi;		emit(Ocopy, Kw, TMP(RCX), r0, R);		fixarg(&i1->arg[0], argcls(&i, 0), i1, fn);		break;	case Ouwtof:		r0 = newtmp("utof", Kl, fn);		emit(Osltof, k, i.to, r0, R);		emit(Oextuw, Kl, r0, i.arg[0], R);		fixarg(&curi->arg[0], k, curi, fn);		break;	case Oultof:		/* %mask =l and %arg.0, 1		   %isbig =l shr %arg.0, 63		   %divided =l shr %arg.0, %isbig		   %or =l or %mask, %divided		   %float =d sltof %or		   %cast =l cast %float		   %addend =l shl %isbig, 52		   %sum =l add %cast, %addend		   %result =d cast %sum		*/		r0 = newtmp("utof", k, fn);		if (k == Ks)			kc = Kw, sh = 23;		else			kc = Kl, sh = 52;		for (j=0; j<4; j++)			tmp[j] = newtmp("utof", Kl, fn);		for (; j<7; j++)			tmp[j] = newtmp("utof", kc, fn);		emit(Ocast, k, i.to, tmp[6], R);		emit(Oadd, kc, tmp[6], tmp[4], tmp[5]);		emit(Oshl, kc, tmp[5], tmp[1], getcon(sh, fn));		emit(Ocast, kc, tmp[4], r0, R);		emit(Osltof, k, r0, tmp[3], R);		emit(Oor, Kl, tmp[3], tmp[0], tmp[2]);		emit(Oshr, Kl, tmp[2], i.arg[0], tmp[1]);		sel(*curi++, 0, fn);		emit(Oshr, Kl, tmp[1], i.arg[0], getcon(63, fn));		fixarg(&curi->arg[0], Kl, curi, fn);		emit(Oand, Kl, tmp[0], i.arg[0], getcon(1, fn));		fixarg(&curi->arg[0], Kl, curi, fn);		break;	case Ostoui:		i.op = Ostosi;		kc = Ks;		tmp[4] = getcon(0xdf000000, fn);		goto Oftoui;	case Odtoui:		i.op = Odtosi;		kc = Kd;		tmp[4] = getcon(0xc3e0000000000000, fn);	Oftoui:		if (k == Kw)			goto Emit;		r0 = newtmp("ftou", kc, fn);		for (j=0; j<4; j++)			tmp[j] = newtmp("ftou", Kl, fn);		emit(Oor, Kl, i.to, tmp[0], tmp[3]);		emit(Oand, Kl, tmp[3], tmp[2], tmp[1]);		emit(i.op, Kl, tmp[2], r0, R);		emit(Oadd, kc, r0, tmp[4], i.arg[0]);		i1 = curi; /* fixarg() can change curi */		fixarg(&i1->arg[0], kc, i1, fn);		fixarg(&i1->arg[1], kc, i1, fn);		emit(Osar, Kl, tmp[1], tmp[0], getcon(63, fn));		emit(i.op, Kl, tmp[0], i.arg[0], R);		fixarg(&curi->arg[0], Kl, curi, fn);		break;	case Onop:		break;	case Ostored:	case Ostores:	case Ostorel:	case Ostorew:	case Ostoreh:	case Ostoreb:		if (rtype(i.arg[0]) == RCon) {			if (i.op == Ostored)				i.op = Ostorel;			if (i.op == Ostores)				i.op = Ostorew;		}		seladdr(&i.arg[1], an, fn);		goto Emit;	case_Oload:		seladdr(&i.arg[0], an, fn);		goto Emit;	case Ocall:	case Osalloc:	case Ocopy:	case Oadd:	case Osub:	case Oneg:	case Omul:	case Oand:	case Oor:	case Oxor:	case Oxtest:	case Ostosi:	case Odtosi:	case Oswtof:	case Osltof:	case Oexts:	case Otruncd:	case Ocast:	case_OExt:Emit:		emiti(i);		i1 = curi; /* fixarg() can change curi */		fixarg(&i1->arg[0], argcls(&i, 0), i1, fn);		fixarg(&i1->arg[1], argcls(&i, 1), i1, fn);		break;	case Oalloc4:	case Oalloc8:	case Oalloc16:		salloc(i.to, i.arg[0], fn);		break;	default:		if (isext(i.op))			goto case_OExt;		if (isload(i.op))			goto case_Oload;		if (iscmp(i.op, &kc, &x)) {			switch (x) {			case NCmpI+Cfeq:				/* zf is set when operands are				 * unordered, so we may have to				 * check pf				 */				r0 = newtmp("isel", Kw, fn);				r1 = newtmp("isel", Kw, fn);				emit(Oand, Kw, i.to, r0, r1);				emit(Oflagfo, k, r1, R, R);				i.to = r0;				break;			case NCmpI+Cfne:				r0 = newtmp("isel", Kw, fn);				r1 = newtmp("isel", Kw, fn);				emit(Oor, Kw, i.to, r0, r1);				emit(Oflagfuo, k, r1, R, R);				i.to = r0;				break;			}			swap = cmpswap(i.arg, x);			if (swap)				x = cmpop(x);			emit(Oflag+x, k, i.to, R, R);			selcmp(i.arg, kc, swap, fn);			break;		}		die("unknown instruction %s", optab[i.op].name);	} 	while (i0>curi && --i0) {		assert(rslot(i0->arg[0], fn) == -1);		assert(rslot(i0->arg[1], fn) == -1);	}} static Ins *flagi(Ins *i0, Ins *i){	while (i>i0) {		i--;		if (amd64_op[i->op].zflag)			return i;		if (amd64_op[i->op].lflag)			continue;		return 0;	}	return 0;} static voidseljmp(Blk *b, Fn *fn){	Ref r;	int c, k, swap;	Ins *fi;	Tmp *t; 	if (b->jmp.type == Jret0	|| b->jmp.type == Jjmp	|| b->jmp.type == Jhlt)		return;	assert(b->jmp.type == Jjnz);	r = b->jmp.arg;	t = &fn->tmp[r.val];	b->jmp.arg = R;	assert(rtype(r) == RTmp);	if (b->s1 == b->s2) {		chuse(r, -1, fn);		b->jmp.type = Jjmp;		b->s2 = 0;		return;	}	fi = flagi(b->ins, &b->ins[b->nins]);	if (!fi || !req(fi->to, r)) {		Ref ref[2] = {r, CON_Z};		selcmp(ref, Kw, 0, fn);		b->jmp.type = Jjf + Cine;	}	else if (iscmp(fi->op, &k, &c)	     && c != NCmpI+Cfeq /* see sel() */	     && c != NCmpI+Cfne) {		swap = cmpswap(fi->arg, c);		if (swap)			c = cmpop(c);		if (t->nuse == 1) {			selcmp(fi->arg, k, swap, fn);			memset(fi, 0, sizeof(*fi));			fi->op = Onop;

Lines 1–500 of 741.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t