Skip to content

Commit b422fa6

Browse files
committed
Revert "[MC/DC][Coverage] Loosen the limit of NumConds from 6 (#82448)"
This broke the lit tests on Mac: https://green.lab.llvm.org/job/llvm.org/job/clang-stage1-RA/1096/ > By storing possible test vectors instead of combinations of conditions, > the restriction is dramatically relaxed. > > This introduces two options to `cc1`: > > * `-fmcdc-max-conditions=32767` > * `-fmcdc-max-test-vectors=2147483646` > > This change makes coverage mapping, profraw, and profdata incompatible > with Clang-18. > > - Bitmap semantics changed. It is incompatible with previous format. > - `BitmapIdx` in `Decision` points to the end of the bitmap. > - Bitmap is packed per function. > - `llvm-cov` can understand `profdata` generated by `llvm-profdata-18`. > > RFC: > https://discourse.llvm.org/t/rfc-coverage-new-algorithm-and-file-format-for-mc-dc/76798 This reverts commit 7ead2d8.
1 parent 880d370 commit b422fa6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+297
-529
lines changed

clang/docs/SourceBasedCodeCoverage.rst

+4-25
Original file line numberDiff line numberDiff line change
@@ -484,31 +484,10 @@ MC/DC Instrumentation
484484
---------------------
485485

486486
When instrumenting for Modified Condition/Decision Coverage (MC/DC) using the
487-
clang option ``-fcoverage-mcdc``, there are two hard limits.
488-
489-
The maximum number of terms is limited to 32767, which is practical for
490-
handwritten expressions. To be more restrictive in order to enforce coding rules,
491-
use ``-Xclang -fmcdc-max-conditions=n``. Expressions with exceeded condition
492-
counts ``n`` will generate warnings and will be excluded in the MC/DC coverage.
493-
494-
The number of test vectors (the maximum number of possible combinations of
495-
expressions) is limited to 2,147,483,646. In this case, approximately
496-
256MiB (==2GiB/8) is used to record test vectors.
497-
498-
To reduce memory usage, users can limit the maximum number of test vectors per
499-
expression with ``-Xclang -fmcdc-max-test-vectors=m``.
500-
If the number of test vectors resulting from the analysis of an expression
501-
exceeds ``m``, a warning will be issued and the expression will be excluded
502-
from the MC/DC coverage.
503-
504-
The number of test vectors ``m``, for ``n`` terms in an expression, can be
505-
``m <= 2^n`` in the theoretical worst case, but is usually much smaller.
506-
In simple cases, such as expressions consisting of a sequence of single
507-
operators, ``m == n+1``. For example, ``(a && b && c && d && e && f && g)``
508-
requires 8 test vectors.
509-
510-
Expressions such as ``((a0 && b0) || (a1 && b1) || ...)`` can cause the
511-
number of test vectors to increase exponentially.
487+
clang option ``-fcoverage-mcdc``, users are limited to at most **six** leaf-level
488+
conditions in a boolean expression. A warning will be generated for boolean
489+
expressions that contain more than six, and they will not be instrumented for
490+
MC/DC.
512491

513492
Also, if a boolean expression is embedded in the nest of another boolean
514493
expression but separated by a non-logical operator, this is also not supported.

clang/include/clang/Basic/CodeGenOptions.def

-2
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,6 @@ CODEGENOPT(CoverageMapping , 1, 0) ///< Generate coverage mapping regions to
223223
CODEGENOPT(DumpCoverageMapping , 1, 0) ///< Dump the generated coverage mapping
224224
///< regions.
225225
CODEGENOPT(MCDCCoverage , 1, 0) ///< Enable MC/DC code coverage criteria.
226-
VALUE_CODEGENOPT(MCDCMaxConds, 16, 32767) ///< MC/DC Maximum conditions.
227-
VALUE_CODEGENOPT(MCDCMaxTVs, 32, 0x7FFFFFFE) ///< MC/DC Maximum test vectors.
228226

229227
/// If -fpcc-struct-return or -freg-struct-return is specified.
230228
ENUM_CODEGENOPT(StructReturnConvention, StructReturnConventionKind, 2, SRCK_Default)

clang/include/clang/Driver/Options.td

