123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535 |
- //===--- GlobalModuleIndex.cpp - Global Module Index ------------*- C++ -*-===//
- //
- // The LLVM Compiler Infrastructure
- //
- // This file is distributed under the University of Illinois Open Source
- // License. See LICENSE.TXT for details.
- //
- //===----------------------------------------------------------------------===//
- //
- // This file implements the GlobalModuleIndex class.
- //
- //===----------------------------------------------------------------------===//
- #include "ASTReaderInternals.h"
- #include "clang/Basic/FileManager.h"
- #include "clang/Basic/OnDiskHashTable.h"
- #include "clang/Serialization/ASTBitCodes.h"
- #include "clang/Serialization/GlobalModuleIndex.h"
- #include "llvm/ADT/DenseMap.h"
- #include "llvm/ADT/MapVector.h"
- #include "llvm/ADT/SmallString.h"
- #include "llvm/ADT/StringExtras.h"
- #include "llvm/Bitcode/BitstreamReader.h"
- #include "llvm/Bitcode/BitstreamWriter.h"
- #include "llvm/Support/Filesystem.h"
- #include "llvm/Support/LockFileManager.h"
- #include "llvm/Support/MemoryBuffer.h"
- #include "llvm/Support/PathV2.h"
- using namespace clang;
- using namespace serialization;
- //----------------------------------------------------------------------------//
- // Shared constants
- //----------------------------------------------------------------------------//
- namespace {
- enum {
- /// \brief The block containing the index.
- GLOBAL_INDEX_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID
- };
- /// \brief Describes the record types in the index.
- enum IndexRecordTypes {
- /// \brief Contains version information and potentially other metadata,
- /// used to determine if we can read this global index file.
- METADATA,
- /// \brief Describes a module, including its file name and dependencies.
- MODULE,
- /// \brief The index for identifiers.
- IDENTIFIER_INDEX
- };
- }
- /// \brief The name of the global index file.
- static const char * const IndexFileName = "modules.idx";
- /// \brief The global index file version.
- static const unsigned CurrentVersion = 1;
- //----------------------------------------------------------------------------//
- // Global module index writer.
- //----------------------------------------------------------------------------//
- namespace {
- /// \brief Provides information about a specific module file.
- struct ModuleFileInfo {
- /// \brief The numberic ID for this module file.
- unsigned ID;
- /// \brief The set of modules on which this module depends. Each entry is
- /// a module ID.
- SmallVector<unsigned, 4> Dependencies;
- };
- /// \brief Builder that generates the global module index file.
- class GlobalModuleIndexBuilder {
- FileManager &FileMgr;
- /// \brief Mapping from files to module file information.
- typedef llvm::MapVector<const FileEntry *, ModuleFileInfo> ModuleFilesMap;
- /// \brief Information about each of the known module files.
- ModuleFilesMap ModuleFiles;
- /// \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;
- /// \brief A mapping from all interesting identifiers to the set of module
- /// files in which those identifiers are considered interesting.
- InterestingIdentifierMap InterestingIdentifiers;
-
- /// \brief Write the block-info block for the global module index file.
- void emitBlockInfoBlock(llvm::BitstreamWriter &Stream);
- /// \brief Retrieve the module file information for the given file.
- ModuleFileInfo &getModuleFileInfo(const FileEntry *File) {
- llvm::MapVector<const FileEntry *, ModuleFileInfo>::iterator Known
- = ModuleFiles.find(File);
- if (Known != ModuleFiles.end())
- return Known->second;
- unsigned NewID = ModuleFiles.size();
- ModuleFileInfo &Info = ModuleFiles[File];
- Info.ID = NewID;
- return Info;
- }
- public:
- explicit GlobalModuleIndexBuilder(FileManager &FileMgr) : FileMgr(FileMgr){}
- /// \brief Load the contents of the given module file into the builder.
- ///
- /// \returns true if an error occurred, false otherwise.
- bool loadModuleFile(const FileEntry *File);
- /// \brief Write the index to the given bitstream.
- void writeIndex(llvm::BitstreamWriter &Stream);
- };
- }
- static void emitBlockID(unsigned ID, const char *Name,
- llvm::BitstreamWriter &Stream,
- SmallVectorImpl<uint64_t> &Record) {
- Record.clear();
- Record.push_back(ID);
- Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record);
- // Emit the block name if present.
- if (Name == 0 || Name[0] == 0) return;
- Record.clear();
- while (*Name)
- Record.push_back(*Name++);
- Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record);
- }
- static void emitRecordID(unsigned ID, const char *Name,
- llvm::BitstreamWriter &Stream,
- SmallVectorImpl<uint64_t> &Record) {
- Record.clear();
- Record.push_back(ID);
- while (*Name)
- Record.push_back(*Name++);
- Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record);
- }
- void
- GlobalModuleIndexBuilder::emitBlockInfoBlock(llvm::BitstreamWriter &Stream) {
- SmallVector<uint64_t, 64> Record;
- Stream.EnterSubblock(llvm::bitc::BLOCKINFO_BLOCK_ID, 3);
- #define BLOCK(X) emitBlockID(X ## _ID, #X, Stream, Record)
- #define RECORD(X) emitRecordID(X, #X, Stream, Record)
- BLOCK(GLOBAL_INDEX_BLOCK);
- RECORD(METADATA);
- RECORD(MODULE);
- RECORD(IDENTIFIER_INDEX);
- #undef RECORD
- #undef BLOCK
- Stream.ExitBlock();
- }
- namespace clang {
- class InterestingASTIdentifierLookupTrait
- : public serialization::reader::ASTIdentifierLookupTraitBase {
- public:
- /// \brief The identifier and whether it is "interesting".
- typedef std::pair<StringRef, bool> data_type;
- data_type ReadData(const internal_key_type& k,
- const unsigned char* d,
- unsigned DataLen) {
- // The first bit indicates whether this identifier is interesting.
- // That's all we care about.
- using namespace clang::io;
- unsigned RawID = ReadUnalignedLE32(d);
- bool IsInteresting = RawID & 0x01;
- return std::make_pair(k, IsInteresting);
- }
- };
- }
- bool GlobalModuleIndexBuilder::loadModuleFile(const FileEntry *File) {
- // Open the module file.
- OwningPtr<llvm::MemoryBuffer> Buffer;
- Buffer.reset(FileMgr.getBufferForFile(File));
- if (!Buffer) {
- return true;
- }
- // Initialize the input stream
- llvm::BitstreamReader InStreamFile;
- llvm::BitstreamCursor InStream;
- InStreamFile.init((const unsigned char *)Buffer->getBufferStart(),
- (const unsigned char *)Buffer->getBufferEnd());
- InStream.init(InStreamFile);
- // Sniff for the signature.
- if (InStream.Read(8) != 'C' ||
- InStream.Read(8) != 'P' ||
- InStream.Read(8) != 'C' ||
- InStream.Read(8) != 'H') {
- return true;
- }
- // Record this module file and assign it a unique ID (if it doesn't have
- // one already).
- unsigned ID = getModuleFileInfo(File).ID;
- // Search for the blocks and records we care about.
- enum { Outer, ControlBlock, ASTBlock } State = Outer;
- bool Done = false;
- while (!Done) {
- const unsigned Flags = llvm::BitstreamCursor::AF_DontPopBlockAtEnd;
- llvm::BitstreamEntry Entry = InStream.advance(Flags);
- switch (Entry.Kind) {
- case llvm::BitstreamEntry::Error:
- return true;
- case llvm::BitstreamEntry::Record:
- // In the outer state, just skip the record. We don't care.
- if (State == Outer) {
- InStream.skipRecord(Entry.ID);
- continue;
- }
- // Handle potentially-interesting records below.
- break;
- case llvm::BitstreamEntry::SubBlock:
- if (State == Outer && Entry.ID == CONTROL_BLOCK_ID) {
- if (InStream.EnterSubBlock(CONTROL_BLOCK_ID))
- return true;
- // Found the control block.
- State = ControlBlock;
- continue;
- }
- if (State == Outer && Entry.ID == AST_BLOCK_ID) {
- if (InStream.EnterSubBlock(AST_BLOCK_ID))
- return true;
- // Found the AST block.
- State = ASTBlock;
- continue;
- }
- if (InStream.SkipBlock())
- return true;
- continue;
- case llvm::BitstreamEntry::EndBlock:
- if (State == Outer) {
- Done = true;
- }
- State = Outer;
- continue;
- }
- // Read the given record.
- SmallVector<uint64_t, 64> Record;
- StringRef Blob;
- unsigned Code = InStream.readRecord(Entry.ID, Record, &Blob);
- // Handle module dependencies.
- if (State == ControlBlock && Code == IMPORTS) {
- // Load each of the imported PCH files.
- unsigned Idx = 0, N = Record.size();
- while (Idx < N) {
- // Read information about the AST file.
- // Skip the imported kind
- ++Idx;
- // Skip the import location
- ++Idx;
- // Retrieve the imported file name.
- unsigned Length = Record[Idx++];
- SmallString<128> ImportedFile(Record.begin() + Idx,
- Record.begin() + Idx + Length);
- Idx += Length;
- // Find the imported module file.
- const FileEntry *DependsOnFile = FileMgr.getFile(ImportedFile);
- if (!DependsOnFile)
- return true;
- // Record the dependency.
- unsigned DependsOnID = getModuleFileInfo(DependsOnFile).ID;
- getModuleFileInfo(File).Dependencies.push_back(DependsOnID);
- }
- continue;
- }
- // Handle the identifier table
- if (State == ASTBlock && Code == IDENTIFIER_TABLE && Record[0] > 0) {
- typedef OnDiskChainedHashTable<InterestingASTIdentifierLookupTrait>
- InterestingIdentifierTable;
- llvm::OwningPtr<InterestingIdentifierTable>
- Table(InterestingIdentifierTable::Create(
- (const unsigned char *)Blob.data() + Record[0],
- (const unsigned char *)Blob.data()));
- for (InterestingIdentifierTable::data_iterator D = Table->data_begin(),
- DEnd = Table->data_end();
- D != DEnd; ++D) {
- std::pair<StringRef, bool> Ident = *D;
- if (Ident.second)
- InterestingIdentifiers[Ident.first].push_back(ID);
- }
- }
- // FIXME: Handle the selector table.
-
- // We don't care about this record.
- }
- return false;
- }
- namespace {
- /// \brief Trait used to generate the identifier index as an on-disk hash
- /// table.
- class IdentifierIndexWriterTrait {
- public:
- typedef StringRef key_type;
- typedef StringRef key_type_ref;
- typedef SmallVector<unsigned, 2> data_type;
- typedef const SmallVector<unsigned, 2> &data_type_ref;
- static unsigned ComputeHash(key_type_ref Key) {
- return llvm::HashString(Key);
- }
- std::pair<unsigned,unsigned>
- EmitKeyDataLength(raw_ostream& Out, key_type_ref Key, data_type_ref Data) {
- unsigned KeyLen = Key.size();
- unsigned DataLen = Data.size() * 4;
- clang::io::Emit16(Out, KeyLen);
- clang::io::Emit16(Out, DataLen);
- return std::make_pair(KeyLen, DataLen);
- }
-
- void EmitKey(raw_ostream& Out, key_type_ref Key, unsigned KeyLen) {
- Out.write(Key.data(), KeyLen);
- }
- void EmitData(raw_ostream& Out, key_type_ref Key, data_type_ref Data,
- unsigned DataLen) {
- for (unsigned I = 0, N = Data.size(); I != N; ++I)
- clang::io::Emit32(Out, Data[I]);
- }
- };
- }
- void GlobalModuleIndexBuilder::writeIndex(llvm::BitstreamWriter &Stream) {
- using namespace llvm;
-
- // Emit the file header.
- Stream.Emit((unsigned)'B', 8);
- Stream.Emit((unsigned)'C', 8);
- Stream.Emit((unsigned)'G', 8);
- Stream.Emit((unsigned)'I', 8);
- // Write the block-info block, which describes the records in this bitcode
- // file.
- emitBlockInfoBlock(Stream);
- Stream.EnterSubblock(GLOBAL_INDEX_BLOCK_ID, 3);
- // Write the metadata.
- SmallVector<uint64_t, 2> Record;
- Record.push_back(CurrentVersion);
- Stream.EmitRecord(METADATA, Record);
- // Write the set of known module files.
- for (ModuleFilesMap::iterator M = ModuleFiles.begin(),
- MEnd = ModuleFiles.end();
- M != MEnd; ++M) {
- Record.clear();
- Record.push_back(M->second.ID);
- Record.push_back(M->first->getSize());
- Record.push_back(M->first->getModificationTime());
- // File name
- StringRef Name(M->first->getName());
- Record.push_back(Name.size());
- Record.append(Name.begin(), Name.end());
- // Dependencies
- Record.push_back(M->second.Dependencies.size());
- Record.append(M->second.Dependencies.begin(), M->second.Dependencies.end());
- Stream.EmitRecord(MODULE, Record);
- }
- // Write the identifier -> module file mapping.
- {
- OnDiskChainedHashTableGenerator<IdentifierIndexWriterTrait> Generator;
- IdentifierIndexWriterTrait Trait;
- // Populate the hash table.
- for (InterestingIdentifierMap::iterator I = InterestingIdentifiers.begin(),
- IEnd = InterestingIdentifiers.end();
- I != IEnd; ++I) {
- Generator.insert(I->first(), I->second, Trait);
- }
-
- // Create the on-disk hash table in a buffer.
- SmallString<4096> IdentifierTable;
- uint32_t BucketOffset;
- {
- llvm::raw_svector_ostream Out(IdentifierTable);
- // Make sure that no bucket is at offset 0
- clang::io::Emit32(Out, 0);
- BucketOffset = Generator.Emit(Out, Trait);
- }
- // Create a blob abbreviation
- BitCodeAbbrev *Abbrev = new BitCodeAbbrev();
- Abbrev->Add(BitCodeAbbrevOp(IDENTIFIER_INDEX));
- Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32));
- Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));
- unsigned IDTableAbbrev = Stream.EmitAbbrev(Abbrev);
- // Write the identifier table
- Record.clear();
- Record.push_back(IDENTIFIER_INDEX);
- Record.push_back(BucketOffset);
- Stream.EmitRecordWithBlob(IDTableAbbrev, Record, IdentifierTable.str());
- }
- // FIXME: Selectors.
- Stream.ExitBlock();
- }
- GlobalModuleIndex::ErrorCode
- GlobalModuleIndex::writeIndex(FileManager &FileMgr, StringRef Path) {
- llvm::SmallString<128> IndexPath;
- IndexPath += Path;
- llvm::sys::path::append(IndexPath, IndexFileName);
- // Coordinate building the global index file with other processes that might
- // try to do the same.
- llvm::LockFileManager Locked(IndexPath);
- switch (Locked) {
- case llvm::LockFileManager::LFS_Error:
- return EC_IOError;
- case llvm::LockFileManager::LFS_Owned:
- // We're responsible for building the index ourselves. Do so below.
- break;
- case llvm::LockFileManager::LFS_Shared:
- // Someone else is responsible for building the index. We don't care
- // when they finish, so we're done.
- return EC_Building;
- }
- // The module index builder.
- GlobalModuleIndexBuilder Builder(FileMgr);
-
- // Load each of the module files.
- llvm::error_code EC;
- for (llvm::sys::fs::directory_iterator D(Path, EC), DEnd;
- D != DEnd && !EC;
- D.increment(EC)) {
- // If this isn't a module file, we don't care.
- if (llvm::sys::path::extension(D->path()) != ".pcm") {
- // ... unless it's a .pcm.lock file, which indicates that someone is
- // in the process of rebuilding a module. They'll rebuild the index
- // at the end of that translation unit, so we don't have to.
- if (llvm::sys::path::extension(D->path()) == ".pcm.lock")
- return EC_Building;
- continue;
- }
- // If we can't find the module file, skip it.
- const FileEntry *ModuleFile = FileMgr.getFile(D->path());
- if (!ModuleFile)
- continue;
- // Load this module file.
- if (Builder.loadModuleFile(ModuleFile))
- return EC_IOError;
- }
- // The output buffer, into which the global index will be written.
- SmallVector<char, 16> OutputBuffer;
- {
- llvm::BitstreamWriter OutputStream(OutputBuffer);
- Builder.writeIndex(OutputStream);
- }
- // Write the global index file to a temporary file.
- llvm::SmallString<128> IndexTmpPath;
- int TmpFD;
- if (llvm::sys::fs::unique_file(IndexPath + "-%%%%%%%%", TmpFD, IndexTmpPath))
- return EC_IOError;
- // Open the temporary global index file for output.
- std::string ErrorInfo;
- llvm::raw_fd_ostream Out(IndexTmpPath.c_str(), ErrorInfo,
- llvm::raw_fd_ostream::F_Binary);
- if (Out.has_error())
- return EC_IOError;
- // Write the index.
- Out.write(OutputBuffer.data(), OutputBuffer.size());
- Out.close();
- if (Out.has_error())
- return EC_IOError;
- // Remove the old index file. It isn't relevant any more.
- bool OldIndexExisted;
- llvm::sys::fs::remove(IndexPath.str(), OldIndexExisted);
- // Rename the newly-written index file to the proper name.
- if (llvm::sys::fs::rename(IndexTmpPath.str(), IndexPath.str())) {
- // Rename failed; just remove the
- llvm::sys::fs::remove(IndexTmpPath.str(), OldIndexExisted);
- return EC_IOError;
- }
- // We're done.
- return EC_None;
- }
|