diff options
| author | 2026-03-17 13:22:00 +0100 | |
|---|---|---|
| committer | 2026-03-17 13:22:00 +0100 | |
| commit | a8d6f8bf30c07edb775e56889f568ca20240bedf (patch) | |
| tree | b5a452b2675b2400f15013617291fe6061180bbf /c | |
| parent | 24f14b7ad1af08d872971d72ce089a529911f657 (diff) | |
REFACTOR: move sources to src/
Diffstat (limited to 'c')
| -rw-r--r-- | c/builtin.c | 177 | ||||
| -rw-r--r-- | c/c.c | 4772 | ||||
| -rw-r--r-- | c/c.h | 139 | ||||
| -rw-r--r-- | c/eval.c | 437 | ||||
| -rw-r--r-- | c/keywords.def | 76 | ||||
| -rw-r--r-- | c/lex.c | 2496 | ||||
| -rw-r--r-- | c/lex.h | 126 |
7 files changed, 0 insertions, 8223 deletions
diff --git a/c/builtin.c b/c/builtin.c deleted file mode 100644 index 5c59857..0000000 --- a/c/builtin.c +++ /dev/null @@ -1,177 +0,0 @@ -#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) { \ - union type par[] = { {{0}}, __VA_ARGS__ }; \ - ex->ty = retty; \ - return callcheck(&ex->span, countof(par)-1, par+1, ex->narg, ex->sub+1); \ - } - -/* __builtin_va_start */ -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(typedecay(ex->sub[1].ty).bits == typedecay(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; -} - -/* __builtin_va_end */ -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; -} - -/* __builtin_va_copy */ -DEF_FNLIKE_SEMA(va_copy, mktype(TYVOID), cvalistty, cvalistty) -static union ref -va_copy_comp(struct function *fn, struct expr *ex, bool discard) -{ - union irtype typ = mkirtype(cvalistty.t == TYARRAY ? typechild(cvalistty) : cvalistty); - for (int i = 1; i <= 2; ++i) - assert(typedecay(ex->sub[i].ty).bits == typedecay(cvalistty).bits); - union ref dst = compileexpr(fn, &ex->sub[1], 0), src = compileexpr(fn, &ex->sub[2], 0); - addinstr(fn, mkarginstr(typ, dst)); - addinstr(fn, mkarginstr(typ, src)); - addinstr(fn, mkintrin(INstructcopy, 0, 2)); - return NOREF; -} - -/* __builtin_trap */ -DEF_FNLIKE_SEMA(trap, mktype(TYVOID), ) -static union ref -trap_comp(struct function *fn, struct expr *ex, bool discard) -{ - puttrap(fn); - useblk(fn, newblk(fn)); /* unreachable block, but simplifies expr codegen */ - return NOREF; -} - -static inline union ref -cvtintref(struct function *fn, enum irclass dst, union ref src) -{ - if (src.t == RTMP) { - if (insrescls(instrtab[src.i]) != dst) - return addinstr(fn, mkinstr(Ocopy, dst, src)); - return src; - } else if (isintcon(src)) { - vlong x = intconval(src); - return mkintcon(dst, cls2siz[dst] == 4 ? (int)x : x); - } - assert(!"int ref?"); -} - -/* __builtin_bswap16 */ -DEF_FNLIKE_SEMA(bswap16, mktype(TYUSHORT), mktype(TYUSHORT)) -static union ref -bswap16_comp(struct function *fn, struct expr *ex, bool discard) -{ - assert(isint(ex->ty)); - return irunop(fn, Obswap16, KI32, scalarcvt(fn, ex->ty, ex->sub[1].ty, - compileexpr(fn, &ex->sub[1], 0))); -} -/* __builtin_bswap32 */ -DEF_FNLIKE_SEMA(bswap32, mktype(TYUINT), mktype(TYUINT)) -static union ref -bswap32_comp(struct function *fn, struct expr *ex, bool discard) -{ - assert(isint(ex->ty)); - return irunop(fn, Obswap32, KI32, scalarcvt(fn, ex->ty, ex->sub[1].ty, - compileexpr(fn, &ex->sub[1], 0))); -} -/* __builtin_bswap64 */ -DEF_FNLIKE_SEMA(bswap64, mktype(TYUVLONG), mktype(TYUVLONG)) -static union ref -bswap64_comp(struct function *fn, struct expr *ex, bool discard) -{ - assert(isint(ex->ty)); - return irunop(fn, Obswap64, KI64, scalarcvt(fn, ex->ty, ex->sub[1].ty, - compileexpr(fn, &ex->sub[1], 0))); -} - -#define LIST_BUILTINS(_) \ - _(va_start) _(va_copy) _(va_end) \ - _(trap) _(bswap16) _(bswap32) _(bswap64) - -static const struct { - const char *name; - struct builtin b; -} tab[] = { -#define FNS(x) { "__builtin_" #x, { x##_sema, x##_comp } }, - LIST_BUILTINS(FNS) -#undef FNS -}; - -void -putbuiltins(struct env *env) -{ - for (int i = 0; i < countof(tab); ++i) { - envadddecl(env, &(struct decl) { - .name = intern(tab[i].name), - .isbuiltin = 1, - .builtin = &tab[i].b, - }); - } -} - -/* this is separate because it's a keyword */ -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)))); -} - -bool -hasbuiltin(const char *name, uint len) -{ - for (int i = 0; i < countof(tab); ++i) - if (!strncmp(name, tab[i].name, len)) - return 1; - return 0; -} - -/* vim:set ts=3 sw=3 expandtab: */ @@ -1,4772 +0,0 @@ -#include "c.h" -#include "lex.h" -#include "../endian.h" -#include "../ir/ir.h" -#include "../obj/obj.h" - -/** Parsing helper functions **/ -#define peek(Cm,Tk) lexpeek((Cm)->lx,Tk) -static int -lexc(struct comp *cm, struct token *tk) -{ - struct token tk2, tk_[1]; - int t = lex(cm->lx, tk ? tk : 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); - if (tk) { - joinspan(&tk->span.ex, tk2.span.ex); - if (!tk->wide) - vpushn(&rest, tk2.s, tk2.len); - else if (tk->wide && targ_primsizes[targ_wchartype] == 2) - vpushn(&rest, tk2.ws16, tk2.len*2); - else - vpushn(&rest, tk2.ws32, tk2.len*4); - } - } while (peek(cm, &tk2) == TKSTRLIT && tk2.wide == tk->wide); - if (tk) { - if (!tk->wide) { - tk->s = memcpy(alloc(&cm->exarena, tk->len + rest.n, 1), tk->s, tk->len); - memcpy((char *)tk->s + tk->len, rest.p, rest.n); - tk->len += rest.n; - } else if (tk->wide == 1) { - tk->ws16 = memcpy(alloc(&cm->exarena, tk->len + rest.n*2, 2), tk->ws16, tk->len*2); - memcpy((short *)tk->s + tk->len, rest.p, rest.n); - tk->len += rest.n * 2; - } else { - tk->ws32 = memcpy(alloc(&cm->exarena, tk->len + rest.n*4, 4), tk->ws32, tk->len*4); - memcpy((int *)tk->s + tk->len, rest.p, rest.n); - tk->len += rest.n * 4; - } - } - vfree(&rest); - } - if (ccopt.pedant && in_range(t, TKWBEGIN_, TKWEND_) && (tk = tk ? tk : tk_)->extwarn) { - static struct bitset already[BSSIZE(TKWEND_-TKWBEGIN_+1)]; - if (!bstest(already, t-TKWBEGIN_)) { - bsset(already, t-TKWBEGIN_); - warn(&tk->span, "%'tk in %M is an extension", tk); - } - } - return t; -} -#define lex(Cm,Tk) lexc(Cm,Tk) -static bool -match(struct comp *cm, struct token *tk, enum toktag t) -{ - if (peek(cm, NULL) == t) { - lex(cm, tk); - return 1; - } - return 0; -} -static bool -expect(struct comp *cm, enum toktag t, const char *s) -{ - struct token tk; - if (!match(cm, &tk, t)) { - peek(cm, &tk); - if (aisprint(t)) tk.span.ex.len = tk.span.sl.len = 1; - error(&tk.span, "expected %'tt%s%s", t, s?" ":"",s ? s : ""); - return 0; - } - return 1; -} - -/******************************************/ -/* Data structures for declaration parser */ -/******************************************/ - -enum declkind { - DTOPLEVEL, - DFUNCPARAM, - DFUNCVAR, - DFIELD, - DCASTEXPR, -}; - -/* Since a declaration can have multiple declarators, and we need to process - * each one individually, the declaration parser is a state machine - * (conceptually a generator coroutine); the state is zero-initialized (except - * for the .kind field), each call to pdecl yields the next individual decl, - * st.more indicates whether there are more decls left to parse (the coroutine - * has yielded), or this declaration list is done (the coroutine has finalized) - */ -struct declstate { - enum declkind kind; - union type base; - uchar scls; - uchar qual; - bool fnnoreturn : 1, - fninline : 1; - uint align; - bool base0, /* caller set initial base type, but there may be declspecs to parse */ - more, /* caller should keep calling pdecl to get next decl */ - varini, /* caller should parse an initializer ('=' <ini>) and - call pdecl() to advance state before checking .more */ - funcdef, /* caller should parse an func definition ('{' <body> '}'). - the declaration list is finished. */ - bitf, /* caller should parse a bitfield size and - call pdecl() to advance state before checking .more */ - tagdecl, /* declarator is a tagged type */ - empty; /* nothing decl (';') */ - internstr *pnames; /* param names for function definition */ - struct span *pspans; /* param spans ditto */ - uchar *pqual; /* param quals ditto */ - int attr; -}; -static struct decl pdecl(struct declstate *st, struct comp *cm); - -static struct decl *finddecl(struct comp *cm, internstr name); - -/* next token starts a decl? */ -static bool -isdecltok(struct comp *cm) -{ - struct token tk; - if (peek(cm, &tk) == TKIDENT) { - struct decl *decl = finddecl(cm, tk.name); - return decl && decl->scls == SCTYPEDEF; - } else { - static const bool kws[] = { -#define kw(x) [TKW##x-TKWBEGIN_] = 1 - kw(auto), kw(extern), kw(static), kw(register), kw(typedef), - kw(_Thread_local), kw(thread_local), kw(_Static_assert), - kw(inline), kw(_Noreturn), - kw(const), kw(volatile), kw(restrict), kw(_Atomic), - kw(void), kw(float), kw(double), kw(_Complex), - kw(signed), kw(unsigned), kw(short), kw(long), - kw(int), kw(char), kw(_Bool), kw(bool), - kw(struct), kw(union), kw(enum), - kw(__typeof__), kw(typeof), kw(typeof_unqual), - kw(__attribute__) -#undef kw - }; - return ((uint)tk.t-TKWBEGIN_) < countof(kws) && kws[tk.t-TKWBEGIN_]; - } -} - -/* next token starts an expr? */ -static bool -isexprtok(struct comp *cm) -{ - struct token tk; - if (peek(cm, &tk) == TKIDENT) { - struct decl *decl = finddecl(cm, tk.name); - return !decl || decl->scls != SCTYPEDEF; - } else { - static const bool tks[] = { -#define tk(x) [x] = 1 - tk('+'), tk('-'), tk('*'), tk('&'), tk('~'), tk('!'), tk(TKINC), tk(TKDEC), - tk(TKWsizeof), tk(TKW_Alignof), tk(TKWalignof), tk(TKWtrue), tk(TKWfalse), - tk('('), tk(TKNUMLIT), tk(TKCHRLIT), tk(TKSTRLIT), tk(TKW_Generic) -#undef tk - }; - return tk.t < countof(tks) && tks[tk.t]; - } -} - -/**********************************/ -/* Environment (scope) management */ -/**********************************/ - -struct envdecls declsbuf; -struct tagged { /* a tagged type declaration */ - union type ty; - struct span span; -}; -static struct tagged envtaggedbuf[1<<7]; -static vec_of(struct tagged) envtagged = VINIT(envtaggedbuf, countof(envtaggedbuf)); -struct env { - struct env *up; - /* list of decls is implicitly envdecls[decl..ndecl] */ - ushort decl, ndecl; - /* ditto for envtagged[] */ - ushort tagged, ntagged; -}; -/* use a hashmap for lookups of top-level declarations, since there's usually many of those */ -static pmap_of(ushort) tldeclmap; - -static void -envdown(struct comp *cm, struct env *e) -{ - assert(cm->env->decl + cm->env->ndecl == declsbuf.n); - assert(cm->env->tagged + cm->env->ntagged == envtagged.n); - e->decl = declsbuf.n; - e->tagged = envtagged.n; - e->ndecl = e->ntagged = 0; - e->up = cm->env; - cm->env = e; -} - -static void -envup(struct comp *cm) -{ - struct env *env = cm->env; - assert(env->decl + env->ndecl == declsbuf.n); - declsbuf.n -= env->ndecl; - envtagged.n -= env->ntagged; - assert(env->up); - cm->env = env->up; -} - -int -envadddecl(struct env *env, const struct decl *d) -{ - assert(env->decl + env->ndecl == declsbuf.n); - vpush(&declsbuf, *d); - assert(declsbuf.n < 1<<16); - ++env->ndecl; - if (!env->up) pmap_set(&tldeclmap, d->name, declsbuf.n-1); - return declsbuf.n - 1; -} - -/* iters in reversed order of insertion (most to least recent) */ -/* use like so: for (d = NULL; enviterdecl(&d, env);) ... */ -static inline bool -enviterdecl(struct decl **d, struct env *env) -{ - if (!env->ndecl) return 0; - if (!*d) *d = &declsbuf.p[env->decl + env->ndecl - 1]; - else if (*d == &declsbuf.p[env->decl]) return 0; - else --*d; - return 1; -} - -static struct tagged * -envaddtagged(struct env *env, union type ty, const struct span *span) -{ - struct tagged tagged = { ty, *span }; - assert(env->tagged + env->ntagged == envtagged.n); - vpush(&envtagged, tagged); - assert(envtagged.n < 1<<16); - ++env->ntagged; - return &envtagged.p[envtagged.n - 1]; -} - -/* like enviterdecl */ -static inline bool -envitertagged(struct tagged **l, struct env *env) -{ - if (!env->ntagged) return 0; - if (!*l) *l = &envtagged.p[env->tagged + env->ntagged - 1]; - else if (*l == &envtagged.p[env->tagged]) return 0; - else --*l; - return 1; -} - -static bool -redeclarationok(const struct decl *old, const struct decl *new) -{ - bool takeoldscls = 0; - if (old->scls != new->scls) { - if (old->scls == SCSTATIC && (new->scls &~ SCEXTERN) == SCNONE && old->ty.t == TYFUNC && new->ty.t == TYFUNC) - takeoldscls = 1; - else - return 0; - } - switch (old->scls) { - case SCSTATIC: - case SCEXTERN: - if (old->ty.bits == new->ty.bits) goto OkFuncs; - if (old->ty.t != new->ty.t) return 0; - if (old->ty.t == TYARRAY /* allow 'int x[]; int x[100];' */ - && typechild(old->ty).bits == typechild(new->ty).bits - && (isincomplete(old->ty) || isincomplete(new->ty))) - { - return 1; - } - if (old->ty.t == TYFUNC /* allow 'int f(); int f(int);' (some K&R) */ - && typedata[old->ty.dat].ret.bits == typedata[new->ty.dat].ret.bits - && (typedata[old->ty.dat].kandr || typedata[new->ty.dat].kandr)) - { OkFuncs: - if (takeoldscls) ((struct decl *)new)->scls = old->scls; - return 1; - } - return 0; - case SCTYPEDEF: - return old->ty.bits == new->ty.bits; - } - return 0; -} - -static int -putdecl(struct comp *cm, const struct decl *decl) -{ - for (struct env *env = cm->env; env; env = env->up) { - struct decl *l; - if (!env->up) { - ushort *pi = pmap_get(&tldeclmap, decl->name); - if (pi) { - l = &declsbuf.p[*pi]; - goto Match; - } - } else for (l = NULL; enviterdecl(&l, env);) { - if (decl->name == l->name) { - Match: - if ((cm->env->up != NULL && decl->scls == SCSTATIC) || (l->isdef && decl->isdef)) { - error(&decl->span, "redefinition of '%s'", decl->name); - note(&l->span, "previously defined here"); - break; - } else if (!redeclarationok(l, decl)) { - error(&decl->span, "incompatible redeclaration of '%s'", decl->name); - note(&l->span, "previously declared here"); - break; - } - if (l->isdef && !decl->isdef) return l - declsbuf.p; - break; - } - } - if (decl->scls != SCEXTERN) break; - } - return envadddecl(cm->env, decl); -} - -static struct decl * -finddecl(struct comp *cm, internstr name) -{ - assert(name); - for (struct env *e = cm->env; e; e = e->up) { - if (!e->up) { - ushort *pi = pmap_get(&tldeclmap, name); - if (pi) return &declsbuf.p[*pi]; - } else for (struct decl *l = NULL; enviterdecl(&l, e);) { - if (name == l->name) - return l; - } - } - return NULL; -} - -static union type -gettagged(struct comp *cm, struct span *span, enum typetag tt, internstr name, bool dodef) -{ - struct typedata td = {0}; - assert(name); - 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; - *span = l->span; - return l->ty; - } - } - } - if (tt == TYENUM && ccopt.pedant) { - warn(span, "forward-declared enum is an extension"); - } -Break2: - td.t = tt; - return envaddtagged(cm->env, mktagtype(name, &td), span)->ty; -} - -static union type -deftagged(struct comp *cm, struct span *span, enum typetag tt, internstr name, union type ty) -{ - struct typedata td = {0}; - assert(name); - for (struct tagged *l = NULL; envitertagged(&l, cm->env);) { - if (name == ttypenames[typedata[l->ty.dat].id]) { - *span = l->span; - return l->ty; - } - } - td.t = tt; - return envaddtagged(cm->env, ty.t ? ty : mktagtype(name, &td), span)->ty; -} - -/*********************/ -/* Expr Typechecking */ -/*********************/ - -#define iszero(ex) ((ex).t == ENUMLIT && isint((ex).ty) && (ex).u == 0) - -static bool -islvalue(const struct expr *ex) -{ - if (ex->t == EGETF) return islvalue(ex->sub); - return ex->t == ESYM || ex->t == EDEREF || ex->t == EINIT || ex->t == ESTRLIT; -} - -static union type /* 6.5.2.6 default argument promotions */ -argpromote(union type t) -{ - if (isint(t)) t.t = intpromote(t.t); - else if (t.t == TYFLOAT) t.t = TYDOUBLE; - else if (t.t == TYARRAY) return mkptrtype(typechild(t), t.flag & TFCHLDQUAL); - else if (t.t == TYFUNC) return mkptrtype(t, 0); - return t; -} - -bool -assigncheck(union type t, const struct expr *src) -{ - union type srcty = typedecay(src->ty);; - if (assigncompat(t, srcty)) { - if (t.t == TYPTR && srcty.t == TYPTR - && (t.flag & TFCHLDQUAL & srcty.flag & TFCHLDQUAL) != (srcty.flag & TFCHLDQUAL)) { - warn(&src->span, "usage of '%ty' discards pointer qualifiers", src->ty); - } - return 1; - } else if (t.t == TYPTR && srcty.t == TYPTR) { - warn(&src->span, "converting between incompatible pointer types ('%ty' -> '%ty')", srcty, t); - return 1; - } else if (t.t == TYPTR && iszero(*src)) return 1; - return 0; -} - -static bool -initcheck(union type t, const struct expr *src) -{ - if (assigncheck(t, src)) return 1; - if (t.bits == src->ty.bits && (src->t == EINIT || src->t == ESTRLIT)) return 1; - return 0; -} - -static void -incdeccheck(enum toktag tt, const struct expr *ex, const struct span *span) -{ - if (!isscalar(ex->ty)) - error(&ex->span, "invalid operand to %tt '%ty'", tt, ex->ty); - else if (!islvalue(ex)) - error(&ex->span, "operand to %tt is not an lvalue", tt); - else if (ex->ty.t == TYPTR && isincomplete(typechild(ex->ty))) - error(span, "arithmetic on pointer to incomplete type '%ty'", ex->ty); - else if (ex->ty.t == TYPTR && typechild(ex->ty).t == TYFUNC) - error(span, "arithmetic on function pointer '%ty'", ex->ty); -} - -static bool /* 6.5.4 Cast operators */ -castcheck(union type to, const struct expr *ex) -{ - union type src = ex->ty; - if (to.t == TYVOID) return 1; - if (isagg(to)) return 0; - if (to.bits == src.bits) return 1; - if (isarith(to) && isarith(src)) return 1; - if (isint(to) && isptrcvt(src)) return 1; - if (to.t == TYPTR && isint(src)) return 1; - if (to.t == TYPTR && isptrcvt(src)) return 1; - return 0; -} - -static union type /* 6.5.2.1 Array subscripting */ -subscriptcheck(const struct expr *ex, const struct expr *rhs, const struct span *span) -{ - union type ty; - if (ex->ty.t == TYPTR || ex->ty.t == TYARRAY) { - if (isincomplete(typedecay(ty = typechild(ex->ty)))) { - error(span, "cannot dereference pointer to incomplete type '%ty'", ty); - ty = mktype(TYINT); - } else if (ty.t == TYFUNC) { - error(span, "subscripted value is pointer to function"); - ty = mktype(TYINT); - } - } else { - error(&ex->span, "subscripted value is not pointer-convertible '%ty'", ex->ty); - ty = mktype(TYINT); - } - if (!isint(rhs->ty)) - error(&rhs->span, "array subscript is not integer ('%ty')", rhs->ty); - return ty; -} - -static uint /* 6.5.3.4 The sizeof and _Alignof operators */ -sizeofalignofcheck(const struct span *span, enum toktag tt, union type ty, const struct expr *ex) -{ - uint r = (tt == TKWsizeof ? typesize : typealign)(ty); - if (ty.t == TYVOID) { - if (ccopt.pedant) warn(span, "applying %'tt to void type", tt); - r = 1; - } else if (isincomplete(ty)) { - error(span, "cannot apply %'tt to incomplete type '%ty'", tt, ty); - } else if (ty.t == TYFUNC) { - error(span, "cannot apply %'tt to function type '%ty'", tt, ty); - } else if (tt == TKWsizeof && ex && ex->t == EGETF && ex->fld.bitsiz) { - error(span, "cannot apply %'tt to bitfield", tt); - } - if (tt != TKWsizeof && ex && ccopt.pedant) - warn(span, "%'tt applied to an expression is a GNU extension", tt); - return r; -} - -static bool /* 6.5.8 Relational operators */ -relationalcheck(const struct expr *a, const struct expr *b) -{ - union type t1 = a->ty, t2 = b->ty; - if (isarith(t1) && isarith(t2)) return 1; - if (isptrcvt(t1) && isptrcvt(t2)) { - t1 = typedecay(t1); - t2 = typedecay(t2); - return t1.dat == t2.dat; - } - return 0; -} - -static bool -isnullpo(const struct expr *ex) /* match '0' or '(void *) 0' */ -{ - static const union type voidptr = {{ TYPTR, .flag = TFCHLDPRIM, .child = TYVOID }}; - while (ex->t == ECAST && ex->ty.bits == voidptr.bits) - ex = ex->sub; - if (iszero(*ex)) return 1; - return eval((struct expr *)ex, EVINTCONST) /* GNU extension. should we warn? */ - && iszero(*ex); -} - -static bool /* 6.5.9 Equality operators */ -equalitycheck(const struct expr *a, const struct expr *b) -{ - union type t1 = a->ty, t2 = b->ty; - if (isarith(t1) && isarith(t2)) return 1; - if (isptrcvt(t1) && isptrcvt(t2)) { - t1 = typedecay(t1), t2 = typedecay(t2); - /* comparing .dat works for both TFCHLDPRIM and not, (checks equal child types) - * quals are ignored either way */ - return t1.dat == t2.dat || typechild(t1).t == TYVOID || typechild(t2).t == TYVOID; - } - return (isptrcvt(t1) && isnullpo(b)) || (isptrcvt(t2) && isnullpo(a)); -} - -static union type /* 6.5.15 Conditional operator */ -condtype(const struct expr *a, const struct expr *b) -{ - union type t1 = typedecay(a->ty), t2 = typedecay(b->ty), s1, s2; - if (isarith(t1) && isarith(t2)) return cvtarith(t1, t2); - if (t1.bits == t2.bits) return t1; - if (t1.t == TYPTR && isnullpo(b)) return t1; - if (isnullpo(a) && t2.t == TYPTR) return t2; - if (t1.t == TYPTR && t2.t == TYPTR) { - s1 = typechild(t1), s2 = typechild(t2); - if (s1.bits == s2.bits || s2.t == TYVOID || s1.t == TYVOID) { - return mkptrtype(s1.t == TYVOID ? s1 : s2, (t1.flag | t2.flag) & TFCHLDQUAL); - } - } - return mktype(0); -} - -static void -bintypeerr(const struct span *span, enum toktag tt, union type lhs, union type rhs) -{ - error(span, "bad operands to %tt ('%ty', '%ty')", tt, lhs, rhs); -} - -enum binopclass { /* binary operator type-checking classes */ - BCSET = 1<<7, /* is a (compound) assignment operator? */ - BCSEQ = 1, BCADDITIVE, BCARITH, BCINT, BCSHFT, BCEQL, BCCMP, BCLOG, -}; - -/* table indexed by binary op token; - * containing precedence level, expression kind and type-checking class */ -static const struct { uchar prec, t, k; } bintab[] = { - ['*'] = {13, EMUL, BCARITH}, - ['/'] = {13, EDIV, BCARITH}, - ['%'] = {13, EREM, BCINT}, - ['+'] = {12, EADD, BCADDITIVE}, - ['-'] = {12, ESUB, BCADDITIVE}, - [TKSHL] = {11, ESHL, BCSHFT}, - [TKSHR] = {11, ESHR, BCSHFT}, - ['<'] = {10, ELTH, BCCMP}, - ['>'] = {10, EGTH, BCCMP}, - [TKLTE] = {10, ELTE, BCCMP}, - [TKGTE] = {10, EGTE, BCCMP}, - [TKEQU] = {9, EEQU, BCEQL}, - [TKNEQ] = {9, ENEQ, BCEQL}, - ['&'] = {8, EBAND, BCINT}, - ['^'] = {7, EXOR, BCINT}, - ['|'] = {6, EBIOR, BCINT}, - [TKLOGAND] = {5, ELOGAND, BCLOG}, - [TKLOGIOR] = {4, ELOGIOR, BCLOG}, - ['?'] = {3, ECOND}, /* not actually a binop (special cased) */ - ['='] = {2, ESET, BCSET}, - [TKSETADD] = {2, ESETADD, BCSET|BCADDITIVE}, [TKSETSUB] = {2, ESETSUB, BCSET|BCADDITIVE}, - [TKSETMUL] = {2, ESETMUL, BCSET|BCARITH}, [TKSETDIV] = {2, ESETDIV, BCSET|BCARITH}, - [TKSETREM] = {2, ESETREM, BCSET|BCINT}, [TKSETAND] = {2, ESETAND, BCSET|BCINT}, - [TKSETIOR] = {2, ESETIOR, BCSET|BCINT}, [TKSETXOR] = {2, ESETXOR, BCSET|BCINT}, - [TKSETSHL] = {2, ESETSHL, BCSET|BCSHFT}, [TKSETSHR] = {2, ESETSHR, BCSET|BCSHFT}, - [','] = {1, ESEQ, BCSEQ} -}; - -static union type -bintypecheck(const struct span *span, enum toktag tt, struct expr *lhs, struct expr *rhs) -{ - enum binopclass k = bintab[tt].k; - union type ty = lhs->ty; - - assert(k); - if (k & BCSET) { - if (!islvalue(lhs)) - error(&lhs->span, "left-hand-side of assignment is not an lvalue"); - else if (lhs->qual & QCONST) - error(&lhs->span, "cannot assign to const-qualified lvalue (%tq)", ty, lhs->qual); - else if (isincomplete(ty)) - error(&lhs->span, "cannot assign to incomplete type '%ty'", ty); - else if (ty.t == TYARRAY) - error(&lhs->span, "cannot assign to array type '%ty'", ty); - else if (ty.t == TYFUNC) - error(&lhs->span, "cannot assign to function designator '%ty'", lhs->ty); - } - switch (k &~ BCSET) { - case 0: - if (isagg(ty) && !(lhs->qual & QCONST) && typedata[ty.dat].anyconst) - error(&lhs->span, "cannot assign to aggregate with const-qualified member"); - if (!assigncheck(ty, rhs)) - goto Error; - break; - case BCSEQ: - ty = rhs->ty; - break; - case BCADDITIVE: - if (tt == '+' && isptrcvt(rhs->ty)) { - /* int + ptr -> ptr + int (for convenience) */ - const struct expr swaptmp = *lhs; - *lhs = *rhs; - *rhs = swaptmp; - ty = lhs->ty; - } - if (isarith(ty) && isarith(rhs->ty)) { - /* num +/- num */ - ty = cvtarith(ty, rhs->ty); - assert(ty.t); - } else if ((ty.t == TYPTR || ty.t == TYARRAY) && isint(rhs->ty)) { - /* ptr +/- int */ - union type pointee = typechild(ty); - if (isincomplete(pointee)) - error(span, "arithmetic on pointer to incomplete type '%ty'", ty); - else if (pointee.t == TYFUNC) - error(span, "arithmetic on function pointer '%ty'", ty); - ty = typedecay(ty); - } else if (tt == '-' && isptrcvt(ty) && isptrcvt(rhs->ty)) { - /* ptr - ptr */ - union type pointee1 = typechild(typedecay(ty)), - pointee2 = typechild(typedecay(rhs->ty)); - if (isincomplete(pointee1)) - error(span, "arithmetic on pointer to incomplete type '%ty'", ty); - else if (pointee1.t == TYFUNC) - error(span, "arithmetic on function pointer '%ty'", lhs->ty); - else if (pointee1.bits != pointee2.bits) { - error(span, "arithmetic on incompatible pointer types: '%ty', '%ty'", - ty, rhs->ty); - } - ty = mktype(targ_ptrdifftype); - } else goto Error; - break; - case BCARITH: - ty = cvtarith(ty, rhs->ty); - if (!ty.t) { - ty.t = TYINT; - Error: - bintypeerr(span, tt, lhs->ty, rhs->ty); - } - break; - case BCINT: - if (!isint(ty) || !isint(rhs->ty)) - goto Error; - ty = cvtarith(ty, rhs->ty); - assert(ty.t); - break; - case BCSHFT: /* 6.5.7 Bitwise shift operators */ - if (!isint(ty) || !isint(rhs->ty)) { - ty = mktype(TYINT); - goto Error; - } - ty.t = intpromote(ty.t); - assert(ty.t); - break; - case BCEQL: - ty = mktype(TYINT); - if (!equalitycheck(lhs, rhs)) { - if (isptrcvt(lhs->ty) && isptrcvt(rhs->ty)) - warn(span, "comparison of distinct pointer types ('%ty' and '%ty')", lhs->ty, rhs->ty); - else - goto Error; - } - break; - case BCCMP: - ty = mktype(TYINT); - if (!relationalcheck(lhs, rhs)) { - if (isptrcvt(lhs->ty) && isptrcvt(rhs->ty)) - warn(span, "comparison of distinct pointer types ('%ty' and '%ty')", lhs->ty, rhs->ty); - else - goto Error; - } - break; - case BCLOG: /* 6.5.13-14 Logical AND/OR operator */ - ty = mktype(TYINT); - if (!isscalar(typedecay(ty)) || !isscalar(typedecay(rhs->ty))) - goto Error; - break; - } - return (k & BCSET) || !ty.t ? lhs->ty : ty; -} - -/****************/ -/* Expr Parsing */ -/****************/ - -#define mkexpr(t_,span_,ty_,...) ((struct expr){.t=(t_), .ty=(ty_), .span=(span_), __VA_ARGS__}) - -static struct expr * -exprdup(struct comp *cm, const struct expr *e) -{ - return alloccopy(&cm->exarena, e, sizeof *e, 0); -} -static struct expr * -exprdup2(struct comp *cm, const struct expr *e1, const struct expr *e2) -{ - struct expr *r = alloc(&cm->exarena, 2*sizeof *r, 0); - r[0] = *e1, r[1] = *e2; - return r; -} - -static struct expr expr(struct comp *cm); -static struct expr commaexpr(struct comp *cm); - -enum { IMPLICITSYMTY = 0xFF, }; - -static struct expr /* 6.5.2.2 Function calls */ -callexpr(struct comp *cm, const struct span *span_, const struct expr *callee) -{ - struct token tk; - struct expr ex, arg; - struct span span = callee->span; - union type ty = callee->ty; - const struct typedata *td = NULL; - struct expr argbuf[10]; - vec_of(struct expr) args = VINIT(argbuf, countof(argbuf)); - bool spanok = joinspan(&span.ex, span_->ex); - bool printsig = 0; - const struct builtin *builtin = NULL; - - if (callee->t == ESYM && !callee->ty.t && declsbuf.p[callee->decl].isbuiltin) { - builtin = declsbuf.p[callee->decl].builtin; - assert(!ty.t); - } - - if (callee->t == ESYM && ty.t == IMPLICITSYMTY) { /* implicit function decl.. */ - internstr name = callee->implicitsym; - struct decl decl = { - (ty = mkfntype(mktype(TYINT), 0, NULL, /* kandr */ 1, 0)), - .scls = SCEXTERN, .span = span, .name = name, .sym = name - }; - warn(&span, "call to undeclared function '%s'", name); - ((struct expr *)callee)->ty = decl.ty; - ((struct expr *)callee)->decl = putdecl(cm, &decl); - } - - if (!builtin) { - if (ty.t == TYPTR) /* auto-deref when calling a function pointer */ - ty = typechild(ty); - if (ty.t != TYFUNC) - error(&span, "calling a value of type '%ty'", callee->ty); - else - td = &typedata[ty.dat]; - if (ty.t == TYFUNC && td->ret.t != TYVOID && isincomplete(td->ret)) - error(&span, "cannot call function with incomplete return type '%ty'", td->ret); - } - - if (!match(cm, &tk, ')')) for (;;) { - arg = expr(cm); - spanok = spanok && joinspan(&span.ex, callee->span.ex); - if (td && args.n == td->nmemb && !td->variadic && !td->kandr) { - error(&arg.span, "too many args to function taking %d params", td->nmemb); - printsig = 1; - } - if (arg.ty.t == TYVOID) { - error(&arg.span, "invalid use of void expression"); - } else if (td && args.n < td->nmemb && !td->kandr) { - if (!assigncheck(td->param[args.n], &arg)) { - error(&arg.span, "arg #%d of type '%ty' is incompatible with '%ty'", - args.n+1, arg.ty, td->param[args.n]); - printsig = 1; - } - } - vpush(&args, arg); - peek(cm, &tk); - if (match(cm, &tk, ',')) { - spanok = spanok && joinspan(&span.ex, tk.span.ex); - } else if (expect(cm, ')', "or ',' after arg")) { - break; - } - } - if (!spanok || !joinspan(&span.ex, tk.span.ex)) span = *span_; - - if (td && !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; - } - if (printsig) note(&callee->span, "function signature is '%ty'", ty); - - ex = mkexpr(ECALL, span, ty.t == TYFUNC ? td->ret : ty, .narg = args.n, - .sub = alloc(&cm->exarena, (args.n+1)*sizeof(struct expr), 0)); - 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; -} - -static void -ppostfixopers(struct comp *cm, struct expr *ex) -{ - struct expr tmp, rhs; - struct token tk, tk2; - struct span span; - union type ty; - - for (;;) switch (peek(cm, &tk)) { - default: return; - case TKINC: - case TKDEC: - lex(cm, &tk); - span = ex->span; - if (!joinspan(&span.ex, tk.span.ex)) span = tk.span; - incdeccheck(tk.t, ex, &span); - *ex = mkexpr(tk.t == TKINC ? EPOSTINC : EPOSTDEC, span, ex->ty, .sub = exprdup(cm, ex)); - continue; - case '[': /* a[subscript] */ - lex(cm, NULL); - rhs = commaexpr(cm); - span = ex->span; - if (!joinspan(&span.ex, tk.span.ex) || !joinspan(&span.ex, ex->span.ex) - || (peek(cm, &tk), !joinspan(&span.ex, tk.span.ex))) - span = tk.span; - expect(cm, ']', NULL); - - if (isint(ex->ty) && isptrcvt(rhs.ty)) { - /* swap idx[ptr] -> ptr[idx] */ - tmp = *ex; - *ex = rhs; - rhs = tmp; - } - - ty = subscriptcheck(ex, &rhs, &span); - assert(ty.t); - if (!iszero(rhs)) { - tmp.sub = exprdup2(cm, ex, &rhs); - tmp.t = EADD; - tmp.span = span; - tmp.ty = typedecay(ex->ty); - } - tmp.sub = exprdup(cm, iszero(rhs) ? ex : &tmp); - tmp.span = span; - tmp.t = EDEREF; - tmp.qual = ex->ty.flag & TFCHLDQUAL; - tmp.ty = ty; - *ex = tmp; - continue; - case '(': /* call(args) */ - lex(cm, &tk); - span = ex->span; - *ex = callexpr(cm, &span, ex); - continue; - case TKARROW: - if (ex->ty.t != TYPTR && ex->ty.t != TYARRAY) - error(&ex->span, "operand to -> is not a pointer: '%ty'", ex->ty); - else - *ex = mkexpr(EDEREF, ex->span, typechild(ex->ty), .qual = ex->ty.flag & TFCHLDQUAL, - .sub = exprdup(cm, ex)); - /* fallthru */ - case '.': - lex(cm, &tk); - span = ex->span; - peek(cm, &tk2); /* field name */ - if (!expect(cm, TKIDENT, NULL)) tk2.s = ""; - if (!joinspan(&span.ex, tk.span.ex) || !joinspan(&span.ex, tk2.span.ex)) - span = tk.span; - if (!isagg(ex->ty)) { - error(&span, "member access operand is not an aggregate: '%ty'%s", ex->ty, - ex->ty.t == TYPTR && isagg(typechild(ex->ty)) ? "; did you mean to use '->'?" : ""); - } else { - struct fielddata fld = {.t = mktype(TYINT)}; - if (*tk2.s && !getfield(&fld, ex->ty, tk2.name)) - error(&span, "'%ty' has no such field: '%s'", ex->ty, tk2.name); - if (ex->t == EGETF && ex->qual == fld.qual) { /* accumulate */ - ex->span = span; - ex->ty = fld.t; - ex->fld.off += fld.off; - ex->fld.bitoff = fld.bitoff; - ex->fld.bitsiz = fld.bitsiz; - } else { - *ex = mkexpr(EGETF, span, fld.t, .qual = ex->qual | fld.qual, .sub = exprdup(cm, ex), - .fld = { fld.off, fld.bitsiz, fld.bitoff }); - } - } - continue; - } -} - -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 struct expr -genericexpr(struct comp *cm, struct span *span) -{ - struct token tk; - if (expect(cm, '(', "after _Generic")) { - struct expr control = expr(cm), dfault = {0}, ex = {0}; - expect(cm, ',', NULL); - for (;;) { - if (match(cm, &tk, TKWdefault)) { - expect(cm, ':', NULL); - if (dfault.t) { - error(&tk.span, "duplicate 'default' specifier in generic selection expression"); - (void)expr(cm); - } else { - dfault = expr(cm); - } - } else { - struct decl decl = pdecl(&(struct declstate){DCASTEXPR}, cm); - union type ty = decl.ty; - expect(cm, ':', NULL); - if (!ex.t && - (typedecay(ty).bits == typedecay(control.ty).bits - || ((ty.t == TYENUM || control.ty.t == TYENUM) && scalartypet(ty) == scalartypet(control.ty)))) - ex = expr(cm); - else - (void)expr(cm); - } - if (match(cm, &tk, ')')) break; - else if (!expect(cm, ',', "or `)'")) { - if (!isdecltok(cm)) { - peek(cm, &tk); /* want the span */ - break; - } - } - } - if (!ex.t) ex = dfault; - if (!ex.t) { - error(&control.span, - "controlling type '%ty' not compatible with any generic association type", - control.ty); - ex.ty.t = TYINT; - } - ex.span = *span; - joinspan(&ex.span.ex, tk.span.ex); - return ex; - } - return mkexpr(ENUMLIT,*span,mktype(TYINT),); -} - -static inline int -tkprec(int tt) -{ - return ((uint)tt < countof(bintab)) ? bintab[tt].prec : 0; -} - -static struct expr initializer(struct comp *cm, union type *ty, enum evalmode ev, - bool globl, enum qualifier qual, internstr name); - -static internstr istr__func__, istr_main, istr_memset; - -static internstr mkhiddensym(const char *fnname, const char *name, int id); - -/* parse an expression with the given operator precedence */ -/* param ident is a kludge to support block labels without backtracking or extra lookahead - * see stmt() */ -enum exprctx { EFROMSTMT = 1, EARRAYCOUNT, EATTRARG }; -static struct expr -exprparse(struct comp *cm, int prec, const struct token *ident, enum exprctx ctx) -{ - struct token tk; - struct span span; - struct expr ex; - union type ty; - struct { - struct span span; - union { - union type ty; /* cast type */ - struct { - uchar t0; /* t == 0 */ - short tt; /* token */ - }; - }; - } unops[4]; - int nunop = 0; - - if (ident) { - assert(ident->t == TKIDENT); - tk = *ident; - ident = NULL; - goto Ident; - } - -Unary: - switch (lex(cm, &tk)) { - /* unary operators (gather) */ - case '*': - if (ctx == EARRAYCOUNT && peek(cm, NULL) == ']') { - /* kludge for C99 `int x[*]` (unk VLA size) */ - return (struct expr) { 0, .span = tk.span }; - } - /* fallthru */ - case '+': case '-': case '~': case '!': - case '&': case TKINC: case TKDEC: - Unops: - unops[nunop].span = tk.span; - unops[nunop].t0 = 0; - unops[nunop].tt = tk.t; - if (++nunop >= countof(unops)) { - ex = exprparse(cm, 999, NULL, 0); - break; - } - goto Unary; - - /* might be unary op (cast) or primary expr */ - case '(': - if (!isdecltok(cm)) { /* (expr) */ - struct span span = tk.span; - ex = commaexpr(cm); - joinspan(&span.ex, ex.span.ex); - peek(cm, &tk); - if (expect(cm, ')', NULL)) joinspan(&span.ex, tk.span.ex); - ex.span = span; - } 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 >= countof(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 %s literal %'tk", tk.t == TKNUMLIT ? "number" : "character", &tk); - ex.ty.t = ty.t ? ty.t : TYINT; - break; - case TKWtrue: case TKWfalse: - ex = mkexpr(ENUMLIT, tk.span, mktype(TYBOOL), .u = tk.t == TKWtrue); - break; - case TKSTRLIT: - ty = mktype(((const char []){TYCHAR, TYSHORT, TYINT})[tk.wide]); - ex = mkexpr(ESTRLIT, tk.span, mkarrtype(ty, 0, tk.len+1), { .s.p = (void *)tk.s, .s.n = tk.len }); - break; - case TKIDENT: Ident: { - struct decl *decl = finddecl(cm, tk.name); - if (!decl) { - if (cm->env->up && tk.name->c == '_' - && (!strcmp(&tk.name->c, "__FUNCTION__") || !strcmp(&tk.name->c, "__PRETTY_FUNCTION__"))) { - /* hack: treat these identifiers as __func__ synonym to support the GNU extension */ - warn(&tk.span, "%'tk is a GNU extension", &tk); - decl = finddecl(cm, istr__func__); - assert(decl && decl->scls == SCSTATIC); - goto Sym; - } else if (ctx == EATTRARG && nunop == 0 && (peek(cm, NULL) == ',' || peek(cm, NULL) == ')')) { - return ex = mkexpr(ESYM, tk.span, mktype(IMPLICITSYMTY), .implicitsym = tk.name); - } else if (peek(cm, NULL) == '(') { /* implicit function decl? */ - ex = mkexpr(ESYM, tk.span, mktype(IMPLICITSYMTY), .implicitsym = tk.name); - } else { - error(&tk.span, "undeclared identifier %'tk", &tk); - ex = mkexpr(ESYM, tk.span, mktype(TYINT), .implicitsym = NULL); - } - } else if (decl->scls == SCTYPEDEF) { - error(&tk.span, "unexpected typename %'tk (expected expression)", &tk); - ex = mkexpr(ESYM, tk.span, decl->ty, .implicitsym = NULL); - } else if (decl->isenum) { - ex = mkexpr(ENUMLIT, tk.span, decl->ty, .i = decl->value); - } else Sym: { - if (decl->name == istr__func__ && decl->isbuiltin) { /* lazy __func__ */ - internstr fnname = decl->sym; - decl->isbuiltin = 0; - decl->sym = mkhiddensym(&fnname->c, "__func__", 1); - uint off = objnewdat(decl->sym, objout.code ? Stext : Srodata, 0, typesize(decl->ty), typealign(decl->ty)); - uchar *p = objout.code ? objout.textbegin + off : objout.rodata.p + off; - memcpy(p, fnname, typearrlen(decl->ty)-1); - } - ex = mkexpr(ESYM, tk.span, decl->ty, .qual = decl->qual, .decl = decl - declsbuf.p); - } - break; } - case TKWsizeof: case TKW_Alignof: case TKWalignof: { - enum toktag tt = tk.t; - uint res; - span = tk.span; - if (!match(cm, NULL, '(')) /* sizeof/alignof expr */ - goto Unops; - else if (isdecltok(cm)) { /* sizeof/alignof (type) */ - struct declstate st = { DCASTEXPR }; - ty = pdecl(&st, cm).ty; - peek(cm, &tk); - if (expect(cm, ')', NULL)) - joinspan(&span.ex, tk.span.ex); - res = sizeofalignofcheck(&span, tt, ty, NULL); - } else { /* sizeof/alignof expr */ - struct expr tmp = commaexpr(cm); - peek(cm, &tk); - if (expect(cm, ')', NULL)) - joinspan(&span.ex, tk.span.ex); - ppostfixopers(cm, &tmp); - ty = tmp.ty; - res = sizeofalignofcheck(&span, tt, ty, &tmp); - } - ex = mkexpr(ENUMLIT, span, mktype(targ_sizetype), .u = res); - break; } - case TKW__builtin_va_arg: - span = tk.span; - ex = vaargexpr(cm, &span); - break; - case TKW_Generic: - span = tk.span; - ex = genericexpr(cm, &span); - break; - default: - fatal(&tk.span, "expected %s (near %'tk)", ctx == EFROMSTMT ? "statement" : "expression", &tk); - } - - ppostfixopers(cm, &ex); - - /* unary operators (process) */ - while (nunop-- > 0) { - enum exprkind ek; - span = unops[nunop].span; - joinspan(&span.ex, ex.span.ex); - if (unops[nunop].t0 == 0) { - switch (unops[nunop].tt) { - case '+': - ek = EPLUS; - goto Alu; - case '-': - ek = ENEG; - goto Alu; - case '~': - ek = ECOMPL; - goto Alu; - case '!': - ek = ELOGNOT; - Alu: - ty = ek == ELOGNOT ? mktype(TYINT) : cvtarith(ex.ty, ex.ty); - if (!ty.t || (ek == ECOMPL && !isint(ty))) { - error(&tk.span, "invalid operand to %'tk '%ty'", &tk, ex.ty); - ty = mktype(TYINT); - } - ex = mkexpr(ek, span, ty, .sub = exprdup(cm, &ex)); - break; - case TKINC: case TKDEC: - ty = ex.ty; - incdeccheck(tk.t, &ex, &span); - ex = mkexpr(unops[nunop].tt == TKINC ? EPREINC : EPREDEC, span, ty, - .sub = exprdup(cm, &ex)); - break; - case '*': - if (ex.ty.t == TYPTR || ex.ty.t == TYARRAY) { - ty = typechild(ex.ty); - if (ty.t != TYVOID && isincomplete(typedecay(ty))) { - error(&span, "cannot dereference pointer to incomplete type '%ty'", ty); - ty = mktype(TYINT); - } - } else { - error(&span, "invalid operand to unary * '%ty'", ex.ty); - ty = mktype(TYINT); - } - ex = mkexpr(EDEREF, span, ty, .qual = ex.ty.flag & TFCHLDQUAL, - .sub = exprdup(cm, &ex)); - break; - 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(cm, &ex)); - break; - case TKWsizeof: case TKW_Alignof: case TKWalignof: - ex = mkexpr(ENUMLIT, span, mktype(targ_sizetype), - .u = sizeofalignofcheck(&span, unops[nunop].tt, ex.ty, &ex)); - break; - default: assert(0); - } - } else { /* cast */ - ty = unops[nunop].ty; - if (!castcheck(ty, &ex)) - error(&span, "cannot cast value of type '%ty' to '%ty'", ex.ty, ty); - if (ex.t == ENUMLIT && isint(ex.ty) && ty.t == TYPTR) - ex.ty = ty; - else - ex = mkexpr(ECAST, span, ty, .sub = exprdup(cm, &ex)); - } - } - - /* binary operators */ - for (int opprec; (opprec = tkprec(peek(cm, &tk))) >= prec;) { - enum exprkind ek = bintab[tk.t].t; - struct expr rhs, tmp; - lex(cm, NULL); - if (ek != ECOND) { - /* only the assignment operators are right-associative */ - bool leftassoc = (bintab[tk.t].k & BCSET) == 0; - /* ex OP rhs */ - span.sl = tk.span.sl; - span.ex = ex.span.ex; - rhs = exprparse(cm, opprec + leftassoc, NULL, 0); - if (!joinspan(&span.ex, tk.span.ex) || !joinspan(&span.ex, rhs.span.ex)) - span.ex = tk.span.ex; - ty = bintypecheck(&span, tk.t, &ex, &rhs); - assert(ty.t); - ex = mkexpr(ek, span, ty, .sub = exprdup2(cm, &ex, &rhs)); - } else { - /* logical-OR-expression ? expression : conditional-expression */ - struct expr *sub; - span.sl = tk.span.sl; - span.ex = ex.span.ex; - if (!isscalar(ex.ty) && !isptrcvt(ex.ty)) - error(&ex.span, "?: condition is not a scalar type: '%ty'", ex.ty); - tmp = commaexpr(cm); - joinspan(&tk.span.ex, tmp.span.ex); - expect(cm, ':', NULL); - rhs = exprparse(cm, opprec, NULL, 0); - if (!joinspan(&span.ex, tk.span.ex) || !joinspan(&span.ex, tmp.span.ex) - || !joinspan(&span.ex, rhs.span.ex)) - span.ex = tk.span.ex; - ty = condtype(&tmp, &rhs); - if (!ty.t) { - error(&span, "incompatible types in conditional expression: '%ty', '%ty'", tmp.ty, rhs.ty); - ty = tmp.ty; - } - sub = alloc(&cm->exarena, 3 * sizeof*sub, 0); - sub[0] = ex, sub[1] = tmp, sub[2] = rhs; - ex = mkexpr(ECOND, span, ty, .sub = sub); - } - } - - return ex; -} - -static struct expr -expr(struct comp *cm) -{ - return exprparse(cm, bintab['='].prec, NULL, 0); /* non-comma expr */ -} - -static struct expr -arraycountexpr(struct comp *cm) -{ - return exprparse(cm, bintab['='].prec, NULL, EARRAYCOUNT); /* non-comma expr, or lone '*' */ -} - -static struct expr -constantexpr(struct comp *cm) -{ - return exprparse(cm, bintab['?'].prec, NULL, 0); /* conditional-expr */ -} - -static struct expr -commaexpr(struct comp *cm) -{ - return exprparse(cm, 1, NULL, 0); -} - -/****************/ -/* Initializers */ -/****************/ - -static uint -nmemb(union type ty) -{ - switch (ty.t) { - case TYARRAY: return typearrlen(ty) ? typearrlen(ty) : -1u; - case TYUNION: case TYSTRUCT: return typedata[ty.dat].nmemb; - default: return 1; - } -} - -static bool -objectp(union type ty) -{ - return isagg(ty) || ty.t == TYARRAY; -} - -static bool -chrarrayof(union type ty, union type chld) -{ - assert(isint(chld)); - return ty.t == TYARRAY && isint(typechild(ty)) && typesize(typechild(ty)) == typesize(chld); -} - -static union type -membertype(uint *off, uint *bitsiz, uint *bitoff, union type ty, uint idx) -{ - *bitsiz = *bitoff = 0; - if (!objectp(ty)) { - *off = 0; - return ty; - } else if (ty.t == TYARRAY) { - *off = typesize(typechild(ty)) * idx; - return typechild(ty); - } else if (idx < typedata[ty.dat].nmemb) { - struct fielddata fld = typedata[ty.dat].fld[idx].f; - *off = fld.off; - *bitsiz = fld.bitsiz, *bitoff = fld.bitoff; - return fld.t; - } - *off = ~0u; - return mktype(0); -} - -struct initparser { - struct initcur { - union type ty; - uint idx; - uint off; - short prev; - } buf[32], *cur, *sub; - struct arena **arena; - uint arrlen; - enum evalmode ev; - bool dyn; /* when set, data is written to a temporary buffer first, because either: - - size is not known until parsing done (implicit array size) - - data section is not known until parsing done (to avoid relocs in .rodata) - otherwise write to the corresponding object data section buffer directly */ - union { - struct init *init; /* for initializer with automatic storage */ - struct { /* for static storage (dyn = 0) */ - enum section sec; - uint off; - }; - struct { /* for static storage (dyn = 1) */ - vec_of(uchar) ddat; - struct dreloc { - struct dreloc *link; - internstr sym; - vlong addend; - uint off; - } *drel; - }; - }; -}; - -static void -excesscheck(struct initparser *ip, const struct span *span) -{ - union type sub = ip->sub->ty; - uint n = nmemb(sub); - if (ip->sub->idx == n) { - if (sub.t == TYARRAY) - warn(span, "excess elements in array initializer for '%ty'", sub); - else if (sub.t == TYSTRUCT) - warn(span, "excess elements in initializer; '%ty' has %u member%s", sub, n, &"s"[n==1]); - else if (sub.t == TYUNION) - warn(span, "excess elements in union initializer"); - else - warn(span, "excess elements in scalar initializer"); - } -} - -#if 1 -#define dumpini(_) -#else -/* debugging */ -static void -dumpini(struct initparser *ip) -{ - efmt(">>>\n"); - for (struct initcur *s = ip->buf; s < ip->sub+1; ++s) { - efmt(" "); - efmt("%d. [%ty, %u]", s- ip->buf, s->ty, s->idx); - if (s == ip->cur) efmt(" <-- cursor"); - ioputc(&bstderr, '\n'); - } - efmt("<<<\n"); -} -#endif - -static vlong /* -> returns addend */ -expr2reloc(internstr *psym, const struct expr *ex) -{ - if (ex->t == ESSYMREF) { - *psym = ex->ssym.sym; - return ex->ssym.off; - } else if (ex->t == ESTRLIT || ex->t == EINIT) { - if (ex->t == ESTRLIT) assert(ex->ty.t == TYARRAY); - *psym = xcon2sym(expraddr(NULL, ex).i); - return 0; - } - fatal(&ex->span, "internal bug: non static reloc?"); -} - -static bool -rodatarelocok(void) -{ - return !(ccopt.pie | ccopt.pic); -} - -static void -iniwrite(struct comp *cm, struct initparser *ip, uint off, uint bitsiz, uint bitoff, union type ty, struct expr *ex) -{ - if (ex->ty.t == TYSTRUCT && ip->ev == EVSTATICINI) { - assert(ty.bits == ex->ty.bits); - for (uint i = 0, n = nmemb(ex->ty); i < n; ++i) { - uint suboff; - union type sub = membertype(&suboff, &bitsiz, &bitoff, ex->ty, i); - iniwrite(cm, ip, off + suboff, bitsiz, bitoff, sub, exprdup(cm, &mkexpr(EGETF, ex->span, sub, .sub = ex))); - } - } else if (ip->ev == EVSTATICINI) { - uchar *p; - uint siz = typesize(ty); - if (nerror) return; - if (ip->dyn) { - if (ip->ddat.n < off + siz) { - uint old = ip->ddat.n; - vresize(&ip->ddat, off + siz); - memset(ip->ddat.p + old, 0, ip->ddat.n - old); - assert(off + siz == ip->ddat.n); - } - p = ip->ddat.p + off; - } else { - p = (ip->sec == Sdata ? objout.data.p : objout.rodata.p) + ip->off + off; - } - - if (ex->t == ENUMLIT) { - struct expr *e = ex, tmp; - if (ex->ty.bits != ty.bits && ty.t != TYPTR) { - tmp = mkexpr(ECAST, ex->span, ty, .sub = ex); - e = &tmp; - assert(eval(e, EVSTATICINI)); - assert(e->t == ENUMLIT); - } - if (!bitsiz) switch (siz) { - default: assert(0); - case 1: *p = e->u; break; - case 2: wr16targ(p, e->u); break; - case 4: isint(ty) ? wr32targ(p, e->u) : wrf32targ(p, e->f); break; - case 8: isint(ty) ? wr64targ(p, e->u) : wrf64targ(p, e->f); break; - } else { - uvlong mask = (bitsiz == 64 ? -1ull : (1ull << bitsiz) - 1) << bitoff; - if (bitoff + bitsiz > siz*8) siz <<= 1; /* straddles an allocation boundary */ - switch (siz) { - default: assert(0); - case 1: *p = (*p &~ mask) | (e->u << bitoff & mask); break; - case 2: wr16targ(p, (rd16targ(p) &~ mask) | (e->u << bitoff & mask)); break; - case 4: wr32targ(p, (rd32targ(p) &~ mask) | (e->u << bitoff & mask)); break; - case 8: wr64targ(p, (rd64targ(p) &~ mask) | (e->u << bitoff & mask)); break; - } - } - } else if (ty.t == TYARRAY && ex->t == ESTRLIT) { - uint n = ex->s.n * typesize(typechild(ty)); - if (siz < n) n = siz; - /* XXX endian for wide strs */ - memcpy(p, ex->s.p, n); - } else { - internstr sym; - vlong addend = expr2reloc(&sym, ex); - if (!ip->dyn) { - assert(ip->sec != Srodata || rodatarelocok()); - objreloc(sym, targ_64bit ? REL_ABS64 : REL_ABS32, - ip->sec, ip->off + off, addend); - } else { - struct dreloc *rel = alloc(ip->arena, sizeof *rel, 0); - rel->link = ip->drel; - rel->sym = sym; - rel->off = off; - rel->addend = addend; - ip->drel = rel; - } - } - } else { - assert(cm != NULL); - struct init *init = ip->init; - struct initval val = { - .off = off, - .bitsiz = bitsiz, - .bitoff = bitoff, - .ex = *ex - }, *new = alloccopy(&cm->exarena, &val, sizeof val, 0); - *init->tail = new; - init->tail = &new->next; - if (!bitsiz) for (uint i = off, end = i + typesize(ex->ty); i < end; ++i) { - if (BSSIZE(end) > countof(init->zero)) break; - bsclr(init->zero, i); - } - } -} - -static bool -iniwriterec(struct comp *cm, struct initparser *ip, uint off, struct expr *ex) -{ - assert(ex->t == EINIT); - for (struct initval *v = ex->init->vals; v; v = v->next) { - if (v->ex.t == EINIT) iniwriterec(cm, ip, off + v->off, &v->ex); - else if (ip->ev && !eval(&v->ex, ip->ev) && ip->ev != EVFOLD) return 0; - else iniwrite(cm, ip, off + v->off, v->bitsiz, v->bitoff, v->ex.ty, &v->ex); - } - return 1; -} - -static struct initcur * -iniadvance(struct initparser *ip, struct initcur *c, const struct span *span) -{ - if (c - ip->buf >= countof(ip->buf) - 1) - fatal(span, "too many nested initializers"); - return c + 1; -} - -/* set the initializer cursor object */ -static void -inifocus(struct initparser *ip, struct comp *cm, const struct span *span, uint idx) -{ - while (idx >= nmemb(ip->sub->ty) && ip->sub != ip->cur) { - --ip->sub; - idx = ip->sub->idx; - } - uint off, bitsiz, bitoff; - union type targ = membertype(&off, &bitsiz, &bitoff, ip->sub->ty, idx); - struct initcur *next = iniadvance(ip, ip->cur, span); - assert(!bitsiz); - - if (isagg(ip->sub->ty) && targ.t == TYARRAY && !typearrlen(targ)) - error(span, "cannot initialize flexible array member"); - excesscheck(ip, span); - - next->ty = targ; - next->idx = 0; - next->off = ip->sub->off + off; - next->prev = ip->cur - ip->buf; - ++ip->cur->idx; - ip->sub = ip->cur = next; -} - -/* initialize a character array with a string literal */ -static void -inistrlit(struct comp *cm, struct expr *ex, union type *ty) -{ - if (isincomplete(*ty)) { - *ty = mkarrtype(typechild(*ty), ty->flag & TFCHLDQUAL, ex->s.n + 1); - } else if (typearrlen(*ty) < ex->s.n) { - warn(&ex->span, "string literal in initializer is truncated from %u to %u bytes", - (ex->s.n+1)*typesize(typechild(*ty)), typesize(*ty)); - } - ex->ty = *ty; -} - -/* read scalar initializer into initializer list and avance */ -static void -ininext(struct initparser *ip, struct comp *cm) -{ - uint off, bitsiz, bitoff; - union type targ; - struct expr ex = expr(cm); - -Retry: - targ = membertype(&off, &bitsiz, &bitoff, ip->sub->ty, ip->sub->idx); - - if (isagg(ip->sub->ty) && targ.t == TYARRAY && !typearrlen(targ)) { - error(&ex.span, "cannot initialize flexible array member"); - ++ip->sub->idx; - return; - } - if (ex.t == ESTRLIT && chrarrayof(targ, typechild(ex.ty))) { - assert(!isincomplete(targ)); - inistrlit(cm, &ex, &targ); - iniwrite(cm, ip, ip->sub->off + off, 0,0, targ, &ex); - ++ip->sub->idx; - return; - } else if (ex.t == ESTRLIT && ip->sub->idx == 0 && chrarrayof(ip->sub->ty, typechild(ex.ty))) { - /* handle e.g. (char []){"foo"} */ - assert(off == 0); - targ = ip->sub->ty; - inistrlit(cm, &ex, &targ); - iniwrite(cm, ip, ip->sub->off, 0,0, targ, &ex); - if (ip->sub == ip->buf && ip->arrlen < ex.s.n+1) - ip->arrlen = ex.s.n+1; - --ip->sub; - return; - } else if (ip->sub->idx >= nmemb(ip->sub->ty) && ip->sub != ip->cur) { - --ip->sub; - goto Retry; - } else if (objectp(targ) && targ.bits != ex.ty.bits) { - struct initcur *next = iniadvance(ip, ip->sub, &ex.span); - if (ip->sub - ip->buf == countof(ip->buf) - 1) - fatal(&ex.span, "too many nested initializers"); - ++ip->sub->idx; - *next = (struct initcur) { targ, .off = ip->sub->off + off }; - ip->sub = next; - goto Retry; - } - excesscheck(ip, &ex.span); - - if (targ.t) { - if (!initcheck(targ, &ex)) - error(&ex.span, "cannot initialize '%ty' with expression of type '%ty'", targ, ex.ty); - else { - if (targ.bits == ex.ty.bits && ex.t == EINIT) { - if (!iniwriterec(cm, ip, ip->sub->off + off, &ex)) - goto CannotEval; - } else if (ip->ev && !eval(&ex, ip->ev) && ip->ev != EVFOLD) { - CannotEval: - error(&ex.span, "cannot evaluate expression statically"); - } else { - struct expr *pex = &ex; - if (ip->ev != EVSTATICINI) { - if (ex.ty.bits != targ.bits) - ex = mkexpr(ECAST, ex.span, targ, .sub = exprdup(cm, &ex)); - pex = exprdup(cm, &ex); - } - iniwrite(cm, ip, ip->sub->off + off, bitsiz, bitoff, targ, pex); - } - } - } - if (ip->sub == ip->buf && ip->arrlen < ip->sub->idx+1) - ip->arrlen = ip->sub->idx+1; - - if (++ip->sub->idx == 0) { - error(&ex.span, "element makes object too large"); - --ip->sub->idx; - } -} - -static int -aggdesignator(struct initparser *ip, union type ty, internstr name, const struct span *span) -{ - const struct typedata *td = &typedata[ty.dat]; - for (int i = 0; i < td->nmemb; ++i) { - struct namedfield *fld = &td->fld[i]; - if (fld->name == name) { - return i; - } else if (!fld->name) { - int save, sub; - struct initcur *next = iniadvance(ip, ip->sub, span); - save = ip->sub->idx; - ip->sub->idx = i+1; - *next = (struct initcur) { fld->f.t, .off = ip->sub->off + fld->f.off }; - ip->sub = next; - sub = aggdesignator(ip, fld->f.t, name, span); - if (sub == -1) { - --ip->sub; - ip->sub->idx = save; - } else return sub; - } - } - return -1; -} - -static bool -designators(struct initparser *ip, struct comp *cm) -{ - struct token tk; - struct span span; - bool some = 0; - - for (;;) { - uint off, bitsiz, bitoff; - uvlong idx = ~0ull; - if (match(cm, &tk, '[')) { - struct expr ex = commaexpr(cm); - span = tk.span; - joinspan(&span.ex, ex.span.ex); - peek(cm, &tk); - if (some) { - union type ty = membertype(&off, &bitsiz, &bitoff, ip->sub->ty, ip->sub->idx++); - struct initcur *next = iniadvance(ip, ip->sub, &tk.span); - assert(!bitsiz); - *next = (struct initcur) { ty, .off = ip->sub->off + off }; - ip->sub = next; - dumpini(ip); - } - if (expect(cm, ']', NULL)) joinspan(&span.ex, tk.span.ex); - if (ip->sub->ty.t != TYARRAY) - error(&ex.span, "array designator used with non-array type '%ty'", ip->sub->ty); - if (!eval(&ex, EVINTCONST)) - error(&ex.span, "array designator index is not an integer constant"); - else if (issigned(ex.ty) && ex.i < 0) - error(&ex.span, "negative array designator index"); - else if (ex.i > ~0u - 1) - error(&ex.span, "index too large"); - else { - idx = ex.u; - ip->sub->idx = idx; - if (ip->sub == ip->buf && ip->arrlen < idx+1) - ip->arrlen = idx+1; - dumpini(ip); - } - some = 1; - } else if (match(cm, &tk, '.')) { - span = tk.span; - peek(cm, &tk); - if (some) { - union type ty = membertype(&off, &bitsiz, &bitoff, ip->sub->ty, ip->sub->idx++); - struct initcur *next = iniadvance(ip, ip->sub, &tk.span); - *next = (struct initcur) { ty, .off = ip->sub->off + off }; - ip->sub = next; - dumpini(ip); - } - if (expect(cm, TKIDENT, NULL)) joinspan(&span.ex, tk.span.ex); - if (!isagg(ip->sub->ty)) - error(&span, "member designator used with non-aggregate type '%ty'", ip->sub->ty); - else if (tk.t == TKIDENT) { - int idx; - for (;;) { - idx = aggdesignator(ip, ip->sub->ty, tk.name, &span); - if (idx >= 0 || ip->sub == ip->cur) break; - --ip->sub; - } - ip->sub->idx = idx; - if (idx < 0) - error(&span, "%ty has no such field: '%s'", ip->cur->ty, tk.name); - dumpini(ip); - } - some = 1; - } else { - if (some) { - expect(cm, '=', NULL); - } - return some; - } - } -} - -static struct expr -initializer(struct comp *cm, union type *ty, enum evalmode ev, bool globl, - enum qualifier qual, internstr sym) -{ - struct token tk; - struct span span; - struct init res = {0}; - struct initparser ip[1] = {0}; - - ip->arena = &cm->exarena; - ip->ev = ev; - if (ev == EVSTATICINI) { - if (ty->t == TYARRAY && isincomplete(*ty)) { - ip->dyn = 1; - } else if (qual & QCONST && !rodatarelocok()) { - ip->dyn = 1; - vresize(&ip->ddat, typesize(*ty)); - memset(ip->ddat.p, 0, typesize(*ty)); - } else { - ip->sec = qual & QCONST ? Srodata : Sdata; - if (!nerror) - ip->off = objnewdat(sym, ip->sec, globl, typesize(*ty), typealign(*ty)); - } - } else { - ip->init = &res; - res.tail = &res.vals; - } - - if (!match(cm, &tk, '{')) { - struct expr ex = expr(cm); - if (ex.t == ESTRLIT && chrarrayof(*ty, typechild(ex.ty))) { - inistrlit(cm, &ex, ty); - iniwrite(cm, ip, 0, 0, 0, *ty, &ex); - } else if (!initcheck(*ty, &ex)) { - error(&ex.span, "cannot initialize '%ty' with expression of type '%ty'", *ty, ex.ty); - } else { - if (ev && !eval(&ex, ev) && ev != EVFOLD) - error(&ex.span, "cannot evaluate expression statically"); - else - iniwrite(cm, ip, 0, 0, 0, *ty, &ex); - } - if (ip->dyn) - goto Dynfix; - return ex; - } - - assert(countof(res.zero) == BSSIZE(64)); - if (ev != EVSTATICINI) { - memset(res.zero, 0xFF, sizeof res.zero); - } - - span = tk.span; - ip->sub = ip->cur = ip->buf; - ip->cur->ty = *ty; - for (;;) { - peek(cm, &tk); - joinspan(&span.ex, tk.span.ex); - if (tk.t == '[' || tk.t == '.') { - designators(ip, cm); - } - if (match(cm, &tk, '}')) { - if (ip->cur == ip->buf) break; - ip->sub = ip->cur = ip->buf + ip->cur->prev; - dumpini(ip); - } else if (match(cm, &tk, '{')) { - struct span span = tk.span; - inifocus(ip, cm, &tk.span, ip->sub->idx); - if (peek(cm, &tk) == '}') { - if (!joinspan(&span.ex, tk.span.ex)) span = tk.span; - if (!objectp(ip->sub->ty)) { - error(&span, "scalar initializer cannot be empty"); - } else if (ccopt.cstd < STDC23 && ccopt.pedant) { - warn(&span, "empty initializer in %M is an extension"); - } - } else if (ip->sub->ty.t && !objectp(ip->sub->ty)) { - warn(&span, "brace initializer for scalar object '%ty'", ip->sub->ty); - } - continue; - } else { - dumpini(ip); - ininext(ip, cm); - } - match(cm, NULL, ','); - if (peek(cm, &tk) != '}' && ip->sub->ty.t == TYUNION) { - if (ip->sub == ip->cur) { - warn(&tk.span, "excess elements in union initializer"); - } else while (ip->sub != ip->cur && ip->sub->ty.t == TYUNION) { - --ip->sub; - } - } - } - if (ip->dyn) { - enum section sec; - uint off, siz, align; - uchar *p; - - if (isincomplete(*ty)) { - uint len = ip->arrlen > ip->cur->idx ? ip->arrlen : ip->cur->idx; - if (len == 0) - error(&span, "array cannot have zero length"); - *ty = mkarrtype(typechild(*ty), ty->flag & TFCHLDQUAL, len); - } - Dynfix: - if (qual & QCONST && (ip->drel == NULL || rodatarelocok())) - sec = Srodata; - else - sec = Sdata; - if (!nerror) { - off = objnewdat(sym, sec, globl, siz = typesize(*ty), align = typealign(*ty)); - p = sec == Srodata ? objout.rodata.p : objout.data.p; - memcpy(p + off, ip->ddat.p, ip->ddat.n); - memset(p + off + ip->ddat.n, 0, typesize(*ty) - ip->ddat.n); - for (struct dreloc *rel = ip->drel; rel; rel = rel->link) { - objreloc(rel->sym, targ_64bit ? REL_ABS64 : REL_ABS32, sec, off + rel->off, rel->addend); - } - } - vfree(&ip->ddat); - } - dumpini(ip); - - if (ev == EVSTATICINI) { - return (struct expr){.span = span}; - } else { - uint siz; - if (isincomplete(*ty)) { - uint len = ip->arrlen > ip->cur->idx ? ip->arrlen : ip->cur->idx; - if (!len) - error(&span, "initializer creates a zero-sized array"); - *ty = mkarrtype(typechild(*ty), ty->flag & TFCHLDQUAL, len); - } - - assert(countof(res.zero) == 1); - siz = typesize(*ty); - if (siz && siz <= 64) - res.zero->u &= ~0ull >> (64 - siz); - - return mkexpr(EINIT, span, *ty, .init = alloccopy(&cm->exarena, &res, sizeof res, 0)); - } -} - -/* debugging */ -void -dumpexpr(const struct expr *ex, bool prity) -{ - static const char *name[] = { - [EXXX] = "xxx", [ENUMLIT] = "numlit", [ESTRLIT] = "strlit", - [ESSYMREF] = "ssymref", [ESYM] = "sym", [EVAARG] = "vaarg", - [EINIT] = "init", [EGETF] = "getf", [ECALL] = "call", - [ECOND] = "cond", [EPLUS] = "plus", [ENEG] = "neg", - [ECOMPL] = "compl", [ELOGNOT] = "lognot", [EDEREF] = "deref", - [EADDROF] = "addrof", [ECAST] = "cast", [EPREINC] = "preinc", - [EPOSTINC] = "postinc", [EPREDEC] = "predec", [EPOSTDEC] = "postdec", - [EADD] = "add", [ESUB] = "sub", [EMUL] = "mul", - [EDIV] = "div", [EREM] = "rem", [EBAND] = "band", - [EBIOR] = "bior", [EXOR] = "xor", [ESHL] = "shl", - [ESHR] = "shr", [ELOGAND] = "logand", [ELOGIOR] = "logior", - [EEQU] = "equ", [ENEQ] = "neq", [ELTH] = "lth", - [EGTH] = "gth", [ELTE] = "lte", [EGTE] = "gte", - [ESET] = "set", [ESETADD] = "setadd", [ESETSUB] = "setsub", - [ESETMUL] = "setmul", [ESETDIV] = "setdiv", [ESETREM] = "setrem", - [ESETAND] = "setand", [ESETIOR] = "setior", [ESETXOR] = "setxor", - [ESETSHL] = "setshl", [ESETSHR] = "setshr", [ESEQ] = "seq", - }; - ioputc(&bstderr, '('); - efmt("%s ", name[ex->t]); - if (ex->ty.t && (prity || ex->t == EVAARG || ex->t == ECAST)) - efmt("<%ty> ", ex->ty); - int nsub = 0; - switch (ex->t) { - case ENUMLIT: if (!isflt(ex->ty)) efmt(isunsigned(ex->ty) ? "%lu" : "%ld", ex->u); - else efmt("%f", ex->f); - break; - case ESTRLIT: efmt("%'S", ex->s.p, ex->s.n); break; /* XXX widestr */ - case ESYM: efmt("%y", declsbuf.p[ex->decl].name); break; - case ESSYMREF: efmt("%y%+d", ex->ssym.sym, ex->ssym.off); - if (ex->ssym.func) efmt(" @func"); - if (ex->ssym.local) efmt(" @local"); - break; - case EVAARG: dumpexpr(ex->sub, prity); break; - case EGETF: dumpexpr(ex->sub, prity); efmt(" #+%u", ex->fld.off); - if (ex->fld.bitsiz) efmt("[%d:%d]", ex->fld.bitoff, ex->fld.bitsiz); - break; - case ECALL: nsub = ex->narg+1; goto Sub; - case ECOND: nsub = ex->narg+1; goto Sub; - default: - if (in_range(ex->t, EPLUS, EPOSTDEC)) nsub = 1; - else nsub = 2; - Sub: - for (int i = 0; i < nsub; ++i) { - if (i) ioputc(&bstderr, ' '); - dumpexpr(&ex->sub[i], prity); - } - break; - case EINIT: assert(!"nyi"); - } - ioputc(&bstderr, ')'); -} - -/*****************/ -/* Decls Parsing */ -/*****************/ - -static union type -buildagg(struct comp *cm, enum typetag tt, internstr name, int id) -{ - struct token tk; - union type t; - struct span flexspan; - struct namedfield fbuf[32]; - vec_of(struct namedfield) fld = VINIT(fbuf, countof(fbuf)); - struct typedata td = {tt}; - bool isunion = tt == TYUNION; - const char *tag = isunion ? "union" : "struct"; - uint bitsiz = 0, bitfbyteoff = 0, - bitoff = 0, bitftypesiz = 0; - - while (!match(cm, &tk, '}')) { - struct declstate st = { DFIELD }; - do { - struct decl decl = pdecl(&st, cm); - uint tysize; - if (st.empty) { - if (ccopt.pedant) - warn(&decl.span, "extra semicolon in aggregate"); - continue; - } - tysize = typesize(decl.ty); - if (fld.n && td.flexi) { - td.flexi = 0; - error(&flexspan, "flexible array member is not at end of struct"); - } - if (!isunion && decl.ty.t == TYARRAY && !typearrlen(decl.ty)) { - td.flexi = 1; - flexspan = decl.span; - } else if (isincomplete(decl.ty)) { - error(&decl.span, "field has incomplete type '%ty'", decl.ty); - } else if (decl.ty.t == TYFUNC) { - error(&decl.span, "field has function type '%ty'", decl.ty); - } - bitsiz = 0; - if (st.bitf) { - struct expr ex = constantexpr(cm); - const char *name = decl.name ? &decl.name->c : "<anonymous>"; - if (!isint(decl.ty)) { - error(&decl.span, "bit-field '%s' has non-integer type '%ty'", name, decl.ty); - } else if (!isint(ex.ty)) { - error(&ex.span, "integer constant expression has non-integer type '%ty'", decl.ty); - } else if (!eval(&ex, EVINTCONST)) { - error(&ex.span, "cannot evaluate integer constant expression"); - } else if (ex.i < 0) { - error(&ex.span, "bit-field '%s' has negative width '%ld'", name, ex.i); - } else if (ex.i > 8*tysize) { - error(&ex.span, "width of bit-field '%s' (%ld) exceeds width of type (%d)", - name, ex.i, 8*tysize); - } else if (ex.i == 0 && decl.name) { - error(&ex.span, "named bit-field '%s' has zero width", name); - } else { - bitsiz = ex.i; - if (bitsiz == 0) { - if (bitftypesiz) { - bitfbyteoff += bitftypesiz; - bitfbyteoff = alignup(bitfbyteoff, typealign(decl.ty)); - } - bitoff = 0; - } else if (bitftypesiz && bitftypesiz < tysize) { - /* end of previous bitfield */ - bitoff = 0; - bitfbyteoff += bitftypesiz; - } else if (!bitftypesiz) { - bitoff = 0; - bitfbyteoff = alignup(td.siz, typealign(decl.ty)); - } else if (bitoff + bitsiz > 8*bitftypesiz) { - /* no straddling boundaries */ - bitoff = 0; - bitfbyteoff += bitftypesiz; - } - if (tysize > bitftypesiz) bitftypesiz = tysize; - } - pdecl(&st, cm); - } else { - bitftypesiz = bitoff = bitsiz = 0; - } - if (decl.ty.t) { - uint align = typealign(decl.ty); - uint siz = tysize; - uint off = bitftypesiz ? bitfbyteoff : isunion ? 0 : alignup(td.siz, align); - struct namedfield f = { decl.name, { decl.ty, off, bitsiz, bitoff, .qual = decl.qual }}; - if (bitftypesiz && siz != bitftypesiz) while (f.f.bitoff + f.f.bitsiz > 8*siz) { - /* adjust bitfields narrower than container type */ - f.f.off += siz; - f.f.bitoff -= 8*siz; - } - if (!decl.name && !bitftypesiz) { - if (!isagg(decl.ty) || ttypenames[typedata[decl.ty.dat].id]) { - warn(&decl.span, "declaration does not declare anything"); - continue; - } else if (ccopt.cstd < STDC11 && ccopt.pedant) { - warn(&decl.span, "anonymous %s in %M is an extension", - decl.ty.t == TYUNION ? "union" : "struct"); - } - } - if (decl.name || !bitftypesiz) - vpush(&fld, f); - td.anyconst |= decl.qual & QCONST; - if (isagg(decl.ty)) { - td.anyconst |= typedata[decl.ty.dat].anyconst; - if (typedata[decl.ty.dat].flexi && !isunion) - error(&decl.span, "nested aggregate has flexible array member"); - } - if (isunion) - td.siz = td.siz < siz ? siz : td.siz; - else - td.siz = off + siz; - td.align = td.align < align ? align : td.align; - bitoff += bitsiz; - } - } while (st.more); - } - if (td.flexi && fld.n == 1) - error(&flexspan, "flexible array member in otherwise empty aggregate"); - if (td.flexi && ccopt.cstd < STDC99 && ccopt.pedant) - warn(&flexspan, "flexible array member in %M is an extension"); - if (fld.n == 0) { - struct namedfield dummy = { intern(""), { mktype(TYCHAR), 0 }}; - error(&tk.span, "%s cannot have zero members", tag); - vpush(&fld, dummy); - td.siz = td.align = 1; - } - td.siz = alignup(td.siz, td.align); - td.fld = fld.p; - td.nmemb = fld.n; - if (id != -1) - t = completetype(name, id, &td); - else - t = mktagtype(name, &td); - vfree(&fld); - return t; -} - -static inline void -inttyminmax(vlong *min, uvlong *max, enum typetag tt) -{ - uint bits = 8*targ_primsizes[tt]; - *min = isunsignedt(tt) ? 0 : -(1ull << (bits - 1)); - *max = isunsignedt(tt) ? ~0ull >> (64 - bits) : bits == 64 ? ~0ull>>1 : (1ll << (bits - 1)) - 1; -} - -/* the backing type of enum (without a C23 fixed backing type) is int or the - * smallest-rank type that all the enumerators fit in, or if it doesn't exist, - * then the biggest signed type. the type of enumeration constants is the type of - * its defining expression when present or the type of the previous enumerator - * or in case of overflow the smallest type that fits (previous value + 1) - * this isn't strictly conforming since pre C23 enums are pretty loosely defined, - * and this is similar to existing compiler's de-facto behaviour (though gcc - * prefers to use unsigned types when possible). should add support for -fshort-enums - */ -static union type -buildenum(struct comp *cm, internstr name, const struct span *span, int id) -{ - struct token tk; - vlong tymin, minv = 0; - uvlong tymax, maxv = 0; - struct typedata td = {TYENUM, .backing = TYINT}; - union type ty = mktype(td.backing); - struct span maxvspan; - vlong iota = 0; - bool somelonglong = 0; - - inttyminmax(&tymin, &tymax, td.backing); - while (!match(cm, &tk, '}')) { - struct decl decl = {0}; - peek(cm, &tk); - expect(cm, TKIDENT, NULL); - if (match(cm, NULL, '=') || (peek(cm, NULL) == TKNUMLIT && !expect(cm, '=', NULL))) { - struct expr ex = expr(cm); - if (eval(&ex, EVINTCONST)) { - iota = ex.i; - if (ex.ty.t != ty.t) - inttyminmax(&tymin, &tymax, ex.ty.t); - ty = ex.ty; - } else { - error(&ex.span, "enum value is not an integer constant"); - } - } else if (tk.t != TKIDENT) { - lex(cm, NULL); - continue; - } - while (issigned(ty) ? (iota > (vlong)tymax || iota < tymin) : iota > tymax) - inttyminmax(&tymin, &tymax, ++ty.t); - somelonglong |= ty.t >= TYVLONG; - if ((isunsigned(ty) || iota > 0) && iota > maxv) - maxv = iota, maxvspan = tk.span; - else if (issigned(ty) && iota < minv) - minv = iota; - - decl.name = tk.name; - decl.ty = ty; - decl.isenum = 1; - decl.value = iota++; - putdecl(cm, &decl); - if (!match(cm, &tk, ',')) { - if (expect(cm, '}', "or `,'")) - break; - else lex(cm, NULL); - } - } - - td.backing = 0; - if (minv >= 0 && maxv <= ~0u) { - td.backing = TYUINT; - } else for (int t = TYINT; t <= TYUVLONG; ++t) { - inttyminmax(&tymin, &tymax, t); - if (minv >= tymin && maxv <= tymax) { - td.backing = t; - break; - } - } - if (!td.backing) { - td.backing = !somelonglong && ccopt.cstd == STDC89 && ccopt.pedant ? TYLONG : TYVLONG; - warn(&maxvspan, "enumerators exceed range of enum's backing type '%ty'", mktype(td.backing)); - } - if (td.backing >= TYVLONG && !somelonglong && ccopt.cstd == STDC89 && ccopt.pedant) - warn(span, "enum backing type is '%ty' in %M", mktype(td.backing)); - - if (id != -1) - ty = completetype(name, id, &td); - else - ty = mktagtype(name, &td); - ty.backing = td.backing; - return ty; -} - -static union type -tagtype(struct comp *cm, enum toktag kind) -{ - struct token tk; - union type t; - struct span span; - enum typetag tt = kind == TKWenum ? TYENUM : kind == TKWstruct ? TYSTRUCT : TYUNION; - internstr tag = NULL; - - peek(cm, &tk); - if (match(cm, &tk, TKIDENT)) - tag = tk.name; - span = tk.span; - if (!match(cm, NULL, '{')) { - if (!tag) { - error(&tk.span, "expected %tt name or '{'", kind); - return mktype(0); - } - t = gettagged(cm, &span, tt, tag, /* def? */ peek(cm, NULL) == ';'); - } else { - if (tag) { - t = deftagged(cm, &span, tt, tag, mktype(0)); - if (t.t != tt || !isincomplete(t)) { - if (t.t != tt) - error(&tk.span, - "defining tagged type %'tk as %tt clashes with previous definition", - &tk, kind); - else - error(&tk.span, "redefinition of '%tt %s'", kind, tag); - note(&span, "previous definition:"); - } - } - if (tt == TYENUM) - t = buildenum(cm, tag, &span, tag ? typedata[t.dat].id : -1); - else - t = buildagg(cm, tt, tag, tag ? typedata[t.dat].id : -1); - } - - if (t.t != tt) { - error(&tk.span, "declaring tagged type %'tk as %tt clashes with previous definition", - &tk, kind); - note(&span, "previous definition:"); - } - return t; -} - -static bool -attrspec(struct comp *cm, int *attr) -{ /* __attribute__ (( attribute-list )) */ - if (!match(cm, NULL, TKW__attribute__)) return 0; - if (!expect(cm, '(', "after __attribute__") || !expect(cm, '(', "after __attribute__")) { - Bad: - fatal(NULL, NULL); - } - while (!match(cm, NULL, ')')) { - struct token tk; - lex(cm, &tk); - if (tk.t != TKIDENT && !in_range(tk.t, TKWBEGIN_, TKWEND_)) { - fatal(&tk.span, "expected attribute name"); - } - internstr name = tk.name; - int ltrim = name[0].c == '_' && name[1].c == '_', - rtrim = tk.len > 2 && name[tk.len-1].c == '_' && name[tk.len-2].c == '_'; - if (ltrim || rtrim) { /* trim surrounding '__' */ - name = intern_(&name->c + ltrim*2, tk.len - ltrim*2 - rtrim*2); - } - if (match(cm, NULL, '(')) { - while (!match(cm, NULL, ')')) { - (void)exprparse(cm, bintab['='].prec, NULL, EATTRARG); - if (!match(cm, NULL, ',')) { - if (expect(cm, ')', NULL)) break; - else goto Bad; - } - } - } - (void)name; - if (!match(cm, NULL, ',')) { - if (expect(cm, ')', NULL)) break; - else goto Bad; - } - } - if (!expect(cm, ')', NULL)) - goto Bad; - return 1; -} - -static union type -ptypeof(struct comp *cm) -{ - union type ty; - expect(cm, '(', NULL); - if (isdecltok(cm)) { /* typeof (type) */ - struct declstate st = { DCASTEXPR }; - ty = pdecl(&st, cm).ty; - } else { /* typeof (expr) */ - ty = commaexpr(cm).ty; - } - expect(cm, ')', NULL); - return ty; -} - -static void -declspec(struct declstate *st, struct comp *cm, struct span *pspan) -{ - struct token tk; - struct decl *decl; - enum arith { - KSIGNED = 1<<0, - KUNSIGNED = 1<<1, - KBOOL = 1<<2, - KCHAR = 1<<3, - KSHORT = 1<<4, - KLONG = 1<<5, - KLONGLONG = 1<<6, - KINT = 1<<7, - KFLOAT = 1<<8, - KDOUBLE = 1<<9, - KCOMPLEX = 1<<10, - } arith = 0; - struct span span = {0}; - union type ty = st->base; - bool properdecl = st->kind == DFUNCVAR || st->kind == DTOPLEVEL; - - for (bool first = 1;; first = 0) { - enum storageclass scls = 0; - peek(cm, &tk); - if (first) span = tk.span; - switch (tk.t) { - /* storage-class-specifier */ - case TKWtypedef: scls = SCTYPEDEF; break; - case TKWextern: scls = SCEXTERN; break; - case TKWstatic: scls = SCSTATIC; break; - case TKWauto: scls = SCAUTO; break; - case TKWregister: scls = SCREGISTER; break; - case TKWthread_local: - case TKW_Thread_local: scls = SCTHREADLOCAL; break; - - /* function-specifier */ - case TKWinline: - if (!properdecl) BadFnSpec: { - error(&tk.span, "function specifier %'tk is not allowed here", &tk); - break; - } - st->fninline = 1; - break; - case TKW_Noreturn: - if (!properdecl) goto BadFnSpec; - st->fnnoreturn = 1; - break; - - /* alignment-specifier */ - case TKW_Alignas: assert(!"nyi alignas"); break; - - /* type-qualifier */ - case TKWconst: st->qual |= QCONST; break; - case TKWvolatile: st->qual |= QVOLATILE; break; - case TKWrestrict: /* unimplemented */ break; - case TKW_Atomic: /* unimplemented */ break; - - /* type-specifier */ - case TKWvoid: - if (st->base.t) { - DupBase: - error(&tk.span, "more than one data type in declaration specifier"); - } else { - st->base = mktype(TYVOID); - } - break; - case TKWsigned: - arith |= KSIGNED; - break; - case TKWunsigned: - arith |= KUNSIGNED; - break; - case TKW_Bool: case TKWbool: - if (arith & KBOOL) goto DupArith; - arith |= KBOOL; - break; - case TKWchar: - if (arith & KCHAR) { - DupArith: - error(&tk.span, "duplicate %tk specifier", &tk); - } - arith |= KCHAR; - break; - case TKWshort: - arith |= KSHORT; - break; - case TKWlong: - if ((arith & (KLONG | KLONGLONG)) == KLONG) - arith = (arith &~ KLONG) | KLONGLONG; - else if ((arith & (KLONG | KLONGLONG)) == 0) - arith |= KLONG; - else - error(&tk.span, "too long"); - break; - case TKWint: - if (arith & KINT) goto DupArith; - arith |= KINT; - break; - case TKWfloat: - if (arith & KFLOAT) goto DupArith; - arith |= KFLOAT; - break; - case TKWdouble: - if (arith & KDOUBLE) goto DupArith; - arith |= KDOUBLE; - break; - case TKW_Complex: - if (arith & KCOMPLEX) goto DupArith; - arith |= KCOMPLEX; - break; - case TKWenum: - case TKWstruct: - case TKWunion: - lex(cm, &tk); - ty = tagtype(cm, tk.t); - st->tagdecl = 1; - joinspan(&span.ex, tk.span.ex); - if (st->base.t) goto DupBase; - st->base = ty; - continue; - case TKW__typeof__: case TKWtypeof: - lex(cm, &tk); - ty = ptypeof(cm); - joinspan(&span.ex, tk.span.ex); - if (st->base.t) goto DupBase; - st->base = ty; - continue; - case TKIDENT: - if (!st->base.t && !arith && (decl = finddecl(cm, tk.name)) - && decl->scls == SCTYPEDEF) { - lex(cm, &tk); - st->base = decl->ty; - continue; - } - /* fallthru */ - default: - goto End; - case TKW_BitInt: - case TKW_Decimal128: case TKW_Decimal32: - case TKW_Decimal64: case TKW_Imaginary: - error(&tk.span, "%'tk is unsupported", &tk); - arith = arith ? arith : KINT; - } - - if ((!properdecl && scls && !(st->kind == DFUNCPARAM && scls == SCREGISTER)) || (scls == SCAUTO && st->kind == DTOPLEVEL)) - error(&tk.span, "storage class specifier %'tk is not allowed here", &tk); - else - st->scls |= scls; - - joinspan(&span.ex, tk.span.ex); - lex(cm, &tk); - } -End: - if (pspan) *pspan = span; - - if (st->scls && properdecl) { - if (popcnt(st->scls) > 1) - error(&span, "invalid combination of storage class specifiers"); - } - if (st->base.t && arith) { - /* combining arith type specifiers and other types */ - Bad: - error(&span, "invalid type specifiers"); - st->base = mktype(TYINT); - } else if (!st->base.t && arith) { - enum typetag t; - if (arith == KFLOAT) - t = TYFLOAT; - else if (arith == KDOUBLE) - t = TYDOUBLE; - else if (arith == (KLONG | KDOUBLE)) - t = TYLDOUBLE; - else if (arith == KBOOL) - t = TYBOOL; - else if (arith == KCHAR) - t = TYCHAR; - else if (arith == (KSIGNED | KCHAR)) - t = TYSCHAR; - else if (arith == (KUNSIGNED | KCHAR)) - t = TYUCHAR; - else if ((arith & ~KINT & ~KSIGNED) == KSHORT) - t = TYSHORT; - else if ((arith & ~KINT) == (KUNSIGNED | KSHORT)) - t = TYUSHORT; - else if ((arith & ~KINT & ~KSIGNED) == 0) - t = TYINT; - else if ((arith & ~KINT) == KUNSIGNED) - t = TYUINT; - else if ((arith & ~KINT & ~KSIGNED) == KLONG) - t = TYLONG; - else if ((arith & ~KINT) == (KUNSIGNED | KLONG)) - t = TYULONG; - else if ((arith & ~KINT & ~KSIGNED) == KLONGLONG) - t = TYVLONG; - else if ((arith & ~KINT) == (KUNSIGNED | KLONGLONG)) - t = TYUVLONG; - else if (arith == (KCOMPLEX | KFLOAT)) - t = TYCOMPLEXF; - else if (arith == (KCOMPLEX | KDOUBLE)) - t = TYCOMPLEX; - else if (arith == (KCOMPLEX | KLONG | KDOUBLE)) - t = TYCOMPLEXL; - else - goto Bad; - st->base = mktype(t ? t : TYINT); - } else if (!st->base.t) { - if (ccopt.cstd < STDC23 && peek(cm, NULL) == TKIDENT) { - if (ccopt.cstd > STDC89) - warn(&tk.span, "type implicitly declared as int"); - st->base = mktype(TYINT); - } else { - error(&tk.span, "type specifier missing"); - } - } -} - -/* circular doubly linked list used to parse declarators */ -enum { EARRAYUNSIZED = 0xFF /* for count.t */ }; -static struct decllist { - struct decllist *prev, *next; - uchar t; /* TYPTR, TYARRAY or TYFUNC */ - union { - uchar qual; /* TYPTR */ - struct expr count; /* TYARRAY */ - struct { /* TYFUNC */ - union type *param; - internstr *pnames; - struct span *pspans; - uchar *pqual; - short npar; - bool kandr : 1, variadic : 1; - }; - }; - struct span span; -} decltmp[64], *declfreelist; -static bool usingdeclparamtmp; -static union type declparamtmp[16]; -static internstr declpnamestmp[16]; -static struct span declpspanstmp[16]; -static uchar declpqualtmp[16]; - -static void -declinsert(struct decllist *list, const struct decllist *node) -{ - struct decllist *pnode = declfreelist; - if (!pnode) fatal(NULL, "too many nested declarators"); - declfreelist = declfreelist->next; - *pnode = *node; - pnode->next = list->next; - pnode->prev = list; - list->next->prev = pnode; - list->next = pnode; -} - -static int -cvqual(struct comp *cm) -{ - struct token tk; - int q = 0; - while (match(cm, &tk, TKWconst) || match(cm, &tk, TKWvolatile) || match(cm, &tk, TKWrestrict)) - q |= tk.t == TKWconst ? QCONST : tk.t == TKWvolatile ? QVOLATILE : 0; - return q; -} - -static void -decltypes(struct comp *cm, struct decllist *list, internstr *name, struct span *span, struct span *namespan) -{ - struct token tk; - struct decllist *ptr, node; - - while (match(cm, &tk, '*')) { - node.t = TYPTR; - node.qual = cvqual(cm); - node.span = tk.span; - declinsert(list, &node); - joinspan(&span->ex, tk.span.ex); - } - ptr = list->next; - switch (peek(cm, &tk)) { - case '(': - lex(cm, &tk); - if (isdecltok(cm)) { - goto Func; - } else if (match(cm, &tk, ')')) { - /* T () is K&R func proto */ - node.span = tk.span; - node.t = TYFUNC; - node.param = NULL; - node.pqual = NULL; - node.pnames = NULL; - node.pspans = NULL; - node.variadic = 0; - node.kandr = 1; - node.npar = 0; - declinsert(ptr->prev, &node); - joinspan(&span->ex, tk.span.ex); - break; - } else { - decltypes(cm, list, name, span, namespan); - expect(cm, ')', NULL); - joinspan(&span->ex, tk.span.ex); - } - break; - case TKIDENT: - if (!name) - error(&tk.span, "unexpected identifier in type name"); - else { - *name = tk.name; - *namespan = tk.span; - } - lex(cm, &tk); - joinspan(&span->ex, tk.span.ex); - break; - default: - if (name) - *name = NULL; - } - for (;;) { - if (match(cm, &tk, '[')) { - node.span = tk.span; - int q = 0; - bool statik = 0; - if (in_range(peek(cm, &tk), TKWBEGIN_, TKWEND_)) { - q = cvqual(cm); - statik = match(cm, NULL, TKWstatic); - q |= cvqual(cm); - } - (void)q, (void)statik; /* stub */ - - if (match(cm, &tk, ']')) { - node.count.t = EARRAYUNSIZED; - } else { - node.count = arraycountexpr(cm); - peek(cm, &tk); - joinspan(&node.span.ex, tk.span.ex); - expect(cm, ']', NULL); - } - node.t = TYARRAY; - declinsert(ptr->prev, &node); - joinspan(&span->ex, node.span.ex); - } else if (match(cm, &tk, '(')) Func: { - vec_of(union type) params = {0}; - vec_of(uchar) qual = {0}; - vec_of(internstr) names = {0}; - vec_of(struct span) spans = {0}; - - if (!usingdeclparamtmp) { - usingdeclparamtmp = 1; - vinit(¶ms, declparamtmp, countof(declparamtmp)); - vinit(&qual, declpqualtmp, countof(declpqualtmp)); - vinit(&names, declpnamestmp, countof(declpnamestmp)); - vinit(&spans, declpspanstmp, countof(declpspanstmp)); - } - - node.span = tk.span; - node.kandr = 0; - node.variadic = 0; - if (ccopt.cstd < STDC23 && !isdecltok(cm)) { - node.kandr = 1; - } - - if (!match(cm, &tk, ')')) for (;;) { - if (match(cm, &tk, TKDOTS)) { - node.variadic = 1; - expect(cm, ')', NULL); - break; - } - if (node.kandr) { - if (match(cm, &tk, TKIDENT)) { - vpush(¶ms, mktype(TYINT)); - vpush(&names, tk.name); - vpush(&spans, tk.span); - } else error(&tk.span, "expected identifier"); - } else if (!isdecltok(cm) && peek(cm, &tk) != TKIDENT) { - error(&tk.span, "expected parameter declarator"); - } else { - struct declstate st = { DFUNCPARAM }; - struct decl decl; - decl = pdecl(&st, cm); - decl.ty = typedecay(decl.ty); - vpush(¶ms, decl.ty); - vpush(&names, decl.name); - vpush(&spans, decl.span); - vpush(&qual, decl.qual); - if (decl.ty.t == TYVOID) { - if (params.n > 1 || decl.qual || decl.name || peek(cm, &tk) != ')') { - error(&decl.span, "function parameter #%d has void type", - params.n, decl.ty, qual.p[params.n-1]); - } - } - } - peek(cm, &tk); - joinspan(&node.span.ex, tk.span.ex); - if (!match(cm, &tk, ',')) { - expect(cm, ')', "or `,'"); - break; - } - } else { - if (ccopt.cstd < STDC23) node.kandr = 1; - } - if (node.kandr && ccopt.cstd != STDC89 && params.n > 0) { - warn(&node.span, "K&R function prototype is deprecated"); - } else if (params.n == 1 && params.p[0].t == TYVOID && !qual.p[0] && !names.p[0]) { /* (void) */ - vfree(¶ms); - vfree(&names); - vfree(&spans); - vfree(&qual); - } - node.t = TYFUNC; - node.param = params.n ? params.p : NULL; - node.pqual = qual.n ? qual.p : NULL; - node.pnames = params.n ? names.p : NULL; - 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 span span0) { - struct decl decl = { st->base, st->scls, .qual = st->qual, .align = 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 < countof(decltmp); ++i) { - decltmp[i].next = declfreelist; - declfreelist = &decltmp[i]; - } - } - - 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"); - } - for (l = list.prev; l != &list; l = l->prev) { - switch (l->t) { - case TYPTR: - decl.ty = mkptrtype(decl.ty, decl.qual); - decl.qual = l->qual; - break; - case TYARRAY: - if (isincomplete(decl.ty)) - error(&l->span, "array has incomplete element type '%ty'", decl.ty); - else if (decl.ty.t == TYFUNC) - error(&l->span, "array has element has function type '%ty'", decl.ty); - if (l->count.t == EARRAYUNSIZED) /* unsized '[]' */ - decl.ty = mkarrtype(decl.ty, decl.qual, 0); - else { - uint n = 0; - struct expr *ex = &l->count; - if (!ex->t) { /* ['*'] */ - if (l->prev != &list) error(&l->span, "[*] array declarator is not allowed here"); - } else if (!eval(ex, EVINTCONST)) { - error(&ex->span, "array length is not an integer constant"); - } else if (issigned(ex->ty) && ex->i < 0) { - error(&ex->span, "array length is negative"); - } else if (ex->u > (1ull << (8*sizeof n)) - 1) { - error(&ex->span, "array too long (%ul)", ex->u); - } else if (ex->u == 0) { - /* when struct field, silently accept zero-length as synonym of '[]' for flexible array member */ - if (l->prev != &list || st->kind != DFIELD) { - warn(&ex->span, "array cannot have zero length"); - } - } else { - n = ex->u; - } - decl.ty = mkarrtype(decl.ty, decl.qual, n); - } - break; - case TYFUNC: - 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 '%ty'", decl.ty); - if (l->kandr && ccopt.cstd > STDC89 && (ccopt.cstd >= STDC23 || ccopt.pedant)) - warn(&l->span, "function declaration without a prototype is deprecated"); - decl.ty = mkfntype(decl.ty, l->npar, l->param, l->kandr, l->variadic); - if (l->param != declparamtmp) free(l->param); - if (l->prev == &list && l->npar) { /* last */ - st->pnames = alloccopy(&cm->fnarena, l->pnames, l->npar * sizeof(char *), 0); - st->pspans = alloccopy(&cm->fnarena, l->pspans, l->npar * sizeof(struct span), 0); - st->pqual = l->pqual ? alloccopy(&cm->fnarena, l->pqual, l->npar, 1) : NULL; - decl.inlin = st->fninline; - decl.noret = st->fnnoreturn; - } - if (l->pqual != declpqualtmp) free(l->pqual); - if (l->pnames != declpnamestmp) free(l->pnames); - if (l->pspans != declpspanstmp) free(l->pspans); - if (l->param == declparamtmp) usingdeclparamtmp = 0; - decl.qual = 0; - break; - } - - l->next = declfreelist; - declfreelist = l; - } - if (st->kind != DCASTEXPR && decl.name) - decl.span = namespan; - return decl; -} - -static void -pstaticassert(struct comp *cm, struct span *span) -{ - struct expr ex; - struct token tk, msg = {0}; - - /* _Static_assert '(' <expr> [ ',' <strlit> ] ')' ';' */ - expect(cm, '(', NULL); - ex = expr(cm); - peek(cm, &tk); - if (match(cm, &tk, ',')) { - peek(cm, &msg); - expect(cm, TKSTRLIT, NULL); - } - peek(cm, &tk); - expect(cm, ')', NULL); - expect(cm, ';', NULL); - - joinspan(&span->ex, tk.span.ex); - if (!msg.t && ccopt.cstd == STDC11) - warn(span, "static assert without message is a C23 extension"); - if (!eval(&ex, EVINTCONST)) { - error(&ex.span, "static assert expression is not an integer constant"); - } else if (iszero(ex)) { - if (msg.t) - error(&ex.span, "static assertion failed: %'S", msg.s, msg.len); - else - error(&ex.span, "static assertion failed"); - } -} - -static struct decl -pdecl(struct declstate *st, struct comp *cm) { - struct token tk; - struct decl decl; - bool properdecl = st->kind == DTOPLEVEL || st->kind == DFUNCVAR; - bool first = 0; - - assert(!st->funcdef); - - if (st->varini || st->bitf) { - memset(&decl, 0, sizeof decl); - goto AfterIniBitf; - } - decl.sym = NULL; - - if (st->base0) goto DeclSpec; - if (!st->base.t) { - if (properdecl && (match(cm, &tk, TKW_Static_assert) || match(cm, &tk, TKWstatic_assert))) { - pstaticassert(cm, &tk.span); - return (struct decl){0}; - } - first = 1; - if (match(cm, &tk, ';')) { - st->empty = 1; - return (struct decl){.span = tk.span}; - } - - DeclSpec: - st->base0 = 0; - while (attrspec(cm, &st->attr)) ; - declspec(st, cm, &decl.span); - } else { - peek(cm, &tk); - decl.span = tk.span; - } - if (st->scls == SCTYPEDEF) properdecl = 0; - - if (first && st->tagdecl && match(cm, &tk, ';')) { - decl = (struct decl) { st->base, st->scls, st->qual, .align = st->align, .span = decl.span }; - return decl; - } else if (st->kind == DFIELD && match(cm, &tk, ':')) { - decl = (struct decl) { st->base, st->scls, st->qual, .align = st->align, .span = decl.span }; - st->bitf = 1; - return decl; - } - decl = declarator(st, cm, decl.span); - if (decl.ty.t != TYFUNC && st->fninline) - error(&decl.span, "`inline' used on non-function declaration"); - if (decl.ty.t != TYFUNC && st->fnnoreturn) - error(&decl.span, "`_Noreturn' used on non-function declaration"); - /* trailing attributes */ - if (st->kind == DTOPLEVEL || st->kind == DFUNCVAR) { - while (attrspec(cm, &st->attr)) ; - if (match(cm, NULL, TKW__asm__) && expect(cm, '(', NULL)) { - peek(cm, &tk); - if (expect(cm, TKSTRLIT, "asm symbol name")) { - decl.sym = intern_(tk.s, tk.len); - } - expect(cm, ')', NULL); - } - while (attrspec(cm, &st->attr)) ; - } - - if (properdecl && match(cm, &tk, '=')) { - st->varini = 1; - return decl; - } else if (first && decl.ty.t == TYFUNC && match(cm, &tk, '{')) { - st->funcdef = 1; - return decl; - } else if (st->kind == DFIELD && match(cm, &tk, ':')) { - st->bitf = 1; - return decl; - } - -AfterIniBitf: - st->varini = st->bitf = 0; - st->more = 0; - if (st->kind != DCASTEXPR && st->kind != DFUNCPARAM) { - if (match(cm, &tk, ',')) - st->more = 1; - else expect(cm, st->kind == DFUNCPARAM ? ')' : ';', "or `,'"); - } - - return decl; -} - -/*****************/ -/* IR Generation */ -/*****************/ - -static inline union ref -exprvalue(struct function *fn, const struct expr *ex) -{ - return compileexpr(fn, ex, /*discard*/ 0); -} -static inline void -expreffects(struct function *fn, const struct expr *ex) -{ - compileexpr(fn, ex, /*discard*/ 1); -} - -static void -structcopy(struct function *fn, union type ty, union ref dst, union ref src) -{ - union irtype typ = mkirtype(ty); - addinstr(fn, mkarginstr(typ, dst)); - addinstr(fn, mkarginstr(typ, src)); - addinstr(fn, mkintrin(INstructcopy, 0, 2)); -} - -static union ref -structreturn(struct function *fn, const struct expr *src) -{ - return expraddr(fn, src); -} - -static union ref compilecall(struct function *fn, const struct expr *ex); - -static internstr -mkhiddensym(const char *fnname, const char *name, int id) -{ - char buf[200]; - struct wbuf wbuf = MEMBUF(buf, sizeof buf); - assert(id > 0); - if (fnname) - bfmt(&wbuf, "%s.%s.%d", fnname, name, id-1); - else - bfmt(&wbuf, "%s.%d", name, id-1); - ioputc(&wbuf, 0); - assert(!wbuf.err); - return intern(buf); -} - -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); - -union ref -expraddr(struct function *fn, const struct expr *ex) -{ - struct decl *decl; - union ref r; - - switch (ex->t) { - case ESYM: - decl = &declsbuf.p[ex->decl]; - assert(decl != NULL); - switch (decl->scls) { - case SCAUTO: case SCREGISTER: - assert(decl->id >= 0); - return mkref(RTMP, decl->id); - case SCEXTERN: case SCNONE: case SCSTATIC: - return mksymref(decl->sym, (SFUNC & -(decl->ty.t == TYFUNC)) | (SLOCAL & -(decl->scls == SCSTATIC || decl->isdef))); - default: - assert(0); - } - break; - case ESSYMREF: - return irbinop(fn, Oadd, KPTR, - mksymref(ex->ssym.sym, (SFUNC & -ex->ssym.func) | (SLOCAL & -ex->ssym.local)), - mkintcon(KPTR, ex->ssym.off)); - case ESTRLIT: - /* XXX endian for wide strs */ - return mkdatref(NULL, ex->ty, typesize(ex->ty), typealign(ex->ty), ex->s.p, ex->s.n * typesize(typechild(ex->ty)), /*deref*/0, fn != NULL); - case EDEREF: - return exprvalue(fn, ex->sub); - case EGETF: - r = expraddr(fn, ex->sub); - assert(ex->fld.bitsiz == 0); - return irbinop(fn, Oadd, KPTR, r, mkintcon(KI32, ex->fld.off)); - case ESET: - assert(isagg(ex->ty)); - r = expraddr(fn, &ex->sub[1]); - structcopy(fn, ex->ty, expraddr(fn, &ex->sub[0]), r); - return r; - case ECALL: - assert(isagg(ex->ty)); - return compilecall(fn, ex); - case EVAARG: - assert(isagg(ex->ty)); - return builtin_va_arg_comp(fn, ex, 0); - case EINIT: - if (fn) { - /* compound literal, allocate temp */ - r = addinstr(fn, mkalloca(typesize(ex->ty), typealign(ex->ty))); - geninit(fn, ex->ty, r, ex); - return r; - } else { - /* emit static dat */ - static int id; - struct initparser ip[1] = {0}; - union type ty = ex->ty; - internstr sym = mkhiddensym(NULL, ".LC", ++id); - ip->sec = Sdata; /* TODO put in rodata if possible */ - ip->ev = EVSTATICINI; - assert(!isincomplete(ty)); - ip->off = objnewdat(sym, ip->sec, 0, typesize(ty), typealign(ty)); - if (!iniwriterec(NULL, ip, 0, (struct expr *)ex)) - error(&ex->span, "cannot not evaluate expression statically"); - return mksymref(sym, 0); - } - case ESEQ: - expreffects(fn, &ex->sub[0]); - return expraddr(fn, &ex->sub[1]); - case ECOND: - assert(isagg(ex->ty)); - return condexprvalue(fn, ex, 0); - default: - assert(!"lvalue?>"); - } - -} - -static union ref -genload(struct function *fn, union type t, union ref ref, bool volatyl) -{ - struct instr ins = {0}; - - assert(isscalar(t)); - ins.cls = type2cls[scalartypet(t)]; - assert(ins.cls); - switch (typesize(t)) { - case 1: ins.op = issigned(t) ? Oloads8 : Oloadu8; break; - case 2: ins.op = issigned(t) ? Oloads16 : Oloadu16; break; - case 4: ins.op = isflt(t) ? Oloadf32 : issigned(t) ? Oloads32 : Oloadu32; break; - case 8: ins.op = isflt(t) ? Oloadf64 : Oloadi64; break; - default: assert(0); - } - ins.l = ref; - ins.keep = volatyl; - return addinstr(fn, ins); -} - -static union ref -genstore(struct function *fn, union type t, union ref ptr, union ref val) -{ - struct instr ins = {0}; - - assert(isscalar(t)); - switch (typesize(t)) { - case 1: ins.op = Ostorei8; break; - case 2: ins.op = Ostorei16; break; - case 4: ins.op = isflt(t) ? Ostoref32 : Ostorei32; break; - case 8: ins.op = isflt(t) ? Ostoref64 : Ostorei64; break; - default: assert(0); - } - ins.l = ptr; - ins.r = val; - return addinstr(fn, ins); -} - -static void genbitfstore(struct function *fn, const union type ty, union ref addr, - const struct exgetfld *fld, union ref tmp, union ref val); - -static void -geninit(struct function *fn, union type t, union ref dst, const struct expr *src) -{ - union ref adr; - if (src->t == EINIT) { - struct init *ini = src->init; - uint siz = typesize(t); - uint align = typealign(t); - struct bitset azero[1] = {0}; - - if (BSSIZE(siz) <= countof(ini->zero)) { - for (int i = 0; i < siz; i += align) { - for (int j = 0; j < align; ++j) { - if (bstest(ini->zero, i + j)) { - bsset(azero, i); - break; - } - } - } - if (bscount(azero, countof(azero)) < 32) { - /* write individual zeros at non initialized gaps */ - for (uint i = 0; bsiter(&i, azero, countof(azero)) && i < siz; i += align) { - adr = irbinop(fn, Oadd, KPTR, dst, mkref(RICON, i)); - addinstr(fn, mkinstr(Ostorei8 + ilog2(align), 0, .l = adr, .r = ZEROREF)); - } - } else { - goto Memset0; - } - } else Memset0: { - /* memset(dst,0,siz) */ - /* TODO make it into an intrinsic */ - struct instr call = { Ocall, KPTR }; - addinstr(fn, mkarginstr(cls2type(KPTR), dst)); - addinstr(fn, mkarginstr(cls2type(KI32), ZEROREF)); - addinstr(fn, mkarginstr(cls2type(type2cls[targ_sizetype]), mkintcon(type2cls[targ_sizetype], siz))); - call.l = mksymref(istr_memset, 1); - call.r = mkcallarg(cls2type(KPTR), 3, -1); - addinstr(fn, call); - } - for (struct initval *val = ini->vals; val; val = val->next) { - uint off = val->off; - struct expr *ex = &val->ex; - adr = irbinop(fn, Oadd, KPTR, dst, mkref(RICON, off)); - if (ex->t == EINIT || ex->t == ESTRLIT) { - geninit(fn, ex->ty, adr, ex); - } else if (isagg(ex->ty)) { - structcopy(fn, ex->ty, adr, expraddr(fn, ex)); - } else if (!val->bitsiz) { - genstore(fn, ex->ty, adr, exprvalue(fn, ex)); - } else { - union ref q = exprvalue(fn, ex); - genbitfstore(fn, ex->ty, adr, &(struct exgetfld){0, val->bitsiz, val->bitoff}, NOREF, q); - } - } - } else if (src->t == ESTRLIT) { - union type ctyp = typechild(src->ty); - uint csiz = typesize(ctyp); - adr = dst; - for (uint i = 0; i < src->s.n; ++i) { - if (csiz == 1) - genstore(fn, ctyp, adr, mkref(RICON, src->s.p[i])); - else if (csiz == 2) - genstore(fn, ctyp, adr, mkref(RICON, src->s.w16[i])); - else - genstore(fn, ctyp, adr, mkintcon(KI32, src->s.w32[i])); - adr = irbinop(fn, Oadd, KPTR, dst, mkref(RICON, (i+1)*csiz)); - } - genstore(fn, ctyp, adr, ZEROREF); /* null term */ - } else assert(0); -} - -static bool -isboollike(struct function *fn, union ref r) -{ - struct instr *ins; - if (r.t == RICON && (r.i == 0 || r.i == 1)) return 1; - if (r.t != RTMP) return 0; - ins = &instrtab[r.i]; - if (oiscmp(ins->op)) /* these instrs already have output range of [0,1] */ - return 1; - if (ins->op == Ophi) { /* check if all the phi args are boollike */ - struct block *blk; - union ref *phi = NULL; - for (blk = fn->curblk; phi == NULL; blk = blk->lprev) { - /* find blk that defines phi */ - assert(blk != fn->entry); - for (int i = 0; i < blk->phi.n; ++i){ - if (blk->phi.p[i] == r.i) { - phi = phitab.p[ins->l.i]; - break; - } - } - } - for (int i = 0; i < blk->npred; ++i) { - if (!isboollike(fn, phi[i])) { - return 0; - } - } - return 1; - } - if (in_range(ins->op, Oand, Oxor)) - return isboollike(fn, ins->l) && isboollike(fn, ins->r); - if (ins->op == Ocopy || in_range(ins->op, Oexts8, Oextu32)) - return isboollike(fn, ins->l); - if (ins->op == Oparam) - return typedata[fn->fnty.dat].param[ins->l.i].t == TYBOOL; - return 0; -} - -union ref -scalarcvt(struct function *fn, union type to, union type from, union ref ref) -{ - enum irclass kto = type2cls[scalartypet(to)], kfrom = type2cls[scalartypet(from)]; - enum op op; - if (to.bits == from.bits) return ref; - assert(kto && kfrom); - if (kto == kfrom && to.t != TYBOOL) return ref; - - if (kisflt(kto) || kisflt(kfrom)) { - if (ref.t == RICON) { - assert(kisflt(kto) && kisint(kfrom)); - return mkfltcon(kto, kto == KF32 ? (float)ref.i : (double)ref.i); - } - if (kisflt(kto) && kfrom == KI32) op = issigned(from) ? Ocvts32f : Ocvtu32f; - else if (to.t == TYBOOL && kisflt(kfrom)) return irbinop(fn, Oneq, kfrom, ref, mkfltcon(kfrom, 0.0)); - else if (kisflt(kto) && kfrom == KI64) op = issigned(from) ? Ocvts64f : Ocvtu64f; - else if (kto == KF64 && kfrom == KF32) op = Ocvtf32f64; - else if (kto == KF32 && kfrom == KF64) op = Ocvtf64f32; - else if (kfrom == KF32) op = issigned(to) ? Ocvtf32s : Ocvtf32u; - else if (kfrom == KF64) op = issigned(to) ? Ocvtf64s : Ocvtf64u; - else assert(0); - } else { - if (to.t == TYBOOL) { - if (from.t == TYBOOL) return ref; - if (isboollike(fn, ref)) - return kfrom == KI32 ? ref : scalarcvt(fn, mktype(TYINT), from, ref); - return irbinop(fn, Oneq, kfrom, ref, ZEROREF); - } - else if (kfrom == KI32 && issigned(from)) op = Oexts32; - else if (kfrom == KI32) op = Oextu32; - else if (kto == KI32 && isintcon(ref)) - return issigned(to) ? mkintcon(kto, (int)intconval(ref)) : mkintcon(kto, (uint)intconval(ref)); - else op = Ocopy; - } - return irunop(fn, op, kto, ref); -} - -static union ref -narrow(struct function *fn, enum irclass to, union type t, union ref ref, uint bitsiz) -{ - enum typetag tt = scalartypet(t); - assert(isscalar(t)); - if (targ_primsizes[tt] < cls2siz[to]) { - enum op op; - if (isfltt(tt)) { - assert(to == KF32 && tt >= TYDOUBLE); - op = Ocvtf64f32; - } else { - static const enum op ext[5][2] = { - [1] = {Oextu8, Oexts8}, [2] = {Oextu16, Oexts16}, [4] = {Oextu32, Oexts32} - }; - op = ext[targ_primsizes[tt]][issignedt(tt)]; - } - ref = irunop(fn, op, to, ref); - } - if (bitsiz) { - assert(kisint(to) && isintt(tt) && bitsiz < 8*targ_primsizes[tt]); - if (!issignedt(tt)) { - ref = irbinop(fn, Oand, to, ref, mkintcon(to, (1ull<<bitsiz)-1)); - } else { - uint sh = 8*cls2siz[to] - bitsiz; - ref = irbinop(fn, Oshl, to, ref, mkref(RICON, sh)); - ref = irbinop(fn, Osar, to, ref, mkref(RICON, sh)); - } - } - return ref; -} - -union ref -genptroff(struct function *fn, enum op op, uint siz, union ref ptr, - union type t, union ref idx) -{ - uint cls = type2cls[targ_sizetype]; - union ref off; - assert(siz); - - idx = scalarcvt(fn, mktype(targ_sizetype), t, idx); - if (siz == 1) off = idx; - else if (idx.t == RICON) { - if (op == Osub) op = Oadd, idx.i = -idx.i; - off = mkintcon(cls, idx.i * (int)siz); - } else { - off = irbinop(fn, Omul, cls, idx, mkintcon(cls, siz)); - } - assert(in_range(op, Oadd, Osub)); - return irbinop(fn, op, KPTR, ptr, off); -} - -union ref -genptrdiff(struct function *fn, uint siz, union ref a, union ref b) -{ - uint cls = type2cls[targ_ptrdifftype]; - assert(siz > 0); - a = irbinop(fn, Osub, cls, a, b); - if (siz == 1) return a; - else if (ispo2(siz)) - return irbinop(fn, Osar, cls, a, mkintcon(cls, ilog2(siz))); - else - return irbinop(fn, Odiv, cls, a, mkintcon(cls, siz)); -} - -/* used to emit the jumps in an in if (), while (), etc condition */ -static void -condjump(struct function *fn, const struct expr *ex, struct block *tr, struct block *fl) -{ - struct block *next, *next2; -Recur: - for (; ex->t == ESEQ; ex = &ex->sub[1]) - expreffects(fn, &ex->sub[0]); - if (ex->t == ELOGAND) { - next = newblk(fn); - condjump(fn, &ex->sub[0], next, fl); - useblk(fn, next); - ex = &ex->sub[1]; - goto Recur; - } else if (ex->t == ELOGIOR) { - next = newblk(fn); - condjump(fn, &ex->sub[0], tr, next); - useblk(fn, next); - ex = &ex->sub[1]; - goto Recur; - } else if (ex->t == ECOND) { - next = newblk(fn); - next2 = newblk(fn); - condjump(fn, &ex->sub[0], next, next2); - useblk(fn, next); - condjump(fn, &ex->sub[1], tr, fl); - useblk(fn, next2); - condjump(fn, &ex->sub[2], tr, fl); - } else if (ex->t == ELOGNOT) { - Negate: - /* swap tr,fl */ - next = tr; - tr = fl; - fl = next; - ex = &ex->sub[0]; - goto Recur; - } else if (ex->t == EEQU && isnullpo(&ex->sub[1])) { /* == 0 */ - goto Negate; - } else if (ex->t == ENEQ && isnullpo(&ex->sub[1])) { /* != 0 */ - ex = &ex->sub[0]; - goto Recur; - } else { - putcondbranch(fn, exprvalue(fn, ex), tr, fl); - } -} - -struct condphis { - union type typ; - vec_of(union ref) ref; -}; - -static void -condexprrec(struct function *fn, const struct expr *ex, struct condphis *phis, struct block *end) -{ -Recur: - for (; ex->t == ESEQ; ex = &ex->sub[1]) - expreffects(fn, &ex->sub[0]); - int prevpred = end->npred; - if (ex->t == ELOGAND) { - struct block *tr = newblk(fn); - condjump(fn, &ex->sub[0], tr, end); - assert(prevpred <= end->npred); - if (phis) for (int n = end->npred - prevpred; n > 0; --n) { - vpush(&phis->ref, mkref(RICON, 0)); - } - useblk(fn, tr); - ex = &ex->sub[1]; - goto Recur; - } else if (ex->t == ELOGIOR) { - struct block *fl = newblk(fn); - condjump(fn, &ex->sub[0], end, fl); - assert(prevpred <= end->npred); - if (phis) for (int n = end->npred - prevpred; n > 0; --n) { - vpush(&phis->ref, mkref(RICON, 1)); - } - useblk(fn, fl); - ex = &ex->sub[1]; - goto Recur; - } else if (ex->t == ECOND) { - struct block *tr = newblk(fn), *fl = newblk(fn); - condjump(fn, &ex->sub[0], tr, fl); - useblk(fn, tr); - condexprrec(fn, &ex->sub[1], phis, end); - useblk(fn, fl); - ex = &ex->sub[2]; - goto Recur; - } else { - if (!phis) { - expreffects(fn, ex); - } else { - union ref val = exprvalue(fn, ex); - if (isscalar(phis->typ)) - val = scalarcvt(fn, phis->typ, ex->ty, val); - else assert(ex->ty.bits == phis->typ.bits); - vpush(&phis->ref, val); - } - putbranch(fn, end); - } -} - -/* the naive way to generate something like a ? b : c ? d : e, uses multiple phis, - * this code reduces such nested conditional expressions into one phi */ -static union ref -condexprvalue(struct function *fn, const struct expr *ex, bool discard) -{ - union ref refbuf[8]; - struct condphis phis = { ex->t == ECOND ? ex->ty : mktype(TYBOOL), VINIT(refbuf, countof(refbuf)) }; - struct block *dst = newblk(fn); - condexprrec(fn, ex, discard ? NULL : &phis, dst); - useblk(fn, dst); - if (discard) return NOREF; - enum irclass k; - if (isscalar(ex->ty)) { - k = type2cls[scalartypet(ex->ty)]; - assert(k); - } else { - assert(isagg(ex->ty) || isptrcvt(ex->ty)); - k = KPTR; - } - union ref r = addphi(fn, k, phis.ref.p); - vfree(&phis.ref); - return r; -} - -static union ref -compilecall(struct function *fn, const struct expr *ex) -{ - struct instr ins = {0}; - struct expr *sub = ex->sub; - const struct typedata *td = &typedata[sub[0].ty.dat]; - struct instr insnsbuf[10]; - vec_of(struct instr) insns = VINIT(insnsbuf, countof(insnsbuf)); - - if (sub[0].t == ESYM && declsbuf.p[sub[0].decl].isbuiltin) { - return declsbuf.p[sub[0].decl].builtin->comp(fn, (struct expr *)ex, 0); - } - ins.op = Ocall; - if (isagg(ex->ty)) { - ins.cls = KPTR; - } else { - assert(isscalar(ex->ty) || ex->ty.t == TYVOID); - ins.cls = type2cls[scalartypet(ex->ty)]; - assert(ins.cls || ex->ty.t == TYVOID); - } - ins.l = exprvalue(fn, &sub[0]); - for (int i = 0; i < ex->narg; ++i) { - struct expr *arg = &sub[i+1]; - union type ty = i < td->nmemb ? td->param[i] : argpromote(arg->ty); - union ref r = scalarcvt(fn, ty, typedecay(arg->ty), exprvalue(fn, arg)); - vpush(&insns, mkarginstr(mkirtype(ty), r)); - } - for (int i = 0; i < insns.n; ++i) - addinstr(fn, insns.p[i]); - vfree(&insns); - ins.r = mkcallarg(mkirtype(ex->ty), ex->narg, td->variadic ? td->nmemb : td->kandr ? 0 : -1); - union ref r = addinstr(fn, ins); - if (sub[0].t == ESYM && declsbuf.p[sub[0].decl].noret) /* trap if noreturn func returns */ - puttrap(fn); - return r; -} - -static union ref -genbitfload(struct function *fn, union ref *tmpval, const union type ty, union ref *addr, - const struct exgetfld *fld, bool volatyl) -{ - enum irclass k = type2cls[scalartypet(ty)]; - uint off = fld->off, bitsiz = fld->bitsiz, bitoff = fld->bitoff; - union ref tmp; - uvlong mask; - - assert(k); - *addr = irbinop(fn, Oadd, KPTR, *addr, mkintcon(KI32, off)); - tmp = genload(fn, ty, *addr, volatyl); - if (tmpval) *tmpval = tmp; - if (!issigned(ty)) { - /* shift right and mask */ - tmp = irbinop(fn, Oslr, k, tmp, mkref(RICON, bitoff)); - if (bitsiz < 8*typesize(ty)) { - mask = bitsiz == 64 ? -1ull : (1ull << bitsiz) - 1; - tmp = irbinop(fn, Oand, k, tmp, mkintcon(k, mask)); - } - } else { - /* shift left and shift right arithmetic to propagate sign bit */ - int sh = 8*cls2siz[k] - bitsiz - bitoff; - tmp = irbinop(fn, Oshl, k, tmp, mkref(RICON, sh)); - sh += bitoff; - tmp = irbinop(fn, Osar, k, tmp, mkref(RICON, sh)); - } - return tmp; -} - -static void -genbitfstore(struct function *fn, const union type ty, union ref addr, - const struct exgetfld *fld, union ref tmp, union ref val) -{ - enum irclass k = type2cls[scalartypet(ty)]; - uint off = fld->off, bitsiz = fld->bitsiz, bitoff = fld->bitoff; - uint bittypesize = 8*typesize(ty); - uvlong mask; - - assert(k); - if (!tmp.bits) { - addr = irbinop(fn, Oadd, KPTR, addr, mkintcon(KPTR, off)); - tmp = genload(fn, ty, addr, 0); - } - mask = (bitsiz == 64 ? -1ull : (1ull << bitsiz) - 1) << bitoff; - - /* mask out bits in existing container */ - tmp = irbinop(fn, Oand, k, tmp, mkintcon(k, ~mask)); - - /* shift and mask source value */ - if (isintcon(val)) { - val = mkintcon(k, ((uvlong)intconval(val) << bitoff) & mask); - } else { - val = irbinop(fn, Oshl, k, val, mkref(RICON, bitoff)); - if (bitsiz < bittypesize) - val = irbinop(fn, Oand, k, val, mkintcon(k, mask)); - } - /* combine and write */ - if (bitsiz < bittypesize) - val = irbinop(fn, Oior, k, tmp, val); - genstore(fn, ty, addr, val); -} - -static bool -knowntruthy(bool *t, struct expr *ex) -{ - if (!eval(ex, EVFOLD)) return 0; - - switch (ex->t) { - default: assert(0 && "!scalar?"); - case ENUMLIT: - *t = isflt(ex->ty) ? ex->f != 0.0 : ex->u != 0; - break; - case ESTRLIT: case ESSYMREF: - /* string literals & symbol addresses are always truthy */ - *t = 1; - break; - } - return 1; -} - -union ref -compileexpr(struct function *fn, const struct expr *ex, bool discard) -{ - union type ty; - union ref l, r, q, adr; - uint bitsiz; - enum op op; - enum irclass cls = type2cls[scalartypet(ex->ty)]; - int swp = 0; - struct expr *sub; - - //eval((struct expr *)ex, EVFOLD); - sub = ex->sub; - - if (ex->ty.t != TYVOID && !isscalar(ex->ty)) { - /* fn & array designators evaluate to their address; - * so do aggregates for the purpose of code generation */ - if (isagg(ex->ty) && isincomplete(ex->ty)) - error(&ex->span, "use of incomplete type '%ty'", ex->ty); - return expraddr(fn, ex); - } - switch (ex->t) { - case ENUMLIT: - if (discard) return NOREF; - if (isflt(ex->ty)) - return mkfltcon(cls, ex->f); - return mkintcon(cls, ex->i); - case ESYM: - if (discard && !(ex->qual & QVOLATILE)) return NOREF; - return genload(fn, ex->ty, expraddr(fn, ex), ex->qual & QVOLATILE); - case ESSYMREF: - return expraddr(fn, ex); - case EVAARG: - return builtin_va_arg_comp(fn, ex, discard); - case EGETF: - if (discard && !(ex->qual & QVOLATILE)) return NOREF; - if (ex->fld.bitsiz) { - /* bit-field */ - r = expraddr(fn, ex->sub); - return genbitfload(fn, NULL, ex->ty, &r, &ex->fld, ex->qual & QVOLATILE); - } - return genload(fn, ex->ty, expraddr(fn, ex), ex->qual & QVOLATILE); - case ECAST: - if (ex->ty.t == TYVOID) { - expreffects(fn, sub); - return NOREF; - } - /* fallthru */ - case EPLUS: - r = compileexpr(fn, sub, discard); - if (discard) return NOREF; - r = scalarcvt(fn, ex->ty, sub->ty, r); - if (isint(ex->ty) && (typesize(ex->ty) < typesize(sub->ty) || issigned(ex->ty) != issigned(sub->ty))) - return narrow(fn, type2cls[scalartypet(ex->ty)], ex->ty, r, 0); - return r; - case ENEG: - op = Oneg; - goto Unary; - case ECOMPL: - op = Onot; - Unary: - l = compileexpr(fn, sub, discard); - if (discard) return NOREF; - l = scalarcvt(fn, ex->ty, sub->ty, l); - return irunop(fn, op, cls, l); - case ELOGNOT: - for (; sub->t == ELOGNOT; ex = sub, sub = sub->sub) - swp ^= 1; - op = Oequ + swp; - l = compileexpr(fn, sub, discard); - if (discard) return NOREF; - l = scalarcvt(fn, ex->ty, sub->ty, l); - r = mkintcon(cls, 0); - return irbinop(fn, op, cls, l, r); - case EDEREF: - discard &= (ex->qual & QVOLATILE) == 0; - r = compileexpr(fn, sub, discard); - if (discard) return NOREF; - return genload(fn, ex->ty, r, ex->qual & QVOLATILE); - case EADDROF: - return expraddr(fn, sub); - case EMUL: - op = Omul; - goto BinArith; - case EDIV: - op = isunsigned(ex->ty) ? Oudiv : Odiv; - goto BinArith; - case EREM: - op = issigned(ex->ty) ? Orem : Ourem; - goto BinArith; - case EBAND: - op = Oand; - goto BinArith; - case EXOR: - op = Oxor; - goto BinArith; - case EBIOR: - op = Oior; - goto BinArith; - case ESHL: - op = Oshl; - goto BinArith; - case ESHR: - op = issigned(ex->ty) ? Osar : Oslr; - goto BinArith; - case ESUB: - op = Osub; - goto BinArith; - case EADD: - op = Oadd; - BinArith: - l = compileexpr(fn, &sub[0], discard); - r = compileexpr(fn, &sub[1], discard); - if (discard) return NOREF; - if (op == Osub && isptrcvt(sub[0].ty) && isptrcvt(sub[1].ty)) { - /* ptr - ptr */ - return genptrdiff(fn, typesize(typechild(sub[0].ty)), l, r); - } else if ((op != Oadd && op != Osub) || cls != KPTR) { - /* num OP num */ - l = scalarcvt(fn, ex->ty, sub[0].ty, l); - r = scalarcvt(fn, ex->ty, sub[1].ty, r); - } else { - assert(isptrcvt(sub[0].ty)); - /* ptr +/- num */ - return genptroff(fn, op, typesize(typechild(sub[0].ty)), l, sub[1].ty, r); - } - return irbinop(fn, op, cls, l, r); - case EPOSTINC: - case EPOSTDEC: - op = ex->t == EPOSTINC ? Oadd : Osub; - if (ex->ty.t == TYPTR) - r = mkintcon(type2cls[targ_sizetype], typesize(typechild(ex->ty))); - else - r = isflt(ex->ty) ? mkfltcon(type2cls[ex->ty.t], 1.0) : mkref(RICON, 1); - bitsiz = 0; - if (sub[0].t == EGETF && (bitsiz = sub->fld.bitsiz)) { - union ref tmp; - adr = expraddr(fn, &sub[0].sub[0]); - l = genbitfload(fn, &tmp, sub[0].ty, &adr, &sub[0].fld, sub[0].qual & QVOLATILE); - q = irbinop(fn, op, cls, l, r); - genbitfstore(fn, sub[0].ty, adr, &sub[0].fld, tmp, q); - } else { - adr = expraddr(fn, sub); - l = genload(fn, sub->ty, adr, sub->qual & QVOLATILE); - q = irbinop(fn, op, cls, l, r); - genstore(fn, sub->ty, adr, q); - } - return discard ? NOREF : l; - case EPREINC: - case EPREDEC: - op = ex->t == EPREINC ? Oadd : Osub; - if (ex->ty.t == TYPTR) - r = mkintcon(type2cls[targ_sizetype], typesize(typechild(ex->ty))); - else - r = isflt(ex->ty) ? mkfltcon(type2cls[ex->ty.t], 1.0) : mkref(RICON, 1); - if (sub[0].t == EGETF && (bitsiz = sub->fld.bitsiz)) { - ty = ex->ty; - goto CompoundBitf; - } - adr = expraddr(fn, sub); - l = genload(fn, sub->ty, adr, sub->qual & QVOLATILE); - q = irbinop(fn, op, cls, l, r); - genstore(fn, sub->ty, adr, q); - if (discard) return NOREF; - return narrow(fn, cls, ex->ty, q, 0); - case EEQU: - op = Oequ; - goto Cmp; - case ENEQ: - op = Oneq; - goto Cmp; - case ELTH: - op = Olth; - goto Cmp; - case ELTE: - op = Olte; - goto Cmp; - case EGTH: - op = Ogth; - goto Cmp; - case EGTE: - op = Ogte; - Cmp: - ty = cvtarith(sub[0].ty, sub[1].ty); - if (!ty.t) ty.t = TYPTR; - if (isunsigned(ty) && in_range(op, Olth, Ogte)) - op += Oulth - Olth; - l = compileexpr(fn, &sub[0], discard); - r = compileexpr(fn, &sub[1], discard); - if (discard) return NOREF; - l = scalarcvt(fn, ty, sub[0].ty, l); - r = scalarcvt(fn, ty, sub[1].ty, r); - cls = type2cls[ty.t]; - return irbinop(fn, op, cls, l, r); - case ESET: - assert(isscalar(ex->ty)); - q = scalarcvt(fn, sub[0].ty, sub[1].ty, exprvalue(fn, &sub[1])); - if (sub[0].t == EGETF && (bitsiz = sub[0].fld.bitsiz)) { - /* bit-field */ - adr = expraddr(fn, &sub[0].sub[0]); - genbitfstore(fn, ex->ty, adr, &sub[0].fld, NOREF, q); - } else { - bitsiz = 0; - adr = expraddr(fn, &sub[0]); - genstore(fn, ex->ty, adr, q); - } - if (discard) return NOREF; - return bitsiz ? narrow(fn, cls, sub[0].ty, q, bitsiz) : q; - case ESETMUL: - op = Omul; - goto Compound; - case ESETDIV: - op = isunsigned(ex->ty) ? Oudiv : Odiv; - goto Compound; - case ESETREM: - op = issigned(ex->ty) ? Orem : Ourem; - goto Compound; - case ESETAND: - op = Oand; - goto Compound; - case ESETXOR: - op = Oxor; - goto Compound; - case ESETIOR: - op = Oior; - goto Compound; - case ESETSHL: - op = Oshl; - goto Compound; - case ESETSHR: - op = issigned(ex->ty) ? Osar : Oslr; - goto Compound; - case ESETSUB: - op = Osub; - goto Compound; - case ESETADD: - op = Oadd; - Compound: - ty = in_range(ex->t, ESETSHL, ESETSHR) ? mktype(intpromote(ex->ty.t)) - : cvtarith(sub[0].ty, sub[1].ty); - r = exprvalue(fn, &sub[1]); - if (sub[0].t == EGETF && (bitsiz = sub[0].fld.bitsiz)) { - /* bit-field */ - union ref tmp; - CompoundBitf: - adr = expraddr(fn, &sub[0].sub[0]); - l = genbitfload(fn, &tmp, sub[0].ty, &adr, &sub[0].fld, sub[0].qual & QVOLATILE); - q = irbinop(fn, op, cls, l, r); - genbitfstore(fn, sub[0].ty, adr, &sub[0].fld, tmp, q); - } else { - bitsiz = 0; - adr = expraddr(fn, &sub[0]); - l = genload(fn, ex->ty, adr, ex->qual & QVOLATILE); - if ((op != Oadd && op != Osub) || cls != KPTR) { - l = scalarcvt(fn, ty, sub[0].ty, l); - r = scalarcvt(fn, ty, sub[1].ty, r); - q = irbinop(fn, op, type2cls[ty.t], l, r); - q = scalarcvt(fn, ex->ty, ty, q); - } else { - q = genptroff(fn, op, typesize(typechild(ex->ty)), l, sub[1].ty, r); - } - genstore(fn, ex->ty, adr, q); - } - if (discard) return NOREF; - return bitsiz ? narrow(fn, cls, ex->ty, q, bitsiz) : q; - case ECALL: - r = compilecall(fn, ex); - if (isint(ex->ty)) - return narrow(fn, cls, ex->ty, r, 0); - return r; - case ECOND: - for (bool c; knowntruthy(&c, &ex->sub[0]);) { - r = compileexpr(fn, &ex->sub[2-c], discard); - if (discard) return NOREF; - return scalarcvt(fn, ex->ty, ex->sub[2-c].ty, r); - } - - if (ex->ty.t == TYVOID || discard) { - struct block *tr, *fl, *end; - condjump(fn, &sub[0], tr = newblk(fn), fl = newblk(fn)); - useblk(fn, tr); - expreffects(fn, &sub[1]); - end = newblk(fn); - if (fn->curblk) - putbranch(fn, end); - useblk(fn, fl); - expreffects(fn, &sub[2]); - if (fn->curblk) - putbranch(fn, end); - useblk(fn, end); - return NOREF; - } - return condexprvalue(fn, ex, discard); - case ELOGAND: - case ELOGIOR: - for (bool c; knowntruthy(&c, &ex->sub[0]);) { - c ^= ex->t == ELOGIOR; - r = compileexpr(fn, &ex->sub[c], discard); - if (discard) return NOREF; - return scalarcvt(fn, mktype(TYBOOL), ex->sub[c].ty, r); - } - return condexprvalue(fn, ex, discard); - case ESEQ: - expreffects(fn, &sub[0]); - return compileexpr(fn, &sub[1], discard); - default: assert(!"nyi expr"); - } -} - -/************************************/ -/* Statements parsing & compilation */ -/************************************/ - -static void -stmtterm(struct comp *cm) -{ - expect(cm, ';', "to terminate previous statement"); -} - -static void block(struct comp *cm, struct function *fn); -static bool stmt(struct comp *cm, struct function *fn); -static void localdecl(struct comp *cm, struct function *fn, bool forinit); - -struct label { - struct label *link; - internstr name; - struct block *blk; - struct span usespan; - /* if usespan.ex.len == 0, this label is resolved and blk is the block that - * the label starts, otherwise the label is unresolved and blk is the head - * of a linked list of relocations, the next list entry is in blk->s1, etc, - * terminated by NULL */ -}; - -static struct label * -findlabel(struct comp *cm, internstr name) -{ - for (struct label *l = cm->labels; l; l = l->link) - if (l->name == name) return l; - return NULL; -} - -static void -deflabel(struct comp *cm, struct function *fn, const struct span *span, internstr name) -{ - struct label *label = findlabel(cm, name); - if (label && label->usespan.ex.len == 0) { - error(span, "redefinition of label '%s'", name); - } else if (label) { - struct block *new = NULL; - if (!nerror) { - new = newblk(fn); - if (fn->curblk) putbranch(fn, new); - } - /* fix up relocations */ - for (struct block *list = label->blk, *next; list; list = next) { - next = list->s1; - if (!nerror) { - useblk(fn, list); - putbranch(fn, new); - } - } - label->usespan = (struct span){0}; - label->blk = new; - if (!nerror) useblk(fn, new); - } else { - struct label l = { cm->labels, name }; - if (!nerror) { - struct block *new = newblk(fn); - if (fn->curblk) putbranch(fn, new); - useblk(fn, new); - } - l.blk = fn->curblk; - cm->labels = alloccopy(fn->arena, &l, sizeof l, 0); - } -} - -static bool -loopbody(struct comp *cm, struct function *fn, struct block *brk, struct block *cont) -{ - struct block *save[2]; - bool terminates = 0; - - save[0] = cm->breakto, save[1] = cm->loopcont; - cm->breakto = brk, cm->loopcont = cont; - ++cm->loopdepth; - - terminates = stmt(cm, fn); - - --cm->loopdepth; - cm->breakto = save[0], cm->loopcont = save[1]; - - return terminates; -} - -#define EMITS if (doemit && !nerror) - -struct swcase { - vlong val; - struct block *blk; - struct span span; -}; -struct switchstmt { - struct block *bdefault; - union type condtype; - vec_of(struct swcase) cases; -}; - -static int -cmpswcase(const void *aa, const void *bb) -{ - const struct swcase *a = aa, *b = bb; - vlong v1 = a->val, v2 = b->val; - if (v1 != v2) return v1 < v2 ? -1 : 1; - return (a > b) - (a < b); /* preserve original order */ -} - -static void -swsortcases(struct swcase *cs, uint n) -{ - void qsort(void *, size_t n, size_t size, int (*)(const void *, const void *)); - qsort(cs, n, sizeof *cs, cmpswcase); -} - -static bool -genswitch(struct comp *cm, struct function *fn, const struct expr *ex) -{ - union ref sel; - bool doemit = fn->curblk; - struct block *begin = NULL, *end = NULL, *breaksave = cm->breakto; - struct switchstmt *stsave = cm->switchstmt, st = {.condtype = ex->ty}; - enum irclass k = type2cls[scalartypet(ex->ty)]; - struct swcase casebuf[8]; - vinit(&st.cases, casebuf, countof(casebuf)); - - assert(k); - end = newblk(fn); - EMITS { - sel = exprvalue(fn, ex); - assert(isint(ex->ty)); - } - cm->switchstmt = &st; - cm->breakto = end; - begin = fn->curblk; - fn->curblk = NULL; - ++cm->switchdepth; - stmt(cm, fn); - --cm->switchdepth; - doemit = fn->curblk; - cm->switchstmt = stsave; - cm->breakto = breaksave; - - EMITS putbranch(fn, end); - useblk(fn, begin); - swsortcases(st.cases.p, st.cases.n); - doemit = 1; - if (!st.bdefault) st.bdefault = end; - /* TODO: optimize instead of generating the equivalent of if == .. else if .. chain - * XX 1. sort by case values (also for easy duplicates checking) - * 2. contiguous ranges (case a..b: -> x >= && x <= b) - * 3. binary search - * 4. jump tables? (harder, backend refactoring) - */ - vlong prev; - for (int i = 0; i < st.cases.n; ++i) { - const struct swcase *c = &st.cases.p[i]; - if (i > 0) { - assert(c->val >= prev); - if (c->val == prev) { - error(&c->span, "duplicate case value"); - note(&c[-1].span, "previously defined here"); - } - } - EMITS { - struct block *next = i < st.cases.n - 1 ? newblk(fn) : st.bdefault; - putcondbranch(fn, irbinop(fn, Oequ, k, sel, mkintcon(k, c->val)), c->blk, next); - if (next != st.bdefault) useblk(fn, next); - } - prev = c->val; - } - vfree(&st.cases); - if (fn->curblk != end) { - if (fn->curblk) EMITS putbranch(fn, end); - if (end->npred > 0) { - useblk(fn, end); - } else { - fn->curblk = NULL; - freeblk(fn, end); - } - } - - return fn->curblk == NULL; -} - -static bool /* return 1 if stmt is terminating (ends with a jump) */ -stmt(struct comp *cm, struct function *fn) -{ - struct block *tr, *fl, *end, *begin; - union { - struct arena a; - char mem[sizeof(struct arena) + sizeof(struct expr)*4]; - } atmp = { .a.cap = sizeof(struct expr)*4 }; - struct arena *atmpp; - struct expr ex; - struct env e; - union ref r; - struct token tk; - bool terminates = 0; - bool doemit = fn->curblk; - - while (match(cm, &tk, TKIDENT) || match(cm, &tk, TKWcase) || match(cm, &tk, TKWdefault)) { - if (tk.t == TKWcase) { - /* case <expr> ':' */ - if (!cm->switchstmt) error(&tk.span, "'case' outside of switch statement"); - ex = constantexpr(cm); - if (!eval(&ex, EVINTCONST)) - error(&ex.span, "not an integer constant expression"); - else if (cm->switchstmt && ex.ty.bits != cm->switchstmt->condtype.bits) { - struct expr tmp = ex; - ex = mkexpr(ECAST, ex.span, cm->switchstmt->condtype, .sub = &tmp); - bool ok = eval(&ex, EVINTCONST); - assert(ok && "cast const int?"); - if (ex.i != tmp.i) - warn(&ex.span, "overflow converting case value to switch condition type"); - } - expect(cm, ':', NULL); - if (!fn->curblk || (fn->curblk->phi.n > 0 || fn->curblk->ins.n > 0)) { - begin = newblk(fn); - EMITS putbranch(fn, begin); - useblk(fn, begin); - } - if (cm->switchstmt) - vpush(&cm->switchstmt->cases, ((struct swcase) {ex.i, fn->curblk, ex.span})); - } else if (tk.t == TKWdefault) { - /* default ':' */ - if (!cm->switchstmt) error(&tk.span, "'default' outside of switch statement"); - expect(cm, ':', NULL); - if (!fn->curblk || (fn->curblk->phi.n > 0 || fn->curblk->ins.n > 0)) { - begin = newblk(fn); - EMITS putbranch(fn, begin); - useblk(fn, begin); - } - if (cm->switchstmt) { - if (cm->switchstmt->bdefault) error(&tk.span, "multiple 'default' labels in one switch"); - cm->switchstmt->bdefault = fn->curblk; - } - } else if (tk.t == TKIDENT && match(cm, NULL, ':')) { - /* <label> ':' */ - deflabel(cm, fn, &tk.span, tk.name); - } else { - assert(tk.t == TKIDENT); - /* kludge for no backtracking and no lookahead */ - ex = exprparse(cm, 1, &tk, EFROMSTMT); - stmtterm(cm); - EMITS expreffects(fn, &ex); - return fn->curblk == NULL; - } - doemit = 1; - } - - switch (peek(cm, NULL)) { - case '{': - lex(cm, NULL); - envdown(cm, &e); - block(cm, fn); - envup(cm); - break; - case ';': - lex(cm, NULL); - break; - case TKWif: - lex(cm, NULL); - expect(cm, '(', NULL); - ex = commaexpr(cm); - expect(cm, ')', NULL); - if (!isscalar(ex.ty)) - error(&ex.span, "'if' condition is not a scalar '%ty'", ex.ty); - tr = fl = end = NULL; - EMITS { - tr = newblk(fn); - fl = newblk(fn); - condjump(fn, &ex, tr, fl); - useblk(fn, tr); - } - terminates = stmt(cm, fn); - if (!match(cm, NULL, TKWelse)) { - end = fl; - EMITS if (!terminates) putbranch(fn, end); - terminates = 0; - } else { - EMITS { - end = newblk(fn); - if (!terminates) putbranch(fn, end); - useblk(fn, fl); - } - terminates &= stmt(cm, fn); - EMITS { - if (fn->curblk) putbranch(fn, end); - } - } - EMITS if (!terminates) useblk(fn, end); - break; - case TKWelse: - lex(cm, &tk); - error(&tk.span, "'else' without matching 'if'"); - break; - case TKWwhile: /* while ( <cond> ) <body> */ - lex(cm, NULL); - expect(cm, '(', NULL); - ex = commaexpr(cm); - expect(cm, ')', NULL); - if (!isscalar(ex.ty)) - error(&ex.span, "'while' condition is not a scalar '%ty'", ex.ty); - tr = begin = end = NULL; - /* @begin: - * <cond> - * b <cond>, @tr, @end - * @tr: - * <body> - * b @begin - * @end: - * <- - */ - doemit = 1; - EMITS { - begin = newblk(fn); - if (fn->curblk) - putbranch(fn, begin); - useblk(fn, begin); - condjump(fn, &ex, tr = newblk(fn), end = newblk(fn)); - useblk(fn, tr); - } - terminates = loopbody(cm, fn, end, begin); - EMITS { - if (fn->curblk) putbranch(fn, begin); - useblk(fn, end); - } - break; - case TKWdo: /* do <body> while ( <cond> ) ; */ - lex(cm, NULL); - begin = tr = end = NULL; - /* @begin: - * <body> - * b @tr - * @tr: <- necessary for continue stmt - * <cond> - * b <cond>, @begin, @end - * @end: - * <- - */ - doemit = 1; - EMITS { - begin = newblk(fn); - if (fn->curblk) - putbranch(fn, begin); - useblk(fn, begin); - tr = newblk(fn); - end = newblk(fn); - } - terminates = loopbody(cm, fn, end, tr); - expect(cm, TKWwhile, NULL); - expect(cm, '(', NULL); - ex = commaexpr(cm); - expect(cm, ')', NULL); - if (!isscalar(ex.ty)) - error(&ex.span, "'while' condition is not a scalar '%ty'", ex.ty); - stmtterm(cm); - EMITS { - if (!terminates) putbranch(fn, tr); - useblk(fn, tr); - condjump(fn, &ex, begin, end); - useblk(fn, end); - } - break; - case TKWfor: /* for ( <init>? ; <cond>? ; <iter>? ) <body> */ - lex(cm, NULL); - begin = tr = end = fl = NULL; - expect(cm, '(', NULL); - /* -> - * <init> - * b @begin - * @begin: - * <cond> - * b <cond>, @tr, @fl - * @tr: - * <body> - * b @end - * @end: <- necessary for continue stmt - * <iter> - * b @begin - * @fl: - * <- - * - * if cond omitted, tr = begin - * if iter omitted, end = begin - */ - envdown(cm, &e); - if (!match(cm, NULL, ';')) { /* init */ - if (isdecltok(cm)) { - localdecl(cm, fn, 1); - } else { - ex = commaexpr(cm); - EMITS expreffects(fn, &ex); - expect(cm, ';', NULL); - } - } - doemit = 1; - EMITS { - end = tr = begin = newblk(fn); - if (fn->curblk) - putbranch(fn, begin); - useblk(fn, begin); - fl = newblk(fn); - } - if (!match(cm, NULL, ';')) { /* cond */ - ex = commaexpr(cm); - expect(cm, ';', NULL); - if (!isscalar(ex.ty)) - error(&ex.span, "'for' condition is not a scalar type ('%ty')", ex.ty); - EMITS { - tr = newblk(fn); - condjump(fn, &ex, tr, fl); - useblk(fn, tr); - } - } - atmpp = NULL; - if (!match(cm, NULL, ')')) { /* iter */ - /* since exarena is free'd at the end of each stmt, create a new temporary - * arena to parse this expression because loop body statements would free it - * otherwise */ - struct arena *tmp = cm->exarena; - cm->exarena = &atmp.a; - ex = commaexpr(cm); - atmpp = cm->exarena; - cm->exarena = tmp; - - end = newblk(fn); - expect(cm, ')', NULL); - } - - terminates = loopbody(cm, fn, fl, end); - - EMITS { - if (end != begin) { /* have iter */ - if (!terminates) putbranch(fn, end); - useblk(fn, end); - expreffects(fn, &ex); - putbranch(fn, begin); - } else if (!terminates) putbranch(fn, begin); - if (fl->npred > 0) { - useblk(fn, fl); - } else { - freeblk(fn, fl); - terminates = 1; - } - } - if (atmpp && atmpp != cm->exarena) freearena(&atmpp); - envup(cm); - break; - case TKWswitch: - lex(cm, NULL); - expect(cm, '(', NULL); - ex = commaexpr(cm); - expect(cm, ')', NULL); - if (!isint(ex.ty)) - error(&ex.span, "'switch' value is not an integer: '%ty'", ex.ty); - terminates = genswitch(cm, fn, &ex); - break; - case TKWbreak: - lex(cm, &tk); - if (!cm->loopdepth && !cm->switchdepth) - error(&tk.span, "'break' outside of loop or switch statement"); - EMITS putbranch(fn, cm->breakto); - stmtterm(cm); - break; - case TKWcontinue: - lex(cm, &tk); - if (!cm->loopdepth) - error(&tk.span, "'continue' outside of loop"); - EMITS putbranch(fn, cm->loopcont); - stmtterm(cm); - break; - case TKWgoto: - lex(cm, &tk); - peek(cm, &tk); - if (expect(cm, TKIDENT, NULL)) { - struct label *label = findlabel(cm, tk.name); - if (!label) { - /* create reloc list */ - struct label l = { cm->labels, tk.name, fn->curblk, tk.span }; - assert(l.usespan.ex.len); - cm->labels = alloccopy(fn->arena, &l, sizeof l, 0); - fn->curblk = NULL; - } else if (label && label->usespan.ex.len != 0) { - /* append to relocs list */ - struct block *next = label->blk; - label->blk = fn->curblk; - EMITS { - fn->curblk->s1 = next; - fn->curblk = NULL; - } - } else { - EMITS { - assert(label->blk); - putbranch(fn, label->blk); - } - } - } - stmtterm(cm); - break; - case TKWreturn: - lex(cm, &tk); - if (isexprtok(cm)) { - ex = commaexpr(cm); - if (fn->retty.t == TYVOID) { - if (ex.ty.t != TYVOID) error(&ex.span, "void function should not return a value"); - else if (ccopt.pedant) warn(&ex.span, "returning void expression is an extension"); - } else if (!assigncheck(fn->retty, &ex)) { - error(&ex.span, - "cannot return '%ty' value from function with return type '%ty'", - ex.ty, fn->retty); - } - EMITS { - if (isscalar(fn->retty)) - r = scalarcvt(fn, fn->retty, ex.ty, exprvalue(fn, &ex)); - else if (fn->retty.t == TYVOID) - r = (expreffects(fn, &ex), NOREF); - else - r = structreturn(fn, &ex); - putreturn(fn, r, NOREF); - } - } else { - if (fn->retty.t != TYVOID) - error(&tk.span, "non-void function should return a value"); - EMITS putreturn(fn, NOREF, NOREF); - } - stmtterm(cm); - break; - default: - ex = exprparse(cm, 1, NULL, EFROMSTMT); - stmtterm(cm); - EMITS expreffects(fn, &ex); - break; - } - freearena(&cm->exarena); - lexerfreetemps(cm->lx); - return fn->curblk == NULL; -} - -/* parse and compile a function-local declaration */ -static void -localdecl(struct comp *cm, struct function *fn, bool forini) -{ - struct expr ini; - struct token tk; - const bool doemit = fn->curblk; - struct declstate st = { DFUNCVAR }; - - if (!forini && match(cm, &tk, TKIDENT)) { - if (match(cm, NULL, ':')) { - /* <label> ':' */ - deflabel(cm, fn, &tk.span, tk.name); - stmt(cm, fn); - return; - } - /* finddecl() -> non null because localdecl() is called when isdecltok() */ - st.base = finddecl(cm, tk.name)->ty; - st.base0 = 1; - } - do { - struct decl decl = pdecl(&st, cm); - if (decl.name) { - static int staticid; - bool put = 0; - bool dynarr = 0; - - switch (decl.scls) { - case SCSTATIC: - if (forini) - error(&decl.span, "static declaration in 'for' loop initializer"); - if (!decl.sym) - decl.sym = mkhiddensym(&fn->name->c, &decl.name->c, ++staticid); - goto Initz; - case SCNONE: - if (decl.ty.t == TYFUNC) { - decl.scls = SCEXTERN; - if (!decl.sym) decl.sym = decl.name; - break; - } - decl.scls = SCAUTO; - /* fallthru */ - case SCAUTO: - case SCREGISTER: - if (decl.ty.t == TYFUNC) { - error(&decl.span, "declaring variable '%s' with function type '%ty'", decl.name, decl.ty); - goto Err; - } else if (isincomplete(decl.ty) && !(dynarr = (decl.ty.t == TYARRAY && st.varini))) { - error(&decl.span, "declaring variable '%s' with incomplete type '%ty'", decl.name, decl.ty); - goto Err; - } - decl.id = -1; - if (!nerror) { - struct instr alloc = mkalloca(typesize(decl.ty), typealign(decl.ty)); - if (fn->curblk) decl.id = addinstr(fn, alloc).i; - else decl.id = insertinstr(fn->entry, fn->entry->ins.n, alloc).i; - } - Initz: - if (st.varini) { - int d = putdecl(cm, &decl); - union type ty = decl.ty; - bool statik = decl.scls & (SCSTATIC | SCEXTERN); - ini = initializer(cm, &ty, statik ? EVSTATICINI : EVFOLD, - /* globl? */ decl.scls == SCEXTERN, decl.qual, statik ? decl.sym : NULL); - declsbuf.p[d].ty = ty; - put = 1; - pdecl(&st, cm); - if (!statik) { - /* fix alloca for actual size, for implicitly sized arrays */ - assert(!isincomplete(ty)); - EMITS instrtab[decl.id] = mkalloca(typesize(ty), typealign(ty)); - - if (!initcheck(ty, &ini)) { - struct span span = decl.span; - joinspan(&span.ex, ini.span.ex); - error(&span, "cannot initialize '%ty' variable with '%ty'", - ty, ini.ty); - } - EMITS { - if (ini.t == EINIT || (ty.t == TYARRAY && ini.t == ESTRLIT)) - geninit(fn, ty, mkref(RTMP, decl.id), &ini); - else if (isagg(ty)) - structcopy(fn, ty, mkref(RTMP, decl.id), exprvalue(fn, &ini)); - else { - genstore(fn, ty, mkref(RTMP, decl.id), - scalarcvt(fn, ty, ini.ty, exprvalue(fn, &ini))); - } - } - } else if (decl.scls == SCEXTERN) { - struct span span = decl.span; - joinspan(&span.ex, ini.span.ex); - error(&span, "block local 'extern' variable cannot have an initializer"); - } - } else if (decl.scls == SCSTATIC) { - /* zero-initialized static */ - if (decl.ty.t == TYARRAY && isincomplete(decl.ty)) - error(&decl.span, "definition of variable with array type needs size or initializer"); - else if (isincomplete(decl.ty)) - error(&decl.span, "definition of static variable with incomplete type"); - else - objnewdat(decl.sym, Sbss, 0, typesize(decl.ty), typealign(decl.ty)); - } - break; - case SCTYPEDEF: - if (forini) - error(&decl.span, "typedef in 'for' loop initializer"); - break; - case SCEXTERN: - if (!decl.sym) decl.sym = decl.name; - if (forini) - error(&decl.span, "extern declaration in 'for' loop initializer"); - if (st.varini) goto Initz; - break; - default: assert(0); - } - if (st.funcdef) { - struct span span = decl.span; - joinspan(&span.ex, (peek(cm, &tk), tk.span.ex)); - error(&span, "function definition not allowed here"); - int bal = 1; - do switch (lex(cm, NULL)) { - case TKEOF: break; - case '{': ++bal; break; - case '}': --bal; break; - } while (bal); - } - Err: - if (!put) putdecl(cm, &decl); - } else if (forini) { - error(&decl.span, "non-variable declaration in 'for' loop initializer"); - } - } while (st.more); -} - -static void -block(struct comp *cm, struct function *fn) -{ - struct token tk; - - while (!match(cm, &tk, '}')) { - if (isdecltok(cm)) - localdecl(cm, fn, 0); - else - stmt(cm, fn); - } - cm->fnblkspan = tk.span; -} - -static void -function(struct comp *cm, struct function *fn, internstr *pnames, const struct span *pspans, uchar *pquals) -{ - const struct typedata *td = &typedata[fn->fnty.dat]; - const bool doemit = fn->curblk; - struct env e; - struct token tk; - envdown(cm, &e); - - /* emit Oparam instructions */ - EMITS { - for (int i = 0; i < td->nmemb; ++i) { - union irtype pty = mkirtype(td->param[i]); - union ref r = addinstr(fn, mkinstr(Oparam, pty.isagg ? KPTR : pty.cls, - mkref(RICON, i), mktyperef(pty))); - assert(r.t == RTMP && r.i == i); - } - } - /* add parameters to symbol table and create prologue (arguments) block */ - for (int i = 0; i < td->nmemb; ++i) { - if (pnames[i]) { - struct decl arg = { .ty = td->param[i], .qual = pquals ? pquals[i] : 0, - .name = pnames[i], .scls = SCAUTO, .span = pspans[i] }; - EMITS { - if (isscalar(arg.ty)) { - arg.id = addinstr(fn, mkalloca(typesize(arg.ty), typealign(arg.ty))).i; - genstore(fn, arg.ty, mkref(RTMP, arg.id), mkref(RTMP, i)); - } else { - arg.id = addinstr(fn, mkinstr(Ocopy, KPTR, mkref(RTMP, i))).i; - } - } - putdecl(cm, &arg); - } else if (ccopt.cstd < STDC23) { - warn(&pspans[i], "missing name of parameter #%d", i+1); - } - } - - /* put __func__, though its data is generated lazily the first time it is encountered */ - putdecl(cm, &(struct decl) { - .ty = mkarrtype(mktype(TYCHAR), QCONST, strlen(&fn->name->c) + 1), .qual = QCONST, - .name = istr__func__, .scls = SCSTATIC, .span = (peek(cm, &tk), tk.span), - .isbuiltin = 1, .sym = fn->name, - }); - - /* end prologue */ - EMITS { - struct block *blk; - putbranch(fn, blk = newblk(fn)); - useblk(fn, blk); - } - cm->labels = NULL; - block(cm, fn); - envup(cm); - for (struct label *l = cm->labels; l; l = l->link) { - if (l->usespan.ex.len) { - error(&l->usespan, "label '%s' used but never defined", l->name); - } - } - if (fn->curblk) { - if (fn->retty.t == TYINT && fn->name == istr_main) { - /* implicit return 0 for main function (ISO C standard behavior) */ - putreturn(fn, ZEROREF, NOREF); - } else { - if (fn->retty.t != TYVOID && !nerror) { - /* it may not actually be reachable after constant-folding - * peephole optimizations (from code like assert(0 && "x")) */ - if (blkreachable(fn, fn->curblk)) { - warn(&cm->fnblkspan, "non-void function '%s' may not return a value", fn->name); - } - } - putreturn(fn, NOREF, NOREF); - } - } -} - -/* top-level declaration */ -static void -tldecl(struct comp *cm) -{ - struct declstate st = { DTOPLEVEL }; - do { - bool noscls = 0; - int nerr = nerror; - struct decl decl = pdecl(&st, cm); - - if (nerror != nerr && st.varini) { - (void)expr(cm); - pdecl(&st, cm); - continue; - } - if (st.empty) break; - if (!decl.scls) { - noscls = 1; - decl.scls = SCEXTERN; - } - if (!decl.sym) decl.sym = decl.name; - decl.isdef = st.varini; - if (st.funcdef) { - const struct typedata *td = &typedata[decl.ty.dat]; - if (td->ret.t != TYVOID && isincomplete(td->ret)) - error(&decl.span, "function definition with incomplete return type '%ty'", td->ret); - for (int i = 0; i < td->nmemb; ++i) { - if (td->param[i].t != TYVOID && isincomplete(td->param[i])) - error(&st.pspans[i], "parameter has incomplete type '%ty'", td->param[i]); - } - decl.isdef = 1; - int idecl = putdecl(cm, &decl); - struct decl *d = &declsbuf.p[idecl]; - if (d->inlin && decl.scls != SCSTATIC) fatal(&d->span, "non-static inline is unimplemented"); - struct function fn = { &cm->fnarena, .name = d->sym, .globl = d->scls != SCSTATIC, .fnty = decl.ty, .retty = td->ret, .inlin = d->inlin }; - irinit(&fn); - function(cm, &fn, st.pnames, st.pspans, st.pqual); - if (!nerror && ccopt.dbg.p) - irdump(&fn); - irfini(&fn); - } else if (decl.name) { - int idecl = putdecl(cm, &decl); - struct decl *d = &declsbuf.p[idecl]; - if (st.varini) { - if (isagg(decl.ty) && isincomplete(decl.ty)) - error(&decl.span, "initialization of variable with incomplete type '%ty'", decl.ty); - struct expr ini = initializer(cm, &decl.ty, EVSTATICINI, d->scls != SCSTATIC, d->qual, d->sym); - d = &declsbuf.p[idecl]; - d->ty = decl.ty; - if (d->scls == SCEXTERN && !noscls) { - struct span span = decl.span; - joinspan(&span.ex, ini.span.ex); - warn(&span, "'extern' variable has initializer"); - } - pdecl(&st, cm); - } else if (d->ty.t != TYFUNC && d->scls != SCTYPEDEF && (d->scls != SCEXTERN || noscls)) { - /* tentative definitions */ - if (!objhassym(d->sym, NULL)) { - uint size = typesize(d->ty); - if (isincomplete(d->ty)) { - if (d->ty.t == TYARRAY) { - warn(&d->span, "tentative array definition assumed to have one element"); - size = typesize(typechild(d->ty)); - assert(size != 0); - } else if (isagg(d->ty)) { - warn(&d->span, "tentative definition with incomplete type '%ty'", d->ty); - assert(size == 0); - } else assert(0); - } - if (size) objnewdat(d->sym, Sbss, d->scls == SCEXTERN, size, typealign(d->ty)); - } - } - if (ccopt.dbg.p) bfmt(ccopt.dbgout, "var %s : %tq\n", d->name, d->ty, d->qual); - } else { - if (ccopt.dbg.p && decl.ty.t) bfmt(ccopt.dbgout, "type %ty\n", decl.ty); - } - freearena(&cm->fnarena); - freearena(&cm->exarena); - lexerfreetemps(cm->lx); - } while (st.more); -} - -union type cvalistty; -void -docomp(struct comp *cm) -{ - static struct env toplevel; - struct token tk[1]; - - istr__func__ = intern("__func__"); - istr_main = intern("main"); - istr_memset = intern("memset"); - if (!cm->env) { - vinit(&declsbuf, NULL, 1<<10); - pmap_init(&tldeclmap, 1<<8); - cm->env = &toplevel; - } - if (!cvalistty.t) { - struct typedata td = { - .t = TYSTRUCT, .siz = targ_valistsize, .align = targ_primalign[TYPTR], .nmemb = 1, - .fld = &(struct namedfield){intern("-"), {mkarrtype(mktype(TYPTR), 0, 3)}} - }; - cvalistty = mkarrtype(mktagtype(intern("__builtin_va_list"), &td), 0, 1); - } - 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) { - if (tk->t == ';') { - lex(cm, tk); - } else if (!isdecltok(cm) && tk->t != TKIDENT) { - error(&tk->span, "expected declaration"); - do lex(cm, tk); while (tk->t != TKEOF && !isdecltok(cm)); - } else { - tldecl(cm); - } - } -} - -static void -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)) { - default: assert(0); - case LXERR: - fatal(NULL, "Cannot open %'s: %s", file, err); - case LXOK: - cm->fnarena = (void *)amem[0].m; - cm->fnarena->cap = N; - cm->exarena = (void *)amem[1].m; - cm->exarena->cap = N; - } -} - -void -ccomp(const char *file) -{ - struct comp cm = {&(struct lexer){0}}; - initcm(&cm, file); - docomp(&cm); -} - -void -cpp(struct wbuf *out, const char *file) -{ - struct comp cm = {&(struct lexer){0}}; - initcm(&cm, file); - lexerdump(cm.lx, out); -} - -/* vim:set ts=3 sw=3 expandtab: */ @@ -1,139 +0,0 @@ -#include "../common.h" -#include "../type.h" - -/*************/ -/* EXPR TREE */ -/*************/ - -enum exprkind { - EXXX, ENUMLIT, ESTRLIT, ESYM, ESSYMREF, EVAARG, EINIT, EGETF, ECALL, ECOND, - /* unary */ - EPLUS, ENEG, ECOMPL, ELOGNOT, EDEREF, EADDROF, ECAST, - EPREINC, EPOSTINC, EPREDEC, EPOSTDEC, - /* binary */ - EADD, ESUB, EMUL, EDIV, EREM, EBAND, EBIOR, EXOR, ESHL, ESHR, - ELOGAND, ELOGIOR, - EEQU, ENEQ, ELTH, EGTH, ELTE, EGTE, - ESET, ESETADD, ESETSUB, ESETMUL, ESETDIV, ESETREM, ESETAND, ESETIOR, ESETXOR, ESETSHL, ESETSHR, - ESEQ, -}; -#define isunop(t) in_range(t, EPLUS, EPOSTDEC) -#define isbinop(t) in_range(t, EADD, ESEQ) -#define isassign(t) in_range(t, ESET, ESETSHR) -#define assigntobinop(t) ((t) - ESETADD + EADD) - -struct expr { - uchar t; - uchar qual; - ushort narg; /* ECALL */ - union type ty; - struct span span; - union { - struct { - struct expr *sub; /* child(ren) */ - struct exgetfld { - ushort off; - uchar bitsiz, bitoff; - } fld; /* EGETF */ - }; - uvlong u; vlong i; double f; /* ENUMLIT */ - struct { - union { - uchar *p; - ushort *w16; - uint *w32; - }; - uint n; - } s; /* ESTRLIT */ - int decl; /* ESYM, index into declsbuf */ - internstr implicitsym; /* ESYM (undeclared) */ - struct { - internstr sym; - int off; - bool func : 1, local : 1; - } ssym; /* ESSYMREF (static symbol addr + off) */ - struct init *init; /* EINIT */ - }; -}; - -struct init { - struct bitset zero[BSSIZE(64)]; /* bytes to zero out up to 64 */ - struct initval { - struct initval *next; - uint off; - uchar bitoff, bitsiz; - struct expr ex; - } *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, - SCEXTERN = 1<<1, - SCSTATIC = 1<<2, - SCTHREADLOCAL = 1<<3, - SCAUTO = 1<<4, - SCREGISTER = 1<<5, -}; - -struct decl { - union type ty; - uchar scls; - uchar qual : 2, - noret : 1, - inlin : 1, - isenum : 1, - isdef : 1, - isbuiltin : 1; - struct span span; - internstr name; - union { - internstr sym; - struct { ushort align; int id; }; - vlong value; - const struct builtin *builtin; - }; -}; - -extern struct envdecls {vec_of(struct decl);} declsbuf; -extern union type cvalistty; -struct function; -int 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 scalarcvt(struct function *, union type to, union type from, union ref); -union ref compileexpr(struct function *, const struct expr *, bool discard); -void dumpexpr(const struct expr *, bool printtypes); - -/** 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, - EVARITH, - EVSTATICINI, - EVFOLD, -}; - -bool eval(struct expr *, enum evalmode); - -/* vim:set ts=3 sw=3 expandtab: */ diff --git a/c/eval.c b/c/eval.c deleted file mode 100644 index 3dfbbfb..0000000 --- a/c/eval.c +++ /dev/null @@ -1,437 +0,0 @@ -#include "c.h" -#include "../ir/ir.h" -#include <limits.h> - -static int -targ2hosttype(enum typetag t) -{ - if (t == TYPTR) t = targ_64bit ? TYUVLONG : TYUINT; - if (isintt(t)) { - int siz = targ_primsizes[t]; - int sgn = issignedt(t); -#define U(Ty,Tag) if (!sgn & (siz == sizeof(unsigned Ty))) return Tag; -#define S(Ty,Tag) if ( sgn & (siz == sizeof(signed Ty))) return Tag; - U(char, TYUCHAR) - S(char, TYSCHAR) - U(short, TYUSHORT) - S(short, TYSHORT) - U(int, TYUINT) - S(int, TYINT) - U(long long, TYUVLONG) - S(long long, TYVLONG) -#undef U -#undef S - } else if (t == TYLDOUBLE) return TYDOUBLE; - else if (isfltt(t) || iscomplext(t)) return t; - return 0; -} - -static bool -numcast(union type ty, struct expr *dst, const struct expr *src) -{ - enum typetag td = targ2hosttype(scalartypet(ty)), - ts = targ2hosttype(scalartypet(src->ty)); - vlong isrc; - struct expr tmp; - if (src == dst) tmp = *src, src = &tmp; - - assert(src->t == ENUMLIT); -#define TT(d,s) (td == d && ts == s) -#define TF(d) (td == d && isfltt(ts)) - if (!ts || !td) return 0; - else if (TT(TYFLOAT, TYFLOAT)) dst->f = (float) src->f; - else if (TT(TYFLOAT, TYDOUBLE)) dst->f = (float) src->f; - else if (TT(TYDOUBLE, TYFLOAT)) dst->f = src->f; - else if (TT(TYDOUBLE, TYDOUBLE)) dst->f = src->f; - else if (TT(TYFLOAT, TYUVLONG)) dst->f = (float) src->u; - else if (TT(TYDOUBLE, TYUVLONG)) dst->f = (double) src->u; - else if (td == TYFLOAT) dst->f = (float) src->i; - else if (td == TYDOUBLE) dst->f = (double) src->i; - else if (TF(TYUVLONG)) dst->u = src->f; - else if (TF(TYBOOL)) dst->i = (bool) src->f; - else if (isfltt(ts)) { isrc = src->f; goto Narrow; } - else { - isrc = src->i; - Narrow: - switch (td) { -#define I(Ty, Tag) case Tag: dst->i = (Ty) isrc; break; - I(bool, TYBOOL) - I(signed char, TYSCHAR) - I(unsigned char, TYUCHAR) - I(signed short, TYSHORT) - I(unsigned short, TYUSHORT) - I(signed int, TYINT) - I(unsigned int, TYUINT) - I(signed long long, TYVLONG) - I(unsigned long long, TYUVLONG) -#undef I - case TYFLOAT: dst->f = (float) src->f; break; - case TYDOUBLE: dst->f = src->f; break; - default: assert(0 && "bad cast?"); - } - } -#undef TT -#undef TF - - dst->t = ENUMLIT; - dst->ty = ty; - return 1; -} - -static struct expr * -lit2ssym(struct expr *ex) -{ - ex->ssym.sym = xcon2sym(expraddr(NULL, ex).i); - ex->ssym.local = 1; - ex->ssym.func = ex->ssym.off = 0; - ex->t = ESSYMREF; - return ex; -} - -static struct expr -staticaddrof(struct expr *ex, enum evalmode mode) -{ - struct expr ret = { .ty = mkptrtype(ex->ty, ex->qual), .span = ex->span }; - if (ex->t == ESYM && ex->ty.t < NTYPETAG) { - const struct decl *decl = &declsbuf.p[ex->decl]; - if (decl->sym && (decl->scls & (SCAUTO|SCREGISTER)) == 0) { - ret.t = ESSYMREF; - ret.ssym.sym = decl->sym; - ret.ssym.off = 0; - ret.ssym.func = decl->ty.t == TYFUNC; - ret.ssym.local = (decl->scls == SCSTATIC || decl->isdef); - } - } else if (ex->t == EDEREF && eval(ex->sub, EVSTATICINI)) { - ret = *ex->sub; - } else if (ex->t == EGETF && (ret = staticaddrof(ex->sub, mode)).t) { - if (ret.t == ESSYMREF) { - ex->t = ESSYMREF; - vlong off = (vlong) ret.ssym.off + ex->fld.off; - if ((int) off != off) return ret.t = 0, ret; - ret.ssym.off = off; - } else if (ret.t == ENUMLIT) { - ret.u += ex->fld.off; - } else assert(0); - } else if (ex->t == ESTRLIT || (mode == EVSTATICINI && ex->t == EINIT)) { - ret = *lit2ssym(ex); - } else if (ex->t == ENUMLIT || ex->t == ESSYMREF) ret = *ex; - return ret; -} - -static bool -isstaticlval(const struct expr *ex, enum evalmode mode) -{ - return ex->t == ESTRLIT || ex->t == ESSYMREF || (mode == EVSTATICINI && ex->t == EINIT); -} - -static bool -truthy(const struct expr *ex) -{ - switch (ex->t) { - default: assert(0 && "!scalar?"); - case ENUMLIT: - return isflt(ex->ty) ? ex->f != 0.0 : ex->u != 0; - case ESTRLIT: case ESSYMREF: - return 1; - } -} - -static bool -unop(struct expr *ex, enum evalmode mode) -{ - struct expr *sub = ex->sub; - - if (mode >= EVSTATICINI && ex->t == EDEREF) { - uvlong off; - uchar *p; - uint len; - uint csiz; - /* HACK */ - if (sub->t == ESTRLIT) { - /* *"s" */ - off = 0; - p = sub->s.p, len = sub->s.n; - csiz = typesize(typechild(sub->ty)); - StrRead: - if (off > len) return 0; - ex->t = ENUMLIT; - ex->ty = mktype(TYINT); - if (off == len) ex->u = 0; - else if (csiz == 1) ex->u = p[off]; - else if (csiz == 2) ex->u = ((short *)p)[off]; - else if (csiz == 4) ex->u = ((int *)p)[off]; - return 1; - } else if (sub->t == EADD && sub->sub[0].t == ESTRLIT && eval(&sub->sub[1], EVINTCONST)) { - /* "s"[0] */ - assert(sub->sub[1].t == ENUMLIT && isint(sub->sub[1].ty)); - off = sub->sub[1].u; - p = sub->sub[0].s.p, len = sub->sub[0].s.n; - csiz = typesize(typechild(sub->sub[0].ty)); - goto StrRead; - } else if (sub->t == EADD && sub->sub[1].t == ESTRLIT && eval(&sub->sub[0], EVINTCONST)) { - /* 0["s"] */ - assert(sub->sub[0].t == ENUMLIT && isint(sub->sub[0].ty)); - off = sub->sub[0].u; - p = sub->sub[1].s.p, len = sub->sub[1].s.n; - csiz = typesize(typechild(sub->sub[1].ty)); - goto StrRead; - } else return 0; - } else if (ex->t == EADDROF) { - assert(ex->ty.t == TYPTR); - struct expr ex2 = staticaddrof(ex->sub, mode); - if (!ex2.t) return 0; - ex2.span = ex->span; - ex2.ty = ex->ty; - *ex = ex2; - return 1; - } else if (ex->t == EGETF && !ex->fld.bitsiz) { - /* <lvalue>.memb -> is an address lvalue if 'memb' is of array type */ - if (ex->ty.t == TYARRAY) { - struct expr ex2; - if ((ex2 = staticaddrof(ex->sub, mode)).t) { - if (ex2.t == ENUMLIT) { - ex->t = ENUMLIT; - ex->u = ex2.u + ex->fld.off; - return 1; - } else { - assert(ex2.t == ESSYMREF); - ex->t = ESSYMREF; - vlong off = (vlong) sub->ssym.off + ex->fld.off; - if ((int) off != off) return 0; - ex->ssym = ex2.ssym; - ex->ssym.off = off; - return mode >= EVSTATICINI; - } - } - } - return 0; - } - if (!eval(sub, mode)) return 0; - switch (ex->t) { - case ECAST: - if (ex->ty.t == TYPTR && sub->t == ENUMLIT) { - ex->t = ENUMLIT; - ex->u = sub->u; - return 1; - } else if (isstaticlval(sub, mode) - && (ex->ty.t == TYPTR || (isint(ex->ty) && typesize(ex->ty) == targ_primsizes[TYPTR]))) { - /* ptr -> int */ - if (sub->t == ESTRLIT || sub->t == EINIT) - lit2ssym(sub); - sub->span = ex->span, sub->ty = ex->ty; - *ex = *sub; - return 1; - } - break; - case EPLUS: - break; - case ENEG: - if (sub->t != ENUMLIT) return 0; - if (isint(sub->ty)) sub->u = -sub->u; - else assert(isflt(sub->ty)), sub->f = -sub->f; - break; - case ECOMPL: - if (sub->t != ENUMLIT) return 0; - assert(isint(sub->ty)); - sub->u = ~sub->u; - break; - case ELOGNOT: - sub->u = !truthy(sub); - sub->t = ENUMLIT; - break; - default: - return 0; - } - if (sub->t != ENUMLIT || !numcast(ex->ty, ex, sub)) return 0; - return 1; -} - -static bool -binop(struct expr *ex, enum evalmode mode) -{ - struct expr *a = &ex->sub[0], *b = &ex->sub[1]; - if (!eval(a, mode)) return 0; - union type opty; - if (in_range(ex->t, EADD, ESHR)) - opty = ex->ty; - else /* compare, logical, set (result type != operation type) */ - opty = cvtarith(a->ty, b->ty); - if (isstaticlval(a, mode)) { - if ((ex->t == EADD || ex->t == ESUB) && eval(b, mode) && b->t == ENUMLIT) { - assert(isint(b->ty)); - assert(in_range(a->ty.t, TYPTR, TYARRAY)); - if (a->t == ESTRLIT) { - lit2ssym(a); - } else assert(a->t == ESSYMREF); - vlong addend = b->i * typesize(typechild(a->ty)), - off = a->ssym.off + (uvlong) (ex->t == EADD ? addend : -addend); - ex->t = ESSYMREF; - if ((int) off != off) return 0; - ex->ssym = a->ssym; - ex->ssym.off = off; - return 1; - } - return 0; - } - - enum { U = 0, S = 1<<8 , F = 1<<9 }; - int op = issigned(opty)<<8 | isflt(opty)<<9 | ex->t; - bool c; - if (ex->t != ELOGAND && ex->t != ELOGIOR) { - if (!numcast(opty, a, a)) return 0; - if (!eval(b, mode) || !numcast(opty, b, b)) return 0; - } - switch (op) { - case EADD|U: - case EADD|S: a->u += opty.t == TYPTR ? b->u * typesize(typechild(opty)) : b->u; break; - case EADD|F: a->f += b->f; break; - - case ESUB|U: - case ESUB|S: if (opty.t == TYPTR) { - assert(a->t == ENUMLIT && b->t == ENUMLIT); - assert(!isincomplete(typechild(ex->ty))); - a->u = (a->u - b->u) / typesize(typechild(ex->ty)); - } else a->u -= b->u; - break; - case ESUB|F: a->f -= b->f; break; - - case EMUL|U: - case EMUL|S: a->u *= b->u; break; - case EMUL|F: a->f *= b->f; break; - - case EDIV|U: if (!b->u) return 0; - a->u /= b->u; - break; - case EDIV|S: if (!b->i) return 0; - if (a->i == LLONG_MIN && b->i == -1) break; - a->i /= b->i; - break; - case EDIV|F: a->f /= b->f; break; - - case EREM|U: if (!b->u) return 0; - a->u %= b->u; - break; - case EREM|S: if (!b->i) return 0; - if (a->i == LLONG_MIN && b->i == -1) a->i = 0; - a->i %= b->i; - break; - - case EBAND|U: - case EBAND|S: a->u &= b->u; break; - - case EBIOR|U: - case EBIOR|S: a->u |= b->u; break; - - case EXOR|U: - case EXOR|S: a->u ^= b->u; break; - - case ESHL|S: if (a->i < 0) return 0; - case ESHL|U: if (b->u >= 8*targ_primsizes[opty.t]) return 0; - a->u <<= b->u; - break; - - case ESHR|U: if (b->u >= 8*targ_primsizes[opty.t]) return 0; - a->u >>= b->i; - break; - case ESHR|S: if (b->u >= 8*targ_primsizes[opty.t]) return 0; - a->i >>= b->i; - break; - - case EEQU|U: - case EEQU|S: a->u = a->u == b->u; break; - case EEQU|F: a->u = a->f == b->f; break; - - case ENEQ|U: - case ENEQ|S: a->u = a->u != b->u; break; - case ENEQ|F: a->u = a->f != b->f; break; - - case ELTH|U: a->u = a->u < b->u; break; - case ELTH|S: a->u = a->i < b->i; break; - case ELTH|F: a->u = a->f < b->f; break; - - case EGTH|U: a->u = a->u > b->u; break; - case EGTH|S: a->u = a->i > b->i; break; - case EGTH|F: a->u = a->f > b->f; break; - - case ELTE|U: a->u = a->u <= b->u; break; - case ELTE|S: a->u = a->i <= b->i; break; - case ELTE|F: a->u = a->f <= b->f; break; - - case EGTE|U: a->u = a->u >= b->u; break; - case EGTE|S: a->u = a->i >= b->i; break; - case EGTE|F: a->u = a->f >= b->f; break; - - case ELOGAND|U: - case ELOGAND|S: - case ELOGAND|F: - c = (op & F) ? a->f : a->u; - if (c) { - if (!eval(b, mode) || !numcast(opty, b, b)) return 0; - c = (op & F) ? b->f : b->u; - } - a->u = c; - break; - - case ELOGIOR|U: - case ELOGIOR|S: - case ELOGIOR|F: - c = op & F ? a->f : a->u; - if (!c) { - if (!eval(b, mode) || !numcast(opty, b, b)) return 0; - c = (op & F) ? b->f : b->u; - } - a->u = c; - break; - default: return 0; - } - - if (!in_range(ex->t, EADD, ESHR)) { - a->t = ENUMLIT; - a->ty = mktype(TYINT); - } - return numcast(ex->ty, ex, a); -} - -bool -eval(struct expr *ex, enum evalmode mode) -{ - switch (ex->t) { - case EGETF: goto Unop; - case ESEQ: - if (!eval(&ex->sub[0], mode)) return 0; - *ex = ex->sub[1]; - return eval(ex, mode); - case ECOND: - if (!eval(&ex->sub[0], mode)) return 0; - *ex = ex->sub[2-truthy(&ex->sub[0])]; - return eval(ex, mode); - case EINIT: - for (struct initval *v = ex->init->vals; v; v = v->next) { - if (!eval(&v->ex, mode)) return 0; - } - return 1; - case ENUMLIT: - if (mode <= EVINTCONST) return !isflt(ex->ty); - return 1; - case ESYM: - if (in_range(ex->ty.t, TYARRAY, TYFUNC) - && mode >= EVSTATICINI) { - struct expr ex2 = staticaddrof(ex, mode); - if (ex2.t) { - union type ty = ex->ty; - *ex = ex2; - ex->ty = ty; - return 1; - } - } - return 0; - case ESTRLIT: case ESSYMREF: - return mode >= EVSTATICINI; - default: - if (isunop(ex->t)) Unop: return unop(ex, mode); - if (isbinop(ex->t)) return binop(ex, mode); - } - return 0; -} - -/* vim:set ts=3 sw=3 expandtab: */ diff --git a/c/keywords.def b/c/keywords.def deleted file mode 100644 index e71f25f..0000000 --- a/c/keywords.def +++ /dev/null @@ -1,76 +0,0 @@ -/* !SORTED */ -/* token cstd (alias) */ -_(_Alignas, STDC11, ) -_(_Alignof, STDC11, ) -_(_Atomic, STDC11, ) -_(_BitInt, STDC23, ) -_(_Bool, STDC99, ) -_(_Complex, STDC99, "__complex", "__complex__") -_(_Decimal128, STDC23, ) -_(_Decimal32, STDC23, ) -_(_Decimal64, STDC23, ) -_(_Generic, STDC11, ) -_(_Imaginary, STDC99, ) -_(_Noreturn, STDC11, ) -_(_Static_assert, STDC11, ) -_(_Thread_local, STDC11, ) -_(__asm__, 0, ) -_(__attribute__, 0, ) -_(__builtin_va_arg, 0, ) -_(__extension__, 0, ) -_(__typeof__, 0, "__typeof") -_(alignas, STDC23, ) -_(alignof, STDC23, ) -_(auto, 0, ) -_(bool, STDC23, ) -_(break, 0, ) -_(case, 0, ) -_(char, 0, ) -_(const, 0, "__const", "__const__") -_(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, "__inline", "__inline__") -_(int, 0, ) -_(long, 0, ) -_(nullptr, STDC23, ) -_(register, 0, ) -_(restrict, STDC99, "__restrict", "__restrict__") -_(return, 0, ) -_(short, 0, ) -_(signed, 0, "__signed", "__signed__") -_(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, "__volatile", "__volatile__") -_(while, 0, ) - -#ifndef TKWBEGIN_ -# define TKWBEGIN_ TKW_Alignas -#endif -#ifndef TKWEND_ -# define TKWEND_ TKWwhile -#endif -#ifndef TKWMAXLEN_ -# define TKWMAXLEN_ (sizeof "__builtin_va_arg" - 1) -#endif diff --git a/c/lex.c b/c/lex.c deleted file mode 100644 index c196a21..0000000 --- a/c/lex.c +++ /dev/null @@ -1,2496 +0,0 @@ -#include "lex.h" -#include "../version.h" -#include <string.h> -#include <stdlib.h> - -/* fill internal circular character buffer with input after translation phase 1 & 2 - * (trigraph substitution and backslash-newline deletion */ -static void -fillchrbuf(struct lexer *lx) -{ - const uchar *p = lx->dat + lx->idx; - int i = lx->chrbuf0, idx = lx->idx; - int rem = countof(lx->chrbuf) - i; - assert(rem >= 0); - if (rem > 0) { - memmove(lx->chrbuf, lx->chrbuf+i, rem * sizeof *lx->chrbuf); - memmove(lx->chridxbuf, lx->chridxbuf+i, rem * sizeof *lx->chridxbuf); - } - lx->chrbuf0 = 0; - i = rem; - - for (; i < countof(lx->chrbuf); ++i) { - uchar c; - /* skip backslash-newline* */ - for (;;) { - if (p[0] == '\\') { - if (p[1] == '\n') { - idx += 2; - p += 2; - } else if (p[1] == '\r' && p[2] == '\n') { - idx += 3; - p += 3; - } else break; - } else if (ccopt.trigraph && !memcmp(p, "\?\?/\n", 4)) { - idx += 4; - p += 4; - } else if (ccopt.trigraph && !memcmp(p, "\?\?/\r\n", 5)) { - idx += 5; - p += 5; - } else break; - addfileline(lx->fileid, idx); - } - - if (idx >= lx->ndat) { - c = 0; - } else if (ccopt.trigraph && ((p[0] == '?') & (p[1] == '?'))) { - switch (p[2]) { - case '=': c = '#'; break; - case '(': c = '['; break; - case ')': c = ']'; break; - case '!': c = '|'; break; - case '<': c = '{'; break; - case '>': c = '}'; break; - case '-': c = '~'; break; - case '/': c = '\\'; break; - case '\'': c = '^'; break; - default: goto NoTrigraph; - } - p += 3; - idx += 3; - } else { - NoTrigraph: - ++idx; - if ((c = *p++) == '\n') - addfileline(lx->fileid, idx); - } - lx->chrbuf[i] = c; - lx->chridxbuf[i] = idx; - } - lx->idx = idx; -} - -static uchar -next(struct lexer *lx) -{ - if (lx->chrbuf0 >= countof(lx->chrbuf)) - fillchrbuf(lx); - lx->chridx = lx->chridxbuf[lx->chrbuf0]; - uchar c = lx->chrbuf[lx->chrbuf0]; - lx->eof = lx->chridx >= lx->ndat; - ++lx->chrbuf0; - return c; -} - -static uchar -peek(struct lexer *lx, int off) -{ - assert(off < countof(lx->chrbuf)); - if (lx->chrbuf0 + off >= countof(lx->chrbuf)) - fillchrbuf(lx); - return lx->chrbuf[lx->chrbuf0 + off]; -} - -static bool -match(struct lexer *lx, uchar c) -{ - if (!lx->eof && peek(lx, 0) == c) { - next(lx); - return 1; - } - return 0; -} - -static bool -aissep(int c) { - static const bool tab[] = { - ['('] = 1, [')'] = 1, ['['] = 1, [']'] = 1, - ['{'] = 1, ['}'] = 1, ['.'] = 1, [','] = 1, - [';'] = 1, ['?'] = 1, ['+'] = 1, ['-'] = 1, - ['*'] = 1, ['/'] = 1, ['&'] = 1, ['|'] = 1, - ['^'] = 1, ['~'] = 1, ['='] = 1, ['\''] = 1, - ['"'] = 1, ['<'] = 1, ['>'] = 1, [':'] = 1, - ['@'] = 1, ['#'] = 1, ['%'] = 1, ['\\'] = 1, - ['`'] = 1, ['!'] = 1, - }; - if (!aisprint(c) || aisspace(c)) - return 1; - return (uint)c < sizeof(tab) && tab[c]; -} - -enum typetag -parsenumlit(uvlong *outi, double *outf, const struct token *tk, bool ispp) -{ - if (tk->t == TKCHRLIT) { - uvlong n = 0; - if (!tk->wide) { - for (int i = 0; i < tk->len; ++i) - n = n << 8 | (uchar)tk->s[i]; - } else if (tk->wide == 1) { - n = tk->ws16[0]; - } else { - assert(tk->wide == 2); - n = tk->ws32[0]; - } - if (outi) *outi = n; - return TYINT; - } else if (memchr(tk->s, '.', tk->len)) { - extern double strtod(const char *, char **); - double f; - char buf[80], *suffix; - Float: /* float literal */ - assert(tk->len < sizeof buf - 1 && "numlit too big"); - memcpy(buf, tk->s, tk->len); - buf[tk->len] = 0; - f = strtod(buf, &suffix); - if (suffix == buf) - return 0; - if (!*suffix) { - if (outf) *outf = f; - return TYDOUBLE; - } else if ((suffix[0]|0x20) == 'f' && !suffix[1]) { - if (outf) *outf = f; - return TYFLOAT; - } else if ((suffix[0]|0x20) == 'l' && !suffix[1]) { - if (outf) *outf = f; - return TYLDOUBLE; - } - return 0; - } else { /* int literal */ - static uvlong max4typ[TYUVLONG-TYINT+1]; - uvlong n = 0; - int base = 10, nsx; - bool dec, u = 0, longlongok = ccopt.cstd >= STDC99 || !ccopt.pedant; - enum typetag ty = 0; - const char *sx; /*suffix*/ - char c; - - if (!max4typ[0]) - for (ty = TYINT; ty <= TYUVLONG; ++ty) - max4typ[ty-TYINT] = ((1ull << (8*targ_primsizes[ty]-1))-1) << isunsignedt(ty) | 1; - - sx = tk->s; - if (tk->len > 2 && sx[0] == '0') { - if ((sx[1]|32) == 'x') sx += 2, base = 16; /* 0x.. */ - else if ((sx[1]|32) == 'b') sx += 2, base = 2; /* 0b.. */ - else base = 8; /* 0.. */ - } - for (; sx < tk->s + tk->len; ++sx) { - if (base < 16) { - if (!in_range(c = *sx, '0', '0'+base-1)) break; - n = n*base + c - '0'; - } else { - if (in_range(c = *sx, '0', '9')) n = n*base + c - '0'; - else if (in_range(c|32, 'a', 'f')) n = n*base + 0xa + (c|32) - 'a'; - else break; - } - } - dec = base == 10; - nsx = tk->len - (sx - tk->s); - - if (nsx == 0) /* '' */ {} - else if ((sx[0]|32) == 'u') { - u = 1; - if (nsx == 1) /* 'u' */ {} - else if ((sx[1]|32) == 'l') { - if (nsx == 2) /* 'ul' */ goto L; - if (sx[1] == sx[2] && nsx == 3) /* 'ull' */ goto LL; - return 0; - } else return 0; - } else if ((sx[0]|32) == 'l') { - if (nsx == 1) /* 'l' */ goto L; - if ((sx[1]|32) == 'u' && nsx == 2) /* 'lu' */ { u=1; goto L; } - if (sx[1] == sx[0]) { - if (nsx == 2) /* 'll' */ goto LL; - if ((sx[2]|32) == 'u' && nsx == 3) /* 'llu' */ { u=1; goto LL; } - } - return 0; - } else if ((sx[0]|32) == 'e' || (sx[0]|32) == 'p') - goto Float; - else return 0; - -#define I(T) if (n <= max4typ[T - TYINT]) { ty = T; goto Ok; } - I(TYINT) - if (u || !dec) I(TYUINT) - L: - I(TYLONG) - if (u || !dec || !longlongok) I(TYULONG) - if (longlongok) { - LL: - I(TYVLONG) - if (u || !dec) I(TYUVLONG) - } - if (ispp) { ty = TYUVLONG; goto Ok; } -#undef I - /* too big */ - if (outi) *outi = n; - return 0; - Ok: - if (u && issignedt(ty)) ++ty; /* make unsigned */ - if (outi) *outi = n; - if (ispp) { - if (u) return TYUVLONG; - else if (n <= max4typ[TYVLONG-TYINT]) return TYVLONG; - } - if (ty >= TYVLONG && !longlongok) - warn(&tk->span, "'long long' in %M is an extension"); - return ty; - } -} - -static void -readstrchrlit(struct lexer *lx, struct token *tk, char delim, int wide) -{ - int c, i; - uchar tmp[200]; - vec_of(uchar) b = VINIT(tmp, sizeof tmp); - struct span span = {0}; - uint n, beginoff, idx; - beginoff = idx = lx->chridx; - - while ((c = next(lx)) != delim) { - static uint wmax[] = {0xFF, 0xFFFF, 0xFFFFFFFFu}; - if (c == '\n' || c == TKEOF) { - Noterm: - span.sl = (struct span0) { idx, lx->chridx - idx, lx->fileid }; - error(&span, "missing terminating %c character", delim); - break; - } else if (c == '\\') { - span.sl = (struct span0) { idx, lx->chridx - idx, lx->fileid }; - switch (c = next(lx)) { - case '\n': case TKEOF: - goto Noterm; - case '\'': c = '\''; break; - case '\\': c = '\\'; break; - case '"': c = '"'; break; - case '?': c = '?'; break; - case 'a': c = '\a'; break; - case 'b': c = '\b'; break; - case 'f': c = '\f'; break; - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 't': c = '\t'; break; - case 'v': c = '\v'; break; - case 'x': case 'X': /* hex */ - n = 0; - if (!aisxdigit(peek(lx, 0))) goto Badescseq; - do { - c = next(lx); - if (c-'0' < 10) n = n<<4 | (c-'0'); - else n = n<<4 | (10 + (c|0x20)-'a'); - } while (aisxdigit(peek(lx, 0))); - if (n > wmax[wide]) { - span.sl.len = lx->chridx - span.sl.off; - error(&span, "hex escape sequence out of range"); - } - c = n; - break; - default: - if (aisodigit(c)) { /* octal */ - n = c-'0'; - for (i = 2; i--;) { - if (!aisodigit(peek(lx, 0))) break; - n = n<<3 | ((c = next(lx))-'0'); - } - if (n > wmax[wide]) { - span.sl.len = lx->chridx - span.sl.off; - error(&span, "octal escape sequence out of range"); - } - c = n; - break; - } - Badescseq: - span.sl.len = lx->chridx - span.sl.off; - error(&span, "invalid escape sequence"); - } - } - if (!wide || c <= 0xFF) { - vpush(&b, c); - } else { - /* XXX this doesn't work for non-utf sequences, UTF-16 surrogates, etc - * the source utf8 -> utf16/32 conversion should be done on the fly, then - * these can also be appended directly, rather than doing the conversion at the end */ - char p[4]; - int n = utf8enc(p, c); - vpushn(&b, p, n); - } - idx = lx->chridx;; - } - if (delim == '"') { - tk->t = TKSTRLIT; - tk->len = b.n; - if ((tk->wide = wide)) { - tk->litlit = 0; - if (wide == 1) - tk->ws16 = utf8to16(&tk->len, lx->tmparena, b.p, b.n); - else - tk->ws32 = utf8to32(&tk->len, lx->tmparena, b.p, b.n); - } else if (lx->chridx - beginoff == tk->len + 1) { - tk->litlit = 1; - tk->s = (char *)&lx->dat[beginoff]; - } else { - tk->litlit = 0; - vpush(&b, 0); - tk->s = alloccopy(lx->tmparena, b.p, b.n, 1); - } - } else { - if (b.n == 0) { - span.sl = (struct span0) { idx, lx->chridx - idx, lx->fileid }; - error(&span, "empty character literal"); - } else if (b.n > targ_primsizes[TYINT]) { - span.sl = (struct span0) { idx, lx->chridx - idx, lx->fileid }; - error(&span, "multicharacter literal too long"); - } - tk->t = TKCHRLIT; - tk->len = b.n; - if ((tk->wide = wide)) { - tk->litlit = 0; - if (wide == 1) - tk->ws16 = utf8to16(&tk->len, lx->tmparena, b.p, b.n); - else - tk->ws32 = utf8to32(&tk->len, lx->tmparena, b.p, b.n); - } else if (lx->chridx - beginoff == tk->len + 1) { - tk->litlit = 1; - tk->s = (char *)&lx->dat[beginoff]; - } else { - tk->litlit = 0; - tk->s = alloccopy(lx->tmparena, b.p, tk->len, 1); - } - } - vfree(&b); -} - -/* for #include directive, read "header" or <header> */ -static void -readheadername(struct lexer *lx, struct token *tk, char delim) -{ - int c; - uchar tmp[200]; - vec_of(uchar) b = VINIT(tmp, sizeof tmp); - struct span span = {0}; - uint beginoff, idx; - beginoff = idx = lx->chridx; - - while ((c = next(lx)) != delim) { - if (c == '\n' || lx->eof) { - span.sl = (struct span0) { idx, lx->chridx - idx, lx->fileid }; - error(&span, "missing terminating %c character", delim); - break; - } - vpush(&b, c); - idx = lx->chridx;; - } - tk->t = delim == '"' ? TKPPHDRQ : TKPPHDRH; - tk->len = b.n; - if (lx->chridx - beginoff == tk->len + 1) { - tk->litlit = 1; - tk->s = (char *)&lx->dat[beginoff]; - } else { - tk->litlit = 0; - vpush(&b, 0); - tk->s = alloccopy(lx->tmparena, b.p, b.n, 1); - } - vfree(&b); -} - -/* matches "<digit> | <identifier-nondigit> | '.' | ([eEpP][+-])" */ -static bool -isppnum(char prev, char c) -{ - if (!aissep(c) || c == '.') - return 1; - if (c == '+' || c == '-') - return (prev|0x20) == 'e' || (prev|0x20) == 'p'; - return 0; -} - -enum { MAXLITLEN = 256 }; /* maximum length of num literals and identifiers */ -static int -lex0(struct lexer *lx, struct token *tk, bool includeheader) -{ - int idx,q; - bool space = 0; -Begin: - idx = lx->chridx; - if (lx->chrbuf0+4 >= countof(lx->chrbuf)) - fillchrbuf(lx); - lx->chridx = lx->chridxbuf[lx->chrbuf0]; - uchar *p = &lx->chrbuf[lx->chrbuf0++], - c = p[0]; - switch (c) { - -#define RET(t_) do { tk->t = (t_); goto End; } while (0) -#define TK2(c2,t) if (p[1] == c2) { \ - lx->chridx = lx->chridxbuf[lx->chrbuf0]; \ - ++lx->chrbuf0; \ - RET(t); \ - } -#define TK3(c2,c3,t) if (p[1] == c2 && p[2] == c3) { \ - lx->chridx = lx->chridxbuf[++lx->chrbuf0]; \ - ++lx->chrbuf0; \ - RET(t); \ - } - - case ' ': case '\t': case '\f': case '\v': case '\r': - space = 1; - goto Begin; - break; - case '(': case ')': case ',': case ':': - case ';': case '?': case '[': case ']': - case '{': case '}': case '~': case '$': - case '@': case '`': case '\\': case '\n': - RET(c); - case '!': - TK2('=', TKNEQ); - RET(c); - case '#': - TK2('#', TKPPCAT); - RET(c); - case '+': - TK2('+', TKINC); - TK2('=', TKSETADD); - RET(c); - case '-': - TK2('-', TKDEC); - TK2('=', TKSETSUB); - TK2('>', TKARROW); - RET(c); - case '*': - TK2('=', TKSETMUL); - RET(c); - case '/': - TK2('=', TKSETDIV); - if (match(lx, '/')) { - /* // single line comment */ - for (;;) { - do { - if (lx->chrbuf[lx->chrbuf0] == '\n') { - lx->chridx = lx->chridxbuf[lx->chrbuf0++]; - lx->eof = lx->chridx >= lx->ndat; - RET('\n'); - } else if (lx->eof) RET(TKEOF); - } while (++lx->chrbuf0 < countof(lx->chrbuf)); - fillchrbuf(lx); - lx->chridx = lx->chridxbuf[lx->chrbuf0]; - lx->eof = lx->chridx >= lx->ndat; - } - } else if (match(lx, '*')) { - // /* multi line comment */ - if (lx->chrbuf0+1 >= countof(lx->chrbuf)) fillchrbuf(lx); - for (;;) { - do { - if (lx->chrbuf[lx->chrbuf0] == '*' && lx->chrbuf[lx->chrbuf0+1] == '/') { - lx->chridx = lx->chridxbuf[lx->chrbuf0+1]; - lx->chrbuf0 += 2; - lx->eof = lx->chridx >= lx->ndat; - space = 1; - goto Begin; - } - } while (++lx->chrbuf0+1 < countof(lx->chrbuf)); - fillchrbuf(lx); - lx->chridx = lx->chridxbuf[lx->chrbuf0]; - if ((lx->eof = (lx->chridx >= lx->ndat))) { - struct span span = {{ idx, lx->chridx - idx, lx->fileid }}; - fatal(&span, "unterminated comment"); - } - } - } - RET(c); - case '%': - TK2('=', TKSETREM); - RET(c); - case '^': - TK2('=', TKSETXOR); - RET(c); - case '=': - TK2('=', TKEQU); - RET(c); - case '<': - if (includeheader) { - readheadername(lx, tk, '>'); - goto End; - } - TK2('=', TKLTE); - TK3('<','=', TKSETSHL) - TK2('<', TKSHL); - RET(c); - case '>': - TK2('=', TKGTE); - TK3('>','=', TKSETSHR) - TK2('>', TKSHR); - RET(c); - case '&': - TK2('&', TKLOGAND); - TK2('=', TKSETAND); - RET(c); - case '|': - TK2('|', TKLOGIOR); - TK2('=', TKSETIOR); - RET(c); - case '"': - if (includeheader) { - readheadername(lx, tk, '"'); - } else { - case '\'': - tk->wideuni = 0; - readstrchrlit(lx, tk, c, 0); - } - goto End; - case '.': - TK3('.','.',TKDOTS) - if (aisdigit(p[1])) goto Numlit; - RET(c); - case 'L': - if (match(lx, (q = '\'')) || match(lx, (q = '"'))) { - tk->wideuni = 0; - readstrchrlit(lx, tk, q, /* wide */ targ_primsizes[targ_wchartype] == 2 ? 1 : 2); - goto End; - } - /* fallthru */ - default: - if (aisdigit(c)) Numlit: { - --lx->chrbuf0; - if (lx->chrbuf0 + MAXLITLEN >= countof(lx->chrbuf)) - fillchrbuf(lx); - int n = 1; - uchar *p = &lx->chrbuf[lx->chrbuf0]; - for (; isppnum(p[n-1], p[n]); ++n) { - if (n >= MAXLITLEN) { - lx->chridx = lx->chridxbuf[lx->chrbuf0+n-1]; - TooLong: - fatal(&(struct span) {{ idx, lx->chridx - idx, lx->fileid }}, - "token is too long"); - } - } - tk->len = n; - lx->chridx = lx->chridxbuf[(lx->chrbuf0 += n) - 1]; - if (n == lx->chridx - idx) { - tk->litlit = 1; - tk->s = (char *)&lx->dat[idx]; - } else { - tk->litlit = 0; - tk->s = alloccopy(lx->tmparena, p, n, 1); - } - RET(TKNUMLIT); - } else if (c == '_' || aisalpha(c)) { - --lx->chrbuf0; - if (lx->chrbuf0 + MAXLITLEN >= countof(lx->chrbuf)) - fillchrbuf(lx); - uchar *p = &lx->chrbuf[lx->chrbuf0]; - int n = 1; - for (; !aissep(p[n]); ++n) { - if (n >= MAXLITLEN) { - lx->chridx = lx->chridxbuf[lx->chrbuf0+n-1]; - goto TooLong; - } - } - tk->blue = 0; - tk->len = n; - tk->name = intern_((char *)p, n); - lx->chridx = lx->chridxbuf[(lx->chrbuf0 += n) - 1]; - RET(TKIDENT); - } - /* fallthru */ - case 0: if (lx->idx >= lx->ndat) RET(TKEOF); -#undef TK2 - } - fatal(&(struct span) {{ idx, lx->chridx - idx, lx->fileid }}, - "unexpected character %'c at %d (%d)", c, idx, lx->idx); -End: - tk->space = space; - tk->span.sl.file = lx->fileid; - tk->span.sl.off = idx; - tk->span.sl.len = lx->chridx - idx; - tk->span.ex = tk->span.sl; - return tk->t; -#undef RET -} - -/****************/ -/* PREPROCESSOR */ -/****************/ - -static bool -tokequ(const struct token *a, const struct token *b) -{ - if (a->t != b->t) return 0; - if (a->t == TKNUMLIT || a->t == TKSTRLIT || a->t == TKCHRLIT) { - if (a->len != b->len) return 0; - return !memcmp(a->s, b->s, a->len); - } else if (a->t == TKIDENT) { - return a->name == b->name; - } else if (a->t == TKPPMACARG || a->t == TKPPMACSTR) { - return a->argidx == b->argidx; - } - return 1; -} - -static vec_of(struct token) mtoksbuf, /* buffers for macro replacement list tokens */ - mdyntoksbuf; /* for function-like macros after parameter substitution */ - -struct macro { - internstr *param; - struct span0 span; - uchar nparam; - bool predef : 1, - special : 1, - fnlike : 1, - variadic : 1; - short id; - union { - void (*handler)(struct lexer *, struct token *); - struct rlist { - uint off; /* mtoksbuf[] */ - int n; - } rl; - const struct token *single; /* predef */ - void (*handlerfn)(struct lexer *, struct token *ret, const struct token *arg, int narg); - }; -}; - -static bool -macroequ(const struct macro *a, const struct macro *b) -{ - if (a->special != b->special) return 0; - if (a->fnlike != b->fnlike || a->variadic != b->variadic) return 0; - if (a->fnlike) { - if (a->nparam != b->nparam) return 0; - for (int i = 0; i < a->nparam; ++i) - if (a->param[i] != b->param[i]) - return 0; - } - if (a->special) return a->handler == b->handler; - if (a->rl.n != b->rl.n) return 0; - const struct token *tka = &mtoksbuf.p[a->rl.off], *tkb = &mtoksbuf.p[b->rl.off]; - for (int i = 0; i < a->rl.n; ++i) { - if (!tokequ(&tka[i], &tkb[i])) - return 0; - if (i > 0 && tka[i].space != tkb[i].space) - return 0; - } - return 1; -} - -static void -freemac(struct macro *mac) -{ - if (mac->special) return; - free(mac->param); -} - -static pmap_of(struct macro) macroht; - -static void -putmac(internstr name, struct macro *mac) -{ - static short id; - if (!macroht.v) pmap_init(¯oht, 1<<10); - struct macro *slot = pmap_get(¯oht, name); - mac->id = id++; - if (slot) { - if (!macroequ(slot, mac)) { - if (slot->predef) - warn(&(struct span){mac->span}, "redefining builtin macro"); - else { - warn(&(struct span){mac->span}, "redefining macro"); - note(&(struct span){slot->span}, "previous definition:"); - } - freemac(slot); - *slot = *mac; - } else { - freemac(mac); - } - } else { - pmap_set(¯oht, name, *mac); - } -} - -static void -delmac(internstr name) -{ - struct macro *slot = pmap_get(¯oht, name); - if (!slot) return; - freemac(slot); - pmap_del(¯oht, name); -} - -static inline internstr -macname(struct macro *mac) -{ - return macroht.mb.k[mac - macroht.v]; -} - -static inline struct macro * -findmac(internstr name) -{ - return pmap_get(¯oht, name); -} - -static void popmac(struct lexer *, bool all); - -static struct macrostack { - struct { - union { - uint off; /* mtoksbuf[]/mdyntoksbuf[] */ - const struct token *p; - }; - int n; - } rl; - struct span0 exspan; - int idx; - short macid; /* -1 for argument undergoing expansion */ - bool space : 1, stop : 1, dyn; -} mstk[1200]; - -static void NORETURN -lxfatal(struct lexer *lx, const struct span *span, const char *fmt, ...) -{ - if (fmt) { - va_list ap; - va_start(ap, fmt); - vdiag(span, DGERROR, fmt, ap); - va_end(ap); - } - int n = lx->macstk ? lx->macstk - mstk : 0, i = 0; - for (struct macrostack *l = lx->macstk; l && l > mstk; --l, ++i) { - if (i < 4 || i > n - 5) { - note(&(struct span){l->exspan}, "expanded from here"); - } else if (i == 5) { - efmt(" (...) \n"); - } - } - for (struct lexer *sv = lx->save; sv; sv = sv->save) { - int line; - const char *f = getfilepos(&line, NULL, sv->fileid, sv->chridx-2); - note(NULL, "in file included from %s:%d", f, line); - } - if (!fmt || span) efmt("Aborting due to previous error.\n"); - exit(1); -} - -static void -ppskipline(struct lexer *lx) -{ - while (lx->macstk) popmac(lx, 1); - for (int c; (c = peek(lx, 0)) != '\n' && !lx->eof; next(lx)) { - if (c == '/' && peek(lx, 1) == '*') { /* comment */ - next(lx), next(lx); - bool done = 0; - while (!((c = peek(lx, 0)) == '*' && peek(lx, 1) == '/')) { - if (lx->eof) { - struct span span = {{ lx->idx, lx->chridx - lx->idx, lx->fileid }}; - lxfatal(lx, &span, "unterminated comment"); - } - done = c == '\n'; - next(lx); - } - next(lx); - if (done) return; - } - } -} - -#define isppident(tk) in_range((tk).t, TKIDENT, TKWEND_) - -static bool -tokpaste(struct lexer *lx, struct token *dst, const struct token *l, const struct token *r) -{ - int t; - if (isppident(*l) && (isppident(*r) || r->t == TKNUMLIT)) { - /* foo ## bar ; foo ## 123 */ - t = TKIDENT; - } else if (l->t == TKNUMLIT && (isppident(*r) || r->t == TKNUMLIT)) { - /* 0x ## abc ; 213 ## 456 */ - t = TKNUMLIT; - } else if (l->t && !r->t) { - if (dst) *dst = *l; - return 1; - } else if (!l->t && r->t) { - if (dst) *dst = *r; - return 1; - } else { - static const struct { char s[2]; char t; } tab[] = { - {"==", TKEQU}, {"!=", TKNEQ}, {"<=", TKLTE}, {">=", TKGTE}, - {">>", TKSHR}, {"<<", TKSHL}, {"++", TKINC}, {"--", TKDEC}, - {"->", TKARROW}, {"##", TKPPCAT}, {"&&", TKLOGAND}, {"||", TKLOGIOR}, - {"+=", TKSETADD}, {"-=", TKSETSUB}, {"*=", TKSETMUL}, {"/=", TKSETDIV}, - {"%=", TKSETREM}, {"|=", TKSETIOR}, {"^=", TKSETXOR}, {"&=", TKSETAND}, - {{TKSHL,'='}, TKSETSHL}, {{TKSHR,'='}, TKSETSHR} - }; - for (int i = 0; i < countof(tab); ++i) { - if (tab[i].s[0] == l->t && tab[i].s[1] == r->t) { - if (dst) dst->t = tab[i].t; - return 1; - } - } - - if (dst) { - error(&l->span, "pasting %'tk and %'tk does not form a valid preprocessing token", l, r); - note(&r->span, "right-hand side"); - } - return 0; - } - - if (!dst) return 1; - char buf[200]; - memset(dst, 0, sizeof *dst); - dst->span = l->span; - if (dst->span.ex.file == r->span.ex.file && dst->span.ex.off < r->span.ex.off) - joinspan(&dst->span.ex, r->span.ex); - dst->t = t; - dst->len = l->len + r->len; - char *s = (isppident(*dst) && dst->len + 1 < sizeof buf) ? buf : alloc(lx->tmparena, dst->len + 1, 1); - memcpy(s, l->s, l->len); - memcpy(s + l->len, r->s, r->len); - s[dst->len] = 0; - dst->space = l->space; - if (isppident(*dst)) { - dst->blue = 0; - dst->name = intern(s); - } else { - dst->s = s; - } - return 1; -} - -enum { MAXMACROARGS = 128 }; - -static void -ppdefine(struct lexer *lx) -{ - struct token tk0, tk; - internstr mname; - struct macro mac = {0}; - struct bitset usedparams[BSSIZE(MAXMACROARGS)] = {0}; - - lex0(lx, &tk0, 0); - if (tk0.t != TKIDENT) { - error(&tk0.span, "macro name missing"); - ppskipline(lx); - return; - } - mname = tk0.name; - mac.span = tk0.span.sl; - - if (match(lx, '(')) { - /* gather params for function-like macro */ - vec_of(internstr) params = {0}; - vinit(¶ms, NULL, 4); - mac.fnlike = 1; - while (lex0(lx, &tk, 0) != ')') { - if (mac.variadic) { - error(&tk.span, "expected `)' after `...'"); - if (tk.t == TKEOF || tk.t == '\n') return; - break; - } - if (params.n > 0) { - if (tk.t == TKDOTS) { /* GNU extension 'args...' */ - mac.variadic = 1; - continue; - } if (tk.t != ',') { - error(&tk.span, "expected `,' or `)'"); - if (tk.t == TKEOF || tk.t == '\n') return; - break; - } - lex0(lx, &tk, 0); - } - if (tk.t == TKIDENT) - vpush(¶ms, tk.name); - else if (tk.t == TKDOTS) { - mac.variadic = 1; - vpush(¶ms, intern("__VA_ARGS__")); - } else { - error(&tk.span, "expected parameter name or `)'"); - if (tk.t == TKEOF || tk.t == '\n') return; - break; - } - } - if (!params.n) vfree(¶ms); - mac.param = params.p; - mac.nparam = params.n; - } - - /* gather replacement list */ - mac.rl.off = mtoksbuf.n; - for (int n = 0; lex0(lx, &tk, 0) != '\n' && tk.t != TKEOF;) { - if (n == 0 && !tk.space) - warn(&tk.span, "no whitespace after macro name"); - struct token *prev = n ? &mtoksbuf.p[mtoksbuf.n-1] : NULL; - if (mac.fnlike && tk.t == TKIDENT) { - for (int i = 0; i < mac.nparam; ++i) { - if (tk.name == mac.param[i]) { - bsset(usedparams, i); - tk.argidx = i; - if (prev && prev->t == '#') { - tk.t = TKPPMACSTR; - *prev = tk; - goto Next; - } else { - tk.t = TKPPMACARG; - break; - } - } - } - } - if (n > 1 && prev->t == TKPPCAT) { - struct token new; - if (prev[-1].t != TKPPMACARG && tk.t != TKPPMACARG - && tokpaste(lx, &new, &prev[-1], &tk)) - { - /* trivial concatenations */ - prev[-1] = new; - --mtoksbuf.n; - --n; - continue; - } - } - if (in_range(tk.t, TKNUMLIT, TKSTRLIT) && !tk.litlit) - tk.s = alloccopy(&globarena, tk.s, tk.len << tk.wide, 1); - vpush(&mtoksbuf, tk); - ++n; - Next:; - } - mac.rl.n = mtoksbuf.n - mac.rl.off; - /* mark unused params as such by nulling out param name, - * this way they aren't expanded when unused in the macro body */ - for (uint i = 0; bsiterzr(&i, usedparams, countof(usedparams)) && i < mac.nparam; ++i) { - mac.param[i] = NULL; - } - putmac(mname, &mac); -} - -static void -expecteol(struct lexer *lx, const char *ppname) -{ - struct token tk; - assert(!lx->macstk); - if (lex0(lx, &tk, 0) != '\n' && tk.t != TKEOF) { - (ccopt.pedant ? error : warn)(&tk.span, "extra tokens after #%s", ppname); - ppskipline(lx); - } -} -static void -ppundef(struct lexer *lx) -{ - struct token tk; - - lex0(lx, &tk, 0); - if (tk.t != TKIDENT) { - error(&tk.span, "macro name missing"); - ppskipline(lx); - return; - } - expecteol(lx, "undef"); - delmac(tk.name); -} - -static void -pushmacstk(struct lexer *lx, const struct span *span, const struct macrostack *m) -{ - struct macrostack *l = lx->macstk; - if (!l) l = mstk; - else if ((++l == mstk+countof(mstk))) lxfatal(lx, span, "macro expansion depth limit reached"); - *l = *m; - l->idx = 0; - l->exspan = span->ex; - lx->macstk = l; -} - -static void -popmac(struct lexer *lx, bool all) -{ - struct macrostack *stk; - - assert(stk = lx->macstk); - do { - if (stk->dyn) - mdyntoksbuf.n -= stk->rl.n; - if (lx->macstk == mstk) lx->macstk = NULL; - else --lx->macstk; - if (!all) break; - } while ((stk = lx->macstk) && stk->idx >= stk->rl.n && !stk->stop); -} - - -static inline const struct token * -stkgetrl(struct macrostack *s) -{ - if (s->macid < 0) return s->rl.p; - return (s->dyn ? mdyntoksbuf.p : mtoksbuf.p) + s->rl.off; -} - -static void expandfnmacro(struct lexer *lx, struct span *span, internstr mname, struct macro *mac); - -static enum expandres { EXPNONE, EXPINL, EXPSTACK } -tryexpand(struct lexer *lx, struct token *tk) -{ - struct span span = tk->span; - struct macro *mac = NULL; - internstr mname = tk->name; - - if (tk->t != TKIDENT || tk->blue || !(mac = findmac(mname))) - return EXPNONE; - - /* prevent infinite recursion */ - for (struct macrostack *l = lx->macstk; l && l+1 > mstk; --l) { - if (l->macid == mac->id) { - tk->blue = 1; - return EXPNONE; - } - } - - struct macrostack *stkprev = lx->macstk; - if (mac->special && !mac->fnlike) { - mac->handler(lx, tk); - return EXPINL; - } else if (mac->fnlike) { - /* look if there is a '(' token ahead, expand if so */ - struct macrostack *s = lx->macstk; - if (s && s->idx >= s->rl.n && !s->stop) { - popmac(lx, 1); - s = lx->macstk; - } - if (!s) { /* top-level context: looking ahead in file data */ - struct token tk; - int t; - for (;;) { /* skip whitespace and comments */ - if (aisspace(t = peek(lx, 0))) next(lx); - else if (t == '/') { - int idx = lx->chridx; - switch (peek(lx, 1)) { - case '/': - while (!lx->eof && next(lx) != '\n') ; - continue; - case '*': - next(lx), next(lx); - while (peek(lx, 0) != '*' || peek(lx, 1) != '/') { - if (lx->eof) { - struct span span = {{ idx, lx->chridx - idx, lx->fileid }}; - lxfatal(lx, &span, "unterminated comment"); - } - next(lx); - } - next(lx), next(lx); - continue; - } - break; - } else break; - } - if (t != '(') return 0; - lex0(lx, &tk, 0); - } else { /* expansion context: look ahead in macro stack */ - if (s->idx >= s->rl.n || stkgetrl(s)[s->idx].t != '(') return 0; - ++s->idx; - } - expandfnmacro(lx, &span, mname, mac); - } else if (mac->predef && mac->single) { - struct span span = tk->span; - *tk = *mac->single; - tk->span = span; - return EXPINL; - } else if (mac->rl.n) { - pushmacstk(lx, &span, &(struct macrostack){ - .rl = { .off = mac->rl.off, .n = mac->rl.n }, - .macid = mac->id, - .space = tk->space, - }); - } - if (lx->macstk != stkprev) { - lx->macstk->space = tk->space; - } - return EXPSTACK; -} - -static bool -advancemacstk(struct lexer *lx, struct token *tk) -{ - struct macrostack *s = lx->macstk; - assert(s != NULL); - if (s->idx >= s->rl.n) { - if (s->stop) { - tk->t = TKEOF; - return 1; - } - popmac(lx, 1); - return 0; - } - *tk = stkgetrl(s)[s->idx]; - if (s->idx == 0) { - /* the first token of the replaced expansion gets its space from the - * context in which it is expanded */ - tk->space = s->space; - } - ++s->idx; - assert(tk->t && tk->t != TKEOF); - tk->span.ex = s->exspan; - return tryexpand(lx, tk) != EXPSTACK; -} - -static void -expandfnmacro(struct lexer *lx, struct span *span, internstr mname, struct macro *mac) -{ - struct token _argsbuf[30]; - vec_of(struct token) argsbuf = VINIT(_argsbuf, countof(_argsbuf)); /* buffer for argument tokens */ - struct span excessspan; - int cur, len, i, bal, narg; - struct token tk; - bool toomany = 0; - struct argtks { - int idx, n; /* slices of argsbuf */ - int idx2, n2; - ushort nfirstx, /* for concatenation to work properly with expanded arguments, */ - nlastx; /* length of expanded first and last tokens of the unexpanded argument */ - } _args0[4], - *args = mac->nparam < countof(_args0) ? _args0 : alloc(lx->tmparena, sizeof *args * mac->nparam, 0); - - cur = i = bal = len = narg = 0; - for (struct macrostack *s = lx->macstk;;) { - if (!s) { - bool nl = 0; - for (;; nl = 1) { - lex0(lx, &tk, 0); - if (tk.t != '\n') break; - } - tk.space |= nl; - } - else { - tk = s->idx < s->rl.n ? stkgetrl(s)[s->idx++] : (struct token){TKEOF}; - } - if (((tk.t == ')' && bal == 0) || tk.t == TKEOF)) break; - if (tk.t == ',' && bal == 0) { - ++narg; - if (i == mac->nparam-1 && !mac->variadic) { - excessspan = tk.span; - toomany = 1; - } else if (i < mac->nparam - mac->variadic) { - assert(i < MAXMACROARGS); - args[i].idx = cur; - args[i].n = len; - cur = argsbuf.n; - len = 0; - ++i; - } else if (mac->variadic) { - vpush(&argsbuf, tk); - ++len; - } - } else if (!toomany) { - if (tk.t == '(') ++bal; - else if (tk.t == ')') --bal; - vpush(&argsbuf, tk); - ++len; - } - } - - if (tk.t == TKEOF) { - joinspan(&span->ex, tk.span.ex); - lxfatal(lx, span, "unterminated function-like macro invocation"); - } else if (i < mac->nparam) { - ++narg; - args[i].idx = cur; - args[i].n = len; - cur = argsbuf.n; - len = 0; - ++i; - } - joinspan(&span->ex, tk.span.ex); - int expargs0 = argsbuf.n; - for (int i = 0; i < mac->nparam; ++i) { - struct argtks *arg = &args[i]; - if (i >= narg) { - memset(arg, 0, sizeof *arg); - } else if (!mac->param || (mac->param[i] && arg->n > 0)) { - /* expand args used in the macro body */ - pushmacstk(lx, &tk.span, &(struct macrostack) { - .rl = { .p = argsbuf.p + arg->idx, .n = arg->n }, - .macid = -1, - .stop = 1, - }); - struct macrostack *l = lx->macstk; - arg->idx2 = argsbuf.n; - arg->nfirstx = arg->nlastx = 1; - int ilastx = -1; - for (bool pad = 0;;) { - struct macrostack *sprev = lx->macstk; - if (!advancemacstk(lx, &tk)) { - pad |= tk.space && lx->macstk == sprev; /* preserve whitespace empty macro */ - if (lx->macstk == l && l->idx == 1) - arg->nfirstx = argsbuf.n - arg->idx2; - if (lx->macstk == l+1 && lx->macstk->idx == 0 && l->idx == l->rl.n) - ilastx = argsbuf.n - arg->idx2; - continue; - } - if (tk.t == TKEOF) break; - size_t off = l->rl.p - argsbuf.p; - tk.space |= pad; - vpush(&argsbuf, tk); - l->rl.p = argsbuf.p + off; - pad = 0; - } - arg->n2 = argsbuf.n - arg->idx2; - arg->nlastx = ilastx < 0 ? 1 : args->n2 - ilastx; - assert(lx->macstk == l); - popmac(lx, 0); - } else { - memset(arg, 0, sizeof *arg); - } - } - if (narg < mac->nparam - mac->variadic) { - warn(span, "macro `%s' passed %d arguments, but takes %d", mname, narg, mac->nparam); - } else if (toomany) { - joinspan(&excessspan.ex, tk.span.ex); - warn(&excessspan, "macro `%s' passed %d arguments, but takes just %d", mname, narg, mac->nparam); - } - if (mac->special) { - mac->handlerfn(lx, &tk, argsbuf.p+expargs0, argsbuf.n-expargs0); - vpush(&mdyntoksbuf, tk); - pushmacstk(lx, span, &(struct macrostack){ - .rl = { .off = mdyntoksbuf.n-1, .n = 1 }, - .dyn = 1, - .macid = mac->id, - }); - } else if (mac->nparam > 0) { /* make new rlist with args replaced */ - bool vaoptskip = 0, spacepad = 0; - int vaoptbal = 0; - uint off = mdyntoksbuf.n; - for (int i = 0; i < mac->rl.n; ++i) { - struct argtks *arg; - const struct token *tki = &mtoksbuf.p[mac->rl.off+i]; - if (vaoptskip) { - assert(vaoptbal > 0); - if (tki->t == '(') ++vaoptbal; - else if (tki->t == ')') { - if (--vaoptbal == 0) vaoptskip = 0; - } - continue; - } - if (tki->t == TKPPCAT && i > 0 && i < mac->rl.n-1) { /* concatenation */ - const struct token *lhs = tki-1, - *rhs = tki+1; - bool space = lhs->space | spacepad; - if (lhs->t == ',' && mac->variadic - && rhs->t == TKPPMACARG && rhs->argidx == mac->nparam-1) { - /* handle GNU extension: ', ## __VA_ARGS__' */ - arg = &args[rhs->argidx]; - if (narg < mac->nparam) { /* no vaargs -> skip comma */ - assert(arg->n == 0); - --mdyntoksbuf.n; - } else { /* otherwise put comma and substitute vaargs */ - vpushn(&mdyntoksbuf, argsbuf.p+arg->idx2, arg->n2); - mdyntoksbuf.p[mdyntoksbuf.n - arg->n2].space |= rhs->space | tk.space; - } - ++i; /* we already handled rhs (__VA_ARGS__) */ - continue; - } - if (i > 2 && tki[-2].t == TKPPCAT) { - /* handles chained concatenations: xyz ## arg ## c - * lhs ^ rhs */ - lhs = (off < mdyntoksbuf.n) ? &mdyntoksbuf.p[--mdyntoksbuf.n] : NULL; - } else if (lhs->t == TKPPMACARG) { - arg = &args[lhs->argidx]; - lhs = arg->n ? &argsbuf.p[arg->idx + arg->n-1] : NULL; - if (lhs && arg->n > 1) space |= lhs->space; - } else { - --mdyntoksbuf.n; - } - if (rhs->t == TKPPMACARG) { - arg = &args[rhs->argidx]; - rhs = arg->n ? &argsbuf.p[arg->idx] : NULL; - } else { - ++i; - } - if (!lhs && !rhs) continue; - spacepad = 0; - if (!lhs) vpush(&mdyntoksbuf, *rhs); - else if (!rhs) vpush(&mdyntoksbuf, *lhs); - else { - struct token new; - if (tokpaste(lx, &new, lhs, rhs)) { - new.span.sl = tki->span.sl; - } - vpush(&mdyntoksbuf, new); - } - mdyntoksbuf.p[mdyntoksbuf.n-1].space = space; - } else if (tki->t != TKPPMACARG && tki->t != TKPPMACSTR) { /* regular token */ - if (tki->t == TKIDENT && mac->variadic) { - /* handle GNUC __VA_OPT__(...) */ - static internstr istr_vaopt; - if (!istr_vaopt) istr_vaopt = intern("__VA_OPT__"); - if (tki->name == istr_vaopt && i+2 < mac->rl.n && tki[1].t == '(') { - vaoptbal = 1; - vaoptskip = args[mac->nparam-1].n == 0; - ++i; /* skip open paren */ - continue; - } - } - if (vaoptbal) { - if (tki->t == '(') ++vaoptbal; - else if (tki->t == ')') { - /* skip closing paren of __VA_OPT__ invocation */ - if (--vaoptbal == 0) continue; - } - } - vpush(&mdyntoksbuf, *tki); - mdyntoksbuf.p[mdyntoksbuf.n-1].space |= spacepad; - spacepad = 0; - } else if (tki->t == TKPPMACARG) { - arg = &args[tki->argidx]; - if (arg->n == 0) { - spacepad = 1; - continue; - } - struct token *rl = argsbuf.p + arg->idx2; - int n = arg->n2; - bool skipfirst = 0; - if (i > 0 && tki[-1].t == TKPPCAT) { - /* skip first unexpanded token, was pasted */ - rl += arg->nfirstx; - n -= arg->nfirstx; - skipfirst = 1; - } - if (i < mac->rl.n-2 && tki[1].t == TKPPCAT) { - /* skip last unexpanded token, will be pasted */ - n -= arg->nlastx; - } - if (n > 0) { - vpushn(&mdyntoksbuf, rl, n); - if (!skipfirst) - /* the first token of the expanded body gets its space from the replacement list */ - mdyntoksbuf.p[mdyntoksbuf.n - n].space = tki->space | spacepad; - } - spacepad = 0; - } else { /* PPMACSTR */ - char tmp[200]; - struct wbuf buf = MEMBUF(tmp, sizeof tmp); - int n = 0; - - arg = &args[tki->argidx]; - // XXX this is wrong bc the string literal produced should be re-parsed later - // i.e. stringifying the token sequence '\n' should ultimately produce a - // string with an actual newline, not {'\\','n'} - Redo: - for (int i = 0; i < arg->n; ++i) { - struct token *tk = &argsbuf.p[arg->idx + i]; - if (i > 0 && tk->space) - n += bfmt(&buf, " "); - n += bfmt(&buf, "%tk", tk); - } - ioputc(&buf, 0); - if (buf.err) { - struct wbuf new = MEMBUF(alloc(lx->tmparena, n+1, 1), n+1); - assert(buf.buf == tmp); - memcpy(&buf, &new, sizeof buf); - goto Redo; - } - vpush(&mdyntoksbuf, ((struct token) { - .t = TKSTRLIT, - .wide = 0, - .space = tki->space | spacepad, - .s = buf.buf != tmp ? buf.buf : alloccopy(lx->tmparena, buf.buf, buf.len, 1), - .len = buf.len-1, - })); - spacepad = 0; - } - } - uint n = mdyntoksbuf.n - off; - - if (n) { - pushmacstk(lx, span, &(struct macrostack){ - .rl = { .off = off, .n = n }, - .macid = mac->id, - .dyn = 1, - }); - } - } else if (mac->rl.n) { - pushmacstk(lx, span, &(struct macrostack){ - .rl = { .off = mac->rl.off, .n = mac->rl.n }, - .macid = mac->id, - }); - } - vfree(&argsbuf); -} - -static struct token epeektk; -static int -elex(struct lexer *lx, struct token *tk) -{ - assert(tk); - if (epeektk.t) { - int tt = epeektk.t; - if (tk) *tk = epeektk; - epeektk.t = 0; - return tt; - } - if (lx->macstk) { - if (!advancemacstk(lx, tk)) - return elex(lx, tk); - return tk->t; - } - - lex0(lx, tk, 0); - return tk->t; -} - -static int -epeek(struct lexer *lx, struct token *tk) -{ - if (!epeektk.t) elex(lx, &epeektk); - if (tk) *tk = epeektk; - return epeektk.t; -} - -static int -tkprec(int tt) -{ - static const char tab[] = { - ['*'] = 12, ['/'] = 12, ['%'] = 12, - ['+'] = 11, ['-'] = 11, - [TKSHL] = 10, [TKSHR] = 10, - ['<'] = 9, ['>'] = 9, [TKLTE] = 9, [TKGTE] = 9, - [TKEQU] = 8, [TKNEQ] = 8, - ['&'] = 7, - ['^'] = 6, - ['|'] = 5, - [TKLOGAND] = 4, - [TKLOGIOR] = 3, - ['?'] = 2, - }; - if ((uint)tt < countof(tab)) - return tab[tt] - 1; - return -1; -} - -static vlong -expr(struct lexer *lx, bool *pu, int prec, bool ignore) -{ - struct token tk; - enum typetag ty; - char unops[16]; - int nunop = 0; - vlong x, y; - bool xu = 0, yu; /* x unsigned?; y unsigned? */ - -Unary: - elex(lx, &tk); -Switch: - switch (tk.t) { - case '-': case '~': case '!': - unops[nunop++] = tk.t; - if (nunop >= countof(unops)) { - x = expr(lx, &xu, 999, ignore); - break; - } - /* fallthru */ - case '+': goto Unary; - case '(': - x = expr(lx, &xu, 1, ignore); - if (elex(lx, &tk) != ')') { - error(&tk.span, "expected ')'"); - goto Err; - } - break; - case TKNUMLIT: - case TKCHRLIT: - ty = parsenumlit((uvlong *)&x, NULL, &tk, 1); - if (!ty) { - error(&tk.span, "bad number literal"); - goto Err; - } else if (isfltt(ty)) { - error(&tk.span, "float literal in preprocessor expresion"); - goto Err; - } - xu = isunsignedt(ty); - break; - default: - if (tk.t == TKIDENT) { - xu = 0; - if (!strcmp(tk.s, "defined")) { - /* 'defined' ppident */ - bool paren = 0; - lex0(lx, &tk, 0); - if ((paren = tk.t == '(')) lex0(lx, &tk, 0); - if (!isppident(tk)) { - error(&tk.span, "expected macro name"); - goto Err; - } - if (paren && lex0(lx, &tk, 0) != ')') { - error(&tk.span, "expected `)'"); - goto Err; - } - x = findmac(tk.name) != NULL; - } else { - switch (tryexpand(lx, &tk)) { - case EXPSTACK: goto Unary; - case EXPINL: goto Switch; - case EXPNONE: x = 0; break; /* non defined pp name -> 0 */ - } - } - break; - } - error(&tk.span, "expected preprocessor integer expression (near %'tk)", &tk); - goto Err; - } - - while (nunop > 0) switch (unops[--nunop]) { - case '-': x = -(uvlong)x; break; - case '~': x = ~x; break; - case '!': x = !x; break; - default: assert(0); - } - - for (int opprec; (opprec = tkprec(epeek(lx, &tk))) >= prec;) { - elex(lx, &tk); - if (tk.t == TKLOGAND) { - x = !!x & !!expr(lx, &yu, opprec+1, ignore || !x); - xu = 0; - } else if (tk.t == TKLOGIOR) { - x = !!x | !!expr(lx, &yu, opprec+1, ignore || x); - xu = 0; - } else if (tk.t == '?') { - struct span span = tk.span; - vlong m = expr(lx, &xu, 1, ignore || !x); - if (elex(lx, &tk) != ':') { - error(&tk.span, "expected ':'"); - note(&span, "to match conditional expression here"); - goto Err; - } - y = expr(lx, &yu, 1, ignore || x); - x = x ? m : y; - xu |= yu; - } else { - y = expr(lx, &yu, opprec + 1, ignore); - bool u = xu | yu; - switch ((int) tk.t) { - case '+': x += (uvlong) y; break; - case '-': x -= (uvlong) y; break; - case '*': x = u ? (uvlong) x * y : x * y; break; - case '&': x &= y; break; - case '^': x ^= y; break; - case '|': x |= y; break; - case '/': if (y) x = u ? (uvlong) x / y : x / y; - else if (ignore) x = 0; - else goto Div0; - break; - case '%': if (y) x = u ? (uvlong) x % y : x % y; - else if (ignore) x = 0; - else Div0: error(&tk.span, "division by zero"); - break; - case TKSHL: if ((uvlong)y < 64) x <<= y; - else if (ignore) x = 0; - else goto BadShift; - break; - u = xu; - case TKSHR: if ((uvlong)y < 64) x = u ? (uvlong) x >> y : x >> y; - else if (ignore) x = 0; - else BadShift: error(&tk.span, "bad shift by %ld", y); - u = xu; - break; - case '<': x = u ? (uvlong) x < y : x < y; u = 0; break; - case '>': x = u ? (uvlong) x > y : x > y; u = 0; break; - case TKLTE: x = u ? (uvlong) x <= y : x <= y; u = 0; break; - case TKGTE: x = u ? (uvlong) x >= y : x >= y; u = 0; break; - case TKEQU: x = x == y; u = 0; break; - case TKNEQ: x = x != y; u = 0; break; - default: assert(0); - } - xu = u; - } - } - if (!prec) { /* not a sub expr */ - if (elex(lx, &tk) != '\n' && tk.t != TKEOF) { - error(&tk.span, "extra tokens after preprocessor expression"); - ppskipline(lx); - } - } - if (pu) *pu = xu; - return x; - -Err: - ppskipline(lx); - if (pu) *pu = xu; - return 0; -} - -enum { - PPCNDFALSE, /* the condition was zero, skip until #else/#elif */ - PPCNDTRUE, /* the condition was non-zero, emit until #else/#elif */ - PPCNDTAKEN /* some branch was already taken, skip until #else */ -}; -static struct ppcnd { - struct span0 ifspan; - int filedepth; - uchar cnd; - bool elsep; -} ppcndstk[32]; -static int nppcnd; - -static int includedepth; - -static void -ppif(struct lexer *lx, const struct span *span) -{ - vlong v = expr(lx, NULL, 0, 0); - assert(nppcnd < countof(ppcndstk) && "too many nested #if"); - ppcndstk[nppcnd].ifspan = span->sl; - ppcndstk[nppcnd].filedepth = includedepth; - ppcndstk[nppcnd].cnd = v ? PPCNDTRUE : PPCNDFALSE; - ppcndstk[nppcnd++].elsep = 0; -} - -static void -ppifxdef(struct lexer *lx, bool defp, const struct span *span) -{ - struct token tk; - - lex0(lx, &tk, 0); - if (tk.t != TKIDENT) { - error(&tk.span, "macro name missing"); - ppskipline(lx); - return; - } - expecteol(lx, defp ? "ifdef" : "ifndef"); - if (!defp && lx->firstdirective) lx->inclguard = tk.name; - assert(nppcnd < countof(ppcndstk) && "too many nested #if"); - ppcndstk[nppcnd].ifspan = span->sl; - ppcndstk[nppcnd].filedepth = includedepth; - ppcndstk[nppcnd].cnd = (findmac(tk.name) == NULL) ^ defp ? PPCNDTRUE : PPCNDFALSE; - ppcndstk[nppcnd++].elsep = 0; -} - -static void -ppelif(struct lexer *lx, const struct span *span) -{ - vlong v; - struct ppcnd *cnd; - - if (!nppcnd) { - error(span, "#elif without matching #if"); - ppif(lx, span); - return; - } - v = expr(lx, NULL, 0, 0); - cnd = &ppcndstk[nppcnd-1]; - if (cnd->elsep) { - error(span, "#elif after #else"); - return; - } - switch (cnd->cnd) { - case PPCNDTRUE: cnd->cnd = PPCNDTAKEN; break; - case PPCNDFALSE: cnd->cnd = v ? PPCNDTRUE : PPCNDFALSE; break; - } -} -static void -ppelifxdef(struct lexer *lx, bool defp, const struct span *span) -{ - struct token tk; - struct ppcnd *cnd; - - if (!nppcnd) { - error(span, "#elif%sdef without matching #if", &"n"[defp]); - ppif(lx, span); - return; - } - cnd = &ppcndstk[nppcnd-1]; - if (cnd->elsep) { - error(span, "#elif%sdef after #else", &"n"[defp]); - return; - } - lex0(lx, &tk, 0); - if (tk.t != TKIDENT) { - error(&tk.span, "macro name missing"); - ppskipline(lx); - return; - } - expecteol(lx, defp ? "elifdef" : "elifndef"); - switch (cnd->cnd) { - case PPCNDTRUE: cnd->cnd = PPCNDTAKEN; break; - case PPCNDFALSE: cnd->cnd = (findmac(tk.name) == NULL) ^ defp ? PPCNDTRUE : PPCNDFALSE; break; - case PPCNDTAKEN: assert(0); - } -} - -static void -ppendif(struct lexer *lx, const struct span *span) -{ - expecteol(lx, "endif"); - if (!nppcnd) { - error(span, "#endif without matching #if"); - return; - } - --nppcnd; -} - -static void -ppelse(struct lexer *lx, const struct span *span) -{ - struct ppcnd *cnd; - expecteol(lx, "else"); - if (!nppcnd) { - error(span, "#else without matching #if"); - return; - } - cnd = &ppcndstk[nppcnd-1]; - if (cnd->elsep) - error(span, "#else after #else"); - switch (cnd->cnd) { - case PPCNDFALSE: cnd->cnd = PPCNDTRUE; break; - case PPCNDTRUE: cnd->cnd = PPCNDTAKEN; break; - } - cnd->elsep = 1; -} - -enum { MAXINCLUDE = 200 }; -static bool -tryincludepath(struct lexer *lx, const struct span *span, char *path) -{ - struct lexer new; - const char *err; - switch (initlexer(&new, &err, path)) { - default: assert(0); - case LXERR: return 0; - case LXFILESEEN: - xbfree(path); - /* fallthru */ - case LXOK: - new.save = xmalloc(sizeof *new.save); - lx->inclnerror = nerror; - lx->inclnwarn = nwarn; - memcpy(new.save, lx, sizeof *lx); - *lx = new; - - if (++includedepth == MAXINCLUDE) - lxfatal(lx, span, "Maximum nested include depth of %d reached", includedepth); - break; - case LXFILESKIP: - xbfree(path); - break; - } - return 1; -} - -static bool -doinclude(struct lexer *lx, const struct span *span, bool quote, const char *str, size_t slen) -{ - char *path = NULL; - const char *base, *end; - if (quote) { - if (str[0] == '/') { - /* try absolute path */ - xbgrow(&path, slen + 1); - memcpy(path, str, slen); - path[slen] = 0; - if (tryincludepath(lx, span, path)) return 1; - goto NotFound; - } - - /* try relative to current file's directory */ - base = getfilename(lx->fileid, 0); - for (end = base; *end != 0; ++end) {} - for (--end; *end != '/' && end != base; --end) {} - if (*end == '/') ++end; - xbgrow(&path, end - base + slen + 1); - memcpy(path, base, end - base); - memcpy(path + (end - base), str, slen); - path[end - base + slen] = 0; - if (tryincludepath(lx, span, path)) return 1; - } - /* try system paths. order: - * 1. -iquote - * 2. -I - * 3. -isystem - * 4. embedded include files - * 5. standard system includes - * 6. -idirafter - */ - for (int i = quote ? CINCL_iquote : CINCL_I; i < countof(cinclpaths); ++i) { - for (struct inclpath *p = cinclpaths[i].list; p; p = p->next) { - if (i == CINCLsys) { - /* try embedded files pseudo-path */ - xbgrow(&path, slen + 3); - path[0] = '@', path[1] = ':'; - memcpy(path+2, str, slen); - path[slen+2] = 0; - if (tryincludepath(lx, span, path)) return 1; - } - int ndir = strlen(p->path); - xbgrow(&path, ndir + slen + 2); - memcpy(path, p->path, ndir); - path[ndir++] = '/'; - memcpy(path + ndir, str, slen); - path[ndir + slen] = 0; - if (tryincludepath(lx, span, path)) return 1; - } - } -NotFound: - error(span, "file not found: %'S", str, slen); - xbfree(path); - return 0; -} - -static bool -ppinclude(struct lexer *lx, const struct span *span0) -{ - struct token tk; - struct span span = *span0; - - if (in_range(lex0(lx, &tk, 1), TKPPHDRH, TKPPHDRQ)) { - expecteol(lx, "include"); - joinspan(&span.ex, tk.span.ex); - return doinclude(lx, &span, tk.t == TKPPHDRQ, tk.s, tk.len); - } else if (tk.t == '\n' || tk.t == TKEOF) { - goto BadSyntax; - } else { - /* '#include pp-tokens' - * gather and expand pp-tokens */ - struct token tksbuf[8]; - vec_of(struct token) tks = VINIT(tksbuf, countof(tksbuf)); - for (;;) { - if (!lx->macstk) { - if (tryexpand(lx, &tk) == EXPSTACK) continue; - vpush(&tks, tk); - } else if (advancemacstk(lx, &tk)) { - vpush(&tks, tk); - continue; - } - if (lex0(lx, &tk, 0) == '\n' || tk.t == TKEOF) break; - } - if (tks.n >= 1 && tks.p[0].t == TKSTRLIT) { /* "header.h" */ - if (tks.n > 1) - (ccopt.pedant ? error : warn)(&tks.p[1].span, "extra tokens after #include"); - joinspan(&span.ex, tks.p[0].span.ex); - return doinclude(lx, &span, 1, tks.p[0].s, tks.p[0].len); - } else if (tks.n > 2 && tks.p[0].t == '<' && tks.p[tks.n-1].t == '>') { /* <header.h> */ - /* this is multiple tokens, concatenate them together */ - char buf[4096]; - struct wbuf wbuf = MEMBUF(buf, sizeof buf); - for (int i = 1; i < tks.n-1; ++i) { - struct token *tk = &tks.p[i]; - bfmt(&wbuf, &" %tk"[!tk->space], tk); - } - joinspan(&span.ex, tks.p[tks.n-1].span.ex); - if (wbuf.err) error(&span, "path too long"); - else { - return doinclude(lx, &span, 0, buf, wbuf.len); - } - } else { - BadSyntax: - error(&tk.span, "expected \"header\" or <header>"); - ppskipline(lx); - } - vfree(&tks); - } - return 1; -} - -static void -ppline(struct lexer *lx, struct token *tk0) -{ - struct token tk, tks[2]; - int ntk = 0; - struct span span = tk0->span; - bool ext = 0; - if (tk0->t == TKNUMLIT) { /* handles GNU-style post preprocessing directive '# n ...' */ - tks[ntk++] = *tk0; - ext = 1; - } - while (ntk < 2) { - if (lx->macstk && advancemacstk(lx, &tk)) { - tks[ntk++] = tk; - if (lx->macstk->idx >= lx->macstk->rl.n) popmac(lx, 1); - } else if (!lx->macstk && (lex0(lx, &tk, 0) == '\n' || tk.t == TKEOF)) { - break; - } else if (tk.t == TKIDENT && tryexpand(lx, &tk) == EXPSTACK) { - continue; - } else { - tks[ntk++] = tk; - } - } - uvlong lineno = 0; - char *file = NULL; - if (ntk > 0 && tks[0].t == TKNUMLIT) { - if (!parsenumlit(&lineno, NULL, &tks[0], 1) || (lineno == 0 && !ext)) - goto BadNum; - if (lineno >= 1<<(32-SPANFILEBITS)) { - warn(&tks[0].span, "ignoring #line number that is too big"); - lineno = 0; - goto Err; - } - } else { - BadNum: - error(ntk ? &tks[0].span : &span, "#line requires a positive integer argument"); - Err: - if (lx->macstk || (tk.t != '\n' && tk.t != TKEOF)) ppskipline(lx); - return; - } - if (ntk > 1) { - if (tks[1].t == TKSTRLIT && !tks[1].wide) { - file = alloc(&globarena, tks[1].len+1, 0); - memcpy(file, tks[1].s, tks[1].len); - file[tks[1].len] = 0; - } else { - error(&tks[1].span, "invalid filename for #line directive"); - } - } - if (lineno) setfileline(lx->fileid, lx->chridx, lineno, file); - if (lx->macstk) { - span.sl.off = span.ex.off = lx->chridx; - span.sl.len = span.ex.len = 1; - ppskipline(lx); - if (!ext) - (ccopt.pedant ? error : warn)(&span, "extra tokens after #line"); - } else if (tk.t != '\n' && tk.t != TKEOF) { - if (ext) ppskipline(lx); - else expecteol(lx, "line"); - } -} - -static void -pppragma(struct lexer *lx, const struct span *span0) -{ - struct token tk; - struct span span = *span0; - if (lex0(lx, &tk, 0) == TKIDENT && !strcmp(tk.s, "once")) { - markfileonce(lx->fileid, NULL); - } else { - joinspan(&span.ex, tk.span.ex); - warn(&span, "unknown pragma ignored"); - ppskipline(lx); - return; - } - expecteol(lx, "pragma"); -} - -static void -ppdiag(struct lexer *lx, const struct span *span0, bool err) -{ - const uchar *p = getfile(lx->fileid)->p; - uint off = lx->chridx, end; - ppskipline(lx); - end = lx->chridx; - while (off < end && aisspace(p[off])) ++off; - (err ? error : warn)(span0, "%S", p + off, end - off); -} - -enum directive { - PPXXX, - /* !sorted */ - PPDEFINE, - PPELIF, - PPELIFDEF, - PPELIFNDEF, - PPELSE, - PPENDIF, - PPERROR, - PPIF, - PPIFDEF, - PPIFNDEF, - PPINCLUDE, - PPLINE, - PPPRAGMA, - PPUNDEF, - PPWARNING, -}; - -static enum directive -findppcmd(const struct token *tk) -{ - static const char *tab[] = { - /* !sorted */ - "define", - "elif", - "elifdef", - "elifndef", - "else", - "endif", - "error", - "if", - "ifdef", - "ifndef", - "include", - "line", - "pragma", - "undef", - "warning", - }; - int l = 0, h = countof(tab) - 1, i, cmp; - const char *s = tk->s; - - if (tk->t == TKWif) return PPIF; - if (tk->t == TKWelse) return PPELSE; - /* binary search over sorted array */ - while (l <= h) { - i = (l + h) / 2; - cmp = strcmp(tab[i], s); - if (cmp < 0) l = i + 1; - else if (cmp > 0) h = i - 1; - else return i + 1; - } - return PPXXX; -} - -static void -identkeyword(struct token *tk) -{ -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmissing-braces" -#endif - static const struct { - const char *s; - struct kw { uchar t, cstd : 4, ext : 1; } kw; - const char *alias[2]; - } kwtab[] = { -#define _(kw, cstd, ...) { #kw, {TKW##kw, cstd}, __VA_ARGS__ }, -#include "keywords.def" -#undef _ - }; -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - static pmap_of(struct kw) kwmap; - if (!kwmap.v) { - pmap_init(&kwmap, 128); - for (int i = 0; i < countof(kwtab); ++i) { - struct kw kw = kwtab[i].kw; - /* allow future keywords but only if they begin with _ */ - if (kw.cstd <= ccopt.cstd || kwtab[i].s[0] == '_') { - kw.ext = kw.cstd > ccopt.cstd; - pmap_set(&kwmap, intern(kwtab[i].s), kw); - } - for (const char *const *palias = kwtab[i].alias, *const *end = palias+2; - palias != end && *palias; ++palias) - { - pmap_set(&kwmap, intern(*palias), kw); - } - } - } - struct kw *kw = pmap_get(&kwmap, tk->name); - if (kw) { - tk->t = kw->t; - tk->extwarn = kw->ext; - } -} - -int -lex(struct lexer *lx, struct token *tk_) -{ - struct token tkx[1], *tk; - int t; - -Begin: - assert(tk_ != &lx->peektok); - tk = tk_ ? tk_ : tkx; - if (lx->peektok.t) { - *tk = lx->peektok; - memset(&lx->peektok, 0, sizeof lx->peektok); - return tk->t; - } - - if (lx->macstk) { - if (!advancemacstk(lx, tk)) - goto Begin; - if (tk->t == TKIDENT) identkeyword(tk); - return tk->t; - } - bool linebegin = 1, - skip = nppcnd ? ppcndstk[nppcnd-1].cnd != PPCNDTRUE : 0, - inclerror = 0; /* set when #include header file not found: process other directives then abort */ - enum directive lastcmd = 0; - for (;;) { - while ((t = lex0(lx, tk, 0)) == '\n') linebegin = 1; - if (t == '#' && linebegin) { - if (lex0(lx, tk, 0) == '\n') { } - else if (tk->t == TKNUMLIT || tk->t == TKIDENT) { - lastcmd = tk->t == TKNUMLIT ? PPLINE : findppcmd(tk); - if (nppcnd == lx->nppcnd0) lx->inclguard = NULL; - if (!skip) { - switch (lastcmd) { - case PPXXX: goto BadPP; - case PPDEFINE: ppdefine(lx); break; - case PPUNDEF: ppundef(lx); break; - case PPIF: ppif(lx, &tk->span); break; - case PPIFDEF: ppifxdef(lx, 1, &tk->span); break; - case PPIFNDEF: ppifxdef(lx, 0, &tk->span); break; - case PPELIF: ppelif(lx, &tk->span); break; - case PPELIFDEF: ppelifxdef(lx, 1, &tk->span); break; - case PPELIFNDEF: ppelifxdef(lx, 0, &tk->span); break; - case PPELSE: ppelse(lx, &tk->span); break; - case PPENDIF: ppendif(lx, &tk->span); break; - case PPLINE: ppline(lx, tk); break; - case PPPRAGMA: pppragma(lx, &tk->span); break; - case PPWARNING: ppdiag(lx, &tk->span, 0); break; - case PPERROR: ppdiag(lx, &tk->span, 1); break; - case PPINCLUDE: inclerror |= !ppinclude(lx, &tk->span); break; - default: assert(0&&"nyi"); - } - } else { - switch (lastcmd) { - case PPIF: /* increment nesting level */ - case PPIFDEF: - case PPIFNDEF: - assert(nppcnd < countof(ppcndstk) && "too many nested #if"); - ppcndstk[nppcnd].ifspan = tk->span.sl; - ppcndstk[nppcnd].cnd = PPCNDTAKEN; - ppcndstk[nppcnd++].elsep = 0; - break; - case PPELIF: ppelif(lx, &tk->span); break; - case PPELIFDEF: ppelifxdef(lx, 1, &tk->span); break; - case PPELIFNDEF: ppelifxdef(lx, 0, &tk->span); break; - case PPELSE: ppelse(lx, &tk->span); break; - case PPENDIF: ppendif(lx, &tk->span); break; - default: ppskipline(lx); break; - } - } - if (lastcmd != PPINCLUDE) - lx->firstdirective = 0; - skip = nppcnd ? ppcndstk[nppcnd-1].cnd != PPCNDTRUE : 0; - } else { - if (!skip) { - BadPP: - error(&tk->span, "invalid preprocessor directive"); - } - ppskipline(lx); - } - linebegin = 1; - } else { - lx->firstdirective = 0; - linebegin = 0; - if (skip && t != TKEOF) - continue; - if (tryexpand(lx, tk) == EXPSTACK) - goto Begin; - if (t == TKEOF && nppcnd && ppcndstk[nppcnd-1].filedepth == includedepth) { - struct span span = { ppcndstk[nppcnd-1].ifspan }; - error(&span, "#if is not matched by #endif"); - } - if (t == TKEOF && lx->save) { - /* end of #include'd file, restore previous state */ - if (lastcmd == PPENDIF && lx->inclguard) { - markfileonce(lx->fileid, lx->inclguard); - } - struct lexer *sv = lx->save; - if (sv->inclnerror != nerror || sv->inclnwarn != nwarn) { - int line; - const char *f = getfilepos(&line, NULL, sv->fileid, sv->chridx-2); - note(NULL, "in file included from %s:%d", f, line); - } - memcpy(lx, sv, sizeof *lx); - free(sv); - --includedepth; - linebegin = 1; - lx->firstdirective = 0; - } else if (t == TKEOF && inclerror) { - break; - } else { - if (nppcnd == lx->nppcnd0) lx->inclguard = NULL; - if (t == TKIDENT) identkeyword(tk); - if (!inclerror) return tk->t; - } - } - } - assert(inclerror); - efmt("Aborting due to previous error(s).\n"); - exit(1); - assert(0); -} - -int -lexpeek(struct lexer *lx, struct token *tk_) -{ - struct token tkx[1], *tk; - uint t; - - tk = tk_ ? tk_ : tkx; - if ((t = lx->peektok.t)) { - *tk = lx->peektok; - return t; - } - t = lex(lx, tk); - lx->peektok = *tk; - return t; -} - -/* Predefined/builtin macros */ - -static vec_of(uchar) ppcmdline; - -void -cpppredef(bool undef, const char *cmd) -{ - const char *sep = strchr(cmd, '='), *body = sep ? sep+1 : "1"; - uint namelen = sep ? sep - cmd : strlen(cmd); - char line[1024]; - struct wbuf wbuf = MEMBUF(line, sizeof line); - if (!ppcmdline.p) vinit(&ppcmdline, NULL, 1<<10); - int n; - if (undef) - n = bfmt(&wbuf, "#undef %S\n", cmd, namelen); - else - n = bfmt(&wbuf, "#define %S %s\n", cmd, namelen, body); - assert(n <= sizeof line); - vpushn(&ppcmdline, line, n); -} - -static void -mac__file__(struct lexer *lx, struct token *tk) -{ - tk->t = TKSTRLIT; - tk->s = getfilename(lx->fileid, lx->chridx); - tk->wide = 0; - tk->len = strlen(tk->s); -} - -static void -mac__line__(struct lexer *lx, struct token *tk) -{ - char buf[20]; - int line; - struct wbuf wbuf = MEMBUF(buf, sizeof buf); - getfilepos(&line, NULL, lx->fileid, lx->chridx); - bfmt(&wbuf, "%d", line), buf[wbuf.len++] = 0; - tk->t = TKNUMLIT; - tk->s = alloccopy(lx->tmparena, buf, wbuf.len, 1); - tk->len = wbuf.len-1; -} - -#include <time.h> - -static void -mac__date__(struct lexer *lx, struct token *tk) -{ - char buf[20]; - struct wbuf wbuf = MEMBUF(buf, sizeof buf); - time_t tm = time(NULL); - struct tm *ts = localtime(&tm); - tk->t = TKSTRLIT; - tk->wide = 0; - tk->len = 11; - if (ts) { - bfmt(&wbuf, "%S %2d %4d%c", - &"JanFebMarAprMayJunJulAugSepOctNovDec"[ts->tm_mon*3], 3, - ts->tm_mday, 1900+ts->tm_year, 0); - assert(wbuf.len == 11+1); - tk->s = alloccopy(lx->tmparena, buf, wbuf.len, 1); - } else { - tk->s = "\?\?\? \?\? \?\?\?\?"; - } -} - -static void -mac__time__(struct lexer *lx, struct token *tk) -{ - char buf[20]; - struct wbuf wbuf = MEMBUF(buf, sizeof buf); - time_t tm = time(NULL); - struct tm *ts = localtime(&tm); - tk->t = TKSTRLIT; - tk->wide = 0; - tk->len = 8; - if (ts) { - bfmt(&wbuf, "%.2d:%.2d:%.2d%c", ts->tm_hour, ts->tm_min, ts->tm_sec, 0); - tk->s = alloccopy(lx->tmparena, buf, wbuf.len, 1); - assert(wbuf.len == 8+1); - } else { - tk->s = "\?\?:\?\?:\?\?"; - } -} - -static void -mac__counter__(struct lexer *lx, struct token *tk) -{ - char buf[20]; - struct wbuf wbuf = MEMBUF(buf, sizeof buf); - static int counter; - bfmt(&wbuf, "%d", counter++), buf[wbuf.len++] = 0; - tk->t = TKNUMLIT; - tk->s = alloccopy(lx->tmparena, buf, wbuf.len, 1); - tk->len = wbuf.len-1; -} - -static void -mac__has_builtin(struct lexer *lx, struct token *tk, const struct token *args, int narg) -{ - extern bool hasbuiltin(const char *, uint n); - bool has = 0; - tk->t = TKNUMLIT, tk->len = 1; - if (narg >= 1) { - if (args[0].t == TKIDENT) - has = hasbuiltin(args[0].s, args[0].len); - else if (in_range(args[0].t, TKWBEGIN_, TKWEND_)) - has = args[0].len >= sizeof "__builtin_" && !memcmp(args[0].s, "__builtin_", 10); - else goto Bad; - if (narg != 1) - error(&args[1].span, "expected `)' after '%tk'", &args[0]); - } else Bad: { - error(narg ? &args[0].span : &tk->span, "'__has_builtin' requires an identifier"); - } - tk->s = &"01"[has]; -} - - -static void -putdef1(const char *name) -{ - static const struct token tok_1 = { TKNUMLIT, .s = "1", .len = 1, .litlit = 1 }; - putmac(intern(name), &(struct macro) { - .predef = 1, - .single = &tok_1 - }); -} - -static void -putdefs1(const char *s) -{ - for (; *s; s += strlen(s) + 1) putdef1(s); -} - -static void -addpredefmacros(struct arena **tmparena) -{ - static struct token tok_stdc = {TKNUMLIT}, - tok_major = {TKNUMLIT, .s = XSTR(ANTCC_VERSION_MAJOR), - .len = sizeof XSTR(ANTCC_VERSION_MAJOR) - 1}, - tok_minor = {TKNUMLIT, .s = XSTR(ANTCC_VERSION_MINOR), - .len = sizeof XSTR(ANTCC_VERSION_MINOR) - 1}, - tok_patch = {TKNUMLIT, .s = XSTR(ANTCC_VERSION_PATCH), - .len = sizeof XSTR(ANTCC_VERSION_PATCH) - 1}; - static struct { const char *name; struct macro m; } macs[] = { - { "__FILE__", { .predef = 1, .special = 1, .handler = mac__file__ }}, - { "__LINE__", { .predef = 1, .special = 1, .handler = mac__line__ }}, - { "__DATE__", { .predef = 1, .special = 1, .handler = mac__date__ }}, - { "__TIME__", { .predef = 1, .special = 1, .handler = mac__time__ }}, - { "__COUNTER__", { .predef = 1, .special = 1, .handler = mac__counter__ }}, - { "__has_builtin", { .predef = 1, .nparam = 1, .fnlike = 1, .special = 1, .handlerfn = mac__has_builtin }}, - { "__STDC_VERSION__", { .predef = 1, .single = &tok_stdc }}, - { "__antcc_major__", { .predef = 1, .single = &tok_major }}, - { "__antcc_minor__", { .predef = 1, .single = &tok_minor }}, - { "__antcc_patch__", { .predef = 1, .single = &tok_patch }}, - { "__extension__", { .predef = 1, .single = NULL }}, - }; - static const char - cpredefs[] = - "__antcc__\0__STDC__\0__STDC_NO_ATOMICS__\0__STDC_NO_COMPLEX__\0__STDC_NO_THREADS__\0__STDC_NO_VLA__\0", - *ospredefs[] = { - [OSlinux] = "__linux\0__linux__\0linux\0unix\0__unix\0__unix__\0" - }, *archpredefs[] = { - [ISx86_64] = "__x86_64__\0__x86_64\0", - [ISaarch64] = "__aarch64__\0__aarch64\0", - }, cstdver[][8] = { - [STDC89] = "199409L", - [STDC99] = "199901L", - [STDC11] = "201112L", - [STDC23] = "202311L", - }; - - tok_stdc.s = cstdver[ccopt.cstd]; - tok_stdc.len = 7; - - for (int i = 0; i < countof(macs); ++i) - putmac(intern(macs[i].name), &macs[i].m); - putdefs1(cpredefs); - if (target.os != OSunknown) putdef1("__STDC_HOSTED__"); - putdefs1(ospredefs[target.os]); - putdefs1(archpredefs[target.arch]); - - if (ppcmdline.n) { - struct memfile *f; - struct lexer lx[1] = {0}; - lx->fileid = getpredeffile(&f, "<command line>"); - assert(!f->p); - lx->ndat = f->n = ppcmdline.n; - vpushn(&ppcmdline, "\0\0\0\0\0\0", 6); - lx->dat = f->p = ppcmdline.p; - lx->tmparena = tmparena; - lx->chrbuf0 = countof(lx->chrbuf); - lx->firstdirective = 1; - while (lex(lx, NULL) != TKEOF) ; - } -} - -enum initlexer -initlexer(struct lexer *lx, const char **err, const char *file) -{ - enum { NARENA = 1<<12 }; - static union { char m[sizeof(struct arena) + NARENA]; struct arena *_align; } amem; - static struct arena *tmparena = (void *)amem.m; - - if (!tmparena->cap) tmparena->cap = NARENA; - if (!mtoksbuf.p) vinit(&mtoksbuf, NULL, 1024); - if (!mdyntoksbuf.p) vinit(&mdyntoksbuf, NULL, 256); - if (!macroht.v) addpredefmacros(&tmparena); - - struct memfile *f; - int fileid = openfile(err, &f, file); - if (fileid < 0) - return LXERR; - internstr guard; - if (isfileseen(fileid) && isoncefile(fileid, &guard) && (!guard || findmac(guard))) { - //efmt("skipping %s .. guard %s\n", file, guard ? guard : "<none>"); - return LXFILESKIP; - } - memset(lx, 0, sizeof *lx); - lx->fileid = fileid; - markfileseen(fileid); - - lx->dat = f->p; - lx->ndat = f->n; - lx->tmparena = &tmparena; - lx->chrbuf0 = countof(lx->chrbuf); - lx->firstdirective = 1; - lx->nppcnd0 = nppcnd; - return getfilename(fileid, 0) != file ? LXFILESEEN : LXOK; -} - -/* callback to let lexer release temp memory for arena allocated token data */ -void -lexerfreetemps(struct lexer *lx) -{ - if (!lx->macstk) { - /* some of the tokens could be somewhere in the macro stack */ - freearena(lx->tmparena); - } -} - -void -lexerdump(struct lexer *lx, struct wbuf *out) -{ - struct token prev = {0}, tok; - int file = lx->fileid, line = 1, col = 1; - const char *lastfile = getfilename(file, 0); - bfmt(out, "# %d %'s\n", 1, lastfile); - while (lex(lx, &tok) != TKEOF) { - int tkline, tkcol; - const char *fname = getfilepos(&tkline, &tkcol, tok.span.ex.file, tok.span.ex.off); - if (tok.span.ex.file != file || fname != lastfile) { - file = tok.span.ex.file; - bfmt(out, "\n# %d %'s\n", tkline, fname); - col = 1; - lexerfreetemps(lx); - lastfile = fname; - } else if (line < tkline && tkline - line < 5) { - do - ioputc(out, '\n'); - while (++line != tkline); - col = 1; - } else if (line != tkline) { - bfmt(out, "\n# %d\n", tkline); - line = tkline; - col = 1; - lexerfreetemps(lx); - } else if (prev.t && (tok.space || tokpaste(lx, NULL, &prev, &tok))) { - /* preserve whitespace & paste avoidance */ - ioputc(out, ' '); - ++col; - } - if (col == 1) - for (; col < tkcol; ++col) - ioputc(out, ' '); - line = tkline; - bfmt(out, "%tk", &tok); - col += tok.span.ex.len; - prev = tok; - } - bfmt(out, "\n"); - ioflush(out); -} - -/* vim:set ts=3 sw=3 expandtab: */ diff --git a/c/lex.h b/c/lex.h deleted file mode 100644 index e70bc78..0000000 --- a/c/lex.h +++ /dev/null @@ -1,126 +0,0 @@ -#include "../common.h" -#include "../type.h" - -static inline bool -joinspan(struct span0 *dst, struct span0 snd) -{ - if (dst->file != snd.file) return 0; - if (dst->off > snd.off) return 0; - dst->len = snd.off + snd.len - dst->off; - return 1; -} - -enum toktag { /* single-character tokens' tag value is the character itself */ - TKEOF = 0xFF, - TKXXX = 0, - TKNUMLIT, - TKCHRLIT, - TKSTRLIT, - TKPPHDRH, /* <hdr> (for #include) */ - TKPPHDRQ, /* "hdr" (for #include) */ - TKPPMACARG, /* macro param, in repl list */ - TKPPMACSTR, /* stringify macro param, in repl list */ - TKEQU = '@', /* == */ - TKNEQ, /* != */ - TKLTE, /* <= */ - TKGTE, /* >= */ - TKSHR, /* >> */ - TKSHL, /* << */ - TKINC, /* ++ */ - TKDEC, /* -- */ - TKDOTS, /* ... */ - TKARROW, /* -> */ - TKPPCAT, /* ## */ - TKLOGAND, /* && */ - TKLOGIOR, /* || */ - TKSETADD, /* += */ - TKSETSUB, /* -= */ - TKSETMUL, /* *= */ - TKSETDIV, /* /= */ - TKSETREM, /* %= */ - TKSETIOR, /* |= */ - TKSETXOR, /* ^= */ - TKSETAND, /* &= */ - TKSETSHL, /* <<= */ - TKSETSHR, /* >>= */ - TKIDENT = 0x80, -#define _(kw, stdc, ...) TKW##kw, -#include "keywords.def" -#undef _ - NTOKTAG, -}; -static_assert(NTOKTAG < 256); - -struct token { - uchar t; /* toktag */ - bool litlit : 1, - blue : 1, /* preprocessor token painted blue */ - extwarn : 1; /* warn this keyword token is an extension */ - uchar wide : 2, /* for CHRLIT & STRLIT; 1 -> 16bit, 2 -> 32bit */ - wideuni : 1, /* ditto, 0 -> 'L', 1 -> 'u'/'U' (C11) */ - space : 1; /* preceded by whitespace? */ - union { - uint len; - ushort argidx; - }; - struct span span; - union { - internstr name; - const char *s; - const ushort *ws16; - const uint *ws32; - }; - /* for (multi-)character tokens s & len are unused - * for keywords, s is constant cstring, len = strlen(s) - * for idents, s is interned cstring, len = strlen(s) - * for strlit and chrlit: - * when litlit : s points to start of string within file buffer (after the ") - * len == span.sl.len - 2 (string data appears literally in source code) - * otherwise s is heap allocated buffer of len bytes - * when wide, litlit = 0 and use ws16/ws32 - * for numlit: - * when litlit : s points to start of token within file buffer (normal case) - * len == span.sl.len (number literal appears literally in source code) - * otherwise s is heap allocated buffer of len bytes - * for macro arg/stringify: - * s is like keyword/ident - * argidx is index in macro param list, - * macidx is macro id of which it is a parameter - */ -}; - -extern int nerror, nwarn; -struct lexer { - struct lexer *save; - short fileid; - const uchar *dat; - uint ndat; - uint idx, chridx; - ushort chrbuf0; - struct macrostack *macstk; - struct token peektok; - bool eof, err; - struct arena **tmparena; - bool firstdirective; - short nppcnd0; - short inclnerror, inclnwarn; - internstr inclguard; - uchar chrbuf[1<<10]; - uint chridxbuf[1<<10]; -}; - -enum initlexer { - LXOK, - LXFILESEEN, - LXFILESKIP, - LXERR, -}; - -int lex(struct lexer *, struct token *); -int lexpeek(struct lexer *, struct token *); -enum typetag parsenumlit(uvlong *, double *, const struct token *, bool ispp); -enum initlexer initlexer(struct lexer *, const char **err, const char *file); -void lexerdump(struct lexer *, struct wbuf *out); -void lexerfreetemps(struct lexer *); - -/* vim:set ts=3 sw=3 expandtab: */ |