[Profiler] Emit skipped regions for inactive #if branches

For any `#if` blocks in the function we're emitting,
emit skipped ranges for the inactive clauses, including
the syntax for the `#if` itself, since that should
not be considered executable code.

rdar://116860865
This commit is contained in:
Hamish Knight
2023-11-14 20:41:35 +00:00
parent d31f76853a
commit 5aab237468
3 changed files with 308 additions and 3 deletions

View File

@@ -1066,19 +1066,41 @@ public:
if (SourceRegions.empty())
return nullptr;
using MappedRegion = SILCoverageMap::MappedRegion;
llvm::coverage::CounterExpressionBuilder Builder;
std::vector<SILCoverageMap::MappedRegion> Regions;
std::vector<MappedRegion> Regions;
SourceRange OuterRange;
for (const auto &Region : SourceRegions) {
assert(Region.hasStartLoc() && "invalid region");
assert(Region.hasEndLoc() && "incomplete region");
// Build up the outer range from the union of all coverage regions.
SourceRange Range(Region.getStartLoc(), Region.getEndLoc());
if (!OuterRange) {
OuterRange = Range;
} else {
OuterRange.widen(Range);
}
auto Start = SM.getLineAndColumnInBuffer(Region.getStartLoc());
auto End = SM.getLineAndColumnInBuffer(Region.getEndLoc());
assert(Start.first <= End.first && "region start and end out of order");
auto Counter = Region.getCounter(CounterMap);
Regions.emplace_back(Start.first, Start.second, End.first, End.second,
Counter.expand(Builder, CounterIndices));
Regions.push_back(
MappedRegion::code(Start.first, Start.second, End.first, End.second,
Counter.expand(Builder, CounterIndices)));
}
// Add any skipped regions present in the outer range.
for (auto IfConfig : SF->getIfConfigsWithin(OuterRange)) {
for (auto SkipRange : IfConfig.getRangesWithoutActiveBody(SM)) {
auto Start = SM.getLineAndColumnInBuffer(SkipRange.getStart());
auto End = SM.getLineAndColumnInBuffer(SkipRange.getEnd());
assert(Start.first <= End.first && "region start and end out of order");
Regions.push_back(MappedRegion::skipped(Start.first, Start.second,
End.first, End.second));
}
}
return SILCoverageMap::create(M, SF, Filename, Name, PGOFuncName, Hash,
Regions, Builder.getExpressions());

View File

