Skip to content

Commit

Permalink
Mark implicit members coming from a PCH as used.
Browse files Browse the repository at this point in the history
Implicitly generated members from a CXXRecordDecls coming from a PCH should not be
unloaded. CodeGen 'records' that it once emitted the implicit members and if they
get unloaded it would be out of sync. The key difference is the information about
all the implicitly generated members (trivial ctors/dtors, etc) comes from an
ASTMutationListener and they are not part of the transaction. We still do not
record it as a part of the transaction, we just mark them as used so that they
don't get unloaded from the llvm::Module.

Once the list of llvm.used decls is emitted we need to clear it, otherwise we
end up emitting one and the same thing over and over.

Fixes ROOT-6722
Tests should be coming shortly.
  • Loading branch information
vgvassilev committed Sep 29, 2014
1 parent 154e43c commit 2abc4e8
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 4 deletions.
1 change: 1 addition & 0 deletions interpreter/cling/lib/Interpreter/CIFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,7 @@ namespace cling {
stateCollector = new DeclCollector();
// Set up the ASTConsumers
CI->setASTConsumer(stateCollector);
CI->getASTContext().setASTMutationListener(stateCollector);

// Set up Sema
CodeCompleteConsumer* CCC = 0;
Expand Down
16 changes: 16 additions & 0 deletions interpreter/cling/lib/Interpreter/DeclCollector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,25 @@ namespace cling {
return D->isFromASTFile();
}

bool DeclCollector::comesFromASTReader(const Decl* D) const {
// The operation is const but clang::DeclGroupRef doesn't allow us to
// express it.
return comesFromASTReader(DeclGroupRef(const_cast<Decl*>(D)));
}

// pin the vtable here.
DeclCollector::~DeclCollector() { }

void DeclCollector::AddedCXXImplicitMember(const CXXRecordDecl *RD,
const Decl *D) {
assert(D->isImplicit());
// We need to mark the decls coming from the modules
if (comesFromASTReader(RD) || comesFromASTReader(D)) {
Decl* implicitD = const_cast<Decl*>(D);
implicitD->addAttr(UsedAttr::CreateImplicit(implicitD->getASTContext()));
}
}

bool DeclCollector::HandleTopLevelDecl(DeclGroupRef DGR) {
Transaction::DelayCallInfo DCI(DGR, Transaction::kCCIHandleTopLevelDecl);
m_CurTransaction->append(DCI);
Expand Down
20 changes: 16 additions & 4 deletions interpreter/cling/lib/Interpreter/DeclCollector.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#define CLING_DECL_COLLECTOR_H

#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTMutationListener.h"
#include "clang/Lex/PPCallbacks.h"

namespace clang {
Expand All @@ -32,19 +33,33 @@ namespace cling {
/// cling::DeclCollector is responsible for appending all the declarations
/// seen by clang.
///
class DeclCollector: public clang::PPCallbacks, public clang::ASTConsumer {
class DeclCollector: public clang::PPCallbacks,
public clang::ASTMutationListener,
public clang::ASTConsumer {
private:
Transaction* m_CurTransaction;

///\brief Test whether the first decl of the DeclGroupRef comes from an AST
/// file.
///
bool comesFromASTReader(clang::DeclGroupRef DGR) const;
bool comesFromASTReader(const clang::Decl* D) const;

public:
DeclCollector() : m_CurTransaction(0){}
virtual ~DeclCollector();

/// \name PPCallbacks overrides
/// Macro support
virtual void MacroDefined(const clang::Token &MacroNameTok,
const clang::MacroDirective *MD);
/// \}

/// \name ASTMutationListeners overrides
virtual void AddedCXXImplicitMember(const clang::CXXRecordDecl *RD,
const clang::Decl *D);
/// \}

/// \{
/// \name ASTConsumer overrides

Expand All @@ -71,9 +86,6 @@ namespace cling {

/// \}

/// Macro support
virtual void MacroDefined (const clang::Token &MacroNameTok,
const clang::MacroDirective *MD);
// dyn_cast/isa support
static bool classof(const clang::ASTConsumer*) { return true; }
};
Expand Down
19 changes: 19 additions & 0 deletions interpreter/cling/lib/Interpreter/TransactionUnloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ namespace clang {
Changed |= RemoveUnusedGlobalValue(*GV);
// Collect all uses of globals by GV
CollectAllUsesOfGlobals(GV);
FindUsedValues(*GV->getParent());

// The first pass is to drop initializers of global vars which are dead.
for (Globals::iterator I = VisitedGlobals.begin(),
Expand Down Expand Up @@ -108,6 +109,24 @@ namespace clang {
}

private:

/// Find values that are marked as llvm.used.
void FindUsedValues(const llvm::Module& m) {
for (const llvm::GlobalVariable& GV : m.globals()) {
if (!GV.getName().startswith("llvm.used"))
continue;

const llvm::ConstantArray* Inits
= cast<llvm::ConstantArray>(GV.getInitializer());

for (unsigned i = 0, e = Inits->getNumOperands(); i != e; ++i) {
llvm::Value *Operand
= Inits->getOperand(i)->stripPointerCastsNoFollowAliases();
VisitedGlobals.erase(cast<llvm::GlobalValue>(Operand));
}

}
}
/// CollectAllUsesOfGlobals - collects recursively all referenced globals by
/// GV.
void CollectAllUsesOfGlobals(llvm::GlobalValue *G) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -927,7 +927,9 @@ static void emitUsed(CodeGenModule &CGM, StringRef Name,

void CodeGenModule::emitLLVMUsed() {
emitUsed(*this, "llvm.used", LLVMUsed);
LLVMUsed.clear();
emitUsed(*this, "llvm.compiler.used", LLVMCompilerUsed);
LLVMCompilerUsed.clear();
}

void CodeGenModule::AppendLinkerOptions(StringRef Opts) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,14 @@ namespace clang {
break;
}
}

for(auto I = Builder->DeferredDeclsToEmit.begin(),
E = Builder->DeferredDeclsToEmit.end(); I != E; ++I) {
if (I->GV == GV) {
Builder->DeferredDeclsToEmit.erase(I);
break;
}
}
}

void Initialize(ASTContext &Context) override {
Expand Down

0 comments on commit 2abc4e8

Please sign in to comment.