diff --git a/include/clang/Basic/OnDiskHashTable.h b/include/clang/Basic/OnDiskHashTable.h index 2019e27ce5..aa3f344a6a 100644 --- a/include/clang/Basic/OnDiskHashTable.h +++ b/include/clang/Basic/OnDiskHashTable.h @@ -124,8 +124,9 @@ class OnDiskChainedHashTableGenerator { Item *next; const uint32_t hash; - Item(typename Info::key_type_ref k, typename Info::data_type_ref d) - : key(k), data(d), next(0), hash(Info::ComputeHash(k)) {} + Item(typename Info::key_type_ref k, typename Info::data_type_ref d, + Info &InfoObj) + : key(k), data(d), next(0), hash(InfoObj.ComputeHash(k)) {} }; class Bucket { @@ -168,10 +169,17 @@ public: void insert(typename Info::key_type_ref key, typename Info::data_type_ref data) { + Info InfoObj; + insert(key, data, InfoObj); + } + + void insert(typename Info::key_type_ref key, + typename Info::data_type_ref data, Info &InfoObj) { ++NumEntries; if (4*NumEntries >= 3*NumBuckets) resize(NumBuckets*2); - insert(Buckets, NumBuckets, new (BA.Allocate()) Item(key, data)); + insert(Buckets, NumBuckets, new (BA.Allocate()) Item(key, data, + InfoObj)); } io::Offset Emit(llvm::raw_ostream &out) { @@ -278,8 +286,8 @@ public: InfoPtr = &InfoObj; using namespace io; - const internal_key_type& iKey = Info::GetInternalKey(eKey); - unsigned key_hash = Info::ComputeHash(iKey); + const internal_key_type& iKey = InfoObj.GetInternalKey(eKey); + unsigned key_hash = InfoObj.ComputeHash(iKey); // Each bucket is just a 32-bit offset into the hash table file. unsigned idx = key_hash & (NumBuckets - 1); diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 3156a36040..4a3fc59e2e 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -20,6 +20,7 @@ #include "clang/AST/Type.h" #include "llvm/Bitcode/BitCodes.h" #include "llvm/System/DataTypes.h" +#include "llvm/ADT/DenseMap.h" namespace clang { namespace serialization { @@ -81,6 +82,36 @@ namespace clang { } }; + /// A structure for putting "fast"-unqualified QualTypes into a + /// DenseMap. This uses the standard pointer hash function. + struct UnsafeQualTypeDenseMapInfo { + static inline bool isEqual(QualType A, QualType B) { return A == B; } + static inline QualType getEmptyKey() { + return QualType::getFromOpaquePtr((void*) 1); + } + static inline QualType getTombstoneKey() { + return QualType::getFromOpaquePtr((void*) 2); + } + static inline unsigned getHashValue(QualType T) { + assert(!T.getLocalFastQualifiers() && + "hash invalid for types with fast quals"); + uintptr_t v = reinterpret_cast(T.getAsOpaquePtr()); + return (unsigned(v) >> 4) ^ (unsigned(v) >> 9); + } + }; + + /// \brief Map that provides the ID numbers of each type within the + /// output stream, plus those deserialized from a chained PCH. + /// + /// The ID numbers of types are consecutive (in order of discovery) + /// and start at 1. 0 is reserved for NULL. When types are actually + /// stored in the stream, the ID number is shifted by 2 bits to + /// allow for the const/volatile qualifiers. + /// + /// Keys in the map never have const/volatile qualifiers. + typedef llvm::DenseMap + TypeIdxMap; + /// \brief An ID number that refers to an identifier in an AST /// file. typedef uint32_t IdentID; diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index cbc3775cef..aff52195a9 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -27,7 +27,6 @@ #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/APSInt.h" -#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" @@ -315,6 +314,17 @@ private: /// ID = (I + 1) << FastQual::Width has already been loaded std::vector TypesLoaded; + /// \brief Map that provides the ID numbers of each type within the + /// output stream, plus those deserialized from a chained PCH. + /// + /// The ID numbers of types are consecutive (in order of discovery) + /// and start at 1. 0 is reserved for NULL. When types are actually + /// stored in the stream, the ID number is shifted by 2 bits to + /// allow for the const/volatile qualifiers. + /// + /// Keys in the map never have const/volatile qualifiers. + serialization::TypeIdxMap TypeIdxs; + /// \brief Declarations that have already been loaded from the chain. /// /// When the pointer at index I is non-NULL, the declaration with ID @@ -720,6 +730,16 @@ public: /// type. QualType GetType(serialization::TypeID ID); + /// \brief Returns the type ID associated with the given type. + /// If the type didn't come from the AST file the ID that is returned is + /// marked as "doesn't exist in AST". + serialization::TypeID GetTypeID(QualType T) const; + + /// \brief Returns the type index associated with the given type. + /// If the type didn't come from the AST file the index that is returned is + /// marked as "doesn't exist in AST". + serialization::TypeIdx GetTypeIdx(QualType T) const; + /// \brief Resolve a declaration ID into a declaration, potentially /// building a new declaration. Decl *GetDecl(serialization::DeclID ID); diff --git a/include/clang/Serialization/ASTWriter.h b/include/clang/Serialization/ASTWriter.h index 15977efe76..9ef14925bc 100644 --- a/include/clang/Serialization/ASTWriter.h +++ b/include/clang/Serialization/ASTWriter.h @@ -20,7 +20,6 @@ #include "clang/Serialization/ASTBitCodes.h" #include "clang/Serialization/ASTDeserializationListener.h" #include "clang/Sema/SemaConsumer.h" -#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Bitcode/BitstreamWriter.h" @@ -50,24 +49,6 @@ class SourceManager; class SwitchCase; class TargetInfo; -/// A structure for putting "fast"-unqualified QualTypes into a -/// DenseMap. This uses the standard pointer hash function. -struct UnsafeQualTypeDenseMapInfo { - static inline bool isEqual(QualType A, QualType B) { return A == B; } - static inline QualType getEmptyKey() { - return QualType::getFromOpaquePtr((void*) 1); - } - static inline QualType getTombstoneKey() { - return QualType::getFromOpaquePtr((void*) 2); - } - static inline unsigned getHashValue(QualType T) { - assert(!T.getLocalFastQualifiers() && - "hash invalid for types with fast quals"); - uintptr_t v = reinterpret_cast(T.getAsOpaquePtr()); - return (unsigned(v) >> 4) ^ (unsigned(v) >> 9); - } -}; - /// \brief Writes an AST file containing the contents of a translation unit. /// /// The ASTWriter class produces a bitstream containing the serialized @@ -146,8 +127,7 @@ private: /// allow for the const/volatile qualifiers. /// /// Keys in the map never have const/volatile qualifiers. - llvm::DenseMap - TypeIdxs; + serialization::TypeIdxMap TypeIdxs; /// \brief Offset of each type in the bitstream, indexed by /// the type's ID. @@ -368,13 +348,13 @@ public: serialization::TypeID GetOrCreateTypeID(QualType T); /// \brief Determine the type ID of an already-emitted type. - serialization::TypeID getTypeID(QualType T); + serialization::TypeID getTypeID(QualType T) const; /// \brief Force a type to be emitted and get its index. serialization::TypeIdx GetOrCreateTypeIdx(QualType T); /// \brief Determine the type index of an already-emitted type. - serialization::TypeIdx getTypeIdx(QualType T); + serialization::TypeIdx getTypeIdx(QualType T) const; /// \brief Emits a reference to a declarator info. void AddTypeSourceInfo(TypeSourceInfo *TInfo, RecordData &Record); diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 040277074c..05fa7cd115 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -704,6 +704,148 @@ public: typedef OnDiskChainedHashTable ASTIdentifierLookupTable; +namespace { +class ASTDeclContextNameLookupTrait { + ASTReader &Reader; + +public: + /// \brief Pair of begin/end iterators for DeclIDs. + typedef std::pair data_type; + + /// \brief Special internal key for declaration names. + /// The hash table creates keys for comparison; we do not create + /// a DeclarationName for the internal key to avoid deserializing types. + struct DeclNameKey { + DeclarationName::NameKind Kind; + uint64_t Data; + DeclNameKey() : Kind((DeclarationName::NameKind)0), Data(0) { } + }; + + typedef DeclarationName external_key_type; + typedef DeclNameKey internal_key_type; + + explicit ASTDeclContextNameLookupTrait(ASTReader &Reader) : Reader(Reader) { } + + static bool EqualKey(const internal_key_type& a, + const internal_key_type& b) { + return a.Kind == b.Kind && a.Data == b.Data; + } + + unsigned ComputeHash(const DeclNameKey &Key) const { + llvm::FoldingSetNodeID ID; + ID.AddInteger(Key.Kind); + + switch (Key.Kind) { + case DeclarationName::Identifier: + case DeclarationName::CXXLiteralOperatorName: + ID.AddString(((IdentifierInfo*)Key.Data)->getName()); + break; + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + ID.AddInteger(serialization::ComputeHash(Selector(Key.Data))); + break; + case DeclarationName::CXXConstructorName: + case DeclarationName::CXXDestructorName: + case DeclarationName::CXXConversionFunctionName: + ID.AddInteger((TypeID)Key.Data); + break; + case DeclarationName::CXXOperatorName: + ID.AddInteger((OverloadedOperatorKind)Key.Data); + break; + case DeclarationName::CXXUsingDirective: + break; + } + + return ID.ComputeHash(); + } + + internal_key_type GetInternalKey(const external_key_type& Name) const { + DeclNameKey Key; + Key.Kind = Name.getNameKind(); + switch (Name.getNameKind()) { + case DeclarationName::Identifier: + Key.Data = (uint64_t)Name.getAsIdentifierInfo(); + break; + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + Key.Data = (uint64_t)Name.getObjCSelector().getAsOpaquePtr(); + break; + case DeclarationName::CXXConstructorName: + case DeclarationName::CXXDestructorName: + case DeclarationName::CXXConversionFunctionName: + Key.Data = Reader.GetTypeID(Name.getCXXNameType()); + break; + case DeclarationName::CXXOperatorName: + Key.Data = Name.getCXXOverloadedOperator(); + break; + case DeclarationName::CXXLiteralOperatorName: + Key.Data = (uint64_t)Name.getCXXLiteralIdentifier(); + break; + case DeclarationName::CXXUsingDirective: + break; + } + + return Key; + } + + static std::pair + ReadKeyDataLength(const unsigned char*& d) { + using namespace clang::io; + unsigned KeyLen = ReadUnalignedLE16(d); + unsigned DataLen = ReadUnalignedLE16(d); + return std::make_pair(KeyLen, DataLen); + } + + internal_key_type ReadKey(const unsigned char* d, unsigned) { + using namespace clang::io; + + DeclNameKey Key; + Key.Kind = (DeclarationName::NameKind)*d++; + switch (Key.Kind) { + case DeclarationName::Identifier: + Key.Data = (uint64_t)Reader.DecodeIdentifierInfo(ReadUnalignedLE32(d)); + break; + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + Key.Data = + (uint64_t)Reader.DecodeSelector(ReadUnalignedLE32(d)).getAsOpaquePtr(); + break; + case DeclarationName::CXXConstructorName: + case DeclarationName::CXXDestructorName: + case DeclarationName::CXXConversionFunctionName: + Key.Data = ReadUnalignedLE32(d); // TypeID + break; + case DeclarationName::CXXOperatorName: + Key.Data = *d++; // OverloadedOperatorKind + break; + case DeclarationName::CXXLiteralOperatorName: + Key.Data = (uint64_t)Reader.DecodeIdentifierInfo(ReadUnalignedLE32(d)); + break; + case DeclarationName::CXXUsingDirective: + break; + } + + return Key; + } + + data_type ReadData(internal_key_type, const unsigned char* d, + unsigned DataLen) { + using namespace clang::io; + unsigned NumDecls = ReadUnalignedLE16(d); + DeclID *Start = (DeclID *)d; + return std::make_pair(Start, Start + NumDecls); + } +}; + +} // end anonymous namespace + +/// \brief The on-disk hash table used for the DeclContext's Name lookup table. +typedef OnDiskChainedHashTable + ASTDeclContextNameLookupTable; + void ASTReader::Error(const char *Msg) { Diag(diag::err_fe_pch_malformed) << Msg; } @@ -2825,6 +2967,7 @@ QualType ASTReader::GetType(TypeID ID) { if (TypesLoaded[Index].isNull()) { TypesLoaded[Index] = ReadTypeRecord(Index); TypesLoaded[Index]->setFromAST(); + TypeIdxs[TypesLoaded[Index]] = TypeIdx::fromTypeID(ID); if (DeserializationListener) DeserializationListener->TypeRead(TypeIdx::fromTypeID(ID), TypesLoaded[Index]); @@ -2833,6 +2976,27 @@ QualType ASTReader::GetType(TypeID ID) { return TypesLoaded[Index].withFastQualifiers(FastQuals); } +TypeID ASTReader::GetTypeID(QualType T) const { + return MakeTypeID(T, + std::bind1st(std::mem_fun(&ASTReader::GetTypeIdx), this)); +} + +TypeIdx ASTReader::GetTypeIdx(QualType T) const { + if (T.isNull()) + return TypeIdx(); + assert(!T.getLocalFastQualifiers()); + + TypeIdxMap::const_iterator I = TypeIdxs.find(T); + // GetTypeIdx is mostly used for computing the hash of DeclarationNames and + // comparing keys of ASTDeclContextNameLookupTable. + // If the type didn't come from the AST file use a specially marked index + // so that any hash/key comparison fail since no such index is stored + // in a AST file. + if (I == TypeIdxs.end()) + return TypeIdx(-1); + return I->second; +} + TemplateArgumentLocInfo ASTReader::GetTemplateArgumentLocInfo(TemplateArgument::ArgKind Kind, llvm::BitstreamCursor &DeclsCursor, diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 716abde892..94be24ad2c 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -1653,6 +1653,7 @@ void ASTWriter::WriteSelectors(Sema &SemaRef) { // Create and write out the blob that contains selectors and the method pool. { OnDiskChainedHashTableGenerator Generator; + ASTMethodPoolTrait Trait(*this); // Create the on-disk hash table representation. We walk through every // selector we've seen and look it up in the method pool. @@ -1692,7 +1693,7 @@ void ASTWriter::WriteSelectors(Sema &SemaRef) { // A new method pool entry. ++NumTableEntries; } - Generator.insert(S, Data); + Generator.insert(S, Data, Trait); } // Create the on-disk hash table in a buffer. @@ -1877,6 +1878,7 @@ void ASTWriter::WriteIdentifierTable(Preprocessor &PP) { // strings. { OnDiskChainedHashTableGenerator Generator; + ASTIdentifierTableTrait Trait(*this, PP); // Look for any identifiers that were named while processing the // headers, but are otherwise not needed. We add these to the hash @@ -1896,7 +1898,7 @@ void ASTWriter::WriteIdentifierTable(Preprocessor &PP) { ID != IDEnd; ++ID) { assert(ID->first && "NULL identifier in identifier table"); if (!Chain || !ID->first->isFromAST()) - Generator.insert(ID->first, ID->second); + Generator.insert(ID->first, ID->second, Trait); } // Create the on-disk hash table in a buffer. @@ -1939,6 +1941,127 @@ void ASTWriter::WriteIdentifierTable(Preprocessor &PP) { IdentifierOffsets.size() * sizeof(uint32_t)); } +//===----------------------------------------------------------------------===// +// DeclContext's Name Lookup Table Serialization +//===----------------------------------------------------------------------===// + +namespace { +// Trait used for the on-disk hash table used in the method pool. +class ASTDeclContextNameLookupTrait { + ASTWriter &Writer; + +public: + typedef DeclarationName key_type; + typedef key_type key_type_ref; + + typedef DeclContext::lookup_result data_type; + typedef const data_type& data_type_ref; + + explicit ASTDeclContextNameLookupTrait(ASTWriter &Writer) : Writer(Writer) { } + + unsigned ComputeHash(DeclarationName Name) { + llvm::FoldingSetNodeID ID; + ID.AddInteger(Name.getNameKind()); + + switch (Name.getNameKind()) { + case DeclarationName::Identifier: + ID.AddString(Name.getAsIdentifierInfo()->getName()); + break; + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + ID.AddInteger(serialization::ComputeHash(Name.getObjCSelector())); + break; + case DeclarationName::CXXConstructorName: + case DeclarationName::CXXDestructorName: + case DeclarationName::CXXConversionFunctionName: + ID.AddInteger(Writer.GetOrCreateTypeID(Name.getCXXNameType())); + break; + case DeclarationName::CXXOperatorName: + ID.AddInteger(Name.getCXXOverloadedOperator()); + break; + case DeclarationName::CXXLiteralOperatorName: + ID.AddString(Name.getCXXLiteralIdentifier()->getName()); + case DeclarationName::CXXUsingDirective: + break; + } + + return ID.ComputeHash(); + } + + std::pair + EmitKeyDataLength(llvm::raw_ostream& Out, DeclarationName Name, + data_type_ref Lookup) { + unsigned KeyLen = 1; + switch (Name.getNameKind()) { + case DeclarationName::Identifier: + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + case DeclarationName::CXXConstructorName: + case DeclarationName::CXXDestructorName: + case DeclarationName::CXXConversionFunctionName: + case DeclarationName::CXXLiteralOperatorName: + KeyLen += 4; + break; + case DeclarationName::CXXOperatorName: + KeyLen += 1; + break; + case DeclarationName::CXXUsingDirective: + break; + } + clang::io::Emit16(Out, KeyLen); + + // 2 bytes for num of decls and 4 for each DeclID. + unsigned DataLen = 2 + 4 * (Lookup.second - Lookup.first); + clang::io::Emit16(Out, DataLen); + + return std::make_pair(KeyLen, DataLen); + } + + void EmitKey(llvm::raw_ostream& Out, DeclarationName Name, unsigned) { + using namespace clang::io; + + assert(Name.getNameKind() < 0x100 && "Invalid name kind ?"); + Emit8(Out, Name.getNameKind()); + switch (Name.getNameKind()) { + case DeclarationName::Identifier: + Emit32(Out, Writer.getIdentifierRef(Name.getAsIdentifierInfo())); + break; + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + Emit32(Out, Writer.getSelectorRef(Name.getObjCSelector())); + break; + case DeclarationName::CXXConstructorName: + case DeclarationName::CXXDestructorName: + case DeclarationName::CXXConversionFunctionName: + Emit32(Out, Writer.getTypeID(Name.getCXXNameType())); + break; + case DeclarationName::CXXOperatorName: + assert(Name.getCXXOverloadedOperator() < 0x100 && "Invalid operator ?"); + Emit8(Out, Name.getCXXOverloadedOperator()); + break; + case DeclarationName::CXXLiteralOperatorName: + Emit32(Out, Writer.getIdentifierRef(Name.getCXXLiteralIdentifier())); + break; + case DeclarationName::CXXUsingDirective: + break; + } + } + + void EmitData(llvm::raw_ostream& Out, key_type_ref, + data_type Lookup, unsigned DataLen) { + uint64_t Start = Out.tell(); (void)Start; + clang::io::Emit16(Out, Lookup.second - Lookup.first); + for (; Lookup.first != Lookup.second; ++Lookup.first) + clang::io::Emit32(Out, Writer.GetDeclRef(*Lookup.first)); + + assert(Out.tell() - Start == DataLen && "Data length is wrong"); + } +}; +} // end anonymous namespace + //===----------------------------------------------------------------------===// // General Serialization Routines //===----------------------------------------------------------------------===// @@ -2587,7 +2710,7 @@ TypeID ASTWriter::GetOrCreateTypeID(QualType T) { std::bind1st(std::mem_fun(&ASTWriter::GetOrCreateTypeIdx), this)); } -TypeID ASTWriter::getTypeID(QualType T) { +TypeID ASTWriter::getTypeID(QualType T) const { return MakeTypeID(T, std::bind1st(std::mem_fun(&ASTWriter::getTypeIdx), this)); } @@ -2607,13 +2730,14 @@ TypeIdx ASTWriter::GetOrCreateTypeIdx(QualType T) { return Idx; } -TypeIdx ASTWriter::getTypeIdx(QualType T) { +TypeIdx ASTWriter::getTypeIdx(QualType T) const { if (T.isNull()) return TypeIdx(); assert(!T.getLocalFastQualifiers()); - assert(TypeIdxs.find(T) != TypeIdxs.end() && "Type not emitted!"); - return TypeIdxs[T]; + TypeIdxMap::const_iterator I = TypeIdxs.find(T); + assert(I != TypeIdxs.end() && "Type not emitted!"); + return I->second; } void ASTWriter::AddDeclRef(const Decl *D, RecordData &Record) {