From ee448630bdf7eb6037fe2c50518d32010c433ca3 Mon Sep 17 00:00:00 2001 From: Reid Spencer Date: Tue, 12 Jul 2005 15:51:55 +0000 Subject: [PATCH] For PR540: This patch completes the changes for making lli thread-safe. Here's the list of changes: * The Support/ThreadSupport* files were removed and replaced with the MutexGuard.h file since all ThreadSupport* declared was a Mutex Guard. The implementation of MutexGuard.h is now based on sys::Mutex which hides its implementation and makes it unnecessary to have the -NoSupport.h and -PThreads.h versions of ThreadSupport. * All places in ExecutionEngine that previously referred to "Mutex" now refer to sys::Mutex * All places in ExecutionEngine that previously referred to "MutexLocker" now refer to MutexGuard (this is frivolous but I believe the technically correct name for such a class is "Guard" not a "Locker"). These changes passed all of llvm-test. All we need now are some test cases that actually use multiple threads. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@22404 91177308-0d34-0410-b5e6-96231b3b80d8 --- autoconf/configure.ac | 4 +- configure | 4 -- .../llvm/ExecutionEngine/ExecutionEngine.h | 60 ++++++++++++----- include/llvm/Support/MutexGuard.h | 47 +++++++------- .../llvm/Support/ThreadSupport-NoSupport.h | 34 ---------- include/llvm/Support/ThreadSupport-PThreads.h | 65 ------------------- include/llvm/Support/ThreadSupport.h.in | 42 ------------ lib/ExecutionEngine/ExecutionEngine.cpp | 17 +++-- lib/ExecutionEngine/JIT/JIT.cpp | 20 ++++-- lib/ExecutionEngine/JIT/JIT.h | 27 ++++++-- lib/ExecutionEngine/JIT/JITEmitter.cpp | 56 +++++++++++----- 11 files changed, 155 insertions(+), 221 deletions(-) delete mode 100644 include/llvm/Support/ThreadSupport-NoSupport.h delete mode 100644 include/llvm/Support/ThreadSupport-PThreads.h delete mode 100644 include/llvm/Support/ThreadSupport.h.in diff --git a/autoconf/configure.ac b/autoconf/configure.ac index 1c3bc1372ce..f20c567f03b 100644 --- a/autoconf/configure.ac +++ b/autoconf/configure.ac @@ -606,12 +606,10 @@ dnl=== dnl===-----------------------------------------------------------------------=== dnl Configure header files -AC_CONFIG_HEADERS(include/llvm/Config/config.h) - +AC_CONFIG_HEADERS([include/llvm/Config/config.h]) AC_CONFIG_HEADERS([include/llvm/Support/DataTypes.h]) AC_CONFIG_HEADERS([include/llvm/ADT/hash_map]) AC_CONFIG_HEADERS([include/llvm/ADT/hash_set]) -AC_CONFIG_HEADERS([include/llvm/Support/ThreadSupport.h]) AC_CONFIG_HEADERS([include/llvm/ADT/iterator]) dnl Configure the makefile's configuration data diff --git a/configure b/configure index b785a780e1a..90fda9a9435 100755 --- a/configure +++ b/configure @@ -30489,15 +30489,12 @@ _ACEOF ac_config_headers="$ac_config_headers include/llvm/Config/config.h" - ac_config_headers="$ac_config_headers include/llvm/Support/DataTypes.h" ac_config_headers="$ac_config_headers include/llvm/ADT/hash_map" ac_config_headers="$ac_config_headers include/llvm/ADT/hash_set" - ac_config_headers="$ac_config_headers include/llvm/Support/ThreadSupport.h" - ac_config_headers="$ac_config_headers include/llvm/ADT/iterator" @@ -31106,7 +31103,6 @@ do "include/llvm/Support/DataTypes.h" ) CONFIG_HEADERS="$CONFIG_HEADERS include/llvm/Support/DataTypes.h" ;; "include/llvm/ADT/hash_map" ) CONFIG_HEADERS="$CONFIG_HEADERS include/llvm/ADT/hash_map" ;; "include/llvm/ADT/hash_set" ) CONFIG_HEADERS="$CONFIG_HEADERS include/llvm/ADT/hash_set" ;; - "include/llvm/Support/ThreadSupport.h" ) CONFIG_HEADERS="$CONFIG_HEADERS include/llvm/Support/ThreadSupport.h" ;; "include/llvm/ADT/iterator" ) CONFIG_HEADERS="$CONFIG_HEADERS include/llvm/ADT/iterator" ;; *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 echo "$as_me: error: invalid argument: $ac_config_target" >&2;} diff --git a/include/llvm/ExecutionEngine/ExecutionEngine.h b/include/llvm/ExecutionEngine/ExecutionEngine.h index 13bc9ccc139..301b6d6eeb8 100644 --- a/include/llvm/ExecutionEngine/ExecutionEngine.h +++ b/include/llvm/ExecutionEngine/ExecutionEngine.h @@ -19,6 +19,7 @@ #include #include #include +#include "llvm/Support/MutexGuard.h" namespace llvm { @@ -33,10 +34,9 @@ class TargetData; class Type; class IntrinsicLowering; -class ExecutionEngine { - Module &CurMod; - const TargetData *TD; +class ExecutionEngineState { +private: /// GlobalAddressMap - A mapping between LLVM global values and their /// actualized version... std::map GlobalAddressMap; @@ -46,6 +46,24 @@ class ExecutionEngine { /// at the address. This map is not computed unless getGlobalValueAtAddress /// is called at some point. std::map GlobalAddressReverseMap; + +public: + std::map& getGlobalAddressMap(const MutexGuard& locked) { + return GlobalAddressMap; + } + + std::map& getGlobalAddressReverseMap(const MutexGuard& locked) { + return GlobalAddressReverseMap; + } +}; + + +class ExecutionEngine { + Module &CurMod; + const TargetData *TD; + + ExecutionEngineState state; + protected: ModuleProvider *MP; @@ -54,6 +72,10 @@ protected: } public: + /// lock - This lock is protects the ExecutionEngine, JIT, JITResolver and JITEmitter classes. + /// It must be held while changing the internal state of any of those classes. + sys::Mutex lock; // Used to make this class and subclasses thread-safe + ExecutionEngine(ModuleProvider *P); ExecutionEngine(Module *M); virtual ~ExecutionEngine(); @@ -81,13 +103,15 @@ public: void addGlobalMapping(const GlobalValue *GV, void *Addr) { - void *&CurVal = GlobalAddressMap[GV]; + MutexGuard locked(lock); + + void *&CurVal = state.getGlobalAddressMap(locked)[GV]; assert((CurVal == 0 || Addr == 0) && "GlobalMapping already established!"); CurVal = Addr; // If we are using the reverse mapping, add it too - if (!GlobalAddressReverseMap.empty()) { - const GlobalValue *&V = GlobalAddressReverseMap[Addr]; + if (!state.getGlobalAddressReverseMap(locked).empty()) { + const GlobalValue *&V = state.getGlobalAddressReverseMap(locked)[Addr]; assert((V == 0 || GV == 0) && "GlobalMapping already established!"); V = GV; } @@ -96,21 +120,25 @@ public: /// clearAllGlobalMappings - Clear all global mappings and start over again /// use in dynamic compilation scenarios when you want to move globals void clearAllGlobalMappings() { - GlobalAddressMap.clear(); - GlobalAddressReverseMap.clear(); + MutexGuard locked(lock); + + state.getGlobalAddressMap(locked).clear(); + state.getGlobalAddressReverseMap(locked).clear(); } /// updateGlobalMapping - Replace an existing mapping for GV with a new /// address. This updates both maps as required. void updateGlobalMapping(const GlobalValue *GV, void *Addr) { - void *&CurVal = GlobalAddressMap[GV]; - if (CurVal && !GlobalAddressReverseMap.empty()) - GlobalAddressReverseMap.erase(CurVal); + MutexGuard locked(lock); + + void *&CurVal = state.getGlobalAddressMap(locked)[GV]; + if (CurVal && !state.getGlobalAddressReverseMap(locked).empty()) + state.getGlobalAddressReverseMap(locked).erase(CurVal); CurVal = Addr; // If we are using the reverse mapping, add it too - if (!GlobalAddressReverseMap.empty()) { - const GlobalValue *&V = GlobalAddressReverseMap[Addr]; + if (!state.getGlobalAddressReverseMap(locked).empty()) { + const GlobalValue *&V = state.getGlobalAddressReverseMap(locked)[Addr]; assert((V == 0 || GV == 0) && "GlobalMapping already established!"); V = GV; } @@ -120,8 +148,10 @@ public: /// global value if it is available, otherwise it returns null. /// void *getPointerToGlobalIfAvailable(const GlobalValue *GV) { - std::map::iterator I = GlobalAddressMap.find(GV); - return I != GlobalAddressMap.end() ? I->second : 0; + MutexGuard locked(lock); + + std::map::iterator I = state.getGlobalAddressMap(locked).find(GV); + return I != state.getGlobalAddressMap(locked).end() ? I->second : 0; } /// getPointerToGlobal - This returns the address of the specified global diff --git a/include/llvm/Support/MutexGuard.h b/include/llvm/Support/MutexGuard.h index bed1a9cec7a..24bbbe1bb31 100644 --- a/include/llvm/Support/MutexGuard.h +++ b/include/llvm/Support/MutexGuard.h @@ -1,4 +1,4 @@ -//===-- Support/ThreadSupport.h - Generic threading support -----*- C++ -*-===// +//===-- Support/MutexGuard.h - Acquire/Release Mutex In Scope ---*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,36 +7,35 @@ // //===----------------------------------------------------------------------===// // -// This file defines platform-agnostic interfaces that can be used to write -// multi-threaded programs. Autoconf is used to chose the correct -// implementation of these interfaces, or default to a non-thread-capable system -// if no matching system support is available. +// This file defines a guard for a block of code that ensures a Mutex is locked +// upon construction and released upon destruction. // //===----------------------------------------------------------------------===// -#ifndef LLVM_SUPPORT_THREADSUPPORT_H -#define LLVM_SUPPORT_THREADSUPPORT_H +#ifndef LLVM_SUPPORT_MUTEXGUARD_H +#define LLVM_SUPPORT_MUTEXGUARD_H -#undef HAVE_PTHREAD_MUTEX_LOCK - -#ifdef HAVE_PTHREAD_MUTEX_LOCK -#include "llvm/Support/ThreadSupport-PThreads.h" -#else -#include "llvm/Support/ThreadSupport-NoSupport.h" -#endif // If no system support is available +#include namespace llvm { - /// MutexLocker - Instances of this class acquire a given Lock when - /// constructed and hold that lock until destruction. - /// - class MutexLocker { - Mutex &M; - MutexLocker(const MutexLocker &); // DO NOT IMPLEMENT - void operator=(const MutexLocker &); // DO NOT IMPLEMENT + /// Instances of this class acquire a given Mutex Lock when constructed and + /// hold that lock until destruction. The intention is to instantiate one of + /// these on the stack at the top of some scope to be assured that C++ + /// destruction of the object will always release the Mutex and thus avoid + /// a host of nasty multi-threading problems in the face of exceptions, etc. + /// @brief Guard a section of code with a Mutex. + class MutexGuard { + sys::Mutex &M; + MutexGuard(const MutexGuard &); // DO NOT IMPLEMENT + void operator=(const MutexGuard &); // DO NOT IMPLEMENT public: - MutexLocker(Mutex &m) : M(m) { M.acquire(); } - ~MutexLocker() { M.release(); } + MutexGuard(sys::Mutex &m) : M(m) { M.acquire(); } + ~MutexGuard() { M.release(); } + /// holds - Returns true if this locker instance holds the specified lock. + /// This is mostly used in assertions to validate that the correct mutex + /// is held. + bool holds(const sys::Mutex& lock) const { return &M == &lock; } }; } -#endif // SUPPORT_THREADSUPPORT_H +#endif // LLVM_SUPPORT_MUTEXGUARD_H diff --git a/include/llvm/Support/ThreadSupport-NoSupport.h b/include/llvm/Support/ThreadSupport-NoSupport.h deleted file mode 100644 index 058f82b41fd..00000000000 --- a/include/llvm/Support/ThreadSupport-NoSupport.h +++ /dev/null @@ -1,34 +0,0 @@ -//===-- llvm/Support/ThreadSupport-NoSupport.h - Generic Impl ---*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines a generic ThreadSupport implementation used when there is -// no supported threading mechanism on the current system. Users should never -// #include this file directly! -// -//===----------------------------------------------------------------------===// - -// Users should never #include this file directly! As such, no include guards -// are needed. - -#ifndef LLVM_SUPPORT_THREADSUPPORT_H -#error "Code should not #include Support/ThreadSupport-NoSupport.h directly!" -#endif - -namespace llvm { - /// Mutex - This class allows user code to protect variables shared between - /// threads. It implements a "recursive" mutex, to simplify user code. - /// - /// Since there is no platform support for _creating threads_, the non-thread - /// implementation of this class is a noop. - /// - struct Mutex { - void acquire () {} - void release () {} - }; -} diff --git a/include/llvm/Support/ThreadSupport-PThreads.h b/include/llvm/Support/ThreadSupport-PThreads.h deleted file mode 100644 index 84c6fac5880..00000000000 --- a/include/llvm/Support/ThreadSupport-PThreads.h +++ /dev/null @@ -1,65 +0,0 @@ -//===-- llvm/Support/ThreadSupport-PThreads.h - PThreads support *- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines pthreads implementations of the generic threading -// mechanisms. Users should never #include this file directly! -// -//===----------------------------------------------------------------------===// - -// Users should never #include this file directly! As such, no include guards -// are needed. - -#ifndef LLVM_SUPPORT_THREADSUPPORT_H -#error "Code should not #include Support/ThreadSupport/PThreads.h directly!" -#endif - -#include - -namespace llvm { - - /// Mutex - This class allows user code to protect variables shared between - /// threads. It implements a "recursive" mutex, to simplify user code. - /// - class Mutex { - pthread_mutex_t mutex; - Mutex(const Mutex &); // DO NOT IMPLEMENT - void operator=(const Mutex &); // DO NOT IMPLEMENT - public: - Mutex() { - // Initialize the mutex as a recursive mutex - pthread_mutexattr_t Attr; - int errorcode = pthread_mutexattr_init(&Attr); - assert(errorcode == 0); - - errorcode = pthread_mutexattr_settype(&Attr, PTHREAD_MUTEX_RECURSIVE); - assert(errorcode == 0); - - errorcode = pthread_mutex_init(&mutex, &Attr); - assert(errorcode == 0); - - errorcode = pthread_mutexattr_destroy(&Attr); - assert(errorcode == 0); - } - - ~Mutex() { - int errorcode = pthread_mutex_destroy(&mutex); - assert(errorcode == 0); - } - - void acquire () { - int errorcode = pthread_mutex_lock(&mutex); - assert(errorcode == 0); - } - - void release () { - int errorcode = pthread_mutex_unlock(&mutex); - assert(errorcode == 0); - } - }; -} // end namespace llvm diff --git a/include/llvm/Support/ThreadSupport.h.in b/include/llvm/Support/ThreadSupport.h.in deleted file mode 100644 index bed1a9cec7a..00000000000 --- a/include/llvm/Support/ThreadSupport.h.in +++ /dev/null @@ -1,42 +0,0 @@ -//===-- Support/ThreadSupport.h - Generic threading support -----*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file was developed by the LLVM research group and is distributed under -// the University of Illinois Open Source License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines platform-agnostic interfaces that can be used to write -// multi-threaded programs. Autoconf is used to chose the correct -// implementation of these interfaces, or default to a non-thread-capable system -// if no matching system support is available. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_SUPPORT_THREADSUPPORT_H -#define LLVM_SUPPORT_THREADSUPPORT_H - -#undef HAVE_PTHREAD_MUTEX_LOCK - -#ifdef HAVE_PTHREAD_MUTEX_LOCK -#include "llvm/Support/ThreadSupport-PThreads.h" -#else -#include "llvm/Support/ThreadSupport-NoSupport.h" -#endif // If no system support is available - -namespace llvm { - /// MutexLocker - Instances of this class acquire a given Lock when - /// constructed and hold that lock until destruction. - /// - class MutexLocker { - Mutex &M; - MutexLocker(const MutexLocker &); // DO NOT IMPLEMENT - void operator=(const MutexLocker &); // DO NOT IMPLEMENT - public: - MutexLocker(Mutex &m) : M(m) { M.acquire(); } - ~MutexLocker() { M.release(); } - }; -} - -#endif // SUPPORT_THREADSUPPORT_H diff --git a/lib/ExecutionEngine/ExecutionEngine.cpp b/lib/ExecutionEngine/ExecutionEngine.cpp index f72ddcbba6e..36f7d2fdfc4 100644 --- a/lib/ExecutionEngine/ExecutionEngine.cpp +++ b/lib/ExecutionEngine/ExecutionEngine.cpp @@ -50,16 +50,18 @@ ExecutionEngine::~ExecutionEngine() { /// at the specified address. /// const GlobalValue *ExecutionEngine::getGlobalValueAtAddress(void *Addr) { + MutexGuard locked(lock); + // If we haven't computed the reverse mapping yet, do so first. - if (GlobalAddressReverseMap.empty()) { + if (state.getGlobalAddressReverseMap(locked).empty()) { for (std::map::iterator I = - GlobalAddressMap.begin(), E = GlobalAddressMap.end(); I != E; ++I) - GlobalAddressReverseMap.insert(std::make_pair(I->second, I->first)); + state.getGlobalAddressMap(locked).begin(), E = state.getGlobalAddressMap(locked).end(); I != E; ++I) + state.getGlobalAddressReverseMap(locked).insert(std::make_pair(I->second, I->first)); } std::map::iterator I = - GlobalAddressReverseMap.find(Addr); - return I != GlobalAddressReverseMap.end() ? I->second : 0; + state.getGlobalAddressReverseMap(locked).find(Addr); + return I != state.getGlobalAddressReverseMap(locked).end() ? I->second : 0; } // CreateArgv - Turn a vector of strings into a nice argv style array of @@ -168,8 +170,9 @@ void *ExecutionEngine::getPointerToGlobal(const GlobalValue *GV) { if (Function *F = const_cast(dyn_cast(GV))) return getPointerToFunction(F); - assert(GlobalAddressMap[GV] && "Global hasn't had an address allocated yet?"); - return GlobalAddressMap[GV]; + MutexGuard locked(lock); + assert(state.getGlobalAddressMap(locked)[GV] && "Global hasn't had an address allocated yet?"); + return state.getGlobalAddressMap(locked)[GV]; } /// FIXME: document diff --git a/lib/ExecutionEngine/JIT/JIT.cpp b/lib/ExecutionEngine/JIT/JIT.cpp index cf4e14481b8..d97f1970d51 100644 --- a/lib/ExecutionEngine/JIT/JIT.cpp +++ b/lib/ExecutionEngine/JIT/JIT.cpp @@ -30,13 +30,15 @@ using namespace llvm; JIT::JIT(ModuleProvider *MP, TargetMachine &tm, TargetJITInfo &tji) - : ExecutionEngine(MP), TM(tm), TJI(tji), PM(MP) { + : ExecutionEngine(MP), TM(tm), TJI(tji), state(MP) { setTargetData(TM.getTargetData()); // Initialize MCE MCE = createEmitter(*this); // Add target data + MutexGuard locked(lock); + FunctionPassManager& PM = state.getPM(locked); PM.add(new TargetData(TM.getTargetData())); // Compile LLVM Code down to machine code in the intermediate representation @@ -216,18 +218,20 @@ GenericValue JIT::runFunction(Function *F, void JIT::runJITOnFunction(Function *F) { static bool isAlreadyCodeGenerating = false; assert(!isAlreadyCodeGenerating && "Error: Recursive compilation detected!"); + + MutexGuard locked(lock); // JIT the function isAlreadyCodeGenerating = true; - PM.run(*F); + state.getPM(locked).run(*F); isAlreadyCodeGenerating = false; // If the function referred to a global variable that had not yet been // emitted, it allocates memory for the global, but doesn't emit it yet. Emit // all of these globals now. - while (!PendingGlobals.empty()) { - const GlobalVariable *GV = PendingGlobals.back(); - PendingGlobals.pop_back(); + while (!state.getPendingGlobals(locked).empty()) { + const GlobalVariable *GV = state.getPendingGlobals(locked).back(); + state.getPendingGlobals(locked).pop_back(); EmitGlobalVariable(GV); } } @@ -236,6 +240,8 @@ void JIT::runJITOnFunction(Function *F) { /// specified function, compiling it if neccesary. /// void *JIT::getPointerToFunction(Function *F) { + MutexGuard locked(lock); + if (void *Addr = getPointerToGlobalIfAvailable(F)) return Addr; // Check if function already code gen'd @@ -270,6 +276,8 @@ void *JIT::getPointerToFunction(Function *F) { /// variable, possibly emitting it to memory if needed. This is used by the /// Emitter. void *JIT::getOrEmitGlobalVariable(const GlobalVariable *GV) { + MutexGuard locked(lock); + void *Ptr = getPointerToGlobalIfAvailable(GV); if (Ptr) return Ptr; @@ -287,7 +295,7 @@ void *JIT::getOrEmitGlobalVariable(const GlobalVariable *GV) { // compilation. uint64_t S = getTargetData().getTypeSize(GV->getType()->getElementType()); Ptr = new char[(size_t)S]; - PendingGlobals.push_back(GV); + state.getPendingGlobals(locked).push_back(GV); } addGlobalMapping(GV, Ptr); return Ptr; diff --git a/lib/ExecutionEngine/JIT/JIT.h b/lib/ExecutionEngine/JIT/JIT.h index 3c14cf71db5..4cce144712f 100644 --- a/lib/ExecutionEngine/JIT/JIT.h +++ b/lib/ExecutionEngine/JIT/JIT.h @@ -27,18 +27,35 @@ class TargetMachine; class TargetJITInfo; class MachineCodeEmitter; -class JIT : public ExecutionEngine { - TargetMachine &TM; // The current target we are compiling to - TargetJITInfo &TJI; // The JITInfo for the target we are compiling to - +class JITState { +private: FunctionPassManager PM; // Passes to compile a function - MachineCodeEmitter *MCE; // MCE object /// PendingGlobals - Global variables which have had memory allocated for them /// while a function was code generated, but which have not been initialized /// yet. std::vector PendingGlobals; +public: + JITState(ModuleProvider *MP) : PM(MP) {} + + FunctionPassManager& getPM(const MutexGuard& locked) { + return PM; + } + + std::vector& getPendingGlobals(const MutexGuard& locked) { + return PendingGlobals; + } +}; + + +class JIT : public ExecutionEngine { + TargetMachine &TM; // The current target we are compiling to + TargetJITInfo &TJI; // The JITInfo for the target we are compiling to + MachineCodeEmitter *MCE; // MCE object + + JITState state; + JIT(ModuleProvider *MP, TargetMachine &tm, TargetJITInfo &tji); public: ~JIT(); diff --git a/lib/ExecutionEngine/JIT/JITEmitter.cpp b/lib/ExecutionEngine/JIT/JITEmitter.cpp index 301f5e77eec..47adee00192 100644 --- a/lib/ExecutionEngine/JIT/JITEmitter.cpp +++ b/lib/ExecutionEngine/JIT/JITEmitter.cpp @@ -120,6 +120,28 @@ void JITMemoryManager::endFunctionBody(unsigned char *FunctionEnd) { // JIT lazy compilation code. // namespace { + class JITResolverState { + private: + /// FunctionToStubMap - Keep track of the stub created for a particular + /// function so that we can reuse them if necessary. + std::map FunctionToStubMap; + + /// StubToFunctionMap - Keep track of the function that each stub + /// corresponds to. + std::map StubToFunctionMap; + + public: + std::map& getFunctionToStubMap(const MutexGuard& locked) { + assert(locked.holds(TheJIT->lock)); + return FunctionToStubMap; + } + + std::map& getStubToFunctionMap(const MutexGuard& locked) { + assert(locked.holds(TheJIT->lock)); + return StubToFunctionMap; + } + }; + /// JITResolver - Keep track of, and resolve, call sites for functions that /// have not yet been compiled. class JITResolver { @@ -130,13 +152,7 @@ namespace { /// rewrite instructions to use. TargetJITInfo::LazyResolverFn LazyResolverFn; - // FunctionToStubMap - Keep track of the stub created for a particular - // function so that we can reuse them if necessary. - std::map FunctionToStubMap; - - // StubToFunctionMap - Keep track of the function that each stub corresponds - // to. - std::map StubToFunctionMap; + JITResolverState state; /// ExternalFnToStubMap - This is the equivalent of FunctionToStubMap for /// external functions. @@ -159,8 +175,9 @@ namespace { /// instruction without the use of a stub, record the location of the use so /// we know which function is being used at the location. void *AddCallbackAtLocation(Function *F, void *Location) { + MutexGuard locked(TheJIT->lock); /// Get the target-specific JIT resolver function. - StubToFunctionMap[Location] = F; + state.getStubToFunctionMap(locked)[Location] = F; return (void*)LazyResolverFn; } @@ -181,8 +198,10 @@ static JITResolver &getJITResolver(MachineCodeEmitter *MCE = 0) { /// getFunctionStub - This returns a pointer to a function stub, creating /// one on demand as needed. void *JITResolver::getFunctionStub(Function *F) { + MutexGuard locked(TheJIT->lock); + // If we already have a stub for this function, recycle it. - void *&Stub = FunctionToStubMap[F]; + void *&Stub = state.getFunctionToStubMap(locked)[F]; if (Stub) return Stub; // Call the lazy resolver function unless we already KNOW it is an external @@ -207,7 +226,7 @@ void *JITResolver::getFunctionStub(Function *F) { // Finally, keep track of the stub-to-Function mapping so that the // JITCompilerFn knows which function to compile! - StubToFunctionMap[Stub] = F; + state.getStubToFunctionMap(locked)[Stub] = F; return Stub; } @@ -231,16 +250,21 @@ void *JITResolver::getExternalFunctionStub(void *FnAddr) { void *JITResolver::JITCompilerFn(void *Stub) { JITResolver &JR = getJITResolver(); + MutexGuard locked(TheJIT->lock); + // The address given to us for the stub may not be exactly right, it might be // a little bit after the stub. As such, use upper_bound to find it. std::map::iterator I = - JR.StubToFunctionMap.upper_bound(Stub); - assert(I != JR.StubToFunctionMap.begin() && "This is not a known stub!"); + JR.state.getStubToFunctionMap(locked).upper_bound(Stub); + assert(I != JR.state.getStubToFunctionMap(locked).begin() && "This is not a known stub!"); Function *F = (--I)->second; - // The target function will rewrite the stub so that the compilation callback - // function is no longer called from this stub. - JR.StubToFunctionMap.erase(I); + // We might like to remove the stub from the StubToFunction map. + // We can't do that! Multiple threads could be stuck, waiting to acquire the + // lock above. As soon as the 1st function finishes compiling the function, + // the next one will be released, and needs to be able to find the function it needs + // to call. + //JR.state.getStubToFunctionMap(locked).erase(I); DEBUG(std::cerr << "JIT: Lazily resolving function '" << F->getName() << "' In stub ptr = " << Stub << " actual ptr = " @@ -249,7 +273,7 @@ void *JITResolver::JITCompilerFn(void *Stub) { void *Result = TheJIT->getPointerToFunction(F); // We don't need to reuse this stub in the future, as F is now compiled. - JR.FunctionToStubMap.erase(F); + JR.state.getFunctionToStubMap(locked).erase(F); // FIXME: We could rewrite all references to this stub if we knew them. return Result;