commit 2ca6fb25a238842418019a3f9ee8d1beb1327f7e
parent 20ee522ce8c4d1ffdd7b6e24a4f7af587c35404a
Author: Quentin Carbonneaux <quentin@c9x.me>
Date: Mon, 31 Jan 2022 22:03:18 +0100
shared linkage logic for func/data
Diffstat:
M | all.h | | | 20 | +++++++++++++++----- |
M | amd64/emit.c | | | 13 | ++----------- |
M | arm64/emit.c | | | 6 | +----- |
M | doc/il.txt | | | 65 | +++++++++++++++++++++++++++++++++++++++++++++++++++++------------ |
M | gas.c | | | 48 | +++++++++++++++++++++++++++--------------------- |
M | parse.c | | | 83 | +++++++++++++++++++++++++++++++++++++++++++------------------------------------ |
6 files changed, 143 insertions(+), 92 deletions(-)
diff --git 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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) {