From 0c1f837141457574bd71ef0d761613ba6a941380 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 31 Jan 2023 19:33:47 +0100 Subject: implement _Thread_local storage class Implements: https://todo.sr.ht/~mcf/cproc/8 --- README.md | 1 - cc.h | 2 +- decl.c | 8 +++----- eval.c | 2 +- qbe.c | 12 ++++++++++-- test/thread-local.c | 10 ++++++++++ test/thread-local.qbe | 23 +++++++++++++++++++++++ 7 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 test/thread-local.c create mode 100644 test/thread-local.qbe diff --git a/README.md b/README.md index f1a1fd1..ea3a807 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,6 @@ specified in `config.h`. - Digraph sequences ([6.4.6p3], will not be implemented). - Variable-length arrays ([#1]). - `volatile`-qualified types ([#7]). -- `_Thread_local` storage-class specifier ([#5]). - `long double` type ([#3]). - Inline assembly ([#5]). - Preprocessor ([#6]). diff --git a/cc.h b/cc.h index fffee6f..d595eb8 100644 --- a/cc.h +++ b/cc.h @@ -541,7 +541,7 @@ void switchcase(struct switchcases *, unsigned long long, struct block *); struct block *mkblock(char *); -struct value *mkglobal(char *, bool); +struct value *mkglobal(char *, bool, bool); struct value *mkintconst(unsigned long long); unsigned long long intconstvalue(struct value *); diff --git a/decl.c b/decl.c index 6e7d360..8f35033 100644 --- a/decl.c +++ b/decl.c @@ -941,7 +941,7 @@ declcommon(struct scope *s, enum declkind kind, char *name, char *asmname, struc if (kind == DECLFUNC || linkage != LINKNONE || sc & SCSTATIC) { if (asmname) name = asmname; - d->value = mkglobal(name, linkage == LINKNONE && !asmname); + d->value = mkglobal(name, linkage == LINKNONE && !asmname, sc & SCTHREADLOCAL); d->asmname = asmname; } return d; @@ -981,8 +981,6 @@ decl(struct scope *s, struct func *f) if (sc & SCREGISTER) error(&tok.loc, "external declaration must not contain 'register'"); } - if (sc & SCTHREADLOCAL) - error(&tok.loc, "'_Thread_local' is not yet supported"); if (consume(TSEMICOLON)) { /* XXX 6.7p2 error unless in function parameter/struct/union, or tag/enum members are declared */ return true; @@ -1029,7 +1027,7 @@ decl(struct scope *s, struct func *f) error(&tok.loc, "object '%s' redefined", name); init = parseinit(s, d->type); hasinit = true; - } else if (d->linkage != LINKNONE) { + } else if (d->linkage != LINKNONE && (!(sc & SCTHREADLOCAL) || sc & SCEXTERN)) { if (!(sc & SCEXTERN) && !d->defined && !d->tentative) { d->tentative = true; *tentativedefnsend = d; @@ -1096,7 +1094,7 @@ stringdecl(struct expr *expr) d = *entry; if (!d) { d = mkdecl(NULL, DECLOBJECT, expr->type, QUALNONE, LINKNONE); - d->value = mkglobal("string", true); + d->value = mkglobal("string", true, false); emitdata(d, mkinit(0, expr->type->size, (struct bitfield){0}, expr)); *entry = d; } diff --git a/eval.c b/eval.c index d6c70b1..2c5a84f 100644 --- a/eval.c +++ b/eval.c @@ -119,7 +119,7 @@ eval(struct expr *expr) if (expr->u.compound.storage != SDSTATIC) break; d = mkdecl(NULL, DECLOBJECT, t, expr->qual, LINKNONE); - d->value = mkglobal(NULL, true); + d->value = mkglobal(NULL, true, false); emitdata(d, expr->u.compound.init); expr->kind = EXPRIDENT; expr->u.ident.decl = d; diff --git a/qbe.c b/qbe.c index b01d2db..a5817f9 100644 --- a/qbe.c +++ b/qbe.c @@ -20,6 +20,7 @@ struct value { VALUE_LABEL, } kind; unsigned id; + bool threadlocal; union { char *name; unsigned long long i; @@ -128,7 +129,7 @@ mkblock(char *name) } struct value * -mkglobal(char *name, bool private) +mkglobal(char *name, bool private, bool threadlocal) { static unsigned id; struct value *v; @@ -137,6 +138,7 @@ mkglobal(char *name, bool private) v->kind = VALUE_GLOBAL; v->u.name = name; v->id = private ? ++id : 0; + v->threadlocal = threadlocal; return v; } @@ -501,7 +503,7 @@ mkfunc(struct decl *decl, char *name, struct type *t, struct scope *s) t = mkarraytype(&typechar, QUALCONST, strlen(name) + 1); d = mkdecl("__func__", DECLOBJECT, t, QUALNONE, LINKNONE); - d->value = mkglobal(d->name, true); + d->value = mkglobal(d->name, true, false); scopeputdecl(s, d); f->namedecl = d; @@ -1125,6 +1127,8 @@ emitinst(struct inst **instp, struct inst **instend) } fputs(instname[inst->kind], stdout); putchar(' '); + if (inst->arg[0]->kind == VALUE_GLOBAL && inst->arg[0]->threadlocal) + fputs("thread ", stdout); emitvalue(inst->arg[0]); ++instp; op = inst->kind; @@ -1152,6 +1156,8 @@ emitinst(struct inst **instp, struct inst **instend) default: if (inst->arg[1]) { fputs(", ", stdout); + if (inst->arg[1]->kind == VALUE_GLOBAL && inst->arg[1]->threadlocal) + fputs("thread ", stdout); emitvalue(inst->arg[1]); } } @@ -1322,6 +1328,8 @@ emitdata(struct decl *d, struct init *init) align = d->u.obj.align; for (cur = init; cur; cur = cur->next) cur->expr = eval(cur->expr); + if (d->value->threadlocal) + fputs("thread ", stdout); if (d->linkage == LINKEXTERN) fputs("export ", stdout); fputs("data ", stdout); diff --git a/test/thread-local.c b/test/thread-local.c new file mode 100644 index 0000000..3086699 --- /dev/null +++ b/test/thread-local.c @@ -0,0 +1,10 @@ +thread_local int a = 1; +static thread_local int b = 2; +extern thread_local int c = 3; +thread_local int d; +static thread_local int e; +extern thread_local int f; +int main(void) { + static thread_local int x = 6; + return a + b + c + d + e - x; +} diff --git a/test/thread-local.qbe b/test/thread-local.qbe new file mode 100644 index 0000000..0e9ba83 --- /dev/null +++ b/test/thread-local.qbe @@ -0,0 +1,23 @@ +thread export data $a = align 4 { w 1, } +thread data $b = align 4 { w 2, } +thread export data $c = align 4 { w 3, } +thread export data $d = align 4 { z 4 } +thread data $e = align 4 { z 4 } +thread data $.Lx.2 = align 4 { w 6, } +export +function w $main() { +@start.1 +@body.2 + %.1 =w loadw thread $a + %.2 =w loadw thread $b + %.3 =w add %.1, %.2 + %.4 =w loadw thread $c + %.5 =w add %.3, %.4 + %.6 =w loadw thread $d + %.7 =w add %.5, %.6 + %.8 =w loadw thread $e + %.9 =w add %.7, %.8 + %.10 =w loadw thread $.Lx.2 + %.11 =w sub %.9, %.10 + ret %.11 +} -- cgit v1.2.3