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
This commit is contained in:
Reid Spencer 2005-07-12 15:51:55 +00:00
parent b2164e5cb5
commit ee448630bd
11 changed files with 155 additions and 221 deletions

View File

@ -606,12 +606,10 @@ dnl===
dnl===-----------------------------------------------------------------------=== dnl===-----------------------------------------------------------------------===
dnl Configure header files 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/Support/DataTypes.h])
AC_CONFIG_HEADERS([include/llvm/ADT/hash_map]) AC_CONFIG_HEADERS([include/llvm/ADT/hash_map])
AC_CONFIG_HEADERS([include/llvm/ADT/hash_set]) AC_CONFIG_HEADERS([include/llvm/ADT/hash_set])
AC_CONFIG_HEADERS([include/llvm/Support/ThreadSupport.h])
AC_CONFIG_HEADERS([include/llvm/ADT/iterator]) AC_CONFIG_HEADERS([include/llvm/ADT/iterator])
dnl Configure the makefile's configuration data dnl Configure the makefile's configuration data

4
configure vendored
View File

@ -30489,15 +30489,12 @@ _ACEOF
ac_config_headers="$ac_config_headers include/llvm/Config/config.h" 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/Support/DataTypes.h"
ac_config_headers="$ac_config_headers include/llvm/ADT/hash_map" 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/ADT/hash_set"
ac_config_headers="$ac_config_headers include/llvm/Support/ThreadSupport.h"
ac_config_headers="$ac_config_headers include/llvm/ADT/iterator" 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/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_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/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" ;; "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:$LINENO: error: invalid argument: $ac_config_target" >&5
echo "$as_me: error: invalid argument: $ac_config_target" >&2;} echo "$as_me: error: invalid argument: $ac_config_target" >&2;}

View File

