diff options
| author | 2026-04-18 16:59:16 +0200 | |
|---|---|---|
| committer | 2026-04-18 16:59:16 +0200 | |
| commit | 554fb8de87bb2e37715dbcc70efc27274b041014 (patch) | |
| tree | 8a9083b6fbcc21d46107465401be1150da45d46b | |
| parent | 7c25529f0a525ec4b4dcf40d847e2734c2349d5d (diff) | |
frontend: allow folding some non constant expressions as a GNU extension
And also permit zero size objects
| -rw-r--r-- | src/c.c | 43 | ||||
| -rw-r--r-- | src/c_eval.c | 40 | ||||
| -rw-r--r-- | src/obj.c | 2 | ||||
| -rw-r--r-- | src/u_endian.h | 10 | ||||
| -rw-r--r-- | test/22-decl.c | 7 |
5 files changed, 82 insertions, 20 deletions
@@ -496,6 +496,19 @@ relationalcheck(const Expr *a, const Expr *b) return 0; } +/* folding where a constant expression is required as an extension, with a warning */ +bool +eval2ext(Expr *ex) +{ + if (eval(ex, EVINTCONST)) return 1; + if (eval(ex, EVFOLD)) { + warn(&ex->span, "expression is not a integer constant expression; " + "folding it here is an extension"); + return 1; + } + return 0; +} + static bool isnullpo(const Expr *ex) /* match '0' or '(void *) 0' */ { @@ -1731,7 +1744,7 @@ designators(InitParser *ip, CComp *cm) if (expect(cm, ']', NULL)) joinspan(&span.ex, tk.span.ex); if (ip->sub->ty.t != TYARRAY) error(&ex.span, "array designator used with non-array type '%ty'", ip->sub->ty); - if (!eval(&ex, EVINTCONST)) + if (!eval2ext(&ex)) error(&ex.span, "array designator index is not an integer constant"); else if (issigned(ex.ty) && ex.i < 0) error(&ex.span, "negative array designator index"); @@ -1892,13 +1905,15 @@ initializer(CComp *cm, Type *ty, enum evalmode ev, bool globl, sec = Sdata; if (!nerror) { off = objnewdat(sym, sec, globl, siz = typesize(*ty), align = typealign(*ty)); - p = sec == Srodata ? objout.rodata.p : objout.data.p; - assert(ip->ddat.n <= siz); - memcpy(p + off, ip->ddat.p, ip->ddat.n); - memset(p + off + ip->ddat.n, 0, siz - ip->ddat.n); - for (InitReloc *rel = ip->drel; rel; rel = rel->link) { - objreloc(rel->sym, rel->flag, targ_64bit ? REL_ABS64 : REL_ABS32, - sec, off + rel->off, rel->addend); + if (siz > 0) { + p = sec == Srodata ? objout.rodata.p : objout.data.p; + assert(ip->ddat.n <= siz); + memcpy(p + off, ip->ddat.p, ip->ddat.n); + memset(p + off + ip->ddat.n, 0, siz - ip->ddat.n); + for (InitReloc *rel = ip->drel; rel; rel = rel->link) { + objreloc(rel->sym, rel->flag, targ_64bit ? REL_ABS64 : REL_ABS32, + sec, off + rel->off, rel->addend); + } } } vfree(&ip->ddat); @@ -2032,7 +2047,7 @@ buildagg(CComp *cm, enum typetag tt, internstr name, int id) error(&decl.span, "bit-field '%s' has non-integer type '%ty'", name, decl.ty); } else if (!isint(ex.ty)) { error(&ex.span, "integer constant expression has non-integer type '%ty'", decl.ty); - } else if (!eval(&ex, EVINTCONST)) { + } else if (!eval2ext(&ex)) { error(&ex.span, "cannot evaluate integer constant expression"); } else if (ex.i < 0) { error(&ex.span, "bit-field '%s' has negative width '%ld'", name, ex.i); @@ -2163,7 +2178,7 @@ buildenum(CComp *cm, internstr name, const Span *span, int id) expect(cm, TKIDENT, NULL); if (match(cm, NULL, '=') || (peek(cm, NULL) == TKNUMLIT && !expect(cm, '=', NULL))) { Expr ex = expr(cm); - if (eval(&ex, EVINTCONST)) { + if (eval2ext(&ex)) { iota = ex.i; if (ex.ty.t != ty.t) inttyminmax(&tymin, &tymax, ex.ty.t); @@ -2783,7 +2798,7 @@ declarator(DeclState *st, CComp *cm, Span span0) { Expr *ex = &l->count; if (!ex->t) { /* ['*'] */ if (l->prev != &list) error(&l->span, "[*] array declarator is not allowed here"); - } else if (!eval(ex, EVINTCONST)) { + } else if (!eval2ext(ex)) { error(&ex->span, "array length is not an integer constant"); } else if (issigned(ex->ty) && ex->i < 0) { error(&ex->span, "array length is negative"); @@ -2852,7 +2867,7 @@ pstaticassert(CComp *cm, Span *span) joinspan(&span->ex, tk.span.ex); if (!msg.t && ccopt.cstd == STDC11) warn(span, "static assert without message is a C23 extension"); - if (!eval(&ex, EVINTCONST)) { + if (!eval2ext(&ex)) { error(&ex.span, "static assert expression is not an integer constant"); } else if (iszero(ex)) { if (msg.t) @@ -4401,12 +4416,12 @@ stmt(CComp *cm, Function *fn) /* case <expr> ':' */ if (!cm->switchstmt) error(&tk.span, "'case' outside of switch statement"); ex = constantexpr(cm); - if (!eval(&ex, EVINTCONST)) + if (!eval2ext(&ex)) error(&ex.span, "not an integer constant expression"); else if (cm->switchstmt && ex.ty.bits != cm->switchstmt->condtype.bits) { Expr tmp = ex; ex = mkexpr(ECAST, ex.span, cm->switchstmt->condtype, .sub = &tmp); - bool ok = eval(&ex, EVINTCONST); + bool ok = eval2ext(&ex); assert(ok && "cast const int?"); if (ex.i != tmp.i) warn(&ex.span, "overflow converting case value to switch condition type"); diff --git a/src/c_eval.c b/src/c_eval.c index 8518b91..8b68a4b 100644 --- a/src/c_eval.c +++ b/src/c_eval.c @@ -1,5 +1,7 @@ #include "c.h" #include "ir.h" +#include "obj.h" +#include "u_endian.h" #include <limits.h> static int @@ -397,6 +399,42 @@ binop(Expr *ex, enum evalmode mode) return numcast(ex->ty, ex, a); } +static bool +tryreadconst(Expr *ex) +{ + assert(ex->t == ESYM); + const struct Decl *decl = &declsbuf.p[ex->decl]; + assert(decl->ty.bits == ex->ty.bits && isarith(ex->ty)); + uint off; + const uchar *dat; + switch (objhassym(decl->sym, &off)) { + default: return 0; + case Stext: + dat = objout.textbegin + off; + break; + case Srodata: + dat = objout.rodata.p + off; + break; + } + int siz = targ_primsizes[scalartypet(decl->ty)]; + ex->t = ENUMLIT; + enum { F = 1 << 6, S = 1 << 5 }; + switch (isflt(ex->ty)*F | issigned(ex->ty)*S | siz) { + case S|1: ex->i = *(schar *)dat; break; + case 1: ex->u = *(uchar *)dat; break; + case S|2: ex->i = rd16targ(dat); break; + case 2: ex->u = rd16targ(dat); break; + case S|4: ex->i = rd32targ(dat); break; + case 4: ex->u = rd32targ(dat); break; + case S|8: + case 8: ex->u = rd64targ(dat); break; + case F|4: ex->f = rdf32targ(dat); break; + case F|8: ex->f = rdf64targ(dat); break; + default: assert(!"nyi"); + } + return 1; +} + bool eval(Expr *ex, enum evalmode mode) { @@ -433,6 +471,8 @@ eval(Expr *ex, enum evalmode mode) ex->ty = ty; return 1; } + } else if (mode == EVFOLD && isarith(ex->ty)) { + return tryreadconst(ex); } return 0; case ESTRLIT: case ESSYMREF: @@ -50,7 +50,7 @@ objnewdat(internstr name, enum section sec, bool globl, uint siz, uint align) { ObjFile *o = &objout; uint off; - assert(siz && align && ispo2(align)); + assert(align && ispo2(align)); switch (sec) { default: assert(0); case Stext: diff --git a/src/u_endian.h b/src/u_endian.h index a020da8..8f931f9 100644 --- a/src/u_endian.h +++ b/src/u_endian.h @@ -108,7 +108,7 @@ wr64be(uchar *p, u64int x) /** target-endian memory read/write **/ static inline ushort -rd16targ(uchar *p) +rd16targ(const uchar *p) { ushort x; memcpy(&x, p, sizeof x); @@ -117,7 +117,7 @@ rd16targ(uchar *p) } static inline uint -rd32targ(uchar *p) +rd32targ(const uchar *p) { uint x; memcpy(&x, p, sizeof x); @@ -126,7 +126,7 @@ rd32targ(uchar *p) } static inline u64int -rd64targ(uchar *p) +rd64targ(const uchar *p) { u64int x; memcpy(&x, p, sizeof x); @@ -135,14 +135,14 @@ rd64targ(uchar *p) } static inline float -rdf32targ(uchar *p) +rdf32targ(const uchar *p) { union { uint i; float f; } u = { rd32targ(p) }; return u.f; } static inline double -rdf64targ(uchar *p) +rdf64targ(const uchar *p) { union { u64int i; double f; } u = { rd64targ(p) }; return u.f; diff --git a/test/22-decl.c b/test/22-decl.c index a14b776..b785021 100644 --- a/test/22-decl.c +++ b/test/22-decl.c @@ -14,6 +14,13 @@ extern unsigned local; typedef struct foo { char r; } foo_t; static const foo_t T = ((foo_t) { 3 }); +static const int X = 4; +struct { + int k : X; /* EXTENSION */ +}; + +foo_t empty[] = {}; /* EXTENSION */ + int main() { } |