ankurrj7 wrote:

Adding a more detailed summary of the scope and stack, since this is one part 
of a larger coverage-mapping cleanup.

## What this PR fixes

This PR fixes source-based coverage mapping when a statement is followed by 
code that is unreachable because the statement inevitably sinks control flow, 
but the immediate call expression is not itself explicitly marked `noreturn`.

The motivating case is a wrapper or call chain like:

```c
#include <stdio.h>
#include <stdlib.h>

static void die(void) {
  exit(0);
}

static void wrapper(void) {
  die();
  puts("after die");       // should be 0
}

int main(void) {
  wrapper();
  puts("after wrapper");   // should be 0
}
```

Before this change, coverage mapping could keep the post-call source range in 
the same live region as the call, so `puts("after die")` or `puts("after 
wrapper")` could be shown as covered even though the program cannot reach those 
lines.

This is a coverage mapping fix, not a runtime/code-generation behavior change. 
The generated program behavior is unchanged; the change is in how Clang decides 
where to terminate source coverage regions.

## Fix approach

The patch extends the existing `noreturn` handling in `CoverageMappingGen.cpp` 
with conservative CFG-based sink detection:

- Direct `noreturn` calls still terminate the current coverage region as before.
- A direct callee with a visible body is also treated as terminating when its 
CFG cannot reach a normal exit.
- This handles simple wrapper chains such as `wrapper -> die -> exit`.
- Infinite loops with no normal exit are detected through statement/CFG 
analysis, so code after the loop is mapped as unreachable.
- Function sink results are cached and guarded against recursion with a 
`Computing` state.
- Virtual dispatch remains conservative: a virtual call is not treated as 
sinking just because the statically visible method body sinks, since a runtime 
override may return normally.

Example of the intended result:

```text
wrapper();               // 1
puts("after wrapper");   // 0
```

and for an infinite loop:

```c++
int f() {
  while (true) {
  }
  return 0;              // should be 0 / unreachable
}
```

## Tests run

- `git diff --check`
- `git-clang-format --diff origin/main HEAD --extensions cpp -- 
clang/lib/CodeGen/CoverageMappingGen.cpp 
clang/test/CoverageMapping/terminate-statements.cpp --diff_from_common_commit`
- Syntax-only compile of `clang/lib/CodeGen/CoverageMappingGen.cpp`
- Coverage-mapping checks in 
`clang/test/CoverageMapping/terminate-statements.cpp` for:
  - direct sink calls
  - nested sink-call chains
  - infinite `while` loops
  - infinite `for` loops
  - virtual-dispatch non-regression
- Runtime coverage repro for `main -> wrapper -> exit(0)` style code
- Runtime coverage repro for the virtual-dispatch conservative case
- Coverage regression suites on the local combined stack:
  - `clang/test/CoverageMapping`: 75 passed
  - `clang/test/Coverage`: 15 passed

## Stack

This PR is PR1 in the stack and is intentionally limited to CFG-proven sink 
termination.

- PR1 / this PR: `cov-sink-termination`
  Fixes direct/nested sink calls and infinite-loop sink coverage mapping.
- PR2 branch: `cov-explicit-skip-counters`
  Follow-up for explicit skip-counter handling around abnormal exits and 
`returns_twice` / `setjmp`-style control flow.
- PR3 branch: `cov-static-fp-sink`
  Follow-up for simple static function-pointer sink cases.

At the moment, only this PR appears to be open upstream; the PR2 and PR3 
branches exist on the fork as stacked follow-ups.


https://github.com/llvm/llvm-project/pull/200190
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to