From dea8fd171acb54b6d9685422d5e391fb55074008 Mon Sep 17 00:00:00 2001 From: lemon Date: Sun, 19 Oct 2025 08:09:09 +0200 Subject: Organize source files into directories --- .gitignore | 2 +- Makefile | 12 +- abi0.c | 393 ------ amd64/all.h | 2 +- amd64/emit.c | 2 +- c.c | 4054 -------------------------------------------------------- c.h | 89 -- c/c.c | 4054 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ c/c.h | 89 ++ c/eval.c | 316 +++++ c/keywords.def | 71 + c/lex.c | 1977 +++++++++++++++++++++++++++ c/lex.h | 115 ++ cfg.c | 43 - elf.c | 526 -------- elf.h | 206 --- eval.c | 316 ----- intrin.c | 78 -- intrin.def | 2 - io.c | 4 +- ir.c | 618 --------- ir.h | 280 ---- ir/abi0.c | 393 ++++++ ir/cfg.c | 43 + ir/dump.c | 255 ++++ ir/intrin.c | 77 ++ ir/intrin.def | 2 + ir/ir.c | 618 +++++++++ ir/ir.h | 280 ++++ ir/op.def | 76 ++ ir/optmem.c | 285 ++++ ir/regalloc.c | 1195 +++++++++++++++++ ir/ssa.c | 58 + irdump.c | 255 ---- keywords.def | 71 - lex.c | 1977 --------------------------- lex.h | 115 -- main.c | 2 +- obj.c | 106 -- obj.h | 30 - obj/elf.c | 525 ++++++++ obj/elf.h | 206 +++ obj/obj.c | 105 ++ obj/obj.h | 30 + op.def | 76 -- optmem.c | 285 ---- regalloc.c | 1195 ----------------- ssa.c | 59 - 48 files changed, 10782 insertions(+), 10786 deletions(-) delete mode 100644 abi0.c delete mode 100644 c.c delete mode 100644 c.h create mode 100644 c/c.c create mode 100644 c/c.h create mode 100644 c/eval.c create mode 100644 c/keywords.def create mode 100644 c/lex.c create mode 100644 c/lex.h delete mode 100644 cfg.c delete mode 100644 elf.c delete mode 100644 elf.h delete mode 100644 eval.c delete mode 100644 intrin.c delete mode 100644 intrin.def delete mode 100644 ir.c delete mode 100644 ir.h create mode 100644 ir/abi0.c create mode 100644 ir/cfg.c create mode 100644 ir/dump.c create mode 100644 ir/intrin.c create mode 100644 ir/intrin.def create mode 100644 ir/ir.c create mode 100644 ir/ir.h create mode 100644 ir/op.def create mode 100644 ir/optmem.c create mode 100644 ir/regalloc.c create mode 100644 ir/ssa.c delete mode 100644 irdump.c delete mode 100644 keywords.def delete mode 100644 lex.c delete mode 100644 lex.h delete mode 100644 obj.c delete mode 100644 obj.h create mode 100644 obj/elf.c create mode 100644 obj/elf.h create mode 100644 obj/obj.c create mode 100644 obj/obj.h delete mode 100644 op.def delete mode 100644 optmem.c delete mode 100644 regalloc.c delete mode 100644 ssa.c diff --git a/.gitignore b/.gitignore index ac09e84..7d028ae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ antcc -obj/ +build/ compile_commands.json .cache/ .gdb_history diff --git a/Makefile b/Makefile index 123409d..3d8f411 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ -SRC=main.c io.c mem.c c.c lex.c type.c targ.c eval.c ir.c irdump.c ssa.c cfg.c \ - intrin.c abi0.c optmem.c regalloc.c amd64/sysv.c amd64/isel.c amd64/emit.c obj.c elf.c\ +SRC=main.c io.c mem.c c/c.c c/lex.c c/eval.c type.c targ.c ir/ir.c ir/dump.c ir/ssa.c ir/cfg.c \ + ir/intrin.c ir/abi0.c ir/optmem.c ir/regalloc.c amd64/sysv.c amd64/isel.c amd64/emit.c obj/obj.c obj/elf.c \ embedfilesdir.c CFLAGS=-Wall -std=c11 -pedantic -OBJ=$(patsubst %.c,obj/%.o,$(SRC)) +OBJ=$(patsubst %.c,build/%.o,$(SRC)) DEP=$(OBJ:.o=.d) OUT=antcc @@ -18,12 +18,12 @@ dbg: $(OUT) $(OUT): $(OBJ) $(CC) $(CFLAGS) -o $@ $(OBJ) -obj/%.o: %.c common.h +build/%.o: %.c common.h @mkdir -p `dirname $@` - $(CC) $(CFLAGS) -MMD -MP -MT $@ -MF obj/$*.d -c -o $@ $< + $(CC) $(CFLAGS) -MMD -MP -MT $@ -MF build/$*.d -c -o $@ $< clean: - $(RM) -r obj/ $(OUT) *.o a.out + $(RM) -r build// $(OUT) *.o a.out .PHONY: clean diff --git a/abi0.c b/abi0.c deleted file mode 100644 index 82d10a5..0000000 --- a/abi0.c +++ /dev/null @@ -1,393 +0,0 @@ -#include "ir.h" - -/** This pass adds in ABI arguments/returns register mappings - ** and lowers aggregate params/args/returns into scalars - ** - ** invariant: all `call` instructions when doing this pass shall be preceded by - ** exactly narg `arg` instructions with no other instructions in between - **/ - -struct abiargsvec { vec_of(struct abiarg); }; - -static int -abiret(struct abiarg abiret[2], struct abiargsvec *abiargs, int *ni, union irtype retty) -{ - short r[2]; - uchar cls[2]; - int retreg = 0; - if (retty.isagg) { - retreg = mctarg->abiret(r, cls, ni, retty); - if (!retreg) { - vpush(abiargs, ((struct abiarg) { cls2type(KPTR), .reg = r[1] })); - if (r[0] == -1) { - memset(abiret, 0, 2*sizeof *abiret); - } else { - abiret[0].ty = cls2type(KPTR); - abiret[0].reg = r[0]; - } - } - } else if (retty.cls) { - retreg = mctarg->abiret(r, cls, ni, retty); - assert(retreg == 1); - } - for (int i = 0; i < retreg; ++i) { - abiret[i].ty = cls2type(cls[i]); - abiret[i].reg = r[i]; - } - return retreg; -} - -static int -abiarg(struct abiargsvec *abiargs, int *ni, int *nf, int *ns, union irtype ty) -{ - short r[2]; - uchar cls[2]; - int ret = mctarg->abiarg(r, cls, ni, nf, ns, ty); - if (!ret) { /* in stack */ - vpush(abiargs, ((struct abiarg) { ty, .stk = r[0] })); - } else if (ret == 1 && ty.isagg && cls[0] == KPTR) { /* aggregate by pointer */ - vpush(abiargs, ((struct abiarg) { cls2type(cls[0]), .reg = r[0] })); - } else { /* by regs */ - vpush(abiargs, ((struct abiarg) { cls2type(cls[0]), .reg = r[0] })); - if (ret == 2) - vpush(abiargs, ((struct abiarg) { cls2type(cls[1]), .reg = r[1] })); - } - return ret; -} - -static struct instr -copyparam(struct function *fn, int *curi, int param, struct abiarg abi) -{ - struct instr par = mkinstr(Oparam, abi.ty.cls, mkref(RICON, param), mktyperef(abi.ty)); - if (abi.reg >= 0) { /* reg */ - assert(!abi.ty.isagg); - return par; - } else if (!abi.ty.isagg) { /* scalar in stack */ - enum op ld; - par.cls = KPTR; - if (abi.ty.cls == KPTR) abi.ty.cls = siz2intcls[cls2siz[abi.ty.cls]]; - switch (abi.ty.cls) { - default: assert(0); - case KI4: ld = Oloadu4; break; - case KI8: ld = Oloadi8; break; - case KF4: ld = Oloadf4; break; - case KF8: ld = Oloadf8; break; - } - return mkinstr(ld, abi.ty.cls, insertinstr(fn->entry, (*curi)++, par)); - } else { /* aggregate in stack */ - par.cls = KPTR; - return par; - } -} - -static void -patchparam(struct function *fn, int *curi, int *param, int tydat, int nabi, struct abiarg abi[2]) -{ - struct block *blk = fn->entry; - assert(in_range(nabi,1,2)); - - for (; *curi < blk->ins.n; ++*curi) { - struct instr *ins = &instrtab[blk->ins.p[*curi]]; - if (ins->op != Oparam) continue; - assert(ins->r.t == RTYPE - && ins->r.i == (tydat < 0 ? abi[0].ty : (union irtype){.isagg=1, .dat=tydat}).bits); - if (abi[0].ty.isagg || tydat < 0) { - /* aggregate in stack or scalar, just copy */ - assert(nabi == 1); - *ins = copyparam(fn, curi, *param, abi[0]); - } else { /* aggregate in registers, materialize */ - union ref alloc, r[2]; - struct instr st; - const struct typedata *td; - uint nalloc; - - assert(tydat >= 0); - td = &typedata[tydat]; - assert(td->siz <= 16 && td->align <= 16); - nalloc = td->align == 16 ? 1 : td->siz/8 + (td->siz%8 != 0); - *ins = mkinstr(Oalloca8 + (td->align==16), KPTR, mkref(RICON, nalloc)); - alloc = mkref(RTMP, ins - instrtab); - r[0] = insertinstr(blk, ++*curi, copyparam(fn, NULL, *param, abi[0])); - if (nabi > 1) - r[1] = insertinstr(blk, ++*curi, copyparam(fn, NULL, ++*param, abi[1])); - /* transform - * %x = copy %p - * into - * %x = alloca... - * store* %x, %a - * store* %x + N, %b - */ - st = mkinstr(Ostore1 + ilog2(cls2siz[abi[0].ty.cls]), 0, alloc, r[0]); - insertinstr(blk, ++*curi, st); - if (nabi > 1) { - struct instr tmp = mkinstr(Oadd, KPTR, alloc, mkref(RICON, cls2siz[abi[0].ty.cls])); - st = mkinstr(Ostore1 + ilog2(cls2siz[abi[1].ty.cls]), 0, insertinstr(blk, ++*curi, tmp), r[1]); - insertinstr(blk, ++*curi, st); - } - } - ++*param; - ++*curi; - break; - } -} - -static int -patcharg(struct block *blk, int *icall, struct call *call, - int argidx, int nabi, struct abiarg abi[2]) -{ - int arginst = *icall - (call->narg - argidx); - struct instr *arg = &instrtab[blk->ins.p[arginst]]; - assert(arg->op == Oarg && arg->l.t == RTYPE); - if (ref2type(arg->l).isagg) { - /* aggregate argument */ - if (abi[0].ty.isagg /* aggregate in stack */ - || abi[0].ty.cls == KPTR) /* aggregate by pointer */ - { - return 1; - } else { /* aggregate in registers */ - union ref src = arg->r; - /* deconstruct into - * %a = load* %x - * (%b = load* %x + N) - */ - delinstr(blk, arginst); - for (int i = 0; i < nabi; ++i) { - /* XXX this can generate unaligned loads */ - struct instr ins = {0}; - union ref temp; - switch (ins.cls = abi[i].ty.cls) { - default: assert(0); - case KI4: ins.op = Oloadu4; break; - case KI8: ins.op = Oloadi8; break; - case KF4: ins.op = Oloadf4; break; - case KF8: ins.op = Oloadf8; break; - } - if (i == 0) - ins.l = src; - else - ins.l = insertinstr(blk, arginst++, - mkinstr(Oadd, KPTR, src, - mkref(RICON, cls2siz[abi[0].ty.cls]))); - temp = insertinstr(blk, arginst++, ins); - insertinstr(blk, arginst++, mkarginstr(abi[i].ty, temp)); - } - *icall = arginst + (call->narg - argidx - 1); - return nabi; - } - } else /* normal scalar argument */ - return 1; -} - -static struct abiarg abiargsbuf[32]; - -void -abi0_call(struct function *fn, struct instr *ins, struct block *blk, int *curi) -{ - union ref retmem; - struct abiargsvec abiargs = {VINIT(abiargsbuf, arraylength(abiargsbuf))}; - bool sretarghidden = 0; - int ni, nf, ns, vararg, nret = 0; - struct call *call = &calltab.p[ins->r.i]; - - vararg = call->vararg; - vinit(&abiargs, abiargsbuf, arraylength(abiargsbuf)); - ni = nf = ns = 0; - assert(!ins->cls == !call->ret.bits); - nret = abiret(call->abiret, &abiargs, &ni, call->ret); - if (call->ret.isagg) { /* adjust struct return */ - union irtype retty = call->ret; - struct typedata *td = &typedata[retty.dat]; - struct instr alloca = mkalloca(td->siz, td->align); - int ialloca; - - sretarghidden = ni == 0; - /* swap alloca and call temps so users of original call point to alloca */ - retmem = insertinstr(blk, ialloca = (*curi)++ - call->narg, *ins); - *ins = alloca; - blk->ins.p[ialloca] = ins - instrtab; - blk->ins.p[*curi] = retmem.i; - ins = &instrtab[retmem.i]; - retmem.i = blk->ins.p[ialloca]; - - if (!nret) /* hidden pointer argument */ - insertinstr(blk, (*curi)++ - call->narg, - mkinstr(Oarg, 0, mktyperef((union irtype){.cls=KPTR}), retmem)); - } - - /* adjust args */ - for (int i = 0, i2 = ni + sretarghidden; i < call->narg; ++i) { - int arginst = *curi - (call->narg - i); - struct instr *arg = &instrtab[blk->ins.p[arginst]]; - union irtype pty = ref2type(arg->l); - int first = abiargs.n; - int ret = abiarg(&abiargs, &ni, &nf, &ns, pty); - ret = patcharg(blk, curi, call, i, ret, &abiargs.p[first]); - if (call->vararg == i) vararg = i2; - i2 += ret; - } - /* adjust return */ - if (call->ret.isagg) { - ins->cls = 0; - if (!nret) { /* hidden pointer argument */ - ins->cls = 0; - if (call->abiret[0].reg >= 0) { - /* the result location pointer is also returned by the callee, e.g. in x86 */ - ins->cls = KPTR; - ++nret; - /* even if this is not used, the register copy - * must be emitted for the register allocator to know */ - } - } else { /* aggregate returned in regs */ - union ref r[2]; - struct instr ret2; - assert(in_range(nret, 1, 2)); - ins->cls = call->abiret[0].ty.cls; - r[0] = mkref(RTMP, ins - instrtab); - if (nret == 2) { - ret2 = mkinstr(Ocall2r, call->abiret[1].ty.cls, r[0]); - r[1] = insertinstr(blk, ++*curi, ret2); - } - for (int i = 0; i < nret; ++i) { - struct instr store = {0}; - /* XXX this can generate unaligned stores */ - switch (call->abiret[i].ty.cls) { - default: assert(0); - case KF4: case KI4: store.op = Ostore4; break; - case KI8: case KF8: store.op = Ostore8; break; - } - if (i == 0) { - store.l = retmem; - } else { - union ref off = mkref(RICON, cls2siz[call->abiret[0].ty.cls]); - struct instr addr = mkinstr(Oadd, KPTR, retmem, off); - store.l = insertinstr(blk, ++*curi, addr); - } - store.r = r[i]; - insertinstr(blk, ++*curi, store); - } - } - } - - if (call->ret.isagg) call->ret = (union irtype){0}; - call->vararg = vararg; - call->abiarg = alloccopy(fn->arena, abiargs.p, abiargs.n * sizeof(struct abiarg), 0); - call->narg = abiargs.n; - vfree(&abiargs); -} - -void -abi0(struct function *fn) -{ - uint nparam = typedata[fn->fnty.dat].nmemb; - const union type *paramty = typedata[fn->fnty.dat].param; - struct abiargsvec abiargs = {VINIT(abiargsbuf, arraylength(abiargsbuf))}; - int rvovar = -1; - int ni = 0, nf = 0, ns = 0, istart = 0; - struct block *blk; - union ref sret = {0}; - - if (fn->retty.t == TYVOID) { - fn->nabiret = 0; - } else { - fn->nabiret = abiret(fn->abiret, &abiargs, &ni, mkirtype(fn->retty)); - if (!fn->nabiret && isagg(fn->retty)) { /* ret agg by hidden pointer */ - struct instr param = copyparam(fn, NULL, 0, abiargs.p[0]); - sret = insertinstr(fn->entry, 0, param); - ++istart; - /* increment real param ordinals */ - for (int i = 1; i < fn->entry->ins.n; ++i) { - struct instr *ins = &instrtab[fn->entry->ins.p[i]]; - if (ins->op == Oparam) ++ins->l.i; - } - } - } - - /* adjust params */ - for (int i = 0, param = abiargs.n; i < nparam; ++i) { - union irtype pty = mkirtype(paramty[i]); - int first = abiargs.n; - int ret = abiarg(&abiargs, &ni, &nf, &ns, pty); - patchparam(fn, &istart, ¶m, pty.isagg ? pty.dat : -1, ret+!ret, &abiargs.p[first]); - } - fn->abiarg = alloccopy(fn->arena, abiargs.p, abiargs.n * sizeof *abiargs.p, 0); - fn->nabiarg = abiargs.n; - vfree(&abiargs); - - if (!fn->nabiret && isagg(fn->retty)) { - /* for structures returned by hidden pointer argument, - * if all return instrs return local var X, make X point to the result location, - * (return value optimization (RVO)) */ - blk = fn->entry; - do { - union ref arg = blk->jmp.arg[0]; - if (blk->jmp.t != Jret) continue; - if (!arg.bits) continue; - if (arg.t != RTMP || !oisalloca(instrtab[arg.i].op)) { - rvovar = -1; - break; - } - if (rvovar == -1) { - rvovar = arg.i; - } else if (arg.i != rvovar) { - rvovar = -1; - break; - } - } while ((blk = blk->lnext) != fn->entry); - if (rvovar != -1) - instrtab[rvovar] = mkinstr(Ocopy, KPTR, sret); - } - - blk = fn->entry->lnext; - do { - /* adjust calls */ - for (int iinstr = 0; iinstr < blk->ins.n; ++iinstr) { - struct instr *ins = &instrtab[blk->ins.p[iinstr]]; - if (ins->op != Ocall) continue; - abi0_call(fn, ins, blk, &iinstr); - } - - /* adjust returns */ - if (isagg(fn->retty) && blk->jmp.t == Jret && blk->jmp.arg[0].bits) { - assert(!blk->jmp.arg[1].bits); - if (fn->nabiret) { /* aggregate return in register(s) */ - union ref src = blk->jmp.arg[0]; - for (int i = 0; i < fn->nabiret; ++i) { - /* XXX this can generate unaligned loads */ - struct instr ins = {0}; - switch (ins.cls = fn->abiret[i].ty.cls) { - default: assert(0); - case KI4: ins.op = Oloadu4; break; - case KI8: ins.op = Oloadi8; break; - case KF4: ins.op = Oloadf4; break; - case KF8: ins.op = Oloadf8; break; - } - if (i == 0) - ins.l = src; - else - ins.l = insertinstr(blk, blk->ins.n, - mkinstr(Oadd, KPTR, src, - mkref(RICON, cls2siz[fn->abiret[0].ty.cls]))); - blk->jmp.arg[i] = insertinstr(blk, blk->ins.n, ins); - } - } else { - /* aggregate return (arg[0] is pointer to return value) */ - if (rvovar == -1) { - /* blit %sret, %arg */ - union irtype typ = mkirtype(fn->retty); - insertinstr(blk, blk->ins.n, mkarginstr(typ, sret)); - insertinstr(blk, blk->ins.n, mkarginstr(typ, blk->jmp.arg[0])); - insertinstr(blk, blk->ins.n, mkintrin(INstructcopy, 0, 2)); - } else assert(blk->jmp.arg[0].bits == mkref(RTMP, rvovar).bits); - if (fn->abiret[0].ty.cls) blk->jmp.arg[0] = rvovar == -1 ? sret : mkref(RTMP, rvovar); - else memset(blk->jmp.arg, 0, sizeof blk->jmp.arg); - } - } - } while ((blk = blk->lnext) != fn->entry); - - if (ccopt.dbg.a) { - efmt("<< After abi0 >>\n"); - irdump(fn); - } -} - -/* vim:set ts=3 sw=3 expandtab: */ diff --git a/amd64/all.h b/amd64/all.h index 42f4d23..992d47e 100644 --- a/amd64/all.h +++ b/amd64/all.h @@ -1,4 +1,4 @@ -#include "../ir.h" +#include "../ir/ir.h" #define LIST_REGS(_) \ _(RAX) _(RCX) _(RDX) _(RBX) _(RSP) _(RBP) _(RSI) _(RDI) \ diff --git a/amd64/emit.c b/amd64/emit.c index a6ce774..1db5093 100644 --- a/amd64/emit.c +++ b/amd64/emit.c @@ -1,5 +1,5 @@ #include "all.h" -#include "../obj.h" +#include "../obj/obj.h" #include "../endian.h" /** Instruction operands ** diff --git a/c.c b/c.c deleted file mode 100644 index ac3b863..0000000 --- a/c.c +++ /dev/null @@ -1,4054 +0,0 @@ -#include "c.h" -#include "lex.h" -#include "ir.h" -#include "endian.h" -#include "obj.h" - -/** C compiler state **/ -struct comp { - struct lexer lx; - struct env *env; - struct arena *fnarena, *exarena; - struct span fnblkspan; - uint loopdepth, switchdepth; - struct block *breakto, *loopcont; - struct switchstmt *switchstmt; - struct label *labels; -}; - -/** Parsing helper functions **/ -#define peek(Cm,Tk) lexpeek(&(Cm)->lx,Tk) -static int -lexc(struct comp *cm, struct token *tk) -{ - struct token tk2; - int t = lex(&cm->lx, tk); - if (t == TKSTRLIT && peek(cm, &tk2) == TKSTRLIT && tk2.wide == tk->wide) { - /* 5.1.1.2 Translation phase 6: concatenate adjacent string literal tokens */ - static char buf[200]; - vec_of(char) rest = VINIT(buf, sizeof buf); - do { - lex(&cm->lx, NULL); - if (tk) { - joinspan(&tk->span.ex, tk2.span.ex); - if (!tk->wide) - vpushn(&rest, tk2.s, tk2.len); - else if (tk->wide && targ_primsizes[targ_wchartype] == 2) - vpushn(&rest, tk2.ws16, tk2.len*2); - else - vpushn(&rest, tk2.ws32, tk2.len*4); - } - } while (peek(cm, &tk2) == TKSTRLIT && tk2.wide == tk->wide); - if (tk) { - if (!tk->wide) { - tk->s = memcpy(alloc(&cm->exarena, tk->len + rest.n, 1), tk->s, tk->len); - memcpy((char *)tk->s + tk->len, rest.p, rest.n); - tk->len += rest.n; - } else if (tk->wide == 1) { - tk->ws16 = memcpy(alloc(&cm->exarena, tk->len + rest.n*2, 2), tk->ws16, tk->len*2); - memcpy((short *)tk->s + tk->len, rest.p, rest.n); - tk->len += rest.n * 2; - } else { - tk->ws32 = memcpy(alloc(&cm->exarena, tk->len + rest.n*4, 4), tk->ws32, tk->len*4); - memcpy((int *)tk->s + tk->len, rest.p, rest.n); - tk->len += rest.n * 4; - } - } - vfree(&rest); - } - return t; -} -#define lex(Cm,Tk) lexc(Cm,Tk) -static bool -match(struct comp *cm, struct token *tk, enum toktag t) -{ - if (peek(cm, NULL) == t) { - lex(cm, tk); - return 1; - } - return 0; -} -static bool -expect(struct comp *cm, enum toktag t, const char *s) -{ - struct token tk; - if (!match(cm, &tk, t)) { - peek(cm, &tk); - if (aisprint(t)) tk.span.ex.len = tk.span.sl.len = 1; - error(&tk.span, "expected %'tt%s%s", t, s?" ":"",s ? s : ""); - return 0; - } - return 1; -} -static struct token -expectdie(struct comp *cm, enum toktag t, const char *s) -{ - struct token tk; - if (!match(cm, &tk, t)) - fatal(&tk.span, "expected %'tt%s%s", t, s?" ":"",s ? s : ""); - return tk; -} - -/******************************************/ -/* Data structures for declaration parser */ -/******************************************/ - -enum declkind { - DTOPLEVEL, - DFUNCPARAM, - DFUNCVAR, - DFIELD, - DCASTEXPR, -}; - -/* Since a declaration can have multiple declarators, and we need to process - * each one individually, the declaration parser is a state machine - * (conceptually a generator coroutine); the state is zero-initialized (except - * for the .kind field), each call to pdecl yields the next individual decl, - * st.more indicates whether there are more decls left to parse (the coroutine - * has yielded), or this declaration list is done (the coroutine has finalized) - */ -struct declstate { - enum declkind kind; - union type base; - enum storageclass scls; - enum qualifier qual; - uint align; - bool more, /* caller should keep calling pdecl to get next decl */ - varini, /* caller should parse an initializer ('=' ) and - call pdecl() to advance state before checking .more */ - funcdef, /* caller should parse an func definition ('{' '}'). - the declaration list is finished. */ - bitf, /* caller should parse a bitfield size and - call pdecl() to advance state before checking .more */ - tagdecl; - const char **pnames; /* param names for function definition */ - struct span *pspans; /* param spans ditto */ -}; -static struct decl pdecl(struct declstate *st, struct comp *cm); - -static struct decl *finddecl(struct comp *cm, const char *name); - -/* next token starts a decl? */ -static bool -isdecltok(struct comp *cm) -{ - struct decl *decl; - struct token tk; - switch (peek(cm, &tk)) { - case TKWsigned: case TKWunsigned: case TKWshort: case TKWlong: - case TKWint: case TKWchar: case TKW_Bool: case TKWauto: - case TKWstruct: case TKWunion: case TKWenum: case TKWtypedef: - case TKWextern: case TKWstatic: case TKWinline: case TKW_Noreturn: - case TKWconst: case TKWvolatile: case TKWvoid: case TKWfloat: - case TKWdouble: case TKWregister: case TKW_Static_assert: - case TKW__typeof__: case TKWtypeof: case TKWtypeof_unqual: - return 1; - case TKIDENT: - return (decl = finddecl(cm, tk.s)) && decl->scls == SCTYPEDEF; - } - return 0; -} - - -/**********************************/ -/* Environment (scope) management */ -/**********************************/ - -static struct decl envdeclsbuf[1<<10]; -static vec_of(struct decl) envdecls = VINIT(envdeclsbuf, arraylength(envdeclsbuf)); -struct tagged { /* a tagged type declaration */ - union type ty; - struct span span; -}; -static struct tagged envtaggedbuf[1<<10]; -static vec_of(struct tagged) envtagged = VINIT(envtaggedbuf, arraylength(envtaggedbuf)); -struct env { - struct env *up; - /* list of decls is implicitly envdecls[decl..ndecl] */ - ushort decl, ndecl; - /* ditto for envtagged[] */ - ushort tagged, ntagged; -}; -static struct env toplevel; - -static void -envdown(struct comp *cm, struct env *e) -{ - assert(cm->env->decl + cm->env->ndecl == envdecls.n); - assert(cm->env->tagged + cm->env->ntagged == envtagged.n); - e->decl = envdecls.n; - e->tagged = envtagged.n; - e->ndecl = e->ntagged = 0; - e->up = cm->env; - cm->env = e; -} - -static void -envup(struct comp *cm) -{ - struct env *env = cm->env; - assert(env->decl + env->ndecl == envdecls.n); - envdecls.n -= env->ndecl; - envtagged.n -= env->ntagged; - assert(env->up); - cm->env = env->up; -} - -static struct decl * -envadddecl(struct env *env, const struct decl *d) -{ - assert(env->decl + env->ndecl == envdecls.n); - vpush(&envdecls, *d); - ++env->ndecl; - return &envdecls.p[envdecls.n - 1]; -} - -/* iters in reversed order of insertion (most to least recent) */ -/* use like so: for (d = NULL; enviterdecl(&d, env);) ... */ -static inline bool -enviterdecl(struct decl **d, struct env *env) -{ - if (!env->ndecl) return 0; - if (!*d) *d = &envdecls.p[env->decl + env->ndecl - 1]; - else if (*d == &envdecls.p[env->decl]) return 0; - else --*d; - return 1; -} - -static struct tagged * -envaddtagged(struct env *env, union type ty, const struct span *span) -{ - struct tagged tagged = { ty, *span }; - assert(env->tagged + env->ntagged == envtagged.n); - vpush(&envtagged, tagged); - ++env->ntagged; - return &envtagged.p[envtagged.n - 1]; -} - -/* like enviterdecl */ -static inline bool -envitertagged(struct tagged **l, struct env *env) -{ - if (!env->ntagged) return 0; - if (!*l) *l = &envtagged.p[env->tagged + env->ntagged - 1]; - else if (*l == &envtagged.p[env->tagged]) return 0; - else --*l; - return 1; -} - -static bool -redeclarationok(const struct decl *old, const struct decl *new) -{ - if (old->scls != new->scls) return 0; - switch (old->scls) { - case SCSTATIC: - if (old->ty.t != TYFUNC) - break; - /*fallthru*/ - case SCEXTERN: - if (old->ty.t == TYARRAY && new->ty.t == TYARRAY - && typechild(old->ty).bits == typechild(new->ty).bits - && isincomplete(old->ty)) - { - return 1; - } - /*fallthru*/ - case SCTYPEDEF: - return old->ty.bits == new->ty.bits; - } - return 0; -} - -static struct decl * -putdecl(struct comp *cm, const struct decl *decl) -{ - struct decl *l; - for (l = NULL; enviterdecl(&l, cm->env);) { - if (decl->name == l->name) { - if (l->isdef && decl->isdef) { - error(&decl->span, "redefinition of '%s'", decl->name); - note(&l->span, "previously defined here"); - break; - } else if (!redeclarationok(l, decl)) { - error(&decl->span, "incompatible redeclaration of '%s'", decl->name); - note(&l->span, "previously declared here"); - break; - } - } - } - l = envadddecl(cm->env, decl); - return l; -} - -static struct decl * -finddecl(struct comp *cm, const char *name) -{ - struct env *e; - struct decl *l; - assert(name); - for (e = cm->env; e; e = e->up) { - for (l = NULL; enviterdecl(&l, e);) { - if (name == l->name) - return l; - } - } - return NULL; -} - -static union type -gettagged(struct comp *cm, struct span *span, enum typetag tt, const char *name, bool dodef) -{ - struct env *e; - struct tagged *l; - struct typedata td = {0}; - assert(name); - for (e = cm->env; e; e = e->up) { - for (l = NULL; envitertagged(&l, e);) { - if (name == ttypenames[typedata[l->ty.dat].id]) { - if (dodef && e != cm->env) - goto Break2; - *span = l->span; - return l->ty; - } - } - } -Break2: - if (tt == TYENUM) - return mktype(0); - td.t = tt; - return envaddtagged(cm->env, mktagtype(name, &td), span)->ty; -} - -static union type -deftagged(struct comp *cm, struct span *span, enum typetag tt, const char *name, union type ty) -{ - struct tagged *l; - struct typedata td = {0}; - assert(name); - for (l = NULL; envitertagged(&l, cm->env);) { - if (name == ttypenames[typedata[l->ty.dat].id]) { - *span = l->span; - return l->ty; - } - } - td.t = tt; - return envaddtagged(cm->env, ty.t ? ty : mktagtype(name, &td), span)->ty; -} - -/*********************/ -/* Expr Typechecking */ -/*********************/ - -#define iszero(ex) ((ex).t == ENUMLIT && isint((ex).ty) && (ex).u == 0) - -static bool -islvalue(const struct expr *ex) -{ - if (ex->t == EGETF) return islvalue(ex->sub); - return ex->t == ESYM || ex->t == EDEREF || ex->t == EINIT; -} - -static union type /* 6.5.2.6 default argument promotions */ -argpromote(union type t) -{ - if (isint(t)) t.t = intpromote(t.t); - else if (t.t == TYFLOAT) t.t = TYDOUBLE; - else if (t.t == TYARRAY) return mkptrtype(typechild(t), t.flag & TFCHLDQUAL); - return t; -} - -static bool -assigncheck(union type t, const struct expr *src) -{ - if (assigncompat(t, typedecay(src->ty))) return 1; - if (t.t == TYPTR && iszero(*src)) return 1; - return 0; -} - -static bool -initcheck(union type t, const struct expr *src) -{ - if (assigncheck(t, src)) return 1; - if (t.bits == src->ty.bits && (src->t == EINIT || src->t == ESTRLIT)) return 1; - return 0; -} - -static void -incdeccheck(enum toktag tt, const struct expr *ex, const struct span *span) -{ - if (!isscalar(ex->ty)) - error(&ex->span, "invalid operand to %tt '%ty'", tt, ex->ty); - else if (!islvalue(ex)) - error(&ex->span, "operand to %tt is not an lvalue", tt); - else if (ex->ty.t == TYPTR && isincomplete(typechild(ex->ty))) - error(span, "arithmetic on pointer to incomplete type '%ty'", ex->ty); - else if (ex->ty.t == TYPTR && typechild(ex->ty).t == TYFUNC) - error(span, "arithmetic on function pointer '%ty'", ex->ty); -} - -static bool /* 6.5.4 Cast operators */ -castcheck(union type to, const struct expr *ex) -{ - union type src = ex->ty; - if (to.t == TYVOID) return 1; - if (isagg(to)) return 0; - if (to.bits == src.bits) return 1; - if (isarith(to) && isarith(src)) return 1; - if (isint(to) && isptrcvt(src)) return 1; - if (to.t == TYPTR && isint(src)) return 1; - if (to.t == TYPTR && isptrcvt(src)) return 1; - return 0; -} - -static union type /* 6.5.2.1 Array subscripting */ -subscriptcheck(const struct expr *ex, const struct expr *rhs, const struct span *span) -{ - union type ty; - if (ex->ty.t == TYPTR || ex->ty.t == TYARRAY) { - if (isincomplete(ty = typechild(ex->ty))) { - error(span, "cannot dereference pointer to incomplete type '%ty'", ty); - ty = mktype(TYINT); - } else if (ty.t == TYFUNC) { - error(span, "subscripted value is pointer to function"); - ty = mktype(TYINT); - } - } else { - error(&ex->span, "subscripted value is not pointer-convertible '%ty'", ex->ty); - ty = mktype(TYINT); - } - if (!isint(rhs->ty)) - error(&rhs->span, "array subscript is not integer ('%ty')", rhs->ty); - return ty; -} - -static void /* 6.5.3.4 The sizeof operator */ -sizeofcheck(const struct span *span, union type ty) -{ - if (isincomplete(ty)) - error(span, "cannot apply sizeof to incomplete type '%ty'", ty); - else if (ty.t == TYFUNC) - error(span, "cannot apply sizeof to function type '%ty'", ty); -} - -static bool /* 6.5.8 Relational operators */ -relationalcheck(const struct expr *a, const struct expr *b) -{ - union type t1 = a->ty, t2 = b->ty; - if (isarith(t1) && isarith(t2)) return 1; - if (isptrcvt(t1) && isptrcvt(t2)) { - t1 = typedecay(t1); - t2 = typedecay(t2); - return t1.dat == t2.dat; - } - return 0; -} - -static bool -isnullpo(const struct expr *ex) /* match '0' or '(void *) 0' */ -{ - static const union type voidptr = {{ TYPTR, .flag = TFCHLDPRIM, .child = TYVOID }}; - if (ex->t == ECAST && ex->ty.bits == voidptr.bits) - ex = ex->sub; - return iszero(*ex); -} - -static bool /* 6.5.9 Equality operators */ -equalitycheck(const struct expr *a, const struct expr *b) -{ - union type t1 = a->ty, t2 = b->ty; - if (isarith(t1) && isarith(t2)) return 1; - if (isptrcvt(t1) && isptrcvt(t2)) { - t1 = typedecay(t1); - t2 = typedecay(t2); - return t1.dat == t2.dat || typechild(t1).t == TYVOID || typechild(t2).t == TYVOID; - } - if (isptrcvt(t1) && isnullpo(b)) return 1; - return isptrcvt(t2) && isnullpo(a); -} - -static union type /* 6.5.15 Conditional operator */ -condtype(const struct expr *a, const struct expr *b) -{ - union type t1 = typedecay(a->ty), t2 = typedecay(b->ty), s1, s2; - if (isarith(t1) && isarith(t2)) return cvtarith(t1, t2); - if (t1.bits == t2.bits) return t1; - if (t1.t == TYPTR && t2.t == TYPTR) { - s1 = typechild(t1); - s2 = typechild(t2); - if (s1.bits == s2.bits || s2.t == TYVOID || s1.t == TYVOID) { - return mkptrtype(s1.t == TYVOID ? s1 : s2, (t1.flag | t2.flag) & TFCHLDQUAL); - } - } - if (t1.t == TYPTR && isnullpo(b)) return t1; - if (isnullpo(a) && t2.t == TYPTR) return t2; - return mktype(0); -} - -static void -bintypeerr(const struct span *span, enum toktag tt, union type lhs, union type rhs) -{ - error(span, "bad operands to %tt: '%ty', '%ty'", tt, lhs, rhs); -} - -enum binopclass { /* binary operator type-checking classes */ - BCSET = 1<<7, /* is a (compound) assignment operator? */ - BCSEQ = 1, BCADDITIVE, BCARITH, BCINT, BCSHFT, BCEQL, BCCMP, BCLOG, -}; - -/* table indexed by binary op token; - * containing precedence level, expression kind and type-checking class */ -static const struct { uchar prec, t, k; } bintab[] = { - ['*'] = {13, EMUL, BCARITH}, - ['/'] = {13, EDIV, BCARITH}, - ['%'] = {13, EREM, BCINT}, - ['+'] = {12, EADD, BCADDITIVE}, - ['-'] = {12, ESUB, BCADDITIVE}, - [TKSHL] = {11, ESHL, BCSHFT}, - [TKSHR] = {11, ESHR, BCSHFT}, - ['<'] = {10, ELTH, BCCMP}, - ['>'] = {10, EGTH, BCCMP}, - [TKLTE] = {10, ELTE, BCCMP}, - [TKGTE] = {10, EGTE, BCCMP}, - [TKEQU] = {9, EEQU, BCEQL}, - [TKNEQ] = {9, ENEQ, BCEQL}, - ['&'] = {8, EBAND, BCINT}, - ['^'] = {7, EXOR, BCINT}, - ['|'] = {6, EBIOR, BCINT}, - [TKLOGAND] = {5, ELOGAND, BCLOG}, - [TKLOGIOR] = {4, ELOGIOR, BCLOG}, - ['?'] = {3, ECOND}, /* not actually a binop (special cased) */ - ['='] = {2, ESET, BCSET}, - [TKSETADD] = {2, ESETADD, BCSET|BCADDITIVE}, [TKSETSUB] = {2, ESETSUB, BCSET|BCADDITIVE}, - [TKSETMUL] = {2, ESETMUL, BCSET|BCARITH}, [TKSETDIV] = {2, ESETDIV, BCSET|BCARITH}, - [TKSETREM] = {2, ESETREM, BCSET|BCINT}, [TKSETAND] = {2, ESETAND, BCSET|BCINT}, - [TKSETIOR] = {2, ESETIOR, BCSET|BCINT}, [TKSETXOR] = {2, ESETXOR, BCSET|BCINT}, - [TKSETSHL] = {2, ESETSHL, BCSET|BCSHFT}, [TKSETSHR] = {2, ESETSHR, BCSET|BCSHFT}, - [','] = {1, ESEQ, BCSEQ} -}; - -static union type -bintypecheck(const struct span *span, enum toktag tt, struct expr *lhs, struct expr *rhs) -{ - enum binopclass k = bintab[tt].k; - union type ty = lhs->ty; - - assert(k); - if (k & BCSET) { - if (!islvalue(lhs)) - error(&lhs->span, "left-hand-side of assignment is not an lvalue"); - else if (lhs->qual & QCONST) - error(&lhs->span, "cannot assign to const-qualified lvalue (%tq)", ty, lhs->qual); - else if (isincomplete(ty)) - error(&lhs->span, "cannot assign to incomplete type '%ty'", ty); - else if (ty.t == TYARRAY) - error(&lhs->span, "cannot assign to array type '%ty'", ty); - else if (ty.t == TYFUNC) - error(&lhs->span, "cannot assign to function designator '%ty'", lhs->ty); - } - switch (k &~ BCSET) { - case 0: - if (isagg(ty) && !(lhs->qual & QCONST) && typedata[ty.dat].anyconst) - error(&lhs->span, "cannot assign to aggregate with const-qualified member"); - if (!assigncheck(ty, rhs)) - goto Error; - break; - case BCSEQ: - ty = rhs->ty; - break; - case BCADDITIVE: - if (tt == '+' && isptrcvt(rhs->ty)) { - /* int + ptr -> ptr + int (for convenience) */ - const struct expr swaptmp = *lhs; - *lhs = *rhs; - *rhs = swaptmp; - ty = lhs->ty; - } - if (isarith(ty) && isarith(rhs->ty)) { - /* num +/- num */ - ty = cvtarith(ty, rhs->ty); - assert(ty.t); - } else if ((ty.t == TYPTR || ty.t == TYARRAY) && isint(rhs->ty)) { - /* ptr +/- int */ - union type pointee = typechild(ty); - if (isincomplete(pointee)) - error(span, "arithmetic on pointer to incomplete type '%ty'", ty); - else if (pointee.t == TYFUNC) - error(span, "arithmetic on function pointer '%ty'", ty); - ty = typedecay(ty); - } else if (tt == '-' && isptrcvt(ty) && isptrcvt(rhs->ty)) { - /* ptr - ptr */ - union type pointee1 = typechild(typedecay(ty)), - pointee2 = typechild(typedecay(rhs->ty)); - if (isincomplete(pointee1)) - error(span, "arithmetic on pointer to incomplete type '%ty'", ty); - else if (pointee1.t == TYFUNC) - error(span, "arithmetic on function pointer '%ty'", lhs->ty); - else if (pointee1.bits != pointee2.bits) { - error(span, "arithmetic on incompatible pointer types: '%ty', '%ty'", - ty, rhs->ty); - } - ty = mktype(targ_ptrdifftype); - } else goto Error; - break; - case BCARITH: - ty = cvtarith(ty, rhs->ty); - if (!ty.t) { - ty.t = TYINT; - Error: - bintypeerr(span, tt, lhs->ty, rhs->ty); - } - break; - case BCINT: - if (!isint(ty) || !isint(rhs->ty)) - goto Error; - ty = cvtarith(ty, rhs->ty); - assert(ty.t); - break; - case BCSHFT: /* 6.5.7 Bitwise shift operators */ - if (!isint(ty) || !isint(rhs->ty)) - goto Error; - ty.t = intpromote(ty.t); - assert(ty.t); - break; - case BCEQL: - if (!equalitycheck(lhs, rhs)) - goto Error; - ty = mktype(TYINT); - break; - case BCCMP: - if (!relationalcheck(lhs, rhs)) - goto Error; - ty = mktype(TYINT); - break; - case BCLOG: /* 6.5.13-14 Logical AND/OR operator */ - if (!isscalar(typedecay(ty)) || !isscalar(typedecay(rhs->ty))) - goto Error; - ty = mktype(TYINT); - break; - } - return (k & BCSET) || !ty.t ? lhs->ty : ty; -} - -/****************/ -/* Expr Parsing */ -/****************/ - -#define mkexpr(t_,span_,ty_,...) ((struct expr){.t=(t_), .ty=(ty_), .span=(span_), __VA_ARGS__}) - -static struct expr * -exprdup(struct comp *cm, const struct expr *e) -{ - return alloccopy(&cm->exarena, e, sizeof *e, 0); -} -static struct expr * -exprdup2(struct comp *cm, const struct expr *e1, const struct expr *e2) -{ - struct expr *r = alloc(&cm->exarena, 2*sizeof *r, 0); - r[0] = *e1, r[1] = *e2; - return r; -} - -static struct expr expr(struct comp *cm); -static struct expr commaexpr(struct comp *cm); - -static struct expr /* 6.5.2.2 Function calls */ -callexpr(struct comp *cm, const struct span *span_, const struct expr *callee) -{ - struct token tk; - struct expr ex, arg; - struct span span = callee->span; - union type ty = callee->ty; - const struct typedata *td = &typedata[ty.dat]; - struct expr argbuf[10]; - vec_of(struct expr) args = VINIT(argbuf, arraylength(argbuf)); - bool spanok = joinspan(&span.ex, span_->ex); - bool printsig = 0; - - if (callee->t == ESYM && !callee->ty.t) { /* implicit function decl.. */ - const char *name = (void *)callee->sym; - struct decl decl = { - ty = mkfntype(mktype(TYINT), 0, NULL, NULL, /* kandr */ 1, 0), - .scls = SCEXTERN, .span = callee->span, .name = name - }; - warn(&callee->span, "call to undeclared function '%s'", name); - ((struct expr *)callee)->ty = decl.ty; - ((struct expr *)callee)->sym = putdecl(cm, &decl); - td = &typedata[ty.dat]; - } - - if (ty.t == TYPTR) /* auto-deref when calling a function pointer */ - ty = typechild(ty); - if (ty.t != TYFUNC) error(&callee->span, "calling a value of type '%ty'", callee->ty); - if (!match(cm, &tk, ')')) for (;;) { - arg = expr(cm); - spanok = spanok && joinspan(&span.ex, callee->span.ex); - if (ty.t == TYFUNC && args.n == td->nmemb && !td->variadic && !td->kandr) { - error(&arg.span, "too many args to function taking %d params", td->nmemb); - printsig = 1; - } - if (ty.t == TYFUNC && args.n < td->nmemb && !td->kandr) { - if (!assigncheck(td->param[args.n], &arg)) { - error(&arg.span, "arg #%d of type '%ty' is incompatible with '%ty'", - args.n+1, arg.ty, td->param[args.n]); - printsig = 1; - } - } - vpush(&args, arg); - peek(cm, &tk); - if (match(cm, &tk, ',')) { - spanok = spanok && joinspan(&span.ex, tk.span.ex); - } else if (expect(cm, ')', "or ',' after arg")) { - break; - } - } - if (!spanok || !joinspan(&span.ex, tk.span.ex)) span = *span_; - - if (!td->variadic && !td->kandr && args.n < td->nmemb) { - error(&tk.span, "not enough args to function taking %d param%s", - td->nmemb, td->nmemb != 1 ? "s" : ""); - printsig = 1; - } - if (printsig) note(&callee->span, "function signature is '%ty'", ty); - - ex = mkexpr(ECALL, span, ty.t == TYFUNC ? td->ret : ty, .narg = args.n, - .sub = alloc(&cm->exarena, (args.n+1)*sizeof(struct expr), 0)); - ex.sub[0] = *callee; - memcpy(ex.sub+1, args.p, args.n*sizeof(struct expr)); - vfree(&args); - return ex; -} - -static inline int -tkprec(int tt) -{ - return ((uint)tt < arraylength(bintab)) ? bintab[tt].prec : 0; -} - -static struct expr initializer(struct comp *cm, union type *ty, enum evalmode ev, - bool globl, enum qualifier qual, const char *name); - -/* parse an expression with the given operator precedence */ -/* param ident is a kludge to support block labels without backtracking or extra lookahead - * see stmt() */ -static struct expr -exprparse(struct comp *cm, int prec, const struct token *ident, bool fromstmt) -{ - struct token tk, tk2; - struct span span; - struct expr ex, rhs, tmp; - struct decl *decl; - union type ty; - int opprec; - enum exprkind ek; - struct { - struct span span; - union { - union type ty; /* cast type */ - struct { - uchar t0; /* t == 0 */ - short tt; /* token */ - }; - }; - } unops[4]; - int nunop = 0; - - if (ident) { - assert(ident->t == TKIDENT); - tk = *ident; - ident = NULL; - goto Ident; - } - -Unary: - switch (lex(cm, &tk)) { - /* unary operators (gather) */ - case '+': case '-': case '~': case '!': - case '*': case '&': case TKINC: case TKDEC: - Unops: - unops[nunop].span = tk.span; - unops[nunop].t0 = 0; - unops[nunop].tt = tk.t; - if (++nunop >= arraylength(unops)) { - ex = exprparse(cm, 999, NULL, 0); - break; - } - goto Unary; - - /* base exprs */ - case TKNUMLIT: - case TKCHRLIT: - ex = mkexpr(ENUMLIT, tk.span, mktype(0), ); - if (!(ty.t = parsenumlit(&ex.u, &ex.f, &tk, 0))) - error(&tk.span, "bad number literal %'tk", &tk); - ex.ty.t = ty.t ? ty.t : TYINT; - break; - case TKSTRLIT: - ty = mktype(((const char []){TYCHAR, TYSHORT, TYINT})[tk.wide]); - ex = mkexpr(ESTRLIT, tk.span, mkarrtype(ty, 0, tk.len+1), .s = { (void *)tk.s, tk.len }); - break; - case TKIDENT: - Ident: - decl = finddecl(cm, tk.s); - if (!decl) { - if (peek(cm, NULL) == '(') { /* implicit function decl? */ - ex = mkexpr(ESYM, tk.span, mktype(0), .sym = (void *)tk.s); - } else { - error(&tk.span, "undeclared identifier %'tk", &tk); - ex = mkexpr(ESYM, tk.span, mktype(TYINT), .sym = NULL); - } - } else if (decl->scls == SCTYPEDEF) { - error(&tk.span, "unexpected typename %'tk (expected expression)", &tk); - ex = mkexpr(ESYM, tk.span, decl->ty, .sym = NULL); - } else if (decl->isenum) { - ex = mkexpr(ENUMLIT, tk.span, decl->ty, .i = decl->value); - } else { - ex = mkexpr(ESYM, tk.span, decl->ty, .qual = decl->qual, .sym = decl); - } - break; - - /* might be unary op or primary expr */ - case '(': - if (!isdecltok(cm)) { /* (expr) */ - ex = commaexpr(cm); - expect(cm, ')', NULL); - break; - } else { /* (type) expr */ - struct declstate st = { DCASTEXPR }; - struct decl decl = pdecl(&st, cm); - expect(cm, ')', NULL); - assert(decl.ty.t); - if (peek(cm, NULL) == '{') { - if (ccopt.cstd < STDC99) - warn(&tk.span, "compound literals are a c99 feature"); - ex = initializer(cm, &decl.ty, (decl.scls & SCSTATIC) ? EVSTATICINI : EVFOLD, - /* globl */ 0, decl.qual, NULL); - break; - } - unops[nunop].span = tk.span; - unops[nunop].ty = decl.ty; - if (++nunop >= arraylength(unops)) { - ex = exprparse(cm, 999, NULL, 0); - break; - } - goto Unary; - } - case TKWsizeof: - span = tk.span; - if (!match(cm, NULL, '(')) /* sizeof expr */ - goto Unops; - else if (isdecltok(cm)) { /* sizeof (type) */ - struct declstate st = { DCASTEXPR }; - ty = pdecl(&st, cm).ty; - } else { /* sizeof (expr) */ - ty = commaexpr(cm).ty; - } - peek(cm, &tk); - if (expect(cm, ')', NULL)) - joinspan(&span.ex, tk.span.ex); - sizeofcheck(&span, ty); - ex = mkexpr(ENUMLIT, span, mktype(targ_sizetype), .u = typesize(ty)); - break; - default: - fatal(&tk.span, "expected %s (near %'tk)", fromstmt ? "statement" : "expression", &tk); - } - - /* postfix operators */ -Postfix: - switch (peek(cm, &tk)) { - default: break; - case TKINC: - case TKDEC: - lex(cm, &tk); - span = ex.span; - if (!joinspan(&span.ex, tk.span.ex)) span = tk.span; - incdeccheck(tk.t, &ex, &span); - ex = mkexpr(tk.t == TKINC ? EPOSTINC : EPOSTDEC, span, ex.ty, .sub = exprdup(cm, &ex)); - goto Postfix; - case '[': /* a[subscript] */ - lex(cm, NULL); - rhs = commaexpr(cm); - span = ex.span; - if (!joinspan(&span.ex, tk.span.ex) || !joinspan(&span.ex, ex.span.ex) - || (peek(cm, &tk2), !joinspan(&span.ex, tk.span.ex))) - span = tk.span; - expect(cm, ']', NULL); - - if (isint(ex.ty) && isptrcvt(rhs.ty)) { - /* swap idx[ptr] -> ptr[idx] */ - tmp = ex; - ex = rhs; - rhs = tmp; - } - - ty = subscriptcheck(&ex, &rhs, &span); - assert(ty.t); - if (!iszero(rhs)) { - tmp.sub = exprdup2(cm, &ex, &rhs); - tmp.t = EADD; - tmp.span = span; - tmp.ty = typedecay(ex.ty); - } - tmp.sub = exprdup(cm, iszero(rhs) ? &ex : &tmp); - tmp.span = span; - tmp.t = EDEREF; - tmp.qual = ex.ty.flag & TFCHLDQUAL; - tmp.ty = ty; - ex = tmp; - goto Postfix; - case '(': /* call(args) */ - lex(cm, &tk); - span = ex.span; - ex = callexpr(cm, &span, &ex); - goto Postfix; - case TKARROW: - if (ex.ty.t != TYPTR && ex.ty.t != TYARRAY) - error(&ex.span, "operand to -> is not a pointer: '%ty'", ex.ty); - else - ex = mkexpr(EDEREF, ex.span, typechild(ex.ty), .qual = ex.ty.flag & TFCHLDQUAL, - .sub = exprdup(cm, &ex)); - /* fallthru */ - case '.': - lex(cm, &tk); - span = ex.span; - peek(cm, &tk2); /* field name */ - if (!expect(cm, TKIDENT, NULL)) tk2.s = ""; - if (!joinspan(&span.ex, tk.span.ex) || !joinspan(&span.ex, tk2.span.ex)) - span = tk.span; - if (!isagg(ex.ty)) { - error(&span, "member access operand is not an aggregate: '%ty'%s", ex.ty, - ex.ty.t == TYPTR && isagg(typechild(ex.ty)) ? "; did you mean to use '->'?" : ""); - } else { - struct fielddata fld = {.t = mktype(TYINT)}; - if (*tk2.s && !getfield(&fld, ex.ty, tk2.s)) - error(&span, "'%ty' has no such field: '%s'", ex.ty, tk2.s); - if (ex.t == EGETF && ex.qual == fld.qual) { /* accumulate */ - ex.span = span; - ex.ty = fld.t; - ex.fld.off += fld.off; - ex.fld.bitoff = fld.bitoff; - ex.fld.bitsiz = fld.bitsiz; - } else { - ex = mkexpr(EGETF, span, fld.t, .qual = ex.qual | fld.qual, .sub = exprdup(cm, &ex), - .fld = { fld.off, fld.bitsiz, fld.bitoff }); - } - } - goto Postfix; - } - - /* unary operators (process) */ - while (nunop-- > 0) { - span = unops[nunop].span; - joinspan(&span.ex, ex.span.ex); - if (unops[nunop].t0 == 0) { - switch (unops[nunop].tt) { - case '+': - ek = EPLUS; - goto Alu; - case '-': - ek = ENEG; - goto Alu; - case '~': - ek = ECOMPL; - goto Alu; - case '!': - ek = ELOGNOT; - Alu: - ty = ek == ELOGNOT ? mktype(TYINT) : cvtarith(ex.ty, ex.ty); - if (!ty.t || (ek == ECOMPL && !isint(ty))) { - error(&tk.span, "invalid operand to %'tk '%ty'", &tk, ex.ty); - ty = mktype(TYINT); - } - ex = mkexpr(ek, span, ty, .sub = exprdup(cm, &ex)); - break; - case TKINC: case TKDEC: - ty = ex.ty; - incdeccheck(tk.t, &ex, &span); - ex = mkexpr(unops[nunop].tt == TKINC ? EPREINC : EPREDEC, span, ty, - .sub = exprdup(cm, &ex)); - break; - case '*': - if (ex.ty.t == TYPTR || ex.ty.t == TYARRAY) { - ty = typechild(ex.ty); - if (isincomplete(ty)) { - error(&span, "cannot dereference pointer to incomplete type '%ty'", ty); - ty = mktype(TYINT); - } - } else { - error(&span, "invalid operand to unary * '%ty'", ex.ty); - ty = mktype(TYINT); - } - ex = mkexpr(EDEREF, span, ty, .qual = ex.ty.flag & TFCHLDQUAL, - .sub = exprdup(cm, &ex)); - break; - case '&': - if (!islvalue(&ex)) - error(&span, "operand to unary & is not an lvalue"); - if (ex.t == EGETF && ex.fld.bitsiz) - error(&span, "cannot take address of bitfield"); - ex = mkexpr(EADDROF, span, mkptrtype(ex.ty, ex.qual), .sub = exprdup(cm, &ex)); - break; - case TKWsizeof: - sizeofcheck(&span, ex.ty); - ex = mkexpr(ENUMLIT, span, mktype(targ_sizetype), .u = typesize(ex.ty)); - break; - default: assert(0); - } - } else { /* cast */ - ty = unops[nunop].ty; - if (!castcheck(ty, &ex)) - error(&span, "cannot cast value of type '%ty' to '%ty'", ex.ty, ty); - ex = mkexpr(ECAST, span, ty, .sub = exprdup(cm, &ex)); - } - } - - /* binary operators */ - while ((opprec = tkprec(peek(cm, &tk))) >= prec) { - lex(cm, &tk); - ek = bintab[tk.t].t; - if (ek != ECOND) { - /* only the assignment operators are right-associative */ - bool leftassoc = (bintab[tk.t].k & BCSET) == 0; - /* ex OP rhs */ - span.sl = tk.span.sl; - span.ex = ex.span.ex; - rhs = exprparse(cm, opprec + leftassoc, NULL, 0); - if (!joinspan(&span.ex, tk.span.ex) || !joinspan(&span.ex, rhs.span.ex)) - span.ex = tk.span.ex; - ty = bintypecheck(&span, tk.t, &ex, &rhs); - assert(ty.t); - ex = mkexpr(ek, span, ty, .sub = exprdup2(cm, &ex, &rhs)); - } else { - /* ex ? tmp : rhs */ - struct expr *sub; - span.sl = tk.span.sl; - span.ex = ex.span.ex; - if (!isscalar(ex.ty)) - error(&ex.span, "?: condition is not a scalar type: '%ty'", ex.ty); - tmp = commaexpr(cm); - joinspan(&tk.span.ex, tmp.span.ex); - expect(cm, ':', NULL); - rhs = expr(cm); - if (!joinspan(&span.ex, tk.span.ex) || !joinspan(&span.ex, tmp.span.ex) - || !joinspan(&span.ex, rhs.span.ex)) - span.ex = tk.span.ex; - ty = condtype(&tmp, &rhs); - if (!ty.t) { - error(&span, "incompatible types in conditional expression: '%ty', '%ty'", tmp.ty, rhs.ty); - ty = tmp.ty; - } - sub = alloc(&cm->exarena, 3 * sizeof*sub, 0); - sub[0] = ex, sub[1] = tmp, sub[2] = rhs; - ex = mkexpr(ECOND, span, ty, .sub = sub); - } - } - - return ex; -} - -static struct expr -expr(struct comp *cm) -{ - return exprparse(cm, bintab['='].prec, NULL, 0); /* non-comma expr */ -} - -static struct expr -constantexpr(struct comp *cm) -{ - return exprparse(cm, bintab['?'].prec, NULL, 0); /* conditional-expr */ -} - -static struct expr -commaexpr(struct comp *cm) -{ - return exprparse(cm, 1, NULL, 0); -} - -/****************/ -/* 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 -chrarrayof(union type ty, union type chld) -{ - assert(isint(chld)); - return ty.t == TYARRAY && isint(typechild(ty)) && typesize(typechild(ty)) == typesize(chld); -} - -static union type -membertype(uint *off, uint *bitsiz, uint *bitoff, union type ty, uint idx) -{ - *bitsiz = *bitoff = 0; - 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) { - struct fielddata fld = typedata[ty.dat].fld[idx].f; - *off = fld.off; - *bitsiz = fld.bitsiz, *bitoff = fld.bitoff; - return fld.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 == EINIT || 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 (ex->t == EADDROF && (ex->sub->t == EGETF && globsym(psym, ex->sub->sub))) { - *paddend = ex->sub->fld.off; - } 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 * typesize(ex->sub[0].ty); - } else if (ex->t == EADD) { - for (int swp = 0; swp < 2; ++swp) { - struct expr *a = &ex->sub[swp], *b = &ex->sub[swp ^ 1]; - if (globsym(psym, a) && isint(b->ty) && b->t == ENUMLIT) { - *paddend = b->i * typesize(a->ty); - return; - } - } - goto Fail; - } else Fail: assert(0 && "non static reloc"); -} - -static void -iniwrite(struct comp *cm, struct initparser *ip, uint off, union type ty, struct expr *ex) -{ - uchar *p; - uint bitsiz, bitoff; - 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, &bitsiz, &bitoff, ex->ty, i); - assert(!bitsiz); - iniwrite(cm, 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) { - uint old = ip->ddat.n; - vresize(&ip->ddat, off + siz); - memset(ip->ddat.p + old, 0, ip->ddat.n - old); - 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 = ex->s.n * typesize(typechild(ty)); - if (siz < n) n = siz; - /* XXX endian for wide strs */ - memcpy(p, ex->s.p, n); - } else { - union ref sym; - vlong addend; - //efmt("<<> %ty <- %ty\n", ty, ex->ty); - 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; - } - } - } else { - struct init *init = ip->init; - struct initval val = { - .off = off, - .ex = *ex - }, *new = alloccopy(&cm->exarena, &val, sizeof val, 0); - *init->tail = new; - init->tail = &new->next; - for (uint i = off, end = i + typesize(ex->ty); i < end; ++i) { - if (BSSIZE(end) > arraylength(init->zero)) break; - bsclr(init->zero, i); - } - } -} - -static bool -iniwriterec(struct comp *cm, struct initparser *ip, uint off, struct expr *ex) -{ - for (struct initval *v = ex->init->vals; v; v = v->next) { - if (v->ex.t == EINIT) iniwriterec(cm, ip, off + v->off, &v->ex); - else if (ip->ev && !eval(&v->ex, ip->ev) && ip->ev != EVFOLD) return 0; - } - return 1; -} - -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, bitsiz, bitoff; - union type targ = membertype(&off, &bitsiz, &bitoff, ip->sub->ty, idx); - struct initcur *next = iniadvance(ip, ip->cur, span); - assert(!bitsiz); - - 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 (typearrlen(*ty) < ex->s.n) { - warn(&ex->span, "string literal in initializer is truncated from %u to %u bytes", - (ex->s.n+1)*typesize(typechild(*ty)), typesize(*ty)); - } - ex->ty = *ty; -} - -/* read scalar initializer into initializer list and avance */ -static void -ininext(struct initparser *ip, struct comp *cm) -{ - uint off, bitsiz, bitoff; - union type targ; - struct expr ex = expr(cm); - -Retry: - targ = membertype(&off, &bitsiz, &bitoff, ip->sub->ty, ip->sub->idx); - assert(!bitsiz); - - 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 && chrarrayof(targ, typechild(ex.ty))) { - assert(!isincomplete(targ)); - inistrlit(cm, &ex, &targ); - iniwrite(cm, ip, ip->sub->off + off, targ, &ex); - ++ip->sub->idx; - return; - } else if (ex.t == ESTRLIT && ip->sub->idx == 0 && chrarrayof(ip->sub->ty, typechild(ex.ty))) { - /* handle e.g. (char []){"foo"} */ - assert(off == 0); - targ = ip->sub->ty; - inistrlit(cm, &ex, &targ); - iniwrite(cm, ip, ip->sub->off, targ, &ex); - if (ip->sub == ip->buf && ip->arrlen < ex.s.n+1) - ip->arrlen = ex.s.n+1; - --ip->sub; - 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 (!initcheck(targ, &ex)) - error(&ex.span, "cannot initialize '%ty' with expression of type '%ty'", targ, ex.ty); - else { - if (targ.bits == ex.ty.bits && ex.t == EINIT) { - if (!iniwriterec(cm, ip, ip->sub->off + off, &ex)) - goto CannotEval; - } else if (ip->ev && !eval(&ex, ip->ev) && ip->ev != EVFOLD) { - CannotEval: - error(&ex.span, "cannot evaluate expression statically"); - } else { - struct expr *pex = &ex; - if (ip->ev != EVSTATICINI) { - if (ex.ty.bits != targ.bits) - ex = mkexpr(ECAST, ex.span, targ, .sub = exprdup(cm, &ex)); - pex = exprdup(cm, &ex); - } - iniwrite(cm, ip, ip->sub->off + off, targ, pex); - } - } - } - if (ip->sub == ip->buf && ip->arrlen < ip->sub->idx+1) - ip->arrlen = ip->sub->idx+1; - - 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; - } - } - return -1; -} - -static bool -designators(struct initparser *ip, struct comp *cm) -{ - struct token tk; - struct span span; - bool some = 0; - - for (;;) { - uint off, bitsiz, bitoff; - 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) { - union type ty = membertype(&off, &bitsiz, &bitoff, ip->sub->ty, ip->sub->idx++); - struct initcur *next = iniadvance(ip, ip->sub, &tk.span); - assert(!bitsiz); - *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) { - union type ty = membertype(&off, &bitsiz, &bitoff, 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) { - do { - idx = aggdesignator(ip, ip->sub->ty, tk.s, &span); - if (idx >= 0) break; - if (ip->sub != ip->cur && !ttypenames[typedata[ip->sub->ty.dat].id]) { - /* if in anonymous aggregate, go up and look again */ - --ip->sub; - continue; - } - } while (0); - ip->sub->idx = idx; - if (idx < 0) - error(&span, "%ty has no such field: '%s'", ip->cur->ty, tk.s); - 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; - res.tail = &res.vals; - } - - if (!match(cm, &tk, '{')) { - struct expr ex = expr(cm); - if (ex.t == ESTRLIT && chrarrayof(*ty, typechild(ex.ty))) { - inistrlit(cm, &ex, ty); - iniwrite(cm, ip, 0, *ty, &ex); - if (ip->dyn) - goto Dynfix; - } - if (!initcheck(*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(cm, ip, 0, *ty, &ex); - } - return ex; - } - - assert(arraylength(res.zero) == 1); - if (ev != EVSTATICINI) { - memset(res.zero, 0xFF, sizeof res.zero); - } - - 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 { - uint siz; - if (isincomplete(*ty)) { - if (!ip->arrlen) - error(&span, "initializer creates a zero-sized array"); - *ty = mkarrtype(typechild(*ty), ty->flag & TFCHLDQUAL, ip->arrlen > 0 ? ip->arrlen : 1); - } - - assert(arraylength(res.zero) == 1); - siz = typesize(*ty); - if (siz && siz <= 64) - res.zero->u &= ~0ull >> (64 - siz); - - return mkexpr(EINIT, span, *ty, .init = alloccopy(&cm->exarena, &res, sizeof res, 0)); - } -} - -/*****************/ -/* Decls Parsing */ -/*****************/ - -static union type -buildagg(struct comp *cm, enum typetag tt, const char *name, int id) -{ - struct token tk; - union type t; - struct span flexspan; - struct namedfield fbuf[32]; - vec_of(struct namedfield) fld = VINIT(fbuf, arraylength(fbuf)); - struct typedata td = {tt}; - bool isunion = tt == TYUNION; - const char *tag = isunion ? "union" : "struct"; - uint bitsiz = 0, bitfbyteoff = 0, - bitoff = 0, bitftypesiz = 0; - - while (!match(cm, &tk, '}')) { - struct declstate st = { DFIELD }; - do { - struct decl decl = pdecl(&st, cm); - uint tysize = typesize(decl.ty); - if (fld.n && td.flexi) { - td.flexi = 0; - error(&flexspan, "flexible array member is not at end of struct"); - } - if (!isunion && decl.ty.t == TYARRAY && !typearrlen(decl.ty)) { - td.flexi = 1; - flexspan = decl.span; - } else if (isincomplete(decl.ty)) { - error(&decl.span, "field has incomplete type '%ty'", decl.ty); - } else if (decl.ty.t == TYFUNC) { - error(&decl.span, "field has function type '%ty'", decl.ty); - } - bitsiz = 0; - if (st.bitf) { - struct expr ex = constantexpr(cm); - const char *name = decl.name ? decl.name : ""; - if (!isint(decl.ty)) { - error(&decl.span, "bit-field '%s' has non-integer type '%ty'", name, decl.ty); - } else if (!isint(ex.ty)) { - error(&ex.span, "integer constant expression has non-integer type '%ty'", decl.ty); - } else if (!eval(&ex, EVINTCONST)) { - error(&ex.span, "cannot evaluate integer constant expression"); - } else if (ex.i < 0) { - error(&ex.span, "bit-field '%s' has negative width '%ld'", name, ex.i); - } else if (ex.i > 8*tysize) { - error(&ex.span, "width of bit-field '%s' (%ld) exceeds width of type (%d)", - name, ex.i, 8*tysize); - } else if (ex.i == 0 && decl.name) { - error(&ex.span, "named bit-field '%s' has zero width", name); - } else { - bitsiz = ex.i; - if (bitsiz == 0) { - if (bitftypesiz) { - bitfbyteoff += bitftypesiz; - bitfbyteoff = alignup(bitfbyteoff, typealign(decl.ty)); - } - bitoff = 0; - } else if (bitftypesiz && bitftypesiz < tysize) { - /* end of previous bitfield */ - bitoff = 0; - bitfbyteoff += bitftypesiz; - } else if (!bitftypesiz) { - bitoff = 0; - bitfbyteoff = alignup(td.siz, typealign(decl.ty)); - } else if (bitoff + bitsiz > 8*bitftypesiz) { - /* no straddling boundaries */ - bitoff = 0; - bitfbyteoff += bitftypesiz; - } - if (tysize > bitftypesiz) bitftypesiz = tysize; - } - pdecl(&st, cm); - } else { - bitftypesiz = bitoff = bitsiz = 0; - } - if (decl.ty.t) { - uint align = typealign(decl.ty); - uint siz = tysize; - uint off = bitftypesiz ? bitfbyteoff : isunion ? 0 : alignup(td.siz, align); - struct namedfield f = { decl.name, { decl.ty, off, bitsiz, bitoff, .qual = decl.qual }}; - if (bitftypesiz && siz != bitftypesiz) while (f.f.bitoff + f.f.bitsiz > 8*siz) { - /* adjust bitfields narrower than container type */ - f.f.off += siz; - f.f.bitoff -= 8*siz; - } - if (!decl.name && !bitftypesiz) { - if (!isagg(decl.ty) || ttypenames[typedata[decl.ty.dat].id]) { - warn(&decl.span, "declaration does not declare anything"); - continue; - } else if (ccopt.cstd < STDC11 && ccopt.pedant) { - warn(&decl.span, "anonymous %s in %M is an extension", - decl.ty.t == TYUNION ? "union" : "struct"); - } - } - if (decl.name || !bitftypesiz) - vpush(&fld, f); - td.anyconst |= decl.qual & QCONST; - if (isagg(decl.ty)) { - td.anyconst |= typedata[decl.ty.dat].anyconst; - if (typedata[decl.ty.dat].flexi && !isunion) - error(&decl.span, "nested aggregate has flexible array member"); - } - if (isunion) - td.siz = td.siz < siz ? siz : td.siz; - else - td.siz = off + siz; - td.align = td.align < align ? align : td.align; - bitoff += bitsiz; - } - } 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) { - struct namedfield dummy = { "", { mktype(TYCHAR), 0 }}; - error(&tk.span, "%s cannot have zero members", tag); - vpush(&fld, dummy); - td.siz = td.align = 1; - } - td.siz = alignup(td.siz, td.align); - td.fld = fld.p; - td.nmemb = fld.n; - if (id != -1) - t = completetype(name, id, &td); - else - t = mktagtype(name, &td); - vfree(&fld); - return t; -} - -static inline void -inttyminmax(vlong *min, uvlong *max, enum typetag tt) -{ - uint bits = 8*targ_primsizes[tt]; - *min = isunsignedt(tt) ? 0 : -(1ull << (bits - 1)); - *max = isunsignedt(tt) ? ~0ull >> (64 - bits) : bits == 64 ? ~0ull>>1 : (1ll << (bits - 1)) - 1; -} - -/* the backing type of enum (without a C23 fixed backing type) is int or the - * smallest-rank type that all the enumerators fit in, or if it doesn't exist, - * then the biggest signed type. the type of enumeration constants is the type of - * its defining expression when present or the type of the previous enumerator - * or in case of overflow the smallest type that fits (previous value + 1) - * this isn't strictly conforming since pre C23 enums are pretty loosely defined, - * and this is similar to existing compiler's de-facto behaviour (though gcc - * prefers to use unsigned types when possible). should add support for -fshort-enums - */ -static union type -buildenum(struct comp *cm, const char *name, const struct span *span) -{ - struct token tk; - vlong tymin, minv = 0; - uvlong tymax, maxv = 0; - struct typedata td = {TYENUM, .backing = TYINT}; - union type ty = mktype(td.backing); - struct span maxvspan; - vlong iota = 0; - bool somelonglong = 0; - - inttyminmax(&tymin, &tymax, td.backing); - while (!match(cm, &tk, '}')) { - struct decl decl = {0}; - peek(cm, &tk); - expect(cm, TKIDENT, NULL); - if (match(cm, NULL, '=') || (peek(cm, NULL) == TKNUMLIT && !expect(cm, '=', NULL))) { - struct expr ex = expr(cm); - if (eval(&ex, EVINTCONST)) { - iota = ex.i; - if (ex.ty.t != ty.t) - inttyminmax(&tymin, &tymax, ex.ty.t); - ty = ex.ty; - } else { - error(&ex.span, "enum value is not an integer constant"); - } - } else if (tk.t != TKIDENT) { - lex(cm, NULL); - continue; - } - while (issigned(ty) ? (iota > (vlong)tymax || iota < tymin) : iota > tymax) - inttyminmax(&tymin, &tymax, ++ty.t); - somelonglong |= ty.t >= TYVLONG; - if ((isunsigned(ty) || iota > 0) && iota > maxv) - maxv = iota, maxvspan = tk.span; - else if (issigned(ty) && iota < minv) - minv = iota; - - decl.name = tk.s; - decl.ty = ty; - decl.isenum = 1; - decl.value = iota++; - putdecl(cm, &decl); - if (!match(cm, &tk, ',')) { - if (expect(cm, '}', "or `,'")) - break; - else lex(cm, NULL); - } - } - - td.backing = 0; - for (int t = TYINT; t <= TYUVLONG; ++t) { - inttyminmax(&tymin, &tymax, t); - if (minv >= tymin && maxv <= tymax) { - td.backing = t; - break; - } - } - if (!td.backing) { - td.backing = !somelonglong && ccopt.cstd == STDC89 && ccopt.pedant ? TYLONG : TYVLONG; - warn(&maxvspan, "enumerators exceed range of enum's backing type '%ty'", mktype(td.backing)); - } - if (td.backing >= TYVLONG && !somelonglong && ccopt.cstd == STDC89 && ccopt.pedant) - warn(span, "enum backing type is '%ty' in %M", mktype(td.backing)); - - ty = mktagtype(name, &td); - ty.backing = td.backing; - return ty; -} - -static union type -tagtype(struct comp *cm, enum toktag kind) -{ - struct token tk; - union type t; - struct span span; - enum typetag tt = kind == TKWenum ? TYENUM : kind == TKWstruct ? TYSTRUCT : TYUNION; - const char *tag = NULL; - - peek(cm, &tk); - if (match(cm, &tk, TKIDENT)) - tag = tk.s; - span = tk.span; - if (!match(cm, NULL, '{')) { - if (!tag) { - error(&tk.span, "expected %tt name or '{'", kind); - return mktype(0); - } - t = gettagged(cm, &span, tt, tag, /* def? */ peek(cm, NULL) == ';'); - if (tt == TYENUM && !t.t) { - error(&tk.span, "cannot forward-declare enum"); - return mktype(TYINT); - } - } else { - if (tt != TYENUM) { - if (tag) { - t = deftagged(cm, &span, tt, tag, mktype(0)); - if (t.t != tt || !isincomplete(t)) { - if (t.t != tt) - error(&tk.span, - "defining tagged type %'tk as %tt clashes with previous definition", - &tk, kind); - else - error(&tk.span, "redefinition of '%tt %s'", kind, tag, mktype(0)); - note(&span, "previous definition:"); - } - } - t = buildagg(cm, tt, tag, tag ? typedata[t.dat].id : -1); - } else { - t = buildenum(cm, tag, &span); - if (tag) deftagged(cm, &span, TYENUM, tag, t); - } - } - - if (t.t != tt) { - error(&tk.span, "declaring tagged type %'tk as %tt clashes with previous definition", - &tk, kind); - note(&span, "previous definition:"); - } - return t; -} - -static union type -ptypeof(struct comp *cm) -{ - union type ty; - expect(cm, '(', NULL); - if (isdecltok(cm)) { /* typeof (type) */ - struct declstate st = { DCASTEXPR }; - ty = pdecl(&st, cm).ty; - } else { /* typeof (expr) */ - ty = commaexpr(cm).ty; - } - expect(cm, ')', NULL); - return ty; -} - -static void -declspec(struct declstate *st, struct comp *cm) -{ - struct token tk; - struct decl *decl; - enum arith { - KSIGNED = 1<<0, - KUNSIGNED = 1<<1, - KBOOL = 1<<2, - KCHAR = 1<<3, - KSHORT = 1<<4, - KLONG = 1<<5, - KLONGLONG = 1<<6, - KINT = 1<<7, - KFLOAT = 1<<8, - KDOUBLE = 1<<9, - } arith = 0; - struct span span = {0}; - - for (;;) { - peek(cm, &tk); - switch (tk.t) { - case TKWconst: - st->qual |= QCONST; - break; - case TKWrestrict: - /* unimplemented */ - /*st->qual |= QRESTRICT;*/ - break; - case TKWvolatile: - st->qual |= QVOLATILE; - break; - case TKW_Noreturn: - st->qual |= QNORETURN; - break; - case TKWinline: - st->qual |= QINLINE; - break; - case TKWvoid: - st->base = mktype(TYVOID); - break; - case TKWsigned: - arith |= KSIGNED; - break; - case TKWunsigned: - arith |= KUNSIGNED; - break; - case TKW_Bool: - case TKWbool: - if (arith & KBOOL) goto Dup; - arith |= KBOOL; - break; - case TKWchar: - if (arith & KCHAR) { - Dup: - error(&tk.span, "duplicate %tk specifier", &tk); - } - arith |= KCHAR; - break; - case TKWshort: - arith |= KSHORT; - break; - case TKWlong: - if ((arith & (KLONG | KLONGLONG)) == KLONG) - arith = (arith &~ KLONG) | KLONGLONG; - else if ((arith & (KLONG | KLONGLONG)) == 0) - arith |= KLONG; - else - error(&tk.span, "too long"); - break; - case TKWint: - if (arith & KINT) goto Dup; - arith |= KINT; - break; - case TKWfloat: - if (arith & KFLOAT) goto Dup; - arith |= KFLOAT; - break; - case TKWdouble: - if (arith & KDOUBLE) goto Dup; - arith |= KDOUBLE; - break; - case TKWenum: - case TKWstruct: - case TKWunion: - lex(cm, &tk); - st->base = tagtype(cm, tk.t); - st->tagdecl = 1; - if (!span.ex.len) span.ex = tk.span.ex; - joinspan(&span.ex, tk.span.ex); - goto End; - case TKW__typeof__: case TKWtypeof: - lex(cm, &tk); - st->base = ptypeof(cm); - if (!span.ex.len) span.ex = tk.span.ex; - joinspan(&span.ex, tk.span.ex); - goto End; - case TKIDENT: - if (!st->base.t && !arith && (decl = finddecl(cm, tk.s)) - && decl->scls == SCTYPEDEF) { - st->base = decl->ty; - break; - } - /* fallthru */ - default: - if (!span.ex.len) span.ex = tk.span.ex; - goto End; - case TKW_BitInt: case TKW_Complex: - case TKW_Decimal128: case TKW_Decimal32: - case TKW_Decimal64: case TKW_Imaginary: - error(&tk.span, "%'tk is unsupported", &tk); - arith = arith ? arith : KINT; - } - if (!span.ex.len) span.ex = tk.span.ex; - joinspan(&span.ex, tk.span.ex); - lex(cm, &tk); - if (st->base.t) break; - } -End: - if (st->base.t && arith) { - /* combining arith type specifiers and other types */ - Bad: - error(&span, "invalid declaration specifier"); - st->base = mktype(TYINT); - } else if (!st->base.t && arith) { - enum typetag t; - if (arith == KFLOAT) - t = TYFLOAT; - else if (arith == KDOUBLE) - t = TYDOUBLE; - else if (arith == (KLONG | KDOUBLE)) { - t = TYLDOUBLE; - } else if (arith == KBOOL) - t = TYBOOL; - else if (arith == KCHAR) - t = TYCHAR; - else if (arith == (KSIGNED | KCHAR)) - t = TYSCHAR; - else if (arith == (KUNSIGNED | KCHAR)) - t = TYUCHAR; - else if ((arith & ~KINT & ~KSIGNED) == KSHORT) - t = TYSHORT; - else if ((arith & ~KINT) == (KUNSIGNED | KSHORT)) - t = TYUSHORT; - else if ((arith & ~KINT & ~KSIGNED) == 0) - t = TYINT; - else if ((arith & ~KINT) == KUNSIGNED) - t = TYUINT; - else if ((arith & ~KINT & ~KSIGNED) == KLONG) - t = TYLONG; - else if ((arith & ~KINT) == (KUNSIGNED | KLONG)) - t = TYULONG; - else if ((arith & ~KINT & ~KSIGNED) == KLONGLONG) - t = TYVLONG; - else if ((arith & ~KINT) == (KUNSIGNED | KLONGLONG)) - t = TYUVLONG; - else - goto Bad; - st->base = mktype(t ? t : TYINT); - } else if (!st->base.t && ccopt.cstd < STDC23) { - warn(&span, "type implicitly declared as int"); - st->base = mktype(TYINT); - } else if (!st->base.t) - fatal(&span, "expected declaration type specifier"); -} - -/* circular doubly linked list used to parse declarators */ -static struct decllist { - struct decllist *prev, *next; - uchar t; /* TYPTR, TYARRAY or TYFUNC */ - union { - uchar qual; /* TYPTR */ - uint len; /* TYARRAY */ - struct { /* TYFUNC */ - union type *param; - const char **pnames; - struct span *pspans; - uchar *pqual; - short npar; - bool kandr : 1, variadic : 1; - }; - }; - struct span span; -} decltmp[64], *declfreelist; -static union type declparamtmp[16]; -static const char *declpnamestmp[16]; -static struct span declpspanstmp[16]; -static uchar declpqualtmp[tdqualsiz(16)]; - -static void -declinsert(struct decllist *list, const struct decllist *node) -{ - struct decllist *pnode = declfreelist; - if (!pnode) fatal(NULL, "too many nested declarators"); - declfreelist = declfreelist->next; - *pnode = *node; - pnode->next = list->next; - pnode->prev = list; - list->next->prev = pnode; - list->next = pnode; -} - -static int -sclass(struct comp *cm, struct span *span) -{ - struct token tk; - int sc = 0, first = 1; - for (;; lex(cm, &tk)) { - switch (peek(cm, &tk)) { - case TKWtypedef: sc |= SCTYPEDEF; break; - case TKWextern: sc |= SCEXTERN; break; - case TKWstatic: sc |= SCSTATIC; break; - case TKWauto: sc |= SCAUTO; break; - case TKWregister: sc |= SCREGISTER; break; - case TKWthread_local: - case TKW_Thread_local: - sc |= SCTHREADLOCAL; break; - default: return sc; - } - if (first) *span = tk.span; - else joinspan(&span->ex, tk.span.ex); - first = 0; - } -} - -static int -cvqual(struct comp *cm) -{ - struct token tk; - int q = 0; - while (match(cm, &tk, TKWconst) || match(cm, &tk, TKWvolatile) || match(cm, &tk, TKWrestrict)) - q |= tk.t == TKWconst ? QCONST : tk.t == TKWvolatile ? QVOLATILE : 0; - return q; -} - -static void -decltypes(struct comp *cm, struct decllist *list, const char **name, struct span *span) { - struct token tk; - struct decllist *ptr, node; - - while (match(cm, &tk, '*')) { - node.t = TYPTR; - node.qual = cvqual(cm); - node.span = tk.span; - declinsert(list, &node); - } - ptr = list->next; - switch (peek(cm, &tk)) { - case '(': - lex(cm, &tk); - if (isdecltok(cm)) { - goto Func; - } else if (match(cm, &tk, ')')) { - /* T () is K&R func proto */ - node.span = tk.span; - node.t = TYFUNC; - node.param = NULL; - node.pqual = NULL; - node.pnames = NULL; - node.variadic = 0; - node.kandr = 1; - node.npar = 0; - declinsert(ptr->prev, &node); - break; - } else { - decltypes(cm, list, name, span); - expect(cm, ')', NULL); - } - break; - case TKIDENT: - if (!name) - error(&tk.span, "unexpected identifier in type name"); - else { - *name = tk.s; - *span = tk.span; - } - lex(cm, &tk); - break; - default: - *span = tk.span; - if (name) - *name = NULL; - } - for (;;) { - if (match(cm, &tk, '[')) { - node.span = tk.span; - uint n = 0; - if (!match(cm, &tk, ']')) { - struct expr ex = expr(cm); - if (!eval(&ex, EVINTCONST)) { - error(&ex.span, "array length is not an integer constant"); - } else if (typesize(ex.ty) < 8 && ex.i < 0) { - error(&ex.span, "array length is negative"); - } else if (ex.u > (1ull << (8*sizeof n)) - 1) { - error(&ex.span, "array too long (%ul)", ex.u); - } else if (ex.u == 0) { - error(&ex.span, "array cannot have zero length"); - } else { - n = ex.u; - } - peek(cm, &tk); - joinspan(&node.span.ex, tk.span.ex); - expect(cm, ']', NULL); - } - node.t = TYARRAY; - node.len = n; - declinsert(ptr->prev, &node); - } else if (match(cm, &tk, '(')) Func: { - static int depth = 0; - vec_of(union type) params = {0}; - vec_of(uchar) qual = {0}; - vec_of(const char *) names = {0}; - vec_of(struct span) spans = {0}; - bool anyqual = 0; - - if (depth++ == 0) { - vinit(¶ms, declparamtmp, arraylength(declparamtmp)); - vinit(&qual, declpqualtmp, arraylength(declpqualtmp)); - vinit(&names, declpnamestmp, arraylength(declpnamestmp)); - vinit(&spans, declpspanstmp, arraylength(declpspanstmp)); - } - node.span = tk.span; - node.kandr = 0; - node.variadic = 0; - - while (!match(cm, &tk, ')')) { - struct declstate st = { DFUNCPARAM }; - struct decl decl; - if (match(cm, &tk, TKDOTS)) { - node.variadic = 1; - expect(cm, ')', NULL); - break; - } - decl = pdecl(&st, cm); - decl.ty = typedecay(decl.ty); - vpush(¶ms, decl.ty); - vpush(&names, decl.name); - vpush(&spans, decl.span); - if (decl.qual) { - anyqual = 1; - while (qual.n < tdqualsiz(params.n)) vpush(&qual, 0); - tdsetqual(qual.p, params.n-1, decl.qual); - } - if (isincomplete(decl.ty)) { - if (params.n > 1 || decl.ty.t != TYVOID || decl.qual || decl.name) { - error(&decl.span, - "function parameter #%d has incomplete type (%tq)", - params.n, decl.ty, tdgetqual(qual.p, params.n-1)); - } - } - joinspan(&node.span.ex, tk.span.ex); - if (!match(cm, &tk, ',')) { - expect(cm, ')', NULL); - break; - } - } - --depth; - node.kandr = params.n == 0 && ccopt.cstd < STDC23; - if (params.n == 1 && params.p[0].t == TYVOID && !qual.n && !names.p[0]) { /* (void) */ - vfree(¶ms); - vfree(&names); - vfree(&spans); - } else if (params.n && params.p[0].t == TYVOID && !qual.n && !names.p[0]) { - error(&node.span, "function parameter #1 has incomplete type (%tq)", - params.p[0], tdgetqual(qual.p, 0)); - } - node.t = TYFUNC; - node.param = params.n ? params.p : NULL; - node.pqual = anyqual ? qual.p : NULL; - node.pnames = params.n ? names.p : NULL; - node.pspans = params.n ? spans.p : NULL; - node.npar = params.n; - declinsert(ptr->prev, &node); - } else break; - } -} - -static struct decl -declarator(struct declstate *st, struct comp *cm) { - struct decl decl = { st->base, st->scls, st->qual, st->align }; - struct decllist list = { &list, &list }, *l; - static bool inidecltmp = 0; - if (!inidecltmp) { - inidecltmp = 1; - for (int i = 0; i < arraylength(decltmp); ++i) { - decltmp[i].next = declfreelist; - declfreelist = &decltmp[i]; - } - } - - decltypes(cm, &list, st->kind == DCASTEXPR ? NULL : &decl.name, &decl.span); - if (!decl.name && st->kind != DCASTEXPR && st->kind != DFUNCPARAM) { - if (list.prev == &list) lex(cm, NULL); - error(&decl.span, "expected `(', `*' or identifier"); - } - for (l = list.prev; l != &list; l = l->prev) { - switch (l->t) { - case TYPTR: - decl.ty = mkptrtype(decl.ty, decl.qual); - decl.qual = l->qual; - break; - case TYARRAY: - if (isincomplete(decl.ty)) - error(&l->span, "array has incomplete element type '%ty'", decl.ty); - else if (decl.ty.t == TYFUNC) - error(&l->span, "array has element has function type '%ty'", decl.ty); - decl.ty = mkarrtype(decl.ty, decl.qual, l->len); - break; - case TYFUNC: - if (decl.ty.t == TYFUNC) - error(&decl.span, "function cannot return function type '%ty'", decl.ty); - else if (decl.ty.t == TYARRAY) - error(&decl.span, "function cannot return array type", decl.ty); - else if (decl.ty.t != TYVOID && isincomplete(decl.ty)) - error(&decl.span, "function cannot return incomplete type '%ty'", decl.ty); - if (l->kandr && ccopt.cstd > STDC89) - warn(&l->span, "function declaration without a prototype is deprecated"); - decl.ty = mkfntype(decl.ty, l->npar, l->param, l->pqual, l->kandr, l->variadic); - if (l->param != declparamtmp) free(l->param); - if (l->pqual != declpqualtmp) free(l->pqual); - if (l->prev == &list && l->npar) { /* last */ - st->pnames = alloccopy(&cm->fnarena, l->pnames, l->npar * sizeof(char *), 0); - st->pspans = alloccopy(&cm->fnarena, l->pspans, l->npar * sizeof(struct span), 0); - } - if (l->pnames != declpnamestmp) free(l->pnames); - if (l->pspans != declpspanstmp) free(l->pspans); - decl.qual = 0; - break; - } - - l->next = declfreelist; - declfreelist = l; - } - - return decl; -} - -static void -pstaticassert(struct comp *cm, struct span *span) -{ - struct expr ex; - struct token tk, msg = {0}; - - /* _Static_assert '(' [ ',' ] ')' ';' */ - expect(cm, '(', NULL); - ex = expr(cm); - peek(cm, &tk); - if (match(cm, &tk, ',')) { - peek(cm, &msg); - expect(cm, TKSTRLIT, NULL); - } - peek(cm, &tk); - expect(cm, ')', NULL); - expect(cm, ';', NULL); - - joinspan(&span->ex, tk.span.ex); - if (!msg.t && ccopt.cstd == STDC11) - warn(span, "static assert without message is a C23 extension"); - if (!eval(&ex, EVINTCONST)) { - error(&ex.span, "static assert expression is not an integer constant"); - } else if (iszero(ex)) { - if (msg.t) - error(&ex.span, "static assertion failed: %'S", msg.s, msg.len); - else - error(&ex.span, "static assertion failed"); - } -} - -static struct decl -pdecl(struct declstate *st, struct comp *cm) { - struct token tk; - struct decl decl; - bool iniallowed = st->kind != DFIELD && st->kind != DFUNCPARAM && st->kind != DCASTEXPR; - bool staticassertok = iniallowed; - bool first = 0; - - assert(!st->funcdef); - - if (st->varini || st->bitf) { - memset(&decl, 0, sizeof decl); - goto AfterIniBitf; - } - - if (!st->base.t) { - if (staticassertok && (match(cm, &tk, TKW_Static_assert) || match(cm, &tk, TKWstatic_assert))) { - pstaticassert(cm, &tk.span); - return decl = (struct decl){0}; - } - first = 1; - st->scls = sclass(cm, &tk.span); - if (popcnt(st->scls) > 1) - error(&tk.span, "invalid combination of storage class specifiers"); - else { - int allowed; - switch (st->kind) { - case DTOPLEVEL: allowed = SCTYPEDEF | SCEXTERN | SCSTATIC | SCTHREADLOCAL; break; - case DCASTEXPR: allowed = 0; break; - case DFIELD: allowed = 0; break; - case DFUNCPARAM: allowed = 0; break; - case DFUNCVAR: - allowed = SCTYPEDEF | SCREGISTER | SCAUTO | SCEXTERN | SCSTATIC | SCTHREADLOCAL; - break; - default: assert(0); - } - if ((st->scls & allowed) != st->scls) - error(&tk.span, "this storage class is not allowed in this context"); - st->scls &= allowed; - } - declspec(st, cm); - } - if (st->scls == SCTYPEDEF) iniallowed = 0; - - if (first && st->tagdecl && match(cm, &tk, ';')) { - decl = (struct decl) { st->base, st->scls, st->qual, st->align, 0, tk.span }; - return decl; - } else if (st->kind == DFIELD && match(cm, &tk, ':')) { - decl = (struct decl) { st->base, st->scls, st->qual, st->align, 0, tk.span }; - st->bitf = 1; - return decl; - } - decl = declarator(st, cm); - - if (iniallowed && match(cm, &tk, '=')) { - st->varini = 1; - return decl; - } else if (first && decl.ty.t == TYFUNC && match(cm, &tk, '{')) { - st->funcdef = 1; - return decl; - } else if (st->kind == DFIELD && match(cm, &tk, ':')) { - st->bitf = 1; - return decl; - } - -AfterIniBitf: - st->varini = st->bitf = 0; - st->more = 0; - if (st->kind != DCASTEXPR && st->kind != DFUNCPARAM) { - if (match(cm, &tk, ',')) - st->more = 1; - else expect(cm, st->kind == DFUNCPARAM ? ')' : ';', "or `,'"); - } - - return decl; -} - -/*****************/ -/* IR Generation */ -/*****************/ - -static union ref expraddr(struct function *, const struct expr *); -static union ref compileexpr(struct function *, const struct expr *, bool discard); -static inline union ref -exprvalue(struct function *fn, const struct expr *ex) -{ - return compileexpr(fn, ex, /*discard*/ 0); -} -static inline void -expreffects(struct function *fn, const struct expr *ex) -{ - compileexpr(fn, ex, /*discard*/ 1); -} - -static void -structcopy(struct function *fn, union type ty, union ref dst, union ref src) -{ - union irtype typ = mkirtype(ty); - addinstr(fn, mkarginstr(typ, dst)); - addinstr(fn, mkarginstr(typ, src)); - addinstr(fn, mkintrin(INstructcopy, 0, 2)); -} - -static union ref -structreturn(struct function *fn, const struct expr *src) -{ - return expraddr(fn, src); -} - -static union ref compilecall(struct function *fn, const struct expr *ex); - -static const char * -mkhiddensym(const char *fnname, const char *name, int id) -{ - char buf[200]; - struct wbuf wbuf = MEMBUF(buf, sizeof buf); - assert(id > 0); - bfmt(&wbuf, "%s.%s.%d", fnname, name, id-1); - ioputc(&wbuf, 0); - assert(!wbuf.err); - return intern(buf); -} - -static void geninit(struct function *fn, union type t, union ref dst, const struct expr *src); - -static union ref -expraddr(struct function *fn, const struct expr *ex) -{ - struct decl *decl; - union ref r; - struct instr ins = {0}; - - switch (ex->t) { - case ESYM: - decl = ex->sym; - assert(decl != NULL); - switch (decl->scls) { - case SCAUTO: case SCREGISTER: - return mkref(RTMP, decl->id); - case SCEXTERN: case SCNONE: - return mksymref(decl->name); - case SCSTATIC: - if (!decl->id) - return mksymref(decl->name); - else - return mksymref(mkhiddensym(fn->name, decl->name, decl->id)); - default: - assert(0); - } - break; - case ESTRLIT: - /* XXX endian for wide strs */ - return mkdatref(NULL, typesize(ex->ty), typealign(ex->ty), ex->s.p, ex->s.n * typesize(typechild(ex->ty)), /*deref*/0); - case EDEREF: - return exprvalue(fn, ex->sub); - case EGETF: - r = expraddr(fn, ex->sub); - assert(ex->fld.bitsiz == 0); - if (ex->fld.off == 0) return r; - ins.cls = KPTR; - ins.op = Oadd; - ins.l = r; - ins.r = mkintcon(KI4, ex->fld.off); - return addinstr(fn, ins); - case ESET: - assert(isagg(ex->ty)); - r = expraddr(fn, &ex->sub[1]); - structcopy(fn, ex->ty, expraddr(fn, &ex->sub[0]), r); - return r; - case ESEQ: - expreffects(fn, &ex->sub[0]); - return expraddr(fn, &ex->sub[1]); - case ECALL: - assert(isagg(ex->ty)); - return compilecall(fn, ex); - case EINIT: - if (fn) { - /* compound literal, allocate temp */ - r = addinstr(fn, mkalloca(typesize(ex->ty), typealign(ex->ty))); - geninit(fn, ex->ty, r, ex); - return r; - } else { - } - default: - assert(!"lvalue?>"); - } - -} - -static union ref -genload(struct function *fn, union type t, union ref ref) -{ - struct instr ins = {0}; - - assert(isscalar(t)); - ins.cls = type2cls[scalartypet(t)]; - assert(ins.cls); - switch (typesize(t)) { - case 1: ins.op = issigned(t) ? Oloads1 : Oloadu1; break; - case 2: ins.op = issigned(t) ? Oloads2 : Oloadu2; break; - case 4: ins.op = isflt(t) ? Oloadf4 : issigned(t) ? Oloads4 : Oloadu4; break; - case 8: ins.op = isflt(t) ? Oloadf8 : Oloadi8; break; - default: assert(0); - } - ins.l = ref; - return addinstr(fn, ins); -} - -static union ref -genstore(struct function *fn, union type t, union ref ptr, union ref val) -{ - struct instr ins = {0}; - - assert(isscalar(t)); - switch (typesize(t)) { - case 1: ins.op = Ostore1; break; - case 2: ins.op = Ostore2; break; - case 4: ins.op = Ostore4; break; - case 8: ins.op = Ostore8; break; - default: assert(0); - } - ins.l = ptr; - ins.r = val; - return addinstr(fn, ins); -} - -static void -geninit(struct function *fn, union type t, union ref dst, const struct expr *src) -{ - union ref adr; - if (src->t == EINIT) { - struct init *ini = src->init; - uint siz = typesize(t); - if (BSSIZE(siz) <= arraylength(ini->zero) && bscount(ini->zero, arraylength(ini->zero)) < 32) { - /* write individual zeros at non initialized gaps */ - for (uint i = 0; bsiter(&i, ini->zero, arraylength(ini->zero)) && i < siz; ++i) { - /* TODO coalesce into multibyte zero writes */ - adr = i == 0 ? dst : addinstr(fn, mkinstr(Oadd, KPTR, dst, mkref(RICON, i))); - genstore(fn, mktype(TYCHAR), adr, ZEROREF); - } - } else { - /* memset(dst,0,siz) */ - /* TODO make it into an intrinsic */ - struct instr call = { Ocall, KPTR }; - addinstr(fn, mkarginstr(cls2type(KPTR), dst)); - addinstr(fn, mkarginstr(cls2type(KI4), ZEROREF)); - addinstr(fn, mkarginstr(cls2type(type2cls[targ_sizetype]), mkintcon(type2cls[targ_sizetype], siz))); - call.l = mksymref("memset"); - call.r = mkcallarg(cls2type(KPTR), 3, -1); - addinstr(fn, call); - } - for (struct initval *val = ini->vals; val; val = val->next) { - uint off = val->off; - struct expr *ex = &val->ex; - adr = off == 0 ? dst : addinstr(fn, mkinstr(Oadd, KPTR, dst, mkref(RICON, off))); - genstore(fn, ex->ty, adr, exprvalue(fn, ex)); - } - } else if (src->t == ESTRLIT) { - adr = dst; - for (uint i = 0; i < src->s.n; ++i) { - genstore(fn, mktype(TYCHAR), adr, mkref(RICON, src->s.p[i])); - adr = addinstr(fn, mkinstr(Oadd, KPTR, dst, mkref(RICON, i+1))); - } - genstore(fn, mktype(TYCHAR), adr, ZEROREF); /* null term */ - } else assert(0); -} - -static union ref -cvt(struct function *fn, union type to, union type from, union ref ref) -{ - enum irclass kto = type2cls[scalartypet(to)], kfrom = type2cls[scalartypet(from)]; - struct instr ins = {0}; - assert(kto && kfrom); - if (kto == kfrom && to.t != TYBOOL) return ref; - if (ref.t == RICON && kto < KF4) return ref; - - ins.cls = kto; - ins.l = ref; - if (kisflt(kto) || kisflt(kfrom)) { - if (ref.t == RICON) { - assert(kisflt(kto) && kisint(kfrom)); - return mkfltcon(kto, kto == KF4 ? (float)ref.i : (double)ref.i); - } - if (kisflt(kto) && kfrom == KI4) ins.op = issigned(from) ? Ocvts4f : Ocvtu4f; - else if (to.t == TYBOOL && kisflt(kfrom)) ins.op = Oneq, ins.r = mkfltcon(kfrom, 0.0); - else if (kisflt(kto) && kfrom == KI8) ins.op = issigned(from) ? Ocvts8f : Ocvtu8f; - else if (kto == KF8 && kfrom == KF4) ins.op = Ocvtf4f8; - else if (kto == KF4 && kfrom == KF8) ins.op = Ocvtf8f4; - else if (kfrom == KF4) ins.op = issigned(to) ? Ocvtf4s : Ocvtf4u; - else if (kfrom == KF8) ins.op = issigned(to) ? Ocvtf8s : Ocvtf8u; - else assert(0); - } else { - if (to.t == TYBOOL) { - if (from.t == TYBOOL) return ref; - if (ref.t == RTMP) - /* these instrs already have output range of [0,1] */ - if (oiscmp(instrtab[ref.i].op)) - return ref; - ins.op = Oneq, ins.r = ZEROREF; - } - else if (kfrom == KI4 && issigned(from)) ins.op = Oexts4; - else if (kfrom == KI4) ins.op = Oextu4; - else if (kto == KI4 && isintcon(ref)) - return issigned(to) ? mkintcon(kto, (int)intconval(ref)) : mkintcon(kto, (uint)intconval(ref)); - else ins.op = Ocopy; - } - return addinstr(fn, ins); -} - -static union ref -narrow(struct function *fn, enum irclass to, union type t, union ref ref, uint bitsiz) -{ - struct instr ins = {0}; - enum typetag tt = scalartypet(t); - assert(isscalar(t)); - if (targ_primsizes[tt] < cls2siz[to]) { - ins.cls = to; - if (isfltt(tt)) { - assert(to == KF4 && tt >= TYDOUBLE); - ins.op = Ocvtf8f4; - } else { - static const enum op ext[5][2] = { - [1] = {Oextu1, Oexts1}, [2] = {Oextu2, Oexts2}, [4] = {Oextu4, Oexts4} - }; - ins.op = ext[targ_primsizes[tt]][issignedt(tt)]; - } - ins.l = ref; - ref = addinstr(fn, ins); - } - if (bitsiz) { - assert(kisint(to) && isintt(tt) && bitsiz < 8*targ_primsizes[tt]); - if (!issignedt(tt)) { - ref = addinstr(fn, mkinstr(Oand, to, .l = ref, .r = mkintcon(to, (1ull< 0); - a = addinstr(fn, mkinstr(Osub, cls, .l = a, .r = b)); - if (siz == 1) return a; - else if ((siz & (siz-1)) == 0) /* is power of 2 */ - return addinstr(fn, mkinstr(Osar, cls, a, mkintcon(cls, ilog2(siz)))); - else - return addinstr(fn, mkinstr(Odiv, cls, a, mkintcon(cls, siz))); -} - -/* used to emit the jumps in an in if (), while (), etc condition */ -static void -condjump(struct function *fn, const struct expr *ex, struct block *tr, struct block *fl) -{ - struct block *next, *next2; -Loop: - while (ex->t == ESEQ) { - expreffects(fn, &ex->sub[0]); - ex = &ex->sub[1]; - } - if (ex->t == ELOGAND) { - next = newblk(fn); - condjump(fn, &ex->sub[0], next, fl); - useblk(fn, next); - ex = &ex->sub[1]; - goto Loop; - } else if (ex->t == ELOGIOR) { - next = newblk(fn); - condjump(fn, &ex->sub[0], tr, next); - useblk(fn, next); - ex = &ex->sub[1]; - goto Loop; - } else if (ex->t == ECOND) { - next = newblk(fn); - next2 = newblk(fn); - condjump(fn, &ex->sub[0], next, next2); - useblk(fn, next); - condjump(fn, &ex->sub[1], tr, fl); - useblk(fn, next2); - condjump(fn, &ex->sub[2], tr, fl); - } else if (ex->t == ELOGNOT) { - Negate: - /* swap tr,fl */ - next = tr; - tr = fl; - fl = next; - ex = &ex->sub[0]; - goto Loop; - } else if (ex->t == EEQU && isnullpo(&ex->sub[1])) { /* == 0 */ - goto Negate; - } else if (ex->t == ENEQ && isnullpo(&ex->sub[1])) { /* != 0 */ - ex = &ex->sub[0]; - goto Loop; - } else { - putcondbranch(fn, exprvalue(fn, ex), tr, fl); - } -} - -struct condphis { - vec_of(union ref) ref; -}; - -static void -condexprrec(struct function *fn, const struct expr *ex, struct condphis *phis, - int boolcon, struct block *const next, struct block *end) -{ - struct block *tr, *fl; - while (ex->t == ESEQ) { - expreffects(fn, &ex->sub[0]); - ex = &ex->sub[1]; - } - if (ex->t == ELOGAND) { - 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) { - 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, end); - useblk(fn, fl); - condexprrec(fn, &ex->sub[2], phis, -1, end, end); - } else { - union ref r, val; - if (!phis && (!next || next == end)) { - expreffects(fn, ex); - } else { - val = r = exprvalue(fn, ex); - if (boolcon >= 0) { - if (!next || next == end) { - boolcon = -1; - val = cvt(fn, mktype(TYBOOL), ex->ty, r); - } else { - val = mkref(RICON, boolcon); - } - } - } - if (phis) - vpush(&phis->ref, val); - if (next && next != end) { - putcondbranch(fn, r, next, end); - } else { - assert(boolcon < 0); - putbranch(fn, end); - } - } -} - -/* the naive way to generate something like a ? b : c ? d : e, uses multiple phis, - * this code reduces such nested conditional expressions into one phi */ -static union ref -condexprvalue(struct function *fn, const struct expr *ex, bool discard) -{ - union ref refbuf[8]; - struct condphis phis = { VINIT(refbuf, arraylength(refbuf)) }; - struct block *dst = newblk(fn); - union ref r; - enum irclass k; - condexprrec(fn, ex, discard ? NULL : &phis, -1, NULL, dst); - useblk(fn, dst); - if (discard) return NOREF; - k = type2cls[scalartypet(ex->ty)]; - assert(k); - r = addphi(fn, k, phis.ref.p); - vfree(&phis.ref); - return r; -} - -static union ref -compilecall(struct function *fn, const struct expr *ex) -{ - struct instr ins = {0}; - struct expr *sub = ex->sub; - const struct typedata *td = &typedata[sub[0].ty.dat]; - struct instr insnsbuf[10]; - vec_of(struct instr) insns = VINIT(insnsbuf, arraylength(insnsbuf)); - - ins.op = Ocall; - if (isagg(ex->ty)) { - ins.cls = KPTR; - } else { - assert(isscalar(ex->ty) || ex->ty.t == TYVOID); - ins.cls = type2cls[scalartypet(ex->ty)]; - assert(ins.cls || ex->ty.t == TYVOID); - } - ins.l = exprvalue(fn, &sub[0]); - for (int i = 0; i < ex->narg; ++i) { - struct expr *arg = &sub[i+1]; - union type ty = i < td->nmemb ? td->param[i] : argpromote(arg->ty); - union ref r = cvt(fn, ty, arg->ty, exprvalue(fn, arg)); - vpush(&insns, mkarginstr(mkirtype(ty), r)); - } - for (int i = 0; i < insns.n; ++i) - addinstr(fn, insns.p[i]); - vfree(&insns); - ins.r = mkcallarg(mkirtype(ex->ty), ex->narg, td->variadic ? td->nmemb : td->kandr ? 0 : -1); - return addinstr(fn, ins); -} - -static union ref -genbitfload(struct function *fn, const union type ty, union ref *addr, const struct exgetfld *fld) -{ - enum irclass k = type2cls[scalartypet(ty)]; - uint off = fld->off, bitsiz = fld->bitsiz, bitoff = fld->bitoff; - union ref tmp; - uvlong mask; - - assert(k); - if (off > 0) - *addr = addinstr(fn, mkinstr(Oadd, KPTR, .l = *addr, .r = mkintcon(KI4, off))); - tmp = genload(fn, ty, *addr); - if (!issigned(ty)) { - /* shift right and mask */ - if (bitoff > 0) - tmp = addinstr(fn, mkinstr(Oslr, k, .l = tmp, .r = mkref(RICON, bitoff))); - if (bitsiz < 8*typesize(ty)) { - mask = bitsiz == 64 ? -1ull : (1ull << bitsiz) - 1; - tmp = addinstr(fn, mkinstr(Oand, k, .l = tmp, .r = mkintcon(k, mask))); - } - } else { - /* shift left and shift right arithmetic to propagate sign bit */ - int sh = 8*cls2siz[k] - bitsiz - bitoff; - if (sh) - tmp = addinstr(fn, mkinstr(Oshl, k, .l = tmp, .r = mkref(RICON, sh))); - sh += bitoff; - if (sh) - tmp = addinstr(fn, mkinstr(Osar, k, .l = tmp, .r = mkref(RICON, sh))); - } - return tmp; -} - -static void -genbitfstore(struct function *fn, const union type ty, union ref addr, - const struct exgetfld *fld, union ref tmp, union ref val) -{ - enum irclass k = type2cls[scalartypet(ty)]; - uint off = fld->off, bitsiz = fld->bitsiz, bitoff = fld->bitoff; - uint bittypesize = 8*typesize(ty); - uvlong mask; - - assert(k); - if (!tmp.bits) { - if (off > 0) - addr = addinstr(fn, mkinstr(Oadd, KPTR, .l = addr, .r = mkintcon(KI4, off))); - tmp = genload(fn, ty, addr); - } - mask = (bitsiz == 64 ? -1ull : (1ull << bitsiz) - 1) << bitoff; - - /* mask out bits in existing container */ - tmp = addinstr(fn, mkinstr(Oand, k, .l = tmp, .r = mkintcon(k, ~mask))); - - /* shift and mask source value */ - if (isintcon(val)) { - val = mkintcon(k, ((uvlong)intconval(val) << bitoff) & mask); - } else { - if (bitoff) - val = addinstr(fn, mkinstr(Oshl, k, .l = val, .r = mkref(RICON, bitoff))); - if (bitsiz < bittypesize) - val = addinstr(fn, mkinstr(Oand, k, .l = val, .r = mkintcon(k, mask))); - } - /* combine and write */ - if (bitsiz < bittypesize) - val = addinstr(fn, mkinstr(Oior, k, .l = tmp, .r = val)); - genstore(fn, ty, addr, val); -} - -static union ref -compileexpr(struct function *fn, const struct expr *ex, bool discard) -{ - union type ty; - union ref r, q; - uint bitsiz; - enum irclass cls = type2cls[scalartypet(ex->ty)]; - struct instr ins = {0}; - int swp = 0; - struct expr *sub; - - eval((struct expr *)ex, EVFOLD); - sub = ex->sub; - - if (ex->ty.t != TYVOID && !isscalar(ex->ty)) - /* fn & array designators evaluate to their address; - * so do aggregates for the purpose of code generation */ - return expraddr(fn, ex); - switch (ex->t) { - case ENUMLIT: - if (discard) return NOREF; - if (isflt(ex->ty)) - return mkfltcon(cls, ex->f); - return mkintcon(cls, ex->i); - case ESYM: - if (discard && !(ex->qual & QVOLATILE)) return NOREF; - return genload(fn, ex->ty, expraddr(fn, ex)); - case EGETF: - if (discard && !(ex->qual & QVOLATILE)) return NOREF; - if (ex->fld.bitsiz) { - /* bit-field */ - r = expraddr(fn, ex->sub); - return genbitfload(fn, ex->ty, &r, &ex->fld); - } - return genload(fn, ex->ty, expraddr(fn, ex)); - case ECAST: - if (ex->ty.t == TYVOID) { - expreffects(fn, sub); - return NOREF; - } - /* fallthru */ - case EPLUS: - r = compileexpr(fn, sub, discard); - if (discard) return NOREF; - return cvt(fn, ex->ty, sub->ty, r); - case ENEG: - ins.op = Oneg; - goto Unary; - case ECOMPL: - ins.op = Onot; - Unary: - ins.l = compileexpr(fn, sub, discard); - if (discard) return NOREF; - ins.l = cvt(fn, ex->ty, sub->ty, ins.l); - ins.cls = cls; - return addinstr(fn, ins); - case ELOGNOT: - for (; sub->t == ELOGNOT; ex = sub, sub = sub->sub) - swp ^= 1; - ins.op = Oequ + swp; - ins.l = compileexpr(fn, sub, discard); - if (discard) return NOREF; - ins.l = cvt(fn, ex->ty, sub->ty, ins.l); - ins.r = mkintcon(cls, 0); - ins.cls = cls; - return addinstr(fn, ins); - case EDEREF: - discard &= (ex->qual & QVOLATILE) == 0; - r = compileexpr(fn, sub, discard); - if (discard) return NOREF; - return genload(fn, ex->ty, r); - case EADDROF: - return expraddr(fn, sub); - case EMUL: - ins.op = isunsigned(ex->ty) ? Oumul : Omul; - goto BinArith; - case EDIV: - ins.op = isunsigned(ex->ty) ? Oudiv : Odiv; - goto BinArith; - case EREM: - ins.op = issigned(ex->ty) ? Orem : Ourem; - goto BinArith; - case EBAND: - ins.op = Oand; - goto BinArith; - case EXOR: - ins.op = Oxor; - goto BinArith; - case EBIOR: - ins.op = Oior; - goto BinArith; - case ESHL: - ins.op = Oshl; - goto BinArith; - case ESHR: - ins.op = issigned(ex->ty) ? Osar : Oslr; - goto BinArith; - case ESUB: - ins.op = Osub; - goto BinArith; - case EADD: - ins.op = Oadd; - BinArith: - ins.l = compileexpr(fn, &sub[0], discard); - ins.r = compileexpr(fn, &sub[1], discard); - if (discard) return NOREF; - if (ins.op == Osub && isptrcvt(sub[0].ty) && isptrcvt(sub[1].ty)) { - /* ptr - ptr */ - return genptrdiff(fn, typesize(typechild(sub[0].ty)), ins.l, ins.r); - } else if ((ins.op != Oadd && ins.op != Osub) || cls != KPTR) { - /* num OP num */ - ins.l = cvt(fn, ex->ty, sub[0].ty, ins.l); - ins.r = cvt(fn, ex->ty, sub[1].ty, ins.r); - } else { - assert(isptrcvt(sub[0].ty)); - /* ptr +/- num */ - return genptroff(fn, ins.op, typesize(typechild(sub[0].ty)), ins.l, sub[1].ty, ins.r); - } - ins.cls = cls; - return addinstr(fn, ins); - case EPOSTINC: - case EPOSTDEC: - ins.op = ex->t == EPOSTINC ? Oadd : Osub; - ins.cls = cls; - r = expraddr(fn, sub); - ins.l = genload(fn, sub->ty, r); - if (ex->ty.t == TYPTR) - ins.r = mkintcon(KI4, typesize(typechild(ex->ty))); - else - ins.r = mkref(RICON, 1); - genstore(fn, sub->ty, r, addinstr(fn, ins)); - return ins.l; - case EPREINC: - case EPREDEC: - ins.op = ex->t == EPREINC ? Oadd : Osub; - ins.cls = cls; - r = expraddr(fn, sub); - ins.l = genload(fn, sub->ty, r); - if (ex->ty.t == TYPTR) - ins.r = mkintcon(KI4, typesize(typechild(ex->ty))); - else - ins.r = mkref(RICON, 1); - q = addinstr(fn, ins); - genstore(fn, sub->ty, r, q); - if (discard) return NOREF; - return narrow(fn, cls, ex->ty, q, 0); - case EEQU: - ins.op = Oequ; - goto Cmp; - case ENEQ: - ins.op = Oneq; - goto Cmp; - case ELTH: - ins.op = Olth; - goto Cmp; - case ELTE: - ins.op = Olte; - goto Cmp; - case EGTH: - ins.op = Ogth; - goto Cmp; - case EGTE: - ins.op = Ogte; - Cmp: - ty = cvtarith(sub[0].ty, sub[1].ty); - if (!ty.t) ty.t = TYPTR; - if (isunsigned(ty) && in_range(ins.op, Olth, Ogte)) - ins.op += Oulth - Olth; - ins.l = compileexpr(fn, &sub[0], discard); - ins.r = compileexpr(fn, &sub[1], discard); - if (discard) return NOREF; - ins.l = cvt(fn, ty, sub[0].ty, ins.l); - ins.r = cvt(fn, ty, sub[1].ty, ins.r); - ins.cls = type2cls[ty.t]; - return addinstr(fn, ins); - case ESET: - assert(isscalar(ex->ty)); - q = cvt(fn, sub[0].ty, sub[1].ty, exprvalue(fn, &sub[1])); - if (sub[0].t == EGETF && (bitsiz = sub[0].fld.bitsiz)) { - /* bit-field */ - r = expraddr(fn, &sub[0].sub[0]); - genbitfstore(fn, ex->ty, r, &sub[0].fld, NOREF, q); - } else { - bitsiz = 0; - r = expraddr(fn, &sub[0]); - genstore(fn, ex->ty, r, q); - } - if (discard) return NOREF; - return bitsiz ? narrow(fn, cls, sub[0].ty, q, bitsiz) : q; - case ESETMUL: - ins.op = isunsigned(ex->ty) ? Oumul : Omul; - goto Compound; - case ESETDIV: - ins.op = isunsigned(ex->ty) ? Oudiv : Odiv; - goto Compound; - case ESETREM: - ins.op = issigned(ex->ty) ? Orem : Ourem; - goto Compound; - case ESETAND: - ins.op = Oand; - goto Compound; - case ESETXOR: - ins.op = Oxor; - goto Compound; - case ESETIOR: - ins.op = Oior; - goto Compound; - case ESETSHL: - ins.op = Oshl; - goto Compound; - case ESETSHR: - ins.op = issigned(ex->ty) ? Osar : Oslr; - goto Compound; - case ESETSUB: - ins.op = Osub; - goto Compound; - case ESETADD: - ins.op = Oadd; - Compound: - ty = in_range(ex->t, ESETSHL, ESETSHR) ? mktype(intpromote(ex->ty.t)) - : cvtarith(sub[0].ty, sub[1].ty); - ins.cls = cls; - ins.r = exprvalue(fn, &sub[1]); - if (sub[0].t == EGETF && (bitsiz = sub[0].fld.bitsiz)) { - /* bit-field */ - r = expraddr(fn, &sub[0].sub[0]); - ins.l = genbitfload(fn, sub[0].ty, &r, &sub[0].fld); - q = addinstr(fn, ins); - genbitfstore(fn, sub[0].ty, r, &sub[0].fld, ins.l, q); - } else { - bitsiz = 0; - r = expraddr(fn, &sub[0]); - ins.l = genload(fn, ex->ty, r); - if ((ins.op != Oadd && ins.op != Osub) || cls != KPTR) { - ins.l = cvt(fn, ty, sub[0].ty, ins.l); - ins.r = cvt(fn, ex->ty, sub[1].ty, ins.r); - q = addinstr(fn, ins); - } else { - q = genptroff(fn, ins.op, typesize(typechild(ex->ty)), ins.l, sub[1].ty, ins.r); - } - genstore(fn, ex->ty, r, q); - } - if (discard) return NOREF; - return bitsiz ? narrow(fn, cls, ex->ty, q, bitsiz) : q; - case ECALL: - r = compilecall(fn, ex); - if (isint(ex->ty)) - return narrow(fn, cls, ex->ty, r, 0); - return r; - case ECOND: - if (ex->ty.t == TYVOID || discard) { - struct block *tr, *fl, *end; - condjump(fn, &sub[0], tr = newblk(fn), fl = newblk(fn)); - useblk(fn, tr); - expreffects(fn, &sub[1]); - end = newblk(fn); - putbranch(fn, end); - useblk(fn, fl); - expreffects(fn, &sub[2]); - putbranch(fn, end); - useblk(fn, end); - return NOREF; - } - /* fallthru */ - case ELOGAND: - case ELOGIOR: - return condexprvalue(fn, ex, discard); - case ESEQ: - expreffects(fn, &sub[0]); - return compileexpr(fn, &sub[1], discard); - default: assert(!"nyi expr"); - } -} - -/************************************/ -/* Statements parsing & compilation */ -/************************************/ - -static void -stmtterm(struct comp *cm) -{ - expect(cm, ';', "to terminate previous statement"); -} - -static void block(struct comp *cm, struct function *fn); -static bool stmt(struct comp *cm, struct function *fn); -static void localdecl(struct comp *cm, struct function *fn, bool forinit); - -struct label { - struct label *link; - const char *name; - struct block *blk; - struct span usespan; - /* if usespan.ex.len == 0, this label is resolved and blk is the block that - * the label starts, otherwise the label is unresolved and blk is the head - * of a linked list of relocations, the next list entry is in blk->s1, etc, - * terminated by NULL */ -}; - -static struct label * -findlabel(struct comp *cm, const char *name) -{ - for (struct label *l = cm->labels; l; l = l->link) - if (l->name == name) return l; - return NULL; -} - -static void -deflabel(struct comp *cm, struct function *fn, const struct span *span, const char *name) -{ - struct label *label = findlabel(cm, name); - if (label && label->usespan.ex.len == 0) { - error(span, "redefinition of label '%s'", name); - } else if (label) { - struct block *new = NULL; - if (!nerror) { - new = newblk(fn); - if (fn->curblk) putbranch(fn, new); - } - /* fix up relocations */ - for (struct block *list = label->blk, *next; list; list = next) { - next = list->s1; - if (!nerror) { - useblk(fn, list); - putbranch(fn, new); - } - } - label->usespan = (struct span){0}; - label->blk = fn->curblk; - if (!nerror) useblk(fn, new); - } else { - struct label l = { cm->labels, name }; - if (!nerror) { - struct block *new = newblk(fn); - if (fn->curblk) putbranch(fn, new); - useblk(fn, new); - } - l.blk = fn->curblk; - cm->labels = alloccopy(fn->arena, &l, sizeof l, 0); - } -} - -static bool -loopbody(struct comp *cm, struct function *fn, struct block *brk, struct block *cont) -{ - struct block *save[2]; - bool terminates = 0; - - save[0] = cm->breakto, save[1] = cm->loopcont; - cm->breakto = brk, cm->loopcont = cont; - ++cm->loopdepth; - - terminates = stmt(cm, fn); - - --cm->loopdepth; - cm->breakto = save[0], cm->loopcont = save[1]; - - return terminates; -} - -#define EMITS if (doemit && !nerror) - -struct swcase { - vlong val; - struct block *blk; -}; -struct switchstmt { - struct block *bdefault; - vec_of(struct swcase) cases; -}; - -static void -genswitch(struct comp *cm, struct function *fn, const struct expr *ex) -{ - union ref sel; - bool doemit = fn->curblk; - struct block *begin = NULL, *end = NULL, *breaksave = cm->breakto; - struct switchstmt *stsave = cm->switchstmt, st = {0}; - enum irclass k = type2cls[scalartypet(ex->ty)]; - struct swcase casebuf[8]; - vinit(&st.cases, casebuf, arraylength(casebuf)); - - assert(k); - end = newblk(fn); - EMITS { - sel = exprvalue(fn, ex); - assert(isint(ex->ty)); - } - cm->switchstmt = &st; - cm->breakto = end; - begin = fn->curblk; - fn->curblk = NULL; - ++cm->switchdepth; - stmt(cm, fn); - --cm->switchdepth; - doemit = fn->curblk; - cm->switchstmt = stsave; - cm->breakto = breaksave; - - EMITS putbranch(fn, end); - useblk(fn, begin); - doemit = 1; - if (!st.bdefault) st.bdefault = end; - /* TODO: optimize instead of generating the equivalent of if == .. else if .. chain - * 1. sort by case values (also for easy duplicates checking) - * 2. contiguous ranges (case a..b: -> x >= && x <= b) - * 3. binary search - * 4. jump tables? (harder) - */ - for (int i = 0; i < st.cases.n; ++i) { - struct swcase c = st.cases.p[i]; - EMITS { - struct block *next = i < st.cases.n - 1 ? newblk(fn) : st.bdefault; - putcondbranch(fn, addinstr(fn, mkinstr(Oequ, k, .l = sel, .r = mkintcon(k, c.val))), c.blk, next); - if (next != st.bdefault) useblk(fn, next); - } - } - vfree(&st.cases); - if (fn->curblk != end) { - if (fn->curblk) EMITS putbranch(fn, end); - useblk(fn, end); - } -} - -static bool /* return 1 if stmt is terminating (ends with a jump) */ -stmt(struct comp *cm, struct function *fn) -{ - struct block *tr, *fl, *end, *begin; - union { - struct arena a; - char mem[sizeof(struct arena) + sizeof(struct expr)*4]; - } atmp = { .a.cap = sizeof(struct expr)*4 }; - struct arena *atmpp; - struct expr ex; - struct env e; - union ref r; - struct token tk; - bool terminates = 0; - bool doemit = fn->curblk; - - while (match(cm, &tk, TKIDENT) || match(cm, &tk, TKWcase) || match(cm, &tk, TKWdefault)) { - if (tk.t == TKWcase) { - /* case ':' */ - if (!cm->switchstmt) error(&tk.span, "'case' outside of switch statement"); - ex = constantexpr(cm); - if (!eval(&ex, EVINTCONST)) error(&ex.span, "not an integer constant expression"); - expect(cm, ':', NULL); - begin = newblk(fn); - EMITS putbranch(fn, begin); - useblk(fn, begin); - if (cm->switchstmt) - vpush(&cm->switchstmt->cases, ((struct swcase) {ex.i, fn->curblk})); - } else if (tk.t == TKWdefault) { - /* default ':' */ - if (!cm->switchstmt) error(&tk.span, "'default' outside of switch statement"); - expect(cm, ':', NULL); - begin = newblk(fn); - EMITS putbranch(fn, begin); - useblk(fn, begin); - if (cm->switchstmt) { - if (cm->switchstmt->bdefault) error(&tk.span, "multiple 'default' labels in one switch"); - cm->switchstmt->bdefault = begin; - } - } else if (tk.t == TKIDENT && match(cm, NULL, ':')) { - /*