---
See test/tls.ssa for usage. Essentially:
threadlocal data $i = { w 42 }
%ret =l loadl threadlocal $i
Seeking sign-off on the approach and syntax. Still to-do:
- rv64, arm64 support
- Documentation updates
Points of note for the review:
- Adding -lpthread to every test
- use of strcmp in gasemitlnk
- Any edge cases I've forgotten about?
all.h | 2 ++
amd64/emit.c | 7 ++++++-
amd64/isel.c | 1 +
gas.c | 6 ++++++
parse.c | 24 ++++++++++++++++++++++++
test/tls.ssa | 23 +++++++++++++++++++++++
tools/lexh.c | 2 +-
tools/test.sh | 10 +++++-----
8 files changed, 68 insertions(+), 7 deletions(-)
create mode 100644 test/tls.ssa
diff --git a/all.h b/all.h
index c8ec93c..3418f63 100644
--- a/all.h
+++ b/all.h
@@ -309,6 +309,7 @@ struct Con {
CUndef,
CBits,
CAddr,
+ CThreadLocal,
} type;
uint32_t label;
union {
@@ -334,6 +335,7 @@ struct Lnk {
char align;
char *sec;
char *secf;
+ char istls;
};
struct Fn {
diff --git a/amd64/emit.c b/amd64/emit.c
index b8e9e8e..9870e0b 100644
--- a/amd64/emit.c
+++ b/amd64/emit.c
@@ -171,6 +171,10 @@ emitcon(Con *con, FILE *f)
if (con->bits.i)
fprintf(f, "%+"PRId64, con->bits.i);
break;
+ case CThreadLocal:
+ l = str(con->label);
+ fprintf(f, "%%fs:%s@tpoff", l);
+ break;
case CBits:
fprintf(f, "%"PRId64, con->bits.i);
break;
@@ -306,7 +310,8 @@ Next:
fputc(')', f);
break;
case RCon:
- fputc('$', f);
+ if (fn->con[ref.val].type != CThreadLocal)
+ fputc('$', f);
emitcon(&fn->con[ref.val], f);
break;
default:
diff --git a/amd64/isel.c b/amd64/isel.c
index 5a64429..592f7fc 100644
--- a/amd64/isel.c
+++ b/amd64/isel.c
@@ -36,6 +36,7 @@ noimm(Ref r, Fn *fn)
return 0;
switch (fn->con[r.val].type) {
case CAddr:
+ case CThreadLocal:
/* we only support the 'small'
* code model of the ABI, this
* means that we can always
diff --git a/gas.c b/gas.c
index 865edba..92ffd7f 100644
--- a/gas.c
+++ b/gas.c
@@ -29,6 +29,12 @@ gasemitlnk(char *n, Lnk *l, char *s, FILE *f)
fprintf(f, ".section %s", l->sec);
if (l->secf)
fprintf(f, ", %s", l->secf);
+ } else if (l->istls) {
+ if (strcmp(s, ".bss") == 0) {
+ fprintf(f, ".section .tbss,\"awT\"");
+ } else {
+ fprintf(f, ".section .tdata,\"awT\"");
+ }
} else {
fputs(s, f);
}
diff --git a/parse.c b/parse.c
index 1912c8b..5e09a21 100644
--- a/parse.c
+++ b/parse.c
@@ -42,6 +42,7 @@ enum {
Ttype,
Tdata,
Tsection,
+ Tthreadlocal,
Talign,
Tl,
Tw,
@@ -92,6 +93,7 @@ static char *kwmap[Ntok] = {
[Ttype] = "type",
[Tdata] = "data",
[Tsection] = "section",
+ [Tthreadlocal] = "threadlocal",
[Talign] = "align",
[Tl] = "l",
[Tw] = "w",
@@ -403,6 +405,13 @@ parseref()
case Tglo:
c.type = CAddr;
c.label = intern(tokval.str);
+ goto Look;
+ case Tthreadlocal:
+ c.type = CThreadLocal;
+ if (next() != Tglo)
+ err("function name expected");
+ c.label = intern(tokval.str);
+ goto Look;
Look:
return newcon(&c, curf);
default:
@@ -798,6 +807,9 @@ parsefn(Lnk *lnk)
int i;
PState ps;
+ if (lnk->istls)
+ err("TLS is not supported for function declarations");
+
curb = 0;
nblk = 0;
curi = insb;
@@ -1073,6 +1085,8 @@ parselnk(Lnk *lnk)
case Tsection:
if (lnk->sec)
err("only one section allowed");
+ if (lnk->istls)
+ err("threadlocal and section cannot co-exist");
if (next() != Tstr)
err("section \"name\" expected");
lnk->sec = tokval.str;
@@ -1081,6 +1095,13 @@ parselnk(Lnk *lnk)
lnk->secf = tokval.str;
}
break;
+ case Tthreadlocal:
+ if (lnk->istls)
+ err("only one TLS directive allowed");
+ if (lnk->sec)
+ err("threadlocal and section cannot co-exist");
+ lnk->istls = 1;
+ break;
default:
if (haslnk)
if (t != Tdata)
@@ -1138,6 +1159,9 @@ printcon(Con *c, FILE *f)
if (c->bits.i)
fprintf(f, "%+"PRIi64, c->bits.i);
break;
+ case CThreadLocal:
+ fprintf(f, "threadlocal $%s", str(c->label));
+ break;
case CBits:
if (c->flt == 1)
fprintf(f, "s_%f", c->bits.s);
diff --git a/test/tls.ssa b/test/tls.ssa
new file mode 100644
index 0000000..5975932
--- /dev/null
+++ b/test/tls.ssa
@@ -0,0 +1,23 @@
+export function w $main() {
+@start
+ %thread =l alloc8 8
+ %retval =l alloc8 8
+
+ storew 1337, threadlocal $i
+ call $pthread_create(l %thread, l 0, l $thread, l 0)
+
+ %thread_id =l loadl %thread
+ call $pthread_join(l %thread_id, l %retval)
+
+ %ret =l loadl %retval
+ %res =l cnel %ret, 42
+ ret %res
+}
+
+function w $thread() {
+@start
+ %ret =l loadl threadlocal $i
+ ret %ret
+}
+
+threadlocal data $i = { w 42 }
diff --git a/tools/lexh.c b/tools/lexh.c
index 8d0af21..5be376e 100644
--- a/tools/lexh.c
+++ b/tools/lexh.c
@@ -26,7 +26,7 @@ char *tok[] = {
"vaarg", "vastart", "...", "env",
"call", "phi", "jmp", "jnz", "ret", "export",
- "function", "type", "data", "section", "align",
+ "function", "type", "data", "section", "threadlocal", "align",
"l", "w", "h", "b", "d", "s", "z", "loadw", "loadl",
"loads", "loadd", "alloc1", "alloc2",
diff --git a/tools/test.sh b/tools/test.sh
index 4653b83..37105df 100755
--- a/tools/test.sh
+++ b/tools/test.sh
@@ -22,7 +22,7 @@ init() {
arm64)
for p in aarch64-linux-musl aarch64-linux-gnu
do
- cc="$p-gcc -no-pie -static"
+ cc="$p-gcc -no-pie -static -lpthread"
qemu="qemu-aarch64"
if
$cc -v >/dev/null 2>&1 &&
@@ -46,7 +46,7 @@ init() {
rv64)
for p in riscv64-linux-musl riscv64-linux-gnu
do
- cc="$p-gcc -no-pie -static"
+ cc="$p-gcc -no-pie -static -lpthread"
qemu="qemu-riscv64"
if
$cc -v >/dev/null 2>&1 &&
@@ -73,13 +73,13 @@ init() {
cc="cc -Wl,-no_pie"
;;
*OpenBSD*)
- cc="cc -nopie"
+ cc="cc -nopie -lpthread"
;;
*FreeBSD*)
- cc="cc"
+ cc="cc -lpthread"
;;
*)
- cc="${CC:-cc} -no-pie"
+ cc="${CC:-cc} -no-pie -lpthread"
testcc "$cc" || cc="${CC:-cc}"
;;
esac
base-commit: c8cd2824eae0137505fe46530c3a8e9788ab9a63
--
2.37.1