Skip to content
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

[MC/DC][Coverage] Loosen the limit of NumConds from 6 #82448

Merged
merged 48 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
d168e0c
Implement MCDCTVIdxBuilder and MCDCTestVectorBuilder (LLVM side)
chapuni Feb 4, 2024
35b19ea
Revert "Implement MCDCTVIdxBuilder and MCDCTestVectorBuilder (LLVM si…
chapuni Feb 6, 2024
8c777eb
[Coverage] MCDCRecordProcessor: Find `ExecVectors` directly
chapuni Feb 6, 2024
56042d3
Merge branch 'mcdc/xv' into HEAD
chapuni Feb 6, 2024
5432aec
Implement MCDCTVIdxBuilder (LLVM side)
chapuni Feb 4, 2024
3ee8a61
Update comments and assertions
chapuni Feb 6, 2024
2fd504a
Merge remote-tracking branch 'origin/main' into mcdc/tvidx
chapuni Feb 8, 2024
1f0f3fc
Reorganize TVIdxBuilder
chapuni Feb 12, 2024
06c0801
Merge remote-tracking branch 'origin/main' into mcdc/tvidx
chapuni Feb 13, 2024
aa5b2f5
Merge remote-tracking branch 'origin/main' into HEAD
chapuni Feb 15, 2024
e3de647
[CoverageMapping] Refactor `mcdc::TVIdxBuilder`
chapuni Feb 15, 2024
753d0ad
remove <functional>
chapuni Feb 15, 2024
17cbac7
Update comments.
chapuni Feb 21, 2024
1a4ffa7
Add unittest
chapuni Feb 21, 2024
b5ecfcc
[MC/DC][Coverage] Loosen the limit of NumConds from 6
chapuni Feb 15, 2024
0ffad9c
Update testcases
chapuni Feb 21, 2024
c96fd2c
Use llvm::sort
chapuni Feb 21, 2024
357a693
EXPECT_
chapuni Feb 21, 2024
83d104c
Merge branch 'mcdc/tvidx' into HEAD
chapuni Feb 21, 2024
14c795e
Hide NConds
chapuni Feb 21, 2024
b6c1174
Merge remote-tracking branch 'chapuni/main' into mcdc/tvidx
chapuni Feb 25, 2024
cf936f6
Merge branch 'users/chapuni/mcdc/tvidx' into HEAD
chapuni Feb 25, 2024
662bdd6
Reformat
chapuni Feb 25, 2024
ac16655
Merge remote-tracking branch 'origin/main' into mcdc/clangtvidx
chapuni Feb 26, 2024
90bf8e9
Clarify bool
chapuni Feb 26, 2024
9eb6951
Update comments
chapuni Feb 26, 2024
86d67c6
Use MutableArrayRef :)
chapuni Feb 26, 2024
01abca2
Merge remote-tracking branch 'origin/main' into mcdc/clangtvidx
chapuni Feb 27, 2024
183c706
Merge branch 'main' into mcdc/clangtvidx
chapuni Apr 19, 2024
dd6f8be
Clarify 3rd arg of mcdc.tvbitmap.update is unused
chapuni May 9, 2024
cdd5531
Modify 3rd arg of mcdc.parameters to bits
chapuni May 8, 2024
54e6044
isMCDCBranch: Remove assert
chapuni May 9, 2024
b1a7100
isMCDCDecision: Remove assert
chapuni May 9, 2024
39802c5
Make llvm-cov capable of clang-18's profdata
chapuni May 14, 2024
f54c64d
Fix unittest
chapuni May 15, 2024
a6f7eef
Introduce `-fmcdc-max-conditions` and `-fmcdc-max-test-vectors`
chapuni May 16, 2024
0dd7ead
Merge branch 'main' into HEAD
chapuni May 20, 2024
cf837ae
mcdc-system-headers.cpp: Update
chapuni May 20, 2024
9e14c2a
llvm-cov tests for old (Version11) files
chapuni May 20, 2024
04f18ae
Merge branch 'main' into mcdc/clangtvidx
chapuni May 27, 2024
3ff3eb7
Update CoverageMapping/mcdc-scratch-space.c
chapuni May 27, 2024
296f5c5
Don't create tvbitmap_update if the record is allocated but excluded.
chapuni May 27, 2024
722424b
Update mcdc-error-conditions.cpp
chapuni Jun 10, 2024
183bc52
Update documents
chapuni Jun 10, 2024
83088f1
mcdc.tvupdate doesn't depend on condupdate
chapuni Jun 10, 2024
40872f5
Update SourceBasedCodeCoverage.rst
chapuni Jun 11, 2024
be5b28b
SourceBasedCodeCoverage.rst: s/you/users/
chapuni Jun 11, 2024
450d86b
SourceBasedCodeCoverage.rst: Clarify to be excluded.
chapuni Jun 13, 2024
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
61 changes: 28 additions & 33 deletions clang/lib/CodeGen/CodeGenPGO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,7 @@ struct MapRegionCounters : public RecursiveASTVisitor<MapRegionCounters> {
PGOHash Hash;
/// The map of statements to counters.
llvm::DenseMap<const Stmt *, unsigned> &CounterMap;
/// The next bitmap byte index to assign.
unsigned NextMCDCBitmapIdx;
/// The state of MC/DC Coverage in this function.
MCDC::State &MCDCState;
/// Maximum number of supported MC/DC conditions in a boolean expression.
unsigned MCDCMaxCond;
Expand All @@ -178,7 +177,7 @@ struct MapRegionCounters : public RecursiveASTVisitor<MapRegionCounters> {
MCDC::State &MCDCState, unsigned MCDCMaxCond,
DiagnosticsEngine &Diag)
: NextCounter(0), Hash(HashVersion), CounterMap(CounterMap),
NextMCDCBitmapIdx(0), MCDCState(MCDCState), MCDCMaxCond(MCDCMaxCond),
MCDCState(MCDCState), MCDCMaxCond(MCDCMaxCond),
ProfileVersion(ProfileVersion), Diag(Diag) {}

// Blocks and lambdas are handled as separate functions, so we need not
Expand Down Expand Up @@ -306,11 +305,8 @@ struct MapRegionCounters : public RecursiveASTVisitor<MapRegionCounters> {
return false;
}

// Otherwise, allocate the number of bytes required for the bitmap
// based on the number of conditions. Must be at least 1-byte long.
MCDCState.BitmapMap[BinOp] = NextMCDCBitmapIdx;
unsigned SizeInBits = std::max<unsigned>(1L << NumCond, CHAR_BIT);
NextMCDCBitmapIdx += SizeInBits / CHAR_BIT;
// Otherwise, allocate the Decision.
MCDCState.BitmapMap[BinOp].BitmapIdx = 0;
}
return true;
}
Expand Down Expand Up @@ -983,7 +979,7 @@ void CodeGenPGO::mapRegionCounters(const Decl *D) {
// for most embedded applications. Setting a maximum value prevents the
// bitmap footprint from growing too large without the user's knowledge. In
// the future, this value could be adjusted with a command-line option.
unsigned MCDCMaxConditions = (CGM.getCodeGenOpts().MCDCCoverage) ? 6 : 0;
unsigned MCDCMaxConditions = (CGM.getCodeGenOpts().MCDCCoverage) ? 32767 : 0;
Copy link

Choose a reason for hiding this comment

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

I think the limit should be controlled by a flag, with a reasonable default value.

E.g.

-mcdc-max-conditions=<value>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

MaxCond is one of options. I guess it could be configurable just for theirs coding rules, since I think controlling MaxCond would not make sense.
I know we have to introduce other options as well. I will do later.

  • MaxCond
  • MaxTVs to restrict per-expression number of TVs
  • "Max bytes of bitmap in the CU" to restict inter-function size of the bitmap.

Copy link
Contributor

Choose a reason for hiding this comment

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

I know you will do this later, but I also wanted to echo the desire for a means to control the condition limit to warn the user against unintended memory footprint expansion, most useful in embedded development contexts. It may not otherwise be obvious to most users what is contributing to larger memory usage. We could also override the default in downstream toolchains.

Copy link

Choose a reason for hiding this comment

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

Also why 32767? Max signed 16-bit value? Is there a reason?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@evodius96 I've introduced -fmcdc-max-conditions=32767.

against unintended memory footprint expansion
I think -fmcdc-max-test-vectors=64 can restrict max bitmap size as the current implementation.

@ornata I have no idea how to determine practical limit for that. So I use SHRT_MAX.


RegionCounterMap.reset(new llvm::DenseMap<const Stmt *, unsigned>);
RegionMCDCState.reset(new MCDC::State);
Expand All @@ -999,7 +995,6 @@ void CodeGenPGO::mapRegionCounters(const Decl *D) {
Walker.TraverseDecl(const_cast<CapturedDecl *>(CD));
assert(Walker.NextCounter > 0 && "no entry counter mapped for decl");
NumRegionCounters = Walker.NextCounter;
RegionMCDCState->BitmapBytes = Walker.NextMCDCBitmapIdx;
FunctionHash = Walker.Hash.finalize();
}

Expand Down Expand Up @@ -1124,9 +1119,11 @@ void CodeGenPGO::emitMCDCParameters(CGBuilderTy &Builder) {
// Emit intrinsic representing MCDC bitmap parameters at function entry.
// This is used by the instrumentation pass, but it isn't actually lowered to
// anything.
llvm::Value *Args[3] = {llvm::ConstantExpr::getBitCast(FuncNameVar, I8PtrTy),
Builder.getInt64(FunctionHash),
Builder.getInt32(RegionMCDCState->BitmapBytes)};
llvm::Value *Args[3] = {
llvm::ConstantExpr::getBitCast(FuncNameVar, I8PtrTy),
Builder.getInt64(FunctionHash),
Builder.getInt32(llvm::alignTo(RegionMCDCState->BitmapBits, CHAR_BIT) /
CHAR_BIT)};
chapuni marked this conversation as resolved.
Show resolved Hide resolved
Builder.CreateCall(
CGM.getIntrinsic(llvm::Intrinsic::instrprof_mcdc_parameters), Args);
}
Expand All @@ -1139,12 +1136,9 @@ void CodeGenPGO::emitMCDCTestVectorBitmapUpdate(CGBuilderTy &Builder,

S = S->IgnoreParens();

auto ExprMCDCBitmapMapIterator = RegionMCDCState->BitmapMap.find(S);
if (ExprMCDCBitmapMapIterator == RegionMCDCState->BitmapMap.end())
if (!RegionMCDCState->BitmapMap.contains(S))
return;

// Extract the ID of the global bitmap associated with this expression.
unsigned MCDCTestVectorBitmapID = ExprMCDCBitmapMapIterator->second;
auto *I8PtrTy = llvm::PointerType::getUnqual(CGM.getLLVMContext());

// Emit intrinsic responsible for updating the global bitmap corresponding to
Expand All @@ -1154,9 +1148,8 @@ void CodeGenPGO::emitMCDCTestVectorBitmapUpdate(CGBuilderTy &Builder,
// index represents an executed test vector.
llvm::Value *Args[5] = {llvm::ConstantExpr::getBitCast(FuncNameVar, I8PtrTy),
Builder.getInt64(FunctionHash),
Builder.getInt32(RegionMCDCState->BitmapBytes),
Builder.getInt32(MCDCTestVectorBitmapID),
MCDCCondBitmapAddr.getPointer()};
Builder.getInt32(RegionMCDCState->BitmapBits),
Builder.getInt32(0), MCDCCondBitmapAddr.getPointer()};
Builder.CreateCall(
CGM.getIntrinsic(llvm::Intrinsic::instrprof_mcdc_tvbitmap_update), Args);
}
Expand Down Expand Up @@ -1195,21 +1188,23 @@ void CodeGenPGO::emitMCDCCondBitmapUpdate(CGBuilderTy &Builder, const Expr *S,
return;

// Extract the ID of the condition we are setting in the bitmap.
auto CondID = ExprMCDCConditionIDMapIterator->second;
assert(CondID >= 0 && "Condition has no ID!");
const auto &Branch = ExprMCDCConditionIDMapIterator->second;
assert(Branch.ID >= 0 && "Condition has no ID!");

auto *I8PtrTy = llvm::PointerType::getUnqual(CGM.getLLVMContext());
// Cancel the emission if the Decision is erased after the allocation.
const auto DecisionIter =
RegionMCDCState->BitmapMap.find(Branch.DecisionStmt);
if (DecisionIter == RegionMCDCState->BitmapMap.end())
return;

// Emit intrinsic that updates a dedicated temporary value on the stack after
// a condition is evaluated. After the set of conditions has been updated,
// the resulting value is used to update the boolean expression's bitmap.
llvm::Value *Args[5] = {llvm::ConstantExpr::getBitCast(FuncNameVar, I8PtrTy),
Builder.getInt64(FunctionHash),
Builder.getInt32(CondID),
MCDCCondBitmapAddr.getPointer(), Val};
Builder.CreateCall(
CGM.getIntrinsic(llvm::Intrinsic::instrprof_mcdc_condbitmap_update),
Args);
const auto &TVIdxs = DecisionIter->second.Indices[Branch.ID];

auto *CurTV = Builder.CreateLoad(MCDCCondBitmapAddr,
"mcdc." + Twine(Branch.ID + 1) + ".cur");
auto *NewTV = Builder.CreateAdd(CurTV, Builder.getInt32(TVIdxs[true]));
NewTV = Builder.CreateSelect(
Val, NewTV, Builder.CreateAdd(CurTV, Builder.getInt32(TVIdxs[false])));
Builder.CreateStore(NewTV, MCDCCondBitmapAddr);
chapuni marked this conversation as resolved.
Show resolved Hide resolved
}

void CodeGenPGO::setValueProfilingFlag(llvm::Module &M) {
Expand Down
115 changes: 111 additions & 4 deletions clang/lib/CodeGen/CoverageMappingGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,16 @@ class SourceMappingRegion {

bool isBranch() const { return FalseCount.has_value(); }

bool isMCDCBranch() const {
const auto *BranchParams = std::get_if<mcdc::BranchParameters>(&MCDCParams);
assert(!BranchParams || BranchParams->ID >= 0);
return BranchParams;
}

const auto &getMCDCBranchParams() const {
return mcdc::getParams<const mcdc::BranchParameters>(MCDCParams);
}

bool isMCDCDecision() const {
const auto *DecisionParams =
std::get_if<mcdc::DecisionParameters>(&MCDCParams);
Expand All @@ -194,6 +204,8 @@ class SourceMappingRegion {
}

const mcdc::Parameters &getMCDCParams() const { return MCDCParams; }

void resetMCDCParams() { MCDCParams = mcdc::Parameters(); }
};

/// Spelling locations for the start and end of a source region.
Expand Down Expand Up @@ -684,7 +696,9 @@ struct MCDCCoverageBuilder {

llvm::SmallVector<mcdc::ConditionIDs> DecisionStack;
MCDC::State &MCDCState;
llvm::DenseMap<const Stmt *, mcdc::ConditionID> &CondIDs;
llvm::DenseMap<const Stmt *, MCDC::State::Branch> &CondIDs;
const Stmt *DecisionStmt = nullptr;
llvm::DenseMap<mcdc::ConditionID, mcdc::ConditionIDs> cache;
mcdc::ConditionID NextID = 0;
bool NotMapped = false;

Expand Down Expand Up @@ -714,7 +728,7 @@ struct MCDCCoverageBuilder {

/// Set the given condition's ID.
void setCondID(const Expr *Cond, mcdc::ConditionID ID) {
CondIDs[CodeGenFunction::stripCond(Cond)] = ID;
CondIDs[CodeGenFunction::stripCond(Cond)] = {ID, DecisionStmt};
}

/// Return the ID of a given condition.
Expand All @@ -723,7 +737,15 @@ struct MCDCCoverageBuilder {
if (I == CondIDs.end())
return -1;
else
return I->second;
return I->second.ID;
}

void ccc(const Expr *CondExpr, mcdc::ConditionIDs IDs) {
chapuni marked this conversation as resolved.
Show resolved Hide resolved
auto ID = getCondID(CondExpr);
if (ID < 0)
return;
if (!cache.contains(ID))
cache[ID] = IDs;
chapuni marked this conversation as resolved.
Show resolved Hide resolved
}

/// Return the LHS Decision ([0,0] if not set).
Expand All @@ -745,6 +767,12 @@ struct MCDCCoverageBuilder {
if (NotMapped)
return;

if (NextID == 0) {
cache.clear();
DecisionStmt = E;
assert(MCDCState.BitmapMap.contains(E));
}

const mcdc::ConditionIDs &ParentDecision = DecisionStack.back();

// If the operator itself has an assigned ID, this means it represents a
Expand Down Expand Up @@ -794,9 +822,33 @@ struct MCDCCoverageBuilder {
// Set number of conditions and reset.
unsigned TotalConds = NextID;

llvm::SmallVector<mcdc::ConditionIDs> R(cache.size());
for (auto [ID, Conds] : cache)
R[ID] = Conds;
mcdc::TVIdxBuilder xxx(R, MCDCState.BitmapBits);
auto NumTVs = xxx.NumTestVectors;
assert(MCDCState.BitmapMap.contains(E));
auto MaxTVs = mcdc::TVIdxBuilder::HardMaxTVs;
if (NumTVs == MaxTVs) {
auto &Diag = CGM.getDiags();
unsigned DiagID =
Diag.getCustomDiagID(DiagnosticsEngine::Warning,
"unsupported MC/DC boolean expression; "
"number of test vectors (%0) exceeds max (%1). "
"Expression will not be covered");
Diag.Report(E->getBeginLoc(), DiagID) << NumTVs << MaxTVs;

MCDCState.BitmapMap[E].BitmapIdx = 0; // Mark to be erased
} else {
// Last pos
MCDCState.BitmapMap[E] = {MCDCState.BitmapBits += NumTVs,
std::move(xxx.Indices)};
}

// Reset ID back to beginning.
NextID = 0;

// Return the number even if the Decision will be erased.
return TotalConds;
}
};
Expand Down Expand Up @@ -853,7 +905,9 @@ struct CounterCoverageMappingBuilder
return Counter::getCounter(CounterMap[S]);
}

unsigned getRegionBitmap(const Stmt *S) { return MCDCState.BitmapMap[S]; }
auto getRegionBitmap(const Stmt *S) {
return MCDCState.BitmapMap[S].BitmapIdx;
}

/// Push a region onto the stack.
///
Expand Down Expand Up @@ -1959,9 +2013,44 @@ struct CounterCoverageMappingBuilder
subtractCounters(ParentCount, TrueCount));
}

void RewindDecision(unsigned Since) {
#ifndef NDEBUG
llvm::DenseSet<mcdc::ConditionID> SeenIDs;
#endif
unsigned NConds = 0;
chapuni marked this conversation as resolved.
Show resolved Hide resolved

assert(Since <= SourceRegions.size());
auto I = SourceRegions.begin() + Since;
while (I != SourceRegions.end()) {
if (I->isMCDCDecision()) {
assert(I->getMCDCDecisionParams().BitmapIdx == 0 &&
"It should be valid");
assert(NConds == 0 && "Duplicate MCDCDecision");
NConds = I->getMCDCDecisionParams().NumConditions;
assert(NConds > 0 && "Malformed MCDCDecision");
I = SourceRegions.erase(I);
continue;
}

if (I->isMCDCBranch()) {
assert(SeenIDs.insert(I->getMCDCBranchParams().ID).second &&
"Duplicate CondID");
I->resetMCDCParams();
}

++I;
}

assert(NConds > 0 && "MCDCDecision wasn't found");
assert(SeenIDs.size() == NConds && "Unexpected number of MCDCBranch(es)");
(void)NConds;
}

void VisitBinLAnd(const BinaryOperator *E) {
bool IsRootNode = MCDCBuilder.isIdle();

unsigned SourceRegionsSince = SourceRegions.size();

// Keep track of Binary Operator and assign MCDC condition IDs.
MCDCBuilder.pushAndAssignIDs(E);

Expand All @@ -1979,6 +2068,9 @@ struct CounterCoverageMappingBuilder
// Track RHS True/False Decision.
const auto DecisionRHS = MCDCBuilder.back();

MCDCBuilder.ccc(E->getLHS(), DecisionLHS);
MCDCBuilder.ccc(E->getRHS(), DecisionRHS);
chapuni marked this conversation as resolved.
Show resolved Hide resolved

// Create MCDC Decision Region if at top-level (root).
unsigned NumConds = 0;
if (IsRootNode && (NumConds = MCDCBuilder.getTotalConditionsAndReset(E)))
Expand All @@ -2000,6 +2092,11 @@ struct CounterCoverageMappingBuilder
// Create Branch Region around RHS condition.
createBranchRegion(E->getRHS(), RHSTrueCnt,
subtractCounters(RHSExecCnt, RHSTrueCnt), DecisionRHS);

if (IsRootNode && NumConds > 0 && getRegionBitmap(E) == 0) {
RewindDecision(SourceRegionsSince);
MCDCState.BitmapMap.erase(E);
}
}

// Determine whether the right side of OR operation need to be visited.
Expand All @@ -2015,6 +2112,8 @@ struct CounterCoverageMappingBuilder
void VisitBinLOr(const BinaryOperator *E) {
bool IsRootNode = MCDCBuilder.isIdle();

unsigned SourceRegionsSince = SourceRegions.size();

// Keep track of Binary Operator and assign MCDC condition IDs.
MCDCBuilder.pushAndAssignIDs(E);

Expand All @@ -2032,6 +2131,9 @@ struct CounterCoverageMappingBuilder
// Track RHS True/False Decision.
const auto DecisionRHS = MCDCBuilder.back();

MCDCBuilder.ccc(E->getLHS(), DecisionLHS);
MCDCBuilder.ccc(E->getRHS(), DecisionRHS);

// Create MCDC Decision Region if at top-level (root).
unsigned NumConds = 0;
if (IsRootNode && (NumConds = MCDCBuilder.getTotalConditionsAndReset(E)))
Expand All @@ -2057,6 +2159,11 @@ struct CounterCoverageMappingBuilder
// Create Branch Region around RHS condition.
createBranchRegion(E->getRHS(), subtractCounters(RHSExecCnt, RHSFalseCnt),
RHSFalseCnt, DecisionRHS);

if (IsRootNode && NumConds > 0 && getRegionBitmap(E) == 0) {
RewindDecision(SourceRegionsSince);
MCDCState.BitmapMap.erase(E);
}
}

void VisitLambdaExpr(const LambdaExpr *LE) {
Expand Down
18 changes: 15 additions & 3 deletions clang/lib/CodeGen/MCDCState.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,21 @@ using namespace llvm::coverage::mcdc;

/// Per-Function MC/DC state
struct State {
unsigned BitmapBytes = 0;
llvm::DenseMap<const Stmt *, unsigned> BitmapMap;
llvm::DenseMap<const Stmt *, ConditionID> CondIDMap;
unsigned BitmapBits = 0;

struct Decision {
unsigned BitmapIdx;
llvm::SmallVector<std::array<int, 2>> Indices;
};

llvm::DenseMap<const Stmt *, Decision> BitmapMap;

struct Branch {
ConditionID ID;
const Stmt *DecisionStmt;
};

llvm::DenseMap<const Stmt *, Branch> CondIDMap;
};

} // namespace clang::CodeGen::MCDC
Expand Down
Loading
Loading