Skip to content

Commit bd18c37

Browse files
committed
[Profiler] Adopt SILDeclRef::hasUserWrittenCode
This replaces some of the existing checks, and ensures that we don't emit coverage maps for property wrapper backing initializers that don't contain user code. rdar://99931619
1 parent 54993b7 commit bd18c37

File tree

6 files changed

+88
-35
lines changed

6 files changed

+88
-35
lines changed

lib/SIL/IR/SILDeclRef.cpp

Lines changed: 19 additions & 0 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();

lib/SIL/IR/SILProfiler.cpp

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,19 @@ static bool shouldProfile(ASTNode N, SILDeclRef Constant) {
6767
}
6868
}
6969

70+
// Do not profile code that hasn't been written by the user.
71+
if (!Constant.hasUserWrittenCode()) {
72+
LLVM_DEBUG(llvm::dbgs() << "Skipping ASTNode: no user-written code\n");
73+
return false;
74+
}
75+
7076
if (auto *E = N.dyn_cast<Expr *>()) {
7177
if (auto *CE = dyn_cast<AbstractClosureExpr>(E)) {
7278
// Only profile closure expressions with bodies.
7379
if (!doesClosureHaveBody(CE)) {
7480
LLVM_DEBUG(llvm::dbgs() << "Skipping ASTNode: closure without body\n");
7581
return false;
7682
}
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-
}
8383
}
8484

8585
// Profile all other kinds of expressions.
@@ -93,20 +93,6 @@ static bool shouldProfile(ASTNode N, SILDeclRef Constant) {
9393
LLVM_DEBUG(llvm::dbgs() << "Skipping ASTNode: function without body\n");
9494
return false;
9595
}
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");
109-
return false;
11096
}
11197

11298
return true;
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+
}

test/Profiler/coverage_var_init.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ struct S {
3232
}
3333

3434
final class VarInit {
35-
// CHECK: sil_coverage_map {{.*}} "$s17coverage_var_init7VarInitC018initializedWrapperE0SivpfP"
36-
// CHECK-NEXT: [[@LINE+1]]:4 -> [[@LINE+1]]:42 : 0
37-
@Wrapper var initializedWrapperInit = 2
38-
3935
// CHECK: sil_coverage_map {{.*}} // variable initialization expression of coverage_var_init.VarInit.(_autoclosureWrapperInit
4036
// CHECK-NEXT: [[@LINE+1]]:52 -> [[@LINE+1]]:53
4137
@AutoClosureWrapper var autoclosureWrapperInit = 3
4238

39+
// CHECK: sil_coverage_map {{.*}} "$s17coverage_var_init7VarInitC019_initializedWrapper{{.*}}" {{.*}} // variable initialization expression of coverage_var_init.VarInit.(_initializedWrapperInit
40+
// CHECK-NEXT: [[@LINE+1]]:41 -> [[@LINE+1]]:42 : 0
41+
@Wrapper var initializedWrapperInit = 2
42+
4343
// CHECK: sil_coverage_map {{.*}} "$s17coverage_var_init7VarInitC04lazydE033_49373CB2DFB47C8DC62FA963604688DFLLSSvgSSyXEfU_"
4444
// CHECK-NEXT: [[@LINE+1]]:42 -> [[@LINE+3]]:4 : 0
4545
private lazy var lazyVarInit: String = {

test/Profiler/unmapped.swift

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -suppress-warnings -profile-generate -profile-coverage-mapping -emit-sorted-sil -emit-sil -module-name unmapped %s | %FileCheck %s
2-
// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -emit-ir %s
1+
// This uses '-primary-file' to ensure we're conservative with lazy SIL emission.
2+
// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -suppress-warnings -profile-generate -profile-coverage-mapping -emit-sorted-sil -emit-sil -module-name unmapped -primary-file %s %S/Inputs/unmapped_secondary.swift | %FileCheck %s
3+
// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -emit-ir %s %S/Inputs/unmapped_secondary.swift
34

45
// This test is exclusively for AST that we should never profile, as there is
56
// no interesting user-written code.
@@ -18,11 +19,21 @@ struct R : Codable {
1819
var y: Int
1920
}
2021

22+
struct Q {
23+
// Don't profile the backing initializer.
24+
@Wrapper
25+
var x: Int
26+
}
27+
2128
// Don't profile the implicit rawValue.
2229
enum E : Int {
2330
case a
2431
}
2532

33+
// Don't profile the backing initalizers of the property wrapper.
34+
@available(*, unavailable)
35+
func hasExternalPropertyWrapper(@WrapperWithProjectedValue x: Int) {}
36+
2637
// We don't profile unavailable functions, as they don't provide useful coverage
2738
// info.
2839

@@ -48,11 +59,6 @@ extension TypeWithUnavailableMethods {
4859
}
4960
}
5061

51-
@propertyWrapper
52-
struct Wrapper<T> {
53-
var wrappedValue: T
54-
}
55-
5662
@available(*, unavailable)
5763
struct UnavailableType {
5864
func foo() -> Int { .random() ? 1 : 2 }

0 commit comments

Comments
 (0)