aboutsummaryrefslogtreecommitdiffhomepage
path: root/c
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2026-03-17 13:22:00 +0100
committerlemon <lsof@mailbox.org>2026-03-17 13:22:00 +0100
commita8d6f8bf30c07edb775e56889f568ca20240bedf (patch)
treeb5a452b2675b2400f15013617291fe6061180bbf /c
parent24f14b7ad1af08d872971d72ce089a529911f657 (diff)
REFACTOR: move sources to src/
Diffstat (limited to 'c')
-rw-r--r--c/builtin.c177
-rw-r--r--c/c.c4772
-rw-r--r--c/c.h139
-rw-r--r--c/eval.c437
-rw-r--r--c/keywords.def76
-rw-r--r--c/lex.c2496
-rw-r--r--c/lex.h126
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: */
diff --git a/c/c.c b/c/c.c
deleted file mode 100644
index 63c3f7f..0000000
--- a/c/c.c
+++ /dev/null
@@ -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(&params, 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(&params, 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(&params, 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(&params);
- 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: */
diff --git a/c/c.h b/c/c.h
deleted file mode 100644
index 0214db5..0000000
--- a/c/c.h
+++ /dev/null
@@ -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(&macroht, 1<<10);
- struct macro *slot = pmap_get(&macroht, 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(&macroht, name, *mac);
- }
-}
-
-static void
-delmac(internstr name)
-{
- struct macro *slot = pmap_get(&macroht, name);
- if (!slot) return;
- freemac(slot);
- pmap_del(&macroht, 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(&macroht, 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(&params, 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(&params, tk.name);
- else if (tk.t == TKDOTS) {
- mac.variadic = 1;
- vpush(&params, intern("__VA_ARGS__"));
- } else {
- error(&tk.span, "expected parameter name or `)'");
- if (tk.t == TKEOF || tk.t == '\n') return;
- break;
- }
- }
- if (!params.n) vfree(&params);
- 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: */