Skip to content

Commit

Permalink
Modular Codegen: Add/use a bit in serialized function definitions to …
Browse files Browse the repository at this point in the history
…track whether they are the subject of modular codegen

Some decls are created not where they are written, but in other module
files/users (implicit special members and function template implicit
specializations). To correctly identify them, use a bit next to the definition
to track the modular codegen property.

Discussed whether the module file bit could be omitted in favor of
reconstituting from the modular codegen decls list - best guess today is that
the efficiency improvement of not having to deserialize the whole list whenever
any function is queried by a module user is worth it for the small size
increase of this redundant (list + bit-on-def) representation.

Reviewers: rsmith

Differential Revision: https://reviews.llvm.org/D29901

llvm-svn: 299982
  • Loading branch information
dwblaikie committed Apr 11, 2017
1 parent 4c54fe0 commit e6b7c28
Show file tree
Hide file tree
Showing 19 changed files with 104 additions and 41 deletions.
2 changes: 1 addition & 1 deletion clang/include/clang/AST/ASTContext.h
Expand Up @@ -2510,7 +2510,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
///
/// \returns true if the function/var must be CodeGen'ed/deserialized even if
/// it is not used.
bool DeclMustBeEmitted(const Decl *D, bool ForModularCodegen = false);
bool DeclMustBeEmitted(const Decl *D);

