diff options
| author | 2025-10-19 08:09:09 +0200 | |
|---|---|---|
| committer | 2025-10-19 08:09:09 +0200 | |
| commit | dea8fd171acb54b6d9685422d5e391fb55074008 (patch) | |
| tree | 2c149892f35c5183c9b2a1da4ab437228dc432ef /c.c | |
| parent | 3437945692f2b87883a4f066473c9deed50f25f5 (diff) | |
Organize source files into directories
Diffstat (limited to 'c.c')
| -rw-r--r-- | c.c | 4054 |
1 files changed, 0 insertions, 4054 deletions
@@ -1,4054 +0,0 @@ -#include "c.h" -#include "lex.h" -#include "ir.h" -#include "endian.h" -#include "obj.h" - -/** C compiler state **/ -struct comp { - struct lexer lx; - struct env *env; - struct arena *fnarena, *exarena; - struct span fnblkspan; - uint loopdepth, switchdepth; - struct block *breakto, *loopcont; - struct switchstmt *switchstmt; - struct label *labels; -}; - -/** Parsing helper functions **/ -#define peek(Cm,Tk) lexpeek(&(Cm)->lx,Tk) -static int -lexc(struct comp *cm, struct token *tk) -{ - struct token tk2; - int t = lex(&cm->lx, tk); - if (t == TKSTRLIT && peek(cm, &tk2) == TKSTRLIT && tk2.wide == tk->wide) { - /* 5.1.1.2 Translation phase 6: concatenate adjacent string literal tokens */ - static char buf[200]; - vec_of(char) rest = VINIT(buf, sizeof buf); - do { - lex(&cm->lx, NULL); - 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); - } - 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; -} -static struct token -expectdie(struct comp *cm, enum toktag t, const char *s) -{ - struct token tk; - if (!match(cm, &tk, t)) - fatal(&tk.span, "expected %'tt%s%s", t, s?" ":"",s ? s : ""); - return tk; -} - -/******************************************/ -/* 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; - enum storageclass scls; - enum qualifier qual; - uint align; - bool 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; - const char **pnames; /* param names for function definition */ - struct span *pspans; /* param spans ditto */ -}; -static struct decl pdecl(struct declstate *st, struct comp *cm); - -static struct decl *finddecl(struct comp *cm, const char *name); - -/* next token starts a decl? */ -static bool -isdecltok(struct comp *cm) -{ - struct decl *decl; - struct token tk; - switch (peek(cm, &tk)) { - case TKWsigned: case TKWunsigned: case TKWshort: case TKWlong: - case TKWint: case TKWchar: case TKW_Bool: case TKWauto: - case TKWstruct: case TKWunion: case TKWenum: case TKWtypedef: - case TKWextern: case TKWstatic: case TKWinline: case TKW_Noreturn: - case TKWconst: case TKWvolatile: case TKWvoid: case TKWfloat: - case TKWdouble: case TKWregister: case TKW_Static_assert: - case TKW__typeof__: case TKWtypeof: case TKWtypeof_unqual: - return 1; - case TKIDENT: - return (decl = finddecl(cm, tk.s)) && decl->scls == SCTYPEDEF; - } - return 0; -} - - -/**********************************/ -/* Environment (scope) management */ -/**********************************/ - -static struct decl envdeclsbuf[1<<10]; -static vec_of(struct decl) envdecls = VINIT(envdeclsbuf, arraylength(envdeclsbuf)); -struct tagged { /* a tagged type declaration */ - union type ty; - struct span span; -}; -static struct tagged envtaggedbuf[1<<10]; -static vec_of(struct tagged) envtagged = VINIT(envtaggedbuf, arraylength(envtaggedbuf)); -struct env { - struct env *up; - /* list of decls is implicitly envdecls[decl..ndecl] */ - ushort decl, ndecl; - /* ditto for envtagged[] */ - ushort tagged, ntagged; -}; -static struct env toplevel; - -static void -envdown(struct comp *cm, struct env *e) -{ - assert(cm->env->decl + cm->env->ndecl == envdecls.n); - assert(cm->env->tagged + cm->env->ntagged == envtagged.n); - e->decl = envdecls.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 == envdecls.n); - envdecls.n -= env->ndecl; - envtagged.n -= env->ntagged; - assert(env->up); - cm->env = env->up; -} - -static struct decl * -envadddecl(struct env *env, const struct decl *d) -{ - assert(env->decl + env->ndecl == envdecls.n); - vpush(&envdecls, *d); - ++env->ndecl; - return &envdecls.p[envdecls.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 = &envdecls.p[env->decl + env->ndecl - 1]; - else if (*d == &envdecls.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); - ++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) -{ - if (old->scls != new->scls) return 0; - switch (old->scls) { - case SCSTATIC: - if (old->ty.t != TYFUNC) - break; - /*fallthru*/ - case SCEXTERN: - if (old->ty.t == TYARRAY && new->ty.t == TYARRAY - && typechild(old->ty).bits == typechild(new->ty).bits - && isincomplete(old->ty)) - { - return 1; - } - /*fallthru*/ - case SCTYPEDEF: - return old->ty.bits == new->ty.bits; - } - return 0; -} - -static struct decl * -putdecl(struct comp *cm, const struct decl *decl) -{ - struct decl *l; - for (l = NULL; enviterdecl(&l, cm->env);) { - if (decl->name == l->name) { - if (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; - } - } - } - l = envadddecl(cm->env, decl); - return l; -} - -static struct decl * -finddecl(struct comp *cm, const char *name) -{ - struct env *e; - struct decl *l; - assert(name); - for (e = cm->env; e; e = e->up) { - for (l = NULL; enviterdecl(&l, e);) { - if (name == l->name) - return l; - } - } - return NULL; -} - -static union type -gettagged(struct comp *cm, struct span *span, enum typetag tt, const char *name, bool dodef) -{ - struct env *e; - struct tagged *l; - struct typedata td = {0}; - assert(name); - for (e = cm->env; e; e = e->up) { - for (l = NULL; envitertagged(&l, e);) { - if (name == ttypenames[typedata[l->ty.dat].id]) { - if (dodef && e != cm->env) - goto Break2; - *span = l->span; - return l->ty; - } - } - } -Break2: - if (tt == TYENUM) - return mktype(0); - 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, const char *name, union type ty) -{ - struct tagged *l; - struct typedata td = {0}; - assert(name); - for (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; -} - -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); - return t; -} - -static bool -assigncheck(union type t, const struct expr *src) -{ - if (assigncompat(t, typedecay(src->ty))) return 1; - 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(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 void /* 6.5.3.4 The sizeof operator */ -sizeofcheck(const struct span *span, union type ty) -{ - if (isincomplete(ty)) - error(span, "cannot apply sizeof to incomplete type '%ty'", ty); - else if (ty.t == TYFUNC) - error(span, "cannot apply sizeof to function type '%ty'", ty); -} - -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 }}; - if (ex->t == ECAST && ex->ty.bits == voidptr.bits) - ex = ex->sub; - return 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); - return t1.dat == t2.dat || typechild(t1).t == TYVOID || typechild(t2).t == TYVOID; - } - if (isptrcvt(t1) && isnullpo(b)) return 1; - return 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 && 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); - } - } - if (t1.t == TYPTR && isnullpo(b)) return t1; - if (isnullpo(a) && t2.t == TYPTR) return t2; - 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)) - goto Error; - ty.t = intpromote(ty.t); - assert(ty.t); - break; - case BCEQL: - if (!equalitycheck(lhs, rhs)) - goto Error; - ty = mktype(TYINT); - break; - case BCCMP: - if (!relationalcheck(lhs, rhs)) - goto Error; - ty = mktype(TYINT); - break; - case BCLOG: /* 6.5.13-14 Logical AND/OR operator */ - if (!isscalar(typedecay(ty)) || !isscalar(typedecay(rhs->ty))) - goto Error; - ty = mktype(TYINT); - 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); - -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 = &typedata[ty.dat]; - struct expr argbuf[10]; - vec_of(struct expr) args = VINIT(argbuf, arraylength(argbuf)); - bool spanok = joinspan(&span.ex, span_->ex); - bool printsig = 0; - - if (callee->t == ESYM && !callee->ty.t) { /* implicit function decl.. */ - const char *name = (void *)callee->sym; - struct decl decl = { - ty = mkfntype(mktype(TYINT), 0, NULL, NULL, /* kandr */ 1, 0), - .scls = SCEXTERN, .span = callee->span, .name = name - }; - warn(&callee->span, "call to undeclared function '%s'", name); - ((struct expr *)callee)->ty = decl.ty; - ((struct expr *)callee)->sym = putdecl(cm, &decl); - td = &typedata[ty.dat]; - } - - if (ty.t == TYPTR) /* auto-deref when calling a function pointer */ - ty = typechild(ty); - if (ty.t != TYFUNC) error(&callee->span, "calling a value of type '%ty'", callee->ty); - if (!match(cm, &tk, ')')) for (;;) { - arg = expr(cm); - spanok = spanok && joinspan(&span.ex, callee->span.ex); - if (ty.t == TYFUNC && args.n == td->nmemb && !td->variadic && !td->kandr) { - error(&arg.span, "too many args to function taking %d params", td->nmemb); - printsig = 1; - } - if (ty.t == TYFUNC && 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->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); - return ex; -} - -static inline int -tkprec(int tt) -{ - return ((uint)tt < arraylength(bintab)) ? bintab[tt].prec : 0; -} - -static struct expr initializer(struct comp *cm, union type *ty, enum evalmode ev, - bool globl, enum qualifier qual, const char *name); - -/* parse an expression with the given operator precedence */ -/* param ident is a kludge to support block labels without backtracking or extra lookahead - * see stmt() */ -static struct expr -exprparse(struct comp *cm, int prec, const struct token *ident, bool fromstmt) -{ - struct token tk, tk2; - struct span span; - struct expr ex, rhs, tmp; - struct decl *decl; - union type ty; - int opprec; - enum exprkind ek; - 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 '+': 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 >= arraylength(unops)) { - ex = exprparse(cm, 999, NULL, 0); - break; - } - goto Unary; - - /* base exprs */ - case TKNUMLIT: - case TKCHRLIT: - ex = mkexpr(ENUMLIT, tk.span, mktype(0), ); - if (!(ty.t = parsenumlit(&ex.u, &ex.f, &tk, 0))) - error(&tk.span, "bad number literal %'tk", &tk); - ex.ty.t = ty.t ? ty.t : TYINT; - break; - case TKSTRLIT: - ty = mktype(((const char []){TYCHAR, TYSHORT, TYINT})[tk.wide]); - ex = mkexpr(ESTRLIT, tk.span, mkarrtype(ty, 0, tk.len+1), .s = { (void *)tk.s, tk.len }); - break; - case TKIDENT: - Ident: - decl = finddecl(cm, tk.s); - if (!decl) { - if (peek(cm, NULL) == '(') { /* implicit function decl? */ - ex = mkexpr(ESYM, tk.span, mktype(0), .sym = (void *)tk.s); - } else { - error(&tk.span, "undeclared identifier %'tk", &tk); - ex = mkexpr(ESYM, tk.span, mktype(TYINT), .sym = NULL); - } - } else if (decl->scls == SCTYPEDEF) { - error(&tk.span, "unexpected typename %'tk (expected expression)", &tk); - ex = mkexpr(ESYM, tk.span, decl->ty, .sym = NULL); - } else if (decl->isenum) { - ex = mkexpr(ENUMLIT, tk.span, decl->ty, .i = decl->value); - } else { - ex = mkexpr(ESYM, tk.span, decl->ty, .qual = decl->qual, .sym = decl); - } - break; - - /* might be unary op or primary expr */ - case '(': - if (!isdecltok(cm)) { /* (expr) */ - ex = commaexpr(cm); - expect(cm, ')', NULL); - break; - } else { /* (type) expr */ - struct declstate st = { DCASTEXPR }; - struct decl decl = pdecl(&st, cm); - expect(cm, ')', NULL); - assert(decl.ty.t); - if (peek(cm, NULL) == '{') { - if (ccopt.cstd < STDC99) - warn(&tk.span, "compound literals are a c99 feature"); - ex = initializer(cm, &decl.ty, (decl.scls & SCSTATIC) ? EVSTATICINI : EVFOLD, - /* globl */ 0, decl.qual, NULL); - break; - } - unops[nunop].span = tk.span; - unops[nunop].ty = decl.ty; - if (++nunop >= arraylength(unops)) { - ex = exprparse(cm, 999, NULL, 0); - break; - } - goto Unary; - } - case TKWsizeof: - span = tk.span; - if (!match(cm, NULL, '(')) /* sizeof expr */ - goto Unops; - else if (isdecltok(cm)) { /* sizeof (type) */ - struct declstate st = { DCASTEXPR }; - ty = pdecl(&st, cm).ty; - } else { /* sizeof (expr) */ - ty = commaexpr(cm).ty; - } - peek(cm, &tk); - if (expect(cm, ')', NULL)) - joinspan(&span.ex, tk.span.ex); - sizeofcheck(&span, ty); - ex = mkexpr(ENUMLIT, span, mktype(targ_sizetype), .u = typesize(ty)); - break; - default: - fatal(&tk.span, "expected %s (near %'tk)", fromstmt ? "statement" : "expression", &tk); - } - - /* postfix operators */ -Postfix: - switch (peek(cm, &tk)) { - default: break; - 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)); - goto Postfix; - 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, &tk2), !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; - goto Postfix; - case '(': /* call(args) */ - lex(cm, &tk); - span = ex.span; - ex = callexpr(cm, &span, &ex); - goto Postfix; - 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.s)) - error(&span, "'%ty' has no such field: '%s'", ex.ty, tk2.s); - 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 }); - } - } - goto Postfix; - } - - /* unary operators (process) */ - while (nunop-- > 0) { - 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 (isincomplete(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: - sizeofcheck(&span, ex.ty); - ex = mkexpr(ENUMLIT, span, mktype(targ_sizetype), .u = typesize(ex.ty)); - 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); - ex = mkexpr(ECAST, span, ty, .sub = exprdup(cm, &ex)); - } - } - - /* binary operators */ - while ((opprec = tkprec(peek(cm, &tk))) >= prec) { - lex(cm, &tk); - ek = bintab[tk.t].t; - 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 { - /* ex ? tmp : rhs */ - struct expr *sub; - span.sl = tk.span.sl; - span.ex = ex.span.ex; - if (!isscalar(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 = expr(cm); - 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 -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) -{ - if (ty.t == TYARRAY) - return typearrlen(ty) ? typearrlen(ty) : -1u; - if (isagg(ty)) - return typedata[ty.dat].nmemb; - 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; /* size is not known until parsing done (implicit array size) */ - 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; - const char *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 union ref expraddr(struct function *, const struct expr *); -static bool -globsym(union ref *psym, const struct expr *ex) -{ - if (ex->t == EINIT || ex->t == ESTRLIT || (ex->t == ESYM && (ex->sym->scls & (SCSTATIC | SCEXTERN)))) { - *psym = expraddr(NULL, ex); - return 1; - } - return 0; - -} - -static void -expr2reloc(union ref *psym, vlong *paddend, const struct expr *ex) -{ - if (ex->t == EADDROF && globsym(psym, ex)) { - *paddend = 0; - } else if (ex->t == EADDROF && (ex->sub->t == EGETF && globsym(psym, ex->sub->sub))) { - *paddend = ex->sub->fld.off; - } else if (globsym(psym, ex) && in_range(ex->ty.t, TYARRAY, TYFUNC)) { - *paddend = 0; - } else if (ex->t == ESUB && globsym(psym, &ex->sub[0]) && isint(ex->sub[1].ty) && ex->sub[1].t == ENUMLIT) { - *paddend = ex->sub[1].i * typesize(ex->sub[0].ty); - } else if (ex->t == EADD) { - for (int swp = 0; swp < 2; ++swp) { - struct expr *a = &ex->sub[swp], *b = &ex->sub[swp ^ 1]; - if (globsym(psym, a) && isint(b->ty) && b->t == ENUMLIT) { - *paddend = b->i * typesize(a->ty); - return; - } - } - goto Fail; - } else Fail: assert(0 && "non static reloc"); -} - -static void -iniwrite(struct comp *cm, struct initparser *ip, uint off, union type ty, struct expr *ex) -{ - uchar *p; - uint bitsiz, bitoff; - if (ex->ty.t == TYSTRUCT) { - 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); - assert(!bitsiz); - iniwrite(cm, ip, off + suboff, sub, &mkexpr(EGETF, ex->span, sub, .sub = ex)); - } - } else if (ip->ev == EVSTATICINI) { - uint siz = typesize(ty); - 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; - eval(e, EVSTATICINI); - assert(e->t == ENUMLIT); - } - // efmt("#%u' wr %lx at %u\n", ip->dyn?0:ip->off, e->u, off); - // ioflush(&bstderr); - 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 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 { - union ref sym; - vlong addend; - //efmt("<<> %ty <- %ty\n", ty, ex->ty); - expr2reloc(&sym, &addend, ex); - assert(sym.t == RXCON); - if (!ip->dyn) { - objreloc(xcon2sym(sym.i), 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 = xcon2sym(sym.i); - rel->off = off; - rel->addend = addend; - ip->drel = rel; - } - } - } else { - struct init *init = ip->init; - struct initval val = { - .off = off, - .ex = *ex - }, *new = alloccopy(&cm->exarena, &val, sizeof val, 0); - *init->tail = new; - init->tail = &new->next; - for (uint i = off, end = i + typesize(ex->ty); i < end; ++i) { - if (BSSIZE(end) > arraylength(init->zero)) break; - bsclr(init->zero, i); - } - } -} - -static bool -iniwriterec(struct comp *cm, struct initparser *ip, uint off, struct expr *ex) -{ - 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; - } - return 1; -} - -static struct initcur * -iniadvance(struct initparser *ip, struct initcur *c, const struct span *span) -{ - if (c - ip->buf >= arraylength(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) -{ - 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); - assert(!bitsiz); - - 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, 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, 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 == arraylength(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, 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, const char *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) { - do { - idx = aggdesignator(ip, ip->sub->ty, tk.s, &span); - if (idx >= 0) break; - if (ip->sub != ip->cur && !ttypenames[typedata[ip->sub->ty.dat].id]) { - /* if in anonymous aggregate, go up and look again */ - --ip->sub; - continue; - } - } while (0); - ip->sub->idx = idx; - if (idx < 0) - error(&span, "%ty has no such field: '%s'", ip->cur->ty, tk.s); - 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, const char *name) -{ - 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 && !typearrlen(*ty)) { - ip->dyn = 1; - } else { - ip->sec = qual & QCONST ? Srodata : Sdata; - ip->off = objnewdat(name, 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, *ty, &ex); - if (ip->dyn) - goto Dynfix; - } - 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, *ty, &ex); - } - return ex; - } - - assert(arraylength(res.zero) == 1); - 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 (ip->dyn) { - enum section sec; - uint off, siz, align; - uchar *p; - 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: - sec = qual & QCONST ? Srodata : Sdata; - off = objnewdat(name, 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); - vpush(&dattab, ((struct irdat) { - align, globl, sec, siz, off, name - })); - vfree(&ip->ddat); - 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); - } - } - dumpini(ip); - - if (ev == EVSTATICINI) { - return (struct expr){0}; - } else { - uint siz; - if (isincomplete(*ty)) { - if (!ip->arrlen) - error(&span, "initializer creates a zero-sized array"); - *ty = mkarrtype(typechild(*ty), ty->flag & TFCHLDQUAL, ip->arrlen > 0 ? ip->arrlen : 1); - } - - assert(arraylength(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)); - } -} - -/*****************/ -/* Decls Parsing */ -/*****************/ - -static union type -buildagg(struct comp *cm, enum typetag tt, const char *name, int id) -{ - struct token tk; - union type t; - struct span flexspan; - struct namedfield fbuf[32]; - vec_of(struct namedfield) fld = VINIT(fbuf, arraylength(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 = 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 : "<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 = { "", { 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, const char *name, const struct span *span) -{ - 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.s; - 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; - 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)); - - 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; - const char *tag = NULL; - - peek(cm, &tk); - if (match(cm, &tk, TKIDENT)) - tag = tk.s; - 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) == ';'); - if (tt == TYENUM && !t.t) { - error(&tk.span, "cannot forward-declare enum"); - return mktype(TYINT); - } - } else { - if (tt != TYENUM) { - 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, mktype(0)); - note(&span, "previous definition:"); - } - } - t = buildagg(cm, tt, tag, tag ? typedata[t.dat].id : -1); - } else { - t = buildenum(cm, tag, &span); - if (tag) deftagged(cm, &span, TYENUM, tag, t); - } - } - - 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 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 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, - } arith = 0; - struct span span = {0}; - - for (;;) { - peek(cm, &tk); - switch (tk.t) { - case TKWconst: - st->qual |= QCONST; - break; - case TKWrestrict: - /* unimplemented */ - /*st->qual |= QRESTRICT;*/ - break; - case TKWvolatile: - st->qual |= QVOLATILE; - break; - case TKW_Noreturn: - st->qual |= QNORETURN; - break; - case TKWinline: - st->qual |= QINLINE; - break; - case TKWvoid: - st->base = mktype(TYVOID); - break; - case TKWsigned: - arith |= KSIGNED; - break; - case TKWunsigned: - arith |= KUNSIGNED; - break; - case TKW_Bool: - case TKWbool: - if (arith & KBOOL) goto Dup; - arith |= KBOOL; - break; - case TKWchar: - if (arith & KCHAR) { - Dup: - 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 Dup; - arith |= KINT; - break; - case TKWfloat: - if (arith & KFLOAT) goto Dup; - arith |= KFLOAT; - break; - case TKWdouble: - if (arith & KDOUBLE) goto Dup; - arith |= KDOUBLE; - break; - case TKWenum: - case TKWstruct: - case TKWunion: - lex(cm, &tk); - st->base = tagtype(cm, tk.t); - st->tagdecl = 1; - if (!span.ex.len) span.ex = tk.span.ex; - joinspan(&span.ex, tk.span.ex); - goto End; - case TKW__typeof__: case TKWtypeof: - lex(cm, &tk); - st->base = ptypeof(cm); - if (!span.ex.len) span.ex = tk.span.ex; - joinspan(&span.ex, tk.span.ex); - goto End; - case TKIDENT: - if (!st->base.t && !arith && (decl = finddecl(cm, tk.s)) - && decl->scls == SCTYPEDEF) { - st->base = decl->ty; - break; - } - /* fallthru */ - default: - if (!span.ex.len) span.ex = tk.span.ex; - goto End; - case TKW_BitInt: case TKW_Complex: - case TKW_Decimal128: case TKW_Decimal32: - case TKW_Decimal64: case TKW_Imaginary: - error(&tk.span, "%'tk is unsupported", &tk); - arith = arith ? arith : KINT; - } - if (!span.ex.len) span.ex = tk.span.ex; - joinspan(&span.ex, tk.span.ex); - lex(cm, &tk); - if (st->base.t) break; - } -End: - if (st->base.t && arith) { - /* combining arith type specifiers and other types */ - Bad: - error(&span, "invalid declaration specifier"); - 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 - goto Bad; - st->base = mktype(t ? t : TYINT); - } else if (!st->base.t && ccopt.cstd < STDC23) { - warn(&span, "type implicitly declared as int"); - st->base = mktype(TYINT); - } else if (!st->base.t) - fatal(&span, "expected declaration type specifier"); -} - -/* circular doubly linked list used to parse declarators */ -static struct decllist { - struct decllist *prev, *next; - uchar t; /* TYPTR, TYARRAY or TYFUNC */ - union { - uchar qual; /* TYPTR */ - uint len; /* TYARRAY */ - struct { /* TYFUNC */ - union type *param; - const char **pnames; - struct span *pspans; - uchar *pqual; - short npar; - bool kandr : 1, variadic : 1; - }; - }; - struct span span; -} decltmp[64], *declfreelist; -static union type declparamtmp[16]; -static const char *declpnamestmp[16]; -static struct span declpspanstmp[16]; -static uchar declpqualtmp[tdqualsiz(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 -sclass(struct comp *cm, struct span *span) -{ - struct token tk; - int sc = 0, first = 1; - for (;; lex(cm, &tk)) { - switch (peek(cm, &tk)) { - case TKWtypedef: sc |= SCTYPEDEF; break; - case TKWextern: sc |= SCEXTERN; break; - case TKWstatic: sc |= SCSTATIC; break; - case TKWauto: sc |= SCAUTO; break; - case TKWregister: sc |= SCREGISTER; break; - case TKWthread_local: - case TKW_Thread_local: - sc |= SCTHREADLOCAL; break; - default: return sc; - } - if (first) *span = tk.span; - else joinspan(&span->ex, tk.span.ex); - first = 0; - } -} - -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, const char **name, struct span *span) { - 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); - } - 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.variadic = 0; - node.kandr = 1; - node.npar = 0; - declinsert(ptr->prev, &node); - break; - } else { - decltypes(cm, list, name, span); - expect(cm, ')', NULL); - } - break; - case TKIDENT: - if (!name) - error(&tk.span, "unexpected identifier in type name"); - else { - *name = tk.s; - *span = tk.span; - } - lex(cm, &tk); - break; - default: - *span = tk.span; - if (name) - *name = NULL; - } - for (;;) { - if (match(cm, &tk, '[')) { - node.span = tk.span; - uint n = 0; - if (!match(cm, &tk, ']')) { - struct expr ex = expr(cm); - if (!eval(&ex, EVINTCONST)) { - error(&ex.span, "array length is not an integer constant"); - } else if (typesize(ex.ty) < 8 && 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) { - error(&ex.span, "array cannot have zero length"); - } else { - n = ex.u; - } - peek(cm, &tk); - joinspan(&node.span.ex, tk.span.ex); - expect(cm, ']', NULL); - } - node.t = TYARRAY; - node.len = n; - declinsert(ptr->prev, &node); - } else if (match(cm, &tk, '(')) Func: { - static int depth = 0; - vec_of(union type) params = {0}; - vec_of(uchar) qual = {0}; - vec_of(const char *) names = {0}; - vec_of(struct span) spans = {0}; - bool anyqual = 0; - - if (depth++ == 0) { - vinit(¶ms, declparamtmp, arraylength(declparamtmp)); - vinit(&qual, declpqualtmp, arraylength(declpqualtmp)); - vinit(&names, declpnamestmp, arraylength(declpnamestmp)); - vinit(&spans, declpspanstmp, arraylength(declpspanstmp)); - } - node.span = tk.span; - node.kandr = 0; - node.variadic = 0; - - while (!match(cm, &tk, ')')) { - struct declstate st = { DFUNCPARAM }; - struct decl decl; - if (match(cm, &tk, TKDOTS)) { - node.variadic = 1; - expect(cm, ')', NULL); - break; - } - decl = pdecl(&st, cm); - decl.ty = typedecay(decl.ty); - vpush(¶ms, decl.ty); - vpush(&names, decl.name); - vpush(&spans, decl.span); - if (decl.qual) { - anyqual = 1; - while (qual.n < tdqualsiz(params.n)) vpush(&qual, 0); - tdsetqual(qual.p, params.n-1, decl.qual); - } - if (isincomplete(decl.ty)) { - if (params.n > 1 || decl.ty.t != TYVOID || decl.qual || decl.name) { - error(&decl.span, - "function parameter #%d has incomplete type (%tq)", - params.n, decl.ty, tdgetqual(qual.p, params.n-1)); - } - } - joinspan(&node.span.ex, tk.span.ex); - if (!match(cm, &tk, ',')) { - expect(cm, ')', NULL); - break; - } - } - --depth; - node.kandr = params.n == 0 && ccopt.cstd < STDC23; - if (params.n == 1 && params.p[0].t == TYVOID && !qual.n && !names.p[0]) { /* (void) */ - vfree(¶ms); - vfree(&names); - vfree(&spans); - } else if (params.n && params.p[0].t == TYVOID && !qual.n && !names.p[0]) { - error(&node.span, "function parameter #1 has incomplete type (%tq)", - params.p[0], tdgetqual(qual.p, 0)); - } - node.t = TYFUNC; - node.param = params.n ? params.p : NULL; - node.pqual = anyqual ? 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); - } else break; - } -} - -static struct decl -declarator(struct declstate *st, struct comp *cm) { - struct decl decl = { st->base, st->scls, st->qual, st->align }; - struct decllist list = { &list, &list }, *l; - static bool inidecltmp = 0; - if (!inidecltmp) { - inidecltmp = 1; - for (int i = 0; i < arraylength(decltmp); ++i) { - decltmp[i].next = declfreelist; - declfreelist = &decltmp[i]; - } - } - - decltypes(cm, &list, st->kind == DCASTEXPR ? NULL : &decl.name, &decl.span); - 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); - decl.ty = mkarrtype(decl.ty, decl.qual, l->len); - 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", decl.ty); - else if (decl.ty.t != TYVOID && isincomplete(decl.ty)) - error(&decl.span, "function cannot return incomplete type '%ty'", decl.ty); - if (l->kandr && ccopt.cstd > STDC89) - warn(&l->span, "function declaration without a prototype is deprecated"); - decl.ty = mkfntype(decl.ty, l->npar, l->param, l->pqual, l->kandr, l->variadic); - if (l->param != declparamtmp) free(l->param); - if (l->pqual != declpqualtmp) free(l->pqual); - 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); - } - if (l->pnames != declpnamestmp) free(l->pnames); - if (l->pspans != declpspanstmp) free(l->pspans); - decl.qual = 0; - break; - } - - l->next = declfreelist; - declfreelist = l; - } - - 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 iniallowed = st->kind != DFIELD && st->kind != DFUNCPARAM && st->kind != DCASTEXPR; - bool staticassertok = iniallowed; - bool first = 0; - - assert(!st->funcdef); - - if (st->varini || st->bitf) { - memset(&decl, 0, sizeof decl); - goto AfterIniBitf; - } - - if (!st->base.t) { - if (staticassertok && (match(cm, &tk, TKW_Static_assert) || match(cm, &tk, TKWstatic_assert))) { - pstaticassert(cm, &tk.span); - return decl = (struct decl){0}; - } - first = 1; - st->scls = sclass(cm, &tk.span); - if (popcnt(st->scls) > 1) - error(&tk.span, "invalid combination of storage class specifiers"); - else { - int allowed; - switch (st->kind) { - case DTOPLEVEL: allowed = SCTYPEDEF | SCEXTERN | SCSTATIC | SCTHREADLOCAL; break; - case DCASTEXPR: allowed = 0; break; - case DFIELD: allowed = 0; break; - case DFUNCPARAM: allowed = 0; break; - case DFUNCVAR: - allowed = SCTYPEDEF | SCREGISTER | SCAUTO | SCEXTERN | SCSTATIC | SCTHREADLOCAL; - break; - default: assert(0); - } - if ((st->scls & allowed) != st->scls) - error(&tk.span, "this storage class is not allowed in this context"); - st->scls &= allowed; - } - declspec(st, cm); - } - if (st->scls == SCTYPEDEF) iniallowed = 0; - - if (first && st->tagdecl && match(cm, &tk, ';')) { - decl = (struct decl) { st->base, st->scls, st->qual, st->align, 0, tk.span }; - return decl; - } else if (st->kind == DFIELD && match(cm, &tk, ':')) { - decl = (struct decl) { st->base, st->scls, st->qual, st->align, 0, tk.span }; - st->bitf = 1; - return decl; - } - decl = declarator(st, cm); - - if (iniallowed && 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 union ref expraddr(struct function *, const struct expr *); -static union ref compileexpr(struct function *, const struct expr *, bool discard); -static inline union ref -exprvalue(struct function *fn, const struct expr *ex) -{ - 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 const char * -mkhiddensym(const char *fnname, const char *name, int id) -{ - char buf[200]; - struct wbuf wbuf = MEMBUF(buf, sizeof buf); - assert(id > 0); - bfmt(&wbuf, "%s.%s.%d", fnname, 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 -expraddr(struct function *fn, const struct expr *ex) -{ - struct decl *decl; - union ref r; - struct instr ins = {0}; - - switch (ex->t) { - case ESYM: - decl = ex->sym; - assert(decl != NULL); - switch (decl->scls) { - case SCAUTO: case SCREGISTER: - return mkref(RTMP, decl->id); - case SCEXTERN: case SCNONE: - return mksymref(decl->name); - case SCSTATIC: - if (!decl->id) - return mksymref(decl->name); - else - return mksymref(mkhiddensym(fn->name, decl->name, decl->id)); - default: - assert(0); - } - break; - case ESTRLIT: - /* XXX endian for wide strs */ - return mkdatref(NULL, typesize(ex->ty), typealign(ex->ty), ex->s.p, ex->s.n * typesize(typechild(ex->ty)), /*deref*/0); - case EDEREF: - return exprvalue(fn, ex->sub); - case EGETF: - r = expraddr(fn, ex->sub); - assert(ex->fld.bitsiz == 0); - if (ex->fld.off == 0) return r; - ins.cls = KPTR; - ins.op = Oadd; - ins.l = r; - ins.r = mkintcon(KI4, ex->fld.off); - return addinstr(fn, ins); - 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 ESEQ: - expreffects(fn, &ex->sub[0]); - return expraddr(fn, &ex->sub[1]); - case ECALL: - assert(isagg(ex->ty)); - return compilecall(fn, ex); - 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 { - } - default: - assert(!"lvalue?>"); - } - -} - -static union ref -genload(struct function *fn, union type t, union ref ref) -{ - struct instr ins = {0}; - - assert(isscalar(t)); - ins.cls = type2cls[scalartypet(t)]; - assert(ins.cls); - switch (typesize(t)) { - case 1: ins.op = issigned(t) ? Oloads1 : Oloadu1; break; - case 2: ins.op = issigned(t) ? Oloads2 : Oloadu2; break; - case 4: ins.op = isflt(t) ? Oloadf4 : issigned(t) ? Oloads4 : Oloadu4; break; - case 8: ins.op = isflt(t) ? Oloadf8 : Oloadi8; break; - default: assert(0); - } - ins.l = ref; - 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 = Ostore1; break; - case 2: ins.op = Ostore2; break; - case 4: ins.op = Ostore4; break; - case 8: ins.op = Ostore8; break; - default: assert(0); - } - ins.l = ptr; - ins.r = val; - return addinstr(fn, ins); -} - -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); - if (BSSIZE(siz) <= arraylength(ini->zero) && bscount(ini->zero, arraylength(ini->zero)) < 32) { - /* write individual zeros at non initialized gaps */ - for (uint i = 0; bsiter(&i, ini->zero, arraylength(ini->zero)) && i < siz; ++i) { - /* TODO coalesce into multibyte zero writes */ - adr = i == 0 ? dst : addinstr(fn, mkinstr(Oadd, KPTR, dst, mkref(RICON, i))); - genstore(fn, mktype(TYCHAR), adr, ZEROREF); - } - } else { - /* 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(KI4), ZEROREF)); - addinstr(fn, mkarginstr(cls2type(type2cls[targ_sizetype]), mkintcon(type2cls[targ_sizetype], siz))); - call.l = mksymref("memset"); - 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 = off == 0 ? dst : addinstr(fn, mkinstr(Oadd, KPTR, dst, mkref(RICON, off))); - genstore(fn, ex->ty, adr, exprvalue(fn, ex)); - } - } else if (src->t == ESTRLIT) { - adr = dst; - for (uint i = 0; i < src->s.n; ++i) { - genstore(fn, mktype(TYCHAR), adr, mkref(RICON, src->s.p[i])); - adr = addinstr(fn, mkinstr(Oadd, KPTR, dst, mkref(RICON, i+1))); - } - genstore(fn, mktype(TYCHAR), adr, ZEROREF); /* null term */ - } else assert(0); -} - -static union ref -cvt(struct function *fn, union type to, union type from, union ref ref) -{ - enum irclass kto = type2cls[scalartypet(to)], kfrom = type2cls[scalartypet(from)]; - struct instr ins = {0}; - assert(kto && kfrom); - if (kto == kfrom && to.t != TYBOOL) return ref; - if (ref.t == RICON && kto < KF4) return ref; - - ins.cls = kto; - ins.l = ref; - if (kisflt(kto) || kisflt(kfrom)) { - if (ref.t == RICON) { - assert(kisflt(kto) && kisint(kfrom)); - return mkfltcon(kto, kto == KF4 ? (float)ref.i : (double)ref.i); - } - if (kisflt(kto) && kfrom == KI4) ins.op = issigned(from) ? Ocvts4f : Ocvtu4f; - else if (to.t == TYBOOL && kisflt(kfrom)) ins.op = Oneq, ins.r = mkfltcon(kfrom, 0.0); - else if (kisflt(kto) && kfrom == KI8) ins.op = issigned(from) ? Ocvts8f : Ocvtu8f; - else if (kto == KF8 && kfrom == KF4) ins.op = Ocvtf4f8; - else if (kto == KF4 && kfrom == KF8) ins.op = Ocvtf8f4; - else if (kfrom == KF4) ins.op = issigned(to) ? Ocvtf4s : Ocvtf4u; - else if (kfrom == KF8) ins.op = issigned(to) ? Ocvtf8s : Ocvtf8u; - else assert(0); - } else { - if (to.t == TYBOOL) { - if (from.t == TYBOOL) return ref; - if (ref.t == RTMP) - /* these instrs already have output range of [0,1] */ - if (oiscmp(instrtab[ref.i].op)) - return ref; - ins.op = Oneq, ins.r = ZEROREF; - } - else if (kfrom == KI4 && issigned(from)) ins.op = Oexts4; - else if (kfrom == KI4) ins.op = Oextu4; - else if (kto == KI4 && isintcon(ref)) - return issigned(to) ? mkintcon(kto, (int)intconval(ref)) : mkintcon(kto, (uint)intconval(ref)); - else ins.op = Ocopy; - } - return addinstr(fn, ins); -} - -static union ref -narrow(struct function *fn, enum irclass to, union type t, union ref ref, uint bitsiz) -{ - struct instr ins = {0}; - enum typetag tt = scalartypet(t); - assert(isscalar(t)); - if (targ_primsizes[tt] < cls2siz[to]) { - ins.cls = to; - if (isfltt(tt)) { - assert(to == KF4 && tt >= TYDOUBLE); - ins.op = Ocvtf8f4; - } else { - static const enum op ext[5][2] = { - [1] = {Oextu1, Oexts1}, [2] = {Oextu2, Oexts2}, [4] = {Oextu4, Oexts4} - }; - ins.op = ext[targ_primsizes[tt]][issignedt(tt)]; - } - ins.l = ref; - ref = addinstr(fn, ins); - } - if (bitsiz) { - assert(kisint(to) && isintt(tt) && bitsiz < 8*targ_primsizes[tt]); - if (!issignedt(tt)) { - ref = addinstr(fn, mkinstr(Oand, to, .l = ref, .r = mkintcon(to, (1ull<<bitsiz)-1))); - } else { - uint sh = 8*cls2siz[to] - bitsiz; - ref = addinstr(fn, mkinstr(Oshl, to, .l = ref, .r = mkref(RICON, sh))); - ref = addinstr(fn, mkinstr(Osar, to, .l = ref, .r = 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 = cvt(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 if (ispo2(siz)) - off = addinstr(fn, - mkinstr(Oshl, cls, .l = idx, .r = mkintcon(cls, ilog2(siz)))); - else - off = addinstr(fn, - mkinstr(Omul, cls, .l = idx, .r = mkintcon(cls, siz))); - assert(in_range(op, Oadd, Osub)); - return addinstr(fn, mkinstr(op, KPTR, .l = ptr, .r = off)); -} - -union ref -genptrdiff(struct function *fn, uint siz, union ref a, union ref b) -{ - uint cls = type2cls[targ_ptrdifftype]; - assert(siz > 0); - a = addinstr(fn, mkinstr(Osub, cls, .l = a, .r = b)); - if (siz == 1) return a; - else if ((siz & (siz-1)) == 0) /* is power of 2 */ - return addinstr(fn, mkinstr(Osar, cls, a, mkintcon(cls, ilog2(siz)))); - else - return addinstr(fn, mkinstr(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; -Loop: - while (ex->t == ESEQ) { - expreffects(fn, &ex->sub[0]); - ex = &ex->sub[1]; - } - if (ex->t == ELOGAND) { - next = newblk(fn); - condjump(fn, &ex->sub[0], next, fl); - useblk(fn, next); - ex = &ex->sub[1]; - goto Loop; - } else if (ex->t == ELOGIOR) { - next = newblk(fn); - condjump(fn, &ex->sub[0], tr, next); - useblk(fn, next); - ex = &ex->sub[1]; - goto Loop; - } 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 Loop; - } 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 Loop; - } else { - putcondbranch(fn, exprvalue(fn, ex), tr, fl); - } -} - -struct condphis { - vec_of(union ref) ref; -}; - -static void -condexprrec(struct function *fn, const struct expr *ex, struct condphis *phis, - int boolcon, struct block *const next, struct block *end) -{ - struct block *tr, *fl; - while (ex->t == ESEQ) { - expreffects(fn, &ex->sub[0]); - ex = &ex->sub[1]; - } - if (ex->t == ELOGAND) { - tr = newblk(fn); - condexprrec(fn, &ex->sub[0], phis, 0, tr, end); - useblk(fn, tr); - condexprrec(fn, &ex->sub[1], phis, 0, next, end); - } else if (ex->t == ELOGIOR) { - fl = newblk(fn); - condexprrec(fn, &ex->sub[0], phis, 1, end, fl); - useblk(fn, fl); - condexprrec(fn, &ex->sub[1], phis, 1, end, next ? next : end); - } else if (ex->t == ECOND) { - tr = newblk(fn); - fl = newblk(fn); - condjump(fn, &ex->sub[0], tr, fl); - useblk(fn, tr); - condexprrec(fn, &ex->sub[1], phis, -1, end, end); - useblk(fn, fl); - condexprrec(fn, &ex->sub[2], phis, -1, end, end); - } else { - union ref r, val; - if (!phis && (!next || next == end)) { - expreffects(fn, ex); - } else { - val = r = exprvalue(fn, ex); - if (boolcon >= 0) { - if (!next || next == end) { - boolcon = -1; - val = cvt(fn, mktype(TYBOOL), ex->ty, r); - } else { - val = mkref(RICON, boolcon); - } - } - } - if (phis) - vpush(&phis->ref, val); - if (next && next != end) { - putcondbranch(fn, r, next, end); - } else { - assert(boolcon < 0); - 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 = { VINIT(refbuf, arraylength(refbuf)) }; - struct block *dst = newblk(fn); - union ref r; - enum irclass k; - condexprrec(fn, ex, discard ? NULL : &phis, -1, NULL, dst); - useblk(fn, dst); - if (discard) return NOREF; - k = type2cls[scalartypet(ex->ty)]; - assert(k); - 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, arraylength(insnsbuf)); - - 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 = cvt(fn, ty, 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); - return addinstr(fn, ins); -} - -static union ref -genbitfload(struct function *fn, const union type ty, union ref *addr, const struct exgetfld *fld) -{ - enum irclass k = type2cls[scalartypet(ty)]; - uint off = fld->off, bitsiz = fld->bitsiz, bitoff = fld->bitoff; - union ref tmp; - uvlong mask; - - assert(k); - if (off > 0) - *addr = addinstr(fn, mkinstr(Oadd, KPTR, .l = *addr, .r = mkintcon(KI4, off))); - tmp = genload(fn, ty, *addr); - if (!issigned(ty)) { - /* shift right and mask */ - if (bitoff > 0) - tmp = addinstr(fn, mkinstr(Oslr, k, .l = tmp, .r = mkref(RICON, bitoff))); - if (bitsiz < 8*typesize(ty)) { - mask = bitsiz == 64 ? -1ull : (1ull << bitsiz) - 1; - tmp = addinstr(fn, mkinstr(Oand, k, .l = tmp, .r = mkintcon(k, mask))); - } - } else { - /* shift left and shift right arithmetic to propagate sign bit */ - int sh = 8*cls2siz[k] - bitsiz - bitoff; - if (sh) - tmp = addinstr(fn, mkinstr(Oshl, k, .l = tmp, .r = mkref(RICON, sh))); - sh += bitoff; - if (sh) - tmp = addinstr(fn, mkinstr(Osar, k, .l = tmp, .r = 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) { - if (off > 0) - addr = addinstr(fn, mkinstr(Oadd, KPTR, .l = addr, .r = mkintcon(KI4, off))); - tmp = genload(fn, ty, addr); - } - mask = (bitsiz == 64 ? -1ull : (1ull << bitsiz) - 1) << bitoff; - - /* mask out bits in existing container */ - tmp = addinstr(fn, mkinstr(Oand, k, .l = tmp, .r = mkintcon(k, ~mask))); - - /* shift and mask source value */ - if (isintcon(val)) { - val = mkintcon(k, ((uvlong)intconval(val) << bitoff) & mask); - } else { - if (bitoff) - val = addinstr(fn, mkinstr(Oshl, k, .l = val, .r = mkref(RICON, bitoff))); - if (bitsiz < bittypesize) - val = addinstr(fn, mkinstr(Oand, k, .l = val, .r = mkintcon(k, mask))); - } - /* combine and write */ - if (bitsiz < bittypesize) - val = addinstr(fn, mkinstr(Oior, k, .l = tmp, .r = val)); - genstore(fn, ty, addr, val); -} - -static union ref -compileexpr(struct function *fn, const struct expr *ex, bool discard) -{ - union type ty; - union ref r, q; - uint bitsiz; - enum irclass cls = type2cls[scalartypet(ex->ty)]; - struct instr ins = {0}; - 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 */ - 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)); - case EGETF: - if (discard && !(ex->qual & QVOLATILE)) return NOREF; - if (ex->fld.bitsiz) { - /* bit-field */ - r = expraddr(fn, ex->sub); - return genbitfload(fn, ex->ty, &r, &ex->fld); - } - return genload(fn, ex->ty, expraddr(fn, ex)); - case ECAST: - if (ex->ty.t == TYVOID) { - expreffects(fn, sub); - return NOREF; - } - /* fallthru */ - case EPLUS: - r = compileexpr(fn, sub, discard); - if (discard) return NOREF; - return cvt(fn, ex->ty, sub->ty, r); - case ENEG: - ins.op = Oneg; - goto Unary; - case ECOMPL: - ins.op = Onot; - Unary: - ins.l = compileexpr(fn, sub, discard); - if (discard) return NOREF; - ins.l = cvt(fn, ex->ty, sub->ty, ins.l); - ins.cls = cls; - return addinstr(fn, ins); - case ELOGNOT: - for (; sub->t == ELOGNOT; ex = sub, sub = sub->sub) - swp ^= 1; - ins.op = Oequ + swp; - ins.l = compileexpr(fn, sub, discard); - if (discard) return NOREF; - ins.l = cvt(fn, ex->ty, sub->ty, ins.l); - ins.r = mkintcon(cls, 0); - ins.cls = cls; - return addinstr(fn, ins); - case EDEREF: - discard &= (ex->qual & QVOLATILE) == 0; - r = compileexpr(fn, sub, discard); - if (discard) return NOREF; - return genload(fn, ex->ty, r); - case EADDROF: - return expraddr(fn, sub); - case EMUL: - ins.op = isunsigned(ex->ty) ? Oumul : Omul; - goto BinArith; - case EDIV: - ins.op = isunsigned(ex->ty) ? Oudiv : Odiv; - goto BinArith; - case EREM: - ins.op = issigned(ex->ty) ? Orem : Ourem; - goto BinArith; - case EBAND: - ins.op = Oand; - goto BinArith; - case EXOR: - ins.op = Oxor; - goto BinArith; - case EBIOR: - ins.op = Oior; - goto BinArith; - case ESHL: - ins.op = Oshl; - goto BinArith; - case ESHR: - ins.op = issigned(ex->ty) ? Osar : Oslr; - goto BinArith; - case ESUB: - ins.op = Osub; - goto BinArith; - case EADD: - ins.op = Oadd; - BinArith: - ins.l = compileexpr(fn, &sub[0], discard); - ins.r = compileexpr(fn, &sub[1], discard); - if (discard) return NOREF; - if (ins.op == Osub && isptrcvt(sub[0].ty) && isptrcvt(sub[1].ty)) { - /* ptr - ptr */ - return genptrdiff(fn, typesize(typechild(sub[0].ty)), ins.l, ins.r); - } else if ((ins.op != Oadd && ins.op != Osub) || cls != KPTR) { - /* num OP num */ - ins.l = cvt(fn, ex->ty, sub[0].ty, ins.l); - ins.r = cvt(fn, ex->ty, sub[1].ty, ins.r); - } else { - assert(isptrcvt(sub[0].ty)); - /* ptr +/- num */ - return genptroff(fn, ins.op, typesize(typechild(sub[0].ty)), ins.l, sub[1].ty, ins.r); - } - ins.cls = cls; - return addinstr(fn, ins); - case EPOSTINC: - case EPOSTDEC: - ins.op = ex->t == EPOSTINC ? Oadd : Osub; - ins.cls = cls; - r = expraddr(fn, sub); - ins.l = genload(fn, sub->ty, r); - if (ex->ty.t == TYPTR) - ins.r = mkintcon(KI4, typesize(typechild(ex->ty))); - else - ins.r = mkref(RICON, 1); - genstore(fn, sub->ty, r, addinstr(fn, ins)); - return ins.l; - case EPREINC: - case EPREDEC: - ins.op = ex->t == EPREINC ? Oadd : Osub; - ins.cls = cls; - r = expraddr(fn, sub); - ins.l = genload(fn, sub->ty, r); - if (ex->ty.t == TYPTR) - ins.r = mkintcon(KI4, typesize(typechild(ex->ty))); - else - ins.r = mkref(RICON, 1); - q = addinstr(fn, ins); - genstore(fn, sub->ty, r, q); - if (discard) return NOREF; - return narrow(fn, cls, ex->ty, q, 0); - case EEQU: - ins.op = Oequ; - goto Cmp; - case ENEQ: - ins.op = Oneq; - goto Cmp; - case ELTH: - ins.op = Olth; - goto Cmp; - case ELTE: - ins.op = Olte; - goto Cmp; - case EGTH: - ins.op = Ogth; - goto Cmp; - case EGTE: - ins.op = Ogte; - Cmp: - ty = cvtarith(sub[0].ty, sub[1].ty); - if (!ty.t) ty.t = TYPTR; - if (isunsigned(ty) && in_range(ins.op, Olth, Ogte)) - ins.op += Oulth - Olth; - ins.l = compileexpr(fn, &sub[0], discard); - ins.r = compileexpr(fn, &sub[1], discard); - if (discard) return NOREF; - ins.l = cvt(fn, ty, sub[0].ty, ins.l); - ins.r = cvt(fn, ty, sub[1].ty, ins.r); - ins.cls = type2cls[ty.t]; - return addinstr(fn, ins); - case ESET: - assert(isscalar(ex->ty)); - q = cvt(fn, sub[0].ty, sub[1].ty, exprvalue(fn, &sub[1])); - if (sub[0].t == EGETF && (bitsiz = sub[0].fld.bitsiz)) { - /* bit-field */ - r = expraddr(fn, &sub[0].sub[0]); - genbitfstore(fn, ex->ty, r, &sub[0].fld, NOREF, q); - } else { - bitsiz = 0; - r = expraddr(fn, &sub[0]); - genstore(fn, ex->ty, r, q); - } - if (discard) return NOREF; - return bitsiz ? narrow(fn, cls, sub[0].ty, q, bitsiz) : q; - case ESETMUL: - ins.op = isunsigned(ex->ty) ? Oumul : Omul; - goto Compound; - case ESETDIV: - ins.op = isunsigned(ex->ty) ? Oudiv : Odiv; - goto Compound; - case ESETREM: - ins.op = issigned(ex->ty) ? Orem : Ourem; - goto Compound; - case ESETAND: - ins.op = Oand; - goto Compound; - case ESETXOR: - ins.op = Oxor; - goto Compound; - case ESETIOR: - ins.op = Oior; - goto Compound; - case ESETSHL: - ins.op = Oshl; - goto Compound; - case ESETSHR: - ins.op = issigned(ex->ty) ? Osar : Oslr; - goto Compound; - case ESETSUB: - ins.op = Osub; - goto Compound; - case ESETADD: - ins.op = Oadd; - Compound: - ty = in_range(ex->t, ESETSHL, ESETSHR) ? mktype(intpromote(ex->ty.t)) - : cvtarith(sub[0].ty, sub[1].ty); - ins.cls = cls; - ins.r = exprvalue(fn, &sub[1]); - if (sub[0].t == EGETF && (bitsiz = sub[0].fld.bitsiz)) { - /* bit-field */ - r = expraddr(fn, &sub[0].sub[0]); - ins.l = genbitfload(fn, sub[0].ty, &r, &sub[0].fld); - q = addinstr(fn, ins); - genbitfstore(fn, sub[0].ty, r, &sub[0].fld, ins.l, q); - } else { - bitsiz = 0; - r = expraddr(fn, &sub[0]); - ins.l = genload(fn, ex->ty, r); - if ((ins.op != Oadd && ins.op != Osub) || cls != KPTR) { - ins.l = cvt(fn, ty, sub[0].ty, ins.l); - ins.r = cvt(fn, ex->ty, sub[1].ty, ins.r); - q = addinstr(fn, ins); - } else { - q = genptroff(fn, ins.op, typesize(typechild(ex->ty)), ins.l, sub[1].ty, ins.r); - } - genstore(fn, ex->ty, r, 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: - 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); - putbranch(fn, end); - useblk(fn, fl); - expreffects(fn, &sub[2]); - putbranch(fn, end); - useblk(fn, end); - return NOREF; - } - /* fallthru */ - case ELOGAND: - case ELOGIOR: - 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; - const char *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, const char *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, const char *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 = fn->curblk; - 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 switchstmt { - struct block *bdefault; - vec_of(struct swcase) cases; -}; - -static void -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 = {0}; - enum irclass k = type2cls[scalartypet(ex->ty)]; - struct swcase casebuf[8]; - vinit(&st.cases, casebuf, arraylength(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); - doemit = 1; - if (!st.bdefault) st.bdefault = end; - /* TODO: optimize instead of generating the equivalent of if == .. else if .. chain - * 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) - */ - for (int i = 0; i < st.cases.n; ++i) { - struct swcase c = st.cases.p[i]; - EMITS { - struct block *next = i < st.cases.n - 1 ? newblk(fn) : st.bdefault; - putcondbranch(fn, addinstr(fn, mkinstr(Oequ, k, .l = sel, .r = mkintcon(k, c.val))), c.blk, next); - if (next != st.bdefault) useblk(fn, next); - } - } - vfree(&st.cases); - if (fn->curblk != end) { - if (fn->curblk) EMITS putbranch(fn, end); - useblk(fn, end); - } -} - -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"); - expect(cm, ':', NULL); - begin = newblk(fn); - EMITS putbranch(fn, begin); - useblk(fn, begin); - if (cm->switchstmt) - vpush(&cm->switchstmt->cases, ((struct swcase) {ex.i, fn->curblk})); - } else if (tk.t == TKWdefault) { - /* default ':' */ - if (!cm->switchstmt) error(&tk.span, "'default' outside of switch statement"); - expect(cm, ':', NULL); - 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 = begin; - } - } else if (tk.t == TKIDENT && match(cm, NULL, ':')) { - /* <label> ':' */ - deflabel(cm, fn, &tk.span, tk.s); - } else { - assert(tk.t == TKIDENT); - /* kludge for no backtracking and no lookahead */ - ex = exprparse(cm, 1, &tk, 1); - 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: - * <- - */ - EMITS { - putbranch(fn, begin = newblk(fn)); - 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: - * <- - */ - EMITS { - putbranch(fn, begin = newblk(fn)); - 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); - } - } - EMITS { - putbranch(fn, end = tr = begin = newblk(fn)); - 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); - } - } - 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); - freearena(&atmpp); - } else if (!terminates) putbranch(fn, begin); - useblk(fn, fl); - } - 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); - 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.s); - if (!label) { - /* create reloc list */ - struct label l = { cm->labels, tk.s, fn->curblk, tk.span }; - 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, NULL); - if (fn->retty.t != TYVOID) { - ex = commaexpr(cm); - 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 = cvt(fn, fn->retty, ex.ty, exprvalue(fn, &ex)); - else - r = structreturn(fn, &ex); - putreturn(fn, r, NOREF); - } - } else { - EMITS putreturn(fn, NOREF, NOREF); - } - stmtterm(cm); - break; - default: - ex = exprparse(cm, 1, NULL, 1); - 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.s); - stmt(cm, fn); - return; - } - st.base = finddecl(cm, tk.s)->ty; - } - 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"); - decl.id = ++staticid; - goto Initz; - case SCNONE: - if (decl.ty.t == TYFUNC) { - decl.scls = SCEXTERN; - 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); - } 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; - } - EMITS { - decl.id = addinstr(fn, mkalloca(typesize(decl.ty), typealign(decl.ty))).i; - } - Initz: - if (st.varini) { - struct decl *d = putdecl(cm, &decl); - bool statik = st.scls & (SCSTATIC | SCEXTERN); - const char *name = decl.name; - put = 1; - if (decl.scls == SCSTATIC) - name = mkhiddensym(fn->name, name, decl.id); - ini = initializer(cm, &d->ty, statik ? EVSTATICINI : EVFOLD, - /* globl? */ decl.scls == SCEXTERN, decl.qual, name); - pdecl(&st, cm); - if (!statik) { - /* fix alloca for actual size, for implicitly sized arrays */ - assert(!isincomplete(d->ty)); - instrtab[decl.id] = mkalloca(typesize(d->ty), typealign(d->ty)); - - if (!initcheck(d->ty, &ini)) { - struct span span = decl.span; - joinspan(&span.ex, ini.span.ex); - error(&span, "cannot initialize '%ty' variable with '%ty'", - d->ty, ini.ty); - } - EMITS { - if (ini.t == EINIT || (d->ty.t == TYARRAY && ini.t == ESTRLIT)) - geninit(fn, d->ty, mkref(RTMP, decl.id), &ini); - else if (isagg(d->ty)) - structcopy(fn, d->ty, mkref(RTMP, decl.id), exprvalue(fn, &ini)); - else { - genstore(fn, d->ty, mkref(RTMP, decl.id), - cvt(fn, d->ty, ini.ty, exprvalue(fn, &ini))); - } - } - } else if (decl.scls == SCEXTERN) { - struct span span = decl.span; - joinspan(&span.ex, ini.span.ex); - error(&span, - "declaration of block local with extern linkage cannot have an initializer"); - } - } else if (decl.scls == SCSTATIC) { - /* zero-initialized static */ - const char *sym = mkhiddensym(fn->name, decl.name, decl.id); - if (decl.ty.t == TYARRAY && isincomplete(decl.ty)) - error(&decl.span, "definition of variable with array type needs size or initializer"); - else - objnewdat(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 (forini) - error(&decl.span, "extern declaration in 'for' loop initializer"); - if (st.varini) goto Initz; - break; - default: assert(0); - } - 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, const char **pnames, const struct span *pspans) -{ - 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 = tdgetqual(td->quals, i), - .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); - } - } - - /* __func__ */ - { - static const char *ifunc; - if (!ifunc) ifunc = intern("__func__"); - union type ty = mkarrtype(mktype(TYCHAR), QCONST, strlen(fn->name) + 1); - const char *sym = mkhiddensym(fn->name, ifunc, 1); - uint off = objnewdat(sym, Srodata, 0, typesize(ty), typealign(ty)); - uchar *p = objout.rodata.p + off; - memcpy(p, fn->name, typearrlen(ty)-1); - putdecl(cm, &(struct decl) { - .ty = ty, .qual = QCONST, - .name = ifunc, .scls = SCSTATIC, .span = (peek(cm, &tk), tk.span), - .id = 1, - }); - } - /* 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 (!strcmp(fn->name, "main") && fn->retty.t == TYINT) { - /* implicit return 0 for main function */ - putreturn(fn, ZEROREF, NOREF); - } else { - if (fn->retty.t != TYVOID && !nerror) { - warn(&cm->fnblkspan, "non-void function may not return a value"); - } - putreturn(fn, NOREF, NOREF); - } - } -} - -void -docomp(struct comp *cm) -{ - struct token tk[1]; - - if (!cm->env) cm->env = &toplevel; - - putdecl(cm, &(struct decl) { mktype(TYVALIST), SCTYPEDEF, .name = intern("__builtin_va_list") }); - - while (peek(cm, tk) != TKEOF) { - struct declstate st = { DTOPLEVEL }; - do { - bool noscls = 0; - int nerr = nerror; - struct decl decl = pdecl(&st, cm); - - if (nerror != nerr) { - if (st.varini) { - (void)expr(cm); - pdecl(&st, cm); - } - continue; - } - if (!decl.scls) { - noscls = 1; - decl.scls = SCEXTERN; - } - decl.isdef = st.varini; - if (st.funcdef) { - const struct typedata *td = &typedata[decl.ty.dat]; - struct function fn = { &cm->fnarena, decl.name, .globl = decl.scls != SCSTATIC }; - fn.fnty = decl.ty; - fn.retty = td->ret; - decl.isdef = 1; - putdecl(cm, &decl); - irinit(&fn); - function(cm, &fn, st.pnames, st.pspans); - if (!nerror && ccopt.dbg.p) - irdump(&fn); - irfini(&fn); - } else if (decl.name) { - struct decl *d = putdecl(cm, &decl); - if (st.varini) { - (void) initializer(cm, &d->ty, EVSTATICINI, decl.scls != SCSTATIC, decl.qual, decl.name); - pdecl(&st, cm); - } else if (decl.ty.t != TYFUNC && decl.scls != SCTYPEDEF && (decl.scls != SCEXTERN || noscls)) { - objnewdat(d->name, Sbss, decl.scls == SCEXTERN, typesize(d->ty), typealign(d->ty)); - } - if (ccopt.dbg.p) efmt("var %s : %tq\n", d->name, d->ty, d->qual); - } else { - if (ccopt.dbg.p && decl.ty.t) efmt("type %ty\n", decl.ty); - } - freearena(&cm->fnarena); - freearena(&cm->exarena); - lexerfreetemps(&cm->lx); - } while (st.more); - } -} - -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 = {0}; - initcm(&cm, file); - docomp(&cm); -} - -void -cpp(struct wbuf *out, const char *file) -{ - struct comp cm = {0}; - initcm(&cm, file); - lexerdump(&cm.lx, out); -} - -/* vim:set ts=3 sw=3 expandtab: */ |