aboutsummaryrefslogtreecommitdiffhomepage
path: root/ir/abi0.c
diff options
context:
space:
mode:
Diffstat (limited to 'ir/abi0.c')
-rw-r--r--ir/abi0.c462
1 files changed, 0 insertions, 462 deletions
diff --git a/ir/abi0.c b/ir/abi0.c
deleted file mode 100644
index a3687d9..0000000
--- a/ir/abi0.c
+++ /dev/null
@@ -1,462 +0,0 @@
-#include "ir.h"
-
-/** This pass adds in ABI arguments/returns register mappings
- ** and lowers aggregate params/args/returns into scalars
- **
- ** invariant: all `call` instructions when doing this pass shall be preceded by
- ** exactly narg `arg` instructions with no other instructions in between
- **/
-
-struct abiargsvec { vec_of(struct abiarg); };
-
-static int
-abiret(struct abiarg abiret[2], struct abiargsvec *abiargs, uchar *r2off, int *ni, union irtype retty)
-{
- short r[2];
- uchar cls[2];
- int retreg = 0;
- retreg = mctarg->abiret(r, cls, r2off, ni, retty);
- if (retty.isagg) {
- if (!retreg) {
- vpush(abiargs, ((struct abiarg) { cls2type(KPTR), .reg = r[1] }));
- if (r[0] == -1) {
- memset(abiret, 0, 2*sizeof *abiret);
- } else {
- abiret[0].ty = cls2type(KPTR);
- abiret[0].reg = r[0];
- }
- }
- } else if (retty.cls) {
- assert(retreg == 1);
- }
- for (int i = 0; i < retreg; ++i) {
- abiret[i].ty = cls2type(cls[i]);
- abiret[i].isstk = 0;
- abiret[i].reg = r[i];
- }
- return retreg;
-}
-
-static int
-abiarg(struct abiargsvec *abiargs, uchar *r2off, int *ni, int *nf, int *ns, union irtype ty)
-{
- short r[2];
- uchar cls[2];
- int ret = mctarg->abiarg(r, cls, r2off, ni, nf, ns, ty);
- if (!ret) { /* in stack */
- vpush(abiargs, ((struct abiarg) { ty, .isstk = 1, .stk = r[0] }));
- } else if (ret == 1 && ty.isagg && cls[0] == KPTR) { /* aggregate by pointer */
- vpush(abiargs, ((struct abiarg) { cls2type(cls[0]), .reg = r[0] }));
- } else { /* by regs */
- vpush(abiargs, ((struct abiarg) { cls2type(cls[0]), .reg = r[0] }));
- if (ret == 2)
- vpush(abiargs, ((struct abiarg) { cls2type(cls[1]), .reg = r[1] }));
- }
- return ret;
-}
-
-static struct instr
-copyparam(struct function *fn, int *curi, int param, struct abiarg abi)
-{
- struct instr par = mkinstr(Oparam, abi.ty.cls, mkref(RICON, param), mktyperef(abi.ty));
- if (!abi.isstk) { /* reg */
- assert(!abi.ty.isagg);
- return par;
- }
- par.r = mktyperef((union irtype){.cls = KPTR});
- if (!abi.ty.isagg) { /* scalar in stack */
- enum op ld;
- par.cls = KPTR;
- if (abi.ty.cls == KPTR) abi.ty.cls = siz2intcls[cls2siz[abi.ty.cls]];
- switch (abi.ty.cls) {
- default: assert(0);
- case KI32: ld = Oloads32; break;
- case KI64: ld = Oloadi64; break;
- case KF32: ld = Oloadf32; break;
- case KF64: ld = Oloadf64; break;
- }
- return mkinstr(ld, abi.ty.cls, insertinstr(fn->entry, (*curi)++, par));
- } else { /* aggregate in stack */
- par.cls = KPTR;
- return par;
- }
-}
-
-static void
-patchparam(struct function *fn, int *curi, int *param, int tydat, int nabi, struct abiarg abi[2], uchar r2off)
-{
- struct block *blk = fn->entry;
- assert(in_range(nabi,1,2));
-
- for (; *curi < blk->ins.n; ++*curi) {
- struct instr *ins = &instrtab[blk->ins.p[*curi]];
- if (ins->op != Oparam) continue;
- assert(ins->r.t == RTYPE
- && ins->r.i == (tydat < 0 ? abi[0].ty : (union irtype){.isagg=1, .dat=tydat}).bits);
- if (abi[0].ty.isagg || tydat < 0) {
- /* aggregate in stack or scalar, just copy */
- assert(nabi == 1);
- *ins = copyparam(fn, curi, *param, abi[0]);
- } else { /* aggregate in registers, materialize */
- union ref alloc, r[2];
- struct instr st;
- const struct typedata *td;
- uint nalloc;
- uint align;
-
- assert(tydat >= 0);
- td = &typedata[tydat];
- assert(td->siz <= 16 && td->align <= 16);
- align = td->siz <= 4 ? 4 : alignup(td->align, 8);
- nalloc = td->siz/align + (td->siz%align != 0);
- *ins = mkinstr(Oalloca1 + ilog2(align), KPTR, mkref(RICON, nalloc));
- alloc = mkref(RTMP, ins - instrtab);
- r[0] = insertinstr(blk, ++*curi, copyparam(fn, NULL, *param, abi[0]));
- if (nabi > 1)
- r[1] = insertinstr(blk, ++*curi, copyparam(fn, NULL, ++*param, abi[1]));
- /* transform
- * %x = copy %p
- * into
- * %x = alloca...
- * store* %x, %a
- * store* %x + N, %b
- */
- st = mkinstr(cls2store[abi[0].ty.cls], 0, alloc, r[0]);
- insertinstr(blk, ++*curi, st);
- if (nabi > 1) {
- struct instr tmp = mkinstr(Oadd, KPTR, alloc, mkref(RICON, r2off));
- st = mkinstr(cls2store[abi[1].ty.cls], 0, insertinstr(blk, ++*curi, tmp), r[1]);
- insertinstr(blk, ++*curi, st);
- }
- }
- ++*param;
- ++*curi;
- break;
- }
-}
-
-static void
-load2regs(union ref out[2], union irtype typ, union ref src, int nabi, struct abiarg abi[2], uchar r2off, struct block *blk, int *curi)
-{
- uint align = typedata[typ.dat].align;
- uint siz = typedata[typ.dat].siz;
- if (src.t == RTMP && oisalloca(instrtab[src.i].op)) {
- /* use actual alignment as opposed to min required type alignment */
- uint aalign = 1 << (instrtab[src.i].op - Oalloca1);
- assert(aalign >= align);
- align = aalign;
- }
- /* deconstruct into
- * %a = load* %x
- * (%b = load* %x + N)
- */
- /* XXX this generates pretty bad code for small-alignment structs even on platforms where unaligned loads are available.. */
- if (align >= 4) {
- for (int i = 0; i < nabi; ++i) {
- struct instr ins = {0};
- union ref temp;
- switch (ins.cls = abi[i].ty.cls) {
- default: assert(0);
- case KI32: ins.op = Oloadu32; break;
- case KI64: ins.op = Oloadi64; break;
- case KF32: ins.op = Oloadf32; break;
- case KF64: ins.op = Oloadf64; break;
- }
- if (i == 0)
- ins.l = src;
- else {
- struct instr adr = mkinstr(Oadd, KPTR, src, mkref(RICON, r2off));
- ins.l = insertinstr(blk, (*curi)++, adr);
- }
- temp = insertinstr(blk, (*curi)++, ins);
- //insertinstr(blk, (*curi)++, mkarginstr(abi[i].ty, temp));
- out[i] = temp;
- }
- } else {
- for (int i = 0; i < nabi; ++i) {
- struct instr ld = {0};
- union ref reg, temp;
- uint n = cls2siz[abi[i].ty.cls] / align;
- assert(n > 0);
- ld.op = Oloadu8 + ilog2(align)*2;
- ld.cls = abi[i].ty.cls;
- for (int o = 0; o < n && (i*cls2siz[ld.cls])+o*align < siz; ++o) {
- if (i+o == 0)
- ld.l = src;
- else {
- struct instr adr = mkinstr(Oadd, KPTR, src, mkref(RICON, (i == 0 ? 0 : r2off) + o*align));
- ld.l = insertinstr(blk, (*curi)++, adr);
- }
- temp = insertinstr(blk, (*curi)++, ld);
- if (o > 0) {
- union ref t = insertinstr(blk, (*curi)++, mkinstr(Oshl, ld.cls, temp, mkref(RICON, o*align*8)));
- reg = insertinstr(blk, (*curi)++, mkinstr(Oior, ld.cls, reg, t));
- } else {
- reg = temp;
- }
- }
- //insertinstr(blk, arginst++, mkarginstr(abi[i].ty, reg));
- out[i] = reg;
- }
- }
-}
-
-static int
-patcharg(struct block *blk, int *icall, struct call *call,
- int argidx, int nabi, struct abiarg abi[2], uchar r2off)
-{
- int arginst = *icall - (call->narg - argidx);
- struct instr *arg = &instrtab[blk->ins.p[arginst]];
- assert(arg->op == Oarg && arg->l.t == RTYPE);
- if (ref2type(arg->l).isagg) { /* aggregate argument */
- if (abi[0].ty.isagg) { /* aggregate in stack */
- /* XXX do this better.. */
- /* ptr %dst = arg <stk dst> */
- /* (blit %dst, %src) */
- union ref dst = mkref(RTMP, arg - instrtab);
- uint align = typedata[abi->ty.dat].align, siz = typedata[abi->ty.dat].siz;
- union ref src = arg->r;
- if (src.t == RTMP && oisalloca(instrtab[src.i].op)) {
- align = 1 << (instrtab[src.i].op - Oalloca1);
- }
- assert(align <= 8);
- arg->cls = KPTR;
- arg->r = mkref(RICON, abi->stk);
- for (uint off = 0; off < siz; off += align) {
- union ref sadr = off == 0 ? src : insertinstr(blk, ++arginst, mkinstr(Oadd, KPTR, src, mkref(RICON, off)));
- union ref tmp = insertinstr(blk, ++arginst, mkinstr(Oloads8+2*ilog2(align), align < 8 ? KI32 : KI64, sadr));
- union ref dadr = off == 0 ? dst : insertinstr(blk, ++arginst, mkinstr(Oadd, KPTR, dst, mkref(RICON, off)));
- insertinstr(blk, ++arginst, mkinstr(Ostorei8+ilog2(align), 0, dadr, tmp));
- }
- *icall = arginst + (call->narg - argidx);
- return 1;
- } else if (abi[0].ty.cls == KPTR) { /* aggregate by pointer */
- arg->cls = KPTR;
- return 1;
- } else { /* aggregate in registers */
- union ref r[2];
- delinstr(blk, arginst);
- load2regs(r, ref2type(arg->l), arg->r, nabi, abi, r2off, blk, &arginst);
- for (int i = 0; i < nabi; ++i)
- insertinstr(blk, arginst++, mkinstr(Oarg, 0, mktyperef(abi[i].ty), r[i]));
- *icall = arginst + (call->narg - argidx - 1);
- return nabi;
- }
- } else { /* normal scalar argument */
- return 1;
- }
-}
-void
-abi0_call(struct function *fn, struct instr *ins, struct block *blk, int *curi)
-{
- union ref retmem;
- struct abiarg abiargsbuf[32];
- struct abiargsvec abiargs = {VINIT(abiargsbuf, countof(abiargsbuf))};
- bool sretarghidden = 0;
- int ni, nf, ns, vararg, nret = 0;
- struct call *call = &calltab.p[ins->r.i];
-
- vararg = call->vararg;
- ni = nf = ns = 0;
- assert(!ins->cls == !call->ret.bits);
- nret = abiret(call->abiret, &abiargs, &call->r2off, &ni, call->ret);
- if (call->ret.isagg) { /* adjust struct return */
- union irtype retty = call->ret;
- struct typedata *td = &typedata[retty.dat];
- uint align = td->align, ralign;
- struct instr alloca;
- int ialloca;
- for (int i = 0; i < nret; ++i)
- align = align < (ralign = cls2siz[call->abiret[i].ty.cls]) ? ralign : align;
- alloca = mkalloca(td->siz, align);
-
- sretarghidden = ni == 0;
- /* swap alloca and call temps so users of original call point to alloca */
- retmem = insertinstr(blk, ialloca = (*curi)++ - call->narg, *ins);
- *ins = alloca;
- blk->ins.p[ialloca] = ins - instrtab;
- blk->ins.p[*curi] = retmem.i;
- ins = &instrtab[retmem.i];
- retmem.i = blk->ins.p[ialloca];
-
- if (!nret) /* hidden pointer argument */
- insertinstr(blk, (*curi)++ - call->narg,
- mkinstr(Oarg, 0, mktyperef((union irtype){.cls=KPTR}), retmem));
- }
-
- /* adjust args */
- for (int i = 0, i2 = ni + sretarghidden; i < call->narg; ++i) {
- int arginst = *curi - (call->narg - i);
- struct instr *arg = &instrtab[blk->ins.p[arginst]];
- union irtype pty = ref2type(arg->l);
- uchar r2off;
- int first = abiargs.n;
- int ret = abiarg(&abiargs, &r2off, &ni, &nf, &ns, pty);
- ret = patcharg(blk, curi, call, i, ret, &abiargs.p[first], r2off);
- if (call->vararg == i) vararg = i2;
- i2 += ret;
- }
- call->argstksiz = ns;
- /* adjust return */
- if (call->ret.isagg) {
- ins->cls = 0;
- if (!nret) { /* hidden pointer argument */
- ins->cls = 0;
- if (!call->abiret[0].isstk) {
- /* the result location pointer is also returned by the callee, e.g. in x86 */
- ins->cls = KPTR;
- ++nret;
- /* even if this is not used, the register copy
- * must be emitted for the register allocator to know */
- }
- } else { /* aggregate returned in regs */
- union ref r[2];
- struct instr ret2;
- assert(in_range(nret, 1, 2));
- ins->cls = call->abiret[0].ty.cls;
- r[0] = mkref(RTMP, ins - instrtab);
- if (nret == 2) {
- ret2 = mkinstr(Ocall2r, call->abiret[1].ty.cls, r[0]);
- r[1] = insertinstr(blk, ++*curi, ret2);
- }
- for (int i = 0; i < nret; ++i) {
- struct instr store = { cls2store[call->abiret[i].ty.cls] };
- if (i == 0) {
- store.l = retmem;
- } else {
- struct instr addr = mkinstr(Oadd, KPTR, retmem, mkref(RICON, call->r2off));
- store.l = insertinstr(blk, ++*curi, addr);
- }
- store.r = r[i];
- insertinstr(blk, ++*curi, store);
- }
- }
- }
-
- if (call->ret.isagg) call->ret = (union irtype){0};
- call->vararg = vararg;
- call->abiarg = alloccopy(fn->arena, abiargs.p, abiargs.n * sizeof(struct abiarg), 0);
- call->narg = abiargs.n;
- vfree(&abiargs);
-}
-
-void
-abi0(struct function *fn)
-{
- struct abiarg abiargsbuf[32];
- uint nparam = typedata[fn->fnty.dat].nmemb;
- const union type *paramty = typedata[fn->fnty.dat].param;
- struct abiargsvec abiargs = {VINIT(abiargsbuf, countof(abiargsbuf))};
- int rvovar = -1;
- int ni = 0, nf = 0, ns = 0, istart = 0;
- uchar r2off;
- struct block *blk;
- union ref sret = {0};
-
- FREQUIRE(FNUSE);
-
- if (fn->retty.t == TYVOID) {
- fn->nabiret = 0;
- } else {
- fn->nabiret = abiret(fn->abiret, &abiargs, &r2off, &ni, mkirtype(fn->retty));
- if (!fn->nabiret && isagg(fn->retty)) { /* ret agg by hidden pointer */
- struct instr param = copyparam(fn, NULL, 0, abiargs.p[0]);
- sret = insertinstr(fn->entry, 0, param);
- ++istart;
- /* increment real param ordinals */
- for (int i = 1; i < fn->entry->ins.n; ++i) {
- struct instr *ins = &instrtab[fn->entry->ins.p[i]];
- if (ins->op == Oparam) ++ins->l.i;
- }
- }
- }
-
- /* adjust params */
- for (int i = 0, param = abiargs.n; i < nparam; ++i) {
- union irtype pty = mkirtype(paramty[i]);
- int first = abiargs.n;
- uchar r2off;
- int ret = abiarg(&abiargs, &r2off, &ni, &nf, &ns, pty);
- patchparam(fn, &istart, &param, pty.isagg ? pty.dat : -1, ret+!ret, &abiargs.p[first], r2off);
- }
- 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.bits) continue;
- if (arg.t != RTMP || !oisalloca(instrtab[arg.i].op)) {
- rvovar = -1;
- break;
- }
- if (rvovar == -1) {
- rvovar = arg.i;
- } else if (arg.i != rvovar) {
- rvovar = -1;
- break;
- }
- } while ((blk = blk->lnext) != fn->entry);
- if (rvovar != -1)
- instrtab[rvovar] = mkinstr(Ocopy, KPTR, sret);
- }
-
- blk = fn->entry->lnext;
- int id = 1;
- do {
- /* adjust vaargs and calls */
- for (int iinstr = 0; iinstr < blk->ins.n; ++iinstr) {
- struct instr *ins = &instrtab[blk->ins.p[iinstr]];
- if (ins->op == Ovastart) mctarg->vastart(fn, blk, &iinstr);
- else if (ins->op == Ovaarg) mctarg->vaarg(fn, blk, &iinstr);
- else if (ins->op == Ocall) abi0_call(fn, ins, blk, &iinstr);
- }
-
- /* adjust returns */
- if (isagg(fn->retty) && blk->jmp.t == Jret && blk->jmp.arg[0].bits) {
- assert(!blk->jmp.arg[1].bits);
- if (fn->nabiret) { /* aggregate return in register(s) */
- union ref r[2];
- int curi = blk->ins.n;
- load2regs(r, mkirtype(fn->retty), blk->jmp.arg[0], fn->nabiret, fn->abiret, r2off, blk, &curi);
- for (int i = 0; i < fn->nabiret; ++i) {
- blk->jmp.arg[i] = r[i];
- adduse(blk, USERJUMP, r[i]);
- }
- } else {
- /* aggregate return (arg[0] is pointer to return value) */
- if (rvovar == -1) {
- /* blit %sret, %arg */
- union irtype typ = mkirtype(fn->retty);
- insertinstr(blk, blk->ins.n, mkarginstr(typ, sret));
- insertinstr(blk, blk->ins.n, mkarginstr(typ, blk->jmp.arg[0]));
- insertinstr(blk, blk->ins.n, mkintrin(INstructcopy, 0, 2));
- } 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);
- adduse(blk, USERJUMP, blk->jmp.arg[0]);
- }
- else memset(blk->jmp.arg, 0, sizeof blk->jmp.arg);
- }
- }
- blk->id = id++;
- } while ((blk = blk->lnext) != fn->entry);
-
-
- /* vaargs might break these */
- if (!(fn->prop & FNUSE)) filluses(fn);
- fn->prop &= ~(FNBLKID | FNRPO);
-
- if (ccopt.dbg.a) {
- bfmt(ccopt.dbgout, "<< After abi0 >>\n");
- irdump(fn);
- }
-}
-
-/* vim:set ts=3 sw=3 expandtab: */