Auto merge of #124255 - RenjiSann:renji/mcdc-nested-expressions, r=Zalathar
MCDC coverage: support nested decision coverage #123409 provided the initial MCDC coverage implementation. As referenced in #124144, it does not currently support "nested" decisions, like the following example : ```rust fn nested_if_in_condition(a: bool, b: bool, c: bool) { if a && if b || c { true } else { false } { say("yes"); } else { say("no"); } } ``` Note that there is an if-expression (`if b || c ...`) embedded inside a boolean expression in the decision of an outer if-expression. This PR proposes a workaround for this cases, by introducing a Decision context stack, and by handing several `temporary condition bitmaps` instead of just one. When instrumenting boolean expressions, if the current node is a leaf condition (i.e. not a `||`/`&&` logical operator nor a `!` not operator), we insert a new decision context, such that if there are more boolean expressions inside the condition, they are handled as separate expressions. On the codegen LLVM side, we allocate as many `temp_cond_bitmap`s as necessary to handle the maximum encountered decision depth.
This commit is contained in:
commit
7a58674259
@ -27,6 +27,7 @@
|
||||
use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target};
|
||||
use smallvec::SmallVec;
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::CString;
|
||||
use std::iter;
|
||||
use std::ops::Deref;
|
||||
use std::ptr;
|
||||
@ -1709,7 +1710,8 @@ pub(crate) fn mcdc_parameters(
|
||||
fn_name: &'ll Value,
|
||||
hash: &'ll Value,
|
||||
bitmap_bytes: &'ll Value,
|
||||
) -> &'ll Value {
|
||||
max_decision_depth: u32,
|
||||
) -> Vec<&'ll Value> {
|
||||
debug!("mcdc_parameters() with args ({:?}, {:?}, {:?})", fn_name, hash, bitmap_bytes);
|
||||
|
||||
assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later");
|
||||
@ -1722,6 +1724,8 @@ pub(crate) fn mcdc_parameters(
|
||||
let args = &[fn_name, hash, bitmap_bytes];
|
||||
let args = self.check_call("call", llty, llfn, args);
|
||||
|
||||
let mut cond_bitmaps = vec![];
|
||||
|
||||
unsafe {
|
||||
let _ = llvm::LLVMRustBuildCall(
|
||||
self.llbuilder,
|
||||
@ -1733,17 +1737,22 @@ pub(crate) fn mcdc_parameters(
|
||||
0 as c_uint,
|
||||
);
|
||||
// Create condition bitmap named `mcdc.addr`.
|
||||
let mut bx = Builder::with_cx(self.cx);
|
||||
bx.position_at_start(llvm::LLVMGetFirstBasicBlock(self.llfn()));
|
||||
let cond_bitmap = {
|
||||
let alloca =
|
||||
llvm::LLVMBuildAlloca(bx.llbuilder, bx.cx.type_i32(), c"mcdc.addr".as_ptr());
|
||||
llvm::LLVMSetAlignment(alloca, 4);
|
||||
alloca
|
||||
};
|
||||
bx.store(self.const_i32(0), cond_bitmap, self.tcx().data_layout.i32_align.abi);
|
||||
cond_bitmap
|
||||
for i in 0..=max_decision_depth {
|
||||
let mut bx = Builder::with_cx(self.cx);
|
||||
bx.position_at_start(llvm::LLVMGetFirstBasicBlock(self.llfn()));
|
||||
|
||||
let name = CString::new(format!("mcdc.addr.{i}")).unwrap();
|
||||
let cond_bitmap = {
|
||||
let alloca =
|
||||
llvm::LLVMBuildAlloca(bx.llbuilder, bx.cx.type_i32(), name.as_ptr());
|
||||
llvm::LLVMSetAlignment(alloca, 4);
|
||||
alloca
|
||||
};
|
||||
bx.store(self.const_i32(0), cond_bitmap, self.tcx().data_layout.i32_align.abi);
|
||||
cond_bitmaps.push(cond_bitmap);
|
||||
}
|
||||
}
|
||||
cond_bitmaps
|
||||
}
|
||||
|
||||
pub(crate) fn mcdc_tvbitmap_update(
|
||||
|
@ -30,7 +30,7 @@ pub struct CrateCoverageContext<'ll, 'tcx> {
|
||||
pub(crate) function_coverage_map:
|
||||
RefCell<FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>>>,
|
||||
pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
|
||||
pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
|
||||
pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, Vec<&'ll llvm::Value>>>,
|
||||
}
|
||||
|
||||
impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
|
||||
@ -49,9 +49,20 @@ pub fn take_function_coverage_map(
|
||||
}
|
||||
|
||||
/// LLVM use a temp value to record evaluated mcdc test vector of each decision, which is called condition bitmap.
|
||||
/// This value is named `mcdc.addr` (same as clang) and is a 32-bit integer.
|
||||
fn try_get_mcdc_condition_bitmap(&self, instance: &Instance<'tcx>) -> Option<&'ll llvm::Value> {
|
||||
self.mcdc_condition_bitmap_map.borrow().get(instance).copied()
|
||||
/// In order to handle nested decisions, several condition bitmaps can be
|
||||
/// allocated for a function body.
|
||||
/// These values are named `mcdc.addr.{i}` and are a 32-bit integers.
|
||||
/// They respectively hold the condition bitmaps for decisions with a depth of `i`.
|
||||
fn try_get_mcdc_condition_bitmap(
|
||||
&self,
|
||||
instance: &Instance<'tcx>,
|
||||
decision_depth: u16,
|
||||
) -> Option<&'ll llvm::Value> {
|
||||
self.mcdc_condition_bitmap_map
|
||||
.borrow()
|
||||
.get(instance)
|
||||
.and_then(|bitmap_map| bitmap_map.get(decision_depth as usize))
|
||||
.copied() // Dereference Option<&&Value> to Option<&Value>
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,7 +154,7 @@ fn add_coverage(&mut self, instance: Instance<'tcx>, kind: &CoverageKind) {
|
||||
CoverageKind::ExpressionUsed { id } => {
|
||||
func_coverage.mark_expression_id_seen(id);
|
||||
}
|
||||
CoverageKind::CondBitmapUpdate { id, value, .. } => {
|
||||
CoverageKind::CondBitmapUpdate { id, value, decision_depth } => {
|
||||
drop(coverage_map);
|
||||
assert_ne!(
|
||||
id.as_u32(),
|
||||
@ -151,7 +162,7 @@ fn add_coverage(&mut self, instance: Instance<'tcx>, kind: &CoverageKind) {
|
||||
"ConditionId of evaluated conditions should never be zero"
|
||||
);
|
||||
let cond_bitmap = coverage_context
|
||||
.try_get_mcdc_condition_bitmap(&instance)
|
||||
.try_get_mcdc_condition_bitmap(&instance, decision_depth)
|
||||
.expect("mcdc cond bitmap should have been allocated for updating");
|
||||
let cond_loc = bx.const_i32(id.as_u32() as i32 - 1);
|
||||
let bool_value = bx.const_bool(value);
|
||||
@ -159,10 +170,10 @@ fn add_coverage(&mut self, instance: Instance<'tcx>, kind: &CoverageKind) {
|
||||
let hash = bx.const_u64(function_coverage_info.function_source_hash);
|
||||
bx.mcdc_condbitmap_update(fn_name, hash, cond_loc, cond_bitmap, bool_value);
|
||||
}
|
||||
CoverageKind::TestVectorBitmapUpdate { bitmap_idx } => {
|
||||
CoverageKind::TestVectorBitmapUpdate { bitmap_idx, decision_depth } => {
|
||||
drop(coverage_map);
|
||||
let cond_bitmap = coverage_context
|
||||
.try_get_mcdc_condition_bitmap(&instance)
|
||||
.try_get_mcdc_condition_bitmap(&instance, decision_depth)
|
||||
.expect("mcdc cond bitmap should have been allocated for merging into the global bitmap");
|
||||
let bitmap_bytes = bx.tcx().coverage_ids_info(instance.def).mcdc_bitmap_bytes;
|
||||
assert!(bitmap_idx < bitmap_bytes, "bitmap index of the decision out of range");
|
||||
@ -195,7 +206,8 @@ fn ensure_mcdc_parameters<'ll, 'tcx>(
|
||||
let fn_name = bx.get_pgo_func_name_var(instance);
|
||||
let hash = bx.const_u64(function_coverage_info.function_source_hash);
|
||||
let bitmap_bytes = bx.const_u32(function_coverage_info.mcdc_bitmap_bytes);
|
||||
let cond_bitmap = bx.mcdc_parameters(fn_name, hash, bitmap_bytes);
|
||||
let max_decision_depth = function_coverage_info.mcdc_max_decision_depth;
|
||||
let cond_bitmap = bx.mcdc_parameters(fn_name, hash, bitmap_bytes, max_decision_depth as u32);
|
||||
bx.coverage_context()
|
||||
.expect("already checked above")
|
||||
.mcdc_condition_bitmap_map
|
||||
|
@ -132,7 +132,7 @@ pub enum CoverageKind {
|
||||
///
|
||||
/// If this statement does not survive MIR optimizations, the condition would never be
|
||||
/// taken as evaluated.
|
||||
CondBitmapUpdate { id: ConditionId, value: bool },
|
||||
CondBitmapUpdate { id: ConditionId, value: bool, decision_depth: u16 },
|
||||
|
||||
/// Marks the point in MIR control flow represented by a evaluated decision.
|
||||
///
|
||||
@ -140,7 +140,7 @@ pub enum CoverageKind {
|
||||
///
|
||||
/// If this statement does not survive MIR optimizations, the decision would never be
|
||||
/// taken as evaluated.
|
||||
TestVectorBitmapUpdate { bitmap_idx: u32 },
|
||||
TestVectorBitmapUpdate { bitmap_idx: u32, decision_depth: u16 },
|
||||
}
|
||||
|
||||
impl Debug for CoverageKind {
|
||||
@ -151,11 +151,17 @@ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()),
|
||||
CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()),
|
||||
ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()),
|
||||
CondBitmapUpdate { id, value } => {
|
||||
write!(fmt, "CondBitmapUpdate({:?}, {:?})", id.index(), value)
|
||||
CondBitmapUpdate { id, value, decision_depth } => {
|
||||
write!(
|
||||
fmt,
|
||||
"CondBitmapUpdate({:?}, {:?}, depth={:?})",
|
||||
id.index(),
|
||||
value,
|
||||
decision_depth
|
||||
)
|
||||
}
|
||||
TestVectorBitmapUpdate { bitmap_idx } => {
|
||||
write!(fmt, "TestVectorUpdate({:?})", bitmap_idx)
|
||||
TestVectorBitmapUpdate { bitmap_idx, decision_depth } => {
|
||||
write!(fmt, "TestVectorUpdate({:?}, depth={:?})", bitmap_idx, decision_depth)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -269,6 +275,9 @@ pub struct FunctionCoverageInfo {
|
||||
pub mcdc_bitmap_bytes: u32,
|
||||
pub expressions: IndexVec<ExpressionId, Expression>,
|
||||
pub mappings: Vec<Mapping>,
|
||||
/// The depth of the deepest decision is used to know how many
|
||||
/// temp condbitmaps should be allocated for the function.
|
||||
pub mcdc_max_decision_depth: u16,
|
||||
}
|
||||
|
||||
/// Branch information recorded during THIR-to-MIR lowering, and stored in MIR.
|
||||
@ -319,6 +328,7 @@ pub struct MCDCBranchSpan {
|
||||
pub condition_info: Option<ConditionInfo>,
|
||||
pub true_marker: BlockMarkerId,
|
||||
pub false_marker: BlockMarkerId,
|
||||
pub decision_depth: u16,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
@ -334,4 +344,5 @@ pub struct MCDCDecisionSpan {
|
||||
pub span: Span,
|
||||
pub conditions_num: usize,
|
||||
pub end_markers: Vec<BlockMarkerId>,
|
||||
pub decision_depth: u16,
|
||||
}
|
||||
|
@ -496,20 +496,27 @@ fn write_coverage_branch_info(
|
||||
)?;
|
||||
}
|
||||
|
||||
for coverage::MCDCBranchSpan { span, condition_info, true_marker, false_marker } in
|
||||
mcdc_branch_spans
|
||||
for coverage::MCDCBranchSpan {
|
||||
span,
|
||||
condition_info,
|
||||
true_marker,
|
||||
false_marker,
|
||||
decision_depth,
|
||||
} in mcdc_branch_spans
|
||||
{
|
||||
writeln!(
|
||||
w,
|
||||
"{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
|
||||
"{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?}, depth: {decision_depth:?} }} => {span:?}",
|
||||
condition_info.map(|info| info.condition_id)
|
||||
)?;
|
||||
}
|
||||
|
||||
for coverage::MCDCDecisionSpan { span, conditions_num, end_markers } in mcdc_decision_spans {
|
||||
for coverage::MCDCDecisionSpan { span, conditions_num, end_markers, decision_depth } in
|
||||
mcdc_decision_spans
|
||||
{
|
||||
writeln!(
|
||||
w,
|
||||
"{INDENT}coverage mcdc decision {{ conditions_num: {conditions_num:?}, end: {end_markers:?} }} => {span:?}"
|
||||
"{INDENT}coverage mcdc decision {{ conditions_num: {conditions_num:?}, end: {end_markers:?}, depth: {decision_depth:?} }} => {span:?}"
|
||||
)?;
|
||||
}
|
||||
|
||||
|
@ -101,10 +101,14 @@ fn fetch_mcdc_condition_info(
|
||||
tcx: TyCtxt<'_>,
|
||||
true_marker: BlockMarkerId,
|
||||
false_marker: BlockMarkerId,
|
||||
) -> Option<ConditionInfo> {
|
||||
) -> Option<(u16, ConditionInfo)> {
|
||||
let mcdc_state = self.mcdc_state.as_mut()?;
|
||||
let decision_depth = mcdc_state.decision_depth();
|
||||
let (mut condition_info, decision_result) =
|
||||
mcdc_state.take_condition(true_marker, false_marker);
|
||||
// take_condition() returns Some for decision_result when the decision stack
|
||||
// is empty, i.e. when all the conditions of the decision were instrumented,
|
||||
// and the decision is "complete".
|
||||
if let Some(decision) = decision_result {
|
||||
match decision.conditions_num {
|
||||
0 => {
|
||||
@ -131,7 +135,7 @@ fn fetch_mcdc_condition_info(
|
||||
}
|
||||
}
|
||||
}
|
||||
condition_info
|
||||
condition_info.map(|cond_info| (decision_depth, cond_info))
|
||||
}
|
||||
|
||||
fn add_two_way_branch<'tcx>(
|
||||
@ -199,17 +203,32 @@ pub(crate) fn into_done(self) -> Option<Box<mir::coverage::BranchInfo>> {
|
||||
/// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged.
|
||||
const MAX_CONDITIONS_NUM_IN_DECISION: usize = 6;
|
||||
|
||||
struct MCDCState {
|
||||
#[derive(Default)]
|
||||
struct MCDCDecisionCtx {
|
||||
/// To construct condition evaluation tree.
|
||||
decision_stack: VecDeque<ConditionInfo>,
|
||||
processing_decision: Option<MCDCDecisionSpan>,
|
||||
}
|
||||
|
||||
struct MCDCState {
|
||||
decision_ctx_stack: Vec<MCDCDecisionCtx>,
|
||||
}
|
||||
|
||||
impl MCDCState {
|
||||
fn new_if_enabled(tcx: TyCtxt<'_>) -> Option<Self> {
|
||||
tcx.sess
|
||||
.instrument_coverage_mcdc()
|
||||
.then(|| Self { decision_stack: VecDeque::new(), processing_decision: None })
|
||||
.then(|| Self { decision_ctx_stack: vec![MCDCDecisionCtx::default()] })
|
||||
}
|
||||
|
||||
/// Decision depth is given as a u16 to reduce the size of the `CoverageKind`,
|
||||
/// as it is very unlikely that the depth ever reaches 2^16.
|
||||
#[inline]
|
||||
fn decision_depth(&self) -> u16 {
|
||||
u16::try_from(
|
||||
self.decision_ctx_stack.len().checked_sub(1).expect("Unexpected empty decision stack"),
|
||||
)
|
||||
.expect("decision depth did not fit in u16, this is likely to be an instrumentation error")
|
||||
}
|
||||
|
||||
// At first we assign ConditionIds for each sub expression.
|
||||
@ -253,19 +272,23 @@ fn new_if_enabled(tcx: TyCtxt<'_>) -> Option<Self> {
|
||||
// - If the op is AND, the "false_next" of LHS and RHS should be the parent's "false_next". While "true_next" of the LHS is the RHS, the "true next" of RHS is the parent's "true_next".
|
||||
// - If the op is OR, the "true_next" of LHS and RHS should be the parent's "true_next". While "false_next" of the LHS is the RHS, the "false next" of RHS is the parent's "false_next".
|
||||
fn record_conditions(&mut self, op: LogicalOp, span: Span) {
|
||||
let decision = match self.processing_decision.as_mut() {
|
||||
let decision_depth = self.decision_depth();
|
||||
let decision_ctx =
|
||||
self.decision_ctx_stack.last_mut().expect("Unexpected empty decision_ctx_stack");
|
||||
let decision = match decision_ctx.processing_decision.as_mut() {
|
||||
Some(decision) => {
|
||||
decision.span = decision.span.to(span);
|
||||
decision
|
||||
}
|
||||
None => self.processing_decision.insert(MCDCDecisionSpan {
|
||||
None => decision_ctx.processing_decision.insert(MCDCDecisionSpan {
|
||||
span,
|
||||
conditions_num: 0,
|
||||
end_markers: vec![],
|
||||
decision_depth,
|
||||
}),
|
||||
};
|
||||
|
||||
let parent_condition = self.decision_stack.pop_back().unwrap_or_default();
|
||||
let parent_condition = decision_ctx.decision_stack.pop_back().unwrap_or_default();
|
||||
let lhs_id = if parent_condition.condition_id == ConditionId::NONE {
|
||||
decision.conditions_num += 1;
|
||||
ConditionId::from(decision.conditions_num)
|
||||
@ -305,8 +328,8 @@ fn record_conditions(&mut self, op: LogicalOp, span: Span) {
|
||||
}
|
||||
};
|
||||
// We visit expressions tree in pre-order, so place the left-hand side on the top.
|
||||
self.decision_stack.push_back(rhs);
|
||||
self.decision_stack.push_back(lhs);
|
||||
decision_ctx.decision_stack.push_back(rhs);
|
||||
decision_ctx.decision_stack.push_back(lhs);
|
||||
}
|
||||
|
||||
fn take_condition(
|
||||
@ -314,10 +337,12 @@ fn take_condition(
|
||||
true_marker: BlockMarkerId,
|
||||
false_marker: BlockMarkerId,
|
||||
) -> (Option<ConditionInfo>, Option<MCDCDecisionSpan>) {
|
||||
let Some(condition_info) = self.decision_stack.pop_back() else {
|
||||
let decision_ctx =
|
||||
self.decision_ctx_stack.last_mut().expect("Unexpected empty decision_ctx_stack");
|
||||
let Some(condition_info) = decision_ctx.decision_stack.pop_back() else {
|
||||
return (None, None);
|
||||
};
|
||||
let Some(decision) = self.processing_decision.as_mut() else {
|
||||
let Some(decision) = decision_ctx.processing_decision.as_mut() else {
|
||||
bug!("Processing decision should have been created before any conditions are taken");
|
||||
};
|
||||
if condition_info.true_next_id == ConditionId::NONE {
|
||||
@ -327,8 +352,8 @@ fn take_condition(
|
||||
decision.end_markers.push(false_marker);
|
||||
}
|
||||
|
||||
if self.decision_stack.is_empty() {
|
||||
(Some(condition_info), self.processing_decision.take())
|
||||
if decision_ctx.decision_stack.is_empty() {
|
||||
(Some(condition_info), decision_ctx.processing_decision.take())
|
||||
} else {
|
||||
(Some(condition_info), None)
|
||||
}
|
||||
@ -364,13 +389,17 @@ pub(crate) fn visit_coverage_branch_condition(
|
||||
|block| branch_info.inject_block_marker(&mut self.cfg, source_info, block);
|
||||
let true_marker = inject_block_marker(then_block);
|
||||
let false_marker = inject_block_marker(else_block);
|
||||
let condition_info =
|
||||
branch_info.fetch_mcdc_condition_info(self.tcx, true_marker, false_marker);
|
||||
let (decision_depth, condition_info) = branch_info
|
||||
.fetch_mcdc_condition_info(self.tcx, true_marker, false_marker)
|
||||
.map_or((0, None), |(decision_depth, condition_info)| {
|
||||
(decision_depth, Some(condition_info))
|
||||
});
|
||||
branch_info.mcdc_branch_spans.push(MCDCBranchSpan {
|
||||
span: source_info.span,
|
||||
condition_info,
|
||||
true_marker,
|
||||
false_marker,
|
||||
decision_depth,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -385,4 +414,20 @@ pub(crate) fn visit_coverage_branch_operation(&mut self, logical_op: LogicalOp,
|
||||
mcdc_state.record_conditions(logical_op, span);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn mcdc_increment_depth_if_enabled(&mut self) {
|
||||
if let Some(branch_info) = self.coverage_branch_info.as_mut()
|
||||
&& let Some(mcdc_state) = branch_info.mcdc_state.as_mut()
|
||||
{
|
||||
mcdc_state.decision_ctx_stack.push(MCDCDecisionCtx::default());
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) fn mcdc_decrement_depth_if_enabled(&mut self) {
|
||||
if let Some(branch_info) = self.coverage_branch_info.as_mut()
|
||||
&& let Some(mcdc_state) = branch_info.mcdc_state.as_mut()
|
||||
{
|
||||
mcdc_state.decision_ctx_stack.pop().expect("Unexpected empty decision stack");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -148,8 +148,14 @@ fn then_else_break_inner(
|
||||
let mut block = block;
|
||||
let temp_scope = args.temp_scope_override.unwrap_or_else(|| this.local_scope());
|
||||
let mutability = Mutability::Mut;
|
||||
|
||||
// Increment the decision depth, in case we encounter boolean expressions
|
||||
// further down.
|
||||
this.mcdc_increment_depth_if_enabled();
|
||||
let place =
|
||||
unpack!(block = this.as_temp(block, Some(temp_scope), expr_id, mutability));
|
||||
this.mcdc_decrement_depth_if_enabled();
|
||||
|
||||
let operand = Operand::Move(Place::from(place));
|
||||
|
||||
let then_block = this.cfg.start_new_block();
|
||||
|
@ -102,12 +102,23 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
|
||||
|
||||
inject_mcdc_statements(mir_body, &basic_coverage_blocks, &coverage_spans);
|
||||
|
||||
let mcdc_max_decision_depth = coverage_spans
|
||||
.mappings
|
||||
.iter()
|
||||
.filter_map(|bcb_mapping| match bcb_mapping.kind {
|
||||
BcbMappingKind::MCDCDecision { decision_depth, .. } => Some(decision_depth),
|
||||
_ => None,
|
||||
})
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
|
||||
mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
|
||||
function_source_hash: hir_info.function_source_hash,
|
||||
num_counters: coverage_counters.num_counters(),
|
||||
mcdc_bitmap_bytes: coverage_spans.test_vector_bitmap_bytes(),
|
||||
expressions: coverage_counters.into_expressions(),
|
||||
mappings,
|
||||
mcdc_max_decision_depth,
|
||||
}));
|
||||
}
|
||||
|
||||
@ -145,16 +156,17 @@ fn create_mappings<'tcx>(
|
||||
|BcbMapping { kind: bcb_mapping_kind, span }| {
|
||||
let kind = match *bcb_mapping_kind {
|
||||
BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)),
|
||||
BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info: None } => {
|
||||
MappingKind::Branch {
|
||||
true_term: term_for_bcb(true_bcb),
|
||||
false_term: term_for_bcb(false_bcb),
|
||||
}
|
||||
}
|
||||
BcbMappingKind::MCDCBranch {
|
||||
true_bcb, false_bcb, condition_info: None, ..
|
||||
} => MappingKind::Branch {
|
||||
true_term: term_for_bcb(true_bcb),
|
||||
false_term: term_for_bcb(false_bcb),
|
||||
},
|
||||
BcbMappingKind::MCDCBranch {
|
||||
true_bcb,
|
||||
false_bcb,
|
||||
condition_info: Some(mcdc_params),
|
||||
..
|
||||
} => MappingKind::MCDCBranch {
|
||||
true_term: term_for_bcb(true_bcb),
|
||||
false_term: term_for_bcb(false_bcb),
|
||||
@ -246,24 +258,28 @@ fn inject_mcdc_statements<'tcx>(
|
||||
}
|
||||
|
||||
// Inject test vector update first because `inject_statement` always insert new statement at head.
|
||||
for (end_bcbs, bitmap_idx) in
|
||||
for (end_bcbs, bitmap_idx, decision_depth) in
|
||||
coverage_spans.mappings.iter().filter_map(|mapping| match &mapping.kind {
|
||||
BcbMappingKind::MCDCDecision { end_bcbs, bitmap_idx, .. } => {
|
||||
Some((end_bcbs, *bitmap_idx))
|
||||
BcbMappingKind::MCDCDecision { end_bcbs, bitmap_idx, decision_depth, .. } => {
|
||||
Some((end_bcbs, *bitmap_idx, *decision_depth))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
for end in end_bcbs {
|
||||
let end_bb = basic_coverage_blocks[*end].leader_bb();
|
||||
inject_statement(mir_body, CoverageKind::TestVectorBitmapUpdate { bitmap_idx }, end_bb);
|
||||
inject_statement(
|
||||
mir_body,
|
||||
CoverageKind::TestVectorBitmapUpdate { bitmap_idx, decision_depth },
|
||||
end_bb,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (true_bcb, false_bcb, condition_id) in
|
||||
for (true_bcb, false_bcb, condition_id, decision_depth) in
|
||||
coverage_spans.mappings.iter().filter_map(|mapping| match mapping.kind {
|
||||
BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info } => {
|
||||
Some((true_bcb, false_bcb, condition_info?.condition_id))
|
||||
BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info, decision_depth } => {
|
||||
Some((true_bcb, false_bcb, condition_info?.condition_id, decision_depth))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
@ -271,13 +287,13 @@ fn inject_mcdc_statements<'tcx>(
|
||||
let true_bb = basic_coverage_blocks[true_bcb].leader_bb();
|
||||
inject_statement(
|
||||
mir_body,
|
||||
CoverageKind::CondBitmapUpdate { id: condition_id, value: true },
|
||||
CoverageKind::CondBitmapUpdate { id: condition_id, value: true, decision_depth },
|
||||
true_bb,
|
||||
);
|
||||
let false_bb = basic_coverage_blocks[false_bcb].leader_bb();
|
||||
inject_statement(
|
||||
mir_body,
|
||||
CoverageKind::CondBitmapUpdate { id: condition_id, value: false },
|
||||
CoverageKind::CondBitmapUpdate { id: condition_id, value: false, decision_depth },
|
||||
false_bb,
|
||||
);
|
||||
}
|
||||
|
@ -26,9 +26,15 @@ pub(super) enum BcbMappingKind {
|
||||
/// If `None`, this actually represents a normal branch mapping inserted
|
||||
/// for code that was too complex for MC/DC.
|
||||
condition_info: Option<ConditionInfo>,
|
||||
decision_depth: u16,
|
||||
},
|
||||
/// Associates a mcdc decision with its join BCB.
|
||||
MCDCDecision { end_bcbs: BTreeSet<BasicCoverageBlock>, bitmap_idx: u32, conditions_num: u16 },
|
||||
MCDCDecision {
|
||||
end_bcbs: BTreeSet<BasicCoverageBlock>,
|
||||
bitmap_idx: u32,
|
||||
conditions_num: u16,
|
||||
decision_depth: u16,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -453,15 +453,25 @@ pub(super) fn extract_mcdc_mappings(
|
||||
Some((span, true_bcb, false_bcb))
|
||||
};
|
||||
|
||||
let mcdc_branch_filter_map =
|
||||
|&MCDCBranchSpan { span: raw_span, true_marker, false_marker, condition_info }| {
|
||||
check_branch_bcb(raw_span, true_marker, false_marker).map(
|
||||
|(span, true_bcb, false_bcb)| BcbMapping {
|
||||
kind: BcbMappingKind::MCDCBranch { true_bcb, false_bcb, condition_info },
|
||||
span,
|
||||
let mcdc_branch_filter_map = |&MCDCBranchSpan {
|
||||
span: raw_span,
|
||||
true_marker,
|
||||
false_marker,
|
||||
condition_info,
|
||||
decision_depth,
|
||||
}| {
|
||||
check_branch_bcb(raw_span, true_marker, false_marker).map(|(span, true_bcb, false_bcb)| {
|
||||
BcbMapping {
|
||||
kind: BcbMappingKind::MCDCBranch {
|
||||
true_bcb,
|
||||
false_bcb,
|
||||
condition_info,
|
||||
decision_depth,
|
||||
},
|
||||
)
|
||||
};
|
||||
span,
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let mut next_bitmap_idx = 0;
|
||||
|
||||
@ -482,6 +492,7 @@ pub(super) fn extract_mcdc_mappings(
|
||||
end_bcbs,
|
||||
bitmap_idx,
|
||||
conditions_num: decision.conditions_num as u16,
|
||||
decision_depth: decision.decision_depth,
|
||||
},
|
||||
span,
|
||||
})
|
||||
|
201
tests/coverage/mcdc_nested_if.cov-map
Normal file
201
tests/coverage/mcdc_nested_if.cov-map
Normal file
@ -0,0 +1,201 @@
|
||||
Function name: mcdc_nested_if::doubly_nested_if_in_condition
|
||||
Raw bytes (168): 0x[01, 01, 0e, 01, 05, 05, 11, 05, 11, 26, 19, 05, 11, 19, 1d, 19, 1d, 1d, 22, 26, 19, 05, 11, 11, 15, 09, 02, 0d, 37, 09, 02, 14, 01, 0f, 01, 01, 09, 28, 02, 02, 01, 08, 00, 4e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 4e, 05, 00, 10, 00, 11, 28, 01, 02, 00, 10, 00, 36, 30, 11, 26, 01, 00, 02, 00, 10, 00, 11, 30, 15, 21, 02, 00, 00, 00, 15, 00, 36, 26, 00, 18, 00, 19, 28, 00, 02, 00, 18, 00, 1e, 30, 19, 22, 01, 02, 00, 00, 18, 00, 19, 19, 00, 1d, 00, 1e, 30, 1a, 1d, 02, 00, 00, 00, 1d, 00, 1e, 1a, 00, 21, 00, 25, 1f, 00, 2f, 00, 34, 2b, 00, 39, 00, 3e, 21, 00, 48, 00, 4c, 0d, 00, 4f, 02, 06, 37, 02, 0c, 02, 06, 33, 03, 01, 00, 02]
|
||||
Number of files: 1
|
||||
- file 0 => global file 1
|
||||
Number of expressions: 14
|
||||
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
|
||||
- expression 1 operands: lhs = Counter(1), rhs = Counter(4)
|
||||
- expression 2 operands: lhs = Counter(1), rhs = Counter(4)
|
||||
- expression 3 operands: lhs = Expression(9, Sub), rhs = Counter(6)
|
||||
- expression 4 operands: lhs = Counter(1), rhs = Counter(4)
|
||||
- expression 5 operands: lhs = Counter(6), rhs = Counter(7)
|
||||
- expression 6 operands: lhs = Counter(6), rhs = Counter(7)
|
||||
- expression 7 operands: lhs = Counter(7), rhs = Expression(8, Sub)
|
||||
- expression 8 operands: lhs = Expression(9, Sub), rhs = Counter(6)
|
||||
- expression 9 operands: lhs = Counter(1), rhs = Counter(4)
|
||||
- expression 10 operands: lhs = Counter(4), rhs = Counter(5)
|
||||
- expression 11 operands: lhs = Counter(2), rhs = Expression(0, Sub)
|
||||
- expression 12 operands: lhs = Counter(3), rhs = Expression(13, Add)
|
||||
- expression 13 operands: lhs = Counter(2), rhs = Expression(0, Sub)
|
||||
Number of file 0 mappings: 20
|
||||
- Code(Counter(0)) at (prev + 15, 1) to (start + 1, 9)
|
||||
- MCDCDecision { bitmap_idx: 2, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 78)
|
||||
- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9)
|
||||
true = c1
|
||||
false = (c0 - c1)
|
||||
- MCDCBranch { true: Counter(3), false: Counter(2), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 78)
|
||||
true = c3
|
||||
false = c2
|
||||
- Code(Counter(1)) at (prev + 0, 16) to (start + 0, 17)
|
||||
- MCDCDecision { bitmap_idx: 1, conditions_num: 2 } at (prev + 0, 16) to (start + 0, 54)
|
||||
- MCDCBranch { true: Counter(4), false: Expression(9, Sub), condition_id: 1, true_next_id: 0, false_next_id: 2 } at (prev + 0, 16) to (start + 0, 17)
|
||||
true = c4
|
||||
false = (c1 - c4)
|
||||
- MCDCBranch { true: Counter(5), false: Counter(8), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 21) to (start + 0, 54)
|
||||
true = c5
|
||||
false = c8
|
||||
- Code(Expression(9, Sub)) at (prev + 0, 24) to (start + 0, 25)
|
||||
= (c1 - c4)
|
||||
- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 0, 24) to (start + 0, 30)
|
||||
- MCDCBranch { true: Counter(6), false: Expression(8, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 24) to (start + 0, 25)
|
||||
true = c6
|
||||
false = ((c1 - c4) - c6)
|
||||
- Code(Counter(6)) at (prev + 0, 29) to (start + 0, 30)
|
||||
- MCDCBranch { true: Expression(6, Sub), false: Counter(7), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 29) to (start + 0, 30)
|
||||
true = (c6 - c7)
|
||||
false = c7
|
||||
- Code(Expression(6, Sub)) at (prev + 0, 33) to (start + 0, 37)
|
||||
= (c6 - c7)
|
||||
- Code(Expression(7, Add)) at (prev + 0, 47) to (start + 0, 52)
|
||||
= (c7 + ((c1 - c4) - c6))
|
||||
- Code(Expression(10, Add)) at (prev + 0, 57) to (start + 0, 62)
|
||||
= (c4 + c5)
|
||||
- Code(Counter(8)) at (prev + 0, 72) to (start + 0, 76)
|
||||
- Code(Counter(3)) at (prev + 0, 79) to (start + 2, 6)
|
||||
- Code(Expression(13, Add)) at (prev + 2, 12) to (start + 2, 6)
|
||||
= (c2 + (c0 - c1))
|
||||
- Code(Expression(12, Add)) at (prev + 3, 1) to (start + 0, 2)
|
||||
= (c3 + (c2 + (c0 - c1)))
|
||||
|
||||
Function name: mcdc_nested_if::nested_if_in_condition
|
||||
Raw bytes (120): 0x[01, 01, 0b, 01, 05, 05, 11, 05, 11, 1e, 15, 05, 11, 11, 15, 1e, 15, 05, 11, 09, 02, 0d, 2b, 09, 02, 0e, 01, 07, 01, 01, 09, 28, 01, 02, 01, 08, 00, 2e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 2e, 05, 00, 10, 00, 11, 28, 00, 02, 00, 10, 00, 16, 30, 11, 1e, 01, 00, 02, 00, 10, 00, 11, 1e, 00, 15, 00, 16, 30, 15, 1a, 02, 00, 00, 00, 15, 00, 16, 17, 00, 19, 00, 1d, 1a, 00, 27, 00, 2c, 0d, 00, 2f, 02, 06, 2b, 02, 0c, 02, 06, 27, 03, 01, 00, 02]
|
||||
Number of files: 1
|
||||
- file 0 => global file 1
|
||||
Number of expressions: 11
|
||||
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
|
||||
- expression 1 operands: lhs = Counter(1), rhs = Counter(4)
|
||||
- expression 2 operands: lhs = Counter(1), rhs = Counter(4)
|
||||
- expression 3 operands: lhs = Expression(7, Sub), rhs = Counter(5)
|
||||
- expression 4 operands: lhs = Counter(1), rhs = Counter(4)
|
||||
- expression 5 operands: lhs = Counter(4), rhs = Counter(5)
|
||||
- expression 6 operands: lhs = Expression(7, Sub), rhs = Counter(5)
|
||||
- expression 7 operands: lhs = Counter(1), rhs = Counter(4)
|
||||
- expression 8 operands: lhs = Counter(2), rhs = Expression(0, Sub)
|
||||
- expression 9 operands: lhs = Counter(3), rhs = Expression(10, Add)
|
||||
- expression 10 operands: lhs = Counter(2), rhs = Expression(0, Sub)
|
||||
Number of file 0 mappings: 14
|
||||
- Code(Counter(0)) at (prev + 7, 1) to (start + 1, 9)
|
||||
- MCDCDecision { bitmap_idx: 1, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 46)
|
||||
- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9)
|
||||
true = c1
|
||||
false = (c0 - c1)
|
||||
- MCDCBranch { true: Counter(3), false: Counter(2), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 46)
|
||||
true = c3
|
||||
false = c2
|
||||
- Code(Counter(1)) at (prev + 0, 16) to (start + 0, 17)
|
||||
- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 0, 16) to (start + 0, 22)
|
||||
- MCDCBranch { true: Counter(4), false: Expression(7, Sub), condition_id: 1, true_next_id: 0, false_next_id: 2 } at (prev + 0, 16) to (start + 0, 17)
|
||||
true = c4
|
||||
false = (c1 - c4)
|
||||
- Code(Expression(7, Sub)) at (prev + 0, 21) to (start + 0, 22)
|
||||
= (c1 - c4)
|
||||
- MCDCBranch { true: Counter(5), false: Expression(6, Sub), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 21) to (start + 0, 22)
|
||||
true = c5
|
||||
false = ((c1 - c4) - c5)
|
||||
- Code(Expression(5, Add)) at (prev + 0, 25) to (start + 0, 29)
|
||||
= (c4 + c5)
|
||||
- Code(Expression(6, Sub)) at (prev + 0, 39) to (start + 0, 44)
|
||||
= ((c1 - c4) - c5)
|
||||
- Code(Counter(3)) at (prev + 0, 47) to (start + 2, 6)
|
||||
- Code(Expression(10, Add)) at (prev + 2, 12) to (start + 2, 6)
|
||||
= (c2 + (c0 - c1))
|
||||
- Code(Expression(9, Add)) at (prev + 3, 1) to (start + 0, 2)
|
||||
= (c3 + (c2 + (c0 - c1)))
|
||||
|
||||
Function name: mcdc_nested_if::nested_in_then_block_in_condition
|
||||
Raw bytes (176): 0x[01, 01, 12, 01, 05, 05, 11, 05, 11, 3a, 15, 05, 11, 11, 15, 33, 19, 11, 15, 19, 1d, 19, 1d, 1d, 2e, 33, 19, 11, 15, 3a, 15, 05, 11, 09, 02, 0d, 47, 09, 02, 14, 01, 22, 01, 01, 09, 28, 02, 02, 01, 08, 00, 4b, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 4b, 05, 00, 10, 00, 11, 28, 00, 02, 00, 10, 00, 16, 30, 11, 3a, 01, 00, 02, 00, 10, 00, 11, 3a, 00, 15, 00, 16, 30, 15, 36, 02, 00, 00, 00, 15, 00, 16, 33, 00, 1c, 00, 1d, 28, 01, 02, 00, 1c, 00, 22, 30, 19, 2e, 01, 02, 00, 00, 1c, 00, 1d, 19, 00, 21, 00, 22, 30, 26, 1d, 02, 00, 00, 00, 21, 00, 22, 26, 00, 25, 00, 29, 2b, 00, 33, 00, 38, 36, 00, 44, 00, 49, 0d, 00, 4c, 02, 06, 47, 02, 0c, 02, 06, 43, 03, 01, 00, 02]
|
||||
Number of files: 1
|
||||
- file 0 => global file 1
|
||||
Number of expressions: 18
|
||||
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
|
||||
- expression 1 operands: lhs = Counter(1), rhs = Counter(4)
|
||||
- expression 2 operands: lhs = Counter(1), rhs = Counter(4)
|
||||
- expression 3 operands: lhs = Expression(14, Sub), rhs = Counter(5)
|
||||
- expression 4 operands: lhs = Counter(1), rhs = Counter(4)
|
||||
- expression 5 operands: lhs = Counter(4), rhs = Counter(5)
|
||||
- expression 6 operands: lhs = Expression(12, Add), rhs = Counter(6)
|
||||
- expression 7 operands: lhs = Counter(4), rhs = Counter(5)
|
||||
- expression 8 operands: lhs = Counter(6), rhs = Counter(7)
|
||||
- expression 9 operands: lhs = Counter(6), rhs = Counter(7)
|
||||
- expression 10 operands: lhs = Counter(7), rhs = Expression(11, Sub)
|
||||
- expression 11 operands: lhs = Expression(12, Add), rhs = Counter(6)
|
||||
- expression 12 operands: lhs = Counter(4), rhs = Counter(5)
|
||||
- expression 13 operands: lhs = Expression(14, Sub), rhs = Counter(5)
|
||||
- expression 14 operands: lhs = Counter(1), rhs = Counter(4)
|
||||
- expression 15 operands: lhs = Counter(2), rhs = Expression(0, Sub)
|
||||
- expression 16 operands: lhs = Counter(3), rhs = Expression(17, Add)
|
||||
- expression 17 operands: lhs = Counter(2), rhs = Expression(0, Sub)
|
||||
Number of file 0 mappings: 20
|
||||
- Code(Counter(0)) at (prev + 34, 1) to (start + 1, 9)
|
||||
- MCDCDecision { bitmap_idx: 2, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 75)
|
||||
- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9)
|
||||
true = c1
|
||||
false = (c0 - c1)
|
||||
- MCDCBranch { true: Counter(3), false: Counter(2), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 75)
|
||||
true = c3
|
||||
false = c2
|
||||
- Code(Counter(1)) at (prev + 0, 16) to (start + 0, 17)
|
||||
- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 0, 16) to (start + 0, 22)
|
||||
- MCDCBranch { true: Counter(4), false: Expression(14, Sub), condition_id: 1, true_next_id: 0, false_next_id: 2 } at (prev + 0, 16) to (start + 0, 17)
|
||||
true = c4
|
||||
false = (c1 - c4)
|
||||
- Code(Expression(14, Sub)) at (prev + 0, 21) to (start + 0, 22)
|
||||
= (c1 - c4)
|
||||
- MCDCBranch { true: Counter(5), false: Expression(13, Sub), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 21) to (start + 0, 22)
|
||||
true = c5
|
||||
false = ((c1 - c4) - c5)
|
||||
- Code(Expression(12, Add)) at (prev + 0, 28) to (start + 0, 29)
|
||||
= (c4 + c5)
|
||||
- MCDCDecision { bitmap_idx: 1, conditions_num: 2 } at (prev + 0, 28) to (start + 0, 34)
|
||||
- MCDCBranch { true: Counter(6), false: Expression(11, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 28) to (start + 0, 29)
|
||||
true = c6
|
||||
false = ((c4 + c5) - c6)
|
||||
- Code(Counter(6)) at (prev + 0, 33) to (start + 0, 34)
|
||||
- MCDCBranch { true: Expression(9, Sub), false: Counter(7), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 33) to (start + 0, 34)
|
||||
true = (c6 - c7)
|
||||
false = c7
|
||||
- Code(Expression(9, Sub)) at (prev + 0, 37) to (start + 0, 41)
|
||||
= (c6 - c7)
|
||||
- Code(Expression(10, Add)) at (prev + 0, 51) to (start + 0, 56)
|
||||
= (c7 + ((c4 + c5) - c6))
|
||||
- Code(Expression(13, Sub)) at (prev + 0, 68) to (start + 0, 73)
|
||||
= ((c1 - c4) - c5)
|
||||
- Code(Counter(3)) at (prev + 0, 76) to (start + 2, 6)
|
||||
- Code(Expression(17, Add)) at (prev + 2, 12) to (start + 2, 6)
|
||||
= (c2 + (c0 - c1))
|
||||
- Code(Expression(16, Add)) at (prev + 3, 1) to (start + 0, 2)
|
||||
= (c3 + (c2 + (c0 - c1)))
|
||||
|
||||
Function name: mcdc_nested_if::nested_single_condition_decision
|
||||
Raw bytes (85): 0x[01, 01, 06, 01, 05, 05, 11, 05, 11, 09, 02, 0d, 17, 09, 02, 0b, 01, 17, 01, 04, 09, 28, 00, 02, 04, 08, 00, 29, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 29, 05, 00, 10, 00, 11, 20, 11, 0a, 00, 10, 00, 11, 11, 00, 14, 00, 19, 0a, 00, 23, 00, 27, 0d, 00, 2a, 02, 06, 17, 02, 0c, 02, 06, 13, 03, 01, 00, 02]
|
||||
Number of files: 1
|
||||
- file 0 => global file 1
|
||||
Number of expressions: 6
|
||||
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
|
||||
- expression 1 operands: lhs = Counter(1), rhs = Counter(4)
|
||||
- expression 2 operands: lhs = Counter(1), rhs = Counter(4)
|
||||
- expression 3 operands: lhs = Counter(2), rhs = Expression(0, Sub)
|
||||
- expression 4 operands: lhs = Counter(3), rhs = Expression(5, Add)
|
||||
- expression 5 operands: lhs = Counter(2), rhs = Expression(0, Sub)
|
||||
Number of file 0 mappings: 11
|
||||
- Code(Counter(0)) at (prev + 23, 1) to (start + 4, 9)
|
||||
- MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 4, 8) to (start + 0, 41)
|
||||
- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9)
|
||||
true = c1
|
||||
false = (c0 - c1)
|
||||
- MCDCBranch { true: Counter(3), false: Counter(2), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 41)
|
||||
true = c3
|
||||
false = c2
|
||||
- Code(Counter(1)) at (prev + 0, 16) to (start + 0, 17)
|
||||
- Branch { true: Counter(4), false: Expression(2, Sub) } at (prev + 0, 16) to (start + 0, 17)
|
||||
true = c4
|
||||
false = (c1 - c4)
|
||||
- Code(Counter(4)) at (prev + 0, 20) to (start + 0, 25)
|
||||
- Code(Expression(2, Sub)) at (prev + 0, 35) to (start + 0, 39)
|
||||
= (c1 - c4)
|
||||
- Code(Counter(3)) at (prev + 0, 42) to (start + 2, 6)
|
||||
- Code(Expression(5, Add)) at (prev + 2, 12) to (start + 2, 6)
|
||||
= (c2 + (c0 - c1))
|
||||
- Code(Expression(4, Add)) at (prev + 3, 1) to (start + 0, 2)
|
||||
= (c3 + (c2 + (c0 - c1)))
|
||||
|
235
tests/coverage/mcdc_nested_if.coverage
Normal file
235
tests/coverage/mcdc_nested_if.coverage
Normal file
@ -0,0 +1,235 @@
|
||||
LL| |#![feature(coverage_attribute)]
|
||||
LL| |//@ edition: 2021
|
||||
LL| |//@ min-llvm-version: 18
|
||||
LL| |//@ compile-flags: -Zcoverage-options=mcdc
|
||||
LL| |//@ llvm-cov-flags: --show-mcdc
|
||||
LL| |
|
||||
LL| 4|fn nested_if_in_condition(a: bool, b: bool, c: bool) {
|
||||
LL| 4| if a && if b || c { true } else { false } {
|
||||
^3 ^2 ^2 ^1
|
||||
------------------
|
||||
|---> MC/DC Decision Region (LL:8) to (LL:46)
|
||||
|
|
||||
| Number of Conditions: 2
|
||||
| Condition C1 --> (LL:8)
|
||||
| Condition C2 --> (LL:13)
|
||||
|
|
||||
| Executed MC/DC Test Vectors:
|
||||
|
|
||||
| C1, C2 Result
|
||||
| 1 { F, - = F }
|
||||
| 2 { T, F = F }
|
||||
| 3 { T, T = T }
|
||||
|
|
||||
| C1-Pair: covered: (1,3)
|
||||
| C2-Pair: covered: (2,3)
|
||||
| MC/DC Coverage for Decision: 100.00%
|
||||
|
|
||||
|---> MC/DC Decision Region (LL:16) to (LL:22)
|
||||
|
|
||||
| Number of Conditions: 2
|
||||
| Condition C1 --> (LL:16)
|
||||
| Condition C2 --> (LL:21)
|
||||
|
|
||||
| Executed MC/DC Test Vectors:
|
||||
|
|
||||
| C1, C2 Result
|
||||
| 1 { F, F = F }
|
||||
| 2 { T, - = T }
|
||||
| 3 { F, T = T }
|
||||
|
|
||||
| C1-Pair: covered: (1,2)
|
||||
| C2-Pair: covered: (1,3)
|
||||
| MC/DC Coverage for Decision: 100.00%
|
||||
|
|
||||
------------------
|
||||
LL| 2| say("yes");
|
||||
LL| 2| } else {
|
||||
LL| 2| say("no");
|
||||
LL| 2| }
|
||||
LL| 4|}
|
||||
LL| |
|
||||
LL| 4|fn doubly_nested_if_in_condition(a: bool, b: bool, c: bool, d: bool) {
|
||||
LL| 4| if a && if b || if c && d { true } else { false } { false } else { true } {
|
||||
^3 ^2 ^1 ^1 ^1 ^2 ^1
|
||||
------------------
|
||||
|---> MC/DC Decision Region (LL:8) to (LL:78)
|
||||
|
|
||||
| Number of Conditions: 2
|
||||
| Condition C1 --> (LL:8)
|
||||
| Condition C2 --> (LL:13)
|
||||
|
|
||||
| Executed MC/DC Test Vectors:
|
||||
|
|
||||
| C1, C2 Result
|
||||
| 1 { F, - = F }
|
||||
| 2 { T, F = F }
|
||||
| 3 { T, T = T }
|
||||
|
|
||||
| C1-Pair: covered: (1,3)
|
||||
| C2-Pair: covered: (2,3)
|
||||
| MC/DC Coverage for Decision: 100.00%
|
||||
|
|
||||
|---> MC/DC Decision Region (LL:16) to (LL:54)
|
||||
|
|
||||
| Number of Conditions: 2
|
||||
| Condition C1 --> (LL:16)
|
||||
| Condition C2 --> (LL:21)
|
||||
|
|
||||
| Executed MC/DC Test Vectors:
|
||||
|
|
||||
| C1, C2 Result
|
||||
| 1 { F, F = F }
|
||||
| 2 { T, - = T }
|
||||
| 3 { F, T = T }
|
||||
|
|
||||
| C1-Pair: covered: (1,2)
|
||||
| C2-Pair: covered: (1,3)
|
||||
| MC/DC Coverage for Decision: 100.00%
|
||||
|
|
||||
|---> MC/DC Decision Region (LL:24) to (LL:30)
|
||||
|
|
||||
| Number of Conditions: 2
|
||||
| Condition C1 --> (LL:24)
|
||||
| Condition C2 --> (LL:29)
|
||||
|
|
||||
| Executed MC/DC Test Vectors:
|
||||
|
|
||||
| C1, C2 Result
|
||||
| 1 { F, - = F }
|
||||
| 2 { T, T = T }
|
||||
|
|
||||
| C1-Pair: covered: (1,2)
|
||||
| C2-Pair: not covered
|
||||
| MC/DC Coverage for Decision: 50.00%
|
||||
|
|
||||
------------------
|
||||
LL| 1| say("yes");
|
||||
LL| 3| } else {
|
||||
LL| 3| say("no");
|
||||
LL| 3| }
|
||||
LL| 4|}
|
||||
LL| |
|
||||
LL| 3|fn nested_single_condition_decision(a: bool, b: bool) {
|
||||
LL| 3| // Decision with only 1 decision should not be instrumented by MCDC because
|
||||
LL| 3| // branch-coverage is equivalent to MCDC coverage in this case, and we don't
|
||||
LL| 3| // want to waste bitmap space for this.
|
||||
LL| 3| if a && if b { false } else { true } {
|
||||
^2 ^1 ^1
|
||||
------------------
|
||||
|---> MC/DC Decision Region (LL:8) to (LL:41)
|
||||
|
|
||||
| Number of Conditions: 2
|
||||
| Condition C1 --> (LL:8)
|
||||
| Condition C2 --> (LL:13)
|
||||
|
|
||||
| Executed MC/DC Test Vectors:
|
||||
|
|
||||
| C1, C2 Result
|
||||
| 1 { F, - = F }
|
||||
| 2 { T, F = F }
|
||||
| 3 { T, T = T }
|
||||
|
|
||||
| C1-Pair: covered: (1,3)
|
||||
| C2-Pair: covered: (2,3)
|
||||
| MC/DC Coverage for Decision: 100.00%
|
||||
|
|
||||
------------------
|
||||
LL| 1| say("yes");
|
||||
LL| 2| } else {
|
||||
LL| 2| say("no");
|
||||
LL| 2| }
|
||||
LL| 3|}
|
||||
LL| |
|
||||
LL| 7|fn nested_in_then_block_in_condition(a: bool, b: bool, c: bool, d: bool, e: bool) {
|
||||
LL| 7| if a && if b || c { if d && e { true } else { false } } else { false } {
|
||||
^6 ^5 ^5 ^2 ^1 ^4 ^1
|
||||
------------------
|
||||
|---> MC/DC Decision Region (LL:8) to (LL:75)
|
||||
|
|
||||
| Number of Conditions: 2
|
||||
| Condition C1 --> (LL:8)
|
||||
| Condition C2 --> (LL:13)
|
||||
|
|
||||
| Executed MC/DC Test Vectors:
|
||||
|
|
||||
| C1, C2 Result
|
||||
| 1 { F, - = F }
|
||||
| 2 { T, F = F }
|
||||
| 3 { T, T = T }
|
||||
|
|
||||
| C1-Pair: covered: (1,3)
|
||||
| C2-Pair: covered: (2,3)
|
||||
| MC/DC Coverage for Decision: 100.00%
|
||||
|
|
||||
|---> MC/DC Decision Region (LL:16) to (LL:22)
|
||||
|
|
||||
| Number of Conditions: 2
|
||||
| Condition C1 --> (LL:16)
|
||||
| Condition C2 --> (LL:21)
|
||||
|
|
||||
| Executed MC/DC Test Vectors:
|
||||
|
|
||||
| C1, C2 Result
|
||||
| 1 { F, F = F }
|
||||
| 2 { T, - = T }
|
||||
| 3 { F, T = T }
|
||||
|
|
||||
| C1-Pair: covered: (1,2)
|
||||
| C2-Pair: covered: (1,3)
|
||||
| MC/DC Coverage for Decision: 100.00%
|
||||
|
|
||||
|---> MC/DC Decision Region (LL:28) to (LL:34)
|
||||
|
|
||||
| Number of Conditions: 2
|
||||
| Condition C1 --> (LL:28)
|
||||
| Condition C2 --> (LL:33)
|
||||
|
|
||||
| Executed MC/DC Test Vectors:
|
||||
|
|
||||
| C1, C2 Result
|
||||
| 1 { F, - = F }
|
||||
| 2 { T, F = F }
|
||||
| 3 { T, T = T }
|
||||
|
|
||||
| C1-Pair: covered: (1,3)
|
||||
| C2-Pair: covered: (2,3)
|
||||
| MC/DC Coverage for Decision: 100.00%
|
||||
|
|
||||
------------------
|
||||
LL| 1| say("yes");
|
||||
LL| 6| } else {
|
||||
LL| 6| say("no");
|
||||
LL| 6| }
|
||||
LL| 7|}
|
||||
LL| |
|
||||
LL| |#[coverage(off)]
|
||||
LL| |fn main() {
|
||||
LL| | nested_if_in_condition(true, false, false);
|
||||
LL| | nested_if_in_condition(true, true, true);
|
||||
LL| | nested_if_in_condition(true, false, true);
|
||||
LL| | nested_if_in_condition(false, true, true);
|
||||
LL| |
|
||||
LL| | doubly_nested_if_in_condition(true, false, false, true);
|
||||
LL| | doubly_nested_if_in_condition(true, true, true, true);
|
||||
LL| | doubly_nested_if_in_condition(true, false, true, true);
|
||||
LL| | doubly_nested_if_in_condition(false, true, true, true);
|
||||
LL| |
|
||||
LL| | nested_single_condition_decision(true, true);
|
||||
LL| | nested_single_condition_decision(true, false);
|
||||
LL| | nested_single_condition_decision(false, false);
|
||||
LL| |
|
||||
LL| | nested_in_then_block_in_condition(false, false, false, false, false);
|
||||
LL| | nested_in_then_block_in_condition(true, false, false, false, false);
|
||||
LL| | nested_in_then_block_in_condition(true, true, false, false, false);
|
||||
LL| | nested_in_then_block_in_condition(true, false, true, false, false);
|
||||
LL| | nested_in_then_block_in_condition(true, false, true, true, false);
|
||||
LL| | nested_in_then_block_in_condition(true, false, true, false, true);
|
||||
LL| | nested_in_then_block_in_condition(true, false, true, true, true);
|
||||
LL| |}
|
||||
LL| |
|
||||
LL| |#[coverage(off)]
|
||||
LL| |fn say(message: &str) {
|
||||
LL| | core::hint::black_box(message);
|
||||
LL| |}
|
||||
|
70
tests/coverage/mcdc_nested_if.rs
Normal file
70
tests/coverage/mcdc_nested_if.rs
Normal file
@ -0,0 +1,70 @@
|
||||
#![feature(coverage_attribute)]
|
||||
//@ edition: 2021
|
||||
//@ min-llvm-version: 18
|
||||
//@ compile-flags: -Zcoverage-options=mcdc
|
||||
//@ llvm-cov-flags: --show-mcdc
|
||||
|
||||
fn nested_if_in_condition(a: bool, b: bool, c: bool) {
|
||||
if a && if b || c { true } else { false } {
|
||||
say("yes");
|
||||
} else {
|
||||
say("no");
|
||||
}
|
||||
}
|
||||
|
||||
fn doubly_nested_if_in_condition(a: bool, b: bool, c: bool, d: bool) {
|
||||
if a && if b || if c && d { true } else { false } { false } else { true } {
|
||||
say("yes");
|
||||
} else {
|
||||
say("no");
|
||||
}
|
||||
}
|
||||
|
||||
fn nested_single_condition_decision(a: bool, b: bool) {
|
||||
// Decision with only 1 decision should not be instrumented by MCDC because
|
||||
// branch-coverage is equivalent to MCDC coverage in this case, and we don't
|
||||
// want to waste bitmap space for this.
|
||||
if a && if b { false } else { true } {
|
||||
say("yes");
|
||||
} else {
|
||||
say("no");
|
||||
}
|
||||
}
|
||||
|
||||
fn nested_in_then_block_in_condition(a: bool, b: bool, c: bool, d: bool, e: bool) {
|
||||
if a && if b || c { if d && e { true } else { false } } else { false } {
|
||||
say("yes");
|
||||
} else {
|
||||
say("no");
|
||||
}
|
||||
}
|
||||
|
||||
#[coverage(off)]
|
||||
fn main() {
|
||||
nested_if_in_condition(true, false, false);
|
||||
nested_if_in_condition(true, true, true);
|
||||
nested_if_in_condition(true, false, true);
|
||||
nested_if_in_condition(false, true, true);
|
||||
|
||||
doubly_nested_if_in_condition(true, false, false, true);
|
||||
doubly_nested_if_in_condition(true, true, true, true);
|
||||
doubly_nested_if_in_condition(true, false, true, true);
|
||||
doubly_nested_if_in_condition(false, true, true, true);
|
||||
|
||||
nested_single_condition_decision(true, true);
|
||||
nested_single_condition_decision(true, false);
|
||||
nested_single_condition_decision(false, false);
|
||||
|
||||
nested_in_then_block_in_condition(false, false, false, false, false);
|
||||
nested_in_then_block_in_condition(true, false, false, false, false);
|
||||
nested_in_then_block_in_condition(true, true, false, false, false);
|
||||
nested_in_then_block_in_condition(true, false, true, false, false);
|
||||
nested_in_then_block_in_condition(true, false, true, true, false);
|
||||
nested_in_then_block_in_condition(true, false, true, false, true);
|
||||
nested_in_then_block_in_condition(true, false, true, true, true);
|
||||
}
|
||||
|
||||
#[coverage(off)]
|
||||
fn say(message: &str) {
|
||||
core::hint::black_box(message);
|
||||
}
|
Loading…
Reference in New Issue
Block a user