[llvm] [MC/DC] Introduce `class TestVector` with a pair of `BitVector` (PR #82174)

NAKAMURA Takumi via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 19 01:16:37 PST 2024


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

>From 9ef9ea31b6fd1d351b019129758f7869519aa23a Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Sat, 17 Feb 2024 23:33:55 +0900
Subject: [PATCH 1/5] Introduce `class TestVector` with a pair of `BitVector`

This replaces `SmallVector<CondState>` and emulates it.

-          True  False DontCare (Impossible)
- Values:  True  False False    True
- Visited: True  True  False    False

`findIndependencePairs()` can be optimized with logical ops.

FIXME: Specialize `findIndependencePairs()` for the single word.
---
 .../ProfileData/Coverage/CoverageMapping.h    | 65 ++++++++++++++++++-
 .../ProfileData/Coverage/CoverageMapping.cpp  | 35 ++++------
 2 files changed, 76 insertions(+), 24 deletions(-)

diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
index c5c9740f25c2ce..0f82f091107f5b 100644
--- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
+++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
@@ -380,7 +380,70 @@ struct MCDCRecord {
   /// are effectively ignored.
   enum CondState { MCDC_DontCare = -1, MCDC_False = 0, MCDC_True = 1 };
 
-  using TestVector = llvm::SmallVector<CondState>;
+  /// Emulate SmallVector<CondState> with a pair of BitVector.
+  ///
+  ///          True  False DontCare (Impossible)
+  /// Values:  True  False False    True
+  /// Visited: True  True  False    False
+  class TestVector {
+    BitVector Values;  /// True/False (False when DontCare)
+    BitVector Visited; /// ~DontCare
+
+  public:
+    /// Assume filling DontCare.
+    TestVector(unsigned n, CondState Cond = MCDC_DontCare) {
+      assert(Cond == MCDC_DontCare);
+      Values.resize(n);
+      Visited.resize(n);
+    }
+
+    /// Emulate RHS SmallVector::operator[]
+    MCDCRecord::CondState operator[](int I) const {
+      return (Visited[I] ? (Values[I] ? MCDC_True : MCDC_False)
+                         : MCDC_DontCare);
+    }
+
+    /// Equivalent to buildTestVector's Index.
+    auto getIndex() const { return Values.getData()[0]; }
+
+    /// Emulate LHS SmallVector::operator[].
+    void set(int I, CondState Val) {
+      Visited[I] = (Val != MCDC_DontCare);
+      Values[I] = (Val == MCDC_True);
+    }
+
+    /// Emulate SmallVector::push_back.
+    void push_back(CondState Val) {
+      Visited.push_back(Val != MCDC_DontCare);
+      Values.push_back(Val == MCDC_True);
+    }
+
+    /// Return the outcome is different each other.
+    /// Assumes back() is the outcome
+    /// Dedicated to findIndependencePairs().
+    bool isDifferentOutcome(const TestVector &B) const {
+      const auto &A = *this;
+      assert(A.Visited.back() && B.Visited.back() &&
+             "Both shouldn't be DontCare");
+      return (A.Values.back() ^ B.Values.back());
+    }
+
+    /// For each element:
+    /// - False if either is DontCare
+    /// - False if both have the same value
+    /// - True if both have the opposite value
+    /// ((A.Values ^ B.Values) & A.Visited & B.Visited)
+    /// Dedicated to findIndependencePairs().
+    auto getDifferences(const TestVector &B) const {
+      const auto &A = *this;
+      BitVector AB = A.Values;
+      AB ^= B.Values;
+      AB &= A.Visited;
+      AB &= B.Visited;
+      return AB;
+    }
+  };
+
   using TestVectors = llvm::SmallVector<TestVector>;
   using BoolVector = llvm::SmallVector<bool>;
   using TVRowPair = std::pair<unsigned, unsigned>;
diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index ddce7580729170..f02ac27ed891af 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -272,22 +272,18 @@ class MCDCRecordProcessor {
   // Walk the binary decision diagram and try assigning both false and true to
   // each node. When a terminal node (ID == 0) is reached, fill in the value in
   // the truth table.
-  void buildTestVector(MCDCRecord::TestVector &TV, mcdc::ConditionID ID,
-                       unsigned Index) {
-    assert((Index & (1 << ID)) == 0);
-
+  void buildTestVector(MCDCRecord::TestVector &TV, mcdc::ConditionID ID) {
     for (auto MCDCCond : {MCDCRecord::MCDC_False, MCDCRecord::MCDC_True}) {
       static_assert(MCDCRecord::MCDC_False == 0);
       static_assert(MCDCRecord::MCDC_True == 1);
-      Index |= MCDCCond << ID;
-      TV[ID] = MCDCCond;
+      TV.set(ID, MCDCCond);
       auto NextID = CondsMap[ID][MCDCCond];
       if (NextID >= 0) {
-        buildTestVector(TV, NextID, Index);
+        buildTestVector(TV, NextID);
         continue;
       }
 
-      if (!Bitmap[BitmapIdx + Index])
+      if (!Bitmap[BitmapIdx + TV.getIndex()])
         continue;
 
       // Copy the completed test vector to the vector of testvectors.
@@ -299,7 +295,7 @@ class MCDCRecordProcessor {
     }
 
     // Reset back to DontCare.
-    TV[ID] = MCDCRecord::MCDC_DontCare;
+    TV.set(ID, MCDCRecord::MCDC_DontCare);
   }
 
   /// Walk the bits in the bitmap.  A bit set to '1' indicates that the test
@@ -309,7 +305,7 @@ class MCDCRecordProcessor {
     // We start at the root node (ID == 0) with all values being DontCare.
     // `Index` encodes the bitmask of true values and is initially 0.
     MCDCRecord::TestVector TV(NumConditions, MCDCRecord::MCDC_DontCare);
-    buildTestVector(TV, 0, 0);
+    buildTestVector(TV, 0);
   }
 
   // Find an independence pair for each condition:
@@ -323,22 +319,15 @@ class MCDCRecordProcessor {
       for (unsigned J = 0; J < I; ++J) {
         const MCDCRecord::TestVector &B = ExecVectors[J];
         // Enumerate two execution vectors whose outcomes are different.
-        if (A[NumConditions] == B[NumConditions])
+        if (!A.isDifferentOutcome(B))
           continue;
-        unsigned Flip = NumConditions, Idx;
-        for (Idx = 0; Idx < NumConditions; ++Idx) {
-          MCDCRecord::CondState ACond = A[Idx], BCond = B[Idx];
-          if (ACond == BCond || ACond == MCDCRecord::MCDC_DontCare ||
-              BCond == MCDCRecord::MCDC_DontCare)
-            continue;
-          if (Flip != NumConditions)
-            break;
-          Flip = Idx;
-        }
         // If the two vectors differ in exactly one condition, ignoring DontCare
         // conditions, we have found an independence pair.
-        if (Idx == NumConditions && Flip != NumConditions)
-          IndependencePairs.insert({Flip, std::make_pair(J + 1, I + 1)});
+        auto AB = A.getDifferences(B);
+        assert(AB[NumConditions] && "The last element should be different");
+        if (AB.count() == 2) // The single condition and the last element
+          IndependencePairs.insert(
+              {AB.find_first(), std::make_pair(J + 1, I + 1)});
       }
     }
   }

>From f9baabb0cd18a6a963d055da37ba3ad9dcd0cb55 Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Mon, 19 Feb 2024 17:53:24 +0900
Subject: [PATCH 2/5] Strip namespace

---
 llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
index 0f82f091107f5b..a54e37f4c9e3f4 100644
--- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
+++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
@@ -398,7 +398,7 @@ struct MCDCRecord {
     }
 
     /// Emulate RHS SmallVector::operator[]
-    MCDCRecord::CondState operator[](int I) const {
+    CondState operator[](int I) const {
       return (Visited[I] ? (Values[I] ? MCDC_True : MCDC_False)
                          : MCDC_DontCare);
     }

>From f9fc96d03306593312cee7b1067d2fac335090d3 Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Mon, 19 Feb 2024 17:50:18 +0900
Subject: [PATCH 3/5] The ctor `TestVector(N)` takes only the length.

---
 llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h | 8 ++------
 llvm/lib/ProfileData/Coverage/CoverageMapping.cpp        | 3 +--
 2 files changed, 3 insertions(+), 8 deletions(-)

diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
index a54e37f4c9e3f4..8f2ca9a68eb2bc 100644
--- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
+++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
@@ -390,12 +390,8 @@ struct MCDCRecord {
     BitVector Visited; /// ~DontCare
 
   public:
-    /// Assume filling DontCare.
-    TestVector(unsigned n, CondState Cond = MCDC_DontCare) {
-      assert(Cond == MCDC_DontCare);
-      Values.resize(n);
-      Visited.resize(n);
-    }
+    /// Default values are filled with DontCare.
+    TestVector(unsigned N) : Values(N), Visited(N) {}
 
     /// Emulate RHS SmallVector::operator[]
     CondState operator[](int I) const {
diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index f02ac27ed891af..23a6142c8c985e 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -303,8 +303,7 @@ class MCDCRecordProcessor {
   void findExecutedTestVectors() {
     // Walk the binary decision diagram to enumerate all possible test vectors.
     // We start at the root node (ID == 0) with all values being DontCare.
-    // `Index` encodes the bitmask of true values and is initially 0.
-    MCDCRecord::TestVector TV(NumConditions, MCDCRecord::MCDC_DontCare);
+    MCDCRecord::TestVector TV(NumConditions);
     buildTestVector(TV, 0);
   }
 

>From 4104cc887988a863bff65a229c817e4a6974f1f6 Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Mon, 19 Feb 2024 17:54:35 +0900
Subject: [PATCH 4/5] Update the comment

---
 llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
index 8f2ca9a68eb2bc..1fac79720bd274 100644
--- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
+++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
@@ -402,7 +402,8 @@ struct MCDCRecord {
     /// Equivalent to buildTestVector's Index.
     auto getIndex() const { return Values.getData()[0]; }
 
-    /// Emulate LHS SmallVector::operator[].
+    /// Set the condition \p Val at position \p I.
+    /// This emulates LHS SmallVector::operator[].
     void set(int I, CondState Val) {
       Visited[I] = (Val != MCDC_DontCare);
       Values[I] = (Val == MCDC_True);

>From 970b4e2a8f00f6de6b792382da2a8fd782d4093e Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Mon, 19 Feb 2024 17:55:13 +0900
Subject: [PATCH 5/5] Add a nervous assertion

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

diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
index 1fac79720bd274..8a43c9a7b14ba6 100644
--- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
+++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
@@ -413,6 +413,7 @@ struct MCDCRecord {
     void push_back(CondState Val) {
       Visited.push_back(Val != MCDC_DontCare);
       Values.push_back(Val == MCDC_True);
+      assert(Values.size() == Visited.size());
     }
 
     /// Return the outcome is different each other.



More information about the llvm-commits mailing list