aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2023-06-29 09:59:30 +0200
committerlemon <lsof@mailbox.org>2023-06-29 09:59:30 +0200
commitf453b313f62ba42d748f00628be7b3750c797c86 (patch)
treee654029d425dee2adf30c0fa2adba31d0266db1c
parent3b96204593b9812674126bad8de14419009682c8 (diff)
add initializers (only static for initialier list rn)
and other fixes
-rw-r--r--amd64/emit.c85
-rw-r--r--amd64/isel.c40
-rw-r--r--c.c623
-rw-r--r--c.h34
-rw-r--r--elf.c79
-rw-r--r--endian.h14
-rw-r--r--eval.c50
-rw-r--r--ir.c47
-rw-r--r--ir.h16
-rw-r--r--irdump.c56
-rw-r--r--mem.c15
-rw-r--r--obj.c44
-rw-r--r--obj.h8
-rw-r--r--test/goto.c25
-rw-r--r--test/init.c65
-rw-r--r--type.c2
16 files changed, 948 insertions, 255 deletions
diff --git a/amd64/emit.c b/amd64/emit.c
index 70386be..3ccf595 100644
--- a/amd64/emit.c
+++ b/amd64/emit.c
@@ -6,18 +6,24 @@
*
* Can be a register, a 32-bit immediate,
* a memory reference [base + index * scale + disp],
- * or a RIP-relative reference to some symbol
+ * or a relocatable reference to some symbol plus a displacement and maybe index*scale
*/
-enum operkind { ONONE, OREG, OIMM, OMEM, OCONR };
-enum { NOBASE = 99, NOINDEX = 99 };
+enum operkind { ONONE, OREG, OIMM, OMEM, OSYM };
+enum { NOBASE = 63, NOINDEX = 63 };
static struct oper {
uchar t;
- struct { uchar shift, index, base; }; /* OMEM */
+ union {
+ struct { uchar base; }; /* OMEM */
+ struct { uchar cindex : 6, cshift : 2; }; /* OSYM */
+ };
+ union {
+ struct { uchar index, shift; }; /* OMEM */
+ ushort con; /* OSYM */
+ };
union {
uchar reg; /* OREG */
- int disp; /* OMEM */
+ int disp; /* OMEM, OSYM */
int imm; /* OIMM */
- int con; /* OCONR, conht index*/
};
} ioper[MAXINSTR];
#define mkoper(t, ...) ((struct oper){(t), __VA_ARGS__})
@@ -36,7 +42,7 @@ ref2oper(union ref r)
if (conht[r.i].cls == KI4)
return mkoper(OIMM, .imm = conht[r.i].i);
else if (!conht[r.i].cls)
- return mkoper(OCONR, .con = r.i);
+ return mkoper(OSYM, .con = r.i, .cindex = NOINDEX);
assert(0);
case RADDR: return mkmemoper(r);
default: assert(0);
@@ -119,13 +125,24 @@ mkmemoper(union ref r)
addmemoper(&mem, mkoper(OIMM, .imm = addr->disp));
return mem;
}
+ if (isaddrcon(addr->base)) {
+ return mkoper(OSYM, .con = addr->base.i,
+ .cindex = addr->index.bits ? mkregoper(addr->index).reg : NOINDEX,
+ .cshift = addr->shift,
+ .disp = addr->disp);
+ } else if (isaddrcon(addr->index)) {
+ assert(!addr->shift);
+ return mkoper(OSYM, .con = addr->index.i,
+ .cindex = addr->base.bits ? mkregoper(addr->base).reg : NOINDEX,
+ .disp = addr->disp);
+ }
return mkoper(OMEM, .base = addr->base.bits ? mkregoper(addr->base).reg : NOBASE,
.index = addr->index.bits ? mkregoper(addr->index).reg : NOINDEX,
.disp = addr->disp,
.shift = addr->shift);
} else if (r.t == RXCON) {
assert(!conht[r.i].cls);
- return mkoper(OCONR, .con = r.i);
+ return mkoper(OSYM, .con = r.i, .cindex = NOINDEX);
} else {
return mkoper(OMEM, .base = isregref(r) ? ref2oper(r).reg : NOBASE,
.index = NOINDEX,
@@ -200,8 +217,8 @@ opermatch(enum operpat pat, struct oper oper)
case PI16: return oper.t == OIMM && (short)oper.imm == oper.imm;
case PI32: return oper.t == OIMM;
case PU32: return oper.t == OIMM && oper.imm >= 0;
- case PMEM: return in_range(oper.t, OMEM, OCONR);
- case PSYM: return oper.t == OCONR;
+ case PMEM: return in_range(oper.t, OMEM, OSYM);
+ case PSYM: return oper.t == OSYM;
}
assert(0);
}
@@ -273,19 +290,33 @@ encode(uchar **pcode, const struct desc *tab, int ntab, enum irclass k, struct o
mem = dst;
reg = en->ext;
Mem:
- if (mem.t == OCONR) { /* RIP-relative addressing with relocation */
- mod = 0;
- mem.disp = mem.con;
- mem.base = RBP;
- sib = 0;
- if (rex) B(0x40 | rex);
- goto EmitMem;
+ if (mem.t == OMEM) {
+ if (mem.base != NOBASE) rex |= mem.base >> 3; /* REX.B */
+ if (mem.index != NOINDEX) rex |= mem.index >> 3 << 1; /* REX.X */
+ } else {
+ if (mem.cindex != NOINDEX) rex |= mem.cindex >> 3 << 1; /* REX.X */
}
- rex |= mem.base >> 3; /* REX.B */
- if (mem.t != EN_M)
+ if (en->operenc != EN_M)
rex |= (reg >> 3) << 2; /* REX.R */
if (rex) B(0x40 | rex);
else if (en->r8 && in_range(reg, RSP, RDI)) B(0x40);
+
+ if (mem.t == OSYM) {
+ /* XXX PIC */
+ D(opc, nopc);
+ if (mem.cindex == NOINDEX) {
+ /* %rip(var) */
+ B(/*mod 0*/ (reg & 7) << 3 | RBP);
+ objreloc(xcon2sym(mem.con), REL_PCREL32, Stext, *pcode - objout.textbegin, -4 + mem.disp);
+ } else {
+ /* var(,%reg,shift) */
+ B(/*mod 0*/ (reg & 7) << 3 | RSP);
+ B(mem.cshift << 6 | mem.cindex << 3 | RBP); /* SIB [index*s + disp32] */
+ objreloc(xcon2sym(mem.con), REL_ABS32S, Stext, *pcode - objout.textbegin, mem.disp);
+ }
+ I32(0);
+ break;
+ }
if (mem.index == NOINDEX && mem.shift == 0) sib = 0;
else sib = 1;
mod = !mem.disp ? 0 /* disp = 0 -> mod = 00 */
@@ -293,17 +324,12 @@ encode(uchar **pcode, const struct desc *tab, int ntab, enum irclass k, struct o
: 2; /* disp32 -> mod = 10 */
if (mod == 0 && (mem.base == RBP || mem.base == R13)) mod = 1;
if (mem.base == RSP || mem.base == R12) sib = 1;
- EmitMem:
D(opc, nopc);
B(mod << 6 | (reg & 7) << 3 | (sib ? 4 : mem.base));
if (sib)
B(mem.shift << 6 | (mem.index & 7) << 3 | (mem.base & 7));
if (mod == 1) B(mem.disp);
else if (mod == 2 || (mod == 0 && mem.base == RBP/*RIP-rel*/)) {
- if (mem.t == OCONR) {
- objreloc(xcon2sym(mem.con), REL_PCREL32, Stext, *pcode - objout.textbegin, -4);
- mem.disp = 0;
- }
I32(mem.disp);
}
if (en->operenc == EN_MI8) B(src.imm);
@@ -335,7 +361,7 @@ encode(uchar **pcode, const struct desc *tab, int ntab, enum irclass k, struct o
case EN_R32:
if (rex) B(0x40 | rex);
D(opc, nopc);
- assert(dst.t == OCONR);
+ assert(dst.t == OSYM);
objreloc(xcon2sym(dst.con), REL_PCREL32, Stext, *pcode - objout.textbegin, -4);
I32(0);
break;
@@ -457,6 +483,10 @@ DEFINSTR2(Xshl,
{4|8, PGPR, PI32, "\xC1", EN_RI8, .ext=4}, /* SHL r32/64, imm */
{4|8, PGPR, PRCX, "\xD3", EN_R, .ext=4}, /* SHL r32/64, CL */
)
+DEFINSTR2(Xcvtss2sd,
+ {-1, PFPR, PFPR, "\xF3\x0F\x5A", EN_RR}, /* CVTSS2SD xmm, xmm */
+ {-1, PFPR, PMEM, "\xF3\x0F\x5A", EN_RM}, /* CVTSS2SD xmm, xmm */
+)
DEFINSTR1(Xinc,
{4|8, PGPR, 0, "\xFF", EN_R, .ext=0} /* INC r32/64 */
)
@@ -641,8 +671,8 @@ gencopy(uchar **pcode, enum irclass cls, struct block *blk, int curi, struct ope
} else if (val.bits == ZEROREF.bits && dst.t == OREG && !flagslivep(blk, curi)) {
/* dst = 0 -> xor dst, dst; but only if it is ok to clobber flags */
Xxor(pcode, kisint(cls) ? KI4 : cls, dst, dst);
- } else if (val.t == RXCON && conht[val.i].isdat && !conht[val.i].deref) {
- Xlea(pcode, cls, dst, mkoper(OCONR, .con = val.i));
+ } else if (isaddrcon(val)) {
+ Xlea(pcode, cls, dst, mkoper(OSYM, .con = val.i, .cindex = NOINDEX));
} else {
struct oper src = mkimmdatregoper(val);
if (memcmp(&dst, &src, sizeof dst) != 0)
@@ -702,6 +732,7 @@ emitinstr(uchar **pcode, struct function *fn, struct block *blk, int curi, struc
case Oloadu4: src = mkmemoper(ins->l); Movzxl: Xmov(pcode, KI4, reg2oper(ins->reg-1), src); break;
case Oloadf4: case Oloadf8: Xmov(pcode, cls, reg2oper(ins->reg-1), mkmemoper(ins->l)); break;
case Oloadi8: Xmov(pcode, KI8, reg2oper(ins->reg-1), mkmemoper(ins->l)); break;
+ case Ocvtf4f8: Xcvtss2sd(pcode, KF4, reg2oper(ins->reg-1), mkdatregoper(ins->l)); break;
case Oadd:
dst = mkregoper(ins->l);
if (kisflt(cls)) {
diff --git a/amd64/isel.c b/amd64/isel.c
index d6fbaf5..911bb2e 100644
--- a/amd64/isel.c
+++ b/amd64/isel.c
@@ -139,11 +139,29 @@ ascale(struct addr *addr, union ref a, union ref b)
{
if (b.t != RICON) return 0;
if (addr->index.bits) return 0;
- if (a.t != RTMP && a.t != RREG) return 0;
- if ((unsigned)b.i > 3) return 0;
- addr->shift = b.i;
- addr->index = a;
- return 1;
+ if (a.t == RREG) {
+ Scaled:
+ if ((unsigned)b.i > 3) return 0;
+ addr->index = a;
+ addr->shift = b.i;
+ return 1;
+ } else if (a.t == RTMP) {
+ struct instr *ins = &instrtab[a.i];
+ /* factor out shifted immediate from 'shl {add %x, imm}, s' */
+ /* XXX maybe we shouldn't do this here because it should be done by a generic
+ * arithemetic optimization pass ? */
+ if (ins->op == Oadd && (ins->l.t == RREG || ins->l.t == RTMP) && isintcon(ins->r)) {
+ vlong a = ((vlong) addr->disp + intconval(ins->r)) << b.i;
+ if (a != (int) a) return 0;
+ addr->disp = a;
+ addr->index = ins->l;
+ addr->shift = b.i;
+ return 1;
+ } else {
+ goto Scaled;
+ }
+ }
+ return 0;
}
static bool
@@ -176,6 +194,11 @@ aadd(struct addr *addr, union ref r)
} else goto Ref;
} else if (isnumcon(r)) {
return acon(addr, r);
+ } else if (isaddrcon(r)) {
+ /* XXX PIC */
+ if (!addr->base.bits && !isaddrcon(addr->index)) addr->base = r;
+ else if (!addr->index.bits && !isaddrcon(addr->base)) addr->index = r;
+ else return 0;
} else if (r.t == RREG) {
/* temporaries are single assignment, but register aren't, so they can't be *
* safely hoisted into an address value, unless they have global lifetime */
@@ -194,7 +217,7 @@ fuseaddr(union ref *r)
struct addr addr = { 0 };
if (r->t == RADDR) return 1;
- if (r->t == RXCON && (!conht[r->i].cls && !conht[r->i].deref)) return 1;
+ if (isaddrcon(*r)) return 1;
if (r->t != RTMP) return 0;
if (!aadd(&addr, *r)) return 0;
@@ -206,8 +229,10 @@ fuseaddr(union ref *r)
static bool
addarg4addrp(union ref r)
{
- struct instr *ins = &instrtab[r.i];
+ struct instr *ins;
+ if (r.t == RXCON && !conht[r.i].cls && !conht[r.i].deref) return 1; /* sym or dat ref */
if (r.t != RTMP) return 0;
+ ins = &instrtab[r.i];
return ins->op == Oshl || (ins->op == Ocopy && ins->l.t == RADDR) || ins->op == Oadd;
}
@@ -332,6 +357,7 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi)
if (iscon(ins->l))
rswap(ins->l, ins->r);
case Oneg: case Onot:
+ case Ocvtf4f8: case Ocvtf8f4: case Ocvtf4s: case Ocvtf8s:
case Oexts1: case Oextu1: case Oexts2: case Oextu2: case Oexts4: case Oextu4:
ALU:
if (!(op == Oadd && kisint(ins->cls))) /* 3-address add is lea */
diff --git a/c.c b/c.c
index 7da19b4..c45a922 100644
--- a/c.c
+++ b/c.c
@@ -1,6 +1,8 @@
#include "c.h"
#include "lex.h"
#include "ir.h"
+#include "endian.h"
+#include "obj.h"
/** C compiler state **/
struct comp {
@@ -56,32 +58,9 @@ expectdie(struct comp *cm, enum toktag t, const char *s)
return tk;
}
-/**************************************/
-/* Data structures for C declarations */
-/**************************************/
-
-enum storageclass {
- SCNONE,
- SCTYPEDEF = 1<<0,
- SCEXTERN = 1<<1,
- SCSTATIC = 1<<2,
- SCTHREADLOCAL = 1<<3,
- SCAUTO = 1<<4,
- SCREGISTER = 1<<5,
-};
-
-struct decl {
- union type ty;
- uchar scls;
- uchar qual : 2;
- uchar isenum : 1;
- struct span span;
- const char *name;
- union {
- struct { ushort align; int id; };
- vlong value;
- };
-};
+/******************************************/
+/* Data structures for declaration parser */
+/******************************************/
enum declkind {
DTOPLEVEL,
@@ -308,7 +287,7 @@ deftagged(struct comp *cm, struct span *span, enum typetag tt, const char *name,
/* Expr Typechecking */
/*********************/
-#define iszero(ex) ((ex).t == ENUMLIT && (ex).u == 0)
+#define iszero(ex) ((ex).t == ENUMLIT && isint((ex).ty) && (ex).u == 0)
static bool
islvalue(const struct expr *ex)
@@ -1008,6 +987,504 @@ commaexpr(struct comp *cm)
return exprparse(cm, 1, NULL);
}
+/****************/
+/* Initializers */
+/****************/
+
+static uint
+nmemb(union type ty)
+{
+ if (ty.t == TYARRAY)
+ return typearrlen(ty) ? typearrlen(ty) : -1u;
+ if (isagg(ty))
+ return typedata[ty.dat].nmemb;
+ return 1;
+}
+
+static bool
+objectp(union type ty)
+{
+ return isagg(ty) || ty.t == TYARRAY;
+}
+
+static bool
+chararrayp(union type ty)
+{
+ return ty.t == TYARRAY && in_range(typechild(ty).t, TYCHAR, TYUCHAR);
+}
+
+static union type
+membertype(uint *off, union type ty, uint idx)
+{
+ if (!objectp(ty)) {
+ *off = 0;
+ return ty;
+ } else if (ty.t == TYARRAY) {
+ *off = typesize(typechild(ty)) * idx;
+ return typechild(ty);
+ } else if (idx < typedata[ty.dat].nmemb) {
+ *off = typedata[ty.dat].fld[idx].f.off;
+ return typedata[ty.dat].fld[idx].f.t;
+ }
+ *off = ~0u;
+ return mktype(0);
+}
+
+struct initparser {
+ struct initcur {
+ union type ty;
+ uint idx;
+ uint off;
+ short prev;
+ } buf[32], *cur, *sub;
+ struct arena **arena;
+ uint arrlen;
+ enum evalmode ev;
+ bool dyn; /* size is not known until parsing done (implicit array size) */
+ union {
+ struct init *init; /* for initializer with automatic storage */
+ struct { /* for static storage (dyn = 0) */
+ enum section sec;
+ uint off;
+ };
+ struct { /* for static storage (dyn = 1) */
+ vec_of(uchar) ddat;
+ struct dreloc {
+ struct dreloc *link;
+ const char *sym;
+ vlong addend;
+ uint off;
+ } *drel;
+ };
+ };
+};
+
+static void
+excesscheck(struct initparser *ip, const struct span *span)
+{
+ union type sub = ip->sub->ty;
+ uint n = nmemb(sub);
+ if (ip->sub->idx == n) {
+ if (sub.t == TYARRAY)
+ warn(span, "excess elements in array initializer for '%ty'", sub);
+ else if (sub.t == TYSTRUCT)
+ warn(span, "excess elements in initializer; '%ty' has %u member%s", sub, n, &"s"[n==1]);
+ else if (sub.t == TYUNION)
+ warn(span, "excess elements in union initializer");
+ else
+ warn(span, "excess elements in scalar initializer");
+ }
+}
+
+#if 1
+#define dumpini(_)
+#else
+/* debugging */
+static void
+dumpini(struct initparser *ip)
+{
+ efmt(">>>\n");
+ for (struct initcur *s = ip->buf; s < ip->sub+1; ++s) {
+ efmt(" ");
+ efmt("%d. [%ty, %u]", s- ip->buf, s->ty, s->idx);
+ if (s == ip->cur) efmt(" <-- cursor");
+ ioputc(&bstderr, '\n');
+ }
+ efmt("<<<\n");
+}
+#endif
+
+static union ref expraddr(struct function *, const struct expr *);
+static bool
+globsym(union ref *psym, const struct expr *ex)
+{
+ if (ex->t == ESTRLIT || (ex->t == ESYM && (ex->sym->scls & (SCSTATIC | SCEXTERN)))) {
+ *psym = expraddr(NULL, ex);
+ return 1;
+ }
+ return 0;
+
+}
+
+static void
+expr2reloc(union ref *psym, vlong *paddend, const struct expr *ex)
+{
+ if (ex->t == EADDROF && globsym(psym, ex)) {
+ *paddend = 0;
+ } else if (globsym(psym, ex) && in_range(ex->ty.t, TYARRAY, TYFUNC)) {
+ *paddend = 0;
+ } else if (ex->t == ESUB && globsym(psym, &ex->sub[0]) && isint(ex->sub[1].ty) && ex->sub[1].t == ENUMLIT) {
+ *paddend = ex->sub[1].i;
+ } else if (ex->t == EADD) {
+ for (int swp = 0; swp < 2; ++swp) {
+ if (globsym(psym, &ex->sub[swp]) && isint(ex->sub[swp^1].ty) && ex->sub[swp^1].t == ENUMLIT) {
+ *paddend = ex->sub[swp^1].i;
+ return;
+ }
+ }
+ } else
+ assert(0 && "non static reloc");
+}
+
+static void
+iniwrite(struct initparser *ip, uint off, union type ty, struct expr *ex)
+{
+ uchar *p;
+ if (ex->ty.t == TYSTRUCT) {
+ assert(ty.bits == ex->ty.bits);
+ for (uint i = 0, n = nmemb(ex->ty); i < n; ++i) {
+ uint suboff;
+ union type sub = membertype(&suboff, ex->ty, i);
+ iniwrite(ip, off + suboff, sub, &mkexpr(EGETF, ex->span, sub, .sub = ex));
+ }
+ } else if (ip->ev == EVSTATICINI) {
+ uint siz = typesize(ty);
+ if (ip->dyn) {
+ if (ip->ddat.n < off + siz) {
+ vresize(&ip->ddat, off + siz);
+ assert(off + siz == ip->ddat.n);
+ }
+ p = ip->ddat.p + off;
+ } else {
+ p = (ip->sec == Sdata ? objout.data.p : objout.rodata.p) + ip->off + off;
+ }
+
+ if (ex->t == ENUMLIT) {
+ struct expr *e = ex, tmp;
+ if (ex->ty.bits != ty.bits && ty.t != TYPTR) {
+ tmp = mkexpr(ECAST, ex->span, ty, .sub = ex);
+ e = &tmp;
+ eval(e, EVSTATICINI);
+ assert(e->t == ENUMLIT);
+ }
+ // efmt("#%u' wr %lx at %u\n", ip->dyn?0:ip->off, e->u, off);
+ ioflush(&bstderr);
+ switch (siz) {
+ default: assert(0);
+ case 1: *p = e->u; break;
+ case 2: wr16targ(p, e->u); break;
+ case 4: isint(ty) ? wr32targ(p, e->u) : wrf32targ(p, e->f); break;
+ case 8: isint(ty) ? wr64targ(p, e->u) : wrf64targ(p, e->f); break;
+ }
+ } else if (ty.t == TYARRAY && ex->t == ESTRLIT) {
+ uint n = siz < ex->s.n ? siz : ex->s.n;
+ //efmt("%s wrs %'S at %u\n", dat->name, ex->s.p, n, off);
+ memcpy(p , ex->s.p, n);
+ } else {
+ union ref sym;
+ vlong addend;
+ expr2reloc(&sym, &addend, ex);
+ assert(sym.t == RXCON);
+ if (!ip->dyn) {
+ objreloc(xcon2sym(sym.i), 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->off = off;
+ rel->addend = addend;
+ ip->drel = rel;
+ }
+ }
+ }
+}
+
+static struct initcur *
+iniadvance(struct initparser *ip, struct initcur *c, const struct span *span)
+{
+ if (c - ip->buf >= arraylength(ip->buf) - 1)
+ fatal(span, "too many nested initializers");
+ return c + 1;
+}
+
+/* set the initializer cursor object */
+static void
+inifocus(struct initparser *ip, struct comp *cm, const struct span *span, uint idx)
+{
+ uint off;
+ union type targ = membertype(&off, ip->sub->ty, idx);
+ struct initcur *next = iniadvance(ip, ip->cur, span);
+
+ if (isagg(ip->sub->ty) && targ.t == TYARRAY && !typearrlen(targ))
+ error(span, "cannot initialize flexible array member");
+ excesscheck(ip, span);
+
+ next->ty = targ;
+ next->idx = 0;
+ next->off = ip->sub->off + off;
+ next->prev = ip->cur - ip->buf;
+ ++ip->cur->idx;
+ ip->sub = ip->cur = next;
+}
+
+/* initialize a character array with a string literal */
+static void
+inistrlit(struct comp *cm, struct expr *ex, union type *ty)
+{
+ if (isincomplete(*ty)) {
+ *ty = mkarrtype(typechild(*ty), ty->flag & TFCHLDQUAL, ex->s.n + 1);
+ } else if (typesize(*ty) < ex->s.n) {
+ warn(&ex->span, "string literal in initializer is truncated from %u to %u bytes",
+ ex->s.n+1, typesize(*ty));
+ }
+ ex->ty = *ty;
+}
+
+/* read scalar initializer into initializer list and avance */
+static void
+ininext(struct initparser *ip, struct comp *cm)
+{
+ uint off;
+ union type targ;
+ struct expr ex = expr(cm);
+
+Retry:
+ targ = membertype(&off, ip->sub->ty, ip->sub->idx);
+
+ if (isagg(ip->sub->ty) && targ.t == TYARRAY && !typearrlen(targ)) {
+ error(&ex.span, "cannot initialize flexible array member");
+ ++ip->sub->idx;
+ return;
+ }
+ if (ex.t == ESTRLIT && chararrayp(targ)) {
+ assert(!isincomplete(targ));
+ inistrlit(cm, &ex, &targ);
+ iniwrite(ip, ip->sub->off + off, targ, &ex);
+ ++ip->sub->idx;
+ return;
+ } else if (ip->sub->idx >= nmemb(ip->sub->ty) && ip->sub != ip->cur) {
+ --ip->sub;
+ goto Retry;
+ } else if (objectp(targ) && targ.bits != ex.ty.bits) {
+ struct initcur *next = iniadvance(ip, ip->sub, &ex.span);
+ if (ip->sub - ip->buf == arraylength(ip->buf) - 1)
+ fatal(&ex.span, "too many nested initializers");
+ ++ip->sub->idx;
+ *next = (struct initcur) { targ, .off = ip->sub->off + off };
+ ip->sub = next;
+ goto Retry;
+ }
+ excesscheck(ip, &ex.span);
+
+ if (targ.t) {
+ if (!assigncheck(targ, &ex))
+ error(&ex.span, "cannot initialize '%ty' with expression of type '%ty'", targ, ex.ty);
+ else {
+ if (ip->ev && !eval(&ex, ip->ev))
+ error(&ex.span, "cannot evaluate expression statically");
+ else
+ iniwrite(ip, ip->sub->off + off, targ, &ex);
+ }
+ }
+ if (++ip->sub->idx == 0) {
+ error(&ex.span, "element makes object too large");
+ --ip->sub->idx;
+ }
+}
+
+static int
+aggdesignator(struct initparser *ip, union type ty, const char *name, const struct span *span)
+{
+ const struct typedata *td = &typedata[ty.dat];
+ for (int i = 0; i < td->nmemb; ++i) {
+ struct namedfield *fld = &td->fld[i];
+ if (fld->name == name) {
+ return i;
+ } else if (!fld->name) {
+ int save, sub;
+ struct initcur *next = iniadvance(ip, ip->sub, span);
+ save = ip->sub->idx;
+ ip->sub->idx = i+1;
+ *next = (struct initcur) { fld->f.t, .off = ip->sub->off + fld->f.off };
+ ip->sub = next;
+ sub = aggdesignator(ip, fld->f.t, name, span);
+ if (sub == -1) {
+ --ip->sub;
+ ip->sub->idx = save;
+ }
+ else return sub;
+ }
+ }
+ if (span)
+ error(span, "%ty has no such field: '%s'", ty, name);
+ return -1;
+}
+
+static bool
+designators(struct initparser *ip, struct comp *cm)
+{
+ struct token tk;
+ struct span span;
+ bool some = 0;
+
+ for (;;) {
+ uvlong idx = ~0ull;
+ if (match(cm, &tk, '[')) {
+ struct expr ex = commaexpr(cm);
+ span = tk.span;
+ joinspan(&span.ex, ex.span.ex);
+ peek(cm, &tk);
+ if (some) {
+ uint off;
+ union type ty = membertype(&off, ip->sub->ty, ip->sub->idx++);
+ struct initcur *next = iniadvance(ip, ip->sub, &tk.span);
+ *next = (struct initcur) { ty, .off = ip->sub->off + off };
+ ip->sub = next;
+ dumpini(ip);
+ }
+ 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))
+ 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");
+ else if (ex.i > ~0u - 1)
+ error(&ex.span, "index too large");
+ else {
+ idx = ex.u;
+ ip->sub->idx = idx;
+ if (ip->sub == ip->buf && ip->arrlen < idx+1)
+ ip->arrlen = idx+1;
+ dumpini(ip);
+ }
+ some = 1;
+ } else if (match(cm, &tk, '.')) {
+ span = tk.span;
+ peek(cm, &tk);
+ if (some) {
+ uint off;
+ union type ty = membertype(&off, ip->sub->ty, ip->sub->idx++);
+ struct initcur *next = iniadvance(ip, ip->sub, &tk.span);
+ *next = (struct initcur) { ty, .off = ip->sub->off + off };
+ ip->sub = next;
+ dumpini(ip);
+ }
+ if (expect(cm, TKIDENT, NULL)) joinspan(&span.ex, tk.span.ex);
+ if (!isagg(ip->sub->ty))
+ error(&span, "member designator used with non-aggregate type '%ty'", ip->sub->ty);
+ else if (tk.t == TKIDENT) {
+ idx = aggdesignator(ip, ip->sub->ty, tk.s, &span);
+ ip->sub->idx = idx;
+ dumpini(ip);
+ }
+ some = 1;
+ } else {
+ if (some) {
+ expect(cm, '=', NULL);
+ }
+ return some;
+ }
+ }
+}
+
+static struct expr
+initializer(struct comp *cm, union type *ty, enum evalmode ev, bool globl,
+ enum qualifier qual, const char *name)
+{
+ struct token tk;
+ struct span span;
+ struct init res = {0};
+ struct initparser ip[1] = {0};
+
+ ip->arena = &cm->exarena;
+ ip->ev = ev;
+ if (ev == EVSTATICINI) {
+ if (ty->t == TYARRAY && !typearrlen(*ty)) {
+ ip->dyn = 1;
+ } else {
+ ip->sec = qual & QCONST ? Srodata : Sdata;
+ ip->off = objnewdat(name, ip->sec, globl, typesize(*ty), typealign(*ty));
+ }
+ } else {
+ ip->init = &res;
+ }
+
+ if (!match(cm, &tk, '{')) {
+ struct expr ex = expr(cm);
+ if (ex.t == ESTRLIT && chararrayp(*ty)) {
+ inistrlit(cm, &ex, ty);
+ iniwrite(ip, 0, *ty, &ex);
+ if (ip->dyn)
+ goto Dynfix;
+ }
+ if (!assigncheck(*ty, &ex))
+ error(&ex.span, "cannot initialize '%ty' with expression of type '%ty'", *ty, ex.ty);
+ else {
+ if (ev && !eval(&ex, ev) && ev != EVFOLD)
+ error(&ex.span, "cannot evaluate expression statically");
+ else
+ iniwrite(ip, 0, *ty, &ex);
+ }
+ return ex;
+ }
+
+ span = tk.span;
+ ip->sub = ip->cur = ip->buf;
+ ip->cur->ty = *ty;
+ for (;;) {
+ peek(cm, &tk);
+ joinspan(&span.ex, tk.span.ex);
+ if (tk.t == '[' || tk.t == '.') {
+ designators(ip, cm);
+ }
+ if (match(cm, &tk, '}')) {
+ if (ip->cur == ip->buf) break;
+ ip->sub = ip->cur = ip->buf + ip->cur->prev;
+ dumpini(ip);
+ } else if (match(cm, &tk, '{')) {
+ struct span span = tk.span;
+ inifocus(ip, cm, &tk.span, ip->sub->idx);
+ if (peek(cm, &tk) == '}') {
+ if (!joinspan(&span.ex, tk.span.ex)) span = tk.span;
+ if (!objectp(ip->sub->ty)) {
+ error(&span, "scalar initializer cannot be empty");
+ } else if (ccopt.cstd < STDC23 && ccopt.pedant) {
+ warn(&span, "empty initializer in %M is an extension");
+ }
+ } else if (ip->sub->ty.t && !objectp(ip->sub->ty)) {
+ warn(&span, "brace initializer for scalar object (%ty)", ip->sub->ty);
+ }
+ continue;
+ } else {
+ dumpini(ip);
+ ininext(ip, cm);
+ }
+ match(cm, NULL, ',');
+ }
+ if (ip->dyn) {
+ enum section sec;
+ uint off, siz, align;
+ uchar *p;
+ uint len = ip->arrlen > ip->cur->idx ? ip->arrlen : ip->cur->idx;
+
+ if (len == 0)
+ error(&span, "array cannot have zero length");
+ *ty = mkarrtype(typechild(*ty), ty->flag & TFCHLDQUAL, len);
+ Dynfix:
+ sec = qual & QCONST ? Srodata : Sdata;
+ off = objnewdat(name, sec, globl, siz = typesize(*ty), align = typealign(*ty));
+ p = sec == Srodata ? objout.rodata.p : objout.data.p;
+ memcpy(p + off, ip->ddat.p, ip->ddat.n);
+ memset(p + off + ip->ddat.n, 0, typesize(*ty) - ip->ddat.n);
+ vpush(&dattab, ((struct irdat) {
+ align, globl, sec, siz, off, name
+ }));
+ vfree(&ip->ddat);
+ for (struct dreloc *rel = ip->drel; rel; rel = rel->link) {
+ objreloc(rel->sym, targ_64bit ? REL_ABS64 : REL_ABS32, sec, off + rel->off, rel->addend);
+ }
+ }
+ dumpini(ip);
+
+ if (ev == EVSTATICINI) {
+ return (struct expr){0};
+ } else {
+ return mkexpr(EINIT, span, *ty, .init = alloccopy(&cm->exarena, &res, sizeof res, 0));
+ }
+}
+
/*****************/
/* Decls Parsing */
/*****************/
@@ -1069,6 +1546,8 @@ buildagg(struct comp *cm, enum typetag tt, const char *name, int id)
}
} while (st.more);
}
+ if (td.flexi && fld.n == 1)
+ error(&flexspan, "flexible array member in otherwise empty aggregate");
if (td.flexi && ccopt.cstd < STDC99 && ccopt.pedant)
warn(&flexspan, "flexible array member in %M is an extension");
if (fld.n == 0) {
@@ -2029,42 +2508,45 @@ struct condphis {
static void
condexprrec(struct function *fn, const struct expr *ex, struct condphis *phis,
- int boolcon, struct block *end, struct block *zero)
+ int boolcon, struct block *const next, struct block *end)
{
- struct block *tr, *fl, *next;
+ struct block *tr, *fl;
union ref r;
while (ex->t == ESEQ) {
expreffects(fn, &ex->sub[0]);
ex = &ex->sub[1];
}
if (ex->t == ELOGAND) {
- next = newblk(fn);
- condexprrec(fn, &ex->sub[0], phis, 0, next, end);
- useblk(fn, next);
- condexprrec(fn, &ex->sub[1], phis, -2, end, zero);
+ tr = newblk(fn);
+ condexprrec(fn, &ex->sub[0], phis, 0, tr, end);
+ useblk(fn, tr);
+ condexprrec(fn, &ex->sub[1], phis, 0, next, end);
} else if (ex->t == ELOGIOR) {
- next = newblk(fn);
- condexprrec(fn, &ex->sub[0], phis, 1, end, next);
- useblk(fn, next);
- condexprrec(fn, &ex->sub[1], phis, -2, end, zero);
+ fl = newblk(fn);
+ condexprrec(fn, &ex->sub[0], phis, 1, end, fl);
+ useblk(fn, fl);
+ condexprrec(fn, &ex->sub[1], phis, 1, end, next ? next : end);
} else if (ex->t == ECOND) {
tr = newblk(fn);
fl = newblk(fn);
condjump(fn, &ex->sub[0], tr, fl);
useblk(fn, tr);
- condexprrec(fn, &ex->sub[1], phis, -1, end, zero);
+ condexprrec(fn, &ex->sub[1], phis, -1, end, end);
useblk(fn, fl);
- condexprrec(fn, &ex->sub[2], phis, -1, end, zero);
+ condexprrec(fn, &ex->sub[2], phis, -1, end, end);
} else {
r = exprvalue(fn, ex);
- if (boolcon == -2)
- r = cvt(fn, TYBOOL, ex->ty.t, r);
- if (boolcon >= 0)
- vpush(&phis->ref, mkintcon(KI4, boolcon));
- else
- vpush(&phis->ref, r);
- if (zero) {
- putcondbranch(fn, r, end, zero);
+ if (boolcon >= 0) {
+ if (!next || next == end) {
+ boolcon = -1;
+ r = cvt(fn, TYBOOL, ex->ty.t, r);
+ } else {
+ r = mkref(RICON, boolcon);
+ }
+ }
+ vpush(&phis->ref, r);
+ if (next && next != end) {
+ putcondbranch(fn, r, next, end);
} else {
assert(boolcon < 0);
putbranch(fn, end);
@@ -2081,9 +2563,8 @@ condexprvalue(struct function *fn, const struct expr *ex)
struct condphis phis = { VINIT(refbuf, arraylength(refbuf)) };
struct block *dst = newblk(fn);
union ref r;
- condexprrec(fn, ex, &phis, -1, dst, NULL);
+ condexprrec(fn, ex, &phis, -1, NULL, dst);
useblk(fn, dst);
- assert(fn->curblk->npred == phis.ref.n);
r = addphi(fn, type2cls[ex->ty.t], phis.ref.p);
vfree(&phis.ref);
return r;
@@ -2458,7 +2939,7 @@ stmt(struct comp *cm, struct function *fn)
/* kludge for no backtracking and no lookahead */
ex = exprparse(cm, 1, &tk);
EMITS expreffects(fn, &ex);
- return fn->curblk != NULL;
+ return fn->curblk == NULL;
}
}
@@ -2727,9 +3208,14 @@ localdecl(struct comp *cm, struct function *fn, bool forini)
if (forini)
error(&decl.span, "static declaration in 'for' loop initializer");
decl.id = ++staticid;
- break;
+ goto Initz;
case SCNONE:
+ if (decl.ty.t == TYFUNC) {
+ decl.scls = SCEXTERN;
+ break;
+ }
decl.scls = SCAUTO;
+ /* fallthru */
case SCAUTO:
case SCREGISTER:
if (isincomplete(decl.ty) || decl.ty.t == TYFUNC) {
@@ -2742,10 +3228,11 @@ localdecl(struct comp *cm, struct function *fn, bool forini)
EMITS {
decl.id = addinstr(fn, mkalloca(typesize(decl.ty), typealign(decl.ty))).i;
}
+ Initz:
if (st.varini) {
- putdecl(cm, &decl);
+ struct decl *d = putdecl(cm, &decl);
put = 1;
- ini = expr(cm);
+ ini = initializer(cm, &d->ty, EVFOLD, 0, decl.qual, decl.name);
pdecl(&st, cm);
if (!assigncheck(decl.ty, &ini)) {
struct span span = decl.span;
@@ -2753,12 +3240,15 @@ localdecl(struct comp *cm, struct function *fn, bool forini)
error(&span, "cannot initialize '%ty' variable with '%ty'",
decl.ty, ini.ty);
}
- EMITS {
- if (isagg(decl.ty))
- structcopy(fn, decl.ty, mkref(RTMP, decl.id), expraddr(fn, &ini));
- else
- genstore(fn, decl.ty, mkref(RTMP, decl.id), exprvalue(fn, &ini));
+ if (decl.scls & (SCAUTO | SCREGISTER)) {
+ EMITS {
+ if (isagg(decl.ty))
+ structcopy(fn, decl.ty, mkref(RTMP, decl.id), expraddr(fn, &ini));
+ else
+ genstore(fn, decl.ty, mkref(RTMP, decl.id), exprvalue(fn, &ini));
+ }
}
+ } else if (decl.scls == SCSTATIC) {
}
break;
case SCTYPEDEF:
@@ -2858,7 +3348,6 @@ function(struct comp *cm, struct function *fn, const char **pnames, const struct
}
}
-
void
docomp(struct comp *cm)
{
@@ -2869,7 +3358,6 @@ docomp(struct comp *cm)
putdecl(cm, &(struct decl) { mktype(TYVALIST), SCTYPEDEF, .name = intern("__builtin_va_list") });
while (peek(cm, tk) != TKEOF) {
- struct expr ini;
struct declstate st = { DTOPLEVEL };
do {
int nerr = nerror;
@@ -2882,6 +3370,7 @@ docomp(struct comp *cm)
}
continue;
}
+ if (!decl.scls) decl.scls = SCEXTERN;
if (st.funcdef) {
const struct typedata *td = &typedata[decl.ty.dat];
struct function fn = { cm->fnarena, decl.name, .globl = decl.scls != SCSTATIC };
@@ -2894,18 +3383,16 @@ docomp(struct comp *cm)
irdump(&fn);
irfini(&fn);
} else if (decl.name) {
- putdecl(cm, &decl);
+ struct decl *d = putdecl(cm, &decl);
if (st.varini) {
- ini = expr(cm);
+ (void) initializer(cm, &d->ty, EVSTATICINI, decl.scls != SCSTATIC, decl.qual, decl.name);
pdecl(&st, cm);
- if (!assigncheck(decl.ty, &ini))
- error(&ini.span, "cannot initialize %ty with %ty", decl.ty, ini.ty);
- if (!eval(&ini, EVSTATICINI))
- error(&ini.span, "cannot evaluate expression statically");
+ } else if (decl.scls == SCSTATIC) {
+ objnewdat(d->name, Sbss, 0, typesize(d->ty), typealign(d->ty));
}
- if (ccopt.dbg.p) efmt("var %s : %tq\n", decl.name, decl.ty, decl.qual);
+ if (ccopt.dbg.p) efmt("var %s : %tq\n", d->name, d->ty, d->qual);
} else {
- if (ccopt.dbg.p) efmt("type %ty\n", decl.ty);
+ if (ccopt.dbg.p && decl.ty.t) efmt("type %ty\n", decl.ty);
}
freearena(cm->fnarena);
freearena(cm->exarena);
diff --git a/c.h b/c.h
index 6cdde09..33eef54 100644
--- a/c.h
+++ b/c.h
@@ -38,11 +38,43 @@ struct expr {
uvlong u; vlong i; double f; /* ENUMLIT */
struct bytes s; /* ESTRLIT */
struct decl *sym; /* ESYM */
- struct initializer *ini; /* EINIT */
+ struct init *init; /* EINIT */
+ };
+};
+
+struct init {
+ uint n;
+ struct bitset *zero;
+ uint *offs;
+ struct { uchar off, siz; } *bitf;
+ struct expr **ex;
+};
+
+enum storageclass {
+ SCNONE,
+ SCTYPEDEF = 1<<0,
+ SCEXTERN = 1<<1,
+ SCSTATIC = 1<<2,
+ SCTHREADLOCAL = 1<<3,
+ SCAUTO = 1<<4,
+ SCREGISTER = 1<<5,
+};
+
+struct decl {
+ union type ty;
+ uchar scls;
+ uchar qual : 2;
+ uchar isenum : 1;
+ struct span span;
+ const char *name;
+ union {
+ struct { ushort align; int id; };
+ vlong value;
};
};
enum evalmode {
+ EVNONE,
EVINTCONST,
EVARITH,
EVSTATICINI,
diff --git a/elf.c b/elf.c
index 934e4f8..d269531 100644
--- a/elf.c
+++ b/elf.c
@@ -21,9 +21,6 @@ struct sym {
size;
};
static vec_of(struct sym) symtab;
-static uchar dataalign = 1, rodataalign = 1, bssalign = 1;
-static uint nbss;
-static vec_of(uchar) data, rodata;
static uint ntextrel, nrodatarel, ndatarel;
struct reloc {
uchar section;
@@ -34,6 +31,8 @@ struct reloc {
};
static vec_of(struct reloc) relocs;
+#define O objout
+
void
elfinit(void)
{
@@ -115,7 +114,9 @@ elfaddsym(const char *nam, int info, enum section sect, uvlong value, uvlong siz
static const ushort relktab[][NRELOCKIND] = {
[ISamd64] = {
- [REL_ABS] = 5, /* R_X86_64_COPY */
+ [REL_ABS64] = 1, /* R_X86_64_64 */
+ [REL_ABS32] = 10, /* R_X86_64_32 */
+ [REL_ABS32S] = 11, /* R_X86_64_32S */
[REL_PCREL32] = 2, /* R_X86_64_PC32 */
}
};
@@ -134,50 +135,6 @@ elfreloc(const char *sym, enum relockind kind, enum section section, uint off, v
vpush(&relocs, ((struct reloc) { section, relktab[mctarg->isa][kind], snam, off, addend}));
}
-void
-elfputdat(const struct irdat *dat)
-{
- static const char zero[8];
- enum section s;
- uint off;
- uint ndat = dat->siz <= 8 ? dat->siz : dat->dat.n;
- uint nzr = dat->siz - ndat;
- const uchar *d = dat->siz <= 8 ? dat->sdat : dat->dat.p;
-
- assert(dat->siz);
- if (!dat->syms && (dat->siz > 8 ? !d : !memcmp(d, zero, dat->siz))) {
- /* all zeroes */
- s = Sbss;
- } else {
- s = dat->mut ? Sdata : Srodata;
- }
-
- switch (s) {
- default: assert(0);
- case Srodata:
- if (dat->align > rodataalign) rodataalign = dat->align;
- while (rodata.n & (dat->align - 1)) vpush(&rodata, 0);
- off = rodata.n;
- vpushn(&rodata, d, ndat);
- while (nzr--) vpush(&rodata, 0);
- break;
- case Sdata:
- if (dat->align > dataalign) dataalign = dat->align;
- while (data.n & (dat->align - 1)) vpush(&data, 0);
- off = data.n;
- vpushn(&data, d, ndat);
- while (nzr--) vpush(&data, 0);
- break;
- case Sbss:
- if (dat->align > bssalign) bssalign = dat->align;
- off = alignup(nbss, dat->align);
- nbss = off + dat->siz;
- break;
- }
- elfaddsym(dat->name, ELF_S_INFO(dat->globl, STT_OBJECT), s, off, dat->siz);
-}
-
-
static void
elf64puthdr(struct wbuf *out, struct elf64hdr *hdr)
{
@@ -419,9 +376,9 @@ elffini(struct wbuf *out)
uchar *p;
switch (rel->section) {
default: assert(0);
- case Sdata: p = data.p + rel->off; break;
- case Srodata: p = rodata.p + rel->off; break;
- case Stext: p = objout.textbegin + rel->off; break;
+ case Sdata: p = O.data.p + rel->off; break;
+ case Srodata: p = O.rodata.p + rel->off; break;
+ case Stext: p = O.textbegin + rel->off; break;
}
if (targ_64bit)
wr64targ(p, rel->addend);
@@ -429,12 +386,12 @@ elffini(struct wbuf *out)
wr32targ(p, rel->addend);
}
}
- size_t codesize = alignup(objout.code - objout.textbegin, align),
+ size_t codesize = alignup(O.code - O.textbegin, align),
rodataoff = (targ_64bit ? sizeof hdr.h64 : sizeof hdr.h32) + codesize,
- rodatasize = rodata.n,
+ rodatasize = O.rodata.n,
dataoff = rodataoff + rodatasize,
- datasize = data.n,
- bsssize = nbss,
+ datasize = O.data.n,
+ bsssize = O.nbss,
shstrsoff = dataoff + datasize,
shstrssize = sizeof(shstrs),
strsoff = shstrsoff + shstrssize,
@@ -470,13 +427,13 @@ elffini(struct wbuf *out)
elf32puthdr(out, &hdr.h32);
/* .text progbits */
- iowrite(out, objout.textbegin, codesize);
+ iowrite(out, O.textbegin, codesize);
/* .rodata progbits */
- iowrite(out, rodata.p, rodata.n);
+ iowrite(out, O.rodata.p, O.rodata.n);
/* .data progbits */
- iowrite(out, data.p, data.n);
+ iowrite(out, O.data.p, O.data.n);
/* section names */
iowrite(out, shstrs, sizeof shstrs);
@@ -518,17 +475,17 @@ elffini(struct wbuf *out)
putshdr(.name = shnam_rodata, .type = SHT_PROGBITS,
.flags = SHF_ALLOC,
.offset = rodataoff, .size = rodatasize,
- .addralign = rodataalign,);
+ .addralign = O.rodataalign,);
/* §3 .data */
putshdr(.name = shnam_data, .type = SHT_PROGBITS,
.flags = SHF_ALLOC | SHF_WRITE,
.offset = dataoff, .size = datasize,
- .addralign = dataalign,);
+ .addralign = O.dataalign,);
/* §4 .bss */
putshdr(.name = shnam_bss, .type = SHT_NOBITS,
.size = bsssize,
.flags = SHF_ALLOC | SHF_WRITE,
- .addralign = bssalign,);
+ .addralign = O.bssalign,);
/* §5 .shstrtab */
putshdr(.name = shnam_shstrtab, .type = SHT_STRTAB,
.offset = shstrsoff, .size = shstrssize,
diff --git a/endian.h b/endian.h
index 8448c79..e1e9740 100644
--- a/endian.h
+++ b/endian.h
@@ -128,6 +128,20 @@ wr64targ(uchar *p, uvlong x)
memcpy(p, &x, sizeof x);
}
+static inline void
+wrf32targ(uchar *p, float x)
+{
+ union { float f; uint i; } u = { x };
+ wr32targ(p, u.i);
+}
+
+static inline void
+wrf64targ(uchar *p, double x)
+{
+ union { double f; uvlong i; } u = { x };
+ wr64targ(p, u.i);
+}
+
#endif /* ENDIAN_H_ */
/* vim:set ts=3 sw=3 expandtab: */
diff --git a/eval.c b/eval.c
index 71be890..2a2de6d 100644
--- a/eval.c
+++ b/eval.c
@@ -77,6 +77,30 @@ unop(struct expr *ex, enum evalmode mode)
{
struct expr *sub = ex->sub;
+ if (mode >= EVSTATICINI && ex->t == EDEREF) {
+ uvlong off;
+ struct bytes s;
+ if (sub->t == ESTRLIT) {
+ /* *"s" */
+ off = 0;
+ s = sub->s;
+ } 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;
+ s = sub->sub[0].s;
+ } 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;
+ s = sub->sub[1].s;
+ } else return 0;
+ if (off > s.n) return 0;
+ ex->t = ENUMLIT;
+ ex->ty = mktype(TYINT);
+ ex->u = off == s.n ? 0 : s.p[off];
+ return 1;
+ }
if (sub->t != ENUMLIT && !eval(sub, mode)) return 0;
switch (ex->t) {
case ECAST:
@@ -101,6 +125,30 @@ unop(struct expr *ex, enum evalmode mode)
return 1;
}
+/* link time constants */
+static bool
+isglobsym(const struct expr *ex)
+{
+ return ex->t == ESTRLIT || (ex->t == ESYM && ex->sym && (ex->sym->scls & (SCSTATIC | SCEXTERN)));
+}
+
+static bool
+isaddrconst(struct expr *ex)
+{
+ if (ex->t == EADDROF && isglobsym(ex->sub))
+ return 1;
+ if (isglobsym(ex) && in_range(ex->ty.t, TYARRAY, TYFUNC))
+ return 1;
+ if (ex->t == ESUB)
+ return isglobsym(&ex->sub[0]) && isint(ex->sub[1].ty) && eval(&ex->sub[1], EVSTATICINI);
+ if (ex->t == EADD) {
+ for (int swp = 0; swp < 2; ++swp)
+ if (isglobsym(&ex->sub[swp]) && isint(ex->sub[swp^1].ty) && eval(&ex->sub[swp^1], EVSTATICINI))
+ return 1;
+ }
+ return 0;
+}
+
static bool
binop(struct expr *ex, enum evalmode mode)
{
@@ -205,6 +253,8 @@ eval(struct expr *ex, enum evalmode mode)
if (mode <= EVINTCONST) return isint(ex->ty);
return 1;
}
+ if (mode == EVSTATICINI && isaddrconst(ex))
+ return 1;
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) {
diff --git a/ir.c b/ir.c
index 79f9ebd..60d9771 100644
--- a/ir.c
+++ b/ir.c
@@ -1,5 +1,6 @@
#include "ir.h"
#include "endian.h"
+#include "obj.h"
uchar type2cls[NTYPETAG];
uchar cls2siz[KF8+1];
@@ -100,36 +101,6 @@ addcon(const struct xcon *con)
}
}
-void
-conputdat(struct irdat *dat, uint off, enum typetag t, const void *src)
-{
- uint siz = targ_primsizes[t];
- bool iszero = 1;
- uchar *pdat;
- assert(off + siz <= dat->siz);
- for (uint i = 0; i < siz; ++i) {
- if (((uchar *)src) != 0) {
- iszero = 0;
- break;
- }
- }
- if (iszero && (dat->siz <= 8 || dat->dat.n < off))
- return;
-
- if (dat->siz > 8)
- while (off + siz > dat->dat.n)
- vpush(&dat->dat, 0);
- pdat = dat->siz <= 8 ? dat->sdat : dat->dat.p;
-
- switch (siz) {
- case 1: pdat[off] = *(uchar *)src; break;
- case 2: wr16targ(&pdat[off], *(ushort *)src); break;
- case 4: wr32targ(&pdat[off], *(uint *)src); break;
- case 8: wr64targ(&pdat[off], *(uvlong *)src); break;
- default: assert(0);
- }
-}
-
union irtype
mkirtype(union type t)
{
@@ -168,14 +139,9 @@ mksymref(const char *s)
union ref
mkdatref(const char *name, uint siz, uint align, const void *bytes, uint n, bool deref)
{
- struct irdat dat = { .align = align, .siz = siz, .name = name };
- if (bytes) {
- if (siz <= 8) memcpy(dat.sdat, bytes, n < siz ? n : siz);
- else {
- while (((uchar *)bytes)[n-1] == 0) --n; /* nip trailing zeroes */
- if (n) vpushn(&dat.dat, bytes, n);
- }
- }
+ struct irdat dat = { .align = align, .siz = siz, .name = name, .section = Srodata };
+
+ assert(n <= siz && siz && align);
if (!name) {
extern const char *intern(const char *);
char buf[32];
@@ -184,8 +150,11 @@ mkdatref(const char *name, uint siz, uint align, const void *bytes, uint n, bool
bfmt(&wbuf, ".L.%d", dattab.n);
ioputc(&wbuf, 0);
assert(!wbuf.err);
- dat.name = intern(buf);
+ dat.name = name = intern(buf);
}
+ dat.off = objnewdat(name, dat.section, 0, siz, align);
+ memcpy(objout.rodata.p+dat.off, bytes, n);
+ memset(objout.rodata.p+dat.off+n, 0, siz - n);
vpush(&dattab, dat);
return mkref(RXCON, addcon(&(struct xcon){.isdat = 1, .deref = deref, .dat = dattab.n - 1}));
}
diff --git a/ir.h b/ir.h
index 6507fe0..b206550 100644
--- a/ir.h
+++ b/ir.h
@@ -16,19 +16,11 @@ union irtype {
};
struct irdat {
- uchar align : 6, mut : 1, globl : 1;
+ uchar align : 6, globl : 1;
+ uchar section;
uint siz;
- union {
- vec_of(uchar) dat;
- uchar sdat[8];
- };
+ uint off;
const char *name;
- struct symref {
- struct symref *next;
- const char *sym;
- uint off;
- vlong addend;
- } *syms;
};
struct xcon {
@@ -221,13 +213,13 @@ union ref mkfltcon(enum irclass, double);
#define isintcon(r) (iscon(r) && kisint(concls(r)))
#define isfltcon(r) ((r).t == RXCON && kisflt(conht[(r).i].cls))
#define isnumcon(r) ((r).t == RICON || ((r).t == RXCON && conht[(r).i].cls))
+#define isaddrcon(r) ((r).t == RXCON && !conht[(r).i].cls && !conht[(r).i].deref)
#define intconval(r) ((r).t == RICON ? (r).i : conht[(r).i].i)
#define fltconval(r) (conht[(r).i].f)
union ref mksymref(const char *);
union ref mkdatref(const char *name, uint siz, uint align, const void *, uint n, bool deref);
const char *xcon2sym(int ref);
struct instr mkalloca(uint siz, uint align);
-void conputdat(struct irdat *, uint off, enum typetag t, const void *dat);
union ref mkcallarg(union irtype ret, uint narg, int vararg);
#define mkintrin(B, C, N) mkinstr(Ointrin, C, {.t=RICON,B}, mkcallarg((union irtype){{0}},N,-1))
union ref mkaddr(struct addr);
diff --git a/irdump.c b/irdump.c
index 21b5f09..60964c0 100644
--- a/irdump.c
+++ b/irdump.c
@@ -1,44 +1,38 @@
#include "ir.h"
+#include "obj.h"
static int nextdat;
static void
pridat(const struct irdat *dat)
{
- efmt("%s %y(align %d, size %d):\n\t", dat->mut ? "dat" : "rodat", dat->name, dat->align, dat->siz);
- assert(!dat->syms);
- if (dat->siz <= 8) {
- efmt("b ");
- for (int i = 0; i < dat->siz; ++i)
- efmt("%d,", dat->sdat[i]);
- } else {
- enum {
- MINZERO = 4,
- MAXLINE = 60,
- };
- int npri = 0;
- int nzero = dat->siz - dat->dat.n;
- int strbegin = 0, nstr = 0;
- for (int i = 0; i < dat->dat.n + (nzero & -(nzero <= MINZERO)); ++i) {
- int c = i < dat->dat.n ? dat->dat.p[i] : 0;
- if (npri > MAXLINE) {
- npri = 0;
- efmt("\n\t");
- }
- if (aisprint(c)) {
- if (!nstr++) strbegin = i;
- } else {
- if (nstr) {
- npri += efmt("asc %'S,", dat->dat.p+strbegin, nstr);
- nstr = 0;
- efmt("b ");
- }
- npri += efmt("%d,", c);
+ uchar *p = (dat->section == Sdata ? objout.data.p : objout.rodata.p) + dat->off;
+ enum {
+ MINZERO = 4,
+ MAXLINE = 60,
+ };
+ int npri = 0;
+ int strbegin = 0, nstr = 0;
+ assert(dat->section == Sdata || dat->section == Srodata);
+ efmt("%s %y(align %d, size %d):\n\t", dat->section == Sdata ? "data" : "rodata", dat->name, dat->align, dat->siz);
+ for (int i = 0; i < dat->siz; ++i) {
+ int c = p[i];
+ if (npri > MAXLINE) {
+ npri = 0;
+ efmt("\n\t");
+ }
+ if (aisprint(c)) {
+ if (!nstr++) strbegin = i;
+ } else {
+ if (nstr) {
+ npri += efmt("asc %'S,", p+strbegin, nstr);
+ nstr = 0;
+ efmt("b ");
}
+ npri += efmt("%d,", c);
}
- if (nstr) npri += efmt("asc %'S,", dat->dat.p+strbegin, nstr);
- if ((nzero -= MINZERO) > 0) efmt("z %d", nzero);
}
+ if (nstr) npri += efmt("asc %'S,", p+strbegin, nstr);
efmt("\n");
}
diff --git a/mem.c b/mem.c
index 9820f0a..f53ba05 100644
--- a/mem.c
+++ b/mem.c
@@ -46,7 +46,9 @@ vpush_(void **p, int *pcap, uint *pn, uint siz)
{
if (*pcap >= 0 && *pn >= *pcap) { /* empty or inline buffer */
int cap = *pcap ? *pcap * 2 : 8;
+ void *old = *p;
*p = xrealloc(NULL, cap * siz);
+ if (old) memcpy(*p, old, *pcap * siz);
*pcap = -cap;
} else if (*pcap < 0 && *pn >= -*pcap) { /* dyn buf */
*p = xrealloc(*p, -(*pcap *= 2) * siz);
@@ -72,8 +74,17 @@ vpushn_(void **p, int *pcap, uint *pn, uint siz, const void *dat, uint ndat)
void
vresize_(void **p, int *pcap, uint *pn, uint siz, uint N)
{
- while (*pcap < N)
- vpush_(p, pcap, pn, siz);
+ if (N <= *pn) {
+ } else if (*pcap > 0 && *pcap < N) {
+ void *old = *p;
+ *p = xrealloc(NULL, -(*pcap = -(N * siz)));
+ if (old) memcpy(*p, old, *pcap * siz);
+ } else if (*pcap <= 0 && -*pcap < N) {
+ *pcap = *pcap ? *pcap : -1;
+ do *pcap *= 2; while (-*pcap < N);
+ *p = xrealloc(*p, -*pcap * siz);
+ memset((char *)*p + *pn*siz, 0, (N - *pn) * siz);
+ }
*pn = N;
}
diff --git a/obj.c b/obj.c
index 127dedd..748e1e7 100644
--- a/obj.c
+++ b/obj.c
@@ -5,10 +5,10 @@
#include <fcntl.h>
#include <unistd.h>
+
void elfinit(void);
void elfaddsym(const char *, int info, enum section, uvlong value, uvlong size);
void elfreloc(const char *sym, enum relockind, enum section, uint off, vlong addend);
-void elfputdat(const struct irdat *);
void elffini(struct wbuf *);
struct objfile objout;
@@ -38,6 +38,44 @@ objdeffunc(const char *nam, bool globl, uint off, uint siz)
}
}
+uint
+objnewdat(const char *name, enum section sec, bool globl, uint siz, uint align)
+{
+ uint off;
+
+ assert(siz && align && ispo2(align));
+
+ switch (sec) {
+ default: assert(0);
+ case Srodata:
+ if (align > objout.rodataalign) objout.rodataalign = align;
+ while (objout.rodata.n & (align - 1)) vpush(&objout.rodata, 0);
+ off = objout.rodata.n;
+ vresize(&objout.rodata, objout.rodata.n + siz);
+ memset(objout.rodata.p+off, 0, siz);
+ break;
+ case Sdata:
+ if (align > objout.dataalign) objout.dataalign = align;
+ while (objout.data.n & (align - 1)) vpush(&objout.data, 0);
+ off = objout.data.n;
+ vresize(&objout.data, objout.data.n + siz);
+ memset(objout.data.p+off, 0, siz);
+ break;
+ case Sbss:
+ if (align > objout.bssalign) objout.bssalign = align;
+ off = alignup(objout.nbss, align);
+ objout.nbss = off + siz;
+ break;
+ }
+
+ switch (mctarg->objkind) {
+ case OBJELF:
+ elfaddsym(name, /*STT_LOCAL/GLOBAL*/globl<<4 | /*STT_OBJECT*/1, sec, off, siz);
+ break;
+ }
+ return off;
+}
+
void
objreloc(const char *sym, enum relockind reloc, enum section section, uint off, vlong addend)
{
@@ -55,10 +93,6 @@ objfini(void)
struct wbuf out = FDBUF(buf, sizeof buf, open(objout.file, O_WRONLY | O_CREAT | O_TRUNC, 0666));
if (out.fd < 0) fatal(NULL, "could not open %'s for writing: %s", objout.file, strerror(errno));
- for (int i = 0; i < dattab.n; ++i) {
- elfputdat(&dattab.p[i]);
- }
-
switch (mctarg->objkind) {
case OBJELF: elffini(&out); break;
}
diff --git a/obj.h b/obj.h
index a0d54da..2c4753d 100644
--- a/obj.h
+++ b/obj.h
@@ -4,10 +4,15 @@ extern struct objfile {
const char *file;
uchar *textbegin, *textend;
uchar *code;
+ uchar dataalign, rodataalign, bssalign;
+ uint nbss;
+ vec_of(uchar) data, rodata;
} objout;
enum relockind {
- REL_ABS,
+ REL_ABS64,
+ REL_ABS32,
+ REL_ABS32S,
REL_PCREL32,
NRELOCKIND,
};
@@ -15,6 +20,7 @@ enum section { Snone, Stext, Srodata, Sdata, Sbss };
void objini(const char *);
void objdeffunc(const char *nam, bool globl, uint off, uint siz);
+uint objnewdat(const char *name, enum section, bool globl, uint siz, uint align);
void objreloc(const char *sym, enum relockind, enum section, uint off, vlong addend);
void objfini(void);
diff --git a/test/goto.c b/test/goto.c
new file mode 100644
index 0000000..0ecbf28
--- /dev/null
+++ b/test/goto.c
@@ -0,0 +1,25 @@
+int crz(int x) {
+/* x <<= 1; while (x < 0) ++x; return x; */
+ goto e;
+a:
+ return x;
+j:
+ ++x;
+ goto q;
+b:
+ if (x < 0)
+ goto j;
+ goto a;
+q:
+ goto b;
+e:
+ x <<= 1;
+ goto b;
+}
+
+int printf(const char *, ...);
+int main() {
+ printf("should print 14: %d\n", crz(7));
+ printf("should print 0: %d\n", crz(-3));
+ printf("should print 0: %d\n", crz(0));
+}
diff --git a/test/init.c b/test/init.c
new file mode 100644
index 0000000..24d1b7f
--- /dev/null
+++ b/test/init.c
@@ -0,0 +1,65 @@
+int gexplicit[4] = { 7, 3, 4, 5, };
+_Static_assert(sizeof gexplicit/sizeof *gexplicit ==4, "");
+int gimplicit[] = { 3, 5, };
+_Static_assert(sizeof gimplicit/sizeof *gimplicit ==2, "");
+char dim2[][2] = { {1,2},4,3 };
+_Static_assert(sizeof dim2 == 4, "dim2");
+struct S{int x; int a[2][12];} S = {1,{{0,3},2}};
+
+const union U {int x; int y[2];} U = {1,};
+
+char str[] = "abcdef";
+_Static_assert(sizeof str == 7, "str[7]");
+
+const char strs[][2] = {"ax", {'c','x'}, 'd',"?x"[1], "bx"};
+
+/*
+struct { int y; signed char x[]; } flex1 = {0, {0}};
+struct { int y; signed char x[]; } flex2 = {0, 1,4,5};
+struct { int y; signed char x[]; } flex3 = {0, "/"};
+*/
+
+struct { float x, y; } desgn1[] = {[1]=1.f,3, -0.5};
+_Static_assert(sizeof desgn1/sizeof *desgn1 == 3,"");
+
+struct n { void * j; struct { int y; float z; }; void *g;} desgn2 = { .y = 0, 1.0f, 0, 8 };
+struct n2 { void * j; struct as { struct { int y; }; float z; } a[3]; char *g;} desgn3 = { .a={-1}, .a[2].y = 6, 1.0f, "k" };
+
+char *s = "abc";
+const char *ss[] = {"red","blue","green"};
+
+union fi {float f; int i;} fi = {.i = 0xFF<<22,};
+
+char arrdsgn[] = { [4] = 1, [1] = 5 };
+_Static_assert(sizeof arrdsgn == 5, "");
+
+// int q[1] = {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}};
+
+/*
+void f() {
+ struct {int x,y;} a = {1,2}, b = {3,4}, c[] = {a,b};
+}
+*/
+
+void *rec[1] = {rec};
+
+int printf(char *, ...);
+int main() {
+ printf("gexplicit[4] \t%d,%d,%d,%d\n", gexplicit[0], gexplicit[1], gexplicit[2], gexplicit[3]);
+ printf("gimplicit[] \t%d,%d\n", gimplicit[0], gimplicit[1]);
+ printf("dim2[2][2] \t{%d,%d}, {%d,%d}\n", dim2[0][0],dim2[0][1],dim2[1][0],dim2[1][1]);
+ printf("S.x = %d, S.a[0][1] = %d, S.a[1][0] = %d\n", S.x, S.a[0][1], S.a[1][0]);
+ printf("U.x = %d\n", U.x);
+ printf("str[] \t\"%s\"\n", str);
+ printf("strs \t\"%.*s\"\n", (int)sizeof strs, strs);
+ printf("desgn1[] \t{%f,%f}, {%f,%f}, {%f,%f}\n", desgn1[0].x, desgn1[0].y, desgn1[1].x, desgn1[1].y, desgn1[2].x, desgn1[2].y);
+ printf("desgn2 \t{%p,{%d,%f},%s}\n", desgn2.j, desgn2.y, desgn2.z, desgn2.g);
+ printf("desgn3 \t{%p,{{{%d},%f},{{%d},%f},{{%d},%f}},\"%s\"}\n", desgn3.j,
+ desgn3.a[0].y, desgn3.a[0].z, desgn3.a[1].y, desgn3.a[1].z,
+ desgn3.a[2].y, desgn3.a[2].z, desgn3.g);
+ printf("fi \t{%f | %d}\n", fi.f, fi.i);
+ printf("arrdsgn[] \t{%d,%d,%d,%d,%d}\n", arrdsgn[0], arrdsgn[1], arrdsgn[2], arrdsgn[3], arrdsgn[4]);
+ printf("s \t\"%s\"\n", s);
+ printf("ss[] \t{\"%s\",\"%s\",\"%s\"}\n", ss[0],ss[1],ss[2]);
+ printf("rec \t%p{%p}\n",rec,rec[0]);
+}
diff --git a/type.c b/type.c
index c698817..807c400 100644
--- a/type.c
+++ b/type.c
@@ -152,7 +152,7 @@ typealign(union type t)
case TYENUM:
return targ_primalign[t.backing];
case TYARRAY:
- return targ_primalign[typechild(t).t];
+ return typealign(typechild(t));
case TYSTRUCT:
case TYUNION:
return typedata[t.dat].align;