diff options
Diffstat (limited to 'clang/lib/Serialization')
-rw-r--r-- | clang/lib/Serialization/ASTReader.cpp | 86 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTWriter.cpp | 59 |
2 files changed, 123 insertions, 22 deletions
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 60be1570bda..20c6cbec572 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -1247,6 +1247,12 @@ void ASTReader::Error(unsigned DiagID, Diag(DiagID) << Arg1 << Arg2; } +void ASTReader::Error(unsigned DiagID, StringRef Arg1, StringRef Arg2, + unsigned Select) const { + if (!Diags.isDiagnosticInFlight()) + Diag(DiagID) << Arg1 << Arg2 << Select; +} + void ASTReader::Error(llvm::Error &&Err) const { Error(toString(std::move(Err))); } @@ -2241,6 +2247,24 @@ ASTReader::readInputFileInfo(ModuleFile &F, unsigned ID) { R.TopLevelModuleMap = static_cast<bool>(Record[5]); R.Filename = Blob; ResolveImportedPath(F, R.Filename); + + Expected<llvm::BitstreamEntry> MaybeEntry = Cursor.advance(); + if (!MaybeEntry) // FIXME this drops errors on the floor. + consumeError(MaybeEntry.takeError()); + llvm::BitstreamEntry Entry = MaybeEntry.get(); + assert(Entry.Kind == llvm::BitstreamEntry::Record && + "expected record type for input file hash"); + + Record.clear(); + if (Expected<unsigned> Maybe = Cursor.readRecord(Entry.ID, Record)) + assert(static_cast<InputFileRecordTypes>(Maybe.get()) == INPUT_FILE_HASH && + "invalid record type for input file hash"); + else { + // FIXME this drops errors on the floor. + consumeError(Maybe.takeError()); + } + R.ContentHash = (static_cast<uint64_t>(Record[1]) << 32) | + static_cast<uint64_t>(Record[0]); return R; } @@ -2271,6 +2295,7 @@ InputFile ASTReader::getInputFile(ModuleFile &F, unsigned ID, bool Complain) { bool Overridden = FI.Overridden; bool Transient = FI.Transient; StringRef Filename = FI.Filename; + uint64_t StoredContentHash = FI.ContentHash; const FileEntry *File = nullptr; if (auto FE = FileMgr.getFile(Filename, /*OpenFile=*/false)) @@ -2325,14 +2350,46 @@ InputFile ASTReader::getInputFile(ModuleFile &F, unsigned ID, bool Complain) { } } - bool IsOutOfDate = false; + enum ModificationType { + Size, + ModTime, + Content, + None, + }; + auto HasInputFileChanged = [&]() { + if (StoredSize != File->getSize()) + return ModificationType::Size; + if (!DisableValidation && StoredTime && + StoredTime != File->getModificationTime()) { + // In case the modification time changes but not the content, + // accept the cached file as legit. + if (ValidateASTInputFilesContent && + StoredContentHash != static_cast<uint64_t>(llvm::hash_code(-1))) { + auto MemBuffOrError = FileMgr.getBufferForFile(File); + if (!MemBuffOrError) { + if (!Complain) + return ModificationType::ModTime; + std::string ErrorStr = "could not get buffer for file '"; + ErrorStr += File->getName(); + ErrorStr += "'"; + Error(ErrorStr); + return ModificationType::ModTime; + } + auto ContentHash = hash_value(MemBuffOrError.get()->getBuffer()); + if (StoredContentHash == static_cast<uint64_t>(ContentHash)) + return ModificationType::None; + return ModificationType::Content; + } + return ModificationType::ModTime; + } + return ModificationType::None; + }; + + bool IsOutOfDate = false; + auto FileChange = HasInputFileChanged(); // For an overridden file, there is nothing to validate. - if (!Overridden && // - (StoredSize != File->getSize() || - (StoredTime && StoredTime != File->getModificationTime() && - !DisableValidation) - )) { + if (!Overridden && FileChange != ModificationType::None) { if (Complain) { // Build a list of the PCH imports that got us here (in reverse). SmallVector<ModuleFile *, 4> ImportStack(1, &F); @@ -2341,13 +2398,17 @@ InputFile ASTReader::getInputFile(ModuleFile &F, unsigned ID, bool Complain) { // The top-level PCH is stale. StringRef TopLevelPCHName(ImportStack.back()->FileName); - unsigned DiagnosticKind = moduleKindForDiagnostic(ImportStack.back()->Kind); + unsigned DiagnosticKind = + moduleKindForDiagnostic(ImportStack.back()->Kind); if (DiagnosticKind == 0) - Error(diag::err_fe_pch_file_modified, Filename, TopLevelPCHName); + Error(diag::err_fe_pch_file_modified, Filename, TopLevelPCHName, + (unsigned)FileChange); else if (DiagnosticKind == 1) - Error(diag::err_fe_module_file_modified, Filename, TopLevelPCHName); + Error(diag::err_fe_module_file_modified, Filename, TopLevelPCHName, + (unsigned)FileChange); else - Error(diag::err_fe_ast_file_modified, Filename, TopLevelPCHName); + Error(diag::err_fe_ast_file_modified, Filename, TopLevelPCHName, + (unsigned)FileChange); // Print the import stack. if (ImportStack.size() > 1 && !Diags.isDiagnosticInFlight()) { @@ -5192,6 +5253,8 @@ bool ASTReader::readASTFileControlBlock( consumeError(MaybeRecordType.takeError()); } switch ((InputFileRecordTypes)MaybeRecordType.get()) { + case INPUT_FILE_HASH: + break; case INPUT_FILE: bool Overridden = static_cast<bool>(Record[3]); std::string Filename = Blob; @@ -12153,7 +12216,7 @@ ASTReader::ASTReader(Preprocessor &PP, InMemoryModuleCache &ModuleCache, StringRef isysroot, bool DisableValidation, bool AllowASTWithCompilerErrors, bool AllowConfigurationMismatch, bool ValidateSystemInputs, - bool UseGlobalIndex, + bool ValidateASTInputFilesContent, bool UseGlobalIndex, std::unique_ptr<llvm::Timer> ReadTimer) : Listener(DisableValidation ? cast<ASTReaderListener>(new SimpleASTReaderListener(PP)) @@ -12167,6 +12230,7 @@ ASTReader::ASTReader(Preprocessor &PP, InMemoryModuleCache &ModuleCache, AllowASTWithCompilerErrors(AllowASTWithCompilerErrors), AllowConfigurationMismatch(AllowConfigurationMismatch), ValidateSystemInputs(ValidateSystemInputs), + ValidateASTInputFilesContent(ValidateASTInputFilesContent), UseGlobalIndex(UseGlobalIndex), CurrSwitchCaseStmts(&SwitchCaseStmts) { SourceMgr.setExternalSLocEntrySource(this); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 3badff45fb4..5e9e650fce8 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -1099,6 +1099,7 @@ void ASTWriter::WriteBlockInfoBlock() { BLOCK(INPUT_FILES_BLOCK); RECORD(INPUT_FILE); + RECORD(INPUT_FILE_HASH); // AST Top-Level Block. BLOCK(AST_BLOCK); @@ -1764,6 +1765,7 @@ struct InputFileEntry { bool IsTransient; bool BufferOverridden; bool IsTopLevelModuleMap; + uint32_t ContentHash[2]; }; } // namespace @@ -1787,6 +1789,13 @@ void ASTWriter::WriteInputFiles(SourceManager &SourceMgr, IFAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name unsigned IFAbbrevCode = Stream.EmitAbbrev(std::move(IFAbbrev)); + // Create input file hash abbreviation. + auto IFHAbbrev = std::make_shared<BitCodeAbbrev>(); + IFHAbbrev->Add(BitCodeAbbrevOp(INPUT_FILE_HASH)); + IFHAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); + IFHAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); + unsigned IFHAbbrevCode = Stream.EmitAbbrev(std::move(IFHAbbrev)); + // Get all ContentCache objects for files, sorted by whether the file is a // system one or not. System files go at the back, users files at the front. std::deque<InputFileEntry> SortedFiles; @@ -1810,6 +1819,25 @@ void ASTWriter::WriteInputFiles(SourceManager &SourceMgr, Entry.BufferOverridden = Cache->BufferOverridden; Entry.IsTopLevelModuleMap = isModuleMap(File.getFileCharacteristic()) && File.getIncludeLoc().isInvalid(); + + auto ContentHash = hash_code(-1); + if (PP->getHeaderSearchInfo() + .getHeaderSearchOpts() + .ValidateASTInputFilesContent) { + auto *MemBuff = Cache->getRawBuffer(); + if (MemBuff) + ContentHash = hash_value(MemBuff->getBuffer()); + else + // FIXME: The path should be taken from the FileEntryRef. + PP->Diag(SourceLocation(), diag::err_module_unable_to_hash_content) + << Entry.File->getName(); + } + auto CH = llvm::APInt(64, ContentHash); + Entry.ContentHash[0] = + static_cast<uint32_t>(CH.getLoBits(32).getZExtValue()); + Entry.ContentHash[1] = + static_cast<uint32_t>(CH.getHiBits(32).getZExtValue()); + if (Entry.IsSystemFile) SortedFiles.push_back(Entry); else @@ -1834,17 +1862,26 @@ void ASTWriter::WriteInputFiles(SourceManager &SourceMgr, // Emit size/modification time for this file. // And whether this file was overridden. - RecordData::value_type Record[] = { - INPUT_FILE, - InputFileOffsets.size(), - (uint64_t)Entry.File->getSize(), - (uint64_t)getTimestampForOutput(Entry.File), - Entry.BufferOverridden, - Entry.IsTransient, - Entry.IsTopLevelModuleMap}; - - // FIXME: The path should be taken from the FileEntryRef. - EmitRecordWithPath(IFAbbrevCode, Record, Entry.File->getName()); + { + RecordData::value_type Record[] = { + INPUT_FILE, + InputFileOffsets.size(), + (uint64_t)Entry.File->getSize(), + (uint64_t)getTimestampForOutput(Entry.File), + Entry.BufferOverridden, + Entry.IsTransient, + Entry.IsTopLevelModuleMap}; + + // FIXME: The path should be taken from the FileEntryRef. + EmitRecordWithPath(IFAbbrevCode, Record, Entry.File->getName()); + } + + // Emit content hash for this file. + { + RecordData::value_type Record[] = {INPUT_FILE_HASH, Entry.ContentHash[0], + Entry.ContentHash[1]}; + Stream.EmitRecordWithAbbrev(IFHAbbrevCode, Record); + } } Stream.ExitBlock(); |