[clang] 8b2bdfb - [Coverage][clang] Enable MC/DC Support in LLVM Source-based Code Coverage (3/3)
Alan Phipps via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 4 10:37:42 PST 2024
Author: Alan Phipps
Date: 2024-01-04T12:29:18-06:00
New Revision: 8b2bdfbca7c1db272e4e703445f5626b4bc4b9d3
URL: https://github.com/llvm/llvm-project/commit/8b2bdfbca7c1db272e4e703445f5626b4bc4b9d3
DIFF: https://github.com/llvm/llvm-project/commit/8b2bdfbca7c1db272e4e703445f5626b4bc4b9d3.diff
LOG: [Coverage][clang] Enable MC/DC Support in LLVM Source-based Code Coverage (3/3)
Part 3 of 3. This includes the MC/DC clang front-end components.
Differential Revision: https://reviews.llvm.org/D138849
Added:
clang/test/CoverageMapping/mcdc-class.cpp
clang/test/CoverageMapping/mcdc-error-conditions.cpp
clang/test/CoverageMapping/mcdc-error-nests.cpp
clang/test/CoverageMapping/mcdc-logical-scalar-ids.cpp
clang/test/CoverageMapping/mcdc-logical-stmt-ids-all.cpp
clang/test/CoverageMapping/mcdc-logical-stmt-ids.cpp
clang/test/Profile/c-mcdc-class.cpp
clang/test/Profile/c-mcdc-nested-ternary.c
clang/test/Profile/c-mcdc-not.c
clang/test/Profile/c-mcdc.c
compiler-rt/test/profile/ContinuousSyncMode/image-with-mcdc.c
Modified:
clang/include/clang/Basic/CodeGenOptions.def
clang/include/clang/Driver/Options.td
clang/lib/CodeGen/CGClass.cpp
clang/lib/CodeGen/CGExprScalar.cpp
clang/lib/CodeGen/CGStmt.cpp
clang/lib/CodeGen/CodeGenFunction.cpp
clang/lib/CodeGen/CodeGenFunction.h
clang/lib/CodeGen/CodeGenPGO.cpp
clang/lib/CodeGen/CodeGenPGO.h
clang/lib/CodeGen/CoverageMappingGen.cpp
clang/lib/CodeGen/CoverageMappingGen.h
clang/lib/Driver/ToolChains/Clang.cpp
clang/test/CoverageMapping/branch-constfolded.cpp
clang/test/CoverageMapping/branch-mincounters.cpp
clang/test/CoverageMapping/branch-templates.cpp
clang/test/CoverageMapping/if.cpp
clang/test/CoverageMapping/logical.cpp
clang/test/Profile/c-linkage-available_externally.c
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index 0acb5ae134ea24..2c4fb6745bc172 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -209,6 +209,7 @@ CODEGENOPT(CoverageMapping , 1, 0) ///< Generate coverage mapping regions to
///< enable code coverage analysis.
CODEGENOPT(DumpCoverageMapping , 1, 0) ///< Dump the generated coverage mapping
///< regions.
+CODEGENOPT(MCDCCoverage , 1, 0) ///< Enable MC/DC code coverage criteria.
/// 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 2b93ddf033499c..6aff37f1336871 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -1695,6 +1695,12 @@ defm coverage_mapping : BoolFOption<"coverage-mapping",
"Generate coverage mapping to enable code coverage analysis">,
NegFlag<SetFalse, [], [ClangOption], "Disable code coverage analysis">, BothFlags<
[], [ClangOption, CLOption]>>;
+defm mcdc_coverage : BoolFOption<"coverage-mcdc",
+ CodeGenOpts<"MCDCCoverage">, DefaultFalse,
+ PosFlag<SetTrue, [], [ClangOption, CC1Option],
+ "Enable MC/DC criteria when generating code coverage">,
+ NegFlag<SetFalse, [], [ClangOption], "Disable MC/DC coverage criteria">,
+ BothFlags<[], [ClangOption, CLOption]>>;
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/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index d18f186ce5b415..34319381901af6 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -856,6 +856,7 @@ void CodeGenFunction::EmitConstructorBody(FunctionArgList &Args) {
EnterCXXTryStmt(*cast<CXXTryStmt>(Body), true);
incrementProfileCounter(Body);
+ maybeCreateMCDCCondBitmap();
RunCleanupsScope RunCleanups(*this);
@@ -1444,8 +1445,10 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
}
Stmt *Body = Dtor->getBody();
- if (Body)
+ if (Body) {
incrementProfileCounter(Body);
+ maybeCreateMCDCCondBitmap();
+ }
// The call to operator delete in a deleting destructor happens
// outside of the function-try-block, which means it's always
@@ -1548,6 +1551,7 @@ void CodeGenFunction::emitImplicitAssignmentOperatorBody(FunctionArgList &Args)
LexicalScope Scope(*this, RootCS->getSourceRange());
incrementProfileCounter(RootCS);
+ maybeCreateMCDCCondBitmap();
AssignmentMemcpyizer AM(*this, AssignOp, Args);
for (auto *I : RootCS->body())
AM.emitAssignment(I);
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index d2c4c7ee50bc89..9ec185153d12b1 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -4564,6 +4564,12 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
if (LHSCondVal) { // If we have 1 && X, just emit X.
CGF.incrementProfileCounter(E);
+ // If the top of the logical operator nest, reset the MCDC temp to 0.
+ if (CGF.MCDCLogOpStack.empty())
+ CGF.maybeResetMCDCCondBitmap(E);
+
+ CGF.MCDCLogOpStack.push_back(E);
+
Value *RHSCond = CGF.EvaluateExprAsBool(E->getRHS());
// If we're generating for profiling or coverage, generate a branch to a
@@ -4572,6 +4578,7 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
// "FalseBlock" after the increment is done.
if (InstrumentRegions &&
CodeGenFunction::isInstrumentedCondition(E->getRHS())) {
+ CGF.maybeUpdateMCDCCondBitmap(E->getRHS(), RHSCond);
llvm::BasicBlock *FBlock = CGF.createBasicBlock("land.end");
llvm::BasicBlock *RHSBlockCnt = CGF.createBasicBlock("land.rhscnt");
Builder.CreateCondBr(RHSCond, RHSBlockCnt, FBlock);
@@ -4581,6 +4588,11 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
CGF.EmitBlock(FBlock);
}
+ CGF.MCDCLogOpStack.pop_back();
+ // If the top of the logical operator nest, update the MCDC bitmap.
+ if (CGF.MCDCLogOpStack.empty())
+ CGF.maybeUpdateMCDCTestVectorBitmap(E);
+
// ZExt result to int or bool.
return Builder.CreateZExtOrBitCast(RHSCond, ResTy, "land.ext");
}
@@ -4590,6 +4602,12 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
return llvm::Constant::getNullValue(ResTy);
}
+ // If the top of the logical operator nest, reset the MCDC temp to 0.
+ if (CGF.MCDCLogOpStack.empty())
+ CGF.maybeResetMCDCCondBitmap(E);
+
+ CGF.MCDCLogOpStack.push_back(E);
+
llvm::BasicBlock *ContBlock = CGF.createBasicBlock("land.end");
llvm::BasicBlock *RHSBlock = CGF.createBasicBlock("land.rhs");
@@ -4622,6 +4640,7 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
// condition coverage.
if (InstrumentRegions &&
CodeGenFunction::isInstrumentedCondition(E->getRHS())) {
+ CGF.maybeUpdateMCDCCondBitmap(E->getRHS(), RHSCond);
llvm::BasicBlock *RHSBlockCnt = CGF.createBasicBlock("land.rhscnt");
Builder.CreateCondBr(RHSCond, RHSBlockCnt, ContBlock);
CGF.EmitBlock(RHSBlockCnt);
@@ -4639,6 +4658,11 @@ Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) {
// Insert an entry into the phi node for the edge with the value of RHSCond.
PN->addIncoming(RHSCond, RHSBlock);
+ CGF.MCDCLogOpStack.pop_back();
+ // If the top of the logical operator nest, update the MCDC bitmap.
+ if (CGF.MCDCLogOpStack.empty())
+ CGF.maybeUpdateMCDCTestVectorBitmap(E);
+
// Artificial location to preserve the scope information
{
auto NL = ApplyDebugLocation::CreateArtificial(CGF);
@@ -4680,6 +4704,12 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) {
if (!LHSCondVal) { // If we have 0 || X, just emit X.
CGF.incrementProfileCounter(E);
+ // If the top of the logical operator nest, reset the MCDC temp to 0.
+ if (CGF.MCDCLogOpStack.empty())
+ CGF.maybeResetMCDCCondBitmap(E);
+
+ CGF.MCDCLogOpStack.push_back(E);
+
Value *RHSCond = CGF.EvaluateExprAsBool(E->getRHS());
// If we're generating for profiling or coverage, generate a branch to a
@@ -4688,6 +4718,7 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) {
// "FalseBlock" after the increment is done.
if (InstrumentRegions &&
CodeGenFunction::isInstrumentedCondition(E->getRHS())) {
+ CGF.maybeUpdateMCDCCondBitmap(E->getRHS(), RHSCond);
llvm::BasicBlock *FBlock = CGF.createBasicBlock("lor.end");
llvm::BasicBlock *RHSBlockCnt = CGF.createBasicBlock("lor.rhscnt");
Builder.CreateCondBr(RHSCond, FBlock, RHSBlockCnt);
@@ -4697,6 +4728,11 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) {
CGF.EmitBlock(FBlock);
}
+ CGF.MCDCLogOpStack.pop_back();
+ // If the top of the logical operator nest, update the MCDC bitmap.
+ if (CGF.MCDCLogOpStack.empty())
+ CGF.maybeUpdateMCDCTestVectorBitmap(E);
+
// ZExt result to int or bool.
return Builder.CreateZExtOrBitCast(RHSCond, ResTy, "lor.ext");
}
@@ -4706,6 +4742,12 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) {
return llvm::ConstantInt::get(ResTy, 1);
}
+ // If the top of the logical operator nest, reset the MCDC temp to 0.
+ if (CGF.MCDCLogOpStack.empty())
+ CGF.maybeResetMCDCCondBitmap(E);
+
+ CGF.MCDCLogOpStack.push_back(E);
+
llvm::BasicBlock *ContBlock = CGF.createBasicBlock("lor.end");
llvm::BasicBlock *RHSBlock = CGF.createBasicBlock("lor.rhs");
@@ -4742,6 +4784,7 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) {
// condition coverage.
if (InstrumentRegions &&
CodeGenFunction::isInstrumentedCondition(E->getRHS())) {
+ CGF.maybeUpdateMCDCCondBitmap(E->getRHS(), RHSCond);
llvm::BasicBlock *RHSBlockCnt = CGF.createBasicBlock("lor.rhscnt");
Builder.CreateCondBr(RHSCond, ContBlock, RHSBlockCnt);
CGF.EmitBlock(RHSBlockCnt);
@@ -4755,6 +4798,11 @@ Value *ScalarExprEmitter::VisitBinLOr(const BinaryOperator *E) {
CGF.EmitBlock(ContBlock);
PN->addIncoming(RHSCond, RHSBlock);
+ CGF.MCDCLogOpStack.pop_back();
+ // If the top of the logical operator nest, update the MCDC bitmap.
+ if (CGF.MCDCLogOpStack.empty())
+ CGF.maybeUpdateMCDCTestVectorBitmap(E);
+
// ZExt result to int.
return Builder.CreateZExtOrBitCast(PN, ResTy, "lor.ext");
}
@@ -4899,6 +4947,10 @@ VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
return Builder.CreateSelect(CondV, LHS, RHS, "cond");
}
+ // If the top of the logical operator nest, reset the MCDC temp to 0.
+ if (CGF.MCDCLogOpStack.empty())
+ CGF.maybeResetMCDCCondBitmap(condExpr);
+
llvm::BasicBlock *LHSBlock = CGF.createBasicBlock("cond.true");
llvm::BasicBlock *RHSBlock = CGF.createBasicBlock("cond.false");
llvm::BasicBlock *ContBlock = CGF.createBasicBlock("cond.end");
@@ -4934,6 +4986,11 @@ VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
llvm::PHINode *PN = Builder.CreatePHI(LHS->getType(), 2, "cond");
PN->addIncoming(LHS, LHSBlock);
PN->addIncoming(RHS, RHSBlock);
+
+ // If the top of the logical operator nest, update the MCDC bitmap.
+ if (CGF.MCDCLogOpStack.empty())
+ CGF.maybeUpdateMCDCTestVectorBitmap(condExpr);
+
return PN;
}
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 0f79a2e861d220..b89017de0bcf14 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -837,7 +837,19 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
if (!ThenCount && !getCurrentProfileCount() &&
CGM.getCodeGenOpts().OptimizationLevel)
LH = Stmt::getLikelihood(S.getThen(), S.getElse());
- EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, ThenCount, LH);
+
+ // When measuring MC/DC, always fully evaluate the condition up front using
+ // EvaluateExprAsBool() so that the test vector bitmap can be updated prior to
+ // executing the body of the if.then or if.else. This is useful for when
+ // there is a 'return' within the body, but this is particularly beneficial
+ // when one if-stmt is nested within another if-stmt so that all of the MC/DC
+ // updates are kept linear and consistent.
+ if (!CGM.getCodeGenOpts().MCDCCoverage)
+ EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, ThenCount, LH);
+ else {
+ llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
+ Builder.CreateCondBr(BoolCondVal, ThenBlock, ElseBlock);
+ }
// Emit the 'then' code.
EmitBlock(ThenBlock);
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 2199d7b58fb96e..2673e4a5cee7bb 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1256,6 +1256,7 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
void CodeGenFunction::EmitFunctionBody(const Stmt *Body) {
incrementProfileCounter(Body);
+ maybeCreateMCDCCondBitmap();
if (const CompoundStmt *S = dyn_cast<CompoundStmt>(Body))
EmitCompoundStmtWithoutScope(*S);
else
@@ -1601,6 +1602,13 @@ bool CodeGenFunction::mightAddDeclToScope(const Stmt *S) {
bool CodeGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond,
bool &ResultBool,
bool AllowLabels) {
+ // If MC/DC is enabled, disable folding so that we can instrument all
+ // conditions to yield complete test vectors. We still keep track of
+ // folded conditions during region mapping and visualization.
+ if (!AllowLabels && CGM.getCodeGenOpts().hasProfileClangInstr() &&
+ CGM.getCodeGenOpts().MCDCCoverage)
+ return false;
+
llvm::APSInt ResultInt;
if (!ConstantFoldsToSimpleInteger(Cond, ResultInt, AllowLabels))
return false;
@@ -1629,16 +1637,20 @@ bool CodeGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond,
return true;
}
+/// Strip parentheses and simplistic logical-NOT operators.
+const Expr *CodeGenFunction::stripCond(const Expr *C) {
+ while (const UnaryOperator *Op = dyn_cast<UnaryOperator>(C->IgnoreParens())) {
+ if (Op->getOpcode() != UO_LNot)
+ break;
+ C = Op->getSubExpr();
+ }
+ return C->IgnoreParens();
+}
+
/// Determine whether the given condition is an instrumentable condition
/// (i.e. no "&&" or "||").
bool CodeGenFunction::isInstrumentedCondition(const Expr *C) {
- // Bypass simplistic logical-NOT operator before determining whether the
- // condition contains any other logical operator.
- if (const UnaryOperator *UnOp = dyn_cast<UnaryOperator>(C->IgnoreParens()))
- if (UnOp->getOpcode() == UO_LNot)
- C = UnOp->getSubExpr();
-
- const BinaryOperator *BOp = dyn_cast<BinaryOperator>(C->IgnoreParens());
+ const BinaryOperator *BOp = dyn_cast<BinaryOperator>(stripCond(C));
return (!BOp || !BOp->isLogicalOp());
}
@@ -1717,17 +1729,19 @@ void CodeGenFunction::EmitBranchToCounterBlock(
/// statement) to the specified blocks. Based on the condition, this might try
/// to simplify the codegen of the conditional based on the branch.
/// \param LH The value of the likelihood attribute on the True branch.
-void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
- llvm::BasicBlock *TrueBlock,
- llvm::BasicBlock *FalseBlock,
- uint64_t TrueCount,
- Stmt::Likelihood LH) {
+/// \param ConditionalOp Used by MC/DC code coverage to track the result of the
+/// ConditionalOperator (ternary) through a recursive call for the operator's
+/// LHS and RHS nodes.
+void CodeGenFunction::EmitBranchOnBoolExpr(
+ const Expr *Cond, llvm::BasicBlock *TrueBlock, llvm::BasicBlock *FalseBlock,
+ uint64_t TrueCount, Stmt::Likelihood LH, const Expr *ConditionalOp) {
Cond = Cond->IgnoreParens();
if (const BinaryOperator *CondBOp = dyn_cast<BinaryOperator>(Cond)) {
-
// Handle X && Y in a condition.
if (CondBOp->getOpcode() == BO_LAnd) {
+ MCDCLogOpStack.push_back(CondBOp);
+
// If we have "1 && X", simplify the code. "0 && X" would have constant
// folded if the case was simple enough.
bool ConstantBool = false;
@@ -1735,8 +1749,10 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
ConstantBool) {
// br(1 && X) -> br(X).
incrementProfileCounter(CondBOp);
- return EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LAnd, TrueBlock,
- FalseBlock, TrueCount, LH);
+ EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LAnd, TrueBlock,
+ FalseBlock, TrueCount, LH);
+ MCDCLogOpStack.pop_back();
+ return;
}
// If we have "X && 1", simplify the code to use an uncond branch.
@@ -1744,8 +1760,10 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
if (ConstantFoldsToSimpleInteger(CondBOp->getRHS(), ConstantBool) &&
ConstantBool) {
// br(X && 1) -> br(X).
- return EmitBranchToCounterBlock(CondBOp->getLHS(), BO_LAnd, TrueBlock,
- FalseBlock, TrueCount, LH, CondBOp);
+ EmitBranchToCounterBlock(CondBOp->getLHS(), BO_LAnd, TrueBlock,
+ FalseBlock, TrueCount, LH, CondBOp);
+ MCDCLogOpStack.pop_back();
+ return;
}
// Emit the LHS as a conditional. If the LHS conditional is false, we
@@ -1774,11 +1792,13 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LAnd, TrueBlock,
FalseBlock, TrueCount, LH);
eval.end(*this);
-
+ MCDCLogOpStack.pop_back();
return;
}
if (CondBOp->getOpcode() == BO_LOr) {
+ MCDCLogOpStack.push_back(CondBOp);
+
// If we have "0 || X", simplify the code. "1 || X" would have constant
// folded if the case was simple enough.
bool ConstantBool = false;
@@ -1786,8 +1806,10 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
!ConstantBool) {
// br(0 || X) -> br(X).
incrementProfileCounter(CondBOp);
- return EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LOr, TrueBlock,
- FalseBlock, TrueCount, LH);
+ EmitBranchToCounterBlock(CondBOp->getRHS(), BO_LOr, TrueBlock,
+ FalseBlock, TrueCount, LH);
+ MCDCLogOpStack.pop_back();
+ return;
}
// If we have "X || 0", simplify the code to use an uncond branch.
@@ -1795,10 +1817,11 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
if (ConstantFoldsToSimpleInteger(CondBOp->getRHS(), ConstantBool) &&
!ConstantBool) {
// br(X || 0) -> br(X).
- return EmitBranchToCounterBlock(CondBOp->getLHS(), BO_LOr, TrueBlock,
- FalseBlock, TrueCount, LH, CondBOp);
+ EmitBranchToCounterBlock(CondBOp->getLHS(), BO_LOr, TrueBlock,
+ FalseBlock, TrueCount, LH, CondBOp);
+ MCDCLogOpStack.pop_back();
+ return;
}
-
// Emit the LHS as a conditional. If the LHS conditional is true, we
// want to jump to the TrueBlock.
llvm::BasicBlock *LHSFalse = createBasicBlock("lor.lhs.false");
@@ -1829,14 +1852,20 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
RHSCount, LH);
eval.end(*this);
-
+ MCDCLogOpStack.pop_back();
return;
}
}
if (const UnaryOperator *CondUOp = dyn_cast<UnaryOperator>(Cond)) {
// br(!x, t, f) -> br(x, f, t)
- if (CondUOp->getOpcode() == UO_LNot) {
+ // Avoid doing this optimization when instrumenting a condition for MC/DC.
+ // LNot is taken as part of the condition for simplicity, and changing its
+ // sense negatively impacts test vector tracking.
+ bool MCDCCondition = CGM.getCodeGenOpts().hasProfileClangInstr() &&
+ CGM.getCodeGenOpts().MCDCCoverage &&
+ isInstrumentedCondition(Cond);
+ if (CondUOp->getOpcode() == UO_LNot && !MCDCCondition) {
// Negate the count.
uint64_t FalseCount = getCurrentProfileCount() - TrueCount;
// The values of the enum are chosen to make this negation possible.
@@ -1876,14 +1905,14 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
{
ApplyDebugLocation DL(*this, Cond);
EmitBranchOnBoolExpr(CondOp->getLHS(), TrueBlock, FalseBlock,
- LHSScaledTrueCount, LH);
+ LHSScaledTrueCount, LH, CondOp);
}
cond.end(*this);
cond.begin(*this);
EmitBlock(RHSBlock);
EmitBranchOnBoolExpr(CondOp->getRHS(), TrueBlock, FalseBlock,
- TrueCount - LHSScaledTrueCount, LH);
+ TrueCount - LHSScaledTrueCount, LH, CondOp);
cond.end(*this);
return;
@@ -1906,6 +1935,21 @@ void CodeGenFunction::EmitBranchOnBoolExpr(const Expr *Cond,
CondV = EvaluateExprAsBool(Cond);
}
+ // If not at the top of the logical operator nest, update MCDC temp with the
+ // boolean result of the evaluated condition.
+ if (!MCDCLogOpStack.empty()) {
+ const Expr *MCDCBaseExpr = Cond;
+ // When a nested ConditionalOperator (ternary) is encountered in a boolean
+ // expression, MC/DC tracks the result of the ternary, and this is tied to
+ // the ConditionalOperator expression and not the ternary's LHS or RHS. If
+ // this is the case, the ConditionalOperator expression is passed through
+ // the ConditionalOp parameter and then used as the MCDC base expression.
+ if (ConditionalOp)
+ MCDCBaseExpr = ConditionalOp;
+
+ maybeUpdateMCDCCondBitmap(MCDCBaseExpr, CondV);
+ }
+
llvm::MDNode *Weights = nullptr;
llvm::MDNode *Unpredictable = nullptr;
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 751d8110b13dfd..07c7678df87eb8 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -287,6 +287,9 @@ class CodeGenFunction : public CodeGenTypeCache {
/// nest would extend.
SmallVector<llvm::CanonicalLoopInfo *, 4> OMPLoopNestStack;
+ /// Stack to track the Logical Operator recursion nest for MC/DC.
+ SmallVector<const BinaryOperator *, 16> MCDCLogOpStack;
+
/// Number of nested loop to be consumed by the last surrounding
/// loop-associated directive.
int ExpectedOMPLoopDepth = 0;
@@ -1521,6 +1524,9 @@ class CodeGenFunction : public CodeGenTypeCache {
CodeGenPGO PGO;
+ /// Bitmap used by MC/DC to track condition outcomes of a boolean expression.
+ Address MCDCCondBitmapAddr = Address::invalid();
+
/// Calculate branch weights appropriate for PGO data
llvm::MDNode *createProfileWeights(uint64_t TrueCount,
uint64_t FalseCount) const;
@@ -1539,6 +1545,52 @@ class CodeGenFunction : public CodeGenTypeCache {
PGO.setCurrentStmt(S);
}
+ bool isMCDCCoverageEnabled() const {
+ return (CGM.getCodeGenOpts().hasProfileClangInstr() &&
+ CGM.getCodeGenOpts().MCDCCoverage &&
+ !CurFn->hasFnAttribute(llvm::Attribute::NoProfile));
+ }
+
+ /// Allocate a temp value on the stack that MCDC can use to track condition
+ /// results.
+ void maybeCreateMCDCCondBitmap() {
+ if (isMCDCCoverageEnabled()) {
+ PGO.emitMCDCParameters(Builder);
+ MCDCCondBitmapAddr =
+ CreateIRTemp(getContext().UnsignedIntTy, "mcdc.addr");
+ }
+ }
+
+ bool isBinaryLogicalOp(const Expr *E) const {
+ const BinaryOperator *BOp = dyn_cast<BinaryOperator>(E->IgnoreParens());
+ return (BOp && BOp->isLogicalOp());
+ }
+
+ /// Zero-init the MCDC temp value.
+ void maybeResetMCDCCondBitmap(const Expr *E) {
+ if (isMCDCCoverageEnabled() && isBinaryLogicalOp(E)) {
+ PGO.emitMCDCCondBitmapReset(Builder, E, MCDCCondBitmapAddr);
+ PGO.setCurrentStmt(E);
+ }
+ }
+
+ /// 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)) {
+ PGO.emitMCDCTestVectorBitmapUpdate(Builder, E, MCDCCondBitmapAddr);
+ PGO.setCurrentStmt(E);
+ }
+ }
+
+ /// Update the MCDC temp value with the condition's evaluated result.
+ void maybeUpdateMCDCCondBitmap(const Expr *E, llvm::Value *Val) {
+ if (isMCDCCoverageEnabled()) {
+ PGO.emitMCDCCondBitmapUpdate(Builder, E, MCDCCondBitmapAddr, Val);
+ PGO.setCurrentStmt(E);
+ }
+ }
+
/// Get the profiler's count for the given statement.
uint64_t getProfileCount(const Stmt *S) {
return PGO.getStmtCount(S).value_or(0);
@@ -4626,6 +4678,9 @@ class CodeGenFunction : public CodeGenTypeCache {
bool ConstantFoldsToSimpleInteger(const Expr *Cond, llvm::APSInt &Result,
bool AllowLabels = false);
+ /// Ignore parentheses and logical-NOT to track conditions consistently.
+ static const Expr *stripCond(const Expr *C);
+
/// isInstrumentedCondition - Determine whether the given condition is an
/// instrumentable condition (i.e. no "&&" or "||").
static bool isInstrumentedCondition(const Expr *C);
@@ -4648,7 +4703,8 @@ class CodeGenFunction : public CodeGenTypeCache {
/// evaluate to true based on PGO data.
void EmitBranchOnBoolExpr(const Expr *Cond, llvm::BasicBlock *TrueBlock,
llvm::BasicBlock *FalseBlock, uint64_t TrueCount,
- Stmt::Likelihood LH = Stmt::LH_None);
+ Stmt::Likelihood LH = Stmt::LH_None,
+ const Expr *ConditionalOp = nullptr);
/// Given an assignment `*LHS = RHS`, emit a test that checks if \p RHS is
/// nonnull, if \p LHS is marked _Nonnull.
diff --git a/clang/lib/CodeGen/CodeGenPGO.cpp b/clang/lib/CodeGen/CodeGenPGO.cpp
index 81bf8ea696b164..d68844d476eb4e 100644
--- a/clang/lib/CodeGen/CodeGenPGO.cpp
+++ b/clang/lib/CodeGen/CodeGenPGO.cpp
@@ -161,13 +161,24 @@ 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 map of statements to MC/DC bitmap coverage objects.
+ llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap;
+ /// Maximum number of supported MC/DC conditions in a boolean expression.
+ unsigned MCDCMaxCond;
/// The profile version.
uint64_t ProfileVersion;
+ /// Diagnostics Engine used to report warnings.
+ DiagnosticsEngine &Diag;
MapRegionCounters(PGOHashVersion HashVersion, uint64_t ProfileVersion,
- llvm::DenseMap<const Stmt *, unsigned> &CounterMap)
+ llvm::DenseMap<const Stmt *, unsigned> &CounterMap,
+ llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap,
+ unsigned MCDCMaxCond, DiagnosticsEngine &Diag)
: NextCounter(0), Hash(HashVersion), CounterMap(CounterMap),
- ProfileVersion(ProfileVersion) {}
+ NextMCDCBitmapIdx(0), MCDCBitmapMap(MCDCBitmapMap),
+ MCDCMaxCond(MCDCMaxCond), ProfileVersion(ProfileVersion), Diag(Diag) {}
// Blocks and lambdas are handled as separate functions, so we need not
// traverse them in the parent context.
@@ -207,15 +218,126 @@ struct MapRegionCounters : public RecursiveASTVisitor<MapRegionCounters> {
return Type;
}
+ /// The following stacks are used with dataTraverseStmtPre() and
+ /// dataTraverseStmtPost() to track the depth of nested logical operators in a
+ /// boolean expression in a function. The ultimate purpose is to keep track
+ /// of the number of leaf-level conditions in the boolean expression so that a
+ /// profile bitmap can be allocated based on that number.
+ ///
+ /// The stacks are also used to find error cases and notify the user. A
+ /// standard logical operator nest for a boolean expression could be in a form
+ /// similar to this: "x = a && b && c && (d || f)"
+ unsigned NumCond = 0;
+ bool SplitNestedLogicalOp = false;
+ SmallVector<const Stmt *, 16> NonLogOpStack;
+ SmallVector<const BinaryOperator *, 16> LogOpStack;
+
+ // 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;
+
+ /// At the top of the logical operator nest, reset the number of conditions.
+ if (LogOpStack.empty())
+ NumCond = 0;
+
+ if (const Expr *E = dyn_cast<Expr>(S)) {
+ const BinaryOperator *BinOp = dyn_cast<BinaryOperator>(E->IgnoreParens());
+ if (BinOp && BinOp->isLogicalOp()) {
+ /// Check for "split-nested" logical operators. This happens when a new
+ /// boolean expression logical-op nest is encountered within an existing
+ /// boolean expression, separated by a non-logical operator. For
+ /// example, in "x = (a && b && c && foo(d && f))", the "d && f" case
+ /// starts a new boolean expression that is separated from the other
+ /// conditions by the operator foo(). Split-nested cases are not
+ /// supported by MC/DC.
+ SplitNestedLogicalOp = SplitNestedLogicalOp || !NonLogOpStack.empty();
+
+ LogOpStack.push_back(BinOp);
+ return true;
+ }
+ }
+
+ /// Keep track of non-logical operators. These are OK as long as we don't
+ /// encounter a new logical operator after seeing one.
+ if (!LogOpStack.empty())
+ NonLogOpStack.push_back(S);
+
+ return true;
+ }
+
+ // Hook: dataTraverseStmtPost() is invoked by the AST visitor after visiting
+ // an AST Stmt node. MC/DC will use it to to signal when the top of a
+ // logical operation (boolean expression) nest is encountered.
+ bool dataTraverseStmtPost(Stmt *S) {
+ /// If MC/DC is not enabled, MCDCMaxCond will be set to 0. Do nothing.
+ if (MCDCMaxCond == 0)
+ return true;
+
+ if (const Expr *E = dyn_cast<Expr>(S)) {
+ const BinaryOperator *BinOp = dyn_cast<BinaryOperator>(E->IgnoreParens());
+ if (BinOp && BinOp->isLogicalOp()) {
+ assert(LogOpStack.back() == BinOp);
+ LogOpStack.pop_back();
+
+ /// At the top of logical operator nest:
+ if (LogOpStack.empty()) {
+ /// Was the "split-nested" logical operator case encountered?
+ if (SplitNestedLogicalOp) {
+ unsigned DiagID = Diag.getCustomDiagID(
+ DiagnosticsEngine::Warning,
+ "unsupported MC/DC boolean expression; "
+ "contains an operation with a nested boolean expression. "
+ "Expression will not be covered");
+ Diag.Report(S->getBeginLoc(), DiagID);
+ return false;
+ }
+
+ /// Was the maximum number of conditions encountered?
+ if (NumCond > MCDCMaxCond) {
+ unsigned DiagID = Diag.getCustomDiagID(
+ DiagnosticsEngine::Warning,
+ "unsupported MC/DC boolean expression; "
+ "number of conditions (%0) exceeds max (%1). "
+ "Expression will not be covered");
+ Diag.Report(S->getBeginLoc(), DiagID) << NumCond << MCDCMaxCond;
+ 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.
+ MCDCBitmapMap[BinOp] = NextMCDCBitmapIdx;
+ unsigned SizeInBits = std::max<unsigned>(1L << NumCond, CHAR_BIT);
+ NextMCDCBitmapIdx += SizeInBits / CHAR_BIT;
+ }
+ return true;
+ }
+ }
+
+ if (!LogOpStack.empty())
+ NonLogOpStack.pop_back();
+
+ return true;
+ }
+
/// The RHS of all logical operators gets a fresh counter in order to count
/// how many times the RHS evaluates to true or false, depending on the
/// semantics of the operator. This is only valid for ">= v7" of the profile
- /// version so that we facilitate backward compatibility.
+ /// version so that we facilitate backward compatibility. In addition, in
+ /// order to use MC/DC, count the number of total LHS and RHS conditions.
bool VisitBinaryOperator(BinaryOperator *S) {
- if (ProfileVersion >= llvm::IndexedInstrProf::Version7)
- if (S->isLogicalOp() &&
- CodeGenFunction::isInstrumentedCondition(S->getRHS()))
- CounterMap[S->getRHS()] = NextCounter++;
+ if (S->isLogicalOp()) {
+ if (CodeGenFunction::isInstrumentedCondition(S->getLHS()))
+ NumCond++;
+
+ if (CodeGenFunction::isInstrumentedCondition(S->getRHS())) {
+ if (ProfileVersion >= llvm::IndexedInstrProf::Version7)
+ CounterMap[S->getRHS()] = NextCounter++;
+
+ NumCond++;
+ }
+ }
return Base::VisitBinaryOperator(S);
}
@@ -851,8 +973,22 @@ void CodeGenPGO::mapRegionCounters(const Decl *D) {
ProfileVersion = PGOReader->getVersion();
}
+ // If MC/DC is enabled, set the MaxConditions to a preset value. Otherwise,
+ // set it to zero. This value impacts the number of conditions accepted in a
+ // given boolean expression, which impacts the size of the bitmap used to
+ // track test vector execution for that boolean expression. Because the
+ // bitmap scales exponentially (2^n) based on the number of conditions seen,
+ // the maximum value is hard-coded at 6 conditions, which is more than enough
+ // 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;
+
RegionCounterMap.reset(new llvm::DenseMap<const Stmt *, unsigned>);
- MapRegionCounters Walker(HashVersion, ProfileVersion, *RegionCounterMap);
+ RegionMCDCBitmapMap.reset(new llvm::DenseMap<const Stmt *, unsigned>);
+ MapRegionCounters Walker(HashVersion, ProfileVersion, *RegionCounterMap,
+ *RegionMCDCBitmapMap, MCDCMaxConditions,
+ 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))
@@ -863,6 +999,7 @@ 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;
+ MCDCBitmapBytes = Walker.NextMCDCBitmapIdx;
FunctionHash = Walker.Hash.finalize();
}
@@ -894,9 +1031,11 @@ void CodeGenPGO::emitCounterRegionMapping(const Decl *D) {
std::string CoverageMapping;
llvm::raw_string_ostream OS(CoverageMapping);
- CoverageMappingGen MappingGen(*CGM.getCoverageMapping(),
- CGM.getContext().getSourceManager(),
- CGM.getLangOpts(), RegionCounterMap.get());
+ RegionCondIDMap.reset(new llvm::DenseMap<const Stmt *, unsigned>);
+ CoverageMappingGen MappingGen(
+ *CGM.getCoverageMapping(), CGM.getContext().getSourceManager(),
+ CGM.getLangOpts(), RegionCounterMap.get(), RegionMCDCBitmapMap.get(),
+ RegionCondIDMap.get());
MappingGen.emitCounterMapping(D, OS);
OS.flush();
@@ -972,6 +1111,108 @@ void CodeGenPGO::emitCounterIncrement(CGBuilderTy &Builder, const Stmt *S,
ArrayRef(Args));
}
+bool CodeGenPGO::canEmitMCDCCoverage(const CGBuilderTy &Builder) {
+ return (CGM.getCodeGenOpts().hasProfileClangInstr() &&
+ CGM.getCodeGenOpts().MCDCCoverage && Builder.GetInsertBlock());
+}
+
+void CodeGenPGO::emitMCDCParameters(CGBuilderTy &Builder) {
+ if (!canEmitMCDCCoverage(Builder) || !RegionMCDCBitmapMap)
+ return;
+
+ auto *I8PtrTy = llvm::PointerType::getUnqual(CGM.getLLVMContext());
+
+ // 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(MCDCBitmapBytes)};
+ Builder.CreateCall(
+ CGM.getIntrinsic(llvm::Intrinsic::instrprof_mcdc_parameters), Args);
+}
+
+void CodeGenPGO::emitMCDCTestVectorBitmapUpdate(CGBuilderTy &Builder,
+ const Expr *S,
+ Address MCDCCondBitmapAddr) {
+ if (!canEmitMCDCCoverage(Builder) || !RegionMCDCBitmapMap)
+ return;
+
+ S = S->IgnoreParens();
+
+ auto ExprMCDCBitmapMapIterator = RegionMCDCBitmapMap->find(S);
+ if (ExprMCDCBitmapMapIterator == RegionMCDCBitmapMap->end())
+ 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
+ // a boolean expression. The index being set is based on the value loaded
+ // from a pointer to a dedicated temporary value on the stack that is itself
+ // updated via emitMCDCCondBitmapReset() and emitMCDCCondBitmapUpdate(). The
+ // index represents an executed test vector.
+ llvm::Value *Args[5] = {llvm::ConstantExpr::getBitCast(FuncNameVar, I8PtrTy),
+ Builder.getInt64(FunctionHash),
+ Builder.getInt32(MCDCBitmapBytes),
+ Builder.getInt32(MCDCTestVectorBitmapID),
+ MCDCCondBitmapAddr.getPointer()};
+ Builder.CreateCall(
+ CGM.getIntrinsic(llvm::Intrinsic::instrprof_mcdc_tvbitmap_update), Args);
+}
+
+void CodeGenPGO::emitMCDCCondBitmapReset(CGBuilderTy &Builder, const Expr *S,
+ Address MCDCCondBitmapAddr) {
+ if (!canEmitMCDCCoverage(Builder) || !RegionMCDCBitmapMap)
+ return;
+
+ S = S->IgnoreParens();
+
+ if (RegionMCDCBitmapMap->find(S) == RegionMCDCBitmapMap->end())
+ return;
+
+ // Emit intrinsic that resets a dedicated temporary value on the stack to 0.
+ Builder.CreateStore(Builder.getInt32(0), MCDCCondBitmapAddr);
+}
+
+void CodeGenPGO::emitMCDCCondBitmapUpdate(CGBuilderTy &Builder, const Expr *S,
+ Address MCDCCondBitmapAddr,
+ llvm::Value *Val) {
+ if (!canEmitMCDCCoverage(Builder) || !RegionCondIDMap)
+ return;
+
+ // Even though, for simplicity, parentheses and unary logical-NOT operators
+ // are considered part of their underlying condition for both MC/DC and
+ // branch coverage, the condition IDs themselves are assigned and tracked
+ // using the underlying condition itself. This is done solely for
+ // consistency since parentheses and logical-NOTs are ignored when checking
+ // whether the condition is actually an instrumentable condition. This can
+ // also make debugging a bit easier.
+ S = CodeGenFunction::stripCond(S);
+
+ auto ExprMCDCConditionIDMapIterator = RegionCondIDMap->find(S);
+ if (ExprMCDCConditionIDMapIterator == RegionCondIDMap->end())
+ return;
+
+ // Extract the ID of the condition we are setting in the bitmap.
+ unsigned CondID = ExprMCDCConditionIDMapIterator->second;
+ assert(CondID > 0 && "Condition has no ID!");
+
+ auto *I8PtrTy = llvm::PointerType::getUnqual(CGM.getLLVMContext());
+
+ // 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 - 1),
+ MCDCCondBitmapAddr.getPointer(), Val};
+ Builder.CreateCall(
+ CGM.getIntrinsic(llvm::Intrinsic::instrprof_mcdc_condbitmap_update),
+ Args);
+}
+
void CodeGenPGO::setValueProfilingFlag(llvm::Module &M) {
if (CGM.getCodeGenOpts().hasProfileClangInstr())
M.addModuleFlag(llvm::Module::Warning, "EnableValueProfiling",
diff --git a/clang/lib/CodeGen/CodeGenPGO.h b/clang/lib/CodeGen/CodeGenPGO.h
index 392ec5a144fee6..6596b6c3527764 100644
--- a/clang/lib/CodeGen/CodeGenPGO.h
+++ b/clang/lib/CodeGen/CodeGenPGO.h
@@ -33,8 +33,11 @@ class CodeGenPGO {
std::array <unsigned, llvm::IPVK_Last + 1> NumValueSites;
unsigned NumRegionCounters;
+ unsigned MCDCBitmapBytes;
uint64_t FunctionHash;
std::unique_ptr<llvm::DenseMap<const Stmt *, unsigned>> RegionCounterMap;
+ std::unique_ptr<llvm::DenseMap<const Stmt *, unsigned>> RegionMCDCBitmapMap;
+ std::unique_ptr<llvm::DenseMap<const Stmt *, unsigned>> RegionCondIDMap;
std::unique_ptr<llvm::DenseMap<const Stmt *, uint64_t>> StmtCountMap;
std::unique_ptr<llvm::InstrProfRecord> ProfRecord;
std::vector<uint64_t> RegionCounts;
@@ -43,7 +46,8 @@ class CodeGenPGO {
public:
CodeGenPGO(CodeGenModule &CGModule)
: CGM(CGModule), FuncNameVar(nullptr), NumValueSites({{0}}),
- NumRegionCounters(0), FunctionHash(0), CurrentRegionCount(0) {}
+ NumRegionCounters(0), MCDCBitmapBytes(0), FunctionHash(0),
+ CurrentRegionCount(0) {}
/// Whether or not we have PGO region data for the current function. This is
/// false both when we have no data at all and when our data has been
@@ -103,10 +107,18 @@ class CodeGenPGO {
bool IsInMainFile);
bool skipRegionMappingForDecl(const Decl *D);
void emitCounterRegionMapping(const Decl *D);
+ bool canEmitMCDCCoverage(const CGBuilderTy &Builder);
public:
void emitCounterIncrement(CGBuilderTy &Builder, const Stmt *S,
llvm::Value *StepV);
+ void emitMCDCTestVectorBitmapUpdate(CGBuilderTy &Builder, const Expr *S,
+ Address MCDCCondBitmapAddr);
+ void emitMCDCParameters(CGBuilderTy &Builder);
+ void emitMCDCCondBitmapReset(CGBuilderTy &Builder, const Expr *S,
+ Address MCDCCondBitmapAddr);
+ void emitMCDCCondBitmapUpdate(CGBuilderTy &Builder, const Expr *S,
+ Address MCDCCondBitmapAddr, llvm::Value *Val);
/// Return the region count for the counter at the given index.
uint64_t getRegionCount(const Stmt *S) {
diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp
index 56411e2240e505..bf227386a71b78 100644
--- a/clang/lib/CodeGen/CoverageMappingGen.cpp
+++ b/clang/lib/CodeGen/CoverageMappingGen.cpp
@@ -95,6 +95,8 @@ void CoverageSourceInfo::updateNextTokLoc(SourceLocation Loc) {
}
namespace {
+using MCDCConditionID = CounterMappingRegion::MCDCConditionID;
+using MCDCParameters = CounterMappingRegion::MCDCParameters;
/// A region of source code that can be mapped to a counter.
class SourceMappingRegion {
@@ -104,6 +106,9 @@ class SourceMappingRegion {
/// Secondary Counter used for Branch Regions for "False" branches.
std::optional<Counter> FalseCount;
+ /// Parameters used for Modified Condition/Decision Coverage
+ MCDCParameters MCDCParams;
+
/// The region's starting location.
std::optional<SourceLocation> LocStart;
@@ -122,11 +127,18 @@ class SourceMappingRegion {
}
SourceMappingRegion(Counter Count, std::optional<Counter> FalseCount,
+ MCDCParameters MCDCParams,
std::optional<SourceLocation> LocStart,
std::optional<SourceLocation> LocEnd,
bool GapRegion = false)
- : Count(Count), FalseCount(FalseCount), LocStart(LocStart),
- LocEnd(LocEnd), GapRegion(GapRegion) {}
+ : Count(Count), FalseCount(FalseCount), MCDCParams(MCDCParams),
+ LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion) {}
+
+ SourceMappingRegion(MCDCParameters MCDCParams,
+ std::optional<SourceLocation> LocStart,
+ std::optional<SourceLocation> LocEnd)
+ : MCDCParams(MCDCParams), LocStart(LocStart), LocEnd(LocEnd),
+ GapRegion(false) {}
const Counter &getCounter() const { return Count; }
@@ -163,6 +175,10 @@ class SourceMappingRegion {
void setGap(bool Gap) { GapRegion = Gap; }
bool isBranch() const { return FalseCount.has_value(); }
+
+ bool isMCDCDecision() const { return MCDCParams.NumConditions != 0; }
+
+ const MCDCParameters &getMCDCParams() const { return MCDCParams; }
};
/// Spelling locations for the start and end of a source region.
@@ -454,8 +470,13 @@ class CoverageMappingBuilder {
SR.LineEnd, SR.ColumnEnd));
} else if (Region.isBranch()) {
MappingRegions.push_back(CounterMappingRegion::makeBranchRegion(
- Region.getCounter(), Region.getFalseCounter(), *CovFileID,
- SR.LineStart, SR.ColumnStart, SR.LineEnd, SR.ColumnEnd));
+ Region.getCounter(), Region.getFalseCounter(),
+ Region.getMCDCParams(), *CovFileID, SR.LineStart, SR.ColumnStart,
+ SR.LineEnd, SR.ColumnEnd));
+ } else if (Region.isMCDCDecision()) {
+ MappingRegions.push_back(CounterMappingRegion::makeDecisionRegion(
+ Region.getMCDCParams(), *CovFileID, SR.LineStart, SR.ColumnStart,
+ SR.LineEnd, SR.ColumnEnd));
} else {
MappingRegions.push_back(CounterMappingRegion::makeRegion(
Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart,
@@ -542,6 +563,239 @@ struct EmptyCoverageMappingBuilder : public CoverageMappingBuilder {
}
};
+/// A wrapper object for maintaining stacks to track the resursive AST visitor
+/// walks for the purpose of assigning IDs to leaf-level conditions measured by
+/// MC/DC. The object is created with a reference to the MCDCBitmapMap that was
+/// created during the initial AST walk. The presence of a bitmap associated
+/// with a boolean expression (top-level logical operator nest) indicates that
+/// the boolean expression qualified for MC/DC. The resulting condition IDs
+/// are preserved in a map reference that is also provided during object
+/// creation.
+struct MCDCCoverageBuilder {
+
+ /// The AST walk recursively visits nested logical-AND or logical-OR binary
+ /// operator nodes and then visits their LHS and RHS children nodes. As this
+ /// happens, the algorithm will assign IDs to each operator's LHS and RHS side
+ /// as the walk moves deeper into the nest. At each level of the recursive
+ /// nest, the LHS and RHS may actually correspond to larger subtrees (not
+ /// leaf-conditions). If this is the case, when that node is visited, the ID
+ /// assigned to the subtree is re-assigned to its LHS, and a new ID is given
+ /// to its RHS. At the end of the walk, all leaf-level conditions will have a
+ /// unique ID -- keep in mind that the final set of IDs may not be in
+ /// numerical order from left to right.
+ ///
+ /// Example: "x = (A && B) || (C && D) || (D && F)"
+ ///
+ /// Visit Depth1:
+ /// (A && B) || (C && D) || (D && F)
+ /// ^-------LHS--------^ ^-RHS--^
+ /// ID=1 ID=2
+ ///
+ /// Visit LHS-Depth2:
+ /// (A && B) || (C && D)
+ /// ^-LHS--^ ^-RHS--^
+ /// ID=1 ID=3
+ ///
+ /// Visit LHS-Depth3:
+ /// (A && B)
+ /// LHS RHS
+ /// ID=1 ID=4
+ ///
+ /// Visit RHS-Depth3:
+ /// (C && D)
+ /// LHS RHS
+ /// ID=3 ID=5
+ ///
+ /// Visit RHS-Depth2: (D && F)
+ /// LHS RHS
+ /// ID=2 ID=6
+ ///
+ /// Visit Depth1:
+ /// (A && B) || (C && D) || (D && F)
+ /// ID=1 ID=4 ID=3 ID=5 ID=2 ID=6
+ ///
+ /// A node ID of '0' always means MC/DC isn't being tracked.
+ ///
+ /// As the AST walk proceeds recursively, the algorithm will also use stacks
+ /// to track the IDs of logical-AND and logical-OR operations on the RHS so
+ /// that it can be determined which nodes are executed next, depending on how
+ /// a LHS or RHS of a logical-AND or logical-OR is evaluated. This
+ /// information relies on the assigned IDs and are embedded within the
+ /// coverage region IDs of each branch region associated with a leaf-level
+ /// condition. This information helps the visualization tool reconstruct all
+ /// possible test vectors for the purposes of MC/DC analysis. if a "next" node
+ /// ID is '0', it means it's the end of the test vector. The following rules
+ /// are used:
+ ///
+ /// For logical-AND ("LHS && RHS"):
+ /// - If LHS is TRUE, execution goes to the RHS node.
+ /// - If LHS is FALSE, execution goes to the LHS node of the next logical-OR.
+ /// If that does not exist, execution exits (ID == 0).
+ ///
+ /// - If RHS is TRUE, execution goes to LHS node of the next logical-AND.
+ /// If that does not exist, execution exits (ID == 0).
+ /// - If RHS is FALSE, execution goes to the LHS node of the next logical-OR.
+ /// If that does not exist, execution exits (ID == 0).
+ ///
+ /// For logical-OR ("LHS || RHS"):
+ /// - If LHS is TRUE, execution goes to the LHS node of the next logical-AND.
+ /// If that does not exist, execution exits (ID == 0).
+ /// - If LHS is FALSE, execution goes to the RHS node.
+ ///
+ /// - If RHS is TRUE, execution goes to LHS node of the next logical-AND.
+ /// If that does not exist, execution exits (ID == 0).
+ /// - If RHS is FALSE, execution goes to the LHS node of the next logical-OR.
+ /// If that does not exist, execution exits (ID == 0).
+ ///
+ /// Finally, the condition IDs are also used when instrumenting the code to
+ /// indicate a unique offset into a temporary bitmap that represents the true
+ /// or false evaluation of that particular condition.
+ ///
+ /// NOTE regarding the use of CodeGenFunction::stripCond(). Even though, for
+ /// simplicity, parentheses and unary logical-NOT operators are considered
+ /// part of their underlying condition for both MC/DC and branch coverage, the
+ /// condition IDs themselves are assigned and tracked using the underlying
+ /// condition itself. This is done solely for consistency since parentheses
+ /// and logical-NOTs are ignored when checking whether the condition is
+ /// actually an instrumentable condition. This can also make debugging a bit
+ /// easier.
+
+private:
+ CodeGenModule &CGM;
+
+ llvm::SmallVector<MCDCConditionID> AndRHS;
+ llvm::SmallVector<MCDCConditionID> OrRHS;
+ llvm::SmallVector<const BinaryOperator *> NestLevel;
+ llvm::DenseMap<const Stmt *, MCDCConditionID> &CondIDs;
+ llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap;
+ MCDCConditionID NextID = 1;
+ bool NotMapped = false;
+
+ /// Is this a logical-AND operation?
+ bool isLAnd(const BinaryOperator *E) const {
+ return E->getOpcode() == BO_LAnd;
+ }
+
+ /// Push an ID onto the corresponding RHS stack.
+ void pushRHS(const BinaryOperator *E) {
+ llvm::SmallVector<MCDCConditionID> &rhs = isLAnd(E) ? AndRHS : OrRHS;
+ rhs.push_back(CondIDs[CodeGenFunction::stripCond(E->getRHS())]);
+ }
+
+ /// Pop an ID from the corresponding RHS stack.
+ void popRHS(const BinaryOperator *E) {
+ llvm::SmallVector<MCDCConditionID> &rhs = isLAnd(E) ? AndRHS : OrRHS;
+ if (!rhs.empty())
+ rhs.pop_back();
+ }
+
+ /// If the expected ID is on top, pop it off the corresponding RHS stack.
+ void popRHSifTop(const BinaryOperator *E) {
+ if (!OrRHS.empty() && CondIDs[E] == OrRHS.back())
+ OrRHS.pop_back();
+ else if (!AndRHS.empty() && CondIDs[E] == AndRHS.back())
+ AndRHS.pop_back();
+ }
+
+public:
+ MCDCCoverageBuilder(CodeGenModule &CGM,
+ llvm::DenseMap<const Stmt *, MCDCConditionID> &CondIDMap,
+ llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap)
+ : CGM(CGM), CondIDs(CondIDMap), MCDCBitmapMap(MCDCBitmapMap) {}
+
+ /// Return the ID of the RHS of the next, upper nest-level logical-OR.
+ MCDCConditionID getNextLOrCondID() const {
+ return OrRHS.empty() ? 0 : OrRHS.back();
+ }
+
+ /// Return the ID of the RHS of the next, upper nest-level logical-AND.
+ MCDCConditionID getNextLAndCondID() const {
+ return AndRHS.empty() ? 0 : AndRHS.back();
+ }
+
+ /// Return the ID of a given condition.
+ MCDCConditionID getCondID(const Expr *Cond) const {
+ auto I = CondIDs.find(CodeGenFunction::stripCond(Cond));
+ if (I == CondIDs.end())
+ return 0;
+ else
+ return I->second;
+ }
+
+ /// Push the binary operator statement to track the nest level and assign IDs
+ /// to the operator's LHS and RHS. The RHS may be a larger subtree that is
+ /// broken up on successive levels.
+ void pushAndAssignIDs(const BinaryOperator *E) {
+ if (!CGM.getCodeGenOpts().MCDCCoverage)
+ return;
+
+ // If binary expression is disqualified, don't do mapping.
+ if (NestLevel.empty() && MCDCBitmapMap.find(CodeGenFunction::stripCond(
+ E)) == MCDCBitmapMap.end())
+ NotMapped = true;
+
+ // Push Stmt on 'NestLevel' stack to keep track of nest location.
+ NestLevel.push_back(E);
+
+ // Don't go any further if we don't need to map condition IDs.
+ if (NotMapped)
+ return;
+
+ // If the operator itself has an assigned ID, this means it represents a
+ // larger subtree. In this case, pop its ID out of the RHS stack and
+ // assign that ID to its LHS node. Its RHS will receive a new ID.
+ if (CondIDs.find(CodeGenFunction::stripCond(E)) != CondIDs.end()) {
+ // If Stmt has an ID, assign its ID to LHS
+ CondIDs[CodeGenFunction::stripCond(E->getLHS())] = CondIDs[E];
+
+ // Since the operator's LHS assumes the operator's same ID, pop the
+ // operator from the RHS stack so that if LHS short-circuits, it won't be
+ // incorrectly re-used as the node executed next.
+ popRHSifTop(E);
+ } else {
+ // Otherwise, assign ID+1 to LHS.
+ CondIDs[CodeGenFunction::stripCond(E->getLHS())] = NextID++;
+ }
+
+ // Assign ID+1 to RHS.
+ CondIDs[CodeGenFunction::stripCond(E->getRHS())] = NextID++;
+
+ // Push ID of Stmt's RHS so that LHS nodes know about it
+ pushRHS(E);
+ }
+
+ /// Pop the binary operator from the next level. If the walk is at the top of
+ /// the next, assign the total number of conditions.
+ unsigned popAndReturnCondCount(const BinaryOperator *E) {
+ if (!CGM.getCodeGenOpts().MCDCCoverage)
+ return 0;
+
+ unsigned TotalConds = 0;
+
+ // Pop Stmt from 'NestLevel' stack.
+ assert(NestLevel.back() == E);
+ NestLevel.pop_back();
+
+ // Reset state if not doing mapping.
+ if (NestLevel.empty() && NotMapped) {
+ NotMapped = false;
+ return 0;
+ }
+
+ // Pop RHS ID.
+ popRHS(E);
+
+ // If at the parent (NestLevel=0), set conds and reset.
+ if (NestLevel.empty()) {
+ TotalConds = NextID - 1;
+
+ // Reset ID back to beginning.
+ NextID = 1;
+ }
+ return TotalConds;
+ }
+};
+
/// A StmtVisitor that creates coverage mapping regions which map
/// from the source code locations to the PGO counters.
struct CounterCoverageMappingBuilder
@@ -550,8 +804,14 @@ struct CounterCoverageMappingBuilder
/// The map of statements to count values.
llvm::DenseMap<const Stmt *, unsigned> &CounterMap;
+ /// The map of statements to bitmap coverage object values.
+ llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap;
+
/// A stack of currently live regions.
- std::vector<SourceMappingRegion> RegionStack;
+ llvm::SmallVector<SourceMappingRegion> RegionStack;
+
+ /// An object to manage MCDC regions.
+ MCDCCoverageBuilder MCDCBuilder;
CounterExpressionBuilder Builder;
@@ -589,6 +849,8 @@ struct CounterCoverageMappingBuilder
return Counter::getCounter(CounterMap[S]);
}
+ unsigned getRegionBitmap(const Stmt *S) { return MCDCBitmapMap[S]; }
+
/// Push a region onto the stack.
///
/// Returns the index on the stack where the region was pushed. This can be
@@ -596,7 +858,9 @@ struct CounterCoverageMappingBuilder
size_t pushRegion(Counter Count,
std::optional<SourceLocation> StartLoc = std::nullopt,
std::optional<SourceLocation> EndLoc = std::nullopt,
- std::optional<Counter> FalseCount = std::nullopt) {
+ std::optional<Counter> FalseCount = std::nullopt,
+ MCDCConditionID ID = 0, MCDCConditionID TrueID = 0,
+ MCDCConditionID FalseID = 0) {
if (StartLoc && !FalseCount) {
MostRecentLocation = *StartLoc;
@@ -615,7 +879,19 @@ struct CounterCoverageMappingBuilder
StartLoc = std::nullopt;
if (EndLoc && EndLoc->isInvalid())
EndLoc = std::nullopt;
- RegionStack.emplace_back(Count, FalseCount, StartLoc, EndLoc);
+ RegionStack.emplace_back(Count, FalseCount,
+ MCDCParameters{0, 0, ID, TrueID, FalseID},
+ StartLoc, EndLoc);
+
+ return RegionStack.size() - 1;
+ }
+
+ size_t pushRegion(unsigned BitmapIdx, unsigned Conditions,
+ std::optional<SourceLocation> StartLoc = std::nullopt,
+ std::optional<SourceLocation> EndLoc = std::nullopt) {
+
+ RegionStack.emplace_back(MCDCParameters{BitmapIdx, Conditions}, StartLoc,
+ EndLoc);
return RegionStack.size() - 1;
}
@@ -746,7 +1022,9 @@ struct CounterCoverageMappingBuilder
/// and add it to the function's SourceRegions. A branch region tracks a
/// "True" counter and a "False" counter for boolean expressions that
/// result in the generation of a branch.
- void createBranchRegion(const Expr *C, Counter TrueCnt, Counter FalseCnt) {
+ void createBranchRegion(const Expr *C, Counter TrueCnt, Counter FalseCnt,
+ MCDCConditionID ID = 0, MCDCConditionID TrueID = 0,
+ MCDCConditionID FalseID = 0) {
// Check for NULL conditions.
if (!C)
return;
@@ -764,13 +1042,21 @@ struct CounterCoverageMappingBuilder
// CodeGenFunction.c always returns false, but that is very heavy-handed.
if (ConditionFoldsToBool(C))
popRegions(pushRegion(Counter::getZero(), getStart(C), getEnd(C),
- Counter::getZero()));
+ Counter::getZero(), ID, TrueID, FalseID));
else
// Otherwise, create a region with the True counter and False counter.
- popRegions(pushRegion(TrueCnt, getStart(C), getEnd(C), FalseCnt));
+ popRegions(pushRegion(TrueCnt, getStart(C), getEnd(C), FalseCnt, ID,
+ TrueID, FalseID));
}
}
+ /// Create a Decision Region with a BitmapIdx and number of Conditions. This
+ /// type of region "contains" branch regions, one for each of the conditions.
+ /// The visualization tool will group everything together.
+ void createDecisionRegion(const Expr *C, unsigned BitmapIdx, unsigned Conds) {
+ popRegions(pushRegion(BitmapIdx, Conds, getStart(C), getEnd(C)));
+ }
+
/// Create a Branch Region around a SwitchCase for code coverage
/// and add it to the function's SourceRegions.
void createSwitchCaseRegion(const SwitchCase *SC, Counter TrueCnt,
@@ -851,8 +1137,12 @@ struct CounterCoverageMappingBuilder
// we've seen this region.
if (StartLocs.insert(Loc).second) {
if (I.isBranch())
- SourceRegions.emplace_back(I.getCounter(), I.getFalseCounter(), Loc,
- getEndOfFileOrMacro(Loc), I.isBranch());
+ SourceRegions.emplace_back(
+ I.getCounter(), I.getFalseCounter(),
+ MCDCParameters{0, 0, I.getMCDCParams().ID,
+ I.getMCDCParams().TrueID,
+ I.getMCDCParams().FalseID},
+ Loc, getEndOfFileOrMacro(Loc), I.isBranch());
else
SourceRegions.emplace_back(I.getCounter(), Loc,
getEndOfFileOrMacro(Loc));
@@ -971,9 +1261,13 @@ struct CounterCoverageMappingBuilder
CounterCoverageMappingBuilder(
CoverageMappingModuleGen &CVM,
- llvm::DenseMap<const Stmt *, unsigned> &CounterMap, SourceManager &SM,
- const LangOptions &LangOpts)
- : CoverageMappingBuilder(CVM, SM, LangOpts), CounterMap(CounterMap) {}
+ llvm::DenseMap<const Stmt *, unsigned> &CounterMap,
+ llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap,
+ llvm::DenseMap<const Stmt *, MCDCConditionID> &CondIDMap,
+ SourceManager &SM, const LangOptions &LangOpts)
+ : CoverageMappingBuilder(CVM, SM, LangOpts), CounterMap(CounterMap),
+ MCDCBitmapMap(MCDCBitmapMap),
+ MCDCBuilder(CVM.getCodeGenModule(), CondIDMap, MCDCBitmapMap) {}
/// Write the mapping data to the output stream
void write(llvm::raw_ostream &OS) {
@@ -1519,6 +1813,9 @@ struct CounterCoverageMappingBuilder
}
void VisitBinLAnd(const BinaryOperator *E) {
+ // Keep track of Binary Operator and assign MCDC condition IDs
+ MCDCBuilder.pushAndAssignIDs(E);
+
extendRegion(E->getLHS());
propagateCounts(getRegion().getCounter(), E->getLHS());
handleFileExit(getEnd(E->getLHS()));
@@ -1527,6 +1824,11 @@ struct CounterCoverageMappingBuilder
extendRegion(E->getRHS());
propagateCounts(getRegionCounter(E), E->getRHS());
+ // Process Binary Operator and create MCDC Decision Region if top-level
+ unsigned NumConds = 0;
+ if ((NumConds = MCDCBuilder.popAndReturnCondCount(E)))
+ createDecisionRegion(E, getRegionBitmap(E), NumConds);
+
// Extract the RHS's Execution Counter.
Counter RHSExecCnt = getRegionCounter(E);
@@ -1536,13 +1838,30 @@ struct CounterCoverageMappingBuilder
// Extract the Parent Region Counter.
Counter ParentCnt = getRegion().getCounter();
+ // Extract the MCDC condition IDs (returns 0 if not needed).
+ MCDCConditionID NextOrID = MCDCBuilder.getNextLOrCondID();
+ MCDCConditionID NextAndID = MCDCBuilder.getNextLAndCondID();
+ MCDCConditionID LHSid = MCDCBuilder.getCondID(E->getLHS());
+ MCDCConditionID RHSid = MCDCBuilder.getCondID(E->getRHS());
+
// Create Branch Region around LHS condition.
+ // MC/DC: For "LHS && RHS"
+ // - If LHS is TRUE, execution goes to the RHS.
+ // - If LHS is FALSE, execution goes to the LHS of the next logical-OR.
+ // If that does not exist, execution exits (ID == 0).
createBranchRegion(E->getLHS(), RHSExecCnt,
- subtractCounters(ParentCnt, RHSExecCnt));
+ subtractCounters(ParentCnt, RHSExecCnt), LHSid, RHSid,
+ NextOrID);
// Create Branch Region around RHS condition.
+ // MC/DC: For "LHS && RHS"
+ // - If RHS is TRUE, execution goes to LHS of the next logical-AND.
+ // If that does not exist, execution exits (ID == 0).
+ // - If RHS is FALSE, execution goes to the LHS of the next logical-OR.
+ // If that does not exist, execution exits (ID == 0).
createBranchRegion(E->getRHS(), RHSTrueCnt,
- subtractCounters(RHSExecCnt, RHSTrueCnt));
+ subtractCounters(RHSExecCnt, RHSTrueCnt), RHSid,
+ NextAndID, NextOrID);
}
// Determine whether the right side of OR operation need to be visited.
@@ -1556,6 +1875,9 @@ struct CounterCoverageMappingBuilder
}
void VisitBinLOr(const BinaryOperator *E) {
+ // Keep track of Binary Operator and assign MCDC condition IDs
+ MCDCBuilder.pushAndAssignIDs(E);
+
extendRegion(E->getLHS());
Counter OutCount = propagateCounts(getRegion().getCounter(), E->getLHS());
handleFileExit(getEnd(E->getLHS()));
@@ -1564,6 +1886,11 @@ struct CounterCoverageMappingBuilder
extendRegion(E->getRHS());
propagateCounts(getRegionCounter(E), E->getRHS());
+ // Process Binary Operator and create MCDC Decision Region if top-level
+ unsigned NumConds = 0;
+ if ((NumConds = MCDCBuilder.popAndReturnCondCount(E)))
+ createDecisionRegion(E, getRegionBitmap(E), NumConds);
+
// Extract the RHS's Execution Counter.
Counter RHSExecCnt = getRegionCounter(E);
@@ -1577,13 +1904,28 @@ struct CounterCoverageMappingBuilder
// Extract the Parent Region Counter.
Counter ParentCnt = getRegion().getCounter();
+ // Extract the MCDC condition IDs (returns 0 if not needed).
+ MCDCConditionID NextOrID = MCDCBuilder.getNextLOrCondID();
+ MCDCConditionID NextAndID = MCDCBuilder.getNextLAndCondID();
+ MCDCConditionID LHSid = MCDCBuilder.getCondID(E->getLHS());
+ MCDCConditionID RHSid = MCDCBuilder.getCondID(E->getRHS());
+
// Create Branch Region around LHS condition.
+ // MC/DC: For "LHS || RHS"
+ // - If LHS is TRUE, execution goes to the LHS of the next logical-AND.
+ // If that does not exist, execution exits (ID == 0).
+ // - If LHS is FALSE, execution goes to the RHS.
createBranchRegion(E->getLHS(), subtractCounters(ParentCnt, RHSExecCnt),
- RHSExecCnt);
+ RHSExecCnt, LHSid, NextAndID, RHSid);
// Create Branch Region around RHS condition.
+ // MC/DC: For "LHS || RHS"
+ // - If RHS is TRUE, execution goes to LHS of the next logical-AND.
+ // If that does not exist, execution exits (ID == 0).
+ // - If RHS is FALSE, execution goes to the LHS of the next logical-OR.
+ // If that does not exist, execution exits (ID == 0).
createBranchRegion(E->getRHS(), subtractCounters(RHSExecCnt, RHSFalseCnt),
- RHSFalseCnt);
+ RHSFalseCnt, RHSid, NextAndID, NextOrID);
}
void VisitLambdaExpr(const LambdaExpr *LE) {
@@ -1633,11 +1975,23 @@ static void dump(llvm::raw_ostream &OS, StringRef FunctionName,
OS << "File " << R.FileID << ", " << R.LineStart << ":" << R.ColumnStart
<< " -> " << R.LineEnd << ":" << R.ColumnEnd << " = ";
- Ctx.dump(R.Count, OS);
- if (R.Kind == CounterMappingRegion::BranchRegion) {
- OS << ", ";
- Ctx.dump(R.FalseCount, OS);
+ if (R.Kind == CounterMappingRegion::MCDCDecisionRegion) {
+ OS << "M:" << R.MCDCParams.BitmapIdx;
+ OS << ", C:" << R.MCDCParams.NumConditions;
+ } else {
+ Ctx.dump(R.Count, OS);
+
+ if (R.Kind == CounterMappingRegion::BranchRegion ||
+ R.Kind == CounterMappingRegion::MCDCBranchRegion) {
+ OS << ", ";
+ Ctx.dump(R.FalseCount, OS);
+ }
+ }
+
+ if (R.Kind == CounterMappingRegion::MCDCBranchRegion) {
+ OS << " [" << R.MCDCParams.ID << "," << R.MCDCParams.TrueID;
+ OS << "," << R.MCDCParams.FalseID << "] ";
}
if (R.Kind == CounterMappingRegion::ExpansionRegion)
@@ -1846,8 +2200,9 @@ unsigned CoverageMappingModuleGen::getFileID(FileEntryRef File) {
void CoverageMappingGen::emitCounterMapping(const Decl *D,
llvm::raw_ostream &OS) {
- assert(CounterMap);
- CounterCoverageMappingBuilder Walker(CVM, *CounterMap, SM, LangOpts);
+ assert(CounterMap && MCDCBitmapMap);
+ CounterCoverageMappingBuilder Walker(CVM, *CounterMap, *MCDCBitmapMap,
+ *CondIDMap, SM, LangOpts);
Walker.VisitDecl(D);
Walker.write(OS);
}
diff --git a/clang/lib/CodeGen/CoverageMappingGen.h b/clang/lib/CodeGen/CoverageMappingGen.h
index 77d7c6cd87cfb0..62cea173c9fc93 100644
--- a/clang/lib/CodeGen/CoverageMappingGen.h
+++ b/clang/lib/CodeGen/CoverageMappingGen.h
@@ -150,16 +150,22 @@ class CoverageMappingGen {
SourceManager &SM;
const LangOptions &LangOpts;
llvm::DenseMap<const Stmt *, unsigned> *CounterMap;
+ llvm::DenseMap<const Stmt *, unsigned> *MCDCBitmapMap;
+ llvm::DenseMap<const Stmt *, unsigned> *CondIDMap;
public:
CoverageMappingGen(CoverageMappingModuleGen &CVM, SourceManager &SM,
const LangOptions &LangOpts)
- : CVM(CVM), SM(SM), LangOpts(LangOpts), CounterMap(nullptr) {}
+ : CVM(CVM), SM(SM), LangOpts(LangOpts), CounterMap(nullptr),
+ MCDCBitmapMap(nullptr), CondIDMap(nullptr) {}
CoverageMappingGen(CoverageMappingModuleGen &CVM, SourceManager &SM,
const LangOptions &LangOpts,
- llvm::DenseMap<const Stmt *, unsigned> *CounterMap)
- : CVM(CVM), SM(SM), LangOpts(LangOpts), CounterMap(CounterMap) {}
+ llvm::DenseMap<const Stmt *, unsigned> *CounterMap,
+ llvm::DenseMap<const Stmt *, unsigned> *MCDCBitmapMap,
+ llvm::DenseMap<const Stmt *, unsigned> *CondIDMap)
+ : CVM(CVM), SM(SM), LangOpts(LangOpts), CounterMap(CounterMap),
+ MCDCBitmapMap(MCDCBitmapMap), CondIDMap(CondIDMap) {}
/// Emit the coverage mapping data which maps the regions of
/// code to counters that will be used to find the execution
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index acfa119805068d..2d8ef841d4f6be 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -698,6 +698,17 @@ static void addPGOAndCoverageFlags(const ToolChain &TC, Compilation &C,
CmdArgs.push_back("-fcoverage-mapping");
}
+ if (Args.hasFlag(options::OPT_fmcdc_coverage, options::OPT_fno_mcdc_coverage,
+ false)) {
+ if (!Args.hasFlag(options::OPT_fcoverage_mapping,
+ options::OPT_fno_coverage_mapping, false))
+ D.Diag(clang::diag::err_drv_argument_only_allowed_with)
+ << "-fcoverage-mcdc"
+ << "-fcoverage-mapping";
+
+ CmdArgs.push_back("-fcoverage-mcdc");
+ }
+
if (Arg *A = Args.getLastArg(options::OPT_ffile_compilation_dir_EQ,
options::OPT_fcoverage_compilation_dir_EQ)) {
if (A->getOption().matches(options::OPT_ffile_compilation_dir_EQ))
diff --git a/clang/test/CoverageMapping/branch-constfolded.cpp b/clang/test/CoverageMapping/branch-constfolded.cpp
index 5173286addbb4d..4fdb640506c9d3 100644
--- a/clang/test/CoverageMapping/branch-constfolded.cpp
+++ b/clang/test/CoverageMapping/branch-constfolded.cpp
@@ -1,90 +1,100 @@
// Test that branch regions are not generated for constant-folded conditions.
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name branch-constfolded.cpp %s | FileCheck %s
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 -fcoverage-mcdc -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name branch-constfolded.cpp %s | FileCheck %s -check-prefix=MCDC
// CHECK-LABEL: _Z6fand_0b:
-bool fand_0(bool a) {
+bool fand_0(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:20 = M:0, C:2
return false && a; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, 0
} // CHECK: Branch,File 0, [[@LINE-1]]:19 -> [[@LINE-1]]:20 = #2, (#1 - #2)
// CHECK-LABEL: _Z6fand_1b:
-bool fand_1(bool a) {
+bool fand_1(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:19 = M:0, C:2
return a && true; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = #1, (#0 - #1)
} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:19 = 0, 0
// CHECK-LABEL: _Z6fand_2bb:
-bool fand_2(bool a, bool b) {
+bool fand_2(bool a, bool b) {// MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:25 = M:0, C:3
return false && a && b; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, 0
} // CHECK: Branch,File 0, [[@LINE-1]]:19 -> [[@LINE-1]]:20 = #4, (#3 - #4)
// CHECK: Branch,File 0, [[@LINE-2]]:24 -> [[@LINE-2]]:25 = #2, (#1 - #2)
// CHECK-LABEL: _Z6fand_3bb:
-bool fand_3(bool a, bool b) {
+bool fand_3(bool a, bool b) {// MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:24 = M:0, C:3
return a && true && b; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = #3, (#0 - #3)
} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:19 = 0, 0
// CHECK: Branch,File 0, [[@LINE-2]]:23 -> [[@LINE-2]]:24 = #2, (#1 - #2)
// CHECK-LABEL: _Z6fand_4bb:
-bool fand_4(bool a, bool b) {
+bool fand_4(bool a, bool b) {// MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:25 = M:0, C:3
return a && b && false; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = #3, (#0 - #3)
} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:16 = #4, (#3 - #4)
// CHECK: Branch,File 0, [[@LINE-2]]:20 -> [[@LINE-2]]:25 = 0, 0
// CHECK-LABEL: _Z6fand_5b:
-bool fand_5(bool a) {
+bool fand_5(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:23 = M:0, C:2
return false && true; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, 0
} // CHECK: Branch,File 0, [[@LINE-1]]:19 -> [[@LINE-1]]:23 = 0, 0
// CHECK-LABEL: _Z6fand_6b:
-bool fand_6(bool a) {
+bool fand_6(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:19 = M:0, C:2
return true && a; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = 0, 0
} // CHECK: Branch,File 0, [[@LINE-1]]:18 -> [[@LINE-1]]:19 = #2, (#1 - #2)
// CHECK-LABEL: _Z6fand_7b:
-bool fand_7(bool a) {
+bool fand_7(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:20 = M:0, C:2
return a && false; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = #1, (#0 - #1)
} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:20 = 0, 0
// CHECK-LABEL: _Z5for_0b:
-bool for_0(bool a) {
+bool for_0(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:19 = M:0, C:2
return true || a; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = 0, 0
} // CHECK: Branch,File 0, [[@LINE-1]]:18 -> [[@LINE-1]]:19 = (#1 - #2), #2
// CHECK-LABEL: _Z5for_1b:
-bool for_1(bool a) {
+bool for_1(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:20 = M:0, C:2
return a || false; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = (#0 - #1), #1
} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:20 = 0, 0
// CHECK-LABEL: _Z5for_2bb:
-bool for_2(bool a, bool b) {
+bool for_2(bool a, bool b) {// MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:24 = M:0, C:3
return true || a || b; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = 0, 0
} // CHECK: Branch,File 0, [[@LINE-1]]:18 -> [[@LINE-1]]:19 = (#3 - #4), #4
// CHECK: Branch,File 0, [[@LINE-2]]:23 -> [[@LINE-2]]:24 = (#1 - #2), #2
// CHECK-LABEL: _Z5for_3bb:
-bool for_3(bool a, bool b) {
+bool for_3(bool a, bool b) {// MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:25 = M:0, C:3
return a || false || b; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = (#0 - #3), #3
} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:20 = 0, 0
// CHECK: Branch,File 0, [[@LINE-2]]:24 -> [[@LINE-2]]:25 = (#1 - #2), #2
// CHECK-LABEL: _Z5for_4bb:
-bool for_4(bool a, bool b) {
+bool for_4(bool a, bool b) {// MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:24 = M:0, C:3
return a || b || true; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = (#0 - #3), #3
} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:16 = (#3 - #4), #4
// CHECK: Branch,File 0, [[@LINE-2]]:20 -> [[@LINE-2]]:24 = 0, 0
// CHECK-LABEL: _Z5for_5b:
-bool for_5(bool a) {
+bool for_5(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:23 = M:0, C:2
return true || false; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = 0, 0
} // CHECK: Branch,File 0, [[@LINE-1]]:18 -> [[@LINE-1]]:23 = 0, 0
// CHECK-LABEL: _Z5for_6b:
-bool for_6(bool a) {
+bool for_6(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:20 = M:0, C:2
return false || a; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, 0
} // CHECK: Branch,File 0, [[@LINE-1]]:19 -> [[@LINE-1]]:20 = (#1 - #2), #2
// CHECK-LABEL: _Z5for_7b:
-bool for_7(bool a) {
+bool for_7(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:19 = M:0, C:2
return a || true; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = (#0 - #1), #1
} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:19 = 0, 0
+// CHECK-LABEL: _Z5for_8b:
+bool for_8(bool a) { // MCDC: Decision,File 0, [[@LINE+3]]:17 -> [[@LINE+3]]:30 = M:0, C:2
+ // CHECK: Branch,File 0, [[@LINE+2]]:17 -> [[@LINE+2]]:21 = 0, 0
+ // CHECK: Branch,File 0, [[@LINE+1]]:25 -> [[@LINE+1]]:30 = 0, 0
+ if constexpr (true && false)
+ return true;
+ else
+ return false;
+}
diff --git a/clang/test/CoverageMapping/branch-mincounters.cpp b/clang/test/CoverageMapping/branch-mincounters.cpp
index 6d4341cbeafd6e..aa1d3928aae254 100644
--- a/clang/test/CoverageMapping/branch-mincounters.cpp
+++ b/clang/test/CoverageMapping/branch-mincounters.cpp
@@ -2,6 +2,7 @@
// logical operators on branch conditions for branch coverage.
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name branch-logical-mixed.cpp %s | FileCheck %s
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 -fcoverage-mcdc -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name branch-logical-mixed.cpp %s | FileCheck %s
// CHECK-LABEL: _Z5func1ii:
diff --git a/clang/test/CoverageMapping/branch-templates.cpp b/clang/test/CoverageMapping/branch-templates.cpp
index 1fd01218c9038a..4e43626b8192d5 100644
--- a/clang/test/CoverageMapping/branch-templates.cpp
+++ b/clang/test/CoverageMapping/branch-templates.cpp
@@ -2,6 +2,7 @@
// instantiations.
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name branch-templates.cpp %s | FileCheck %s
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 -fcoverage-mcdc -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name branch-templates.cpp %s | FileCheck %s
template<typename T>
void unused(T x) {
diff --git a/clang/test/CoverageMapping/if.cpp b/clang/test/CoverageMapping/if.cpp
index 4326add3b34434..65e3d62df79db4 100644
--- a/clang/test/CoverageMapping/if.cpp
+++ b/clang/test/CoverageMapping/if.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fms-extensions -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -std=c++23 -triple %itanium_abi_triple -main-file-name if.cpp %s | FileCheck %s
+// RUN: %clang_cc1 -fms-extensions -mllvm -emptyline-comment-coverage=false -fcoverage-mcdc -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -std=c++2b -triple %itanium_abi_triple -main-file-name if.cpp %s | FileCheck %s
int nop() { return 0; }
struct S {
diff --git a/clang/test/CoverageMapping/logical.cpp b/clang/test/CoverageMapping/logical.cpp
index cd1a388ec4c29f..7de59e1429808a 100644
--- a/clang/test/CoverageMapping/logical.cpp
+++ b/clang/test/CoverageMapping/logical.cpp
@@ -1,22 +1,24 @@
// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name logical.cpp %s | FileCheck %s
+// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -fcoverage-mcdc -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name logical.cpp %s | FileCheck %s -check-prefix=MCDC
-int main() { // CHECK: File 0, [[@LINE]]:12 -> [[@LINE+22]]:2 = #0
+int main() { // CHECK: File 0, [[@LINE]]:12 -> [[@LINE+23]]:2 = #0
bool bt = true;
- bool bf = false;
+ bool bf = false; // MCDC: Decision,File 0, [[@LINE+1]]:12 -> [[@LINE+1]]:20 = M:0, C:2
bool a = bt && bf; // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE]]:14 = #0
// CHECK-NEXT: Branch,File 0, [[@LINE-1]]:12 -> [[@LINE-1]]:14 = #1, (#0 - #1)
// CHECK-NEXT: File 0, [[@LINE-2]]:18 -> [[@LINE-2]]:20 = #1
// CHECK-NEXT: Branch,File 0, [[@LINE-3]]:18 -> [[@LINE-3]]:20 = #2, (#1 - #2)
-
+ // MCDC: Decision,File 0, [[@LINE+1]]:7 -> [[@LINE+2]]:9 = M:1, C:2
a = bt && // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:9 = #0
bf; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:9 = #3, (#0 - #3)
// CHECK-NEXT: File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:9 = #3
// CHECK-NEXT: Branch,File 0, [[@LINE-2]]:7 -> [[@LINE-2]]:9 = #4, (#3 - #4)
+ // MCDC: Decision,File 0, [[@LINE+1]]:7 -> [[@LINE+1]]:15 = M:2, C:2
a = bf || bt; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:9 = #0
// CHECK-NEXT: Branch,File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:9 = (#0 - #5), #5
// CHECK-NEXT: File 0, [[@LINE-2]]:13 -> [[@LINE-2]]:15 = #5
// CHECK-NEXT: Branch,File 0, [[@LINE-3]]:13 -> [[@LINE-3]]:15 = (#5 - #6), #6
-
+ // MCDC: Decision,File 0, [[@LINE+1]]:7 -> [[@LINE+2]]:9 = M:3, C:2
a = bf || // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:9 = #0
bt; // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:9 = (#0 - #7), #7
// CHECK-NEXT: File 0, [[@LINE-1]]:7 -> [[@LINE-1]]:9 = #7
diff --git a/clang/test/CoverageMapping/mcdc-class.cpp b/clang/test/CoverageMapping/mcdc-class.cpp
new file mode 100644
index 00000000000000..dcf6123ee0fc74
--- /dev/null
+++ b/clang/test/CoverageMapping/mcdc-class.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 -fcoverage-mcdc -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only %s | FileCheck %s
+
+extern void foo();
+extern void bar();
+class Value {
+ public:
+ void setValue( int len );
+ int getValue( void );
+ Value(); // This is the constructor declaration
+ ~Value(); // This is the destructor declaration
+
+ private:
+ int value;
+};
+
+// Member functions definitions including constructor
+Value::Value(void) {
+ if (value == 2 || value == 6)
+ foo();
+}
+Value::~Value(void) {
+ if (value == 2 || value == 3)
+ bar();
+}
+
+// CHECK-LABEL: Decision,File 0, 18:7 -> 18:31 = M:0, C:2
+// CHECK-NEXT: Branch,File 0, 18:7 -> 18:17 = (#0 - #2), #2 [1,0,2]
+// CHECK: Branch,File 0, 18:21 -> 18:31 = (#2 - #3), #3 [2,0,0]
+// CHECK-LABEL: Decision,File 0, 22:7 -> 22:31 = M:0, C:2
+// CHECK-NEXT: Branch,File 0, 22:7 -> 22:17 = (#0 - #2), #2 [1,0,2]
+// CHECK: Branch,File 0, 22:21 -> 22:31 = (#2 - #3), #3 [2,0,0]
diff --git a/clang/test/CoverageMapping/mcdc-error-conditions.cpp b/clang/test/CoverageMapping/mcdc-error-conditions.cpp
new file mode 100644
index 00000000000000..d34ed693434795
--- /dev/null
+++ b/clang/test/CoverageMapping/mcdc-error-conditions.cpp
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 -fcoverage-mcdc -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only %s 2>&1| FileCheck %s
+
+bool func_conditions(bool a, bool b, bool c, bool d, bool e, bool f, bool g) {
+ return a && b && c && d && e && f && g;
+}
+
+// CHECK: warning: unsupported MC/DC boolean expression; number of conditions{{.*}} exceeds max
diff --git a/clang/test/CoverageMapping/mcdc-error-nests.cpp b/clang/test/CoverageMapping/mcdc-error-nests.cpp
new file mode 100644
index 00000000000000..3add2b9ccd3fb3
--- /dev/null
+++ b/clang/test/CoverageMapping/mcdc-error-nests.cpp
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 -fcoverage-mcdc -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only %s 2>&1| FileCheck %s
+
+// "Split-nest" -- boolean expressions within boolean expressions.
+extern bool bar(bool);
+bool func_split_nest(bool a, bool b, bool c, bool d, bool e, bool f, bool g) {
+ bool res = a && b && c && bar(d && e) && f && g;
+ return bar(res);
+}
+
+// CHECK: warning: unsupported MC/DC boolean expression; contains an operation with a nested boolean expression.
diff --git a/clang/test/CoverageMapping/mcdc-logical-scalar-ids.cpp b/clang/test/CoverageMapping/mcdc-logical-scalar-ids.cpp
new file mode 100644
index 00000000000000..c820b5df5ad3a9
--- /dev/null
+++ b/clang/test/CoverageMapping/mcdc-logical-scalar-ids.cpp
@@ -0,0 +1,110 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 -fcoverage-mcdc -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only %s | FileCheck %s
+
+extern bool bar (bool, bool, bool, bool, bool);
+bool func_scalar_and(bool a, bool b, bool c, bool d, bool e, bool f) {
+ bool res1 = a && b;
+ bool res2 = a && b && c;
+ bool res3 = a && b && c && d;
+ bool res4 = a && b && c && d && e;
+ bool res5 = a && b && c && d && e && f;
+ return bar(res1, res2, res3, res4, res5);
+}
+
+// CHECK-LABEL: Decision,File 0, 5:17 -> 5:23 = M:0, C:2
+// CHECK-NEXT: Branch,File 0, 5:17 -> 5:18 = #1, (#0 - #1) [1,2,0]
+// CHECK: Branch,File 0, 5:22 -> 5:23 = #2, (#1 - #2) [2,0,0]
+// CHECK-LABEL: Decision,File 0, 6:17 -> 6:28 = M:1, C:3
+// CHECK-NEXT: Branch,File 0, 6:17 -> 6:18 = #5, (#0 - #5) [1,3,0]
+// CHECK: Branch,File 0, 6:22 -> 6:23 = #6, (#5 - #6) [3,2,0]
+// CHECK: Branch,File 0, 6:27 -> 6:28 = #4, (#3 - #4) [2,0,0]
+// CHECK-LABEL: Decision,File 0, 7:17 -> 7:33 = M:2, C:4
+// CHECK-NEXT: Branch,File 0, 7:17 -> 7:18 = #11, (#0 - #11) [1,4,0]
+// CHECK: Branch,File 0, 7:22 -> 7:23 = #12, (#11 - #12) [4,3,0]
+// CHECK: Branch,File 0, 7:27 -> 7:28 = #10, (#9 - #10) [3,2,0]
+// CHECK: Branch,File 0, 7:32 -> 7:33 = #8, (#7 - #8) [2,0,0]
+// CHECK-LABEL: Decision,File 0, 8:17 -> 8:38 = M:4, C:5
+// CHECK-NEXT: Branch,File 0, 8:17 -> 8:18 = #19, (#0 - #19) [1,5,0]
+// CHECK: Branch,File 0, 8:22 -> 8:23 = #20, (#19 - #20) [5,4,0]
+// CHECK: Branch,File 0, 8:27 -> 8:28 = #18, (#17 - #18) [4,3,0]
+// CHECK: Branch,File 0, 8:32 -> 8:33 = #16, (#15 - #16) [3,2,0]
+// CHECK: Branch,File 0, 8:37 -> 8:38 = #14, (#13 - #14) [2,0,0]
+// CHECK-LABEL: Decision,File 0, 9:17 -> 9:43 = M:8, C:6
+// CHECK-NEXT: Branch,File 0, 9:17 -> 9:18 = #29, (#0 - #29) [1,6,0]
+// CHECK: Branch,File 0, 9:22 -> 9:23 = #30, (#29 - #30) [6,5,0]
+// CHECK: Branch,File 0, 9:27 -> 9:28 = #28, (#27 - #28) [5,4,0]
+// CHECK: Branch,File 0, 9:32 -> 9:33 = #26, (#25 - #26) [4,3,0]
+// CHECK: Branch,File 0, 9:37 -> 9:38 = #24, (#23 - #24) [3,2,0]
+// CHECK: Branch,File 0, 9:42 -> 9:43 = #22, (#21 - #22) [2,0,0]
+
+bool func_scalar_or(bool a, bool b, bool c, bool d, bool e, bool f) {
+ bool res1 = a || b;
+ bool res2 = a || b || c;
+ bool res3 = a || b || c || d;
+ bool res4 = a || b || c || d || e;
+ bool res5 = a || b || c || d || e || f;
+ return bar(res1, res2, res3, res4, res5);
+}
+
+// CHECK-LABEL: Decision,File 0, 40:17 -> 40:23 = M:0, C:2
+// CHECK-NEXT: Branch,File 0, 40:17 -> 40:18 = (#0 - #1), #1 [1,0,2]
+// CHECK: Branch,File 0, 40:22 -> 40:23 = (#1 - #2), #2 [2,0,0]
+// CHECK-LABEL: Decision,File 0, 41:17 -> 41:28 = M:1, C:3
+// CHECK-NEXT: Branch,File 0, 41:17 -> 41:18 = (#0 - #5), #5 [1,0,3]
+// CHECK: Branch,File 0, 41:22 -> 41:23 = (#5 - #6), #6 [3,0,2]
+// CHECK: Branch,File 0, 41:27 -> 41:28 = (#3 - #4), #4 [2,0,0]
+// CHECK-LABEL: Decision,File 0, 42:17 -> 42:33 = M:2, C:4
+// CHECK-NEXT: Branch,File 0, 42:17 -> 42:18 = (#0 - #11), #11 [1,0,4]
+// CHECK: Branch,File 0, 42:22 -> 42:23 = (#11 - #12), #12 [4,0,3]
+// CHECK: Branch,File 0, 42:27 -> 42:28 = (#9 - #10), #10 [3,0,2]
+// CHECK: Branch,File 0, 42:32 -> 42:33 = (#7 - #8), #8 [2,0,0]
+// CHECK-LABEL: Decision,File 0, 43:17 -> 43:38 = M:4, C:5
+// CHECK-NEXT: Branch,File 0, 43:17 -> 43:18 = (#0 - #19), #19 [1,0,5]
+// CHECK: Branch,File 0, 43:22 -> 43:23 = (#19 - #20), #20 [5,0,4]
+// CHECK: Branch,File 0, 43:27 -> 43:28 = (#17 - #18), #18 [4,0,3]
+// CHECK: Branch,File 0, 43:32 -> 43:33 = (#15 - #16), #16 [3,0,2]
+// CHECK: Branch,File 0, 43:37 -> 43:38 = (#13 - #14), #14 [2,0,0]
+// CHECK-LABEL: Decision,File 0, 44:17 -> 44:43 = M:8, C:6
+// CHECK-NEXT: Branch,File 0, 44:17 -> 44:18 = (#0 - #29), #29 [1,0,6]
+// CHECK: Branch,File 0, 44:22 -> 44:23 = (#29 - #30), #30 [6,0,5]
+// CHECK: Branch,File 0, 44:27 -> 44:28 = (#27 - #28), #28 [5,0,4]
+// CHECK: Branch,File 0, 44:32 -> 44:33 = (#25 - #26), #26 [4,0,3]
+// CHECK: Branch,File 0, 44:37 -> 44:38 = (#23 - #24), #24 [3,0,2]
+// CHECK: Branch,File 0, 44:42 -> 44:43 = (#21 - #22), #22 [2,0,0]
+
+
+bool func_scalar_mix(bool a, bool b, bool c, bool d, bool e, bool f) {
+ bool res1 = a || b;
+ bool res2 = a && (b || c);
+ bool res3 = (a || b) && (c || d);
+ bool res4 = a && (b || c) && (d || e);
+ bool res5 = (a || b) && (c || d) && (e || f);
+ return bar(res1, res2, res3, res4, res5);
+}
+
+// CHECK-LABEL: Decision,File 0, 76:17 -> 76:23 = M:0, C:2
+// CHECK-NEXT: Branch,File 0, 76:17 -> 76:18 = (#0 - #1), #1 [1,0,2]
+// CHECK: Branch,File 0, 76:22 -> 76:23 = (#1 - #2), #2 [2,0,0]
+// CHECK-LABEL: Decision,File 0, 77:17 -> 77:30 = M:1, C:3
+// CHECK-NEXT: Branch,File 0, 77:17 -> 77:18 = #3, (#0 - #3) [1,2,0]
+// CHECK: Branch,File 0, 77:23 -> 77:24 = (#3 - #4), #4 [2,0,3]
+// CHECK: Branch,File 0, 77:28 -> 77:29 = (#4 - #5), #5 [3,0,0]
+// CHECK-LABEL: Decision,File 0, 78:17 -> 78:37 = M:2, C:4
+// CHECK-NEXT: File 0
+// CHECK-NEXT: Branch,File 0, 78:18 -> 78:19 = (#0 - #7), #7 [1,2,3]
+// CHECK: Branch,File 0, 78:23 -> 78:24 = (#7 - #8), #8 [3,2,0]
+// CHECK: Branch,File 0, 78:30 -> 78:31 = (#6 - #9), #9 [2,0,4]
+// CHECK: Branch,File 0, 78:35 -> 78:36 = (#9 - #10), #10 [4,0,0]
+// CHECK-LABEL: Decision,File 0, 79:17 -> 79:42 = M:4, C:5
+// CHECK-NEXT: Branch,File 0, 79:17 -> 79:18 = #12, (#0 - #12) [1,3,0]
+// CHECK: Branch,File 0, 79:23 -> 79:24 = (#12 - #13), #13 [3,2,4]
+// CHECK: Branch,File 0, 79:28 -> 79:29 = (#13 - #14), #14 [4,2,0]
+// CHECK: Branch,File 0, 79:35 -> 79:36 = (#11 - #15), #15 [2,0,5]
+// CHECK: Branch,File 0, 79:40 -> 79:41 = (#15 - #16), #16 [5,0,0]
+// CHECK-LABEL: Decision,File 0, 80:17 -> 80:49 = M:8, C:6
+// CHECK-NEXT: File 0
+// CHECK-NEXT: Branch,File 0, 80:18 -> 80:19 = (#0 - #19), #19 [1,3,4]
+// CHECK: Branch,File 0, 80:23 -> 80:24 = (#19 - #20), #20 [4,3,0]
+// CHECK: Branch,File 0, 80:30 -> 80:31 = (#18 - #21), #21 [3,2,5]
+// CHECK: Branch,File 0, 80:35 -> 80:36 = (#21 - #22), #22 [5,2,0]
+// CHECK: Branch,File 0, 80:42 -> 80:43 = (#17 - #23), #23 [2,0,6]
+// CHECK: Branch,File 0, 80:47 -> 80:48 = (#23 - #24), #24 [6,0,0]
diff --git a/clang/test/CoverageMapping/mcdc-logical-stmt-ids-all.cpp b/clang/test/CoverageMapping/mcdc-logical-stmt-ids-all.cpp
new file mode 100644
index 00000000000000..5b13cc3507e96c
--- /dev/null
+++ b/clang/test/CoverageMapping/mcdc-logical-stmt-ids-all.cpp
@@ -0,0 +1,131 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 -fcoverage-mcdc -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only %s | FileCheck %s
+
+bool func_if_and(bool a, bool b, bool c, bool d, bool e, bool f) {
+ if (a && b && c && d && e && f)
+ return true;
+ return false;
+}
+
+// CHECK-LABEL: Decision,File 0, 4:7 -> 4:33 = M:0, C:6
+// CHECK-NEXT: Branch,File 0, 4:7 -> 4:8 = #10, (#0 - #10) [1,6,0]
+// CHECK: Branch,File 0, 4:12 -> 4:13 = #11, (#10 - #11) [6,5,0]
+// CHECK: Branch,File 0, 4:17 -> 4:18 = #9, (#8 - #9) [5,4,0]
+// CHECK: Branch,File 0, 4:22 -> 4:23 = #7, (#6 - #7) [4,3,0]
+// CHECK: Branch,File 0, 4:27 -> 4:28 = #5, (#4 - #5) [3,2,0]
+// CHECK: Branch,File 0, 4:32 -> 4:33 = #3, (#2 - #3) [2,0,0]
+
+bool func_if_or(bool a, bool b, bool c, bool d, bool e, bool f) {
+ if (a || b || c || d || e || f)
+ return true;
+ return false;
+}
+
+// CHECK-LABEL: Decision,File 0, 18:7 -> 18:33 = M:0, C:6
+// CHECK-NEXT: Branch,File 0, 18:7 -> 18:8 = (#0 - #10), #10 [1,0,6]
+// CHECK: Branch,File 0, 18:12 -> 18:13 = (#10 - #11), #11 [6,0,5]
+// CHECK: Branch,File 0, 18:17 -> 18:18 = (#8 - #9), #9 [5,0,4]
+// CHECK: Branch,File 0, 18:22 -> 18:23 = (#6 - #7), #7 [4,0,3]
+// CHECK: Branch,File 0, 18:27 -> 18:28 = (#4 - #5), #5 [3,0,2]
+// CHECK: Branch,File 0, 18:32 -> 18:33 = (#2 - #3), #3 [2,0,0]
+
+bool func_while_and(bool a, bool b, bool c, bool d, bool e, bool f) {
+ while (a && b && c && d && e && f) { return true; }
+ return false;
+}
+
+// CHECK-LABEL: Decision,File 0, 32:10 -> 32:36 = M:0, C:6
+// CHECK-NEXT: Branch,File 0, 32:10 -> 32:11 = #10, (#0 - #10) [1,6,0]
+// CHECK: Branch,File 0, 32:15 -> 32:16 = #11, (#10 - #11) [6,5,0]
+// CHECK: Branch,File 0, 32:20 -> 32:21 = #9, (#8 - #9) [5,4,0]
+// CHECK: Branch,File 0, 32:25 -> 32:26 = #7, (#6 - #7) [4,3,0]
+// CHECK: Branch,File 0, 32:30 -> 32:31 = #5, (#4 - #5) [3,2,0]
+// CHECK: Branch,File 0, 32:35 -> 32:36 = #3, (#2 - #3) [2,0,0]
+
+bool func_while_or(bool a, bool b, bool c, bool d, bool e, bool f) {
+ while (a || b || c || d || e || f) { return true; }
+ return false;
+}
+
+// CHECK-LABEL: Decision,File 0, 45:10 -> 45:36 = M:0, C:6
+// CHECK-NEXT: Branch,File 0, 45:10 -> 45:11 = (#0 - #10), #10 [1,0,6]
+// CHECK: Branch,File 0, 45:15 -> 45:16 = (#10 - #11), #11 [6,0,5]
+// CHECK: Branch,File 0, 45:20 -> 45:21 = (#8 - #9), #9 [5,0,4]
+// CHECK: Branch,File 0, 45:25 -> 45:26 = (#6 - #7), #7 [4,0,3]
+// CHECK: Branch,File 0, 45:30 -> 45:31 = (#4 - #5), #5 [3,0,2]
+// CHECK: Branch,File 0, 45:35 -> 45:36 = (#2 - #3), #3 [2,0,0]
+
+bool func_for_and(bool a, bool b, bool c, bool d, bool e, bool f) {
+ for (;a && b && c && d && e && f;) { return true; }
+ return false;
+}
+
+// CHECK-LABEL: Decision,File 0, 58:9 -> 58:35 = M:0, C:6
+// CHECK-NEXT: Branch,File 0, 58:9 -> 58:10 = #10, (#0 - #10) [1,6,0]
+// CHECK: Branch,File 0, 58:14 -> 58:15 = #11, (#10 - #11) [6,5,0]
+// CHECK: Branch,File 0, 58:19 -> 58:20 = #9, (#8 - #9) [5,4,0]
+// CHECK: Branch,File 0, 58:24 -> 58:25 = #7, (#6 - #7) [4,3,0]
+// CHECK: Branch,File 0, 58:29 -> 58:30 = #5, (#4 - #5) [3,2,0]
+// CHECK: Branch,File 0, 58:34 -> 58:35 = #3, (#2 - #3) [2,0,0]
+
+bool func_for_or(bool a, bool b, bool c, bool d, bool e, bool f) {
+ for (;a || b || c || d || e || f;) { return true; }
+ return false;
+}
+
+// CHECK-LABEL: Decision,File 0, 71:9 -> 71:35 = M:0, C:6
+// CHECK-NEXT: Branch,File 0, 71:9 -> 71:10 = (#0 - #10), #10 [1,0,6]
+// CHECK: Branch,File 0, 71:14 -> 71:15 = (#10 - #11), #11 [6,0,5]
+// CHECK: Branch,File 0, 71:19 -> 71:20 = (#8 - #9), #9 [5,0,4]
+// CHECK: Branch,File 0, 71:24 -> 71:25 = (#6 - #7), #7 [4,0,3]
+// CHECK: Branch,File 0, 71:29 -> 71:30 = (#4 - #5), #5 [3,0,2]
+// CHECK: Branch,File 0, 71:34 -> 71:35 = (#2 - #3), #3 [2,0,0]
+
+bool func_do_and(bool a, bool b, bool c, bool d, bool e, bool f) {
+ do {} while (a && b && c && d && e && f);
+ return false;
+}
+
+// CHECK-LABEL: Decision,File 0, 84:16 -> 84:42 = M:0, C:6
+// CHECK-NEXT: Branch,File 0, 84:16 -> 84:17 = #10, ((#0 + #1) - #10) [1,6,0]
+// CHECK: Branch,File 0, 84:21 -> 84:22 = #11, (#10 - #11) [6,5,0]
+// CHECK: Branch,File 0, 84:26 -> 84:27 = #9, (#8 - #9) [5,4,0]
+// CHECK: Branch,File 0, 84:31 -> 84:32 = #7, (#6 - #7) [4,3,0]
+// CHECK: Branch,File 0, 84:36 -> 84:37 = #5, (#4 - #5) [3,2,0]
+// CHECK: Branch,File 0, 84:41 -> 84:42 = #3, (#2 - #3) [2,0,0]
+
+bool func_do_or(bool a, bool b, bool c, bool d, bool e, bool f) {
+ do {} while (a || b || c || d || e || f);
+ return false;
+}
+
+// CHECK-LABEL: Decision,File 0, 97:16 -> 97:42 = M:0, C:6
+// CHECK-NEXT: Branch,File 0, 97:16 -> 97:17 = ((#0 + #1) - #10), #10 [1,0,6]
+// CHECK: Branch,File 0, 97:21 -> 97:22 = (#10 - #11), #11 [6,0,5]
+// CHECK: Branch,File 0, 97:26 -> 97:27 = (#8 - #9), #9 [5,0,4]
+// CHECK: Branch,File 0, 97:31 -> 97:32 = (#6 - #7), #7 [4,0,3]
+// CHECK: Branch,File 0, 97:36 -> 97:37 = (#4 - #5), #5 [3,0,2]
+// CHECK: Branch,File 0, 97:41 -> 97:42 = (#2 - #3), #3 [2,0,0]
+
+bool func_ternary_and(bool a, bool b, bool c, bool d, bool e, bool f) {
+ return (a && b && c && d && e && f) ? true : false;
+}
+
+// CHECK-LABEL: Decision,File 0, 110:11 -> 110:37 = M:0, C:6
+// CHECK-NEXT: Branch,File 0, 110:11 -> 110:12 = #10, (#0 - #10) [1,6,0]
+// CHECK: Branch,File 0, 110:16 -> 110:17 = #11, (#10 - #11) [6,5,0]
+// CHECK: Branch,File 0, 110:21 -> 110:22 = #9, (#8 - #9) [5,4,0]
+// CHECK: Branch,File 0, 110:26 -> 110:27 = #7, (#6 - #7) [4,3,0]
+// CHECK: Branch,File 0, 110:31 -> 110:32 = #5, (#4 - #5) [3,2,0]
+// CHECK: Branch,File 0, 110:36 -> 110:37 = #3, (#2 - #3) [2,0,0]
+
+bool func_ternary_or(bool a, bool b, bool c, bool d, bool e, bool f) {
+ return (a || b || c || d || e || f) ? true : false;
+}
+
+// CHECK-LABEL: Decision,File 0, 122:11 -> 122:37 = M:0, C:6
+// CHECK-NEXT: Branch,File 0, 122:11 -> 122:12 = (#0 - #10), #10 [1,0,6]
+// CHECK: Branch,File 0, 122:16 -> 122:17 = (#10 - #11), #11 [6,0,5]
+// CHECK: Branch,File 0, 122:21 -> 122:22 = (#8 - #9), #9 [5,0,4]
+// CHECK: Branch,File 0, 122:26 -> 122:27 = (#6 - #7), #7 [4,0,3]
+// CHECK: Branch,File 0, 122:31 -> 122:32 = (#4 - #5), #5 [3,0,2]
+// CHECK: Branch,File 0, 122:36 -> 122:37 = (#2 - #3), #3 [2,0,0]
diff --git a/clang/test/CoverageMapping/mcdc-logical-stmt-ids.cpp b/clang/test/CoverageMapping/mcdc-logical-stmt-ids.cpp
new file mode 100644
index 00000000000000..99854ec27a3fbd
--- /dev/null
+++ b/clang/test/CoverageMapping/mcdc-logical-stmt-ids.cpp
@@ -0,0 +1,111 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 -fcoverage-mcdc -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only %s | FileCheck %s
+
+bool func_if_and(bool a, bool b, bool c, bool d, bool e, bool f) {
+ if (a && b)
+ if (a && b && c)
+ if (a && b && c && d)
+ if (a && b && c && d && e)
+ if (a && b && c && d && e && f)
+ return true;
+ return false;
+}
+
+// CHECK-LABEL: Decision,File 0, 4:7 -> 4:13 = M:0, C:2
+// CHECK-NEXT: Branch,File 0, 4:7 -> 4:8 = #2, (#0 - #2) [1,2,0]
+// CHECK: Branch,File 0, 4:12 -> 4:13 = #3, (#2 - #3) [2,0,0]
+// CHECK-LABEL: Decision,File 0, 5:9 -> 5:20 = M:1, C:3
+// CHECK-NEXT: Branch,File 0, 5:9 -> 5:10 = #7, (#1 - #7) [1,3,0]
+// CHECK: Branch,File 0, 5:14 -> 5:15 = #8, (#7 - #8) [3,2,0]
+// CHECK: Branch,File 0, 5:19 -> 5:20 = #6, (#5 - #6) [2,0,0]
+// CHECK-LABEL: Decision,File 0, 6:11 -> 6:27 = M:2, C:4
+// CHECK-NEXT: Branch,File 0, 6:11 -> 6:12 = #14, (#4 - #14) [1,4,0]
+// CHECK: Branch,File 0, 6:16 -> 6:17 = #15, (#14 - #15) [4,3,0]
+// CHECK: Branch,File 0, 6:21 -> 6:22 = #13, (#12 - #13) [3,2,0]
+// CHECK: Branch,File 0, 6:26 -> 6:27 = #11, (#10 - #11) [2,0,0]
+// CHECK-LABEL: Decision,File 0, 7:13 -> 7:34 = M:4, C:5
+// CHECK-NEXT: Branch,File 0, 7:13 -> 7:14 = #23, (#9 - #23) [1,5,0]
+// CHECK: Branch,File 0, 7:18 -> 7:19 = #24, (#23 - #24) [5,4,0]
+// CHECK: Branch,File 0, 7:23 -> 7:24 = #22, (#21 - #22) [4,3,0]
+// CHECK: Branch,File 0, 7:28 -> 7:29 = #20, (#19 - #20) [3,2,0]
+// CHECK: Branch,File 0, 7:33 -> 7:34 = #18, (#17 - #18) [2,0,0]
+// CHECK-LABEL: Decision,File 0, 8:16 -> 8:42 = M:8, C:6
+// CHECK-NEXT: Branch,File 0, 8:16 -> 8:17 = #34, (#16 - #34) [1,6,0]
+// CHECK: Branch,File 0, 8:21 -> 8:22 = #35, (#34 - #35) [6,5,0]
+// CHECK: Branch,File 0, 8:26 -> 8:27 = #33, (#32 - #33) [5,4,0]
+// CHECK: Branch,File 0, 8:31 -> 8:32 = #31, (#30 - #31) [4,3,0]
+// CHECK: Branch,File 0, 8:36 -> 8:37 = #29, (#28 - #29) [3,2,0]
+// CHECK: Branch,File 0, 8:41 -> 8:42 = #27, (#26 - #27) [2,0,0]
+
+bool func_if_or(bool a, bool b, bool c, bool d, bool e, bool f) {
+ if (a || b)
+ if (a || b || c)
+ if (a || b || c || d)
+ if (a || b || c || d || e)
+ if (a || b || c || d || e || f)
+ return true;
+ return false;
+}
+
+// CHECK-LABEL: Decision,File 0, 40:7 -> 40:13 = M:0, C:2
+// CHECK-NEXT: Branch,File 0, 40:7 -> 40:8 = (#0 - #2), #2 [1,0,2]
+// CHECK: Branch,File 0, 40:12 -> 40:13 = (#2 - #3), #3 [2,0,0]
+// CHECK-LABEL: Decision,File 0, 41:9 -> 41:20 = M:1, C:3
+// CHECK-NEXT: Branch,File 0, 41:9 -> 41:10 = (#1 - #7), #7 [1,0,3]
+// CHECK: Branch,File 0, 41:14 -> 41:15 = (#7 - #8), #8 [3,0,2]
+// CHECK: Branch,File 0, 41:19 -> 41:20 = (#5 - #6), #6 [2,0,0]
+// CHECK-LABEL: Decision,File 0, 42:11 -> 42:27 = M:2, C:4
+// CHECK-NEXT: Branch,File 0, 42:11 -> 42:12 = (#4 - #14), #14 [1,0,4]
+// CHECK: Branch,File 0, 42:16 -> 42:17 = (#14 - #15), #15 [4,0,3]
+// CHECK: Branch,File 0, 42:21 -> 42:22 = (#12 - #13), #13 [3,0,2]
+// CHECK: Branch,File 0, 42:26 -> 42:27 = (#10 - #11), #11 [2,0,0]
+// CHECK-LABEL: Decision,File 0, 43:13 -> 43:34 = M:4, C:5
+// CHECK-NEXT: Branch,File 0, 43:13 -> 43:14 = (#9 - #23), #23 [1,0,5]
+// CHECK: Branch,File 0, 43:18 -> 43:19 = (#23 - #24), #24 [5,0,4]
+// CHECK: Branch,File 0, 43:23 -> 43:24 = (#21 - #22), #22 [4,0,3]
+// CHECK: Branch,File 0, 43:28 -> 43:29 = (#19 - #20), #20 [3,0,2]
+// CHECK: Branch,File 0, 43:33 -> 43:34 = (#17 - #18), #18 [2,0,0]
+// CHECK-LABEL: Decision,File 0, 44:16 -> 44:42 = M:8, C:6
+// CHECK-NEXT: Branch,File 0, 44:16 -> 44:17 = (#16 - #34), #34 [1,0,6]
+// CHECK: Branch,File 0, 44:21 -> 44:22 = (#34 - #35), #35 [6,0,5]
+// CHECK: Branch,File 0, 44:26 -> 44:27 = (#32 - #33), #33 [5,0,4]
+// CHECK: Branch,File 0, 44:31 -> 44:32 = (#30 - #31), #31 [4,0,3]
+// CHECK: Branch,File 0, 44:36 -> 44:37 = (#28 - #29), #29 [3,0,2]
+// CHECK: Branch,File 0, 44:41 -> 44:42 = (#26 - #27), #27 [2,0,0]
+
+bool func_if_mix(bool a, bool b, bool c, bool d, bool e, bool f) {
+ if (a || b)
+ if (a && (b || c))
+ if ((a || b) && (c || d))
+ if (a && (b || c) && (d || e))
+ if ((a || b) && (c || d) && (e || f))
+ return true;
+ return false;
+}
+
+// CHECK-LABEL: Decision,File 0, 76:7 -> 76:13 = M:0, C:2
+// CHECK-NEXT: Branch,File 0, 76:7 -> 76:8 = (#0 - #2), #2 [1,0,2]
+// CHECK: Branch,File 0, 76:12 -> 76:13 = (#2 - #3), #3 [2,0,0]
+// CHECK-LABEL: Decision,File 0, 77:9 -> 77:22 = M:1, C:3
+// CHECK-NEXT: Branch,File 0, 77:9 -> 77:10 = #5, (#1 - #5) [1,2,0]
+// CHECK: Branch,File 0, 77:15 -> 77:16 = (#5 - #6), #6 [2,0,3]
+// CHECK: Branch,File 0, 77:20 -> 77:21 = (#6 - #7), #7 [3,0,0]
+// CHECK-LABEL: Decision,File 0, 78:11 -> 78:31 = M:2, C:4
+// CHECK-NEXT: File 0
+// CHECK-NEXT: Branch,File 0, 78:12 -> 78:13 = (#4 - #10), #10 [1,2,3]
+// CHECK: Branch,File 0, 78:17 -> 78:18 = (#10 - #11), #11 [3,2,0]
+// CHECK: Branch,File 0, 78:24 -> 78:25 = (#9 - #12), #12 [2,0,4]
+// CHECK: Branch,File 0, 78:29 -> 78:30 = (#12 - #13), #13 [4,0,0]
+// CHECK-LABEL: Decision,File 0, 79:13 -> 79:38 = M:4, C:5
+// CHECK-NEXT: Branch,File 0, 79:13 -> 79:14 = #16, (#8 - #16) [1,3,0]
+// CHECK: Branch,File 0, 79:19 -> 79:20 = (#16 - #17), #17 [3,2,4]
+// CHECK: Branch,File 0, 79:24 -> 79:25 = (#17 - #18), #18 [4,2,0]
+// CHECK: Branch,File 0, 79:31 -> 79:32 = (#15 - #19), #19 [2,0,5]
+// CHECK: Branch,File 0, 79:36 -> 79:37 = (#19 - #20), #20 [5,0,0]
+// CHECK-LABEL: Decision,File 0, 80:15 -> 80:47 = M:8, C:6
+// CHECK-NEXT: File 0
+// CHECK-NEXT: Branch,File 0, 80:16 -> 80:17 = (#14 - #24), #24 [1,3,4]
+// CHECK: Branch,File 0, 80:21 -> 80:22 = (#24 - #25), #25 [4,3,0]
+// CHECK: Branch,File 0, 80:28 -> 80:29 = (#23 - #26), #26 [3,2,5]
+// CHECK: Branch,File 0, 80:33 -> 80:34 = (#26 - #27), #27 [5,2,0]
+// CHECK: Branch,File 0, 80:40 -> 80:41 = (#22 - #28), #28 [2,0,6]
+// CHECK: Branch,File 0, 80:45 -> 80:46 = (#28 - #29), #29 [6,0,0]
diff --git a/clang/test/Profile/c-linkage-available_externally.c b/clang/test/Profile/c-linkage-available_externally.c
index f6375e15f451ab..010f191dc5e1b1 100644
--- a/clang/test/Profile/c-linkage-available_externally.c
+++ b/clang/test/Profile/c-linkage-available_externally.c
@@ -1,8 +1,10 @@
// Make sure instrumentation data from available_externally functions doesn't
// get thrown out and are emitted with the expected linkage.
// RUN: %clang_cc1 -O2 -triple x86_64-apple-macosx10.9 -main-file-name c-linkage-available_externally.c %s -o - -emit-llvm -fprofile-instrument=clang | FileCheck %s
+// RUN: %clang_cc1 -fcoverage-mcdc -O2 -triple x86_64-apple-macosx10.9 -main-file-name c-linkage-available_externally.c %s -o - -emit-llvm -fprofile-instrument=clang | FileCheck %s -check-prefix=MCDC
// CHECK: @__profc_foo = linkonce_odr hidden global [1 x i64] zeroinitializer, section "__DATA,__llvm_prf_cnts", align 8
+// MCDC: @__profbm_foo = linkonce_odr hidden global [0 x i8] zeroinitializer, section "__DATA,__llvm_prf_bits", align 1
// CHECK: @__profd_foo = linkonce_odr hidden global {{.*}} i64 sub (i64 ptrtoint (ptr @__profc_foo to i64), i64 ptrtoint (ptr @__profd_foo to i64)), {{.*}}, section "__DATA,__llvm_prf_data,regular,live_support", align 8
inline int foo(void) { return 1; }
diff --git a/clang/test/Profile/c-mcdc-class.cpp b/clang/test/Profile/c-mcdc-class.cpp
new file mode 100644
index 00000000000000..2206a39ee4ffb4
--- /dev/null
+++ b/clang/test/Profile/c-mcdc-class.cpp
@@ -0,0 +1,104 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple %s -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -fcoverage-mcdc | FileCheck %s -check-prefix=MCDCCTOR
+// RUN: %clang_cc1 -triple %itanium_abi_triple %s -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -fcoverage-mcdc | FileCheck %s -check-prefix=MCDCDTOR
+
+extern void foo();
+extern void bar();
+class Value {
+ public:
+ void setValue(int len);
+ int getValue(void);
+ Value(); // This is the constructor declaration
+ ~Value(); // This is the destructor declaration
+
+ private:
+ int value;
+};
+
+// Member functions definitions including constructor
+Value::Value(void) {
+ if (value != 2 || value != 6)
+ foo();
+}
+Value::~Value(void) {
+ if (value != 2 || value != 3)
+ bar();
+}
+
+// MCDC BOOKKEEPING.
+// MCDCCTOR: @__profbm__ZN5ValueC2Ev = private global [1 x i8] zeroinitializer
+// MCDCCTOR: @__profc__ZN5ValueC2Ev = private global [4 x i64] zeroinitializer
+
+// ALLOCATE MCDC TEMP AND ZERO IT.
+// MCDCCTOR-LABEL: @_ZN5ValueC2Ev(
+// MCDCCTOR: %mcdc.addr = alloca i32, align 4
+// MCDCCTOR: store i32 0, ptr %mcdc.addr, align 4
+
+// SHIFT FIRST CONDITION WITH ID = 0.
+// MCDCCTOR: %[[LAB1:[0-9]+]] = load i32, ptr %value, align 4
+// MCDCCTOR-DAG: %[[BOOL:cmp[0-9]*]] = icmp ne i32 %[[LAB1]], 2
+// MCDCCTOR-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDCCTOR-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[BOOL]] to i32
+// MCDCCTOR-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 0
+// MCDCCTOR-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDCCTOR-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// SHIFT SECOND CONDITION WITH ID = 1.
+// MCDCCTOR: %[[LAB1:[0-9]+]] = load i32, ptr %value2, align 4
+// MCDCCTOR-DAG: %[[BOOL:cmp[0-9]*]] = icmp ne i32 %[[LAB1]], 6
+// MCDCCTOR-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDCCTOR-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[BOOL]] to i32
+// MCDCCTOR-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 1
+// MCDCCTOR-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDCCTOR-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// UPDATE FINAL BITMASK WITH RESULT.
+// MCDCCTOR-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDCCTOR: %[[LAB1:[0-9]+]] = lshr i32 %[[TEMP]], 3
+// MCDCCTOR: %[[LAB2:[0-9]+]] = zext i32 %[[LAB1]] to i64
+// MCDCCTOR: %[[LAB3:[0-9]+]] = add i64 ptrtoint (ptr @__profbm__ZN5ValueC2Ev to i64), %[[LAB2]]
+// MCDCCTOR: %[[LAB4:[0-9]+]] = inttoptr i64 %[[LAB3]] to ptr
+// MCDCCTOR: %[[LAB5:[0-9]+]] = and i32 %[[TEMP]], 7
+// MCDCCTOR: %[[LAB6:[0-9]+]] = trunc i32 %[[LAB5]] to i8
+// MCDCCTOR: %[[LAB7:[0-9]+]] = shl i8 1, %[[LAB6]]
+// MCDCCTOR: %mcdc.bits = load i8, ptr %[[LAB4]], align 1
+// MCDCCTOR: %[[LAB8:[0-9]+]] = or i8 %mcdc.bits, %[[LAB7]]
+// MCDCCTOR: store i8 %[[LAB8]], ptr %[[LAB4]], align 1
+
+// MCDCDTOR: @__profbm__ZN5ValueD2Ev = private global [1 x i8] zeroinitializer
+// MCDCDTOR: @__profc__ZN5ValueD2Ev = private global [4 x i64] zeroinitializer
+
+// ALLOCATE MCDC TEMP AND ZERO IT.
+// MCDCDTOR-LABEL: @_ZN5ValueD2Ev(
+// MCDCDTOR: %mcdc.addr = alloca i32, align 4
+// MCDCDTOR: store i32 0, ptr %mcdc.addr, align 4
+
+// SHIFT FIRST CONDITION WITH ID = 0.
+// MCDCDTOR: %[[LAB1:[0-9]+]] = load i32, ptr %value, align 4
+// MCDCDTOR-DAG: %[[BOOL:cmp[0-9]*]] = icmp ne i32 %[[LAB1]], 2
+// MCDCDTOR-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDCDTOR-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[BOOL]] to i32
+// MCDCDTOR-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 0
+// MCDCDTOR-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDCDTOR-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// SHIFT SECOND CONDITION WITH ID = 1.
+// MCDCDTOR: %[[LAB1:[0-9]+]] = load i32, ptr %value2, align 4
+// MCDCDTOR-DAG: %[[BOOL:cmp[0-9]*]] = icmp ne i32 %[[LAB1]], 3
+// MCDCDTOR-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDCDTOR-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[BOOL]] to i32
+// MCDCDTOR-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 1
+// MCDCDTOR-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDCDTOR-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// UPDATE FINAL BITMASK WITH RESULT.
+// MCDCDTOR-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDCDTOR: %[[LAB1:[0-9]+]] = lshr i32 %[[TEMP]], 3
+// MCDCDTOR: %[[LAB2:[0-9]+]] = zext i32 %[[LAB1]] to i64
+// MCDCDTOR: %[[LAB3:[0-9]+]] = add i64 ptrtoint (ptr @__profbm__ZN5ValueD2Ev to i64), %[[LAB2]]
+// MCDCDTOR: %[[LAB4:[0-9]+]] = inttoptr i64 %[[LAB3]] to ptr
+// MCDCDTOR: %[[LAB5:[0-9]+]] = and i32 %[[TEMP]], 7
+// MCDCDTOR: %[[LAB6:[0-9]+]] = trunc i32 %[[LAB5]] to i8
+// MCDCDTOR: %[[LAB7:[0-9]+]] = shl i8 1, %[[LAB6]]
+// MCDCDTOR: %mcdc.bits = load i8, ptr %[[LAB4]], align 1
+// MCDCDTOR: %[[LAB8:[0-9]+]] = or i8 %mcdc.bits, %[[LAB7]]
+// MCDCDTOR: store i8 %[[LAB8]], ptr %[[LAB4]], align 1
diff --git a/clang/test/Profile/c-mcdc-nested-ternary.c b/clang/test/Profile/c-mcdc-nested-ternary.c
new file mode 100644
index 00000000000000..4b014e07f6df53
--- /dev/null
+++ b/clang/test/Profile/c-mcdc-nested-ternary.c
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple %s -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -fcoverage-mcdc | FileCheck %s -check-prefix=MCDC
+// RUN: %clang_cc1 -triple %itanium_abi_triple %s -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping | FileCheck %s -check-prefix=NOMCDC
+
+int test(int b, int c, int d, int e, int f) {
+ return ((b ? c : d) && e && f);
+}
+
+// NOMCDC-NOT: %mcdc.addr
+// NOMCDC-NOT: __profbm_test
+
+// MCDC BOOKKEEPING.
+// MCDC: @__profbm_test = private global [1 x i8] zeroinitializer
+
+// ALLOCATE MCDC TEMP AND ZERO IT.
+// MCDC-LABEL: @test(
+// MCDC: %mcdc.addr = alloca i32, align 4
+// MCDC: store i32 0, ptr %mcdc.addr, align 4
+
+// TERNARY TRUE SHOULD SHIFT ID = 0 FOR CONDITION 'c'.
+// MCDC-LABEL: cond.true:
+// MCDC: %[[LAB1:[0-9]+]] = load i32, ptr %c.addr, align 4
+// MCDC-DAG: %[[BOOL:tobool[0-9]*]] = icmp ne i32 %[[LAB1]], 0
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[BOOL]] to i32
+// MCDC-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 0
+// MCDC-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDC-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// TERNARY FALSE SHOULD SHIFT ID = 0 FOR CONDITION 'd'.
+// MCDC-LABEL: cond.false:
+// MCDC: %[[LAB1:[0-9]+]] = load i32, ptr %d.addr, align 4
+// MCDC-DAG: %[[BOOL:tobool[0-9]*]] = icmp ne i32 %[[LAB1]], 0
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[BOOL]] to i32
+// MCDC-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 0
+// MCDC-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDC-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// SHIFT SECOND CONDITION WITH ID = 2.
+// MCDC: %[[LAB1:[0-9]+]] = load i32, ptr %e.addr, align 4
+// MCDC-DAG: %[[BOOL:tobool[0-9]*]] = icmp ne i32 %[[LAB1]], 0
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[BOOL]] to i32
+// MCDC-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 2
+// MCDC-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDC-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// SHIFT THIRD CONDITION WITH ID = 1.
+// MCDC: %[[LAB1:[0-9]+]] = load i32, ptr %f.addr, align 4
+// MCDC-DAG: %[[BOOL:tobool[0-9]*]] = icmp ne i32 %[[LAB1]], 0
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[BOOL]] to i32
+// MCDC-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 1
+// MCDC-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDC-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// UPDATE FINAL BITMASK WITH RESULT.
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC: %[[LAB1:[0-9]+]] = lshr i32 %[[TEMP]], 3
+// MCDC: %[[LAB2:[0-9]+]] = zext i32 %[[LAB1]] to i64
+// MCDC: %[[LAB3:[0-9]+]] = add i64 ptrtoint (ptr @__profbm_test to i64), %[[LAB2]]
+// MCDC: %[[LAB4:[0-9]+]] = inttoptr i64 %[[LAB3]] to ptr
+// MCDC: %[[LAB5:[0-9]+]] = and i32 %[[TEMP]], 7
+// MCDC: %[[LAB6:[0-9]+]] = trunc i32 %[[LAB5]] to i8
+// MCDC: %[[LAB7:[0-9]+]] = shl i8 1, %[[LAB6]]
+// MCDC: %mcdc.bits = load i8, ptr %[[LAB4]], align 1
+// MCDC: %[[LAB8:[0-9]+]] = or i8 %mcdc.bits, %[[LAB7]]
+// MCDC: store i8 %[[LAB8]], ptr %[[LAB4]], align 1
diff --git a/clang/test/Profile/c-mcdc-not.c b/clang/test/Profile/c-mcdc-not.c
new file mode 100644
index 00000000000000..aa638b9680b841
--- /dev/null
+++ b/clang/test/Profile/c-mcdc-not.c
@@ -0,0 +1,88 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple %s -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -fcoverage-mcdc | FileCheck %s -check-prefix=MCDC
+// RUN: %clang_cc1 -triple %itanium_abi_triple %s -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping | FileCheck %s -check-prefix=NOMCDC
+
+int test(int a, int b, int c, int d, int e, int f) {
+ return ((!a && b) || ((!c && d) || (e && !f)));
+}
+
+// NOMCDC-NOT: %mcdc.addr
+// NOMCDC-NOT: __profbm_test
+
+// MCDC BOOKKEEPING.
+// MCDC: @__profbm_test = private global [8 x i8] zeroinitializer
+// MCDC: @__profc_test = private global [9 x i64] zeroinitializer
+
+// ALLOCATE MCDC TEMP AND ZERO IT.
+// MCDC-LABEL: @test(
+// MCDC: %mcdc.addr = alloca i32, align 4
+// MCDC: store i32 0, ptr %mcdc.addr, align 4
+
+// SHIFT FIRST CONDITION WITH ID = 0.
+// MCDC: %[[LAB1:[0-9]+]] = load i32, ptr %a.addr, align 4
+// MCDC-DAG: %[[BOOL:tobool[0-9]*]] = icmp ne i32 %[[LAB1]], 0
+// MCDC-DAG: %[[LNOT:lnot[0-9]*]] = xor i1 %[[BOOL]]
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[LNOT]] to i32
+// MCDC-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 0
+// MCDC-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDC-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// SHIFT SECOND CONDITION WITH ID = 2.
+// MCDC: %[[LAB1:[0-9]+]] = load i32, ptr %b.addr, align 4
+// MCDC-DAG: %[[BOOL:tobool[0-9]*]] = icmp ne i32 %[[LAB1]], 0
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[BOOL]] to i32
+// MCDC-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 2
+// MCDC-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDC-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// SHIFT THIRD CONDITION WITH ID = 1.
+// MCDC: %[[LAB1:[0-9]+]] = load i32, ptr %c.addr, align 4
+// MCDC-DAG: %[[BOOL:tobool[0-9]*]] = icmp ne i32 %[[LAB1]], 0
+// MCDC-DAG: %[[LNOT:lnot[0-9]*]] = xor i1 %[[BOOL]]
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[LNOT]] to i32
+// MCDC-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 1
+// MCDC-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDC-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// SHIFT FOURTH CONDITION WITH ID = 4.
+// MCDC: %[[LAB1:[0-9]+]] = load i32, ptr %d.addr, align 4
+// MCDC-DAG: %[[BOOL:tobool[0-9]*]] = icmp ne i32 %[[LAB1]], 0
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[BOOL]] to i32
+// MCDC-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 4
+// MCDC-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDC-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// SHIFT FIFTH CONDITION WITH ID = 3.
+// MCDC: %[[LAB1:[0-9]+]] = load i32, ptr %e.addr, align 4
+// MCDC-DAG: %[[BOOL:tobool[0-9]*]] = icmp ne i32 %[[LAB1]], 0
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[BOOL]] to i32
+// MCDC-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 3
+// MCDC-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDC-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// SHIFT SIXTH CONDITION WITH ID = 5.
+// MCDC: %[[LAB1:[0-9]+]] = load i32, ptr %f.addr, align 4
+// MCDC-DAG: %[[BOOL:tobool[0-9]*]] = icmp ne i32 %[[LAB1]], 0
+// MCDC-DAG: %[[LNOT:lnot[0-9]*]] = xor i1 %[[BOOL]]
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[LNOT]] to i32
+// MCDC-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 5
+// MCDC-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDC-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// UPDATE FINAL BITMASK WITH RESULT.
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC: %[[LAB1:[0-9]+]] = lshr i32 %[[TEMP]], 3
+// MCDC: %[[LAB2:[0-9]+]] = zext i32 %[[LAB1]] to i64
+// MCDC: %[[LAB3:[0-9]+]] = add i64 ptrtoint (ptr @__profbm_test to i64), %[[LAB2]]
+// MCDC: %[[LAB4:[0-9]+]] = inttoptr i64 %[[LAB3]] to ptr
+// MCDC: %[[LAB5:[0-9]+]] = and i32 %[[TEMP]], 7
+// MCDC: %[[LAB6:[0-9]+]] = trunc i32 %[[LAB5]] to i8
+// MCDC: %[[LAB7:[0-9]+]] = shl i8 1, %[[LAB6]]
+// MCDC: %mcdc.bits = load i8, ptr %[[LAB4]], align 1
+// MCDC: %[[LAB8:[0-9]+]] = or i8 %mcdc.bits, %[[LAB7]]
+// MCDC: store i8 %[[LAB8]], ptr %[[LAB4]], align 1
diff --git a/clang/test/Profile/c-mcdc.c b/clang/test/Profile/c-mcdc.c
new file mode 100644
index 00000000000000..ac845d204853db
--- /dev/null
+++ b/clang/test/Profile/c-mcdc.c
@@ -0,0 +1,102 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple %s -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -fcoverage-mcdc | FileCheck %s -check-prefix=MCDC
+// RUN: %clang_cc1 -triple %itanium_abi_triple %s -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping | FileCheck %s -check-prefix=NOMCDC
+// RUN: %clang_cc1 -triple %itanium_abi_triple %s -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping -fcoverage-mcdc -disable-llvm-passes | FileCheck %s -check-prefix=NOPROFPASS
+
+int test(int a, int b, int c, int d, int e, int f) {
+ return ((a && b) || ((c && d) || (e && f)));
+}
+
+// NOMCDC-NOT: %mcdc.addr
+// NOMCDC-NOT: __profbm_test
+// NOPROFPASS-NOT: __profbm_test
+
+// MCDC BOOKKEEPING.
+// MCDC: @__profbm_test = private global [8 x i8] zeroinitializer
+// MCDC: @__profc_test = private global [9 x i64] zeroinitializer
+
+// ALLOCATE MCDC TEMP AND ZERO IT.
+// NOPROFPASS-LABEL: @test(
+// NOPROFPASS: call void @llvm.instrprof.mcdc.parameters(ptr @__profn_test, i64 [[HASH:[0-9]+]], i32 8)
+// MCDC-LABEL: @test(
+// MCDC: %mcdc.addr = alloca i32, align 4
+// MCDC: store i32 0, ptr %mcdc.addr, align 4
+
+// SHIFT FIRST CONDITION WITH ID = 0.
+// NOPROFPASS: call void @llvm.instrprof.mcdc.condbitmap.update(ptr @__profn_test, i64 [[HASH]], i32 0, ptr %mcdc.addr, i1 %tobool{{[0-9]*}})
+// MCDC: %[[LAB1:[0-9]+]] = load i32, ptr %a.addr, align 4
+// MCDC-DAG: %[[BOOL:tobool[0-9]*]] = icmp ne i32 %[[LAB1]], 0
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[BOOL]] to i32
+// MCDC-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 0
+// MCDC-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDC-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// SHIFT SECOND CONDITION WITH ID = 2.
+// NOPROFPASS-LABEL: land.lhs.true:
+// NOPROFPASS: call void @llvm.instrprof.mcdc.condbitmap.update(ptr @__profn_test, i64 [[HASH]], i32 2, ptr %mcdc.addr, i1 %tobool{{[0-9]*}})
+// MCDC: %[[LAB1:[0-9]+]] = load i32, ptr %b.addr, align 4
+// MCDC-DAG: %[[BOOL:tobool[0-9]*]] = icmp ne i32 %[[LAB1]], 0
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[BOOL]] to i32
+// MCDC-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 2
+// MCDC-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDC-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// SHIFT THIRD CONDITION WITH ID = 1.
+// NOPROFPASS-LABEL: lor.rhs:
+// NOPROFPASS: call void @llvm.instrprof.mcdc.condbitmap.update(ptr @__profn_test, i64 [[HASH]], i32 1, ptr %mcdc.addr, i1 %tobool{{[0-9]*}})
+// MCDC: %[[LAB1:[0-9]+]] = load i32, ptr %c.addr, align 4
+// MCDC-DAG: %[[BOOL:tobool[0-9]*]] = icmp ne i32 %[[LAB1]], 0
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[BOOL]] to i32
+// MCDC-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 1
+// MCDC-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDC-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// SHIFT FOURTH CONDITION WITH ID = 4.
+// NOPROFPASS-LABEL: land.lhs.true3:
+// NOPROFPASS: call void @llvm.instrprof.mcdc.condbitmap.update(ptr @__profn_test, i64 [[HASH]], i32 4, ptr %mcdc.addr, i1 %tobool{{[0-9]*}})
+// MCDC: %[[LAB1:[0-9]+]] = load i32, ptr %d.addr, align 4
+// MCDC-DAG: %[[BOOL:tobool[0-9]*]] = icmp ne i32 %[[LAB1]], 0
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[BOOL]] to i32
+// MCDC-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 4
+// MCDC-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDC-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// SHIFT FIFTH CONDITION WITH ID = 3.
+// NOPROFPASS-LABEL: lor.rhs6:
+// NOPROFPASS: call void @llvm.instrprof.mcdc.condbitmap.update(ptr @__profn_test, i64 [[HASH]], i32 3, ptr %mcdc.addr, i1 %tobool{{[0-9]*}})
+// MCDC: %[[LAB1:[0-9]+]] = load i32, ptr %e.addr, align 4
+// MCDC-DAG: %[[BOOL:tobool[0-9]*]] = icmp ne i32 %[[LAB1]], 0
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[BOOL]] to i32
+// MCDC-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 3
+// MCDC-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDC-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// SHIFT SIXTH CONDITION WITH ID = 5.
+// NOPROFPASS-LABEL: land.rhs:
+// NOPROFPASS: call void @llvm.instrprof.mcdc.condbitmap.update(ptr @__profn_test, i64 [[HASH]], i32 5, ptr %mcdc.addr, i1 %tobool{{[0-9]*}})
+// MCDC: %[[LAB1:[0-9]+]] = load i32, ptr %f.addr, align 4
+// MCDC-DAG: %[[BOOL:tobool[0-9]*]] = icmp ne i32 %[[LAB1]], 0
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG: %[[LAB2:[0-9]+]] = zext i1 %[[BOOL]] to i32
+// MCDC-DAG: %[[LAB3:[0-9]+]] = shl i32 %[[LAB2]], 5
+// MCDC-DAG: %[[LAB4:[0-9]+]] = or i32 %[[TEMP]], %[[LAB3]]
+// MCDC-DAG: store i32 %[[LAB4]], ptr %mcdc.addr, align 4
+
+// UPDATE FINAL BITMASK WITH RESULT.
+// NOPROFPASS-LABEL: lor.end:
+// NOPROFPASS: call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @__profn_test, i64 [[HASH]], i32 8, i32 0, ptr %mcdc.addr)
+// MCDC-DAG: %[[TEMP:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC: %[[LAB1:[0-9]+]] = lshr i32 %[[TEMP]], 3
+// MCDC: %[[LAB2:[0-9]+]] = zext i32 %[[LAB1]] to i64
+// MCDC: %[[LAB3:[0-9]+]] = add i64 ptrtoint (ptr @__profbm_test to i64), %[[LAB2]]
+// MCDC: %[[LAB4:[0-9]+]] = inttoptr i64 %[[LAB3]] to ptr
+// MCDC: %[[LAB5:[0-9]+]] = and i32 %[[TEMP]], 7
+// MCDC: %[[LAB6:[0-9]+]] = trunc i32 %[[LAB5]] to i8
+// MCDC: %[[LAB7:[0-9]+]] = shl i8 1, %[[LAB6]]
+// MCDC: %mcdc.bits = load i8, ptr %[[LAB4]], align 1
+// MCDC: %[[LAB8:[0-9]+]] = or i8 %mcdc.bits, %[[LAB7]]
+// MCDC: store i8 %[[LAB8]], ptr %[[LAB4]], align 1
diff --git a/compiler-rt/test/profile/ContinuousSyncMode/image-with-mcdc.c b/compiler-rt/test/profile/ContinuousSyncMode/image-with-mcdc.c
new file mode 100644
index 00000000000000..748af46ee52fa5
--- /dev/null
+++ b/compiler-rt/test/profile/ContinuousSyncMode/image-with-mcdc.c
@@ -0,0 +1,26 @@
+// REQUIRES: darwin
+
+// RUN: %clang_profgen -fcoverage-mapping -fcoverage-mcdc -O3 -o %t.exe %s
+// RUN: env LLVM_PROFILE_FILE="%c%t.profraw" %run %t.exe 3 3
+// RUN: llvm-profdata show --text --all-functions %t.profraw | FileCheck %s
+
+// CHECK: Num Bitmap Bytes:
+// CHECK-NEXT: $1
+// CHECK-NEXT: Bitmap Byte Values:
+// CHECK-NEXT: 8
+#include <stdio.h>
+#include <stdlib.h>
+extern int __llvm_profile_is_continuous_mode_enabled(void);
+int main(int argc, char *const argv[]) {
+ if (!__llvm_profile_is_continuous_mode_enabled())
+ return 1;
+
+ if (argc < 3)
+ return 1;
+
+ if ((atoi(argv[1]) > 2) && (atoi(argv[2]) > 2)) {
+ printf("Decision Satisfied");
+ }
+
+ return 0;
+}
More information about the cfe-commits
mailing list