-8
Original file line numberDiff line numberDiff line change
@@ -1790,14 +1790,6 @@ defm mcdc_coverage : BoolFOption<"coverage-mcdc",
17901790
"Enable MC/DC criteria when generating code coverage">,
17911791
NegFlag<SetFalse, [], [ClangOption], "Disable MC/DC coverage criteria">,
17921792
BothFlags<[], [ClangOption, CLOption]>>;
1793-
def fmcdc_max_conditions_EQ : Joined<["-"], "fmcdc-max-conditions=">,
1794-
Group<f_Group>, Visibility<[CC1Option]>,
1795-
HelpText<"Maximum number of conditions in MC/DC coverage">,
1796-
MarshallingInfoInt<CodeGenOpts<"MCDCMaxConds">, "32767">;
1797-
def fmcdc_max_test_vectors_EQ : Joined<["-"], "fmcdc-max-test-vectors=">,
1798-
Group<f_Group>, Visibility<[CC1Option]>,
1799-
HelpText<"Maximum number of test vectors in MC/DC coverage">,
1800-
MarshallingInfoInt<CodeGenOpts<"MCDCMaxTVs">, "0x7FFFFFFE">;
18011793
def fprofile_generate : Flag<["-"], "fprofile-generate">,
18021794
Group<f_Group>, Visibility<[ClangOption, CLOption]>,
18031795
HelpText<"Generate instrumented code to collect execution counts into default.profraw (overridden by LLVM_PROFILE_FILE env var)">;

clang/lib/CodeGen/CodeGenPGO.cpp

