Skip to content

Commit c5268e2

Browse files
authored
Merge pull request #61351 from hamishknight/undercover
2 parents a14b5fd + 0b25742 commit c5268e2

File tree

10 files changed

+122
-142
lines changed

10 files changed

+122
-142
lines changed

include/swift/SIL/SILDeclRef.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -483,9 +483,6 @@ struct SILDeclRef {
483483
return result;
484484
}
485485

486-
/// True is the decl ref references any kind of thunk.
487-
bool isAnyThunk() const;
488-
489486
/// True if the decl ref references a thunk from a natively foreign
490487
/// declaration to Swift calling convention.
491488
bool isForeignToNativeThunk() const;

include/swift/SIL/SILProfiler.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ class SILCoverageMap;
3131
class SILFunction;
3232
class SILModule;
3333

34-
/// Returns whether the given AST node requires profiling instrumentation.
35-
bool doesASTRequireProfiling(SILModule &M, ASTNode N, SILDeclRef Constant);
36-
3734
/// SILProfiler - Maps AST nodes to profile counters.
3835
class SILProfiler : public SILAllocated<SILProfiler> {
3936
private:

lib/SIL/IR/SILDeclRef.cpp

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,25 @@ bool SILDeclRef::hasUserWrittenCode() const {
313313
// user code into their body.
314314
switch (kind) {
315315
case Kind::Func: {
316+
if (getAbstractClosureExpr()) {
317+
// Auto-closures have user-written code.
318+
if (auto *ACE = getAutoClosureExpr()) {
319+
// Currently all types of auto-closures can contain user code. Note this
320+
// logic does not affect delayed emission, as we eagerly emit all
321+
// closure definitions. This does however affect profiling.
322+
switch (ACE->getThunkKind()) {
323+
case AutoClosureExpr::Kind::None:
324+
case AutoClosureExpr::Kind::SingleCurryThunk:
325+
case AutoClosureExpr::Kind::DoubleCurryThunk:
326+
case AutoClosureExpr::Kind::AsyncLet:
327+
return true;
328+
}
329+
llvm_unreachable("Unhandled case in switch!");
330+
}
331+
// Otherwise, assume an implicit closure doesn't have user code.
332+
return false;
333+
}
334+
316335
// Lazy getters splice in the user-written initializer expr.
317336
if (auto *accessor = dyn_cast<AccessorDecl>(getFuncDecl())) {
318337
auto *storage = accessor->getStorage();
@@ -914,11 +933,6 @@ bool SILDeclRef::isBackDeployed() const {
914933
return false;
915934
}
916935

917-
bool SILDeclRef::isAnyThunk() const {
918-
return isForeignToNativeThunk() || isNativeToForeignThunk() ||
919-
isDistributedThunk() || isBackDeploymentThunk();
920-
}
921-
922936
bool SILDeclRef::isForeignToNativeThunk() const {
923937
// If this isn't a native entry-point, it's not a foreign-to-native thunk.
924938
if (isForeign)
@@ -1082,7 +1096,7 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const {
10821096
// Use the SILGen name only for the original non-thunked, non-curried entry
10831097
// point.
10841098
if (auto NameA = getDecl()->getAttrs().getAttribute<SILGenNameAttr>())
1085-
if (!NameA->Name.empty() && !isAnyThunk()) {
1099+
if (!NameA->Name.empty() && !isThunk()) {
10861100
return NameA->Name.str();
10871101
}
10881102

lib/SIL/IR/SILFunction.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,20 @@ void SILFunction::deleteSnapshot(int ID) {
335335
void SILFunction::createProfiler(ASTNode Root, SILDeclRef Ref) {
336336
assert(!Profiler && "Function already has a profiler");
337337
Profiler = SILProfiler::create(Module, Root, Ref);
338+
if (!Profiler || Ref.isNull())
339+
return;
340+
341+
// If we loaded a profile, set the entry counts for functions and closures
342+
// for PGO to use.
343+
if (Ref.isFunc()) {
344+
if (auto *Closure = Ref.getAbstractClosureExpr()) {
345+
setEntryCount(Profiler->getExecutionCount(Closure));
346+
} else {
347+
auto *FD = Ref.getFuncDecl();
348+
assert(FD);
349+
setEntryCount(Profiler->getExecutionCount(FD->getBody()));
350+
}
351+
}
338352
}
339353

340354
bool SILFunction::hasForeignBody() const {

lib/SIL/IR/SILProfiler.cpp

Lines changed: 14 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,6 @@
3232

3333
using namespace swift;
3434

35-
/// Check if a closure has a body.
36-
static bool doesClosureHaveBody(AbstractClosureExpr *ACE) {
37-
if (auto *CE = dyn_cast<ClosureExpr>(ACE))
38-
return CE->getBody();
39-
if (auto *autoCE = dyn_cast<AutoClosureExpr>(ACE))
40-
return autoCE->getBody();
41-
return false;
42-
}
43-
4435
/// Check whether a root AST node should be profiled.
4536
static bool shouldProfile(ASTNode N, SILDeclRef Constant) {
4637
// Do not profile AST nodes with invalid source locations.
@@ -67,62 +58,15 @@ static bool shouldProfile(ASTNode N, SILDeclRef Constant) {
6758
}
6859
}
6960

70-
if (auto *E = N.dyn_cast<Expr *>()) {
71-
if (auto *CE = dyn_cast<AbstractClosureExpr>(E)) {
72-
// Only profile closure expressions with bodies.
73-
if (!doesClosureHaveBody(CE)) {
74-
LLVM_DEBUG(llvm::dbgs() << "Skipping ASTNode: closure without body\n");
75-
return false;
76-
}
77-
78-
// Don't profile implicit closures, unless they're autoclosures.
79-
if (!isa<AutoClosureExpr>(CE) && CE->isImplicit()) {
80-
LLVM_DEBUG(llvm::dbgs() << "Skipping ASTNode: implicit closure expr\n");
81-
return false;
82-
}
83-
}
84-
85-
// Profile all other kinds of expressions.
86-
return true;
87-
}
88-
89-
auto *D = N.get<Decl *>();
90-
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
91-
// Don't profile functions without bodies.
92-
if (!AFD->hasBody()) {
93-
LLVM_DEBUG(llvm::dbgs() << "Skipping ASTNode: function without body\n");
94-
return false;
95-
}
96-
97-
// Profile implicit getters for lazy variables.
98-
if (auto *accessor = dyn_cast<AccessorDecl>(AFD)) {
99-
if (accessor->isImplicit() && accessor->isGetter() &&
100-
accessor->getStorage()->getAttrs().hasAttribute<LazyAttr>()) {
101-
return true;
102-
}
103-
}
104-
}
105-
106-
// Skip any remaining implicit, or otherwise unsupported decls.
107-
if (D->isImplicit() || isa<EnumCaseDecl>(D)) {
108-
LLVM_DEBUG(llvm::dbgs() << "Skipping ASTNode: implicit/unsupported decl\n");
61+
// Do not profile code that hasn't been written by the user.
62+
if (!Constant.hasUserWrittenCode()) {
63+
LLVM_DEBUG(llvm::dbgs() << "Skipping ASTNode: no user-written code\n");
10964
return false;
11065
}
11166

11267
return true;
11368
}
11469

115-
namespace swift {
116-
bool doesASTRequireProfiling(SILModule &M, ASTNode N, SILDeclRef Constant) {
117-
// If profiling isn't enabled, don't profile anything.
118-
auto &Opts = M.getOptions();
119-
if (Opts.UseProfile.empty() && !Opts.GenerateProfile)
120-
return false;
121-
122-
return shouldProfile(N, Constant);
123-
}
124-
} // namespace swift
125-
12670
/// Get the DeclContext for the decl referenced by \p forDecl.
12771
DeclContext *getProfilerContextForDecl(ASTNode N, SILDeclRef forDecl) {
12872
if (auto *D = N.dyn_cast<Decl *>())
@@ -152,29 +96,34 @@ static bool hasASTBeenTypeChecked(ASTNode N, SILDeclRef forDecl) {
15296
return !SF || SF->ASTStage >= SourceFile::TypeChecked;
15397
}
15498

155-
/// Check whether a mapped AST node requires a new profiler.
99+
/// Check whether a mapped AST node is valid for profiling.
156100
static bool canCreateProfilerForAST(ASTNode N, SILDeclRef forDecl) {
157101
assert(hasASTBeenTypeChecked(N, forDecl) &&
158102
"Cannot use this AST for profiling");
159103

160104
if (auto *D = N.dyn_cast<Decl *>()) {
161-
if (isa<AbstractFunctionDecl>(D))
162-
return true;
105+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D))
106+
return AFD->hasBody();
163107

164108
if (isa<TopLevelCodeDecl>(D))
165109
return true;
166110
} else if (N.get<Expr *>()) {
111+
if (auto *closure = forDecl.getAbstractClosureExpr())
112+
return closure->hasBody();
167113
if (forDecl.isStoredPropertyInitializer() ||
168-
forDecl.isPropertyWrapperBackingInitializer() ||
169-
forDecl.getAbstractClosureExpr())
114+
forDecl.isPropertyWrapperBackingInitializer())
170115
return true;
171116
}
172117
return false;
173118
}
174119

175120
SILProfiler *SILProfiler::create(SILModule &M, ASTNode N, SILDeclRef Ref) {
121+
// If profiling isn't enabled, don't profile anything.
176122
const auto &Opts = M.getOptions();
177-
if (!doesASTRequireProfiling(M, N, Ref))
123+
if (!Opts.GenerateProfile && Opts.UseProfile.empty())
124+
return nullptr;
125+
126+
if (!shouldProfile(N, Ref))
178127
return nullptr;
179128

180129
if (!canCreateProfilerForAST(N, Ref)) {

lib/SILGen/SILGen.cpp

Lines changed: 11 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -654,35 +654,6 @@ static SILFunction *getFunctionToInsertAfter(SILGenModule &SGM,
654654
return nullptr;
655655
}
656656

657-
static bool haveProfiledAssociatedFunction(SILDeclRef constant) {
658-
return constant.isDefaultArgGenerator() || constant.isForeign;
659-
}
660-
661-
/// Set up the function for profiling instrumentation.
662-
static void setUpForProfiling(SILDeclRef constant, SILFunction *F,
663-
ForDefinition_t forDefinition) {
664-
if (!forDefinition || F->getProfiler())
665-
return;
666-
667-
ASTNode profiledNode;
668-
if (!haveProfiledAssociatedFunction(constant)) {
669-
if (constant.hasDecl()) {
670-
if (auto *fd = constant.getFuncDecl()) {
671-
if (fd->hasBody()) {
672-
F->createProfiler(fd, constant);
673-
profiledNode = fd->getBody(/*canSynthesize=*/false);
674-
}
675-
}
676-
} else if (auto *ace = constant.getAbstractClosureExpr()) {
677-
F->createProfiler(ace, constant);
678-
profiledNode = ace;
679-
}
680-
// Set the function entry count for PGO.
681-
if (SILProfiler *SP = F->getProfiler())
682-
F->setEntryCount(SP->getExecutionCount(profiledNode));
683-
}
684-
}
685-
686657
static bool isEmittedOnDemand(SILModule &M, SILDeclRef constant) {
687658
if (!constant.hasDecl())
688659
return false;
@@ -730,12 +701,9 @@ static bool isEmittedOnDemand(SILModule &M, SILDeclRef constant) {
730701

731702
SILFunction *SILGenModule::getFunction(SILDeclRef constant,
732703
ForDefinition_t forDefinition) {
733-
// If we already emitted the function, return it (potentially preparing it
734-
// for definition).
735-
if (auto emitted = getEmittedFunction(constant, forDefinition)) {
736-
setUpForProfiling(constant, emitted, forDefinition);
704+
// If we already emitted the function, return it.
705+
if (auto emitted = getEmittedFunction(constant, forDefinition))
737706
return emitted;
738-
}
739707

740708
auto getBestLocation = [](SILDeclRef ref) -> SILLocation {
741709
if (ref.hasDecl())
@@ -755,7 +723,6 @@ SILFunction *SILGenModule::getFunction(SILDeclRef constant,
755723
[&IGM](SILLocation loc, SILDeclRef constant) -> SILFunction * {
756724
return IGM.getFunction(constant, NotForDefinition);
757725
});
758-
setUpForProfiling(constant, F, forDefinition);
759726

760727
assert(F && "SILFunction should have been defined");
761728

@@ -889,6 +856,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
889856
if (auto *ce = constant.getAbstractClosureExpr()) {
890857
preEmitFunction(constant, f, ce);
891858
PrettyStackTraceSILFunction X("silgen closureexpr", f);
859+
f->createProfiler(ce, constant);
892860
SILGenFunction(*this, *f, ce).emitClosure(ce);
893861
postEmitFunction(constant, f);
894862
break;
@@ -898,6 +866,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
898866

899867
preEmitFunction(constant, f, fd);
900868
PrettyStackTraceSILFunction X("silgen emitFunction", f);
869+
f->createProfiler(fd, constant);
901870
SILGenFunction(*this, *f, fd).emitFunction(fd);
902871
postEmitFunction(constant, f);
903872
break;
@@ -1173,24 +1142,20 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
11731142

11741143
/// Emit a function now, if it's externally usable or has been referenced in
11751144
/// the current TU, or remember how to emit it later if not.
1176-
static void emitOrDelayFunction(SILGenModule &SGM,
1177-
SILDeclRef constant,
1178-
bool forceEmission = false) {
1145+
static void emitOrDelayFunction(SILGenModule &SGM, SILDeclRef constant) {
11791146
assert(!constant.isThunk());
11801147
assert(!constant.isClangImported());
11811148

11821149
auto emitAfter = SGM.lastEmittedFunction;
11831150

1184-
SILFunction *f = nullptr;
1185-
11861151
// Implicit decls may be delayed if they can't be used externally.
11871152
auto linkage = constant.getLinkage(ForDefinition);
1188-
bool mayDelay = !forceEmission &&
1189-
(!constant.hasUserWrittenCode() &&
1190-
!constant.isDynamicallyReplaceable() &&
1191-
!isPossiblyUsedExternally(linkage, SGM.M.isWholeModule()));
1153+
bool mayDelay = !constant.hasUserWrittenCode() &&
1154+
!constant.isDynamicallyReplaceable() &&
1155+
!isPossiblyUsedExternally(linkage, SGM.M.isWholeModule());
11921156

11931157
// Avoid emitting a delayable definition if it hasn't already been referenced.
1158+
SILFunction *f = nullptr;
11941159
if (mayDelay)
11951160
f = SGM.getEmittedFunction(constant, ForDefinition);
11961161
else
@@ -1444,12 +1409,8 @@ void SILGenModule::emitFunction(FuncDecl *fd) {
14441409

14451410
emitAbstractFuncDecl(fd);
14461411

1447-
if (fd->hasBody()) {
1448-
SILDeclRef constant(decl);
1449-
bool ForCoverageMapping = doesASTRequireProfiling(M, fd, constant);
1450-
emitOrDelayFunction(*this, constant,
1451-
/*forceEmission=*/ForCoverageMapping);
1452-
}
1412+
if (fd->hasBody())
1413+
emitOrDelayFunction(*this, SILDeclRef(decl));
14531414
}
14541415

14551416
void SILGenModule::addGlobalVariable(VarDecl *global) {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// A secondary file for unmapped.swift that can contain profiled code.
2+
3+
@propertyWrapper
4+
struct Wrapper<T> {
5+
var wrappedValue: T
6+
}
7+
8+
struct Projected<T> {
9+
var value: T
10+
}
11+
12+
@propertyWrapper
13+
struct WrapperWithProjectedValue<T> {
14+
var wrappedValue: T
15+
init(projectedValue: Projected<T>) {
16+
self.wrappedValue = projectedValue.value
17+
}
18+
var projectedValue: Projected<T> {
19+
Projected(value: wrappedValue)
20+
}
21+
}
22+

test/Profiler/coverage_property_wrapper_backing.swift

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -emit-sorted-sil -emit-sil -module-name coverage_property_wrapper_backing %s | %FileCheck %s
1+
// This uses '-primary-file' to ensure we're conservative with lazy SIL emission.
2+
// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -emit-sorted-sil -emit-sil -module-name coverage_property_wrapper_backing -primary-file %s | %FileCheck %s
23
// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -emit-ir %s
34

45
@propertyWrapper
@@ -9,6 +10,11 @@ struct Wrapper<T> {
910
}
1011
}
1112

13+
@propertyWrapper
14+
struct PassThroughWrapper<T> {
15+
var wrappedValue: T
16+
}
17+
1218
// rdar://99931619 – Make sure we emit the profiler increment for the backing
1319
// initializer.
1420

@@ -32,6 +38,20 @@ struct S {
3238
// CHECK-NEXT: }
3339
}
3440

35-
// FIXME(rdar://99962285): This is currently needed to SILGen the property
36-
// initializer for 'i'.
37-
_ = S().i
41+
struct T {
42+
// The backing initializer for j has no user-written code, so no coverage map needed.
43+
// CHECK-NOT: sil_coverage_map {{.*}} "s33coverage_property_wrapper_backing1TV1jSivpfP"
44+
@PassThroughWrapper
45+
var j = 3
46+
47+
// FIXME: Ideally the region here would only span the length of the @Wrapper
48+
// argument list.
49+
// CHECK-LABEL: sil_coverage_map {{.*}} "$s33coverage_property_wrapper_backing1TV1kSivpfP" {{.*}} // property wrapper backing initializer of coverage_property_wrapper_backing.T.k
50+
// CHECK-NEXT: [[@LINE+4]]:4 -> [[@LINE+5]]:30 : 0
51+
// CHECK-NEXT: [[@LINE+4]]:24 -> [[@LINE+4]]:25 : 1
52+
// CHECK-NEXT: [[@LINE+3]]:28 -> [[@LINE+3]]:29 : (0 - 1)
53+
// CHECK-NEXT: }
54+
@PassThroughWrapper
55+
@Wrapper(.random() ? 1 : 2)
56+
var k = 3
57+
}

0 commit comments

Comments
 (0)