diff options
author | Michael Forney <mforney@mforney.org> | 2019-05-15 17:47:25 -0700 |
---|---|---|
committer | Michael Forney <mforney@mforney.org> | 2019-05-15 17:58:37 -0700 |
commit | 68726abdaa443926b8a1d939a6224dd12597e6af (patch) | |
tree | 51e70a72920de751de3c7b65675677de01b7ab93 | |
parent | 46ec9b869105951521341db5d5a75aa55b156601 (diff) |
Implement asm labels
-rw-r--r-- | cc.h | 6 | ||||
-rw-r--r-- | decl.c | 30 | ||||
-rw-r--r-- | doc/extensions.md | 7 | ||||
-rw-r--r-- | qbe.c | 15 | ||||
-rw-r--r-- | test/asm-label.c | 5 | ||||
-rw-r--r-- | test/asm-label.qbe | 7 |
6 files changed, 59 insertions, 11 deletions
@@ -474,12 +474,14 @@ struct switchcases *mkswitch(void); void switchcase(struct switchcases *, uint64_t, struct value *); struct value *mkblock(char *); + struct value *mkglobal(char *, _Bool); -struct value *mkintconst(struct repr *, uint64_t); +char *globalname(struct value *); +struct value *mkintconst(struct repr *, uint64_t); uint64_t intconstvalue(struct value *); -struct func *mkfunc(char *, struct type *, struct scope *); +struct func *mkfunc(struct decl *, char *, struct type *, struct scope *); void delfunc(struct func *); struct type *functype(struct func *); void funclabel(struct func *, struct value *); @@ -786,11 +786,12 @@ getlinkage(enum declkind kind, enum storageclass sc, struct decl *prior, bool fi } static struct decl * -declcommon(struct scope *s, enum declkind kind, char *name, struct type *t, enum typequal tq, enum storageclass sc, struct decl *prior) +declcommon(struct scope *s, enum declkind kind, char *name, char *asmname, struct type *t, enum typequal tq, enum storageclass sc, struct decl *prior) { struct decl *d; enum linkage linkage; const char *kindstr = kind == DECLFUNC ? "function" : "object"; + char *priorname; if (prior) { if (prior->linkage == LINKNONE) @@ -800,6 +801,8 @@ declcommon(struct scope *s, enum declkind kind, char *name, struct type *t, enum error(&tok.loc, "%s '%s' redeclared with different linkage", kindstr, name); if (!typecompatible(t, prior->type) || tq != prior->qual) error(&tok.loc, "%s '%s' redeclared with incompatible type", kindstr, name); + if (asmname && strcmp(globalname(prior->value), asmname) != 0) + error(&tok.loc, "%s '%s' redeclared with different assembler name", kindstr, name); prior->type = typecomposite(t, prior->type); return prior; } @@ -817,13 +820,18 @@ declcommon(struct scope *s, enum declkind kind, char *name, struct type *t, enum error(&tok.loc, "%s '%s' redeclared with different linkage", kindstr, name); if (!typecompatible(t, prior->type) || tq != prior->qual) error(&tok.loc, "%s '%s' redeclared with incompatible type", kindstr, name); + priorname = globalname(prior->value); + if (!asmname) + asmname = priorname; + else if (strcmp(priorname, asmname) != 0) + error(&tok.loc, "%s '%s' redeclared with different assembler name", kindstr, name); t = typecomposite(t, prior->type); } } d = mkdecl(kind, t, tq, linkage); scopeputdecl(s, name, d); if (kind == DECLFUNC || linkage != LINKNONE || sc & SCSTATIC) - d->value = mkglobal(name, linkage == LINKNONE); + d->value = mkglobal(asmname ? asmname : name, linkage == LINKNONE && !asmname); return d; } @@ -837,7 +845,7 @@ decl(struct scope *s, struct func *f) enum funcspec fs; struct init *init; struct param *p; - char *name; + char *name, *asmname; int allowfunc = !f; struct decl *d, *prior; enum declkind kind; @@ -873,6 +881,14 @@ decl(struct scope *s, struct func *f) qt = declarator(s, base, &name, false); t = qt.type; tq = qt.qual; + if (consume(T__ASM__)) { + expect(TLPAREN, "after __asm__"); + asmname = expect(TSTRINGLIT, "for assembler name"); + expect(TRPAREN, "after assembler name"); + allowfunc = 0; + } else { + asmname = NULL; + } kind = sc & SCTYPEDEF ? DECLTYPE : t->kind == TYPEFUNC ? DECLFUNC : DECLOBJECT; prior = scopegetdecl(s, name, false); if (prior && prior->kind != kind) @@ -881,13 +897,15 @@ decl(struct scope *s, struct func *f) case DECLTYPE: if (align) error(&tok.loc, "typedef '%s' declared with alignment specifier", name); + if (asmname) + error(&tok.loc, "typedef '%s' declared with assembler label", name); if (!prior) scopeputdecl(s, name, mkdecl(DECLTYPE, t, tq, LINKNONE)); else if (!typesame(prior->type, t) || prior->qual != tq) error(&tok.loc, "typedef '%s' redefined with different type", name); break; case DECLOBJECT: - d = declcommon(s, kind, name, t, tq, sc, prior); + d = declcommon(s, kind, name, asmname, t, tq, sc, prior); if (d->align < align) d->align = align; if (consume(TASSIGN)) { @@ -935,14 +953,14 @@ decl(struct scope *s, struct func *f) error(&tok.loc, "old-style function definition does not declare '%s'", p->name); } } - d = declcommon(s, kind, name, t, tq, sc, prior); + d = declcommon(s, kind, name, asmname, t, tq, sc, prior); if (tok.kind == TLBRACE) { if (!allowfunc) error(&tok.loc, "function definition not allowed"); if (d->defined) error(&tok.loc, "function '%s' redefined", name); s = mkscope(&filescope); - f = mkfunc(name, t, s); + f = mkfunc(d, name, t, s); stmt(f, s); /* XXX: need to keep track of function in case a later declaration specifies extern */ if (!(fs & FUNCINLINE) || sc) diff --git a/doc/extensions.md b/doc/extensions.md index e3f9085..9a7b81d 100644 --- a/doc/extensions.md +++ b/doc/extensions.md @@ -11,6 +11,13 @@ may be implemented in the future. and function designator expressions do not decay into pointers, just like when used with `sizeof`. +### `__asm__` labels + +A declarator can be followed by `__asm__("somelabel")` to specify the +assembler name of the object or function. This name is taken literally, so +the resulting symbol will not be mangled according to the target's usual +rules. The name may contain characters not allowed in regular identifiers. + ### Built-in functions and types - **`__builtin_alloca`**: Allocate memory on the stack. @@ -68,8 +68,8 @@ struct switchcase { }; struct func { + struct decl *decl, *namedecl; char *name; - struct decl *namedecl; struct type *type; struct block *start, *end; struct map *gotos; @@ -366,6 +366,13 @@ mkglobal(char *name, bool private) return v; } +char * +globalname(struct value *v) +{ + assert(v->kind == VALGLOBAL && !v->name.id); + return v->name.str; +} + /* XXX: If a function declared without a prototype is declared with a parameter affected by default argument promotion, we need to emit a QBE @@ -373,13 +380,14 @@ function with the promoted type and implicitly convert to the declared parameter type before storing into the allocated memory for the parameter. */ struct func * -mkfunc(char *name, struct type *t, struct scope *s) +mkfunc(struct decl *decl, char *name, struct type *t, struct scope *s) { struct func *f; struct param *p; struct decl *d; f = xmalloc(sizeof(*f)); + f->decl = decl; f->name = name; f->type = t; f->start = f->end = (struct block *)mkblock("start"); @@ -1187,7 +1195,8 @@ emitfunc(struct func *f, bool global) emitrepr(f->type->base->repr, true, false); putchar(' '); } - printf("$%s(", f->name); + emitvalue(f->decl->value); + putchar('('); for (p = f->type->func.params; p; p = p->next) { if (p != f->type->func.params) fputs(", ", stdout); diff --git a/test/asm-label.c b/test/asm-label.c new file mode 100644 index 0000000..b9489da --- /dev/null +++ b/test/asm-label.c @@ -0,0 +1,5 @@ +int x __asm__("y"); +int x = 2; + +void f(void) __asm__("g"); +void f(void) {} diff --git a/test/asm-label.qbe b/test/asm-label.qbe new file mode 100644 index 0000000..b7cda41 --- /dev/null +++ b/test/asm-label.qbe @@ -0,0 +1,7 @@ +export data $"y" = align 4 { w 2, } +export +function $"g"() { +@start.1 +@body.2 + ret +} |