[modules] Rework serialized DeclContext lookup table management. Instead of

walking the loaded ModuleFiles looking for lookup tables for the context, store
them all in one place, and merge them together if we find we have too many
(currently, more than 4). If we do merge, include the merged form in our
serialized lookup table, so that downstream readers never need to look at our
imports' tables.

This gives a huge performance improvement to builds with very large numbers of
modules (in some cases, more than a 2x speedup was observed).

llvm-svn: 246497
This commit is contained in:
Richard Smith 2015-08-31 22:17:11 +00:00
parent 0518574a73
commit 9ce2b45936
13 changed files with 524 additions and 255 deletions

View File

@ -1530,4 +1530,23 @@ namespace clang {
}
} // end namespace clang
namespace llvm {
template <> struct DenseMapInfo<clang::serialization::DeclarationNameKey> {
static clang::serialization::DeclarationNameKey getEmptyKey() {
return clang::serialization::DeclarationNameKey(-1, 1);
}
static clang::serialization::DeclarationNameKey getTombstoneKey() {
return clang::serialization::DeclarationNameKey(-1, 2);
}
static unsigned
getHashValue(const clang::serialization::DeclarationNameKey &Key) {
return Key.getHash();
}
static bool isEqual(const clang::serialization::DeclarationNameKey &L,
const clang::serialization::DeclarationNameKey &R) {
return L == R;
}
};
}
#endif

View File