@@ -0,0 +1,154 @@
// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -suppress-warnings -profile-generate -profile-coverage-mapping -emit-sorted-sil -emit-sil -module-name coverage_pound_if %s | %FileCheck %s
// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -emit-ir %s
func poundIf1() -> Int {
#if true
#if true
return 1
#else
return 2
#endif
#else
#if true
return 3
#else
return 4
#endif
#endif
}
// CHECK-LABEL: sil_coverage_map {{.*}} "$s17coverage_pound_if0B3If1SiyF"
// CHECK-NEXT: [[@LINE-16]]:24 -> [[@LINE-2]]:2 : 0
// CHECK-NEXT: [[@LINE-16]]:3 -> [[@LINE-16]]:11 : skipped
// CHECK-NEXT: [[@LINE-16]]:5 -> [[@LINE-16]]:13 : skipped
// CHECK-NEXT: [[@LINE-15]]:5 -> [[@LINE-13]]:11 : skipped
// CHECK-NEXT: [[@LINE-13]]:3 -> [[@LINE-7]]:9 : skipped
// CHECK-NEXT: }
func poundIf2(_ x: [Int]) -> [Int] {
return x
#if false
.map { $0 + 1 }
#endif
}
// CHECK-LABEL: sil_coverage_map {{.*}} "$s17coverage_pound_if0B3If2ySaySiGACF"
// CHECK-NEXT: [[@LINE-7]]:36 -> [[@LINE-2]]:2 : 0
// CHECK-NEXT: [[@LINE-6]]:3 -> [[@LINE-4]]:9 : skipped
// CHECK-NEXT: }
func poundIf3() -> Any {
#if false
@objc
#endif
class C {}
return C()
}
// CHECK-LABEL: sil_coverage_map {{.*}} "$s17coverage_pound_if0B3If3ypyF"
// CHECK-NEXT: [[@LINE-8]]:24 -> [[@LINE-2]]:2 : 0
// CHECK-NEXT: [[@LINE-8]]:1 -> [[@LINE-6]]:7 : skipped
// CHECK-NEXT: }
func poundIf4(_ x: Bool) -> Int {
switch x {
#if true
case true:
return 0
#else
case false:
return 0
#endif
#if false
case false:
return 1
#endif
case false:
return 0
}
}
// CHECK-LABEL: sil_coverage_map {{.*}} "$s17coverage_pound_if0B3If4ySiSbF"
// CHECK-NEXT: [[@LINE-18]]:33 -> [[@LINE-2]]:2 : 0
// CHECK-NEXT: [[@LINE-18]]:10 -> [[@LINE-18]]:11 : 0
// CHECK-NEXT: [[@LINE-18]]:3 -> [[@LINE-18]]:11 : skipped
// CHECK-NEXT: [[@LINE-18]]:3 -> [[@LINE-17]]:13 : 1
// CHECK-NEXT: [[@LINE-17]]:3 -> [[@LINE-14]]:9 : skipped
// CHECK-NEXT: [[@LINE-14]]:3 -> [[@LINE-11]]:9 : skipped
// CHECK-NEXT: [[@LINE-11]]:3 -> [[@LINE-10]]:13 : 2
// CHECK-NEXT: }
func poundIf5() {
struct S {
#if false
var foo: Int
#else
var foo: Int
#endif
}
}
// CHECK-LABEL: sil_coverage_map {{.*}} "$s17coverage_pound_if0B3If5yyF"
// CHECK-NEXT: [[@LINE-10]]:17 -> [[@LINE-2]]:2 : 0
// CHECK-NEXT: [[@LINE-9]]:1 -> [[@LINE-7]]:6 : skipped
// CHECK-NEXT: [[@LINE-6]]:1 -> [[@LINE-6]]:7 : skipped
// CHECK-NEXT: }
func poundIf6() -> Int {
#if true
struct S {
var foo: Int
}
#else
struct S {
var foo: Int
}
#endif
return S(foo: 0).foo
}
// CHECK-LABEL: sil_coverage_map {{.*}} "$s17coverage_pound_if0B3If6SiyF"
// CHECK-NEXT: [[@LINE-13]]:24 -> [[@LINE-2]]:2 : 0
// CHECK-NEXT: [[@LINE-13]]:3 -> [[@LINE-13]]:11 : skipped
// CHECK-NEXT: [[@LINE-10]]:3 -> [[@LINE-6]]:9 : skipped
// CHECK-NEXT: }
func poundIf7() -> Int {
#if false
#endif
return 0
}
// CHECK-LABEL: sil_coverage_map {{.*}} "$s17coverage_pound_if0B3If7SiyF"
// CHECK-NEXT: [[@LINE-6]]:24 -> [[@LINE-2]]:2 : 0
// CHECK-NEXT: [[@LINE-6]]:3 -> [[@LINE-5]]:9 : skipped
// CHECK-NEXT: }
func poundIf8() -> Int {
#if true
#endif
return 0
}
// CHECK-LABEL: sil_coverage_map {{.*}} "$s17coverage_pound_if0B3If8SiyF"
// CHECK-NEXT: [[@LINE-6]]:24 -> [[@LINE-2]]:2 : 0
// CHECK-NEXT: [[@LINE-6]]:3 -> [[@LINE-6]]:11 : skipped
// CHECK-NEXT: [[@LINE-6]]:3 -> [[@LINE-6]]:9 : skipped
// CHECK-NEXT: }
func poundIf9() -> Int {
#if true
#else
#endif
return 0
}
// CHECK-LABEL: sil_coverage_map {{.*}} "$s17coverage_pound_if0B3If9SiyF"
// CHECK-NEXT: [[@LINE-7]]:24 -> [[@LINE-2]]:2 : 0
// CHECK-NEXT: [[@LINE-7]]:3 -> [[@LINE-7]]:11 : skipped
// CHECK-NEXT: [[@LINE-7]]:3 -> [[@LINE-6]]:9 : skipped
// CHECK-NEXT: }
func poundIf10() -> Int {
#if true
return 0
#else
return 1#endif
}
// CHECK-LABEL: sil_coverage_map {{.*}} "$s17coverage_pound_if0B4If10SiyF"
// CHECK-NEXT: [[@LINE-7]]:25 -> [[@LINE-2]]:2 : 0
// CHECK-NEXT: [[@LINE-7]]:3 -> [[@LINE-7]]:11 : skipped
// CHECK-NEXT: [[@LINE-6]]:3 -> [[@LINE-5]]:17 : skipped
// CHECK-NEXT: }

