summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--lisc/emit.c344
1 files changed, 170 insertions, 174 deletions
diff --git a/lisc/emit.c b/lisc/emit.c
index 4259b4a..0a6ec1e 100644
--- a/lisc/emit.c
+++ b/lisc/emit.c
@@ -42,6 +42,7 @@ static struct {
 	char *asm;
 } omap[] = {
 	{ OAdd,    Ka, "+add%k %0, %1" },
+	{ OSub,    Ka, "-sub%k %0, %1" },
 	{ OAnd,    Ki, "+and%k %0, %1" },
 	{ OMul,    Ki, "+imul%k %0, %1" },
 	{ ODiv,    Ka, "-div%k %0, %1" },
@@ -81,7 +82,7 @@ static struct {
 	{ OXSet+Cne,  Ki, "setnz %B=\n\tmovzb%k %B=, %=" },
 };
 
-static char *rsub[][4] = {
+static char *rname[][4] = {
 	[RAX] = {"rax", "eax", "ax", "al"},
 	[RBX] = {"rbx", "ebx", "bx", "bl"},
 	[RCX] = {"rcx", "ecx", "cx", "cl"},
@@ -100,11 +101,13 @@ static char *rsub[][4] = {
 	[R15] = {"r15", "r15d", "r15w", "r15b"},
 };
 
+
 static int
 slot(int s, Fn *fn)
 {
 	struct { int i:14; } x;
 
+	/* sign extend s using a bitfield */
 	x.i = s;
 	assert(NAlign == 3);
 	if (x.i < 0)
@@ -133,55 +136,106 @@ emitcon(Con *con, FILE *f)
 }
 
 static void
-emitf(char *fmt, Ins *i, Fn *fn, FILE *f)
+regtoa(int reg, int sz)
+{
+	static char buf[6];
+
+	if (reg >= XMM0) {
+		sprintf(buf, "XMM%d", reg-XMM0);
+		return buf;
+	} else
+		return rname[reg][sz];
+}
+
+static Ref
+getarg(char c, Ins *i)
+{
+	switch (c) {
+	default:
+		diag("emit: 0, 1, = expected in format");
+	case '0':
+		return i->arg[0];
+	case '1':
+		return i->arg[1];
+	case '=':
+		return i->to;
+	}
+}
+
+static void emiti(Ins, Fn *, FILE *);
+
+static void
+emitcopy(Ref r1, Ref r2, int k, Fn *fn, FILE *f)
+{
+	Ins icp;
+
+	icp.op = OCopy;
+	icp.arg[0] = r2;
+	icp.to = r1;
+	icp.k = k;
+	emiti(icp, fn, f);
+}
+
+static void
+emitf(char *s, Ins *i, Fn *fn, FILE *f)
 {
-	static char stoa[] = "qlwb";
-	va_list ap;
-	char c, *s, *s1;
-	int i, ty;
+	static char ktoa[][3] = {"l", "q", "ss", "sd"};
+	char c, *s1;
+	int i, sz;
 	Ref ref;
 	Mem *m;
 	Con off;
 
-	va_start(ap, fmt);
 	ty = SWord;
-	s = fmt;
 	fputc('\t', f);
+
+	switch (*s) {
+	case '+':
+		if (req(i->arg[1], i->to)) {
+			ref = i->arg[0];
+			i->arg[0] = i->arg[1];
+			i->arg[1] = ref;
+		}
+		/* fall through */
+	case '-':
+		if (req(i->arg[1], i->to) && !req(i->arg[0], i->to))
+			diag("emit: cannot convert to 2-address");
+		emitcopy(i->arg[0], i->to, i->cls, fn, f);
+		break;
+	}
 Next:
 	while ((c = *s++) != '%')
 		if (!c) {
-			va_end(ap);
 			fputc('\n', f);
 			return;
 		} else
 			fputc(c, f);
 	switch ((c = *s++)) {
 	default:
-		diag("emit: unknown escape");
-	case 'w':
-	case 'W':
-		i = va_arg(ap, int);
-		ty = i ? SLong: SWord;
-	if (0) {
-	case 't':
-	case 'T':
-		ty = va_arg(ap, int);
-	}
-		if (c == 't' || c == 'w')
-			fputc(stoa[ty], f);
+		diag("emit: invalid escape");
+	case '%':
+		fputc('%', f);
 		break;
-	case 's':
-		s1 = va_arg(ap, char *);
-		fputs(s1, f);
+	case 'k':
+		fputs(ktoa[ty], f);
 		break;
-	case 'R':
-		ref = va_arg(ap, Ref);
+	case '0':
+	case '1':
+	case '=':
+		sz = KWIDE(i->cls) ? SLong : SWord;
+		s--;
+		/* fall through */
+	case 'D':
+	case 'S':
+	Ref:
+		c = *s++;
+		ref = getarg(c, i);
 		switch (rtype(ref)) {
 		default:
 			diag("emit: invalid reference");
 		case RTmp:
 			assert(isreg(ref));
-			fprintf(f, "%%%s", rsub[ref.val][ty]);
+			fprintf(f, "%%%s", regtoa(ref.val, sz));
 			break;
 		case RSlot:
 			fprintf(f, "%d(%%rbp)", slot(ref.val, fn));
@@ -201,10 +255,10 @@ Next:
 				break;
 			fputc('(', f);
 			if (!req(m->base, R))
-				fprintf(f, "%%%s", rsub[m->base.val][SLong]);
+				fprintf(f, "%%%s", regtoa(m->base.val, SLong));
 			if (!req(m->index, R))
 				fprintf(f, ", %%%s, %d",
-					rsub[m->index.val][SLong],
+					regtoa(m->index.val, SLong),
 					m->scale
 				);
 			fputc(')', f);
@@ -215,8 +269,21 @@ Next:
 			break;
 		}
 		break;
+	case 'L':
+		sz = SLong;
+		goto Ref;
+	case 'W':
+		sz = SWord;
+		goto Ref;
+	case 'H':
+		sz = SShort;
+		goto Ref;
+	case 'B':
+		sz = SByte;
+		goto Ref;
 	case 'M':
-		ref = va_arg(ap, Ref);
+		c = *s++;
+		ref = getarg(c, i);
 		switch (rtype(ref)) {
 		default:
 			diag("emit: invalid memory reference");
@@ -230,7 +297,7 @@ Next:
 			break;
 		case RTmp:
 			assert(isreg(ref));
-			fprintf(f, "(%%%s)", rsub[ref.val][SLong]);
+			fprintf(f, "(%%%s)", regtoa(ref.val, SLong));
 			break;
 		}
 		break;
@@ -238,179 +305,104 @@ Next:
 	goto Next;
 }
 
-static int
-cneg(int cmp)
-{
-	switch (cmp) {
-	default:   diag("cneg: unhandled comparison");
-	case Ceq:  return Cne;
-	case Csle: return Csgt;
-	case Cslt: return Csge;
-	case Csgt: return Csle;
-	case Csge: return Cslt;
-	case Cne:  return Ceq;
-	}
-}
-
 static void
-eins(Ins i, Fn *fn, FILE *f)
+emiti(Ins i, Fn *fn, FILE *f)
 {
-	static char *otoa[NOp] = {
-		[OAdd]    = "add",
-		[OSub]    = "sub",
-		[OMul]    = "imul",
-		[OAnd]    = "and",
-		[OLoad+Tl]  = "mov",
-		[OLoad+Tsw] = "movsl",
-		/* [OLoad+Tuw] treated manually */
-		[OLoad+Tsh] = "movsw",
-		[OLoad+Tuh] = "movzw",
-		[OLoad+Tsb] = "movsb",
-		[OLoad+Tub] = "movzb",
-	};
-	Ref r0, r1;
+	Ref r;
 	int64_t val;
 
 	switch (i.op) {
+	default:
+	Table:
+		/* most instructions are just pulled out of
+		 * the table omap[], some special cases are
+		 * detailed below */
+		//
+		// look in the table
+		//
+		diag("emit: unhandled instruction (3)");
+	case ONop:
+		/* just do nothing for nops, they are inserted
+		 * by some passes */
+		break;
 	case OMul:
+		/* here, we try to use the 3-addresss form
+		 * of multiplication when possible */
 		if (rtype(i.arg[1]) == RCon) {
-			r0 = i.arg[1];
-			r1 = i.arg[0];
-		} else {
-			r0 = i.arg[0];
-			r1 = i.arg[1];
+			r = i.arg[0];
+			i.arg[0] = i.arg[1];
+			i.arg[1] = r;
 		}
-		if (rtype(r0) == RCon && rtype(r1) == RTmp) {
-			emitf(fn, f, "imul%w %R, %R, %R",
-				i.wide, r0, r1, i.to);
+		if (KTYPE(i.cls) == 0 /* only available for ints */
+		&& rtype(i.arg[0]) == RCon
+		&& rtype(i.arg[1]) == RTmp) {
+			emitf("imul%k %0, %1, %=", &i, fn, f);
 			break;
 		}
-		/* fall through */
-	case OAdd:
+		goto Table;
 	case OSub:
-	case OAnd:
+		/* we have to use the negation trick to handle
+		 * some 3-address substractions */
 		if (req(i.to, i.arg[1])) {
-			if (i.op == OSub) {
-				emitf(fn, f, "neg%w %R", i.wide, i.to);
-				emitf(fn, f, "add%w %R, %R",
-					i.wide, i.arg[0], i.to);
-				break;
-			}
-			i.arg[1] = i.arg[0];
-			i.arg[0] = i.to;
+			emitf("neg%k %=", &i, fn, f);
+			emitf("add%k %0, %=", &i, fn, f);
+			break;
 		}
-		if (!req(i.to, i.arg[0]))
-			emitf(fn, f, "mov%w %R, %R",
-				i.wide, i.arg[0], i.to);
-		emitf(fn, f, "%s%w %R, %R", otoa[i.op],
-			i.wide, i.arg[1], i.to);
-		break;
+		goto Table;
 	case OCopy:
+		/* make sure we don't emit useless copies,
+		 * also, we can use a trick to load 64-bits
+		 * registers, it's detailed in my note below
+		 * http://c9x.me/art/notes.html?09/19/2015 */
 		if (req(i.to, R) || req(i.arg[0], R))
 			break;
 		if (isreg(i.to)
-		&& i.wide
 		&& rtype(i.arg[0]) == RCon
+		&& i.cls == Kl
 		&& fn->con[i.arg[0].val].type == CNum
 		&& (val = fn->con[i.arg[0].val].val) >= 0
 		&& val <= UINT32_MAX) {
-			emitf(fn, f, "movl %R, %R", i.arg[0], i.to);
+			emitf("movl %W0, %W=", &i, fn, f);
 		} else if (!req(i.arg[0], i.to))
-			emitf(fn, f, "mov%w %R, %R",
-				i.wide, i.arg[0], i.to);
-		break;
-	case OStorel:
-	case OStorew:
-	case OStoreh:
-	case OStoreb:
-		emitf(fn, f, "mov%t %R, %M",
-			i.op - OStorel, i.arg[0], i.arg[1]);
-		break;
-	case OLoad+Tuw:
-		emitf(fn, f, "movl %M, %R", i.arg[0], i.to);
-		break;
-	case OLoad+Tsw:
-		if (i.wide == 0) {
-			emitf(fn, f, "movl %M, %R", i.arg[0], i.to);
-			break;
-		}
-	case OLoad+Tl:
-	case OLoad+Tsh:
-	case OLoad+Tuh:
-	case OLoad+Tsb:
-	case OLoad+Tub:
-		emitf(fn, f, "%s%w %M, %R", otoa[i.op],
-			i.wide, i.arg[0], i.to);
-		break;
-	case OExt+Tuw:
-		emitf(fn, f, "movl %R, %R", i.arg[0], i.to);
-		break;
-	case OExt+Tsw:
-	case OExt+Tsh:
-	case OExt+Tuh:
-	case OExt+Tsb:
-	case OExt+Tub:
-		emitf(fn, f, "mov%s%t%s %R, %W%R",
-			(i.op-OExt-Tsw)%2 ? "z" : "s",
-			1+(i.op-OExt-Tsw)/2,
-			i.wide ? "q" : "l",
-			i.arg[0], i.wide, i.to);
+			emitf("mov%k %0, %=", &i, fn, f);
 		break;
 	case OCall:
+		/* calls simply have a weird syntax in AT&T
+		 * assembly... */
 		switch (rtype(i.arg[0])) {
 		default:
 			diag("emit: invalid call instruction");
 		case RCon:
-			emitf(fn, f, "callq %M", i.arg[0]);
+			emitf("callq %0", &i, fn, f);
 			break;
 		case RTmp:
-			emitf(fn, f, "call%w *%R", 1, i.arg[0]);
+			emitf("callq *%L0", &i, fn, f);
 			break;
 		}
 		break;
-	case OAddr:
-		emitf(fn, f, "lea%w %M, %R", i.wide, i.arg[0], i.to);
-		break;
-	case OSwap:
-		emitf(fn, f, "xchg%w %R, %R", i.wide, i.arg[0], i.arg[1]);
-		break;
-	case OSign:
-		if (req(i.to, TMP(RDX)) && req(i.arg[0], TMP(RAX))) {
-			if (i.wide)
-				fprintf(f, "\tcqto\n");
-			else
-				fprintf(f, "\tcltd\n");
-		} else
-			diag("emit: unhandled instruction (2)");
-		break;
 	case OSAlloc:
-		emitf(fn, f, "sub%w %R, %R", 1, i.arg[0], TMP(RSP));
+		/* there is no good reason why this is here
+		 * maybe we should split OSAlloc in 2 different
+		 * instructions depending on the result
+		 */
+		emitf("subq %L0, %%rsp", &i, fn, f);
 		if (!req(i.to, R))
-			emitf(fn, f, "mov%w %R, %R", 1, TMP(RSP), i.to);
-		break;
-	case OXPush:
-		emitf(fn, f, "push%w %R", i.wide, i.arg[0]);
-		break;
-	case OXDiv:
-		emitf(fn, f, "idiv%w %R", i.wide, i.arg[0]);
-		break;
-	case OXCmp:
-		emitf(fn, f, "cmp%w %R, %R", i.wide, i.arg[0], i.arg[1]);
+			emitcopy(TMP(RSP), i.to, Kl, fn, f);
 		break;
-	case OXTest:
-		emitf(fn, f, "test%w %R, %R", i.wide, i.arg[0], i.arg[1]);
-		break;
-	case ONop:
-		break;
-	default:
-		if (OXSet <= i.op && i.op <= OXSet1) {
-			emitf(fn, f, "set%s%t %R",
-				ctoa[i.op-OXSet], SByte, i.to);
-			emitf(fn, f, "movzb%w %T%R, %W%R",
-				i.wide, SByte, i.to, i.wide, i.to);
-			break;
-		}
-		diag("emit: unhandled instruction (3)");
+	}
+}
+
+static int
+cneg(int cmp)
+{
+	switch (cmp) {
+	default:   diag("emit: cneg() unhandled comparison");
+	case Ceq:  return Cne;
+	case Csle: return Csgt;
+	case Cslt: return Csge;
+	case Csgt: return Csle;
+	case Csge: return Cslt;
+	case Cne:  return Ceq;
 	}
 }
 
@@ -431,7 +423,7 @@ void
 emitfn(Fn *fn, FILE *f)
 {
 	Blk *b, *s;
-	Ins *i;
+	Ins *i, itmp;
 	int *r, c, fs;
 
 	fprintf(f,
@@ -447,18 +439,22 @@ emitfn(Fn *fn, FILE *f)
 	if (fs)
 		fprintf(f, "\tsub $%d, %%rsp\n", fs);
 	for (r=rclob; r-rclob < NRClob; r++)
-		if (fn->reg & BIT(*r))
-			emitf(fn, f, "push%w %R", 1, TMP(*r));
+		if (fn->reg & BIT(*r)) {
+			itmp.arg[0] = TMP(*r);
+			emitf("pushq %L0", &itmp, fn, f);
+		}
 
 	for (b=fn->start; b; b=b->link) {
 		fprintf(f, ".L%s:\n", b->name);
-		for (i=b->ins; i-b->ins < b->nins; i++)
+		for (i=b->ins; i!=&b->ins[b->nins]; i++)
 			eins(*i, fn, f);
 		switch (b->jmp.type) {
 		case JRet0:
 			for (r=&rclob[NRClob]; r>rclob;)
-				if (fn->reg & BIT(*--r))
-					emitf(fn, f, "pop%w %R", 1, TMP(*r));
+				if (fn->reg & BIT(*--r)) {
+					itmp.arg[0] = TMP(*r);
+					emitf("popq %L0", &itmp, fn, f);
+				}
 			fprintf(f,
 				"\tleave\n"
 				"\tret\n"