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
