Skip to content

Commit a744d3e

Browse files
authored
Merge pull request #68760 from tshortli/requestify-function-body-skipping
AST/SILGen: Requestify function body skipping
2 parents 5ced53e + 1d8fc10 commit a744d3e

17 files changed

+239
-116
lines changed

include/swift/AST/Decl.h

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -450,10 +450,13 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
450450
SWIFT_INLINE_BITFIELD(SubscriptDecl, VarDecl, 2,
451451
StaticSpelling : 2
452452
);
453-
SWIFT_INLINE_BITFIELD(AbstractFunctionDecl, ValueDecl, 3+2+8+1+1+1+1+1+1+1,
453+
SWIFT_INLINE_BITFIELD(AbstractFunctionDecl, ValueDecl, 3+2+2+8+1+1+1+1+1+1+1,
454454
/// \see AbstractFunctionDecl::BodyKind
455455
BodyKind : 3,
456456

457+
/// \see AbstractFunctionDecl::BodySkippedStatus
458+
BodySkippedStatus : 2,
459+
457460
/// \see AbstractFunctionDecl::SILSynthesizeKind
458461
SILSynthesizeKind : 2,
459462

@@ -6857,9 +6860,6 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
68576860
/// Function body is parsed and available as an AST subtree.
68586861
Parsed,
68596862

6860-
/// Function body is not available, although it was written in the source.
6861-
Skipped,
6862-
68636863
/// Function body will be synthesized on demand.
68646864
Synthesize,
68656865

@@ -6875,6 +6875,14 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
68756875
// This enum currently needs to fit in a 3-bit bitfield.
68766876
};
68776877

6878+
enum class BodySkippedStatus {
6879+
Unknown,
6880+
Skipped,
6881+
NotSkipped,
6882+
6883+
// This enum needs to fit in a 2-bit bitfield.
6884+
};
6885+
68786886
BodyKind getBodyKind() const {
68796887
return BodyKind(Bits.AbstractFunctionDecl.BodyKind);
68806888
}
@@ -6926,13 +6934,13 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
69266934

69276935
/// The location of the function body when the body is delayed or skipped.
69286936
///
6929-
/// This enum member is active if getBodyKind() is BodyKind::Unparsed or
6930-
/// BodyKind::Skipped.
6937+
/// This enum member is active if getBodyKind() is BodyKind::Unparsed.
69316938
SourceRange BodyRange;
69326939
};
69336940

69346941
friend class ParseAbstractFunctionBodyRequest;
69356942
friend class TypeCheckFunctionBodyRequest;
6943+
friend class IsFunctionBodySkippedRequest;
69366944

69376945
CaptureInfo Captures;
69386946

@@ -6970,6 +6978,14 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
69706978
Bits.AbstractFunctionDecl.BodyKind = unsigned(K);
69716979
}
69726980

6981+
BodySkippedStatus getBodySkippedStatus() const {
6982+
return BodySkippedStatus(Bits.AbstractFunctionDecl.BodySkippedStatus);
6983+
}
6984+
6985+
void setBodySkippedStatus(BodySkippedStatus status) {
6986+
Bits.AbstractFunctionDecl.BodySkippedStatus = unsigned(status);
6987+
}
6988+
69736989
void setSILSynthesizeKind(SILSynthesizeKind K) {
69746990
Bits.AbstractFunctionDecl.SILSynthesizeKind = unsigned(K);
69756991
}
@@ -7088,10 +7104,7 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
70887104
///
70897105
/// Note that a true return value does not imply that the body was actually
70907106
/// parsed.
7091-
bool hasBody() const {
7092-
return getBodyKind() != BodyKind::None &&
7093-
getBodyKind() != BodyKind::Skipped;
7094-
}
7107+
bool hasBody() const { return getBodyKind() != BodyKind::None; }
70957108

70967109
/// Returns true if the text of this function's body can be retrieved either
70977110
/// by extracting the text from the source buffer or reading the inlinable
@@ -7113,21 +7126,6 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
71137126
/// Set a new body for the function.
71147127
void setBody(BraceStmt *S, BodyKind NewBodyKind);
71157128

