commit 240cfcd5cdda9301457867f9d26edcd0cf272b24
parent 2889d44f24d8017f527f6f5aa1163065cc57e994
Author: Quentin Carbonneaux <quentin.carbonneaux@yale.edu>
Date: Thu, 17 Mar 2016 13:41:12 -0400
support return of structs
Diffstat:
5 files changed, 193 insertions(+), 109 deletions(-)
diff --git 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
@@ -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
@@ -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
@@ -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
@@ -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;