aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cp/ChangeLog3
-rw-r--r--gcc/cp/decl.c117
-rw-r--r--gcc/testsuite/ChangeLog5
-rw-r--r--gcc/testsuite/g++.dg/tls/thread_local3.C37
-rw-r--r--gcc/testsuite/g++.dg/tls/thread_local4.C47
-rw-r--r--gcc/testsuite/g++.dg/tls/thread_local5.C47
-rw-r--r--gcc/testsuite/g++.dg/tls/thread_local6.C33
7 files changed, 245 insertions, 44 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 871dbaa..1807f5b 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,8 @@
2012-10-08 Jason Merrill <jason@redhat.com>
+ * decl.c (get_thread_atexit_node): New.
+ (register_dtor_fn): Use it for TLS.
+
Partial implementation of C++11 thread_local.
* decl.c (cp_finish_decl): Remove errors about non-trivial
initialization and destruction of TLS variables.
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index b409c34..7dc13fb 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -6575,6 +6575,24 @@ get_atexit_node (void)
return atexit_node;
}
+/* Like get_atexit_node, but for thread-local cleanups. */
+
+static tree
+get_thread_atexit_node (void)
+{
+ /* The declaration for `__cxa_thread_atexit' is:
+
+ int __cxa_thread_atexit (void (*)(void *), void *, void *) */
+ tree fn_type = build_function_type_list (integer_type_node,
+ get_atexit_fn_ptr_type (),
+ ptr_type_node, ptr_type_node,
+ NULL_TREE);
+
+ /* Now, build the function declaration. */
+ tree atexit_fndecl = build_library_fn_ptr ("__cxa_thread_atexit", fn_type);
+ return decay_conversion (atexit_fndecl, tf_warning_or_error);
+}
+
/* Returns the __dso_handle VAR_DECL. */
static tree
@@ -6666,23 +6684,27 @@ tree
register_dtor_fn (tree decl)
{
tree cleanup;
+ tree addr;
tree compound_stmt;
tree fcall;
tree type;
- bool use_dtor;
- tree arg0, arg1 = NULL_TREE, arg2 = NULL_TREE;
+ bool ob_parm, dso_parm, use_dtor;
+ tree arg0, arg1, arg2;
+ tree atex_node;
type = TREE_TYPE (decl);
if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type))
return void_zero_node;
- /* If we're using "__cxa_atexit" (or "__aeabi_atexit"), and DECL is
- a class object, we can just pass the destructor to
- "__cxa_atexit"; we don't have to build a temporary function to do
- the cleanup. */
- use_dtor = (flag_use_cxa_atexit
- && !targetm.cxx.use_atexit_for_cxa_atexit ()
- && CLASS_TYPE_P (type));
+ /* If we're using "__cxa_atexit" (or "__cxa_thread_atexit" or
+ "__aeabi_atexit"), and DECL is a class object, we can just pass the
+ destructor to "__cxa_atexit"; we don't have to build a temporary
+ function to do the cleanup. */
+ ob_parm = (DECL_THREAD_LOCAL_P (decl)
+ || (flag_use_cxa_atexit
+ && !targetm.cxx.use_atexit_for_cxa_atexit ()));
+ dso_parm = ob_parm;
+ use_dtor = ob_parm && CLASS_TYPE_P (type);
if (use_dtor)
{
int idx;
@@ -6720,44 +6742,48 @@ register_dtor_fn (tree decl)
end_cleanup_fn ();
}
- if (DECL_THREAD_LOCAL_P (decl))
- /* We don't have a thread-local atexit yet. FIXME write one using
- pthread_key_create and friends. */
- sorry ("thread-local variable %q#D with non-trivial "
- "destructor", decl);
-
/* Call atexit with the cleanup function. */
mark_used (cleanup);
cleanup = build_address (cleanup);
- if (flag_use_cxa_atexit && !targetm.cxx.use_atexit_for_cxa_atexit ())
+
+ if (DECL_THREAD_LOCAL_P (decl))
+ atex_node = get_thread_atexit_node ();
+ else
+ atex_node = get_atexit_node ();
+
+ if (use_dtor)
{
- tree addr;
+ /* We must convert CLEANUP to the type that "__cxa_atexit"
+ expects. */
+ cleanup = build_nop (get_atexit_fn_ptr_type (), cleanup);
+ /* "__cxa_atexit" will pass the address of DECL to the
+ cleanup function. */
+ mark_used (decl);
+ addr = build_address (decl);
+ /* The declared type of the parameter to "__cxa_atexit" is
+ "void *". For plain "T*", we could just let the
+ machinery in cp_build_function_call convert it -- but if the
+ type is "cv-qualified T *", then we need to convert it
+ before passing it in, to avoid spurious errors. */
+ addr = build_nop (ptr_type_node, addr);
+ }
+ else if (ob_parm)
+ /* Since the cleanup functions we build ignore the address
+ they're given, there's no reason to pass the actual address
+ in, and, in general, it's cheaper to pass NULL than any
+ other value. */
+ addr = null_pointer_node;
+
+ if (dso_parm)
+ arg2 = cp_build_addr_expr (get_dso_handle_node (),
+ tf_warning_or_error);
+ else
+ arg2 = NULL_TREE;
- if (use_dtor)
- {
- /* We must convert CLEANUP to the type that "__cxa_atexit"
- expects. */
- cleanup = build_nop (get_atexit_fn_ptr_type (), cleanup);
- /* "__cxa_atexit" will pass the address of DECL to the
- cleanup function. */
- mark_used (decl);
- addr = build_address (decl);
- /* The declared type of the parameter to "__cxa_atexit" is
- "void *". For plain "T*", we could just let the
- machinery in cp_build_function_call convert it -- but if the
- type is "cv-qualified T *", then we need to convert it
- before passing it in, to avoid spurious errors. */
- addr = build_nop (ptr_type_node, addr);
- }
- else
- /* Since the cleanup functions we build ignore the address
- they're given, there's no reason to pass the actual address
- in, and, in general, it's cheaper to pass NULL than any
- other value. */
- addr = null_pointer_node;
- arg2 = cp_build_addr_expr (get_dso_handle_node (),
- tf_warning_or_error);
- if (targetm.cxx.use_aeabi_atexit ())
+ if (ob_parm)
+ {
+ if (!DECL_THREAD_LOCAL_P (decl)
+ && targetm.cxx.use_aeabi_atexit ())
{
arg1 = cleanup;
arg0 = addr;
@@ -6769,8 +6795,11 @@ register_dtor_fn (tree decl)
}
}
else
- arg0 = cleanup;
- return cp_build_function_call_nary (get_atexit_node (), tf_warning_or_error,
+ {
+ arg0 = cleanup;
+ arg1 = NULL_TREE;
+ }
+ return cp_build_function_call_nary (atex_node, tf_warning_or_error,
arg0, arg1, arg2, NULL_TREE);
}
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 76d0762..d575489 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,10 @@
2012-10-08 Jason Merrill <jason@redhat.com>
+ * g++.dg/tls/thread_local3.C: New.
+ * g++.dg/tls/thread_local4.C: New.
+ * g++.dg/tls/thread_local5.C: New.
+ * g++.dg/tls/thread_local6.C: New.
+
* g++.dg/tls/init-2.C: Tweak errors.
* g++.dg/tls/thread_local1.C: New.
* g++.dg/tls/thread_local2.C: New.
diff --git a/gcc/testsuite/g++.dg/tls/thread_local3.C b/gcc/testsuite/g++.dg/tls/thread_local3.C
new file mode 100644
index 0000000..461f126
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local3.C
@@ -0,0 +1,37 @@
+// { dg-do run }
+// { dg-require-effective-target c++11 }
+// { dg-require-effective-target tls_runtime }
+// { dg-require-effective-target pthread }
+// { dg-options -pthread }
+
+int c;
+int d;
+struct A
+{
+ A() { ++c; }
+ ~A() { ++d; }
+};
+
+void f()
+{
+ thread_local A a;
+}
+
+void *thread_main(void *)
+{
+ f(); f(); f();
+}
+
+#include <pthread.h>
+
+int main()
+{
+ pthread_t thread;
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_join(thread, 0);
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_join(thread, 0);
+
+ if (c != 2 || d != 2)
+ __builtin_abort();
+}
diff --git a/gcc/testsuite/g++.dg/tls/thread_local4.C b/gcc/testsuite/g++.dg/tls/thread_local4.C
new file mode 100644
index 0000000..53b1f05
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local4.C
@@ -0,0 +1,47 @@
+// Test for cleanups with pthread_cancel.
+
+// { dg-do run }
+// { dg-require-effective-target c++11 }
+// { dg-require-effective-target tls_runtime }
+// { dg-require-effective-target pthread }
+// { dg-options -pthread }
+
+#include <pthread.h>
+#include <unistd.h>
+
+int c;
+int d;
+struct A
+{
+ A() { ++c; }
+ ~A() { ++d; }
+};
+
+void f()
+{
+ thread_local A a;
+}
+
+void *thread_main(void *)
+{
+ f(); f(); f();
+ while (true)
+ {
+ pthread_testcancel();
+ sleep (1);
+ }
+}
+
+int main()
+{
+ pthread_t thread;
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_cancel(thread);
+ pthread_join(thread, 0);
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_cancel(thread);
+ pthread_join(thread, 0);
+
+ if (c != 2 || d != 2)
+ __builtin_abort();
+}
diff --git a/gcc/testsuite/g++.dg/tls/thread_local5.C b/gcc/testsuite/g++.dg/tls/thread_local5.C
new file mode 100644
index 0000000..7ce02f6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local5.C
@@ -0,0 +1,47 @@
+// Test for cleanups in the main thread, too.
+
+// { dg-do run }
+// { dg-require-effective-target c++11 }
+// { dg-require-effective-target tls_runtime }
+// { dg-require-effective-target pthread }
+// { dg-options -pthread }
+
+#include <pthread.h>
+#include <unistd.h>
+
+int c;
+int d;
+struct A
+{
+ A() { ++c; }
+ ~A() {
+ if (++d == 3)
+ _exit (0);
+ }
+};
+
+void f()
+{
+ thread_local A a;
+}
+
+void *thread_main(void *)
+{
+ f(); f(); f();
+}
+
+int main()
+{
+ pthread_t thread;
+ thread_main(0);
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_join(thread, 0);
+ pthread_create (&thread, 0, thread_main, 0);
+ pthread_join(thread, 0);
+
+ // The dtor for a in the main thread is run after main exits, so we
+ // return 1 now and override the return value with _exit above.
+ if (c != 3 || d != 2)
+ __builtin_abort();
+ return 1;
+}
diff --git a/gcc/testsuite/g++.dg/tls/thread_local6.C b/gcc/testsuite/g++.dg/tls/thread_local6.C
new file mode 100644
index 0000000..118969a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tls/thread_local6.C
@@ -0,0 +1,33 @@
+// Test for cleanups in the main thread without -pthread.
+
+// { dg-do run }
+// { dg-options "-std=c++11" }
+// { dg-require-effective-target tls_runtime }
+
+extern "C" void _exit (int);
+
+int c;
+struct A
+{
+ A() { ++c; }
+ ~A() { if (c == 1) _exit(0); }
+};
+
+void f()
+{
+ thread_local A a;
+}
+
+void *thread_main(void *)
+{
+ f(); f(); f();
+}
+
+int main()
+{
+ thread_main(0);
+
+ // The dtor for a in the main thread is run after main exits, so we
+ // return 1 now and override the return value with _exit above.
+ return 1;
+}