llvm-cov: Combine segments that cover the same location

If we have multiple coverage counts for the same segment, we need to
add them up rather than arbitrarily choosing one. This fixes that and
adds a test with template instantiations to exercise it.

llvm-svn: 218432
diff --git a/llvm/include/llvm/ProfileData/CoverageMapping.h b/llvm/include/llvm/ProfileData/CoverageMapping.h
index a7c124a..15445b5 100644
--- a/llvm/include/llvm/ProfileData/CoverageMapping.h
+++ b/llvm/include/llvm/ProfileData/CoverageMapping.h
@@ -274,6 +274,7 @@
     Count = NewCount;
     HasCount = true;
   }
+  void addCount(uint64_t NewCount) { setCount(Count + NewCount); }
 };
 
 /// \brief Coverage information to be processed or displayed.
diff --git a/llvm/lib/ProfileData/CoverageMapping.cpp b/llvm/lib/ProfileData/CoverageMapping.cpp
index 540e643..06a950c 100644
--- a/llvm/lib/ProfileData/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/CoverageMapping.cpp
@@ -19,11 +19,14 @@
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ProfileData/CoverageMappingReader.h"
 #include "llvm/ProfileData/InstrProfReader.h"
+#include "llvm/Support/Debug.h"
 #include "llvm/Support/ErrorHandling.h"
 
 using namespace llvm;
 using namespace coverage;
 
+#define DEBUG_TYPE "coverage-mapping"
+
 CounterExpressionBuilder::CounterExpressionBuilder(unsigned NumCounterValues) {
   Terms.resize(NumCounterValues);
 }
@@ -228,6 +231,7 @@
 
   /// Start a segment with no count specified.
   void startSegment(unsigned Line, unsigned Col) {
+    DEBUG(dbgs() << "Top level segment at " << Line << ":" << Col << "\n");
     Segments.emplace_back(Line, Col, /*IsRegionEntry=*/false);
   }
 
@@ -242,9 +246,13 @@
       Segments.emplace_back(Line, Col, IsRegionEntry);
       S = Segments.back();
     }
+    DEBUG(dbgs() << "Segment at " << Line << ":" << Col);
     // Set this region's count.
-    if (Region.Kind != coverage::CounterMappingRegion::SkippedRegion)
+    if (Region.Kind != coverage::CounterMappingRegion::SkippedRegion) {
+      DEBUG(dbgs() << " with count " << Region.ExecutionCount);
       Segments.back().setCount(Region.ExecutionCount);
+    }
+    DEBUG(dbgs() << "\n");
   }
 
   /// Start a segment for the given region.
@@ -272,9 +280,15 @@
       while (!ActiveRegions.empty() &&
              ActiveRegions.back()->endLoc() <= Region.startLoc())
         popRegion();
-      // Add this region to the stack.
-      ActiveRegions.push_back(&Region);
-      startSegment(Region);
+      if (Segments.size() && Segments.back().Line == Region.LineStart &&
+          Segments.back().Col == Region.ColumnStart) {
+        if (Region.Kind != coverage::CounterMappingRegion::SkippedRegion)
+          Segments.back().addCount(Region.ExecutionCount);
+      } else {
+        // Add this region to the stack.
+        ActiveRegions.push_back(&Region);
+        startSegment(Region);
+      }
     }
     // Pop any regions that are left in the stack.
     while (!ActiveRegions.empty())
diff --git a/llvm/test/tools/llvm-cov/Inputs/templateInstantiations.covmapping b/llvm/test/tools/llvm-cov/Inputs/templateInstantiations.covmapping
new file mode 100644
index 0000000..d243736
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/Inputs/templateInstantiations.covmapping
Binary files differ
diff --git a/llvm/test/tools/llvm-cov/Inputs/templateInstantiations.profdata b/llvm/test/tools/llvm-cov/Inputs/templateInstantiations.profdata
new file mode 100644
index 0000000..6ccf526
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/Inputs/templateInstantiations.profdata
Binary files differ
diff --git a/llvm/test/tools/llvm-cov/showTemplateInstantiations.cpp b/llvm/test/tools/llvm-cov/showTemplateInstantiations.cpp
new file mode 100644
index 0000000..2b72d83c
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/showTemplateInstantiations.cpp
@@ -0,0 +1,43 @@
+// RUN: llvm-cov show %S/Inputs/templateInstantiations.covmapping -instr-profile %S/Inputs/templateInstantiations.profdata -no-colors -filename-equivalence %s | FileCheck -check-prefix=CHECK -check-prefix=ALL %s
+// RUN: llvm-cov show %S/Inputs/templateInstantiations.covmapping -instr-profile %S/Inputs/templateInstantiations.profdata -no-colors -filename-equivalence -name=_Z4funcIbEiT_ %s | FileCheck -check-prefix=CHECK -check-prefix=FILTER %s
+
+// before coverage   // WHOLE-FILE:   | [[@LINE]]|// before
+                     // FILTER-NOT:   | [[@LINE-1]]|// before
+template<typename T> // ALL:          | [[@LINE]]|template<typename T>
+int func(T x) {      // ALL-NEXT:    2| [[@LINE]]|int func(T x) {
+  if(x)              // ALL-NEXT:    2| [[@LINE]]|  if(x)
+    return 0;        // ALL-NEXT:    1| [[@LINE]]|    return 0;
+  else               // ALL-NEXT:    1| [[@LINE]]|  else
+    return 1;        // ALL-NEXT:    1| [[@LINE]]|    return 1;
+  int j = 1;         // ALL-NEXT:    0| [[@LINE]]|  int j = 1;
+}                    // ALL-NEXT:    1| [[@LINE]]|}
+
+                     // CHECK:       {{^ *(\| )?}}_Z4funcIbEiT_:
+                     // CHECK-NEXT:  1| [[@LINE-9]]|int func(T x) {
+                     // CHECK-NEXT:  1| [[@LINE-9]]|  if(x)
+                     // CHECK-NEXT:  1| [[@LINE-9]]|    return 0;
+                     // CHECK-NEXT:  1| [[@LINE-9]]|  else
+                     // CHECK-NEXT:  0| [[@LINE-9]]|    return 1;
+                     // CHECK-NEXT:  0| [[@LINE-9]]|  int j = 1;
+                     // CHECK-NEXT:  1| [[@LINE-9]]|}
+
+                     // ALL:         {{^ *}}| _Z4funcIiEiT_:
+                     // FILTER-NOT:  {{^ *(\| )?}} _Z4funcIiEiT_:
+                     // ALL-NEXT:    1| [[@LINE-19]]|int func(T x) {
+                     // ALL-NEXT:    1| [[@LINE-19]]|  if(x)
+                     // ALL-NEXT:    0| [[@LINE-19]]|    return 0;
+                     // ALL-NEXT:    1| [[@LINE-19]]|  else
+                     // ALL-NEXT:    1| [[@LINE-19]]|    return 1;
+                     // ALL-NEXT:    0| [[@LINE-19]]|  int j = 1;
+                     // ALL-NEXT:    1| [[@LINE-19]]|}
+
+int main() {         // ALL:         1| [[@LINE]]|int main() {
+  func<int>(0);      // ALL-NEXT:    1| [[@LINE]]|  func<int>(0);
+  func<bool>(true);  // ALL-NEXT:    1| [[@LINE]]|  func<bool>(true);
+  return 0;          // ALL-NEXT:    1| [[@LINE]]|  return 0;
+}                    // ALL-NEXT:    1| [[@LINE]]|}
+// after coverage    // ALL-NEXT:     | [[@LINE]]|// after
+                     // FILTER-NOT:   | [[@LINE-1]]|// after
+
+// llvm-cov doesn't work on big endian yet
+// XFAIL: powerpc64-, s390x, mips-, mips64-, sparc