diff options
author | Duncan P. N. Exon Smith <dexonsmith@apple.com> | 2017-03-13 18:45:08 +0000 |
---|---|---|
committer | Duncan P. N. Exon Smith <dexonsmith@apple.com> | 2017-03-13 18:45:08 +0000 |
commit | 60fa28882eda2a05576e94690a586940ba9ca43c (patch) | |
tree | f96f8f1f6610cde2c0f267d531326ce362c47976 /clang/lib/Serialization | |
parent | 75a7e6589f643f6ed47d1fc04339696e7fb1e120 (diff) | |
download | bcm5719-llvm-60fa28882eda2a05576e94690a586940ba9ca43c.tar.gz bcm5719-llvm-60fa28882eda2a05576e94690a586940ba9ca43c.zip |
Modules: Use hash of PCM content for SIGNATURE
Change ASTFileSignature from a random 32-bit number to the hash of the
PCM content.
- Move definition ASTFileSignature to Basic/Module.h so Module and
ASTSourceDescriptor can use it.
- Change the signature from uint64_t to std::array<uint32_t,5>.
- Stop using (saving/reading) the size and modification time of PCM
files when there is a valid SIGNATURE.
- Add UNHASHED_CONTROL_BLOCK, and use it to store the SIGNATURE record
and other records that shouldn't affect the hash. Because implicit
modules reuses the same file for multiple levels of -Werror, this
includes DIAGNOSTIC_OPTIONS and DIAG_PRAGMA_MAPPINGS.
This helps to solve a PCH + implicit Modules dependency issue: PCH files
are handled by the external build system, whereas implicit modules are
handled by internal compiler build system. This prevents invalidating a
PCH when the compiler overwrites a PCM file with the same content
(modulo the diagnostic differences).
Design and original patch by Manman Ren!
llvm-svn: 297655
Diffstat (limited to 'clang/lib/Serialization')
-rw-r--r-- | clang/lib/Serialization/ASTReader.cpp | 199 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTWriter.cpp | 150 | ||||
-rw-r--r-- | clang/lib/Serialization/GeneratePCH.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Serialization/GlobalModuleIndex.cpp | 76 |
4 files changed, 316 insertions, 111 deletions
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 648c7b48ed8..be0cc9ab1a8 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -2163,7 +2163,7 @@ static bool isDiagnosedResult(ASTReader::ASTReadResult ARR, unsigned Caps) { ASTReader::ASTReadResult ASTReader::ReadOptionsBlock( BitstreamCursor &Stream, unsigned ClientLoadCapabilities, bool AllowCompatibleConfigurationMismatch, ASTReaderListener &Listener, - std::string &SuggestedPredefines, bool ValidateDiagnosticOptions) { + std::string &SuggestedPredefines) { if (Stream.EnterSubBlock(OPTIONS_BLOCK_ID)) return Failure; @@ -2205,15 +2205,6 @@ ASTReader::ASTReadResult ASTReader::ReadOptionsBlock( break; } - case DIAGNOSTIC_OPTIONS: { - bool Complain = (ClientLoadCapabilities & ARR_OutOfDate) == 0; - if (ValidateDiagnosticOptions && - !AllowCompatibleConfigurationMismatch && - ParseDiagnosticOptions(Record, Complain, Listener)) - return OutOfDate; - break; - } - case FILE_SYSTEM_OPTIONS: { bool Complain = (ClientLoadCapabilities & ARR_ConfigurationMismatch) == 0; if (!AllowCompatibleConfigurationMismatch && @@ -2254,6 +2245,23 @@ ASTReader::ReadControlBlock(ModuleFile &F, return Failure; } + // Lambda to read the unhashed control block the first time it's called. + // + // For PCM files, the unhashed control block cannot be read until after the + // MODULE_NAME record. However, PCH files have no MODULE_NAME, and yet still + // need to look ahead before reading the IMPORTS record. For consistency, + // this block is always read somehow (see BitstreamEntry::EndBlock). + bool HasReadUnhashedControlBlock = false; + auto readUnhashedControlBlockOnce = [&]() { + if (!HasReadUnhashedControlBlock) { + HasReadUnhashedControlBlock = true; + if (ASTReadResult Result = + readUnhashedControlBlock(F, ImportedBy, ClientLoadCapabilities)) + return Result; + } + return Success; + }; + // Read all of the records and blocks in the control block. RecordData Record; unsigned NumInputs = 0; @@ -2266,6 +2274,11 @@ ASTReader::ReadControlBlock(ModuleFile &F, Error("malformed block record in AST file"); return Failure; case llvm::BitstreamEntry::EndBlock: { + // Validate the module before returning. This call catches an AST with + // no module name and no imports. + if (ASTReadResult Result = readUnhashedControlBlockOnce()) + return Result; + // Validate input files. const HeaderSearchOptions &HSOpts = PP.getHeaderSearchInfo().getHeaderSearchOpts(); @@ -2337,13 +2350,10 @@ ASTReader::ReadControlBlock(ModuleFile &F, // FIXME: Allow this for files explicitly specified with -include-pch. bool AllowCompatibleConfigurationMismatch = F.Kind == MK_ExplicitModule || F.Kind == MK_PrebuiltModule; - const HeaderSearchOptions &HSOpts = - PP.getHeaderSearchInfo().getHeaderSearchOpts(); Result = ReadOptionsBlock(Stream, ClientLoadCapabilities, AllowCompatibleConfigurationMismatch, - *Listener, SuggestedPredefines, - HSOpts.ModulesValidateDiagnosticOptions); + *Listener, SuggestedPredefines); if (Result == Failure) { Error("malformed block record in AST file"); return Result; @@ -2417,12 +2427,13 @@ ASTReader::ReadControlBlock(ModuleFile &F, break; } - case SIGNATURE: - assert((!F.Signature || F.Signature == Record[0]) && "signature changed"); - F.Signature = Record[0]; - break; - case IMPORTS: { + // Validate the AST before processing any imports (otherwise, untangling + // them can be error-prone and expensive). A module will have a name and + // will already have been validated, but this catches the PCH case. + if (ASTReadResult Result = readUnhashedControlBlockOnce()) + return Result; + // Load each of the imported PCH files. unsigned Idx = 0, N = Record.size(); while (Idx < N) { @@ -2435,7 +2446,10 @@ ASTReader::ReadControlBlock(ModuleFile &F, ReadUntranslatedSourceLocation(Record[Idx++]); off_t StoredSize = (off_t)Record[Idx++]; time_t StoredModTime = (time_t)Record[Idx++]; - ASTFileSignature StoredSignature = Record[Idx++]; + ASTFileSignature StoredSignature = { + {{(uint32_t)Record[Idx++], (uint32_t)Record[Idx++], + (uint32_t)Record[Idx++], (uint32_t)Record[Idx++], + (uint32_t)Record[Idx++]}}}; auto ImportedFile = ReadPath(F, Record, Idx); // If our client can't cope with us being out of date, we can't cope with @@ -2487,6 +2501,12 @@ ASTReader::ReadControlBlock(ModuleFile &F, F.ModuleName = Blob; if (Listener) Listener->ReadModuleName(F.ModuleName); + + // Validate the AST as soon as we have a name so we can exit early on + // failure. + if (ASTReadResult Result = readUnhashedControlBlockOnce()) + return Result; + break; case MODULE_DIRECTORY: { @@ -3076,14 +3096,6 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { F.ObjCCategories.swap(Record); break; - case DIAG_PRAGMA_MAPPINGS: - if (F.PragmaDiagMappings.empty()) - F.PragmaDiagMappings.swap(Record); - else - F.PragmaDiagMappings.insert(F.PragmaDiagMappings.end(), - Record.begin(), Record.end()); - break; - case CUDA_SPECIAL_DECL_REFS: // Later tables overwrite earlier ones. // FIXME: Modules will have trouble with this. @@ -3657,10 +3669,10 @@ ASTReader::ASTReadResult ASTReader::ReadAST(StringRef FileName, unsigned NumModules = ModuleMgr.size(); SmallVector<ImportedModule, 4> Loaded; - switch(ASTReadResult ReadResult = ReadASTCore(FileName, Type, ImportLoc, - /*ImportedBy=*/nullptr, Loaded, - 0, 0, 0, - ClientLoadCapabilities)) { + switch (ASTReadResult ReadResult = + ReadASTCore(FileName, Type, ImportLoc, + /*ImportedBy=*/nullptr, Loaded, 0, 0, + ASTFileSignature(), ClientLoadCapabilities)) { case Failure: case Missing: case OutOfDate: @@ -4021,6 +4033,12 @@ ASTReader::ReadASTCore(StringRef FileName, Loaded.push_back(ImportedModule(M, ImportedBy, ImportLoc)); return Success; + case UNHASHED_CONTROL_BLOCK_ID: + // This block is handled using look-ahead during ReadControlBlock. We + // shouldn't get here! + Error("malformed block record in AST file"); + return Failure; + default: if (Stream.SkipBlock()) { Error("malformed block record in AST file"); @@ -4033,6 +4051,93 @@ ASTReader::ReadASTCore(StringRef FileName, return Success; } +ASTReader::ASTReadResult +ASTReader::readUnhashedControlBlock(ModuleFile &F, bool WasImportedBy, + unsigned ClientLoadCapabilities) { + const HeaderSearchOptions &HSOpts = + PP.getHeaderSearchInfo().getHeaderSearchOpts(); + bool AllowCompatibleConfigurationMismatch = + F.Kind == MK_ExplicitModule || F.Kind == MK_PrebuiltModule; + + ASTReadResult Result = readUnhashedControlBlockImpl( + &F, F.Data, ClientLoadCapabilities, AllowCompatibleConfigurationMismatch, + Listener.get(), + WasImportedBy ? false : HSOpts.ModulesValidateDiagnosticOptions); + + if (DisableValidation || WasImportedBy || + (AllowConfigurationMismatch && Result == ConfigurationMismatch)) + return Success; + + if (Result == Failure) + Error("malformed block record in AST file"); + + return Result; +} + +ASTReader::ASTReadResult ASTReader::readUnhashedControlBlockImpl( + ModuleFile *F, llvm::StringRef StreamData, unsigned ClientLoadCapabilities, + bool AllowCompatibleConfigurationMismatch, ASTReaderListener *Listener, + bool ValidateDiagnosticOptions) { + // Initialize a stream. + BitstreamCursor Stream(StreamData); + + // Sniff for the signature. + if (!startsWithASTFileMagic(Stream)) + return Failure; + + // Scan for the UNHASHED_CONTROL_BLOCK_ID block. + if (SkipCursorToBlock(Stream, UNHASHED_CONTROL_BLOCK_ID)) + return Failure; + + // Read all of the records in the options block. + RecordData Record; + ASTReadResult Result = Success; + while (1) { + llvm::BitstreamEntry Entry = Stream.advance(); + + switch (Entry.Kind) { + case llvm::BitstreamEntry::Error: + case llvm::BitstreamEntry::SubBlock: + return Failure; + + case llvm::BitstreamEntry::EndBlock: + return Result; + + case llvm::BitstreamEntry::Record: + // The interesting case. + break; + } + + // Read and process a record. + Record.clear(); + switch ( + (UnhashedControlBlockRecordTypes)Stream.readRecord(Entry.ID, Record)) { + case SIGNATURE: { + if (F) + std::copy(Record.begin(), Record.end(), F->Signature.data()); + break; + } + case DIAGNOSTIC_OPTIONS: { + bool Complain = (ClientLoadCapabilities & ARR_OutOfDate) == 0; + if (Listener && ValidateDiagnosticOptions && + !AllowCompatibleConfigurationMismatch && + ParseDiagnosticOptions(Record, Complain, *Listener)) + return OutOfDate; + break; + } + case DIAG_PRAGMA_MAPPINGS: + if (!F) + break; + if (F->PragmaDiagMappings.empty()) + F->PragmaDiagMappings.swap(Record); + else + F->PragmaDiagMappings.insert(F->PragmaDiagMappings.end(), + Record.begin(), Record.end()); + break; + } + } +} + /// Parse a record and blob containing module file extension metadata. static bool parseModuleFileExtensionMetadata( const SmallVectorImpl<uint64_t> &Record, @@ -4249,23 +4354,24 @@ void ASTReader::finalizeForWriting() { static ASTFileSignature readASTFileSignature(StringRef PCH) { BitstreamCursor Stream(PCH); if (!startsWithASTFileMagic(Stream)) - return 0; + return ASTFileSignature(); - // Scan for the CONTROL_BLOCK_ID block. - if (SkipCursorToBlock(Stream, CONTROL_BLOCK_ID)) - return 0; + // Scan for the UNHASHED_CONTROL_BLOCK_ID block. + if (SkipCursorToBlock(Stream, UNHASHED_CONTROL_BLOCK_ID)) + return ASTFileSignature(); - // Scan for SIGNATURE inside the control block. + // Scan for SIGNATURE inside the diagnostic options block. ASTReader::RecordData Record; while (true) { llvm::BitstreamEntry Entry = Stream.advanceSkippingSubblocks(); if (Entry.Kind != llvm::BitstreamEntry::Record) - return 0; + return ASTFileSignature(); Record.clear(); StringRef Blob; if (SIGNATURE == Stream.readRecord(Entry.ID, Record, &Blob)) - return Record[0]; + return {{{(uint32_t)Record[0], (uint32_t)Record[1], (uint32_t)Record[2], + (uint32_t)Record[3], (uint32_t)Record[4]}}}; } } @@ -4384,7 +4490,8 @@ bool ASTReader::readASTFileControlBlock( } // Initialize the stream - BitstreamCursor Stream(PCHContainerRdr.ExtractPCH(**Buffer)); + StringRef Bytes = PCHContainerRdr.ExtractPCH(**Buffer); + BitstreamCursor Stream(Bytes); // Sniff for the signature. if (!startsWithASTFileMagic(Stream)) @@ -4412,8 +4519,7 @@ bool ASTReader::readASTFileControlBlock( std::string IgnoredSuggestedPredefines; if (ReadOptionsBlock(Stream, ARR_ConfigurationMismatch | ARR_OutOfDate, /*AllowCompatibleConfigurationMismatch*/ false, - Listener, IgnoredSuggestedPredefines, - ValidateDiagnosticOptions) != Success) + Listener, IgnoredSuggestedPredefines) != Success) return true; break; } @@ -4534,6 +4640,7 @@ bool ASTReader::readASTFileControlBlock( // Look for module file extension blocks, if requested. if (FindModuleFileExtensions) { + BitstreamCursor SavedStream = Stream; while (!SkipCursorToBlock(Stream, EXTENSION_BLOCK_ID)) { bool DoneWithExtensionBlock = false; while (!DoneWithExtensionBlock) { @@ -4572,8 +4679,16 @@ bool ASTReader::readASTFileControlBlock( } } } + Stream = SavedStream; } + // Scan for the UNHASHED_CONTROL_BLOCK_ID block. + if (readUnhashedControlBlockImpl( + nullptr, Bytes, ARR_ConfigurationMismatch | ARR_OutOfDate, + /*AllowCompatibleConfigurationMismatch*/ false, &Listener, + ValidateDiagnosticOptions) != Success) + return true; + return false; } diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index cdc1cf5bcb0..b13a4e1ff4b 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -18,8 +18,8 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTUnresolvedSet.h" #include "clang/AST/Decl.h" -#include "clang/AST/DeclContextInternals.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclContextInternals.h" #include "clang/AST/DeclFriend.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" @@ -33,8 +33,8 @@ #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/FileSystemOptions.h" -#include "clang/Basic/LangOptions.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" #include "clang/Basic/Module.h" #include "clang/Basic/ObjCRuntime.h" #include "clang/Basic/SourceManager.h" @@ -64,9 +64,9 @@ #include "llvm/ADT/Hashing.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" -#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Bitcode/BitCodes.h" #include "llvm/Bitcode/BitstreamWriter.h" @@ -79,6 +79,7 @@ #include "llvm/Support/OnDiskHashTable.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" +#include "llvm/Support/SHA1.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cassert> @@ -1016,7 +1017,6 @@ void ASTWriter::WriteBlockInfoBlock() { // Control Block. BLOCK(CONTROL_BLOCK); RECORD(METADATA); - RECORD(SIGNATURE); RECORD(MODULE_NAME); RECORD(MODULE_DIRECTORY); RECORD(MODULE_MAP_FILE); @@ -1029,7 +1029,6 @@ void ASTWriter::WriteBlockInfoBlock() { BLOCK(OPTIONS_BLOCK); RECORD(LANGUAGE_OPTIONS); RECORD(TARGET_OPTIONS); - RECORD(DIAGNOSTIC_OPTIONS); RECORD(FILE_SYSTEM_OPTIONS); RECORD(HEADER_SEARCH_OPTIONS); RECORD(PREPROCESSOR_OPTIONS); @@ -1065,7 +1064,6 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(UPDATE_VISIBLE); RECORD(DECL_UPDATE_OFFSETS); RECORD(DECL_UPDATES); - RECORD(DIAG_PRAGMA_MAPPINGS); RECORD(CUDA_SPECIAL_DECL_REFS); RECORD(HEADER_SEARCH_TABLE); RECORD(FP_PRAGMA_OPTIONS); @@ -1258,6 +1256,11 @@ void ASTWriter::WriteBlockInfoBlock() { BLOCK(EXTENSION_BLOCK); RECORD(EXTENSION_METADATA); + BLOCK(UNHASHED_CONTROL_BLOCK); + RECORD(SIGNATURE); + RECORD(DIAGNOSTIC_OPTIONS); + RECORD(DIAG_PRAGMA_MAPPINGS); + #undef RECORD #undef BLOCK Stream.ExitBlock(); @@ -1320,21 +1323,73 @@ adjustFilenameForRelocatableAST(const char *Filename, StringRef BaseDir) { return Filename + Pos; } -static ASTFileSignature getSignature() { - while (true) { - if (ASTFileSignature S = llvm::sys::Process::GetRandomNumber()) - return S; - // Rely on GetRandomNumber to eventually return non-zero... +ASTFileSignature ASTWriter::createSignature(StringRef Bytes) { + // Calculate the hash till start of UNHASHED_CONTROL_BLOCK. + llvm::SHA1 Hasher; + Hasher.update(ArrayRef<uint8_t>(Bytes.bytes_begin(), Bytes.size())); + auto Hash = Hasher.result(); + + // Convert to an array [5*i32]. + ASTFileSignature Signature; + auto LShift = [&](unsigned char Val, unsigned Shift) { + return (uint32_t)Val << Shift; + }; + for (int I = 0; I != 5; ++I) + Signature[I] = LShift(Hash[I * 4 + 0], 24) | LShift(Hash[I * 4 + 1], 16) | + LShift(Hash[I * 4 + 2], 8) | LShift(Hash[I * 4 + 3], 0); + + return Signature; +} + +ASTFileSignature ASTWriter::writeUnhashedControlBlock(Preprocessor &PP, + ASTContext &Context) { + // Flush first to prepare the PCM hash (signature). + Stream.FlushToWord(); + auto StartOfUnhashedControl = Stream.GetCurrentBitNo() >> 3; + + // Enter the block and prepare to write records. + RecordData Record; + Stream.EnterSubblock(UNHASHED_CONTROL_BLOCK_ID, 5); + + // For implicit modules, write the hash of the PCM as its signature. + ASTFileSignature Signature; + if (WritingModule && + PP.getHeaderSearchInfo().getHeaderSearchOpts().ModulesHashContent) { + Signature = createSignature(StringRef(Buffer.begin(), StartOfUnhashedControl)); + Record.append(Signature.begin(), Signature.end()); + Stream.EmitRecord(SIGNATURE, Record); + Record.clear(); } + + // Diagnostic options. + const auto &Diags = Context.getDiagnostics(); + const DiagnosticOptions &DiagOpts = Diags.getDiagnosticOptions(); +#define DIAGOPT(Name, Bits, Default) Record.push_back(DiagOpts.Name); +#define ENUM_DIAGOPT(Name, Type, Bits, Default) \ + Record.push_back(static_cast<unsigned>(DiagOpts.get##Name())); +#include "clang/Basic/DiagnosticOptions.def" + Record.push_back(DiagOpts.Warnings.size()); + for (unsigned I = 0, N = DiagOpts.Warnings.size(); I != N; ++I) + AddString(DiagOpts.Warnings[I], Record); + Record.push_back(DiagOpts.Remarks.size()); + for (unsigned I = 0, N = DiagOpts.Remarks.size(); I != N; ++I) + AddString(DiagOpts.Remarks[I], Record); + // Note: we don't serialize the log or serialization file names, because they + // are generally transient files and will almost always be overridden. + Stream.EmitRecord(DIAGNOSTIC_OPTIONS, Record); + + // Write out the diagnostic/pragma mappings. + WritePragmaDiagnosticMappings(Diags, /* IsModule = */ WritingModule); + + // Leave the options block. + Stream.ExitBlock(); + return Signature; } /// \brief Write the control block. -uint64_t ASTWriter::WriteControlBlock(Preprocessor &PP, - ASTContext &Context, - StringRef isysroot, - const std::string &OutputFile) { - ASTFileSignature Signature = 0; - +void ASTWriter::WriteControlBlock(Preprocessor &PP, ASTContext &Context, + StringRef isysroot, + const std::string &OutputFile) { using namespace llvm; Stream.EnterSubblock(CONTROL_BLOCK_ID, 5); RecordData Record; @@ -1362,15 +1417,6 @@ uint64_t ASTWriter::WriteControlBlock(Preprocessor &PP, getClangFullRepositoryVersion()); } if (WritingModule) { - // For implicit modules we output a signature that we can use to ensure - // duplicate module builds don't collide in the cache as their output order - // is non-deterministic. - // FIXME: Remove this when output is deterministic. - if (Context.getLangOpts().ImplicitModules) { - Signature = getSignature(); - RecordData::value_type Record[] = {Signature}; - Stream.EmitRecord(SIGNATURE, Record); - } // Module name auto Abbrev = std::make_shared<BitCodeAbbrev>(); @@ -1443,9 +1489,15 @@ uint64_t ASTWriter::WriteControlBlock(Preprocessor &PP, Record.push_back((unsigned)M.Kind); // FIXME: Stable encoding AddSourceLocation(M.ImportLoc, Record); - Record.push_back(M.File->getSize()); - Record.push_back(getTimestampForOutput(M.File)); - Record.push_back(M.Signature); + + // If we have calculated signature, there is no need to store + // the size or timestamp. + Record.push_back(M.Signature ? 0 : M.File->getSize()); + Record.push_back(M.Signature ? 0 : getTimestampForOutput(M.File)); + + for (auto I : M.Signature) + Record.push_back(I); + AddPath(M.FileName, Record); } Stream.EmitRecord(IMPORTS, Record); @@ -1508,24 +1560,6 @@ uint64_t ASTWriter::WriteControlBlock(Preprocessor &PP, } Stream.EmitRecord(TARGET_OPTIONS, Record); - // Diagnostic options. - Record.clear(); - const DiagnosticOptions &DiagOpts - = Context.getDiagnostics().getDiagnosticOptions(); -#define DIAGOPT(Name, Bits, Default) Record.push_back(DiagOpts.Name); -#define ENUM_DIAGOPT(Name, Type, Bits, Default) \ - Record.push_back(static_cast<unsigned>(DiagOpts.get##Name())); -#include "clang/Basic/DiagnosticOptions.def" - Record.push_back(DiagOpts.Warnings.size()); - for (unsigned I = 0, N = DiagOpts.Warnings.size(); I != N; ++I) - AddString(DiagOpts.Warnings[I], Record); - Record.push_back(DiagOpts.Remarks.size()); - for (unsigned I = 0, N = DiagOpts.Remarks.size(); I != N; ++I) - AddString(DiagOpts.Remarks[I], Record); - // Note: we don't serialize the log or serialization file names, because they - // are generally transient files and will almost always be overridden. - Stream.EmitRecord(DIAGNOSTIC_OPTIONS, Record); - // File system options. Record.clear(); const FileSystemOptions &FSOpts = @@ -1639,7 +1673,6 @@ uint64_t ASTWriter::WriteControlBlock(Preprocessor &PP, PP.getHeaderSearchInfo().getHeaderSearchOpts(), PP.getLangOpts().Modules); Stream.ExitBlock(); - return Signature; } namespace { @@ -4267,9 +4300,10 @@ void ASTWriter::SetSelectorOffset(Selector Sel, uint32_t Offset) { } ASTWriter::ASTWriter(llvm::BitstreamWriter &Stream, + SmallVectorImpl<char> &Buffer, ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions, bool IncludeTimestamps) - : Stream(Stream), IncludeTimestamps(IncludeTimestamps) { + : Stream(Stream), Buffer(Buffer), IncludeTimestamps(IncludeTimestamps) { for (const auto &Ext : Extensions) { if (auto Writer = Ext->createExtensionWriter(*this)) ModuleFileExtensionWriters.push_back(std::move(Writer)); @@ -4289,9 +4323,10 @@ time_t ASTWriter::getTimestampForOutput(const FileEntry *E) const { return IncludeTimestamps ? E->getModificationTime() : 0; } -uint64_t ASTWriter::WriteAST(Sema &SemaRef, const std::string &OutputFile, - Module *WritingModule, StringRef isysroot, - bool hasErrors) { +ASTFileSignature ASTWriter::WriteAST(Sema &SemaRef, + const std::string &OutputFile, + Module *WritingModule, StringRef isysroot, + bool hasErrors) { WritingAST = true; ASTHasCompilerErrors = hasErrors; @@ -4327,9 +4362,9 @@ static void AddLazyVectorDecls(ASTWriter &Writer, Vector &Vec, } } -uint64_t ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, - const std::string &OutputFile, - Module *WritingModule) { +ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, + const std::string &OutputFile, + Module *WritingModule) { using namespace llvm; bool isModule = WritingModule != nullptr; @@ -4477,7 +4512,7 @@ uint64_t ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, } // Write the control block - uint64_t Signature = WriteControlBlock(PP, Context, isysroot, OutputFile); + WriteControlBlock(PP, Context, isysroot, OutputFile); // Write the remaining AST contents. Stream.EnterSubblock(AST_BLOCK_ID, 5); @@ -4694,7 +4729,6 @@ uint64_t ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, WriteOpenCLExtensionTypes(SemaRef); WriteOpenCLExtensionDecls(SemaRef); WriteCUDAPragmas(SemaRef); - WritePragmaDiagnosticMappings(Context.getDiagnostics(), isModule); // If we're emitting a module, write out the submodule information. if (WritingModule) @@ -4823,7 +4857,7 @@ uint64_t ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, for (const auto &ExtWriter : ModuleFileExtensionWriters) WriteModuleFileExtension(SemaRef, *ExtWriter); - return Signature; + return writeUnhashedControlBlock(PP, Context); } void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) { diff --git a/clang/lib/Serialization/GeneratePCH.cpp b/clang/lib/Serialization/GeneratePCH.cpp index 7f1b75055b4..141a5594aed 100644 --- a/clang/lib/Serialization/GeneratePCH.cpp +++ b/clang/lib/Serialization/GeneratePCH.cpp @@ -28,7 +28,7 @@ PCHGenerator::PCHGenerator( bool AllowASTWithErrors, bool IncludeTimestamps) : PP(PP), OutputFile(OutputFile), isysroot(isysroot.str()), SemaPtr(nullptr), Buffer(Buffer), Stream(Buffer->Data), - Writer(Stream, Extensions, IncludeTimestamps), + Writer(Stream, Buffer->Data, Extensions, IncludeTimestamps), AllowASTWithErrors(AllowASTWithErrors) { Buffer->IsComplete = false; } diff --git a/clang/lib/Serialization/GlobalModuleIndex.cpp b/clang/lib/Serialization/GlobalModuleIndex.cpp index ae5796ede12..6978e7e0977 100644 --- a/clang/lib/Serialization/GlobalModuleIndex.cpp +++ b/clang/lib/Serialization/GlobalModuleIndex.cpp @@ -376,6 +376,15 @@ namespace { /// \brief The set of modules on which this module depends. Each entry is /// a module ID. SmallVector<unsigned, 4> Dependencies; + ASTFileSignature Signature; + }; + + struct ImportedModuleFileInfo { + off_t StoredSize; + time_t StoredModTime; + ASTFileSignature StoredSignature; + ImportedModuleFileInfo(off_t Size, time_t ModTime, ASTFileSignature Sig) + : StoredSize(Size), StoredModTime(ModTime), StoredSignature(Sig) {} }; /// \brief Builder that generates the global module index file. @@ -383,12 +392,20 @@ namespace { FileManager &FileMgr; const PCHContainerReader &PCHContainerRdr; - /// \brief Mapping from files to module file information. + /// Mapping from files to module file information. typedef llvm::MapVector<const FileEntry *, ModuleFileInfo> ModuleFilesMap; - /// \brief Information about each of the known module files. + /// Information about each of the known module files. ModuleFilesMap ModuleFiles; + /// \brief Mapping from the imported module file to the imported + /// information. + typedef std::multimap<const FileEntry *, ImportedModuleFileInfo> + ImportedModuleFilesMap; + + /// \brief Information about each importing of a module file. + ImportedModuleFilesMap ImportedModuleFiles; + /// \brief Mapping from identifiers to the list of module file IDs that /// consider this identifier to be interesting. typedef llvm::StringMap<SmallVector<unsigned, 2> > InterestingIdentifierMap; @@ -424,7 +441,8 @@ namespace { bool loadModuleFile(const FileEntry *File); /// \brief Write the index to the given bitstream. - void writeIndex(llvm::BitstreamWriter &Stream); + /// \returns true if an error occurred, false otherwise. + bool writeIndex(llvm::BitstreamWriter &Stream); }; } @@ -515,7 +533,7 @@ bool GlobalModuleIndexBuilder::loadModuleFile(const FileEntry *File) { unsigned ID = getModuleFileInfo(File).ID; // Search for the blocks and records we care about. - enum { Other, ControlBlock, ASTBlock } State = Other; + enum { Other, ControlBlock, ASTBlock, DiagnosticOptionsBlock } State = Other; bool Done = false; while (!Done) { llvm::BitstreamEntry Entry = InStream.advance(); @@ -553,6 +571,15 @@ bool GlobalModuleIndexBuilder::loadModuleFile(const FileEntry *File) { continue; } + if (Entry.ID == UNHASHED_CONTROL_BLOCK_ID) { + if (InStream.EnterSubBlock(UNHASHED_CONTROL_BLOCK_ID)) + return true; + + // Found the Diagnostic Options block. + State = DiagnosticOptionsBlock; + continue; + } + if (InStream.SkipBlock()) return true; @@ -587,7 +614,10 @@ bool GlobalModuleIndexBuilder::loadModuleFile(const FileEntry *File) { // Skip the stored signature. // FIXME: we could read the signature out of the import and validate it. - Idx++; + ASTFileSignature StoredSignature = { + {{(uint32_t)Record[Idx++], (uint32_t)Record[Idx++], + (uint32_t)Record[Idx++], (uint32_t)Record[Idx++], + (uint32_t)Record[Idx++]}}}; // Retrieve the imported file name. unsigned Length = Record[Idx++]; @@ -599,11 +629,16 @@ bool GlobalModuleIndexBuilder::loadModuleFile(const FileEntry *File) { const FileEntry *DependsOnFile = FileMgr.getFile(ImportedFile, /*openFile=*/false, /*cacheFailure=*/false); - if (!DependsOnFile || - (StoredSize != DependsOnFile->getSize()) || - (StoredModTime != DependsOnFile->getModificationTime())) + + if (!DependsOnFile) return true; + // Save the information in ImportedModuleFileInfo so we can verify after + // loading all pcms. + ImportedModuleFiles.insert(std::make_pair( + DependsOnFile, ImportedModuleFileInfo(StoredSize, StoredModTime, + StoredSignature))); + // Record the dependency. unsigned DependsOnID = getModuleFileInfo(DependsOnFile).ID; getModuleFileInfo(File).Dependencies.push_back(DependsOnID); @@ -632,6 +667,12 @@ bool GlobalModuleIndexBuilder::loadModuleFile(const FileEntry *File) { } } + // Get Signature. + if (State == DiagnosticOptionsBlock && Code == SIGNATURE) + getModuleFileInfo(File).Signature = { + {{(uint32_t)Record[0], (uint32_t)Record[1], (uint32_t)Record[2], + (uint32_t)Record[3], (uint32_t)Record[4]}}}; + // We don't care about this record. } @@ -680,7 +721,20 @@ public: } -void GlobalModuleIndexBuilder::writeIndex(llvm::BitstreamWriter &Stream) { +bool GlobalModuleIndexBuilder::writeIndex(llvm::BitstreamWriter &Stream) { + for (auto MapEntry : ImportedModuleFiles) { + auto *File = MapEntry.first; + ImportedModuleFileInfo &Info = MapEntry.second; + if (getModuleFileInfo(File).Signature) { + if (getModuleFileInfo(File).Signature != Info.StoredSignature) + // Verify Signature. + return true; + } else if (Info.StoredSize != File->getSize() || + Info.StoredModTime != File->getModificationTime()) + // Verify Size and ModTime. + return true; + } + using namespace llvm; // Emit the file header. @@ -756,6 +810,7 @@ void GlobalModuleIndexBuilder::writeIndex(llvm::BitstreamWriter &Stream) { } Stream.ExitBlock(); + return false; } GlobalModuleIndex::ErrorCode @@ -816,7 +871,8 @@ GlobalModuleIndex::writeIndex(FileManager &FileMgr, SmallVector<char, 16> OutputBuffer; { llvm::BitstreamWriter OutputStream(OutputBuffer); - Builder.writeIndex(OutputStream); + if (Builder.writeIndex(OutputStream)) + return EC_IOError; } // Write the global index file to a temporary file. |