scc

Simple C99 Compiler
Log | Files | Refs | README | LICENSE

commit 84977e24ad1f367840d132c50b89d090a275ecbc
parent f089efb927e64fab733dc62d3e5627c832269cef
Author: Roberto E. Vargas Caballero <k0ga@shike2.com>
Date:   Fri,  7 Aug 2015 09:13:00 +0200

Emit function types before emiting functions

Function types are needed when a function is called, because
the ABI for a function like f(...) can be different to the
ABI for f(int), even in the case the receives only one
parameter.

This change emits parameters after emiting the function, so
we can keep the storage specifier of the parameters and free
P for pointers.

Diffstat:
cc1/cc1.h | 9+++++----
cc1/code.c | 42+++++++++++++++++++++++++++++++++---------
cc1/decl.c | 235++++++++++++++++++++++++++++++++++++-------------------------------------------
cc1/stmt.c | 6++++++
cc2/main.c | 2++
inc/cc.h | 2+-
6 files changed, 154 insertions(+), 142 deletions(-)

diff --git a/cc1/cc1.h b/cc1/cc1.h @@ -51,6 +51,7 @@ struct symbol { TFLOAT f; char *s; unsigned char token; + Symbol **pars; } u; struct symbol *next; struct symbol *hash; @@ -108,6 +109,7 @@ enum { FTN = 1, PTR, ARY, + PARS, }; /* namespaces */ @@ -129,10 +131,9 @@ enum { ISREGISTER = 8, ISDEFINED = 16, ISFIELD = 32, - ISPARAM = 64, - ISEXTERN = 128, - ISUSED = 256, - ISCONSTANT = 512 + ISEXTERN = 64, + ISUSED = 128, + ISCONSTANT = 256 }; diff --git a/cc1/code.c b/cc1/code.c @@ -147,6 +147,7 @@ emit(unsigned op, void *arg) (*opcode[op])(op, arg); } +/* TODO: move these letters to cc.h */ static void emitvar(Symbol *sym) { @@ -157,11 +158,9 @@ emitvar(Symbol *sym) else if (sym->flags & ISGLOBAL) c = 'G'; else if (sym->flags & ISREGISTER) - c = 'K'; + c = 'R'; else if (sym->flags & ISFIELD) c = 'M'; - else if (sym->flags & ISPARAM) - c = 'P'; else c = 'A'; printf("%c%d", c, sym->id); @@ -204,26 +203,44 @@ static void emitletter(Type *tp) { putchar(tp->letter); - if (tp->op == ARY) + switch (tp->op) { + case ARY: + case FTN: + case STRUCT: + case UNION: printf("%d", tp->id); + } } static void emittype(Type *tp) { + int n; + Type **vp; + if (tp->printed) return; switch (tp->op) { case ARY: emittype(tp->type); - printf("V%d\t", tp->id); + emitletter(tp); + putchar('\t'); emitletter(tp->type); printf("\t#%d\n", tp->n.elem); return; case PTR: emittype(tp->type); return; + case FTN: + emitletter(tp); + n = tp->n.elem; + for (vp = tp->pars; n-- > 0; ++vp) { + putchar('\t'); + emitletter(*vp); + } + putchar('\n'); + return; default: abort(); } @@ -238,7 +255,8 @@ emitdcl(unsigned op, void *arg) emitvar(sym); putchar('\t'); emitletter(sym->type); - putchar('\n'); + if (op != OFUN) + putchar('\n'); } static void @@ -275,10 +293,16 @@ emitexp(unsigned op, void *arg) static void emitfun(unsigned op, void *arg) { - Symbol *sym = arg; + Symbol *sym = arg, **sp; + int n; + + emitdcl(op, arg); + puts("\t{"); - printf("%c%d\tF\t%s\t{\n", - sym->flags & ISGLOBAL ? 'G' : 'Y', sym->id, sym->name); + n = sym->type->n.elem; + for (sp = sym->u.pars; n-- > 0; ++sp) + emit(ODECL, *sp); + puts("-"); } static void diff --git a/cc1/decl.c b/cc1/decl.c @@ -8,9 +8,6 @@ #include "../inc/cc.h" #include "cc1.h" -#define OUTCTX 0 -#define PARCTX 1 - struct dcldata { unsigned char op; unsigned short nelem; @@ -52,16 +49,76 @@ arydcl(struct dcldata *dp) return queue(dp, ARY, n, NULL); } -static void parlist(Type *); +static void +parameter(Symbol *sym, int sclass, Type *data) +{ + Type *tp = sym->type, *funtp = data; + size_t n = funtp->n.elem; + + if (tp == voidtype) { + if (n != 0) + error("incorrect void parameter"); + funtp->n.elem = -1; + return; + } + if (n == -1) + error("'void' must be the only parameter"); + tp = sym->type; + if (tp->op == FTN) + error("incorrect function type for a function parameter"); + if (tp->op == ARY) + tp = mktype(tp->type, PTR, 0, NULL); + if (!sclass) + sym->flags |= ISAUTO; + if (sym->flags & (ISSTATIC|ISEXTERN)) + error("bad storage class in function parameter"); + if (n++ == NR_FUNPARAM) + error("too much parameters in function definition"); + funtp->pars = xrealloc(funtp->pars, n); + funtp->pars[n-1] = tp; + funtp->n.elem = n; +} + +static Symbol *dodcl(int rep, + void (*fun)(Symbol *, int, Type *), + uint8_t ns, Type *type); + +/* FIXME: what happens with the context in int (*f)(int)[];? */ static struct dcldata * fundcl(struct dcldata *dp) { - Type dummy = {.n = {.elem = 0}, .pars = NULL}; + Type type = {.n = {.elem = -1}, .pars = NULL}; + Symbol *syms[NR_FUNPARAM], **sp; + size_t size; + + pushctx(); + expect('('); - parlist(&dummy); + if (accept(')')) + goto nopars; - return queue(dp, FTN, dummy.n.elem, dummy.pars); + type.n.elem = 0; + sp = syms; + do + *sp++ = dodcl(0, parameter, NS_IDEN, &type); + while (accept(',')); + + if (ahead() != '{') + goto nopars; + + expect(')'); + + dp = queue(dp, FTN, type.n.elem, type.pars); + if (type.n.elem != -1) { + size = type.n.elem * sizeof(Symbol *); + dp = queue(dp, PARS, 0, memcpy(xmalloc(size), syms, size)); + } + return dp; + +nopars: + expect(')'); + return queue(dp, FTN, type.n.elem, type.pars); } static struct dcldata *declarator0(struct dcldata *dp, unsigned ns); @@ -75,6 +132,8 @@ directdcl(struct dcldata *dp, unsigned ns) dp = declarator0(dp, ns); expect(')'); } else { + /* TODO: check type of the function */ + /* TODO: check function is not redefined */ if (yytoken == IDEN || yytoken == TYPEIDEN) { if ((sym = install(ns)) == NULL) error("redeclaration of '%s'", yytext); @@ -117,19 +176,27 @@ declarator(Type *tp, unsigned ns) { struct dcldata data[NR_DECLARATORS+1]; struct dcldata *bp; - Symbol *sym; + Symbol *sym, **pars = NULL; data[0].ndcl = 0; - for (bp = declarator0(data, ns); bp > data; ) { - --bp; - if (bp->op != IDEN) { - tp = mktype(tp, bp->op, bp->nelem, bp->data); - } else { + for (bp = declarator0(data, ns); bp-- > data; ) { + switch (bp->op) { + case IDEN: sym = bp->data; break; + case PARS: + pars = bp->data; + break; + default: + tp = mktype(tp, bp->op, bp->nelem, bp->data); + break; } } + sym->u.pars = pars; + if (tp->op == FTN && sym->flags & (ISREGISTER|ISAUTO)) + error("invalid storage class for function '%s'", sym->name); + /* TODO: deal with external array declarations of [] */ if (!tp->defined && sym->name) error("declared variable '%s' of incomplete type", sym->name); @@ -321,8 +388,10 @@ enumdcl(void) for (val = 0; yytoken != ')'; ++val) { if (yytoken != IDEN) unexpected(); - if ((sym = install(NS_IDEN)) == NULL) - error("'%s' redeclared as different kind of symbol", yytext); + if ((sym = install(NS_IDEN)) == NULL) { + error("'%s' redeclared as different kind of symbol", + yytext); + } next(); sym->flags |= ISCONSTANT; sym->type = inttype; @@ -382,50 +451,21 @@ field(Symbol *sym, int sclass, Type *data) } static void -parameter(Symbol *sym, int sclass, Type *data) -{ - Type *tp = sym->type, *funtp = data; - size_t n = funtp->n.elem; - - if (tp == voidtype) { - if (n != 0) - error("incorrect void parameter"); - funtp->n.elem = -1; - return; - } - - if (n == -1) - error("'void' must be the only parameter"); - tp = sym->type; - if (tp->op == FTN) - error("incorrect function type for a function parameter"); - if (tp->op == ARY) - tp = mktype(tp->type, PTR, 0, NULL); - if (!sclass) - sym->flags |= ISAUTO; - if (sym->flags & (ISSTATIC|ISEXTERN)) - error("bad storage class in function parameter"); - if (n++ == NR_FUNPARAM) - error("too much parameters in function definition"); - sym->flags |= ISPARAM; - funtp->pars = xrealloc(funtp->pars, n); - funtp->pars[n-1] = tp; - funtp->n.elem = n; -} - -static void internal(Symbol *sym, int sclass, Type *data) { - if (!sym->name) { warn("empty declaration"); return; } - if (!sclass) - sym->flags |= ISAUTO; - if (accept('=')) - initializer(sym); - /* TODO: check if the variable is extern and has initializer */ + if (sym->type->op == FTN) { + popctx(); + } else { + if (!sclass) + sym->flags |= ISAUTO; + if (accept('=')) + initializer(sym); + /* TODO: check if the variable is extern and has initializer */ + } emit(ODECL, sym); } @@ -440,37 +480,21 @@ external(Symbol *sym, int sclass, Type *data) if (sym->flags & (ISREGISTER|ISAUTO)) error("incorrect storage class for file-scope declaration"); - if (accept('=')) - initializer(sym); - /* TODO: check if the variable is extern and has initializer */ - emit(ODECL, sym); -} - -static int -prototype(Symbol *sym) -{ - int r = 1; - - /* TODO: check type of the function */ - /* TODO: check function is not redefined */ - if (sym->flags & (ISREGISTER|ISAUTO)) - error("invalid storage class for function '%s'", sym->name); - - if (curctx == PARCTX && yytoken == '{') { + if (sym->type->op == FTN && yytoken == '{') { if (sym->token == TYPEIDEN) error("function definition declared 'typedef'"); - - sym->flags |= ISDEFINED; curfun = sym; + sym->flags |= ISDEFINED; emit(OFUN, sym); compound(NULL, NULL, NULL); emit(OEFUN, NULL); - popctx(); - r = 0; + return; } - - return r; + if (accept('=')) + initializer(sym); + /* TODO: check if the variable is extern and has initializer */ + emit(ODECL, sym); } static Symbol * @@ -480,11 +504,8 @@ dodcl(int rep, void (*fun)(Symbol *, int, Type *), uint8_t ns, Type *type) Type *base, *tp; int sclass; - /* FIXME: curctx == PARCTX is incorrect. Structs also - * create new contexts - */ if ((base = specifier(&sclass)) == NULL) { - if (curctx != OUTCTX) + if (curctx != 0) unexpected(); warn("type defaults to 'int' in declaration"); base = inttype; @@ -511,11 +532,8 @@ dodcl(int rep, void (*fun)(Symbol *, int, Type *), uint8_t ns, Type *type) sym->token = TYPEIDEN; break; } - if (tp->op == FTN && !prototype(sym)) - return NULL; (*fun)(sym, sclass, type); - - } while (rep && accept(',')); + } while (rep && !curfun && accept(',')); return sym; } @@ -525,51 +543,12 @@ decl(void) { if (accept(';')) return; - if (!dodcl(1, curctx == OUTCTX ? external : internal, NS_IDEN, NULL)) - return; - expect(';'); -} - -/* - * parlist() is called every time there is a argument list. - * It means that is called for prototypes and for functions. - * In both cases a new context is needed for the arguments, - * but in the case of prototypes we need pop the context - * before parsing anything else or we can have name conflicts. - * The heuristic used here to detect a function is check if - * next token will be '{', but it implies that K&R alike - * functions are not allowed. - */ -static void -parlist(Type *tp) -{ - Symbol *pars[NR_FUNPARAM], **sp = pars; - bool isfun; - int n; - - pushctx(); - expect('('); - - if (accept(')')) { - tp->n.elem = -1; - return; - } - - do - *sp++ = dodcl(0, parameter, NS_IDEN, tp); - while (accept(',')); - - isfun = ahead() == '{'; - if (!isfun) - popctx(); - expect(')'); - - if (!isfun) + if (!dodcl(1, (curctx == 0) ? external : internal, NS_IDEN, NULL)) return; - - n = tp->n.elem; - for (sp = pars; n-- > 0; ++sp) - emit(ODECL, *sp); + if (curfun) + curfun == NULL; + else + expect(';'); } static void diff --git a/cc1/stmt.c b/cc1/stmt.c @@ -334,6 +334,12 @@ compound(Symbol *lbreak, Symbol *lcont, Caselist *lswitch) } popctx(); + /* + * curctx == 1 means we are at the end of a function + * so we have to pop the context related to the parameters + */ + if (curctx == 1) + popctx(); expect('}'); } diff --git a/cc2/main.c b/cc2/main.c @@ -42,6 +42,8 @@ repeat: int main(void) { + fputs("cc2 is not updated with the output of cc1", stderr); + exit(1); while (moreinput()) { parse(); optimize(); diff --git a/inc/cc.h b/inc/cc.h @@ -37,7 +37,7 @@ typedef unsigned bool; #define L_UINT64 'O' #define L_VOID '0' -#define L_POINTER 'R' +#define L_POINTER 'P' #define L_FUNCTION 'F' #define L_ARRAY 'V' #define L_UNION 'U'