mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[Profiler] Map regions for error-throwing AST nodes
Map a counter for the error branch of a given potentially-throwing expression, and subtract it from the following region count. rdar://34244637
This commit is contained in:
@@ -262,6 +262,28 @@ static bool shouldWalkIntoUnhandledDecl(const Decl *D) {
|
||||
return isa<PatternBindingDecl>(D);
|
||||
}
|
||||
|
||||
/// Whether the expression \c E could potentially throw an error.
|
||||
static bool mayExpressionThrow(const Expr *E) {
|
||||
if (auto *AE = dyn_cast<ApplyExpr>(E)) {
|
||||
// Throws if the function throws.
|
||||
return bool(AE->throws());
|
||||
}
|
||||
if (auto *S = dyn_cast<SubscriptExpr>(E)) {
|
||||
// Throws if subscript has a throwing getter.
|
||||
auto *SD = cast<SubscriptDecl>(S->getDecl().getDecl());
|
||||
if (auto *accessor = SD->getEffectfulGetAccessor())
|
||||
return accessor->hasThrows();
|
||||
}
|
||||
if (auto *DE = dyn_cast<DeclRefExpr>(E)) {
|
||||
if (auto *VD = dyn_cast<VarDecl>(DE->getDecl())) {
|
||||
// Throws if the getter throws.
|
||||
if (auto *accessor = VD->getEffectfulGetAccessor())
|
||||
return accessor->hasThrows();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// An ASTWalker that maps ASTNodes to profiling counters.
|
||||
struct MapRegionCounters : public ASTWalker {
|
||||
/// The SIL function being profiled.
|
||||
@@ -355,7 +377,26 @@ struct MapRegionCounters : public ASTWalker {
|
||||
if (isa<LazyInitializerExpr>(E))
|
||||
mapRegion(E);
|
||||
|
||||
return shouldWalkIntoExpr(E, Parent, Constant);
|
||||
auto WalkResult = shouldWalkIntoExpr(E, Parent, Constant);
|
||||
if (WalkResult.Action.Action == PreWalkAction::SkipChildren) {
|
||||
// We need to manually do the post-visit here since the ASTWalker will
|
||||
// skip it.
|
||||
// FIXME: The ASTWalker should do a post-visit.
|
||||
walkToExprPost(E);
|
||||
}
|
||||
return WalkResult;
|
||||
}
|
||||
|
||||
PostWalkResult<Expr *> walkToExprPost(Expr *E) override {
|
||||
if (shouldSkipExpr(E))
|
||||
return Action::Continue(E);
|
||||
|
||||
// If we have an expr that may throw an error, give it a counter for the
|
||||
// error branch.
|
||||
if (mayExpressionThrow(E))
|
||||
mapRegion(ProfileCounterRef::errorBranchOf(E));
|
||||
|
||||
return Action::Continue(E);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -809,7 +850,26 @@ struct PGOMapping : public ASTWalker {
|
||||
if (isa<LazyInitializerExpr>(E))
|
||||
setKnownExecutionCount(E);
|
||||
|
||||
return shouldWalkIntoExpr(E, Parent, Constant);
|
||||
auto WalkResult = shouldWalkIntoExpr(E, Parent, Constant);
|
||||
if (WalkResult.Action.Action == PreWalkAction::SkipChildren) {
|
||||
// We need to manually do the post-visit here since the ASTWalker will
|
||||
// skip it.
|
||||
// FIXME: The ASTWalker should do a post-visit.
|
||||
walkToExprPost(E);
|
||||
}
|
||||
return WalkResult;
|
||||
}
|
||||
|
||||
PostWalkResult<Expr *> walkToExprPost(Expr *E) override {
|
||||
if (shouldSkipExpr(E))
|
||||
return Action::Continue(E);
|
||||
|
||||
// If we have an expr that may throw an error, give it a counter for the
|
||||
// error branch.
|
||||
if (mayExpressionThrow(E))
|
||||
setKnownExecutionCount(ProfileCounterRef::errorBranchOf(E));
|
||||
|
||||
return Action::Continue(E);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1379,11 +1439,10 @@ public:
|
||||
}
|
||||
auto WalkResult = shouldWalkIntoExpr(E, Parent, Constant);
|
||||
if (WalkResult.Action.Action == PreWalkAction::SkipChildren) {
|
||||
// We need to manually pop the region here as the ASTWalker won't call
|
||||
// the post-visitation.
|
||||
// We need to manually do the post-visit here since the ASTWalker will
|
||||
// skip it.
|
||||
// FIXME: The ASTWalker should do a post-visit.
|
||||
if (hasCounter(E))
|
||||
popRegions(E);
|
||||
walkToExprPost(E);
|
||||
}
|
||||
return WalkResult;
|
||||
}
|
||||
@@ -1395,6 +1454,15 @@ public:
|
||||
if (hasCounter(E))
|
||||
popRegions(E);
|
||||
|
||||
// The region following the expression gets current counter minus the
|
||||
// error branch counter, i.e the number of times we didn't throw an error.
|
||||
if (!RegionStack.empty() && mayExpressionThrow(E)) {
|
||||
auto ThrowCount = assignKnownCounter(ProfileCounterRef::errorBranchOf(E));
|
||||
replaceCount(
|
||||
CounterExpr::Sub(getCurrentCounter(), ThrowCount, CounterAlloc),
|
||||
Lexer::getLocForEndOfToken(SM, E->getEndLoc()));
|
||||
}
|
||||
|
||||
return Action::Continue(E);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "swift/SIL/BasicBlockUtils.h"
|
||||
#include "swift/SIL/AbstractionPatternGenerators.h"
|
||||
#include "swift/SIL/SILArgument.h"
|
||||
#include "swift/SIL/SILProfiler.h"
|
||||
#include "llvm/Support/SaveAndRestore.h"
|
||||
|
||||
using namespace swift;
|
||||
@@ -1516,6 +1517,15 @@ void SILGenFunction::emitThrow(SILLocation loc, ManagedValue exnMV,
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto *E = loc.getAsASTNode<Expr>()) {
|
||||
// Check to see whether we have a counter associated with the error branch
|
||||
// of this node, and if so emit a counter increment.
|
||||
auto *P = F.getProfiler();
|
||||
auto ref = ProfileCounterRef::errorBranchOf(E);
|
||||
if (P && P->hasCounterFor(ref))
|
||||
emitProfilerIncrement(ref);
|
||||
}
|
||||
|
||||
SmallVector<SILValue, 1> args;
|
||||
|
||||
auto indirectErrorAddr = ThrowDest.getThrownError().IndirectErrorResult;
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -emit-sorted-sil -emit-sil -module-name coverage_catch %s | %FileCheck %s
|
||||
// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -emit-sorted-sil -emit-sil -module-name coverage_errors %s | %FileCheck %s
|
||||
// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -emit-ir %s
|
||||
|
||||
struct S {
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}}// coverage_catch.S.init() -> coverage_catch.S
|
||||
init() { // CHECK: [[@LINE]]:10 -> [[@LINE+6]]:4 : 0
|
||||
do { // CHECK: [[@LINE]]:8 -> [[@LINE+2]]:6 : 0
|
||||
throw SomeErr.Err1
|
||||
} catch {
|
||||
// CHECK: [[@LINE-1]]:13 -> [[@LINE+1]]:6 : 1
|
||||
} // CHECK: [[@LINE]]:6 -> [[@LINE+1]]:4 : 0
|
||||
static subscript() -> Int {
|
||||
get throws { 5 }
|
||||
}
|
||||
subscript() -> Int {
|
||||
get throws { 5 }
|
||||
}
|
||||
func throwingMethod() throws -> Int { 0 }
|
||||
}
|
||||
|
||||
func throwingFn() throws -> Int { 0 }
|
||||
|
||||
var throwingProp: Int {
|
||||
get throws { 5 }
|
||||
}
|
||||
|
||||
var throwingS: S {
|
||||
get throws { S() }
|
||||
}
|
||||
|
||||
enum SomeErr : Error {
|
||||
@@ -17,56 +26,214 @@ enum SomeErr : Error {
|
||||
case Err2
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}}// coverage_catch.test1
|
||||
func test1() throws {
|
||||
// Unfortunately due the sorting of the SIL, we have to write the SIL function
|
||||
// checks up here.
|
||||
|
||||
// func test1() -> Int {
|
||||
// do {
|
||||
// let x = try throwingFn()
|
||||
// return x
|
||||
// } catch {
|
||||
// return 0
|
||||
// }
|
||||
// }
|
||||
// CHECK-LABEL: sil hidden @$s15coverage_errors5test1SiyF : $@convention(thin) () -> Int
|
||||
// CHECK: bb0:
|
||||
// CHECK: increment_profiler_counter 0
|
||||
// CHECK: [[FN:%[0-9]+]] = function_ref @$s15coverage_errors10throwingFnSiyKF
|
||||
// CHECK: try_apply [[FN]]() : $@convention(thin) () -> (Int, @error any Error), normal [[BB_NORMAL:bb[0-9]+]], error [[BB_ERR:bb[0-9]+]]
|
||||
//
|
||||
// CHECK: [[BB_ERR]]
|
||||
// CHECK-NEXT: increment_profiler_counter 1
|
||||
// FIXME: This next counter is redundant, we ought to be able to
|
||||
// eliminate this with the SILOptimizer implementation.
|
||||
// CHECK: increment_profiler_counter 2
|
||||
|
||||
// func test2() throws -> Int {
|
||||
// let x = try throwingFn()
|
||||
// return x
|
||||
// }
|
||||
// CHECK-LABEL: sil hidden @$s15coverage_errors5test2SiyKF : $@convention(thin) () -> (Int, @error any Error)
|
||||
// CHECK: bb0:
|
||||
// CHECK: increment_profiler_counter 0
|
||||
// CHECK: [[FN:%[0-9]+]] = function_ref @$s15coverage_errors10throwingFnSiyKF
|
||||
// CHECK: try_apply [[FN]]() : $@convention(thin) () -> (Int, @error any Error), normal [[BB_NORMAL:bb[0-9]+]], error [[BB_ERR:bb[0-9]+]]
|
||||
//
|
||||
// CHECK: [[BB_ERR]]
|
||||
// CHECK-NEXT: increment_profiler_counter 1
|
||||
|
||||
// func test3() throws -> Int {
|
||||
// let x = try throwingProp
|
||||
// return x
|
||||
// }
|
||||
// CHECK-LABEL: sil hidden @$s15coverage_errors5test3SiyKF : $@convention(thin) () -> (Int, @error any Error)
|
||||
// CHECK: bb0:
|
||||
// CHECK: increment_profiler_counter 0
|
||||
// CHECK: [[FN:%[0-9]+]] = function_ref @$s15coverage_errors12throwingPropSivg
|
||||
// CHECK: try_apply [[FN]]() : $@convention(thin) () -> (Int, @error any Error), normal [[BB_NORMAL:bb[0-9]+]], error [[BB_ERR:bb[0-9]+]]
|
||||
//
|
||||
// CHECK: [[BB_ERR]]
|
||||
// CHECK-NEXT: increment_profiler_counter 1
|
||||
|
||||
// func test4() throws -> Int {
|
||||
// let x = try S[]
|
||||
// return x
|
||||
// }
|
||||
// CHECK-LABEL: sil hidden @$s15coverage_errors5test4SiyKF : $@convention(thin) () -> (Int, @error any Error)
|
||||
// CHECK: bb0:
|
||||
// CHECK: increment_profiler_counter 0
|
||||
// CHECK: [[FN:%[0-9]+]] = function_ref @$s15coverage_errors1SVSiycigZ
|
||||
// CHECK: try_apply [[FN]]({{%[0-9]+}}) : $@convention(method) (@thin S.Type) -> (Int, @error any Error), normal [[BB_NORMAL:bb[0-9]+]], error [[BB_ERR:bb[0-9]+]]
|
||||
//
|
||||
// CHECK: [[BB_ERR]]
|
||||
// CHECK-NEXT: increment_profiler_counter 1
|
||||
|
||||
// func test17(
|
||||
// _ x: S
|
||||
// ) throws -> Int {
|
||||
// let y = try x[]
|
||||
// return y
|
||||
// }
|
||||
// CHECK-LABEL: sil hidden @$s15coverage_errors6test17ySiAA1SVKF : $@convention(thin) (S) -> (Int, @error any Error)
|
||||
// CHECK: bb0(%0 : $S):
|
||||
// CHECK: increment_profiler_counter 0
|
||||
// CHECK: [[FN:%[0-9]+]] = function_ref @$s15coverage_errors1SVSiycig
|
||||
// CHECK: try_apply [[FN]](%0) : $@convention(method) (S) -> (Int, @error any Error), normal [[BB_NORMAL:bb[0-9]+]], error [[BB_ERR:bb[0-9]+]]
|
||||
//
|
||||
// CHECK: [[BB_ERR]]
|
||||
// CHECK-NEXT: increment_profiler_counter 1
|
||||
|
||||
// func test18(
|
||||
// _ x: S
|
||||
// ) throws -> Int {
|
||||
// let y = try x.throwingMethod()
|
||||
// return y
|
||||
// }
|
||||
// CHECK-LABEL: sil hidden @$s15coverage_errors6test18ySiAA1SVKF : $@convention(thin) (S) -> (Int, @error any Error)
|
||||
// CHECK: bb0(%0 : $S):
|
||||
// CHECK: increment_profiler_counter 0
|
||||
// CHECK: [[FN:%[0-9]+]] = function_ref @$s15coverage_errors1SV14throwingMethodSiyKF
|
||||
// CHECK: try_apply [[FN]](%0) : $@convention(method) (S) -> (Int, @error any Error), normal [[BB_NORMAL:bb[0-9]+]], error [[BB_ERR:bb[0-9]+]]
|
||||
//
|
||||
// CHECK: [[BB_ERR]]
|
||||
// CHECK-NEXT: increment_profiler_counter 1
|
||||
|
||||
// func test19() throws -> Int {
|
||||
// let x = try throwingS.throwingMethod()
|
||||
// return x
|
||||
// }
|
||||
// CHECK-LABEL: sil hidden @$s15coverage_errors6test19SiyKF : $@convention(thin) () -> (Int, @error any Error)
|
||||
// CHECK: bb0:
|
||||
// CHECK: increment_profiler_counter 0
|
||||
// CHECK: [[GETTER:%[0-9]+]] = function_ref @$s15coverage_errors9throwingSAA1SVvg
|
||||
// CHECK: try_apply [[GETTER]]() : $@convention(thin) () -> (S, @error any Error), normal [[BB_NORMAL:bb[0-9]+]], error [[BB_ERR:bb[0-9]+]]
|
||||
//
|
||||
// CHECK: [[BB_ERR]]
|
||||
// CHECK-NEXT: increment_profiler_counter 1
|
||||
// CHECK: [[BB_NORMAL]]([[S:%[0-9]+]] : $S):
|
||||
// CHECK: [[FN:%[0-9]+]] = function_ref @$s15coverage_errors1SV14throwingMethodSiyKF
|
||||
// CHECK: try_apply [[FN]]([[S]]) : $@convention(method) (S) -> (Int, @error any Error), normal [[BB_NORMAL:bb[0-9]+]], error [[BB_ERR:bb[0-9]+]]
|
||||
//
|
||||
// CHECK: [[BB_ERR]]
|
||||
// CHECK-NEXT: increment_profiler_counter 2
|
||||
|
||||
// func test21() throws -> Int {
|
||||
// try { throw SomeErr.Err1 }()
|
||||
// return 1
|
||||
// }
|
||||
// CHECK-LABEL: sil hidden @$s15coverage_errors6test21SiyKF : $@convention(thin) () -> (Int, @error any Error)
|
||||
// CHECK: bb0:
|
||||
// CHECK: increment_profiler_counter 0
|
||||
// CHECK: [[FN:%[0-9]+]] = function_ref @$s15coverage_errors6test21SiyKFyyKXEfU_
|
||||
// CHECK: try_apply [[FN]]() : $@convention(thin) () -> @error any Error, normal [[BB_NORMAL:bb[0-9]+]], error [[BB_ERR:bb[0-9]+]]
|
||||
//
|
||||
// CHECK: [[BB_ERR]]
|
||||
// CHECK-NEXT: increment_profiler_counter 1
|
||||
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors5test1SiyF"
|
||||
func test1() -> Int { // CHECK-NEXT: [[@LINE]]:21 -> [[@LINE+7]]:2 : 0
|
||||
do { // CHECK-NEXT: [[@LINE]]:6 -> [[@LINE+3]]:4 : 0
|
||||
let x = try throwingFn() // CHECK-NEXT: [[@LINE]]:29 -> [[@LINE+1]]:13 : (0 - 1)
|
||||
return x
|
||||
} catch { // CHECK-NEXT: [[@LINE]]:11 -> [[@LINE+2]]:4 : 2
|
||||
return 0 // FIXME: The below region shouldn't exist
|
||||
} // CHECK-NEXT: [[@LINE]]:4 -> [[@LINE+1]]:2 : (1 - 2)
|
||||
} // CHECK-NEXT: }
|
||||
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors5test2SiyKF"
|
||||
func test2() throws -> Int { // CHECK-NEXT: [[@LINE]]:28 -> [[@LINE+3]]:2 : 0
|
||||
let x = try throwingFn() // CHECK-NEXT: [[@LINE]]:27 -> [[@LINE+1]]:11 : (0 - 1)
|
||||
return x
|
||||
} // CHECK-NEXT: }
|
||||
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors5test3SiyKF"
|
||||
func test3() throws -> Int { // CHECK-NEXT: [[@LINE]]:28 -> [[@LINE+3]]:2 : 0
|
||||
let x = try throwingProp // CHECK-NEXT: [[@LINE]]:27 -> [[@LINE+1]]:11 : (0 - 1)
|
||||
return x
|
||||
} // CHECK-NEXT: }
|
||||
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors5test4SiyKF"
|
||||
func test4() throws -> Int { // CHECK-NEXT: [[@LINE]]:28 -> [[@LINE+3]]:2 : 0
|
||||
let x = try S[] // CHECK-NEXT: [[@LINE]]:18 -> [[@LINE+1]]:11 : (0 - 1)
|
||||
return x
|
||||
} // CHECK-NEXT: }
|
||||
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors5test5yyKF"
|
||||
func test5() throws {
|
||||
// CHECK-NEXT: [[@LINE-1]]:21 -> [[@LINE+2]]:2 : 0
|
||||
throw SomeErr.Err2
|
||||
} // CHECK-NEXT: }
|
||||
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}}// coverage_catch.test2
|
||||
func test2(_ fn: () throws -> ()) rethrows {
|
||||
do {
|
||||
try fn()
|
||||
} catch SomeErr.Err1 { // CHECK: [[@LINE]]:24 -> {{[0-9]+}}:4 : 1
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors5test6yyyyKXEKF"
|
||||
func test6(
|
||||
_ fn: () throws -> ()
|
||||
) rethrows { // CHECK-NEXT: [[@LINE]]:12 -> [[@LINE+8]]:2 : 0
|
||||
do { // CHECK-NEXT: [[@LINE]]:6 -> [[@LINE+2]]:4 : 0
|
||||
try fn() // CHECK-NEXT: [[@LINE]]:13 -> [[@LINE+1]]:4 : (0 - 1)
|
||||
} catch SomeErr.Err1 { // CHECK-NEXT: [[@LINE]]:24 -> [[@LINE+2]]:4 : 2
|
||||
return
|
||||
} // CHECK-NEXT: [[@LINE]]:4 -> {{[0-9]+}}:2 : (0 - 1)
|
||||
} // CHECK-NEXT: [[@LINE]]:4 -> [[@LINE+3]]:2 : (0 - 2)
|
||||
|
||||
try fn()
|
||||
} // CHECK-NEXT: }
|
||||
try fn() // CHECK-NEXT: [[@LINE]]:11 -> [[@LINE+1]]:2 : ((0 - 2) - 3)
|
||||
} // CHECK-NEXT: }
|
||||
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}}// coverage_catch.test3
|
||||
func test3() -> Int32 {
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors5test7s5Int32VyF"
|
||||
func test7() -> Int32 { // CHECK-NEXT: [[@LINE]]:23 -> [[@LINE+31]]:2 : 0
|
||||
var x : Int32 = 0
|
||||
|
||||
do {
|
||||
do { // CHECK-NEXT: [[@LINE]]:6 -> [[@LINE+3]]:4 : 0
|
||||
throw SomeErr.Err1
|
||||
x += 2 // [[@LINE]]:5 -> [[@LINE+1]]:4 : zero
|
||||
} catch SomeErr.Err1 {
|
||||
// CHECK: [[@LINE-1]]:24 -> [[@LINE+1]]:4 : 1
|
||||
} catch _ {
|
||||
// CHECK: [[@LINE-1]]:13 -> [[@LINE+1]]:4 : 2
|
||||
} // CHECK: [[@LINE]]:4 -> {{[0-9:]+}} : 0
|
||||
x += 2 // CHECK-NEXT: [[@LINE]]:5 -> [[@LINE+1]]:4 : zero
|
||||
} catch SomeErr.Err1 { // CHECK-NEXT: [[@LINE]]:24 -> [[@LINE+1]]:4 : 1
|
||||
} catch _ { // CHECK-NEXT: [[@LINE]]:13 -> [[@LINE+1]]:4 : 2
|
||||
} // CHECK-NEXT: [[@LINE]]:4 -> {{[0-9:]+}} : 0
|
||||
|
||||
do {
|
||||
try test2(test1)
|
||||
} catch _ {
|
||||
// CHECK: [[@LINE-1]]:13 -> [[@LINE+1]]:4 : 3
|
||||
} // CHECK: [[@LINE]]:4 -> {{[0-9:]+}} : 0
|
||||
do { // CHECK-NEXT: [[@LINE]]:6 -> [[@LINE+2]]:4 : 0
|
||||
try test6(test5) // CHECK-NEXT: [[@LINE]]:21 -> [[@LINE+1]]:4 : (0 - 3)
|
||||
} catch _ { // CHECK-NEXT: [[@LINE]]:13 -> [[@LINE+1]]:4 : 4
|
||||
} // CHECK-NEXT: [[@LINE]]:4 -> {{[0-9:]+}} : 0
|
||||
|
||||
do {
|
||||
try test2 { () throws -> () in throw SomeErr.Err1 }
|
||||
} catch _ {}
|
||||
do { // CHECK-NEXT: [[@LINE]]:6 -> [[@LINE+5]]:4 : 0
|
||||
try test6 { // (closures are mapped separately)
|
||||
() throws -> () in
|
||||
throw SomeErr.Err1
|
||||
} // CHECK-NEXT: [[@LINE]]:6 -> [[@LINE+1]]:4 : (0 - 5)
|
||||
} catch _ {} // CHECK-NEXT: [[@LINE]]:13 -> [[@LINE]]:15 : 6
|
||||
// CHECK-NEXT: [[@LINE-1]]:15 -> {{[0-9:]+}} : 0
|
||||
|
||||
try! test2 { () throws -> () in return }
|
||||
// TODO: We ought to realize that everything after try! is unreachable
|
||||
// This is similar issue to rdar://100896177
|
||||
try! test6 {
|
||||
() throws -> () in
|
||||
return
|
||||
} // CHECK-NEXT: [[@LINE]]:4 -> [[@LINE+2]]:11 : (0 - 7)
|
||||
|
||||
return x
|
||||
}
|
||||
|
||||
let _ = test3()
|
||||
} // CHECK-NEXT: }
|
||||
|
||||
// rdar://34244637 - Coverage after a do-catch is incorrect
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}}// coverage_catch.test4
|
||||
func test4(_ b: Bool) -> Int { // CHECK-NEXT: [[@LINE]]:30 {{.*}} : 0
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors5test8ySiSbF"
|
||||
func test8(_ b: Bool) -> Int { // CHECK-NEXT: [[@LINE]]:30 {{.*}} : 0
|
||||
do { // CHECK-NEXT: [[@LINE]]:6 -> [[@LINE+2]]:4 : 0
|
||||
throw SomeErr.Err1
|
||||
} catch { // CHECK-NEXT: [[@LINE]]:11 {{.*}} : 1
|
||||
@@ -79,57 +246,120 @@ func test4(_ b: Bool) -> Int { // CHECK-NEXT: [[@LINE]]:30 {{.*}} : 0
|
||||
}
|
||||
|
||||
// Test coverage with nested do-catches
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}}// coverage_catch.test5
|
||||
func test5() -> Int {
|
||||
do {
|
||||
try test1()
|
||||
do {
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors5test9SiyF"
|
||||
func test9() -> Int { // CHECK-NEXT: [[@LINE]]:21 -> [[@LINE+12]]:2 : 0
|
||||
do { // CHECK-NEXT: [[@LINE]]:6 -> [[@LINE+8]]:4 : 0
|
||||
try test5() // CHECK-NEXT: [[@LINE]]:16 -> [[@LINE+7]]:4 : (0 - 1)
|
||||
do { // CHECK-NEXT: [[@LINE]]:8 -> [[@LINE+2]]:6 : (0 - 1)
|
||||
throw SomeErr.Err1
|
||||
} catch {
|
||||
} catch { // CHECK-NEXT: [[@LINE]]:13 -> [[@LINE+3]]:6 : 2
|
||||
return 0
|
||||
} // CHECK: [[@LINE]]:6 {{.*}} : (0 - 1)
|
||||
|
||||
} catch {
|
||||
// FIXME: There shouldn't be region here, it's unreachable (rdar://100470244)
|
||||
} // CHECK-NEXT: [[@LINE]]:6 -> [[@LINE+1]]:4 : ((0 - 1) - 2)
|
||||
} catch { // CHECK-NEXT: [[@LINE]]:11 -> [[@LINE+2]]:4 : 3
|
||||
return 1
|
||||
} // CHECK: [[@LINE]]:4 {{.*}} : ((0 - 1) - 2)
|
||||
|
||||
}
|
||||
} // CHECK-NEXT: [[@LINE]]:4 -> [[@LINE+1]]:2 : ((0 - 2) - 3)
|
||||
} // CHECK-NEXT: }
|
||||
|
||||
// Test coverage with a do-catch inside of a repeat-while
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}}// coverage_catch.test6
|
||||
func test6() -> Int {
|
||||
repeat { // CHECK: [[@LINE]]:10 {{.*}} : 1
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors6test10SiyF"
|
||||
func test10() -> Int {
|
||||
repeat { // CHECK: [[@LINE]]:10 {{.*}} : 1
|
||||
do {
|
||||
throw SomeErr.Err1
|
||||
} catch { // CHECK: [[@LINE]]:13 {{.*}} : 2
|
||||
} catch { // CHECK: [[@LINE]]:13 {{.*}} : 2
|
||||
return 0
|
||||
} // CHECK: [[@LINE]]:6 {{.*}} : (1 - 2)
|
||||
} // CHECK: [[@LINE]]:6 {{.*}} : (1 - 2)
|
||||
|
||||
} while false // CHECK: [[@LINE]]:11 {{.*}} : (1 - 2)
|
||||
} while false // CHECK: [[@LINE]]:11 {{.*}} : (1 - 2)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Test coverage with a break inside a do-catch inside of a repeat-while
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}}// coverage_catch.test7
|
||||
func test7() -> Int {
|
||||
repeat { // CHECK: [[@LINE]]:10 {{.*}} : 1
|
||||
do {
|
||||
try test1()
|
||||
} catch { // CHECK: [[@LINE]]:13 {{.*}} : 2
|
||||
break
|
||||
} // CHECK: [[@LINE]]:6 {{.*}} : (1 - 2)
|
||||
|
||||
} while false // CHECK: [[@LINE]]:11 {{.*}} : (1 - 2)
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors6test11SiyF"
|
||||
func test11() -> Int { // CHECK-NEXT: [[@LINE]]:22 -> [[@LINE+11]]:2 : 0
|
||||
repeat { // CHECK-NEXT: [[@LINE]]:10 -> [[@LINE+8]]:4 : 1
|
||||
do { // CHECK-NEXT: [[@LINE]]:8 -> [[@LINE+2]]:6 : 1
|
||||
try test5() // CHECK-NEXT: [[@LINE]]:18 -> [[@LINE+1]]:6 : (1 - 2)
|
||||
} catch { // CHECK-NEXT: [[@LINE]]:13 -> [[@LINE+2]]:6 : 3
|
||||
break // FIXME: This counter should account for the counter 2 (rdar://100470244)
|
||||
} // CHECK-NEXT: [[@LINE]]:6 -> [[@LINE+3]]:4 : (1 - 3)
|
||||
// FIXME: This exit counter is wrong (rdar://118472537)
|
||||
// CHECK-NEXT: [[@LINE+1]]:4 -> [[@LINE+2]]:11 : (0 - 3)
|
||||
} while false // CHECK-NEXT: [[@LINE]]:11 -> [[@LINE]]:16 : (1 - 3)
|
||||
return 1
|
||||
}
|
||||
} // CHECK-NEXT: }
|
||||
|
||||
// rdar://41010883 – Make sure we don't introduce an empty unreachable region.
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s14coverage_catch5test8SiyKF"
|
||||
func test8() throws -> Int { // CHECK-NEXT: [[@LINE]]:28 -> [[@LINE+7]]:2 : 0
|
||||
do { // CHECK-NEXT: [[@LINE]]:6 -> [[@LINE+3]]:4 : 0
|
||||
try test1()
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors6test12SiyKF"
|
||||
func test12() throws -> Int { // CHECK-NEXT: [[@LINE]]:29 -> [[@LINE+7]]:2 : 0
|
||||
do { // CHECK-NEXT: [[@LINE]]:6 -> [[@LINE+3]]:4 : 0
|
||||
try test5() // CHECK-NEXT: [[@LINE]]:16 -> [[@LINE+1]]:13 : (0 - 1)
|
||||
return 1
|
||||
} catch is SomeErr { // CHECK-NEXT: [[@LINE]]:22 -> [[@LINE+2]]:4 : 1
|
||||
throw SomeErr.Err1
|
||||
}
|
||||
} // CHECK-NEXT: }
|
||||
} catch is SomeErr { // CHECK-NEXT: [[@LINE]]:22 -> [[@LINE+2]]:4 : 2
|
||||
throw SomeErr.Err1 // FIXME: The below region shouldn't exist
|
||||
} // CHECK-NEXT: [[@LINE]]:4 -> [[@LINE+1]]:2 : 1
|
||||
} // CHECK-NEXT: }
|
||||
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors6test13SiyF"
|
||||
func test13() -> Int { // CHECK-NEXT: [[@LINE]]:22 -> [[@LINE+6]]:2 : 0
|
||||
do { // CHECK-NEXT: [[@LINE]]:6 -> [[@LINE+2]]:4 : 0
|
||||
return try throwingFn() // Note we don't emit a region here because it would be empty.
|
||||
} catch { // CHECK-NEXT: [[@LINE]]:11 -> [[@LINE+2]]:4 : 2
|
||||
return 0 // FIXME: The below region shouldn't exist
|
||||
} // CHECK-NEXT: [[@LINE]]:4 -> [[@LINE+1]]:2 : (1 - 2)
|
||||
} // CHECK-NEXT: }
|
||||
|
||||
func takesInts(_ x: Int, _ y: Int) {}
|
||||
|
||||
// The throwing expr is nested here, the region starts after the throwing expr.
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors6test14yyKF"
|
||||
func test14() throws { // CHECK-NEXT: [[@LINE]]:22 -> [[@LINE+2]]:2 : 0
|
||||
takesInts(try throwingFn(), 0) // CHECK-NEXT: [[@LINE]]:29 -> [[@LINE+1]]:2 : (0 - 1)
|
||||
} // CHECK-NEXT: }
|
||||
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors6test17ySiAA1SVKF"
|
||||
func test17(
|
||||
_ x: S
|
||||
) throws -> Int { // CHECK-NEXT: [[@LINE]]:17 -> [[@LINE+3]]:2 : 0
|
||||
let y = try x[] // CHECK-NEXT: [[@LINE]]:18 -> [[@LINE+1]]:11 : (0 - 1)
|
||||
return y
|
||||
} // CHECK-NEXT: }
|
||||
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors6test18ySiAA1SVKF"
|
||||
func test18(
|
||||
_ x: S
|
||||
) throws -> Int { // CHECK-NEXT: [[@LINE]]:17 -> [[@LINE+3]]:2 : 0
|
||||
let y = try x.throwingMethod() // CHECK-NEXT: [[@LINE]]:33 -> [[@LINE+1]]:11 : (0 - 1)
|
||||
return y
|
||||
} // CHECK-NEXT: }
|
||||
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors6test19SiyKF"
|
||||
func test19() throws -> Int { // CHECK-NEXT: [[@LINE]]:29 -> [[@LINE+3]]:2 : 0
|
||||
let x = try throwingS.throwingMethod() // CHECK-NEXT: [[@LINE]]:24 -> [[@LINE+1]]:11 : (0 - 1)
|
||||
return x // CHECK-NEXT: [[@LINE-1]]:41 -> [[@LINE]]:11 : ((0 - 1) - 2)
|
||||
} // CHECK-NEXT: }
|
||||
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors6test20yyKF"
|
||||
func test20() throws { // CHECK-NEXT: [[@LINE]]:22 -> [[@LINE+5]]:2 : 0
|
||||
takesInts(
|
||||
try throwingFn(), // CHECK-NEXT: [[@LINE]]:21 -> [[@LINE+3]]:2 : (0 - 1)
|
||||
try throwingFn() // CHECK-NEXT: [[@LINE]]:21 -> [[@LINE+2]]:2 : ((0 - 1) - 2)
|
||||
)
|
||||
} // CHECK-NEXT: }
|
||||
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}} "$s15coverage_errors6test21SiyKF"
|
||||
func test21() throws -> Int { // CHECK-NEXT: [[@LINE]]:29 -> [[@LINE+3]]:2 : 0
|
||||
try { throw SomeErr.Err1 }() // CHECK-NEXT: [[@LINE]]:31 -> [[@LINE+1]]:11 : (0 - 1)
|
||||
return 1
|
||||
} // CHECK-NEXT: }
|
||||
|
||||
struct TestInit {
|
||||
// CHECK-LABEL: sil_coverage_map {{.*}}// coverage_errors.TestInit.init() -> coverage_errors.TestInit
|
||||
init() { // CHECK-NEXT: [[@LINE]]:10 -> [[@LINE+5]]:4 : 0
|
||||
do { // CHECK-NEXT: [[@LINE]]:8 -> [[@LINE+2]]:6 : 0
|
||||
throw SomeErr.Err1
|
||||
} catch { // CHECK-NEXT: [[@LINE]]:13 -> [[@LINE+1]]:6 : 1
|
||||
} // CHECK-NEXT: [[@LINE]]:6 -> [[@LINE+1]]:4 : 0
|
||||
} // CHECK-NEXT: }
|
||||
}
|
||||
|
||||
128
test/Profiler/coverage_errors_exec.swift
Normal file
128
test/Profiler/coverage_errors_exec.swift
Normal file
@@ -0,0 +1,128 @@
|
||||
// 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 show %t/main -instr-profile=%t/default.profdata | %FileCheck %s
|
||||
|
||||
// REQUIRES: profile_runtime
|
||||
// REQUIRES: executable_test
|
||||
// REQUIRES: OS=macosx
|
||||
|
||||
struct Err: Error {}
|
||||
|
||||
struct S {
|
||||
static subscript() -> Int {
|
||||
get throws { throw Err() }
|
||||
}
|
||||
func throwingMethod() throws -> Int { throw Err() }
|
||||
}
|
||||
|
||||
func noThrowingFn() throws -> Int { 0 }
|
||||
func throwingFn() throws -> Int { throw Err() }
|
||||
|
||||
var throwingProp: Int {
|
||||
get throws { throw Err() }
|
||||
}
|
||||
|
||||
var throwingS: S {
|
||||
get throws { throw Err() }
|
||||
}
|
||||
|
||||
var noThrowingS: S {
|
||||
get throws { S() }
|
||||
}
|
||||
|
||||
func test1() -> Int { // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
do { // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
let x = try throwingFn() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
return x // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
} catch { // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
return 0 // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
} // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
} // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
_ = test1() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
|
||||
func test2() throws -> Int { // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
let x = try throwingFn() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
return x // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
} // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
_ = try? test2() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
|
||||
func test3() throws -> Int { // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
let x = try throwingProp // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
return x // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
} // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
_ = try? test3() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
|
||||
func test4() throws -> Int { // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
let x = try S[] // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
return x // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
} // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
_ = try? test4() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
|
||||
// Note we don't emit a region after the call since it would be empty.
|
||||
func test5() -> Int { // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
do { // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
return try throwingFn() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
} catch { // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
return 0 // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
} // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
} // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
_ = test5() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
|
||||
func takesInts(_ x: Int, _ y: Int) {} // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
|
||||
// The throwing expr is nested here, the region starts after the throwing expr.
|
||||
func test6() throws { // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
takesInts(try throwingFn(), 0) // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
} // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
try? test6() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
|
||||
func test7() throws { // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
takesInts( // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
try throwingFn(), // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
0 // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
) // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
} // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
try? test7() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
|
||||
func test8() throws -> Int { // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
let x = try throwingS.throwingMethod() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
return x // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
} // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
_ = try? test8()
|
||||
|
||||
func test9() throws -> Int { // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
let x = try noThrowingS.throwingMethod() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
return x // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
} // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
_ = try? test9() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
|
||||
func test10() throws { // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
takesInts( // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
try throwingFn(), // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
try throwingFn() // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
) // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
} // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
try? test10() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
|
||||
func test11() throws { // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
takesInts( // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
try noThrowingFn(), // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
try throwingFn() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
) // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
} // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
try? test11() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
|
||||
func test21() throws -> Int { // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
try { // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
throw Err() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
}() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
return 1 // CHECK: {{ *}}[[@LINE]]|{{ *}}0
|
||||
} // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
_ = try? test21() // CHECK: {{ *}}[[@LINE]]|{{ *}}1
|
||||
@@ -150,7 +150,7 @@ func throwError(_ b: Bool) throws {
|
||||
func catchError(_ b: Bool) -> Int {
|
||||
do {
|
||||
try throwError(b) // CHECK-COV: {{ *}}[[@LINE]]|{{ *}}2
|
||||
} catch { // CHECK-COV: {{ *}}[[@LINE]]|{{ *}}2
|
||||
} catch { // CHECK-COV: {{ *}}[[@LINE]]|{{ *}}1
|
||||
return 1 // CHECK-COV: {{ *}}[[@LINE]]|{{ *}}1
|
||||
} // CHECK-COV: {{ *}}[[@LINE]]|{{ *}}1
|
||||
let _ = 1 + 1 // CHECK-COV: {{ *}}[[@LINE]]|{{ *}}1
|
||||
|
||||
@@ -37,9 +37,10 @@ func basic(a : Int32) {
|
||||
// CHECK: sil hidden [ossa] @[[F_THROWING_NOP:.*throwing_nop.*]] :
|
||||
func throwing_nop() throws {}
|
||||
// CHECK: sil hidden [ossa] @[[F_EXCEPTIONS:.*exceptions.*]] :
|
||||
// CHECK: increment_profiler_counter 0, "{{.*}}instrprof_basic.swift:[[F_EXCEPTIONS]]", num_counters 2, hash
|
||||
// CHECK: increment_profiler_counter 0, "{{.*}}instrprof_basic.swift:[[F_EXCEPTIONS]]", num_counters 3, hash
|
||||
func exceptions() {
|
||||
do {
|
||||
// CHECK: increment_profiler_counter
|
||||
try throwing_nop()
|
||||
} catch {
|
||||
// CHECK: increment_profiler_counter
|
||||
|
||||
Reference in New Issue
Block a user