diff --git a/clang/include/clang/CodeGen/ObjectFilePCHContainerOperations.h b/clang/include/clang/CodeGen/ObjectFilePCHContainerOperations.h --- a/clang/include/clang/CodeGen/ObjectFilePCHContainerOperations.h +++ b/clang/include/clang/CodeGen/ObjectFilePCHContainerOperations.h @@ -27,6 +27,12 @@ const std::string &OutputFileName, std::unique_ptr OS, std::shared_ptr Buffer) const override; + + std::unique_ptr CreatePCHDeferredContainerGenerator( + CompilerInstance &CI, const std::string &MainFileName, + const std::string &OutputFileName, + std::unique_ptr OS, + std::shared_ptr Buffer) const override; }; /// A PCHContainerReader implementation that uses LLVM to diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -798,6 +798,7 @@ ASTWriter Writer; bool AllowASTWithErrors; bool ShouldCacheASTInMemory; + bool IsForBMI; protected: ASTWriter &getWriter() { return Writer; } @@ -810,7 +811,7 @@ std::shared_ptr Buffer, ArrayRef> Extensions, bool AllowASTWithErrors = false, bool IncludeTimestamps = true, - bool ShouldCacheASTInMemory = false); + bool ShouldCacheASTInMemory = false, bool IsForBMI = false); ~PCHGenerator() override; void InitializeSema(Sema &S) override { SemaPtr = &S; } diff --git a/clang/include/clang/Serialization/PCHContainerOperations.h b/clang/include/clang/Serialization/PCHContainerOperations.h --- a/clang/include/clang/Serialization/PCHContainerOperations.h +++ b/clang/include/clang/Serialization/PCHContainerOperations.h @@ -26,6 +26,7 @@ struct PCHBuffer { ASTFileSignature Signature; + std::string PresumedFileName; llvm::SmallVector Data; bool IsComplete; }; @@ -47,6 +48,15 @@ const std::string &OutputFileName, std::unique_ptr OS, std::shared_ptr Buffer) const = 0; + + /// Return an ASTConsumer that can be chained with a + /// PCHGenerator that produces a wrapper file format containing a + /// serialized AST bitstream. + virtual std::unique_ptr CreatePCHDeferredContainerGenerator( + CompilerInstance &CI, const std::string &MainFileName, + const std::string &OutputFileName, + std::unique_ptr OS, + std::shared_ptr Buffer) const = 0; }; /// This abstract interface provides operations for unwrapping @@ -74,6 +84,12 @@ const std::string &OutputFileName, std::unique_ptr OS, std::shared_ptr Buffer) const override; + + std::unique_ptr CreatePCHDeferredContainerGenerator( + CompilerInstance &CI, const std::string &MainFileName, + const std::string &OutputFileName, + std::unique_ptr OS, + std::shared_ptr Buffer) const override; }; /// Implements read operations for a raw pass-through PCH container. diff --git a/clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp b/clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp --- a/clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp +++ b/clang/lib/CodeGen/ObjectFilePCHContainerOperations.cpp @@ -354,6 +354,16 @@ return Formats; } +std::unique_ptr +ObjectFilePCHContainerWriter::CreatePCHDeferredContainerGenerator( + CompilerInstance &CI, const std::string &MainFileName, + const std::string &OutputFileName, + std::unique_ptr OS, + std::shared_ptr Buffer) const { + assert(0 && "Did not mean to arrive here"); + return nullptr; +} + StringRef ObjectFilePCHContainerReader::ExtractPCH(llvm::MemoryBufferRef Buffer) const { StringRef PCH; diff --git a/clang/lib/Serialization/GeneratePCH.cpp b/clang/lib/Serialization/GeneratePCH.cpp --- a/clang/lib/Serialization/GeneratePCH.cpp +++ b/clang/lib/Serialization/GeneratePCH.cpp @@ -13,6 +13,7 @@ #include "clang/AST/ASTContext.h" #include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/HeaderSearchOptions.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/SemaConsumer.h" #include "clang/Serialization/ASTWriter.h" @@ -25,13 +26,13 @@ StringRef OutputFile, StringRef isysroot, std::shared_ptr Buffer, ArrayRef> Extensions, bool AllowASTWithErrors, bool IncludeTimestamps, - bool ShouldCacheASTInMemory) + bool ShouldCacheASTInMemory, bool IsForBMI) : PP(PP), OutputFile(OutputFile), isysroot(isysroot.str()), SemaPtr(nullptr), Buffer(std::move(Buffer)), Stream(this->Buffer->Data), Writer(Stream, this->Buffer->Data, ModuleCache, Extensions, IncludeTimestamps), AllowASTWithErrors(AllowASTWithErrors), - ShouldCacheASTInMemory(ShouldCacheASTInMemory) { + ShouldCacheASTInMemory(ShouldCacheASTInMemory), IsForBMI(IsForBMI) { this->Buffer->IsComplete = false; } @@ -48,23 +49,46 @@ return; Module *Module = nullptr; - if (PP.getLangOpts().isCompilingModule()) { + if (PP.getLangOpts().isCompilingModule() || IsForBMI) { Module = PP.getHeaderSearchInfo().lookupModule( PP.getLangOpts().CurrentModule, SourceLocation(), /*AllowSearch*/ false); if (!Module) { - assert(hasErrors && "emitting module but current module doesn't exist"); + // If we have errors, then that might have prevented the creation of the + // module - otherwise, for the case we are compiling a module, it must be + // present. + // Conversely, IsForBMI output is speculative and only produced for TUs + // in which module interfaces are discovered, thus it is not an error to + // find that there is no module in this case. + assert((hasErrors || IsForBMI) && + "emitting module but current module doesn't exist"); return; } - } + } // else, non-modular PCH. // Errors that do not prevent the PCH from being written should not cause the // overall compilation to fail either. if (AllowASTWithErrors) PP.getDiagnostics().getClient()->clear(); - // Emit the PCH file to the Buffer. assert(SemaPtr && "No Sema?"); + + // A module implementation implicitly pulls in its interface module. + // Since it has the same name as the implementation, it will be found + // by the lookup above. Fortunately, Sema records the difference in + // the ModuleScopes; We do not need to output the BMI in that case. + if (IsForBMI && SemaPtr->isModuleImplementation()) + return; + + if (IsForBMI) { + + assert(Module && !Module->IsFromModuleFile && + "trying to re-write a module?"); + + // So now attach that name to the buffer we are about to create. + Buffer->PresumedFileName = OutputFile; + } + Buffer->Signature = Writer.WriteAST(*SemaPtr, OutputFile, Module, isysroot, // For serialization we are lenient if the errors were diff --git a/clang/lib/Serialization/PCHContainerOperations.cpp b/clang/lib/Serialization/PCHContainerOperations.cpp --- a/clang/lib/Serialization/PCHContainerOperations.cpp +++ b/clang/lib/Serialization/PCHContainerOperations.cpp @@ -12,8 +12,11 @@ #include "clang/Serialization/PCHContainerOperations.h" #include "clang/AST/ASTConsumer.h" +#include "clang/Frontend/CompilerInstance.h" #include "clang/Lex/ModuleLoader.h" #include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include @@ -48,6 +51,45 @@ } }; +/// A PCHContainerGenerator that writes out the PCH to a flat file if the +/// action is needed (and the filename is determined at the time the output +/// is done). +class RawPCHDeferredContainerGenerator : public ASTConsumer { + std::shared_ptr Buffer; + +public: + RawPCHDeferredContainerGenerator(std::shared_ptr Buffer) + : Buffer(std::move(Buffer)) {} + + ~RawPCHDeferredContainerGenerator() override = default; + + void HandleTranslationUnit(ASTContext &Ctx) override { + if (Buffer->IsComplete && !Buffer->PresumedFileName.empty()) { + std::error_code EC; + StringRef Parent = llvm::sys::path::parent_path(Buffer->PresumedFileName); + if (!Parent.empty()) + EC = llvm::sys::fs::create_directory(Parent); + if (!EC) { + int FD; + EC = llvm::sys::fs::openFileForWrite(Buffer->PresumedFileName, FD); + if (!EC) { + std::unique_ptr OS; + OS.reset(new llvm::raw_fd_ostream(FD, /*shouldClose=*/true)); + *OS << Buffer->Data; + OS->flush(); // Make sure it hits disk now. + } else + llvm::dbgs() << " Problem creating : " << Buffer->PresumedFileName + << "\n"; + } else + llvm::dbgs() << " Problem creating dir : " << Parent << "\n"; + } + + // Free the space of the temporary buffer. + llvm::SmallVector Empty; + Buffer->Data = std::move(Empty); + } +}; + } // anonymous namespace std::unique_ptr RawPCHContainerWriter::CreatePCHContainerGenerator( @@ -62,6 +104,15 @@ return ArrayRef(Raw); } +std::unique_ptr +RawPCHContainerWriter::CreatePCHDeferredContainerGenerator( + CompilerInstance &CI, const std::string &MainFileName, + const std::string &OutputFileName, + std::unique_ptr OS, + std::shared_ptr Buffer) const { + return std::make_unique(Buffer); +} + StringRef RawPCHContainerReader::ExtractPCH(llvm::MemoryBufferRef Buffer) const { return Buffer.getBuffer();