diff options
| -rw-r--r-- | c/c.c | 141 | ||||
| -rw-r--r-- | c/c.h | 46 | ||||
| -rw-r--r-- | c/eval.c | 459 | ||||
| -rw-r--r-- | io.c | 13 | ||||
| -rw-r--r-- | ir/ir.c | 8 | ||||
| -rw-r--r-- | ir/ir.h | 3 | ||||
| -rw-r--r-- | test/17-misc.c | 14 | ||||
| -rw-r--r-- | x86_64/isel.c | 21 |
8 files changed, 426 insertions, 279 deletions
@@ -296,7 +296,6 @@ redeclarationok(const struct decl *old, const struct decl *new) static int putdecl(struct comp *cm, const struct decl *decl) { - assert(!decl->isbuiltin); for (struct env *env = cm->env; env; env = env->up) { struct decl *l; if (!env->up) { @@ -390,7 +389,7 @@ static bool islvalue(const struct expr *ex) { if (ex->t == EGETF) return islvalue(ex->sub); - return ex->t == ESYM || ex->t == EDEREF || ex->t == EINIT; + return ex->t == ESYM || ex->t == EDEREF || ex->t == EINIT || ex->t == ESTRLIT; } static union type /* 6.5.2.6 default argument promotions */ @@ -996,6 +995,8 @@ static struct expr initializer(struct comp *cm, union type *ty, enum evalmode ev static internstr istr__func__, istr_main, istr_memset; +static internstr mkhiddensym(const char *fnname, const char *name, int id); + /* parse an expression with the given operator precedence */ /* param ident is a kludge to support block labels without backtracking or extra lookahead * see stmt() */ @@ -1119,6 +1120,14 @@ Unary: } else if (decl->isenum) { ex = mkexpr(ENUMLIT, tk.span, decl->ty, .i = decl->value); } else Sym: { + if (decl->name == istr__func__ && decl->isbuiltin) { /* lazy __func__ */ + internstr fnname = decl->sym; + decl->isbuiltin = 0; + decl->sym = mkhiddensym(&fnname->c, "__func__", 1); + uint off = objnewdat(decl->sym, objout.code ? Stext : Srodata, 0, typesize(decl->ty), typealign(decl->ty)); + uchar *p = objout.code ? objout.textbegin + off : objout.rodata.p + off; + memcpy(p, fnname, typearrlen(decl->ty)-1); + } ex = mkexpr(ESYM, tk.span, decl->ty, .qual = decl->qual, .decl = decl - declsbuf.p); } break; } @@ -1223,7 +1232,10 @@ Unary: ty = unops[nunop].ty; if (!castcheck(ty, &ex)) error(&span, "cannot cast value of type '%ty' to '%ty'", ex.ty, ty); - ex = mkexpr(ECAST, span, ty, .sub = exprdup(cm, &ex)); + if (ex.t == ENUMLIT && isint(ex.ty) && ty.t == TYPTR) + ex.ty = ty; + else + ex = mkexpr(ECAST, span, ty, .sub = exprdup(cm, &ex)); } } @@ -1410,38 +1422,16 @@ dumpini(struct initparser *ip) } #endif -static bool -globsym(union ref *psym, const struct expr *ex) -{ - if (ex->t == EINIT || ex->t == ESTRLIT || (ex->t == ESYM && (declsbuf.p[ex->decl].scls & (SCSTATIC | SCEXTERN)))) { - *psym = expraddr(NULL, ex); - return 1; - } - return 0; -} - static vlong /* -> returns addend */ -expr2reloc(union ref *psym, const struct expr *ex) +expr2reloc(internstr *psym, const struct expr *ex) { - if (ex->t == EADDROF && globsym(psym, ex->sub)) { - return 0; - } else if ((isptrcvt(ex->ty) || (isint(ex->ty) && typesize(ex->ty) == targ_primsizes[TYPTR])) - && globsym(psym, ex)) { + if (ex->t == ESSYMREF) { + *psym = ex->ssym.sym; + return ex->ssym.off; + } else if (ex->t == ESTRLIT || ex->t == EINIT) { + if (ex->t == ESTRLIT) assert(ex->ty.t == TYARRAY); + *psym = xcon2sym(expraddr(NULL, ex).i); return 0; - } else if (ex->t == EADDROF && ex->sub->t == EGETF && globsym(psym, ex->sub->sub)) { - return ex->sub->fld.off; - } else if (ex->t == EGETF && ex->ty.t == TYARRAY && globsym(psym, ex->sub)) { - return ex->sub->fld.off; - } else if (globsym(psym, ex) && in_range(ex->ty.t, TYARRAY, TYFUNC)) { - return 0; - } else if (ex->t == ESUB && globsym(psym, &ex->sub[0]) && isint(ex->sub[1].ty) && ex->sub[1].t == ENUMLIT) { - return -ex->sub[1].i * typesize(typechild(ex->sub[0].ty)); - } else if (ex->t == EADD) { - int swp = ex->sub[0].t == ENUMLIT; - struct expr *a = &ex->sub[swp], *b = &ex->sub[swp ^ 1]; - if (isint(b->ty) && b->t == ENUMLIT) { - return expr2reloc(psym, a) + b->i * typesize(typechild(a->ty)); - } } fatal(&ex->span, "internal bug: non static reloc?"); } @@ -1509,17 +1499,16 @@ iniwrite(struct comp *cm, struct initparser *ip, uint off, uint bitsiz, uint bit /* XXX endian for wide strs */ memcpy(p, ex->s.p, n); } else { - union ref sym; + internstr sym; vlong addend = expr2reloc(&sym, ex); - assert(sym.t == RXCON); if (!ip->dyn) { assert(ip->sec != Srodata || rodatarelocok()); - objreloc(xcon2sym(sym.i), targ_64bit ? REL_ABS64 : REL_ABS32, + objreloc(sym, targ_64bit ? REL_ABS64 : REL_ABS32, ip->sec, ip->off + off, addend); } else { struct dreloc *rel = alloc(ip->arena, sizeof *rel, 0); rel->link = ip->drel; - rel->sym = xcon2sym(sym.i); + rel->sym = sym; rel->off = off; rel->addend = addend; ip->drel = rel; @@ -1917,6 +1906,64 @@ initializer(struct comp *cm, union type *ty, enum evalmode ev, bool globl, } } +/* debugging */ +void +dumpexpr(const struct expr *ex, bool prity) +{ + static const char *name[] = { + [EXXX] = "xxx", [ENUMLIT] = "numlit", [ESTRLIT] = "strlit", + [ESSYMREF] = "ssymref", [ESYM] = "sym", [EVAARG] = "vaarg", + [EINIT] = "init", [EGETF] = "getf", [ECALL] = "call", + [ECOND] = "cond", [EPLUS] = "plus", [ENEG] = "neg", + [ECOMPL] = "compl", [ELOGNOT] = "lognot", [EDEREF] = "deref", + [EADDROF] = "addrof", [ECAST] = "cast", [EPREINC] = "preinc", + [EPOSTINC] = "postinc", [EPREDEC] = "predec", [EPOSTDEC] = "postdec", + [EADD] = "add", [ESUB] = "sub", [EMUL] = "mul", + [EDIV] = "div", [EREM] = "rem", [EBAND] = "band", + [EBIOR] = "bior", [EXOR] = "xor", [ESHL] = "shl", + [ESHR] = "shr", [ELOGAND] = "logand", [ELOGIOR] = "logior", + [EEQU] = "equ", [ENEQ] = "neq", [ELTH] = "lth", + [EGTH] = "gth", [ELTE] = "lte", [EGTE] = "gte", + [ESET] = "set", [ESETADD] = "setadd", [ESETSUB] = "setsub", + [ESETMUL] = "setmul", [ESETDIV] = "setdiv", [ESETREM] = "setrem", + [ESETAND] = "setand", [ESETIOR] = "setior", [ESETXOR] = "setxor", + [ESETSHL] = "setshl", [ESETSHR] = "setshr", [ESEQ] = "seq", + }; + ioputc(&bstderr, '('); + efmt("%s ", name[ex->t]); + if (ex->ty.t && (prity || ex->t == EVAARG || ex->t == ECAST)) + efmt("<%ty> ", ex->ty); + int nsub = 0; + switch (ex->t) { + case ENUMLIT: if (!isflt(ex->ty)) efmt(isunsigned(ex->ty) ? "%lu" : "%ld", ex->u); + else efmt("%f", ex->f); + break; + case ESTRLIT: efmt("%'S", ex->s.p, ex->s.n); break; /* XXX widestr */ + case ESYM: efmt("%y", declsbuf.p[ex->decl].name); break; + case ESSYMREF: efmt("%y%+d", ex->ssym.sym, ex->ssym.off); + if (ex->ssym.func) efmt(" @func"); + if (ex->ssym.local) efmt(" @local"); + break; + case EVAARG: dumpexpr(ex->sub, prity); break; + case EGETF: dumpexpr(ex->sub, prity); efmt(" #+%u", ex->fld.off); + if (ex->fld.bitsiz) efmt("[%d:%d]", ex->fld.bitoff, ex->fld.bitsiz); + break; + case ECALL: nsub = ex->narg+1; goto Sub; + case ECOND: nsub = ex->narg+1; goto Sub; + default: + if (in_range(ex->t, EPLUS, EPOSTDEC)) nsub = 1; + else nsub = 2; + Sub: + for (int i = 0; i < nsub; ++i) { + if (i) ioputc(&bstderr, ' '); + dumpexpr(&ex->sub[i], prity); + } + break; + case EINIT: assert(!"nyi"); + } + ioputc(&bstderr, ')'); +} + /*****************/ /* Decls Parsing */ /*****************/ @@ -2939,21 +2986,18 @@ expraddr(struct function *fn, const struct expr *ex) assert(decl->id >= 0); return mkref(RTMP, decl->id); case SCEXTERN: case SCNONE: case SCSTATIC: - if (!decl->sym) { /* lazy __func__ */ - assert(decl->name == istr__func__); - decl->sym = mkhiddensym(&fn->name->c, &intern("__func__")->c, 1); - uint off = objnewdat(decl->sym, objout.code ? Stext : Srodata, 0, typesize(decl->ty), typealign(decl->ty)); - uchar *p = objout.code ? objout.textbegin + off : objout.rodata.p + off; - memcpy(p, fn->name, typearrlen(decl->ty)-1); - } return mksymref(decl->sym, (SFUNC & -(decl->ty.t == TYFUNC)) | (SLOCAL & -(decl->scls == SCSTATIC || decl->isdef))); default: assert(0); } break; + case ESSYMREF: + return irbinop(fn, Oadd, KPTR, + mksymref(ex->ssym.sym, (SFUNC & -ex->ssym.func) | (SLOCAL & -ex->ssym.local)), + mkintcon(KPTR, ex->ssym.off)); case ESTRLIT: /* XXX endian for wide strs */ - return mkdatref(NULL, ex->ty, typesize(ex->ty), typealign(ex->ty), ex->s.p, ex->s.n * typesize(typechild(ex->ty)), /*deref*/0); + return mkdatref(NULL, ex->ty, typesize(ex->ty), typealign(ex->ty), ex->s.p, ex->s.n * typesize(typechild(ex->ty)), /*deref*/0, fn != NULL); case EDEREF: return exprvalue(fn, ex->sub); case EGETF: @@ -3489,9 +3533,7 @@ knowntruthy(bool *t, struct expr *ex) case ENUMLIT: *t = isflt(ex->ty) ? ex->f != 0.0 : ex->u != 0; break; - case ESYM: - assert(isptrcvt(ex->ty)); - case ESTRLIT: + case ESTRLIT: case ESSYMREF: /* string literals & symbol addresses are always truthy */ *t = 1; break; @@ -3529,6 +3571,8 @@ compileexpr(struct function *fn, const struct expr *ex, bool discard) case ESYM: if (discard && !(ex->qual & QVOLATILE)) return NOREF; return genload(fn, ex->ty, expraddr(fn, ex), ex->qual & QVOLATILE); + case ESSYMREF: + return expraddr(fn, ex); case EVAARG: return builtin_va_arg_comp(fn, ex, discard); case EGETF: @@ -4531,10 +4575,11 @@ function(struct comp *cm, struct function *fn, internstr *pnames, const struct s } } - /* put __func__, though its data is generated lazily in expraddr() */ + /* put __func__, though its data is generated lazily the first time it is encountered */ putdecl(cm, &(struct decl) { .ty = mkarrtype(mktype(TYCHAR), QCONST, strlen(&fn->name->c) + 1), .qual = QCONST, .name = istr__func__, .scls = SCSTATIC, .span = (peek(cm, &tk), tk.span), + .isbuiltin = 1, .sym = fn->name, }); /* end prologue */ @@ -6,7 +6,7 @@ /*************/ enum exprkind { - EXXX, ENUMLIT, ESTRLIT, ESYM, EVAARG, EINIT, EGETF, ECALL, ECOND, + EXXX, ENUMLIT, ESTRLIT, ESYM, ESSYMREF, EVAARG, EINIT, EGETF, ECALL, ECOND, /* unary */ EPLUS, ENEG, ECOMPL, ELOGNOT, EDEREF, EADDROF, ECAST, EPREINC, EPOSTINC, EPREDEC, EPOSTDEC, @@ -29,25 +29,30 @@ struct expr { union type ty; struct span span; union { - struct { - struct expr *sub; /* child(ren) */ - struct exgetfld { - ushort off; - uchar bitsiz, bitoff; - } fld; /* EGETF */ - }; - uvlong u; vlong i; double f; /* ENUMLIT */ - struct { - union { - uchar *p; - ushort *w16; - uint *w32; - }; - uint n; - } s; /* ESTRLIT */ - int decl; /* ESYM, index into declsbuf */ - internstr implicitsym; /* ESYM (undeclared) */ - struct init *init; /* EINIT */ + struct { + struct expr *sub; /* child(ren) */ + struct exgetfld { + ushort off; + uchar bitsiz, bitoff; + } fld; /* EGETF */ + }; + uvlong u; vlong i; double f; /* ENUMLIT */ + struct { + union { + uchar *p; + ushort *w16; + uint *w32; + }; + uint n; + } s; /* ESTRLIT */ + int decl; /* ESYM, index into declsbuf */ + internstr implicitsym; /* ESYM (undeclared) */ + struct { + internstr sym; + int off; + bool func : 1, local : 1; + } ssym; /* ESSYMREF (static symbol addr + off) */ + struct init *init; /* EINIT */ }; }; @@ -110,6 +115,7 @@ bool assigncheck(union type t, const struct expr *src); union ref expraddr(struct function *, const struct expr *); union ref scalarcvt(struct function *, union type to, union type from, union ref); union ref compileexpr(struct function *, const struct expr *, bool discard); +void dumpexpr(const struct expr *, bool printtypes); /** builtin.c **/ struct builtin { @@ -1,4 +1,5 @@ #include "c.h" +#include "../ir/ir.h" static int targ2hosttype(enum typetag t) @@ -76,6 +77,64 @@ numcast(union type ty, struct expr *dst, const struct expr *src) 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) { @@ -86,231 +145,242 @@ unop(struct expr *ex, enum evalmode mode) 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; - 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; - } - if (ex->t == EADDROF) { + } else 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; + 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 (sub->t != ENUMLIT && !eval(sub, mode)) return 0; - if (sub->t != ENUMLIT) return 0; + if (!eval(sub, mode)) return 0; switch (ex->t) { case ECAST: - if (ex->ty.t == TYPTR) { + 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 (!isint(sub->ty)) return 0; + if (sub->t != ENUMLIT) return 0; + assert(isint(sub->ty)); 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; + sub->u = !truthy(sub); + sub->t = ENUMLIT; break; default: return 0; } - if (!numcast(ex->ty, ex, sub)) return 0; + if (sub->t != ENUMLIT || !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->ty.t < NTYPETAG && (declsbuf.p[ex->decl].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 (!isptrcvt(ex->ty)) return 0; - if (ex->t == EADDROF && (isglobsym(ex->sub) || (ex->sub->t == EGETF && isglobsym(ex->sub->sub)))) - return 1; - if (ex->t == EADDROF && ex->sub->t == EDEREF && isaddrconst(ex->sub->sub)) { - *ex = *ex->sub->sub; - return 1; - } - if (ex->t == EADDROF && ex->sub->t == EINIT) - return eval(ex->sub, EVSTATICINI); - if (ex->t == EGETF && ex->ty.t == TYARRAY && isglobsym(ex->sub)) - return 1; - if (isglobsym(ex) && in_range(ex->ty.t, TYARRAY, TYFUNC)) - return 1; - if (ex->t == ESUB) { - if (isaddrconst(&ex->sub[0]) && isint(ex->sub[1].ty) && eval(&ex->sub[1], EVSTATICINI)) { - assert(ex->sub[1].t == ENUMLIT); - if (eval(&ex->sub[0], EVSTATICINI) && ex->sub[0].t == ENUMLIT) { - /* handles (char *)10 - 5 */ - ex->u = ex->sub[0].u - ex->sub[1].u * typesize(typechild(ex->sub[0].ty)); - ex->t = ENUMLIT; - } - return 1; - } - } else if (ex->t == EADD) { - for (int swp = 0; swp < 2; ++swp) - if (isaddrconst(&ex->sub[swp]) && isint(ex->sub[swp^1].ty) && eval(&ex->sub[swp^1], EVSTATICINI)) { - assert(ex->sub[swp^1].t == ENUMLIT); - if (eval(&ex->sub[swp], EVSTATICINI) && ex->sub[swp].t == ENUMLIT) { - /* handles (char *)1234 + 5678 */ - ex->u = ex->sub[swp].u + ex->sub[swp^1].u * typesize(typechild(ex->sub[swp].ty)); - ex->t = ENUMLIT; - } - return 1; - } - } - return 0; -} - static bool binop(struct expr *ex, enum evalmode mode) { - union type oty; - bool flt; - bool sgn; - int t; + union type opty; struct expr *lhs = &ex->sub[0], *rhs = &ex->sub[1]; - if (ex->ty.t == TYPTR) mode = EVFOLD; if (!eval(lhs, mode)) return 0; if (in_range(ex->t, EADD, ESHR)) - oty = ex->ty; - else - oty = cvtarith(lhs->ty, rhs->ty); - flt = isflt(oty); - sgn = issigned(oty); + 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(oty, lhs, lhs)) return 0; - if (!eval(rhs, mode) || !numcast(oty, rhs, rhs)) return 0; + if (!numcast(opty, lhs, lhs)) return 0; + if (!eval(rhs, mode) || !numcast(opty, rhs, rhs)) return 0; } - 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 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; - case ELOGAND: if (flt) t = lhs->f; else t = lhs->u; - if (t) { - if (!eval(rhs, mode) || !numcast(oty, rhs, rhs)) return 0; - if (flt) t = t && lhs->f; - else t = t && lhs->u; - } - lhs->u = t; + 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; + 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; + 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 ELOGIOR: if (flt) t = lhs->f; else t = lhs->u; - if (!t) { - if (!eval(rhs, mode) || !numcast(oty, rhs, rhs)) return 0; - if (flt) t = t || lhs->f; - else t = t || lhs->u; - } - lhs->u = t; + 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; -#undef ef } if (!in_range(ex->t, EADD, ESHR)) { @@ -323,40 +393,41 @@ binop(struct expr *ex, enum evalmode mode) 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 && (isptrcvt(ex->ty) || (isint(ex->ty) && typesize(ex->ty) == targ_primsizes[TYPTR])) - && isaddrconst(ex)) { - struct expr *e = ex; - while (e->t == ECAST) e = e->sub; - if (e != ex) { - if (e->ty.t != TYARRAY) - 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) { + 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); - } - if (ex->t == ECOND) { - if (!eval(&ex->sub[0], mode) || ex->sub[0].t != ENUMLIT) return 0; - *ex = ex->sub[!ex->sub[0].u + 1]; + case ECOND: + if (!eval(&ex->sub[0], mode)) return 0; + *ex = ex->sub[2-truthy(&ex->sub[0])]; return eval(ex, mode); - } - if (ex->t == EINIT) { + 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; } @@ -300,7 +300,7 @@ putdouble(struct wbuf *buf, double x) int vbfmt(struct wbuf *out, const char *fmt, va_list ap) { - bool quote, umod, lmod, zmod, lower; + bool quote, umod, lmod, zmod, lower, possign; int base; vlong i; int pad, prec, q; @@ -326,6 +326,7 @@ vbfmt(struct wbuf *out, const char *fmt, va_list ap) continue; } fmt += quote = *fmt == '\''; + fmt += possign = *fmt == '+'; pad = 0; if (aisdigit(*fmt)) { /* left pad */ for (; aisdigit(*fmt); ++fmt) @@ -421,6 +422,12 @@ vbfmt(struct wbuf *out, const char *fmt, va_list ap) : zmod && sizeof(&i-&i) > sizeof(int) ? va_arg(ap, vlong) : (vlong)va_arg(ap, int); tmp2.len = 0; + if (!umod && i < 0) { + n += bputc(buf, '-'); + i = -(uvlong)i; + } else if (possign) { + n += bputc(buf, '+'); + } if (quote) { switch (base) { case 2: n += bwriteS(buf, "0b"); break; @@ -428,10 +435,6 @@ vbfmt(struct wbuf *out, const char *fmt, va_list ap) case 16: n += bwriteS(buf, "0x"); break; } } - if (!umod && i < 0) { - n += bputc(buf, '-'); - i = -(uvlong)i; - } n += putuint(prec > 0 ? &tmp2 : buf, i, base, lower); if (prec > 0) { int fil = prec - tmp2.len; @@ -159,10 +159,12 @@ mksymref(internstr s, enum symflags symflags) } union ref -mkdatref(internstr name, union type ctype, uint siz, uint align, const void *bytes, uint n, bool deref) +mkdatref(internstr name, union type ctype, uint siz, uint align, + const void *bytes, uint n, bool deref, bool funclocal) { - struct irdat dat = { .ctype = ctype, .align = align, .siz = siz, .name = name }; - dat.section = objout.code && align >= 4 && align <= targ_primsizes[TYPTR] && siz <= 16 ? Stext : Srodata; + struct irdat dat = { .ctype = ctype, .align = align, .siz = siz, .name = name, .section = Srodata }; + if (funclocal && objout.code && align >= 4 && align <= targ_primsizes[TYPTR] && siz <= 16) + dat.section = Stext; assert(n <= siz && siz && align); if (!name) { @@ -265,7 +265,8 @@ union ref mkfltcon(enum irclass, double); #define intconval(r) ((r).t == RICON ? (r).i : contab.p[(r).i].i) #define fltconval(r) ((r).t == RICON ? (r).i : contab.p[(r).i].f) union ref mksymref(internstr, enum symflags); -union ref mkdatref(internstr name, union type ctype, uint siz, uint align, const void *, uint n, bool deref); +union ref mkdatref(internstr sym, union type ctype, uint siz, uint align, + const void *, uint n, bool deref, bool funclocal); internstr xcon2sym(int ref); struct instr mkalloca(uint siz, uint align); union ref mkcallarg(union irtype ret, uint narg, int vararg); diff --git a/test/17-misc.c b/test/17-misc.c index 521b948..ffd2470 100644 --- a/test/17-misc.c +++ b/test/17-misc.c @@ -26,7 +26,21 @@ _Static_assert(__is_constexpr(5 &&(1<<3) || 0/0), ""); _Static_assert(!__is_constexpr(5/0), ""); _Static_assert(!__is_constexpr(fn1(0)), ""); +#include <stddef.h> +#include <stdint.h> +struct foo { int *a, b[2]; }; +static intptr_t offst[] = { + (long)((struct foo *)0)->b, + (long)&((struct foo *)0)->a, + (long)&((struct foo *)0)->b[1], + (intptr_t)("12" - 5) +}; + extern int printf(const char *, ...); +#include <assert.h> int main() { printf("%d\n", fn1(-77ull)); + assert(offst[0] == offsetof(struct foo, b)); + assert(offst[1] == offsetof(struct foo, a)); + assert(offst[2] == offsetof(struct foo, b[1])); } diff --git a/x86_64/isel.c b/x86_64/isel.c index ded1a52..4b4a099 100644 --- a/x86_64/isel.c +++ b/x86_64/isel.c @@ -94,7 +94,7 @@ Begin: wr32le(data, pun.i); ctype = mktype(TYFLOAT); } - *r = mkdatref(NULL, ctype, ksiz, /*align*/ksiz, data, ksiz, /*deref*/1); + *r = mkdatref(NULL, ctype, ksiz, /*align*/ksiz, data, ksiz, /*deref*/1 , /*funclocal*/1); } if (docopy) *r = inscopy(blk, curi, con->cls, *r); @@ -257,9 +257,13 @@ aadd(struct addr *out, struct block *blk, int *curi, union ref r, bool recurring if (n1 == 0) { *out = adr; } else if (n1 == 1 && n2 == 1) { - assert(!out->index.bits); - out->index = adr.index.bits ? adr.index : adr.base; - out->shift = adr.shift; + if (!out->index.bits) { + out->index = adr.index.bits ? adr.index : adr.base; + out->shift = adr.index.bits ? adr.shift : 0; + } else { + if (adr.index.bits && adr.shift) return 0; + out->base = adr.index.bits ? adr.index : adr.base; + } } else assert(n1 <= 2 && n2 == 0); out->disp = off; ins->skip = 1; @@ -350,9 +354,10 @@ arithfold(struct instr *ins) if (isnumcon(ins->l) && (!ins->r.t || isnumcon(ins->r))) { union ref r; bool ok = ins->r.t ? foldbinop(&r, ins->op, ins->cls, ins->l, ins->r) : foldunop(&r, ins->op, ins->cls, ins->l); - assert(ok && "fold?"); - *ins = mkinstr(Ocopy, insrescls(*ins), r); - return 1; + if (ok) { + *ins = mkinstr(Ocopy, insrescls(*ins), r); + return 1; + } } return 0; } @@ -474,7 +479,7 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi) static const uint sf[4] = {0x80000000,80000000,0x80000000,80000000}; ins->op = Oxor; ins->r = mkdatref(NULL, mktype(ins->cls == KF32 ? TYFLOAT : TYDOUBLE), /*siz*/16, - /*align*/16, ins->cls == KF32 ? (void *)sf : sd, /*siz*/16, /*deref*/1); + /*align*/16, ins->cls == KF32 ? (void *)sf : sd, /*siz*/16, /*deref*/1, /*funclocal*/1); } /* fallthru */ case Onot: |