aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Cady <desk@justincady.com>2025-03-21 11:59:01 -0400
committerGitHub <noreply@github.com>2025-03-21 11:59:01 -0400
commit954a3de7838a8c496ef1d1730127415b3e9786f9 (patch)
tree959eb8b142c182ace01a6c905ffe92fbf71dfb67
parent79079c94699a4210bd500556d2864cbdf4a37e78 (diff)
downloadllvm-954a3de7838a8c496ef1d1730127415b3e9786f9.zip
llvm-954a3de7838a8c496ef1d1730127415b3e9786f9.tar.gz
llvm-954a3de7838a8c496ef1d1730127415b3e9786f9.tar.bz2
Reland [Coverage] Fix region termination for GNU statement expressions (#132222)
Relands #130976 with adjustments to test requirements. Calls to __noreturn__ functions result in region termination for coverage mapping. But this creates incorrect coverage results when __noreturn__ functions (or other constructs that result in region termination) occur within [GNU statement expressions][1]. In this scenario an extra gap region is introduced within VisitStmt, such that if the following line does not introduce a new region it is unconditionally counted as uncovered. This change adjusts the mapping such that terminate statements within statement expressions do not propagate that termination state after the statement expression is processed. [1]: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html Fixes #124296
-rw-r--r--clang/lib/CodeGen/CoverageMappingGen.cpp8
-rw-r--r--clang/test/CoverageMapping/terminate-statements.cpp7
-rw-r--r--compiler-rt/test/profile/Linux/coverage-statement-expression.cpp24
3 files changed, 39 insertions, 0 deletions
diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp
index f091577..73811d1 100644
--- a/clang/lib/CodeGen/CoverageMappingGen.cpp
+++ b/clang/lib/CodeGen/CoverageMappingGen.cpp
@@ -1505,6 +1505,14 @@ struct CounterCoverageMappingBuilder
handleFileExit(getEnd(S));
}
+ void VisitStmtExpr(const StmtExpr *E) {
+ Visit(E->getSubStmt());
+ // Any region termination (such as a noreturn CallExpr) within the statement
+ // expression has been handled by visiting the sub-statement. The visitor
+ // cannot be at a terminate statement leaving the statement expression.
+ HasTerminateStmt = false;
+ }
+
void VisitDecl(const Decl *D) {
Stmt *Body = D->getBody();
diff --git a/clang/test/CoverageMapping/terminate-statements.cpp b/clang/test/CoverageMapping/terminate-statements.cpp
index 0067185..ef6b6fd 100644
--- a/clang/test/CoverageMapping/terminate-statements.cpp
+++ b/clang/test/CoverageMapping/terminate-statements.cpp
@@ -346,6 +346,12 @@ int elsecondnoret(void) {
return 0;
}
+// CHECK-LABEL: _Z18statementexprnoretb:
+int statementexprnoret(bool crash) {
+ int rc = ({ if (crash) abort(); 0; }); // CHECK: File 0, 351:35 -> 352:12 = (#0 - #1)
+ return rc; // CHECK-NOT: Gap
+}
+
int main() {
foo(0);
foo(1);
@@ -368,5 +374,6 @@ int main() {
ornoret();
abstractcondnoret();
elsecondnoret();
+ statementexprnoret(false);
return 0;
}
diff --git a/compiler-rt/test/profile/Linux/coverage-statement-expression.cpp b/compiler-rt/test/profile/Linux/coverage-statement-expression.cpp
new file mode 100644
index 0000000..1930041
--- /dev/null
+++ b/compiler-rt/test/profile/Linux/coverage-statement-expression.cpp
@@ -0,0 +1,24 @@
+// REQUIRES: lld-available
+// XFAIL: powerpc64-target-arch
+
+// RUN: %clangxx_profgen -std=gnu++17 -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
+__attribute__ ((__noreturn__))
+void foo(void) { while (1); } // CHECK: [[@LINE]]| 0|void foo(void)
+_Noreturn void bar(void) { while (1); } // CHECK: [[@LINE]]| 0|_Noreturn void bar(void)
+ // CHECK: [[@LINE]]| |
+int main(int argc, char **argv) { // CHECK: [[@LINE]]| 1|int main(
+ int rc = ({ if (argc > 3) foo(); 0; }); // CHECK: [[@LINE]]| 1| int rc =
+ printf("coverage after foo is present\n"); // CHECK: [[@LINE]]| 1| printf(
+ // CHECK: [[@LINE]]| |
+ int rc2 = ({ if (argc > 3) bar(); 0; }); // CHECK: [[@LINE]]| 1| int rc2 =
+ printf("coverage after bar is present\n"); // CHECK: [[@LINE]]| 1| printf(
+ return rc + rc2; // CHECK: [[@LINE]]| 1| return rc
+} // CHECK: [[@LINE]]| 1|}
+// clang-format on