Skip to content

[Coverage] MCDC: Move findIndependencePairs deferred into MCDCRecord #121188

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 16 additions & 11 deletions llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <cstdint>
#include <iterator>
#include <memory>
#include <optional>
#include <sstream>
#include <string>
#include <system_error>
Expand Down Expand Up @@ -451,19 +452,22 @@ struct MCDCRecord {
private:
CounterMappingRegion Region;
TestVectors TV;
TVPairMap IndependencePairs;
std::optional<TVPairMap> IndependencePairs;
BoolVector Folded;
CondIDMap PosToID;
LineColPairMap CondLoc;

public:
MCDCRecord(const CounterMappingRegion &Region, TestVectors &&TV,
TVPairMap &&IndependencePairs, BoolVector &&Folded,
CondIDMap &&PosToID, LineColPairMap &&CondLoc)
: Region(Region), TV(std::move(TV)),
IndependencePairs(std::move(IndependencePairs)),
Folded(std::move(Folded)), PosToID(std::move(PosToID)),
CondLoc(std::move(CondLoc)){};
BoolVector &&Folded, CondIDMap &&PosToID, LineColPairMap &&CondLoc)
: Region(Region), TV(std::move(TV)), Folded(std::move(Folded)),
PosToID(std::move(PosToID)), CondLoc(std::move(CondLoc)) {
findIndependencePairs();
}

// Compare executed test vectors against each other to find an independence
// pairs for each condition. This processing takes the most time.
void findIndependencePairs();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it make sense to have this actually return the std::optional<TVPairMap>?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd make sense since it always flush and reconstruct the map.


