~mcf/cproc

68726abdaa443926b8a1d939a6224dd12597e6af — Michael Forney 1 year, 6 months ago 46ec9b8
Implement asm labels
6 files changed, 59 insertions(+), 11 deletions(-)

M cc.h
M decl.c
M doc/extensions.md
M qbe.c
A test/asm-label.c
A test/asm-label.qbe
M cc.h => cc.h +4 -2
@@ 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 *);

M decl.c => decl.c +24 -6
@@ 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)

M doc/extensions.md => doc/extensions.md +7 -0
@@ 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.

M qbe.c => qbe.c +12 -3
@@ 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);

A test/asm-label.c => test/asm-label.c +5 -0
@@ 0,0 1,5 @@
int x __asm__("y");
int x = 2;

void f(void) __asm__("g");
void f(void) {}

A test/asm-label.qbe => test/asm-label.qbe +7 -0
@@ 0,0 1,7 @@
export data $"y" = align 4 { w 2, }
export
function $"g"() {
@start.1
@body.2
	ret
}