Subzero: Second attempt at fixing MacOS 10.6 build.

Manages thread_local pointer fields through a set of macros.  If ICE_THREAD_LOCAL_HACK is defined, the thread_local definitions and accesses are defined in terms of pthread operations.  This assumes that the underlying std::thread library is based on pthread.

BUG= none
R=jfb@chromium.org, jvoung@chromium.org

Review URL: https://codereview.chromium.org/872933002
diff --git a/src/IceCfg.cpp b/src/IceCfg.cpp
index a56ae2e..c7be511 100644
--- a/src/IceCfg.cpp
+++ b/src/IceCfg.cpp
@@ -25,7 +25,7 @@
 
 namespace Ice {
 
-ICE_ATTRIBUTE_TLS const Cfg *Cfg::CurrentCfg = nullptr;
+ICE_TLS_DEFINE_FIELD(const Cfg *, Cfg, CurrentCfg);
 
 ArenaAllocator<> *getCurrentCfgAllocator() {
   return Cfg::getCurrentCfgAllocator();
diff --git a/src/IceCfg.h b/src/IceCfg.h
index b45a95be..472ba79 100644
--- a/src/IceCfg.h
+++ b/src/IceCfg.h
@@ -37,15 +37,15 @@
   // destruction requirements more explicit.
   static Cfg *create(GlobalContext *Ctx) {
     Cfg *Func = new Cfg(Ctx);
-    CurrentCfg = Func;
+    ICE_TLS_SET_FIELD(CurrentCfg, Func);
     return Func;
   }
   // Gets a pointer to the current thread's Cfg.
-  static const Cfg *getCurrentCfg() { return CurrentCfg; }
+  static const Cfg *getCurrentCfg() { return ICE_TLS_GET_FIELD(CurrentCfg); }
   // Gets a pointer to the current thread's Cfg's allocator.
   static ArenaAllocator<> *getCurrentCfgAllocator() {
-    assert(CurrentCfg);
-    return CurrentCfg->Allocator.get();
+    assert(ICE_TLS_GET_FIELD(CurrentCfg));
+    return ICE_TLS_GET_FIELD(CurrentCfg)->Allocator.get();
   }
 
   GlobalContext *getContext() const { return Ctx; }
@@ -213,7 +213,10 @@
   // Maintain a pointer in TLS to the current Cfg being translated.
   // This is primarily for accessing its allocator statelessly, but
   // other uses are possible.
-  ICE_ATTRIBUTE_TLS static const Cfg *CurrentCfg;
+  ICE_TLS_DECLARE_FIELD(const Cfg *, CurrentCfg);
+
+public:
+  static void TlsInit() { ICE_TLS_INIT_FIELD(CurrentCfg); }
 };
 
 } // end of namespace Ice
diff --git a/src/IceDefs.h b/src/IceDefs.h
index 66a500f..9add7ba 100644
--- a/src/IceDefs.h
+++ b/src/IceDefs.h
@@ -38,14 +38,7 @@
 #include "llvm/Support/ELF.h"
 #include "llvm/Support/raw_ostream.h"
 
-// TODO(stichnot): Define ICE_ATTRIBUTE_TLS as thread_local after all
-// compilers support that C++11 keyword.  In particular, MacOS 10.6
-// does not support it.
-#if defined (_MSC_VER)
-#define ICE_ATTRIBUTE_TLS __declspec(thread)
-#else // !_MSC_VER
-#define ICE_ATTRIBUTE_TLS __thread
-#endif // !_MSC_VER
+#include "IceTLS.h"
 
 namespace Ice {
 
diff --git a/src/IceGlobalContext.cpp b/src/IceGlobalContext.cpp
index 9e1b72c..d5766d4 100644
--- a/src/IceGlobalContext.cpp
+++ b/src/IceGlobalContext.cpp
@@ -132,11 +132,18 @@
     : StrDump(OsDump), StrEmit(OsEmit), VMask(Mask),
       ConstPool(new ConstantPool()), Arch(Arch), Opt(Opt),
       TestPrefix(TestPrefix), Flags(Flags), RNG(""), ObjectWriter() {
+  // Make sure thread_local fields are properly initialized before any
+  // accesses are made.  Do this here instead of at the start of
+  // main() so that all clients (e.g. unit tests) can benefit for
+  // free.
+  GlobalContext::TlsInit();
+  Cfg::TlsInit();
+
   // Create a new ThreadContext for the current thread.  No need to
   // lock AllThreadContexts at this point since no other threads have
   // access yet to this GlobalContext object.
   AllThreadContexts.push_back(new ThreadContext());
-  TLS = AllThreadContexts.back();
+  ICE_TLS_SET_FIELD(TLS, AllThreadContexts.back());
   // Pre-register built-in stack names.
   if (ALLOW_DUMP) {
     // TODO(stichnot): There needs to be a strong relationship between
@@ -495,7 +502,7 @@
   if (Final) {
     getStatsCumulative()->dump(Name, getStrDump());
   } else {
-    TLS->StatsFunction.dump(Name, getStrDump());
+    ICE_TLS_GET_FIELD(TLS)->StatsFunction.dump(Name, getStrDump());
     getStatsCumulative()->dump("_TOTAL_", getStrDump());
   }
 }
@@ -518,6 +525,6 @@
   }
 }
 
