Skip to content

[Coverage] Fix mapping for do-while loops with terminating statements #139777

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 19, 2025

Conversation

justincady
Copy link
Contributor

The current region mapping for do-while loops that contain statements
such as break or continue results in inaccurate line coverage reports
for the line following the loop.

This change handles terminating statements the same way that other loop
constructs do, correcting the region mapping for accurate reports. It
also fixes a fragile test relying on exact line numbers.

Fixes #139122

@justincady justincady marked this pull request as ready for review May 13, 2025 19:19
@llvmbot llvmbot added clang Clang issues not falling into any other category compiler-rt clang:codegen IR generation bugs: mangling, exceptions, etc. PGO Profile Guided Optimizations labels May 13, 2025
@justincady justincady requested a review from MaskRay May 13, 2025 19:20
@llvmbot
Copy link
Member

llvmbot commented May 13, 2025

@llvm/pr-subscribers-clang-codegen

@llvm/pr-subscribers-pgo

Author: Justin Cady (justincady)

Changes

The current region mapping for do-while loops that contain statements
such as break or continue results in inaccurate line coverage reports
for the line following the loop.

This change handles terminating statements the same way that other loop
constructs do, correcting the region mapping for accurate reports. It
also fixes a fragile test relying on exact line numbers.

Fixes #139122


Full diff: https://github.com/llvm/llvm-project/pull/139777.diff

3 Files Affected:

  • (modified) clang/lib/CodeGen/CoverageMappingGen.cpp (+2-3)
  • (modified) clang/test/CoverageMapping/terminate-statements.cpp (+12-1)
  • (added) compiler-rt/test/profile/Linux/coverage-do-while.c (+21)
diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp
index 73811d15979d5..6170dc7f59107 100644
--- a/clang/lib/CodeGen/CoverageMappingGen.cpp
+++ b/clang/lib/CodeGen/CoverageMappingGen.cpp
@@ -1703,14 +1703,13 @@ struct CounterCoverageMappingBuilder
     if (!IsCounterEqual(OutCount, ParentCount)) {
       pushRegion(OutCount);
       GapRegionCounter = OutCount;
+      if (BodyHasTerminateStmt)
+        HasTerminateStmt = true;
     }
 
     // Create Branch Region around condition.
     if (!llvm::EnableSingleByteCoverage)
       createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped);
