@@ -422,6 +422,13 @@ class CounterExpr {
422
422
// / Returns true if this is a zero counter.
423
423
bool isZero () const { return Counter.isZero (); }
424
424
425
+ friend bool operator ==(const CounterExpr &LHS, const CounterExpr &RHS) {
426
+ return LHS.Counter == RHS.Counter ;
427
+ }
428
+ friend bool operator !=(const CounterExpr &LHS, const CounterExpr &RHS) {
429
+ return !(LHS == RHS);
430
+ }
431
+
425
432
llvm::coverage::Counter getLLVMCounter () const { return Counter; }
426
433
427
434
void print (raw_ostream &OS,
@@ -476,8 +483,9 @@ class SourceMappingRegion {
476
483
}
477
484
478
485
SourceMappingRegion (Kind RegionKind, ASTNode Node, SourceRange Range,
486
+ llvm::Optional<CounterExpr> Counter,
479
487
const SourceManager &SM)
480
- : RegionKind(RegionKind), Node(Node) {
488
+ : RegionKind(RegionKind), Node(Node), Counter(Counter) {
481
489
assert (Range.isValid ());
482
490
StartLoc = Range.Start ;
483
491
EndLoc = Lexer::getLocForEndOfToken (SM, Range.End );
@@ -492,16 +500,18 @@ class SourceMappingRegion {
492
500
493
501
// Note we don't store counters for nodes, as we need to be able to fix them
494
502
// up later.
495
- return SourceMappingRegion (Kind::Node, Node, Range, SM);
503
+ return SourceMappingRegion (Kind::Node, Node, Range, /* Counter*/ llvm::None,
504
+ SM);
496
505
}
497
506
498
507
// / Create a source region for an ASTNode that is only present for scoping of
499
508
// / child regions, and doesn't need to be included in the resulting set of
500
509
// / regions.
501
- static SourceMappingRegion scopingOnly (ASTNode Node,
502
- const SourceManager &SM) {
510
+ static SourceMappingRegion
511
+ scopingOnly (ASTNode Node, const SourceManager &SM,
512
+ llvm::Optional<CounterExpr> Counter = llvm::None) {
503
513
return SourceMappingRegion (Kind::ScopingOnly, Node, Node.getSourceRange (),
504
- SM);
514
+ Counter, SM);
505
515
}
506
516
507
517
// / Create a refined region for a given counter.
@@ -967,6 +977,14 @@ struct CoverageMapping : public ASTWalker {
967
977
return ;
968
978
}
969
979
980
+ // Don't apply exit adjustments to if statement branches, they should
981
+ // be handled at the end of the statement. This avoids creating awkward
982
+ // overlapping exit regions for each branch, and ensures 'break'
983
+ // statements only have their jump counted once for the entire
984
+ // statement.
985
+ if (isa<IfStmt>(ParentStmt))
986
+ return ;
987
+
970
988
if (auto *LS = dyn_cast<LabeledStmt>(ParentStmt))
971
989
JumpsToLabel = getCounter (LS);
972
990
}
@@ -1225,7 +1243,7 @@ struct CoverageMapping : public ASTWalker {
1225
1243
// it by break statements.
1226
1244
assignCounter (IS, CounterExpr::Zero ());
1227
1245
1228
- // FIXME: This is a redundant region.
1246
+ // FIXME: This is a redundant region for non else-ifs .
1229
1247
if (auto *Cond = getConditionNode (IS->getCond ()))
1230
1248
assignCounter (Cond, getCurrentCounter ());
1231
1249
@@ -1244,13 +1262,68 @@ struct CoverageMapping : public ASTWalker {
1244
1262
// terms of it.
1245
1263
auto ThenCounter = assignKnownCounter (IS->getThenStmt ());
1246
1264
IS->getThenStmt ()->walk (*this );
1265
+ auto ThenDelta =
1266
+ CounterExpr::Sub (ThenCounter, getExitCounter (), CounterBuilder);
1247
1267
1268
+ llvm::Optional<CounterExpr> ElseDelta;
1248
1269
if (auto *Else = IS->getElseStmt ()) {
1249
1270
auto ElseCounter = CounterExpr::Sub (ParentCounter, ThenCounter,
1250
1271
CounterBuilder);
1251
- assignCounter (Else, ElseCounter);
1272
+ // We handle `else if` and `else` slightly differently here. For
1273
+ // `else` we have a BraceStmt, and can use the existing scoping logic
1274
+ // to handle calculating the exit count. For `else if`, we need to
1275
+ // set up a new scope to contain the child `if` statement, effectively
1276
+ // we treat:
1277
+ //
1278
+ // if .random() {
1279
+ // } else if .random() {
1280
+ // } else {
1281
+ // }
1282
+ //
1283
+ // the same as:
1284
+ //
1285
+ // if .random() {
1286
+ // } else {
1287
+ // if .random() {
1288
+ // } else {
1289
+ // }
1290
+ // }
1291
+ //
1292
+ // This ensures we assign a correct counter to the `else if`
1293
+ // condition, and allows us to compute the exit count correctly. We
1294
+ // don't need the fake `else` scope to be included in the resulting
1295
+ // set of regions, so we mark it scoping-only.
1296
+ if (isa<BraceStmt>(Else)) {
1297
+ assignCounter (Else, ElseCounter);
1298
+ } else {
1299
+ pushRegion (SourceMappingRegion::scopingOnly (Else, SM, ElseCounter));
1300
+ }
1252
1301
Else->walk (*this );
1302
+
1303
+ // Once we've walked the `else`, compute the delta exit count. For
1304
+ // a normal `else` we can use the computed exit count, for an
1305
+ // `else if` we can take the current region count since we don't have
1306
+ // a proper scope. This is a little hacked together, but we'll be able
1307
+ // to do away with all of this once we re-implement as a SILOptimizer
1308
+ // pass.
1309
+ auto AfterElse = isa<BraceStmt>(Else) ? getExitCounter ()
1310
+ : getCurrentCounter ();
1311
+ if (!isa<BraceStmt>(Else))
1312
+ popRegions (Else);
1313
+
1314
+ ElseDelta = CounterExpr::Sub (ElseCounter, AfterElse, CounterBuilder);
1253
1315
}
1316
+ // Compute the exit count following the `if`, taking jumps to the
1317
+ // statement by breaks into account, and the delta of the `then` branch
1318
+ // and `else` branch if we have one.
1319
+ auto AfterIf = getCurrentCounter ();
1320
+ AfterIf = CounterExpr::Add (AfterIf, getCounter (IS), CounterBuilder);
1321
+ AfterIf = CounterExpr::Sub (AfterIf, ThenDelta, CounterBuilder);
1322
+ if (ElseDelta)
1323
+ AfterIf = CounterExpr::Sub (AfterIf, *ElseDelta, CounterBuilder);
1324
+
1325
+ if (AfterIf != getCurrentCounter ())
1326
+ replaceCount (AfterIf, getEndLoc (IS));
1254
1327
}
1255
1328
// Already visited the children.
1256
1329
// FIXME: The ASTWalker should do a post-walk for SkipChildren.
0 commit comments