+23-27
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ struct MapRegionCounters : public RecursiveASTVisitor<MapRegionCounters> {
167167
PGOHash Hash;
168168
/// The map of statements to counters.
169169
llvm::DenseMap<const Stmt *, unsigned> &CounterMap;
170+
/// The next bitmap byte index to assign.
171+
unsigned NextMCDCBitmapIdx;
170172
/// The state of MC/DC Coverage in this function.
171173
MCDC::State &MCDCState;
172174
/// Maximum number of supported MC/DC conditions in a boolean expression.
@@ -181,7 +183,7 @@ struct MapRegionCounters : public RecursiveASTVisitor<MapRegionCounters> {
181183
MCDC::State &MCDCState, unsigned MCDCMaxCond,
182184
DiagnosticsEngine &Diag)
183185
: NextCounter(0), Hash(HashVersion), CounterMap(CounterMap),
184-
MCDCState(MCDCState), MCDCMaxCond(MCDCMaxCond),
186+
NextMCDCBitmapIdx(0), MCDCState(MCDCState), MCDCMaxCond(MCDCMaxCond),
185187
ProfileVersion(ProfileVersion), Diag(Diag) {}
186188

187189
// Blocks and lambdas are handled as separate functions, so we need not
@@ -312,8 +314,11 @@ struct MapRegionCounters : public RecursiveASTVisitor<MapRegionCounters> {
312314
return true;
313315
}
314316

315-
// Otherwise, allocate the Decision.
316-
MCDCState.DecisionByStmt[BinOp].BitmapIdx = 0;
317+
// Otherwise, allocate the number of bytes required for the bitmap
318+
// based on the number of conditions. Must be at least 1-byte long.
319+
MCDCState.DecisionByStmt[BinOp].BitmapIdx = NextMCDCBitmapIdx;
320+
unsigned SizeInBits = std::max<unsigned>(1L << NumCond, CHAR_BIT);
321+
NextMCDCBitmapIdx += SizeInBits / CHAR_BIT;
317322
}
318323
return true;
319324
}
@@ -1078,9 +1083,7 @@ void CodeGenPGO::mapRegionCounters(const Decl *D) {
10781083
// for most embedded applications. Setting a maximum value prevents the
10791084
// bitmap footprint from growing too large without the user's knowledge. In
10801085
// the future, this value could be adjusted with a command-line option.
1081-
unsigned MCDCMaxConditions =
1082-
(CGM.getCodeGenOpts().MCDCCoverage ? CGM.getCodeGenOpts().MCDCMaxConds
1083-
: 0);
1086+
unsigned MCDCMaxConditions = (CGM.getCodeGenOpts().MCDCCoverage) ? 6 : 0;
10841087

10851088
RegionCounterMap.reset(new llvm::DenseMap<const Stmt *, unsigned>);
10861089
RegionMCDCState.reset(new MCDC::State);
@@ -1096,6 +1099,7 @@ void CodeGenPGO::mapRegionCounters(const Decl *D) {
10961099
Walker.TraverseDecl(const_cast<CapturedDecl *>(CD));
10971100
assert(Walker.NextCounter > 0 && "no entry counter mapped for decl");
10981101
NumRegionCounters = Walker.NextCounter;
1102+
RegionMCDCState->BitmapBytes = Walker.NextMCDCBitmapIdx;
10991103
FunctionHash = Walker.Hash.finalize();
11001104
}
11011105

@@ -1228,7 +1232,7 @@ void CodeGenPGO::emitMCDCParameters(CGBuilderTy &Builder) {
12281232
// anything.
12291233
llvm::Value *Args[3] = {llvm::ConstantExpr::getBitCast(FuncNameVar, I8PtrTy),
12301234
Builder.getInt64(FunctionHash),
1231-
Builder.getInt32(RegionMCDCState->BitmapBits)};
1235+
Builder.getInt32(RegionMCDCState->BitmapBytes)};
12321236
Builder.CreateCall(
12331237
CGM.getIntrinsic(llvm::Intrinsic::instrprof_mcdc_parameters), Args);
12341238
}
@@ -1246,11 +1250,6 @@ void CodeGenPGO::emitMCDCTestVectorBitmapUpdate(CGBuilderTy &Builder,
12461250
if (DecisionStateIter == RegionMCDCState->DecisionByStmt.end())
12471251
return;
12481252

1249-
// Don't create tvbitmap_update if the record is allocated but excluded.
1250-
// Or `bitmap |= (1 << 0)` would be wrongly executed to the next bitmap.
1251-
if (DecisionStateIter->second.Indices.size() == 0)
1252-
return;
1253-
12541253
// Extract the offset of the global bitmap associated with this expression.
12551254
unsigned MCDCTestVectorBitmapOffset = DecisionStateIter->second.BitmapIdx;
12561255
auto *I8PtrTy = llvm::PointerType::getUnqual(CGM.getLLVMContext());
@@ -1262,7 +1261,7 @@ void CodeGenPGO::emitMCDCTestVectorBitmapUpdate(CGBuilderTy &Builder,
12621261
// index represents an executed test vector.
12631262
llvm::Value *Args[5] = {llvm::ConstantExpr::getBitCast(FuncNameVar, I8PtrTy),
12641263
Builder.getInt64(FunctionHash),
1265-
Builder.getInt32(0), // Unused
1264+
Builder.getInt32(RegionMCDCState->BitmapBytes),
12661265
Builder.getInt32(MCDCTestVectorBitmapOffset),
12671266
MCDCCondBitmapAddr.emitRawPointer(CGF)};
12681267
Builder.CreateCall(
@@ -1306,22 +1305,19 @@ void CodeGenPGO::emitMCDCCondBitmapUpdate(CGBuilderTy &Builder, const Expr *S,
13061305
// Extract the ID of the condition we are setting in the bitmap.
13071306
const auto &Branch = BranchStateIter->second;
13081307
assert(Branch.ID >= 0 && "Condition has no ID!");
1309-
assert(Branch.DecisionStmt);
1310-
1311-
// Cancel the emission if the Decision is erased after the allocation.
1312-
const auto DecisionIter =
1313-
RegionMCDCState->DecisionByStmt.find(Branch.DecisionStmt);
1314-
if (DecisionIter == RegionMCDCState->DecisionByStmt.end())
1315-
return;
13161308

1317-
const auto &TVIdxs = DecisionIter->second.Indices[Branch.ID];
1309+
auto *I8PtrTy = llvm::PointerType::getUnqual(CGM.getLLVMContext());
13181310

1319-
auto *CurTV = Builder.CreateLoad(MCDCCondBitmapAddr,
1320-
"mcdc." + Twine(Branch.ID + 1) + ".cur");
1321-
auto *NewTV = Builder.CreateAdd(CurTV, Builder.getInt32(TVIdxs[true]));
1322-
NewTV = Builder.CreateSelect(
1323-
Val, NewTV, Builder.CreateAdd(CurTV, Builder.getInt32(TVIdxs[false])));
1324-
Builder.CreateStore(NewTV, MCDCCondBitmapAddr);
1311+
// Emit intrinsic that updates a dedicated temporary value on the stack after
1312+
// a condition is evaluated. After the set of conditions has been updated,
1313+
// the resulting value is used to update the boolean expression's bitmap.
1314+
llvm::Value *Args[5] = {llvm::ConstantExpr::getBitCast(FuncNameVar, I8PtrTy),
1315+
Builder.getInt64(FunctionHash),
1316+
Builder.getInt32(Branch.ID),
1317+
MCDCCondBitmapAddr.emitRawPointer(CGF), Val};
1318+
Builder.CreateCall(
1319+
CGM.getIntrinsic(llvm::Intrinsic::instrprof_mcdc_condbitmap_update),
1320+
Args);
13251321
}
13261322

13271323
void CodeGenPGO::setValueProfilingFlag(llvm::Module &M) {

clang/lib/CodeGen/CoverageMappingGen.cpp

+5-72
Original file line numberDiff line numberDiff line change
@@ -195,10 +195,6 @@ class SourceMappingRegion {
195195
return std::holds_alternative<mcdc::BranchParameters>(MCDCParams);
196196
}
197197

198-
const auto &getMCDCBranchParams() const {
199-
return mcdc::getParams<const mcdc::BranchParameters>(MCDCParams);
200-
}
201-
202198
bool isMCDCDecision() const {
203199
return std::holds_alternative<mcdc::DecisionParameters>(MCDCParams);
204200
}
@@ -208,8 +204,6 @@ class SourceMappingRegion {
208204
}
209205

210206
const mcdc::Parameters &getMCDCParams() const { return MCDCParams; }
211-
212-
void resetMCDCParams() { MCDCParams = mcdc::Parameters(); }
213207
};
214208

215209
/// Spelling locations for the start and end of a source region.
@@ -754,7 +748,6 @@ struct MCDCCoverageBuilder {
754748

755749
llvm::SmallVector<mcdc::ConditionIDs> DecisionStack;
756750
MCDC::State &MCDCState;
757-
const Stmt *DecisionStmt = nullptr;
758751
mcdc::ConditionID NextID = 0;
759752
bool NotMapped = false;
760753

@@ -784,8 +777,7 @@ struct MCDCCoverageBuilder {
784777

785778
/// Set the given condition's ID.
786779
void setCondID(const Expr *Cond, mcdc::ConditionID ID) {
787-
MCDCState.BranchByStmt[CodeGenFunction::stripCond(Cond)] = {ID,
788-
DecisionStmt};
780+
MCDCState.BranchByStmt[CodeGenFunction::stripCond(Cond)].ID = ID;
789781
}
790782

791783
/// Return the ID of a given condition.
@@ -816,11 +808,6 @@ struct MCDCCoverageBuilder {
816808
if (NotMapped)
817809
return;
818810

819-
if (NextID == 0) {
820-
DecisionStmt = E;
821-
assert(MCDCState.DecisionByStmt.contains(E));
822-
}
823-
824811
const mcdc::ConditionIDs &ParentDecision = DecisionStack.back();
825812

826813
// If the operator itself has an assigned ID, this means it represents a
@@ -2135,70 +2122,20 @@ struct CounterCoverageMappingBuilder
21352122
subtractCounters(ParentCount, TrueCount));
21362123
}
21372124

2138-
void createOrCancelDecision(const BinaryOperator *E, unsigned Since) {
2125+
void createDecision(const BinaryOperator *E) {
21392126
unsigned NumConds = MCDCBuilder.getTotalConditionsAndReset(E);
21402127
if (NumConds == 0)
21412128
return;
21422129

2143-
// Extract [ID, Conds] to construct the graph.
2144-
llvm::SmallVector<mcdc::ConditionIDs> CondIDs(NumConds);
2145-
for (const auto &SR : ArrayRef(SourceRegions).slice(Since)) {
2146-
if (SR.isMCDCBranch()) {
2147-
auto [ID, Conds] = SR.getMCDCBranchParams();
2148-
CondIDs[ID] = Conds;
2149-
}
2150-
}
2151-
2152-
// Construct the graph and calculate `Indices`.
2153-
mcdc::TVIdxBuilder Builder(CondIDs);
2154-
unsigned NumTVs = Builder.NumTestVectors;
2155-
unsigned MaxTVs = CVM.getCodeGenModule().getCodeGenOpts().MCDCMaxTVs;
2156-
assert(MaxTVs < mcdc::TVIdxBuilder::HardMaxTVs);
2157-
2158-
if (NumTVs > MaxTVs) {
2159-
// NumTVs exceeds MaxTVs -- warn and cancel the Decision.
2160-
cancelDecision(E, Since, NumTVs, MaxTVs);
2161-
return;
2162-
}
2163-
2164-
// Update the state for CodeGenPGO
2165-
assert(MCDCState.DecisionByStmt.contains(E));
2166-
MCDCState.DecisionByStmt[E] = {
2167-
MCDCState.BitmapBits, // Top
2168-
std::move(Builder.Indices),
2169-
};
2170-
21712130
auto DecisionParams = mcdc::DecisionParameters{
2172-
MCDCState.BitmapBits += NumTVs, // Tail
2131+
MCDCState.DecisionByStmt[E].BitmapIdx,
21732132
NumConds,
21742133
};
21752134

21762135
// Create MCDC Decision Region.
21772136
createDecisionRegion(E, DecisionParams);
21782137
}
21792138

2180-
// Warn and cancel the Decision.
2181-
void cancelDecision(const BinaryOperator *E, unsigned Since, int NumTVs,
2182-
int MaxTVs) {
2183-
auto &Diag = CVM.getCodeGenModule().getDiags();
2184-
unsigned DiagID =
2185-
Diag.getCustomDiagID(DiagnosticsEngine::Warning,
2186-
"unsupported MC/DC boolean expression; "
2187-
"number of test vectors (%0) exceeds max (%1). "
2188-
"Expression will not be covered");
2189-
Diag.Report(E->getBeginLoc(), DiagID) << NumTVs << MaxTVs;
2190-
2191-
// Restore MCDCBranch to Branch.
2192-
for (auto &SR : MutableArrayRef(SourceRegions).slice(Since)) {
2193-
assert(!SR.isMCDCDecision() && "Decision shouldn't be seen here");
2194-
if (SR.isMCDCBranch())
2195-
SR.resetMCDCParams();
2196-
}
2197-
2198-
// Tell CodeGenPGO not to instrument.
2199-
MCDCState.DecisionByStmt.erase(E);
2200-
}
2201-
22022139
/// Check if E belongs to system headers.
22032140
bool isExprInSystemHeader(const BinaryOperator *E) const {
22042141
return (!SystemHeadersCoverage &&
@@ -2215,8 +2152,6 @@ struct CounterCoverageMappingBuilder
22152152

22162153
bool IsRootNode = MCDCBuilder.isIdle();
22172154

2218-
unsigned SourceRegionsSince = SourceRegions.size();
2219-
22202155
// Keep track of Binary Operator and assign MCDC condition IDs.
22212156
MCDCBuilder.pushAndAssignIDs(E);
22222157

@@ -2255,7 +2190,7 @@ struct CounterCoverageMappingBuilder
22552190

22562191
// Create MCDC Decision Region if at top-level (root).
22572192
if (IsRootNode)
2258-
createOrCancelDecision(E, SourceRegionsSince);
2193+
createDecision(E);
22592194
}
22602195

22612196
// Determine whether the right side of OR operation need to be visited.
@@ -2276,8 +2211,6 @@ struct CounterCoverageMappingBuilder
22762211

22772212
bool IsRootNode = MCDCBuilder.isIdle();
22782213

2279-
unsigned SourceRegionsSince = SourceRegions.size();
2280-
22812214
// Keep track of Binary Operator and assign MCDC condition IDs.
22822215
MCDCBuilder.pushAndAssignIDs(E);
22832216

@@ -2320,7 +2253,7 @@ struct CounterCoverageMappingBuilder
23202253

23212254
// Create MCDC Decision Region if at top-level (root).
23222255
if (IsRootNode)
2323-
createOrCancelDecision(E, SourceRegionsSince);
2256+
createDecision(E);
23242257
}
23252258

23262259
void VisitLambdaExpr(const LambdaExpr *LE) {

clang/lib/CodeGen/MCDCState.h

+1-3
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,16 @@ using namespace llvm::coverage::mcdc;
2727

2828
/// Per-Function MC/DC state
2929
struct State {
30-
unsigned BitmapBits = 0;
30+
unsigned BitmapBytes = 0;
3131

3232
struct Decision {
3333
unsigned BitmapIdx;
34-
llvm::SmallVector<std::array<int, 2>> Indices;
3534
};
3635

3736
llvm::DenseMap<const Stmt *, Decision> DecisionByStmt;
3837

3938
struct Branch {
4039
ConditionID ID;
41-
const Stmt *DecisionStmt;
4240
};
4341

4442
llvm::DenseMap<const Stmt *, Branch> BranchByStmt;

0 commit comments

Comments
 (0)