summary refs log tree commit diff
path: root/lisc
diff options
context:
space:
mode:
authorQuentin Carbonneaux <quentin.carbonneaux@yale.edu>2016-03-17 13:41:12 -0400
committerQuentin Carbonneaux <quentin.carbonneaux@yale.edu>2016-03-17 13:41:12 -0400
commit240cfcd5cdda9301457867f9d26edcd0cf272b24 (patch)
tree83cf01d43ad41163c96c34f16f2d24a4e8f9eff9 /lisc
parent2889d44f24d8017f527f6f5aa1163065cc57e994 (diff)
downloadroux-240cfcd5cdda9301457867f9d26edcd0cf272b24.tar.gz
support return of structs
Diffstat (limited to 'lisc')
-rw-r--r--lisc/isel.c253
-rw-r--r--lisc/lisc.h6
-rw-r--r--lisc/ssa.c2
-rw-r--r--lisc/test/abi4.ssa38
-rw-r--r--lisc/util.c3
5 files changed, 193 insertions, 109 deletions
diff --git a/lisc/isel.c b/lisc/isel.c
index 8752012..ef9cbb7 100644
--- a/lisc/isel.c
+++ b/lisc/isel.c
@@ -140,7 +140,7 @@ fixarg(Ref *r, int k, int phi, Fn *fn)
 		 * a 32bit signed integer into a
 		 * long temporary
 		 */
-		r1 = newtmp("isel", fn);
+		r1 = newtmp("isel", Kl, fn);
 		emit(OCopy, Kl, r1, r0, R);
 	}
 	else if (s != -1) {
@@ -148,7 +148,7 @@ fixarg(Ref *r, int k, int phi, Fn *fn)
 		 * temporaries right before the
 		 * instruction
 		 */
-		r1 = newtmp("isel", fn);
+		r1 = newtmp("isel", Kl, fn);
 		emit(OAddr, Kl, r1, SLOT(s), R);
 	}
 	*r = r1;
@@ -233,7 +233,7 @@ sel(Ins i, ANum *an, Fn *fn)
 			/* immediates not allowed for
 			 * divisions in x86
 			 */
-			r0 = newtmp("isel", fn);
+			r0 = newtmp("isel", k, fn);
 		} else
 			r0 = i.arg[1];
 		if (i.op == ODiv || i.op == ORem) {
@@ -310,11 +310,11 @@ Emit:
 			emit(OAlloc, Kl, i.to, getcon(val, fn), R);
 		} else {
 			/* r0 = (i.arg[0] + 15) & -16 */
-			r0 = newtmp("isel", fn);
-			r1 = newtmp("isel", fn);
+			r0 = newtmp("isel", Kl, fn);
+			r1 = newtmp("isel", Kl, fn);
 			emit(OSAlloc, Kl, i.to, r0, R);
-			emit(OAnd, 1, r0, r1, getcon(-16, fn));
-			emit(OAdd, 1, r1, i.arg[0], getcon(15, fn));
+			emit(OAnd, Kl, r0, r1, getcon(-16, fn));
+			emit(OAdd, Kl, r1, i.arg[0], getcon(15, fn));
 		}
 		break;
 	default:
@@ -352,33 +352,134 @@ flagi(Ins *i0, Ins *i)
 	return 0;
 }
 
+struct AClass {
+	int inmem;
+	int align;
+	uint size;
+	int cls[2];
+};
+
 static void
