mirror of
git://c9x.me/qbe.git
synced 2026-05-26 15:34:41 +00:00
extern DYNCONST
This commit is contained in:
parent
dba8d5a4bf
commit
7ab2fbe07c
11
all.h
11
all.h
@ -292,9 +292,16 @@ struct Use {
|
||||
};
|
||||
|
||||
struct Sym {
|
||||
/* SGlo = 0 (direct access)
|
||||
* SThr = 1 (local-exec TLS)
|
||||
* SExt = 2 (GOT/PLT access)
|
||||
* SExt|SThr = 3 (initial-exec TLS)
|
||||
*/
|
||||
enum {
|
||||
SGlo,
|
||||
SThr,
|
||||
SGlo = 0,
|
||||
SThr = 1,
|
||||
SExt = 2,
|
||||
SExtThr = SExt|SThr,
|
||||
} type;
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
38
amd64/emit.c
38
amd64/emit.c
@ -195,7 +195,7 @@ emitcon(Con *con, E *e)
|
||||
case CAddr:
|
||||
l = str(con->sym.id);
|
||||
p = l[0] == '"' ? "" : T.assym;
|
||||
if (con->sym.type == SThr) {
|
||||
if (con->sym.type & SThr) {
|
||||
if (T.apple)
|
||||
fprintf(e->f, "%s%s@TLVP", p, l);
|
||||
else
|
||||
@ -551,14 +551,48 @@ emitins(Ins i, E *e)
|
||||
regtoa(i.to.val, SLong));
|
||||
break;
|
||||
}
|
||||
if (rtype(i.arg[0]) == RCon
|
||||
&& e->fn->con[i.arg[0].val].sym.type == SExt) {
|
||||
/* load address from the GOT */
|
||||
assert(isreg(i.to));
|
||||
con = &e->fn->con[i.arg[0].val];
|
||||
sym = str(con->sym.id);
|
||||
assert(!con->bits.i);
|
||||
fprintf(e->f,
|
||||
"\tmovq %s%s@GOTPCREL(%%rip), %%%s\n",
|
||||
sym[0] == '"' ? "" : T.assym, sym,
|
||||
regtoa(i.to.val, SLong));
|
||||
break;
|
||||
}
|
||||
if (!T.apple
|
||||
&& rtype(i.arg[0]) == RCon
|
||||
&& e->fn->con[i.arg[0].val].sym.type == SExtThr) {
|
||||
/* initial-exec TLS: load offset from
|
||||
* GOT, add to thread-base register */
|
||||
assert(isreg(i.to));
|
||||
con = &e->fn->con[i.arg[0].val];
|
||||
sym = str(con->sym.id);
|
||||
assert(!con->bits.i);
|
||||
emitf("movq %%fs:0, %L=", &i, e);
|
||||
fprintf(e->f,
|
||||
"\taddq %s%s@gottpoff(%%rip), %%%s\n",
|
||||
sym[0] == '"' ? "" : T.assym, sym,
|
||||
regtoa(i.to.val, SLong));
|
||||
break;
|
||||
}
|
||||
goto Table;
|
||||
case Ocall:
|
||||
/* calls simply have a weird syntax in AT&T
|
||||
* assembly... */
|
||||
switch (rtype(i.arg[0])) {
|
||||
case RCon:
|
||||
con = &e->fn->con[i.arg[0].val];
|
||||
fprintf(e->f, "\tcallq ");
|
||||
emitcon(&e->fn->con[i.arg[0].val], e);
|
||||
emitcon(con, e);
|
||||
if (con->type == CAddr
|
||||
&& (con->sym.type & SExt)
|
||||
&& !T.apple)
|
||||
fprintf(e->f, "@PLT");
|
||||
fprintf(e->f, "\n");
|
||||
break;
|
||||
case RTmp:
|
||||
|
||||
28
amd64/isel.c
28
amd64/isel.c
@ -120,8 +120,34 @@ fixarg(Ref *r, int k, Ins *i, Fn *fn)
|
||||
r1 = newtmp("isel", Kl, fn);
|
||||
emit(Oaddr, Kl, r1, SLOT(s), R);
|
||||
}
|
||||
else if (op != Ocall && hascon(r0, &c, fn)
|
||||
&& c->type == CAddr && (c->sym.type & SExt)
|
||||
&& (!T.apple || c->sym.type == SExt)) {
|
||||
/* extern symbols need indirection
|
||||
* through the GOT
|
||||
*/
|
||||
r1 = newtmp("isel", Kl, fn);
|
||||
if (c->bits.i) {
|
||||
r2 = newtmp("isel", Kl, fn);
|
||||
cc = (Con){.type = CBits};
|
||||
cc.bits.i = c->bits.i;
|
||||
r3 = newcon(&cc, fn);
|
||||
emit(Oadd, Kl, r1, r2, r3);
|
||||
} else
|
||||
r2 = r1;
|
||||
cc = *c;
|
||||
cc.bits.i = 0;
|
||||
r3 = newcon(&cc, fn);
|
||||
emit(Oaddr, Kl, r2, r3, R);
|
||||
if (rtype(r0) == RMem) {
|
||||
m = &fn->mem[r0.val];
|
||||
m->offset.type = CUndef;
|
||||
m->base = r1;
|
||||
r1 = r0;
|
||||
}
|
||||
}
|
||||
else if (T.apple && hascon(r0, &c, fn)
|
||||
&& c->type == CAddr && c->sym.type == SThr) {
|
||||
&& c->type == CAddr && (c->sym.type & SThr)) {
|
||||
r1 = newtmp("isel", Kl, fn);
|
||||
if (c->bits.i) {
|
||||
r2 = newtmp("isel", Kl, fn);
|
||||
|
||||
13
arm64/emit.c
13
arm64/emit.c
@ -287,6 +287,17 @@ loadaddr(Con *c, char *rn, E *e)
|
||||
"\tadd\tR, R, #:tprel_hi12:SO, lsl #12\n"
|
||||
"\tadd\tR, R, #:tprel_lo12_nc:SO\n";
|
||||
break;
|
||||
case SExt:
|
||||
if (T.apple)
|
||||
s = "\tadrp\tR, S@gotpageO\n"
|
||||
"\tldr\tR, [R, S@gotpageoffO]\n";
|
||||
else
|
||||
s = "\tadrp\tR, :got:SO\n"
|
||||
"\tldr\tR, [R, #:got_lo12:SO]\n";
|
||||
break;
|
||||
case SExtThr:
|
||||
die("extern thread not yet implemented on arm64");
|
||||
break;
|
||||
}
|
||||
|
||||
l = str(c->sym.id);
|
||||
@ -468,7 +479,7 @@ emitins(Ins *i, E *e)
|
||||
goto Table;
|
||||
c = &e->fn->con[i->arg[0].val];
|
||||
if (c->type != CAddr
|
||||
|| c->sym.type != SGlo
|
||||
|| (c->sym.type & SThr)
|
||||
|| c->bits.i)
|
||||
die("invalid call argument");
|
||||
l = str(c->sym.id);
|
||||
|
||||
12
doc/il.txt
12
doc/il.txt
@ -181,6 +181,8 @@ by zero-extension, or by sign-extension.
|
||||
DYNCONST :=
|
||||
CONST
|
||||
| 'thread' $IDENT # Thread-local symbol
|
||||
| 'extern' $IDENT # Extern symbol (GOT)
|
||||
| 'extern' 'thread' $IDENT # Extern thread-local (initial-exec)
|
||||
|
||||
VAL :=
|
||||
DYNCONST
|
||||
@ -225,6 +227,16 @@ When the `thread` keyword prefixes a symbol name, the
|
||||
symbol's numeric value is resolved at runtime in the
|
||||
thread-local storage.
|
||||
|
||||
When the `extern` keyword prefixes a symbol name, the
|
||||
symbol is accessed indirectly through the Global Offset
|
||||
Table (GOT). Function calls to extern symbols use the
|
||||
Procedure Linkage Table (PLT). This enables PIE and
|
||||
PIC code generation for symbols that are not part of
|
||||
the main executable. When `extern` is combined with
|
||||
`thread`, the symbol is accessed using the initial-exec
|
||||
TLS model, suitable for thread-local variables defined
|
||||
in shared libraries.
|
||||
|
||||
Vals are used as arguments in regular, phi, and jump
|
||||
instructions within function definitions. They are
|
||||
either constants or function-scope temporaries.
|
||||
|
||||
15
parse.c
15
parse.c
@ -57,6 +57,7 @@ enum Token {
|
||||
Thlt,
|
||||
Texport,
|
||||
Tthread,
|
||||
Textern,
|
||||
Tcommon,
|
||||
Tfunc,
|
||||
Ttype,
|
||||
@ -116,6 +117,7 @@ static char *kwmap[Ntok] = {
|
||||
[Thlt] = "hlt",
|
||||
[Texport] = "export",
|
||||
[Tthread] = "thread",
|
||||
[Textern] = "extern",
|
||||
[Tcommon] = "common",
|
||||
[Tfunc] = "function",
|
||||
[Ttype] = "type",
|
||||
@ -448,11 +450,20 @@ parseref()
|
||||
c.bits.d = tokval.fltd;
|
||||
c.flt = 2;
|
||||
break;
|
||||
case Textern:
|
||||
c.sym.type = SExt;
|
||||
if (peek() == Tthread) {
|
||||
next();
|
||||
c.sym.type |= SThr;
|
||||
}
|
||||
expect(Tglo);
|
||||
goto Glo;
|
||||
case Tthread:
|
||||
c.sym.type = SThr;
|
||||
expect(Tglo);
|
||||
/* fall through */
|
||||
case Tglo:
|
||||
Glo:
|
||||
c.type = CAddr;
|
||||
c.sym.id = intern(tokval.str);
|
||||
break;
|
||||
@ -1255,7 +1266,9 @@ printcon(Con *c, FILE *f)
|
||||
case CUndef:
|
||||
break;
|
||||
case CAddr:
|
||||
if (c->sym.type == SThr)
|
||||
if (c->sym.type & SExt)
|
||||
fprintf(f, "extern ");
|
||||
if (c->sym.type & SThr)
|
||||
fprintf(f, "thread ");
|
||||
fprintf(f, "$%s", str(c->sym.id));
|
||||
if (c->bits.i)
|
||||
|
||||
21
rv64/emit.c
21
rv64/emit.c
@ -131,7 +131,7 @@ slot(Ref r, Fn *fn)
|
||||
static void
|
||||
emitaddr(Con *c, FILE *f)
|
||||
{
|
||||
assert(c->sym.type == SGlo);
|
||||
assert(c->sym.type == SGlo || c->sym.type == SExt);
|
||||
fputs(str(c->sym.id), f);
|
||||
if (c->bits.i)
|
||||
fprintf(f, "+%"PRIi64, c->bits.i);
|
||||
@ -231,7 +231,8 @@ loadaddr(Con *c, char *rn, FILE *f)
|
||||
{
|
||||
char off[32];
|
||||
|
||||
if (c->sym.type == SThr) {
|
||||
switch (c->sym.type) {
|
||||
case SThr:
|
||||
if (c->bits.i)
|
||||
sprintf(off, "+%"PRIi64, c->bits.i);
|
||||
else
|
||||
@ -242,10 +243,20 @@ loadaddr(Con *c, char *rn, FILE *f)
|
||||
rn, rn, str(c->sym.id), off);
|
||||
fprintf(f, "\taddi %s, %s, %%tprel_lo(%s)%s\n",
|
||||
rn, rn, str(c->sym.id), off);
|
||||
} else {
|
||||
break;
|
||||
case SExt:
|
||||
fprintf(f, "\tla %s, ", rn);
|
||||
emitaddr(c, f);
|
||||
fputc('\n', f);
|
||||
break;
|
||||
case SExtThr:
|
||||
die("extern thread not yet implemented on rv64");
|
||||
break;
|
||||
default:
|
||||
fprintf(f, "\tla %s, ", rn);
|
||||
emitaddr(c, f);
|
||||
fputc('\n', f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,7 +293,7 @@ fixmem(Ref *pr, Fn *fn, FILE *f)
|
||||
if (rtype(r) == RCon) {
|
||||
c = &fn->con[r.val];
|
||||
if (c->type == CAddr)
|
||||
if (c->sym.type == SThr) {
|
||||
if (c->sym.type != SGlo) {
|
||||
loadcon(c, T6, Kl, f);
|
||||
*pr = TMP(T6);
|
||||
}
|
||||
@ -387,7 +398,7 @@ emitins(Ins *i, Fn *fn, FILE *f)
|
||||
case RCon:
|
||||
con = &fn->con[i->arg[0].val];
|
||||
if (con->type != CAddr
|
||||
|| con->sym.type != SGlo
|
||||
|| (con->sym.type & SThr)
|
||||
|| con->bits.i)
|
||||
goto Invalid;
|
||||
fprintf(f, "\tcall %s\n", str(con->sym.id));
|
||||
|
||||
@ -29,7 +29,7 @@ char *tok[] = {
|
||||
"function", "type", "data", "section", "align", "dbgfile",
|
||||
"blit", "l", "w", "sh", "uh", "h", "sb", "ub", "b",
|
||||
"d", "s", "z", "loadw", "loadl", "loads", "loadd",
|
||||
"alloc1", "alloc2", "thread", "common",
|
||||
"alloc1", "alloc2", "thread", "extern", "common",
|
||||
|
||||
};
|
||||
enum {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user