Skip to content

Commit 376870e

Browse files
committed
[Coverage] Fix handling of return statements within repeat-while loops
In 2a34b7c, we introduced an incorrect test case for return statements within repeat-while loops. Consider this situation: repeat { // Region 1 return } while C1 // Should be "zero", not "Region 1" The fix requires maintaining a stack of active repeat-while loops. This is an accepted idiom (c.f the clang CoverageMapping implementation). To see why a stack is needed, consider: repeat { // Region 1 repeat { // Region 2 if (C1) { // Region 3 return } } while C2 // Should be "Region 2 - *Region 3*", not "Region 2" } while C3 // Should be "Region 1 - *Region 3*", not "Region 1" rdar://problem/24572268
1 parent c165811 commit 376870e

File tree

2 files changed

+35
-4
lines changed

2 files changed

+35
-4
lines changed

lib/SILGen/SILGenProfiling.cpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,9 @@ struct CoverageMapping : public ASTWalker {
229229
/// \brief A stack of currently live regions.
230230
std::vector<SourceMappingRegion> RegionStack;
231231

232+
/// \brief A stack of active repeat-while loops.
233+
std::vector<RepeatWhileStmt *> RepeatWhileStack;
234+
232235
CounterExpr *ExitCounter;
233236

234237
/// \brief Return true if \c Node has an associated counter.
@@ -271,6 +274,16 @@ struct CoverageMapping : public ASTWalker {
271274
Counter = CounterExpr::Add(createCounter(std::move(Counter)), Expr);
272275
}
273276

277+
/// \brief Subtract \c Expr from \c Node's counter.
278+
void subtractFromCounter(ASTNode Node, CounterExpr &Expr) {
279+
CounterExpr &Counter = getCounter(Node);
280+
assert(!Counter.isZero() && "Cannot create a negative counter");
281+
if (const CounterExpr *ReferencedCounter = Counter.getReferencedNode())
282+
Counter = CounterExpr::Sub(*ReferencedCounter, Expr);
283+
else
284+
Counter = CounterExpr::Sub(createCounter(std::move(Counter)), Expr);
285+
}
286+
274287
/// \brief Return the current region's counter.
275288
CounterExpr &getCurrentCounter() { return getRegion().getCounter(); }
276289

@@ -457,6 +470,7 @@ struct CoverageMapping : public ASTWalker {
457470
assignCounter(RWS, CounterExpr::Zero());
458471
CounterExpr &BodyCounter = assignCounter(RWS->getBody());
459472
assignCounter(RWS->getCond(), CounterExpr::Ref(BodyCounter));
473+
RepeatWhileStack.push_back(RWS);
460474

461475
} else if (auto *FS = dyn_cast<ForStmt>(S)) {
462476
assignCounter(FS, CounterExpr::Zero());
@@ -505,6 +519,10 @@ struct CoverageMapping : public ASTWalker {
505519
if (auto *E = getConditionNode(WS->getCond()))
506520
addToCounter(E, getExitCounter());
507521

522+
} else if (auto *RWS = dyn_cast<RepeatWhileStmt>(S)) {
523+
assert(RepeatWhileStack.back() == RWS && "Malformed repeat-while stack");
524+
RepeatWhileStack.pop_back();
525+
508526
} else if (auto *FS = dyn_cast<ForStmt>(S)) {
509527
// Both the condition and the increment are reached through the backedge.
510528
if (Expr *E = FS->getCond().getPtrOrNull())
@@ -527,8 +545,7 @@ struct CoverageMapping : public ASTWalker {
527545
} else if (auto *BS = dyn_cast<BreakStmt>(S)) {
528546
// When we break from a loop, we need to adjust the exit count.
529547
if (auto *RWS = dyn_cast<RepeatWhileStmt>(BS->getTarget())) {
530-
auto CondCount = CounterExpr::Sub(getCounter(RWS), getCurrentCounter());
531-
addToCounter(RWS->getCond(), createCounter(std::move(CondCount)));
548+
subtractFromCounter(RWS->getCond(), getCurrentCounter());
532549
} else if (!isa<SwitchStmt>(BS->getTarget())) {
533550
addToCounter(BS->getTarget(), getCurrentCounter());
534551
}
@@ -548,6 +565,10 @@ struct CoverageMapping : public ASTWalker {
548565
replaceCount(CounterExpr::Ref(getCounter(S)), getEndLoc(S));
549566

550567
} else if (isa<ReturnStmt>(S) || isa<FailStmt>(S) || isa<ThrowStmt>(S)) {
568+
// When we return, we may need to adjust some loop condition counts.
569+
for (auto *RWS : RepeatWhileStack)
570+
subtractFromCounter(RWS->getCond(), getCurrentCounter());
571+
551572
terminateRegion(S);
552573
}
553574
return S;

test/SILGen/coverage_while.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,18 @@ func goo() {
9797
repeat { // CHECK-DAG: [[@LINE]]:10 -> [[@LINE+4]]:4 : [[RWS6:[0-9]+]]
9898
repeat { // CHECK-DAG: [[@LINE]]:12 -> [[@LINE+2]]:6 : [[RWS7:[0-9]+]]
9999
return
100-
} while false // CHECK-DAG: [[@LINE]]:13 -> [[@LINE]]:18 : [[RWS7]]
101-
} while false // CHECK-DAG: [[@LINE]]:11 -> [[@LINE]]:16 : [[RWS6]]
100+
} while false // CHECK-DAG: [[@LINE]]:13 -> [[@LINE]]:18 : zero
101+
} while false // CHECK-DAG: [[@LINE]]:11 -> [[@LINE]]:16 : ([[RWS6]] - [[RWS7]])
102+
103+
repeat { // CHECK-DAG: [[@LINE]]:10 -> [[@LINE+6]]:4 : [[RWS8:[0-9]+]]
104+
repeat { // CHECK-DAG: [[@LINE]]:12 -> [[@LINE+4]]:6 : [[RWS9:[0-9]+]]
105+
if (true) { // CHECK-DAG: [[@LINE]]:17 -> [[@LINE+2]]:8 : [[RET1:[0-9]+]]
106+
return
107+
}
108+
} while false // CHECK-DAG: [[@LINE]]:13 -> [[@LINE]]:18 : ([[RWS9]] - [[RET1]])
109+
} while false // CHECK-DAG: [[@LINE]]:11 -> [[@LINE]]:16 : ([[RWS8]] - [[RET1]])
110+
111+
// TODO(vsk): need tests for fail and throw statements.
102112
}
103113

104114
foo()

0 commit comments

Comments
 (0)