diff options
| author | 2023-06-29 09:59:30 +0200 | |
|---|---|---|
| committer | 2023-06-29 09:59:30 +0200 | |
| commit | f453b313f62ba42d748f00628be7b3750c797c86 (patch) | |
| tree | e654029d425dee2adf30c0fa2adba31d0266db1c | |
| parent | 3b96204593b9812674126bad8de14419009682c8 (diff) | |
add initializers (only static for initialier list rn)
and other fixes
| -rw-r--r-- | amd64/emit.c | 85 | ||||
| -rw-r--r-- | amd64/isel.c | 40 | ||||
| -rw-r--r-- | c.c | 623 | ||||
| -rw-r--r-- | c.h | 34 | ||||
| -rw-r--r-- | elf.c | 79 | ||||
| -rw-r--r-- | endian.h | 14 | ||||
| -rw-r--r-- | eval.c | 50 | ||||
| -rw-r--r-- | ir.c | 47 | ||||
| -rw-r--r-- | ir.h | 16 | ||||
| -rw-r--r-- | irdump.c | 56 | ||||
| -rw-r--r-- | mem.c | 15 | ||||
| -rw-r--r-- | obj.c | 44 | ||||
| -rw-r--r-- | obj.h | 8 | ||||
| -rw-r--r-- | test/goto.c | 25 | ||||
| -rw-r--r-- | test/init.c | 65 | ||||
| -rw-r--r-- | type.c | 2 |
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 */ @@ -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); @@ -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, @@ -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, @@ -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: */ @@ -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) { @@ -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})); } @@ -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); @@ -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"); } @@ -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; } @@ -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; } @@ -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]); +} @@ -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; |