diff options
| -rw-r--r-- | Makefile | 3 | ||||
| -rw-r--r-- | amd64/all.h | 2 | ||||
| -rw-r--r-- | ir.c | 13 | ||||
| -rw-r--r-- | ir.h | 21 | ||||
| -rw-r--r-- | irdump.c | 40 | ||||
| -rw-r--r-- | op.def | 1 | ||||
| -rw-r--r-- | parse.c | 93 | ||||
| -rw-r--r-- | regalloc.c | 22 | ||||
| -rw-r--r-- | test.c | 9 |
9 files changed, 150 insertions, 54 deletions
@@ -19,10 +19,11 @@ obj/%.o: %.c common.h @mkdir -p `dirname $@` $(CC) $(CFLAGS) -c -o $@ $< +ir.h: op.def builtin.def obj/main.o: parse.h obj/parse.o: parse.h ir.h obj/ir.o: ir.h -obj/irdump.o: ir.h op.def +obj/irdump.o: ir.h obj/lex.o: parse.h obj/eval.o: parse.h obj/io.o: parse.h keywords.def diff --git a/amd64/all.h b/amd64/all.h index ae21d3d..fdd759a 100644 --- a/amd64/all.h +++ b/amd64/all.h @@ -8,14 +8,12 @@ _(XMM8) _(XMM9) _(XMM10) _(XMM11) _(XMM12) _(XMM13) _(XMM14) _(XMM15) enum { - Rxxx, #define R(r) r, LIST_REGS(R) #undef R }; const char amd64_rnames[][6] = { - "?", #define R(r) #r, LIST_REGS(R) #undef R @@ -163,12 +163,11 @@ mksymref(struct function *fn, const char *s) } union ref -mkcall(struct function *fn, union type fnty, uint narg, union ref *args, union irtype *typs) +mkcallarg(struct function *fn, uint narg, int vararg, union ref *args, union irtype *typs) { - const struct typedata *td = &typedata[fnty.dat]; - struct call call = { narg, td->variadic ? td->nmemb : -1 }; + struct call call = { narg, vararg }; - if (!td->kandr) assert(td->variadic ? narg >= td->nmemb : narg == td->nmemb); + assert((long) vararg <= narg); if (narg) { call.args = alloc(&fn->arena, narg*sizeof *args + narg*sizeof(union irtype), 0); call.typs = (union irtype *)((char *)call.args + narg*sizeof *args); @@ -176,7 +175,7 @@ mkcall(struct function *fn, union type fnty, uint narg, union ref *args, union i memcpy(call.typs, typs, narg*sizeof *typs); } vpush(&calls, call); - return mkref(REXT, calls.n-1); + return mkref(RMORE, calls.n-1); } union ref @@ -202,7 +201,7 @@ addphi2(struct function *fn, enum irclass cls, phi.blk[1] = b2; phi.ref[1] = r2; vpush(&phis, phi); - ins.l = mkref(REXT, phis.n-1); + ins.l = mkref(RMORE, phis.n-1); assert(ninstr < arraylength(instr)); assert(fn->curblk != NULL); assert(fn->curblk->ins.n == 0); @@ -222,7 +221,7 @@ addphi(struct function *fn, enum irclass cls, struct block **blk, union ref *ref memcpy(phi.blk, blk, n * sizeof(struct block *)); memcpy(phi.ref, ref, n * sizeof(union ref)); vpush(&phis, phi); - ins.l = mkref(REXT, phis.n-1); + ins.l = mkref(RMORE, phis.n-1); assert(ninstr < arraylength(instr)); assert(fn->curblk != NULL); assert(fn->curblk->ins.n == 0); @@ -10,8 +10,8 @@ enum irclass { #define kisflt(k) in_range((k), KF4, KF8) union irtype { - struct { ushort isagg : 1, cls : 15; }; - struct { ushort _ : 1, dat : 15; }; + struct { ushort _ : 1, cls : 15; }; + struct { ushort isagg : 1, dat : 15; }; ushort bits; }; @@ -61,7 +61,7 @@ enum refkind { RARG, /* function argument */ RICON, /* small integer constants */ RXCON, /* other constants (incl. external symbols) */ - REXT, /* reference to extra data for Ocall and Ophi */ + RMORE, /* reference to extra data for Ocall and Ophi */ RREG, /* machine register */ }; @@ -78,9 +78,16 @@ enum op { #undef _ }; +enum builtin { + BTxxx, +#define _(b,...) BT##b, +#include "builtin.def" +#undef _ +}; + struct instr { uchar op, cls; - uchar reg, hint; + ushort reg; /* 0 -> unallocated; else reg + 1 */ union ref l, r; }; @@ -107,7 +114,7 @@ struct function { bool globl; }; -enum { MAXREGS = 32 }; +enum { MAXREGS = 64 }; struct mctarg { short gpr0, /* first gpr */ @@ -125,7 +132,7 @@ extern const uchar siz2intcls[]; #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,.hint=0, __VA_ARGS__ }) +#define mkinstr(O, C, ...) ((struct instr) { .op = (O), .cls = (C), .reg=0, __VA_ARGS__ }) void irinit(struct function *); void irfini(struct function *); union irtype mkirtype(union type); @@ -133,7 +140,7 @@ union ref mkintcon(struct function *, enum irclass, vlong); union ref mkfltcon(struct function *, enum irclass, double); union ref mksymref(struct function *, const char *); void conputdat(struct irdat *, uint off, enum typetag t, const void *dat); -union ref mkcall(struct function *, union type fnty, uint narg, union ref *, union irtype *); +union ref mkcallarg(struct function *, uint narg, int vararg, union ref *, union irtype *); union ref addinstr(struct function *, struct instr); union ref addphi2(struct function *, enum irclass cls, struct block *b1, union ref r1, struct block *b2, union ref r2); @@ -22,6 +22,13 @@ prityp(union irtype typ) } } +static const char *builtinname[] = { + "?\??", +#define _(b,...) #b, +#include "builtin.def" +#undef _ +}; + static void dumpref(enum op o, union ref ref) { @@ -29,7 +36,10 @@ dumpref(enum op o, union ref ref) switch (ref.t) { case RTMP: efmt("%%%d", ref.idx); break; case RARG: efmt("%%arg%d", ref.idx); break; - case RICON: efmt("%d", ref.i); break; + case RICON: + if (o == Obuiltin) efmt("\"%s\"", builtinname[ref.i]); + else efmt("%d", ref.i); + break; case RXCON: con = &conht[ref.idx]; if (con->issym) efmt("$%s", con->sym); @@ -42,15 +52,14 @@ dumpref(enum op o, union ref ref) default: assert(0); } break; - case REXT: - if (o == Ocall) { + case RMORE: + if (o == Ocall || o == Obuiltin) { extern vec_of(struct call) calls; struct call *call = &calls.p[ref.idx]; for (int i = 0; i < call->narg; ++i) { - if (call->vararg == i) { - if (i > 0) efmt(", "); + if (i > 0) efmt(", "); + if (call->vararg == i) efmt("..., "); - } prityp(call->typs[i]); efmt(" "); dumpref(0, call->args[i]); @@ -64,7 +73,7 @@ dumpref(enum op o, union ref ref) dumpref(0, phi->ref[i]); efmt("]"); } - } + } else assert(0); break; default: assert(!"ref"); } @@ -72,7 +81,7 @@ dumpref(enum op o, union ref ref) extern struct instr instr[]; static const char *opname[] = { - "???", + "?\??", #define _(o,...) #o, #include "op.def" #undef _ @@ -91,7 +100,7 @@ dumpinst(const struct instr *ins) efmt(" "); if (ins->cls) { efmt("%s %%%d", clsname[ins->cls], ins - instr); - if (ins->reg) efmt("(%ls)", mctarg->rnames[ins->reg]); + if (ins->reg) efmt("(%ls)", mctarg->rnames[ins->reg - 1]); efmt(" = "); } efmt("%s ", opname[ins->op]); @@ -103,9 +112,9 @@ dumpinst(const struct instr *ins) } static void -dumpblk(struct block *blk) +dumpblk(struct function *fn, struct block *blk) { - static const char *jnames[] = { 0, "b", "b", "ret", "rets" }; + static const char *jnames[] = { 0, "b", "b", "ret", "ret" }; static const uchar jnarg[] = { 0, 0, 1, 0, 1 }; efmt(" .L%d:\n", blk->id); for (int i = 0; i < blk->phi.n; ++i) { @@ -116,6 +125,10 @@ dumpblk(struct block *blk) } efmt(" %s ", jnames[blk->jmp.t]); if (jnarg[blk->jmp.t]) { + if (blk->jmp.t == Jrets) { + prityp(mkirtype(fn->retty)); + efmt(" "); + } dumpref(0, blk->jmp.arg); if (blk->s1) efmt(", "); } @@ -132,9 +145,8 @@ irdump(struct function *fn, const char *fname) efmt("function %s : %ty\n", fname, fn->fnty); blk = fn->entry; do { - dumpblk(blk); - blk = blk->lnext; - } while (blk != fn->entry); + dumpblk(fn, blk); + } while ((blk = blk->lnext) != fn->entry); } /* vim:set ts=3 sw=3 expandtab: */ @@ -58,4 +58,5 @@ _(store2, 2) _(store4, 2) _(store8, 2) _(call, 2) +_(builtin, 2) _(phi, 1) @@ -873,6 +873,8 @@ Postfix: case '&': if (!islvalue(&ex)) error(&span, "operand to unary & is not an lvalue"); + if (ex.t == EGETF && ex.fld.bitsiz) + error(&span, "cannot take address of bitfield"); ex = mkexpr(EADDROF, span, mkptrtype(ex.ty, ex.qual), .sub = exprdup(pr, &ex)); break; case TKWsizeof: @@ -947,8 +949,47 @@ commaexpr(struct parser *pr) /* -> IR */ /*********/ +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"); +} + +static union ref +structreturn(struct function *fn, const struct expr *src) +{ + if (exhasaddr(src)) { + return expraddr(fn, src); + } + assert(!"nyi"); +} + static union ref expraddr(struct function *fn, const struct expr *ex) { @@ -983,7 +1024,14 @@ expraddr(struct function *fn, const struct expr *ex) ins.l = r; ins.r = mkintcon(fn, KI4, ex->fld.off); return addinstr(fn, ins); - break; + case ESET: + assert(isagg(ex->ty)); + r = expraddr(fn, &ex->sub[0]); + structcopy(fn, r, &ex->sub[1]); + return r; + case ESEQ: + (void)exprvalue(fn, &ex->sub[0]); + return expraddr(fn, &ex->sub[1]); default: assert(!"lvalue?>"); } @@ -1071,14 +1119,19 @@ static union ref narrow(struct function *fn, enum irclass to, enum typetag tt, union ref ref) { struct instr ins = {0}; - assert(isintt(tt) || tt == TYPTR); + assert(isscalart(tt)); if (targ_primsizes[tt] >= cls2siz[to]) return ref; ins.cls = to; - switch (targ_primsizes[tt]) { - case 1: ins.op = issignedt(tt) ? Oexts1 : Oextu1; break; - case 2: ins.op = issignedt(tt) ? Oexts2 : Oextu2; break; - case 4: ins.op = issignedt(tt) ? Oexts4 : Oextu4; break; - default: assert(0); + if (isfltt(tt)) { + assert(to == KF4 && tt == TYDOUBLE); + ins.op = Ocvtf8f4; + } else { + switch (targ_primsizes[tt]) { + case 1: ins.op = issignedt(tt) ? Oexts1 : Oextu1; break; + case 2: ins.op = issignedt(tt) ? Oexts2 : Oextu2; break; + case 4: ins.op = issignedt(tt) ? Oexts4 : Oextu4; break; + default: assert(0); + } } ins.l = ref; return addinstr(fn, ins); @@ -1261,7 +1314,7 @@ exprvalue(struct function *fn, const struct expr *ex) eval((struct expr *)ex, EVFOLD); sub = ex->sub; - if (ex->ty.t == TYARRAY || ex->ty.t == TYFUNC) + if (ex->ty.t != TYVOID && !isscalar(ex->ty)) return expraddr(fn, ex); switch (ex->t) { case ENUMLIT: @@ -1404,10 +1457,12 @@ exprvalue(struct function *fn, const struct expr *ex) ins.r = cvt(fn, ty.t, sub[1^swp].ty.t, ins.r); ins.cls = cls; return addinstr(fn, ins); - break; case ESET: assert(isscalar(ex->ty)); - return genstore(fn, ex->ty, expraddr(fn, &sub[0]), exprvalue(fn, &sub[1])); + r = expraddr(fn, &sub[0]); + q = cvt(fn, sub[0].ty.t, sub[1].ty.t, exprvalue(fn, &sub[1])); + genstore(fn, ex->ty, r, q); + return narrow(fn, cls, sub[0].ty.t, q); case ESETMUL: ins.op = isunsigned(ex->ty) ? Oumul : Omul; goto Compound; @@ -1470,7 +1525,8 @@ exprvalue(struct function *fn, const struct expr *ex) vpush(&args, cvt(fn, ty.t, arg->ty.t, exprvalue(fn, arg))); vpush(&typs, mkirtype(ty)); } - ins.r = mkcall(fn, sub[0].ty, ex->narg, args.p, typs.p); + 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); @@ -1594,7 +1650,10 @@ stmt(struct parser *pr, struct function *fn) ex.ty, fn->retty); } EMITS { - r = cvt(fn, fn->retty.t, ex.ty.t, exprvalue(fn, &ex)); + if (isscalar(fn->retty)) + r = cvt(fn, fn->retty.t, ex.ty.t, exprvalue(fn, &ex)); + else + r = structreturn(fn, &ex); putjump(fn, Jrets, r, NULL, NULL); } } else { @@ -1711,9 +1770,13 @@ function(struct parser *pr, struct function *fn, const char **pnames, const stru siz = typesize(arg.ty); nalloc = siz/align + ((siz&(align-1)) != 0); EMITS { - 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)); + 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)); + } else { + arg.id = addinstr(fn, mkinstr(Ocopy, KPTR, mkref(RARG, i))).idx; + } } putdecl(pr, &arg); } else { @@ -6,6 +6,13 @@ extern vec_of(struct phi) phis; static struct bitset taken[1]; +static void +def(struct instr *ins) +{ + if (ins->reg) + bsclr(taken, ins->reg - 1); +} + static int nextreg(enum irclass cls) { @@ -31,7 +38,7 @@ static void use(struct block *blk, enum op op, int hint, union ref *ref) { struct instr *ins; - if (ref->t == REXT) { + if (ref->t == RMORE) { if (op == Ocall) { struct call *call = &calls.p[ref->idx]; for (int i = 0; i < call->narg; ++i) @@ -52,17 +59,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; - ins->reg = hint ? hint : nextreg(ins->cls); + ins->reg = (hint ? hint : nextreg(ins->cls)) + 1; } } void regalloc(struct function *fn) { - struct block *blk = fn->entry->lprev; struct instr *ins; + struct block *last = fn->entry->lprev, *blk = last; - /* a dumb linear register allocator that visits instructions backwards + /* 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 @@ -71,18 +78,17 @@ regalloc(struct function *fn) if (blk->jmp.arg.t) use(blk, blk->jmp.t == Jbcnd ? -1 : 0, 0, &blk->jmp.arg); for (int i = blk->phi.n - 1; i >= 0; --i) { ins = &instr[blk->phi.p[i]]; - bsclr(taken, ins->reg); + def(ins); assert(ins->op == Ophi); use(blk, Ophi, ins->reg, &ins->l); } for (int i = blk->ins.n - 1; i >= 0; --i) { ins = &instr[blk->ins.p[i]]; - bsclr(taken, ins->reg); + def(ins); if (ins->l.t) use(blk, ins->op, 0, &ins->l); if (ins->r.t) use(blk, ins->op, 0, &ins->r); } - blk = blk->lprev; - } while (blk != fn->entry->lprev); + } while ((blk = blk->lprev) != last); } /* vim:set ts=3 sw=3 expandtab: */ @@ -63,4 +63,13 @@ int hmm(float x, int a, char *p, char *q) { return x > 1 ? a || p : p < q && a > 0; } +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; +} + // |