@ -19,6 +19,7 @@
#include <map> #include <map>
#include <cassert> #include <cassert>
#include <string> #include <string>
#include "llvm/Support/MutexGuard.h"
namespace llvm { namespace llvm {
@ -33,10 +34,9 @@ class TargetData;
class Type; class Type;
class IntrinsicLowering; class IntrinsicLowering;
class ExecutionEngine {
Module &CurMod;
const TargetData *TD;
class ExecutionEngineState {
private:
/// GlobalAddressMap - A mapping between LLVM global values and their /// GlobalAddressMap - A mapping between LLVM global values and their
/// actualized version... /// actualized version...
std::map<const GlobalValue*, void *> GlobalAddressMap; std::map<const GlobalValue*, void *> GlobalAddressMap;
@ -46,6 +46,24 @@ class ExecutionEngine {
/// at the address. This map is not computed unless getGlobalValueAtAddress /// at the address. This map is not computed unless getGlobalValueAtAddress
/// is called at some point. /// is called at some point.
std::map<void *, const GlobalValue*> GlobalAddressReverseMap; std::map<void *, const GlobalValue*> GlobalAddressReverseMap;
public:
std::map<const GlobalValue*, void *>& getGlobalAddressMap(const MutexGuard& locked) {
return GlobalAddressMap;
}
std::map<void *, const GlobalValue*>& getGlobalAddressReverseMap(const MutexGuard& locked) {
return GlobalAddressReverseMap;
}
};
class ExecutionEngine {
Module &CurMod;
const TargetData *TD;
ExecutionEngineState state;
protected: protected:
ModuleProvider *MP; ModuleProvider *MP;
@ -54,6 +72,10 @@ protected:
} }
public: 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(ModuleProvider *P);
ExecutionEngine(Module *M); ExecutionEngine(Module *M);
virtual ~ExecutionEngine(); virtual ~ExecutionEngine();
@ -81,13 +103,15 @@ public:
void addGlobalMapping(const GlobalValue *GV, void *Addr) { 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!"); assert((CurVal == 0 || Addr == 0) && "GlobalMapping already established!");
CurVal = Addr; CurVal = Addr;
// If we are using the reverse mapping, add it too // If we are using the reverse mapping, add it too
if (!GlobalAddressReverseMap.empty()) { if (!state.getGlobalAddressReverseMap(locked).empty()) {
const GlobalValue *&V = GlobalAddressReverseMap[Addr]; const GlobalValue *&V = state.getGlobalAddressReverseMap(locked)[Addr];
assert((V == 0 || GV == 0) && "GlobalMapping already established!"); assert((V == 0 || GV == 0) && "GlobalMapping already established!");
V = GV; V = GV;
} }
@ -96,21 +120,25 @@ public:
/// clearAllGlobalMappings - Clear all global mappings and start over again /// clearAllGlobalMappings - Clear all global mappings and start over again
/// use in dynamic compilation scenarios when you want to move globals /// use in dynamic compilation scenarios when you want to move globals
void clearAllGlobalMappings() { void clearAllGlobalMappings() {
GlobalAddressMap.clear(); MutexGuard locked(lock);
GlobalAddressReverseMap.clear();
state.getGlobalAddressMap(locked).clear();
state.getGlobalAddressReverseMap(locked).clear();
} }
/// updateGlobalMapping - Replace an existing mapping for GV with a new /// updateGlobalMapping - Replace an existing mapping for GV with a new
/// address. This updates both maps as required. /// address. This updates both maps as required.
void updateGlobalMapping(const GlobalValue *GV, void *Addr) { void updateGlobalMapping(const GlobalValue *GV, void *Addr) {
void *&CurVal = GlobalAddressMap[GV]; MutexGuard locked(lock);
if (CurVal && !GlobalAddressReverseMap.empty())
GlobalAddressReverseMap.erase(CurVal); void *&CurVal = state.getGlobalAddressMap(locked)[GV];
if (CurVal && !state.getGlobalAddressReverseMap(locked).empty())
state.getGlobalAddressReverseMap(locked).erase(CurVal);
CurVal = Addr; CurVal = Addr;
// If we are using the reverse mapping, add it too // If we are using the reverse mapping, add it too
if (!GlobalAddressReverseMap.empty()) { if (!state.getGlobalAddressReverseMap(locked).empty()) {
const GlobalValue *&V = GlobalAddressReverseMap[Addr]; const GlobalValue *&V = state.getGlobalAddressReverseMap(locked)[Addr];
assert((V == 0 || GV == 0) && "GlobalMapping already established!"); assert((V == 0 || GV == 0) && "GlobalMapping already established!");
V = GV; V = GV;
} }
@ -120,8 +148,10 @@ public:
/// global value if it is available, otherwise it returns null. /// global value if it is available, otherwise it returns null.
/// ///
void *getPointerToGlobalIfAvailable(const GlobalValue *GV) { void *getPointerToGlobalIfAvailable(const GlobalValue *GV) {
std::map<const GlobalValue*, void*>::iterator I = GlobalAddressMap.find(GV); MutexGuard locked(lock);
return I != GlobalAddressMap.end() ? I->second : 0;
std::map<const GlobalValue*, void*>::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 /// getPointerToGlobal - This returns the address of the specified global

View File

@ -1,4 +1,4 @@
//===-- Support/ThreadSupport.h - Generic threading support -----*- C++ -*-===// //===-- Support/MutexGuard.h - Acquire/Release Mutex In Scope ---*- C++ -*-===//
// //
// The LLVM Compiler Infrastructure // The LLVM Compiler Infrastructure
// //
@ -7,36 +7,35 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// //
// This file defines platform-agnostic interfaces that can be used to write // This file defines a guard for a block of code that ensures a Mutex is locked
// multi-threaded programs. Autoconf is used to chose the correct // upon construction and released upon destruction.
// implementation of these interfaces, or default to a non-thread-capable system
// if no matching system support is available.
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_THREADSUPPORT_H #ifndef LLVM_SUPPORT_MUTEXGUARD_H
#define LLVM_SUPPORT_THREADSUPPORT_H #define LLVM_SUPPORT_MUTEXGUARD_H
#undef HAVE_PTHREAD_MUTEX_LOCK #include <llvm/System/Mutex.h>
#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 { namespace llvm {
/// MutexLocker - Instances of this class acquire a given Lock when /// Instances of this class acquire a given Mutex Lock when constructed and
/// constructed and hold that lock until destruction. /// 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++
class MutexLocker { /// destruction of the object will always release the Mutex and thus avoid
Mutex &M; /// a host of nasty multi-threading problems in the face of exceptions, etc.
MutexLocker(const MutexLocker &); // DO NOT IMPLEMENT /// @brief Guard a section of code with a Mutex.
void operator=(const MutexLocker &); // DO NOT IMPLEMENT class MutexGuard {
sys::Mutex &M;
MutexGuard(const MutexGuard &); // DO NOT IMPLEMENT
void operator=(const MutexGuard &); // DO NOT IMPLEMENT
public: public:
MutexLocker(Mutex &m) : M(m) { M.acquire(); } MutexGuard(sys::Mutex &m) : M(m) { M.acquire(); }
~MutexLocker() { M.release(); } ~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

View File

@ -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 () {}
};
}

View File

@ -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 <pthread.h>
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

View File

@ -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

View File

@ -50,16 +50,18 @@ ExecutionEngine::~ExecutionEngine() {
/// at the specified address. /// at the specified address.
/// ///
const GlobalValue *ExecutionEngine::getGlobalValueAtAddress(void *Addr) { const GlobalValue *ExecutionEngine::getGlobalValueAtAddress(void *Addr) {
MutexGuard locked(lock);
// If we haven't computed the reverse mapping yet, do so first. // If we haven't computed the reverse mapping yet, do so first.
if (GlobalAddressReverseMap.empty()) { if (state.getGlobalAddressReverseMap(locked).empty()) {
for (std::map<const GlobalValue*, void *>::iterator I = for (std::map<const GlobalValue*, void *>::iterator I =
GlobalAddressMap.begin(), E = GlobalAddressMap.end(); I != E; ++I) state.getGlobalAddressMap(locked).begin(), E = state.getGlobalAddressMap(locked).end(); I != E; ++I)
GlobalAddressReverseMap.insert(std::make_pair(I->second, I->first)); state.getGlobalAddressReverseMap(locked).insert(std::make_pair(I->second, I->first));
} }
std::map<void *, const GlobalValue*>::iterator I = std::map<void *, const GlobalValue*>::iterator I =
GlobalAddressReverseMap.find(Addr); state.getGlobalAddressReverseMap(locked).find(Addr);
return I != GlobalAddressReverseMap.end() ? I->second : 0; return I != state.getGlobalAddressReverseMap(locked).end() ? I->second : 0;
} }
// CreateArgv - Turn a vector of strings into a nice argv style array of // 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<Function*>(dyn_cast<Function>(GV))) if (Function *F = const_cast<Function*>(dyn_cast<Function>(GV)))
return getPointerToFunction(F); return getPointerToFunction(F);
assert(GlobalAddressMap[GV] && "Global hasn't had an address allocated yet?"); MutexGuard locked(lock);
return GlobalAddressMap[GV]; assert(state.getGlobalAddressMap(locked)[GV] && "Global hasn't had an address allocated yet?");
return state.getGlobalAddressMap(locked)[GV];
} }
/// FIXME: document /// FIXME: document

View File

@ -30,13 +30,15 @@
using namespace llvm; using namespace llvm;
JIT::JIT(ModuleProvider *MP, TargetMachine &tm, TargetJITInfo &tji) 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()); setTargetData(TM.getTargetData());
// Initialize MCE // Initialize MCE
MCE = createEmitter(*this); MCE = createEmitter(*this);
// Add target data // Add target data
MutexGuard locked(lock);
FunctionPassManager& PM = state.getPM(locked);
PM.add(new TargetData(TM.getTargetData())); PM.add(new TargetData(TM.getTargetData()));
// Compile LLVM Code down to machine code in the intermediate representation // 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) { void JIT::runJITOnFunction(Function *F) {
static bool isAlreadyCodeGenerating = false; static bool isAlreadyCodeGenerating = false;
assert(!isAlreadyCodeGenerating && "Error: Recursive compilation detected!"); assert(!isAlreadyCodeGenerating && "Error: Recursive compilation detected!");
MutexGuard locked(lock);
// JIT the function // JIT the function
isAlreadyCodeGenerating = true; isAlreadyCodeGenerating = true;
PM.run(*F); state.getPM(locked).run(*F);
isAlreadyCodeGenerating = false; isAlreadyCodeGenerating = false;
// If the function referred to a global variable that had not yet been // 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 // emitted, it allocates memory for the global, but doesn't emit it yet. Emit
// all of these globals now. // all of these globals now.
while (!PendingGlobals.empty()) { while (!state.getPendingGlobals(locked).empty()) {
const GlobalVariable *GV = PendingGlobals.back(); const GlobalVariable *GV = state.getPendingGlobals(locked).back();
PendingGlobals.pop_back(); state.getPendingGlobals(locked).pop_back();
EmitGlobalVariable(GV); EmitGlobalVariable(GV);
} }
} }
@ -236,6 +240,8 @@ void JIT::runJITOnFunction(Function *F) {
/// specified function, compiling it if neccesary. /// specified function, compiling it if neccesary.
/// ///
void *JIT::getPointerToFunction(Function *F) { void *JIT::getPointerToFunction(Function *F) {
MutexGuard locked(lock);
if (void *Addr = getPointerToGlobalIfAvailable(F)) if (void *Addr = getPointerToGlobalIfAvailable(F))
return Addr; // Check if function already code gen'd 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 /// variable, possibly emitting it to memory if needed. This is used by the
/// Emitter. /// Emitter.
void *JIT::getOrEmitGlobalVariable(const GlobalVariable *GV) { void *JIT::getOrEmitGlobalVariable(const GlobalVariable *GV) {
MutexGuard locked(lock);
void *Ptr = getPointerToGlobalIfAvailable(GV); void *Ptr = getPointerToGlobalIfAvailable(GV);
if (Ptr) return Ptr; if (Ptr) return Ptr;
@ -287,7 +295,7 @@ void *JIT::getOrEmitGlobalVariable(const GlobalVariable *GV) {
// compilation. // compilation.
uint64_t S = getTargetData().getTypeSize(GV->getType()->getElementType()); uint64_t S = getTargetData().getTypeSize(GV->getType()->getElementType());
Ptr = new char[(size_t)S]; Ptr = new char[(size_t)S];
PendingGlobals.push_back(GV); state.getPendingGlobals(locked).push_back(GV);
} }
addGlobalMapping(GV, Ptr); addGlobalMapping(GV, Ptr);
return Ptr; return Ptr;

View File

@ -27,18 +27,35 @@ class TargetMachine;
class TargetJITInfo; class TargetJITInfo;
class MachineCodeEmitter; class MachineCodeEmitter;
class JIT : public ExecutionEngine { class JITState {
TargetMachine &TM; // The current target we are compiling to private:
TargetJITInfo &TJI; // The JITInfo for the target we are compiling to
FunctionPassManager PM; // Passes to compile a function FunctionPassManager PM; // Passes to compile a function
MachineCodeEmitter *MCE; // MCE object
/// PendingGlobals - Global variables which have had memory allocated for them /// PendingGlobals - Global variables which have had memory allocated for them
/// while a function was code generated, but which have not been initialized /// while a function was code generated, but which have not been initialized
/// yet. /// yet.
std::vector<const GlobalVariable*> PendingGlobals; std::vector<const GlobalVariable*> PendingGlobals;
public:
JITState(ModuleProvider *MP) : PM(MP) {}
FunctionPassManager& getPM(const MutexGuard& locked) {
return PM;
}
std::vector<const GlobalVariable*>& 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); JIT(ModuleProvider *MP, TargetMachine &tm, TargetJITInfo &tji);
public: public:
~JIT(); ~JIT();

View File

@ -120,6 +120,28 @@ void JITMemoryManager::endFunctionBody(unsigned char *FunctionEnd) {
// JIT lazy compilation code. // JIT lazy compilation code.
// //
namespace { 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<Function*, void*> FunctionToStubMap;
/// StubToFunctionMap - Keep track of the function that each stub
/// corresponds to.
std::map<void*, Function*> StubToFunctionMap;
public:
std::map<Function*, void*>& getFunctionToStubMap(const MutexGuard& locked) {
assert(locked.holds(TheJIT->lock));
return FunctionToStubMap;
}
std::map<void*, Function*>& getStubToFunctionMap(const MutexGuard& locked) {
assert(locked.holds(TheJIT->lock));
return StubToFunctionMap;
}
};
/// JITResolver - Keep track of, and resolve, call sites for functions that /// JITResolver - Keep track of, and resolve, call sites for functions that
/// have not yet been compiled. /// have not yet been compiled.
class JITResolver { class JITResolver {
@ -130,13 +152,7 @@ namespace {
/// rewrite instructions to use. /// rewrite instructions to use.
TargetJITInfo::LazyResolverFn LazyResolverFn; TargetJITInfo::LazyResolverFn LazyResolverFn;
// FunctionToStubMap - Keep track of the stub created for a particular JITResolverState state;
// function so that we can reuse them if necessary.
std::map<Function*, void*> FunctionToStubMap;
// StubToFunctionMap - Keep track of the function that each stub corresponds
// to.
std::map<void*, Function*> StubToFunctionMap;
/// ExternalFnToStubMap - This is the equivalent of FunctionToStubMap for /// ExternalFnToStubMap - This is the equivalent of FunctionToStubMap for
/// external functions. /// external functions.
@ -159,8 +175,9 @@ namespace {
/// instruction without the use of a stub, record the location of the use so /// instruction without the use of a stub, record the location of the use so
/// we know which function is being used at the location. /// we know which function is being used at the location.
void *AddCallbackAtLocation(Function *F, void *Location) { void *AddCallbackAtLocation(Function *F, void *Location) {
MutexGuard locked(TheJIT->lock);
/// Get the target-specific JIT resolver function. /// Get the target-specific JIT resolver function.
StubToFunctionMap[Location] = F; state.getStubToFunctionMap(locked)[Location] = F;
return (void*)LazyResolverFn; return (void*)LazyResolverFn;
} }
@ -181,8 +198,10 @@ static JITResolver &getJITResolver(MachineCodeEmitter *MCE = 0) {
/// getFunctionStub - This returns a pointer to a function stub, creating /// getFunctionStub - This returns a pointer to a function stub, creating
/// one on demand as needed. /// one on demand as needed.
void *JITResolver::getFunctionStub(Function *F) { void *JITResolver::getFunctionStub(Function *F) {
MutexGuard locked(TheJIT->lock);
// If we already have a stub for this function, recycle it. // 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; if (Stub) return Stub;
// Call the lazy resolver function unless we already KNOW it is an external // 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 // Finally, keep track of the stub-to-Function mapping so that the
// JITCompilerFn knows which function to compile! // JITCompilerFn knows which function to compile!
StubToFunctionMap[Stub] = F; state.getStubToFunctionMap(locked)[Stub] = F;
return Stub; return Stub;
} }
@ -231,16 +250,21 @@ void *JITResolver::getExternalFunctionStub(void *FnAddr) {
void *JITResolver::JITCompilerFn(void *Stub) { void *JITResolver::JITCompilerFn(void *Stub) {
JITResolver &JR = getJITResolver(); JITResolver &JR = getJITResolver();
MutexGuard locked(TheJIT->lock);
// The address given to us for the stub may not be exactly right, it might be // 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. // a little bit after the stub. As such, use upper_bound to find it.
std::map<void*, Function*>::iterator I = std::map<void*, Function*>::iterator I =
JR.StubToFunctionMap.upper_bound(Stub); JR.state.getStubToFunctionMap(locked).upper_bound(Stub);
assert(I != JR.StubToFunctionMap.begin() && "This is not a known stub!"); assert(I != JR.state.getStubToFunctionMap(locked).begin() && "This is not a known stub!");
Function *F = (--I)->second; Function *F = (--I)->second;
// The target function will rewrite the stub so that the compilation callback // We might like to remove the stub from the StubToFunction map.
// function is no longer called from this stub. // We can't do that! Multiple threads could be stuck, waiting to acquire the
JR.StubToFunctionMap.erase(I); // 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() DEBUG(std::cerr << "JIT: Lazily resolving function '" << F->getName()
<< "' In stub ptr = " << Stub << " actual ptr = " << "' In stub ptr = " << Stub << " actual ptr = "
@ -249,7 +273,7 @@ void *JITResolver::JITCompilerFn(void *Stub) {
void *Result = TheJIT->getPointerToFunction(F); void *Result = TheJIT->getPointerToFunction(F);
// We don't need to reuse this stub in the future, as F is now compiled. // 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. // FIXME: We could rewrite all references to this stub if we knew them.
return Result; return Result;