https://github.com/ankurrj7 created 
https://github.com/llvm/llvm-project/pull/201079

This is a WIP/RFC PR to discuss adding an opt-in source coverage mode that 
tracks whether execution returns to the statement after a call.

The motivating case is code where the terminating operation is not visible at 
the immediate call site. For example, `main()` calls `fun1()`, `fun1()` calls 
`fun2()`, and `fun2()` eventually calls `exit(0)`. In that situation the source 
line containing `fun1()` was reached, but the following statement in `main()` 
was not. Today the existing coverage mapping can keep both in the same region, 
so the post-call statement can be reported as covered even though control never 
reached it.

This patch adds `-fcoverage-call-continuations`. When enabled, Clang emits an 
additional continuation counter after eligible calls and uses that counter 
while building coverage regions. The intent is not to prove that a callee is 
`noreturn`; it is to record the dynamic fact that control actually returned to 
the next source statement.

This is behind a flag because the counter overhead is real. The discussion in 
#179662 also raised whether this should use or extend single-byte counters. I 
think that is worth discussing before treating this as ready for review.

What this currently tries to handle:

- basic post-call continuation coverage
- multi-function terminating chains, including when the final `exit(0)` is not 
in the same function
- calls in loop/branch/logical expression conditions where the branch parent 
count needs to use the post-call continuation count
- `returns_twice` style cases more conservatively
- `musttail` calls are skipped, since there is no normal continuation point 
after the tail call

Validation run locally:

```text
git diff --check origin/main...HEAD
git-clang-format --diff HEAD --binary .../clang-format --extensions cpp,c,h,td 
-- clang/lib/CodeGen/CGExpr.cpp clang/lib/CodeGen/CodeGenPGO.cpp 
clang/lib/CodeGen/CoverageMappingGen.cpp
clang-format --dry-run -Werror 
clang/test/CoverageMapping/call-continuations-tight.c
llvm-lit -sv clang/test/CoverageMapping/call-continuations.c 
clang/test/CoverageMapping/call-continuations-tight.c 
clang/test/CoverageMapping/terminate-statements.cpp
```

I also checked the same style of example against GCC/gcov and ICC classic. Both 
appear to model this as call-continuation/block-edge coverage rather than as 
callee `noreturn` inference.

>From e5cedf34444e2bf026ad30145a0dff1dc28bf53e Mon Sep 17 00:00:00 2001
From: "[email protected]"
 <[email protected]>
Date: Sat, 30 May 2026 15:51:58 +0000
Subject: [PATCH 1/2] [clang][coverage] Add opt-in call continuation counters

---
 clang/include/clang/Basic/CodeGenOptions.def  |   3 +
 clang/include/clang/Options/Options.td        |   7 +
 clang/lib/CodeGen/CGExpr.cpp                  |  62 +++++----
 clang/lib/CodeGen/CodeGenFunction.h           |   1 +
 clang/lib/CodeGen/CodeGenPGO.cpp              | 103 ++++++++++++--
 clang/lib/CodeGen/CodeGenPGO.h                |   3 +
 clang/lib/CodeGen/CoverageMappingGen.cpp      | 127 +++++++++++-------
 clang/lib/CodeGen/CoverageMappingGen.h        |  20 ++-
 clang/lib/Driver/ToolChains/Clang.cpp         |  22 ++-
 clang/lib/Driver/ToolChains/SYCL.cpp          |   5 +-
 .../test/CoverageMapping/call-continuations.c |  32 +++++
 .../test/Driver/coverage-call-continuations.c |   7 +
 12 files changed, 298 insertions(+), 94 deletions(-)
 create mode 100644 clang/test/CoverageMapping/call-continuations.c
 create mode 100644 clang/test/Driver/coverage-call-continuations.c

diff --git a/clang/include/clang/Basic/CodeGenOptions.def 
b/clang/include/clang/Basic/CodeGenOptions.def
index 6cce4ada1dfd1..091f814841777 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -250,6 +250,9 @@ CODEGENOPT(CoverageMapping , 1, 0, Benign) ///< Generate 
coverage mapping region
                                            ///< enable code coverage analysis.
 CODEGENOPT(DumpCoverageMapping , 1, 0, Benign) ///< Dump the generated 
coverage mapping
                                                ///< regions.
+CODEGENOPT(CoverageCallContinuations , 1, 0, Benign) ///< Track coverage after
+                                                     ///< calls with returned
+                                                     ///< from call counters.
 CODEGENOPT(MCDCCoverage , 1, 0, Benign) ///< Enable MC/DC code coverage 
criteria.
 VALUE_CODEGENOPT(MCDCMaxConds, 16, 32767, Benign) ///< MC/DC Maximum 
conditions.
 VALUE_CODEGENOPT(MCDCMaxTVs, 32, 0x7FFFFFFE, Benign) ///< MC/DC Maximum test 
vectors.
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 025e8e7d7d761..9aca148f0129d 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -1857,6 +1857,13 @@ defm coverage_mapping : BoolFOption<"coverage-mapping",
           "Generate coverage mapping to enable code coverage analysis">,
   NegFlag<SetFalse, [], [ClangOption], "Disable code coverage analysis">, 
BothFlags<
           [], [ClangOption, CLOption]>>;
+defm coverage_call_continuations : BoolFOption<"coverage-call-continuations",
+  CodeGenOpts<"CoverageCallContinuations">, DefaultFalse,
+  PosFlag<SetTrue, [], [ClangOption, CC1Option],
+          "Track coverage after calls with returned-from-call counters">,
+  NegFlag<SetFalse, [], [ClangOption],
+          "Disable returned-from-call coverage counters">,
+          BothFlags<[], [ClangOption, CLOption]>>;
 defm mcdc_coverage : BoolFOption<"coverage-mcdc",
   CodeGenOpts<"MCDCCoverage">, DefaultFalse,
   PosFlag<SetTrue, [], [ClangOption, CC1Option],
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 325902f2127bc..0df4940963349 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -6468,37 +6468,43 @@ RValue CodeGenFunction::EmitCallExpr(const CallExpr *E,
     }
   });
 
-  // Builtins never have block type.
-  if (E->getCallee()->getType()->isBlockPointerType())
-    return EmitBlockCallExpr(E, ReturnValue, CallOrInvoke);
-
-  if (const auto *CE = dyn_cast<CXXMemberCallExpr>(E))
-    return EmitCXXMemberCallExpr(CE, ReturnValue, CallOrInvoke);
-
-  if (const auto *CE = dyn_cast<CUDAKernelCallExpr>(E))
-    return EmitCUDAKernelCallExpr(CE, ReturnValue, CallOrInvoke);
-
-  // A CXXOperatorCallExpr is created even for explicit object methods, but
-  // these should be treated like static function call.
-  if (const auto *CE = dyn_cast<CXXOperatorCallExpr>(E))
-    if (const auto *MD =
-            dyn_cast_if_present<CXXMethodDecl>(CE->getCalleeDecl());
-        MD && MD->isImplicitObjectMemberFunction())
-      return EmitCXXOperatorMemberCallExpr(CE, MD, ReturnValue, CallOrInvoke);
-
-  CGCallee callee = EmitCallee(E->getCallee());
+  auto EmitTheCall = [&]() -> RValue {
+    // Builtins never have block type.
+    if (E->getCallee()->getType()->isBlockPointerType())
+      return EmitBlockCallExpr(E, ReturnValue, CallOrInvoke);
+
+    if (const auto *CE = dyn_cast<CXXMemberCallExpr>(E))
+      return EmitCXXMemberCallExpr(CE, ReturnValue, CallOrInvoke);
+
+    if (const auto *CE = dyn_cast<CUDAKernelCallExpr>(E))
+      return EmitCUDAKernelCallExpr(CE, ReturnValue, CallOrInvoke);
+
+    // A CXXOperatorCallExpr is created even for explicit object methods, but
+    // these should be treated like static function call.
+    if (const auto *CE = dyn_cast<CXXOperatorCallExpr>(E))
+      if (const auto *MD =
+              dyn_cast_if_present<CXXMethodDecl>(CE->getCalleeDecl());
+          MD && MD->isImplicitObjectMemberFunction())
+        return EmitCXXOperatorMemberCallExpr(CE, MD, ReturnValue, 
CallOrInvoke);
+
+    CGCallee callee = EmitCallee(E->getCallee());
+
+    if (callee.isBuiltin()) {
+      return EmitBuiltinExpr(callee.getBuiltinDecl(), callee.getBuiltinID(), E,
+                             ReturnValue);
+    }
 
-  if (callee.isBuiltin()) {
-    return EmitBuiltinExpr(callee.getBuiltinDecl(), callee.getBuiltinID(),
-                           E, ReturnValue);
-  }
+    if (callee.isPseudoDestructor()) {
+      return EmitCXXPseudoDestructorExpr(callee.getPseudoDestructorExpr());
+    }
 
-  if (callee.isPseudoDestructor()) {
-    return EmitCXXPseudoDestructorExpr(callee.getPseudoDestructorExpr());
-  }
+    return EmitCall(E->getCallee()->getType(), callee, E, ReturnValue,
+                    /*Chain=*/nullptr, CallOrInvoke);
+  };
 