-
-    if (BodyHasTerminateStmt)
-      HasTerminateStmt = true;
   }
 
   void VisitForStmt(const ForStmt *S) {
diff --git a/clang/test/CoverageMapping/terminate-statements.cpp b/clang/test/CoverageMapping/terminate-statements.cpp
index ef6b6fd82687d..93d655794ccf5 100644
--- a/clang/test/CoverageMapping/terminate-statements.cpp
+++ b/clang/test/CoverageMapping/terminate-statements.cpp
@@ -348,10 +348,20 @@ int elsecondnoret(void) {
 
 // CHECK-LABEL: _Z18statementexprnoretb:
 int statementexprnoret(bool crash) {
-  int rc = ({ if (crash) abort(); 0; }); // CHECK: File 0, 351:35 -> 352:12 = (#0 - #1)
+  int rc = ({ if (crash) abort(); 0; }); // CHECK: File 0, [[@LINE]]:35 -> [[@LINE+1]]:12 = (#0 - #1)
   return rc;                             // CHECK-NOT: Gap
 }
 
+// CHECK-LABEL: _Z13do_with_breaki:
+int do_with_break(int n) {
+  do {
+    if (n == 87) {
+      break;
+    }               // CHECK: File 0, [[@LINE-2]]:18 -> [[@LINE]]:6 = #2
+  } while (0);      // CHECK: File 0, [[@LINE]]:12 -> [[@LINE]]:13 = ((#0 + #1) - #2)
+  return 0;         // CHECK-NOT: Gap,File 0, [[@LINE-1]]:15
+}
+
 int main() {
   foo(0);
   foo(1);
@@ -375,5 +385,6 @@ int main() {
   abstractcondnoret();
   elsecondnoret();
   statementexprnoret(false);
+  do_with_break(0);
   return 0;
 }
diff --git a/compiler-rt/test/profile/Linux/coverage-do-while.c b/compiler-rt/test/profile/Linux/coverage-do-while.c
new file mode 100644
index 0000000000000..9e112cbc4c6a3
--- /dev/null
+++ b/compiler-rt/test/profile/Linux/coverage-do-while.c
@@ -0,0 +1,21 @@
+// REQUIRES: lld-available
+// XFAIL: powerpc64-target-arch
+
+// RUN: %clangxx_profgen -fuse-ld=lld -fcoverage-mapping -o %t %s
+// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t
+// RUN: llvm-profdata merge -o %t.profdata %t.profraw
+// RUN: llvm-cov show %t -instr-profile=%t.profdata 2>&1 | FileCheck %s
+
+#include <stdio.h>
+
+// clang-format off
+int main(int argc, char **argv) {           // CHECK:  [[@LINE]]| 1|int main(
+  do {                                      // CHECK:  [[@LINE]]| 1|  do {
+    if (argc == 87) {                       // CHECK:  [[@LINE]]| 1|    if (argc
+      break;                                // CHECK:  [[@LINE]]| 0|      break
+    }                                       // CHECK:  [[@LINE]]| 0|    }
+  } while (0);                              // CHECK:  [[@LINE]]| 1|  } while
+  printf("coverage after do is present\n"); // CHECK:  [[@LINE]]| 1|  printf(
+  return 0;                                 // CHECK:  [[@LINE]]| 1|  return
+}                                           // CHECK:  [[@LINE]]| 1|}
+// clang-format on

@llvmbot
Copy link
Member

llvmbot commented May 13, 2025

@llvm/pr-subscribers-clang

Author: Justin Cady (justincady)

Changes

The current region mapping for do-while loops that contain statements
such as break or continue results in inaccurate line coverage reports
for the line following the loop.

This change handles terminating statements the same way that other loop
constructs do, correcting the region mapping for accurate reports. It
also fixes a fragile test relying on exact line numbers.

Fixes #139122


Full diff: https://github.com/llvm/llvm-project/pull/139777.diff

3 Files Affected:

  • (modified) clang/lib/CodeGen/CoverageMappingGen.cpp (+2-3)
  • (modified) clang/test/CoverageMapping/terminate-statements.cpp (+12-1)
  • (added) compiler-rt/test/profile/Linux/coverage-do-while.c (+21)
diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp
index 73811d15979d5..6170dc7f59107 100644
--- a/clang/lib/CodeGen/CoverageMappingGen.cpp
+++ b/clang/lib/CodeGen/CoverageMappingGen.cpp
@@ -1703,14 +1703,13 @@ struct CounterCoverageMappingBuilder
     if (!IsCounterEqual(OutCount, ParentCount)) {
       pushRegion(OutCount);
       GapRegionCounter = OutCount;
+      if (BodyHasTerminateStmt)
+        HasTerminateStmt = true;
     }
 
     // Create Branch Region around condition.
     if (!llvm::EnableSingleByteCoverage)
       createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped);
-
-    if (BodyHasTerminateStmt)
-      HasTerminateStmt = true;
   }
 
   void VisitForStmt(const ForStmt *S) {
diff --git a/clang/test/CoverageMapping/terminate-statements.cpp b/clang/test/CoverageMapping/terminate-statements.cpp
index ef6b6fd82687d..93d655794ccf5 100644
--- a/clang/test/CoverageMapping/terminate-statements.cpp
+++ b/clang/test/CoverageMapping/terminate-statements.cpp
@@ -348,10 +348,20 @@ int elsecondnoret(void) {
 
 // CHECK-LABEL: _Z18statementexprnoretb:
 int statementexprnoret(bool crash) {
-  int rc = ({ if (crash) abort(); 0; }); // CHECK: File 0, 351:35 -> 352:12 = (#0 - #1)
+  int rc = ({ if (crash) abort(); 0; }); // CHECK: File 0, [[@LINE]]:35 -> [[@LINE+1]]:12 = (#0 - #1)
   return rc;                             // CHECK-NOT: Gap
 }
 
+// CHECK-LABEL: _Z13do_with_breaki:
+int do_with_break(int n) {
+  do {
+    if (n == 87) {
+      break;
+    }               // CHECK: File 0, [[@LINE-2]]:18 -> [[@LINE]]:6 = #2
+  } while (0);      // CHECK: File 0, [[@LINE]]:12 -> [[@LINE]]:13 = ((#0 + #1) - #2)
+  return 0;         // CHECK-NOT: Gap,File 0, [[@LINE-1]]:15
+}
+
 int main() {
   foo(0);
   foo(1);
@@ -375,5 +385,6 @@ int main() {
   abstractcondnoret();
   elsecondnoret();
   statementexprnoret(false);
+  do_with_break(0);
   return 0;
 }
diff --git a/compiler-rt/test/profile/Linux/coverage-do-while.c b/compiler-rt/test/profile/Linux/coverage-do-while.c
new file mode 100644
index 0000000000000..9e112cbc4c6a3
--- /dev/null
+++ b/compiler-rt/test/profile/Linux/coverage-do-while.c
@@ -0,0 +1,21 @@
+// REQUIRES: lld-available
+// XFAIL: powerpc64-target-arch
+
+// RUN: %clangxx_profgen -fuse-ld=lld -fcoverage-mapping -o %t %s
+// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t
+// RUN: llvm-profdata merge -o %t.profdata %t.profraw
+// RUN: llvm-cov show %t -instr-profile=%t.profdata 2>&1 | FileCheck %s
+
+#include <stdio.h>
+
+// clang-format off
+int main(int argc, char **argv) {           // CHECK:  [[@LINE]]| 1|int main(
+  do {                                      // CHECK:  [[@LINE]]| 1|  do {
+    if (argc == 87) {                       // CHECK:  [[@LINE]]| 1|    if (argc
+      break;                                // CHECK:  [[@LINE]]| 0|      break
+    }                                       // CHECK:  [[@LINE]]| 0|    }
+  } while (0);                              // CHECK:  [[@LINE]]| 1|  } while
+  printf("coverage after do is present\n"); // CHECK:  [[@LINE]]| 1|  printf(
+  return 0;                                 // CHECK:  [[@LINE]]| 1|  return
+}                                           // CHECK:  [[@LINE]]| 1|}
+// clang-format on

@justincady justincady requested review from ZequanWu and chapuni May 13, 2025 19:20
@evodius96
Copy link
Contributor

Thank you for looking at this!

@justincady justincady requested a review from evodius96 May 14, 2025 14:02
@justincady
Copy link
Contributor Author

justincady commented May 19, 2025

Ping. :)

cc @ZequanWu @MaskRay @chapuni @evodius96

Copy link
Contributor

@ZequanWu ZequanWu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for fixing it.

The current region mapping for do-while loops that contain statements
such as break or continue results in inaccurate line coverage reports
for the line following the loop.

This change handles terminating statements the same way that other loop
constructs do, correcting the region mapping for accurate reports. It
also fixes a fragile test relying on exact line numbers.

Fixes llvm#139122
@hiraditya
Copy link
Collaborator

LGTM!

@justincady justincady merged commit 025639b into llvm:main May 19, 2025
11 checks passed
@justincady justincady deleted the do-coverage branch May 20, 2025 15:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen IR generation bugs: mangling, exceptions, etc. clang Clang issues not falling into any other category compiler-rt PGO Profile Guided Optimizations
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[coverage] incorrect coverage when a do-while contains break or continue statements
5 participants