aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2026-03-14 17:27:37 +0100
committerlemon <lsof@mailbox.org>2026-03-14 17:46:54 +0100
commit42ce457bba06bd3607fc04700a919b6c395f22c3 (patch)
tree77a3f2230324248055a69a41d12b18471d56fe4f
parent1f3ebe69478f245f69cd6f77db946226557085d2 (diff)
c: static eval refactoring
Explicit node for static symbol (addresses). Should not break with edge cases like the previous ad-hoc approach. And some other bugfixes
-rw-r--r--c/c.c141
-rw-r--r--c/c.h46
-rw-r--r--c/eval.c459
-rw-r--r--io.c13
-rw-r--r--ir/ir.c8
-rw-r--r--ir/ir.h3
-rw-r--r--test/17-misc.c14
-rw-r--r--x86_64/isel.c21
8 files changed, 426 insertions, 279 deletions
diff --git a/c/c.c b/c/c.c
index c7a6eac..4a461b9 100644
--- a/c/c.c
+++ b/c/c.c
@@ -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 */
diff --git a/c/c.h b/c/c.h
index a5f702e..0214db5 100644
--- a/c/c.h
+++ b/c/c.h
@@ -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 {
diff --git a/c/eval.c b/c/eval.c
index 839ff7a..721401b 100644
--- a/c/eval.c
+++ b/c/eval.c
@@ -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;
}
diff --git a/io.c b/io.c
index f29e6d3..d33afb1 100644
--- a/io.c
+++ b/io.c
@@ -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;
diff --git a/ir/ir.c b/ir/ir.c
index bb75d4b..626311e 100644
--- a/ir/ir.c
+++ b/ir/ir.c
@@ -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) {
diff --git a/ir/ir.h b/ir/ir.h
index 39fa116..a4da8ad 100644
--- a/ir/ir.h
+++ b/ir/ir.h
@@ -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: