Cache Code Completion results from PCH files

- Add CompilerInvocation::getPCHHash
  This will be used when creating a unique filename for a persistent
  precompiled bridging header.

- Automatically generate and use a precompiled briding header
  When we're given both -import-objc-header and -pch-output-dir
  arguments, we will try to:
  - Validate what we think the PCH filename should be for the bridging
    header, based on the Swift PCH hash and the clang module hash.
    - If we're successful, we'll just use it.
    - If it's out of date or something else is wrong, we'll try to
      emit it.
  - This gives us a single filename which we can `stat` to check for the
    validity of our code completion cache, which is keyed off of module
    name, module filename, and module file age.

- Cache code completion results from imported modules
  If we just have a single .PCH file imported, we can use that file as
  part of the key used to cache declarations in a module.  Because
  multiple files can contribute to the __ObjC module, we've always given
  it the phony filename "<imports>", which never exists, so `stat`-ing it
  always fails and we never cache declarations in it.

  This is extremely problematic for projects with huge bridging headers.
  In the case where we have a single PCH import, this can bring warm code
  completion times down to about 500ms from over 2-3s, so it can provide a
  nice performance win for IDEs.

- Add a new test that performs two code-completion requests with a bridging header.
- Add some -pch-output-dir flags to existing SourceKit tests that import a bridging
  header.

rdar://problem/31198982
This commit is contained in:
David Farler
2017-03-22 11:44:08 -07:00
parent f4640bdbc7
commit 65668c9d82
23 changed files with 329 additions and 22 deletions

View File

@@ -209,6 +209,12 @@ public:
return OutputKind == IRGenOutputKind::LLVMAssembly; return OutputKind == IRGenOutputKind::LLVMAssembly;
} }
} }
/// Return a hash code of any components from these options that should
/// contribute to a Swift Bridging PCH hash.
llvm::hash_code getPCHHashComponents() const {
return llvm::hash_value(0);
}
}; };
} // end namespace swift } // end namespace swift

View File

@@ -19,6 +19,7 @@
#define SWIFT_AST_SILOPTIONS_H #define SWIFT_AST_SILOPTIONS_H
#include "swift/Basic/Sanitizers.h" #include "swift/Basic/Sanitizers.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringRef.h"
#include <string> #include <string>
#include <climits> #include <climits>
@@ -147,6 +148,11 @@ public:
SILOptions() : Sanitize(SanitizerKind::None) {} SILOptions() : Sanitize(SanitizerKind::None) {}
/// Return a hash code of any components from these options that should
/// contribute to a Swift Bridging PCH hash.
llvm::hash_code getPCHHashComponents() const {
return llvm::hash_value(0);
}
}; };
} // end namespace swift } // end namespace swift

View File

@@ -13,6 +13,8 @@
#ifndef SWIFT_AST_SEARCHPATHOPTIONS_H #ifndef SWIFT_AST_SEARCHPATHOPTIONS_H
#define SWIFT_AST_SEARCHPATHOPTIONS_H #define SWIFT_AST_SEARCHPATHOPTIONS_H
#include "llvm/ADT/Hashing.h"
#include <string> #include <string>
#include <vector> #include <vector>
@@ -67,6 +69,26 @@ public:
/// Don't look in for compiler-provided modules. /// Don't look in for compiler-provided modules.
bool SkipRuntimeLibraryImportPath = false; bool SkipRuntimeLibraryImportPath = false;
/// Return a hash code of any components from these options that should
/// contribute to a Swift Bridging PCH hash.
llvm::hash_code getPCHHashComponents() const {
using llvm::hash_value;
using llvm::hash_combine;
auto Code = hash_value(SDKPath);
for (auto Import : ImportSearchPaths) {
Code = hash_combine(Code, Import);
}
for (const auto &FrameworkPath : FrameworkSearchPaths) {
Code = hash_combine(Code, FrameworkPath.Path);
}
for (auto LibraryPath : LibrarySearchPaths) {
Code = hash_combine(Code, LibraryPath);
}
Code = hash_combine(Code, RuntimeResourcePath);
Code = hash_combine(Code, RuntimeLibraryImportPath);
return Code;
}
}; };
} }

View File

@@ -49,6 +49,13 @@ public:
/// Treat all warnings as errors /// Treat all warnings as errors
bool WarningsAsErrors = false; bool WarningsAsErrors = false;
/// Return a hash code of any components from these options that should
/// contribute to a Swift Bridging PCH hash.
llvm::hash_code getPCHHashComponents() const {
// Nothing here that contributes anything significant when emitting the PCH.
return llvm::hash_value(0);
}
}; };
} // end namespace swift } // end namespace swift

View File

@@ -22,9 +22,12 @@
#include "swift/Basic/Version.h" #include "swift/Basic/Version.h"
#include "clang/Basic/VersionTuple.h" #include "clang/Basic/VersionTuple.h"
#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringRef.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Triple.h" #include "llvm/ADT/Triple.h"
#include "llvm/Support/raw_ostream.h"
#include <string> #include <string>
#include <vector> #include <vector>
@@ -278,6 +281,17 @@ namespace swift {
PlatformConditionKind Kind, StringRef Value, PlatformConditionKind Kind, StringRef Value,
std::vector<StringRef> &suggestions); std::vector<StringRef> &suggestions);
/// Return a hash code of any components from these options that should
/// contribute to a Swift Bridging PCH hash.
llvm::hash_code getPCHHashComponents() const {
auto code = llvm::hash_value(Target.str());
SmallString<16> Scratch;
llvm::raw_svector_ostream OS(Scratch);
OS << EffectiveLanguageVersion;
code = llvm::hash_combine(code, OS.str());
return code;
}
private: private:
llvm::SmallVector<std::pair<PlatformConditionKind, std::string>, llvm::SmallVector<std::pair<PlatformConditionKind, std::string>,
NumPlatformConditionKind> NumPlatformConditionKind>

View File

@@ -31,6 +31,7 @@ namespace clang {
class CodeGenOptions; class CodeGenOptions;
class Decl; class Decl;
class DependencyCollector; class DependencyCollector;
class DiagnosticConsumer;
class EnumConstantDecl; class EnumConstantDecl;
class EnumDecl; class EnumDecl;
class MacroInfo; class MacroInfo;
@@ -44,6 +45,7 @@ namespace clang {
namespace swift { namespace swift {
class ASTContext; class ASTContext;
class CompilerInvocation;
class ClangImporterOptions; class ClangImporterOptions;
class ClangModuleUnit; class ClangModuleUnit;
class ClangNode; class ClangNode;
@@ -78,14 +80,19 @@ public:
/// \param ctx The ASTContext into which the module will be imported. /// \param ctx The ASTContext into which the module will be imported.
/// The ASTContext's SearchPathOptions will be used for the Clang importer. /// The ASTContext's SearchPathOptions will be used for the Clang importer.
/// ///
/// \param clangImporterOpts The options to use for the Clang importer. /// \param importerOpts The options to use for the Clang importer.
///
/// \param swiftPCHHash A hash of Swift's various options in a compiler
/// invocation, used to create a unique Bridging PCH if requested.
/// ///
/// \param tracker The object tracking files this compilation depends on. /// \param tracker The object tracking files this compilation depends on.
/// ///
/// \returns a new Clang module importer, or null (with a diagnostic) if /// \returns a new Clang module importer, or null (with a diagnostic) if
/// an error occurred. /// an error occurred.
static std::unique_ptr<ClangImporter> static std::unique_ptr<ClangImporter>
create(ASTContext &ctx, const ClangImporterOptions &clangImporterOpts, create(ASTContext &ctx,
const ClangImporterOptions &importerOpts,
std::string swiftPCHHash = "",
DependencyTracker *tracker = nullptr); DependencyTracker *tracker = nullptr);
ClangImporter(const ClangImporter &) = delete; ClangImporter(const ClangImporter &) = delete;
@@ -234,7 +241,14 @@ public:
/// replica. /// replica.
/// ///
/// \sa clang::GeneratePCHAction /// \sa clang::GeneratePCHAction
bool emitBridgingPCH(StringRef headerPath, StringRef outputPCHPath); bool emitBridgingPCH(StringRef headerPath,
StringRef outputPCHPath,
clang::DiagnosticConsumer *Diags = nullptr);
/// Returns true if a clang CompilerInstance can successfully read in a PCH,
/// assuming it exists, with the current options. This can be used to find out
/// if we need to persist a PCH for later reuse.
bool canReadPCH(StringRef PCHFilename);
const clang::Module *getClangOwningModule(ClangNode Node) const; const clang::Module *getClangOwningModule(ClangNode Node) const;
bool hasTypedef(const clang::Decl *typeDecl) const; bool hasTypedef(const clang::Decl *typeDecl) const;
@@ -291,6 +305,13 @@ public:
DeclName importName(const clang::NamedDecl *D, DeclName importName(const clang::NamedDecl *D,
clang::DeclarationName givenName); clang::DeclarationName givenName);
Optional<std::string>
getOrCreatePCH(const ClangImporterOptions &ImporterOptions,
const std::string &SwiftPCHHash);
Optional<std::string>
getPCHFilename(const ClangImporterOptions &ImporterOptions,
const std::string &SwiftPCHHash);
}; };
ImportDecl *createImportDecl(ASTContext &Ctx, DeclContext *DC, ClangNode ClangN, ImportDecl *createImportDecl(ASTContext &Ctx, DeclContext *DC, ClangNode ClangN,

View File

@@ -13,6 +13,8 @@
#ifndef SWIFT_CLANGIMPORTER_CLANGIMPORTEROPTIONS_H #ifndef SWIFT_CLANGIMPORTER_CLANGIMPORTEROPTIONS_H
#define SWIFT_CLANGIMPORTER_CLANGIMPORTEROPTIONS_H #define SWIFT_CLANGIMPORTER_CLANGIMPORTEROPTIONS_H
#include "llvm/ADT/Hashing.h"
#include <string> #include <string>
#include <vector> #include <vector>
@@ -43,7 +45,7 @@ public:
std::string PrecompiledHeaderOutputDir; std::string PrecompiledHeaderOutputDir;
/// \see Mode /// \see Mode
enum class Modes { enum class Modes : uint8_t {
/// Set up Clang for importing modules into Swift and generating IR from /// Set up Clang for importing modules into Swift and generating IR from
/// Swift code. /// Swift code.
Normal, Normal,
@@ -84,6 +86,28 @@ public:
/// When set, don't look for or load adapter modules. /// When set, don't look for or load adapter modules.
bool DisableAdapterModules = false; bool DisableAdapterModules = false;
/// Return a hash code of any components from these options that should
/// contribute to a Swift Bridging PCH hash.
llvm::hash_code getPCHHashComponents() const {
using llvm::hash_value;
using llvm::hash_combine;
auto Code = hash_value(ModuleCachePath);
// ExtraArgs ignored - already considered in Clang's module hashing.
Code = hash_combine(Code, OverrideResourceDir);
Code = hash_combine(Code, TargetCPU);
Code = hash_combine(Code, BridgingHeader);
Code = hash_combine(Code, PrecompiledHeaderOutputDir);
Code = hash_combine(Code, static_cast<uint8_t>(Mode));
Code = hash_combine(Code, DetailedPreprocessingRecord);
Code = hash_combine(Code, ImportForwardDeclarations);
Code = hash_combine(Code, InferImportAsMember);
Code = hash_combine(Code, DisableSwiftBridgeAttr);
Code = hash_combine(Code, DisableModulesValidateSystemHeaders);
Code = hash_combine(Code, DisableAdapterModules);
return Code;
}
}; };
} // end namespace swift } // end namespace swift

View File

@@ -292,6 +292,11 @@ public:
bool isDelayedFunctionBodyParsing() const { bool isDelayedFunctionBodyParsing() const {
return FrontendOpts.DelayedFunctionBodyParsing; return FrontendOpts.DelayedFunctionBodyParsing;
} }
/// Retrieve a module hash string that is suitable for uniquely
/// identifying the conditions under which the module was built, for use
/// in generating a cached PCH file for the bridging header.
std::string getPCHHash() const;
}; };
/// A class which manages the state and execution of the compiler. /// A class which manages the state and execution of the compiler.

View File

@@ -14,6 +14,7 @@
#define SWIFT_FRONTEND_FRONTENDOPTIONS_H #define SWIFT_FRONTEND_FRONTENDOPTIONS_H
#include "swift/AST/Module.h" #include "swift/AST/Module.h"
#include "llvm/ADT/Hashing.h"
#include <string> #include <string>
#include <vector> #include <vector>
@@ -305,6 +306,12 @@ public:
OutputFilenames.clear(); OutputFilenames.clear();
OutputFilenames.push_back(FileName); OutputFilenames.push_back(FileName);
} }
/// Return a hash code of any components from these options that should
/// contribute to a Swift Bridging PCH hash.
llvm::hash_code getPCHHashComponents() const {
return llvm::hash_value(0);
}
}; };
} }