-  return EmitCall(E->getCallee()->getType(), callee, E, ReturnValue,
-                  /*Chain=*/nullptr, CallOrInvoke);
+  RValue RV = EmitTheCall();
+  incrementCallContinuationProfileCounter(E);
+  return RV;
 }
 
 /// Emit a CallExpr without considering whether it might be a subclass.
diff --git a/clang/lib/CodeGen/CodeGenFunction.h 
b/clang/lib/CodeGen/CodeGenFunction.h
index 721ef6a64e7f9..53a4f3147c72b 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -1700,6 +1700,7 @@ class CodeGenFunction : public CodeGenTypeCache {
   void incrementProfileCounter(CounterForIncrement ExecSkip, const Stmt *S,
                                bool UseBoth = false,
                                llvm::Value *StepV = nullptr);
+  void incrementCallContinuationProfileCounter(const Stmt *S);
 
   bool isMCDCCoverageEnabled() const {
     return (CGM.getCodeGenOpts().hasProfileClangInstr() &&
diff --git a/clang/lib/CodeGen/CodeGenPGO.cpp b/clang/lib/CodeGen/CodeGenPGO.cpp
index 59faa3aef2460..989ede7e26222 100644
--- a/clang/lib/CodeGen/CodeGenPGO.cpp
+++ b/clang/lib/CodeGen/CodeGenPGO.cpp
@@ -17,6 +17,7 @@
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/Basic/DiagnosticFrontend.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/MDBuilder.h"
 #include "llvm/Support/CommandLine.h"
@@ -169,21 +170,31 @@ struct MapRegionCounters : public 
RecursiveASTVisitor<MapRegionCounters> {
   PGOHash Hash;
   /// The map of statements to counters.
   llvm::DenseMap<const Stmt *, CounterPair> &CounterMap;
+  /// The map of calls to counters reached only when the call returns.
+  llvm::DenseMap<const Stmt *, unsigned> *CallContinuationCounterMap;
+  /// Calls that need continuation counters assigned after normal counters.
+  SmallVector<const CallExpr *, 8> CallContinuations;
   /// The state of MC/DC Coverage in this function.
   MCDC::State &MCDCState;
   /// Maximum number of supported MC/DC conditions in a boolean expression.
   unsigned MCDCMaxCond;
+  /// Whether call-continuation coverage counters are enabled.
+  bool CoverageCallContinuations;
   /// The profile version.
   uint64_t ProfileVersion;
   /// Diagnostics Engine used to report warnings.
   DiagnosticsEngine &Diag;
 
-  MapRegionCounters(PGOHashVersion HashVersion, uint64_t ProfileVersion,
-                    llvm::DenseMap<const Stmt *, CounterPair> &CounterMap,
-                    MCDC::State &MCDCState, unsigned MCDCMaxCond,
-                    DiagnosticsEngine &Diag)
+  MapRegionCounters(
+      PGOHashVersion HashVersion, uint64_t ProfileVersion,
+      llvm::DenseMap<const Stmt *, CounterPair> &CounterMap,
+      llvm::DenseMap<const Stmt *, unsigned> *CallContinuationCounterMap,
+      MCDC::State &MCDCState, unsigned MCDCMaxCond,
+      bool CoverageCallContinuations, DiagnosticsEngine &Diag)
       : NextCounter(0), Hash(HashVersion), CounterMap(CounterMap),
+        CallContinuationCounterMap(CallContinuationCounterMap),
         MCDCState(MCDCState), MCDCMaxCond(MCDCMaxCond),
+        CoverageCallContinuations(CoverageCallContinuations),
         ProfileVersion(ProfileVersion), Diag(Diag) {}
 
   // Blocks and lambdas are handled as separate functions, so we need not
@@ -343,6 +354,29 @@ struct MapRegionCounters : public 
RecursiveASTVisitor<MapRegionCounters> {
     return Base::VisitBinaryOperator(S);
   }
 
+  static bool isNoReturnCall(const CallExpr *E) {
+    QualType CalleeType = E->getCallee()->getType();
+    return getFunctionExtInfo(*CalleeType).getNoReturn();
+  }
+
+  /// Calls optionally get a continuation counter which is reached only if the
+  /// call returns normally. This lets coverage mapping distinguish a call 
being
+  /// reached from later source being reached.
+  bool VisitCallExpr(CallExpr *S) {
+    if (CoverageCallContinuations && CallContinuationCounterMap &&
+        !isNoReturnCall(S))
+      CallContinuations.push_back(S);
+    return Base::VisitCallExpr(S);
+  }
+
+  void assignCallContinuationCounters() {
+    if (!CallContinuationCounterMap)
+      return;
+
+    for (const CallExpr *S : CallContinuations)
+      (*CallContinuationCounterMap)[S] = NextCounter++;
+  }
+
   /// Include \p S in the function hash.
   bool VisitStmt(Stmt *S) {
     auto Type = updateCounterMappings(S);
@@ -992,11 +1026,21 @@ void CodeGenPGO::mapRegionCounters(const Decl *D) {
   unsigned MCDCMaxConditions =
       (CGM.getCodeGenOpts().MCDCCoverage ? CGM.getCodeGenOpts().MCDCMaxConds
                                          : 0);
+  bool CoverageCallContinuations =
+      CGM.getCodeGenOpts().CoverageMapping &&
+      CGM.getCodeGenOpts().CoverageCallContinuations;
 
   RegionCounterMap.reset(new llvm::DenseMap<const Stmt *, CounterPair>);
+  if (CoverageCallContinuations)
+    CallContinuationCounterMap.reset(
+        new llvm::DenseMap<const Stmt *, unsigned>);
+  else
+    CallContinuationCounterMap.reset();
   RegionMCDCState.reset(new MCDC::State);
   MapRegionCounters Walker(HashVersion, ProfileVersion, *RegionCounterMap,
-                           *RegionMCDCState, MCDCMaxConditions, 
CGM.getDiags());
+                           CallContinuationCounterMap.get(), *RegionMCDCState,
+                           MCDCMaxConditions, CoverageCallContinuations,
+                           CGM.getDiags());
   if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D))
     Walker.TraverseDecl(const_cast<FunctionDecl *>(FD));
   else if (const ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(D))
@@ -1005,6 +1049,7 @@ void CodeGenPGO::mapRegionCounters(const Decl *D) {
     Walker.TraverseDecl(const_cast<BlockDecl *>(BD));
   else if (const CapturedDecl *CD = dyn_cast_or_null<CapturedDecl>(D))
     Walker.TraverseDecl(const_cast<CapturedDecl *>(CD));
+  Walker.assignCallContinuationCounters();
   assert(Walker.NextCounter > 0 && "no entry counter mapped for decl");
   NumRegionCounters = Walker.NextCounter;
   FunctionHash = Walker.Hash.finalize();
@@ -1041,9 +1086,11 @@ void CodeGenPGO::emitCounterRegionMapping(const Decl *D) 
{
   std::string CoverageMapping;
   llvm::raw_string_ostream OS(CoverageMapping);
   RegionMCDCState->BranchByStmt.clear();
-  CoverageMappingGen MappingGen(
-      *CGM.getCoverageMapping(), CGM.getContext().getSourceManager(),
-      CGM.getLangOpts(), RegionCounterMap.get(), RegionMCDCState.get());
+  CoverageMappingGen MappingGen(*CGM.getCoverageMapping(),
+                                CGM.getContext().getSourceManager(),
+                                CGM.getLangOpts(), RegionCounterMap.get(),
+                                CallContinuationCounterMap.get(),
+                                RegionMCDCState.get(), NumRegionCounters);
   MappingGen.emitCounterMapping(D, OS);
 
   if (CoverageMapping.empty())
@@ -1057,6 +1104,9 @@ void CodeGenPGO::emitCounterRegionMapping(const Decl *D) {
     if (V.Skipped.hasValue())
       MaxNumCounters = std::max(MaxNumCounters, V.Skipped + 1);
   }
+  if (CallContinuationCounterMap)
+    for (const auto &[_, V] : *CallContinuationCounterMap)
+      MaxNumCounters = std::max(MaxNumCounters, V + 1);
   NumRegionCounters = MaxNumCounters;
 
   CGM.getCoverageMapping()->addFunctionMappingRecord(
@@ -1157,6 +1207,34 @@ void CodeGenPGO::emitCounterSetOrIncrement(CGBuilderTy 
&Builder, const Stmt *S,
         CGM.getIntrinsic(llvm::Intrinsic::instrprof_increment_step), Args);
 }
 
+void CodeGenPGO::emitCallContinuationCounter(CGBuilderTy &Builder,
+                                             const Stmt *S) {
+  if (!CallContinuationCounterMap)
+    return;
+
+  auto I = CallContinuationCounterMap->find(S);
+  if (I == CallContinuationCounterMap->end())
+    return;
+
+  if (!Builder.GetInsertBlock())
+    return;
+
+  auto *NormalizedFuncNameVarPtr =
+      llvm::ConstantExpr::getPointerBitCastOrAddrSpaceCast(
+          FuncNameVar, llvm::PointerType::get(CGM.getLLVMContext(), 0));
+
+  llvm::Value *Args[] = {
+      NormalizedFuncNameVarPtr, Builder.getInt64(FunctionHash),
+      Builder.getInt32(NumRegionCounters), Builder.getInt32(I->second)};
+
+  if (llvm::EnableSingleByteCoverage)
+    Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::instrprof_cover),
+                       ArrayRef(Args, 4));
+  else
+    Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::instrprof_increment),
+                       ArrayRef(Args, 4));
+}
+
 bool CodeGenPGO::canEmitMCDCCoverage(const CGBuilderTy &Builder) {
   return (CGM.getCodeGenOpts().hasProfileClangInstr() &&
           CGM.getCodeGenOpts().MCDCCoverage && Builder.GetInsertBlock());
@@ -1487,6 +1565,15 @@ void 
CodeGenFunction::incrementProfileCounter(CounterForIncrement ExecSkip,
   PGO->setCurrentStmt(S);
 }
 
+void CodeGenFunction::incrementCallContinuationProfileCounter(const Stmt *S) {
+  if (CGM.getCodeGenOpts().hasProfileClangInstr() &&
+      !CurFn->hasFnAttribute(llvm::Attribute::NoProfile) &&
+      !CurFn->hasFnAttribute(llvm::Attribute::SkipProfile)) {
+    auto AL = ApplyDebugLocation::CreateArtificial(*this);
+    PGO->emitCallContinuationCounter(Builder, S);
+  }
+}
+
 bool CodeGenFunction::hasSkipCounter(const Stmt *S) const {
   return PGO->hasSkipCounter(S);
 }
diff --git a/clang/lib/CodeGen/CodeGenPGO.h b/clang/lib/CodeGen/CodeGenPGO.h
index e44574b5d209f..613ba8db4bdab 100644
--- a/clang/lib/CodeGen/CodeGenPGO.h
+++ b/clang/lib/CodeGen/CodeGenPGO.h
@@ -36,6 +36,8 @@ class CodeGenPGO {
   unsigned NumRegionCounters;
   uint64_t FunctionHash;
   std::unique_ptr<llvm::DenseMap<const Stmt *, CounterPair>> RegionCounterMap;
+  std::unique_ptr<llvm::DenseMap<const Stmt *, unsigned>>
+      CallContinuationCounterMap;
   std::unique_ptr<llvm::DenseMap<const Stmt *, uint64_t>> StmtCountMap;
   std::unique_ptr<llvm::InstrProfRecord> ProfRecord;
   std::unique_ptr<MCDC::State> RegionMCDCState;
@@ -128,6 +130,7 @@ class CodeGenPGO {
   void emitCounterSetOrIncrement(CGBuilderTy &Builder, const Stmt *S,
                                  bool UseFalsePath, bool UseBoth,
                                  llvm::Value *StepV);
+  void emitCallContinuationCounter(CGBuilderTy &Builder, const Stmt *S);
   void emitMCDCTestVectorBitmapUpdate(CGBuilderTy &Builder, const Expr *S,
                                       CodeGenFunction &CGF);
   void emitMCDCParameters(CGBuilderTy &Builder);
diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp 
b/clang/lib/CodeGen/CoverageMappingGen.cpp
index c90afacbde293..1865a4f4d1cb1 100644
--- a/clang/lib/CodeGen/CoverageMappingGen.cpp
+++ b/clang/lib/CodeGen/CoverageMappingGen.cpp
@@ -914,6 +914,9 @@ struct CounterCoverageMappingBuilder
   /// The map of statements to count values.
   llvm::DenseMap<const Stmt *, CounterPair> &CounterMap;
 
+  /// The map of calls to counters reached only when the call returns.
+  llvm::DenseMap<const Stmt *, unsigned> *CallContinuationCounterMap;
+
   /// Used to expand an allocatd SkipCnt to Expression with known counters.
   /// Key: SkipCnt
   /// Val: Subtract Expression
@@ -942,10 +945,11 @@ struct CounterCoverageMappingBuilder
   /// expressions cross file or macro boundaries.
   SourceLocation MostRecentLocation;
 
-  /// Whether the visitor at a terminate statement.
-  bool HasTerminateStmt = false;
+  /// Whether the visitor changed the active region and needs a gap region
+  /// before the next statement.
+  bool HasGapRegion = false;
 
-  /// Gap region counter after terminate statement.
+  /// Counter to use for the pending gap region.
   Counter GapRegionCounter;
 
   /// Return a counter for the subtraction of \c RHS from \c LHS
@@ -972,6 +976,15 @@ struct CounterCoverageMappingBuilder
     return Counter::getCounter(CounterMap[S].Executed);
   }
 
+  std::optional<Counter> getCallContinuationCounter(const Stmt *S) {
+    if (!CallContinuationCounterMap)
+      return std::nullopt;
+    auto I = CallContinuationCounterMap->find(S);
+    if (I == CallContinuationCounterMap->end())
+      return std::nullopt;
+    return Counter::getCounter(I->second);
+  }
+
   struct BranchCounterPair {
     Counter Executed; ///< The Counter previously assigned.
     Counter Skipped;  ///< An expression (Parent-Executed), or equivalent to 
it.
@@ -1397,7 +1410,22 @@ struct CounterCoverageMappingBuilder
     if (!Region.hasEndLoc())
       Region.setEndLoc(EndLoc);
     pushRegion(Counter::getZero());
-    HasTerminateStmt = true;
+    HasGapRegion = true;
+  }
+
+  void startCallContinuationRegion(const Stmt *S, Counter ContinuationCount) {
+    SourceMappingRegion &Region = getRegion();
+    SourceLocation EndLoc = getEnd(S);
+    if (EndLoc.isInvalid())
+      return;
+
+    std::optional<SourceLocation> OriginalEndLoc =
+        Region.hasEndLoc() ? std::optional<SourceLocation>(Region.getEndLoc())
+                           : std::nullopt;
+    Region.setEndLoc(EndLoc);
+    pushRegion(ContinuationCount, std::nullopt, OriginalEndLoc);
+    GapRegionCounter = ContinuationCount;
+    HasGapRegion = true;
   }
 
   /// Find a valid gap range between \p AfterLoc and \p BeforeLoc.
@@ -1539,9 +1567,12 @@ struct CounterCoverageMappingBuilder
   CounterCoverageMappingBuilder(
       CoverageMappingModuleGen &CVM,
       llvm::DenseMap<const Stmt *, CounterPair> &CounterMap,
-      MCDC::State &MCDCState, SourceManager &SM, const LangOptions &LangOpts)
+      llvm::DenseMap<const Stmt *, unsigned> *CallContinuationCounterMap,
+      MCDC::State &MCDCState, SourceManager &SM, const LangOptions &LangOpts,
+      unsigned NextCounter)
       : CoverageMappingBuilder(CVM, SM, LangOpts), CounterMap(CounterMap),
-        NextCounterNum(CounterMap.size()), MCDCState(MCDCState),
+        CallContinuationCounterMap(CallContinuationCounterMap),
+        NextCounterNum(NextCounter), MCDCState(MCDCState),
         MCDCBuilder(CVM.getCodeGenModule(), MCDCState) {}
 
   /// Write the mapping data to the output stream
@@ -1564,35 +1595,34 @@ struct CounterCoverageMappingBuilder
     if (S->getBeginLoc().isValid())
       extendRegion(S);
     const Stmt *LastStmt = nullptr;
-    bool SaveTerminateStmt = HasTerminateStmt;
-    HasTerminateStmt = false;
+    bool SaveGapRegion = HasGapRegion;
+    HasGapRegion = false;
     GapRegionCounter = Counter::getZero();
     for (const Stmt *Child : S->children())
       if (Child) {
-        // If last statement contains terminate statements, add a gap area
-        // between the two statements.
-        if (LastStmt && HasTerminateStmt) {
+        // If the last statement changed the active region, add a gap area
+        // between the two statements with the new counter.
+        if (LastStmt && HasGapRegion) {
           auto Gap = findGapAreaBetween(getEnd(LastStmt), getStart(Child));
           if (Gap)
             fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(),
                                  GapRegionCounter);
-          SaveTerminateStmt = true;
-          HasTerminateStmt = false;
+          SaveGapRegion = true;
+          HasGapRegion = false;
         }
         this->Visit(Child);
         LastStmt = Child;
       }
-    if (SaveTerminateStmt)
-      HasTerminateStmt = true;
+    if (SaveGapRegion)
+      HasGapRegion = true;
     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;
+    // Any region transition within the statement expression has been handled 
by
+    // visiting the sub-statement. It cannot need a gap outside the expression.
+    HasGapRegion = false;
   }
 
   void VisitDecl(const Decl *D) {
@@ -1692,6 +1722,9 @@ struct CounterCoverageMappingBuilder
     QualType CalleeType = E->getCallee()->getType();
     if (getFunctionExtInfo(*CalleeType).getNoReturn())
       terminateRegion(E);
+    else if (std::optional<Counter> ContinuationCounter =
+                 getCallContinuationCounter(E))
+      startCallContinuationRegion(E, *ContinuationCounter);
   }
 
   void VisitWhileStmt(const WhileStmt *S) {
@@ -1706,8 +1739,8 @@ struct CounterCoverageMappingBuilder
     Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());
     BreakContinue BC = BreakContinueStack.pop_back_val();
 
-    bool BodyHasTerminateStmt = HasTerminateStmt;
-    HasTerminateStmt = false;
+    bool BodyHasGapRegion = HasGapRegion;
+    HasGapRegion = false;
 
     // Go back to handle the condition.
     Counter CondCount =
@@ -1727,8 +1760,8 @@ struct CounterCoverageMappingBuilder
     if (!IsCounterEqual(OutCount, ParentCount)) {
       pushRegion(OutCount);
       GapRegionCounter = OutCount;
-      if (BodyHasTerminateStmt)
-        HasTerminateStmt = true;
+      if (BodyHasGapRegion)
+        HasGapRegion = true;
     }
 
     // Create Branch Region around condition.
@@ -1749,8 +1782,8 @@ struct CounterCoverageMappingBuilder
 
     BreakContinue BC = BreakContinueStack.pop_back_val();
 
-    bool BodyHasTerminateStmt = HasTerminateStmt;
-    HasTerminateStmt = false;
+    bool BodyHasGapRegion = HasGapRegion;
+    HasGapRegion = false;
 
     Counter CondCount = addCounters(BackedgeCount, BC.ContinueCount);
     auto BranchCount = getBranchCounterPair(S, CondCount);
@@ -1762,8 +1795,8 @@ struct CounterCoverageMappingBuilder
     if (!IsCounterEqual(OutCount, ParentCount)) {
       pushRegion(OutCount);
       GapRegionCounter = OutCount;
-      if (BodyHasTerminateStmt)
-        HasTerminateStmt = true;
+      if (BodyHasGapRegion)
+        HasGapRegion = true;
     }
 
     // Create Branch Region around condition.
@@ -1788,8 +1821,8 @@ struct CounterCoverageMappingBuilder
     Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());
     BreakContinue BodyBC = BreakContinueStack.pop_back_val();
 