-seljmp(Blk *b, Fn *fn)
+aclass(AClass *a, Typ *t)
 {
-	Ref r;
-	int c, k;
-	Ins *fi;
+	int e, s, n, cls;
+	uint sz, al;
 
-	switch (b->jmp.type) {
-	default:
+	sz = t->size;
+	al = 1u << t->align;
+
+	/* the ABI requires sizes to be rounded
+	 * up to the nearest multiple of 8, moreover
+	 * it makes it easy load and store structures
+	 * in registers
+	 */
+	if (al < 8)
+		al = 8;
+	sz = (sz + al-1) & -al;
+
+	a->size = sz;
+	a->align = t->align;
+
+	if (t->dark || sz > 16) {
+		/* large or unaligned structures are
+		 * required to be passed in memory
+		 */
+		a->inmem = 1;
+		return;
+	}
+
+	for (e=0, s=0; e<2; e++) {
+		cls = -1;
+		for (n=0; n<8 && t->seg[s].len; s++) {
+			if (t->seg[s].flt) {
+				if (cls == -1)
+					cls = Kd;
+			} else
+				cls = Kl;
+			n += t->seg[s].len;
+		}
+		assert(n <= 8);
+		a->cls[e] = cls;
+	}
+}
+
+static void
+blit(Ref rstk, uint soff, Ref rsrc, uint sz, Fn *fn)
+{
+	Ref r, r1;
+	uint boff;
+
+	/* it's an impolite blit, we might go across the end
+	 * of the source object a little bit... */
+	for (boff=0; sz>0; sz-=8, soff+=8, boff+=8) {
+		r = newtmp("abi", Kl, fn);
+		r1 = newtmp("abi", Kl, fn);
+		emit(OStorel, 0, R, r, r1);
+		emit(OAdd, Kl, r1, rstk, getcon(soff, fn));
+		r1 = newtmp("abi", Kl, fn);
+		emit(OLoad, Kl, r, r1, R);
+		emit(OAdd, Kl, r1, rsrc, getcon(boff, fn));
+		chuse(rsrc, +1, fn);
+		chuse(rstk, +1, fn);
+	}
+}
+
+static void
+selret(Blk *b, Fn *fn)
+{
+	static int retreg[2][2] = {{RAX, RDX}, {XMM0, XMM0+1}};
+	int j, n, k, nr[2];
+	Ref r, r0, reg[2];
+	AClass a;
+
+	j = b->jmp.type;
+
+	if (!isret(j) || j == JRet0)
 		return;
-	case JRetc:
-		assert(!"retc todo");
-	case JRetw:
-	case JRetl:
-	case JRets:
-	case JRetd:
-		k = b->jmp.type - JRetw;
+
+	r0 = b->jmp.arg;
+	b->jmp.arg = R;
+	b->jmp.type = JRet0;
+
+	if (j == JRetc) {
+		aclass(&a, &typ[fn->retty]);
 		b->jmp.type = JRet0;
-		r = b->jmp.arg;
-		b->jmp.arg = R;
+		if (a.inmem) {
+			assert(rtype(fn->retr) == RTmp);
+			emit(OCopy, Kl, TMP(RAX), fn->retr, R);
+			blit(fn->retr, 0, r0, a.size, fn);
+		} else {
+			nr[0] = nr[1] = 0;
+			for (n=0; n<2; n++) {
+				k = KBASE(a.cls[n]);
+				reg[n] = TMP(retreg[k][nr[k]++]);
+			}
+			if (a.size > 8) {
+				r = newtmp("abi", Kl, fn);
+				emit(OLoad, Kl, reg[1], r, R);
+				emit(OAdd, Kl, r, r0, getcon(8, fn));
+			}
+			emit(OLoad, Kl, reg[0], r0, R);
+		}
+	} else {
+		k = j - JRetw;
 		if (KBASE(k) == 0)
-			emit(OCopy, k, TMP(RAX), r, R);
+			emit(OCopy, k, TMP(RAX), r0, R);
 		else
-			emit(OCopy, k, TMP(XMM0), r, R);
-		return;
-	case JJnz:;
+			emit(OCopy, k, TMP(XMM0), r0, R);
 	}
+}
+
+static void
+seljmp(Blk *b, Fn *fn)
+{
+	Ref r;
+	int c, k;
+	Ins *fi;
+
+	if (b->jmp.type == JRet0 || b->jmp.type == JJmp)
+		return;
+	assert(b->jmp.type == JJnz);
 	r = b->jmp.arg;
 	b->jmp.arg = R;
 	assert(!req(r, R));
@@ -428,57 +529,6 @@ seljmp(Blk *b, Fn *fn)
 	b->jmp.type = JXJc + ICne;
 }
 
-struct AClass {
-	int inmem;
-	int align;
-	uint size;
-	int cls[2];
-};
-
-static void
-aclass(AClass *a, Typ *t)
-{
-	int e, s, n, cls;
-	uint sz, al;
-
-	sz = t->size;
-	al = 1u << t->align;
-
-	/* the ABI requires sizes to be rounded
-	 * up to the nearest multiple of 8, moreover
-	 * it makes it easy load and store structures
-	 * in registers
-	 */
-	if (al < 8)
-		al = 8;
-	sz = (sz + al-1) & -al;
-
-	a->size = sz;
-	a->align = t->align;
-
-	if (t->dark || sz > 16) {
-		/* large or unaligned structures are
-		 * required to be passed in memory
-		 */
-		a->inmem = 1;
-		return;
-	}
-
-	for (e=0, s=0; e<2; e++) {
-		cls = -1;
-		for (n=0; n<8 && t->seg[s].len; s++) {
-			if (t->seg[s].flt) {
-				if (cls == -1)
-					cls = Kd;
-			} else
-				cls = Kl;
-			n += t->seg[s].len;
-		}
-		assert(n <= 8);
-		a->cls[e] = cls;
-	}
-}
-
 static int
 classify(Ins *i0, Ins *i1, AClass *ac, int op)
 {
@@ -578,25 +628,6 @@ calluse(Ins i, int p[2])
 	return b;
 }
 
-static void
-blit(Ref rstk, uint soff, Ref rsrc, uint sz, Fn *fn)
-{
-	Ref r, r1;
-	uint boff;
-
-	/* it's an impolite blit, we might go across the end
-	 * of the source object a little bit... */
-	for (boff=0; sz>0; sz-=8, soff+=8, boff+=8) {
-		r = newtmp("abi", fn);
-		r1 = newtmp("abi", fn);
-		emit(OStorel, 0, R, r, r1);
-		emit(OAdd, Kl, r1, rstk, getcon(soff, fn));
-		r1 = newtmp("abi", fn);
-		emit(OLoad, Kl, r, r1, R);
-		emit(OAdd, Kl, r1, rsrc, getcon(boff, fn));
-	}
-}
-
 static Ref
 rarg(int ty, int *ni, int *ns)
 {
@@ -643,7 +674,7 @@ selcall(Fn *fn, Ins *i0, Ins *i1)
 		if (i->op == OArgc) {
 			if (a->size > 8) {
 				r2 = rarg(a->cls[1], &ni, &ns);
-				r = newtmp("abi", fn);
+				r = newtmp("abi", Kl, fn);
 				emit(OLoad, a->cls[1], r2, r, R);
 				emit(OAdd, Kl, r, i->arg[1], getcon(8, fn));
 			}
@@ -652,7 +683,7 @@ selcall(Fn *fn, Ins *i0, Ins *i1)
 			emit(OCopy, i->cls, r1, i->arg[0], R);
 	}
 
-	r = newtmp("abi", fn);
+	r = newtmp("abi", Kl, fn);
 	for (i=i0, a=ac, off=0; i<i1; i++, a++) {
 		if (!a->inmem)
 			continue;
@@ -661,19 +692,20 @@ selcall(Fn *fn, Ins *i0, Ins *i1)
 				off += off & 15;
 			blit(r, off, i->arg[1], a->size, fn);
 		} else {
-			r1 = newtmp("abi", fn);
+			r1 = newtmp("abi", Kl, fn);
 			emit(OStorel, 0, R, i->arg[0], r1);
 			emit(OAdd, Kl, r1, r, getcon(off, fn));
 		}
 		off += a->size;
 	}
-	emit(OSAlloc, Kl, r, getcon(stk, fn), R);
+	if (stk)
+		emit(OSAlloc, Kl, r, getcon(stk, fn), R);
 }
 
 static void
 selpar(Fn *fn, Ins *i0, Ins *i1)
 {
-	AClass *ac, *a;
+	AClass *ac, *a, aret;
 	Ins *i;
 	int ni, ns, s, al;
 	Ref r, r1;
@@ -684,6 +716,16 @@ selpar(Fn *fn, Ins *i0, Ins *i1)
 	curi = insb;
 	ni = ns = 0;
 	assert(NAlign == 3);
+
+	if (fn->retty >= 0) {
+		aclass(&aret, &typ[fn->retty]);
+		if (aret.inmem) {
+			r = newtmp("abi", Kl, fn);
+			*curi++ = (Ins){OCopy, r, {rarg(Kl, &ni, &ns)}, Kl};
+			fn->retr = r;
+		}
+	}
+
 	s = 4;
 	for (i=i0, a=ac; i<i1; i++, a++) {
 		switch (a->inmem) {
@@ -701,12 +743,12 @@ selpar(Fn *fn, Ins *i0, Ins *i1)
 		}
 		r1 = rarg(a->cls[0], &ni, &ns);
 		if (i->op == OParc) {
-			r = newtmp("abi", fn);
+			r = newtmp("abi", Kl, fn);
 			*curi++ = (Ins){OCopy, r, {r1}, Kl};
 			a->cls[0] = r.val;
 			if (a->size > 8) {
 				r1 = rarg(a->cls[1], &ni, &ns);
-				r = newtmp("abi", fn);
+				r = newtmp("abi", Kl, fn);
 				*curi++ = (Ins){OCopy, r, {r1}, Kl};
 				a->cls[1] = r.val;
 			}
@@ -724,7 +766,7 @@ selpar(Fn *fn, Ins *i0, Ins *i1)
 		*curi++ = (Ins){OAlloc+al, r1, {getcon(a->size, fn)}, Kl};
 		*curi++ = (Ins){OStorel, R, {r, r1}, 0};
 		if (a->size > 8) {
-			r = newtmp("abi", fn);
+			r = newtmp("abi", Kl, fn);
 			*curi++ = (Ins){OAdd, r, {r1, getcon(8, fn)}, Kl};
 			r1 = TMP(a->cls[1]);
 			*curi++ = (Ins){OStorel, R, {r1, r}, 0};
@@ -916,9 +958,10 @@ isel(Fn *fn)
 	b->nins = n;
 	b->ins = i0;
 
-	/* lower function calls */
+	/* lower function calls and returns */
 	for (b=fn->start; b; b=b->link) {
 		curi = &insb[NIns];
+		selret(b, fn);
 		for (i=&b->ins[b->nins]; i!=b->ins;) {
 			if ((--i)->op == OCall) {
 				for (i0=i; i0>b->ins; i0--)
diff --git a/lisc/lisc.h b/lisc/lisc.h
index 5374bdd..c730b1f 100644
--- a/lisc/lisc.h
+++ b/lisc/lisc.h
@@ -297,6 +297,7 @@ enum Jmp {
 	JRets,
 	JRetd,
 	JRetc,
+#define isret(j) (JRet0 <= j && j <= JRetc)
 	JJmp,
 	JJnz,
 	JXJc,
@@ -419,7 +420,8 @@ struct Fn {
 	int ncon;
 	int nmem;
 	int nblk;
-	int retty;
+	int retty; /* index in typ[], -1 if no aggregate return */
+	Ref retr;
 	Blk **rpo;
 	bits reg;
 	int slot;
@@ -483,7 +485,7 @@ Ins *icpy(Ins *, Ins *, ulong);
 void *vnew(ulong, size_t);
 void vgrow(void *, ulong);
 int phicls(int, Tmp *);
-Ref newtmp(char *, Fn *);
+Ref newtmp(char *, int, Fn *);
 Ref getcon(int64_t, Fn *);
 void addcon(Con *, Con *);
 void dumpts(BSet *, Tmp *, FILE *);
diff --git a/lisc/ssa.c b/lisc/ssa.c
index 18178c3..269406b 100644
--- a/lisc/ssa.c
+++ b/lisc/ssa.c
@@ -282,7 +282,7 @@ fillfron(Fn *fn)
 static Ref
 refindex(int t, Fn *fn)
 {
-	return newtmp(fn->tmp[t].name, fn);
+	return newtmp(fn->tmp[t].name, fn->tmp[t].cls, fn);
 }
 
 static void
diff --git a/lisc/test/abi4.ssa b/lisc/test/abi4.ssa
new file mode 100644
index 0000000..4c3d89b
--- /dev/null
+++ b/lisc/test/abi4.ssa
@@ -0,0 +1,38 @@
+# return a large struct to C
+
+type :mem = { b 17 }
+
+function $alpha(l %p, w %l, l %n) {
+@ini
+	%pe =l add %p, %n
+@lop
+	%p1 =l phi @ini %p, @lop %p2
+	%l1 =w phi @ini %l, @lop %l2
+	storeb %l1, %p1
+	%p2 =l add %p1, 1
+	%l2 =w add %l1, 1
+	%c1 =w ceql %p1, %pe
+	jnz %c1, @end, @lop
+@end
+	storeb 0, %pe
+	ret
+}
+
+function :mem $test() {
+@start
+	%p =l alloc4 17
+	%r0 =w call $alpha(l %p, w 65, l 16)
+	ret %p
+}
+
+
+# >>> driver
+# #include <stdio.h>
+# typedef struct { char t[17]; } mem;
+# extern mem test(void);
+# int main() { mem m = test(); printf("%s\n", m.t); return 0; }
+# <<<
+
+# >>> output
+# ABCDEFGHIJKLMNOP
+# <<<
diff --git a/lisc/util.c b/lisc/util.c
index fc9884a..368a060 100644
--- a/lisc/util.c
+++ b/lisc/util.c
@@ -174,7 +174,7 @@ phicls(int t, Tmp *tmp /*, int c*/)
 }
 
 Ref
-newtmp(char *prfx, Fn *fn)
+newtmp(char *prfx, int k,  Fn *fn)
 {
 	static int n;
 	int t;
@@ -182,6 +182,7 @@ newtmp(char *prfx, Fn *fn)
 	t = fn->ntmp++;
 	vgrow(&fn->tmp, fn->ntmp);
 	sprintf(fn->tmp[t].name, "%s%d", prfx, ++n);
+	fn->tmp[t].cls = k;
 	fn->tmp[t].slot = -1;
 	fn->tmp[t].nuse = +1;
 	fn->tmp[t].ndef = +1;