aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2026-04-18 16:59:16 +0200
committerlemon <lsof@mailbox.org>2026-04-18 16:59:16 +0200
commit554fb8de87bb2e37715dbcc70efc27274b041014 (patch)
tree8a9083b6fbcc21d46107465401be1150da45d46b /src
parent7c25529f0a525ec4b4dcf40d847e2734c2349d5d (diff)
frontend: allow folding some non constant expressions as a GNU extension
And also permit zero size objects
Diffstat (limited to 'src')
-rw-r--r--src/c.c43
-rw-r--r--src/c_eval.c40
-rw-r--r--src/obj.c2
-rw-r--r--src/u_endian.h10
4 files changed, 75 insertions, 20 deletions
diff --git a/src/c.c b/src/c.c
index 0aff669..00721c2 100644
--- a/src/c.c
+++ b/src/c.c
@@ -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:
diff --git a/src/obj.c b/src/obj.c
index 95371c3..67cb9af 100644
--- a/src/obj.c
+++ b/src/obj.c
@@ -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;