~lsof/antcc

f453b313f62ba42d748f00628be7b3750c797c86 — lemon 9 months ago 3b96204
add initializers (only static for initialier list rn)

and other fixes
16 files changed, 948 insertions(+), 255 deletions(-)

M amd64/emit.c
M amd64/isel.c
M c.c
M c.h
M elf.c
M endian.h
M eval.c
M ir.c
M ir.h
M irdump.c
M mem.c
M obj.c
M obj.h
A test/goto.c
A test/init.c
M type.c
M amd64/emit.c => amd64/emit.c +58 -27
@@ 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)) {

M amd64/isel.c => amd64/isel.c +33 -7
@@ 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 */

M c.c => c.c +555 -68
@@ 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);

M c.h => c.h +33 -1
@@ 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,

M elf.c => elf.c +18 -61
@@ 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,

M endian.h => endian.h +14 -0
@@ 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: */

M eval.c => eval.c +50 -0
@@ 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) {

M ir.c => ir.c +8 -39
@@ 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}));
}

M ir.h => ir.h +4 -12
@@ 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);

M irdump.c => irdump.c +25 -31
@@ 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");
}


M mem.c => mem.c +13 -2
@@ 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;
}


M obj.c => obj.c +39 -5
@@ 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;
   }

M obj.h => obj.h +7 -1
@@ 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);


A test/goto.c => test/goto.c +25 -0
@@ 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));
}

A test/init.c => test/init.c +65 -0
@@ 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]);
}

M type.c => type.c +1 -1
@@ 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;