-ICE_ATTRIBUTE_TLS GlobalContext::ThreadContext *GlobalContext::TLS;
+ICE_TLS_DEFINE_FIELD(GlobalContext::ThreadContext *, GlobalContext, TLS);
 
 } // end of namespace Ice
diff --git a/src/IceGlobalContext.h b/src/IceGlobalContext.h
index 1f0a600..35d5493 100644
--- a/src/IceGlobalContext.h
+++ b/src/IceGlobalContext.h
@@ -179,37 +179,37 @@
   // Reset stats at the beginning of a function.
   void resetStats() {
     if (ALLOW_DUMP)
-      TLS->StatsFunction.reset();
+      ICE_TLS_GET_FIELD(TLS)->StatsFunction.reset();
   }
   void dumpStats(const IceString &Name, bool Final = false);
   void statsUpdateEmitted(uint32_t InstCount) {
     if (!ALLOW_DUMP || !getFlags().DumpStats)
       return;
-    TLS->StatsFunction.updateEmitted(InstCount);
+    ICE_TLS_GET_FIELD(TLS)->StatsFunction.updateEmitted(InstCount);
     getStatsCumulative()->updateEmitted(InstCount);
   }
   void statsUpdateRegistersSaved(uint32_t Num) {
     if (!ALLOW_DUMP || !getFlags().DumpStats)
       return;
-    TLS->StatsFunction.updateRegistersSaved(Num);
+    ICE_TLS_GET_FIELD(TLS)->StatsFunction.updateRegistersSaved(Num);
     getStatsCumulative()->updateRegistersSaved(Num);
   }
   void statsUpdateFrameBytes(uint32_t Bytes) {
     if (!ALLOW_DUMP || !getFlags().DumpStats)
       return;
-    TLS->StatsFunction.updateFrameBytes(Bytes);
+    ICE_TLS_GET_FIELD(TLS)->StatsFunction.updateFrameBytes(Bytes);
     getStatsCumulative()->updateFrameBytes(Bytes);
   }
   void statsUpdateSpills() {
     if (!ALLOW_DUMP || !getFlags().DumpStats)
       return;
-    TLS->StatsFunction.updateSpills();
+    ICE_TLS_GET_FIELD(TLS)->StatsFunction.updateSpills();
     getStatsCumulative()->updateSpills();
   }
   void statsUpdateFills() {
     if (!ALLOW_DUMP || !getFlags().DumpStats)
       return;
-    TLS->StatsFunction.updateFills();
+    ICE_TLS_GET_FIELD(TLS)->StatsFunction.updateFills();
     getStatsCumulative()->updateFills();
   }
 
@@ -274,11 +274,14 @@
   std::vector<ThreadContext *> AllThreadContexts;
   // Each thread has its own TLS pointer which is also held in
   // AllThreadContexts.
-  ICE_ATTRIBUTE_TLS static ThreadContext *TLS;
+  ICE_TLS_DECLARE_FIELD(ThreadContext *, TLS);
 
   // Private helpers for mangleName()
   typedef llvm::SmallVector<char, 32> ManglerVector;
   void incrementSubstitutions(ManglerVector &OldName) const;
+
+public:
+  static void TlsInit() { ICE_TLS_INIT_FIELD(TLS); }
 };
 
 // Helper class to push and pop a timer marker.  The constructor
