diff --git a/lib/Index/CMakeLists.txt b/lib/Index/CMakeLists.txt index ff30c74e803..24aef385b62 100644 --- a/lib/Index/CMakeLists.txt +++ b/lib/Index/CMakeLists.txt @@ -14,7 +14,6 @@ add_clang_library(clangIndex IndexDecl.cpp IndexingAction.cpp IndexingContext.cpp - IndexRecordHasher.cpp IndexRecordReader.cpp IndexRecordWriter.cpp IndexSymbol.cpp @@ -36,4 +35,7 @@ add_clang_library(clangIndex clangRewrite clangSerialization clangToolingCore + clangIndexRecordHasher ) + +add_subdirectory(RecordHasher) diff --git a/lib/Index/ClangIndexRecordWriter.cpp b/lib/Index/ClangIndexRecordWriter.cpp index 41d45a0360d..c8177a9e212 100644 --- a/lib/Index/ClangIndexRecordWriter.cpp +++ b/lib/Index/ClangIndexRecordWriter.cpp @@ -41,8 +41,7 @@ StringRef ClangIndexRecordWriter::getUSRNonCached(const Decl *D) { ClangIndexRecordWriter::ClangIndexRecordWriter(ASTContext &Ctx, RecordingOptions Opts) - : Impl(Opts.DataDirPath), Ctx(Ctx), RecordOpts(std::move(Opts)), - Hasher(Ctx) { + : Impl(Opts.DataDirPath), Ctx(Ctx), RecordOpts(std::move(Opts)) { if (Opts.RecordSymbolCodeGenName) CGNameGen.reset(new CodegenNameGenerator(Ctx)); } @@ -54,7 +53,8 @@ bool ClangIndexRecordWriter::writeRecord(StringRef Filename, std::string &Error, std::string *OutRecordFile) { - auto RecordHash = Hasher.hashRecord(IdxRecord); + ASTContext &Ctx = getASTContext(); + auto RecordHash = hashRecord(Ctx, IdxRecord); switch (Impl.beginRecord(Filename, RecordHash, Error, OutRecordFile)) { case IndexRecordWriter::Result::Success: @@ -65,7 +65,6 @@ bool ClangIndexRecordWriter::writeRecord(StringRef Filename, return false; } - ASTContext &Ctx = getASTContext(); SourceManager &SM = Ctx.getSourceManager(); FileID FID = IdxRecord.getFileID(); auto getLineCol = [&](unsigned Offset) -> std::pair { diff --git a/lib/Index/ClangIndexRecordWriter.h b/lib/Index/ClangIndexRecordWriter.h index b68f9875fb3..55908bbe2d7 100644 --- a/lib/Index/ClangIndexRecordWriter.h +++ b/lib/Index/ClangIndexRecordWriter.h @@ -32,7 +32,6 @@ class ClangIndexRecordWriter { std::unique_ptr CGNameGen; llvm::BumpPtrAllocator Allocator; llvm::DenseMap USRByDecl; - IndexRecordHasher Hasher; public: ClangIndexRecordWriter(ASTContext &Ctx, RecordingOptions Opts); diff --git a/lib/Index/IndexRecordHasher.cpp b/lib/Index/IndexRecordHasher.cpp deleted file mode 100644 index ee22ec8bcf3..00000000000 --- a/lib/Index/IndexRecordHasher.cpp +++ /dev/null @@ -1,481 +0,0 @@ -//===--- IndexRecordHasher.cpp - Index record hashing ---------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "IndexRecordHasher.h" -#include "FileIndexRecord.h" -#include "clang/AST/ASTContext.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DeclVisitor.h" -#include "llvm/Support/Path.h" - -#define INITIAL_HASH 5381 -#define COMBINE_HASH(...) (Hash = hash_combine(Hash, __VA_ARGS__)) - -using namespace clang; -using namespace clang::index; -using namespace llvm; - -static hash_code computeHash(const TemplateArgument &Arg, - IndexRecordHasher &Hasher); - -namespace { -class DeclHashVisitor : public ConstDeclVisitor { - IndexRecordHasher &Hasher; - -public: - DeclHashVisitor(IndexRecordHasher &Hasher) : Hasher(Hasher) {} - - hash_code VisitDecl(const Decl *D) { - return VisitDeclContext(D->getDeclContext()); - } - - hash_code VisitNamedDecl(const NamedDecl *D) { - hash_code Hash = VisitDecl(D); - if (auto *attr = D->getExternalSourceSymbolAttr()) { - COMBINE_HASH(hash_value(attr->getDefinedIn())); - } - return COMBINE_HASH(Hasher.hash(D->getDeclName())); - } - - hash_code VisitTagDecl(const TagDecl *D) { - if (D->getDeclName().isEmpty()) { - if (const TypedefNameDecl *TD = D->getTypedefNameForAnonDecl()) - return Visit(TD); - - hash_code Hash = VisitDeclContext(D->getDeclContext()); - if (D->isEmbeddedInDeclarator() && !D->isFreeStanding()) { - COMBINE_HASH(hashLoc(D->getLocation(), /*IncludeOffset=*/true)); - } else - COMBINE_HASH('a'); - return Hash; - } - - hash_code Hash = VisitTypeDecl(D); - return COMBINE_HASH('T'); - } - - hash_code VisitClassTemplateSpecializationDecl(const ClassTemplateSpecializationDecl *D) { - hash_code Hash = VisitCXXRecordDecl(D); - const TemplateArgumentList &Args = D->getTemplateArgs(); - COMBINE_HASH('>'); - for (unsigned I = 0, N = Args.size(); I != N; ++I) { - COMBINE_HASH(computeHash(Args.get(I), Hasher)); - } - return Hash; - } - - hash_code VisitObjCContainerDecl(const ObjCContainerDecl *D) { - hash_code Hash = VisitNamedDecl(D); - return COMBINE_HASH('I'); - } - - hash_code VisitObjCImplDecl(const ObjCImplDecl *D) { - if (auto *ID = D->getClassInterface()) - return VisitObjCInterfaceDecl(ID); - else - return 0; - } - - hash_code VisitObjCCategoryDecl(const ObjCCategoryDecl *D) { - // FIXME: Differentiate between category and the interface ? - if (auto *ID = D->getClassInterface()) - return VisitObjCInterfaceDecl(ID); - else - return 0; - } - - hash_code VisitFunctionDecl(const FunctionDecl *D) { - hash_code Hash = VisitNamedDecl(D); - ASTContext &Ctx = Hasher.getASTContext(); - if ((!Ctx.getLangOpts().CPlusPlus && !D->hasAttr()) - || D->isExternC()) - return Hash; - - for (auto param : D->parameters()) { - COMBINE_HASH(Hasher.hash(param->getType())); - } - return Hash; - } - - hash_code VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D) { - hash_code Hash = VisitNamedDecl(D); - COMBINE_HASH(Hasher.hash(D->getQualifier())); - return Hash; - } - - hash_code VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D) { - hash_code Hash = VisitNamedDecl(D); - COMBINE_HASH(Hasher.hash(D->getQualifier())); - return Hash; - } - - hash_code VisitDeclContext(const DeclContext *DC) { - // FIXME: Add location if this is anonymous namespace ? - DC = DC->getRedeclContext(); - const Decl *D = cast(DC)->getCanonicalDecl(); - if (auto *ND = dyn_cast(D)) - return Hasher.hash(ND); - else - return 0; - } - - hash_code hashLoc(SourceLocation Loc, bool IncludeOffset) { - if (Loc.isInvalid()) { - return 0; - } - hash_code Hash = INITIAL_HASH; - const SourceManager &SM = Hasher.getASTContext().getSourceManager(); - Loc = SM.getFileLoc(Loc); - const std::pair &Decomposed = SM.getDecomposedLoc(Loc); - const FileEntry *FE = SM.getFileEntryForID(Decomposed.first); - if (FE) { - COMBINE_HASH(llvm::sys::path::filename(FE->getName())); - } else { - // This case really isn't interesting. - return 0; - } - if (IncludeOffset) { - // Use the offest into the FileID to represent the location. Using - // a line/column can cause us to look back at the original source file, - // which is expensive. - COMBINE_HASH(Decomposed.second); - } - return Hash; - } -}; -} - -hash_code IndexRecordHasher::hashRecord(const FileIndexRecord &Record) { - hash_code Hash = INITIAL_HASH; - for (auto &Info : Record.getDeclOccurrencesSortedByOffset()) { - COMBINE_HASH(Info.Roles, Info.Offset, hash(Info.Dcl)); - for (auto &Rel : Info.Relations) { - COMBINE_HASH(hash(Rel.RelatedSymbol)); - } - } - return Hash; -} - -hash_code IndexRecordHasher::hash(const Decl *D) { - assert(D->isCanonicalDecl()); - - if (isa(D) || isa(D)) { - return tryCache(D, D); - } else if (auto *NS = dyn_cast(D)) { - if (NS->isAnonymousNamespace()) - return hash_value(StringRef("@aN")); - return tryCache(D, D); - } else { - // There's a balance between caching results and not growing the cache too - // much. Measurements showed that avoiding caching all decls is beneficial - // particularly when including all of Cocoa. - return hashImpl(D); - } -} - -hash_code IndexRecordHasher::hash(QualType NonCanTy) { - CanQualType CanTy = Ctx.getCanonicalType(NonCanTy); - return hash(CanTy); -} - -hash_code IndexRecordHasher::hash(CanQualType CT) { - // Do some hashing without going to the cache, for example we can avoid - // storing the hash for both the type and its const-qualified version. - hash_code Hash = INITIAL_HASH; - - auto asCanon = [](QualType Ty) -> CanQualType { - return CanQualType::CreateUnsafe(Ty); - }; - - while (true) { - Qualifiers Q = CT.getQualifiers(); - CT = CT.getUnqualifiedType(); - const Type *T = CT.getTypePtr(); - unsigned qVal = 0; - if (Q.hasConst()) - qVal |= 0x1; - if (Q.hasVolatile()) - qVal |= 0x2; - if (Q.hasRestrict()) - qVal |= 0x4; - if(qVal) - COMBINE_HASH(qVal); - - // Hash in ObjC GC qualifiers? - - if (const BuiltinType *BT = dyn_cast(T)) { - return COMBINE_HASH(BT->getKind()); - } - if (const PointerType *PT = dyn_cast(T)) { - COMBINE_HASH('*'); - CT = asCanon(PT->getPointeeType()); - continue; - } - if (const ReferenceType *RT = dyn_cast(T)) { - COMBINE_HASH('&'); - CT = asCanon(RT->getPointeeType()); - continue; - } - if (const BlockPointerType *BT = dyn_cast(T)) { - COMBINE_HASH('B'); - CT = asCanon(BT->getPointeeType()); - continue; - } - if (const ObjCObjectPointerType *OPT = dyn_cast(T)) { - COMBINE_HASH('*'); - CT = asCanon(OPT->getPointeeType()); - continue; - } - if (const TagType *TT = dyn_cast(T)) { - return COMBINE_HASH('$', hash(TT->getDecl()->getCanonicalDecl())); - } - if (const ObjCInterfaceType *OIT = dyn_cast(T)) { - return COMBINE_HASH('$', hash(OIT->getDecl()->getCanonicalDecl())); - } - if (const ObjCObjectType *OIT = dyn_cast(T)) { - for (auto *Prot : OIT->getProtocols()) - COMBINE_HASH(hash(Prot)); - CT = asCanon(OIT->getBaseType()); - continue; - } - if (const TemplateTypeParmType *TTP = dyn_cast(T)) { - return COMBINE_HASH('t', TTP->getDepth(), TTP->getIndex()); - } - if (const InjectedClassNameType *InjT = dyn_cast(T)) { - CT = asCanon(InjT->getInjectedSpecializationType().getCanonicalType()); - continue; - } - - break; - } - - return COMBINE_HASH(tryCache(CT.getAsOpaquePtr(), CT)); -} - -hash_code IndexRecordHasher::hash(DeclarationName Name) { - assert(!Name.isEmpty()); - // Measurements for using cache or not here, showed significant slowdown when - // using the cache for all DeclarationNames when parsing Cocoa, and minor - // improvement or no difference for a couple of C++ single translation unit - // files. So we avoid caching DeclarationNames. - return hashImpl(Name); -} - -hash_code IndexRecordHasher::hash(const NestedNameSpecifier *NNS) { - assert(NNS); - // Measurements for the C++ single translation unit files did not show much - // difference here; choosing to cache them currently. - return tryCache(NNS, NNS); -} - -template -hash_code IndexRecordHasher::tryCache(const void *Ptr, T Obj) { - auto It = HashByPtr.find(Ptr); - if (It != HashByPtr.end()) - return It->second; - - hash_code Hash = hashImpl(Obj); - // hashImpl() may call into tryCache recursively and mutate - // HashByPtr, so we use find() earlier and insert the hash with another - // lookup here instead of calling insert() earlier and utilizing the iterator - // that insert() returns. - HashByPtr[Ptr] = Hash; - return Hash; -} - -hash_code IndexRecordHasher::hashImpl(const Decl *D) { - return DeclHashVisitor(*this).Visit(D); -} - -static hash_code computeHash(const IdentifierInfo *II) { - return hash_value(II->getName()); -} - -static hash_code computeHash(Selector Sel) { - unsigned N = Sel.getNumArgs(); - if (N == 0) - ++N; - hash_code Hash = INITIAL_HASH; - for (unsigned I = 0; I != N; ++I) - if (IdentifierInfo *II = Sel.getIdentifierInfoForSlot(I)) - COMBINE_HASH(computeHash(II)); - return Hash; -} - -static hash_code computeHash(TemplateName Name, IndexRecordHasher &Hasher) { - hash_code Hash = INITIAL_HASH; - if (TemplateDecl *Template = Name.getAsTemplateDecl()) { - if (TemplateTemplateParmDecl *TTP - = dyn_cast(Template)) { - return COMBINE_HASH('t', TTP->getDepth(), TTP->getIndex()); - } - - return COMBINE_HASH(Hasher.hash(Template->getCanonicalDecl())); - } - - // FIXME: Hash dependent template names. - return Hash; -} - -static hash_code computeHash(const TemplateArgument &Arg, - IndexRecordHasher &Hasher) { - hash_code Hash = INITIAL_HASH; - - switch (Arg.getKind()) { - case TemplateArgument::Null: - break; - - case TemplateArgument::Declaration: - COMBINE_HASH(Hasher.hash(Arg.getAsDecl())); - break; - - case TemplateArgument::NullPtr: - break; - - case TemplateArgument::TemplateExpansion: - COMBINE_HASH('P'); // pack expansion of... - LLVM_FALLTHROUGH; - case TemplateArgument::Template: - COMBINE_HASH(computeHash(Arg.getAsTemplateOrTemplatePattern(), Hasher)); - break; - - case TemplateArgument::Expression: - // FIXME: Hash expressions. - break; - - case TemplateArgument::Pack: - COMBINE_HASH('p'); - for (const auto &P : Arg.pack_elements()) - COMBINE_HASH(computeHash(P, Hasher)); - break; - - case TemplateArgument::Type: - COMBINE_HASH(Hasher.hash(Arg.getAsType())); - break; - - case TemplateArgument::Integral: - COMBINE_HASH('V', Hasher.hash(Arg.getIntegralType()), Arg.getAsIntegral()); - break; - } - - return Hash; -} - -hash_code IndexRecordHasher::hashImpl(CanQualType CQT) { - hash_code Hash = INITIAL_HASH; - - auto asCanon = [](QualType Ty) -> CanQualType { - return CanQualType::CreateUnsafe(Ty); - }; - - const Type *T = CQT.getTypePtr(); - - if (const PackExpansionType *Expansion = dyn_cast(T)) { - return COMBINE_HASH('P', hash(asCanon(Expansion->getPattern()))); - } - if (const RValueReferenceType *RT = dyn_cast(T)) { - return COMBINE_HASH('%', hash(asCanon(RT->getPointeeType()))); - } - if (const FunctionProtoType *FT = dyn_cast(T)) { - COMBINE_HASH('F', hash(asCanon(FT->getReturnType()))); - for (const auto &I : FT->param_types()) - COMBINE_HASH(hash(asCanon(I))); - return COMBINE_HASH(FT->isVariadic()); - } - if (const ComplexType *CT = dyn_cast(T)) { - return COMBINE_HASH('<', hash(asCanon(CT->getElementType()))); - } - if (const TemplateSpecializationType *Spec - = dyn_cast(T)) { - COMBINE_HASH('>', computeHash(Spec->getTemplateName(), *this)); - for (unsigned I = 0, N = Spec->getNumArgs(); I != N; ++I) - COMBINE_HASH(computeHash(Spec->getArg(I), *this)); - return Hash; - } - if (const DependentNameType *DNT = dyn_cast(T)) { - COMBINE_HASH('^'); - if (const NestedNameSpecifier *NNS = DNT->getQualifier()) - COMBINE_HASH(hash(NNS)); - return COMBINE_HASH(computeHash(DNT->getIdentifier())); - } - - // Unhandled type. - return Hash; -} - -hash_code IndexRecordHasher::hashImpl(DeclarationName Name) { - hash_code Hash = INITIAL_HASH; - COMBINE_HASH(Name.getNameKind()); - - switch (Name.getNameKind()) { - case DeclarationName::Identifier: - COMBINE_HASH(computeHash(Name.getAsIdentifierInfo())); - break; - case DeclarationName::ObjCZeroArgSelector: - case DeclarationName::ObjCOneArgSelector: - case DeclarationName::ObjCMultiArgSelector: - COMBINE_HASH(computeHash(Name.getObjCSelector())); - break; - case DeclarationName::CXXConstructorName: - case DeclarationName::CXXDestructorName: - case DeclarationName::CXXConversionFunctionName: - break; - case DeclarationName::CXXOperatorName: - COMBINE_HASH(Name.getCXXOverloadedOperator()); - break; - case DeclarationName::CXXLiteralOperatorName: - COMBINE_HASH(computeHash(Name.getCXXLiteralIdentifier())); - break; - case DeclarationName::CXXUsingDirective: - break; - case DeclarationName::CXXDeductionGuideName: - COMBINE_HASH(computeHash(Name.getCXXDeductionGuideTemplate() - ->getDeclName().getAsIdentifierInfo())); - break; - } - - return Hash; -} - -hash_code IndexRecordHasher::hashImpl(const NestedNameSpecifier *NNS) { - hash_code Hash = INITIAL_HASH; - if (auto *Pre = NNS->getPrefix()) - COMBINE_HASH(hash(Pre)); - - COMBINE_HASH(NNS->getKind()); - - switch (NNS->getKind()) { - case NestedNameSpecifier::Identifier: - COMBINE_HASH(computeHash(NNS->getAsIdentifier())); - break; - - case NestedNameSpecifier::Namespace: - COMBINE_HASH(hash(NNS->getAsNamespace()->getCanonicalDecl())); - break; - - case NestedNameSpecifier::NamespaceAlias: - COMBINE_HASH(hash(NNS->getAsNamespaceAlias()->getCanonicalDecl())); - break; - - case NestedNameSpecifier::Global: - break; - - case NestedNameSpecifier::Super: - break; - - case NestedNameSpecifier::TypeSpecWithTemplate: - // Fall through to hash the type. - - case NestedNameSpecifier::TypeSpec: - COMBINE_HASH(hash(QualType(NNS->getAsType(), 0))); - break; - } - - return Hash; -} diff --git a/lib/Index/IndexRecordHasher.h b/lib/Index/IndexRecordHasher.h index af3acccff52..52937761cf8 100644 --- a/lib/Index/IndexRecordHasher.h +++ b/lib/Index/IndexRecordHasher.h @@ -1,58 +1,25 @@ -//===--- IndexRecordHasher.h - Index record hashing -----------------------===// +//===--- IndexRecordHasher.h - Hashing of FileIndexRecord -------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_LIB_INDEX_INDEXRECORDHASHER_H #define LLVM_CLANG_LIB_INDEX_INDEXRECORDHASHER_H -#include "clang/Basic/LLVM.h" -#include "llvm/ADT/DenseMap.h" +#include "clang/AST/ASTContext.h" #include "llvm/ADT/Hashing.h" namespace clang { - class ASTContext; - class Decl; - class DeclarationName; - class NestedNameSpecifier; - class QualType; - class Type; - template class CanQual; - typedef CanQual CanQualType; - namespace index { class FileIndexRecord; -class IndexRecordHasher { - ASTContext &Ctx; - llvm::DenseMap HashByPtr; - -public: - explicit IndexRecordHasher(ASTContext &Ctx) : Ctx(Ctx) {} - ASTContext &getASTContext() { return Ctx; } - - llvm::hash_code hashRecord(const FileIndexRecord &Record); - llvm::hash_code hash(const Decl *D); - llvm::hash_code hash(QualType Ty); - llvm::hash_code hash(CanQualType Ty); - llvm::hash_code hash(DeclarationName Name); - llvm::hash_code hash(const NestedNameSpecifier *NNS); - -private: - template - llvm::hash_code tryCache(const void *Ptr, T Obj); - - llvm::hash_code hashImpl(const Decl *D); - llvm::hash_code hashImpl(CanQualType Ty); - llvm::hash_code hashImpl(DeclarationName Name); - llvm::hash_code hashImpl(const NestedNameSpecifier *NNS); -}; + /// \returns hash of the \p Record + llvm::hash_code hashRecord(ASTContext &Ctx, const FileIndexRecord &Record); } // end namespace index } // end namespace clang -#endif +#endif // LLVM_CLANG_LIB_INDEX_INDEXRECORDHASHER_H diff --git a/lib/Index/RecordHasher/CMakeLists.txt b/lib/Index/RecordHasher/CMakeLists.txt new file mode 100644 index 00000000000..239adfd80e9 --- /dev/null +++ b/lib/Index/RecordHasher/CMakeLists.txt @@ -0,0 +1,15 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) + +set(LLVM_LINK_COMPONENTS + Support + ) + +add_clang_library(clangIndexRecordHasher + IndexRecordHasher.cpp + DeclHasher.cpp + CachingHasher.cpp + + LINK_LIBS + clangAST + clangBasic + ) diff --git a/lib/Index/RecordHasher/CachingHasher.cpp b/lib/Index/RecordHasher/CachingHasher.cpp new file mode 100644 index 00000000000..a644bba6294 --- /dev/null +++ b/lib/Index/RecordHasher/CachingHasher.cpp @@ -0,0 +1,337 @@ +//===--- CachingHasher.cpp - Hashing of indexed entities --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RecordHasher/CachingHasher.h" +#include "RecordHasher/DeclHasher.h" + +namespace clang { +namespace index { + +using llvm::hash_code; + +hash_code CachingHasher::hash(const Decl *D) { + assert(D->isCanonicalDecl()); + + if (isa(D) || isa(D)) { + return getCachedHash(D, D); + } else if (auto *NS = dyn_cast(D)) { + if (NS->isAnonymousNamespace()) + return hash_value(StringRef("@aN")); + return getCachedHash(D, D); + } else { + // There's a balance between caching results and not growing the cache too + // much. Measurements showed that avoiding caching hashes for all decls is + // beneficial particularly when including all of Cocoa. + return hashImpl(D); + } +} + +hash_code CachingHasher::hash(QualType NonCanTy) { + CanQualType CanTy = Ctx.getCanonicalType(NonCanTy); + return hash(CanTy); +} + +hash_code CachingHasher::hash(CanQualType CT) { + // Do some hashing without going to the cache, for example we can avoid + // storing the hash for both the type and its const-qualified version. + hash_code Hash = InitialHash; + + while (true) { + Qualifiers Q = CT.getQualifiers(); + CT = CT.getUnqualifiedType(); + const Type *T = CT.getTypePtr(); + unsigned qVal = 0; + if (Q.hasConst()) + qVal |= 0x1; + if (Q.hasVolatile()) + qVal |= 0x2; + if (Q.hasRestrict()) + qVal |= 0x4; + if(qVal) + Hash = hash_combine(Hash, qVal); + + // FIXME: Hash in ObjC GC qualifiers + + if (const BuiltinType *BT = dyn_cast(T)) { + return hash_combine(Hash, BT->getKind()); + } + if (const PointerType *PT = dyn_cast(T)) { + Hash = hash_combine(Hash, '*'); + CT = CanQualType::CreateUnsafe(PT->getPointeeType()); + continue; + } + if (const ReferenceType *RT = dyn_cast(T)) { + Hash = hash_combine(Hash, '&'); + CT = CanQualType::CreateUnsafe(RT->getPointeeType()); + continue; + } + if (const BlockPointerType *BT = dyn_cast(T)) { + Hash = hash_combine(Hash, 'B'); + CT = CanQualType::CreateUnsafe(BT->getPointeeType()); + continue; + } + if (const ObjCObjectPointerType *OPT = dyn_cast(T)) { + Hash = hash_combine(Hash, '*'); + CT = CanQualType::CreateUnsafe(OPT->getPointeeType()); + continue; + } + if (const TagType *TT = dyn_cast(T)) { + return hash_combine(Hash, '$', hash(TT->getDecl()->getCanonicalDecl())); + } + if (const ObjCInterfaceType *OIT = dyn_cast(T)) { + return hash_combine(Hash, '$', hash(OIT->getDecl()->getCanonicalDecl())); + } + if (const ObjCObjectType *OIT = dyn_cast(T)) { + for (auto *Prot : OIT->getProtocols()) + Hash = hash_combine(Hash, hash(Prot)); + CT = CanQualType::CreateUnsafe(OIT->getBaseType()); + continue; + } + if (const TemplateTypeParmType *TTP = dyn_cast(T)) { + return hash_combine(Hash, 't', TTP->getDepth(), TTP->getIndex()); + } + if (const InjectedClassNameType *InjT = dyn_cast(T)) { + CT = CanQualType::CreateUnsafe(InjT->getInjectedSpecializationType().getCanonicalType()); + continue; + } + + break; + } + + return hash_combine(Hash, getCachedHash(CT.getAsOpaquePtr(), CT)); +} + +hash_code CachingHasher::hash(DeclarationName Name) { + assert(!Name.isEmpty()); + // Measurements for using cache or not here, showed significant slowdown when + // using the cache for all DeclarationNames when parsing Cocoa, and minor + // improvement or no difference for a couple of C++ single translation unit + // files. So we avoid caching DeclarationNames. + return hashImpl(Name); +} + +hash_code CachingHasher::hash(const NestedNameSpecifier *NNS) { + assert(NNS); + // Measurements for the C++ single translation unit files did not show much + // difference here; choosing to cache them currently. + return getCachedHash(NNS, NNS); +} + +hash_code CachingHasher::hash(const TemplateArgument &Arg) { + // No caching. + return hashImpl(Arg); +} + +template +hash_code CachingHasher::getCachedHash(const void *Ptr, T Obj) { + auto It = HashByPtr.find(Ptr); + if (It != HashByPtr.end()) + return It->second; + + hash_code Hash = hashImpl(Obj); + // hashImpl() may call into getCachedHash recursively and mutate + // HashByPtr, so we use find() earlier and insert the hash with another + // lookup here instead of calling insert() earlier and utilizing the iterator + // that insert() returns. + HashByPtr[Ptr] = Hash; + return Hash; +} + +hash_code CachingHasher::hashImpl(const Decl *D) { + return DeclHasher(*this).Visit(D); +} + +hash_code CachingHasher::hashImpl(const IdentifierInfo *II) { + return hash_value(II->getName()); +} + +hash_code CachingHasher::hashImpl(Selector Sel) { + unsigned N = Sel.getNumArgs(); + if (N == 0) + ++N; + hash_code Hash = InitialHash; + for (unsigned I = 0; I != N; ++I) + if (IdentifierInfo *II = Sel.getIdentifierInfoForSlot(I)) + Hash = hash_combine(Hash, hashImpl(II)); + return Hash; +} + +hash_code CachingHasher::hashImpl(TemplateName Name) { + hash_code Hash = InitialHash; + if (TemplateDecl *Template = Name.getAsTemplateDecl()) { + if (TemplateTemplateParmDecl *TTP + = dyn_cast(Template)) { + return hash_combine(Hash, 't', TTP->getDepth(), TTP->getIndex()); + } + + return hash_combine(Hash, hash(Template->getCanonicalDecl())); + } + + // FIXME: Hash dependent template names. + return Hash; +} + +hash_code CachingHasher::hashImpl(const TemplateArgument &Arg) { + hash_code Hash = InitialHash; + + switch (Arg.getKind()) { + case TemplateArgument::Null: + break; + + case TemplateArgument::Declaration: + Hash = hash_combine(Hash, hash(Arg.getAsDecl())); + break; + + case TemplateArgument::NullPtr: + break; + + case TemplateArgument::TemplateExpansion: + Hash = hash_combine(Hash, 'P'); // pack expansion of... + LLVM_FALLTHROUGH; + case TemplateArgument::Template: + Hash = hash_combine(Hash, hashImpl(Arg.getAsTemplateOrTemplatePattern())); + break; + + case TemplateArgument::Expression: + // FIXME: Hash expressions. + break; + + case TemplateArgument::Pack: + Hash = hash_combine(Hash, 'p'); + for (const auto &P : Arg.pack_elements()) + Hash = hash_combine(Hash, hashImpl(P)); + break; + + case TemplateArgument::Type: + Hash = hash_combine(Hash, hash(Arg.getAsType())); + break; + + case TemplateArgument::Integral: + Hash = hash_combine(Hash, 'V', hash(Arg.getIntegralType()), + Arg.getAsIntegral()); + break; + } + + return Hash; +} + +hash_code CachingHasher::hashImpl(CanQualType CQT) { + hash_code Hash = InitialHash; + + auto asCanon = [](QualType Ty) -> CanQualType { + return CanQualType::CreateUnsafe(Ty); + }; + + const Type *T = CQT.getTypePtr(); + + if (const PackExpansionType *Expansion = dyn_cast(T)) { + return hash_combine(Hash, 'P', hash(asCanon(Expansion->getPattern()))); + } + if (const RValueReferenceType *RT = dyn_cast(T)) { + return hash_combine(Hash, '%', hash(asCanon(RT->getPointeeType()))); + } + if (const FunctionProtoType *FT = dyn_cast(T)) { + Hash = hash_combine(Hash, 'F', hash(asCanon(FT->getReturnType()))); + for (const auto &I : FT->param_types()) + Hash = hash_combine(Hash, hash(asCanon(I))); + return hash_combine(Hash, FT->isVariadic()); + } + if (const ComplexType *CT = dyn_cast(T)) { + return hash_combine(Hash, '<', hash(asCanon(CT->getElementType()))); + } + if (const TemplateSpecializationType *Spec + = dyn_cast(T)) { + Hash = hash_combine(Hash, '>', hashImpl(Spec->getTemplateName())); + for (unsigned I = 0, N = Spec->getNumArgs(); I != N; ++I) + Hash = hash_combine(Hash, hashImpl(Spec->getArg(I))); + return Hash; + } + if (const DependentNameType *DNT = dyn_cast(T)) { + Hash = hash_combine(Hash, '^'); + if (const NestedNameSpecifier *NNS = DNT->getQualifier()) + Hash = hash_combine(Hash, hash(NNS)); + return hash_combine(Hash, hashImpl(DNT->getIdentifier())); + } + + // Unhandled type. + return Hash; +} + +hash_code CachingHasher::hashImpl(DeclarationName Name) { + hash_code Hash = InitialHash; + Hash = hash_combine(Hash, Name.getNameKind()); + + switch (Name.getNameKind()) { + case DeclarationName::Identifier: + Hash = hash_combine(Hash, hashImpl(Name.getAsIdentifierInfo())); + break; + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + Hash = hash_combine(Hash, hashImpl(Name.getObjCSelector())); + break; + case DeclarationName::CXXConstructorName: + case DeclarationName::CXXDestructorName: + case DeclarationName::CXXConversionFunctionName: + break; + case DeclarationName::CXXOperatorName: + Hash = hash_combine(Hash, Name.getCXXOverloadedOperator()); + break; + case DeclarationName::CXXLiteralOperatorName: + Hash = hash_combine(Hash, hashImpl(Name.getCXXLiteralIdentifier())); + break; + case DeclarationName::CXXUsingDirective: + break; + case DeclarationName::CXXDeductionGuideName: + Hash = hash_combine(Hash, hashImpl(Name.getCXXDeductionGuideTemplate() + ->getDeclName() + .getAsIdentifierInfo())); + break; + } + + return Hash; +} + +hash_code CachingHasher::hashImpl(const NestedNameSpecifier *NNS) { + hash_code Hash = InitialHash; + if (auto *Pre = NNS->getPrefix()) + Hash = hash_combine(Hash, hash(Pre)); + + Hash = hash_combine(Hash, NNS->getKind()); + + switch (NNS->getKind()) { + case NestedNameSpecifier::Identifier: + Hash = hash_combine(Hash, hashImpl(NNS->getAsIdentifier())); + break; + + case NestedNameSpecifier::Namespace: + Hash = hash_combine(Hash, hash(NNS->getAsNamespace()->getCanonicalDecl())); + break; + + case NestedNameSpecifier::NamespaceAlias: + Hash = hash_combine(Hash, + hash(NNS->getAsNamespaceAlias()->getCanonicalDecl())); + break; + + case NestedNameSpecifier::Global: + break; + + case NestedNameSpecifier::Super: + break; + + case NestedNameSpecifier::TypeSpecWithTemplate: + case NestedNameSpecifier::TypeSpec: + Hash = hash_combine(Hash, hash(QualType(NNS->getAsType(), 0))); + break; + } + + return Hash; +} + +} // end namespace index +} // end namespace clang diff --git a/lib/Index/RecordHasher/CachingHasher.h b/lib/Index/RecordHasher/CachingHasher.h new file mode 100644 index 00000000000..dc7557552dd --- /dev/null +++ b/lib/Index/RecordHasher/CachingHasher.h @@ -0,0 +1,60 @@ +//===--- CachingHasher.h - Hashing of indexed entities ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_RECORDHASHER_CACHINGHASHER_H +#define LLVM_CLANG_LIB_INDEX_RECORDHASHER_CACHINGHASHER_H + +#include "clang/AST/ASTContext.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Hashing.h" + +namespace clang { +namespace index { + +constexpr size_t InitialHash = 5381; + +/// Utility class implementing hashing and caching of hashes. +class CachingHasher { + ASTContext &Ctx; + llvm::DenseMap HashByPtr; + +public: + explicit CachingHasher(ASTContext &Ctx) : Ctx(Ctx) {} + ASTContext &getASTContext() { return Ctx; } + + /// Public interface that implements caching strategy. + llvm::hash_code hash(const Decl *D); + llvm::hash_code hash(QualType Ty); + llvm::hash_code hash(CanQualType Ty); + llvm::hash_code hash(DeclarationName Name); + llvm::hash_code hash(const NestedNameSpecifier *NNS); + llvm::hash_code hash(const TemplateArgument &Arg); + +private: + /// \returns hash of \p Obj. + /// Uses cached value if it exists otherwise calculates the hash, adds it to + /// the cache and returns. + template llvm::hash_code getCachedHash(const void *Ptr, T Obj); + + // Private methods implement hashing itself. Intentionally hidden from client + // (DeclHasher) to prevent accidental caching bypass. + llvm::hash_code hashImpl(const Decl *D); + llvm::hash_code hashImpl(CanQualType Ty); + llvm::hash_code hashImpl(DeclarationName Name); + llvm::hash_code hashImpl(const NestedNameSpecifier *NNS); + llvm::hash_code hashImpl(const TemplateArgument &Arg); + llvm::hash_code hashImpl(const IdentifierInfo *II); + llvm::hash_code hashImpl(Selector Sel); + llvm::hash_code hashImpl(TemplateName Name); +}; + +} // end namespace index +} // end namespace clang + +#endif // LLVM_CLANG_LIB_INDEX_RECORDHASHER_CACHINGHASHER_H diff --git a/lib/Index/RecordHasher/DeclHasher.cpp b/lib/Index/RecordHasher/DeclHasher.cpp new file mode 100644 index 00000000000..a1746ccf4a3 --- /dev/null +++ b/lib/Index/RecordHasher/DeclHasher.cpp @@ -0,0 +1,140 @@ +//===--- DeclHasher.cpp - Hashing of Decl nodes in AST ----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RecordHasher/DeclHasher.h" +#include "RecordHasher/CachingHasher.h" + +namespace clang { +namespace index { + +using llvm::hash_code; + +hash_code DeclHasher::VisitDecl(const Decl *D) { + return VisitDeclContext(D->getDeclContext()); +} + +hash_code DeclHasher::VisitNamedDecl(const NamedDecl *D) { + hash_code Hash = VisitDecl(D); + if (auto *attr = D->getExternalSourceSymbolAttr()) { + Hash = hash_combine(Hash, hash_value(attr->getDefinedIn())); + } + return hash_combine(Hash, Hasher.hash(D->getDeclName())); +} + +hash_code DeclHasher::VisitTagDecl(const TagDecl *D) { + if (D->getDeclName().isEmpty()) { + if (const TypedefNameDecl *TD = D->getTypedefNameForAnonDecl()) + return Visit(TD); + + hash_code Hash = VisitDeclContext(D->getDeclContext()); + if (D->isEmbeddedInDeclarator() && !D->isFreeStanding()) { + Hash = + hash_combine(Hash, hashLoc(D->getLocation(), /*IncludeOffset=*/true)); + } else + Hash = hash_combine(Hash, 'a'); + return Hash; + } + + hash_code Hash = VisitTypeDecl(D); + return hash_combine(Hash, 'T'); +} + +hash_code DeclHasher::VisitClassTemplateSpecializationDecl( + const ClassTemplateSpecializationDecl *D) { + hash_code Hash = VisitCXXRecordDecl(D); + const TemplateArgumentList &Args = D->getTemplateArgs(); + Hash = hash_combine(Hash, '>'); + for (unsigned I = 0, N = Args.size(); I != N; ++I) { + Hash = hash_combine(Hash, Hasher.hash(Args.get(I))); + } + return Hash; +} + +hash_code DeclHasher::VisitObjCContainerDecl(const ObjCContainerDecl *D) { + hash_code Hash = VisitNamedDecl(D); + return hash_combine(Hash, 'I'); +} + +hash_code DeclHasher::VisitObjCImplDecl(const ObjCImplDecl *D) { + if (auto *ID = D->getClassInterface()) + return VisitObjCInterfaceDecl(ID); + else + return 0; +} + +hash_code DeclHasher::VisitObjCCategoryDecl(const ObjCCategoryDecl *D) { + // FIXME: Differentiate between category and the interface ? + if (auto *ID = D->getClassInterface()) + return VisitObjCInterfaceDecl(ID); + else + return 0; +} + +hash_code DeclHasher::VisitFunctionDecl(const FunctionDecl *D) { + hash_code Hash = VisitNamedDecl(D); + ASTContext &Ctx = Hasher.getASTContext(); + if ((!Ctx.getLangOpts().CPlusPlus && !D->hasAttr()) || + D->isExternC()) + return Hash; + + for (auto param : D->parameters()) { + Hash = hash_combine(Hash, Hasher.hash(param->getType())); + } + return Hash; +} + +hash_code DeclHasher::VisitUnresolvedUsingTypenameDecl( + const UnresolvedUsingTypenameDecl *D) { + hash_code Hash = VisitNamedDecl(D); + Hash = hash_combine(Hash, Hasher.hash(D->getQualifier())); + return Hash; +} + +hash_code +DeclHasher::VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D) { + hash_code Hash = VisitNamedDecl(D); + Hash = hash_combine(Hash, Hasher.hash(D->getQualifier())); + return Hash; +} + +hash_code DeclHasher::VisitDeclContext(const DeclContext *DC) { + // FIXME: Add location if this is anonymous namespace ? + DC = DC->getRedeclContext(); + const Decl *D = cast(DC)->getCanonicalDecl(); + if (auto *ND = dyn_cast(D)) + return Hasher.hash(ND); + else + return 0; +} + +hash_code DeclHasher::hashLoc(SourceLocation Loc, bool IncludeOffset) { + if (Loc.isInvalid()) { + return 0; + } + hash_code Hash = InitialHash; + const SourceManager &SM = Hasher.getASTContext().getSourceManager(); + Loc = SM.getFileLoc(Loc); + const std::pair &Decomposed = SM.getDecomposedLoc(Loc); + const FileEntry *FE = SM.getFileEntryForID(Decomposed.first); + if (FE) { + Hash = hash_combine(Hash, llvm::sys::path::filename(FE->getName())); + } else { + // This case really isn't interesting. + return 0; + } + if (IncludeOffset) { + // Use the offest into the FileID to represent the location. Using + // a line/column can cause us to look back at the original source file, + // which is expensive. + Hash = hash_combine(Hash, Decomposed.second); + } + return Hash; +} + +} // end namespace index +} // end namespace clang diff --git a/lib/Index/RecordHasher/DeclHasher.h b/lib/Index/RecordHasher/DeclHasher.h new file mode 100644 index 00000000000..6fff62e60dd --- /dev/null +++ b/lib/Index/RecordHasher/DeclHasher.h @@ -0,0 +1,51 @@ +//===--- DeclHasher.h - Hashing of Decl nodes in AST ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_RECORDHASHER_DECLHASHER_H +#define LLVM_CLANG_LIB_INDEX_RECORDHASHER_DECLHASHER_H + +#include "clang/AST/Decl.h" +#include "clang/AST/DeclVisitor.h" +#include "llvm/Support/Path.h" + +namespace clang { +namespace index { + +class CachingHasher; + +/// Implements hashing for declaration nodes in AST. +/// This is just a convenient way how to avoid writing a huge switch for various +/// types derived from Decl. Uses CachingHasher for hashing of atomic entities. + +class DeclHasher : public ConstDeclVisitor { + CachingHasher &Hasher; + +public: + DeclHasher(CachingHasher &Hasher) : Hasher(Hasher) {} + + llvm::hash_code VisitDecl(const Decl *D); + llvm::hash_code VisitNamedDecl(const NamedDecl *D); + llvm::hash_code VisitTagDecl(const TagDecl *D); + llvm::hash_code VisitClassTemplateSpecializationDecl( + const ClassTemplateSpecializationDecl *D); + llvm::hash_code VisitObjCContainerDecl(const ObjCContainerDecl *D); + llvm::hash_code VisitObjCImplDecl(const ObjCImplDecl *D); + llvm::hash_code VisitObjCCategoryDecl(const ObjCCategoryDecl *D); + llvm::hash_code VisitFunctionDecl(const FunctionDecl *D); + llvm::hash_code + VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D); + llvm::hash_code + VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D); + llvm::hash_code VisitDeclContext(const DeclContext *DC); + llvm::hash_code hashLoc(SourceLocation Loc, bool IncludeOffset); +}; + +} // end namespace index +} // end namespace clang + +#endif // LLVM_CLANG_LIB_INDEX_RECORDHASHER_DECLHASHER_H diff --git a/lib/Index/RecordHasher/IndexRecordHasher.cpp b/lib/Index/RecordHasher/IndexRecordHasher.cpp new file mode 100644 index 00000000000..431d9f3a5d0 --- /dev/null +++ b/lib/Index/RecordHasher/IndexRecordHasher.cpp @@ -0,0 +1,33 @@ +//===--- IndexRecordHasher.cpp - Hashing of FileIndexRecord -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "IndexRecordHasher.h" +#include "FileIndexRecord.h" +#include "RecordHasher/CachingHasher.h" + +namespace clang { +namespace index { + +using llvm::hash_code; + +hash_code hashRecord(ASTContext &Ctx, const FileIndexRecord &Record) { + + CachingHasher Hasher(Ctx); + + hash_code Hash = InitialHash; + for (auto &Info : Record.getDeclOccurrencesSortedByOffset()) { + Hash = hash_combine(Hash, Info.Roles, Info.Offset, Hasher.hash(Info.Dcl)); + for (auto &Rel : Info.Relations) { + Hash = hash_combine(Hash, Hasher.hash(Rel.RelatedSymbol)); + } + } + return Hash; +} + +} // end namespace index +} // end namespace clang