View File

@@ -0,0 +1,129 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -profile-generate -profile-coverage-mapping -o %t/main
// This unusual use of 'sh' allows the path of the profraw file to be
// substituted by %target-run.
// RUN: %target-codesign %t/main
// RUN: %target-run sh -c 'env LLVM_PROFILE_FILE=$1 $2' -- %t/default.profraw %t/main
// RUN: %llvm-profdata merge %t/default.profraw -o %t/default.profdata
// RUN: %llvm-cov export -summary-only %t/main -instr-profile=%t/default.profdata | %FileCheck --check-prefix SUMMARY %s
// RUN: %llvm-cov show %t/main -instr-profile=%t/default.profdata | %FileCheck --implicit-check-not "{{[ ]*\|[ ]*[0-9]+}}" %s
// REQUIRES: profile_runtime
// REQUIRES: executable_test
// REQUIRES: OS=macosx
// The line count here excludes the inactive regions.
// SUMMARY: "lines":{"count":41,"covered":0
// This test works by marking active lines with the pattern. The implicit
// CHECK-NOT in the FileCheck invocation ensures everything else is a skipped
// region (or doesn't have any coverage mapping such as these comments).
func poundIfDecl() -> Int { // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
#if true
#if true
return 1 // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
#else
return 2
#endif
#else
#if true
return 3
#else
return 4
#endif
#endif
} // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
func poundIfMember(
_ x: [Int]
) -> [Int] { // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
return x // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
#if false
.map { $0 + 1 }
#endif
} // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
func poundIfAttr() -> Any { // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
#if false
@objc
#endif
class C {} // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
return C() // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
} // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
func poundIfSwitch(
_ x: Bool
) -> Int { // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
switch x { // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
#if true
case true: // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
return 0 // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
#else
case false:
return 0
#endif
#if false
case false:
return 1
#endif
case false: // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
return 0 // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
} // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
} // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
func poundIfMember() { // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
struct S { // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
#if false
var foo: Int
#else
var foo: Int // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
#endif
} // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
} // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
func poundIfDecl2() -> Int { // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
#if true
struct S { // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
var foo: Int // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
} // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
#else
struct S {
var foo: Int
}
#endif
return S(foo: 0).foo // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
} // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
func emptyPoundIf1(
) -> Int { // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
#if false
#endif
return 0 // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
} // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
func emptyPoundIf2(
) -> Int { // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
#if true
#endif
return 0 // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
} // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
func emptyPoundIf3(
) -> Int { // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
#if true
#else
#endif
return 0 // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
} // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
func poundEndIfSameLine(
) -> Int { // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
#if true
return 0 // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}
#else
return 1#endif
} // CHECK: [[@LINE]]{{[ ]*\|[ ]*[0-9]+}}