diff options
author | David M Peixotto <dmp@rice.edu> | 2011-06-28 15:31:42 -0500 |
---|---|---|
committer | David M Peixotto <dmp@rice.edu> | 2011-10-07 16:48:34 -0500 |
commit | dba7254566b121408e7167200d0223a531b66e8b (patch) | |
tree | 0a3bfcb739b35123822cb644db93081b46c54fca | |
parent | 29a97fded4010bd01aa0a17945c84258e285d421 (diff) | |
download | haskell-dba7254566b121408e7167200d0223a531b66e8b.tar.gz |
Enable pthread_getspecific() tls for LLVM compiler
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.
-rw-r--r-- | includes/Stg.h | 5 | ||||
-rw-r--r-- | rts/StgCRun.c | 14 | ||||
-rw-r--r-- | rts/Task.c | 13 | ||||
-rw-r--r-- | rts/Task.h | 11 | ||||
-rw-r--r-- | rts/sm/GC.c | 2 | ||||
-rw-r--r-- | rts/sm/GCTDecl.h | 15 |
6 files changed, 51 insertions, 9 deletions
diff --git a/includes/Stg.h b/includes/Stg.h index 5665018d93..5b3b20d4d6 100644 --- a/includes/Stg.h +++ b/includes/Stg.h @@ -49,7 +49,10 @@ # 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 diff --git a/rts/StgCRun.c b/rts/StgCRun.c index 7251e64253..be732d1640 100644 --- a/rts/StgCRun.c +++ b/rts/StgCRun.c @@ -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" diff --git a/rts/Task.c b/rts/Task.c index cf406b2abe..9e8214899c 100644 --- a/rts/Task.c +++ b/rts/Task.c @@ -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(¤tTaskKey); #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(¤tTaskKey); #endif +#if defined(llvm_CC_FLAVOR) + freeThreadLocalKey(&gctKey); +#endif +#endif tasksInitialized = 0; diff --git a/rts/Task.h b/rts/Task.h index 424af607ea..4000a045d4 100644 --- a/rts/Task.h +++ b/rts/Task.h @@ -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 diff --git a/rts/sm/GC.c b/rts/sm/GC.c index 2252cfcd63..17d048c1fa 100644 --- a/rts/sm/GC.c +++ b/rts/sm/GC.c @@ -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); diff --git a/rts/sm/GCTDecl.h b/rts/sm/GCTDecl.h index 11795ca7fd..f9c8fcb137 100644 --- a/rts/sm/GCTDecl.h +++ b/rts/sm/GCTDecl.h @@ -26,7 +26,11 @@ #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 @@ -36,12 +40,19 @@ // 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 |