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> > template<> struct simplify_type<AssertingVH<Value> >
: public simplify_type<const 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), /// TrackingVH - This is a value handle that tracks a Value (or Value subclass),
/// even across RAUW operations. /// even across RAUW operations.
/// ///

View File

@ -63,17 +63,20 @@ static JIT *TheJIT = 0;
namespace { namespace {
class JITResolverState { class JITResolverState {
public: public:
typedef std::map<AssertingVH<Function>, void*> FunctionToStubMapTy; typedef DenseMap<AssertingVH<Function>, void*> FunctionToStubMapTy;
typedef std::map<void*, AssertingVH<Function> > StubToFunctionMapTy; typedef std::map<void*, AssertingVH<Function> > CallSiteToFunctionMapTy;
typedef DenseMap<AssertingVH<Function>, SmallPtrSet<void*, 1> >
FunctionToCallSitesMapTy;
typedef std::map<AssertingVH<GlobalValue>, void*> GlobalToIndirectSymMapTy; typedef std::map<AssertingVH<GlobalValue>, void*> GlobalToIndirectSymMapTy;
private: private:
/// FunctionToStubMap - Keep track of the stub created for a particular /// FunctionToStubMap - Keep track of the stub created for a particular
/// function so that we can reuse them if necessary. /// function so that we can reuse them if necessary.
FunctionToStubMapTy FunctionToStubMap; FunctionToStubMapTy FunctionToStubMap;
/// StubToFunctionMap - Keep track of the function that each stub /// CallSiteToFunctionMap - Keep track of the function that each lazy call
/// corresponds to. /// site corresponds to, and vice versa.
StubToFunctionMapTy StubToFunctionMap; CallSiteToFunctionMapTy CallSiteToFunctionMap;
FunctionToCallSitesMapTy FunctionToCallSitesMap;
/// GlobalToIndirectSymMap - Keep track of the indirect symbol created for a /// GlobalToIndirectSymMap - Keep track of the indirect symbol created for a
/// particular GlobalVariable so that we can reuse them if necessary. /// particular GlobalVariable so that we can reuse them if necessary.
@ -85,22 +88,86 @@ namespace {
return FunctionToStubMap; return FunctionToStubMap;
} }
StubToFunctionMapTy& getStubToFunctionMap(const MutexGuard& locked) {
assert(locked.holds(TheJIT->lock));
return StubToFunctionMap;
}
GlobalToIndirectSymMapTy& getGlobalToIndirectSymMap(const MutexGuard& locked) { GlobalToIndirectSymMapTy& getGlobalToIndirectSymMap(const MutexGuard& locked) {
assert(locked.holds(TheJIT->lock)); assert(locked.holds(TheJIT->lock));
return GlobalToIndirectSymMap; 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 /// 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 {
typedef JITResolverState::FunctionToStubMapTy FunctionToStubMapTy; typedef JITResolverState::FunctionToStubMapTy FunctionToStubMapTy;
typedef JITResolverState::StubToFunctionMapTy StubToFunctionMapTy; typedef JITResolverState::CallSiteToFunctionMapTy CallSiteToFunctionMapTy;
typedef JITResolverState::GlobalToIndirectSymMapTy GlobalToIndirectSymMapTy; typedef JITResolverState::GlobalToIndirectSymMapTy GlobalToIndirectSymMapTy;
/// LazyResolverFn - The target lazy resolver function that we actually /// LazyResolverFn - The target lazy resolver function that we actually
@ -154,7 +221,7 @@ namespace {
void *AddCallbackAtLocation(Function *F, void *Location) { void *AddCallbackAtLocation(Function *F, void *Location) {
MutexGuard locked(TheJIT->lock); MutexGuard locked(TheJIT->lock);
/// Get the target-specific JIT resolver function. /// Get the target-specific JIT resolver function.
state.getStubToFunctionMap(locked)[Location] = F; state.AddCallSite(locked, Location, F);
return (void*)(intptr_t)LazyResolverFn; return (void*)(intptr_t)LazyResolverFn;
} }
@ -183,8 +250,7 @@ void *JITResolver::getFunctionStubIfAvailable(Function *F) {
MutexGuard locked(TheJIT->lock); 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 = state.getFunctionToStubMap(locked)[F]; return state.getFunctionToStubMap(locked).lookup(F);
return Stub;
} }
/// getFunctionStub - This returns a pointer to a function stub, creating /// 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 // Finally, keep track of the stub-to-Function mapping so that the
// JITCompilerFn knows which function to compile! // 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 // 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 // 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) { SmallVectorImpl<void*> &Ptrs) {
MutexGuard locked(TheJIT->lock); MutexGuard locked(TheJIT->lock);
FunctionToStubMapTy &FM = state.getFunctionToStubMap(locked); const FunctionToStubMapTy &FM = state.getFunctionToStubMap(locked);
GlobalToIndirectSymMapTy &GM = state.getGlobalToIndirectSymMap(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; Function *F = i->first;
if (F->isDeclaration() && F->hasExternalLinkage()) { if (F->isDeclaration() && F->hasExternalLinkage()) {
GVs.push_back(i->first); GVs.push_back(i->first);
@ -311,16 +378,11 @@ void JITResolver::getRelocatableGVs(SmallVectorImpl<GlobalValue*> &GVs,
GlobalValue *JITResolver::invalidateStub(void *Stub) { GlobalValue *JITResolver::invalidateStub(void *Stub) {
MutexGuard locked(TheJIT->lock); MutexGuard locked(TheJIT->lock);
FunctionToStubMapTy &FM = state.getFunctionToStubMap(locked);
StubToFunctionMapTy &SM = state.getStubToFunctionMap(locked);
GlobalToIndirectSymMapTy &GM = state.getGlobalToIndirectSymMap(locked); GlobalToIndirectSymMapTy &GM = state.getGlobalToIndirectSymMap(locked);
// Look up the cheap way first, to see if it's a function stub we are // 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. // invalidating. If so, remove it from both the forward and reverse maps.
if (SM.find(Stub) != SM.end()) { if (Function *F = state.EraseStub(locked, Stub)) {
Function *F = SM[Stub];
SM.erase(Stub);
FM.erase(F);
return F; return F;
} }
@ -361,14 +423,12 @@ void *JITResolver::JITCompilerFn(void *Stub) {
// JIT lock to be unlocked. // JIT lock to be unlocked.
MutexGuard locked(TheJIT->lock); 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
// a little bit after the stub. As such, use upper_bound to find it. // be a little bit after the stub. As such, use upper_bound to find it.
StubToFunctionMapTy::iterator I = pair<void*, Function*> I =
JR.state.getStubToFunctionMap(locked).upper_bound(Stub); JR.state.LookupFunctionFromCallSite(locked, Stub);
assert(I != JR.state.getStubToFunctionMap(locked).begin() && F = I.second;
"This is not a known stub!"); ActualPtr = I.first;
F = (--I)->second;
ActualPtr = I->first;
} }
// If we have already code generated the function, just return the address. // 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!"); + 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() DEBUG(errs() << "JIT: Lazily resolving function '" << F->getName()
<< "' In stub ptr = " << Stub << " actual ptr = " << "' In stub ptr = " << Stub << " actual ptr = "
<< ActualPtr << "\n"); << ActualPtr << "\n");
@ -397,11 +450,14 @@ void *JITResolver::JITCompilerFn(void *Stub) {
Result = TheJIT->getPointerToFunction(F); 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); MutexGuard locked(TheJIT->lock);
// We don't need to reuse this stub in the future, as F is now compiled. // We might like to remove the call site from the CallSiteToFunction map, but
JR.state.getFunctionToStubMap(locked).erase(F); // 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. // FIXME: We could rewrite all references to this stub if we knew them.