Move the GlobalValue revert logic into separate routines. Enhance it to support reverting of the static init functions, too.
This commit is contained in:
parent
25d67e7461
commit
c5fa742fc6
@ -12,6 +12,7 @@
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclVisitor.h"
|
||||
#include "clang/AST/DependentDiagnostic.h"
|
||||
#include "clang/AST/GlobalDecl.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Basic/FileManager.h"
|
||||
#include "clang/Sema/Scope.h"
|
||||
@ -21,6 +22,7 @@
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/DerivedTypes.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/Transforms/IPO.h"
|
||||
|
||||
using namespace clang;
|
||||
|
||||
@ -121,6 +123,9 @@ namespace cling {
|
||||
///
|
||||
bool VisitNamespaceDecl(NamespaceDecl* NSD);
|
||||
|
||||
void RemoveDeclFromModule(const GlobalDecl& GD) const;
|
||||
void RemoveStaticInit(llvm::Function& F) const;
|
||||
|
||||
/// @name Helpers
|
||||
/// @{
|
||||
|
||||
@ -267,61 +272,6 @@ namespace cling {
|
||||
|
||||
}
|
||||
|
||||
// if it was successfully removed from the AST we have to check whether
|
||||
// code was generated and remove it.
|
||||
|
||||
// From llvm's mailing list, explanation of the RAUW'd assert:
|
||||
//
|
||||
// The problem isn't with your call to
|
||||
// replaceAllUsesWith per se, the problem is that somebody (I would guess
|
||||
// the JIT?) is holding it in a ValueMap.
|
||||
//
|
||||
// We used to have a problem that some parts of the code would keep a
|
||||
// mapping like so:
|
||||
// std::map<Value *, ...>
|
||||
// while somebody else would modify the Value* without them noticing,
|
||||
// leading to a dangling pointer in the map. To fix that, we invented the
|
||||
// ValueMap which puts a Use that doesn't show up in the use_iterator on
|
||||
// the Value it holds. When the Value is erased or RAUW'd, the ValueMap is
|
||||
// notified and in this case decides that's not okay and terminates the
|
||||
// program.
|
||||
//
|
||||
// Probably what's happened here is that the calling function has had its
|
||||
// code generated by the JIT, but not the callee. Thus the JIT emitted a
|
||||
// call to a generated stub, and will do the codegen of the callee once
|
||||
// that stub is reached. Of course, once the JIT is in this state, it holds
|
||||
// on to the Function with a ValueMap in order to prevent things from
|
||||
// getting out of sync.
|
||||
//
|
||||
if (Successful && m_CurTransaction->getState() == Transaction::kCommitted) {
|
||||
std::string mangledName = ND->getName();
|
||||
|
||||
if (m_Mangler->shouldMangleDeclName(ND)) {
|
||||
mangledName = "";
|
||||
llvm::raw_string_ostream RawStr(mangledName);
|
||||
m_Mangler->mangleName(ND, RawStr);
|
||||
RawStr.flush();
|
||||
}
|
||||
|
||||
llvm::GlobalValue* GV
|
||||
= m_CurTransaction->getModule()->getNamedValue(mangledName);
|
||||
|
||||
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.
|
||||
GV->use_back()->dropAllReferences();
|
||||
}
|
||||
GV->eraseFromParent();
|
||||
}
|
||||
|
||||
return Successful;
|
||||
}
|
||||
|
||||
@ -353,6 +303,9 @@ namespace cling {
|
||||
m_Sema->IdResolver.AddDecl(MostRecentVD);
|
||||
}
|
||||
|
||||
//If the transaction was committed we need to cleanup the execution engine.
|
||||
GlobalDecl GD(VD);
|
||||
RemoveDeclFromModule(GD);
|
||||
return Successful;
|
||||
}
|
||||
|
||||
@ -458,6 +411,10 @@ namespace cling {
|
||||
return false;
|
||||
}
|
||||
|
||||
//If the transaction was committed we need to cleanup the execution engine.
|
||||
GlobalDecl GD(FD);
|
||||
RemoveDeclFromModule(GD);
|
||||
|
||||
return Successful;
|
||||
}
|
||||
|
||||
@ -501,6 +458,87 @@ namespace cling {
|
||||
return Successful;
|
||||
}
|
||||
|
||||
void DeclReverter::RemoveDeclFromModule(const GlobalDecl& GD) const {
|
||||
// if it was successfully removed from the AST we have to check whether
|
||||
// code was generated and remove it.
|
||||
|
||||
// From llvm's mailing list, explanation of the RAUW'd assert:
|
||||
//
|
||||
// The problem isn't with your call to
|
||||
// replaceAllUsesWith per se, the problem is that somebody (I would guess
|
||||
// the JIT?) is holding it in a ValueMap.
|
||||
//
|
||||
// We used to have a problem that some parts of the code would keep a
|
||||
// mapping like so:
|
||||
// std::map<Value *, ...>
|
||||
// while somebody else would modify the Value* without them noticing,
|
||||
// leading to a dangling pointer in the map. To fix that, we invented the
|
||||
// ValueMap which puts a Use that doesn't show up in the use_iterator on
|
||||
// the Value it holds. When the Value is erased or RAUW'd, the ValueMap is
|
||||
// notified and in this case decides that's not okay and terminates the
|
||||
// program.
|
||||
//
|
||||
// Probably what's happened here is that the calling function has had its
|
||||
// code generated by the JIT, but not the callee. Thus the JIT emitted a
|
||||
// call to a generated stub, and will do the codegen of the callee once
|
||||
// that stub is reached. Of course, once the JIT is in this state, it holds
|
||||
// on to the Function with a ValueMap in order to prevent things from
|
||||
// getting out of sync.
|
||||
//
|
||||
if (m_CurTransaction->getState() == Transaction::kCommitted) {
|
||||
std::string mangledName;
|
||||
const NamedDecl* ND = cast<NamedDecl>(GD.getDecl());
|
||||
utils::Analyze::maybeMangleDeclName(ND, mangledName);
|
||||
|
||||
llvm::GlobalValue* GV
|
||||
= m_CurTransaction->getModule()->getNamedValue(mangledName);
|
||||
if (GV) { // May be deferred decl and thus 0
|
||||
if (GV && !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.
|
||||
|
||||
//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());
|
||||
}
|
||||
GV->eraseFromParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeclReverter::RemoveStaticInit(llvm::Function& F) const {
|
||||
// In our very controlled case the parent of the BasicBlock is the
|
||||
// static init llvm::Function.
|
||||
assert(F.getName().startswith("__cxx_global_var_init")
|
||||
&& "Not a static init");
|
||||
assert(F.hasInternalLinkage() && "Not a static init");
|
||||
// The static init functions have the layout:
|
||||
// declare internal void @__cxx_global_var_init1() section "..."
|
||||
//
|
||||
// define internal void @_GLOBAL__I_a2() section "..." {
|
||||
// entry:
|
||||
// call void @__cxx_global_var_init1()
|
||||
// ret void
|
||||
// }
|
||||
//
|
||||
assert(F.hasOneUse() && "Must have only one use");
|
||||
// erase _GLOBAL__I* first
|
||||
llvm::BasicBlock* BB = cast<llvm::Instruction>(F.use_back())->getParent();
|
||||
BB->getParent()->eraseFromParent();
|
||||
F.eraseFromParent();
|
||||
}
|
||||
|
||||
|
||||
// See Sema::PushOnScopeChains
|
||||
bool DeclReverter::isOnScopeChains(NamedDecl* ND) {
|
||||
|
||||
@ -572,6 +610,10 @@ namespace cling {
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup the module from unused global values.
|
||||
//llvm::ModulePass* globalDCE = llvm::createGlobalDCEPass();
|
||||
//globalDCE->runOnModule(*T->getModule());
|
||||
|
||||
return Successful;
|
||||
}
|
||||
} // end namespace cling
|
||||
|
Loading…
x
Reference in New Issue
Block a user