Skip to content

Commit c8d3e93

Browse files
committed
[Modules] Record whether VarDecl initializers contain side effects (llvm#143739)
Calling `DeclMustBeEmitted` should not lead to more deserialization, as it may occur before previous deserialization has finished. When passed a `VarDecl` with an initializer however, `DeclMustBeEmitted` needs to know whether that initializer contains side effects. When the `VarDecl` is deserialized but the initializer is not, this triggers deserialization of the initializer. To avoid this we add a bit to the serialization format for `VarDecl`s, indicating whether its initializer contains side effects or not, so that the `ASTReader` can query this information directly without deserializing the initializer. rdar://153085264 This is a cherry-pick of 319a51a
1 parent 2dff8f3 commit c8d3e93

File tree

11 files changed

+116
-9
lines changed

11 files changed

+116
-9
lines changed

clang/include/clang/AST/Decl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,6 +1339,11 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
13391339
return const_cast<VarDecl *>(this)->getInitializingDeclaration();
13401340
}
13411341

1342+
/// Checks whether this declaration has an initializer with side effects,
1343+
/// without triggering deserialization if the initializer is not yet
1344+
/// deserialized.
1345+
bool hasInitWithSideEffects() const;
1346+
13421347
/// Determine whether this variable's value might be usable in a
13431348
/// constant expression, according to the relevant language standard.
13441349
/// This only checks properties of the declaration, and does not check

clang/include/clang/AST/ExternalASTSource.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class RecordDecl;
5151
class Selector;
5252
class Stmt;
5353
class TagDecl;
54+
class VarDecl;
5455

