Skip to content

[C++20] [Modules] [Reduced BMI] Remove unreachable decls GMF in redued BMI #88359

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang/include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,10 @@ enum ASTRecordTypes {
/// Record code for an unterminated \#pragma clang assume_nonnull begin
/// recorded in a preamble.
PP_ASSUME_NONNULL_LOC = 67,

/// Record code for lexical and visible block for delayed namespace in
/// reduced BMI.
DELAYED_NAMESPACE_LEXICAL_VISIBLE_RECORD = 68,
};

/// Record types used within a source manager block.
Expand Down
14 changes: 14 additions & 0 deletions clang/include/clang/Serialization/ASTReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,20 @@ class ASTReader
/// in the chain.
DeclUpdateOffsetsMap DeclUpdateOffsets;

using DelayedNamespaceOffsetMapTy = llvm::DenseMap<
serialization::DeclID,
std::pair</*LexicalOffset*/ uint64_t, /*VisibleOffset*/ uint64_t>>;

/// Mapping from global declaration IDs to the lexical and visible block
/// offset for delayed namespace in reduced BMI.
///
/// We can't use the existing DeclUpdate mechanism since the DeclUpdate
/// may only be applied in an outer most read. However, we need to know
/// whether or not a DeclContext has external storage during the recursive
/// reading. So we need to apply the offset immediately after we read the
/// namespace as if it is not delayed.
DelayedNamespaceOffsetMapTy DelayedNamespaceOffsetMap;

struct PendingUpdateRecord {
Decl *D;
serialization::GlobalDeclID ID;
Expand Down
26 changes: 25 additions & 1 deletion clang/include/clang/Serialization/ASTWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,16 @@ class ASTWriter : public ASTDeserializationListener,
/// The declarations and types to emit.
std::queue<DeclOrType> DeclTypesToEmit;

/// The delayed namespace to emit. Only meaningful for reduced BMI.
///
/// In reduced BMI, we want to elide the unreachable declarations in
/// the global module fragment. However, in ASTWriterDecl, when we see
/// a namespace, all the declarations in the namespace would be emitted.
/// So the optimization become meaningless. To solve the issue, we
/// delay recording all the declarations until we emit all the declarations.
/// Then we can safely record the reached declarations only.
llvm::SmallVector<NamespaceDecl *, 16> DelayedNamespace;

/// The first ID number we can use for our own declarations.
serialization::DeclID FirstDeclID = serialization::NUM_PREDEF_DECL_IDS;

Expand Down Expand Up @@ -529,7 +539,8 @@ class ASTWriter : public ASTDeserializationListener,
void WriteType(QualType T);

bool isLookupResultExternal(StoredDeclsList &Result, DeclContext *DC);
bool isLookupResultEntirelyExternal(StoredDeclsList &Result, DeclContext *DC);
bool isLookupResultEntirelyExternalOrUnreachable(StoredDeclsList &Result,
DeclContext *DC);

void GenerateNameLookupTable(const DeclContext *DC,
llvm::SmallVectorImpl<char> &LookupTable);
Expand Down Expand Up @@ -704,6 +715,15 @@ class ASTWriter : public ASTDeserializationListener,
/// declaration.
serialization::DeclID getDeclID(const Decl *D);

/// Whether or not the declaration got emitted. If not, it wouldn't be
/// emitted.
///
/// This may only be called after we've done the job to write the
/// declarations (marked by DoneWritingDeclsAndTypes).
///
/// A declaration may only be omitted in reduced BMI.
bool wasDeclEmitted(const Decl *D) const;

unsigned getAnonymousDeclarationNumber(const NamedDecl *D);

/// Add a string to the given record.
Expand Down Expand Up @@ -798,6 +818,10 @@ class ASTWriter : public ASTDeserializationListener,
return WritingModule && WritingModule->isNamedModule();
}

bool isGeneratingReducedBMI() const { return GeneratingReducedBMI; }

bool getDoneWritingDeclsAndTypes() const { return DoneWritingDeclsAndTypes; }

private:
// ASTDeserializationListener implementation
void ReaderInitialized(ASTReader *Reader) override;
Expand Down
23 changes: 23 additions & 0 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3795,6 +3795,29 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
}
break;

