[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
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)">;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -287,6 +287,9 @@ public:
|
||||
/// 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 @@ private:
|
||||
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
/// 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.
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -33,8 +33,11 @@ private:
|
||||
|
||||
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 @@ private:
|
||||
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 @@ private:
|
||||
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) {
|
||||
|
||||
@@ -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 @@ public:
|
||||
}
|
||||
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
31
clang/test/CoverageMapping/mcdc-class.cpp
Normal file
31
clang/test/CoverageMapping/mcdc-class.cpp
Normal file
@@ -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]
|
||||
7
clang/test/CoverageMapping/mcdc-error-conditions.cpp
Normal file
7
clang/test/CoverageMapping/mcdc-error-conditions.cpp
Normal file
@@ -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
|
||||
10
clang/test/CoverageMapping/mcdc-error-nests.cpp
Normal file
10
clang/test/CoverageMapping/mcdc-error-nests.cpp
Normal file
@@ -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.
|
||||
110
clang/test/CoverageMapping/mcdc-logical-scalar-ids.cpp
Normal file
110
clang/test/CoverageMapping/mcdc-logical-scalar-ids.cpp
Normal file
@@ -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]
|
||||
131
clang/test/CoverageMapping/mcdc-logical-stmt-ids-all.cpp
Normal file
131
clang/test/CoverageMapping/mcdc-logical-stmt-ids-all.cpp
Normal file
@@ -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]
|
||||
111
clang/test/CoverageMapping/mcdc-logical-stmt-ids.cpp
Normal file
111
clang/test/CoverageMapping/mcdc-logical-stmt-ids.cpp
Normal file
@@ -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]
|
||||
@@ -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; }
|
||||
|
||||
|
||||
104
clang/test/Profile/c-mcdc-class.cpp
Normal file
104
clang/test/Profile/c-mcdc-class.cpp
Normal file
@@ -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
|
||||
68
clang/test/Profile/c-mcdc-nested-ternary.c
Normal file
68
clang/test/Profile/c-mcdc-nested-ternary.c
Normal file
@@ -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
|
||||
88
clang/test/Profile/c-mcdc-not.c
Normal file
88
clang/test/Profile/c-mcdc-not.c
Normal file
@@ -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
|
||||
102
clang/test/Profile/c-mcdc.c
Normal file
102
clang/test/Profile/c-mcdc.c
Normal file
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user