diff options
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | abistruct.c | 148 | ||||
| -rw-r--r-- | amd64/all.h | 15 | ||||
| -rw-r--r-- | amd64/sysv.c | 103 | ||||
| -rw-r--r-- | builtin.def | 2 | ||||
| -rw-r--r-- | common.h | 14 | ||||
| -rw-r--r-- | ir.c | 83 | ||||
| -rw-r--r-- | ir.h | 33 | ||||
| -rw-r--r-- | irdump.c | 46 | ||||
| -rw-r--r-- | parse.c | 45 | ||||
| -rw-r--r-- | regalloc.c | 45 | ||||
| -rw-r--r-- | test.c | 68 |
12 files changed, 447 insertions, 159 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 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) @@ -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 } /******************/ @@ -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); @@ -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: */ @@ -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); @@ -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); } } @@ -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); } @@ -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; -} // |