//===-- ODRHash.cpp - Hashing to diagnose ODR failures ----------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// This file implements the ODRHash class, which calculates a hash based /// on AST nodes, which is stable across different runs. /// //===----------------------------------------------------------------------===// #include "clang/AST/ODRHash.h" #include "clang/AST/DeclVisitor.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/StmtVisitor.h" #include "clang/AST/TypeVisitor.h" using namespace clang; void ODRHash::AddStmt(const Stmt *S) { assert(S && "Expecting non-null pointer."); S->ProcessODRHash(ID, *this); } void ODRHash::AddIdentifierInfo(const IdentifierInfo *II) { assert(II && "Expecting non-null pointer."); ID.AddString(II->getName()); } void ODRHash::AddDeclarationName(DeclarationName Name) { AddBoolean(Name.isEmpty()); if (Name.isEmpty()) return; auto Kind = Name.getNameKind(); ID.AddInteger(Kind); switch (Kind) { case DeclarationName::Identifier: AddIdentifierInfo(Name.getAsIdentifierInfo()); break; case DeclarationName::ObjCZeroArgSelector: case DeclarationName::ObjCOneArgSelector: case DeclarationName::ObjCMultiArgSelector: { Selector S = Name.getObjCSelector(); AddBoolean(S.isNull()); AddBoolean(S.isKeywordSelector()); AddBoolean(S.isUnarySelector()); unsigned NumArgs = S.getNumArgs(); for (unsigned i = 0; i < NumArgs; ++i) { AddIdentifierInfo(S.getIdentifierInfoForSlot(i)); } break; } case DeclarationName::CXXConstructorName: case DeclarationName::CXXDestructorName: AddQualType(Name.getCXXNameType()); break; case DeclarationName::CXXOperatorName: ID.AddInteger(Name.getCXXOverloadedOperator()); break; case DeclarationName::CXXLiteralOperatorName: AddIdentifierInfo(Name.getCXXLiteralIdentifier()); break; case DeclarationName::CXXConversionFunctionName: AddQualType(Name.getCXXNameType()); break; case DeclarationName::CXXUsingDirective: break; case DeclarationName::CXXDeductionGuideName: { auto *Template = Name.getCXXDeductionGuideTemplate(); AddBoolean(Template); if (Template) { AddDecl(Template); } } } } void ODRHash::AddNestedNameSpecifier(const NestedNameSpecifier *NNS) {} void ODRHash::AddTemplateName(TemplateName Name) {} void ODRHash::AddTemplateArgument(TemplateArgument TA) {} void ODRHash::AddTemplateParameterList(const TemplateParameterList *TPL) {} void ODRHash::clear() { DeclMap.clear(); TypeMap.clear(); Bools.clear(); ID.clear(); } unsigned ODRHash::CalculateHash() { // Append the bools to the end of the data segment backwards. This allows // for the bools data to be compressed 32 times smaller compared to using // ID.AddBoolean const unsigned unsigned_bits = sizeof(unsigned) * CHAR_BIT; const unsigned size = Bools.size(); const unsigned remainder = size % unsigned_bits; const unsigned loops = size / unsigned_bits; auto I = Bools.rbegin(); unsigned value = 0; for (unsigned i = 0; i < remainder; ++i) { value <<= 1; value |= *I; ++I; } ID.AddInteger(value); for (unsigned i = 0; i < loops; ++i) { value = 0; for (unsigned j = 0; j < unsigned_bits; ++j) { value <<= 1; value |= *I; ++I; } ID.AddInteger(value); } assert(I == Bools.rend()); Bools.clear(); return ID.ComputeHash(); } // Process a Decl pointer. Add* methods call back into ODRHash while Visit* // methods process the relevant parts of the Decl. class ODRDeclVisitor : public ConstDeclVisitor { typedef ConstDeclVisitor Inherited; llvm::FoldingSetNodeID &ID; ODRHash &Hash; public: ODRDeclVisitor(llvm::FoldingSetNodeID &ID, ODRHash &Hash) : ID(ID), Hash(Hash) {} void AddStmt(const Stmt *S) { Hash.AddBoolean(S); if (S) { Hash.AddStmt(S); } } void AddIdentifierInfo(const IdentifierInfo *II) { Hash.AddBoolean(II); if (II) { Hash.AddIdentifierInfo(II); } } void AddQualType(QualType T) { Hash.AddQualType(T); } void Visit(const Decl *D) { ID.AddInteger(D->getKind()); Inherited::Visit(D); } void VisitNamedDecl(const NamedDecl *D) { Hash.AddDeclarationName(D->getDeclName()); Inherited::VisitNamedDecl(D); } void VisitValueDecl(const ValueDecl *D) { AddQualType(D->getType()); Inherited::VisitValueDecl(D); } void VisitAccessSpecDecl(const AccessSpecDecl *D) { ID.AddInteger(D->getAccess()); Inherited::VisitAccessSpecDecl(D); } void VisitStaticAssertDecl(const StaticAssertDecl *D) { AddStmt(D->getAssertExpr()); AddStmt(D->getMessage()); Inherited::VisitStaticAssertDecl(D); } void VisitFieldDecl(const FieldDecl *D) { const bool IsBitfield = D->isBitField(); Hash.AddBoolean(IsBitfield); if (IsBitfield) { AddStmt(D->getBitWidth()); } Hash.AddBoolean(D->isMutable()); AddStmt(D->getInClassInitializer()); Inherited::VisitFieldDecl(D); } void VisitFunctionDecl(const FunctionDecl *D) { ID.AddInteger(D->getStorageClass()); Hash.AddBoolean(D->isInlineSpecified()); Hash.AddBoolean(D->isVirtualAsWritten()); Hash.AddBoolean(D->isPure()); Hash.AddBoolean(D->isDeletedAsWritten()); Inherited::VisitFunctionDecl(D); } void VisitCXXMethodDecl(const CXXMethodDecl *D) { Hash.AddBoolean(D->isConst()); Hash.AddBoolean(D->isVolatile()); Inherited::VisitCXXMethodDecl(D); } void VisitTypedefNameDecl(const TypedefNameDecl *D) { AddQualType(D->getUnderlyingType()); Inherited::VisitTypedefNameDecl(D); } void VisitTypedefDecl(const TypedefDecl *D) { Inherited::VisitTypedefDecl(D); } void VisitTypeAliasDecl(const TypeAliasDecl *D) { Inherited::VisitTypeAliasDecl(D); } }; // Only allow a small portion of Decl's to be processed. Remove this once // all Decl's can be handled. bool ODRHash::isWhitelistedDecl(const Decl *D, const CXXRecordDecl *Parent) { if (D->isImplicit()) return false; if (D->getDeclContext() != Parent) return false; switch (D->getKind()) { default: return false; case Decl::AccessSpec: case Decl::CXXMethod: case Decl::Field: case Decl::StaticAssert: case Decl::TypeAlias: case Decl::Typedef: return true; } } void ODRHash::AddSubDecl(const Decl *D) { assert(D && "Expecting non-null pointer."); AddDecl(D); ODRDeclVisitor(ID, *this).Visit(D); } void ODRHash::AddCXXRecordDecl(const CXXRecordDecl *Record) { assert(Record && Record->hasDefinition() && "Expected non-null record to be a definition."); AddDecl(Record); // Filter out sub-Decls which will not be processed in order to get an // accurate count of Decl's. llvm::SmallVector Decls; for (const Decl *SubDecl : Record->decls()) { if (isWhitelistedDecl(SubDecl, Record)) { Decls.push_back(SubDecl); } } ID.AddInteger(Decls.size()); for (auto SubDecl : Decls) { AddSubDecl(SubDecl); } } void ODRHash::AddDecl(const Decl *D) { assert(D && "Expecting non-null pointer."); auto Result = DeclMap.insert(std::make_pair(D, DeclMap.size())); ID.AddInteger(Result.first->second); // On first encounter of a Decl pointer, process it. Every time afterwards, // only the index value is needed. if (!Result.second) { return; } ID.AddInteger(D->getKind()); if (const NamedDecl *ND = dyn_cast(D)) { AddDeclarationName(ND->getDeclName()); } } // Process a Type pointer. Add* methods call back into ODRHash while Visit* // methods process the relevant parts of the Type. class ODRTypeVisitor : public TypeVisitor { typedef TypeVisitor Inherited; llvm::FoldingSetNodeID &ID; ODRHash &Hash; public: ODRTypeVisitor(llvm::FoldingSetNodeID &ID, ODRHash &Hash) : ID(ID), Hash(Hash) {} void AddStmt(Stmt *S) { Hash.AddBoolean(S); if (S) { Hash.AddStmt(S); } } void AddDecl(Decl *D) { Hash.AddBoolean(D); if (D) { Hash.AddDecl(D); } } void Visit(const Type *T) { ID.AddInteger(T->getTypeClass()); Inherited::Visit(T); } void VisitType(const Type *T) {} void VisitBuiltinType(const BuiltinType *T) { ID.AddInteger(T->getKind()); VisitType(T); } void VisitTypedefType(const TypedefType *T) { AddDecl(T->getDecl()); Hash.AddQualType(T->getDecl()->getUnderlyingType()); VisitType(T); } }; void ODRHash::AddType(const Type *T) { assert(T && "Expecting non-null pointer."); auto Result = TypeMap.insert(std::make_pair(T, TypeMap.size())); ID.AddInteger(Result.first->second); // On first encounter of a Type pointer, process it. Every time afterwards, // only the index value is needed. if (!Result.second) { return; } ODRTypeVisitor(ID, *this).Visit(T); } void ODRHash::AddQualType(QualType T) { AddBoolean(T.isNull()); if (T.isNull()) return; SplitQualType split = T.split(); ID.AddInteger(split.Quals.getAsOpaqueValue()); AddType(split.Ty); } void ODRHash::AddBoolean(bool Value) { Bools.push_back(Value); }