Drew DeVault: 1 Implement thread-local storage 8 files changed, 68 insertions(+), 7 deletions(-)
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~mpu/qbe/patches/34203/mbox | git am -3Learn more about email & git
--- 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