From 240cfcd5cdda9301457867f9d26edcd0cf272b24 Mon Sep 17 00:00:00 2001 From: Quentin Carbonneaux Date: Thu, 17 Mar 2016 13:41:12 -0400 Subject: support return of structs --- lisc/isel.c | 253 +++++++++++++++++++++++++++++++---------------------- lisc/lisc.h | 6 +- lisc/ssa.c | 2 +- lisc/test/abi4.ssa | 38 ++++++++ lisc/util.c | 3 +- 5 files changed, 193 insertions(+), 109 deletions(-) create mode 100644 lisc/test/abi4.ssa 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; iinmem) 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; iinmem) { @@ -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 +# 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; -- cgit 1.4.1