Browse Source

shared linkage logic for func/data

Quentin Carbonneaux 3 years ago
parent
commit
2ca6fb25a2
6 changed files with 143 additions and 92 deletions
  1. 15 5
      all.h
  2. 2 11
      amd64/emit.c
  3. 1 5
      arm64/emit.c
  4. 53 12
      doc/il.txt
  5. 27 21
      gas.c
  6. 45 38
      parse.c

+ 15 - 5
all.h

@@ -28,6 +28,7 @@ typedef struct Fn Fn;
 typedef struct Typ Typ;
 typedef struct Typ Typ;
 typedef struct Field Field;
 typedef struct Field Field;
 typedef struct Dat Dat;
 typedef struct Dat Dat;
+typedef struct Lnk Lnk;
 typedef struct Target Target;
 typedef struct Target Target;
 
 
 enum {
 enum {
@@ -327,6 +328,13 @@ struct Addr { /* amd64 addressing */
 	int scale;
 	int scale;
 };
 };
 
 
+struct Lnk {
+	char export;
+	char align;
+	char *sec;
+	char *secf;
+};
+
 struct Fn {
 struct Fn {
 	Blk *start;
 	Blk *start;
 	Tmp *tmp;
 	Tmp *tmp;
@@ -341,10 +349,10 @@ struct Fn {
 	Blk **rpo;
 	Blk **rpo;
 	bits reg;
 	bits reg;
 	int slot;
 	int slot;
-	char export;
 	char vararg;
 	char vararg;
 	char dynalloc;
 	char dynalloc;
 	char name[NString];
 	char name[NString];
+	Lnk lnk;
 };
 };
 
 
 struct Typ {
 struct Typ {
@@ -373,8 +381,6 @@ struct Dat {
 	enum {
 	enum {
 		DStart,
 		DStart,
 		DEnd,
 		DEnd,
-		DName,
-		DAlign,
 		DB,
 		DB,
 		DH,
 		DH,
 		DW,
 		DW,
@@ -387,13 +393,16 @@ struct Dat {
 		float flts;
 		float flts;
 		char *str;
 		char *str;
 		struct {
 		struct {
-			char *nam;
+			char *name;
 			int64_t off;
 			int64_t off;
 		} ref;
 		} ref;
+		struct {
+			char *name;
+			Lnk *lnk;
+		} start;
 	} u;
 	} u;
 	char isref;
 	char isref;
 	char isstr;
 	char isstr;
-	char export;
 };
 };
 
 
 /* main.c */
 /* main.c */
@@ -516,6 +525,7 @@ void rega(Fn *);
 /* gas.c */
 /* gas.c */
 extern char *gasloc;
 extern char *gasloc;
 extern char *gassym;
 extern char *gassym;
+void gasemitlnk(char *, Lnk *, char *, FILE *);
 void gasemitdat(Dat *, FILE *);
 void gasemitdat(Dat *, FILE *);
 int gasstash(void *, int);
 int gasstash(void *, int);
 void gasemitfin(FILE *);
 void gasemitfin(FILE *);

+ 2 - 11
amd64/emit.c

@@ -548,18 +548,9 @@ amd64_emitfn(Fn *fn, FILE *f)
 	Ins *i, itmp;
 	Ins *i, itmp;
 	int *r, c, o, n, lbl;
 	int *r, c, o, n, lbl;
 	uint64_t fs;
 	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);
 	fs = framesz(fn);
 	if (fs)
 	if (fs)
 		fprintf(f, "\tsubq $%"PRIu64", %%rsp\n", fs);
 		fprintf(f, "\tsubq $%"PRIu64", %%rsp\n", fs);

+ 1 - 5
arm64/emit.c

@@ -446,14 +446,10 @@ arm64_emitfn(Fn *fn, FILE *out)
 	Ins *i;
 	Ins *i;
 	E *e;
 	E *e;
 
 
+	gasemitlnk(fn->name, &fn->lnk, ".text", out);
 	e = &(E){.f = out, .fn = fn};
 	e = &(E){.f = out, .fn = fn};
 	framelayout(e);
 	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) {
 	if (e->fn->vararg) {
 		for (n=7; n>=0; n--)
 		for (n=7; n>=0; n--)
 			fprintf(e->f, "\tstr\tq%d, [sp, -16]!\n", n);
 			fprintf(e->f, "\tstr\tq%d, [sp, -16]!\n", n);

+ 53 - 12
doc/il.txt

@@ -15,14 +15,15 @@
       * <@ Simple Types >
       * <@ Simple Types >
       * <@ Subtyping >
       * <@ Subtyping >
   3. <@ Constants >
   3. <@ Constants >
-  4. <@ Definitions >
+  4. <@ Linkage >
+  5. <@ Definitions >
       * <@ Aggregate Types >
       * <@ Aggregate Types >
       * <@ Data >
       * <@ Data >
       * <@ Functions >
       * <@ Functions >
-  5. <@ Control >
+  6. <@ Control >
       * <@ Blocks >
       * <@ Blocks >
       * <@ Jumps >
       * <@ Jumps >
-  6. <@ Instructions >
+  7. <@ Instructions >
       * <@ Arithmetic and Bits >
       * <@ Arithmetic and Bits >
       * <@ Memory >
       * <@ Memory >
       * <@ Comparisons >
       * <@ Comparisons >
@@ -31,7 +32,7 @@
       * <@ Call >
       * <@ Call >
       * <@ Variadic >
       * <@ Variadic >
       * <@ Phi >
       * <@ Phi >
-  7. <@ Instructions Index >
+  8. <@ Instructions Index >
 
 
 - 1. Basic Concepts
 - 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
 they will be resolved and turned into actual numeric
 constants by the linker.
 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.
 Definitions are the essential components of an IL file.
@@ -254,7 +294,7 @@ their size between curly braces.
 
 
     `bnf
     `bnf
     DATADEF :=
     DATADEF :=
-        ['export'] 'data' $IDENT '=' ['align' NUMBER]
+        LINKAGE* 'data' $IDENT '=' ['align' NUMBER]
         '{'
         '{'
             ( EXTTY DATAITEM+
             ( EXTTY DATAITEM+
             | 'z'   NUMBER ),
             | 'z'   NUMBER ),
@@ -266,8 +306,9 @@ their size between curly braces.
       |  CONST               # Constant
       |  CONST               # Constant
 
 
 Data definitions express objects that will be emitted in the
 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
 They define a global identifier (starting with the sigil
 `$`), that will contain a pointer to the object specified
 `$`), that will contain a pointer to the object specified
@@ -311,7 +352,7 @@ Here are various examples of data definitions.
 
 
     `bnf
     `bnf
     FUNCDEF :=
     FUNCDEF :=
-        ['export'] 'function' [ABITY] $IDENT '(' (PARAM), ')'
+        LINKAGE* 'function' [ABITY] $IDENT '(' (PARAM), ')'
         '{'
         '{'
            BLOCK+
            BLOCK+
         '}'
         '}'
@@ -384,7 +425,7 @@ is provided in the call instructions.
 The syntax and semantics for the body of functions
 The syntax and semantics for the body of functions
 are described in the <@ Control > section.
 are described in the <@ Control > section.
 
 
-- 5. Control
+- 6. Control
 ------------
 ------------
 
 
 The IL represents programs as textual transcriptions of
 The IL represents programs as textual transcriptions of
@@ -465,7 +506,7 @@ the following list.
     prototype.  If the function prototype does not specify
     prototype.  If the function prototype does not specify
     a return type, no return value can be used.
     a return type, no return value can be used.
 
 
-- 6. Instructions
+- 7. Instructions
 -----------------
 -----------------
 
 
 Instructions are the smallest piece of code in the IL, they
 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
 all the SSA invariants.  So it is critical to not use phi
 instructions unless you know exactly what you are doing.
 instructions unless you know exactly what you are doing.
 
 
-- 7. Instructions Index
+- 8. Instructions Index
 -----------------------
 -----------------------
 
 
   * <@ Arithmetic and Bits >:
   * <@ Arithmetic and Bits >:

+ 27 - 21
gas.c

@@ -3,12 +3,31 @@
 
 
 char *gasloc, *gassym;
 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
 void
 gasemitdat(Dat *d, FILE *f)
 gasemitdat(Dat *d, FILE *f)
 {
 {
-	static int aligned;
 	static char *dtoa[] = {
 	static char *dtoa[] = {
-		[DAlign] = ".balign",
 		[DB] = "\t.byte",
 		[DB] = "\t.byte",
 		[DH] = "\t.short",
 		[DH] = "\t.short",
 		[DW] = "\t.int",
 		[DW] = "\t.int",
@@ -18,39 +37,26 @@ gasemitdat(Dat *d, FILE *f)
 
 
 	switch (d->type) {
 	switch (d->type) {
 	case DStart:
 	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;
 		break;
 	case DEnd:
 	case DEnd:
 		break;
 		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:
 	case DZ:
 		fprintf(f, "\t.fill %"PRId64",1,0\n", d->u.num);
 		fprintf(f, "\t.fill %"PRId64",1,0\n", d->u.num);
 		break;
 		break;
 	default:
 	default:
-		if (d->type == DAlign)
-			aligned = 1;
-
 		if (d->isstr) {
 		if (d->isstr) {
 			if (d->type != DB)
 			if (d->type != DB)
 				err("strings only supported for 'b' currently");
 				err("strings only supported for 'b' currently");
 			fprintf(f, "\t.ascii %s\n", d->u.str);
 			fprintf(f, "\t.ascii %s\n", d->u.str);
 		}
 		}
 		else if (d->isref) {
 		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",
 			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);
 				d->u.ref.off);
 		}
 		}
 		else {
 		else {

+ 45 - 38
parse.c

@@ -792,7 +792,7 @@ typecheck(Fn *fn)
 }
 }
 
 
 static Fn *
 static Fn *
-parsefn(int export)
+parsefn(Lnk *lnk)
 {
 {
 	Blk *b;
 	Blk *b;
 	int i;
 	int i;
@@ -812,7 +812,7 @@ parsefn(int export)
 		else
 		else
 			newtmp(0, Kl, curf);
 			newtmp(0, Kl, curf);
 	curf->con[0].type = CBits;
 	curf->con[0].type = CBits;
-	curf->export = export;
+	curf->lnk = *lnk;
 	blink = &curf->start;
 	blink = &curf->start;
 	curf->retty = Kx;
 	curf->retty = Kx;
 	if (peek() != Tglo)
 	if (peek() != Tglo)
@@ -973,7 +973,7 @@ parsedatref(Dat *d)
 	int t;
 	int t;
 
 
 	d->isref = 1;
 	d->isref = 1;
-	d->u.ref.nam = tokval.str;
+	d->u.ref.name = tokval.str;
 	d->u.ref.off = 0;
 	d->u.ref.off = 0;
 	t = peek();
 	t = peek();
 	if (t == Tplus) {
 	if (t == Tplus) {
@@ -992,7 +992,7 @@ parsedatstr(Dat *d)
 }
 }
 
 
 static void
 static void
-parsedat(void cb(Dat *), int export)
+parsedat(void cb(Dat *), Lnk *lnk)
 {
 {
 	char name[NString] = {0};
 	char name[NString] = {0};
 	int t;
 	int t;
@@ -1002,28 +1002,16 @@ parsedat(void cb(Dat *), int export)
 		err("data name, then = expected");
 		err("data name, then = expected");
 	strncpy(name, tokval.str, NString-1);
 	strncpy(name, tokval.str, NString-1);
 	t = nextnl();
 	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 (t == Talign) {
 		if (nextnl() != Tint)
 		if (nextnl() != Tint)
 			err("alignment expected");
 			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();
 		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);
 	cb(&d);
 
 
 	if (t != Tlbrace)
 	if (t != Tlbrace)
@@ -1070,10 +1058,38 @@ Done:
 	cb(&d);
 	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
 void
 parse(FILE *f, char *path, void data(Dat *), void func(Fn *))
 parse(FILE *f, char *path, void data(Dat *), void func(Fn *))
 {
 {
-	int t, export;
+	Lnk lnk;
 
 
 	lexinit();
 	lexinit();
 	inf = f;
 	inf = f;
@@ -1083,25 +1099,16 @@ parse(FILE *f, char *path, void data(Dat *), void func(Fn *))
 	ntyp = 0;
 	ntyp = 0;
 	typ = vnew(0, sizeof typ[0], Pheap);
 	typ = vnew(0, sizeof typ[0], Pheap);
 	for (;;) {
 	for (;;) {
-		export = 0;
-		switch (nextnl()) {
+		lnk = (Lnk){0};
+		switch (parselnk(&lnk)) {
 		default:
 		default:
 			err("top-level definition expected");
 			err("top-level definition expected");
-		case Texport:
-			export = 1;
-			t = nextnl();
-			if (t == Tfunc) {
 		case Tfunc:
 		case Tfunc:
-				func(parsefn(export));
-				break;
-			}
-			else if (t == Tdata) {
+			func(parsefn(&lnk));
+			break;
 		case Tdata:
 		case Tdata:
-				parsedat(data, export);
-				break;
-			}
-			else
-				err("export can only qualify data and function");
+			parsedat(data, &lnk);
+			break;
 		case Ttype:
 		case Ttype:
 			parsetyp();
 			parsetyp();
 			break;
 			break;
@@ -1198,7 +1205,7 @@ printfn(Fn *fn, FILE *f)
 	Ins *i;
 	Ins *i;
 	uint n;
 	uint n;
 
 
-	if (fn->export)
+	if (fn->lnk.export)
 		fprintf(f, "export ");
 		fprintf(f, "export ");
 	fprintf(f, "function $%s() {\n", fn->name);
 	fprintf(f, "function $%s() {\n", fn->name);
 	for (b=fn->start; b; b=b->link) {
 	for (b=fn->start; b; b=b->link) {