View File

@@ -18,6 +18,7 @@
#include "swift/Basic/LangOptions.h" #include "swift/Basic/LangOptions.h"
#include "swift/Basic/Range.h" #include "swift/Basic/Range.h"
#include "swift/Config.h" #include "swift/Config.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"
#include <limits.h> #include <limits.h>

View File

@@ -145,27 +145,44 @@ namespace {
ASTContext &Ctx; ASTContext &Ctx;
ClangImporter &Importer; ClangImporter &Importer;
ClangImporter::Implementation &Impl; ClangImporter::Implementation &Impl;
const ClangImporterOptions &ImporterOpts;
std::string SwiftPCHHash;
public: public:
explicit ParsingAction(ASTContext &ctx, explicit ParsingAction(ASTContext &ctx,
ClangImporter &importer, ClangImporter &importer,
ClangImporter::Implementation &impl) ClangImporter::Implementation &impl,
: Ctx(ctx), Importer(importer), Impl(impl) {} const ClangImporterOptions &importerOpts,
std::string swiftPCHHash)
: Ctx(ctx), Importer(importer), Impl(impl), ImporterOpts(importerOpts),
SwiftPCHHash(swiftPCHHash) {}
std::unique_ptr<clang::ASTConsumer> std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance &CI, StringRef InFile) override { CreateASTConsumer(clang::CompilerInstance &CI, StringRef InFile) override {
return llvm::make_unique<HeaderParsingASTConsumer>(Impl); return llvm::make_unique<HeaderParsingASTConsumer>(Impl);
} }
bool BeginSourceFileAction(CompilerInstance &CI, bool BeginSourceFileAction(clang::CompilerInstance &CI,
StringRef Filename) override { StringRef Filename) override {
// Prefer frameworks over plain headers. // Prefer frameworks over plain headers.
// We add search paths here instead of when building the initial invocation // We add search paths here instead of when building the initial invocation
// so that (a) we use the same code as search paths for imported modules, // so that (a) we use the same code as search paths for imported modules,
// and (b) search paths are always added after -Xcc options. // and (b) search paths are always added after -Xcc options.
SearchPathOptions &searchPathOpts = Ctx.SearchPathOpts; SearchPathOptions &searchPathOpts = Ctx.SearchPathOpts;
for (const auto &framepath : searchPathOpts.FrameworkSearchPaths) for (const auto &framepath : searchPathOpts.FrameworkSearchPaths) {
Importer.addSearchPath(framepath.Path, /*isFramework*/true, Importer.addSearchPath(framepath.Path, /*isFramework*/true,
framepath.IsSystem); framepath.IsSystem);
for (auto path : searchPathOpts.ImportSearchPaths) }
for (auto path : searchPathOpts.ImportSearchPaths) {
Importer.addSearchPath(path, /*isFramework*/false, /*isSystem=*/false); Importer.addSearchPath(path, /*isFramework*/false, /*isSystem=*/false);
}
auto PCH = Importer.getOrCreatePCH(ImporterOpts, SwiftPCHHash);
if (PCH.hasValue()) {
Impl.getClangInstance()->getPreprocessorOpts().ImplicitPCHInclude =
PCH.getValue();
Impl.IsReadingBridgingPCH = true;
Impl.setSinglePCHImport(PCH.getValue());
}
return true; return true;
} }
}; };
@@ -692,11 +709,73 @@ addCommonInvocationArguments(std::vector<std::string> &invocationArgStrs,
llvm::itostr(ctx.LangOpts.EffectiveLanguageVersion[0])); llvm::itostr(ctx.LangOpts.EffectiveLanguageVersion[0]));
} }
bool ClangImporter::canReadPCH(StringRef PCHFilename) {
return clang::ASTReader::isAcceptableASTFile(PCHFilename,
Impl.Instance->getFileManager(),
Impl.Instance->getPCHContainerReader(),
Impl.Instance->getLangOpts(),
Impl.Instance->getTargetOpts(),
Impl.Instance->getPreprocessorOpts(),
Impl.Instance->getSpecificModuleCachePath());
}
Optional<std::string>
ClangImporter::getPCHFilename(const ClangImporterOptions &ImporterOptions,
const std::string &SwiftPCHHash) {
if (llvm::sys::path::extension(ImporterOptions.BridgingHeader)
.endswith(PCH_EXTENSION)) {
return ImporterOptions.BridgingHeader;
}
const auto &BridgingHeader = ImporterOptions.BridgingHeader;
const auto &PCHOutputDir = ImporterOptions.PrecompiledHeaderOutputDir;
if (SwiftPCHHash.empty() || BridgingHeader.empty() || PCHOutputDir.empty()) {
return None;
}
SmallString<256> PCHBasename { llvm::sys::path::filename(BridgingHeader) };
llvm::sys::path::replace_extension(PCHBasename, "");
PCHBasename.append("-swift_");
PCHBasename.append(SwiftPCHHash);
PCHBasename.append("-clang_");
PCHBasename.append(getClangModuleHash());
llvm::sys::path::replace_extension(PCHBasename, ".pch");
SmallString<256> PCHFilename { PCHOutputDir };
llvm::sys::path::append(PCHFilename, PCHBasename);
return PCHFilename.str().str();
}
Optional<std::string>
ClangImporter::getOrCreatePCH(const ClangImporterOptions &ImporterOptions,
const std::string &SwiftPCHHash) {
auto PCHFilename = getPCHFilename(ImporterOptions, SwiftPCHHash);
if (!PCHFilename.hasValue()) {
return None;
}
if (!canReadPCH(PCHFilename.getValue())) {
SmallString<256> Message;
llvm::raw_svector_ostream OS(Message);
auto Diags = new clang::TextDiagnosticPrinter {
llvm::errs(),
&Impl.Instance->getDiagnosticOpts()
};
auto FailedToEmit = emitBridgingPCH(ImporterOptions.BridgingHeader,
PCHFilename.getValue(),
Diags);
if (FailedToEmit) {
return None;
}
}
return PCHFilename.getValue();
}
std::unique_ptr<ClangImporter> std::unique_ptr<ClangImporter>
ClangImporter::create(ASTContext &ctx, ClangImporter::create(ASTContext &ctx,
const ClangImporterOptions &importerOpts, const ClangImporterOptions &importerOpts,
std::string swiftPCHHash,
DependencyTracker *tracker) { DependencyTracker *tracker) {
std::unique_ptr<ClangImporter> importer{ std::unique_ptr<ClangImporter> importer{
new ClangImporter(ctx, importerOpts, tracker) new ClangImporter(ctx, importerOpts, tracker)
}; };
@@ -731,6 +810,7 @@ ClangImporter::create(ASTContext &ctx,
if (llvm::sys::path::extension(importerOpts.BridgingHeader).endswith( if (llvm::sys::path::extension(importerOpts.BridgingHeader).endswith(
PCH_EXTENSION)) { PCH_EXTENSION)) {
importer->Impl.setSinglePCHImport(importerOpts.BridgingHeader);
importer->Impl.IsReadingBridgingPCH = true; importer->Impl.IsReadingBridgingPCH = true;
if (tracker) { if (tracker) {
// Currently ignoring dependency on bridging .pch files because they are // Currently ignoring dependency on bridging .pch files because they are
@@ -751,8 +831,9 @@ ClangImporter::create(ASTContext &ctx,
new ClangDiagnosticConsumer(importer->Impl, *diagnosticOpts, new ClangDiagnosticConsumer(importer->Impl, *diagnosticOpts,
importerOpts.DumpClangDiagnostics) importerOpts.DumpClangDiagnostics)
}; };
auto clangDiags = CompilerInstance::createDiagnostics(diagnosticOpts.get(), auto clangDiags =
diagClient.release()); clang::CompilerInstance::createDiagnostics(diagnosticOpts.get(),
diagClient.release());
// Create a new Clang compiler invocation. // Create a new Clang compiler invocation.
importer->Impl.Invocation = importer->Impl.Invocation =
@@ -794,7 +875,8 @@ ClangImporter::create(ASTContext &ctx,
llvm::make_unique<clang::ObjectFilePCHContainerWriter>()); llvm::make_unique<clang::ObjectFilePCHContainerWriter>());
PCHContainerOperations->registerReader( PCHContainerOperations->registerReader(
llvm::make_unique<clang::ObjectFilePCHContainerReader>()); llvm::make_unique<clang::ObjectFilePCHContainerReader>());
importer->Impl.Instance.reset(new CompilerInstance(PCHContainerOperations)); importer->Impl.Instance.reset(
new clang::CompilerInstance(PCHContainerOperations));
auto &instance = *importer->Impl.Instance; auto &instance = *importer->Impl.Instance;
if (tracker) if (tracker)
instance.addDependencyCollector(tracker->getClangCollector()); instance.addDependencyCollector(tracker->getClangCollector());
@@ -804,7 +886,9 @@ ClangImporter::create(ASTContext &ctx,
// Create the associated action. // Create the associated action.
importer->Impl.Action.reset(new ParsingAction(ctx, *importer, importer->Impl.Action.reset(new ParsingAction(ctx, *importer,
importer->Impl)); importer->Impl,
importerOpts,
swiftPCHHash));
auto *action = importer->Impl.Action.get(); auto *action = importer->Impl.Action.get();
// Execute the action. We effectively inline most of // Execute the action. We effectively inline most of
@@ -934,6 +1018,7 @@ bool ClangImporter::Implementation::importHeader(
bool trackParsedSymbols, bool trackParsedSymbols,
std::unique_ptr<llvm::MemoryBuffer> sourceBuffer, std::unique_ptr<llvm::MemoryBuffer> sourceBuffer,
bool implicitImport) { bool implicitImport) {
// Don't even try to load the bridging header if the Clang AST is in a bad // Don't even try to load the bridging header if the Clang AST is in a bad
// state. It could cause a crash. // state. It could cause a crash.
auto &clangDiags = getClangASTContext().getDiagnostics(); auto &clangDiags = getClangASTContext().getDiagnostics();
@@ -1061,6 +1146,11 @@ bool ClangImporter::importHeader(StringRef header, ModuleDecl *adapter,
return importBridgingHeader(header, adapter, diagLoc, false, true); return importBridgingHeader(header, adapter, diagLoc, false, true);
} }
// If we've made it to here, this is some header other than the bridging
// header, which means we can no longer rely on one file's modification time
// to invalid code completion caches. :-(
Impl.setSinglePCHImport(None);
if (!cachedContents.empty() && cachedContents.back() == '\0') if (!cachedContents.empty() && cachedContents.back() == '\0')
cachedContents = cachedContents.drop_back(); cachedContents = cachedContents.drop_back();
std::unique_ptr<llvm::MemoryBuffer> sourceBuffer{ std::unique_ptr<llvm::MemoryBuffer> sourceBuffer{
@@ -1082,6 +1172,7 @@ bool ClangImporter::importBridgingHeader(StringRef header, ModuleDecl *adapter,
Impl.handleDeferredImports(); Impl.handleDeferredImports();
return false; return false;
} }
clang::FileManager &fileManager = Impl.Instance->getFileManager(); clang::FileManager &fileManager = Impl.Instance->getFileManager();
const clang::FileEntry *headerFile = fileManager.getFile(header, const clang::FileEntry *headerFile = fileManager.getFile(header,
/*OpenFile=*/true); /*OpenFile=*/true);
@@ -1152,7 +1243,8 @@ std::string ClangImporter::getBridgingHeaderContents(StringRef headerPath,
bool bool
ClangImporter::emitBridgingPCH(StringRef headerPath, ClangImporter::emitBridgingPCH(StringRef headerPath,
StringRef outputPCHPath) { StringRef outputPCHPath,
clang::DiagnosticConsumer *Diags) {
auto invocation = std::make_shared<clang::CompilerInvocation> auto invocation = std::make_shared<clang::CompilerInvocation>
(clang::CompilerInvocation(*Impl.Invocation)); (clang::CompilerInvocation(*Impl.Invocation));
invocation->getFrontendOpts().DisableFree = false; invocation->getFrontendOpts().DisableFree = false;
@@ -1165,8 +1257,12 @@ ClangImporter::emitBridgingPCH(StringRef headerPath,
clang::CompilerInstance emitInstance( clang::CompilerInstance emitInstance(
Impl.Instance->getPCHContainerOperations()); Impl.Instance->getPCHContainerOperations());
emitInstance.setInvocation(std::move(invocation)); emitInstance.setInvocation(std::move(invocation));
emitInstance.createDiagnostics(&Impl.Instance->getDiagnosticClient(),
false); auto ReusingDiags = Diags == nullptr;
if (ReusingDiags) {
Diags = &Impl.Instance->getDiagnosticClient();
}
emitInstance.createDiagnostics(Diags, /*ShouldOwnDiags=*/!ReusingDiags);
clang::FileManager &fileManager = Impl.Instance->getFileManager(); clang::FileManager &fileManager = Impl.Instance->getFileManager();
emitInstance.setFileManager(&fileManager); emitInstance.setFileManager(&fileManager);
@@ -2446,8 +2542,14 @@ void ClangModuleUnit::collectLinkLibraries(
} }
StringRef ClangModuleUnit::getFilename() const { StringRef ClangModuleUnit::getFilename() const {
if (!clangModule) if (!clangModule) {
return "<imports>"; auto SinglePCH = owner.getSinglePCHImport();
if (SinglePCH.hasValue()) {
return SinglePCH.getValue();
} else {
return "<imports>";
}
}
if (const clang::FileEntry *F = clangModule->getASTFile()) if (const clang::FileEntry *F = clangModule->getASTFile())
if (!F->getName().empty()) if (!F->getName().empty())
return F->getName(); return F->getName();

View File

@@ -29,6 +29,7 @@
#include "swift/AST/Type.h" #include "swift/AST/Type.h"
#include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/ForeignErrorConvention.h"
#include "swift/Basic/StringExtras.h" #include "swift/Basic/StringExtras.h"
#include "swift/Strings.h"
#include "clang/AST/ASTContext.h" #include "clang/AST/ASTContext.h"
#include "clang/AST/DeclVisitor.h" #include "clang/AST/DeclVisitor.h"
#include "clang/Basic/IdentifierTable.h" #include "clang/Basic/IdentifierTable.h"
@@ -40,6 +41,7 @@
#include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/TinyPtrVector.h" #include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Support/Path.h"
#include <set> #include <set>
namespace llvm { namespace llvm {
@@ -386,6 +388,10 @@ public:
llvm::DenseMap<std::pair<ObjCSelector, char>, unsigned> llvm::DenseMap<std::pair<ObjCSelector, char>, unsigned>
ActiveSelectors; ActiveSelectors;
clang::CompilerInstance *getClangInstance() {
return Instance.get();
}
private: private:
/// \brief Generation number that is used for crude versioning. /// \brief Generation number that is used for crude versioning.
/// ///
@@ -532,6 +538,11 @@ private:
/// after having set up a suitable Clang instance. /// after having set up a suitable Clang instance.
std::unique_ptr<importer::NameImporter> nameImporter = nullptr; std::unique_ptr<importer::NameImporter> nameImporter = nullptr;
/// If there is a single .PCH file imported into the __ObjC module, this
/// is the filename of that PCH. When other files are imported, this should
/// be llvm::None.
Optional<std::string> SinglePCHImport = None;
public: public:
importer::NameImporter &getNameImporter() { importer::NameImporter &getNameImporter() {
assert(nameImporter && "haven't finished initialization"); assert(nameImporter && "haven't finished initialization");
@@ -1158,6 +1169,22 @@ public:
/// Dump the Swift-specific name lookup tables we generate. /// Dump the Swift-specific name lookup tables we generate.
void dumpSwiftLookupTables(); void dumpSwiftLookupTables();
void setSinglePCHImport(Optional<std::string> PCHFilename) {
if (PCHFilename.hasValue()) {
assert(llvm::sys::path::extension(PCHFilename.getValue())
.endswith(PCH_EXTENSION) &&
"Single PCH imported filename doesn't have .pch extension!");
}
SinglePCHImport = PCHFilename;
}
/// If there was is a single .pch bridging header without other imported
/// files, we can provide the PCH filename for declaration caching,
/// especially in code completion. If there are other
Optional<std::string> getSinglePCHImport() const {
return SinglePCHImport;
}
}; };
namespace importer { namespace importer {

View File

@@ -1583,7 +1583,6 @@ bool CompilerInvocation::parseArgs(ArrayRef<const char *> Args,
return false; return false;
} }
serialization::Status serialization::Status
CompilerInvocation::loadFromSerializedAST(StringRef data) { CompilerInvocation::loadFromSerializedAST(StringRef data) {
serialization::ExtendedValidationInfo extendedInfo; serialization::ExtendedValidationInfo extendedInfo;

View File

@@ -27,6 +27,7 @@
#include "swift/Parse/Lexer.h" #include "swift/Parse/Lexer.h"
#include "swift/SIL/SILModule.h" #include "swift/SIL/SILModule.h"
#include "swift/Serialization/SerializedModuleLoader.h" #include "swift/Serialization/SerializedModuleLoader.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Triple.h" #include "llvm/ADT/Triple.h"
#include "llvm/Support/CommandLine.h" #include "llvm/Support/CommandLine.h"
@@ -35,6 +36,22 @@
using namespace swift; using namespace swift;
std::string CompilerInvocation::getPCHHash() const {
using llvm::hash_code;
using llvm::hash_value;
using llvm::hash_combine;
auto Code = hash_value(LangOpts.getPCHHashComponents());
Code = hash_combine(Code, FrontendOpts.getPCHHashComponents());
Code = hash_combine(Code, ClangImporterOpts.getPCHHashComponents());
Code = hash_combine(Code, SearchPathOpts.getPCHHashComponents());
Code = hash_combine(Code, DiagnosticOpts.getPCHHashComponents());
Code = hash_combine(Code, SILOpts.getPCHHashComponents());
Code = hash_combine(Code, IRGenOpts.getPCHHashComponents());
return llvm::APInt(64, Code).toString(36, /*Signed=*/false);
}
void CompilerInstance::createSILModule(bool WholeModule) { void CompilerInstance::createSILModule(bool WholeModule) {
assert(MainModule && "main module not created yet"); assert(MainModule && "main module not created yet");
TheSILModule = SILModule::createEmptyModule(getMainModule(), TheSILModule = SILModule::createEmptyModule(getMainModule(),
@@ -103,6 +120,7 @@ bool CompilerInstance::setup(const CompilerInvocation &Invok) {
// knowledge. // knowledge.
auto clangImporter = auto clangImporter =
ClangImporter::create(*Context, Invocation.getClangImporterOptions(), ClangImporter::create(*Context, Invocation.getClangImporterOptions(),
Invocation.getPCHHash(),
DepTracker); DepTracker);
if (!clangImporter) { if (!clangImporter) {
Diagnostics.diagnose(SourceLoc(), diag::error_clang_importer_create_fail); Diagnostics.diagnose(SourceLoc(), diag::error_clang_importer_create_fail);

View File

@@ -367,7 +367,8 @@ static bool performCompile(std::unique_ptr<CompilerInstance> &Instance,
auto clangImporter = static_cast<ClangImporter *>( auto clangImporter = static_cast<ClangImporter *>(
Instance->getASTContext().getClangModuleLoader()); Instance->getASTContext().getClangModuleLoader());
return clangImporter->emitBridgingPCH( return clangImporter->emitBridgingPCH(
Invocation.getInputFilenames()[0], opts.getSingleOutputFilename()); Invocation.getInputFilenames()[0],
opts.getSingleOutputFilename());
} }
IRGenOptions &IRGenOpts = Invocation.getIRGenOptions(); IRGenOptions &IRGenOpts = Invocation.getIRGenOptions();

View File

@@ -1,5 +1,9 @@
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TOP -import-objc-header %S/Inputs/header.h | %FileCheck %s -check-prefix=CHECK-TOP // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TOP -import-objc-header %S/Inputs/header.h | %FileCheck %s -check-prefix=CHECK-TOP
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE -import-objc-header %S/Inputs/header.h | %FileCheck %s -check-prefix=CHECK-TYPE // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TYPE -import-objc-header %S/Inputs/header.h | %FileCheck %s -check-prefix=CHECK-TYPE
// RUN: rm -rf %t && mkdir -p %t
// RUN: %target-swift-ide-test -code-completion -pch-output-dir %t -source-filename %s -code-completion-token=TOP -import-objc-header %S/Inputs/header.h | %FileCheck %s -check-prefix=CHECK-TOP
// RUN: %target-swift-ide-test -code-completion -pch-output-dir %t -source-filename %s -code-completion-token=TYPE -import-objc-header %S/Inputs/header.h | %FileCheck %s -check-prefix=CHECK-TYPE
// RUN: stat %t/*.pch
// REQUIRES: objc_interop // REQUIRES: objc_interop

View File

@@ -0,0 +1,13 @@
// REQUIRES: objc_interop
// RUN: rm -rf %t && mkdir -p %t
// RUN: %sourcekitd-test -req=complete -pos=8:5 %s -- %s -module-name Mixed -pch-output-dir %t -import-objc-header %S/Inputs/header.h == -req=complete -pos=8:5 %s -- %s -module-name Mixed -pch-output-dir %t -import-objc-header %S/Inputs/header.h | %FileCheck %s --check-prefix=CHECK-MEMBERS
// RUN: %sourcekitd-test -req=complete -pos=9:1 %s -- %s -module-name Mixed -pch-output-dir %t -import-objc-header %S/Inputs/header.h == -req=complete -pos=9:1 %s -- %s -module-name Mixed -pch-output-dir %t -import-objc-header %S/Inputs/header.h | %FileCheck %s --check-prefix=CHECK-GLOBALS
// RUN: stat %t/*.pch
func foo(x: BaseInHead) {
x.
}
// CHECK-GLOBALS: doSomethingInHead(:)
// CHECK-GLOBALS: test1(:)
// CHECK-MEMBERS: doIt(:)

View File

@@ -10,6 +10,10 @@ func test(_ b : BaseInHead) {
// RUN: %sourcekitd-test -req=cursor -pos=3:7 %s -- %s %mcp_opt -module-name Mixed -import-objc-header %S/Inputs/header.h | %FileCheck %s // RUN: %sourcekitd-test -req=cursor -pos=3:7 %s -- %s %mcp_opt -module-name Mixed -import-objc-header %S/Inputs/header.h | %FileCheck %s
// RUN: rm -rf %t && mkdir -p %t
// RUN: %sourcekitd-test -req=cursor -pos=3:7 %s -- %s %mcp_opt -module-name Mixed -pch-output-dir %t -import-objc-header %S/Inputs/header.h | %FileCheck %s
// RUN: stat %t/*.pch
// CHECK: source.lang.swift.ref.function.method.instance ({{.*}}Inputs/header.h:4:9-4:23) // CHECK: source.lang.swift.ref.function.method.instance ({{.*}}Inputs/header.h:4:9-4:23)
// CHECK: doIt(_:) // CHECK: doIt(_:)
// CHECK: c:objc(cs)BaseInHead(im)doIt: // CHECK: c:objc(cs)BaseInHead(im)doIt:

View File

@@ -17,6 +17,7 @@
#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Mutex.h" #include "llvm/Support/Mutex.h"
#include "llvm/Support/TargetSelect.h"
#include <chrono> #include <chrono>
#include <xpc/xpc.h> #include <xpc/xpc.h>
#include <dispatch/dispatch.h> #include <dispatch/dispatch.h>
@@ -250,6 +251,11 @@ static void handleInterruptedConnection(xpc_object_t event, xpc_connection_t con
void sourcekitd::initialize() { void sourcekitd::initialize() {
initializeTracing(); initializeTracing();
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmPrinters();
llvm::InitializeAllAsmParsers();
assert(!GlobalConn); assert(!GlobalConn);
GlobalConn = xpc_connection_create(SOURCEKIT_XPCSERVICE_IDENTIFIER, nullptr); GlobalConn = xpc_connection_create(SOURCEKIT_XPCSERVICE_IDENTIFIER, nullptr);

View File

@@ -35,6 +35,7 @@
#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h" #include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"
#include "llvm/Support/TargetSelect.h"
#include <mutex> #include <mutex>
// FIXME: Portability. // FIXME: Portability.
@@ -149,6 +150,11 @@ static void onDocumentUpdateNotification(StringRef DocumentName) {
static SourceKit::Context *GlobalCtx = nullptr; static SourceKit::Context *GlobalCtx = nullptr;
void sourcekitd::initialize() { void sourcekitd::initialize() {
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmPrinters();
llvm::InitializeAllAsmParsers();
GlobalCtx = new SourceKit::Context(sourcekitd::getRuntimeLibPath(), GlobalCtx = new SourceKit::Context(sourcekitd::getRuntimeLibPath(),
SourceKit::createSwiftLangSupport); SourceKit::createSwiftLangSupport);
GlobalCtx->getNotificationCenter().addDocumentUpdateNotificationReceiver( GlobalCtx->getNotificationCenter().addDocumentUpdateNotificationReceiver(

View File

@@ -168,7 +168,7 @@ int modulewrap_main(ArrayRef<const char *> Args, const char *Argv0,
LangOpts.Target = Invocation.getTargetTriple(); LangOpts.Target = Invocation.getTargetTriple();
ASTContext ASTCtx(LangOpts, SearchPathOpts, SrcMgr, Instance.getDiags()); ASTContext ASTCtx(LangOpts, SearchPathOpts, SrcMgr, Instance.getDiags());
ClangImporterOptions ClangImporterOpts; ClangImporterOptions ClangImporterOpts;
ASTCtx.addModuleLoader(ClangImporter::create(ASTCtx, ClangImporterOpts), ASTCtx.addModuleLoader(ClangImporter::create(ASTCtx, ClangImporterOpts, ""),
true); true);
ModuleDecl *M = ModuleDecl::create(ASTCtx.getIdentifier("swiftmodule"), ASTCtx); ModuleDecl *M = ModuleDecl::create(ASTCtx.getIdentifier("swiftmodule"), ASTCtx);
SILOptions SILOpts; SILOptions SILOpts;

View File

@@ -243,6 +243,10 @@ SwiftVersion("swift-version", llvm::cl::desc("Swift version"));
static llvm::cl::opt<std::string> static llvm::cl::opt<std::string>
ModuleCachePath("module-cache-path", llvm::cl::desc("Clang module cache path")); ModuleCachePath("module-cache-path", llvm::cl::desc("Clang module cache path"));
static llvm::cl::opt<std::string>
PCHOutputDir("pch-output-dir", llvm::cl::desc("place autogenerated PCH files in this directory"));
static llvm::cl::opt<std::string> static llvm::cl::opt<std::string>
CompletionCachePath("completion-cache-path", CompletionCachePath("completion-cache-path",
llvm::cl::desc("Code completion cache path"), llvm::cl::desc("Code completion cache path"),
@@ -599,6 +603,7 @@ static int doCodeCompletion(const CompilerInvocation &InitInvok,
<< " at offset " << CodeCompletionOffset << "\n"; << " at offset " << CodeCompletionOffset << "\n";
CompilerInvocation Invocation(InitInvok); CompilerInvocation Invocation(InitInvok);
Invocation.setCodeCompletionPoint(CleanFile.get(), CodeCompletionOffset); Invocation.setCodeCompletionPoint(CleanFile.get(), CodeCompletionOffset);
@@ -2973,6 +2978,8 @@ int main(int argc, char *argv[]) {
} }
InitInvok.getClangImporterOptions().ModuleCachePath = InitInvok.getClangImporterOptions().ModuleCachePath =
options::ModuleCachePath; options::ModuleCachePath;
InitInvok.getClangImporterOptions().PrecompiledHeaderOutputDir =
options::PCHOutputDir;
InitInvok.setImportSearchPaths(options::ImportPaths); InitInvok.setImportSearchPaths(options::ImportPaths);
std::vector<SearchPathOptions::FrameworkSearchPath> FramePaths; std::vector<SearchPathOptions::FrameworkSearchPath> FramePaths;
for (const auto &path : options::FrameworkPaths) { for (const auto &path : options::FrameworkPaths) {
@@ -2986,6 +2993,8 @@ int main(int argc, char *argv[]) {
options::EnableSourceImport; options::EnableSourceImport;
InitInvok.getFrontendOptions().ImplicitObjCHeaderPath = InitInvok.getFrontendOptions().ImplicitObjCHeaderPath =
options::ImportObjCHeader; options::ImportObjCHeader;
InitInvok.getClangImporterOptions().BridgingHeader =
options::ImportObjCHeader;
InitInvok.getLangOptions().EnableAccessControl = InitInvok.getLangOptions().EnableAccessControl =
!options::DisableAccessControl; !options::DisableAccessControl;
InitInvok.getLangOptions().CodeCompleteInitsInPostfixExpr |= InitInvok.getLangOptions().CodeCompleteInitsInPostfixExpr |=

View File

@@ -17,6 +17,7 @@
#include "SourceKit/SwiftLang/Factory.h" #include "SourceKit/SwiftLang/Factory.h"
#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h" #include "llvm/Support/Path.h"
#include "llvm/Support/TargetSelect.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
using namespace SourceKit; using namespace SourceKit;
@@ -102,6 +103,10 @@ public:
LangSupport &getLang() { return Ctx.getSwiftLangSupport(); } LangSupport &getLang() { return Ctx.getSwiftLangSupport(); }
void SetUp() { void SetUp() {
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmPrinters();
llvm::InitializeAllAsmParsers();
NumTasks = 0; NumTasks = 0;
} }