5556
/// Abstract interface for external sources of AST nodes.
5657
///
@@ -173,6 +174,10 @@ class ExternalASTSource : public RefCountedBase<ExternalASTSource> {
173174

174175
virtual ExtKind hasExternalDefinitions(const Decl *D);
175176

177+
virtual bool hasInitializerWithSideEffects(const VarDecl *VD) const {
178+
return false;
179+
}
180+
176181
/// Finds all declarations lexically contained within the given
177182
/// DeclContext, after applying an optional filter predicate.
178183
///
@@ -407,6 +412,17 @@ struct LazyOffsetPtr {
407412
return GetPtr();
408413
}
409414

415+
/// Retrieve the pointer to the AST node that this lazy pointer points to,
416+
/// if it can be done without triggering deserialization.
417+
///
418+
/// \returns a pointer to the AST node, or null if not yet deserialized.
419+
T *getWithoutDeserializing() const {
420+
if (isOffset()) {
421+
return nullptr;
422+
}
423+
return GetPtr();
424+
}
425+
410426
/// Retrieve the address of the AST node pointer. Deserializes the pointee if
411427
/// necessary.
412428
T **getAddressOfPointer(ExternalASTSource *Source) const {

clang/include/clang/Sema/MultiplexExternalSemaSource.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ class MultiplexExternalSemaSource : public ExternalSemaSource {
9292

9393
ExtKind hasExternalDefinitions(const Decl *D) override;
9494

95+
bool hasInitializerWithSideEffects(const VarDecl *VD) const override;
96+
9597
/// Find all declarations with the given name in the
9698
/// given context.
9799
bool FindExternalVisibleDeclsByName(const DeclContext *DC,

clang/include/clang/Serialization/ASTReader.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,6 +1348,12 @@ class ASTReader
13481348
const StringRef &operator*() && = delete;
13491349
};
13501350

1351+
/// VarDecls with initializers containing side effects must be emitted,
1352+
/// but DeclMustBeEmitted is not allowed to deserialize the intializer.
1353+
/// FIXME: Lower memory usage by removing VarDecls once the initializer
1354+
/// is deserialized.
1355+
llvm::SmallPtrSet<Decl *, 16> InitSideEffectVars;
1356+
13511357
public:
13521358
/// Get the buffer for resolving paths.
13531359
SmallString<0> &getPathBuf() { return PathBuf; }
@@ -2263,6 +2269,8 @@ class ASTReader
22632269

22642270
ExtKind hasExternalDefinitions(const Decl *D) override;
22652271

2272+
bool hasInitializerWithSideEffects(const VarDecl *VD) const override;
2273+
22662274
/// Retrieve a selector from the given module with its local ID
22672275
/// number.
22682276
Selector getLocalSelector(ModuleFile &M, unsigned LocalID);

clang/lib/AST/ASTContext.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13262,9 +13262,7 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
1326213262
return true;
1326313263

1326413264
// Variables that have initialization with side-effects are required.
13265-
if (VD->getInit() && VD->getInit()->HasSideEffects(*this) &&
13266-
// We can get a value-dependent initializer during error recovery.
13267-
(VD->getInit()->isValueDependent() || !VD->evaluateValue()))
13265+
if (VD->hasInitWithSideEffects())
1326813266
return true;
1326913267

1327013268
// Likewise, variables with tuple-like bindings are required if their

clang/lib/AST/Decl.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2440,6 +2440,30 @@ VarDecl *VarDecl::getInitializingDeclaration() {
24402440
return Def;
24412441
}
24422442

2443+
bool VarDecl::hasInitWithSideEffects() const {
2444+
if (!hasInit())
2445+
return false;
2446+
2447+
// Check if we can get the initializer without deserializing
2448+
const Expr *E = nullptr;
2449+
if (auto *S = dyn_cast<Stmt *>(Init)) {
2450+
E = cast<Expr>(S);
2451+
} else {
2452+
E = cast_or_null<Expr>(getEvaluatedStmt()->Value.getWithoutDeserializing());
2453+
}
2454+
2455+
if (E)
2456+
return E->HasSideEffects(getASTContext()) &&
2457+
// We can get a value-dependent initializer during error recovery.
2458+
(E->isValueDependent() || !evaluateValue());
2459+
2460+
assert(getEvaluatedStmt()->Value.isOffset());
2461+
// ASTReader tracks this without having to deserialize the initializer
2462+
if (auto Source = getASTContext().getExternalSource())
2463+
return Source->hasInitializerWithSideEffects(this);
2464+
return false;
2465+
}
2466+
24432467
bool VarDecl::isOutOfLine() const {
24442468
if (Decl::isOutOfLine())
24452469
return true;

clang/lib/Sema/MultiplexExternalSemaSource.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,14 @@ MultiplexExternalSemaSource::hasExternalDefinitions(const Decl *D) {
107107
return EK_ReplyHazy;
108108
}
109109

110+
bool MultiplexExternalSemaSource::hasInitializerWithSideEffects(
111+
const VarDecl *VD) const {
112+
for (const auto &S : Sources)
113+
if (S->hasInitializerWithSideEffects(VD))
114+
return true;
115+
return false;
116+
}
117+
110118
bool MultiplexExternalSemaSource::
111119
FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name) {
112120
bool AnyDeclsFound = false;

clang/lib/Serialization/ASTReader.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9268,6 +9268,10 @@ ExternalASTSource::ExtKind ASTReader::hasExternalDefinitions(const Decl *FD) {
92689268
return I->second ? EK_Never : EK_Always;
92699269
}
92709270

9271+
bool ASTReader::hasInitializerWithSideEffects(const VarDecl *VD) const {
9272+
return InitSideEffectVars.count(VD);
9273+
}
9274+
92719275
Selector ASTReader::getLocalSelector(ModuleFile &M, unsigned LocalID) {
92729276
return DecodeSelector(getGlobalSelectorID(M, LocalID));
92739277
}

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,6 +1622,9 @@ ASTDeclReader::RedeclarableResult ASTDeclReader::VisitVarDeclImpl(VarDecl *VD) {
16221622
VD->NonParmVarDeclBits.PreviousDeclInSameBlockScope =
16231623
VarDeclBits.getNextBit();
16241624

1625+
if (VarDeclBits.getNextBit())
1626+
Reader.InitSideEffectVars.insert(VD);
1627+
16251628
VD->NonParmVarDeclBits.EscapingByref = VarDeclBits.getNextBit();
16261629
HasDeducedType = VarDeclBits.getNextBit();
16271630
VD->NonParmVarDeclBits.ImplicitParamKind =

clang/lib/Serialization/ASTWriterDecl.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) {
11581158
VarDeclBits.addBit(D->isConstexpr());
11591159
VarDeclBits.addBit(D->isInitCapture());
11601160
VarDeclBits.addBit(D->isPreviousDeclInSameBlockScope());
1161+
VarDeclBits.addBit(D->hasInitWithSideEffects());
11611162

11621163
VarDeclBits.addBit(D->isEscapingByref());
11631164
HasDeducedType = D->getType()->getContainedDeducedType();
@@ -1207,10 +1208,11 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) {
12071208
!D->hasExtInfo() && D->getFirstDecl() == D->getMostRecentDecl() &&
12081209
D->getKind() == Decl::Var && !D->isInline() && !D->isConstexpr() &&
12091210
!D->isInitCapture() && !D->isPreviousDeclInSameBlockScope() &&
1210-
!D->isEscapingByref() && !HasDeducedType &&
1211-
D->getStorageDuration() != SD_Static && !D->getDescribedVarTemplate() &&
1212-
!D->getMemberSpecializationInfo() && !D->isObjCForDecl() &&
1213-
!isa<ImplicitParamDecl>(D) && !D->isEscapingByref())
1211+
!D->hasInitWithSideEffects() && !D->isEscapingByref() &&
1212+
!HasDeducedType && D->getStorageDuration() != SD_Static &&
1213+
!D->getDescribedVarTemplate() && !D->getMemberSpecializationInfo() &&
1214+
!D->isObjCForDecl() && !isa<ImplicitParamDecl>(D) &&
1215+
!D->isEscapingByref())
12141216
AbbrevToUse = Writer.getDeclVarAbbrev();
12151217

12161218
Code = serialization::DECL_VAR;
@@ -2529,12 +2531,12 @@ void ASTWriter::WriteDeclAbbrevs() {
25292531
// VarDecl
25302532
Abv->Add(BitCodeAbbrevOp(
25312533
BitCodeAbbrevOp::Fixed,
2532-
21)); // Packed Var Decl bits: Linkage, ModulesCodegen,
2534+
22)); // Packed Var Decl bits: Linkage, ModulesCodegen,
25332535
// SClass, TSCSpec, InitStyle,
25342536
// isARCPseudoStrong, IsThisDeclarationADemotedDefinition,
25352537
// isExceptionVariable, isNRVOVariable, isCXXForRangeDecl,
25362538
// isInline, isInlineSpecified, isConstexpr,
2537-
// isInitCapture, isPrevDeclInSameScope,
2539+
// isInitCapture, isPrevDeclInSameScope, hasInitWithSideEffects,
25382540
// EscapingByref, HasDeducedType, ImplicitParamKind, isObjCForDecl
25392541
Abv->Add(BitCodeAbbrevOp(0)); // VarKind (local enum)
25402542
// Type Source Info
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Tests referencing variable with initializer containing side effect across module boundary
2+
//
3+
// RUN: rm -rf %t
4+
// RUN: mkdir -p %t
5+
// RUN: split-file %s %t
6+
7+
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/Foo.cppm \
8+
// RUN: -o %t/Foo.pcm
9+
10+
// RUN: %clang_cc1 -std=c++20 -emit-module-interface \
11+
// RUN: -fmodule-file=Foo=%t/Foo.pcm \
12+
// RUN: %t/Bar.cppm \
13+
// RUN: -o %t/Bar.pcm
14+
15+
// RUN: %clang_cc1 -std=c++20 -emit-obj \
16+
// RUN: -main-file-name Bar.cppm \
17+
// RUN: -fmodule-file=Foo=%t/Foo.pcm \
18+
// RUN: -x pcm %t/Bar.pcm \
19+
// RUN: -o %t/Bar.o
20+
21+
//--- Foo.cppm
22+
export module Foo;
23+
24+
export {
25+
class S {};
26+
27+
inline S s = S{};
28+
}
29+
30+
//--- Bar.cppm
31+
export module Bar;
32+
import Foo;
33+
34+
S bar() {
35+
return s;
36+
}
37+

0 commit comments

Comments
 (0)