7116-
/// Note that the body was skipped for this function. Function body
7117-
/// cannot be attached after this call.
7118-
void setBodySkipped(SourceRange bodyRange) {
7119-
// FIXME: Remove 'Parsed' from this list once we can always delay
7120-
// parsing bodies. The -experimental-skip-*-function-bodies options
7121-
// do currently skip parsing, unless disabled through other means in
7122-
// SourceFile::hasDelayedBodyParsing.
7123-
assert(getBodyKind() == BodyKind::None ||
7124-
getBodyKind() == BodyKind::Unparsed ||
7125-
getBodyKind() == BodyKind::Parsed);
7126-
assert(bodyRange.isValid());
7127-
BodyRange = bodyRange;
7128-
setBodyKind(BodyKind::Skipped);
7129-
}
7130-
71317129
/// Note that parsing for the body was delayed.
71327130
void setBodyDelayed(SourceRange bodyRange) {
71337131
assert(getBodyKind() == BodyKind::None);
@@ -7205,9 +7203,9 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
72057203
return getBodyKind() == BodyKind::SILSynthesize;
72067204
}
72077205

7208-
bool isBodySkipped() const {
7209-
return getBodyKind() == BodyKind::Skipped;
7210-
}
7206+
/// Indicates whether the body of this function is skipped during
7207+
/// typechecking.
7208+
bool isBodySkipped() const;
72117209

72127210
bool isMemberwiseInitializer() const {
72137211
return getBodyKind() == BodyKind::SILSynthesize

include/swift/AST/TypeCheckRequests.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4423,6 +4423,25 @@ class SerializeAttrGenericSignatureRequest
44234423
void cacheResult(GenericSignature signature) const;
44244424
};
44254425