-    bool BodyHasTerminateStmt = HasTerminateStmt;
-    HasTerminateStmt = false;
+    bool BodyHasGapRegion = HasGapRegion;
+    HasGapRegion = false;
 
     // The increment is essentially part of the body but it needs to include
     // the count for all the continue statements.
@@ -1821,8 +1854,8 @@ struct CounterCoverageMappingBuilder
     if (!IsCounterEqual(OutCount, ParentCount)) {
       pushRegion(OutCount);
       GapRegionCounter = OutCount;
-      if (BodyHasTerminateStmt)
-        HasTerminateStmt = true;
+      if (BodyHasGapRegion)
+        HasGapRegion = true;
     }
 
     // Create Branch Region around condition.
@@ -1844,8 +1877,8 @@ struct CounterCoverageMappingBuilder
     Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());
     BreakContinue BC = BreakContinueStack.pop_back_val();
 
-    bool BodyHasTerminateStmt = HasTerminateStmt;
-    HasTerminateStmt = false;
+    bool BodyHasGapRegion = HasGapRegion;
+    HasGapRegion = false;
 
     // The body count applies to the area immediately after the range.
     auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));
@@ -1861,8 +1894,8 @@ struct CounterCoverageMappingBuilder
     if (!IsCounterEqual(OutCount, ParentCount)) {
       pushRegion(OutCount);
       GapRegionCounter = OutCount;
-      if (BodyHasTerminateStmt)
-        HasTerminateStmt = true;
+      if (BodyHasGapRegion)
+        HasGapRegion = true;
     }
 
     // Create Branch Region around condition.
