[llvm-branch-commits] [llvm] llvm-cov: Introduce `--merge-instantiations=<MergeStrategy>` (PR #121194)

NAKAMURA Takumi via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Jan 10 06:35:59 PST 2025


https://github.com/chapuni updated https://github.com/llvm/llvm-project/pull/121194

>From 273eea73c158acbf7140bd599554b2ba02d88097 Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Fri, 27 Dec 2024 16:14:24 +0900
Subject: [PATCH 1/2] llvm-cov: Introduce
 `--merge-instantiations=<MergeStrategy>`

---
 .../ProfileData/Coverage/CoverageMapping.h    |  82 ++++++++-
 .../ProfileData/Coverage/CoverageMapping.cpp  | 170 ++++++++++++++++--
 .../llvm-cov/Inputs/branch-templates.cpp      |   6 +-
 .../llvm-cov/Inputs/mcdc-templates-merge.cpp  |  54 ++++++
 .../Inputs/mcdc-templates-merge.proftext      |  73 ++++++++
 .../llvm-cov/Inputs/mcdc-templates-merge.yaml | 105 +++++++++++
 .../tools/llvm-cov/branch-export-json.test    |   2 +-
 .../tools/llvm-cov/branch-export-lcov.test    |   4 +-
 llvm/test/tools/llvm-cov/branch-macros.test   |   9 +
 .../test/tools/llvm-cov/branch-templates.test |   4 +-
 .../tools/llvm-cov/mcdc-templates-merge.test  |  41 +++++
 llvm/tools/llvm-cov/CodeCoverage.cpp          |  12 +-
 llvm/tools/llvm-cov/CoverageReport.cpp        |   4 +-
 llvm/tools/llvm-cov/CoverageViewOptions.h     |   2 +
 llvm/tools/llvm-cov/SourceCoverageView.cpp    |   4 +-
 15 files changed, 540 insertions(+), 32 deletions(-)
 create mode 100644 llvm/test/tools/llvm-cov/Inputs/mcdc-templates-merge.cpp
 create mode 100644 llvm/test/tools/llvm-cov/Inputs/mcdc-templates-merge.proftext
 create mode 100644 llvm/test/tools/llvm-cov/Inputs/mcdc-templates-merge.yaml
 create mode 100644 llvm/test/tools/llvm-cov/mcdc-templates-merge.test

diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
index 64416fdba1b247..d6df8403a2cd1c 100644
--- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
+++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
@@ -59,6 +59,12 @@ namespace coverage {
 class CoverageMappingReader;
 struct CoverageMappingRecord;
 
+enum class MergeStrategy {
+  Merge,
+  Any,
+  All,
+};
+
 enum class coveragemap_error {
   success = 0,
   eof,
@@ -375,6 +381,32 @@ struct CountedRegion : public CounterMappingRegion {
       : CounterMappingRegion(R), ExecutionCount(ExecutionCount),
         FalseExecutionCount(FalseExecutionCount), TrueFolded(false),
         FalseFolded(false) {}
+
+  LineColPair viewLoc() const { return startLoc(); }
+
+  bool isMergeable(const CountedRegion &RHS) const {
+    return (this->viewLoc() == RHS.viewLoc());
+  }
+
+  void merge(const CountedRegion &RHS, MergeStrategy Strategy);
+
+  /// Returns comparable rank value in selecting a better Record for merging.
+  auto getMergeRank(MergeStrategy Strategy) const {
+    assert(isBranch() && "Dedicated to Branch");
+    assert(Strategy == MergeStrategy::Any && "Dedicated to Any");
+    unsigned m = 0;
+    // Prefer both Counts have values.
+    m = (m << 1) | (ExecutionCount != 0 && FalseExecutionCount != 0);
+    // Prefer both are unfolded.
+    m = (m << 1) | (!TrueFolded && !FalseFolded);
+    // Prefer either Count has value.
+    m = (m << 1) | (ExecutionCount != 0 || FalseExecutionCount != 0);
+    // Prefer either is unfolded.
+    m = (m << 1) | (!TrueFolded || !FalseFolded);
+    return std::make_pair(m, ExecutionCount + FalseExecutionCount);
+  }
+
+  void commit() const {}
 };
 
 /// MCDC Record grouping all information together.
@@ -462,6 +494,19 @@ struct MCDCRecord {
     findIndependencePairs();
   }
 
+  inline LineColPair viewLoc() const { return Region.endLoc(); }
+
+  bool isMergeable(const MCDCRecord &RHS) const {
+    return (this->viewLoc() == RHS.viewLoc() && this->PosToID == RHS.PosToID &&
+            this->CondLoc == RHS.CondLoc);
+  }
+
+  // This may invalidate IndependencePairs
+  // MCDCRecord &operator+=(const MCDCRecord &RHS);
+  void merge(MCDCRecord &&RHS, MergeStrategy Strategy);
+
+  void commit() { findIndependencePairs(); }
+
   // Compare executed test vectors against each other to find an independence
   // pairs for each condition.  This processing takes the most time.
   void findIndependencePairs();
@@ -512,15 +557,42 @@ struct MCDCRecord {
     return (*IndependencePairs)[PosToID[Condition]];
   }
 
-  float getPercentCovered() const {
-    unsigned Folded = 0;
+  std::pair<unsigned, unsigned> getCoveredCount() const {
     unsigned Covered = 0;
+    unsigned Folded = 0;
     for (unsigned C = 0; C < getNumConditions(); C++) {
       if (isCondFolded(C))
         Folded++;
       else if (isConditionIndependencePairCovered(C))
         Covered++;
     }
+    return {Covered, Folded};
+  }
+
+  /// Returns comparable rank value in selecting a better Record for merging.
+  std::tuple<unsigned, unsigned, unsigned>
+  getMergeRank(MergeStrategy Strategy) const {
+    auto [Covered, Folded] = getCoveredCount();
+    auto NumTVs = getNumTestVectors();
+    switch (Strategy) {
+    case MergeStrategy::Merge:
+    case MergeStrategy::Any:
+      return {
+          Covered, // The largest covered number
+          ~Folded, // Less folded is better
+          NumTVs,  // Show more test vectors
+      };
+    case MergeStrategy::All:
+      return {
+          ~Covered, // The smallest covered number
+          ~Folded,  // Less folded is better
+          NumTVs,   // Show more test vectors
+      };
+    }
+  }
+
+  float getPercentCovered() const {
+    auto [Covered, Folded] = getCoveredCount();
 
     unsigned Total = getNumConditions() - Folded;
     if (Total == 0)
@@ -1013,11 +1085,13 @@ class CoverageMapping {
   /// information. That is, only names returned from getUniqueSourceFiles will
   /// yield a result.
   CoverageData getCoverageForFile(
-      StringRef Filename,
+      StringRef Filename, MergeStrategy Strategy = MergeStrategy::Merge,
       const DenseSet<const FunctionRecord *> &FilteredOutFunctions = {}) const;
 
   /// Get the coverage for a particular function.
-  CoverageData getCoverageForFunction(const FunctionRecord &Function) const;
+  CoverageData
+  getCoverageForFunction(const FunctionRecord &Function,
+                         MergeStrategy Strategy = MergeStrategy::Merge) const;
 
   /// Get the coverage for an expansion within a coverage set.
   CoverageData getCoverageForExpansion(const ExpansionRecord &Expansion) const;
diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index e7780b465186df..dbbf5b03d0205e 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -221,6 +221,58 @@ Expected<int64_t> CounterMappingContext::evaluate(const Counter &C) const {
   return LastPoppedValue;
 }
 
+void CountedRegion::merge(const CountedRegion &RHS, MergeStrategy Strategy) {
+  assert(this->isBranch() && RHS.isBranch());
+  auto MergeCounts = [Strategy](uint64_t &LHSCount, bool &LHSFolded,
+                                uint64_t RHSCount, bool RHSFolded) {
+    switch (Strategy) {
+    default:
+      llvm_unreachable("Don't perform by-parameter merging");
+    case MergeStrategy::Merge:
+      LHSCount += RHSCount;
+      LHSFolded = (LHSFolded && !LHSCount && RHSFolded);
+      break;
+    case MergeStrategy::All:
+      LHSCount = (LHSFolded   ? RHSCount
+                  : RHSFolded ? LHSCount
+                              : std::min(LHSCount, RHSCount));
+      LHSFolded = (LHSFolded && RHSFolded);
+      break;
+    }
+  };
+
+  switch (Strategy) {
+  case MergeStrategy::Any:
+    // Take either better (more satisfied) hand side.
+    // FIXME: Really needed? Better just to merge?
+    if (this->getMergeRank(Strategy) < RHS.getMergeRank(Strategy))
+      *this = RHS;
+    break;
+  case MergeStrategy::Merge:
+  case MergeStrategy::All:
+    // Able to merge by parameter.
+    MergeCounts(this->ExecutionCount, this->TrueFolded, RHS.ExecutionCount,
+                RHS.TrueFolded);
+    MergeCounts(this->FalseExecutionCount, this->FalseFolded,
+                RHS.FalseExecutionCount, RHS.FalseFolded);
+    break;
+  }
+}
+
+void MCDCRecord::merge(MCDCRecord &&RHS, MergeStrategy Strategy) {
+  assert(this->PosToID == RHS.PosToID);
+  assert(this->CondLoc == RHS.CondLoc);
+
+  switch (Strategy) {
+  case MergeStrategy::Merge:
+  case MergeStrategy::Any:
+  case MergeStrategy::All:
+    if (this->getMergeRank(Strategy) < RHS.getMergeRank(Strategy))
+      *this = std::move(RHS);
+    return;
+  }
+}
+
 // Find an independence pair for each condition:
 // - The condition is true in one test and false in the other.
 // - The decision outcome is true one test and false in the other.
@@ -1272,7 +1324,8 @@ class SegmentBuilder {
 
   /// Combine counts of regions which cover the same area.
   static ArrayRef<CountedRegion>
-  combineRegions(MutableArrayRef<CountedRegion> Regions) {
+  combineRegions(MutableArrayRef<CountedRegion> Regions,
+                 MergeStrategy Strategy) {
     if (Regions.empty())
       return Regions;
     auto Active = Regions.begin();
@@ -1298,8 +1351,22 @@ class SegmentBuilder {
       // value for that area.
       // We add counts of the regions of the same kind as the active region
       // to handle the both situations.
-      if (I->Kind == Active->Kind)
+      if (I->Kind != Active->Kind)
+        continue;
+
+      switch (Strategy) {
+      case MergeStrategy::Merge:
         Active->ExecutionCount += I->ExecutionCount;
+        break;
+      case MergeStrategy::Any:
+        Active->ExecutionCount =
+            std::max(Active->ExecutionCount, I->ExecutionCount);
+        break;
+      case MergeStrategy::All:
+        Active->ExecutionCount =
+            std::min(Active->ExecutionCount, I->ExecutionCount);
+        break;
+      }
     }
     return Regions.drop_back(std::distance(++Active, End));
   }
@@ -1307,12 +1374,13 @@ class SegmentBuilder {
 public:
   /// Build a sorted list of CoverageSegments from a list of Regions.
   static std::vector<CoverageSegment>
-  buildSegments(MutableArrayRef<CountedRegion> Regions) {
+  buildSegments(MutableArrayRef<CountedRegion> Regions,
+                MergeStrategy Strategy) {
     std::vector<CoverageSegment> Segments;
     SegmentBuilder Builder(Segments);
 
     sortNestedRegions(Regions);
-    ArrayRef<CountedRegion> CombinedRegions = combineRegions(Regions);
+    ArrayRef<CountedRegion> CombinedRegions = combineRegions(Regions, Strategy);
 
     LLVM_DEBUG({
       dbgs() << "Combined regions:\n";
@@ -1342,11 +1410,78 @@ class SegmentBuilder {
   }
 };
 
+template <class RecTy>
+static void mergeRecords(std::vector<RecTy> &Records, MergeStrategy Strategy) {
+  if (Records.empty())
+    return;
+
+  std::vector<RecTy> NewRecords;
+
+  assert(Records.size() <= std::numeric_limits<unsigned>::max());
+
+  // Build up sorted indices (rather than pointers) of Records.
+  SmallVector<unsigned, 1> BIdxs(Records.size());
+  std::iota(BIdxs.begin(), BIdxs.end(), 0);
+  llvm::stable_sort(BIdxs, [&](auto A, auto B) {
+    auto StartA = Records[A].viewLoc();
+    auto StartB = Records[B].viewLoc();
+    return (std::tie(StartA, A) < std::tie(StartB, B));
+  });
+
+  // 1st element should be stored into SubView.
+  auto I = BIdxs.begin(), E = BIdxs.end();
+  SmallVector<RecTy, 1> ViewRecords{Records[*I++]};
+
+  auto findMergeableInViewRecords = [&](const RecTy &Branch) {
+    auto I = ViewRecords.rbegin(), E = ViewRecords.rend();
+    for (; I != E; ++I)
+      if (I->isMergeable(Branch))
+        return I;
+
+    // Not mergeable.
+    return E;
+  };
+
+  auto addRecordToSubView = [&] {
+    assert(!ViewRecords.empty() && "Should have the back");
+    for (auto &Acc : ViewRecords) {
+      Acc.commit();
+      NewRecords.push_back(std::move(Acc));
+    }
+  };
+
+  for (; I != E; ++I) {
+    assert(!ViewRecords.empty() && "Should have the back in the loop");
+    auto &AccB = ViewRecords.back();
+    auto &Branch = Records[*I];
+
+    // Flush current and create the next SubView at the different line.
+    if (AccB.viewLoc().first != Branch.viewLoc().first) {
+      addRecordToSubView();
+      ViewRecords = {Branch};
+    } else if (auto AccI = findMergeableInViewRecords(Branch);
+               AccI != ViewRecords.rend()) {
+      // Merge the current Branch into the back of SubView.
+      AccI->merge(std::move(Branch), Strategy);
+    } else {
+      // Not mergeable.
+      ViewRecords.push_back(Branch);
+    }
+  }
+
+  // Flush the last SubView.
+  addRecordToSubView();
+
+  // Replace
+  Records = std::move(NewRecords);
+}
+
 struct MergeableCoverageData : public CoverageData {
   std::vector<CountedRegion> CodeRegions;
+  MergeStrategy Strategy;
 
-  MergeableCoverageData(bool Single, StringRef Filename)
-      : CoverageData(Single, Filename) {}
+  MergeableCoverageData(MergeStrategy Strategy, bool Single, StringRef Filename)
+      : CoverageData(Single, Filename), Strategy(Strategy) {}
 
   void addFunctionRegions(
       const FunctionRecord &Function,
@@ -1368,8 +1503,11 @@ struct MergeableCoverageData : public CoverageData {
         MCDCRecords.push_back(MR);
   }
 
-  CoverageData buildSegments() {
-    Segments = SegmentBuilder::buildSegments(CodeRegions);
+  CoverageData merge(MergeStrategy Strategy) {
+    mergeRecords(BranchRegions, Strategy);
+    mergeRecords(MCDCRecords, Strategy);
+
+    Segments = SegmentBuilder::buildSegments(CodeRegions, Strategy);
     return CoverageData(std::move(*this));
   }
 };
@@ -1423,10 +1561,10 @@ static bool isExpansion(const CountedRegion &R, unsigned FileID) {
 }
 
 CoverageData CoverageMapping::getCoverageForFile(
-    StringRef Filename,
+    StringRef Filename, MergeStrategy Strategy,
     const DenseSet<const FunctionRecord *> &FilteredOutFunctions) const {
   assert(SingleByteCoverage);
-  MergeableCoverageData FileCoverage(*SingleByteCoverage, Filename);
+  MergeableCoverageData FileCoverage(Strategy, *SingleByteCoverage, Filename);
 
   // Look up the function records in the given file. Due to hash collisions on
   // the filename, we may get back some records that are not in the file.
@@ -1445,7 +1583,7 @@ CoverageData CoverageMapping::getCoverageForFile(
 
   LLVM_DEBUG(dbgs() << "Emitting segments for file: " << Filename << "\n");
 
-  return FileCoverage.buildSegments();
+  return FileCoverage.merge(Strategy);
 }
 
 std::vector<InstantiationGroup>
@@ -1474,13 +1612,14 @@ CoverageMapping::getInstantiationGroups(StringRef Filename) const {
 }
 
 CoverageData
-CoverageMapping::getCoverageForFunction(const FunctionRecord &Function) const {
+CoverageMapping::getCoverageForFunction(const FunctionRecord &Function,
+                                        MergeStrategy Strategy) const {
   auto MainFileID = findMainViewFileID(Function);
   if (!MainFileID)
     return CoverageData();
 
   assert(SingleByteCoverage);
-  MergeableCoverageData FunctionCoverage(*SingleByteCoverage,
+  MergeableCoverageData FunctionCoverage(Strategy, *SingleByteCoverage,
                                          Function.Filenames[*MainFileID]);
   FunctionCoverage.addFunctionRegions(
       Function, [&](auto &CR) { return (CR.FileID == *MainFileID); },
@@ -1489,7 +1628,7 @@ CoverageMapping::getCoverageForFunction(const FunctionRecord &Function) const {
   LLVM_DEBUG(dbgs() << "Emitting segments for function: " << Function.Name
                     << "\n");
 
-  return FunctionCoverage.buildSegments();
+  return FunctionCoverage.merge(Strategy);
 }
 
 CoverageData CoverageMapping::getCoverageForExpansion(
@@ -1511,7 +1650,8 @@ CoverageData CoverageMapping::getCoverageForExpansion(
 
   LLVM_DEBUG(dbgs() << "Emitting segments for expansion of file "
                     << Expansion.FileID << "\n");
-  ExpansionCoverage.Segments = SegmentBuilder::buildSegments(Regions);
+  ExpansionCoverage.Segments =
+      SegmentBuilder::buildSegments(Regions, MergeStrategy::Merge);
 
   return ExpansionCoverage;
 }
diff --git a/llvm/test/tools/llvm-cov/Inputs/branch-templates.cpp b/llvm/test/tools/llvm-cov/Inputs/branch-templates.cpp
index 4d932eaf5944a8..d8a7c056f36779 100644
--- a/llvm/test/tools/llvm-cov/Inputs/branch-templates.cpp
+++ b/llvm/test/tools/llvm-cov/Inputs/branch-templates.cpp
@@ -11,9 +11,9 @@ void unused(T x) {
 
 template<typename T>
 int func(T x) {
-  if(x)       // BRCOV: |  Branch ([[@LINE]]:6): [True: 0, False: 1]
-    return 0; // BRCOV: |  Branch ([[@LINE-1]]:6): [True: 1, False: 0]
-  else        // BRCOV: |  Branch ([[@LINE-2]]:6): [True: 0, False: 1]
+  if(x)       // BRCOV: |  Branch ([[@LINE]]:6): [True: 1, False: {{2|1}}]
+    return 0;
+  else
     return 1;
   int j = 1;
 }
diff --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-templates-merge.cpp b/llvm/test/tools/llvm-cov/Inputs/mcdc-templates-merge.cpp
new file mode 100644
index 00000000000000..09c2e0980cca85
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/Inputs/mcdc-templates-merge.cpp
@@ -0,0 +1,54 @@
+#include <cstdio>
+
+template <typename Ty>
+bool ab(Ty a, Ty b) {
+  return (a && b);
+}
+// MERGE: [[@LINE-2]]| 4| return
+// ANY:   [[@LINE-3]]| 2| return
+// ALL:   [[@LINE-4]]| 0| return
+
+// MERGE: MC/DC Coverage for Decision{{[:]}}  50.00%
+// ANY:   MC/DC Coverage for Decision{{[:]}}  50.00%
+// ALL:   MC/DC Coverage for Decision{{[:]}}   0.00%
+
+// CHECK: _Z2abIbEbT_S0_{{[:]}}
+// CHECK: MC/DC Coverage for Decision{{[:]}} 50.00%
+
+// CHECK: _Z2abIxEbT_S0_{{[:]}}
+// CHECK: MC/DC Coverage for Decision{{[:]}} 50.00%
+
+// CHECK: Unexecuted instantiation{{[:]}} _Z2abIdEbT_S0_
+
+template <bool C>
+bool Cab(bool a, bool b) {
+  return (a && b && C);
+}
+// MERGE: [[@LINE-2]]| 4| return
+// ANY:   [[@LINE-3]]| 2| return
+// ALL:   [[@LINE-4]]| 2| return
+
+// MERGE:  MC/DC Coverage for Decision{{[:]}}  50.00%
+// ANY:    MC/DC Coverage for Decision{{[:]}}  50.00%
+// ALL:    MC/DC Coverage for Decision{{[:]}}   0.00%
+
+// CHECK: _Z3CabILb0EEbbb{{[:]}}
+// CHECK:  MC/DC Coverage for Decision{{[:]}} 0.00%
+
+// CHECK: _Z3CabILb1EEbbb{{[:]}}
+// CHECK: MC/DC Coverage for Decision{{[:]}} 50.00%
+
+// CHECK: [[@LINE+1]]| 1|int main
+int main(int argc, char **argv) {
+  printf("%d\n", Cab<false>(false, false));
+  printf("%d\n", Cab<false>(true, true));
+  printf("%d\n", Cab<true>(true, false));
+  printf("%d\n", Cab<true>(true, true));
+  printf("%d\n", ab(false, false));
+  printf("%d\n", ab(true, true));
+  printf("%d\n", ab(1LL, 0LL));
+  printf("%d\n", ab(1LL, 1LL));
+  if (argc == 2)
+    printf("%d\n", ab(0.0, 0.0));
+  return 0;
+}
diff --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-templates-merge.proftext b/llvm/test/tools/llvm-cov/Inputs/mcdc-templates-merge.proftext
new file mode 100644
index 00000000000000..61369462b2fd46
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/Inputs/mcdc-templates-merge.proftext
@@ -0,0 +1,73 @@
+_Z2abIbEbT_S0_
+# Func Hash:
+1550
+# Num Counters:
+3
+# Counter Values:
+2
+1
+1
+# Num Bitmap Bytes:
+$1
+# Bitmap Byte Values:
+0x5
+
+
+_Z2abIxEbT_S0_
+# Func Hash:
+1550
+# Num Counters:
+3
+# Counter Values:
+2
+2
+1
+# Num Bitmap Bytes:
+$1
+# Bitmap Byte Values:
+0x6
+
+
+_Z3CabILb0EEbbb
+# Func Hash:
+99214
+# Num Counters:
+5
+# Counter Values:
+2
+1
+0
+1
+1
+# Num Bitmap Bytes:
+$1
+# Bitmap Byte Values:
+0x5
+
+
+_Z3CabILb1EEbbb
+# Func Hash:
+99214
+# Num Counters:
+5
+# Counter Values:
+2
+1
+1
+2
+1
+# Num Bitmap Bytes:
+$1
+# Bitmap Byte Values:
+0xa
+
+
+main
+# Func Hash:
+175973464
+# Num Counters:
+2
+# Counter Values:
+1
+0
+
diff --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-templates-merge.yaml b/llvm/test/tools/llvm-cov/Inputs/mcdc-templates-merge.yaml
new file mode 100644
index 00000000000000..91d701a2172f82
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/Inputs/mcdc-templates-merge.yaml
@@ -0,0 +1,105 @@
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  OSABI:           ELFOSABI_GNU
+  Type:            ET_REL
+  Machine:         EM_X86_64
+  SectionHeaderStringTable: .strtab
+Sections:
+  - Name:            __llvm_covfun
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_GNU_RETAIN ]
+    AddressAlign:    0x8
+    Content:         FAD58DE7366495DB2500000058247D0A00000000249EC986A505B62F010101010505012A210C020109070010200502000700100500110185808080080501050021
+  - Name:            '__llvm_covfun (1)'
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_GNU_RETAIN ]
+    AddressAlign:    0x8
+    Content:         9CC3B348501DBFE8480000008E83010000000000249EC986A505B62F010103010D0D1105090901181A020201010B000C01000B0011280403000B0016300D02010300000B000C0D0010001130110603020000100011050015001630000A02000000150016
+  - Name:            '__llvm_covfun (2)'
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_GNU_RETAIN ]
+    AddressAlign:    0x8
+    Content:         9873627177A03F8E460000008E83010000000000249EC986A505B62F010102010D0D110901181A020201010B000C01000B0011280403000B0016300D02010300000B000C0D0010001130110603020000100011050015001630090002000000150016
+  - Name:            '__llvm_covfun (3)'
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_GNU_RETAIN ]
+    AddressAlign:    0x8
+    Content:         BF407A207503B266320000000E06000000000000249EC986A505B62F0101020105050906010415020201010B000C280302000B0011300502010200000B000C050010001130090602000000100011
+  - Name:            '__llvm_covfun (4)'
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_GNU_RETAIN ]
+    AddressAlign:    0x8
+    Content:         8A05A22CB467C37D320000000E06000000000000249EC986A505B62F0101020105050906010415020201010B000C280302000B0011300502010200000B000C050010001130090602000000100011
+  - Name:            '__llvm_covfun (5)'
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_GNU_RETAIN ]
+    AddressAlign:    0x8
+    Content:         1700192CAC8F3F26320000000E06000000000000249EC986A505B62F0101020105050906010415020201010B000C280302000B0011300502010200000B000C050010001130090602000000100011
+  - Name:            __llvm_covmap
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_GNU_RETAIN ]
+    AddressAlign:    0x8
+    Content:         000000001D0000000000000006000000021A0000186D6364632D74656D706C617465732D6D657267652E637070000000
+  - Name:            __llvm_prf_names
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_GNU_RETAIN ]
+    AddressAlign:    0x1
+    Content:         51006D61696E015F5A33436162494C62304545626262015F5A33436162494C62314545626262015F5A32616249624562545F53305F015F5A32616249784562545F53305F015F5A32616249644562545F53305F
+  - Type:            SectionHeaderTable
+    Sections:
+      - Name:            .strtab
+      - Name:            __llvm_covfun
+      - Name:            '__llvm_covfun (1)'
+      - Name:            '__llvm_covfun (2)'
+      - Name:            '__llvm_covfun (3)'
+      - Name:            '__llvm_covfun (4)'
+      - Name:            '__llvm_covfun (5)'
+      - Name:            __llvm_covmap
+      - Name:            __llvm_prf_names
+      - Name:            .symtab
+Symbols:
+  - Name:            __llvm_covmap
+    Type:            STT_SECTION
+    Section:         __llvm_covmap
+  - Name:            __llvm_prf_names
+    Type:            STT_SECTION
+    Section:         __llvm_prf_names
+  - Name:            __covrec_DB956436E78DD5FAu
+    Type:            STT_OBJECT
+    Section:         __llvm_covfun
+    Binding:         STB_WEAK
+    Size:            0x41
+    Other:           [ STV_HIDDEN ]
+  - Name:            __covrec_E8BF1D5048B3C39Cu
+    Type:            STT_OBJECT
+    Section:         '__llvm_covfun (1)'
+    Binding:         STB_WEAK
+    Size:            0x64
+    Other:           [ STV_HIDDEN ]
+  - Name:            __covrec_8E3FA07771627398u
+    Type:            STT_OBJECT
+    Section:         '__llvm_covfun (2)'
+    Binding:         STB_WEAK
+    Size:            0x62
+    Other:           [ STV_HIDDEN ]
+  - Name:            __covrec_66B20375207A40BFu
+    Type:            STT_OBJECT
+    Section:         '__llvm_covfun (3)'
+    Binding:         STB_WEAK
+    Size:            0x4E
+    Other:           [ STV_HIDDEN ]
+  - Name:            __covrec_7DC367B42CA2058Au
+    Type:            STT_OBJECT
+    Section:         '__llvm_covfun (4)'
+    Binding:         STB_WEAK
+    Size:            0x4E
+    Other:           [ STV_HIDDEN ]
+  - Name:            __covrec_263F8FAC2C190017u
+    Type:            STT_OBJECT
+    Section:         '__llvm_covfun (5)'
+    Binding:         STB_WEAK
+    Size:            0x4E
+    Other:           [ STV_HIDDEN ]
+...
diff --git a/llvm/test/tools/llvm-cov/branch-export-json.test b/llvm/test/tools/llvm-cov/branch-export-json.test
index 4278482c6d870e..77e2bab7d9eb33 100644
--- a/llvm/test/tools/llvm-cov/branch-export-json.test
+++ b/llvm/test/tools/llvm-cov/branch-export-json.test
@@ -46,4 +46,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}
+// MACROS: {"count":16,"covered":6,"notcovered":10,"percent":37.5}
diff --git a/llvm/test/tools/llvm-cov/branch-export-lcov.test b/llvm/test/tools/llvm-cov/branch-export-lcov.test
index fe43dd66de8d04..444277435d60d6 100644
--- a/llvm/test/tools/llvm-cov/branch-export-lcov.test
+++ b/llvm/test/tools/llvm-cov/branch-export-lcov.test
@@ -71,8 +71,8 @@
 // MACROS-COUNT-10: BRDA:37
 // MACROS-NOT: BRDA:37
 // MACROS-NOT: BRDA
-// MACROS: BRF:40
-// MACROS: BRH:24
+// MACROS: BRF:16
+// MACROS: BRH:6
 
 // NOBRANCH-NOT: BRDA
 // NOBRANCH-NOT: BRF
diff --git a/llvm/test/tools/llvm-cov/branch-macros.test b/llvm/test/tools/llvm-cov/branch-macros.test
index b892aba0a044a3..1023d7cc47d4d9 100644
--- a/llvm/test/tools/llvm-cov/branch-macros.test
+++ b/llvm/test/tools/llvm-cov/branch-macros.test
@@ -1,5 +1,8 @@
 // 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/Inputs | FileCheck %S/Inputs/branch-macros.cpp -check-prefixes=CHECK,BRCOV -D#C=999
+// RUN: llvm-cov report --show-branch-summary %S/Inputs/branch-macros.o32l -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S/Inputs %S/Inputs/branch-macros.cpp | FileCheck %s -check-prefix=REPORT
+// RUN: llvm-cov report --show-branch-summary %S/Inputs/branch-macros.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/branch-macros.cpp | FileCheck %s -check-prefix=FILE
+
 // RUN: llvm-cov show --binary-counters=true --show-expansions --show-branches=count %S/Inputs/branch-macros.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs | FileCheck %S/Inputs/branch-macros.cpp -check-prefixes=CHECK,BRCOV -D#C=1
 // RUN: llvm-cov report --show-branch-summary %S/Inputs/branch-macros.o32l -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S/Inputs %S/Inputs/branch-macros.cpp | FileCheck %s -check-prefix=REPORT
 
@@ -14,3 +17,9 @@
 // REPORT-NEXT: main                              1       0 100.00%         6       0 100.00%         0       0   0.00%
 // REPORT-NEXT: ---
 // REPORT-NEXT: TOTAL                            16       4  75.00%        32       0 100.00%        40      16  60.00%
+
+// FILE:      Filename          Regions Missed Regions  Cover   Functions Missed Functions Executed      Lines  Missed Lines    Cover   Branches Missed Branches Cover
+// FILE-NEXT: ---
+// FILE-NEXT: branch-macros.cpp      28              5  82.14%          3                0  100.00%         39             0   100.00%        16              10 37.50%
+// FILE-NEXT: ---
+// FILE-NEXT: TOTAL                  28              5  82.14%          3                0  100.00%         39             0   100.00%        16              10 37.50%
diff --git a/llvm/test/tools/llvm-cov/branch-templates.test b/llvm/test/tools/llvm-cov/branch-templates.test
index 594a3ca533678b..c4fc9cafbfd80e 100644
--- a/llvm/test/tools/llvm-cov/branch-templates.test
+++ b/llvm/test/tools/llvm-cov/branch-templates.test
@@ -26,6 +26,6 @@
 
 // REPORTFILE:      Filename                 Regions    Missed Regions     Cover   Functions  Missed Functions  Executed       Lines      Missed Lines     Cover    Branches   Missed Branches     Cover
 // REPORTFILE-NEXT: ---
-// REPORTFILE-NEXT: branch-templates.cpp          12                 2    83.33%           2                 0   100.00%          17                 3    82.35%          12                 6    50.00%
+// REPORTFILE-NEXT: branch-templates.cpp          12                 2    83.33%           2                 0   100.00%          17                 3    82.35%           8                 3    62.50%
 // REPORTFILE-NEXT: ---
-// REPORTFILE-NEXT: TOTAL                         12                 2    83.33%           2                 0   100.00%          17                 3    82.35%          12                 6    50.00%
+// REPORTFILE-NEXT: TOTAL                         12                 2    83.33%           2                 0   100.00%          17                 3    82.35%           8                 3    62.50%
diff --git a/llvm/test/tools/llvm-cov/mcdc-templates-merge.test b/llvm/test/tools/llvm-cov/mcdc-templates-merge.test
new file mode 100644
index 00000000000000..21c5458edfa846
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/mcdc-templates-merge.test
@@ -0,0 +1,41 @@
+# Test `merge-instantiations=merge/any/all`
+
+RUN: yaml2obj %S/Inputs/mcdc-templates-merge.yaml -o %t.o
+RUN: llvm-profdata merge %S/Inputs/mcdc-templates-merge.proftext -o %t.profdata
+
+RUN: llvm-cov show -merge-instantiations=merge --show-mcdc -show-instantiations=true %t.o -instr-profile %t.profdata -path-equivalence=.,%S/Inputs | FileCheck %S/Inputs/mcdc-templates-merge.cpp --check-prefixes=CHECK,MERGE
+RUN: llvm-cov show -merge-instantiations=any --show-mcdc -show-instantiations=true %t.o -instr-profile %t.profdata -path-equivalence=.,%S/Inputs | FileCheck %S/Inputs/mcdc-templates-merge.cpp --check-prefixes=CHECK,ANY
+RUN: llvm-cov show -merge-instantiations=all --show-mcdc -show-instantiations=true %t.o -instr-profile %t.profdata -path-equivalence=.,%S/Inputs | FileCheck %S/Inputs/mcdc-templates-merge.cpp --check-prefixes=CHECK,ALL
+
+RUN: llvm-cov report -merge-instantiations=merge --show-mcdc-summary %t.o -instr-profile %t.profdata -path-equivalence=.,%S/Inputs | FileCheck %s -check-prefixes=REPORT,MERGE
+RUN: llvm-cov report -merge-instantiations=any --show-mcdc-summary %t.o -instr-profile %t.profdata -path-equivalence=.,%S/Inputs | FileCheck %s -check-prefixes=REPORT,ANY
+RUN: llvm-cov report -merge-instantiations=all --show-mcdc-summary %t.o -instr-profile %t.profdata -path-equivalence=.,%S/Inputs | FileCheck %s -check-prefixes=REPORT,ALL
+
+REPORT: mcdc-templates-merge.cpp
+
+# Regions
+MERGE:      10  1  90.00%
+ANY:        10  1  90.00%
+ALL:        10  4  60.00%
+
+# Functions
+MERGE:       3  0 100.00%
+ANY:         3  0 100.00%
+ALL:         3  0 100.00%
+
+# Lines
+MERGE:      19  1  94.74%
+ANY:        19  1  94.74%
+ALL:        19  4  78.95%
+
+# Branches
+MERGE:      12  1  91.67%
+ANY:        11  1  90.91%
+ALL:        12  7  41.67%
+
+# MC/DC Conditions
+MERGE:       4  2  50.00%
+ANY:         4  2  50.00%
+ALL:         4  4   0.00%
+
+REPORT: TOTAL
diff --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp
index 2d6da7efc69798..4ae4fff64ad4bc 100644
--- a/llvm/tools/llvm-cov/CodeCoverage.cpp
+++ b/llvm/tools/llvm-cov/CodeCoverage.cpp
@@ -395,7 +395,8 @@ CodeCoverageTool::createSourceFileView(StringRef SourceFile,
   auto SourceBuffer = getSourceFile(SourceFile);
   if (!SourceBuffer)
     return nullptr;
-  auto FileCoverage = Coverage.getCoverageForFile(SourceFile);
+  auto FileCoverage =
+      Coverage.getCoverageForFile(SourceFile, ViewOpts.MergeStrategyOpts);
   if (FileCoverage.empty())
     return nullptr;
 
@@ -795,6 +796,14 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
       "check-binary-ids", cl::desc("Fail if an object couldn't be found for a "
                                    "binary ID in the profile"));
 
+  cl::opt<MergeStrategy> MergeStrategyOpts(
+      "merge-instantiations", cl::desc("Merge instantiations"),
+      cl::values(
+          clEnumValN(MergeStrategy::Merge, "merge", "Merge entries by adding"),
+          clEnumValN(MergeStrategy::Any, "any", "Pick up any better entries"),
+          clEnumValN(MergeStrategy::All, "all", "Pick up the worst entries")),
+      cl::init(MergeStrategy::Merge));
+
   auto commandLineParser = [&, this](int argc, const char **argv) -> int {
     cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
     ViewOpts.Debug = DebugDump;
@@ -951,6 +960,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
     ViewOpts.ExportSummaryOnly = SummaryOnly;
     ViewOpts.NumThreads = NumThreads;
     ViewOpts.CompilationDirectory = CompilationDirectory;
+    ViewOpts.MergeStrategyOpts = MergeStrategyOpts;
 
     return 0;
   };
diff --git a/llvm/tools/llvm-cov/CoverageReport.cpp b/llvm/tools/llvm-cov/CoverageReport.cpp
index 0046b968756dd0..9215e2421f5145 100644
--- a/llvm/tools/llvm-cov/CoverageReport.cpp
+++ b/llvm/tools/llvm-cov/CoverageReport.cpp
@@ -480,8 +480,8 @@ void CoverageReport::prepareSingleFileReport(const StringRef Filename,
         /*Covered=*/Group.getTotalExecutionCount() > 0);
   }
 
-  auto FileCoverage =
-      Coverage->getCoverageForFile(Filename, FilteredOutFunctions);
+  auto FileCoverage = Coverage->getCoverageForFile(
+      Filename, Options.MergeStrategyOpts, FilteredOutFunctions);
   if (FileCoverage.empty())
     return;
 
diff --git a/llvm/tools/llvm-cov/CoverageViewOptions.h b/llvm/tools/llvm-cov/CoverageViewOptions.h
index 81e69c3814e306..d2135a19e11f69 100644
--- a/llvm/tools/llvm-cov/CoverageViewOptions.h
+++ b/llvm/tools/llvm-cov/CoverageViewOptions.h
@@ -11,6 +11,7 @@
 
 #include "RenderingSupport.h"
 #include "llvm/Config/llvm-config.h"
+#include "llvm/ProfileData/Coverage/CoverageMapping.h"
 #include <vector>
 
 namespace llvm {
@@ -48,6 +49,7 @@ struct CoverageViewOptions {
   bool BinaryCounters;
   OutputFormat Format;
   BranchOutputType ShowBranches;
+  coverage::MergeStrategy MergeStrategyOpts;
   std::string ShowOutputDirectory;
   std::vector<std::string> DemanglerOpts;
   uint32_t TabSize;
diff --git a/llvm/tools/llvm-cov/SourceCoverageView.cpp b/llvm/tools/llvm-cov/SourceCoverageView.cpp
index dfecddfaf4143f..1ef1952e691c9a 100644
--- a/llvm/tools/llvm-cov/SourceCoverageView.cpp
+++ b/llvm/tools/llvm-cov/SourceCoverageView.cpp
@@ -207,8 +207,8 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
   // through them while we iterate lines.
   llvm::stable_sort(ExpansionSubViews);
   llvm::stable_sort(InstantiationSubViews);
-  llvm::stable_sort(BranchSubViews);
-  llvm::stable_sort(MCDCSubViews);
+  // BranchSubViews is sorted.
+  // MCDCSubViews is sorted.
   auto NextESV = ExpansionSubViews.begin();
   auto EndESV = ExpansionSubViews.end();
   auto NextISV = InstantiationSubViews.begin();

>From 5cf0511fdc928f3f1ae3bdae9d20612ad4978740 Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Fri, 10 Jan 2025 23:35:04 +0900
Subject: [PATCH 2/2] <numeric> std::iota

---
 llvm/lib/ProfileData/Coverage/CoverageMapping.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index 1a00519806b8c4..4c99e81d4f6d01 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -36,6 +36,7 @@
 #include <iterator>
 #include <map>
 #include <memory>
+#include <numeric>
 #include <optional>
 #include <stack>
 #include <string>



More information about the llvm-branch-commits mailing list