const CounterMappingRegion &getDecisionRegion() const { return Region; }
unsigned getNumConditions() const {
Expand Down Expand Up @@ -494,10 +498,10 @@ struct MCDCRecord {
/// TestVectors requires a translation from a ordinal position to actual
/// condition ID. This is done via PosToID[].
bool isConditionIndependencePairCovered(unsigned Condition) const {
assert(IndependencePairs);
auto It = PosToID.find(Condition);
if (It != PosToID.end())
return IndependencePairs.contains(It->second);
llvm_unreachable("Condition ID without an Ordinal mapping");
assert(It != PosToID.end() && "Condition ID without an Ordinal mapping");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this changed to an assert from unreachable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we expect "satisfies or crash" here? I wanted to eliminate the condition.

return IndependencePairs->contains(It->second);
}

/// Return the Independence Pair that covers the given condition. Because
Expand All @@ -507,7 +511,8 @@ struct MCDCRecord {
/// via PosToID[].
TVRowPair getConditionIndependencePair(unsigned Condition) {
assert(isConditionIndependencePairCovered(Condition));
return IndependencePairs[PosToID[Condition]];
assert(IndependencePairs);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert is unneeded due to immediate dereference

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's redundant since isConditionIndependencePairCovered checks IndependencePairs. That said, I want to reconfirm before its use *IndependencePairs.

return (*IndependencePairs)[PosToID[Condition]];
}

float getPercentCovered() const {
Expand Down
67 changes: 35 additions & 32 deletions llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,40 @@ Expected<int64_t> CounterMappingContext::evaluate(const Counter &C) const {
return LastPoppedValue;
}

// Find an independence pair for each condition:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this be a doxygen comment in CoverageMapping.h?

// - The condition is true in one test and false in the other.
// - The decision outcome is true one test and false in the other.
// - All other conditions' values must be equal or marked as "don't care".
void MCDCRecord::findIndependencePairs() {
if (IndependencePairs)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug output for something like "independence pairs already computed; not finding anything"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Soft updates" is assumed. It is invalidated if "merging" is executed.

return;

IndependencePairs.emplace();

unsigned NumTVs = TV.size();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const?

// Will be replaced to shorter expr.
unsigned TVTrueIdx = std::distance(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const?

TV.begin(),
std::find_if(TV.begin(), TV.end(),
[&](auto I) { return (I.second == MCDCRecord::MCDC_True); })

);
for (unsigned I = TVTrueIdx; I < NumTVs; ++I) {
const auto &[A, ACond] = TV[I];
assert(ACond == MCDCRecord::MCDC_True);
for (unsigned J = 0; J < TVTrueIdx; ++J) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if I understand correctly, we assume that

  • The outer loop handles true conditions
  • The inner loop handles false conditions

I think this renaming may be clearer:

// All true test vectors are in the range [FirstTrueTVIdx, NumTVs).
for (unsigned TrueTVIdx = FirstTrueTVIdx; TrueTVIdx < NumTVs; ++TrueTVIdx) {
  ...
  // All false test vectors are in the range [0, FirstTrueTVIdx)
  for (unsigned FalseTVIdx = 0; FalseTVIdx < FirstTrueTVIdx; ++ FalseTVIdx) {
    ...
  }
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah this is all code movement. Duh. Perhaps we can address this in a follow-up NFC commit.

const auto &[B, BCond] = TV[J];
assert(BCond == MCDCRecord::MCDC_False);
// If the two vectors differ in exactly one condition, ignoring DontCare
// conditions, we have found an independence pair.
auto AB = A.getDifferences(B);
if (AB.count() == 1)
IndependencePairs->insert(
{AB.find_first(), std::make_pair(J + 1, I + 1)});
}
}
}

mcdc::TVIdxBuilder::TVIdxBuilder(const SmallVectorImpl<ConditionIDs> &NextIDs,
int Offset)
: Indices(NextIDs.size()) {
Expand Down Expand Up @@ -375,9 +409,6 @@ class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder {
/// ExecutedTestVectorBitmap.
MCDCRecord::TestVectors &ExecVectors;

/// Number of False items in ExecVectors
unsigned NumExecVectorsF;

#ifndef NDEBUG
DenseSet<unsigned> TVIdxs;
#endif
Expand Down Expand Up @@ -446,34 +477,11 @@ class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder {
// Fill ExecVectors order by False items and True items.
// ExecVectors is the alias of ExecVectorsByCond[false], so
// Append ExecVectorsByCond[true] on it.
NumExecVectorsF = ExecVectors.size();
auto &ExecVectorsT = ExecVectorsByCond[true];
ExecVectors.append(std::make_move_iterator(ExecVectorsT.begin()),
std::make_move_iterator(ExecVectorsT.end()));
}

// Find an independence pair for each condition:
// - The condition is true in one test and false in the other.
// - The decision outcome is true one test and false in the other.
// - All other conditions' values must be equal or marked as "don't care".
void findIndependencePairs() {
unsigned NumTVs = ExecVectors.size();
for (unsigned I = NumExecVectorsF; I < NumTVs; ++I) {
const auto &[A, ACond] = ExecVectors[I];
assert(ACond == MCDCRecord::MCDC_True);
for (unsigned J = 0; J < NumExecVectorsF; ++J) {
const auto &[B, BCond] = ExecVectors[J];
assert(BCond == MCDCRecord::MCDC_False);
// If the two vectors differ in exactly one condition, ignoring DontCare
// conditions, we have found an independence pair.
auto AB = A.getDifferences(B);
if (AB.count() == 1)
IndependencePairs.insert(
{AB.find_first(), std::make_pair(J + 1, I + 1)});
}
}
}

public:
/// Process the MC/DC Record in order to produce a result for a boolean
/// expression. This process includes tracking the conditions that comprise
Expand Down Expand Up @@ -509,13 +517,8 @@ class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder {
// 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.
findIndependencePairs();

// Record Test vectors, executed vectors, and independence pairs.
return MCDCRecord(Region, std::move(ExecVectors),
std::move(IndependencePairs), std::move(Folded),
return MCDCRecord(Region, std::move(ExecVectors), std::move(Folded),
std::move(PosToID), std::move(CondLoc));
}
};
Expand Down
Loading