const CXXConstructorDecl *
getCopyConstructorForExceptionObject(CXXRecordDecl *RD);
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/AST/ExternalASTSource.h
Expand Up @@ -172,7 +172,7 @@ class ExternalASTSource : public RefCountedBase<ExternalASTSource> {

enum ExtKind { EK_Always, EK_Never, EK_ReplyHazy };

virtual ExtKind hasExternalDefinitions(unsigned ID);
virtual ExtKind hasExternalDefinitions(const FunctionDecl *FD);

/// \brief Finds all declarations lexically contained within the given
/// DeclContext, after applying an optional filter predicate.
Expand Down
2 changes: 0 additions & 2 deletions clang/include/clang/Basic/Module.h
Expand Up @@ -215,8 +215,6 @@ class Module {
/// and headers from used modules.
unsigned NoUndeclaredIncludes : 1;

unsigned WithCodegen : 1;

/// \brief Describes the visibility of the various names within a
/// particular module.
enum NameVisibilityKind {
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Sema/MultiplexExternalSemaSource.h
Expand Up @@ -90,7 +90,7 @@ class MultiplexExternalSemaSource : public ExternalSemaSource {
/// initializers themselves.
CXXCtorInitializer **GetExternalCXXCtorInitializers(uint64_t Offset) override;

ExtKind hasExternalDefinitions(unsigned ID) override;
ExtKind hasExternalDefinitions(const FunctionDecl *FD) override;

/// \brief Find all declarations with the given name in the
/// given context.
Expand Down
4 changes: 3 additions & 1 deletion clang/include/clang/Serialization/ASTReader.h
Expand Up @@ -1115,6 +1115,8 @@ class ASTReader
/// predefines buffer may contain additional definitions.
std::string SuggestedPredefines;

llvm::DenseMap<const FunctionDecl *, bool> BodySource;

/// \brief Reads a statement from the specified cursor.
Stmt *ReadStmtFromStream(ModuleFile &F);

Expand Down Expand Up @@ -1997,7 +1999,7 @@ class ASTReader
/// \brief Return a descriptor for the corresponding module.
llvm::Optional<ASTSourceDescriptor> getSourceDescriptor(unsigned ID) override;

ExtKind hasExternalDefinitions(unsigned ID) override;
ExtKind hasExternalDefinitions(const FunctionDecl *FD) override;

/// \brief Retrieve a selector from the given module with its local ID
/// number.
Expand Down
7 changes: 2 additions & 5 deletions clang/lib/AST/ASTContext.cpp
Expand Up @@ -8897,7 +8897,7 @@ GVALinkage ASTContext::GetGVALinkageForFunction(const FunctionDecl *FD) const {
*this, basicGVALinkageForFunction(*this, FD), FD);
auto EK = ExternalASTSource::EK_ReplyHazy;
if (auto *Ext = getExternalSource())
EK = Ext->hasExternalDefinitions(FD->getOwningModuleID());
EK = Ext->hasExternalDefinitions(FD);
switch (EK) {
case ExternalASTSource::EK_Never:
if (L == GVA_DiscardableODR)
Expand Down Expand Up @@ -8993,7 +8993,7 @@ GVALinkage ASTContext::GetGVALinkageForVariable(const VarDecl *VD) {
*this, basicGVALinkageForVariable(*this, VD), VD);
}

bool ASTContext::DeclMustBeEmitted(const Decl *D, bool ForModularCodegen) {
bool ASTContext::DeclMustBeEmitted(const Decl *D) {
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
if (!VD->isFileVarDecl())
return false;
Expand Down Expand Up @@ -9059,9 +9059,6 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D, bool ForModularCodegen) {

GVALinkage Linkage = GetGVALinkageForFunction(FD);

if (Linkage == GVA_DiscardableODR && ForModularCodegen)
return true;

// static, static inline, always_inline, and extern inline functions can
// always be deferred. Normal inline functions can be deferred in C99/C++.
// Implicit template instantiations can also be deferred in C++.
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/ExternalASTSource.cpp
Expand Up @@ -29,7 +29,7 @@ ExternalASTSource::getSourceDescriptor(unsigned ID) {
}

ExternalASTSource::ExtKind
ExternalASTSource::hasExternalDefinitions(unsigned ID) {
ExternalASTSource::hasExternalDefinitions(const FunctionDecl *FD) {
return EK_ReplyHazy;
}

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Basic/Module.cpp
Expand Up @@ -33,7 +33,7 @@ Module::Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent,
IsExplicit(IsExplicit), IsSystem(false), IsExternC(false),
IsInferred(false), InferSubmodules(false), InferExplicitSubmodules(false),
InferExportWildcard(false), ConfigMacrosExhaustive(false),
NoUndeclaredIncludes(false), WithCodegen(false), NameVisibility(Hidden) {
NoUndeclaredIncludes(false), NameVisibility(Hidden) {
if (Parent) {
if (!Parent->isAvailable())
IsAvailable = false;
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Sema/MultiplexExternalSemaSource.cpp
Expand Up @@ -95,9 +95,9 @@ MultiplexExternalSemaSource::GetExternalCXXCtorInitializers(uint64_t Offset) {
}

ExternalASTSource::ExtKind
MultiplexExternalSemaSource::hasExternalDefinitions(unsigned int ID) {
MultiplexExternalSemaSource::hasExternalDefinitions(const FunctionDecl *FD) {
for (const auto &S : Sources)
if (auto EK = S->hasExternalDefinitions(ID))
if (auto EK = S->hasExternalDefinitions(FD))
if (EK != EK_ReplyHazy)
return EK;
return EK_ReplyHazy;
Expand Down
20 changes: 7 additions & 13 deletions clang/lib/Serialization/ASTReader.cpp
Expand Up @@ -4834,7 +4834,6 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
bool InferExplicitSubmodules = Record[Idx++];
bool InferExportWildcard = Record[Idx++];
bool ConfigMacrosExhaustive = Record[Idx++];
bool WithCodegen = Record[Idx++];

Module *ParentModule = nullptr;
if (Parent)
Expand Down Expand Up @@ -4880,7 +4879,6 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
CurrentModule->InferExplicitSubmodules = InferExplicitSubmodules;
CurrentModule->InferExportWildcard = InferExportWildcard;
CurrentModule->ConfigMacrosExhaustive = ConfigMacrosExhaustive;
CurrentModule->WithCodegen = WithCodegen;
if (DeserializationListener)
DeserializationListener->ModuleRead(GlobalID, CurrentModule);

Expand Down Expand Up @@ -8149,16 +8147,12 @@ ASTReader::getSourceDescriptor(unsigned ID) {
return None;
}

ExternalASTSource::ExtKind ASTReader::hasExternalDefinitions(unsigned ID) {
const Module *M = getSubmodule(ID);
if (!M || !M->WithCodegen)
ExternalASTSource::ExtKind
ASTReader::hasExternalDefinitions(const FunctionDecl *FD) {
auto I = BodySource.find(FD);
if (I == BodySource.end())
return EK_ReplyHazy;

ModuleFile *MF = ModuleMgr.lookup(M->getASTFile());
assert(MF); // ?
if (MF->Kind == ModuleKind::MK_MainFile)
return EK_Never;
return EK_Always;
return I->second ? EK_Never : EK_Always;
}

Selector ASTReader::getLocalSelector(ModuleFile &M, unsigned LocalID) {
Expand Down Expand Up @@ -8992,9 +8986,9 @@ void ASTReader::finishPendingActions() {
// FIXME: Check for =delete/=default?
// FIXME: Complain about ODR violations here?
const FunctionDecl *Defn = nullptr;
if (!getContext().getLangOpts().Modules || !FD->hasBody(Defn))
if (!getContext().getLangOpts().Modules || !FD->hasBody(Defn)) {
FD->setLazyBody(PB->second);
else
} else
mergeDefinitionVisibility(const_cast<FunctionDecl*>(Defn), FD);
continue;
}
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Expand Up @@ -424,6 +424,8 @@ uint64_t ASTDeclReader::GetCurrentCursorOffset() {
}

void ASTDeclReader::ReadFunctionDefinition(FunctionDecl *FD) {
if (Record.readInt())
Reader.BodySource[FD] = Loc.F->Kind == ModuleKind::MK_MainFile;
if (auto *CD = dyn_cast<CXXConstructorDecl>(FD)) {
CD->NumCtorInitializers = Record.readInt();
if (CD->NumCtorInitializers)
Expand Down
4 changes: 1 addition & 3 deletions clang/lib/Serialization/ASTWriter.cpp
Expand Up @@ -2627,7 +2627,6 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) {
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferExplicit...
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferExportWild...
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // ConfigMacrosExh...
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // WithCodegen
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name
unsigned DefinitionAbbrev = Stream.EmitAbbrev(std::move(Abbrev));

Expand Down Expand Up @@ -2726,8 +2725,7 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) {
Mod->InferSubmodules,
Mod->InferExplicitSubmodules,
Mod->InferExportWildcard,
Mod->ConfigMacrosExhaustive,
Context->getLangOpts().ModularCodegen && WritingModule};
Mod->ConfigMacrosExhaustive};
Stream.EmitRecordWithBlob(DefinitionAbbrev, Record, Mod->Name);
}

Expand Down
14 changes: 8 additions & 6 deletions clang/lib/Serialization/ASTWriterDecl.cpp
Expand Up @@ -2159,7 +2159,7 @@ void ASTWriter::WriteDeclAbbrevs() {
/// relatively painless since they would presumably only do it for top-level
/// decls.
static bool isRequiredDecl(const Decl *D, ASTContext &Context,
bool WritingModule, bool ModularCode) {
bool WritingModule) {
// An ObjCMethodDecl is never considered as "required" because its
// implementation container always is.

Expand All @@ -2175,7 +2175,7 @@ static bool isRequiredDecl(const Decl *D, ASTContext &Context,
return false;
}

return Context.DeclMustBeEmitted(D, ModularCode);
return Context.DeclMustBeEmitted(D);
}

void ASTWriter::WriteDecl(ASTContext &Context, Decl *D) {
Expand Down Expand Up @@ -2219,18 +2219,20 @@ void ASTWriter::WriteDecl(ASTContext &Context, Decl *D) {

// Note declarations that should be deserialized eagerly so that we can add
// them to a record in the AST file later.
if (isRequiredDecl(D, Context, WritingModule, false))
if (isRequiredDecl(D, Context, WritingModule))
EagerlyDeserializedDecls.push_back(ID);
else if (Context.getLangOpts().ModularCodegen && WritingModule &&
isRequiredDecl(D, Context, true, true))
ModularCodegenDecls.push_back(ID);
}

void ASTRecordWriter::AddFunctionDefinition(const FunctionDecl *FD) {
// Switch case IDs are per function body.
Writer->ClearSwitchCaseIDs();

assert(FD->doesThisDeclarationHaveABody());
bool ModularCodegen = Writer->Context->getLangOpts().ModularCodegen &&
Writer->WritingModule && !FD->isDependentContext();
Record->push_back(ModularCodegen);
if (ModularCodegen)
Writer->ModularCodegenDecls.push_back(Writer->GetDeclRef(FD));
if (auto *CD = dyn_cast<CXXConstructorDecl>(FD)) {
Record->push_back(CD->getNumCtorInitializers());
if (CD->getNumCtorInitializers())
Expand Down
5 changes: 5 additions & 0 deletions clang/test/Modules/Inputs/codegen-nodep/foo.h
@@ -0,0 +1,5 @@
template <typename T>
void ftempl() {
}
inline void f() {
}
1 change: 1 addition & 0 deletions clang/test/Modules/Inputs/codegen-nodep/foo.modulemap
@@ -0,0 +1 @@
module foo { header "foo.h" }
26 changes: 26 additions & 0 deletions clang/test/Modules/Inputs/codegen/foo.h
Expand Up @@ -2,3 +2,29 @@ inline void f1(const char* fmt, ...) {
__builtin_va_list args;
__builtin_va_start(args, fmt);
}

struct non_trivial_dtor {
~non_trivial_dtor();
};

struct implicit_dtor {
non_trivial_dtor d;
};

struct uninst_implicit_dtor {
non_trivial_dtor d;
};

inline void use_implicit_dtor() {
implicit_dtor d;
}

template <typename T>
void inst() {
}

inline void inst_decl() {
// cause inst<int>'s declaration to be instantiated, without a definition.
(void)sizeof(&inst<int>);
inst<float>();
}
8 changes: 8 additions & 0 deletions clang/test/Modules/Inputs/codegen/use.cpp
@@ -0,0 +1,8 @@
#include "foo.h"
void non_modular_use_of_implicit_dtor() {
implicit_dtor d1;
uninst_implicit_dtor d2;
}
void use_of_instantiated_declaration_without_definition() {
inst<int>();
}
13 changes: 13 additions & 0 deletions clang/test/Modules/codegen-nodep.test
@@ -0,0 +1,13 @@
RUN: rm -rf %t
REQUIRES: x86-registered-target

RUN: %clang_cc1 -triple=x86_64-linux-gnu -fmodules-codegen -x c++ -fmodules \
RUN: -emit-module -fmodule-name=foo \
RUN: %S/Inputs/codegen-nodep/foo.modulemap -o - \
RUN: | llvm-bcanalyzer - -dump \
RUN: | FileCheck %s

Ensure there's only one modular codegen decl - the sentinel plain inline
function, not any for the function template.

CHECK: <MODULAR_CODEGEN_DECLS op0={{[0-9]+}}/>
25 changes: 21 additions & 4 deletions clang/test/Modules/codegen.test
Expand Up @@ -3,8 +3,25 @@ REQUIRES: x86-registered-target

RUN: %clang_cc1 -triple=x86_64-linux-gnu -fmodules-codegen -x c++ -fmodules -emit-module -fmodule-name=foo %S/Inputs/codegen/foo.modulemap -o %t/foo.pcm

RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %t/foo.pcm | FileCheck %s
RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %t/foo.pcm | FileCheck --check-prefix=FOO --check-prefix=BOTH %s
RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - -fmodules -fmodule-file=%t/foo.pcm %S/Inputs/codegen/use.cpp | FileCheck --check-prefix=BOTH --check-prefix=USE %s

CHECK: $_Z2f1PKcz = comdat any
CHECK: define weak_odr void @_Z2f1PKcz(i8* %fmt, ...) #{{[0-9]+}} comdat
CHECK: call void @llvm.va_start(i8* %{{[a-zA-Z0-9]*}})
FOO: $_Z2f1PKcz = comdat any
FOO: $_ZN13implicit_dtorD1Ev = comdat any
USE: $_Z4instIiEvv = comdat any
FOO: $_ZN13implicit_dtorD2Ev = comdat any
FOO: define weak_odr void @_Z2f1PKcz(i8* %fmt, ...) #{{[0-9]+}} comdat
FOO: call void @llvm.va_start(i8* %{{[a-zA-Z0-9]*}})

Test that implicit special members are emitted into the FOO module if they're
ODR used there, otherwise emit them linkonce_odr as usual in the use.

FIXME: Proactively instantiate any valid implicit special members to emit them into the module object.

FOO: define weak_odr void @_ZN13implicit_dtorD1Ev
FOO: define weak_odr void @_Z4instIfEvv
FOO: define weak_odr void @_ZN13implicit_dtorD2Ev

USE: define linkonce_odr void @_ZN20uninst_implicit_dtorD1Ev
USE: define linkonce_odr void @_Z4instIiEvv
USE: define linkonce_odr void @_ZN20uninst_implicit_dtorD2Ev

0 comments on commit e6b7c28

Please sign in to comment.