}
} // 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
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
/// \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
struct PendingVisibleUpdate {
ModuleFile *Mod;
const unsigned char *Data;
- unsigned BucketOffset;
};
typedef SmallVector<PendingVisibleUpdate, 1> DeclContextVisibleUpdates;
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;
/// 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.
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();
unsigned getExprImplicitCastAbbrev() const { return ExprImplicitCastAbbrev; }
bool hasChain() const { return Chain; }
+ ASTReader *getChain() const { return Chain; }
// ASTDeserializationListener implementation
void ReaderInitialized(ASTReader *Reader) override;
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.
/// 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;
#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"
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;
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,
// 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;
}
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))
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;
- 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);
+ auto It = Lookups.find(DC);
+ assert(It != Lookups.end() &&
+ "have external visible storage but no lookup tables");
- 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])));
- }
+ DeclsMap Decls;
+
+ 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) {
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
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;
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);
}
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);
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);
}
#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 {
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;
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;
}
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
#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"
// 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;
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) {
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);
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");
}
};
return true;
}
-uint32_t
+void
ASTWriter::GenerateNameLookupTable(const DeclContext *ConstDC,
llvm::SmallVectorImpl<char> &LookupTable) {
assert(!ConstDC->HasLazyLocalLexicalLookups &&
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
switch (Name.getNameKind()) {
default:
- Generator.insert(Name, Result, Trait);
+ Generator.insert(Name, Trait.getData(Result), Trait);
break;
case DeclarationName::CXXConstructorName:
// 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
// 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;
// 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.
RecordData Record;
Record.push_back(UPDATE_VISIBLE);
Record.push_back(getDeclID(cast<Decl>(DC)));
- Record.push_back(BucketOffset);
Stream.EmitRecordWithBlob(UpdateVisibleAbbrev, Record, LookupTable);
}
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);
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);
}
{}
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);
--- /dev/null
+//===--- 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
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
// 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
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.