@@ -2085,12 +2118,13 @@ struct CounterCoverageMappingBuilder
     // needed to handle macros that generate the "if" but not the condition.
     extendRegion(S->getCond());
 
-    Counter ParentCount = getRegion().getCounter();
-    auto [ThenCount, ElseCount] = getBranchCounterPair(S, ParentCount);
-
     // Emitting a counter for the condition makes it easier to interpret the
     // counter for the body when looking at the coverage.
-    propagateCounts(ParentCount, S->getCond());
+    Counter ParentCount = getRegion().getCounter();
+    Counter CondExitCount = propagateCounts(ParentCount, S->getCond());
+    Counter BranchParentCount =
+        CallContinuationCounterMap ? CondExitCount : ParentCount;
+    auto [ThenCount, ElseCount] = getBranchCounterPair(S, BranchParentCount);
 
     // The 'then' count applies to the area immediately after the condition.
     std::optional<SourceRange> Gap =
@@ -2102,8 +2136,8 @@ struct CounterCoverageMappingBuilder
     Counter OutCount = propagateCounts(ThenCount, S->getThen());
 
     if (const Stmt *Else = S->getElse()) {
-      bool ThenHasTerminateStmt = HasTerminateStmt;
-      HasTerminateStmt = false;
+      bool ThenHasGapRegion = HasGapRegion;
+      HasGapRegion = false;
       // The 'else' count applies to the area immediately after the 'then'.
       std::optional<SourceRange> Gap =
           findGapAreaBetween(getEnd(S->getThen()), getStart(Else));
@@ -2113,12 +2147,12 @@ struct CounterCoverageMappingBuilder
 
       OutCount = addCounters(OutCount, propagateCounts(ElseCount, Else));
 
-      if (ThenHasTerminateStmt)
-        HasTerminateStmt = true;
+      if (ThenHasGapRegion)
+        HasGapRegion = true;
     } else
       OutCount = addCounters(OutCount, ElseCount);
 
