diff options
| author | 2025-10-19 08:09:09 +0200 | |
|---|---|---|
| committer | 2025-10-19 08:09:09 +0200 | |
| commit | dea8fd171acb54b6d9685422d5e391fb55074008 (patch) | |
| tree | 2c149892f35c5183c9b2a1da4ab437228dc432ef /c/eval.c | |
| parent | 3437945692f2b87883a4f066473c9deed50f25f5 (diff) | |
Organize source files into directories
Diffstat (limited to 'c/eval.c')
| -rw-r--r-- | c/eval.c | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/c/eval.c b/c/eval.c new file mode 100644 index 0000000..d32cd6e --- /dev/null +++ b/c/eval.c @@ -0,0 +1,316 @@ +#include "c.h" + +static int +targ2hosttype(enum typetag t) +{ + if (t == TYPTR) t = targ_64bit ? TYUVLONG : TYUINT; + if (isintt(t)) { + int siz = targ_primsizes[t]; + int sgn = issignedt(t); +#define U(Ty,Tag) if (!sgn & (siz == sizeof(unsigned Ty))) return Tag; +#define S(Ty,Tag) if ( sgn & (siz == sizeof(signed Ty))) return Tag; + U(char, TYUCHAR) + S(char, TYSCHAR) + U(short, TYUSHORT) + S(short, TYSHORT) + U(int, TYUINT) + S(int, TYINT) + U(long long, TYUVLONG) + S(long long, TYVLONG) +#undef U +#undef S + } else if (isfltt(t)) return t; + return 0; +} + +static bool +numcast(union type ty, struct expr *dst, const struct expr *src) +{ + struct expr tmp; + enum typetag td = targ2hosttype(ty.t); + enum typetag ts = targ2hosttype(src->ty.t == TYENUM ? src->ty.backing : src->ty.t); + if (src == dst) tmp = *src, src = &tmp; + + assert(src->t == ENUMLIT); +#define TT(d,s) (td == d && ts == s) +#define TF(d) (td == d && isfltt(ts)) + if (!ts || !td) return 0; + else if (TT(TYFLOAT, TYFLOAT)) dst->f = (float) src->f; + else if (TT(TYFLOAT, TYDOUBLE)) dst->f = (float) src->f; + else if (TT(TYDOUBLE, TYFLOAT)) dst->f = src->f; + else if (TT(TYDOUBLE, TYDOUBLE)) dst->f = src->f; + else if (TT(TYFLOAT, TYUVLONG)) dst->f = (float) src->u; + else if (TT(TYDOUBLE, TYUVLONG)) dst->f = (double) src->u; + else if (td == TYFLOAT) dst->f = (float) src->i; + else if (td == TYDOUBLE) dst->f = (double) src->i; + else if (TF(TYUVLONG)) dst->u = src->f; + else if (TF(TYBOOL)) dst->i = (bool) src->f; + else if (isfltt(ts)) { dst->i = src->f; goto Narrow; } + else { + Narrow: + switch (td) { +#define I(Ty, Tag) case Tag: dst->i = (Ty) src->i; break; + I(bool, TYBOOL) + I(signed char, TYSCHAR) + I(unsigned char, TYUCHAR) + I(signed short, TYSHORT) + I(unsigned short, TYUSHORT) + I(signed int, TYINT) + I(unsigned int, TYUINT) + I(signed long long, TYVLONG) + I(unsigned long long, TYUVLONG) +#undef I + case TYFLOAT: dst->f = (float) src->f; break; + case TYDOUBLE: dst->f = src->f; break; + default: assert(0 && "bad cast?"); + } + } +#undef TT +#undef TF + + dst->t = ENUMLIT; + dst->ty = ty; + return 1; +} + +static bool +unop(struct expr *ex, enum evalmode mode) +{ + struct expr *sub = ex->sub; + + if (mode >= EVSTATICINI && ex->t == EDEREF) { + uvlong off; + struct bytes s; + if (sub->t == ESTRLIT) { + /* *"s" */ + off = 0; + s = sub->s; + } else if (sub->t == EADD && sub->sub[0].t == ESTRLIT && eval(&sub->sub[1], EVINTCONST)) { + /* "s"[0] */ + assert(sub->sub[1].t == ENUMLIT && isint(sub->sub[1].ty)); + off = sub->sub[1].u; + s = sub->sub[0].s; + } else if (sub->t == EADD && sub->sub[1].t == ESTRLIT && eval(&sub->sub[0], EVINTCONST)) { + /* 0["s"] */ + assert(sub->sub[0].t == ENUMLIT && isint(sub->sub[0].ty)); + off = sub->sub[0].u; + s = sub->sub[1].s; + } else return 0; + if (off > s.n) return 0; + ex->t = ENUMLIT; + ex->ty = mktype(TYINT); + ex->u = off == s.n ? 0 : s.p[off]; + return 1; + } + if (ex->t == EADDROF) { + assert(ex->ty.t == TYPTR); + /* oh boy */ + /* match &(*(T *)12345).fld */ + if (sub->t == EGETF && sub->sub->t == EDEREF && eval(sub->sub->sub, EVFOLD) && sub->sub->sub->t == ENUMLIT) { + ex->t = ENUMLIT; + ex->u = sub->sub->sub->u + sub->fld.off; + return 1; + } + } + if (sub->t != ENUMLIT && !eval(sub, mode)) return 0; + if (sub->t != ENUMLIT) return 0; + switch (ex->t) { + case ECAST: + if (ex->ty.t == TYPTR) { + ex->t = ENUMLIT; + ex->u = sub->u; + return 1; + } + break; + case EPLUS: + break; + case ENEG: + if (isint(sub->ty)) sub->u = -sub->u; + else assert(isflt(sub->ty)), sub->f = -sub->f; + break; + case ECOMPL: + if (!isint(sub->ty)) return 0; + sub->u = ~sub->u; + break; + case ELOGNOT: + if (isint(sub->ty) || isptrcvt(sub->ty)) sub->u = !sub->u; + else assert(isflt(sub->ty)), sub->u = !sub->f; + break; + default: + return 0; + } + if (!numcast(ex->ty, ex, sub)) return 0; + return 1; +} + +/* link time constants */ +static bool +isglobsym(const struct expr *ex) +{ + return ex->t == ESTRLIT || (ex->t == ESYM && ex->sym && (ex->sym->scls & (SCSTATIC | SCEXTERN))); +} + +static bool +isaddrconst(struct expr *ex) +{ + if (ex->t == ECAST) + return isaddrconst(ex->sub) || (eval(ex->sub, EVSTATICINI) && ex->sub->t == ENUMLIT); + if (ex->t == EADDROF && (isglobsym(ex->sub) || (ex->sub->t == EGETF && isglobsym(ex->sub->sub)))) + return 1; + if (isglobsym(ex) && in_range(ex->ty.t, TYARRAY, TYFUNC)) + return 1; + if (ex->t == ESUB) + return isglobsym(&ex->sub[0]) && isint(ex->sub[1].ty) && eval(&ex->sub[1], EVSTATICINI); + if (ex->t == EADD) { + for (int swp = 0; swp < 2; ++swp) + if (isglobsym(&ex->sub[swp]) && isint(ex->sub[swp^1].ty) && eval(&ex->sub[swp^1], EVSTATICINI)) + return 1; + } + return 0; +} + +static bool +binop(struct expr *ex, enum evalmode mode) +{ + union type oty; + bool flt; + bool sgn; + int t; + struct expr *lhs = &ex->sub[0], *rhs = &ex->sub[1]; + + if (ex->ty.t == TYPTR) mode = EVFOLD; + if (!eval(lhs, mode)) return 0; + if (!eval(rhs, mode)) return 0; + if (in_range(ex->t, EADD, ESHR)) + oty = ex->ty; + else + oty = cvtarith(lhs->ty, rhs->ty); + if (!numcast(oty, lhs, lhs)) return 0; + if (!numcast(oty, rhs, rhs)) return 0; + flt = isflt(oty); + sgn = issigned(oty); + switch (ex->t) { +#define ef else if + case EADD: if (flt) lhs->f += rhs->f; + else lhs->u += rhs->u; + break; + case ESUB: if (oty.t == TYPTR) { + assert(lhs->t == ENUMLIT && rhs->t == ENUMLIT); + assert(!isincomplete(typechild(ex->ty))); + lhs->u = (lhs->u - rhs->u) / typesize(typechild(ex->ty)); + } + ef (flt) lhs->f -= rhs->f; + else lhs->u -= rhs->u; + break; + case EMUL: if (flt) lhs->f *= rhs->f; + ef (sgn) lhs->i *= rhs->i; + else lhs->u *= rhs->u; + break; + case EDIV: if (!flt && !rhs->i) return 0; + ef (flt) lhs->f /= rhs->f; + ef (sgn) lhs->i /= rhs->i; + else lhs->u /= rhs->u; + break; + case EREM: if (!rhs->i) return 0; + ef (sgn) lhs->i %= rhs->i; + else lhs->u %= rhs->u; + break; + case EBAND: lhs->u &= rhs->u; + break; + case EBIOR: lhs->u |= rhs->u; + break; + case EXOR: lhs->u ^= rhs->u; + break; + case ESHL: if (sgn && lhs->i < 0) return 0; + ef (rhs->i >= 8*targ_primsizes[oty.t]) return 0; + lhs->u <<= rhs->u; + break; + case ESHR: if (rhs->i >= 8*targ_primsizes[oty.t]) return 0; + ef (sgn) lhs->i >>= rhs->i; + else lhs->u >>= rhs->u; + break; + case ELOGAND: if (flt) t = lhs->f && rhs->f; + else t = lhs->u && rhs->u; + lhs->u = t; + break; + case ELOGIOR: if (flt) t = lhs->f || rhs->f; + else t = lhs->u || rhs->u; + lhs->u = t; + break; + case EEQU: if (flt) t = lhs->f == rhs->f; + else t = lhs->u == rhs->u; + lhs->u = t; + break; + case ENEQ: if (flt) t = lhs->f != rhs->f; + else t = lhs->u != rhs->u; + lhs->u = t; + break; + case ELTH: if (flt) t = lhs->f < rhs->f; + ef (sgn) t = lhs->i < rhs->i; + else t = lhs->u < rhs->u; + lhs->u = t; + break; + case EGTH: if (flt) t = lhs->f > rhs->f; + ef (sgn) t = lhs->i > rhs->i; + else t = lhs->u > rhs->u; + lhs->u = t; + break; + case ELTE: if (flt) t = lhs->f <= rhs->f; + ef (sgn) t = lhs->i <= rhs->i; + else t = lhs->u <= rhs->u; + lhs->u = t; + break; + case EGTE: if (flt) t = lhs->f >= rhs->f; + ef (sgn) t = lhs->i >= rhs->i; + else t = lhs->u >= rhs->u; + lhs->u = t; + break; + default: return 0; +#undef ef + } + + return numcast(ex->ty, ex, lhs); +} + +bool +eval(struct expr *ex, enum evalmode mode) +{ + if (ex->t == ENUMLIT) { + if (mode <= EVINTCONST) return isint(ex->ty); + return 1; + } + if (ex->t == ESTRLIT && mode > EVINTCONST) return 1; + if (mode == EVSTATICINI && isaddrconst(ex)) { + struct expr *e = ex; + while (e->t == ECAST) e = e->sub; + if (e != ex) { + e->ty = ex->ty; + *ex = *e; + } + return 1; + } + mode += mode == EVINTCONST; + if (isunop(ex->t)) return unop(ex, mode) && eval(ex, mode); + if (isbinop(ex->t)) return binop(ex, mode) && eval(ex, mode); + if (ex->t == ESEQ) { + if (!eval(&ex->sub[0], mode)) return 0; + *ex = ex->sub[1]; + return eval(ex, mode); + } + if (ex->t == ECOND) { + if (!eval(&ex->sub[0], mode) || ex->sub[0].t != ENUMLIT) return 0; + if (!eval(&ex->sub[1], mode)) return 0; + if (!eval(&ex->sub[2], mode)) return 0; + *ex = ex->sub[!ex->sub[0].u + 1]; + return eval(ex, mode); + } + if (ex->t == EINIT) { + for (struct initval *v = ex->init->vals; v; v = v->next) { + if (!eval(&v->ex, mode)) return 0; + } + return 1; + } + return 0; +} + +/* vim:set ts=3 sw=3 expandtab: */ |