static Node * mkunop(SrcPos *p, int op, Node *o) { Node *n; n = mknode(NUNOP, p); n->Unop.op = op; switch(op) { case '&': if(!islval(o)) errorposf(&o->pos, "& expects an lvalue"); n->type = mkptr(o->type); break; case '*': if(!isptr(o->type)) errorposf(&o->pos, "cannot deref non pointer"); n->type = o->type->Ptr.subty; break; default: o = ipromote(o); n->type = o->type; break; } n->Unop.operand = o; return n; }
static Node * decl() { Node *n, *init; char *name; CTy *type, *basety; SrcPos *pos; Sym *sym; Vec *syms; int sclass; pos = &tok->pos; syms = vec(); basety = declspecs(&sclass); while(tok->k != ';' && tok->k != TOKEOF) { type = declarator(basety, &name, &init); switch(sclass){ case SCNONE: if(isglobal()) { sclass = SCGLOBAL; } else { sclass = SCAUTO; } break; case SCTYPEDEF: if(init) errorposf(pos, "typedef cannot have an initializer"); break; } if(!name) errorposf(pos, "decl needs to specify a name"); sym = definesym(pos, sclass, name, type, init); vecappend(syms, sym); if(isglobal() && tok->k == '{') { if(init) errorposf(pos, "function declaration has an initializer"); if(type->t != CFUNC) errorposf(pos, "expected a function"); curfunc = mknode(NFUNC, pos); curfunc->type = type; curfunc->Func.name = name; curfunc->Func.params = vec(); curfunc->Func.stkslots = vec(); fbody(); definesym(pos, sclass, name, type, curfunc); curfunc = 0; goto done; } if(tok->k == ',') next(); else break; } expect(';'); done: n = mknode(NDECL, pos); n->Decl.syms = syms; return n; }
/* Declarator is what introduces names into the program. */ static CTy * declarator(CTy *basety, char **name, Node **init) { CTy *t; while (tok->k == TOKCONST || tok->k == TOKVOLATILE) next(); switch(tok->k) { case '*': next(); basety = mkptr(basety); t = declarator(basety, name, init); return t; default: t = directdeclarator(basety, name); if(tok->k == '=') { if(!init) errorposf(&tok->pos, "unexpected initializer"); next(); *init = declinit(); } else { if(init) *init = 0; } return t; } }
static void fbody(void) { Node *gotofixup; int i; char *l; NameTy *nt; Sym *sym; pushscope(); labels = map(); gotos = vec(); for(i = 0; i < curfunc->type->Func.params->len; i++) { nt = vecget(curfunc->type->Func.params, i); if(nt->name) { sym = definesym(&curfunc->pos, SCAUTO, nt->name, nt->type, 0); vecappend(curfunc->Func.params, sym); } } curfunc->Func.body = block(); popscope(); for(i = 0 ; i < gotos->len ; i++) { gotofixup = vecget(gotos, i); l = mapget(labels, gotofixup->Goto.name); if(!l) errorposf(&gotofixup->pos, "goto target does not exist"); gotofixup->Goto.l = l; } }
static void params(CTy *fty) { int sclass; CTy *t; char *name; SrcPos *pos; fty->Func.isvararg = 0; if(tok->k == ')') return; for(;;) { pos = &tok->pos; t = declspecs(&sclass); t = declarator(t, &name, 0); if(sclass != SCNONE) errorposf(pos, "storage class not allowed in parameter decl"); vecappend(fty->Func.params, newnamety(name, t)); if(tok->k != ',') break; next(); } if(tok->k == TOKELLIPSIS) { fty->Func.isvararg = 1; next(); } }
static CTy * directdeclarator(CTy *basety, char **name) { CTy *ty, *stub; *name = 0; switch(tok->k) { case '(': expect('('); stub = gcmalloc(sizeof(CTy)); *stub = *basety; ty = declarator(stub, name, 0); expect(')'); *stub = *declaratortail(basety); return ty; case TOKIDENT: if(name) *name = tok->v; next(); return declaratortail(basety); default: if(!name) errorposf(&tok->pos, "expected ident or ( but got %s", tokktostr(tok->k)); return declaratortail(basety); } errorf("unreachable"); return 0; }
static void call(Node *n) { int i, nargs, nintargs, cleanup; Vec *args; Node *arg; args = n->Call.args; i = nargs = args->len; /* Push args in reverse order */ while(i-- != 0) { arg = vecget(args, i); if(!isitype(arg->type) && !isptr(arg->type) && !isarray(arg->type) && !isfunc(arg->type)) errorposf(&arg->pos, "unimplemented arg type\n"); expr(arg); pushq("rax"); } nintargs = nargs; if(nintargs > 6) nintargs = 6; for(i = 0; i < nintargs; i++) popq(intargregs[i]); expr(n->Call.funclike); outi("call *%%rax\n"); cleanup = 8 * (nargs - nintargs); if(cleanup) { outi("add $%d, %%rsp\n", cleanup); stackoffset -= cleanup; } }
static void expect(int kind) { if(tok->k != kind) errorposf(&tok->pos,"expected %s, got %s", tokktostr(kind), tokktostr(tok->k)); next(); }
static CTy * declaratortail(CTy *basety) { SrcPos *p; CTy *t, *newt; Const *c; t = basety; for(;;) { c = 0; switch (tok->k) { case '[': newt = newtype(CARR); newt->Arr.subty = t; newt->Arr.dim = -1; next(); if(tok->k != ']') { p = &tok->pos; c = constexpr(); if(c->p) errorposf(p, "pointer derived constant in array size"); newt->Arr.dim = c->v; newt->size = newt->Arr.dim * newt->Arr.subty->size; } newt->align = newt->Arr.subty->align; expect(']'); t = newt; break; case '(': newt = newtype(CFUNC); newt->Func.rtype = basety; newt->Func.params = vec(); next(); params(newt); if(tok->k != ')') errorposf(&tok->pos, "expected valid parameter or )"); next(); t = newt; break; default: return t; } }
static Node * mkincdec(SrcPos *p, int op, int post, Node *operand) { Node *n; if(!islval(operand)) errorposf(&operand->pos, "++ and -- expects an lvalue"); n = mknode(NINCDEC, p); n->Incdec.op = op; n->Incdec.post = post; n->Incdec.operand = operand; n->type = operand->type; return n; }
static void data(Data *d) { InitMember *initmemb; int i, offset; char *l; if(!d->init) { out(".comm %s, %d, %d\n", d->label, d->type->size, d->type->align); return; } if(d->isglobal) out(".globl %s\n", d->label); out("%s:\n", d->label); if(ischararray(d->type)) if(d->init->t == NSTR) { out(".string %s\n", d->init->Str.v); return; } if(ischarptr(d->type)) if(d->init->t == NSTR) { l = newlabel(); out(".quad %s\n", l); out("%s:\n", l); out(".string %s\n", d->init->Str.v); return; } if(isitype(d->type) || isptr(d->type)) { itypedata(d->init); return; } if(isarray(d->type) || isstruct(d->type)) { if(d->init->t != NINIT) errorposf(&d->init->pos, "array/struct expects a '{' style initializer"); offset = 0; for(i = 0; i < d->init->Init.inits->len ; i++) { initmemb = vecget(d->init->Init.inits, i); if(initmemb->offset != offset) out(".fill %d, 1, 0\n", initmemb->offset - offset); itypedata(initmemb->n); offset = initmemb->offset + initmemb->n->type->size; } if(offset < d->type->size) out(".fill %d, 1, 0\n", d->type->size - offset); return; } panic("internal error"); }
static void ereturn(Node *r) { CTy *ty; if(r->Return.expr) { ty = r->Return.expr->type; if(!isitype(ty) && !isptr(ty)) errorposf(&r->pos, "unimplemented return type"); expr(r->Return.expr); } /* No need to cleanup with leave */ outi("leave\n"); outi("ret\n"); }
static Node * ipromote(Node *n) { if(!isitype(n->type)) errorposf(&n->pos, "internal error - ipromote expects itype got %d", n->type->t); switch(n->type->Prim.type) { case PRIMCHAR: case PRIMSHORT: if(n->type->Prim.issigned) return mkcast(&n->pos, n, cint); else return mkcast(&n->pos, n, cuint); } return n; }
static Sym * defineenum(SrcPos *p, char *name, CTy *type, int64 v) { Sym *sym; sym = gcmalloc(sizeof(Sym)); sym->pos = p; sym->name = name; sym->type = type; sym->k = SYMENUM; sym->Enum.v = v; if(!define(syms, name, sym)) errorposf(p, "redefinition of %s", name); return sym; }
static void func(Node *f, char *label, int isglobal) { Vec *v; Sym *sym; int i; calcslotoffsets(f); out("\n"); out(".text\n"); out("# function %s\n", f->Func.name); if(isglobal) out(".globl %s\n", label); out("%s:\n", label); pushq("rbp"); outi("movq %%rsp, %%rbp\n"); if(f->type->Func.isvararg) { stackoffset += 176; outi("sub $176, %%rsp\n"); outi("movq %%rdi, (%%rsp)\n"); outi("movq %%rsi, 8(%%rsp)\n"); outi("movq %%rdx, 16(%%rsp)\n"); outi("movq %%rcx, 24(%%rsp)\n"); outi("movq %%r8, 32(%%rsp)\n"); outi("movq %%r9, 40(%%rsp)\n"); } if(f->Func.localsz) { outi("sub $%d, %%rsp\n", f->Func.localsz); stackoffset += f->Func.localsz; } v = f->Func.params; for(i = 0; i < v->len; i++) { sym = vecget(v, i); if(!isitype(sym->type) && !isptr(sym->type) && !isarray(sym->type)) errorposf(&f->pos, "unimplemented arg type"); if(i < 6) { outi("movq %%%s, %d(%%rbp)\n", intargregs[i], sym->Local.slot->offset); } else { outi("movq %d(%%rbp), %%rcx\n", 16 + 8 * (i - 6)); outi("leaq %d(%%rbp), %%rax\n", sym->Local.slot->offset); store(sym->type); } } block(f->Func.body); outi("leave\n"); outi("ret\n"); }
static void itypedata(Node *prim) { Const *c; if(!isitype(prim->type) && !isptr(prim->type)) panic("internal error %d"); c = foldexpr(prim); if(!c) errorposf(&prim->pos, "not a constant expression"); if(c->p) { switch(prim->type->size) { case 8: out(".quad %s + %d\n", c->p, c->v); return; case 4: out(".long %s + %d\n", c->p, c->v); return; case 2: out(".short %s + %d\n", c->p, c->v); return; case 1: out(".byte %s + %d\n", c->p, c->v); return; default: panic("unimplemented"); } } switch(prim->type->size) { case 8: out(".quad %d\n", c->v); return; case 4: out(".long %d\n", c->v); return; case 2: out(".short %d\n", c->v); return; case 1: out(".byte %d\n", c->v); return; default: panic("unimplemented"); } panic("internal error"); }
static Node * mkassign(SrcPos *p, int op, Node *l, Node *r) { Node *n; CTy *t; if(!islval(l)) errorposf(&l->pos, "assign expects an lvalue"); r = mkcast(p, r, l->type); t = l->type; n = mknode(NASSIGN, p); switch(op) { case '=': n->Assign.op = '='; break; case TOKADDASS: n->Assign.op = '+'; break; case TOKSUBASS: n->Assign.op = '-'; break; case TOKORASS: n->Assign.op = '|'; break; case TOKANDASS: n->Assign.op = '&'; break; case TOKMULASS: n->Assign.op = '*'; break; default: panic("mkassign"); } n->Assign.l = l; n->Assign.r = r; n->type = t; return n; }
static void directive() { Tok *t; char *dir; t = ppnoexpand(); dir = t->v; if(strcmp(dir, "include") == 0) include(); else if(strcmp(dir, "define") == 0) define(); else if(strcmp(dir, "if") == 0) pif(); else if(strcmp(dir, "elseif") == 0) elseif(); else if(strcmp(dir, "else") == 0) pelse(); else if(strcmp(dir, "endif") == 0) endif(); else errorposf(&t->pos, "invalid directive %s", dir); }
void addstructmember(SrcPos *pos, CTy *t, char *name, CTy *membt) { StructMember *sm, *subsm; int i, align, sz; sm = gcmalloc(sizeof(StructMember)); sm->name = name; sm->type = membt; if(!isstruct(t)) panic("internal error"); if(sm->name == 0 && isstruct(sm->type)) { for(i = 0; i < sm->type->Struct.members->len; i++) { subsm = vecget(sm->type->Struct.members, i); addstructmember(pos, t, subsm->name, subsm->type); } return; } if(sm->name) { for(i = 0; i < t->Struct.members->len; i++) { subsm = vecget(t->Struct.members, i); if(subsm->name) if(strcmp(sm->name, subsm->name) == 0) errorposf(pos ,"struct already has a member named %s", sm->name); } } if(membt->align < t->align) t->align = membt->align; sz = t->size; align = membt->align; if(sz % align) sz = sz + align - (sz % align); sm->offset = sz; sz += sm->type->size; t->size = sz; vecappend(t->Struct.members, sm); }
static Sym * definesym(SrcPos *p, int sclass, char *name, CTy *type, Node *n) { Sym *sym; if(sclass == SCAUTO || n != 0) if(type->incomplete) errorposf(p, "cannot use incomplete type in this context"); if(sclass == SCAUTO && isglobal()) errorposf(p, "defining local symbol in global scope"); sym = mapget(syms[nscopes - 1], name); if(sym) { switch(sym->k) { case SYMTYPE: if(sclass != SCTYPEDEF || !sametype(sym->type, type)) errorposf(p, "incompatible redefinition of typedef %s", name); break; case SYMGLOBAL: if(sym->Global.sclass != sclass) errorposf(p, "redefinition of %s with differing storage class", name); if(sym->init && n) errorposf(p, "%s already initialized", name); if(!sym->init && n) { sym->init = n; emitsym(sym); removetentativesym(sym); } break; default: errorposf(p, "redefinition of %s", name); } return sym; } sym = gcmalloc(sizeof(Sym)); sym->name = name; sym->type = type; sym->init = n; switch(sclass) { case SCAUTO: sym->k = SYMLOCAL; sym->Local.slot = gcmalloc(sizeof(StkSlot)); sym->Local.slot->size = sym->type->size; sym->Local.slot->align = sym->type->align; vecappend(curfunc->Func.stkslots, sym->Local.slot); break; case SCTYPEDEF: sym->k = SYMTYPE; break; case SCGLOBAL: sym->k = SYMGLOBAL; sym->Global.label = name; sym->Global.sclass = SCGLOBAL; break; case SCSTATIC: sym->k = SYMGLOBAL; sym->Global.label = newlabel(); sym->Global.sclass = SCSTATIC; break; } if(sym->k == SYMGLOBAL) { if(sym->init) emitsym(sym); else if(!isfunc(sym->type)) addtentativesym(sym); } if(!define(syms, name, sym)) panic("internal error"); return sym; }
static void expr(Node *n) { switch(n->t){ case NCOMMA: comma(n); break; case NCAST: cast(n); break; case NSTR: str(n); break; case NSIZEOF: outi("movq $%lld, %%rax\n", n->Sizeof.type->size); break; case NNUM: outi("movq $%lld, %%rax\n", n->Num.v); break; case NIDENT: ident(n); break; case NUNOP: unop(n); break; case NASSIGN: assign(n); break; case NBINOP: binop(n); break; case NIDX: idx(n); break; case NSEL: sel(n); break; case NCOND: cond(n); break; case NCALL: call(n); break; case NPTRADD: ptradd(n); break; case NINCDEC: incdec(n); break; case NBUILTIN: switch(n->Builtin.t) { case BUILTIN_VASTART: vastart(n); break; default: errorposf(&n->pos, "unimplemented builtin"); } break; default: errorf("unimplemented emit expr %d\n", n->t); } }
static CTy * declspecs(int *sclass) { CTy *t; SrcPos *pos; Sym *sym; int bits; enum { BITCHAR = 1<<0, BITSHORT = 1<<1, BITINT = 1<<2, BITLONG = 1<<3, BITLONGLONG = 1<<4, BITSIGNED = 1<<5, BITUNSIGNED = 1<<6, BITFLOAT = 1<<7, BITDOUBLE = 1<<8, BITENUM = 1<<9, BITSTRUCT = 1<<10, BITVOID = 1<<11, BITIDENT = 1<<12, }; t = 0; bits = 0; pos = &tok->pos; *sclass = SCNONE; for(;;) { if(issclasstok(tok)) { if(*sclass != SCNONE) errorposf(pos, "multiple storage classes in declaration specifiers."); switch(tok->k) { case TOKEXTERN: *sclass = SCEXTERN; break; case TOKSTATIC: *sclass = SCSTATIC; break; case TOKREGISTER: *sclass = SCREGISTER; break; case TOKAUTO: *sclass = SCAUTO; break; case TOKTYPEDEF: *sclass = SCTYPEDEF; break; default: panic("internal error"); } next(); continue; } switch(tok->k) { case TOKCONST: case TOKVOLATILE: next(); break; case TOKSTRUCT: case TOKUNION: if(bits) goto err; bits |= BITSTRUCT; t = ptag(); goto done; case TOKENUM: if(bits) goto err; bits |= BITENUM; t = ptag(); goto done; case TOKVOID: if(bits&BITVOID) goto err; bits |= BITVOID; next(); goto done; case TOKCHAR: if(bits&BITCHAR) goto err; bits |= BITCHAR; next(); break; case TOKSHORT: if(bits&BITSHORT) goto err; bits |= BITSHORT; next(); break; case TOKINT: if(bits&BITINT) goto err; bits |= BITINT; next(); break; case TOKLONG: if(bits&BITLONGLONG) goto err; if(bits&BITLONG) { bits &= ~BITLONG; bits |= BITLONGLONG; } else { bits |= BITLONG; } next(); break; case TOKFLOAT: if(bits&BITFLOAT) goto err; bits |= BITFLOAT; next(); break; case TOKDOUBLE: if(bits&BITDOUBLE) goto err; bits |= BITDOUBLE; next(); break; case TOKSIGNED: if(bits&BITSIGNED) goto err; bits |= BITSIGNED; next(); break; case TOKUNSIGNED: if(bits&BITUNSIGNED) goto err; bits |= BITUNSIGNED; next(); break; case TOKIDENT: sym = lookup(syms, tok->v); if(sym && sym->k == SYMTYPE) t = sym->type; if(t && !bits) { bits |= BITIDENT; next(); goto done; } /* fallthrough */ default: goto done; } } done: switch(bits){ case BITFLOAT: return cfloat; case BITDOUBLE: return cdouble; case BITLONG|BITDOUBLE: return cldouble; case BITSIGNED|BITCHAR: case BITCHAR: return cchar; case BITUNSIGNED|BITCHAR: return cuchar; case BITSIGNED|BITSHORT|BITINT: case BITSHORT|BITINT: case BITSHORT: return cshort; case BITUNSIGNED|BITSHORT|BITINT: case BITUNSIGNED|BITSHORT: return cushort; case BITSIGNED|BITINT: case BITSIGNED: case BITINT: case 0: return cint; case BITUNSIGNED|BITINT: case BITUNSIGNED: return cuint; case BITSIGNED|BITLONG|BITINT: case BITSIGNED|BITLONG: case BITLONG|BITINT: case BITLONG: return clong; case BITUNSIGNED|BITLONG|BITINT: case BITUNSIGNED|BITLONG: return culong; case BITSIGNED|BITLONGLONG|BITINT: case BITSIGNED|BITLONGLONG: case BITLONGLONG|BITINT: case BITLONGLONG: return cllong; case BITUNSIGNED|BITLONGLONG|BITINT: case BITUNSIGNED|BITLONGLONG: return cullong; case BITVOID: t = cvoid; return t; case BITENUM: case BITSTRUCT: case BITIDENT: return t; default: goto err; } err: errorposf(pos, "invalid declaration specifiers"); return 0; }