@ -282,9 +282,8 @@ class ReadMethodPoolVisitor;
namespace reader {
class ASTIdentifierLookupTrait;
/// \brief The on-disk hash table used for the DeclContext's Name lookup table.
typedef llvm::OnDiskIterableChainedHashTable<ASTDeclContextNameLookupTrait>
ASTDeclContextNameLookupTable;
/// \brief The on-disk hash table(s) used for DeclContext name lookup.
struct DeclContextLookupTable;
}
} // end namespace serialization
@ -507,6 +506,10 @@ private:
/// \brief Map from the TU to its lexical contents from each module file.
std::vector<std::pair<ModuleFile*, LexicalContents>> TULexicalDecls;
/// \brief Map from a DeclContext to its lookup tables.
llvm::DenseMap<const DeclContext *,
serialization::reader::DeclContextLookupTable> Lookups;
// Updates for visible decls can occur for other contexts than just the
// TU, and when we read those update records, the actual context may not
// be available yet, so have this pending map using the ID as a key. It
@ -514,7 +517,6 @@ private:
struct PendingVisibleUpdate {
ModuleFile *Mod;
const unsigned char *Data;
unsigned BucketOffset;
};
typedef SmallVector<PendingVisibleUpdate, 1> DeclContextVisibleUpdates;
@ -1089,6 +1091,10 @@ public:
Visit(GetExistingDecl(ID));
}
/// \brief Get the loaded lookup tables for \p Primary, if any.
const serialization::reader::DeclContextLookupTable *
getLoadedLookupTables(DeclContext *Primary) const;
private:
struct ImportedModule {
ModuleFile *Mod;
@ -1870,6 +1876,13 @@ public:
/// Note: overrides method in ExternalASTSource
Module *getModule(unsigned ID) override;
/// \brief Retrieve the module file with a given local ID within the specified
/// ModuleFile.
ModuleFile *getLocalModuleFile(ModuleFile &M, unsigned ID);
/// \brief Get an ID for the given module file.
unsigned getModuleFileID(ModuleFile *M);
/// \brief Return a descriptor for the corresponding module.
llvm::Optional<ASTSourceDescriptor> getSourceDescriptor(unsigned ID) override;
/// \brief Return a descriptor for the module.

View File

@ -529,8 +529,8 @@ private:
bool isLookupResultExternal(StoredDeclsList &Result, DeclContext *DC);
bool isLookupResultEntirelyExternal(StoredDeclsList &Result, DeclContext *DC);
uint32_t GenerateNameLookupTable(const DeclContext *DC,
llvm::SmallVectorImpl<char> &LookupTable);
void GenerateNameLookupTable(const DeclContext *DC,
llvm::SmallVectorImpl<char> &LookupTable);
uint64_t WriteDeclContextLexicalBlock(ASTContext &Context, DeclContext *DC);
uint64_t WriteDeclContextVisibleBlock(ASTContext &Context, DeclContext *DC);
void WriteTypeDeclOffsets();
@ -849,6 +849,7 @@ public:
unsigned getExprImplicitCastAbbrev() const { return ExprImplicitCastAbbrev; }
bool hasChain() const { return Chain; }
ASTReader *getChain() const { return Chain; }
// ASTDeserializationListener implementation
void ReaderInitialized(ASTReader *Reader) override;

View File

@ -50,14 +50,6 @@ enum ModuleKind {
MK_MainFile ///< File is a PCH file treated as the actual main file.
};
/// \brief Information about the contents of a DeclContext.
struct DeclContextInfo {
DeclContextInfo() : NameLookupTableData() {}
llvm::OnDiskIterableChainedHashTable<reader::ASTDeclContextNameLookupTrait>
*NameLookupTableData; // an ASTDeclContextNameLookupTable.
};
/// \brief The input file that has been loaded from this AST file, along with
/// bools indicating whether this was an overridden buffer or if it was
/// out-of-date or not-found.
@ -416,13 +408,6 @@ public:
/// indexed by the C++ ctor initializer list ID minus 1.
const uint32_t *CXXCtorInitializersOffsets;
typedef llvm::DenseMap<const DeclContext *, DeclContextInfo>
DeclContextInfosMap;
/// \brief Information about the lexical and visible declarations
/// for each DeclContext.
DeclContextInfosMap DeclContextInfos;
/// \brief Array of file-level DeclIDs sorted by file.
const serialization::DeclID *FileSortedDecls;
unsigned NumFileSortedDecls;

View File

@ -20,6 +20,7 @@
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Frontend/PCHContainerOperations.h"
#include "clang/AST/ASTMutationListener.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLocVisitor.h"
@ -903,6 +904,13 @@ unsigned DeclarationNameKey::getHash() const {
return ID.ComputeHash();
}
ModuleFile *
ASTDeclContextNameLookupTrait::ReadFileRef(const unsigned char *&d) {
using namespace llvm::support;
uint32_t ModuleFileID = endian::readNext<uint32_t, little, unaligned>(d);
return Reader.getLocalModuleFile(F, ModuleFileID);
}
std::pair<unsigned, unsigned>
ASTDeclContextNameLookupTrait::ReadKeyDataLength(const unsigned char *&d) {
using namespace llvm::support;
@ -948,15 +956,15 @@ ASTDeclContextNameLookupTrait::ReadKey(const unsigned char *d, unsigned) {
return DeclarationNameKey(Kind, Data);
}
ASTDeclContextNameLookupTrait::data_type
ASTDeclContextNameLookupTrait::ReadData(internal_key_type,
const unsigned char *d,
unsigned DataLen) {
void ASTDeclContextNameLookupTrait::ReadDataInto(internal_key_type,
const unsigned char *d,
unsigned DataLen,
data_type &Val) {
using namespace llvm::support;
unsigned NumDecls = DataLen / 4;
LE32DeclID *Start = reinterpret_cast<LE32DeclID *>(
const_cast<unsigned char *>(d));
return std::make_pair(Start, Start + NumDecls);
for (unsigned NumDecls = DataLen / 4; NumDecls; --NumDecls) {
uint32_t LocalID = endian::readNext<uint32_t, little, unaligned>(d);
Val.insert(Reader.getGlobalDeclID(F, LocalID));
}
}
bool ASTReader::ReadLexicalDeclContextStorage(ModuleFile &M,
@ -1015,9 +1023,8 @@ bool ASTReader::ReadVisibleDeclContextStorage(ModuleFile &M,
// We can't safely determine the primary context yet, so delay attaching the
// lookup table until we're done with recursive deserialization.
unsigned BucketOffset = Record[0];
PendingVisibleUpdates[ID].push_back(PendingVisibleUpdate{
&M, (const unsigned char *)Blob.data(), BucketOffset});
auto *Data = (const unsigned char*)Blob.data();
PendingVisibleUpdates[ID].push_back(PendingVisibleUpdate{&M, Data});
return false;
}
@ -2551,9 +2558,7 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
unsigned Idx = 0;
serialization::DeclID ID = ReadDeclID(F, Record, Idx);
auto *Data = (const unsigned char*)Blob.data();
unsigned BucketOffset = Record[Idx++];
PendingVisibleUpdates[ID].push_back(
PendingVisibleUpdate{&F, Data, BucketOffset});
PendingVisibleUpdates[ID].push_back(PendingVisibleUpdate{&F, Data});
// If we've already loaded the decl, perform the updates when we finish
// loading this block.
if (Decl *D = GetExistingDecl(ID))
@ -6354,196 +6359,48 @@ void ASTReader::FindFileRegionDecls(FileID File,
Decls.push_back(GetDecl(getGlobalDeclID(*DInfo.Mod, *DIt)));
}
/// \brief Retrieve the "definitive" module file for the definition of the
/// given declaration context, if there is one.
///
/// The "definitive" module file is the only place where we need to look to
/// find information about the declarations within the given declaration
/// context. For example, C++ and Objective-C classes, C structs/unions, and
/// Objective-C protocols, categories, and extensions are all defined in a
/// single place in the source code, so they have definitive module files
/// associated with them. C++ namespaces, on the other hand, can have
/// definitions in multiple different module files.
///
/// Note: this needs to be kept in sync with ASTWriter::AddedVisibleDecl's
/// NDEBUG checking.
static ModuleFile *getDefinitiveModuleFileFor(const DeclContext *DC,
ASTReader &Reader) {
if (const DeclContext *DefDC = getDefinitiveDeclContext(DC))
return Reader.getOwningModuleFile(cast<Decl>(DefDC));
return nullptr;
}
namespace {
/// \brief ModuleFile visitor used to perform name lookup into a
/// declaration context.
class DeclContextNameLookupVisitor {
ASTReader &Reader;
const DeclContext *Context;
DeclarationName Name;
DeclarationNameKey NameKey;
unsigned NameHash;
SmallVectorImpl<NamedDecl *> &Decls;
llvm::SmallPtrSetImpl<NamedDecl *> &DeclSet;
public:
DeclContextNameLookupVisitor(ASTReader &Reader, const DeclContext *Context,
DeclarationName Name,
SmallVectorImpl<NamedDecl *> &Decls,
llvm::SmallPtrSetImpl<NamedDecl *> &DeclSet)
: Reader(Reader), Context(Context), Name(Name), NameKey(Name),
NameHash(NameKey.getHash()), Decls(Decls), DeclSet(DeclSet) {}
bool operator()(ModuleFile &M) {
// Check whether we have any visible declaration information for
// this context in this module.
auto Info = M.DeclContextInfos.find(Context);
if (Info == M.DeclContextInfos.end() || !Info->second.NameLookupTableData)
return false;
// Look for this name within this module.
ASTDeclContextNameLookupTable *LookupTable =
Info->second.NameLookupTableData;
ASTDeclContextNameLookupTable::iterator Pos =
LookupTable->find_hashed(NameKey, NameHash);
if (Pos == LookupTable->end())
return false;
bool FoundAnything = false;
ASTDeclContextNameLookupTrait::data_type Data = *Pos;
for (; Data.first != Data.second; ++Data.first) {
NamedDecl *ND = Reader.GetLocalDeclAs<NamedDecl>(M, *Data.first);
if (!ND)
continue;
if (ND->getDeclName() != Name) {
// Not all names map to a unique DeclarationNameKey.
assert(DeclarationNameKey(ND->getDeclName()) == NameKey &&
"mismatched name for decl in decl context lookup table?");
continue;
}
// Record this declaration.
FoundAnything = true;
if (DeclSet.insert(ND).second)
Decls.push_back(ND);
}
return FoundAnything;
}
};
}
bool
ASTReader::FindExternalVisibleDeclsByName(const DeclContext *DC,
DeclarationName Name) {
assert(DC->hasExternalVisibleStorage() &&
assert(DC->hasExternalVisibleStorage() && DC == DC->getPrimaryContext() &&
"DeclContext has no visible decls in storage");
if (!Name)
return false;
auto It = Lookups.find(DC);
if (It == Lookups.end())
return false;
Deserializing LookupResults(this);
// Load the list of declarations.
SmallVector<NamedDecl *, 64> Decls;
llvm::SmallPtrSet<NamedDecl*, 64> DeclSet;
DeclContextNameLookupVisitor Visitor(*this, DC, Name, Decls, DeclSet);
// If we can definitively determine which module file to look into,
// only look there. Otherwise, look in all module files.
if (ModuleFile *Definitive = getDefinitiveModuleFileFor(DC, *this))
Visitor(*Definitive);
else
ModuleMgr.visit(Visitor);
for (DeclID ID : It->second.Table.find(Name)) {
NamedDecl *ND = cast<NamedDecl>(GetDecl(ID));
if (ND->getDeclName() == Name)
Decls.push_back(ND);
}
++NumVisibleDeclContextsRead;
SetExternalVisibleDeclsForName(DC, Name, Decls);
return !Decls.empty();
}
namespace {
/// \brief ModuleFile visitor used to retrieve all visible names in a
/// declaration context.
class DeclContextAllNamesVisitor {
ASTReader &Reader;
SmallVectorImpl<const DeclContext *> &Contexts;
DeclsMap &Decls;
llvm::SmallPtrSet<NamedDecl *, 256> DeclSet;
bool VisitAll;
public:
DeclContextAllNamesVisitor(ASTReader &Reader,
SmallVectorImpl<const DeclContext *> &Contexts,
DeclsMap &Decls, bool VisitAll)
: Reader(Reader), Contexts(Contexts), Decls(Decls), VisitAll(VisitAll) { }
bool operator()(ModuleFile &M) {
// Check whether we have any visible declaration information for
// this context in this module.
ModuleFile::DeclContextInfosMap::iterator Info;
bool FoundInfo = false;
for (unsigned I = 0, N = Contexts.size(); I != N; ++I) {
Info = M.DeclContextInfos.find(Contexts[I]);
if (Info != M.DeclContextInfos.end() &&
Info->second.NameLookupTableData) {
FoundInfo = true;
break;
}
}
if (!FoundInfo)
return false;
ASTDeclContextNameLookupTable *LookupTable =
Info->second.NameLookupTableData;
bool FoundAnything = false;
for (ASTDeclContextNameLookupTable::data_iterator
I = LookupTable->data_begin(), E = LookupTable->data_end();
I != E;
++I) {
ASTDeclContextNameLookupTrait::data_type Data = *I;
for (; Data.first != Data.second; ++Data.first) {
NamedDecl *ND = Reader.GetLocalDeclAs<NamedDecl>(M, *Data.first);
if (!ND)
continue;
// Record this declaration.
FoundAnything = true;
if (DeclSet.insert(ND).second)
Decls[ND->getDeclName()].push_back(ND);
}
}
return FoundAnything && !VisitAll;
}
};
}
void ASTReader::completeVisibleDeclsMap(const DeclContext *DC) {
if (!DC->hasExternalVisibleStorage())
return;
auto It = Lookups.find(DC);
assert(It != Lookups.end() &&
"have external visible storage but no lookup tables");
DeclsMap Decls;
// Compute the declaration contexts we need to look into. Multiple such
// declaration contexts occur when two declaration contexts from disjoint
// modules get merged, e.g., when two namespaces with the same name are
// independently defined in separate modules.
SmallVector<const DeclContext *, 2> Contexts;
Contexts.push_back(DC);
if (DC->isNamespace()) {
KeyDeclsMap::iterator Key =
KeyDecls.find(const_cast<Decl *>(cast<Decl>(DC)));
if (Key != KeyDecls.end()) {
for (unsigned I = 0, N = Key->second.size(); I != N; ++I)
Contexts.push_back(cast<DeclContext>(GetDecl(Key->second[I])));
}
for (DeclID ID : It->second.Table.findAll()) {
NamedDecl *ND = cast<NamedDecl>(GetDecl(ID));
Decls[ND->getDeclName()].push_back(ND);
}
DeclContextAllNamesVisitor Visitor(*this, Contexts, Decls,
/*VisitAll=*/DC->isFileContext());
ModuleMgr.visit(Visitor);
++NumVisibleDeclContextsRead;
for (DeclsMap::iterator I = Decls.begin(), E = Decls.end(); I != E; ++I) {
@ -6552,6 +6409,12 @@ void ASTReader::completeVisibleDeclsMap(const DeclContext *DC) {
const_cast<DeclContext *>(DC)->setHasExternalVisibleStorage(false);
}
const serialization::reader::DeclContextLookupTable *
ASTReader::getLoadedLookupTables(DeclContext *Primary) const {
auto I = Lookups.find(Primary);
return I == Lookups.end() ? nullptr : &I->second;
}
/// \brief Under non-PCH compilation the consumer receives the objc methods
/// before receiving the implementation, and codegen depends on this.
/// We simulate this by deserializing and passing to consumer the methods of the
@ -7383,6 +7246,37 @@ Module *ASTReader::getModule(unsigned ID) {
return getSubmodule(ID);
}
ModuleFile *ASTReader::getLocalModuleFile(ModuleFile &F, unsigned ID) {
if (ID & 1) {
// It's a module, look it up by submodule ID.
auto I = GlobalSubmoduleMap.find(getGlobalSubmoduleID(F, ID >> 1));
return I == GlobalSubmoduleMap.end() ? nullptr : I->second;
} else {
// It's a prefix (preamble, PCH, ...). Look it up by index.
unsigned IndexFromEnd = ID >> 1;
assert(IndexFromEnd && "got reference to unknown module file");
return getModuleManager().pch_modules().end()[-IndexFromEnd];
}
}
unsigned ASTReader::getModuleFileID(ModuleFile *F) {
if (!F)
return 1;
// For a file representing a module, use the submodule ID of the top-level
// module as the file ID. For any other kind of file, the number of such
// files loaded beforehand will be the same on reload.
// FIXME: Is this true even if we have an explicit module file and a PCH?
if (F->isModule())
// FIXME: BaseSubmoduleID appears to be off by one.
return ((F->BaseSubmoduleID + 1) << 1) | 1;
auto PCHModules = getModuleManager().pch_modules();
auto I = std::find(PCHModules.begin(), PCHModules.end(), F);
assert(I != PCHModules.end() && "emitting reference to unknown file");
return (I - PCHModules.end()) << 1;
}
ExternalASTSource::ASTSourceDescriptor
ASTReader::getSourceDescriptor(const Module &M) {
StringRef Dir, Filename;
@ -8431,6 +8325,8 @@ void ASTReader::FinishedDeserializing() {
for (auto Update : Updates) {
auto *FPT = Update.second->getType()->castAs<FunctionProtoType>();
auto ESI = FPT->getExtProtoInfo().ExceptionSpec;
if (auto *Listener = Context.getASTMutationListener())
Listener->ResolvedExceptionSpec(cast<FunctionDecl>(Update.second));
for (auto *Redecl : Update.second->redecls())
Context.adjustExceptionSpec(cast<FunctionDecl>(Redecl), ESI);
}

View File

@ -1489,6 +1489,8 @@ void ASTDeclReader::MergeDefinitionData(
Reader.PendingDefinitions.erase(MergeDD.Definition);
MergeDD.Definition->IsCompleteDefinition = false;
mergeDefinitionVisibility(DD.Definition, MergeDD.Definition);
assert(Reader.Lookups.find(MergeDD.Definition) == Reader.Lookups.end() &&
"already loaded pending lookups for merged definition");
}
auto PFDI = Reader.PendingFakeDefinitionData.find(&DD);
@ -3346,15 +3348,10 @@ void ASTReader::loadDeclUpdateRecords(serialization::DeclID ID, Decl *D) {
PendingVisibleUpdates.erase(I);
auto *DC = cast<DeclContext>(D)->getPrimaryContext();
for (const PendingVisibleUpdate &Update : VisibleUpdates) {
auto *&LookupTable = Update.Mod->DeclContextInfos[DC].NameLookupTableData;
assert(!LookupTable && "multiple lookup tables for DC in module");
LookupTable = reader::ASTDeclContextNameLookupTable::Create(
Update.Data + Update.BucketOffset,
Update.Data + sizeof(uint32_t),
Update.Data,
for (const PendingVisibleUpdate &Update : VisibleUpdates)
Lookups[DC].Table.add(
Update.Mod, Update.Data,
reader::ASTDeclContextNameLookupTrait(*this, *Update.Mod));
}
DC->setHasExternalVisibleStorage(true);
}

View File

@ -15,8 +15,12 @@
#include "clang/AST/DeclarationName.h"
#include "clang/Serialization/ASTBitCodes.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/OnDiskHashTable.h"
#include "MultiOnDiskHashTable.h"
#include <utility>
namespace clang {
@ -39,14 +43,15 @@ class ASTDeclContextNameLookupTrait {
ModuleFile &F;
public:
/// \brief Pair of begin/end iterators for DeclIDs.
///
/// Note that these declaration IDs are local to the module that contains this
/// particular lookup t
typedef llvm::support::ulittle32_t LE32DeclID;
typedef std::pair<LE32DeclID *, LE32DeclID *> data_type;
// Maximum number of lookup tables we allow before condensing the tables.
static const int MaxTables = 4;
/// The lookup result is a list of global declaration IDs.
// FIXME: LLVM doesn't really have a good data structure for this.
typedef llvm::DenseSet<DeclID> data_type;
typedef unsigned hash_value_type;
typedef unsigned offset_type;
typedef ModuleFile *file_type;
typedef DeclarationName external_key_type;
typedef DeclarationNameKey internal_key_type;
@ -54,8 +59,7 @@ public:
explicit ASTDeclContextNameLookupTrait(ASTReader &Reader, ModuleFile &F)
: Reader(Reader), F(F) { }
static bool EqualKey(const internal_key_type& a,
const internal_key_type& b) {
static bool EqualKey(const internal_key_type &a, const internal_key_type &b) {
return a == b;
}
@ -71,8 +75,18 @@ public:
internal_key_type ReadKey(const unsigned char *d, unsigned);
data_type ReadData(internal_key_type, const unsigned char *d,
unsigned DataLen);
void ReadDataInto(internal_key_type, const unsigned char *d,
unsigned DataLen, data_type &Val);
static void MergeDataInto(const data_type &From, data_type &To) {
To.insert(From.begin(), From.end());
}
file_type ReadFileRef(const unsigned char *&d);
};
struct DeclContextLookupTable {
MultiOnDiskHashTable<ASTDeclContextNameLookupTrait> Table;
};
/// \brief Base class for the trait describing the on-disk hash table for the

View File

@ -13,6 +13,8 @@
#include "clang/Serialization/ASTWriter.h"
#include "ASTCommon.h"
#include "ASTReaderInternals.h"
#include "MultiOnDiskHashTable.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclContextInternals.h"
@ -3338,12 +3340,14 @@ namespace {
// Trait used for the on-disk hash table used in the method pool.
class ASTDeclContextNameLookupTrait {
ASTWriter &Writer;
llvm::SmallVector<DeclID, 64> DeclIDs;
public:
typedef DeclarationNameKey key_type;
typedef key_type key_type_ref;
typedef DeclContext::lookup_result data_type;
/// A start and end index into DeclIDs, representing a sequence of decls.
typedef std::pair<unsigned, unsigned> data_type;
typedef const data_type& data_type_ref;
typedef unsigned hash_value_type;
@ -3351,10 +3355,40 @@ public:
explicit ASTDeclContextNameLookupTrait(ASTWriter &Writer) : Writer(Writer) { }
template<typename Coll>
data_type getData(const Coll &Decls) {
unsigned Start = DeclIDs.size();
for (NamedDecl *D : Decls) {
DeclIDs.push_back(
Writer.GetDeclRef(getDeclForLocalLookup(Writer.getLangOpts(), D)));
}
return std::make_pair(Start, DeclIDs.size());
}
data_type ImportData(const reader::ASTDeclContextNameLookupTrait::data_type &FromReader) {
unsigned Start = DeclIDs.size();
for (auto ID : FromReader)
DeclIDs.push_back(ID);
return std::make_pair(Start, DeclIDs.size());
}
static bool EqualKey(key_type_ref a, key_type_ref b) {
return a == b;
}
hash_value_type ComputeHash(DeclarationNameKey Name) {
return Name.getHash();
}
void EmitFileRef(raw_ostream &Out, ModuleFile *F) const {
assert(Writer.hasChain() &&
"have reference to loaded module file but no chain?");
using namespace llvm::support;
endian::Writer<little>(Out)
.write<uint32_t>(Writer.getChain()->getModuleFileID(F));
}
std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &Out,
DeclarationNameKey Name,
data_type_ref Lookup) {
@ -3381,7 +3415,9 @@ public:
LE.write<uint16_t>(KeyLen);
// 4 bytes for each DeclID.
unsigned DataLen = 4 * Lookup.size();
unsigned DataLen = 4 * (Lookup.second - Lookup.first);
assert(uint16_t(DataLen) == DataLen &&
"too many decls for serialized lookup result");
LE.write<uint16_t>(DataLen);
return std::make_pair(KeyLen, DataLen);
@ -3421,11 +3457,8 @@ public:
using namespace llvm::support;
endian::Writer<little> LE(Out);
uint64_t Start = Out.tell(); (void)Start;
for (DeclContext::lookup_iterator I = Lookup.begin(), E = Lookup.end();
I != E; ++I)
LE.write<uint32_t>(
Writer.GetDeclRef(getDeclForLocalLookup(Writer.getLangOpts(), *I)));
for (unsigned I = Lookup.first, N = Lookup.second; I != N; ++I)
LE.write<uint32_t>(DeclIDs[I]);
assert(Out.tell() - Start == DataLen && "Data length is wrong");
}
};
@ -3445,7 +3478,7 @@ bool ASTWriter::isLookupResultEntirelyExternal(StoredDeclsList &Result,
return true;
}
uint32_t
void
ASTWriter::GenerateNameLookupTable(const DeclContext *ConstDC,
llvm::SmallVectorImpl<char> &LookupTable) {
assert(!ConstDC->HasLazyLocalLexicalLookups &&
@ -3457,8 +3490,8 @@ ASTWriter::GenerateNameLookupTable(const DeclContext *ConstDC,
assert(DC == DC->getPrimaryContext() && "only primary DC has lookup table");
// Create the on-disk hash table representation.
llvm::OnDiskChainedHashTableGenerator<ASTDeclContextNameLookupTrait>
Generator;
MultiOnDiskHashTableGenerator<reader::ASTDeclContextNameLookupTrait,
ASTDeclContextNameLookupTrait> Generator;
ASTDeclContextNameLookupTrait Trait(*this);
// The first step is to collect the declaration names which we need to
@ -3593,7 +3626,7 @@ ASTWriter::GenerateNameLookupTable(const DeclContext *ConstDC,
switch (Name.getNameKind()) {
default:
Generator.insert(Name, Result, Trait);
Generator.insert(Name, Trait.getData(Result), Trait);
break;
case DeclarationName::CXXConstructorName:
@ -3611,17 +3644,15 @@ ASTWriter::GenerateNameLookupTable(const DeclContext *ConstDC,
// the key, only the kind of name is used.
if (!ConstructorDecls.empty())
Generator.insert(ConstructorDecls.front()->getDeclName(),
DeclContext::lookup_result(ConstructorDecls), Trait);
Trait.getData(ConstructorDecls), Trait);
if (!ConversionDecls.empty())
Generator.insert(ConversionDecls.front()->getDeclName(),
DeclContext::lookup_result(ConversionDecls), Trait);
Trait.getData(ConversionDecls), Trait);
// Create the on-disk hash table in a buffer.
llvm::raw_svector_ostream Out(LookupTable);
// Make sure that no bucket is at offset 0
using namespace llvm::support;
endian::Writer<little>(Out).write<uint32_t>(0);
return Generator.Emit(Out, Trait);
// Create the on-disk hash table. Also emit the existing imported and
// merged table if there is one.
auto *Lookups = Chain ? Chain->getLoadedLookupTables(DC) : nullptr;
Generator.emit(LookupTable, Trait, Lookups ? &Lookups->Table : nullptr);
}
/// \brief Write the block containing all of the declaration IDs
@ -3704,12 +3735,11 @@ uint64_t ASTWriter::WriteDeclContextVisibleBlock(ASTContext &Context,
// Create the on-disk hash table in a buffer.
SmallString<4096> LookupTable;
uint32_t BucketOffset = GenerateNameLookupTable(DC, LookupTable);
GenerateNameLookupTable(DC, LookupTable);
// Write the lookup table
RecordData Record;
Record.push_back(DECL_CONTEXT_VISIBLE);
Record.push_back(BucketOffset);
Stream.EmitRecordWithBlob(DeclContextVisibleLookupAbbrev, Record,
LookupTable);
++NumVisibleDeclContexts;
@ -3732,7 +3762,7 @@ void ASTWriter::WriteDeclContextVisibleUpdate(const DeclContext *DC) {
// Create the on-disk hash table in a buffer.
SmallString<4096> LookupTable;
uint32_t BucketOffset = GenerateNameLookupTable(DC, LookupTable);
GenerateNameLookupTable(DC, LookupTable);
// If we're updating a namespace, select a key declaration as the key for the
// update record; those are the only ones that will be checked on reload.
@ -3743,7 +3773,6 @@ void ASTWriter::WriteDeclContextVisibleUpdate(const DeclContext *DC) {
RecordData Record;
Record.push_back(UPDATE_VISIBLE);
Record.push_back(getDeclID(cast<Decl>(DC)));
Record.push_back(BucketOffset);
Stream.EmitRecordWithBlob(UpdateVisibleAbbrev, Record, LookupTable);
}
@ -4207,7 +4236,6 @@ void ASTWriter::WriteASTCore(Sema &SemaRef,
Abv = new llvm::BitCodeAbbrev();
Abv->Add(llvm::BitCodeAbbrevOp(UPDATE_VISIBLE));
Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::VBR, 6));
Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed, 32));
Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob));
UpdateVisibleAbbrev = Stream.EmitAbbrev(Abv);
WriteDeclContextVisibleUpdate(TU);

View File

@ -2049,7 +2049,6 @@ void ASTWriter::WriteDeclAbbrevs() {
Abv = new BitCodeAbbrev();
Abv->Add(BitCodeAbbrevOp(serialization::DECL_CONTEXT_VISIBLE));
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32));
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));
DeclContextVisibleLookupAbbrev = Stream.EmitAbbrev(Abv);
}

View File

@ -45,13 +45,6 @@ ModuleFile::ModuleFile(ModuleKind Kind, unsigned Generation)
{}
ModuleFile::~ModuleFile() {
for (DeclContextInfosMap::iterator I = DeclContextInfos.begin(),
E = DeclContextInfos.end();
I != E; ++I) {
if (I->second.NameLookupTableData)
delete I->second.NameLookupTableData;
}
delete static_cast<ASTIdentifierLookupTable *>(IdentifierLookupTable);
delete static_cast<HeaderFileInfoLookupTable *>(HeaderFileInfoTable);
delete static_cast<ASTSelectorLookupTable *>(SelectorLookupTable);

View File

@ -0,0 +1,318 @@
//===--- MultiOnDiskHashTable.h - Merged set of hash tables -----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file provides a hash table data structure suitable for incremental and
// distributed storage across a set of files.
//
// Multiple hash tables from different files are implicitly merged to improve
// performance, and on reload the merged table will override those from other
// files.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_LIB_SERIALIZATION_MULTIONDISKHASHTABLE_H
#define LLVM_CLANG_LIB_SERIALIZATION_MULTIONDISKHASHTABLE_H
#include "llvm/ADT/PointerUnion.h"
#include "llvm/Support/EndianStream.h"
#include "llvm/Support/OnDiskHashTable.h"
namespace clang {
namespace serialization {
class ModuleFile;
/// \brief A collection of on-disk hash tables, merged when relevant for performance.
template<typename Info> class MultiOnDiskHashTable {
public:
/// A handle to a file, used when overriding tables.
typedef typename Info::file_type file_type;
/// A pointer to an on-disk representation of the hash table.
typedef const unsigned char *storage_type;
typedef typename Info::external_key_type external_key_type;
typedef typename Info::internal_key_type internal_key_type;
typedef typename Info::data_type data_type;
typedef unsigned hash_value_type;
private:
/// \brief A hash table stored on disk.
struct OnDiskTable {
typedef llvm::OnDiskIterableChainedHashTable<Info> HashTable;
file_type File;
HashTable Table;
OnDiskTable(file_type File, unsigned NumBuckets, unsigned NumEntries,
storage_type Buckets, storage_type Payload, storage_type Base,
const Info &InfoObj)
: File(File),
Table(NumBuckets, NumEntries, Buckets, Payload, Base, InfoObj) {}
};
struct MergedTable {
std::vector<file_type> Files;
llvm::DenseMap<internal_key_type, data_type> Data;
};
typedef llvm::PointerUnion<OnDiskTable*, MergedTable*> Table;
typedef llvm::TinyPtrVector<void*> TableVector;
/// \brief The current set of on-disk and merged tables.
/// We manually store the opaque value of the Table because TinyPtrVector
/// can't cope with holding a PointerUnion directly.
/// There can be at most one MergedTable in this vector, and if present,
/// it is the first table.
TableVector Tables;
/// \brief Files corresponding to overridden tables that we've not yet
/// discarded.
llvm::TinyPtrVector<file_type> PendingOverrides;
struct AsOnDiskTable {
typedef OnDiskTable *result_type;
result_type operator()(void *P) const {
return Table::getFromOpaqueValue(P).template get<OnDiskTable *>();
}
};
typedef llvm::mapped_iterator<TableVector::iterator, AsOnDiskTable>
table_iterator;
typedef llvm::iterator_range<table_iterator> table_range;
/// \brief The current set of on-disk tables.
table_range tables() {
auto Begin = Tables.begin(), End = Tables.end();
if (getMergedTable())
++Begin;
return llvm::make_range(llvm::map_iterator(Begin, AsOnDiskTable()),
llvm::map_iterator(End, AsOnDiskTable()));
}
MergedTable *getMergedTable() const {
// If we already have a merged table, it's the first one.
return Tables.empty() ? nullptr : Table::getFromOpaqueValue(*Tables.begin())
.template dyn_cast<MergedTable*>();
}
/// \brief Delete all our current on-disk tables.
void clear() {
if (auto *M = getMergedTable())
delete M;
for (auto *T : tables())
delete T;
}
void removeOverriddenTables() {
llvm::DenseSet<file_type> Files;
Files.insert(PendingOverrides.begin(), PendingOverrides.end());
Tables.erase(
std::remove_if(tables().begin().getCurrent(), Tables.end(), [&](void *T) -> bool {
auto *ODT = Table::getFromOpaqueValue(T).template get<OnDiskTable*>();
return Files.count(ODT->File);
}), Tables.end());
PendingOverrides.clear();
}
void condense() {
MergedTable *Merged = getMergedTable();
if (!Merged)
Merged = new MergedTable;
// Read in all the tables and merge them together.
// FIXME: Be smarter about which tables we merge.
for (auto *ODT : tables()) {
auto &HT = ODT->Table;
Info &InfoObj = HT.getInfoObj();
for (auto I = HT.data_begin(), E = HT.data_end(); I != E; ++I) {
auto *LocalPtr = I.getItem();
// FIXME: Don't rely on the OnDiskHashTable format here.
auto L = InfoObj.ReadKeyDataLength(LocalPtr);
const internal_key_type &Key = InfoObj.ReadKey(LocalPtr, L.first);
InfoObj.ReadDataInto(Key, LocalPtr + L.first, L.second,
Merged->Data[Key]);
}
Merged->Files.push_back(ODT->File);
delete ODT;
}
Tables.clear();
Tables.push_back(Table(Merged).getOpaqueValue());
}
/// The generator is permitted to read our merged table.
template<typename ReaderInfo, typename WriterInfo>
friend class MultiOnDiskHashTableGenerator;
public:
MultiOnDiskHashTable() {}
MultiOnDiskHashTable(MultiOnDiskHashTable &&O)
: Tables(std::move(O.Tables)),
PendingOverrides(std::move(O.PendingOverrides)) {
O.Tables.clear();
}
MultiOnDiskHashTable &operator=(MultiOnDiskHashTable &&O) {
if (&O == this)
return *this;
clear();
Tables = std::move(O.Tables);
O.Tables.clear();
PendingOverrides = std::move(O.PendingOverrides);
return *this;
}
~MultiOnDiskHashTable() { clear(); }
/// \brief Add the table \p Data loaded from file \p File.
void add(file_type File, storage_type Data, Info InfoObj = Info()) {
using namespace llvm::support;
storage_type Ptr = Data;
uint32_t BucketOffset = endian::readNext<uint32_t, little, unaligned>(Ptr);
// Read the list of overridden files.
uint32_t NumFiles = endian::readNext<uint32_t, little, unaligned>(Ptr);
// FIXME: Add a reserve() to TinyPtrVector so that we don't need to make
// an additional copy.
llvm::SmallVector<file_type, 16> OverriddenFiles;
OverriddenFiles.reserve(NumFiles);
for (/**/; NumFiles != 0; --NumFiles)
OverriddenFiles.push_back(InfoObj.ReadFileRef(Ptr));
PendingOverrides.insert(PendingOverrides.end(), OverriddenFiles.begin(),
OverriddenFiles.end());
// Read the OnDiskChainedHashTable header.
storage_type Buckets = Data + BucketOffset;
auto NumBucketsAndEntries =
OnDiskTable::HashTable::readNumBucketsAndEntries(Buckets);
// Register the table.
Table NewTable = new OnDiskTable(File, NumBucketsAndEntries.first,
NumBucketsAndEntries.second,
Buckets, Ptr, Data, std::move(InfoObj));
Tables.push_back(NewTable.getOpaqueValue());
}
/// \brief Find and read the lookup results for \p EKey.
data_type find(const external_key_type &EKey) {
data_type Result;
if (!PendingOverrides.empty())
removeOverriddenTables();
if (Tables.size() > Info::MaxTables)
condense();
internal_key_type Key = Info::GetInternalKey(EKey);
auto KeyHash = Info::ComputeHash(Key);
if (MergedTable *M = getMergedTable()) {
auto It = M->Data.find(Key);
if (It != M->Data.end())
Result = It->second;
}
for (auto *ODT : tables()) {
auto &HT = ODT->Table;
auto It = HT.find_hashed(Key, KeyHash);
if (It != HT.end())
HT.getInfoObj().ReadDataInto(Key, It.getDataPtr(), It.getDataLen(),
Result);
}
return Result;
}
/// \brief Read all the lookup results into a single value. This only makes
/// sense if merging values across keys is meaningful.
data_type findAll() {
data_type Result;
if (!PendingOverrides.empty())
removeOverriddenTables();
if (MergedTable *M = getMergedTable()) {
for (auto &KV : M->Data)
Info::MergeDataInto(KV.second, Result);
}
for (auto *ODT : tables()) {
auto &HT = ODT->Table;
Info &InfoObj = HT.getInfoObj();
for (auto I = HT.data_begin(), E = HT.data_end(); I != E; ++I) {
auto *LocalPtr = I.getItem();
// FIXME: Don't rely on the OnDiskHashTable format here.
auto L = InfoObj.ReadKeyDataLength(LocalPtr);
const internal_key_type &Key = InfoObj.ReadKey(LocalPtr, L.first);
InfoObj.ReadDataInto(Key, LocalPtr + L.first, L.second, Result);
}
}
return Result;
}
};
/// \brief Writer for the on-disk hash table.
template<typename ReaderInfo, typename WriterInfo>
class MultiOnDiskHashTableGenerator {
typedef MultiOnDiskHashTable<ReaderInfo> BaseTable;
typedef llvm::OnDiskChainedHashTableGenerator<WriterInfo> Generator;
Generator Gen;
public:
MultiOnDiskHashTableGenerator() : Gen() {}
void insert(typename WriterInfo::key_type_ref Key,
typename WriterInfo::data_type_ref Data, WriterInfo &Info) {
Gen.insert(Key, Data, Info);
}
void emit(llvm::SmallVectorImpl<char> &Out, WriterInfo &Info,
const BaseTable *Base) {
using namespace llvm::support;
llvm::raw_svector_ostream OutStream(Out);
// Write our header information.
{
endian::Writer<little> Writer(OutStream);
// Reserve four bytes for the bucket offset.
Writer.write<uint32_t>(0);
if (auto *Merged = Base ? Base->getMergedTable() : nullptr) {
// Write list of overridden files.
Writer.write<uint32_t>(Merged->Files.size());
for (const auto &F : Merged->Files)
Info.EmitFileRef(OutStream, F);
// Add all merged entries from Base to the generator.
for (auto &KV : Merged->Data) {
if (!Gen.contains(KV.first, Info))
Gen.insert(KV.first, Info.ImportData(KV.second), Info);
}
} else {
Writer.write<uint32_t>(0);
}
}
// Write the table itself.
uint32_t BucketOffset = Gen.Emit(OutStream, Info);
// Replace the first four bytes with the bucket offset.
endian::write32le(Out.data(), BucketOffset);
}
};
} // end namespace clang::serialization
} // end namespace clang
#endif

View File

@ -29,14 +29,14 @@ void g() {
f<int>();
f(); // expected-error {{no matching function}}
// expected-note@Inputs/cxx-templates-b.h:3 {{couldn't infer template argument}}
// expected-note@Inputs/cxx-templates-b.h:4 {{requires single argument}}
// expected-note-re@Inputs/cxx-templates-a.h:4 {{requires {{single|1}} argument}}
N::f(0);
N::f<double>(1.0);
N::f<int>();
N::f(); // expected-error {{no matching function}}
// expected-note@Inputs/cxx-templates-b.h:6 {{couldn't infer template argument}}
// expected-note@Inputs/cxx-templates-b.h:7 {{requires single argument}}
// expected-note-re@Inputs/cxx-templates-a.h:7 {{requires {{single|1}} argument}}
template_param_kinds_1<0>(); // ok, from cxx-templates-a.h
template_param_kinds_1<int>(); // ok, from cxx-templates-b.h
@ -179,10 +179,14 @@ namespace Std {
// CHECK-GLOBAL: DeclarationName 'f'
// CHECK-GLOBAL-NEXT: |-FunctionTemplate {{.*}} 'f'
// CHECK-GLOBAL-NEXT: |-FunctionTemplate {{.*}} 'f'
// CHECK-GLOBAL-NEXT: |-FunctionTemplate {{.*}} 'f'
// CHECK-GLOBAL-NEXT: `-FunctionTemplate {{.*}} 'f'
// CHECK-NAMESPACE-N: DeclarationName 'f'
// CHECK-NAMESPACE-N-NEXT: |-FunctionTemplate {{.*}} 'f'
// CHECK-NAMESPACE-N-NEXT: |-FunctionTemplate {{.*}} 'f'
// CHECK-NAMESPACE-N-NEXT: |-FunctionTemplate {{.*}} 'f'
// CHECK-NAMESPACE-N-NEXT: `-FunctionTemplate {{.*}} 'f'
// CHECK-DUMP: ClassTemplateDecl {{.*}} <{{.*[/\\]}}cxx-templates-common.h:1:1, {{.*}}> col:{{.*}} in cxx_templates_common SomeTemplate

View File

@ -31,7 +31,9 @@ template int UseAll<YA>();
template int UseAll<YB>();
template int UseAll<Y>();
#if ORDER == 1
// Which of these two sets of diagnostics is chosen is not important. It's OK
// if this varies with ORDER, but it must be consistent across runs.
#if 1
// Here, we're instantiating the definition from 'A' and merging the definition
// from 'B' into it.