case DELAYED_NAMESPACE_LEXICAL_VISIBLE_RECORD: {
if (Record.size() % 3 != 0)
return llvm::createStringError(
std::errc::illegal_byte_sequence,
"invalid DELAYED_NAMESPACE_LEXICAL_VISIBLE_RECORD block in AST "
"file");
for (unsigned I = 0, N = Record.size(); I != N; I += 3) {
GlobalDeclID ID = getGlobalDeclID(F, Record[I]);

uint64_t BaseOffset = F.DeclsBlockStartOffset;
assert(BaseOffset && "Invalid DeclsBlockStartOffset for module file!");
uint64_t LexicalOffset = Record[I + 1] ? BaseOffset + Record[I + 1] : 0;
uint64_t VisibleOffset = Record[I + 2] ? BaseOffset + Record[I + 2] : 0;

DelayedNamespaceOffsetMap[ID] = {LexicalOffset, VisibleOffset};

assert(!GetExistingDecl(ID) &&
"We shouldn't load the namespace in the front of delayed "
"namespace lexical and visible block");
}
break;
}

case OBJC_CATEGORIES_MAP:
if (F.LocalNumObjCCategoriesInMap != 0)
return llvm::createStringError(
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4124,6 +4124,15 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) {
// offsets for its tables of lexical and visible declarations.
if (auto *DC = dyn_cast<DeclContext>(D)) {
std::pair<uint64_t, uint64_t> Offsets = Reader.VisitDeclContext(DC);

// Get the lexical and visible block for the delayed namespace.
// It is sufficient to judge if ID is in DelayedNamespaceOffsetMap.
// But it may be more efficient to filter the other cases.
if (!Offsets.first && !Offsets.second && isa<NamespaceDecl>(D))
if (auto Iter = DelayedNamespaceOffsetMap.find(ID);
Iter != DelayedNamespaceOffsetMap.end())
Offsets = Iter->second;

if (Offsets.first &&
ReadLexicalDeclContextStorage(*Loc.F, DeclsCursor, Offsets.first, DC))
return nullptr;
Expand Down
139 changes: 117 additions & 22 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,7 @@ void ASTWriter::WriteBlockInfoBlock() {
RECORD(WEAK_UNDECLARED_IDENTIFIERS);
RECORD(PENDING_IMPLICIT_INSTANTIATIONS);
RECORD(UPDATE_VISIBLE);
RECORD(DELAYED_NAMESPACE_LEXICAL_VISIBLE_RECORD);
RECORD(DECL_UPDATE_OFFSETS);
RECORD(DECL_UPDATES);
RECORD(CUDA_SPECIAL_DECL_REFS);
Expand Down Expand Up @@ -3029,10 +3030,12 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) {
Stream.EmitRecordWithBlob(ConfigMacroAbbrev, Record, CM);
}

// Emit the initializers, if any.
// Emit the reachable initializers.
// The initializer may only be unreachable in reduced BMI.
RecordData Inits;
for (Decl *D : Context->getModuleInitializers(Mod))
Inits.push_back(GetDeclRef(D));
if (wasDeclEmitted(D))
Inits.push_back(GetDeclRef(D));
if (!Inits.empty())
Stream.EmitRecord(SUBMODULE_INITIALIZERS, Inits);

Expand Down Expand Up @@ -3211,6 +3214,9 @@ uint64_t ASTWriter::WriteDeclContextLexicalBlock(ASTContext &Context,
uint64_t Offset = Stream.GetCurrentBitNo();
SmallVector<uint32_t, 128> KindDeclPairs;
for (const auto *D : DC->decls()) {
if (DoneWritingDeclsAndTypes && !wasDeclEmitted(D))
continue;

KindDeclPairs.push_back(D->getKind());
KindDeclPairs.push_back(GetDeclRef(D));
}
Expand Down Expand Up @@ -3865,8 +3871,14 @@ class ASTDeclContextNameLookupTrait {
data_type getData(const Coll &Decls) {
unsigned Start = DeclIDs.size();
for (NamedDecl *D : Decls) {
DeclIDs.push_back(
Writer.GetDeclRef(getDeclForLocalLookup(Writer.getLangOpts(), D)));
NamedDecl *DeclForLocalLookup =
getDeclForLocalLookup(Writer.getLangOpts(), D);

if (Writer.getDoneWritingDeclsAndTypes() &&
!Writer.wasDeclEmitted(DeclForLocalLookup))
continue;

DeclIDs.push_back(Writer.GetDeclRef(DeclForLocalLookup));
}
return std::make_pair(Start, DeclIDs.size());
}
Expand Down Expand Up @@ -3975,11 +3987,20 @@ bool ASTWriter::isLookupResultExternal(StoredDeclsList &Result,
DC->hasNeedToReconcileExternalVisibleStorage();
}

bool ASTWriter::isLookupResultEntirelyExternal(StoredDeclsList &Result,
DeclContext *DC) {
for (auto *D : Result.getLookupResult())
if (!getDeclForLocalLookup(getLangOpts(), D)->isFromASTFile())
return false;
bool ASTWriter::isLookupResultEntirelyExternalOrUnreachable(
StoredDeclsList &Result, DeclContext *DC) {
for (auto *D : Result.getLookupResult()) {
auto *LocalD = getDeclForLocalLookup(getLangOpts(), D);
if (LocalD->isFromASTFile())
continue;

// We can only be sure whether the local declaration is reachable
// after we done writing the declarations and types.
if (DoneWritingDeclsAndTypes && !wasDeclEmitted(LocalD))
continue;

return false;
}

return true;
}
Expand Down Expand Up @@ -4017,8 +4038,17 @@ ASTWriter::GenerateNameLookupTable(const DeclContext *ConstDC,
// don't need to write an entry for the name at all. If we can't
// write out a lookup set without performing more deserialization,
// just skip this entry.
if (isLookupResultExternal(Result, DC) &&
isLookupResultEntirelyExternal(Result, DC))
//
// Also in reduced BMI, we'd like to avoid writing unreachable
// declarations in GMF, so we need to avoid writing declarations
// that entirely external or unreachable.
//
// FIMXE: It looks sufficient to test
// isLookupResultEntirelyExternalOrUnreachable here. But due to bug we have
// to test isLookupResultExternal here. See
// https://github.com/llvm/llvm-project/issues/61065 for details.
if ((GeneratingReducedBMI || isLookupResultExternal(Result, DC)) &&
isLookupResultEntirelyExternalOrUnreachable(Result, DC))
continue;

// We also skip empty results. If any of the results could be external and
Expand Down Expand Up @@ -4209,9 +4239,15 @@ uint64_t ASTWriter::WriteDeclContextVisibleBlock(ASTContext &Context,
continue;
}

for (NamedDecl *ND : Result)
if (!ND->isFromASTFile())
GetDeclRef(ND);
for (NamedDecl *ND : Result) {
if (ND->isFromASTFile())
continue;

if (DoneWritingDeclsAndTypes && !wasDeclEmitted(ND))
continue;

GetDeclRef(ND);
}
}

return 0;
Expand Down Expand Up @@ -4979,9 +5015,18 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot,
const TranslationUnitDecl *TU = Context.getTranslationUnitDecl();

// Force all top level declarations to be emitted.
for (const auto *D : TU->noload_decls())
if (!D->isFromASTFile())
GetDeclRef(D);
//
// We start emitting top level declarations from the module purview to
// implement the eliding unreachable declaration feature.
for (const auto *D : TU->noload_decls()) {
if (D->isFromASTFile())
continue;

if (GeneratingReducedBMI && D->isFromExplicitGlobalModule())
continue;

GetDeclRef(D);
}

// If the translation unit has an anonymous namespace, and we don't already
// have an update block for it, write it as an update block.
Expand Down Expand Up @@ -5291,24 +5336,59 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) {
WriteDecl(Context, DOT.getDecl());
}
} while (!DeclUpdates.empty());
Stream.ExitBlock();

DoneWritingDeclsAndTypes = true;

// DelayedNamespace is only meaningful in reduced BMI.
// See the comments of DelayedNamespace for details.
assert(DelayedNamespace.empty() || GeneratingReducedBMI);
RecordData DelayedNamespaceRecord;
for (NamespaceDecl *NS : DelayedNamespace) {
uint64_t LexicalOffset = WriteDeclContextLexicalBlock(Context, NS);
uint64_t VisibleOffset = WriteDeclContextVisibleBlock(Context, NS);

// Write the offset relative to current block.
if (LexicalOffset)
LexicalOffset -= DeclTypesBlockStartOffset;

if (VisibleOffset)
VisibleOffset -= DeclTypesBlockStartOffset;

DelayedNamespaceRecord.push_back(getDeclID(NS));
DelayedNamespaceRecord.push_back(LexicalOffset);
DelayedNamespaceRecord.push_back(VisibleOffset);
}

// The process of writing lexical and visible block for delayed namespace
// shouldn't introduce any new decls, types or update to emit.
assert(DeclTypesToEmit.empty());
assert(DeclUpdates.empty());

Stream.ExitBlock();

// These things can only be done once we've written out decls and types.
WriteTypeDeclOffsets();
if (!DeclUpdatesOffsetsRecord.empty())
Stream.EmitRecord(DECL_UPDATE_OFFSETS, DeclUpdatesOffsetsRecord);

if (!DelayedNamespaceRecord.empty())
Stream.EmitRecord(DELAYED_NAMESPACE_LEXICAL_VISIBLE_RECORD,
DelayedNamespaceRecord);

const TranslationUnitDecl *TU = Context.getTranslationUnitDecl();
// Create a lexical update block containing all of the declarations in the
// translation unit that do not come from other AST files.
SmallVector<uint32_t, 128> NewGlobalKindDeclPairs;
for (const auto *D : TU->noload_decls()) {
if (!D->isFromASTFile()) {
NewGlobalKindDeclPairs.push_back(D->getKind());
NewGlobalKindDeclPairs.push_back(GetDeclRef(D));
}
if (D->isFromASTFile())
continue;

// In reduced BMI, skip unreached declarations.
if (!wasDeclEmitted(D))
continue;

NewGlobalKindDeclPairs.push_back(D->getKind());
NewGlobalKindDeclPairs.push_back(GetDeclRef(D));
}

auto Abv = std::make_shared<llvm::BitCodeAbbrev>();
Expand Down Expand Up @@ -5817,6 +5897,21 @@ DeclID ASTWriter::getDeclID(const Decl *D) {
return DeclIDs[D];
}

bool ASTWriter::wasDeclEmitted(const Decl *D) const {
assert(D);

assert(DoneWritingDeclsAndTypes &&
"wasDeclEmitted should only be called after writing declarations");

if (D->isFromASTFile())
return true;

bool Emitted = DeclIDs.contains(D);
assert((Emitted || GeneratingReducedBMI) &&
"The declaration can only be omitted in reduced BMI.");
return Emitted;
}

void ASTWriter::associateDeclWithFile(const Decl *D, DeclID ID) {
assert(ID);
assert(D);
Expand Down
27 changes: 25 additions & 2 deletions clang/lib/Serialization/ASTWriterDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1719,6 +1719,15 @@ void ASTDeclWriter::VisitClassTemplateDecl(ClassTemplateDecl *D) {

if (D->isFirstDecl())
AddTemplateSpecializations(D);

// Force emitting the corresponding deduction guide in reduced BMI mode.
// Otherwise, the deduction guide may be optimized out incorrectly.
if (Writer.isGeneratingReducedBMI()) {
auto Name = Context.DeclarationNames.getCXXDeductionGuideName(D);
for (auto *DG : D->getDeclContext()->noload_lookup(Name))
Writer.GetDeclRef(DG);
}

Code = serialization::DECL_CLASS_TEMPLATE;
}

Expand Down Expand Up @@ -1962,8 +1971,22 @@ void ASTDeclWriter::VisitDeclContext(DeclContext *DC) {
"You need to update the serializer after you change the "
"DeclContextBits");

Record.AddOffset(Writer.WriteDeclContextLexicalBlock(Context, DC));
Record.AddOffset(Writer.WriteDeclContextVisibleBlock(Context, DC));
uint64_t LexicalOffset = 0;
uint64_t VisibleOffset = 0;

if (Writer.isGeneratingReducedBMI() && isa<NamespaceDecl>(DC) &&
cast<NamespaceDecl>(DC)->isFromExplicitGlobalModule()) {
// In reduced BMI, delay writing lexical and visible block for namespace
// in the global module fragment. See the comments of DelayedNamespace for
// details.
Writer.DelayedNamespace.push_back(cast<NamespaceDecl>(DC));
} else {
LexicalOffset = Writer.WriteDeclContextLexicalBlock(Context, DC);
VisibleOffset = Writer.WriteDeclContextVisibleBlock(Context, DC);
}

Record.AddOffset(LexicalOffset);
Record.AddOffset(VisibleOffset);
}

const Decl *ASTWriter::getFirstLocalDecl(const Decl *D) {
Expand Down
Loading