#include "c.h" #include "../ir/ir.h" #include 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) { struct expr tmp; enum typetag td = targ2hosttype(scalartypet(ty)); enum typetag ts = targ2hosttype(scalartypet(src->ty)); vlong isrc; 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) { /* .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) { union type opty; struct expr *lhs = &ex->sub[0], *rhs = &ex->sub[1]; if (!eval(lhs, mode)) return 0; if (in_range(ex->t, EADD, ESHR)) opty = ex->ty; else /* compare, logical, set (result type != operation type) */ opty = cvtarith(lhs->ty, rhs->ty); if (isstaticlval(lhs, mode)) { if ((ex->t == EADD || ex->t == ESUB) && eval(rhs, mode) && rhs->t == ENUMLIT) { assert(isint(rhs->ty)); assert(in_range(lhs->ty.t, TYPTR, TYARRAY)); if (lhs->t == ESTRLIT) { lit2ssym(lhs); } else assert(lhs->t == ESSYMREF); vlong addend = rhs->i * typesize(typechild(lhs->ty)), off = lhs->ssym.off + (uvlong) (ex->t == EADD ? addend : -addend); ex->t = ESSYMREF; if ((int) off != off) return 0; ex->ssym = lhs->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 b; if (ex->t != ELOGAND && ex->t != ELOGIOR) { if (!numcast(opty, lhs, lhs)) return 0; if (!eval(rhs, mode) || !numcast(opty, rhs, rhs)) return 0; } switch (op) { case EADD|U: case EADD|S: lhs->u += opty.t == TYPTR ? rhs->u * typesize(typechild(opty)) : rhs->u; break; case EADD|F: lhs->f += rhs->f; break; case ESUB|U: case ESUB|S: if (opty.t == TYPTR) { assert(lhs->t == ENUMLIT && rhs->t == ENUMLIT); assert(!isincomplete(typechild(ex->ty))); lhs->u = (lhs->u - rhs->u) / typesize(typechild(ex->ty)); } else lhs->u -= rhs->u; break; case ESUB|F: lhs->f -= rhs->f; break; case EMUL|U: case EMUL|S: lhs->u *= rhs->u; break; case EMUL|F: lhs->f *= rhs->f; break; case EDIV|U: if (!rhs->u) return 0; lhs->u /= rhs->u; break; case EDIV|S: if (!rhs->i) return 0; if (lhs->i == LLONG_MIN && rhs->i == -1) break; lhs->i /= rhs->i; break; case EDIV|F: lhs->f /= rhs->f; break; case EREM|U: if (!rhs->u) return 0; lhs->u %= rhs->u; break; case EREM|S: if (!rhs->i) return 0; if (lhs->i == LLONG_MIN && rhs->i == -1) lhs->i = 0; lhs->i %= rhs->i; break; case EBAND|U: case EBAND|S: lhs->u &= rhs->u; break; case EBIOR|U: case EBIOR|S: lhs->u |= rhs->u; break; case EXOR|U: case EXOR|S: lhs->u ^= rhs->u; break; case ESHL|S: if (lhs->i < 0) return 0; case ESHL|U: if (rhs->u >= 8*targ_primsizes[opty.t]) return 0; lhs->u <<= rhs->u; break; case ESHR|U: if (rhs->u >= 8*targ_primsizes[opty.t]) return 0; lhs->u >>= rhs->i; break; case ESHR|S: if (rhs->u >= 8*targ_primsizes[opty.t]) return 0; lhs->i >>= rhs->i; break; case EEQU|U: case EEQU|S: lhs->u = lhs->u == rhs->u; break; case EEQU|F: lhs->u = lhs->f == rhs->f; break; case ENEQ|U: case ENEQ|S: lhs->u = lhs->u != rhs->u; break; case ENEQ|F: lhs->u = lhs->f != rhs->f; break; case ELTH|U: lhs->u = lhs->u < rhs->u; break; case ELTH|S: lhs->u = lhs->i < rhs->i; break; case ELTH|F: lhs->u = lhs->f < rhs->f; break; case EGTH|U: lhs->u = lhs->u > rhs->u; break; case EGTH|S: lhs->u = lhs->i > rhs->i; break; case EGTH|F: lhs->u = lhs->f > rhs->f; break; case ELTE|U: lhs->u = lhs->u <= rhs->u; break; case ELTE|S: lhs->u = lhs->i <= rhs->i; break; case ELTE|F: lhs->u = lhs->f <= rhs->f; break; case EGTE|U: lhs->u = lhs->u >= rhs->u; break; case EGTE|S: lhs->u = lhs->i >= rhs->i; break; case EGTE|F: lhs->u = lhs->f >= rhs->f; break; case ELOGAND|U: case ELOGAND|S: case ELOGAND|F: b = (op & F) ? lhs->f : lhs->u; if (b) { if (!eval(rhs, mode) || !numcast(opty, rhs, rhs)) return 0; b = (op & F) ? rhs->f : rhs->u; } lhs->u = b; break; case ELOGIOR|U: case ELOGIOR|S: case ELOGIOR|F: b = op & F ? lhs->f : lhs->u; if (!b) { if (!eval(rhs, mode) || !numcast(opty, rhs, rhs)) return 0; b = (op & F) ? lhs->f : lhs->u; } lhs->u = b; break; default: return 0; } if (!in_range(ex->t, EADD, ESHR)) { lhs->t = ENUMLIT; lhs->ty = mktype(TYINT); } return numcast(ex->ty, ex, lhs); } 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: */