mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-06-15 05:24:01 +00:00
Fix http://llvm.org/PR4822: allow module deletion after a function has been
compiled. When functions are compiled, they accumulate references in the JITResolver's stub maps. This patch removes those references when the functions are destroyed. It's illegal to destroy a Function when any thread may still try to call its machine code. This patch also updates r83987 to use ValueMap instead of explicit CallbackVHs and fixes a couple "do stuff inside assert()" bugs from r84522. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@84975 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@ -19,6 +19,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "llvm/ADT/SmallVector.h"
|
#include "llvm/ADT/SmallVector.h"
|
||||||
|
#include "llvm/ADT/ValueMap.h"
|
||||||
#include "llvm/Support/ValueHandle.h"
|
#include "llvm/Support/ValueHandle.h"
|
||||||
#include "llvm/System/Mutex.h"
|
#include "llvm/System/Mutex.h"
|
||||||
#include "llvm/Target/TargetMachine.h"
|
#include "llvm/Target/TargetMachine.h"
|
||||||
@ -42,26 +43,23 @@ class Type;
|
|||||||
|
|
||||||
class ExecutionEngineState {
|
class ExecutionEngineState {
|
||||||
public:
|
public:
|
||||||
class MapUpdatingCVH : public CallbackVH {
|
struct AddressMapConfig : public ValueMapConfig<const GlobalValue*> {
|
||||||
ExecutionEngineState &EES;
|
typedef ExecutionEngineState *ExtraData;
|
||||||
|
static sys::Mutex *getMutex(ExecutionEngineState *EES);
|
||||||
public:
|
static void onDelete(ExecutionEngineState *EES, const GlobalValue *Old);
|
||||||
MapUpdatingCVH(ExecutionEngineState &EES, const GlobalValue *GV);
|
static void onRAUW(ExecutionEngineState *, const GlobalValue *,
|
||||||
|
const GlobalValue *);
|
||||||
operator const GlobalValue*() const {
|
|
||||||
return cast<GlobalValue>(getValPtr());
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void deleted();
|
|
||||||
virtual void allUsesReplacedWith(Value *new_value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef ValueMap<const GlobalValue *, void *, AddressMapConfig>
|
||||||
|
GlobalAddressMapTy;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ExecutionEngine &EE;
|
ExecutionEngine &EE;
|
||||||
|
|
||||||
/// GlobalAddressMap - A mapping between LLVM global values and their
|
/// GlobalAddressMap - A mapping between LLVM global values and their
|
||||||
/// actualized version...
|
/// actualized version...
|
||||||
std::map<MapUpdatingCVH, void *> GlobalAddressMap;
|
GlobalAddressMapTy GlobalAddressMap;
|
||||||
|
|
||||||
/// GlobalAddressReverseMap - This is the reverse mapping of GlobalAddressMap,
|
/// GlobalAddressReverseMap - This is the reverse mapping of GlobalAddressMap,
|
||||||
/// used to convert raw addresses into the LLVM global value that is emitted
|
/// used to convert raw addresses into the LLVM global value that is emitted
|
||||||
@ -70,13 +68,9 @@ private:
|
|||||||
std::map<void *, AssertingVH<const GlobalValue> > GlobalAddressReverseMap;
|
std::map<void *, AssertingVH<const GlobalValue> > GlobalAddressReverseMap;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ExecutionEngineState(ExecutionEngine &EE) : EE(EE) {}
|
ExecutionEngineState(ExecutionEngine &EE);
|
||||||
|
|
||||||
MapUpdatingCVH getVH(const GlobalValue *GV) {
|
GlobalAddressMapTy &
|
||||||
return MapUpdatingCVH(*this, GV);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<MapUpdatingCVH, void *> &
|
|
||||||
getGlobalAddressMap(const MutexGuard &) {
|
getGlobalAddressMap(const MutexGuard &) {
|
||||||
return GlobalAddressMap;
|
return GlobalAddressMap;
|
||||||
}
|
}
|
||||||
@ -485,15 +479,8 @@ class EngineBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ExecutionEngine *create();
|
ExecutionEngine *create();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool operator<(const ExecutionEngineState::MapUpdatingCVH& lhs,
|
|
||||||
const ExecutionEngineState::MapUpdatingCVH& rhs) {
|
|
||||||
return static_cast<const GlobalValue*>(lhs) <
|
|
||||||
static_cast<const GlobalValue*>(rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // End llvm namespace
|
} // End llvm namespace
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -117,8 +117,7 @@ Function *ExecutionEngine::FindFunctionNamed(const char *FnName) {
|
|||||||
|
|
||||||
void *ExecutionEngineState::RemoveMapping(
|
void *ExecutionEngineState::RemoveMapping(
|
||||||
const MutexGuard &, const GlobalValue *ToUnmap) {
|
const MutexGuard &, const GlobalValue *ToUnmap) {
|
||||||
std::map<MapUpdatingCVH, void *>::iterator I =
|
GlobalAddressMapTy::iterator I = GlobalAddressMap.find(ToUnmap);
|
||||||
GlobalAddressMap.find(getVH(ToUnmap));
|
|
||||||
void *OldVal;
|
void *OldVal;
|
||||||
if (I == GlobalAddressMap.end())
|
if (I == GlobalAddressMap.end())
|
||||||
OldVal = 0;
|
OldVal = 0;
|
||||||
@ -141,7 +140,7 @@ void ExecutionEngine::addGlobalMapping(const GlobalValue *GV, void *Addr) {
|
|||||||
|
|
||||||
DEBUG(errs() << "JIT: Map \'" << GV->getName()
|
DEBUG(errs() << "JIT: Map \'" << GV->getName()
|
||||||
<< "\' to [" << Addr << "]\n";);
|
<< "\' to [" << Addr << "]\n";);
|
||||||
void *&CurVal = EEState.getGlobalAddressMap(locked)[EEState.getVH(GV)];
|
void *&CurVal = EEState.getGlobalAddressMap(locked)[GV];
|
||||||
assert((CurVal == 0 || Addr == 0) && "GlobalMapping already established!");
|
assert((CurVal == 0 || Addr == 0) && "GlobalMapping already established!");
|
||||||
CurVal = Addr;
|
CurVal = Addr;
|
||||||
|
|
||||||
@ -183,7 +182,7 @@ void ExecutionEngine::clearGlobalMappingsFromModule(Module *M) {
|
|||||||
void *ExecutionEngine::updateGlobalMapping(const GlobalValue *GV, void *Addr) {
|
void *ExecutionEngine::updateGlobalMapping(const GlobalValue *GV, void *Addr) {
|
||||||
MutexGuard locked(lock);
|
MutexGuard locked(lock);
|
||||||
|
|
||||||
std::map<ExecutionEngineState::MapUpdatingCVH, void *> &Map =
|
ExecutionEngineState::GlobalAddressMapTy &Map =
|
||||||
EEState.getGlobalAddressMap(locked);
|
EEState.getGlobalAddressMap(locked);
|
||||||
|
|
||||||
// Deleting from the mapping?
|
// Deleting from the mapping?
|
||||||
@ -191,7 +190,7 @@ void *ExecutionEngine::updateGlobalMapping(const GlobalValue *GV, void *Addr) {
|
|||||||
return EEState.RemoveMapping(locked, GV);
|
return EEState.RemoveMapping(locked, GV);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *&CurVal = Map[EEState.getVH(GV)];
|
void *&CurVal = Map[GV];
|
||||||
void *OldVal = CurVal;
|
void *OldVal = CurVal;
|
||||||
|
|
||||||
if (CurVal && !EEState.getGlobalAddressReverseMap(locked).empty())
|
if (CurVal && !EEState.getGlobalAddressReverseMap(locked).empty())
|
||||||
@ -214,8 +213,8 @@ void *ExecutionEngine::updateGlobalMapping(const GlobalValue *GV, void *Addr) {
|
|||||||
void *ExecutionEngine::getPointerToGlobalIfAvailable(const GlobalValue *GV) {
|
void *ExecutionEngine::getPointerToGlobalIfAvailable(const GlobalValue *GV) {
|
||||||
MutexGuard locked(lock);
|
MutexGuard locked(lock);
|
||||||
|
|
||||||
std::map<ExecutionEngineState::MapUpdatingCVH, void*>::iterator I =
|
ExecutionEngineState::GlobalAddressMapTy::iterator I =
|
||||||
EEState.getGlobalAddressMap(locked).find(EEState.getVH(GV));
|
EEState.getGlobalAddressMap(locked).find(GV);
|
||||||
return I != EEState.getGlobalAddressMap(locked).end() ? I->second : 0;
|
return I != EEState.getGlobalAddressMap(locked).end() ? I->second : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,7 +226,7 @@ const GlobalValue *ExecutionEngine::getGlobalValueAtAddress(void *Addr) {
|
|||||||
|
|
||||||
// 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 (EEState.getGlobalAddressReverseMap(locked).empty()) {
|
if (EEState.getGlobalAddressReverseMap(locked).empty()) {
|
||||||
for (std::map<ExecutionEngineState::MapUpdatingCVH, void *>::iterator
|
for (ExecutionEngineState::GlobalAddressMapTy::iterator
|
||||||
I = EEState.getGlobalAddressMap(locked).begin(),
|
I = EEState.getGlobalAddressMap(locked).begin(),
|
||||||
E = EEState.getGlobalAddressMap(locked).end(); I != E; ++I)
|
E = EEState.getGlobalAddressMap(locked).end(); I != E; ++I)
|
||||||
EEState.getGlobalAddressReverseMap(locked).insert(std::make_pair(I->second,
|
EEState.getGlobalAddressReverseMap(locked).insert(std::make_pair(I->second,
|
||||||
@ -476,7 +475,7 @@ void *ExecutionEngine::getPointerToGlobal(const GlobalValue *GV) {
|
|||||||
return getPointerToFunction(F);
|
return getPointerToFunction(F);
|
||||||
|
|
||||||
MutexGuard locked(lock);
|
MutexGuard locked(lock);
|
||||||
void *p = EEState.getGlobalAddressMap(locked)[EEState.getVH(GV)];
|
void *p = EEState.getGlobalAddressMap(locked)[GV];
|
||||||
if (p)
|
if (p)
|
||||||
return p;
|
return p;
|
||||||
|
|
||||||
@ -486,7 +485,7 @@ void *ExecutionEngine::getPointerToGlobal(const GlobalValue *GV) {
|
|||||||
EmitGlobalVariable(GVar);
|
EmitGlobalVariable(GVar);
|
||||||
else
|
else
|
||||||
llvm_unreachable("Global hasn't had an address allocated yet!");
|
llvm_unreachable("Global hasn't had an address allocated yet!");
|
||||||
return EEState.getGlobalAddressMap(locked)[EEState.getVH(GV)];
|
return EEState.getGlobalAddressMap(locked)[GV];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function converts a Constant* into a GenericValue. The interesting
|
/// This function converts a Constant* into a GenericValue. The interesting
|
||||||
@ -1072,17 +1071,22 @@ void ExecutionEngine::EmitGlobalVariable(const GlobalVariable *GV) {
|
|||||||
++NumGlobals;
|
++NumGlobals;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecutionEngineState::MapUpdatingCVH::MapUpdatingCVH(
|
ExecutionEngineState::ExecutionEngineState(ExecutionEngine &EE)
|
||||||
ExecutionEngineState &EES, const GlobalValue *GV)
|
: EE(EE), GlobalAddressMap(this) {
|
||||||
: CallbackVH(const_cast<GlobalValue*>(GV)), EES(EES) {}
|
|
||||||
|
|
||||||
void ExecutionEngineState::MapUpdatingCVH::deleted() {
|
|
||||||
MutexGuard locked(EES.EE.lock);
|
|
||||||
EES.RemoveMapping(locked, *this); // Destroys *this.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExecutionEngineState::MapUpdatingCVH::allUsesReplacedWith(
|
sys::Mutex *ExecutionEngineState::AddressMapConfig::getMutex(
|
||||||
Value *new_value) {
|
ExecutionEngineState *EES) {
|
||||||
|
return &EES->EE.lock;
|
||||||
|
}
|
||||||
|
void ExecutionEngineState::AddressMapConfig::onDelete(
|
||||||
|
ExecutionEngineState *EES, const GlobalValue *Old) {
|
||||||
|
void *OldVal = EES->GlobalAddressMap.lookup(Old);
|
||||||
|
EES->GlobalAddressReverseMap.erase(OldVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExecutionEngineState::AddressMapConfig::onRAUW(
|
||||||
|
ExecutionEngineState *, const GlobalValue *, const GlobalValue *) {
|
||||||
assert(false && "The ExecutionEngine doesn't know how to handle a"
|
assert(false && "The ExecutionEngine doesn't know how to handle a"
|
||||||
" RAUW on a value it has a global mapping for.");
|
" RAUW on a value it has a global mapping for.");
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#include "llvm/ADT/SmallPtrSet.h"
|
#include "llvm/ADT/SmallPtrSet.h"
|
||||||
#include "llvm/ADT/SmallVector.h"
|
#include "llvm/ADT/SmallVector.h"
|
||||||
#include "llvm/ADT/Statistic.h"
|
#include "llvm/ADT/Statistic.h"
|
||||||
|
#include "llvm/ADT/ValueMap.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
@ -62,12 +63,29 @@ static JIT *TheJIT = 0;
|
|||||||
// JIT lazy compilation code.
|
// JIT lazy compilation code.
|
||||||
//
|
//
|
||||||
namespace {
|
namespace {
|
||||||
|
class JITResolverState;
|
||||||
|
|
||||||
|
template<typename ValueTy>
|
||||||
|
struct NoRAUWValueMapConfig : public ValueMapConfig<ValueTy> {
|
||||||
|
typedef JITResolverState *ExtraData;
|
||||||
|
static void onRAUW(JITResolverState *, Value *Old, Value *New) {
|
||||||
|
assert(false && "The JIT doesn't know how to handle a"
|
||||||
|
" RAUW on a value it has emitted.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CallSiteValueMapConfig : public NoRAUWValueMapConfig<Function*> {
|
||||||
|
typedef JITResolverState *ExtraData;
|
||||||
|
static void onDelete(JITResolverState *JRS, Function *F);
|
||||||
|
};
|
||||||
|
|
||||||
class JITResolverState {
|
class JITResolverState {
|
||||||
public:
|
public:
|
||||||
typedef DenseMap<AssertingVH<Function>, void*> FunctionToStubMapTy;
|
typedef ValueMap<Function*, void*, NoRAUWValueMapConfig<Function*> >
|
||||||
|
FunctionToStubMapTy;
|
||||||
typedef std::map<void*, AssertingVH<Function> > CallSiteToFunctionMapTy;
|
typedef std::map<void*, AssertingVH<Function> > CallSiteToFunctionMapTy;
|
||||||
typedef DenseMap<AssertingVH<Function>, SmallPtrSet<void*, 1> >
|
typedef ValueMap<Function *, SmallPtrSet<void*, 1>,
|
||||||
FunctionToCallSitesMapTy;
|
CallSiteValueMapConfig> 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
|
||||||
@ -84,6 +102,9 @@ namespace {
|
|||||||
GlobalToIndirectSymMapTy GlobalToIndirectSymMap;
|
GlobalToIndirectSymMapTy GlobalToIndirectSymMap;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
JITResolverState() : FunctionToStubMap(this),
|
||||||
|
FunctionToCallSitesMap(this) {}
|
||||||
|
|
||||||
FunctionToStubMapTy& getFunctionToStubMap(const MutexGuard& locked) {
|
FunctionToStubMapTy& getFunctionToStubMap(const MutexGuard& locked) {
|
||||||
assert(locked.holds(TheJIT->lock));
|
assert(locked.holds(TheJIT->lock));
|
||||||
return FunctionToStubMap;
|
return FunctionToStubMap;
|
||||||
@ -111,8 +132,10 @@ namespace {
|
|||||||
void AddCallSite(const MutexGuard &locked, void *CallSite, Function *F) {
|
void AddCallSite(const MutexGuard &locked, void *CallSite, Function *F) {
|
||||||
assert(locked.holds(TheJIT->lock));
|
assert(locked.holds(TheJIT->lock));
|
||||||
|
|
||||||
assert(CallSiteToFunctionMap.insert(std::make_pair(CallSite, F)).second &&
|
bool Inserted = CallSiteToFunctionMap.insert(
|
||||||
"Pair was already in CallSiteToFunctionMap");
|
std::make_pair(CallSite, F)).second;
|
||||||
|
(void)Inserted;
|
||||||
|
assert(Inserted && "Pair was already in CallSiteToFunctionMap");
|
||||||
FunctionToCallSitesMap[F].insert(CallSite);
|
FunctionToCallSitesMap[F].insert(CallSite);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,8 +165,9 @@ namespace {
|
|||||||
FunctionToCallSitesMapTy::iterator F2C_I = FunctionToCallSitesMap.find(F);
|
FunctionToCallSitesMapTy::iterator F2C_I = FunctionToCallSitesMap.find(F);
|
||||||
assert(F2C_I != FunctionToCallSitesMap.end() &&
|
assert(F2C_I != FunctionToCallSitesMap.end() &&
|
||||||
"FunctionToCallSitesMap broken");
|
"FunctionToCallSitesMap broken");
|
||||||
assert(F2C_I->second.erase(Stub) &&
|
bool Erased = F2C_I->second.erase(Stub);
|
||||||
"FunctionToCallSitesMap broken");
|
(void)Erased;
|
||||||
|
assert(Erased && "FunctionToCallSitesMap broken");
|
||||||
if (F2C_I->second.empty())
|
if (F2C_I->second.empty())
|
||||||
FunctionToCallSitesMap.erase(F2C_I);
|
FunctionToCallSitesMap.erase(F2C_I);
|
||||||
|
|
||||||
@ -152,13 +176,17 @@ namespace {
|
|||||||
|
|
||||||
void EraseAllCallSites(const MutexGuard &locked, Function *F) {
|
void EraseAllCallSites(const MutexGuard &locked, Function *F) {
|
||||||
assert(locked.holds(TheJIT->lock));
|
assert(locked.holds(TheJIT->lock));
|
||||||
|
EraseAllCallSitesPrelocked(F);
|
||||||
|
}
|
||||||
|
void EraseAllCallSitesPrelocked(Function *F) {
|
||||||
FunctionToCallSitesMapTy::iterator F2C = FunctionToCallSitesMap.find(F);
|
FunctionToCallSitesMapTy::iterator F2C = FunctionToCallSitesMap.find(F);
|
||||||
if (F2C == FunctionToCallSitesMap.end())
|
if (F2C == FunctionToCallSitesMap.end())
|
||||||
return;
|
return;
|
||||||
for (SmallPtrSet<void*, 1>::const_iterator I = F2C->second.begin(),
|
for (SmallPtrSet<void*, 1>::const_iterator I = F2C->second.begin(),
|
||||||
E = F2C->second.end(); I != E; ++I) {
|
E = F2C->second.end(); I != E; ++I) {
|
||||||
assert(CallSiteToFunctionMap.erase(*I) == 1 &&
|
bool Erased = CallSiteToFunctionMap.erase(*I);
|
||||||
"Missing call site->function mapping");
|
(void)Erased;
|
||||||
|
assert(Erased && "Missing call site->function mapping");
|
||||||
}
|
}
|
||||||
FunctionToCallSitesMap.erase(F2C);
|
FunctionToCallSitesMap.erase(F2C);
|
||||||
}
|
}
|
||||||
@ -245,6 +273,10 @@ namespace {
|
|||||||
|
|
||||||
JITResolver *JITResolver::TheJITResolver = 0;
|
JITResolver *JITResolver::TheJITResolver = 0;
|
||||||
|
|
||||||
|
void CallSiteValueMapConfig::onDelete(JITResolverState *JRS, Function *F) {
|
||||||
|
JRS->EraseAllCallSitesPrelocked(F);
|
||||||
|
}
|
||||||
|
|
||||||
/// getFunctionStubIfAvailable - This returns a pointer to a function stub
|
/// getFunctionStubIfAvailable - This returns a pointer to a function stub
|
||||||
/// if it has already been created.
|
/// if it has already been created.
|
||||||
void *JITResolver::getFunctionStubIfAvailable(Function *F) {
|
void *JITResolver::getFunctionStubIfAvailable(Function *F) {
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "llvm/ADT/OwningPtr.h"
|
#include "llvm/ADT/OwningPtr.h"
|
||||||
|
#include "llvm/Assembly/Parser.h"
|
||||||
#include "llvm/BasicBlock.h"
|
#include "llvm/BasicBlock.h"
|
||||||
#include "llvm/Constant.h"
|
#include "llvm/Constant.h"
|
||||||
#include "llvm/Constants.h"
|
#include "llvm/Constants.h"
|
||||||
@ -22,6 +23,7 @@
|
|||||||
#include "llvm/Module.h"
|
#include "llvm/Module.h"
|
||||||
#include "llvm/ModuleProvider.h"
|
#include "llvm/ModuleProvider.h"
|
||||||
#include "llvm/Support/IRBuilder.h"
|
#include "llvm/Support/IRBuilder.h"
|
||||||
|
#include "llvm/Support/SourceMgr.h"
|
||||||
#include "llvm/Support/TypeBuilder.h"
|
#include "llvm/Support/TypeBuilder.h"
|
||||||
#include "llvm/Target/TargetSelect.h"
|
#include "llvm/Target/TargetSelect.h"
|
||||||
#include "llvm/Type.h"
|
#include "llvm/Type.h"
|
||||||
@ -49,14 +51,25 @@ class JITTest : public testing::Test {
|
|||||||
protected:
|
protected:
|
||||||
virtual void SetUp() {
|
virtual void SetUp() {
|
||||||
M = new Module("<main>", Context);
|
M = new Module("<main>", Context);
|
||||||
|
MP = new ExistingModuleProvider(M);
|
||||||
std::string Error;
|
std::string Error;
|
||||||
TheJIT.reset(EngineBuilder(M).setEngineKind(EngineKind::JIT)
|
TheJIT.reset(EngineBuilder(MP).setEngineKind(EngineKind::JIT)
|
||||||
.setErrorStr(&Error).create());
|
.setErrorStr(&Error).create());
|
||||||
ASSERT_TRUE(TheJIT.get() != NULL) << Error;
|
ASSERT_TRUE(TheJIT.get() != NULL) << Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LoadAssembly(const char *assembly) {
|
||||||
|
SMDiagnostic Error;
|
||||||
|
bool success = NULL != ParseAssemblyString(assembly, M, Error, Context);
|
||||||
|
std::string errMsg;
|
||||||
|
raw_string_ostream os(errMsg);
|
||||||
|
Error.Print("", os);
|
||||||
|
ASSERT_TRUE(success) << os.str();
|
||||||
|
}
|
||||||
|
|
||||||
LLVMContext Context;
|
LLVMContext Context;
|
||||||
Module *M; // Owned by ExecutionEngine.
|
Module *M; // Owned by MP.
|
||||||
|
ModuleProvider *MP; // Owned by ExecutionEngine.
|
||||||
OwningPtr<ExecutionEngine> TheJIT;
|
OwningPtr<ExecutionEngine> TheJIT;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -264,6 +277,20 @@ TEST_F(JITTest, NonLazyLeaksNoStubs) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
TEST_F(JITTest, ModuleDeletion) {
|
||||||
|
LoadAssembly("define void @main() { "
|
||||||
|
" call i32 @computeVal() "
|
||||||
|
" ret void "
|
||||||
|
"} "
|
||||||
|
" "
|
||||||
|
"define internal i32 @computeVal() { "
|
||||||
|
" ret i32 0 "
|
||||||
|
"} ");
|
||||||
|
Function *func = M->getFunction("main");
|
||||||
|
TheJIT->getPointerToFunction(func);
|
||||||
|
TheJIT->deleteModuleProvider(MP);
|
||||||
|
}
|
||||||
|
|
||||||
// This code is copied from JITEventListenerTest, but it only runs once for all
|
// This code is copied from JITEventListenerTest, but it only runs once for all
|
||||||
// the tests in this directory. Everything seems fine, but that's strange
|
// the tests in this directory. Everything seems fine, but that's strange
|
||||||
// behavior.
|
// behavior.
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
LEVEL = ../../..
|
LEVEL = ../../..
|
||||||
TESTNAME = JIT
|
TESTNAME = JIT
|
||||||
LINK_COMPONENTS := core support jit native
|
LINK_COMPONENTS := asmparser core support jit native
|
||||||
|
|
||||||
include $(LEVEL)/Makefile.config
|
include $(LEVEL)/Makefile.config
|
||||||
include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
|
include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
|
||||||
|
Reference in New Issue
Block a user