aboutsummaryrefslogtreecommitdiffhomepage
path: root/c/eval.c
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2025-10-19 08:09:09 +0200
committerlemon <lsof@mailbox.org>2025-10-19 08:09:09 +0200
commitdea8fd171acb54b6d9685422d5e391fb55074008 (patch)
tree2c149892f35c5183c9b2a1da4ab437228dc432ef /c/eval.c
parent3437945692f2b87883a4f066473c9deed50f25f5 (diff)
Organize source files into directories
Diffstat (limited to 'c/eval.c')
-rw-r--r--c/eval.c316
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: */