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