summary refs log tree commit diff
diff options
context:
space:
mode:
authorQuentin Carbonneaux <quentin@c9x.me>2022-01-31 22:03:18 +0100
committerQuentin Carbonneaux <quentin@c9x.me>2022-02-02 21:09:37 +0100
commit2ca6fb25a238842418019a3f9ee8d1beb1327f7e (patch)
treebe083fc02dc8c1175f454b0adfd23d662f96476f
parent20ee522ce8c4d1ffdd7b6e24a4f7af587c35404a (diff)
downloadroux-2ca6fb25a238842418019a3f9ee8d1beb1327f7e.tar.gz
shared linkage logic for func/data
-rw-r--r--all.h20
-rw-r--r--amd64/emit.c13
-rw-r--r--arm64/emit.c6
-rw-r--r--doc/il.txt65
-rw-r--r--gas.c48
-rw-r--r--parse.c83
6 files changed, 143 insertions, 92 deletions
diff --git a/all.h b/all.h
index 98b77dd..942c52d 100644
--- a/all.h
+++ b/all.h
@@ -28,6 +28,7 @@ typedef struct Fn Fn;
 typedef struct Typ Typ;
 typedef struct Field Field;
 typedef struct Dat Dat;
+typedef struct Lnk Lnk;
 typedef struct Target Target;
 
 enum {
@@ -327,6 +328,13 @@ struct Addr { /* amd64 addressing */
 	int scale;
 };
 
+struct Lnk {
+	char export;
+	char align;
+	char *sec;
+	char *secf;
+};
+
 struct Fn {
 	Blk *start;
 	Tmp *tmp;
@@ -341,10 +349,10 @@ struct Fn {
 	Blk **rpo;
 	bits reg;
 	int slot;
-	char export;
 	char vararg;
 	char dynalloc;
 	char name[NString];
+	Lnk lnk;
 };
 
 struct Typ {
@@ -373,8 +381,6 @@ struct Dat {
 	enum {
 		DStart,
 		DEnd,
-		DName,
-		DAlign,
 		DB,
 		DH,
 		DW,
@@ -387,13 +393,16 @@ struct Dat {
 		float flts;
 		char *str;
 		struct {
-			char *nam;
+			char *name;
 			int64_t off;
 		} ref;
+		struct {
+			char *name;
+			Lnk *lnk;
+		} start;
 	} u;
 	char isref;
 	char isstr;
-	char export;
 };
 
 /* main.c */
@@ -516,6 +525,7 @@ void rega(Fn *);
 /* gas.c */
 extern char *gasloc;
 extern char *gassym;
+void gasemitlnk(char *, Lnk *, char *, FILE *);
 void gasemitdat(Dat *, FILE *);
 int gasstash(void *, int);
 void gasemitfin(FILE *);
diff --git a/amd64/emit.c b/amd64/emit.c
index 4cb340d..b8e9e8e 100644
--- a/amd64/emit.c
+++ b/amd64/emit.c
@@ -548,18 +548,9 @@ amd64_emitfn(Fn *fn, FILE *f)
 	Ins *i, itmp;
 	int *r, c, o, n, lbl;
 	uint64_t fs;
-	char *p;
 
-	p = fn->name[0] == '"' ? "" : gassym;
-	fprintf(f, ".text\n");
-	if (fn->export)
-		fprintf(f, ".globl %s%s\n", p, fn->name);
-	fprintf(f,
-		"%s%s:\n"
-		"\tpushq %%rbp\n"
-		"\tmovq %%rsp, %%rbp\n",
-		p, fn->name
-	);
+	gasemitlnk(fn->name, &fn->lnk, ".text", f);
+	fputs("\tpushq %rbp\n\tmovq %rsp, %rbp\n", f);
 	fs = framesz(fn);
 	if (fs)
 		fprintf(f, "\tsubq $%"PRIu64", %%rsp\n", fs);
diff --git a/arm64/emit.c b/arm64/emit.c
index 9700abe..b25f4f5 100644
--- a/arm64/emit.c
+++ b/arm64/emit.c
@@ -446,14 +446,10 @@ arm64_emitfn(Fn *fn, FILE *out)
 	Ins *i;
 	E *e;
 
+	gasemitlnk(fn->name, &fn->lnk, ".text", out);
 	e = &(E){.f = out, .fn = fn};
 	framelayout(e);
 
-	fprintf(e->f, ".text\n");
-	if (e->fn->export)
-		fprintf(e->f, ".globl %s\n", e->fn->name);
-	fprintf(e->f, "%s:\n", e->fn->name);
-
 	if (e->fn->vararg) {
 		for (n=7; n>=0; n--)
 			fprintf(e->f, "\tstr\tq%d, [sp, -16]!\n", n);
diff --git a/doc/il.txt b/doc/il.txt
index 2236340..b667b64 100644
--- a/doc/il.txt
+++ b/doc/il.txt
@@ -15,14 +15,15 @@
       * <@ Simple Types >
       * <@ Subtyping >
   3. <@ Constants >
-  4. <@ Definitions >
+  4. <@ Linkage >
+  5. <@ Definitions >
       * <@ Aggregate Types >
       * <@ Data >
       * <@ Functions >
-  5. <@ Control >
+  6. <@ Control >
       * <@ Blocks >
       * <@ Jumps >
-  6. <@ Instructions >
+  7. <@ Instructions >
       * <@ Arithmetic and Bits >
       * <@ Memory >
       * <@ Comparisons >
@@ -31,7 +32,7 @@
       * <@ Call >
       * <@ Variadic >
       * <@ Phi >
-  7. <@ Instructions Index >
+  8. <@ Instructions Index >
 
 - 1. Basic Concepts
 -------------------
@@ -196,7 +197,46 @@ Global symbols can also be used directly as constants;
 they will be resolved and turned into actual numeric
 constants by the linker.
 
-- 4. Definitions
+- 4. Linkage
+------------
+
+    `bnf
+    LINKAGE :=
+        'extern'
+      | 'section' SECNAME
+      | 'section' SECNAME SECFLAGS
+
+    SECNAME  := '"' .... '"'
+    SECFLAGS := '"' .... '"'
+
+Function and data definitions (see below) can specify
+linkage information to be passed to the assembler and
+eventually to the linker.
+
+The `extern` linkage flag marks the defined item as
+visible outside the current file's scope.  If absent,
+the symbol can only be referred to locally.  Functions
+compiled by QBE and called from C need to have extern
+linkage.
+
+A `section` flag can be specified to tell the linker to
+put the defined item in a certain section.  The use of
+the section flag is platform dependent and we refer the
+user to the documentation of their assembler and linker
+for relevant information.
+
+    export section ".bss"
+    data $zerobuf = { z 1024 }
+
+Example uses of the section flag include adding function
+pointers to a global initialization list, or storing a
+zeroed object in the BSS section, as depicted above.
+
+The section and extern linkage flags should each appear
+at most once in a definition.  If multiple occurrences
+are present, QBE is free to use any.
+
+- 5. Definitions
 ----------------
 
 Definitions are the essential components of an IL file.
@@ -254,7 +294,7 @@ their size between curly braces.
 
     `bnf
     DATADEF :=
-        ['export'] 'data' $IDENT '=' ['align' NUMBER]
+        LINKAGE* 'data' $IDENT '=' ['align' NUMBER]
         '{'
             ( EXTTY DATAITEM+
             | 'z'   NUMBER ),
@@ -266,8 +306,9 @@ their size between curly braces.
       |  CONST               # Constant
 
 Data definitions express objects that will be emitted in the
-compiled file.  They can be local to the file or exported
-with global visibility to the whole program.
+compiled file.  Their visibility and location in the compiled
+artifact are controlled with linkage flags described in the
+<@ Linkage > section.
 
 They define a global identifier (starting with the sigil
 `$`), that will contain a pointer to the object specified
@@ -311,7 +352,7 @@ Here are various examples of data definitions.
 
     `bnf
     FUNCDEF :=
-        ['export'] 'function' [ABITY] $IDENT '(' (PARAM), ')'
+        LINKAGE* 'function' [ABITY] $IDENT '(' (PARAM), ')'
         '{'
            BLOCK+
         '}'
@@ -384,7 +425,7 @@ is provided in the call instructions.
 The syntax and semantics for the body of functions
 are described in the <@ Control > section.
 
-- 5. Control
+- 6. Control
 ------------
 
 The IL represents programs as textual transcriptions of
@@ -465,7 +506,7 @@ the following list.
     prototype.  If the function prototype does not specify
     a return type, no return value can be used.
 
-- 6. Instructions
+- 7. Instructions
 -----------------
 
 Instructions are the smallest piece of code in the IL, they
@@ -903,7 +944,7 @@ assumes that if a variable is defined by a phi it respects
 all the SSA invariants.  So it is critical to not use phi
 instructions unless you know exactly what you are doing.
 
-- 7. Instructions Index
+- 8. Instructions Index
 -----------------------
 
   * <@ Arithmetic and Bits >:
diff --git a/gas.c b/gas.c
index 8c31794..4400769 100644
--- a/gas.c
+++ b/gas.c
@@ -4,11 +4,30 @@
 char *gasloc, *gassym;
 
 void
+gasemitlnk(char *n, Lnk *l, char *s, FILE *f)
+{
+	char *p;
+
+	if (l->sec) {
+		fprintf(f, ".section %s", l->sec);
+		if (l->secf)
+			fprintf(f, ", %s", l->secf);
+	} else {
+		fputs(s, f);
+	}
+	fputc('\n', f);
+	if (l->align)
+		fprintf(f, ".balign %d\n", l->align);
+	p = n[0] == '"' ? "" : gassym;
+	if (l->export)
+		fprintf(f, ".globl %s%s\n", p, n);
+	fprintf(f, "%s%s:\n", p, n);
+}
+
+void
 gasemitdat(Dat *d, FILE *f)
 {
-	static int aligned;
 	static char *dtoa[] = {
-		[DAlign] = ".balign",
 		[DB] = "\t.byte",
 		[DH] = "\t.short",
 		[DW] = "\t.int",
@@ -18,39 +37,26 @@ gasemitdat(Dat *d, FILE *f)
 
 	switch (d->type) {
 	case DStart:
-		aligned = 0;
-		if (d->u.str) {
-			fprintf(f, ".section %s\n", d->u.str);
-		} else {
-			fprintf(f, ".data\n");
-		}
+		gasemitlnk(
+			d->u.start.name,
+			d->u.start.lnk,
+			".data", f);
 		break;
 	case DEnd:
 		break;
-	case DName:
-		if (!aligned)
-			fprintf(f, ".balign 8\n");
-		p = d->u.str[0] == '"' ? "" : gassym;
-		if (d->export)
-			fprintf(f, ".globl %s%s\n", p, d->u.str);
-		fprintf(f, "%s%s:\n", p, d->u.str);
-		break;
 	case DZ:
 		fprintf(f, "\t.fill %"PRId64",1,0\n", d->u.num);
 		break;
 	default:
-		if (d->type == DAlign)
-			aligned = 1;
-
 		if (d->isstr) {
 			if (d->type != DB)
 				err("strings only supported for 'b' currently");
 			fprintf(f, "\t.ascii %s\n", d->u.str);
 		}
 		else if (d->isref) {
-			p = d->u.ref.nam[0] == '"' ? "" : gassym;
+			p = d->u.ref.name[0] == '"' ? "" : gassym;
 			fprintf(f, "%s %s%s%+"PRId64"\n",
-				dtoa[d->type], p, d->u.ref.nam,
+				dtoa[d->type], p, d->u.ref.name,
 				d->u.ref.off);
 		}
 		else {
diff --git a/parse.c b/parse.c
index e97b196..5e5ab66 100644
--- a/parse.c
+++ b/parse.c
@@ -792,7 +792,7 @@ typecheck(Fn *fn)
 }
 
 static Fn *
-parsefn(int export)
+parsefn(Lnk *lnk)
 {
 	Blk *b;
 	int i;
@@ -812,7 +812,7 @@ parsefn(int export)
 		else
 			newtmp(0, Kl, curf);
 	curf->con[0].type = CBits;
-	curf->export = export;
+	curf->lnk = *lnk;
 	blink = &curf->start;
 	curf->retty = Kx;
 	if (peek() != Tglo)
@@ -973,7 +973,7 @@ parsedatref(Dat *d)
 	int t;
 
 	d->isref = 1;
-	d->u.ref.nam = tokval.str;
+	d->u.ref.name = tokval.str;
 	d->u.ref.off = 0;
 	t = peek();
 	if (t == Tplus) {
@@ -992,7 +992,7 @@ parsedatstr(Dat *d)
 }
 
 static void
-parsedat(void cb(Dat *), int export)
+parsedat(void cb(Dat *), Lnk *lnk)
 {
 	char name[NString] = {0};
 	int t;
@@ -1002,28 +1002,16 @@ parsedat(void cb(Dat *), int export)
 		err("data name, then = expected");
 	strncpy(name, tokval.str, NString-1);
 	t = nextnl();
-	d.u.str = 0;
-	if (t == Tsection) {
-		if (nextnl() != Tstr)
-			err("section \"name\" expected");
-		d.u.str = tokval.str;
-		t = nextnl();
-	}
-	d.type = DStart;
-	cb(&d);
+	lnk->align = 8;
 	if (t == Talign) {
 		if (nextnl() != Tint)
 			err("alignment expected");
-		d.type = DAlign;
-		d.u.num = tokval.num;
-		d.isstr = 0;
-		d.isref = 0;
-		cb(&d);
+		lnk->align = tokval.num;
 		t = nextnl();
 	}
-	d.type = DName;
-	d.u.str = name;
-	d.export = export;
+	d.type = DStart;
+	d.u.start.name = name;
+	d.u.start.lnk = lnk;
 	cb(&d);
 
 	if (t != Tlbrace)
@@ -1070,10 +1058,38 @@ Done:
 	cb(&d);
 }
 
+static int
+parselnk(Lnk *lnk)
+{
+	int t, haslnk;
+
+	for (haslnk=0;; haslnk=1)
+		switch ((t=nextnl())) {
+		case Texport:
+			lnk->export = 1;
+			break;
+		case Tsection:
+			if (next() != Tstr)
+				err("section \"name\" expected");
+			lnk->sec = tokval.str;
+			if (peek() == Tstr) {
+				next();
+				lnk->secf = tokval.str;
+			}
+			break;
+		default:
+			if (haslnk)
+			if (t != Tdata)
+			if (t != Tfunc)
+				err("only data and function have linkage");
+			return t;
+		}
+}
+
 void
 parse(FILE *f, char *path, void data(Dat *), void func(Fn *))
 {
-	int t, export;
+	Lnk lnk;
 
 	lexinit();
 	inf = f;
@@ -1083,25 +1099,16 @@ parse(FILE *f, char *path, void data(Dat *), void func(Fn *))
 	ntyp = 0;
 	typ = vnew(0, sizeof typ[0], Pheap);
 	for (;;) {
-		export = 0;
-		switch (nextnl()) {
+		lnk = (Lnk){0};
+		switch (parselnk(&lnk)) {
 		default:
 			err("top-level definition expected");
-		case Texport:
-			export = 1;
-			t = nextnl();
-			if (t == Tfunc) {
 		case Tfunc:
-				func(parsefn(export));
-				break;
-			}
-			else if (t == Tdata) {
+			func(parsefn(&lnk));
+			break;
 		case Tdata:
-				parsedat(data, export);
-				break;
-			}
-			else
-				err("export can only qualify data and function");
+			parsedat(data, &lnk);
+			break;
 		case Ttype:
 			parsetyp();
 			break;
@@ -1198,7 +1205,7 @@ printfn(Fn *fn, FILE *f)
 	Ins *i;
 	uint n;
 
-	if (fn->export)
+	if (fn->lnk.export)
 		fprintf(f, "export ");
 	fprintf(f, "function $%s() {\n", fn->name);
 	for (b=fn->start; b; b=b->link) {