diff options
Diffstat (limited to 'src/c_eval.c')
| -rw-r--r-- | src/c_eval.c | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/src/c_eval.c b/src/c_eval.c new file mode 100644 index 0000000..3dfbbfb --- /dev/null +++ b/src/c_eval.c @@ -0,0 +1,437 @@ +#include "c.h" +#include "../ir/ir.h" +#include <limits.h> + +static int +targ2hosttype(enum typetag t) +{ + if (t == TYPTR) t = targ_64bit ? TYUVLONG : TYUINT; + if (isintt(t)) { + int siz = targ_primsizes[t]; + int sgn = issignedt(t); +#define U(Ty,Tag) if (!sgn & (siz == sizeof(unsigned Ty))) return Tag; +#define S(Ty,Tag) if ( sgn & (siz == sizeof(signed Ty))) return Tag; + U(char, TYUCHAR) + S(char, TYSCHAR) + U(short, TYUSHORT) + S(short, TYSHORT) + U(int, TYUINT) + S(int, TYINT) + U(long long, TYUVLONG) + S(long long, TYVLONG) +#undef U +#undef S + } else if (t == TYLDOUBLE) return TYDOUBLE; + else if (isfltt(t) || iscomplext(t)) return t; + return 0; +} + +static bool +numcast(union type ty, struct expr *dst, const struct expr *src) +{ + enum typetag td = targ2hosttype(scalartypet(ty)), + ts = targ2hosttype(scalartypet(src->ty)); + vlong isrc; + struct expr tmp; + if (src == dst) tmp = *src, src = &tmp; + + assert(src->t == ENUMLIT); +#define TT(d,s) (td == d && ts == s) +#define TF(d) (td == d && isfltt(ts)) + if (!ts || !td) return 0; + else if (TT(TYFLOAT, TYFLOAT)) dst->f = (float) src->f; + else if (TT(TYFLOAT, TYDOUBLE)) dst->f = (float) src->f; + else if (TT(TYDOUBLE, TYFLOAT)) dst->f = src->f; + else if (TT(TYDOUBLE, TYDOUBLE)) dst->f = src->f; + else if (TT(TYFLOAT, TYUVLONG)) dst->f = (float) src->u; + else if (TT(TYDOUBLE, TYUVLONG)) dst->f = (double) src->u; + else if (td == TYFLOAT) dst->f = (float) src->i; + else if (td == TYDOUBLE) dst->f = (double) src->i; + else if (TF(TYUVLONG)) dst->u = src->f; + else if (TF(TYBOOL)) dst->i = (bool) src->f; + else if (isfltt(ts)) { isrc = src->f; goto Narrow; } + else { + isrc = src->i; + Narrow: + switch (td) { +#define I(Ty, Tag) case Tag: dst->i = (Ty) isrc; break; + I(bool, TYBOOL) + I(signed char, TYSCHAR) + I(unsigned char, TYUCHAR) + I(signed short, TYSHORT) + I(unsigned short, TYUSHORT) + I(signed int, TYINT) + I(unsigned int, TYUINT) + I(signed long long, TYVLONG) + I(unsigned long long, TYUVLONG) +#undef I + case TYFLOAT: dst->f = (float) src->f; break; + case TYDOUBLE: dst->f = src->f; break; + default: assert(0 && "bad cast?"); + } + } +#undef TT +#undef TF + + dst->t = ENUMLIT; + dst->ty = ty; + return 1; +} + +static struct expr * +lit2ssym(struct expr *ex) +{ + ex->ssym.sym = xcon2sym(expraddr(NULL, ex).i); + ex->ssym.local = 1; + ex->ssym.func = ex->ssym.off = 0; + ex->t = ESSYMREF; + return ex; +} + +static struct expr +staticaddrof(struct expr *ex, enum evalmode mode) +{ + struct expr ret = { .ty = mkptrtype(ex->ty, ex->qual), .span = ex->span }; + if (ex->t == ESYM && ex->ty.t < NTYPETAG) { + const struct decl *decl = &declsbuf.p[ex->decl]; + if (decl->sym && (decl->scls & (SCAUTO|SCREGISTER)) == 0) { + ret.t = ESSYMREF; + ret.ssym.sym = decl->sym; + ret.ssym.off = 0; + ret.ssym.func = decl->ty.t == TYFUNC; + ret.ssym.local = (decl->scls == SCSTATIC || decl->isdef); + } + } else if (ex->t == EDEREF && eval(ex->sub, EVSTATICINI)) { + ret = *ex->sub; + } else if (ex->t == EGETF && (ret = staticaddrof(ex->sub, mode)).t) { + if (ret.t == ESSYMREF) { + ex->t = ESSYMREF; + vlong off = (vlong) ret.ssym.off + ex->fld.off; + if ((int) off != off) return ret.t = 0, ret; + ret.ssym.off = off; + } else if (ret.t == ENUMLIT) { + ret.u += ex->fld.off; + } else assert(0); + } else if (ex->t == ESTRLIT || (mode == EVSTATICINI && ex->t == EINIT)) { + ret = *lit2ssym(ex); + } else if (ex->t == ENUMLIT || ex->t == ESSYMREF) ret = *ex; + return ret; +} + +static bool +isstaticlval(const struct expr *ex, enum evalmode mode) +{ + return ex->t == ESTRLIT || ex->t == ESSYMREF || (mode == EVSTATICINI && ex->t == EINIT); +} + +static bool +truthy(const struct expr *ex) +{ + switch (ex->t) { + default: assert(0 && "!scalar?"); + case ENUMLIT: + return isflt(ex->ty) ? ex->f != 0.0 : ex->u != 0; + case ESTRLIT: case ESSYMREF: + return 1; + } +} + +static bool +unop(struct expr *ex, enum evalmode mode) +{ + struct expr *sub = ex->sub; + + if (mode >= EVSTATICINI && ex->t == EDEREF) { + uvlong off; + uchar *p; + uint len; + uint csiz; + /* HACK */ + if (sub->t == ESTRLIT) { + /* *"s" */ + off = 0; + p = sub->s.p, len = sub->s.n; + csiz = typesize(typechild(sub->ty)); + StrRead: + if (off > len) return 0; + ex->t = ENUMLIT; + ex->ty = mktype(TYINT); + if (off == len) ex->u = 0; + else if (csiz == 1) ex->u = p[off]; + else if (csiz == 2) ex->u = ((short *)p)[off]; + else if (csiz == 4) ex->u = ((int *)p)[off]; + return 1; + } else if (sub->t == EADD && sub->sub[0].t == ESTRLIT && eval(&sub->sub[1], EVINTCONST)) { + /* "s"[0] */ + assert(sub->sub[1].t == ENUMLIT && isint(sub->sub[1].ty)); + off = sub->sub[1].u; + p = sub->sub[0].s.p, len = sub->sub[0].s.n; + csiz = typesize(typechild(sub->sub[0].ty)); + goto StrRead; + } else if (sub->t == EADD && sub->sub[1].t == ESTRLIT && eval(&sub->sub[0], EVINTCONST)) { + /* 0["s"] */ + assert(sub->sub[0].t == ENUMLIT && isint(sub->sub[0].ty)); + off = sub->sub[0].u; + p = sub->sub[1].s.p, len = sub->sub[1].s.n; + csiz = typesize(typechild(sub->sub[1].ty)); + goto StrRead; + } else return 0; + } else if (ex->t == EADDROF) { + assert(ex->ty.t == TYPTR); + struct expr ex2 = staticaddrof(ex->sub, mode); + if (!ex2.t) return 0; + ex2.span = ex->span; + ex2.ty = ex->ty; + *ex = ex2; + return 1; + } else if (ex->t == EGETF && !ex->fld.bitsiz) { + /* <lvalue>.memb -> is an address lvalue if 'memb' is of array type */ + if (ex->ty.t == TYARRAY) { + struct expr ex2; + if ((ex2 = staticaddrof(ex->sub, mode)).t) { + if (ex2.t == ENUMLIT) { + ex->t = ENUMLIT; + ex->u = ex2.u + ex->fld.off; + return 1; + } else { + assert(ex2.t == ESSYMREF); + ex->t = ESSYMREF; + vlong off = (vlong) sub->ssym.off + ex->fld.off; + if ((int) off != off) return 0; + ex->ssym = ex2.ssym; + ex->ssym.off = off; + return mode >= EVSTATICINI; + } + } + } + return 0; + } + if (!eval(sub, mode)) return 0; + switch (ex->t) { + case ECAST: + if (ex->ty.t == TYPTR && sub->t == ENUMLIT) { + ex->t = ENUMLIT; + ex->u = sub->u; + return 1; + } else if (isstaticlval(sub, mode) + && (ex->ty.t == TYPTR || (isint(ex->ty) && typesize(ex->ty) == targ_primsizes[TYPTR]))) { + /* ptr -> int */ + if (sub->t == ESTRLIT || sub->t == EINIT) + lit2ssym(sub); + sub->span = ex->span, sub->ty = ex->ty; + *ex = *sub; + return 1; + } + break; + case EPLUS: + break; + case ENEG: + if (sub->t != ENUMLIT) return 0; + if (isint(sub->ty)) sub->u = -sub->u; + else assert(isflt(sub->ty)), sub->f = -sub->f; + break; + case ECOMPL: + if (sub->t != ENUMLIT) return 0; + assert(isint(sub->ty)); + sub->u = ~sub->u; + break; + case ELOGNOT: + sub->u = !truthy(sub); + sub->t = ENUMLIT; + break; + default: + return 0; + } + if (sub->t != ENUMLIT || !numcast(ex->ty, ex, sub)) return 0; + return 1; +} + +static bool +binop(struct expr *ex, enum evalmode mode) +{ + struct expr *a = &ex->sub[0], *b = &ex->sub[1]; + if (!eval(a, mode)) return 0; + union type opty; + if (in_range(ex->t, EADD, ESHR)) + opty = ex->ty; + else /* compare, logical, set (result type != operation type) */ + opty = cvtarith(a->ty, b->ty); + if (isstaticlval(a, mode)) { + if ((ex->t == EADD || ex->t == ESUB) && eval(b, mode) && b->t == ENUMLIT) { + assert(isint(b->ty)); + assert(in_range(a->ty.t, TYPTR, TYARRAY)); + if (a->t == ESTRLIT) { + lit2ssym(a); + } else assert(a->t == ESSYMREF); + vlong addend = b->i * typesize(typechild(a->ty)), + off = a->ssym.off + (uvlong) (ex->t == EADD ? addend : -addend); + ex->t = ESSYMREF; + if ((int) off != off) return 0; + ex->ssym = a->ssym; + ex->ssym.off = off; + return 1; + } + return 0; + } + + enum { U = 0, S = 1<<8 , F = 1<<9 }; + int op = issigned(opty)<<8 | isflt(opty)<<9 | ex->t; + bool c; + if (ex->t != ELOGAND && ex->t != ELOGIOR) { + if (!numcast(opty, a, a)) return 0; + if (!eval(b, mode) || !numcast(opty, b, b)) return 0; + } + switch (op) { + case EADD|U: + case EADD|S: a->u += opty.t == TYPTR ? b->u * typesize(typechild(opty)) : b->u; break; + case EADD|F: a->f += b->f; break; + + case ESUB|U: + case ESUB|S: if (opty.t == TYPTR) { + assert(a->t == ENUMLIT && b->t == ENUMLIT); + assert(!isincomplete(typechild(ex->ty))); + a->u = (a->u - b->u) / typesize(typechild(ex->ty)); + } else a->u -= b->u; + break; + case ESUB|F: a->f -= b->f; break; + + case EMUL|U: + case EMUL|S: a->u *= b->u; break; + case EMUL|F: a->f *= b->f; break; + + case EDIV|U: if (!b->u) return 0; + a->u /= b->u; + break; + case EDIV|S: if (!b->i) return 0; + if (a->i == LLONG_MIN && b->i == -1) break; + a->i /= b->i; + break; + case EDIV|F: a->f /= b->f; break; + + case EREM|U: if (!b->u) return 0; + a->u %= b->u; + break; + case EREM|S: if (!b->i) return 0; + if (a->i == LLONG_MIN && b->i == -1) a->i = 0; + a->i %= b->i; + break; + + case EBAND|U: + case EBAND|S: a->u &= b->u; break; + + case EBIOR|U: + case EBIOR|S: a->u |= b->u; break; + + case EXOR|U: + case EXOR|S: a->u ^= b->u; break; + + case ESHL|S: if (a->i < 0) return 0; + case ESHL|U: if (b->u >= 8*targ_primsizes[opty.t]) return 0; + a->u <<= b->u; + break; + + case ESHR|U: if (b->u >= 8*targ_primsizes[opty.t]) return 0; + a->u >>= b->i; + break; + case ESHR|S: if (b->u >= 8*targ_primsizes[opty.t]) return 0; + a->i >>= b->i; + break; + + case EEQU|U: + case EEQU|S: a->u = a->u == b->u; break; + case EEQU|F: a->u = a->f == b->f; break; + + case ENEQ|U: + case ENEQ|S: a->u = a->u != b->u; break; + case ENEQ|F: a->u = a->f != b->f; break; + + case ELTH|U: a->u = a->u < b->u; break; + case ELTH|S: a->u = a->i < b->i; break; + case ELTH|F: a->u = a->f < b->f; break; + + case EGTH|U: a->u = a->u > b->u; break; + case EGTH|S: a->u = a->i > b->i; break; + case EGTH|F: a->u = a->f > b->f; break; + + case ELTE|U: a->u = a->u <= b->u; break; + case ELTE|S: a->u = a->i <= b->i; break; + case ELTE|F: a->u = a->f <= b->f; break; + + case EGTE|U: a->u = a->u >= b->u; break; + case EGTE|S: a->u = a->i >= b->i; break; + case EGTE|F: a->u = a->f >= b->f; break; + + case ELOGAND|U: + case ELOGAND|S: + case ELOGAND|F: + c = (op & F) ? a->f : a->u; + if (c) { + if (!eval(b, mode) || !numcast(opty, b, b)) return 0; + c = (op & F) ? b->f : b->u; + } + a->u = c; + break; + + case ELOGIOR|U: + case ELOGIOR|S: + case ELOGIOR|F: + c = op & F ? a->f : a->u; + if (!c) { + if (!eval(b, mode) || !numcast(opty, b, b)) return 0; + c = (op & F) ? b->f : b->u; + } + a->u = c; + break; + default: return 0; + } + + if (!in_range(ex->t, EADD, ESHR)) { + a->t = ENUMLIT; + a->ty = mktype(TYINT); + } + return numcast(ex->ty, ex, a); +} + +bool +eval(struct expr *ex, enum evalmode mode) +{ + switch (ex->t) { + case EGETF: goto Unop; + case ESEQ: + if (!eval(&ex->sub[0], mode)) return 0; + *ex = ex->sub[1]; + return eval(ex, mode); + case ECOND: + if (!eval(&ex->sub[0], mode)) return 0; + *ex = ex->sub[2-truthy(&ex->sub[0])]; + return eval(ex, mode); + case EINIT: + for (struct initval *v = ex->init->vals; v; v = v->next) { + if (!eval(&v->ex, mode)) return 0; + } + return 1; + case ENUMLIT: + if (mode <= EVINTCONST) return !isflt(ex->ty); + return 1; + case ESYM: + if (in_range(ex->ty.t, TYARRAY, TYFUNC) + && mode >= EVSTATICINI) { + struct expr ex2 = staticaddrof(ex, mode); + if (ex2.t) { + union type ty = ex->ty; + *ex = ex2; + ex->ty = ty; + return 1; + } + } + return 0; + case ESTRLIT: case ESSYMREF: + return mode >= EVSTATICINI; + default: + if (isunop(ex->t)) Unop: return unop(ex, mode); + if (isbinop(ex->t)) return binop(ex, mode); + } + return 0; +} + +/* vim:set ts=3 sw=3 expandtab: */ |