From 9ef9ea31b6fd1d351b019129758f7869519aa23a Mon Sep 17 00:00:00 2001 From: NAKAMURA Takumi Date: Sat, 17 Feb 2024 23:33:55 +0900 Subject: [PATCH 1/5] Introduce `class TestVector` with a pair of `BitVector` This replaces `SmallVector` 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 c5c9740f25c2c..0f82f091107f5 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; + /// Emulate SmallVector 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; using BoolVector = llvm::SmallVector; using TVRowPair = std::pair; diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp index ddce758072917..f02ac27ed891a 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 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 0f82f091107f5..a54e37f4c9e3f 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 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 a54e37f4c9e3f..8f2ca9a68eb2b 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 f02ac27ed891a..23a6142c8c985 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 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 8f2ca9a68eb2b..1fac79720bd27 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 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 1fac79720bd27..8a43c9a7b14ba 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.