Modules: Add LangOptions::CacheGeneratedPCH

Add an option to cache the generated PCH in the ModuleCache when
emitting it.  This protects clients that build PCHs and read them in the
same process, allowing them to avoid race conditions between parallel
jobs the same way that Clang's implicit module build system does.

rdar://problem/48740787

llvm-svn: 355950
This commit is contained in:
Duncan P. N. Exon Smith 2019-03-12 18:38:04 +00:00
parent 4ab0a9f0a4
commit 70d759b4eb
6 changed files with 57 additions and 9 deletions

View File

@ -154,6 +154,7 @@ BENIGN_ENUM_LANGOPT(CompilingModule, CompilingModuleKind, 2, CMK_None,
"compiling a module interface") "compiling a module interface")
BENIGN_LANGOPT(CompilingPCH, 1, 0, "building a pch") BENIGN_LANGOPT(CompilingPCH, 1, 0, "building a pch")
BENIGN_LANGOPT(BuildingPCHWithObjectFile, 1, 0, "building a pch which has a corresponding object file") BENIGN_LANGOPT(BuildingPCHWithObjectFile, 1, 0, "building a pch which has a corresponding object file")
BENIGN_LANGOPT(CacheGeneratedPCH, 1, 0, "cache generated PCH files in memory")
COMPATIBLE_LANGOPT(ModulesDeclUse , 1, 0, "require declaration of module uses") COMPATIBLE_LANGOPT(ModulesDeclUse , 1, 0, "require declaration of module uses")
BENIGN_LANGOPT(ModulesSearchAll , 1, 1, "searching even non-imported modules to find unresolved references") BENIGN_LANGOPT(ModulesSearchAll , 1, 1, "searching even non-imported modules to find unresolved references")
COMPATIBLE_LANGOPT(ModulesStrictDeclUse, 1, 0, "requiring declaration of module uses and all headers to be in modules") COMPATIBLE_LANGOPT(ModulesStrictDeclUse, 1, 0, "requiring declaration of module uses and all headers to be in modules")

View File

@ -570,7 +570,8 @@ public:
/// the module but currently is merely a random 32-bit number. /// the module but currently is merely a random 32-bit number.
ASTFileSignature WriteAST(Sema &SemaRef, const std::string &OutputFile, ASTFileSignature WriteAST(Sema &SemaRef, const std::string &OutputFile,
Module *WritingModule, StringRef isysroot, Module *WritingModule, StringRef isysroot,
bool hasErrors = false); bool hasErrors = false,
bool ShouldCacheASTInMemory = false);
/// Emit a token. /// Emit a token.
void AddToken(const Token &Tok, RecordDataImpl &Record); void AddToken(const Token &Tok, RecordDataImpl &Record);
@ -974,6 +975,7 @@ class PCHGenerator : public SemaConsumer {
llvm::BitstreamWriter Stream; llvm::BitstreamWriter Stream;
ASTWriter Writer; ASTWriter Writer;
bool AllowASTWithErrors; bool AllowASTWithErrors;
bool ShouldCacheASTInMemory;
protected: protected:
ASTWriter &getWriter() { return Writer; } ASTWriter &getWriter() { return Writer; }
@ -985,7 +987,8 @@ public:
StringRef OutputFile, StringRef isysroot, StringRef OutputFile, StringRef isysroot,
std::shared_ptr<PCHBuffer> Buffer, std::shared_ptr<PCHBuffer> Buffer,
ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions, ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions,
bool AllowASTWithErrors = false, bool IncludeTimestamps = true); bool AllowASTWithErrors = false, bool IncludeTimestamps = true,
bool ShouldCacheASTInMemory = false);
~PCHGenerator() override; ~PCHGenerator() override;
void InitializeSema(Sema &S) override { SemaPtr = &S; } void InitializeSema(Sema &S) override { SemaPtr = &S; }

View File

