r275481 - [modules] Don't pass interesting decls to the consumer for a module file that's
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 14 14:50:10 PDT 2016
Author: rsmith
Date: Thu Jul 14 16:50:09 2016
New Revision: 275481
URL: http://llvm.org/viewvc/llvm-project?rev=275481&view=rev
Log:
[modules] Don't pass interesting decls to the consumer for a module file that's
passed on the command line but never actually used. We consider a (top-level)
module to be used if any part of it is imported, either by the current
translation unit, or by any part of a top-level module that is itself used.
(Put another way, a module is used if an implicit modules build would have
loaded its .pcm file.)
Added:
cfe/trunk/test/Modules/unused-global-init.cpp
Modified:
cfe/trunk/include/clang/Serialization/ASTReader.h
cfe/trunk/lib/Serialization/ASTReader.cpp
cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
Modified: cfe/trunk/include/clang/Serialization/ASTReader.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Serialization/ASTReader.h?rev=275481&r1=275480&r2=275481&view=diff
==============================================================================
--- cfe/trunk/include/clang/Serialization/ASTReader.h (original)
+++ cfe/trunk/include/clang/Serialization/ASTReader.h Thu Jul 14 16:50:09 2016
@@ -613,7 +613,7 @@ private:
/// \brief A mapping from each of the hidden submodules to the deserialized
/// declarations in that submodule that could be made visible.
HiddenNamesMapType HiddenNamesMap;
-
+
/// \brief A module import, export, or conflict that hasn't yet been resolved.
struct UnresolvedModuleRef {
@@ -947,6 +947,24 @@ private:
/// Objective-C protocols.
std::deque<Decl *> InterestingDecls;
+ /// \brief Contains imported interesting declarations from modules that have
+ /// not yet themselves been imported, even indirectly.
+ typedef llvm::SmallVector<Decl *, 8> ModuleInterestingDecls;
+
+ /// Map from top-level modules to their list of interesting declarations.
+ /// Each map entry may be in one of three states: no entry means we've never
+ /// seen this module and never transitively imported it. A null pointer means
+ /// we're not tracking interesting decls: they should be handed straight to
+ /// the consumer because we've transitively imported this module already.
+ /// Otherwise, a list of pending interesting decls.
+ ///
+ /// As an added twist, declarations that are re-exported are listed against
+ /// the owning module and the re-exporting one, but the owning module's list
+ /// is definitive: the decl is there if and only if it has not been handed to
+ /// the consumer already.
+ llvm::DenseMap<Module *, ModuleInterestingDecls *>
+ UnimportedModuleInterestingDecls;
+
/// \brief The list of redeclaration chains that still need to be
/// reconstructed, and the local offset to the corresponding list
/// of redeclarations.
@@ -1249,6 +1267,9 @@ private:
llvm::iterator_range<ModuleDeclIterator>
getModuleFileLevelDecls(ModuleFile &Mod);
+ void addInterestingDecl(Decl *D,
+ llvm::Optional<Module *> OwnerOverride = llvm::None);
+
void PassInterestingDeclsToConsumer();
void PassInterestingDeclToConsumer(Decl *D);
@@ -1386,6 +1407,11 @@ public:
/// \brief Make the names within this set of hidden names visible.
void makeNamesVisible(const HiddenNames &Names, Module *Owner);
+ /// \brief Mark a module as "used". This implies that any global initializers
+ /// of declarations contained transitively within it should be run as part of
+ /// the current compilation.
+ void markModuleUsed(Module *M);
+
/// \brief Take the AST callbacks listener.
std::unique_ptr<ASTReaderListener> takeListener() {
return std::move(Listener);
Modified: cfe/trunk/lib/Serialization/ASTReader.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReader.cpp?rev=275481&r1=275480&r2=275481&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReader.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReader.cpp Thu Jul 14 16:50:09 2016
@@ -2691,6 +2691,8 @@ ASTReader::ReadASTBlock(ModuleFile &F, u
case EAGERLY_DESERIALIZED_DECLS:
// FIXME: Skip reading this record if our ASTConsumer doesn't care
// about "interesting" decls (for instance, if we're building a module).
+ // FIXME: Store this somewhere per-module and defer until
+ // markModuleReferenced is called.
for (unsigned I = 0, N = Record.size(); I != N; ++I)
EagerlyDeserializedDecls.push_back(getGlobalDeclID(F, Record[I]));
break;
@@ -3361,6 +3363,9 @@ void ASTReader::makeNamesVisible(const H
void ASTReader::makeModuleVisible(Module *Mod,
Module::NameVisibilityKind NameVisibility,
SourceLocation ImportLoc) {
+ // If we import anything from the module in any way, then it is used.
+ markModuleUsed(Mod);
+
llvm::SmallPtrSet<Module *, 4> Visited;
SmallVector<Module *, 4> Stack;
Stack.push_back(Mod);
@@ -6727,10 +6732,95 @@ void ASTReader::PassInterestingDeclsToCo
Decl *D = InterestingDecls.front();
InterestingDecls.pop_front();
+ // If we have found an interesting ImportDecl, then its imported module
+ // is considered used.
+ if (auto *ID = dyn_cast<ImportDecl>(D))
+ markModuleUsed(ID->getImportedModule());
+
PassInterestingDeclToConsumer(D);
}
}
+void ASTReader::markModuleUsed(Module *M) {
+ M = M->getTopLevelModule();
+ // Mark that interesting decls in this module should now be passed to the
+ // consumer, and pass any pending decls.
+ auto MInterestingDecls =
+ UnimportedModuleInterestingDecls.insert(std::make_pair(M, nullptr)).first;
+ if (auto *Decls = MInterestingDecls->second) {
+ MInterestingDecls->second = nullptr;
+ for (auto *D : *Decls) {
+ Module *Owner = D->getImportedOwningModule();
+ if (Owner)
+ Owner = Owner->getTopLevelModule();
+ if (Owner != M) {
+ // Mark that this decl has been handed to the consumer in its original
+ // module, and stop if it's already been removed from there.
+ auto OwnerIt = UnimportedModuleInterestingDecls.find(Owner);
+ if (OwnerIt == UnimportedModuleInterestingDecls.end() ||
+ !OwnerIt->second)
+ continue;
+ auto NewEnd =
+ std::remove(OwnerIt->second->begin(), OwnerIt->second->end(), D);
+ if (NewEnd == OwnerIt->second->end())
+ continue;
+ OwnerIt->second->erase(NewEnd, OwnerIt->second->end());
+ }
+ InterestingDecls.push_back(D);
+ }
+ }
+}
+
+void ASTReader::addInterestingDecl(Decl *D,
+ llvm::Optional<Module *> OwnerOverride) {
+ Module *Owner = D->getImportedOwningModule();
+ if (Owner)
+ Owner = Owner->getTopLevelModule();
+ Module *ExportedBy = OwnerOverride ? *OwnerOverride : Owner;
+ if (ExportedBy)
+ ExportedBy = ExportedBy->getTopLevelModule();
+
+ auto It = ExportedBy ? UnimportedModuleInterestingDecls.find(ExportedBy)
+ : UnimportedModuleInterestingDecls.end();
+ if (It == UnimportedModuleInterestingDecls.end())
+ It = UnimportedModuleInterestingDecls.insert(
+ std::make_pair(ExportedBy, new (Context) ModuleInterestingDecls))
+ .first;
+ ModuleInterestingDecls *Interesting = It->second;
+
+ // If this declaration's module has been imported, hand it to the consumer.
+ if (!ExportedBy || !Interesting) {
+ if (Owner != ExportedBy) {
+ // Mark that this decl has been handed to the consumer in its original
+ // module, and stop if it's already been removed from there.
+ auto OwnerIt = UnimportedModuleInterestingDecls.find(Owner);
+ if (OwnerIt == UnimportedModuleInterestingDecls.end() || !OwnerIt->second)
+ return;
+ auto NewEnd =
+ std::remove(OwnerIt->second->begin(), OwnerIt->second->end(), D);
+ if (NewEnd == OwnerIt->second->end())
+ return;
+ OwnerIt->second->erase(NewEnd, OwnerIt->second->end());
+ }
+ InterestingDecls.push_back(D);
+ return;
+ }
+ assert(Owner && "re-export of unowned decl");
+
+ // If this is a re-export of another module's decl, check whether the decl
+ // has already been handed to the consumer.
+ if (Owner != ExportedBy) {
+ auto OwnerIt = UnimportedModuleInterestingDecls.find(Owner);
+ if (OwnerIt != UnimportedModuleInterestingDecls.end() &&
+ (!OwnerIt->second ||
+ std::find(OwnerIt->second->begin(), OwnerIt->second->end(), D) ==
+ OwnerIt->second->end()))
+ return;
+ }
+
+ Interesting->push_back(D);
+}
+
void ASTReader::PassInterestingDeclToConsumer(Decl *D) {
if (ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D))
PassObjCImplDeclToConsumer(ImplD, Consumer);
Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=275481&r1=275480&r2=275481&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Thu Jul 14 16:50:09 2016
@@ -3462,6 +3462,13 @@ Decl *ASTReader::ReadDeclRecord(DeclID I
}
assert(Idx == Record.size());
+ // If we have deserialized a declaration that has a definition the
+ // AST consumer might need to know about, queue it.
+ // We don't pass it to the consumer immediately because we may be in recursive
+ // loading, and some declarations may still be initializing.
+ if (isConsumerInterestedIn(D, Reader.hasPendingBody()))
+ addInterestingDecl(D);
+
// Load any relevant update records.
PendingUpdateRecords.push_back(std::make_pair(ID, D));
@@ -3470,13 +3477,6 @@ Decl *ASTReader::ReadDeclRecord(DeclID I
if (Class->isThisDeclarationADefinition())
loadObjCCategories(ID, Class);
- // If we have deserialized a declaration that has a definition the
- // AST consumer might need to know about, queue it.
- // We don't pass it to the consumer immediately because we may be in recursive
- // loading, and some declarations may still be initializing.
- if (isConsumerInterestedIn(D, Reader.hasPendingBody()))
- InterestingDecls.push_back(D);
-
return D;
}
@@ -3511,7 +3511,7 @@ void ASTReader::loadDeclUpdateRecords(se
// we need to hand it off to the consumer.
if (!WasInteresting &&
isConsumerInterestedIn(D, Reader.hasPendingBody())) {
- InterestingDecls.push_back(D);
+ addInterestingDecl(D);
WasInteresting = true;
}
}
@@ -3945,6 +3945,7 @@ void ASTDeclReader::UpdateDecl(Decl *D,
// The declaration is now visible.
Exported->Hidden = false;
}
+ Reader.addInterestingDecl(Exported, Owner);
break;
}
Modified: cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterDecl.cpp?rev=275481&r1=275480&r2=275481&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriterDecl.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriterDecl.cpp Thu Jul 14 16:50:09 2016
@@ -2124,7 +2124,7 @@ static bool isRequiredDecl(const Decl *D
// ImportDecl is used by codegen to determine the set of imported modules to
// search for inputs for automatic linking; include it if it has a semantic
// effect.
- if (isa<ImportDecl>(D) && !WritingModule)
+ if (isa<ImportDecl>(D))
return true;
return Context.DeclMustBeEmitted(D);
Added: cfe/trunk/test/Modules/unused-global-init.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/unused-global-init.cpp?rev=275481&view=auto
==============================================================================
--- cfe/trunk/test/Modules/unused-global-init.cpp (added)
+++ cfe/trunk/test/Modules/unused-global-init.cpp Thu Jul 14 16:50:09 2016
@@ -0,0 +1,36 @@
+// RUN: rm -rf %t
+//
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-module %S/Inputs/unused-global-init/module.modulemap -fmodule-name=init -o %t/init.pcm
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-module %S/Inputs/unused-global-init/module.modulemap -fmodule-name=unused -o %t/unused.pcm -fmodule-file=%t/init.pcm
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-module %S/Inputs/unused-global-init/module.modulemap -fmodule-name=used -o %t/used.pcm -fmodule-file=%t/init.pcm
+//
+// No module file: init.h performs init.
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -DINIT | FileCheck --check-prefix=CHECK-INIT %s
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -DOTHER -DUSED -DUNUSED | FileCheck --check-prefix=CHECK-NO-INIT %s
+//
+// With module files: if there is a transitive import of any part of the
+// module, we run its global initializers (even if the imported piece is not
+// visible here).
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -fmodule-file=%t/used.pcm -fmodule-file=%t/unused.pcm -DINIT | FileCheck --check-prefix=CHECK-INIT %s
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -fmodule-file=%t/used.pcm -fmodule-file=%t/unused.pcm -DOTHER | FileCheck --check-prefix=CHECK-INIT %s
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -fmodule-file=%t/used.pcm -fmodule-file=%t/unused.pcm -DUSED | FileCheck --check-prefix=CHECK-INIT %s
+// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -fmodule-file=%t/used.pcm -fmodule-file=%t/unused.pcm -DUNUSED | FileCheck --check-prefix=CHECK-NO-INIT %s
+
+#ifdef INIT
+#include "init.h"
+#endif
+
+#ifdef OTHER
+#include "other.h"
+#endif
+
+#ifdef USED
+#include "used.h"
+#endif
+
+#ifdef UNUSED
+#include "unused.h"
+#endif
+
+// CHECK-INIT: call {{.*}}@_ZN4InitC
+// CHECK-NO-INIT-NOT: call {{.*}}@_ZN4InitC
More information about the cfe-commits
mailing list