Skip to content

Commit a1d3884

Browse files
authored
Merge pull request #60579 from hamishknight/never-say-never
2 parents a850a01 + d1eb6f9 commit a1d3884

File tree

2 files changed

+123
-8
lines changed

2 files changed

+123
-8
lines changed

lib/SIL/IR/SILProfiler.cpp

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -341,31 +341,65 @@ class CounterExpr {
341341
/// Returns true if this is a Zero node.
342342
bool isZero() const { return K == Kind::Zero; }
343343

344+
/// Returns true if the counter is semantically a Zero node. This considers
345+
/// the simplified version of the counter that has eliminated redundant
346+
/// operations.
347+
bool isSemanticallyZero() const {
348+
// Run the counter through the counter builder to simplify it, using a dummy
349+
// mapping of unique counter indices for each node reference. The value of
350+
// the indices doesn't matter, but we need to ensure that e.g subtraction
351+
// of a node from itself cancels out.
352+
llvm::coverage::CounterExpressionBuilder Builder;
353+
llvm::DenseMap<ASTNode, unsigned> DummyIndices;
354+
unsigned LastIdx = 0;
355+
auto Counter = expand(Builder, [&](auto Node) {
356+
if (!DummyIndices.count(Node)) {
357+
DummyIndices[Node] = LastIdx;
358+
LastIdx += 1;
359+
}
360+
return DummyIndices[Node];
361+
});
362+
return Counter.isZero();
363+
}
364+
344365
/// Expand this node into an llvm::coverage::Counter.
345366
///
346367
/// Updates \c Builder with any expressions that are needed to represent this
347368
/// counter.
348369
llvm::coverage::Counter
349370
expand(llvm::coverage::CounterExpressionBuilder &Builder,
350-
llvm::DenseMap<ASTNode, unsigned> &Counters) const {
371+
llvm::function_ref<unsigned(ASTNode)> GetCounterIdx) const {
351372
switch (K) {
352373
case Kind::Zero:
353374
return llvm::coverage::Counter::getZero();
354375
case Kind::Node:
355-
return llvm::coverage::Counter::getCounter(Counters[Node]);
376+
return llvm::coverage::Counter::getCounter(GetCounterIdx(Node));
356377
case Kind::Add:
357-
return Builder.add(LHS->expand(Builder, Counters),
358-
RHS->expand(Builder, Counters));
378+
return Builder.add(LHS->expand(Builder, GetCounterIdx),
379+
RHS->expand(Builder, GetCounterIdx));
359380
case Kind::Sub:
360-
return Builder.subtract(LHS->expand(Builder, Counters),
361-
RHS->expand(Builder, Counters));
381+
return Builder.subtract(LHS->expand(Builder, GetCounterIdx),
382+
RHS->expand(Builder, GetCounterIdx));
362383
case Kind::Ref:
363-
return LHS->expand(Builder, Counters);
384+
return LHS->expand(Builder, GetCounterIdx);
364385
}
365386

366387
llvm_unreachable("Unhandled Kind in switch.");
367388
}
368389

390+
/// Expand this node into an llvm::coverage::Counter.
391+
///
392+
/// Updates \c Builder with any expressions that are needed to represent this
393+
/// counter.
394+
llvm::coverage::Counter
395+
expand(llvm::coverage::CounterExpressionBuilder &Builder,
396+
const llvm::DenseMap<ASTNode, unsigned> &Counters) const {
397+
return expand(Builder, [&](auto Node) {
398+
// FIXME: We ought to assert that the node is present.
399+
return Counters.lookup(Node);
400+
});
401+
}
402+
369403
void print(raw_ostream &OS) const {
370404
switch (K) {
371405
case Kind::Zero:
@@ -769,7 +803,17 @@ struct CoverageMapping : public ASTWalker {
769803
if (ControlFlowAdjust)
770804
Count = &createCounter(CounterExpr::Sub(*Count, *ControlFlowAdjust));
771805

772-
RegionStack.emplace_back(ASTNode(), *Count, getEndLoc(Scope), None);
806+
if (Count->isSemanticallyZero()) {
807+
// If the counter is semantically zero, form an 'incomplete' region with
808+
// no starting location. This prevents forming unreachable regions unless
809+
// there is a following statement or expression to extend the region.
810+
RegionStack.emplace_back(ASTNode(), *Count, None, None);
811+
} else {
812+
// Otherwise, we have a non-zero counter, so form a new region starting
813+
// at the end of the previous scope. This ensures the user covers both
814+
// branches of a condition.
815+
RegionStack.emplace_back(ASTNode(), *Count, getEndLoc(Scope), None);
816+
}
773817
}
774818

775819
/// Push a region covering \c Node onto the stack.

test/Profiler/coverage_if.swift

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,74 @@ func foo(x : Bool) { // CHECK: [[@LINE]]:20 -> {{[0-9]+}}:2 : 0
1717

1818
foo(x: true);
1919
foo(x: false);
20+
21+
// rdar://29390569 – Make sure we don't add a spurious unreachable empty region.
22+
23+
// CHECK-LABEL: sil_coverage_map {{.*}}// coverage_if.foo1
24+
func foo1() -> Int { // CHECK: [[@LINE]]:20 -> {{[0-9]+}}:2 : 0
25+
if .random() { // CHECK: [[@LINE]]:6 -> [[@LINE]]:15 : 0
26+
return 0 // CHECK: [[@LINE-1]]:16 -> [[@LINE+2]]:4 : 1
27+
// CHECK: [[@LINE+1]]:4 -> {{[0-9]+}}:2 : (0 - 1)
28+
} else if .random() { // CHECK: [[@LINE]]:13 -> [[@LINE]]:22 : (0 - 1)
29+
return 1 // CHECK: [[@LINE-1]]:23 -> [[@LINE+1]]:4 : 2
30+
} else { // CHECK: [[@LINE]]:4 -> {{[0-9]+}}:2 : ((0 - 1) - 2)
31+
return 2 // CHECK: [[@LINE-1]]:10 -> [[@LINE+1]]:4 : ((0 - 1) - 2)
32+
} // CHECK-NOT: zero
33+
}
34+
35+
// ...but we will add an unreachable region if you write code there.
36+
// CHECK-LABEL: sil_coverage_map {{.*}}// coverage_if.foo2
37+
func foo2() -> Int {
38+
if .random() {
39+
return 0
40+
} else if .random() {
41+
return 1
42+
} else {
43+
return 2
44+
}
45+
_ = foo1() // CHECK: [[@LINE]]:3 -> [[@LINE+1]]:2 : zero
46+
}
47+
48+
// Make sure we don't add unreachable regions for a labeled jump either.
49+
// CHECK-LABEL: sil_coverage_map {{.*}}// coverage_if.foo3
50+
func foo3() -> Int { // CHECK: [[@LINE]]:20 -> {{[0-9]+}}:2 : 0
51+
x: do { // CHECK: [[@LINE]]:9 -> [[@LINE+6]]:4 : 0
52+
if .random() { // CHECK: [[@LINE]]:8 -> [[@LINE]]:17 : 0
53+
return 0 // CHECK: [[@LINE-1]]:18 -> [[@LINE+1]]:6 : 1
54+
} else { // CHECK: [[@LINE]]:6 -> [[@LINE+3]]:4 : (0 - 1)
55+
break x // CHECK: [[@LINE-1]]:12 -> [[@LINE+1]]:6 : (0 - 1)
56+
} // CHECK-NOT: zero
57+
}
58+
return 2
59+
}
60+
// CHECK-LABEL: sil_coverage_map {{.*}}// coverage_if.foo4
61+
func foo4() -> Int {
62+
x: do {
63+
if .random() {
64+
return 0
65+
} else {
66+
break x
67+
}
68+
let y = 0 // CHECK: [[@LINE]]:13 -> [[@LINE+1]]:4 : zero
69+
}
70+
return 2
71+
}
72+
73+
// CHECK-LABEL: sil_coverage_map {{.*}}// coverage_if.foo5
74+
func foo5() throws -> Int { // CHECK: [[@LINE]]:27 -> {{[0-9]+}}:2 : 0
75+
if .random() { // CHECK: [[@LINE]]:6 -> [[@LINE]]:15 : 0
76+
return 0 // CHECK: [[@LINE-1]]:16 -> [[@LINE+1]]:4 : 1
77+
} else { // CHECK: [[@LINE]]:4 -> {{[0-9]+}}:2 : (0 - 1)
78+
struct S: Error {} // CHECK: [[@LINE-1]]:10 -> [[@LINE+2]]:4 : (0 - 1)
79+
throw S()
80+
} // CHECK-NOT: zero
81+
}
82+
83+
// CHECK-LABEL: sil_coverage_map {{.*}}// coverage_if.foo6
84+
func foo6(_ x: Int?) -> Int? { // CHECK: [[@LINE]]:30 -> {{[0-9]+}}:2 : 0
85+
if let x = x {
86+
return x // CHECK: [[@LINE-1]]:16 -> [[@LINE+1]]:4 : 1
87+
} else { // CHECK: [[@LINE]]:4 -> {{[0-9]+}}:2 : (0 - 1)
88+
return nil // CHECK: [[@LINE-1]]:10 -> [[@LINE+1]]:4 : (0 - 1)
89+
} // CHECK-NOT: zero
90+
}

0 commit comments

Comments
 (0)