aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2023-06-03 21:51:28 +0200
committerlemon <lsof@mailbox.org>2023-06-04 10:20:19 +0200
commit2ca24f83c35b253593b5aa8775d37923c8383149 (patch)
tree09fc86a228b81ac574233a922758953c4c460861
parent65ace14e184807df026e985e073b3b5c5aaf576c (diff)
abi lowering pass
-rw-r--r--Makefile6
-rw-r--r--abi0.c361
-rw-r--r--abistruct.c148
-rw-r--r--amd64/sysv.c74
-rw-r--r--common.h7
-rw-r--r--intrin.def (renamed from builtin.def)0
-rw-r--r--ir.c120
-rw-r--r--ir.h55
-rw-r--r--irdump.c30
-rw-r--r--op.def3
-rw-r--r--parse.c138
-rw-r--r--regalloc.c20
-rw-r--r--test.c11
-rw-r--r--type.c6
14 files changed, 667 insertions, 312 deletions
diff --git a/Makefile b/Makefile
index c107b23..64e2354 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/abi0.c b/abi0.c
new file mode 100644
index 0000000..2703713
--- /dev/null
+++ b/abi0.c
@@ -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: */
diff --git a/common.h b/common.h
index bd78a26..0bdd21a 100644
--- a/common.h
+++ b/common.h
@@ -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);
diff --git a/builtin.def b/intrin.def
index 2ccc3b5..2ccc3b5 100644
--- a/builtin.def
+++ b/intrin.def
diff --git a/ir.c b/ir.c
index e34ad0f..ba46a97 100644
--- a/ir.c
+++ b/ir.c
@@ -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);
}
diff --git a/ir.h b/ir.h
index c666ebc..a08bbce 100644
--- a/ir.h
+++ b/ir.h
@@ -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: */
diff --git a/irdump.c b/irdump.c
index 6e84838..4ff3f61 100644
--- a/irdump.c
+++ b/irdump.c
@@ -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 {
diff --git a/op.def b/op.def
index 9fc7d7a..55d2159 100644
--- a/op.def
+++ b/op.def
@@ -58,5 +58,6 @@ _(store2, 2)
_(store4, 2)
_(store8, 2)
_(call, 2)
-_(builtin, 2)
+_(call2r, 1)
+_(intrin, 2)
_(phi, 1)
diff --git a/parse.c b/parse.c
index f88fc2e..5d40258 100644
--- a/parse.c
+++ b/parse.c
@@ -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) {
diff --git a/regalloc.c b/regalloc.c
index 338d6a6..6200d0f 100644
--- a/regalloc.c
+++ b/regalloc.c
@@ -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);
}
diff --git a/test.c b/test.c
index 333114a..9638de2 100644
--- a/test.c
+++ b/test.c
@@ -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;
+}
+
//
diff --git a/type.c b/type.c
index 66e5e63..798994d 100644
--- a/type.c
+++ b/type.c
@@ -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)
{