aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cc.h6
-rw-r--r--decl.c30
-rw-r--r--doc/extensions.md7
-rw-r--r--qbe.c15
-rw-r--r--test/asm-label.c5
-rw-r--r--test/asm-label.qbe7
6 files changed, 59 insertions, 11 deletions
diff --git a/cc.h b/cc.h
index 7d85355..ce88f92 100644
--- a/cc.h
+++ b/cc.h
@@ -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 *);
diff --git a/decl.c b/decl.c
index e4187e4..c231e38 100644
--- a/decl.c
+++ b/decl.c
@@ -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.
diff --git a/qbe.c b/qbe.c
index 5135eca..6c1474c 100644
--- a/qbe.c
+++ b/qbe.c
@@ -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
+}