Clean up the JITResolver stub/callsite<->function maps.

The JITResolver maps Functions to their canonical stubs and all callsites for
lazily-compiled functions to their target Functions. To make Function
destruction work, I'm going to need to remove all callsites on destruction, so
this patch also adds the reverse mapping for that.

There was an incorrect assumption in here that the only stub for a function
would be the one caused by needing to lazily compile it, while x86-64 far calls
and dlsym-stubs could also cause such stubs, but I didn't look for a test case
that the assumption broke.

This also adds DenseMapInfo<AssertingVH> so I can use DenseMaps instead of
std::maps.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@84522 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Jeffrey Yasskin 2009-10-19 18:49:59 +00:00
parent b8f64a72d8
commit ebbcef945d
2 changed files with 126 additions and 45 deletions

View File

@ -238,6 +238,31 @@ template<> struct simplify_type<const AssertingVH<Value> > {
template<> struct simplify_type<AssertingVH<Value> >
: public simplify_type<const AssertingVH<Value> > {};
// Specialize DenseMapInfo to allow AssertingVH to participate in DenseMap.
template<typename T>
struct DenseMapInfo<AssertingVH<T> > {
typedef DenseMapInfo<T*> PointerInfo;
static inline AssertingVH<T> getEmptyKey() {
return AssertingVH<T>(PointerInfo::getEmptyKey());
}
static inline T* getTombstoneKey() {
return AssertingVH<T>(PointerInfo::getTombstoneKey());
}
static unsigned getHashValue(const AssertingVH<T> &Val) {
return PointerInfo::getHashValue(Val);
}
static bool isEqual(const AssertingVH<T> &LHS, const AssertingVH<T> &RHS) {
return LHS == RHS;
}
static bool isPod() {
#ifdef NDEBUG
return true;
#else
return false;
#endif
}
};
/// TrackingVH - This is a value handle that tracks a Value (or Value subclass),
/// even across RAUW operations.
///

View File

@ -63,17 +63,20 @@ static JIT *TheJIT = 0;
namespace {
class JITResolverState {
public:
typedef std::map<AssertingVH<Function>, void*> FunctionToStubMapTy;
typedef std::map<void*, AssertingVH<Function> > StubToFunctionMapTy;
typedef DenseMap<AssertingVH<Function>, void*> FunctionToStubMapTy;
typedef std::map<void*, AssertingVH<Function> > CallSiteToFunctionMapTy;
typedef DenseMap<AssertingVH<Function>, SmallPtrSet<void*, 1> >
FunctionToCallSitesMapTy;
typedef std::map<AssertingVH<GlobalValue>, void*> GlobalToIndirectSymMapTy;
private:
/// FunctionToStubMap - Keep track of the stub created for a particular
/// function so that we can reuse them if necessary.
FunctionToStubMapTy FunctionToStubMap;
/// StubToFunctionMap - Keep track of the function that each stub
/// corresponds to.
StubToFunctionMapTy StubToFunctionMap;
/// CallSiteToFunctionMap - Keep track of the function that each lazy call
/// site corresponds to, and vice versa.
CallSiteToFunctionMapTy CallSiteToFunctionMap;
FunctionToCallSitesMapTy FunctionToCallSitesMap;
/// GlobalToIndirectSymMap - Keep track of the indirect symbol created for a
/// particular GlobalVariable so that we can reuse them if necessary.
@ -85,22 +88,86 @@ namespace {
return FunctionToStubMap;
}
StubToFunctionMapTy& getStubToFunctionMap(const MutexGuard& locked) {
assert(locked.holds(TheJIT->lock));
return StubToFunctionMap;
}
GlobalToIndirectSymMapTy& getGlobalToIndirectSymMap(const MutexGuard& locked) {
assert(locked.holds(TheJIT->lock));
return GlobalToIndirectSymMap;
}
pair<void *, Function *> LookupFunctionFromCallSite(
const MutexGuard &locked, void *CallSite) const {
assert(locked.holds(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.
CallSiteToFunctionMapTy::const_iterator I =
CallSiteToFunctionMap.upper_bound(CallSite);
assert(I != CallSiteToFunctionMap.begin() &&
"This is not a known call site!");
--I;
return *I;
}
void AddCallSite(const MutexGuard &locked, void *CallSite, Function *F) {
assert(locked.holds(TheJIT->lock));
assert(CallSiteToFunctionMap.insert(std::make_pair(CallSite, F)).second &&
"Pair was already in CallSiteToFunctionMap");
FunctionToCallSitesMap[F].insert(CallSite);
}
// Returns the Function of the stub if a stub was erased, or NULL if there
// was no stub. This function uses the call-site->function map to find a
// relevant function, but asserts that only stubs and not other call sites
// will be passed in.
Function *EraseStub(const MutexGuard &locked, void *Stub) {
CallSiteToFunctionMapTy::iterator C2F_I =
CallSiteToFunctionMap.find(Stub);
if (C2F_I == CallSiteToFunctionMap.end()) {
// Not a stub.
return NULL;
}
Function *const F = C2F_I->second;
#ifndef NDEBUG
void *RealStub = FunctionToStubMap.lookup(F);
assert(RealStub == Stub &&
"Call-site that wasn't a stub pass in to EraseStub");
#endif
FunctionToStubMap.erase(F);
CallSiteToFunctionMap.erase(C2F_I);
// Remove the stub from the function->call-sites map, and remove the whole
// entry from the map if that was the last call site.
FunctionToCallSitesMapTy::iterator F2C_I = FunctionToCallSitesMap.find(F);
assert(F2C_I != FunctionToCallSitesMap.end() &&
"FunctionToCallSitesMap broken");
assert(F2C_I->second.erase(Stub) &&
"FunctionToCallSitesMap broken");
if (F2C_I->second.empty())
FunctionToCallSitesMap.erase(F2C_I);
return F;
}
void EraseAllCallSites(const MutexGuard &locked, Function *F) {
assert(locked.holds(TheJIT->lock));
FunctionToCallSitesMapTy::iterator F2C = FunctionToCallSitesMap.find(F);
if (F2C == FunctionToCallSitesMap.end())
return;
for (SmallPtrSet<void*, 1>::const_iterator I = F2C->second.begin(),
E = F2C->second.end(); I != E; ++I) {
assert(CallSiteToFunctionMap.erase(*I) == 1 &&
"Missing call site->function mapping");
}
FunctionToCallSitesMap.erase(F2C);
}
};
/// JITResolver - Keep track of, and resolve, call sites for functions that
/// have not yet been compiled.
class JITResolver {
typedef JITResolverState::FunctionToStubMapTy FunctionToStubMapTy;
typedef JITResolverState::StubToFunctionMapTy StubToFunctionMapTy;
typedef JITResolverState::CallSiteToFunctionMapTy CallSiteToFunctionMapTy;
typedef JITResolverState::GlobalToIndirectSymMapTy GlobalToIndirectSymMapTy;
/// LazyResolverFn - The target lazy resolver function that we actually
@ -154,7 +221,7 @@ namespace {
void *AddCallbackAtLocation(Function *F, void *Location) {
MutexGuard locked(TheJIT->lock);
/// Get the target-specific JIT resolver function.
state.getStubToFunctionMap(locked)[Location] = F;
state.AddCallSite(locked, Location, F);
return (void*)(intptr_t)LazyResolverFn;
}
@ -183,8 +250,7 @@ void *JITResolver::getFunctionStubIfAvailable(Function *F) {
MutexGuard locked(TheJIT->lock);
// If we already have a stub for this function, recycle it.
void *&Stub = state.getFunctionToStubMap(locked)[F];
return Stub;
return state.getFunctionToStubMap(locked).lookup(F);
}
/// getFunctionStub - This returns a pointer to a function stub, creating
@ -230,7 +296,7 @@ void *JITResolver::getFunctionStub(Function *F) {
// Finally, keep track of the stub-to-Function mapping so that the
// JITCompilerFn knows which function to compile!
state.getStubToFunctionMap(locked)[Stub] = F;
state.AddCallSite(locked, Stub, F);
// If we are JIT'ing non-lazily but need to call a function that does not
// exist yet, add it to the JIT's work list so that we can fill in the stub
@ -291,10 +357,11 @@ void JITResolver::getRelocatableGVs(SmallVectorImpl<GlobalValue*> &GVs,
SmallVectorImpl<void*> &Ptrs) {
MutexGuard locked(TheJIT->lock);
FunctionToStubMapTy &FM = state.getFunctionToStubMap(locked);
const FunctionToStubMapTy &FM = state.getFunctionToStubMap(locked);
GlobalToIndirectSymMapTy &GM = state.getGlobalToIndirectSymMap(locked);
for (FunctionToStubMapTy::iterator i = FM.begin(), e = FM.end(); i != e; ++i){
for (FunctionToStubMapTy::const_iterator i = FM.begin(), e = FM.end();
i != e; ++i){
Function *F = i->first;
if (F->isDeclaration() && F->hasExternalLinkage()) {
GVs.push_back(i->first);
@ -311,16 +378,11 @@ void JITResolver::getRelocatableGVs(SmallVectorImpl<GlobalValue*> &GVs,
GlobalValue *JITResolver::invalidateStub(void *Stub) {
MutexGuard locked(TheJIT->lock);
FunctionToStubMapTy &FM = state.getFunctionToStubMap(locked);
StubToFunctionMapTy &SM = state.getStubToFunctionMap(locked);
GlobalToIndirectSymMapTy &GM = state.getGlobalToIndirectSymMap(locked);
// Look up the cheap way first, to see if it's a function stub we are
// invalidating. If so, remove it from both the forward and reverse maps.
if (SM.find(Stub) != SM.end()) {
Function *F = SM[Stub];
SM.erase(Stub);
FM.erase(F);
if (Function *F = state.EraseStub(locked, Stub)) {
return F;
}
@ -361,14 +423,12 @@ void *JITResolver::JITCompilerFn(void *Stub) {
// JIT lock to be unlocked.
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.
StubToFunctionMapTy::iterator I =
JR.state.getStubToFunctionMap(locked).upper_bound(Stub);
assert(I != JR.state.getStubToFunctionMap(locked).begin() &&
"This is not a known stub!");
F = (--I)->second;
ActualPtr = I->first;
// 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.
pair<void*, Function*> I =
JR.state.LookupFunctionFromCallSite(locked, Stub);
F = I.second;
ActualPtr = I.first;
}
// If we have already code generated the function, just return the address.
@ -383,13 +443,6 @@ void *JITResolver::JITCompilerFn(void *Stub) {
+ F->getName() + "' when lazy compiles are disabled!");
}
// 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(errs() << "JIT: Lazily resolving function '" << F->getName()
<< "' In stub ptr = " << Stub << " actual ptr = "
<< ActualPtr << "\n");
@ -397,11 +450,14 @@ void *JITResolver::JITCompilerFn(void *Stub) {
Result = TheJIT->getPointerToFunction(F);
}
// Reacquire the lock to erase the stub in the map.
// Reacquire the lock to update the GOT map.
MutexGuard locked(TheJIT->lock);
// We don't need to reuse this stub in the future, as F is now compiled.
JR.state.getFunctionToStubMap(locked).erase(F);
// We might like to remove the call site from the CallSiteToFunction map, but
// 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.
// FIXME: We could rewrite all references to this stub if we knew them.