diff options
Diffstat (limited to 'type.c')
| -rw-r--r-- | type.c | 287 |
1 files changed, 287 insertions, 0 deletions
@@ -0,0 +1,287 @@ +#include "common.h" +#include <string.h> + +struct typedata typedata[1<<13]; +const char *ttypenames[1<<10]; + +static ushort +hashtd(const struct typedata *td) +{ + uint h = td->t*33; + switch (td->t) { + case TYARRAY: + h = hashb(h, &td->arrlen, sizeof td->arrlen); + /* fallthru */ + case TYPTR: + h = hashb(h, &td->child, sizeof td->child); + break; + case TYSTRUCT: + case TYUNION: + case TYENUM: + h = hashb(h, &td->id, sizeof td->id); + break; + case TYFUNC: + h = hashb(h, &td->ret, sizeof td->ret); + h = hashb(h, &td->nmemb, sizeof td->nmemb); + for (int i = 0; i < td->nmemb; ++i) { + uchar q = tdgetqual(td->quals, i); + h = hashb(h, &td->param[i], sizeof *td->param); + h = hashb(h, &q, sizeof q); + } + break; + default: + assert(0 && "bad typedata tag"); + } + return h ^ h>>16; +} + +static bool +tdequ(const struct typedata *a, const struct typedata *b) +{ + if (a->t != b->t) return 0; + switch (a->t) { + case TYARRAY: + return a->arrlen == b->arrlen && a->child.bits == b->child.bits; + case TYPTR: + return a->child.bits == b->child.bits; + case TYSTRUCT: + case TYUNION: + case TYENUM: + return a->id == b->id; + case TYFUNC: + if (a->ret.bits != b->ret.bits) return 0; + if (a->nmemb != b->nmemb) return 0; + if (a->variadic != b->variadic) return 0; + for (int i = 0; i < a->nmemb; ++i) { + if (!a->quals != !b->quals || a->param[i].bits != b->param[i].bits) + return 0; + if (a->quals && tdgetqual(a->quals, i) != tdgetqual(b->quals, i)) + return 0; + } + return 1; + break; + default: + assert(0 && "bad typedata tag"); + } +} + +static void * +alloccopy(struct arena **arena, const void *src, uint siz, uint align) +{ + return memcpy(alloc(arena, siz, align), src, siz); +} + +static ushort +interntd(const struct typedata *td) +{ + uint h, i, n = arraylength(typedata); + for (i = h = hashtd(td); n--; ++i) { + struct typedata *slot = &typedata[i &= arraylength(typedata) - 1]; + if (!slot->t) { + uint nmemb; + static struct arena *datarena; + if (!datarena) { + enum { N = 1<<12 }; + static union { char m[sizeof(struct arena) + N]; struct arena *_align; } amem; + datarena = (void *)amem.m, datarena->cap = N; + } + + Copy: + nmemb = td->nmemb; + *slot = *td; + switch (slot->t) { + case TYENUM: + if (slot->var) + slot->var = alloccopy(&datarena, td->var, nmemb * sizeof *slot->var, 0); + break; + case TYSTRUCT: + case TYUNION: + if (slot->fld) + slot->fld = alloccopy(&datarena, td->fld, nmemb * sizeof *slot->fld, 0); + Qual: + if (slot->quals) + slot->quals = alloccopy(&datarena, td->quals, tdqualsiz(nmemb), 1); + break; + case TYFUNC: + if (slot->param) { + slot->param = alloccopy(&datarena, td->param, nmemb * sizeof *slot->param, 0); + } + goto Qual; + } + return i; + } else if (tdequ(slot, td)) { + if (td->t == TYSTRUCT || td->t == TYUNION) + goto Copy; + return i; + } + } + assert(0 && "typedata[] full"); +} + +bool +isincomplete(union type t) +{ + switch (t.t) { + case TYVOID: return 1; + case TYARRAY: return !typearrlen(t); + case TYSTRUCT: + case TYUNION: + return typedata[t.dat].nmemb == 0; + } + return 0; +} + +uint +typesize(union type t) +{ + if (isprim(t) || t.t == TYPTR) return targ_primsizes[t.t]; + switch (t.t) { + case TYENUM: + return targ_primsizes[t.backing]; + case TYARRAY: + return t.flag & TFCHLDPRIM ? targ_primsizes[t.child] * t.arrlen : typedata[t.dat].siz; + case TYSTRUCT: + case TYUNION: + return typedata[t.dat].siz; + } + return 0; +} + +uint +typealign(union type t) +{ + if (isprim(t) || t.t == TYPTR) return targ_primalign[t.t]; + switch (t.t) { + case TYENUM: + return targ_primalign[t.backing]; + case TYARRAY: + return targ_primalign[typechild(t).t]; + case TYSTRUCT: + case TYUNION: + return typedata[t.dat].align; + } + return 0; +} + +union type +mkptrtype(union type t, int qual) +{ + if (isprim(t)) + return mktype(TYPTR, .flag = TFCHLDPRIM | (qual & TFCHLDQUAL), .child = t.t); + else if (t.t == TYENUM || t.t == TYFUNC || isagg(t)) + return mktype(TYPTR, .flag = TFCHLDISDAT | (qual & TFCHLDQUAL), .dat = t.dat); + return mktype(TYPTR, .flag = qual & TFCHLDQUAL, + .dat = interntd(&(struct typedata) { TYPTR, .child = t })); +} + +union type +mkarrtype(union type t, int qual, uint n) +{ + if (isprim(t) && n < 256) + return mktype(TYARRAY, .flag = TFCHLDPRIM | (qual & TFCHLDQUAL), .child = t.t, .arrlen = n); + return mktype(TYARRAY, .flag = qual & TFCHLDQUAL, + .dat = interntd(&(struct typedata) { TYARRAY, .child = t, .arrlen = n })); +} + +union type +mkfntype(union type ret, uint n, const union type *par, const uchar *qual, bool kandr, bool variadic) +{ + struct typedata td = { TYFUNC, .ret = ret, .nmemb = n, .param = par, .quals = qual }; + td.kandr = kandr, td.variadic = variadic; + return mktype(TYFUNC, .dat = interntd(&td)); +} + + +union type +completetype(const char *name, int id, struct typedata *td) +{ + assert(td->t == TYENUM || td->t == TYSTRUCT || td->t == TYUNION); + td->id = id; + assert(id < arraylength(ttypenames) && "too many tag types"); + if (ttypenames[id]) + assert(ttypenames[id] == name && "bad redefn"); + else + ttypenames[id] = name; + return mktype(td->t, .dat = interntd(td)); +} + +union type +mktagtype(const char *name, struct typedata *td) +{ + static int id; + return completetype(name, id++, td); +} + +union type +typedecay(union type t) +{ + if (t.t == TYARRAY) + return mkptrtype(typechild(t), t.flag & TFCHLDQUAL); + if (t.t == TYFUNC) + return mkptrtype(t, 0); + return t; +} + +bool /* 6.5.16.1 Simple assignment Constraints */ +assigncompat(union type dst, union type src) +{ + union type ds, ss; + if (dst.bits == src.bits) return 1; + if (isarith(dst) && isarith(src)) return 1; + if (dst.t == TYPTR && src.t == TYPTR) { + ds = typechild(dst); + ss = typechild(src); + if (ds.bits == ss.bits || ss.t == TYVOID || ds.t == TYVOID) + return ((dst.flag & TFCHLDQUAL) & (src.flag & TFCHLDQUAL)) == (src.flag & TFCHLDQUAL); + } + if (dst.t == TYBOOL && src.t == TYPTR) + return 1; + return 0; +} + +enum typetag +intpromote(enum typetag t) +{ + static int intisshort = -1; + if (intisshort < 0) intisshort = targ_primsizes[TYINT] == targ_primsizes[TYSHORT]; + if (intisshort && t == TYUSHORT) return TYUINT; + return t < TYINT ? TYINT : t; +} + +union type /* 6.3.1.8 Usual arithmetic conversions */ +cvtarith(union type a, union type b) +{ + const union type none = {0}; + + if (!isarith(a) || !isarith(b)) return none; + if (a.t == TYENUM) a = typechild(a); + if (b.t == TYENUM) b = typechild(b); + if (isflt(a) || isflt(b)) { + /* when one type is float, choose type with greatest rank */ + /* enumeration order of type tags reflects arithmetic type rank */ + return a.t > b.t ? a : b; + } + a.t = intpromote(a.t); + b.t = intpromote(b.t); + if (a.bits == b.bits) return a; + + if (issigned(a) == issigned(b)) { + /* when both are integers with same signage, choose type with greatest rank */ + return a.t > b.t ? a : b; + } + /* if the signed type can represent all values of the unsigned type, + * choose it, otherwise choose its corresponding unsigned type */ + /* so long long + unsigned = long long; + * but long long + unsigned long = unsigned long long */ + if (issigned(a)) { + if (targ_primsizes[a.t] <= targ_primsizes[b.t]) + a.t += 1; /* make unsigned */ + return a; + } else { + if (targ_primsizes[b.t] <= targ_primsizes[a.t]) + b.t += 1; /* make unsigned */ + return b; + } +} + +/* vim:set ts=3 sw=3 expandtab: */ |