-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[MC/DC] Introduce -fmcdc-single-conditions to include also single conditions
#125484
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] Introduce -fmcdc-single-conditions to include also single conditions
#125484
Conversation
…onditions
`-fmcdc-single-conditions` is `CC1Option` for now.
This change discovers `isInstrumentedCondition(Cond)` on
`DoStmt/ForStmt/IfStmt/WhleStmt/AbstractConditionalOperator` and add
them into Decisions.
An example of the report:
```
MC/DC Decision Region (mmm:nn) to (mmm:nn)
Number of Conditions: 1
Condition C1 -->(mmm:nn)
Executed MC/DC Test Vectors:
C1 Result
1 { F = F }
2 { T = T }
C1-Pair: covered: (1,2)
MC/DC Coverage for Expression: 100.00%
```
The Decision is covered only if both `true` and `false` are covered.
Fixes #95336
|
@llvm/pr-subscribers-clang-codegen @llvm/pr-subscribers-clang Author: NAKAMURA Takumi (chapuni) Changes
This change discovers An example of the report: The Decision is covered only if both Fixes #95336 Patch is 24.76 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/125484.diff 9 Files Affected:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 42054fe27c5ee1..4138fc2f11e0c1 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -118,6 +118,9 @@ Improvements to Coverage Mapping
- [MC/DC] Nested expressions are handled as individual MC/DC expressions.
+- [MC/DC] Non-boolean expressions on conditions can be included with
+ `-fmcdc-single-conditions`. (#GH95336)
+
Bug Fixes in This Version
-------------------------
diff --git a/clang/docs/SourceBasedCodeCoverage.rst b/clang/docs/SourceBasedCodeCoverage.rst
index d26babe829ab5b..bcd4ae0e9748d1 100644
--- a/clang/docs/SourceBasedCodeCoverage.rst
+++ b/clang/docs/SourceBasedCodeCoverage.rst
@@ -510,6 +510,10 @@ requires 8 test vectors.
Expressions such as ``((a0 && b0) || (a1 && b1) || ...)`` can cause the
number of test vectors to increase exponentially.
+Clang handles only binary logical operators as MC/DC coverage. Single
+conditions without logcal operators on `do/for/while/if/?!` can be
+included with `-Xclang -fmcdc-single-conditions`.
+
Switch statements
-----------------
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index 259972bdf8f001..1a9ebae845619b 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -236,6 +236,7 @@ CODEGENOPT(DumpCoverageMapping , 1, 0) ///< Dump the generated coverage mapping
CODEGENOPT(MCDCCoverage , 1, 0) ///< Enable MC/DC code coverage criteria.
VALUE_CODEGENOPT(MCDCMaxConds, 16, 32767) ///< MC/DC Maximum conditions.
VALUE_CODEGENOPT(MCDCMaxTVs, 32, 0x7FFFFFFE) ///< MC/DC Maximum test vectors.
+VALUE_CODEGENOPT(MCDCSingleCond, 1, 0) ///< Enable MC/DC single conditions.
/// If -fpcc-struct-return or -freg-struct-return is specified.
ENUM_CODEGENOPT(StructReturnConvention, StructReturnConventionKind, 2, SRCK_Default)
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 6eabd9f76a792d..57b826bce6da82 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -1742,6 +1742,10 @@ def fmcdc_max_test_vectors_EQ : Joined<["-"], "fmcdc-max-test-vectors=">,
Group<f_Group>, Visibility<[CC1Option]>,
HelpText<"Maximum number of test vectors in MC/DC coverage">,
MarshallingInfoInt<CodeGenOpts<"MCDCMaxTVs">, "0x7FFFFFFE">;
+def fmcdc_single_conditions : Flag<["-"], "fmcdc-single-conditions">,
+ Group<f_Group>, Visibility<[CC1Option]>,
+ HelpText<"Include also single conditions as MC/DC coverage">,
+ MarshallingInfoFlag<CodeGenOpts<"MCDCSingleCond">>;
def fprofile_generate : Flag<["-"], "fprofile-generate">,
Group<f_Group>, Visibility<[ClangOption, CLOption]>,
HelpText<"Generate instrumented code to collect execution counts into default.profraw (overridden by LLVM_PROFILE_FILE env var)">;
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 9676e61cf322d9..82a31cb3721473 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -196,20 +196,36 @@ RawAddress CodeGenFunction::CreateMemTempWithoutCast(QualType Ty,
/// EvaluateExprAsBool - Perform the usual unary conversions on the specified
/// expression and compare the result against zero, returning an Int1Ty value.
llvm::Value *CodeGenFunction::EvaluateExprAsBool(const Expr *E) {
+ auto DecisionExpr = stripCond(E);
+ if (isMCDCDecisionExpr(DecisionExpr) && isInstrumentedCondition(DecisionExpr))
+ maybeResetMCDCCondBitmap(DecisionExpr);
+ else
+ DecisionExpr = nullptr;
+
PGO.setCurrentStmt(E);
+ llvm::Value *Result;
if (const MemberPointerType *MPT = E->getType()->getAs<MemberPointerType>()) {
llvm::Value *MemPtr = EmitScalarExpr(E);
- return CGM.getCXXABI().EmitMemberPointerIsNotNull(*this, MemPtr, MPT);
+ Result = CGM.getCXXABI().EmitMemberPointerIsNotNull(*this, MemPtr, MPT);
+ } else {
+ QualType BoolTy = getContext().BoolTy;
+ SourceLocation Loc = E->getExprLoc();
+ CGFPOptionsRAII FPOptsRAII(*this, E);
+ if (!E->getType()->isAnyComplexType())
+ Result =
+ EmitScalarConversion(EmitScalarExpr(E), E->getType(), BoolTy, Loc);
+ else
+ Result = EmitComplexToScalarConversion(EmitComplexExpr(E), E->getType(),
+ BoolTy, Loc);
}
- QualType BoolTy = getContext().BoolTy;
- SourceLocation Loc = E->getExprLoc();
- CGFPOptionsRAII FPOptsRAII(*this, E);
- if (!E->getType()->isAnyComplexType())
- return EmitScalarConversion(EmitScalarExpr(E), E->getType(), BoolTy, Loc);
+ if (DecisionExpr) {
+ if (isMCDCBranchExpr(stripCond(E)))
+ maybeUpdateMCDCCondBitmap(stripCond(E), Result);
+ maybeUpdateMCDCTestVectorBitmap(DecisionExpr);
+ }
- return EmitComplexToScalarConversion(EmitComplexExpr(E), E->getType(), BoolTy,
- Loc);
+ return Result;
}
/// EmitIgnoredExpr - Emit code to compute the specified expression,
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index e4780f88026009..de8d61c448d0c3 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -1684,7 +1684,7 @@ class CodeGenFunction : public CodeGenTypeCache {
/// Zero-init the MCDC temp value.
void maybeResetMCDCCondBitmap(const Expr *E) {
- if (isMCDCCoverageEnabled() && isBinaryLogicalOp(E)) {
+ if (isMCDCCoverageEnabled()) {
PGO.emitMCDCCondBitmapReset(Builder, E);
PGO.setCurrentStmt(E);
}
@@ -1693,7 +1693,7 @@ class CodeGenFunction : public CodeGenTypeCache {
/// Increment the profiler's counter for the given expression by \p StepV.
/// If \p StepV is null, the default increment is 1.
void maybeUpdateMCDCTestVectorBitmap(const Expr *E) {
- if (isMCDCCoverageEnabled() && isBinaryLogicalOp(E)) {
+ if (isMCDCCoverageEnabled()) {
PGO.emitMCDCTestVectorBitmapUpdate(Builder, E, *this);
PGO.setCurrentStmt(E);
}
diff --git a/clang/lib/CodeGen/CodeGenPGO.cpp b/clang/lib/CodeGen/CodeGenPGO.cpp
index 0c3973aa4dccfd..b1a6c8714fe783 100644
--- a/clang/lib/CodeGen/CodeGenPGO.cpp
+++ b/clang/lib/CodeGen/CodeGenPGO.cpp
@@ -168,6 +168,8 @@ struct MapRegionCounters : public RecursiveASTVisitor<MapRegionCounters> {
MCDC::State &MCDCState;
/// Maximum number of supported MC/DC conditions in a boolean expression.
unsigned MCDCMaxCond;
+ /// Take single conditions into account.
+ bool MCDCSingleCond;
/// The profile version.
uint64_t ProfileVersion;
/// Diagnostics Engine used to report warnings.
@@ -176,10 +178,11 @@ struct MapRegionCounters : public RecursiveASTVisitor<MapRegionCounters> {
MapRegionCounters(PGOHashVersion HashVersion, uint64_t ProfileVersion,
llvm::DenseMap<const Stmt *, CounterPair> &CounterMap,
MCDC::State &MCDCState, unsigned MCDCMaxCond,
- DiagnosticsEngine &Diag)
+ bool MCDCSingleCond, DiagnosticsEngine &Diag)
: NextCounter(0), Hash(HashVersion), CounterMap(CounterMap),
MCDCState(MCDCState), MCDCMaxCond(MCDCMaxCond),
- ProfileVersion(ProfileVersion), Diag(Diag) {}
+ MCDCSingleCond(MCDCSingleCond), ProfileVersion(ProfileVersion),
+ Diag(Diag) {}
// Blocks and lambdas are handled as separate functions, so we need not
// traverse them in the parent context.
@@ -240,12 +243,31 @@ struct MapRegionCounters : public RecursiveASTVisitor<MapRegionCounters> {
SmallVector<DecisionState, 1> DecisionStack;
+ llvm::DenseSet<const Expr *> StagingDecisions;
+
+ template <class T> bool pushInstrumentedCond(Stmt *S) {
+ if (auto *St = dyn_cast<T>(S)) {
+ if (auto *Cond = St->getCond();
+ Cond && CodeGenFunction::isInstrumentedCondition(Cond)) {
+ StagingDecisions.insert(CodeGenFunction::stripCond(Cond));
+ return true;
+ }
+ }
+
+ return false;
+ }
+
// Hook: dataTraverseStmtPre() is invoked prior to visiting an AST Stmt node.
bool dataTraverseStmtPre(Stmt *S) {
/// If MC/DC is not enabled, MCDCMaxCond will be set to 0. Do nothing.
if (MCDCMaxCond == 0)
return true;
+ const auto *E = dyn_cast<Expr>(S);
+
+ if (StagingDecisions.contains(E))
+ DecisionStack.emplace_back(E, true);
+
/// Mark "in splitting" when a leaf is met.
if (!DecisionStack.empty()) {
auto &StackTop = DecisionStack.back();
@@ -262,13 +284,20 @@ struct MapRegionCounters : public RecursiveASTVisitor<MapRegionCounters> {
assert(!StackTop.Leaves.contains(S));
}
- if (const auto *E = dyn_cast<Expr>(S)) {
+ if (E) {
if (const auto *BinOp =
dyn_cast<BinaryOperator>(CodeGenFunction::stripCond(E));
BinOp && BinOp->isLogicalOp())
DecisionStack.emplace_back(E);
}
+ if (MCDCSingleCond) {
+ pushInstrumentedCond<AbstractConditionalOperator>(S) ||
+ pushInstrumentedCond<DoStmt>(S) || pushInstrumentedCond<IfStmt>(S) ||
+ pushInstrumentedCond<ForStmt>(S) ||
+ pushInstrumentedCond<WhileStmt>(S);
+ }
+
return true;
}
@@ -1098,7 +1127,8 @@ void CodeGenPGO::mapRegionCounters(const Decl *D) {
RegionCounterMap.reset(new llvm::DenseMap<const Stmt *, CounterPair>);
RegionMCDCState.reset(new MCDC::State);
MapRegionCounters Walker(HashVersion, ProfileVersion, *RegionCounterMap,
- *RegionMCDCState, MCDCMaxConditions, CGM.getDiags());
+ *RegionMCDCState, MCDCMaxConditions,
+ CGM.getCodeGenOpts().MCDCSingleCond, CGM.getDiags());
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D))
Walker.TraverseDecl(const_cast<FunctionDecl *>(FD));
else if (const ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(D))
diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp
index 3a281fd39b4bcb..6f600a00296a69 100644
--- a/clang/lib/CodeGen/CoverageMappingGen.cpp
+++ b/clang/lib/CodeGen/CoverageMappingGen.cpp
@@ -1164,9 +1164,7 @@ struct CounterCoverageMappingBuilder
/// result in the generation of a branch.
void createBranchRegion(const Expr *C, Counter TrueCnt, Counter FalseCnt,
const mcdc::ConditionIDs &Conds = {}) {
- // Check for NULL conditions.
- if (!C)
- return;
+ assert(C && "Condition Expr shouldn't be null!");
// Ensure we are an instrumentable condition (i.e. no "&&" or "||"). Push
// region onto RegionStack but immediately pop it (which adds it to the
@@ -1630,6 +1628,9 @@ struct CounterCoverageMappingBuilder
}
void VisitWhileStmt(const WhileStmt *S) {
+ unsigned SourceRegionsSince = SourceRegions.size();
+ MCDCBuilder.checkDecisionRootOrPush(S->getCond());
+
extendRegion(S);
Counter ParentCount = getRegion().getCounter();
@@ -1676,10 +1677,15 @@ struct CounterCoverageMappingBuilder
// Create Branch Region around condition.
if (!llvm::EnableSingleByteCoverage)
- createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped);
+ createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped,
+ MCDCBuilder.getCurCondIDs());
+ createOrCancelDecision(S->getCond(), SourceRegionsSince);
}
void VisitDoStmt(const DoStmt *S) {
+ unsigned SourceRegionsSince = SourceRegions.size();
+ MCDCBuilder.checkDecisionRootOrPush(S->getCond());
+
extendRegion(S);
Counter ParentCount = getRegion().getCounter();
@@ -1722,13 +1728,20 @@ struct CounterCoverageMappingBuilder
// Create Branch Region around condition.
if (!llvm::EnableSingleByteCoverage)
- createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped);
+ createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped,
+ MCDCBuilder.getCurCondIDs());
+ createOrCancelDecision(S->getCond(), SourceRegionsSince);
if (BodyHasTerminateStmt)
HasTerminateStmt = true;
}
void VisitForStmt(const ForStmt *S) {
+ const Expr *Cond = S->getCond();
+ unsigned SourceRegionsSince = SourceRegions.size();
+ if (Cond)
+ MCDCBuilder.checkDecisionRootOrPush(Cond);
+
extendRegion(S);
if (S->getInit())
Visit(S->getInit());
@@ -1775,7 +1788,7 @@ struct CounterCoverageMappingBuilder
assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount ||
llvm::EnableSingleByteCoverage);
- if (const Expr *Cond = S->getCond()) {
+ if (Cond) {
propagateCounts(CondCount, Cond);
adjustForOutOfOrderTraversal(getEnd(S));
}
@@ -1797,8 +1810,11 @@ struct CounterCoverageMappingBuilder
}
// Create Branch Region around condition.
- if (!llvm::EnableSingleByteCoverage)
- createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped);
+ if (!llvm::EnableSingleByteCoverage && Cond) {
+ createBranchRegion(Cond, BodyCount, BranchCount.Skipped,
+ MCDCBuilder.getCurCondIDs());
+ createOrCancelDecision(Cond, SourceRegionsSince);
+ }
}
void VisitCXXForRangeStmt(const CXXForRangeStmt *S) {
@@ -2069,6 +2085,9 @@ struct CounterCoverageMappingBuilder
else if (S->isConstexpr())
return coverIfConstexpr(S);
+ unsigned SourceRegionsSince = SourceRegions.size();
+ MCDCBuilder.checkDecisionRootOrPush(S->getCond());
+
extendRegion(S);
if (S->getInit())
Visit(S->getInit());
@@ -2127,7 +2146,9 @@ struct CounterCoverageMappingBuilder
if (!llvm::EnableSingleByteCoverage)
// Create Branch Region around condition.
- createBranchRegion(S->getCond(), ThenCount, ElseCount);
+ createBranchRegion(S->getCond(), ThenCount, ElseCount,
+ MCDCBuilder.getCurCondIDs());
+ createOrCancelDecision(S->getCond(), SourceRegionsSince);
}
void VisitCXXTryStmt(const CXXTryStmt *S) {
@@ -2150,6 +2171,9 @@ struct CounterCoverageMappingBuilder
}
void VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
+ unsigned SourceRegionsSince = SourceRegions.size();
+ MCDCBuilder.checkDecisionRootOrPush(E->getCond());
+
extendRegion(E);
Counter ParentCount = getRegion().getCounter();
@@ -2189,7 +2213,9 @@ struct CounterCoverageMappingBuilder
// Create Branch Region around condition.
if (!llvm::EnableSingleByteCoverage)
- createBranchRegion(E->getCond(), TrueCount, FalseCount);
+ createBranchRegion(E->getCond(), TrueCount, FalseCount,
+ MCDCBuilder.getCurCondIDs());
+ createOrCancelDecision(E->getCond(), SourceRegionsSince);
}
inline unsigned findMCDCBranchesInSourceRegion(
diff --git a/clang/test/CoverageMapping/mcdc-single-cond.cpp b/clang/test/CoverageMapping/mcdc-single-cond.cpp
index d6c694a63dd285..67b011b7be66c8 100644
--- a/clang/test/CoverageMapping/mcdc-single-cond.cpp
+++ b/clang/test/CoverageMapping/mcdc-single-cond.cpp
@@ -1,3 +1,5 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 -fcoverage-mcdc -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -disable-llvm-passes -emit-llvm -o %t1.ll %s -fmcdc-single-conditions | FileCheck %s --check-prefixes=MM,MM1
+// RUN: FileCheck %s --check-prefixes=LL,LL1 < %t1.ll
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 -fcoverage-mcdc -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -disable-llvm-passes -emit-llvm -o %t2.ll %s | FileCheck %s --check-prefixes=MM,MM2
// RUN: FileCheck %s --check-prefixes=LL,LL2 < %t2.ll
@@ -5,33 +7,69 @@
// MM: func_cond{{.*}}:
int func_cond(bool a, bool b) {
// %mcdc.addr* are emitted by static order.
+ // LL1: %[[MA1:mcdc.addr.*]] = alloca i32, align 4
+ // LL1: %[[MA2:mcdc.addr.*]] = alloca i32, align 4
+ // LL1: %[[MA3:mcdc.addr.*]] = alloca i32, align 4
// LL: %[[MA4:mcdc.addr.*]] = alloca i32, align 4
+ // LL1: %[[MA5:mcdc.addr.*]] = alloca i32, align 4
// LL: %[[MA6:mcdc.addr.*]] = alloca i32, align 4
// LL: %[[MA7:mcdc.addr.*]] = alloca i32, align 4
+ // LL1: %[[MA8:mcdc.addr.*]] = alloca i32, align 4
// LL: %[[MA9:mcdc.addr.*]] = alloca i32, align 4
// LL: %[[MA10:mcdc.addr.*]] = alloca i32, align 4
+ // LL1: %[[MA11:mcdc.addr.*]] = alloca i32, align 4
+ // LL1: %[[MA12:mcdc.addr.*]] = alloca i32, align 4
+ // LL1: %[[MA13:mcdc.addr.*]] = alloca i32, align 4
+ // LL1: %[[MA14:mcdc.addr.*]] = alloca i32, align 4
+ // LL1: %[[MA15:mcdc.addr.*]] = alloca i32, align 4
// LL: call void @llvm.instrprof.mcdc.parameters(ptr @[[PROFN:.+]], i64 [[#H:]], i32 [[#BS:]])
int count = 0;
if (a)
// NB=2 Single cond
+ // MM1: Decision,File 0, [[#L:@LINE-2]]:7 -> [[#L:@LINE-2]]:8 = M:[[#I:2]], C:1
+ // MM1: Branch,File 0, [[#L]]:7 -> [[#L]]:8 = #1, (#0 - #1) [1,0,0]
// MM2-NOT: Decision
+ // LL1: store i32 0, ptr %[[MA1]], align 4
+ // LL1: = load i32, ptr %[[MA1]], align 4
+ // LL1: store i32 %{{.+}}, ptr %[[MA1]], align 4
+ // LL1: call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:0]], ptr %[[MA1]])
++count;
if (a ? true : false)
// NB=2,2 Wider decision comes first.
+ // MM1: Decision,File 0, [[@LINE-2]]:7 -> [[#L:@LINE-2]]:23 = M:[[#I:I+2+2]], C:1
+ // MM1: Decision,File 0, [[#L]]:7 -> [[#L]]:8 = M:[[#I-2]], C:1
+ // MM1: Branch,File 0, [[#L]]:7 -> [[#L]]:23 = #2, (#0 - #2) [1,0,0]
+ // MM1: Branch,File 0, [[#L]]:7 -> [[#L]]:8 = #3, (#0 - #3) [1,0,0]
+ // LL1: store i32 0, ptr %[[MA3]], align 4
+ // LL1: store i32 0, ptr %[[MA2]], align 4
// MA2 has C:2
+ // LL1: = load i32, ptr %[[MA2]], align 4
+ // LL1: store i32 %{{.+}}, ptr %[[MA2]], align 4
+ // LL1: call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:B+2]], ptr %[[MA2]])
// MA3 has C:1
+ // LL1: = load i32, ptr %[[MA3]], align 4
+ // LL1: store i32 %{{.+}}, ptr %[[MA3]], align 4
+ // LL1: call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:B+2]], ptr %[[MA3]])
++count;
if (a && b ? true : false)
// NB=2,3 Wider decision comes first.
- // MM2: Decision,File 0, [[@LINE-2]]:7 -> [[#L:@LINE-2]]:13 = M:[[#I:3]], C:2
+ // MM1: Decision,File 0, [[@LINE-2]]:7 -> [[#L:@LINE-2]]:28 = M:[[#I:I+3+2]], C:1
+ // MM1: Decision,File 0, [[@LINE-3]]:7 -> [[#L:@LINE-3]]:13 = M:[[#I-2]], C:2
+ // MM2: Decision,File 0, [[@LINE-4]]:7 -> [[#L:@LINE-4]]:13 = M:[[#I:3]], C:2
+ // MM1: Branch,File 0, [[#L]]:7 -> [[#L]]:28 = #4, (#0 - #4) [1,0,0]
// MM: Branch,File 0, [[#L]]:7 -> [[#L]]:8 = #6, (#0 - #6) [1,2,0]
// MM: Branch,File 0, [[#L]]:12 -> [[#L]]:13 = #7, (#6 - #7) [2,0,0]
+ // LL1: store i32 0, ptr %[[MA5]], align 4
// LL: store i32 0, ptr %[[MA4]], align 4
// LL: = load i32, ptr %[[MA4]], align 4
// LL: store i32 %{{.+}}, ptr %[[MA4]], align 4
// LL: = load i32, ptr %[[MA4]], align 4
// LL: store i32 %{{.+}}, ptr %[[MA4]], align 4
+ // LL1: call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:B+2]], ptr %[[MA4]])
// LL2: call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:0]], ptr %[[MA4]])
+ // LL1: = load i32, ptr %[[MA5]], align 4
+ // LL1: store i32 %{{.+}}, ptr %[[MA5]], align 4
+ // LL1: call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:B+3]], ptr %[[MA5]])
++count;
while (a || true) {
// NB=3 BinOp only
@@ -43,21 +81,29 @@ int func_cond(bool a, bool b) {
// LL: store i32 %{{.+}}, ptr %[[MA6]], align 4
// LL: = load i32, ptr %[[MA6]], align 4
// LL: store i32 %{{.+}}, ptr %[[MA6]], align 4
+ // LL1: call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:B+2]], ptr %[[MA6]])
// LL2: call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:B+3]], ptr %[[MA6]])
++count;
break;
}
while (a || true ? false : true) {
// Wider decision comes first.
- // MM2: Decision,File 0, [[@LINE-2]]:10 -> [[#L:@LINE-2]]:19 = M:[[#I:I+3]], C:2
+ // MM1: Decision,File 0, [[@LINE-2]]:10 -> [[#L:@LINE-2]]:34 = M:[[#I:I+3+2]], C:1
+ // MM1: Decision,File 0, [[@LINE-3]]:1...
[truncated]
|
|
This is pertinent to #109930 from Validas as well, and they would like something like this to be included by default to make it less confusing for users of MC/DC. On the other hand, as I point out in the issue, I think introducing MC/DC for single-conditions is redundant (given branch coverage) and introduces unnecessary overhead. So, that's an argument for keeping it as a separate option and perhaps finding another way to work branch coverage into the overall MC/DC metric. Also, Branch Coverage gives you counts for switch statement cases, whereas I'm not sure that makes sense for single-condition MC/DC, though perhaps that's overkill. |
|
@evodius96 Thanks for the comment. I've missed #109930 since I supposed it as rust-specific issue. I've noticed I misunderstood "a request from our teams (or ours customers)" so I implemented raising single cond to mcdc decision. Now I'm certain it wouldn't make sense. (I won't use this functionality in my projects since I don't prefer this) So, I'll withdraw this later. Instead, I'll propose for llvm-cov to induce kinda "condition coverage" from branches and decisions. I'll start if anyone hasn't started one. Besides, re. SwitchStmt, I think we can introduce additional region types for SwitchStmt to clarify |
-fmcdc-single-conditionsisCC1Optionfor now.This change discovers
isInstrumentedCondition(Cond)onDoStmt/ForStmt/IfStmt/WhleStmt/AbstractConditionalOperatorand add them into Decisions.An example of the report:
The Decision is covered only if both
trueandfalseare covered.Fixes #95336