4426+
class IsFunctionBodySkippedRequest
4427+
: public SimpleRequest<IsFunctionBodySkippedRequest,
4428+
bool(const AbstractFunctionDecl *),
4429+
RequestFlags::SeparatelyCached> {
4430+
public:
4431+
using SimpleRequest::SimpleRequest;
4432+
4433+
private:
4434+
friend SimpleRequest;
4435+
4436+
bool evaluate(Evaluator &evaluator, const AbstractFunctionDecl *) const;
4437+
4438+
public:
4439+
// Separate caching.
4440+
bool isCached() const { return true; }
4441+
llvm::Optional<bool> getCachedResult() const;
4442+
void cacheResult(bool isSkipped) const;
4443+
};
4444+
44264445
#define SWIFT_TYPEID_ZONE TypeChecker
44274446
#define SWIFT_TYPEID_HEADER "swift/AST/TypeCheckerTypeIDZone.def"
44284447
#include "swift/Basic/DefineTypeIDZone.h"

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,3 +503,6 @@ SWIFT_REQUEST(TypeChecker, ExpandChildTypeRefinementContextsRequest,
503503
SWIFT_REQUEST(TypeChecker, SerializeAttrGenericSignatureRequest,
504504
GenericSignature(Decl *, SpecializeAttr *),
505505
SeparatelyCached, NoLocationInfo)
506+
SWIFT_REQUEST(TypeChecker, IsFunctionBodySkippedRequest,
507+
bool(const AbstractFunctionDecl *),
508+
SeparatelyCached, NoLocationInfo)

lib/AST/ASTVerifier.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,6 @@ class Verifier : public ASTWalker {
496496
switch (afd->getBodyKind()) {
497497
case AbstractFunctionDecl::BodyKind::None:
498498
case AbstractFunctionDecl::BodyKind::TypeChecked:
499-
case AbstractFunctionDecl::BodyKind::Skipped:
500499
case AbstractFunctionDecl::BodyKind::SILSynthesize:
501500
case AbstractFunctionDecl::BodyKind::Deserialized:
502501
return true;

lib/AST/Decl.cpp

Lines changed: 88 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8934,9 +8934,6 @@ BraceStmt *AbstractFunctionDecl::getTypecheckedBody() const {
89348934
}
89358935

89368936
void AbstractFunctionDecl::setBody(BraceStmt *S, BodyKind NewBodyKind) {
8937-
assert(getBodyKind() != BodyKind::Skipped &&
8938-
"cannot set a body if it was skipped");
8939-
89408937
llvm::Optional<Fingerprint> fp = llvm::None;
89418938
if (getBodyKind() == BodyKind::TypeChecked ||
89428939
getBodyKind() == BodyKind::Parsed) {
@@ -8952,6 +8949,72 @@ void AbstractFunctionDecl::setBody(BraceStmt *S, BodyKind NewBodyKind) {
89528949
}
89538950
}
89548951

8952+
bool AbstractFunctionDecl::isBodySkipped() const {
8953+
return evaluateOrDefault(getASTContext().evaluator,
8954+
IsFunctionBodySkippedRequest{this}, false);
8955+
}
8956+
8957+
/// Determines whether typechecking can be skipped for a function body. Bodies
8958+
/// are skipped as a performance optimization when an
8959+
/// `-experimental-skip-*-function-bodies` flag is specified and the body meets
8960+
/// the criteria for skipping. If a body is skipped during typechecking, it is
8961+
/// also skipped during SILGen. Some bodies cannot be skipped, even when they
8962+
/// otherwise meet the criteria, because typechecking them has essential
8963+
/// side-effects that are required for correctness of the AST.
8964+
bool IsFunctionBodySkippedRequest::evaluate(
8965+
Evaluator &evaluator, const AbstractFunctionDecl *afd) const {
8966+
auto &Ctx = afd->getASTContext();
8967+
auto skippingMode = Ctx.TypeCheckerOpts.SkipFunctionBodies;
8968+
if (skippingMode == FunctionBodySkipping::None)
8969+
return false;
8970+
8971+
// Functions that have been synthesized for clang modules will be serialized
8972+
// because they have shared linkage.
8973+
if (isa<ClangModuleUnit>(afd->getDeclContext()->getModuleScopeContext()))
8974+
return false;
8975+
8976+
if (auto *accessor = dyn_cast<AccessorDecl>(afd)) {
8977+
// didSet accessors needs to be checked to determine whether to keep their
8978+
// parameters.
8979+
if (accessor->getAccessorKind() == AccessorKind::DidSet)
8980+
return false;
8981+
8982+
// Synthesized accessors with forced static dispatch are emitted on-demand
8983+
// and are serialized. Since they are serialized we must be willing to
8984+
// typecheck them.
8985+
if (accessor->hasForcedStaticDispatch())
8986+
return false;
8987+
8988+
if (auto *varDecl = dyn_cast<VarDecl>(accessor->getStorage())) {
8989+
// FIXME: If we don't typecheck the synthesized accessors of lazy storage
8990+
// properties then SILGen crashes when emitting the initializer.
8991+
if (varDecl->getAttrs().hasAttribute<LazyAttr>() && accessor->isSynthesized())
8992+
return false;
8993+
}
8994+
}
8995+
8996+
// Actor initializers need to be checked to determine delegation status.
8997+
if (auto *ctor = dyn_cast<ConstructorDecl>(afd))
8998+
if (auto *nom = ctor->getParent()->getSelfNominalTypeDecl())
8999+
if (nom->isAnyActor())
9000+
return false;
9001+
9002+
// Skipping all bodies won't serialize anything, so we can skip everything
9003+
// else.
9004+
if (skippingMode == FunctionBodySkipping::All)
9005+
return true;
9006+
9007+
// If we want all types (for LLDB) then we can't skip functions with nested
9008+
// types. We could probably improve upon this and type-check only the nested
9009+
// types instead for better performances.
9010+
if (afd->hasNestedTypeDeclarations() &&
9011+
skippingMode == FunctionBodySkipping::NonInlinableWithoutTypes)
9012+
return false;
9013+
9014+
// Skip functions that don't need to be serialized.
9015+
return afd->getResilienceExpansion() != ResilienceExpansion::Minimal;
9016+
}
9017+
89559018
void AbstractFunctionDecl::setBodyToBeReparsed(SourceRange bodyRange) {
89569019
assert(bodyRange.isValid());
89579020
assert(getBodyKind() == BodyKind::Unparsed ||
@@ -8986,7 +9049,6 @@ SourceRange AbstractFunctionDecl::getBodySourceRange() const {
89869049

89879050
return SourceRange();
89889051

8989-
case BodyKind::Skipped:
89909052
case BodyKind::Unparsed:
89919053
return BodyRange;
89929054
}
@@ -9396,7 +9458,6 @@ bool AbstractFunctionDecl::hasInlinableBodyText() const {
93969458

93979459
case BodyKind::None:
93989460
case BodyKind::Synthesize:
9399-
case BodyKind::Skipped:
94009461
case BodyKind::SILSynthesize:
94019462
return false;
94029463
}
@@ -9847,8 +9908,7 @@ SourceRange FuncDecl::getSourceRange() const {
98479908
if (StartLoc.isInvalid())
98489909
return SourceRange();
98499910

9850-
if (getBodyKind() == BodyKind::Unparsed ||
9851-
getBodyKind() == BodyKind::Skipped)
9911+
if (getBodyKind() == BodyKind::Unparsed)
98529912
return { StartLoc, BodyRange.End };
98539913

98549914
SourceLoc RBraceLoc = getOriginalBodySourceRange().End;
@@ -10530,7 +10590,6 @@ ParseAbstractFunctionBodyRequest::getCachedResult() const {
1053010590
case BodyKind::Deserialized:
1053110591
case BodyKind::SILSynthesize:
1053210592
case BodyKind::None:
10533-
case BodyKind::Skipped:
1053410593
return BodyAndFingerprint{};
1053510594

1053610595
case BodyKind::TypeChecked:
@@ -10552,7 +10611,6 @@ void ParseAbstractFunctionBodyRequest::cacheResult(
1055210611
case BodyKind::Deserialized:
1055310612
case BodyKind::SILSynthesize:
1055410613
case BodyKind::None:
10555-
case BodyKind::Skipped:
1055610614
// The body is always empty, so don't cache anything.
1055710615
assert(!value.getFingerprint().has_value() && value.getBody() == nullptr);
1055810616
return;
@@ -10569,6 +10627,27 @@ void ParseAbstractFunctionBodyRequest::cacheResult(
1056910627
}
1057010628
}
1057110629

10630+
llvm::Optional<bool> IsFunctionBodySkippedRequest::getCachedResult() const {
10631+
using BodySkippedStatus = AbstractFunctionDecl::BodySkippedStatus;
10632+
auto afd = std::get<0>(getStorage());
10633+
switch (afd->getBodySkippedStatus()) {
10634+
case BodySkippedStatus::Unknown:
10635+
return llvm::None;
10636+
case BodySkippedStatus::Skipped:
10637+
return true;
10638+
case BodySkippedStatus::NotSkipped:
10639+
return false;
10640+
}
10641+
llvm_unreachable("bad BodySkippedStatus");
10642+
}
10643+
10644+
void IsFunctionBodySkippedRequest::cacheResult(bool isSkipped) const {
10645+
using BodySkippedStatus = AbstractFunctionDecl::BodySkippedStatus;
10646+
auto afd = std::get<0>(getStorage());
10647+
const_cast<AbstractFunctionDecl *>(afd)->setBodySkippedStatus(
10648+
isSkipped ? BodySkippedStatus::Skipped : BodySkippedStatus::NotSkipped);
10649+
}
10650+
1057210651
void swift::simple_display(llvm::raw_ostream &out, BodyAndFingerprint value) {
1057310652
out << "(";
1057410653
simple_display(out, value.getBody());

lib/AST/TypeCheckRequests.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1457,11 +1457,13 @@ llvm::Optional<BraceStmt *>
14571457
TypeCheckFunctionBodyRequest::getCachedResult() const {
14581458
using BodyKind = AbstractFunctionDecl::BodyKind;
14591459
auto *afd = std::get<0>(getStorage());
1460+
if (afd->isBodySkipped())
1461+
return nullptr;
1462+
14601463
switch (afd->getBodyKind()) {
14611464
case BodyKind::Deserialized:
14621465
case BodyKind::SILSynthesize:
14631466
case BodyKind::None:
1464-
case BodyKind::Skipped:
14651467
// These cases don't have any body available.
14661468
return nullptr;
14671469

lib/Parse/ParseRequests.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ ParseAbstractFunctionBodyRequest::evaluate(Evaluator &evaluator,
9191
case BodyKind::Deserialized:
9292
case BodyKind::SILSynthesize:
9393
case BodyKind::None:
94-
case BodyKind::Skipped:
9594
return {};
9695

9796
case BodyKind::TypeChecked:

0 commit comments

Comments
 (0)