Move the GlobalValue revert logic into separate routines. Enhance it to support reverting of the static init functions, too.

This commit is contained in:
Vassil Vassilev 2013-10-24 12:01:30 -05:00 committed by sftnight
parent 25d67e7461
commit c5fa742fc6

View File

@ -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