Skip to content

Commit a9da803

Browse files
authored
[SILProfiler] Set up profiling for lazily-emitted functions (#22403)
A SIL function that's initially only emitted as a declaration may later be prepared for definition. When this happens, set up a profiler for the definition. This makes code coverage visible for private methods (the frontend follows a declare-then-define pattern for these). rdar://47759243
1 parent 59282c4 commit a9da803

File tree

3 files changed

+52
-13
lines changed

3 files changed

+52
-13
lines changed

lib/SILGen/SILGen.cpp

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -606,20 +606,12 @@ SILGenModule::getOrCreateProfilerForConstructors(DeclContext *ctx,
606606
return profiler;
607607
}
608608

609-
SILFunction *SILGenModule::getFunction(SILDeclRef constant,
610-
ForDefinition_t forDefinition) {
611-
// If we already emitted the function, return it (potentially preparing it
612-
// for definition).
613-
if (auto emitted = getEmittedFunction(constant, forDefinition))
614-
return emitted;
615-
616-
// Note: Do not provide any SILLocation. You can set it afterwards.
617-
SILGenFunctionBuilder builder(*this);
618-
auto *F = builder.getOrCreateFunction(constant.hasDecl() ? constant.getDecl()
619-
: (Decl *)nullptr,
620-
constant, forDefinition);
609+
/// Set up the function for profiling instrumentation.
610+
static void setUpForProfiling(SILDeclRef constant, SILFunction *F,
611+
ForDefinition_t forDefinition) {
612+
if (!forDefinition)
613+
return;
621614

622-
// Set up the function for profiling instrumentation.
623615
ASTNode profiledNode;
624616
if (!haveProfiledAssociatedFunction(constant)) {
625617
if (constant.hasDecl()) {
@@ -637,6 +629,23 @@ SILFunction *SILGenModule::getFunction(SILDeclRef constant,
637629
if (SILProfiler *SP = F->getProfiler())
638630
F->setEntryCount(SP->getExecutionCount(profiledNode));
639631
}
632+
}
633+
634+
SILFunction *SILGenModule::getFunction(SILDeclRef constant,
635+
ForDefinition_t forDefinition) {
636+
// If we already emitted the function, return it (potentially preparing it
637+
// for definition).
638+
if (auto emitted = getEmittedFunction(constant, forDefinition)) {
639+
setUpForProfiling(constant, emitted, forDefinition);
640+
return emitted;
641+
}
642+
643+
// Note: Do not provide any SILLocation. You can set it afterwards.
644+
SILGenFunctionBuilder builder(*this);
645+
auto *F = builder.getOrCreateFunction(constant.hasDecl() ? constant.getDecl()
646+
: (Decl *)nullptr,
647+
constant, forDefinition);
648+
setUpForProfiling(constant, F, forDefinition);
640649

641650
assert(F && "SILFunction should have been defined");
642651

test/Profiler/coverage_private.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -emit-sorted-sil -emit-sil -module-name coverage_private %s | %FileCheck %s
2+
3+
struct S {
4+
func visible() {
5+
hidden()
6+
}
7+
8+
// CHECK-LABEL: sil_coverage_map {{.*}}// coverage_private.S.(hidden in {{.*}})() -> ()
9+
// CHECK-NEXT: [[@LINE+1]]:25 -> [[@LINE+2]]:4 : 0
10+
private func hidden() {
11+
}
12+
}
13+
14+
S().visible()

test/Profiler/coverage_smoke.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,20 @@ let _ = Class3()
189189
let _ = Struct1(field: 1)
190190
let _ = Struct1()
191191

192+
struct Struct2 {
193+
func visible() {
194+
hidden()
195+
}
196+
private func hidden() {
197+
var x: Int = 0 // CHECK-COV: {{ *}}[[@LINE]]|{{ *}}1
198+
func helper() {
199+
x += 1 // CHECK-COV: {{ *}}[[@LINE]]|{{ *}}1
200+
}
201+
helper() // CHECK-COV: {{ *}}[[@LINE]]|{{ *}}1
202+
}
203+
}
204+
205+
var s2 = Struct2()
206+
s2.visible()
207+
192208
// CHECK-REPORT: TOTAL {{.*}} 100.00% {{.*}} 100.00%

0 commit comments

Comments
 (0)