diff --git a/clang/docs/SourceBasedCodeCoverage.rst b/clang/docs/SourceBasedCodeCoverage.rst --- a/clang/docs/SourceBasedCodeCoverage.rst +++ b/clang/docs/SourceBasedCodeCoverage.rst @@ -179,6 +179,31 @@ | 4| 1|} ------------------ +If ``--show-branches=count`` and ``--show-expansions`` are also enabled, the +sub-views will show detailed branch coverage information in addition to the +region counts: + +.. code-block:: none + + ------------------ + | void foo(int): + | 2| 1|template void foo(T x) { + | 3| 11| for (unsigned I = 0; I < 10; ++I) { BAR(I); } + | ^11 ^10 ^10^10 + | ------------------ + | | | 1| 10|#define BAR(x) ((x) || (x)) + | | | ^10 ^1 + | | | ------------------ + | | | | Branch (1:17): [True: 9, False: 1] + | | | | Branch (1:24): [True: 0, False: 1] + | | | ------------------ + | ------------------ + | | Branch (3:23): [True: 10, False: 1] + | ------------------ + | 4| 1|} + ------------------ + + To generate a file-level summary of coverage statistics instead of a line-oriented report, try: @@ -186,11 +211,11 @@ # Step 3(c): Create a coverage summary. % llvm-cov report ./foo -instr-profile=foo.profdata - Filename Regions Missed Regions Cover Functions Missed Functions Executed Lines Missed Lines Cover - -------------------------------------------------------------------------------------------------------------------------------------- - /tmp/foo.cc 13 0 100.00% 3 0 100.00% 13 0 100.00% - -------------------------------------------------------------------------------------------------------------------------------------- - TOTAL 13 0 100.00% 3 0 100.00% 13 0 100.00% + Filename Regions Missed Regions Cover Functions Missed Functions Executed Lines Missed Lines Cover Branches Missed Branches Cover + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + /tmp/foo.cc 13 0 100.00% 3 0 100.00% 13 0 100.00% 12 2 83.33% + -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + TOTAL 13 0 100.00% 3 0 100.00% 13 0 100.00% 12 2 83.33% The ``llvm-cov`` tool supports specifying a custom demangler, writing out reports in a directory structure, and generating html reports. For the full @@ -246,8 +271,17 @@ body with no control flow). However, it's also possible for a single line to contain multiple code regions (e.g in "return x || y && z"). -Of these four statistics, function coverage is usually the least granular while -region coverage is the most granular. The project-wide totals for each +* Branch coverage is the percentage of "true" and "false" branches that have + been taken at least once. Each branch is tied to individual conditions in the + source code that may each evaluate to either "true" or "false". These + conditions may comprise larger boolean expressions linked by boolean logical + operators. For example, "x = (y == 2) || (z < 10)" is a boolean expression + that is comprised of two individual conditions, each of which evaluates to + either true or false, producing four total branch outcomes. + +Of these five statistics, function coverage is usually the least granular while +branch coverage is the most granular. 100% branch coverage for a function +implies 100% region coverage for a function. The project-wide totals for each statistic are listed in the summary. Format compatibility guarantees @@ -351,6 +385,25 @@ or blue "executed" highlights present at the start of a line that is otherwise not executed. +Branch regions +-------------- +When viewing branch coverage details in source-based file-level sub-views using +``--show-branches``, it is recommended that users show all macro expansions +(using option ``--show-expansions``) since macros may contain hidden branch +conditions. The coverage summary report will always include these macro-based +boolean expressions in the overall branch coverage count for a function or +source file. + +Branch coverage is not tracked for constant folded branch conditions since +branches are not generated for these cases. In the source-based file-level +sub-view, these branches will simply be shown as ``[Folded - Ignored]`` so that +users are informed about what happened. + +Branch coverage is tied directly to branch-generating conditions in the source +code. Users should not see hidden branches that aren't actually tied to the +source code. + + Switch statements ----------------- @@ -366,3 +419,10 @@ For switches with ``CompoundStmt`` bodies, a new region is created at the start of each switch case. + +Branch regions are also generated for each switch case, including the default +case. If there is no explicitly defined default case in the source code, a +branch region is generated to correspond to the implicit default case that is +generated by the compiler. The implicit branch region is tied to the line and +column number of the switch statement condition since no source code for the +implicit case exists. diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -4191,6 +4191,7 @@ return Builder.CreateSExt(And, ConvertType(E->getType()), "sext"); } + bool InstrumentRegions = CGF.CGM.getCodeGenOpts().hasProfileClangInstr(); llvm::Type *ResTy = ConvertType(E->getType()); // If we have 0 && RHS, see if we can elide RHS, if so, just return 0. @@ -4201,6 +4202,22 @@ CGF.incrementProfileCounter(E); Value *RHSCond = CGF.EvaluateExprAsBool(E->getRHS()); + + // If we're generating for profiling or coverage, generate a branch to a + // block that increments the RHS counter needed to track branch condition + // coverage. In this case, use "FBlock" as both the final "TrueBlock" and + // "FalseBlock" after the increment is done. + if (InstrumentRegions && + CodeGenFunction::isInstrumentedCondition(E->getRHS())) { + llvm::BasicBlock *FBlock = CGF.createBasicBlock("land.end"); + llvm::BasicBlock *RHSBlockCnt = CGF.createBasicBlock("land.rhscnt"); + Builder.CreateCondBr(RHSCond, RHSBlockCnt, FBlock); + CGF.EmitBlock(RHSBlockCnt); + CGF.incrementProfileCounter(E->getRHS()); + CGF.EmitBranch(FBlock); + CGF.EmitBlock(FBlock); + } + // ZExt result to int or bool. return Builder.CreateZExtOrBitCast(RHSCond, ResTy, "land.ext"); } @@ -4237,6 +4254,19 @@ // Reaquire the RHS block, as there may be subblocks inserted. RHSBlock = Builder.GetInsertBlock(); + // If we're generating for profiling or coverage, generate a branch on the + // RHS to a block that increments the RHS true counter needed to track branch + // condition coverage. + if (InstrumentRegions && + CodeGenFunction::isInstrumentedCondition(E->getRHS())) { + llvm::BasicBlock *RHSBlockCnt = CGF.createBasicBlock("land.rhscnt"); + Builder.CreateCondBr(RHSCond, RHSBlockCnt, ContBlock); + CGF.EmitBlock(RHSBlockCnt); + CGF.incrementProfileCounter(E->getRHS()); + CGF.EmitBranch(ContBlock); + PN->addIncoming(RHSCond, RHSBlockCnt); + } + // Emit an unconditional branch from this block to ContBlock. { // There is no need to emit line number for unconditional branch. @@ -4277,6 +4307,7 @@ return Builder.CreateSExt(Or, ConvertType(E->getType()), "sext"); } + bool InstrumentRegions = CGF.CGM.getCodeGenOpts().hasProfileClangInstr(); llvm::Type *ResTy = ConvertType(E->getType()); // If we have 1 || RHS, see if we can elide RHS, if so, just return 1. @@ -4287,6 +4318,22 @@ CGF.incrementProfileCounter(E); Value *RHSCond = CGF.EvaluateExprAsBool(E->getRHS()); + + // If we're generating for profiling or coverage, generate a branch to a + // block that increments the RHS counter need to track branch condition + // coverage. In this case, use "FBlock" as both the final "TrueBlock" and + // "FalseBlock" after the increment is done. + if (InstrumentRegions && + CodeGenFunction::isInstrumentedCondition(E->getRHS())) { + llvm::BasicBlock *FBlock = CGF.createBasicBlock("lor.end"); + llvm::BasicBlock *RHSBlockCnt = CGF.createBasicBlock("lor.rhscnt"); + Builder.CreateCondBr(RHSCond, FBlock, RHSBlockCnt); + CGF.EmitBlock(RHSBlockCnt); + CGF.incrementProfileCounter(E->getRHS()); + CGF.EmitBranch(FBlock); + CGF.EmitBlock(FBlock); + } + // ZExt result to int or bool. return Builder.CreateZExtOrBitCast(RHSCond, ResTy, "lor.ext"); } @@ -4327,6 +4374,19 @@ // Reaquire the RHS block, as there may be subblocks inserted. RHSBlock = Builder.GetInsertBlock(); + // If we're generating for profiling or coverage, generate a branch on the + // RHS to a block that increments the RHS true counter needed to track branch + // condition coverage. + if (InstrumentRegions && + CodeGenFunction::isInstrumentedCondition(E->getRHS())) { + llvm::BasicBlock *RHSBlockCnt = CGF.createBasicBlock("lor.rhscnt"); + Builder.CreateCondBr(RHSCond, ContBlock, RHSBlockCnt); + CGF.EmitBlock(RHSBlockCnt); + CGF.incrementProfileCounter(E->getRHS()); + CGF.EmitBranch(ContBlock); + PN->addIncoming(RHSCond, RHSBlockCnt); + } + // Emit an unconditional branch from this block to ContBlock. Insert an entry // into the phi node for the edge with the value of RHSCond. CGF.EmitBlock(ContBlock); diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -1441,7 +1441,7 @@ SwitchWeights->push_back(getProfileCount(NextCase)); if (CGM.getCodeGenOpts().hasProfileClangInstr()) { CaseDest = createBasicBlock("sw.bb"); - EmitBlockWithFallThrough(CaseDest, &S); + EmitBlockWithFallThrough(CaseDest, CurCase); } // Since this loop is only executed when the CaseStmt has no attributes // use a hard-coded value. diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -4394,6 +4394,21 @@ bool ConstantFoldsToSimpleInteger(const Expr *Cond, llvm::APSInt &Result, bool AllowLabels = false); + /// isInstrumentedCondition - Determine whether the given condition is an + /// instrumentable condition (i.e. no "&&" or "||"). + static bool isInstrumentedCondition(const Expr *C); + + /// EmitBranchToCounterBlock - Emit a conditional branch to a new block that + /// increments a profile counter based on the semantics of the given logical + /// operator opcode. This is used to instrument branch condition coverage + /// for logical operators. + void EmitBranchToCounterBlock(const Expr *Cond, BinaryOperator::Opcode LOp, + llvm::BasicBlock *TrueBlock, + llvm::BasicBlock *FalseBlock, + uint64_t TrueCount = 0, + Stmt::Likelihood LH = Stmt::LH_None, + const Expr *CntrIdx = nullptr); + /// EmitBranchOnBoolExpr - Emit a branch on a boolean condition (e.g. for an /// if statement) to the specified blocks. Based on the condition, this might /// try to simplify the codegen of the conditional based on the branch. diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -1501,6 +1501,90 @@ return true; } +/// Determine whether the given condition is an instrumentable condition +/// (i.e. no "&&" or "||"). +bool CodeGenFunction::isInstrumentedCondition(const Expr *C) { + // Bypass simplistic logical-NOT operator before determining whether the + // condition contains any other logical operator. + if (const UnaryOperator *UnOp = dyn_cast(C->IgnoreParens())) + if (UnOp->getOpcode() == UO_LNot) + C = UnOp->getSubExpr(); + + const BinaryOperator *BOp = dyn_cast(C->IgnoreParens()); + return (!BOp || !BOp->isLogicalOp()); +} + +/// EmitBranchToCounterBlock - Emit a conditional branch to a new block that +/// increments a profile counter based on the semantics of the given logical +/// operator opcode. This is used to instrument branch condition coverage for +/// logical operators. +void CodeGenFunction::EmitBranchToCounterBlock( + const Expr *Cond, BinaryOperator::Opcode LOp, llvm::BasicBlock *TrueBlock, + llvm::BasicBlock *FalseBlock, uint64_t TrueCount /* = 0 */, + Stmt::Likelihood LH /* =None */, const Expr *CntrIdx /* = nullptr */) { + // If not instrumenting, just emit a branch. + bool InstrumentRegions = CGM.getCodeGenOpts().hasProfileClangInstr(); + if (!InstrumentRegions || !isInstrumentedCondition(Cond)) + return EmitBranchOnBoolExpr(Cond, TrueBlock, FalseBlock, TrueCount, LH); + + llvm::BasicBlock *ThenBlock = NULL; + llvm::BasicBlock *ElseBlock = NULL; + llvm::BasicBlock *NextBlock = NULL; + + // Create the block we'll use to increment the appropriate counter. + llvm::BasicBlock *CounterIncrBlock = createBasicBlock("lop.rhscnt"); + + // Set block pointers according to Logical-AND (BO_LAnd) semantics. This + // means we need to evaluate the condition and increment the counter on TRUE: + // + // if (Cond) + // goto CounterIncrBlock; + // else + // goto FalseBlock; + // + // CounterIncrBlock: + // Counter++; + // goto TrueBlock; + + if (LOp == BO_LAnd) { + ThenBlock = CounterIncrBlock; + ElseBlock = FalseBlock; + NextBlock = TrueBlock; + } + + // Set block pointers according to Logical-OR (BO_LOr) semantics. This means + // we need to evaluate the condition and increment the counter on FALSE: + // + // if (Cond) + // goto TrueBlock; + // else + // goto CounterIncrBlock; + // + // CounterIncrBlock: + // Counter++; + // goto FalseBlock; + + else if (LOp == BO_LOr) { + ThenBlock = TrueBlock; + ElseBlock = CounterIncrBlock; + NextBlock = FalseBlock; + } else { + llvm_unreachable("Expected Opcode must be that of a Logical Operator"); + } + + // Emit Branch based on condition. + EmitBranchOnBoolExpr(Cond, ThenBlock, ElseBlock, TrueCount, LH); + + // Emit the block containing the counter increment(s). + EmitBlock(CounterIncrBlock); + + // Increment corresponding counter; if index not provided, use Cond as index. + incrementProfileCounter(CntrIdx ? CntrIdx : Cond); + + // Go to the next block. + EmitBranch(NextBlock); +} + /// EmitBranchOnBoolExpr - Emit a branch on a boolean condition (e.g. for an if /// statement) to the specified blocks. Based on the condition, this might try /// to simplify the codegen of the conditional based on the branch. @@ -1523,8 +1607,8 @@ ConstantBool) { // br(1 && X) -> br(X). incrementProfileCounter(CondBOp); - return EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock, - TrueCount, LH); + return EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LAnd, TrueBlock, + FalseBlock, TrueCount, LH); } // If we have "X && 1", simplify the code to use an uncond branch. @@ -1532,8 +1616,8 @@ if (ConstantFoldsToSimpleInteger(CondBOp->getRHS(), ConstantBool) && ConstantBool) { // br(X && 1) -> br(X). - return EmitBranchOnBoolExpr(CondBOp->getLHS(), TrueBlock, FalseBlock, - TrueCount, LH); + return EmitBranchToCounterBlock(CondBOp->getLHS(), BO_LAnd, TrueBlock, + FalseBlock, TrueCount, LH, CondBOp); } // Emit the LHS as a conditional. If the LHS conditional is false, we @@ -1559,8 +1643,8 @@ // Any temporaries created here are conditional. eval.begin(*this); - EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock, TrueCount, - LH); + EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LAnd, TrueBlock, + FalseBlock, TrueCount, LH); eval.end(*this); return; @@ -1574,8 +1658,8 @@ !ConstantBool) { // br(0 || X) -> br(X). incrementProfileCounter(CondBOp); - return EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock, - TrueCount, LH); + return EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LOr, TrueBlock, + FalseBlock, TrueCount, LH); } // If we have "X || 0", simplify the code to use an uncond branch. @@ -1583,8 +1667,8 @@ if (ConstantFoldsToSimpleInteger(CondBOp->getRHS(), ConstantBool) && !ConstantBool) { // br(X || 0) -> br(X). - return EmitBranchOnBoolExpr(CondBOp->getLHS(), TrueBlock, FalseBlock, - TrueCount, LH); + return EmitBranchToCounterBlock(CondBOp->getLHS(), BO_LOr, TrueBlock, + FalseBlock, TrueCount, LH, CondBOp); } // Emit the LHS as a conditional. If the LHS conditional is true, we @@ -1613,8 +1697,8 @@ // Any temporaries created here are conditional. eval.begin(*this); - EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock, RHSCount, - LH); + EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LOr, TrueBlock, FalseBlock, + RHSCount, LH); eval.end(*this); diff --git a/clang/lib/CodeGen/CodeGenPGO.cpp b/clang/lib/CodeGen/CodeGenPGO.cpp --- a/clang/lib/CodeGen/CodeGenPGO.cpp +++ b/clang/lib/CodeGen/CodeGenPGO.cpp @@ -160,10 +160,13 @@ PGOHash Hash; /// The map of statements to counters. llvm::DenseMap &CounterMap; + /// The profile version. + uint64_t ProfileVersion; - MapRegionCounters(PGOHashVersion HashVersion, + MapRegionCounters(PGOHashVersion HashVersion, uint64_t ProfileVersion, llvm::DenseMap &CounterMap) - : NextCounter(0), Hash(HashVersion), CounterMap(CounterMap) {} + : NextCounter(0), Hash(HashVersion), CounterMap(CounterMap), + ProfileVersion(ProfileVersion) {} // Blocks and lambdas are handled as separate functions, so we need not // traverse them in the parent context. @@ -203,6 +206,18 @@ return Type; } + /// The RHS of all logical operators gets a fresh counter in order to count + /// how many times the RHS evaluates to true or false, depending on the + /// semantics of the operator. This is only valid for ">= v7" of the profile + /// version so that we facilitate backward compatibility. + bool VisitBinaryOperator(BinaryOperator *S) { + if (ProfileVersion >= llvm::IndexedInstrProf::Version7) + if (S->isLogicalOp() && + CodeGenFunction::isInstrumentedCondition(S->getRHS())) + CounterMap[S->getRHS()] = NextCounter++; + return Base::VisitBinaryOperator(S); + } + /// Include \p S in the function hash. bool VisitStmt(Stmt *S) { auto Type = updateCounterMappings(S); @@ -814,11 +829,14 @@ // Use the latest hash version when inserting instrumentation, but use the // version in the indexed profile if we're reading PGO data. PGOHashVersion HashVersion = PGO_HASH_LATEST; - if (auto *PGOReader = CGM.getPGOReader()) + uint64_t ProfileVersion = llvm::IndexedInstrProf::Version; + if (auto *PGOReader = CGM.getPGOReader()) { HashVersion = getPGOHashVersion(PGOReader, CGM); + ProfileVersion = PGOReader->getVersion(); + } RegionCounterMap.reset(new llvm::DenseMap); - MapRegionCounters Walker(HashVersion, *RegionCounterMap); + MapRegionCounters Walker(HashVersion, ProfileVersion, *RegionCounterMap); if (const FunctionDecl *FD = dyn_cast_or_null(D)) Walker.TraverseDecl(const_cast(FD)); else if (const ObjCMethodDecl *MD = dyn_cast_or_null(D)) diff --git a/clang/lib/CodeGen/CoverageMappingGen.h b/clang/lib/CodeGen/CoverageMappingGen.h --- a/clang/lib/CodeGen/CoverageMappingGen.h +++ b/clang/lib/CodeGen/CoverageMappingGen.h @@ -122,6 +122,9 @@ /// Return the coverage mapping translation unit file id /// for the given file. unsigned getFileID(const FileEntry *File); + + /// Return an interface into CodeGenModule. + CodeGenModule &getCodeGenModule() { return CGM; } }; /// Organizes the per-function state that is used while generating diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp --- a/clang/lib/CodeGen/CoverageMappingGen.cpp +++ b/clang/lib/CodeGen/CoverageMappingGen.cpp @@ -92,8 +92,12 @@ /// A region of source code that can be mapped to a counter. class SourceMappingRegion { + /// Primary Counter that is also used for Branch Regions for "True" branches. Counter Count; + /// Secondary Counter used for Branch Regions for "False" branches. + Optional FalseCount; + /// The region's starting location. Optional LocStart; @@ -114,8 +118,20 @@ : Count(Count), LocStart(LocStart), LocEnd(LocEnd), DeferRegion(DeferRegion), GapRegion(GapRegion) {} + SourceMappingRegion(Counter Count, Optional FalseCount, + Optional LocStart, + Optional LocEnd, bool DeferRegion = false, + bool GapRegion = false) + : Count(Count), FalseCount(FalseCount), LocStart(LocStart), + LocEnd(LocEnd), DeferRegion(DeferRegion), GapRegion(GapRegion) {} + const Counter &getCounter() const { return Count; } + const Counter &getFalseCounter() const { + assert(FalseCount && "Region has no alternate counter"); + return *FalseCount; + } + void setCounter(Counter C) { Count = C; } bool hasStartLoc() const { return LocStart.hasValue(); } @@ -146,6 +162,8 @@ bool isGap() const { return GapRegion; } void setGap(bool Gap) { GapRegion = Gap; } + + bool isBranch() const { return FalseCount.hasValue(); } }; /// Spelling locations for the start and end of a source region. @@ -425,6 +443,10 @@ MappingRegions.push_back(CounterMappingRegion::makeGapRegion( Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart, SR.LineEnd, SR.ColumnEnd)); + } else if (Region.isBranch()) { + MappingRegions.push_back(CounterMappingRegion::makeBranchRegion( + Region.getCounter(), Region.getFalseCounter(), *CovFileID, + SR.LineStart, SR.ColumnStart, SR.LineEnd, SR.ColumnEnd)); } else { MappingRegions.push_back(CounterMappingRegion::makeRegion( Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart, @@ -563,12 +585,16 @@ /// Returns the index on the stack where the region was pushed. This can be /// used with popRegions to exit a "scope", ending the region that was pushed. size_t pushRegion(Counter Count, Optional StartLoc = None, - Optional EndLoc = None) { - if (StartLoc) { + Optional EndLoc = None, + Optional FalseCount = None) { + + if (StartLoc && !FalseCount.hasValue()) { MostRecentLocation = *StartLoc; completeDeferred(Count, MostRecentLocation); } - RegionStack.emplace_back(Count, StartLoc, EndLoc); + + RegionStack.emplace_back(Count, FalseCount, StartLoc, EndLoc, + FalseCount.hasValue()); return RegionStack.size() - 1; } @@ -658,49 +684,64 @@ SourceLocation EndLoc = Region.hasEndLoc() ? Region.getEndLoc() : RegionStack[ParentIndex].getEndLoc(); + bool isBranch = Region.isBranch(); size_t StartDepth = locationDepth(StartLoc); size_t EndDepth = locationDepth(EndLoc); while (!SM.isWrittenInSameFile(StartLoc, EndLoc)) { bool UnnestStart = StartDepth >= EndDepth; bool UnnestEnd = EndDepth >= StartDepth; if (UnnestEnd) { - // The region ends in a nested file or macro expansion. Create a - // separate region for each expansion. + // The region ends in a nested file or macro expansion. If the + // region is not a branch region, create a separate region for each + // expansion, and for all regions, update the EndLoc. Branch + // regions should not be split in order to keep a straightforward + // correspondance between the region and its associated branch + // condition, even if the condition spans multiple depths. SourceLocation NestedLoc = getStartOfFileOrMacro(EndLoc); assert(SM.isWrittenInSameFile(NestedLoc, EndLoc)); - if (!isRegionAlreadyAdded(NestedLoc, EndLoc)) - SourceRegions.emplace_back(Region.getCounter(), NestedLoc, EndLoc); + if (!isBranch && !isRegionAlreadyAdded(NestedLoc, EndLoc)) + SourceRegions.emplace_back(Region.getCounter(), NestedLoc, + EndLoc); EndLoc = getPreciseTokenLocEnd(getIncludeOrExpansionLoc(EndLoc)); if (EndLoc.isInvalid()) - llvm::report_fatal_error("File exit not handled before popRegions"); + llvm::report_fatal_error( + "File exit not handled before popRegions"); EndDepth--; } if (UnnestStart) { - // The region begins in a nested file or macro expansion. Create a - // separate region for each expansion. + // The region ends in a nested file or macro expansion. If the + // region is not a branch region, create a separate region for each + // expansion, and for all regions, update the StartLoc. Branch + // regions should not be split in order to keep a straightforward + // correspondance between the region and its associated branch + // condition, even if the condition spans multiple depths. SourceLocation NestedLoc = getEndOfFileOrMacro(StartLoc); assert(SM.isWrittenInSameFile(StartLoc, NestedLoc)); - if (!isRegionAlreadyAdded(StartLoc, NestedLoc)) - SourceRegions.emplace_back(Region.getCounter(), StartLoc, NestedLoc); + if (!isBranch && !isRegionAlreadyAdded(StartLoc, NestedLoc)) + SourceRegions.emplace_back(Region.getCounter(), StartLoc, + NestedLoc); StartLoc = getIncludeOrExpansionLoc(StartLoc); if (StartLoc.isInvalid()) - llvm::report_fatal_error("File exit not handled before popRegions"); + llvm::report_fatal_error( + "File exit not handled before popRegions"); StartDepth--; } } Region.setStartLoc(StartLoc); Region.setEndLoc(EndLoc); - MostRecentLocation = EndLoc; - // If this region happens to span an entire expansion, we need to make - // sure we don't overlap the parent region with it. - if (StartLoc == getStartOfFileOrMacro(StartLoc) && - EndLoc == getEndOfFileOrMacro(EndLoc)) - MostRecentLocation = getIncludeOrExpansionLoc(EndLoc); + if (!isBranch) { + MostRecentLocation = EndLoc; + // If this region happens to span an entire expansion, we need to + // make sure we don't overlap the parent region with it. + if (StartLoc == getStartOfFileOrMacro(StartLoc) && + EndLoc == getEndOfFileOrMacro(EndLoc)) + MostRecentLocation = getIncludeOrExpansionLoc(EndLoc); + } assert(SM.isWrittenInSameFile(Region.getBeginLoc(), EndLoc)); assert(SpellingRegion(SM, Region).isInSourceOrder()); @@ -759,14 +800,61 @@ return ExitCount; } + /// Determine whether the given condition can be constant folded. + bool ConditionFoldsToBool(const Expr *Cond) { + Expr::EvalResult Result; + return (Cond->EvaluateAsInt(Result, CVM.getCodeGenModule().getContext())); + } + + /// Create a Branch Region around an instrumentable condition for coverage + /// and add it to the function's SourceRegions. A branch region tracks a + /// "True" counter and a "False" counter for boolean expressions that + /// result in the generation of a branch. + void createBranchRegion(const Expr *C, Counter TrueCnt, Counter FalseCnt) { + // Check for NULL conditions. + if (!C) + return; + + // Ensure we are an instrumentable condition (i.e. no "&&" or "||"). Push + // region onto RegionStack but immediately pop it (which adds it to the + // function's SourceRegions) because it doesn't apply to any other source + // code other than the Condition. + if (CodeGenFunction::isInstrumentedCondition(C)) { + // If a condition can fold to true or false, the corresponding branch + // will be removed. Create a region with both counters hard-coded to + // zero. This allows us to visualize them in a special way. + // Alternatively, we can prevent any optimization done via + // constant-folding by ensuring that ConstantFoldsToSimpleInteger() in + // CodeGenFunction.c always returns false, but that is very heavy-handed. + if (ConditionFoldsToBool(C)) + popRegions(pushRegion(Counter::getZero(), getStart(C), getEnd(C), + Counter::getZero())); + else + // Otherwise, create a region with the True counter and False counter. + popRegions(pushRegion(TrueCnt, getStart(C), getEnd(C), FalseCnt)); + } + } + + /// Create a Branch Region around a SwitchCase for code coverage + /// and add it to the function's SourceRegions. + void createSwitchCaseRegion(const SwitchCase *SC, Counter TrueCnt, + Counter FalseCnt) { + // Push region onto RegionStack but immediately pop it (which adds it to + // the function's SourceRegions) because it doesn't apply to any other + // source other than the SwitchCase. + popRegions(pushRegion(TrueCnt, getStart(SC), SC->getColonLoc(), FalseCnt)); + } + /// Check whether a region with bounds \c StartLoc and \c EndLoc /// is already added to \c SourceRegions. - bool isRegionAlreadyAdded(SourceLocation StartLoc, SourceLocation EndLoc) { + bool isRegionAlreadyAdded(SourceLocation StartLoc, SourceLocation EndLoc, + bool isBranch = false) { return SourceRegions.rend() != std::find_if(SourceRegions.rbegin(), SourceRegions.rend(), [&](const SourceMappingRegion &Region) { return Region.getBeginLoc() == StartLoc && - Region.getEndLoc() == EndLoc; + Region.getEndLoc() == EndLoc && + Region.isBranch() == isBranch; }); } @@ -783,7 +871,7 @@ if (getRegion().hasEndLoc() && MostRecentLocation == getEndOfFileOrMacro(MostRecentLocation) && isRegionAlreadyAdded(getStartOfFileOrMacro(MostRecentLocation), - MostRecentLocation)) + MostRecentLocation, getRegion().isBranch())) MostRecentLocation = getIncludeOrExpansionLoc(MostRecentLocation); } @@ -827,9 +915,14 @@ // The most nested region for each start location is the one with the // correct count. We avoid creating redundant regions by stopping once // we've seen this region. - if (StartLocs.insert(Loc).second) - SourceRegions.emplace_back(I.getCounter(), Loc, - getEndOfFileOrMacro(Loc)); + if (StartLocs.insert(Loc).second) { + if (I.isBranch()) + SourceRegions.emplace_back(I.getCounter(), I.getFalseCounter(), Loc, + getEndOfFileOrMacro(Loc), I.isBranch()); + else + SourceRegions.emplace_back(I.getCounter(), Loc, + getEndOfFileOrMacro(Loc)); + } Loc = getIncludeOrExpansionLoc(Loc); } I.setStartLoc(getPreciseTokenLocEnd(Loc)); @@ -1070,6 +1163,10 @@ addCounters(BC.BreakCount, subtractCounters(CondCount, BodyCount)); if (OutCount != ParentCount) pushRegion(OutCount); + + // Create Branch Region around condition. + createBranchRegion(S->getCond(), BodyCount, + subtractCounters(CondCount, BodyCount)); } void VisitDoStmt(const DoStmt *S) { @@ -1091,6 +1188,10 @@ addCounters(BC.BreakCount, subtractCounters(CondCount, BodyCount)); if (OutCount != ParentCount) pushRegion(OutCount); + + // Create Branch Region around condition. + createBranchRegion(S->getCond(), BodyCount, + subtractCounters(CondCount, BodyCount)); } void VisitForStmt(const ForStmt *S) { @@ -1138,6 +1239,10 @@ subtractCounters(CondCount, BodyCount)); if (OutCount != ParentCount) pushRegion(OutCount); + + // Create Branch Region around condition. + createBranchRegion(S->getCond(), BodyCount, + subtractCounters(CondCount, BodyCount)); } void VisitCXXForRangeStmt(const CXXForRangeStmt *S) { @@ -1167,6 +1272,10 @@ addCounters(BC.BreakCount, subtractCounters(LoopCount, BodyCount)); if (OutCount != ParentCount) pushRegion(OutCount); + + // Create Branch Region around condition. + createBranchRegion(S->getCond(), BodyCount, + subtractCounters(LoopCount, BodyCount)); } void VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S) { @@ -1231,6 +1340,7 @@ BreakContinueStack.back().ContinueCount = addCounters( BreakContinueStack.back().ContinueCount, BC.ContinueCount); + Counter ParentCount = getRegion().getCounter(); Counter ExitCount = getRegionCounter(S); SourceLocation ExitLoc = getEnd(S); pushRegion(ExitCount); @@ -1239,6 +1349,28 @@ // in a different file. MostRecentLocation = getStart(S); handleFileExit(ExitLoc); + + // Create a Branch Region around each Case. Subtract the case's + // counter from the Parent counter to track the "False" branch count. + Counter CaseCountSum; + bool HasDefaultCase = false; + const SwitchCase *Case = S->getSwitchCaseList(); + for (; Case; Case = Case->getNextSwitchCase()) { + HasDefaultCase = HasDefaultCase || isa(Case); + CaseCountSum = addCounters(CaseCountSum, getRegionCounter(Case)); + createSwitchCaseRegion( + Case, getRegionCounter(Case), + subtractCounters(ParentCount, getRegionCounter(Case))); + } + + // If no explicit default case exists, create a branch region to represent + // the hidden branch, which will be added later by the CodeGen. This region + // will be associated with the switch statement's condition. + if (!HasDefaultCase) { + Counter DefaultTrue = subtractCounters(ParentCount, CaseCountSum); + Counter DefaultFalse = subtractCounters(ParentCount, DefaultTrue); + createBranchRegion(S->getCond(), DefaultTrue, DefaultFalse); + } } void VisitSwitchCase(const SwitchCase *S) { @@ -1299,6 +1431,10 @@ if (OutCount != ParentCount) pushRegion(OutCount); + + // Create Branch Region around condition. + createBranchRegion(S->getCond(), ThenCount, + subtractCounters(ParentCount, ThenCount)); } void VisitCXXTryStmt(const CXXTryStmt *S) { @@ -1342,6 +1478,10 @@ extendRegion(E->getFalseExpr()); propagateCounts(subtractCounters(ParentCount, TrueCount), E->getFalseExpr()); + + // Create Branch Region around condition. + createBranchRegion(E->getCond(), TrueCount, + subtractCounters(ParentCount, TrueCount)); } void VisitBinLAnd(const BinaryOperator *E) { @@ -1349,8 +1489,26 @@ propagateCounts(getRegion().getCounter(), E->getLHS()); handleFileExit(getEnd(E->getLHS())); + // Counter tracks the right hand side of a logical and operator. extendRegion(E->getRHS()); propagateCounts(getRegionCounter(E), E->getRHS()); + + // Extract the RHS's Execution Counter. + Counter RHSExecCnt = getRegionCounter(E); + + // Extract the RHS's "True" Instance Counter. + Counter RHSTrueCnt = getRegionCounter(E->getRHS()); + + // Extract the Parent Region Counter. + Counter ParentCnt = getRegion().getCounter(); + + // Create Branch Region around LHS condition. + createBranchRegion(E->getLHS(), RHSExecCnt, + subtractCounters(ParentCnt, RHSExecCnt)); + + // Create Branch Region around RHS condition. + createBranchRegion(E->getRHS(), RHSTrueCnt, + subtractCounters(RHSExecCnt, RHSTrueCnt)); } void VisitBinLOr(const BinaryOperator *E) { @@ -1358,8 +1516,26 @@ propagateCounts(getRegion().getCounter(), E->getLHS()); handleFileExit(getEnd(E->getLHS())); + // Counter tracks the right hand side of a logical or operator. extendRegion(E->getRHS()); propagateCounts(getRegionCounter(E), E->getRHS()); + + // Extract the RHS's Execution Counter. + Counter RHSExecCnt = getRegionCounter(E); + + // Extract the RHS's "False" Instance Counter. + Counter RHSFalseCnt = getRegionCounter(E->getRHS()); + + // Extract the Parent Region Counter. + Counter ParentCnt = getRegion().getCounter(); + + // Create Branch Region around LHS condition. + createBranchRegion(E->getLHS(), subtractCounters(ParentCnt, RHSExecCnt), + RHSExecCnt); + + // Create Branch Region around RHS condition. + createBranchRegion(E->getRHS(), subtractCounters(RHSExecCnt, RHSFalseCnt), + RHSFalseCnt); } void VisitLambdaExpr(const LambdaExpr *LE) { @@ -1396,11 +1572,20 @@ case CounterMappingRegion::GapRegion: OS << "Gap,"; break; + case CounterMappingRegion::BranchRegion: + OS << "Branch,"; + break; } OS << "File " << R.FileID << ", " << R.LineStart << ":" << R.ColumnStart << " -> " << R.LineEnd << ":" << R.ColumnEnd << " = "; Ctx.dump(R.Count, OS); + + if (R.Kind == CounterMappingRegion::BranchRegion) { + OS << ", "; + Ctx.dump(R.FalseCount, OS); + } + if (R.Kind == CounterMappingRegion::ExpansionRegion) OS << " (Expanded file = " << R.ExpandedFileID << ")"; OS << "\n"; diff --git a/clang/test/CoverageMapping/branch-constfolded.cpp b/clang/test/CoverageMapping/branch-constfolded.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CoverageMapping/branch-constfolded.cpp @@ -0,0 +1,90 @@ +// Test that branch regions are not generated for constant-folded conditions. + +// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name branch-constfolded.cpp %s | FileCheck %s + +// CHECK-LABEL: _Z6fand_0b: +bool fand_0(bool a) { + return false && a; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, 0 +} // CHECK: Branch,File 0, [[@LINE-1]]:19 -> [[@LINE-1]]:20 = #2, (#1 - #2) + +// CHECK-LABEL: _Z6fand_1b: +bool fand_1(bool a) { + return a && true; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = #1, (#0 - #1) +} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:19 = 0, 0 + +// CHECK-LABEL: _Z6fand_2bb: +bool fand_2(bool a, bool b) { + return false && a && b; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, 0 +} // CHECK: Branch,File 0, [[@LINE-1]]:19 -> [[@LINE-1]]:20 = #4, (#3 - #4) + // CHECK: Branch,File 0, [[@LINE-2]]:24 -> [[@LINE-2]]:25 = #2, (#1 - #2) + +// CHECK-LABEL: _Z6fand_3bb: +bool fand_3(bool a, bool b) { + return a && true && b; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = #3, (#0 - #3) +} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:19 = 0, 0 + // CHECK: Branch,File 0, [[@LINE-2]]:23 -> [[@LINE-2]]:24 = #2, (#1 - #2) + +// CHECK-LABEL: _Z6fand_4bb: +bool fand_4(bool a, bool b) { + return a && b && false; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = #3, (#0 - #3) +} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:16 = #4, (#3 - #4) + // CHECK: Branch,File 0, [[@LINE-2]]:20 -> [[@LINE-2]]:25 = 0, 0 + +// CHECK-LABEL: _Z6fand_5b: +bool fand_5(bool a) { + return false && true; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, 0 +} // CHECK: Branch,File 0, [[@LINE-1]]:19 -> [[@LINE-1]]:23 = 0, 0 + +// CHECK-LABEL: _Z6fand_6b: +bool fand_6(bool a) { + return true && a; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = 0, 0 +} // CHECK: Branch,File 0, [[@LINE-1]]:18 -> [[@LINE-1]]:19 = #2, (#1 - #2) + +// CHECK-LABEL: _Z6fand_7b: +bool fand_7(bool a) { + return a && false; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = #1, (#0 - #1) +} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:20 = 0, 0 + +// CHECK-LABEL: _Z5for_0b: +bool for_0(bool a) { + return true || a; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = 0, 0 +} // CHECK: Branch,File 0, [[@LINE-1]]:18 -> [[@LINE-1]]:19 = (#1 - #2), #2 + +// CHECK-LABEL: _Z5for_1b: +bool for_1(bool a) { + return a || false; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = (#0 - #1), #1 +} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:20 = 0, 0 + +// CHECK-LABEL: _Z5for_2bb: +bool for_2(bool a, bool b) { + return true || a || b; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = 0, 0 +} // CHECK: Branch,File 0, [[@LINE-1]]:18 -> [[@LINE-1]]:19 = (#3 - #4), #4 + // CHECK: Branch,File 0, [[@LINE-2]]:23 -> [[@LINE-2]]:24 = (#1 - #2), #2 + +// CHECK-LABEL: _Z5for_3bb: +bool for_3(bool a, bool b) { + return a || false || b; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = (#0 - #3), #3 +} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:20 = 0, 0 + // CHECK: Branch,File 0, [[@LINE-2]]:24 -> [[@LINE-2]]:25 = (#1 - #2), #2 + +// CHECK-LABEL: _Z5for_4bb: +bool for_4(bool a, bool b) { + return a || b || true; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = (#0 - #3), #3 +} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:16 = (#3 - #4), #4 + // CHECK: Branch,File 0, [[@LINE-2]]:20 -> [[@LINE-2]]:24 = 0, 0 + +// CHECK-LABEL: _Z5for_5b: +bool for_5(bool a) { + return true || false; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = 0, 0 +} // CHECK: Branch,File 0, [[@LINE-1]]:18 -> [[@LINE-1]]:23 = 0, 0 + +// CHECK-LABEL: _Z5for_6b: +bool for_6(bool a) { + return false || a; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, 0 +} // CHECK: Branch,File 0, [[@LINE-1]]:19 -> [[@LINE-1]]:20 = (#1 - #2), #2 + +// CHECK-LABEL: _Z5for_7b: +bool for_7(bool a) { + return a || true; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = (#0 - #1), #1 +} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:19 = 0, 0 + diff --git a/clang/test/CoverageMapping/branch-logical-mixed.cpp b/clang/test/CoverageMapping/branch-logical-mixed.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CoverageMapping/branch-logical-mixed.cpp @@ -0,0 +1,64 @@ +// Test to ensure that each branch condition has an associated branch region +// with expected True/False counters. + +// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name branch-logical-mixed.cpp %s | FileCheck %s + + + + +bool func() { // CHECK: File 0, [[@LINE]]:13 -> [[@LINE+55]]:2 = #0 + bool bt0 = true; + bool bt1 = true; + bool bt2 = true; + bool bt3 = true; + bool bt4 = true; + bool bt5 = true; + bool bf0 = false; + bool bf1 = false; + bool bf2 = false; + bool bf3 = false; + bool bf4 = false; + bool bf5 = false; + + bool a = bt0 && // CHECK: Branch,File 0, [[@LINE]]:12 -> [[@LINE]]:15 = #9, (#0 - #9) + bf0 && // CHECK: Branch,File 0, [[@LINE]]:12 -> [[@LINE]]:15 = #10, (#9 - #10) + bt1 && // CHECK: Branch,File 0, [[@LINE]]:12 -> [[@LINE]]:15 = #8, (#7 - #8) + bf1 && // CHECK: Branch,File 0, [[@LINE]]:12 -> [[@LINE]]:15 = #6, (#5 - #6) + bt2 && // CHECK: Branch,File 0, [[@LINE]]:12 -> [[@LINE]]:15 = #4, (#3 - #4) + bf2; // CHECK: Branch,File 0, [[@LINE]]:12 -> [[@LINE]]:15 = #2, (#1 - #2) + + bool b = bt0 || // CHECK: Branch,File 0, [[@LINE]]:12 -> [[@LINE]]:15 = (#0 - #19), #19 + bf0 || // CHECK: Branch,File 0, [[@LINE]]:12 -> [[@LINE]]:15 = (#19 - #20), #20 + bt1 || // CHECK: Branch,File 0, [[@LINE]]:12 -> [[@LINE]]:15 = (#17 - #18), #18 + bf1 || // CHECK: Branch,File 0, [[@LINE]]:12 -> [[@LINE]]:15 = (#15 - #16), #16 + bt2 || // CHECK: Branch,File 0, [[@LINE]]:12 -> [[@LINE]]:15 = (#13 - #14), #14 + bf2; // CHECK: Branch,File 0, [[@LINE]]:12 -> [[@LINE]]:15 = (#11 - #12), #12 + + bool c = (bt0 && // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = #26, (#0 - #26) + bf0) || // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = #27, (#26 - #27) + (bt1 && // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = #28, (#25 - #28) + bf1) || // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = #29, (#28 - #29) + (bt2 && // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = #30, (#24 - #30) + bf2) || // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = #31, (#30 - #31) + (bt3 && // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = #32, (#23 - #32) + bf3) || // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = #33, (#32 - #33) + (bt4 && // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = #34, (#22 - #34) + bf4) || // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = #35, (#34 - #35) + (bf5 && // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = #36, (#21 - #36) + bf5); // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = #37, (#36 - #37) + + bool d = (bt0 || // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = (#0 - #43), #43 + bf0) && // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = (#43 - #44), #44 + (bt1 || // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = (#42 - #45), #45 + bf1) && // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = (#45 - #46), #46 + (bt2 || // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = (#41 - #47), #47 + bf2) && // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = (#47 - #48), #48 + (bt3 || // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = (#40 - #49), #49 + bf3) && // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = (#49 - #50), #50 + (bt4 || // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = (#39 - #51), #51 + bf4) && // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = (#51 - #52), #52 + (bt5 || // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = (#38 - #53), #53 + bf5); // CHECK: Branch,File 0, [[@LINE]]:13 -> [[@LINE]]:16 = (#53 - #54), #54 + + return a && b && c && d; +} diff --git a/clang/test/CoverageMapping/branch-macros.cpp b/clang/test/CoverageMapping/branch-macros.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CoverageMapping/branch-macros.cpp @@ -0,0 +1,43 @@ +// Test that branch regions are generated for conditions in nested macro +// expansions. + +// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name branch-macros.cpp %s | FileCheck %s + +#define COND1 (a == b) +#define COND2 (a != b) +#define COND3 (COND1 && COND2) +#define COND4 (COND3 ? COND2 : COND1) +#define MACRO1 COND3 +#define MACRO2 MACRO1 +#define MACRO3 MACRO2 + + +// CHECK-LABEL: _Z4funcii: +bool func(int a, int b) { + // CHECK: Branch,File 0, [[@LINE+15]]:12 -> [[@LINE+15]]:13 = #17, (#0 - #17) + // CHECK: Branch,File 0, [[@LINE+14]]:17 -> [[@LINE+14]]:18 = #18, (#17 - #18) + // CHECK: Branch,File 0, [[@LINE+13]]:22 -> [[@LINE+13]]:23 = #16, (#15 - #16) + // CHECK: Branch,File 0, [[@LINE+12]]:27 -> [[@LINE+12]]:28 = #14, (#13 - #14) + // CHECK: Branch,File 0, [[@LINE+11]]:32 -> [[@LINE+11]]:33 = #12, (#11 - #12) + bool c = COND1 && COND2; // CHECK: Branch,File 1, [[@LINE-16]]:15 -> [[@LINE-16]]:23 = #1, (#0 - #1) + // CHECK: Branch,File 2, [[@LINE-16]]:15 -> [[@LINE-16]]:23 = #2, (#1 - #2) + bool d = COND3; // CHECK: Branch,File 7, [[@LINE-18]]:15 -> [[@LINE-18]]:23 = #3, (#0 - #3) + // CHECK: Branch,File 8, [[@LINE-18]]:15 -> [[@LINE-18]]:23 = #4, (#3 - #4) + bool e = MACRO1; // CHECK: Branch,File 12, [[@LINE-20]]:15 -> [[@LINE-20]]:23 = #5, (#0 - #5) + // CHECK: Branch,File 13, [[@LINE-20]]:15 -> [[@LINE-20]]:23 = #6, (#5 - #6) + bool f = MACRO2; // CHECK: Branch,File 16, [[@LINE-22]]:15 -> [[@LINE-22]]:23 = #7, (#0 - #7) + // CHECK: Branch,File 17, [[@LINE-22]]:15 -> [[@LINE-22]]:23 = #8, (#7 - #8) + bool g = MACRO3; // CHECK: Branch,File 19, [[@LINE-24]]:15 -> [[@LINE-24]]:23 = #9, (#0 - #9) + // CHECK: Branch,File 20, [[@LINE-24]]:15 -> [[@LINE-24]]:23 = #10, (#9 - #10) + return c && d && e && f && g; +} + +// CHECK-LABEL: _Z5func2ii: +bool func2(int a, int b) { + bool h = MACRO3 || COND4;// CHECK: Branch,File 2, [[@LINE-28]]:15 -> [[@LINE-28]]:38 = (#1 - #2), #2 + // CHECK: Branch,File 8, [[@LINE-32]]:15 -> [[@LINE-32]]:23 = #6, (#1 - #6) + // CHECK: Branch,File 9, [[@LINE-32]]:15 -> [[@LINE-32]]:23 = #7, (#6 - #7) + // CHECK: Branch,File 11, [[@LINE-34]]:15 -> [[@LINE-34]]:23 = #3, (#0 - #3) + // CHECK: Branch,File 12, [[@LINE-34]]:15 -> [[@LINE-34]]:23 = #4, (#3 - #4) + return h; +} diff --git a/clang/test/CoverageMapping/branch-mincounters.cpp b/clang/test/CoverageMapping/branch-mincounters.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CoverageMapping/branch-mincounters.cpp @@ -0,0 +1,54 @@ +// Test to ensure right number of counters are allocated and used for nested +// logical operators on branch conditions for branch coverage. + +// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name branch-logical-mixed.cpp %s | FileCheck %s + + +// CHECK-LABEL: _Z5func1ii: +bool func1(int a, int b) { + bool b0 = a <= b; + bool b1 = a == b; + bool b2 = a >= b; + + // This should allocate a RHS branch counter on b2 (counter #3). + bool c = b0 && (b1 || b2); + // CHECK: Branch,File 0, [[@LINE-1]]:12 -> [[@LINE-1]]:14 = #1, (#0 - #1) + // CHECK: Branch,File 0, [[@LINE-2]]:19 -> [[@LINE-2]]:21 = (#1 - #2), #2 + // CHECK: Branch,File 0, [[@LINE-3]]:25 -> [[@LINE-3]]:27 = (#2 - #3), #3 + + return c; +} + +// CHECK-LABEL: _Z5func2ii: +bool func2(int a, int b) { + bool b0 = a <= b; + bool b1 = a == b; + bool b2 = a >= b; + + // This should allocate a RHS branch counter on b1 and b2 (counters #2, #4) + // This could possibly be further optimized through counter reuse (future). + bool c = (b0 && b1) || b2; + // CHECK: Branch,File 0, [[@LINE-1]]:13 -> [[@LINE-1]]:15 = #3, (#0 - #3) + // CHECK: Branch,File 0, [[@LINE-2]]:19 -> [[@LINE-2]]:21 = #4, (#3 - #4) + // CHECK: Branch,File 0, [[@LINE-3]]:26 -> [[@LINE-3]]:28 = (#1 - #2), #2 + + return c; +} + +// CHECK-LABEL: _Z5func3ii: +bool func3(int a, int b) { + bool b0 = a <= b; + bool b1 = a == b; + bool b2 = a >= b; + bool b3 = a < b; + + // This should allocate a RHS branch counter on b1 and b3 (counters #3, #5) + // This could possibly be further optimized through counter reuse (future). + bool c = (b0 || b1) && (b2 || b3); + // CHECK: Branch,File 0, [[@LINE-1]]:13 -> [[@LINE-1]]:15 = (#0 - #2), #2 + // CHECK: Branch,File 0, [[@LINE-2]]:19 -> [[@LINE-2]]:21 = (#2 - #3), #3 + // CHECK: Branch,File 0, [[@LINE-3]]:27 -> [[@LINE-3]]:29 = (#1 - #4), #4 + // CHECK: Branch,File 0, [[@LINE-4]]:33 -> [[@LINE-4]]:35 = (#4 - #5), #5 + + return c; +} diff --git a/clang/test/CoverageMapping/branch-templates.cpp b/clang/test/CoverageMapping/branch-templates.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CoverageMapping/branch-templates.cpp @@ -0,0 +1,32 @@ +// Test that branch regions are generated for conditions in function template +// instantiations. + +// RUN: %clang_cc1 -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name branch-templates.cpp %s | FileCheck %s + +template +void unused(T x) { + return; +} + +template +int func(T x) { + if(x) + return 0; + else + return 1; + int j = 1; +} + +int main() { + func(0); + func(true); + func(0.0); + return 0; +} + +// CHECK-LABEL: _Z4funcIiEiT_: +// CHECK: Branch,File 0, [[@LINE-15]]:6 -> [[@LINE-15]]:7 = #1, (#0 - #1) +// CHECK-LABEL: _Z4funcIbEiT_: +// CHECK: Branch,File 0, [[@LINE-17]]:6 -> [[@LINE-17]]:7 = #1, (#0 - #1) +// CHECK-LABEL: _Z4funcIfEiT_: +// CHECK: Branch,File 0, [[@LINE-19]]:6 -> [[@LINE-19]]:7 = #1, (#0 - #1) diff --git a/clang/test/CoverageMapping/continue.c b/clang/test/CoverageMapping/continue.c --- a/clang/test/CoverageMapping/continue.c +++ b/clang/test/CoverageMapping/continue.c @@ -1,7 +1,8 @@ // RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name continue.c %s | FileCheck %s -int main() { // CHECK: File 0, [[@LINE]]:12 -> [[@LINE+21]]:2 = #0 - int j = 0; // CHECK-NEXT: File 0, [[@LINE+2]]:18 -> [[@LINE+2]]:24 = (#0 + #1) +int main() { // CHECK: File 0, [[@LINE]]:12 -> [[@LINE+22]]:2 = #0 + int j = 0; // CHECK-NEXT: File 0, [[@LINE+3]]:18 -> [[@LINE+3]]:24 = (#0 + #1) + // CHECK-NEXT: Branch,File 0, [[@LINE+2]]:18 -> [[@LINE+2]]:24 = #1, #0 // CHECK-NEXT: File 0, [[@LINE+1]]:26 -> [[@LINE+1]]:29 = #1 for(int i = 0; i < 20; ++i) { // CHECK: File 0, [[@LINE]]:31 -> [[@LINE+17]]:4 = #1 if(i < 10) { // CHECK: File 0, [[@LINE]]:16 -> [[@LINE+13]]:6 = #2 diff --git a/clang/test/CoverageMapping/coroutine.cpp b/clang/test/CoverageMapping/coroutine.cpp --- a/clang/test/CoverageMapping/coroutine.cpp +++ b/clang/test/CoverageMapping/coroutine.cpp @@ -37,10 +37,11 @@ }; // CHECK-LABEL: _Z2f1i: -int f1(int x) { // CHECK-NEXT: File 0, [[@LINE]]:15 -> [[@LINE+7]]:2 = #0 +int f1(int x) { // CHECK-NEXT: File 0, [[@LINE]]:15 -> [[@LINE+8]]:2 = #0 if (x > 42) { // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:13 = #0 - ++x; // CHECK-NEXT: Gap,File 0, [[@LINE-1]]:14 -> [[@LINE-1]]:15 = #1 - } else { // CHECK-NEXT: File 0, [[@LINE-2]]:15 -> [[@LINE]]:4 = #1 + // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:13 = #1, (#0 - #1) + ++x; // CHECK-NEXT: Gap,File 0, [[@LINE-2]]:14 -> [[@LINE-2]]:15 = #1 + } else { // CHECK-NEXT: File 0, [[@LINE-3]]:15 -> [[@LINE]]:4 = #1 co_return x + 42; // CHECK-NEXT: Gap,File 0, [[@LINE-1]]:4 -> [[@LINE-1]]:10 = (#0 - #1) } // CHECK-NEXT: File 0, [[@LINE-2]]:10 -> [[@LINE]]:4 = (#0 - #1) co_return x; // CHECK-NEXT: Gap,File 0, [[@LINE-1]]:4 -> [[@LINE]]:3 = #1 diff --git a/clang/test/CoverageMapping/if.cpp b/clang/test/CoverageMapping/if.cpp --- a/clang/test/CoverageMapping/if.cpp +++ b/clang/test/CoverageMapping/if.cpp @@ -3,40 +3,47 @@ int nop() { return 0; } // CHECK-LABEL: _Z3foov: - // CHECK-NEXT: [[@LINE+1]]:12 -> [[@LINE+6]]:2 = #0 + // CHECK-NEXT: [[@LINE+2]]:12 -> [[@LINE+7]]:2 = #0 + // CHECK-NEXT: Branch,File 0, [[@LINE+2]]:15 -> [[@LINE+2]]:19 = 0, 0 void foo() { // CHECK-NEXT: Gap,File 0, [[@LINE+1]]:20 -> [[@LINE+1]]:22 = #2 if (int j = true ? nop() // CHECK-NEXT: [[@LINE]]:22 -> [[@LINE]]:27 = #2 : nop(); // CHECK-NEXT: [[@LINE]]:22 -> [[@LINE]]:27 = (#0 - #2) j) // CHECK-NEXT: [[@LINE]]:7 -> [[@LINE]]:8 = #0 - ++j; // CHECK-NEXT: [[@LINE-1]]:9 -> [[@LINE]]:5 = #1 -} // CHECK-NEXT: [[@LINE-1]]:5 -> [[@LINE-1]]:8 = #1 - + ++j; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:8 = #1, (#0 - #1) +} // CHECK-NEXT: [[@LINE-2]]:9 -> [[@LINE-1]]:5 = #1 + // CHECK-NEXT: [[@LINE-2]]:5 -> [[@LINE-2]]:8 = #1 // CHECK-LABEL: main: int main() { // CHECK: File 0, [[@LINE]]:12 -> {{[0-9]+}}:2 = #0 int i = 0; - // CHECK-NEXT: File 0, [[@LINE+2]]:6 -> [[@LINE+2]]:12 = #0 + // CHECK-NEXT: File 0, [[@LINE+3]]:6 -> [[@LINE+3]]:12 = #0 + // CHECK-NEXT: Branch,File 0, [[@LINE+2]]:6 -> [[@LINE+2]]:12 = #1, (#0 - #1) // CHECK-NEXT: Gap,File 0, [[@LINE+1]]:13 -> [[@LINE+1]]:14 = #1 if(i == 0) i = 1; // CHECK-NEXT: File 0, [[@LINE]]:14 -> [[@LINE]]:19 = #1 - // CHECK-NEXT: File 0, [[@LINE+1]]:6 -> [[@LINE+1]]:12 = #0 + // CHECK-NEXT: File 0, [[@LINE+2]]:6 -> [[@LINE+2]]:12 = #0 + // CHECK-NEXT: Branch,File 0, [[@LINE+1]]:6 -> [[@LINE+1]]:12 = #2, (#0 - #2) if(i == 1) // CHECK-NEXT: Gap,File 0, [[@LINE]]:13 -> [[@LINE+1]]:5 = #2 i = 2; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:10 = #2 - // CHECK-NEXT: File 0, [[@LINE+1]]:6 -> [[@LINE+1]]:12 = #0 + // CHECK-NEXT: File 0, [[@LINE+2]]:6 -> [[@LINE+2]]:12 = #0 + // CHECK-NEXT: Branch,File 0, [[@LINE+1]]:6 -> [[@LINE+1]]:12 = #3, (#0 - #3) if(i == 0) { i = 1; // CHECK-NEXT: Gap,File 0, [[@LINE]]:13 -> [[@LINE]]:14 = #3 i = 2; // CHECK-NEXT: File 0, [[@LINE-1]]:14 -> [[@LINE+1]]:4 = #3 } - // CHECK-NEXT: File 0, [[@LINE+1]]:6 -> [[@LINE+1]]:12 = #0 + // CHECK-NEXT: File 0, [[@LINE+2]]:6 -> [[@LINE+2]]:12 = #0 + // CHECK-NEXT: Branch,File 0, [[@LINE+1]]:6 -> [[@LINE+1]]:12 = #4, (#0 - #4) if(i != 0) { // CHECK-NEXT: Gap,File 0, [[@LINE]]:13 -> [[@LINE]]:14 = #4 i = 1; // CHECK-NEXT: File 0, [[@LINE-1]]:14 -> [[@LINE+1]]:4 = #4 } else { // CHECK-NEXT: Gap,File 0, [[@LINE]]:4 -> [[@LINE]]:10 = (#0 - #4) i = 3; // CHECK-NEXT: File 0, [[@LINE-1]]:10 -> [[@LINE+1]]:4 = (#0 - #4) } + // CHECK-NEXT: Branch,File 0, [[@LINE+1]]:7 -> [[@LINE+1]]:13 = #5, (#0 - #5) i = i == 0? // CHECK-NEXT: Gap,File 0, [[@LINE]]:13 -> [[@LINE+1]]:9 = #5 i + 1 : // CHECK-NEXT: File 0, [[@LINE]]:9 -> [[@LINE]]:14 = #5 i + 2; // CHECK-NEXT: File 0, [[@LINE]]:9 -> [[@LINE]]:14 = (#0 - #5) + // CHECK-NEXT: Branch,File 0, [[@LINE+3]]:7 -> [[@LINE+3]]:13 = #6, (#0 - #6) // CHECK-NEXT: Gap,File 0, [[@LINE+2]]:13 -> [[@LINE+2]]:14 = #6 // CHECK-NEXT: File 0, [[@LINE+1]]:14 -> [[@LINE+1]]:20 = #6 i = i == 0?i + 12:i + 10; // CHECK-NEXT: File 0, [[@LINE]]:21 -> [[@LINE]]:27 = (#0 - #6) diff --git a/clang/test/CoverageMapping/label.cpp b/clang/test/CoverageMapping/label.cpp --- a/clang/test/CoverageMapping/label.cpp +++ b/clang/test/CoverageMapping/label.cpp @@ -2,7 +2,8 @@ // CHECK: func void func() { // CHECK-NEXT: File 0, [[@LINE]]:13 -> {{[0-9]+}}:2 = #0 - int i = 0; // CHECK-NEXT: File 0, [[@LINE+2]]:14 -> [[@LINE+2]]:20 = (#0 + #3) + int i = 0; // CHECK-NEXT: File 0, [[@LINE+3]]:14 -> [[@LINE+3]]:20 = (#0 + #3) + // CHECK-NEXT: Branch,File 0, [[@LINE+2]]:14 -> [[@LINE+2]]:20 = #1, ((#0 + #3) - #1) // CHECK-NEXT: File 0, [[@LINE+1]]:22 -> [[@LINE+1]]:25 = #3 for(i = 0; i < 10; ++i) { // CHECK: File 0, [[@LINE]]:27 -> [[@LINE+11]]:4 = #1 // CHECK-NEXT: File 0, [[@LINE+1]]:8 -> [[@LINE+1]]:13 = #1 diff --git a/clang/test/CoverageMapping/logical.cpp b/clang/test/CoverageMapping/logical.cpp --- a/clang/test/CoverageMapping/logical.cpp +++ b/clang/test/CoverageMapping/logical.cpp @@ -1,18 +1,25 @@ // RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name logical.cpp %s | FileCheck %s -int main() { // CHECK: File 0, [[@LINE]]:12 -> [[@LINE+15]]:2 = #0 +int main() { // CHECK: File 0, [[@LINE]]:12 -> [[@LINE+22]]:2 = #0 bool bt = true; bool bf = false; bool a = bt && bf; // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE]]:14 = #0 - // CHECK-NEXT: File 0, [[@LINE-1]]:18 -> [[@LINE-1]]:20 = #1 + // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:12 -> [[@LINE-1]]:14 = #1, (#0 - #1) + // CHECK-NEXT: File 0, [[@LINE-2]]:18 -> [[@LINE-2]]:20 = #1 + // CHECK-NEXT: Branch,File 0, [[@LINE-3]]:18 -> [[@LINE-3]]:20 = #2, (#1 - #2) a = bt && // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:9 = #0 - bf; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:9 = #2 - + bf; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:9 = #3, (#0 - #3) + // CHECK-NEXT: File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:9 = #3 + // CHECK-NEXT: Branch,File 0, [[@LINE-2]]:7 -> [[@LINE-2]]:9 = #4, (#3 - #4) a = bf || bt; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:9 = #0 - // CHECK-NEXT: File 0, [[@LINE-1]]:13 -> [[@LINE-1]]:15 = #3 + // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:9 = (#0 - #5), #5 + // CHECK-NEXT: File 0, [[@LINE-2]]:13 -> [[@LINE-2]]:15 = #5 + // CHECK-NEXT: Branch,File 0, [[@LINE-3]]:13 -> [[@LINE-3]]:15 = (#5 - #6), #6 a = bf || // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:9 = #0 - bt; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:9 = #4 + bt; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:9 = (#0 - #7), #7 + // CHECK-NEXT: File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:9 = #7 + // CHECK-NEXT: Branch,File 0, [[@LINE-2]]:7 -> [[@LINE-2]]:9 = (#7 - #8), #8 return 0; } diff --git a/clang/test/CoverageMapping/loopmacro.c b/clang/test/CoverageMapping/loopmacro.c --- a/clang/test/CoverageMapping/loopmacro.c +++ b/clang/test/CoverageMapping/loopmacro.c @@ -6,6 +6,7 @@ // CHECK-NEXT: Expansion,File 0, {{[0-9]+}}:7 -> {{[0-9]+}}:20 = (#0 + #1) // CHECK-NEXT: File 0, {{[0-9]+}}:12 -> {{[0-9]+}}:30 = (#0 + #1) +// CHECK-NEXT: Branch,File 0, {{[0-9]+}}:12 -> {{[0-9]+}}:30 = #1, #0 // CHECK-NEXT: File 1, [[@LINE+4]]:4 -> [[@LINE+6]]:23 = (#0 + #1) // CHECK-NEXT: Expansion,File 1, [[@LINE+3]]:5 -> [[@LINE+3]]:16 = (#0 + #1) // CHECK-NEXT: Expansion,File 1, [[@LINE+3]]:16 -> [[@LINE+3]]:21 = (#0 + #1) diff --git a/clang/test/CoverageMapping/loops.cpp b/clang/test/CoverageMapping/loops.cpp --- a/clang/test/CoverageMapping/loops.cpp +++ b/clang/test/CoverageMapping/loops.cpp @@ -3,7 +3,8 @@ // CHECK: rangedFor void rangedFor() { // CHECK-NEXT: File 0, [[@LINE]]:18 -> {{[0-9]+}}:2 = #0 int arr[] = { 1, 2, 3, 4, 5 }; - int sum = 0; // CHECK: Gap,File 0, [[@LINE+1]]:20 -> [[@LINE+1]]:21 = #1 + int sum = 0; // CHECK-NEXT: Branch,File 0, [[@LINE+2]]:14 -> [[@LINE+2]]:15 = #1, (#0 - #3) + // CHECK: Gap,File 0, [[@LINE+1]]:20 -> [[@LINE+1]]:21 = #1 for(auto i : arr) { // CHECK: File 0, [[@LINE]]:21 -> [[@LINE+6]]:4 = #1 if (i == 3) continue; // CHECK: File 0, [[@LINE]]:7 -> [[@LINE]]:15 = #2 @@ -19,29 +20,34 @@ // CHECK: main: int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> {{.*}}:2 = #0 // CHECK-NEXT: File 0, [[@LINE+1]]:18 -> [[@LINE+1]]:24 = (#0 + #1) - for(int i = 0; i < 10; ++i) // CHECK-NEXT: File 0, [[@LINE]]:26 -> [[@LINE]]:29 = #1 - ; // CHECK-NEXT: Gap,File 0, [[@LINE-1]]:30 -> [[@LINE]]:6 = #1 - // CHECK-NEXT: File 0, [[@LINE-1]]:6 -> [[@LINE-1]]:7 = #1 + for(int i = 0; i < 10; ++i) // CHECK-NEXT: Branch,File 0, [[@LINE]]:18 -> [[@LINE]]:24 = #1, #0 + ; // CHECK-NEXT: File 0, [[@LINE-1]]:26 -> [[@LINE-1]]:29 = #1 + // CHECK-NEXT: Gap,File 0, [[@LINE-2]]:30 -> [[@LINE-1]]:6 = #1 + // CHECK-NEXT: File 0, [[@LINE-2]]:6 -> [[@LINE-2]]:7 = #1 for(int i = 0; i < 10; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:13 = (#0 + #2) - ++i) // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:10 = #2 - { // CHECK-NEXT: Gap,File 0, [[@LINE-1]]:11 -> [[@LINE]]:3 = #2 + ++i) // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:13 = #2, #0 + // CHECK-NEXT: File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:10 = #2 + { // CHECK-NEXT: Gap,File 0, [[@LINE-2]]:11 -> [[@LINE]]:3 = #2 int x = 0; // CHECK-NEXT: File 0, [[@LINE-1]]:3 -> [[@LINE+1]]:4 = #2 } - int j = 0; // CHECK-NEXT: File 0, [[@LINE+1]]:9 -> [[@LINE+1]]:14 = (#0 + #3) + int j = 0; // CHECK-NEXT: File 0, [[@LINE+2]]:9 -> [[@LINE+2]]:14 = (#0 + #3) + // CHECK-NEXT: Branch,File 0, [[@LINE+1]]:9 -> [[@LINE+1]]:14 = #3, #0 while(j < 5) ++j; // CHECK-NEXT: Gap,File 0, [[@LINE]]:15 -> [[@LINE]]:16 = #3 // CHECK-NEXT: File 0, [[@LINE-1]]:16 -> [[@LINE-1]]:19 = #3 do { // CHECK-NEXT: File 0, [[@LINE]]:6 -> [[@LINE+2]]:4 = (#0 + #4) ++j; } while(j < 10); // CHECK-NEXT: File 0, [[@LINE]]:11 -> [[@LINE]]:17 = (#0 + #4) + // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:11 -> [[@LINE-1]]:17 = #4, #0 j = 0; while // CHECK-NEXT: File 0, [[@LINE+1]]:5 -> [[@LINE+1]]:10 = (#0 + #5) - (j < 5) // CHECK-NEXT: Gap,File 0, [[@LINE]]:11 -> [[@LINE+1]]:6 = #5 - ++j; // CHECK-NEXT: File 0, [[@LINE]]:6 -> [[@LINE]]:9 = #5 - do + (j < 5) // CHECK-NEXT: Branch,File 0, [[@LINE]]:5 -> [[@LINE]]:10 = #5, #0 + ++j; // CHECK-NEXT: Gap,File 0, [[@LINE-1]]:11 -> [[@LINE]]:6 = #5 + do // CHECK-NEXT: File 0, [[@LINE-1]]:6 -> [[@LINE-1]]:9 = #5 ++j; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:8 = (#0 + #6) while(j < 10); // CHECK-NEXT: File 0, [[@LINE]]:9 -> [[@LINE]]:15 = (#0 + #6) + // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:9 -> [[@LINE-1]]:15 = #6, #0 rangedFor(); return 0; } diff --git a/clang/test/CoverageMapping/macro-expansion.c b/clang/test/CoverageMapping/macro-expansion.c --- a/clang/test/CoverageMapping/macro-expansion.c +++ b/clang/test/CoverageMapping/macro-expansion.c @@ -1,42 +1,53 @@ // RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name macro-expansion.c %s | FileCheck %s // CHECK: func -// CHECK: File 1, [[@LINE+5]]:12 -> [[@LINE+5]]:38 = #0 -// CHECK-NEXT: File 1, [[@LINE+4]]:15 -> [[@LINE+4]]:28 = (#0 + #2) -// CHECK-NEXT: File 1, [[@LINE+3]]:21 -> [[@LINE+3]]:22 = (#0 + #2) -// CHECK: File 1, [[@LINE+2]]:24 -> [[@LINE+2]]:26 = #3 -// CHECK-NEXT: File 1, [[@LINE+1]]:36 -> [[@LINE+1]]:37 = (#0 + #2) +// CHECK: File 1, [[@LINE+7]]:12 -> [[@LINE+7]]:38 = #0 +// CHECK-NEXT: File 1, [[@LINE+6]]:15 -> [[@LINE+6]]:28 = (#0 + #2) +// CHECK-NEXT: File 1, [[@LINE+5]]:21 -> [[@LINE+5]]:22 = (#0 + #2) +// CHECK: Branch,File 1, [[@LINE+4]]:21 -> [[@LINE+4]]:22 = 0, 0 +// CHECK-NEXT: File 1, [[@LINE+3]]:24 -> [[@LINE+3]]:26 = #3 +// CHECK-NEXT: File 1, [[@LINE+2]]:36 -> [[@LINE+2]]:37 = (#0 + #2) +// CHECK-NEXT: Branch,File 1, [[@LINE+1]]:36 -> [[@LINE+1]]:37 = 0, 0 #define M1 do { if (0) {} } while (0) -// CHECK-NEXT: File 2, [[@LINE+10]]:15 -> [[@LINE+10]]:41 = #0 -// CHECK-NEXT: File 2, [[@LINE+9]]:18 -> [[@LINE+9]]:31 = (#0 + #4) -// CHECK-NEXT: File 2, [[@LINE+8]]:24 -> [[@LINE+8]]:25 = (#0 + #4) -// CHECK: File 2, [[@LINE+7]]:27 -> [[@LINE+7]]:29 = #5 -// CHECK-NEXT: File 2, [[@LINE+6]]:39 -> [[@LINE+6]]:40 = (#0 + #4) -// CHECK-NEXT: File 3, [[@LINE+5]]:15 -> [[@LINE+5]]:41 = #0 -// CHECK-NEXT: File 3, [[@LINE+4]]:18 -> [[@LINE+4]]:31 = (#0 + #6) -// CHECK-NEXT: File 3, [[@LINE+3]]:24 -> [[@LINE+3]]:25 = (#0 + #6) -// CHECK: File 3, [[@LINE+2]]:27 -> [[@LINE+2]]:29 = #7 -// CHECK-NEXT: File 3, [[@LINE+1]]:39 -> [[@LINE+1]]:40 = (#0 + #6) +// CHECK-NEXT: File 2, [[@LINE+12]]:15 -> [[@LINE+12]]:41 = #0 +// CHECK-NEXT: File 2, [[@LINE+11]]:18 -> [[@LINE+11]]:31 = (#0 + #4) +// CHECK-NEXT: File 2, [[@LINE+10]]:24 -> [[@LINE+10]]:25 = (#0 + #4) +// CHECK: File 2, [[@LINE+9]]:27 -> [[@LINE+9]]:29 = #5 +// CHECK-NEXT: File 2, [[@LINE+8]]:39 -> [[@LINE+8]]:40 = (#0 + #4) +// CHECK-NEXT: Branch,File 2, [[@LINE+7]]:39 -> [[@LINE+7]]:40 = 0, 0 +// CHECK-NEXT: File 3, [[@LINE+6]]:15 -> [[@LINE+6]]:41 = #0 +// CHECK-NEXT: File 3, [[@LINE+5]]:18 -> [[@LINE+5]]:31 = (#0 + #6) +// CHECK-NEXT: File 3, [[@LINE+4]]:24 -> [[@LINE+4]]:25 = (#0 + #6) +// CHECK: File 3, [[@LINE+3]]:27 -> [[@LINE+3]]:29 = #7 +// CHECK-NEXT: File 3, [[@LINE+2]]:39 -> [[@LINE+2]]:40 = (#0 + #6) +// CHECK-NEXT: Branch,File 3, [[@LINE+1]]:39 -> [[@LINE+1]]:40 = 0, 0 #define M2(x) do { if (x) {} } while (0) -// CHECK-NEXT: File 4, [[@LINE+4]]:15 -> [[@LINE+4]]:38 = #0 -// CHECK-NEXT: File 4, [[@LINE+3]]:18 -> [[@LINE+3]]:28 = (#0 + #8) -// CHECK-NEXT: Expansion,File 4, [[@LINE+2]]:20 -> [[@LINE+2]]:22 = (#0 + #8) -// CHECK-NEXT: File 4, [[@LINE+1]]:36 -> [[@LINE+1]]:37 = (#0 + #8) +// CHECK-NEXT: File 4, [[@LINE+5]]:15 -> [[@LINE+5]]:38 = #0 +// CHECK-NEXT: File 4, [[@LINE+4]]:18 -> [[@LINE+4]]:28 = (#0 + #8) +// CHECK-NEXT: Expansion,File 4, [[@LINE+3]]:20 -> [[@LINE+3]]:22 = (#0 + #8) +// CHECK-NEXT: File 4, [[@LINE+2]]:36 -> [[@LINE+2]]:37 = (#0 + #8) +// CHECK-NEXT: Branch,File 4, [[@LINE+1]]:36 -> [[@LINE+1]]:37 = 0, 0 #define M3(x) do { M2(x); } while (0) -// CHECK-NEXT: File 5, [[@LINE+3]]:15 -> [[@LINE+3]]:27 = #0 -// CHECK-NEXT: File 5, [[@LINE+2]]:16 -> [[@LINE+2]]:19 = #0 +// CHECK-NEXT: File 5, [[@LINE+4]]:15 -> [[@LINE+4]]:27 = #0 +// CHECK-NEXT: File 5, [[@LINE+3]]:16 -> [[@LINE+3]]:19 = #0 +// CHECK-NEXT: Branch,File 5, [[@LINE+2]]:16 -> [[@LINE+2]]:19 = #12, (#0 - #12) // CHECK-NEXT: File 5, [[@LINE+1]]:23 -> [[@LINE+1]]:26 = #12 #define M4(x) ((x) && (x)) -// CHECK-NEXT: File 6, [[@LINE+3]]:15 -> [[@LINE+3]]:27 = #0 -// CHECK-NEXT: File 6, [[@LINE+2]]:16 -> [[@LINE+2]]:19 = #0 -// CHECK-NEXT: File 6, [[@LINE+1]]:23 -> [[@LINE+1]]:26 = #14 +// CHECK-NEXT: Branch,File 5, [[@LINE-1]]:23 -> [[@LINE-1]]:26 = #13, (#12 - #13) +// CHECK-NEXT: File 6, [[@LINE+4]]:15 -> [[@LINE+4]]:27 = #0 +// CHECK-NEXT: File 6, [[@LINE+3]]:16 -> [[@LINE+3]]:19 = #0 +// CHECK-NEXT: Branch,File 6, [[@LINE+2]]:16 -> [[@LINE+2]]:19 = (#0 - #15), #15 +// CHECK-NEXT: File 6, [[@LINE+1]]:23 -> [[@LINE+1]]:26 = #15 #define M5(x) ((x) || (x)) +// CHECK-NEXT: Branch,File 6, [[@LINE-1]]:23 -> [[@LINE-1]]:26 = (#15 - #16), #16 // CHECK-NEXT: File 7, [[@LINE+1]]:15 -> [[@LINE+1]]:26 = #0 #define M6(x) ((x) + (x)) +// CHECK-NEXT: Branch,File 7, [[@LINE-1]]:15 -> [[@LINE-1]]:26 = #17, (#0 - #17) // CHECK-NEXT: File 8, [[@LINE+1]]:15 -> [[@LINE+1]]:18 = #0 #define M7(x) (x) // Check for the expansion of M2 within M3. +// CHECK-NEXT: Branch,File 8, [[@LINE-3]]:15 -> [[@LINE-3]]:18 = #18, (#0 - #18) // CHECK-NEXT: File 9, {{[0-9]+}}:15 -> {{[0-9]+}}:41 = (#0 + #8) // CHECK-NEXT: File 9, {{[0-9]+}}:18 -> {{[0-9]+}}:31 = ((#0 + #8) + #9) // CHECK-NEXT: File 9, {{[0-9]+}}:24 -> {{[0-9]+}}:25 = ((#0 + #8) + #9) diff --git a/clang/test/CoverageMapping/macro-expressions.cpp b/clang/test/CoverageMapping/macro-expressions.cpp --- a/clang/test/CoverageMapping/macro-expressions.cpp +++ b/clang/test/CoverageMapping/macro-expressions.cpp @@ -77,11 +77,13 @@ // CHECK-NEXT: File 0, [[@LINE+2]]:6 -> [[@LINE+2]]:8 = (#0 + #6) // CHECK-NEXT: Expansion,File 0, [[@LINE+1]]:16 -> [[@LINE+1]]:21 = (#0 + #6) do {} while (NEXPR(i)); - // CHECK-NEXT: Expansion,File 0, [[@LINE+3]]:8 -> [[@LINE+3]]:12 = #0 + // CHECK-NEXT: Expansion,File 0, [[@LINE+4]]:8 -> [[@LINE+4]]:12 = #0 + // CHECK-NEXT: Branch,File 0, [[@LINE+3]]:21 -> [[@LINE+3]]:22 = #7, #0 // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:23 -> [[@LINE+2]]:26 = #0 // CHECK: File 0, [[@LINE+1]]:42 -> [[@LINE+1]]:44 = #7 for (DECL(int, j) : ARR(int, 1, 2, 3)) {} + // CHECK-NEXT: Branch,File 0, [[@LINE+3]]:10 -> [[@LINE+3]]:11 = #8, (#0 - #8) // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:14 -> [[@LINE+2]]:20 = #0 // CHECK-NEXT: Expansion,File 0, [[@LINE+1]]:23 -> [[@LINE+1]]:29 = #0 (void)(i ? PRIo64 : PRIu64); @@ -92,6 +94,7 @@ // CHECK: File 0, [[@LINE+2]]:28 -> [[@LINE+2]]:29 = #10 // CHECK-NEXT: File 0, [[@LINE+1]]:32 -> [[@LINE+1]]:33 = ((#0 - #9) - #10) (void)(i ? i : EXPR(i) ? i : 0); + // CHECK-NEXT: Branch,File 0, [[@LINE+4]]:10 -> [[@LINE+4]]:11 = #11, (#0 - #11) // CHECK-NEXT: Expansion,File 0, [[@LINE+3]]:15 -> [[@LINE+3]]:19 = (#0 - #11) // CHECK-NEXT: File 0, [[@LINE+2]]:19 -> [[@LINE+2]]:27 = (#0 - #11) // CHECK-NEXT: File 0, [[@LINE+1]]:26 -> [[@LINE+1]]:27 = ((#0 - #11) - #12) @@ -99,14 +102,19 @@ } // CHECK-NEXT: File {{[0-9]+}}, 3:17 -> 3:20 = #0 +// CHECK-NEXT: Branch,File {{[0-9]+}}, 3:17 -> 3:20 = #2, (#0 - #2) // CHECK-NEXT: File {{[0-9]+}}, 4:18 -> 4:22 = (#0 + #3) +// CHECK-NEXT: Branch,File {{[0-9]+}}, 4:18 -> 4:22 = #3, #0 // CHECK-NEXT: File {{[0-9]+}}, 6:22 -> 6:27 = #0 // CHECK-NEXT: File {{[0-9]+}}, 8:16 -> 8:19 = #4 // CHECK-NEXT: File {{[0-9]+}}, 7:18 -> 7:23 = (#0 + #4) +// CHECK-NEXT: Branch,File {{[0-9]+}}, 7:18 -> 7:23 = #4, #0 // CHECK-NEXT: File {{[0-9]+}}, 6:22 -> 6:27 = #0 // CHECK-NEXT: File {{[0-9]+}}, 8:16 -> 8:19 = #5 // CHECK-NEXT: File {{[0-9]+}}, 7:18 -> 7:23 = (#0 + #5) +// CHECK-NEXT: Branch,File {{[0-9]+}}, 7:18 -> 7:23 = #5, #0 // CHECK-NEXT: File {{[0-9]+}}, 4:18 -> 4:22 = (#0 + #6) +// CHECK-NEXT: Branch,File {{[0-9]+}}, 4:18 -> 4:22 = #6, #0 // CHECK-NEXT: File {{[0-9]+}}, 5:20 -> 5:23 = #0 // CHECK-NEXT: File {{[0-9]+}}, 9:25 -> 9:40 = #0 // CHECK-NEXT: File {{[0-9]+}}, 12:16 -> 12:42 = #0 @@ -116,7 +124,9 @@ // CHECK-NEXT: Expansion,File {{[0-9]+}}, 13:16 -> 13:38 = (#0 - #8) // CHECK-NEXT: File {{[0-9]+}}, 13:38 -> 13:42 = (#0 - #8) // CHECK-NEXT: File {{[0-9]+}}, 3:17 -> 3:20 = (#0 - #9) +// CHECK-NEXT: Branch,File {{[0-9]+}}, 3:17 -> 3:20 = #10, ((#0 - #9) - #10) // CHECK-NEXT: File {{[0-9]+}}, 3:17 -> 3:20 = (#0 - #11) +// CHECK-NEXT: Branch,File {{[0-9]+}}, 3:17 -> 3:20 = #12, ((#0 - #11) - #12) // CHECK-NEXT: File {{[0-9]+}}, 11:32 -> 11:36 = #8 // CHECK-NEXT: File {{[0-9]+}}, 11:32 -> 11:36 = (#0 - #8) diff --git a/clang/test/CoverageMapping/macros.c b/clang/test/CoverageMapping/macros.c --- a/clang/test/CoverageMapping/macros.c +++ b/clang/test/CoverageMapping/macros.c @@ -38,29 +38,32 @@ // CHECK-NEXT: File 2, 4:17 -> 4:22 = #0 // CHECK-NEXT: func4 -void func4() { // CHECK-NEXT: File 0, [[@LINE]]:14 -> [[@LINE+6]]:2 = #0 +void func4() { // CHECK-NEXT: File 0, [[@LINE]]:14 -> [[@LINE+7]]:2 = #0 int i = 0; while (i++ < 10) // CHECK-NEXT: File 0, [[@LINE]]:10 -> [[@LINE]]:18 = (#0 + #1) - if (i < 5) // CHECK: File 0, [[@LINE]]:5 -> [[@LINE+2]]:14 = #1 + if (i < 5) // CHECK: File 0, [[@LINE]]:5 -> [[@LINE+3]]:14 = #1 // CHECK-NEXT: File 0, [[@LINE-1]]:9 -> [[@LINE-1]]:14 = #1 + // CHECK-NEXT: Branch,File 0, [[@LINE-2]]:9 -> [[@LINE-2]]:14 = #2, (#1 - #2) MACRO_2; // CHECK-NEXT: Expansion,File 0, [[@LINE]]:7 -> [[@LINE]]:14 = #2 } // CHECK-NEXT: File 1, 4:17 -> 4:22 = #2 // CHECK-NOT: File 1 // CHECK-NEXT: func5 -void func5() { // CHECK-NEXT: File 0, [[@LINE]]:14 -> [[@LINE+4]]:2 = #0 +void func5() { // CHECK-NEXT: File 0, [[@LINE]]:14 -> [[@LINE+5]]:2 = #0 int i = 0; if (i > 5) // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:12 = #0 + // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:12 = #1, (#0 - #1) MACRO_3; // CHECK-NEXT: Expansion,File 0, [[@LINE]]:5 -> [[@LINE]]:12 = #1 } // CHECK-NEXT: Expansion,File 1, 6:17 -> 6:24 = #1 // CHECK-NEXT: File 2, 4:17 -> 4:22 = #1 // CHECK-NEXT: func6 -void func6(unsigned count) { // CHECK-NEXT: File 0, [[@LINE]]:28 -> [[@LINE+4]]:2 = #0 -begin: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE+3]]:2 = #1 +void func6(unsigned count) { // CHECK-NEXT: File 0, [[@LINE]]:28 -> [[@LINE+5]]:2 = #0 +begin: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE+4]]:2 = #1 if (count--) // CHECK-NEXT: File 0, [[@LINE]]:9 -> [[@LINE]]:16 = #1 + // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:9 -> [[@LINE-1]]:16 = #2, (#1 - #2) GOTO begin; // CHECK-NEXT: File 0, [[@LINE]]:9 -> [[@LINE]]:19 = #2 } // CHECK-NEXT: Expansion,File 0, [[@LINE-2]]:9 -> [[@LINE-2]]:13 = #2 diff --git a/clang/test/CoverageMapping/macroscopes.cpp b/clang/test/CoverageMapping/macroscopes.cpp --- a/clang/test/CoverageMapping/macroscopes.cpp +++ b/clang/test/CoverageMapping/macroscopes.cpp @@ -85,6 +85,7 @@ // CHECK-NEXT: File 1, 3:24 -> 3:53 = #0 // CHECK-NEXT: File 1, 3:40 -> 3:45 = (#0 + #1) +// CHECK-NEXT: Branch,File 1, 3:40 -> 3:45 = #1, #0 // CHECK-NEXT: File 1, 3:47 -> 3:50 = #1 // CHECK-NEXT: File 1, 3:52 -> 3:53 = #1 // CHECK-NEXT: File 2, 10:3 -> 20:4 = #1 @@ -97,6 +98,7 @@ // CHECK-NEXT: File 3, 6:3 -> 7:4 = #1 // CHECK-NEXT: File 4, 3:24 -> 3:53 = #0 // CHECK-NEXT: File 4, 3:40 -> 3:45 = (#0 + #4) +// CHECK-NEXT: Branch,File 4, 3:40 -> 3:45 = #4, #0 // CHECK-NEXT: File 4, 3:47 -> 3:50 = #4 // CHECK-NEXT: File 4, 3:52 -> 3:53 = #4 // CHECK-NEXT: File 5, 10:3 -> 20:4 = #4 @@ -109,20 +111,25 @@ // CHECK-NEXT: File 6, 6:3 -> 7:4 = #4 // CHECK-NEXT: File 7, 3:24 -> 3:53 = #0 // CHECK-NEXT: File 7, 3:40 -> 3:45 = (#0 + #7) +// CHECK-NEXT: Branch,File 7, 3:40 -> 3:45 = #7, #0 // CHECK-NEXT: File 7, 3:47 -> 3:50 = #7 // CHECK-NEXT: File 7, 3:52 -> 3:53 = #7 // CHECK-NEXT: File 8, 6:3 -> 7:4 = #7 // CHECK-NEXT: File 9, 22:24 -> 22:37 = #0 // CHECK-NEXT: File 9, 22:31 -> 22:36 = (#0 + #8) +// CHECK-NEXT: Branch,File 9, 22:31 -> 22:36 = #8, #0 // CHECK-NEXT: File 10, 23:21 -> 23:24 = #8 // CHECK-NEXT: File 11, 22:24 -> 22:37 = #0 // CHECK-NEXT: File 11, 22:31 -> 22:36 = (#0 + #9) +// CHECK-NEXT: Branch,File 11, 22:31 -> 22:36 = #9, #0 // CHECK-NEXT: File 12, 23:21 -> 23:24 = #9 // CHECK-NEXT: File 13, 6:3 -> 7:4 = #9 // CHECK-NEXT: File 14, 26:3 -> 28:4 = #0 // CHECK-NEXT: File 14, 27:19 -> 27:24 = (#0 + #10) +// CHECK-NEXT: Branch,File 14, 27:19 -> 27:24 = #10, #0 // CHECK-NEXT: File 14, 27:26 -> 27:29 = #10 // CHECK-NEXT: File 14, 27:31 -> 28:4 = #10 // CHECK-NEXT: File 15, 31:3 -> 34:4 = #0 // CHECK-NEXT: File 15, 32:10 -> 32:15 = (#0 + #11) +// CHECK-NEXT: Branch,File 15, 32:10 -> 32:15 = #11, #0 // CHECK-NEXT: File 15, 32:17 -> 34:4 = #11 diff --git a/clang/test/CoverageMapping/moremacros.c b/clang/test/CoverageMapping/moremacros.c --- a/clang/test/CoverageMapping/moremacros.c +++ b/clang/test/CoverageMapping/moremacros.c @@ -9,16 +9,18 @@ // CHECK-NEXT: File 0, [[@LINE+1]]:7 -> [[@LINE+1]]:12 = #0 if (!argc) {} // CHECK: File 0, [[@LINE]]:14 -> [[@LINE]]:16 = #1 - // CHECK-NEXT: File 0, [[@LINE+3]]:7 -> [[@LINE+3]]:12 = #0 + // CHECK-NEXT: File 0, [[@LINE+4]]:7 -> [[@LINE+4]]:12 = #0 + // CHECK-NEXT: Branch,File 0, [[@LINE+3]]:7 -> [[@LINE+3]]:12 = #2, (#0 - #2) // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:14 -> [[@LINE+2]]:19 = #2 // CHECK-NEXT: File 0, [[@LINE+1]]:19 -> [[@LINE+4]]:8 = #2 if (!argc) LBRAC return 0; // CHECK-NEXT: Expansion,File 0, [[@LINE+1]]:3 -> [[@LINE+1]]:8 = #2 - RBRAC // CHECK-NEXT: [[@LINE]]:8 -> [[@LINE+6]]:3 = (#0 - #2) + RBRAC // CHECK-NEXT: [[@LINE]]:8 -> [[@LINE+7]]:3 = (#0 - #2) - // CHECK-NEXT: File 0, [[@LINE+4]]:3 -> [[@LINE+15]]:2 = (#0 - #2) - // CHECK-NEXT: File 0, [[@LINE+3]]:7 -> [[@LINE+3]]:12 = (#0 - #2) + // CHECK-NEXT: File 0, [[@LINE+5]]:3 -> [[@LINE+16]]:2 = (#0 - #2) + // CHECK-NEXT: File 0, [[@LINE+4]]:7 -> [[@LINE+4]]:12 = (#0 - #2) + // CHECK-NEXT: Branch,File 0, [[@LINE+3]]:7 -> [[@LINE+3]]:12 = #3, ((#0 - #2) - #3) // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:14 -> [[@LINE+2]]:19 = #3 // CHECK-NEXT: File 0, [[@LINE+1]]:19 -> [[@LINE+3]]:4 = #3 if (!argc) LBRAC diff --git a/clang/test/CoverageMapping/return.c b/clang/test/CoverageMapping/return.c --- a/clang/test/CoverageMapping/return.c +++ b/clang/test/CoverageMapping/return.c @@ -8,7 +8,8 @@ // CHECK-NEXT: func2 void func2() { // CHECK-NEXT: File 0, [[@LINE]]:14 -> {{[0-9]+}}:2 = #0 - // CHECK-NEXT: File 0, [[@LINE+2]]:18 -> [[@LINE+2]]:24 = ((#0 + #1) - #2) + // CHECK-NEXT: File 0, [[@LINE+3]]:18 -> [[@LINE+3]]:24 = ((#0 + #1) - #2) + // CHECK-NEXT: Branch,File 0, [[@LINE+2]]:18 -> [[@LINE+2]]:24 = #1, (#0 - #2) // CHECK-NEXT: File 0, [[@LINE+1]]:26 -> [[@LINE+1]]:29 = (#1 - #2) for(int i = 0; i < 10; ++i) { // CHECK: File 0, [[@LINE]]:31 -> {{[0-9]+}}:4 = #1 // CHECK-NEXT: File 0, [[@LINE+1]]:8 -> [[@LINE+1]]:13 = #1 diff --git a/clang/test/CoverageMapping/switch.cpp b/clang/test/CoverageMapping/switch.cpp --- a/clang/test/CoverageMapping/switch.cpp +++ b/clang/test/CoverageMapping/switch.cpp @@ -1,37 +1,39 @@ // RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -std=c++1z -triple %itanium_abi_triple -main-file-name switch.cpp %s | FileCheck %s // CHECK: foo -void foo(int i) { // CHECK-NEXT: File 0, [[@LINE]]:17 -> [[@LINE+8]]:2 = #0 - switch(i) { // CHECK-NEXT: Gap,File 0, [[@LINE]]:13 -> [[@LINE+4]]:10 = 0 +void foo(int i) { // CHECK-NEXT: File 0, [[@LINE]]:17 -> [[@LINE+9]]:2 = #0 + switch(i) { // CHECK-NEXT: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = ((#0 - #2) - #3), (#2 + #3) + // CHECK-NEXT: Gap,File 0, [[@LINE-1]]:13 -> [[@LINE+4]]:10 = 0 case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:11 = #2 - return; + return; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:9 = #2, (#0 - #2) case 2: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #3 - break; // CHECK-NEXT: Gap,File 0, [[@LINE]]:10 -> [[@LINE+2]]:3 = #1 - } + break; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:9 = #3, (#0 - #3) + } // CHECK-NEXT: Gap,File 0, [[@LINE-1]]:10 -> [[@LINE+1]]:3 = #1 int x = 0; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:2 = #1 } int nop() { return 0; } // CHECK: bar -void bar(int i) { // CHECK-NEXT: File 0, [[@LINE]]:17 -> [[@LINE+20]]:2 = #0 - switch (i) +void bar(int i) { // CHECK-NEXT: File 0, [[@LINE]]:17 -> [[@LINE+21]]:2 = #0 + switch (i) // CHECK-NEXT: Branch,File 0, [[@LINE]]:11 -> [[@LINE]]:12 = #0, 0 ; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:6 = 0 - switch (i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+16]]:2 = #1 - } - - switch (i) // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+13]]:2 = #2 - nop(); // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:10 = 0 - - switch (i) // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+10]]:2 = #3 - case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #5 - nop(); - - switch (i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+6]]:2 = #4 + switch (i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+17]]:2 = #1 + } // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:11 -> [[@LINE-1]]:12 = #1, 0 + + switch (i) // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+14]]:2 = #2 + nop(); // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:11 -> [[@LINE-1]]:12 = #2, 0 + // CHECK-NEXT: File 0, [[@LINE-1]]:5 -> [[@LINE-1]]:10 = 0 + switch (i) // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+11]]:2 = #3 + case 1: // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:11 -> [[@LINE-1]]:12 = (#3 - #5), #5 + // CHECK-NEXT: File 0, [[@LINE-1]]:3 -> [[@LINE+1]]:10 = #5 + nop(); // CHECK-NEXT: Branch,File 0, [[@LINE-2]]:3 -> [[@LINE-2]]:9 = #5, (#3 - #5) + // CHECK-NEXT: File 0, [[@LINE+1]]:3 -> [[@LINE+7]]:2 = #4 + switch (i) { // CHECK-NEXT: Branch,File 0, [[@LINE]]:11 -> [[@LINE]]:12 = (#4 - #7), #7 nop(); // CHECK-NEXT: Gap,File 0, [[@LINE-1]]:14 -> [[@LINE+2]]:10 = 0 case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #7 - nop(); + nop(); // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:9 = #7, (#4 - #7) } nop(); // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:2 = #6 } @@ -40,40 +42,43 @@ void baz() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+5]]:2 = #0 switch (int i = true ? nop() // CHECK: [[@LINE]]:26 -> [[@LINE]]:31 = #2 : nop(); // CHECK-NEXT: [[@LINE]]:26 -> [[@LINE]]:31 = (#0 - #2) - i) {} + i) {} // CHECK-NEXT: Branch,File 0, [[@LINE]]:11 -> [[@LINE]]:12 = #0, 0 nop(); // CHECK-NEXT: [[@LINE]]:3 -> [[@LINE+1]]:2 = #1 } // CHECK-NEXT: main -int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+35]]:2 = #0 +int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+38]]:2 = #0 int i = 0; switch(i) { // CHECK-NEXT: Gap,File 0, [[@LINE]]:13 -> [[@LINE+8]]:10 = 0 case 0: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:10 = #2 - i = 1; + i = 1; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:9 = #2, (#0 - #2) break; case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:10 = #3 - i = 2; + i = 2; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:9 = #3, (#0 - #3) break; default: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #4 - break; // CHECK-NEXT: File 0, [[@LINE]]:10 -> [[@LINE+2]]:3 = #1 - } - switch(i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+23]]:2 = #1 + break; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:10 = #4, (#0 - #4) + } // CHECK-NEXT: File 0, [[@LINE-1]]:10 -> [[@LINE+1]]:3 = #1 + switch(i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+26]]:2 = #1 case 0: // CHECK-NEXT: Gap,File 0, [[@LINE-1]]:13 -> [[@LINE+6]]:10 = 0 i = 1; // CHECK-NEXT: File 0, [[@LINE-1]]:3 -> [[@LINE+1]]:10 = #6 - break; + break; // CHECK-NEXT: Branch,File 0, [[@LINE-2]]:3 -> [[@LINE-2]]:9 = #6, (#1 - #6) case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+3]]:10 = #7 - i = 2; + i = 2; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:9 = #7, (#1 - #7) default: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = (#7 + #8) - break; // CHECK-NEXT: File 0, [[@LINE]]:10 -> [[@LINE+3]]:3 = #5 - } - // CHECK-NEXT: File 0, [[@LINE+1]]:3 -> [[@LINE+14]]:2 = #5 - switch(i) { // CHECK-NEXT: Gap,File 0, [[@LINE]]:13 -> [[@LINE+6]]:11 = 0 - case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+5]]:11 = #10 - case 2: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+4]]:11 = (#10 + #11) - i = 11; - case 3: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:11 = ((#10 + #11) + #12) + break; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:10 = #8, (#1 - #8) + } // CHECK-NEXT: File 0, [[@LINE-1]]:10 -> [[@LINE+2]]:3 = #5 + // CHECK-NEXT: File 0, [[@LINE+1]]:3 -> [[@LINE+17]]:2 = #5 + switch(i) { // CHECK-NEXT: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = ((((#5 - #10) - #11) - #12) - #13), (((#10 + #11) + #12) + #13) + // CHECK-NEXT: Gap,File 0, [[@LINE-1]]:13 -> [[@LINE+8]]:11 = 0 + case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+7]]:11 = #10 + // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:9 = #10, (#5 - #10) + case 2: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+5]]:11 = (#10 + #11) + i = 11; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:9 = #11, (#5 - #11) + case 3: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+3]]:11 = ((#10 + #11) + #12) + // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:9 = #12, (#5 - #12) case 4: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:11 = (((#10 + #11) + #12) + #13) - i = 99; + i = 99; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:9 = #13, (#5 - #13) } foo(1); // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+3]]:11 = #9 @@ -87,26 +92,28 @@ switch (i) { // CHECK-NEXT: Gap,File 0, [[@LINE]]:14 -> [[@LINE+6]]:13 = 0 case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:13 = #2 - return 0; + return 0; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:9 = #2, (#0 - #2) default: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:13 = #3 - return 1; + return 1; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:10 = #3, (#0 - #3) } } // A region for counter #1 is missing due to the missing return. // FIXME: End location for "case 1" shouldn't point at the end of the switch. // CHECK: fallthrough -int fallthrough(int i) { // CHECK-NEXT: File 0, [[@LINE]]:24 -> [[@LINE+12]]:2 = #0 - switch(i) { // CHECK-NEXT: Gap,File 0, [[@LINE]]:13 -> [[@LINE+9]]:10 = 0 - case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+8]]:10 = #2 - i = 23; +int fallthrough(int i) { // CHECK-NEXT: File 0, [[@LINE]]:24 -> [[@LINE+14]]:2 = #0 + // CHECK-NEXT: Branch,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:11 = ((((#0 - #2) - #3) - #4) - #5), (((#2 + #3) + #4) + #5) + switch(i) { // CHECK-NEXT: Gap,File 0, [[@LINE]]:13 -> [[@LINE+10]]:10 = 0 + case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+9]]:10 = #2 + i = 23; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:9 = #2, (#0 - #2) case 2: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:10 = (#2 + #3) - i = 11; + i = 11; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:9 = #3, (#0 - #3) break; - case 3: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+3]]:10 = #4 + case 3: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+4]]:10 = #4 + // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:9 = #4, (#0 - #4) case 4: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:10 = (#4 + #5) - i = 99; + i = 99; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:9 = #5, (#0 - #5) break; } } @@ -116,10 +123,10 @@ int noret(int x) { // CHECK-NEXT: File 0, [[@LINE]]:18 -> [[@LINE+9]]:2 switch (x) { // CHECK-NEXT: Gap,File 0, [[@LINE]]:14 -> [[@LINE+6]]:14 = 0 default: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:12 - abort(); + abort(); // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:10 = #2, (#0 - #2) case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:13 - return 5; + return 5; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:9 = #3, (#0 - #3) case 2: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:14 - return 10; + return 10; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:9 = #4, (#0 - #4) } } diff --git a/clang/test/CoverageMapping/switchmacro.c b/clang/test/CoverageMapping/switchmacro.c --- a/clang/test/CoverageMapping/switchmacro.c +++ b/clang/test/CoverageMapping/switchmacro.c @@ -6,13 +6,16 @@ int foo(int i) { // CHECK-NEXT: File 0, [[@LINE]]:16 -> {{[0-9]+}}:2 = #0 switch (i) { // CHECK-NEXT: Gap,File 0, [[@LINE]]:14 -> {{[0-9]+}}:11 = 0 default: // CHECK-NEXT: File 0, [[@LINE]]:3 -> {{[0-9]+}}:11 = #2 + // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:10 = #2, (#0 - #2) if (i == 1) // CHECK-NEXT: File 0, [[@LINE]]:9 -> [[@LINE]]:15 = #2 + // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:9 -> [[@LINE-1]]:15 = #3, (#2 - #3) return 0; // CHECK: File 0, [[@LINE]]:7 -> [[@LINE]]:15 = #3 // CHECK-NEXT: File 0, [[@LINE-1]]:15 -> [[@LINE+3]]:5 = (#2 - #3) // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:5 -> [[@LINE+2]]:8 = (#2 - #3) (Expanded file = 1) // CHECK-NEXT: File 0, [[@LINE+1]]:8 -> {{[0-9]+}}:11 = (#2 - #3) FOO(1); - case 0: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:13 = ((#2 + #4) - #3) + case 0: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:13 = ((#2 + #4) - #3) + // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:9 = #4, (#0 - #4) return 2; // CHECK-NEXT: Gap,File 0, [[@LINE]]:13 -> [[@LINE+6]]:3 = #5 // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:3 -> [[@LINE+2]]:6 = 0 diff --git a/clang/test/CoverageMapping/test.c b/clang/test/CoverageMapping/test.c --- a/clang/test/CoverageMapping/test.c +++ b/clang/test/CoverageMapping/test.c @@ -4,8 +4,9 @@ static void static_func(); // CHECK: main -int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+7]]:2 = #0 - // CHECK-NEXT: File 0, [[@LINE+1]]:18 -> [[@LINE+1]]:24 = (#0 + #1) +int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+8]]:2 = #0 + // CHECK-NEXT: File 0, [[@LINE+2]]:18 -> [[@LINE+2]]:24 = (#0 + #1) + // CHECK-NEXT: Branch,File 0, [[@LINE+1]]:18 -> [[@LINE+1]]:24 = #1, #0 for(int i = 0; i < 10; ++i) { // CHECK-NEXT: File 0, [[@LINE]]:26 -> [[@LINE]]:29 = #1 bar(); // CHECK: File 0, [[@LINE-1]]:31 -> [[@LINE+1]]:4 = #1 } diff --git a/clang/test/CoverageMapping/unreachable-macro.c b/clang/test/CoverageMapping/unreachable-macro.c --- a/clang/test/CoverageMapping/unreachable-macro.c +++ b/clang/test/CoverageMapping/unreachable-macro.c @@ -12,4 +12,5 @@ } // CHECK-NEXT: File 1, 3:15 -> 3:27 = 0 // CHECK-NEXT: File 1, 3:22 -> 3:23 = #1 +// CHECK-NEXT: Branch,File 1, 3:22 -> 3:23 = 0, 0 // CHECK-NEXT: File 1, 3:25 -> 3:27 = #1 diff --git a/clang/test/CoverageMapping/while.c b/clang/test/CoverageMapping/while.c --- a/clang/test/CoverageMapping/while.c +++ b/clang/test/CoverageMapping/while.c @@ -1,12 +1,14 @@ // RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name loops.cpp %s | FileCheck %s // CHECK: main -int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+8]]:2 = #0 +int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+10]]:2 = #0 int j = 0; // CHECK-NEXT: File 0, [[@LINE+1]]:9 -> [[@LINE+1]]:14 = (#0 + #1) - while(j < 5) ++j; // CHECK-NEXT: File 0, [[@LINE]]:15 -> [[@LINE]]:16 = #1 - j = 0; // CHECK-NEXT: File 0, [[@LINE-1]]:16 -> [[@LINE-1]]:19 = #1 + while(j < 5) ++j; // CHECK-NEXT: Branch,File 0, [[@LINE]]:9 -> [[@LINE]]:14 = #1, #0 + // CHECK-NEXT: File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:16 = #1 + j = 0; // CHECK-NEXT: File 0, [[@LINE-2]]:16 -> [[@LINE-2]]:19 = #1 while // CHECK-NEXT: File 0, [[@LINE+1]]:5 -> [[@LINE+1]]:10 = (#0 + #2) - (j < 5) // CHECK-NEXT: Gap,File 0, [[@LINE]]:11 -> [[@LINE+1]]:6 = #2 - ++j; // CHECK-NEXT: File 0, [[@LINE]]:6 -> [[@LINE]]:9 = #2 + (j < 5) // CHECK-NEXT: Branch,File 0, [[@LINE]]:5 -> [[@LINE]]:10 = #2, #0 + ++j; // CHECK-NEXT: Gap,File 0, [[@LINE-1]]:11 -> [[@LINE]]:6 = #2 + // CHECK-NEXT: File 0, [[@LINE-1]]:6 -> [[@LINE-1]]:9 = #2 return 0; } diff --git a/clang/test/Profile/Inputs/c-general.proftext b/clang/test/Profile/Inputs/c-general.proftext --- a/clang/test/Profile/Inputs/c-general.proftext +++ b/clang/test/Profile/Inputs/c-general.proftext @@ -8,7 +8,7 @@ conditionals 4904767535850050386 -11 +13 1 100 50 @@ -19,7 +19,9 @@ 99 100 99 +99 100 +1 early_exits 2880354649761471549 @@ -106,28 +108,38 @@ boolean_operators 1245693242827665 -8 +14 1 100 34 +1 +66 66 17 +1 34 +17 +33 33 50 +33 boolop_loops 12402604614320574815 -9 +13 1 50 51 50 +50 26 +1 50 51 50 +50 26 +1 conditional_operator 54992 diff --git a/clang/test/Profile/branch-logical-mixed.cpp b/clang/test/Profile/branch-logical-mixed.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Profile/branch-logical-mixed.cpp @@ -0,0 +1,66 @@ +// Test to ensure instrumentation of logical operator RHS True/False counters +// are being instrumented for branch coverage + +// RUN: %clang_cc1 -main-file-name branch-logical-mixed.cpp %s -o - -emit-llvm -fprofile-instrument=clang | FileCheck -allow-deprecated-dag-overlap %s + + +// CHECK: @[[FUNC:__profc__Z4funcv]] = private global [61 x i64] zeroinitializer + + +// CHECK-LABEL: @_Z4funcv() +bool func() { + bool bt0 = true; + bool bt1 = true; + bool bt2 = true; + bool bt3 = true; + bool bt4 = true; + bool bt5 = true; + bool bf0 = false; + bool bf1 = false; + bool bf2 = false; + bool bf3 = false; + bool bf4 = false; + bool bf5 = false; + + bool a = bt0 && + bf0 && // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 10 + bt1 && // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 8 + bf1 && // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 6 + bt2 && // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 4 + bf2; // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 2 + + bool b = bt0 || + bf0 || // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 20 + bt1 || // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 18 + bf1 || // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 16 + bt2 || // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 14 + bf2; // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 12 + + bool c = (bt0 && + bf0) || // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 27 + (bt1 && + bf1) || // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 29 + (bt2 && + bf2) || // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 31 + (bt3 && + bf3) || // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 33 + (bt4 && + bf4) || // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 35 + (bf5 && + bf5); // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 37 + + bool d = (bt0 || + bf0) && // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 44 + (bt1 || + bf1) && // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 46 + (bt2 || + bf2) && // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 48 + (bt3 || + bf3) && // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 50 + (bt4 || + bf4) && // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 52 + (bt5 || + bf5); // CHECK: store {{.*}} @[[FUNC]], i64 0, i64 54 + + return a && b && c && d; +} diff --git a/clang/test/Profile/branch-profdup.cpp b/clang/test/Profile/branch-profdup.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Profile/branch-profdup.cpp @@ -0,0 +1,76 @@ +// Test to ensure RHS condition of logical operators isn't evaluated more than +// one time when instrumenting RHS counter blocks for branch coverage. + +// RUN: %clang_cc1 -main-file-name branch-profdup.cpp %s -o - -emit-llvm -fprofile-instrument=clang | FileCheck -allow-deprecated-dag-overlap %s + +// CHECK-LABEL: define {{.*}}@_Z5test1b +// CHECK-COUNT-1: = call {{.*}}@_Z5fval1v() +// CHECK-NOT: = call {{.*}}@_Z5fval1v() +extern bool fval1(); +bool test1(bool a) { + return (a && fval1()); +} + +// CHECK-LABEL: define {{.*}}@_Z5test2b +// CHECK-COUNT-1: call {{.*}}_Z5fval2v() +// CHECK-NOT: call {{.*}}_Z5fval2v() +extern bool fval2(); +bool test2(bool a) { + return (a || fval2()); +} + +// CHECK-LABEL: define {{.*}}@_Z5test3v +// CHECK-COUNT-1: call {{.*}}_Z5fval3v() +// CHECK-NOT: call {{.*}}_Z5fval3v() +extern bool fval3(); +bool test3() { + return (1 && fval3()); +} + +// CHECK-LABEL: define {{.*}}@_Z5test4v +// CHECK-COUNT-1: call {{.*}}_Z5fval4v() +// CHECK-NOT: call {{.*}}_Z5fval4v() +extern bool fval4(); +bool test4() { + return (0 || fval4()); +} + +// CHECK-LABEL: define {{.*}}@_Z5test5b +// CHECK-COUNT-1: call {{.*}}_Z5fval5v() +// CHECK-NOT: call {{.*}}_Z5fval5v() +extern bool fval5(); +bool test5(bool a) { + if (a && fval5()) + return true; + return false; +} + +// CHECK-LABEL: define {{.*}}@_Z5test6b +// CHECK-COUNT-1: call {{.*}}_Z5fval6v() +// CHECK-NOT: call {{.*}}_Z5fval6v() +extern bool fval6(); +bool test6(bool a) { + if (a || fval6()) + return true; + return false; +} + +// CHECK-LABEL: define {{.*}}@_Z5test7v +// CHECK-COUNT-1: call {{.*}}_Z5fval7v() +// CHECK-NOT: call {{.*}}_Z5fval7v() +extern bool fval7(); +bool test7() { + if (1 && fval7()) + return true; + return false; +} + +// CHECK-LABEL: define {{.*}}@_Z5test8v +// CHECK-COUNT-1: call {{.*}}_Z5fval8v() +// CHECK-NOT: call {{.*}}_Z5fval8v() +extern bool fval8(); +bool test8() { + if (0 || fval8()) + return true; + return false; +} diff --git a/clang/test/Profile/c-general.c b/clang/test/Profile/c-general.c --- a/clang/test/Profile/c-general.c +++ b/clang/test/Profile/c-general.c @@ -10,13 +10,13 @@ // RUN: %clang_cc1 -triple x86_64-apple-macosx10.9 -main-file-name c-general.c %s -o - -emit-llvm -fprofile-instrument-use-path=%S/Inputs/c-general.profdata.v1 | FileCheck -allow-deprecated-dag-overlap -check-prefix=PGOUSE %s // PGOGEN: @[[SLC:__profc_simple_loops]] = private global [4 x i64] zeroinitializer -// PGOGEN: @[[IFC:__profc_conditionals]] = private global [11 x i64] zeroinitializer +// PGOGEN: @[[IFC:__profc_conditionals]] = private global [13 x i64] zeroinitializer // PGOGEN: @[[EEC:__profc_early_exits]] = private global [9 x i64] zeroinitializer // PGOGEN: @[[JMC:__profc_jumps]] = private global [22 x i64] zeroinitializer // PGOGEN: @[[SWC:__profc_switches]] = private global [19 x i64] zeroinitializer // PGOGEN: @[[BSC:__profc_big_switch]] = private global [17 x i64] zeroinitializer -// PGOGEN: @[[BOC:__profc_boolean_operators]] = private global [8 x i64] zeroinitializer -// PGOGEN: @[[BLC:__profc_boolop_loops]] = private global [9 x i64] zeroinitializer +// PGOGEN: @[[BOC:__profc_boolean_operators]] = private global [14 x i64] zeroinitializer +// PGOGEN: @[[BLC:__profc_boolop_loops]] = private global [13 x i64] zeroinitializer // PGOGEN: @[[COC:__profc_conditional_operator]] = private global [3 x i64] zeroinitializer // PGOGEN: @[[DFC:__profc_do_fallthrough]] = private global [4 x i64] zeroinitializer // PGOGEN: @[[MAC:__profc_main]] = private global [1 x i64] zeroinitializer @@ -69,11 +69,13 @@ } // PGOGEN: store {{.*}} @[[IFC]], i64 0, i64 8 + // PGOGEN: store {{.*}} @[[IFC]], i64 0, i64 9 // PGOGEN: store {{.*}} @[[IFC]], i64 0, i64 7 // PGOUSE: br {{.*}} !prof ![[IF7:[0-9]+]] if (1 && i) {} + // PGOGEN: store {{.*}} @[[IFC]], i64 0, i64 11 + // PGOGEN: store {{.*}} @[[IFC]], i64 0, i64 12 // PGOGEN: store {{.*}} @[[IFC]], i64 0, i64 10 - // PGOGEN: store {{.*}} @[[IFC]], i64 0, i64 9 // PGOUSE: br {{.*}} !prof ![[IF8:[0-9]+]] if (0 || i) {} } @@ -360,21 +362,27 @@ // PGOUSE: br {{.*}} !prof ![[BO1:[0-9]+]] for (int i = 0; i < 100; ++i) { // PGOGEN: store {{.*}} @[[BOC]], i64 0, i64 2 + // PGOGEN: store {{.*}} @[[BOC]], i64 0, i64 3 // PGOUSE: br {{.*}} !prof ![[BO2:[0-9]+]] v = i % 3 || i; - // PGOGEN: store {{.*}} @[[BOC]], i64 0, i64 3 + // PGOGEN: store {{.*}} @[[BOC]], i64 0, i64 4 + // PGOGEN: store {{.*}} @[[BOC]], i64 0, i64 5 // PGOUSE: br {{.*}} !prof ![[BO3:[0-9]+]] v = i % 3 && i; - // PGOGEN: store {{.*}} @[[BOC]], i64 0, i64 5 - // PGOGEN: store {{.*}} @[[BOC]], i64 0, i64 4 + // PGOGEN: store {{.*}} @[[BOC]], i64 0, i64 8 + // PGOGEN: store {{.*}} @[[BOC]], i64 0, i64 9 + // PGOGEN: store {{.*}} @[[BOC]], i64 0, i64 6 + // PGOGEN: store {{.*}} @[[BOC]], i64 0, i64 7 // PGOUSE: br {{.*}} !prof ![[BO4:[0-9]+]] // PGOUSE: br {{.*}} !prof ![[BO5:[0-9]+]] v = i % 3 || i % 2 || i; - // PGOGEN: store {{.*}} @[[BOC]], i64 0, i64 7 - // PGOGEN: store {{.*}} @[[BOC]], i64 0, i64 6 + // PGOGEN: store {{.*}} @[[BOC]], i64 0, i64 12 + // PGOGEN: store {{.*}} @[[BOC]], i64 0, i64 13 + // PGOGEN: store {{.*}} @[[BOC]], i64 0, i64 10 + // PGOGEN: store {{.*}} @[[BOC]], i64 0, i64 11 // PGOUSE: br {{.*}} !prof ![[BO6:[0-9]+]] // PGOUSE: br {{.*}} !prof ![[BO7:[0-9]+]] v = i % 2 && i % 3 && i; @@ -391,27 +399,31 @@ int i = 100; // PGOGEN: store {{.*}} @[[BLC]], i64 0, i64 2 + // PGOGEN: store {{.*}} @[[BLC]], i64 0, i64 3 // PGOGEN: store {{.*}} @[[BLC]], i64 0, i64 1 // PGOUSE: br {{.*}} !prof ![[BL1:[0-9]+]] // PGOUSE: br {{.*}} !prof ![[BL2:[0-9]+]] while (i && i > 50) i--; + // PGOGEN: store {{.*}} @[[BLC]], i64 0, i64 5 + // PGOGEN: store {{.*}} @[[BLC]], i64 0, i64 6 // PGOGEN: store {{.*}} @[[BLC]], i64 0, i64 4 - // PGOGEN: store {{.*}} @[[BLC]], i64 0, i64 3 // PGOUSE: br {{.*}} !prof ![[BL3:[0-9]+]] // PGOUSE: br {{.*}} !prof ![[BL4:[0-9]+]] while ((i % 2) || (i > 0)) i--; - // PGOGEN: store {{.*}} @[[BLC]], i64 0, i64 6 - // PGOGEN: store {{.*}} @[[BLC]], i64 0, i64 5 + // PGOGEN: store {{.*}} @[[BLC]], i64 0, i64 8 + // PGOGEN: store {{.*}} @[[BLC]], i64 0, i64 9 + // PGOGEN: store {{.*}} @[[BLC]], i64 0, i64 7 // PGOUSE: br {{.*}} !prof ![[BL5:[0-9]+]] // PGOUSE: br {{.*}} !prof ![[BL6:[0-9]+]] for (i = 100; i && i > 50; --i); - // PGOGEN: store {{.*}} @[[BLC]], i64 0, i64 8 - // PGOGEN: store {{.*}} @[[BLC]], i64 0, i64 7 + // PGOGEN: store {{.*}} @[[BLC]], i64 0, i64 11 + // PGOGEN: store {{.*}} @[[BLC]], i64 0, i64 12 + // PGOGEN: store {{.*}} @[[BLC]], i64 0, i64 10 // PGOUSE: br {{.*}} !prof ![[BL7:[0-9]+]] // PGOUSE: br {{.*}} !prof ![[BL8:[0-9]+]] for (; (i % 2) || (i > 0); --i); diff --git a/clang/test/Profile/cxx-lambda.cpp b/clang/test/Profile/cxx-lambda.cpp --- a/clang/test/Profile/cxx-lambda.cpp +++ b/clang/test/Profile/cxx-lambda.cpp @@ -11,7 +11,7 @@ // PGOGEN: @[[LWC:__profc__Z7lambdasv]] = {{(private|internal)}} global [4 x i64] zeroinitializer // PGOGEN: @[[MAC:__profc_main]] = {{(private|internal)}} global [1 x i64] zeroinitializer -// LMBGEN: @[[LFC:"__profc_cxx_lambda.cpp__ZZ7lambdasvENK3\$_0clEi"]] = {{(private|internal)}} global [3 x i64] zeroinitializer +// LMBGEN: @[[LFC:"__profc_cxx_lambda.cpp__ZZ7lambdasvENK3\$_0clEi"]] = {{(private|internal)}} global [4 x i64] zeroinitializer // PGOGEN-LABEL: define {{.*}}void @_Z7lambdasv() // PGOUSE-LABEL: define {{.*}}void @_Z7lambdasv() diff --git a/compiler-rt/include/profile/InstrProfData.inc b/compiler-rt/include/profile/InstrProfData.inc --- a/compiler-rt/include/profile/InstrProfData.inc +++ b/compiler-rt/include/profile/InstrProfData.inc @@ -647,9 +647,9 @@ /* Raw profile format version (start from 1). */ #define INSTR_PROF_RAW_VERSION 5 /* Indexed profile format version (start from 1). */ -#define INSTR_PROF_INDEX_VERSION 6 +#define INSTR_PROF_INDEX_VERSION 7 /* Coverage mapping format version (start from 0). */ -#define INSTR_PROF_COVMAP_VERSION 3 +#define INSTR_PROF_COVMAP_VERSION 4 /* Profile version is always of type uint64_t. Reserve the upper 8 bits in the * version for other variants of profile. We set the lowest bit of the upper 8 diff --git a/llvm/docs/CommandGuide/llvm-cov.rst b/llvm/docs/CommandGuide/llvm-cov.rst --- a/llvm/docs/CommandGuide/llvm-cov.rst +++ b/llvm/docs/CommandGuide/llvm-cov.rst @@ -202,6 +202,11 @@ OPTIONS ^^^^^^^ +.. option:: -show-branches= + + Show coverage for branch conditions in terms of either count or percentage. + The supported views are: "count", "percent". + .. option:: -show-line-counts Show the execution counts for each line. Defaults to true, unless another @@ -359,6 +364,10 @@ universal binary or to use an architecture that does not match a non-universal binary. +.. option:: -show-branch-summary + + Show statistics for all branch conditions. Defaults to true. + .. option:: -show-functions Show coverage summaries for each function. Defaults to false. @@ -390,9 +399,9 @@ *BIN*,... using the profile data *PROFILE* in either JSON or lcov trace file format. -When exporting JSON, the regions, functions, expansions, and summaries of the -coverage data will be exported. When exporting an lcov trace file, the -line-based coverage and summaries will be exported. +When exporting JSON, the regions, functions, branches, expansions, and +summaries of the coverage data will be exported. When exporting an lcov trace +file, the line-based coverage, branch coverage, and summaries will be exported. The exported data can optionally be filtered to only export the coverage for the files listed in *SOURCES*. diff --git a/llvm/docs/CoverageMappingFormat.rst b/llvm/docs/CoverageMappingFormat.rst --- a/llvm/docs/CoverageMappingFormat.rst +++ b/llvm/docs/CoverageMappingFormat.rst @@ -126,6 +126,25 @@ return MAX(x, 42); // Expansion Region from 3:10 to 3:13 } ` +* Branch regions associate instrumentable branch conditions in the source code + with a `coverage mapping counter`_ to track how many times an individual + condition evaluated to 'true' and another `coverage mapping counter`_ to + track how many times that condition evaluated to false. Instrumentable + branch conditions may comprise larger boolean expressions using boolean + logical operators. The 'true' and 'false' cases reflect unique branch paths + that can be traced back to the source code. + For example: + + :raw-html:`
int func(int x, int y) {
+    if ((x > 1) || (y > 3)) {  // Branch Region from 3:6 to 3:12
+                               // Branch Region from 3:17 to 3:23
+      printf("%d\n", x);              
+    } else {                                
+      printf("\n");                         
+    }
+    return 0;                                 
+  }
+  
` .. _source code range: @@ -198,6 +217,11 @@ Without them, the tool would think that those lines and regions were still executed, as it doesn't possess the frontend's knowledge. +Note that branch regions are created to track branch conditions in the source +code and refer to two coverage mapping counters, one to track the number of +times the branch condition evaluated to "true", and one to track the number of +times the branch condition evaluated to "false". + LLVM IR Representation ====================== @@ -242,7 +266,14 @@ [32 x i8] c"..." ; Encoded data (dissected later) }, section "__llvm_covmap", align 8 -The current version of the format is version 4. There are two differences from version 3: +The current version of the format is version 5. There is one difference from version 4: + +* The notion of branch region has been introduced along with a corresponding + region kind. Branch regions encode two counters, one to track how many + times a "true" branch condition is taken, and one to track how many times a + "false" branch condition is taken. + +There are two differences between versions 4 and 3: * Function records are now named symbols, and are marked *linkonce_odr*. This allows linkers to merge duplicate function records. Merging of duplicate @@ -511,7 +542,8 @@ ``[pseudo-counter]`` -The header encodes the region's counter and the region's kind. +The header encodes the region's counter and the region's kind. A branch region +will encode two counters. The value of the counter's tag distinguishes between the counters and pseudo-counters --- if the tag is zero, than this header contains a @@ -544,6 +576,7 @@ * 0 - This mapping region is a code region with a counter of zero. * 2 - This mapping region is a skipped region. + * 4 - This mapping region is a branch region. .. _source range: diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h --- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h +++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h @@ -90,6 +90,8 @@ /// A Counter is an abstract value that describes how to compute the /// execution count for a region of code using the collected profile count data. struct Counter { + /// The CounterExpression kind (Add or Subtract) is encoded in bit 0 next to + /// the CounterKind. This means CounterKind has to leave bit 0 free. enum CounterKind { Zero, CounterValueReference, Expression }; static const unsigned EncodingTagBits = 2; static const unsigned EncodingTagMask = 0x3; @@ -219,10 +221,20 @@ /// A GapRegion is like a CodeRegion, but its count is only set as the /// line execution count when its the only region in the line. - GapRegion + GapRegion, + + /// A BranchRegion represents leaf-level boolean expressions and is + /// associated with two counters, each representing the number of times the + /// expression evaluates to true or false. + BranchRegion }; + /// Primary Counter that is also used for Branch Regions (TrueCount). Counter Count; + + /// Secondary Counter used for Branch Regions (FalseCount). + Counter FalseCount; + unsigned FileID, ExpandedFileID; unsigned LineStart, ColumnStart, LineEnd, ColumnEnd; RegionKind Kind; @@ -234,6 +246,15 @@ LineStart(LineStart), ColumnStart(ColumnStart), LineEnd(LineEnd), ColumnEnd(ColumnEnd), Kind(Kind) {} + CounterMappingRegion(Counter Count, Counter FalseCount, unsigned FileID, + unsigned ExpandedFileID, unsigned LineStart, + unsigned ColumnStart, unsigned LineEnd, + unsigned ColumnEnd, RegionKind Kind) + : Count(Count), FalseCount(FalseCount), FileID(FileID), + ExpandedFileID(ExpandedFileID), LineStart(LineStart), + ColumnStart(ColumnStart), LineEnd(LineEnd), ColumnEnd(ColumnEnd), + Kind(Kind) {} + static CounterMappingRegion makeRegion(Counter Count, unsigned FileID, unsigned LineStart, unsigned ColumnStart, unsigned LineEnd, unsigned ColumnEnd) { @@ -263,6 +284,14 @@ LineEnd, (1U << 31) | ColumnEnd, GapRegion); } + static CounterMappingRegion + makeBranchRegion(Counter Count, Counter FalseCount, unsigned FileID, + unsigned LineStart, unsigned ColumnStart, unsigned LineEnd, + unsigned ColumnEnd) { + return CounterMappingRegion(Count, FalseCount, FileID, 0, LineStart, + ColumnStart, LineEnd, ColumnEnd, BranchRegion); + } + inline LineColPair startLoc() const { return LineColPair(LineStart, ColumnStart); } @@ -273,9 +302,17 @@ /// Associates a source range with an execution count. struct CountedRegion : public CounterMappingRegion { uint64_t ExecutionCount; + uint64_t FalseExecutionCount; + bool Folded; CountedRegion(const CounterMappingRegion &R, uint64_t ExecutionCount) - : CounterMappingRegion(R), ExecutionCount(ExecutionCount) {} + : CounterMappingRegion(R), ExecutionCount(ExecutionCount), + FalseExecutionCount(0), Folded(false) {} + + CountedRegion(const CounterMappingRegion &R, uint64_t ExecutionCount, + uint64_t FalseExecutionCount) + : CounterMappingRegion(R), ExecutionCount(ExecutionCount), + FalseExecutionCount(FalseExecutionCount), Folded(false) {} }; /// A Counter mapping context is used to connect the counters, expressions @@ -312,6 +349,8 @@ std::vector Filenames; /// Regions in the function along with their counts. std::vector CountedRegions; + /// Branch Regions in the function along with their counts. + std::vector CountedBranchRegions; /// The number of times this function was executed. uint64_t ExecutionCount = 0; @@ -321,10 +360,19 @@ FunctionRecord(FunctionRecord &&FR) = default; FunctionRecord &operator=(FunctionRecord &&) = default; - void pushRegion(CounterMappingRegion Region, uint64_t Count) { + void pushRegion(CounterMappingRegion Region, uint64_t Count, + uint64_t FalseCount) { + if (Region.Kind == CounterMappingRegion::BranchRegion) { + CountedBranchRegions.emplace_back(Region, Count, FalseCount); + // If both counters are hard-coded to zero, then this region represents a + // constant-folded branch. + if (Region.Count.isZero() && Region.FalseCount.isZero()) + CountedBranchRegions.back().Folded = true; + return; + } if (CountedRegions.empty()) ExecutionCount = Count; - CountedRegions.emplace_back(Region, Count); + CountedRegions.emplace_back(Region, Count, FalseCount); } }; @@ -403,7 +451,8 @@ IsRegionEntry(IsRegionEntry), IsGapRegion(false) {} CoverageSegment(unsigned Line, unsigned Col, uint64_t Count, - bool IsRegionEntry, bool IsGapRegion = false) + bool IsRegionEntry, bool IsGapRegion = false, + bool IsBranchRegion = false) : Line(Line), Col(Col), Count(Count), HasCount(true), IsRegionEntry(IsRegionEntry), IsGapRegion(IsGapRegion) {} @@ -483,6 +532,7 @@ std::string Filename; std::vector Segments; std::vector Expansions; + std::vector BranchRegions; public: CoverageData() = default; @@ -506,6 +556,9 @@ /// Expansions that can be further processed. ArrayRef getExpansions() const { return Expansions; } + + /// Branches that can be further processed. + ArrayRef getBranches() const { return BranchRegions; } }; /// The mapping of profile information to coverage data. @@ -941,7 +994,9 @@ Version3 = 2, // Function records are named, uniqued, and moved to a dedicated section. Version4 = 3, - // The current version is Version4. + // Branch regions referring to two counters are added + Version5 = 4, + // The current version is Version5. CurrentVersion = INSTR_PROF_COVMAP_VERSION }; diff --git a/llvm/include/llvm/ProfileData/InstrProf.h b/llvm/include/llvm/ProfileData/InstrProf.h --- a/llvm/include/llvm/ProfileData/InstrProf.h +++ b/llvm/include/llvm/ProfileData/InstrProf.h @@ -983,7 +983,9 @@ // In this version, the frontend PGO stable hash algorithm got fixed and // may produce hashes different from Version5. Version6 = 6, - // The current version is 5. + // An additional counter is added around logical operators. + Version7 = 7, + // The current version is 7. CurrentVersion = INSTR_PROF_INDEX_VERSION }; const uint64_t Version = ProfVersion::CurrentVersion; diff --git a/llvm/include/llvm/ProfileData/InstrProfData.inc b/llvm/include/llvm/ProfileData/InstrProfData.inc --- a/llvm/include/llvm/ProfileData/InstrProfData.inc +++ b/llvm/include/llvm/ProfileData/InstrProfData.inc @@ -647,9 +647,9 @@ /* Raw profile format version (start from 1). */ #define INSTR_PROF_RAW_VERSION 5 /* Indexed profile format version (start from 1). */ -#define INSTR_PROF_INDEX_VERSION 6 +#define INSTR_PROF_INDEX_VERSION 7 /* Coverage mapping format version (start from 0). */ -#define INSTR_PROF_COVMAP_VERSION 3 +#define INSTR_PROF_COVMAP_VERSION 4 /* Profile version is always of type uint64_t. Reserve the upper 8 bits in the * version for other variants of profile. We set the lowest bit of the upper 8 diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp --- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp @@ -249,7 +249,12 @@ consumeError(std::move(E)); return Error::success(); } - Function.pushRegion(Region, *ExecutionCount); + Expected AltExecutionCount = Ctx.evaluate(Region.FalseCount); + if (auto E = AltExecutionCount.takeError()) { + consumeError(std::move(E)); + return Error::success(); + } + Function.pushRegion(Region, *ExecutionCount, *AltExecutionCount); } // Don't create records for (filenames, function) pairs we've already seen. @@ -672,6 +677,10 @@ if (MainFileID && isExpansion(CR, *MainFileID)) FileCoverage.Expansions.emplace_back(CR, Function); } + // Capture branch regions specific to the function (excluding expansions). + for (const auto &CR : Function.CountedBranchRegions) + if (FileIDs.test(CR.FileID) && (CR.FileID == CR.ExpandedFileID)) + FileCoverage.BranchRegions.push_back(CR); } LLVM_DEBUG(dbgs() << "Emitting segments for file: " << Filename << "\n"); @@ -719,6 +728,10 @@ if (isExpansion(CR, *MainFileID)) FunctionCoverage.Expansions.emplace_back(CR, Function); } + // Capture branch regions specific to the function (excluding expansions). + for (const auto &CR : Function.CountedBranchRegions) + if (CR.FileID == *MainFileID) + FunctionCoverage.BranchRegions.push_back(CR); LLVM_DEBUG(dbgs() << "Emitting segments for function: " << Function.Name << "\n"); @@ -738,6 +751,10 @@ if (isExpansion(CR, Expansion.FileID)) ExpansionCoverage.Expansions.emplace_back(CR, Expansion.Function); } + for (const auto &CR : Expansion.Function.CountedBranchRegions) + // Capture branch regions that only pertain to the corresponding expansion. + if (CR.FileID == Expansion.FileID) + ExpansionCoverage.BranchRegions.push_back(CR); LLVM_DEBUG(dbgs() << "Emitting segments for expansion of file " << Expansion.FileID << "\n"); diff --git a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp --- a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp @@ -213,7 +213,7 @@ return Err; unsigned LineStart = 0; for (size_t I = 0; I < NumRegions; ++I) { - Counter C; + Counter C, C2; CounterMappingRegion::RegionKind Kind = CounterMappingRegion::CodeRegion; // Read the combined counter + region kind. @@ -223,6 +223,18 @@ return Err; unsigned Tag = EncodedCounterAndRegion & Counter::EncodingTagMask; uint64_t ExpandedFileID = 0; + + // If Tag does not represent a ZeroCounter, then it is understood to refer + // to a counter or counter expression with region kind assumed to be + // "CodeRegion". In that case, EncodedCounterAndRegion actually encodes the + // referenced counter or counter expression (and nothing else). + // + // If Tag represents a ZeroCounter and EncodingExpansionRegionBit is set, + // then EncodedCounterAndRegion is interpreted to represent an + // ExpansionRegion. In all other cases, EncodedCounterAndRegion is + // interpreted to refer to a specific region kind, after which additional + // fields may be read (e.g. BranchRegions have two encoded counters that + // follow an encoded region kind value). if (Tag != Counter::Zero) { if (auto Err = decodeCounter(EncodedCounterAndRegion, C)) return Err; @@ -243,6 +255,14 @@ case CounterMappingRegion::SkippedRegion: Kind = CounterMappingRegion::SkippedRegion; break; + case CounterMappingRegion::BranchRegion: + // For a Branch Region, read two successive counters. + Kind = CounterMappingRegion::BranchRegion; + if (auto Err = readCounter(C)) + return Err; + if (auto Err = readCounter(C2)) + return Err; + break; default: return make_error(coveragemap_error::malformed); } @@ -294,7 +314,7 @@ dbgs() << "\n"; }); - auto CMR = CounterMappingRegion(C, InferredFileID, ExpandedFileID, + auto CMR = CounterMappingRegion(C, C2, InferredFileID, ExpandedFileID, LineStart, ColumnStart, LineStart + NumLines, ColumnEnd, Kind); if (CMR.startLoc() > CMR.endLoc()) @@ -600,7 +620,7 @@ CovBuf += FilenamesSize; FilenameRange FileRange(FilenamesBegin, Filenames.size() - FilenamesBegin); - if (Version == CovMapVersion::Version4) { + if (Version >= CovMapVersion::Version4) { // Map a hash of the filenames region to the filename range associated // with this coverage header. int64_t FilenamesRef = @@ -628,7 +648,7 @@ // This is a no-op in Version4 (coverage mappings are not affixed to the // coverage header). const char *MappingBuf = CovBuf; - if (Version == CovMapVersion::Version4 && CoverageSize != 0) + if (Version >= CovMapVersion::Version4 && CoverageSize != 0) return make_error(coveragemap_error::malformed); CovBuf += CoverageSize; const char *MappingEnd = CovBuf; @@ -682,7 +702,7 @@ if (FileRange && !FileRange->isInvalid()) { StringRef Mapping = CFR->template getCoverageMapping(OutOfLineMappingBuf); - if (Version == CovMapVersion::Version4 && + if (Version >= CovMapVersion::Version4 && Mapping.data() + Mapping.size() > FuncRecBufEnd) return make_error(coveragemap_error::malformed); if (Error Err = insertFunctionRecordIfNeeded(CFR, Mapping, *FileRange)) @@ -711,6 +731,7 @@ case CovMapVersion::Version2: case CovMapVersion::Version3: case CovMapVersion::Version4: + case CovMapVersion::Version5: // Decompress the name data. if (Error E = P.create(P.getNameData())) return std::move(E); @@ -723,6 +744,9 @@ else if (Version == CovMapVersion::Version4) return std::make_unique>(P, R, F); + else if (Version == CovMapVersion::Version5) + return std::make_unique>(P, R, F); } llvm_unreachable("Unsupported version"); } @@ -766,7 +790,7 @@ } // In Version4, function records are not affixed to coverage headers. Read // the records from their dedicated section. - if (Version == CovMapVersion::Version4) + if (Version >= CovMapVersion::Version4) return Reader->readFunctionRecords(FuncRecBuf, FuncRecBufEnd, None, nullptr, nullptr); return Error::success(); diff --git a/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp --- a/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp @@ -80,10 +80,14 @@ ArrayRef MappingRegions) : Expressions(Expressions) { AdjustedExpressionIDs.resize(Expressions.size(), 0); - for (const auto &I : MappingRegions) + for (const auto &I : MappingRegions) { mark(I.Count); - for (const auto &I : MappingRegions) + mark(I.FalseCount); + } + for (const auto &I : MappingRegions) { gatherUsed(I.Count); + gatherUsed(I.FalseCount); + } } void mark(Counter C) { @@ -201,6 +205,7 @@ PrevLineStart = 0; } Counter Count = Minimizer.adjust(I->Count); + Counter FalseCount = Minimizer.adjust(I->FalseCount); switch (I->Kind) { case CounterMappingRegion::CodeRegion: case CounterMappingRegion::GapRegion: @@ -226,6 +231,13 @@ << Counter::EncodingCounterTagAndExpansionRegionTagBits, OS); break; + case CounterMappingRegion::BranchRegion: + encodeULEB128(unsigned(I->Kind) + << Counter::EncodingCounterTagAndExpansionRegionTagBits, + OS); + writeCounter(MinExpressions, Count, OS); + writeCounter(MinExpressions, FalseCount, OS); + break; } assert(I->LineStart >= PrevLineStart); encodeULEB128(I->LineStart - PrevLineStart, OS); diff --git a/llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json b/llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json --- a/llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json +++ b/llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json @@ -1,24 +1,28 @@ CHECK: {"data": CHECK-SAME: [{ CHECK-SAME: "files":[ -CHECK-SAME: {"expansions":[], +CHECK-SAME: {"branches":[], +CHECK-SAME: "expansions":[], CHECK-SAME: "filename":"/tmp/binary-formats.c", CHECK-SAME: "segments": CHECK-SAME: 4,40,100,true,true,false CHECK-SAME: 4,42,0,false,false,false -CHECK-SAME: "summary":{"functions":{"count":1,"covered":1,"percent":100}, +CHECK-SAME: "summary":{"branches":{"count":0,"covered":0,"notcovered":0,"percent":0}, +CHECK-SAME: "functions":{"count":1,"covered":1,"percent":100}, CHECK-SAME: "instantiations":{"count":1,"covered":1,"percent":100}, CHECK-SAME: "lines":{"count":1,"covered":1,"percent":100}, CHECK-SAME: "regions":{"count":1,"covered":1,"notcovered":0,"percent":100}}} CHECK-SAME: ], CHECK-SAME: "functions":[ -CHECK-SAME: {"count":100,"filenames":["/tmp/binary-formats.c"],"name":"main", +CHECK-SAME: {"branches":[], +CHECK-SAME: "count":100,"filenames":["/tmp/binary-formats.c"],"name":"main", CHECK-SAME: "regions": CHECK-SAME: 4,40,4,42,100,0,0,0 CHECK-SAME: } CHECK-SAME: ], CHECK-SAME: "totals": -CHECK-SAME: {"functions":{"count":1,"covered":1,"percent":100}, +CHECK-SAME: {"branches":{"count":0,"covered":0,"notcovered":0,"percent":0}, +CHECK-SAME: "functions":{"count":1,"covered":1,"percent":100}, CHECK-SAME: "instantiations":{"count":1,"covered":1,"percent":100}, CHECK-SAME: "lines":{"count":1,"covered":1,"percent":100}, CHECK-SAME: "regions":{"count":1,"covered":1,"notcovered":0,"percent":100}}} diff --git a/llvm/test/tools/llvm-cov/Inputs/branch-c-general.o32l b/llvm/test/tools/llvm-cov/Inputs/branch-c-general.o32l new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ 0) // CHECK: Branch ([[@LINE]]:10): [True: 100, False: 1] + i--; + do {} while (i++ < 75); // CHECK: Branch ([[@LINE]]:16): [True: 75, False: 1] + +} + +void conditionals() { + for (int i = 0; i < 100; ++i) {// CHECK: Branch ([[@LINE]]:19): [True: 100, False: 1] + if (i % 2) { // CHECK: Branch ([[@LINE]]:9): [True: 50, False: 50] + if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 50, False: 0] + } else if (i % 3) { // CHECK: Branch ([[@LINE]]:16): [True: 33, False: 17] + if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 33, False: 0] + } else { + if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 16, False: 1] + } + // CHECK: Branch ([[@LINE+1]]:9): [Folded - Ignored] + if (1 && i) {} // CHECK: Branch ([[@LINE]]:14): [True: 99, False: 1] + if (0 || i) {} // CHECK: Branch ([[@LINE]]:9): [Folded - Ignored] + } // CHECK: Branch ([[@LINE-1]]:14): [True: 99, False: 1] + +} + +void early_exits() { + int i = 0; + + if (i) {} // CHECK: Branch ([[@LINE]]:7): [True: 0, False: 1] + + while (i < 100) { // CHECK: Branch ([[@LINE]]:10): [True: 51, False: 0] + i++; + if (i > 50) // CHECK: Branch ([[@LINE]]:9): [True: 1, False: 50] + break; + if (i % 2) // CHECK: Branch ([[@LINE]]:9): [True: 25, False: 25] + continue; + } + + if (i) {} // CHECK: Branch ([[@LINE]]:7): [True: 1, False: 0] + + do { + if (i > 75) // CHECK: Branch ([[@LINE]]:9): [True: 1, False: 25] + return; + else + i++; + } while (i < 100); // CHECK: Branch ([[@LINE]]:12): [True: 25, False: 0] + + if (i) {} // CHECK: Branch ([[@LINE]]:7): [True: 0, False: 0] + +} + +void jumps() { + int i; + + for (i = 0; i < 2; ++i) { // CHECK: Branch ([[@LINE]]:15): [True: 1, False: 0] + goto outofloop; + // Never reached -> no weights + if (i) {} // CHECK: Branch ([[@LINE]]:9): [True: 0, False: 0] + } + +outofloop: + if (i) {} // CHECK: Branch ([[@LINE]]:7): [True: 0, False: 1] + + goto loop1; + + while (i) { // CHECK: Branch ([[@LINE]]:10): [True: 0, False: 1] + loop1: + if (i) {} // CHECK: Branch ([[@LINE]]:9): [True: 0, False: 1] + } + + goto loop2; +first: +second: +third: + i++; + if (i < 3) // CHECK: Branch ([[@LINE]]:7): [True: 2, False: 1] + goto loop2; + + while (i < 3) { // CHECK: Branch ([[@LINE]]:10): [True: 0, False: 1] + loop2: + switch (i) { + case 0: // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 2] + goto first; + case 1: // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 2] + goto second; + case 2: // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 2] + goto third; + } + } + + for (i = 0; i < 10; ++i) { // CHECK: Branch ([[@LINE]]:15): [True: 10, False: 1] + goto withinloop; + // never reached -> no weights + if (i) {} // CHECK: Branch ([[@LINE]]:9): [True: 0, False: 0] + withinloop: + if (i) {} // CHECK: Branch ([[@LINE]]:9): [True: 9, False: 1] + } + +} + +void switches() { + static int weights[] = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5}; + + // No cases -> no weights + switch (weights[0]) { + default: // CHECK: Branch ([[@LINE]]:3): [True: 1, False: 0] + break; + } + // CHECK: Branch ([[@LINE+1]]:63): [True: 15, False: 0] + for (int i = 0, len = sizeof(weights) / sizeof(weights[0]); i < len; ++i) { + switch (i[weights]) { + case 1: // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 14] + if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 0, False: 1] + // fallthrough + case 2: // CHECK: Branch ([[@LINE]]:5): [True: 2, False: 13] + if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 2, False: 1] + break; + case 3: // CHECK: Branch ([[@LINE]]:5): [True: 3, False: 12] + if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 3, False: 0] + continue; + case 4: // CHECK: Branch ([[@LINE]]:5): [True: 4, False: 11] + if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 4, False: 0] + switch (i) { + case 6 ... 9: // CHECK: Branch ([[@LINE]]:7): [True: 4, False: 0] + if (i) {} // CHECK: Branch ([[@LINE]]:13): [True: 4, False: 0] + continue; + } + + default: // CHECK: Branch ([[@LINE]]:5): [True: 5, False: 10] + if (i == len - 1) // CHECK: Branch ([[@LINE]]:11): [True: 1, False: 4] + return; + } + } + + // Never reached -> no weights + if (weights[0]) {} // CHECK: Branch ([[@LINE]]:7): [True: 0, False: 0] + +} + +void big_switch() { + for (int i = 0; i < 32; ++i) {// CHECK: Branch ([[@LINE]]:19): [True: 32, False: 1] + switch (1 << i) { + case (1 << 0): // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 31] + if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 0, False: 1] + // fallthrough + case (1 << 1): // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 31] + if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 1, False: 1] + break; + case (1 << 2) ... (1 << 12):// CHECK: Branch ([[@LINE]]:5): [True: 11, False: 21] + if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 11, False: 0] + break; + // The branch for the large case range above appears after the case body. + + case (1 << 13): // CHECK: Branch ([[@LINE]]:5): [True: 1, False: 31] + if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 1, False: 0] + break; + case (1 << 14) ... (1 << 28):// CHECK: Branch ([[@LINE]]:5): [True: 15, False: 17] + if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 15, False: 0] + break; + // The branch for the large case range above appears after the case body. + // CHECK: Branch ([[@LINE+1]]:5): [True: 1, False: 31] + case (1 << 29) ... ((1 << 29) + 1): + if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 1, False: 0] + break; + default: // CHECK: Branch ([[@LINE]]:5): [True: 2, False: 30] + if (i) {} // CHECK: Branch ([[@LINE]]:11): [True: 2, False: 0] + break; + } + } + +} + +void boolean_operators() { + int v; // CHECK: Branch ([[@LINE+1]]:19): [True: 100, False: 1] + for (int i = 0; i < 100; ++i) { + v = i % 3 || i; // CHECK: Branch ([[@LINE]]:9): [True: 66, False: 34] + // CHECK: Branch ([[@LINE-1]]:18): [True: 33, False: 1] + v = i % 3 && i; // CHECK: Branch ([[@LINE]]:9): [True: 66, False: 34] + // CHECK: Branch ([[@LINE-1]]:18): [True: 66, False: 0] + v = i % 3 || i % 2 || i; // CHECK: Branch ([[@LINE]]:9): [True: 66, False: 34] + // CHECK: Branch ([[@LINE-1]]:18): [True: 17, False: 17] + v = i % 2 && i % 3 && i; // CHECK: Branch ([[@LINE-2]]:27): [True: 16, False: 1] + } // CHECK: Branch ([[@LINE-1]]:9): [True: 50, False: 50] + // CHECK: Branch ([[@LINE-2]]:18): [True: 33, False: 17] +} // CHECK: Branch ([[@LINE-3]]:27): [True: 33, False: 0] + +void boolop_loops() { + int i = 100; + + while (i && i > 50) // CHECK: Branch ([[@LINE]]:10): [True: 51, False: 0] + i--; // CHECK: Branch ([[@LINE-1]]:15): [True: 50, False: 1] + + while ((i % 2) || (i > 0)) // CHECK: Branch ([[@LINE]]:10): [True: 25, False: 26] + i--; // CHECK: Branch ([[@LINE-1]]:21): [True: 25, False: 1] + + for (i = 100; i && i > 50; --i); // CHECK: Branch ([[@LINE]]:17): [True: 51, False: 0] + // CHECK: Branch ([[@LINE-1]]:22): [True: 50, False: 1] + for (; (i % 2) || (i > 0); --i); // CHECK: Branch ([[@LINE]]:10): [True: 25, False: 26] + // CHECK: Branch ([[@LINE-1]]:21): [True: 25, False: 1] +} + +void conditional_operator() { + int i = 100; + + int j = i < 50 ? i : 1; // CHECK: Branch ([[@LINE]]:11): [True: 0, False: 1] + + int k = i ?: 0; // CHECK: Branch ([[@LINE]]:11): [True: 1, False: 0] + +} + +void do_fallthrough() { + for (int i = 0; i < 10; ++i) {// CHECK: Branch ([[@LINE]]:19): [True: 10, False: 1] + int j = 0; + do { + // The number of exits out of this do-loop via the break statement + // exceeds the counter value for the loop (which does not include the + // fallthrough count). Make sure that does not violate any assertions. + if (i < 8) break; // CHECK: Branch ([[@LINE]]:11): [True: 8, False: 4] + j++; + } while (j < 2); // CHECK: Branch ([[@LINE]]:14): [True: 2, False: 2] + } +} + +static void static_func() { + for (int i = 0; i < 10; ++i) {// CHECK: Branch ([[@LINE]]:19): [True: 10, False: 1] + } +} + + + + + + + + + + +int main(int argc, const char *argv[]) { + simple_loops(); + conditionals(); + early_exits(); + jumps(); + switches(); + big_switch(); + boolean_operators(); + boolop_loops(); + conditional_operator(); + do_fallthrough(); + static_func(); + extern void __llvm_profile_write_file(); + __llvm_profile_write_file(); + return 0; +} + +// REPORT: Name Regions Miss Cover Lines Miss Cover Branches Miss Cover +// REPORT-NEXT: --- +// REPORT-NEXT: simple_loops 8 0 100.00% 9 0 100.00% 6 0 100.00% +// REPORT-NEXT: conditionals 24 0 100.00% 15 0 100.00% 16 2 87.50% +// REPORT-NEXT: early_exits 20 4 80.00% 25 3 88.00% 16 6 62.50% +// REPORT-NEXT: jumps 39 12 69.23% 48 4 91.67% 26 9 65.38% +// REPORT-NEXT: switches 28 5 82.14% 38 5 86.84% 30 9 70.00% +// REPORT-NEXT: big_switch 25 1 96.00% 32 0 100.00% 30 6 80.00% +// REPORT-NEXT: boolean_operators 16 0 100.00% 13 0 100.00% 22 2 90.91% +// REPORT-NEXT: boolop_loops 19 0 100.00% 14 0 100.00% 16 2 87.50% +// REPORT-NEXT: conditional_operator 4 2 50.00% 8 1 87.50% 4 2 50.00% +// REPORT-NEXT: do_fallthrough 9 0 100.00% 12 0 100.00% 6 0 100.00% +// REPORT-NEXT: main 1 0 100.00% 16 0 100.00% 0 0 0.00% +// REPORT-NEXT: c-general.c:static_func 4 0 100.00% 4 0 100.00% 2 0 100.00% +// REPORT-NEXT: --- +// REPORT-NEXT: TOTAL 197 24 87.82% 234 13 94.44% 174 38 78.16% + +// Test file-level report. +// RUN: llvm-profdata merge %S/Inputs/branch-c-general.proftext -o %t.profdata +// RUN: llvm-cov report %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s -check-prefix=FILEREPORT +// FILEREPORT: TOTAL{{.*}}174 38 78.16% + +// Test color True/False output. +// RUN: llvm-cov show --use-color --show-branches=count %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s -check-prefix=USECOLOR +// USECOLOR: Branch ({{[0-9]+}}:7): {{.*}}: 0, {{.*}}0] + +// Test html output. +// RUN: llvm-cov show --show-branch-summary --show-branches=count %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s -format html -o %t.html.dir +// RUN: FileCheck -check-prefix=HTML -input-file=%t.html.dir/coverage/tmp/branch-c-general.c.html %s +// HTML-COUNT-89: Branch ( +// HTML-NOT: Branch ( + +// RUN: FileCheck -check-prefix HTML-INDEX -input-file %t.html.dir/index.html %s +// HTML-INDEX-LABEL: +// HTML-INDEX: +// HTML-INDEX: +// HTML-INDEX: +// HTML-INDEX: +// HTML-INDEX: +// HTML-INDEX: +// HTML-INDEX: +// HTML-INDEX: Totals diff --git a/llvm/test/tools/llvm-cov/branch-export-json.test b/llvm/test/tools/llvm-cov/branch-export-json.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cov/branch-export-json.test @@ -0,0 +1,49 @@ + +// RUN: llvm-profdata merge %S/Inputs/branch-showBranchPercentage.proftext -o %t.profdata +// RUN: llvm-cov export --format=text %S/Inputs/branch-showBranchPercentage.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %S/branch-showBranchPercentage.c | FileCheck %s + +// CHECK: "branches": +// CHECK: 14,7,14,15,1,5,0,0,4 +// CHECK: 27,8,27,14,1,4,0,0,4 +// CHECK: 27,18,27,24,0,1,0,0,4 +// CHECK: 27,29,27,36,0,5,0,0,4 +// CHECK: 27,40,27,46,2,3,0,0,4 +// CHECK: 30,8,30,14,4,1,0,0,4 +// CHECK: 30,18,30,24,0,1,0,0,4 +// CHECK: 32,8,32,14,4,1,0,0,4 +// CHECK: 32,18,32,24,1,3,0,0,4 +// CHECK: 34,15,34,20,1,5,0,0,4 +// CHECK: 41,5,41,11,1,4,0,0,4 +// CHECK: 43,5,43,11,1,4,0,0,4 +// CHECK: 45,5,45,11,0,5,0,0,4 +// CHECK: 47,5,47,12,3,2,0,0,4 +// CHECK: 53,12,53,20,50,5,0,0,4 +// CHECK: {"count":30,"covered":26,"notcovered":4,"percent":86.666666666666671} + +// Check recursive macro-expansions. +// RUN: llvm-profdata merge %S/Inputs/branch-macros.proftext -o %t.profdata +// RUN: llvm-cov export --format=text %S/Inputs/branch-macros.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %S/branch-macros.c | FileCheck %s -check-prefix=MACROS + +// MACROS: "branches": +// MACROS: 27,10,27,11,0,3,0,0,4 +// MACROS: 27,15,27,16,0,0,0,0,4 +// MACROS: 27,20,27,21,0,0,0,0,4 +// MACROS: 27,25,27,26,0,0,0,0,4 +// MACROS: 27,30,27,31,0,0,0,0,4 + +// MACROS: 15,5,23,1,2,1,0,4 +// MACROS: 6,15,6,23,0,1,2,0,4 +// MACROS: 5,15,5,23,1,2,7,0,4 +// MACROS: 6,15,6,23,0,1,8,0,4 +// MACROS: 5,15,5,23,1,2,12,0,4 +// MACROS: 6,15,6,23,0,1,13,0,4 +// MACROS: 5,15,5,23,1,2,16,0,4 +// MACROS: 6,15,6,23,0,1,17,0,4 +// MACROS: 5,15,5,23,1,2,19,0,4 +// MACROS: 6,15,6,23,0,1,20,0,4 +// MACROS: 5,15,5,23,1,2,11,0,4 +// MACROS: 6,15,6,23,0,1,12,0,4 +// MACROS: 5,15,5,23,1,2,8,0,4 +// MACROS: 6,15,6,23,0,1,9,0,4 +// MACROS: 8,15,8,38,1,2,2,0,4 +// MACROS: {"count":40,"covered":24,"notcovered":16,"percent":60} diff --git a/llvm/test/tools/llvm-cov/branch-export-lcov.test b/llvm/test/tools/llvm-cov/branch-export-lcov.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cov/branch-export-lcov.test @@ -0,0 +1,73 @@ + +// RUN: llvm-profdata merge %S/Inputs/branch-showBranchPercentage.proftext -o %t.profdata +// RUN: llvm-cov export --format=lcov %S/Inputs/branch-showBranchPercentage.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %S/branch-showBranchPercentage.c | FileCheck %s + +// CHECK-DAG: BRDA:14,0,0,1 +// CHECK-DAG: BRDA:14,0,1,5 +// CHECK-DAG: BRDA:27,0,0,1 +// CHECK-DAG: BRDA:27,0,1,4 +// CHECK-DAG: BRDA:27,1,2,0 +// CHECK-DAG: BRDA:27,1,3,1 +// CHECK-DAG: BRDA:27,2,4,0 +// CHECK-DAG: BRDA:27,2,5,5 +// CHECK-DAG: BRDA:27,3,6,2 +// CHECK-DAG: BRDA:27,3,7,3 +// CHECK-DAG: BRDA:30,0,0,4 +// CHECK-DAG: BRDA:30,0,1,1 +// CHECK-DAG: BRDA:30,1,2,0 +// CHECK-DAG: BRDA:30,1,3,1 +// CHECK-DAG: BRDA:32,0,0,4 +// CHECK-DAG: BRDA:32,0,1,1 +// CHECK-DAG: BRDA:32,1,2,1 +// CHECK-DAG: BRDA:32,1,3,3 +// CHECK-DAG: BRDA:34,0,0,1 +// CHECK-DAG: BRDA:34,0,1,5 +// CHECK-DAG: BRDA:41,0,0,1 +// CHECK-DAG: BRDA:41,0,1,4 +// CHECK-DAG: BRDA:43,0,0,1 +// CHECK-DAG: BRDA:43,0,1,4 +// CHECK-DAG: BRDA:45,0,0,0 +// CHECK-DAG: BRDA:45,0,1,5 +// CHECK-DAG: BRDA:47,0,0,3 +// CHECK-DAG: BRDA:47,0,1,2 +// CHECK-DAG: BRDA:53,0,0,50 +// CHECK-DAG: BRDA:53,0,1,5 +// CHECK-NOT: BRDA +// CHECK: BRF:30 +// CHECK: BFH:26 + +// Check recursive macro-expansions. +// RUN: llvm-profdata merge %S/Inputs/branch-macros.proftext -o %t.profdata +// RUN: llvm-cov export --format=lcov %S/Inputs/branch-macros.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %S/branch-macros.cpp | FileCheck %s -check-prefix=MACROS + +// MACROS-COUNT-4: BRDA:17 +// MACROS-NOT: BRDA:17 + +// MACROS-COUNT-4: BRDA:19 +// MACROS-NOT: BRDA:19 + +// MACROS-COUNT-4: BRDA:21 +// MACROS-NOT: BRDA:21 + +// MACROS-COUNT-4: BRDA:23 +// MACROS-NOT: BRDA:23 + +// MACROS-COUNT-4: BRDA:25 +// MACROS-NOT: BRDA:25 + +// MACROS: BRDA:27,0,0,0 +// MACROS: BRDA:27,0,1,3 +// MACROS: BRDA:27,1,2,- +// MACROS: BRDA:27,1,3,- +// MACROS: BRDA:27,2,4,- +// MACROS: BRDA:27,2,5,- +// MACROS: BRDA:27,3,6,- +// MACROS: BRDA:27,3,7,- +// MACROS: BRDA:27,4,8,- +// MACROS: BRDA:27,4,9,- + +// MACROS-COUNT-10: BRDA:37 +// MACROS-NOT: BRDA:37 +// MACROS-NOT: BRDA +// MACROS: BRF:40 +// MACROS: BFH:24 diff --git a/llvm/test/tools/llvm-cov/branch-logical-mixed.cpp b/llvm/test/tools/llvm-cov/branch-logical-mixed.cpp new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cov/branch-logical-mixed.cpp @@ -0,0 +1,90 @@ +// RUN: llvm-profdata merge %S/Inputs/branch-logical-mixed.proftext -o %t.profdata +// RUN: llvm-cov show --show-branches=count %S/Inputs/branch-logical-mixed.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s +// RUN: llvm-cov report --show-branch-summary %S/Inputs/branch-logical-mixed.o32l -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S %s | FileCheck %s -check-prefix=REPORT + +#include +#include + +void func(int a, int b) { + bool b0 = a <= b; + bool b1 = a == b; + bool b2 = a >= b; + bool b3 = a < b; + bool b4 = a > b; + bool b5 = a != b; + + bool c = b0 && // CHECK: Branch ([[@LINE]]:12): [True: 3, False: 1] + b1 && // CHECK: Branch ([[@LINE]]:12): [True: 2, False: 1] + b2 && // CHECK: Branch ([[@LINE]]:12): [True: 2, False: 0] + b3 && // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 2] + b4 && // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 0] + b5; // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 0] + + bool d = b0 || // CHECK: Branch ([[@LINE]]:12): [True: 3, False: 1] + b1 || // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 1] + b2 || // CHECK: Branch ([[@LINE]]:12): [True: 1, False: 0] + b3 || // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 0] + b4 || // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 0] + b5; // CHECK: Branch ([[@LINE]]:12): [True: 0, False: 0] + + bool e = (b0 && // CHECK: Branch ([[@LINE]]:13): [True: 3, False: 1] + b5) || // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 2] + (b1 && // CHECK: Branch ([[@LINE]]:13): [True: 2, False: 1] + b4) || // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 2] + (b2 && // CHECK: Branch ([[@LINE]]:13): [True: 3, False: 0] + b3) || // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 3] + (b3 && // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 3] + b2) || // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 0] + (b4 && // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 2] + b1) || // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 1] + (b5 && // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 2] + b0); // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 1] + + bool f = (b0 || // CHECK: Branch ([[@LINE]]:13): [True: 3, False: 1] + b5) && // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 0] + (b1 || // CHECK: Branch ([[@LINE]]:13): [True: 2, False: 2] + b4) && // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 1] + (b2 || // CHECK: Branch ([[@LINE]]:13): [True: 3, False: 0] + b3) && // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 0] + (b3 || // CHECK: Branch ([[@LINE]]:13): [True: 0, False: 3] + b2) && // CHECK: Branch ([[@LINE]]:13): [True: 3, False: 0] + (b4 || // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 2] + b1) && // CHECK: Branch ([[@LINE]]:13): [True: 2, False: 0] + (b5 || // CHECK: Branch ([[@LINE]]:13): [True: 1, False: 2] + b0); // CHECK: Branch ([[@LINE]]:13): [True: 2, False: 0] + + if (c) // CHECK: Branch ([[@LINE]]:7): [True: 0, False: 4] + printf("case0\n"); + else + printf("case1\n"); + + if (d) // CHECK: Branch ([[@LINE]]:7): [True: 4, False: 0] + printf("case2\n"); + else + printf("case3\n"); + + if (e) // CHECK: Branch ([[@LINE]]:7): [True: 1, False: 3] + printf("case4\n"); + else + printf("case5\n"); + + if (f) // CHECK: Branch ([[@LINE]]:7): [True: 3, False: 1] + printf("case6\n"); + else + printf("case7\n"); +} + +extern "C" { extern void __llvm_profile_write_file(void); } +int main(int argc, char *argv[]) +{ + func(atoi(argv[1]), atoi(argv[2])); + __llvm_profile_write_file(); + return 0; +} + +// REPORT: Name Regions Miss Cover Lines Miss Cover Branches Miss Cover +// REPORT-NEXT: --- +// REPORT-NEXT: _Z4funcii 77 9 88.31% 68 10 85.29% 80 32 60.00% +// REPORT-NEXT: main 1 0 100.00% 5 0 100.00% 0 0 0.00% +// REPORT-NEXT: --- +// REPORT-NEXT: TOTAL 78 9 88.46% 73 10 86.30% 80 32 60.00% diff --git a/llvm/test/tools/llvm-cov/branch-macros.cpp b/llvm/test/tools/llvm-cov/branch-macros.cpp new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cov/branch-macros.cpp @@ -0,0 +1,60 @@ +// RUN: llvm-profdata merge %S/Inputs/branch-macros.proftext -o %t.profdata +// RUN: llvm-cov show --show-expansions --show-branches=count %S/Inputs/branch-macros.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s +// RUN: llvm-cov report --show-branch-summary %S/Inputs/branch-macros.o32l -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S %s | FileCheck %s -check-prefix=REPORT + +#define COND1 (a == b) +#define COND2 (a != b) +#define COND3 (COND1 && COND2) +#define COND4 (COND3 ? COND2 : COND1) +#define MACRO1 COND3 +#define MACRO2 MACRO1 +#define MACRO3 MACRO2 + +#include + + +bool func(int a, int b) { + bool c = COND1 && COND2; // CHECK: | | | Branch ([[@LINE-12]]:15): [True: 1, False: 2] + // CHECK: | | | Branch ([[@LINE-12]]:15): [True: 0, False: 1] + bool d = COND3; // CHECK: | | | | | Branch ([[@LINE-14]]:15): [True: 1, False: 2] + // CHECK: | | | | | Branch ([[@LINE-14]]:15): [True: 0, False: 1] + bool e = MACRO1; // CHECK: | | | | | | | Branch ([[@LINE-16]]:15): [True: 1, False: 2] + // CHECK: | | | | | | | Branch ([[@LINE-16]]:15): [True: 0, False: 1] + bool f = MACRO2; // CHECK: | | | | | | | | | Branch ([[@LINE-18]]:15): [True: 1, False: 2] + // CHECK: | | | | | | | | | Branch ([[@LINE-18]]:15): [True: 0, False: 1] + bool g = MACRO3; // CHECK: | | | | | | | | | | | Branch ([[@LINE-20]]:15): [True: 1, False: 2] + // CHECK: | | | | | | | | | | | Branch ([[@LINE-20]]:15): [True: 0, False: 1] + return c && d && e && f && g; + // CHECK: | Branch ([[@LINE-1]]:10): [True: 0, False: 3] + // CHECK: | Branch ([[@LINE-2]]:15): [True: 0, False: 0] + // CHECK: | Branch ([[@LINE-3]]:20): [True: 0, False: 0] + // CHECK: | Branch ([[@LINE-4]]:25): [True: 0, False: 0] + // CHECK: | Branch ([[@LINE-5]]:30): [True: 0, False: 0] +} + + +bool func2(int a, int b) { + bool h = MACRO3 || COND4; // CHECK: | | | | | | | | | | | Branch ([[@LINE-32]]:15): [True: 1, False: 2] + // CHECK: | | | | | | | | | | | Branch ([[@LINE-32]]:15): [True: 0, False: 1] + // CHECK: | | | | | | | Branch ([[@LINE-34]]:15): [True: 1, False: 2] + // CHECK: | | | | | | | Branch ([[@LINE-34]]:15): [True: 0, False: 1] + // CHECK: | | | Branch ([[@LINE-33]]:15): [True: 1, False: 2] + return h; +} + +extern "C" { extern void __llvm_profile_write_file(void); } +int main(int argc, char *argv[]) +{ + func(atoi(argv[1]), atoi(argv[2])); + func2(atoi(argv[1]), atoi(argv[2])); + __llvm_profile_write_file(); + return 0; +} + +// REPORT: Name Regions Miss Cover Lines Miss Cover Branches Miss Cover +// REPORT-NEXT: --- +// REPORT-NEXT: _Z4funcii 28 4 85.71% 18 0 100.00% 30 14 53.33% +// REPORT-NEXT: _Z5func2ii 13 1 92.31% 8 0 100.00% 10 2 80.00% +// REPORT-NEXT: main 1 0 100.00% 6 0 100.00% 0 0 0.00% +// REPORT-NEXT: --- +// REPORT-NEXT: TOTAL 42 5 88.10% 32 0 100.00% 40 16 60.00% diff --git a/llvm/test/tools/llvm-cov/branch-noShowBranch.test b/llvm/test/tools/llvm-cov/branch-noShowBranch.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cov/branch-noShowBranch.test @@ -0,0 +1,25 @@ + +// RUN: llvm-profdata merge %S/Inputs/branch-c-general.proftext -o %t.profdata +// RUN: llvm-cov show %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %S/branch-c-general.c | FileCheck %s +// RUN: llvm-cov report %S/Inputs/branch-c-general.o32l --show-branch-summary=false -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S %S/branch-c-general.c | FileCheck %s -check-prefix=REPORT + +// CHECK-NOT: | Branch + +// REPORT: Name Regions Miss Cover Lines Miss Cover +// REPORT-NOT: Name Regions Miss Cover Lines Miss Cover Branches Miss Cover +// REPORT: --- +// REPORT-NOT: simple_loops 8 0 100.00% 9 0 100.00% 6 0 100.00% +// REPORT-NOT: conditionals 24 0 100.00% 15 0 100.00% 16 2 87.50% +// REPORT-NOT: early_exits 20 4 80.00% 25 2 92.00% 16 6 62.50% +// REPORT-NOT: jumps 39 12 69.23% 48 2 95.83% 26 9 65.38% +// REPORT-NOT: switches 28 5 82.14% 38 4 89.47% 30 9 70.00% +// REPORT-NOT: big_switch 25 1 96.00% 32 0 100.00% 30 6 80.00% +// REPORT-NOT: boolean_operators 16 0 100.00% 13 0 100.00% 22 2 90.91% +// REPORT-NOT: boolop_loops 19 0 100.00% 14 0 100.00% 16 2 87.50% +// REPORT-NOT: conditional_operator 4 2 50.00% 8 0 100.00% 4 2 50.00% +// REPORT-NOT: do_fallthrough 9 0 100.00% 12 0 100.00% 6 0 100.00% +// REPORT-NOT: main 1 0 100.00% 16 0 100.00% 0 0 0.00% +// REPORT-NOT: c-general.c:static_func 4 0 100.00% 4 0 100.00% 2 0 100.00% +// REPORT: TOTAL 197 24 87.82% 234 13 94.44% +// REPORT-NOT: TOTAL 197 24 87.82% 234 13 94.44% 174 38 78.16% + diff --git a/llvm/test/tools/llvm-cov/branch-showBranchPercentage.c b/llvm/test/tools/llvm-cov/branch-showBranchPercentage.c new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cov/branch-showBranchPercentage.c @@ -0,0 +1,77 @@ +// Test visualization of branch taken percentages + +// RUN: llvm-profdata merge %S/Inputs/branch-showBranchPercentage.proftext -o %t.profdata +// RUN: llvm-cov show --show-branches=percent %S/Inputs/branch-showBranchPercentage.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s + +#include +#include + +extern void __llvm_profile_write_file(void); + +int main(int argc, char *argv[]) +{ + int i = 0; + if (argc < 3) // CHECK: Branch ([[@LINE]]:7): [True: 16.67%, False: 83.33%] + { + __llvm_profile_write_file(); + return 0; + } + + int a = atoi(argv[1]); + int b = atoi(argv[2]); + + // CHECK: Branch ([[@LINE+4]]:8): [True: 20.00%, False: 80.00%] + // CHECK: Branch ([[@LINE+3]]:18): [True: 0.00%, False: 100.00%] + // CHECK: Branch ([[@LINE+2]]:29): [True: 0.00%, False: 100.00%] + // CHECK: Branch ([[@LINE+1]]:40): [True: 40.00%, False: 60.00%] + if ((a == 0 && b == 2) || b == 34 || a == b) + printf("case1\n"); + + b = (a != 0 || a == 2) ? b : b+2; // CHECK: Branch ([[@LINE]]:8): [True: 80.00%, False: 20.00%] + // CHECK: Branch ([[@LINE-1]]:18): [True: 0.00%, False: 100.00%] + b = (a != 0 && a == 1); // CHECK: Branch ([[@LINE]]:8): [True: 80.00%, False: 20.00%] + // CHECK: Branch ([[@LINE-1]]:18): [True: 25.00%, False: 75.00%] + for (i = 0; i < b; i++) { a = 2 + b + b; } + // CHECK: Branch ([[@LINE-1]]:15): [True: 16.67%, False: 83.33%] + + b = a; + + switch (a) + { + case 0: // CHECK: Branch ([[@LINE]]:5): [True: 20.00%, False: 80.00%] + printf("case0\n"); + case 2: // CHECK: Branch ([[@LINE]]:5): [True: 20.00%, False: 80.00%] + printf("case2\n"); + case 3: // CHECK: Branch ([[@LINE]]:5): [True: 0.00%, False: 100.00%] + printf("case3\n"); + default: break; // CHECK: Branch ([[@LINE]]:5): [True: 60.00%, False: 40.00%] + } + + i = 0; + do { + printf("loop\n"); + } while (i++ < 10); // CHECK: Branch ([[@LINE]]:12): [True: 90.91%, False: 9.09%] + + __llvm_profile_write_file(); + + return b; +} +// RUN: llvm-profdata merge %S/Inputs/branch-showBranchPercentage.proftext -o %t.profdata +// RUN: llvm-cov show --show-branches=percent %S/Inputs/branch-showBranchPercentage.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s -format html -o %t.html.dir + +// Test html output. +// RUN: FileCheck -check-prefix=HTML -input-file=%t.html.dir/coverage/tmp/branch-showBranchPercentage.c.html %s +// HTML: Branch ({{.*}}16.67%,{{.*}}83.33%] +// HTML: Branch ({{.*}}20.00%,{{.*}}80.00%] +// HTML: Branch ({{.*}}0.00%,{{.*}}100.00%] +// HTML: Branch ({{.*}}0.00%,{{.*}}100.00%] +// HTML: Branch ({{.*}}40.00%,{{.*}}60.00%] +// HTML: Branch ({{.*}}80.00%,{{.*}}20.00%] +// HTML: Branch ({{.*}}0.00%,{{.*}}100.00%] +// HTML: Branch ({{.*}}80.00%,{{.*}}20.00%] +// HTML: Branch ({{.*}}25.00%,{{.*}}75.00%] +// HTML: Branch ({{.*}}16.67%,{{.*}}83.33%] +// HTML: Branch ({{.*}}20.00%,{{.*}}80.00%] +// HTML: Branch ({{.*}}20.00%,{{.*}}80.00%] +// HTML: Branch ({{.*}}0.00%,{{.*}}100.00%] +// HTML: Branch ({{.*}}60.00%,{{.*}}40.00%] diff --git a/llvm/test/tools/llvm-cov/branch-templates.cpp b/llvm/test/tools/llvm-cov/branch-templates.cpp new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-cov/branch-templates.cpp @@ -0,0 +1,47 @@ +// RUN: llvm-profdata merge %S/Inputs/branch-templates.proftext -o %t.profdata +// RUN: llvm-cov show --show-expansions --show-branches=count %S/Inputs/branch-templates.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s +// RUN: llvm-cov report --show-branch-summary %S/Inputs/branch-templates.o32l -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S %s | FileCheck %s -check-prefix=REPORT + +#include + +template +void unused(T x) { + return; +} + +template +int func(T x) { + if(x) // CHECK: | Branch ([[@LINE]]:6): [True: 0, False: 1] + return 0; // CHECK: | Branch ([[@LINE-1]]:6): [True: 1, False: 0] + else // CHECK: | Branch ([[@LINE-2]]:6): [True: 0, False: 1] + return 1; + int j = 1; +} + + // CHECK-LABEL: _Z4funcIiEiT_: + // CHECK: | | Branch ([[@LINE-8]]:6): [True: 0, False: 1] + // CHECK-LABEL: _Z4funcIbEiT_: + // CHECK: | | Branch ([[@LINE-10]]:6): [True: 1, False: 0] + // CHECK-LABEL: _Z4funcIfEiT_: + // CHECK: | | Branch ([[@LINE-12]]:6): [True: 0, False: 1] + +extern "C" { extern void __llvm_profile_write_file(void); } +int main() { + if (func(0)) // CHECK: | Branch ([[@LINE]]:7): [True: 1, False: 0] + printf("case1\n"); + if (func(true)) // CHECK: | Branch ([[@LINE]]:7): [True: 0, False: 1] + printf("case2\n"); + if (func(0.0)) // CHECK: | Branch ([[@LINE]]:7): [True: 1, False: 0] + printf("case3\n"); + __llvm_profile_write_file(); + return 0; +} + +// REPORT: Name Regions Miss Cover Lines Miss Cover Branches Miss Cover +// REPORT-NEXT: --- +// REPORT-NEXT: main 7 1 85.71% 10 1 90.00% 6 3 50.00% +// REPORT-NEXT: _Z4funcIiEiT_ 5 2 60.00% 7 3 57.14% 2 1 50.00% +// REPORT-NEXT: _Z4funcIbEiT_ 5 2 60.00% 7 4 42.86% 2 1 50.00% +// REPORT-NEXT: _Z4funcIfEiT_ 5 2 60.00% 7 3 57.14% 2 1 50.00% +// REPORT-NEXT: --- +// REPORT-NEXT: TOTAL 22 7 68.18% 31 11 64.52% 12 6 50.00% diff --git a/llvm/test/tools/llvm-cov/ignore-filename-regex.test b/llvm/test/tools/llvm-cov/ignore-filename-regex.test --- a/llvm/test/tools/llvm-cov/ignore-filename-regex.test +++ b/llvm/test/tools/llvm-cov/ignore-filename-regex.test @@ -4,7 +4,7 @@ # Ignore all header files. RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \ RUN: -path-equivalence=/tmp,%S/Inputs -ignore-filename-regex='.*\.h$' \ -RUN: %S/Inputs/sources_specified/main.covmapping \ +RUN: %S/Inputs/sources_specified/main.covmapping --show-branch-summary=false \ RUN: | FileCheck -check-prefix=REPORT_IGNORE_HEADERS %s REPORT_IGNORE_HEADERS-NOT: {{.*}}dec.h{{.*}} @@ -15,7 +15,7 @@ # Ignore all files from "extra" directory. RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \ RUN: -path-equivalence=/tmp,%S/Inputs -ignore-filename-regex='.*extra[/\\].*' \ -RUN: %S/Inputs/sources_specified/main.covmapping \ +RUN: %S/Inputs/sources_specified/main.covmapping --show-branch-summary=false \ RUN: | FileCheck -check-prefix=REPORT_IGNORE_DIR %s REPORT_IGNORE_DIR-NOT: {{.*}}extra{{[/\\]}}dec.h{{.*}} @@ -27,7 +27,7 @@ # Ignore all files from "extra" directory even when SOURCES specified. RUN: llvm-cov report -instr-profile %S/Inputs/sources_specified/main.profdata \ RUN: -path-equivalence=/tmp,%S/Inputs -ignore-filename-regex='.*extra[/\\].*' \ -RUN: %S/Inputs/sources_specified/main.covmapping \ +RUN: %S/Inputs/sources_specified/main.covmapping --show-branch-summary=false \ RUN: %S/Inputs/sources_specified/extra %S/Inputs/sources_specified/abs.h \ RUN: | FileCheck -check-prefix=REPORT_IGNORE_DIR_WITH_SOURCES %s diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp --- a/llvm/tools/llvm-cov/CodeCoverage.cpp +++ b/llvm/tools/llvm-cov/CodeCoverage.cpp @@ -88,6 +88,12 @@ ArrayRef Expansions, const CoverageMapping &Coverage); + /// Create source views for the branches of the view. + void attachBranchSubViews(SourceCoverageView &View, StringRef SourceName, + ArrayRef Branches, + const MemoryBuffer &File, + CoverageData &CoverageInfo); + /// Create the source view of a particular function. std::unique_ptr createFunctionView(const FunctionRecord &Function, @@ -268,15 +274,45 @@ if (!SourceBuffer) continue; + auto SubViewBranches = ExpansionCoverage.getBranches(); auto SubViewExpansions = ExpansionCoverage.getExpansions(); auto SubView = SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(), ViewOpts, std::move(ExpansionCoverage)); attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); + attachBranchSubViews(*SubView, Expansion.Function.Name, SubViewBranches, + SourceBuffer.get(), ExpansionCoverage); View.addExpansion(Expansion.Region, std::move(SubView)); } } +void CodeCoverageTool::attachBranchSubViews(SourceCoverageView &View, + StringRef SourceName, + ArrayRef Branches, + const MemoryBuffer &File, + CoverageData &CoverageInfo) { + if (!ViewOpts.ShowBranchCounts && !ViewOpts.ShowBranchPercents) + return; + + const auto *NextBranch = Branches.begin(); + const auto *EndBranch = Branches.end(); + + // Group branches that have the same line number into the same subview. + while (NextBranch != EndBranch) { + std::vector ViewBranches; + unsigned CurrentLine = NextBranch->LineStart; + + while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart) + ViewBranches.push_back(*NextBranch++); + + if (!ViewBranches.empty()) { + auto SubView = SourceCoverageView::create(SourceName, File, ViewOpts, + std::move(CoverageInfo)); + View.addBranch(CurrentLine, ViewBranches, std::move(SubView)); + } + } +} + std::unique_ptr CodeCoverageTool::createFunctionView(const FunctionRecord &Function, const CoverageMapping &Coverage) { @@ -287,11 +323,14 @@ if (!SourceBuffer) return nullptr; + auto Branches = FunctionCoverage.getBranches(); auto Expansions = FunctionCoverage.getExpansions(); auto View = SourceCoverageView::create(DC.demangle(Function.Name), SourceBuffer.get(), ViewOpts, std::move(FunctionCoverage)); attachExpansionSubViews(*View, Expansions, Coverage); + attachBranchSubViews(*View, DC.demangle(Function.Name), Branches, + SourceBuffer.get(), FunctionCoverage); return View; } @@ -306,10 +345,13 @@ if (FileCoverage.empty()) return nullptr; + auto Branches = FileCoverage.getBranches(); auto Expansions = FileCoverage.getExpansions(); auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(), ViewOpts, std::move(FileCoverage)); attachExpansionSubViews(*View, Expansions, Coverage); + attachBranchSubViews(*View, SourceFile, Branches, SourceBuffer.get(), + FileCoverage); if (!ViewOpts.ShowFunctionInstantiations) return View; @@ -326,9 +368,12 @@ if (Function->ExecutionCount > 0) { auto SubViewCoverage = Coverage.getCoverageForFunction(*Function); auto SubViewExpansions = SubViewCoverage.getExpansions(); + auto SubViewBranches = SubViewCoverage.getBranches(); SubView = SourceCoverageView::create( Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage)); attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); + attachBranchSubViews(*SubView, SourceFile, SubViewBranches, + SourceBuffer.get(), SubViewCoverage); } unsigned FileID = Function->CountedRegions.front().FileID; @@ -645,6 +690,11 @@ cl::desc("Show region statistics in summary table"), cl::init(true)); + cl::opt BranchSummary( + "show-branch-summary", cl::Optional, + cl::desc("Show branch condition statistics in summary table"), + cl::init(true)); + cl::opt InstantiationSummary( "show-instantiation-summary", cl::Optional, cl::desc("Show instantiation statistics in summary table")); @@ -788,6 +838,7 @@ ::exit(0); } + ViewOpts.ShowBranchSummary = BranchSummary; ViewOpts.ShowRegionSummary = RegionSummary; ViewOpts.ShowInstantiationSummary = InstantiationSummary; ViewOpts.ExportSummaryOnly = SummaryOnly; @@ -822,6 +873,15 @@ cl::desc("Show the execution counts for each region"), cl::cat(ViewCategory)); + cl::opt ShowBranches( + "show-branches", cl::Optional, + cl::desc("Show coverage for branch conditions"), cl::cat(ViewCategory), + cl::values(clEnumValN(CoverageViewOptions::BranchOutputType::Count, + "count", "Show True/False counts"), + clEnumValN(CoverageViewOptions::BranchOutputType::Percent, + "percent", "Show True/False percent")), + cl::init(CoverageViewOptions::BranchOutputType::Off)); + cl::opt ShowBestLineRegionsCounts( "show-line-counts-or-regions", cl::Optional, cl::desc("Show the execution counts for each line, or the execution " @@ -865,6 +925,10 @@ !ShowRegions || ShowBestLineRegionsCounts; ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; ViewOpts.ShowExpandedRegions = ShowExpansions; + ViewOpts.ShowBranchCounts = + ShowBranches == CoverageViewOptions::BranchOutputType::Count; + ViewOpts.ShowBranchPercents = + ShowBranches == CoverageViewOptions::BranchOutputType::Percent; ViewOpts.ShowFunctionInstantiations = ShowInstantiations; ViewOpts.ShowOutputDirectory = ShowOutputDirectory; ViewOpts.TabSize = TabSize; diff --git a/llvm/tools/llvm-cov/CoverageExporterJson.cpp b/llvm/tools/llvm-cov/CoverageExporterJson.cpp --- a/llvm/tools/llvm-cov/CoverageExporterJson.cpp +++ b/llvm/tools/llvm-cov/CoverageExporterJson.cpp @@ -18,6 +18,8 @@ // -- Export: dict => Json representation of one CoverageMapping // -- Files: array => List of objects describing coverage for files // -- File: dict => Coverage for a single file +// -- Branches: array => List of Branches in the file +// -- Branch: dict => Describes a branch of the file with counters // -- Segments: array => List of Segments contained in the file // -- Segment: dict => Describes a segment of the file with a counter // -- Expansions: array => List of expansion records @@ -25,10 +27,13 @@ // -- CountedRegion: dict => The region to be expanded // -- TargetRegions: array => List of Regions in the expansion // -- CountedRegion: dict => Single Region in the expansion +// -- Branches: array => List of Branches in the expansion +// -- Branch: dict => Describes a branch in expansion and counters // -- Summary: dict => Object summarizing the coverage for this file // -- LineCoverage: dict => Object summarizing line coverage // -- FunctionCoverage: dict => Object summarizing function coverage // -- RegionCoverage: dict => Object summarizing region coverage +// -- BranchCoverage: dict => Object summarizing branch coverage // -- Functions: array => List of objects describing coverage for functions // -- Function: dict => Coverage info for a single function // -- Filenames: array => List of filenames that the function relates to @@ -37,6 +42,7 @@ // -- FunctionCoverage: dict => Object summarizing function coverage // -- InstantiationCoverage: dict => Object summarizing inst. coverage // -- RegionCoverage: dict => Object summarizing region coverage +// -- BranchCoverage: dict => Object summarizing branch coverage // //===----------------------------------------------------------------------===// @@ -84,6 +90,14 @@ int64_t(Region.Kind)}); } +json::Array renderBranch(const coverage::CountedRegion &Region) { + return json::Array( + {Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd, + clamp_uint64_to_int64(Region.ExecutionCount), + clamp_uint64_to_int64(Region.FalseExecutionCount), Region.FileID, + Region.ExpandedFileID, int64_t(Region.Kind)}); +} + json::Array renderRegions(ArrayRef Regions) { json::Array RegionArray; for (const auto &Region : Regions) @@ -91,13 +105,49 @@ return RegionArray; } -json::Object renderExpansion(const coverage::ExpansionRecord &Expansion) { +json::Array renderBranchRegions(ArrayRef Regions) { + json::Array RegionArray; + for (const auto &Region : Regions) + if (!Region.Folded) + RegionArray.push_back(renderBranch(Region)); + return RegionArray; +} + +std::vector +collectNestedBranches(const coverage::CoverageMapping &Coverage, + ArrayRef Expansions) { + std::vector Branches; + for (const auto &Expansion : Expansions) { + auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion); + + // Recursively collect branches from nested expansions. + auto NestedExpansions = ExpansionCoverage.getExpansions(); + auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions); + Branches.insert(Branches.end(), NestedExBranches.begin(), + NestedExBranches.end()); + + // Add branches from this level of expansion. + auto ExBranches = ExpansionCoverage.getBranches(); + for (auto B : ExBranches) + if (B.FileID == Expansion.FileID) + Branches.push_back(B); + } + + return Branches; +} + +json::Object renderExpansion(const coverage::CoverageMapping &Coverage, + const coverage::ExpansionRecord &Expansion) { + std::vector Expansions = {Expansion}; return json::Object( {{"filenames", json::Array(Expansion.Function.Filenames)}, // Mark the beginning and end of this expansion in the source file. {"source_region", renderRegion(Expansion.Region)}, // Enumerate the coverage information for the expansion. - {"target_regions", renderRegions(Expansion.Function.CountedRegions)}}); + {"target_regions", renderRegions(Expansion.Function.CountedRegions)}, + // Enumerate the branch coverage information for the expansion. + {"branches", + renderBranchRegions(collectNestedBranches(Coverage, Expansions))}}); } json::Object renderSummary(const FileCoverageSummary &Summary) { @@ -123,14 +173,22 @@ {"covered", int64_t(Summary.RegionCoverage.getCovered())}, {"notcovered", int64_t(Summary.RegionCoverage.getNumRegions() - Summary.RegionCoverage.getCovered())}, - {"percent", Summary.RegionCoverage.getPercentCovered()}})}}); + {"percent", Summary.RegionCoverage.getPercentCovered()}})}, + {"branches", + json::Object( + {{"count", int64_t(Summary.BranchCoverage.getNumBranches())}, + {"covered", int64_t(Summary.BranchCoverage.getCovered())}, + {"notcovered", int64_t(Summary.BranchCoverage.getNumBranches() - + Summary.BranchCoverage.getCovered())}, + {"percent", Summary.BranchCoverage.getPercentCovered()}})}}); } -json::Array renderFileExpansions(const coverage::CoverageData &FileCoverage, +json::Array renderFileExpansions(const coverage::CoverageMapping &Coverage, + const coverage::CoverageData &FileCoverage, const FileCoverageSummary &FileReport) { json::Array ExpansionArray; for (const auto &Expansion : FileCoverage.getExpansions()) - ExpansionArray.push_back(renderExpansion(Expansion)); + ExpansionArray.push_back(renderExpansion(Coverage, Expansion)); return ExpansionArray; } @@ -142,6 +200,14 @@ return SegmentArray; } +json::Array renderFileBranches(const coverage::CoverageData &FileCoverage, + const FileCoverageSummary &FileReport) { + json::Array BranchArray; + for (const auto &Branch : FileCoverage.getBranches()) + BranchArray.push_back(renderBranch(Branch)); + return BranchArray; +} + json::Object renderFile(const coverage::CoverageMapping &Coverage, const std::string &Filename, const FileCoverageSummary &FileReport, @@ -151,8 +217,10 @@ // Calculate and render detailed coverage information for given file. auto FileCoverage = Coverage.getCoverageForFile(Filename); File["segments"] = renderFileSegments(FileCoverage, FileReport); + File["branches"] = renderFileBranches(FileCoverage, FileReport); if (!Options.SkipExpansions) { - File["expansions"] = renderFileExpansions(FileCoverage, FileReport); + File["expansions"] = + renderFileExpansions(Coverage, FileCoverage, FileReport); } } File["summary"] = renderSummary(FileReport); @@ -197,6 +265,7 @@ json::Object({{"name", F.Name}, {"count", clamp_uint64_to_int64(F.ExecutionCount)}, {"regions", renderRegions(F.CountedRegions)}, + {"branches", renderBranchRegions(F.CountedBranchRegions)}, {"filenames", json::Array(F.Filenames)}})); return FunctionArray; } diff --git a/llvm/tools/llvm-cov/CoverageExporterLcov.cpp b/llvm/tools/llvm-cov/CoverageExporterLcov.cpp --- a/llvm/tools/llvm-cov/CoverageExporterLcov.cpp +++ b/llvm/tools/llvm-cov/CoverageExporterLcov.cpp @@ -26,6 +26,10 @@ // - "FNH:" // - for each instrumented line: // - "DA:,[,] +// - for each branch: +// - "BRDA:,,," +// - "BRF:" +// - "BRH:" // - "LH:" // - "LF:" // - "end_of_record" @@ -71,11 +75,102 @@ } } +std::vector +collectNestedBranches(const coverage::CoverageMapping &Coverage, + ArrayRef Expansions, + int ViewDepth = 0, int SrcLine = 0) { + std::vector Branches; + for (const auto &Expansion : Expansions) { + auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion); + + // If we're at the top level, set the corresponding source line. + if (ViewDepth == 0) + SrcLine = Expansion.Region.LineStart; + + // Recursively collect branches from nested expansions. + auto NestedExpansions = ExpansionCoverage.getExpansions(); + auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions, + ViewDepth + 1, SrcLine); + Branches.insert(Branches.end(), NestedExBranches.begin(), + NestedExBranches.end()); + + // Add branches from this level of expansion. + auto ExBranches = ExpansionCoverage.getBranches(); + for (auto B : ExBranches) + if (B.FileID == Expansion.FileID) { + B.LineStart = SrcLine; + Branches.push_back(B); + } + } + + return Branches; +} + +bool sortLine(llvm::coverage::CountedRegion I, + llvm::coverage::CountedRegion J) { + return (I.LineStart < J.LineStart) || + ((I.LineStart == J.LineStart) && (I.ColumnStart < J.ColumnStart)); +} + +void renderBranchExecutionCounts(raw_ostream &OS, + const coverage::CoverageMapping &Coverage, + const coverage::CoverageData &FileCoverage) { + std::vector Branches = + FileCoverage.getBranches(); + + // Recursively collect branches for all file expansions. + std::vector ExBranches = + collectNestedBranches(Coverage, FileCoverage.getExpansions()); + + // Append Expansion Branches to Source Branches. + Branches.insert(Branches.end(), ExBranches.begin(), ExBranches.end()); + + // Sort branches based on line number to ensure branches corresponding to the + // same source line are counted together. + std::sort(Branches.begin(), Branches.end(), sortLine); + + auto NextBranch = Branches.begin(); + auto EndBranch = Branches.end(); + + // Branches with the same source line are enumerated individually + // (BranchIndex) as well as based on True/False pairs (PairIndex). + while (NextBranch != EndBranch) { + unsigned CurrentLine = NextBranch->LineStart; + unsigned PairIndex = 0; + unsigned BranchIndex = 0; + + while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart) { + if (!NextBranch->Folded) { + unsigned BC1 = NextBranch->ExecutionCount; + unsigned BC2 = NextBranch->FalseExecutionCount; + bool BranchNotExecuted = (BC1 == 0 && BC2 == 0); + + for (int I = 0; I < 2; I++, BranchIndex++) { + OS << "BRDA:" << CurrentLine << ',' << PairIndex << ',' + << BranchIndex; + if (BranchNotExecuted) + OS << ',' << '-' << '\n'; + else + OS << ',' << (I == 0 ? BC1 : BC2) << '\n'; + } + + PairIndex++; + } + NextBranch++; + } + } +} + void renderLineSummary(raw_ostream &OS, const FileCoverageSummary &Summary) { OS << "LF:" << Summary.LineCoverage.getNumLines() << '\n' << "LH:" << Summary.LineCoverage.getCovered() << '\n'; } +void renderBranchSummary(raw_ostream &OS, const FileCoverageSummary &Summary) { + OS << "BRF:" << Summary.BranchCoverage.getNumBranches() << '\n' + << "BFH:" << Summary.BranchCoverage.getCovered() << '\n'; +} + void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage, const std::string &Filename, const FileCoverageSummary &FileReport, bool ExportSummaryOnly, @@ -91,7 +186,9 @@ // Calculate and render detailed coverage information for given file. auto FileCoverage = Coverage.getCoverageForFile(Filename); renderLineExecutionCounts(OS, FileCoverage); + renderBranchExecutionCounts(OS, Coverage, FileCoverage); } + renderBranchSummary(OS, FileReport); renderLineSummary(OS, FileReport); OS << "end_of_record\n"; diff --git a/llvm/tools/llvm-cov/CoverageReport.cpp b/llvm/tools/llvm-cov/CoverageReport.cpp --- a/llvm/tools/llvm-cov/CoverageReport.cpp +++ b/llvm/tools/llvm-cov/CoverageReport.cpp @@ -86,9 +86,9 @@ } // Specify the default column widths. -size_t FileReportColumns[] = {25, 12, 18, 10, 12, 18, 10, - 16, 16, 10, 12, 18, 10}; -size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8}; +size_t FileReportColumns[] = {25, 12, 18, 10, 12, 18, 10, 16, + 16, 10, 12, 18, 10, 12, 18, 10}; +size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8, 10, 8, 8}; /// Adjust column widths to fit long file paths and function names. void adjustColumnWidths(ArrayRef Files, @@ -239,6 +239,23 @@ << '%'; else OS << column("-", FileReportColumns[12], Column::RightAlignment); + + if (Options.ShowBranchSummary) { + OS << format("%*u", FileReportColumns[13], + (unsigned)File.BranchCoverage.getNumBranches()); + Options.colored_ostream(OS, LineCoverageColor) + << format("%*u", FileReportColumns[14], + (unsigned)(File.BranchCoverage.getNumBranches() - + File.BranchCoverage.getCovered())); + if (File.BranchCoverage.getNumBranches()) + Options.colored_ostream(OS, LineCoverageColor) + << format("%*.2f", FileReportColumns[15] - 1, + File.BranchCoverage.getPercentCovered()) + << '%'; + else + OS << column("-", FileReportColumns[15], Column::RightAlignment); + } + OS << "\n"; } @@ -273,6 +290,19 @@ << format("%*.2f", FunctionReportColumns[6] - 1, Function.LineCoverage.getPercentCovered()) << '%'; + if (Options.ShowBranchSummary) { + OS << format("%*u", FunctionReportColumns[7], + (unsigned)Function.BranchCoverage.getNumBranches()); + Options.colored_ostream(OS, LineCoverageColor) + << format("%*u", FunctionReportColumns[8], + (unsigned)(Function.BranchCoverage.getNumBranches() - + Function.BranchCoverage.getCovered())); + Options.colored_ostream( + OS, determineCoveragePercentageColor(Function.BranchCoverage)) + << format("%*.2f", FunctionReportColumns[9] - 1, + Function.BranchCoverage.getPercentCovered()) + << '%'; + } OS << "\n"; } @@ -301,6 +331,10 @@ << column("Lines", FunctionReportColumns[4], Column::RightAlignment) << column("Miss", FunctionReportColumns[5], Column::RightAlignment) << column("Cover", FunctionReportColumns[6], Column::RightAlignment); + if (Options.ShowBranchSummary) + OS << column("Branches", FunctionReportColumns[7], Column::RightAlignment) + << column("Miss", FunctionReportColumns[8], Column::RightAlignment) + << column("Cover", FunctionReportColumns[9], Column::RightAlignment); OS << "\n"; renderDivider(FunctionReportColumns, OS); OS << "\n"; @@ -310,6 +344,7 @@ ++Totals.ExecutionCount; Totals.RegionCoverage += Function.RegionCoverage; Totals.LineCoverage += Function.LineCoverage; + Totals.BranchCoverage += Function.BranchCoverage; render(Function, DC, OS); } if (Totals.ExecutionCount) { @@ -420,7 +455,13 @@ << column("Executed", FileReportColumns[9], Column::RightAlignment); OS << column("Lines", FileReportColumns[10], Column::RightAlignment) << column("Missed Lines", FileReportColumns[11], Column::RightAlignment) - << column("Cover", FileReportColumns[12], Column::RightAlignment) << "\n"; + << column("Cover", FileReportColumns[12], Column::RightAlignment); + if (Options.ShowBranchSummary) + OS << column("Branches", FileReportColumns[13], Column::RightAlignment) + << column("Missed Branches", FileReportColumns[14], + Column::RightAlignment) + << column("Cover", FileReportColumns[15], Column::RightAlignment); + OS << "\n"; renderDivider(FileReportColumns, OS); OS << "\n"; diff --git a/llvm/tools/llvm-cov/CoverageSummaryInfo.h b/llvm/tools/llvm-cov/CoverageSummaryInfo.h --- a/llvm/tools/llvm-cov/CoverageSummaryInfo.h +++ b/llvm/tools/llvm-cov/CoverageSummaryInfo.h @@ -101,6 +101,42 @@ } }; +/// Provides information about branches coverage for a function/file. +class BranchCoverageInfo { + /// The number of branches that were executed at least once. + size_t Covered; + + /// The total number of branches in a function/file. + size_t NumBranches; + +public: + BranchCoverageInfo() : Covered(0), NumBranches(0) {} + + BranchCoverageInfo(size_t Covered, size_t NumBranches) + : Covered(Covered), NumBranches(NumBranches) { + assert(Covered <= NumBranches && "Covered branches over-counted"); + } + + BranchCoverageInfo &operator+=(const BranchCoverageInfo &RHS) { + Covered += RHS.Covered; + NumBranches += RHS.NumBranches; + return *this; + } + + size_t getCovered() const { return Covered; } + + size_t getNumBranches() const { return NumBranches; } + + bool isFullyCovered() const { return Covered == NumBranches; } + + double getPercentCovered() const { + assert(Covered <= NumBranches && "Covered branches over-counted"); + if (NumBranches == 0) + return 0.0; + return double(Covered) / double(NumBranches) * 100.0; + } +}; + /// Provides information about function coverage for a file. class FunctionCoverageInfo { /// The number of functions that were executed. @@ -147,15 +183,19 @@ uint64_t ExecutionCount; RegionCoverageInfo RegionCoverage; LineCoverageInfo LineCoverage; + BranchCoverageInfo BranchCoverage; FunctionCoverageSummary(const std::string &Name) - : Name(Name), ExecutionCount(0), RegionCoverage(), LineCoverage() {} + : Name(Name), ExecutionCount(0), RegionCoverage(), LineCoverage(), + BranchCoverage() {} FunctionCoverageSummary(const std::string &Name, uint64_t ExecutionCount, const RegionCoverageInfo &RegionCoverage, - const LineCoverageInfo &LineCoverage) + const LineCoverageInfo &LineCoverage, + const BranchCoverageInfo &BranchCoverage) : Name(Name), ExecutionCount(ExecutionCount), - RegionCoverage(RegionCoverage), LineCoverage(LineCoverage) {} + RegionCoverage(RegionCoverage), LineCoverage(LineCoverage), + BranchCoverage(BranchCoverage) {} /// Compute the code coverage summary for the given function coverage /// mapping record. @@ -174,6 +214,7 @@ StringRef Name; RegionCoverageInfo RegionCoverage; LineCoverageInfo LineCoverage; + BranchCoverageInfo BranchCoverage; FunctionCoverageInfo FunctionCoverage; FunctionCoverageInfo InstantiationCoverage; @@ -185,6 +226,7 @@ RegionCoverage += RHS.RegionCoverage; LineCoverage += RHS.LineCoverage; FunctionCoverage += RHS.FunctionCoverage; + BranchCoverage += RHS.BranchCoverage; InstantiationCoverage += RHS.InstantiationCoverage; return *this; } @@ -192,6 +234,7 @@ void addFunction(const FunctionCoverageSummary &Function) { RegionCoverage += Function.RegionCoverage; LineCoverage += Function.LineCoverage; + BranchCoverage += Function.BranchCoverage; FunctionCoverage.addFunction(/*Covered=*/Function.ExecutionCount > 0); } diff --git a/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp b/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp --- a/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp +++ b/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp @@ -16,6 +16,34 @@ using namespace llvm; using namespace coverage; +static void sumBranches(size_t &NumBranches, size_t &CoveredBranches, + const ArrayRef &Branches) { + for (const auto &BR : Branches) { + // Skip folded branches. + if (BR.Folded) + continue; + + // "True" Condition Branches. + ++NumBranches; + if (BR.ExecutionCount > 0) + ++CoveredBranches; + // "False" Condition Branches. + ++NumBranches; + if (BR.FalseExecutionCount > 0) + ++CoveredBranches; + } +} + +static void sumBranchExpansions(size_t &NumBranches, size_t &CoveredBranches, + const CoverageMapping &CM, + ArrayRef Expansions) { + for (const auto &Expansion : Expansions) { + auto CE = CM.getCoverageForExpansion(Expansion); + sumBranches(NumBranches, CoveredBranches, CE.getBranches()); + sumBranchExpansions(NumBranches, CoveredBranches, CM, CE.getExpansions()); + } +} + FunctionCoverageSummary FunctionCoverageSummary::get(const CoverageMapping &CM, const coverage::FunctionRecord &Function) { @@ -40,10 +68,16 @@ ++CoveredLines; } + // Compute the branch coverage, including branches from expansions. + size_t NumBranches = 0, CoveredBranches = 0; + sumBranches(NumBranches, CoveredBranches, CD.getBranches()); + sumBranchExpansions(NumBranches, CoveredBranches, CM, CD.getExpansions()); + return FunctionCoverageSummary( Function.Name, Function.ExecutionCount, RegionCoverageInfo(CoveredRegions, NumCodeRegions), - LineCoverageInfo(CoveredLines, NumLines)); + LineCoverageInfo(CoveredLines, NumLines), + BranchCoverageInfo(CoveredBranches, NumBranches)); } FunctionCoverageSummary @@ -62,9 +96,15 @@ Summary.ExecutionCount = Group.getTotalExecutionCount(); Summary.RegionCoverage = Summaries[0].RegionCoverage; Summary.LineCoverage = Summaries[0].LineCoverage; + Summary.BranchCoverage = Summaries[0].BranchCoverage; for (const auto &FCS : Summaries.drop_front()) { Summary.RegionCoverage.merge(FCS.RegionCoverage); Summary.LineCoverage.merge(FCS.LineCoverage); + + // Sum branch coverage across instantiation groups for the summary rather + // than "merge" the maximum count. This is a clearer view into whether all + // created branches are covered. + Summary.BranchCoverage += FCS.BranchCoverage; } return Summary; } diff --git a/llvm/tools/llvm-cov/CoverageViewOptions.h b/llvm/tools/llvm-cov/CoverageViewOptions.h --- a/llvm/tools/llvm-cov/CoverageViewOptions.h +++ b/llvm/tools/llvm-cov/CoverageViewOptions.h @@ -23,20 +23,26 @@ Lcov }; + enum class BranchOutputType { Count, Percent, Off }; + bool Debug; bool Colors; bool ShowLineNumbers; bool ShowLineStats; bool ShowRegionMarkers; + bool ShowBranchCounts; + bool ShowBranchPercents; bool ShowExpandedRegions; bool ShowFunctionInstantiations; bool ShowFullFilenames; + bool ShowBranchSummary; bool ShowRegionSummary; bool ShowInstantiationSummary; bool ExportSummaryOnly; bool SkipExpansions; bool SkipFunctions; OutputFormat Format; + BranchOutputType ShowBranches; std::string ShowOutputDirectory; std::vector DemanglerOpts; uint32_t TabSize; diff --git a/llvm/tools/llvm-cov/SourceCoverageView.h b/llvm/tools/llvm-cov/SourceCoverageView.h --- a/llvm/tools/llvm-cov/SourceCoverageView.h +++ b/llvm/tools/llvm-cov/SourceCoverageView.h @@ -67,6 +67,23 @@ } }; +/// A view that represents one or more branch regions on a given source line. +struct BranchView { + std::vector Regions; + std::unique_ptr View; + unsigned Line; + + BranchView(unsigned Line, ArrayRef Regions, + std::unique_ptr View) + : Regions(Regions), View(std::move(View)), Line(Line) {} + + unsigned getLine() const { return Line; } + + friend bool operator<(const BranchView &LHS, const BranchView &RHS) { + return LHS.Line < RHS.Line; + } +}; + /// A file manager that handles format-aware file creation. class CoveragePrinter { public: @@ -140,6 +157,9 @@ /// A container for all expansions (e.g macros) in the source on display. std::vector ExpansionSubViews; + /// A container for all branches in the source on display. + std::vector BranchSubViews; + /// A container for all instantiations (e.g template functions) in the source /// on display. std::vector InstantiationSubViews; @@ -209,6 +229,10 @@ virtual void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV, unsigned ViewDepth) = 0; + /// Render a branch view and any nested views. + virtual void renderBranchView(raw_ostream &OS, BranchView &BRV, + unsigned ViewDepth) = 0; + /// Render \p Title, a project title if one is available, and the /// created time. virtual void renderTitle(raw_ostream &OS, StringRef CellText) = 0; @@ -255,6 +279,10 @@ void addInstantiation(StringRef FunctionName, unsigned Line, std::unique_ptr View); + /// Add a branch subview to this view. + void addBranch(unsigned Line, ArrayRef Regions, + std::unique_ptr View); + /// Print the code coverage information for a specific portion of a /// source file to the output stream. void print(raw_ostream &OS, bool WholeFile, bool ShowSourceName, diff --git a/llvm/tools/llvm-cov/SourceCoverageView.cpp b/llvm/tools/llvm-cov/SourceCoverageView.cpp --- a/llvm/tools/llvm-cov/SourceCoverageView.cpp +++ b/llvm/tools/llvm-cov/SourceCoverageView.cpp @@ -132,7 +132,8 @@ } bool SourceCoverageView::hasSubViews() const { - return !ExpansionSubViews.empty() || !InstantiationSubViews.empty(); + return !ExpansionSubViews.empty() || !InstantiationSubViews.empty() || + !BranchSubViews.empty(); } std::unique_ptr @@ -167,6 +168,12 @@ ExpansionSubViews.emplace_back(Region, std::move(View)); } +void SourceCoverageView::addBranch(unsigned Line, + ArrayRef Regions, + std::unique_ptr View) { + BranchSubViews.emplace_back(Line, Regions, std::move(View)); +} + void SourceCoverageView::addInstantiation( StringRef FunctionName, unsigned Line, std::unique_ptr View) { @@ -187,14 +194,17 @@ renderTableHeader(OS, (ViewDepth > 0) ? 0 : getFirstUncoveredLineNo(), ViewDepth); - // We need the expansions and instantiations sorted so we can go through them - // while we iterate lines. + // We need the expansions, instantiations, and branches sorted so we can go + // through them while we iterate lines. llvm::stable_sort(ExpansionSubViews); llvm::stable_sort(InstantiationSubViews); + llvm::stable_sort(BranchSubViews); auto NextESV = ExpansionSubViews.begin(); auto EndESV = ExpansionSubViews.end(); auto NextISV = InstantiationSubViews.begin(); auto EndISV = InstantiationSubViews.end(); + auto NextBRV = BranchSubViews.begin(); + auto EndBRV = BranchSubViews.end(); // Get the coverage information for the file. auto StartSegment = CoverageInfo.begin(); @@ -234,7 +244,7 @@ if (shouldRenderRegionMarkers(*LCI)) renderRegionMarkers(OS, *LCI, ViewDepth); - // Show the expansions and instantiations for this line. + // Show the expansions, instantiations, and branches for this line. bool RenderedSubView = false; for (; NextESV != EndESV && NextESV->getLine() == LI.line_number(); ++NextESV) { @@ -257,6 +267,11 @@ renderInstantiationView(OS, *NextISV, ViewDepth + 1); RenderedSubView = true; } + for (; NextBRV != EndBRV && NextBRV->Line == LI.line_number(); ++NextBRV) { + renderViewDivider(OS, ViewDepth + 1); + renderBranchView(OS, *NextBRV, ViewDepth + 1); + RenderedSubView = true; + } if (RenderedSubView) renderViewDivider(OS, ViewDepth + 1); renderLineSuffix(OS, ViewDepth); diff --git a/llvm/tools/llvm-cov/SourceCoverageViewHTML.h b/llvm/tools/llvm-cov/SourceCoverageViewHTML.h --- a/llvm/tools/llvm-cov/SourceCoverageViewHTML.h +++ b/llvm/tools/llvm-cov/SourceCoverageViewHTML.h @@ -68,6 +68,9 @@ void renderExpansionView(raw_ostream &OS, ExpansionView &ESV, unsigned ViewDepth) override; + void renderBranchView(raw_ostream &OS, BranchView &BRV, + unsigned ViewDepth) override; + void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV, unsigned ViewDepth) override; diff --git a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp --- a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp +++ b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp @@ -313,6 +313,8 @@ Columns.emplace_back(tag("td", "Line Coverage", "column-entry-bold")); if (Opts.ShowRegionSummary) Columns.emplace_back(tag("td", "Region Coverage", "column-entry-bold")); + if (Opts.ShowBranchSummary) + Columns.emplace_back(tag("td", "Branch Coverage", "column-entry-bold")); OS << tag("tr", join(Columns.begin(), Columns.end(), "")); } @@ -378,6 +380,10 @@ AddCoverageTripleToColumn(FCS.RegionCoverage.getCovered(), FCS.RegionCoverage.getNumRegions(), FCS.RegionCoverage.getPercentCovered()); + if (Opts.ShowBranchSummary) + AddCoverageTripleToColumn(FCS.BranchCoverage.getCovered(), + FCS.BranchCoverage.getNumBranches(), + FCS.BranchCoverage.getPercentCovered()); if (IsTotals) OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row-bold"); @@ -650,6 +656,72 @@ OS << EndExpansionDiv; } +void SourceCoverageViewHTML::renderBranchView(raw_ostream &OS, BranchView &BRV, + unsigned ViewDepth) { + // Render the child subview. + if (getOptions().Debug) + errs() << "Branch at line " << BRV.getLine() << '\n'; + + OS << BeginExpansionDiv; + OS << BeginPre; + for (const auto &R : BRV.Regions) { + // Calculate TruePercent and False Percent. + double TruePercent = 0.0; + double FalsePercent = 0.0; + unsigned Total = R.ExecutionCount + R.FalseExecutionCount; + + if (!getOptions().ShowBranchCounts && Total != 0) { + TruePercent = ((double)(R.ExecutionCount) / (double)Total) * 100.0; + FalsePercent = ((double)(R.FalseExecutionCount) / (double)Total) * 100.0; + } + + // Display Line + Column. + std::string LineNoStr = utostr(uint64_t(R.LineStart)); + std::string ColNoStr = utostr(uint64_t(R.ColumnStart)); + std::string TargetName = "L" + LineNoStr; + + OS << " Branch ("; + OS << tag("span", + a("#" + TargetName, tag("span", LineNoStr + ":" + ColNoStr), + TargetName), + "line-number") + + "): ["; + + if (R.Folded) { + OS << "Folded - Ignored]\n"; + continue; + } + + // Display TrueCount or TruePercent. + std::string TrueColor = R.ExecutionCount ? "None" : "red"; + std::string TrueCovClass = + (R.ExecutionCount > 0) ? "covered-line" : "uncovered-line"; + + OS << tag("span", "True", TrueColor); + OS << ": "; + if (getOptions().ShowBranchCounts) + OS << tag("span", formatCount(R.ExecutionCount), TrueCovClass) << ", "; + else + OS << format("%0.2f", TruePercent) << "%, "; + + // Display FalseCount or FalsePercent. + std::string FalseColor = R.FalseExecutionCount ? "None" : "red"; + std::string FalseCovClass = + (R.FalseExecutionCount > 0) ? "covered-line" : "uncovered-line"; + + OS << tag("span", "False", FalseColor); + OS << ": "; + if (getOptions().ShowBranchCounts) + OS << tag("span", formatCount(R.FalseExecutionCount), FalseCovClass); + else + OS << format("%0.2f", FalsePercent) << "%"; + + OS << "]\n"; + } + OS << EndPre; + OS << EndExpansionDiv; +} + void SourceCoverageViewHTML::renderInstantiationView(raw_ostream &OS, InstantiationView &ISV, unsigned ViewDepth) { diff --git a/llvm/tools/llvm-cov/SourceCoverageViewText.h b/llvm/tools/llvm-cov/SourceCoverageViewText.h --- a/llvm/tools/llvm-cov/SourceCoverageViewText.h +++ b/llvm/tools/llvm-cov/SourceCoverageViewText.h @@ -59,6 +59,9 @@ void renderExpansionView(raw_ostream &OS, ExpansionView &ESV, unsigned ViewDepth) override; + void renderBranchView(raw_ostream &OS, BranchView &BRV, + unsigned ViewDepth) override; + void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV, unsigned ViewDepth) override; diff --git a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp --- a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp +++ b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp @@ -10,11 +10,12 @@ /// //===----------------------------------------------------------------------===// -#include "CoverageReport.h" #include "SourceCoverageViewText.h" +#include "CoverageReport.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Format.h" using namespace llvm; @@ -222,6 +223,53 @@ /*ShowTitle=*/false, ViewDepth + 1); } +void SourceCoverageViewText::renderBranchView(raw_ostream &OS, BranchView &BRV, + unsigned ViewDepth) { + // Render the child subview. + if (getOptions().Debug) + errs() << "Branch at line " << BRV.getLine() << '\n'; + + for (const auto &R : BRV.Regions) { + double TruePercent = 0.0; + double FalsePercent = 0.0; + unsigned Total = R.ExecutionCount + R.FalseExecutionCount; + + if (!getOptions().ShowBranchCounts && Total != 0) { + TruePercent = ((double)(R.ExecutionCount) / (double)Total) * 100.0; + FalsePercent = ((double)(R.FalseExecutionCount) / (double)Total) * 100.0; + } + + renderLinePrefix(OS, ViewDepth); + OS << " Branch (" << R.LineStart << ":" << R.ColumnStart << "): ["; + + if (R.Folded) { + OS << "Folded - Ignored]\n"; + continue; + } + + colored_ostream(OS, raw_ostream::RED, + getOptions().Colors && !R.ExecutionCount, + /*Bold=*/false, /*BG=*/true) + << "True"; + + if (getOptions().ShowBranchCounts) + OS << ": " << formatCount(R.ExecutionCount) << ", "; + else + OS << ": " << format("%0.2f", TruePercent) << "%, "; + + colored_ostream(OS, raw_ostream::RED, + getOptions().Colors && !R.FalseExecutionCount, + /*Bold=*/false, /*BG=*/true) + << "False"; + + if (getOptions().ShowBranchCounts) + OS << ": " << formatCount(R.FalseExecutionCount); + else + OS << ": " << format("%0.2f", FalsePercent) << "%"; + OS << "]\n"; + } +} + void SourceCoverageViewText::renderInstantiationView(raw_ostream &OS, InstantiationView &ISV, unsigned ViewDepth) {
FilenameFunction CoverageLine CoverageRegion CoverageBranch Coverage +// HTML-INDEX: 100.00% (12/12) +// HTML-INDEX: +// HTML-INDEX: 94.44% (221/234) +// HTML-INDEX: +// HTML-INDEX: 87.82% (173/197) +// HTML-INDEX: +// HTML-INDEX: 78.16% (136/174) +// HTML-INDEX: