From 80ee6292c33b6342e97c46af9577a588f9fabd4a Mon Sep 17 00:00:00 2001 From: lemon Date: Mon, 26 Jun 2023 09:34:38 +0200 Subject: c: move codegen code after decl parser --- c.c | 3404 +++++++++++++++++++++++++++++++++---------------------------------- 1 file changed, 1702 insertions(+), 1702 deletions(-) (limited to 'c.c') diff --git a/c.c b/c.c index 2035d5d..bb3b064 100644 --- a/c.c +++ b/c.c @@ -1009,1856 +1009,1856 @@ commaexpr(struct comp *cm) } /*****************/ -/* IR Generation */ +/* Decls Parsing */ /*****************/ -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) +static union type +buildagg(struct comp *cm, enum typetag tt, const char *name, int id) { - compileexpr(fn, ex, /*discard*/ 1); -} + 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"; + while (!match(cm, &tk, '}')) { + struct declstate st = { DFIELD }; + do { + struct decl decl = pdecl(&st, cm); + 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); + } + if (decl.ty.t) { + uint align = typealign(decl.ty); + uint siz = typesize(decl.ty); + uint off = isunion ? 0 : alignup(td.siz, align); + struct namedfield f = { decl.name, { decl.ty, off, .qual = decl.qual }}; + if (!decl.name) { + 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"); + } + } + 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) + 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; + } + } while (st.more); + } + 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 void -structcopy(struct function *fn, union type ty, union ref dst, union ref src) +static inline void +inttyminmax(vlong *min, uvlong *max, enum typetag tt) { - union irtype typ = mkirtype(ty); - addinstr(fn, mkarginstr(typ, dst)); - addinstr(fn, mkarginstr(typ, src)); - addinstr(fn, mkintrin(INstructcopy, 0, 2)); + uint bits = 8*targ_primsizes[tt]; + *min = isunsignedt(tt) ? 0 : -(1ll << (bits - 1)); + *max = isunsignedt(tt) ? ~0ull >> (64 - bits) : (1ll << (bits - 1)) - 1; } -static union ref -structreturn(struct function *fn, const struct expr *src) +/* 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) { - return expraddr(fn, src); -} + 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; -static union ref compilecall(struct function *fn, const struct expr *ex); + 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; -static union ref -expraddr(struct function *fn, const struct expr *ex) -{ - struct decl *decl; - union ref r; - struct instr ins = {0}; + 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); + } + } - 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: - assert(!"nyi"); + td.backing = 0; + for (int t = TYINT; t <= TYUVLONG; ++t) { + inttyminmax(&tymin, &tymax, t); + if (minv >= tymin && maxv <= tymax) { + td.backing = t; break; - default: - assert(0); } - break; - case ESTRLIT: - return mkdatref(NULL, ex->s.n+1, /*align*/ 1, ex->s.p, ex->s.n, /*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); - 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[t.t]; - 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); + 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)); } - 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}; + if (td.backing >= TYVLONG && !somelonglong && ccopt.cstd == STDC89 && ccopt.pedant) + warn(span, "enum backing type is '%ty' in %M", mktype(td.backing)); - 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); + ty = mktagtype(name, &td); + ty.backing = td.backing; + return ty; } -static union ref -cvt(struct function *fn, enum typetag to, enum typetag from, union ref ref) +static union type +tagtype(struct comp *cm, enum toktag kind) { - enum irclass kto = type2cls[to], kfrom = type2cls[from]; - struct instr ins = {0}; - if (kto == kfrom && to != TYBOOL) return ref; - if (ref.t == RICON && kto < KF4) return ref; + struct token tk; + union type t; + struct span span; + enum typetag tt = kind == TKWenum ? TYENUM : kind == TKWstruct ? TYSTRUCT : TYUNION; + const char *tag = NULL; - 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); + 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); } - if (kisflt(kto) && kfrom == KI4) ins.op = issignedt(from) ? Ocvts4f : Ocvtu4f; - else if (to == TYBOOL && kisflt(kfrom)) ins.op = Oneq, ins.r = mkfltcon(kfrom, 0.0); - else if (kisflt(kto) && kfrom == KI8) ins.op = issignedt(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 = issignedt(to) ? Ocvtf4s : Ocvtf4u; - else if (kfrom == KF8) ins.op = issignedt(to) ? Ocvtf8s : Ocvtf8u; - else assert(0); - } else { - if (to == TYBOOL) { - if (from == 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; + 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 (kfrom == KI4 && issignedt(from)) ins.op = Oexts4; - else if (kfrom == KI4) ins.op = Oextu4; - else if (kto == KI4 && isintcon(ref)) - return issignedt(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, enum typetag tt, union ref ref) -{ - struct instr ins = {0}; - assert(isscalart(tt)); - if (targ_primsizes[tt] >= cls2siz[to]) return ref; - 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)]; + 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); + } } - ins.l = ref; - return addinstr(fn, ins); -} - -union ref -genptroff(struct function *fn, enum op op, uint siz, union ref ptr, - enum typetag tt, union ref idx) -{ - uint cls = type2cls[targ_sizetype]; - union ref off; - assert(siz); - - idx = cvt(fn, targ_sizetype, tt, idx); - if (siz == 1) off = idx; - else if (idx.t == RICON) - off = mkintcon(cls, idx.i * 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); + 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; } -struct condphis { - vec_of(union ref) ref; -}; - static void -condexprrec(struct function *fn, const struct expr *ex, struct condphis *phis, - int boolcon, struct block *end, struct block *zero) -{ - struct block *tr, *fl, *next; - union ref r; - while (ex->t == ESEQ) { - expreffects(fn, &ex->sub[0]); - ex = &ex->sub[1]; - } - if (ex->t == ELOGAND) { - next = newblk(fn); - condexprrec(fn, &ex->sub[0], phis, 0, next, end); - useblk(fn, next); - condexprrec(fn, &ex->sub[1], phis, -2, end, zero); - } else if (ex->t == ELOGIOR) { - next = newblk(fn); - condexprrec(fn, &ex->sub[0], phis, 1, end, next); - useblk(fn, next); - condexprrec(fn, &ex->sub[1], phis, -2, end, zero); - } 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, zero); - useblk(fn, fl); - condexprrec(fn, &ex->sub[2], phis, -1, end, zero); - } else { - r = exprvalue(fn, ex); - if (boolcon == -2) - r = cvt(fn, TYBOOL, ex->ty.t, r); - if (boolcon >= 0) - vpush(&phis->ref, mkintcon(KI4, boolcon)); - else - vpush(&phis->ref, r); - if (zero) { - putcondbranch(fn, r, end, zero); - } 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) +declspec(struct declstate *st, struct comp *cm) { - union ref refbuf[8]; - struct condphis phis = { VINIT(refbuf, arraylength(refbuf)) }; - struct block *dst = newblk(fn); - union ref r; - condexprrec(fn, ex, &phis, -1, dst, NULL); - useblk(fn, dst); - assert(fn->curblk->npred == phis.ref.n); - r = addphi(fn, type2cls[ex->ty.t], phis.ref.p); - vfree(&phis.ref); - return r; -} + 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}; -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[ex->ty.t]; + for (;;) { + peek(cm, &tk); + switch (tk.t) { + case TKWconst: + st->qual |= QCONST; + 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 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; } - 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.t, arg->ty.t, exprvalue(fn, arg)); - vpush(&insns, mkarginstr(mkirtype(ty), r)); +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; + ioflush(&bstderr); + if (arith == KFLOAT) + t = TYFLOAT; + else if (arith == KDOUBLE) + t = TYDOUBLE; + else if (arith == (KLONG | KDOUBLE)) { + t = TYLDOUBLE; + error(&span, "`long double' is unsupported"); + } 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; } - 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 -compileexpr(struct function *fn, const struct expr *ex, bool discard) +static int +cvqual(struct comp *cm) { - union type ty; - union ref r, q; - enum irclass cls = type2cls[ex->ty.t]; - struct instr ins = {0}; - int swp = 0; - struct expr *sub; + struct token tk; + int q = 0; + while (match(cm, &tk, TKWconst) || match(cm, &tk, TKWvolatile)) + q |= tk.t == TKWconst ? QCONST : QVOLATILE; + return q; +} - eval((struct expr *)ex, EVFOLD); - sub = ex->sub; +static void +decltypes(struct comp *cm, struct decllist *list, const char **name, struct span *span) { + struct token tk; + struct decllist *ptr, node; - 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; - 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.t, sub->ty.t, 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.t, sub->ty.t, 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.t, sub->ty.t, 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.t, sub[0].ty.t, ins.l); - ins.r = cvt(fn, ex->ty.t, sub[1].ty.t, 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.t, 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.t, q); - 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.t, sub[0].ty.t, ins.l); - ins.r = cvt(fn, ty.t, sub[1].ty.t, ins.r); - ins.cls = type2cls[ty.t]; - return addinstr(fn, ins); - case ESET: - assert(isscalar(ex->ty)); - q = cvt(fn, sub[0].ty.t, sub[1].ty.t, exprvalue(fn, &sub[1])); - r = expraddr(fn, &sub[0]); - genstore(fn, ex->ty, r, q); - if (discard) return NOREF; - return narrow(fn, cls, sub[0].ty.t, 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: - r = expraddr(fn, &sub[0]); - ty = in_range(ex->t, ESETSHL, ESETSHR) ? mktype(intpromote(ex->ty.t)) - : cvtarith(sub[0].ty, sub[1].ty); - ins.cls = cls; - ins.l = genload(fn, ex->ty, r); - ins.r = exprvalue(fn, &sub[1]); - if ((ins.op != Oadd && ins.op != Osub) || cls != KPTR) { - ins.l = cvt(fn, ty.t, sub[0].ty.t, ins.l); - ins.r = cvt(fn, ex->ty.t, sub[1].ty.t, ins.r); - q = addinstr(fn, ins); - } else { - q = genptroff(fn, ins.op, typesize(typechild(ex->ty)), ins.l, sub[1].ty.t, ins.r); - } - genstore(fn, ex->ty, r, q); - if (discard) return NOREF; - return narrow(fn, cls, ex->ty.t, q); - case ECALL: - r = compilecall(fn, ex); - if (isint(ex->ty)) - return narrow(fn, cls, ex->ty.t, r); - return r; - case ECOND: - if (ex->ty.t == TYVOID) { - 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); - 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); - -static void -deflabel(struct comp *cm, struct function *fn, const struct span *span, const char *name) -{ - struct label *label = pmap_get(&cm->labels, name); - if (label && label->usespan.ex.len == 0) { - error(span, "redefinition of label '%s'", name); - } else if (label) { - struct block *new; - 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 { - if (!nerror) { - struct block *new = newblk(fn); - if (fn->curblk) putbranch(fn, new); - useblk(fn, new); - } - pmap_set(&cm->labels, name, ((struct label) { .blk = fn->curblk })); - } -} - -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->loopbreak, save[1] = cm->loopcont; - cm->loopbreak = brk, cm->loopcont = cont; - ++cm->loopdepth; - - terminates = stmt(cm, fn); - - --cm->loopdepth; - cm->loopbreak = save[0], cm->loopcont = save[1]; - - return terminates; -} - -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; - struct expr ex; - struct env e; - union ref r; - struct token tk; - bool terminates = 0; - bool doemit = fn->curblk; - -#define EMITS if (doemit && !nerror) - - while (match(cm, &tk, TKIDENT)) { - if (match(cm, NULL, ':')){ - /*