Skip to content

Commit 3d0a689

Browse files
authored
[llvm-cov] Simplify and optimize MC/DC computation (llvm#79727)
Update code from https://reviews.llvm.org/D138847 `buildTestVector` is a standard DFS (walking a reduced ordered binary decision diagram). Avoid shouldCopyOffTestVectorFor{True,False}Path complexity and redundant `Map[ID]` lookups. `findIndependencePairs` unnecessarily uses four nested loops (n<=6) to find independence pairs. Instead, enumerate the two execution vectors and find the number of mismatches. This algorithm can be optimized using the marking function technique described in _Efficient Test Coverage Measurement for MC/DC, 2013_, but this may be overkill.
1 parent faf675c commit 3d0a689

File tree

1 file changed

+41
-101
lines changed

1 file changed

+41
-101
lines changed

llvm/lib/ProfileData/Coverage/CoverageMapping.cpp

Lines changed: 41 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -286,17 +286,8 @@ class MCDCRecordProcessor {
286286
TestVectors((size_t)1 << NumConditions) {}
287287

288288
private:
289-
void recordTestVector(MCDCRecord::TestVector &TV,
289+
void recordTestVector(MCDCRecord::TestVector &TV, unsigned Index,
290290
MCDCRecord::CondState Result) {
291-
// Calculate an index that is used to identify the test vector in a vector
292-
// of test vectors. This index also corresponds to the index values of an
293-
// MCDC Region's bitmap (see findExecutedTestVectors()).
294-
unsigned Index = 0;
295-
for (auto Cond = std::rbegin(TV); Cond != std::rend(TV); ++Cond) {
296-
Index <<= 1;
297-
Index |= (*Cond == MCDCRecord::MCDC_True) ? 0x1 : 0x0;
298-
}
299-
300291
// Copy the completed test vector to the vector of testvectors.
301292
TestVectors[Index] = TV;
302293

@@ -305,38 +296,25 @@ class MCDCRecordProcessor {
305296
TestVectors[Index].push_back(Result);
306297
}
307298

308-
void shouldCopyOffTestVectorForTruePath(MCDCRecord::TestVector &TV,
309-
unsigned ID) {
310-
// Branch regions are hashed based on an ID.
311-
const CounterMappingRegion *Branch = Map[ID];
312-
313-
TV[ID - 1] = MCDCRecord::MCDC_True;
314-
if (Branch->MCDCParams.TrueID > 0)
315-
buildTestVector(TV, Branch->MCDCParams.TrueID);
316-
else
317-
recordTestVector(TV, MCDCRecord::MCDC_True);
318-
}
319-
320-
void shouldCopyOffTestVectorForFalsePath(MCDCRecord::TestVector &TV,
321-
unsigned ID) {
322-
// Branch regions are hashed based on an ID.
299+
// Walk the binary decision diagram and try assigning both false and true to
300+
// each node. When a terminal node (ID == 0) is reached, fill in the value in
301+
// the truth table.
302+
void buildTestVector(MCDCRecord::TestVector &TV, unsigned ID,
303+
unsigned Index) {
323304
const CounterMappingRegion *Branch = Map[ID];
324305

325306
TV[ID - 1] = MCDCRecord::MCDC_False;
326307
if (Branch->MCDCParams.FalseID > 0)
327-
buildTestVector(TV, Branch->MCDCParams.FalseID);
308+
buildTestVector(TV, Branch->MCDCParams.FalseID, Index);
328309
else
329-
recordTestVector(TV, MCDCRecord::MCDC_False);
330-
}
310+
recordTestVector(TV, Index, MCDCRecord::MCDC_False);
331311

332-
/// Starting with the base test vector, build a comprehensive list of
333-
/// possible test vectors by recursively walking the branch condition IDs
334-
/// provided. Once an end node is reached, record the test vector in a vector
335-
/// of test vectors that can be matched against during MC/DC analysis, and
336-
/// then reset the positions to 'DontCare'.
337-
void buildTestVector(MCDCRecord::TestVector &TV, unsigned ID = 1) {
338-
shouldCopyOffTestVectorForTruePath(TV, ID);
339-
shouldCopyOffTestVectorForFalsePath(TV, ID);
312+
Index |= 1 << (ID - 1);
313+
TV[ID - 1] = MCDCRecord::MCDC_True;
314+
if (Branch->MCDCParams.TrueID > 0)
315+
buildTestVector(TV, Branch->MCDCParams.TrueID, Index);
316+
else
317+
recordTestVector(TV, Index, MCDCRecord::MCDC_True);
340318

341319
// Reset back to DontCare.
342320
TV[ID - 1] = MCDCRecord::MCDC_DontCare;
@@ -353,71 +331,33 @@ class MCDCRecordProcessor {
353331
}
354332
}
355333

356-
/// For a given condition and two executed Test Vectors, A and B, see if the
357-
/// two test vectors match forming an Independence Pair for the condition.
358-
/// For two test vectors to match, the following must be satisfied:
359-
/// - The condition's value in each test vector must be opposite.
360-
/// - The result's value in each test vector must be opposite.
361-
/// - All other conditions' values must be equal or marked as "don't care".
362-
bool matchTestVectors(unsigned Aidx, unsigned Bidx, unsigned ConditionIdx) {
363-
const MCDCRecord::TestVector &A = ExecVectors[Aidx];
364-
const MCDCRecord::TestVector &B = ExecVectors[Bidx];
365-
366-
// If condition values in both A and B aren't opposites, no match.
367-
// Because a value can be 0 (false), 1 (true), or -1 (DontCare), a check
368-
// that "XOR != 1" will ensure that the values are opposites and that
369-
// neither of them is a DontCare.
370-
// 1 XOR 0 == 1 | 0 XOR 0 == 0 | -1 XOR 0 == -1
371-
// 1 XOR 1 == 0 | 0 XOR 1 == 1 | -1 XOR 1 == -2
372-
// 1 XOR -1 == -2 | 0 XOR -1 == -1 | -1 XOR -1 == 0
373-
if ((A[ConditionIdx] ^ B[ConditionIdx]) != 1)
374-
return false;
375-
376-
// If the results of both A and B aren't opposites, no match.
377-
if ((A[NumConditions] ^ B[NumConditions]) != 1)
378-
return false;
379-
380-
for (unsigned Idx = 0; Idx < NumConditions; ++Idx) {
381-
// Look for other conditions that don't match. Skip over the given
382-
// Condition as well as any conditions marked as "don't care".
383-
const auto ARecordTyForCond = A[Idx];
384-
const auto BRecordTyForCond = B[Idx];
385-
if (Idx == ConditionIdx ||
386-
ARecordTyForCond == MCDCRecord::MCDC_DontCare ||
387-
BRecordTyForCond == MCDCRecord::MCDC_DontCare)
388-
continue;
389-
390-
// If there is a condition mismatch with any of the other conditions,
391-
// there is no match for the test vectors.
392-
if (ARecordTyForCond != BRecordTyForCond)
393-
return false;
394-
}
395-
396-
// Otherwise, match.
397-
return true;
398-
}
399-
400-
/// Find all possible Independence Pairs for a boolean expression given its
401-
/// executed Test Vectors. This process involves looking at each condition
402-
/// and attempting to find two Test Vectors that "match", giving us a pair.
334+
// Find an independence pair for each condition:
335+
// - The condition is true in one test and false in the other.
336+
// - The decision outcome is true one test and false in the other.
337+
// - All other conditions' values must be equal or marked as "don't care".
403338
void findIndependencePairs() {
404339
unsigned NumTVs = ExecVectors.size();
405-
406-
// For each condition.
407-
for (unsigned C = 0; C < NumConditions; ++C) {
408-
bool PairFound = false;
409-
410-
// For each executed test vector.
411-
for (unsigned I = 0; !PairFound && I < NumTVs; ++I) {
412-
// Compared to every other executed test vector.
413-
for (unsigned J = 0; !PairFound && J < NumTVs; ++J) {
414-
if (I == J)
340+
for (unsigned I = 1; I < NumTVs; ++I) {
341+
const MCDCRecord::TestVector &A = ExecVectors[I];
342+
for (unsigned J = 0; J < I; ++J) {
343+
const MCDCRecord::TestVector &B = ExecVectors[J];
344+
// Enumerate two execution vectors whose outcomes are different.
345+
if (A[NumConditions] == B[NumConditions])
346+
continue;
347+
unsigned Flip = NumConditions, Idx;
348+
for (Idx = 0; Idx < NumConditions; ++Idx) {
349+
MCDCRecord::CondState ACond = A[Idx], BCond = B[Idx];
350+
if (ACond == BCond || ACond == MCDCRecord::MCDC_DontCare ||
351+
BCond == MCDCRecord::MCDC_DontCare)
415352
continue;
416-
417-
// If a matching pair of vectors is found, record them.
418-
if ((PairFound = matchTestVectors(I, J, C)))
419-
IndependencePairs[C] = std::make_pair(I + 1, J + 1);
353+
if (Flip != NumConditions)
354+
break;
355+
Flip = Idx;
420356
}
357+
// If the two vectors differ in exactly one condition, ignoring DontCare
358+
// conditions, we have found an independence pair.
359+
if (Idx == NumConditions && Flip != NumConditions)
360+
IndependencePairs.insert({Flip, std::make_pair(J + 1, I + 1)});
421361
}
422362
}
423363
}
@@ -454,11 +394,11 @@ class MCDCRecordProcessor {
454394
Folded[I++] = (B->Count.isZero() && B->FalseCount.isZero());
455395
}
456396

457-
// Initialize a base test vector as 'DontCare'.
397+
// Walk the binary decision diagram to enumerate all possible test vectors.
398+
// We start at the root node (ID == 1) with all values being DontCare.
399+
// `Index` encodes the bitmask of true values and is initially 0.
458400
MCDCRecord::TestVector TV(NumConditions, MCDCRecord::MCDC_DontCare);
459-
460-
// Use the base test vector to build the list of all possible test vectors.
461-
buildTestVector(TV);
401+
buildTestVector(TV, 1, 0);
462402

463403
// Using Profile Bitmap from runtime, mark the executed test vectors.
464404
findExecutedTestVectors(ExecutedTestVectorBitmap);

0 commit comments

Comments
 (0)