diff options
| author | 2025-11-14 18:49:04 +0100 | |
|---|---|---|
| committer | 2025-11-14 19:04:51 +0100 | |
| commit | a287fe5aeb6b681ab405c0297841dce64ab4b946 (patch) | |
| tree | 5ae81f3b60cc910cd356059a77e89dd50ca83b42 | |
| parent | fc91a4ce139fd0236ad9e8c4fe1e7dad42f0b178 (diff) | |
preeliminary va_list support
| -rw-r--r-- | Makefile | 5 | ||||
| -rw-r--r-- | amd64/emit.c | 49 | ||||
| -rw-r--r-- | amd64/isel.c | 28 | ||||
| -rw-r--r-- | amd64/sysv.c | 120 | ||||
| -rw-r--r-- | c/builtin.c | 101 | ||||
| -rw-r--r-- | c/c.c | 252 | ||||
| -rw-r--r-- | c/c.h | 38 | ||||
| -rw-r--r-- | c/keywords.def | 123 | ||||
| -rw-r--r-- | ir/abi0.c | 16 | ||||
| -rw-r--r-- | ir/builder.c | 8 | ||||
| -rw-r--r-- | ir/dump.c | 4 | ||||
| -rw-r--r-- | ir/ir.c | 53 | ||||
| -rw-r--r-- | ir/ir.h | 9 | ||||
| -rw-r--r-- | ir/op.def | 3 | ||||
| -rw-r--r-- | test/varargs.c | 17 |
15 files changed, 620 insertions, 206 deletions
@@ -1,5 +1,6 @@ -SRC=main.c io.c mem.c c/c.c c/lex.c c/eval.c type.c targ.c ir/ir.c ir/builder.c ir/fold.c ir/dump.c ir/ssa.c ir/cfg.c \ - ir/intrin.c ir/abi0.c ir/optmem.c ir/regalloc.c amd64/sysv.c amd64/isel.c amd64/emit.c obj/obj.c obj/elf.c \ +SRC=main.c io.c mem.c c/c.c c/lex.c c/eval.c c/builtin.c type.c targ.c \ + ir/ir.c ir/builder.c ir/fold.c ir/dump.c ir/ssa.c ir/cfg.c ir/intrin.c ir/abi0.c ir/optmem.c ir/regalloc.c \ + amd64/sysv.c amd64/isel.c amd64/emit.c obj/obj.c obj/elf.c \ embedfilesdir.c CFLAGS=-Wall -std=c11 -pedantic OBJ=$(patsubst %.c,build/%.o,$(SRC)) diff --git a/amd64/emit.c b/amd64/emit.c index e098a81..30c0b99 100644 --- a/amd64/emit.c +++ b/amd64/emit.c @@ -518,6 +518,9 @@ DEFINSTR2(Xmovzxb, {4|8, PGPR, PMEM, "\x0F\xB6", EN_RM}, /* MOVZX r64, m8 */ {4|8, PGPR, PGPR, "\x0F\xB6", EN_RR, .r8=1}, /* MOVZX r64, r8 */ ) +DEFINSTR2(Xmovaps, + {-1, PMEM, PFPR, "\x0F\x29", EN_MR}, /* MOVAPS mem, xmm */ +) DEFINSTR2(Xxchg, {4|8, PGPR, PGPR, "\x87", EN_RR}, /* XCHG r32/64, r32/64 */ //{4|8, PGPR, PMEM, "\x87", EN_RM}, /* XCHG r32/64, m32/64 */ @@ -884,6 +887,43 @@ gencopy(uchar **pcode, enum irclass cls, struct block *blk, int curi, struct ope } } +static void +Xvaprologue(uchar **pcode, struct function *fn, struct oper sav) +{ + uint gpr0 = 0, fpr0 = 0, jmpaddr; + for (int i = 0; i < fn->nabiarg; ++i) { + struct abiarg abi = fn->abiarg[i]; + if (!abi.isstk) { + if (abi.reg < XMM0) ++gpr0; + else ++fpr0; + } + } + assert(sav.t == OMEM && sav.base == RBP); + /* save GPRS */ + for (int r = 0; r < 6; ++r) { + static const char reg[] = {RDI,RSI,RDX,RCX,R8,R9}; + if (r >= gpr0) + Xmov(pcode, KI8, sav, reg2oper(reg[r])); + sav.disp += 8; + } + + /* save FPRs, but only if al is non zero */ + if (fpr0 < 8) { + DS("\x84\xC0"); /* TEST al,al */ + jmpaddr = *pcode - objout.textbegin; + DS("\x74\xFE"); /* JE rel8 */ + } + for (int r = 0; r < 8; ++r) { + if (r >= fpr0) + Xmovaps(pcode, KF8, sav, reg2oper(XMM0 + r)); + sav.disp += 16; + } + if (fpr0 < 8) {/* patch relative jump */ + int off = (*pcode - objout.textbegin) - jmpaddr - 2; + objout.textbegin[jmpaddr+1] = off; + } +} + /* condition code for CMP */ static const uchar icmpop2cc[] = { [Oequ] = CCE, [Oneq] = CCNE, @@ -1085,14 +1125,17 @@ emitinstr(uchar **pcode, struct function *fn, struct block *blk, int curi, struc /* variadic functions need the caller to write num of args in sse regs to %al */ int n = 0; for (int i = 0; i < call->narg; ++i) - n += call->abiarg[i].reg >= XMM0; + if (!call->abiarg[i].isstk && call->abiarg[i].reg >= XMM0) + ++n; if (!n) DS("\x31\xC0"); /* XOR EAX, EAX */ else B(0xB0), B(n); /* MOV AL, n */ } Xcall(pcode, KPTR, ref2oper(ins->l)); break; + case Oxvaprologue: + Xvaprologue(pcode, fn, mkmemoper(ins->l)); + break; } - // if (ins->reg) ioper(ins - instrtab) = reg2oper(ins->reg-1); } static void @@ -1218,8 +1261,8 @@ emitbin(struct function *fn) if (!fn->isleaf && ((fn->stksiz + npush*8) & 0xF) != 0x8) { assert(usebp); fn->stksiz += 8; - rbpoff -= 8; } + rbpoff = alignup(rbpoff, 16); if (fn->stksiz != 0) { /* sub rsp, <stack size> */ diff --git a/amd64/isel.c b/amd64/isel.c index 23645bb..f8adb9a 100644 --- a/amd64/isel.c +++ b/amd64/isel.c @@ -118,11 +118,11 @@ selcall(struct function *fn, struct instr *ins, struct block *blk, int *curi) } assert(!abi.ty.isagg); - if (abi.reg >= 0) { + if (!abi.isstk) { assert(!abi.ty.isagg); *arg = mkinstr(Omove, call->abiarg[i].ty.cls, mkref(RREG, abi.reg), arg->r); } else { - union ref adr = mkaddr((struct addr){mkref(RREG, RSP), .disp = argstksiz+abi.stk}); + union ref adr = mkaddr((struct addr){mkref(RREG, RSP), .disp = abi.stk}); *arg = mkinstr(Ostore1+ilog2(cls2siz[abi.ty.cls]), 0, adr, arg->r); } } @@ -241,8 +241,16 @@ fuseaddr(union ref *r, struct block *blk, int *curi) { struct addr addr = { 0 }; - if (r->t == RADDR) return 1; if (isaddrcon(*r)) return 1; + if (r->t == RADDR) { + const struct addr *a0 = &addrht[r->i]; + if (aadd(&addr, a0->base) + && (!addr.index.bits || ascale(&addr, a0->index, mkref(RICON, a0->shift))) + && aadd(&addr, mkintcon(KPTR, a0->disp))) { + *r = mkaddr(addr); + } + return 1; + } if (r->t != RTMP) return 0; if (!aadd(&addr, *r)) return 0; @@ -311,10 +319,10 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi) break; case Oparam: assert(ins->l.t == RICON && ins->l.i < fn->nabiarg); - if (fn->abiarg[ins->l.i].reg >= 0) + if (!fn->abiarg[ins->l.i].isstk) *ins = mkinstr(Ocopy, ins->cls, mkref(RREG, fn->abiarg[ins->l.i].reg)); else /* stack */ - *ins = mkinstr(Oadd, KPTR, mkref(RREG, RBP), mkref(RICON, -fn->abiarg[ins->l.i].stk)); + *ins = mkinstr(Oadd, KPTR, mkref(RREG, RBP), mkref(RICON, 16+fn->abiarg[ins->l.i].stk)); break; case Oarg: fixarg(&ins->r, ins, blk, curi); @@ -457,6 +465,16 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi) case Ocopy: fixarg(&ins->l, ins, blk, curi); break; + case Oxvaprologue: + fuseaddr(&ins->l, blk, curi); + assert(ins->l.t == RADDR); + /* !this must be the first instruction */ + assert(*curi == 1); + assert(blk == fn->entry); + t = blk->ins.p[0]; + blk->ins.p[0] = blk->ins.p[1]; + blk->ins.p[1] = t; + break; } } diff --git a/amd64/sysv.c b/amd64/sysv.c index 334be26..af0ade0 100644 --- a/amd64/sysv.c +++ b/amd64/sysv.c @@ -77,7 +77,7 @@ abiarg(short r[2], uchar cls[2], int *ni, int *nf, int *ns, union irtype typ) } else if (*ni < NINT) { r[0] = intregs[(*ni)++]; } else { - r[0] = -*ns - 16; + r[0] = *ns; *ns += 8; return 0; /* MEMORY */ } @@ -86,7 +86,7 @@ abiarg(short r[2], uchar cls[2], int *ni, int *nf, int *ns, union irtype typ) cls[0] = cls[1] = 0; ret = classify(cls, &typedata[typ.dat], 0); if (!ret) { /*MEMORY*/ - r[0] = -*ns - 16; + r[0] = *ns; *ns = alignup(*ns + typedata[typ.dat].siz, 8); return 0; } @@ -100,7 +100,7 @@ abiarg(short r[2], uchar cls[2], int *ni, int *nf, int *ns, union irtype typ) r[i] = intregs[(*ni)++]; else { /* MEMORY */ *ni = ni_save, *nf = nf_save; - r[0] = -*ns - 16; + r[0] = *ns; *ns = alignup(*ns + typedata[typ.dat].siz, 8); r[1] = -1; return cls[0] = cls[1] = 0; @@ -140,6 +140,118 @@ abiret(short r[2], uchar cls[2], int *ni, union irtype typ) return ret; } +static void +vastart(struct function *fn, struct block *blk, int *curi) +{ + union ref rsave; /* register save area */ + int gpr0 = 0, fpr0 = 0, stk0 = 0; + struct instr *ins = &instrtab[blk->ins.p[*curi]]; + union ref ap = ins->l, src, dst; + assert(ins->op == Ovastart); + /* add xvaprologue if not there yet, which must be the first instruction in the function */ + if (fn->entry->ins.n > 0 && instrtab[fn->entry->ins.p[0]].op == Oxvaprologue) { + rsave = mkref(RTMP, fn->entry->ins.p[0]); + } else { + rsave = insertinstr(fn->entry, 0, mkalloca(192, 16)); + insertinstr(fn->entry, 1, mkinstr(Oxvaprologue, 0, rsave, .keep=1)); + } + /* find first unnamed gpr and fpr */ + for (int i = 0; i < fn->nabiarg; ++i) { + struct abiarg abi = fn->abiarg[i]; + if (!abi.isstk){ + if (abi.reg < XMM0) ++gpr0; + else ++fpr0; + } else { + stk0 = abi.stk+8; + } + } + /* set ap->reg_save_area */ + *ins = mkinstr(Oadd, KPTR, ap, mkref(RICON, 16)); + dst = mkref(RTMP, ins - instrtab); + int i = *curi + 1; + insertinstr(blk, i++, mkinstr(Ostore8, 0, dst, rsave)); + /* set ap->overflow_arg_area */ + src = insertinstr(blk, i++, mkinstr(Oadd, KPTR, mkref(RREG, RBP), mkref(RICON, 16+stk0))); + dst = insertinstr(blk, i++, mkinstr(Oadd, KPTR, ap, mkref(RICON, 8))); + insertinstr(blk, i++, mkinstr(Ostore8, 0, dst, src)); + /* set ap->gp_offset */ + insertinstr(blk, i++, mkinstr(Ostore4, 0, ap, mkref(RICON, gpr0*8))); + /* set ap->fp_offset */ + dst = insertinstr(blk, i++, mkinstr(Oadd, KPTR, ap, mkref(RICON, 4))); + insertinstr(blk, i++, mkinstr(Ostore4, 0, dst, mkref(RICON, 6*8 + fpr0*8))); + *curi = i; +} + +static void +vaarg(struct function *fn, struct block *blk, int *curi) +{ + short r[2]; + uchar cls[2]; + union ref tmp; + int ni = 0, nf = 0, ns = 0; + int var = blk->ins.p[*curi]; + union ref ap = instrtab[var].l; + union irtype ty = ref2type(instrtab[var].r); + + assert(instrtab[var].op == Ovaarg); + blk->ins.p[*curi] = newinstr(blk, (struct instr){Onop}); + + int ret = abiarg(r, cls, &ni, &nf, &ns, ty); + + if (ret == 2) assert(!"nyi"); + else if (ret == 1) { + struct block *merge; + union ref phi, phiargs[2]; + if (ni) { + /* l->gp_offset < 48 - num_gp * 8 */ + tmp = insertinstr(blk, (*curi)++, mkinstr(Oloadu4, KI4, ap)); + tmp = insertinstr(blk, (*curi)++, mkinstr(Oulte, KI4, tmp, mkref(RICON, 48 - ni*8))); + merge = blksplitafter(fn, blk, *curi); + blk->jmp.t = 0; + useblk(fn, blk); + putcondbranch(fn, tmp, newblk(fn), newblk(fn)); + useblk(fn, blk->s1); + { + /* phi0: &l->reg_save_area[l->gp_offset] */ + union ref sav = addinstr(fn, mkinstr(Oloadi8, KPTR, irbinop(fn, Oadd, KPTR, ap, mkref(RICON, 16)))); + union ref gpoff = addinstr(fn, mkinstr(Oloadu4, KI4, ap)); + phiargs[0] = irbinop(fn, Oadd, KPTR, sav, gpoff); + /* l->gp_offset += num_gp * 8 */ + gpoff = irbinop(fn, Oadd, KI4, gpoff, mkref(RICON, ni * 8)); + addinstr(fn, mkinstr(Ostore4, 0, ap, gpoff)); + assert(merge->npred == 1); + blkpred(merge, 0) = blk->s1; + blk->s1->jmp.t = Jb; + blk->s1->s1 = merge; + } + useblk(fn, blk->s2); + { + /* phi1: l->overflow_arg_area */ + union ref adr = irbinop(fn, Oadd, KPTR, ap, mkref(RICON, 8)); + union ref ovf = addinstr(fn, mkinstr(Oloadi8, KPTR, adr)); + /* align no-op */ + + phiargs[1] = ovf; + /* update l->overflow_arg_area += num_gp*8 */ + int siz = 8; + addinstr(fn, mkinstr(Ostore8, 0, adr, irbinop(fn, Oadd, KPTR, ovf, mkref(RICON, siz)))); + putbranch(fn, merge); + } + assert(merge->npred == 2); + vpush(&merge->ins, 0); + memmove(merge->ins.p+1, merge->ins.p, (merge->ins.n-1)*sizeof *merge->ins.p); + merge->ins.p[0] = var; + phi = insertphi(merge, KPTR); + memcpy(phitab.p[instrtab[phi.i].l.i], phiargs, sizeof phiargs); + instrtab[var] = mkinstr(cls[0] == KI4 ? Oloads4 : Oloadi8, cls[0], phi); + } else { + assert(0&&nf); + } + } else { + assert(!"nyi"); + } +} + static const char amd64_rnames[][6] = { #define R(r) #r, LIST_REGS(R) @@ -158,6 +270,8 @@ const struct mctarg t_amd64_sysv = { .isa = ISamd64, .abiret = abiret, .abiarg = abiarg, + .vastart = vastart, + .vaarg = vaarg, .isel = amd64_isel, .emit = amd64_emit }; diff --git a/c/builtin.c b/c/builtin.c new file mode 100644 index 0000000..d386480 --- /dev/null +++ b/c/builtin.c @@ -0,0 +1,101 @@ +#include "c.h" +#include "../ir/ir.h" + +static bool +callcheck(const struct span *span, int nparam, const union type *param, int narg, struct expr *args) +{ + bool ok = 1; + for (int i = 0, n = narg < nparam ? narg : nparam; i < n; ++i) { + if (!assigncheck(typedecay(param[i]), &args[i])) { + ok = 0; + error(&args[i].span, "arg #%d of type '%ty' is incompatible with '%ty'", + i, args[i].ty, param[i]); + } + } + + if (narg > nparam) { + error(&args[nparam].span, "too many args to builtin function taking %d params", nparam); + ok = 0; + } else if (narg < nparam) { + error(span, "not enough args to builtin function taking %d param%s", nparam, + nparam != 1 ? "s" : ""); + ok = 0; + } + return ok; +} + +#define DEF_FNLIKE_SEMA(name, retty, ...) \ + static bool \ + name##_sema(struct comp *cm, struct expr *ex) { \ + static union type par[] = { __VA_ARGS__, {0} }; \ + ex->ty = retty; \ + return callcheck(&ex->span, arraylength(par)-1, par, ex->narg, ex->sub+1); \ + } + +static bool +va_start_sema(struct comp *cm, struct expr *ex) +{ + ex->ty = mktype(TYVOID); + return callcheck(&ex->span, 1, &cvalistty, ex->narg, ex->sub+1); +} + +static union ref +va_start_comp(struct function *fn, struct expr *ex, bool discard) +{ + assert(ex->t == ECALL && ex->narg == 1); + assert(ex->sub[1].ty.bits == cvalistty.bits); + if (!typedata[fn->fnty.dat].variadic) + error(&ex->span, "va_start used in non-variadic function"); + addinstr(fn, mkinstr(Ovastart, 0, compileexpr(fn, &ex->sub[1], 0))); + return NOREF; +} + +static bool +va_end_sema(struct comp *cm, struct expr *ex) +{ + ex->ty = mktype(TYVOID); + return callcheck(&ex->span, 1, &cvalistty, ex->narg, ex->sub+1); +} + +static union ref +va_end_comp(struct function *fn, struct expr *ex, bool discard) +{ + return NOREF; +} + +union ref +builtin_va_arg_comp(struct function *fn, const struct expr *ex, bool discard) +{ + assert(ex->t == EVAARG && ex->ty.t); + enum irclass k = isagg(ex->ty) ? KPTR : type2cls[scalartypet(ex->ty)]; + return addinstr(fn, mkinstr(Ovaarg, k, compileexpr(fn, ex->sub, 0), mktyperef(mkirtype(ex->ty)))); +} + +#define LIST_BUILTINS(_) \ + _(va_start) \ + _(va_end) \ + +static const struct { + const char *name; + struct builtin b; +} tab[] = { +#define FNS(x) { "__builtin_" #x, { x##_sema, x##_comp } }, + LIST_BUILTINS(FNS) +#undef FNS +}; + +const char *intern(const char *); +void +putbuiltins(struct env *env) +{ + for (int i = 0; i < arraylength(tab); ++i) { + envadddecl(env, &(struct decl) { + .name = intern(tab[i].name), + .isbuiltin = 1, + .builtin = &tab[i].b, + }); + } +} + + +/* vim:set ts=3 sw=3 expandtab: */ @@ -4,31 +4,19 @@ #include "../ir/ir.h" #include "../obj/obj.h" -/** C compiler state **/ -struct comp { - struct lexer lx; - struct env *env; - struct arena *fnarena, *exarena; - struct span fnblkspan; - uint loopdepth, switchdepth; - struct block *breakto, *loopcont; - struct switchstmt *switchstmt; - struct label *labels; -}; - /** Parsing helper functions **/ -#define peek(Cm,Tk) lexpeek(&(Cm)->lx,Tk) +#define peek(Cm,Tk) lexpeek((Cm)->lx,Tk) static int lexc(struct comp *cm, struct token *tk) { struct token tk2; - int t = lex(&cm->lx, tk); + int t = lex(cm->lx, tk); if (t == TKSTRLIT && peek(cm, &tk2) == TKSTRLIT && tk2.wide == tk->wide) { /* 5.1.1.2 Translation phase 6: concatenate adjacent string literal tokens */ static char buf[200]; vec_of(char) rest = VINIT(buf, sizeof buf); do { - lex(&cm->lx, NULL); + lex(cm->lx, NULL); if (tk) { joinspan(&tk->span.ex, tk2.span.ex); if (!tk->wide) @@ -171,7 +159,6 @@ struct env { /* ditto for envtagged[] */ ushort tagged, ntagged; }; -static struct env toplevel; static void envdown(struct comp *cm, struct env *e) @@ -196,7 +183,7 @@ envup(struct comp *cm) cm->env = env->up; } -static struct decl * +struct decl * envadddecl(struct env *env, const struct decl *d) { assert(env->decl + env->ndecl == envdecls.n); @@ -264,8 +251,8 @@ redeclarationok(const struct decl *old, const struct decl *new) static struct decl * putdecl(struct comp *cm, const struct decl *decl) { - struct decl *l; - for (l = NULL; enviterdecl(&l, cm->env);) { + assert(!decl->isbuiltin); + for (struct decl *l = NULL; enviterdecl(&l, cm->env);) { if (decl->name == l->name) { if (l->isdef && decl->isdef) { error(&decl->span, "redefinition of '%s'", decl->name); @@ -278,18 +265,15 @@ putdecl(struct comp *cm, const struct decl *decl) } } } - l = envadddecl(cm->env, decl); - return l; + return envadddecl(cm->env, decl); } static struct decl * finddecl(struct comp *cm, const char *name) { - struct env *e; - struct decl *l; assert(name); - for (e = cm->env; e; e = e->up) { - for (l = NULL; enviterdecl(&l, e);) { + for (struct env *e = cm->env; e; e = e->up) { + for (struct decl *l = NULL; enviterdecl(&l, e);) { if (name == l->name) return l; } @@ -300,12 +284,10 @@ finddecl(struct comp *cm, const char *name) static union type gettagged(struct comp *cm, struct span *span, enum typetag tt, const char *name, bool dodef) { - struct env *e; - struct tagged *l; struct typedata td = {0}; assert(name); - for (e = cm->env; e; e = e->up) { - for (l = NULL; envitertagged(&l, e);) { + for (struct env *e = cm->env; e; e = e->up) { + for (struct tagged *l = NULL; envitertagged(&l, e);) { if (name == ttypenames[typedata[l->ty.dat].id]) { if (dodef && e != cm->env) goto Break2; @@ -324,10 +306,9 @@ Break2: static union type deftagged(struct comp *cm, struct span *span, enum typetag tt, const char *name, union type ty) { - struct tagged *l; struct typedata td = {0}; assert(name); - for (l = NULL; envitertagged(&l, cm->env);) { + for (struct tagged *l = NULL; envitertagged(&l, cm->env);) { if (name == ttypenames[typedata[l->ty.dat].id]) { *span = l->span; return l->ty; @@ -359,7 +340,7 @@ argpromote(union type t) return t; } -static bool +bool assigncheck(union type t, const struct expr *src) { if (assigncompat(t, typedecay(src->ty))) return 1; @@ -653,6 +634,8 @@ exprdup2(struct comp *cm, const struct expr *e1, const struct expr *e2) static struct expr expr(struct comp *cm); static struct expr commaexpr(struct comp *cm); +enum { IMPLICITFUNCTY = 0xFF, }; + static struct expr /* 6.5.2.2 Function calls */ callexpr(struct comp *cm, const struct span *span_, const struct expr *callee) { @@ -665,11 +648,17 @@ callexpr(struct comp *cm, const struct span *span_, const struct expr *callee) vec_of(struct expr) args = VINIT(argbuf, arraylength(argbuf)); bool spanok = joinspan(&span.ex, span_->ex); bool printsig = 0; + const struct builtin *builtin = NULL; + + if (callee->t == ESYM && !callee->ty.t && callee->sym->isbuiltin) { + builtin = callee->sym->builtin; + assert(!ty.t); + } - if (callee->t == ESYM && !callee->ty.t) { /* implicit function decl.. */ + if (callee->t == ESYM && ty.t == IMPLICITFUNCTY) { /* implicit function decl.. */ const char *name = (void *)callee->sym; struct decl decl = { - ty = mkfntype(mktype(TYINT), 0, NULL, NULL, /* kandr */ 1, 0), + (ty = mkfntype(mktype(TYINT), 0, NULL, NULL, /* kandr */ 1, 0)), .scls = SCEXTERN, .span = callee->span, .name = name }; warn(&callee->span, "call to undeclared function '%s'", name); @@ -678,9 +667,12 @@ callexpr(struct comp *cm, const struct span *span_, const struct expr *callee) td = &typedata[ty.dat]; } - if (ty.t == TYPTR) /* auto-deref when calling a function pointer */ - ty = typechild(ty); - if (ty.t != TYFUNC) error(&callee->span, "calling a value of type '%ty'", callee->ty); + if (!builtin) { + if (ty.t == TYPTR) /* auto-deref when calling a function pointer */ + ty = typechild(ty); + if (ty.t != TYFUNC) + error(&callee->span, "calling a value of type '%ty'", callee->ty); + } if (!match(cm, &tk, ')')) for (;;) { arg = expr(cm); spanok = spanok && joinspan(&span.ex, callee->span.ex); @@ -705,7 +697,7 @@ callexpr(struct comp *cm, const struct span *span_, const struct expr *callee) } if (!spanok || !joinspan(&span.ex, tk.span.ex)) span = *span_; - if (!td->variadic && !td->kandr && args.n < td->nmemb) { + if (ty.t == TYFUNC && !td->variadic && !td->kandr && args.n < td->nmemb) { error(&tk.span, "not enough args to function taking %d param%s", td->nmemb, td->nmemb != 1 ? "s" : ""); printsig = 1; @@ -717,6 +709,9 @@ callexpr(struct comp *cm, const struct span *span_, const struct expr *callee) ex.sub[0] = *callee; memcpy(ex.sub+1, args.p, args.n*sizeof(struct expr)); vfree(&args); + if (builtin) { + builtin->sema(cm, &ex); + } return ex; } @@ -810,6 +805,38 @@ ppostfixopers(struct comp *cm, struct expr *ex) } } +static struct expr +vaargexpr(struct comp *cm, struct span *span) +{ + struct token tk; + struct expr ex = mkexpr(EXXX, *span, mktype(TYVOID), ); + if (expect(cm, '(', "after __builtin_va_arg")) { + struct expr arg = expr(cm); + struct decl decl; + union type ty; + expect(cm, ',', NULL); + decl = pdecl(&(struct declstate){DCASTEXPR}, cm); + ty = decl.ty; + peek(cm, &tk); + if (expect(cm, ')', NULL)) + joinspan(&span->ex, tk.span.ex); + if (ty.t == TYARRAY) + warn(&decl.span, "va_arg type argument is array type '%ty', which is undefined behavior", decl.ty); + else if (ty.t == TYFUNC) + error(&decl.span, "va_arg type argument is function type '%ty'", decl.ty); + else { + ty = argpromote(ty); + if (ty.bits != decl.ty.bits) { + warn(&decl.span, + "va_arg type argument is promotable type '%ty', which has undefined behavior" + " (it will be promoted to '%ty')", decl.ty, ty); + } + } + ex = mkexpr(EVAARG, *span, decl.ty, .sub = exprdup(cm, &arg)); + } + return ex; +} + static inline int tkprec(int tt) { @@ -866,12 +893,41 @@ Unary: } goto Unary; + /* might be unary op (cast) or primary expr */ + case '(': + if (!isdecltok(cm)) { /* (expr) */ + ex = commaexpr(cm); + expect(cm, ')', NULL); + } else { /* (type) expr */ + struct declstate st = { DCASTEXPR }; + struct decl decl = pdecl(&st, cm); + struct span span = tk.span; + assert(decl.ty.t); + peek(cm, &tk); + if (expect(cm, ')', NULL)) + joinspan(&span.ex, tk.span.ex); + if (peek(cm, NULL) == '{') { + if (ccopt.cstd < STDC99) + warn(&tk.span, "compound literals are a c99 feature"); + ex = initializer(cm, &decl.ty, (decl.scls & SCSTATIC) ? EVSTATICINI : EVFOLD, + /* globl */ 0, decl.qual, NULL); + break; + } + unops[nunop].span = span; + unops[nunop].ty = decl.ty; + if (++nunop >= arraylength(unops)) { + ex = exprparse(cm, 999, NULL, 0); + break; + } + goto Unary; + } + break; /* base exprs */ case TKNUMLIT: case TKCHRLIT: ex = mkexpr(ENUMLIT, tk.span, mktype(0), ); if (!(ty.t = parsenumlit(&ex.u, &ex.f, &tk, 0))) - error(&tk.span, "bad number literal %'tk", &tk); + error(&tk.span, "bad %s literal %'tk", tk.t == TKNUMLIT ? "number" : "character", &tk); ex.ty.t = ty.t ? ty.t : TYINT; break; case TKSTRLIT: @@ -883,7 +939,7 @@ Unary: decl = finddecl(cm, tk.s); if (!decl) { if (peek(cm, NULL) == '(') { /* implicit function decl? */ - ex = mkexpr(ESYM, tk.span, mktype(0), .sym = (void *)tk.s); + ex = mkexpr(ESYM, tk.span, mktype(IMPLICITFUNCTY), .sym = (void *)tk.s); } else { error(&tk.span, "undeclared identifier %'tk", &tk); ex = mkexpr(ESYM, tk.span, mktype(TYINT), .sym = NULL); @@ -897,33 +953,6 @@ Unary: ex = mkexpr(ESYM, tk.span, decl->ty, .qual = decl->qual, .sym = decl); } break; - - /* might be unary op or primary expr */ - case '(': - if (!isdecltok(cm)) { /* (expr) */ - ex = commaexpr(cm); - expect(cm, ')', NULL); - break; - } else { /* (type) expr */ - struct declstate st = { DCASTEXPR }; - struct decl decl = pdecl(&st, cm); - expect(cm, ')', NULL); - assert(decl.ty.t); - if (peek(cm, NULL) == '{') { - if (ccopt.cstd < STDC99) - warn(&tk.span, "compound literals are a c99 feature"); - ex = initializer(cm, &decl.ty, (decl.scls & SCSTATIC) ? EVSTATICINI : EVFOLD, - /* globl */ 0, decl.qual, NULL); - break; - } - unops[nunop].span = tk.span; - unops[nunop].ty = decl.ty; - if (++nunop >= arraylength(unops)) { - ex = exprparse(cm, 999, NULL, 0); - break; - } - goto Unary; - } case TKWsizeof: span = tk.span; if (!match(cm, NULL, '(')) /* sizeof expr */ @@ -945,6 +974,10 @@ Unary: sizeofcheck(&span, ty); ex = mkexpr(ENUMLIT, span, mktype(targ_sizetype), .u = typesize(ty)); break; + case TKW__builtin_va_arg: + span = tk.span; + return vaargexpr(cm, &span); + break; default: fatal(&tk.span, "expected %s (near %'tk)", fromstmt ? "statement" : "expression", &tk); } @@ -1193,7 +1226,6 @@ dumpini(struct initparser *ip) } #endif -static union ref expraddr(struct function *, const struct expr *); static bool globsym(union ref *psym, const struct expr *ex) { @@ -1981,7 +2013,7 @@ ptypeof(struct comp *cm) } static bool -declspec(struct declstate *st, struct comp *cm) +declspec(struct declstate *st, struct comp *cm, struct span *pspan) { struct token tk; struct decl *decl; @@ -2001,6 +2033,7 @@ declspec(struct declstate *st, struct comp *cm) for (;;) { peek(cm, &tk); + if (!span.ex.len) span = tk.span; switch (tk.t) { case TKWconst: st->qual |= QCONST; @@ -2068,13 +2101,11 @@ declspec(struct declstate *st, struct comp *cm) lex(cm, &tk); st->base = tagtype(cm, tk.t); st->tagdecl = 1; - if (!span.ex.len) span.ex = tk.span.ex; joinspan(&span.ex, tk.span.ex); goto End; case TKW__typeof__: case TKWtypeof: lex(cm, &tk); st->base = ptypeof(cm); - if (!span.ex.len) span.ex = tk.span.ex; joinspan(&span.ex, tk.span.ex); goto End; case TKIDENT: @@ -2085,7 +2116,6 @@ declspec(struct declstate *st, struct comp *cm) } /* fallthru */ default: - if (!span.ex.len) span.ex = tk.span.ex; goto End; case TKW_BitInt: case TKW_Complex: case TKW_Decimal128: case TKW_Decimal32: @@ -2093,12 +2123,12 @@ declspec(struct declstate *st, struct comp *cm) error(&tk.span, "%'tk is unsupported", &tk); arith = arith ? arith : KINT; } - if (!span.ex.len) span.ex = tk.span.ex; joinspan(&span.ex, tk.span.ex); lex(cm, &tk); if (st->base.t) break; } End: + if (pspan) *pspan = span; if (st->base.t && arith) { /* combining arith type specifiers and other types */ Bad: @@ -2221,7 +2251,7 @@ cvqual(struct comp *cm) } static void -decltypes(struct comp *cm, struct decllist *list, const char **name, struct span *span) { +decltypes(struct comp *cm, struct decllist *list, const char **name, struct span *span, struct span *namespan) { struct token tk; struct decllist *ptr, node; @@ -2230,6 +2260,7 @@ decltypes(struct comp *cm, struct decllist *list, const char **name, struct span node.qual = cvqual(cm); node.span = tk.span; declinsert(list, &node); + joinspan(&span->ex, tk.span.ex); } ptr = list->next; switch (peek(cm, &tk)) { @@ -2248,10 +2279,12 @@ decltypes(struct comp *cm, struct decllist *list, const char **name, struct span node.kandr = 1; node.npar = 0; declinsert(ptr->prev, &node); + joinspan(&span->ex, tk.span.ex); break; } else { - decltypes(cm, list, name, span); + decltypes(cm, list, name, span, namespan); expect(cm, ')', NULL); + joinspan(&span->ex, tk.span.ex); } break; case TKIDENT: @@ -2259,12 +2292,12 @@ decltypes(struct comp *cm, struct decllist *list, const char **name, struct span error(&tk.span, "unexpected identifier in type name"); else { *name = tk.s; - *span = tk.span; + *namespan = tk.span; } lex(cm, &tk); + joinspan(&span->ex, tk.span.ex); break; default: - *span = tk.span; if (name) *name = NULL; } @@ -2292,6 +2325,7 @@ decltypes(struct comp *cm, struct decllist *list, const char **name, struct span node.t = TYARRAY; node.len = n; declinsert(ptr->prev, &node); + joinspan(&span->ex, node.span.ex); } else if (match(cm, &tk, '(')) Func: { static int depth = 0; vec_of(union type) params = {0}; @@ -2335,7 +2369,8 @@ decltypes(struct comp *cm, struct decllist *list, const char **name, struct span params.n, decl.ty, tdgetqual(qual.p, params.n-1)); } } - joinspan(&node.span.ex, tk.span.ex); + peek(cm, &tk); + joinspan(&span->ex, tk.span.ex); if (!match(cm, &tk, ',')) { expect(cm, ')', NULL); break; @@ -2359,15 +2394,17 @@ decltypes(struct comp *cm, struct decllist *list, const char **name, struct span node.pspans = params.n ? spans.p : NULL; node.npar = params.n; declinsert(ptr->prev, &node); + joinspan(&span->ex, node.span.ex); } else break; } } static struct decl -declarator(struct declstate *st, struct comp *cm) { - struct decl decl = { st->base, st->scls, st->qual, st->align }; +declarator(struct declstate *st, struct comp *cm, struct span span0) { + struct decl decl = { st->base, st->scls, st->qual, st->align, .span = span0 }; struct decllist list = { &list, &list }, *l; static bool inidecltmp = 0; + struct span namespan ={0}; if (!inidecltmp) { inidecltmp = 1; for (int i = 0; i < arraylength(decltmp); ++i) { @@ -2376,7 +2413,7 @@ declarator(struct declstate *st, struct comp *cm) { } } - decltypes(cm, &list, st->kind == DCASTEXPR ? NULL : &decl.name, &decl.span); + decltypes(cm, &list, st->kind == DCASTEXPR ? NULL : &decl.name, &decl.span, &namespan); if (!decl.name && st->kind != DCASTEXPR && st->kind != DFUNCPARAM) { if (list.prev == &list) lex(cm, NULL); error(&decl.span, "expected `(', `*' or identifier"); @@ -2398,7 +2435,7 @@ declarator(struct declstate *st, struct comp *cm) { if (decl.ty.t == TYFUNC) error(&decl.span, "function cannot return function type '%ty'", decl.ty); else if (decl.ty.t == TYARRAY) - error(&decl.span, "function cannot return array type", decl.ty); + error(&decl.span, "function cannot return array type '%ty'", decl.ty); else if (decl.ty.t != TYVOID && isincomplete(decl.ty)) error(&decl.span, "function cannot return incomplete type '%ty'", decl.ty); if (l->kandr && ccopt.cstd > STDC89) @@ -2419,7 +2456,8 @@ declarator(struct declstate *st, struct comp *cm) { l->next = declfreelist; declfreelist = l; } - + if (st->kind != DCASTEXPR) + decl.span = namespan; return decl; } @@ -2499,22 +2537,25 @@ pdecl(struct declstate *st, struct comp *cm) { st->scls &= allowed; } peek(cm, &tk); - if (!declspec(st, cm) && st->kind != DTOPLEVEL) { + if (!declspec(st, cm, &decl.span) && st->kind != DTOPLEVEL) { lex(cm, &tk); error(&tk.span, "unknown type name %'s", tk.s); } + } else { + peek(cm, &tk); + decl.span = tk.span; } if (st->scls == SCTYPEDEF) iniallowed = 0; if (first && st->tagdecl && match(cm, &tk, ';')) { - decl = (struct decl) { st->base, st->scls, st->qual, st->align, 0, tk.span }; + decl = (struct decl) { st->base, st->scls, st->qual, st->align, .span = decl.span }; return decl; } else if (st->kind == DFIELD && match(cm, &tk, ':')) { - decl = (struct decl) { st->base, st->scls, st->qual, st->align, 0, tk.span }; + decl = (struct decl) { st->base, st->scls, st->qual, st->align, .span = decl.span }; st->bitf = 1; return decl; } - decl = declarator(st, cm); + decl = declarator(st, cm, decl.span); if (iniallowed && match(cm, &tk, '=')) { st->varini = 1; @@ -2543,8 +2584,6 @@ AfterIniBitf: /* IR Generation */ /*****************/ -static union ref expraddr(struct function *, const struct expr *); -static union ref compileexpr(struct function *, const struct expr *, bool discard); static inline union ref exprvalue(struct function *fn, const struct expr *ex) { @@ -2588,7 +2627,7 @@ mkhiddensym(const char *fnname, const char *name, int id) static void geninit(struct function *fn, union type t, union ref dst, const struct expr *src); static union ref condexprvalue(struct function *fn, const struct expr *ex, bool discard); -static union ref +union ref expraddr(struct function *fn, const struct expr *ex) { struct decl *decl; @@ -3042,6 +3081,9 @@ compilecall(struct function *fn, const struct expr *ex) struct instr insnsbuf[10]; vec_of(struct instr) insns = VINIT(insnsbuf, arraylength(insnsbuf)); + if (ex->sub[0].t == ESYM && ex->sub[0].sym->isbuiltin) { + return ex->sub[0].sym->builtin->comp(fn, (struct expr *)ex, 0); + } ins.op = Ocall; if (isagg(ex->ty)) { ins.cls = KPTR; @@ -3127,7 +3169,7 @@ genbitfstore(struct function *fn, const union type ty, union ref addr, genstore(fn, ty, addr, val); } -static union ref +union ref compileexpr(struct function *fn, const struct expr *ex, bool discard) { union type ty; @@ -3154,6 +3196,8 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) case ESYM: if (discard && !(ex->qual & QVOLATILE)) return NOREF; return genload(fn, ex->ty, expraddr(fn, ex), ex->qual & QVOLATILE); + case EVAARG: + return builtin_va_arg_comp(fn, ex, discard); case EGETF: if (discard && !(ex->qual & QVOLATILE)) return NOREF; if (ex->fld.bitsiz) { @@ -3904,7 +3948,7 @@ stmt(struct comp *cm, struct function *fn) break; } freearena(&cm->exarena); - lexerfreetemps(&cm->lx); + lexerfreetemps(cm->lx); return fn->curblk == NULL; } @@ -4120,22 +4164,24 @@ function(struct comp *cm, struct function *fn, const char **pnames, const struct } } +union type cvalistty; void docomp(struct comp *cm) { - static union type valistty; + static struct env toplevel; struct token tk[1]; if (!cm->env) cm->env = &toplevel; - - if (!valistty.t) { + if (!cvalistty.t) { struct typedata td = { .t = TYSTRUCT, .siz = targ_valistsize, .align = targ_primalign[TYPTR], .nmemb = 1, - .fld = (struct namedfield [1]){{"?"}} + .fld = &(struct namedfield){"-", {mkarrtype(mktype(TYPTR), 0, 3)}} }; - valistty = mktagtype(intern("__builtin_va_list"), &td); + cvalistty = mkarrtype(mktagtype(intern("__builtin_va_list"), &td), 0, 1); } - putdecl(cm, &(struct decl) { valistty, SCTYPEDEF, .name = intern("__builtin_va_list") }); + peek(cm, tk); + envadddecl(cm->env, &(struct decl) { cvalistty, SCTYPEDEF, .span = tk->span, .name = intern("__builtin_va_list") }); + putbuiltins(cm->env); while (peek(cm, tk) != TKEOF) { struct declstate st = { DTOPLEVEL }; @@ -4181,7 +4227,7 @@ docomp(struct comp *cm) } freearena(&cm->fnarena); freearena(&cm->exarena); - lexerfreetemps(&cm->lx); + lexerfreetemps(cm->lx); } while (st.more); } } @@ -4192,7 +4238,7 @@ initcm(struct comp *cm, const char *file) enum { N = 1<<12 }; static union { char m[sizeof(struct arena) + N]; struct arena *_align; } amem[2]; const char *err; - switch (initlexer(&cm->lx, &err, file)) { + switch (initlexer(cm->lx, &err, file)) { default: assert(0); case LXERR: fatal(NULL, "Cannot open %'s: %s", file, err); @@ -4207,7 +4253,7 @@ initcm(struct comp *cm, const char *file) void ccomp(const char *file) { - struct comp cm = {0}; + struct comp cm = {&(struct lexer){0}}; initcm(&cm, file); docomp(&cm); } @@ -4215,9 +4261,9 @@ ccomp(const char *file) void cpp(struct wbuf *out, const char *file) { - struct comp cm = {0}; + struct comp cm = {&(struct lexer){0}}; initcm(&cm, file); - lexerdump(&cm.lx, out); + lexerdump(cm.lx, out); } /* vim:set ts=3 sw=3 expandtab: */ @@ -5,7 +5,7 @@ /*************/ enum exprkind { - EXXX, ENUMLIT, ESTRLIT, ESYM, EINIT, EGETF, ECALL, ECOND, + EXXX, ENUMLIT, ESTRLIT, ESYM, EVAARG, EINIT, EGETF, ECALL, ECOND, /* unary */ EPLUS, ENEG, ECOMPL, ELOGNOT, EDEREF, EADDROF, ECAST, EPREINC, EPOSTINC, EPREDEC, EPOSTDEC, @@ -59,6 +59,18 @@ struct init { } *vals, **tail; }; +/** C compiler state **/ +struct comp { + struct lexer *lx; + struct env *env; + struct arena *fnarena, *exarena; + struct span fnblkspan; + uint loopdepth, switchdepth; + struct block *breakto, *loopcont; + struct switchstmt *switchstmt; + struct label *labels; +}; + enum storageclass { SCNONE, SCTYPEDEF = 1<<0, @@ -72,17 +84,35 @@ enum storageclass { struct decl { union type ty; uchar scls; - uchar qual : 2; - uchar isenum : 1; - uchar isdef : 1; + uchar qual : 2, + isenum : 1, + isdef : 1, + isbuiltin : 1; struct span span; const char *name; union { struct { ushort align; int id; }; vlong value; + const struct builtin *builtin; }; }; +extern union type cvalistty; +struct function; +struct decl *envadddecl(struct env *env, const struct decl *d); +bool assigncheck(union type t, const struct expr *src); +union ref expraddr(struct function *, const struct expr *); +union ref compileexpr(struct function *, const struct expr *, bool discard); + +/** builtin.c **/ +struct builtin { + bool (*sema)(struct comp *, struct expr *); + union ref (*comp)(struct function *, struct expr *, bool discard); +}; +void putbuiltins(struct env *); +union ref builtin_va_arg_comp(struct function *, const struct expr *, bool discard); + +/** eval.c **/ enum evalmode { EVNONE, EVINTCONST, diff --git a/c/keywords.def b/c/keywords.def index 258a396..f971830 100644 --- a/c/keywords.def +++ b/c/keywords.def @@ -1,64 +1,65 @@ /* !SORTED */ -_(_Alignas, STDC11) -_(_Alignof, STDC11) -_(_Atomic, STDC11) -_(_BitInt, STDC23) -_(_Bool, STDC99) -_(_Complex, STDC99) -_(_Decimal128, STDC23) -_(_Decimal32, STDC23) -_(_Decimal64, STDC23) -_(_Generic, STDC11) -_(_Imaginary, STDC99) -_(_Noreturn, STDC11) -_(_Static_assert, STDC11) -_(_Thread_local, STDC11) -_(__typeof__, 0) -_(alignas, STDC23) -_(alignof, STDC23) -_(auto, 0) -_(bool, STDC23) -_(break, 0) -_(case, 0) -_(char, 0) -_(const, 0) -_(constexpr, STDC23) -_(continue, 0) -_(default, 0) -_(do, 0) -_(double, 0) -_(else, 0) -_(enum, 0) -_(extern, 0) -_(false, STDC23) -_(float, 0) -_(for, 0) -_(goto, 0) -_(if, 0) -_(inline, STDC99) -_(int, 0) -_(long, 0) -_(nullptr, STDC23) -_(register, 0) -_(restrict, STDC99) -_(return, 0) -_(short, 0) -_(signed, 0) -_(sizeof, 0) -_(static, 0) -_(static_assert, STDC23) -_(struct, 0) -_(switch, 0) -_(thread_local, STDC23) -_(true, STDC23) -_(typedef, 0) -_(typeof, STDC23) -_(typeof_unqual, STDC23) -_(union, 0) -_(unsigned, 0) -_(void, 0) -_(volatile, 0) -_(while, 0) +_(_Alignas, STDC11) +_(_Alignof, STDC11) +_(_Atomic, STDC11) +_(_BitInt, STDC23) +_(_Bool, STDC99) +_(_Complex, STDC99) +_(_Decimal128, STDC23) +_(_Decimal32, STDC23) +_(_Decimal64, STDC23) +_(_Generic, STDC11) +_(_Imaginary, STDC99) +_(_Noreturn, STDC11) +_(_Static_assert, STDC11) +_(_Thread_local, STDC11) +_(__builtin_va_arg, 0) +_(__typeof__, 0) +_(alignas, STDC23) +_(alignof, STDC23) +_(auto, 0) +_(bool, STDC23) +_(break, 0) +_(case, 0) +_(char, 0) +_(const, 0) +_(constexpr, STDC23) +_(continue, 0) +_(default, 0) +_(do, 0) +_(double, 0) +_(else, 0) +_(enum, 0) +_(extern, 0) +_(false, STDC23) +_(float, 0) +_(for, 0) +_(goto, 0) +_(if, 0) +_(inline, STDC99) +_(int, 0) +_(long, 0) +_(nullptr, STDC23) +_(register, 0) +_(restrict, STDC99) +_(return, 0) +_(short, 0) +_(signed, 0) +_(sizeof, 0) +_(static, 0) +_(static_assert, STDC23) +_(struct, 0) +_(switch, 0) +_(thread_local, STDC23) +_(true, STDC23) +_(typedef, 0) +_(typeof, STDC23) +_(typeof_unqual, STDC23) +_(union, 0) +_(unsigned, 0) +_(void, 0) +_(volatile, 0) +_(while, 0) #ifndef TKWBEGIN_ # define TKWBEGIN_ TKW_Alignas @@ -67,5 +68,5 @@ _(while, 0) # define TKWEND_ TKWwhile #endif #ifndef TKWMAXLEN_ -# define TKWMAXLEN_ (sizeof "_Static_assert" - 1) +# define TKWMAXLEN_ (sizeof "__builtin_va_arg" - 1) #endif @@ -32,6 +32,7 @@ abiret(struct abiarg abiret[2], struct abiargsvec *abiargs, int *ni, union irtyp } for (int i = 0; i < retreg; ++i) { abiret[i].ty = cls2type(cls[i]); + abiret[i].isstk = 0; abiret[i].reg = r[i]; } return retreg; @@ -44,7 +45,7 @@ abiarg(struct abiargsvec *abiargs, int *ni, int *nf, int *ns, union irtype ty) uchar cls[2]; int ret = mctarg->abiarg(r, cls, ni, nf, ns, ty); if (!ret) { /* in stack */ - vpush(abiargs, ((struct abiarg) { ty, .stk = r[0] })); + vpush(abiargs, ((struct abiarg) { ty, .isstk = 1, .stk = r[0] })); } else if (ret == 1 && ty.isagg && cls[0] == KPTR) { /* aggregate by pointer */ vpush(abiargs, ((struct abiarg) { cls2type(cls[0]), .reg = r[0] })); } else { /* by regs */ @@ -59,7 +60,7 @@ static struct instr copyparam(struct function *fn, int *curi, int param, struct abiarg abi) { struct instr par = mkinstr(Oparam, abi.ty.cls, mkref(RICON, param), mktyperef(abi.ty)); - if (abi.reg >= 0) { /* reg */ + if (!abi.isstk) { /* reg */ assert(!abi.ty.isagg); return par; } else if (!abi.ty.isagg) { /* scalar in stack */ @@ -237,7 +238,6 @@ abi0_call(struct function *fn, struct instr *ins, struct block *blk, int *curi) struct call *call = &calltab.p[ins->r.i]; vararg = call->vararg; - vinit(&abiargs, abiargsbuf, arraylength(abiargsbuf)); ni = nf = ns = 0; assert(!ins->cls == !call->ret.bits); nret = abiret(call->abiret, &abiargs, &ni, call->ret); @@ -282,7 +282,7 @@ abi0_call(struct function *fn, struct instr *ins, struct block *blk, int *curi) ins->cls = 0; if (!nret) { /* hidden pointer argument */ ins->cls = 0; - if (call->abiret[0].reg >= 0) { + if (!call->abiret[0].isstk) { /* the result location pointer is also returned by the callee, e.g. in x86 */ ins->cls = KPTR; ++nret; @@ -401,11 +401,12 @@ abi0(struct function *fn) blk = fn->entry->lnext; do { - /* adjust calls */ + /* adjust vaargs and calls */ for (int iinstr = 0; iinstr < blk->ins.n; ++iinstr) { struct instr *ins = &instrtab[blk->ins.p[iinstr]]; - if (ins->op != Ocall) continue; - abi0_call(fn, ins, blk, &iinstr); + if (ins->op == Ovastart) mctarg->vastart(fn, blk, &iinstr); + else if (ins->op == Ovaarg) mctarg->vaarg(fn, blk, &iinstr); + else if (ins->op == Ocall) abi0_call(fn, ins, blk, &iinstr); } /* adjust returns */ @@ -432,6 +433,7 @@ abi0(struct function *fn) } } while ((blk = blk->lnext) != fn->entry); + sortrpo(fn); if (ccopt.dbg.a) { efmt("<< After abi0 >>\n"); irdump(fn); diff --git a/ir/builder.c b/ir/builder.c index 964099a..640c1fd 100644 --- a/ir/builder.c +++ b/ir/builder.c @@ -124,12 +124,12 @@ irunop(struct function *fn, enum op op, enum irclass k, union ref a) return addinstr(fn, mkinstr(op, k, a)); } -int newinstr(void); +int allocinstr(void); union ref addinstr(struct function *fn, struct instr ins) { - int new = newinstr(); + int new = allocinstr(); assert(fn->curblk != NULL); instrtab[new] = ins; adduse(fn->curblk, new, ins.l); @@ -168,8 +168,8 @@ addphi(struct function *fn, enum irclass cls, union ref *r) ins.l = mkref(RXXX, phitab.n-1); assert(fn->curblk != NULL); - assert(fn->curblk->ins.n == 0); - new = newinstr(); + /*assert(fn->curblk->ins.n == 0);*/ + new = allocinstr(); instrtab[new] = ins; for (int i = 0; i < fn->curblk->npred; ++i) { adduse(fn->curblk, new, r[i]); @@ -173,7 +173,7 @@ dumpinst(const struct instr *ins) efmt("\n"); } -static void +void dumpblk(struct function *fn, struct block *blk) { static const char *jnames[] = { 0, "b", "ret" }; @@ -228,7 +228,7 @@ irdump(struct function *fn) efmt("abi: ("); for (int i = 0; i < fn->nabiarg; ++i) { if (i > 0) efmt(", "); - if (fn->abiarg[i].reg >= 0) { + if (!fn->abiarg[i].isstk) { efmt("%s", mctarg->rnames[fn->abiarg[i].reg]); } else { prityp(fn->abiarg[i].ty); @@ -234,8 +234,7 @@ delpred(struct block *blk, struct block *p) struct block * newblk(struct function *fn) { - struct block *blk = alloc(fn->arena, sizeof(struct block), 0); - memset(blk, 0, sizeof *blk); + struct block *blk = allocz(fn->arena, sizeof(struct block), 0); blk->id = -1; return blk; } @@ -297,8 +296,36 @@ insertblk(struct function *fn, struct block *pred, struct block *subst) assert(0); } +struct block * +blksplitafter(struct function *fn, struct block *blk, int idx) +{ + struct block *new = newblk(fn); + ++fn->nblk; + new->lprev = blk; + new->lnext = blk->lnext; + blk->lnext = new; + new->lnext->lprev = new; + if (idx < blk->ins.n-1) + vpushn(&new->ins, &blk->ins.p[idx+1], blk->ins.n-idx-1); + blk->ins.n -= new->ins.n; + new->jmp = blk->jmp; + blk->jmp.t = Jb; + memset(blk->jmp.arg, 0, sizeof blk->jmp.arg); + for (int i = 0; i < 2; ++i) { + struct block *s = (&blk->s1)[i]; + for (int i = 0; i < s->npred; ++i) { + if (blkpred(s, i) == blk) + blkpred(s, i) = new; + } + } + new->s1 = blk->s1, new->s2 = blk->s2; + blk->s1 = new, blk->s2 = NULL; + addpred(new, blk); + return new; +} + int -newinstr(void) +allocinstr(void) { int t; if (instrfreelist != -1) { @@ -358,12 +385,22 @@ Shrink: return 1; } +int +newinstr(struct block *at, struct instr ins) +{ + int new = allocinstr(); + instrtab[new] = ins; + if (at) { + adduse(at, new, ins.l); + adduse(at, new, ins.r); + } + return new; +} + union ref insertinstr(struct block *blk, int idx, struct instr ins) { - int new = newinstr(); - - instrtab[new] = ins; + int new = newinstr(blk, ins); if (idx == blk->ins.n) vpush(&blk->ins, new); else { assert(idx >= 0 && idx < blk->ins.n); @@ -373,15 +410,13 @@ insertinstr(struct block *blk, int idx, struct instr ins) blk->ins.p[i] = blk->ins.p[i - 1]; blk->ins.p[idx] = new; } - adduse(blk, new, ins.l); - adduse(blk, new, ins.r); return mkref(RTMP, new); } union ref insertphi(struct block *blk, enum irclass cls) { - int new = newinstr(); + int new = allocinstr(); union ref *refs = NULL; assert(blk->npred > 0); xbgrowz(&refs, blk->npred); @@ -37,8 +37,8 @@ struct xcon { struct abiarg { union irtype ty; union { - short reg; /* >= 0 */ - short stk; /* < 0 */ + struct { ushort _ : 1, reg : 15; }; + struct { ushort isstk : 1, stk : 15; }; }; }; @@ -188,7 +188,8 @@ struct mctarg { * or negative SP offset if stack */ int (*abiarg)(short r[2], uchar cls[2], int *ni, int *nf, int *ns, union irtype); - + void (*vastart)(struct function *, struct block *, int *curi); + void (*vaarg)(struct function *, struct block *, int *curi); void (*isel)(struct function *); void (*emit)(struct function *); }; @@ -236,7 +237,9 @@ void addpred(struct block *blk, struct block *p); struct block *newblk(struct function *); void freeblk(struct function *, struct block *); struct block *insertblk(struct function *, struct block *pred, struct block *subst); +struct block *blksplitafter(struct function *, struct block *, int idx); void adduse(struct block *ublk, int ui, union ref r); +int newinstr(struct block *at, struct instr ins); union ref insertinstr(struct block *, int idx, struct instr); union ref insertphi(struct block *, enum irclass cls); void replcuses(union ref from, union ref to); @@ -69,6 +69,9 @@ _(call2r, 1) _(intrin, 2) _(phi, 1) _(swap, 2) +_(vastart, 1) +_(vaarg, 2) /* machine-specific instructions */ +_(xvaprologue, 1) _(xsave, 1) _(xrestore, 1) diff --git a/test/varargs.c b/test/varargs.c new file mode 100644 index 0000000..ff7d41d --- /dev/null +++ b/test/varargs.c @@ -0,0 +1,17 @@ +#include <stdarg.h> +#include <stdio.h> + +int sum(int x, ...) { + va_list ap; + va_start(ap, x); + for (int y; (y = va_arg(ap, int));) { + printf("got %d\n",y); + x += y; + } + va_end(ap); + return x; +} + +int main() { + printf("%d\n", sum(1,2,3,4,5,6,7,0,0)); +} |