diff options
| author | 2023-06-03 21:51:28 +0200 | |
|---|---|---|
| committer | 2023-06-04 10:20:19 +0200 | |
| commit | 2ca24f83c35b253593b5aa8775d37923c8383149 (patch) | |
| tree | 09fc86a228b81ac574233a922758953c4c460861 | |
| parent | 65ace14e184807df026e985e073b3b5c5aaf576c (diff) | |
abi lowering pass
| -rw-r--r-- | Makefile | 6 | ||||
| -rw-r--r-- | abi0.c | 361 | ||||
| -rw-r--r-- | abistruct.c | 148 | ||||
| -rw-r--r-- | amd64/sysv.c | 74 | ||||
| -rw-r--r-- | common.h | 7 | ||||
| -rw-r--r-- | intrin.def (renamed from builtin.def) | 0 | ||||
| -rw-r--r-- | ir.c | 120 | ||||
| -rw-r--r-- | ir.h | 55 | ||||
| -rw-r--r-- | irdump.c | 30 | ||||
| -rw-r--r-- | op.def | 3 | ||||
| -rw-r--r-- | parse.c | 138 | ||||
| -rw-r--r-- | regalloc.c | 20 | ||||
| -rw-r--r-- | test.c | 11 | ||||
| -rw-r--r-- | type.c | 6 |
14 files changed, 667 insertions, 312 deletions
@@ -1,4 +1,4 @@ -SRC=main.c io.c mem.c parse.c lex.c type.c targ.c eval.c ir.c irdump.c abistruct.c regalloc.c amd64/sysv.c +SRC=main.c io.c mem.c parse.c lex.c type.c targ.c eval.c ir.c irdump.c abi0.c regalloc.c amd64/sysv.c CFLAGS=-Wall -std=c11 -pedantic OBJ=$(patsubst %.c,obj/%.o,$(SRC)) OUT=cchomp @@ -19,7 +19,7 @@ obj/%.o: %.c common.h @mkdir -p `dirname $@` $(CC) $(CFLAGS) -c -o $@ $< -ir.h: op.def builtin.def +ir.h: op.def intrin.def obj/main.o: parse.h obj/parse.o: parse.h ir.h obj/ir.o: ir.h @@ -27,7 +27,7 @@ obj/irdump.o: ir.h obj/lex.o: parse.h obj/eval.o: parse.h obj/io.o: parse.h keywords.def -obj/abistruct.o: ir.h +obj/abi0.o: ir.h obj/regalloc.o: ir.h obj/amd64/sysv.o: ir.h amd64/all.h @@ -0,0 +1,361 @@ +#include "ir.h" + +/** This pass adds in ABI arguments/returns register mappings + ** and lowers aggregate params/args/returns into scalars + **/ + +struct abiargsvec { vec_of(struct abiarg); }; + +static int +abiret(struct abiarg abiret[2], struct abiargsvec *abiargs, int *ni, union irtype retty) +{ + short r[2]; + uchar cls[2]; + int retreg = 0; + if (retty.isagg) { + retreg = mctarg->abiret(r, cls, ni, retty); + if (!retreg) { + vpush(abiargs, ((struct abiarg) { {.cls = KPTR}, r[1] })); + if (r[0] == -1) { + memset(abiret, 0, 2*sizeof *abiret); + } else { + abiret[0].ty = (union irtype) {.cls = KPTR}; + abiret[0].reg = r[0]; + } + } + } else if (retty.cls) { + retreg = mctarg->abiret(r, cls, ni, retty); + assert(retreg == 1); + } + for (int i = 0; i < retreg; ++i) { + abiret[i].ty = (union irtype) {.cls = cls[i]}; + abiret[i].reg = r[i]; + } + return retreg; +} + +static int +abiarg(struct abiargsvec *abiargs, int *ni, int *nf, int *ns, union irtype ty) +{ + short r[2]; + uchar cls[2]; + int ret = mctarg->abiarg(r, cls, ni, nf, ns, ty); + if (!ret) { /* aggregate in stack */ + vpush(abiargs, ((struct abiarg) { ty, -1 })); + } else if (ret == 1 && ty.isagg && cls[0] == KPTR) { /* aggregate by pointer */ + vpush(abiargs, ((struct abiarg) { {.cls = cls[0]}, r[0] })); + } else { + vpush(abiargs, ((struct abiarg) { {.cls = cls[0]}, r[0] })); + if (ret == 2) + vpush(abiargs, ((struct abiarg) { {.cls = cls[1]}, r[1] })); + } + return ret; +} + +/* RPARAM can only appear in the entry block (prologue), each RARG can only appear once. + * this function patches param starting at instruction no. *start according to cls + * to patch it to use arg no. `to' (and maybe also `to + 1') */ +static void +patchparam(struct function *fn, int *start, int param, int tydat, int to, struct abiarg abi[2]) +{ + struct block *blk = fn->entry; + assert(!blk->phi.n); + while((*start)++ < blk->ins.n) { + struct instr *ins = &instrtab[blk->ins.p[*start - 1]]; + if (ins->op == Ocopy && ins->l.t == RPARAM && ins->l.i == param) { + /* originally aggregate argument */ + assert(tydat != -1); + if (abi[0].ty.isagg /* aggregate in stack */ + || abi[0].ty.cls == KPTR) /* aggregate by pointer */ + { + ins->l.i = to; + } else { /* aggregate in registers */ + const struct typedata *td = &typedata[tydat]; + /* transform + * %x = copy %argX + * into + * %x = alloca... + * store* %x, %argN + * store* %x + I, %argM + */ + assert(td->siz <= 16 && td->align <= 16); + ins->op = Oalloca8 + (td->align == 16); + ins->l = mkref(RICON, td->align == 16 ? 1 : td->siz / 8); + insertinstr(blk, *start, mkinstr(Ostore1 + ilog2(cls2siz[abi[0].ty.cls]), 0, + mkref(RTMP, ins - instrtab), mkref(RPARAM, to))); + *start += 1; + if (td->siz > 8) { + struct instr tmp = mkinstr(Oadd, KPTR, + mkref(RTMP, ins - instrtab), mkref(RICON, cls2siz[abi[0].ty.cls])); + insertinstr(blk, *start+1, mkinstr(Ostore1 + ilog2(cls2siz[abi[1].ty.cls]), 0, + insertinstr(blk, *start, tmp), mkref(RPARAM, to+1))); + *start += 2; + } + } + break; + } else if (oisstore(ins->op) && ins->r.t == RPARAM && ins->r.i == param) { + /* normal scalar argument */ + assert(tydat == -1); + ins->r.i = to; + break; + } + } +} + +static int +patcharg(union ref newargs[2], union irtype newtyps[2], struct block *blk, int *icall, + struct call *call, int arg, int nabi, struct abiarg abi[2]) +{ + if (call->typs[arg].isagg) { + /* originally aggregate argument */ + if (abi[0].ty.isagg /* aggregate in stack */ + || abi[0].ty.cls == KPTR) /* aggregate by pointer */ + { + newargs[0] = call->args[arg]; + newtyps[0] = call->typs[arg]; + } else { /* aggregate in registers */ + union ref src = call->args[arg]; + /* deconstruct into + * %a = load* %x + * (%b = load* %x + N) + */ + for (int i = 0; i < nabi; ++i) { + /* XXX this can generate unaligned loads */ + struct instr ins = {0}; + switch (ins.cls = abi[i].ty.cls) { + default: assert(0); + case KI4: ins.op = Oloadu4; break; + case KI8: ins.op = Oloadi8; break; + case KF4: ins.op = Oloadf4; break; + case KF8: ins.op = Oloadf8; break; + } + if (i == 0) + ins.l = src; + else + ins.l = insertinstr(blk, (*icall)++ - 1, + mkinstr(Oadd, KPTR, src, + mkref(RICON, cls2siz[abi[0].ty.cls]))); + newargs[i] = insertinstr(blk, (*icall)++ - 1, ins); + } + return nabi; + } + } else { + /* normal scalar argument */ + newargs[0] = call->args[arg]; + newtyps[0] = call->typs[arg]; + } + return 1; +} + +void +abi0(struct function *fn) +{ + uint nparam = typedata[fn->fnty.dat].nmemb; + const union type *paramty = typedata[fn->fnty.dat].param; + static struct abiarg abiargsbuf[32]; + struct abiargsvec abiargs = {VINIT(abiargsbuf, arraylength(abiargsbuf))}; + int rvovar = -1; + int ni = 0, nf = 0, ns = 0, istart = 0; + struct block *blk; + union ref sret; + bool sretarghidden = 0; + + if (fn->retty.t == TYVOID) { + fn->nabiret = 0; + } else { + fn->nabiret = abiret(fn->abiret, &abiargs, &ni, mkirtype(fn->retty)); + if (!fn->nabiret && isagg(fn->retty)) { /* ret by hidden pointer */ + sretarghidden = ni == 0; + sret = insertinstr(fn->entry, 0, mkinstr(Ocopy, KPTR, mkref(RPARAM, 0))); + ++istart; + } + } + + /* adjust params */ + for (int i = 0; i < nparam; ++i) { + union irtype pty = mkirtype(paramty[i]); + int thisi = sretarghidden + ni + nf + ns; + int first = abiargs.n; + int ret = abiarg(&abiargs, &ni, &nf, &ns, pty); + if (i != thisi || (pty.isagg && ret)) + patchparam(fn, &istart, i, pty.isagg ? pty.dat : -1, thisi, &abiargs.p[first]); + } + fn->abiarg = alloccopy(&fn->arena, abiargs.p, abiargs.n * sizeof *abiargs.p, 0); + fn->nabiarg = abiargs.n; + vfree(&abiargs); + + if (!fn->nabiret && isagg(fn->retty)) { + /* for structures returned by hidden pointer argument, + * if all return instrs return local var X, make X point to the result location, + * (return value optimization (RVO)) */ + blk = fn->entry; + do { + union ref arg = blk->jmp.arg[0]; + if (blk->jmp.t != Jret) continue; + if (!arg.t) continue; + if (arg.t != RTMP || !oisalloca(instrtab[arg.idx].op)) { + rvovar = -1; + break; + } + if (rvovar == -1) { + rvovar = arg.idx; + } else if (arg.idx != rvovar) { + rvovar = -1; + break; + } + } while ((blk = blk->lnext) != fn->entry); + if (rvovar != -1) + instrtab[rvovar] = mkinstr(Ocopy, KPTR, sret); + } + + blk = fn->entry->lnext; + do { + /* adjust calls */ + for (int iinstr = 0; iinstr < blk->ins.n; ++iinstr) { + struct instr *ins = &instrtab[blk->ins.p[iinstr]]; + struct call *call = &calltab.p[ins->r.idx]; + static union ref newargsbuf[32]; + static union irtype newtypsbuf[32]; + vec_of(union ref) newargs; + vec_of(union irtype) newtyps; + bool structbyval; + int vararg; + + if (ins->op != Ocall) continue; + + vararg = call->vararg; + vinit(&abiargs, abiargsbuf, arraylength(abiargsbuf)); + vinit(&newargs, newargsbuf, arraylength(newargsbuf)); + vinit(&newtyps, newtypsbuf, arraylength(newtypsbuf)); + if (!(structbyval = call->sret)) + for (int i = 0; i < call->narg; ++i) + if ((structbyval = call->typs[i].isagg)) + break; + + ni = nf = ns = 0; + memset(call->abiret, 0, sizeof call->abiret); + if (call->sret) { + union irtype retty = call->typs[call->narg]; + int ret = abiret(call->abiret, &abiargs, &ni, retty); + if (retty.isagg) { + union ref temp; + struct instr alloca = { .cls = KPTR }; + struct typedata *td = &typedata[retty.dat]; + + sretarghidden = ni == 0; + alloca.op = Oalloca8 + (td->align == 16); + alloca.l = mkref(RICON, td->align == 16 ? 1 : td->siz / 8); + temp = insertinstr(blk, iinstr++, alloca); + replref(fn, blk, iinstr, mkref(RTMP, ins - instrtab), temp); + if (!ret) { /* hidden pointer argument */ + vpush(&newargs, temp); + vpush(&newtyps, (union irtype) {.cls = KPTR}); + ins->cls = 0; + } else { + union ref call2r; + int to = iinstr + 1; + assert(in_range(ret, 1, 2)); + ins->cls = call->abiret[0].ty.cls; + if (ret == 2) + call2r = insertinstr(blk, to++, mkinstr(Ocall2r, call->abiret[1].ty.cls, + mkref(RTMP, ins - instrtab))); + for (int i = 0; i < ret; ++i) { + uchar cls; + struct instr store = {0}; + /* XXX this can generate unaligned stores */ + switch (cls = call->abiret[i].ty.cls) { + default: assert(0); + case KF4: case KI4: store.op = Ostore4; break; + case KI8: case KF8: store.op = Ostore8; break; + } + if (i == 0) { + store.l = temp; + store.r = mkref(RTMP, ins - instrtab); + } else { + store.l = insertinstr(blk, to++, + mkinstr(Oadd, KPTR, temp, + mkref(RICON, cls2siz[call->abiret[0].ty.cls]))); + store.r = call2r; + } + insertinstr(blk, to++, store); + } + } + } + } else if (ins->cls) { + int ret = abiret(call->abiret, &abiargs, &ni, (union irtype){.cls = ins->cls}); + assert(ret == 1 && !ni); + } + + for (int i = 0; i < call->narg; ++i) { + union irtype pty = call->typs[i]; + int thisi = sretarghidden + ni + nf + ns; + int first = abiargs.n; + int ret = abiarg(&abiargs, &ni, &nf, &ns, pty); + if (structbyval) { + union ref argss[2]; + union irtype typss[2]; + assert(structbyval); + ret = patcharg(argss, typss, blk, &iinstr, call, i, ret, &abiargs.p[first]); + vpushn(&newargs, argss, ret); + vpushn(&newtyps, typss, ret); + if (call->vararg == i) + vararg = thisi; + } + } + call->sret = 0; + call->vararg = vararg; + if (structbyval) { + if (newargs.n != call->narg) { + call->args = alloc(&fn->arena, newargs.n * (sizeof *newargs.p + sizeof *newtyps.p), 0); + call->typs = (union irtype *)((char *)call->args + newargs.n * sizeof *newargs.p); + } + memcpy(call->args, newargs.p, newargs.n * sizeof *call->args); + memcpy(call->typs, newtyps.p, newtyps.n * sizeof *call->typs); + call->abiargregs = alloc(&fn->arena, abiargs.n * sizeof *call->abiargregs, 0); + for (int i = 0; i < abiargs.n; ++i) call->abiargregs[i] = abiargs.p[i].reg; + } + vfree(&abiargs); + vfree(&newargs); + vfree(&newtyps); + } + + /* adjust returns */ + if (isagg(fn->retty) && blk->jmp.t == Jret && blk->jmp.arg[0].t) { + assert(!blk->jmp.arg[1].t); + if (fn->nabiret) { /* aggregate return in register(s) */ + union ref src = blk->jmp.arg[0]; + for (int i = 0; i < fn->nabiret; ++i) { + /* XXX this can generate unaligned loads */ + struct instr ins = {0}; + switch (ins.cls = fn->abiret[i].ty.cls) { + default: assert(0); + case KI4: ins.op = Oloadu4; break; + case KI8: ins.op = Oloadi8; break; + case KF4: ins.op = Oloadf4; break; + case KF8: ins.op = Oloadf8; break; + } + if (i == 0) + ins.l = src; + else + ins.l = insertinstr(blk, blk->ins.n, + mkinstr(Oadd, KPTR, src, + mkref(RICON, cls2siz[fn->abiret[0].ty.cls]))); + blk->jmp.arg[i] = insertinstr(blk, blk->ins.n, ins); + } + } else { + /* aggregate return (arg[0] is pointer to return value) */ + if (rvovar == -1) { + /* blit %sret, %arg */ + union ref args[2] = { sret, blk->jmp.arg[0] }; + union irtype typ[2] = { mkirtype(fn->retty) }; + typ[1] = typ[0]; + insertinstr(blk, blk->ins.n, mkintrin(fn, INstructcopy, 0, 2, args, typ)); + } else assert(blk->jmp.arg[0].bits == mkref(RTMP, rvovar).bits); + if (fn->abiret[0].ty.cls) blk->jmp.arg[0] = rvovar == -1 ? sret : mkref(RTMP, rvovar); + else memset(blk->jmp.arg, 0, sizeof blk->jmp.arg); + } + } + } while ((blk = blk->lnext) != fn->entry); +} + +/* vim:set ts=3 sw=3 expandtab: */ diff --git a/abistruct.c b/abistruct.c deleted file mode 100644 index 36b686b..0000000 --- a/abistruct.c +++ /dev/null @@ -1,148 +0,0 @@ -#include "common.h" -#include "ir.h" - -/* This pass lowers aggregate params/args/returns into scalars, according to abi */ - -/* RARG can only appear in the entry block (prologue), each RARG can only appear once - * this function patches arg starting at instruction no. *start according to cls - * to redirect it to use arg no. `to' (and maybe also `to + 1') - */ -static void -patcharg(struct function *fn, int *start, int arg, int tydat, int to, uchar cls[2]) -{ - struct block *blk = fn->entry; - assert(!blk->phi.n); - while((*start)++ < blk->ins.n) { - struct instr *ins = &instrtab[blk->ins.p[*start - 1]]; - if (ins->op == Ocopy && ins->l.t == RARG && ins->l.i == arg) { - /* originally aggregate argument */ - assert(tydat != -1); - if (!cls[0]) { /* memory class */ - ins->l.i = to; - } else { /* aggregate in registers */ - const struct typedata *td = &typedata[tydat]; - /* transform - * %x = copy %argX - * into - * %x = alloca... - * store* %x, %argN - * store* %x + I, %argM - */ - assert(td->siz <= 16 && td->align <= 16); - ins->op = Oalloca8 + (td->align == 16); - ins->l = mkref(RICON, td->align == 16 ? 1 : td->siz / 8); - insertinstr(blk, *start, mkinstr(Ostore1 + ilog2(cls2siz[cls[0]]), 0, - mkref(RTMP, ins - instrtab), mkref(RARG, to))); - *start += 1; - if (cls[1]) { - struct instr tmp = mkinstr(Oadd, KPTR, - mkref(RTMP, ins - instrtab), mkref(RICON, cls2siz[cls[0]])); - insertinstr(blk, *start+1, mkinstr(Ostore1 + ilog2(cls2siz[cls[1]]), 0, - insertinstr(blk, *start, tmp), mkref(RARG, to+1))); - *start += 2; - } - } - break; - } else if (oisstore(ins->op) && ins->r.t == RARG && ins->r.i == arg) { - /* normal scalar argument */ - assert(tydat == -1); - ins->r.i = to; - break; - } - } -} - -void -abistruct(struct function *fn) -{ - uint nparam = typedata[fn->fnty.dat].nmemb; - const union type *paramty = typedata[fn->fnty.dat].param; - vec_of(struct abiarg) abiargs = {0}; - int retreg = 0; - struct abiarg retval[2]; - int ni = 0, nf = 0, mi = 0, istart = 0; - short r[2]; - uchar cls[2]; - struct block *blk; - union ref sret; - - if (isagg(fn->retty)) { - retreg = mctarg->abi_retregs(r, cls, mkirtype(fn->retty)); - if (!retreg) { - /* return location is first (pointer) argument */ - vpush(&abiargs, ((struct abiarg) { {.cls = KPTR}, r[0] })); - ++ni; - sret = insertinstr(fn->entry, 0, mkinstr(Ocopy, KPTR, mkref(RARG, 0))); - ++istart; - } else for (int i = 0; i < retreg; ++i) { - /* return in 1 or 2 registers */ - retval[i].ty = (union irtype) {.cls = cls[i]}; - retval[i].reg = r[i]; - } - } - memcpy(fn->abiret, retval, sizeof retval); - fn->nabiret = retreg; - - /* adjust params */ - for (int i = 0, newi; i < nparam; ++i) { - union irtype pty = mkirtype(paramty[i]); - int ret; - assert(mctarg->abi_argregs); - newi = ni + nf + mi; - ret = mctarg->abi_argregs(r, cls, &ni, &nf, pty); - if (!ret) { /* memory */ - vpush(&abiargs, ((struct abiarg) { pty, -1 })); - ++mi; - } else { - vpush(&abiargs, ((struct abiarg) { {.cls = cls[0]}, r[0] })); - if (ret == 2) - vpush(&abiargs, ((struct abiarg) { {.cls = cls[1]}, r[1] })); - } - if (i != newi || (pty.isagg && ret)) - patcharg(fn, &istart, i, pty.isagg ? pty.dat : -1, newi, cls); - } - fn->abiarg = abiargs.p; - fn->nabiarg = abiargs.n; - - /* adjust calls and returns */ - blk = fn->entry; - do { - for (int i = 0; i < blk->ins.n; ++i) { - struct instr *ins = &instrtab[blk->ins.p[i]]; - } - if (isagg(fn->retty) && blk->jmp.t == Jret && blk->jmp.arg[0].t) { - /* aggregate return (arg[0] is pointer to return value) */ - assert(!blk->jmp.arg[1].t); - if (retreg) { - union ref src = blk->jmp.arg[0]; - for (int i = 0; i < retreg; ++i) { - /* XXX this can generate unaligned loads */ - struct instr ins = {0}; - switch (ins.cls = retval[i].ty.cls) { - default: assert(0); - case KI4: ins.op = Oloadu4; break; - case KI8: ins.op = Oloadi8; break; - case KF4: ins.op = Oloadf4; break; - case KF8: ins.op = Oloadf8; break; - } - if (i == 0) - ins.l = src; - else - ins.l = insertinstr(blk, blk->ins.n, - mkinstr(Oadd, KPTR, src, - mkref(RICON, cls2siz[retval[0].ty.cls]))); - blk->jmp.arg[i] = insertinstr(blk, blk->ins.n, ins); - } - } else { - /* blit %sret, %arg */ - union ref args[2] = { sret, blk->jmp.arg[0] }; - union irtype typ[2] = { mkirtype(fn->retty) }; - typ[1] = typ[0]; - insertinstr(blk, blk->ins.n, mkbuiltin(fn, BTstructcopy, 0, 2, args, typ)); - memset(&blk->jmp.arg[0], 0, sizeof(union ref)); - } - } - } while ((blk = blk->lnext) != fn->entry); -} - -/* vim:set ts=3 sw=3 expandtab: */ diff --git a/amd64/sysv.c b/amd64/sysv.c index 55739e8..be9fbde 100644 --- a/amd64/sysv.c +++ b/amd64/sysv.c @@ -1,5 +1,31 @@ #include "all.h" + +static int classify(uchar cls[2], const struct typedata *td, uint off); +static int +classifyarr(uchar cls[2], union type ty, uint off) +{ + union type chld = typechild(ty); + uint n = typearrlen(ty), siz = typesize(chld); + assert(n > 0); + for (uint i = 0; i < n; ++i) { + uint offx = off + i * siz; + if (isagg(chld)) { + if (!classify(cls, &typedata[chld.dat], offx)) + return cls[0] = cls[1] = 0; + } else if (chld.t == TYARRAY) { + if (!classifyarr(cls, chld, offx)) + return cls[0] = cls[1] = 0; + } else if (isflt(chld)) { /* SSE */ + if (!cls[offx/8]) + cls[offx/8] = KF8; + } else { /* INTEGER */ + assert(isint(chld)); + cls[offx/8] = KI8; + } + } + return !!cls[0] + !!cls[1]; +} static int classify(uchar cls[2], const struct typedata *td, uint off) { @@ -9,15 +35,20 @@ classify(uchar cls[2], const struct typedata *td, uint off) for (int i = 0; i < td->nmemb; ++i) { struct fielddata *fld = &td->fld[i].f; uint align = typealign(fld->t); - if (alignup(fld->off, align) != fld->off) /* unaligned field */ + if (alignup(fld->off, align) != fld->off) /* unaligned field -> MEMORY */ return cls[0] = cls[1] = 0; if (isagg(fld->t)) { - if (!classify(cls, &typedata[fld->t.dat], fld->off)) + if (!classify(cls, &typedata[fld->t.dat], off + fld->off)) + return cls[0] = cls[1] = 0; + } else if (fld->t.t == TYARRAY) { + if (isincomplete(fld->t)) continue; + if (!classifyarr(cls, fld->t, off + fld->off)) return cls[0] = cls[1] = 0; } else if (isflt(fld->t)) { /* SSE */ if (!cls[(fld->off + off)/8]) cls[(fld->off + off)/8] = KF8; } else { /* INTEGER */ + assert(isint(fld->t)); cls[(fld->off + off)/8] = KI8; } } @@ -25,30 +56,32 @@ classify(uchar cls[2], const struct typedata *td, uint off) } static int -argabi(short r[2], uchar cls[2], int *ni, int *nf, union irtype typ) +abiarg(short r[2], uchar cls[2], int *ni, int *nf, int *ns, union irtype typ) { static const uchar intregs[] = { RDI, RSI, RDX, RCX, R8, R9 }; enum { NINT = arraylength(intregs), NFLT = 8 }; int ret, ni_save, nf_save; if (!typ.isagg) { - if (kisflt(typ.cls) && *nf < NFLT) { - cls[0] = KF8; /* SSE */ + if (kisflt(cls[0] = typ.cls) && *nf < NFLT) { r[0] = XMM0 + (*nf)++; - return 1; - } - if (*ni < NINT) { - cls[0] = KI8; /* INTEGER */ + } else if (*ni < NINT) { r[0] = intregs[(*ni)++]; - return 1; + } else { + ++*ns; + r[0] = -1; + return 0; /* MEMORY */ } - return 0; /* MEMORY */ + return 1; } cls[0] = cls[1] = 0; ret = classify(cls, &typedata[typ.dat], 0); - if (!ret) return 0; - ni_save = *ni, nf_save = *nf; + if (!ret) { /*MEMORY*/ + ++*ns; + return 0; + } assert(ret <= 2); + ni_save = *ni, nf_save = *nf; for (int i = 0; i < ret; ++i) { assert(cls[i]); if (cls[i] == KF8 && *nf < NFLT) @@ -57,6 +90,8 @@ argabi(short r[2], uchar cls[2], int *ni, int *nf, union irtype typ) r[i] = intregs[(*ni)++]; else { /* MEMORY */ *ni = ni_save, *nf = nf_save; + ++*ns; + r[0] = r[1] = -1; return cls[0] = cls[1] = 0; } } @@ -64,15 +99,18 @@ argabi(short r[2], uchar cls[2], int *ni, int *nf, union irtype typ) } static int -retabi(short r[2], uchar cls[2], union irtype typ) +abiret(short r[2], uchar cls[2], int *ni, union irtype typ) { int ret; - if (!typ.isagg) return kisflt(typ.cls) ? XMM0 : RAX; + if (!typ.isagg) return kisflt(cls[0] = typ.cls) ? XMM0 : RAX; cls[0] = cls[1] = 0; ret = classify(cls, &typedata[typ.dat], 0); if (!ret) { /* MEMORY */ - r[0] = RDI; /* register for caller-owned result location argument */ + assert(*ni == 0); + r[0] = RAX; /* on return should contain result location address */ + r[1] = RDI; /* register for caller-owned result location argument */ + ++*ni; return 0; } assert(ret <= 2); @@ -99,8 +137,8 @@ const struct mctarg t_amd64_sysv = { .rcallee = {{1<<RBX | 1<<R12 | 1<<R13 | 1<<R14 | 1<<R15}}, .rglob = {{1<<RSP | 1<<RBP}}, .rnames = amd64_rnames, - .abi_argregs = argabi, - .abi_retregs = retabi, + .abiret = abiret, + .abiarg = abiarg, }; /* vim:set ts=3 sw=3 expandtab: */ @@ -68,7 +68,7 @@ popcnt(uvlong x) { } static inline bool ispo2(uvlong x) { - return x & ((x & (x - 1)) == 0); + return (x != 0) & ((x & (x - 1)) == 0); } static inline uint ilog2(uint x) { /* assumes x is a power of 2 */ @@ -306,6 +306,11 @@ struct arena { struct arena *newarena(uint chunksiz); void *alloc(struct arena **, uint siz, uint align); +static inline void * +alloccopy(struct arena **arena, const void *src, uint siz, uint align) +{ + return memcpy(alloc(arena, siz, align), src, siz); +} void freearena(struct arena *); void vinit_(void **p, int *pcap, void *inlbuf, int cap, uint siz); void vpush_(void **p, int *pcap, uint *pn, uint siz); @@ -8,6 +8,7 @@ const uchar siz2intcls[] = { [1] = KI4, [2] = KI4, [4] = KI4, [8] = KI8 }; static vec_of(struct irdat) dats; struct instr instrtab[1<<14]; static int ninstr; +static int instrfreelist; struct calltab calltab; struct phitab phitab; @@ -18,6 +19,7 @@ irinit(struct function *fn) static struct phi phisbuf[64]; ninstr = 0; + instrfreelist = -1; vinit(&calltab, callsbuf, arraylength(callsbuf)); vinit(&phitab, phisbuf, arraylength(phisbuf)); if (!type2cls[TYINT]) { @@ -162,54 +164,89 @@ mksymref(struct function *fn, const char *s) return mkref(RXCON, addcon(&con)); } +struct instr +mkalloca(uint siz, uint align) +{ + struct instr ins = { .cls = KPTR }; + assert(ispo2(align) && align <= 16); + ins.op = Oalloca1 + ilog2(align); + ins.l = mkref(RICON, siz/align + (siz%align != 0)); + return ins; +} + union ref -mkcallarg(struct function *fn, uint narg, int vararg, union ref *args, union irtype *typs) +mkcallarg(struct function *fn, bool sret, uint narg, int vararg, union ref *args, union irtype *typs) { - struct call call = { narg, vararg }; + struct call call = { .sret=sret, .narg=narg, .vararg=vararg }; assert((long) vararg <= narg); if (narg) { - call.args = alloc(&fn->arena, narg*sizeof *args + narg*sizeof(union irtype), 0); + call.args = alloc(&fn->arena, narg*sizeof *args + (narg+sret)*sizeof(union irtype), 0); call.typs = (union irtype *)((char *)call.args + narg*sizeof *args); memcpy(call.args, args, narg*sizeof *args); - memcpy(call.typs, typs, narg*sizeof *typs); + memcpy(call.typs, typs, (narg+sret)*sizeof *typs); } vpush(&calltab, call); return mkref(RMORE, calltab.n-1); } +static inline int +newinstr(void) +{ + if (instrfreelist != -1) { + int t = instrfreelist; + memcpy(&instrfreelist, &instrtab[instrfreelist], sizeof(int)); + return t; + } + assert(ninstr < arraylength(instrtab)); + return ninstr++; +} + union ref addinstr(struct function *fn, struct instr ins) { - assert(ninstr < arraylength(instrtab)); + int new = newinstr(); assert(fn->curblk != NULL); - instrtab[ninstr] = ins; - vpush(&fn->curblk->ins, ninstr); - return mkref(RTMP, ninstr++); + instrtab[new] = ins; + vpush(&fn->curblk->ins, new); + return mkref(RTMP, new); } union ref insertinstr(struct block *blk, int idx, struct instr ins) { - assert(ninstr < arraylength(instrtab)); - instrtab[ninstr] = ins; - if (idx == blk->ins.n) vpush(&blk->ins, ninstr); + int new = newinstr(); + instrtab[new] = ins; + if (idx == blk->ins.n) vpush(&blk->ins, new); else { assert(idx >= 0 && idx < blk->ins.n); vpush_((void **)&blk->ins.p, &blk->ins._cap, &blk->ins.n, sizeof *blk->ins.p); for (int i = blk->ins.n++; i > idx; --i) blk->ins.p[i] = blk->ins.p[i - 1]; - blk->ins.p[idx] = ninstr; + blk->ins.p[idx] = new; } - return mkref(RTMP, ninstr++); + return mkref(RTMP, new); +} + +void +delinstr(struct block *blk, int idx) +{ + assert(idx >= 0 && idx < blk->ins.n); + memcpy(&instrtab[blk->ins.p[idx]], &instrfreelist, sizeof(int)); + instrfreelist = idx; + for (int i = idx; i < blk->ins.n; ++i) + blk->ins.p[i] = blk->ins.p[i + 1]; + --blk->ins.n; } union ref addphi2(struct function *fn, enum irclass cls, struct block *b1, union ref r1, struct block *b2, union ref r2) { + int new; struct phi phi = { .n = 2, .cap = -1 }; struct instr ins = { Ophi, cls }; + phi.blk = alloc(&fn->arena, 2*sizeof(struct block *) + 2*sizeof r1, 0); phi.ref = (union ref *)((char *)phi.blk + 2*sizeof(struct block *)); phi.blk[0] = b1; @@ -218,19 +255,21 @@ addphi2(struct function *fn, enum irclass cls, phi.ref[1] = r2; vpush(&phitab, phi); ins.l = mkref(RMORE, phitab.n-1); - assert(ninstr < arraylength(instrtab)); assert(fn->curblk != NULL); assert(fn->curblk->ins.n == 0); - instrtab[ninstr] = ins; - vpush(&fn->curblk->phi, ninstr); - return mkref(RTMP, ninstr++); + new = newinstr(); + instrtab[new] = ins; + vpush(&fn->curblk->phi, new); + return mkref(RTMP, new); } union ref addphi(struct function *fn, enum irclass cls, struct block **blk, union ref *ref, uint n) { + int new; struct phi phi = { .n = n, .cap = -1 }; struct instr ins = { Ophi, cls }; + assert(n > 0); phi.blk = alloc(&fn->arena, n*sizeof(struct block *) + n*sizeof(union ref), 0); phi.ref = (union ref *)((char *)phi.blk + n*sizeof(struct block *)); @@ -238,12 +277,12 @@ addphi(struct function *fn, enum irclass cls, struct block **blk, union ref *ref memcpy(phi.ref, ref, n * sizeof(union ref)); vpush(&phitab, phi); ins.l = mkref(RMORE, phitab.n-1); - assert(ninstr < arraylength(instrtab)); assert(fn->curblk != NULL); assert(fn->curblk->ins.n == 0); - instrtab[ninstr] = ins; - vpush(&fn->curblk->phi, ninstr); - return mkref(RTMP, ninstr++); + new = newinstr(); + instrtab[new] = ins; + vpush(&fn->curblk->phi, new); + return mkref(RTMP, new); } struct block * @@ -300,6 +339,34 @@ putreturn(struct function *fn, union ref r0, union ref r1) #undef putjump +void +replref(struct function *fn, struct block *blk, int i0, union ref from, union ref to) +{ + do { + if (!i0) for (int i = 0; i < blk->phi.n; ++i) { + struct phi *phi = &phitab.p[instrtab[blk->phi.p[i]].l.idx]; + for (int i = 0; i < phi->n; ++i) + if (phi->ref[i].bits == to.bits) phi->ref[i] = from; + } + + for (int i = i0; i < blk->ins.n; ++i) { + struct instr *ins = &instrtab[blk->ins.p[i]]; + for (int i = 0; i < 2; ++i) { + union ref *r = &(&ins->l)[i]; + if (r->bits == from.bits) *r = to; + else if (r->t == RMORE) { + struct call *call = &calltab.p[r->idx]; + assert(ins->op == Ocall || ins->op == Ointrin); + for (int i = 0; i < call->narg; ++i) + if (call->args[i].bits == from.bits) + call->args[i] = to; + } + } + } + i0 = 0; + } while ((blk = blk->lnext) != fn->entry); +} + static void freefn(struct function *fn) { @@ -314,10 +381,13 @@ freefn(struct function *fn) void irfini(struct function *fn) { - abistruct(fn); - regalloc(fn); - efmt("after regalloc:\n"); - irdump(fn, fn->name); + extern int nerror; + if (!nerror) { + abi0(fn); + regalloc(fn); + efmt("after regalloc:\n"); + irdump(fn, fn->name); + } freefn(fn); } @@ -42,11 +42,19 @@ struct xcon { }; }; +struct abiarg { + union irtype ty; + short reg; /* -1 -> stack */ +}; + struct call { - short narg; + ushort narg : 15; + ushort sret : 1; short vararg; /* first variadic arg or -1 */ - union irtype *typs; union ref *args; + union irtype *typs; + short *abiargregs; + struct abiarg abiret[2]; }; struct phi { @@ -58,7 +66,7 @@ struct phi { enum refkind { RNONE, RTMP, /* reference to another instruction's result */ - RARG, /* function argument */ + RPARAM, /* function param */ RICON, /* small integer constants */ RXCON, /* other constants (incl. external symbols) */ RMORE, /* reference to extra data for Ocall and Ophi */ @@ -82,10 +90,10 @@ enum op { #define oisalloca(o) in_range(o, Oalloca1, Oalloca16) #define oisstore(o) in_range(o, Ostore1, Ostore8) -enum builtin { - BTxxx, -#define _(b,...) BT##b, -#include "builtin.def" +enum intrin { + INxxx, +#define _(b,...) IN##b, +#include "intrin.def" #undef _ }; @@ -108,11 +116,6 @@ struct block { struct block *lprev, *lnext; }; -struct abiarg { - union irtype ty; - short reg; /* -1 -> stack */ -}; - struct function { struct arena *arena; const char *name; @@ -134,8 +137,24 @@ struct mctarg { struct bitset rcallee[1], /* callee-saved */ rglob[1]; /* globally live (never used for regalloc) */ const char (*rnames)[6]; - int (*abi_argregs)(short r[2], uchar cls[2], int *ni, int *nf, union irtype); - int (*abi_retregs)(short r[2], uchar cls[2], union irtype); + /* abiret: lower return type: + * scalar/small struct -> returns number of regs (1..2), + * r & cls filled with reg and irclass of each scalar return + * big struct -> returns 0, is passed via hidden pointer argument, + * r[0] contains register for returning said pointer or -1, + * r[1] contains register for hidden pointer argument, + * *ni is set to 1 if said register is the first ABI integer argument + */ + int (*abiret)(short r[2], uchar cls[2], int *ni, union irtype); + /* abiarg: lower argument type: + * scalar/small struct -> returns number of regs (1..2), + * r & cls filled with reg and irclass of each scalar arg + * if reg == -1 -> stack + * big struct -> returns 0, + * if passed in stack cls[0] == 0, r[0] == -1 + * if passed by pointer cls[0] == KPTR, r[0] contains integer register or -1 if stack + */ + int (*abiarg)(short r[2], uchar cls[2], int *ni, int *nf, int *ns, union irtype); }; extern uchar type2cls[]; @@ -148,15 +167,16 @@ extern struct phitab {vec_of(struct phi);} phitab; #define mkref(t, x) ((union ref) {{ (t), (x) }}) #define mkzerocon() ((union ref) {{ RICON, 0 }}) #define mkinstr(O, C, ...) ((struct instr) { .op = (O), .cls = (C), .reg=0, __VA_ARGS__ }) -#define mkbuiltin(F, B, C, n, arg, typ) mkinstr(Obuiltin, C, {.t=RICON,B}, mkcallarg(F,n,-1,arg,typ)) void irinit(struct function *); void irfini(struct function *); union irtype mkirtype(union type); union ref mkintcon(struct function *, enum irclass, vlong); union ref mkfltcon(struct function *, enum irclass, double); union ref mksymref(struct function *, const char *); +struct instr mkalloca(uint siz, uint align); void conputdat(struct irdat *, uint off, enum typetag t, const void *dat); -union ref mkcallarg(struct function *, uint narg, int vararg, union ref *, union irtype *); +union ref mkcallarg(struct function *, bool sret, uint narg, int vararg, union ref *, union irtype *); +#define mkintrin(F, B, C, N, A, T) mkinstr(Ointrin, C, {.t=RICON,B}, mkcallarg(F,0,N,-1,A,T)) union ref addinstr(struct function *, struct instr); union ref insertinstr(struct block *, int idx, struct instr); void delinstr(struct block *, int idx); @@ -168,10 +188,11 @@ void useblk(struct function *, struct block *); void putbranch(struct function *, struct block *); void putcondbranch(struct function *, union ref arg, struct block *t, struct block *f); void putreturn(struct function *, union ref r0, union ref r1); +void replref(struct function *, struct block *, int, union ref from, union ref to); void irdump(struct function *, const char *fname); -void abistruct(struct function *); +void abi0(struct function *); void regalloc(struct function *); /* vim:set ts=3 sw=3 expandtab: */ @@ -22,10 +22,10 @@ prityp(union irtype typ) } } -static const char *builtinname[] = { +static const char *intrinname[] = { "?\??", #define _(b,...) #b, -#include "builtin.def" +#include "intrin.def" #undef _ }; @@ -35,9 +35,9 @@ dumpref(enum op o, union ref ref) struct xcon *con; switch (ref.t) { case RTMP: efmt("%%%d", ref.idx); break; - case RARG: efmt("%%arg%d", ref.idx); break; + case RPARAM: efmt("%%param%d", ref.idx); break; case RICON: - if (o == Obuiltin) efmt("\"%s\"", builtinname[ref.i]); + if (o == Ointrin) efmt("\"%s\"", intrinname[ref.i]); else efmt("%d", ref.i); break; case RXCON: @@ -53,10 +53,14 @@ dumpref(enum op o, union ref ref) } break; case RMORE: - if (o == Ocall || o == Obuiltin) { + if (o == Ocall || o == Ointrin) { struct call *call = &calltab.p[ref.idx]; + if (call->sret) { + efmt("sret "); + prityp(call->typs[call->narg]); + } for (int i = 0; i < call->narg; ++i) { - if (i > 0) efmt(", "); + if (i > 0 || call->sret) efmt(", "); if (call->vararg == i) efmt("..., "); prityp(call->typs[i]); @@ -142,18 +146,24 @@ irdump(struct function *fn, const char *fname) struct block *blk; efmt("function %s : %ty\n", fname, fn->fnty); - if (fn->abiarg) { - efmt("abi args: ("); + if (fn->abiarg || fn->retty.t != TYVOID) { + efmt("abi: ("); for (int i = 0; i < fn->nabiarg; ++i) { if (i > 0) efmt(", "); if (fn->abiarg[i].reg != -1) { - efmt("%%%ls", mctarg->rnames[fn->abiarg[i].reg]); + efmt("%ls", mctarg->rnames[fn->abiarg[i].reg]); } else { prityp(fn->abiarg[i].ty); efmt(" <stk>"); } } - efmt(")\n"); + efmt(")"); + if (fn->retty.t != TYVOID) { + efmt(" -> %ls", mctarg->rnames[fn->abiret[0].reg]); + if (fn->nabiret > 1) + efmt(", %ls", mctarg->rnames[fn->abiret[1].reg]); + } + efmt("\n"); } blk = fn->entry; do { @@ -58,5 +58,6 @@ _(store2, 2) _(store4, 2) _(store8, 2) _(call, 2) -_(builtin, 2) +_(call2r, 1) +_(intrin, 2) _(phi, 1) @@ -952,44 +952,26 @@ commaexpr(struct parser *pr) static union ref expraddr(struct function *, const struct expr *); static union ref exprvalue(struct function *, const struct expr *); -static bool -exhasaddr(const struct expr *ex) -{ - if (ex->t == ECOND) - return exhasaddr(&ex->sub[1]) && exhasaddr(&ex->sub[2]); - if (ex->t == ESEQ) - return exhasaddr(&ex->sub[1]); - if (ex->t == ESET) - return exhasaddr(&ex->sub[0]); - if (ex->t == EGETF) - return !ex->fld.bitsiz; - return islvalue(ex); -} + static void structcopy(struct function *fn, union ref dst, const struct expr *src) { - if (exhasaddr(src)) { - union ref argsr; - union ref srcref = expraddr(fn, src); - union ref args[2] = { dst, srcref }; - union irtype typ[2] = { mkirtype(src->ty) }; - typ[1] = typ[0]; - argsr = mkcallarg(fn, 2, -1, args, typ); - addinstr(fn, mkinstr(Obuiltin, 0, mkref(RICON, BTstructcopy), argsr)); - } - assert(!"nyi"); + union ref srcref = expraddr(fn, src); + union ref args[2] = { dst, srcref }; + union irtype typ[2] = { mkirtype(src->ty) }; + typ[1] = typ[0]; + addinstr(fn, mkintrin(fn, INstructcopy, 0, 2, args, typ)); } static union ref structreturn(struct function *fn, const struct expr *src) { - if (exhasaddr(src)) { - return expraddr(fn, src); - } - assert(!"nyi"); + return expraddr(fn, src); } +static union ref compilecall(struct function *fn, const struct expr *ex); + static union ref expraddr(struct function *fn, const struct expr *ex) { @@ -1032,6 +1014,9 @@ expraddr(struct function *fn, const struct expr *ex) case ESEQ: (void)exprvalue(fn, &ex->sub[0]); return expraddr(fn, &ex->sub[1]); + case ECALL: + assert(isagg(ex->ty)); + return compilecall(fn, ex); default: assert(!"lvalue?>"); } @@ -1269,6 +1254,40 @@ condexprrec(struct function *fn, const struct expr *ex, struct condphis *phis, } } +static union ref +compilecall(struct function *fn, const struct expr *ex) +{ + struct instr ins = {0}; + struct expr *sub = ex->sub; + const struct typedata *td = &typedata[sub[0].ty.dat]; + union ref argsbuf[10]; + union irtype typbuf[10]; + vec_of(union ref) args = VINIT(argsbuf, arraylength(argsbuf)); + vec_of(union irtype) typs = VINIT(typbuf, arraylength(typbuf)); + + ins.op = Ocall; + if (isagg(ex->ty)) { + ins.cls = KPTR; + } else { + assert(isscalar(ex->ty) || ex->ty.t == TYVOID); + ins.cls = type2cls[ex->ty.t]; + } + ins.l = exprvalue(fn, &sub[0]); + for (int i = 0; i < ex->narg; ++i) { + struct expr *arg = &sub[i+1]; + union type ty = i < td->nmemb ? td->param[i] : argpromote(arg->ty); + vpush(&args, cvt(fn, ty.t, arg->ty.t, exprvalue(fn, arg))); + vpush(&typs, mkirtype(ty)); + } + if (isagg(ex->ty)) + vpush(&typs, mkirtype(ex->ty)); + ins.r = mkcallarg(fn, isagg(ex->ty), ex->narg, td->variadic ? td->nmemb : td->kandr ? 0 : -1, + args.p, typs.p); + vfree(&args); + vfree(&typs); + return addinstr(fn, ins); +} + /* the naive way to generate something like a ? b : c ? d : e, uses multiple phis, * this code reduces such nested conditional expressions into one phi */ static union ref @@ -1496,28 +1515,7 @@ exprvalue(struct function *fn, const struct expr *ex) genstore(fn, ex->ty, r, q); return narrow(fn, cls, ex->ty.t, q); case ECALL: - { - const struct typedata *td = &typedata[sub[0].ty.dat]; - union ref argsbuf[10]; - union irtype typbuf[10]; - vec_of(union ref) args = VINIT(argsbuf, arraylength(argsbuf)); - vec_of(union irtype) typs = VINIT(typbuf, arraylength(typbuf)); - ins.op = Ocall; - assert(isscalar(ex->ty) || ex->ty.t == TYVOID); - ins.cls = type2cls[ex->ty.t]; - ins.l = exprvalue(fn, &sub[0]); - for (int i = 0; i < ex->narg; ++i) { - struct expr *arg = &sub[i+1]; - union type ty = i < td->nmemb ? td->param[i] : argpromote(arg->ty); - vpush(&args, cvt(fn, ty.t, arg->ty.t, exprvalue(fn, arg))); - vpush(&typs, mkirtype(ty)); - } - ins.r = mkcallarg(fn, ex->narg, td->variadic ? td->nmemb : td->kandr ? 0 : -1, - args.p, typs.p); - vfree(&args); - vfree(&typs); - return addinstr(fn, ins); - } + return compilecall(fn, ex); case ECOND: if (ex->ty.t == TYVOID) { struct block *tr, *fl, *end; @@ -1670,8 +1668,6 @@ block(struct parser *pr, struct function *fn) struct declstate st = { DFUNCVAR }; do { struct decl decl = pdecl(&st, pr); - enum op op; - uint siz, align, nalloc; if (decl.name) { static int staticid; bool put = 0; @@ -1691,18 +1687,8 @@ block(struct parser *pr, struct function *fn) decl.ty); goto Err; } - switch (align = typealign(decl.ty)) { - case 1: op = Oalloca1; break; - case 2: op = Oalloca2; break; - case 4: op = Oalloca4; break; - case 8: op = Oalloca8; break; - case 16: op = Oalloca16; break; - default: assert(!"align"); - } - siz = typesize(decl.ty); - nalloc = siz/align + ((siz&(align-1)) != 0); EMITS { - decl.id = addinstr(fn, mkinstr(op, KPTR, mkintcon(fn, KI4, nalloc))).idx; + decl.id = addinstr(fn, mkalloca(typesize(decl.ty), typealign(decl.ty))).idx; } if (st.varini) { putdecl(pr, &decl); @@ -1725,7 +1711,7 @@ block(struct parser *pr, struct function *fn) Err: if (!put) putdecl(pr, &decl); } - } while (0); + } while (st.more); } else { stmt(pr, fn); } @@ -1740,29 +1726,17 @@ function(struct parser *pr, struct function *fn, const char **pnames, const stru const bool doemit = fn->curblk; struct env e; envdown(pr, &e); + /* add parameters to symbol table and create prologue (arguments) block */ for (int i = 0; i < td->nmemb; ++i) { if (pnames[i]) { - uint siz, align, nalloc; struct decl arg = { .ty = td->param[i], .qual = tdgetqual(td->quals, i), .name = pnames[i], .scls = SCAUTO, .span = pspans[i] }; - enum op op; - switch (align = typealign(arg.ty)) { - case 1: op = Oalloca1; break; - case 2: op = Oalloca2; break; - case 4: op = Oalloca4; break; - case 8: op = Oalloca8; break; - case 16: op = Oalloca16; break; - default: assert(!"align"); - } - siz = typesize(arg.ty); - nalloc = siz/align + ((siz&(align-1)) != 0); EMITS { if (isscalar(arg.ty)) { - struct instr alloca = { op, KPTR, .l = mkintcon(fn, KI4, nalloc) }; - arg.id = addinstr(fn, alloca).idx; - genstore(fn, arg.ty, mkref(RTMP, arg.id), mkref(RARG, i)); + arg.id = addinstr(fn, mkalloca(typesize(arg.ty), typealign(arg.ty))).idx; + genstore(fn, arg.ty, mkref(RTMP, arg.id), mkref(RPARAM, i)); } else { - arg.id = addinstr(fn, mkinstr(Ocopy, KPTR, mkref(RARG, i))).idx; + arg.id = addinstr(fn, mkinstr(Ocopy, KPTR, mkref(RPARAM, i))).idx; } } putdecl(pr, &arg); @@ -1770,6 +1744,12 @@ function(struct parser *pr, struct function *fn, const char **pnames, const stru warn(&pspans[i], "missing name of parameter #%d", i+1); } } + /* end prologue */ + EMITS { + struct block *blk; + putbranch(fn, blk = newblk(fn)); + useblk(fn, blk); + } block(pr, fn); envup(pr); if (fn->curblk) { @@ -36,7 +36,7 @@ use(struct block *blk, enum op op, int hint, union ref *ref) { struct instr *ins; if (ref->t == RMORE) { - if (op == Ocall || op == Obuiltin) { + if (op == Ocall || op == Ointrin) { struct call *call = &calltab.p[ref->idx]; for (int i = 0; i < call->narg; ++i) use(blk, 0, 0, &call->args[i]); @@ -57,7 +57,17 @@ use(struct block *blk, enum op op, int hint, union ref *ref) /* result of comparison instr is only used to conditionally branch, * doesn't usually need a reg (handled by isel) */ return; - if (hint != -1) { + /* TODO implement actual constraints and stuff */ + if (ins->op == Ocall) { + struct call *call = &calltab.p[ins->r.idx]; + hint = call->abiret[0].reg; + } else if (ins->op == Ocall2r) { + struct instr *ins2 = &instrtab[ins->l.idx]; + struct call *call = &calltab.p[ins2->r.idx]; + assert(ins->l.t == RTMP && ins2->op == Ocall); + hint = call->abiret[0].reg; + } + if (hint != -1 && !bstest(taken, hint)) { bsset(taken, hint); ins->reg = hint + 1; } else { @@ -92,10 +102,12 @@ regalloc(struct function *fn) use(blk, Ophi, ins->reg - 1, &ins->l); } for (int i = blk->ins.n - 1; i >= 0; --i) { + int hint0 = -1, hint1 = -1; ins = &instrtab[blk->ins.p[i]]; def(ins); - if (ins->l.t) use(blk, ins->op, -1, &ins->l); - if (ins->r.t) use(blk, ins->op, -1, &ins->r); + if (ins->op == Ocopy) hint0 = ins->reg - 1; + if (ins->l.t) use(blk, ins->op, hint0, &ins->l); + if (ins->r.t) use(blk, ins->op, hint1, &ins->r); } } while ((blk = blk->lprev) != last); } @@ -45,5 +45,16 @@ struct quad quad(long x, long y, long z, long w) { return q; } +void silly(struct pair *p, struct quad *q) +{ + *p = pair(1,2); + *q = quad(1,2,3,4); +} + +struct f2 { float f,g; }; +struct f2 f2test(struct f2 *r) { + return *r; +} + // @@ -65,12 +65,6 @@ tdequ(const struct typedata *a, const struct typedata *b) } } -static void * -alloccopy(struct arena **arena, const void *src, uint siz, uint align) -{ - return memcpy(alloc(arena, siz, align), src, siz); -} - static ushort interntd(const struct typedata *td) { |