Prototype of modules codegen

First pass at generating weak definitions of inline functions from module files
(& skipping (-O0) or emitting available_externally (optimizations)
definitions where those modules are used).

External functions defined in modules are emitted into the modular
object file as well (this may turn an existing ODR violation (if that
module were imported into multiple translations) into valid/linkable
code).

Internal symbols (static functions, for example) are not correctly
supported yet. The symbol will be produced, internal, in the modular
object - unreferenceable from the users.

Reviewers: rsmith

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

llvm-svn: 293456
This commit is contained in:
David Blaikie 2017-01-30 05:00:26 +00:00
parent c95726ea39
commit 9ffe5a3525
25 changed files with 199 additions and 22 deletions

View File

@ -2494,7 +2494,7 @@ public:
///
/// \returns true if the function/var must be CodeGen'ed/deserialized even if
/// it is not used.
bool DeclMustBeEmitted(const Decl *D);
bool DeclMustBeEmitted(const Decl *D, bool ForModularCodegen = false);
const CXXConstructorDecl *
getCopyConstructorForExceptionObject(CXXRecordDecl *RD);

View File

@ -16,6 +16,7 @@
#include "clang/AST/CharUnits.h"
#include "clang/AST/DeclBase.h"
#include "clang/Basic/Module.h"
#include "llvm/ADT/DenseMap.h"
namespace clang {
@ -169,6 +170,10 @@ public:
/// Return a descriptor for the corresponding module, if one exists.
virtual llvm::Optional<ASTSourceDescriptor> getSourceDescriptor(unsigned ID);
enum ExtKind { EK_Always, EK_Never, EK_ReplyHazy };
virtual ExtKind hasExternalDefinitions(unsigned ID);
/// \brief Finds all declarations lexically contained within the given
/// DeclContext, after applying an optional filter predicate.
///

View File

@ -201,6 +201,7 @@ LANGOPT(SizedDeallocation , 1, 0, "sized deallocation")
LANGOPT(AlignedAllocation , 1, 0, "aligned allocation")
LANGOPT(NewAlignOverride , 32, 0, "maximum alignment guaranteed by '::operator new(size_t)'")
LANGOPT(ConceptsTS , 1, 0, "enable C++ Extensions for Concepts")
BENIGN_LANGOPT(ModularCodegen , 1, 0, "Modular codegen")
BENIGN_LANGOPT(ElideConstructors , 1, 1, "C++ copy constructor elision")
BENIGN_LANGOPT(DumpRecordLayouts , 1, 0, "dumping the layout of IRgen'd records")
BENIGN_LANGOPT(DumpRecordLayoutsSimple , 1, 0, "dumping the layout of IRgen'd records in a simple form")

View File

@ -205,6 +205,8 @@ public:
/// 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 {

View File

@ -432,6 +432,10 @@ def fmodules_local_submodule_visibility :
Flag<["-"], "fmodules-local-submodule-visibility">,
HelpText<"Enforce name visibility rules across submodules of the same "
"top-level module.">;
def fmodule_codegen :
Flag<["-"], "fmodules-codegen">,
HelpText<"Generate code for uses of this module that assumes an explicit "
"object file will be built for the module">;
def fmodule_format_EQ : Joined<["-"], "fmodule-format=">,
HelpText<"Select the container format for clang modules and PCH. "
"Supported options are 'raw' and 'obj'.">;

View File

@ -90,6 +90,8 @@ public:
/// initializers themselves.
CXXCtorInitializer **GetExternalCXXCtorInitializers(uint64_t Offset) override;
ExtKind hasExternalDefinitions(unsigned ID) override;
/// \brief Find all declarations with the given name in the
/// given context.
bool FindExternalVisibleDeclsByName(const DeclContext *DC,

View File

@ -591,6 +591,8 @@ namespace clang {
/// \brief Record code for declarations associated with OpenCL extensions.
OPENCL_EXTENSION_DECLS = 59,
MODULAR_CODEGEN_DECLS = 60,
};
/// \brief Record types used within a source manager block.

View File

@ -715,6 +715,8 @@ private:
/// the consumer eagerly.
SmallVector<uint64_t, 16> EagerlyDeserializedDecls;
SmallVector<uint64_t, 16> ModularCodegenDecls;
/// \brief The IDs of all tentative definitions stored in the chain.
///
/// Sema keeps track of all tentative definitions in a TU because it has to
@ -1968,6 +1970,8 @@ public:
/// \brief Return a descriptor for the corresponding module.
llvm::Optional<ASTSourceDescriptor> getSourceDescriptor(unsigned ID) override;
ExtKind hasExternalDefinitions(unsigned ID) override;
/// \brief Retrieve a selector from the given module with its local ID
/// number.
Selector getLocalSelector(ModuleFile &M, unsigned LocalID);

View File

@ -365,6 +365,7 @@ private:
/// IDs, since they will be written out to an EAGERLY_DESERIALIZED_DECLS
/// record.
SmallVector<uint64_t, 16> EagerlyDeserializedDecls;
SmallVector<uint64_t, 16> ModularCodegenDecls;
/// \brief DeclContexts that have received extensions since their serialized
/// form.

View File

@ -8882,8 +8882,22 @@ static GVALinkage adjustGVALinkageForAttributes(const ASTContext &Context,
}
GVALinkage ASTContext::GetGVALinkageForFunction(const FunctionDecl *FD) const {
return adjustGVALinkageForAttributes(
auto L = adjustGVALinkageForAttributes(
*this, basicGVALinkageForFunction(*this, FD), FD);
auto EK = ExternalASTSource::EK_ReplyHazy;
if (auto *Ext = getExternalSource())
EK = Ext->hasExternalDefinitions(FD->getOwningModuleID());
switch (EK) {
case ExternalASTSource::EK_Never:
if (L == GVA_DiscardableODR)
return GVA_StrongODR;
break;
case ExternalASTSource::EK_Always:
return GVA_AvailableExternally;
case ExternalASTSource::EK_ReplyHazy:
break;
}
return L;
}
static GVALinkage basicGVALinkageForVariable(const ASTContext &Context,
@ -8968,7 +8982,7 @@ GVALinkage ASTContext::GetGVALinkageForVariable(const VarDecl *VD) {
*this, basicGVALinkageForVariable(*this, VD), VD);
}
bool ASTContext::DeclMustBeEmitted(const Decl *D) {
bool ASTContext::DeclMustBeEmitted(const Decl *D, bool ForModularCodegen) {
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
if (!VD->isFileVarDecl())
return false;
@ -9032,10 +9046,15 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
}
}
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++.
return !isDiscardableGVALinkage(GetGVALinkageForFunction(FD));
return !isDiscardableGVALinkage(Linkage);
}
const VarDecl *VD = cast<VarDecl>(D);

View File

@ -28,6 +28,11 @@ ExternalASTSource::getSourceDescriptor(unsigned ID) {
return None;
}
ExternalASTSource::ExtKind
ExternalASTSource::hasExternalDefinitions(unsigned ID) {
return EK_ReplyHazy;
}
ExternalASTSource::ASTSourceDescriptor::ASTSourceDescriptor(const Module &M)
: Signature(M.Signature), ClangModule(&M) {
if (M.Directory)

View File

@ -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), NameVisibility(Hidden) {
NoUndeclaredIncludes(false), WithCodegen(false), NameVisibility(Hidden) {
if (Parent) {
if (!Parent->isAvailable())
IsAvailable = false;

View File

@ -2796,7 +2796,7 @@ llvm::GlobalValue::LinkageTypes CodeGenModule::getLLVMLinkageForDeclarator(
// We are guaranteed to have a strong definition somewhere else,
// so we can use available_externally linkage.
if (Linkage == GVA_AvailableExternally)
return llvm::Function::AvailableExternallyLinkage;
return llvm::GlobalValue::AvailableExternallyLinkage;
// Note that Apple's kernel linker doesn't support symbol
// coalescing, so we need to avoid linkonce and weak linkages there.

View File

@ -1955,6 +1955,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
Args.hasArg(OPT_fmodules_decluse) || Opts.ModulesStrictDeclUse;
Opts.ModulesLocalVisibility =
Args.hasArg(OPT_fmodules_local_submodule_visibility) || Opts.ModulesTS;
Opts.ModularCodegen = Args.hasArg(OPT_fmodule_codegen);
Opts.ModulesSearchAll = Opts.Modules &&
!Args.hasArg(OPT_fno_modules_search_all) &&
Args.hasArg(OPT_fmodules_search_all);

View File

@ -91,6 +91,7 @@ ModuleMap::ModuleMap(SourceManager &SourceMgr, DiagnosticsEngine &Diags,
HeaderInfo(HeaderInfo), BuiltinIncludeDir(nullptr),
SourceModule(nullptr), NumCreatedModules(0) {
MMapLangOpts.LineComment = true;
MMapLangOpts.ModularCodegen = LangOpts.ModularCodegen;
}
ModuleMap::~ModuleMap() {
@ -554,16 +555,17 @@ Module *ModuleMap::lookupModuleQualified(StringRef Name, Module *Context) const{
return Context->findSubmodule(Name);
}
std::pair<Module *, bool>
ModuleMap::findOrCreateModule(StringRef Name, Module *Parent, bool IsFramework,
bool IsExplicit) {
std::pair<Module *, bool> ModuleMap::findOrCreateModule(StringRef Name,
Module *Parent,
bool IsFramework,
bool IsExplicit) {
// Try to find an existing module with this name.
if (Module *Sub = lookupModuleQualified(Name, Parent))
return std::make_pair(Sub, false);
// Create a new module with this name.
Module *Result = new Module(Name, SourceLocation(), Parent,
IsFramework, IsExplicit, NumCreatedModules++);
Module *Result = new Module(Name, SourceLocation(), Parent, IsFramework,
IsExplicit, NumCreatedModules++);
if (!Parent) {
if (LangOpts.CurrentModule == Name)
SourceModule = Result;
@ -1499,6 +1501,7 @@ void ModuleMapParser::parseModuleDecl() {
(!ActiveModule->Parent && ModuleName == "Darwin"))
ActiveModule->NoUndeclaredIncludes = true;
ActiveModule->Directory = Directory;
ActiveModule->WithCodegen = L.getLangOpts().ModularCodegen;
if (!ActiveModule->Parent) {
StringRef MapFileName(ModuleMapFile->getName());

View File

@ -94,6 +94,15 @@ MultiplexExternalSemaSource::GetExternalCXXCtorInitializers(uint64_t Offset) {
return nullptr;
}
ExternalASTSource::ExtKind
MultiplexExternalSemaSource::hasExternalDefinitions(unsigned int ID) {
for (const auto &S : Sources)
if (auto EK = S->hasExternalDefinitions(ID))
if (EK != EK_ReplyHazy)
return EK;
return EK_ReplyHazy;
}
bool MultiplexExternalSemaSource::
FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name) {
bool AnyDeclsFound = false;

View File

@ -2607,7 +2607,8 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
break;
case SUBMODULE_BLOCK_ID:
if (ASTReadResult Result = ReadSubmoduleBlock(F, ClientLoadCapabilities))
if (ASTReadResult Result =
ReadSubmoduleBlock(F, ClientLoadCapabilities))
return Result;
break;
@ -2772,6 +2773,14 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
EagerlyDeserializedDecls.push_back(getGlobalDeclID(F, Record[I]));
break;
case MODULAR_CODEGEN_DECLS:
// FIXME: Skip reading this record if our ASTConsumer doesn't care about
// them (ie: if we're not codegenerating this module).
if (F.Kind == MK_MainFile)
for (unsigned I = 0, N = Record.size(); I != N; ++I)
EagerlyDeserializedDecls.push_back(getGlobalDeclID(F, Record[I]));
break;
case SPECIAL_TYPES:
if (SpecialTypes.empty()) {
for (unsigned I = 0, N = Record.size(); I != N; ++I)
@ -4628,6 +4637,7 @@ 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)
@ -4635,8 +4645,9 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
// Retrieve this (sub)module from the module map, creating it if
// necessary.
CurrentModule = ModMap.findOrCreateModule(Name, ParentModule, IsFramework,
IsExplicit).first;
CurrentModule =
ModMap.findOrCreateModule(Name, ParentModule, IsFramework, IsExplicit)
.first;
// FIXME: set the definition loc for CurrentModule, or call
// ModMap.setInferredModuleAllowedBy()
@ -4672,6 +4683,7 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
CurrentModule->InferExplicitSubmodules = InferExplicitSubmodules;
CurrentModule->InferExportWildcard = InferExportWildcard;
CurrentModule->ConfigMacrosExhaustive = ConfigMacrosExhaustive;
CurrentModule->WithCodegen = WithCodegen;
if (DeserializationListener)
DeserializationListener->ModuleRead(GlobalID, CurrentModule);
@ -7895,6 +7907,18 @@ ASTReader::getSourceDescriptor(unsigned ID) {
return None;
}
ExternalASTSource::ExtKind ASTReader::hasExternalDefinitions(unsigned ID) {
const Module *M = getSubmodule(ID);
if (!M || !M->WithCodegen)
return EK_ReplyHazy;
ModuleFile *MF = ModuleMgr.lookup(M->getASTFile());
assert(MF); // ?
if (MF->Kind == ModuleKind::MK_MainFile)
return EK_Never;
return EK_Always;
}
Selector ASTReader::getLocalSelector(ModuleFile &M, unsigned LocalID) {
return DecodeSelector(getGlobalSelectorID(M, LocalID));
}

View File

@ -1044,6 +1044,7 @@ void ASTWriter::WriteBlockInfoBlock() {
RECORD(IDENTIFIER_OFFSET);
RECORD(IDENTIFIER_TABLE);
RECORD(EAGERLY_DESERIALIZED_DECLS);
RECORD(MODULAR_CODEGEN_DECLS);
RECORD(SPECIAL_TYPES);
RECORD(STATISTICS);
RECORD(TENTATIVE_DEFINITIONS);
@ -2589,6 +2590,7 @@ 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, 2)); // WithCodegen
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name
unsigned DefinitionAbbrev = Stream.EmitAbbrev(std::move(Abbrev));
@ -2677,11 +2679,18 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) {
// Emit the definition of the block.
{
RecordData::value_type Record[] = {
SUBMODULE_DEFINITION, ID, ParentID, Mod->IsFramework, Mod->IsExplicit,
Mod->IsSystem, Mod->IsExternC, Mod->InferSubmodules,
Mod->InferExplicitSubmodules, Mod->InferExportWildcard,
Mod->ConfigMacrosExhaustive};
RecordData::value_type Record[] = {SUBMODULE_DEFINITION,
ID,
ParentID,
Mod->IsFramework,
Mod->IsExplicit,
Mod->IsSystem,
Mod->IsExternC,
Mod->InferSubmodules,
Mod->InferExplicitSubmodules,
Mod->InferExportWildcard,
Mod->ConfigMacrosExhaustive,
Mod->WithCodegen};
Stream.EmitRecordWithBlob(DefinitionAbbrev, Record, Mod->Name);
}
@ -4694,6 +4703,9 @@ uint64_t ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot,
if (!EagerlyDeserializedDecls.empty())
Stream.EmitRecord(EAGERLY_DESERIALIZED_DECLS, EagerlyDeserializedDecls);
if (Context.getLangOpts().ModularCodegen)
Stream.EmitRecord(MODULAR_CODEGEN_DECLS, ModularCodegenDecls);
// Write the record containing tentative definitions.
if (!TentativeDefinitions.empty())
Stream.EmitRecord(TENTATIVE_DEFINITIONS, TentativeDefinitions);

View File

@ -2153,7 +2153,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 WritingModule, bool ModularCode) {
// An ObjCMethodDecl is never considered as "required" because its
// implementation container always is.
@ -2169,7 +2169,7 @@ static bool isRequiredDecl(const Decl *D, ASTContext &Context,
return false;
}
return Context.DeclMustBeEmitted(D);
return Context.DeclMustBeEmitted(D, ModularCode);
}
void ASTWriter::WriteDecl(ASTContext &Context, Decl *D) {
@ -2213,8 +2213,11 @@ 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))
if (isRequiredDecl(D, Context, WritingModule, false))
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) {

View File

@ -0,0 +1,2 @@
#include "foo.h"
inline void bar() { foo(); }

View File

@ -0,0 +1 @@
module bar { header "bar.h" }

View File

@ -0,0 +1,10 @@
void f1(int &);
static void f2() {}
inline void foo() {
static int i;
f1(i);
f2();
}
inline void foo2() {
}
void foo_ext() {}

View File

@ -0,0 +1 @@
module foo { header "foo.h" }

View File

@ -0,0 +1,2 @@
#include "bar.h"
int main() { bar(); }

View File

@ -0,0 +1,64 @@
RUN: rm -rf %t
RUN: %clang_cc1 -fmodules-codegen -x c++ -fmodules -emit-module -fmodule-name=foo %S/Inputs/codegen/foo.modulemap -o %t/foo.pcm
RUN: %clang_cc1 -fmodules-codegen -x c++ -fmodules -emit-module -fmodule-name=bar %S/Inputs/codegen/bar.modulemap -o %t/bar.pcm -fmodule-file=%t/foo.pcm
RUN: %clang_cc1 -emit-llvm %t/foo.pcm -o - | FileCheck --check-prefix=FOO %s
RUN: %clang_cc1 -emit-llvm %t/bar.pcm -o - -fmodule-file=%t/foo.pcm | FileCheck --check-prefix=BAR-CMN --check-prefix=BAR %s
RUN: %clang_cc1 -fmodules -fmodule-file=%t/foo.pcm -fmodule-file=%t/bar.pcm %S/Inputs/codegen/use.cpp -emit-llvm -o - | FileCheck --check-prefix=USE-CMN --check-prefix=USE %s
RUN: %clang_cc1 -O2 -disable-llvm-passes -emit-llvm %t/foo.pcm -o - | FileCheck --check-prefix=FOO %s
RUN: %clang_cc1 -O2 -disable-llvm-passes -emit-llvm %t/bar.pcm -o - -fmodule-file=%t/foo.pcm | FileCheck --check-prefix=BAR-CMN --check-prefix=BAR-OPT %s
RUN: %clang_cc1 -O2 -disable-llvm-passes -fmodules -fmodule-file=%t/foo.pcm -fmodule-file=%t/bar.pcm %S/Inputs/codegen/use.cpp -emit-llvm -o - | FileCheck --check-prefix=USE-CMN --check-prefix=USE-OPT %s
FOO-NOT: comdat
FOO: $_Z3foov = comdat any
FOO: $_Z4foo2v = comdat any
FOO: $_ZZ3foovE1i = comdat any
FOO: @_ZZ3foovE1i = linkonce_odr global i32 0, comdat
FOO-NOT: {{comdat|define|declare}}
FOO: define void @_Z7foo_extv()
FOO-NOT: {{define|declare}}
FOO: define weak_odr void @_Z3foov() #{{[0-9]+}} comdat
FOO-NOT: {{define|declare}}
FOO: declare void @_Z2f1Ri(i32*
FOO-NOT: {{define|declare}}
FIXME: this internal function should be weak_odr, comdat, and with a new mangling
FOO: define internal void @_ZL2f2v() #{{[0-9]+}}
FOO-NOT: {{define|declare}}
FOO: define weak_odr void @_Z4foo2v() #{{[0-9]+}} comdat
FOO-NOT: {{define|declare}}
BAR-CMN-NOT: comdat
BAR-CMN: $_Z3barv = comdat any
BAR-OPT: @_ZZ3foovE1i = linkonce_odr global i32 0, comdat
BAR-CMN-NOT: {{comdat|define|declare}}
BAR-CMN: define weak_odr void @_Z3barv() #{{[0-9]+}} comdat
BAR-CMN-NOT: {{define|declare}}
BAR: declare void @_Z3foov()
Include all the available_externally definitions required for bar (foo -> f2)
BAR-OPT: define available_externally void @_Z3foov()
BAR-CMN-NOT: {{define|declare}}
BAR-OPT: declare void @_Z2f1Ri(i32*
BAR-OPT-NOT: {{define|declare}}
BAR-OPT: define available_externally void @_ZL2f2v()
BAR-OPT-NOT: {{define|declare}}
USE-OPT: @_ZZ3foovE1i = linkonce_odr global i32 0, comdat
USE-CMN-NOT: {{comdat|define|declare}}
USE-CMN: define i32 @main()
USE-CMN-NOT: {{define|declare}}
USE: declare void @_Z3barv()
Include all the available_externally definitions required for main (bar -> foo -> f2)
USE-OPT: define available_externally void @_Z3barv()
USE-CMN-NOT: {{define|declare}}
USE-OPT: define available_externally void @_Z3foov()
USE-OPT-NOT: {{define|declare}}
USE-OPT: declare void @_Z2f1Ri(i32*
USE-OPT-NOT: {{define|declare}}
USE-OPT: define available_externally void @_ZL2f2v()
USE-OPT-NOT: {{define|declare}}