@ -112,7 +112,7 @@ GeneratePCHAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
CI.getPreprocessor(), CI.getModuleCache(), OutputFile, Sysroot, Buffer, CI.getPreprocessor(), CI.getModuleCache(), OutputFile, Sysroot, Buffer,
FrontendOpts.ModuleFileExtensions, FrontendOpts.ModuleFileExtensions,
CI.getPreprocessorOpts().AllowPCHWithCompilerErrors, CI.getPreprocessorOpts().AllowPCHWithCompilerErrors,
FrontendOpts.IncludeTimestamps)); FrontendOpts.IncludeTimestamps, +CI.getLangOpts().CacheGeneratedPCH));
Consumers.push_back(CI.getPCHContainerWriter().CreatePCHContainerGenerator( Consumers.push_back(CI.getPCHContainerWriter().CreatePCHContainerGenerator(
CI, InFile, OutputFile, std::move(OS), Buffer)); CI, InFile, OutputFile, std::move(OS), Buffer));
@ -176,6 +176,8 @@ GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI,
CI.getFrontendOpts().ModuleFileExtensions, CI.getFrontendOpts().ModuleFileExtensions,
/*AllowASTWithErrors=*/false, /*AllowASTWithErrors=*/false,
/*IncludeTimestamps=*/ /*IncludeTimestamps=*/
+CI.getFrontendOpts().BuildingImplicitModule,
/*ShouldCacheASTInMemory=*/
+CI.getFrontendOpts().BuildingImplicitModule)); +CI.getFrontendOpts().BuildingImplicitModule));
Consumers.push_back(CI.getPCHContainerWriter().CreatePCHContainerGenerator( Consumers.push_back(CI.getPCHContainerWriter().CreatePCHContainerGenerator(
CI, InFile, OutputFile, std::move(OS), Buffer)); CI, InFile, OutputFile, std::move(OS), Buffer));

View File