diff --git a/src/IceTLS.h b/src/IceTLS.h
new file mode 100644
index 0000000..3fba803
--- /dev/null
+++ b/src/IceTLS.h
@@ -0,0 +1,100 @@
+//===- subzero/src/IceTLS.h - thread_local workaround -----------*- C++ -*-===//
+//
+//                        The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines macros for working around the lack of support for
+// thread_local in MacOS 10.6.  It assumes std::thread is written in
+// terms of pthread.  Define ICE_THREAD_LOCAL_HACK to enable the
+// pthread workarounds.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUBZERO_SRC_ICETLS_H
+#define SUBZERO_SRC_ICETLS_H
+
+#if defined(_MSC_VER)
+#define ICE_ATTRIBUTE_TLS __declspec(thread)
+#else // !_MSC_VER
+#define ICE_ATTRIBUTE_TLS thread_local
+#endif // !_MSC_VER
+
+// Defines 4 macros for unifying thread_local and pthread:
+//
+// ICE_TLS_DECLARE_FIELD(Type, FieldName): Declare a static
+// thread_local field inside the current class definition.  "Type"
+// needs to be a pointer type, such as int* or class Foo*.
+//
+// ICE_TLS_DEFINE_FIELD(Type, ClassName, FieldName): Define a static
+// thread_local field outside of its class definition.  The field will
+// ultimately be initialized to nullptr.
+//
+// ICE_TLS_INIT_FIELD(FieldName): Ensure the thread_local field is
+// properly initialized.  This is intended to be called from within a
+// static method of the field's class after main() starts (to ensure
+// that the pthread library is fully initialized) but before any uses
+// of ICE_TLS_GET_FIELD or ICE_TLS_SET_FIELD.
+//
+// ICE_TLS_GET_FIELD(Type, FieldName): Read the value of the static
+// thread_local field.  Must be done within the context of its class.
+//
+// ICE_TLS_SET_FIELD(FieldName, Value): Write a value into the static
+// thread_local field.  Must be done within the context of its class.
+
+// TODO(stichnot): Limit this define to only the platforms that
+// absolutely require it.  And ideally, eventually remove this hack
+// altogether.
+#define ICE_THREAD_LOCAL_HACK
+#ifdef ICE_THREAD_LOCAL_HACK
+
+// For a static thread_local field F of a class C, instead of
+// declaring and defining C::F, we create two static fields:
+//   static pthread_key_t F__key;
+//   static int F__initStatus;
+//
+// The F__initStatus field is used to hold the result of the
+// pthread_key_create() call, where a zero value indicates success,
+// and a nonzero value indicates failure or that ICE_TLS_INIT_FIELD()
+// was never called.
+// The F__key field is used as the argument to
+// pthread_getspecific() and pthread_setspecific().
+
+#include <pthread.h>
+
+#define ICE_TLS_DECLARE_FIELD(Type, FieldName)                                 \
+  typedef Type FieldName##__type;                                              \
+  static pthread_key_t FieldName##__key;                                       \
+  static int FieldName##__initStatus
+#define ICE_TLS_DEFINE_FIELD(Type, ClassName, FieldName)                       \
+  pthread_key_t ClassName::FieldName##__key;                                   \
+  int ClassName::FieldName##__initStatus = 1;
+#define ICE_TLS_INIT_FIELD(FieldName)                                          \
+  if (FieldName##__initStatus) {                                               \
+    FieldName##__initStatus = pthread_key_create(&FieldName##__key, nullptr);  \
+    if (FieldName##__initStatus)                                               \
+      llvm::report_fatal_error("Failed to create pthread key");                \
+  }
+#define ICE_TLS_GET_FIELD(FieldName)                                           \
+  (assert(FieldName##__initStatus == 0),                                       \
+   static_cast<FieldName##__type>(pthread_getspecific(FieldName##__key)))
+#define ICE_TLS_SET_FIELD(FieldName, Value)                                    \
+  (assert(FieldName##__initStatus == 0),                                       \
+   pthread_setspecific(FieldName##__key, (Value)))
+
+#else // !ICE_THREAD_LOCAL_HACK
+
+#define ICE_TLS_DECLARE_FIELD(Type, FieldName)                                 \
+  static ICE_ATTRIBUTE_TLS Type FieldName
+#define ICE_TLS_DEFINE_FIELD(Type, ClassName, FieldName)                       \
+  ICE_ATTRIBUTE_TLS Type ClassName::FieldName = nullptr
+#define ICE_TLS_INIT_FIELD(FieldName)
+#define ICE_TLS_GET_FIELD(FieldName) (FieldName)
+#define ICE_TLS_SET_FIELD(FieldName, Value) (FieldName = (Value))
+
+#endif // !ICE_THREAD_LOCAL_HACK
+
+#endif // SUBZERO_SRC_ICETLS_H