-    if (!IsCounterEqual(OutCount, ParentCount)) {
+    if (!IsCounterEqual(OutCount, BranchParentCount)) {
       pushRegion(OutCount);
       GapRegionCounter = OutCount;
     }
@@ -2682,8 +2716,9 @@ unsigned CoverageMappingModuleGen::getFileID(FileEntryRef 
File) {
 void CoverageMappingGen::emitCounterMapping(const Decl *D,
                                             llvm::raw_ostream &OS) {
   assert(CounterMap && MCDCState);
-  CounterCoverageMappingBuilder Walker(CVM, *CounterMap, *MCDCState, SM,
-                                       LangOpts);
+  CounterCoverageMappingBuilder Walker(CVM, *CounterMap,
+                                       CallContinuationCounterMap, *MCDCState,
+                                       SM, LangOpts, NextCounter);
   Walker.VisitDecl(D);
   Walker.write(OS);
 }
diff --git a/clang/lib/CodeGen/CoverageMappingGen.h 
b/clang/lib/CodeGen/CoverageMappingGen.h
index 0ed50597e1dc3..15fe381206830 100644
--- a/clang/lib/CodeGen/CoverageMappingGen.h
+++ b/clang/lib/CodeGen/CoverageMappingGen.h
@@ -160,20 +160,26 @@ class CoverageMappingGen {
   SourceManager &SM;
   const LangOptions &LangOpts;
   llvm::DenseMap<const Stmt *, CounterPair> *CounterMap;
+  llvm::DenseMap<const Stmt *, unsigned> *CallContinuationCounterMap;
   MCDC::State *MCDCState;
+  unsigned NextCounter;
 
 public:
   CoverageMappingGen(CoverageMappingModuleGen &CVM, SourceManager &SM,
                      const LangOptions &LangOpts)
       : CVM(CVM), SM(SM), LangOpts(LangOpts), CounterMap(nullptr),
-        MCDCState(nullptr) {}
-
-  CoverageMappingGen(CoverageMappingModuleGen &CVM, SourceManager &SM,
-                     const LangOptions &LangOpts,
-                     llvm::DenseMap<const Stmt *, CounterPair> *CounterMap,
-                     MCDC::State *MCDCState)
+        CallContinuationCounterMap(nullptr), MCDCState(nullptr),
+        NextCounter(0) {}
+
+  CoverageMappingGen(
+      CoverageMappingModuleGen &CVM, SourceManager &SM,
+      const LangOptions &LangOpts,
+      llvm::DenseMap<const Stmt *, CounterPair> *CounterMap,
+      llvm::DenseMap<const Stmt *, unsigned> *CallContinuationCounterMap,
+      MCDC::State *MCDCState, unsigned NextCounter)
       : CVM(CVM), SM(SM), LangOpts(LangOpts), CounterMap(CounterMap),
-        MCDCState(MCDCState) {}
+        CallContinuationCounterMap(CallContinuationCounterMap),
+        MCDCState(MCDCState), NextCounter(NextCounter) {}
 
   /// Emit the coverage mapping data which maps the regions of
   /// code to counters that will be used to find the execution
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 7657afb14f077..26d271dd9811b 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -539,8 +539,9 @@ static void addPGOAndCoverageFlags(const ToolChain &TC, 
Compilation &C,
                       Args.hasArg(options::OPT_coverage);
   bool EmitCovData = TC.needsGCovInstrumentation(Args);
 
-  if (Args.hasFlag(options::OPT_fcoverage_mapping,
-                   options::OPT_fno_coverage_mapping, false)) {
+  bool CoverageMappingEnabled = Args.hasFlag(
+      options::OPT_fcoverage_mapping, options::OPT_fno_coverage_mapping, 
false);
+  if (CoverageMappingEnabled) {
     if (!ProfileGenerateArg)
       D.Diag(clang::diag::err_drv_argument_only_allowed_with)
           << "-fcoverage-mapping"
@@ -549,10 +550,19 @@ static void addPGOAndCoverageFlags(const ToolChain &TC, 
Compilation &C,
     CmdArgs.push_back("-fcoverage-mapping");
   }
 
+  if (Args.hasFlag(options::OPT_fcoverage_call_continuations,
+                   options::OPT_fno_coverage_call_continuations, false)) {
+    if (!CoverageMappingEnabled)
+      D.Diag(clang::diag::err_drv_argument_only_allowed_with)
+          << "-fcoverage-call-continuations"
+          << "-fcoverage-mapping";
+
+    CmdArgs.push_back("-fcoverage-call-continuations");
+  }
+
   if (Args.hasFlag(options::OPT_fmcdc_coverage, options::OPT_fno_mcdc_coverage,
                    false)) {
-    if (!Args.hasFlag(options::OPT_fcoverage_mapping,
-                      options::OPT_fno_coverage_mapping, false))
+    if (!CoverageMappingEnabled)
       D.Diag(clang::diag::err_drv_argument_only_allowed_with)
           << "-fcoverage-mcdc"
           << "-fcoverage-mapping";
@@ -9532,6 +9542,8 @@ static bool requiresProfileRT(unsigned ID) {
   case options::OPT_fprofile_instr_generate_EQ:
   case options::OPT_fcoverage_mapping:
   case options::OPT_fno_coverage_mapping:
+  case options::OPT_fcoverage_call_continuations:
+  case options::OPT_fno_coverage_call_continuations:
   case options::OPT_fcoverage_compilation_dir_EQ:
   case options::OPT_ffile_compilation_dir_EQ:
   case options::OPT_fcoverage_prefix_map_EQ:
@@ -9603,6 +9615,8 @@ void LinkerWrapper::ConstructJob(Compilation &C, const 
JobAction &JA,
       OPT_fprofile_instr_generate_EQ,
       OPT_fcoverage_mapping,
       OPT_fno_coverage_mapping,
+      OPT_fcoverage_call_continuations,
+      OPT_fno_coverage_call_continuations,
       OPT_fcoverage_compilation_dir_EQ,
       OPT_ffile_compilation_dir_EQ,
       OPT_fcoverage_prefix_map_EQ,
diff --git a/clang/lib/Driver/ToolChains/SYCL.cpp 
b/clang/lib/Driver/ToolChains/SYCL.cpp
index 9d13d3314dcc5..04f3161f4b104 100644
--- a/clang/lib/Driver/ToolChains/SYCL.cpp
+++ b/clang/lib/Driver/ToolChains/SYCL.cpp
@@ -94,7 +94,10 @@ static ArrayRef<options::ID> getUnsupportedOpts() {
       options::OPT_fno_test_coverage, // -f[no-]test-coverage
       options::OPT_fcoverage_mapping,
       options::OPT_fno_coverage_mapping, // -f[no-]coverage-mapping
-      options::OPT_coverage,             // --coverage
+      options::OPT_fcoverage_call_continuations,
+      options::OPT_fno_coverage_call_continuations,
+      // -f[no-]coverage-call-continuations
+      options::OPT_coverage, // --coverage
       options::OPT_fprofile_instr_generate,
       options::OPT_fprofile_instr_generate_EQ,
       options::OPT_fno_profile_instr_generate, // -f[no-]profile-instr-generate
diff --git a/clang/test/CoverageMapping/call-continuations.c 
b/clang/test/CoverageMapping/call-continuations.c
new file mode 100644
index 0000000000000..ef4d430267958
--- /dev/null
+++ b/clang/test/CoverageMapping/call-continuations.c
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fprofile-instrument=clang 
-fcoverage-mapping -fcoverage-call-continuations -dump-coverage-mapping 
-emit-llvm -o - %s | FileCheck %s --check-prefixes=MAP,IR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fprofile-instrument=clang 
-fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -o - %s | FileCheck 
%s --check-prefix=NOCC
+
+void f(void);
+__attribute__((returns_twice)) int returns_twice(void);
+
+int after_call(void) {
+  f();
+  return 1;
+}
+
+// IR-LABEL: define{{.*}} i32 @after_call(
+// IR: call void @f()
+// IR-NEXT: load i64, ptr getelementptr inbounds ([2 x i64], ptr 
@__profc_after_call, i32 0, i32 1)
+// IR: ret i32 1
+
+int setjmp_like(void) {
+  if (returns_twice() == 0)
+    return 1;
+  return 2;
+}
+
+// IR-LABEL: define{{.*}} i32 @setjmp_like
+// IR: call{{.*}}@returns_twice
+// IR-NEXT: load i64, ptr getelementptr inbounds ([3 x i64], ptr 
@__profc_setjmp_like, i32 0, i32 2)
+// MAP-LABEL: after_call:
+// MAP: Gap,File 0, [[@LINE-19]]:7 -> [[@LINE-18]]:3 = #1
+// MAP: File 0, [[@LINE-19]]:3 -> [[@LINE-18]]:2 = #1
+// MAP-LABEL: setjmp_like:
+// MAP: Branch,File 0, [[@LINE-12]]:7 -> [[@LINE-12]]:27 = #1, (#2 - #1)
+// NOCC-LABEL: setjmp_like:
+// NOCC: Branch,File 0, [[@LINE-14]]:7 -> [[@LINE-14]]:27 = #1, (#0 - #1)
diff --git a/clang/test/Driver/coverage-call-continuations.c 
b/clang/test/Driver/coverage-call-continuations.c
new file mode 100644
index 0000000000000..67b4053c60773
--- /dev/null
+++ b/clang/test/Driver/coverage-call-continuations.c
@@ -0,0 +1,7 @@
+// RUN: %clang -### -fprofile-instr-generate -fcoverage-mapping 
-fcoverage-call-continuations -c %s 2>&1 | FileCheck %s
+// RUN: not %clang -### -fcoverage-call-continuations -c %s 2>&1 | FileCheck 
%s --check-prefix=ERR
+
+// CHECK: "-fcoverage-mapping"
+// CHECK: "-fcoverage-call-continuations"
+
+// ERR: error: invalid argument '-fcoverage-call-continuations' only allowed 
with '-fcoverage-mapping'

>From 2be52f1b4c7dd7907b08f554c0160550727195cb Mon Sep 17 00:00:00 2001
From: "[email protected]"
 <[email protected]>
Date: Tue, 2 Jun 2026 09:48:22 +0000
Subject: [PATCH 2/2] [clang][coverage] Tighten call continuation counters

---
 clang/lib/CodeGen/CGExpr.cpp                  |   3 +-
 clang/lib/CodeGen/CodeGenPGO.cpp              |  29 +++-
 clang/lib/CodeGen/CoverageMappingGen.cpp      | 141 ++++++++++++------
 .../call-continuations-tight.c                |  69 +++++++++
 4 files changed, 193 insertions(+), 49 deletions(-)
 create mode 100644 clang/test/CoverageMapping/call-continuations-tight.c

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 0df4940963349..da1c70822de89 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -6503,7 +6503,8 @@ RValue CodeGenFunction::EmitCallExpr(const CallExpr *E,
   };
 
   RValue RV = EmitTheCall();
-  incrementCallContinuationProfileCounter(E);
+  if (E != MustTailCall)
+    incrementCallContinuationProfileCounter(E);
   return RV;
 }
 
diff --git a/clang/lib/CodeGen/CodeGenPGO.cpp b/clang/lib/CodeGen/CodeGenPGO.cpp
index 989ede7e26222..d5e41e2d13196 100644
--- a/clang/lib/CodeGen/CodeGenPGO.cpp
+++ b/clang/lib/CodeGen/CodeGenPGO.cpp
@@ -14,6 +14,7 @@
 #include "CGDebugInfo.h"
 #include "CodeGenFunction.h"
 #include "CoverageMappingGen.h"
+#include "clang/AST/Attr.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/Basic/DiagnosticFrontend.h"
@@ -23,6 +24,7 @@
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Endian.h"
 #include "llvm/Support/MD5.h"
+#include "llvm/Support/SaveAndRestore.h"
 #include <optional>
 
 namespace llvm {
@@ -180,6 +182,8 @@ struct MapRegionCounters : public 
RecursiveASTVisitor<MapRegionCounters> {
   unsigned MCDCMaxCond;
   /// Whether call-continuation coverage counters are enabled.
   bool CoverageCallContinuations;
+  /// The call currently marked as musttail, if any.
+  const CallExpr *MustTailCall = nullptr;
   /// The profile version.
   uint64_t ProfileVersion;
   /// Diagnostics Engine used to report warnings.
@@ -208,6 +212,29 @@ struct MapRegionCounters : public 
RecursiveASTVisitor<MapRegionCounters> {
   }
   bool TraverseCapturedStmt(CapturedStmt *CS) { return true; }
 
+  static const CallExpr *getMustTailCall(const AttributedStmt *S) {
+    for (const Attr *A : S->getAttrs()) {
+      if (A->getKind() != attr::MustTail)
+        continue;
+
+      const auto *R = dyn_cast<ReturnStmt>(S->getSubStmt());
+      if (!R || !R->getRetValue())
+        return nullptr;
+      return dyn_cast<CallExpr>(R->getRetValue()->IgnoreParens());
+    }
+    return nullptr;
+  }
+
+  bool TraverseAttributedStmt(AttributedStmt *S) {
+    const CallExpr *NewMustTailCall = getMustTailCall(S);
+    if (!NewMustTailCall)
+      return Base::TraverseAttributedStmt(S);
+
+    llvm::SaveAndRestore<const CallExpr *> SaveMustTail(MustTailCall,
+                                                        NewMustTailCall);
+    return Base::TraverseAttributedStmt(S);
+  }
+
   bool VisitDecl(const Decl *D) {
     switch (D->getKind()) {
     default:
@@ -364,7 +391,7 @@ struct MapRegionCounters : public 
RecursiveASTVisitor<MapRegionCounters> {
   /// reached from later source being reached.
   bool VisitCallExpr(CallExpr *S) {
     if (CoverageCallContinuations && CallContinuationCounterMap &&
-        !isNoReturnCall(S))
+        !isNoReturnCall(S) && S != MustTailCall)
       CallContinuations.push_back(S);
     return Base::VisitCallExpr(S);
   }
diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp 
b/clang/lib/CodeGen/CoverageMappingGen.cpp
index 1865a4f4d1cb1..ac70667040727 100644
--- a/clang/lib/CodeGen/CoverageMappingGen.cpp
+++ b/clang/lib/CodeGen/CoverageMappingGen.cpp
@@ -1745,11 +1745,13 @@ struct CounterCoverageMappingBuilder
     // Go back to handle the condition.
     Counter CondCount =
         addCounters(ParentCount, BackedgeCount, BC.ContinueCount);
-    auto BranchCount = getBranchCounterPair(S, CondCount);
-    assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount);
 
-    propagateCounts(CondCount, S->getCond());
+    Counter CondExitCount = propagateCounts(CondCount, S->getCond());
     adjustForOutOfOrderTraversal(getEnd(S));
+    Counter BranchParentCount =
+        CallContinuationCounterMap ? CondExitCount : CondCount;
+    auto BranchCount = getBranchCounterPair(S, BranchParentCount);
+    assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount);
 
     // The body count applies to the area immediately after the increment.
     auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));
@@ -1786,10 +1788,12 @@ struct CounterCoverageMappingBuilder
     HasGapRegion = false;
 
     Counter CondCount = addCounters(BackedgeCount, BC.ContinueCount);
-    auto BranchCount = getBranchCounterPair(S, CondCount);
-    assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount);
 
-    propagateCounts(CondCount, S->getCond());
+    Counter CondExitCount = propagateCounts(CondCount, S->getCond());
+    Counter BranchParentCount =
+        CallContinuationCounterMap ? CondExitCount : CondCount;
+    auto BranchCount = getBranchCounterPair(S, BranchParentCount);
+    assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount);
 
     Counter OutCount = addCounters(BC.BreakCount, BranchCount.Skipped);
     if (!IsCounterEqual(OutCount, ParentCount)) {
@@ -1836,13 +1840,16 @@ struct CounterCoverageMappingBuilder
     Counter CondCount = addCounters(
         addCounters(ParentCount, BackedgeCount, BodyBC.ContinueCount),
         IncrementBC.ContinueCount);
-    auto BranchCount = getBranchCounterPair(S, CondCount);
-    assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount);
 
+    Counter CondExitCount = CondCount;
     if (const Expr *Cond = S->getCond()) {
-      propagateCounts(CondCount, Cond);
+      CondExitCount = propagateCounts(CondCount, Cond);
       adjustForOutOfOrderTraversal(getEnd(S));
     }
+    Counter BranchParentCount =
+        CallContinuationCounterMap ? CondExitCount : CondCount;
+    auto BranchCount = getBranchCounterPair(S, BranchParentCount);
+    assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount);
 
     // The body count applies to the area immediately after the increment.
     auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));
@@ -2184,14 +2191,31 @@ struct CounterCoverageMappingBuilder
     extendRegion(E);
 
     Counter ParentCount = getRegion().getCounter();
-    auto [TrueCount, FalseCount] = getBranchCounterPair(E, ParentCount);
     Counter OutCount;
 
     if (const auto *BCO = dyn_cast<BinaryConditionalOperator>(E)) {
-      propagateCounts(ParentCount, BCO->getCommon());
+      Counter CondExitCount = propagateCounts(ParentCount, BCO->getCommon());
+      Counter BranchParentCount =
+          CallContinuationCounterMap ? CondExitCount : ParentCount;
+      auto [TrueCount, FalseCount] = getBranchCounterPair(E, 
BranchParentCount);
       OutCount = TrueCount;
+
+      extendRegion(E->getFalseExpr());
+      OutCount =
+          addCounters(OutCount, propagateCounts(FalseCount, 
E->getFalseExpr()));
+
+      if (!IsCounterEqual(OutCount, BranchParentCount)) {
+        pushRegion(OutCount);
+        GapRegionCounter = OutCount;
+      }
+
+      createBranchRegion(E->getCond(), TrueCount, FalseCount);
+      return;
     } else {
-      propagateCounts(ParentCount, E->getCond());
+      Counter CondExitCount = propagateCounts(ParentCount, E->getCond());
+      Counter BranchParentCount =
+          CallContinuationCounterMap ? CondExitCount : ParentCount;
+      auto [TrueCount, FalseCount] = getBranchCounterPair(E, 
BranchParentCount);
       // The 'then' count applies to the area immediately after the condition.
       auto Gap =
           findGapAreaBetween(E->getQuestionLoc(), getStart(E->getTrueExpr()));
@@ -2200,19 +2224,19 @@ struct CounterCoverageMappingBuilder
 
       extendRegion(E->getTrueExpr());
       OutCount = propagateCounts(TrueCount, E->getTrueExpr());
-    }
 
-    extendRegion(E->getFalseExpr());
-    OutCount =
-        addCounters(OutCount, propagateCounts(FalseCount, E->getFalseExpr()));
+      extendRegion(E->getFalseExpr());
+      OutCount =
+          addCounters(OutCount, propagateCounts(FalseCount, 
E->getFalseExpr()));
 
-    if (!IsCounterEqual(OutCount, ParentCount)) {
-      pushRegion(OutCount);
-      GapRegionCounter = OutCount;
-    }
+      if (!IsCounterEqual(OutCount, BranchParentCount)) {
+        pushRegion(OutCount);
+        GapRegionCounter = OutCount;
+      }
 
-    // Create Branch Region around condition.
-    createBranchRegion(E->getCond(), TrueCount, FalseCount);
+      // Create Branch Region around condition.
+      createBranchRegion(E->getCond(), TrueCount, FalseCount);
+    }
   }
 
   inline unsigned findMCDCBranchesInSourceRegion(
@@ -2332,8 +2356,10 @@ struct CounterCoverageMappingBuilder
     CurCondIDs[true] = RHSid;
     auto DecisionLHS = CurCondIDs;
 
+    Counter ParentCnt = getRegion().getCounter();
+
     extendRegion(E->getLHS());
-    propagateCounts(getRegion().getCounter(), E->getLHS());
+    Counter LHSExitCnt = propagateCounts(ParentCnt, E->getLHS());
     handleFileExit(getEnd(E->getLHS()));
 
     // Restore CurCondIDs.
@@ -2349,25 +2375,35 @@ struct CounterCoverageMappingBuilder
       fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), 