@ -4596,7 +4596,8 @@ time_t ASTWriter::getTimestampForOutput(const FileEntry *E) const {
ASTFileSignature ASTWriter::WriteAST(Sema &SemaRef, ASTFileSignature ASTWriter::WriteAST(Sema &SemaRef,
const std::string &OutputFile, const std::string &OutputFile,
Module *WritingModule, StringRef isysroot, Module *WritingModule, StringRef isysroot,
bool hasErrors) { bool hasErrors,
bool ShouldCacheASTInMemory) {
WritingAST = true; WritingAST = true;
ASTHasCompilerErrors = hasErrors; ASTHasCompilerErrors = hasErrors;
@ -4620,7 +4621,7 @@ ASTFileSignature ASTWriter::WriteAST(Sema &SemaRef,
this->BaseDirectory.clear(); this->BaseDirectory.clear();
WritingAST = false; WritingAST = false;
if (SemaRef.Context.getLangOpts().ImplicitModules && WritingModule) { if (ShouldCacheASTInMemory) {
// Construct MemoryBuffer and update buffer manager. // Construct MemoryBuffer and update buffer manager.
ModuleCache.addBuiltPCM(OutputFile, ModuleCache.addBuiltPCM(OutputFile,
llvm::MemoryBuffer::getMemBufferCopy( llvm::MemoryBuffer::getMemBufferCopy(

View File

@ -24,12 +24,14 @@ PCHGenerator::PCHGenerator(
const Preprocessor &PP, InMemoryModuleCache &ModuleCache, const Preprocessor &PP, InMemoryModuleCache &ModuleCache,
StringRef OutputFile, StringRef isysroot, std::shared_ptr<PCHBuffer> Buffer, StringRef OutputFile, StringRef isysroot, std::shared_ptr<PCHBuffer> Buffer,
ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions, ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions,
bool AllowASTWithErrors, bool IncludeTimestamps) bool AllowASTWithErrors, bool IncludeTimestamps,
bool ShouldCacheASTInMemory)
: PP(PP), OutputFile(OutputFile), isysroot(isysroot.str()), : PP(PP), OutputFile(OutputFile), isysroot(isysroot.str()),
SemaPtr(nullptr), Buffer(std::move(Buffer)), Stream(this->Buffer->Data), SemaPtr(nullptr), Buffer(std::move(Buffer)), Stream(this->Buffer->Data),
Writer(Stream, this->Buffer->Data, ModuleCache, Extensions, Writer(Stream, this->Buffer->Data, ModuleCache, Extensions,
IncludeTimestamps), IncludeTimestamps),
AllowASTWithErrors(AllowASTWithErrors) { AllowASTWithErrors(AllowASTWithErrors),
ShouldCacheASTInMemory(ShouldCacheASTInMemory) {
this->Buffer->IsComplete = false; this->Buffer->IsComplete = false;
} }
@ -61,7 +63,8 @@ void PCHGenerator::HandleTranslationUnit(ASTContext &Ctx) {
Writer.WriteAST(*SemaPtr, OutputFile, Module, isysroot, Writer.WriteAST(*SemaPtr, OutputFile, Module, isysroot,
// For serialization we are lenient if the errors were // For serialization we are lenient if the errors were
// only warn-as-error kind. // only warn-as-error kind.
PP.getDiagnostics().hasUncompilableErrorOccurred()); PP.getDiagnostics().hasUncompilableErrorOccurred(),
ShouldCacheASTInMemory);
Buffer->IsComplete = true; Buffer->IsComplete = true;
} }

View File

@ -6,18 +6,20 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "clang/Frontend/FrontendAction.h"
#include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h" #include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/FrontendActions.h" #include "clang/Frontend/FrontendActions.h"
#include "clang/Lex/Preprocessor.h" #include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PreprocessorOptions.h" #include "clang/Lex/PreprocessorOptions.h"
#include "clang/Sema/Sema.h" #include "clang/Sema/Sema.h"
#include "clang/Serialization/InMemoryModuleCache.h"
#include "llvm/ADT/Triple.h" #include "llvm/ADT/Triple.h"
#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/ToolOutputFile.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
using namespace llvm; using namespace llvm;
@ -253,4 +255,40 @@ TEST(ASTFrontendAction, ExternalSemaSource) {
EXPECT_EQ("This is a note", TDC->Note.str().str()); EXPECT_EQ("This is a note", TDC->Note.str().str());
} }
TEST(GeneratePCHFrontendAction, CacheGeneratedPCH) {
// Create a temporary file for writing out the PCH that will be cleaned up.
int PCHFD;
llvm::SmallString<128> PCHFilename;
ASSERT_FALSE(
llvm::sys::fs::createTemporaryFile("test.h", "pch", PCHFD, PCHFilename));
llvm::ToolOutputFile PCHFile(PCHFilename, PCHFD);
for (bool ShouldCache : {false, true}) {
auto Invocation = std::make_shared<CompilerInvocation>();
Invocation->getLangOpts()->CacheGeneratedPCH = ShouldCache;
Invocation->getPreprocessorOpts().addRemappedFile(
"test.h",
MemoryBuffer::getMemBuffer("int foo(void) { return 1; }\n").release());
Invocation->getFrontendOpts().Inputs.push_back(
FrontendInputFile("test.h", InputKind::C));
Invocation->getFrontendOpts().OutputFile = StringRef(PCHFilename);
Invocation->getFrontendOpts().ProgramAction = frontend::GeneratePCH;
Invocation->getTargetOpts().Triple = "x86_64-apple-darwin19.0.0";
CompilerInstance Compiler;
Compiler.setInvocation(std::move(Invocation));
Compiler.createDiagnostics();
GeneratePCHAction TestAction;
ASSERT_TRUE(Compiler.ExecuteAction(TestAction));
// Check whether the PCH was cached.
if (ShouldCache)
EXPECT_EQ(InMemoryModuleCache::Final,
Compiler.getModuleCache().getPCMState(PCHFilename));
else
EXPECT_EQ(InMemoryModuleCache::Unknown,
Compiler.getModuleCache().getPCMState(PCHFilename));
}
}
} // anonymous namespace } // anonymous namespace