Index: clang/lib/CodeGen/CGExprScalar.cpp =================================================================== --- clang/lib/CodeGen/CGExprScalar.cpp +++ clang/lib/CodeGen/CGExprScalar.cpp @@ -4223,6 +4223,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. @@ -4232,6 +4233,16 @@ if (LHSCondVal) { // If we have 1 && X, just emit X. CGF.incrementProfileCounter(E); + // 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 the final "TrueBlock" and + // "FalseBlock" after the increment is done. + if (InstrumentRegions && CodeGenFunction::isLeafCondition(E->getRHS())) { + llvm::BasicBlock *FBlock = CGF.createBasicBlock("land.end"); + CGF.EmitBranchToCounterBlock(E->getRHS(), BO_LAnd, FBlock, FBlock); + CGF.EmitBlock(FBlock); + } + Value *RHSCond = CGF.EvaluateExprAsBool(E->getRHS()); // ZExt result to int or bool. return Builder.CreateZExtOrBitCast(RHSCond, ResTy, "land.ext"); @@ -4269,6 +4280,18 @@ // 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::isLeafCondition(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. @@ -4309,6 +4332,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. @@ -4318,6 +4342,16 @@ if (!LHSCondVal) { // If we have 0 || X, just emit X. CGF.incrementProfileCounter(E); + // 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 the final "TrueBlock" and + // "FalseBlock" after the increment is done. + if (InstrumentRegions && CodeGenFunction::isLeafCondition(E->getRHS())) { + llvm::BasicBlock *FBlock = CGF.createBasicBlock("lor.end"); + CGF.EmitBranchToCounterBlock(E->getRHS(), BO_LOr, FBlock, FBlock); + CGF.EmitBlock(FBlock); + } + Value *RHSCond = CGF.EvaluateExprAsBool(E->getRHS()); // ZExt result to int or bool. return Builder.CreateZExtOrBitCast(RHSCond, ResTy, "lor.ext"); @@ -4353,12 +4387,23 @@ CGF.EmitBlock(RHSBlock); CGF.incrementProfileCounter(E); Value *RHSCond = CGF.EvaluateExprAsBool(E->getRHS()); - eval.end(CGF); // 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::isLeafCondition(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); Index: clang/lib/CodeGen/CGStmt.cpp =================================================================== --- clang/lib/CodeGen/CGStmt.cpp +++ clang/lib/CodeGen/CGStmt.cpp @@ -1365,7 +1365,7 @@ SwitchWeights->push_back(getProfileCount(NextCase)); if (CGM.getCodeGenOpts().hasProfileClangInstr()) { CaseDest = createBasicBlock("sw.bb"); - EmitBlockWithFallThrough(CaseDest, &S); + EmitBlockWithFallThrough(CaseDest, CurCase); } SwitchInsn->addCase(CaseVal, CaseDest); Index: clang/lib/CodeGen/CodeGenFunction.h =================================================================== --- clang/lib/CodeGen/CodeGenFunction.h +++ clang/lib/CodeGen/CodeGenFunction.h @@ -4353,6 +4353,21 @@ /// constant folds return true and set the folded value. bool ConstantFoldsToSimpleInteger(const Expr *Cond, llvm::APSInt &Result, bool AllowLabels = false); + + /// isLeafCondition - Determine whether the given condition is a leaf-level + /// condition (i.e. no "&&" or "||"). + static bool isLeafCondition(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, + const Expr *CntrIdx = NULL); /// EmitBranchOnBoolExpr - Emit a branch on a boolean condition (e.g. for an /// if statement) to the specified blocks. Based on the condition, this might Index: clang/lib/CodeGen/CodeGenFunction.cpp =================================================================== --- clang/lib/CodeGen/CodeGenFunction.cpp +++ clang/lib/CodeGen/CodeGenFunction.cpp @@ -1462,7 +1462,87 @@ return true; } +/// Determine whether the given condition is a leaf-level condition +/// (i.e. no "&&" or "||"). +bool CodeGenFunction::isLeafCondition(const Expr *C) { + 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 */, + const Expr *CntrIdx /*=NULL*/) +{ + // If not instrumenting or this isn't a leaf condition, just emit a branch + bool InstrumentRegions = CGM.getCodeGenOpts().hasProfileClangInstr(); + if (!InstrumentRegions || !isLeafCondition(Cond)) + return EmitBranchOnBoolExpr(Cond, TrueBlock, FalseBlock, TrueCount); + + 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 { + assert("Expected Opcode must be that of a Logical Operator"); + } + // Emit Branch based on condition + EmitBranchOnBoolExpr(Cond, ThenBlock, ElseBlock, TrueCount); + + // 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 @@ -1475,7 +1555,6 @@ Cond = Cond->IgnoreParens(); if (const BinaryOperator *CondBOp = dyn_cast(Cond)) { - // Handle X && Y in a condition. if (CondBOp->getOpcode() == BO_LAnd) { // If we have "1 && X", simplify the code. "0 && X" would have constant @@ -1485,8 +1564,8 @@ ConstantBool) { // br(1 && X) -> br(X). incrementProfileCounter(CondBOp); - return EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock, - TrueCount); + return EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LAnd, + TrueBlock, FalseBlock, TrueCount); } // If we have "X && 1", simplify the code to use an uncond branch. @@ -1494,8 +1573,8 @@ if (ConstantFoldsToSimpleInteger(CondBOp->getRHS(), ConstantBool) && ConstantBool) { // br(X && 1) -> br(X). - return EmitBranchOnBoolExpr(CondBOp->getLHS(), TrueBlock, FalseBlock, - TrueCount); + return EmitBranchToCounterBlock(CondBOp->getLHS(), BO_LAnd, TrueBlock, + FalseBlock, TrueCount, CondBOp); } // Emit the LHS as a conditional. If the LHS conditional is false, we @@ -1517,7 +1596,8 @@ // Any temporaries created here are conditional. eval.begin(*this); - EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock, TrueCount); + EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LAnd, TrueBlock, + FalseBlock, TrueCount); eval.end(*this); return; @@ -1531,8 +1611,8 @@ !ConstantBool) { // br(0 || X) -> br(X). incrementProfileCounter(CondBOp); - return EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock, - TrueCount); + return EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LOr, + TrueBlock, FalseBlock, TrueCount); } // If we have "X || 0", simplify the code to use an uncond branch. @@ -1540,8 +1620,8 @@ if (ConstantFoldsToSimpleInteger(CondBOp->getRHS(), ConstantBool) && !ConstantBool) { // br(X || 0) -> br(X). - return EmitBranchOnBoolExpr(CondBOp->getLHS(), TrueBlock, FalseBlock, - TrueCount); + return EmitBranchToCounterBlock(CondBOp->getLHS(), BO_LOr, TrueBlock, + FalseBlock, TrueCount, CondBOp); } // Emit the LHS as a conditional. If the LHS conditional is true, we @@ -1566,8 +1646,8 @@ // Any temporaries created here are conditional. eval.begin(*this); - EmitBranchOnBoolExpr(CondBOp->getRHS(), TrueBlock, FalseBlock, RHSCount); - + EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LOr, TrueBlock, FalseBlock, + RHSCount); eval.end(*this); return; Index: clang/lib/CodeGen/CodeGenPGO.cpp =================================================================== --- clang/lib/CodeGen/CodeGenPGO.cpp +++ 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,17 @@ 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::isLeafCondition(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); @@ -809,11 +823,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)) Index: clang/lib/CodeGen/CoverageMappingGen.h =================================================================== --- clang/lib/CodeGen/CoverageMappingGen.h +++ clang/lib/CodeGen/CoverageMappingGen.h @@ -87,6 +87,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 Index: clang/lib/CodeGen/CoverageMappingGen.cpp =================================================================== --- clang/lib/CodeGen/CoverageMappingGen.cpp +++ clang/lib/CodeGen/CoverageMappingGen.cpp @@ -43,8 +43,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; @@ -60,13 +64,28 @@ public: SourceMappingRegion(Counter Count, Optional LocStart, - Optional LocEnd, bool DeferRegion = false, + Optional LocEnd, + bool DeferRegion = false, bool GapRegion = false) : 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(); } @@ -97,6 +116,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. @@ -348,6 +369,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, @@ -485,13 +510,18 @@ /// /// 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) { + size_t pushRegion(Counter Count, + Optional StartLoc = None, + 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; } @@ -581,6 +611,7 @@ 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)) { @@ -589,11 +620,14 @@ if (UnnestEnd) { // The region ends in a nested file or macro expansion. Create a // separate region for each expansion. + // Don't do this for branch regions 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()) @@ -603,11 +637,14 @@ if (UnnestStart) { // The region begins in a nested file or macro expansion. Create a // separate region for each expansion. + // Don't do this for branch regions 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()) @@ -618,12 +655,14 @@ 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()); @@ -682,14 +721,62 @@ return ExitCount; } + /// Determine whether the given condition folds to true or false + bool ConditionFoldsToBool(const Expr *Cond) { + Expr::EvalResult Result; + return (Cond->EvaluateAsInt(Result, CVM.getCodeGenModule().getContext())); + } + + /// Create a Branch Region around a leaf-level condition for code coverage + /// and add it to the function's SourceRegions. A branch region tracks a + /// "True" counter and a "False" counter for all leaf-level 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 a leaf-level 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::isLeafCondition(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; }); } @@ -706,7 +793,7 @@ if (getRegion().hasEndLoc() && MostRecentLocation == getEndOfFileOrMacro(MostRecentLocation) && isRegionAlreadyAdded(getStartOfFileOrMacro(MostRecentLocation), - MostRecentLocation)) + MostRecentLocation, getRegion().isBranch())) MostRecentLocation = getIncludeOrExpansionLoc(MostRecentLocation); } @@ -750,9 +837,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)); @@ -993,6 +1085,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) { @@ -1014,6 +1110,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) { @@ -1061,6 +1161,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) { @@ -1154,6 +1258,7 @@ BreakContinueStack.back().ContinueCount = addCounters( BreakContinueStack.back().ContinueCount, BC.ContinueCount); + Counter ParentCount = getRegion().getCounter(); Counter ExitCount = getRegionCounter(S); SourceLocation ExitLoc = getEnd(S); pushRegion(ExitCount); @@ -1162,6 +1267,27 @@ // 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) { @@ -1222,6 +1348,10 @@ if (OutCount != ParentCount) pushRegion(OutCount); + + // Create Branch Region around condition + createBranchRegion(S->getCond(), ThenCount, + subtractCounters(ParentCount, ThenCount)); } void VisitCXXTryStmt(const CXXTryStmt *S) { @@ -1265,6 +1395,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) { @@ -1272,8 +1406,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) { @@ -1281,8 +1433,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) { @@ -1319,11 +1489,19 @@ 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"; Index: clang/test/CoverageMapping/branch-constfolded.cpp =================================================================== --- /dev/null +++ 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 + Index: clang/test/CoverageMapping/branch-logical-mixed.cpp =================================================================== --- /dev/null +++ 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; +} Index: clang/test/CoverageMapping/branch-macros.cpp =================================================================== --- /dev/null +++ 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; +} Index: clang/test/CoverageMapping/branch-mincounters.cpp =================================================================== --- /dev/null +++ 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; +} Index: clang/test/CoverageMapping/branch-templates.cpp =================================================================== --- /dev/null +++ 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) Index: clang/test/CoverageMapping/continue.c =================================================================== --- clang/test/CoverageMapping/continue.c +++ clang/test/CoverageMapping/continue.c @@ -1,7 +1,8 @@ // RUN: %clang_cc1 -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 Index: clang/test/CoverageMapping/coroutine.cpp =================================================================== --- clang/test/CoverageMapping/coroutine.cpp +++ 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 Index: clang/test/CoverageMapping/if.cpp =================================================================== --- clang/test/CoverageMapping/if.cpp +++ 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) Index: clang/test/CoverageMapping/label.cpp =================================================================== --- clang/test/CoverageMapping/label.cpp +++ 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 Index: clang/test/CoverageMapping/logical.cpp =================================================================== --- clang/test/CoverageMapping/logical.cpp +++ clang/test/CoverageMapping/logical.cpp @@ -1,18 +1,25 @@ // RUN: %clang_cc1 -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; } Index: clang/test/CoverageMapping/loopmacro.c =================================================================== --- clang/test/CoverageMapping/loopmacro.c +++ 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) Index: clang/test/CoverageMapping/loops.cpp =================================================================== --- clang/test/CoverageMapping/loops.cpp +++ clang/test/CoverageMapping/loops.cpp @@ -19,29 +19,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; } Index: clang/test/CoverageMapping/macro-expansion.c =================================================================== --- clang/test/CoverageMapping/macro-expansion.c +++ clang/test/CoverageMapping/macro-expansion.c @@ -1,42 +1,53 @@ // RUN: %clang_cc1 -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) Index: clang/test/CoverageMapping/macro-expressions.cpp =================================================================== --- clang/test/CoverageMapping/macro-expressions.cpp +++ clang/test/CoverageMapping/macro-expressions.cpp @@ -82,6 +82,7 @@ // 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 +93,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 +101,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 +123,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) Index: clang/test/CoverageMapping/macros.c =================================================================== --- clang/test/CoverageMapping/macros.c +++ 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 Index: clang/test/CoverageMapping/macroscopes.cpp =================================================================== --- clang/test/CoverageMapping/macroscopes.cpp +++ 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 Index: clang/test/CoverageMapping/moremacros.c =================================================================== --- clang/test/CoverageMapping/moremacros.c +++ 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 Index: clang/test/CoverageMapping/return.c =================================================================== --- clang/test/CoverageMapping/return.c +++ 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 Index: clang/test/CoverageMapping/switch.cpp =================================================================== --- clang/test/CoverageMapping/switch.cpp +++ clang/test/CoverageMapping/switch.cpp @@ -1,37 +1,39 @@ // RUN: %clang_cc1 -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) } } Index: clang/test/CoverageMapping/switchmacro.c =================================================================== --- clang/test/CoverageMapping/switchmacro.c +++ 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 Index: clang/test/CoverageMapping/test.c =================================================================== --- clang/test/CoverageMapping/test.c +++ 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 } Index: clang/test/CoverageMapping/unreachable-macro.c =================================================================== --- clang/test/CoverageMapping/unreachable-macro.c +++ 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 Index: clang/test/CoverageMapping/while.c =================================================================== --- clang/test/CoverageMapping/while.c +++ clang/test/CoverageMapping/while.c @@ -1,12 +1,14 @@ // RUN: %clang_cc1 -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; } Index: clang/test/Profile/Inputs/c-general.proftext =================================================================== --- clang/test/Profile/Inputs/c-general.proftext +++ 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 Index: clang/test/Profile/branch-logical-mixed.cpp =================================================================== --- /dev/null +++ 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; +} Index: clang/test/Profile/c-general.c =================================================================== --- clang/test/Profile/c-general.c +++ 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); Index: clang/test/Profile/cxx-lambda.cpp =================================================================== --- clang/test/Profile/cxx-lambda.cpp +++ 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() Index: llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h =================================================================== --- llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h +++ llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h @@ -89,6 +89,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; @@ -218,10 +220,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; @@ -233,6 +245,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) { @@ -262,6 +283,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); } @@ -272,9 +301,18 @@ /// 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 @@ -311,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; @@ -320,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); } }; @@ -402,15 +451,16 @@ 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) {} friend bool operator==(const CoverageSegment &L, const CoverageSegment &R) { - return std::tie(L.Line, L.Col, L.Count, L.HasCount, L.IsRegionEntry, - L.IsGapRegion) == std::tie(R.Line, R.Col, R.Count, - R.HasCount, R.IsRegionEntry, - R.IsGapRegion); + return std::tie(L.Line, L.Col, L.Count, L.HasCount, + L.IsRegionEntry, L.IsGapRegion) == + std::tie(R.Line, R.Col, R.Count, R.HasCount, + R.IsRegionEntry, R.IsGapRegion); } }; @@ -482,6 +532,7 @@ std::string Filename; std::vector Segments; std::vector Expansions; + std::vector BranchRegions; public: CoverageData() = default; @@ -505,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. Index: llvm/include/llvm/ProfileData/InstrProf.h =================================================================== --- llvm/include/llvm/ProfileData/InstrProf.h +++ llvm/include/llvm/ProfileData/InstrProf.h @@ -982,7 +982,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; Index: llvm/include/llvm/ProfileData/InstrProfData.inc =================================================================== --- llvm/include/llvm/ProfileData/InstrProfData.inc +++ llvm/include/llvm/ProfileData/InstrProfData.inc @@ -657,7 +657,7 @@ /* 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 Index: llvm/lib/ProfileData/Coverage/CoverageMapping.cpp =================================================================== --- llvm/lib/ProfileData/Coverage/CoverageMapping.cpp +++ 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. @@ -664,6 +669,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"); @@ -711,6 +720,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"); @@ -730,6 +743,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"); Index: llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp =================================================================== --- llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp +++ 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. @@ -221,8 +221,22 @@ if (auto Err = readIntMax(EncodedCounterAndRegion, std::numeric_limits::max())) 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 +257,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 +316,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()) Index: llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp =================================================================== --- llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp +++ 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); Index: llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json =================================================================== --- llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json +++ 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}}} Index: llvm/test/tools/llvm-cov/Inputs/branch-c-general.proftext =================================================================== --- /dev/null +++ llvm/test/tools/llvm-cov/Inputs/branch-c-general.proftext @@ -0,0 +1,206 @@ +big_switch +# Func Hash: +13144136522122330070 +# Num Counters: +17 +# Counter Values: +1 +32 +32 +1 +0 +1 +1 +11 +11 +1 +1 +15 +15 +1 +1 +2 +2 + +boolean_operators +# Func Hash: +1245693242827665 +# Num Counters: +14 +# Counter Values: +1 +100 +34 +1 +66 +66 +17 +1 +34 +17 +33 +33 +50 +33 + +boolop_loops +# Func Hash: +12402604614320574815 +# Num Counters: +13 +# Counter Values: +1 +50 +51 +50 +50 +26 +1 +50 +51 +50 +50 +26 +1 + +branch-c-general.c:static_func +# Func Hash: +18129 +# Num Counters: +2 +# Counter Values: +1 +10 + +conditional_operator +# Func Hash: +54992 +# Num Counters: +3 +# Counter Values: +1 +0 +1 + +conditionals +# Func Hash: +4904767535850050386 +# Num Counters: +13 +# Counter Values: +1 +100 +50 +50 +33 +33 +16 +99 +100 +99 +99 +100 +1 + +do_fallthrough +# Func Hash: +8714614136504380050 +# Num Counters: +4 +# Counter Values: +1 +10 +2 +8 + +early_exits +# Func Hash: +2880354649761471549 +# Num Counters: +9 +# Counter Values: +1 +0 +51 +1 +25 +1 +25 +1 +0 + +jumps +# Func Hash: +15051420506203462683 +# Num Counters: +22 +# Counter Values: +1 +1 +0 +1 +0 +0 +1 +0 +1 +2 +3 +2 +0 +3 +0 +1 +1 +1 +10 +0 +10 +9 + +main +# Func Hash: +24 +# Num Counters: +2 +# Counter Values: +1 +0 + +simple_loops +# Func Hash: +1245818015463121 +# Num Counters: +4 +# Counter Values: +1 +100 +100 +75 + +switches +# Func Hash: +43242458792028222 +# Num Counters: +19 +# Counter Values: +1 +1 +1 +15 +7 +1 +0 +2 +2 +3 +3 +4 +4 +0 +4 +4 +5 +1 +0 + Index: llvm/test/tools/llvm-cov/Inputs/branch-logical-mixed.proftext =================================================================== --- /dev/null +++ llvm/test/tools/llvm-cov/Inputs/branch-logical-mixed.proftext @@ -0,0 +1,84 @@ +_Z4funcii +# Func Hash: +9495997393973228792 +# Num Counters: +69 +# Counter Values: +4 +0 +0 +0 +0 +2 +0 +2 +2 +3 +2 +0 +0 +0 +0 +0 +0 +1 +0 +1 +1 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +3 +1 +2 +0 +3 +0 +0 +0 +1 +0 +1 +0 +3 +3 +3 +3 +3 +3 +3 +3 +4 +3 +1 +0 +2 +1 +0 +0 +3 +0 +2 +0 +2 +0 +0 +4 +1 +3 + +main +# Func Hash: +24 +# Num Counters: +1 +# Counter Values: +4 + Index: llvm/test/tools/llvm-cov/Inputs/branch-macros.proftext =================================================================== --- /dev/null +++ llvm/test/tools/llvm-cov/Inputs/branch-macros.proftext @@ -0,0 +1,49 @@ +_Z4funcii +# Func Hash: +2263559805428111017 +# Num Counters: +19 +# Counter Values: +3 +1 +0 +1 +0 +1 +0 +1 +0 +1 +0 +0 +0 +0 +0 +0 +0 +0 +0 + +_Z5func2ii +# Func Hash: +5552544297182115648 +# Num Counters: +8 +# Counter Values: +3 +3 +2 +1 +0 +0 +1 +0 + +main +# Func Hash: +24 +# Num Counters: +1 +# Counter Values: +3 + Index: llvm/test/tools/llvm-cov/Inputs/branch-showBranchPercentage.proftext =================================================================== --- /dev/null +++ llvm/test/tools/llvm-cov/Inputs/branch-showBranchPercentage.proftext @@ -0,0 +1,28 @@ +main +# Func Hash: +3890582504168513655 +# Num Counters: +21 +# Counter Values: +6 +1 +2 +5 +3 +5 +5 +1 +0 +4 +1 +1 +4 +1 +1 +5 +1 +1 +0 +3 +50 + Index: llvm/test/tools/llvm-cov/Inputs/branch-templates.proftext =================================================================== --- /dev/null +++ llvm/test/tools/llvm-cov/Inputs/branch-templates.proftext @@ -0,0 +1,38 @@ +_Z4funcIbEiT_ +# Func Hash: +11045778961 +# Num Counters: +2 +# Counter Values: +1 +1 + +_Z4funcIfEiT_ +# Func Hash: +11045778961 +# Num Counters: +2 +# Counter Values: +1 +0 + +_Z4funcIiEiT_ +# Func Hash: +11045778961 +# Num Counters: +2 +# Counter Values: +1 +0 + +main +# Func Hash: +185286008276329560 +# Num Counters: +4 +# Counter Values: +1 +1 +0 +1 + Index: llvm/test/tools/llvm-cov/Inputs/showExpansions.json =================================================================== --- llvm/test/tools/llvm-cov/Inputs/showExpansions.json +++ llvm/test/tools/llvm-cov/Inputs/showExpansions.json @@ -1,4 +1,4 @@ -CHECK: {"expansions": +CHECK: {"branches":[],"expansions": CHECK: "filenames":["{{[^"]+}}showExpansions.cpp", CHECK: "source_region":[24,5,24,17,100,0,1,1], CHECK: "filename":"{{[^"]+}}showExpansions.cpp", Index: llvm/test/tools/llvm-cov/branch-c-general.c =================================================================== --- /dev/null +++ llvm/test/tools/llvm-cov/branch-c-general.c @@ -0,0 +1,311 @@ +// Test visualization of general branch constructs in C. + +// RUN: llvm-profdata merge %S/Inputs/branch-c-general.proftext -o %t.profdata +// RUN: llvm-cov show --show-branches=count %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S %s | FileCheck %s +// RUN: llvm-cov report --show-branch-summary %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S %s | FileCheck %s -check-prefix=REPORT + +void simple_loops() { + int i; + for (i = 0; i < 100; ++i) { // CHECK: Branch ([[@LINE]]:15): [True: 100, False: 1] + } + while (i > 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 2 92.00% 16 6 62.50% +// REPORT-NEXT: jumps 39 12 69.23% 48 2 95.83% 26 9 65.38% +// REPORT-NEXT: switches 28 5 82.14% 38 4 89.47% 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 0 100.00% 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 8 96.58% 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 Index: llvm/test/tools/llvm-cov/branch-export-json.test =================================================================== --- /dev/null +++ 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} Index: llvm/test/tools/llvm-cov/branch-export-lcov.test =================================================================== --- /dev/null +++ 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 Index: llvm/test/tools/llvm-cov/branch-logical-mixed.cpp =================================================================== --- /dev/null +++ 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 3 95.59% 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 3 95.89% 80 32 60.00% Index: llvm/test/tools/llvm-cov/branch-macros.cpp =================================================================== --- /dev/null +++ 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% Index: llvm/test/tools/llvm-cov/branch-noShowBranch.test =================================================================== --- /dev/null +++ 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 8 96.58% +// REPORT-NOT: TOTAL 197 24 87.82% 234 8 96.58% 174 38 78.16% + Index: llvm/test/tools/llvm-cov/branch-showBranchPercentage.c =================================================================== --- /dev/null +++ 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%] Index: llvm/test/tools/llvm-cov/branch-templates.cpp =================================================================== --- /dev/null +++ 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% Index: llvm/test/tools/llvm-cov/ignore-filename-regex.test =================================================================== --- llvm/test/tools/llvm-cov/ignore-filename-regex.test +++ 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 Index: llvm/tools/llvm-cov/CodeCoverage.cpp =================================================================== --- llvm/tools/llvm-cov/CodeCoverage.cpp +++ llvm/tools/llvm-cov/CodeCoverage.cpp @@ -88,6 +88,13 @@ 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, @@ -264,15 +271,44 @@ 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; + + auto NextBranch = Branches.begin(); + 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) { @@ -283,11 +319,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; } @@ -302,10 +341,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; @@ -322,9 +364,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; @@ -641,6 +686,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")); @@ -778,6 +828,7 @@ ::exit(0); } + ViewOpts.ShowBranchSummary = BranchSummary; ViewOpts.ShowRegionSummary = RegionSummary; ViewOpts.ShowInstantiationSummary = InstantiationSummary; ViewOpts.ExportSummaryOnly = SummaryOnly; @@ -812,6 +863,16 @@ 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 " @@ -855,6 +916,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; Index: llvm/tools/llvm-cov/CoverageExporterJson.cpp =================================================================== --- llvm/tools/llvm-cov/CoverageExporterJson.cpp +++ 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,50 @@ 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 +174,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 +201,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 +218,9 @@ // 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; } Index: llvm/tools/llvm-cov/CoverageExporterLcov.cpp =================================================================== --- llvm/tools/llvm-cov/CoverageExporterLcov.cpp +++ 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,101 @@ } } +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 << ',' < 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,11 @@ << 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 +345,7 @@ ++Totals.ExecutionCount; Totals.RegionCoverage += Function.RegionCoverage; Totals.LineCoverage += Function.LineCoverage; + Totals.BranchCoverage += Function.BranchCoverage; render(Function, DC, OS); } if (Totals.ExecutionCount) { @@ -420,7 +456,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"; Index: llvm/tools/llvm-cov/CoverageSummaryInfo.h =================================================================== --- llvm/tools/llvm-cov/CoverageSummaryInfo.h +++ 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); } Index: llvm/tools/llvm-cov/CoverageSummaryInfo.cpp =================================================================== --- llvm/tools/llvm-cov/CoverageSummaryInfo.cpp +++ 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 (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 (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; } Index: llvm/tools/llvm-cov/CoverageViewOptions.h =================================================================== --- llvm/tools/llvm-cov/CoverageViewOptions.h +++ llvm/tools/llvm-cov/CoverageViewOptions.h @@ -23,20 +23,30 @@ 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; Index: llvm/tools/llvm-cov/SourceCoverageView.h =================================================================== --- llvm/tools/llvm-cov/SourceCoverageView.h +++ llvm/tools/llvm-cov/SourceCoverageView.h @@ -67,6 +67,24 @@ } }; +/// 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 +158,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 +230,10 @@ virtual void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV, unsigned ViewDepth) = 0; + /// Render an 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 +280,11 @@ void addInstantiation(StringRef FunctionName, unsigned Line, std::unique_ptr View); + /// Add an 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, Index: llvm/tools/llvm-cov/SourceCoverageView.cpp =================================================================== --- llvm/tools/llvm-cov/SourceCoverageView.cpp +++ 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,13 @@ 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 +195,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 +245,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 +268,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); Index: llvm/tools/llvm-cov/SourceCoverageViewHTML.h =================================================================== --- llvm/tools/llvm-cov/SourceCoverageViewHTML.h +++ 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; Index: llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp =================================================================== --- llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp +++ 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,71 @@ 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) { Index: llvm/tools/llvm-cov/SourceCoverageViewText.h =================================================================== --- llvm/tools/llvm-cov/SourceCoverageViewText.h +++ 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; Index: llvm/tools/llvm-cov/SourceCoverageViewText.cpp =================================================================== --- llvm/tools/llvm-cov/SourceCoverageViewText.cpp +++ llvm/tools/llvm-cov/SourceCoverageViewText.cpp @@ -15,6 +15,7 @@ #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,52 @@ /*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: 96.58% (226/234) +// HTML-INDEX: +// HTML-INDEX: 87.82% (173/197) +// HTML-INDEX: +// HTML-INDEX: 78.16% (136/174) +// HTML-INDEX: