From 2ca6fb25a238842418019a3f9ee8d1beb1327f7e Mon Sep 17 00:00:00 2001 From: Quentin Carbonneaux Date: Mon, 31 Jan 2022 22:03:18 +0100 Subject: shared linkage logic for func/data --- all.h | 20 +++++++++++---- amd64/emit.c | 13 ++-------- arm64/emit.c | 6 +---- doc/il.txt | 65 ++++++++++++++++++++++++++++++++++++++--------- gas.c | 48 ++++++++++++++++++++--------------- parse.c | 83 ++++++++++++++++++++++++++++++++---------------------------- 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 @@ -3,12 +3,31 @@ 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) { -- cgit 1.4.1