aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile5
-rw-r--r--amd64/emit.c49
-rw-r--r--amd64/isel.c28
-rw-r--r--amd64/sysv.c120
-rw-r--r--c/builtin.c101
-rw-r--r--c/c.c252
-rw-r--r--c/c.h38
-rw-r--r--c/keywords.def123
-rw-r--r--ir/abi0.c16
-rw-r--r--ir/builder.c8
-rw-r--r--ir/dump.c4
-rw-r--r--ir/ir.c53
-rw-r--r--ir/ir.h9
-rw-r--r--ir/op.def3
-rw-r--r--test/varargs.c17
15 files changed, 620 insertions, 206 deletions
diff --git a/Makefile b/Makefile
index 2a39644..5af40ba 100644
--- a/Makefile
+++ b/Makefile
@@ -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: */
diff --git a/c/c.c b/c/c.c
index 415b584..3755261 100644
--- a/c/c.c
+++ b/c/c.c
@@ -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: */
diff --git a/c/c.h b/c/c.h
index 81c07f9..e8e797c 100644
--- a/c/c.h
+++ b/c/c.h
@@ -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
diff --git a/ir/abi0.c b/ir/abi0.c
index 404947e..dc479bf 100644
--- a/ir/abi0.c
+++ b/ir/abi0.c
@@ -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]);
diff --git a/ir/dump.c b/ir/dump.c
index f261150..2245d3f 100644
--- a/ir/dump.c
+++ b/ir/dump.c
@@ -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);
diff --git a/ir/ir.c b/ir/ir.c
index f3e6e89..0f716f9 100644
--- a/ir/ir.c
+++ b/ir/ir.c
@@ -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);
diff --git a/ir/ir.h b/ir/ir.h
index 66fc56d..22b920f 100644
--- a/ir/ir.h
+++ b/ir/ir.h
@@ -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);
diff --git a/ir/op.def b/ir/op.def
index 0c94d09..7bcaad1 100644
--- a/ir/op.def
+++ b/ir/op.def
@@ -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));
+}