[llvm] c087beb - Introduce mcdc::TVIdxBuilder (LLVM side, NFC) (#80676)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Feb 25 20:23:47 PST 2024
Author: NAKAMURA Takumi
Date: 2024-02-26T13:23:43+09:00
New Revision: c087bebb02ef35021547c6ddf0a3fdf1cbc8ad17
URL: https://github.com/llvm/llvm-project/commit/c087bebb02ef35021547c6ddf0a3fdf1cbc8ad17
DIFF: https://github.com/llvm/llvm-project/commit/c087bebb02ef35021547c6ddf0a3fdf1cbc8ad17.diff
LOG: Introduce mcdc::TVIdxBuilder (LLVM side, NFC) (#80676)
This is a preparation of incoming Clang changes (#82448) and just checks
`TVIdx` is calculated correctly. NFC.
`TVIdxBuilder` calculates deterministic Indices for each Condition Node.
It is used for `clang` to emit `TestVector` indices (aka ID) and for
`llvm-cov` to reconstruct `TestVectors`.
This includes the unittest `CoverageMappingTest.TVIdxBuilder`.
See also
https://discourse.llvm.org/t/rfc-coverage-new-algorithm-and-file-format-for-mc-dc/76798
Added:
Modified:
llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
llvm/unittests/ProfileData/CoverageMappingTest.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
index c5c9740f25c2ce..b39f262c3d976b 100644
--- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
+++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
@@ -550,6 +550,55 @@ struct MCDCRecord {
}
};
+namespace mcdc {
+/// Compute TestVector Indices "TVIdx" from the Conds graph.
+///
+/// Clang CodeGen handles the bitmap index based on TVIdx.
+/// llvm-cov reconstructs conditions from TVIdx.
+///
+/// For each leaf "The final decision",
+/// - TVIdx should be unique.
+/// - TVIdx has the Width.
+/// - The width represents the number of possible paths.
+/// - The minimum width is 1 "deterministic".
+/// - The order of leaves are sorted by Width DESC. It expects
+/// latter TVIdx(s) (with Width=1) could be pruned and altered to
+/// other simple branch conditions.
+///
+class TVIdxBuilder {
+public:
+ struct MCDCNode {
+ int InCount = 0; /// Reference count; temporary use
+ int Width; /// Number of accumulated paths (>= 1)
+ ConditionIDs NextIDs;
+ };
+
+#ifndef NDEBUG
+ /// This is no longer needed after the assignment.
+ /// It may be used in assert() for reconfirmation.
+ SmallVector<MCDCNode> SavedNodes;
+#endif
+
+ /// Output: Index for TestVectors bitmap (These are not CondIDs)
+ SmallVector<std::array<int, 2>> Indices;
+
+ /// Output: The number of test vectors.
+ /// Error with HardMaxTVs if the number has exploded.
+ int NumTestVectors;
+
+ /// Hard limit of test vectors
+ static constexpr auto HardMaxTVs =
+ std::numeric_limits<decltype(NumTestVectors)>::max();
+
+public:
+ /// Calculate and assign Indices
+ /// \param NextIDs The list of {FalseID, TrueID} indexed by ID
+ /// The first element [0] should be the root node.
+ /// \param Offset Offset of index to final decisions.
+ TVIdxBuilder(const SmallVectorImpl<ConditionIDs> &NextIDs, int Offset = 0);
+};
+} // namespace mcdc
+
/// A Counter mapping context is used to connect the counters, expressions
/// and the obtained counter values.
class CounterMappingContext {
diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index 8f9d1eadc2dafc..334f5dce879e5c 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -223,9 +223,130 @@ Expected<int64_t> CounterMappingContext::evaluate(const Counter &C) const {
return LastPoppedValue;
}
+mcdc::TVIdxBuilder::TVIdxBuilder(const SmallVectorImpl<ConditionIDs> &NextIDs,
+ int Offset)
+ : Indices(NextIDs.size()) {
+ // Construct Nodes and set up each InCount
+ auto N = NextIDs.size();
+ SmallVector<MCDCNode> Nodes(N);
+ for (unsigned ID = 0; ID < N; ++ID) {
+ for (unsigned C = 0; C < 2; ++C) {
+#ifndef NDEBUG
+ Indices[ID][C] = INT_MIN;
+#endif
+ auto NextID = NextIDs[ID][C];
+ Nodes[ID].NextIDs[C] = NextID;
+ if (NextID >= 0)
+ ++Nodes[NextID].InCount;
+ }
+ }
+
+ // Sort key ordered by <-Width, Ord>
+ SmallVector<std::tuple<int, /// -Width
+ unsigned, /// Ord
+ int, /// ID
+ unsigned /// Cond (0 or 1)
+ >>
+ Decisions;
+
+ // Traverse Nodes to assign Idx
+ SmallVector<int> Q;
+ assert(Nodes[0].InCount == 0);
+ Nodes[0].Width = 1;
+ Q.push_back(0);
+
+ unsigned Ord = 0;
+ while (!Q.empty()) {
+ auto IID = Q.begin();
+ int ID = *IID;
+ Q.erase(IID);
+ auto &Node = Nodes[ID];
+ assert(Node.Width > 0);
+
+ for (unsigned I = 0; I < 2; ++I) {
+ auto NextID = Node.NextIDs[I];
+ assert(NextID != 0 && "NextID should not point to the top");
+ if (NextID < 0) {
+ // Decision
+ Decisions.emplace_back(-Node.Width, Ord++, ID, I);
+ assert(Ord == Decisions.size());
+ continue;
+ }
+
+ // Inter Node
+ auto &NextNode = Nodes[NextID];
+ assert(NextNode.InCount > 0);
+
+ // Assign Idx
+ assert(Indices[ID][I] == INT_MIN);
+ Indices[ID][I] = NextNode.Width;
+ auto NextWidth = int64_t(NextNode.Width) + Node.Width;
+ if (NextWidth > HardMaxTVs) {
+ NumTestVectors = HardMaxTVs; // Overflow
+ return;
+ }
+ NextNode.Width = NextWidth;
+
+ // Ready if all incomings are processed.
+ // Or NextNode.Width hasn't been confirmed yet.
+ if (--NextNode.InCount == 0)
+ Q.push_back(NextID);
+ }
+ }
+
+ llvm::sort(Decisions);
+
+ // Assign TestVector Indices in Decision Nodes
+ int64_t CurIdx = 0;
+ for (auto [NegWidth, Ord, ID, C] : Decisions) {
+ int Width = -NegWidth;
+ assert(Nodes[ID].Width == Width);
+ assert(Nodes[ID].NextIDs[C] < 0);
+ assert(Indices[ID][C] == INT_MIN);
+ Indices[ID][C] = Offset + CurIdx;
+ CurIdx += Width;
+ if (CurIdx > HardMaxTVs) {
+ NumTestVectors = HardMaxTVs; // Overflow
+ return;
+ }
+ }
+
+ assert(CurIdx < HardMaxTVs);
+ NumTestVectors = CurIdx;
+
+#ifndef NDEBUG
+ for (const auto &Idxs : Indices)
+ for (auto Idx : Idxs)
+ assert(Idx != INT_MIN);
+ SavedNodes = std::move(Nodes);
+#endif
+}
+
namespace {
-class MCDCRecordProcessor {
+/// Construct this->NextIDs with Branches for TVIdxBuilder to use it
+/// before MCDCRecordProcessor().
+class NextIDsBuilder {
+protected:
+ SmallVector<mcdc::ConditionIDs> NextIDs;
+
+public:
+ NextIDsBuilder(const ArrayRef<const CounterMappingRegion *> Branches)
+ : NextIDs(Branches.size()) {
+#ifndef NDEBUG
+ DenseSet<mcdc::ConditionID> SeenIDs;
+#endif
+ for (const auto *Branch : Branches) {
+ const auto &BranchParams = Branch->getBranchParams();
+ assert(BranchParams.ID >= 0 && "CondID isn't set");
+ assert(SeenIDs.insert(BranchParams.ID).second && "Duplicate CondID");
+ NextIDs[BranchParams.ID] = BranchParams.Conds;
+ }
+ assert(SeenIDs.size() == Branches.size());
+ }
+};
+
+class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder {
/// A bitmap representing the executed test vectors for a boolean expression.
/// Each index of the bitmap corresponds to a possible test vector. An index
/// with a bit value of '1' indicates that the corresponding Test Vector
@@ -243,9 +364,6 @@ class MCDCRecordProcessor {
/// Total number of conditions in the boolean expression.
unsigned NumConditions;
- /// Mapping of a condition ID to its corresponding branch params.
- llvm::DenseMap<unsigned, mcdc::ConditionIDs> CondsMap;
-
/// Vector used to track whether a condition is constant folded.
MCDCRecord::BoolVector Folded;
@@ -256,13 +374,17 @@ class MCDCRecordProcessor {
/// ExecutedTestVectorBitmap.
MCDCRecord::TestVectors ExecVectors;
+#ifndef NDEBUG
+ DenseSet<unsigned> TVIdxs;
+#endif
+
public:
MCDCRecordProcessor(const BitVector &Bitmap,
const CounterMappingRegion &Region,
ArrayRef<const CounterMappingRegion *> Branches)
- : Bitmap(Bitmap), Region(Region),
- DecisionParams(Region.getDecisionParams()), Branches(Branches),
- NumConditions(DecisionParams.NumConditions),
+ : NextIDsBuilder(Branches), TVIdxBuilder(this->NextIDs), Bitmap(Bitmap),
+ Region(Region), DecisionParams(Region.getDecisionParams()),
+ Branches(Branches), NumConditions(DecisionParams.NumConditions),
Folded(NumConditions, false), IndependencePairs(NumConditions) {}
private:
@@ -270,7 +392,7 @@ class MCDCRecordProcessor {
// 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) {
+ int TVIdx, unsigned Index) {
assert((Index & (1 << ID)) == 0);
for (auto MCDCCond : {MCDCRecord::MCDC_False, MCDCRecord::MCDC_True}) {
@@ -278,12 +400,17 @@ class MCDCRecordProcessor {
static_assert(MCDCRecord::MCDC_True == 1);
Index |= MCDCCond << ID;
TV[ID] = MCDCCond;
- auto NextID = CondsMap[ID][MCDCCond];
+ auto NextID = NextIDs[ID][MCDCCond];
+ auto NextTVIdx = TVIdx + Indices[ID][MCDCCond];
+ assert(NextID == SavedNodes[ID].NextIDs[MCDCCond]);
if (NextID >= 0) {
- buildTestVector(TV, NextID, Index);
+ buildTestVector(TV, NextID, NextTVIdx, Index);
continue;
}
+ assert(TVIdx < SavedNodes[ID].Width);
+ assert(TVIdxs.insert(NextTVIdx).second && "Duplicate TVIdx");
+
if (!Bitmap[DecisionParams.BitmapIdx * CHAR_BIT + Index])
continue;
@@ -304,9 +431,12 @@ 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.
+ // `TVIdx` starts with 0 and is in the traversal.
// `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, 0, 0);
+ assert(TVIdxs.size() == unsigned(NumTestVectors) &&
+ "TVIdxs wasn't fulfilled");
}
// Find an independence pair for each condition:
@@ -367,7 +497,6 @@ class MCDCRecordProcessor {
// from being measured.
for (const auto *B : Branches) {
const auto &BranchParams = B->getBranchParams();
- CondsMap[BranchParams.ID] = BranchParams.Conds;
PosToID[I] = BranchParams.ID;
CondLoc[I] = B->startLoc();
Folded[I++] = (B->Count.isZero() && B->FalseCount.isZero());
diff --git a/llvm/unittests/ProfileData/CoverageMappingTest.cpp b/llvm/unittests/ProfileData/CoverageMappingTest.cpp
index 425b3d10510af7..f063a33205b30a 100644
--- a/llvm/unittests/ProfileData/CoverageMappingTest.cpp
+++ b/llvm/unittests/ProfileData/CoverageMappingTest.cpp
@@ -16,6 +16,7 @@
#include "llvm/Testing/Support/SupportHelpers.h"
#include "gtest/gtest.h"
+#include <map>
#include <ostream>
#include <utility>
@@ -1074,4 +1075,59 @@ TEST(CoverageMappingTest, filename_compilation_dir) {
}
}
+TEST(CoverageMappingTest, TVIdxBuilder) {
+ // ((n0 && n3) || (n2 && n4) || (n1 && n5))
+ static const std::array<mcdc::ConditionIDs, 6> Branches = {{
+ {2, 3},
+ {-1, 5},
+ {1, 4},
+ {2, -1},
+ {1, -1},
+ {-1, -1},
+ }};
+ int Offset = 1000;
+ auto TheBuilder = mcdc::TVIdxBuilder(
+ SmallVector<mcdc::ConditionIDs>(ArrayRef(Branches)), Offset);
+ EXPECT_TRUE(TheBuilder.NumTestVectors < TheBuilder.HardMaxTVs);
+ EXPECT_EQ(TheBuilder.Indices.size(), 6u);
+ EXPECT_EQ(TheBuilder.NumTestVectors, 15);
+
+ std::map<int, int> Decisions;
+ for (unsigned I = 0; I < TheBuilder.Indices.size(); ++I) {
+ struct Rec {
+ int Width;
+ std::array<int, 2> Indices;
+ };
+ static const std::array<Rec, 6> IndicesRefs = {{
+ {1, {0, 0}},
+ {4, {1000, 0}},
+ {2, {0, 0}},
+ {1, {1, 1014}},
+ {2, {2, 1012}},
+ {4, {1004, 1008}},
+ }};
+ EXPECT_EQ(TheBuilder.Indices[I], IndicesRefs[I].Indices);
+
+#ifndef NDEBUG
+ const auto &Node = TheBuilder.SavedNodes[I];
+ EXPECT_EQ(Node.Width, IndicesRefs[I].Width);
+ for (int C = 0; C < 2; ++C) {
+ auto Index = TheBuilder.Indices[I][C];
+ if (Node.NextIDs[C] < 0)
+ EXPECT_TRUE(Decisions.insert({Index, Node.Width}).second);
+ }
+#endif
+ }
+
+#ifndef NDEBUG
+ int NextIdx = Offset;
+ for (const auto [Index, Width] : Decisions) {
+ EXPECT_EQ(Index, NextIdx);
+ NextIdx += Width;
+ }
+ // The sum of Width(s) is NumTVs.
+ EXPECT_EQ(NextIdx, Offset + TheBuilder.NumTestVectors);
+#endif
+}
+
} // end anonymous namespace
More information about the llvm-commits
mailing list