Enable pthread_getspecific() tls for LLVM compiler
authorDavid M Peixotto <dmp@rice.edu>
Tue, 28 Jun 2011 20:31:42 +0000 (15:31 -0500)
committerDavid M Peixotto <dmp@rice.edu>
Fri, 7 Oct 2011 21:48:34 +0000 (16:48 -0500)
LLVM does not support the __thread attribute for thread
local storage and may generate incorrect code for global
register variables. We want to allow building the runtime with
LLVM-based compilers such as llvm-gcc and clang,
particularly for MacOS.

This patch changes the gct variable used by the garbage
collector to use pthread_getspecific() for thread local
storage when an llvm based compiler is used to build the
runtime.

includes/Stg.h
rts/StgCRun.c
rts/Task.c
rts/Task.h
rts/sm/GC.c
rts/sm/GCTDecl.h

index 5665018..5b3b20d 100644 (file)
 # define _BSD_SOURCE
 #endif
 
-#if IN_STG_CODE == 0
+#if IN_STG_CODE == 0 || defined(llvm_CC_FLAVOR)
+// C compilers that use an LLVM back end (clang or llvm-gcc) do not
+// correctly support global register variables so we make sure that
+// we do not declare them for these compilers.
 # define NO_GLOBAL_REG_DECLS   /* don't define fixed registers */
 #endif
 
index 7251e64..be732d1 100644 (file)
@@ -62,13 +62,23 @@ register double fake_f9 __asm__("$f9");
 #endif
 #endif
 
+// yeuch
+#define IN_STGCRUN 1
+#ifdef sparc_HOST_ARCH
 /* include Stg.h first because we want real machine regs in here: we
  * have to get the value of R1 back from Stg land to C land intact.
  */
-// yeuch
-#define IN_STGCRUN 1
 #include "Stg.h"
 #include "Rts.h"
+#else
+/* The other architectures do not require the actual register macro
+ * definitons here because they use hand written assembly to implement
+ * the StgRun function. The sparc code could be changed so that it does
+ * not require the register macro definitions.
+ */
+#include "Rts.h"
+#include "Stg.h"
+#endif
 
 #include "StgRun.h"
 #include "Capability.h"
index cf406b2..9e82148 100644 (file)
@@ -49,6 +49,9 @@ __thread Task *my_task;
 # else
 ThreadLocalKey currentTaskKey;
 # endif
+#ifdef llvm_CC_FLAVOR
+ThreadLocalKey gctKey;
+#endif
 #else
 Task *my_task;
 #endif
@@ -67,6 +70,9 @@ initTaskManager (void)
 #if !defined(MYTASK_USE_TLV)
        newThreadLocalKey(&currentTaskKey);
 #endif
+#if defined(llvm_CC_FLAVOR)
+       newThreadLocalKey(&gctKey);
+#endif
         initMutex(&all_tasks_mutex);
 #endif
     }
@@ -96,10 +102,15 @@ freeTaskManager (void)
 
     RELEASE_LOCK(&all_tasks_mutex);
 
-#if defined(THREADED_RTS) && !defined(MYTASK_USE_TLV)
+#if defined(THREADED_RTS)
     closeMutex(&all_tasks_mutex); 
+#if !defined(MYTASK_USE_TLV)
     freeThreadLocalKey(&currentTaskKey);
 #endif
+#if defined(llvm_CC_FLAVOR)
+    freeThreadLocalKey(&gctKey);
+#endif
+#endif
 
     tasksInitialized = 0;
 
index 424af60..4000a04 100644 (file)
@@ -241,14 +241,21 @@ void interruptWorkerTask (Task *task);
 // A thread-local-storage key that we can use to get access to the
 // current thread's Task structure.
 #if defined(THREADED_RTS)
-#if (defined(linux_HOST_OS) && \
+#if ((defined(linux_HOST_OS) && \
      (defined(i386_HOST_ARCH) || defined(x86_64_HOST_ARCH))) || \
-    (defined(mingw32_HOST_OS) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 4)
+    (defined(mingw32_HOST_OS) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 4)) && \
+    (!defined(llvm_CC_FLAVOR))
 #define MYTASK_USE_TLV
 extern __thread Task *my_task;
 #else
 extern ThreadLocalKey currentTaskKey;
 #endif
+// LLVM-based compilers do not upport the __thread attribute, so we need
+// to store the gct variable as a pthread local storage. We declare the
+// key here to keep thread local storage initialization in the same place.
+#if defined(llvm_CC_FLAVOR)
+extern ThreadLocalKey gctKey;
+#endif
 #else
 extern Task *my_task;
 #endif
index 2252cfc..17d048c 100644 (file)
@@ -1026,7 +1026,7 @@ gcWorkerThread (Capability *cap)
     // necessary if we stole a callee-saves register for gct:
     saved_gct = gct;
 
-    gct = gc_threads[cap->no];
+    SET_GCT(gc_threads[cap->no]);
     gct->id = osThreadId();
 
     stat_gcWorkerThreadStart(gct);
index 11795ca..f9c8fcb 100644 (file)
 
 #define GLOBAL_REG_DECL(type,name,reg) register type name REG(reg);
 
+#ifdef llvm_CC_FLAVOR
+#define SET_GCT(to) (pthread_setspecific(gctKey, to))
+#else
 #define SET_GCT(to) gct = (to)
+#endif
 
 
 
 // about 5% in GC performance, but of course that might change as gcc
 // improves. -- SDM 2009/04/03
 //
-// We ought to do the same on MacOS X, but __thread is not
-// supported there yet (gcc 4.0.1).
+// For MacOSX, we can use an llvm-based C compiler which will store the gct
+// in a thread local variable using pthreads.
 
 extern __thread gc_thread* gct;
 #define DECLARE_GCT __thread gc_thread* gct;
 
+#elif defined(llvm_CC_FLAVOR)
+// LLVM does not support the __thread extension and will generate
+// incorrect code for global register variables. If we are compiling
+// with a C compiler that uses an LLVM back end (clang or llvm-gcc) then we
+// use pthread_getspecific() to handle the thread local storage for gct.
+#define gct ((gc_thread *)(pthread_getspecific(gctKey)))
+#define DECLARE_GCT /* nothing */
 
 #elif defined(sparc_HOST_ARCH)
 // On SPARC we can't pin gct to a register. Names like %l1 are just offsets