Skip to content

[llvm-cov] Fix branch counts of template functions (#111743) #113925

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions llvm/test/tools/llvm-cov/branch-export-lcov-unify-instances.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@

// RUN: llvm-profdata merge %S/Inputs/branch-templates.proftext -o %t.profdata
// RUN: llvm-cov export --format=lcov --unify-instantiations=true %S/Inputs/branch-templates.o32l -instr-profile %t.profdata | FileCheck %s -check-prefix=UNIFY

// UNIFY-DAG: BRDA:14,0,0,1
// UNIFY-DAG: BRDA:14,0,1,2
// UNIFY-DAG: BRDA:30,0,0,1
// UNIFY-DAG: BRDA:30,0,1,0
// UNIFY-DAG: BRDA:32,0,0,0
// UNIFY-DAG: BRDA:32,0,1,1
// UNIFY-DAG: BRDA:34,0,0,1
// UNIFY-DAG: BRDA:34,0,1,0
// UNIFY-NOT: BRDA
// UNIFY: BRF:8
// UNIFY: BRH:4
// UNIFY: LF:17
// UNIFY: LH:13

// RUN: llvm-profdata merge %S/Inputs/branch-templates.proftext -o %t.profdata
// RUN: llvm-cov export --format=lcov --unify-instantiations=false %S/Inputs/branch-templates.o32l -instr-profile %t.profdata | FileCheck %s

// CHECK-DAG: BRDA:14,0,0,0
// CHECK-DAG: BRDA:14,0,1,1
// CHECK-DAG: BRDA:14,1,2,1
// CHECK-DAG: BRDA:14,1,3,0
// CHECK-DAG: BRDA:14,2,4,0
// CHECK-DAG: BRDA:14,2,5,1
// CHECK-DAG: BRDA:30,0,0,1
// CHECK-DAG: BRDA:30,0,1,0
// CHECK-DAG: BRDA:32,0,0,0
// CHECK-DAG: BRDA:32,0,1,1
// CHECK-DAG: BRDA:34,0,0,1
// CHECK-DAG: BRDA:34,0,1,0
// CHECK-NOT: BRDA
// CHECK: BRF:8
// CHECK: BRH:4
// CHECK: LF:17
// CHECK: LH:13
37 changes: 36 additions & 1 deletion llvm/test/tools/llvm-cov/branch-export-lcov.test
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

// 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 | FileCheck %s -check-prefix=MACROS
// RUN: llvm-cov export --format=lcov --unify-instantiations=false %S/Inputs/branch-macros.o32l -instr-profile %t.profdata | FileCheck %s -check-prefix=MACROS
// RUN: llvm-cov export --format=lcov --skip-branches %S/Inputs/branch-macros.o32l -instr-profile %t.profdata | FileCheck %s -check-prefix=NOBRANCH

// MACROS-COUNT-4: BRDA:17
Expand Down Expand Up @@ -78,3 +78,38 @@
// NOBRANCH-NOT: BRF
// NOBRANCH-NOT: BRH

// Check recursive macro-expansions with unify mode.
// RUN: llvm-profdata merge %S/Inputs/branch-macros.proftext -o %t.profdata
// RUN: llvm-cov export --format=lcov --unify-instantiations=true %S/Inputs/branch-macros.o32l -instr-profile %t.profdata | FileCheck %s -check-prefix=MACROS2

// MACROS2-COUNT-4: BRDA:17
// MACROS2-NOT: BRDA:17

// MACROS2-COUNT-4: BRDA:19
// MACROS2-NOT: BRDA:19

// MACROS2-COUNT-4: BRDA:21
// MACROS2-NOT: BRDA:21

// MACROS2-COUNT-4: BRDA:23
// MACROS2-NOT: BRDA:23

// MACROS2-COUNT-4: BRDA:25
// MACROS2-NOT: BRDA:25

// MACROS2: BRDA:27,0,0,0
// MACROS2: BRDA:27,0,1,3
// MACROS2: BRDA:27,1,2,-
// MACROS2: BRDA:27,1,3,-
// MACROS2: BRDA:27,2,4,-
// MACROS2: BRDA:27,2,5,-
// MACROS2: BRDA:27,3,6,-
// MACROS2: BRDA:27,3,7,-
// MACROS2: BRDA:27,4,8,-
// MACROS2: BRDA:27,4,9,-

// MACROS2-COUNT-10: BRDA:37
// MACROS2-NOT: BRDA:37
// MACROS2-NOT: BRDA
// MACROS2: BRF:40
// MACROS2: BRH:24
5 changes: 5 additions & 0 deletions llvm/tools/llvm-cov/CodeCoverage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1270,13 +1270,18 @@ int CodeCoverageTool::doExport(int argc, const char **argv,
cl::desc("Don't export branch data (LCOV)"),
cl::cat(ExportCategory));

cl::opt<bool> UnifyInstantiations("unify-instantiations", cl::Optional,
cl::desc("Unify function instantiations"),
cl::init(true), cl::cat(ExportCategory));

auto Err = commandLineParser(argc, argv);
if (Err)
return Err;

ViewOpts.SkipExpansions = SkipExpansions;
ViewOpts.SkipFunctions = SkipFunctions;
ViewOpts.SkipBranches = SkipBranches;
ViewOpts.UnifyFunctionInstantiations = UnifyInstantiations;

if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text &&
ViewOpts.Format != CoverageViewOptions::OutputFormat::Lcov) {
Expand Down
130 changes: 99 additions & 31 deletions llvm/tools/llvm-cov/CoverageExporterLcov.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,26 @@
#include "CoverageReport.h"

using namespace llvm;
using namespace coverage;

namespace {

struct NestedCountedRegion : public coverage::CountedRegion {
// Contains the path to default and expanded branches.
// Size is 1 for default branches and greater 1 for expanded branches.
std::vector<LineColPair> NestedPath;
// Indicates whether this item should be ignored at rendering.
bool Ignore = false;

NestedCountedRegion(llvm::coverage::CountedRegion Region,
std::vector<LineColPair> NestedPath)
: llvm::coverage::CountedRegion(std::move(Region)),
NestedPath(std::move(NestedPath)) {}

// Returns the root line of the branch.
unsigned getEffectiveLine() const { return NestedPath.front().first; }
};

void renderFunctionSummary(raw_ostream &OS,
const FileCoverageSummary &Summary) {
OS << "FNF:" << Summary.FunctionCoverage.getNumFunctions() << '\n'
Expand Down Expand Up @@ -75,71 +92,121 @@ void renderLineExecutionCounts(raw_ostream &OS,
}
}

std::vector<llvm::coverage::CountedRegion>
std::vector<NestedCountedRegion>
collectNestedBranches(const coverage::CoverageMapping &Coverage,
ArrayRef<llvm::coverage::ExpansionRecord> Expansions,
int ViewDepth = 0, int SrcLine = 0) {
std::vector<llvm::coverage::CountedRegion> Branches;
std::vector<LineColPair> &NestedPath) {
std::vector<NestedCountedRegion> 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;
// Track the path to the nested expansions.
NestedPath.push_back(Expansion.Region.startLoc());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much better than tracking LineStart. Thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's required to avoid unifying different expanded branches of the same root line.


// Recursively collect branches from nested expansions.
auto NestedExpansions = ExpansionCoverage.getExpansions();
auto NestedExBranches = collectNestedBranches(Coverage, NestedExpansions,
ViewDepth + 1, SrcLine);
auto NestedExBranches =
collectNestedBranches(Coverage, NestedExpansions, NestedPath);
append_range(Branches, NestedExBranches);

// Add branches from this level of expansion.
auto ExBranches = ExpansionCoverage.getBranches();
for (auto B : ExBranches)
for (auto &B : ExBranches)
if (B.FileID == Expansion.FileID) {
B.LineStart = SrcLine;
Branches.push_back(B);
Branches.push_back(NestedCountedRegion(B, NestedPath));
}

NestedPath.pop_back();
}

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 appendNestedCountedRegions(const std::vector<CountedRegion> &Src,
std::vector<NestedCountedRegion> &Dst) {
auto Unfolded = make_filter_range(Src, [](auto &Region) {
return !Region.TrueFolded || !Region.FalseFolded;
});
Dst.reserve(Dst.size() + Src.size());
std::transform(Unfolded.begin(), Unfolded.end(), std::back_inserter(Dst),
[=](auto &Region) {
return NestedCountedRegion(Region, {Region.startLoc()});
});
}

void appendNestedCountedRegions(const std::vector<NestedCountedRegion> &Src,
std::vector<NestedCountedRegion> &Dst) {
auto Unfolded = make_filter_range(Src, [](auto &NestedRegion) {
return !NestedRegion.TrueFolded || !NestedRegion.FalseFolded;
});
Dst.reserve(Dst.size() + Src.size());
std::copy(Unfolded.begin(), Unfolded.end(), std::back_inserter(Dst));
}

bool sortNested(const NestedCountedRegion &I, const NestedCountedRegion &J) {
// This sorts each element by line and column.
// Implies that all elements are first sorted by getEffectiveLine().
return I.NestedPath < J.NestedPath;
}

void combineInstanceCounts(std::vector<NestedCountedRegion> &Branches) {
auto NextBranch = Branches.begin();
auto EndBranch = Branches.end();

while (NextBranch != EndBranch) {
auto SumBranch = NextBranch++;

// Ensure that only branches with the same NestedPath are summed up.
while (NextBranch != EndBranch &&
SumBranch->NestedPath == NextBranch->NestedPath) {
SumBranch->ExecutionCount += NextBranch->ExecutionCount;
SumBranch->FalseExecutionCount += NextBranch->FalseExecutionCount;
// Mark this branch as ignored.
NextBranch->Ignore = true;

NextBranch++;
}
}
}

void renderBranchExecutionCounts(raw_ostream &OS,
const coverage::CoverageMapping &Coverage,
const coverage::CoverageData &FileCoverage) {
std::vector<llvm::coverage::CountedRegion> Branches =
FileCoverage.getBranches();
const coverage::CoverageData &FileCoverage,
bool UnifyInstances) {

std::vector<NestedCountedRegion> Branches;

appendNestedCountedRegions(FileCoverage.getBranches(), Branches);

// Recursively collect branches for all file expansions.
std::vector<llvm::coverage::CountedRegion> ExBranches =
collectNestedBranches(Coverage, FileCoverage.getExpansions());
std::vector<LineColPair> NestedPath;
std::vector<NestedCountedRegion> ExBranches =
collectNestedBranches(Coverage, FileCoverage.getExpansions(), NestedPath);

// Append Expansion Branches to Source Branches.
append_range(Branches, ExBranches);
appendNestedCountedRegions(ExBranches, Branches);

// Sort branches based on line number to ensure branches corresponding to the
// same source line are counted together.
llvm::sort(Branches, sortLine);
llvm::sort(Branches, sortNested);

if (UnifyInstances) {
combineInstanceCounts(Branches);
}

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 CurrentLine = NextBranch->getEffectiveLine();
unsigned PairIndex = 0;
unsigned BranchIndex = 0;

while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart) {
if (!NextBranch->TrueFolded || !NextBranch->FalseFolded) {
while (NextBranch != EndBranch &&
CurrentLine == NextBranch->getEffectiveLine()) {
if (!NextBranch->Ignore) {
unsigned BC1 = NextBranch->ExecutionCount;
unsigned BC2 = NextBranch->FalseExecutionCount;
bool BranchNotExecuted = (BC1 == 0 && BC2 == 0);
Expand Down Expand Up @@ -173,7 +240,7 @@ void renderBranchSummary(raw_ostream &OS, const FileCoverageSummary &Summary) {
void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
const std::string &Filename,
const FileCoverageSummary &FileReport, bool ExportSummaryOnly,
bool SkipFunctions, bool SkipBranches) {
bool SkipFunctions, bool SkipBranches, bool UnifyInstances) {
OS << "SF:" << Filename << '\n';

if (!ExportSummaryOnly && !SkipFunctions) {
Expand All @@ -186,7 +253,7 @@ void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
auto FileCoverage = Coverage.getCoverageForFile(Filename);
renderLineExecutionCounts(OS, FileCoverage);
if (!SkipBranches)
renderBranchExecutionCounts(OS, Coverage, FileCoverage);
renderBranchExecutionCounts(OS, Coverage, FileCoverage, UnifyInstances);
}
if (!SkipBranches)
renderBranchSummary(OS, FileReport);
Expand All @@ -198,11 +265,11 @@ void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
void renderFiles(raw_ostream &OS, const coverage::CoverageMapping &Coverage,
ArrayRef<std::string> SourceFiles,
ArrayRef<FileCoverageSummary> FileReports,
bool ExportSummaryOnly, bool SkipFunctions,
bool SkipBranches) {
bool ExportSummaryOnly, bool SkipFunctions, bool SkipBranches,
bool UnifyInstances) {
for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I)
renderFile(OS, Coverage, SourceFiles[I], FileReports[I], ExportSummaryOnly,
SkipFunctions, SkipBranches);
SkipFunctions, SkipBranches, UnifyInstances);
}

} // end anonymous namespace
Expand All @@ -221,5 +288,6 @@ void CoverageExporterLcov::renderRoot(ArrayRef<std::string> SourceFiles) {
auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals,
SourceFiles, Options);
renderFiles(OS, Coverage, SourceFiles, FileReports, Options.ExportSummaryOnly,
Options.SkipFunctions, Options.SkipBranches);
Options.SkipFunctions, Options.SkipBranches,
Options.UnifyFunctionInstantiations);
}
1 change: 1 addition & 0 deletions llvm/tools/llvm-cov/CoverageViewOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ struct CoverageViewOptions {
bool ShowBranchPercents;
bool ShowExpandedRegions;
bool ShowFunctionInstantiations;
bool UnifyFunctionInstantiations;
bool ShowFullFilenames;
bool ShowBranchSummary;
bool ShowMCDCSummary;
Expand Down
Loading