[clang] [llvm] [Coverage][MC/DC] Show uncoverable and unreachable conditions (PR #94137)

via cfe-commits cfe-commits at lists.llvm.org
Sat Jun 1 20:09:33 PDT 2024


https://github.com/Lambdaris created https://github.com/llvm/llvm-project/pull/94137

To close #93560 .

# Changes

1. Clang only sets the counter in folded branch to `Zero`. And llvm-cov shows a branch as folded as long as either of its counters is `Zero`.
2. Add two additional results, `uncoverable` and `unreachable` to MCDC conditions. `uncoverable` means the condition can not effect value of the decision, while `unreachable` means the condition is always short-circuited. Since both the two kinds of conditions is by no means covered, they are excluded from MCDC and informed to users. The scheme to identify such conditions is described at this [comment](https://github.com/llvm/llvm-project/issues/93560#issuecomment-2142551335).

>From d2fe89a625e84b585362e8800d7fac2d034ec85a Mon Sep 17 00:00:00 2001
From: Lambdaris <Lambdaris at outlook.com>
Date: Sun, 2 Jun 2024 10:19:31 +0800
Subject: [PATCH 1/2] [coverage] Mark branches with either counter is zero as
 folded

---
 clang/lib/CodeGen/CoverageMappingGen.cpp      | 27 +++++++++----
 .../CoverageMapping/branch-constfolded.cpp    | 40 +++++++++----------
 clang/test/CoverageMapping/if.cpp             |  4 +-
 clang/test/CoverageMapping/macro-expansion.c  | 10 ++---
 .../test/CoverageMapping/mcdc-scratch-space.c |  4 +-
 .../CoverageMapping/mcdc-system-headers.cpp   |  4 +-
 .../ProfileData/Coverage/CoverageMapping.h    |  4 +-
 .../ProfileData/Coverage/CoverageMapping.cpp  |  4 +-
 .../test/tools/llvm-cov/branch-c-general.test | 12 +++---
 9 files changed, 61 insertions(+), 48 deletions(-)

diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp
index 6ce2d32dd292e..37e6543795558 100644
--- a/clang/lib/CodeGen/CoverageMappingGen.cpp
+++ b/clang/lib/CodeGen/CoverageMappingGen.cpp
@@ -1084,9 +1084,13 @@ struct CounterCoverageMappingBuilder
   }
 
   /// Determine whether the given condition can be constant folded.
-  bool ConditionFoldsToBool(const Expr *Cond) {
+  bool ConditionFoldsToBool(const Expr *Cond, bool &ResultBool) {
     Expr::EvalResult Result;
-    return (Cond->EvaluateAsInt(Result, CVM.getCodeGenModule().getContext()));
+    if (Cond->EvaluateAsInt(Result, CVM.getCodeGenModule().getContext())) {
+      ResultBool = Result.Val.getInt().getBoolValue();
+      return true;
+    }
+    return false;
   }
 
   /// Create a Branch Region around an instrumentable condition for coverage
@@ -1113,15 +1117,22 @@ struct CounterCoverageMappingBuilder
         BranchParams = mcdc::BranchParameters{ID, Conds};
 
       // 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.
+      // will be removed. Create a region with the relative counter 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(), BranchParams));
-      else
+      bool ConstantBool = false;
+      if (ConditionFoldsToBool(C, ConstantBool)) {
+        if (ConstantBool) {
+          popRegions(pushRegion(TrueCnt, getStart(C), getEnd(C),
+                                Counter::getZero(), BranchParams));
+        } else {
+          popRegions(pushRegion(Counter::getZero(), getStart(C), getEnd(C),
+                                FalseCnt, BranchParams));
+        }
+
+      } else
         // Otherwise, create a region with the True counter and False counter.
         popRegions(pushRegion(TrueCnt, getStart(C), getEnd(C), FalseCnt,
                               BranchParams));
diff --git a/clang/test/CoverageMapping/branch-constfolded.cpp b/clang/test/CoverageMapping/branch-constfolded.cpp
index c8755d5d752b6..1e1639ba796df 100644
--- a/clang/test/CoverageMapping/branch-constfolded.cpp
+++ b/clang/test/CoverageMapping/branch-constfolded.cpp
@@ -5,94 +5,94 @@
 
 // CHECK-LABEL: _Z6fand_0b:
 bool fand_0(bool a) {      // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:20 = M:0, C:2
-  return false && a;       // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, 0
+  return false && a;       // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, (#0 - #1)
 }                          // CHECK: Branch,File 0, [[@LINE-1]]:19 -> [[@LINE-1]]:20 = #2, (#1 - #2)
 
 // CHECK-LABEL: _Z6fand_1b:
 bool fand_1(bool a) {      // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:19 = M:0, C:2
   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: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:19 = #2, 0
 
 // CHECK-LABEL: _Z6fand_2bb:
 bool fand_2(bool a, bool b) {// MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:25 = M:0, C:3
-  return false && a && b;  // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, 0
+  return false && a && b;  // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, (#0 - #3)
 }                          // 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) {// MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:24 = M:0, C:3
   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-1]]:15 -> [[@LINE-1]]:19 = #4, 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) {// MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:25 = M:0, C:3
   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: Branch,File 0, [[@LINE-2]]:20 -> [[@LINE-2]]:25 = 0, (#1 - #2)
 
 // CHECK-LABEL: _Z6fand_5b:
 bool fand_5(bool a) {      // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:23 = M:0, C:2
-  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
+  return false && true;    // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, (#0 - #1)
+}                          // CHECK: Branch,File 0, [[@LINE-1]]:19 -> [[@LINE-1]]:23 = #2, 0
 
 // CHECK-LABEL: _Z6fand_6b:
 bool fand_6(bool a) {      // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:19 = M:0, C:2
-  return true && a;        // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = 0, 0
+  return true && a;        // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = #1, 0
 }                          // CHECK: Branch,File 0, [[@LINE-1]]:18 -> [[@LINE-1]]:19 = #2, (#1 - #2)
 
 // CHECK-LABEL: _Z6fand_7b:
 bool fand_7(bool a) {      // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:20 = M:0, C:2
   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: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:20 = 0, (#1 - #2)
 
 // CHECK-LABEL: _Z5for_0b:
 bool for_0(bool a) {       // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:19 = M:0, C:2
-  return true || a;        // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = 0, 0
+  return true || a;        // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = (#0 - #1), 0
 }                          // CHECK: Branch,File 0, [[@LINE-1]]:18 -> [[@LINE-1]]:19 = (#1 - #2), #2
 
 // CHECK-LABEL: _Z5for_1b:
 bool for_1(bool a) {       // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:20 = M:0, C:2
   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: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:20 = 0, #2
 
 // CHECK-LABEL: _Z5for_2bb:
 bool for_2(bool a, bool b) {// MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:24 = M:0, C:3
-  return true || a || b;   // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = 0, 0
+  return true || a || b;   // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = (#0 - #3), 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) {// MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:25 = M:0, C:3
   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-1]]:15 -> [[@LINE-1]]:20 = 0, #4
                            // CHECK: Branch,File 0, [[@LINE-2]]:24 -> [[@LINE-2]]:25 = (#1 - #2), #2
 
 // CHECK-LABEL: _Z5for_4bb:
 bool for_4(bool a, bool b) {// MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:24 = M:0, C:3
   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: Branch,File 0, [[@LINE-2]]:20 -> [[@LINE-2]]:24 = (#1 - #2), 0
 
 // CHECK-LABEL: _Z5for_5b:
 bool for_5(bool a) {       // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:23 = M:0, C:2
-  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
+  return true || false;    // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = (#0 - #1), 0
+}                          // CHECK: Branch,File 0, [[@LINE-1]]:18 -> [[@LINE-1]]:23 = 0, #2
 
 // CHECK-LABEL: _Z5for_6b:
 bool for_6(bool a) {       // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:20 = M:0, C:2
-  return false || a;       // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, 0
+  return false || a;       // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, #1
 }                          // CHECK: Branch,File 0, [[@LINE-1]]:19 -> [[@LINE-1]]:20 = (#1 - #2), #2
 
 // CHECK-LABEL: _Z5for_7b:
 bool for_7(bool a) {       // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:19 = M:0, C:2
   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
+}                          // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:19 = (#1 - #2), 0
 
 // CHECK-LABEL: _Z5for_8b:
 bool for_8(bool a) {       // MCDC: Decision,File 0, [[@LINE+3]]:7 -> [[@LINE+3]]:20 = M:0, C:2
-                           // CHECK: Branch,File 0, [[@LINE+2]]:7 -> [[@LINE+2]]:11 = 0, 0
-                           // CHECK: Branch,File 0, [[@LINE+1]]:15 -> [[@LINE+1]]:20 = 0, 0
+                           // CHECK: Branch,File 0, [[@LINE+2]]:7 -> [[@LINE+2]]:11 = #2, 0
+                           // CHECK: Branch,File 0, [[@LINE+1]]:15 -> [[@LINE+1]]:20 = 0, (#2 - #3)
   if (true && false)
       return true;
   else
diff --git a/clang/test/CoverageMapping/if.cpp b/clang/test/CoverageMapping/if.cpp
index 445cdfc20e2af..b6fd525e930f9 100644
--- a/clang/test/CoverageMapping/if.cpp
+++ b/clang/test/CoverageMapping/if.cpp
@@ -14,7 +14,7 @@ struct S {
 // CHECK-LABEL: _Z3foov:
                                 // CHECK-NEXT: [[@LINE+3]]:12 -> [[@LINE+8]]:2 = #0
                                 // CHECK-NEXT: [[@LINE+3]]:15 -> [[@LINE+3]]:19 = #0
-                                // CHECK-NEXT: Branch,File 0, [[@LINE+2]]:15 -> [[@LINE+2]]:19 = 0, 0
+                                // CHECK-NEXT: Branch,File 0, [[@LINE+2]]:15 -> [[@LINE+2]]:19 = #2, 0
 void foo() {                    // CHECK-NEXT: Gap,File 0, [[@LINE+1]]:21 -> [[@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)
@@ -168,7 +168,7 @@ int main() {                    // CHECK: File 0, [[@LINE]]:12 -> {{[0-9]+}}:2 =
   // GH-45481
   S s;                    
   s.the_prop = 0? 1 : 2;        // CHECK-NEXT: File 0, [[@LINE]]:16 -> [[@LINE]]:17 = #0
-                                // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:16 -> [[@LINE-1]]:17 = 0, 0
+                                // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:16 -> [[@LINE-1]]:17 = 0, (#0 - #7)
                                 // CHECK-NEXT: Gap,File 0, [[@LINE-2]]:18 -> [[@LINE-2]]:19 = #7
                                 // CHECK-NEXT: File 0, [[@LINE-3]]:19 -> [[@LINE-3]]:20 = #7
                                 // CHECK-NEXT: File 0, [[@LINE-4]]:23 -> [[@LINE-4]]:24 = (#0 - #7)
diff --git a/clang/test/CoverageMapping/macro-expansion.c b/clang/test/CoverageMapping/macro-expansion.c
index ad71fb15eda42..4cd2c93437193 100644
--- a/clang/test/CoverageMapping/macro-expansion.c
+++ b/clang/test/CoverageMapping/macro-expansion.c
@@ -4,29 +4,29 @@
 // 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: Branch,File 1, [[@LINE+4]]:21 -> [[@LINE+4]]:22 = 0, ((#0 + #2) - #3)
 // 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
+// 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+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: 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
+// 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+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
+// 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+4]]:15 -> [[@LINE+4]]:27 = #0
 // CHECK-NEXT: File 5, [[@LINE+3]]:16 -> [[@LINE+3]]:19 = #0
diff --git a/clang/test/CoverageMapping/mcdc-scratch-space.c b/clang/test/CoverageMapping/mcdc-scratch-space.c
index 2b5b12d9dcad6..1b22bb7e421b6 100644
--- a/clang/test/CoverageMapping/mcdc-scratch-space.c
+++ b/clang/test/CoverageMapping/mcdc-scratch-space.c
@@ -3,7 +3,7 @@
 // CHECK: builtin_macro0:
 int builtin_macro0(int a) {
   // CHECK: Decision,File 0, [[@LINE+1]]:11 -> [[@LINE+2]]:15 = M:0, C:2
-  return (__LINE__ // CHECK: Branch,File 0, [[@LINE]]:11 -> [[@LINE]]:11 = 0, 0 [1,2,0]
+  return (__LINE__ // CHECK: Branch,File 0, [[@LINE]]:11 -> [[@LINE]]:11 = #1, 0 [1,2,0]
           && a); //   CHECK: Branch,File 0, [[@LINE]]:14 -> [[@LINE]]:15 = #2, (#1 - #2) [2,0,0]
 }
 
@@ -11,7 +11,7 @@ int builtin_macro0(int a) {
 int builtin_macro1(int a) {
   // CHECK: Decision,File 0, [[@LINE+1]]:11 -> [[@LINE+2]]:22 = M:0, C:2
   return (a // CHECK: Branch,File 0, [[@LINE]]:11 -> [[@LINE]]:12 = (#0 - #1), #1 [1,0,2]
-          || __LINE__); // CHECK: Branch,File 0, [[@LINE]]:14 -> [[@LINE]]:14 = 0, 0 [2,0,0]
+          || __LINE__); // CHECK: Branch,File 0, [[@LINE]]:14 -> [[@LINE]]:14 = (#1 - #2), 0 [2,0,0]
 }
 
 #define PRE(x) pre_##x
diff --git a/clang/test/CoverageMapping/mcdc-system-headers.cpp b/clang/test/CoverageMapping/mcdc-system-headers.cpp
index 4dfbb17c2bba8..7a867be5c9e6c 100644
--- a/clang/test/CoverageMapping/mcdc-system-headers.cpp
+++ b/clang/test/CoverageMapping/mcdc-system-headers.cpp
@@ -17,10 +17,10 @@
 int func0(int a) {
   // CHECK: Decision,File 0, [[@LINE+3]]:11 -> [[@LINE+3]]:21 = M:0, C:2
   // W_SYS: Expansion,File 0, [[@LINE+2]]:11 -> [[@LINE+2]]:16 = #0 (Expanded file = 1)
-  // X_SYS: Branch,File 0, [[@LINE+1]]:11 -> [[@LINE+1]]:11 = 0, 0 [1,2,0]
+  // X_SYS: Branch,File 0, [[@LINE+1]]:11 -> [[@LINE+1]]:11 = #1, 0 [1,2,0]
   return (CONST && a);
   // CHECK: Branch,File 0, [[@LINE-1]]:20 -> [[@LINE-1]]:21 = #2, (#1 - #2) [2,0,0]
-  // W_SYS: Branch,File 1, [[@LINE-16]]:15 -> [[@LINE-16]]:17 = 0, 0 [1,2,0]
+  // W_SYS: Branch,File 1, [[@LINE-16]]:15 -> [[@LINE-16]]:17 = #1, 0 [1,2,0]
 }
 
 // CHECK: _Z5func1ii:
diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
index da03104045249..c0458b93d1343 100644
--- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
+++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
@@ -718,9 +718,9 @@ struct FunctionRecord {
         Region.Kind == CounterMappingRegion::MCDCBranchRegion) {
       CountedBranchRegions.emplace_back(Region, Count, FalseCount,
                                         HasSingleByteCoverage);
-      // If both counters are hard-coded to zero, then this region represents a
+      // If either counter is hard-coded to zero, then this region represents a
       // constant-folded branch.
-      if (Region.Count.isZero() && Region.FalseCount.isZero())
+      if (Region.Count.isZero() || Region.FalseCount.isZero())
         CountedBranchRegions.back().Folded = true;
       return;
     }
diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index 8c81bbe8e9c4e..e5df21a3bcf7d 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -500,7 +500,7 @@ class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder {
       const auto &BranchParams = B->getBranchParams();
       PosToID[I] = BranchParams.ID;
       CondLoc[I] = B->startLoc();
-      Folded[I++] = (B->Count.isZero() && B->FalseCount.isZero());
+      Folded[I++] = (B->Count.isZero() || B->FalseCount.isZero());
     }
 
     // Using Profile Bitmap from runtime, mark the executed test vectors.
@@ -605,6 +605,8 @@ static unsigned getMaxCounterID(const CounterMappingContext &Ctx,
   unsigned MaxCounterID = 0;
   for (const auto &Region : Record.MappingRegions) {
     MaxCounterID = std::max(MaxCounterID, Ctx.getMaxCounterID(Region.Count));
+    MaxCounterID =
+        std::max(MaxCounterID, Ctx.getMaxCounterID(Region.FalseCount));
   }
   return MaxCounterID;
 }
diff --git a/llvm/test/tools/llvm-cov/branch-c-general.test b/llvm/test/tools/llvm-cov/branch-c-general.test
index 9b5889babde36..419e92d3fa521 100644
--- a/llvm/test/tools/llvm-cov/branch-c-general.test
+++ b/llvm/test/tools/llvm-cov/branch-c-general.test
@@ -47,7 +47,7 @@
 // CHECK: Branch (103:9): [True: 9, False: 1]
 
 // CHECK: switches()
-// CHECK: Branch (113:3): [True: 1, False: 0]
+// CHECK: Branch (113:3): [Folded - Ignored]
 // CHECK: Branch (117:63): [True: 15, False: 0]
 // CHECK: Branch (119:5): [True: 1, False: 14]
 // CHECK: Branch (120:11): [True: 0, False: 1]
@@ -57,7 +57,7 @@
 // CHECK: Branch (126:11): [True: 3, False: 0]
 // CHECK: Branch (128:5): [True: 4, False: 11]
 // CHECK: Branch (129:11): [True: 4, False: 0]
-// CHECK: Branch (131:7): [True: 4, False: 0]
+// CHECK: Branch (131:7): [Folded - Ignored]
 // CHECK: Branch (132:13): [True: 4, False: 0]
 // CHECK: Branch (136:5): [True: 5, False: 10]
 // CHECK: Branch (137:11): [True: 1, False: 4]
@@ -120,7 +120,7 @@
 // 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: switches                         28       5  82.14%        38       4  89.47%        26       7  73.08%
 // 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%
@@ -129,12 +129,12 @@
 // 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%
+// REPORT-NEXT: TOTAL                           197      24  87.82%       234       8  96.58%       170      36  78.82%
 
 // 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/Inputs %S/Inputs/branch-c-general.c | FileCheck %s -check-prefix=FILEREPORT
-// FILEREPORT: TOTAL{{.*}}174                38    78.16%
+// FILEREPORT: TOTAL{{.*}}170                36    78.82%
 
 // 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/Inputs %S/Inputs/branch-c-general.c | FileCheck %s -check-prefix=USECOLOR
@@ -161,6 +161,6 @@
 // HTML-INDEX: <td class='column-entry-yellow'>
 // HTML-INDEX: 87.82% (173/197)
 // HTML-INDEX: <td class='column-entry-red'>
-// HTML-INDEX: 78.16% (136/174)
+// HTML-INDEX: 78.82% (134/170)
 // HTML-INDEX: <tr class='light-row-bold'>
 // HTML-INDEX: Totals

>From f0250b88ac0b3f2de738d7d6caf2684b314658dd Mon Sep 17 00:00:00 2001
From: Lambdaris <Lambdaris at outlook.com>
Date: Sun, 2 Jun 2024 10:58:56 +0800
Subject: [PATCH 2/2] [coverage] MCDC reports unrechable and uncoverable
 conditions

---
 clang/docs/SourceBasedCodeCoverage.rst        |  12 ++
 .../ProfileData/Coverage/CoverageMapping.h    |  67 +++++++---
 .../ProfileData/Coverage/CoverageMapping.cpp  | 114 ++++++++++++++++--
 .../llvm-cov/Inputs/mcdc-const-folding.o      | Bin 34504 -> 34592 bytes
 llvm/test/tools/llvm-cov/Inputs/mcdc-const.o  | Bin 5208 -> 5296 bytes
 llvm/test/tools/llvm-cov/Inputs/mcdc-macro.o  | Bin 6480 -> 6560 bytes
 llvm/test/tools/llvm-cov/mcdc-const.test      |  94 +++++++--------
 llvm/tools/llvm-cov/CoverageReport.cpp        |   5 +-
 llvm/tools/llvm-cov/CoverageSummaryInfo.cpp   |   2 +-
 .../tools/llvm-cov/SourceCoverageViewHTML.cpp |   7 +-
 .../tools/llvm-cov/SourceCoverageViewText.cpp |  14 ++-
 11 files changed, 229 insertions(+), 86 deletions(-)

diff --git a/clang/docs/SourceBasedCodeCoverage.rst b/clang/docs/SourceBasedCodeCoverage.rst
index cee706289284d..f875187c80608 100644
--- a/clang/docs/SourceBasedCodeCoverage.rst
+++ b/clang/docs/SourceBasedCodeCoverage.rst
@@ -496,6 +496,18 @@ starts a new boolean expression that is separated from the other conditions by
 the operator ``func()``.  When this is encountered, a warning will be generated
 and the boolean expression will not be instrumented.
 
+Similar as branch coverage, MCDC also is not tracked for constant folded conditions.
+Moreover it's even not tracked for conditions that can not effect outcomes of decisions
+due to other constants. These conditions will be shown as ``uncoverable`` or 
+``unreachable``, determined by whether they can be visited. For instance, in 
+``a || true || b``, value of the decision is always ``true`` by reason of the constant
+condition. Thus ``a`` can not lead the decision to ``false`` even though it could branch, 
+while ``b`` is always short-circuited. Hence ``a`` is shown as ``uncoverable``
+and ``b`` is marked as ``unreachable``. Statistics of MCDC does not count constant 
+conditions which do not vary or such conditions which make no difference on value of 
+decisions. If a decision is proved to no branch theoretically, it shows mark ``Folded``
+rather than coverage percent.
+
 Switch statements
 -----------------
 
diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
index c0458b93d1343..157ac83d69ee6 100644
--- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
+++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
@@ -384,6 +384,13 @@ struct MCDCRecord {
   /// are effectively ignored.
   enum CondState { MCDC_DontCare = -1, MCDC_False = 0, MCDC_True = 1 };
 
+  enum CondResult {
+    MCDC_Normal,
+    MCDC_Constant,
+    MCDC_Uncoverable,
+    MCDC_Unreachable
+  };
+
   /// Emulate SmallVector<CondState> with a pair of BitVector.
   ///
   ///          True  False DontCare (Impossible)
@@ -442,22 +449,23 @@ struct MCDCRecord {
   using TVPairMap = llvm::DenseMap<unsigned, TVRowPair>;
   using CondIDMap = llvm::DenseMap<unsigned, unsigned>;
   using LineColPairMap = llvm::DenseMap<unsigned, LineColPair>;
+  using ResultVector = llvm::SmallVector<CondResult>;
 
 private:
   CounterMappingRegion Region;
   TestVectors TV;
   TVPairMap IndependencePairs;
-  BoolVector Folded;
+  ResultVector CondResults;
   CondIDMap PosToID;
   LineColPairMap CondLoc;
 
 public:
   MCDCRecord(const CounterMappingRegion &Region, TestVectors &&TV,
-             TVPairMap &&IndependencePairs, BoolVector &&Folded,
+             TVPairMap &&IndependencePairs, ResultVector &&CondResults,
              CondIDMap &&PosToID, LineColPairMap &&CondLoc)
       : Region(Region), TV(std::move(TV)),
         IndependencePairs(std::move(IndependencePairs)),
-        Folded(std::move(Folded)), PosToID(std::move(PosToID)),
+        CondResults(std::move(CondResults)), PosToID(std::move(PosToID)),
         CondLoc(std::move(CondLoc)){};
 
   CounterMappingRegion getDecisionRegion() const { return Region; }
@@ -465,7 +473,12 @@ struct MCDCRecord {
     return Region.getDecisionParams().NumConditions;
   }
   unsigned getNumTestVectors() const { return TV.size(); }
-  bool isCondFolded(unsigned Condition) const { return Folded[Condition]; }
+  bool isCondCoverable(unsigned Condition) const {
+    return getCondResult(Condition) == CondResult::MCDC_Normal;
+  }
+  CondResult getCondResult(unsigned Condition) const {
+    return CondResults[Condition];
+  }
 
   /// Return the evaluation of a condition (indicated by Condition) in an
   /// executed test vector (indicated by TestVectorIndex), which will be True,
@@ -505,20 +518,25 @@ struct MCDCRecord {
     return IndependencePairs[PosToID[Condition]];
   }
 
-  float getPercentCovered() const {
-    unsigned Folded = 0;
+  /// Return if the decision is coverable and percent of covered conditions.
+  /// Only coverable conditions are counted as denominator.
+  std::pair<bool, float> getPercentCovered() const {
+    unsigned Excluded = 0;
     unsigned Covered = 0;
     for (unsigned C = 0; C < getNumConditions(); C++) {
-      if (isCondFolded(C))
-        Folded++;
+      if (!isCondCoverable(C))
+        Excluded++;
       else if (isConditionIndependencePairCovered(C))
         Covered++;
     }
 
-    unsigned Total = getNumConditions() - Folded;
+    unsigned Total = getNumConditions() - Excluded;
     if (Total == 0)
-      return 0.0;
-    return (static_cast<double>(Covered) / static_cast<double>(Total)) * 100.0;
+      return {false, 0.0};
+    return {
+        true,
+        (static_cast<double>(Covered) / static_cast<double>(Total)) * 100.0,
+    };
   }
 
   std::string getConditionHeaderString(unsigned Condition) {
@@ -553,7 +571,7 @@ struct MCDCRecord {
     // Add individual condition values to the string.
     OS << "  " << TestVectorIndex + 1 << " { ";
     for (unsigned Condition = 0; Condition < NumConditions; Condition++) {
-      if (isCondFolded(Condition))
+      if (getCondResult(Condition) == CondResult::MCDC_Constant)
         OS << "C";
       else {
         switch (getTVCondition(TestVectorIndex, Condition)) {
@@ -589,14 +607,25 @@ struct MCDCRecord {
     std::ostringstream OS;
 
     OS << "  C" << Condition + 1 << "-Pair: ";
-    if (isCondFolded(Condition)) {
+    switch (getCondResult(Condition)) {
+    case CondResult::MCDC_Normal:
+      if (isConditionIndependencePairCovered(Condition)) {
+        TVRowPair rows = getConditionIndependencePair(Condition);
+        OS << "covered: (" << rows.first << ",";
+        OS << rows.second << ")\n";
+      } else
+        OS << "not covered\n";
+      break;
+    case CondResult::MCDC_Constant:
       OS << "constant folded\n";
-    } else if (isConditionIndependencePairCovered(Condition)) {
-      TVRowPair rows = getConditionIndependencePair(Condition);
-      OS << "covered: (" << rows.first << ",";
-      OS << rows.second << ")\n";
-    } else
-      OS << "not covered\n";
+      break;
+    case CondResult::MCDC_Uncoverable:
+      OS << "uncoverable\n";
+      break;
+    case CondResult::MCDC_Unreachable:
+      OS << "unreachable\n";
+      break;
+    }
 
     return OS.str();
   }
diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index e5df21a3bcf7d..760cd0c0ef520 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -364,11 +364,15 @@ class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder {
   unsigned NumConditions;
 
   /// Vector used to track whether a condition is constant folded.
-  MCDCRecord::BoolVector Folded;
+  MCDCRecord::ResultVector CondResults;
 
   /// Mapping of calculated MC/DC Independence Pairs for each condition.
   MCDCRecord::TVPairMap IndependencePairs;
 
+  /// All possible Test Vectors for the boolean expression derived from
+  /// binary decision diagran of the expression.
+  MCDCRecord::TestVectors TestVectors;
+
   /// Storage for ExecVectors
   /// ExecVectors is the alias of its 0th element.
   std::array<MCDCRecord::TestVectors, 2> ExecVectorsByCond;
@@ -391,7 +395,8 @@ class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder {
       : NextIDsBuilder(Branches), TVIdxBuilder(this->NextIDs), Bitmap(Bitmap),
         Region(Region), DecisionParams(Region.getDecisionParams()),
         Branches(Branches), NumConditions(DecisionParams.NumConditions),
-        Folded(NumConditions, false), IndependencePairs(NumConditions),
+        CondResults(NumConditions, MCDCRecord::CondResult::MCDC_Normal),
+        IndependencePairs(NumConditions),
         ExecVectors(ExecVectorsByCond[false]) {}
 
 private:
@@ -415,6 +420,8 @@ class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder {
       assert(TVIdx < SavedNodes[ID].Width);
       assert(TVIdxs.insert(NextTVIdx).second && "Duplicate TVIdx");
 
+      TestVectors.push_back({TV, MCDCCond});
+
       if (!Bitmap[DecisionParams.BitmapIdx * CHAR_BIT + TV.getIndex()])
         continue;
 
@@ -439,7 +446,6 @@ class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder {
     buildTestVector(TV, 0, 0);
     assert(TVIdxs.size() == unsigned(NumTestVectors) &&
            "TVIdxs wasn't fulfilled");
-
     // Fill ExecVectors order by False items and True items.
     // ExecVectors is the alias of ExecVectorsByCond[false], so
     // Append ExecVectorsByCond[true] on it.
@@ -471,48 +477,130 @@ class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder {
     }
   }
 
+  void findCoverablePairs(const MCDCRecord::CondIDMap &PosToID) {
+    llvm::SmallVector<unsigned> FoldedCondPos;
+    for (unsigned I = 0; I < CondResults.size(); ++I) {
+      if (CondResults[I] == MCDCRecord::MCDC_Constant ||
+          CondResults[I] == MCDCRecord::MCDC_Unreachable) {
+        FoldedCondPos.push_back(I);
+      }
+    }
+    if (FoldedCondPos.empty()) {
+      return;
+    }
+    std::array<MCDCRecord::TestVectors, 2> PracticalTestVectorsByCond;
+    for (const auto &TVWithCond : TestVectors) {
+      const bool Practical =
+          llvm::all_of(FoldedCondPos, [&](const unsigned &Pos) {
+            const auto &[TV, Cond] = TVWithCond;
+            const auto ID = PosToID.at(Pos);
+            if (TV[ID] == MCDCRecord::MCDC_DontCare) {
+              return true;
+            }
+            if (CondResults[Pos] == MCDCRecord::MCDC_Constant) {
+              const auto ConstantValue = Branches[Pos]->Count.isZero()
+                                             ? MCDCRecord::MCDC_False
+                                             : MCDCRecord::MCDC_True;
+              if (TV[ID] == ConstantValue) {
+                return true;
+              }
+            }
+            return false;
+          });
+
+      if (Practical) {
+        PracticalTestVectorsByCond[TVWithCond.second].push_back(TVWithCond);
+      }
+    }
+
+    // If a condition:
+    // - is uncoverable, all test vectors in exact one element of
+    // `PracticalTestVectorsByCond` show it is `DontCare`;
+    // - is unreachable, all test vectors in both elements of
+    // `PracticalTestVectorsByCond` show it is `DontCare`;
+    //
+    // Otherwise, the condition is coverable as long as it has not been marked
+    // as constant or unreachable before.
+    for (unsigned Pos = 0; Pos < Branches.size(); ++Pos) {
+      if (CondResults[Pos] != MCDCRecord::MCDC_Normal) {
+        continue;
+      }
+      const auto ID = PosToID.at(Pos);
+      unsigned InaccessibleCondCount =
+          llvm::count_if(PracticalTestVectorsByCond,
+                         [=](const MCDCRecord::TestVectors &TestVectors) {
+                           for (const auto &[TV, Cond] : TestVectors) {
+                             if (TV[ID] != MCDCRecord::MCDC_DontCare) {
+                               return false;
+                             }
+                           }
+                           return true;
+                         });
+      switch (InaccessibleCondCount) {
+      case 1:
+        CondResults[Pos] = MCDCRecord::CondResult::MCDC_Uncoverable;
+        break;
+      case 2:
+        CondResults[Pos] = MCDCRecord::CondResult::MCDC_Unreachable;
+        break;
+      default:
+        break;
+      }
+    }
+  }
+
 public:
   /// Process the MC/DC Record in order to produce a result for a boolean
   /// expression. This process includes tracking the conditions that comprise
   /// the decision region, calculating the list of all possible test vectors,
   /// marking the executed test vectors, and then finding an Independence Pair
   /// out of the executed test vectors for each condition in the boolean
-  /// expression. A condition is tracked to ensure that its ID can be mapped to
-  /// its ordinal position in the boolean expression. The condition's source
-  /// location is also tracked, as well as whether it is constant folded (in
-  /// which case it is excuded from the metric).
+  /// expression. A condition is tracked to ensure that its ID can be mapped
+  /// to its ordinal position in the boolean expression. The condition's
+  /// source location is also tracked, as well as whether it is constant
+  /// folded (in which case it is excuded from the metric).
   MCDCRecord processMCDCRecord() {
     unsigned I = 0;
     MCDCRecord::CondIDMap PosToID;
     MCDCRecord::LineColPairMap CondLoc;
 
     // Walk the Record's BranchRegions (representing Conditions) in order to:
-    // - Hash the condition based on its corresponding ID. This will be used to
+    // - Hash the condition based on its corresponding ID. This will be used
+    // to
     //   calculate the test vectors.
     // - Keep a map of the condition's ordinal position (1, 2, 3, 4) to its
     //   actual ID.  This will be used to visualize the conditions in the
     //   correct order.
     // - Keep track of the condition source location. This will be used to
     //   visualize where the condition is.
-    // - Record whether the condition is constant folded so that we exclude it
+    // - Record whether the condition is folded so that we exclude it
     //   from being measured.
     for (const auto *B : Branches) {
       const auto &BranchParams = B->getBranchParams();
       PosToID[I] = BranchParams.ID;
       CondLoc[I] = B->startLoc();
-      Folded[I++] = (B->Count.isZero() || B->FalseCount.isZero());
+      if (B->Count.isZero() && B->FalseCount.isZero()) {
+        CondResults[I] = MCDCRecord::CondResult::MCDC_Unreachable;
+      } else if (B->Count.isZero() || B->FalseCount.isZero()) {
+        CondResults[I] = MCDCRecord::CondResult::MCDC_Constant;
+      }
+      ++I;
     }
 
     // Using Profile Bitmap from runtime, mark the executed test vectors.
     findExecutedTestVectors();
 
-    // Compare executed test vectors against each other to find an independence
-    // pairs for each condition.  This processing takes the most time.
+    // Compare executed test vectors against each other to find an
+    // independence pairs for each condition.  This processing takes the most
+    // time.
     findIndependencePairs();
 
+    // Identify all conditions making no difference on outcome of the decision.
+    findCoverablePairs(PosToID);
+
     // Record Test vectors, executed vectors, and independence pairs.
     return MCDCRecord(Region, std::move(ExecVectors),
-                      std::move(IndependencePairs), std::move(Folded),
+                      std::move(IndependencePairs), std::move(CondResults),
                       std::move(PosToID), std::move(CondLoc));
   }
 };
diff --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.o b/llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.o
index 4f54fa7b8a1a1492a9a455bbf6fa651844345c59..544716b291c9b8f01b95c8447ea947163a12bc26 100644
GIT binary patch
delta 2040
zcmaJ>U2Icj7(U;rEj^`W>;4!Z*dk;b8LV4NJH>dWF at hmkaN}pcrDtomc63|E4r9PZ
z%g-hWQJ1|pBwpB)Z~;jZA<TGg48*IMn21-JV9Xkc5^n^vsEJ(oeCM2vX~ZQv at ArPs
z^Stl#e&3fazAxrK5a((|U)$|nVJB-M#aX#eWOlpcV73ZTWXvsiY6Ven3uOU8WTJqT
z$1Zr0N)BXvB?$AW&5Pgel&>0kyP>Bqoi96A81%a{%<QyVidIP|W~l_>nlJm|WX-li
zO{7VyeM|n`R}`4x!0PLeRx3!Ur~AiWn15pEt-g|LyX07DLSa_~jYnM3%4aG;_~MtX
znWfH8dk;|2RcW&^5B=fV8Wfq$7|&yj=XKc72jkg=Dq&+la`CQM;VbOe(OUKOUmv#{
zdOP$UFDdJHZG4V|uCJHT<Kg-V=A~};PIiDUxGP<T&Mrx;mVPLy3SvqdKV7rE!)wD3
ztL<HeeXIT9&)<%1%LfZa8LK_>)KxoB;J-BVmZt^%XFR<BQ_l|c$K30z{<LpHq;l<a
z*B)H?<g<6CCe}`;ZFv544>$BOiQX*fK=qGGtm#&(KPU-Txs3a;+(!q#`fblo*Z<-E
zjne;df4MY@`ahQP`mcGvr;4&#YAI_rV&07N<z^bQ;yo++D$M;H;sX93TQOE?=J&1G
zQf211tmxZl=5uD8uQt<+6_>1dXp^~r&x(P~W}dfV=@v83Ay$-?-dWsooOSqWld75-
zf3=}uFr_9>_3h~&9d1acC(O_8%*oNAM8CQRpf3_^-Wv~w2Lg%4xDwo(2oD6CU+L?Q
zM<U at +Qz#IKH!Dry#ze4=Kgv5bTfG&VTKN)x?Rc(!ah;jq+e+^??q5Y?jH4xM(Az-c
zRxTP^<E0xraq>1jLw`23FRg0hG`0pkXP`A}&~MhDFJj*>HatV`GzNd&#-5PBEz7<m
ztxcv_M+Ggng;*=yY>&_qGPRv&b)5%kxjjuwo!B{?rdStvhYeoWK~j&bC}Q2%KLQ at 2
zdKA1K<VQm+PB(kNJH}Uay{L5zoL+D`z=?v>VQ`}0bn;c|I5?eY^2MB#>~WLYm1ZrZ
z^pw+bm%)TPp8|dNN|skl=5lwMb%2?rd>`uetl++W;Jq!}NqdB4sMwn&UmO*pY0Af)
z^eC#cE2JHVF5#rwn9h<^jL9rdeg(`yC*4qVc8!!indzkUftLiYUuTO{?3dYn^2fm&
z0xzz!KS at c*>=9`R at J@g?pyP)|Ny7g?Jrl at 3Tuz}u%r^}Fu$vYJ3*2>5X4M9^kc6ud
z6APsZa8^voEMV$}pvzdgp#t0}CuIDp at R|!Jpc}Jv=>i-S(=v+zgST<MucL!9R!-_j
zJ)0zL1pZX`8`V*P?=O$mvpMo-z at Gp=ql2Z4$?OJcW8j_!_oR-jcv5Ef$UhF=8Sut+
zydXuD`8!g<I}6?^ojIvEiJGVC*<<ogfI9{5gwC3cef4xb7}_*^pN8*gU{h`iO&0js
zPs+?^V4*XZH)~=GXA1ld&dR7@=*~iyvvlVQ{4UPP?2xIOg6<;6jQg0DQB|9Uw at dJL
z-nfq(yq&LSIr3+*KMQVF=l774(M8K;`R!l0%TND;%x;nYVwT_i#k>5>FUjbpUCQ#C
qzkHXU^yT;1Fr{W9Y>KoQ8C8ojvRHk9w$Hwf2dB)+Y>Bkln!f?4_(j(M

delta 1572
zcmZWnUrbw77{BM}lK!DkSZRS;C<$cMsbOQ%7MI>e8O*FP9dxXKbY)}X&k7cqjEk%n
zm>_2Usr_D9_Fx};z+{PCh_NNH7k#P;M&2~h#3n9g at kQ{#a3_g==c2tg at jjgI_kG{*
zeCPar=cYfPh4*RFMGNO2JkzF2>FrfdSSTf=j5E17%9U|dEgU?jx9d`4b%pPn`8yEQ
z+u>VXqXD}dSIJdz9Q>(kBa9WP=F}?3tvSUHXOGnf!Edl1t at z(q4kitCI{nx|*YuOA
zJxChtU@|z3ni0 at uaO2=fL&Tz;`}J>*c(L#ELC6>#@VCJR4~@_2nwnm<e)reMJ+NC|
zd*p~-Pa42q-h{gc${et6d=b$@<BXCwL(t?Pu9VS=>9Xm#F7#z*v_9Q>ACl(7y~m+g
z(S-Wo!1NTHHJw&?Z1aJs2 at h}pU;ctsxssK$p$+)0{kZzs7q86El+O2P&VN+ihnruj
z2;+LMq8ZO|z|R$@a4R#hG$V3t`;X^;{Y1S-hxQ(;=6>A%i<u2MUHKF2+s)vwt^t3Q
zr-aigP90UtQ4QYGV5ddpEc}aGx?QlV!O`kN4y*5Iu-~fIGaBr)sr96aQ#EQiuE9kO
z4%VuiEe&p|Q|oCBHrv(u!Xe%$*e{U{(NSq30r=>I4cAXkbm6+EVExH1av9E_wBb6Y
z;0Fr+M!}V>tp0Q><4iHU at owwy_2e!H-6B~4xtk}W;O$}KNXSj%kW=tt!~@<CW8`pM
z1 at DFb7)i*L at EUk~MUsYGuSnM$p#5Seo`Pd&rU5ofNftZd1bBKZupN#;7Gt3Al)^j*
z+X`9?or6Gs3_KT*gE5OSFmT9mgQpj(O19XGyw{L-5i1z87z2X}FO0JeQOFJ1FxDf;
z>q8#KEXKg4`{3!fKv^_KJRn3RVg~OJ57_|=yf+jhL6C<e;)mR29z27{yAnfdgewx7
z;vGg_1bM?TX4<gCL|^4e66C8QSp(skM0Z|>_7NXlcf#CAf at YlXcqEU%b#9cWTbPU{
zXaQSedC~&jai0Ew$#{Ymu{EA2QOI5A=`JSM6LcS2u{;j<PVhtr!i0!dl_w;+)d8E6
zKH>y<QY0=AUYF=@2eik1Oi*0J%gb>I+qo&8$(a&K7=$+@#NKK4&cd{aV&rLwz3UC0
zNx8vZS9nvRnKRHn<6~N8*ptdL5-pyA&6_@&X at ti&6ExchbF+D7n!wWnCPISlU`xm|
z)8=?u#AGf(A7SgQJTq;cr~8=9C(s6YUh(V}&!pTENeu`I^sE!wMIVzQis*|hO3bkZ
zo+()n$ypE<CFa)>&y*~Q=##u8F}H5>Ov!DL%!2T?#JpO5ht$GX%Wmp&z+R;l%quP&
W>|H_oyI^j`hezgCcvLH|9Qy|}%NHB~

diff --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-const.o b/llvm/test/tools/llvm-cov/Inputs/mcdc-const.o
index 1145fcc6f7125452262d0745b9de0591c1e48cda..d5bbebf7ae21905929b8c7f3f2682bb0d8c2a8a4 100644
GIT binary patch
delta 340
zcmXAjy-&h$5Qgu51)?T}VnIPdtdn3ATFOUdanMBm1XezZ9|0+f1{aCOff2mLkwgYI
zt!UyT4(>X#nm9R_usK?EdG5*kUM at EZ-G$pBCiCXXGX`1!(D~HfZmikAI7mNczN&~E
zdfh=nrp5hW0}gm_x<Q118$95Z!NJ+t#Dd_51$k-44s2r6un3Y2JXM&VT5Lp at yg&wb
zmX$)g^}2DCjz)L(>buAJNTFJZmdhvqSThc*`+A`su}UeWYKfwnEXlf3%&7 at ISyHw2
ze4&_1CF8NUEElz0EUD;f*!mX+-`o|$-r(GE>%e<b_$L04X|YX;a~pef>P+D^na&}0
zspe{8pT at 2s`p6VTdKFgjiN-<`U#R9DV&*U1nti|ub+~yFM2T(M=!4BsmlG%}`p9ri
TQA5<QVXbaVj#+#s#k2ei65C+x

delta 236
zcmWlRF-yZ>7=-VAza)@gQ)5ygidZM3QwO2Jp*m^1>FVSnIN8-L;82K at dViqQ#Z9HS
z8YiJH;#S=RCl~t at OqRO`9_}7CrpZ;J)49jZXNd;@u7A?^+p_O2&bFVrzkLyv292g$
zfCYmZ`~V at A14L-F0Ap)KD~PewT61h at MJ`s+EG_#QbD&?gRbGN8CLixvH!7p?r*rSf
z5%XFmL%Mz at DRX~cM*Q&GvP-vKNSBj(CI{@z7y9DS3p#SlUXaNtCqbbzkCzLt67w)@
c>!0DbE^-(qIx~C=JJO^Z?NsYB&zVQ-|A<&TXaE2J

diff --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-macro.o b/llvm/test/tools/llvm-cov/Inputs/mcdc-macro.o
index c0a9d6044553030ed1cc28bce2e764bcf3877869..c734cf01bddbee3e517d5cd6509de75a50991c6e 100644
GIT binary patch
delta 258
zcmca$w7__R24liT&7I7Qs*@ixJ24tgR%20N4BYI^@|2O40SI^}o3fRJC}?DqloS+O
z>FcLwmSmJB=_Tjq>gVK?K?vP~qWrAX<Ptrgq=LDzg=vbhS(-trVM?O0X{uS8v4v$)
za*DaRnTe5!fkBEzqLG<ls<GzgwQRdN8Lv&w6sTvcnEVn*PMDl2SkE|P@=GAOU~;C=
ze#RA(HHGULH%wkByr1#I<V+EJ#siaAiqtcnn5-$fpGkpl at +(n0#v7A0#p)R!OkOFr
zpD|!^rg%GJ?&Oc+=1l+CCNr{%O?HsbU}9jOoF!q$*f4peggsF6OCb4SvZkau03F#?
A&j0`b

delta 178
zcmWlSy$-=p0EK({ml&E<OSQXDn-Gy26qCeaG<pPsL8rT6Y;s<pvx%54Ltnu=h==GP
zah>7IS-x|M;cJk`aBn?nK;zq3D0a*?wEN2Zz;ZMdj_kepFZZwgtl>!M3dBt60y9d*
zB@<6zP3e``F!Kerto#xMWh2FoRYTywtXb+Whm&B6GqXS-XBCv{Pqt2oI1k7A#U`wP
Xc^K<gn~!jcKC3WQauTI#E{nQ9tH3vg

diff --git a/llvm/test/tools/llvm-cov/mcdc-const.test b/llvm/test/tools/llvm-cov/mcdc-const.test
index 5424625cf6a6b..d81de535c4c29 100644
--- a/llvm/test/tools/llvm-cov/mcdc-const.test
+++ b/llvm/test/tools/llvm-cov/mcdc-const.test
@@ -28,9 +28,9 @@
 // CHECKGENERALCASE-NEXT:  |  C1-Pair: covered: (1,2)
 // CHECKGENERALCASE-NEXT:  |  C2-Pair: constant folded
 // CHECKGENERALCASE-NEXT:  |  C3-Pair: constant folded
-// CHECKGENERALCASE-NEXT:  |  C4-Pair: not covered
+// CHECKGENERALCASE-NEXT:  |  C4-Pair: unreachable
 // CHECKGENERALCASE-NEXT:  |  C5-Pair: constant folded
-// CHECKGENERALCASE-NEXT:  |  MC/DC Coverage for Decision: 50.00%
+// CHECKGENERALCASE-NEXT:  |  MC/DC Coverage for Decision: 100.00%
 // CHECKGENERALCASE-NEXT:  |
 // CHECKGENERALCASE-NEXT:  ------------------
 
@@ -40,13 +40,13 @@
 
 //      CHECKFULLCASE: |  1 { C,  -  = F      }
 //      CHECKFULLCASE: |  C1-Pair: constant folded
-// CHECKFULLCASE-NEXT: |  C2-Pair: not covered
-//      CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// CHECKFULLCASE-NEXT: |  C2-Pair: unreachable
+//      CHECKFULLCASE: |  MC/DC Coverage for Decision: Folded
 //      CHECKFULLCASE: |  1 { F,  C  = F      }
 // CHECKFULLCASE-NEXT: |  2 { T,  C  = F      }
-//      CHECKFULLCASE: |  C1-Pair: not covered
+//      CHECKFULLCASE: |  C1-Pair: uncoverable
 // CHECKFULLCASE-NEXT: |  C2-Pair: constant folded
-//      CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+//      CHECKFULLCASE: |  MC/DC Coverage for Decision: Folded
 //      CHECKFULLCASE: |  1 { C,  F  = F      }
 // CHECKFULLCASE-NEXT: |  2 { C,  T  = T      }
 //      CHECKFULLCASE: |  C1-Pair: constant folded
@@ -59,13 +59,13 @@
 //      CHECKFULLCASE: |  MC/DC Coverage for Decision: 100.00%
 //      CHECKFULLCASE: |  1 { C,  -  = T      }
 //      CHECKFULLCASE: |  C1-Pair: constant folded
-// CHECKFULLCASE-NEXT: |  C2-Pair: not covered
-//      CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// CHECKFULLCASE-NEXT: |  C2-Pair: unreachable
+//      CHECKFULLCASE: |  MC/DC Coverage for Decision: Folded
 //      CHECKFULLCASE: |  1 { F,  C  = T      }
 // CHECKFULLCASE-NEXT: |  2 { T,  C  = T      }
-//      CHECKFULLCASE: |  C1-Pair: not covered
+//      CHECKFULLCASE: |  C1-Pair: uncoverable
 // CHECKFULLCASE-NEXT: |  C2-Pair: constant folded
-//      CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+//      CHECKFULLCASE: |  MC/DC Coverage for Decision: Folded
 //      CHECKFULLCASE: |  1 { C,  F  = F      }
 // CHECKFULLCASE-NEXT: |  2 { C,  T  = T      }
 //      CHECKFULLCASE: |  C1-Pair: constant folded
@@ -78,15 +78,15 @@
 //      CHECKFULLCASE: |  MC/DC Coverage for Decision: 100.00%
 //      CHECKFULLCASE: |  1 { C,  -,  -  = F      }
 //      CHECKFULLCASE: |  C1-Pair: constant folded
-// CHECKFULLCASE-NEXT: |  C2-Pair: not covered
-// CHECKFULLCASE-NEXT: |  C3-Pair: not covered
-//      CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// CHECKFULLCASE-NEXT: |  C2-Pair: unreachable
+// CHECKFULLCASE-NEXT: |  C3-Pair: unreachable
+//      CHECKFULLCASE: |  MC/DC Coverage for Decision: Folded
 //      CHECKFULLCASE: |  1 { F,  C,  -  = F      }
 // CHECKFULLCASE-NEXT: |  2 { T,  C,  -  = F      }
-//      CHECKFULLCASE: |  C1-Pair: not covered
+//      CHECKFULLCASE: |  C1-Pair: uncoverable
 // CHECKFULLCASE-NEXT: |  C2-Pair: constant folded
-// CHECKFULLCASE-NEXT: |  C3-Pair: not covered
-//      CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// CHECKFULLCASE-NEXT: |  C3-Pair: unreachable
+//      CHECKFULLCASE: |  MC/DC Coverage for Decision: Folded
 //      CHECKFULLCASE: |  1 { C,  F,  -  = F      }
 // CHECKFULLCASE-NEXT: |  2 { C,  T,  F  = F      }
 // CHECKFULLCASE-NEXT: |  3 { C,  T,  T  = T      }
@@ -103,15 +103,15 @@
 //      CHECKFULLCASE: |  MC/DC Coverage for Decision: 100.00%
 //      CHECKFULLCASE: |  1 { C,  -,  -  = T      }
 //      CHECKFULLCASE: |  C1-Pair: constant folded
-// CHECKFULLCASE-NEXT: |  C2-Pair: not covered
-// CHECKFULLCASE-NEXT: |  C3-Pair: not covered
-//      CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// CHECKFULLCASE-NEXT: |  C2-Pair: unreachable
+// CHECKFULLCASE-NEXT: |  C3-Pair: unreachable
+//      CHECKFULLCASE: |  MC/DC Coverage for Decision: Folded
 //      CHECKFULLCASE: |  1 { F,  C,  -  = T      }
 // CHECKFULLCASE-NEXT: |  2 { T,  C,  -  = T      }
-//      CHECKFULLCASE: |  C1-Pair: not covered
+//      CHECKFULLCASE: |  C1-Pair: uncoverable
 // CHECKFULLCASE-NEXT: |  C2-Pair: constant folded
-// CHECKFULLCASE-NEXT: |  C3-Pair: not covered
-//      CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// CHECKFULLCASE-NEXT: |  C3-Pair: unreachable
+//      CHECKFULLCASE: |  MC/DC Coverage for Decision: Folded
 //      CHECKFULLCASE: |  1 { C,  F,  T  = T      }
 // CHECKFULLCASE-NEXT: |  2 { C,  T,  -  = T      }
 //      CHECKFULLCASE: |  C1-Pair: constant folded
@@ -127,16 +127,16 @@
 //      CHECKFULLCASE: |  1 { F,  -,  C  = F      }
 // CHECKFULLCASE-NEXT: |  2 { T,  F,  C  = F      }
 // CHECKFULLCASE-NEXT: |  3 { T,  T,  C  = F      }
-//      CHECKFULLCASE: |  C1-Pair: not covered
-// CHECKFULLCASE-NEXT: |  C2-Pair: not covered
+//      CHECKFULLCASE: |  C1-Pair: uncoverable
+// CHECKFULLCASE-NEXT: |  C2-Pair: uncoverable
 // CHECKFULLCASE-NEXT: |  C3-Pair: constant folded
-//      CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+//      CHECKFULLCASE: |  MC/DC Coverage for Decision: Folded
 //      CHECKFULLCASE: |  1 { F,  C,  -  = F      }
 // CHECKFULLCASE-NEXT: |  2 { T,  C,  -  = F      }
-//      CHECKFULLCASE: |  C1-Pair: not covered
+//      CHECKFULLCASE: |  C1-Pair: uncoverable
 // CHECKFULLCASE-NEXT: |  C2-Pair: constant folded
-// CHECKFULLCASE-NEXT: |  C3-Pair: not covered
-//      CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// CHECKFULLCASE-NEXT: |  C3-Pair: unreachable
+//      CHECKFULLCASE: |  MC/DC Coverage for Decision: Folded
 //      CHECKFULLCASE: |  1 { F,  -,  C  = F      }
 // CHECKFULLCASE-NEXT: |  2 { T,  F,  C  = F      }
 // CHECKFULLCASE-NEXT: |  3 { T,  T,  C  = T      }
@@ -153,16 +153,16 @@
 //      CHECKFULLCASE: |  MC/DC Coverage for Decision: 100.00%
 //      CHECKFULLCASE: |  1 { F,  T,  C  = T      }
 // CHECKFULLCASE-NEXT: |  2 { T,  -,  C  = T      }
-//      CHECKFULLCASE: |  C1-Pair: not covered
-// CHECKFULLCASE-NEXT: |  C2-Pair: not covered
+//      CHECKFULLCASE: |  C1-Pair: uncoverable
+// CHECKFULLCASE-NEXT: |  C2-Pair: uncoverable
 // CHECKFULLCASE-NEXT: |  C3-Pair: constant folded
-//      CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+//      CHECKFULLCASE: |  MC/DC Coverage for Decision: Folded
 //      CHECKFULLCASE: |  1 { F,  C,  -  = T      }
 // CHECKFULLCASE-NEXT: |  2 { T,  C,  -  = T      }
-//      CHECKFULLCASE: |  C1-Pair: not covered
+//      CHECKFULLCASE: |  C1-Pair: uncoverable
 // CHECKFULLCASE-NEXT: |  C2-Pair: constant folded
-// CHECKFULLCASE-NEXT: |  C3-Pair: not covered
-//      CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// CHECKFULLCASE-NEXT: |  C3-Pair: unreachable
+//      CHECKFULLCASE: |  MC/DC Coverage for Decision: Folded
 //      CHECKFULLCASE: |  1 { F,  T,  C  = T      }
 // CHECKFULLCASE-NEXT: |  2 { T,  -,  C  = T      }
 //      CHECKFULLCASE: |  C1-Pair: not covered
@@ -176,31 +176,31 @@
 // CHECKFULLCASE-NEXT: |  C3-Pair: not covered
 //      CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
 
-//      REPORT: _Z5case0b {{.*}} 1       1   0.00%
-// REPORT-NEXT: _Z5case1b {{.*}} 1       1   0.00%
+//      REPORT: _Z5case0b {{.*}} 0       0   0.00%
+// REPORT-NEXT: _Z5case1b {{.*}} 0       0   0.00%
 // REPORT-NEXT: _Z5case2b {{.*}} 1       0 100.00%
 // REPORT-NEXT: _Z5case3b {{.*}} 1       0 100.00%
-// REPORT-NEXT: _Z5case4b {{.*}} 1       1   0.00%
-// REPORT-NEXT: _Z5case5b {{.*}} 1       1   0.00%
+// REPORT-NEXT: _Z5case4b {{.*}} 0       0   0.00%
+// REPORT-NEXT: _Z5case5b {{.*}} 0       0   0.00%
 // REPORT-NEXT: _Z5case6b {{.*}} 1       0 100.00%
 // REPORT-NEXT: _Z5case7b {{.*}} 1       0 100.00%
-// REPORT-NEXT: _Z5case8bb {{.*}} 2       2   0.00%
-// REPORT-NEXT: _Z5case9bb {{.*}} 2       2   0.00%
+// REPORT-NEXT: _Z5case8bb {{.*}} 0       0   0.00%
+// REPORT-NEXT: _Z5case9bb {{.*}} 0       0   0.00%
 // REPORT-NEXT: _Z5caseabb {{.*}} 2       0 100.00%
 // REPORT-NEXT: _Z5casebbb {{.*}} 2       0 100.00%
-// REPORT-NEXT: _Z5casecbb {{.*}} 2       2   0.00%
-// REPORT-NEXT: _Z5casedbb {{.*}} 2       2   0.00%
+// REPORT-NEXT: _Z5casecbb {{.*}} 0       0   0.00%
+// REPORT-NEXT: _Z5casedbb {{.*}} 0       0   0.00%
 // REPORT-NEXT: _Z5caseebb {{.*}} 2       2   0.00%
 // REPORT-NEXT: _Z5casefbb {{.*}} 2       2   0.00%
-// REPORT-NEXT: _Z5casegbb {{.*}} 2       2   0.00%
-// REPORT-NEXT: _Z5casehbb {{.*}} 2       2   0.00%
+// REPORT-NEXT: _Z5casegbb {{.*}} 0       0   0.00%
+// REPORT-NEXT: _Z5casehbb {{.*}} 0       0   0.00%
 // REPORT-NEXT: _Z5caseibb {{.*}} 2       0 100.00%
 // REPORT-NEXT: _Z5casejbb {{.*}} 2       0 100.00%
-// REPORT-NEXT: _Z5casekbb {{.*}} 2       2   0.00%
-// REPORT-NEXT: _Z5caselbb {{.*}} 2       2   0.00%
+// REPORT-NEXT: _Z5casekbb {{.*}} 0       0   0.00%
+// REPORT-NEXT: _Z5caselbb {{.*}} 0       0   0.00%
 // REPORT-NEXT: _Z5casembb {{.*}} 2       2   0.00%
 // REPORT-NEXT: _Z5casenbb {{.*}} 2       2   0.00%
-//      REPORT: TOTAL {{.*}} 40      28  30.00%
+//      REPORT: TOTAL {{.*}} 20       8  60.00%
 
 Instructions for regenerating the test:
 
diff --git a/llvm/tools/llvm-cov/CoverageReport.cpp b/llvm/tools/llvm-cov/CoverageReport.cpp
index 49a35f2a943e6..5d3b218e3c34f 100644
--- a/llvm/tools/llvm-cov/CoverageReport.cpp
+++ b/llvm/tools/llvm-cov/CoverageReport.cpp
@@ -17,6 +17,7 @@
 #include "llvm/Support/Path.h"
 #include "llvm/Support/ThreadPool.h"
 #include "llvm/Support/Threading.h"
+#include "llvm/Support/raw_ostream.h"
 #include <numeric>
 
 using namespace llvm;
@@ -362,7 +363,9 @@ void CoverageReport::render(const FunctionCoverageSummary &Function,
                   (unsigned)(Function.MCDCCoverage.getNumPairs() -
                              Function.MCDCCoverage.getCoveredPairs()));
     Options.colored_ostream(
-        OS, determineCoveragePercentageColor(Function.MCDCCoverage))
+        OS, Function.MCDCCoverage.getNumPairs() == 0
+                ? raw_ostream::GREEN
+                : determineCoveragePercentageColor(Function.MCDCCoverage))
         << format("%*.2f", FunctionReportColumns[12] - 1,
                   Function.MCDCCoverage.getPercentCovered())
         << '%';
diff --git a/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp b/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp
index 4f150020ee381..268912f589fe7 100644
--- a/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp
+++ b/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp
@@ -50,7 +50,7 @@ sumMCDCPairs(const ArrayRef<MCDCRecord> &Records) {
   for (const auto &Record : Records) {
     const auto NumConditions = Record.getNumConditions();
     for (unsigned C = 0; C < NumConditions; C++) {
-      if (!Record.isCondFolded(C))
+      if (Record.isCondCoverable(C))
         ++NumPairs;
       if (Record.isConditionIndependencePairCovered(C))
         ++CoveredPairs;
diff --git a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp
index b93d8cb035306..f2484658685b7 100644
--- a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp
+++ b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp
@@ -1017,7 +1017,12 @@ void SourceCoverageViewHTML::renderMCDCView(raw_ostream &OS, MCDCView &MRV,
     for (unsigned i = 0; i < Record.getNumConditions(); i++)
       OS << Record.getConditionCoverageString(i);
     OS << "  MC/DC Coverage for Expression: ";
-    OS << format("%0.2f", Record.getPercentCovered()) << "%\n";
+    const auto [Coverable, Percent] = Record.getPercentCovered();
+    if (Coverable) {
+      OS << format("%0.2f", Percent) << "%\n";
+    } else {
+      OS << "Folded\n";
+    }
     OS << EndPre;
     OS << EndExpansionDiv;
   }
diff --git a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp
index 580da45ecfc0d..01142e71a1080 100644
--- a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp
+++ b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp
@@ -379,10 +379,16 @@ void SourceCoverageViewText::renderMCDCView(raw_ostream &OS, MCDCView &MRV,
     }
     renderLinePrefix(OS, ViewDepth);
     OS << "  MC/DC Coverage for Decision: ";
-    colored_ostream(OS, raw_ostream::RED,
-                    getOptions().Colors && Record.getPercentCovered() < 100.0,
-                    /*Bold=*/false, /*BG=*/true)
-        << format("%0.2f", Record.getPercentCovered()) << "%";
+    const auto [Coverable, Percent] = Record.getPercentCovered();
+    if (Coverable) {
+      colored_ostream(OS, raw_ostream::RED,
+                      getOptions().Colors && Percent < 100.0,
+                      /*Bold=*/false, /*BG=*/true)
+          << format("%0.2f", Percent) << "%";
+    } else {
+      OS << "Folded";
+    }
+
     OS << "\n";
     renderLinePrefix(OS, ViewDepth);
     OS << "\n";



More information about the cfe-commits mailing list