Enhance the support for removing global values from the module with JIT lock.
When the machine code was generated the JIT retains a lock to the global values so they don't go out of sync. In the cases of circular references like: int g(); int f() {g();} int g() {f();} we cannot count on the removal order to get rid of the uses. We need to use the replaceAllUsesWith. This is however tricky because the JIT already generated the code for f and g and it doesn't make sense to replace anything with anything else. Thus in order to support that we need to hack one of the JIT callbacks which prevents the replace of values of already emitted code.
This commit is contained in:
parent
bf03ba5040
commit
a08152d576
@ -451,6 +451,7 @@ namespace cling {
|
||||
}
|
||||
|
||||
void DeclReverter::RemoveDeclFromModule(GlobalDecl& GD) const {
|
||||
using namespace llvm;
|
||||
// if it was successfully removed from the AST we have to check whether
|
||||
// code was generated and remove it.
|
||||
|
||||
@ -481,26 +482,46 @@ namespace cling {
|
||||
std::string mangledName;
|
||||
utils::Analyze::maybeMangleDeclName(GD, mangledName);
|
||||
|
||||
llvm::GlobalValue* GV
|
||||
GlobalValue* GV
|
||||
= m_CurTransaction->getModule()->getNamedValue(mangledName);
|
||||
if (GV) { // May be deferred decl and thus 0
|
||||
if (GV && !GV->use_empty()) {
|
||||
GV->removeDeadConstantUsers();
|
||||
if (!GV->use_empty()) {
|
||||
// Assert that if there was a use it is not coming from the explicit
|
||||
// AST node, but from the implicitly generated functions, which ensure
|
||||
// the initialization order semantics. Such functions are:
|
||||
// _GLOBAL__I* and __cxx_global_var_init*
|
||||
//
|
||||
assert(GV->hasOneUse()
|
||||
&& "Must have only one use coming from the static inits");
|
||||
// We can 'afford' to drop all the references because we know that the
|
||||
// static init functions must be called only once, and that was
|
||||
// already done.
|
||||
SmallVector<User*, 4> uses;
|
||||
|
||||
for(llvm::Value::use_iterator I = GV->use_begin(), E = GV->use_end();
|
||||
I != E; ++I) {
|
||||
uses.push_back(*I);
|
||||
}
|
||||
|
||||
for(SmallVector<User*, 4>::iterator I = uses.begin(), E = uses.end();
|
||||
I != E; ++I)
|
||||
if (llvm::Instruction* instr = dyn_cast<llvm::Instruction>(*I)) {
|
||||
llvm::Function* F = instr->getParent()->getParent();
|
||||
if (F->getName().startswith("__cxx_global_var_init"))
|
||||
RemoveStaticInit(*F);
|
||||
}
|
||||
|
||||
//m_EEngine->updateGlobalMapping(GV, 0);
|
||||
//GV->replaceAllUsesWith(llvm::UndefValue::get(GV->getType()));
|
||||
llvm::BasicBlock* BB
|
||||
= cast<llvm::Instruction>(GV->use_back())->getParent();
|
||||
RemoveStaticInit(*BB->getParent());
|
||||
}
|
||||
|
||||
// Cleanup the jit mapping of GV->addr.
|
||||
m_EEngine->updateGlobalMapping(GV, 0);
|
||||
GV->dropAllReferences();
|
||||
if (!GV->use_empty()) {
|
||||
if (Function* F = dyn_cast<Function>(GV)) {
|
||||
Function* dummy = Function::Create(F->getFunctionType(), F->getLinkage());
|
||||
F->replaceAllUsesWith(dummy);
|
||||
}
|
||||
else
|
||||
GV->replaceAllUsesWith(UndefValue::get(GV->getType()));
|
||||
}
|
||||
GV->eraseFromParent();
|
||||
}
|
||||
|
14
test/CodeUnloading/CircularReferences.C
Normal file
14
test/CodeUnloading/CircularReferences.C
Normal file
@ -0,0 +1,14 @@
|
||||
// RUN: cat %s | %cling 2>&1 | FileCheck %s
|
||||
|
||||
.storeState "preUnload"
|
||||
.rawInput 1
|
||||
int g(); int f(int i) { if (i != 1) return g(); return 0; } int g() { return f(1); } int x = f(0);
|
||||
.rawInput 0
|
||||
.U
|
||||
.compareState "preUnload"
|
||||
//CHECK-NOT: Differences
|
||||
float f = 1.1
|
||||
//CHECK: (float) 1.1
|
||||
int g = 42
|
||||
//CHECK: (int) 42
|
||||
.q
|
Loading…
x
Reference in New Issue
Block a user