getRegionCounter(E));
     }
 
+    // Extract the RHS's Execution Counter.
+    Counter LHSBranchParentCnt =
+        CallContinuationCounterMap ? LHSExitCnt : getRegion().getCounter();
+    auto [RHSExecCnt, LHSFalseCnt] =
+        getBranchCounterPair(E, LHSBranchParentCnt);
+
     // Counter tracks the right hand side of a logical and operator.
     extendRegion(E->getRHS());
-    propagateCounts(getRegionCounter(E), E->getRHS());
-
-    // Extract the Parent Region Counter.
-    Counter ParentCnt = getRegion().getCounter();
-
-    // Extract the RHS's Execution Counter.
-    auto [RHSExecCnt, LHSExitCnt] = getBranchCounterPair(E, ParentCnt);
+    Counter RHSExitCnt = propagateCounts(RHSExecCnt, E->getRHS());
 
     // Extract the RHS's "True" Instance Counter.
-    auto [RHSTrueCnt, RHSExitCnt] =
-        getBranchCounterPair(E->getRHS(), RHSExecCnt);
+    Counter RHSBranchParentCnt =
+        CallContinuationCounterMap ? RHSExitCnt : RHSExecCnt;
+    auto [RHSTrueCnt, RHSFalseCnt] =
+        getBranchCounterPair(E->getRHS(), RHSBranchParentCnt);
+
+    if (CallContinuationCounterMap) {
+      Counter OutCount = addCounters(LHSFalseCnt, RHSExitCnt);
+      if (!IsCounterEqual(OutCount, ParentCnt)) {
+        getRegion().setCounter(OutCount);
+        GapRegionCounter = OutCount;
+      }
+    }
 
     // Create Branch Region around LHS condition.
