aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--abistruct.c148
-rw-r--r--amd64/all.h15
-rw-r--r--amd64/sysv.c103
-rw-r--r--builtin.def2
-rw-r--r--common.h14
-rw-r--r--ir.c83
-rw-r--r--ir.h33
-rw-r--r--irdump.c46
-rw-r--r--parse.c45
-rw-r--r--regalloc.c45
-rw-r--r--test.c68
12 files changed, 447 insertions, 159 deletions
diff --git a/Makefile b/Makefile
index 14824e3..c107b23 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 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 abistruct.c regalloc.c amd64/sysv.c
CFLAGS=-Wall -std=c11 -pedantic
OBJ=$(patsubst %.c,obj/%.o,$(SRC))
OUT=cchomp
@@ -27,6 +27,8 @@ 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/regalloc.o: ir.h
obj/amd64/sysv.o: ir.h amd64/all.h
clean:
diff --git a/abistruct.c b/abistruct.c
new file mode 100644
index 0000000..36b686b
--- /dev/null
+++ b/abistruct.c
@@ -0,0 +1,148 @@
+#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/all.h b/amd64/all.h
index fdd759a..915d93a 100644
--- a/amd64/all.h
+++ b/amd64/all.h
@@ -1,6 +1,5 @@
#include "../ir.h"
-
#define LIST_REGS(_) \
_(RAX) _(RCX) _(RDX) _(RBX) _(RSP) _(RBP) _(RSI) _(RDI) \
_(R8) _(R9) _(R10) _(R11) _(R12) _(R13) _(R14) _(R15) \
@@ -13,18 +12,4 @@ enum {
#undef R
};
-const char amd64_rnames[][6] = {
-#define R(r) #r,
- LIST_REGS(R)
-#undef R
-};
-
-const struct mctarg t_amd64_sysv = {
- .gpr0 = RAX, .ngpr = R15 - RAX + 1,
- .fpr0 = XMM0, .nfpr = XMM15 - XMM0 + 1,
- .rcallee = {{1<<RBX | 1<<R12 | 1<<R13 | 1<<R14 | 1<<R15}},
- .rglob = {{1<<RSP | 1<<RBP}},
- .rnames = amd64_rnames,
-};
-
/* vim:set ts=3 sw=3 expandtab: */
diff --git a/amd64/sysv.c b/amd64/sysv.c
index e998036..55739e8 100644
--- a/amd64/sysv.c
+++ b/amd64/sysv.c
@@ -1,3 +1,106 @@
#include "all.h"
+static int
+classify(uchar cls[2], const struct typedata *td, uint off)
+{
+ uint siz = alignup(td->siz, 8);
+ if (siz > 16) /* MEMORY */
+ return 0;
+ 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 */
+ return cls[0] = cls[1] = 0;
+ if (isagg(fld->t)) {
+ if (!classify(cls, &typedata[fld->t.dat], 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 */
+ cls[(fld->off + off)/8] = KI8;
+ }
+ }
+ return !!cls[0] + !!cls[1];
+}
+
+static int
+argabi(short r[2], uchar cls[2], int *ni, int *nf, 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 */
+ r[0] = XMM0 + (*nf)++;
+ return 1;
+ }
+ if (*ni < NINT) {
+ cls[0] = KI8; /* INTEGER */
+ r[0] = intregs[(*ni)++];
+ return 1;
+ }
+ return 0; /* MEMORY */
+ }
+ cls[0] = cls[1] = 0;
+ ret = classify(cls, &typedata[typ.dat], 0);
+ if (!ret) return 0;
+ ni_save = *ni, nf_save = *nf;
+ assert(ret <= 2);
+ for (int i = 0; i < ret; ++i) {
+ assert(cls[i]);
+ if (cls[i] == KF8 && *nf < NFLT)
+ r[i] = XMM0 + (*nf)++;
+ else if (cls[i] == KI8 && *ni < NINT)
+ r[i] = intregs[(*ni)++];
+ else { /* MEMORY */
+ *ni = ni_save, *nf = nf_save;
+ return cls[0] = cls[1] = 0;
+ }
+ }
+ return ret;
+}
+
+static int
+retabi(short r[2], uchar cls[2], union irtype typ)
+{
+ int ret;
+
+ if (!typ.isagg) return kisflt(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 */
+ return 0;
+ }
+ assert(ret <= 2);
+ for (int i = 0; i < ret; ++i) {
+ assert(cls[i]);
+ if (cls[i] == KF8) /* SSE (XMM0, XMM1) */
+ r[i] = XMM0 + i;
+ else if (cls[i] == KI8) /* INTEGER (RAX, RDX) */
+ r[i] = i == 0 ? RAX : RDX;
+ else assert(0);
+ }
+ return ret;
+}
+
+const char amd64_rnames[][6] = {
+#define R(r) #r,
+ LIST_REGS(R)
+#undef R
+};
+
+const struct mctarg t_amd64_sysv = {
+ .gpr0 = RAX, .ngpr = R15 - RAX + 1,
+ .fpr0 = XMM0, .nfpr = XMM15 - XMM0 + 1,
+ .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,
+};
+
/* vim:set ts=3 sw=3 expandtab: */
diff --git a/builtin.def b/builtin.def
new file mode 100644
index 0000000..2ccc3b5
--- /dev/null
+++ b/builtin.def
@@ -0,0 +1,2 @@
+/* NAME NARG */
+_(structcopy, 2)
diff --git a/common.h b/common.h
index 0871257..bd78a26 100644
--- a/common.h
+++ b/common.h
@@ -58,7 +58,7 @@ ptrhash(const void *p) {
}
static inline uint
popcnt(uvlong x) {
-#ifdef __GNUC__
+#if defined __has_builtin && __has_builtin(__builtin_popcountll)
return __builtin_popcountll(x);
#else
uint n = 0;
@@ -68,7 +68,17 @@ popcnt(uvlong x) {
}
static inline bool
ispo2(uvlong x) {
- return (x & (x - 1)) == 0;
+ return x & ((x & (x - 1)) == 0);
+}
+static inline uint
+ilog2(uint x) { /* assumes x is a power of 2 */
+#if defined __has_builtin && __has_builtin(__builtin_ctz)
+ return __builtin_ctz(x);
+#else
+ uint n = 0;
+ while (x >>= 1) ++n;
+ return n;
+#endif
}
/******************/
diff --git a/ir.c b/ir.c
index 251990d..e34ad0f 100644
--- a/ir.c
+++ b/ir.c
@@ -6,10 +6,10 @@ uchar cls2siz[KF8+1];
const uchar siz2intcls[] = { [1] = KI4, [2] = KI4, [4] = KI4, [8] = KI8 };
static vec_of(struct irdat) dats;
-struct instr instr[1<<14];
+struct instr instrtab[1<<14];
static int ninstr;
-vec_of(struct call) calls;
-vec_of(struct phi) phis;
+struct calltab calltab;
+struct phitab phitab;
void
irinit(struct function *fn)
@@ -18,8 +18,8 @@ irinit(struct function *fn)
static struct phi phisbuf[64];
ninstr = 0;
- vinit(&calls, callsbuf, arraylength(callsbuf));
- vinit(&phis, phisbuf, arraylength(phisbuf));
+ vinit(&calltab, callsbuf, arraylength(callsbuf));
+ vinit(&phitab, phisbuf, arraylength(phisbuf));
if (!type2cls[TYINT]) {
for (int i = TYBOOL; i <= TYUVLONG; ++i) {
int siz = targ_primsizes[i];
@@ -174,21 +174,37 @@ mkcallarg(struct function *fn, uint narg, int vararg, union ref *args, union irt
memcpy(call.args, args, narg*sizeof *args);
memcpy(call.typs, typs, narg*sizeof *typs);
}
- vpush(&calls, call);
- return mkref(RMORE, calls.n-1);
+ vpush(&calltab, call);
+ return mkref(RMORE, calltab.n-1);
}
union ref
addinstr(struct function *fn, struct instr ins)
{
- assert(ninstr < arraylength(instr));
+ assert(ninstr < arraylength(instrtab));
assert(fn->curblk != NULL);
- instr[ninstr] = ins;
+ instrtab[ninstr] = ins;
vpush(&fn->curblk->ins, ninstr);
return mkref(RTMP, ninstr++);
}
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);
+ 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;
+ }
+ return mkref(RTMP, ninstr++);
+}
+
+union ref
addphi2(struct function *fn, enum irclass cls,
struct block *b1, union ref r1, struct block *b2, union ref r2)
{
@@ -200,12 +216,12 @@ addphi2(struct function *fn, enum irclass cls,
phi.ref[0] = r1;
phi.blk[1] = b2;
phi.ref[1] = r2;
- vpush(&phis, phi);
- ins.l = mkref(RMORE, phis.n-1);
- assert(ninstr < arraylength(instr));
+ vpush(&phitab, phi);
+ ins.l = mkref(RMORE, phitab.n-1);
+ assert(ninstr < arraylength(instrtab));
assert(fn->curblk != NULL);
assert(fn->curblk->ins.n == 0);
- instr[ninstr] = ins;
+ instrtab[ninstr] = ins;
vpush(&fn->curblk->phi, ninstr);
return mkref(RTMP, ninstr++);
}
@@ -220,12 +236,12 @@ addphi(struct function *fn, enum irclass cls, struct block **blk, union ref *ref
phi.ref = (union ref *)((char *)phi.blk + n*sizeof(struct block *));
memcpy(phi.blk, blk, n * sizeof(struct block *));
memcpy(phi.ref, ref, n * sizeof(union ref));
- vpush(&phis, phi);
- ins.l = mkref(RMORE, phis.n-1);
- assert(ninstr < arraylength(instr));
+ vpush(&phitab, phi);
+ ins.l = mkref(RMORE, phitab.n-1);
+ assert(ninstr < arraylength(instrtab));
assert(fn->curblk != NULL);
assert(fn->curblk->ins.n == 0);
- instr[ninstr] = ins;
+ instrtab[ninstr] = ins;
vpush(&fn->curblk->phi, ninstr);
return mkref(RTMP, ninstr++);
}
@@ -254,16 +270,36 @@ useblk(struct function *fn, struct block *blk)
fn->curblk = blk;
}
+#define putjump(fn, j, arg0, arg1, T, F) \
+ fn->curblk->jmp.t = j; \
+ fn->curblk->jmp.arg[0] = arg0; \
+ fn->curblk->jmp.arg[1] = arg1; \
+ fn->curblk->s1 = T; \
+ fn->curblk->s2 = F; \
+ fn->curblk = NULL;
+
void
-putjump(struct function *fn, enum jumpkind j, union ref arg, struct block *t, struct block *f)
+putbranch(struct function *fn, struct block *blk)
{
- fn->curblk->jmp.t = j;
- fn->curblk->jmp.arg = arg;
- fn->curblk->s1 = t;
- fn->curblk->s2 = f;
- fn->curblk = NULL;
+ assert(blk);
+ putjump(fn, Jb, NOREF, NOREF, blk, NULL);
}
+void
+putcondbranch(struct function *fn, union ref arg, struct block *t, struct block *f)
+{
+ assert(t && f);
+ putjump(fn, Jb, arg, NOREF, t, f);
+}
+
+void
+putreturn(struct function *fn, union ref r0, union ref r1)
+{
+ putjump(fn, Jret, r0, r1, NULL, NULL);
+}
+
+#undef putjump
+
static void
freefn(struct function *fn)
{
@@ -278,6 +314,7 @@ freefn(struct function *fn)
void
irfini(struct function *fn)
{
+ abistruct(fn);
regalloc(fn);
efmt("after regalloc:\n");
irdump(fn, fn->name);
diff --git a/ir.h b/ir.h
index ed4854a..c666ebc 100644
--- a/ir.h
+++ b/ir.h
@@ -78,6 +78,10 @@ enum op {
#undef _
};
+#define oiscmp(o) in_range(o, Oequ, Oulte)
+#define oisalloca(o) in_range(o, Oalloca1, Oalloca16)
+#define oisstore(o) in_range(o, Ostore1, Ostore8)
+
enum builtin {
BTxxx,
#define _(b,...) BT##b,
@@ -92,7 +96,7 @@ struct instr {
};
enum jumpkind {
- JXXX, Jb, Jbcnd, Jret, Jrets,
+ JXXX, Jb, Jret,
};
struct block {
@@ -100,17 +104,23 @@ struct block {
struct block *s1, *s2;
vec_of(ushort) phi;
vec_of(ushort) ins;
- struct { uchar t; union ref arg; } jmp;
+ struct { uchar t; union ref arg[2]; } jmp;
struct block *lprev, *lnext;
};
+struct abiarg {
+ union irtype ty;
+ short reg; /* -1 -> stack */
+};
+
struct function {
struct arena *arena;
const char *name;
struct block *entry, *curblk;
- union type fnty, retty, *paramty;
+ union type fnty, retty;
+ struct abiarg *abiarg, abiret[2];
uint nblk;
- uint nparam;
+ ushort nabiarg, nabiret;
bool globl;
};
@@ -124,15 +134,21 @@ 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);
};
extern uchar type2cls[];
extern uchar cls2siz[];
extern const uchar siz2intcls[];
+extern struct instr instrtab[];
+extern struct calltab {vec_of(struct call);} calltab;
+extern struct phitab {vec_of(struct phi);} phitab;
#define NOREF ((union ref) {0})
#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);
@@ -142,17 +158,20 @@ union ref mksymref(struct function *, const char *);
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 addinstr(struct function *, struct instr);
+union ref insertinstr(struct block *, int idx, struct instr);
+void delinstr(struct block *, int idx);
union ref addphi2(struct function *, enum irclass cls,
struct block *b1, union ref r1, struct block *b2, union ref r2);
union ref addphi(struct function *, enum irclass cls, struct block **blk, union ref *ref, uint n);
struct block *newblk(struct function *);
void useblk(struct function *, struct block *);
-void putjump(struct function *, enum jumpkind, union ref arg, struct block *t, struct block *f);
-void insertinstr(struct block *, int idx, struct instr);
-void delinstr(struct block *, int idx);
+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 irdump(struct function *, const char *fname);
+void abistruct(struct function *);
void regalloc(struct function *);
/* vim:set ts=3 sw=3 expandtab: */
diff --git a/irdump.c b/irdump.c
index 38b074e..6e84838 100644
--- a/irdump.c
+++ b/irdump.c
@@ -54,8 +54,7 @@ dumpref(enum op o, union ref ref)
break;
case RMORE:
if (o == Ocall || o == Obuiltin) {
- extern vec_of(struct call) calls;
- struct call *call = &calls.p[ref.idx];
+ struct call *call = &calltab.p[ref.idx];
for (int i = 0; i < call->narg; ++i) {
if (i > 0) efmt(", ");
if (call->vararg == i)
@@ -65,8 +64,7 @@ dumpref(enum op o, union ref ref)
dumpref(0, call->args[i]);
}
} else if (o == Ophi) {
- extern vec_of(struct phi) phis;
- struct phi *phi = &phis.p[ref.idx];
+ struct phi *phi = &phitab.p[ref.idx];
for (int i = 0; i < phi->n; ++i) {
if (i > 0) efmt(", ");
efmt("[.L%d ", phi->blk[i]->id);
@@ -79,7 +77,6 @@ dumpref(enum op o, union ref ref)
}
}
-extern struct instr instr[];
static const char *opname[] = {
"?\??",
#define _(o,...) #o,
@@ -99,7 +96,7 @@ dumpinst(const struct instr *ins)
int i;
efmt(" ");
if (ins->cls) {
- efmt("%s %%%d", clsname[ins->cls], ins - instr);
+ efmt("%s %%%d", clsname[ins->cls], ins - instrtab);
if (ins->reg) efmt("(%ls)", mctarg->rnames[ins->reg - 1]);
efmt(" = ");
}
@@ -114,24 +111,26 @@ dumpinst(const struct instr *ins)
static void
dumpblk(struct function *fn, struct block *blk)
{
- static const char *jnames[] = { 0, "b", "b", "ret", "ret" };
- static const uchar jnarg[] = { 0, 0, 1, 0, 1 };
+ static const char *jnames[] = { 0, "b", "ret" };
+ int i;
efmt(" .L%d:\n", blk->id);
- for (int i = 0; i < blk->phi.n; ++i) {
- dumpinst(&instr[blk->phi.p[i]]);
+ for (i = 0; i < blk->phi.n; ++i) {
+ dumpinst(&instrtab[blk->phi.p[i]]);
}
- for (int i = 0; i < blk->ins.n; ++i) {
- dumpinst(&instr[blk->ins.p[i]]);
+ for (i = 0; i < blk->ins.n; ++i) {
+ dumpinst(&instrtab[blk->ins.p[i]]);
}
efmt(" %s ", jnames[blk->jmp.t]);
- if (jnarg[blk->jmp.t]) {
- if (blk->jmp.t == Jrets) {
- prityp(mkirtype(fn->retty));
+ for (i = 0; i < 2; ++i) {
+ if (!blk->jmp.arg[i].t) break;
+ if (i > 0) efmt(", ");
+ if (blk->jmp.t == Jret && fn->nabiret > i) {
+ prityp(fn->abiret[i].ty);
efmt(" ");
}
- dumpref(0, blk->jmp.arg);
- if (blk->s1) efmt(", ");
+ dumpref(0, blk->jmp.arg[i]);
}
+ if (i && blk->s1) efmt(", ");
if (blk->s1 && blk->s2) efmt(".L%d, .L%d", blk->s1->id, blk->s2->id);
else if (blk->s1) efmt(".L%d", blk->s1->id);
efmt("\n");
@@ -143,6 +142,19 @@ irdump(struct function *fn, const char *fname)
struct block *blk;
efmt("function %s : %ty\n", fname, fn->fnty);
+ if (fn->abiarg) {
+ efmt("abi args: (");
+ 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]);
+ } else {
+ prityp(fn->abiarg[i].ty);
+ efmt(" <stk>");
+ }
+ }
+ efmt(")\n");
+ }
blk = fn->entry;
do {
dumpblk(fn, blk);
diff --git a/parse.c b/parse.c
index 006117e..f88fc2e 100644
--- a/parse.c
+++ b/parse.c
@@ -1100,12 +1100,10 @@ cvt(struct function *fn, enum typetag to, enum typetag from, union ref ref)
} else {
if (to == TYBOOL) {
if (from == TYBOOL) return ref;
- if (ref.t == RTMP) {
- extern struct instr instr[];
+ if (ref.t == RTMP)
/* these instrs already have output range of [0,1] */
- if (in_range(instr[ref.idx].op, Oequ, Oulte) || instr[ref.idx].op == Onot)
+ if (instrtab[ref.idx].op == Onot || oiscmp(instrtab[ref.idx].op))
return ref;
- }
ins.op = Oneq, ins.r = mkzerocon();
}
else if (kfrom == KI4 && issignedt(from)) ins.op = Oexts4;
@@ -1137,17 +1135,6 @@ narrow(struct function *fn, enum irclass to, enum typetag tt, union ref ref)
return addinstr(fn, ins);
}
-static inline uint
-ilog2(uint x) { /* assumes x is a power of 2 */
-#ifdef __GNUC__
- return __builtin_ctz(x);
-#else
- uint n = 0;
- while (x >>= 1) ++n;
- return n;
-#endif
-}
-
union ref
genptroff(struct function *fn, enum op op, uint siz, union ref ptr,
enum typetag tt, union ref idx)
@@ -1160,7 +1147,7 @@ genptroff(struct function *fn, enum op op, uint siz, union ref ptr,
if (siz == 1) off = idx;
else if (idx.t == RICON)
off = mkintcon(fn, cls, idx.i * siz);
- else if ((siz & (siz-1)) == 0) /* is power of 2 */
+ else if (ispo2(siz))
off = addinstr(fn,
mkinstr(Oshl, cls, .l = idx, .r = mkintcon(fn, cls, ilog2(siz))));
else
@@ -1227,7 +1214,7 @@ Loop:
ex = &ex->sub[0];
goto Loop;
} else {
- putjump(fn, Jbcnd, exprvalue(fn, ex), tr, fl);
+ putcondbranch(fn, exprvalue(fn, ex), tr, fl);
}
}
@@ -1274,10 +1261,10 @@ condexprrec(struct function *fn, const struct expr *ex, struct condphis *phis,
else
vpush(&phis->ref, r);
if (zero) {
- putjump(fn, Jbcnd, r, end, zero);
+ putcondbranch(fn, r, end, zero);
} else {
assert(boolcon < 0);
- putjump(fn, Jb, NOREF, end, NULL);
+ putbranch(fn, end);
}
}
}
@@ -1538,10 +1525,10 @@ exprvalue(struct function *fn, const struct expr *ex)
useblk(fn, tr);
(void)exprvalue(fn, &ex->sub[1]);
end = newblk(fn);
- putjump(fn, Jb, NOREF, end, NULL);
+ putbranch(fn, end);
useblk(fn, fl);
(void)exprvalue(fn, &ex->sub[2]);
- putjump(fn, Jb, NOREF, end, NULL);
+ putbranch(fn, end);
useblk(fn, end);
return NOREF;
}
@@ -1605,16 +1592,16 @@ stmt(struct parser *pr, struct function *fn)
terminates = stmt(pr, fn);
if (!match(pr, NULL, TKWelse)) {
end = fl;
- EMITS if (!terminates) putjump(fn, Jb, NOREF, end, NULL);
+ EMITS if (!terminates) putbranch(fn, end);
terminates = 0;
} else {
EMITS {
- if (!terminates) putjump(fn, Jb, NOREF, end = newblk(fn), NULL);
+ if (!terminates) putbranch(fn, end = newblk(fn));
useblk(fn, fl);
}
terminates &= stmt(pr, fn);
EMITS {
- if (fn->curblk) putjump(fn, Jb, NOREF, end, NULL);
+ if (fn->curblk) putbranch(fn, end);
}
}
EMITS if (!terminates) useblk(fn, end);
@@ -1629,14 +1616,14 @@ stmt(struct parser *pr, struct function *fn)
tr = begin = end = NULL;
EMITS {
begin = newblk(fn);
- putjump(fn, Jb, NOREF, begin, NULL);
+ putbranch(fn, begin);
useblk(fn, begin);
condjump(fn, &ex, tr = newblk(fn), end = newblk(fn));
useblk(fn, tr);
}
terminates = stmt(pr, fn);
EMITS {
- if (!terminates) putjump(fn, Jb, NOREF, begin, NULL);
+ if (!terminates) putbranch(fn, begin);
useblk(fn, end);
}
break;
@@ -1654,10 +1641,10 @@ stmt(struct parser *pr, struct function *fn)
r = cvt(fn, fn->retty.t, ex.ty.t, exprvalue(fn, &ex));
else
r = structreturn(fn, &ex);
- putjump(fn, Jrets, r, NULL, NULL);
+ putreturn(fn, r, NOREF);
}
} else {
- EMITS putjump(fn, Jret, NOREF, NULL, NULL);
+ EMITS putreturn(fn, NOREF, NOREF);
}
stmtterm(pr);
break;
@@ -1789,7 +1776,7 @@ function(struct parser *pr, struct function *fn, const char **pnames, const stru
if (fn->retty.t != TYVOID && !nerror) {
warn(&pr->fnblkspan, "non-void function may not return a value");
}
- putjump(fn, Jret, NOREF, NULL, NULL);
+ putreturn(fn, NOREF, NOREF);
}
}
diff --git a/regalloc.c b/regalloc.c
index 3b7ab74..338d6a6 100644
--- a/regalloc.c
+++ b/regalloc.c
@@ -1,9 +1,5 @@
#include "ir.h"
-extern struct instr instr[];
-extern vec_of(struct call) calls;
-extern vec_of(struct phi) phis;
-
static struct bitset taken[1];
static void
@@ -18,6 +14,7 @@ nextreg(enum irclass cls)
{
int r0, rend, i;
+ assert(cls);
if (kisint(cls)) {
r0 = mctarg->gpr0;
rend = mctarg->gpr0 + mctarg->ngpr;
@@ -39,27 +36,33 @@ use(struct block *blk, enum op op, int hint, union ref *ref)
{
struct instr *ins;
if (ref->t == RMORE) {
- if (op == Ocall) {
- struct call *call = &calls.p[ref->idx];
+ if (op == Ocall || op == Obuiltin) {
+ struct call *call = &calltab.p[ref->idx];
for (int i = 0; i < call->narg; ++i)
use(blk, 0, 0, &call->args[i]);
} else if (op == Ophi) {
- struct phi *phi = &phis.p[ref->idx];
+ struct phi *phi = &phitab.p[ref->idx];
for (int i = 0; i < phi->n; ++i)
use(blk, 0, hint, &phi->ref[i]);
} else assert("ext?");
return;
} else if (ref->t != RTMP) return;
- ins = &instr[ref->idx];
- if (in_range(ins->op, Oalloca1, Oalloca16)) return;
+ ins = &instrtab[ref->idx];
+ if (oisalloca(ins->op)) return;
+ if (!ins->cls) return;
if (!ins->reg) {
if (op == -1) /* cond branch */
- if (in_range(ins->op, Oequ, Oulte) && ref->idx == blk->ins.p[blk->ins.n-1])
+ if (oiscmp(ins->op) && ref->idx == blk->ins.p[blk->ins.n-1])
/* result of comparison instr is only used to conditionally branch,
* doesn't usually need a reg (handled by isel) */
return;
- ins->reg = (hint ? hint : nextreg(ins->cls)) + 1;
+ if (hint != -1) {
+ bsset(taken, hint);
+ ins->reg = hint + 1;
+ } else {
+ ins->reg = nextreg(ins->cls) + 1;
+ }
}
}
@@ -67,26 +70,32 @@ void
regalloc(struct function *fn)
{
struct instr *ins;
- struct block *last = fn->entry->lprev, *blk = last;
+ struct block *last = fn->entry->lprev, *blk;
/* a dumb linear register allocator that visits instructions physically backwards
* starting at the end of the function, when encountering a use of a new
* temporary, it allocates a register for it. when encountering the definition
* of a temporary, it frees up its register
*/
+ blk = last;
do {
- if (blk->jmp.arg.t) use(blk, blk->jmp.t == Jbcnd ? -1 : 0, 0, &blk->jmp.arg);
+ for (int i = 0; i < 2; ++i) {
+ if (!blk->jmp.arg[i].t) break;
+ use(blk, (blk->jmp.t == Jb) - 1,
+ blk->jmp.t == Jret ? fn->abiret[i].reg : -1,
+ &blk->jmp.arg[i]);
+ }
for (int i = blk->phi.n - 1; i >= 0; --i) {
- ins = &instr[blk->phi.p[i]];
+ ins = &instrtab[blk->phi.p[i]];
def(ins);
assert(ins->op == Ophi);
- use(blk, Ophi, ins->reg, &ins->l);
+ use(blk, Ophi, ins->reg - 1, &ins->l);
}
for (int i = blk->ins.n - 1; i >= 0; --i) {
- ins = &instr[blk->ins.p[i]];
+ ins = &instrtab[blk->ins.p[i]];
def(ins);
- if (ins->l.t) use(blk, ins->op, 0, &ins->l);
- if (ins->r.t) use(blk, ins->op, 0, &ins->r);
+ if (ins->l.t) use(blk, ins->op, -1, &ins->l);
+ if (ins->r.t) use(blk, ins->op, -1, &ins->r);
}
} while ((blk = blk->lprev) != last);
}
diff --git a/test.c b/test.c
index 83a9d8b..333114a 100644
--- a/test.c
+++ b/test.c
@@ -15,61 +15,35 @@ struct foo {
int x, y, z;
};
+struct v2f { double x, y; };
+struct big { int x[10]; };
-int test0(struct foo *foo) { return foo->x ? foo->y : foo->z; }
-int test1(int x, int y, int z) { return x && y || z; }
-int test2(int x, int y, int z) { return x || y && z; }
-
-extern void f();
-int test3(int x, int y) {
- if (x < 0 && y < 0 && 1) return x - y;
- if ((x == 0 || x > 0) && y > 0) return y;
- if (f(), x == y ? x && 1 : 0 || y) return x;
- return x + y;
-}
-
-int test4(int c) {
- return c == 'a' || c == 'x' ? 1
- : (f(), c == 'b' || c == 'y') ? 2
- : c == 'c' || c == 'z' ? 3
- : 0;
-}
-
-int test5(int *p)
-{
- return p ? *p : 0;
-}
-
-int test6(int x)
-{
- return !!!!x;
+struct v2f add(struct v2f a, struct v2f b, struct big B, struct { char c; } small, void *prt) {
+ struct v2f r;
+ r.y = a.y + b.y;
+ return r;
}
-float sqr(float x) { return x * x; }
+struct pair {
+ long x, y;
+};
-int mula(int x, int y, int z) {
- if (x < 0)
- return -x * y + z;
- return x * y + z;
+struct pair pair(long x, long y) {
+ struct pair p;
+ p.x = x;
+ p.y = y;
+ return p;
}
-void *copy(char *d, char *s, int n) {
- while (n--)
- *d++ = *s++;
- return d;
-}
+struct quad {
+ long x, y, z, w;
+};
-int hmm(float x, int a, char *p, char *q) {
- return x > 1 ? a || p : p < q && a > 0;
+struct quad quad(long x, long y, long z, long w) {
+ struct quad q;
+ q.x = x, q.y = y, q.z = z, q.w = w;
+ return q;
}
-struct v2f { float x, y; };
-
-struct v2f add(struct v2f a, struct v2f b) {
- struct v2f r;
- r.x = a.x + b.x;
- r.y = a.y + b.y;
- return r;
-}
//