diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2068,6 +2068,10 @@ def fsystem_module : Flag<["-"], "fsystem-module">, Flags<[CC1Option]>, HelpText<"Build this module as a system module. Only used with -emit-module">, MarshallingInfoFlag>; +def fbackup_module : Flag<["-"], "fbackup-module">, + Group, Flags<[CC1Option, NoDriverOption]>, + HelpText<"Backup the generated pre-compiled module/header to an unique location.">, + MarshallingInfoFlag>; def fmodule_map_file : Joined<["-"], "fmodule-map-file=">, Group, Flags<[NoXarchOption,CC1Option]>, MetaVarName<"">, HelpText<"Load this module map file">, diff --git a/clang/include/clang/Frontend/FrontendOptions.h b/clang/include/clang/Frontend/FrontendOptions.h --- a/clang/include/clang/Frontend/FrontendOptions.h +++ b/clang/include/clang/Frontend/FrontendOptions.h @@ -274,6 +274,9 @@ /// Whether we can generate the global module index if needed. unsigned GenerateGlobalModuleIndex : 1; + /// Whether we are backing up the generated module file to an unique location. + unsigned BackupGeneratedModuleFile : 1; + /// Whether we include declaration dumps in AST dumps. unsigned ASTDumpDecls : 1; @@ -459,11 +462,11 @@ FixWhatYouCan(false), FixOnlyWarnings(false), FixAndRecompile(false), FixToTemporaries(false), ARCMTMigrateEmitARCErrors(false), SkipFunctionBodies(false), UseGlobalModuleIndex(true), - GenerateGlobalModuleIndex(true), ASTDumpDecls(false), - ASTDumpLookups(false), BuildingImplicitModule(false), - ModulesEmbedAllFiles(false), IncludeTimestamps(true), - UseTemporary(true), AllowPCMWithCompilerErrors(false), - TimeTraceGranularity(500) {} + GenerateGlobalModuleIndex(true), BackupGeneratedModuleFile(false), + ASTDumpDecls(false), ASTDumpLookups(false), + BuildingImplicitModule(false), ModulesEmbedAllFiles(false), + IncludeTimestamps(true), UseTemporary(true), + AllowPCMWithCompilerErrors(false), TimeTraceGranularity(500) {} /// getInputKindForExtension - Return the appropriate input kind for a file /// extension. For example, "c" would return Language::C. 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 @@ -240,6 +240,8 @@ std::unique_ptr VMContext = std::move(this->VMContext); std::unique_ptr M = std::move(this->M); std::unique_ptr Builder = std::move(this->Builder); + // Decrease the reference count on function exit. + std::shared_ptr Buffer = std::move(this->Buffer); if (Diags.hasErrorOccurred()) return; @@ -306,10 +308,6 @@ LangOpts, Ctx.getTargetInfo().getDataLayoutString(), M.get(), BackendAction::Backend_EmitObj, std::move(OS)); - - // Free the memory for the temporary buffer. - llvm::SmallVector Empty; - SerializedAST = std::move(Empty); } }; diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -9,8 +9,8 @@ #include "clang/Frontend/FrontendActions.h" #include "clang/AST/ASTConsumer.h" #include "clang/Basic/FileManager.h" -#include "clang/Basic/TargetInfo.h" #include "clang/Basic/LangStandard.h" +#include "clang/Basic/TargetInfo.h" #include "clang/Frontend/ASTConsumers.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" @@ -26,6 +26,7 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" #include @@ -48,6 +49,26 @@ CI.createSema(Action.getTranslationUnitKind(), GetCodeCompletionConsumer(CI)); } + +static void +AddBackupPCHWriter(std::vector> &Consumers, + CompilerInstance &CI, StringRef InFile, + const std::string &OutFile, + std::shared_ptr &Buffer) { + std::string BackupOutFile = + OutFile + "." + std::to_string(llvm::sys::Process::getProcessId()); + + std::error_code EC; + auto OS = std::make_unique(BackupOutFile, EC); + if (EC) { + CI.getDiagnostics().Report(diag::err_fe_error_opening) + << BackupOutFile << EC.message(); + return; + } + + Consumers.push_back(CI.getPCHContainerWriter().CreatePCHContainerGenerator( + CI, std::string(InFile), BackupOutFile, std::move(OS), Buffer)); +} } // namespace //===----------------------------------------------------------------------===// @@ -118,6 +139,8 @@ FrontendOpts.IncludeTimestamps, +CI.getLangOpts().CacheGeneratedPCH)); Consumers.push_back(CI.getPCHContainerWriter().CreatePCHContainerGenerator( CI, std::string(InFile), OutputFile, std::move(OS), Buffer)); + if (CI.getInvocation().getFrontendOpts().BackupGeneratedModuleFile) + AddBackupPCHWriter(Consumers, CI, InFile, OutputFile, Buffer); return std::make_unique(std::move(Consumers)); } @@ -181,6 +204,8 @@ +CI.getFrontendOpts().BuildingImplicitModule)); Consumers.push_back(CI.getPCHContainerWriter().CreatePCHContainerGenerator( CI, std::string(InFile), OutputFile, std::move(OS), Buffer)); + if (CI.getInvocation().getFrontendOpts().BackupGeneratedModuleFile) + AddBackupPCHWriter(Consumers, CI, InFile, OutputFile, Buffer); return std::make_unique(std::move(Consumers)); } 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 @@ -37,14 +37,14 @@ ~RawPCHContainerGenerator() override = default; void HandleTranslationUnit(ASTContext &Ctx) override { + // Decrease the reference count on function exit. + std::shared_ptr Buffer = std::move(this->Buffer); + if (Buffer->IsComplete) { // Make sure it hits disk now. *OS << Buffer->Data; OS->flush(); } - // Free the space of the temporary buffer. - llvm::SmallVector Empty; - Buffer->Data = std::move(Empty); } }; diff --git a/clang/test/Modules/backup-module.h b/clang/test/Modules/backup-module.h new file mode 100644 --- /dev/null +++ b/clang/test/Modules/backup-module.h @@ -0,0 +1 @@ +int function(void); diff --git a/clang/test/Modules/backup-module.c b/clang/test/Modules/backup-module.c new file mode 100644 --- /dev/null +++ b/clang/test/Modules/backup-module.c @@ -0,0 +1,10 @@ +// RUN: rm -rf %t && mkdir %t +// RUN: %clang_cc1 %S/backup-module.h -emit-pch -o %t/backup-module.pch +// RUN: find %t -name 'backup-module.pch.*' | not grep backup-module +// +// RUN: %clang_cc1 %S/backup-module.h -emit-pch -o %t/backup-module.pch -fbackup-module +// RUN: find %t -name 'backup-module.pch.*' | grep backup-module +// RUN: find %t -name 'backup-module.pch.*' | xargs diff %t/backup-module.pch + +// The purpose of this test is to make sure that the module file backup has +// the same contents as the original.