}
} // 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(s) used for DeclContext name lookup.
- struct DeclContextLookupTable;
+ /// \brief The on-disk hash table used for the DeclContext's Name lookup table.
+ typedef llvm::OnDiskIterableChainedHashTable<ASTDeclContextNameLookupTrait>
+ ASTDeclContextNameLookupTable;
}
} // 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);
- void GenerateNameLookupTable(const DeclContext *DC,
- llvm::SmallVectorImpl<char> &LookupTable);
+ uint32_t 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);
}
-void ASTDeclContextNameLookupTrait::ReadDataInto(internal_key_type,
- const unsigned char *d,
- unsigned DataLen,
- data_type_builder &Val) {
+ASTDeclContextNameLookupTrait::data_type
+ASTDeclContextNameLookupTrait::ReadData(internal_key_type,
+ const unsigned char *d,
+ unsigned DataLen) {
using namespace llvm::support;
- for (unsigned NumDecls = DataLen / 4; NumDecls; --NumDecls) {
- uint32_t LocalID = endian::readNext<uint32_t, little, unaligned>(d);
- Val.insert(Reader.getGlobalDeclID(F, LocalID));
- }
+ unsigned NumDecls = DataLen / 4;
+ LE32DeclID *Start = reinterpret_cast<LE32DeclID *>(
+ const_cast<unsigned char *>(d));
+ return std::make_pair(Start, Start + NumDecls);
}
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.
- auto *Data = (const unsigned char*)Blob.data();
- PendingVisibleUpdates[ID].push_back(PendingVisibleUpdate{&M, Data});
+ unsigned BucketOffset = Record[0];
+ PendingVisibleUpdates[ID].push_back(PendingVisibleUpdate{
+ &M, (const unsigned char *)Blob.data(), BucketOffset});
return false;
}
unsigned Idx = 0;
serialization::DeclID ID = ReadDeclID(F, Record, Idx);
auto *Data = (const unsigned char*)Blob.data();
- PendingVisibleUpdates[ID].push_back(PendingVisibleUpdate{&F, Data});
+ unsigned BucketOffset = Record[Idx++];
+ PendingVisibleUpdates[ID].push_back(
+ PendingVisibleUpdate{&F, Data, BucketOffset});
// 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() && DC == DC->getPrimaryContext() &&
+ assert(DC->hasExternalVisibleStorage() &&
"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;
- for (DeclID ID : It->second.Table.find(Name)) {
- NamedDecl *ND = cast<NamedDecl>(GetDecl(ID));
- if (ND->getDeclName() == Name)
- Decls.push_back(ND);
- }
+ 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);
++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;
- for (DeclID ID : It->second.Table.findAll()) {
- NamedDecl *ND = cast<NamedDecl>(GetDecl(ID));
- Decls[ND->getDeclName()].push_back(ND);
+ // 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])));
+ }
}
+ 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())
- return ((F->BaseSubmoduleID + NUM_PREDEF_SUBMODULE_IDS) << 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)
- Lookups[DC].Table.add(
- Update.Mod, Update.Data,
+ 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,
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:
- // 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.
- typedef llvm::SmallVector<DeclID, 4> data_type;
- struct data_type_builder {
- data_type &Data;
- llvm::DenseSet<DeclID> Found;
-
- data_type_builder(data_type &D) : Data(D) {}
- void insert(DeclID ID) {
- // Just use a linear scan unless we have more than a few IDs.
- if (Found.empty() && !Data.empty()) {
- if (Data.size() <= 4) {
- for (auto I : Found)
- if (I == ID)
- return;
- Data.push_back(ID);
- return;
- }
-
- // Switch to tracking found IDs in the set.
- Found.insert(Data.begin(), Data.end());
- }
-
- if (Found.insert(ID).second)
- Data.push_back(ID);
- }
- };
+ /// \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;
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);
- void ReadDataInto(internal_key_type, const unsigned char *d,
- unsigned DataLen, data_type_builder &Val);
-
- static void MergeDataInto(const data_type &From, data_type_builder &To) {
- To.Data.reserve(To.Data.size() + From.size());
- for (DeclID ID : From)
- To.insert(ID);
- }
-
- file_type ReadFileRef(const unsigned char *&d);
-};
-
-struct DeclContextLookupTable {
- MultiOnDiskHashTable<ASTDeclContextNameLookupTrait> Table;
+ data_type ReadData(internal_key_type, const unsigned char *d,
+ unsigned DataLen);
};
/// \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;
- /// A start and end index into DeclIDs, representing a sequence of decls.
- typedef std::pair<unsigned, unsigned> data_type;
+ typedef DeclContext::lookup_result 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.second - Lookup.first);
- assert(uint16_t(DataLen) == DataLen &&
- "too many decls for serialized lookup result");
+ unsigned DataLen = 4 * Lookup.size();
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 (unsigned I = Lookup.first, N = Lookup.second; I != N; ++I)
- LE.write<uint32_t>(DeclIDs[I]);
+ for (DeclContext::lookup_iterator I = Lookup.begin(), E = Lookup.end();
+ I != E; ++I)
+ LE.write<uint32_t>(
+ Writer.GetDeclRef(getDeclForLocalLookup(Writer.getLangOpts(), *I)));
+
assert(Out.tell() - Start == DataLen && "Data length is wrong");
}
};
return true;
}
-void
+uint32_t
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.
- MultiOnDiskHashTableGenerator<reader::ASTDeclContextNameLookupTrait,
- ASTDeclContextNameLookupTrait> Generator;
+ llvm::OnDiskChainedHashTableGenerator<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, Trait.getData(Result), Trait);
+ Generator.insert(Name, Result, Trait);
break;
case DeclarationName::CXXConstructorName:
// the key, only the kind of name is used.
if (!ConstructorDecls.empty())
Generator.insert(ConstructorDecls.front()->getDeclName(),
- Trait.getData(ConstructorDecls), Trait);
+ DeclContext::lookup_result(ConstructorDecls), Trait);
if (!ConversionDecls.empty())
Generator.insert(ConversionDecls.front()->getDeclName(),
- Trait.getData(ConversionDecls), Trait);
+ DeclContext::lookup_result(ConversionDecls), 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);
+ // 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);
}
/// \brief Write the block containing all of the declaration IDs
// Create the on-disk hash table in a buffer.
SmallString<4096> LookupTable;
- GenerateNameLookupTable(DC, LookupTable);
+ uint32_t BucketOffset = 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;
- GenerateNameLookupTable(DC, LookupTable);
+ uint32_t BucketOffset = 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 typename Info::data_type_builder data_type_builder;
- 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);
- data_type_builder ValueBuilder(Merged->Data[Key]);
- InfoObj.ReadDataInto(Key, LocalPtr + L.first, L.second,
- ValueBuilder);
- }
-
- 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;
- }
-
- data_type_builder ResultBuilder(Result);
-
- 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(),
- ResultBuilder);
- }
-
- 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;
- data_type_builder ResultBuilder(Result);
-
- if (!PendingOverrides.empty())
- removeOverriddenTables();
-
- if (MergedTable *M = getMergedTable()) {
- for (auto &KV : M->Data)
- Info::MergeDataInto(KV.second, ResultBuilder);
- }
-
- 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, ResultBuilder);
- }
- }
-
- 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<double>(1.0);
f<int>();
f(); // expected-error {{no matching function}}
- // expected-note@Inputs/cxx-templates-a.h:3 {{couldn't infer template argument}}
- // expected-note@Inputs/cxx-templates-a.h:4 {{requires 1 argument}}
+ // expected-note@Inputs/cxx-templates-b.h:3 {{couldn't infer template argument}}
+ // expected-note@Inputs/cxx-templates-b.h:4 {{requires single argument}}
N::f(0);
N::f<double>(1.0);
// 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>();
-// 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 ORDER == 1
// Here, we're instantiating the definition from 'A' and merging the definition
// from 'B' into it.