-    createBranchRegion(E->getLHS(), RHSExecCnt, LHSExitCnt, DecisionLHS);
+    createBranchRegion(E->getLHS(), RHSExecCnt, LHSFalseCnt, DecisionLHS);
 
     // Create Branch Region around RHS condition.
-    createBranchRegion(E->getRHS(), RHSTrueCnt, RHSExitCnt, DecisionRHS);
+    createBranchRegion(E->getRHS(), RHSTrueCnt, RHSFalseCnt, DecisionRHS);
 
     // Create MCDC Decision Region when E is at the top level.
     createOrCancelDecision(E, SourceRegionsSince);
@@ -2400,8 +2436,10 @@ struct CounterCoverageMappingBuilder
     CurCondIDs[false] = RHSid;
     auto DecisionLHS = CurCondIDs;
 
+    Counter ParentCnt = getRegion().getCounter();
+
     extendRegion(E->getLHS());
-    Counter OutCount = propagateCounts(getRegion().getCounter(), E->getLHS());
+    Counter LHSExitCnt = propagateCounts(ParentCnt, E->getLHS());
     handleFileExit(getEnd(E->getLHS()));
 
     // Track LHS True/False Decision.
@@ -2417,29 +2455,38 @@ struct CounterCoverageMappingBuilder
       fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), 
