aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md1
-rw-r--r--cc.h2
-rw-r--r--decl.c8
-rw-r--r--eval.c2
-rw-r--r--qbe.c12
-rw-r--r--test/thread-local.c10
-rw-r--r--test/thread-local.qbe23
7 files changed, 48 insertions, 10 deletions
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
+}