-
Notifications
You must be signed in to change notification settings - Fork 11k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add the basics for the ODRHash class, which will only process Decl's from a whitelist, which currently only has AccessSpecDecl. Different access specifiers in merged classes can now be detected. Differential Revision: https://reviews.llvm.org/D21675 llvm-svn: 295800
- Loading branch information
1 parent
e67e162
commit e7f7ed2
Showing
7 changed files
with
800 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
//===-- ODRHash.h - 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 contains the declaration of the ODRHash class, which calculates | ||
/// a hash based on AST nodes, which is stable across different runs. | ||
/// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "clang/AST/DeclarationName.h" | ||
#include "clang/AST/Type.h" | ||
#include "clang/AST/TemplateBase.h" | ||
#include "llvm/ADT/DenseMap.h" | ||
#include "llvm/ADT/FoldingSet.h" | ||
#include "llvm/ADT/PointerUnion.h" | ||
#include "llvm/ADT/SmallVector.h" | ||
|
||
namespace clang { | ||
|
||
class Decl; | ||
class IdentifierInfo; | ||
class NestedNameSpecifer; | ||
class Stmt; | ||
class TemplateParameterList; | ||
|
||
// ODRHash is used to calculate a hash based on AST node contents that | ||
// does not rely on pointer addresses. This allows the hash to not vary | ||
// between runs and is usable to detect ODR problems in modules. To use, | ||
// construct an ODRHash object, then call Add* methods over the nodes that | ||
// need to be hashed. Then call CalculateHash to get the hash value. | ||
// Typically, only one Add* call is needed. clear can be called to reuse the | ||
// object. | ||
class ODRHash { | ||
// Use DenseMaps to convert between Decl and Type pointers and an index value. | ||
llvm::DenseMap<const Decl*, unsigned> DeclMap; | ||
llvm::DenseMap<const Type*, unsigned> TypeMap; | ||
|
||
// Save space by processing bools at the end. | ||
llvm::SmallVector<bool, 128> Bools; | ||
|
||
llvm::FoldingSetNodeID ID; | ||
|
||
public: | ||
ODRHash() {} | ||
|
||
// Use this for ODR checking classes between modules. This method compares | ||
// more information than the AddDecl class. | ||
void AddCXXRecordDecl(const CXXRecordDecl *Record); | ||
|
||
// Process SubDecls of the main Decl. This method calls the DeclVisitor | ||
// while AddDecl does not. | ||
void AddSubDecl(const Decl *D); | ||
|
||
// Reset the object for reuse. | ||
void clear(); | ||
|
||
// Add booleans to ID and uses it to calculate the hash. | ||
unsigned CalculateHash(); | ||
|
||
// Add AST nodes that need to be processed. | ||
void AddDecl(const Decl *D); | ||
void AddType(const Type *T); | ||
void AddQualType(QualType T); | ||
void AddStmt(const Stmt *S); | ||
void AddIdentifierInfo(const IdentifierInfo *II); | ||
void AddNestedNameSpecifier(const NestedNameSpecifier *NNS); | ||
void AddTemplateName(TemplateName Name); | ||
void AddDeclarationName(DeclarationName Name); | ||
void AddTemplateArgument(TemplateArgument TA); | ||
void AddTemplateParameterList(const TemplateParameterList *TPL); | ||
|
||
// Save booleans until the end to lower the size of data to process. | ||
void AddBoolean(bool value); | ||
|
||
static bool isWhitelistedDecl(const Decl* D, const CXXRecordDecl *Record); | ||
}; | ||
|
||
} // end namespace clang |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
//===-- 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) {} | ||
void ODRHash::AddIdentifierInfo(const IdentifierInfo *II) {} | ||
void ODRHash::AddNestedNameSpecifier(const NestedNameSpecifier *NNS) {} | ||
void ODRHash::AddTemplateName(TemplateName Name) {} | ||
void ODRHash::AddDeclarationName(DeclarationName 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<ODRDeclVisitor> { | ||
typedef ConstDeclVisitor<ODRDeclVisitor> Inherited; | ||
llvm::FoldingSetNodeID &ID; | ||
ODRHash &Hash; | ||
|
||
public: | ||
ODRDeclVisitor(llvm::FoldingSetNodeID &ID, ODRHash &Hash) | ||
: ID(ID), Hash(Hash) {} | ||
|
||
void Visit(const Decl *D) { | ||
ID.AddInteger(D->getKind()); | ||
Inherited::Visit(D); | ||
} | ||
|
||
void VisitAccessSpecDecl(const AccessSpecDecl *D) { | ||
ID.AddInteger(D->getAccess()); | ||
Inherited::VisitAccessSpecDecl(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: | ||
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<const Decl *, 16> 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()); | ||
} | ||
|
||
void ODRHash::AddType(const Type *T) {} | ||
void ODRHash::AddQualType(QualType T) {} | ||
void ODRHash::AddBoolean(bool Value) { | ||
Bools.push_back(Value); | ||
} |
Oops, something went wrong.