getRegionCounter(E));
     }
 
+    // Extract the RHS's Execution Counter.
+    Counter LHSBranchParentCnt =
+        CallContinuationCounterMap ? LHSExitCnt : getRegion().getCounter();
+    auto [RHSExecCnt, LHSTrueCnt] = getBranchCounterPair(E, 
LHSBranchParentCnt);
+
     // Counter tracks the right hand side of a logical or operator.
     extendRegion(E->getRHS());
-    propagateCounts(getRegionCounter(E), E->getRHS());
-
-    // Extract the Parent Region Counter.
-    Counter ParentCnt = getRegion().getCounter();
-
-    // Extract the RHS's Execution Counter.
-    auto [RHSExecCnt, LHSExitCnt] = getBranchCounterPair(E, ParentCnt);
+    Counter RHSExitCnt = propagateCounts(RHSExecCnt, E->getRHS());
 
     // Extract the RHS's "False" Instance Counter.
-    auto [RHSFalseCnt, RHSExitCnt] =
-        getBranchCounterPair(E->getRHS(), RHSExecCnt);
+    Counter RHSBranchParentCnt =
+        CallContinuationCounterMap ? RHSExitCnt : RHSExecCnt;
+    auto [RHSFalseCnt, RHSTrueCnt] =
+        getBranchCounterPair(E->getRHS(), RHSBranchParentCnt);
 
     if (!shouldVisitRHS(E->getLHS())) {
-      GapRegionCounter = OutCount;
+      GapRegionCounter = LHSExitCnt;
+    }
+
+    if (CallContinuationCounterMap) {
+      Counter OutCount = addCounters(LHSTrueCnt, RHSExitCnt);
+      if (!IsCounterEqual(OutCount, ParentCnt)) {
+        getRegion().setCounter(OutCount);
+        GapRegionCounter = OutCount;
+      }
     }
 
     // Create Branch Region around LHS condition.
-    createBranchRegion(E->getLHS(), LHSExitCnt, RHSExecCnt, DecisionLHS);
+    createBranchRegion(E->getLHS(), LHSTrueCnt, RHSExecCnt, DecisionLHS);
 
     // Create Branch Region around RHS condition.
-    createBranchRegion(E->getRHS(), RHSExitCnt, RHSFalseCnt, DecisionRHS);
+    createBranchRegion(E->getRHS(), RHSTrueCnt, RHSFalseCnt, DecisionRHS);
 
     // Create MCDC Decision Region when E is at the top level.
     createOrCancelDecision(E, SourceRegionsSince);
diff --git a/clang/test/CoverageMapping/call-continuations-tight.c 
b/clang/test/CoverageMapping/call-continuations-tight.c
new file mode 100644
index 0000000000000..11b01870cf620
--- /dev/null
+++ b/clang/test/CoverageMapping/call-continuations-tight.c
@@ -0,0 +1,69 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fprofile-instrument=clang 
-fcoverage-mapping -fcoverage-call-continuations -dump-coverage-mapping 
-emit-llvm -o - %s | FileCheck %s --check-prefixes=IR,MAP
+
+int printf(const char *, ...);
+void f(void);
+int g(void);
+__attribute__((returns_twice)) int returns_twice(void);
+int tail_callee(int);
+
+int block_after_call(int argc) {
+  {
+    if (argc > 2)
+      return 2;
+    printf("one\n");
+  }
+  printf("two\n");
+  return 0;
+}
+
+int while_call_condition(void) {
+  while (returns_twice())
+    f();
+  return 1;
+}
+
+int logical_and_call(void) {
+  if (returns_twice() && g())
+    return 1;
+  return 2;
+}
+
+int musttail_call(int x) {
+  __attribute__((musttail)) return tail_callee(x);
+}
+
+// IR-LABEL: define{{.*}} i32 @block_after_call(
+// IR: call{{.*}} @printf(
+// IR-NEXT: load i64, ptr getelementptr inbounds 
({{.*}}@__profc_block_after_call
+// IR: call{{.*}} @printf(
+// IR-NEXT: load i64, ptr getelementptr inbounds 
({{.*}}@__profc_block_after_call
+
+// IR-LABEL: define{{.*}} i32 @while_call_condition(
+// IR: call{{.*}} @returns_twice
+// IR-NEXT: load i64, ptr getelementptr inbounds 
({{.*}}@__profc_while_call_condition
+
+// IR-LABEL: define{{.*}} i32 @logical_and_call(
+// IR: call{{.*}} @returns_twice
+// IR-NEXT: load i64, ptr getelementptr inbounds 
({{.*}}@__profc_logical_and_call
+// IR: call{{.*}} @g
+// IR-NEXT: load i64, ptr getelementptr inbounds 
({{.*}}@__profc_logical_and_call
+
+// IR-LABEL: define{{.*}} i32 @musttail_call(
+// IR: musttail call i32 @tail_callee
+// IR-NEXT: ret i32
+// IR-NOT: getelementptr inbounds ({{.*}}@__profc_musttail_call
+
+// MAP-LABEL: block_after_call:
+// MAP: Gap,File 0, [[BLOCK_CLOSE:[0-9]+]]:4 -> [[BLOCK_NEXT:[0-9]+]]:3 = #2
+// MAP-NEXT: File 0, [[BLOCK_NEXT]]:3 -> [[BLOCK_NEXT]]:18 = #2
+
+// MAP-LABEL: while_call_condition:
+// MAP: File 0, [[WHILE_COND:[0-9]+]]:10 -> [[WHILE_COND]]:25 = (#0 + #3)
+// MAP-NEXT: Branch,File 0, [[WHILE_COND]]:10 -> [[WHILE_COND]]:25 = #1, (#2 - 
#1)
+
+// MAP-LABEL: logical_and_call:
+// MAP: Branch,File 0, [[LAND_COND:[0-9]+]]:7 -> [[LAND_COND]]:22 = #2, (#4 - 
#2)
+// MAP: Branch,File 0, [[LAND_COND]]:26 -> [[LAND_COND]]:29 = #3, (#5 - #3)
+
+// MAP-LABEL: musttail_call:
+// MAP-NEXT: File 0, {{[0-9]+}}:26 -> {{[0-9]+}}:2 = #0

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

Reply via email to