aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Serialization/ASTWriter.cpp
diff options
context:
space:
mode:
authorChuanqi Xu <yedeng.yd@linux.alibaba.com>2025-01-15 15:15:35 +0800
committerGitHub <noreply@github.com>2025-01-15 15:15:35 +0800
commit7201cae106260aeb3e9bbbb7d5291ff30f05076a (patch)
tree531fa26f453c70f4f29cce0dd1103d234f4ee37a /clang/lib/Serialization/ASTWriter.cpp
parentedc02351dd11cc4a39b7c541b26b71c6f36c8e55 (diff)
downloadllvm-7201cae106260aeb3e9bbbb7d5291ff30f05076a.zip
llvm-7201cae106260aeb3e9bbbb7d5291ff30f05076a.tar.gz
llvm-7201cae106260aeb3e9bbbb7d5291ff30f05076a.tar.bz2
[C++20] [Modules] Support module level lookup (#122887)
Close https://github.com/llvm/llvm-project/issues/90154 This patch is also an optimization to the lookup process to utilize the information provided by `export` keyword. Previously, in the lookup process, the `export` keyword only takes part in the check part, it doesn't get involved in the lookup process. That said, previously, in a name lookup for 'name', we would load all of declarations with the name 'name' and check if these declarations are valid or not. It works well. But it is inefficient since it may load declarations that may not be wanted. Note that this patch actually did a trick in the lookup process instead of bring module information to DeclarationName or considering module information when deciding if two declarations are the same. So it may not be a surprise to me if there are missing cases. But it is not a regression. It should be already the case. Issue reports are welcomed. In this patch, I tried to split the big lookup table into a lookup table as before and a module local lookup table, which takes a combination of the ID of the DeclContext and hash value of the primary module name as the key. And refactored `DeclContext::lookup()` method to take the module information. So that a lookup in a DeclContext won't load declarations that are local to **other** modules. And also I think it is already beneficial to split the big lookup table since it may reduce the conflicts during lookups in the hash table. BTW, this patch introduced a **regression** for a reachability rule in C++20 but it was false-negative. See 'clang/test/CXX/module/module.interface/p7.cpp' for details. This patch is not expected to introduce any other regressions for non-c++20-modules users since the module local lookup table should be empty for them. --- On the API side, this patch unfortunately add a maybe-confusing argument `Module *NamedModule` to `ExternalASTSource::FindExternalVisibleDeclsByName()`. People may think we can get the information from the first argument `const DeclContext *DC`. But sadly there are declarations (e.g., namespace) can appear in multiple different modules as a single declaration. So we have to add additional information to indicate this.
Diffstat (limited to 'clang/lib/Serialization/ASTWriter.cpp')
-rw-r--r--clang/lib/Serialization/ASTWriter.cpp273
1 files changed, 213 insertions, 60 deletions
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 345d496..a6f8c60 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -1088,6 +1088,7 @@ void ASTWriter::WriteBlockInfoBlock() {
RECORD(DECL_BLOCK);
RECORD(DECL_CONTEXT_LEXICAL);
RECORD(DECL_CONTEXT_VISIBLE);
+ RECORD(DECL_CONTEXT_MODULE_LOCAL_VISIBLE);
RECORD(DECL_NAMESPACE);
RECORD(DECL_NAMESPACE_ALIAS);
RECORD(DECL_USING);
@@ -4024,15 +4025,13 @@ void ASTWriter::handleVTable(CXXRecordDecl *RD) {
namespace {
-// Trait used for the on-disk hash table used in the method pool.
-class ASTDeclContextNameLookupTrait {
+class ASTDeclContextNameLookupTraitBase {
+protected:
ASTWriter &Writer;
- llvm::SmallVector<LocalDeclID, 64> DeclIDs;
+ using DeclIDsTy = llvm::SmallVector<LocalDeclID, 64>;
+ DeclIDsTy DeclIDs;
public:
- using key_type = DeclarationNameKey;
- using key_type_ref = key_type;
-
/// A start and end index into DeclIDs, representing a sequence of decls.
using data_type = std::pair<unsigned, unsigned>;
using data_type_ref = const data_type &;
@@ -4040,31 +4039,11 @@ public:
using hash_value_type = unsigned;
using offset_type = unsigned;
- explicit ASTDeclContextNameLookupTrait(ASTWriter &Writer) : Writer(Writer) {}
-
- template<typename Coll>
- data_type getData(const Coll &Decls) {
- unsigned Start = DeclIDs.size();
- for (NamedDecl *D : Decls) {
- NamedDecl *DeclForLocalLookup =
- getDeclForLocalLookup(Writer.getLangOpts(), D);
-
- if (Writer.getDoneWritingDeclsAndTypes() &&
- !Writer.wasDeclEmitted(DeclForLocalLookup))
- continue;
-
- // Try to avoid writing internal decls to reduced BMI.
- // See comments in ASTWriter::WriteDeclContextLexicalBlock for details.
- if (Writer.isGeneratingReducedBMI() &&
- !DeclForLocalLookup->isFromExplicitGlobalModule() &&
- IsInternalDeclFromFileContext(DeclForLocalLookup))
- continue;
-
- DeclIDs.push_back(Writer.GetDeclRef(DeclForLocalLookup));
- }
- return std::make_pair(Start, DeclIDs.size());
- }
+protected:
+ explicit ASTDeclContextNameLookupTraitBase(ASTWriter &Writer)
+ : Writer(Writer) {}
+public:
data_type ImportData(const reader::ASTDeclContextNameLookupTrait::data_type &FromReader) {
unsigned Start = DeclIDs.size();
DeclIDs.insert(
@@ -4074,14 +4053,6 @@ public:
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?");
@@ -4092,9 +4063,9 @@ public:
llvm::endianness::little);
}
- std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &Out,
- DeclarationNameKey Name,
- data_type_ref Lookup) {
+ std::pair<unsigned, unsigned> EmitKeyDataLengthBase(raw_ostream &Out,
+ DeclarationNameKey Name,
+ data_type_ref Lookup) {
unsigned KeyLen = 1;
switch (Name.getKind()) {
case DeclarationName::Identifier:
@@ -4120,10 +4091,10 @@ public:
// length of DeclIDs.
unsigned DataLen = sizeof(DeclID) * (Lookup.second - Lookup.first);
- return emitULEBKeyDataLength(KeyLen, DataLen, Out);
+ return {KeyLen, DataLen};
}
- void EmitKey(raw_ostream &Out, DeclarationNameKey Name, unsigned) {
+ void EmitKeyBase(raw_ostream &Out, DeclarationNameKey Name) {
using namespace llvm::support;
endian::Writer LE(Out, llvm::endianness::little);
@@ -4154,8 +4125,7 @@ public:
llvm_unreachable("Invalid name kind?");
}
- void EmitData(raw_ostream &Out, key_type_ref, data_type Lookup,
- unsigned DataLen) {
+ void EmitDataBase(raw_ostream &Out, data_type Lookup, unsigned DataLen) {
using namespace llvm::support;
endian::Writer LE(Out, llvm::endianness::little);
@@ -4166,6 +4136,129 @@ public:
}
};
+class ModuleLocalNameLookupTrait : public ASTDeclContextNameLookupTraitBase {
+public:
+ using primary_module_hash_type = unsigned;
+
+ using key_type = std::pair<DeclarationNameKey, primary_module_hash_type>;
+ using key_type_ref = key_type;
+
+ explicit ModuleLocalNameLookupTrait(ASTWriter &Writer)
+ : ASTDeclContextNameLookupTraitBase(Writer) {}
+
+ data_type getData(const DeclIDsTy &LocalIDs) {
+ unsigned Start = DeclIDs.size();
+ for (auto ID : LocalIDs)
+ 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(key_type Key) {
+ llvm::FoldingSetNodeID ID;
+ ID.AddInteger(Key.first.getHash());
+ ID.AddInteger(Key.second);
+ return ID.computeStableHash();
+ }
+
+ std::pair<unsigned, unsigned>
+ EmitKeyDataLength(raw_ostream &Out, key_type Key, data_type_ref Lookup) {
+ auto [KeyLen, DataLen] = EmitKeyDataLengthBase(Out, Key.first, Lookup);
+ KeyLen += sizeof(Key.second);
+ return emitULEBKeyDataLength(KeyLen, DataLen, Out);
+ }
+
+ void EmitKey(raw_ostream &Out, key_type Key, unsigned) {
+ EmitKeyBase(Out, Key.first);
+ llvm::support::endian::Writer LE(Out, llvm::endianness::little);
+ LE.write<primary_module_hash_type>(Key.second);
+ }
+
+ void EmitData(raw_ostream &Out, key_type_ref, data_type Lookup,
+ unsigned DataLen) {
+ EmitDataBase(Out, Lookup, DataLen);
+ }
+};
+
+// Trait used for the on-disk hash table used in the method pool.
+class ASTDeclContextNameLookupTrait : public ASTDeclContextNameLookupTraitBase {
+public:
+ using ModuleLocalDeclsMapTy =
+ llvm::DenseMap<ModuleLocalNameLookupTrait::key_type, DeclIDsTy>;
+
+private:
+ ModuleLocalDeclsMapTy ModuleLocalDeclsMap;
+
+public:
+ using key_type = DeclarationNameKey;
+ using key_type_ref = key_type;
+
+ explicit ASTDeclContextNameLookupTrait(ASTWriter &Writer)
+ : ASTDeclContextNameLookupTraitBase(Writer) {}
+
+ template <typename Coll> data_type getData(const Coll &Decls) {
+ unsigned Start = DeclIDs.size();
+ for (NamedDecl *D : Decls) {
+ NamedDecl *DeclForLocalLookup =
+ getDeclForLocalLookup(Writer.getLangOpts(), D);
+
+ if (Writer.getDoneWritingDeclsAndTypes() &&
+ !Writer.wasDeclEmitted(DeclForLocalLookup))
+ continue;
+
+ // Try to avoid writing internal decls to reduced BMI.
+ // See comments in ASTWriter::WriteDeclContextLexicalBlock for details.
+ if (Writer.isGeneratingReducedBMI() &&
+ !DeclForLocalLookup->isFromExplicitGlobalModule() &&
+ IsInternalDeclFromFileContext(DeclForLocalLookup))
+ continue;
+
+ auto ID = Writer.GetDeclRef(DeclForLocalLookup);
+
+ if (D->getFormalLinkage() == Linkage::Module) {
+ if (std::optional<unsigned> PrimaryModuleHash =
+ getPrimaryModuleHash(D->getOwningModule())) {
+ auto Key = std::make_pair(D->getDeclName(), *PrimaryModuleHash);
+ auto Iter = ModuleLocalDeclsMap.find(Key);
+ if (Iter == ModuleLocalDeclsMap.end())
+ ModuleLocalDeclsMap.insert({Key, DeclIDsTy{ID}});
+ else
+ Iter->second.push_back(ID);
+ continue;
+ }
+ }
+
+ DeclIDs.push_back(ID);
+ }
+ return std::make_pair(Start, DeclIDs.size());
+ }
+
+ const ModuleLocalDeclsMapTy &getModuleLocalDecls() {
+ return ModuleLocalDeclsMap;
+ }
+
+ static bool EqualKey(key_type_ref a, key_type_ref b) { return a == b; }
+
+ hash_value_type ComputeHash(key_type Name) { return Name.getHash(); }
+
+ std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &Out,
+ DeclarationNameKey Name,
+ data_type_ref Lookup) {
+ auto [KeyLen, DataLen] = EmitKeyDataLengthBase(Out, Name, Lookup);
+ return emitULEBKeyDataLength(KeyLen, DataLen, Out);
+ }
+
+ void EmitKey(raw_ostream &Out, DeclarationNameKey Name, unsigned) {
+ return EmitKeyBase(Out, Name);
+ }
+
+ void EmitData(raw_ostream &Out, key_type_ref, data_type Lookup,
+ unsigned DataLen) {
+ EmitDataBase(Out, Lookup, DataLen);
+ }
+};
+
} // namespace
namespace {
@@ -4371,7 +4464,8 @@ static bool isLookupResultNotInteresting(ASTWriter &Writer,
void ASTWriter::GenerateNameLookupTable(
ASTContext &Context, const DeclContext *ConstDC,
- llvm::SmallVectorImpl<char> &LookupTable) {
+ llvm::SmallVectorImpl<char> &LookupTable,
+ llvm::SmallVectorImpl<char> &ModuleLocalLookupTable) {
assert(!ConstDC->hasLazyLocalLexicalLookups() &&
!ConstDC->hasLazyExternalLexicalLookups() &&
"must call buildLookups first");
@@ -4553,6 +4647,28 @@ void ASTWriter::GenerateNameLookupTable(
// merged table if there is one.
auto *Lookups = Chain ? Chain->getLoadedLookupTables(DC) : nullptr;
Generator.emit(LookupTable, Trait, Lookups ? &Lookups->Table : nullptr);
+
+ const auto &ModuleLocalDecls = Trait.getModuleLocalDecls();
+ if (ModuleLocalDecls.empty())
+ return;
+
+ MultiOnDiskHashTableGenerator<reader::ModuleLocalNameLookupTrait,
+ ModuleLocalNameLookupTrait>
+ ModuleLocalLookupGenerator;
+ ModuleLocalNameLookupTrait ModuleLocalTrait(*this);
+
+ for (const auto &ModuleLocalIter : ModuleLocalDecls) {
+ const auto &Key = ModuleLocalIter.first;
+ const auto &IDs = ModuleLocalIter.second;
+ ModuleLocalLookupGenerator.insert(Key, ModuleLocalTrait.getData(IDs),
+ ModuleLocalTrait);
+ }
+
+ auto *ModuleLocalLookups =
+ Chain ? Chain->getModuleLocalLookupTables(DC) : nullptr;
+ ModuleLocalLookupGenerator.emit(
+ ModuleLocalLookupTable, ModuleLocalTrait,
+ ModuleLocalLookups ? &ModuleLocalLookups->Table : nullptr);
}
/// Write the block containing all of the declaration IDs
@@ -4560,8 +4676,10 @@ void ASTWriter::GenerateNameLookupTable(
///
/// \returns the offset of the DECL_CONTEXT_VISIBLE block within the
/// bitstream, or 0 if no block was written.
-uint64_t ASTWriter::WriteDeclContextVisibleBlock(ASTContext &Context,
- DeclContext *DC) {
+void ASTWriter::WriteDeclContextVisibleBlock(ASTContext &Context,
+ DeclContext *DC,
+ uint64_t &VisibleBlockOffset,
+ uint64_t &ModuleLocalBlockOffset) {
// If we imported a key declaration of this namespace, write the visible
// lookup results as an update record for it rather than including them
// on this declaration. We will only look at key declarations on reload.
@@ -4571,7 +4689,7 @@ uint64_t ASTWriter::WriteDeclContextVisibleBlock(ASTContext &Context,
for (auto *Prev = cast<NamespaceDecl>(DC)->getPreviousDecl(); Prev;
Prev = Prev->getPreviousDecl())
if (!Prev->isFromASTFile())
- return 0;
+ return;
// Note that we need to emit an update record for the primary context.
UpdatedDeclContexts.insert(DC->getPrimaryContext());
@@ -4620,41 +4738,53 @@ uint64_t ASTWriter::WriteDeclContextVisibleBlock(ASTContext &Context,
}
}
- return 0;
+ return;
}
if (DC->getPrimaryContext() != DC)
- return 0;
+ return;
// Skip contexts which don't support name lookup.
if (!DC->isLookupContext())
- return 0;
+ return;
// If not in C++, we perform name lookup for the translation unit via the
// IdentifierInfo chains, don't bother to build a visible-declarations table.
if (DC->isTranslationUnit() && !Context.getLangOpts().CPlusPlus)
- return 0;
+ return;
// Serialize the contents of the mapping used for lookup. Note that,
// although we have two very different code paths, the serialized
// representation is the same for both cases: a declaration name,
// followed by a size, followed by references to the visible
// declarations that have that name.
- uint64_t Offset = Stream.GetCurrentBitNo();
StoredDeclsMap *Map = DC->buildLookup();
if (!Map || Map->empty())
- return 0;
+ return;
+ VisibleBlockOffset = Stream.GetCurrentBitNo();
// Create the on-disk hash table in a buffer.
SmallString<4096> LookupTable;
- GenerateNameLookupTable(Context, DC, LookupTable);
+ SmallString<4096> ModuleLocalLookupTable;
+ GenerateNameLookupTable(Context, DC, LookupTable, ModuleLocalLookupTable);
// Write the lookup table
RecordData::value_type Record[] = {DECL_CONTEXT_VISIBLE};
Stream.EmitRecordWithBlob(DeclContextVisibleLookupAbbrev, Record,
LookupTable);
++NumVisibleDeclContexts;
- return Offset;
+
+ if (ModuleLocalLookupTable.empty())
+ return;
+
+ ModuleLocalBlockOffset = Stream.GetCurrentBitNo();
+ assert(ModuleLocalBlockOffset > VisibleBlockOffset);
+ // Write the lookup table
+ RecordData::value_type ModuleLocalRecord[] = {
+ DECL_CONTEXT_MODULE_LOCAL_VISIBLE};
+ Stream.EmitRecordWithBlob(DeclModuleLocalVisibleLookupAbbrev,
+ ModuleLocalRecord, ModuleLocalLookupTable);
+ ++NumModuleLocalDeclContexts;
}
/// Write an UPDATE_VISIBLE block for the given context.
@@ -4671,7 +4801,8 @@ void ASTWriter::WriteDeclContextVisibleUpdate(ASTContext &Context,
// Create the on-disk hash table in a buffer.
SmallString<4096> LookupTable;
- GenerateNameLookupTable(Context, DC, LookupTable);
+ SmallString<4096> ModuleLocalLookupTable;
+ GenerateNameLookupTable(Context, DC, LookupTable, ModuleLocalLookupTable);
// 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.
@@ -4682,6 +4813,15 @@ void ASTWriter::WriteDeclContextVisibleUpdate(ASTContext &Context,
RecordData::value_type Record[] = {UPDATE_VISIBLE,
getDeclID(cast<Decl>(DC)).getRawValue()};
Stream.EmitRecordWithBlob(UpdateVisibleAbbrev, Record, LookupTable);
+
+ if (ModuleLocalLookupTable.empty())
+ return;
+
+ // Write the module local lookup table
+ RecordData::value_type ModuleLocalRecord[] = {
+ UPDATE_MODULE_LOCAL_VISIBLE, getDeclID(cast<Decl>(DC)).getRawValue()};
+ Stream.EmitRecordWithBlob(ModuleLocalUpdateVisibleAbbrev, ModuleLocalRecord,
+ ModuleLocalLookupTable);
}
/// Write an FP_PRAGMA_OPTIONS block for the given FPOptions.
@@ -5865,7 +6005,8 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema *SemaPtr, StringRef isysroot,
// Some simple statistics
RecordData::value_type Record[] = {
- NumStatements, NumMacros, NumLexicalDeclContexts, NumVisibleDeclContexts};
+ NumStatements, NumMacros, NumLexicalDeclContexts, NumVisibleDeclContexts,
+ NumModuleLocalDeclContexts};
Stream.EmitRecord(STATISTICS, Record);
Stream.ExitBlock();
Stream.FlushToWord();
@@ -5942,7 +6083,9 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) {
RecordData DelayedNamespaceRecord;
for (NamespaceDecl *NS : DelayedNamespace) {
uint64_t LexicalOffset = WriteDeclContextLexicalBlock(Context, NS);
- uint64_t VisibleOffset = WriteDeclContextVisibleBlock(Context, NS);
+ uint64_t VisibleOffset = 0;
+ uint64_t ModuleLocalOffset = 0;
+ WriteDeclContextVisibleBlock(Context, NS, VisibleOffset, ModuleLocalOffset);
// Write the offset relative to current block.
if (LexicalOffset)
@@ -5951,9 +6094,13 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) {
if (VisibleOffset)
VisibleOffset -= DeclTypesBlockStartOffset;
+ if (ModuleLocalOffset)
+ ModuleLocalOffset -= DeclTypesBlockStartOffset;
+
AddDeclRef(NS, DelayedNamespaceRecord);
DelayedNamespaceRecord.push_back(LexicalOffset);
DelayedNamespaceRecord.push_back(VisibleOffset);
+ DelayedNamespaceRecord.push_back(ModuleLocalOffset);
}
// The process of writing lexical and visible block for delayed namespace
@@ -6033,6 +6180,12 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) {
Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob));
UpdateVisibleAbbrev = Stream.EmitAbbrev(std::move(Abv));
+ Abv = std::make_shared<llvm::BitCodeAbbrev>();
+ Abv->Add(llvm::BitCodeAbbrevOp(UPDATE_MODULE_LOCAL_VISIBLE));
+ Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::VBR, 6));
+ Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob));
+ ModuleLocalUpdateVisibleAbbrev = Stream.EmitAbbrev(std::move(Abv));
+
// And a visible updates block for the translation unit.
WriteDeclContextVisibleUpdate(Context, TU);