From: Douglas Gregor Date: Mon, 14 Jan 2013 17:21:00 +0000 (+0000) Subject: Implement parsing, AST, (de-)serialization, and placeholder global X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b6cbe517237c3c223beb064d60d5b49e56d65c06;p=clang Implement parsing, AST, (de-)serialization, and placeholder global metadata for linking against the libraries/frameworks for imported modules. The module map language is extended with a new "link" directive that specifies what library or framework to link against when a module is imported, e.g., link "clangAST" or link framework "MyFramework" Importing the corresponding module (or any of its submodules) will eventually link against the named library/framework. For now, I've added some placeholder global metadata that encodes the imported libraries/frameworks, so that we can test that this information gets through to the IR. The format of the data is still under discussion. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@172437 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index 3c036a3e65..b9dbaf2e9e 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -486,6 +486,8 @@ def err_mmap_umbrella_clash : Error< "umbrella for module '%0' already covers this directory">; def err_mmap_export_module_id : Error< "expected an exported module name or '*'">; +def err_mmap_expected_library_name : Error< + "expected %select{library|framework}0 name as a string">; def err_mmap_missing_module_unqualified : Error< "no module named '%0' visible from '%1'">; def err_mmap_missing_module_qualified : Error< diff --git a/include/clang/Basic/Module.h b/include/clang/Basic/Module.h index b638068e74..2bda801d18 100644 --- a/include/clang/Basic/Module.h +++ b/include/clang/Basic/Module.h @@ -164,7 +164,28 @@ public: /// \brief The set of export declarations that have yet to be resolved. SmallVector UnresolvedExports; - + + /// \brief A library or framework to link against when an entity from this + /// module is used. + struct LinkLibrary { + LinkLibrary() : IsFramework(false) { } + LinkLibrary(const std::string &Library, bool IsFramework) + : Library(Library), IsFramework(IsFramework) { } + + /// \brief The library to link against. + /// + /// This will typically be a library or framework name, but can also + /// be an absolute path to the library or framework. + std::string Library; + + /// \brief Whether this is a framework rather than a library. + bool IsFramework; + }; + + /// \brief The set of libraries or frameworks to link against when + /// an entity from this module is used. + llvm::SmallVector LinkLibraries; + /// \brief Construct a top-level module. explicit Module(StringRef Name, SourceLocation DefinitionLoc, bool IsFramework) diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 2dbe136e89..a938697263 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -603,7 +603,9 @@ namespace clang { SUBMODULE_REQUIRES = 8, /// \brief Specifies a header that has been explicitly excluded /// from this submodule. - SUBMODULE_EXCLUDED_HEADER = 9 + SUBMODULE_EXCLUDED_HEADER = 9, + /// \brief Specifies a library or framework to link against. + SUBMODULE_LINK_LIBRARY = 10 }; /// \brief Record types used within a comments block. diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp index 417c7e5745..5e5e431d31 100644 --- a/lib/Basic/Module.cpp +++ b/lib/Basic/Module.cpp @@ -257,6 +257,16 @@ void Module::print(raw_ostream &OS, unsigned Indent) const { OS << "\n"; } + for (unsigned I = 0, N = LinkLibraries.size(); I != N; ++I) { + OS.indent(Indent + 2); + OS << "link "; + if (LinkLibraries[I].IsFramework) + OS << "framework "; + OS << "\""; + OS.write_escaped(LinkLibraries[I].Library); + OS << "\""; + } + if (InferSubmodules) { OS.indent(Indent + 2); if (InferExplicitSubmodules) diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 0257ce23f6..da37e4982d 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -32,6 +32,7 @@ #include "clang/Basic/Builtins.h" #include "clang/Basic/ConvertUTF.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/Module.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Frontend/CodeGenOptions.h" @@ -172,7 +173,8 @@ void CodeGenModule::Release() { EmitCtorList(GlobalDtors, "llvm.global_dtors"); EmitGlobalAnnotations(); EmitLLVMUsed(); - + EmitLinkLibraries(); + SimplifyPersonality(); if (getCodeGenOpts().EmitDeclMetadata) @@ -714,6 +716,24 @@ void CodeGenModule::EmitLLVMUsed() { GV->setSection("llvm.metadata"); } +void CodeGenModule::EmitLinkLibraries() { + // If there are no libraries to link against, do nothing. + if (LinkLibraries.empty()) + return; + + // Create metadata for each library we're linking against. + llvm::NamedMDNode *Metadata + = getModule().getOrInsertNamedMetadata("llvm.link.libraries"); + for (unsigned I = 0, N = LinkLibraries.size(); I != N; ++I) { + llvm::Value *Args[2] = { + llvm::MDString::get(getLLVMContext(), LinkLibraries[I].Library), + llvm::ConstantInt::get(llvm::Type::getInt1Ty(getLLVMContext()), + LinkLibraries[I].IsFramework) + }; + Metadata->addOperand(llvm::MDNode::get(getLLVMContext(), Args)); + } +} + void CodeGenModule::EmitDeferred() { // Emit code for any potentially referenced deferred decls. Since a // previously unused static decl may become used during the generation of code @@ -2681,7 +2701,6 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { case Decl::TypeAliasTemplate: case Decl::NamespaceAlias: case Decl::Block: - case Decl::Import: break; case Decl::CXXConstructor: // Skip function templates @@ -2762,6 +2781,53 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { break; } + case Decl::Import: { + ImportDecl *Import = cast(D); + + // Ignore import declarations that come from imported modules. + if (clang::Module *Owner = Import->getOwningModule()) { + if (getLangOpts().CurrentModule.empty() || + Owner->getTopLevelModule()->Name == getLangOpts().CurrentModule) + break; + } + + // Walk from this module up to its top-level module; we'll import all of + // these modules and their non-explicit child modules. + llvm::SmallVector Stack; + for (clang::Module *Mod = Import->getImportedModule(); Mod; + Mod = Mod->Parent) { + if (!ImportedModules.insert(Mod)) + break; + + Stack.push_back(Mod); + } + + // Find all of the non-explicit submodules of the modules we've imported and + // import them. + while (!Stack.empty()) { + clang::Module *Mod = Stack.back(); + Stack.pop_back(); + + // Add the link libraries for this module. + LinkLibraries.insert(LinkLibraries.end(), + Mod->LinkLibraries.begin(), + Mod->LinkLibraries.end()); + + // We've imported this module; now import any of its children that haven't + // already been imported. + for (clang::Module::submodule_iterator Sub = Mod->submodule_begin(), + SubEnd = Mod->submodule_end(); + Sub != SubEnd; ++Sub) { + if ((*Sub)->IsExplicit) + continue; + + if (ImportedModules.insert(*Sub)) + Stack.push_back(*Sub); + } + } + break; + } + default: // Make sure we handled everything we should, every other kind is a // non-top-level decl. FIXME: Would be nice to have an isTopLevelDeclKind diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index 34fa19c010..0d644a748e 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -23,7 +23,9 @@ #include "clang/AST/Mangle.h" #include "clang/Basic/ABI.h" #include "clang/Basic/LangOptions.h" +#include "clang/Basic/Module.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringMap.h" #include "llvm/IR/Module.h" @@ -66,6 +68,7 @@ namespace clang { class AnnotateAttr; class CXXDestructorDecl; class MangleBuffer; + class Module; namespace CodeGen { @@ -313,6 +316,12 @@ class CodeGenModule : public CodeGenTypeCache { /// run on termination. std::vector > CXXGlobalDtors; + /// \brief The complete set of modules that has been imported. + llvm::SetVector ImportedModules; + + /// \brief The set of libraries to link against. + std::vector LinkLibraries; + /// @name Cache for Objective-C runtime types /// @{ @@ -989,6 +998,9 @@ private: /// references to global which may otherwise be optimized out. void EmitLLVMUsed(); + /// \brief Emit the set of libraries to link against. + void EmitLinkLibraries(); + void EmitDeclMetadata(); /// EmitCoverageFile - Emit the llvm.gcov metadata used to tell LLVM where diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index d954bc9fc2..6b0eb79a10 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -645,6 +645,7 @@ namespace clang { ExplicitKeyword, ExportKeyword, FrameworkKeyword, + LinkKeyword, ModuleKeyword, Period, UmbrellaKeyword, @@ -732,6 +733,7 @@ namespace clang { void parseHeaderDecl(SourceLocation UmbrellaLoc, SourceLocation ExcludeLoc); void parseUmbrellaDirDecl(SourceLocation UmbrellaLoc); void parseExportDecl(); + void parseLinkDecl(); void parseInferredModuleDecl(bool Framework, bool Explicit); bool parseOptionalAttributes(Attributes &Attrs); @@ -774,6 +776,7 @@ retry: .Case("explicit", MMToken::ExplicitKeyword) .Case("export", MMToken::ExportKeyword) .Case("framework", MMToken::FrameworkKeyword) + .Case("link", MMToken::LinkKeyword) .Case("module", MMToken::ModuleKeyword) .Case("requires", MMToken::RequiresKeyword) .Case("umbrella", MMToken::UmbrellaKeyword) @@ -944,6 +947,7 @@ namespace { /// header-declaration /// submodule-declaration /// export-declaration +/// link-declaration /// /// submodule-declaration: /// module-declaration @@ -1123,7 +1127,11 @@ void ModuleMapParser::parseModuleDecl() { case MMToken::HeaderKeyword: parseHeaderDecl(SourceLocation(), SourceLocation()); break; - + + case MMToken::LinkKeyword: + parseLinkDecl(); + break; + default: Diags.Report(Tok.getLocation(), diag::err_mmap_expected_member); consumeToken(); @@ -1440,7 +1448,36 @@ void ModuleMapParser::parseExportDecl() { ActiveModule->UnresolvedExports.push_back(Unresolved); } -/// \brief Parse an inferried module declaration (wildcard modules). +/// \brief Parse a link declaration. +/// +/// module-declaration: +/// 'link' 'framework'[opt] string-literal +void ModuleMapParser::parseLinkDecl() { + assert(Tok.is(MMToken::LinkKeyword)); + SourceLocation LinkLoc = consumeToken(); + + // Parse the optional 'framework' keyword. + bool IsFramework = false; + if (Tok.is(MMToken::FrameworkKeyword)) { + consumeToken(); + IsFramework = true; + } + + // Parse the library name + if (!Tok.is(MMToken::StringLiteral)) { + Diags.Report(Tok.getLocation(), diag::err_mmap_expected_library_name) + << IsFramework << SourceRange(LinkLoc); + HadError = true; + return; + } + + std::string LibraryName = Tok.getString(); + consumeToken(); + ActiveModule->LinkLibraries.push_back(Module::LinkLibrary(LibraryName, + IsFramework)); +} + +/// \brief Parse an inferred module declaration (wildcard modules). /// /// module-declaration: /// 'explicit'[opt] 'framework'[opt] 'module' * attributes[opt] @@ -1679,13 +1716,14 @@ bool ModuleMapParser::parseModuleMapFile() { case MMToken::FrameworkKeyword: parseModuleDecl(); break; - + case MMToken::Comma: case MMToken::ExcludeKeyword: case MMToken::ExportKeyword: case MMToken::HeaderKeyword: case MMToken::Identifier: case MMToken::LBrace: + case MMToken::LinkKeyword: case MMToken::LSquare: case MMToken::Period: case MMToken::RBrace: diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 2ed8853bb2..81d3cea7ba 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -3413,6 +3413,9 @@ bool ASTReader::ReadSubmoduleBlock(ModuleFile &F) { DeserializationListener->ModuleRead(GlobalID, CurrentModule); SubmodulesLoaded[GlobalIndex] = CurrentModule; + + // Clear out link libraries; the module file has them. + CurrentModule->LinkLibraries.clear(); break; } @@ -3600,6 +3603,20 @@ bool ASTReader::ReadSubmoduleBlock(ModuleFile &F) { Context.getTargetInfo()); break; } + + case SUBMODULE_LINK_LIBRARY: + if (First) { + Error("missing submodule metadata record at beginning of block"); + return true; + } + + if (!CurrentModule) + break; + + CurrentModule->LinkLibraries.push_back( + Module::LinkLibrary(StringRef(BlobStart, BlobLen), + Record[0])); + break; } } } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index fe498bee77..d4b0367cac 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -2107,6 +2107,12 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name unsigned ExcludedHeaderAbbrev = Stream.EmitAbbrev(Abbrev); + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(SUBMODULE_LINK_LIBRARY)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsFramework + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name + unsigned LinkLibraryAbbrev = Stream.EmitAbbrev(Abbrev); + // Write the submodule metadata block. RecordData Record; Record.push_back(getNumberOfModules(WritingModule)); @@ -2209,7 +2215,16 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { } Stream.EmitRecord(SUBMODULE_EXPORTS, Record); } - + + // Emit the link libraries. + for (unsigned I = 0, N = Mod->LinkLibraries.size(); I != N; ++I) { + Record.clear(); + Record.push_back(SUBMODULE_LINK_LIBRARY); + Record.push_back(Mod->LinkLibraries[I].IsFramework); + Stream.EmitRecordWithBlob(LinkLibraryAbbrev, Record, + Mod->LinkLibraries[I].Library); + } + // Queue up the submodules of this module. for (Module::submodule_iterator Sub = Mod->submodule_begin(), SubEnd = Mod->submodule_end(); diff --git a/test/Modules/Inputs/autolink-sub.h b/test/Modules/Inputs/autolink-sub.h new file mode 100644 index 0000000000..60f9aa037e --- /dev/null +++ b/test/Modules/Inputs/autolink-sub.h @@ -0,0 +1 @@ +int autolink_sub(void); diff --git a/test/Modules/Inputs/autolink-sub2.h b/test/Modules/Inputs/autolink-sub2.h new file mode 100644 index 0000000000..c3ea7021ab --- /dev/null +++ b/test/Modules/Inputs/autolink-sub2.h @@ -0,0 +1 @@ +int autolink_sub2(void); diff --git a/test/Modules/Inputs/autolink.h b/test/Modules/Inputs/autolink.h new file mode 100644 index 0000000000..1014e29dc6 --- /dev/null +++ b/test/Modules/Inputs/autolink.h @@ -0,0 +1 @@ +extern int autolink; diff --git a/test/Modules/Inputs/module.map b/test/Modules/Inputs/module.map index 9657c165a8..702f5c11cd 100644 --- a/test/Modules/Inputs/module.map +++ b/test/Modules/Inputs/module.map @@ -124,3 +124,18 @@ module linkage_merge_left { header "linkage-merge-sub.h" } } + +module autolink { + header "autolink.h" + link "autolink" + + explicit module sub { + header "autolink-sub.h" + link "autolink_sub" + } + + explicit module sub2 { + header "autolink-sub2.h" + link framework "autolink_framework" + } +} diff --git a/test/Modules/autolink.m b/test/Modules/autolink.m new file mode 100644 index 0000000000..e4db6991fc --- /dev/null +++ b/test/Modules/autolink.m @@ -0,0 +1,18 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -emit-llvm -o - -fmodule-cache-path %t -fmodules -F %S/Inputs -I %S/Inputs %s | FileCheck %s + +@import autolink.sub2; + +int f() { + return autolink_sub2(); +} + +@import autolink; + +int g() { + return autolink; +} + +// CHECK: !llvm.link.libraries = !{![[AUTOLINK:[0-9]+]], ![[AUTOLINK_FRAMEWORK:[0-9]+]]} +// CHECK: ![[AUTOLINK]] = metadata !{metadata !"autolink", i1 false} +// CHECK: ![[AUTOLINK_FRAMEWORK]] = metadata !{metadata !"autolink_framework", i1 true}