diff options
Diffstat (limited to 'lib/Serialization')
-rw-r--r-- | lib/Serialization/ASTReader.cpp | 156 | ||||
-rw-r--r-- | lib/Serialization/ASTWriter.cpp | 154 |
2 files changed, 276 insertions, 34 deletions
diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 559d927dac..60eeb6c308 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -40,12 +40,14 @@ #include "llvm/Bitcode/BitstreamReader.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/system_error.h" #include <algorithm> #include <iterator> #include <cstdio> #include <sys/stat.h> + using namespace clang; using namespace clang::serialization; @@ -1244,7 +1246,7 @@ ASTReader::ASTReadResult ASTReader::ReadSLocEntryRecord(unsigned ID) { return Failure; } - if (Record.size() < 10) { + if (Record.size() < 6) { Error("source location entry is incorrect"); return Failure; } @@ -1269,15 +1271,7 @@ ASTReader::ASTReadResult ASTReader::ReadSLocEntryRecord(unsigned ID) { if (Record[3]) const_cast<SrcMgr::FileInfo&>(SourceMgr.getSLocEntry(FID).getFile()) .setHasLineDirectives(); - - // Reconstruct header-search information for this file. - HeaderFileInfo HFI; - HFI.isImport = Record[6]; - HFI.DirInfo = Record[7]; - HFI.NumIncludes = Record[8]; - HFI.ControllingMacroID = Record[9]; - if (Listener) - Listener->ReadHeaderFileInfo(HFI, File->getUID()); + break; } @@ -1572,6 +1566,99 @@ PreprocessedEntity *ASTReader::LoadPreprocessedEntity(PerFileData &F) { return 0; } +namespace { + /// \brief Trait class used to search the on-disk hash table containing all of + /// the header search information. + /// + /// The on-disk hash table contains a mapping from each header path to + /// information about that header (how many times it has been included, its + /// controlling macro, etc.). Note that we actually hash based on the + /// filename, and support "deep" comparisons of file names based on current + /// inode numbers, so that the search can cope with non-normalized path names + /// and symlinks. + class HeaderFileInfoTrait { + const char *SearchPath; + struct stat SearchPathStatBuf; + llvm::Optional<int> SearchPathStatResult; + + int StatSimpleCache(const char *Path, struct stat *StatBuf) { + if (Path == SearchPath) { + if (!SearchPathStatResult) + SearchPathStatResult = stat(Path, &SearchPathStatBuf); + + *StatBuf = SearchPathStatBuf; + return *SearchPathStatResult; + } + + return stat(Path, StatBuf); + } + + public: + typedef const char *external_key_type; + typedef const char *internal_key_type; + + typedef HeaderFileInfo data_type; + + HeaderFileInfoTrait(const char *SearchPath = 0) : SearchPath(SearchPath) { } + + static unsigned ComputeHash(const char *path) { + return llvm::HashString(llvm::sys::path::filename(path)); + } + + static internal_key_type GetInternalKey(const char *path) { return path; } + + bool EqualKey(internal_key_type a, internal_key_type b) { + if (strcmp(a, b) == 0) + return true; + + if (llvm::sys::path::filename(a) != llvm::sys::path::filename(b)) + return false; + + // The file names match, but the path names don't. stat() the files to + // see if they are the same. + struct stat StatBufA, StatBufB; + if (StatSimpleCache(a, &StatBufA) || StatSimpleCache(b, &StatBufB)) + return false; + + return StatBufA.st_ino == StatBufB.st_ino; + } + + static std::pair<unsigned, unsigned> + ReadKeyDataLength(const unsigned char*& d) { + unsigned KeyLen = (unsigned) clang::io::ReadUnalignedLE16(d); + unsigned DataLen = (unsigned) *d++; + return std::make_pair(KeyLen + 1, DataLen); + } + + static internal_key_type ReadKey(const unsigned char *d, unsigned) { + return (const char *)d; + } + + static data_type ReadData(const internal_key_type, const unsigned char *d, + unsigned DataLen) { + const unsigned char *End = d + DataLen; + using namespace clang::io; + HeaderFileInfo HFI; + unsigned Flags = *d++; + HFI.isImport = (Flags >> 3) & 0x01; + HFI.DirInfo = (Flags >> 1) & 0x03; + HFI.Resolved = Flags & 0x01; + HFI.NumIncludes = ReadUnalignedLE16(d); + HFI.ControllingMacroID = ReadUnalignedLE32(d); + assert(End == d && "Wrong data length in HeaderFileInfo deserialization"); + (void)End; + + // This HeaderFileInfo was externally loaded. + HFI.External = true; + return HFI; + } + }; +} + +/// \brief The on-disk hash table used for the global method pool. +typedef OnDiskChainedHashTable<HeaderFileInfoTrait> + HeaderFileInfoLookupTable; + void ASTReader::SetIdentifierIsMacro(IdentifierInfo *II, PerFileData &F, uint64_t Offset) { // Note that this identifier has a macro definition. @@ -2152,11 +2239,24 @@ ASTReader::ReadASTBlock(PerFileData &F) { PragmaDiagMappings.insert(PragmaDiagMappings.end(), Record.begin(), Record.end()); break; - + case CUDA_SPECIAL_DECL_REFS: // Later tables overwrite earlier ones. CUDASpecialDeclRefs.swap(Record); break; + + case HEADER_SEARCH_TABLE: + F.HeaderFileInfoTableData = BlobStart; + F.LocalNumHeaderFileInfos = Record[1]; + if (Record[0]) { + F.HeaderFileInfoTable + = HeaderFileInfoLookupTable::Create( + (const unsigned char *)F.HeaderFileInfoTableData + Record[0], + (const unsigned char *)F.HeaderFileInfoTableData); + if (PP) + PP->getHeaderSearchInfo().SetExternalSource(this); + } + break; } First = false; } @@ -2400,7 +2500,8 @@ void ASTReader::InitializeContext(ASTContext &Ctx) { PP->getIdentifierTable().setExternalIdentifierLookup(this); PP->getHeaderSearchInfo().SetExternalLookup(this); PP->setExternalSource(this); - + PP->getHeaderSearchInfo().SetExternalSource(this); + // If we have an update block for the TU waiting, we have to add it before // deserializing the decl. DeclContextOffsetsMap::iterator DCU = DeclContextOffsets.find(0); @@ -2709,6 +2810,31 @@ PreprocessedEntity *ASTReader::ReadPreprocessedEntity(uint64_t Offset) { return LoadPreprocessedEntity(*F); } +HeaderFileInfo ASTReader::GetHeaderFileInfo(const FileEntry *FE) { + HeaderFileInfoTrait Trait(FE->getName()); + for (unsigned I = 0, N = Chain.size(); I != N; ++I) { + PerFileData &F = *Chain[I]; + HeaderFileInfoLookupTable *Table + = static_cast<HeaderFileInfoLookupTable *>(F.HeaderFileInfoTable); + if (!Table) + continue; + + // Look in the on-disk hash table for an entry for this file name. + HeaderFileInfoLookupTable::iterator Pos = Table->find(FE->getName(), + &Trait); + if (Pos == Table->end()) + continue; + + HeaderFileInfo HFI = *Pos; + if (Listener) + Listener->ReadHeaderFileInfo(HFI, FE->getUID()); + + return HFI; + } + + return HeaderFileInfo(); +} + void ASTReader::ReadPragmaDiagnosticMappings(Diagnostic &Diag) { unsigned Idx = 0; while (Idx < PragmaDiagMappings.size()) { @@ -4765,7 +4891,10 @@ ASTReader::PerFileData::PerFileData(ASTFileType Ty) : Type(Ty), SizeInBits(0), LocalNumSLocEntries(0), SLocOffsets(0), LocalSLocSize(0), LocalNumIdentifiers(0), IdentifierOffsets(0), IdentifierTableData(0), IdentifierLookupTable(0), LocalNumMacroDefinitions(0), - MacroDefinitionOffsets(0), LocalNumSelectors(0), SelectorOffsets(0), + MacroDefinitionOffsets(0), + LocalNumHeaderFileInfos(0), HeaderFileInfoTableData(0), + HeaderFileInfoTable(0), + LocalNumSelectors(0), SelectorOffsets(0), SelectorLookupTableData(0), SelectorLookupTable(0), LocalNumDecls(0), DeclOffsets(0), LocalNumCXXBaseSpecifiers(0), CXXBaseSpecifiersOffsets(0), LocalNumTypes(0), TypeOffsets(0), StatCache(0), @@ -4774,6 +4903,7 @@ ASTReader::PerFileData::PerFileData(ASTFileType Ty) ASTReader::PerFileData::~PerFileData() { delete static_cast<ASTIdentifierLookupTable *>(IdentifierLookupTable); + delete static_cast<HeaderFileInfoLookupTable *>(HeaderFileInfoTable); delete static_cast<ASTSelectorLookupTable *>(SelectorLookupTable); } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 624cf10349..e4fe99cde3 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -45,6 +45,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include <cstdio> +#include <string.h> using namespace clang; using namespace clang::serialization; @@ -739,7 +740,8 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(DECL_UPDATES); RECORD(CXX_BASE_SPECIFIER_OFFSETS); RECORD(DIAG_PRAGMA_MAPPINGS); - + RECORD(HEADER_SEARCH_TABLE); + // SourceManager Block. BLOCK(SOURCE_MANAGER_BLOCK); RECORD(SM_SLOC_FILE_ENTRY); @@ -1146,11 +1148,6 @@ static unsigned CreateSLocFileAbbrev(llvm::BitstreamWriter &Stream) { // FileEntry fields. Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 12)); // Size Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 32)); // Modification time - // HeaderFileInfo fields. - Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isImport - Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // DirInfo - Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // NumIncludes - Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // ControllingMacro Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name return Stream.EmitAbbrev(Abbrev); } @@ -1193,6 +1190,135 @@ static unsigned CreateSLocInstantiationAbbrev(llvm::BitstreamWriter &Stream) { return Stream.EmitAbbrev(Abbrev); } +namespace { + // Trait used for the on-disk hash table of header search information. + class HeaderFileInfoTrait { + ASTWriter &Writer; + HeaderSearch &HS; + + public: + HeaderFileInfoTrait(ASTWriter &Writer, HeaderSearch &HS) + : Writer(Writer), HS(HS) { } + + typedef const char *key_type; + typedef key_type key_type_ref; + + typedef HeaderFileInfo data_type; + typedef const data_type &data_type_ref; + + static unsigned ComputeHash(const char *path) { + // The hash is based only on the filename portion of the key, so that the + // reader can match based on filenames when symlinking or excess path + // elements ("foo/../", "../") change the form of the name. However, + // complete path is still the key. + return llvm::HashString(llvm::sys::path::filename(path)); + } + + std::pair<unsigned,unsigned> + EmitKeyDataLength(llvm::raw_ostream& Out, const char *path, + data_type_ref Data) { + unsigned StrLen = strlen(path); + clang::io::Emit16(Out, StrLen); + unsigned DataLen = 1 + 2 + 4; + clang::io::Emit8(Out, DataLen); + return std::make_pair(StrLen + 1, DataLen); + } + + void EmitKey(llvm::raw_ostream& Out, const char *path, unsigned KeyLen) { + Out.write(path, KeyLen); + } + + void EmitData(llvm::raw_ostream &Out, key_type_ref, + data_type_ref Data, unsigned DataLen) { + using namespace clang::io; + uint64_t Start = Out.tell(); (void)Start; + + unsigned char Flags = (Data.isImport << 3) + | (Data.DirInfo << 1) + | Data.Resolved; + Emit8(Out, (uint8_t)Flags); + Emit16(Out, (uint16_t) Data.NumIncludes); + + if (!Data.ControllingMacro) + Emit32(Out, (uint32_t)Data.ControllingMacroID); + else + Emit32(Out, (uint32_t)Writer.getIdentifierRef(Data.ControllingMacro)); + assert(Out.tell() - Start == DataLen && "Wrong data length"); + } + }; +} // end anonymous namespace + +/// \brief Write the header search block for the list of files that +/// +/// \param HS The header search structure to save. +/// +/// \param Chain Whether we're creating a chained AST file. +void ASTWriter::WriteHeaderSearch(HeaderSearch &HS, const char* isysroot) { + llvm::SmallVector<const FileEntry *, 16> FilesByUID; + HS.getFileMgr().GetUniqueIDMapping(FilesByUID); + + if (FilesByUID.size() > HS.header_file_size()) + FilesByUID.resize(HS.header_file_size()); + + HeaderFileInfoTrait GeneratorTrait(*this, HS); + OnDiskChainedHashTableGenerator<HeaderFileInfoTrait> Generator; + llvm::SmallVector<const char *, 4> SavedStrings; + unsigned NumHeaderSearchEntries = 0; + for (unsigned UID = 0, LastUID = FilesByUID.size(); UID != LastUID; ++UID) { + const FileEntry *File = FilesByUID[UID]; + if (!File) + continue; + + const HeaderFileInfo &HFI = HS.header_file_begin()[UID]; + if (HFI.External && Chain) + continue; + + // Turn the file name into an absolute path, if it isn't already. + const char *Filename = File->getName(); + Filename = adjustFilenameForRelocatablePCH(Filename, isysroot); + + // If we performed any translation on the file name at all, we need to + // save this string, since the generator will refer to it later. + if (Filename != File->getName()) { + Filename = strdup(Filename); + SavedStrings.push_back(Filename); + } + + Generator.insert(Filename, HFI, GeneratorTrait); + ++NumHeaderSearchEntries; + } + + // Create the on-disk hash table in a buffer. + llvm::SmallString<4096> TableData; + uint32_t BucketOffset; + { + llvm::raw_svector_ostream Out(TableData); + // Make sure that no bucket is at offset 0 + clang::io::Emit32(Out, 0); + BucketOffset = Generator.Emit(Out, GeneratorTrait); + } + + // Create a blob abbreviation + using namespace llvm; + BitCodeAbbrev *Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(HEADER_SEARCH_TABLE)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); + unsigned TableAbbrev = Stream.EmitAbbrev(Abbrev); + + // Write the stat cache + RecordData Record; + Record.push_back(HEADER_SEARCH_TABLE); + Record.push_back(BucketOffset); + Record.push_back(NumHeaderSearchEntries); + Stream.EmitRecordWithBlob(TableAbbrev, Record, TableData.str()); + + // Free all of the strings we had to duplicate. + for (unsigned I = 0, N = SavedStrings.size(); I != N; ++I) + free((void*)SavedStrings[I]); +} + /// \brief Writes the block containing the serialized form of the /// source manager. /// @@ -1294,16 +1420,6 @@ void ASTWriter::WriteSourceManagerBlock(SourceManager &SourceMgr, Record.push_back(Content->Entry->getSize()); Record.push_back(Content->Entry->getModificationTime()); - // Emit header-search information associated with this file. - HeaderFileInfo HFI; - HeaderSearch &HS = PP.getHeaderSearchInfo(); - if (Content->Entry->getUID() < HS.header_file_size()) - HFI = HS.header_file_begin()[Content->Entry->getUID()]; - Record.push_back(HFI.isImport); - Record.push_back(HFI.DirInfo); - Record.push_back(HFI.NumIncludes); - AddIdentifierRef(HFI.ControllingMacro, Record); - // Turn the file name into an absolute path, if it isn't already. const char *Filename = Content->Entry->getName(); llvm::SmallString<128> FilePath(Filename); @@ -1312,11 +1428,6 @@ void ASTWriter::WriteSourceManagerBlock(SourceManager &SourceMgr, Filename = adjustFilenameForRelocatablePCH(Filename, isysroot); Stream.EmitRecordWithBlob(SLocFileAbbrv, Record, Filename); - - // FIXME: For now, preload all file source locations, so that - // we get the appropriate File entries in the reader. This is - // a temporary measure. - PreloadSLocs.push_back(BaseSLocID + SLocEntryOffsets.size()); } else { // The source location entry is a buffer. The blob associated // with this entry contains the contents of the buffer. @@ -2596,6 +2707,7 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls, Stream.ExitBlock(); WritePreprocessor(PP); + WriteHeaderSearch(PP.getHeaderSearchInfo(), isysroot); WriteSelectors(SemaRef); WriteReferencedSelectorsPool(SemaRef); WriteIdentifierTable(PP); |